Ahora el browser tiene consola en vez del output.
Pasado todo el sistema de compilacion a cmake. git-svn-id: https://opengnsys.es/svn/trunk@408 a21b9725-9963-47de-94b9-378ad31fedc9remotes/github/master
parent
45157b354d
commit
050d67a7fc
|
@ -0,0 +1,17 @@
|
|||
project(browser)
|
||||
|
||||
cmake_minimum_required(VERSION 2.6)
|
||||
|
||||
find_package(Qt4 REQUIRED)
|
||||
|
||||
include_directories(qtermwidget/src/ ${QT_INCLUDES} ${QT_QTWEBKIT_INCLUDE_DIR} ${CMAKE_CURRENT_BINARY_DIR})
|
||||
|
||||
add_subdirectory(qtermwidget)
|
||||
|
||||
set(browser_SRCS main.cpp mainwindow.cpp)
|
||||
|
||||
qt4_automoc(${browser_SRCS})
|
||||
|
||||
add_executable(browser ${browser_SRCS})
|
||||
|
||||
target_link_libraries(browser qtermwidget ${QT_LIBRARIES} ${QT_QTWEBKIT_LIBRARIES} )
|
|
@ -0,0 +1,17 @@
|
|||
#include <QtGui/QApplication>
|
||||
#include <stdio.h>
|
||||
#include "mainwindow.h"
|
||||
|
||||
int main(int argc, char *argv[])
|
||||
{
|
||||
if(argc<=1)
|
||||
{
|
||||
printf("Usage: %s http://siteweb.com/\n",argv[0]);
|
||||
return -1;
|
||||
}
|
||||
|
||||
QApplication a(argc, argv);
|
||||
MainWindow w;
|
||||
w.show();
|
||||
return a.exec();
|
||||
}
|
|
@ -0,0 +1,272 @@
|
|||
#include "mainwindow.h"
|
||||
#include "mainwindow.moc"
|
||||
#include <QtWebKit>
|
||||
#include <QStringList>
|
||||
#include <QWebView>
|
||||
#include <QDockWidget>
|
||||
#include <QtDebug>
|
||||
#include <QWebPage>
|
||||
#include <QProcess>
|
||||
#include <QTextEdit>
|
||||
#include <QMessageBox>
|
||||
#include <QPushButton>
|
||||
#include <QDateTime>
|
||||
|
||||
#include "qtermwidget.h"
|
||||
|
||||
#define BUFFERSIZE 2048
|
||||
|
||||
#define CURRENT_TIME() QDateTime::currentDateTime().toString("dd/MM/yy hh:mm:ss")
|
||||
|
||||
MainWindow::MainWindow(QWidget *parent)
|
||||
: QMainWindow(parent),web(new QWebView()),text(new QTextEdit()),
|
||||
process(new QProcess(this)),
|
||||
logfile(NULL),logstream(NULL)
|
||||
{
|
||||
// Graphic
|
||||
setCentralWidget(web);
|
||||
|
||||
dock=new QDockWidget(this);
|
||||
dock->setAllowedAreas(Qt::BottomDockWidgetArea);
|
||||
dock->setFeatures(QDockWidget::NoDockWidgetFeatures);
|
||||
|
||||
console = new QTermWidget();
|
||||
|
||||
QFont font = QApplication::font();
|
||||
font.setFamily("Terminus");
|
||||
font.setPointSize(12);
|
||||
|
||||
console->setTerminalFont(font);
|
||||
|
||||
//console->setColorScheme(COLOR_SCHEME_BLACK_ON_LIGHT_YELLOW);
|
||||
console->setScrollBarPosition(QTermWidget::ScrollBarRight);
|
||||
|
||||
//showFullScreen();
|
||||
|
||||
dock->setWidget(console);
|
||||
addDockWidget(Qt::BottomDockWidgetArea,dock);
|
||||
|
||||
text->setReadOnly(true);
|
||||
|
||||
web->page()->setLinkDelegationPolicy(QWebPage::DelegateAllLinks);
|
||||
|
||||
// Web signals
|
||||
connect(web->page(),SIGNAL(linkClicked(const QUrl&)),this,
|
||||
SLOT(slotLinkHandle(const QUrl&)));
|
||||
connect(web,SIGNAL(loadStarted()),this,SLOT(slotWebLoadStarted()));
|
||||
connect(web,SIGNAL(loadFinished(bool)),this,SLOT(slotWebLoadFinished(bool)));
|
||||
connect(web,SIGNAL(loadProgress(int)),this,SLOT(slotWebLoadProgress(int)));
|
||||
|
||||
QStringList arguments=QCoreApplication::arguments();
|
||||
web->load(QUrl(arguments[1]));
|
||||
|
||||
|
||||
// Process signals
|
||||
connect(process,SIGNAL(started()),this,SLOT(slotProcessStarted()));
|
||||
connect(process,SIGNAL(finished(int,QProcess::ExitStatus)),
|
||||
this,SLOT(slotProcessFinished(int,QProcess::ExitStatus)));
|
||||
|
||||
connect(process,SIGNAL(error(QProcess::ProcessError)),
|
||||
this,SLOT(slotProcessError(QProcess::ProcessError)));
|
||||
|
||||
connect(process,SIGNAL(readyReadStandardOutput()),this,SLOT(slotProcessOutput()));
|
||||
connect(process,SIGNAL(readyReadStandardError()),
|
||||
this,SLOT(slotProcessErrorOutput()));
|
||||
|
||||
// Set tittle
|
||||
setWindowTitle(tr("OpenGNSys Browser"));
|
||||
|
||||
if(!readEnvironmentValues())
|
||||
text->insertPlainText(tr("Any environment variable/s didn't be setted\n"));
|
||||
|
||||
if(env.contains("OGLOGFILE") && env["OGLOGFILE"]!="")
|
||||
{
|
||||
logfile=new QFile(env["OGLOGFILE"]);
|
||||
if(!logfile->open(QIODevice::WriteOnly | QIODevice::Text |
|
||||
QIODevice::Append))
|
||||
{
|
||||
text->insertPlainText(tr("The log file couldn't be opened: ")+logfile->fileName());
|
||||
delete logfile;
|
||||
logfile=NULL;
|
||||
}
|
||||
else
|
||||
logstream=new QTextStream(logfile);
|
||||
}
|
||||
}
|
||||
|
||||
MainWindow::~MainWindow()
|
||||
{
|
||||
if(logfile)
|
||||
{
|
||||
logfile->close();
|
||||
delete logfile;
|
||||
}
|
||||
if(logstream)
|
||||
delete logstream;
|
||||
}
|
||||
|
||||
void MainWindow::slotLinkHandle(const QUrl &url)
|
||||
{
|
||||
QString string = url.toString();
|
||||
qDebug() << string;
|
||||
// Si es un link del tipo PROTOCOL lo ejecutamos
|
||||
if(string.startsWith(PROTOCOL))
|
||||
{
|
||||
string=string.remove(0,QString(PROTOCOL).length());
|
||||
QStringList list=string.split(" ",QString::SkipEmptyParts);
|
||||
QString command=list.takeFirst();
|
||||
process->setReadChannel(QProcess::StandardOutput);
|
||||
// Le ponemos el mismo entorno que tiene el browser ahora mismo
|
||||
process->setEnvironment(QProcess::systemEnvironment());
|
||||
process->start(command,list);
|
||||
}
|
||||
else
|
||||
{
|
||||
qDebug() << "Load URL: " << url <<endl;
|
||||
web->load(url);
|
||||
}
|
||||
}
|
||||
|
||||
void MainWindow::slotWebLoadStarted()
|
||||
{
|
||||
qDebug()<<"Empieza la carga de la web";
|
||||
}
|
||||
|
||||
void MainWindow::slotWebLoadProgress(int progress)
|
||||
{
|
||||
qDebug()<<"Progress "<<progress;
|
||||
}
|
||||
|
||||
void MainWindow::slotWebLoadFinished(bool ok)
|
||||
{
|
||||
// If any error ocurred, show a pop up
|
||||
// Sometimes when the url hasn't got a dot, i.e /var/www/pageweb,
|
||||
// the return value is always true so we check the bytes received too
|
||||
if(ok == false || web->page()->totalBytes() == 0)
|
||||
{
|
||||
qDebug()<<"Error accediendo a la web";
|
||||
QMessageBox msgBox;
|
||||
msgBox.setText(tr("The web page couldn't load. What do you want to do?"));
|
||||
|
||||
QPushButton *reloadButton = msgBox.addButton(tr("Reload"), QMessageBox::ActionRole);
|
||||
msgBox.addButton(QMessageBox::Abort);
|
||||
|
||||
msgBox.exec();
|
||||
|
||||
if (msgBox.clickedButton() == reloadButton)
|
||||
{
|
||||
web->reload();
|
||||
}
|
||||
else
|
||||
{
|
||||
close();
|
||||
}
|
||||
}
|
||||
else
|
||||
qDebug()<<"Descarga finalizada satisfactoriamente";
|
||||
}
|
||||
|
||||
void MainWindow::slotProcessStarted()
|
||||
{
|
||||
qDebug()<<"Proceso inicializado"<<endl;
|
||||
}
|
||||
|
||||
void MainWindow::slotProcessOutput()
|
||||
{
|
||||
qDebug()<<"Output"<<endl;
|
||||
process->setReadChannel(QProcess::StandardOutput);
|
||||
char buf[BUFFERSIZE];
|
||||
while((process->readLine(buf,BUFFERSIZE) > 0))
|
||||
{
|
||||
text->insertPlainText(buf);
|
||||
/*
|
||||
QString str="<b>";
|
||||
str+=buf;
|
||||
str+="</b>";
|
||||
text->insertHtml(str);
|
||||
*/
|
||||
output<<buf;
|
||||
if(logstream)
|
||||
*logstream<<CURRENT_TIME()<<": "<<buf;
|
||||
}
|
||||
}
|
||||
|
||||
void MainWindow::slotProcessErrorOutput()
|
||||
{
|
||||
qDebug()<<"ErrorOutput"<<endl;
|
||||
process->setReadChannel(QProcess::StandardError);
|
||||
char buf[BUFFERSIZE];
|
||||
while((process->readLine(buf,BUFFERSIZE) > 0))
|
||||
{
|
||||
text->insertPlainText(buf);
|
||||
errors<<buf;
|
||||
}
|
||||
}
|
||||
|
||||
void MainWindow::slotProcessFinished(int code,QProcess::ExitStatus status)
|
||||
{
|
||||
if(status==QProcess::NormalExit)
|
||||
{
|
||||
qDebug()<<"Finished: "<<code<<" "<<status<<endl;
|
||||
qDebug()<<"OUTPUT:"<<endl<<output<<endl<<"ERROR:"<<endl<<errors<<endl;
|
||||
}
|
||||
else
|
||||
{
|
||||
qDebug()<<"Ha petado"<<endl;
|
||||
qDebug()<<"Finished: "<<code<<" "<<status<<endl;
|
||||
qDebug()<<"OUTPUT:"<<endl<<output<<endl<<"ERROR:"<<endl<<errors<<endl;
|
||||
}
|
||||
}
|
||||
|
||||
void MainWindow::slotProcessError(QProcess::ProcessError error)
|
||||
{
|
||||
switch(error)
|
||||
{
|
||||
case QProcess::FailedToStart:
|
||||
qDebug()<<"Imposible arrancar el programa"<<endl;
|
||||
break;
|
||||
// No capturo crashed porque la pillo por finished
|
||||
case QProcess::Crashed:
|
||||
case QProcess::Timedout:
|
||||
case QProcess::WriteError:
|
||||
case QProcess::ReadError:
|
||||
case QProcess::UnknownError:
|
||||
default:
|
||||
qDebug()<<"Otro error"<<endl;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
int MainWindow::readEnvironmentValues()
|
||||
{
|
||||
// The return value
|
||||
int ret=true;
|
||||
|
||||
// Get all environment variables
|
||||
QStringList environmentlist=QProcess::systemEnvironment();
|
||||
// This is the list of the important variables
|
||||
QStringList variablelist=QString(ENVIRONMENT).split(",");
|
||||
|
||||
// This is an auxiliar variable
|
||||
QStringList stringlist;
|
||||
|
||||
foreach (QString str,variablelist)
|
||||
{
|
||||
// Look for the variable in the environment
|
||||
stringlist=environmentlist.filter(str+"=");
|
||||
|
||||
if(stringlist.isEmpty())
|
||||
{
|
||||
env[str]="";
|
||||
ret=false;
|
||||
}
|
||||
else
|
||||
{
|
||||
// Get the first element and get the value part
|
||||
env[str]=(stringlist.first().split("="))[1];
|
||||
}
|
||||
}
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
|
@ -0,0 +1,67 @@
|
|||
#ifndef MAINWINDOW_H
|
||||
#define MAINWINDOW_H
|
||||
|
||||
#define PROTOCOL "command:"
|
||||
#define ENVIRONMENT "OGLOGFILE"
|
||||
//#define ENVIRONMENT "OGIP,OGSERVER,OGLOG"
|
||||
|
||||
#include <QWidget>
|
||||
#include <QProcess>
|
||||
#include <QMap>
|
||||
#include <QMainWindow>
|
||||
|
||||
class QWebView;
|
||||
class QTextEdit;
|
||||
class QVBoxLayout;
|
||||
class QProcess;
|
||||
class QStringList;
|
||||
class QString;
|
||||
class QUrl;
|
||||
class QFile;
|
||||
class QTextStream;
|
||||
class QDockWidget;
|
||||
class QTermWidget;
|
||||
|
||||
class MainWindow : public QMainWindow
|
||||
{
|
||||
Q_OBJECT
|
||||
|
||||
public:
|
||||
MainWindow(QWidget *parent = 0);
|
||||
~MainWindow();
|
||||
|
||||
public slots:
|
||||
// Funcion que maneja los links
|
||||
void slotLinkHandle(const QUrl& url);
|
||||
void slotWebLoadStarted();
|
||||
void slotWebLoadFinished(bool ok);
|
||||
void slotWebLoadProgress(int progress);
|
||||
|
||||
// Funciones que manejan cada vez que el proceso hace algo
|
||||
void slotProcessStarted();
|
||||
void slotProcessFinished(int code,QProcess::ExitStatus status);
|
||||
void slotProcessError(QProcess::ProcessError error);
|
||||
void slotProcessOutput();
|
||||
void slotProcessErrorOutput();
|
||||
|
||||
//Functions
|
||||
protected:
|
||||
int readEnvironmentValues();
|
||||
|
||||
|
||||
protected:
|
||||
QWebView *web;
|
||||
QTextEdit *text;
|
||||
QDockWidget *dock;
|
||||
QTermWidget *console;
|
||||
|
||||
QProcess *process;
|
||||
QStringList output;
|
||||
QStringList errors;
|
||||
|
||||
QMap<QString,QString> env;
|
||||
QFile *logfile;
|
||||
QTextStream *logstream;
|
||||
};
|
||||
|
||||
#endif // MAINWINDOW_H
|
|
@ -0,0 +1,9 @@
|
|||
add_definitions(-DHAVE_POSIX_OPENPT)
|
||||
|
||||
aux_source_directory(src qtermwidget_SRCS)
|
||||
|
||||
include_directories(${CMAKE_CURRENT_BINARY_DIR})
|
||||
|
||||
qt4_automoc(${qtermwidget_SRCS})
|
||||
|
||||
add_library(qtermwidget STATIC ${qtermwidget_SRCS})
|
|
@ -0,0 +1,19 @@
|
|||
31.07.2008
|
||||
Interface class from c-style conversions rewritten with pimpl support.
|
||||
|
||||
|
||||
16.07.2008
|
||||
Added optional scrollbar
|
||||
|
||||
|
||||
06.06.2008
|
||||
Some artefacts were removed, some added...
|
||||
Also added support for color schemes, and 3 color schemes provided (classical - white on black, green on black, black on light yellow). Is it enough or not?
|
||||
|
||||
|
||||
26.05.2008
|
||||
Added file release as an archive with source code. But preferrable way is still getting code from CVS, cause file release can be outdated.
|
||||
|
||||
|
||||
11.05.2008
|
||||
Initial CVS import - first version comes with number 0.0.1
|
|
@ -1,10 +1,8 @@
|
|||
This is a external source gotten it from:
|
||||
This is a external source gotten from:
|
||||
|
||||
http://www.qt-apps.org/content/show.php/QTermWidget?content=82832
|
||||
|
||||
Only library and headers needed. Compile with QtEmbedded-4.5.1.
|
||||
|
||||
**********************************************************************************
|
||||
*************************************************************************************
|
||||
QTermWidget
|
||||
version 0.1.0
|
||||
|
||||
|
|
|
@ -0,0 +1,10 @@
|
|||
Global
|
||||
- provide more compatibility for vttest
|
||||
|
||||
Package
|
||||
- migrate to autotools if needed
|
||||
|
||||
Source
|
||||
- provide more options for customization
|
||||
- clean unused code
|
||||
- add some QT3 support features if needed
|
Binary file not shown.
Binary file not shown.
|
@ -0,0 +1,337 @@
|
|||
/*
|
||||
This file is part of Konsole, an X terminal.
|
||||
Copyright (C) 2000 by Stephan Kulow <coolo@kde.org>
|
||||
|
||||
Rewritten for QT4 by e_k <e_k at users.sourceforge.net>, Copyright (C)2008
|
||||
|
||||
This program is free software; you can redistribute it and/or modify
|
||||
it under the terms of the GNU General Public License as published by
|
||||
the Free Software Foundation; either version 2 of the License, or
|
||||
(at your option) any later version.
|
||||
|
||||
This program is distributed in the hope that it will be useful,
|
||||
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
GNU General Public License for more details.
|
||||
|
||||
You should have received a copy of the GNU General Public License
|
||||
along with this program; if not, write to the Free Software
|
||||
Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
|
||||
02110-1301 USA.
|
||||
|
||||
*/
|
||||
|
||||
// Own
|
||||
#include "BlockArray.h"
|
||||
|
||||
#include <QtCore>
|
||||
|
||||
// System
|
||||
#include <assert.h>
|
||||
#include <sys/mman.h>
|
||||
#include <sys/param.h>
|
||||
#include <unistd.h>
|
||||
#include <stdio.h>
|
||||
|
||||
|
||||
using namespace Konsole;
|
||||
|
||||
static int blocksize = 0;
|
||||
|
||||
BlockArray::BlockArray()
|
||||
: size(0),
|
||||
current(size_t(-1)),
|
||||
index(size_t(-1)),
|
||||
lastmap(0),
|
||||
lastmap_index(size_t(-1)),
|
||||
lastblock(0), ion(-1),
|
||||
length(0)
|
||||
{
|
||||
// lastmap_index = index = current = size_t(-1);
|
||||
if (blocksize == 0)
|
||||
blocksize = ((sizeof(Block) / getpagesize()) + 1) * getpagesize();
|
||||
|
||||
}
|
||||
|
||||
BlockArray::~BlockArray()
|
||||
{
|
||||
setHistorySize(0);
|
||||
assert(!lastblock);
|
||||
}
|
||||
|
||||
size_t BlockArray::append(Block *block)
|
||||
{
|
||||
if (!size)
|
||||
return size_t(-1);
|
||||
|
||||
++current;
|
||||
if (current >= size) current = 0;
|
||||
|
||||
int rc;
|
||||
rc = lseek(ion, current * blocksize, SEEK_SET); if (rc < 0) { perror("HistoryBuffer::add.seek"); setHistorySize(0); return size_t(-1); }
|
||||
rc = write(ion, block, blocksize); if (rc < 0) { perror("HistoryBuffer::add.write"); setHistorySize(0); return size_t(-1); }
|
||||
|
||||
length++;
|
||||
if (length > size) length = size;
|
||||
|
||||
++index;
|
||||
|
||||
delete block;
|
||||
return current;
|
||||
}
|
||||
|
||||
size_t BlockArray::newBlock()
|
||||
{
|
||||
if (!size)
|
||||
return size_t(-1);
|
||||
append(lastblock);
|
||||
|
||||
lastblock = new Block();
|
||||
return index + 1;
|
||||
}
|
||||
|
||||
Block *BlockArray::lastBlock() const
|
||||
{
|
||||
return lastblock;
|
||||
}
|
||||
|
||||
bool BlockArray::has(size_t i) const
|
||||
{
|
||||
if (i == index + 1)
|
||||
return true;
|
||||
|
||||
if (i > index)
|
||||
return false;
|
||||
if (index - i >= length)
|
||||
return false;
|
||||
return true;
|
||||
}
|
||||
|
||||
const Block* BlockArray::at(size_t i)
|
||||
{
|
||||
if (i == index + 1)
|
||||
return lastblock;
|
||||
|
||||
if (i == lastmap_index)
|
||||
return lastmap;
|
||||
|
||||
if (i > index) {
|
||||
qDebug() << "BlockArray::at() i > index\n";
|
||||
return 0;
|
||||
}
|
||||
|
||||
// if (index - i >= length) {
|
||||
// kDebug(1211) << "BlockArray::at() index - i >= length\n";
|
||||
// return 0;
|
||||
// }
|
||||
|
||||
size_t j = i; // (current - (index - i) + (index/size+1)*size) % size ;
|
||||
|
||||
assert(j < size);
|
||||
unmap();
|
||||
|
||||
Block *block = (Block*)mmap(0, blocksize, PROT_READ, MAP_PRIVATE, ion, j * blocksize);
|
||||
|
||||
if (block == (Block*)-1) { perror("mmap"); return 0; }
|
||||
|
||||
lastmap = block;
|
||||
lastmap_index = i;
|
||||
|
||||
return block;
|
||||
}
|
||||
|
||||
void BlockArray::unmap()
|
||||
{
|
||||
if (lastmap) {
|
||||
int res = munmap((char*)lastmap, blocksize);
|
||||
if (res < 0) perror("munmap");
|
||||
}
|
||||
lastmap = 0;
|
||||
lastmap_index = size_t(-1);
|
||||
}
|
||||
|
||||
bool BlockArray::setSize(size_t newsize)
|
||||
{
|
||||
return setHistorySize(newsize * 1024 / blocksize);
|
||||
}
|
||||
|
||||
bool BlockArray::setHistorySize(size_t newsize)
|
||||
{
|
||||
// kDebug(1211) << "setHistorySize " << size << " " << newsize;
|
||||
|
||||
if (size == newsize)
|
||||
return false;
|
||||
|
||||
unmap();
|
||||
|
||||
if (!newsize) {
|
||||
delete lastblock;
|
||||
lastblock = 0;
|
||||
if (ion >= 0) close(ion);
|
||||
ion = -1;
|
||||
current = size_t(-1);
|
||||
return true;
|
||||
}
|
||||
|
||||
if (!size) {
|
||||
FILE* tmp = tmpfile();
|
||||
if (!tmp) {
|
||||
perror("konsole: cannot open temp file.\n");
|
||||
} else {
|
||||
ion = dup(fileno(tmp));
|
||||
if (ion<0) {
|
||||
perror("konsole: cannot dup temp file.\n");
|
||||
fclose(tmp);
|
||||
}
|
||||
}
|
||||
if (ion < 0)
|
||||
return false;
|
||||
|
||||
assert(!lastblock);
|
||||
|
||||
lastblock = new Block();
|
||||
size = newsize;
|
||||
return false;
|
||||
}
|
||||
|
||||
if (newsize > size) {
|
||||
increaseBuffer();
|
||||
size = newsize;
|
||||
return false;
|
||||
} else {
|
||||
decreaseBuffer(newsize);
|
||||
ftruncate(ion, length*blocksize);
|
||||
size = newsize;
|
||||
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
void moveBlock(FILE *fion, int cursor, int newpos, char *buffer2)
|
||||
{
|
||||
int res = fseek(fion, cursor * blocksize, SEEK_SET);
|
||||
if (res)
|
||||
perror("fseek");
|
||||
res = fread(buffer2, blocksize, 1, fion);
|
||||
if (res != 1)
|
||||
perror("fread");
|
||||
|
||||
res = fseek(fion, newpos * blocksize, SEEK_SET);
|
||||
if (res)
|
||||
perror("fseek");
|
||||
res = fwrite(buffer2, blocksize, 1, fion);
|
||||
if (res != 1)
|
||||
perror("fwrite");
|
||||
// printf("moving block %d to %d\n", cursor, newpos);
|
||||
}
|
||||
|
||||
void BlockArray::decreaseBuffer(size_t newsize)
|
||||
{
|
||||
if (index < newsize) // still fits in whole
|
||||
return;
|
||||
|
||||
int offset = (current - (newsize - 1) + size) % size;
|
||||
|
||||
if (!offset)
|
||||
return;
|
||||
|
||||
// The Block constructor could do somthing in future...
|
||||
char *buffer1 = new char[blocksize];
|
||||
|
||||
FILE *fion = fdopen(dup(ion), "w+b");
|
||||
if (!fion) {
|
||||
delete [] buffer1;
|
||||
perror("fdopen/dup");
|
||||
return;
|
||||
}
|
||||
|
||||
int firstblock;
|
||||
if (current <= newsize) {
|
||||
firstblock = current + 1;
|
||||
} else {
|
||||
firstblock = 0;
|
||||
}
|
||||
|
||||
size_t oldpos;
|
||||
for (size_t i = 0, cursor=firstblock; i < newsize; i++) {
|
||||
oldpos = (size + cursor + offset) % size;
|
||||
moveBlock(fion, oldpos, cursor, buffer1);
|
||||
if (oldpos < newsize) {
|
||||
cursor = oldpos;
|
||||
} else
|
||||
cursor++;
|
||||
}
|
||||
|
||||
current = newsize - 1;
|
||||
length = newsize;
|
||||
|
||||
delete [] buffer1;
|
||||
|
||||
fclose(fion);
|
||||
|
||||
}
|
||||
|
||||
void BlockArray::increaseBuffer()
|
||||
{
|
||||
if (index < size) // not even wrapped once
|
||||
return;
|
||||
|
||||
int offset = (current + size + 1) % size;
|
||||
if (!offset) // no moving needed
|
||||
return;
|
||||
|
||||
// The Block constructor could do somthing in future...
|
||||
char *buffer1 = new char[blocksize];
|
||||
char *buffer2 = new char[blocksize];
|
||||
|
||||
int runs = 1;
|
||||
int bpr = size; // blocks per run
|
||||
|
||||
if (size % offset == 0) {
|
||||
bpr = size / offset;
|
||||
runs = offset;
|
||||
}
|
||||
|
||||
FILE *fion = fdopen(dup(ion), "w+b");
|
||||
if (!fion) {
|
||||
perror("fdopen/dup");
|
||||
delete [] buffer1;
|
||||
delete [] buffer2;
|
||||
return;
|
||||
}
|
||||
|
||||
int res;
|
||||
for (int i = 0; i < runs; i++)
|
||||
{
|
||||
// free one block in chain
|
||||
int firstblock = (offset + i) % size;
|
||||
res = fseek(fion, firstblock * blocksize, SEEK_SET);
|
||||
if (res)
|
||||
perror("fseek");
|
||||
res = fread(buffer1, blocksize, 1, fion);
|
||||
if (res != 1)
|
||||
perror("fread");
|
||||
int newpos = 0;
|
||||
for (int j = 1, cursor=firstblock; j < bpr; j++)
|
||||
{
|
||||
cursor = (cursor + offset) % size;
|
||||
newpos = (cursor - offset + size) % size;
|
||||
moveBlock(fion, cursor, newpos, buffer2);
|
||||
}
|
||||
res = fseek(fion, i * blocksize, SEEK_SET);
|
||||
if (res)
|
||||
perror("fseek");
|
||||
res = fwrite(buffer1, blocksize, 1, fion);
|
||||
if (res != 1)
|
||||
perror("fwrite");
|
||||
}
|
||||
current = size - 1;
|
||||
length = size;
|
||||
|
||||
delete [] buffer1;
|
||||
delete [] buffer2;
|
||||
|
||||
fclose(fion);
|
||||
|
||||
}
|
||||
|
|
@ -0,0 +1,544 @@
|
|||
/*
|
||||
This file is part of Konsole, an X terminal.
|
||||
|
||||
Copyright (C) 2007 Robert Knight <robertknight@gmail.com>
|
||||
Copyright (C) 1997,1998 by Lars Doelle <lars.doelle@on-line.de>
|
||||
Copyright (C) 1996 by Matthias Ettrich <ettrich@kde.org>
|
||||
|
||||
Rewritten for QT4 by e_k <e_k at users.sourceforge.net>, Copyright (C)2008
|
||||
|
||||
This program is free software; you can redistribute it and/or modify
|
||||
it under the terms of the GNU General Public License as published by
|
||||
the Free Software Foundation; either version 2 of the License, or
|
||||
(at your option) any later version.
|
||||
|
||||
This program is distributed in the hope that it will be useful,
|
||||
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
GNU General Public License for more details.
|
||||
|
||||
You should have received a copy of the GNU General Public License
|
||||
along with this program; if not, write to the Free Software
|
||||
Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
|
||||
02110-1301 USA.
|
||||
*/
|
||||
|
||||
// Own
|
||||
#include "Emulation.h"
|
||||
#include "Emulation.moc"
|
||||
|
||||
// System
|
||||
#include <assert.h>
|
||||
#include <stdio.h>
|
||||
#include <stdlib.h>
|
||||
#include <unistd.h>
|
||||
|
||||
// Qt
|
||||
#include <QtGui/QApplication>
|
||||
#include <QtGui/QClipboard>
|
||||
#include <QtCore/QHash>
|
||||
#include <QtGui/QKeyEvent>
|
||||
#include <QtCore/QRegExp>
|
||||
#include <QtCore/QTextStream>
|
||||
#include <QtCore/QThread>
|
||||
|
||||
#include <QtCore/QTime>
|
||||
|
||||
// Konsole
|
||||
#include "KeyboardTranslator.h"
|
||||
#include "Screen.h"
|
||||
#include "TerminalCharacterDecoder.h"
|
||||
#include "ScreenWindow.h"
|
||||
|
||||
using namespace Konsole;
|
||||
|
||||
/* ------------------------------------------------------------------------- */
|
||||
/* */
|
||||
/* Emulation */
|
||||
/* */
|
||||
/* ------------------------------------------------------------------------- */
|
||||
|
||||
//#define CNTL(c) ((c)-'@')
|
||||
|
||||
/*!
|
||||
*/
|
||||
|
||||
Emulation::Emulation() :
|
||||
_currentScreen(0),
|
||||
_codec(0),
|
||||
_decoder(0),
|
||||
_keyTranslator(0),
|
||||
_usesMouse(false)
|
||||
{
|
||||
|
||||
// create screens with a default size
|
||||
_screen[0] = new Screen(40,80);
|
||||
_screen[1] = new Screen(40,80);
|
||||
_currentScreen = _screen[0];
|
||||
|
||||
QObject::connect(&_bulkTimer1, SIGNAL(timeout()), this, SLOT(showBulk()) );
|
||||
QObject::connect(&_bulkTimer2, SIGNAL(timeout()), this, SLOT(showBulk()) );
|
||||
|
||||
// listen for mouse status changes
|
||||
connect( this , SIGNAL(programUsesMouseChanged(bool)) ,
|
||||
SLOT(usesMouseChanged(bool)) );
|
||||
}
|
||||
|
||||
bool Emulation::programUsesMouse() const
|
||||
{
|
||||
return _usesMouse;
|
||||
}
|
||||
|
||||
void Emulation::usesMouseChanged(bool usesMouse)
|
||||
{
|
||||
_usesMouse = usesMouse;
|
||||
}
|
||||
|
||||
ScreenWindow* Emulation::createWindow()
|
||||
{
|
||||
ScreenWindow* window = new ScreenWindow();
|
||||
window->setScreen(_currentScreen);
|
||||
_windows << window;
|
||||
|
||||
connect(window , SIGNAL(selectionChanged()),
|
||||
this , SLOT(bufferedUpdate()));
|
||||
|
||||
connect(this , SIGNAL(outputChanged()),
|
||||
window , SLOT(notifyOutputChanged()) );
|
||||
return window;
|
||||
}
|
||||
|
||||
/*!
|
||||
*/
|
||||
|
||||
Emulation::~Emulation()
|
||||
{
|
||||
QListIterator<ScreenWindow*> windowIter(_windows);
|
||||
|
||||
while (windowIter.hasNext())
|
||||
{
|
||||
delete windowIter.next();
|
||||
}
|
||||
|
||||
delete _screen[0];
|
||||
delete _screen[1];
|
||||
delete _decoder;
|
||||
}
|
||||
|
||||
/*! change between primary and alternate _screen
|
||||
*/
|
||||
|
||||
void Emulation::setScreen(int n)
|
||||
{
|
||||
Screen *old = _currentScreen;
|
||||
_currentScreen = _screen[n&1];
|
||||
if (_currentScreen != old)
|
||||
{
|
||||
old->setBusySelecting(false);
|
||||
|
||||
// tell all windows onto this emulation to switch to the newly active _screen
|
||||
QListIterator<ScreenWindow*> windowIter(_windows);
|
||||
while ( windowIter.hasNext() )
|
||||
{
|
||||
windowIter.next()->setScreen(_currentScreen);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void Emulation::clearHistory()
|
||||
{
|
||||
_screen[0]->setScroll( _screen[0]->getScroll() , false );
|
||||
}
|
||||
void Emulation::setHistory(const HistoryType& t)
|
||||
{
|
||||
_screen[0]->setScroll(t);
|
||||
|
||||
showBulk();
|
||||
}
|
||||
|
||||
const HistoryType& Emulation::history()
|
||||
{
|
||||
return _screen[0]->getScroll();
|
||||
}
|
||||
|
||||
void Emulation::setCodec(const QTextCodec * qtc)
|
||||
{
|
||||
Q_ASSERT( qtc );
|
||||
|
||||
_codec = qtc;
|
||||
delete _decoder;
|
||||
_decoder = _codec->makeDecoder();
|
||||
|
||||
emit useUtf8Request(utf8());
|
||||
}
|
||||
|
||||
void Emulation::setCodec(EmulationCodec codec)
|
||||
{
|
||||
if ( codec == Utf8Codec )
|
||||
setCodec( QTextCodec::codecForName("utf8") );
|
||||
else if ( codec == LocaleCodec )
|
||||
setCodec( QTextCodec::codecForLocale() );
|
||||
}
|
||||
|
||||
void Emulation::setKeyBindings(const QString& name)
|
||||
{
|
||||
_keyTranslator = KeyboardTranslatorManager::instance()->findTranslator(name);
|
||||
}
|
||||
|
||||
QString Emulation::keyBindings()
|
||||
{
|
||||
return _keyTranslator->name();
|
||||
}
|
||||
|
||||
|
||||
// Interpreting Codes ---------------------------------------------------------
|
||||
|
||||
/*
|
||||
This section deals with decoding the incoming character stream.
|
||||
Decoding means here, that the stream is first separated into `tokens'
|
||||
which are then mapped to a `meaning' provided as operations by the
|
||||
`Screen' class.
|
||||
*/
|
||||
|
||||
/*!
|
||||
*/
|
||||
|
||||
void Emulation::receiveChar(int c)
|
||||
// process application unicode input to terminal
|
||||
// this is a trivial scanner
|
||||
{
|
||||
c &= 0xff;
|
||||
switch (c)
|
||||
{
|
||||
case '\b' : _currentScreen->BackSpace(); break;
|
||||
case '\t' : _currentScreen->Tabulate(); break;
|
||||
case '\n' : _currentScreen->NewLine(); break;
|
||||
case '\r' : _currentScreen->Return(); break;
|
||||
case 0x07 : emit stateSet(NOTIFYBELL);
|
||||
break;
|
||||
default : _currentScreen->ShowCharacter(c); break;
|
||||
};
|
||||
}
|
||||
|
||||
/* ------------------------------------------------------------------------- */
|
||||
/* */
|
||||
/* Keyboard Handling */
|
||||
/* */
|
||||
/* ------------------------------------------------------------------------- */
|
||||
|
||||
/*!
|
||||
*/
|
||||
|
||||
void Emulation::sendKeyEvent( QKeyEvent* ev )
|
||||
{
|
||||
emit stateSet(NOTIFYNORMAL);
|
||||
|
||||
if (!ev->text().isEmpty())
|
||||
{ // A block of text
|
||||
// Note that the text is proper unicode.
|
||||
// We should do a conversion here, but since this
|
||||
// routine will never be used, we simply emit plain ascii.
|
||||
//emit sendBlock(ev->text().toAscii(),ev->text().length());
|
||||
emit sendData(ev->text().toUtf8(),ev->text().length());
|
||||
}
|
||||
}
|
||||
|
||||
void Emulation::sendString(const char*,int)
|
||||
{
|
||||
// default implementation does nothing
|
||||
}
|
||||
|
||||
void Emulation::sendMouseEvent(int /*buttons*/, int /*column*/, int /*row*/, int /*eventType*/)
|
||||
{
|
||||
// default implementation does nothing
|
||||
}
|
||||
|
||||
// Unblocking, Byte to Unicode translation --------------------------------- --
|
||||
|
||||
/*
|
||||
We are doing code conversion from locale to unicode first.
|
||||
TODO: Character composition from the old code. See #96536
|
||||
*/
|
||||
|
||||
void Emulation::receiveData(const char* text, int length)
|
||||
{
|
||||
emit stateSet(NOTIFYACTIVITY);
|
||||
|
||||
bufferedUpdate();
|
||||
|
||||
QString unicodeText = _decoder->toUnicode(text,length);
|
||||
|
||||
//send characters to terminal emulator
|
||||
for (int i=0;i<unicodeText.length();i++)
|
||||
{
|
||||
receiveChar(unicodeText[i].unicode());
|
||||
}
|
||||
|
||||
//look for z-modem indicator
|
||||
//-- someone who understands more about z-modems that I do may be able to move
|
||||
//this check into the above for loop?
|
||||
for (int i=0;i<length;i++)
|
||||
{
|
||||
if (text[i] == '\030')
|
||||
{
|
||||
if ((length-i-1 > 3) && (strncmp(text+i+1, "B00", 3) == 0))
|
||||
emit zmodemDetected();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
//OLDER VERSION
|
||||
//This version of onRcvBlock was commented out because
|
||||
// a) It decoded incoming characters one-by-one, which is slow in the current version of Qt (4.2 tech preview)
|
||||
// b) It messed up decoding of non-ASCII characters, with the result that (for example) chinese characters
|
||||
// were not printed properly.
|
||||
//
|
||||
//There is something about stopping the _decoder if "we get a control code halfway a multi-byte sequence" (see below)
|
||||
//which hasn't been ported into the newer function (above). Hopefully someone who understands this better
|
||||
//can find an alternative way of handling the check.
|
||||
|
||||
|
||||
/*void Emulation::onRcvBlock(const char *s, int len)
|
||||
{
|
||||
emit notifySessionState(NOTIFYACTIVITY);
|
||||
|
||||
bufferedUpdate();
|
||||
for (int i = 0; i < len; i++)
|
||||
{
|
||||
|
||||
QString result = _decoder->toUnicode(&s[i],1);
|
||||
int reslen = result.length();
|
||||
|
||||
// If we get a control code halfway a multi-byte sequence
|
||||
// we flush the _decoder and continue with the control code.
|
||||
if ((s[i] < 32) && (s[i] > 0))
|
||||
{
|
||||
// Flush _decoder
|
||||
while(!result.length())
|
||||
result = _decoder->toUnicode(&s[i],1);
|
||||
reslen = 1;
|
||||
result.resize(reslen);
|
||||
result[0] = QChar(s[i]);
|
||||
}
|
||||
|
||||
for (int j = 0; j < reslen; j++)
|
||||
{
|
||||
if (result[j].characterategory() == QChar::Mark_NonSpacing)
|
||||
_currentScreen->compose(result.mid(j,1));
|
||||
else
|
||||
onRcvChar(result[j].unicode());
|
||||
}
|
||||
if (s[i] == '\030')
|
||||
{
|
||||
if ((len-i-1 > 3) && (strncmp(s+i+1, "B00", 3) == 0))
|
||||
emit zmodemDetected();
|
||||
}
|
||||
}
|
||||
}*/
|
||||
|
||||
// Selection --------------------------------------------------------------- --
|
||||
|
||||
#if 0
|
||||
void Emulation::onSelectionBegin(const int x, const int y, const bool columnmode) {
|
||||
if (!connected) return;
|
||||
_currentScreen->setSelectionStart( x,y,columnmode);
|
||||
showBulk();
|
||||
}
|
||||
|
||||
void Emulation::onSelectionExtend(const int x, const int y) {
|
||||
if (!connected) return;
|
||||
_currentScreen->setSelectionEnd(x,y);
|
||||
showBulk();
|
||||
}
|
||||
|
||||
void Emulation::setSelection(const bool preserve_line_breaks) {
|
||||
if (!connected) return;
|
||||
QString t = _currentScreen->selectedText(preserve_line_breaks);
|
||||
if (!t.isNull())
|
||||
{
|
||||
QListIterator< TerminalDisplay* > viewIter(_views);
|
||||
|
||||
while (viewIter.hasNext())
|
||||
viewIter.next()->setSelection(t);
|
||||
}
|
||||
}
|
||||
|
||||
void Emulation::testIsSelected(const int x, const int y, bool &selected)
|
||||
{
|
||||
if (!connected) return;
|
||||
selected=_currentScreen->isSelected(x,y);
|
||||
}
|
||||
|
||||
void Emulation::clearSelection() {
|
||||
if (!connected) return;
|
||||
_currentScreen->clearSelection();
|
||||
showBulk();
|
||||
}
|
||||
|
||||
#endif
|
||||
|
||||
void Emulation::writeToStream( TerminalCharacterDecoder* _decoder ,
|
||||
int startLine ,
|
||||
int endLine)
|
||||
{
|
||||
_currentScreen->writeToStream(_decoder,startLine,endLine);
|
||||
}
|
||||
|
||||
int Emulation::lineCount()
|
||||
{
|
||||
// sum number of lines currently on _screen plus number of lines in history
|
||||
return _currentScreen->getLines() + _currentScreen->getHistLines();
|
||||
}
|
||||
|
||||
// Refreshing -------------------------------------------------------------- --
|
||||
|
||||
#define BULK_TIMEOUT1 10
|
||||
#define BULK_TIMEOUT2 40
|
||||
|
||||
/*!
|
||||
*/
|
||||
void Emulation::showBulk()
|
||||
{
|
||||
_bulkTimer1.stop();
|
||||
_bulkTimer2.stop();
|
||||
|
||||
emit outputChanged();
|
||||
|
||||
_currentScreen->resetScrolledLines();
|
||||
_currentScreen->resetDroppedLines();
|
||||
}
|
||||
|
||||
void Emulation::bufferedUpdate()
|
||||
{
|
||||
_bulkTimer1.setSingleShot(true);
|
||||
_bulkTimer1.start(BULK_TIMEOUT1);
|
||||
if (!_bulkTimer2.isActive())
|
||||
{
|
||||
_bulkTimer2.setSingleShot(true);
|
||||
_bulkTimer2.start(BULK_TIMEOUT2);
|
||||
}
|
||||
}
|
||||
|
||||
char Emulation::getErase() const
|
||||
{
|
||||
return '\b';
|
||||
}
|
||||
|
||||
void Emulation::setImageSize(int lines, int columns)
|
||||
{
|
||||
//kDebug() << "Resizing image to: " << lines << "by" << columns << QTime::currentTime().msec();
|
||||
Q_ASSERT( lines > 0 );
|
||||
Q_ASSERT( columns > 0 );
|
||||
|
||||
_screen[0]->resizeImage(lines,columns);
|
||||
_screen[1]->resizeImage(lines,columns);
|
||||
|
||||
emit imageSizeChanged(lines,columns);
|
||||
|
||||
bufferedUpdate();
|
||||
}
|
||||
|
||||
QSize Emulation::imageSize()
|
||||
{
|
||||
return QSize(_currentScreen->getColumns(), _currentScreen->getLines());
|
||||
}
|
||||
|
||||
ushort ExtendedCharTable::extendedCharHash(ushort* unicodePoints , ushort length) const
|
||||
{
|
||||
ushort hash = 0;
|
||||
for ( ushort i = 0 ; i < length ; i++ )
|
||||
{
|
||||
hash = 31*hash + unicodePoints[i];
|
||||
}
|
||||
return hash;
|
||||
}
|
||||
bool ExtendedCharTable::extendedCharMatch(ushort hash , ushort* unicodePoints , ushort length) const
|
||||
{
|
||||
ushort* entry = extendedCharTable[hash];
|
||||
|
||||
// compare given length with stored sequence length ( given as the first ushort in the
|
||||
// stored buffer )
|
||||
if ( entry == 0 || entry[0] != length )
|
||||
return false;
|
||||
// if the lengths match, each character must be checked. the stored buffer starts at
|
||||
// entry[1]
|
||||
for ( int i = 0 ; i < length ; i++ )
|
||||
{
|
||||
if ( entry[i+1] != unicodePoints[i] )
|
||||
return false;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
ushort ExtendedCharTable::createExtendedChar(ushort* unicodePoints , ushort length)
|
||||
{
|
||||
// look for this sequence of points in the table
|
||||
ushort hash = extendedCharHash(unicodePoints,length);
|
||||
|
||||
// check existing entry for match
|
||||
while ( extendedCharTable.contains(hash) )
|
||||
{
|
||||
if ( extendedCharMatch(hash,unicodePoints,length) )
|
||||
{
|
||||
// this sequence already has an entry in the table,
|
||||
// return its hash
|
||||
return hash;
|
||||
}
|
||||
else
|
||||
{
|
||||
// if hash is already used by another, different sequence of unicode character
|
||||
// points then try next hash
|
||||
hash++;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
// add the new sequence to the table and
|
||||
// return that index
|
||||
ushort* buffer = new ushort[length+1];
|
||||
buffer[0] = length;
|
||||
for ( int i = 0 ; i < length ; i++ )
|
||||
buffer[i+1] = unicodePoints[i];
|
||||
|
||||
extendedCharTable.insert(hash,buffer);
|
||||
|
||||
return hash;
|
||||
}
|
||||
|
||||
ushort* ExtendedCharTable::lookupExtendedChar(ushort hash , ushort& length) const
|
||||
{
|
||||
// lookup index in table and if found, set the length
|
||||
// argument and return a pointer to the character sequence
|
||||
|
||||
ushort* buffer = extendedCharTable[hash];
|
||||
if ( buffer )
|
||||
{
|
||||
length = buffer[0];
|
||||
return buffer+1;
|
||||
}
|
||||
else
|
||||
{
|
||||
length = 0;
|
||||
return 0;
|
||||
}
|
||||
}
|
||||
|
||||
ExtendedCharTable::ExtendedCharTable()
|
||||
{
|
||||
}
|
||||
ExtendedCharTable::~ExtendedCharTable()
|
||||
{
|
||||
// free all allocated character buffers
|
||||
QHashIterator<ushort,ushort*> iter(extendedCharTable);
|
||||
while ( iter.hasNext() )
|
||||
{
|
||||
iter.next();
|
||||
delete[] iter.value();
|
||||
}
|
||||
}
|
||||
|
||||
// global instance
|
||||
ExtendedCharTable ExtendedCharTable::instance;
|
||||
|
||||
|
||||
//#include "moc_Emulation.cpp"
|
||||
|
|
@ -0,0 +1,564 @@
|
|||
/*
|
||||
Copyright (C) 2007 by Robert Knight <robertknight@gmail.com>
|
||||
|
||||
Rewritten for QT4 by e_k <e_k at users.sourceforge.net>, Copyright (C)2008
|
||||
|
||||
This program is free software; you can redistribute it and/or modify
|
||||
it under the terms of the GNU General Public License as published by
|
||||
the Free Software Foundation; either version 2 of the License, or
|
||||
(at your option) any later version.
|
||||
|
||||
This program is distributed in the hope that it will be useful,
|
||||
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
GNU General Public License for more details.
|
||||
|
||||
You should have received a copy of the GNU General Public License
|
||||
along with this program; if not, write to the Free Software
|
||||
Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
|
||||
02110-1301 USA.
|
||||
*/
|
||||
|
||||
// Own
|
||||
#include "Filter.h"
|
||||
#include "Filter.moc"
|
||||
|
||||
|
||||
// System
|
||||
#include <iostream>
|
||||
|
||||
// Qt
|
||||
#include <QtGui/QAction>
|
||||
#include <QtGui/QApplication>
|
||||
#include <QtGui/QClipboard>
|
||||
#include <QtCore/QString>
|
||||
|
||||
#include <QtCore/QSharedData>
|
||||
#include <QtCore>
|
||||
|
||||
// KDE
|
||||
//#include <KLocale>
|
||||
//#include <KRun>
|
||||
|
||||
// Konsole
|
||||
#include "TerminalCharacterDecoder.h"
|
||||
|
||||
using namespace Konsole;
|
||||
|
||||
FilterChain::~FilterChain()
|
||||
{
|
||||
QMutableListIterator<Filter*> iter(*this);
|
||||
|
||||
while ( iter.hasNext() )
|
||||
{
|
||||
Filter* filter = iter.next();
|
||||
iter.remove();
|
||||
delete filter;
|
||||
}
|
||||
}
|
||||
|
||||
void FilterChain::addFilter(Filter* filter)
|
||||
{
|
||||
append(filter);
|
||||
}
|
||||
void FilterChain::removeFilter(Filter* filter)
|
||||
{
|
||||
removeAll(filter);
|
||||
}
|
||||
bool FilterChain::containsFilter(Filter* filter)
|
||||
{
|
||||
return contains(filter);
|
||||
}
|
||||
void FilterChain::reset()
|
||||
{
|
||||
QListIterator<Filter*> iter(*this);
|
||||
while (iter.hasNext())
|
||||
iter.next()->reset();
|
||||
}
|
||||
void FilterChain::setBuffer(const QString* buffer , const QList<int>* linePositions)
|
||||
{
|
||||
QListIterator<Filter*> iter(*this);
|
||||
while (iter.hasNext())
|
||||
iter.next()->setBuffer(buffer,linePositions);
|
||||
}
|
||||
void FilterChain::process()
|
||||
{
|
||||
QListIterator<Filter*> iter(*this);
|
||||
while (iter.hasNext())
|
||||
iter.next()->process();
|
||||
}
|
||||
void FilterChain::clear()
|
||||
{
|
||||
QList<Filter*>::clear();
|
||||
}
|
||||
Filter::HotSpot* FilterChain::hotSpotAt(int line , int column) const
|
||||
{
|
||||
QListIterator<Filter*> iter(*this);
|
||||
while (iter.hasNext())
|
||||
{
|
||||
Filter* filter = iter.next();
|
||||
Filter::HotSpot* spot = filter->hotSpotAt(line,column);
|
||||
if ( spot != 0 )
|
||||
{
|
||||
return spot;
|
||||
}
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
QList<Filter::HotSpot*> FilterChain::hotSpots() const
|
||||
{
|
||||
QList<Filter::HotSpot*> list;
|
||||
QListIterator<Filter*> iter(*this);
|
||||
while (iter.hasNext())
|
||||
{
|
||||
Filter* filter = iter.next();
|
||||
list << filter->hotSpots();
|
||||
}
|
||||
return list;
|
||||
}
|
||||
//QList<Filter::HotSpot*> FilterChain::hotSpotsAtLine(int line) const;
|
||||
|
||||
TerminalImageFilterChain::TerminalImageFilterChain()
|
||||
: _buffer(0)
|
||||
, _linePositions(0)
|
||||
{
|
||||
}
|
||||
|
||||
TerminalImageFilterChain::~TerminalImageFilterChain()
|
||||
{
|
||||
delete _buffer;
|
||||
delete _linePositions;
|
||||
}
|
||||
|
||||
void TerminalImageFilterChain::setImage(const Character* const image , int lines , int columns, const QVector<LineProperty>& lineProperties)
|
||||
{
|
||||
//qDebug("%s %d", __FILE__, __LINE__);
|
||||
if (empty())
|
||||
return;
|
||||
//qDebug("%s %d", __FILE__, __LINE__);
|
||||
|
||||
// reset all filters and hotspots
|
||||
reset();
|
||||
//qDebug("%s %d", __FILE__, __LINE__);
|
||||
|
||||
PlainTextDecoder decoder;
|
||||
decoder.setTrailingWhitespace(false);
|
||||
|
||||
//qDebug("%s %d", __FILE__, __LINE__);
|
||||
// setup new shared buffers for the filters to process on
|
||||
QString* newBuffer = new QString();
|
||||
QList<int>* newLinePositions = new QList<int>();
|
||||
setBuffer( newBuffer , newLinePositions );
|
||||
|
||||
// free the old buffers
|
||||
delete _buffer;
|
||||
delete _linePositions;
|
||||
|
||||
_buffer = newBuffer;
|
||||
_linePositions = newLinePositions;
|
||||
|
||||
QTextStream lineStream(_buffer);
|
||||
decoder.begin(&lineStream);
|
||||
|
||||
for (int i=0 ; i < lines ; i++)
|
||||
{
|
||||
_linePositions->append(_buffer->length());
|
||||
decoder.decodeLine(image + i*columns,columns,LINE_DEFAULT);
|
||||
|
||||
// pretend that each line ends with a newline character.
|
||||
// this prevents a link that occurs at the end of one line
|
||||
// being treated as part of a link that occurs at the start of the next line
|
||||
//
|
||||
// the downside is that links which are spread over more than one line are not
|
||||
// highlighted.
|
||||
//
|
||||
// TODO - Use the "line wrapped" attribute associated with lines in a
|
||||
// terminal image to avoid adding this imaginary character for wrapped
|
||||
// lines
|
||||
if ( !(lineProperties.value(i,LINE_DEFAULT) & LINE_WRAPPED) )
|
||||
lineStream << QChar('\n');
|
||||
}
|
||||
decoder.end();
|
||||
// qDebug("%s %d", __FILE__, __LINE__);
|
||||
}
|
||||
|
||||
Filter::Filter() :
|
||||
_linePositions(0),
|
||||
_buffer(0)
|
||||
{
|
||||
}
|
||||
|
||||
Filter::~Filter()
|
||||
{
|
||||
QListIterator<HotSpot*> iter(_hotspotList);
|
||||
while (iter.hasNext())
|
||||
{
|
||||
delete iter.next();
|
||||
}
|
||||
}
|
||||
void Filter::reset()
|
||||
{
|
||||
_hotspots.clear();
|
||||
_hotspotList.clear();
|
||||
}
|
||||
|
||||
void Filter::setBuffer(const QString* buffer , const QList<int>* linePositions)
|
||||
{
|
||||
_buffer = buffer;
|
||||
_linePositions = linePositions;
|
||||
}
|
||||
|
||||
void Filter::getLineColumn(int position , int& startLine , int& startColumn)
|
||||
{
|
||||
Q_ASSERT( _linePositions );
|
||||
Q_ASSERT( _buffer );
|
||||
|
||||
|
||||
for (int i = 0 ; i < _linePositions->count() ; i++)
|
||||
{
|
||||
//kDebug() << "line position at " << i << " = " << _linePositions[i];
|
||||
int nextLine = 0;
|
||||
|
||||
if ( i == _linePositions->count()-1 )
|
||||
{
|
||||
nextLine = _buffer->length() + 1;
|
||||
}
|
||||
else
|
||||
{
|
||||
nextLine = _linePositions->value(i+1);
|
||||
}
|
||||
|
||||
// kDebug() << "pos - " << position << " line pos(" << i<< ") " << _linePositions->value(i) <<
|
||||
// " next = " << nextLine << " buffer len = " << _buffer->length();
|
||||
|
||||
if ( _linePositions->value(i) <= position && position < nextLine )
|
||||
{
|
||||
startLine = i;
|
||||
startColumn = position - _linePositions->value(i);
|
||||
return;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
/*void Filter::addLine(const QString& text)
|
||||
{
|
||||
_linePositions << _buffer.length();
|
||||
_buffer.append(text);
|
||||
}*/
|
||||
|
||||
const QString* Filter::buffer()
|
||||
{
|
||||
return _buffer;
|
||||
}
|
||||
Filter::HotSpot::~HotSpot()
|
||||
{
|
||||
}
|
||||
void Filter::addHotSpot(HotSpot* spot)
|
||||
{
|
||||
_hotspotList << spot;
|
||||
|
||||
for (int line = spot->startLine() ; line <= spot->endLine() ; line++)
|
||||
{
|
||||
_hotspots.insert(line,spot);
|
||||
}
|
||||
}
|
||||
QList<Filter::HotSpot*> Filter::hotSpots() const
|
||||
{
|
||||
return _hotspotList;
|
||||
}
|
||||
QList<Filter::HotSpot*> Filter::hotSpotsAtLine(int line) const
|
||||
{
|
||||
return _hotspots.values(line);
|
||||
}
|
||||
|
||||
Filter::HotSpot* Filter::hotSpotAt(int line , int column) const
|
||||
{
|
||||
QListIterator<HotSpot*> spotIter(_hotspots.values(line));
|
||||
|
||||
while (spotIter.hasNext())
|
||||
{
|
||||
HotSpot* spot = spotIter.next();
|
||||
|
||||
if ( spot->startLine() == line && spot->startColumn() > column )
|
||||
continue;
|
||||
if ( spot->endLine() == line && spot->endColumn() < column )
|
||||
continue;
|
||||
|
||||
return spot;
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
Filter::HotSpot::HotSpot(int startLine , int startColumn , int endLine , int endColumn)
|
||||
: _startLine(startLine)
|
||||
, _startColumn(startColumn)
|
||||
, _endLine(endLine)
|
||||
, _endColumn(endColumn)
|
||||
, _type(NotSpecified)
|
||||
{
|
||||
}
|
||||
QString Filter::HotSpot::tooltip() const
|
||||
{
|
||||
return QString();
|
||||
}
|
||||
QList<QAction*> Filter::HotSpot::actions()
|
||||
{
|
||||
return QList<QAction*>();
|
||||
}
|
||||
int Filter::HotSpot::startLine() const
|
||||
{
|
||||
return _startLine;
|
||||
}
|
||||
int Filter::HotSpot::endLine() const
|
||||
{
|
||||
return _endLine;
|
||||
}
|
||||
int Filter::HotSpot::startColumn() const
|
||||
{
|
||||
return _startColumn;
|
||||
}
|
||||
int Filter::HotSpot::endColumn() const
|
||||
{
|
||||
return _endColumn;
|
||||
}
|
||||
Filter::HotSpot::Type Filter::HotSpot::type() const
|
||||
{
|
||||
return _type;
|
||||
}
|
||||
void Filter::HotSpot::setType(Type type)
|
||||
{
|
||||
_type = type;
|
||||
}
|
||||
|
||||
RegExpFilter::RegExpFilter()
|
||||
{
|
||||
}
|
||||
|
||||
RegExpFilter::HotSpot::HotSpot(int startLine,int startColumn,int endLine,int endColumn)
|
||||
: Filter::HotSpot(startLine,startColumn,endLine,endColumn)
|
||||
{
|
||||
setType(Marker);
|
||||
}
|
||||
|
||||
void RegExpFilter::HotSpot::activate(QObject*)
|
||||
{
|
||||
}
|
||||
|
||||
void RegExpFilter::HotSpot::setCapturedTexts(const QStringList& texts)
|
||||
{
|
||||
_capturedTexts = texts;
|
||||
}
|
||||
QStringList RegExpFilter::HotSpot::capturedTexts() const
|
||||
{
|
||||
return _capturedTexts;
|
||||
}
|
||||
|
||||
void RegExpFilter::setRegExp(const QRegExp& regExp)
|
||||
{
|
||||
_searchText = regExp;
|
||||
}
|
||||
QRegExp RegExpFilter::regExp() const
|
||||
{
|
||||
return _searchText;
|
||||
}
|
||||
/*void RegExpFilter::reset(int)
|
||||
{
|
||||
_buffer = QString();
|
||||
}*/
|
||||
void RegExpFilter::process()
|
||||
{
|
||||
int pos = 0;
|
||||
const QString* text = buffer();
|
||||
|
||||
Q_ASSERT( text );
|
||||
|
||||
// ignore any regular expressions which match an empty string.
|
||||
// otherwise the while loop below will run indefinitely
|
||||
static const QString emptyString("");
|
||||
if ( _searchText.exactMatch(emptyString) )
|
||||
return;
|
||||
|
||||
while(pos >= 0)
|
||||
{
|
||||
pos = _searchText.indexIn(*text,pos);
|
||||
|
||||
if ( pos >= 0 )
|
||||
{
|
||||
|
||||
int startLine = 0;
|
||||
int endLine = 0;
|
||||
int startColumn = 0;
|
||||
int endColumn = 0;
|
||||
|
||||
|
||||
//kDebug() << "pos from " << pos << " to " << pos + _searchText.matchedLength();
|
||||
|
||||
getLineColumn(pos,startLine,startColumn);
|
||||
getLineColumn(pos + _searchText.matchedLength(),endLine,endColumn);
|
||||
|
||||
//kDebug() << "start " << startLine << " / " << startColumn;
|
||||
//kDebug() << "end " << endLine << " / " << endColumn;
|
||||
|
||||
RegExpFilter::HotSpot* spot = newHotSpot(startLine,startColumn,
|
||||
endLine,endColumn);
|
||||
spot->setCapturedTexts(_searchText.capturedTexts());
|
||||
|
||||
addHotSpot( spot );
|
||||
pos += _searchText.matchedLength();
|
||||
|
||||
// if matchedLength == 0, the program will get stuck in an infinite loop
|
||||
Q_ASSERT( _searchText.matchedLength() > 0 );
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
RegExpFilter::HotSpot* RegExpFilter::newHotSpot(int startLine,int startColumn,
|
||||
int endLine,int endColumn)
|
||||
{
|
||||
return new RegExpFilter::HotSpot(startLine,startColumn,
|
||||
endLine,endColumn);
|
||||
}
|
||||
RegExpFilter::HotSpot* UrlFilter::newHotSpot(int startLine,int startColumn,int endLine,
|
||||
int endColumn)
|
||||
{
|
||||
return new UrlFilter::HotSpot(startLine,startColumn,
|
||||
endLine,endColumn);
|
||||
}
|
||||
UrlFilter::HotSpot::HotSpot(int startLine,int startColumn,int endLine,int endColumn)
|
||||
: RegExpFilter::HotSpot(startLine,startColumn,endLine,endColumn)
|
||||
, _urlObject(new FilterObject(this))
|
||||
{
|
||||
setType(Link);
|
||||
}
|
||||
QString UrlFilter::HotSpot::tooltip() const
|
||||
{
|
||||
QString url = capturedTexts().first();
|
||||
|
||||
const UrlType kind = urlType();
|
||||
|
||||
if ( kind == StandardUrl )
|
||||
return QString();
|
||||
else if ( kind == Email )
|
||||
return QString();
|
||||
else
|
||||
return QString();
|
||||
}
|
||||
UrlFilter::HotSpot::UrlType UrlFilter::HotSpot::urlType() const
|
||||
{
|
||||
QString url = capturedTexts().first();
|
||||
|
||||
if ( FullUrlRegExp.exactMatch(url) )
|
||||
return StandardUrl;
|
||||
else if ( EmailAddressRegExp.exactMatch(url) )
|
||||
return Email;
|
||||
else
|
||||
return Unknown;
|
||||
}
|
||||
|
||||
void UrlFilter::HotSpot::activate(QObject* object)
|
||||
{
|
||||
QString url = capturedTexts().first();
|
||||
|
||||
const UrlType kind = urlType();
|
||||
|
||||
const QString& actionName = object ? object->objectName() : QString();
|
||||
|
||||
if ( actionName == "copy-action" )
|
||||
{
|
||||
//kDebug() << "Copying url to clipboard:" << url;
|
||||
|
||||
QApplication::clipboard()->setText(url);
|
||||
return;
|
||||
}
|
||||
|
||||
if ( !object || actionName == "open-action" )
|
||||
{
|
||||
if ( kind == StandardUrl )
|
||||
{
|
||||
// if the URL path does not include the protocol ( eg. "www.kde.org" ) then
|
||||
// prepend http:// ( eg. "www.kde.org" --> "http://www.kde.org" )
|
||||
if (!url.contains("://"))
|
||||
{
|
||||
url.prepend("http://");
|
||||
}
|
||||
}
|
||||
else if ( kind == Email )
|
||||
{
|
||||
url.prepend("mailto:");
|
||||
}
|
||||
|
||||
// new KRun(url,QApplication::activeWindow());
|
||||
}
|
||||
}
|
||||
|
||||
// Note: Altering these regular expressions can have a major effect on the performance of the filters
|
||||
// used for finding URLs in the text, especially if they are very general and could match very long
|
||||
// pieces of text.
|
||||
// Please be careful when altering them.
|
||||
|
||||
//regexp matches:
|
||||
// full url:
|
||||
// protocolname:// or www. followed by anything other than whitespaces, <, >, ' or ", and ends before whitespaces, <, >, ', ", ], !, comma and dot
|
||||
const QRegExp UrlFilter::FullUrlRegExp("(www\\.(?!\\.)|[a-z][a-z0-9+.-]*://)[^\\s<>'\"]+[^!,\\.\\s<>'\"\\]]");
|
||||
// email address:
|
||||
// [word chars, dots or dashes]@[word chars, dots or dashes].[word chars]
|
||||
const QRegExp UrlFilter::EmailAddressRegExp("\\b(\\w|\\.|-)+@(\\w|\\.|-)+\\.\\w+\\b");
|
||||
|
||||
// matches full url or email address
|
||||
const QRegExp UrlFilter::CompleteUrlRegExp('('+FullUrlRegExp.pattern()+'|'+
|
||||
EmailAddressRegExp.pattern()+')');
|
||||
|
||||
UrlFilter::UrlFilter()
|
||||
{
|
||||
setRegExp( CompleteUrlRegExp );
|
||||
}
|
||||
UrlFilter::HotSpot::~HotSpot()
|
||||
{
|
||||
delete _urlObject;
|
||||
}
|
||||
void FilterObject::activated()
|
||||
{
|
||||
_filter->activate(sender());
|
||||
}
|
||||
QList<QAction*> UrlFilter::HotSpot::actions()
|
||||
{
|
||||
QList<QAction*> list;
|
||||
|
||||
const UrlType kind = urlType();
|
||||
|
||||
QAction* openAction = new QAction(_urlObject);
|
||||
QAction* copyAction = new QAction(_urlObject);;
|
||||
|
||||
Q_ASSERT( kind == StandardUrl || kind == Email );
|
||||
|
||||
if ( kind == StandardUrl )
|
||||
{
|
||||
openAction->setText(("Open Link"));
|
||||
copyAction->setText(("Copy Link Address"));
|
||||
}
|
||||
else if ( kind == Email )
|
||||
{
|
||||
openAction->setText(("Send Email To..."));
|
||||
copyAction->setText(("Copy Email Address"));
|
||||
}
|
||||
|
||||
// object names are set here so that the hotspot performs the
|
||||
// correct action when activated() is called with the triggered
|
||||
// action passed as a parameter.
|
||||
openAction->setObjectName("open-action");
|
||||
copyAction->setObjectName("copy-action");
|
||||
|
||||
QObject::connect( openAction , SIGNAL(triggered()) , _urlObject , SLOT(activated()) );
|
||||
QObject::connect( copyAction , SIGNAL(triggered()) , _urlObject , SLOT(activated()) );
|
||||
|
||||
list << openAction;
|
||||
list << copyAction;
|
||||
|
||||
return list;
|
||||
}
|
||||
|
||||
//#include "moc_Filter.cpp"
|
|
@ -0,0 +1,698 @@
|
|||
/*
|
||||
This file is part of Konsole, an X terminal.
|
||||
Copyright (C) 1997,1998 by Lars Doelle <lars.doelle@on-line.de>
|
||||
|
||||
Rewritten for QT4 by e_k <e_k at users.sourceforge.net>, Copyright (C)2008
|
||||
|
||||
This program is free software; you can redistribute it and/or modify
|
||||
it under the terms of the GNU General Public License as published by
|
||||
the Free Software Foundation; either version 2 of the License, or
|
||||
(at your option) any later version.
|
||||
|
||||
This program is distributed in the hope that it will be useful,
|
||||
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
GNU General Public License for more details.
|
||||
|
||||
You should have received a copy of the GNU General Public License
|
||||
along with this program; if not, write to the Free Software
|
||||
Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
|
||||
02110-1301 USA.
|
||||
*/
|
||||
|
||||
// Own
|
||||
#include "History.h"
|
||||
|
||||
// System
|
||||
#include <iostream>
|
||||
#include <stdlib.h>
|
||||
#include <assert.h>
|
||||
#include <stdio.h>
|
||||
#include <sys/types.h>
|
||||
#include <sys/mman.h>
|
||||
#include <unistd.h>
|
||||
#include <errno.h>
|
||||
|
||||
|
||||
// Reasonable line size
|
||||
#define LINE_SIZE 1024
|
||||
|
||||
using namespace Konsole;
|
||||
|
||||
/*
|
||||
An arbitrary long scroll.
|
||||
|
||||
One can modify the scroll only by adding either cells
|
||||
or newlines, but access it randomly.
|
||||
|
||||
The model is that of an arbitrary wide typewriter scroll
|
||||
in that the scroll is a serie of lines and each line is
|
||||
a serie of cells with no overwriting permitted.
|
||||
|
||||
The implementation provides arbitrary length and numbers
|
||||
of cells and line/column indexed read access to the scroll
|
||||
at constant costs.
|
||||
|
||||
KDE4: Can we use QTemporaryFile here, instead of KTempFile?
|
||||
|
||||
FIXME: some complain about the history buffer comsuming the
|
||||
memory of their machines. This problem is critical
|
||||
since the history does not behave gracefully in cases
|
||||
where the memory is used up completely.
|
||||
|
||||
I put in a workaround that should handle it problem
|
||||
now gracefully. I'm not satisfied with the solution.
|
||||
|
||||
FIXME: Terminating the history is not properly indicated
|
||||
in the menu. We should throw a signal.
|
||||
|
||||
FIXME: There is noticeable decrease in speed, also. Perhaps,
|
||||
there whole feature needs to be revisited therefore.
|
||||
Disadvantage of a more elaborated, say block-oriented
|
||||
scheme with wrap around would be it's complexity.
|
||||
*/
|
||||
|
||||
//FIXME: tempory replacement for tmpfile
|
||||
// this is here one for debugging purpose.
|
||||
|
||||
//#define tmpfile xTmpFile
|
||||
|
||||
// History File ///////////////////////////////////////////
|
||||
|
||||
/*
|
||||
A Row(X) data type which allows adding elements to the end.
|
||||
*/
|
||||
|
||||
HistoryFile::HistoryFile()
|
||||
: ion(-1),
|
||||
length(0),
|
||||
fileMap(0)
|
||||
{
|
||||
if (tmpFile.open())
|
||||
{
|
||||
tmpFile.setAutoRemove(true);
|
||||
ion = tmpFile.handle();
|
||||
}
|
||||
}
|
||||
|
||||
HistoryFile::~HistoryFile()
|
||||
{
|
||||
if (fileMap)
|
||||
unmap();
|
||||
}
|
||||
|
||||
//TODO: Mapping the entire file in will cause problems if the history file becomes exceedingly large,
|
||||
//(ie. larger than available memory). HistoryFile::map() should only map in sections of the file at a time,
|
||||
//to avoid this.
|
||||
void HistoryFile::map()
|
||||
{
|
||||
assert( fileMap == 0 );
|
||||
|
||||
fileMap = (char*)mmap( 0 , length , PROT_READ , MAP_PRIVATE , ion , 0 );
|
||||
|
||||
//if mmap'ing fails, fall back to the read-lseek combination
|
||||
if ( fileMap == MAP_FAILED )
|
||||
{
|
||||
readWriteBalance = 0;
|
||||
fileMap = 0;
|
||||
qDebug() << ": mmap'ing history failed. errno = " << errno;
|
||||
}
|
||||
}
|
||||
|
||||
void HistoryFile::unmap()
|
||||
{
|
||||
int result = munmap( fileMap , length );
|
||||
assert( result == 0 );
|
||||
|
||||
fileMap = 0;
|
||||
}
|
||||
|
||||
bool HistoryFile::isMapped()
|
||||
{
|
||||
return (fileMap != 0);
|
||||
}
|
||||
|
||||
void HistoryFile::add(const unsigned char* bytes, int len)
|
||||
{
|
||||
if ( fileMap )
|
||||
unmap();
|
||||
|
||||
readWriteBalance++;
|
||||
|
||||
int rc = 0;
|
||||
|
||||
rc = lseek(ion,length,SEEK_SET); if (rc < 0) { perror("HistoryFile::add.seek"); return; }
|
||||
rc = write(ion,bytes,len); if (rc < 0) { perror("HistoryFile::add.write"); return; }
|
||||
length += rc;
|
||||
}
|
||||
|
||||
void HistoryFile::get(unsigned char* bytes, int len, int loc)
|
||||
{
|
||||
//count number of get() calls vs. number of add() calls.
|
||||
//If there are many more get() calls compared with add()
|
||||
//calls (decided by using MAP_THRESHOLD) then mmap the log
|
||||
//file to improve performance.
|
||||
readWriteBalance--;
|
||||
if ( !fileMap && readWriteBalance < MAP_THRESHOLD )
|
||||
map();
|
||||
|
||||
if ( fileMap )
|
||||
{
|
||||
for (int i=0;i<len;i++)
|
||||
bytes[i]=fileMap[loc+i];
|
||||
}
|
||||
else
|
||||
{
|
||||
int rc = 0;
|
||||
|
||||
if (loc < 0 || len < 0 || loc + len > length)
|
||||
fprintf(stderr,"getHist(...,%d,%d): invalid args.\n",len,loc);
|
||||
rc = lseek(ion,loc,SEEK_SET); if (rc < 0) { perror("HistoryFile::get.seek"); return; }
|
||||
rc = read(ion,bytes,len); if (rc < 0) { perror("HistoryFile::get.read"); return; }
|
||||
}
|
||||
}
|
||||
|
||||
int HistoryFile::len()
|
||||
{
|
||||
return length;
|
||||
}
|
||||
|
||||
|
||||
// History Scroll abstract base class //////////////////////////////////////
|
||||
|
||||
|
||||
HistoryScroll::HistoryScroll(HistoryType* t)
|
||||
: m_histType(t)
|
||||
{
|
||||
}
|
||||
|
||||
HistoryScroll::~HistoryScroll()
|
||||
{
|
||||
delete m_histType;
|
||||
}
|
||||
|
||||
bool HistoryScroll::hasScroll()
|
||||
{
|
||||
return true;
|
||||
}
|
||||
|
||||
// History Scroll File //////////////////////////////////////
|
||||
|
||||
/*
|
||||
The history scroll makes a Row(Row(Cell)) from
|
||||
two history buffers. The index buffer contains
|
||||
start of line positions which refere to the cells
|
||||
buffer.
|
||||
|
||||
Note that index[0] addresses the second line
|
||||
(line #1), while the first line (line #0) starts
|
||||
at 0 in cells.
|
||||
*/
|
||||
|
||||
HistoryScrollFile::HistoryScrollFile(const QString &logFileName)
|
||||
: HistoryScroll(new HistoryTypeFile(logFileName)),
|
||||
m_logFileName(logFileName)
|
||||
{
|
||||
}
|
||||
|
||||
HistoryScrollFile::~HistoryScrollFile()
|
||||
{
|
||||
}
|
||||
|
||||
int HistoryScrollFile::getLines()
|
||||
{
|
||||
return index.len() / sizeof(int);
|
||||
}
|
||||
|
||||
int HistoryScrollFile::getLineLen(int lineno)
|
||||
{
|
||||
return (startOfLine(lineno+1) - startOfLine(lineno)) / sizeof(Character);
|
||||
}
|
||||
|
||||
bool HistoryScrollFile::isWrappedLine(int lineno)
|
||||
{
|
||||
if (lineno>=0 && lineno <= getLines()) {
|
||||
unsigned char flag;
|
||||
lineflags.get((unsigned char*)&flag,sizeof(unsigned char),(lineno)*sizeof(unsigned char));
|
||||
return flag;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
int HistoryScrollFile::startOfLine(int lineno)
|
||||
{
|
||||
if (lineno <= 0) return 0;
|
||||
if (lineno <= getLines())
|
||||
{
|
||||
|
||||
if (!index.isMapped())
|
||||
index.map();
|
||||
|
||||
int res;
|
||||
index.get((unsigned char*)&res,sizeof(int),(lineno-1)*sizeof(int));
|
||||
return res;
|
||||
}
|
||||
return cells.len();
|
||||
}
|
||||
|
||||
void HistoryScrollFile::getCells(int lineno, int colno, int count, Character res[])
|
||||
{
|
||||
cells.get((unsigned char*)res,count*sizeof(Character),startOfLine(lineno)+colno*sizeof(Character));
|
||||
}
|
||||
|
||||
void HistoryScrollFile::addCells(const Character text[], int count)
|
||||
{
|
||||
cells.add((unsigned char*)text,count*sizeof(Character));
|
||||
}
|
||||
|
||||
void HistoryScrollFile::addLine(bool previousWrapped)
|
||||
{
|
||||
if (index.isMapped())
|
||||
index.unmap();
|
||||
|
||||
int locn = cells.len();
|
||||
index.add((unsigned char*)&locn,sizeof(int));
|
||||
unsigned char flags = previousWrapped ? 0x01 : 0x00;
|
||||
lineflags.add((unsigned char*)&flags,sizeof(unsigned char));
|
||||
}
|
||||
|
||||
|
||||
// History Scroll Buffer //////////////////////////////////////
|
||||
HistoryScrollBuffer::HistoryScrollBuffer(unsigned int maxLineCount)
|
||||
: HistoryScroll(new HistoryTypeBuffer(maxLineCount))
|
||||
,_historyBuffer()
|
||||
,_maxLineCount(0)
|
||||
,_usedLines(0)
|
||||
,_head(0)
|
||||
{
|
||||
setMaxNbLines(maxLineCount);
|
||||
}
|
||||
|
||||
HistoryScrollBuffer::~HistoryScrollBuffer()
|
||||
{
|
||||
delete[] _historyBuffer;
|
||||
}
|
||||
|
||||
void HistoryScrollBuffer::addCellsVector(const QVector<Character>& cells)
|
||||
{
|
||||
_head++;
|
||||
if ( _usedLines < _maxLineCount )
|
||||
_usedLines++;
|
||||
|
||||
if ( _head >= _maxLineCount )
|
||||
{
|
||||
_head = 0;
|
||||
}
|
||||
|
||||
_historyBuffer[bufferIndex(_usedLines-1)] = cells;
|
||||
_wrappedLine[bufferIndex(_usedLines-1)] = false;
|
||||
}
|
||||
void HistoryScrollBuffer::addCells(const Character a[], int count)
|
||||
{
|
||||
HistoryLine newLine(count);
|
||||
qCopy(a,a+count,newLine.begin());
|
||||
|
||||
addCellsVector(newLine);
|
||||
}
|
||||
|
||||
void HistoryScrollBuffer::addLine(bool previousWrapped)
|
||||
{
|
||||
_wrappedLine[bufferIndex(_usedLines-1)] = previousWrapped;
|
||||
}
|
||||
|
||||
int HistoryScrollBuffer::getLines()
|
||||
{
|
||||
return _usedLines;
|
||||
}
|
||||
|
||||
int HistoryScrollBuffer::getLineLen(int lineNumber)
|
||||
{
|
||||
Q_ASSERT( lineNumber >= 0 && lineNumber < _maxLineCount );
|
||||
|
||||
if ( lineNumber < _usedLines )
|
||||
{
|
||||
return _historyBuffer[bufferIndex(lineNumber)].size();
|
||||
}
|
||||
else
|
||||
{
|
||||
return 0;
|
||||
}
|
||||
}
|
||||
|
||||
bool HistoryScrollBuffer::isWrappedLine(int lineNumber)
|
||||
{
|
||||
Q_ASSERT( lineNumber >= 0 && lineNumber < _maxLineCount );
|
||||
|
||||
if (lineNumber < _usedLines)
|
||||
{
|
||||
//kDebug() << "Line" << lineNumber << "wrapped is" << _wrappedLine[bufferIndex(lineNumber)];
|
||||
return _wrappedLine[bufferIndex(lineNumber)];
|
||||
}
|
||||
else
|
||||
return false;
|
||||
}
|
||||
|
||||
void HistoryScrollBuffer::getCells(int lineNumber, int startColumn, int count, Character* buffer)
|
||||
{
|
||||
if ( count == 0 ) return;
|
||||
|
||||
Q_ASSERT( lineNumber < _maxLineCount );
|
||||
|
||||
if (lineNumber >= _usedLines)
|
||||
{
|
||||
memset(buffer, 0, count * sizeof(Character));
|
||||
return;
|
||||
}
|
||||
|
||||
const HistoryLine& line = _historyBuffer[bufferIndex(lineNumber)];
|
||||
|
||||
//kDebug() << "startCol " << startColumn;
|
||||
//kDebug() << "line.size() " << line.size();
|
||||
//kDebug() << "count " << count;
|
||||
|
||||
Q_ASSERT( startColumn <= line.size() - count );
|
||||
|
||||
memcpy(buffer, line.constData() + startColumn , count * sizeof(Character));
|
||||
}
|
||||
|
||||
void HistoryScrollBuffer::setMaxNbLines(unsigned int lineCount)
|
||||
{
|
||||
HistoryLine* oldBuffer = _historyBuffer;
|
||||
HistoryLine* newBuffer = new HistoryLine[lineCount];
|
||||
|
||||
for ( int i = 0 ; i < qMin(_usedLines,(int)lineCount) ; i++ )
|
||||
{
|
||||
newBuffer[i] = oldBuffer[bufferIndex(i)];
|
||||
}
|
||||
|
||||
_usedLines = qMin(_usedLines,(int)lineCount);
|
||||
_maxLineCount = lineCount;
|
||||
_head = ( _usedLines == _maxLineCount ) ? 0 : _usedLines-1;
|
||||
|
||||
_historyBuffer = newBuffer;
|
||||
delete[] oldBuffer;
|
||||
|
||||
_wrappedLine.resize(lineCount);
|
||||
}
|
||||
|
||||
int HistoryScrollBuffer::bufferIndex(int lineNumber)
|
||||
{
|
||||
Q_ASSERT( lineNumber >= 0 );
|
||||
Q_ASSERT( lineNumber < _maxLineCount );
|
||||
Q_ASSERT( (_usedLines == _maxLineCount) || lineNumber <= _head );
|
||||
|
||||
if ( _usedLines == _maxLineCount )
|
||||
{
|
||||
return (_head+lineNumber+1) % _maxLineCount;
|
||||
}
|
||||
else
|
||||
{
|
||||
return lineNumber;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
// History Scroll None //////////////////////////////////////
|
||||
|
||||
HistoryScrollNone::HistoryScrollNone()
|
||||
: HistoryScroll(new HistoryTypeNone())
|
||||
{
|
||||
}
|
||||
|
||||
HistoryScrollNone::~HistoryScrollNone()
|
||||
{
|
||||
}
|
||||
|
||||
bool HistoryScrollNone::hasScroll()
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
int HistoryScrollNone::getLines()
|
||||
{
|
||||
return 0;
|
||||
}
|
||||
|
||||
int HistoryScrollNone::getLineLen(int)
|
||||
{
|
||||
return 0;
|
||||
}
|
||||
|
||||
bool HistoryScrollNone::isWrappedLine(int /*lineno*/)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
void HistoryScrollNone::getCells(int, int, int, Character [])
|
||||
{
|
||||
}
|
||||
|
||||
void HistoryScrollNone::addCells(const Character [], int)
|
||||
{
|
||||
}
|
||||
|
||||
void HistoryScrollNone::addLine(bool)
|
||||
{
|
||||
}
|
||||
|
||||
// History Scroll BlockArray //////////////////////////////////////
|
||||
|
||||
HistoryScrollBlockArray::HistoryScrollBlockArray(size_t size)
|
||||
: HistoryScroll(new HistoryTypeBlockArray(size))
|
||||
{
|
||||
m_blockArray.setHistorySize(size); // nb. of lines.
|
||||
}
|
||||
|
||||
HistoryScrollBlockArray::~HistoryScrollBlockArray()
|
||||
{
|
||||
}
|
||||
|
||||
int HistoryScrollBlockArray::getLines()
|
||||
{
|
||||
return m_lineLengths.count();
|
||||
}
|
||||
|
||||
int HistoryScrollBlockArray::getLineLen(int lineno)
|
||||
{
|
||||
if ( m_lineLengths.contains(lineno) )
|
||||
return m_lineLengths[lineno];
|
||||
else
|
||||
return 0;
|
||||
}
|
||||
|
||||
bool HistoryScrollBlockArray::isWrappedLine(int /*lineno*/)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
void HistoryScrollBlockArray::getCells(int lineno, int colno,
|
||||
int count, Character res[])
|
||||
{
|
||||
if (!count) return;
|
||||
|
||||
const Block *b = m_blockArray.at(lineno);
|
||||
|
||||
if (!b) {
|
||||
memset(res, 0, count * sizeof(Character)); // still better than random data
|
||||
return;
|
||||
}
|
||||
|
||||
assert(((colno + count) * sizeof(Character)) < ENTRIES);
|
||||
memcpy(res, b->data + (colno * sizeof(Character)), count * sizeof(Character));
|
||||
}
|
||||
|
||||
void HistoryScrollBlockArray::addCells(const Character a[], int count)
|
||||
{
|
||||
Block *b = m_blockArray.lastBlock();
|
||||
|
||||
if (!b) return;
|
||||
|
||||
// put cells in block's data
|
||||
assert((count * sizeof(Character)) < ENTRIES);
|
||||
|
||||
memset(b->data, 0, ENTRIES);
|
||||
|
||||
memcpy(b->data, a, count * sizeof(Character));
|
||||
b->size = count * sizeof(Character);
|
||||
|
||||
size_t res = m_blockArray.newBlock();
|
||||
assert (res > 0);
|
||||
Q_UNUSED( res );
|
||||
|
||||
m_lineLengths.insert(m_blockArray.getCurrent(), count);
|
||||
}
|
||||
|
||||
void HistoryScrollBlockArray::addLine(bool)
|
||||
{
|
||||
}
|
||||
|
||||
//////////////////////////////////////////////////////////////////////
|
||||
// History Types
|
||||
//////////////////////////////////////////////////////////////////////
|
||||
|
||||
HistoryType::HistoryType()
|
||||
{
|
||||
}
|
||||
|
||||
HistoryType::~HistoryType()
|
||||
{
|
||||
}
|
||||
|
||||
//////////////////////////////
|
||||
|
||||
HistoryTypeNone::HistoryTypeNone()
|
||||
{
|
||||
}
|
||||
|
||||
bool HistoryTypeNone::isEnabled() const
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
HistoryScroll* HistoryTypeNone::scroll(HistoryScroll *old) const
|
||||
{
|
||||
delete old;
|
||||
return new HistoryScrollNone();
|
||||
}
|
||||
|
||||
int HistoryTypeNone::maximumLineCount() const
|
||||
{
|
||||
return 0;
|
||||
}
|
||||
|
||||
//////////////////////////////
|
||||
|
||||
HistoryTypeBlockArray::HistoryTypeBlockArray(size_t size)
|
||||
: m_size(size)
|
||||
{
|
||||
}
|
||||
|
||||
bool HistoryTypeBlockArray::isEnabled() const
|
||||
{
|
||||
return true;
|
||||
}
|
||||
|
||||
int HistoryTypeBlockArray::maximumLineCount() const
|
||||
{
|
||||
return m_size;
|
||||
}
|
||||
|
||||
HistoryScroll* HistoryTypeBlockArray::scroll(HistoryScroll *old) const
|
||||
{
|
||||
delete old;
|
||||
return new HistoryScrollBlockArray(m_size);
|
||||
}
|
||||
|
||||
|
||||
//////////////////////////////
|
||||
|
||||
HistoryTypeBuffer::HistoryTypeBuffer(unsigned int nbLines)
|
||||
: m_nbLines(nbLines)
|
||||
{
|
||||
}
|
||||
|
||||
bool HistoryTypeBuffer::isEnabled() const
|
||||
{
|
||||
return true;
|
||||
}
|
||||
|
||||
int HistoryTypeBuffer::maximumLineCount() const
|
||||
{
|
||||
return m_nbLines;
|
||||
}
|
||||
|
||||
HistoryScroll* HistoryTypeBuffer::scroll(HistoryScroll *old) const
|
||||
{
|
||||
if (old)
|
||||
{
|
||||
HistoryScrollBuffer *oldBuffer = dynamic_cast<HistoryScrollBuffer*>(old);
|
||||
if (oldBuffer)
|
||||
{
|
||||
oldBuffer->setMaxNbLines(m_nbLines);
|
||||
return oldBuffer;
|
||||
}
|
||||
|
||||
HistoryScroll *newScroll = new HistoryScrollBuffer(m_nbLines);
|
||||
int lines = old->getLines();
|
||||
int startLine = 0;
|
||||
if (lines > (int) m_nbLines)
|
||||
startLine = lines - m_nbLines;
|
||||
|
||||
Character line[LINE_SIZE];
|
||||
for(int i = startLine; i < lines; i++)
|
||||
{
|
||||
int size = old->getLineLen(i);
|
||||
if (size > LINE_SIZE)
|
||||
{
|
||||
Character *tmp_line = new Character[size];
|
||||
old->getCells(i, 0, size, tmp_line);
|
||||
newScroll->addCells(tmp_line, size);
|
||||
newScroll->addLine(old->isWrappedLine(i));
|
||||
delete [] tmp_line;
|
||||
}
|
||||
else
|
||||
{
|
||||
old->getCells(i, 0, size, line);
|
||||
newScroll->addCells(line, size);
|
||||
newScroll->addLine(old->isWrappedLine(i));
|
||||
}
|
||||
}
|
||||
delete old;
|
||||
return newScroll;
|
||||
}
|
||||
return new HistoryScrollBuffer(m_nbLines);
|
||||
}
|
||||
|
||||
//////////////////////////////
|
||||
|
||||
HistoryTypeFile::HistoryTypeFile(const QString& fileName)
|
||||
: m_fileName(fileName)
|
||||
{
|
||||
}
|
||||
|
||||
bool HistoryTypeFile::isEnabled() const
|
||||
{
|
||||
return true;
|
||||
}
|
||||
|
||||
const QString& HistoryTypeFile::getFileName() const
|
||||
{
|
||||
return m_fileName;
|
||||
}
|
||||
|
||||
HistoryScroll* HistoryTypeFile::scroll(HistoryScroll *old) const
|
||||
{
|
||||
if (dynamic_cast<HistoryFile *>(old))
|
||||
return old; // Unchanged.
|
||||
|
||||
HistoryScroll *newScroll = new HistoryScrollFile(m_fileName);
|
||||
|
||||
Character line[LINE_SIZE];
|
||||
int lines = (old != 0) ? old->getLines() : 0;
|
||||
for(int i = 0; i < lines; i++)
|
||||
{
|
||||
int size = old->getLineLen(i);
|
||||
if (size > LINE_SIZE)
|
||||
{
|
||||
Character *tmp_line = new Character[size];
|
||||
old->getCells(i, 0, size, tmp_line);
|
||||
newScroll->addCells(tmp_line, size);
|
||||
newScroll->addLine(old->isWrappedLine(i));
|
||||
delete [] tmp_line;
|
||||
}
|
||||
else
|
||||
{
|
||||
old->getCells(i, 0, size, line);
|
||||
newScroll->addCells(line, size);
|
||||
newScroll->addLine(old->isWrappedLine(i));
|
||||
}
|
||||
}
|
||||
|
||||
delete old;
|
||||
return newScroll;
|
||||
}
|
||||
|
||||
int HistoryTypeFile::maximumLineCount() const
|
||||
{
|
||||
return 0;
|
||||
}
|
|
@ -0,0 +1,903 @@
|
|||
/*
|
||||
This source file was part of Konsole, a terminal emulator.
|
||||
|
||||
Copyright (C) 2007 by Robert Knight <robertknight@gmail.com>
|
||||
|
||||
Rewritten for QT4 by e_k <e_k at users.sourceforge.net>, Copyright (C)2008
|
||||
|
||||
This program is free software; you can redistribute it and/or modify
|
||||
it under the terms of the GNU General Public License as published by
|
||||
the Free Software Foundation; either version 2 of the License, or
|
||||
(at your option) any later version.
|
||||
|
||||
This program is distributed in the hope that it will be useful,
|
||||
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
GNU General Public License for more details.
|
||||
|
||||
You should have received a copy of the GNU General Public License
|
||||
along with this program; if not, write to the Free Software
|
||||
Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
|
||||
02110-1301 USA.
|
||||
*/
|
||||
|
||||
// Own
|
||||
#include "KeyboardTranslator.h"
|
||||
|
||||
// System
|
||||
#include <ctype.h>
|
||||
#include <stdio.h>
|
||||
|
||||
// Qt
|
||||
#include <QtCore/QBuffer>
|
||||
//#include <KDebug>
|
||||
#include <QtCore/QFile>
|
||||
#include <QtCore/QFileInfo>
|
||||
#include <QtCore>
|
||||
#include <QtGui>
|
||||
|
||||
// KDE
|
||||
//#include <KDebug>
|
||||
//#include <KLocale>
|
||||
//#include <KStandardDirs>
|
||||
|
||||
using namespace Konsole;
|
||||
|
||||
//this is for default REALLY fallback translator.
|
||||
|
||||
//const char* KeyboardTranslatorManager::defaultTranslatorText =
|
||||
//#include "DefaultTranslatorText.h"
|
||||
//;
|
||||
|
||||
//and this is default now translator - default.keytab from original Konsole
|
||||
const char* KeyboardTranslatorManager::defaultTranslatorText =
|
||||
#include "ExtendedDefaultTranslator.h"
|
||||
;
|
||||
|
||||
KeyboardTranslatorManager::KeyboardTranslatorManager()
|
||||
: _haveLoadedAll(false)
|
||||
{
|
||||
}
|
||||
KeyboardTranslatorManager::~KeyboardTranslatorManager()
|
||||
{
|
||||
qDeleteAll(_translators.values());
|
||||
}
|
||||
QString KeyboardTranslatorManager::findTranslatorPath(const QString& name)
|
||||
{
|
||||
return QString("kb-layouts/" + name + ".keytab");
|
||||
}
|
||||
void KeyboardTranslatorManager::findTranslators()
|
||||
{
|
||||
QDir dir("kb-layouts/");
|
||||
QStringList filters;
|
||||
filters << "*.keytab";
|
||||
dir.setNameFilters(filters);
|
||||
QStringList list = dir.entryList(filters); //(".keytab"); // = KGlobal::dirs()->findAllResources("data",
|
||||
// "konsole/*.keytab",
|
||||
// KStandardDirs::NoDuplicates);
|
||||
list = dir.entryList(filters);
|
||||
// add the name of each translator to the list and associated
|
||||
// the name with a null pointer to indicate that the translator
|
||||
// has not yet been loaded from disk
|
||||
QStringListIterator listIter(list);
|
||||
while (listIter.hasNext())
|
||||
{
|
||||
QString translatorPath = listIter.next();
|
||||
|
||||
QString name = QFileInfo(translatorPath).baseName();
|
||||
|
||||
if ( !_translators.contains(name) ) {
|
||||
_translators.insert(name,0);
|
||||
}
|
||||
}
|
||||
_haveLoadedAll = true;
|
||||
}
|
||||
|
||||
const KeyboardTranslator* KeyboardTranslatorManager::findTranslator(const QString& name)
|
||||
{
|
||||
if ( name.isEmpty() )
|
||||
return defaultTranslator();
|
||||
|
||||
//here was smth wrong in original Konsole source
|
||||
findTranslators();
|
||||
|
||||
if ( _translators.contains(name) && _translators[name] != 0 ) {
|
||||
return _translators[name];
|
||||
}
|
||||
|
||||
KeyboardTranslator* translator = loadTranslator(name);
|
||||
|
||||
if ( translator != 0 )
|
||||
_translators[name] = translator;
|
||||
else if ( !name.isEmpty() )
|
||||
qWarning() << "Unable to load translator" << name;
|
||||
|
||||
return translator;
|
||||
}
|
||||
|
||||
bool KeyboardTranslatorManager::saveTranslator(const KeyboardTranslator* translator)
|
||||
{
|
||||
const QString path = ".keytab";// = KGlobal::dirs()->saveLocation("data","konsole/")+translator->name()
|
||||
// +".keytab";
|
||||
|
||||
qDebug() << "Saving translator to" << path;
|
||||
|
||||
QFile destination(path);
|
||||
|
||||
if (!destination.open(QIODevice::WriteOnly | QIODevice::Text))
|
||||
{
|
||||
qWarning() << "Unable to save keyboard translation:"
|
||||
<< destination.errorString();
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
{
|
||||
KeyboardTranslatorWriter writer(&destination);
|
||||
writer.writeHeader(translator->description());
|
||||
|
||||
QListIterator<KeyboardTranslator::Entry> iter(translator->entries());
|
||||
while ( iter.hasNext() )
|
||||
writer.writeEntry(iter.next());
|
||||
}
|
||||
|
||||
destination.close();
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
KeyboardTranslator* KeyboardTranslatorManager::loadTranslator(const QString& name)
|
||||
{
|
||||
const QString& path = findTranslatorPath(name);
|
||||
|
||||
QFile source(path);
|
||||
|
||||
if (name.isEmpty() || !source.open(QIODevice::ReadOnly | QIODevice::Text))
|
||||
return 0;
|
||||
|
||||
return loadTranslator(&source,name);
|
||||
}
|
||||
|
||||
const KeyboardTranslator* KeyboardTranslatorManager::defaultTranslator()
|
||||
{
|
||||
qDebug() << "Loading default translator from text";
|
||||
QBuffer textBuffer;
|
||||
textBuffer.setData(defaultTranslatorText,strlen(defaultTranslatorText));
|
||||
|
||||
if (!textBuffer.open(QIODevice::ReadOnly))
|
||||
return 0;
|
||||
|
||||
return loadTranslator(&textBuffer,"fallback");
|
||||
}
|
||||
|
||||
KeyboardTranslator* KeyboardTranslatorManager::loadTranslator(QIODevice* source,const QString& name)
|
||||
{
|
||||
KeyboardTranslator* translator = new KeyboardTranslator(name);
|
||||
KeyboardTranslatorReader reader(source);
|
||||
translator->setDescription( reader.description() );
|
||||
|
||||
while ( reader.hasNextEntry() ) {
|
||||
translator->addEntry(reader.nextEntry());
|
||||
}
|
||||
|
||||
source->close();
|
||||
|
||||
if ( !reader.parseError() )
|
||||
{
|
||||
return translator;
|
||||
}
|
||||
else
|
||||
{
|
||||
delete translator;
|
||||
return 0;
|
||||
}
|
||||
}
|
||||
|
||||
KeyboardTranslatorWriter::KeyboardTranslatorWriter(QIODevice* destination)
|
||||
: _destination(destination)
|
||||
{
|
||||
Q_ASSERT( destination && destination->isWritable() );
|
||||
|
||||
_writer = new QTextStream(_destination);
|
||||
}
|
||||
KeyboardTranslatorWriter::~KeyboardTranslatorWriter()
|
||||
{
|
||||
delete _writer;
|
||||
}
|
||||
void KeyboardTranslatorWriter::writeHeader( const QString& description )
|
||||
{
|
||||
*_writer << "keyboard \"" << description << '\"' << '\n';
|
||||
}
|
||||
void KeyboardTranslatorWriter::writeEntry( const KeyboardTranslator::Entry& entry )
|
||||
{
|
||||
QString result;
|
||||
|
||||
if ( entry.command() != KeyboardTranslator::NoCommand )
|
||||
result = entry.resultToString();
|
||||
else
|
||||
result = '\"' + entry.resultToString() + '\"';
|
||||
|
||||
*_writer << "key " << entry.conditionToString() << " : " << result << '\n';
|
||||
}
|
||||
|
||||
|
||||
// each line of the keyboard translation file is one of:
|
||||
//
|
||||
// - keyboard "name"
|
||||
// - key KeySequence : "characters"
|
||||
// - key KeySequence : CommandName
|
||||
//
|
||||
// KeySequence begins with the name of the key ( taken from the Qt::Key enum )
|
||||
// and is followed by the keyboard modifiers and state flags ( with + or - in front
|
||||
// of each modifier or flag to indicate whether it is required ). All keyboard modifiers
|
||||
// and flags are optional, if a particular modifier or state is not specified it is
|
||||
// assumed not to be a part of the sequence. The key sequence may contain whitespace
|
||||
//
|
||||
// eg: "key Up+Shift : scrollLineUp"
|
||||
// "key Next-Shift : "\E[6~"
|
||||
//
|
||||
// (lines containing only whitespace are ignored, parseLine assumes that comments have
|
||||
// already been removed)
|
||||
//
|
||||
|
||||
KeyboardTranslatorReader::KeyboardTranslatorReader( QIODevice* source )
|
||||
: _source(source)
|
||||
, _hasNext(false)
|
||||
{
|
||||
// read input until we find the description
|
||||
while ( _description.isEmpty() && !source->atEnd() )
|
||||
{
|
||||
const QList<Token>& tokens = tokenize( QString(source->readLine()) );
|
||||
|
||||
if ( !tokens.isEmpty() && tokens.first().type == Token::TitleKeyword )
|
||||
{
|
||||
_description = (tokens[1].text.toUtf8());
|
||||
}
|
||||
}
|
||||
|
||||
readNext();
|
||||
}
|
||||
void KeyboardTranslatorReader::readNext()
|
||||
{
|
||||
// find next entry
|
||||
while ( !_source->atEnd() )
|
||||
{
|
||||
const QList<Token>& tokens = tokenize( QString(_source->readLine()) );
|
||||
if ( !tokens.isEmpty() && tokens.first().type == Token::KeyKeyword )
|
||||
{
|
||||
KeyboardTranslator::States flags = KeyboardTranslator::NoState;
|
||||
KeyboardTranslator::States flagMask = KeyboardTranslator::NoState;
|
||||
Qt::KeyboardModifiers modifiers = Qt::NoModifier;
|
||||
Qt::KeyboardModifiers modifierMask = Qt::NoModifier;
|
||||
|
||||
int keyCode = Qt::Key_unknown;
|
||||
|
||||
decodeSequence(tokens[1].text.toLower(),
|
||||
keyCode,
|
||||
modifiers,
|
||||
modifierMask,
|
||||
flags,
|
||||
flagMask);
|
||||
|
||||
KeyboardTranslator::Command command = KeyboardTranslator::NoCommand;
|
||||
QByteArray text;
|
||||
|
||||
// get text or command
|
||||
if ( tokens[2].type == Token::OutputText )
|
||||
{
|
||||
text = tokens[2].text.toLocal8Bit();
|
||||
}
|
||||
else if ( tokens[2].type == Token::Command )
|
||||
{
|
||||
// identify command
|
||||
if (!parseAsCommand(tokens[2].text,command))
|
||||
qWarning() << "Command" << tokens[2].text << "not understood.";
|
||||
}
|
||||
|
||||
KeyboardTranslator::Entry newEntry;
|
||||
newEntry.setKeyCode( keyCode );
|
||||
newEntry.setState( flags );
|
||||
newEntry.setStateMask( flagMask );
|
||||
newEntry.setModifiers( modifiers );
|
||||
newEntry.setModifierMask( modifierMask );
|
||||
newEntry.setText( text );
|
||||
newEntry.setCommand( command );
|
||||
|
||||
_nextEntry = newEntry;
|
||||
|
||||
_hasNext = true;
|
||||
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
_hasNext = false;
|
||||
}
|
||||
|
||||
bool KeyboardTranslatorReader::parseAsCommand(const QString& text,KeyboardTranslator::Command& command)
|
||||
{
|
||||
if ( text.compare("erase",Qt::CaseInsensitive) == 0 )
|
||||
command = KeyboardTranslator::EraseCommand;
|
||||
else if ( text.compare("scrollpageup",Qt::CaseInsensitive) == 0 )
|
||||
command = KeyboardTranslator::ScrollPageUpCommand;
|
||||
else if ( text.compare("scrollpagedown",Qt::CaseInsensitive) == 0 )
|
||||
command = KeyboardTranslator::ScrollPageDownCommand;
|
||||
else if ( text.compare("scrolllineup",Qt::CaseInsensitive) == 0 )
|
||||
command = KeyboardTranslator::ScrollLineUpCommand;
|
||||
else if ( text.compare("scrolllinedown",Qt::CaseInsensitive) == 0 )
|
||||
command = KeyboardTranslator::ScrollLineDownCommand;
|
||||
else if ( text.compare("scrolllock",Qt::CaseInsensitive) == 0 )
|
||||
command = KeyboardTranslator::ScrollLockCommand;
|
||||
else
|
||||
return false;
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
bool KeyboardTranslatorReader::decodeSequence(const QString& text,
|
||||
int& keyCode,
|
||||
Qt::KeyboardModifiers& modifiers,
|
||||
Qt::KeyboardModifiers& modifierMask,
|
||||
KeyboardTranslator::States& flags,
|
||||
KeyboardTranslator::States& flagMask)
|
||||
{
|
||||
bool isWanted = true;
|
||||
bool endOfItem = false;
|
||||
QString buffer;
|
||||
|
||||
Qt::KeyboardModifiers tempModifiers = modifiers;
|
||||
Qt::KeyboardModifiers tempModifierMask = modifierMask;
|
||||
KeyboardTranslator::States tempFlags = flags;
|
||||
KeyboardTranslator::States tempFlagMask = flagMask;
|
||||
|
||||
for ( int i = 0 ; i < text.count() ; i++ )
|
||||
{
|
||||
const QChar& ch = text[i];
|
||||
bool isLastLetter = ( i == text.count()-1 );
|
||||
|
||||
endOfItem = true;
|
||||
if ( ch.isLetterOrNumber() )
|
||||
{
|
||||
endOfItem = false;
|
||||
buffer.append(ch);
|
||||
}
|
||||
|
||||
if ( (endOfItem || isLastLetter) && !buffer.isEmpty() )
|
||||
{
|
||||
Qt::KeyboardModifier itemModifier = Qt::NoModifier;
|
||||
int itemKeyCode = 0;
|
||||
KeyboardTranslator::State itemFlag = KeyboardTranslator::NoState;
|
||||
|
||||
if ( parseAsModifier(buffer,itemModifier) )
|
||||
{
|
||||
tempModifierMask |= itemModifier;
|
||||
|
||||
if ( isWanted )
|
||||
tempModifiers |= itemModifier;
|
||||
}
|
||||
else if ( parseAsStateFlag(buffer,itemFlag) )
|
||||
{
|
||||
tempFlagMask |= itemFlag;
|
||||
|
||||
if ( isWanted )
|
||||
tempFlags |= itemFlag;
|
||||
}
|
||||
else if ( parseAsKeyCode(buffer,itemKeyCode) )
|
||||
keyCode = itemKeyCode;
|
||||
else
|
||||
qDebug() << "Unable to parse key binding item:" << buffer;
|
||||
|
||||
buffer.clear();
|
||||
}
|
||||
|
||||
// check if this is a wanted / not-wanted flag and update the
|
||||
// state ready for the next item
|
||||
if ( ch == '+' )
|
||||
isWanted = true;
|
||||
else if ( ch == '-' )
|
||||
isWanted = false;
|
||||
}
|
||||
|
||||
modifiers = tempModifiers;
|
||||
modifierMask = tempModifierMask;
|
||||
flags = tempFlags;
|
||||
flagMask = tempFlagMask;
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
bool KeyboardTranslatorReader::parseAsModifier(const QString& item , Qt::KeyboardModifier& modifier)
|
||||
{
|
||||
if ( item == "shift" )
|
||||
modifier = Qt::ShiftModifier;
|
||||
else if ( item == "ctrl" || item == "control" )
|
||||
modifier = Qt::ControlModifier;
|
||||
else if ( item == "alt" )
|
||||
modifier = Qt::AltModifier;
|
||||
else if ( item == "meta" )
|
||||
modifier = Qt::MetaModifier;
|
||||
else if ( item == "keypad" )
|
||||
modifier = Qt::KeypadModifier;
|
||||
else
|
||||
return false;
|
||||
|
||||
return true;
|
||||
}
|
||||
bool KeyboardTranslatorReader::parseAsStateFlag(const QString& item , KeyboardTranslator::State& flag)
|
||||
{
|
||||
if ( item == "appcukeys" )
|
||||
flag = KeyboardTranslator::CursorKeysState;
|
||||
else if ( item == "ansi" )
|
||||
flag = KeyboardTranslator::AnsiState;
|
||||
else if ( item == "newline" )
|
||||
flag = KeyboardTranslator::NewLineState;
|
||||
else if ( item == "appscreen" )
|
||||
flag = KeyboardTranslator::AlternateScreenState;
|
||||
else if ( item == "anymod" )
|
||||
flag = KeyboardTranslator::AnyModifierState;
|
||||
else
|
||||
return false;
|
||||
|
||||
return true;
|
||||
}
|
||||
bool KeyboardTranslatorReader::parseAsKeyCode(const QString& item , int& keyCode)
|
||||
{
|
||||
QKeySequence sequence = QKeySequence::fromString(item);
|
||||
if ( !sequence.isEmpty() )
|
||||
{
|
||||
keyCode = sequence[0];
|
||||
|
||||
if ( sequence.count() > 1 )
|
||||
{
|
||||
qDebug() << "Unhandled key codes in sequence: " << item;
|
||||
}
|
||||
}
|
||||
// additional cases implemented for backwards compatibility with KDE 3
|
||||
else if ( item == "prior" )
|
||||
keyCode = Qt::Key_PageUp;
|
||||
else if ( item == "next" )
|
||||
keyCode = Qt::Key_PageDown;
|
||||
else
|
||||
return false;
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
QString KeyboardTranslatorReader::description() const
|
||||
{
|
||||
return _description;
|
||||
}
|
||||
bool KeyboardTranslatorReader::hasNextEntry()
|
||||
{
|
||||
return _hasNext;
|
||||
}
|
||||
KeyboardTranslator::Entry KeyboardTranslatorReader::createEntry( const QString& condition ,
|
||||
const QString& result )
|
||||
{
|
||||
QString entryString("keyboard \"temporary\"\nkey ");
|
||||
entryString.append(condition);
|
||||
entryString.append(" : ");
|
||||
|
||||
// if 'result' is the name of a command then the entry result will be that command,
|
||||
// otherwise the result will be treated as a string to echo when the key sequence
|
||||
// specified by 'condition' is pressed
|
||||
KeyboardTranslator::Command command;
|
||||
if (parseAsCommand(result,command))
|
||||
entryString.append(result);
|
||||
else
|
||||
entryString.append('\"' + result + '\"');
|
||||
|
||||
QByteArray array = entryString.toUtf8();
|
||||
|
||||
KeyboardTranslator::Entry entry;
|
||||
|
||||
QBuffer buffer(&array);
|
||||
buffer.open(QIODevice::ReadOnly);
|
||||
KeyboardTranslatorReader reader(&buffer);
|
||||
|
||||
if ( reader.hasNextEntry() )
|
||||
entry = reader.nextEntry();
|
||||
|
||||
return entry;
|
||||
}
|
||||
|
||||
KeyboardTranslator::Entry KeyboardTranslatorReader::nextEntry()
|
||||
{
|
||||
Q_ASSERT( _hasNext );
|
||||
|
||||
|
||||
KeyboardTranslator::Entry entry = _nextEntry;
|
||||
|
||||
readNext();
|
||||
|
||||
return entry;
|
||||
}
|
||||
bool KeyboardTranslatorReader::parseError()
|
||||
{
|
||||
return false;
|
||||
}
|
||||
QList<KeyboardTranslatorReader::Token> KeyboardTranslatorReader::tokenize(const QString& line)
|
||||
{
|
||||
QString text = line.simplified();
|
||||
|
||||
// comment line: # comment
|
||||
static QRegExp comment("\\#.*");
|
||||
// title line: keyboard "title"
|
||||
static QRegExp title("keyboard\\s+\"(.*)\"");
|
||||
// key line: key KeySequence : "output"
|
||||
// key line: key KeySequence : command
|
||||
static QRegExp key("key\\s+([\\w\\+\\s\\-]+)\\s*:\\s*(\"(.*)\"|\\w+)");
|
||||
|
||||
QList<Token> list;
|
||||
|
||||
if ( text.isEmpty() || comment.exactMatch(text) )
|
||||
{
|
||||
return list;
|
||||
}
|
||||
|
||||
if ( title.exactMatch(text) )
|
||||
{
|
||||
Token titleToken = { Token::TitleKeyword , QString() };
|
||||
Token textToken = { Token::TitleText , title.capturedTexts()[1] };
|
||||
|
||||
list << titleToken << textToken;
|
||||
}
|
||||
else if ( key.exactMatch(text) )
|
||||
{
|
||||
Token keyToken = { Token::KeyKeyword , QString() };
|
||||
Token sequenceToken = { Token::KeySequence , key.capturedTexts()[1].remove(' ') };
|
||||
|
||||
list << keyToken << sequenceToken;
|
||||
|
||||
if ( key.capturedTexts()[3].isEmpty() )
|
||||
{
|
||||
// capturedTexts()[2] is a command
|
||||
Token commandToken = { Token::Command , key.capturedTexts()[2] };
|
||||
list << commandToken;
|
||||
}
|
||||
else
|
||||
{
|
||||
// capturedTexts()[3] is the output string
|
||||
Token outputToken = { Token::OutputText , key.capturedTexts()[3] };
|
||||
list << outputToken;
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
qWarning() << "Line in keyboard translator file could not be understood:" << text;
|
||||
}
|
||||
|
||||
return list;
|
||||
}
|
||||
|
||||
QList<QString> KeyboardTranslatorManager::allTranslators()
|
||||
{
|
||||
if ( !_haveLoadedAll )
|
||||
{
|
||||
findTranslators();
|
||||
}
|
||||
|
||||
return _translators.keys();
|
||||
}
|
||||
|
||||
KeyboardTranslator::Entry::Entry()
|
||||
: _keyCode(0)
|
||||
, _modifiers(Qt::NoModifier)
|
||||
, _modifierMask(Qt::NoModifier)
|
||||
, _state(NoState)
|
||||
, _stateMask(NoState)
|
||||
, _command(NoCommand)
|
||||
{
|
||||
}
|
||||
|
||||
bool KeyboardTranslator::Entry::operator==(const Entry& rhs) const
|
||||
{
|
||||
return _keyCode == rhs._keyCode &&
|
||||
_modifiers == rhs._modifiers &&
|
||||
_modifierMask == rhs._modifierMask &&
|
||||
_state == rhs._state &&
|
||||
_stateMask == rhs._stateMask &&
|
||||
_command == rhs._command &&
|
||||
_text == rhs._text;
|
||||
}
|
||||
|
||||
bool KeyboardTranslator::Entry::matches(int keyCode ,
|
||||
Qt::KeyboardModifiers modifiers,
|
||||
States state) const
|
||||
{
|
||||
if ( _keyCode != keyCode )
|
||||
return false;
|
||||
|
||||
if ( (modifiers & _modifierMask) != (_modifiers & _modifierMask) )
|
||||
return false;
|
||||
|
||||
// if modifiers is non-zero, the 'any modifier' state is implicit
|
||||
if ( modifiers != 0 )
|
||||
state |= AnyModifierState;
|
||||
|
||||
if ( (state & _stateMask) != (_state & _stateMask) )
|
||||
return false;
|
||||
|
||||
// special handling for the 'Any Modifier' state, which checks for the presence of
|
||||
// any or no modifiers. In this context, the 'keypad' modifier does not count.
|
||||
bool anyModifiersSet = modifiers != 0 && modifiers != Qt::KeypadModifier;
|
||||
if ( _stateMask & KeyboardTranslator::AnyModifierState )
|
||||
{
|
||||
// test fails if any modifier is required but none are set
|
||||
if ( (_state & KeyboardTranslator::AnyModifierState) && !anyModifiersSet )
|
||||
return false;
|
||||
|
||||
// test fails if no modifier is allowed but one or more are set
|
||||
if ( !(_state & KeyboardTranslator::AnyModifierState) && anyModifiersSet )
|
||||
return false;
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
QByteArray KeyboardTranslator::Entry::escapedText(bool expandWildCards,Qt::KeyboardModifiers modifiers) const
|
||||
{
|
||||
QByteArray result(text(expandWildCards,modifiers));
|
||||
|
||||
for ( int i = 0 ; i < result.count() ; i++ )
|
||||
{
|
||||
char ch = result[i];
|
||||
char replacement = 0;
|
||||
|
||||
switch ( ch )
|
||||
{
|
||||
case 27 : replacement = 'E'; break;
|
||||
case 8 : replacement = 'b'; break;
|
||||
case 12 : replacement = 'f'; break;
|
||||
case 9 : replacement = 't'; break;
|
||||
case 13 : replacement = 'r'; break;
|
||||
case 10 : replacement = 'n'; break;
|
||||
default:
|
||||
// any character which is not printable is replaced by an equivalent
|
||||
// \xhh escape sequence (where 'hh' are the corresponding hex digits)
|
||||
if ( !QChar(ch).isPrint() )
|
||||
replacement = 'x';
|
||||
}
|
||||
|
||||
if ( replacement == 'x' )
|
||||
{
|
||||
result.replace(i,1,"\\x"+QByteArray(1,ch).toInt(0, 16));
|
||||
} else if ( replacement != 0 )
|
||||
{
|
||||
result.remove(i,1);
|
||||
result.insert(i,'\\');
|
||||
result.insert(i+1,replacement);
|
||||
}
|
||||
}
|
||||
|
||||
return result;
|
||||
}
|
||||
QByteArray KeyboardTranslator::Entry::unescape(const QByteArray& input) const
|
||||
{
|
||||
QByteArray result(input);
|
||||
|
||||
for ( int i = 0 ; i < result.count()-1 ; i++ )
|
||||
{
|
||||
|
||||
QByteRef ch = result[i];
|
||||
if ( ch == '\\' )
|
||||
{
|
||||
char replacement[2] = {0,0};
|
||||
int charsToRemove = 2;
|
||||
bool escapedChar = true;
|
||||
|
||||
switch ( result[i+1] )
|
||||
{
|
||||
case 'E' : replacement[0] = 27; break;
|
||||
case 'b' : replacement[0] = 8 ; break;
|
||||
case 'f' : replacement[0] = 12; break;
|
||||
case 't' : replacement[0] = 9 ; break;
|
||||
case 'r' : replacement[0] = 13; break;
|
||||
case 'n' : replacement[0] = 10; break;
|
||||
case 'x' :
|
||||
{
|
||||
// format is \xh or \xhh where 'h' is a hexadecimal
|
||||
// digit from 0-9 or A-F which should be replaced
|
||||
// with the corresponding character value
|
||||
char hexDigits[3] = {0};
|
||||
|
||||
if ( (i < result.count()-2) && isxdigit(result[i+2]) )
|
||||
hexDigits[0] = result[i+2];
|
||||
if ( (i < result.count()-3) && isxdigit(result[i+3]) )
|
||||
hexDigits[1] = result[i+3];
|
||||
|
||||
int charValue = 0;
|
||||
sscanf(hexDigits,"%x",&charValue);
|
||||
|
||||
replacement[0] = (char)charValue;
|
||||
|
||||
charsToRemove = 2 + strlen(hexDigits);
|
||||
}
|
||||
break;
|
||||
default:
|
||||
escapedChar = false;
|
||||
}
|
||||
|
||||
if ( escapedChar )
|
||||
result.replace(i,charsToRemove,replacement);
|
||||
}
|
||||
}
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
void KeyboardTranslator::Entry::insertModifier( QString& item , int modifier ) const
|
||||
{
|
||||
if ( !(modifier & _modifierMask) )
|
||||
return;
|
||||
|
||||
if ( modifier & _modifiers )
|
||||
item += '+';
|
||||
else
|
||||
item += '-';
|
||||
|
||||
if ( modifier == Qt::ShiftModifier )
|
||||
item += "Shift";
|
||||
else if ( modifier == Qt::ControlModifier )
|
||||
item += "Ctrl";
|
||||
else if ( modifier == Qt::AltModifier )
|
||||
item += "Alt";
|
||||
else if ( modifier == Qt::MetaModifier )
|
||||
item += "Meta";
|
||||
else if ( modifier == Qt::KeypadModifier )
|
||||
item += "KeyPad";
|
||||
}
|
||||
void KeyboardTranslator::Entry::insertState( QString& item , int state ) const
|
||||
{
|
||||
if ( !(state & _stateMask) )
|
||||
return;
|
||||
|
||||
if ( state & _state )
|
||||
item += '+' ;
|
||||
else
|
||||
item += '-' ;
|
||||
|
||||
if ( state == KeyboardTranslator::AlternateScreenState )
|
||||
item += "AppScreen";
|
||||
else if ( state == KeyboardTranslator::NewLineState )
|
||||
item += "NewLine";
|
||||
else if ( state == KeyboardTranslator::AnsiState )
|
||||
item += "Ansi";
|
||||
else if ( state == KeyboardTranslator::CursorKeysState )
|
||||
item += "AppCuKeys";
|
||||
else if ( state == KeyboardTranslator::AnyModifierState )
|
||||
item += "AnyMod";
|
||||
}
|
||||
QString KeyboardTranslator::Entry::resultToString(bool expandWildCards,Qt::KeyboardModifiers modifiers) const
|
||||
{
|
||||
if ( !_text.isEmpty() )
|
||||
return escapedText(expandWildCards,modifiers);
|
||||
else if ( _command == EraseCommand )
|
||||
return "Erase";
|
||||
else if ( _command == ScrollPageUpCommand )
|
||||
return "ScrollPageUp";
|
||||
else if ( _command == ScrollPageDownCommand )
|
||||
return "ScrollPageDown";
|
||||
else if ( _command == ScrollLineUpCommand )
|
||||
return "ScrollLineUp";
|
||||
else if ( _command == ScrollLineDownCommand )
|
||||
return "ScrollLineDown";
|
||||
else if ( _command == ScrollLockCommand )
|
||||
return "ScrollLock";
|
||||
|
||||
return QString();
|
||||
}
|
||||
QString KeyboardTranslator::Entry::conditionToString() const
|
||||
{
|
||||
QString result = QKeySequence(_keyCode).toString();
|
||||
|
||||
// add modifiers
|
||||
insertModifier( result , Qt::ShiftModifier );
|
||||
insertModifier( result , Qt::ControlModifier );
|
||||
insertModifier( result , Qt::AltModifier );
|
||||
insertModifier( result , Qt::MetaModifier );
|
||||
|
||||
// add states
|
||||
insertState( result , KeyboardTranslator::AlternateScreenState );
|
||||
insertState( result , KeyboardTranslator::NewLineState );
|
||||
insertState( result , KeyboardTranslator::AnsiState );
|
||||
insertState( result , KeyboardTranslator::CursorKeysState );
|
||||
insertState( result , KeyboardTranslator::AnyModifierState );
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
KeyboardTranslator::KeyboardTranslator(const QString& name)
|
||||
: _name(name)
|
||||
{
|
||||
}
|
||||
|
||||
void KeyboardTranslator::setDescription(const QString& description)
|
||||
{
|
||||
_description = description;
|
||||
}
|
||||
QString KeyboardTranslator::description() const
|
||||
{
|
||||
return _description;
|
||||
}
|
||||
void KeyboardTranslator::setName(const QString& name)
|
||||
{
|
||||
_name = name;
|
||||
}
|
||||
QString KeyboardTranslator::name() const
|
||||
{
|
||||
return _name;
|
||||
}
|
||||
|
||||
QList<KeyboardTranslator::Entry> KeyboardTranslator::entries() const
|
||||
{
|
||||
return _entries.values();
|
||||
}
|
||||
|
||||
void KeyboardTranslator::addEntry(const Entry& entry)
|
||||
{
|
||||
const int keyCode = entry.keyCode();
|
||||
_entries.insertMulti(keyCode,entry);
|
||||
}
|
||||
void KeyboardTranslator::replaceEntry(const Entry& existing , const Entry& replacement)
|
||||
{
|
||||
if ( !existing.isNull() )
|
||||
_entries.remove(existing.keyCode());
|
||||
_entries.insertMulti(replacement.keyCode(),replacement);
|
||||
}
|
||||
void KeyboardTranslator::removeEntry(const Entry& entry)
|
||||
{
|
||||
_entries.remove(entry.keyCode());
|
||||
}
|
||||
KeyboardTranslator::Entry KeyboardTranslator::findEntry(int keyCode, Qt::KeyboardModifiers modifiers, States state) const
|
||||
{
|
||||
if ( _entries.contains(keyCode) )
|
||||
{
|
||||
QList<Entry> entriesForKey = _entries.values(keyCode);
|
||||
|
||||
QListIterator<Entry> iter(entriesForKey);
|
||||
|
||||
while (iter.hasNext())
|
||||
{
|
||||
const Entry& next = iter.next();
|
||||
if ( next.matches(keyCode,modifiers,state) )
|
||||
return next;
|
||||
}
|
||||
|
||||
return Entry(); // entry not found
|
||||
}
|
||||
else
|
||||
{
|
||||
return Entry();
|
||||
}
|
||||
|
||||
}
|
||||
void KeyboardTranslatorManager::addTranslator(KeyboardTranslator* translator)
|
||||
{
|
||||
_translators.insert(translator->name(),translator);
|
||||
|
||||
if ( !saveTranslator(translator) )
|
||||
qWarning() << "Unable to save translator" << translator->name()
|
||||
<< "to disk.";
|
||||
}
|
||||
bool KeyboardTranslatorManager::deleteTranslator(const QString& name)
|
||||
{
|
||||
Q_ASSERT( _translators.contains(name) );
|
||||
|
||||
// locate and delete
|
||||
QString path = findTranslatorPath(name);
|
||||
if ( QFile::remove(path) )
|
||||
{
|
||||
_translators.remove(name);
|
||||
return true;
|
||||
}
|
||||
else
|
||||
{
|
||||
qWarning() << "Failed to remove translator - " << path;
|
||||
return false;
|
||||
}
|
||||
}
|
||||
K_GLOBAL_STATIC( KeyboardTranslatorManager , theKeyboardTranslatorManager )
|
||||
KeyboardTranslatorManager* KeyboardTranslatorManager::instance()
|
||||
{
|
||||
return theKeyboardTranslatorManager;
|
||||
}
|
|
@ -0,0 +1,321 @@
|
|||
/*
|
||||
This file is part of Konsole, an X terminal.
|
||||
Copyright (C) 1997,1998 by Lars Doelle <lars.doelle@on-line.de>
|
||||
|
||||
Rewritten for QT4 by e_k <e_k at users.sourceforge.net>, Copyright (C)2008
|
||||
|
||||
This program is free software; you can redistribute it and/or modify
|
||||
it under the terms of the GNU General Public License as published by
|
||||
the Free Software Foundation; either version 2 of the License, or
|
||||
(at your option) any later version.
|
||||
|
||||
This program is distributed in the hope that it will be useful,
|
||||
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
GNU General Public License for more details.
|
||||
|
||||
You should have received a copy of the GNU General Public License
|
||||
along with this program; if not, write to the Free Software
|
||||
Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
|
||||
02110-1301 USA.
|
||||
*/
|
||||
|
||||
// Own
|
||||
#include "Pty.h"
|
||||
#include "Pty.moc"
|
||||
|
||||
// System
|
||||
#include <sys/types.h>
|
||||
#include <sys/stat.h>
|
||||
#include <unistd.h>
|
||||
#include <errno.h>
|
||||
#include <termios.h>
|
||||
|
||||
// Qt
|
||||
#include <QtCore>
|
||||
|
||||
// KDE
|
||||
//#include <KStandardDirs>
|
||||
//#include <KLocale>
|
||||
//#include <KDebug>
|
||||
#include "kpty.h"
|
||||
|
||||
using namespace Konsole;
|
||||
|
||||
void Pty::donePty()
|
||||
{
|
||||
emit done(exitStatus());
|
||||
}
|
||||
|
||||
void Pty::setWindowSize(int lines, int cols)
|
||||
{
|
||||
_windowColumns = cols;
|
||||
_windowLines = lines;
|
||||
|
||||
if (pty()->masterFd() >= 0)
|
||||
pty()->setWinSize(lines, cols);
|
||||
}
|
||||
QSize Pty::windowSize() const
|
||||
{
|
||||
return QSize(_windowColumns,_windowLines);
|
||||
}
|
||||
|
||||
void Pty::setXonXoff(bool enable)
|
||||
{
|
||||
_xonXoff = enable;
|
||||
|
||||
if (pty()->masterFd() >= 0)
|
||||
{
|
||||
struct ::termios ttmode;
|
||||
pty()->tcGetAttr(&ttmode);
|
||||
if (!enable)
|
||||
ttmode.c_iflag &= ~(IXOFF | IXON);
|
||||
else
|
||||
ttmode.c_iflag |= (IXOFF | IXON);
|
||||
if (!pty()->tcSetAttr(&ttmode))
|
||||
qWarning("Unable to set terminal attributes.");
|
||||
}
|
||||
}
|
||||
|
||||
void Pty::setUtf8Mode(bool enable)
|
||||
{
|
||||
#ifdef IUTF8 // XXX not a reasonable place to check it.
|
||||
_utf8 = enable;
|
||||
|
||||
if (pty()->masterFd() >= 0)
|
||||
{
|
||||
struct ::termios ttmode;
|
||||
pty()->tcGetAttr(&ttmode);
|
||||
if (!enable)
|
||||
ttmode.c_iflag &= ~IUTF8;
|
||||
else
|
||||
ttmode.c_iflag |= IUTF8;
|
||||
if (!pty()->tcSetAttr(&ttmode))
|
||||
qWarning("Unable to set terminal attributes.");
|
||||
}
|
||||
#endif
|
||||
}
|
||||
|
||||
void Pty::setErase(char erase)
|
||||
{
|
||||
_eraseChar = erase;
|
||||
|
||||
if (pty()->masterFd() >= 0)
|
||||
{
|
||||
struct ::termios ttmode;
|
||||
|
||||
pty()->tcGetAttr(&ttmode);
|
||||
|
||||
ttmode.c_cc[VERASE] = erase;
|
||||
|
||||
if (!pty()->tcSetAttr(&ttmode))
|
||||
qWarning("Unable to set terminal attributes.");
|
||||
}
|
||||
}
|
||||
|
||||
char Pty::erase() const
|
||||
{
|
||||
if (pty()->masterFd() >= 0)
|
||||
{
|
||||
qDebug() << "Getting erase char";
|
||||
struct ::termios ttyAttributes;
|
||||
pty()->tcGetAttr(&ttyAttributes);
|
||||
return ttyAttributes.c_cc[VERASE];
|
||||
}
|
||||
|
||||
return _eraseChar;
|
||||
}
|
||||
|
||||
void Pty::addEnvironmentVariables(const QStringList& environment)
|
||||
{
|
||||
QListIterator<QString> iter(environment);
|
||||
while (iter.hasNext())
|
||||
{
|
||||
QString pair = iter.next();
|
||||
|
||||
// split on the first '=' character
|
||||
int pos = pair.indexOf('=');
|
||||
|
||||
if ( pos >= 0 )
|
||||
{
|
||||
QString variable = pair.left(pos);
|
||||
QString value = pair.mid(pos+1);
|
||||
|
||||
//kDebug() << "Setting environment pair" << variable <<
|
||||
// " set to " << value;
|
||||
|
||||
setEnvironment(variable,value);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
int Pty::start(const QString& program,
|
||||
const QStringList& programArguments,
|
||||
const QStringList& environment,
|
||||
ulong winid,
|
||||
bool addToUtmp
|
||||
// const QString& dbusService,
|
||||
// const QString& dbusSession)
|
||||
)
|
||||
{
|
||||
clearArguments();
|
||||
|
||||
setBinaryExecutable(program.toLatin1());
|
||||
|
||||
addEnvironmentVariables(environment);
|
||||
|
||||
QStringListIterator it( programArguments );
|
||||
while (it.hasNext())
|
||||
arguments.append( it.next().toUtf8() );
|
||||
|
||||
// if ( !dbusService.isEmpty() )
|
||||
// setEnvironment("KONSOLE_DBUS_SERVICE",dbusService);
|
||||
// if ( !dbusSession.isEmpty() )
|
||||
// setEnvironment("KONSOLE_DBUS_SESSION", dbusSession);
|
||||
|
||||
setEnvironment("WINDOWID", QString::number(winid));
|
||||
|
||||
// unless the LANGUAGE environment variable has been set explicitly
|
||||
// set it to a null string
|
||||
// this fixes the problem where KCatalog sets the LANGUAGE environment
|
||||
// variable during the application's startup to something which
|
||||
// differs from LANG,LC_* etc. and causes programs run from
|
||||
// the terminal to display mesages in the wrong language
|
||||
//
|
||||
// this can happen if LANG contains a language which KDE
|
||||
// does not have a translation for
|
||||
//
|
||||
// BR:149300
|
||||
if (!environment.contains("LANGUAGE"))
|
||||
setEnvironment("LANGUAGE",QString());
|
||||
|
||||
setUsePty(All, addToUtmp);
|
||||
|
||||
pty()->open();
|
||||
|
||||
struct ::termios ttmode;
|
||||
pty()->tcGetAttr(&ttmode);
|
||||
if (!_xonXoff)
|
||||
ttmode.c_iflag &= ~(IXOFF | IXON);
|
||||
else
|
||||
ttmode.c_iflag |= (IXOFF | IXON);
|
||||
#ifdef IUTF8 // XXX not a reasonable place to check it.
|
||||
if (!_utf8)
|
||||
ttmode.c_iflag &= ~IUTF8;
|
||||
else
|
||||
ttmode.c_iflag |= IUTF8;
|
||||
#endif
|
||||
|
||||
if (_eraseChar != 0)
|
||||
ttmode.c_cc[VERASE] = _eraseChar;
|
||||
|
||||
if (!pty()->tcSetAttr(&ttmode))
|
||||
qWarning("Unable to set terminal attributes.");
|
||||
|
||||
pty()->setWinSize(_windowLines, _windowColumns);
|
||||
|
||||
if ( K3Process::start(NotifyOnExit, (Communication) (Stdin | Stdout)) == false )
|
||||
return -1;
|
||||
|
||||
resume(); // Start...
|
||||
return 0;
|
||||
|
||||
}
|
||||
|
||||
void Pty::setWriteable(bool writeable)
|
||||
{
|
||||
struct stat sbuf;
|
||||
stat(pty()->ttyName(), &sbuf);
|
||||
if (writeable)
|
||||
chmod(pty()->ttyName(), sbuf.st_mode | S_IWGRP);
|
||||
else
|
||||
chmod(pty()->ttyName(), sbuf.st_mode & ~(S_IWGRP|S_IWOTH));
|
||||
}
|
||||
|
||||
Pty::Pty()
|
||||
: _bufferFull(false),
|
||||
_windowColumns(0),
|
||||
_windowLines(0),
|
||||
_eraseChar(0),
|
||||
_xonXoff(true),
|
||||
_utf8(true)
|
||||
{
|
||||
connect(this, SIGNAL(receivedStdout(K3Process *, char *, int )),
|
||||
this, SLOT(dataReceived(K3Process *,char *, int)));
|
||||
connect(this, SIGNAL(processExited(K3Process *)),
|
||||
this, SLOT(donePty()));
|
||||
connect(this, SIGNAL(wroteStdin(K3Process *)),
|
||||
this, SLOT(writeReady()));
|
||||
_pty = new KPty;
|
||||
|
||||
setUsePty(All, false); // utmp will be overridden later
|
||||
}
|
||||
|
||||
Pty::~Pty()
|
||||
{
|
||||
delete _pty;
|
||||
}
|
||||
|
||||
void Pty::writeReady()
|
||||
{
|
||||
_pendingSendJobs.erase(_pendingSendJobs.begin());
|
||||
_bufferFull = false;
|
||||
doSendJobs();
|
||||
}
|
||||
|
||||
void Pty::doSendJobs() {
|
||||
if(_pendingSendJobs.isEmpty())
|
||||
{
|
||||
emit bufferEmpty();
|
||||
return;
|
||||
}
|
||||
|
||||
SendJob& job = _pendingSendJobs.first();
|
||||
|
||||
|
||||
if (!writeStdin( job.data(), job.length() ))
|
||||
{
|
||||
qWarning("Pty::doSendJobs - Could not send input data to terminal process.");
|
||||
return;
|
||||
}
|
||||
_bufferFull = true;
|
||||
}
|
||||
|
||||
void Pty::appendSendJob(const char* s, int len)
|
||||
{
|
||||
_pendingSendJobs.append(SendJob(s,len));
|
||||
}
|
||||
|
||||
void Pty::sendData(const char* s, int len)
|
||||
{
|
||||
appendSendJob(s,len);
|
||||
if (!_bufferFull)
|
||||
doSendJobs();
|
||||
}
|
||||
|
||||
void Pty::dataReceived(K3Process *,char *buf, int len)
|
||||
{
|
||||
emit receivedData(buf,len);
|
||||
}
|
||||
|
||||
void Pty::lockPty(bool lock)
|
||||
{
|
||||
if (lock)
|
||||
suspend();
|
||||
else
|
||||
resume();
|
||||
}
|
||||
|
||||
int Pty::foregroundProcessGroup() const
|
||||
{
|
||||
int pid = tcgetpgrp(pty()->masterFd());
|
||||
|
||||
if ( pid != -1 )
|
||||
{
|
||||
return pid;
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
//#include "moc_Pty.cpp"
|
|
@ -0,0 +1,7 @@
|
|||
lib.pro is a *.pro-file for qmake
|
||||
|
||||
It produces static lib (libqtermwidget.a) only.
|
||||
For creating shared lib (*.so) uncomment "dll" in "CONFIG" line in *.pro-file
|
||||
|
||||
Library was tested both with HAVE_POSIX_OPENPT and HAVE_GETPT precompiler directives,
|
||||
defined in "DEFINES" line. You should select variant which would be correct for your system.
|
File diff suppressed because it is too large
Load Diff
|
@ -0,0 +1,298 @@
|
|||
/*
|
||||
Copyright (C) 2007 by Robert Knight <robertknight@gmail.com>
|
||||
|
||||
Rewritten for QT4 by e_k <e_k at users.sourceforge.net>, Copyright (C)2008
|
||||
|
||||
This program is free software; you can redistribute it and/or modify
|
||||
it under the terms of the GNU General Public License as published by
|
||||
the Free Software Foundation; either version 2 of the License, or
|
||||
(at your option) any later version.
|
||||
|
||||
This program is distributed in the hope that it will be useful,
|
||||
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
GNU General Public License for more details.
|
||||
|
||||
You should have received a copy of the GNU General Public License
|
||||
along with this program; if not, write to the Free Software
|
||||
Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
|
||||
02110-1301 USA.
|
||||
*/
|
||||
|
||||
// Own
|
||||
#include "ScreenWindow.h"
|
||||
#include "ScreenWindow.moc"
|
||||
|
||||
// Qt
|
||||
#include <QtCore>
|
||||
|
||||
// Konsole
|
||||
#include "Screen.h"
|
||||
|
||||
|
||||
using namespace Konsole;
|
||||
|
||||
ScreenWindow::ScreenWindow(QObject* parent)
|
||||
: QObject(parent)
|
||||
, _windowBuffer(0)
|
||||
, _windowBufferSize(0)
|
||||
, _bufferNeedsUpdate(true)
|
||||
, _windowLines(1)
|
||||
, _currentLine(0)
|
||||
, _trackOutput(true)
|
||||
, _scrollCount(0)
|
||||
{
|
||||
}
|
||||
ScreenWindow::~ScreenWindow()
|
||||
{
|
||||
delete[] _windowBuffer;
|
||||
}
|
||||
void ScreenWindow::setScreen(Screen* screen)
|
||||
{
|
||||
Q_ASSERT( screen );
|
||||
|
||||
_screen = screen;
|
||||
}
|
||||
|
||||
Screen* ScreenWindow::screen() const
|
||||
{
|
||||
return _screen;
|
||||
}
|
||||
|
||||
Character* ScreenWindow::getImage()
|
||||
{
|
||||
// reallocate internal buffer if the window size has changed
|
||||
int size = windowLines() * windowColumns();
|
||||
if (_windowBuffer == 0 || _windowBufferSize != size)
|
||||
{
|
||||
delete[] _windowBuffer;
|
||||
_windowBufferSize = size;
|
||||
_windowBuffer = new Character[size];
|
||||
_bufferNeedsUpdate = true;
|
||||
}
|
||||
|
||||
if (!_bufferNeedsUpdate)
|
||||
return _windowBuffer;
|
||||
|
||||
_screen->getImage(_windowBuffer,size,
|
||||
currentLine(),endWindowLine());
|
||||
|
||||
// this window may look beyond the end of the screen, in which
|
||||
// case there will be an unused area which needs to be filled
|
||||
// with blank characters
|
||||
fillUnusedArea();
|
||||
|
||||
_bufferNeedsUpdate = false;
|
||||
return _windowBuffer;
|
||||
}
|
||||
|
||||
void ScreenWindow::fillUnusedArea()
|
||||
{
|
||||
int screenEndLine = _screen->getHistLines() + _screen->getLines() - 1;
|
||||
int windowEndLine = currentLine() + windowLines() - 1;
|
||||
|
||||
int unusedLines = windowEndLine - screenEndLine;
|
||||
int charsToFill = unusedLines * windowColumns();
|
||||
|
||||
Screen::fillWithDefaultChar(_windowBuffer + _windowBufferSize - charsToFill,charsToFill);
|
||||
}
|
||||
|
||||
// return the index of the line at the end of this window, or if this window
|
||||
// goes beyond the end of the screen, the index of the line at the end
|
||||
// of the screen.
|
||||
//
|
||||
// when passing a line number to a Screen method, the line number should
|
||||
// never be more than endWindowLine()
|
||||
//
|
||||
int ScreenWindow::endWindowLine() const
|
||||
{
|
||||
return qMin(currentLine() + windowLines() - 1,
|
||||
lineCount() - 1);
|
||||
}
|
||||
QVector<LineProperty> ScreenWindow::getLineProperties()
|
||||
{
|
||||
QVector<LineProperty> result = _screen->getLineProperties(currentLine(),endWindowLine());
|
||||
|
||||
if (result.count() != windowLines())
|
||||
result.resize(windowLines());
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
QString ScreenWindow::selectedText( bool preserveLineBreaks ) const
|
||||
{
|
||||
return _screen->selectedText( preserveLineBreaks );
|
||||
}
|
||||
|
||||
void ScreenWindow::getSelectionStart( int& column , int& line )
|
||||
{
|
||||
_screen->getSelectionStart(column,line);
|
||||
line -= currentLine();
|
||||
}
|
||||
void ScreenWindow::getSelectionEnd( int& column , int& line )
|
||||
{
|
||||
_screen->getSelectionEnd(column,line);
|
||||
line -= currentLine();
|
||||
}
|
||||
void ScreenWindow::setSelectionStart( int column , int line , bool columnMode )
|
||||
{
|
||||
_screen->setSelectionStart( column , qMin(line + currentLine(),endWindowLine()) , columnMode);
|
||||
|
||||
_bufferNeedsUpdate = true;
|
||||
emit selectionChanged();
|
||||
}
|
||||
|
||||
void ScreenWindow::setSelectionEnd( int column , int line )
|
||||
{
|
||||
_screen->setSelectionEnd( column , qMin(line + currentLine(),endWindowLine()) );
|
||||
|
||||
_bufferNeedsUpdate = true;
|
||||
emit selectionChanged();
|
||||
}
|
||||
|
||||
bool ScreenWindow::isSelected( int column , int line )
|
||||
{
|
||||
return _screen->isSelected( column , qMin(line + currentLine(),endWindowLine()) );
|
||||
}
|
||||
|
||||
void ScreenWindow::clearSelection()
|
||||
{
|
||||
_screen->clearSelection();
|
||||
|
||||
emit selectionChanged();
|
||||
}
|
||||
|
||||
void ScreenWindow::setWindowLines(int lines)
|
||||
{
|
||||
Q_ASSERT(lines > 0);
|
||||
_windowLines = lines;
|
||||
}
|
||||
int ScreenWindow::windowLines() const
|
||||
{
|
||||
return _windowLines;
|
||||
}
|
||||
|
||||
int ScreenWindow::windowColumns() const
|
||||
{
|
||||
return _screen->getColumns();
|
||||
}
|
||||
|
||||
int ScreenWindow::lineCount() const
|
||||
{
|
||||
return _screen->getHistLines() + _screen->getLines();
|
||||
}
|
||||
|
||||
int ScreenWindow::columnCount() const
|
||||
{
|
||||
return _screen->getColumns();
|
||||
}
|
||||
|
||||
QPoint ScreenWindow::cursorPosition() const
|
||||
{
|
||||
QPoint position;
|
||||
|
||||
position.setX( _screen->getCursorX() );
|
||||
position.setY( _screen->getCursorY() );
|
||||
|
||||
return position;
|
||||
}
|
||||
|
||||
int ScreenWindow::currentLine() const
|
||||
{
|
||||
return qBound(0,_currentLine,lineCount()-windowLines());
|
||||
}
|
||||
|
||||
void ScreenWindow::scrollBy( RelativeScrollMode mode , int amount )
|
||||
{
|
||||
if ( mode == ScrollLines )
|
||||
{
|
||||
scrollTo( currentLine() + amount );
|
||||
}
|
||||
else if ( mode == ScrollPages )
|
||||
{
|
||||
scrollTo( currentLine() + amount * ( windowLines() / 2 ) );
|
||||
}
|
||||
}
|
||||
|
||||
bool ScreenWindow::atEndOfOutput() const
|
||||
{
|
||||
return currentLine() == (lineCount()-windowLines());
|
||||
}
|
||||
|
||||
void ScreenWindow::scrollTo( int line )
|
||||
{
|
||||
int maxCurrentLineNumber = lineCount() - windowLines();
|
||||
line = qBound(0,line,maxCurrentLineNumber);
|
||||
|
||||
const int delta = line - _currentLine;
|
||||
_currentLine = line;
|
||||
|
||||
// keep track of number of lines scrolled by,
|
||||
// this can be reset by calling resetScrollCount()
|
||||
_scrollCount += delta;
|
||||
|
||||
_bufferNeedsUpdate = true;
|
||||
|
||||
emit scrolled(_currentLine);
|
||||
}
|
||||
|
||||
void ScreenWindow::setTrackOutput(bool trackOutput)
|
||||
{
|
||||
_trackOutput = trackOutput;
|
||||
}
|
||||
|
||||
bool ScreenWindow::trackOutput() const
|
||||
{
|
||||
return _trackOutput;
|
||||
}
|
||||
|
||||
int ScreenWindow::scrollCount() const
|
||||
{
|
||||
return _scrollCount;
|
||||
}
|
||||
|
||||
void ScreenWindow::resetScrollCount()
|
||||
{
|
||||
_scrollCount = 0;
|
||||
}
|
||||
|
||||
QRect ScreenWindow::scrollRegion() const
|
||||
{
|
||||
bool equalToScreenSize = windowLines() == _screen->getLines();
|
||||
|
||||
if ( atEndOfOutput() && equalToScreenSize )
|
||||
return _screen->lastScrolledRegion();
|
||||
else
|
||||
return QRect(0,0,windowColumns(),windowLines());
|
||||
}
|
||||
|
||||
void ScreenWindow::notifyOutputChanged()
|
||||
{
|
||||
// move window to the bottom of the screen and update scroll count
|
||||
// if this window is currently tracking the bottom of the screen
|
||||
if ( _trackOutput )
|
||||
{
|
||||
_scrollCount -= _screen->scrolledLines();
|
||||
_currentLine = qMax(0,_screen->getHistLines() - (windowLines()-_screen->getLines()));
|
||||
}
|
||||
else
|
||||
{
|
||||
// if the history is not unlimited then it may
|
||||
// have run out of space and dropped the oldest
|
||||
// lines of output - in this case the screen
|
||||
// window's current line number will need to
|
||||
// be adjusted - otherwise the output will scroll
|
||||
_currentLine = qMax(0,_currentLine -
|
||||
_screen->droppedLines());
|
||||
|
||||
// ensure that the screen window's current position does
|
||||
// not go beyond the bottom of the screen
|
||||
_currentLine = qMin( _currentLine , _screen->getHistLines() );
|
||||
}
|
||||
|
||||
_bufferNeedsUpdate = true;
|
||||
|
||||
emit outputChanged();
|
||||
}
|
||||
|
||||
//#include "moc_ScreenWindow.cpp"
|
File diff suppressed because it is too large
Load Diff
|
@ -0,0 +1,168 @@
|
|||
/*
|
||||
Copyright (C) 2007 by Robert Knight <robertknight@gmail.com>
|
||||
|
||||
Rewritten for QT4 by e_k <e_k at users.sourceforge.net>, Copyright (C)2008
|
||||
|
||||
This program is free software; you can redistribute it and/or modify
|
||||
it under the terms of the GNU General Public License as published by
|
||||
the Free Software Foundation; either version 2 of the License, or
|
||||
(at your option) any later version.
|
||||
|
||||
This program is distributed in the hope that it will be useful,
|
||||
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
GNU General Public License for more details.
|
||||
|
||||
You should have received a copy of the GNU General Public License
|
||||
along with this program; if not, write to the Free Software
|
||||
Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
|
||||
02110-1301 USA.
|
||||
*/
|
||||
|
||||
// Own
|
||||
#include "ShellCommand.h"
|
||||
|
||||
//some versions of gcc(4.3) require explicit include
|
||||
#include <cstdlib>
|
||||
|
||||
|
||||
using namespace Konsole;
|
||||
|
||||
// expands environment variables in 'text'
|
||||
// function copied from kdelibs/kio/kio/kurlcompletion.cpp
|
||||
static bool expandEnv(QString& text);
|
||||
|
||||
ShellCommand::ShellCommand(const QString& fullCommand)
|
||||
{
|
||||
bool inQuotes = false;
|
||||
|
||||
QString builder;
|
||||
|
||||
for ( int i = 0 ; i < fullCommand.count() ; i++ )
|
||||
{
|
||||
QChar ch = fullCommand[i];
|
||||
|
||||
const bool isLastChar = ( i == fullCommand.count() - 1 );
|
||||
const bool isQuote = ( ch == '\'' || ch == '\"' );
|
||||
|
||||
if ( !isLastChar && isQuote )
|
||||
inQuotes = !inQuotes;
|
||||
else
|
||||
{
|
||||
if ( (!ch.isSpace() || inQuotes) && !isQuote )
|
||||
builder.append(ch);
|
||||
|
||||
if ( (ch.isSpace() && !inQuotes) || ( i == fullCommand.count()-1 ) )
|
||||
{
|
||||
_arguments << builder;
|
||||
builder.clear();
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
ShellCommand::ShellCommand(const QString& command , const QStringList& arguments)
|
||||
{
|
||||
_arguments = arguments;
|
||||
|
||||
if ( !_arguments.isEmpty() )
|
||||
_arguments[0] == command;
|
||||
}
|
||||
QString ShellCommand::fullCommand() const
|
||||
{
|
||||
return _arguments.join(QChar(' '));
|
||||
}
|
||||
QString ShellCommand::command() const
|
||||
{
|
||||
if ( !_arguments.isEmpty() )
|
||||
return _arguments[0];
|
||||
else
|
||||
return QString();
|
||||
}
|
||||
QStringList ShellCommand::arguments() const
|
||||
{
|
||||
return _arguments;
|
||||
}
|
||||
bool ShellCommand::isRootCommand() const
|
||||
{
|
||||
Q_ASSERT(0); // not implemented yet
|
||||
return false;
|
||||
}
|
||||
bool ShellCommand::isAvailable() const
|
||||
{
|
||||
Q_ASSERT(0); // not implemented yet
|
||||
return false;
|
||||
}
|
||||
QStringList ShellCommand::expand(const QStringList& items)
|
||||
{
|
||||
QStringList result;
|
||||
|
||||
foreach( QString item , items )
|
||||
result << expand(item);
|
||||
|
||||
return result;
|
||||
}
|
||||
QString ShellCommand::expand(const QString& text)
|
||||
{
|
||||
QString result = text;
|
||||
expandEnv(result);
|
||||
return result;
|
||||
}
|
||||
|
||||
/*
|
||||
* expandEnv
|
||||
*
|
||||
* Expand environment variables in text. Escaped '$' characters are ignored.
|
||||
* Return true if any variables were expanded
|
||||
*/
|
||||
static bool expandEnv( QString &text )
|
||||
{
|
||||
// Find all environment variables beginning with '$'
|
||||
//
|
||||
int pos = 0;
|
||||
|
||||
bool expanded = false;
|
||||
|
||||
while ( (pos = text.indexOf(QLatin1Char('$'), pos)) != -1 ) {
|
||||
|
||||
// Skip escaped '$'
|
||||
//
|
||||
if ( pos > 0 && text.at(pos-1) == QLatin1Char('\\') ) {
|
||||
pos++;
|
||||
}
|
||||
// Variable found => expand
|
||||
//
|
||||
else {
|
||||
// Find the end of the variable = next '/' or ' '
|
||||
//
|
||||
int pos2 = text.indexOf( QLatin1Char(' '), pos+1 );
|
||||
int pos_tmp = text.indexOf( QLatin1Char('/'), pos+1 );
|
||||
|
||||
if ( pos2 == -1 || (pos_tmp != -1 && pos_tmp < pos2) )
|
||||
pos2 = pos_tmp;
|
||||
|
||||
if ( pos2 == -1 )
|
||||
pos2 = text.length();
|
||||
|
||||
// Replace if the variable is terminated by '/' or ' '
|
||||
// and defined
|
||||
//
|
||||
if ( pos2 >= 0 ) {
|
||||
int len = pos2 - pos;
|
||||
QString key = text.mid( pos+1, len-1);
|
||||
QString value =
|
||||
QString::fromLocal8Bit( ::getenv(key.toLocal8Bit()) );
|
||||
|
||||
if ( !value.isEmpty() ) {
|
||||
expanded = true;
|
||||
text.replace( pos, len, value );
|
||||
pos = pos + value.length();
|
||||
}
|
||||
else {
|
||||
pos = pos2;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return expanded;
|
||||
}
|
|
@ -0,0 +1,227 @@
|
|||
/*
|
||||
This file is part of Konsole, an X terminal.
|
||||
|
||||
Copyright (C) 2006 by Robert Knight <robertknight@gmail.com>
|
||||
|
||||
Rewritten for QT4 by e_k <e_k at users.sourceforge.net>, Copyright (C)2008
|
||||
|
||||
This program is free software; you can redistribute it and/or modify
|
||||
it under the terms of the GNU Lesser General Public License as published by
|
||||
the Free Software Foundation; either version 2 of the License, or
|
||||
(at your option) any later version.
|
||||
|
||||
This program is distributed in the hope that it will be useful,
|
||||
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
GNU General Public License for more details.
|
||||
|
||||
You should have received a copy of the GNU Lesser General Public License
|
||||
along with this program; if not, write to the Free Software
|
||||
Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
|
||||
02110-1301 USA.
|
||||
*/
|
||||
|
||||
// Own
|
||||
#include "TerminalCharacterDecoder.h"
|
||||
|
||||
// Qt
|
||||
#include <QtCore/QTextStream>
|
||||
|
||||
|
||||
using namespace Konsole;
|
||||
|
||||
PlainTextDecoder::PlainTextDecoder()
|
||||
: _output(0)
|
||||
, _includeTrailingWhitespace(true)
|
||||
{
|
||||
|
||||
}
|
||||
void PlainTextDecoder::setTrailingWhitespace(bool enable)
|
||||
{
|
||||
_includeTrailingWhitespace = enable;
|
||||
}
|
||||
bool PlainTextDecoder::trailingWhitespace() const
|
||||
{
|
||||
return _includeTrailingWhitespace;
|
||||
}
|
||||
void PlainTextDecoder::begin(QTextStream* output)
|
||||
{
|
||||
_output = output;
|
||||
}
|
||||
void PlainTextDecoder::end()
|
||||
{
|
||||
_output = 0;
|
||||
}
|
||||
void PlainTextDecoder::decodeLine(const Character* const characters, int count, LineProperty /*properties*/
|
||||
)
|
||||
{
|
||||
Q_ASSERT( _output );
|
||||
|
||||
//TODO should we ignore or respect the LINE_WRAPPED line property?
|
||||
|
||||
//note: we build up a QString and send it to the text stream rather writing into the text
|
||||
//stream a character at a time because it is more efficient.
|
||||
//(since QTextStream always deals with QStrings internally anyway)
|
||||
QString plainText;
|
||||
plainText.reserve(count);
|
||||
|
||||
int outputCount = count;
|
||||
|
||||
// if inclusion of trailing whitespace is disabled then find the end of the
|
||||
// line
|
||||
if ( !_includeTrailingWhitespace )
|
||||
{
|
||||
for (int i = count-1 ; i >= 0 ; i--)
|
||||
{
|
||||
if ( characters[i].character != ' ' )
|
||||
break;
|
||||
else
|
||||
outputCount--;
|
||||
}
|
||||
}
|
||||
|
||||
for (int i=0;i<outputCount;i++)
|
||||
{
|
||||
plainText.append( QChar(characters[i].character) );
|
||||
}
|
||||
|
||||
*_output << plainText;
|
||||
}
|
||||
|
||||
HTMLDecoder::HTMLDecoder() :
|
||||
_output(0)
|
||||
,_colorTable(base_color_table)
|
||||
,_innerSpanOpen(false)
|
||||
,_lastRendition(DEFAULT_RENDITION)
|
||||
{
|
||||
|
||||
}
|
||||
|
||||
void HTMLDecoder::begin(QTextStream* output)
|
||||
{
|
||||
_output = output;
|
||||
|
||||
QString text;
|
||||
|
||||
//open monospace span
|
||||
openSpan(text,"font-family:monospace");
|
||||
|
||||
*output << text;
|
||||
}
|
||||
|
||||
void HTMLDecoder::end()
|
||||
{
|
||||
Q_ASSERT( _output );
|
||||
|
||||
QString text;
|
||||
|
||||
closeSpan(text);
|
||||
|
||||
*_output << text;
|
||||
|
||||
_output = 0;
|
||||
|
||||
}
|
||||
|
||||
//TODO: Support for LineProperty (mainly double width , double height)
|
||||
void HTMLDecoder::decodeLine(const Character* const characters, int count, LineProperty /*properties*/
|
||||
)
|
||||
{
|
||||
Q_ASSERT( _output );
|
||||
|
||||
QString text;
|
||||
|
||||
int spaceCount = 0;
|
||||
|
||||
for (int i=0;i<count;i++)
|
||||
{
|
||||
QChar ch(characters[i].character);
|
||||
|
||||
//check if appearance of character is different from previous char
|
||||
if ( characters[i].rendition != _lastRendition ||
|
||||
characters[i].foregroundColor != _lastForeColor ||
|
||||
characters[i].backgroundColor != _lastBackColor )
|
||||
{
|
||||
if ( _innerSpanOpen )
|
||||
closeSpan(text);
|
||||
|
||||
_lastRendition = characters[i].rendition;
|
||||
_lastForeColor = characters[i].foregroundColor;
|
||||
_lastBackColor = characters[i].backgroundColor;
|
||||
|
||||
//build up style string
|
||||
QString style;
|
||||
|
||||
if ( _lastRendition & RE_BOLD ||
|
||||
(_colorTable && characters[i].isBold(_colorTable)) )
|
||||
style.append("font-weight:bold;");
|
||||
|
||||
|
||||
if ( _lastRendition & RE_UNDERLINE )
|
||||
style.append("font-decoration:underline;");
|
||||
|
||||
//colours - a colour table must have been defined first
|
||||
if ( _colorTable )
|
||||
{
|
||||
style.append( QString("color:%1;").arg(_lastForeColor.color(_colorTable).name() ) );
|
||||
|
||||
if (!characters[i].isTransparent(_colorTable))
|
||||
{
|
||||
style.append( QString("background-color:%1;").arg(_lastBackColor.color(_colorTable).name() ) );
|
||||
}
|
||||
}
|
||||
|
||||
//open the span with the current style
|
||||
openSpan(text,style);
|
||||
_innerSpanOpen = true;
|
||||
}
|
||||
|
||||
//handle whitespace
|
||||
if (ch.isSpace())
|
||||
spaceCount++;
|
||||
else
|
||||
spaceCount = 0;
|
||||
|
||||
|
||||
//output current character
|
||||
if (spaceCount < 2)
|
||||
{
|
||||
//escape HTML tag characters and just display others as they are
|
||||
if ( ch == '<' )
|
||||
text.append("<");
|
||||
else if (ch == '>')
|
||||
text.append(">");
|
||||
else
|
||||
text.append(ch);
|
||||
}
|
||||
else
|
||||
{
|
||||
text.append(" "); //HTML truncates multiple spaces, so use a space marker instead
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
//close any remaining open inner spans
|
||||
if ( _innerSpanOpen )
|
||||
closeSpan(text);
|
||||
|
||||
//start new line
|
||||
text.append("<br>");
|
||||
|
||||
*_output << text;
|
||||
}
|
||||
|
||||
void HTMLDecoder::openSpan(QString& text , const QString& style)
|
||||
{
|
||||
text.append( QString("<span style=\"%1\">").arg(style) );
|
||||
}
|
||||
|
||||
void HTMLDecoder::closeSpan(QString& text)
|
||||
{
|
||||
text.append("</span>");
|
||||
}
|
||||
|
||||
void HTMLDecoder::setColorTable(const ColorEntry* table)
|
||||
{
|
||||
_colorTable = table;
|
||||
}
|
File diff suppressed because it is too large
Load Diff
File diff suppressed because it is too large
Load Diff
File diff suppressed because it is too large
Load Diff
|
@ -0,0 +1,336 @@
|
|||
/* This file is part of the KDE libraries
|
||||
Copyright (C) 1997 Christian Czezakte (e9025461@student.tuwien.ac.at)
|
||||
|
||||
Rewritten for QT4 by e_k <e_k at users.sourceforge.net>, Copyright (C)2008
|
||||
|
||||
This library is free software; you can redistribute it and/or
|
||||
modify it under the terms of the GNU Library General Public
|
||||
License as published by the Free Software Foundation; either
|
||||
version 2 of the License, or (at your option) any later version.
|
||||
|
||||
This library is distributed in the hope that it will be useful,
|
||||
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
|
||||
Library General Public License for more details.
|
||||
|
||||
You should have received a copy of the GNU Library General Public License
|
||||
along with this library; see the file COPYING.LIB. If not, write to
|
||||
the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
|
||||
Boston, MA 02110-1301, USA.
|
||||
*/
|
||||
|
||||
#include "k3processcontroller.h"
|
||||
#include "k3processcontroller.moc"
|
||||
#include "k3process.h"
|
||||
|
||||
//#include <config.h>
|
||||
|
||||
#include <sys/time.h>
|
||||
#include <sys/types.h>
|
||||
#include <sys/wait.h>
|
||||
#include <unistd.h>
|
||||
#include <errno.h>
|
||||
#include <fcntl.h>
|
||||
#include <stdio.h>
|
||||
#include <stdlib.h>
|
||||
|
||||
#include <QtCore/QSocketNotifier>
|
||||
|
||||
|
||||
class K3ProcessController::Private
|
||||
{
|
||||
public:
|
||||
Private()
|
||||
: needcheck( false ),
|
||||
notifier( 0 )
|
||||
{
|
||||
}
|
||||
|
||||
~Private()
|
||||
{
|
||||
delete notifier;
|
||||
}
|
||||
|
||||
int fd[2];
|
||||
bool needcheck;
|
||||
QSocketNotifier *notifier;
|
||||
QList<K3Process*> kProcessList;
|
||||
QList<int> unixProcessList;
|
||||
static struct sigaction oldChildHandlerData;
|
||||
static bool handlerSet;
|
||||
static int refCount;
|
||||
static K3ProcessController* instance;
|
||||
};
|
||||
|
||||
K3ProcessController *K3ProcessController::Private::instance = 0;
|
||||
int K3ProcessController::Private::refCount = 0;
|
||||
|
||||
void K3ProcessController::ref()
|
||||
{
|
||||
if ( !Private::refCount ) {
|
||||
Private::instance = new K3ProcessController;
|
||||
setupHandlers();
|
||||
}
|
||||
Private::refCount++;
|
||||
}
|
||||
|
||||
void K3ProcessController::deref()
|
||||
{
|
||||
Private::refCount--;
|
||||
if( !Private::refCount ) {
|
||||
resetHandlers();
|
||||
delete Private::instance;
|
||||
Private::instance = 0;
|
||||
}
|
||||
}
|
||||
|
||||
K3ProcessController* K3ProcessController::instance()
|
||||
{
|
||||
/*
|
||||
* there were no safety guards in previous revisions, is that ok?
|
||||
if ( !Private::instance ) {
|
||||
ref();
|
||||
}
|
||||
*/
|
||||
|
||||
return Private::instance;
|
||||
}
|
||||
|
||||
K3ProcessController::K3ProcessController()
|
||||
: d( new Private )
|
||||
{
|
||||
if( pipe( d->fd ) )
|
||||
{
|
||||
perror( "pipe" );
|
||||
abort();
|
||||
}
|
||||
|
||||
fcntl( d->fd[0], F_SETFL, O_NONBLOCK ); // in case slotDoHousekeeping is called without polling first
|
||||
fcntl( d->fd[1], F_SETFL, O_NONBLOCK ); // in case it fills up
|
||||
fcntl( d->fd[0], F_SETFD, FD_CLOEXEC );
|
||||
fcntl( d->fd[1], F_SETFD, FD_CLOEXEC );
|
||||
|
||||
d->notifier = new QSocketNotifier( d->fd[0], QSocketNotifier::Read );
|
||||
d->notifier->setEnabled( true );
|
||||
QObject::connect( d->notifier, SIGNAL(activated(int)),
|
||||
SLOT(slotDoHousekeeping()));
|
||||
}
|
||||
|
||||
K3ProcessController::~K3ProcessController()
|
||||
{
|
||||
#ifndef Q_OS_MAC
|
||||
/* not sure why, but this is causing lockups */
|
||||
close( d->fd[0] );
|
||||
close( d->fd[1] );
|
||||
#else
|
||||
#warning FIXME: why does close() freeze up destruction?
|
||||
#endif
|
||||
|
||||
delete d;
|
||||
}
|
||||
|
||||
|
||||
extern "C" {
|
||||
static void theReaper( int num )
|
||||
{
|
||||
K3ProcessController::theSigCHLDHandler( num );
|
||||
}
|
||||
}
|
||||
|
||||
#ifdef Q_OS_UNIX
|
||||
struct sigaction K3ProcessController::Private::oldChildHandlerData;
|
||||
#endif
|
||||
bool K3ProcessController::Private::handlerSet = false;
|
||||
|
||||
void K3ProcessController::setupHandlers()
|
||||
{
|
||||
if( Private::handlerSet )
|
||||
return;
|
||||
Private::handlerSet = true;
|
||||
|
||||
#ifdef Q_OS_UNIX
|
||||
struct sigaction act;
|
||||
sigemptyset( &act.sa_mask );
|
||||
|
||||
act.sa_handler = SIG_IGN;
|
||||
act.sa_flags = 0;
|
||||
sigaction( SIGPIPE, &act, 0L );
|
||||
|
||||
act.sa_handler = theReaper;
|
||||
act.sa_flags = SA_NOCLDSTOP;
|
||||
// CC: take care of SunOS which automatically restarts interrupted system
|
||||
// calls (and thus does not have SA_RESTART)
|
||||
#ifdef SA_RESTART
|
||||
act.sa_flags |= SA_RESTART;
|
||||
#endif
|
||||
sigaction( SIGCHLD, &act, &Private::oldChildHandlerData );
|
||||
|
||||
sigaddset( &act.sa_mask, SIGCHLD );
|
||||
// Make sure we don't block this signal. gdb tends to do that :-(
|
||||
sigprocmask( SIG_UNBLOCK, &act.sa_mask, 0 );
|
||||
#else
|
||||
//TODO: win32
|
||||
#endif
|
||||
}
|
||||
|
||||
void K3ProcessController::resetHandlers()
|
||||
{
|
||||
if( !Private::handlerSet )
|
||||
return;
|
||||
Private::handlerSet = false;
|
||||
|
||||
#ifdef Q_OS_UNIX
|
||||
sigset_t mask, omask;
|
||||
sigemptyset( &mask );
|
||||
sigaddset( &mask, SIGCHLD );
|
||||
sigprocmask( SIG_BLOCK, &mask, &omask );
|
||||
|
||||
struct sigaction act;
|
||||
sigaction( SIGCHLD, &Private::oldChildHandlerData, &act );
|
||||
if (act.sa_handler != theReaper) {
|
||||
sigaction( SIGCHLD, &act, 0 );
|
||||
Private::handlerSet = true;
|
||||
}
|
||||
|
||||
sigprocmask( SIG_SETMASK, &omask, 0 );
|
||||
#else
|
||||
//TODO: win32
|
||||
#endif
|
||||
// there should be no problem with SIGPIPE staying SIG_IGN
|
||||
}
|
||||
|
||||
// the pipe is needed to sync the child reaping with our event processing,
|
||||
// as otherwise there are race conditions, locking requirements, and things
|
||||
// generally get harder
|
||||
void K3ProcessController::theSigCHLDHandler( int arg )
|
||||
{
|
||||
int saved_errno = errno;
|
||||
|
||||
char dummy = 0;
|
||||
::write( instance()->d->fd[1], &dummy, 1 );
|
||||
|
||||
#ifdef Q_OS_UNIX
|
||||
if ( Private::oldChildHandlerData.sa_handler != SIG_IGN &&
|
||||
Private::oldChildHandlerData.sa_handler != SIG_DFL ) {
|
||||
Private::oldChildHandlerData.sa_handler( arg ); // call the old handler
|
||||
}
|
||||
#else
|
||||
//TODO: win32
|
||||
#endif
|
||||
|
||||
errno = saved_errno;
|
||||
}
|
||||
|
||||
int K3ProcessController::notifierFd() const
|
||||
{
|
||||
return d->fd[0];
|
||||
}
|
||||
|
||||
void K3ProcessController::unscheduleCheck()
|
||||
{
|
||||
char dummy[16]; // somewhat bigger - just in case several have queued up
|
||||
if( ::read( d->fd[0], dummy, sizeof(dummy) ) > 0 )
|
||||
d->needcheck = true;
|
||||
}
|
||||
|
||||
void
|
||||
K3ProcessController::rescheduleCheck()
|
||||
{
|
||||
if( d->needcheck )
|
||||
{
|
||||
d->needcheck = false;
|
||||
char dummy = 0;
|
||||
::write( d->fd[1], &dummy, 1 );
|
||||
}
|
||||
}
|
||||
|
||||
void K3ProcessController::slotDoHousekeeping()
|
||||
{
|
||||
char dummy[16]; // somewhat bigger - just in case several have queued up
|
||||
::read( d->fd[0], dummy, sizeof(dummy) );
|
||||
|
||||
int status;
|
||||
again:
|
||||
QList<K3Process*>::iterator it( d->kProcessList.begin() );
|
||||
QList<K3Process*>::iterator eit( d->kProcessList.end() );
|
||||
while( it != eit )
|
||||
{
|
||||
K3Process *prc = *it;
|
||||
if( prc->runs && waitpid( prc->pid_, &status, WNOHANG ) > 0 )
|
||||
{
|
||||
prc->processHasExited( status );
|
||||
// the callback can nuke the whole process list and even 'this'
|
||||
if (!instance())
|
||||
return;
|
||||
goto again;
|
||||
}
|
||||
++it;
|
||||
}
|
||||
QList<int>::iterator uit( d->unixProcessList.begin() );
|
||||
QList<int>::iterator ueit( d->unixProcessList.end() );
|
||||
while( uit != ueit )
|
||||
{
|
||||
if( waitpid( *uit, 0, WNOHANG ) > 0 )
|
||||
{
|
||||
uit = d->unixProcessList.erase( uit );
|
||||
deref(); // counterpart to addProcess, can invalidate 'this'
|
||||
} else
|
||||
++uit;
|
||||
}
|
||||
}
|
||||
|
||||
bool K3ProcessController::waitForProcessExit( int timeout )
|
||||
{
|
||||
#ifdef Q_OS_UNIX
|
||||
for(;;)
|
||||
{
|
||||
struct timeval tv, *tvp;
|
||||
if (timeout < 0)
|
||||
tvp = 0;
|
||||
else
|
||||
{
|
||||
tv.tv_sec = timeout;
|
||||
tv.tv_usec = 0;
|
||||
tvp = &tv;
|
||||
}
|
||||
|
||||
fd_set fds;
|
||||
FD_ZERO( &fds );
|
||||
FD_SET( d->fd[0], &fds );
|
||||
|
||||
switch( select( d->fd[0]+1, &fds, 0, 0, tvp ) )
|
||||
{
|
||||
case -1:
|
||||
if( errno == EINTR )
|
||||
continue;
|
||||
// fall through; should never happen
|
||||
case 0:
|
||||
return false;
|
||||
default:
|
||||
slotDoHousekeeping();
|
||||
return true;
|
||||
}
|
||||
}
|
||||
#else
|
||||
//TODO: win32
|
||||
return false;
|
||||
#endif
|
||||
}
|
||||
|
||||
void K3ProcessController::addKProcess( K3Process* p )
|
||||
{
|
||||
d->kProcessList.append( p );
|
||||
}
|
||||
|
||||
void K3ProcessController::removeKProcess( K3Process* p )
|
||||
{
|
||||
d->kProcessList.removeAll( p );
|
||||
}
|
||||
|
||||
void K3ProcessController::addProcess( int pid )
|
||||
{
|
||||
d->unixProcessList.append( pid );
|
||||
ref(); // make sure we stay around when the K3Process goes away
|
||||
}
|
||||
|
||||
//#include "moc_k3processcontroller.cpp"
|
|
@ -0,0 +1,4 @@
|
|||
/default.keytab/1.1.1.1/Sat May 10 21:27:57 2008//
|
||||
/linux.keytab/1.1.1.1/Sat May 10 21:27:57 2008//
|
||||
/vt420pc.keytab/1.1.1.1/Sat May 10 21:27:57 2008//
|
||||
D
|
|
@ -0,0 +1 @@
|
|||
qtermwidget/lib/kb-layouts
|
|
@ -0,0 +1 @@
|
|||
:ext:e_k@qtermwidget.cvs.sourceforge.net:/cvsroot/qtermwidget
|
|
@ -0,0 +1,133 @@
|
|||
# [README.default.Keytab] Buildin Keyboard Table
|
||||
#
|
||||
# To customize your keyboard, copy this file to something
|
||||
# ending with .keytab and change it to meet you needs.
|
||||
# Please read the README.KeyTab and the README.keyboard
|
||||
# in this case.
|
||||
#
|
||||
# --------------------------------------------------------------
|
||||
|
||||
keyboard "Default (XFree 4)"
|
||||
|
||||
# --------------------------------------------------------------
|
||||
#
|
||||
# Note that this particular table is a "risc" version made to
|
||||
# ease customization without bothering with obsolete details.
|
||||
# See VT100.keytab for the more hairy stuff.
|
||||
#
|
||||
# --------------------------------------------------------------
|
||||
|
||||
# common keys
|
||||
|
||||
key Escape : "\E"
|
||||
|
||||
key Tab -Shift : "\t"
|
||||
key Tab +Shift+Ansi : "\E[Z"
|
||||
key Tab +Shift-Ansi : "\t"
|
||||
key Backtab +Ansi : "\E[Z"
|
||||
key Backtab -Ansi : "\t"
|
||||
|
||||
key Return-Shift-NewLine : "\r"
|
||||
key Return-Shift+NewLine : "\r\n"
|
||||
|
||||
key Return+Shift : "\EOM"
|
||||
|
||||
# Backspace and Delete codes are preserving CTRL-H.
|
||||
|
||||
key Backspace : "\x7f"
|
||||
|
||||
# Arrow keys in VT52 mode
|
||||
# shift up/down are reserved for scrolling.
|
||||
# shift left/right are reserved for switching between tabs (this is hardcoded).
|
||||
|
||||
key Up -Shift-Ansi : "\EA"
|
||||
key Down -Shift-Ansi : "\EB"
|
||||
key Right-Shift-Ansi : "\EC"
|
||||
key Left -Shift-Ansi : "\ED"
|
||||
|
||||
# Arrow keys in ANSI mode with Application - and Normal Cursor Mode)
|
||||
|
||||
key Up -Shift-AnyMod+Ansi+AppCuKeys : "\EOA"
|
||||
key Down -Shift-AnyMod+Ansi+AppCuKeys : "\EOB"
|
||||
key Right -Shift-AnyMod+Ansi+AppCuKeys : "\EOC"
|
||||
key Left -Shift-AnyMod+Ansi+AppCuKeys : "\EOD"
|
||||
|
||||
key Up -Shift-AnyMod+Ansi-AppCuKeys : "\E[A"
|
||||
key Down -Shift-AnyMod+Ansi-AppCuKeys : "\E[B"
|
||||
key Right -Shift-AnyMod+Ansi-AppCuKeys : "\E[C"
|
||||
key Left -Shift-AnyMod+Ansi-AppCuKeys : "\E[D"
|
||||
|
||||
key Up -Shift+AnyMod+Ansi : "\E[1;*A"
|
||||
key Down -Shift+AnyMod+Ansi : "\E[1;*B"
|
||||
key Right -Shift+AnyMod+Ansi : "\E[1;*C"
|
||||
key Left -Shift+AnyMod+Ansi : "\E[1;*D"
|
||||
|
||||
# other grey PC keys
|
||||
|
||||
key Enter+NewLine : "\r\n"
|
||||
key Enter-NewLine : "\r"
|
||||
|
||||
key Home -AnyMod -AppCuKeys : "\E[H"
|
||||
key End -AnyMod -AppCuKeys : "\E[F"
|
||||
key Home -AnyMod +AppCuKeys : "\EOH"
|
||||
key End -AnyMod +AppCuKeys : "\EOF"
|
||||
key Home +AnyMod : "\E[1;*H"
|
||||
key End +AnyMod : "\E[1;*F"
|
||||
|
||||
key Insert -AnyMod : "\E[2~"
|
||||
key Delete -AnyMod : "\E[3~"
|
||||
key Insert +AnyMod : "\E[2;*~"
|
||||
key Delete +AnyMod : "\E[3;*~"
|
||||
|
||||
key Prior -Shift-AnyMod : "\E[5~"
|
||||
key Next -Shift-AnyMod : "\E[6~"
|
||||
key Prior -Shift+AnyMod : "\E[5;*~"
|
||||
key Next -Shift+AnyMod : "\E[6;*~"
|
||||
|
||||
# Function keys
|
||||
key F1 -AnyMod : "\EOP"
|
||||
key F2 -AnyMod : "\EOQ"
|
||||
key F3 -AnyMod : "\EOR"
|
||||
key F4 -AnyMod : "\EOS"
|
||||
key F5 -AnyMod : "\E[15~"
|
||||
key F6 -AnyMod : "\E[17~"
|
||||
key F7 -AnyMod : "\E[18~"
|
||||
key F8 -AnyMod : "\E[19~"
|
||||
key F9 -AnyMod : "\E[20~"
|
||||
key F10 -AnyMod : "\E[21~"
|
||||
key F11 -AnyMod : "\E[23~"
|
||||
key F12 -AnyMod : "\E[24~"
|
||||
|
||||
key F1 +AnyMod : "\EO*P"
|
||||
key F2 +AnyMod : "\EO*Q"
|
||||
key F3 +AnyMod : "\EO*R"
|
||||
key F4 +AnyMod : "\EO*S"
|
||||
key F5 +AnyMod : "\E[15;*~"
|
||||
key F6 +AnyMod : "\E[17;*~"
|
||||
key F7 +AnyMod : "\E[18;*~"
|
||||
key F8 +AnyMod : "\E[19;*~"
|
||||
key F9 +AnyMod : "\E[20;*~"
|
||||
key F10 +AnyMod : "\E[21;*~"
|
||||
key F11 +AnyMod : "\E[23;*~"
|
||||
key F12 +AnyMod : "\E[24;*~"
|
||||
|
||||
# Work around dead keys
|
||||
|
||||
key Space +Control : "\x00"
|
||||
|
||||
# Some keys are used by konsole to cause operations.
|
||||
# The scroll* operations refer to the history buffer.
|
||||
|
||||
key Up +Shift-AppScreen : scrollLineUp
|
||||
key Prior +Shift-AppScreen : scrollPageUp
|
||||
key Down +Shift-AppScreen : scrollLineDown
|
||||
key Next +Shift-AppScreen : scrollPageDown
|
||||
|
||||
#key Up +Shift : scrollLineUp
|
||||
#key Prior +Shift : scrollPageUp
|
||||
#key Down +Shift : scrollLineDown
|
||||
#key Next +Shift : scrollPageDown
|
||||
|
||||
key ScrollLock : scrollLock
|
||||
|
||||
# keypad characters are not offered differently by Qt.
|
|
@ -0,0 +1,133 @@
|
|||
# [linux.keytab] Konsole Keyboard Table (Linux console keys)
|
||||
#
|
||||
# --------------------------------------------------------------
|
||||
|
||||
# NOT TESTED, MAY NEED SOME CLEANUPS
|
||||
keyboard "Linux console"
|
||||
|
||||
# --------------------------------------------------------------
|
||||
#
|
||||
# This configuration table allows to customize the
|
||||
# meaning of the keys.
|
||||
#
|
||||
# The syntax is that each entry has the form :
|
||||
#
|
||||
# "key" Keyname { ("+"|"-") Modename } ":" (String|Operation)
|
||||
#
|
||||
# Keynames are those defined in <qnamespace.h> with the
|
||||
# "Qt::Key_" removed. (We'd better insert the list here)
|
||||
#
|
||||
# Mode names are :
|
||||
#
|
||||
# - Shift
|
||||
# - Alt
|
||||
# - Control
|
||||
#
|
||||
# The VT100 emulation has two modes that can affect the
|
||||
# sequences emitted by certain keys. These modes are
|
||||
# under control of the client program.
|
||||
#
|
||||
# - Newline : effects Return and Enter key.
|
||||
# - Application : effects Up and Down key.
|
||||
#
|
||||
# - Ansi : effects Up and Down key (This is for VT52, really).
|
||||
#
|
||||
# Operations are
|
||||
#
|
||||
# - scrollUpLine
|
||||
# - scrollUpPage
|
||||
# - scrollDownLine
|
||||
# - scrollDownPage
|
||||
#
|
||||
# - emitSelection
|
||||
#
|
||||
# If the key is not found here, the text of the
|
||||
# key event as provided by QT is emitted, possibly
|
||||
# preceeded by ESC if the Alt key is pressed.
|
||||
#
|
||||
# --------------------------------------------------------------
|
||||
|
||||
key Escape : "\E"
|
||||
key Tab : "\t"
|
||||
|
||||
# VT100 can add an extra \n after return.
|
||||
# The NewLine mode is set by an escape sequence.
|
||||
|
||||
key Return-NewLine : "\r"
|
||||
key Return+NewLine : "\r\n"
|
||||
|
||||
# Some desperately try to save the ^H.
|
||||
|
||||
key Backspace : "\x7f"
|
||||
key Delete : "\E[3~"
|
||||
|
||||
# These codes are for the VT52 mode of VT100
|
||||
# The Ansi mode (i.e. VT100 mode) is set by
|
||||
# an escape sequence
|
||||
|
||||
key Up -Shift-Ansi : "\EA"
|
||||
key Down -Shift-Ansi : "\EB"
|
||||
key Right-Shift-Ansi : "\EC"
|
||||
key Left -Shift-Ansi : "\ED"
|
||||
|
||||
# VT100 emits a mode bit together
|
||||
# with the arrow keys.The AppCuKeys
|
||||
# mode is set by an escape sequence.
|
||||
|
||||
key Up -Shift+Ansi+AppCuKeys : "\EOA"
|
||||
key Down -Shift+Ansi+AppCuKeys : "\EOB"
|
||||
key Right-Shift+Ansi+AppCuKeys : "\EOC"
|
||||
key Left -Shift+Ansi+AppCuKeys : "\EOD"
|
||||
|
||||
key Up -Shift+Ansi-AppCuKeys : "\E[A"
|
||||
key Down -Shift+Ansi-AppCuKeys : "\E[B"
|
||||
key Right-Shift+Ansi-AppCuKeys : "\E[C"
|
||||
key Left -Shift+Ansi-AppCuKeys : "\E[D"
|
||||
|
||||
# linux functions keys F1-F5 differ from xterm
|
||||
|
||||
key F1 : "\E[[A"
|
||||
key F2 : "\E[[B"
|
||||
key F3 : "\E[[C"
|
||||
key F4 : "\E[[D"
|
||||
key F5 : "\E[[E"
|
||||
|
||||
key F6 : "\E[17~"
|
||||
key F7 : "\E[18~"
|
||||
key F8 : "\E[19~"
|
||||
key F9 : "\E[20~"
|
||||
key F10 : "\E[21~"
|
||||
key F11 : "\E[23~"
|
||||
key F12 : "\E[24~"
|
||||
|
||||
key Home : "\E[1~"
|
||||
key End : "\E[4~"
|
||||
|
||||
key Prior -Shift : "\E[5~"
|
||||
key Next -Shift : "\E[6~"
|
||||
key Insert-Shift : "\E[2~"
|
||||
|
||||
# Keypad-Enter. See comment on Return above.
|
||||
|
||||
key Enter+NewLine : "\r\n"
|
||||
key Enter-NewLine : "\r"
|
||||
|
||||
key Space +Control : "\x00"
|
||||
|
||||
# some of keys are used by konsole.
|
||||
|
||||
key Up +Shift : scrollLineUp
|
||||
key Prior +Shift : scrollPageUp
|
||||
key Down +Shift : scrollLineDown
|
||||
key Next +Shift : scrollPageDown
|
||||
|
||||
key ScrollLock : scrollLock
|
||||
|
||||
#----------------------------------------------------------
|
||||
|
||||
# keypad characters as offered by Qt
|
||||
# cannot be recognized as such.
|
||||
|
||||
#----------------------------------------------------------
|
||||
|
||||
# Following other strings as emitted by konsole.
|
|
@ -0,0 +1,163 @@
|
|||
# [vt420pc.keytab] Konsole Keyboard Table (VT420pc keys)
|
||||
# adapted by ferdinand gassauer f.gassauer@aon.at
|
||||
# Nov 2000
|
||||
#
|
||||
################################################################
|
||||
#
|
||||
# The escape sequences emmited by the
|
||||
# keys Shift+F1 to Shift+F12 might not fit your needs
|
||||
#
|
||||
################# IMPORTANT NOTICE #############################
|
||||
# the key bindings (Kcontrol -> look and feel -> keybindgs)
|
||||
# overrule the settings in this file. The key bindings might be
|
||||
# changed by the user WITHOUT notification of the maintainer of
|
||||
# the keytab file. Konsole will not work as expected by
|
||||
# the maintainer of the keytab file.
|
||||
################################################################
|
||||
#
|
||||
# --------------------------------------------------------------
|
||||
|
||||
keyboard "DEC VT420 Terminal"
|
||||
|
||||
# --------------------------------------------------------------
|
||||
#
|
||||
# This configuration table allows to customize the
|
||||
# meaning of the keys.
|
||||
#
|
||||
# The syntax is that each entry has the form :
|
||||
#
|
||||
# "key" Keyname { ("+"|"-") Modename } ":" (String|Operation)
|
||||
#
|
||||
# Keynames are those defined in <qnamespace.h> with the
|
||||
# "Qt::Key_" removed. (We'd better insert the list here)
|
||||
#
|
||||
# Mode names are :
|
||||
#
|
||||
# - Shift
|
||||
# - Alt
|
||||
# - Control
|
||||
#
|
||||
# The VT100 emulation has two modes that can affect the
|
||||
# sequences emitted by certain keys. These modes are
|
||||
# under control of the client program.
|
||||
#
|
||||
# - Newline : effects Return and Enter key.
|
||||
# - Application : effects Up and Down key.
|
||||
#
|
||||
# - Ansi : effects Up and Down key (This is for VT52, really).
|
||||
#
|
||||
# Operations are
|
||||
#
|
||||
# - scrollUpLine
|
||||
# - scrollUpPage
|
||||
# - scrollDownLine
|
||||
# - scrollDownPage
|
||||
#
|
||||
# - emitSelection
|
||||
#
|
||||
# If the key is not found here, the text of the
|
||||
# key event as provided by QT is emitted, possibly
|
||||
# preceeded by ESC if the Alt key is pressed.
|
||||
#
|
||||
# --------------------------------------------------------------
|
||||
|
||||
key Escape : "\E"
|
||||
key Tab : "\t"
|
||||
key Backtab: "\E[Z"
|
||||
|
||||
# VT100 can add an extra \n after return.
|
||||
# The NewLine mode is set by an escape sequence.
|
||||
|
||||
key Return-NewLine : "\r"
|
||||
key Return+NewLine : "\r\n"
|
||||
|
||||
# Some desperately try to save the ^H.
|
||||
# may be not everyone wants this
|
||||
|
||||
key Backspace : "\x08" # Control H
|
||||
key Delete : "\x7f"
|
||||
|
||||
# These codes are for the VT420pc
|
||||
# The Ansi mode (i.e. VT100 mode) is set by
|
||||
# an escape sequence
|
||||
|
||||
key Up -Shift-Ansi : "\EA"
|
||||
key Down -Shift-Ansi : "\EB"
|
||||
key Right-Shift-Ansi : "\EC"
|
||||
key Left -Shift-Ansi : "\ED"
|
||||
|
||||
# VT100 emits a mode bit together
|
||||
# with the arrow keys.The AppCuKeys
|
||||
# mode is set by an escape sequence.
|
||||
|
||||
key Up -Shift+Ansi+AppCuKeys : "\EOA"
|
||||
key Down -Shift+Ansi+AppCuKeys : "\EOB"
|
||||
key Right-Shift+Ansi+AppCuKeys : "\EOC"
|
||||
key Left -Shift+Ansi+AppCuKeys : "\EOD"
|
||||
|
||||
key Up -Shift+Ansi-AppCuKeys : "\E[A"
|
||||
key Down -Shift+Ansi-AppCuKeys : "\E[B"
|
||||
key Right-Shift+Ansi-AppCuKeys : "\E[C"
|
||||
key Left -Shift+Ansi-AppCuKeys : "\E[D"
|
||||
|
||||
# function keys
|
||||
|
||||
key F1 -Shift : "\E[11~"
|
||||
key F2 -Shift : "\E[12~"
|
||||
key F3 -Shift : "\E[13~"
|
||||
key F4 -Shift : "\E[14~"
|
||||
key F5 -Shift : "\E[15~"
|
||||
key F6 -Shift : "\E[17~"
|
||||
key F7 -Shift : "\E[18~"
|
||||
key F8 -Shift : "\E[19~"
|
||||
key F9 -Shift : "\E[20~"
|
||||
key F10-Shift : "\E[21~"
|
||||
key F11-Shift : "\E[23~"
|
||||
key F12-Shift : "\E[24~"
|
||||
#
|
||||
# Shift F1-F12
|
||||
#
|
||||
key F1 +Shift : "\E[11;2~"
|
||||
key F2 +Shift : "\E[12;2~"
|
||||
key F3 +Shift : "\E[13;2~"
|
||||
key F4 +Shift : "\E[14;2~"
|
||||
key F5 +Shift : "\E[15;2~"
|
||||
key F6 +Shift : "\E[17;2~"
|
||||
key F7 +Shift : "\E[18;2~"
|
||||
key F8 +Shift : "\E[19;2~"
|
||||
key F9 +Shift : "\E[20;2~"
|
||||
key F10+Shift : "\E[21;2~"
|
||||
key F11+Shift : "\E[23;2~"
|
||||
key F12+Shift : "\E[24;2~"
|
||||
|
||||
key Home : "\E[H"
|
||||
key End : "\E[F"
|
||||
|
||||
key Prior -Shift : "\E[5~"
|
||||
key Next -Shift : "\E[6~"
|
||||
key Insert-Shift : "\E[2~"
|
||||
|
||||
# Keypad-Enter. See comment on Return above.
|
||||
|
||||
key Enter+NewLine : "\r\n"
|
||||
key Enter-NewLine : "\r"
|
||||
|
||||
key Space +Control : "\x00"
|
||||
|
||||
# some of keys are used by konsole.
|
||||
|
||||
key Up +Shift : scrollLineUp
|
||||
key Prior +Shift : scrollPageUp
|
||||
key Down +Shift : scrollLineDown
|
||||
key Next +Shift : scrollPageDown
|
||||
|
||||
key ScrollLock : scrollLock
|
||||
|
||||
#----------------------------------------------------------
|
||||
|
||||
# keypad characters as offered by Qt
|
||||
# cannot be recognized as such.
|
||||
|
||||
#----------------------------------------------------------
|
||||
|
||||
# Following other strings as emitted by konsole.
|
|
@ -0,0 +1,216 @@
|
|||
/* $XFree86: xc/programs/xterm/wcwidth.character,v 1.3 2001/07/29 22:08:16 tsi Exp $ */
|
||||
/*
|
||||
* This is an implementation of wcwidth() and wcswidth() as defined in
|
||||
* "The Single UNIX Specification, Version 2, The Open Group, 1997"
|
||||
* <http://www.UNIX-systems.org/online.html>
|
||||
*
|
||||
* Markus Kuhn -- 2001-01-12 -- public domain
|
||||
*/
|
||||
|
||||
#include "konsole_wcwidth.h"
|
||||
|
||||
struct interval {
|
||||
unsigned short first;
|
||||
unsigned short last;
|
||||
};
|
||||
|
||||
/* auxiliary function for binary search in interval table */
|
||||
static int bisearch(quint16 ucs, const struct interval *table, int max) {
|
||||
int min = 0;
|
||||
int mid;
|
||||
|
||||
if (ucs < table[0].first || ucs > table[max].last)
|
||||
return 0;
|
||||
while (max >= min) {
|
||||
mid = (min + max) / 2;
|
||||
if (ucs > table[mid].last)
|
||||
min = mid + 1;
|
||||
else if (ucs < table[mid].first)
|
||||
max = mid - 1;
|
||||
else
|
||||
return 1;
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
|
||||
/* The following functions define the column width of an ISO 10646
|
||||
* character as follows:
|
||||
*
|
||||
* - The null character (U+0000) has a column width of 0.
|
||||
*
|
||||
* - Other C0/C1 control characters and DEL will lead to a return
|
||||
* value of -1.
|
||||
*
|
||||
* - Non-spacing and enclosing combining characters (general
|
||||
* category code Mn or Me in the Unicode database) have a
|
||||
* column width of 0.
|
||||
*
|
||||
* - Other format characters (general category code Cf in the Unicode
|
||||
* database) and ZERO WIDTH SPACE (U+200B) have a column width of 0.
|
||||
*
|
||||
* - Hangul Jamo medial vowels and final consonants (U+1160-U+11FF)
|
||||
* have a column width of 0.
|
||||
*
|
||||
* - Spacing characters in the East Asian Wide (W) or East Asian
|
||||
* FullWidth (F) category as defined in Unicode Technical
|
||||
* Report #11 have a column width of 2.
|
||||
*
|
||||
* - All remaining characters (including all printable
|
||||
* ISO 8859-1 and WGL4 characters, Unicode control characters,
|
||||
* etc.) have a column width of 1.
|
||||
*
|
||||
* This implementation assumes that quint16 characters are encoded
|
||||
* in ISO 10646.
|
||||
*/
|
||||
|
||||
int konsole_wcwidth(quint16 ucs)
|
||||
{
|
||||
/* sorted list of non-overlapping intervals of non-spacing characters */
|
||||
static const struct interval combining[] = {
|
||||
{ 0x0300, 0x034E }, { 0x0360, 0x0362 }, { 0x0483, 0x0486 },
|
||||
{ 0x0488, 0x0489 }, { 0x0591, 0x05A1 }, { 0x05A3, 0x05B9 },
|
||||
{ 0x05BB, 0x05BD }, { 0x05BF, 0x05BF }, { 0x05C1, 0x05C2 },
|
||||
{ 0x05C4, 0x05C4 }, { 0x064B, 0x0655 }, { 0x0670, 0x0670 },
|
||||
{ 0x06D6, 0x06E4 }, { 0x06E7, 0x06E8 }, { 0x06EA, 0x06ED },
|
||||
{ 0x070F, 0x070F }, { 0x0711, 0x0711 }, { 0x0730, 0x074A },
|
||||
{ 0x07A6, 0x07B0 }, { 0x0901, 0x0902 }, { 0x093C, 0x093C },
|
||||
{ 0x0941, 0x0948 }, { 0x094D, 0x094D }, { 0x0951, 0x0954 },
|
||||
{ 0x0962, 0x0963 }, { 0x0981, 0x0981 }, { 0x09BC, 0x09BC },
|
||||
{ 0x09C1, 0x09C4 }, { 0x09CD, 0x09CD }, { 0x09E2, 0x09E3 },
|
||||
{ 0x0A02, 0x0A02 }, { 0x0A3C, 0x0A3C }, { 0x0A41, 0x0A42 },
|
||||
{ 0x0A47, 0x0A48 }, { 0x0A4B, 0x0A4D }, { 0x0A70, 0x0A71 },
|
||||
{ 0x0A81, 0x0A82 }, { 0x0ABC, 0x0ABC }, { 0x0AC1, 0x0AC5 },
|
||||
{ 0x0AC7, 0x0AC8 }, { 0x0ACD, 0x0ACD }, { 0x0B01, 0x0B01 },
|
||||
{ 0x0B3C, 0x0B3C }, { 0x0B3F, 0x0B3F }, { 0x0B41, 0x0B43 },
|
||||
{ 0x0B4D, 0x0B4D }, { 0x0B56, 0x0B56 }, { 0x0B82, 0x0B82 },
|
||||
{ 0x0BC0, 0x0BC0 }, { 0x0BCD, 0x0BCD }, { 0x0C3E, 0x0C40 },
|
||||
{ 0x0C46, 0x0C48 }, { 0x0C4A, 0x0C4D }, { 0x0C55, 0x0C56 },
|
||||
{ 0x0CBF, 0x0CBF }, { 0x0CC6, 0x0CC6 }, { 0x0CCC, 0x0CCD },
|
||||
{ 0x0D41, 0x0D43 }, { 0x0D4D, 0x0D4D }, { 0x0DCA, 0x0DCA },
|
||||
{ 0x0DD2, 0x0DD4 }, { 0x0DD6, 0x0DD6 }, { 0x0E31, 0x0E31 },
|
||||
{ 0x0E34, 0x0E3A }, { 0x0E47, 0x0E4E }, { 0x0EB1, 0x0EB1 },
|
||||
{ 0x0EB4, 0x0EB9 }, { 0x0EBB, 0x0EBC }, { 0x0EC8, 0x0ECD },
|
||||
{ 0x0F18, 0x0F19 }, { 0x0F35, 0x0F35 }, { 0x0F37, 0x0F37 },
|
||||
{ 0x0F39, 0x0F39 }, { 0x0F71, 0x0F7E }, { 0x0F80, 0x0F84 },
|
||||
{ 0x0F86, 0x0F87 }, { 0x0F90, 0x0F97 }, { 0x0F99, 0x0FBC },
|
||||
{ 0x0FC6, 0x0FC6 }, { 0x102D, 0x1030 }, { 0x1032, 0x1032 },
|
||||
{ 0x1036, 0x1037 }, { 0x1039, 0x1039 }, { 0x1058, 0x1059 },
|
||||
{ 0x1160, 0x11FF }, { 0x17B7, 0x17BD }, { 0x17C6, 0x17C6 },
|
||||
{ 0x17C9, 0x17D3 }, { 0x180B, 0x180E }, { 0x18A9, 0x18A9 },
|
||||
{ 0x200B, 0x200F }, { 0x202A, 0x202E }, { 0x206A, 0x206F },
|
||||
{ 0x20D0, 0x20E3 }, { 0x302A, 0x302F }, { 0x3099, 0x309A },
|
||||
{ 0xFB1E, 0xFB1E }, { 0xFE20, 0xFE23 }, { 0xFEFF, 0xFEFF },
|
||||
{ 0xFFF9, 0xFFFB }
|
||||
};
|
||||
|
||||
/* test for 8-bit control characters */
|
||||
if (ucs == 0)
|
||||
return 0;
|
||||
if (ucs < 32 || (ucs >= 0x7f && ucs < 0xa0))
|
||||
return -1;
|
||||
|
||||
/* binary search in table of non-spacing characters */
|
||||
if (bisearch(ucs, combining,
|
||||
sizeof(combining) / sizeof(struct interval) - 1))
|
||||
return 0;
|
||||
|
||||
/* if we arrive here, ucs is not a combining or C0/C1 control character */
|
||||
|
||||
return 1 +
|
||||
(ucs >= 0x1100 &&
|
||||
(ucs <= 0x115f || /* Hangul Jamo init. consonants */
|
||||
(ucs >= 0x2e80 && ucs <= 0xa4cf && (ucs & ~0x0011) != 0x300a &&
|
||||
ucs != 0x303f) || /* CJK ... Yi */
|
||||
(ucs >= 0xac00 && ucs <= 0xd7a3) || /* Hangul Syllables */
|
||||
(ucs >= 0xf900 && ucs <= 0xfaff) || /* CJK Compatibility Ideographs */
|
||||
(ucs >= 0xfe30 && ucs <= 0xfe6f) || /* CJK Compatibility Forms */
|
||||
(ucs >= 0xff00 && ucs <= 0xff5f) || /* Fullwidth Forms */
|
||||
(ucs >= 0xffe0 && ucs <= 0xffe6) /* do not compare UINT16 with 0x20000 ||
|
||||
(ucs >= 0x20000 && ucs <= 0x2ffff) */));
|
||||
}
|
||||
|
||||
#if 0
|
||||
/*
|
||||
* The following function is the same as konsole_wcwidth(), except that
|
||||
* spacing characters in the East Asian Ambiguous (A) category as
|
||||
* defined in Unicode Technical Report #11 have a column width of 2.
|
||||
* This experimental variant might be useful for users of CJK legacy
|
||||
* encodings who want to migrate to UCS. It is not otherwise
|
||||
* recommended for general use.
|
||||
*/
|
||||
int konsole_wcwidth_cjk(quint16 ucs)
|
||||
{
|
||||
/* sorted list of non-overlapping intervals of East Asian Ambiguous
|
||||
* characters */
|
||||
static const struct interval ambiguous[] = {
|
||||
{ 0x00A1, 0x00A1 }, { 0x00A4, 0x00A4 }, { 0x00A7, 0x00A8 },
|
||||
{ 0x00AA, 0x00AA }, { 0x00AD, 0x00AD }, { 0x00B0, 0x00B4 },
|
||||
{ 0x00B6, 0x00BA }, { 0x00BC, 0x00BF }, { 0x00C6, 0x00C6 },
|
||||
{ 0x00D0, 0x00D0 }, { 0x00D7, 0x00D8 }, { 0x00DE, 0x00E1 },
|
||||
{ 0x00E6, 0x00E6 }, { 0x00E8, 0x00EA }, { 0x00EC, 0x00ED },
|
||||
{ 0x00F0, 0x00F0 }, { 0x00F2, 0x00F3 }, { 0x00F7, 0x00FA },
|
||||
{ 0x00FC, 0x00FC }, { 0x00FE, 0x00FE }, { 0x0101, 0x0101 },
|
||||
{ 0x0111, 0x0111 }, { 0x0113, 0x0113 }, { 0x011B, 0x011B },
|
||||
{ 0x0126, 0x0127 }, { 0x012B, 0x012B }, { 0x0131, 0x0133 },
|
||||
{ 0x0138, 0x0138 }, { 0x013F, 0x0142 }, { 0x0144, 0x0144 },
|
||||
{ 0x0148, 0x014A }, { 0x014D, 0x014D }, { 0x0152, 0x0153 },
|
||||
{ 0x0166, 0x0167 }, { 0x016B, 0x016B }, { 0x01CE, 0x01CE },
|
||||
{ 0x01D0, 0x01D0 }, { 0x01D2, 0x01D2 }, { 0x01D4, 0x01D4 },
|
||||
{ 0x01D6, 0x01D6 }, { 0x01D8, 0x01D8 }, { 0x01DA, 0x01DA },
|
||||
{ 0x01DC, 0x01DC }, { 0x0251, 0x0251 }, { 0x0261, 0x0261 },
|
||||
{ 0x02C7, 0x02C7 }, { 0x02C9, 0x02CB }, { 0x02CD, 0x02CD },
|
||||
{ 0x02D0, 0x02D0 }, { 0x02D8, 0x02DB }, { 0x02DD, 0x02DD },
|
||||
{ 0x0391, 0x03A1 }, { 0x03A3, 0x03A9 }, { 0x03B1, 0x03C1 },
|
||||
{ 0x03C3, 0x03C9 }, { 0x0401, 0x0401 }, { 0x0410, 0x044F },
|
||||
{ 0x0451, 0x0451 }, { 0x2010, 0x2010 }, { 0x2013, 0x2016 },
|
||||
{ 0x2018, 0x2019 }, { 0x201C, 0x201D }, { 0x2020, 0x2021 },
|
||||
{ 0x2025, 0x2027 }, { 0x2030, 0x2030 }, { 0x2032, 0x2033 },
|
||||
{ 0x2035, 0x2035 }, { 0x203B, 0x203B }, { 0x2074, 0x2074 },
|
||||
{ 0x207F, 0x207F }, { 0x2081, 0x2084 }, { 0x20AC, 0x20AC },
|
||||
{ 0x2103, 0x2103 }, { 0x2105, 0x2105 }, { 0x2109, 0x2109 },
|
||||
{ 0x2113, 0x2113 }, { 0x2121, 0x2122 }, { 0x2126, 0x2126 },
|
||||
{ 0x212B, 0x212B }, { 0x2154, 0x2155 }, { 0x215B, 0x215B },
|
||||
{ 0x215E, 0x215E }, { 0x2160, 0x216B }, { 0x2170, 0x2179 },
|
||||
{ 0x2190, 0x2199 }, { 0x21D2, 0x21D2 }, { 0x21D4, 0x21D4 },
|
||||
{ 0x2200, 0x2200 }, { 0x2202, 0x2203 }, { 0x2207, 0x2208 },
|
||||
{ 0x220B, 0x220B }, { 0x220F, 0x220F }, { 0x2211, 0x2211 },
|
||||
{ 0x2215, 0x2215 }, { 0x221A, 0x221A }, { 0x221D, 0x2220 },
|
||||
{ 0x2223, 0x2223 }, { 0x2225, 0x2225 }, { 0x2227, 0x222C },
|
||||
{ 0x222E, 0x222E }, { 0x2234, 0x2237 }, { 0x223C, 0x223D },
|
||||
{ 0x2248, 0x2248 }, { 0x224C, 0x224C }, { 0x2252, 0x2252 },
|
||||
{ 0x2260, 0x2261 }, { 0x2264, 0x2267 }, { 0x226A, 0x226B },
|
||||
{ 0x226E, 0x226F }, { 0x2282, 0x2283 }, { 0x2286, 0x2287 },
|
||||
{ 0x2295, 0x2295 }, { 0x2299, 0x2299 }, { 0x22A5, 0x22A5 },
|
||||
{ 0x22BF, 0x22BF }, { 0x2312, 0x2312 }, { 0x2460, 0x24BF },
|
||||
{ 0x24D0, 0x24E9 }, { 0x2500, 0x254B }, { 0x2550, 0x2574 },
|
||||
{ 0x2580, 0x258F }, { 0x2592, 0x2595 }, { 0x25A0, 0x25A1 },
|
||||
{ 0x25A3, 0x25A9 }, { 0x25B2, 0x25B3 }, { 0x25B6, 0x25B7 },
|
||||
{ 0x25BC, 0x25BD }, { 0x25C0, 0x25C1 }, { 0x25C6, 0x25C8 },
|
||||
{ 0x25CB, 0x25CB }, { 0x25CE, 0x25D1 }, { 0x25E2, 0x25E5 },
|
||||
{ 0x25EF, 0x25EF }, { 0x2605, 0x2606 }, { 0x2609, 0x2609 },
|
||||
{ 0x260E, 0x260F }, { 0x261C, 0x261C }, { 0x261E, 0x261E },
|
||||
{ 0x2640, 0x2640 }, { 0x2642, 0x2642 }, { 0x2660, 0x2661 },
|
||||
{ 0x2663, 0x2665 }, { 0x2667, 0x266A }, { 0x266C, 0x266D },
|
||||
{ 0x266F, 0x266F }, { 0x300A, 0x300B }, { 0x301A, 0x301B },
|
||||
{ 0xE000, 0xF8FF }, { 0xFFFD, 0xFFFD }
|
||||
};
|
||||
|
||||
/* binary search in table of non-spacing characters */
|
||||
if (bisearch(ucs, ambiguous,
|
||||
sizeof(ambiguous) / sizeof(struct interval) - 1))
|
||||
return 2;
|
||||
|
||||
return konsole_wcwidth(ucs);
|
||||
}
|
||||
#endif
|
||||
|
||||
// single byte char: +1, multi byte char: +2
|
||||
int string_width( const QString &txt )
|
||||
{
|
||||
int w = 0;
|
||||
for ( int i = 0; i < txt.length(); ++i )
|
||||
w += konsole_wcwidth( txt[ i ].unicode() );
|
||||
return w;
|
||||
}
|
|
@ -0,0 +1,624 @@
|
|||
/*
|
||||
|
||||
This file is part of the KDE libraries
|
||||
Copyright (C) 2002 Waldo Bastian <bastian@kde.org>
|
||||
Copyright (C) 2002-2003,2007 Oswald Buddenhagen <ossi@kde.org>
|
||||
|
||||
Rewritten for QT4 by e_k <e_k at users.sourceforge.net>, Copyright (C)2008
|
||||
|
||||
This library is free software; you can redistribute it and/or
|
||||
modify it under the terms of the GNU Library General Public
|
||||
License as published by the Free Software Foundation; either
|
||||
version 2 of the License, or (at your option) any later version.
|
||||
|
||||
This library is distributed in the hope that it will be useful,
|
||||
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
|
||||
Library General Public License for more details.
|
||||
|
||||
You should have received a copy of the GNU Library General Public License
|
||||
along with this library; see the file COPYING.LIB. If not, write to
|
||||
the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
|
||||
Boston, MA 02110-1301, USA.
|
||||
*/
|
||||
|
||||
#include "kpty_p.h"
|
||||
|
||||
#ifdef __sgi
|
||||
#define __svr4__
|
||||
#endif
|
||||
|
||||
#ifdef __osf__
|
||||
#define _OSF_SOURCE
|
||||
#include <float.h>
|
||||
#endif
|
||||
|
||||
#ifdef _AIX
|
||||
#define _ALL_SOURCE
|
||||
#endif
|
||||
|
||||
// __USE_XOPEN isn't defined by default in ICC
|
||||
// (needed for ptsname(), grantpt() and unlockpt())
|
||||
#ifdef __INTEL_COMPILER
|
||||
# ifndef __USE_XOPEN
|
||||
# define __USE_XOPEN
|
||||
# endif
|
||||
#endif
|
||||
|
||||
#include <sys/types.h>
|
||||
#include <sys/ioctl.h>
|
||||
#include <sys/time.h>
|
||||
#include <sys/resource.h>
|
||||
#include <sys/stat.h>
|
||||
#include <sys/param.h>
|
||||
|
||||
#include <errno.h>
|
||||
#include <fcntl.h>
|
||||
#include <time.h>
|
||||
#include <stdlib.h>
|
||||
#include <stdio.h>
|
||||
#include <string.h>
|
||||
#include <unistd.h>
|
||||
#include <grp.h>
|
||||
|
||||
#if defined(HAVE_PTY_H)
|
||||
# include <pty.h>
|
||||
#endif
|
||||
|
||||
#ifdef HAVE_LIBUTIL_H
|
||||
# include <libutil.h>
|
||||
#elif defined(HAVE_UTIL_H)
|
||||
# include <util.h>
|
||||
#endif
|
||||
|
||||
#ifdef HAVE_UTEMPTER
|
||||
extern "C" {
|
||||
# include <utempter.h>
|
||||
}
|
||||
#else
|
||||
# include <utmp.h>
|
||||
# ifdef HAVE_UTMPX
|
||||
# include <utmpx.h>
|
||||
# endif
|
||||
# if !defined(_PATH_UTMPX) && defined(_UTMPX_FILE)
|
||||
# define _PATH_UTMPX _UTMPX_FILE
|
||||
# endif
|
||||
# if !defined(_PATH_WTMPX) && defined(_WTMPX_FILE)
|
||||
# define _PATH_WTMPX _WTMPX_FILE
|
||||
# endif
|
||||
#endif
|
||||
|
||||
/* for HP-UX (some versions) the extern C is needed, and for other
|
||||
platforms it doesn't hurt */
|
||||
extern "C" {
|
||||
#include <termios.h>
|
||||
#if defined(HAVE_TERMIO_H)
|
||||
# include <termio.h> // struct winsize on some systems
|
||||
#endif
|
||||
}
|
||||
|
||||
#if defined (_HPUX_SOURCE)
|
||||
# define _TERMIOS_INCLUDED
|
||||
# include <bsdtty.h>
|
||||
#endif
|
||||
|
||||
#ifdef HAVE_SYS_STROPTS_H
|
||||
# include <sys/stropts.h> // Defines I_PUSH
|
||||
# define _NEW_TTY_CTRL
|
||||
#endif
|
||||
|
||||
#if defined (__FreeBSD__) || defined (__NetBSD__) || defined (__OpenBSD__) || defined (__bsdi__) || defined(__APPLE__) || defined (__DragonFly__)
|
||||
# define _tcgetattr(fd, ttmode) ioctl(fd, TIOCGETA, (char *)ttmode)
|
||||
#else
|
||||
# if defined(_HPUX_SOURCE) || defined(__Lynx__) || defined (__CYGWIN__)
|
||||
# define _tcgetattr(fd, ttmode) tcgetattr(fd, ttmode)
|
||||
# else
|
||||
# define _tcgetattr(fd, ttmode) ioctl(fd, TCGETS, (char *)ttmode)
|
||||
# endif
|
||||
#endif
|
||||
|
||||
#if defined (__FreeBSD__) || defined (__NetBSD__) || defined (__OpenBSD__) || defined (__bsdi__) || defined(__APPLE__) || defined (__DragonFly__)
|
||||
# define _tcsetattr(fd, ttmode) ioctl(fd, TIOCSETA, (char *)ttmode)
|
||||
#else
|
||||
# if defined(_HPUX_SOURCE) || defined(__CYGWIN__)
|
||||
# define _tcsetattr(fd, ttmode) tcsetattr(fd, TCSANOW, ttmode)
|
||||
# else
|
||||
# define _tcsetattr(fd, ttmode) ioctl(fd, TCSETS, (char *)ttmode)
|
||||
# endif
|
||||
#endif
|
||||
|
||||
//#include <kdebug.h>
|
||||
//#include <kstandarddirs.h> // findExe
|
||||
|
||||
#include <QtCore>
|
||||
|
||||
// not defined on HP-UX for example
|
||||
#ifndef CTRL
|
||||
# define CTRL(x) ((x) & 037)
|
||||
#endif
|
||||
|
||||
#define TTY_GROUP "tty"
|
||||
|
||||
///////////////////////
|
||||
// private functions //
|
||||
///////////////////////
|
||||
|
||||
//////////////////
|
||||
// private data //
|
||||
//////////////////
|
||||
|
||||
KPtyPrivate::KPtyPrivate() :
|
||||
masterFd(-1), slaveFd(-1)
|
||||
{
|
||||
}
|
||||
|
||||
bool KPtyPrivate::chownpty(bool)
|
||||
{
|
||||
// return !QProcess::execute(KStandardDirs::findExe("kgrantpty"),
|
||||
// QStringList() << (grant?"--grant":"--revoke") << QString::number(masterFd));
|
||||
return true;
|
||||
}
|
||||
|
||||
/////////////////////////////
|
||||
// public member functions //
|
||||
/////////////////////////////
|
||||
|
||||
KPty::KPty() :
|
||||
d_ptr(new KPtyPrivate)
|
||||
{
|
||||
d_ptr->q_ptr = this;
|
||||
}
|
||||
|
||||
KPty::KPty(KPtyPrivate *d) :
|
||||
d_ptr(d)
|
||||
{
|
||||
d_ptr->q_ptr = this;
|
||||
}
|
||||
|
||||
KPty::~KPty()
|
||||
{
|
||||
close();
|
||||
delete d_ptr;
|
||||
}
|
||||
|
||||
bool KPty::open()
|
||||
{
|
||||
Q_D(KPty);
|
||||
|
||||
if (d->masterFd >= 0)
|
||||
return true;
|
||||
|
||||
QByteArray ptyName;
|
||||
|
||||
// Find a master pty that we can open ////////////////////////////////
|
||||
|
||||
// Because not all the pty animals are created equal, they want to
|
||||
// be opened by several different methods.
|
||||
|
||||
// We try, as we know them, one by one.
|
||||
|
||||
#ifdef HAVE_OPENPTY
|
||||
|
||||
char ptsn[PATH_MAX];
|
||||
if (::openpty( &d->masterFd, &d->slaveFd, ptsn, 0, 0))
|
||||
{
|
||||
d->masterFd = -1;
|
||||
d->slaveFd = -1;
|
||||
kWarning(175) << "Can't open a pseudo teletype";
|
||||
return false;
|
||||
}
|
||||
d->ttyName = ptsn;
|
||||
|
||||
#else
|
||||
|
||||
#ifdef HAVE__GETPTY // irix
|
||||
|
||||
char *ptsn = _getpty(&d->masterFd, O_RDWR|O_NOCTTY, S_IRUSR|S_IWUSR, 0);
|
||||
if (ptsn) {
|
||||
d->ttyName = ptsn;
|
||||
goto grantedpt;
|
||||
}
|
||||
|
||||
#elif defined(HAVE_PTSNAME) || defined(TIOCGPTN)
|
||||
|
||||
#ifdef HAVE_POSIX_OPENPT
|
||||
d->masterFd = ::posix_openpt(O_RDWR|O_NOCTTY);
|
||||
#elif defined(HAVE_GETPT)
|
||||
d->masterFd = ::getpt();
|
||||
#elif defined(PTM_DEVICE)
|
||||
d->masterFd = ::open(PTM_DEVICE, O_RDWR|O_NOCTTY);
|
||||
#else
|
||||
# error No method to open a PTY master detected.
|
||||
#endif
|
||||
|
||||
if (d->masterFd >= 0)
|
||||
{
|
||||
|
||||
#ifdef HAVE_PTSNAME
|
||||
char *ptsn = ptsname(d->masterFd);
|
||||
if (ptsn) {
|
||||
d->ttyName = ptsn;
|
||||
#else
|
||||
int ptyno;
|
||||
if (!ioctl(d->masterFd, TIOCGPTN, &ptyno)) {
|
||||
d->ttyName = QByteArray("/dev/pts/") + QByteArray::number(ptyno);
|
||||
#endif
|
||||
#ifdef HAVE_GRANTPT
|
||||
if (!grantpt(d->masterFd))
|
||||
goto grantedpt;
|
||||
#else
|
||||
|
||||
goto gotpty;
|
||||
#endif
|
||||
}
|
||||
::close(d->masterFd);
|
||||
d->masterFd = -1;
|
||||
}
|
||||
#endif // HAVE_PTSNAME || TIOCGPTN
|
||||
|
||||
// Linux device names, FIXME: Trouble on other systems?
|
||||
for (const char* s3 = "pqrstuvwxyzabcde"; *s3; s3++)
|
||||
{
|
||||
for (const char* s4 = "0123456789abcdef"; *s4; s4++)
|
||||
{
|
||||
ptyName = QString().sprintf("/dev/pty%c%c", *s3, *s4).toAscii();
|
||||
d->ttyName = QString().sprintf("/dev/tty%c%c", *s3, *s4).toAscii();
|
||||
|
||||
d->masterFd = ::open(ptyName.data(), O_RDWR);
|
||||
if (d->masterFd >= 0)
|
||||
{
|
||||
#ifdef Q_OS_SOLARIS
|
||||
/* Need to check the process group of the pty.
|
||||
* If it exists, then the slave pty is in use,
|
||||
* and we need to get another one.
|
||||
*/
|
||||
int pgrp_rtn;
|
||||
if (ioctl(d->masterFd, TIOCGPGRP, &pgrp_rtn) == 0 || errno != EIO) {
|
||||
::close(d->masterFd);
|
||||
d->masterFd = -1;
|
||||
continue;
|
||||
}
|
||||
#endif /* Q_OS_SOLARIS */
|
||||
if (!access(d->ttyName.data(),R_OK|W_OK)) // checks availability based on permission bits
|
||||
{
|
||||
if (!geteuid())
|
||||
{
|
||||
struct group* p = getgrnam(TTY_GROUP);
|
||||
if (!p)
|
||||
p = getgrnam("wheel");
|
||||
gid_t gid = p ? p->gr_gid : getgid ();
|
||||
|
||||
chown(d->ttyName.data(), getuid(), gid);
|
||||
chmod(d->ttyName.data(), S_IRUSR|S_IWUSR|S_IWGRP);
|
||||
}
|
||||
goto gotpty;
|
||||
}
|
||||
::close(d->masterFd);
|
||||
d->masterFd = -1;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
qWarning() << "Can't open a pseudo teletype";
|
||||
return false;
|
||||
|
||||
gotpty:
|
||||
struct stat st;
|
||||
if (stat(d->ttyName.data(), &st)) {
|
||||
return false; // this just cannot happen ... *cough* Yeah right, I just
|
||||
// had it happen when pty #349 was allocated. I guess
|
||||
// there was some sort of leak? I only had a few open.
|
||||
}
|
||||
if (((st.st_uid != getuid()) ||
|
||||
(st.st_mode & (S_IRGRP|S_IXGRP|S_IROTH|S_IWOTH|S_IXOTH))) &&
|
||||
!d->chownpty(true))
|
||||
{
|
||||
qWarning()
|
||||
<< "chownpty failed for device " << ptyName << "::" << d->ttyName
|
||||
<< "\nThis means the communication can be eavesdropped." << endl;
|
||||
}
|
||||
|
||||
#if defined (HAVE__GETPTY) || defined (HAVE_GRANTPT)
|
||||
grantedpt:
|
||||
#endif
|
||||
|
||||
#ifdef HAVE_REVOKE
|
||||
revoke(d->ttyName.data());
|
||||
#endif
|
||||
|
||||
#ifdef HAVE_UNLOCKPT
|
||||
unlockpt(d->masterFd);
|
||||
#elif defined(TIOCSPTLCK)
|
||||
int flag = 0;
|
||||
ioctl(d->masterFd, TIOCSPTLCK, &flag);
|
||||
#endif
|
||||
|
||||
d->slaveFd = ::open(d->ttyName.data(), O_RDWR | O_NOCTTY);
|
||||
if (d->slaveFd < 0)
|
||||
{
|
||||
qWarning() << "Can't open slave pseudo teletype";
|
||||
::close(d->masterFd);
|
||||
d->masterFd = -1;
|
||||
return false;
|
||||
}
|
||||
|
||||
#if (defined(__svr4__) || defined(__sgi__))
|
||||
// Solaris
|
||||
ioctl(d->slaveFd, I_PUSH, "ptem");
|
||||
ioctl(d->slaveFd, I_PUSH, "ldterm");
|
||||
#endif
|
||||
|
||||
#endif /* HAVE_OPENPTY */
|
||||
|
||||
fcntl(d->masterFd, F_SETFD, FD_CLOEXEC);
|
||||
fcntl(d->slaveFd, F_SETFD, FD_CLOEXEC);
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
void KPty::closeSlave()
|
||||
{
|
||||
Q_D(KPty);
|
||||
|
||||
if (d->slaveFd < 0)
|
||||
return;
|
||||
::close(d->slaveFd);
|
||||
d->slaveFd = -1;
|
||||
}
|
||||
|
||||
void KPty::close()
|
||||
{
|
||||
Q_D(KPty);
|
||||
|
||||
if (d->masterFd < 0)
|
||||
return;
|
||||
closeSlave();
|
||||
// don't bother resetting unix98 pty, it will go away after closing master anyway.
|
||||
if (memcmp(d->ttyName.data(), "/dev/pts/", 9)) {
|
||||
if (!geteuid()) {
|
||||
struct stat st;
|
||||
if (!stat(d->ttyName.data(), &st)) {
|
||||
chown(d->ttyName.data(), 0, st.st_gid == getgid() ? 0 : -1);
|
||||
chmod(d->ttyName.data(), S_IRUSR|S_IWUSR|S_IRGRP|S_IWGRP|S_IROTH|S_IWOTH);
|
||||
}
|
||||
} else {
|
||||
fcntl(d->masterFd, F_SETFD, 0);
|
||||
d->chownpty(false);
|
||||
}
|
||||
}
|
||||
::close(d->masterFd);
|
||||
d->masterFd = -1;
|
||||
}
|
||||
|
||||
void KPty::setCTty()
|
||||
{
|
||||
Q_D(KPty);
|
||||
|
||||
// Setup job control //////////////////////////////////
|
||||
|
||||
// Become session leader, process group leader,
|
||||
// and get rid of the old controlling terminal.
|
||||
setsid();
|
||||
|
||||
// make our slave pty the new controlling terminal.
|
||||
#ifdef TIOCSCTTY
|
||||
ioctl(d->slaveFd, TIOCSCTTY, 0);
|
||||
#else
|
||||
// __svr4__ hack: the first tty opened after setsid() becomes controlling tty
|
||||
::close(::open(d->ttyName, O_WRONLY, 0));
|
||||
#endif
|
||||
|
||||
// make our new process group the foreground group on the pty
|
||||
int pgrp = getpid();
|
||||
#if defined(_POSIX_VERSION) || defined(__svr4__)
|
||||
tcsetpgrp(d->slaveFd, pgrp);
|
||||
#elif defined(TIOCSPGRP)
|
||||
ioctl(d->slaveFd, TIOCSPGRP, (char *)&pgrp);
|
||||
#endif
|
||||
}
|
||||
|
||||
void KPty::login(const char *user, const char *remotehost)
|
||||
{
|
||||
#ifdef HAVE_UTEMPTER
|
||||
Q_D(KPty);
|
||||
|
||||
addToUtmp(d->ttyName, remotehost, d->masterFd);
|
||||
Q_UNUSED(user);
|
||||
#else
|
||||
# ifdef HAVE_UTMPX
|
||||
struct utmpx l_struct;
|
||||
# else
|
||||
struct utmp l_struct;
|
||||
# endif
|
||||
memset(&l_struct, 0, sizeof(l_struct));
|
||||
// note: strncpy without terminators _is_ correct here. man 4 utmp
|
||||
|
||||
if (user)
|
||||
strncpy(l_struct.ut_name, user, sizeof(l_struct.ut_name));
|
||||
|
||||
if (remotehost) {
|
||||
strncpy(l_struct.ut_host, remotehost, sizeof(l_struct.ut_host));
|
||||
# ifdef HAVE_STRUCT_UTMP_UT_SYSLEN
|
||||
l_struct.ut_syslen = qMin(strlen(remotehost), sizeof(l_struct.ut_host));
|
||||
# endif
|
||||
}
|
||||
|
||||
# ifndef __GLIBC__
|
||||
Q_D(KPty);
|
||||
const char *str_ptr = d->ttyName.data();
|
||||
if (!memcmp(str_ptr, "/dev/", 5))
|
||||
str_ptr += 5;
|
||||
strncpy(l_struct.ut_line, str_ptr, sizeof(l_struct.ut_line));
|
||||
# ifdef HAVE_STRUCT_UTMP_UT_ID
|
||||
strncpy(l_struct.ut_id,
|
||||
str_ptr + strlen(str_ptr) - sizeof(l_struct.ut_id),
|
||||
sizeof(l_struct.ut_id));
|
||||
# endif
|
||||
# endif
|
||||
|
||||
# ifdef HAVE_UTMPX
|
||||
gettimeofday(&l_struct.ut_tv, 0);
|
||||
# else
|
||||
l_struct.ut_time = time(0);
|
||||
# endif
|
||||
|
||||
# ifdef HAVE_LOGIN
|
||||
# ifdef HAVE_LOGINX
|
||||
::loginx(&l_struct);
|
||||
# else
|
||||
::login(&l_struct);
|
||||
# endif
|
||||
# else
|
||||
# ifdef HAVE_STRUCT_UTMP_UT_TYPE
|
||||
l_struct.ut_type = USER_PROCESS;
|
||||
# endif
|
||||
# ifdef HAVE_STRUCT_UTMP_UT_PID
|
||||
l_struct.ut_pid = getpid();
|
||||
# ifdef HAVE_STRUCT_UTMP_UT_SESSION
|
||||
l_struct.ut_session = getsid(0);
|
||||
# endif
|
||||
# endif
|
||||
# ifdef HAVE_UTMPX
|
||||
utmpxname(_PATH_UTMPX);
|
||||
setutxent();
|
||||
pututxline(&l_struct);
|
||||
endutxent();
|
||||
updwtmpx(_PATH_WTMPX, &l_struct);
|
||||
# else
|
||||
utmpname(_PATH_UTMP);
|
||||
setutent();
|
||||
pututline(&l_struct);
|
||||
endutent();
|
||||
updwtmp(_PATH_WTMP, &l_struct);
|
||||
# endif
|
||||
# endif
|
||||
#endif
|
||||
}
|
||||
|
||||
void KPty::logout()
|
||||
{
|
||||
#ifdef HAVE_UTEMPTER
|
||||
Q_D(KPty);
|
||||
|
||||
removeLineFromUtmp(d->ttyName, d->masterFd);
|
||||
#else
|
||||
Q_D(KPty);
|
||||
|
||||
const char *str_ptr = d->ttyName.data();
|
||||
if (!memcmp(str_ptr, "/dev/", 5))
|
||||
str_ptr += 5;
|
||||
# ifdef __GLIBC__
|
||||
else {
|
||||
const char *sl_ptr = strrchr(str_ptr, '/');
|
||||
if (sl_ptr)
|
||||
str_ptr = sl_ptr + 1;
|
||||
}
|
||||
# endif
|
||||
# ifdef HAVE_LOGIN
|
||||
# ifdef HAVE_LOGINX
|
||||
::logoutx(str_ptr, 0, DEAD_PROCESS);
|
||||
# else
|
||||
::logout(str_ptr);
|
||||
# endif
|
||||
# else
|
||||
# ifdef HAVE_UTMPX
|
||||
struct utmpx l_struct, *ut;
|
||||
# else
|
||||
struct utmp l_struct, *ut;
|
||||
# endif
|
||||
memset(&l_struct, 0, sizeof(l_struct));
|
||||
|
||||
strncpy(l_struct.ut_line, str_ptr, sizeof(l_struct.ut_line));
|
||||
|
||||
# ifdef HAVE_UTMPX
|
||||
utmpxname(_PATH_UTMPX);
|
||||
setutxent();
|
||||
if ((ut = getutxline(&l_struct))) {
|
||||
# else
|
||||
utmpname(_PATH_UTMP);
|
||||
setutent();
|
||||
if ((ut = getutline(&l_struct))) {
|
||||
# endif
|
||||
memset(ut->ut_name, 0, sizeof(*ut->ut_name));
|
||||
memset(ut->ut_host, 0, sizeof(*ut->ut_host));
|
||||
# ifdef HAVE_STRUCT_UTMP_UT_SYSLEN
|
||||
ut->ut_syslen = 0;
|
||||
# endif
|
||||
# ifdef HAVE_STRUCT_UTMP_UT_TYPE
|
||||
ut->ut_type = DEAD_PROCESS;
|
||||
# endif
|
||||
# ifdef HAVE_UTMPX
|
||||
gettimeofday(ut->ut_tv, 0);
|
||||
pututxline(ut);
|
||||
}
|
||||
endutxent();
|
||||
# else
|
||||
ut->ut_time = time(0);
|
||||
pututline(ut);
|
||||
}
|
||||
endutent();
|
||||
# endif
|
||||
# endif
|
||||
#endif
|
||||
}
|
||||
|
||||
// XXX Supposedly, tc[gs]etattr do not work with the master on Solaris.
|
||||
// Please verify.
|
||||
|
||||
bool KPty::tcGetAttr(struct ::termios *ttmode) const
|
||||
{
|
||||
Q_D(const KPty);
|
||||
|
||||
return _tcgetattr(d->masterFd, ttmode) == 0;
|
||||
}
|
||||
|
||||
bool KPty::tcSetAttr(struct ::termios *ttmode)
|
||||
{
|
||||
Q_D(KPty);
|
||||
|
||||
return _tcsetattr(d->masterFd, ttmode) == 0;
|
||||
}
|
||||
|
||||
bool KPty::setWinSize(int lines, int columns)
|
||||
{
|
||||
Q_D(KPty);
|
||||
|
||||
struct winsize winSize;
|
||||
memset(&winSize, 0, sizeof(winSize));
|
||||
winSize.ws_row = (unsigned short)lines;
|
||||
winSize.ws_col = (unsigned short)columns;
|
||||
return ioctl(d->masterFd, TIOCSWINSZ, (char *)&winSize) == 0;
|
||||
}
|
||||
|
||||
bool KPty::setEcho(bool echo)
|
||||
{
|
||||
struct ::termios ttmode;
|
||||
if (!tcGetAttr(&ttmode))
|
||||
return false;
|
||||
if (!echo)
|
||||
ttmode.c_lflag &= ~ECHO;
|
||||
else
|
||||
ttmode.c_lflag |= ECHO;
|
||||
return tcSetAttr(&ttmode);
|
||||
}
|
||||
|
||||
const char *KPty::ttyName() const
|
||||
{
|
||||
Q_D(const KPty);
|
||||
|
||||
return d->ttyName.data();
|
||||
}
|
||||
|
||||
int KPty::masterFd() const
|
||||
{
|
||||
Q_D(const KPty);
|
||||
|
||||
return d->masterFd;
|
||||
}
|
||||
|
||||
int KPty::slaveFd() const
|
||||
{
|
||||
Q_D(const KPty);
|
||||
|
||||
return d->slaveFd;
|
||||
}
|
|
@ -0,0 +1,224 @@
|
|||
/* Copyright (C) 2008 e_k (e_k@users.sourceforge.net)
|
||||
|
||||
This library is free software; you can redistribute it and/or
|
||||
modify it under the terms of the GNU Library General Public
|
||||
License as published by the Free Software Foundation; either
|
||||
version 2 of the License, or (at your option) any later version.
|
||||
|
||||
This library is distributed in the hope that it will be useful,
|
||||
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
|
||||
Library General Public License for more details.
|
||||
|
||||
You should have received a copy of the GNU Library General Public License
|
||||
along with this library; see the file COPYING.LIB. If not, write to
|
||||
the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
|
||||
Boston, MA 02110-1301, USA.
|
||||
*/
|
||||
|
||||
|
||||
#include "qtermwidget.h"
|
||||
#include "qtermwidget.moc"
|
||||
|
||||
#include "Session.h"
|
||||
#include "TerminalDisplay.h"
|
||||
|
||||
|
||||
using namespace Konsole;
|
||||
|
||||
void *createTermWidget(int startnow, void *parent)
|
||||
{
|
||||
return (void*) new QTermWidget(startnow, (QWidget*)parent);
|
||||
}
|
||||
|
||||
struct TermWidgetImpl
|
||||
{
|
||||
TermWidgetImpl(QWidget* parent = 0);
|
||||
|
||||
TerminalDisplay *m_terminalDisplay;
|
||||
Session *m_session;
|
||||
|
||||
Session* createSession();
|
||||
TerminalDisplay* createTerminalDisplay(Session *session, QWidget* parent);
|
||||
};
|
||||
|
||||
TermWidgetImpl::TermWidgetImpl(QWidget* parent)
|
||||
{
|
||||
this->m_session = createSession();
|
||||
this->m_terminalDisplay = createTerminalDisplay(this->m_session, parent);
|
||||
}
|
||||
|
||||
|
||||
Session *TermWidgetImpl::createSession()
|
||||
{
|
||||
Session *session = new Session();
|
||||
|
||||
session->setTitle(Session::NameRole, "QTermWidget");
|
||||
session->setProgram("/bin/bash");
|
||||
QStringList args("");
|
||||
session->setArguments(args);
|
||||
session->setAutoClose(true);
|
||||
|
||||
session->setCodec(QTextCodec::codecForName("UTF-8"));
|
||||
|
||||
session->setFlowControlEnabled(true);
|
||||
session->setHistoryType(HistoryTypeBuffer(1000));
|
||||
|
||||
session->setDarkBackground(true);
|
||||
|
||||
session->setKeyBindings("");
|
||||
return session;
|
||||
}
|
||||
|
||||
TerminalDisplay *TermWidgetImpl::createTerminalDisplay(Session *session, QWidget* parent)
|
||||
{
|
||||
// TerminalDisplay* display = new TerminalDisplay(this);
|
||||
TerminalDisplay* display = new TerminalDisplay(parent);
|
||||
|
||||
display->setBellMode(TerminalDisplay::NotifyBell);
|
||||
display->setTerminalSizeHint(true);
|
||||
display->setTripleClickMode(TerminalDisplay::SelectWholeLine);
|
||||
display->setTerminalSizeStartup(true);
|
||||
|
||||
display->setRandomSeed(session->sessionId() * 31);
|
||||
|
||||
return display;
|
||||
}
|
||||
|
||||
|
||||
QTermWidget::QTermWidget(int startnow, QWidget *parent)
|
||||
:QWidget(parent)
|
||||
{
|
||||
m_impl = new TermWidgetImpl(this);
|
||||
|
||||
init();
|
||||
|
||||
if (startnow && m_impl->m_session) {
|
||||
m_impl->m_session->run();
|
||||
}
|
||||
|
||||
this->setFocus( Qt::OtherFocusReason );
|
||||
m_impl->m_terminalDisplay->resize(this->size());
|
||||
|
||||
this->setFocusProxy(m_impl->m_terminalDisplay);
|
||||
}
|
||||
|
||||
void QTermWidget::startShellProgram()
|
||||
{
|
||||
if ( m_impl->m_session->isRunning() )
|
||||
return;
|
||||
|
||||
m_impl->m_session->run();
|
||||
}
|
||||
|
||||
void QTermWidget::init()
|
||||
{
|
||||
m_impl->m_terminalDisplay->setSize(80, 40);
|
||||
|
||||
QFont font = QApplication::font();
|
||||
font.setFamily("Monospace");
|
||||
font.setPointSize(10);
|
||||
font.setStyleHint(QFont::TypeWriter);
|
||||
setTerminalFont(font);
|
||||
setScrollBarPosition(NoScrollBar);
|
||||
|
||||
m_impl->m_session->addView(m_impl->m_terminalDisplay);
|
||||
|
||||
connect(m_impl->m_session, SIGNAL(finished()), this, SLOT(sessionFinished()));
|
||||
}
|
||||
|
||||
|
||||
QTermWidget::~QTermWidget()
|
||||
{
|
||||
emit destroyed();
|
||||
}
|
||||
|
||||
|
||||
void QTermWidget::setTerminalFont(QFont &font)
|
||||
{
|
||||
if (!m_impl->m_terminalDisplay)
|
||||
return;
|
||||
m_impl->m_terminalDisplay->setVTFont(font);
|
||||
}
|
||||
|
||||
void QTermWidget::setShellProgram(QString &progname)
|
||||
{
|
||||
if (!m_impl->m_session)
|
||||
return;
|
||||
m_impl->m_session->setProgram(progname);
|
||||
}
|
||||
|
||||
void QTermWidget::setArgs(QStringList &args)
|
||||
{
|
||||
if (!m_impl->m_session)
|
||||
return;
|
||||
m_impl->m_session->setArguments(args);
|
||||
}
|
||||
|
||||
void QTermWidget::setTextCodec(QTextCodec *codec)
|
||||
{
|
||||
if (!m_impl->m_session)
|
||||
return;
|
||||
m_impl->m_session->setCodec(codec);
|
||||
}
|
||||
|
||||
void QTermWidget::setColorScheme(int scheme)
|
||||
{
|
||||
switch(scheme) {
|
||||
case COLOR_SCHEME_WHITE_ON_BLACK:
|
||||
m_impl->m_terminalDisplay->setColorTable(whiteonblack_color_table);
|
||||
break;
|
||||
case COLOR_SCHEME_GREEN_ON_BLACK:
|
||||
m_impl->m_terminalDisplay->setColorTable(greenonblack_color_table);
|
||||
break;
|
||||
case COLOR_SCHEME_BLACK_ON_LIGHT_YELLOW:
|
||||
m_impl->m_terminalDisplay->setColorTable(blackonlightyellow_color_table);
|
||||
break;
|
||||
default: //do nothing
|
||||
break;
|
||||
};
|
||||
}
|
||||
|
||||
void QTermWidget::setSize(int h, int v)
|
||||
{
|
||||
if (!m_impl->m_terminalDisplay)
|
||||
return;
|
||||
m_impl->m_terminalDisplay->setSize(h, v);
|
||||
}
|
||||
|
||||
void QTermWidget::setHistorySize(int lines)
|
||||
{
|
||||
if (lines < 0)
|
||||
m_impl->m_session->setHistoryType(HistoryTypeFile());
|
||||
else
|
||||
m_impl->m_session->setHistoryType(HistoryTypeBuffer(lines));
|
||||
}
|
||||
|
||||
void QTermWidget::setScrollBarPosition(ScrollBarPosition pos)
|
||||
{
|
||||
if (!m_impl->m_terminalDisplay)
|
||||
return;
|
||||
m_impl->m_terminalDisplay->setScrollBarPosition((TerminalDisplay::ScrollBarPosition)pos);
|
||||
}
|
||||
|
||||
void QTermWidget::sendText(QString &text)
|
||||
{
|
||||
m_impl->m_session->sendText(text);
|
||||
}
|
||||
|
||||
void QTermWidget::resizeEvent(QResizeEvent*)
|
||||
{
|
||||
//qDebug("global window resizing...with %d %d", this->size().width(), this->size().height());
|
||||
m_impl->m_terminalDisplay->resize(this->size());
|
||||
}
|
||||
|
||||
|
||||
|
||||
void QTermWidget::sessionFinished()
|
||||
{
|
||||
emit finished();
|
||||
}
|
||||
|
||||
|
||||
//#include "moc_consoleq.cpp"
|
||||
|
Loading…
Reference in New Issue