├── .gitignore ├── LICENSE ├── README ├── doc ├── ctlseqs(ms).html └── ctlseqs(ms)_files │ └── man2html.css ├── gui ├── __init__.py ├── font.py └── terminal.py ├── main.py ├── src ├── gui │ ├── __init__.py │ ├── font.py │ └── terminal.py ├── main.py └── terminal │ ├── __init__.py │ ├── ctrl.py │ ├── cursor.py │ ├── driver.py │ ├── emulator.py │ ├── esc.py │ ├── rendition.py │ └── screen.py ├── support └── gen-term-colors.py └── terminal ├── __init__.py ├── ctrl.py ├── cursor.py ├── emulator.py ├── esc.py ├── process.py ├── rendition.py └── screen.py /.gitignore: -------------------------------------------------------------------------------- 1 | *.pyc 2 | __pycache__ 3 | _PySide 4 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | Copyright (c) The Regents of the University of California. 2 | All rights reserved. 3 | 4 | Redistribution and use in source and binary forms, with or without 5 | modification, are permitted provided that the following conditions 6 | are met: 7 | 1. Redistributions of source code must retain the above copyright 8 | notice, this list of conditions and the following disclaimer. 9 | 2. Redistributions in binary form must reproduce the above copyright 10 | notice, this list of conditions and the following disclaimer in the 11 | documentation and/or other materials provided with the distribution. 12 | 3. Neither the name of the University nor the names of its contributors 13 | may be used to endorse or promote products derived from this software 14 | without specific prior written permission. 15 | 16 | THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND 17 | ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 18 | IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE 19 | ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE 20 | FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL 21 | DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS 22 | OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) 23 | HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT 24 | LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY 25 | OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF 26 | SUCH DAMAGE. 27 | -------------------------------------------------------------------------------- /README: -------------------------------------------------------------------------------- 1 | Simple attempt at terminal emulator in python and PySide (free QT library). 2 | It's very slow and more a proof of concept than anything as it shouldn't be 3 | used for anything serious. 4 | -------------------------------------------------------------------------------- /doc/ctlseqs(ms).html: -------------------------------------------------------------------------------- 1 | 2 |
3 | 4 | 5 | 6 |http://invisible-island.net/xterm/ 15 |
18 | XTerm Control Sequences 19 | 20 | Edward Moy 21 | University of California, Berkeley 22 | 23 | Revised by 24 | 25 | Stephen Gildea 26 | X Consortium (1994) 27 | 28 | Thomas Dickey 29 | XFree86 Project (1996-2006) 30 | invisible-island.net (2006-2016) 31 | updated for XTerm Patch #323 (2016/02/21) 32 | 33 |34 |
c The literal character c. 36 | 37 | C A single (required) character. 38 | 39 | Ps A single (usually optional) numeric parameter, composed of one of 40 | more digits. 41 | 42 | Pm A multiple numeric parameter composed of any number of single 43 | numeric parameters, separated by ; character(s). Individual val- 44 | ues for the parameters are listed with Ps . 45 | 46 | Pt A text parameter composed of printable characters. 47 | 48 |49 |
ECMA-48 (aka "ISO 6429") documents C1 (8-bit) and C0 (7-bit) codes. 51 | Those are respectively codes 128 to 159 and 0 to 31. ECMA-48 avoids 52 | referring to these codes as characters, because that term is associated 53 | with graphic characters. Instead, it uses "bytes" and "codes", with 54 | occasional lapses to "characters" where the meaning cannot be mistaken. 55 | 56 | Controls (including the escape code 27) are processed once: 57 | 58 | o This means that a C1 control can be mistaken for badly-formed UTF-8 59 | when the terminal runs in UTF-8 mode because C1 controls are valid 60 | continuation bytes of a UTF-8 encoded (multibyte) value. 61 | 62 | o It is not possible to use a C1 control obtained from decoding the 63 | UTF-8 text, because that would require reprocessing the data. Conse- 64 | quently there is no ambiguity in the way this document uses the term 65 | "character" to refer to bytes in a control sequence. 66 | 67 | The order of processing is a necessary consequence of the way ECMA-48 is 68 | designed: 69 | 70 | o Each byte sent to the terminal can be unambiguously determined to fall 71 | into one of a few categories (C0, C1 and graphic characters). 72 | 73 | o ECMA-48 is modal; once it starts processing a control sequence, the 74 | terminal continues until the sequence is complete, or some byte is 75 | found which is not allowed in the sequence. 76 | 77 | o Intermediate, parameter and final bytes may use the same codes as 78 | graphic characters, but they are processed as part of a control 79 | sequence and are not actually graphic characters. 80 | 81 | o Eight-bit controls can have intermediate, etc., bytes in the range 160 82 | to 255. Those can be treated as their counterparts in the range 32 to 83 | 127. 84 | 85 | o Single-byte controls can be handled separately from multi-byte control 86 | sequences because ECMA-48's rules are unambiguous. 87 | 88 | As a special case, ECMA-48 (section 9) mentions that the control func- 89 | tions shift-in and shift-out are allowed to occur within a 7-bit 90 | multibyte control sequence because those cannot alter the meaning of 91 | the control sequence. 92 | 93 | o Some controls (such as OSC ) introduce a string mode, which is ended 94 | on a ST (string terminator). 95 | 96 | Again, the terminal should accept single-byte controls within the 97 | string. However, xterm has a resource setting brokenLinuxOSC to allow 98 | recovery from applications which rely upon malformed palette sequences 99 | used by the Linux console. 100 | 101 |102 |
The xterm program recognizes both 8-bit and 7-bit control characters. 104 | It generates 7-bit controls (by default) or 8-bit if S8C1T is enabled. 105 | The following pairs of 7-bit and 8-bit control characters are equiva- 106 | lent: 107 | 108 | ESC D 109 | Index (IND is 0x84). 110 | ESC E 111 | Next Line (NEL is 0x85). 112 | ESC H 113 | Tab Set (HTS is 0x88). 114 | ESC M 115 | Reverse Index (RI is 0x8d). 116 | ESC N 117 | Single Shift Select of G2 Character Set (SS2 is 0x8e). This 118 | affects next character only. 119 | ESC O 120 | Single Shift Select of G3 Character Set (SS3 is 0x8f). This 121 | affects next character only. 122 | ESC P 123 | Device Control String (DCS is 0x90). 124 | ESC V 125 | Start of Guarded Area (SPA is 0x96). 126 | ESC W 127 | End of Guarded Area (EPA is 0x97). 128 | ESC X 129 | Start of String (SOS is 0x98). 130 | ESC Z 131 | Return Terminal ID (DECID is 0x9a). Obsolete form of CSI c (DA). 132 | ESC [ 133 | Control Sequence Introducer (CSI is 0x9b). 134 | ESC \ 135 | String Terminator (ST is 0x9c). 136 | ESC ] 137 | Operating System Command (OSC is 0x9d). 138 | ESC ^ 139 | Privacy Message (PM is 0x9e). 140 | ESC _ 141 | Application Program Command (APC is 0x9f). 142 | 143 | These control characters are used in the vtXXX emulation. 144 | 145 |146 |
Most of these control sequences are standard VT102 control sequences, 148 | but there is support for later DEC VT terminals (i.e., VT220, VT320, 149 | VT420, VT510), as well as ISO 6429 and aixterm color controls. The only 150 | VT102 feature not supported is auto-repeat, since the only way X pro- 151 | vides for this will affect all windows. 152 | There are additional control sequences to provide xterm-dependent func- 153 | tions, such as the scrollbar or window size. Where the function is 154 | specified by DEC or ISO 6429, the code assigned to it is given in paren- 155 | theses. 156 | The escape codes to designate and invoke character sets are specified by 157 | ISO 2022 (see that document for a discussion of character sets). 158 | Many of the features are optional; xterm can be configured and built 159 | without support for them. 160 | 161 |162 |
BEL Bell (Ctrl-G). 164 | BS Backspace (Ctrl-H). 165 | CR Carriage Return (Ctrl-M). 166 | ENQ Return Terminal Status (Ctrl-E). Default response is an empty 167 | string, but may be overridden by a resource answerbackString. 168 | FF Form Feed or New Page (NP). Ctrl-L is treated the same as LF. 169 | LF Line Feed or New Line (NL). (LF is Ctrl-J). 170 | SI Shift In (Ctrl-O) -> Switch to Standard Character Set. This 171 | invokes the G0 character set (the default). 172 | SO Shift Out (Ctrl-N) -> Switch to Alternate Character Set. This 173 | invokes the G1 character set. 174 | SP Space. 175 | TAB Horizontal Tab (HT) (Ctrl-I). 176 | VT Vertical Tab (Ctrl-K). This is treated the same as LF. 177 | 178 |179 |
This excludes controls where ESC is part of a 7-bit equivalent to 8-bit 181 | C1 controls, ordered by the final character(s). 182 | ESC SP F 7-bit controls (S7C1T). 183 | ESC SP G 8-bit controls (S8C1T). 184 | ESC SP L Set ANSI conformance level 1 (dpANS X3.134.1). 185 | ESC SP M Set ANSI conformance level 2 (dpANS X3.134.1). 186 | ESC SP N Set ANSI conformance level 3 (dpANS X3.134.1). 187 | ESC # 3 DEC double-height line, top half (DECDHL). 188 | ESC # 4 DEC double-height line, bottom half (DECDHL). 189 | ESC # 5 DEC single-width line (DECSWL). 190 | ESC # 6 DEC double-width line (DECDWL). 191 | ESC # 8 DEC Screen Alignment Test (DECALN). 192 | ESC % @ Select default character set. That is ISO 8859-1 (ISO 2022). 193 | ESC % G Select UTF-8 character set (ISO 2022). 194 | ESC ( C Designate G0 Character Set (ISO 2022, VT100). 195 | Final character C for designating 94-character sets. In this 196 | list, 0 , A and B apply to VT100 and up, the remainder to 197 | VT220 and up. The VT220 character sets, together with the 198 | Portuguese character set are activated by the National 199 | Replacement Character controls. The A is a special case, 200 | since it is also activated by the VT300-control for British 201 | Latin-1 separately from the National Replacement Character 202 | controls. 203 | C = 0 -> DEC Special Character and Line Drawing Set. 204 | C = < -> DEC Supplementary (VT200). 205 | C = % 5 -> DEC Supplementary Graphics (VT300). 206 | C = > -> DEC Technical (VT300). 207 | C = A -> United Kingdom (UK). 208 | C = B -> United States (USASCII). 209 | C = 4 -> Dutch. 210 | C = C or 5 -> Finnish. 211 | C = R or f -> French. 212 | C = Q or 9 -> French Canadian (VT200, VT300). 213 | C = K -> German. 214 | C = Y -> Italian. 215 | C = ` , E or 6 -> Norwegian/Danish. 216 | C = % 6 -> Portuguese (VT300). 217 | C = Z -> Spanish. 218 | C = H or 7 -> Swedish. 219 | C = = -> Swiss. 220 | ESC ) C Designate G1 Character Set (ISO 2022, VT100). 221 | The same character sets apply as for ESC ( C. 222 | ESC * C Designate G2 Character Set (ISO 2022, VT220). 223 | The same character sets apply as for ESC ( C. 224 | ESC + C Designate G3 Character Set (ISO 2022, VT220). 225 | The same character sets apply as for ESC ( C. 226 | ESC - C Designate G1 Character Set (VT300). 227 | The same character sets apply as for ESC ( C. 228 | ESC . C Designate G2 Character Set (VT300). 229 | The same character sets apply as for ESC ( C. 230 | ESC / C Designate G3 Character Set (VT300). 231 | These work for 96-character sets only. 232 | C = A -> ISO Latin-1 Supplemental. 233 | ESC 6 Back Index (DECBI), VT420 and up. 234 | ESC 7 Save Cursor (DECSC). 235 | ESC 8 Restore Cursor (DECRC). 236 | ESC 9 Forward Index (DECFI), VT420 and up. 237 | ESC = Application Keypad (DECKPAM). 238 | ESC > Normal Keypad (DECKPNM). 239 | ESC F Cursor to lower left corner of screen. This is enabled by the 240 | hpLowerleftBugCompat resource. 241 | ESC c Full Reset (RIS). 242 | ESC l Memory Lock (per HP terminals). Locks memory above the cur- 243 | sor. 244 | ESC m Memory Unlock (per HP terminals). 245 | ESC n Invoke the G2 Character Set as GL (LS2). 246 | ESC o Invoke the G3 Character Set as GL (LS3). 247 | ESC | Invoke the G3 Character Set as GR (LS3R). 248 | ESC } Invoke the G2 Character Set as GR (LS2R). 249 | ESC ~ Invoke the G1 Character Set as GR (LS1R). 250 | 251 |252 |
APC Pt ST None. xterm implements no APC functions; Pt is ignored. Pt 254 | need not be printable characters. 255 | 256 |257 |
DCS Ps; Ps| Pt ST 259 | User-Defined Keys (DECUDK). The first parameter: 260 | Ps = 0 -> Clear all UDK definitions before starting 261 | (default). 262 | Ps = 1 -> Erase Below (default). 263 | The second parameter: 264 | Ps = 0 <- Lock the keys (default). 265 | Ps = 1 <- Do not lock. 266 | The third parameter is a ';'-separated list of strings denot- 267 | ing the key-code separated by a '/' from the hex-encoded key 268 | value. The key codes correspond to the DEC function-key codes 269 | (e.g., F6=17). 270 | DCS $ q Pt ST 271 | Request Status String (DECRQSS). The string following the "q" 272 | is one of the following: 273 | " q -> DECSCA 274 | " p -> DECSCL 275 | r -> DECSTBM 276 | s -> DECSLRM 277 | m -> SGR 278 | SP q -> DECSCUSR 279 | xterm responds with DCS 1 $ r Pt ST for valid requests, 280 | replacing the Pt with the corresponding CSI string, or DCS 0 $ 281 | r Pt ST for invalid requests. 282 | DCS + p Pt ST 283 | Set Termcap/Terminfo Data (xterm, experimental). The string 284 | following the "p" is a name to use for retrieving data from 285 | the terminal database. The data will be used for the "tcap" 286 | keyboard configuration's function- and special-keys, as well 287 | as by the Request Termcap/Terminfo String control. 288 | DCS + q Pt ST 289 | Request Termcap/Terminfo String (xterm, experimental). The 290 | string following the "q" is a list of names encoded in hexa- 291 | decimal (2 digits per character) separated by ; which corre- 292 | spond to termcap or terminfo key names. 293 | Two special features are also recognized, which are not key 294 | names: Co for termcap colors (or colors for terminfo colors), 295 | and TN for termcap name (or name for terminfo name). 296 | xterm responds with DCS 1 + r Pt ST for valid requests, adding 297 | to Pt an = , and the value of the corresponding string that 298 | xterm would send, or DCS 0 + r Pt ST for invalid requests. 299 | The strings are encoded in hexadecimal (2 digits per charac- 300 | ter). 301 | 302 |303 |
CSI Ps @ Insert Ps (Blank) Character(s) (default = 1) (ICH). 305 | CSI Ps A Cursor Up Ps Times (default = 1) (CUU). 306 | CSI Ps B Cursor Down Ps Times (default = 1) (CUD). 307 | CSI Ps C Cursor Forward Ps Times (default = 1) (CUF). 308 | CSI Ps D Cursor Backward Ps Times (default = 1) (CUB). 309 | CSI Ps E Cursor Next Line Ps Times (default = 1) (CNL). 310 | CSI Ps F Cursor Preceding Line Ps Times (default = 1) (CPL). 311 | CSI Ps G Cursor Character Absolute [column] (default = [row,1]) (CHA). 312 | CSI Ps ; Ps H 313 | Cursor Position [row;column] (default = [1,1]) (CUP). 314 | CSI Ps I Cursor Forward Tabulation Ps tab stops (default = 1) (CHT). 315 | CSI Ps J Erase in Display (ED). 316 | Ps = 0 -> Erase Below (default). 317 | Ps = 1 -> Erase Above. 318 | Ps = 2 -> Erase All. 319 | Ps = 3 -> Erase Saved Lines (xterm). 320 | CSI ? Ps J 321 | Erase in Display (DECSED). 322 | Ps = 0 -> Selective Erase Below (default). 323 | Ps = 1 -> Selective Erase Above. 324 | Ps = 2 -> Selective Erase All. 325 | CSI Ps K Erase in Line (EL). 326 | Ps = 0 -> Erase to Right (default). 327 | Ps = 1 -> Erase to Left. 328 | Ps = 2 -> Erase All. 329 | CSI ? Ps K 330 | Erase in Line (DECSEL). 331 | Ps = 0 -> Selective Erase to Right (default). 332 | Ps = 1 -> Selective Erase to Left. 333 | Ps = 2 -> Selective Erase All. 334 | CSI Ps L Insert Ps Line(s) (default = 1) (IL). 335 | CSI Ps M Delete Ps Line(s) (default = 1) (DL). 336 | CSI Ps P Delete Ps Character(s) (default = 1) (DCH). 337 | CSI Ps S Scroll up Ps lines (default = 1) (SU). 338 | CSI ? Pi; Pa; Pv S 339 | If configured to support either Sixel Graphics or ReGIS Graph- 340 | ics, xterm accepts a three-parameter control sequence, where 341 | Pi, Pa and Pv are the item, action and value. 342 | Pi = 1 -> item (color registers) 343 | Pa = 1 -> read the number of color registers 344 | Pa = 2 -> reset the number of color registers 345 | Pa = 3 -> set the number of color registers to the value Pv 346 | The control sequence returns a response using the same form: 347 | 348 | CSI ? Pi; Ps; Pv S 349 | 350 | where Ps is the status: 351 | Ps = 0 -> success 352 | Ps = 3 -> failure 353 | CSI Ps T Scroll down Ps lines (default = 1) (SD). 354 | CSI Ps ; Ps ; Ps ; Ps ; Ps T 355 | Initiate highlight mouse tracking. Parameters are 356 | [func;startx;starty;firstrow;lastrow]. See the section Mouse 357 | Tracking. 358 | CSI > Ps; Ps T 359 | Reset one or more features of the title modes to the default 360 | value. Normally, "reset" disables the feature. It is possi- 361 | ble to disable the ability to reset features by compiling a 362 | different default for the title modes into xterm. 363 | Ps = 0 -> Do not set window/icon labels using hexadecimal. 364 | Ps = 1 -> Do not query window/icon labels using hexadeci- 365 | mal. 366 | Ps = 2 -> Do not set window/icon labels using UTF-8. 367 | Ps = 3 -> Do not query window/icon labels using UTF-8. 368 | (See discussion of "Title Modes"). 369 | CSI Ps X Erase Ps Character(s) (default = 1) (ECH). 370 | CSI Ps Z Cursor Backward Tabulation Ps tab stops (default = 1) (CBT). 371 | CSI Pm ` Character Position Absolute [column] (default = [row,1]) 372 | (HPA). 373 | CSI Pm a Character Position Relative [columns] (default = [row,col+1]) 374 | (HPR). 375 | CSI Ps b Repeat the preceding graphic character Ps times (REP). 376 | CSI Ps c Send Device Attributes (Primary DA). 377 | Ps = 0 or omitted -> request attributes from terminal. The 378 | response depends on the decTerminalID resource setting. 379 | -> CSI ? 1 ; 2 c ("VT100 with Advanced Video Option") 380 | -> CSI ? 1 ; 0 c ("VT101 with No Options") 381 | -> CSI ? 6 c ("VT102") 382 | -> CSI ? 6 2 ; Psc ("VT220") 383 | -> CSI ? 6 3 ; Psc ("VT320") 384 | -> CSI ? 6 4 ; Psc ("VT420") 385 | The VT100-style response parameters do not mean anything by 386 | themselves. VT220 (and higher) parameters do, telling the 387 | host what features the terminal supports: 388 | Ps = 1 -> 132-columns. 389 | Ps = 2 -> Printer. 390 | Ps = 3 -> ReGIS graphics. 391 | Ps = 4 -> Sixel graphics. 392 | Ps = 6 -> Selective erase. 393 | Ps = 8 -> User-defined keys. 394 | Ps = 9 -> National Replacement Character sets. 395 | Ps = 1 5 -> Technical characters. 396 | Ps = 1 8 -> User windows. 397 | Ps = 2 1 -> Horizontal scrolling. 398 | Ps = 2 2 -> ANSI color, e.g., VT525. 399 | Ps = 2 9 -> ANSI text locator (i.e., DEC Locator mode). 400 | CSI > Ps c 401 | Send Device Attributes (Secondary DA). 402 | Ps = 0 or omitted -> request the terminal's identification 403 | code. The response depends on the decTerminalID resource set- 404 | ting. It should apply only to VT220 and up, but xterm extends 405 | this to VT100. 406 | -> CSI > Pp ; Pv ; Pc c 407 | where Pp denotes the terminal type 408 | Pp = 0 -> "VT100". 409 | Pp = 1 -> "VT220". 410 | Pp = 2 -> "VT240". 411 | Pp = 1 8 -> "VT330". 412 | Pp = 1 9 -> "VT340". 413 | Pp = 2 4 -> "VT320". 414 | Pp = 4 1 -> "VT420". 415 | Pp = 6 1 -> "VT510". 416 | Pp = 6 4 -> "VT520". 417 | Pp = 6 5 -> "VT525". 418 | and Pv is the firmware version (for xterm, this was originally 419 | the XFree86 patch number, starting with 95). In a DEC termi- 420 | nal, Pc indicates the ROM cartridge registration number and is 421 | always zero. 422 | CSI Pm d Line Position Absolute [row] (default = [1,column]) (VPA). 423 | CSI Pm e Line Position Relative [rows] (default = [row+1,column]) 424 | (VPR). 425 | CSI Ps ; Ps f 426 | Horizontal and Vertical Position [row;column] (default = 427 | [1,1]) (HVP). 428 | CSI Ps g Tab Clear (TBC). 429 | Ps = 0 -> Clear Current Column (default). 430 | Ps = 3 -> Clear All. 431 | CSI Pm h Set Mode (SM). 432 | Ps = 2 -> Keyboard Action Mode (AM). 433 | Ps = 4 -> Insert Mode (IRM). 434 | Ps = 1 2 -> Send/receive (SRM). 435 | Ps = 2 0 -> Automatic Newline (LNM). 436 | CSI ? Pm h 437 | DEC Private Mode Set (DECSET). 438 | Ps = 1 -> Application Cursor Keys (DECCKM). 439 | Ps = 2 -> Designate USASCII for character sets G0-G3 440 | (DECANM), and set VT100 mode. 441 | Ps = 3 -> 132 Column Mode (DECCOLM). 442 | Ps = 4 -> Smooth (Slow) Scroll (DECSCLM). 443 | Ps = 5 -> Reverse Video (DECSCNM). 444 | Ps = 6 -> Origin Mode (DECOM). 445 | Ps = 7 -> Wraparound Mode (DECAWM). 446 | Ps = 8 -> Auto-repeat Keys (DECARM). 447 | Ps = 9 -> Send Mouse X & Y on button press. See the sec- 448 | tion Mouse Tracking. This is the X10 xterm mouse protocol. 449 | Ps = 1 0 -> Show toolbar (rxvt). 450 | Ps = 1 2 -> Start Blinking Cursor (att610). 451 | Ps = 1 8 -> Print form feed (DECPFF). 452 | Ps = 1 9 -> Set print extent to full screen (DECPEX). 453 | Ps = 2 5 -> Show Cursor (DECTCEM). 454 | Ps = 3 0 -> Show scrollbar (rxvt). 455 | Ps = 3 5 -> Enable font-shifting functions (rxvt). 456 | Ps = 3 8 -> Enter Tektronix Mode (DECTEK). 457 | Ps = 4 0 -> Allow 80 -> 132 Mode. 458 | Ps = 4 1 -> more(1) fix (see curses resource). 459 | Ps = 4 2 -> Enable National Replacement Character sets 460 | (DECNRCM). 461 | Ps = 4 4 -> Turn On Margin Bell. 462 | Ps = 4 5 -> Reverse-wraparound Mode. 463 | Ps = 4 6 -> Start Logging. This is normally disabled by a 464 | compile-time option. 465 | Ps = 4 7 -> Use Alternate Screen Buffer. (This may be dis- 466 | abled by the titeInhibit resource). 467 | Ps = 6 6 -> Application keypad (DECNKM). 468 | Ps = 6 7 -> Backarrow key sends backspace (DECBKM). 469 | Ps = 6 9 -> Enable left and right margin mode (DECLRMM), 470 | VT420 and up. 471 | Ps = 9 5 -> Do not clear screen when DECCOLM is set/reset 472 | (DECNCSM), VT510 and up. 473 | Ps = 1 0 0 0 -> Send Mouse X & Y on button press and 474 | release. See the section Mouse Tracking. This is the X11 475 | xterm mouse protocol. 476 | Ps = 1 0 0 1 -> Use Hilite Mouse Tracking. 477 | Ps = 1 0 0 2 -> Use Cell Motion Mouse Tracking. 478 | Ps = 1 0 0 3 -> Use All Motion Mouse Tracking. 479 | Ps = 1 0 0 4 -> Send FocusIn/FocusOut events. 480 | Ps = 1 0 0 5 -> Enable UTF-8 Mouse Mode. 481 | Ps = 1 0 0 6 -> Enable SGR Mouse Mode. 482 | Ps = 1 0 0 7 -> Enable Alternate Scroll Mode. 483 | Ps = 1 0 1 0 -> Scroll to bottom on tty output (rxvt). 484 | Ps = 1 0 1 1 -> Scroll to bottom on key press (rxvt). 485 | Ps = 1 0 1 5 -> Enable urxvt Mouse Mode. 486 | Ps = 1 0 3 4 -> Interpret "meta" key, sets eighth bit. 487 | (enables the eightBitInput resource). 488 | Ps = 1 0 3 5 -> Enable special modifiers for Alt and Num- 489 | Lock keys. (This enables the numLock resource). 490 | Ps = 1 0 3 6 -> Send ESC when Meta modifies a key. (This 491 | enables the metaSendsEscape resource). 492 | Ps = 1 0 3 7 -> Send DEL from the editing-keypad Delete 493 | key. 494 | Ps = 1 0 3 9 -> Send ESC when Alt modifies a key. (This 495 | enables the altSendsEscape resource). 496 | Ps = 1 0 4 0 -> Keep selection even if not highlighted. 497 | (This enables the keepSelection resource). 498 | Ps = 1 0 4 1 -> Use the CLIPBOARD selection. (This enables 499 | the selectToClipboard resource). 500 | Ps = 1 0 4 2 -> Enable Urgency window manager hint when 501 | Control-G is received. (This enables the bellIsUrgent 502 | resource). 503 | Ps = 1 0 4 3 -> Enable raising of the window when Control-G 504 | is received. (enables the popOnBell resource). 505 | Ps = 1 0 4 4 -> Reuse the most recent data copied to CLIP- 506 | BOARD. (This enables the keepClipboard resource). 507 | Ps = 1 0 4 7 -> Use Alternate Screen Buffer. (This may be 508 | disabled by the titeInhibit resource). 509 | Ps = 1 0 4 8 -> Save cursor as in DECSC. (This may be dis- 510 | abled by the titeInhibit resource). 511 | Ps = 1 0 4 9 -> Save cursor as in DECSC and use Alternate 512 | Screen Buffer, clearing it first. (This may be disabled by 513 | the titeInhibit resource). This combines the effects of the 1 514 | 0 4 7 and 1 0 4 8 modes. Use this with terminfo-based 515 | applications rather than the 4 7 mode. 516 | Ps = 1 0 5 0 -> Set terminfo/termcap function-key mode. 517 | Ps = 1 0 5 1 -> Set Sun function-key mode. 518 | Ps = 1 0 5 2 -> Set HP function-key mode. 519 | Ps = 1 0 5 3 -> Set SCO function-key mode. 520 | Ps = 1 0 6 0 -> Set legacy keyboard emulation (X11R6). 521 | Ps = 1 0 6 1 -> Set VT220 keyboard emulation. 522 | Ps = 2 0 0 4 -> Set bracketed paste mode. 523 | CSI Pm i Media Copy (MC). 524 | Ps = 0 -> Print screen (default). 525 | Ps = 4 -> Turn off printer controller mode. 526 | Ps = 5 -> Turn on printer controller mode. 527 | Ps = 1 0 -> HTML screen dump. 528 | Ps = 1 1 -> SVG screen dump. 529 | CSI ? Pm i 530 | Media Copy (MC, DEC-specific). 531 | Ps = 1 -> Print line containing cursor. 532 | Ps = 4 -> Turn off autoprint mode. 533 | Ps = 5 -> Turn on autoprint mode. 534 | Ps = 1 0 -> Print composed display, ignores DECPEX. 535 | Ps = 1 1 -> Print all pages. 536 | CSI Pm l Reset Mode (RM). 537 | Ps = 2 -> Keyboard Action Mode (AM). 538 | Ps = 4 -> Replace Mode (IRM). 539 | Ps = 1 2 -> Send/receive (SRM). 540 | Ps = 2 0 -> Normal Linefeed (LNM). 541 | CSI ? Pm l 542 | DEC Private Mode Reset (DECRST). 543 | Ps = 1 -> Normal Cursor Keys (DECCKM). 544 | Ps = 2 -> Designate VT52 mode (DECANM). 545 | Ps = 3 -> 80 Column Mode (DECCOLM). 546 | Ps = 4 -> Jump (Fast) Scroll (DECSCLM). 547 | Ps = 5 -> Normal Video (DECSCNM). 548 | Ps = 6 -> Normal Cursor Mode (DECOM). 549 | Ps = 7 -> No Wraparound Mode (DECAWM). 550 | Ps = 8 -> No Auto-repeat Keys (DECARM). 551 | Ps = 9 -> Don't send Mouse X & Y on button press. 552 | Ps = 1 0 -> Hide toolbar (rxvt). 553 | Ps = 1 2 -> Stop Blinking Cursor (att610). 554 | Ps = 1 8 -> Don't print form feed (DECPFF). 555 | Ps = 1 9 -> Limit print to scrolling region (DECPEX). 556 | Ps = 2 5 -> Hide Cursor (DECTCEM). 557 | Ps = 3 0 -> Don't show scrollbar (rxvt). 558 | Ps = 3 5 -> Disable font-shifting functions (rxvt). 559 | Ps = 4 0 -> Disallow 80 -> 132 Mode. 560 | Ps = 4 1 -> No more(1) fix (see curses resource). 561 | Ps = 4 2 -> Disable National Replacement Character sets 562 | (DECNRCM). 563 | Ps = 4 4 -> Turn Off Margin Bell. 564 | Ps = 4 5 -> No Reverse-wraparound Mode. 565 | Ps = 4 6 -> Stop Logging. (This is normally disabled by a 566 | compile-time option). 567 | Ps = 4 7 -> Use Normal Screen Buffer. 568 | Ps = 6 6 -> Numeric keypad (DECNKM). 569 | Ps = 6 7 -> Backarrow key sends delete (DECBKM). 570 | Ps = 6 9 -> Disable left and right margin mode (DECLRMM), 571 | VT420 and up. 572 | Ps = 9 5 -> Clear screen when DECCOLM is set/reset (DEC- 573 | NCSM), VT510 and up. 574 | Ps = 1 0 0 0 -> Don't send Mouse X & Y on button press and 575 | release. See the section Mouse Tracking. 576 | Ps = 1 0 0 1 -> Don't use Hilite Mouse Tracking. 577 | Ps = 1 0 0 2 -> Don't use Cell Motion Mouse Tracking. 578 | Ps = 1 0 0 3 -> Don't use All Motion Mouse Tracking. 579 | Ps = 1 0 0 4 -> Don't send FocusIn/FocusOut events. 580 | Ps = 1 0 0 5 -> Disable UTF-8 Mouse Mode. 581 | Ps = 1 0 0 6 -> Disable SGR Mouse Mode. 582 | Ps = 1 0 0 7 -> Disable Alternate Scroll Mode. 583 | Ps = 1 0 1 0 -> Don't scroll to bottom on tty output 584 | (rxvt). 585 | Ps = 1 0 1 1 -> Don't scroll to bottom on key press (rxvt). 586 | Ps = 1 0 1 5 -> Disable urxvt Mouse Mode. 587 | Ps = 1 0 3 4 -> Don't interpret "meta" key. (This disables 588 | the eightBitInput resource). 589 | Ps = 1 0 3 5 -> Disable special modifiers for Alt and Num- 590 | Lock keys. (This disables the numLock resource). 591 | Ps = 1 0 3 6 -> Don't send ESC when Meta modifies a key. 592 | (This disables the metaSendsEscape resource). 593 | Ps = 1 0 3 7 -> Send VT220 Remove from the editing-keypad 594 | Delete key. 595 | Ps = 1 0 3 9 -> Don't send ESC when Alt modifies a key. 596 | (This disables the altSendsEscape resource). 597 | Ps = 1 0 4 0 -> Do not keep selection when not highlighted. 598 | (This disables the keepSelection resource). 599 | Ps = 1 0 4 1 -> Use the PRIMARY selection. (This disables 600 | the selectToClipboard resource). 601 | Ps = 1 0 4 2 -> Disable Urgency window manager hint when 602 | Control-G is received. (This disables the bellIsUrgent 603 | resource). 604 | Ps = 1 0 4 3 -> Disable raising of the window when Control- 605 | G is received. (This disables the popOnBell resource). 606 | Ps = 1 0 4 7 -> Use Normal Screen Buffer, clearing screen 607 | first if in the Alternate Screen. (This may be disabled by 608 | the titeInhibit resource). 609 | Ps = 1 0 4 8 -> Restore cursor as in DECRC. (This may be 610 | disabled by the titeInhibit resource). 611 | Ps = 1 0 4 9 -> Use Normal Screen Buffer and restore cursor 612 | as in DECRC. (This may be disabled by the titeInhibit 613 | resource). This combines the effects of the 1 0 4 7 and 1 0 614 | 4 8 modes. Use this with terminfo-based applications rather 615 | than the 4 7 mode. 616 | Ps = 1 0 5 0 -> Reset terminfo/termcap function-key mode. 617 | Ps = 1 0 5 1 -> Reset Sun function-key mode. 618 | Ps = 1 0 5 2 -> Reset HP function-key mode. 619 | Ps = 1 0 5 3 -> Reset SCO function-key mode. 620 | Ps = 1 0 6 0 -> Reset legacy keyboard emulation (X11R6). 621 | Ps = 1 0 6 1 -> Reset keyboard emulation to Sun/PC style. 622 | Ps = 2 0 0 4 -> Reset bracketed paste mode. 623 | CSI Pm m Character Attributes (SGR). 624 | Ps = 0 -> Normal (default). 625 | Ps = 1 -> Bold. 626 | Ps = 2 -> Faint, decreased intensity (ISO 6429). 627 | Ps = 3 -> Italicized (ISO 6429). 628 | Ps = 4 -> Underlined. 629 | Ps = 5 -> Blink (appears as Bold). 630 | Ps = 7 -> Inverse. 631 | Ps = 8 -> Invisible, i.e., hidden (VT300). 632 | Ps = 9 -> Crossed-out characters (ISO 6429). 633 | Ps = 2 1 -> Doubly-underlined (ISO 6429). 634 | Ps = 2 2 -> Normal (neither bold nor faint). 635 | Ps = 2 3 -> Not italicized (ISO 6429). 636 | Ps = 2 4 -> Not underlined. 637 | Ps = 2 5 -> Steady (not blinking). 638 | Ps = 2 7 -> Positive (not inverse). 639 | Ps = 2 8 -> Visible, i.e., not hidden (VT300). 640 | Ps = 2 9 -> Not crossed-out (ISO 6429). 641 | Ps = 3 0 -> Set foreground color to Black. 642 | Ps = 3 1 -> Set foreground color to Red. 643 | Ps = 3 2 -> Set foreground color to Green. 644 | Ps = 3 3 -> Set foreground color to Yellow. 645 | Ps = 3 4 -> Set foreground color to Blue. 646 | Ps = 3 5 -> Set foreground color to Magenta. 647 | Ps = 3 6 -> Set foreground color to Cyan. 648 | Ps = 3 7 -> Set foreground color to White. 649 | Ps = 3 9 -> Set foreground color to default (original). 650 | Ps = 4 0 -> Set background color to Black. 651 | Ps = 4 1 -> Set background color to Red. 652 | Ps = 4 2 -> Set background color to Green. 653 | Ps = 4 3 -> Set background color to Yellow. 654 | Ps = 4 4 -> Set background color to Blue. 655 | Ps = 4 5 -> Set background color to Magenta. 656 | Ps = 4 6 -> Set background color to Cyan. 657 | Ps = 4 7 -> Set background color to White. 658 | Ps = 4 9 -> Set background color to default (original). 659 | 660 | If 16-color support is compiled, the following apply. Assume 661 | that xterm's resources are set so that the ISO color codes are 662 | the first 8 of a set of 16. Then the aixterm colors are the 663 | bright versions of the ISO colors: 664 | Ps = 9 0 -> Set foreground color to Black. 665 | Ps = 9 1 -> Set foreground color to Red. 666 | Ps = 9 2 -> Set foreground color to Green. 667 | Ps = 9 3 -> Set foreground color to Yellow. 668 | Ps = 9 4 -> Set foreground color to Blue. 669 | Ps = 9 5 -> Set foreground color to Magenta. 670 | Ps = 9 6 -> Set foreground color to Cyan. 671 | Ps = 9 7 -> Set foreground color to White. 672 | Ps = 1 0 0 -> Set background color to Black. 673 | Ps = 1 0 1 -> Set background color to Red. 674 | Ps = 1 0 2 -> Set background color to Green. 675 | Ps = 1 0 3 -> Set background color to Yellow. 676 | Ps = 1 0 4 -> Set background color to Blue. 677 | Ps = 1 0 5 -> Set background color to Magenta. 678 | Ps = 1 0 6 -> Set background color to Cyan. 679 | Ps = 1 0 7 -> Set background color to White. 680 | 681 | If xterm is compiled with the 16-color support disabled, it 682 | supports the following, from rxvt: 683 | Ps = 1 0 0 -> Set foreground and background color to 684 | default. 685 | 686 | Xterm maintains a color palette whose entries are identified 687 | by an index beginning with zero. If 88- or 256-color support 688 | is compiled, the following apply: 689 | o All parameters are decimal integers. 690 | o RGB values range from zero (0) to 255. 691 | o ISO-8613-3 can be interpreted in more than one way; xterm 692 | allows the semicolons in this control to be replaced by 693 | colons (but after the first colon, colons must be used). 694 | 695 | These ISO-8613-3 controls are supported: 696 | Pm = 3 8 ; 2 ; Pr; Pg; Pb -> Set foreground color to the 697 | closest match in xterm's palette for the given RGB Pr/Pg/Pb. 698 | Pm = 3 8 ; 5 ; Ps -> Set foreground color to Ps. 699 | Pm = 4 8 ; 2 ; Pr; Pg; Pb -> Set background color to the 700 | closest match in xterm's palette for the given RGB Pr/Pg/Pb. 701 | Pm = 4 8 ; 5 ; Ps -> Set background color to Ps. 702 | 703 | CSI > Ps; Ps m 704 | Set or reset resource-values used by xterm to decide whether 705 | to construct escape sequences holding information about the 706 | modifiers pressed with a given key. The first parameter iden- 707 | tifies the resource to set/reset. The second parameter is the 708 | value to assign to the resource. If the second parameter is 709 | omitted, the resource is reset to its initial value. 710 | Ps = 0 -> modifyKeyboard. 711 | Ps = 1 -> modifyCursorKeys. 712 | Ps = 2 -> modifyFunctionKeys. 713 | Ps = 4 -> modifyOtherKeys. 714 | If no parameters are given, all resources are reset to their 715 | initial values. 716 | CSI Ps n Device Status Report (DSR). 717 | Ps = 5 -> Status Report. 718 | Result ("OK") is CSI 0 n 719 | Ps = 6 -> Report Cursor Position (CPR) [row;column]. 720 | Result is CSI r ; c R 721 | 722 | Note: it is possible for this sequence to be sent by a func- 723 | tion key. For example, with the default keyboard configura- 724 | tion the shifted F1 key may send (with shift-, control-, alt- 725 | modifiers) 726 | CSI 1 ; 2 R , or 727 | CSI 1 ; 5 R , or 728 | CSI 1 ; 6 R , etc. 729 | The second parameter encodes the modifiers; values range from 730 | 2 to 16. See the section PC-Style Function Keys for the 731 | codes. The modifyFunctionKeys and modifyKeyboard resources 732 | can change the form of the string sent from the modified F1 733 | key. 734 | 735 | CSI > Ps n 736 | Disable modifiers which may be enabled via the CSI > Ps; Ps m 737 | sequence. This corresponds to a resource value of "-1", which 738 | cannot be set with the other sequence. The parameter identi- 739 | fies the resource to be disabled: 740 | Ps = 0 -> modifyKeyboard. 741 | Ps = 1 -> modifyCursorKeys. 742 | Ps = 2 -> modifyFunctionKeys. 743 | Ps = 4 -> modifyOtherKeys. 744 | If the parameter is omitted, modifyFunctionKeys is disabled. 745 | When modifyFunctionKeys is disabled, xterm uses the modifier 746 | keys to make an extended sequence of functions rather than 747 | adding a parameter to each function key to denote the modi- 748 | fiers. 749 | CSI ? Ps n 750 | Device Status Report (DSR, DEC-specific). 751 | Ps = 6 -> Report Cursor Position (DECXCPR) [row;column] as 752 | CSI ? r ; c R (assumes the default page, i.e., "1"). 753 | Ps = 1 5 -> Report Printer status as CSI ? 1 0 n (ready). 754 | or CSI ? 1 1 n (not ready). 755 | Ps = 2 5 -> Report UDK status as CSI ? 2 0 n (unlocked) or 756 | CSI ? 2 1 n (locked). 757 | Ps = 2 6 -> Report Keyboard status as 758 | CSI ? 2 7 ; 1 ; 0 ; 0 n (North American). 759 | The last two parameters apply to VT400 & up, and denote key- 760 | board ready and LK01 respectively. 761 | Ps = 5 3 -> Report Locator status as CSI ? 5 3 n Locator 762 | available, if compiled-in, or CSI ? 5 0 n No Locator, if not. 763 | Ps = 5 5 -> Report Locator status as CSI ? 5 3 n Locator 764 | available, if compiled-in, or CSI ? 5 0 n No Locator, if not. 765 | Ps = 5 6 -> Report Locator type as CSI ? 5 7 ; 1 n Mouse, 766 | if compiled-in, or CSI ? 5 7 ; 0 n Cannot identify, if not. 767 | Ps = 6 2 -> Report macro space (DECMSR) as CSI Pn \* { 768 | Ps = 6 3 -> Report memory checksum (DECCKSR) as DCS Pt ! x 769 | x x x ST 770 | Pt is the request id (from an optional parameter to the 771 | request). 772 | The x's are hexadecimal digits 0-9 and A-F. 773 | Ps = 7 5 -> Report data integrity as CSI ? 7 0 n (ready, 774 | no errors) 775 | Ps = 8 5 -> Report multi-session configuration as CSI ? 8 3 776 | n (not configured for multiple-session operation). 777 | CSI > Ps p 778 | Set resource value pointerMode. This is used by xterm to 779 | decide whether to hide the pointer cursor as the user types. 780 | Valid values for the parameter: 781 | Ps = 0 -> never hide the pointer. 782 | Ps = 1 -> hide if the mouse tracking mode is not enabled. 783 | Ps = 2 -> always hide the pointer, except when leaving the 784 | window. 785 | Ps = 3 -> always hide the pointer, even if leaving/entering 786 | the window. If no parameter is given, xterm uses the default, 787 | which is 1 . 788 | CSI ! p Soft terminal reset (DECSTR). 789 | CSI Ps $ p 790 | Request ANSI mode (DECRQM). For VT300 and up, reply is 791 | CSI Ps; Pm$ y 792 | where Ps is the mode number as in RM, and Pm is the mode 793 | value: 794 | 0 - not recognized 795 | 1 - set 796 | 2 - reset 797 | 3 - permanently set 798 | 4 - permanently reset 799 | CSI ? Ps$ p 800 | Request DEC private mode (DECRQM). For VT300 and up, reply is 801 | CSI ? Ps; Pm$ y 802 | where Ps is the mode number as in DECSET, Pm is the mode value 803 | as in the ANSI DECRQM. 804 | CSI Ps ; Ps " p 805 | Set conformance level (DECSCL). Valid values for the first 806 | parameter: 807 | Ps = 6 1 -> VT100. 808 | Ps = 6 2 -> VT200. 809 | Ps = 6 3 -> VT300. 810 | Valid values for the second parameter: 811 | Ps = 0 -> 8-bit controls. 812 | Ps = 1 -> 7-bit controls (always set for VT100). 813 | Ps = 2 -> 8-bit controls. 814 | CSI Ps q Load LEDs (DECLL). 815 | Ps = 0 -> Clear all LEDS (default). 816 | Ps = 1 -> Light Num Lock. 817 | Ps = 2 -> Light Caps Lock. 818 | Ps = 3 -> Light Scroll Lock. 819 | Ps = 2 1 -> Extinguish Num Lock. 820 | Ps = 2 2 -> Extinguish Caps Lock. 821 | Ps = 2 3 -> Extinguish Scroll Lock. 822 | CSI Ps SP q 823 | Set cursor style (DECSCUSR, VT520). 824 | Ps = 0 -> blinking block. 825 | Ps = 1 -> blinking block (default). 826 | Ps = 2 -> steady block. 827 | Ps = 3 -> blinking underline. 828 | Ps = 4 -> steady underline. 829 | Ps = 5 -> blinking bar (xterm). 830 | Ps = 6 -> steady bar (xterm). 831 | CSI Ps " q 832 | Select character protection attribute (DECSCA). Valid values 833 | for the parameter: 834 | Ps = 0 -> DECSED and DECSEL can erase (default). 835 | Ps = 1 -> DECSED and DECSEL cannot erase. 836 | Ps = 2 -> DECSED and DECSEL can erase. 837 | CSI Ps ; Ps r 838 | Set Scrolling Region [top;bottom] (default = full size of win- 839 | dow) (DECSTBM). 840 | CSI ? Pm r 841 | Restore DEC Private Mode Values. The value of Ps previously 842 | saved is restored. Ps values are the same as for DECSET. 843 | CSI Pt; Pl; Pb; Pr; Ps$ r 844 | Change Attributes in Rectangular Area (DECCARA), VT400 and up. 845 | Pt; Pl; Pb; Pr denotes the rectangle. 846 | Ps denotes the SGR attributes to change: 0, 1, 4, 5, 7. 847 | CSI Pl ; Pr s 848 | Set left and right margins (DECSLRM), available only when 849 | DECLRMM is enabled (VT420 and up). 850 | CSI s Save cursor (ANSI.SYS), available only when DECLRMM is dis- 851 | abled. 852 | CSI ? Pm s 853 | Save DEC Private Mode Values. Ps values are the same as for 854 | DECSET. 855 | CSI Ps ; Ps ; Ps t 856 | Window manipulation (from dtterm, as well as extensions). 857 | These controls may be disabled using the allowWindowOps 858 | resource. Valid values for the first (and any additional 859 | parameters) are: 860 | Ps = 1 -> De-iconify window. 861 | Ps = 2 -> Iconify window. 862 | Ps = 3 ; x ; y -> Move window to [x, y]. 863 | Ps = 4 ; height ; width -> Resize the xterm window to 864 | given height and width in pixels. Omitted parameters reuse 865 | the current height or width. Zero parameters use the dis- 866 | play's height or width. 867 | Ps = 5 -> Raise the xterm window to the front of the stack- 868 | ing order. 869 | Ps = 6 -> Lower the xterm window to the bottom of the 870 | stacking order. 871 | Ps = 7 -> Refresh the xterm window. 872 | Ps = 8 ; height ; width -> Resize the text area to given 873 | height and width in characters. Omitted parameters reuse the 874 | current height or width. Zero parameters use the display's 875 | height or width. 876 | Ps = 9 ; 0 -> Restore maximized window. 877 | Ps = 9 ; 1 -> Maximize window (i.e., resize to screen 878 | size). 879 | Ps = 9 ; 2 -> Maximize window vertically. 880 | Ps = 9 ; 3 -> Maximize window horizontally. 881 | Ps = 1 0 ; 0 -> Undo full-screen mode. 882 | Ps = 1 0 ; 1 -> Change to full-screen. 883 | Ps = 1 0 ; 2 -> Toggle full-screen. 884 | Ps = 1 1 -> Report xterm window state. If the xterm window 885 | is open (non-iconified), it returns CSI 1 t . If the xterm 886 | window is iconified, it returns CSI 2 t . 887 | Ps = 1 3 -> Report xterm window position. 888 | Result is CSI 3 ; x ; y t 889 | Ps = 1 4 -> Report xterm window in pixels. 890 | Result is CSI 4 ; height ; width t 891 | Ps = 1 8 -> Report the size of the text area in characters. 892 | Result is CSI 8 ; height ; width t 893 | Ps = 1 9 -> Report the size of the screen in characters. 894 | Result is CSI 9 ; height ; width t 895 | Ps = 2 0 -> Report xterm window's icon label. 896 | Result is OSC L label ST 897 | Ps = 2 1 -> Report xterm window's title. 898 | Result is OSC l label ST 899 | Ps = 2 2 ; 0 -> Save xterm icon and window title on 900 | stack. 901 | Ps = 2 2 ; 1 -> Save xterm icon title on stack. 902 | Ps = 2 2 ; 2 -> Save xterm window title on stack. 903 | Ps = 2 3 ; 0 -> Restore xterm icon and window title from 904 | stack. 905 | Ps = 2 3 ; 1 -> Restore xterm icon title from stack. 906 | Ps = 2 3 ; 2 -> Restore xterm window title from stack. 907 | Ps >= 2 4 -> Resize to Ps lines (DECSLPP). 908 | CSI Pt; Pl; Pb; Pr; Ps$ t 909 | Reverse Attributes in Rectangular Area (DECRARA), VT400 and 910 | up. 911 | Pt; Pl; Pb; Pr denotes the rectangle. 912 | Ps denotes the attributes to reverse, i.e., 1, 4, 5, 7. 913 | CSI > Ps; Ps t 914 | Set one or more features of the title modes. Each parameter 915 | enables a single feature. 916 | Ps = 0 -> Set window/icon labels using hexadecimal. 917 | Ps = 1 -> Query window/icon labels using hexadecimal. 918 | Ps = 2 -> Set window/icon labels using UTF-8. 919 | Ps = 3 -> Query window/icon labels using UTF-8. (See dis- 920 | cussion of "Title Modes") 921 | CSI Ps SP t 922 | Set warning-bell volume (DECSWBV, VT520). 923 | Ps = 0 or 1 -> off. 924 | Ps = 2 , 3 or 4 -> low. 925 | Ps = 5 , 6 , 7 , or 8 -> high. 926 | CSI u Restore cursor (ANSI.SYS). 927 | CSI Ps SP u 928 | Set margin-bell volume (DECSMBV, VT520). 929 | Ps = 1 -> off. 930 | Ps = 2 , 3 or 4 -> low. 931 | Ps = 0 , 5 , 6 , 7 , or 8 -> high. 932 | CSI Pt; Pl; Pb; Pr; Pp; Pt; Pl; Pp$ v 933 | Copy Rectangular Area (DECCRA, VT400 and up). 934 | Pt; Pl; Pb; Pr denotes the rectangle. 935 | Pp denotes the source page. 936 | Pt; Pl denotes the target location. 937 | Pp denotes the target page. 938 | CSI Pt ; Pl ; Pb ; Pr ' w 939 | Enable Filter Rectangle (DECEFR), VT420 and up. 940 | Parameters are [top;left;bottom;right]. 941 | Defines the coordinates of a filter rectangle and activates 942 | it. Anytime the locator is detected outside of the filter 943 | rectangle, an outside rectangle event is generated and the 944 | rectangle is disabled. Filter rectangles are always treated 945 | as "one-shot" events. Any parameters that are omitted default 946 | to the current locator position. If all parameters are omit- 947 | ted, any locator motion will be reported. DECELR always can- 948 | cels any prevous rectangle definition. 949 | CSI Ps x Request Terminal Parameters (DECREQTPARM). 950 | if Ps is a "0" (default) or "1", and xterm is emulating VT100, 951 | the control sequence elicits a response of the same form whose 952 | parameters describe the terminal: 953 | Ps -> the given Ps incremented by 2. 954 | Pn = 1 <- no parity. 955 | Pn = 1 <- eight bits. 956 | Pn = 1 <- 2 8 transmit 38.4k baud. 957 | Pn = 1 <- 2 8 receive 38.4k baud. 958 | Pn = 1 <- clock multiplier. 959 | Pn = 0 <- STP flags. 960 | CSI Ps * x 961 | Select Attribute Change Extent (DECSACE). 962 | Ps = 0 -> from start to end position, wrapped. 963 | Ps = 1 -> from start to end position, wrapped. 964 | Ps = 2 -> rectangle (exact). 965 | CSI Pi ; Pg ; Pt; Pl; Pb; Pr * y 966 | Request Checksum of Rectangular Area (DECRQCRA), VT420 and up. 967 | Response is 968 | DCS Pi ! x x x x ST 969 | Pi is the request id. 970 | Pg is the page number. 971 | Pt; Pl; Pb; Pr denotes the rectangle. 972 | The x's are hexadecimal digits 0-9 and A-F. 973 | CSI Pc ; Pt ; Pl ; Pb ; Pr $ x 974 | Fill Rectangular Area (DECFRA), VT420 and up. 975 | Pc is the character to use. 976 | Pt; Pl; Pb; Pr denotes the rectangle. 977 | CSI Ps ; Pu ' z 978 | Enable Locator Reporting (DECELR). 979 | Valid values for the first parameter: 980 | Ps = 0 -> Locator disabled (default). 981 | Ps = 1 -> Locator enabled. 982 | Ps = 2 -> Locator enabled for one report, then disabled. 983 | The second parameter specifies the coordinate unit for locator 984 | reports. 985 | Valid values for the second parameter: 986 | Pu = 0 <- or omitted -> default to character cells. 987 | Pu = 1 <- device physical pixels. 988 | Pu = 2 <- character cells. 989 | CSI Pt; Pl; Pb; Pr$ z 990 | Erase Rectangular Area (DECERA), VT400 and up. 991 | Pt; Pl; Pb; Pr denotes the rectangle. 992 | CSI Pm ' { 993 | Select Locator Events (DECSLE). 994 | Valid values for the first (and any additional parameters) 995 | are: 996 | Ps = 0 -> only respond to explicit host requests (DECRQLP). 997 | (This is default). It also cancels any filter 998 | rectangle. 999 | Ps = 1 -> report button down transitions. 1000 | Ps = 2 -> do not report button down transitions. 1001 | Ps = 3 -> report button up transitions. 1002 | Ps = 4 -> do not report button up transitions. 1003 | CSI Pt; Pl; Pb; Pr $ { 1004 | Selective Erase Rectangular Area (DECSERA), VT400 and up. 1005 | Pt; Pl; Pb; Pr denotes the rectangle. 1006 | CSI Ps ' | 1007 | Request Locator Position (DECRQLP). 1008 | Valid values for the parameter are: 1009 | Ps = 0 , 1 or omitted -> transmit a single DECLRP locator 1010 | report. 1011 | 1012 | If Locator Reporting has been enabled by a DECELR, xterm will 1013 | respond with a DECLRP Locator Report. This report is also 1014 | generated on button up and down events if they have been 1015 | enabled with a DECSLE, or when the locator is detected outside 1016 | of a filter rectangle, if filter rectangles have been enabled 1017 | with a DECEFR. 1018 | 1019 | -> CSI Pe ; Pb ; Pr ; Pc ; Pp & w 1020 | 1021 | Parameters are [event;button;row;column;page]. 1022 | Valid values for the event: 1023 | Pe = 0 -> locator unavailable - no other parameters sent. 1024 | Pe = 1 -> request - xterm received a DECRQLP. 1025 | Pe = 2 -> left button down. 1026 | Pe = 3 -> left button up. 1027 | Pe = 4 -> middle button down. 1028 | Pe = 5 -> middle button up. 1029 | Pe = 6 -> right button down. 1030 | Pe = 7 -> right button up. 1031 | Pe = 8 -> M4 button down. 1032 | Pe = 9 -> M4 button up. 1033 | Pe = 1 0 -> locator outside filter rectangle. 1034 | The "button" parameter is a bitmask indicating which buttons 1035 | are pressed: 1036 | Pb = 0 <- no buttons down. 1037 | Pb & 1 <- right button down. 1038 | Pb & 2 <- middle button down. 1039 | Pb & 4 <- left button down. 1040 | Pb & 8 <- M4 button down. 1041 | The "row" and "column" parameters are the coordinates of the 1042 | locator position in the xterm window, encoded as ASCII deci- 1043 | mal. 1044 | The "page" parameter is not used by xterm. 1045 | CSI Pm ' } 1046 | Insert Ps Column(s) (default = 1) (DECIC), VT420 and up. 1047 | CSI Pm ' ~ 1048 | Delete Ps Column(s) (default = 1) (DECDC), VT420 and up. 1049 | 1050 |1051 |
OSC Ps ; Pt ST 1053 | OSC Ps ; Pt BEL 1054 | Set Text Parameters. For colors and font, if Pt is a "?", the 1055 | control sequence elicits a response which consists of the con- 1056 | trol sequence which would set the corresponding value. The 1057 | dtterm control sequences allow you to determine the icon name 1058 | and window title. 1059 | Ps = 0 -> Change Icon Name and Window Title to Pt. 1060 | Ps = 1 -> Change Icon Name to Pt. 1061 | Ps = 2 -> Change Window Title to Pt. 1062 | Ps = 3 -> Set X property on top-level window. Pt should be 1063 | in the form "prop=value", or just "prop" to delete the prop- 1064 | erty 1065 | Ps = 4 ; c; spec -> Change Color Number c to the color spec- 1066 | ified by spec. This can be a name or RGB specification as per 1067 | XParseColor. Any number of c/spec pairs may be given. The 1068 | color numbers correspond to the ANSI colors 0-7, their bright 1069 | versions 8-15, and if supported, the remainder of the 88-color 1070 | or 256-color table. 1071 | 1072 | If a "?" is given rather than a name or RGB specification, 1073 | xterm replies with a control sequence of the same form which 1074 | can be used to set the corresponding color. Because more than 1075 | one pair of color number and specification can be given in one 1076 | control sequence, xterm can make more than one reply. 1077 | 1078 | Ps = 5 ; c; spec -> Change Special Color Number c to the 1079 | color specified by spec. This can be a name or RGB specifica- 1080 | tion as per XParseColor. Any number of c/spec pairs may be 1081 | given. The special colors can also be set by adding the maxi- 1082 | mum number of colors to these codes in an OSC 4 control: 1083 | 1084 | Pc = 0 <- resource colorBD (BOLD). 1085 | Pc = 1 <- resource colorUL (UNDERLINE). 1086 | Pc = 2 <- resource colorBL (BLINK). 1087 | Pc = 3 <- resource colorRV (REVERSE). 1088 | Pc = 4 <- resource colorIT (ITALIC). 1089 | 1090 | Ps = 6 ; c; f -> Enable/disable Special Color Number c. OSC 1091 | 6 is the same as OSC 1 0 6 . 1092 | 1093 | The 10 colors (below) which may be set or queried using 1 0 1094 | through 1 9 are denoted dynamic colors, since the correspond- 1095 | ing control sequences were the first means for setting xterm's 1096 | colors dynamically, i.e., after it was started. They are not 1097 | the same as the ANSI colors. These controls may be disabled 1098 | using the allowColorOps resource. At least one parameter is 1099 | expected for Pt. Each successive parameter changes the next 1100 | color in the list. The value of Ps tells the starting point 1101 | in the list. The colors are specified by name or RGB specifi- 1102 | cation as per XParseColor. 1103 | 1104 | If a "?" is given rather than a name or RGB specification, 1105 | xterm replies with a control sequence of the same form which 1106 | can be used to set the corresponding dynamic color. Because 1107 | more than one pair of color number and specification can be 1108 | given in one control sequence, xterm can make more than one 1109 | reply. 1110 | 1111 | Ps = 1 0 -> Change VT100 text foreground color to Pt. 1112 | Ps = 1 1 -> Change VT100 text background color to Pt. 1113 | Ps = 1 2 -> Change text cursor color to Pt. 1114 | Ps = 1 3 -> Change mouse foreground color to Pt. 1115 | Ps = 1 4 -> Change mouse background color to Pt. 1116 | Ps = 1 5 -> Change Tektronix foreground color to Pt. 1117 | Ps = 1 6 -> Change Tektronix background color to Pt. 1118 | Ps = 1 7 -> Change highlight background color to Pt. 1119 | Ps = 1 8 -> Change Tektronix cursor color to Pt. 1120 | Ps = 1 9 -> Change highlight foreground color to Pt. 1121 | 1122 | Ps = 4 6 -> Change Log File to Pt. (This is normally dis- 1123 | abled by a compile-time option). 1124 | 1125 | Ps = 5 0 -> Set Font to Pt. These controls may be disabled 1126 | using the allowFontOps resource. If Pt begins with a "#", 1127 | index in the font menu, relative (if the next character is a 1128 | plus or minus sign) or absolute. A number is expected but not 1129 | required after the sign (the default is the current entry for 1130 | relative, zero for absolute indexing). 1131 | The same rule (plus or minus sign, optional number) is used 1132 | when querying the font. The remainder of Pt is ignored. 1133 | A font can be specified after a "#" index expression, by 1134 | adding a space and then the font specifier. 1135 | If the "TrueType Fonts" menu entry is set (the renderFont 1136 | resource), then this control sets/queries the faceName 1137 | resource. 1138 | 1139 | Ps = 5 1 -> reserved for Emacs shell. 1140 | 1141 | Ps = 5 2 -> Manipulate Selection Data. These controls may 1142 | be disabled using the allowWindowOps resource. The parameter 1143 | Pt is parsed as 1144 | Pc; Pd 1145 | The first, Pc, may contain zero or more characters from the 1146 | set c p s 0 1 2 3 4 5 6 7 . It is used to construct 1147 | a list of selection parameters for clipboard, primary, select, 1148 | or cut buffers 0 through 7 respectively, in the order given. 1149 | If the parameter is empty, xterm uses s 0 , to specify the 1150 | configurable primary/clipboard selection and cut buffer 0. 1151 | The second parameter, Pd, gives the selection data. Normally 1152 | this is a string encoded in base64. The data becomes the new 1153 | selection, which is then available for pasting by other appli- 1154 | cations. 1155 | If the second parameter is a ? , xterm replies to the host 1156 | with the selection data encoded using the same protocol. 1157 | If the second parameter is neither a base64 string nor ? , 1158 | then the selection is cleared. 1159 | 1160 | Ps = 1 0 4 ; c -> Reset Color Number c. It is reset to the 1161 | color specified by the corresponding X resource. Any number 1162 | of c parameters may be given. These parameters correspond to 1163 | the ANSI colors 0-7, their bright versions 8-15, and if sup- 1164 | ported, the remainder of the 88-color or 256-color table. If 1165 | no parameters are given, the entire table will be reset. 1166 | 1167 | Ps = 1 0 5 ; c -> Reset Special Color Number c. It is reset 1168 | to the color specified by the corresponding X resource. Any 1169 | number of c parameters may be given. These parameters corre- 1170 | spond to the special colors which can be set using an OSC 5 1171 | control (or by adding the maximum number of colors using an 1172 | OSC 4 control). 1173 | 1174 | Ps = 1 0 6 ; c; f -> Enable/disable Special Color Number c. 1175 | The second parameter tells xterm to enable the corresponding 1176 | color mode if nonzero, disable it if zero. 1177 | 1178 | Pc = 0 <- resource colorBDMode (BOLD). 1179 | Pc = 1 <- resource colorULMode (UNDERLINE). 1180 | Pc = 2 <- resource colorBLMode (BLINK). 1181 | Pc = 3 <- resource colorRVMode (REVERSE). 1182 | Pc = 4 <- resource colorITMode (ITALIC). 1183 | Pc = 5 <- resource colorAttrMode (Override ANSI). 1184 | 1185 | The dynamic colors can also be reset to their default 1186 | (resource) values: 1187 | Ps = 1 1 0 -> Reset VT100 text foreground color. 1188 | Ps = 1 1 1 -> Reset VT100 text background color. 1189 | Ps = 1 1 2 -> Reset text cursor color. 1190 | Ps = 1 1 3 -> Reset mouse foreground color. 1191 | Ps = 1 1 4 -> Reset mouse background color. 1192 | Ps = 1 1 5 -> Reset Tektronix foreground color. 1193 | Ps = 1 1 6 -> Reset Tektronix background color. 1194 | Ps = 1 1 7 -> Reset highlight color. 1195 | Ps = 1 1 8 -> Reset Tektronix cursor color. 1196 | Ps = 1 1 9 -> Reset highlight foreground color. 1197 | 1198 |1199 |
PM Pt ST xterm implements no PM functions; Pt is ignored. Pt need not 1201 | be printable characters. 1202 | 1203 |1204 |
Many keyboards have keys labeled "Alt". Few have keys labeled "Meta". 1206 | However, xterm's default translations use the Meta modifier. Common 1207 | keyboard configurations assign the Meta modifier to an "Alt" key. By 1208 | using xmodmap one may have the modifier assigned to a different key, and 1209 | have "real" alt and meta keys. Here is an example: 1210 | 1211 | ! put meta on mod3 to distinguish it from alt 1212 | keycode 64 = Alt_L 1213 | clear mod1 1214 | add mod1 = Alt_L 1215 | keycode 115 = Meta_L 1216 | clear mod3 1217 | add mod3 = Meta_L 1218 | 1219 | The metaSendsEscape resource (and altSendsEscape if altIsNotMeta is set) 1220 | can be used to control the way the Meta modifier applies to ordinary 1221 | keys unless the modifyOtherKeys resource is set: 1222 | - prefix a key with the ESC character. 1223 | - shift the key from codes 0-127 to 128-255 by adding 128. 1224 | 1225 | The table shows the result for a given character "x" with modifiers 1226 | according to the default translations with the resources set on or off. 1227 | This assumes altIsNotMeta is set: 1228 | 1229 | ----------------------------------------------------------- 1230 | key altSendsEscape metaSendsEscape result 1231 | -----------+----------------+-----------------+------------ 1232 | x | off | off | x 1233 | Meta-x | off | off | shift 1234 | Alt-x | off | off | shift 1235 | Alt+Meta-x | off | off | shift 1236 | x | ON | off | x 1237 | Meta-x | ON | off | shift 1238 | Alt-x | ON | off | ESC x 1239 | Alt+Meta-x | ON | off | ESC shift 1240 | x | off | ON | x 1241 | Meta-x | off | ON | ESC x 1242 | Alt-x | off | ON | shift 1243 | Alt+Meta-x | off | ON | ESC shift 1244 | x | ON | ON | x 1245 | Meta-x | ON | ON | ESC x 1246 | Alt-x | ON | ON | ESC x 1247 | Alt+Meta-x | ON | ON | ESC x 1248 | -----------+----------------+-----------------+------------ 1249 | 1250 |1251 |
If xterm does minimal translation of the function keys, it usually does 1253 | this with a PC-style keyboard, so PC-style function keys result. Sun 1254 | keyboards are similar to PC keyboards. Both have cursor and scrolling 1255 | operations printed on the keypad, which duplicate the smaller cursor and 1256 | scrolling keypads. 1257 | 1258 | X does not predefine NumLock (used for VT220 keyboards) or Alt (used as 1259 | an extension for the Sun/PC keyboards) as modifiers. These keys are 1260 | recognized as modifiers when enabled by the numLock resource, or by the 1261 | "DECSET 1 0 3 5 " control sequence. 1262 | 1263 | The cursor keys transmit the following escape sequences depending on the 1264 | mode specified via the DECCKM escape sequence. 1265 | 1266 | Key Normal Application 1267 | -------------+----------+------------- 1268 | Cursor Up | CSI A | SS3 A 1269 | Cursor Down | CSI B | SS3 B 1270 | Cursor Right | CSI C | SS3 C 1271 | Cursor Left | CSI D | SS3 D 1272 | -------------+----------+------------- 1273 | 1274 | The home- and end-keys (unlike PageUp and other keys also on the 6-key 1275 | editing keypad) are considered "cursor keys" by xterm. Their mode is 1276 | also controlled by the DECCKM escape sequence: 1277 | 1278 | Key Normal Application 1279 | ---------+----------+------------- 1280 | Home | CSI H | SS3 H 1281 | End | CSI F | SS3 F 1282 | ---------+----------+------------- 1283 | 1284 | The application keypad transmits the following escape sequences depend- 1285 | ing on the mode specified via the DECKPNM and DECKPAM escape sequences. 1286 | Use the NumLock key to override the application mode. 1287 | 1288 | Not all keys are present on the Sun/PC keypad (e.g., PF1, Tab), but are 1289 | supported by the program. 1290 | 1291 | Key Numeric Application Terminfo Termcap 1292 | ---------------+----------+-------------+----------+---------- 1293 | Space | SP | SS3 SP | - | - 1294 | Tab | TAB | SS3 I | - | - 1295 | Enter | CR | SS3 M | kent | @8 1296 | PF1 | SS3 P | SS3 P | kf1 | k1 1297 | PF2 | SS3 Q | SS3 Q | kf2 | k2 1298 | PF3 | SS3 R | SS3 R | kf3 | k3 1299 | PF4 | SS3 S | SS3 S | kf4 | k4 1300 | * (multiply) | * | SS3 j | - | - 1301 | + (add) | + | SS3 k | - | - 1302 | , (comma) | , | SS3 l | - | - 1303 | - (minus) | - | SS3 m | - | - 1304 | . (Delete) | . | CSI 3 ~ | - | - 1305 | / (divide) | / | SS3 o | - | - 1306 | 0 (Insert) | 0 | CSI 2 ~ | - | - 1307 | 1 (End) | 1 | SS3 F | kc1 | K4 1308 | 2 (DownArrow) | 2 | CSI B | - | - 1309 | 3 (PageDown) | 3 | CSI 6 ~ | kc3 | K5 1310 | 4 (LeftArrow) | 4 | CSI D | - | - 1311 | 5 (Begin) | 5 | CSI E | kb2 | K2 1312 | 6 (RightArrow) | 6 | CSI C | - | - 1313 | 7 (Home) | 7 | SS3 H | ka1 | K1 1314 | 8 (UpArrow) | 8 | CSI A | - | - 1315 | 9 (PageUp) | 9 | CSI 5 ~ | ka3 | K3 1316 | = (equal) | = | SS3 X | - | - 1317 | ---------------+----------+-------------+----------+---------- 1318 | 1319 | They also provide 12 function keys, as well as a few other special-pur- 1320 | pose keys: 1321 | 1322 | Key Escape Sequence 1323 | ---------+----------------- 1324 | F1 | SS3 P 1325 | F2 | SS3 Q 1326 | F3 | SS3 R 1327 | F4 | SS3 S 1328 | F5 | CSI 1 5 ~ 1329 | F6 | CSI 1 7 ~ 1330 | F7 | CSI 1 8 ~ 1331 | F8 | CSI 1 9 ~ 1332 | F9 | CSI 2 0 ~ 1333 | F10 | CSI 2 1 ~ 1334 | F11 | CSI 2 3 ~ 1335 | F12 | CSI 2 4 ~ 1336 | ---------+----------------- 1337 | 1338 | Note that F1 through F4 are prefixed with SS3 , while the other keys are 1339 | prefixed with CSI . Older versions of xterm implement different escape 1340 | sequences for F1 through F4, with a CSI prefix. These can be activated 1341 | by setting the oldXtermFKeys resource. However, since they do not cor- 1342 | respond to any hardware terminal, they have been deprecated. (The DEC 1343 | VT220 reserves F1 through F5 for local functions such as Setup). 1344 | 1345 | Key Escape Sequence 1346 | ---------+----------------- 1347 | F1 | CSI 1 1 ~ 1348 | F2 | CSI 1 2 ~ 1349 | F3 | CSI 1 3 ~ 1350 | F4 | CSI 1 4 ~ 1351 | ---------+----------------- 1352 | 1353 | In normal mode, i.e., a Sun/PC keyboard when the sunKeyboard resource is 1354 | false (and none of the other keyboard resources such as oldXtermFKeys 1355 | resource is set), xterm encodes function key modifiers as parameters 1356 | appended before the final character of the control sequence. As a spe- 1357 | cial case, the SS3 sent before F1 through F4 is altered to CSI when 1358 | sending a function key modifier as a parameter. 1359 | 1360 | Code Modifiers 1361 | ---------+--------------------------- 1362 | 2 | Shift 1363 | 3 | Alt 1364 | 4 | Shift + Alt 1365 | 5 | Control 1366 | 6 | Shift + Control 1367 | 7 | Alt + Control 1368 | 8 | Shift + Alt + Control 1369 | 9 | Meta 1370 | 10 | Meta + Shift 1371 | 11 | Meta + Alt 1372 | 12 | Meta + Alt + Shift 1373 | 13 | Meta + Ctrl 1374 | 14 | Meta + Ctrl + Shift 1375 | 15 | Meta + Ctrl + Alt 1376 | 16 | Meta + Ctrl + Alt + Shift 1377 | ---------+--------------------------- 1378 | 1379 | For example, shift-F5 would be sent as CSI 1 5 ; 2 ~ 1380 | 1381 | If the alwaysUseMods resource is set, the Meta modifier also is recog- 1382 | nized, making parameters 9 through 16. 1383 | 1384 |1385 |
However, xterm is most useful as a DEC VT102 or VT220 emulator. Set the 1387 | sunKeyboard resource to true to force a Sun/PC keyboard to act like a 1388 | VT220 keyboard. 1389 | 1390 | The VT102/VT220 application keypad transmits unique escape sequences in 1391 | application mode, which are distinct from the cursor and scrolling key- 1392 | pad: 1393 | 1394 | Key Numeric Application 1395 | -------------+----------+------------- 1396 | Space | SP | SS3 SP 1397 | Tab | TAB | SS3 I 1398 | Enter | CR | SS3 M 1399 | PF1 | SS3 P | SS3 P 1400 | PF2 | SS3 Q | SS3 Q 1401 | PF3 | SS3 R | SS3 R 1402 | PF4 | SS3 S | SS3 S 1403 | * (multiply) | * | SS3 j 1404 | + (add) | + | SS3 k 1405 | , (comma) | , | SS3 l 1406 | - (minus) | - | SS3 m 1407 | . (period) | . | SS3 n 1408 | / (divide) | / | SS3 o 1409 | 0 | 0 | SS3 p 1410 | 1 | 1 | SS3 q 1411 | 2 | 2 | SS3 r 1412 | 3 | 3 | SS3 s 1413 | 4 | 4 | SS3 t 1414 | 5 | 5 | SS3 u 1415 | 6 | 6 | SS3 v 1416 | 7 | 7 | SS3 w 1417 | 8 | 8 | SS3 x 1418 | 9 | 9 | SS3 y 1419 | = (equal) | = | SS3 X 1420 | -------------+----------+------------- 1421 | 1422 | The VT220 provides a 6-key editing keypad, which is analogous to that on 1423 | the PC keyboard. It is not affected by DECCKM or DECKPNM/DECKPAM: 1424 | 1425 | Key Normal Application 1426 | ---------+----------+------------- 1427 | Insert | CSI 2 ~ | CSI 2 ~ 1428 | Delete | CSI 3 ~ | CSI 3 ~ 1429 | Home | CSI 1 ~ | CSI 1 ~ 1430 | End | CSI 4 ~ | CSI 4 ~ 1431 | PageUp | CSI 5 ~ | CSI 5 ~ 1432 | PageDown | CSI 6 ~ | CSI 6 ~ 1433 | ---------+----------+------------- 1434 | 1435 | The VT220 provides 8 additional function keys. With a Sun/PC keyboard, 1436 | access these keys by Control/F1 for F13, etc. 1437 | 1438 | Key Escape Sequence 1439 | ---------+----------------- 1440 | F13 | CSI 2 5 ~ 1441 | F14 | CSI 2 6 ~ 1442 | F15 | CSI 2 8 ~ 1443 | F16 | CSI 2 9 ~ 1444 | F17 | CSI 3 1 ~ 1445 | F18 | CSI 3 2 ~ 1446 | F19 | CSI 3 3 ~ 1447 | F20 | CSI 3 4 ~ 1448 | ---------+----------------- 1449 | 1450 |1451 |
A VT52 does not have function keys, but it does have a numeric keypad 1453 | and cursor keys. They differ from the other emulations by the prefix. 1454 | Also, the cursor keys do not change: 1455 | 1456 | Key Normal/Application 1457 | -------------+-------------------- 1458 | Cursor Up | ESC A 1459 | Cursor Down | ESC B 1460 | Cursor Right | ESC C 1461 | Cursor Left | ESC D 1462 | -------------+-------------------- 1463 | 1464 | The keypad is similar: 1465 | 1466 | Key Numeric Application 1467 | -------------+----------+------------- 1468 | Space | SP | ESC ? SP 1469 | Tab | TAB | ESC ? I 1470 | Enter | CR | ESC ? M 1471 | PF1 | ESC P | ESC P 1472 | PF2 | ESC Q | ESC Q 1473 | PF3 | ESC R | ESC R 1474 | PF4 | ESC S | ESC S 1475 | * (multiply) | * | ESC ? j 1476 | + (add) | + | ESC ? k 1477 | , (comma) | , | ESC ? l 1478 | - (minus) | - | ESC ? m 1479 | . (period) | . | ESC ? n 1480 | / (divide) | / | ESC ? o 1481 | 0 | 0 | ESC ? p 1482 | 1 | 1 | ESC ? q 1483 | 2 | 2 | ESC ? r 1484 | 3 | 3 | ESC ? s 1485 | 4 | 4 | ESC ? t 1486 | 5 | 5 | ESC ? u 1487 | 6 | 6 | ESC ? v 1488 | 7 | 7 | ESC ? w 1489 | 8 | 8 | ESC ? x 1490 | 9 | 9 | ESC ? y 1491 | = (equal) | = | ESC ? X 1492 | -------------+----------+------------- 1493 | 1494 |1495 |
The xterm program provides support for Sun keyboards more directly, by a 1497 | menu toggle that causes it to send Sun-style function key codes rather 1498 | than VT220. Note, however, that the sun and VT100 emulations are not 1499 | really compatible. For example, their wrap-margin behavior differs. 1500 | 1501 | Only function keys are altered; keypad and cursor keys are the same. 1502 | The emulation responds identically. See the xterm-sun terminfo entry 1503 | for details. 1504 | 1505 |1506 |
Similarly, xterm can be compiled to support HP keyboards. See the 1508 | xterm-hp terminfo entry for details. 1509 | 1510 |1511 |
Xterm maintains two screen buffers. The normal screen buffer allows you 1513 | to scroll back to view saved lines of output up to the maximum set by 1514 | the saveLines resource. The alternate screen buffer is exactly as large 1515 | as the display, contains no additional saved lines. When the alternate 1516 | screen buffer is active, you cannot scroll back to view saved lines. 1517 | Xterm provides control sequences and menu entries for switching between 1518 | the two. 1519 | 1520 | Most full-screen applications use terminfo or termcap to obtain strings 1521 | used to start/stop full-screen mode, i.e., smcup and rmcup for terminfo, 1522 | or the corresponding ti and te for termcap. The titeInhibit resource 1523 | removes the ti and te strings from the TERMCAP string which is set in 1524 | the environment for some platforms. That is not done when xterm is 1525 | built with terminfo libraries because terminfo does not provide the 1526 | whole text of the termcap data in one piece. It would not work for ter- 1527 | minfo anyway, since terminfo data is not passed in environment vari- 1528 | ables; setting an environment variable in this manner would have no 1529 | effect on the application's ability to switch between normal and alter- 1530 | nate screen buffers. Instead, the newer private mode controls (such as 1531 | 1 0 4 9 ) for switching between normal and alternate screen buffers sim- 1532 | ply disable the switching. They add other features such as clearing the 1533 | display for the same reason: to make the details of switching indepen- 1534 | dent of the application that requests the switch. 1535 | 1536 |1537 |
When bracketed paste mode is set, pasted text is bracketed with control 1539 | sequences so that the program can differentiate pasted text from typed- 1540 | in text. When bracketed paste mode is set, the program will receive: 1541 | ESC [ 2 0 0 ~ , 1542 | followed by the pasted text, followed by 1543 | ESC [ 2 0 1 ~ . 1544 | 1545 |1546 |
The window- and icon-labels can be set or queried using control 1548 | sequences. As a VT220-emulator, xterm "should" limit the character 1549 | encoding for the corresponding strings to ISO-8859-1. Indeed, it used 1550 | to be the case (and was documented) that window titles had to be 1551 | ISO-8859-1. This is no longer the case. However, there are many appli- 1552 | cations which still assume that titles are set using ISO-8859-1. So 1553 | that is the default behavior. 1554 | 1555 | If xterm is running with UTF-8 encoding, it is possible to use window- 1556 | and icon-labels encoded using UTF-8. That is because the underlying X 1557 | libraries (and many, but not all) window managers support this feature. 1558 | 1559 | The utf8Title X resource setting tells xterm to disable a reconversion 1560 | of the title string back to ISO-8859-1, allowing the title strings to be 1561 | interpreted as UTF-8. The same feature can be enabled using the title 1562 | mode control sequence described in this summary. 1563 | 1564 | Separate from the ability to set the titles, xterm provides the ability 1565 | to query the titles, returning them either in ISO-8859-1 or UTF-8. This 1566 | choice is available only while xterm is using UTF-8 encoding. 1567 | 1568 | Finally, the characters sent to, or returned by a title control are less 1569 | constrained than the rest of the control sequences. To make them more 1570 | manageable (and constrained), for use in shell scripts, xterm has an 1571 | optional feature which decodes the string from hexadecimal (for setting 1572 | titles) or for encoding the title into hexadecimal when querying the 1573 | value. 1574 | 1575 |1576 |
The VT widget can be set to send the mouse position and other informa- 1578 | tion on button presses. These modes are typically used by editors and 1579 | other full-screen applications that want to make use of the mouse. 1580 | 1581 | There are two sets of mutually exclusive modes: 1582 | o mouse protocol 1583 | o protocol encoding 1584 | 1585 | The mouse protocols include DEC Locator mode, enabled by the DECELR CSI 1586 | Ps ; Ps ' z control sequence, and is not described here (control 1587 | sequences are summarized above). The remaining five modes of the mouse 1588 | protocols are each enabled (or disabled) by a different parameter in the 1589 | "DECSET CSI ? Pm h " or "DECRST CSI ? Pm l " control sequence. 1590 | 1591 | Manifest constants for the parameter values are defined in xcharmouse.h 1592 | as follows: 1593 | 1594 | #define SET_X10_MOUSE 9 1595 | #define SET_VT200_MOUSE 1000 1596 | #define SET_VT200_HIGHLIGHT_MOUSE 1001 1597 | #define SET_BTN_EVENT_MOUSE 1002 1598 | #define SET_ANY_EVENT_MOUSE 1003 1599 | 1600 | #define SET_FOCUS_EVENT_MOUSE 1004 1601 | 1602 | #define SET_EXT_MODE_MOUSE 1005 1603 | #define SET_SGR_EXT_MODE_MOUSE 1006 1604 | #define SET_URXVT_EXT_MODE_MOUSE 1015 1605 | 1606 | #define SET_ALTERNATE_SCROLL 1007 1607 | 1608 | The motion reporting modes are strictly xterm extensions, and are not 1609 | part of any standard, though they are analogous to the DEC VT200 DECELR 1610 | locator reports. 1611 | 1612 | Normally, parameters (such as pointer position and button number) for 1613 | all mouse tracking escape sequences generated by xterm encode numeric 1614 | parameters in a single character as value+32. For example, ! specifies 1615 | the value 1. The upper left character position on the terminal is 1616 | denoted as 1,1. This scheme dates back to X10, though the normal mouse- 1617 | tracking (from X11) is more elaborate. 1618 |1619 |
X10 compatibility mode sends an escape sequence only on button press, 1621 | encoding the location and the mouse button pressed. It is enabled by 1622 | specifying parameter 9 to DECSET. On button press, xterm sends CSI M 1623 | CbCxCy (6 characters). 1624 | o Cb is button-1. 1625 | o Cx and Cy are the x and y coordinates of the mouse when the button was 1626 | pressed. 1627 |1628 |
Normal tracking mode sends an escape sequence on both button press and 1630 | release. Modifier key (shift, ctrl, meta) information is also sent. It 1631 | is enabled by specifying parameter 1000 to DECSET. On button press or 1632 | release, xterm sends CSI M CbCxCy. 1633 | o The low two bits of Cb encode button information: 0=MB1 pressed, 1=MB2 1634 | pressed, 2=MB3 pressed, 3=release. 1635 | o The next three bits encode the modifiers which were down when the but- 1636 | ton was pressed and are added together: 4=Shift, 8=Meta, 16=Control. 1637 | Note however that the shift and control bits are normally unavailable 1638 | because xterm uses the control modifier with mouse for popup menus, 1639 | and the shift modifier is used in the default translations for button 1640 | events. The Meta modifier recognized by xterm is the mod1 mask, and 1641 | is not necessarily the "Meta" key (see xmodmap). 1642 | o Cx and Cy are the x and y coordinates of the mouse event, encoded as 1643 | in X10 mode. 1644 |1645 |
Wheel mice may return buttons 4 and 5. Those buttons are represented by 1647 | the same event codes as buttons 1 and 2 respectively, except that 64 is 1648 | added to the event code. Release events for the wheel buttons are not 1649 | reported. By default, the wheel mouse events are translated to scroll- 1650 | back and scroll-forw actions. Those actions normally scroll the whole 1651 | window, as if the scrollbar was used. However if Alternate Scroll mode 1652 | is set, then cursor up/down controls are sent when the terminal is dis- 1653 | playing the alternate screen. The initial state of Alternate Scroll 1654 | mode is set using the alternateScroll resource. 1655 |1656 |
Mouse highlight tracking notifies a program of a button press, receives 1658 | a range of lines from the program, highlights the region covered by the 1659 | mouse within that range until button release, and then sends the program 1660 | the release coordinates. It is enabled by specifying parameter 1001 to 1661 | DECSET. Highlighting is performed only for button 1, though other but- 1662 | ton events can be received. 1663 | 1664 | Warning: use of this mode requires a cooperating program or it will hang 1665 | xterm. 1666 | 1667 | On button press, the same information as for normal tracking is gener- 1668 | ated; xterm then waits for the program to send mouse tracking informa- 1669 | tion. All X events are ignored until the proper escape sequence is 1670 | received from the pty: CSI Ps ; Ps ; Ps ; Ps ; Ps T . The parameters 1671 | are func, startx, starty, firstrow, and lastrow. func is non-zero to 1672 | initiate highlight tracking and zero to abort. startx and starty give 1673 | the starting x and y location for the highlighted region. The ending 1674 | location tracks the mouse, but will never be above row firstrow and will 1675 | always be above row lastrow. (The top of the screen is row 1.) When 1676 | the button is released, xterm reports the ending position one of two 1677 | ways: 1678 | o if the start and end coordinates are the same locations: 1679 | CSI t CxCy. 1680 | o otherwise: 1681 | CSI T CxCyCxCyCxCy. 1682 | The parameters are startx, starty, endx, endy, mousex, and mousey. 1683 | - startx, starty, endx, and endy give the starting and ending charac- 1684 | ter positions of the region. 1685 | - mousex and mousey give the location of the mouse at button up, which 1686 | may not be over a character. 1687 |1688 |
Button-event tracking is essentially the same as normal tracking, but 1690 | xterm also reports button-motion events. Motion events are reported 1691 | only if the mouse pointer has moved to a different character cell. It 1692 | is enabled by specifying parameter 1002 to DECSET. On button press or 1693 | release, xterm sends the same codes used by normal tracking mode. 1694 | o On button-motion events, xterm adds 32 to the event code (the third 1695 | character, Cb). 1696 | o The other bits of the event code specify button and modifier keys as 1697 | in normal mode. For example, motion into cell x,y with button 1 down 1698 | is reported as CSI M @ CxCy. ( @ = 32 + 0 (button 1) + 32 (motion 1699 | indicator) ). Similarly, motion with button 3 down is reported as CSI 1700 | M B CxCy. ( B = 32 + 2 (button 3) + 32 (motion indicator) ). 1701 |1702 |
Any-event mode is the same as button-event mode, except that all motion 1704 | events are reported, even if no mouse button is down. It is enabled by 1705 | specifying 1003 to DECSET. 1706 |1707 |
FocusIn/FocusOut can be combined with any of the mouse events since it 1709 | uses a different protocol. When set, it causes xterm to send CSI I 1710 | when the terminal gains focus, and CSI O when it loses focus. 1711 |1712 |
The original X10 mouse protocol limits the Cx and Cy ordinates to 223 1714 | (=255 - 32). Xterm supports more than one scheme for extending this 1715 | range, by changing the protocol encoding: 1716 | UTF-8 (1005) 1717 | This enables UTF-8 encoding for Cx and Cy under all tracking 1718 | modes, expanding the maximum encodable position from 223 to 1719 | 2015. For positions less than 95, the resulting output is 1720 | identical under both modes. Under extended mouse mode, posi- 1721 | tions greater than 95 generate "extra" bytes which will con- 1722 | fuse applications which do not treat their input as a UTF-8 1723 | stream. Likewise, Cb will be UTF-8 encoded, to reduce confu- 1724 | sion with wheel mouse events. 1725 | Under normal mouse mode, positions outside (160,94) result in 1726 | byte pairs which can be interpreted as a single UTF-8 charac- 1727 | ter; applications which do treat their input as UTF-8 will 1728 | almost certainly be confused unless extended mouse mode is 1729 | active. 1730 | This scheme has the drawback that the encoded coordinates will 1731 | not pass through luit unchanged, e.g., for locales using non- 1732 | UTF-8 encoding. 1733 | SGR (1006) 1734 | The normal mouse response is altered to use CSI < followed by 1735 | semicolon-separated encoded button value, the Cx and Cy ordi- 1736 | nates and a final character which is M for button press and m 1737 | for button release. 1738 | o The encoded button value in this case does not add 32 since 1739 | that was useful only in the X10 scheme for ensuring that the 1740 | byte containing the button value is a printable code. 1741 | o The modifiers are encoded in the same way. 1742 | o A different final character is used for button release to 1743 | resolve the X10 ambiguity regarding which button was 1744 | released. 1745 | The highlight tracking responses are also modified to an SGR- 1746 | like format, using the same SGR-style scheme and button-encod- 1747 | ings. 1748 | URXVT (1015) 1749 | The normal mouse response is altered to use CSI followed by 1750 | semicolon-separated encoded button value, the Cx and Cy ordi- 1751 | nates and final character M . 1752 | This uses the same button encoding as X10, but printing it as 1753 | a decimal integer rather than as a single byte. 1754 | However, CSI M can be mistaken for DL (delete lines), while 1755 | the highlight tracking CSI T can be mistaken for SD (scroll 1756 | down), and the Window manipulation controls. For these rea- 1757 | sons, the 1015 control is not recommended; it is not an 1758 | improvement over 1005. 1759 | 1760 |1761 |
If xterm is configured as VT240, VT241, VT330, VT340 or VT382 using the 1763 | decTerminalID resource, it supports Sixel Graphics controls, a palleted 1764 | bitmap graphics system using sets of six vertical pixels as the basic 1765 | element. 1766 | 1767 | CSI Ps c xterm responds to Send Device Attributes (Primary DA) with 1768 | these additional codes: 1769 | Ps = 4 -> Sixel graphics. 1770 | CSI ? Pm h 1771 | xterm has these additional private Set Mode values: 1772 | Ps = 8 0 -> Sixel scrolling. 1773 | Ps = 1 0 7 0 -> use private color registers for each 1774 | graphic. 1775 | Ps = 8 4 5 2 -> Sixel scrolling leaves cursor to right of 1776 | graphic. 1777 | DCS Pa; Pb; Ph q Ps..Ps ST 1778 | See 1779 | 1780 | http://vt100.net/docs/vt3xx-gp/chapter14.html 1781 | 1782 | The sixel data device control string has three positional 1783 | parameters, following the q with sixel data. 1784 | Pa -> pixel aspect ratio 1785 | Pb -> background color option 1786 | Ph -> horizontal grid size (ignored). 1787 | Ps -> sixel data 1788 | 1789 |1790 |
If xterm is configured as VT125, VT240, VT241, VT330 or VT340 using the 1792 | decTerminalID resource, it supports Remote Graphic Instruction Set, a 1793 | graphics description language. 1794 | 1795 | CSI Ps c xterm responds to Send Device Attributes (Primary DA) with 1796 | these additional codes: 1797 | Ps = 3 -> ReGIS graphics. 1798 | CSI ? Pm h 1799 | xterm has these additional private Set Mode values: 1800 | Ps = 1 0 7 0 -> use private color registers for each 1801 | graphic. 1802 | DCS Pm p Pr..Pr ST 1803 | See 1804 | 1805 | http://vt100.net/docs/vt3xx-gp/chapter1.html 1806 | 1807 | The ReGIS data device control string has one positional param- 1808 | eter with four possible values: 1809 | Pm = 0 -> resume command, use fullscreen mode 1810 | Pm = 1 -> start new command, use fullscreen mode 1811 | Pm = 2 -> resume command, use command display mode 1812 | Pm = 3 -> start new command, use command display mode 1813 | 1814 |1815 |
Most of these sequences are standard Tektronix 4014 control sequences. 1817 | Graph mode supports the 12-bit addressing of the Tektronix 4014. The 1818 | major features missing are the write-through and defocused modes. This 1819 | document does not describe the commands used in the various Tektronix 1820 | plotting modes but does describe the commands to switch modes. 1821 | 1822 | BEL Bell (Ctrl-G). 1823 | BS Backspace (Ctrl-H). 1824 | TAB Horizontal Tab (Ctrl-I). 1825 | LF Line Feed or New Line (Ctrl-J). 1826 | VT Cursor up (Ctrl-K). 1827 | FF Form Feed or New Page (Ctrl-L). 1828 | CR Carriage Return (Ctrl-M). 1829 | ESC ETX Switch to VT100 Mode (ESC Ctrl-C). 1830 | ESC ENQ Return Terminal Status (ESC Ctrl-E). 1831 | ESC FF PAGE (Clear Screen) (ESC Ctrl-L). 1832 | ESC SO Begin 4015 APL mode (ESC Ctrl-N). (This is ignored by 1833 | xterm). 1834 | ESC SI End 4015 APL mode (ESC Ctrl-O). (This is ignored by xterm). 1835 | ESC ETB COPY (Save Tektronix Codes to file COPYyyyy-mm-dd.hh:mm:ss). 1836 | ETB (end transmission block) is the same as Ctrl-W. 1837 | ESC CAN Bypass Condition (ESC Ctrl-X). 1838 | ESC SUB GIN mode (ESC Ctrl-Z). 1839 | ESC FS Special Point Plot Mode (ESC Ctrl-\). 1840 | ESC 8 Select Large Character Set. 1841 | ESC 9 Select #2 Character Set. 1842 | ESC : Select #3 Character Set. 1843 | ESC ; Select Small Character Set. 1844 | OSC Ps ; Pt BEL 1845 | Set Text Parameters of VT window. 1846 | Ps = 0 -> Change Icon Name and Window Title to Pt. 1847 | Ps = 1 -> Change Icon Name to Pt. 1848 | Ps = 2 -> Change Window Title to Pt. 1849 | Ps = 4 6 -> Change Log File to Pt. (This is normally dis- 1850 | abled by a compile-time option). 1851 | ESC ` Normal Z Axis and Normal (solid) Vectors. 1852 | ESC a Normal Z Axis and Dotted Line Vectors. 1853 | ESC b Normal Z Axis and Dot-Dashed Vectors. 1854 | ESC c Normal Z Axis and Short-Dashed Vectors. 1855 | ESC d Normal Z Axis and Long-Dashed Vectors. 1856 | ESC h Defocused Z Axis and Normal (solid) Vectors. 1857 | ESC i Defocused Z Axis and Dotted Line Vectors. 1858 | ESC j Defocused Z Axis and Dot-Dashed Vectors. 1859 | ESC k Defocused Z Axis and Short-Dashed Vectors. 1860 | ESC l Defocused Z Axis and Long-Dashed Vectors. 1861 | ESC p Write-Thru Mode and Normal (solid) Vectors. 1862 | ESC q Write-Thru Mode and Dotted Line Vectors. 1863 | ESC r Write-Thru Mode and Dot-Dashed Vectors. 1864 | ESC s Write-Thru Mode and Short-Dashed Vectors. 1865 | ESC t Write-Thru Mode and Long-Dashed Vectors. 1866 | FS Point Plot Mode (Ctrl-\). 1867 | GS Graph Mode (Ctrl-]). 1868 | RS Incremental Plot Mode (Ctrl-^). 1869 | US Alpha Mode (Ctrl-_). 1870 | 1871 |1872 |
Parameters for cursor movement are at the end of the ESC Y escape 1874 | sequence. Each ordinate is encoded in a single character as value+32. 1875 | For example, ! is 1. The screen coordinate system is 0-based. 1876 | 1877 | ESC A Cursor up. 1878 | ESC B Cursor down. 1879 | ESC C Cursor right. 1880 | ESC D Cursor left. 1881 | ESC F Enter graphics mode. 1882 | ESC G Exit graphics mode. 1883 | ESC H Move the cursor to the home position. 1884 | ESC I Reverse line feed. 1885 | ESC J Erase from the cursor to the end of the screen. 1886 | ESC K Erase from the cursor to the end of the line. 1887 | ESC Y Ps Ps 1888 | Move the cursor to given row and column. 1889 | ESC Z Identify. 1890 | -> ESC / Z ("I am a VT52."). 1891 | ESC = Enter alternate keypad mode. 1892 | ESC > Exit alternate keypad mode. 1893 | ESC < Exit VT52 mode (Enter VT100 mode). 1894 |1895 | 1933 | 1934 | 1935 | -------------------------------------------------------------------------------- /doc/ctlseqs(ms)_files/man2html.css: -------------------------------------------------------------------------------- 1 | body { 2 | /* Verdana, - too wide */ 3 | /* Lucida Grande, - cluttered */ 4 | /* Droid Serif - ? */ 5 | /* Optima, - maybe - seems light */ 6 | /* Verdana, - large */ 7 | /* Tahoma, - not bad */ 8 | font-family: Tahoma, Georgia, "Times New Roman", Times, serif; 9 | } 10 | 11 | h1,h2,h3,h4 { 12 | font-family: Helvetica, Geneva, Arial, SunSans-Regular, sans-serif; 13 | margin: 0px; 14 | line-height: 100%; 15 | } 16 | 17 | h3,h4 { 18 | margin-left: 30px; 19 | } 20 | 21 | pre, 22 | code, 23 | kbd, 24 | samp, 25 | tt{ 26 | /* font-family:monospace,monospace; */ 27 | font-family: "Andale Mono", "Monotype.com", monospace; 28 | font-size:1em; 29 | padding: 0; 30 | margin: 0; 31 | padding: 0; 32 | } 33 | 34 | *.no-header { 35 | visibility:hidden; 36 | overflow:hidden; 37 | float:left; 38 | clear: both; 39 | width: 1px; 40 | height: 1px; 41 | } 42 | 43 | pre { 44 | white-space: pre-wrap; /* CSS 3 */ 45 | white-space: -moz-pre-wrap; /* Mozilla, since 1999 */ 46 | white-space: -pre-wrap; /* Opera 4-6 */ 47 | white-space: -o-pre-wrap; /* Opera 7 */ 48 | word-wrap: break-word; /* Internet Explorer 5.5+ */ 49 | } 50 | -------------------------------------------------------------------------------- /gui/__init__.py: -------------------------------------------------------------------------------- 1 | from .terminal import TerminalWidget 2 | -------------------------------------------------------------------------------- /gui/font.py: -------------------------------------------------------------------------------- 1 | from PyQt5.QtGui import QFont, QFontMetricsF 2 | 3 | class MonoFont: 4 | def __init__(self, name, size): 5 | self.font = QFont(name, size) 6 | self.font.setKerning(False) 7 | self.ascent = int(QFontMetricsF(self.font).ascent()) 8 | self.charWidth = QFontMetricsF(self.font).width("X") 9 | self.charHeight = int(QFontMetricsF(self.font).height()) 10 | self.charOffset = 0 # can introduce extra linespacing here 11 | 12 | self.bold = QFont(self.font) 13 | self.bold.setBold(True) 14 | 15 | self.under = QFont(self.font) 16 | self.under.setUnderline(True) 17 | 18 | # align character width properly 19 | if self.charWidth % 1.0 < 0.5: 20 | adjust = -(self.charWidth % 1.0) 21 | else: 22 | adjust = 1.0 - (self.charWidth % 1.0) 23 | 24 | self.charWidth += adjust 25 | self.font.setLetterSpacing(QFont.AbsoluteSpacing, adjust) 26 | self.bold.setLetterSpacing(QFont.AbsoluteSpacing, adjust) 27 | self.under.setLetterSpacing(QFont.AbsoluteSpacing, adjust) 28 | 29 | 30 | -------------------------------------------------------------------------------- /gui/terminal.py: -------------------------------------------------------------------------------- 1 | from PyQt5.QtCore import * 2 | from PyQt5.QtGui import * 3 | from PyQt5.QtWidgets import QAbstractScrollArea, QFrame 4 | 5 | from . import font 6 | from terminal import emulator, process, screen, rendition 7 | 8 | class TerminalWidget(QAbstractScrollArea): 9 | def __init__(self, parent): 10 | super(TerminalWidget, self).__init__(parent) 11 | 12 | self.setFrameStyle(QFrame.NoFrame) 13 | self.historySize = 0 14 | self.cols, self.rows = 80, 25 15 | 16 | cmd = "/bin/bash" 17 | self.process = process.TerminalProcessThread(self.rows, self.cols, cmd) 18 | self.process.emulator.set_update_callback(self.update_screen) 19 | self.process.emulator.set_data_callback(self.update_data) 20 | self.process.emulator.set_title_callback(self.update_title) 21 | self.process.start() 22 | 23 | self.font = font.MonoFont("Monospace", 14) 24 | 25 | self.setVerticalScrollBarPolicy(Qt.ScrollBarAlwaysOn) 26 | self.setViewportMargins(2, 2, 2, 2) 27 | 28 | p = QPalette(self.palette()) 29 | p.setColor(QPalette.Background, Qt.black) 30 | self.setPalette(p) 31 | self.setAutoFillBackground(True) 32 | 33 | # XXX platform dependent 34 | self.ctrl = Qt.ControlModifier 35 | 36 | self.cursorRow = 0 37 | self.blink = False 38 | self.cursorBlink = QTimer() 39 | self.cursorBlink.setInterval(500) 40 | self.cursorBlink.setSingleShot(False) 41 | self.cursorBlink.timeout.connect(self.cursorBlinkEvent) 42 | self.cursorBlink.start() 43 | self.caretVisible = False 44 | 45 | def renderCursor(self): 46 | self.viewport().update(0, self.cursorRow * self.font.charHeight, 47 | self.viewport().size().width(), self.font.charHeight) 48 | self.cursorRow = self.process.emulator.cursor.y 49 | self.viewport().update(0, self.cursorRow * self.font.charHeight, 50 | self.viewport().size().width(), self.font.charHeight) 51 | 52 | def cursorBlinkEvent(self): 53 | self.blink = not self.blink 54 | self.renderCursor() 55 | 56 | def update_screen(self): 57 | self.viewport().update() 58 | 59 | def update_title(self, title): 60 | pass 61 | 62 | def update_data(self, data): 63 | pass 64 | 65 | def event(self, event): 66 | t = event.type() 67 | if t == QEvent.KeyPress and (event.key() == Qt.Key_Tab or event.key() == Qt.Key_Backtab): 68 | self.keyPressEvent(event) 69 | return True 70 | if t == QEvent.ShortcutOverride and event.key() >= Qt.Key_A and event.key() <= Qt.Key_Z: 71 | if (event.modifiers() & (self.ctrl | Qt.AltModifier | Qt.ShiftModifier)) == self.ctrl: 72 | event.accept() 73 | return True 74 | return super(TerminalWidget, self).event(event) 75 | 76 | def getKeyModifierString(self, mod): 77 | if (mod & (self.ctrl | Qt.ShiftModifier | Qt.AltModifier)) == (Qt.ShiftModifier): 78 | return ";2" 79 | if (mod & (self.ctrl | Qt.ShiftModifier | Qt.AltModifier)) == (Qt.AltModifier): 80 | return ";3" 81 | if (mod & (self.ctrl | Qt.ShiftModifier | Qt.AltModifier)) == (Qt.ShiftModifier | Qt.AltModifier): 82 | return ";4" 83 | if (mod & (self.ctrl | Qt.ShiftModifier | Qt.AltModifier)) == (self.ctrl): 84 | return ";5" 85 | if (mod & (self.ctrl | Qt.ShiftModifier | Qt.AltModifier)) == (Qt.ShiftModifier | self.ctrl): 86 | return ";6" 87 | if (mod & (self.ctrl | Qt.ShiftModifier | Qt.AltModifier)) == (Qt.AltModifier | self.ctrl): 88 | return ";7" 89 | if (mod & (self.ctrl | Qt.ShiftModifier | Qt.AltModifier)) == (Qt.ShiftModifier | Qt.AltModifier | self.ctrl): 90 | return ";8" 91 | return "" 92 | 93 | def keyPressEvent(self, event): 94 | key_data = { 95 | Qt.Key_F5:"15", 96 | Qt.Key_F6:"17", 97 | Qt.Key_F7:"18", 98 | Qt.Key_F8:"19", 99 | Qt.Key_F9:"20", 100 | Qt.Key_F10:"21", 101 | Qt.Key_F11:"23", 102 | Qt.Key_F12:"24", 103 | Qt.Key_Delete:"3", 104 | } 105 | arrow_keys = { 106 | Qt.Key_Up:"A", 107 | Qt.Key_Down:"B", 108 | Qt.Key_Right:"C", 109 | Qt.Key_Left:"D" 110 | } 111 | ctrl = self.ctrl 112 | alt = Qt.AltModifier 113 | shift = Qt.ShiftModifier 114 | mods = event.modifiers() 115 | modstr = self.getKeyModifierString(mods) 116 | key = event.key() 117 | text = event.text() 118 | data = None 119 | if key in arrow_keys: 120 | if mods & (ctrl|alt|shift): 121 | data = "\033[%s%c" % (modstr, arrow_keys[key]) 122 | else: 123 | data = "\033[%c" % arrow_keys[key] 124 | #XXX if cursor_keys option set 125 | 126 | if key in key_data: 127 | data = "\033[%s%s~" % (key_data[key], modstr) 128 | elif key == Qt.Key_Backspace: 129 | data = "\x7f" 130 | elif key == Qt.Key_Tab: 131 | data = "\t" 132 | elif key == Qt.Key_Backtab: 133 | data = "\033[Z" 134 | elif key >= Qt.Key_A and key <= Qt.Key_Z and (mods & (ctrl|alt|shift))==ctrl: 135 | data = "%c" % (chr(key - Qt.Key_A) + 1) 136 | elif len(text) > 0: 137 | data = event.text() 138 | if len(text) == 1 and text >= ' ' and text <= '~' and mods & alt: 139 | data = "\033" + data 140 | 141 | if not data: 142 | return 143 | 144 | self.process.input(data) 145 | #self.verticalScrollBar().setValue(0) 146 | 147 | def mousePressEvent(self, event): 148 | pass 149 | 150 | def mouseMoveEvent(self, event): 151 | pass 152 | 153 | def mouseReleaseEvent(self, event): 154 | pass 155 | 156 | def mouseDoubleClickEvent(self, event): 157 | pass 158 | 159 | def focusInEvent(self, event): 160 | self.caretVisible = True 161 | self.blink = True 162 | self.cursorBlink.stop() 163 | self.cursorBlink.start() 164 | self.renderCursor() 165 | 166 | def focusOutEvent(self, event): 167 | self.caretVisible = False 168 | self.renderCursor() 169 | 170 | def get_colors(self, gfx): 171 | fg, bg = rendition.get_colors(gfx) 172 | fg = QColor(*fg) if fg else Qt.white 173 | bg = QColor(*bg) if bg else Qt.black 174 | return fg, bg 175 | 176 | def paintRow(self, y, row, rowgfx): 177 | p = QPainter(self.viewport()) 178 | p.setFont(self.font.font) 179 | 180 | bg = Qt.black 181 | fg = Qt.white 182 | length = 0 183 | current = None 184 | x = 0 185 | for i in range(0, self.cols): 186 | gfx = rowgfx[i] 187 | 188 | if self.blink and self.caretVisible and self.process.emulator.cursor.visible and \ 189 | self.process.emulator.cursor.y == y and self.process.emulator.cursor.x == i: 190 | gfx ^= screen.GFX_INV 191 | 192 | 193 | if gfx != current: 194 | if length > 0: 195 | p.fillRect(x * self.font.charWidth, y * self.font.charHeight, 196 | length * self.font.charWidth, self.font.charHeight, bg) 197 | x = x + length 198 | length = 0 199 | current = gfx 200 | fg, bg = self.get_colors(gfx) 201 | length = length + 1 202 | 203 | if length > 0: 204 | p.fillRect(x * self.font.charWidth, y * self.font.charHeight, 205 | length * self.font.charWidth, self.font.charHeight, bg) 206 | 207 | length = 0 208 | current = None 209 | x = 0 210 | for i in range(0, self.cols): 211 | gfx = rowgfx[i] 212 | if gfx != current: 213 | if length > 0: 214 | line = "".join(row[x:i]) 215 | p.setPen(fg) 216 | p.drawText(x * self.font.charWidth, y * self.font.charHeight + self.font.charOffset + self.font.ascent, line) 217 | if gfx & screen.GFX_BOLD: 218 | p.setFont(self.font.bold) 219 | elif gfx & screen.GFX_UL: 220 | p.setFont(self.font.under) 221 | else: 222 | p.setFont(self.font.font) 223 | x = x + length 224 | length = 0 225 | current = gfx 226 | fg, bg = self.get_colors(gfx) 227 | length = length + 1 228 | 229 | if length > 0: 230 | line = "".join(row[x:i]) 231 | p.setPen(fg) 232 | p.drawText(x * self.font.charWidth, y * self.font.charHeight + self.font.charOffset + self.font.ascent, line) 233 | 234 | if not self.caretVisible and self.process.emulator.cursor.y == y: 235 | fg, bg = self.get_colors(rowgfx[self.process.emulator.cursor.x]) 236 | p.setPen(fg) 237 | p.drawRect(x * self.font.charWidth, y * self.font.charHeight, self.font.charWidth -1, self.font.charHeight - 1) 238 | 239 | 240 | def paintEvent(self, event): 241 | p = QPainter(self.viewport()) 242 | p.setFont(self.font.font) 243 | p.fillRect(event.rect(), Qt.black) 244 | 245 | yofs = self.verticalScrollBar().value() 246 | y = event.rect().y() 247 | bot = y + event.rect().height() 248 | top = int(y / self.font.charHeight) 249 | bot = int((bot / self.font.charHeight) + 1) 250 | 251 | screen = self.process.emulator.screen 252 | for y in range(0, self.rows): 253 | self.paintRow(y, screen.cells[y], screen.gfx[y]) 254 | 255 | def resizeEvent(self, event): 256 | sz = event.size() 257 | self.resize(sz.width(), sz.height()) 258 | 259 | def resize(self, w, h): 260 | self.cols = int(w / self.font.charWidth) 261 | self.rows = int(h / self.font.charHeight) 262 | #self.verticalScrollBar().setPageStep(self.rows) 263 | #self.verticalScrollBar().setRange(-self.historySize, 0) 264 | self.process.resize(self.rows, self.cols) 265 | -------------------------------------------------------------------------------- /main.py: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env python3 2 | 3 | import os 4 | import sys 5 | from PyQt5.QtCore import * 6 | from PyQt5.QtGui import * 7 | from PyQt5.QtWidgets import QApplication, QMainWindow, QMenu, QTabWidget 8 | 9 | import terminal 10 | import gui 11 | 12 | class Main(QMainWindow): 13 | def __init__(self, parent=None): 14 | super(Main, self).__init__(parent) 15 | self.setWindowTitle("blabla") 16 | 17 | self.fileMenu = QMenu("&File", self) 18 | self.menuBar().addMenu(self.fileMenu) 19 | 20 | self.tabs = QTabWidget(self) 21 | self.tabs.setDocumentMode(True) 22 | self.tabs.setTabsClosable(True) 23 | self.tabs.setMovable(True) 24 | 25 | frame = gui.TerminalWidget(self) 26 | self.tabs.addTab(frame, 'Terminal #1') 27 | frame = gui.TerminalWidget(self) 28 | self.tabs.addTab(frame, 'Terminal #2') 29 | frame = gui.TerminalWidget(self) 30 | self.tabs.addTab(frame, 'Terminal #3') 31 | 32 | self.setCentralWidget(self.tabs) 33 | 34 | self.show() 35 | 36 | def sizeHint(self): 37 | return QSize(800, 600) 38 | 39 | if __name__ == "__main__": 40 | app = QApplication(sys.argv) 41 | main = Main() 42 | app.exec_() 43 | -------------------------------------------------------------------------------- /src/gui/__init__.py: -------------------------------------------------------------------------------- 1 | from .terminal import TerminalWidget 2 | -------------------------------------------------------------------------------- /src/gui/font.py: -------------------------------------------------------------------------------- 1 | from PySide.QtGui import QFont, QFontMetricsF 2 | 3 | class MonoFont: 4 | def __init__(self, name, size): 5 | self.font = QFont(name, size) 6 | self.font.setKerning(False) 7 | self.ascent = int(QFontMetricsF(self.font).ascent()) 8 | self.charWidth = QFontMetricsF(self.font).width("X") 9 | self.charHeight = int(QFontMetricsF(self.font).height()) 10 | self.charOffset = 0 # can introduce extra linespacing here 11 | 12 | self.bold = QFont(self.font) 13 | self.bold.setBold(True) 14 | 15 | self.under = QFont(self.font) 16 | self.under.setUnderline(True) 17 | 18 | # align character width properly 19 | if self.charWidth % 1.0 < 0.5: 20 | adjust = -(self.charWidth % 1.0) 21 | else: 22 | adjust = 1.0 - (self.charWidth % 1.0) 23 | 24 | self.charWidth += adjust 25 | self.font.setLetterSpacing(QFont.AbsoluteSpacing, adjust) 26 | self.bold.setLetterSpacing(QFont.AbsoluteSpacing, adjust) 27 | self.under.setLetterSpacing(QFont.AbsoluteSpacing, adjust) 28 | 29 | 30 | -------------------------------------------------------------------------------- /src/gui/terminal.py: -------------------------------------------------------------------------------- 1 | from PySide.QtCore import * 2 | from PySide.QtGui import * 3 | 4 | from . import font 5 | from terminal import emulator, screen, rendition 6 | 7 | 8 | class TerminalWidget(QWidget): 9 | def __init__(self, parent, cmd="/bin/sh"): 10 | QWidget.__init__(self, parent) 11 | layout = QBoxLayout(QBoxLayout.TopToBottom) 12 | self.setLayout(layout) 13 | self.w = TerminalRenderWidget(self, cmd) 14 | layout.addWidget(self.w) 15 | self.b = QStatusBar() 16 | layout.addWidget(self.b) 17 | l = QLabel("wut") 18 | #l.setPixmap(QPixmap("exit.png")) 19 | self.b.addPermanentWidget(l) 20 | layout.setContentsMargins(1, 1, 1, 1) 21 | 22 | def update_title(self, title): 23 | print("!!!%s"% title) 24 | return 25 | self.b.showMessage(title) 26 | 27 | class TerminalRenderWidget(QAbstractScrollArea): 28 | 29 | def __init__(self, parent=None, cmd="/bin/sh"): 30 | super(TerminalRenderWidget, self).__init__(parent) 31 | 32 | self.setFrameStyle(QFrame.NoFrame) 33 | self.historySize = 0 34 | 35 | self.rows, self.cols = 25, 80 36 | self.emulator = emulator.Emulator(self.rows, self.cols) 37 | self.emulator.set_update_callback(self.update_screen) 38 | self.emulator.set_title_callback(parent.update_title) 39 | self.emulator.set_data_callback(self.update_data) 40 | self.terminal_id = QApplication.instance().td.startTerminal("/bin/sh", [], self) 41 | 42 | 43 | self.font = font.MonoFont("Monospace", 10) 44 | 45 | self.setVerticalScrollBarPolicy(Qt.ScrollBarAlwaysOn) 46 | self.setViewportMargins(2, 2, 2, 2) 47 | 48 | p = QPalette(self.palette()) 49 | p.setColor(QPalette.Background, Qt.black) 50 | self.setPalette(p) 51 | self.setAutoFillBackground(True) 52 | 53 | # XXX platform dependent 54 | self.ctrl = Qt.ControlModifier 55 | 56 | self.cursorRow = 0 57 | self.blink = False 58 | self.cursorBlink = QTimer() 59 | self.cursorBlink.setInterval(500) 60 | self.cursorBlink.setSingleShot(False) 61 | self.cursorBlink.timeout.connect(self.cursorBlinkEvent) 62 | self.cursorBlink.start() 63 | self.caretVisible = False 64 | self.blink = False 65 | 66 | def exited(self, ok): 67 | print("process exited!!?") 68 | 69 | def renderCursor(self): 70 | self.viewport().update(0, self.cursorRow * self.font.charHeight, 71 | self.viewport().size().width(), self.font.charHeight) 72 | self.cursorRow = self.emulator.cursor.y 73 | self.viewport().update(0, self.cursorRow * self.font.charHeight, 74 | self.viewport().size().width(), self.font.charHeight) 75 | 76 | def cursorBlinkEvent(self): 77 | self.blink = not self.blink 78 | self.renderCursor() 79 | 80 | def update_screen(self): 81 | self.viewport().update() 82 | 83 | def update_data(self, data): 84 | pass 85 | 86 | def event(self, event): 87 | t = event.type() 88 | if t == QEvent.KeyPress and (event.key() == Qt.Key_Tab or event.key() == Qt.Key_Backtab): 89 | self.keyPressEvent(event) 90 | return True 91 | if t == QEvent.ShortcutOverride and event.key() >= Qt.Key_A and event.key() <= Qt.Key_Z: 92 | if (event.modifiers() & (self.ctrl | Qt.AltModifier | Qt.ShiftModifier)) == self.ctrl: 93 | event.accept() 94 | return True 95 | return super(TerminalRenderWidget, self).event(event) 96 | 97 | def getKeyModifierString(self, mod): 98 | if (mod & (self.ctrl | Qt.ShiftModifier | Qt.AltModifier)) == (Qt.ShiftModifier): 99 | return ";2" 100 | if (mod & (self.ctrl | Qt.ShiftModifier | Qt.AltModifier)) == (Qt.AltModifier): 101 | return ";3" 102 | if (mod & (self.ctrl | Qt.ShiftModifier | Qt.AltModifier)) == (Qt.ShiftModifier | Qt.AltModifier): 103 | return ";4" 104 | if (mod & (self.ctrl | Qt.ShiftModifier | Qt.AltModifier)) == (self.ctrl): 105 | return ";5" 106 | if (mod & (self.ctrl | Qt.ShiftModifier | Qt.AltModifier)) == (Qt.ShiftModifier | self.ctrl): 107 | return ";6" 108 | if (mod & (self.ctrl | Qt.ShiftModifier | Qt.AltModifier)) == (Qt.AltModifier | self.ctrl): 109 | return ";7" 110 | if (mod & (self.ctrl | Qt.ShiftModifier | Qt.AltModifier)) == (Qt.ShiftModifier | Qt.AltModifier | self.ctrl): 111 | return ";8" 112 | return "" 113 | 114 | def keyPressEvent(self, event): 115 | key_data = { 116 | Qt.Key_F5:"15", 117 | Qt.Key_F6:"17", 118 | Qt.Key_F7:"18", 119 | Qt.Key_F8:"19", 120 | Qt.Key_F9:"20", 121 | Qt.Key_F10:"21", 122 | Qt.Key_F11:"23", 123 | Qt.Key_F12:"24", 124 | Qt.Key_Delete:"3", 125 | } 126 | arrow_keys = { 127 | Qt.Key_Up:"A", 128 | Qt.Key_Down:"B", 129 | Qt.Key_Right:"C", 130 | Qt.Key_Left:"D" 131 | } 132 | ctrl = self.ctrl 133 | alt = Qt.AltModifier 134 | shift = Qt.ShiftModifier 135 | mods = event.modifiers() 136 | modstr = self.getKeyModifierString(mods) 137 | key = event.key() 138 | text = event.text() 139 | data = None 140 | if key in arrow_keys: 141 | if mods & (ctrl|alt|shift): 142 | data = "\033[%s%c" % (modstr, arrow_keys[key]) 143 | else: 144 | data = "\033[%c" % arrow_keys[key] 145 | #XXX if cursor_keys option set 146 | 147 | if key in key_data: 148 | data = "\033[%s%s~" % (key_data[key], modstr) 149 | elif key == Qt.Key_Backspace: 150 | data = "\x7f" 151 | elif key == Qt.Key_Tab: 152 | data = "\t" 153 | elif key == Qt.Key_Backtab: 154 | data = "\033[Z" 155 | elif key >= Qt.Key_A and key <= Qt.Key_Z and (mods & (ctrl|alt|shift))==ctrl: 156 | try: 157 | data = "%c" % (chr(key-ord('A')+1)) 158 | #data = "%c" % (chr(key - Qt.Key_A) + 1) 159 | except Exception as e: 160 | print(e) 161 | print(key - Qt.Key_A) 162 | print(key, ord('A'), key-ord('A')+1, chr(key-ord('A')+1)) 163 | elif len(text) > 0: 164 | data = event.text() 165 | if len(text) == 1 and text >= ' ' and text <= '~' and mods & alt: 166 | data = "\033" + data 167 | 168 | self.send_input(data) 169 | if not data: 170 | return 171 | 172 | #self.verticalScrollBar().setValue(0) 173 | 174 | def send_input(self, data): 175 | QApplication.instance().td.sendInput(self.terminal_id, data) 176 | self.update_screen() 177 | 178 | def data(self, data): 179 | self.emulator.input_data(data) 180 | self.update_screen() 181 | 182 | def mousePressEvent(self, event): 183 | pass 184 | 185 | def mouseMoveEvent(self, event): 186 | pass 187 | 188 | def mouseReleaseEvent(self, event): 189 | pass 190 | 191 | def mouseDoubleClickEvent(self, event): 192 | pass 193 | 194 | def focusInEvent(self, event): 195 | self.caretVisible = True 196 | self.blink = True 197 | self.cursorBlink.stop() 198 | self.cursorBlink.start() 199 | self.renderCursor() 200 | 201 | def focusOutEvent(self, event): 202 | self.caretVisible = False 203 | self.renderCursor() 204 | 205 | def get_colors(self, gfx): 206 | fg, bg = rendition.get_colors(gfx) 207 | fg = QColor(*fg) if fg else Qt.white 208 | bg = QColor(*bg) if bg else Qt.black 209 | return fg, bg 210 | 211 | def paintRow(self, y, row, rowgfx): 212 | p = QPainter(self.viewport()) 213 | p.setFont(self.font.font) 214 | 215 | # render background 216 | bg, fg = Qt.black, Qt.white 217 | length = 0 218 | current = None 219 | x = 0 220 | for i in range(0, self.cols): 221 | gfx = rowgfx[i] 222 | 223 | # invert colors when rendering the cursor 224 | if self.blink and self.caretVisible and self.emulator.cursor.visible and \ 225 | self.emulator.cursor.y == y and self.emulator.cursor.x == i: 226 | gfx ^= rendition.GFX_INV 227 | 228 | if gfx != current: 229 | if length > 0: 230 | p.fillRect(x * self.font.charWidth, y * self.font.charHeight, 231 | length * self.font.charWidth, self.font.charHeight, bg) 232 | x = x + length 233 | length = 0 234 | current = gfx 235 | fg, bg = self.get_colors(gfx) 236 | length = length + 1 237 | if length > 0: 238 | p.fillRect(x * self.font.charWidth, y * self.font.charHeight, 239 | length * self.font.charWidth, self.font.charHeight, bg) 240 | 241 | # render foreground 242 | bg, fg = Qt.black, Qt.white 243 | length = 0 244 | current = None 245 | x = 0 246 | for i in range(0, self.cols): 247 | gfx = rowgfx[i] 248 | if gfx != current: 249 | if length > 0: 250 | line = "".join(row[x:i]) 251 | p.setPen(fg) 252 | p.drawText(x * self.font.charWidth, y * self.font.charHeight + self.font.charOffset + self.font.ascent, line) 253 | if gfx & rendition.GFX_BOLD: 254 | p.setFont(self.font.bold) 255 | elif gfx & rendition.GFX_UL: 256 | p.setFont(self.font.under) 257 | else: 258 | p.setFont(self.font.font) 259 | x = x + length 260 | length = 0 261 | current = gfx 262 | fg, bg = self.get_colors(gfx) 263 | length = length + 1 264 | if length > 0: 265 | line = "".join(row[x:i]) 266 | p.setPen(fg) 267 | p.drawText(x * self.font.charWidth, y * self.font.charHeight + self.font.charOffset + self.font.ascent, line) 268 | 269 | # draw disabled cursor as the screen's not active right now 270 | if not self.caretVisible and self.emulator.cursor.y == y: 271 | 272 | if self.emulator.cursor.x == len(rowgfx): 273 | fg, bg = self.get_colors(rowgfx[self.emulator.cursor.x-1]) 274 | elif self.emulator.cursor.x > len(rowgfx): 275 | print(self.emulator.cursor.x, len(rowgfx)) 276 | print(rowgfx) 277 | raise Exception("") 278 | else: 279 | fg, bg = self.get_colors(rowgfx[self.emulator.cursor.x]) 280 | p.setPen(fg) 281 | p.drawRect(x * self.font.charWidth, y * self.font.charHeight, self.font.charWidth -1, self.font.charHeight - 1) 282 | 283 | 284 | def paintEvent(self, event): 285 | p = QPainter(self.viewport()) 286 | p.setFont(self.font.font) 287 | p.fillRect(event.rect(), Qt.black) 288 | 289 | yofs = self.verticalScrollBar().value() 290 | y = event.rect().y() 291 | bot = y + event.rect().height() 292 | top = int(y / self.font.charHeight) 293 | bot = int((bot / self.font.charHeight) + 1) 294 | 295 | screen = self.emulator.screen 296 | for y in range(0, self.rows): 297 | self.paintRow(y, screen.cells[y], screen.gfx[y]) 298 | 299 | def closeRequest(self): 300 | print("clooooooooose") 301 | #self.process.kill() 302 | 303 | def resizeEvent(self, event): 304 | sz = event.size() 305 | self.resize(sz.width(), sz.height()) 306 | 307 | def resize(self, w, h): 308 | self.cols = int(w / self.font.charWidth) 309 | self.rows = int(h / self.font.charHeight) 310 | #self.verticalScrollBar().setPageStep(self.rows) 311 | #self.verticalScrollBar().setRange(-self.historySize, 0) 312 | self.emulator.resize(self.rows, self.cols) 313 | QApplication.instance().td.resizeTerminal(self.terminal_id, self.rows, self.cols) 314 | -------------------------------------------------------------------------------- /src/main.py: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env python3 2 | 3 | import sys, argparse 4 | 5 | from PySide.QtCore import * 6 | from PySide.QtGui import * 7 | 8 | import terminal 9 | import gui 10 | 11 | arg_ns = None 12 | 13 | class Main(QMainWindow): 14 | def __init__(self, parent=None): 15 | super(Main, self).__init__(parent) 16 | self.setWindowTitle("blabla") 17 | 18 | self.fileMenu = QMenu("&File", self) 19 | self.menuBar().addMenu(self.fileMenu) 20 | 21 | self.tabs = QTabWidget(self) 22 | self.tabs.setDocumentMode(True) 23 | self.tabs.setTabsClosable(True) 24 | self.tabs.setMovable(True) 25 | 26 | global arg_ns 27 | if arg_ns.terminal_enabled: 28 | frame = gui.TerminalWidget(self, "/bin/sh") 29 | self.tabs.addTab(frame, 'Terminal #1') 30 | #frame = gui.TerminalWidget(self, "/bin/sh") 31 | #self.tabs.addTab(frame, 'Terminal #2') 32 | 33 | self.splitter = QSplitter() 34 | 35 | self.splitter.addWidget(self.tabs) 36 | 37 | self.tabs2 = QTabWidget(self) 38 | self.tabs2.setDocumentMode(True) 39 | self.tabs2.setTabsClosable(True) 40 | self.tabs2.setMovable(True) 41 | self.splitter.addWidget(self.tabs2) 42 | 43 | if arg_ns.terminal_enabled: 44 | frame = gui.TerminalWidget(self, "/usr/bin/vim") 45 | self.tabs2.addTab(frame, 'Terminal #3') 46 | 47 | #self.setCentralWidget(self.tabs) 48 | self.setCentralWidget(self.splitter) 49 | self.splitter.setOrientation(Qt.Vertical) 50 | self.splitter.setSizes([1, 1]) 51 | 52 | self.show() 53 | 54 | def sizeHint(self): 55 | return QSize(800, 600) 56 | 57 | def run(): 58 | parser = argparse.ArgumentParser(description="Santarago Labs Pentest Framework") 59 | parser.add_argument("--disable-terminal", help="disable terminal feature", action="store_false", dest="terminal_enabled") 60 | global arg_ns 61 | arg_ns = parser.parse_args() 62 | 63 | if arg_ns.terminal_enabled: 64 | td = terminal.driver.TerminalDriver() 65 | 66 | app = QApplication(sys.argv) 67 | 68 | if arg_ns.terminal_enabled: 69 | app.td = td 70 | 71 | main = Main() 72 | app.exec_() 73 | 74 | if __name__ == "__main__": 75 | run() 76 | -------------------------------------------------------------------------------- /src/terminal/__init__.py: -------------------------------------------------------------------------------- 1 | from . import emulator 2 | from . import driver 3 | 4 | -------------------------------------------------------------------------------- /src/terminal/ctrl.py: -------------------------------------------------------------------------------- 1 | NUL = 0x0 # NULL 2 | SP = 0x20 # space 3 | BEL = 0x07 # ^G) beeps; 4 | BS = 0x08 # ^H) backspaces one column (but not past the beginning of the line); 5 | HT = 0x09 # ^I) goes to the next tab stop or to the end of the line if there is no earlier tab stop; 6 | LF = 0x0A # ^J) 7 | VT = 0x0B # ^K) and FF (0x0C, ^L) all give a linefeed, and if LF/NL (new-line mode) is set also a carriage return; 8 | FF = 0x0C # see VT 9 | CR = 0x0D # ^M) gives a carriage return; 10 | SO = 0x0E # ^N) activates the G1 character set; 11 | SI = 0x0F # ^O) activates the G0 character set; 12 | CAN = 0x18 # ^X) and SUB (0x1A, ^Z) interrupt escape sequences; 13 | SUB = 0x1A # see CAN 14 | ESC = 0x1B # ^[) starts an escape sequence; 15 | DEL = 0x7F # is ignored; 16 | CSI = 0x9B # is equivalent to ESC [. 17 | -------------------------------------------------------------------------------- /src/terminal/cursor.py: -------------------------------------------------------------------------------- 1 | class Cursor: 2 | def __init__(self, x, y, visible = True): 3 | self.x = x 4 | self.y = y 5 | self.visible = visible 6 | -------------------------------------------------------------------------------- /src/terminal/driver.py: -------------------------------------------------------------------------------- 1 | import os, pty, select, threading, signal 2 | import queue 3 | import multiprocessing 4 | import termios, struct, fcntl 5 | 6 | START = 0x1 7 | EXITED = 0x2 8 | DATA = 0x3 9 | RESIZE = 0x4 10 | 11 | class TerminalDriverProcess(multiprocessing.Process): 12 | def __init__(self, transport, queue, daemon=True): 13 | multiprocessing.Process.__init__(self, daemon=daemon) 14 | self.transport = transport 15 | self.queue = queue 16 | self.terminals = {} 17 | self.todelete = [] 18 | self.stopped = False 19 | 20 | def sigchild(self, sig, frame): 21 | todelete = [] 22 | for pid in self.terminals: 23 | ret = os.waitpid(pid, os.WNOHANG) 24 | if ret[0] == pid and ret[0] != 0: 25 | self.queue.put((pid, EXITED, None)) 26 | self.todelete.append(pid) 27 | 28 | def startTerminal(self, cmd, args=[]): 29 | signal.signal(signal.SIGCHLD, self.sigchild) 30 | pid, fd = pty.fork() 31 | if pid == 0: 32 | args = [cmd] if len(args) == 0 else args 33 | os.execv(cmd, args) 34 | os._exit(1) 35 | 36 | self.terminals[pid] = fd 37 | return pid 38 | 39 | def resizeTerminal(self, pid, rows, cols): 40 | data = struct.pack("HHHH", rows, cols, 0, 0) 41 | fd = self.terminals[pid] 42 | fcntl.ioctl(fd, termios.TIOCSWINSZ, data) 43 | attr = termios.tcgetattr(fd) 44 | termios.tcsetattr(fd, termios.TCSAFLUSH, attr) 45 | 46 | def _run(self): 47 | for pid in self.todelete: 48 | del self.terminals[pid] 49 | self.todelete = [] 50 | fds = [x for x in self.terminals.values()] 51 | transport_fd = self.transport.fileno() 52 | fds.append(transport_fd) 53 | 54 | i, o, e = select.select(fds, [], [], 0.01) 55 | 56 | if transport_fd in i: 57 | msg, cmd, args = self.transport.recv() 58 | if msg == START: 59 | pid = self.startTerminal(cmd, args) 60 | self.transport.send(pid) 61 | if cmd not in self.terminals: 62 | pass 63 | elif msg == DATA: 64 | fd = self.terminals[cmd] 65 | os.write(fd, args) 66 | elif msg == RESIZE: 67 | self.resizeTerminal(cmd, *args) 68 | 69 | for pid in self.terminals: 70 | fd = self.terminals[pid] 71 | if fd in i: 72 | try: 73 | data = os.read(fd, 4096) 74 | if len(data) == 0: 75 | continue 76 | self.queue.put((pid, DATA, data)) 77 | except OSError: 78 | pass 79 | 80 | def run(self): 81 | while not self.stopped: 82 | self._run() 83 | 84 | class TerminalCallbackThread(threading.Thread): 85 | def __init__(self, queue, queue2): 86 | threading.Thread.__init__(self) 87 | self.proc_queue = queue 88 | self.thread_queue = queue2 89 | self.callbacks = {} 90 | self.stopped = False 91 | 92 | def _run(self): 93 | try: 94 | pid, msg, data = self.proc_queue.get(0.01) 95 | if msg == EXITED: 96 | self.thread_queue.put((pid, EXITED, data)) 97 | elif msg == DATA: 98 | if pid in self.callbacks: 99 | #$self.callbacks[pid].data(data) 100 | self.thread_queue.put((pid, DATA, data)) 101 | except queue.Empty: 102 | pass 103 | 104 | try: 105 | pid, msg, data = self.thread_queue.get(0.01) 106 | if pid not in self.callbacks: 107 | if msg == START: 108 | self.callbacks[pid] = data 109 | return 110 | if msg == EXITED: 111 | self.callbacks[pid].exited(data) 112 | del self.callbacks[pid] 113 | elif msg == DATA: 114 | if pid in self.callbacks: 115 | self.callbacks[pid].data(data) 116 | except queue.Empty: 117 | pass 118 | 119 | def run(self): 120 | while not self.stopped: 121 | self._run() 122 | 123 | class TerminalDriver: 124 | def __init__(self): 125 | self.pipe = multiprocessing.Pipe() 126 | self.queue = multiprocessing.Queue() 127 | self.proc = TerminalDriverProcess(self.pipe[0], self.queue) 128 | self.proc.start() 129 | self.queue2 = queue.Queue() 130 | self.cb = TerminalCallbackThread(self.queue, self.queue2) 131 | self.cb.start() 132 | 133 | def startTerminal(self, cmd, args, cb): 134 | p = self.pipe[1] 135 | p.send((START, cmd, args)) 136 | pid = int(p.recv()) 137 | self.queue2.put((pid, START, cb)) 138 | return pid 139 | 140 | def sendInput(self, terminal_id, data): 141 | p = self.pipe[1] 142 | if not data: 143 | data = "" 144 | data = data.encode("utf8") 145 | p.send((DATA, terminal_id, data)) 146 | 147 | def resizeTerminal(self, terminal_id, rows, cols): 148 | p = self.pipe[1] 149 | p.send((RESIZE, terminal_id, (rows,cols))) 150 | 151 | """ 152 | class Test: 153 | def __init__(self): 154 | pass 155 | 156 | def exited(self, data): 157 | print("CALLBACK exited", data) 158 | 159 | def data(self, data): 160 | print("CALLBACK data", data) 161 | 162 | test1 = Test() 163 | 164 | td = TerminalDriver() 165 | terminal_id = td.startTerminal("/bin/sh", [], test1) 166 | td.sendInput(terminal_id, "ls -lha\n") 167 | td.resizeTerminal(terminal_id, 30, 100) 168 | 169 | import time 170 | time.sleep(10) 171 | print("done") 172 | """ 173 | -------------------------------------------------------------------------------- /src/terminal/emulator.py: -------------------------------------------------------------------------------- 1 | from . import ctrl, esc, cursor, screen, rendition 2 | 3 | class Emulator: 4 | def __init__(self, rows=80, cols=25, debug=True): 5 | # configure two supported screens and cursors 6 | self.cols, self.rows = cols, rows 7 | self.screens = [screen.Screen(rows, cols), screen.Screen(rows, cols)] 8 | self.cursors = [cursor.Cursor(0,0), cursor.Cursor(0, 0)] 9 | 10 | # set currently active screen, cursor, scroll region and graphics rendition settings 11 | self.screen = self.screens[0] 12 | self.cursor = self.cursors[0] 13 | self.scroll_bottom = rows - 1 14 | self.scroll_top = 0 15 | self.gfx = 0 16 | 17 | # vars for parser state machine of escape sequences etc 18 | self.escape_str = "" 19 | self.in_escape = False # in escape sequence 20 | self.in_csi = False # in Control Sequence Introducer 21 | 22 | # xterm set title support 23 | self.current_title = None # currently set title 24 | self.set_title = False # set window title 25 | self.ignore_title = True # ignore setting of window title 26 | 27 | # misc settings 28 | self.tabstop = 8 # amount of spaces in a tab 29 | self.draw_lines = False # line drawing mode 30 | self.insert_mode = False # insert mode for drawing characters 31 | self.cursor_keys = False # interpet cursor keys on numpad as keys or not 32 | 33 | # references to callback functions 34 | self.callback_data = None # respond with data to f.e. a specific CSI sequence 35 | self.callback_update = None # let caller know there's new data available 36 | self.callback_title = None # inform caller of updated window title 37 | 38 | # debug log support 39 | self.debug = debug 40 | self.debuglog = [] 41 | self.debugloglen = 256 # maximum number of lines stored in self.debuglog 42 | 43 | # control character dispatch 44 | self.ctrl_dispatch = { 45 | ctrl.BEL : self.bell, 46 | ctrl.BS : self.backspace, 47 | ctrl.HT : self.htab, 48 | ctrl.LF : self.lf, 49 | ctrl.VT : self.lf, 50 | ctrl.FF : self.lf, 51 | ctrl.CR : self.cr, 52 | ctrl.SO : self.shift_out, 53 | ctrl.SI : self.shift_in, 54 | } 55 | 56 | # escape sequence dispatcher for CSI escape sequences as per VT102 57 | self.csi_dispatch = { 58 | esc.ICH : self.insert_chars, 59 | esc.CUU : self.cursor_up, 60 | esc.CUD : self.cursor_down, 61 | esc.CUF : self.cursor_forward, 62 | esc.CUB : self.cursor_back, 63 | esc.CNL : self.cursor_next_line, 64 | esc.CPL : self.cursor_prev_line, 65 | esc.CHA : self.cursor_halign, 66 | esc.CUP : self.cursor_set, 67 | esc.ED : self.erase_data, 68 | esc.EL : self.erase_in_line, 69 | esc.IL : self.insert_lines, 70 | esc.DL : self.delete_lines, 71 | esc.DCH : self.delete_characters, 72 | esc.ECH : self.erase_characters, 73 | esc.HPR : self.cursor_forward, 74 | esc.DA : self.report_da, 75 | esc.VPA : self.cursor_set, 76 | #esc.VPR : self.vertical_position_relative, 77 | #esc.HVP : self.hvposition, 78 | esc.TBC : self.tabulation_clear, 79 | esc.SM : self.set_mode, 80 | esc.RM : self.reset_mode, 81 | esc.SGR : self.select_gfx, 82 | esc.DSR : self.report_status, 83 | esc.DECSTBM : self.scroll_region, 84 | esc.HPA : self.cursor_halign, 85 | #esc.RIS : self.reset, 86 | #esc.IND : self.index, 87 | #esc.RI : self.rindex, 88 | #esc.NEL : self.nextline, 89 | esc.DECSC : self.cursor_save, 90 | esc.DECRC : self.cursor_restore, 91 | } 92 | 93 | # charset setting identifiers used for non "standard" escape sequences etc 94 | self.charset = [" ", "#", "%", "(", ")", "*", "+"] 95 | 96 | # replace key chars with values when in line drawing mode 97 | self.line_draw_map = { 98 | u"j": u"\xe2\x94\x98", 99 | u"k": u"\xe2\x94\x90", 100 | u"l": u"\xe2\x94\x8c", 101 | u"m": u"\xe2\x94\x94", 102 | u"n": u"\xe2\x94\xbc", 103 | u"q": u"\xe2\x94\x80", 104 | u"t": u"\xe2\x94\x9c", 105 | u"u": u"\xe2\x94\xa4", 106 | u"v": u"\xe2\x94\xb4", 107 | u"w": u"\xe2\x94\xac", 108 | u"x": u"\xe2\x94\x82", 109 | } 110 | 111 | def set_update_callback(self, cb): 112 | self.callback_update = cb 113 | 114 | def set_data_callback(self, cb): 115 | self.callback_data = cb 116 | 117 | def set_title_callback(self, cb): 118 | self.callback_title = cb 119 | 120 | def update_callback(self): 121 | if self.callback_update: 122 | self.callback_update() 123 | 124 | def data_callback(self, data): 125 | if self.callback_data: 126 | self.callback_data(data) 127 | 128 | def title_callback(self, title): 129 | if title == self.current_title: 130 | return 131 | self.current_title = title 132 | if self.callback_title: 133 | self.callback_title(title) 134 | 135 | def shift_in(self): 136 | raise Exception("shift in") 137 | 138 | def shift_out(self): 139 | raise Exception("shift out") 140 | 141 | def bell(self): 142 | pass 143 | 144 | def backspace(self): 145 | if self.cursor.x > 0: 146 | self.cursor.x = self.cursor.x - 1 147 | 148 | def htab(self): 149 | self.cursor.x = self.cursor.x + self.tabstop - (self.cursor.x % self.tabstop) 150 | if self.cursor.x > self.cols: 151 | self.cursor.x = self.cols 152 | 153 | def cursor_save(self, *params): 154 | print("Save cursor1") 155 | #raise Exception("save cursor") 156 | 157 | def cursor_restore(self, *params): 158 | print("Save cursor2") 159 | #raise Exception("restore cursor") 160 | 161 | def insert_lines(self, count=None): 162 | pass 163 | 164 | def insert_chars(self, count=None): 165 | #raise Exception("todo") 166 | pass 167 | 168 | def cursor_up(self, count=None): 169 | self.cursor.y += count or 1 170 | self.fix_cursors() 171 | 172 | def cursor_down(self, count=None): 173 | self.cursor.y -= count or 1 174 | self.fix_cursors() 175 | 176 | def cursor_forward(self, count=None): 177 | self.cursor.x += count or 1 178 | self.fix_cursors() 179 | 180 | def cursor_back(self, count=None): 181 | self.cursor.x -= count or 1 182 | self.fix_cursors() 183 | 184 | def cursor_next_line(self, count=None): 185 | self.cursor_down(count) 186 | self.cursor.x = 0 187 | 188 | def cursor_prev_line(self, count=None): 189 | self.cursor_up(count) 190 | self.cursor.x = 0 191 | 192 | def cursor_halign(self, param=None): 193 | raise Exception("!!!!", param) 194 | if param: 195 | self.cursor.x = param - 1 196 | self.fix_cursors() 197 | 198 | def cursor_set(self, y = None, x = None): 199 | x = 0 if not x or x == 0 else x - 1 200 | y = 0 if not y or y == 0 else y - 1 201 | self.cursor.x = x 202 | self.cursor.y = y 203 | self.fix_cursors() 204 | 205 | def fix_cursor_bounds(self, cursor): 206 | if cursor.x > self.cols: 207 | cursor.x = self.cols 208 | if cursor.y >= self.rows: 209 | cursor.y = self.rows - 1 210 | 211 | def fix_cursors(self): 212 | self.fix_cursor_bounds(self.cursors[0]) 213 | self.fix_cursor_bounds(self.cursors[1]) 214 | 215 | def delete_lines(self, count): 216 | raise Exception("delete lines") 217 | 218 | def delete_characters(self, *params): 219 | raise Exception("delete characters") 220 | 221 | def report_da(self, *params): 222 | self.data_callback("\033[?1;2c") 223 | 224 | def is_alternative_screen(self): 225 | return self.screen == self.screens[1] 226 | 227 | def set_mode(self, *params): 228 | if len(params) < 0: 229 | return 230 | private = False 231 | if params[-1] == "?": 232 | private = True 233 | params = params[:-1] 234 | for p in params: 235 | if p == 4: 236 | self.insert_mode = True 237 | elif p == 1049 or p == 47 and not self.is_alternative_screen(): 238 | self.screen = self.screen[1] 239 | self.cursor = self.cursor[1] 240 | elif p == 1: 241 | self.cursor_keys = True 242 | elif p == 2: 243 | pass 244 | elif p == 7: 245 | pass 246 | elif p == 12: 247 | pass 248 | elif p == 25: 249 | raise Exception("visible cursor") 250 | self.cursor.visible = True 251 | elif p == 0: 252 | pass 253 | else: 254 | raise Exception("set mode unkonwn: %s" % p) 255 | 256 | 257 | def reset_mode(self, *params): 258 | if len(params) < 0: 259 | return 260 | private = False 261 | if params[-1] == "?": 262 | private = True 263 | params = params[:-1] 264 | for p in params: 265 | if p == 4: 266 | self.insert_mode = False 267 | elif p == 1049 or p == 47 and self.is_alternative_screen(): 268 | self.screen = self.screen[0] 269 | self.cursor = self.cursor[0] 270 | elif p == 1: 271 | self.cursor_keys = False 272 | elif p == 2: 273 | pass 274 | elif p == 7: 275 | pass 276 | elif p == 12: 277 | pass 278 | elif p == 25: 279 | raise Exception("not visible cursor") 280 | self.cursor.visible = False 281 | elif p == 0: 282 | pass 283 | else: 284 | raise Exception("set mode unkonwn: %s" % p) 285 | 286 | def report_status(self, param=None): 287 | if not param or param not in [5, 6]: 288 | return 289 | if param == 5: 290 | self.data_callback("\033[0n") 291 | elif param == 6: 292 | self.data_callback("\033[%d;%dR" % (self.cursor.y + 1, self.cursor.x + 1)) 293 | 294 | def scroll_region(self, *params): 295 | if len(params) < 2: 296 | return 297 | 298 | self.scroll_top = params[0] - 1 299 | self.scroll_bottom = params[1] - 1 300 | if self.scroll_top < 0: 301 | self.scroll_top = 0 302 | elif self.scroll_top >= self.rows: 303 | self.scroll_top = self.rows - 1 304 | if self.scroll_bottom < 0: 305 | self.scroll_bottom = 0 306 | elif self.scroll_bottom >= self.rows: 307 | self.scroll_bottom = self.rows - 1 308 | 309 | def select_gfx(self, *params): 310 | i = 0 311 | lp = len(params) 312 | # ISO-8613-3 support for 256-colors is implemented 313 | # 24-bit color support is not implemented 314 | if (lp == 0): 315 | raise Exception("no args to gfx") 316 | while i < lp: 317 | p = params[i] 318 | if p == 0: 319 | # default graphics rendition 320 | self.gfx = 0 321 | elif p >= 1 and p <= 9: 322 | # set a style 323 | self.gfx &= ~0xff 324 | self.gfx |= 1 << (p - 1) 325 | elif p >= 21 and p <= 29: 326 | # clear a style 327 | self.gfx &= ~(1 << (p - 21)) 328 | elif p >= 30 and p <= 37: 329 | self.gfx &= ~(0x00FF0000 | rendition.GFX_FG) 330 | self.gfx |= ((p - 29) << 16) 331 | elif p == 38 and i + 2 < len(params): 332 | if params[i+1] == 5: 333 | self.gfx &= ~0x00ff0000 334 | self.gfx |= rendition.GFX_FG 335 | self.gfx |= (params[i+2] & 0xff) << 16 336 | i = i + 2 337 | elif p == 39: 338 | self.gfx &= ~(0x00ff0000 | rendition.GFX_FG) 339 | elif p >= 40 and p <= 47: 340 | self.gfx &= ~(0xFF000000 | rendition.GFX_BG) 341 | self.gfx |= ((p - 39) << 24) 342 | elif p == 48 and i + 2 < len(params): 343 | if params[i+1] == 5: 344 | self.gfx &= ~0x00ff0000 345 | self.gfx |= rendition.GFX_BG 346 | self.gfx |= (params[i+2] & 0xff) << 24 347 | i = i + 2 348 | elif p == 49: 349 | self.gfx &= ~(0xff000000 | rendition.GFX_BG) 350 | elif p >= 90 and p <= 97: 351 | self.gfx &= ~(0x00ff0000 | rendition.GFX_FG) 352 | self.gfx |= ((p - 81) << 16) 353 | elif p >= 100 and p <= 107: 354 | self.gfx &= ~(0xff000000 | rendition.GFX_BG) 355 | self.gfx |= ((p - 91) << 16) 356 | else: 357 | raise Exception("unsupported gfx rendition %i" % p) 358 | i = i + 1 359 | 360 | def tabulation_clear(self, param = 0): 361 | if param == 0: 362 | self.tabstops.discard(self.cursor.x) 363 | elif param == 3: 364 | self.tabstops = set() 365 | 366 | def erase_characters(self, count=None): 367 | if not count or count == 0: 368 | count = 1 369 | self.screen.erase_rectangle(self.cursor.y, self.cursor.x, self.cursor.y + 1, self.x + count, self.gfx) 370 | 371 | def erase_in_line(self, param = None): 372 | if not param: 373 | param = 0 374 | if param == 0: 375 | self.screen.erase_rectangle(self.cursor.y, self.cursor.x, self.cursor.y + 1, self.cols, self.gfx) 376 | elif param == 1: 377 | self.screen.erase_rectangle(self.cursor.y, 0, self.cursor.y + 1, self.cursor.x + 1, self.gfx) 378 | elif param == 2: 379 | self.screen.erase_rectangle(self.cursor.y, 0, self.cursor.y + 1, self.cols, self.gfx) 380 | 381 | def erase_data(self, param = None): 382 | if not param: 383 | param = 0 384 | if param == 0: 385 | self.screen.erase_rectangle(self.cursor.y, self.cursor.x, self.cursor.y + 1, self.cols, self.gfx) 386 | self.screen.erase_rectangle(self.cursor.y + 1, 0, self.rows, self.cols, self.gfx) 387 | elif param == 1: 388 | self.screen.erase_rectangle(0, 0, self.cursor.y, self.cols, self.gfx) 389 | self.screen.erase_rectangle(self.cursor.y, 0, self.cursor.y + 1, self.cursor.x + 1, self.gfx) 390 | elif param == 2: 391 | self.screen.erase_rectangle(0, 0, self.rows, self.cols, self.gfx) 392 | self.cursor.x, self.cursor.y = 0, 0 393 | 394 | def resize(self, rows, cols): 395 | if rows == self.rows and cols == self.cols: 396 | return 397 | self.screens[0].resize(rows, cols) 398 | self.screens[1].resize(rows, cols) 399 | self.scroll_top = 0 400 | self.scroll_bottom = rows - 1 401 | self.rows = rows 402 | self.cols = cols 403 | self.fix_cursors() 404 | 405 | def lf(self): 406 | if self.cursor.y >= self.scroll_bottom: 407 | self.screen.scroll_up(self.scroll_top, self.scroll_bottom, self.is_alternative_screen(), self.gfx) 408 | else: 409 | self.cursor.y += 1 410 | 411 | def cr(self): 412 | self.cursor.x = 0 413 | 414 | def newline(self): 415 | self.lf() 416 | self.cursor.x = 0 417 | 418 | def write_char(self, ch): 419 | if self.cursor.x >= self.cols: 420 | self.newline() 421 | 422 | if self.draw_lines: 423 | if ch in self.linemap: 424 | ch = self.linemap[ch] 425 | 426 | if self.insert_mode: 427 | self.insert_chars([1]) 428 | 429 | self.screen.write_char(self.cursor, ch, self.gfx) 430 | self.cursor.x += 1 431 | 432 | self.update_callback() 433 | 434 | def parse_escape_sequence(self, data): 435 | l = len(data) 436 | if l == 0: 437 | # XXX log 438 | raise Exception("unhandled escape seq") 439 | return 440 | 441 | if data[0] != "[": 442 | # ignore numpad handling 443 | if data[0] in ["=", ">"]: 444 | return 445 | else: 446 | raise Exception("unhandled data %s" % data) 447 | 448 | seq = data[1:-1] 449 | mode = ord(data[-1]) 450 | option = None 451 | if len(seq) > 1 and seq[0] in ["?", ">", "!"]: 452 | option = seq[0] 453 | seq = seq[1] 454 | try: 455 | if len(seq) > 0: 456 | params = [int(x) for x in seq.split(";")] 457 | else: 458 | params = [0] 459 | except Exception as e: 460 | params = [0] 461 | 462 | if mode not in self.csi_dispatch: 463 | s="".join(["%x " % ord(x) for x in data]) 464 | s2="".join(["%c " % ord(x) for x in data]) 465 | self.write_debug_logdata("! unknown CSI: %s [%s]" % (s2, s)) 466 | return 467 | 468 | if not option: 469 | self.csi_dispatch[mode](*params) 470 | else: 471 | self.csi_dispatch[mode](*params, option) 472 | self.write_debug_logdata("ESC %s -> %s" % (data, self.csi_dispatch[mode])) 473 | 474 | def write_debug_logdata(self, data): 475 | if not self.debug: 476 | return 477 | data = data.splitlines() 478 | for line in data: 479 | self.debuglog.append(line) 480 | ld = len(self.debuglog) 481 | if ld > self.debugloglen: 482 | self.debuglog = self.debuglog[ld - self.debugloglen:] 483 | 484 | def parse_data(self, data): 485 | for c in data: 486 | oc = ord(c) 487 | if self.set_title: 488 | if oc != ctrl.BEL: 489 | self.escape_str += c 490 | continue 491 | if self.ignore_title == False: 492 | self.title_callback(self.escape_str) 493 | self.escape_str = "" 494 | self.set_title = False 495 | continue 496 | elif oc in self.ctrl_dispatch: 497 | self.ctrl_dispatch[oc]() 498 | continue 499 | elif self.in_escape and not self.in_csi: 500 | self.escape_str += c 501 | l = len(self.escape_str) 502 | if l == 1 and c == "[": 503 | self.in_csi = True 504 | continue 505 | elif l == 1 and c != "[" and c != "]" and c not in self.charset: 506 | self.parse_escape_sequence(self.escape_str) 507 | self.escape_str = "" 508 | self.in_escape = False 509 | continue 510 | elif l == 2 and self.escape_str[0] in self.charset: 511 | if self.escape_str == "(0": 512 | self.draw_lines = True 513 | else: 514 | self.draw_lines = False 515 | self.escape_str = "" 516 | self.in_escape = False 517 | continue 518 | elif l > 1 and self.escape_str[0] == "]" and c == ";": 519 | self.set_title = True 520 | try: 521 | param = [int(x) for x in self.escape_str[1:-1].split(";")] 522 | except: 523 | param = [0] 524 | if len(param) == 0 or param[0]==0 or param[0] == 2: 525 | self.ignore_title = False 526 | else: 527 | self.ignore_title = True 528 | self.in_escape = False 529 | self.escape_str = "" 530 | continue 531 | elif self.in_csi: 532 | self.escape_str += c 533 | if oc >= ord("@") and oc <= ord("~"): 534 | self.parse_escape_sequence(self.escape_str) 535 | self.in_csi = False 536 | self.in_escape = False 537 | self.escape_str = "" 538 | continue 539 | elif oc == ctrl.ESC: 540 | self.in_escape = True 541 | continue 542 | else: 543 | self.write_char(c) 544 | 545 | def input_data(self, data): 546 | try: 547 | data = data.decode(encoding="utf8") 548 | self.parse_data(data) 549 | except Exception as e: 550 | self.write_debug_logdata(e) 551 | -------------------------------------------------------------------------------- /src/terminal/esc.py: -------------------------------------------------------------------------------- 1 | # Taken from: VT102 User Guide 2 | # found http://vt100.net/docs/vt102-ug/chapter5.html 3 | ICH = 64 # "@" -- Insert Blank Characters 4 | CUU = 65 # "A" -- Cursur Up 5 | CUD = 66 # "B" -- Cursor Down 6 | CUF = 67 # "C" -- Cursor Forward 7 | CUB = 68 # "D" -- Cursor Backward 8 | CNL = 69 # "E" -- Cursor Next line 9 | CPL = 70 # "F" -- Cursor Previous Line 10 | CHA = 71 # "G" -- Cursor Horizontal Align 11 | CUP = 72 # "H" -- Cursor Position 12 | ED = 74 # "J" -- Erase data 13 | EL = 75 # "K" -- Erase in line 14 | IL = 76 # "L" -- Insert line 15 | DL = 77 # "M" -- Delete line 16 | DCH = 80 # "P" -- Delete character 17 | ECH = 88 # "X" -- Erase character 18 | HPR = 97 # "a" -- Horizontal Postion Relative 19 | DA = 99 # "c" -- Device Attributes 20 | VPA = 100 # "d" -- Vertial Position Adjust 21 | VPR = 101 # "e" -- Vertical Position Relative 22 | HVP = 102 # "f" -- Horizontal / Vertical postion 23 | TBC = 103 # "g" -- Tabulation Clear 24 | SM = 104 # "h" -- Set Mode 25 | RM = 108 # "l" -- Reset Mode" 26 | SGR = 109 # "m" -- Select Graphics rendition" 27 | DSR = 110 # "n" -- Device Status Report" 28 | DECSTBM = 114 # "r" -- Select Top and Bottom Margins 29 | HPA = 39 # "'" -- Horizontal Position Adjust 30 | RIS = 99 # "c" -- Reset to Initial State 31 | IND = 68 # "D" -- Index 32 | RI = 77 # "M" -- Reverse Index 33 | NEL = 69 # "E" -- Next Line 34 | DECSC = 55 # "7" -- Save Cursor 35 | DECRC = 56 # "8" -- Restore Cursor 36 | -------------------------------------------------------------------------------- /src/terminal/rendition.py: -------------------------------------------------------------------------------- 1 | # color scheme generated by gen-term-colors.py 2 | 3 | colors = [ 4 | ( 46, 52, 54), (204, 0, 0), ( 78,154, 6), (196,160, 0), ( 52,101,164), 5 | (117, 80,123), ( 6,152,154), (211,215,207), ( 85, 87, 83), (239, 41, 41), 6 | (138,226, 52), (252,233, 79), (114,159,207), (173,127,168), ( 52,226,226), 7 | (255,255,255), ( 0, 0, 0), ( 0, 0, 95), ( 0, 0,135), ( 0, 0,175), 8 | ( 0, 0,215), ( 0, 0,255), ( 0, 95, 0), ( 0, 95, 95), ( 0, 95,135), 9 | ( 0, 95,175), ( 0, 95,215), ( 0, 95,255), ( 0,135, 0), ( 0,135, 95), 10 | ( 0,135,135), ( 0,135,175), ( 0,135,215), ( 0,135,255), ( 0,175, 0), 11 | ( 0,175, 95), ( 0,175,135), ( 0,175,175), ( 0,175,215), ( 0,175,255), 12 | ( 0,215, 0), ( 0,215, 95), ( 0,215,135), ( 0,215,175), ( 0,215,215), 13 | ( 0,215,255), ( 0,255, 0), ( 0,255, 95), ( 0,255,135), ( 0,255,175), 14 | ( 0,255,215), ( 0,255,255), ( 95, 0, 0), ( 95, 0, 95), ( 95, 0,135), 15 | ( 95, 0,175), ( 95, 0,215), ( 95, 0,255), ( 95, 95, 0), ( 95, 95, 95), 16 | ( 95, 95,135), ( 95, 95,175), ( 95, 95,215), ( 95, 95,255), ( 95,135, 0), 17 | ( 95,135, 95), ( 95,135,135), ( 95,135,175), ( 95,135,215), ( 95,135,255), 18 | ( 95,175, 0), ( 95,175, 95), ( 95,175,135), ( 95,175,175), ( 95,175,215), 19 | ( 95,175,255), ( 95,215, 0), ( 95,215, 95), ( 95,215,135), ( 95,215,175), 20 | ( 95,215,215), ( 95,215,255), ( 95,255, 0), ( 95,255, 95), ( 95,255,135), 21 | ( 95,255,175), ( 95,255,215), ( 95,255,255), (135, 0, 0), (135, 0, 95), 22 | (135, 0,135), (135, 0,175), (135, 0,215), (135, 0,255), (135, 95, 0), 23 | (135, 95, 95), (135, 95,135), (135, 95,175), (135, 95,215), (135, 95,255), 24 | (135,135, 0), (135,135, 95), (135,135,135), (135,135,175), (135,135,215), 25 | (135,135,255), (135,175, 0), (135,175, 95), (135,175,135), (135,175,175), 26 | (135,175,215), (135,175,255), (135,215, 0), (135,215, 95), (135,215,135), 27 | (135,215,175), (135,215,215), (135,215,255), (135,255, 0), (135,255, 95), 28 | (135,255,135), (135,255,175), (135,255,215), (135,255,255), (175, 0, 0), 29 | (175, 0, 95), (175, 0,135), (175, 0,175), (175, 0,215), (175, 0,255), 30 | (175, 95, 0), (175, 95, 95), (175, 95,135), (175, 95,175), (175, 95,215), 31 | (175, 95,255), (175,135, 0), (175,135, 95), (175,135,135), (175,135,175), 32 | (175,135,215), (175,135,255), (175,175, 0), (175,175, 95), (175,175,135), 33 | (175,175,175), (175,175,215), (175,175,255), (175,215, 0), (175,215, 95), 34 | (175,215,135), (175,215,175), (175,215,215), (175,215,255), (175,255, 0), 35 | (175,255, 95), (175,255,135), (175,255,175), (175,255,215), (175,255,255), 36 | (215, 0, 0), (215, 0, 95), (215, 0,135), (215, 0,175), (215, 0,215), 37 | (215, 0,255), (215, 95, 0), (215, 95, 95), (215, 95,135), (215, 95,175), 38 | (215, 95,215), (215, 95,255), (215,135, 0), (215,135, 95), (215,135,135), 39 | (215,135,175), (215,135,215), (215,135,255), (215,175, 0), (215,175, 95), 40 | (215,175,135), (215,175,175), (215,175,215), (215,175,255), (215,215, 0), 41 | (215,215, 95), (215,215,135), (215,215,175), (215,215,215), (215,215,255), 42 | (215,255, 0), (215,255, 95), (215,255,135), (215,255,175), (215,255,215), 43 | (215,255,255), (255, 0, 0), (255, 0, 95), (255, 0,135), (255, 0,175), 44 | (255, 0,215), (255, 0,255), (255, 95, 0), (255, 95, 95), (255, 95,135), 45 | (255, 95,175), (255, 95,215), (255, 95,255), (255,135, 0), (255,135, 95), 46 | (255,135,135), (255,135,175), (255,135,215), (255,135,255), (255,175, 0), 47 | (255,175, 95), (255,175,135), (255,175,175), (255,175,215), (255,175,255), 48 | (255,215, 0), (255,215, 95), (255,215,135), (255,215,175), (255,215,215), 49 | (255,215,255), (255,255, 0), (255,255, 95), (255,255,135), (255,255,175), 50 | (255,255,215), (255,255,255), ( 8, 8, 8), ( 18, 18, 18), ( 28, 28, 28), 51 | ( 38, 38, 38), ( 48, 48, 48), ( 58, 58, 58), ( 68, 68, 68), ( 78, 78, 78), 52 | ( 88, 88, 88), ( 98, 98, 98), (108,108,108), (118,118,118), (128,128,128), 53 | (138,138,138), (148,148,148), (158,158,158), (168,168,168), (178,178,178), 54 | (188,188,188), (198,198,198), (208,208,208), (218,218,218), (228,228,228), 55 | (238,238,238) 56 | ] 57 | 58 | back_colors = [ 59 | ( 0, 0, 0), ( 46, 52, 54), (204, 0, 0), ( 78,154, 6), (196,160, 0), 60 | ( 52,101,164), (117, 80,123), ( 6,152,154), (211,215,207), ( 85, 87, 83), 61 | (239, 41, 41), (138,226, 52), (252,233, 79), (114,159,207), (173,127,168), 62 | ( 52,226,226), (255,255,255) 63 | ] 64 | 65 | fore_colors = [ 66 | (255,255,255), ( 0, 0, 0), (135, 0, 0), ( 0,135, 0), (135,135, 0), 67 | ( 0, 0,135), (135, 0,135), ( 0,135,135), (135,135,135), ( 46, 52, 54), 68 | (204, 0, 0), ( 78,154, 6), (196,160, 0), ( 52,101,164), (117, 80,123), 69 | ( 6,152,154), (211,215,207), ( 85, 87, 83), (239, 41, 41), (138,226, 52), 70 | (252,233, 79), (114,159,207), (173,127,168), ( 52,226,226), (255,255,255), 71 | ( 85, 87, 83), (239, 41, 41), (138,226, 52), (252,233, 79), (114,159,207), 72 | (173,127,168), ( 52,226,226), (255,255,255) 73 | ] 74 | 75 | # graphics rendition settings for bold, dim, underlined, 76 | # inverse, hidden, fgcolor, bgcolor respectively 77 | GFX_BOLD = 0x001 78 | GFX_DIM = 0x002 79 | GFX_UL = 0x008 80 | GFX_INV = 0x040 81 | GFX_HIDE = 0x080 82 | GFX_FG = 0x100 83 | GFX_BG = 0x200 84 | 85 | # special for detecting line wrap when selecting text 86 | GFX_WRITTEN = 0x400 87 | 88 | def get_colors(rendition): 89 | fg = bg = None 90 | if rendition & GFX_BG: 91 | bg = colors[(rendition >> 24) & 0xff] 92 | else: 93 | bg = back_colors[(rendition >> 24) & 0x1f] 94 | if rendition & GFX_FG: 95 | if ((rendition >> 16) & 0xff) < 16: 96 | if rendition & GFX_BOLD: 97 | fg = fore_colors[((rendition >> 16)&7)+17] 98 | else: 99 | fg = fore_colors[((rendition >> 16) & 0xf) + 9] 100 | else: 101 | fg = colors[(rendition >> 16) & 0xff] 102 | elif (rendition & 0x1f0000) == 0 or (rendition & GFX_DIM): 103 | fg = fore_colors[(rendition >> 16) & 0x1f] 104 | elif rendition & GFX_BOLD: 105 | fg = fore_colors[((rendition >> 16) & 0x1f) + 16] 106 | else: 107 | fg = fore_colors[((rendition >> 16) & 0x1f) + 8] 108 | if rendition & GFX_INV: 109 | fg, bg = bg, fg 110 | return fg, bg 111 | -------------------------------------------------------------------------------- /src/terminal/screen.py: -------------------------------------------------------------------------------- 1 | import array 2 | 3 | from . import rendition 4 | 5 | class Screen: 6 | def __init__(self, rows, cols): 7 | self.reset(rows, cols) 8 | 9 | def reset(self, rows, cols): 10 | self.cols = cols 11 | self.rows = rows 12 | self.cells = [] 13 | self.gfx = [] 14 | self.empty_line = array.array("u", u" "*cols) 15 | self.empty_gfx = array.array("I", [0]*cols) 16 | 17 | for i in range(0, rows): 18 | self.cells.append(array.array("u", self.empty_line)) 19 | self.gfx.append(array.array("I", self.empty_gfx)) 20 | 21 | def scroll_up(self, scroll_top, scroll_bottom, alt, current_gfx): 22 | top_screen = self.cells.pop(scroll_top) 23 | top_gfx = self.gfx.pop(scroll_top) 24 | if scroll_top == 0 and scroll_bottom == self.rows -1 and alt: 25 | top_screen = array.array("u", self.empty_line) 26 | top_gfx = array.array("I", self.empty_gfx) 27 | else: 28 | top_screen[0:self.cols] = array.array("u", self.empty_line) 29 | top_gfx[0:self.cols] = array.array("I", self.empty_gfx) 30 | for i in range(0, self.cols): 31 | top_gfx[i] = current_gfx 32 | self.cells.insert(scroll_bottom, top_screen) 33 | self.gfx.insert(scroll_bottom, top_gfx) 34 | 35 | def resize(self, rows, cols): 36 | if rows > self.rows: 37 | for row in range(self.rows, rows): 38 | self.cells.append(array.array("u", self.empty_line)) 39 | self.gfx.append(array.array("I", self.empty_gfx)) 40 | elif rows < self.rows: 41 | self.cells = self.cells[:rows] 42 | self.gfx = self.gfx[:rows] 43 | if cols > self.cols: 44 | for row in range(0, rows): 45 | for col in range(self.cols, cols): 46 | self.cells[row].append(u" ") 47 | self.gfx[row].append(0) 48 | self.empty_line = array.array("u", u" "*cols) 49 | self.empty_gfx = array.array("I", [0]*cols) 50 | elif cols < self.cols: 51 | for row in range(0, rows): 52 | self.cells[row] = self.cells[row][0:cols] 53 | self.gfx[row] = self.gfx[row][0:cols] 54 | self.empty_line = self.empty_line[0:cols] 55 | self.empty_gfx = self.empty_gfx[0:cols] 56 | self.rows = rows 57 | self.cols = cols 58 | 59 | def write_char(self, cursor, ch, gfx): 60 | self.cells[cursor.y][cursor.x] = ch 61 | self.gfx[cursor.y][cursor.x] = gfx | rendition.GFX_WRITTEN 62 | 63 | def erase_rectangle(self, top_row, left_col, bot_row, right_col, gfx=0): # XXX: need to pass in active rendition 64 | for y in range(top_row, bot_row): 65 | if y < 0 or y >= self.rows: 66 | continue 67 | for x in range(left_col, right_col): 68 | if x < 0 or x >= self.cols: 69 | continue 70 | self.cells[y][x] = u" " 71 | self.gfx[y][x] = gfx 72 | -------------------------------------------------------------------------------- /support/gen-term-colors.py: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env python3 2 | 3 | def generate_colors(): 4 | # System colors 5 | dim_colors = [(0,0,0), (135, 0, 0), (0, 135, 0), (135, 135, 0), 6 | (0, 0, 135), (135, 0, 135), (0, 135, 135), (135, 135, 135)] 7 | normal_colors = [(46, 52, 54), (204, 0, 0), (78, 154, 6), (196, 160, 0), 8 | (52, 101, 164), (117, 80, 123), (6, 152, 154), (211, 215, 207)] 9 | bright_colors = [(85, 87, 83), (239, 41, 41), (138, 226, 52), (252, 233, 79), 10 | (114, 159, 207), (173, 127, 168), (52, 226, 226), (255,255,255)] 11 | 12 | # Create color arrays for normal mode 13 | fore_colors = [(255,255,255)] + dim_colors + normal_colors + bright_colors + bright_colors 14 | back_colors = [(0,0,0)] + normal_colors + bright_colors 15 | 16 | # Create color array for 256-color mode 17 | colors = normal_colors + bright_colors 18 | 19 | values = [0x00, 0x5f, 0x87, 0xaf, 0xd7, 0xff] 20 | for red in values: 21 | for green in values: 22 | for blue in values: 23 | color = (red, green, blue) 24 | colors.append(color) 25 | 26 | values = [0x08, 0x12, 0x1c, 0x26, 0x30, 0x3a, 0x44, 0x4e, 0x58, 0x62, 0x6c, 0x76, 0x80, 0x8a, 0x94, 0x9e, 27 | 0xa8, 0xb2, 0xbc, 0xc6, 0xd0, 0xda, 0xe4, 0xee] 28 | for gray in values: 29 | color = (gray, gray, gray) 30 | colors.append(color) 31 | 32 | def genstringforcolors(colors): 33 | s2 = [] 34 | i = 0 35 | for c in colors: 36 | if i > 0 and i % 5 == 0: 37 | s2.append("\n\t") 38 | s2.append("(%3i,%3i,%3i), " % (c[0],c[1],c[2])) 39 | i = i + 1 40 | return "".join(s2)[:-2] 41 | 42 | print("colors = [\n\t%s\n]" % genstringforcolors(colors)) 43 | print("back_colors = [\n\t%s\n]" % genstringforcolors(back_colors)) 44 | print("fore_colors = [\n\t%s\n]" % genstringforcolors(fore_colors)) 45 | 46 | generate_colors() 47 | -------------------------------------------------------------------------------- /terminal/__init__.py: -------------------------------------------------------------------------------- 1 | from . import emulator 2 | 3 | -------------------------------------------------------------------------------- /terminal/ctrl.py: -------------------------------------------------------------------------------- 1 | NUL = 0x0 # NULL 2 | SP = 0x20 # space 3 | BEL = 0x07 # ^G) beeps; 4 | BS = 0x08 # ^H) backspaces one column (but not past the beginning of the line); 5 | HT = 0x09 # ^I) goes to the next tab stop or to the end of the line if there is no earlier tab stop; 6 | LF = 0x0A # ^J) 7 | VT = 0x0B # ^K) and FF (0x0C, ^L) all give a linefeed, and if LF/NL (new-line mode) is set also a carriage return; 8 | FF = 0x0C # see VT 9 | CR = 0x0D # ^M) gives a carriage return; 10 | SO = 0x0E # ^N) activates the G1 character set; 11 | SI = 0x0F # ^O) activates the G0 character set; 12 | CAN = 0x18 # ^X) and SUB (0x1A, ^Z) interrupt escape sequences; 13 | SUB = 0x1A # see CAN 14 | ESC = 0x1B # ^[) starts an escape sequence; 15 | DEL = 0x7F # is ignored; 16 | CSI = 0x9B # is equivalent to ESC [. 17 | -------------------------------------------------------------------------------- /terminal/cursor.py: -------------------------------------------------------------------------------- 1 | class Cursor: 2 | def __init__(self, x, y, visible = True): 3 | self.x = x 4 | self.y = y 5 | self.visible = visible 6 | -------------------------------------------------------------------------------- /terminal/emulator.py: -------------------------------------------------------------------------------- 1 | from . import ctrl, esc, cursor, screen 2 | 3 | class Emulator: 4 | def __init__(self, rows=80, cols=25, debug=True): 5 | 6 | # configure two supported screens and cursors 7 | self.cols, self.rows = cols, rows 8 | self.screens = [screen.Screen(rows, cols), screen.Screen(rows, cols)] 9 | self.cursors = [cursor.Cursor(0,0), cursor.Cursor(0, 0)] 10 | 11 | # set currently active screen, cursor, scroll region and graphics rendition settings 12 | self.screen = self.screens[0] 13 | self.cursor = self.cursors[0] 14 | self.scroll_bottom = rows - 1 15 | self.scroll_top = 0 16 | self.gfx = 0 17 | 18 | # vars for parser state machine of escape sequences etc 19 | self.escape_str = "" 20 | self.in_escape = False # in escape sequence 21 | self.in_csi = False # in Control Sequence Introducer 22 | 23 | # xterm set title support 24 | self.current_title = None # currently set title 25 | self.set_title = False # set window title 26 | self.ignore_title = True # ignore setting of window title 27 | 28 | # misc settings 29 | self.tabstop = 8 # amount of spaces in a tab 30 | self.draw_lines = False # line drawing mode 31 | self.insert_mode = False # insert mode for drawing characters 32 | self.cursor_keys = False # interpet cursor keys on numpad as keys or not 33 | 34 | # references to callback functions 35 | self.callback_data = None # respond with data to f.e. a specific CSI sequence 36 | self.callback_update = None # let caller know there's new data available 37 | self.callback_title = None # inform caller of updated window title 38 | 39 | # debug log support 40 | self.debug = debug 41 | self.debuglog = [] 42 | self.debugloglen = 256 # maximum number of lines stored in self.debuglog 43 | 44 | # control character dispatch 45 | self.ctrl_dispatch = { 46 | ctrl.BEL : self.bell, 47 | ctrl.BS : self.backspace, 48 | ctrl.HT : self.htab, 49 | ctrl.LF : self.lf, 50 | ctrl.VT : self.lf, 51 | ctrl.FF : self.lf, 52 | ctrl.CR : self.cr, 53 | ctrl.SO : self.shift_out, 54 | ctrl.SI : self.shift_in, 55 | } 56 | 57 | # escape sequence dispatcher for CSI escape sequences as per VT102 58 | self.csi_dispatch = { 59 | esc.ICH : self.insert_chars, 60 | esc.CUU : self.cursor_up, 61 | esc.CUD : self.cursor_down, 62 | esc.CUF : self.cursor_forward, 63 | esc.CUB : self.cursor_back, 64 | esc.CNL : self.cursor_next_line, 65 | esc.CPL : self.cursor_prev_line, 66 | esc.CHA : self.cursor_halign, 67 | esc.CUP : self.cursor_set, 68 | esc.ED : self.erase_data, 69 | esc.EL : self.erase_in_line, 70 | esc.IL : self.insert_lines, 71 | esc.DL : self.delete_lines, 72 | esc.DCH : self.delete_characters, 73 | esc.ECH : self.erase_characters, 74 | esc.HPR : self.cursor_forward, 75 | esc.DA : self.report_da, 76 | esc.VPA : self.cursor_set, 77 | #esc.VPR : self.vertical_position_relative, 78 | #esc.HVP : self.hvposition, 79 | esc.TBC : self.tabulation_clear, 80 | esc.SM : self.set_mode, 81 | esc.RM : self.reset_mode, 82 | esc.SGR : self.select_gfx, 83 | esc.DSR : self.report_status, 84 | esc.DECSTBM : self.scroll_region, 85 | esc.HPA : self.cursor_halign, 86 | #esc.RIS : self.reset, 87 | #esc.IND : self.index, 88 | #esc.RI : self.rindex, 89 | #esc.NEL : self.nextline, 90 | esc.DECSC : self.cursor_save, 91 | esc.DECRC : self.cursor_restore, 92 | } 93 | 94 | # charset setting identifiers used for non "standard" escape sequences etc 95 | self.charset = [" ", "#", "%", "(", ")", "*", "+"] 96 | 97 | # replace key chars with values when in line drawing mode 98 | self.line_draw_map = { 99 | u"j": u"\xe2\x94\x98", 100 | u"k": u"\xe2\x94\x90", 101 | u"l": u"\xe2\x94\x8c", 102 | u"m": u"\xe2\x94\x94", 103 | u"n": u"\xe2\x94\xbc", 104 | u"q": u"\xe2\x94\x80", 105 | u"t": u"\xe2\x94\x9c", 106 | u"u": u"\xe2\x94\xa4", 107 | u"v": u"\xe2\x94\xb4", 108 | u"w": u"\xe2\x94\xac", 109 | u"x": u"\xe2\x94\x82", 110 | } 111 | 112 | def set_update_callback(self, cb): 113 | self.callback_update = cb 114 | 115 | def set_data_callback(self, cb): 116 | self.callback_data = cb 117 | 118 | def set_title_callback(self, cb): 119 | self.callback_title = cb 120 | 121 | def update_callback(self): 122 | if self.callback_update: 123 | self.callback_update() 124 | 125 | def data_callback(self, data): 126 | if self.callback_data: 127 | self.callback_data(data) 128 | 129 | def title_callback(self, title): 130 | if title == self.current_title: 131 | return 132 | self.current_title = title 133 | if self.callback_title: 134 | self.callback_title(self.current_title) 135 | 136 | def shift_in(self): 137 | raise Exception("shift in") 138 | 139 | def shift_out(self): 140 | raise Exception("shift out") 141 | 142 | def bell(self): 143 | pass 144 | 145 | def backspace(self): 146 | if self.cursor.x > 0: 147 | self.cursor.x = self.cursor.x - 1 148 | 149 | def htab(self): 150 | self.cursor.x = self.cursor.x + self.tabstop - (self.cursor.x % self.tabstop) 151 | if self.cursor.x > self.cols: 152 | self.cursor.x = self.cols 153 | 154 | def cursor_save(self, *params): 155 | print("Save cursor1") 156 | #raise Exception("save cursor") 157 | 158 | def cursor_restore(self, *params): 159 | print("Save cursor2") 160 | #raise Exception("restore cursor") 161 | 162 | def insert_lines(self, count=None): 163 | pass 164 | 165 | def insert_chars(self, count=None): 166 | #raise Exception("todo") 167 | pass 168 | 169 | def cursor_up(self, count=None): 170 | self.cursor.y += count or 1 171 | self.fix_cursors() 172 | 173 | def cursor_down(self, count=None): 174 | self.cursor.y -= count or 1 175 | self.fix_cursors() 176 | 177 | def cursor_forward(self, count=None): 178 | self.cursor.x += count or 1 179 | self.fix_cursors() 180 | 181 | def cursor_back(self, count=None): 182 | self.cursor.x -= count or 1 183 | self.fix_cursors() 184 | 185 | def cursor_next_line(self, count=None): 186 | self.cursor_down(count) 187 | self.cursor.x = 0 188 | 189 | def cursor_prev_line(self, count=None): 190 | self.cursor_up(count) 191 | self.cursor.x = 0 192 | 193 | def cursor_halign(self, param=None): 194 | raise Exception("!!!!", param) 195 | if param: 196 | self.cursor.x = param - 1 197 | self.fix_cursors() 198 | 199 | def cursor_set(self, y = None, x = None): 200 | x = 0 if not x or x == 0 else x - 1 201 | y = 0 if not y or y == 0 else y - 1 202 | self.cursor.x = x 203 | self.cursor.y = y 204 | self.fix_cursors() 205 | 206 | def fix_cursor_bounds(self, cursor): 207 | if cursor.x > self.cols: 208 | cursor.x = self.cols 209 | if cursor.y >= self.rows: 210 | cursor.y = self.rows - 1 211 | 212 | def fix_cursors(self): 213 | self.fix_cursor_bounds(self.cursors[0]) 214 | self.fix_cursor_bounds(self.cursors[1]) 215 | 216 | def delete_lines(self, count): 217 | raise Exception("delete lines") 218 | 219 | def delete_characters(self, *params): 220 | raise Exception("delete characters") 221 | 222 | def report_da(self, *params): 223 | self.data_callback("\033[?1;2c") 224 | 225 | def is_alternative_screen(self): 226 | return self.screen == self.screens[1] 227 | 228 | def set_mode(self, *params): 229 | if len(params) < 0: 230 | return 231 | private = False 232 | if params[-1] == "?": 233 | private = True 234 | params = params[:-1] 235 | for p in params: 236 | if p == 4: 237 | self.insert_mode = True 238 | elif p == 1049 or p == 47 and not self.is_alternative_screen(): 239 | self.screen = self.screen[1] 240 | self.cursor = self.cursor[1] 241 | elif p == 1: 242 | self.cursor_keys = True 243 | elif p == 2: 244 | pass 245 | elif p == 7: 246 | pass 247 | elif p == 12: 248 | pass 249 | elif p == 25: 250 | raise Exception("visible cursor") 251 | self.cursor.visible = True 252 | elif p == 0: 253 | pass 254 | else: 255 | raise Exception("set mode unkonwn: %s" % p) 256 | 257 | 258 | def reset_mode(self, *params): 259 | if len(params) < 0: 260 | return 261 | private = False 262 | if params[-1] == "?": 263 | private = True 264 | params = params[:-1] 265 | for p in params: 266 | if p == 4: 267 | self.insert_mode = False 268 | elif p == 1049 or p == 47 and self.is_alternative_screen(): 269 | self.screen = self.screen[0] 270 | self.cursor = self.cursor[0] 271 | elif p == 1: 272 | self.cursor_keys = False 273 | elif p == 2: 274 | pass 275 | elif p == 7: 276 | pass 277 | elif p == 12: 278 | pass 279 | elif p == 25: 280 | raise Exception("not visible cursor") 281 | self.cursor.visible = False 282 | elif p == 0: 283 | pass 284 | else: 285 | raise Exception("set mode unkonwn: %s" % p) 286 | 287 | def report_status(self, param=None): 288 | if not param or param not in [5, 6]: 289 | return 290 | if param == 5: 291 | self.data_callback("\033[0n") 292 | elif param == 6: 293 | self.data_callback("\033[%d;%dR" % (self.cursor.y + 1, self.cursor.x + 1)) 294 | 295 | def scroll_region(self, *params): 296 | if len(params) < 2: 297 | return 298 | 299 | self.scroll_top = params[0] - 1 300 | self.scroll_bottom = params[1] - 1 301 | if self.scroll_top < 0: 302 | self.scroll_top = 0 303 | elif self.scroll_top >= self.rows: 304 | self.scroll_top = self.rows - 1 305 | if self.scroll_bottom < 0: 306 | self.scroll_bottom = 0 307 | elif self.scroll_bottom >= self.rows: 308 | self.scroll_bottom = self.rows - 1 309 | 310 | def select_gfx(self, *params): 311 | i = 0 312 | lp = len(params) 313 | # ISO-8613-3 support for 256-colors is implemented 314 | # 24-bit color support is not implemented 315 | if (lp == 0): 316 | raise Exception("no args to gfx") 317 | while i < lp: 318 | p = params[i] 319 | if p == 0: 320 | # default graphics rendition 321 | self.gfx = 0 322 | elif p >= 1 and p <= 9: 323 | # set a style 324 | self.gfx &= ~0xff 325 | self.gfx |= 1 << (p - 1) 326 | elif p >= 21 and p <= 29: 327 | # clear a style 328 | self.gfx &= ~(1 << (p - 21)) 329 | elif p >= 30 and p <= 37: 330 | self.gfx &= ~(0x00FF0000 | screen.GFX_FG) 331 | self.gfx |= ((p - 29) << 16) 332 | elif p == 38 and i + 2 < len(params): 333 | if params[i+1] == 5: 334 | self.gfx &= ~0x00ff0000 335 | self.gfx |= screen.GFX_FG 336 | self.gfx |= (params[i+2] & 0xff) << 16 337 | i = i + 2 338 | elif p == 39: 339 | self.gfx &= ~(0x00ff0000 | screen.GFX_FG) 340 | elif p >= 40 and p <= 47: 341 | self.gfx &= ~(0xFF000000 | screen.GFX_BG) 342 | self.gfx |= ((p - 39) << 24) 343 | elif p == 48 and i + 2 < len(params): 344 | if params[i+1] == 5: 345 | self.gfx &= ~0x00ff0000 346 | self.gfx |= screen.GFX_BG 347 | self.gfx |= (params[i+2] & 0xff) << 24 348 | i = i + 2 349 | elif p == 49: 350 | self.gfx &= ~(0xff000000 | screen.GFX_BG) 351 | elif p >= 90 and p <= 97: 352 | self.gfx &= ~(0x00ff0000 | screen.GFX_FG) 353 | self.gfx |= ((p - 81) << 16) 354 | elif p >= 100 and p <= 107: 355 | self.gfx &= ~(0xff000000 | screen.GFX_BG) 356 | self.gfx |= ((p - 91) << 16) 357 | else: 358 | raise Exception("unsupported gfx rendition %i" % p) 359 | i = i + 1 360 | 361 | def tabulation_clear(self, param = 0): 362 | if param == 0: 363 | self.tabstops.discard(self.cursor.x) 364 | elif param == 3: 365 | self.tabstops = set() 366 | 367 | def erase_characters(self, count=None): 368 | if not count or count == 0: 369 | count = 1 370 | self.screen.erase_rectangle(self.cursor.y, self.cursor.x, self.cursor.y + 1, self.x + count, self.gfx) 371 | 372 | def erase_in_line(self, param = None): 373 | if not param: 374 | param = 0 375 | if param == 0: 376 | self.screen.erase_rectangle(self.cursor.y, self.cursor.x, self.cursor.y + 1, self.cols, self.gfx) 377 | elif param == 1: 378 | self.screen.erase_rectangle(self.cursor.y, 0, self.cursor.y + 1, self.cursor.x + 1, self.gfx) 379 | elif param == 2: 380 | self.screen.erase_rectangle(self.cursor.y, 0, self.cursor.y + 1, self.cols, self.gfx) 381 | 382 | def erase_data(self, param = None): 383 | if not param: 384 | param = 0 385 | if param == 0: 386 | self.screen.erase_rectangle(self.cursor.y, self.cursor.x, self.cursor.y + 1, self.cols, self.gfx) 387 | self.screen.erase_rectangle(self.cursor.y + 1, 0, self.rows, self.cols, self.gfx) 388 | elif param == 1: 389 | self.screen.erase_rectangle(0, 0, self.cursor.y, self.cols, self.gfx) 390 | self.screen.erase_rectangle(self.cursor.y, 0, self.cursor.y + 1, self.cursor.x + 1, self.gfx) 391 | elif param == 2: 392 | self.screen.erase_rectangle(0, 0, self.rows, self.cols, self.gfx) 393 | self.cursor.x, self.cursor.y = 0, 0 394 | 395 | def resize(self, rows, cols): 396 | if rows == self.rows and cols == self.cols: 397 | return 398 | self.screens[0].resize(rows, cols) 399 | self.screens[1].resize(rows, cols) 400 | self.scroll_top = 0 401 | self.scroll_bottom = rows - 1 402 | self.rows = rows 403 | self.cols = cols 404 | self.fix_cursors() 405 | 406 | def lf(self): 407 | if self.cursor.y >= self.scroll_bottom: 408 | self.screen.scroll_up(self.scroll_top, self.scroll_bottom, self.is_alternative_screen(), self.gfx) 409 | else: 410 | self.cursor.y += 1 411 | 412 | def cr(self): 413 | self.cursor.x = 0 414 | 415 | def newline(self): 416 | self.lf() 417 | self.cursor.x = 0 418 | 419 | def write_char(self, ch): 420 | if self.cursor.x >= self.cols: 421 | self.newline() 422 | 423 | if self.draw_lines: 424 | if ch in self.linemap: 425 | ch = self.linemap[ch] 426 | 427 | if self.insert_mode: 428 | self.insert_chars([1]) 429 | 430 | self.screen.write_char(self.cursor, ch, self.gfx) 431 | self.cursor.x += 1 432 | 433 | self.update_callback() 434 | 435 | def parse_escape_sequence(self, data): 436 | l = len(data) 437 | if l == 0: 438 | # XXX log 439 | raise Exception("unhandled escape seq") 440 | return 441 | 442 | if data[0] != "[": 443 | # ignore numpad handling 444 | if data[0] in ["=", ">"]: 445 | return 446 | else: 447 | raise Exception("unhandled data %s" % data) 448 | 449 | seq = data[1:-1] 450 | mode = ord(data[-1]) 451 | option = None 452 | if len(seq) > 1 and seq[0] in ["?", ">", "!"]: 453 | option = seq[0] 454 | seq = seq[1] 455 | try: 456 | if len(seq) > 0: 457 | params = [int(x) for x in seq.split(";")] 458 | else: 459 | params = [0] 460 | except Exception as e: 461 | params = [0] 462 | 463 | if mode not in self.csi_dispatch: 464 | s="".join(["%x " % ord(x) for x in data]) 465 | s2="".join(["%c " % ord(x) for x in data]) 466 | self.write_debug_logdata("! unknown CSI: %s [%s]" % (s2, s)) 467 | return 468 | 469 | if not option: 470 | self.csi_dispatch[mode](*params) 471 | else: 472 | self.csi_dispatch[mode](*params, option) 473 | self.write_debug_logdata("ESC %s -> %s" % (data, self.csi_dispatch[mode])) 474 | 475 | def write_debug_logdata(self, data): 476 | if not self.debug: 477 | return 478 | data = data.splitlines() 479 | for line in data: 480 | self.debuglog.append(line) 481 | ld = len(self.debuglog) 482 | if ld > self.debugloglen: 483 | self.debuglog = self.debuglog[ld - self.debugloglen:] 484 | 485 | def parse_data(self, data): 486 | for c in data: 487 | oc = ord(c) 488 | if self.set_title: 489 | if oc != ctrl.BEL: 490 | self.escape_str += c 491 | continue 492 | if self.ignore_title == False: 493 | self.title_callback(self.escape_str) 494 | self.escape_str = "" 495 | self.set_title = False 496 | continue 497 | elif oc in self.ctrl_dispatch: 498 | self.ctrl_dispatch[oc]() 499 | continue 500 | elif self.in_escape and not self.in_csi: 501 | self.escape_str += c 502 | l = len(self.escape_str) 503 | if l == 1 and c == "[": 504 | self.in_csi = True 505 | continue 506 | elif l == 1 and c != "[" and c != "]" and c not in self.charset: 507 | self.parse_escape_sequence(self.escape_str) 508 | self.escape_str = "" 509 | self.in_escape = False 510 | continue 511 | elif l == 2 and self.escape_str[0] in self.charset: 512 | if self.escape_str == "(0": 513 | self.draw_lines = True 514 | else: 515 | self.draw_lines = False 516 | self.escape_str = "" 517 | self.in_escape = False 518 | continue 519 | elif l > 1 and self.escape_str[0] == "]" and c == ";": 520 | self.set_title = True 521 | try: 522 | param = [int(x) for x in self.escape_str[1:-1].split(";")] 523 | except: 524 | param = [0] 525 | if len(param) == 0 or param[0]==0 or param[0] == 2: 526 | self.ignore_title = False 527 | else: 528 | self.ignore_title = True 529 | self.in_escape = False 530 | self.escape_str = "" 531 | continue 532 | elif self.in_csi: 533 | self.escape_str += c 534 | if oc >= ord("@") and oc <= ord("~"): 535 | self.parse_escape_sequence(self.escape_str) 536 | self.in_csi = False 537 | self.in_escape = False 538 | self.escape_str = "" 539 | continue 540 | elif oc == ctrl.ESC: 541 | self.in_escape = True 542 | continue 543 | else: 544 | self.write_char(c) 545 | 546 | def input_data(self, data): 547 | try: 548 | data = data.decode(encoding="utf8") 549 | self.parse_data(data) 550 | except Exception as e: 551 | self.write_debug_logdata(e) 552 | -------------------------------------------------------------------------------- /terminal/esc.py: -------------------------------------------------------------------------------- 1 | # Taken from: VT102 User Guide 2 | # found http://vt100.net/docs/vt102-ug/chapter5.html 3 | ICH = 64 # "@" -- Insert Blank Characters 4 | CUU = 65 # "A" -- Cursur Up 5 | CUD = 66 # "B" -- Cursor Down 6 | CUF = 67 # "C" -- Cursor Forward 7 | CUB = 68 # "D" -- Cursor Backward 8 | CNL = 69 # "E" -- Cursor Next line 9 | CPL = 70 # "F" -- Cursor Previous Line 10 | CHA = 71 # "G" -- Cursor Horizontal Align 11 | CUP = 72 # "H" -- Cursor Position 12 | ED = 74 # "J" -- Erase data 13 | EL = 75 # "K" -- Erase in line 14 | IL = 76 # "L" -- Insert line 15 | DL = 77 # "M" -- Delete line 16 | DCH = 80 # "P" -- Delete character 17 | ECH = 88 # "X" -- Erase character 18 | HPR = 97 # "a" -- Horizontal Postion Relative 19 | DA = 99 # "c" -- Device Attributes 20 | VPA = 100 # "d" -- Vertial Position Adjust 21 | VPR = 101 # "e" -- Vertical Position Relative 22 | HVP = 102 # "f" -- Horizontal / Vertical postion 23 | TBC = 103 # "g" -- Tabulation Clear 24 | SM = 104 # "h" -- Set Mode 25 | RM = 108 # "l" -- Reset Mode" 26 | SGR = 109 # "m" -- Select Graphics rendition" 27 | DSR = 110 # "n" -- Device Status Report" 28 | DECSTBM = 114 # "r" -- Select Top and Bottom Margins 29 | HPA = 39 # "'" -- Horizontal Position Adjust 30 | RIS = 99 # "c" -- Reset to Initial State 31 | IND = 68 # "D" -- Index 32 | RI = 77 # "M" -- Reverse Index 33 | NEL = 69 # "E" -- Next Line 34 | DECSC = 55 # "7" -- Save Cursor 35 | DECRC = 56 # "8" -- Restore Cursor 36 | -------------------------------------------------------------------------------- /terminal/process.py: -------------------------------------------------------------------------------- 1 | import pty, os, subprocess, fcntl, termios 2 | import struct, select, threading 3 | 4 | from . import emulator 5 | 6 | class TerminalProcessThread(threading.Thread): 7 | def __init__(self, rows, cols, cmd="/bin/sh"): 8 | super(TerminalProcessThread, self).__init__() 9 | 10 | self.pipes = os.pipe() 11 | self.pid, self.fd = pty.fork() 12 | if self.pid == 0: 13 | os.environ["TERM"] = "xterm-256color" 14 | ret = subprocess.call(cmd, close_fds=True) 15 | os.write(self.pipes[1], "\x00") 16 | os._exit(1) 17 | os.close(self.pipes[1]) 18 | self.stopped = False 19 | 20 | self.emulator = emulator.Emulator(rows, cols) 21 | self.set_rowcol_pty() 22 | 23 | def input(self, data): 24 | ret = False 25 | try: 26 | ret = os.write(self.fd, bytes(data, encoding="utf-8")) 27 | except Exception as e: 28 | print(e) 29 | 30 | def stop(self): 31 | self.stopped = True 32 | 33 | def _run(self): 34 | timeout = 0.01 35 | i, o, e = select.select([self.fd, self.pipes[0]], [], [], timeout) 36 | if self.fd in i: 37 | try: 38 | data = os.read(self.fd, 4096) 39 | self.emulator.input_data(data) 40 | except: 41 | pass 42 | 43 | if self.pipes[0] in i: 44 | try: 45 | data = os.read(self.pipes[0], 1) 46 | except: 47 | pass 48 | self.stop() 49 | 50 | def run(self): 51 | while not self.stopped: 52 | self._run() 53 | 54 | os.close(self.fd) 55 | os.close(self.pipes[0]) 56 | 57 | def set_rowcol_pty(self): 58 | data = struct.pack("HHHH", self.emulator.rows, self.emulator.cols, 0, 0) 59 | fcntl.ioctl(self.fd, termios.TIOCSWINSZ, data) 60 | attribute = termios.tcgetattr(self.fd) 61 | termios.tcsetattr(self.fd, termios.TCSAFLUSH, attribute) 62 | 63 | def resize(self, rows, cols): 64 | # prevent unnecessary updates 65 | if rows == self.emulator.rows and cols == self.emulator.cols: 66 | return 67 | self.emulator.resize(rows, cols) 68 | self.set_rowcol_pty() 69 | -------------------------------------------------------------------------------- /terminal/rendition.py: -------------------------------------------------------------------------------- 1 | """ 2 | generate the color scheme with the code below 3 | 4 | def generate_colors(): 5 | # System colors 6 | dim_colors = [(0,0,0), (135, 0, 0), (0, 135, 0), (135, 135, 0), 7 | (0, 0, 135), (135, 0, 135), (0, 135, 135), (135, 135, 135)] 8 | normal_colors = [(46, 52, 54), (204, 0, 0), (78, 154, 6), (196, 160, 0), 9 | (52, 101, 164), (117, 80, 123), (6, 152, 154), (211, 215, 207)] 10 | bright_colors = [(85, 87, 83), (239, 41, 41), (138, 226, 52), (252, 233, 79), 11 | (114, 159, 207), (173, 127, 168), (52, 226, 226), (255,255,255)] 12 | 13 | # Create color arrays for normal mode 14 | fore_colors = [(255,255,255)] + dim_colors + normal_colors + bright_colors + bright_colors 15 | back_colors = [(0,0,0)] + normal_colors + bright_colors 16 | 17 | # Create color array for 256-color mode 18 | colors = normal_colors + bright_colors 19 | 20 | values = [0x00, 0x5f, 0x87, 0xaf, 0xd7, 0xff] 21 | for red in values: 22 | for green in values: 23 | for blue in values: 24 | color = (red, green, blue) 25 | colors.append(color) 26 | 27 | values = [0x08, 0x12, 0x1c, 0x26, 0x30, 0x3a, 0x44, 0x4e, 0x58, 0x62, 0x6c, 0x76, 0x80, 0x8a, 0x94, 0x9e, 28 | 0xa8, 0xb2, 0xbc, 0xc6, 0xd0, 0xda, 0xe4, 0xee] 29 | for gray in values: 30 | color = (gray, gray, gray) 31 | colors.append(color) 32 | 33 | def genstringforcolors(colors): 34 | s2 = [] 35 | i = 0 36 | for c in colors: 37 | if i > 0 and i % 5 == 0: 38 | s2.append("\n\t") 39 | s2.append("(%3i,%3i,%3i), " % (c[0],c[1],c[2])) 40 | i = i + 1 41 | return "".join(s2)[:-2] 42 | 43 | print "colors = [\n\t%s\n]" % genstringforcolors(colors) 44 | print "back_colors = [\n\t%s\n]" % genstringforcolors(back_colors) 45 | print "fore_colors = [\n\t%s\n]" % genstringforcolors(fore_colors) 46 | 47 | generate_colors() 48 | """ 49 | colors = [ 50 | ( 46, 52, 54), (204, 0, 0), ( 78,154, 6), (196,160, 0), ( 52,101,164), 51 | (117, 80,123), ( 6,152,154), (211,215,207), ( 85, 87, 83), (239, 41, 41), 52 | (138,226, 52), (252,233, 79), (114,159,207), (173,127,168), ( 52,226,226), 53 | (255,255,255), ( 0, 0, 0), ( 0, 0, 95), ( 0, 0,135), ( 0, 0,175), 54 | ( 0, 0,215), ( 0, 0,255), ( 0, 95, 0), ( 0, 95, 95), ( 0, 95,135), 55 | ( 0, 95,175), ( 0, 95,215), ( 0, 95,255), ( 0,135, 0), ( 0,135, 95), 56 | ( 0,135,135), ( 0,135,175), ( 0,135,215), ( 0,135,255), ( 0,175, 0), 57 | ( 0,175, 95), ( 0,175,135), ( 0,175,175), ( 0,175,215), ( 0,175,255), 58 | ( 0,215, 0), ( 0,215, 95), ( 0,215,135), ( 0,215,175), ( 0,215,215), 59 | ( 0,215,255), ( 0,255, 0), ( 0,255, 95), ( 0,255,135), ( 0,255,175), 60 | ( 0,255,215), ( 0,255,255), ( 95, 0, 0), ( 95, 0, 95), ( 95, 0,135), 61 | ( 95, 0,175), ( 95, 0,215), ( 95, 0,255), ( 95, 95, 0), ( 95, 95, 95), 62 | ( 95, 95,135), ( 95, 95,175), ( 95, 95,215), ( 95, 95,255), ( 95,135, 0), 63 | ( 95,135, 95), ( 95,135,135), ( 95,135,175), ( 95,135,215), ( 95,135,255), 64 | ( 95,175, 0), ( 95,175, 95), ( 95,175,135), ( 95,175,175), ( 95,175,215), 65 | ( 95,175,255), ( 95,215, 0), ( 95,215, 95), ( 95,215,135), ( 95,215,175), 66 | ( 95,215,215), ( 95,215,255), ( 95,255, 0), ( 95,255, 95), ( 95,255,135), 67 | ( 95,255,175), ( 95,255,215), ( 95,255,255), (135, 0, 0), (135, 0, 95), 68 | (135, 0,135), (135, 0,175), (135, 0,215), (135, 0,255), (135, 95, 0), 69 | (135, 95, 95), (135, 95,135), (135, 95,175), (135, 95,215), (135, 95,255), 70 | (135,135, 0), (135,135, 95), (135,135,135), (135,135,175), (135,135,215), 71 | (135,135,255), (135,175, 0), (135,175, 95), (135,175,135), (135,175,175), 72 | (135,175,215), (135,175,255), (135,215, 0), (135,215, 95), (135,215,135), 73 | (135,215,175), (135,215,215), (135,215,255), (135,255, 0), (135,255, 95), 74 | (135,255,135), (135,255,175), (135,255,215), (135,255,255), (175, 0, 0), 75 | (175, 0, 95), (175, 0,135), (175, 0,175), (175, 0,215), (175, 0,255), 76 | (175, 95, 0), (175, 95, 95), (175, 95,135), (175, 95,175), (175, 95,215), 77 | (175, 95,255), (175,135, 0), (175,135, 95), (175,135,135), (175,135,175), 78 | (175,135,215), (175,135,255), (175,175, 0), (175,175, 95), (175,175,135), 79 | (175,175,175), (175,175,215), (175,175,255), (175,215, 0), (175,215, 95), 80 | (175,215,135), (175,215,175), (175,215,215), (175,215,255), (175,255, 0), 81 | (175,255, 95), (175,255,135), (175,255,175), (175,255,215), (175,255,255), 82 | (215, 0, 0), (215, 0, 95), (215, 0,135), (215, 0,175), (215, 0,215), 83 | (215, 0,255), (215, 95, 0), (215, 95, 95), (215, 95,135), (215, 95,175), 84 | (215, 95,215), (215, 95,255), (215,135, 0), (215,135, 95), (215,135,135), 85 | (215,135,175), (215,135,215), (215,135,255), (215,175, 0), (215,175, 95), 86 | (215,175,135), (215,175,175), (215,175,215), (215,175,255), (215,215, 0), 87 | (215,215, 95), (215,215,135), (215,215,175), (215,215,215), (215,215,255), 88 | (215,255, 0), (215,255, 95), (215,255,135), (215,255,175), (215,255,215), 89 | (215,255,255), (255, 0, 0), (255, 0, 95), (255, 0,135), (255, 0,175), 90 | (255, 0,215), (255, 0,255), (255, 95, 0), (255, 95, 95), (255, 95,135), 91 | (255, 95,175), (255, 95,215), (255, 95,255), (255,135, 0), (255,135, 95), 92 | (255,135,135), (255,135,175), (255,135,215), (255,135,255), (255,175, 0), 93 | (255,175, 95), (255,175,135), (255,175,175), (255,175,215), (255,175,255), 94 | (255,215, 0), (255,215, 95), (255,215,135), (255,215,175), (255,215,215), 95 | (255,215,255), (255,255, 0), (255,255, 95), (255,255,135), (255,255,175), 96 | (255,255,215), (255,255,255), ( 8, 8, 8), ( 18, 18, 18), ( 28, 28, 28), 97 | ( 38, 38, 38), ( 48, 48, 48), ( 58, 58, 58), ( 68, 68, 68), ( 78, 78, 78), 98 | ( 88, 88, 88), ( 98, 98, 98), (108,108,108), (118,118,118), (128,128,128), 99 | (138,138,138), (148,148,148), (158,158,158), (168,168,168), (178,178,178), 100 | (188,188,188), (198,198,198), (208,208,208), (218,218,218), (228,228,228), 101 | (238,238,238) 102 | ] 103 | 104 | back_colors = [ 105 | ( 0, 0, 0), ( 46, 52, 54), (204, 0, 0), ( 78,154, 6), (196,160, 0), 106 | ( 52,101,164), (117, 80,123), ( 6,152,154), (211,215,207), ( 85, 87, 83), 107 | (239, 41, 41), (138,226, 52), (252,233, 79), (114,159,207), (173,127,168), 108 | ( 52,226,226), (255,255,255) 109 | ] 110 | 111 | fore_colors = [ 112 | (255,255,255), ( 0, 0, 0), (135, 0, 0), ( 0,135, 0), (135,135, 0), 113 | ( 0, 0,135), (135, 0,135), ( 0,135,135), (135,135,135), ( 46, 52, 54), 114 | (204, 0, 0), ( 78,154, 6), (196,160, 0), ( 52,101,164), (117, 80,123), 115 | ( 6,152,154), (211,215,207), ( 85, 87, 83), (239, 41, 41), (138,226, 52), 116 | (252,233, 79), (114,159,207), (173,127,168), ( 52,226,226), (255,255,255), 117 | ( 85, 87, 83), (239, 41, 41), (138,226, 52), (252,233, 79), (114,159,207), 118 | (173,127,168), ( 52,226,226), (255,255,255) 119 | ] 120 | 121 | from . import screen 122 | 123 | def get_colors(rendition): 124 | fg = bg = None 125 | if rendition & screen.GFX_BG: 126 | bg = colors[(rendition >> 24) & 0xff] 127 | else: 128 | bg = back_colors[(rendition >> 24) & 0x1f] 129 | if rendition & screen.GFX_FG: 130 | if ((rendition >> 16) & 0xff) < 16: 131 | if rendition & screen.GFX_BOLD: 132 | fg = fore_colors[((rendition >> 16)&7)+17] 133 | else: 134 | fg = fore_colors[((rendition >> 16) & 0xf) + 9] 135 | else: 136 | fg = colors[(rendition >> 16) & 0xff] 137 | elif (rendition & 0x1f0000) == 0 or (rendition & screen.GFX_DIM): 138 | fg = fore_colors[(rendition >> 16) & 0x1f] 139 | elif rendition & screen.GFX_BOLD: 140 | fg = fore_colors[((rendition >> 16) & 0x1f) + 16] 141 | else: 142 | fg = fore_colors[((rendition >> 16) & 0x1f) + 8] 143 | if rendition & screen.GFX_INV: 144 | fg, bg = bg, fg 145 | return fg, bg 146 | -------------------------------------------------------------------------------- /terminal/screen.py: -------------------------------------------------------------------------------- 1 | import array 2 | 3 | # graphics rendition settings for bold, dim, underlined, 4 | # inverse, hidden, fgcolor, bgcolor respectively 5 | GFX_BOLD = 0x001 6 | GFX_DIM = 0x002 7 | GFX_UL = 0x008 8 | GFX_INV = 0x040 9 | GFX_HIDE = 0x080 10 | GFX_FG = 0x100 11 | GFX_BG = 0x200 12 | 13 | # special for detecting line wrap when selecting text 14 | GFX_WRITTEN = 0x400 15 | 16 | class Screen: 17 | def __init__(self, rows, cols): 18 | self.reset(rows, cols) 19 | 20 | def reset(self, rows, cols): 21 | self.cols = cols 22 | self.rows = rows 23 | self.cells = [] 24 | self.gfx = [] 25 | self.empty_line = array.array("u", u" "*cols) 26 | self.empty_gfx = array.array("I", [0]*cols) 27 | 28 | for i in range(0, rows): 29 | self.cells.append(array.array("u", self.empty_line)) 30 | self.gfx.append(array.array("I", self.empty_gfx)) 31 | 32 | def scroll_up(self, scroll_top, scroll_bottom, alt, current_gfx): 33 | top_screen = self.cells.pop(scroll_top) 34 | top_gfx = self.gfx.pop(scroll_top) 35 | if scroll_top == 0 and scroll_bottom == self.rows -1 and alt: 36 | top_screen = array.array("u", self.empty_line) 37 | top_gfx = array.array("I", self.empty_gfx) 38 | else: 39 | top_screen[0:self.cols] = array.array("u", self.empty_line) 40 | top_gfx[0:self.cols] = array.array("I", self.empty_gfx) 41 | for i in range(0, self.cols): 42 | top_gfx[i] = current_gfx 43 | self.cells.insert(scroll_bottom, top_screen) 44 | self.gfx.insert(scroll_bottom, top_gfx) 45 | 46 | def resize(self, rows, cols): 47 | if rows > self.rows: 48 | for row in range(self.rows, rows): 49 | self.cells.append(array.array("u", self.empty_line)) 50 | self.gfx.append(array.array("I", self.empty_gfx)) 51 | elif rows < self.rows: 52 | self.cells = self.cells[:rows] 53 | self.gfx = self.gfx[:rows] 54 | if cols > self.cols: 55 | for row in range(0, rows): 56 | for col in range(self.cols, cols): 57 | self.cells[row].append(u" ") 58 | self.gfx[row].append(0) 59 | self.empty_line = array.array("u", u" "*cols) 60 | self.empty_gfx = array.array("I", [0]*cols) 61 | elif cols < self.cols: 62 | for row in range(0, rows): 63 | self.cells[row] = self.cells[row][0:cols] 64 | self.gfx[row] = self.gfx[row][0:cols] 65 | self.empty_line = self.empty_line[0:cols] 66 | self.empty_gfx = self.empty_gfx[0:cols] 67 | self.rows = rows 68 | self.cols = cols 69 | 70 | def write_char(self, cursor, ch, rendition): 71 | self.cells[cursor.y][cursor.x] = ch 72 | self.gfx[cursor.y][cursor.x] = rendition | GFX_WRITTEN 73 | 74 | def erase_rectangle(self, top_row, left_col, bot_row, right_col, rendition=0): # XXX: need to pass in active rendition 75 | for y in range(top_row, bot_row): 76 | if y < 0 or y >= self.rows: 77 | continue 78 | for x in range(left_col, right_col): 79 | if x < 0 or x >= self.cols: 80 | continue 81 | self.cells[y][x] = u" " 82 | self.gfx[y][x] = rendition 83 | --------------------------------------------------------------------------------