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
23
#include "Emulation.h"
32
#include <QGuiApplication>
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>
46
#include "KeyboardTranslator.h"
48
#include "TerminalCharacterDecoder.h"
49
#include "ScreenWindow.h"
51
Emulation::Emulation() :
58
// create screens with a default size
59
_screen[0] = new Screen(40,80);
60
_screen[1] = new Screen(40,80);
61
_currentScreen = _screen[0];
63
QObject::connect(&_bulkTimer1, SIGNAL(timeout()), this, SLOT(showBulk()) );
64
QObject::connect(&_bulkTimer2, SIGNAL(timeout()), this, SLOT(showBulk()) );
66
// listen for mouse status changes
67
connect( this , SIGNAL(programUsesMouseChanged(bool)) ,
68
SLOT(usesMouseChanged(bool)) );
71
bool Emulation::programUsesMouse() const
76
void Emulation::usesMouseChanged(bool usesMouse)
78
_usesMouse = usesMouse;
81
ScreenWindow* Emulation::createWindow()
83
ScreenWindow* window = new ScreenWindow();
84
window->setScreen(_currentScreen);
87
connect(window , SIGNAL(selectionChanged()),
88
this , SLOT(bufferedUpdate()));
90
connect(this , SIGNAL(outputChanged()),
91
window , SLOT(notifyOutputChanged()) );
95
Emulation::~Emulation()
97
QListIterator<ScreenWindow*> windowIter(_windows);
99
while (windowIter.hasNext())
101
delete windowIter.next();
109
void Emulation::setScreen(int n)
111
Screen *old = _currentScreen;
112
_currentScreen = _screen[n & 1];
113
if (_currentScreen != old)
115
// tell all windows onto this emulation to switch to the newly active screen
116
foreach(ScreenWindow* window,_windows)
117
window->setScreen(_currentScreen);
121
void Emulation::clearHistory()
123
_screen[0]->setScroll( _screen[0]->getScroll() , false );
125
void Emulation::setHistory(const HistoryType& t)
127
_screen[0]->setScroll(t);
132
const HistoryType& Emulation::history() const
134
return _screen[0]->getScroll();
137
void Emulation::setCodec(const QTextCodec * qtc)
142
setCodec(LocaleCodec);
145
_decoder = _codec->makeDecoder();
147
emit useUtf8Request(utf8());
150
void Emulation::setCodec(EmulationCodec codec)
152
if ( codec == Utf8Codec )
153
setCodec( QTextCodec::codecForName("utf8") );
154
else if ( codec == LocaleCodec )
155
setCodec( QTextCodec::codecForLocale() );
158
void Emulation::setKeyBindings(const QString& name)
160
_keyTranslator = KeyboardTranslatorManager::instance()->findTranslator(name);
163
_keyTranslator = KeyboardTranslatorManager::instance()->defaultTranslator();
167
QString Emulation::keyBindings() const
169
return _keyTranslator->name();
172
void Emulation::receiveChar(int c)
173
// process application unicode input to terminal
174
// this is a trivial scanner
179
case '\b' : _currentScreen->backspace(); break;
180
case '\t' : _currentScreen->tab(); break;
181
case '\n' : _currentScreen->newLine(); break;
182
case '\r' : _currentScreen->toStartOfLine(); break;
183
case 0x07 : emit stateSet(NOTIFYBELL);
185
default : _currentScreen->displayCharacter(c); break;
189
void Emulation::sendKeyEvent( QKeyEvent* ev )
191
emit stateSet(NOTIFYNORMAL);
193
if (!ev->text().isEmpty())
195
// Note that the text is proper unicode.
196
// We should do a conversion here
197
emit sendData(ev->text().toUtf8(),ev->text().length());
201
void Emulation::sendString(const char*,int)
203
// default implementation does nothing
206
void Emulation::sendMouseEvent(int /*buttons*/, int /*column*/, int /*row*/, int /*eventType*/)
208
// default implementation does nothing
212
We are doing code conversion from locale to unicode first.
213
TODO: Character composition from the old code. See #96536
216
void Emulation::receiveData(const char* text, int length)
218
emit stateSet(NOTIFYACTIVITY);
222
QString unicodeText = _decoder->toUnicode(text,length);
224
//send characters to terminal emulator
225
for (int i=0;i<unicodeText.length();i++)
226
receiveChar(unicodeText[i].unicode());
228
//look for z-modem indicator
229
//-- someone who understands more about z-modems that I do may be able to move
230
//this check into the above for loop?
231
for (int i=0;i<length;i++)
233
if (text[i] == '\030')
235
if ((length-i-1 > 3) && (strncmp(text+i+1, "B00", 3) == 0))
236
emit zmodemDetected();
242
//This version of onRcvBlock was commented out because
243
// a) It decoded incoming characters one-by-one, which is slow in the current version of Qt (4.2 tech preview)
244
// b) It messed up decoding of non-ASCII characters, with the result that (for example) chinese characters
245
// were not printed properly.
247
//There is something about stopping the _decoder if "we get a control code halfway a multi-byte sequence" (see below)
248
//which hasn't been ported into the newer function (above). Hopefully someone who understands this better
249
//can find an alternative way of handling the check.
252
/*void Emulation::onRcvBlock(const char *s, int len)
254
emit notifySessionState(NOTIFYACTIVITY);
257
for (int i = 0; i < len; i++)
260
QString result = _decoder->toUnicode(&s[i],1);
261
int reslen = result.length();
263
// If we get a control code halfway a multi-byte sequence
264
// we flush the _decoder and continue with the control code.
265
if ((s[i] < 32) && (s[i] > 0))
268
while(!result.length())
269
result = _decoder->toUnicode(&s[i],1);
271
result.resize(reslen);
272
result[0] = QChar(s[i]);
275
for (int j = 0; j < reslen; j++)
277
if (result[j].characterategory() == QChar::Mark_NonSpacing)
278
_currentScreen->compose(result.mid(j,1));
280
onRcvChar(result[j].unicode());
284
if ((len-i-1 > 3) && (strncmp(s+i+1, "B00", 3) == 0))
285
emit zmodemDetected();
290
void Emulation::writeToStream( TerminalCharacterDecoder* _decoder ,
294
_currentScreen->writeLinesToStream(_decoder,startLine,endLine);
297
int Emulation::lineCount() const
299
// sum number of lines currently on _screen plus number of lines in history
300
return _currentScreen->getLines() + _currentScreen->getHistLines();
303
#define BULK_TIMEOUT1 10
304
#define BULK_TIMEOUT2 40
306
void Emulation::showBulk()
311
emit outputChanged();
313
_currentScreen->resetScrolledLines();
314
_currentScreen->resetDroppedLines();
317
void Emulation::bufferedUpdate()
319
_bulkTimer1.setSingleShot(true);
320
_bulkTimer1.start(BULK_TIMEOUT1);
321
if (!_bulkTimer2.isActive())
323
_bulkTimer2.setSingleShot(true);
324
_bulkTimer2.start(BULK_TIMEOUT2);
328
char Emulation::eraseChar() const
333
void Emulation::setImageSize(int lines, int columns)
335
if ((lines < 1) || (columns < 1))
338
QSize screenSize[2] = { QSize(_screen[0]->getColumns(),
339
_screen[0]->getLines()),
340
QSize(_screen[1]->getColumns(),
341
_screen[1]->getLines()) };
342
QSize newSize(columns,lines);
344
if (newSize == screenSize[0] && newSize == screenSize[1])
347
_screen[0]->resizeImage(lines,columns);
348
_screen[1]->resizeImage(lines,columns);
350
emit imageSizeChanged(lines,columns);
355
QSize Emulation::imageSize() const
357
return QSize(_currentScreen->getColumns(), _currentScreen->getLines());
360
ushort ExtendedCharTable::extendedCharHash(ushort* unicodePoints , ushort length) const
363
for ( ushort i = 0 ; i < length ; i++ )
365
hash = 31*hash + unicodePoints[i];
369
bool ExtendedCharTable::extendedCharMatch(ushort hash , ushort* unicodePoints , ushort length) const
371
ushort* entry = extendedCharTable[hash];
373
// compare given length with stored sequence length ( given as the first ushort in the
375
if ( entry == 0 || entry[0] != length )
377
// if the lengths match, each character must be checked. the stored buffer starts at
379
for ( int i = 0 ; i < length ; i++ )
381
if ( entry[i+1] != unicodePoints[i] )
386
ushort ExtendedCharTable::createExtendedChar(ushort* unicodePoints , ushort length)
388
// look for this sequence of points in the table
389
ushort hash = extendedCharHash(unicodePoints,length);
391
// check existing entry for match
392
while ( extendedCharTable.contains(hash) )
394
if ( extendedCharMatch(hash,unicodePoints,length) )
396
// this sequence already has an entry in the table,
402
// if hash is already used by another, different sequence of unicode character
403
// points then try next hash
409
// add the new sequence to the table and
411
ushort* buffer = new ushort[length+1];
413
for ( int i = 0 ; i < length ; i++ )
414
buffer[i+1] = unicodePoints[i];
416
extendedCharTable.insert(hash,buffer);
421
ushort* ExtendedCharTable::lookupExtendedChar(ushort hash , ushort& length) const
423
// lookup index in table and if found, set the length
424
// argument and return a pointer to the character sequence
426
ushort* buffer = extendedCharTable[hash];
439
ExtendedCharTable::ExtendedCharTable()
442
ExtendedCharTable::~ExtendedCharTable()
444
// free all allocated character buffers
445
QHashIterator<ushort,ushort*> iter(extendedCharTable);
446
while ( iter.hasNext() )
449
delete[] iter.value();
454
ExtendedCharTable ExtendedCharTable::instance;
457
//#include "Emulation.moc"