/* This file is part of Konsole, an X terminal. Copyright (C) 2007 Robert Knight Copyright (C) 1997,1998 by Lars Doelle Copyright (C) 1996 by Matthias Ettrich Rewritten for QT4 by e_k , 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" // System #include #include #include #include // Qt #include #include #include #include #include #include #include #include // 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 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 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 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 iter(extendedCharTable); while ( iter.hasNext() ) { iter.next(); delete[] iter.value(); } } // global instance ExtendedCharTable ExtendedCharTable::instance; //#include "moc_Emulation.cpp"