UNPKG

23.2 kBJavaScriptView Raw
1/*
2 Terminal Kit
3
4 Copyright (c) 2009 - 2020 Cédric Ronvel
5
6 The MIT License (MIT)
7
8 Permission is hereby granted, free of charge, to any person obtaining a copy
9 of this software and associated documentation files (the "Software"), to deal
10 in the Software without restriction, including without limitation the rights
11 to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
12 copies of the Software, and to permit persons to whom the Software is
13 furnished to do so, subject to the following conditions:
14
15 The above copyright notice and this permission notice shall be included in all
16 copies or substantial portions of the Software.
17
18 THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
19 IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
20 FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
21 AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
22 LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
23 OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
24 SOFTWARE.
25*/
26
27"use strict" ;
28
29
30
31const string = require( 'string-kit' ) ;
32
33
34
35
36
37/* Escape sequences */
38
39
40
41// Mini-doc:
42
43// ESC = \x1b
44// CSI = ESC + [ (Control Sequence Introducer)
45// OSC = ESC + ] (OS Command)
46// DSC = ESC + P
47// ST = ESC + \ (string terminator: it ends user strings and some sequences)
48
49// CSI: ESC + [ + <command> + <type>
50// It is possible to separate many command with a ';' before the final 'type'.
51
52// See: http://en.wikipedia.org/wiki/ANSI_escape_code
53// and: http://invisible-island.net/xterm/ctlseqs/ctlseqs.html
54// man tput
55// man infocmp
56// man 5 terminfo
57// For tput tcap name, see: http://pubs.opengroup.org/onlinepubs/7990989799/xcurses/terminfo.html
58// useful command: infocmp -l $TERM
59
60
61// Common sequences
62
63// Remove colors
64const defaultColor = '\x1b[39m' ; // back to the default color, most of time it is the same than .white
65const bgDefaultColor = '\x1b[49m' ; // back to the default color, most of time it is the same than .bgBlack
66
67
68
69const esc = {
70
71 /* Common sequences */
72
73 // Reset the terminal
74 reset: { on: '\x1bc' } ,
75
76 /* Cursor sequences */
77
78 saveCursor: { on: '\x1b7' } , // also '\x1b[s'
79 restoreCursor: { on: '\x1b8' } , // also '\x1b[u'
80
81 up: { on: '\x1b[%UA' } ,
82 down: { on: '\x1b[%UB' } ,
83 right: { on: '\x1b[%UC' } ,
84 left: { on: '\x1b[%UD' } ,
85 nextLine: { on: '\x1b[%UE' } ,
86 previousLine: { on: '\x1b[%UF' } ,
87 column: { on: '\x1b[%UG' } ,
88 row: { on: '\x1b[%Ud' } ,
89 moveTo: { on: '\x1b[%+1U;%-1UH' , optimized: ( x , y ) => '\x1b[' + y + ';' + x + 'H' } ,
90 //moveToBottomLeft: { on: '\x1bF' } , // Not widely supported
91 hideCursor: { on: '\x1b[?25l' , off: '\x1b[?25h' } ,
92
93 tabSet: { on: '\x1bH' } , // HTS
94 tabClear: { on: '\x1b[0g' } , // TBC
95 tabClearAll: { on: '\x1b[3g' } , // TBC
96 forwardTab: { on: '\x1b[%UI' } , // CHT
97 backwardTab: { on: '\x1b[%UZ' } , // CBT
98
99 // Cursor styles
100 blockCursor: { on: '\x1b[2 q' } ,
101 blinkingBlockCursor: { on: '\x1b[0 q' } ,
102 underlineCursor: { on: '\x1b[4 q' } ,
103 blinkingUnderlineCursor: { on: '\x1b[3 q' } ,
104 beamCursor: { on: '\x1b[6 q' } ,
105 blinkingBeamCursor: { on: '\x1b[5 q' } ,
106
107 /* Editing sequences */
108
109 clear: { on: '\x1b[H\x1b[2J' } ,
110 eraseDisplayBelow: { on: '\x1b[0J' } ,
111 eraseDisplayAbove: { on: '\x1b[1J' } ,
112 eraseDisplay: { on: '\x1b[2J' } ,
113 eraseSavedLine: { on: '\x1b[3J' } ,
114 eraseLineAfter: { on: '\x1b[0K' } ,
115 eraseLineBefore: { on: '\x1b[1K' } ,
116 eraseLine: { on: '\x1b[2K' } ,
117 insertLine: { on: '\x1b[%UL' } ,
118 deleteLine: { on: '\x1b[%UM' } ,
119 insert: { on: '\x1b[%U@' } , // insert char
120 'delete': { on: '\x1b[%UP' } , // delete char
121 erase: { on: '\x1b[%UX' } , // erase char, does not move the cursor
122 backDelete: { on: '\x1b[1D\x1b[1P' } , // Backspace-like, left(1) followed by delete(1) /!\ WARNING /!\ this doesn't work with full-width char !!! /!\
123 scrollUp: { on: '\x1b[%US' } , // scroll up n lines, new lines are added at the bottom
124 scrollDown: { on: '\x1b[%UT' } , // scroll down n lines, new lines are added at the top
125 scrollingRegion: { on: '\x1b[%U;%Ur' } , // top line, bottom line, scrolling affect only this region,
126 resetScrollingRegion: { on: '\x1b[r' } ,
127
128 // Looks like it does not works
129 //scrollLeft: { on: '\x1b[%U @' } , // scroll left n columns, new columns are added at the right
130 //scrollRight: { on: '\x1b[%U A' } , // scroll right n columns, new columns are added at the left
131 setClipboardLL: { on: '\x1b]52;%s;%s\x07' } , // Not supported outside of xterm
132 requestClipboard: { on: '\x1b]52;%s;?\x07' } , // Not supported outside of xterm
133
134 // This set the alternate screen buffer, do not work on many term, due to that 'titeInhibit' shit...
135 alternateScreenBuffer: { on: '\x1b[?1049h' , off: '\x1b[?1049l' } ,
136
137 /* Misc sequences */
138
139 beep: { on: '\x07' } , // Deprecated -- use bell instead
140 bell: { on: '\x07' } , // Emit an audible bell
141
142 /* Style sequences */
143
144 styleReset: { on: '\x1b[0m' } ,
145
146 bold: { on: '\x1b[1m' , off: '\x1b[22m' } , // here we use the dim.off code (22) that have a better support than (21), for God-only known reason...
147 dim: { on: '\x1b[2m' , off: '\x1b[22m' } , // dim: darker, 'off' remove removes also bold/bright
148 italic: { on: '\x1b[3m' , off: '\x1b[23m' } ,
149 underline: { on: '\x1b[4m' , off: '\x1b[24m' } ,
150 blink: { on: '\x1b[5m' , off: '\x1b[25m' } ,
151 inverse: { on: '\x1b[7m' , off: '\x1b[27m' } ,
152 hidden: { on: '\x1b[8m' , off: '\x1b[28m' } , // invisible, but can be copy/paste'd
153 strike: { on: '\x1b[9m' , off: '\x1b[29m' } ,
154
155 // Foreground color
156 defaultColor: { on: defaultColor } ,
157 black: { on: '\x1b[30m' , off: defaultColor } ,
158 red: { on: '\x1b[31m' , off: defaultColor } ,
159 green: { on: '\x1b[32m' , off: defaultColor } ,
160 yellow: { on: '\x1b[33m' , off: defaultColor } ,
161 blue: { on: '\x1b[34m' , off: defaultColor } ,
162 magenta: { on: '\x1b[35m' , off: defaultColor } ,
163 cyan: { on: '\x1b[36m' , off: defaultColor } ,
164 white: { on: '\x1b[37m' , off: defaultColor } ,
165 darkColor: { on: '\x1b[3%um' , off: defaultColor } , // should be called with a 0..7 integer
166 brightBlack: { on: '\x1b[90m' , off: defaultColor } ,
167 brightRed: { on: '\x1b[91m' , off: defaultColor } ,
168 brightGreen: { on: '\x1b[92m' , off: defaultColor } ,
169 brightYellow: { on: '\x1b[93m' , off: defaultColor } ,
170 brightBlue: { on: '\x1b[94m' , off: defaultColor } ,
171 brightMagenta: { on: '\x1b[95m' , off: defaultColor } ,
172 brightCyan: { on: '\x1b[96m' , off: defaultColor } ,
173 brightWhite: { on: '\x1b[97m' , off: defaultColor } ,
174 brightColor: { on: '\x1b[9%um' , off: defaultColor } , // should be called with a 0..7 integer
175
176 // Background color
177 bgDefaultColor: { on: bgDefaultColor } ,
178 bgBlack: { on: '\x1b[40m' , off: bgDefaultColor } ,
179 bgRed: { on: '\x1b[41m' , off: bgDefaultColor } ,
180 bgGreen: { on: '\x1b[42m' , off: bgDefaultColor } ,
181 bgYellow: { on: '\x1b[43m' , off: bgDefaultColor } ,
182 bgBlue: { on: '\x1b[44m' , off: bgDefaultColor } ,
183 bgMagenta: { on: '\x1b[45m' , off: bgDefaultColor } ,
184 bgCyan: { on: '\x1b[46m' , off: bgDefaultColor } ,
185 bgWhite: { on: '\x1b[47m' , off: bgDefaultColor } ,
186 bgDarkColor: { on: '\x1b[4%um' , off: bgDefaultColor } , // should be called with a 0..7 integer
187 bgBrightBlack: { on: '\x1b[100m' , off: bgDefaultColor } ,
188 bgBrightRed: { on: '\x1b[101m' , off: bgDefaultColor } ,
189 bgBrightGreen: { on: '\x1b[102m' , off: bgDefaultColor } ,
190 bgBrightYellow: { on: '\x1b[103m' , off: bgDefaultColor } ,
191 bgBrightBlue: { on: '\x1b[104m' , off: bgDefaultColor } ,
192 bgBrightMagenta: { on: '\x1b[105m' , off: bgDefaultColor } ,
193 bgBrightCyan: { on: '\x1b[106m' , off: bgDefaultColor } ,
194 bgBrightWhite: { on: '\x1b[107m' , off: bgDefaultColor } ,
195 bgBrightColor: { on: '\x1b[10%um' , off: bgDefaultColor } , // should be called with a 0..7 integer
196
197 /* Input / Output sequences */
198
199 // Request terminal ID
200 // requestTerminalId: { on: '\x1b[>c' } ,
201
202 // Terminal will send the cursor coordinate only one time
203 requestCursorLocation: { on: '\x1b[6n' } , // '\x1b[?6n' is not widely supported, '\x1b[6n' is better
204
205 // Terminal will send the screen-size only one time
206 requestScreenSize: { on: '\x1b[18t' } ,
207
208 // Terminal will send the rgb color for a register
209 requestColor: { on: '\x1b]4;%u;?\x07' } ,
210
211 // Terminal will send event on button pressed with mouse position
212 mouseButton: { on: '\x1b[?1000h' , off: '\x1b[?1000l' } ,
213
214 // Terminal will send position of the column hilighted
215 mouseHilight: { on: '\x1b[?1001h' , off: '\x1b[?1001l' } ,
216
217 // Terminal will send event on button pressed and mouse motion as long as a button is down, with mouse position
218 mouseDrag: { on: '\x1b[?1002h' , off: '\x1b[?1002l' } ,
219
220 // Terminal will send event on button pressed and motion
221 mouseMotion: { on: '\x1b[?1003h' , off: '\x1b[?1003l' } ,
222
223 // Another mouse protocol that extend coordinate mapping (without it, it supports only 223 rows and columns)
224 mouseSGR: { on: '\x1b[?1006h' , off: '\x1b[?1006l' } ,
225
226 // Terminal will send event when it gains and loses focus
227 focusEvent: { on: '\x1b[?1004h' , off: '\x1b[?1004l' } ,
228
229 // Should allow keypad to send different code than 0..9 keys but it does not work on some setup
230 applicationKeypad: { on: '\x1b[?1h\x1b=' , off: '\x1b[?1l\x1b>' } ,
231
232 // When enabled, the terminal will report if modifiers (SHIFT, CTRL, ALT) are on
233 /* Not widely supported
234 keyboardModifier: { on: '\x1b[>0;1m' , off: '\x1b[>0;0m' } ,
235 cursorKeyModifier: { on: '\x1b[>1;1m' , off: '\x1b[>1;0m' } ,
236 functionKeyModifier: { on: '\x1b[>2;1m' , off: '\x1b[>2;0m' } ,
237 otherKeyModifier: { on: '\x1b[>3;1m' , off: '\x1b[>3;0m' } ,
238 */
239
240 // Do not work... use node.js stdout.setRawMode(true) instead
241 //noecho: { on: '\x1b[12h' } ,
242
243 /* OSC - OS Command sequences: may be unavailable on some context */
244
245 // Set the title of an xterm-compatible window
246 windowTitle: { on: '\x1b]2;%s\x1b\\' } ,
247 iconName: { on: '\x1b]1;%s\x1b\\' } ,
248 cwd: { on: '\x1b]7;%s\x1b\\' } ,
249 // Sometime (e.g.: gnome) the notification is displayed only if the user had switched to another window/workspace
250 notify: { on: '\x1b]777;notify;%s;%s\x1b\\' } ,
251
252 // Those sequences accept either #%x%x%x or rgb:%d/%d/%d
253 // Sometime rgb:%d/%d/%d should be encoded into the 0..65535 range, so #%x%x%x is more reliable
254 setCursorColorRgb: { on: '\x1b]12;#%x%x%x\x07' } , // it want rgb as parameter, like rgb:127/0/32
255 resetCursorColorRgb: { on: '\x1b]112\x07' } ,
256 setDefaultColorRgb: { on: '\x1b]10;#%x%x%x\x07' } , // not widely supported
257 resetDefaultColorRgb: { on: '\x1b]110\x07' } ,
258 setDefaultBgColorRgb: { on: '\x1b]11;#%x%x%x\x07' } , // not widely supported
259 resetDefaultBgColorRgb: { on: '\x1b]111\x07' } ,
260 setHighlightBgColorRgb: { on: '\x1b]17;#%x%x%x\x07' } , // not widely supported
261 resetHighlightBgColorRgb: { on: '\x1b]117\x07' } ,
262 setColorLL: { on: '\x1b]4;%u;#%x%x%x\x07' } , // LL= Low Level
263 resetColorLL: { on: '\x1b]104;%u\x07' } ,
264
265 setFont: { on: '\x1b]50;%s\x07' } , // ->|TODOC|<- rarely supported
266 color24bits: { on: '%D%D%D' , na: true } , // not capable
267 bgColor24bits: { on: '%D%D%D' , na: true } , // not capable
268
269 /* Functions */
270
271 color256: {
272 on: '%[color256:%a]F' ,
273 off: defaultColor ,
274 fb: true ,
275 handler: function( register ) {
276
277 if ( typeof register !== 'number' ) { return '' ; }
278 if ( register < 0 || register > 255 ) { return '' ; }
279
280 // If the register is greater than 15, find the 0..15 register that is close to it
281 if ( register > 15 ) {
282 register = this.root.registerForRgb( this.root.rgbForRegister( register ) , 0 , 15 ) ;
283 }
284
285 //return string.format.call( this.root.escHandler , this.root.esc.color.on , register ) ;
286 return this.root.escHandler.color( register ) ;
287 }
288 } ,
289
290 bgColor256: {
291 on: '%[bgColor256:%a]F' ,
292 off: bgDefaultColor ,
293 fb: true ,
294 handler: function( register ) {
295
296 if ( typeof register !== 'number' ) { return '' ; }
297 if ( register < 0 || register > 255 ) { return '' ; }
298
299 // If the register is greater than 15, find the 0..15 register that is close to it
300 if ( register > 15 ) {
301 register = this.root.registerForRgb( this.root.rgbForRegister( register ) , 0 , 15 ) ;
302 }
303
304 //return string.format.call( this.root.escHandler , this.root.esc.bgColor.on , register ) ;
305 return this.root.escHandler.bgColor( register ) ;
306 }
307 } ,
308
309 // Cannot find a way to set the cursor to a register, so try to guess
310 setCursorColor: {
311 on: '%[setCursorColor:%a%a]F' ,
312 handler: function( bg , fg ) {
313
314 if ( typeof fg !== 'number' || typeof bg !== 'number' ) { return '' ; }
315
316 fg = Math.floor( fg ) ;
317 bg = Math.floor( bg ) ;
318
319 if ( fg < 0 || fg > 255 || bg < 0 || bg > 255 ) { return '' ; }
320
321 var rgb = this.root.rgbForRegister( bg ) ;
322
323 return string.format( this.root.esc.setCursorColorRgb.on , rgb.r , rgb.g , rgb.b ) ;
324 }
325 }
326} ;
327
328
329
330
331
332/* Inputs management */
333
334
335
336const handler = {} ;
337
338
339
340handler.mouseX11Protocol = function( basename , buffer ) {
341 var code = buffer[ 0 ] ;
342 var result = {
343 data: {
344 shift: !! ( code & 4 ) ,
345 alt: !! ( code & 8 ) ,
346 ctrl: !! ( code & 16 )
347 }
348 } ;
349
350 if ( code & 32 ) {
351 if ( code & 64 ) {
352 result.name = basename + ( code & 1 ? '_WHEEL_DOWN' : '_WHEEL_UP' ) ;
353 }
354 else {
355 // Button event
356 switch ( code & 3 ) {
357 case 0 : result.name = basename + '_LEFT_BUTTON_PRESSED' ; break ;
358 case 1 : result.name = basename + '_MIDDLE_BUTTON_PRESSED' ; break ;
359 case 2 : result.name = basename + '_RIGHT_BUTTON_PRESSED' ; break ;
360 case 3 : result.name = basename + '_BUTTON_RELEASED' ; break ;
361 }
362 }
363 }
364 else if ( code & 64 ) {
365 // Motion event
366 result.name = basename + '_MOTION' ;
367 }
368
369 result.eaten = 3 ;
370 result.data.code = code ;
371 result.data.x = buffer[ 1 ] - 32 ;
372 result.data.y = buffer[ 2 ] - 32 ;
373
374 return result ;
375} ;
376
377
378
379handler.mouseSGRProtocol = function( basename , buffer ) {
380 var code , pressed , matches , result ;
381
382 matches = buffer.toString().match( /^(-?[0-9]*);?([0-9]*);?([0-9]*)(M|m)/ ) ;
383
384 if ( ! matches || matches[ 3 ].length === 0 ) {
385 return {
386 name: 'ERROR' ,
387 eaten: matches ? matches[ 0 ].length : 0 ,
388 data: { matches: matches }
389 } ;
390 }
391
392 code = parseInt( matches[ 1 ] , 10 ) ;
393 pressed = matches[ 4 ] !== 'm' ;
394
395 result = {
396 data: {
397 shift: !! ( code & 4 ) ,
398 alt: !! ( code & 8 ) ,
399 ctrl: !! ( code & 16 )
400 // , pressed: pressed
401 }
402 } ;
403
404 result.data.x = parseInt( matches[ 2 ] , 10 ) ;
405 result.data.y = parseInt( matches[ 3 ] , 10 ) ;
406 result.eaten = matches[ 0 ].length ;
407
408 if ( code & 32 ) {
409 // Motions / drag event
410
411 switch ( code & 3 ) {
412 case 0 :
413 // Left drag, or maybe something else (left+right combo)
414 result.name = basename + '_DRAG' ;
415 result.data.left = true ;
416 result.data.right = false ;
417 result.data.xFrom = this.state.button.left.x ;
418 result.data.yFrom = this.state.button.left.y ;
419 break ;
420
421 // Doesn't seem to exist, middle drag does not discriminate from motion
422 //case 1 :
423
424 case 2 :
425 // Right drag
426 result.name = basename + '_DRAG' ;
427 result.data.left = false ;
428 result.data.right = true ;
429 result.data.xFrom = this.state.button.right.x ;
430 result.data.yFrom = this.state.button.right.y ;
431 break ;
432
433 case 3 :
434 default :
435 result.name = basename + '_MOTION' ;
436 break ;
437 }
438 }
439 else if ( code & 64 ) {
440 result.name = basename + ( code & 1 ? '_WHEEL_DOWN' : '_WHEEL_UP' ) ;
441 }
442 else {
443 // Button event
444 switch ( code & 3 ) {
445 case 0 :
446 result.name = basename + '_LEFT_BUTTON' ;
447 //if ( this.state.button.left === pressed ) { result.disable = true ; }
448 this.state.button.left = pressed ? result.data : null ;
449 break ;
450
451 case 1 :
452 result.name = basename + '_MIDDLE_BUTTON' ;
453 //if ( this.state.button.middle === pressed ) { result.disable = true ; }
454 this.state.button.middle = pressed ? result.data : null ;
455 break ;
456
457 case 2 :
458 result.name = basename + '_RIGHT_BUTTON' ;
459 //if ( this.state.button.right === pressed ) { result.disable = true ; }
460 this.state.button.right = pressed ? result.data : null ;
461 break ;
462
463 case 3 :
464 result.name = basename + '_OTHER_BUTTON' ;
465 //if ( this.state.button.other === pressed ) { result.disable = true ; }
466 this.state.button.other = pressed ? result.data : null ;
467 break ;
468 }
469
470 result.name += pressed ? '_PRESSED' : '_RELEASED' ;
471 }
472
473 result.data.code = code ;
474
475 return result ;
476} ;
477
478
479
480handler.cursorLocation = function( basename , paramString ) {
481 var params = paramString.split( ';' ) ;
482
483 return {
484 name: 'CURSOR_LOCATION' ,
485 data: {
486 x: parseInt( params[ 1 ] , 10 ) ,
487 y: parseInt( params[ 0 ] , 10 )
488 }
489 } ;
490} ;
491
492
493
494handler.colorRegister = function( basename , paramString ) {
495 var matches = paramString.match( /^([0-9]*);rgb:([0-9a-f]{2})[0-9a-f]*\/([0-9a-f]{2})[0-9a-f]*\/([0-9a-f]{2})[0-9a-f]*/ ) ;
496
497 return {
498 name: 'COLOR_REGISTER' ,
499 data: {
500 register: parseInt( matches[ 1 ] , 10 ) ,
501 r: parseInt( matches[ 2 ] , 16 ) ,
502 g: parseInt( matches[ 3 ] , 16 ) ,
503 b: parseInt( matches[ 4 ] , 16 )
504 }
505 } ;
506} ;
507
508
509
510handler.screenSize = function( basename , paramString ) {
511 var params = paramString.split( ';' ) ,
512 width = parseInt( params[ 1 ] , 10 ) ,
513 height = parseInt( params[ 0 ] , 10 ) ,
514 resized = this.root.width !== width || this.root.height !== height ;
515
516 this.root.width = width ;
517 this.root.height = height ;
518
519 return {
520 name: 'SCREEN_SIZE' ,
521 data: {
522 resized: resized ,
523 width: width ,
524 height: height
525 }
526 } ;
527} ;
528
529
530
531handler.clipboard = function( basename , paramString ) {
532 console.log( "handler.clipboard: " , basename , paramString ) ;
533 var matches = paramString.match( /^([cps][0-7]*);(.*)/ ) ;
534
535 return {
536 name: 'CLIPBOARD' ,
537 data: {
538 source: matches[ 1 ] ,
539 content: Buffer.from( matches[ 2 ] , 'base64' ).toString()
540 }
541 } ;
542} ;
543
544
545
546
547
548/* Key Mapping */
549
550
551
552const keymap = {
553 ESCAPE: '\x1b' ,
554 TAB: '\x09' ,
555 ENTER: '\x0d' ,
556
557 SHIFT_TAB: '\x1b[Z' ,
558 ALT_TAB: '\x1b\x09' , // Also CTRL_ALT_I, most of time it is grabbed by the window manager before reaching the terminal
559 ALT_ENTER: '\x1b\x0d' ,
560
561 UP: [ '\x1bOA' , '\x1b[A' ] ,
562 DOWN: [ '\x1bOB' , '\x1b[B' ] ,
563 RIGHT: [ '\x1bOC' , '\x1b[C' ] ,
564 LEFT: [ '\x1bOD' , '\x1b[D' ] ,
565
566 SHIFT_UP: '\x1b[1;2A' ,
567 SHIFT_DOWN: '\x1b[1;2B' ,
568 SHIFT_RIGHT: '\x1b[1;2C' ,
569 SHIFT_LEFT: '\x1b[1;2D' ,
570 ALT_UP: '\x1b[1;3A' ,
571 ALT_DOWN: '\x1b[1;3B' ,
572 ALT_RIGHT: '\x1b[1;3C' ,
573 ALT_LEFT: '\x1b[1;3D' ,
574 CTRL_UP: '\x1b[1;5A' ,
575 CTRL_DOWN: '\x1b[1;5B' ,
576 CTRL_RIGHT: '\x1b[1;5C' ,
577 CTRL_LEFT: '\x1b[1;5D' ,
578
579 //BACKSPACE: '\x7f' ,
580 BACKSPACE: [ '\x7f' , '\x08' ] , // Most terminal use \x08 for Ctrl-backspace, except Terminology...
581 INSERT: '\x1b[2~' ,
582 DELETE: '\x1b[3~' ,
583 HOME: [ '\x1b[1~' , '\x1b[H' , '\x1bOH' ] ,
584 END: [ '\x1b[4~' , '\x1b[F' , '\x1bOF' ] ,
585 PAGE_UP: '\x1b[5~' ,
586 PAGE_DOWN: '\x1b[6~' ,
587
588 //CTRL_BACKSPACE: '\x08' ,
589 CTRL_INSERT: '\x1b[2;5~' ,
590 CTRL_DELETE: '\x1b[3;5~' ,
591 CTRL_HOME: [ '\x1b[1;5~' , '\x1b[1;5H' ] ,
592 CTRL_END: [ '\x1b[4;5~' , '\x1b[1;5F' ] ,
593 CTRL_PAGE_UP: '\x1b[5;5~' ,
594 CTRL_PAGE_DOWN: '\x1b[6;5~' ,
595
596 SHIFT_INSERT: '\x1b[2;2~' ,
597 SHIFT_DELETE: '\x1b[3;2~' ,
598 SHIFT_HOME: [ '\x1b[1;2~' , '\x1b[1;2H' ] ,
599 SHIFT_END: [ '\x1b[4;2~' , '\x1b[1;2F' ] ,
600 SHIFT_PAGE_UP: '\x1b[5;2~' ,
601 SHIFT_PAGE_DOWN: '\x1b[6;2~' ,
602
603 ALT_BACKSPACE: [ '\x1b\x7f' , '\x1b\x08' ] ,
604 ALT_INSERT: '\x1b[2;3~' ,
605 ALT_DELETE: '\x1b[3;3~' ,
606 ALT_HOME: [ '\x1b[1;3~' , '\x1b[1;3H' ] ,
607 ALT_END: [ '\x1b[4;3~' , '\x1b[1;3F' ] ,
608 ALT_PAGE_UP: '\x1b[5;3~' ,
609 ALT_PAGE_DOWN: '\x1b[6;3~' ,
610
611 // Application Keypad
612 KP_NUMLOCK: [] , // '\x1bOP' ,
613 KP_DIVIDE: '\x1bOo' ,
614 KP_MULTIPLY: '\x1bOj' ,
615 KP_MINUS: '\x1bOm' ,
616 KP_0: [] , // '\x1b[2~' ,
617 KP_1: [] , // '\x1bOF' ,
618 KP_2: [] , // '\x1b[B' ,
619 KP_3: [] , // '\x1b[6~' ,
620 KP_4: [] , // '\x1b[D' ,
621 KP_5: [ '\x1bOE' , '\x1b[E' ] ,
622 KP_6: [] , // '\x1b[C' ,
623 KP_7: [] , // '\x1bOH' ,
624 KP_8: [] , // '\x1b[A' ,
625 KP_9: [] , // '\x1b[5~' ,
626 KP_PLUS: '\x1bOk' ,
627 KP_DELETE: [] , // '\x1b[3~' ,
628 KP_ENTER: '\x1bOM' ,
629
630 F1: '\x1bOP' ,
631 F2: '\x1bOQ' ,
632 F3: '\x1bOR' ,
633 F4: '\x1bOS' ,
634 F5: '\x1b[15~' ,
635 F6: '\x1b[17~' ,
636 F7: '\x1b[18~' ,
637 F8: '\x1b[19~' ,
638 F9: '\x1b[20~' ,
639 F10: '\x1b[21~' ,
640 F11: [ '\x1b[23~' , '\x1b[22~' ] ,
641 F12: '\x1b[24~' ,
642
643 SHIFT_F1: [ '\x1bO1;2P' , '\x1bO2P' , '\x1b[1;2P' ] ,
644 SHIFT_F2: [ '\x1bO1;2Q' , '\x1bO2Q' , '\x1b[1;2Q' ] ,
645 SHIFT_F3: [ '\x1bO1;2R' , '\x1bO2R' , '\x1b[1;2R' ] ,
646 SHIFT_F4: [ '\x1bO1;2S' , '\x1bO2S' , '\x1b[1;2S' ] ,
647 SHIFT_F5: '\x1b[15;2~' ,
648 SHIFT_F6: '\x1b[17;2~' ,
649 SHIFT_F7: '\x1b[18;2~' ,
650 SHIFT_F8: '\x1b[19;2~' ,
651 SHIFT_F9: '\x1b[20;2~' ,
652 SHIFT_F10: '\x1b[21;2~' ,
653 SHIFT_F11: [ '\x1b[23;2~' , '\x1b[22;2~' ] ,
654 SHIFT_F12: '\x1b[24;2~' ,
655
656 CTRL_F1: [ '\x1bO1;5P' , '\x1bO5P' , '\x1b[1;5P' ] ,
657 CTRL_F2: [ '\x1bO1;5Q' , '\x1bO5Q' , '\x1b[1;5Q' ] ,
658 CTRL_F3: [ '\x1bO1;5R' , '\x1bO5R' /*, '\x1b[1;5R'*/ ] , // also used for cursor location response... :/
659 CTRL_F4: [ '\x1bO1;5S' , '\x1bO5S' , '\x1b[1;5S' ] ,
660 CTRL_F5: '\x1b[15;5~' ,
661 CTRL_F6: '\x1b[17;5~' ,
662 CTRL_F7: '\x1b[18;5~' ,
663 CTRL_F8: '\x1b[19;5~' ,
664 CTRL_F9: '\x1b[20;5~' ,
665 CTRL_F10: '\x1b[21;5~' ,
666 CTRL_F11: [ '\x1b[23;5~' , '\x1b[22;5~' ] ,
667 CTRL_F12: '\x1b[24;5~' ,
668
669 CTRL_SHIFT_F1: [ '\x1bO1;6P' , '\x1bO6P' , '\x1b[1;6P' ] ,
670 CTRL_SHIFT_F2: [ '\x1bO1;6Q' , '\x1bO6Q' , '\x1b[1;6Q' ] ,
671 CTRL_SHIFT_F3: [ '\x1bO1;6R' , '\x1bO6R' /*, '\x1b[1;6R'*/ ] , // also used for cursor location response... :/
672 CTRL_SHIFT_F4: [ '\x1bO1;6S' , '\x1bO6S' , '\x1b[1;6S' ] ,
673 CTRL_SHIFT_F5: '\x1b[15;6~' ,
674 CTRL_SHIFT_F6: '\x1b[17;6~' ,
675 CTRL_SHIFT_F7: '\x1b[18;6~' ,
676 CTRL_SHIFT_F8: '\x1b[19;6~' ,
677 CTRL_SHIFT_F9: '\x1b[20;6~' ,
678 CTRL_SHIFT_F10: '\x1b[21;6~' ,
679 CTRL_SHIFT_F11: [ '\x1b[23;6~' , '\x1b[22;6~' ] ,
680 CTRL_SHIFT_F12: '\x1b[24;6~' ,
681
682 NUL: '\x00' ,
683
684 //CTRL_SPACE: '\x00' , // also NUL
685 ALT_SPACE: '\x1b ' ,
686 CTRL_ALT_SPACE: '\x1b\x00' ,
687
688 CURSOR_LOCATION: {
689 starter: '\x1b[' , ender: 'R' , event: 'terminal' , handler: 'cursorLocation'
690 } ,
691 SCREEN_SIZE: {
692 starter: '\x1b[8;' , ender: 't' , event: 'terminal' , handler: 'screenSize'
693 } ,
694 COLOR_REGISTER: {
695 starter: '\x1b]4;' , ender: '\x07' , altEnder: '\x1b\\' , event: 'terminal' , handler: 'colorRegister'
696 } ,
697
698 FOCUS_IN: { code: '\x1b[I' , event: 'terminal' , data: {} } ,
699 FOCUS_OUT: { code: '\x1b[O' , event: 'terminal' , data: {} } ,
700
701 CLIPBOARD: {
702 starter: '\x1b]52;' , ender: '\x07' , altEnder: '\x1b\\' , accumulate: true , event: 'terminal' , handler: 'clipboard'
703 } ,
704
705 MOUSE: [
706 { code: '\x1b[<' , event: 'mouse' , handler: 'mouseSGRProtocol' } ,
707 { code: '\x1b[M' , event: 'mouse' , handler: 'mouseX11Protocol' }
708 ]
709} ;
710
711
712
713// Complete with Modifier + [A-Z]
714for ( let i = 1 ; i <= 26 ; i ++ ) {
715 keymap[ 'CTRL_' + String.fromCharCode( 64 + i ) ] = String.fromCharCode( i ) ;
716 keymap[ 'ALT_' + String.fromCharCode( 64 + i ) ] = '\x1b' + String.fromCharCode( 96 + i ) ;
717 keymap[ 'CTRL_ALT_' + String.fromCharCode( 64 + i ) ] = '\x1b' + String.fromCharCode( i ) ;
718 keymap[ 'ALT_SHIFT_' + String.fromCharCode( 64 + i ) ] = '\x1b' + String.fromCharCode( 64 + i ) ;
719}
720
721
722
723module.exports = {
724 esc: esc ,
725 keymap: keymap ,
726 handler: handler ,
727 support: {
728 deltaEscapeSequence: true ,
729 "256colors": false ,
730 "24bitsColors": false , // DEPRECATED
731 "trueColor": false
732 } ,
733 colorRegister: require( '../colorScheme/xterm.json' )
734} ;
735