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 <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>
46 #include "KeyboardTranslator.h"
48 #include "TerminalCharacterDecoder.h"
49 #include "ScreenWindow.h"
51 using namespace Konsole
;
53 Emulation::Emulation() :
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
78 void Emulation::usesMouseChanged(bool usesMouse
)
80 _usesMouse
= usesMouse
;
83 ScreenWindow
* Emulation::createWindow()
85 ScreenWindow
* window
= new ScreenWindow();
86 window
->setScreen(_currentScreen
);
89 connect(window
, SIGNAL(selectionChanged()),
90 this , SLOT(bufferedUpdate()));
92 connect(this , SIGNAL(outputChanged()),
93 window
, SLOT(notifyOutputChanged()) );
97 Emulation::~Emulation()
99 QListIterator
<ScreenWindow
*> windowIter(_windows
);
101 while (windowIter
.hasNext())
103 delete windowIter
.next();
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
);
134 const HistoryType
& Emulation::history() const
136 return _screen
[0]->getScroll();
139 void Emulation::setCodec(const QTextCodec
* qtc
)
144 setCodec(LocaleCodec
);
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
);
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
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
);
187 default : _currentScreen
->displayCharacter(c
); break;
191 void Emulation::sendKeyEvent( QKeyEvent
* ev
)
193 emit
stateSet(NOTIFYNORMAL
);
195 if (!ev
->text().isEmpty())
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
);
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();
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);
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))
270 while(!result.length())
271 result = _decoder->toUnicode(&s[i],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));
282 onRcvChar(result[j].unicode());
286 if ((len-i-1 > 3) && (strncmp(s+i+1, "B00", 3) == 0))
287 emit zmodemDetected();
292 void Emulation::writeToStream( TerminalCharacterDecoder
* _decoder
,
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()
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
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])
349 _screen
[0]->resizeImage(lines
,columns
);
350 _screen
[1]->resizeImage(lines
,columns
);
352 emit
imageSizeChanged(lines
,columns
);
357 QSize
Emulation::imageSize() const
359 return QSize(_currentScreen
->getColumns(), _currentScreen
->getLines());
362 ushort
ExtendedCharTable::extendedCharHash(ushort
* unicodePoints
, ushort length
) const
365 for ( ushort i
= 0 ; i
< length
; i
++ )
367 hash
= 31*hash
+ unicodePoints
[i
];
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
377 if ( entry
== 0 || entry
[0] != length
)
379 // if the lengths match, each character must be checked. the stored buffer starts at
381 for ( int i
= 0 ; i
< length
; i
++ )
383 if ( entry
[i
+1] != unicodePoints
[i
] )
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,
404 // if hash is already used by another, different sequence of unicode character
405 // points then try next hash
411 // add the new sequence to the table and
413 ushort
* buffer
= new ushort
[length
+1];
415 for ( int i
= 0 ; i
< length
; i
++ )
416 buffer
[i
+1] = unicodePoints
[i
];
418 extendedCharTable
.insert(hash
,buffer
);
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
];
441 ExtendedCharTable::ExtendedCharTable()
444 ExtendedCharTable::~ExtendedCharTable()
446 // free all allocated character buffers
447 QHashIterator
<ushort
,ushort
*> iter(extendedCharTable
);
448 while ( iter
.hasNext() )
451 delete[] iter
.value();
456 ExtendedCharTable
ExtendedCharTable::instance
;
459 #include "Emulation.moc"