source: ogBrowser-Git/qtermwidget/lib/Emulation.cpp @ 9004d96

jenkinsmain
Last change on this file since 9004d96 was 64efc22, checked in by Vadim Troshchinskiy <vtroshchinskiy@…>, 19 months ago

Update qtermwidget to modern version

  • Property mode set to 100644
File size: 12.7 KB
Line 
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>
5
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.
10
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.
15
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.
20*/
21
22// Own
23#include "Emulation.h"
24
25// System
26#include <cstdio>
27#include <cstdlib>
28#include <unistd.h>
29#include <string>
30
31// Qt
32#include <QApplication>
33#include <QClipboard>
34#include <QHash>
35#include <QKeyEvent>
36#include <QRegExp>
37#include <QTextStream>
38#include <QThread>
39
40#include <QTime>
41
42// KDE
43//#include <kdebug.h>
44
45// Konsole
46#include "KeyboardTranslator.h"
47#include "Screen.h"
48#include "TerminalCharacterDecoder.h"
49#include "ScreenWindow.h"
50
51using namespace Konsole;
52
53Emulation::Emulation() :
54  _currentScreen(nullptr),
55  _codec(nullptr),
56  _decoder(nullptr),
57  _keyTranslator(nullptr),
58  _usesMouse(false),
59  _bracketedPasteMode(false)
60{
61  // create screens with a default size
62  _screen[0] = new Screen(40,80);
63  _screen[1] = new Screen(40,80);
64  _currentScreen = _screen[0];
65
66  QObject::connect(&_bulkTimer1, SIGNAL(timeout()), this, SLOT(showBulk()) );
67  QObject::connect(&_bulkTimer2, SIGNAL(timeout()), this, SLOT(showBulk()) );
68
69  // listen for mouse status changes
70  connect(this , SIGNAL(programUsesMouseChanged(bool)) ,
71           SLOT(usesMouseChanged(bool)));
72  connect(this , SIGNAL(programBracketedPasteModeChanged(bool)) ,
73           SLOT(bracketedPasteModeChanged(bool)));
74
75  connect(this, &Emulation::cursorChanged, this, [this] (KeyboardCursorShape cursorShape, bool blinkingCursorEnabled) {
76    emit titleChanged( 50, QString(QLatin1String("CursorShape=%1;BlinkingCursorEnabled=%2"))
77                               .arg(static_cast<int>(cursorShape)).arg(blinkingCursorEnabled) );
78  });
79}
80
81bool Emulation::programUsesMouse() const
82{
83    return _usesMouse;
84}
85
86void Emulation::usesMouseChanged(bool usesMouse)
87{
88    _usesMouse = usesMouse;
89}
90
91bool Emulation::programBracketedPasteMode() const
92{
93    return _bracketedPasteMode;
94}
95
96void Emulation::bracketedPasteModeChanged(bool bracketedPasteMode)
97{
98    _bracketedPasteMode = bracketedPasteMode;
99}
100
101ScreenWindow* Emulation::createWindow()
102{
103    ScreenWindow* window = new ScreenWindow();
104    window->setScreen(_currentScreen);
105    _windows << window;
106
107    connect(window , SIGNAL(selectionChanged()),
108            this , SLOT(bufferedUpdate()));
109
110    connect(this , SIGNAL(outputChanged()),
111            window , SLOT(notifyOutputChanged()) );
112
113    connect(this, &Emulation::handleCommandFromKeyboard,
114            window, &ScreenWindow::handleCommandFromKeyboard);
115    connect(this, &Emulation::outputFromKeypressEvent,
116            window, &ScreenWindow::scrollToEnd);
117
118    return window;
119}
120
121Emulation::~Emulation()
122{
123  QListIterator<ScreenWindow*> windowIter(_windows);
124
125  while (windowIter.hasNext())
126  {
127    delete windowIter.next();
128  }
129
130  delete _screen[0];
131  delete _screen[1];
132  delete _decoder;
133}
134
135void Emulation::setScreen(int n)
136{
137  Screen *old = _currentScreen;
138  _currentScreen = _screen[n & 1];
139  if (_currentScreen != old)
140  {
141     // tell all windows onto this emulation to switch to the newly active screen
142     for(ScreenWindow* window : qAsConst(_windows))
143         window->setScreen(_currentScreen);
144  }
145}
146
147void Emulation::clearHistory()
148{
149    _screen[0]->setScroll( _screen[0]->getScroll() , false );
150}
151void Emulation::setHistory(const HistoryType& t)
152{
153  _screen[0]->setScroll(t);
154
155  showBulk();
156}
157
158const HistoryType& Emulation::history() const
159{
160  return _screen[0]->getScroll();
161}
162
163void Emulation::setCodec(const QTextCodec * qtc)
164{
165  if (qtc)
166      _codec = qtc;
167  else
168     setCodec(LocaleCodec);
169
170  delete _decoder;
171  _decoder = _codec->makeDecoder();
172
173  emit useUtf8Request(utf8());
174}
175
176void Emulation::setCodec(EmulationCodec codec)
177{
178    if ( codec == Utf8Codec )
179        setCodec( QTextCodec::codecForName("utf8") );
180    else if ( codec == LocaleCodec )
181        setCodec( QTextCodec::codecForLocale() );
182}
183
184void Emulation::setKeyBindings(const QString& name)
185{
186  _keyTranslator = KeyboardTranslatorManager::instance()->findTranslator(name);
187  if (!_keyTranslator)
188  {
189      _keyTranslator = KeyboardTranslatorManager::instance()->defaultTranslator();
190  }
191}
192
193QString Emulation::keyBindings() const
194{
195  return _keyTranslator->name();
196}
197
198void Emulation::receiveChar(wchar_t c)
199// process application unicode input to terminal
200// this is a trivial scanner
201{
202  c &= 0xff;
203  switch (c)
204  {
205    case '\b'      : _currentScreen->backspace();                 break;
206    case '\t'      : _currentScreen->tab();                       break;
207    case '\n'      : _currentScreen->newLine();                   break;
208    case '\r'      : _currentScreen->toStartOfLine();             break;
209    case 0x07      : emit stateSet(NOTIFYBELL);
210                     break;
211    default        : _currentScreen->displayCharacter(c);         break;
212  };
213}
214
215void Emulation::sendKeyEvent(QKeyEvent* ev, bool)
216{
217  emit stateSet(NOTIFYNORMAL);
218
219  if (!ev->text().isEmpty())
220  { // A block of text
221    // Note that the text is proper unicode.
222    // We should do a conversion here
223    emit sendData(ev->text().toUtf8().constData(),ev->text().length());
224  }
225}
226
227void Emulation::sendString(const char*,int)
228{
229    // default implementation does nothing
230}
231
232void Emulation::sendMouseEvent(int /*buttons*/, int /*column*/, int /*row*/, int /*eventType*/)
233{
234    // default implementation does nothing
235}
236
237/*
238   We are doing code conversion from locale to unicode first.
239TODO: Character composition from the old code.  See #96536
240*/
241
242void Emulation::receiveData(const char* text, int length)
243{
244    emit stateSet(NOTIFYACTIVITY);
245
246    bufferedUpdate();
247
248    /* XXX: the following code involves encoding & decoding of "UTF-16
249     * surrogate pairs", which does not work with characters higher than
250     * U+10FFFF
251     * https://unicodebook.readthedocs.io/unicode_encodings.html#surrogates
252     */
253    QString utf16Text = _decoder->toUnicode(text,length);
254    std::wstring unicodeText = utf16Text.toStdWString();
255
256    //send characters to terminal emulator
257    for (size_t i=0;i<unicodeText.length();i++)
258        receiveChar(unicodeText[i]);
259
260    //look for z-modem indicator
261    //-- someone who understands more about z-modems that I do may be able to move
262    //this check into the above for loop?
263    for (int i=0;i<length;i++)
264    {
265        if (text[i] == '\030')
266        {
267            if ((length-i-1 > 3) && (strncmp(text+i+1, "B00", 3) == 0))
268                emit zmodemDetected();
269        }
270    }
271}
272
273//OLDER VERSION
274//This version of onRcvBlock was commented out because
275//    a)  It decoded incoming characters one-by-one, which is slow in the current version of Qt (4.2 tech preview)
276//    b)  It messed up decoding of non-ASCII characters, with the result that (for example) chinese characters
277//        were not printed properly.
278//
279//There is something about stopping the _decoder if "we get a control code halfway a multi-byte sequence" (see below)
280//which hasn't been ported into the newer function (above).  Hopefully someone who understands this better
281//can find an alternative way of handling the check.
282
283
284/*void Emulation::onRcvBlock(const char *s, int len)
285{
286  emit notifySessionState(NOTIFYACTIVITY);
287
288  bufferedUpdate();
289  for (int i = 0; i < len; i++)
290  {
291
292    QString result = _decoder->toUnicode(&s[i],1);
293    int reslen = result.length();
294
295    // If we get a control code halfway a multi-byte sequence
296    // we flush the _decoder and continue with the control code.
297    if ((s[i] < 32) && (s[i] > 0))
298    {
299       // Flush _decoder
300       while(!result.length())
301          result = _decoder->toUnicode(&s[i],1);
302       reslen = 1;
303       result.resize(reslen);
304       result[0] = QChar(s[i]);
305    }
306
307    for (int j = 0; j < reslen; j++)
308    {
309      if (result[j].characterategory() == QChar::Mark_NonSpacing)
310         _currentScreen->compose(result.mid(j,1));
311      else
312         onRcvChar(result[j].unicode());
313    }
314    if (s[i] == '\030')
315    {
316      if ((len-i-1 > 3) && (strncmp(s+i+1, "B00", 3) == 0))
317          emit zmodemDetected();
318    }
319  }
320}*/
321
322void Emulation::writeToStream( TerminalCharacterDecoder* _decoder ,
323                               int startLine ,
324                               int endLine)
325{
326  _currentScreen->writeLinesToStream(_decoder,startLine,endLine);
327}
328
329int Emulation::lineCount() const
330{
331    // sum number of lines currently on _screen plus number of lines in history
332    return _currentScreen->getLines() + _currentScreen->getHistLines();
333}
334
335#define BULK_TIMEOUT1 10
336#define BULK_TIMEOUT2 40
337
338void Emulation::showBulk()
339{
340    _bulkTimer1.stop();
341    _bulkTimer2.stop();
342
343    emit outputChanged();
344
345    _currentScreen->resetScrolledLines();
346    _currentScreen->resetDroppedLines();
347}
348
349void Emulation::bufferedUpdate()
350{
351   _bulkTimer1.setSingleShot(true);
352   _bulkTimer1.start(BULK_TIMEOUT1);
353   if (!_bulkTimer2.isActive())
354   {
355      _bulkTimer2.setSingleShot(true);
356      _bulkTimer2.start(BULK_TIMEOUT2);
357   }
358}
359
360char Emulation::eraseChar() const
361{
362  return '\b';
363}
364
365void Emulation::setImageSize(int lines, int columns)
366{
367  if ((lines < 1) || (columns < 1))
368    return;
369
370  QSize screenSize[2] = { QSize(_screen[0]->getColumns(),
371                                _screen[0]->getLines()),
372                          QSize(_screen[1]->getColumns(),
373                                _screen[1]->getLines()) };
374  QSize newSize(columns,lines);
375
376  if (newSize == screenSize[0] && newSize == screenSize[1])
377    return;
378
379  _screen[0]->resizeImage(lines,columns);
380  _screen[1]->resizeImage(lines,columns);
381
382  emit imageSizeChanged(lines,columns);
383
384  bufferedUpdate();
385}
386
387QSize Emulation::imageSize() const
388{
389  return {_currentScreen->getColumns(), _currentScreen->getLines()};
390}
391
392ushort ExtendedCharTable::extendedCharHash(ushort* unicodePoints , ushort length) const
393{
394    ushort hash = 0;
395    for ( ushort i = 0 ; i < length ; i++ )
396    {
397        hash = 31*hash + unicodePoints[i];
398    }
399    return hash;
400}
401bool ExtendedCharTable::extendedCharMatch(ushort hash , ushort* unicodePoints , ushort length) const
402{
403    ushort* entry = extendedCharTable[hash];
404
405    // compare given length with stored sequence length ( given as the first ushort in the
406    // stored buffer )
407    if ( entry == nullptr || entry[0] != length )
408       return false;
409    // if the lengths match, each character must be checked.  the stored buffer starts at
410    // entry[1]
411    for ( int i = 0 ; i < length ; i++ )
412    {
413        if ( entry[i+1] != unicodePoints[i] )
414           return false;
415    }
416    return true;
417}
418ushort ExtendedCharTable::createExtendedChar(ushort* unicodePoints , ushort length)
419{
420    // look for this sequence of points in the table
421    ushort hash = extendedCharHash(unicodePoints,length);
422
423    // check existing entry for match
424    while ( extendedCharTable.contains(hash) )
425    {
426        if ( extendedCharMatch(hash,unicodePoints,length) )
427        {
428            // this sequence already has an entry in the table,
429            // return its hash
430            return hash;
431        }
432        else
433        {
434            // if hash is already used by another, different sequence of unicode character
435            // points then try next hash
436            hash++;
437        }
438    }
439
440
441     // add the new sequence to the table and
442     // return that index
443    ushort* buffer = new ushort[length+1];
444    buffer[0] = length;
445    for ( int i = 0 ; i < length ; i++ )
446       buffer[i+1] = unicodePoints[i];
447
448    extendedCharTable.insert(hash,buffer);
449
450    return hash;
451}
452
453ushort* ExtendedCharTable::lookupExtendedChar(ushort hash , ushort& length) const
454{
455    // lookup index in table and if found, set the length
456    // argument and return a pointer to the character sequence
457
458    ushort* buffer = extendedCharTable[hash];
459    if ( buffer )
460    {
461        length = buffer[0];
462        return buffer+1;
463    }
464    else
465    {
466        length = 0;
467        return nullptr;
468    }
469}
470
471ExtendedCharTable::ExtendedCharTable()
472{
473}
474ExtendedCharTable::~ExtendedCharTable()
475{
476    // free all allocated character buffers
477    QHashIterator<ushort,ushort*> iter(extendedCharTable);
478    while ( iter.hasNext() )
479    {
480        iter.next();
481        delete[] iter.value();
482    }
483}
484
485// global instance
486ExtendedCharTable ExtendedCharTable::instance;
487
488
489//#include "Emulation.moc"
490
Note: See TracBrowser for help on using the repository browser.