comparison gui/qtermwidget/lib/Emulation.cpp @ 14240:a9992bc3c3f7 gui

GUI: Added qtermwidget snapshot as a subproject to build as a library that links with Octave GUI.
author Jacob Dawid <jacob.dawid@googlemail.com>
date Sat, 21 Jan 2012 11:26:36 +0100 (2012-01-21)
parents fd14634f9c1e
children
comparison
equal deleted inserted replaced
14239:7ecaa8a66d5a 14240:a9992bc3c3f7
1 /*
2 This file is part of Konsole, an X terminal.
3
4 Copyright (C) 2007 Robert Knight <robertknight@gmail.com>
5 Copyright (C) 1997,1998 by Lars Doelle <lars.doelle@on-line.de>
6 Copyright (C) 1996 by Matthias Ettrich <ettrich@kde.org>
7
8 Rewritten for QT4 by e_k <e_k at users.sourceforge.net>, Copyright (C)2008
9
10 This program is free software; you can redistribute it and/or modify
11 it under the terms of the GNU General Public License as published by
12 the Free Software Foundation; either version 2 of the License, or
13 (at your option) any later version.
14
15 This program is distributed in the hope that it will be useful,
16 but WITHOUT ANY WARRANTY; without even the implied warranty of
17 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
18 GNU General Public License for more details.
19
20 You should have received a copy of the GNU General Public License
21 along with this program; if not, write to the Free Software
22 Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
23 02110-1301 USA.
24 */
25
26 // Own
27 #include "Emulation.h"
28
29 // System
30 #include <assert.h>
31 #include <stdio.h>
32 #include <stdlib.h>
33 #include <unistd.h>
34
35 // Qt
36 #include <QtGui/QApplication>
37 #include <QtGui/QClipboard>
38 #include <QtCore/QHash>
39 #include <QtGui/QKeyEvent>
40 #include <QtCore/QRegExp>
41 #include <QtCore/QTextStream>
42 #include <QtCore/QThread>
43
44 #include <QtCore/QTime>
45
46 // Konsole
47 #include "KeyboardTranslator.h"
48 #include "Screen.h"
49 #include "TerminalCharacterDecoder.h"
50 #include "ScreenWindow.h"
51
52 using namespace Konsole;
53
54 /* ------------------------------------------------------------------------- */
55 /* */
56 /* Emulation */
57 /* */
58 /* ------------------------------------------------------------------------- */
59
60 //#define CNTL(c) ((c)-'@')
61
62 /*!
63 */
64
65 Emulation::Emulation() :
66 _currentScreen(0),
67 _codec(0),
68 _decoder(0),
69 _keyTranslator(0),
70 _usesMouse(false)
71 {
72
73 // create screens with a default size
74 _screen[0] = new Screen(40,80);
75 _screen[1] = new Screen(40,80);
76 _currentScreen = _screen[0];
77
78 QObject::connect(&_bulkTimer1, SIGNAL(timeout()), this, SLOT(showBulk()) );
79 QObject::connect(&_bulkTimer2, SIGNAL(timeout()), this, SLOT(showBulk()) );
80
81 // listen for mouse status changes
82 connect( this , SIGNAL(programUsesMouseChanged(bool)) ,
83 SLOT(usesMouseChanged(bool)) );
84 }
85
86 bool Emulation::programUsesMouse() const
87 {
88 return _usesMouse;
89 }
90
91 void Emulation::usesMouseChanged(bool usesMouse)
92 {
93 _usesMouse = usesMouse;
94 }
95
96 ScreenWindow* Emulation::createWindow()
97 {
98 ScreenWindow* window = new ScreenWindow();
99 window->setScreen(_currentScreen);
100 _windows << window;
101
102 connect(window , SIGNAL(selectionChanged()),
103 this , SLOT(bufferedUpdate()));
104
105 connect(this , SIGNAL(outputChanged()),
106 window , SLOT(notifyOutputChanged()) );
107 return window;
108 }
109
110 /*!
111 */
112
113 Emulation::~Emulation()
114 {
115 QListIterator<ScreenWindow*> windowIter(_windows);
116
117 while (windowIter.hasNext())
118 {
119 delete windowIter.next();
120 }
121
122 delete _screen[0];
123 delete _screen[1];
124 delete _decoder;
125 }
126
127 /*! change between primary and alternate _screen
128 */
129
130 void Emulation::setScreen(int n)
131 {
132 Screen *old = _currentScreen;
133 _currentScreen = _screen[n&1];
134 if (_currentScreen != old)
135 {
136 old->setBusySelecting(false);
137
138 // tell all windows onto this emulation to switch to the newly active _screen
139 QListIterator<ScreenWindow*> windowIter(_windows);
140 while ( windowIter.hasNext() )
141 {
142 windowIter.next()->setScreen(_currentScreen);
143 }
144 }
145 }
146
147 void Emulation::clearHistory()
148 {
149 _screen[0]->setScroll( _screen[0]->getScroll() , false );
150 }
151 void Emulation::setHistory(const HistoryType& t)
152 {
153 _screen[0]->setScroll(t);
154
155 showBulk();
156 }
157
158 const HistoryType& Emulation::history()
159 {
160 return _screen[0]->getScroll();
161 }
162
163 void Emulation::setCodec(const QTextCodec * qtc)
164 {
165 Q_ASSERT( qtc );
166
167 _codec = qtc;
168 delete _decoder;
169 _decoder = _codec->makeDecoder();
170
171 emit useUtf8Request(utf8());
172 }
173
174 void Emulation::setCodec(EmulationCodec codec)
175 {
176 if ( codec == Utf8Codec )
177 setCodec( QTextCodec::codecForName("utf8") );
178 else if ( codec == LocaleCodec )
179 setCodec( QTextCodec::codecForLocale() );
180 }
181
182 void Emulation::setKeyBindings(const QString& name)
183 {
184 _keyTranslator = KeyboardTranslatorManager::instance()->findTranslator(name);
185 }
186
187 QString Emulation::keyBindings()
188 {
189 return _keyTranslator->name();
190 }
191
192
193 // Interpreting Codes ---------------------------------------------------------
194
195 /*
196 This section deals with decoding the incoming character stream.
197 Decoding means here, that the stream is first separated into `tokens'
198 which are then mapped to a `meaning' provided as operations by the
199 `Screen' class.
200 */
201
202 /*!
203 */
204
205 void Emulation::receiveChar(int c)
206 // process application unicode input to terminal
207 // this is a trivial scanner
208 {
209 c &= 0xff;
210 switch (c)
211 {
212 case '\b' : _currentScreen->BackSpace(); break;
213 case '\t' : _currentScreen->Tabulate(); break;
214 case '\n' : _currentScreen->NewLine(); break;
215 case '\r' : _currentScreen->Return(); break;
216 case 0x07 : emit stateSet(NOTIFYBELL);
217 break;
218 default : _currentScreen->ShowCharacter(c); break;
219 };
220 }
221
222 /* ------------------------------------------------------------------------- */
223 /* */
224 /* Keyboard Handling */
225 /* */
226 /* ------------------------------------------------------------------------- */
227
228 /*!
229 */
230
231 void Emulation::sendKeyEvent( QKeyEvent* ev )
232 {
233 emit stateSet(NOTIFYNORMAL);
234
235 if (!ev->text().isEmpty())
236 { // A block of text
237 // Note that the text is proper unicode.
238 // We should do a conversion here, but since this
239 // routine will never be used, we simply emit plain ascii.
240 //emit sendBlock(ev->text().toAscii(),ev->text().length());
241 emit sendData(ev->text().toUtf8(),ev->text().length());
242 }
243 }
244
245 void Emulation::sendString(const char*,int)
246 {
247 // default implementation does nothing
248 }
249
250 void Emulation::sendMouseEvent(int /*buttons*/, int /*column*/, int /*row*/, int /*eventType*/)
251 {
252 // default implementation does nothing
253 }
254
255 // Unblocking, Byte to Unicode translation --------------------------------- --
256
257 /*
258 We are doing code conversion from locale to unicode first.
259 TODO: Character composition from the old code. See #96536
260 */
261
262 void Emulation::receiveData(const char* text, int length)
263 {
264 emit stateSet(NOTIFYACTIVITY);
265
266 bufferedUpdate();
267
268 QString unicodeText = _decoder->toUnicode(text,length);
269
270 //send characters to terminal emulator
271 for (int i=0;i<unicodeText.length();i++)
272 {
273 receiveChar(unicodeText[i].unicode());
274 }
275
276 //look for z-modem indicator
277 //-- someone who understands more about z-modems that I do may be able to move
278 //this check into the above for loop?
279 for (int i=0;i<length;i++)
280 {
281 if (text[i] == '\030')
282 {
283 if ((length-i-1 > 3) && (strncmp(text+i+1, "B00", 3) == 0))
284 emit zmodemDetected();
285 }
286 }
287 }
288
289 //OLDER VERSION
290 //This version of onRcvBlock was commented out because
291 // a) It decoded incoming characters one-by-one, which is slow in the current version of Qt (4.2 tech preview)
292 // b) It messed up decoding of non-ASCII characters, with the result that (for example) chinese characters
293 // were not printed properly.
294 //
295 //There is something about stopping the _decoder if "we get a control code halfway a multi-byte sequence" (see below)
296 //which hasn't been ported into the newer function (above). Hopefully someone who understands this better
297 //can find an alternative way of handling the check.
298
299
300 /*void Emulation::onRcvBlock(const char *s, int len)
301 {
302 emit notifySessionState(NOTIFYACTIVITY);
303
304 bufferedUpdate();
305 for (int i = 0; i < len; i++)
306 {
307
308 QString result = _decoder->toUnicode(&s[i],1);
309 int reslen = result.length();
310
311 // If we get a control code halfway a multi-byte sequence
312 // we flush the _decoder and continue with the control code.
313 if ((s[i] < 32) && (s[i] > 0))
314 {
315 // Flush _decoder
316 while(!result.length())
317 result = _decoder->toUnicode(&s[i],1);
318 reslen = 1;
319 result.resize(reslen);
320 result[0] = QChar(s[i]);
321 }
322
323 for (int j = 0; j < reslen; j++)
324 {
325 if (result[j].characterategory() == QChar::Mark_NonSpacing)
326 _currentScreen->compose(result.mid(j,1));
327 else
328 onRcvChar(result[j].unicode());
329 }
330 if (s[i] == '\030')
331 {
332 if ((len-i-1 > 3) && (strncmp(s+i+1, "B00", 3) == 0))
333 emit zmodemDetected();
334 }
335 }
336 }*/
337
338 // Selection --------------------------------------------------------------- --
339
340 #if 0
341 void Emulation::onSelectionBegin(const int x, const int y, const bool columnmode) {
342 if (!connected) return;
343 _currentScreen->setSelectionStart( x,y,columnmode);
344 showBulk();
345 }
346
347 void Emulation::onSelectionExtend(const int x, const int y) {
348 if (!connected) return;
349 _currentScreen->setSelectionEnd(x,y);
350 showBulk();
351 }
352
353 void Emulation::setSelection(const bool preserve_line_breaks) {
354 if (!connected) return;
355 QString t = _currentScreen->selectedText(preserve_line_breaks);
356 if (!t.isNull())
357 {
358 QListIterator< TerminalDisplay* > viewIter(_views);
359
360 while (viewIter.hasNext())
361 viewIter.next()->setSelection(t);
362 }
363 }
364
365 void Emulation::testIsSelected(const int x, const int y, bool &selected)
366 {
367 if (!connected) return;
368 selected=_currentScreen->isSelected(x,y);
369 }
370
371 void Emulation::clearSelection() {
372 if (!connected) return;
373 _currentScreen->clearSelection();
374 showBulk();
375 }
376
377 #endif
378
379 void Emulation::writeToStream( TerminalCharacterDecoder* _decoder ,
380 int startLine ,
381 int endLine)
382 {
383 _currentScreen->writeToStream(_decoder,startLine,endLine);
384 }
385
386 int Emulation::lineCount()
387 {
388 // sum number of lines currently on _screen plus number of lines in history
389 return _currentScreen->getLines() + _currentScreen->getHistLines();
390 }
391
392 // Refreshing -------------------------------------------------------------- --
393
394 #define BULK_TIMEOUT1 10
395 #define BULK_TIMEOUT2 40
396
397 /*!
398 */
399 void Emulation::showBulk()
400 {
401 _bulkTimer1.stop();
402 _bulkTimer2.stop();
403
404 emit outputChanged();
405
406 _currentScreen->resetScrolledLines();
407 _currentScreen->resetDroppedLines();
408 }
409
410 void Emulation::bufferedUpdate()
411 {
412 _bulkTimer1.setSingleShot(true);
413 _bulkTimer1.start(BULK_TIMEOUT1);
414 if (!_bulkTimer2.isActive())
415 {
416 _bulkTimer2.setSingleShot(true);
417 _bulkTimer2.start(BULK_TIMEOUT2);
418 }
419 }
420
421 char Emulation::getErase() const
422 {
423 return '\b';
424 }
425
426 void Emulation::setImageSize(int lines, int columns)
427 {
428 //kDebug() << "Resizing image to: " << lines << "by" << columns << QTime::currentTime().msec();
429 Q_ASSERT( lines > 0 );
430 Q_ASSERT( columns > 0 );
431
432 _screen[0]->resizeImage(lines,columns);
433 _screen[1]->resizeImage(lines,columns);
434
435 emit imageSizeChanged(lines,columns);
436
437 bufferedUpdate();
438 }
439
440 QSize Emulation::imageSize()
441 {
442 return QSize(_currentScreen->getColumns(), _currentScreen->getLines());
443 }
444
445 ushort ExtendedCharTable::extendedCharHash(ushort* unicodePoints , ushort length) const
446 {
447 ushort hash = 0;
448 for ( ushort i = 0 ; i < length ; i++ )
449 {
450 hash = 31*hash + unicodePoints[i];
451 }
452 return hash;
453 }
454 bool ExtendedCharTable::extendedCharMatch(ushort hash , ushort* unicodePoints , ushort length) const
455 {
456 ushort* entry = extendedCharTable[hash];
457
458 // compare given length with stored sequence length ( given as the first ushort in the
459 // stored buffer )
460 if ( entry == 0 || entry[0] != length )
461 return false;
462 // if the lengths match, each character must be checked. the stored buffer starts at
463 // entry[1]
464 for ( int i = 0 ; i < length ; i++ )
465 {
466 if ( entry[i+1] != unicodePoints[i] )
467 return false;
468 }
469 return true;
470 }
471 ushort ExtendedCharTable::createExtendedChar(ushort* unicodePoints , ushort length)
472 {
473 // look for this sequence of points in the table
474 ushort hash = extendedCharHash(unicodePoints,length);
475
476 // check existing entry for match
477 while ( extendedCharTable.contains(hash) )
478 {
479 if ( extendedCharMatch(hash,unicodePoints,length) )
480 {
481 // this sequence already has an entry in the table,
482 // return its hash
483 return hash;
484 }
485 else
486 {
487 // if hash is already used by another, different sequence of unicode character
488 // points then try next hash
489 hash++;
490 }
491 }
492
493
494 // add the new sequence to the table and
495 // return that index
496 ushort* buffer = new ushort[length+1];
497 buffer[0] = length;
498 for ( int i = 0 ; i < length ; i++ )
499 buffer[i+1] = unicodePoints[i];
500
501 extendedCharTable.insert(hash,buffer);
502
503 return hash;
504 }
505
506 ushort* ExtendedCharTable::lookupExtendedChar(ushort hash , ushort& length) const
507 {
508 // lookup index in table and if found, set the length
509 // argument and return a pointer to the character sequence
510
511 ushort* buffer = extendedCharTable[hash];
512 if ( buffer )
513 {
514 length = buffer[0];
515 return buffer+1;
516 }
517 else
518 {
519 length = 0;
520 return 0;
521 }
522 }
523
524 ExtendedCharTable::ExtendedCharTable()
525 {
526 }
527 ExtendedCharTable::~ExtendedCharTable()
528 {
529 // free all allocated character buffers
530 QHashIterator<ushort,ushort*> iter(extendedCharTable);
531 while ( iter.hasNext() )
532 {
533 iter.next();
534 delete[] iter.value();
535 }
536 }
537
538 // global instance
539 ExtendedCharTable ExtendedCharTable::instance;
540
541
542 //#include "moc_Emulation.cpp"
543