Warning, file /utilities/konsole/src/Vt102Emulation.cpp was not indexed or was modified since last indexation (in which case cross-reference links may be missing, inaccurate or erroneous).

0001 /*
0002     SPDX-FileCopyrightText: 2007-2008 Robert Knight <robert.knight@gmail.com>
0003     SPDX-FileCopyrightText: 1997, 1998 Lars Doelle <lars.doelle@on-line.de>
0004 
0005     SPDX-License-Identifier: GPL-2.0-or-later
0006 */
0007 // Own
0008 #include "Vt102Emulation.h"
0009 #include "config-konsole.h"
0010 
0011 // Standard
0012 #include <cstdio>
0013 
0014 // Qt
0015 #include <QBuffer>
0016 #include <QEvent>
0017 #include <QKeyEvent>
0018 #include <QTimer>
0019 #include <QtEndian>
0020 
0021 // KDE
0022 #include <KLocalizedString>
0023 #include <KNotification>
0024 
0025 // Konsole
0026 #include "EscapeSequenceUrlExtractor.h"
0027 #include "session/SessionController.h"
0028 #include "session/SessionManager.h"
0029 #include "terminalDisplay/TerminalDisplay.h"
0030 #include "terminalDisplay/TerminalFonts.h"
0031 
0032 #include <konsoledebug.h>
0033 
0034 using Konsole::Vt102Emulation;
0035 
0036 /*
0037    The VT100 has 32 special graphical characters. The usual vt100 extended
0038    xterm fonts have these at 0x00..0x1f.
0039 
0040    QT's iso mapping leaves 0x00..0x7f without any changes. But the graphicals
0041    come in here as proper unicode characters.
0042 
0043    We treat non-iso10646 fonts as VT100 extended and do the required mapping
0044    from unicode to 0x00..0x1f. The remaining translation is then left to the
0045    QCodec.
0046 */
0047 
0048 // assert for i in [0..31] : vt100extended(vt100_graphics[i]) == i.
0049 
0050 /* clang-format off */
0051 unsigned short Konsole::vt100_graphics[32] = {
0052     // 0/8     1/9    2/10    3/11    4/12    5/13    6/14    7/15
0053     0x0020, 0x25C6, 0x2592, 0x2409, 0x240c, 0x240d, 0x240a, 0x00b0,
0054     0x00b1, 0x2424, 0x240b, 0x2518, 0x2510, 0x250c, 0x2514, 0x253c,
0055     0x23ba, 0x23bb, 0x2500, 0x23bc, 0x23bd, 0x251c, 0x2524, 0x2534,
0056     0x252c, 0x2502, 0x2264, 0x2265, 0x03C0, 0x2260, 0x00A3, 0x00b7
0057 };
0058 /* clang-format on */
0059 
0060 enum XTERM_EXTENDED {
0061     URL_LINK = '8',
0062 };
0063 
0064 Vt102Emulation::Vt102Emulation()
0065     : Emulation()
0066     , _currentModes(TerminalState())
0067     , _savedModes(TerminalState())
0068     , _pendingSessionAttributesUpdates(QHash<int, QString>())
0069     , _sessionAttributesUpdateTimer(new QTimer(this))
0070     , _reportFocusEvents(false)
0071     , player(nullptr)
0072 {
0073     _sessionAttributesUpdateTimer->setSingleShot(true);
0074     QObject::connect(_sessionAttributesUpdateTimer, &QTimer::timeout, this, &Konsole::Vt102Emulation::updateSessionAttributes);
0075 
0076     initTokenizer();
0077     imageId = 0;
0078     savedKeys = QMap<char, qint64>();
0079     tokenData = QByteArray();
0080 
0081     for (int i = 0; i < 256; i++) {
0082         colorTable[i] = QColor();
0083     }
0084 }
0085 
0086 Vt102Emulation::~Vt102Emulation()
0087 {
0088 }
0089 
0090 void Vt102Emulation::clearEntireScreen()
0091 {
0092     _currentScreen->clearEntireScreen();
0093     bufferedUpdate();
0094 }
0095 
0096 void Vt102Emulation::clearHistory()
0097 {
0098     _graphicsImages.clear();
0099 
0100     Emulation::clearHistory();
0101 }
0102 
0103 void Vt102Emulation::reset(bool softReset, bool preservePrompt)
0104 {
0105     // Save the current codec so we can set it later.
0106     // Ideally we would want to use the profile setting
0107     const QTextCodec *currentCodec = codec();
0108 
0109     resetTokenizer();
0110     if (softReset) {
0111         resetMode(MODE_AppCuKeys);
0112         saveMode(MODE_AppCuKeys);
0113         resetMode(MODE_AppKeyPad);
0114         saveMode(MODE_AppKeyPad);
0115     } else {
0116         resetModes();
0117     }
0118 
0119     resetCharset(0);
0120     _screen[0]->reset(softReset, preservePrompt);
0121     resetCharset(1);
0122     _screen[1]->reset(softReset, preservePrompt);
0123 
0124     if (currentCodec != nullptr) {
0125         setCodec(currentCodec);
0126     } else {
0127 #if defined(Q_OS_WIN)
0128         setCodec(Utf8Codec);
0129 #else
0130         setCodec(LocaleCodec);
0131 #endif
0132     }
0133 
0134     Q_EMIT resetCursorStyleRequest();
0135 
0136     bufferedUpdate();
0137 }
0138 
0139 /* ------------------------------------------------------------------------- */
0140 /*                                                                           */
0141 /*                     Processing the incoming byte stream                   */
0142 /*                                                                           */
0143 /* ------------------------------------------------------------------------- */
0144 
0145 /* Incoming Bytes Event pipeline
0146 
0147    This section deals with decoding the incoming character stream.
0148    Decoding means here, that the stream is first separated into `tokens'
0149    which are then mapped to a `meaning' provided as operations by the
0150    `Screen' class or by the emulation class itself.
0151 
0152    The pipeline proceeds as follows:
0153 
0154    - Tokenizing the ESC codes (onReceiveChar)
0155    - VT100 code page translation of plain characters (applyCharset)
0156    - Interpretation of ESC codes (processToken)
0157 
0158    The escape codes and their meaning are described in the
0159    technical reference of this program.
0160 */
0161 
0162 // Tokens ------------------------------------------------------------------ --
0163 
0164 /*
0165    Since the tokens are the central notion if this section, we've put them
0166    in front. They provide the syntactical elements used to represent the
0167    terminals operations as byte sequences.
0168 
0169    They are encodes here into a single machine word, so that we can later
0170    switch over them easily. Depending on the token itself, additional
0171    argument variables are filled with parameter values.
0172 
0173    The tokens are defined below:
0174 
0175    - CHR        - Printable characters     (32..255 but DEL (=127))
0176    - CTL        - Control characters       (0..31 but ESC (= 27), DEL)
0177    - ESC        - Escape codes of the form <ESC><CHR but `[]()+*#'>
0178    - ESC_DE     - Escape codes of the form <ESC><any of `()+*#%'> C
0179    - CSI_PN     - Escape codes of the form <ESC>'['     {Pn} ';' {Pn} C
0180    - CSI_PS     - Escape codes of the form <ESC>'['     {Pn} ';' ...  C
0181    - CSI_PR     - Escape codes of the form <ESC>'[' '?' {Pn} ';' ...  C
0182    - CSI_PE     - Escape codes of the form <ESC>'[' '!' {Pn} ';' ...  C
0183    - CSI_SP     - Escape codes of the form <ESC>'[' ' ' C
0184                   (3rd field is a space)
0185    - CSI_PSP    - Escape codes of the form <ESC>'[' '{Pn}' ' ' C
0186                   (4th field is a space)
0187    - VT52       - VT52 escape codes
0188                   - <ESC><Chr>
0189                   - <ESC>'Y'{Pc}{Pc}
0190    - XTE_HA     - Xterm window/terminal attribute commands
0191                   of the form <ESC>`]' {Pn} `;' {Text} <BEL>
0192                   (Note that these are handled differently to the other formats)
0193 
0194    The last two forms allow list of arguments. Since the elements of
0195    the lists are treated individually the same way, they are passed
0196    as individual tokens to the interpretation. Further, because the
0197    meaning of the parameters are names (although represented as numbers),
0198    they are includes within the token ('N').
0199 
0200 */
0201 constexpr int token_construct(int t, int a, int n)
0202 {
0203     return (((n & 0xffff) << 16) | ((a & 0xff) << 8) | (t & 0xff));
0204 }
0205 constexpr int token_chr()
0206 {
0207     return token_construct(0, 0, 0);
0208 }
0209 constexpr int token_ctl(int a)
0210 {
0211     return token_construct(1, a, 0);
0212 }
0213 constexpr int token_esc(int a)
0214 {
0215     return token_construct(2, a, 0);
0216 }
0217 constexpr int token_esc_cs(int a, int b)
0218 {
0219     return token_construct(3, a, b);
0220 }
0221 constexpr int token_esc_de(int a)
0222 {
0223     return token_construct(4, a, 0);
0224 }
0225 constexpr int token_csi_ps(int a, int n)
0226 {
0227     return token_construct(5, a, n);
0228 }
0229 constexpr int token_csi_pn(int a)
0230 {
0231     return token_construct(6, a, 0);
0232 }
0233 constexpr int token_csi_pr(int a, int n)
0234 {
0235     return token_construct(7, a, n);
0236 }
0237 constexpr int token_vt52(int a)
0238 {
0239     return token_construct(8, a, 0);
0240 }
0241 constexpr int token_csi_pg(int a)
0242 {
0243     return token_construct(9, a, 0);
0244 }
0245 constexpr int token_csi_pe(int a)
0246 {
0247     return token_construct(10, a, 0);
0248 }
0249 constexpr int token_csi_sp(int a)
0250 {
0251     return token_construct(11, a, 0);
0252 }
0253 constexpr int token_csi_psp(int a, int n)
0254 {
0255     return token_construct(12, a, n);
0256 }
0257 constexpr int token_csi_pq(int a)
0258 {
0259     return token_construct(13, a, 0);
0260 }
0261 constexpr int token_osc(int a)
0262 {
0263     return token_construct(14, a, 0);
0264 }
0265 constexpr int token_apc(int a)
0266 {
0267     return token_construct(15, a, 0);
0268 }
0269 
0270 const int MAX_ARGUMENT = 40960;
0271 
0272 // Tokenizer --------------------------------------------------------------- --
0273 
0274 /* The tokenizer's state
0275 
0276    The state is represented by the buffer (tokenBuffer, tokenBufferPos),
0277    and accompanied by decoded arguments kept in params.
0278    Note that they are kept internal in the tokenizer.
0279 */
0280 
0281 void Vt102Emulation::resetTokenizer()
0282 {
0283     tokenBufferPos = 0;
0284     params.count = 0;
0285     params.value[0] = 0;
0286     params.value[1] = 0;
0287     params.sub[0].value[0] = 0;
0288     params.sub[0].count = 0;
0289     params.hasSubParams = false;
0290     tokenState = -1;
0291 }
0292 
0293 void Vt102Emulation::addDigit(int digit)
0294 {
0295     if (params.sub[params.count].count == 0) {
0296         params.value[params.count] = qMin(10 * params.value[params.count] + digit, MAX_ARGUMENT);
0297     } else {
0298         struct subParam *sub = &params.sub[params.count];
0299         sub->value[sub->count] = qMin(10 * sub->value[sub->count] + digit, MAX_ARGUMENT);
0300     }
0301 }
0302 
0303 void Vt102Emulation::addArgument()
0304 {
0305     params.count = qMin(params.count + 1, MAXARGS - 1);
0306     params.value[params.count] = 0;
0307     params.sub[params.count].value[0] = 0;
0308     params.sub[params.count].count = 0;
0309 }
0310 
0311 void Vt102Emulation::addSub()
0312 {
0313     struct subParam *sub = &params.sub[params.count];
0314     sub->count = qMin(sub->count + 1, MAXARGS - 1);
0315     sub->value[sub->count] = 0;
0316     params.hasSubParams = true;
0317 }
0318 
0319 void Vt102Emulation::addToCurrentToken(uint cc)
0320 {
0321     tokenBufferPos = qMin(tokenBufferPos, MAX_TOKEN_LENGTH - 1);
0322     tokenBuffer[tokenBufferPos] = cc;
0323     tokenBufferPos++;
0324 }
0325 
0326 // Character Class flags used while decoding
0327 const int CTL = 1; // Control character
0328 const int CHR = 2; // Printable character
0329 const int CPN = 4; // TODO: Document me
0330 const int DIG = 8; // Digit
0331 const int SCS = 16; // Select Character Set
0332 const int GRP = 32; // TODO: Document me
0333 const int CPS = 64; // Character which indicates end of window resize
0334 const int INT = 128; // Intermediate Byte (ECMA 48 5.4 -> CSI P..P I..I F)
0335 
0336 void Vt102Emulation::initTokenizer()
0337 {
0338     int i;
0339     quint8 *s;
0340     for (i = 0; i < 256; ++i) {
0341         charClass[i] = 0;
0342     }
0343     for (i = 0; i < 32; ++i) {
0344         charClass[i] |= CTL;
0345     }
0346     for (i = 32; i < 256; ++i) {
0347         charClass[i] |= CHR;
0348     }
0349     for (i = 0x20; i < 0x30; ++i) {
0350         charClass[i] |= INT;
0351     }
0352     for (s = (quint8 *)"@ABCDEFGHILMPSTXZbcdfry"; *s != 0U; ++s) {
0353         charClass[*s] |= CPN;
0354     }
0355     // resize = \e[8;<row>;<col>t
0356     for (s = (quint8 *)"t"; *s != 0U; ++s) {
0357         charClass[*s] |= CPS;
0358     }
0359     for (s = (quint8 *)"0123456789"; *s != 0U; ++s) {
0360         charClass[*s] |= DIG;
0361     }
0362     for (s = (quint8 *)"()+*%"; *s != 0U; ++s) {
0363         charClass[*s] |= SCS;
0364     }
0365     for (s = (quint8 *)"()+*#[]%"; *s != 0U; ++s) {
0366         charClass[*s] |= GRP;
0367     }
0368 
0369     resetTokenizer();
0370 }
0371 
0372 /* Ok, here comes the nasty part of the decoder.
0373 
0374    Instead of keeping an explicit state, we deduce it from the
0375    token scanned so far. It is then immediately combined with
0376    the current character to form a scanning decision.
0377 
0378    This is done by the following defines.
0379 
0380    - P is the length of the token scanned so far.
0381    - L (often P-1) is the position on which contents we base a decision.
0382    - C is a character or a group of characters (taken from 'charClass').
0383 
0384    - 'cc' is the current character
0385    - 's' is a pointer to the start of the token buffer
0386    - 'p' is the current position within the token buffer
0387 
0388    Note that they need to applied in proper order.
0389 */
0390 
0391 /* clang-format off */
0392 #define lec(P,L,C) (p == (P) && s[(L)] == (C))
0393 #define les(P,L,C) (p == (P) && s[L] < 256 && (charClass[s[(L)]] & (C)) == (C))
0394 #define eec(C)     (p >=  3  && cc == (C))
0395 #define ccc(C)     (cc < 256 && (charClass[cc] & (C)) == (C))
0396 #define ees(C)     (p >=  3  && cc < 256 && (charClass[cc] & (C)) == (C))
0397 #define eps(C)     (p >=  3  && s[2] != '?' && s[2] != '!' && s[2] != '=' && s[2] != '>' && cc < 256 && (charClass[cc] & (C)) == (C) && (charClass[s[p-2]] & (INT)) != (INT) )
0398 #define epp( )     (p >=  3  && s[2] == '?')
0399 #define epe( )     (p >=  3  && s[2] == '!')
0400 #define eeq( )     (p >=  3  && s[2] == '=')
0401 #define egt( )     (p >=  3  && s[2] == '>')
0402 #define esp( )     (p >=  4  && s[2] == SP )
0403 #define epsp( )    (p >=  5  && s[3] == SP )
0404 #define osc        (tokenBufferPos >= 2 && tokenBuffer[1] == ']')
0405 //#define apc(C)     (p >= 3 && s[1] == '_' && s[2] == C)
0406 #define pm         (tokenBufferPos >= 2 && tokenBuffer[1] == '^')
0407 #define apc        (tokenBufferPos >= 2 && tokenBuffer[1] == '_')
0408 #define ces(C)     (cc < 256 && (charClass[cc] & (C)) == (C))
0409 #define dcs        (p >= 2   && s[0] == ESC && s[1] == 'P')
0410 #define sixel( )   (p == 1 && cc >= '?' && cc <= '~')
0411 
0412 /* clang-format on */
0413 
0414 #define CNTL(c) ((c) - '@')
0415 /* Not used ATM
0416 const int ESC = 27;
0417 const int DEL = 127;
0418 const int SP = 32; */
0419 
0420 // process an incoming unicode character
0421 //
0422 // Parser based on the vt100.net diagram:
0423 // Williams, Paul Flo. “A parser for DEC’s ANSI-compatible video
0424 // terminals.” VT100.net. https://vt100.net/emu/dec_ansi_parser
0425 
0426 void Vt102Emulation::switchState(const ParserStates newState, const uint cc)
0427 {
0428     switch (_state) {
0429     case DcsPassthrough:
0430         unhook();
0431         break;
0432     case OscString:
0433         osc_end(cc);
0434         break;
0435     case SosPmApcString:
0436         apc_end();
0437         break;
0438     default:
0439         break;
0440     }
0441 
0442     _state = newState;
0443 }
0444 
0445 void Vt102Emulation::esc_dispatch(const uint cc)
0446 {
0447     if (_ignore)
0448         return;
0449     if (_nIntermediate == 0) {
0450         processToken(token_esc(cc), 0, 0);
0451     } else if (_nIntermediate == 1) {
0452         const uint intermediate = _intermediate[0];
0453         if ((charClass[intermediate] & SCS) == SCS) {
0454             processToken(token_esc_cs(intermediate, cc), 0, 0);
0455         } else if (intermediate == '#') {
0456             processToken(token_esc_de(cc), 0, 0);
0457         }
0458     }
0459 }
0460 
0461 void Vt102Emulation::clear()
0462 {
0463     _nIntermediate = 0;
0464     _ignore = false;
0465     resetTokenizer();
0466 }
0467 
0468 #define MAX_INTERMEDIATES 1
0469 void Vt102Emulation::collect(const uint cc)
0470 {
0471     addToCurrentToken(cc);
0472     if (cc > 0x30)
0473         return;
0474     if (_nIntermediate >= MAX_INTERMEDIATES) {
0475         _ignore = true;
0476         return;
0477     }
0478     _intermediate[_nIntermediate++] = cc;
0479 }
0480 
0481 void Vt102Emulation::param(const uint cc)
0482 {
0483     addToCurrentToken(cc);
0484     if ((charClass[cc] & DIG) == DIG) {
0485         addDigit(cc - '0');
0486     } else if (cc == ';') {
0487         addArgument();
0488     } else if (cc == ':') {
0489         addSub();
0490     }
0491 }
0492 
0493 void Vt102Emulation::csi_dispatch(const uint cc)
0494 {
0495     if (_ignore || (params.hasSubParams && cc != 'm')) // Be conservative for now
0496         return;
0497     if ((tokenBufferPos == 0 || (tokenBuffer[0] != '?' && tokenBuffer[0] != '!' && tokenBuffer[0] != '=' && tokenBuffer[0] != '>')) && cc < 256
0498         && (charClass[cc] & CPN) == CPN && _nIntermediate == 0) {
0499         processToken(token_csi_pn(cc), params.value[0], params.value[1]);
0500     } else if ((tokenBufferPos == 0 || (tokenBuffer[0] != '?' && tokenBuffer[0] != '!' && tokenBuffer[0] != '=' && tokenBuffer[0] != '>')) && cc < 256
0501                && (charClass[cc] & CPS) == CPS && _nIntermediate == 0) {
0502         processToken(token_csi_ps(cc, params.value[0]), params.value[1], params.value[2]);
0503     } else if (tokenBufferPos != 0 && tokenBuffer[0] == '!') {
0504         processToken(token_csi_pe(cc), 0, 0);
0505     } else if (_nIntermediate == 1 && _intermediate[0] == ' ') {
0506         if (tokenBufferPos == 1) {
0507             processToken(token_csi_sp(cc), 0, 0);
0508         } else {
0509             processToken(token_csi_psp(cc, params.value[0]), 0, 0);
0510         }
0511     } else if (cc == 'y' && _nIntermediate == 1 && _intermediate[0] == '*') {
0512         processChecksumRequest(params.count, params.value);
0513     } else {
0514         for (int i = 0; i <= params.count; i++) {
0515             if (tokenBufferPos != 0 && tokenBuffer[0] == '?') {
0516                 processToken(token_csi_pr(cc, params.value[i]), i, 0);
0517             } else if (tokenBufferPos != 0 && tokenBuffer[0] == '=') {
0518                 processToken(token_csi_pq(cc), 0, 0);
0519             } else if (tokenBufferPos != 0 && tokenBuffer[0] == '>') {
0520                 processToken(token_csi_pg(cc), 0, 0);
0521             } else if (cc == 'm' && !params.sub[i].count && params.count - i >= 4 && (params.value[i] == 38 || params.value[i] == 48 || params.value[i] == 58)
0522                        && params.value[i + 1] == 2) {
0523                 // ESC[ ... 48;2;<red>;<green>;<blue> ... m -or- ESC[ ... 38;2;<red>;<green>;<blue> ... m
0524                 i += 2;
0525                 processToken(token_csi_ps(cc, params.value[i - 2]),
0526                              COLOR_SPACE_RGB,
0527                              (params.value[i] << 16) | (params.value[i + 1] << 8) | params.value[i + 2]);
0528                 i += 2;
0529             } else if (cc == 'm' && params.sub[i].count >= 5 && (params.value[i] == 38 || params.value[i] == 48 || params.value[i] == 58)
0530                        && params.sub[i].value[1] == 2) {
0531                 // ESC[ ... 48:2:<id>:<red>:<green>:<blue> ... m -or- ESC[ ... 38:2:<id>:<red>:<green>:<blue> ... m
0532                 processToken(token_csi_ps(cc, params.value[i]),
0533                              COLOR_SPACE_RGB,
0534                              (params.sub[i].value[3] << 16) | (params.sub[i].value[4] << 8) | params.sub[i].value[5]);
0535             } else if (cc == 'm' && params.sub[i].count == 4 && (params.value[i] == 38 || params.value[i] == 48 || params.value[i] == 58)
0536                        && params.sub[i].value[1] == 2) {
0537                 // ESC[ ... 48:2:<red>:<green>:<blue> ... m -or- ESC[ ... 38:2:<red>:<green>:<blue> ... m
0538                 processToken(token_csi_ps(cc, params.value[i]),
0539                              COLOR_SPACE_RGB,
0540                              (params.sub[i].value[2] << 16) | (params.sub[i].value[3] << 8) | params.sub[i].value[4]);
0541             } else if (cc == 'm' && params.sub[i].count == 1 && params.value[i] == 4) {
0542                 processToken(token_csi_ps(cc, params.value[i]), params.sub[i].value[1], 1);
0543             } else if (cc == 'm' && !params.sub[i].count && params.count - i >= 2 && (params.value[i] == 38 || params.value[i] == 48 || params.value[i] == 58)
0544                        && params.value[i + 1] == 5) {
0545                 // ESC[ ... 48;5;<index> ... m -or- ESC[ ... 38;5;<index> ... m
0546                 i += 2;
0547                 processToken(token_csi_ps(cc, params.value[i - 2]), COLOR_SPACE_256, params.value[i]);
0548             } else if (cc == 'm' && params.sub[i].count >= 2 && (params.value[i] == 38 || params.value[i] == 48 || params.value[i] == 58)
0549                        && params.sub[i].value[1] == 5) {
0550                 // ESC[ ... 48:5:<index> ... m -or- ESC[ ... 38:5:<index> ... m
0551                 processToken(token_csi_ps(cc, params.value[i]), COLOR_SPACE_256, params.sub[i].value[2]);
0552             } else if (_nIntermediate == 0) {
0553                 processToken(token_csi_ps(cc, params.value[i]), 0, 0);
0554             }
0555         }
0556     }
0557 }
0558 
0559 void Vt102Emulation::osc_start()
0560 {
0561     tokenBufferPos = 0;
0562 }
0563 
0564 void Vt102Emulation::osc_put(const uint cc)
0565 {
0566     addToCurrentToken(cc);
0567 
0568     // Special case: iterm file protocol is a long escape sequence
0569     if (tokenState == -1) {
0570         tokenStateChange = "1337;File=:";
0571         tokenState = 0;
0572     }
0573     if (tokenState >= 0) {
0574         if ((uint)tokenStateChange[tokenState] == tokenBuffer[tokenBufferPos - 1]) {
0575             tokenState++;
0576             tokenPos = tokenBufferPos;
0577             if ((uint)tokenState == strlen(tokenStateChange)) {
0578                 tokenState = -2;
0579                 tokenData.clear();
0580             }
0581             return;
0582         }
0583     } else if (tokenState == -2) {
0584         if (tokenBufferPos - tokenPos == 4) {
0585             tokenData.append(QByteArray::fromBase64(QString::fromUcs4(&tokenBuffer[tokenPos], 4).toLocal8Bit()));
0586             tokenBufferPos -= 4;
0587             return;
0588         }
0589     }
0590 }
0591 
0592 void Vt102Emulation::osc_end(const uint cc)
0593 {
0594     // This runs two times per link, the first prepares the link to be read,
0595     // the second finalizes it. The escape sequence is in two parts
0596     //  start: '\e ] 8 ; <id-path> ; <url-part> \e \\'
0597     //  end:   '\e ] 8 ; ; \e \\'
0598     // GNU libtextstyle inserts the IDs, for instance; many examples
0599     // do not.
0600     if (tokenBuffer[0] == XTERM_EXTENDED::URL_LINK) {
0601         // printf '\e]8;;https://example.com\e\\This is a link\e]8;;\e\\\n'
0602         Q_EMIT toggleUrlExtractionRequest();
0603     }
0604 
0605     processSessionAttributeRequest(tokenBufferPos, cc);
0606 }
0607 
0608 void Vt102Emulation::put(const uint cc)
0609 {
0610     if (m_SixelPictureDefinition && cc >= 0x21) {
0611         addToCurrentToken(cc);
0612         processSixel(cc);
0613     }
0614 }
0615 
0616 void Vt102Emulation::hook(const uint cc)
0617 {
0618     if (cc == 'q' && _nIntermediate == 0) {
0619         m_SixelPictureDefinition = true;
0620         resetTokenizer();
0621     }
0622 }
0623 
0624 void Vt102Emulation::unhook()
0625 {
0626     m_SixelPictureDefinition = false;
0627     SixelModeDisable();
0628     resetTokenizer();
0629 }
0630 
0631 void Vt102Emulation::apc_start(const uint cc)
0632 {
0633     tokenBufferPos = 0;
0634     if (cc == 0x9F || cc == 0x5F) {
0635         _sosPmApc = Apc;
0636     } else if (cc == 0x9E || cc == 0x5E) {
0637         _sosPmApc = Pm;
0638     } else {
0639         // 0x98, 0x58
0640         _sosPmApc = Sos;
0641     }
0642 }
0643 
0644 void Vt102Emulation::apc_put(const uint cc)
0645 {
0646     if (_sosPmApc != Apc) {
0647         return;
0648     }
0649 
0650     addToCurrentToken(cc);
0651 
0652     // <ESC> '_' ... <ESC> '\'
0653     if (tokenBufferPos > 1 && tokenBuffer[0] == 'G') {
0654         if (tokenState == -1) {
0655             tokenStateChange = ";";
0656             tokenState = 0;
0657         } else if (tokenState >= 0) {
0658             if ((uint)tokenStateChange[tokenState] == tokenBuffer[tokenBufferPos - 1]) {
0659                 tokenState++;
0660                 tokenPos = tokenBufferPos;
0661                 if ((uint)tokenState == strlen(tokenStateChange)) {
0662                     tokenState = -2;
0663                     tokenData.clear();
0664                 }
0665             }
0666         } else if (tokenState == -2) {
0667             if (tokenBufferPos - tokenPos == 4) {
0668                 tokenData.append(QByteArray::fromBase64(QString::fromUcs4(&tokenBuffer[tokenPos], 4).toLocal8Bit()));
0669                 tokenBufferPos -= 4;
0670             }
0671         }
0672     }
0673 }
0674 
0675 void Vt102Emulation::apc_end()
0676 {
0677     if (_sosPmApc == Apc && tokenBuffer[0] == 'G') {
0678         // Graphics command
0679         processGraphicsToken(tokenBufferPos);
0680         resetTokenizer();
0681     }
0682 }
0683 
0684 void Vt102Emulation::receiveChars(const QVector<uint> &chars)
0685 {
0686     for (uint cc : chars) {
0687         // early out for displayable characters
0688         if (_state == Ground && ((cc >= 0x20 && cc <= 0x7E) || cc >= 0xA0)) {
0689             _currentScreen->displayCharacter(applyCharset(cc));
0690             continue;
0691         }
0692 
0693         if (getMode(MODE_Ansi)) {
0694             // First, process characters that act the same on all states, i.e.
0695             // coming from "anywhere" in the VT100.net diagram.
0696             if (cc == 0x1B) {
0697                 switchState(Escape, cc);
0698                 clear();
0699             } else if (cc == 0x9B) {
0700                 switchState(CsiEntry, cc);
0701                 clear();
0702             } else if (cc == 0x90) {
0703                 switchState(DcsEntry, cc);
0704                 clear();
0705             } else if (cc == 0x9D) {
0706                 osc_start();
0707                 switchState(OscString, cc);
0708             } else if (cc == 0x98 || cc == 0x9E || cc == 0x9F) {
0709                 apc_start(cc);
0710                 switchState(SosPmApcString, cc);
0711             } else if (cc == 0x18 || cc == 0x1A || (cc >= 0x80 && cc <= 0x9A)) { // 0x90, 0x98 taken care of just above.
0712                 // konsole has always ignored CAN and SUB in OSC, extend the behavior a bit
0713                 // this differs from VT240, where 7-bit ST, 8-bit ST, ESC + chr, ***CAN, SUB, C1*** terminate and show SIXEL
0714                 if (_state != OscString && _state != SosPmApcString && _state != DcsPassthrough) {
0715                     processToken(token_ctl(cc + '@'), 0, 0);
0716                     switchState(Ground, cc);
0717                 }
0718             } else if (cc == 0x9C) {
0719                 // no action
0720                 switchState(Ground, cc);
0721 
0722             } else {
0723                 // Now take the current state into account
0724                 switch (_state) {
0725                 case Ground:
0726                     if (cc <= 0x1F) { // 0x18, 0x1A, 0x1B already taken care of.
0727                         processToken(token_ctl(cc + '@'), 0, 0);
0728                     } else {
0729                         // 0x7F is ignored by displayCharacter(), since its Character::width() is -1
0730                         _currentScreen->displayCharacter(applyCharset(cc));
0731                     }
0732                     break;
0733                 case Escape:
0734                     if (cc == 0x5B) {
0735                         switchState(CsiEntry, cc);
0736                         clear();
0737                     } else if ((cc >= 0x30 && cc <= 0x4F) || (cc >= 0x51 && cc <= 0x57) || (cc >= 0x59 && cc <= 0x5A) || cc == 0x5C
0738                                || (cc >= 0x60 && cc <= 0x7E)) {
0739                         esc_dispatch(cc);
0740                         switchState(Ground, cc);
0741                     } else if (cc >= 0x20 && cc <= 0x2F) {
0742                         collect(cc);
0743                         switchState(EscapeIntermediate, cc);
0744                     } else if (cc == 0x5D) {
0745                         osc_start();
0746                         switchState(OscString, cc);
0747                     } else if (cc == 0x50) {
0748                         switchState(DcsEntry, cc);
0749                         clear();
0750                     } else if (cc == 0x58 || cc == 0x5E || cc == 0x5F) {
0751                         apc_start(cc);
0752                         switchState(SosPmApcString, cc);
0753                     } else if (cc <= 0x1F) { // 0x18, 0x1A, 0x1B already taken care of.
0754                         processToken(token_ctl(cc + '@'), 0, 0);
0755                     } else if (cc == 0x7F) {
0756                         // ignore
0757                     }
0758                     break;
0759                 case EscapeIntermediate:
0760                     if (cc >= 0x30 && cc <= 0x7E) {
0761                         esc_dispatch(cc);
0762                         switchState(Ground, cc);
0763                     } else if (cc >= 0x20 && cc <= 0x2F) {
0764                         collect(cc);
0765                     } else if (cc <= 0x1F) { // 0x18, 0x1A, 0x1B already taken care of.
0766                         processToken(token_ctl(cc + '@'), 0, 0);
0767                     } else if (cc == 0x7F) {
0768                         // ignore
0769                     }
0770                     break;
0771                 case CsiEntry:
0772                     if (cc >= 0x40 && cc <= 0x7E) {
0773                         csi_dispatch(cc);
0774                         switchState(Ground, cc);
0775                     } else if (cc >= 0x30 && cc <= 0x3B) { // recognize 0x3A as part of params
0776                         param(cc);
0777                         switchState(CsiParam, cc);
0778                     } else if (cc >= 0x3C && cc <= 0x3F) {
0779                         collect(cc);
0780                         switchState(CsiParam, cc);
0781                     } else if (cc >= 0x20 && cc <= 0x2F) {
0782                         collect(cc);
0783                         switchState(CsiIntermediate, cc);
0784                     } else if (cc <= 0x1F) { // 0x18, 0x1A, 0x1B already taken care of.
0785                         processToken(token_ctl(cc + '@'), 0, 0);
0786                     } else if (cc == 0x7F) {
0787                         // ignore
0788                     }
0789                     break;
0790                 case CsiParam:
0791                     if (cc >= 0x40 && cc <= 0x7E) {
0792                         csi_dispatch(cc);
0793                         switchState(Ground, cc);
0794                     } else if (cc >= 0x30 && cc <= 0x3B) { // recognize 0x3A as part of params
0795                         param(cc);
0796                     } else if (cc >= 0x3C && cc <= 0x3F) {
0797                         switchState(CsiIgnore, cc);
0798                     } else if (cc >= 0x20 && cc <= 0x2F) {
0799                         collect(cc);
0800                         switchState(CsiIntermediate, cc);
0801                     } else if (cc <= 0x1F) { // 0x18, 0x1A, 0x1B already taken care of.
0802                         processToken(token_ctl(cc + '@'), 0, 0);
0803                     } else if (cc == 0x7F) {
0804                         // ignore
0805                     }
0806                     break;
0807                 case CsiIntermediate:
0808                     if (cc >= 0x40 && cc <= 0x7E) {
0809                         csi_dispatch(cc);
0810                         switchState(Ground, cc);
0811                     } else if (cc >= 0x20 && cc <= 0x2F) {
0812                         collect(cc);
0813                     } else if (cc >= 0x30 && cc <= 0x3F) {
0814                         switchState(CsiIgnore, cc);
0815                     } else if (cc <= 0x1F) { // 0x18, 0x1A, 0x1B already taken care of.
0816                         processToken(token_ctl(cc + '@'), 0, 0);
0817                     } else if (cc == 0x7F) {
0818                         // ignore
0819                     }
0820                     break;
0821                 case CsiIgnore:
0822                     if (cc >= 0x40 && cc <= 0x7E) {
0823                         switchState(Ground, cc);
0824                     } else if (cc <= 0x1F) { // 0x18, 0x1A, 0x1B already taken care of.
0825                         processToken(token_ctl(cc + '@'), 0, 0);
0826                     } else if ((/*cc >= 0x20 && */ cc <= 0x3F) || cc == 0x7F) { // cc <= 0x1F taking care of just above
0827                         // ignore
0828                     }
0829                     break;
0830                 case DcsEntry:
0831                     if (cc >= 0x40 && cc <= 0x7E) {
0832                         hook(cc);
0833                         switchState(DcsPassthrough, cc);
0834                     } else if (cc >= 0x30 && cc <= 0x3B) { // recognize 0x3A as part of params
0835                         param(cc);
0836                         switchState(DcsParam, cc);
0837                     } else if (cc >= 0x3C && cc <= 0x3F) {
0838                         collect(cc);
0839                         switchState(DcsParam, cc);
0840                     } else if (cc >= 0x20 && cc <= 0x2F) {
0841                         collect(cc);
0842                         switchState(DcsIntermediate, cc);
0843                     } else if (cc <= 0x1F) { // 0x18, 0x1A, 0x1B already taken care of.
0844                         processToken(token_ctl(cc + '@'), 0, 0);
0845                     } else if (cc == 0x7F) {
0846                         // ignore
0847                     }
0848                     break;
0849                 case DcsParam:
0850                     if (cc >= 0x40 && cc <= 0x7E) {
0851                         hook(cc);
0852                         switchState(DcsPassthrough, cc);
0853                     } else if (cc >= 0x30 && cc <= 0x3B) { // recognize 0x3A as part of params
0854                         param(cc);
0855                     } else if (cc >= 0x3C && cc <= 0x3F) {
0856                         switchState(DcsIgnore, cc);
0857                     } else if (cc >= 0x20 && cc <= 0x2F) {
0858                         collect(cc);
0859                         switchState(DcsIntermediate, cc);
0860                     } else if (cc <= 0x1F) { // 0x18, 0x1A, 0x1B already taken care of.
0861                         processToken(token_ctl(cc + '@'), 0, 0);
0862                     } else if (cc == 0x7F) {
0863                         // ignore
0864                     }
0865                     break;
0866                 case DcsIntermediate:
0867                     if (cc >= 0x40 && cc <= 0x7E) {
0868                         hook(cc);
0869                         switchState(DcsPassthrough, cc);
0870                     } else if (cc >= 0x20 && cc <= 0x2F) {
0871                         collect(cc);
0872                     } else if (cc >= 0x30 && cc <= 0x3F) {
0873                         switchState(DcsIgnore, cc);
0874                     } else if (cc <= 0x1F) { // 0x18, 0x1A, 0x1B already taken care of.
0875                         processToken(token_ctl(cc + '@'), 0, 0);
0876                     } else if (cc == 0x7F) {
0877                         // ignore
0878                     }
0879                     break;
0880                 case DcsPassthrough:
0881                     if (cc <= 0x7E || cc >= 0xA0) { // 0x18, 0x1A, 0x1B already taken care of
0882                         put(cc);
0883                         // 0x9C already taken care of.
0884                     } else if (cc == 0x7F) {
0885                         // ignore
0886                     }
0887                     break;
0888                 case DcsIgnore:
0889                     // 0x9C already taken care of.
0890                     if (cc <= 0x7F) {
0891                         // ignore
0892                     }
0893                     break;
0894                 case OscString:
0895                     if ((cc >= 0x20 && cc <= 0x7F) || cc >= 0xA0) {
0896                         osc_put(cc);
0897                     } else if (cc == 0x07 // recognize BEL as OSC terminator
0898                                || cc == 0x9C) {
0899                         switchState(Ground, cc);
0900                     } else if (cc <= 0x1F) { // 0x18, 0x1A, 0x1B already taken care of. 0x07 taken care of just above.
0901                         // ignore
0902                     }
0903                     break;
0904                 case SosPmApcString:
0905                     if (cc <= 0x7F || cc >= 0xA0) { // 0x18, 0x1A, 0x1B already taken care of.
0906                         apc_put(cc); // while the vt100.net diagram has ignore here, konsole does process some APCs (kitty images).
0907                     }
0908                     // 0x9C already taken care of.
0909                     break;
0910                 default:
0911                     break;
0912                 }
0913             }
0914         } else {
0915             // VT52 Mode
0916 
0917             // First, process characters that act the same on all states
0918             if (cc == 0x18 || cc == 0x1A) {
0919                 processToken(token_ctl(cc + '@'), 0, 0);
0920                 switchState(Ground, cc);
0921             } else if (cc == 0x1B) {
0922                 switchState(Vt52Escape, cc);
0923             } else if (cc <= 0x1F) { // 0x18, 0x1A, 0x1B taken care of just above
0924                 processToken(token_ctl(cc + '@'), 0, 0);
0925             } else {
0926                 // Now take the current state into account
0927                 switch (_state) {
0928                 case Ground:
0929                     _currentScreen->displayCharacter(applyCharset(cc));
0930                     break;
0931                 case Vt52Escape:
0932                     if (cc == 'Y') {
0933                         switchState(Vt52CupRow, cc);
0934                     } else if ((cc >= 0x20 && cc <= 'X') || (cc >= 'Z' && cc <= 0x7F)) {
0935                         processToken(token_vt52(cc), 0, 0);
0936                         switchState(Ground, cc);
0937                     }
0938                     break;
0939                 case Vt52CupRow:
0940                     tokenBuffer[0] = cc;
0941                     switchState(Vt52CupColumn, cc);
0942                     break;
0943                 case Vt52CupColumn:
0944                     processToken(token_vt52('Y'), tokenBuffer[0], cc);
0945                     switchState(Ground, cc);
0946                     break;
0947                 default:
0948                     break;
0949                 }
0950             }
0951         }
0952     }
0953 }
0954 
0955 void Vt102Emulation::processChecksumRequest([[maybe_unused]] int crargc, int crargv[])
0956 {
0957     int checksum = 0;
0958 
0959 #if ENABLE_DECRQCRA
0960     int top, left, bottom, right;
0961 
0962     /* DEC STD-070 5-179 "If Pp is 0 or omitted, subsequent parameters are ignored
0963      *  and a checksum for all page memory will be reported."
0964      */
0965     if (crargv[1] == 0) {
0966         crargc = 1;
0967     }
0968     /* clang-format off */
0969     if (crargc >= 2) { top    = crargv[2]; } else { top    = 1; }
0970     if (crargc >= 3) { left   = crargv[3]; } else { left   = 1; }
0971     if (crargc >= 4) { bottom = crargv[4]; } else { bottom = _currentScreen->getLines();   }
0972     if (crargc >= 5) { right  = crargv[5]; } else { right  = _currentScreen->getColumns(); }
0973     /* clang-format on */
0974 
0975     if (top > bottom || left > right) {
0976         return;
0977     }
0978 
0979     if (_currentScreen->getMode(MODE_Origin)) {
0980         top += _currentScreen->topMargin();
0981         bottom += _currentScreen->topMargin();
0982     }
0983 
0984     top = qBound(1, top, _currentScreen->getLines());
0985     bottom = qBound(1, bottom, _currentScreen->getLines());
0986 
0987     int imgsize = sizeof(Character) * _currentScreen->getLines() * _currentScreen->getColumns();
0988     Character *image = (Character *)malloc(imgsize);
0989     if (image == nullptr) {
0990         fprintf(stderr, "couldn't alloc mem\n");
0991         return;
0992     }
0993     _currentScreen->getImage(image, imgsize, _currentScreen->getHistLines(), _currentScreen->getHistLines() + _currentScreen->getLines() - 1);
0994 
0995     for (int y = top - 1; y <= bottom - 1; y++) {
0996         for (int x = left - 1; x <= right - 1; x++) {
0997             // XXX: Apparently, VT520 uses 0x00 for uninitialized cells, konsole can't tell uninitialized cells from spaces
0998             Character c = image[y * _currentScreen->getColumns() + x];
0999 
1000             if (c.rendition.f.conceal) {
1001                 checksum += 0x20; // don't reveal secrets
1002             } else {
1003                 checksum += c.character;
1004             }
1005 
1006             checksum += c.rendition.f.bold * 0x80;
1007             checksum += c.rendition.f.blink * 0x40;
1008             checksum += c.rendition.f.reverse * 0x20;
1009             checksum += !!(c.rendition.all & RE_UNDERLINE_MASK) * 0x10;
1010         }
1011     }
1012 
1013     free(image);
1014 #endif
1015 
1016     char tmp[30];
1017     checksum = -checksum;
1018     checksum &= 0xffff;
1019     snprintf(tmp, sizeof(tmp), "\033P%d!~%04X\033\\", crargv[0], checksum);
1020     sendString(tmp);
1021 }
1022 
1023 void Vt102Emulation::processSessionAttributeRequest(const int tokenSize, const uint terminator)
1024 {
1025     // Describes the window or terminal session attribute to change
1026     // See Session::SessionAttributes for possible values
1027     int attribute = 0;
1028     int i;
1029 
1030     /* clang-format off */
1031     for (i = 0; i < tokenSize &&
1032                 tokenBuffer[i] >= '0'  &&
1033                 tokenBuffer[i] <= '9'; i++)
1034     {
1035         attribute = 10 * attribute + (tokenBuffer[i]-'0');
1036     }
1037     /* clang-format on */
1038 
1039     if (tokenBuffer[i] != ';') {
1040         // No arguments
1041         switch (attribute) {
1042         case 104: // 104 without any argument means clear entire color table
1043             for (int i = 0; i < 256; i++) {
1044                 colorTable[i] = QColor();
1045             }
1046             break;
1047         default:
1048             reportDecodingError(token_osc(terminator));
1049             break;
1050         }
1051 
1052         return;
1053     }
1054     // skip initial ';'
1055     ++i;
1056 
1057     QString value = QString::fromUcs4(&tokenBuffer[i], tokenSize - i);
1058     if (_currentScreen->urlExtractor() && _currentScreen->urlExtractor()->reading()) {
1059         // To handle '\e ] 8 ; <id-part> ; <url-part>' we discard
1060         // the <id-part>. Often it is empty, but GNU libtextstyle
1061         // may output an id here, see e.g.
1062         // https://www.gnu.org/software/gettext/libtextstyle/manual/libtextstyle.html#index-styled_005fostream_005fset_005fhyperlink
1063         value.remove(0, value.indexOf(QLatin1Char(';')) + 1);
1064         _currentScreen->urlExtractor()->setUrl(value);
1065         return;
1066     }
1067 
1068     if (attribute == SemanticPrompts) {
1069         if (value[0] == QLatin1Char('A') || value[0] == QLatin1Char('N') || value[0] == QLatin1Char('P')) {
1070             _currentScreen->setReplMode(REPL_PROMPT);
1071         }
1072         if (value[0] == QLatin1Char('L') && _currentScreen->getCursorX() > 0) {
1073             _currentScreen->nextLine();
1074         }
1075         if (value[0] == QLatin1Char('B')) {
1076             _currentScreen->setReplMode(REPL_INPUT);
1077         }
1078         if (value[0] == QLatin1Char('C')) {
1079             _currentScreen->setReplMode(REPL_OUTPUT);
1080         }
1081         if (value[0] == QLatin1Char('D')) {
1082             _currentScreen->setReplMode(REPL_None);
1083         }
1084         QMap<QString, QString> params;
1085         auto list = value.split(QLatin1Char(';'));
1086         for (int i = 1; i < list.size(); i++) {
1087             int eq = list.at(i).indexOf(QLatin1Char('='));
1088             if (i == 1 && value[0] == QLatin1Char('D')) {
1089                 // Special case - exit code without '='
1090                 params[QLatin1String("exit_code")] = list.at(1);
1091                 int exitCode = list.at(1).toInt();
1092                 if (exitCode) {
1093                     _currentScreen->setExitCode(exitCode);
1094                 }
1095             } else if (eq > 0) {
1096                 params[list.at(i).mid(0, eq)] = list.at(i).mid(eq + 1);
1097             }
1098         }
1099     }
1100     if (attribute == ReportColors) {
1101         // RGB colors
1102         QStringList params = value.split(QLatin1Char(';'));
1103         for (int j = 0; j < params.length(); j += 2) {
1104             if (params.length() == j + 1) {
1105                 return;
1106             }
1107             int c = params[j].toInt();
1108             if (params[j + 1] == QLatin1String("?")) {
1109                 QColor color = colorTable[c];
1110                 if (!color.isValid()) {
1111                     color = CharacterColor(COLOR_SPACE_256, c).color(ColorScheme::defaultTable);
1112                 }
1113                 reportColor(c, color);
1114                 return;
1115             }
1116             QColor col(params[1]);
1117             colorTable[c] = col;
1118         }
1119         return;
1120     }
1121     if (attribute == ResetColors) {
1122         // RGB colors
1123         QStringList params = value.split(QLatin1Char(';'));
1124         for (int k = 0; k < params.length(); k++) {
1125             int c = params[k].toInt();
1126             colorTable[c] = QColor();
1127         }
1128     }
1129     if (attribute == Notification) {
1130         // Notification
1131         auto params = value.split(QLatin1Char(';'));
1132         if (params.length() < 1 || params[0] != QLatin1String("notify")) {
1133             return;
1134         }
1135 
1136         const auto hasFocus = _currentScreen->currentTerminalDisplay()->hasFocus();
1137         KNotification *notification = nullptr;
1138         if (params.length() >= 3) {
1139             notification =
1140                 KNotification::event(hasFocus ? QStringLiteral("ProcessNotification") : QStringLiteral("ProcessNotificationHidden"), params[1], params[2]);
1141         } else {
1142             notification = KNotification::event(hasFocus ? QStringLiteral("ProcessNotification") : QStringLiteral("ProcessNotificationHidden"), params[1]);
1143         }
1144 
1145         auto action = notification->addDefaultAction(i18n("Show session"));
1146         connect(action, &KNotificationAction::activated, this, [this, notification]() {
1147             _currentScreen->currentTerminalDisplay()->notificationClicked(notification->xdgActivationToken());
1148         });
1149 
1150         return;
1151     }
1152 
1153     if (value == QLatin1String("?")) {
1154         // pass terminator type indication here, because OSC response terminator
1155         // should match the terminator of OSC request.
1156         Q_EMIT sessionAttributeRequest(attribute, terminator);
1157         return;
1158     }
1159 
1160     if (attribute == Session::ProfileChange) {
1161         bool styleChanged = false;
1162         bool isBlinking = false;
1163         Enum::CursorShapeEnum shape = Enum::BlockCursor;
1164         QColor customColor;
1165 
1166         if (TerminalDisplay *currentView = _currentScreen->currentTerminalDisplay()) {
1167             if (SessionController *sessionController = currentView->sessionController()) {
1168                 Session *session = sessionController->session();
1169                 SessionManager *manager = SessionManager::instance();
1170                 Profile::Ptr currentProfile = manager->sessionProfile(session);
1171                 shape = currentProfile->cursorShape();
1172                 isBlinking = currentProfile->blinkingCursorEnabled();
1173                 customColor = currentProfile->customCursorColor();
1174             }
1175         }
1176 
1177         const QLatin1String cursorShapeStr("CursorShape=");
1178         const QLatin1String blinkingCursorStr("BlinkingCursorEnabled=");
1179         const QLatin1String customCursorColorStr("CustomCursorColor=");
1180 
1181         QString newValue;
1182         const auto items = QStringView(value).split(QLatin1Char(';'));
1183         for (const auto &item : items) {
1184             if (item.startsWith(cursorShapeStr)) {
1185                 const int s = item.at(cursorShapeStr.size()).digitValue();
1186                 shape = static_cast<Enum::CursorShapeEnum>(s != -1 ? s : 0);
1187                 styleChanged = true;
1188 
1189             } else if (item.startsWith(blinkingCursorStr)) {
1190                 const auto str = item.mid(blinkingCursorStr.size());
1191                 if (str.compare(QString::fromLatin1("true"), Qt::CaseInsensitive) == 0) {
1192                     isBlinking = true;
1193                     styleChanged = true;
1194                 } else if (str.compare(QString::fromLatin1("false"), Qt::CaseInsensitive) == 0) {
1195                     isBlinking = false;
1196                     styleChanged = true;
1197                 } else {
1198                     bool ok = false;
1199                     bool newIsBlinking = str.toInt(&ok);
1200                     if (ok) {
1201                         isBlinking = newIsBlinking;
1202                         styleChanged = true;
1203                     }
1204                 }
1205 
1206             } else if (item.startsWith(customCursorColorStr)) {
1207                 const auto colorStr = item.mid(customCursorColorStr.size());
1208                 customColor = QColor(colorStr);
1209                 styleChanged = true;
1210 
1211             } else {
1212                 if (!newValue.isEmpty())
1213                     newValue.append(QChar::fromLatin1(';'));
1214                 newValue.append(item);
1215             }
1216         }
1217 
1218         if (styleChanged) {
1219             Q_EMIT setCursorStyleRequest(shape, isBlinking, customColor);
1220         }
1221 
1222         if (newValue.isEmpty()) {
1223             return;
1224         }
1225 
1226         value = newValue;
1227     }
1228 
1229     if (attribute == Image) {
1230         bool inlineImage = false;
1231         bool inlineMedia = false;
1232         if (value.startsWith(QLatin1String("ReportCellSize"))) {
1233             iTermReportCellSize();
1234             return;
1235         }
1236         if (!value.startsWith(QLatin1String("File="))) {
1237             return;
1238         }
1239         int pos = value.indexOf(QLatin1Char(':'));
1240         QStringList params = value.mid(5, pos - 5).split(QLatin1Char(';'));
1241         bool keepAspect = true;
1242         int scaledWidth = 0;
1243         int scaledHeight = 0;
1244         bool moveCursor = true;
1245         for (const auto &p : params) {
1246             int eq = p.indexOf(QLatin1Char('='));
1247             if (eq > 0) {
1248                 QString var = p.mid(0, eq);
1249                 QString val = p.mid(eq + 1);
1250                 if (var == QLatin1String("inline")) {
1251                     inlineImage = val == QLatin1String("1");
1252                 }
1253                 if (var == QLatin1String("inlineMedia")) {
1254                     inlineMedia = val == QLatin1String("1");
1255                 }
1256                 if (var == QLatin1String("preserveAspectRatio")) {
1257                     keepAspect = val == QLatin1String("0");
1258                 }
1259                 if (var == QLatin1String("doNotMoveCursor")) {
1260                     moveCursor = val != QLatin1String("1");
1261                 }
1262                 if (var == QLatin1String("width")) {
1263                     int unitPos = val.toStdString().find_first_not_of("0123456789");
1264                     scaledWidth = QStringView(val).mid(0, unitPos).toInt();
1265                     if (unitPos == -1) {
1266                         scaledWidth *= _currentScreen->currentTerminalDisplay()->terminalFont()->fontWidth();
1267                     } else {
1268                         if (val.mid(unitPos) == QLatin1String("%")) {
1269                             scaledWidth *= _currentScreen->currentTerminalDisplay()->terminalFont()->fontWidth() * _currentScreen->getColumns() / 100;
1270                         }
1271                     }
1272                 }
1273                 if (var == QLatin1String("height")) {
1274                     int unitPos = val.toStdString().find_first_not_of("0123456789");
1275                     scaledHeight = QStringView(val).mid(0, unitPos).toInt();
1276                     if (unitPos == -1) {
1277                         scaledHeight *= _currentScreen->currentTerminalDisplay()->terminalFont()->fontHeight();
1278                     } else {
1279                         if (val.mid(unitPos) == QLatin1String("%")) {
1280                             scaledHeight *= _currentScreen->currentTerminalDisplay()->terminalFont()->fontHeight() * _currentScreen->getLines() / 100;
1281                         }
1282                     }
1283                 }
1284             }
1285         }
1286         if (inlineMedia) {
1287             if (player == nullptr) {
1288                 player = new QMediaPlayer(this);
1289                 connect(player, SIGNAL(mediaStatusChanged(QMediaPlayer::MediaStatus)), this, SLOT(deletePlayer(QMediaPlayer::MediaStatus)));
1290             }
1291             QBuffer *buffer = new QBuffer(player);
1292             buffer->setData(tokenData);
1293             buffer->open(QIODevice::ReadOnly);
1294             delete (QIODevice *)(player->sourceDevice());
1295             player->setSourceDevice(buffer);
1296             player->play();
1297             return;
1298         }
1299         if (!inlineImage) {
1300             return;
1301         }
1302         QPixmap pixmap;
1303         pixmap.loadFromData(tokenData);
1304         tokenData.clear();
1305         if (pixmap.isNull()) {
1306             return;
1307         }
1308         if (scaledWidth && scaledHeight) {
1309             pixmap = pixmap.scaled(scaledWidth, scaledHeight, (Qt::AspectRatioMode)keepAspect);
1310         } else {
1311             if (keepAspect && scaledWidth) {
1312                 pixmap = pixmap.scaledToWidth(scaledWidth);
1313             } else if (keepAspect && scaledHeight) {
1314                 pixmap = pixmap.scaledToHeight(scaledHeight);
1315             }
1316         }
1317         int rows = -1, cols = -1;
1318         _currentScreen->addPlacement(pixmap, rows, cols, -1, -1, true, moveCursor);
1319     }
1320     _pendingSessionAttributesUpdates[attribute] = value;
1321     _sessionAttributesUpdateTimer->start(20);
1322 }
1323 
1324 void Vt102Emulation::deletePlayer(QMediaPlayer::MediaStatus mediaStatus)
1325 {
1326     if (mediaStatus == QMediaPlayer::EndOfMedia || mediaStatus == QMediaPlayer::InvalidMedia) {
1327         QIODevice *buffer = (QIODevice *)(player->sourceDevice());
1328         buffer->deleteLater();
1329         player->deleteLater();
1330         player = nullptr;
1331     }
1332 }
1333 
1334 void Vt102Emulation::updateSessionAttributes()
1335 {
1336     QListIterator<int> iter(_pendingSessionAttributesUpdates.keys());
1337     while (iter.hasNext()) {
1338         int arg = iter.next();
1339         Q_EMIT sessionAttributeChanged(arg, _pendingSessionAttributesUpdates[arg]);
1340     }
1341     _pendingSessionAttributesUpdates.clear();
1342 }
1343 
1344 // Interpreting Codes ---------------------------------------------------------
1345 
1346 /*
1347    Now that the incoming character stream is properly tokenized,
1348    meaning is assigned to them. These are either operations of
1349    the current _screen, or of the emulation class itself.
1350 
1351    The token to be interpreted comes in as a machine word
1352    possibly accompanied by two parameters.
1353 
1354    Likewise, the operations assigned to, come with up to two
1355    arguments. One could consider to make up a proper table
1356    from the function below.
1357 
1358    The technical reference manual provides more information
1359    about this mapping.
1360 */
1361 
1362 void Vt102Emulation::processToken(int token, int p, int q)
1363 {
1364     /* clang-format off */
1365     switch (token) {
1366     case token_chr(         ) :
1367         _currentScreen->displayCharacter     (p         ); break; //UTF16
1368 
1369     //             127 DEL    : ignored on input
1370 
1371     case token_ctl('@'      ) : /* NUL: ignored                      */ break;
1372     case token_ctl('A'      ) : /* SOH: ignored                      */ break;
1373     case token_ctl('B'      ) : /* STX: ignored                      */ break;
1374     case token_ctl('C'      ) : /* ETX: ignored                      */ break;
1375     case token_ctl('D'      ) : /* EOT: ignored                      */ break;
1376     case token_ctl('E'      ) :      reportAnswerBack     (          ); break; //VT100
1377     case token_ctl('F'      ) : /* ACK: ignored                      */ break;
1378     case token_ctl('G'      ) : Q_EMIT bell();
1379                                 break; //VT100
1380     case token_ctl('H'      ) : _currentScreen->backspace            (          ); break; //VT100
1381     case token_ctl('I'      ) : _currentScreen->tab                  (          ); break; //VT100
1382     case token_ctl('J'      ) : _currentScreen->newLine              (          ); break; //VT100
1383     case token_ctl('K'      ) : _currentScreen->newLine              (          ); break; //VT100
1384     case token_ctl('L'      ) : _currentScreen->newLine              (          ); break; //VT100
1385     case token_ctl('M'      ) : _currentScreen->toStartOfLine        (          ); break; //VT100
1386 
1387     case token_ctl('N'      ) :      useCharset           (         1); break; //VT100
1388     case token_ctl('O'      ) :      useCharset           (         0); break; //VT100
1389 
1390     case token_ctl('P'      ) : /* DLE: ignored                      */ break;
1391     case token_ctl('Q'      ) : /* DC1: XON continue                 */ break; //VT100
1392     case token_ctl('R'      ) : /* DC2: ignored                      */ break;
1393     case token_ctl('S'      ) : /* DC3: XOFF halt                    */ break; //VT100
1394     case token_ctl('T'      ) : /* DC4: ignored                      */ break;
1395     case token_ctl('U'      ) : /* NAK: ignored                      */ break;
1396     case token_ctl('V'      ) : /* SYN: ignored                      */ break;
1397     case token_ctl('W'      ) : /* ETB: ignored                      */ break;
1398     case token_ctl('X'      ) : _currentScreen->displayCharacter     (    0x2592); break; //VT100
1399     case token_ctl('Y'      ) : /* EM : ignored                      */ break;
1400     case token_ctl('Z'      ) : _currentScreen->displayCharacter     (    0x2592); break; //VT100
1401     case token_ctl('['      ) : /* ESC: cannot be seen here.         */ break;
1402     case token_ctl('\\'     ) : /* FS : ignored                      */ break;
1403     case token_ctl(']'      ) : /* GS : ignored                      */ break;
1404     case token_ctl('^'      ) : /* RS : ignored                      */ break;
1405     case token_ctl('_'      ) : /* US : ignored                      */ break;
1406 
1407     case token_esc('D'      ) : _currentScreen->index                (          ); break; //VT100
1408     case token_esc('E'      ) : _currentScreen->nextLine             (          ); break; //VT100
1409     case token_esc('H'      ) : _currentScreen->changeTabStop        (true      ); break; //VT100
1410     case token_esc('M'      ) : _currentScreen->reverseIndex         (          ); break; //VT100
1411     case token_esc('Z'      ) :      reportTerminalType   (          ); break;
1412     case token_esc('c'      ) :      reset                (          ); break;
1413 
1414     case token_esc('n'      ) :      useCharset           (         2); break;
1415     case token_esc('o'      ) :      useCharset           (         3); break;
1416     case token_esc('7'      ) :      saveCursor           (          ); break;
1417     case token_esc('8'      ) :      restoreCursor        (          ); break;
1418 
1419     case token_esc('='      ) :          setMode      (MODE_AppKeyPad); break;
1420     case token_esc('>'      ) :        resetMode      (MODE_AppKeyPad); break;
1421     case token_esc('<'      ) :          setMode      (MODE_Ansi     ); break; //VT100
1422 
1423     case token_esc('\\'      ) :       resetMode      (MODE_Sixel    ); break;
1424 
1425     case token_esc_cs('(', '0') :      setCharset           (0,    '0'); break; //VT100
1426     case token_esc_cs('(', 'A') :      setCharset           (0,    'A'); break; //VT100
1427     case token_esc_cs('(', 'B') :      setCharset           (0,    'B'); break; //VT100
1428 
1429     case token_esc_cs(')', '0') :      setCharset           (1,    '0'); break; //VT100
1430     case token_esc_cs(')', 'A') :      setCharset           (1,    'A'); break; //VT100
1431     case token_esc_cs(')', 'B') :      setCharset           (1,    'B'); break; //VT100
1432 
1433     case token_esc_cs('*', '0') :      setCharset           (2,    '0'); break; //VT100
1434     case token_esc_cs('*', 'A') :      setCharset           (2,    'A'); break; //VT100
1435     case token_esc_cs('*', 'B') :      setCharset           (2,    'B'); break; //VT100
1436 
1437     case token_esc_cs('+', '0') :      setCharset           (3,    '0'); break; //VT100
1438     case token_esc_cs('+', 'A') :      setCharset           (3,    'A'); break; //VT100
1439     case token_esc_cs('+', 'B') :      setCharset           (3,    'B'); break; //VT100
1440 
1441     case token_esc_cs('%', 'G') :      setCodec             (Utf8Codec   ); break; //LINUX
1442     case token_esc_cs('%', '@') :      setCodec             (LocaleCodec ); break; //LINUX
1443 
1444     case token_esc_de('3'      ) : /* Double height line, top half    */
1445                                 _currentScreen->setLineProperty( LINE_DOUBLEWIDTH , true );
1446                                 _currentScreen->setLineProperty( LINE_DOUBLEHEIGHT_TOP , true );
1447                                 _currentScreen->setLineProperty( LINE_DOUBLEHEIGHT_BOTTOM , false );
1448                                     break;
1449     case token_esc_de('4'      ) : /* Double height line, bottom half */
1450                                 _currentScreen->setLineProperty( LINE_DOUBLEWIDTH , true );
1451                                 _currentScreen->setLineProperty( LINE_DOUBLEHEIGHT_TOP , false );
1452                                 _currentScreen->setLineProperty( LINE_DOUBLEHEIGHT_BOTTOM , true );
1453                                     break;
1454     case token_esc_de('5'      ) : /* Single width, single height line*/
1455                                 _currentScreen->setLineProperty( LINE_DOUBLEWIDTH , false);
1456                                 _currentScreen->setLineProperty( LINE_DOUBLEHEIGHT_TOP , false);
1457                                 _currentScreen->setLineProperty( LINE_DOUBLEHEIGHT_BOTTOM , false);
1458                                 break;
1459     case token_esc_de('6'      ) : /* Double width, single height line*/
1460                                 _currentScreen->setLineProperty( LINE_DOUBLEWIDTH , true);
1461                                 _currentScreen->setLineProperty( LINE_DOUBLEHEIGHT_TOP , false);
1462                                 _currentScreen->setLineProperty( LINE_DOUBLEHEIGHT_BOTTOM , false);
1463                                 break;
1464     case token_esc_de('8'      ) : _currentScreen->helpAlign            (          ); break;
1465 
1466     // resize = \e[8;<rows>;<cols>t
1467     case token_csi_ps('t',   8) : setImageSize( p /* rows */, q /* columns */ );
1468                                Q_EMIT imageResizeRequest(QSize(q, p)); // Note columns (x), rows (y) in QSize
1469                                break;
1470 
1471     case token_csi_ps('t',   14) : reportPixelSize();          break;
1472     case token_csi_ps('t',   16) : reportCellSize();           break;
1473     case token_csi_ps('t',   18) : reportSize();               break;
1474     // change tab text color : \e[28;<color>t  color: 0-16,777,215
1475     case token_csi_ps('t',   28) : /* IGNORED: konsole-specific KDE3-era extension, not implemented */ break;
1476 
1477     case token_csi_ps('t',  22) : /* IGNORED: Save icon and window title on stack */      break; //XTERM
1478     case token_csi_ps('t',  23) : /* IGNORED: Restore icon and window title from stack */ break; //XTERM
1479 
1480     case token_csi_ps('K',   0) : _currentScreen->clearToEndOfLine     (          ); break;
1481     case token_csi_ps('K',   1) : _currentScreen->clearToBeginOfLine   (          ); break;
1482     case token_csi_ps('K',   2) : _currentScreen->clearEntireLine      (          ); break;
1483     case token_csi_ps('J',   0) : _currentScreen->clearToEndOfScreen   (          ); break;
1484     case token_csi_ps('J',   1) : _currentScreen->clearToBeginOfScreen (          ); break;
1485     case token_csi_ps('J',   2) : _currentScreen->clearEntireScreen    (          ); break;
1486     case token_csi_ps('J',      3) : clearHistory();                            break;
1487     case token_csi_ps('g',   0) : _currentScreen->changeTabStop        (false     ); break; //VT100
1488     case token_csi_ps('g',   3) : _currentScreen->clearTabStops        (          ); break; //VT100
1489     case token_csi_ps('h',   4) : _currentScreen->    setMode      (MODE_Insert   ); break;
1490     case token_csi_ps('h',  20) :          setMode      (MODE_NewLine  ); break;
1491     case token_csi_ps('i',   0) : /* IGNORE: attached printer          */ break; //VT100
1492     case token_csi_ps('l',   4) : _currentScreen->  resetMode      (MODE_Insert   ); break;
1493     case token_csi_ps('l',  20) :        resetMode      (MODE_NewLine  ); break;
1494     case token_csi_ps('s',   0) :      saveCursor           (          ); break;
1495     case token_csi_ps('u',   0) :      restoreCursor        (          ); break;
1496 
1497     case token_csi_ps('m',   0) : _currentScreen->setDefaultRendition  (          ); break;
1498     case token_csi_ps('m',   1) : _currentScreen->  setRendition     (RE_BOLD     ); break; //VT100
1499     case token_csi_ps('m',   2) : _currentScreen->  setRendition     (RE_FAINT    ); break;
1500     case token_csi_ps('m',   3) : _currentScreen->  setRendition     (RE_ITALIC   ); break; //VT100
1501     case token_csi_ps('m',   4) :
1502         if (q == 1) {
1503             _currentScreen->setUnderlineType(p);
1504         } else {
1505             _currentScreen->setUnderlineType(RE_UNDERLINE);
1506         }
1507         break; //VT100
1508     case token_csi_ps('m',   5) : _currentScreen->  setRendition     (RE_BLINK    ); break; //VT100
1509     case token_csi_ps('m',   7) : _currentScreen->  setRendition     (RE_REVERSE  ); break;
1510     case token_csi_ps('m',   8) : _currentScreen->  setRendition     (RE_CONCEAL  ); break;
1511     case token_csi_ps('m',   9) : _currentScreen->  setRendition     (RE_STRIKEOUT); break;
1512     case token_csi_ps('m',  53) : _currentScreen->  setRendition     (RE_OVERLINE ); break;
1513     case token_csi_ps('m',  10) : /* IGNORED: mapping related          */ break; //LINUX
1514     case token_csi_ps('m',  11) : /* IGNORED: mapping related          */ break; //LINUX
1515     case token_csi_ps('m',  12) : /* IGNORED: mapping related          */ break; //LINUX
1516     case token_csi_ps('m',  21) : _currentScreen->setUnderlineType(RE_UNDERLINE_DOUBLE); break;
1517     case token_csi_ps('m',  22) : _currentScreen->resetRendition     (RE_BOLD     );
1518                                _currentScreen->resetRendition     (RE_FAINT    ); break;
1519     case token_csi_ps('m',  23) : _currentScreen->resetRendition     (RE_ITALIC   ); break; //VT100
1520     case token_csi_ps('m',  24) : _currentScreen->resetRendition     (RE_UNDERLINE_MASK); break;
1521     case token_csi_ps('m',  25) : _currentScreen->resetRendition     (RE_BLINK    ); break;
1522     case token_csi_ps('m',  27) : _currentScreen->resetRendition     (RE_REVERSE  ); break;
1523     case token_csi_ps('m',  28) : _currentScreen->resetRendition     (RE_CONCEAL  ); break;
1524     case token_csi_ps('m',  29) : _currentScreen->resetRendition     (RE_STRIKEOUT); break;
1525     case token_csi_ps('m',  55) : _currentScreen->resetRendition     (RE_OVERLINE ); break;
1526 
1527     case token_csi_ps('m',   30) : _currentScreen->setForeColor         (COLOR_SPACE_SYSTEM,  0); break;
1528     case token_csi_ps('m',   31) : _currentScreen->setForeColor         (COLOR_SPACE_SYSTEM,  1); break;
1529     case token_csi_ps('m',   32) : _currentScreen->setForeColor         (COLOR_SPACE_SYSTEM,  2); break;
1530     case token_csi_ps('m',   33) : _currentScreen->setForeColor         (COLOR_SPACE_SYSTEM,  3); break;
1531     case token_csi_ps('m',   34) : _currentScreen->setForeColor         (COLOR_SPACE_SYSTEM,  4); break;
1532     case token_csi_ps('m',   35) : _currentScreen->setForeColor         (COLOR_SPACE_SYSTEM,  5); break;
1533     case token_csi_ps('m',   36) : _currentScreen->setForeColor         (COLOR_SPACE_SYSTEM,  6); break;
1534     case token_csi_ps('m',   37) : _currentScreen->setForeColor         (COLOR_SPACE_SYSTEM,  7); break;
1535 
1536     case token_csi_ps('m',   38) : _currentScreen->setForeColor         (p,       q); break;
1537 
1538     case token_csi_ps('m',   39) : _currentScreen->setForeColor         (COLOR_SPACE_DEFAULT,  0); break;
1539 
1540     case token_csi_ps('m',   40) : _currentScreen->setBackColor         (COLOR_SPACE_SYSTEM,  0); break;
1541     case token_csi_ps('m',   41) : _currentScreen->setBackColor         (COLOR_SPACE_SYSTEM,  1); break;
1542     case token_csi_ps('m',   42) : _currentScreen->setBackColor         (COLOR_SPACE_SYSTEM,  2); break;
1543     case token_csi_ps('m',   43) : _currentScreen->setBackColor         (COLOR_SPACE_SYSTEM,  3); break;
1544     case token_csi_ps('m',   44) : _currentScreen->setBackColor         (COLOR_SPACE_SYSTEM,  4); break;
1545     case token_csi_ps('m',   45) : _currentScreen->setBackColor         (COLOR_SPACE_SYSTEM,  5); break;
1546     case token_csi_ps('m',   46) : _currentScreen->setBackColor         (COLOR_SPACE_SYSTEM,  6); break;
1547     case token_csi_ps('m',   47) : _currentScreen->setBackColor         (COLOR_SPACE_SYSTEM,  7); break;
1548 
1549     case token_csi_ps('m',   48) : _currentScreen->setBackColor         (p,       q); break;
1550 
1551     case token_csi_ps('m',   49) : _currentScreen->setBackColor         (COLOR_SPACE_DEFAULT,  1); break;
1552 
1553     case token_csi_ps('m',   58) : _currentScreen->setULColor           (p,       q); break;
1554 
1555     case token_csi_ps('m',   59) : _currentScreen->setULColor           (COLOR_SPACE_UNDEFINED,  0); break;
1556 
1557     case token_csi_ps('m',   90) : _currentScreen->setForeColor         (COLOR_SPACE_SYSTEM,  8); break;
1558     case token_csi_ps('m',   91) : _currentScreen->setForeColor         (COLOR_SPACE_SYSTEM,  9); break;
1559     case token_csi_ps('m',   92) : _currentScreen->setForeColor         (COLOR_SPACE_SYSTEM, 10); break;
1560     case token_csi_ps('m',   93) : _currentScreen->setForeColor         (COLOR_SPACE_SYSTEM, 11); break;
1561     case token_csi_ps('m',   94) : _currentScreen->setForeColor         (COLOR_SPACE_SYSTEM, 12); break;
1562     case token_csi_ps('m',   95) : _currentScreen->setForeColor         (COLOR_SPACE_SYSTEM, 13); break;
1563     case token_csi_ps('m',   96) : _currentScreen->setForeColor         (COLOR_SPACE_SYSTEM, 14); break;
1564     case token_csi_ps('m',   97) : _currentScreen->setForeColor         (COLOR_SPACE_SYSTEM, 15); break;
1565 
1566     case token_csi_ps('m',  100) : _currentScreen->setBackColor         (COLOR_SPACE_SYSTEM,  8); break;
1567     case token_csi_ps('m',  101) : _currentScreen->setBackColor         (COLOR_SPACE_SYSTEM,  9); break;
1568     case token_csi_ps('m',  102) : _currentScreen->setBackColor         (COLOR_SPACE_SYSTEM, 10); break;
1569     case token_csi_ps('m',  103) : _currentScreen->setBackColor         (COLOR_SPACE_SYSTEM, 11); break;
1570     case token_csi_ps('m',  104) : _currentScreen->setBackColor         (COLOR_SPACE_SYSTEM, 12); break;
1571     case token_csi_ps('m',  105) : _currentScreen->setBackColor         (COLOR_SPACE_SYSTEM, 13); break;
1572     case token_csi_ps('m',  106) : _currentScreen->setBackColor         (COLOR_SPACE_SYSTEM, 14); break;
1573     case token_csi_ps('m',  107) : _currentScreen->setBackColor         (COLOR_SPACE_SYSTEM, 15); break;
1574 
1575     case token_csi_ps('n',   5) :      reportStatus         (          ); break;
1576     case token_csi_ps('n',   6) :      reportCursorPosition (          ); break;
1577     case token_csi_ps('q',   0) : /* IGNORED: LEDs off                 */ break; //VT100
1578     case token_csi_ps('q',   1) : /* IGNORED: LED1 on                  */ break; //VT100
1579     case token_csi_ps('q',   2) : /* IGNORED: LED2 on                  */ break; //VT100
1580     case token_csi_ps('q',   3) : /* IGNORED: LED3 on                  */ break; //VT100
1581     case token_csi_ps('q',   4) : /* IGNORED: LED4 on                  */ break; //VT100
1582     case token_csi_ps('x',   0) :      reportTerminalParms  (         2); break; //VT100
1583     case token_csi_ps('x',   1) :      reportTerminalParms  (         3); break; //VT100
1584 
1585     case token_csi_pn('@'      ) : _currentScreen->insertChars          (p         ); break;
1586     case token_csi_pn('A'      ) : _currentScreen->cursorUp             (p         ); break; //VT100
1587     case token_csi_pn('B'      ) : _currentScreen->cursorDown           (p         ); break; //VT100
1588     case token_csi_pn('C'      ) : _currentScreen->cursorRight          (p         ); break; //VT100
1589     case token_csi_pn('D'      ) : _currentScreen->cursorLeft           (p         ); break; //VT100
1590     case token_csi_pn('E'      ) : _currentScreen->cursorNextLine       (p         ); break; //VT100
1591     case token_csi_pn('F'      ) : _currentScreen->cursorPreviousLine   (p         ); break; //VT100
1592     case token_csi_pn('G'      ) : _currentScreen->setCursorX           (p         ); break; //LINUX
1593     case token_csi_pn('H'      ) : _currentScreen->setCursorYX          (p,      q); break; //VT100
1594     case token_csi_pn('I'      ) : _currentScreen->tab                  (p         ); break;
1595     case token_csi_pn('L'      ) : _currentScreen->insertLines          (p         ); break;
1596     case token_csi_pn('M'      ) : _currentScreen->deleteLines          (p         ); break;
1597     case token_csi_pn('P'      ) : _currentScreen->deleteChars          (p         ); break;
1598     case token_csi_pn('S'      ) : _currentScreen->scrollUp             (p         ); break;
1599     case token_csi_pn('T'      ) : _currentScreen->scrollDown           (p         ); break;
1600     case token_csi_pn('X'      ) : _currentScreen->eraseChars           (p         ); break;
1601     case token_csi_pn('Z'      ) : _currentScreen->backtab              (p         ); break;
1602     case token_csi_pn('b'      ) : _currentScreen->repeatChars          (p         ); break;
1603     case token_csi_pn('c'      ) :      reportTerminalType   (          ); break; //VT100
1604     case token_csi_pn('d'      ) : _currentScreen->setCursorY           (p         ); break; //LINUX
1605     case token_csi_pn('f'      ) : _currentScreen->setCursorYX          (p,      q); break; //VT100
1606     case token_csi_pn('r'      ) :      setMargins           (p,      q); break; //VT100
1607     case token_csi_pn('y'      ) : /* IGNORED: Confidence test          */ break; //VT100
1608 
1609     case token_csi_pr('h',   1) :          setMode      (MODE_AppCuKeys); break; //VT100
1610     case token_csi_pr('l',   1) :        resetMode      (MODE_AppCuKeys); break; //VT100
1611     case token_csi_pr('s',   1) :         saveMode      (MODE_AppCuKeys); break; //FIXME
1612     case token_csi_pr('r',   1) :      restoreMode      (MODE_AppCuKeys); break; //FIXME
1613 
1614     case token_csi_pr('l',   2) :        resetMode      (MODE_Ansi     ); break; //VT100
1615 
1616     case token_csi_pr('h',   3) :          setMode      (MODE_132Columns); break; //VT100
1617     case token_csi_pr('l',   3) :        resetMode      (MODE_132Columns); break; //VT100
1618 
1619     case token_csi_pr('h',   4) : /* IGNORED: soft scrolling           */ break; //VT100
1620     case token_csi_pr('l',   4) : /* IGNORED: soft scrolling           */ break; //VT100
1621 
1622     case token_csi_pr('h',   5) : _currentScreen->    setMode      (MODE_Screen   ); break; //VT100
1623     case token_csi_pr('l',   5) : _currentScreen->  resetMode      (MODE_Screen   ); break; //VT100
1624 
1625     case token_csi_pr('h',   6) : _currentScreen->    setMode      (MODE_Origin   ); break; //VT100
1626     case token_csi_pr('l',   6) : _currentScreen->  resetMode      (MODE_Origin   ); break; //VT100
1627     case token_csi_pr('s',   6) : _currentScreen->   saveMode      (MODE_Origin   ); break; //FIXME
1628     case token_csi_pr('r',   6) : _currentScreen->restoreMode      (MODE_Origin   ); break; //FIXME
1629 
1630     case token_csi_pr('h',   7) : _currentScreen->    setMode      (MODE_Wrap     ); break; //VT100
1631     case token_csi_pr('l',   7) : _currentScreen->  resetMode      (MODE_Wrap     ); break; //VT100
1632     case token_csi_pr('s',   7) : _currentScreen->   saveMode      (MODE_Wrap     ); break; //FIXME
1633     case token_csi_pr('r',   7) : _currentScreen->restoreMode      (MODE_Wrap     ); break; //FIXME
1634 
1635     case token_csi_pr('h',   8) : /* IGNORED: autorepeat on            */ break; //VT100
1636     case token_csi_pr('l',   8) : /* IGNORED: autorepeat off           */ break; //VT100
1637     case token_csi_pr('s',   8) : /* IGNORED: autorepeat on            */ break; //VT100
1638     case token_csi_pr('r',   8) : /* IGNORED: autorepeat off           */ break; //VT100
1639 
1640     case token_csi_pr('h',   9) : /* IGNORED: interlace                */ break; //VT100
1641     case token_csi_pr('l',   9) : /* IGNORED: interlace                */ break; //VT100
1642     case token_csi_pr('s',   9) : /* IGNORED: interlace                */ break; //VT100
1643     case token_csi_pr('r',   9) : /* IGNORED: interlace                */ break; //VT100
1644 
1645     case token_csi_pr('h',  12) : /* IGNORED: Cursor blink             */ break; //att610
1646     case token_csi_pr('l',  12) : /* IGNORED: Cursor blink             */ break; //att610
1647     case token_csi_pr('s',  12) : /* IGNORED: Cursor blink             */ break; //att610
1648     case token_csi_pr('r',  12) : /* IGNORED: Cursor blink             */ break; //att610
1649 
1650     case token_csi_pr('h',  25) :          setMode      (MODE_Cursor   ); break; //VT100
1651     case token_csi_pr('l',  25) :        resetMode      (MODE_Cursor   ); break; //VT100
1652     case token_csi_pr('s',  25) :         saveMode      (MODE_Cursor   ); break; //VT100
1653     case token_csi_pr('r',  25) :      restoreMode      (MODE_Cursor   ); break; //VT100
1654 
1655     case token_csi_pr('h',  40) :         setMode(MODE_Allow132Columns ); break; // XTERM
1656     case token_csi_pr('l',  40) :       resetMode(MODE_Allow132Columns ); break; // XTERM
1657 
1658     case token_csi_pr('h',  41) : /* IGNORED: obsolete more(1) fix     */ break; //XTERM
1659     case token_csi_pr('l',  41) : /* IGNORED: obsolete more(1) fix     */ break; //XTERM
1660     case token_csi_pr('s',  41) : /* IGNORED: obsolete more(1) fix     */ break; //XTERM
1661     case token_csi_pr('r',  41) : /* IGNORED: obsolete more(1) fix     */ break; //XTERM
1662 
1663     case token_csi_pr('h',  47) :          setMode      (MODE_AppScreen); break; //VT100
1664     case token_csi_pr('l',  47) :        resetMode      (MODE_AppScreen); break; //VT100
1665     case token_csi_pr('s',  47) :         saveMode      (MODE_AppScreen); break; //XTERM
1666     case token_csi_pr('r',  47) :      restoreMode      (MODE_AppScreen); break; //XTERM
1667 
1668     case token_csi_pr('h',  67) : /* IGNORED: DECBKM                   */ break; //XTERM
1669     case token_csi_pr('l',  67) : /* IGNORED: DECBKM                   */ break; //XTERM
1670     case token_csi_pr('s',  67) : /* IGNORED: DECBKM                   */ break; //XTERM
1671     case token_csi_pr('r',  67) : /* IGNORED: DECBKM                   */ break; //XTERM
1672 
1673     case token_csi_pr('h',  80) : m_SixelScrolling = false;               break;
1674     case token_csi_pr('l',  80) : m_SixelScrolling = true;                break;
1675 
1676     // XTerm defines the following modes:
1677     // SET_VT200_MOUSE             1000
1678     // SET_VT200_HIGHLIGHT_MOUSE   1001
1679     // SET_BTN_EVENT_MOUSE         1002
1680     // SET_ANY_EVENT_MOUSE         1003
1681     //
1682 
1683     //Note about mouse modes:
1684     //There are four mouse modes which xterm-compatible terminals can support - 1000,1001,1002,1003
1685     //Konsole currently supports mode 1000 (basic mouse press and release),  mode 1002 (dragging the mouse)
1686     //and mode 1003 (moving the mouse).
1687     //TODO:  Implementation of mouse mode 1001 (something called highlight tracking).
1688     //
1689 
1690     case token_csi_pr('h', 1000) :          setMode      (MODE_Mouse1000); break; //XTERM
1691     case token_csi_pr('l', 1000) :        resetMode      (MODE_Mouse1000); break; //XTERM
1692     case token_csi_pr('s', 1000) :         saveMode      (MODE_Mouse1000); break; //XTERM
1693     case token_csi_pr('r', 1000) :      restoreMode      (MODE_Mouse1000); break; //XTERM
1694 
1695     case token_csi_pr('h', 1001) : /* IGNORED: hilite mouse tracking    */ break; //XTERM
1696     case token_csi_pr('l', 1001) :        resetMode      (MODE_Mouse1001); break; //XTERM
1697     case token_csi_pr('s', 1001) : /* IGNORED: hilite mouse tracking    */ break; //XTERM
1698     case token_csi_pr('r', 1001) : /* IGNORED: hilite mouse tracking    */ break; //XTERM
1699 
1700     case token_csi_pr('h', 1002) :          setMode      (MODE_Mouse1002); break; //XTERM
1701     case token_csi_pr('l', 1002) :        resetMode      (MODE_Mouse1002); break; //XTERM
1702     case token_csi_pr('s', 1002) :         saveMode      (MODE_Mouse1002); break; //XTERM
1703     case token_csi_pr('r', 1002) :      restoreMode      (MODE_Mouse1002); break; //XTERM
1704 
1705     case token_csi_pr('h', 1003) :          setMode      (MODE_Mouse1003); break; //XTERM
1706     case token_csi_pr('l', 1003) :        resetMode      (MODE_Mouse1003); break; //XTERM
1707     case token_csi_pr('s', 1003) :         saveMode      (MODE_Mouse1003); break; //XTERM
1708     case token_csi_pr('r', 1003) :      restoreMode      (MODE_Mouse1003); break; //XTERM
1709 
1710     case token_csi_pr('h',  1004) : _reportFocusEvents = true; break;
1711     case token_csi_pr('l',  1004) : _reportFocusEvents = false; break;
1712 
1713     case token_csi_pr('h', 1005) :          setMode      (MODE_Mouse1005); break; //XTERM
1714     case token_csi_pr('l', 1005) :        resetMode      (MODE_Mouse1005); break; //XTERM
1715     case token_csi_pr('s', 1005) :         saveMode      (MODE_Mouse1005); break; //XTERM
1716     case token_csi_pr('r', 1005) :      restoreMode      (MODE_Mouse1005); break; //XTERM
1717 
1718     case token_csi_pr('h', 1006) :          setMode      (MODE_Mouse1006); break; //XTERM
1719     case token_csi_pr('l', 1006) :        resetMode      (MODE_Mouse1006); break; //XTERM
1720     case token_csi_pr('s', 1006) :         saveMode      (MODE_Mouse1006); break; //XTERM
1721     case token_csi_pr('r', 1006) :      restoreMode      (MODE_Mouse1006); break; //XTERM
1722 
1723     case token_csi_pr('h', 1007) :          setMode      (MODE_Mouse1007); break; //XTERM
1724     case token_csi_pr('l', 1007) :        resetMode      (MODE_Mouse1007); break; //XTERM
1725     case token_csi_pr('s', 1007) :         saveMode      (MODE_Mouse1007); break; //XTERM
1726     case token_csi_pr('r', 1007) :      restoreMode      (MODE_Mouse1007); break; //XTERM
1727 
1728     case token_csi_pr('h', 1015) :          setMode      (MODE_Mouse1015); break; //URXVT
1729     case token_csi_pr('l', 1015) :        resetMode      (MODE_Mouse1015); break; //URXVT
1730     case token_csi_pr('s', 1015) :         saveMode      (MODE_Mouse1015); break; //URXVT
1731     case token_csi_pr('r', 1015) :      restoreMode      (MODE_Mouse1015); break; //URXVT
1732 
1733     case token_csi_pr('h', 1034) : /* IGNORED: 8bitinput activation     */ break; //XTERM
1734 
1735     case token_csi_pr('h', 1047) :          setMode      (MODE_AppScreen); break; //XTERM
1736     case token_csi_pr('l', 1047) : _screen[1]->clearEntireScreen(); resetMode(MODE_AppScreen); break; //XTERM
1737     case token_csi_pr('s', 1047) :         saveMode      (MODE_AppScreen); break; //XTERM
1738     case token_csi_pr('r', 1047) :      restoreMode      (MODE_AppScreen); break; //XTERM
1739 
1740     //FIXME: Unitoken: save translations
1741     case token_csi_pr('h', 1048) :      saveCursor           (          ); break; //XTERM
1742     case token_csi_pr('l', 1048) :      restoreCursor        (          ); break; //XTERM
1743     case token_csi_pr('s', 1048) :      saveCursor           (          ); break; //XTERM
1744     case token_csi_pr('r', 1048) :      restoreCursor        (          ); break; //XTERM
1745 
1746     //FIXME: every once new sequences like this pop up in xterm.
1747     //       Here's a guess of what they could mean.
1748     case token_csi_pr('h', 1049) : saveCursor(); _screen[1]->clearEntireScreen(); setMode(MODE_AppScreen); break; //XTERM
1749     case token_csi_pr('l', 1049) : resetMode(MODE_AppScreen); restoreCursor(); break; //XTERM
1750 
1751     case token_csi_pr('h', 2004) :          setMode      (MODE_BracketedPaste); break; //XTERM
1752     case token_csi_pr('l', 2004) :        resetMode      (MODE_BracketedPaste); break; //XTERM
1753     case token_csi_pr('s', 2004) :         saveMode      (MODE_BracketedPaste); break; //XTERM
1754     case token_csi_pr('r', 2004) :      restoreMode      (MODE_BracketedPaste); break; //XTERM
1755 
1756     case token_csi_pr('S',    1) : if(!p) sixelQuery        (1          ); break;
1757     case token_csi_pr('S',    2) : if(!p) sixelQuery        (2          ); break;
1758     // Set Cursor Style (DECSCUSR), VT520, with the extra xterm sequences
1759     // the first one is a special case, 'ESC[ q', which mimics 'ESC[1 q'
1760     // Using 0 to reset to default is matching VTE, but not any official standard.
1761     case token_csi_sp ('q'    ) : Q_EMIT setCursorStyleRequest(Enum::BlockCursor,     true);  break;
1762     case token_csi_psp('q',  0) : Q_EMIT resetCursorStyleRequest();                           break;
1763     case token_csi_psp('q',  1) : Q_EMIT setCursorStyleRequest(Enum::BlockCursor,     true);  break;
1764     case token_csi_psp('q',  2) : Q_EMIT setCursorStyleRequest(Enum::BlockCursor,     false); break;
1765     case token_csi_psp('q',  3) : Q_EMIT setCursorStyleRequest(Enum::UnderlineCursor, true);  break;
1766     case token_csi_psp('q',  4) : Q_EMIT setCursorStyleRequest(Enum::UnderlineCursor, false); break;
1767     case token_csi_psp('q',  5) : Q_EMIT setCursorStyleRequest(Enum::IBeamCursor,     true);  break;
1768     case token_csi_psp('q',  6) : Q_EMIT setCursorStyleRequest(Enum::IBeamCursor,     false); break;
1769 
1770     // DECSTR (Soft Terminal Reset)
1771     case token_csi_pe('p'      ) : reset(true); break; //VT220
1772 
1773     case token_csi_pq('c'      ) :  reportTertiaryAttributes(          ); break; //VT420
1774     case token_csi_pg('c'      ) :  reportSecondaryAttributes(          ); break; //VT100
1775     case token_csi_pg('q'      ) :  reportVersion(          ); break;
1776 
1777     //FIXME: when changing between vt52 and ansi mode evtl do some resetting.
1778     case token_vt52('A'      ) : _currentScreen->cursorUp             (         1); break; //VT52
1779     case token_vt52('B'      ) : _currentScreen->cursorDown           (         1); break; //VT52
1780     case token_vt52('C'      ) : _currentScreen->cursorRight          (         1); break; //VT52
1781     case token_vt52('D'      ) : _currentScreen->cursorLeft           (         1); break; //VT52
1782 
1783     case token_vt52('F'      ) :      setAndUseCharset     (0,    '0'); break; //VT52
1784     case token_vt52('G'      ) :      setAndUseCharset     (0,    'B'); break; //VT52
1785 
1786     case token_vt52('H'      ) : _currentScreen->setCursorYX          (1,1       ); break; //VT52
1787     case token_vt52('I'      ) : _currentScreen->reverseIndex         (          ); break; //VT52
1788     case token_vt52('J'      ) : _currentScreen->clearToEndOfScreen   (          ); break; //VT52
1789     case token_vt52('K'      ) : _currentScreen->clearToEndOfLine     (          ); break; //VT52
1790     case token_vt52('Y'      ) : _currentScreen->setCursorYX          (p-31,q-31 ); break; //VT52
1791     case token_vt52('Z'      ) :      reportTerminalType   (           ); break; //VT52
1792     case token_vt52('<'      ) :          setMode      (MODE_Ansi     ); break; //VT52
1793     case token_vt52('='      ) :          setMode      (MODE_AppKeyPad); break; //VT52
1794     case token_vt52('>'      ) :        resetMode      (MODE_AppKeyPad); break; //VT52
1795 
1796     default:
1797         reportDecodingError(token);
1798         break;
1799     }
1800     /* clang-format on */
1801 }
1802 
1803 void delete_func(void *p)
1804 {
1805     delete (QByteArray *)p;
1806 }
1807 
1808 void Vt102Emulation::processGraphicsToken(int tokenSize)
1809 {
1810     QString value = QString::fromUcs4(&tokenBuffer[1], tokenSize - 1);
1811     QStringList list;
1812     QPixmap pixmap;
1813 
1814     int dataPos = value.indexOf(QLatin1Char(';'));
1815     if (dataPos == -1) {
1816         dataPos = value.size();
1817     }
1818     if (dataPos > 1024) {
1819         reportDecodingError(token_apc('G'));
1820         return;
1821     }
1822     list = value.mid(0, dataPos).split(QLatin1Char(','));
1823 
1824     QMap<char, qint64> keys; // Keys may be signed or unsigned 32 bit integers
1825 
1826     if (savedKeys.empty()) {
1827         keys['a'] = 't';
1828         keys['t'] = 'd';
1829         keys['q'] = 0;
1830         keys['m'] = 0;
1831         keys['f'] = 32;
1832         keys['i'] = 0;
1833         keys['o'] = 0;
1834         keys['X'] = 0;
1835         keys['Y'] = 0;
1836         keys['x'] = 0;
1837         keys['y'] = 0;
1838         keys['z'] = 0;
1839         keys['C'] = 0;
1840         keys['c'] = 0;
1841         keys['r'] = 0;
1842         keys['A'] = 255;
1843         keys['I'] = 0;
1844         keys['d'] = 'a';
1845         keys['p'] = -1;
1846     } else {
1847         keys = QMap<char, qint64>(savedKeys);
1848     }
1849 
1850     for (int i = 0; i < list.size(); i++) {
1851         if (list.at(i).at(1).toLatin1() != '=') {
1852             reportDecodingError(token_apc('G'));
1853             return;
1854         }
1855         if (list.at(i).at(2).isNumber() || list.at(i).at(2).toLatin1() == '-') {
1856             keys[list.at(i).at(0).toLatin1()] = QStringView(list.at(i)).mid(2).toInt();
1857         } else {
1858             keys[list.at(i).at(0).toLatin1()] = list.at(i).at(2).toLatin1();
1859         }
1860     }
1861 
1862     if (keys['a'] == 't' || keys['a'] == 'T' || keys['a'] == 'q') {
1863         if (keys['q'] < 2 && keys['t'] != 'd') {
1864             QString params = QStringLiteral("i=") + QString::number(keys['i']);
1865             QString error = QStringLiteral("ENOTSUPPORTED:");
1866             sendGraphicsReply(params, error);
1867             return;
1868         }
1869         if (keys['I']) {
1870             keys['i'] = getFreeGraphicsImageId();
1871         }
1872         if (imageId != keys['i']) {
1873             imageId = keys['i'];
1874             imageData.clear();
1875         }
1876         imageData.append(tokenData);
1877         tokenData.clear();
1878         imageData.append(QByteArray::fromBase64(value.mid(dataPos + 1).toLocal8Bit()));
1879         if (keys['m'] == 0) {
1880             imageId = 0;
1881             savedKeys = QMap<char, qint64>();
1882             QByteArray out;
1883 
1884             uint32_t byteCount = 0;
1885             if (keys['f'] == 24 || keys['f'] == 32) {
1886                 int bpp = keys['f'] / 8;
1887                 byteCount = bpp * keys['s'] * keys['v'];
1888             } else {
1889                 byteCount = 8 * 1024 * 1024;
1890             }
1891 
1892             if (keys['o'] == 'z') {
1893                 char header[sizeof byteCount];
1894                 qToBigEndian(byteCount, header);
1895                 imageData.prepend(header, sizeof header);
1896                 out = qUncompress(imageData);
1897 
1898                 if (keys['f'] != 24 && keys['f'] != 32) {
1899                     imageData = out;
1900                 }
1901             }
1902             if (out.isEmpty()) {
1903                 out = imageData;
1904             }
1905 
1906             if (keys['f'] == 24 || keys['f'] == 32) {
1907                 if (unsigned(out.size()) < byteCount) {
1908                     qCWarning(KonsoleDebug) << "Not enough image data" << out.size() << "require" << byteCount;
1909                     imageData.clear();
1910                     return;
1911                 }
1912                 QImage::Format format = keys['f'] == 24 ? QImage::Format_RGB888 : QImage::Format_RGBA8888;
1913                 pixmap = QPixmap::fromImage(QImage((const uchar *)out.constData(), 0 + keys['s'], 0 + keys['v'], 0 + keys['s'] * keys['f'] / 8, format));
1914                 pixmap.detach();
1915             } else {
1916                 pixmap.loadFromData(out);
1917             }
1918 
1919             if (keys['a'] == 'q') {
1920                 QString params = QStringLiteral("i=") + QString::number(keys['i']);
1921                 sendGraphicsReply(params, QString());
1922             } else {
1923                 if (keys['i']) {
1924                     _graphicsImages[keys['i']] = pixmap;
1925                 }
1926                 if (keys['q'] == 0 && keys['a'] == 't') {
1927                     QString params = QStringLiteral("i=") + QString::number(keys['i']);
1928                     if (keys['I']) {
1929                         params = params + QStringLiteral(",I=") + QString::number(keys['I']);
1930                     }
1931                     sendGraphicsReply(params, QString());
1932                 }
1933             }
1934             imageData.clear();
1935         } else {
1936             if (savedKeys.empty()) {
1937                 savedKeys = QMap<char, qint64>(keys);
1938                 savedKeys.remove('m');
1939             }
1940         }
1941     }
1942     if (keys['a'] == 'p' || (keys['a'] == 'T' && keys['m'] == 0)) {
1943         if (keys['a'] == 'p') {
1944             pixmap = _graphicsImages[keys['i']];
1945         }
1946         if (!pixmap.isNull()) {
1947             if (keys['x'] || keys['y'] || keys['w'] || keys['h']) {
1948                 int w = keys['w'] ? keys['w'] : pixmap.width() - keys['x'];
1949                 int h = keys['h'] ? keys['h'] : pixmap.height() - keys['y'];
1950                 pixmap = pixmap.copy(keys['x'], keys['y'], w, h);
1951             }
1952             if (keys['c'] && keys['r']) {
1953                 pixmap = pixmap.scaled(keys['c'] * _currentScreen->currentTerminalDisplay()->terminalFont()->fontWidth(),
1954                                        keys['r'] * _currentScreen->currentTerminalDisplay()->terminalFont()->fontHeight());
1955             }
1956             int rows = -1, cols = -1;
1957             _currentScreen->addPlacement(pixmap,
1958                                          rows,
1959                                          cols,
1960                                          -1,
1961                                          -1,
1962                                          true,
1963                                          keys['C'] == 0,
1964                                          true,
1965                                          keys['z'],
1966                                          keys['i'],
1967                                          keys['p'],
1968                                          keys['A'] / 255.0,
1969                                          keys['X'],
1970                                          keys['Y']);
1971             if (keys['q'] == 0 && keys['i']) {
1972                 QString params = QStringLiteral("i=") + QString::number(keys['i']);
1973                 if (keys['I']) {
1974                     params = params + QStringLiteral(",I=") + QString::number(keys['I']);
1975                 }
1976                 if (keys['p'] >= 0) {
1977                     params = params + QStringLiteral(",p=") + QString::number(keys['p']);
1978                 }
1979                 sendGraphicsReply(params, QString());
1980             }
1981         } else {
1982             if (keys['q'] < 2) {
1983                 QString params = QStringLiteral("i=") + QString::number(keys['i']);
1984                 sendGraphicsReply(params, QStringLiteral("ENOENT:No such image"));
1985             }
1986         }
1987     }
1988     if (keys['a'] == 'd') {
1989         int action = keys['d'] | 0x20;
1990         int id = keys['i'];
1991         int pid = keys['p'];
1992         int x = keys['x'];
1993         int y = keys['y'];
1994         if (action == 'n') {
1995         } else if (action == 'c') {
1996             action = 'p';
1997             x = _currentScreen->getCursorX();
1998             y = _currentScreen->getCursorY();
1999         }
2000         _currentScreen->delPlacements(action, id, pid, x, y, keys['z']);
2001     }
2002 }
2003 
2004 void Vt102Emulation::clearScreenAndSetColumns(int columnCount)
2005 {
2006     setImageSize(_currentScreen->getLines(), columnCount);
2007     clearEntireScreen();
2008     setDefaultMargins();
2009     _currentScreen->setCursorYX(0, 0);
2010 }
2011 
2012 void Vt102Emulation::sendString(const QByteArray &s)
2013 {
2014     Q_EMIT sendData(s);
2015 }
2016 
2017 void Vt102Emulation::sendGraphicsReply(const QString &params, const QString &error)
2018 {
2019     sendString(
2020         (QStringLiteral("\033_G") + params + QStringLiteral(";") + (error.isEmpty() ? QStringLiteral("OK") : error) + QStringLiteral("\033\\")).toLatin1());
2021 }
2022 
2023 void Vt102Emulation::reportCursorPosition()
2024 {
2025     char tmp[30];
2026     int y = _currentScreen->getCursorY() + 1;
2027     int x = _currentScreen->getCursorX() + 1;
2028     if (_currentScreen->getMode(MODE_Origin)) {
2029         y -= _currentScreen->topMargin();
2030     }
2031     snprintf(tmp, sizeof(tmp), "\033[%d;%dR", y, x);
2032     sendString(tmp);
2033 }
2034 
2035 void Vt102Emulation::reportPixelSize()
2036 {
2037     char tmp[30];
2038     snprintf(tmp,
2039              sizeof(tmp),
2040              "\033[4;%d;%dt",
2041              _currentScreen->currentTerminalDisplay()->terminalFont()->fontHeight() * _currentScreen->getLines(),
2042              _currentScreen->currentTerminalDisplay()->terminalFont()->fontWidth() * _currentScreen->getColumns());
2043     sendString(tmp);
2044 }
2045 
2046 void Vt102Emulation::iTermReportCellSize()
2047 {
2048     char tmp[50];
2049     snprintf(tmp,
2050              sizeof(tmp),
2051              "\033]1337;ReportCellSize=%d.0;%d.0;1.0\007",
2052              _currentScreen->currentTerminalDisplay()->terminalFont()->fontHeight(),
2053              _currentScreen->currentTerminalDisplay()->terminalFont()->fontWidth());
2054     sendString(tmp);
2055 }
2056 
2057 void Vt102Emulation::reportCellSize()
2058 {
2059     char tmp[30];
2060     snprintf(tmp,
2061              sizeof(tmp),
2062              "\033[6;%d;%dt",
2063              _currentScreen->currentTerminalDisplay()->terminalFont()->fontHeight(),
2064              _currentScreen->currentTerminalDisplay()->terminalFont()->fontWidth());
2065     sendString(tmp);
2066 }
2067 
2068 void Vt102Emulation::reportColor(int c, QColor color)
2069 {
2070     char tmp[60];
2071     snprintf(tmp,
2072              sizeof(tmp),
2073              "\033]4;%i;rgb:%02x%02x/%02x%02x/%02x%02x\007",
2074              c,
2075              color.red(),
2076              color.red(),
2077              color.green(),
2078              color.green(),
2079              color.blue(),
2080              color.blue());
2081     sendString(tmp);
2082 }
2083 
2084 void Vt102Emulation::reportSize()
2085 {
2086     char tmp[30];
2087     snprintf(tmp, sizeof(tmp), "\033[8;%d;%dt", _currentScreen->getLines(), _currentScreen->getColumns());
2088     sendString(tmp);
2089 }
2090 
2091 void Vt102Emulation::reportTerminalType()
2092 {
2093     // Primary device attribute response (Request was: ^[[0c or ^[[c (from TT321 Users Guide))
2094     // VT220:  ^[[?63;1;2;3;6;7;8c   (list deps on emul. capabilities)
2095     // VT100:  ^[[?1;2c
2096     // VT101:  ^[[?1;0c
2097     // VT102:  ^[[?6v
2098     if (getMode(MODE_Ansi)) {
2099         sendString("\033[?62;1;4c"); // I'm a VT2xx with 132 columns and Sixel
2100     } else {
2101         sendString("\033/Z"); // I'm a VT52
2102     }
2103 }
2104 
2105 void Vt102Emulation::reportTertiaryAttributes()
2106 {
2107     // Tertiary device attribute response DECRPTUI (Request was: ^[[=0c or ^[[=c)
2108     // 7E4B4445 is hex for ASCII "~KDE"
2109     sendString("\033P!|7E4B4445\033\\");
2110 }
2111 
2112 void Vt102Emulation::reportSecondaryAttributes()
2113 {
2114     // Secondary device attribute response (Request was: ^[[>0c or ^[[>c)
2115     if (getMode(MODE_Ansi)) {
2116         sendString("\033[>1;115;0c"); // Why 115?  ;)
2117     } else {
2118         sendString("\033/Z"); // FIXME I don't think VT52 knows about it but kept for
2119     }
2120     // konsoles backward compatibility.
2121 }
2122 
2123 void Vt102Emulation::reportVersion()
2124 {
2125     sendString("\033P>|Konsole " KONSOLE_VERSION "\033\\");
2126 }
2127 
2128 /* DECREPTPARM – Report Terminal Parameters
2129     ESC [ <sol>; <par>; <nbits>; <xspeed>; <rspeed>; <clkmul>; <flags> x
2130 
2131     https://vt100.net/docs/vt100-ug/chapter3.html
2132 */
2133 void Vt102Emulation::reportTerminalParms(int p)
2134 {
2135     char tmp[100];
2136     /*
2137        sol=1: This message is a request; report in response to a request.
2138        par=1: No parity set
2139        nbits=1: 8 bits per character
2140        xspeed=112: 9600
2141        rspeed=112: 9600
2142        clkmul=1: The bit rate multiplier is 16.
2143        flags=0: None
2144     */
2145     snprintf(tmp, sizeof(tmp), "\033[%d;1;1;112;112;1;0x", p); // not really true.
2146     sendString(tmp);
2147 }
2148 
2149 void Vt102Emulation::reportStatus()
2150 {
2151     sendString("\033[0n"); // VT100. Device status report. 0 = Ready.
2152 }
2153 
2154 void Vt102Emulation::reportAnswerBack()
2155 {
2156     // FIXME - Test this with VTTEST
2157     // This is really obsolete VT100 stuff.
2158     const char *ANSWER_BACK = "";
2159     sendString(ANSWER_BACK);
2160 }
2161 
2162 /*!
2163     `cx',`cy' are 1-based.
2164     `cb' indicates the button pressed or released (0-2) or scroll event (64-65) plus key modifiers (Alt=8, Control=16).
2165 
2166     eventType represents the kind of mouse action that occurred:
2167         0 = Mouse button press
2168         1 = Mouse drag
2169         2 = Mouse button release
2170         3 = Mouse click to move cursor in input field
2171 */
2172 
2173 void Vt102Emulation::sendMouseEvent(int cb, int cx, int cy, int eventType)
2174 {
2175     if (cx < 1 || cy < 1) {
2176         return;
2177     }
2178 
2179     if (eventType == 3) {
2180         // We know we are in input mode
2181         TerminalDisplay *currentView = _currentScreen->currentTerminalDisplay();
2182         bool isReadOnly = false;
2183         if (currentView != nullptr) {
2184             isReadOnly = currentView->getReadOnly();
2185         }
2186         auto point = std::make_pair(cy, cx);
2187         if (!isReadOnly && _currentScreen->replModeStart() <= point && point <= _currentScreen->replModeEnd()) {
2188             KeyboardTranslator::States states = KeyboardTranslator::NoState;
2189 
2190             // get current states
2191             if (getMode(MODE_NewLine)) {
2192                 states |= KeyboardTranslator::NewLineState;
2193             }
2194             if (getMode(MODE_Ansi)) {
2195                 states |= KeyboardTranslator::AnsiState;
2196             }
2197             if (getMode(MODE_AppCuKeys)) {
2198                 states |= KeyboardTranslator::CursorKeysState;
2199             }
2200             if (getMode(MODE_AppScreen)) {
2201                 states |= KeyboardTranslator::AlternateScreenState;
2202             }
2203             KeyboardTranslator::Entry LRKeys[2] = {_keyTranslator->findEntry(Qt::Key_Left, Qt::NoModifier, states),
2204                                                    _keyTranslator->findEntry(Qt::Key_Right, Qt::NoModifier, states)};
2205             QVector<LineProperty> lineProperties = _currentScreen->getLineProperties(cy + _currentScreen->getHistLines(), cy + _currentScreen->getHistLines());
2206             cx = qMin(cx, (int)lineProperties[0].length);
2207             int cuX = _currentScreen->getCursorX();
2208             int cuY = _currentScreen->getCursorY();
2209             QByteArray textToSend;
2210             if (cuY != cy) {
2211                 for (int i = abs(cy - cuY); i > 0; i--) {
2212                     emulateUpDown(cy < cuY, LRKeys[cy > cuY], textToSend, i == 1 ? cx : -1);
2213                     textToSend += LRKeys[cy > cuY].text();
2214                 }
2215             } else {
2216                 if (cuX < cx) {
2217                     for (int i = 0; i < cx - cuX; i++) {
2218                         textToSend += LRKeys[1].text();
2219                     }
2220                 } else {
2221                     for (int i = 0; i < cuX - cx; i++) {
2222                         textToSend += LRKeys[0].text();
2223                     }
2224                 }
2225             }
2226             Q_EMIT sendData(textToSend);
2227         }
2228         return;
2229     }
2230 
2231     // Don't send move/drag events if only press and release requested
2232     if (eventType == 1 && getMode(MODE_Mouse1000)) {
2233         return;
2234     }
2235 
2236     // Don't send move with no button pressed if button-motion requested
2237     if ((cb & 3) == 3 && getMode(MODE_Mouse1002)) {
2238         return;
2239     }
2240 
2241     // With the exception of the 1006 mode, button release is encoded in cb.
2242     // Note that if multiple extensions are enabled, the 1006 is used, so it's okay to check for only that.
2243     if (eventType == 2 && !getMode(MODE_Mouse1006)) {
2244         cb &= ~3;
2245         cb |= 3;
2246     }
2247 
2248     // Mouse motion handling
2249     if ((getMode(MODE_Mouse1002) || getMode(MODE_Mouse1003)) && eventType == 1) {
2250         cb += 0x20; // add 32 to signify motion event
2251     }
2252     char command[40];
2253     command[0] = '\0';
2254     // Check the extensions in decreasing order of preference. Encoding the release event above assumes that 1006 comes first.
2255     if (getMode(MODE_Mouse1006)) {
2256         snprintf(command, sizeof(command), "\033[<%d;%d;%d%c", cb, cx, cy, eventType == 2 ? 'm' : 'M');
2257     } else if (getMode(MODE_Mouse1015)) {
2258         snprintf(command, sizeof(command), "\033[%d;%d;%dM", cb + 0x20, cx, cy);
2259     } else if (getMode(MODE_Mouse1005)) {
2260         if (cx <= 2015 && cy <= 2015) {
2261             // The xterm extension uses UTF-8 (up to 2 bytes) to encode
2262             // coordinate+32, no matter what the locale is. We could easily
2263             // convert manually, but QString can also do it for us.
2264             QChar coords[2];
2265             coords[0] = QChar(cx + 0x20);
2266             coords[1] = QChar(cy + 0x20);
2267             QString coordsStr = QString(coords, 2);
2268             QByteArray utf8 = coordsStr.toUtf8();
2269             snprintf(command, sizeof(command), "\033[M%c%s", cb + 0x20, utf8.constData());
2270         }
2271     } else if (cx <= 223 && cy <= 223) {
2272         snprintf(command, sizeof(command), "\033[M%c%c%c", cb + 0x20, cx + 0x20, cy + 0x20);
2273     }
2274 
2275     sendString(command);
2276 }
2277 
2278 void Vt102Emulation::emulateUpDown(bool up, KeyboardTranslator::Entry entry, QByteArray &textToSend, int toCol)
2279 {
2280     int cuX = _currentScreen->getCursorX();
2281     int cuY = _currentScreen->getCursorY();
2282     int realX = cuX;
2283     QVector<LineProperty> lineProperties = _currentScreen->getLineProperties(cuY - 1 + _currentScreen->getHistLines(),
2284                                                                              qMin(_currentScreen->getLines() - 1, cuY + 1) + _currentScreen->getHistLines());
2285     int num = _currentScreen->getColumns();
2286     if (up) {
2287         if ((lineProperties[0].flags.f.wrapped) == 0) {
2288             num = cuX + qMax(0, lineProperties[0].length - cuX) + 1;
2289         }
2290     } else {
2291         if ((lineProperties[1].flags.f.wrapped) == 0 || (lineProperties[2].flags.f.wrapped) == 0) {
2292             realX = qMin(cuX, lineProperties[2].length + 1);
2293             num = lineProperties[1].length - cuX + realX;
2294         }
2295     }
2296     if (toCol > -1) {
2297         num += up ? realX - toCol : toCol - realX;
2298     }
2299     for (int i = 1; i < num; i++) {
2300         // One more will be added by the rest of the code.
2301         textToSend += entry.text();
2302     }
2303 }
2304 
2305 /**
2306  * The focus change event can be used by Vim (or other terminal applications)
2307  * to recognize that the konsole window has changed focus.
2308  * The escape sequence is also used by iTerm2.
2309  * Vim needs the following plugin to be installed to convert the escape
2310  * sequence into the FocusLost/FocusGained autocmd:
2311  * https://github.com/sjl/vitality.vim
2312  */
2313 void Vt102Emulation::focusChanged(bool focused)
2314 {
2315     if (_reportFocusEvents) {
2316         sendString(focused ? "\033[I" : "\033[O");
2317     }
2318 }
2319 
2320 void Vt102Emulation::sendText(const QString &text)
2321 {
2322     if (!text.isEmpty()) {
2323         QKeyEvent event(QEvent::KeyPress, 0, Qt::NoModifier, text);
2324         sendKeyEvent(&event); // expose as a big fat keypress event
2325     }
2326 }
2327 
2328 void Vt102Emulation::sendKeyEvent(QKeyEvent *event)
2329 {
2330     const Qt::KeyboardModifiers modifiers = event->modifiers();
2331     KeyboardTranslator::States states = KeyboardTranslator::NoState;
2332 
2333     TerminalDisplay *currentView = _currentScreen->currentTerminalDisplay();
2334     bool isReadOnly = false;
2335     if (currentView != nullptr) {
2336         isReadOnly = currentView->getReadOnly();
2337     }
2338 
2339     // get current states
2340     if (getMode(MODE_NewLine)) {
2341         states |= KeyboardTranslator::NewLineState;
2342     }
2343     if (getMode(MODE_Ansi)) {
2344         states |= KeyboardTranslator::AnsiState;
2345     }
2346     if (getMode(MODE_AppCuKeys)) {
2347         states |= KeyboardTranslator::CursorKeysState;
2348     }
2349     if (getMode(MODE_AppScreen)) {
2350         states |= KeyboardTranslator::AlternateScreenState;
2351     }
2352     if (getMode(MODE_AppKeyPad) && ((modifiers & Qt::KeypadModifier) != 0U)) {
2353         states |= KeyboardTranslator::ApplicationKeypadState;
2354     }
2355 
2356     if (!isReadOnly) {
2357         // check flow control state
2358         if ((modifiers & Qt::ControlModifier) != 0U) {
2359             switch (event->key()) {
2360             case Qt::Key_S:
2361                 Q_EMIT flowControlKeyPressed(true);
2362                 break;
2363             case Qt::Key_C:
2364                 if (m_SixelStarted) {
2365                     SixelModeAbort();
2366                 }
2367 
2368                 // Allow the user to take back control
2369                 resetTokenizer();
2370                 Q_EMIT flowControlKeyPressed(false);
2371                 break;
2372             case Qt::Key_Q: // cancel flow control
2373                 Q_EMIT flowControlKeyPressed(false);
2374                 break;
2375             }
2376         }
2377     }
2378     // look up key binding
2379     if (_keyTranslator != nullptr) {
2380         KeyboardTranslator::Entry entry = _keyTranslator->findEntry(event->key(), modifiers, states);
2381         // send result to terminal
2382         QByteArray textToSend;
2383 
2384         int cuX = _currentScreen->getCursorX();
2385         int cuY = _currentScreen->getCursorY();
2386         if ((event->key() == Qt::Key_Up || event->key() == Qt::Key_Down) && _currentScreen->replMode() == REPL_INPUT
2387             && _currentScreen->currentTerminalDisplay()->semanticUpDown()) {
2388             bool up = event->key() == Qt::Key_Up;
2389             if ((up && _currentScreen->replModeStart() <= std::make_pair(cuY - 1, cuX))
2390                 || (!up && std::make_pair(cuY + 1, cuX) <= _currentScreen->replModeEnd())) {
2391                 entry = _keyTranslator->findEntry(up ? Qt::Key_Left : Qt::Key_Right, Qt::NoModifier, states);
2392                 emulateUpDown(up, entry, textToSend);
2393             }
2394         }
2395 
2396         // special handling for the Alt (aka. Meta) modifier.  pressing
2397         // Alt+[Character] results in Esc+[Character] being sent
2398         // (unless there is an entry defined for this particular combination
2399         //  in the keyboard modifier)
2400         const bool wantsAltModifier = ((entry.modifiers() & entry.modifierMask() & Qt::AltModifier) != 0U);
2401         const bool wantsMetaModifier = ((entry.modifiers() & entry.modifierMask() & Qt::MetaModifier) != 0U);
2402         const bool wantsAnyModifier = ((entry.state() & entry.stateMask() & KeyboardTranslator::AnyModifierState) != 0);
2403 
2404         if (((modifiers & Qt::AltModifier) != 0U) && !(wantsAltModifier || wantsAnyModifier) && !event->text().isEmpty()) {
2405             textToSend.prepend("\033");
2406         }
2407         if (((modifiers & Qt::MetaModifier) != 0U) && !(wantsMetaModifier || wantsAnyModifier) && !event->text().isEmpty()) {
2408             textToSend.prepend("\030@s");
2409         }
2410 
2411         if (entry.command() != KeyboardTranslator::NoCommand) {
2412             if ((entry.command() & KeyboardTranslator::EraseCommand) != 0) {
2413                 textToSend += eraseChar();
2414             }
2415             if (currentView != nullptr) {
2416                 if ((entry.command() & KeyboardTranslator::ScrollPageUpCommand) != 0) {
2417                     currentView->scrollScreenWindow(ScreenWindow::ScrollPages, -1);
2418                 } else if ((entry.command() & KeyboardTranslator::ScrollPageDownCommand) != 0) {
2419                     currentView->scrollScreenWindow(ScreenWindow::ScrollPages, 1);
2420                 } else if ((entry.command() & KeyboardTranslator::ScrollLineUpCommand) != 0) {
2421                     currentView->scrollScreenWindow(ScreenWindow::ScrollLines, -1);
2422                 } else if ((entry.command() & KeyboardTranslator::ScrollLineDownCommand) != 0) {
2423                     currentView->scrollScreenWindow(ScreenWindow::ScrollLines, 1);
2424                 } else if ((entry.command() & KeyboardTranslator::ScrollUpToTopCommand) != 0) {
2425                     currentView->scrollScreenWindow(ScreenWindow::ScrollLines, -currentView->screenWindow()->currentLine());
2426                 } else if ((entry.command() & KeyboardTranslator::ScrollDownToBottomCommand) != 0) {
2427                     currentView->scrollScreenWindow(ScreenWindow::ScrollLines, lineCount());
2428                 } else if ((entry.command() & KeyboardTranslator::ScrollPromptUpCommand) != 0) {
2429                     currentView->scrollScreenWindow(ScreenWindow::ScrollPrompts, -1);
2430                 } else if ((entry.command() & KeyboardTranslator::ScrollPromptDownCommand) != 0) {
2431                     currentView->scrollScreenWindow(ScreenWindow::ScrollPrompts, 1);
2432                 }
2433             }
2434         } else if (!entry.text().isEmpty()) {
2435             textToSend += entry.text(true, modifiers);
2436         } else {
2437             Q_ASSERT(_codec);
2438             textToSend += _codec->fromUnicode(event->text());
2439         }
2440 
2441         if (!isReadOnly) {
2442             Q_EMIT sendData(textToSend);
2443         }
2444     } else {
2445         if (!isReadOnly) {
2446             // print an error message to the terminal if no key translator has been
2447             // set
2448             QString translatorError = i18n(
2449                 "No keyboard translator available.  "
2450                 "The information needed to convert key presses "
2451                 "into characters to send to the terminal "
2452                 "is missing.");
2453             reset();
2454             receiveData(translatorError.toLatin1().constData(), translatorError.length());
2455         }
2456     }
2457 }
2458 
2459 /* ------------------------------------------------------------------------- */
2460 /*                                                                           */
2461 /*                                VT100 Charsets                             */
2462 /*                                                                           */
2463 /* ------------------------------------------------------------------------- */
2464 
2465 // Character Set Conversion ------------------------------------------------ --
2466 
2467 /*
2468    The processing contains a VT100 specific code translation layer.
2469    It's still in use and mainly responsible for the line drawing graphics.
2470 
2471    These and some other glyphs are assigned to codes (0x5f-0xfe)
2472    normally occupied by the latin letters. Since this codes also
2473    appear within control sequences, the extra code conversion
2474    does not permute with the tokenizer and is placed behind it
2475    in the pipeline. It only applies to tokens, which represent
2476    plain characters.
2477 
2478    This conversion it eventually continued in TerminalDisplay.C, since
2479    it might involve VT100 enhanced fonts, which have these
2480    particular glyphs allocated in (0x00-0x1f) in their code page.
2481 */
2482 
2483 #define CHARSET _charset[_currentScreen == _screen[1]]
2484 
2485 // Apply current character map.
2486 
2487 unsigned int Vt102Emulation::applyCharset(uint c)
2488 {
2489     if (CHARSET.graphic && 0x5f <= c && c <= 0x7e) {
2490         return vt100_graphics[c - 0x5f];
2491     }
2492     if (CHARSET.pound && c == '#') {
2493         return 0xa3; // This mode is obsolete
2494     }
2495     return c;
2496 }
2497 
2498 /*
2499    "Charset" related part of the emulation state.
2500    This configures the VT100 charset filter.
2501 
2502    While most operation work on the current _screen,
2503    the following two are different.
2504 */
2505 
2506 void Vt102Emulation::resetCharset(int scrno)
2507 {
2508     _charset[scrno].cu_cs = 0;
2509     qstrncpy(_charset[scrno].charset, "BBBB", 4);
2510     _charset[scrno].sa_graphic = false;
2511     _charset[scrno].sa_pound = false;
2512     _charset[scrno].graphic = false;
2513     _charset[scrno].pound = false;
2514 }
2515 
2516 void Vt102Emulation::setCharset(int n, int cs) // on both screens.
2517 {
2518     _charset[0].charset[n & 3] = cs;
2519     useCharset(_charset[0].cu_cs);
2520     _charset[1].charset[n & 3] = cs;
2521     useCharset(_charset[1].cu_cs);
2522 }
2523 
2524 void Vt102Emulation::setAndUseCharset(int n, int cs)
2525 {
2526     CHARSET.charset[n & 3] = cs;
2527     useCharset(n & 3);
2528 }
2529 
2530 void Vt102Emulation::useCharset(int n)
2531 {
2532     CHARSET.cu_cs = n & 3;
2533     CHARSET.graphic = (CHARSET.charset[n & 3] == '0');
2534     CHARSET.pound = (CHARSET.charset[n & 3] == 'A'); // This mode is obsolete
2535 }
2536 
2537 void Vt102Emulation::setDefaultMargins()
2538 {
2539     _screen[0]->setDefaultMargins();
2540     _screen[1]->setDefaultMargins();
2541 }
2542 
2543 void Vt102Emulation::setMargins(int t, int b)
2544 {
2545     _screen[0]->setMargins(t, b);
2546     _screen[1]->setMargins(t, b);
2547 }
2548 
2549 void Vt102Emulation::saveCursor()
2550 {
2551     CHARSET.sa_graphic = CHARSET.graphic;
2552     CHARSET.sa_pound = CHARSET.pound; // This mode is obsolete
2553     // we are not clear about these
2554     // sa_charset = charsets[cScreen->_charset];
2555     // sa_charset_num = cScreen->_charset;
2556     _currentScreen->saveCursor();
2557 }
2558 
2559 void Vt102Emulation::restoreCursor()
2560 {
2561     CHARSET.graphic = CHARSET.sa_graphic;
2562     CHARSET.pound = CHARSET.sa_pound; // This mode is obsolete
2563     _currentScreen->restoreCursor();
2564 }
2565 
2566 /* ------------------------------------------------------------------------- */
2567 /*                                                                           */
2568 /*                                Mode Operations                            */
2569 /*                                                                           */
2570 /* ------------------------------------------------------------------------- */
2571 
2572 /*
2573    Some of the emulations state is either added to the state of the screens.
2574 
2575    This causes some scoping problems, since different emulations choose to
2576    located the mode either to the current _screen or to both.
2577 
2578    For strange reasons, the extend of the rendition attributes ranges over
2579    all screens and not over the actual _screen.
2580 
2581    We decided on the precise precise extend, somehow.
2582 */
2583 
2584 // "Mode" related part of the state. These are all booleans.
2585 
2586 void Vt102Emulation::resetModes()
2587 {
2588     // MODE_Allow132Columns is not reset here
2589     // to match Xterm's behavior (see Xterm's VTReset() function)
2590 
2591     // MODE_Mouse1007 (Alternate Scrolling) is not reset here, to maintain
2592     // the profile alternate scrolling property after reset() is called, which
2593     // makes more sense; also this matches XTerm behavior.
2594 
2595     resetMode(MODE_132Columns);
2596     saveMode(MODE_132Columns);
2597     resetMode(MODE_Mouse1000);
2598     saveMode(MODE_Mouse1000);
2599     resetMode(MODE_Mouse1001);
2600     saveMode(MODE_Mouse1001);
2601     resetMode(MODE_Mouse1002);
2602     saveMode(MODE_Mouse1002);
2603     resetMode(MODE_Mouse1003);
2604     saveMode(MODE_Mouse1003);
2605     resetMode(MODE_Mouse1005);
2606     saveMode(MODE_Mouse1005);
2607     resetMode(MODE_Mouse1006);
2608     saveMode(MODE_Mouse1006);
2609     resetMode(MODE_Mouse1015);
2610     saveMode(MODE_Mouse1015);
2611     resetMode(MODE_BracketedPaste);
2612     saveMode(MODE_BracketedPaste);
2613 
2614     resetMode(MODE_AppScreen);
2615     saveMode(MODE_AppScreen);
2616     resetMode(MODE_AppCuKeys);
2617     saveMode(MODE_AppCuKeys);
2618     resetMode(MODE_AppKeyPad);
2619     saveMode(MODE_AppKeyPad);
2620     resetMode(MODE_NewLine);
2621     setMode(MODE_Ansi);
2622 }
2623 
2624 void Vt102Emulation::setMode(int m)
2625 {
2626     _currentModes.mode[m] = true;
2627     switch (m) {
2628     case MODE_132Columns:
2629         if (getMode(MODE_Allow132Columns)) {
2630             clearScreenAndSetColumns(132);
2631         } else {
2632             _currentModes.mode[m] = false;
2633         }
2634         break;
2635     case MODE_Mouse1000:
2636     case MODE_Mouse1001:
2637     case MODE_Mouse1002:
2638     case MODE_Mouse1003:
2639         _currentModes.mode[MODE_Mouse1000] = false;
2640         _currentModes.mode[MODE_Mouse1001] = false;
2641         _currentModes.mode[MODE_Mouse1002] = false;
2642         _currentModes.mode[MODE_Mouse1003] = false;
2643         _currentModes.mode[m] = true;
2644         Q_EMIT programRequestsMouseTracking(true);
2645         break;
2646     case MODE_Mouse1007:
2647         Q_EMIT enableAlternateScrolling(true);
2648         break;
2649     case MODE_Mouse1005:
2650     case MODE_Mouse1006:
2651     case MODE_Mouse1015:
2652         _currentModes.mode[MODE_Mouse1005] = false;
2653         _currentModes.mode[MODE_Mouse1006] = false;
2654         _currentModes.mode[MODE_Mouse1015] = false;
2655         _currentModes.mode[m] = true;
2656         break;
2657 
2658     case MODE_BracketedPaste:
2659         Q_EMIT programBracketedPasteModeChanged(true);
2660         break;
2661 
2662     case MODE_AppScreen:
2663         _screen[1]->setDefaultRendition();
2664         _screen[1]->clearSelection();
2665         setScreen(1);
2666         if (_currentScreen && _currentScreen->currentTerminalDisplay()) {
2667             _currentScreen->delPlacements(1);
2668             _currentScreen->currentTerminalDisplay()->update();
2669         }
2670         break;
2671     }
2672     // FIXME: Currently this has a redundant condition as MODES_SCREEN is 6
2673     // and MODE_NewLine is 5
2674     if (m < MODES_SCREEN || m == MODE_NewLine) {
2675         _screen[0]->setMode(m);
2676         _screen[1]->setMode(m);
2677     }
2678 }
2679 
2680 void Vt102Emulation::resetMode(int m)
2681 {
2682     _currentModes.mode[m] = false;
2683     switch (m) {
2684     case MODE_132Columns:
2685         if (getMode(MODE_Allow132Columns)) {
2686             clearScreenAndSetColumns(80);
2687         }
2688         break;
2689     case MODE_Mouse1000:
2690     case MODE_Mouse1001:
2691     case MODE_Mouse1002:
2692     case MODE_Mouse1003:
2693         // Same behavior as xterm, these modes are mutually exclusive,
2694         // and disabling any disables mouse tracking.
2695         _currentModes.mode[MODE_Mouse1000] = false;
2696         _currentModes.mode[MODE_Mouse1001] = false;
2697         _currentModes.mode[MODE_Mouse1002] = false;
2698         _currentModes.mode[MODE_Mouse1003] = false;
2699         Q_EMIT programRequestsMouseTracking(false);
2700         break;
2701     case MODE_Mouse1007:
2702         Q_EMIT enableAlternateScrolling(false);
2703         break;
2704 
2705     case MODE_BracketedPaste:
2706         Q_EMIT programBracketedPasteModeChanged(false);
2707         break;
2708 
2709     case MODE_AppScreen:
2710         _screen[0]->clearSelection();
2711         setScreen(0);
2712         if (_currentScreen && _currentScreen->currentTerminalDisplay()) {
2713             _currentScreen->currentTerminalDisplay()->update();
2714         }
2715         break;
2716     }
2717     // FIXME: Currently this has a redundant condition as MODES_SCREEN is 7
2718     // MODE_AppScreen is 6 and MODE_NewLine is 5
2719     if (m < MODES_SCREEN || m == MODE_NewLine) {
2720         _screen[0]->resetMode(m);
2721         _screen[1]->resetMode(m);
2722     }
2723 }
2724 
2725 void Vt102Emulation::saveMode(int m)
2726 {
2727     _savedModes.mode[m] = _currentModes.mode[m];
2728 }
2729 
2730 void Vt102Emulation::restoreMode(int m)
2731 {
2732     if (_savedModes.mode[m]) {
2733         setMode(m);
2734     } else {
2735         resetMode(m);
2736     }
2737 }
2738 
2739 bool Vt102Emulation::getMode(int m)
2740 {
2741     return _currentModes.mode[m];
2742 }
2743 
2744 char Vt102Emulation::eraseChar() const
2745 {
2746     KeyboardTranslator::Entry entry = _keyTranslator->findEntry(Qt::Key_Backspace, Qt::NoModifier, KeyboardTranslator::NoState);
2747     if (!entry.text().isEmpty()) {
2748         return entry.text().at(0);
2749     } else {
2750         return '\b';
2751     }
2752 }
2753 
2754 // return contents of the scan buffer
2755 static QString hexdump2(char32_t *s, int len)
2756 {
2757     int i;
2758     char dump[128];
2759     QString returnDump;
2760 
2761     for (i = 0; i < len; i++) {
2762         if (s[i] == '\\') {
2763             snprintf(dump, sizeof(dump), "%s", "\\\\");
2764         } else if ((s[i]) > 32 && s[i] < 127) {
2765             snprintf(dump, sizeof(dump), "%c", s[i]);
2766         } else if (s[i] == 0x1b) {
2767             snprintf(dump, sizeof(dump), "ESC");
2768         } else {
2769             snprintf(dump, sizeof(dump), "\\%04x(hex)", s[i]);
2770         }
2771         returnDump.append(QLatin1String(dump));
2772     }
2773     return returnDump;
2774 }
2775 
2776 void Vt102Emulation::reportDecodingError(int token)
2777 {
2778     QString outputError = QStringLiteral("Undecodable sequence: ");
2779 
2780     switch (token & 0xff) {
2781     case 2:
2782     case 3:
2783     case 4:
2784         outputError.append(QStringLiteral("ESC "));
2785         break;
2786     case 5:
2787     case 6:
2788     case 7:
2789     case 9:
2790     case 10:
2791     case 11:
2792     case 12:
2793     case 13:
2794         outputError.append(QStringLiteral("CSI "));
2795         break;
2796     case 14:
2797         outputError.append(QStringLiteral("OSC "));
2798         break;
2799     case 15:
2800         outputError.append(QStringLiteral("APC "));
2801         break;
2802     }
2803     if ((token & 0xff) != 8) { // VT52
2804         outputError.append(hexdump2(tokenBuffer, tokenBufferPos));
2805     } else {
2806         outputError.append(QStringLiteral("(VT52) ESC"));
2807     }
2808 
2809     if ((token & 0xff) != 3) { // SCS
2810         outputError.append(QLatin1Char((token >> 8) & 0xff));
2811     } else {
2812         outputError.append(QLatin1Char((token >> 16) & 0xff));
2813     }
2814 
2815     qCDebug(KonsoleDebug).noquote() << outputError;
2816 
2817     resetTokenizer();
2818 
2819     if (m_SixelStarted) {
2820         SixelModeAbort();
2821     }
2822 }
2823 
2824 void Vt102Emulation::sixelQuery(int q)
2825 {
2826     char tmp[30];
2827     if (q == 1) {
2828         if (params.value[1] == 1 || params.value[1] == 4) {
2829             snprintf(tmp, sizeof(tmp), "\033[?1;0;%dS", MAX_SIXEL_COLORS);
2830             sendString(tmp);
2831         }
2832     }
2833     if (q == 2) {
2834         if (params.value[1] == 1 || params.value[1] == 4) {
2835             snprintf(tmp, sizeof(tmp), "\033[?2;0;%d;%dS", MAX_IMAGE_DIM, MAX_IMAGE_DIM);
2836             sendString(tmp);
2837         }
2838     }
2839 }
2840 
2841 void Vt102Emulation::SixelModeEnable(int width, int height)
2842 {
2843     if (width <= 0 || height <= 0) {
2844         return;
2845     }
2846     if (width > MAX_IMAGE_DIM) {
2847         width = MAX_IMAGE_DIM;
2848     }
2849     if (height > MAX_IMAGE_DIM) {
2850         height = MAX_IMAGE_DIM;
2851     }
2852     m_actualSize = QSize(width, height);
2853 
2854     // We assume square pixels because I'm lazy and didn't implement the stuff in vt102emulation.
2855     int charactersHeight = height / 6 + 1;
2856 
2857     m_currentImage = QImage(width, charactersHeight * 6 + 1, QImage::Format_Indexed8);
2858     m_currentColor = 3;
2859     m_currentX = 0;
2860     m_verticalPosition = 0;
2861     if (!m_currentImage.isNull()) {
2862         m_SixelStarted = true;
2863     }
2864     m_currentImage.fill(0);
2865     std::string initial_colors[16] = {"#000000",
2866                                       "#3333CC",
2867                                       "#CC2323",
2868                                       "#33CC33",
2869                                       "#CC33CC",
2870                                       "#33CCCC",
2871                                       "#CCCC33",
2872                                       "#777777",
2873                                       "#444444",
2874                                       "#565699",
2875                                       "#994444",
2876                                       "#569956",
2877                                       "#995699",
2878                                       "#569999",
2879                                       "#999956",
2880                                       "#CCCCCC"};
2881     for (int i = 0; i < 16; i++) {
2882         m_currentImage.setColor(i, QColor(initial_colors[i].c_str()).rgb());
2883     }
2884 }
2885 
2886 void Vt102Emulation::SixelModeAbort()
2887 {
2888     if (!m_SixelStarted) {
2889         return;
2890     }
2891     resetMode(MODE_Sixel);
2892     resetTokenizer();
2893     m_SixelStarted = false;
2894     m_currentImage = QImage();
2895 }
2896 
2897 void Vt102Emulation::SixelModeDisable()
2898 {
2899     if (!m_SixelStarted) {
2900         return;
2901     }
2902     m_SixelStarted = false;
2903     int col, row;
2904     if (m_SixelScrolling) {
2905         col = _currentScreen->getCursorX();
2906         row = _currentScreen->getCursorY();
2907     } else {
2908         col = 0;
2909         row = 0;
2910     }
2911     QPixmap pixmap = QPixmap::fromImage(m_currentImage.copy(QRect(0, 0, m_actualSize.width(), m_actualSize.height())));
2912     if (m_aspect.first != m_aspect.second) {
2913         pixmap = pixmap.scaled(pixmap.width(), m_aspect.first * pixmap.height() / m_aspect.second);
2914     }
2915     int rows = -1, cols = -1;
2916     _currentScreen->addPlacement(pixmap, rows, cols, row, col, m_SixelScrolling, m_SixelScrolling * 2, false);
2917 }
2918 
2919 void Vt102Emulation::SixelColorChangeRGB(const int index, int red, int green, int blue)
2920 {
2921     if (index < 0 || index >= MAX_SIXEL_COLORS) {
2922         return;
2923     }
2924 
2925     red = red * 255 / 100;
2926     green = green * 255 / 100;
2927     blue = blue * 255 / 100;
2928 
2929     // QImage automatically handles the size of the color table
2930     m_currentImage.setColor(index, qRgb(red, green, blue));
2931     m_currentColor = index;
2932 }
2933 
2934 void Vt102Emulation::SixelColorChangeHSL(const int index, int hue, int saturation, int value)
2935 {
2936     if (index < 0 || index >= MAX_SIXEL_COLORS) {
2937         return;
2938     }
2939 
2940     hue = qBound(0, hue, 360);
2941     saturation = qBound(0, saturation, 100);
2942     value = qBound(0, value, 100);
2943 
2944     // libsixel is offset by 240 degrees, so we assume that is correct
2945     hue = (hue + 240) % 360;
2946 
2947     saturation = saturation * 255 / 100;
2948     value = value * 255 / 100;
2949 
2950     m_currentImage.setColor(index, QColor::fromHsl(hue, saturation, value).rgb());
2951     m_currentColor = index;
2952 }
2953 
2954 void Vt102Emulation::SixelCharacterAdd(uint8_t character, int repeat)
2955 {
2956     if (!m_SixelStarted) {
2957         return;
2958     }
2959 
2960     switch (character) {
2961     case '\r':
2962         m_currentX = 0;
2963         return;
2964     case '\n':
2965         m_verticalPosition++;
2966         return;
2967     default:
2968         break;
2969     }
2970     character -= '?';
2971     const int top = m_verticalPosition * 6;
2972     const int bottom = (m_verticalPosition + 1) * 6;
2973     if (bottom > MAX_IMAGE_DIM) { // Ignore lines below MAX_IMAGE_DIM
2974         return;
2975     }
2976     repeat = std::clamp(repeat, 1, MAX_IMAGE_DIM - m_currentX); // Won't repeat beyond MAX_IMAGE_DIM
2977 
2978     if (bottom >= m_currentImage.height() - 1 || m_currentX + repeat >= m_currentImage.width()) {
2979         // If we copy out of bounds it gets filled with 0
2980         int extraWidth = 255 + repeat; // Increase size by at least 256, to avoid increasing for every pixel
2981         int newWidth = qMax(m_currentX + extraWidth, m_currentImage.width() + extraWidth);
2982         int newHeight = (qMax(bottom + 256, m_currentImage.height() + 256) / 6 + 1) * 6;
2983         newWidth = qMin(newWidth, MAX_IMAGE_DIM);
2984         newHeight = qMin(newHeight, MAX_IMAGE_DIM);
2985         if (newWidth != m_currentImage.width() || newHeight != m_currentImage.height()) {
2986             m_currentImage = m_currentImage.copy(0, 0, newWidth, newHeight);
2987         }
2988         if (m_currentImage.isNull()) {
2989             m_SixelStarted = false;
2990             return;
2991         }
2992     }
2993 
2994     const ptrdiff_t bpl = m_currentImage.bytesPerLine();
2995     uchar *data = m_currentImage.bits() + top * bpl + m_currentX;
2996 
2997     if (repeat == 1) {
2998         if (m_preserveBackground) {
2999             // Konsole is built without _any_ optimizations by default, so a little
3000             // manual unrolling to avoid calling shift for every loop iteration.
3001             if (character & (1 << 0)) {
3002                 data[0 * bpl] = m_currentColor;
3003             }
3004             if (character & (1 << 1)) {
3005                 data[1 * bpl] = m_currentColor;
3006             }
3007             if (character & (1 << 2)) {
3008                 data[2 * bpl] = m_currentColor;
3009             }
3010             if (character & (1 << 3)) {
3011                 data[3 * bpl] = m_currentColor;
3012             }
3013             if (character & (1 << 4)) {
3014                 data[4 * bpl] = m_currentColor;
3015             }
3016             if (character & (1 << 5)) {
3017                 data[5 * bpl] = m_currentColor;
3018             }
3019         } else {
3020             for (int i = 0; i < 6; i++, data += bpl) {
3021                 *data = (character & (1 << i)) * m_currentColor;
3022             }
3023         }
3024         m_currentX++;
3025     } else {
3026         if (m_preserveBackground) {
3027             // Konsole is built without _any_ optimizations by default, so a little
3028             // manual unrolling to avoid calling shift for every loop iteration.
3029             if (character & (1 << 0)) {
3030                 memset(&data[0 * bpl], m_currentColor, repeat);
3031             }
3032             if (character & (1 << 1)) {
3033                 memset(&data[1 * bpl], m_currentColor, repeat);
3034             }
3035             if (character & (1 << 2)) {
3036                 memset(&data[2 * bpl], m_currentColor, repeat);
3037             }
3038             if (character & (1 << 3)) {
3039                 memset(&data[3 * bpl], m_currentColor, repeat);
3040             }
3041             if (character & (1 << 4)) {
3042                 memset(&data[4 * bpl], m_currentColor, repeat);
3043             }
3044             if (character & (1 << 5)) {
3045                 memset(&data[5 * bpl], m_currentColor, repeat);
3046             }
3047         } else {
3048             for (int i = 0; i < 6; i++, data += bpl) {
3049                 memset(data, (character & (1 << i)) * m_currentColor, repeat);
3050             }
3051         }
3052         m_currentX += repeat;
3053     }
3054     if (m_currentX > m_actualSize.width()) {
3055         m_actualSize.setWidth(m_currentX);
3056     }
3057     if (bottom > m_actualSize.height()) {
3058         m_actualSize.setHeight(bottom);
3059     }
3060 }
3061 
3062 bool Vt102Emulation::processSixel(uint cc)
3063 {
3064     switch (cc) {
3065     case '$':
3066         SixelCharacterAdd('\r');
3067         resetTokenizer();
3068         return true;
3069     case '-':
3070         SixelCharacterAdd('\r');
3071         SixelCharacterAdd('\n');
3072         resetTokenizer();
3073         return true;
3074     default:
3075         break;
3076     }
3077     char32_t *s = tokenBuffer;
3078     const int p = tokenBufferPos;
3079 
3080     if (!m_SixelStarted && (sixel() || s[0] == '!' || s[0] == '#')) {
3081         m_aspect = qMakePair(1, 1);
3082         SixelModeEnable(30, 6);
3083     }
3084     if (sixel()) {
3085         SixelCharacterAdd(uint8_t(cc));
3086         resetTokenizer();
3087         return true;
3088     }
3089     if (ccc(DIG)) {
3090         addDigit(cc - '0');
3091         return true;
3092     }
3093     if (cc == ';') {
3094         addArgument();
3095         return true;
3096     }
3097 
3098     if (s[0] == '"') {
3099         if (p < 3) {
3100             return true;
3101         }
3102         addArgument();
3103 
3104         if (params.count == 4 || params.count == 2) {
3105             // We just ignore the pixel aspect ratio, it's dumb
3106             // const int pixelWidth = params.value[0];
3107             // const int pixelHeight = params.value[1];
3108 
3109             if (!m_SixelStarted) {
3110                 if (params.value[1] == 0 || params.value[0] == 0) {
3111                     m_aspect = qMakePair(1, 1);
3112                 } else {
3113                     m_aspect = qMakePair(params.value[0], params.value[1]);
3114                 }
3115                 int width;
3116                 int height;
3117                 if (params.count == 4) {
3118                     width = params.value[2];
3119                     height = params.value[3];
3120                 } else {
3121                     // Default size
3122                     width = 8;
3123                     height = 6;
3124                 }
3125                 SixelModeEnable(width, height);
3126             }
3127             resetTokenizer();
3128             receiveChars(QVector<uint>{cc}); // re-send the actual character
3129             return true;
3130         }
3131         return false;
3132     }
3133 
3134     // Repeat character
3135     if (s[0] == '!') {
3136         if (p < 2) {
3137             return true;
3138         }
3139         if (ces(DIG)) {
3140             addDigit(cc - '0');
3141             return true;
3142         }
3143 
3144         SixelCharacterAdd(cc, params.value[0]);
3145         resetTokenizer();
3146         return true;
3147     }
3148 
3149     if (s[0] == '#') {
3150         if (p < 2) {
3151             return true;
3152         }
3153         addArgument();
3154         if (params.count < 1) {
3155             return false;
3156         }
3157         const int index = params.value[0];
3158         if (params.count == 5) {
3159             const int colorspace = params.value[1];
3160             switch (colorspace) {
3161             case 1:
3162                 // Confusingly it is in HLS order...
3163                 SixelColorChangeHSL(index, params.value[2], params.value[4], params.value[3]);
3164                 break;
3165             case 2:
3166                 SixelColorChangeRGB(index, params.value[2], params.value[3], params.value[4]);
3167                 break;
3168             default:
3169                 return false;
3170             }
3171         } else if (params.count == 1 && index >= 0) { // Negative index is an error. Too large index is ignored
3172             if (index < MAX_SIXEL_COLORS) {
3173                 m_currentColor = index;
3174             }
3175         } else {
3176             return false;
3177         }
3178         resetTokenizer();
3179         receiveChars(QVector<uint>{cc}); // re-send the actual character
3180         return true;
3181     }
3182     return false;
3183 }
3184 
3185 int Vt102Emulation::getFreeGraphicsImageId()
3186 {
3187     int i = 1;
3188     while (1) {
3189         if (!_graphicsImages.count(i)) {
3190             return i;
3191         }
3192         i++;
3193     }
3194 }
3195 
3196 #include "moc_Vt102Emulation.cpp"