MauiKit Terminal

Emulation.cpp
1/*
2 SPDX-FileCopyrightText: 2007-2008 Robert Knight <robertknight@gmail.com>
3 SPDX-FileCopyrightText: 1997, 1998 Lars Doelle <lars.doelle@on-line.de>
4 SPDX-FileCopyrightText: 1996 Matthias Ettrich <ettrich@kde.org>
5
6 SPDX-License-Identifier: GPL-2.0-or-later
7
8 This program is distributed in the hope that it will be useful,
9 but WITHOUT ANY WARRANTY; without even the implied warranty of
10 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
11 GNU General Public License for more details.
12
13 You should have received a copy of the GNU General Public License
14 along with this program; if not, write to the Free Software
15 Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
16 02110-1301 USA.
17*/
18
19// Own
20#include "Emulation.h"
21
22// System
23#include <cstdio>
24#include <cstdlib>
25#include <string>
26#include <unistd.h>
27
28// Qt
29#include <QApplication>
30#include <QClipboard>
31#include <QHash>
32#include <QKeyEvent>
33#include <QRegExp>
34#include <QTextStream>
35#include <QThread>
36
37#include <QTime>
38
39// KDE
40// #include <kdebug.h>
41
42// Konsole
43#include "KeyboardTranslator.h"
44#include "Screen.h"
45#include "ScreenWindow.h"
46#include "TerminalCharacterDecoder.h"
47
48using namespace Konsole;
49
51 : _currentScreen(nullptr)
52 , _codec(nullptr)
53 , _decoder(nullptr)
54 , _keyTranslator(nullptr)
55 , _usesMouse(false)
56 , _bracketedPasteMode(false)
57{
58 // create screens with a default size
59 _primaryScreen = std::make_unique<Screen>(40, 80);
60 _alternateScreen = std::make_unique<Screen>(40, 80);
61 _currentScreen = _primaryScreen.get();
62
63 QObject::connect(&_bulkTimer1, &QTimer::timeout, this, &Emulation::showBulk);
64 QObject::connect(&_bulkTimer2, &QTimer::timeout, this, &Emulation::showBulk);
65
66 // listen for mouse status changes
67 connect(this, &Emulation::programUsesMouseChanged, &Emulation::usesMouseChanged);
68 connect(this, &Emulation::programBracketedPasteModeChanged, &Emulation::bracketedPasteModeChanged);
69
70 connect(this, &Emulation::cursorChanged, [this](KeyboardCursorShape cursorShape, bool blinkingCursorEnabled) {
72 QString(QLatin1String("CursorShape=%1;BlinkingCursorEnabled=%2")).arg(static_cast<int>(cursorShape)).arg(blinkingCursorEnabled));
73 });
74}
75
76Emulation::~Emulation() = default;
77
79{
80 return _usesMouse;
81}
82
83void Emulation::usesMouseChanged(bool usesMouse)
84{
85 _usesMouse = usesMouse;
86}
87
88bool Emulation::programBracketedPasteMode() const
89{
90 return _bracketedPasteMode;
91}
92
93void Emulation::bracketedPasteModeChanged(bool bracketedPasteMode)
94{
95 _bracketedPasteMode = bracketedPasteMode;
96}
97
99{
100 auto window = std::make_unique<ScreenWindow>();
101 window->setScreen(_currentScreen);
102
104
106
107 connect(this, &Emulation::handleCommandFromKeyboard, window.get(), &ScreenWindow::handleCommandFromKeyboard);
108 connect(this, &Emulation::outputFromKeypressEvent, window.get(), &ScreenWindow::scrollToEnd);
109
110 _windows.push_back(std::move(window));
111
112 return _windows.back().get();
113}
114
116{
117 Screen *old = _currentScreen;
118 Screen *screens[2] = {_primaryScreen.get(), _alternateScreen.get()};
119 _currentScreen = screens[n & 1];
120 if (_currentScreen != old) {
121 // tell all windows onto this emulation to switch to the newly active screen
122 for (const auto &window : std::as_const(_windows))
123 window->setScreen(_currentScreen);
124 }
125}
126
128{
129 _primaryScreen->setScroll(_primaryScreen->getScroll(), false);
130}
131void Emulation::setHistory(const HistoryType &t)
132{
133 _primaryScreen->setScroll(t);
134
135 showBulk();
136}
137
138const HistoryType &Emulation::history() const
139{
140 return _primaryScreen->getScroll();
141}
142
143void Emulation::setCodec(const QTextCodec *qtc)
144{
145 if (qtc)
146 _codec = qtc;
147 else
148 setCodec(LocaleCodec);
149
150 _decoder.reset();
151 _decoder = std::unique_ptr<QTextDecoder>(_codec->makeDecoder());
152
154}
155
156void Emulation::setCodec(EmulationCodec codec)
157{
158 if (codec == Utf8Codec)
159 setCodec(QTextCodec::codecForName("utf8"));
160 else if (codec == LocaleCodec)
161 setCodec(QTextCodec::codecForLocale());
162}
163
165{
166 _keyTranslator = KeyboardTranslatorManager::instance()->findTranslator(name);
167 if (!_keyTranslator) {
169 }
170}
171
173{
174 return _keyTranslator->name();
175}
176
178// process application unicode input to terminal
179// this is a trivial scanner
180{
181 c.unicode() &= 0xff;
182 switch (c.unicode()) {
183 case '\b':
184 _currentScreen->backspace();
185 break;
186 case '\t':
187 _currentScreen->tab();
188 break;
189 case '\n':
190 _currentScreen->newLine();
191 break;
192 case '\r':
193 _currentScreen->toStartOfLine();
194 break;
195 case 0x07:
196 Q_EMIT stateSet(NOTIFYBELL);
197 break;
198 default:
199 _currentScreen->displayCharacter(c);
200 break;
201 };
202}
203
205{
206 Q_EMIT stateSet(NOTIFYNORMAL);
207
208 if (!ev->text().isEmpty()) { // A block of text
209 // Note that the text is proper unicode.
210 // We should do a conversion here
211 Q_EMIT sendData(ev->text().toUtf8().constData(), ev->text().length());
212 }
213}
214
215void Emulation::sendString(const char *, int)
216{
217 // default implementation does nothing
218}
219
220void Emulation::sendMouseEvent(int /*buttons*/, int /*column*/, int /*row*/, int /*eventType*/)
221{
222 // default implementation does nothing
223}
224
225/*
226 We are doing code conversion from locale to unicode first.
227TODO: Character composition from the old code. See #96536
228*/
229
230void Emulation::receiveData(const char *text, int length)
231{
232 Q_EMIT stateSet(NOTIFYACTIVITY);
233
235
236 /* XXX: the following code involves encoding & decoding of "UTF-16
237 * surrogate pairs", which does not work with characters higher than
238 * U+10FFFF
239 * https://unicodebook.readthedocs.io/unicode_encodings.html#surrogates
240 */
241 QString utf16Text = _decoder->toUnicode(text, length);
242
243 // send characters to terminal emulator
244 for (auto c : utf16Text) {
245 receiveChar(c);
246 }
247
248 // look for z-modem indicator
249 //-- someone who understands more about z-modems that I do may be able to move
250 // this check into the above for loop?
251 for (int i = 0; i < length; i++) {
252 if (text[i] == '\030') {
253 if ((length - i - 1 > 3) && (strncmp(text + i + 1, "B00", 3) == 0))
255 }
256 }
257}
258
259// OLDER VERSION
260// This version of onRcvBlock was commented out because
261// a) It decoded incoming characters one-by-one, which is slow in the current version of Qt (4.2 tech preview)
262// b) It messed up decoding of non-ASCII characters, with the result that (for example) chinese characters
263// were not printed properly.
264//
265// There is something about stopping the _decoder if "we get a control code halfway a multi-byte sequence" (see below)
266// which hasn't been ported into the newer function (above). Hopefully someone who understands this better
267// can find an alternative way of handling the check.
268
269/*void Emulation::onRcvBlock(const char *s, int len)
270{
271 Q_EMIT notifySessionState(NOTIFYACTIVITY);
272
273 bufferedUpdate();
274 for (int i = 0; i < len; i++)
275 {
276
277 QString result = _decoder->toUnicode(&s[i],1);
278 int reslen = result.length();
279
280 // If we get a control code halfway a multi-byte sequence
281 // we flush the _decoder and continue with the control code.
282 if ((s[i] < 32) && (s[i] > 0))
283 {
284 // Flush _decoder
285 while(!result.length())
286 result = _decoder->toUnicode(&s[i],1);
287 reslen = 1;
288 result.resize(reslen);
289 result[0] = QChar(s[i]);
290 }
291
292 for (int j = 0; j < reslen; j++)
293 {
294 if (result[j].characterategory() == QChar::Mark_NonSpacing)
295 _currentScreen->compose(result.mid(j,1));
296 else
297 onRcvChar(result[j].unicode());
298 }
299 if (s[i] == '\030')
300 {
301 if ((len-i-1 > 3) && (strncmp(s+i+1, "B00", 3) == 0))
302 Q_EMIT zmodemDetected();
303 }
304 }
305}*/
306
307void Emulation::writeToStream(TerminalCharacterDecoder *_decoder, int startLine, int endLine)
308{
309 _currentScreen->writeLinesToStream(_decoder, startLine, endLine);
310}
311
313{
314 _currentScreen->writeLinesToStream(_decoder, 0, _currentScreen->getHistLines());
315}
316
318{
319 // sum number of lines currently on _screen plus number of lines in history
320 return _currentScreen->getLines() + _currentScreen->getHistLines();
321}
322
323#define BULK_TIMEOUT1 10
324#define BULK_TIMEOUT2 40
325
326void Emulation::showBulk()
327{
328 _bulkTimer1.stop();
329 _bulkTimer2.stop();
330
332
333 _currentScreen->resetScrolledLines();
334 _currentScreen->resetDroppedLines();
335}
336
338{
339 _bulkTimer1.setSingleShot(true);
340 _bulkTimer1.start(BULK_TIMEOUT1);
341 if (!_bulkTimer2.isActive()) {
342 _bulkTimer2.setSingleShot(true);
343 _bulkTimer2.start(BULK_TIMEOUT2);
344 }
345}
346
348{
349 return '\b';
350}
351
352void Emulation::setImageSize(int lines, int columns)
353{
354 if ((lines < 1) || (columns < 1))
355 return;
356
357 QSize screenSize[2] = {QSize(_primaryScreen->getColumns(), _primaryScreen->getLines()),
358 QSize(_alternateScreen->getColumns(), _alternateScreen->getLines())};
359 QSize newSize(columns, lines);
360
361 if (newSize == screenSize[0] && newSize == screenSize[1])
362 return;
363
364 _primaryScreen->resizeImage(lines, columns);
365 _alternateScreen->resizeImage(lines, columns);
366
367 Q_EMIT imageSizeChanged(lines, columns);
368
370}
371
373{
374 return {_currentScreen->getColumns(), _currentScreen->getLines()};
375}
376
377ushort ExtendedCharTable::extendedCharHash(ushort *unicodePoints, ushort length) const
378{
379 ushort hash = 0;
380 for (ushort i = 0; i < length; i++) {
381 hash = 31 * hash + unicodePoints[i];
382 }
383 return hash;
384}
385bool ExtendedCharTable::extendedCharMatch(ushort hash, ushort *unicodePoints, ushort length) const
386{
387 std::span entry = extendedCharTable.at(hash);
388
389 // compare given length with stored sequence length ( given as the first ushort in the
390 // stored buffer )
391 if (entry.empty() || entry[0] != length)
392 return false;
393 // if the lengths match, each character must be checked. the stored buffer starts at
394 // entry[1]
395 for (int i = 0; i < length; i++) {
396 if (entry[i + 1] != unicodePoints[i])
397 return false;
398 }
399 return true;
400}
401ushort ExtendedCharTable::createExtendedChar(ushort *unicodePoints, ushort length)
402{
403 // look for this sequence of points in the table
404 ushort hash = extendedCharHash(unicodePoints, length);
405
406 // check existing entry for match
407 while (extendedCharTable.contains(hash)) {
408 if (extendedCharMatch(hash, unicodePoints, length)) {
409 // this sequence already has an entry in the table,
410 // return its hash
411 return hash;
412 } else {
413 // if hash is already used by another, different sequence of unicode character
414 // points then try next hash
415 hash++;
416 }
417 }
418
419 // add the new sequence to the table and
420 // return that index
421 std::vector<ushort> buffer(length + 1);
422 buffer[0] = length;
423 for (int i = 0; i < length; i++)
424 buffer[i + 1] = unicodePoints[i];
425
426 extendedCharTable.insert({hash, std::move(buffer)});
427
428 return hash;
429}
430
431std::span<const ushort> ExtendedCharTable::lookupExtendedChar(ushort hash, ushort &length) const
432{
433 // lookup index in table and if found, set the length
434 // argument and return a pointer to the character sequence
435
436 std::span buffer = extendedCharTable.at(hash);
437 if (!buffer.empty()) {
438 length = buffer[0];
439 return buffer.subspan(1);
440 } else {
441 length = 0;
442 return std::span<ushort>();
443 }
444}
445
447ExtendedCharTable::~ExtendedCharTable() = default;
448
449// global instance
451
452// #include "Emulation.moc"
virtual void sendMouseEvent(int buttons, int column, int line, int eventType)
Converts information about a mouse event into an xterm-compatible escape sequence and emits the chara...
void imageSizeChanged(int lineCount, int columnCount)
Emitted when the program running in the terminal changes the screen size.
bool programUsesMouse() const
Returns true if the active terminal program wants mouse input events.
Definition Emulation.cpp:78
void programUsesMouseChanged(bool usesMouse)
This is emitted when the program running in the shell indicates whether or not it is interested in mo...
void zmodemDetected()
TODO Document me.
void receiveData(const char *buffer, int len)
Processes an incoming stream of characters.
Emulation()
Constructs a new terminal emulation.
Definition Emulation.cpp:50
KeyboardCursorShape
This enum describes the available shapes for the keyboard cursor.
Definition Emulation.h:128
void bufferedUpdate()
Schedules an update of attached views.
void stateSet(int state)
Emitted when the activity state of the emulation is set.
ScreenWindow * createWindow()
Creates a new window onto the output from this emulation.
Definition Emulation.cpp:98
int lineCount() const
Returns the total number of lines, including those stored in the history.
const HistoryType & history() const
Returns the history store used by this emulation.
virtual char eraseChar() const
TODO Document me.
void outputChanged()
Emitted when the contents of the screen image change.
const QTextCodec * codec() const
Returns the codec used to decode incoming characters.
Definition Emulation.h:200
QSize imageSize() const
Returns the size of the screen image which the emulation produces.
virtual void setImageSize(int lines, int columns)
Change the size of the emulation's image.
void setScreen(int index)
Sets the active screen.
void titleChanged(int title, const QString &newTitle)
Emitted when the program running in the terminal wishes to update the session's title.
virtual void sendKeyEvent(QKeyEvent *, bool fromPaste)
Interprets a key press event and emits the sendData() signal with the resulting character stream.
void clearHistory()
Clears the history scroll.
bool utf8() const
Convenience method.
Definition Emulation.h:212
void setKeyBindings(const QString &name)
Sets the key bindings used to key events ( received through sendKeyEvent() ) into character streams t...
void setHistory(const HistoryType &)
Sets the history store used by this emulation.
QString keyBindings() const
Returns the name of the emulation's current key bindings.
void cursorChanged(KeyboardCursorShape cursorShape, bool blinkingCursorEnabled)
Emitted when the cursor shape or its blinking state is changed via DECSCUSR sequences.
virtual void writeToStream(TerminalCharacterDecoder *decoder, int startLine, int endLine)
Copies the output history from startLine to endLine into stream, using decoder to convert the termina...
void sendData(const char *data, int len)
Emitted when a buffer of data is ready to send to the standard input of the terminal.
void useUtf8Request(bool)
Requests that the pty used by the terminal process be set to UTF 8 mode.
void setCodec(const QTextCodec *)
Sets the codec used to decode incoming characters.
virtual void sendString(const char *string, int length=-1)=0
Sends a string of characters to the foreground terminal process.
virtual void receiveChar(QChar ch)
Processes an incoming character.
A table which stores sequences of unicode characters, referenced by hash keys.
Definition Character.h:172
static ExtendedCharTable instance
The global ExtendedCharTable instance.
Definition Character.h:203
std::span< const ushort > lookupExtendedChar(ushort hash, ushort &length) const
Looks up and returns a pointer to a sequence of unicode characters which was added to the table using...
ushort createExtendedChar(ushort *unicodePoints, ushort length)
Adds a sequences of unicode characters to the table and returns a hash code which can be used later t...
ExtendedCharTable()
Constructs a new character table.
const KeyboardTranslator * defaultTranslator()
Returns the default translator for Konsole.
const KeyboardTranslator * findTranslator(const QString &name)
Returns the keyboard translator with the given name or 0 if no translator with that name exists.
static KeyboardTranslatorManager * instance()
Returns the global KeyboardTranslatorManager instance.
QString name() const
Returns the name of this keyboard translator.
Provides a window onto a section of a terminal screen.
void selectionChanged()
Emitted when the selection is changed.
void notifyOutputChanged()
Notifies the window that the contents of the associated terminal screen have changed.
An image of characters with associated attributes.
Definition Screen.h:70
void tab(int n=1)
Moves the cursor n tab-stops to the right.
Definition Screen.cpp:579
void displayCharacter(QChar c)
Displays a new character at the current cursor position.
Definition Screen.cpp:646
void writeLinesToStream(TerminalCharacterDecoder *decoder, int fromLine, int toLine) const
Copies part of the output to a stream.
Definition Screen.cpp:1237
int getHistLines() const
Return the number of lines in the history buffer.
Definition Screen.cpp:1295
int getLines() const
Return the number of lines.
Definition Screen.h:385
void resetScrolledLines()
Resets the count of the number of lines that the image has been scrolled up or down by,...
Definition Screen.cpp:732
void backspace()
Moves the cursor one column to the left and erases the character at the new cursor position.
Definition Screen.cpp:567
void toStartOfLine()
Moves the cursor to the beginning of the current line.
Definition Screen.cpp:818
void resetDroppedLines()
Resets the count of the number of lines dropped from the history.
Definition Screen.cpp:728
void newLine()
Moves the cursor down one line, if the MODE_NewLine mode flag is enabled then the cursor is returned ...
Definition Screen.cpp:629
int getColumns() const
Return the number of columns.
Definition Screen.h:390
Base class for terminal character decoders.
const char * constData() const const
char16_t & unicode()
QString text() const const
Q_EMITQ_EMIT
QMetaObject::Connection connect(const QObject *sender, PointerToMemberFunction signal, Functor functor)
bool isEmpty() const const
qsizetype length() const const
QByteArray toUtf8() const const
bool isActive() const const
void setSingleShot(bool singleShot)
void start()
void stop()
void timeout()
void setScreen(QScreen *screen)
This file is part of the KDE documentation.
Documentation copyright © 1996-2024 The KDE developers.
Generated on Mon Nov 18 2024 12:10:32 by doxygen 1.12.0 written by Dimitri van Heesch, © 1997-2006

KDE's Doxygen guidelines are available online.