Fix crash if key bindings specified in profile cannot be found. Improve
[personal-kdebase.git] / apps / konsole / src / Emulation.cpp
blob475780f89421da80c93d2f9ee5ea4a4a82c8fbc1
1 /*
2 Copyright 2007-2008 Robert Knight <robertknight@gmail.com>
3 Copyright 1997,1998 by Lars Doelle <lars.doelle@on-line.de>
4 Copyright 1996 by Matthias Ettrich <ettrich@kde.org>
6 This program is free software; you can redistribute it and/or modify
7 it under the terms of the GNU General Public License as published by
8 the Free Software Foundation; either version 2 of the License, or
9 (at your option) any later version.
11 This program is distributed in the hope that it will be useful,
12 but WITHOUT ANY WARRANTY; without even the implied warranty of
13 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
14 GNU General Public License for more details.
16 You should have received a copy of the GNU General Public License
17 along with this program; if not, write to the Free Software
18 Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
19 02110-1301 USA.
22 // Own
23 #include "Emulation.h"
25 // System
26 #include <assert.h>
27 #include <stdio.h>
28 #include <stdlib.h>
29 #include <unistd.h>
31 // Qt
32 #include <QtGui/QApplication>
33 #include <QtGui/QClipboard>
34 #include <QtCore/QHash>
35 #include <QtGui/QKeyEvent>
36 #include <QtCore/QRegExp>
37 #include <QtCore/QTextStream>
38 #include <QtCore/QThread>
40 #include <QtCore/QTime>
42 // KDE
43 #include <kdebug.h>
45 // Konsole
46 #include "KeyboardTranslator.h"
47 #include "Screen.h"
48 #include "TerminalCharacterDecoder.h"
49 #include "ScreenWindow.h"
51 using namespace Konsole;
53 Emulation::Emulation() :
54 _currentScreen(0),
55 _codec(0),
56 _decoder(0),
57 _keyTranslator(0),
58 _usesMouse(false)
60 // create screens with a default size
61 _screen[0] = new Screen(40,80);
62 _screen[1] = new Screen(40,80);
63 _currentScreen = _screen[0];
65 QObject::connect(&_bulkTimer1, SIGNAL(timeout()), this, SLOT(showBulk()) );
66 QObject::connect(&_bulkTimer2, SIGNAL(timeout()), this, SLOT(showBulk()) );
68 // listen for mouse status changes
69 connect( this , SIGNAL(programUsesMouseChanged(bool)) ,
70 SLOT(usesMouseChanged(bool)) );
73 bool Emulation::programUsesMouse() const
75 return _usesMouse;
78 void Emulation::usesMouseChanged(bool usesMouse)
80 _usesMouse = usesMouse;
83 ScreenWindow* Emulation::createWindow()
85 ScreenWindow* window = new ScreenWindow();
86 window->setScreen(_currentScreen);
87 _windows << window;
89 connect(window , SIGNAL(selectionChanged()),
90 this , SLOT(bufferedUpdate()));
92 connect(this , SIGNAL(outputChanged()),
93 window , SLOT(notifyOutputChanged()) );
94 return window;
97 Emulation::~Emulation()
99 QListIterator<ScreenWindow*> windowIter(_windows);
101 while (windowIter.hasNext())
103 delete windowIter.next();
106 delete _screen[0];
107 delete _screen[1];
108 delete _decoder;
111 void Emulation::setScreen(int n)
113 Screen *old = _currentScreen;
114 _currentScreen = _screen[n & 1];
115 if (_currentScreen != old)
117 // tell all windows onto this emulation to switch to the newly active screen
118 foreach(ScreenWindow* window,_windows)
119 window->setScreen(_currentScreen);
123 void Emulation::clearHistory()
125 _screen[0]->setScroll( _screen[0]->getScroll() , false );
127 void Emulation::setHistory(const HistoryType& t)
129 _screen[0]->setScroll(t);
131 showBulk();
134 const HistoryType& Emulation::history() const
136 return _screen[0]->getScroll();
139 void Emulation::setCodec(const QTextCodec * qtc)
141 if (qtc)
142 _codec = qtc;
143 else
144 setCodec(LocaleCodec);
146 delete _decoder;
147 _decoder = _codec->makeDecoder();
149 emit useUtf8Request(utf8());
152 void Emulation::setCodec(EmulationCodec codec)
154 if ( codec == Utf8Codec )
155 setCodec( QTextCodec::codecForName("utf8") );
156 else if ( codec == LocaleCodec )
157 setCodec( QTextCodec::codecForLocale() );
160 void Emulation::setKeyBindings(const QString& name)
162 _keyTranslator = KeyboardTranslatorManager::instance()->findTranslator(name);
163 if (!_keyTranslator)
165 _keyTranslator = KeyboardTranslatorManager::instance()->defaultTranslator();
169 QString Emulation::keyBindings() const
171 return _keyTranslator->name();
174 void Emulation::receiveChar(int c)
175 // process application unicode input to terminal
176 // this is a trivial scanner
178 c &= 0xff;
179 switch (c)
181 case '\b' : _currentScreen->backspace(); break;
182 case '\t' : _currentScreen->tab(); break;
183 case '\n' : _currentScreen->newLine(); break;
184 case '\r' : _currentScreen->toStartOfLine(); break;
185 case 0x07 : emit stateSet(NOTIFYBELL);
186 break;
187 default : _currentScreen->displayCharacter(c); break;
191 void Emulation::sendKeyEvent( QKeyEvent* ev )
193 emit stateSet(NOTIFYNORMAL);
195 if (!ev->text().isEmpty())
196 { // A block of text
197 // Note that the text is proper unicode.
198 // We should do a conversion here
199 emit sendData(ev->text().toUtf8(),ev->text().length());
203 void Emulation::sendString(const char*,int)
205 // default implementation does nothing
208 void Emulation::sendMouseEvent(int /*buttons*/, int /*column*/, int /*row*/, int /*eventType*/)
210 // default implementation does nothing
214 We are doing code conversion from locale to unicode first.
215 TODO: Character composition from the old code. See #96536
218 void Emulation::receiveData(const char* text, int length)
220 emit stateSet(NOTIFYACTIVITY);
222 bufferedUpdate();
224 QString unicodeText = _decoder->toUnicode(text,length);
226 //send characters to terminal emulator
227 for (int i=0;i<unicodeText.length();i++)
228 receiveChar(unicodeText[i].unicode());
230 //look for z-modem indicator
231 //-- someone who understands more about z-modems that I do may be able to move
232 //this check into the above for loop?
233 for (int i=0;i<length;i++)
235 if (text[i] == '\030')
237 if ((length-i-1 > 3) && (strncmp(text+i+1, "B00", 3) == 0))
238 emit zmodemDetected();
243 //OLDER VERSION
244 //This version of onRcvBlock was commented out because
245 // a) It decoded incoming characters one-by-one, which is slow in the current version of Qt (4.2 tech preview)
246 // b) It messed up decoding of non-ASCII characters, with the result that (for example) chinese characters
247 // were not printed properly.
249 //There is something about stopping the _decoder if "we get a control code halfway a multi-byte sequence" (see below)
250 //which hasn't been ported into the newer function (above). Hopefully someone who understands this better
251 //can find an alternative way of handling the check.
254 /*void Emulation::onRcvBlock(const char *s, int len)
256 emit notifySessionState(NOTIFYACTIVITY);
258 bufferedUpdate();
259 for (int i = 0; i < len; i++)
262 QString result = _decoder->toUnicode(&s[i],1);
263 int reslen = result.length();
265 // If we get a control code halfway a multi-byte sequence
266 // we flush the _decoder and continue with the control code.
267 if ((s[i] < 32) && (s[i] > 0))
269 // Flush _decoder
270 while(!result.length())
271 result = _decoder->toUnicode(&s[i],1);
272 reslen = 1;
273 result.resize(reslen);
274 result[0] = QChar(s[i]);
277 for (int j = 0; j < reslen; j++)
279 if (result[j].characterategory() == QChar::Mark_NonSpacing)
280 _currentScreen->compose(result.mid(j,1));
281 else
282 onRcvChar(result[j].unicode());
284 if (s[i] == '\030')
286 if ((len-i-1 > 3) && (strncmp(s+i+1, "B00", 3) == 0))
287 emit zmodemDetected();
292 void Emulation::writeToStream( TerminalCharacterDecoder* _decoder ,
293 int startLine ,
294 int endLine)
296 _currentScreen->writeLinesToStream(_decoder,startLine,endLine);
299 int Emulation::lineCount() const
301 // sum number of lines currently on _screen plus number of lines in history
302 return _currentScreen->getLines() + _currentScreen->getHistLines();
305 #define BULK_TIMEOUT1 10
306 #define BULK_TIMEOUT2 40
308 void Emulation::showBulk()
310 _bulkTimer1.stop();
311 _bulkTimer2.stop();
313 emit outputChanged();
315 _currentScreen->resetScrolledLines();
316 _currentScreen->resetDroppedLines();
319 void Emulation::bufferedUpdate()
321 _bulkTimer1.setSingleShot(true);
322 _bulkTimer1.start(BULK_TIMEOUT1);
323 if (!_bulkTimer2.isActive())
325 _bulkTimer2.setSingleShot(true);
326 _bulkTimer2.start(BULK_TIMEOUT2);
330 char Emulation::eraseChar() const
332 return '\b';
335 void Emulation::setImageSize(int lines, int columns)
337 Q_ASSERT( lines > 0 );
338 Q_ASSERT( columns > 0 );
340 QSize screenSize[2] = { QSize(_screen[0]->getColumns(),
341 _screen[0]->getLines()),
342 QSize(_screen[1]->getColumns(),
343 _screen[1]->getLines()) };
344 QSize newSize(columns,lines);
346 if (newSize == screenSize[0] && newSize == screenSize[1])
347 return;
349 _screen[0]->resizeImage(lines,columns);
350 _screen[1]->resizeImage(lines,columns);
352 emit imageSizeChanged(lines,columns);
354 bufferedUpdate();
357 QSize Emulation::imageSize() const
359 return QSize(_currentScreen->getColumns(), _currentScreen->getLines());
362 ushort ExtendedCharTable::extendedCharHash(ushort* unicodePoints , ushort length) const
364 ushort hash = 0;
365 for ( ushort i = 0 ; i < length ; i++ )
367 hash = 31*hash + unicodePoints[i];
369 return hash;
371 bool ExtendedCharTable::extendedCharMatch(ushort hash , ushort* unicodePoints , ushort length) const
373 ushort* entry = extendedCharTable[hash];
375 // compare given length with stored sequence length ( given as the first ushort in the
376 // stored buffer )
377 if ( entry == 0 || entry[0] != length )
378 return false;
379 // if the lengths match, each character must be checked. the stored buffer starts at
380 // entry[1]
381 for ( int i = 0 ; i < length ; i++ )
383 if ( entry[i+1] != unicodePoints[i] )
384 return false;
386 return true;
388 ushort ExtendedCharTable::createExtendedChar(ushort* unicodePoints , ushort length)
390 // look for this sequence of points in the table
391 ushort hash = extendedCharHash(unicodePoints,length);
393 // check existing entry for match
394 while ( extendedCharTable.contains(hash) )
396 if ( extendedCharMatch(hash,unicodePoints,length) )
398 // this sequence already has an entry in the table,
399 // return its hash
400 return hash;
402 else
404 // if hash is already used by another, different sequence of unicode character
405 // points then try next hash
406 hash++;
411 // add the new sequence to the table and
412 // return that index
413 ushort* buffer = new ushort[length+1];
414 buffer[0] = length;
415 for ( int i = 0 ; i < length ; i++ )
416 buffer[i+1] = unicodePoints[i];
418 extendedCharTable.insert(hash,buffer);
420 return hash;
423 ushort* ExtendedCharTable::lookupExtendedChar(ushort hash , ushort& length) const
425 // lookup index in table and if found, set the length
426 // argument and return a pointer to the character sequence
428 ushort* buffer = extendedCharTable[hash];
429 if ( buffer )
431 length = buffer[0];
432 return buffer+1;
434 else
436 length = 0;
437 return 0;
441 ExtendedCharTable::ExtendedCharTable()
444 ExtendedCharTable::~ExtendedCharTable()
446 // free all allocated character buffers
447 QHashIterator<ushort,ushort*> iter(extendedCharTable);
448 while ( iter.hasNext() )
450 iter.next();
451 delete[] iter.value();
455 // global instance
456 ExtendedCharTable ExtendedCharTable::instance;
459 #include "Emulation.moc"