File indexing completed on 2024-03-24 05:54:27
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 = ¶ms.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 = ¶ms.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 ¶ms, 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"