UNPKG

12.5 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 tree = require( 'tree-kit' ) ;
32const string = require( 'string-kit' ) ;
33const xterm = require( './xterm.js' ) ;
34const gpm = require( '../gpm.js' ) ;
35
36
37
38// shortcuts
39const bold = '\x1b[1m' ;
40const noBold = '\x1b[22m' ;
41const blink = '\x1b[5m' ;
42const noBlink = '\x1b[25m' ;
43const defaultColor = noBold + '\x1b[39m' ; // back to the default color, most of time it is the same than .white
44const bgDefaultColor = noBold + '\x1b[49m' ; // back to the default color, most of time it is the same than .bgBlack
45
46
47
48const fgCursorTable = [
49 0 , 3 , 5 , 1 , 6 , 2 , 4 , 7 ,
50 8 , 11 , 13 , 9 , 14 , 10 , 12 , 15
51] ;
52
53const bgCursorTable = [
54 0 , 4 , 2 , 6 , 1 , 5 , 3 , 7 ,
55 8 , 12 , 10 , 14 , 9 , 13 , 11 , 15
56] ;
57
58
59
60const esc = tree.extend( null , Object.create( xterm.esc ) , {
61 // Clear screen
62 clear: { on: '\x1b[H\x1b[J' } ,
63
64 // Linux console doesn't have bright color code, they are produced using 'bold' (which is not bold, by the way...)
65 defaultColor: { on: defaultColor } ,
66 black: { on: noBold + '\x1b[30m' , off: defaultColor } ,
67 red: { on: noBold + '\x1b[31m' , off: defaultColor } ,
68 green: { on: noBold + '\x1b[32m' , off: defaultColor } ,
69 yellow: { on: noBold + '\x1b[33m' , off: defaultColor } ,
70 blue: { on: noBold + '\x1b[34m' , off: defaultColor } ,
71 magenta: { on: noBold + '\x1b[35m' , off: defaultColor } ,
72 cyan: { on: noBold + '\x1b[36m' , off: defaultColor } ,
73 white: { on: noBold + '\x1b[37m' , off: defaultColor } ,
74 darkColor: { on: noBold + '\x1b[3%um' , off: defaultColor } , // should be called with a 0..7 integer
75 brightBlack: { on: bold + '\x1b[30m' , off: defaultColor } ,
76 brightRed: { on: bold + '\x1b[31m' , off: defaultColor } ,
77 brightGreen: { on: bold + '\x1b[32m' , off: defaultColor } ,
78 brightYellow: { on: bold + '\x1b[33m' , off: defaultColor } ,
79 brightBlue: { on: bold + '\x1b[34m' , off: defaultColor } ,
80 brightMagenta: { on: bold + '\x1b[35m' , off: defaultColor } ,
81 brightCyan: { on: bold + '\x1b[36m' , off: defaultColor } ,
82 brightWhite: { on: bold + '\x1b[37m' , off: defaultColor } ,
83 brightColor: { on: bold + '\x1b[3%um' , off: defaultColor } , // should be called with a 0..7 integer
84
85 // Linux console doesn't have bright bg color code, they are produced using 'blink' (which does not blink, by the way...)
86 bgDefaultColor: { on: bgDefaultColor } ,
87 bgBlack: { on: noBlink + '\x1b[40m' , off: bgDefaultColor } ,
88 bgRed: { on: noBlink + '\x1b[41m' , off: bgDefaultColor } ,
89 bgGreen: { on: noBlink + '\x1b[42m' , off: bgDefaultColor } ,
90 bgYellow: { on: noBlink + '\x1b[43m' , off: bgDefaultColor } ,
91 bgBlue: { on: noBlink + '\x1b[44m' , off: bgDefaultColor } ,
92 bgMagenta: { on: noBlink + '\x1b[45m' , off: bgDefaultColor } ,
93 bgCyan: { on: noBlink + '\x1b[46m' , off: bgDefaultColor } ,
94 bgWhite: { on: noBlink + '\x1b[47m' , off: bgDefaultColor } ,
95 bgDarkColor: { on: noBlink + '\x1b[4%um' , off: bgDefaultColor } , // should be called with a 0..7 integer
96 bgBrightBlack: { on: blink + '\x1b[40m' , off: bgDefaultColor } ,
97 bgBrightRed: { on: blink + '\x1b[41m' , off: bgDefaultColor } ,
98 bgBrightGreen: { on: blink + '\x1b[42m' , off: bgDefaultColor } ,
99 bgBrightYellow: { on: blink + '\x1b[43m' , off: bgDefaultColor } ,
100 bgBrightBlue: { on: blink + '\x1b[44m' , off: bgDefaultColor } ,
101 bgBrightMagenta: { on: blink + '\x1b[45m' , off: bgDefaultColor } ,
102 bgBrightCyan: { on: blink + '\x1b[46m' , off: bgDefaultColor } ,
103 bgBrightWhite: { on: blink + '\x1b[47m' , off: bgDefaultColor } ,
104 bgBrightColor: { on: blink + '\x1b[4%um' , off: bgDefaultColor } , // should be called with a 0..7 integer
105
106 // Those either does not produce anything or switch to some arbitrary color, so we will use our own settings instead
107 dim: { on: bold + '\x1b[30m' , off: defaultColor } , // dim does not produce dim, so we use brightBlack instead
108 underline: { on: blink + '\x1b[40m' , off: bgDefaultColor } , // underline does not produce underline, so we use bgBrightBlack instead
109 italic: { on: '\x1b[1m' , off: '\x1b[22m' } , // italic does not produce italic, so we use bold instead (which is no bold but bright BTW)
110 hidden: { on: '\x1b[40m\x1b[30m' , off: '\x1b[49m\x1b[39m' } , // hidden does not produce hidden, so we use black + bgBlack instead
111 strike: { on: bold + '\x1b[30m' , off: defaultColor } , // strike does not produce strike, so we use brightBlack instead
112
113 // Cursor styles
114 hideCursor: { on: '\x1b[?1c' , off: '\x1b[?0c' } ,
115 blockCursor: { on: '\x1b[?16;0;16c' } ,
116 blinkingBlockCursor: { on: '\x1b[?6c' } ,
117 underlineCursor: { on: '\x1b[?2c' } , // it's blinking anyway
118 blinkingUnderlineCursor: { on: '\x1b[?2c' } ,
119 beamCursor: { on: '' , na: true } , // do not exists
120 blinkingBeamCursor: { on: '' , na: true } , // do not exists
121
122 /* OSC */
123
124 // Does not exist, silently drop it...
125 windowTitle: { on: '%D' , na: true } ,
126 iconName: { on: '%D' , na: true } ,
127 cwd: { on: '%D' , na: true } ,
128 notify: { on: '%D%D' , na: true } ,
129
130 setDefaultColorRgb: { on: '\x1b]P7%x%x%x' } ,
131 resetDefaultColorRgb: { on: '' , na: true } , // not possible? should be investigated...
132 setDefaultBgColorRgb: { on: '\x1b]P0%x%x%x' } ,
133 resetDefaultBgColorRgb: { on: '' , na: true } , // not possible? should be investigated...
134 setHighlightBgColorRgb: { on: '%D%D%D' , na: true } , // not possible? should be investigated...
135 resetHighlightBgColorRgb: { on: '' , na: true } , // not possible? should be investigated...
136 setColorLL: { on: '\x1b]P%h%x%x%x' } ,
137 resetColorLL: { on: '%D' , na: true } , // not possible? should be investigated...
138 setFont: { on: '%D' , na: true } , // not capable
139 requestColor: { on: '%D' , na: true } , // not capable
140
141 /* Functions */
142
143 color256: {
144 on: '%[color256:%a]F' ,
145 off: defaultColor ,
146 fb: true ,
147 handler: function( register ) {
148 if ( typeof register !== 'number' ) { return '' ; }
149 if ( register < 0 || register > 255 ) { return '' ; }
150
151 // If the register is greater than 15, find the 0..15 register that is close to it
152 if ( register > 15 ) {
153 register = this.root.registerForRgb( this.root.rgbForRegister( register ) , 0 , 15 ) ;
154 }
155
156 //return string.format.call( this.root.escHandler , this.root.esc.color.on , register ) ;
157 return this.root.escHandler.color( register ) ;
158 }
159 } ,
160
161 bgColor256: {
162 on: '%[bgColor256:%a]F' ,
163 off: bgDefaultColor ,
164 fb: true ,
165 handler: function( register ) {
166 if ( typeof register !== 'number' ) { return '' ; }
167 if ( register < 0 || register > 255 ) { return '' ; }
168
169 // If the register is greater than 15, find the 0..15 register that is close to it
170 if ( register > 15 ) {
171 register = this.root.registerForRgb( this.root.rgbForRegister( register ) , 0 , 15 ) ;
172 }
173
174 //return string.format.call( this.root.escHandler , this.root.esc.bgColor.on , register ) ;
175 return this.root.escHandler.bgColor( register ) ;
176 }
177 } ,
178
179 setCursorColor: {
180 on: '%[setCursorColor:%a%a]F' ,
181 handler: function( bg , fg ) {
182 if ( typeof fg !== 'number' || typeof bg !== 'number' ) { return '' ; }
183
184 fg = Math.floor( fg ) ;
185 bg = Math.floor( bg ) ;
186
187 if ( fg < 0 || fg > 255 || bg < 0 || bg > 255 ) { return '' ; }
188
189 // If the register is greater than 15, find the 0..15 register that is close to it
190 if ( fg > 15 ) { fg = this.root.registerForRgb( this.root.rgbForRegister( fg ) , 0 , 15 ) ; }
191 if ( bg > 15 ) { bg = this.root.registerForRgb( this.root.rgbForRegister( bg ) , 0 , 15 ) ; }
192
193 //console.log( 'fg bg: ' , fg , bg ) ;
194
195 fg = fgCursorTable[ fg ] ;
196 bg = bgCursorTable[ bg ] ;
197
198 return string.format( '\x1b[?16;%u;%uc' , fg , bg * 16 ) ;
199 }
200 } ,
201
202 // It doesn't support RGB, but we can choose an ANSI color close to it
203 setCursorColorRgb: {
204 on: '%[setCursorColorRgb:%a%a%a]F' ,
205 handler: function( r , g , b ) {
206 if ( typeof r !== 'number' || typeof g !== 'number' || typeof b !== 'number' ) { return '' ; }
207
208 r = Math.floor( r ) ;
209 g = Math.floor( g ) ;
210 b = Math.floor( b ) ;
211
212 if ( r < 0 || r > 255 || g < 0 || g > 255 || b < 0 || b > 255 ) { return '' ; }
213
214 var register = this.root.registerForRgb( r , g , b , 0 , 15 ) ;
215
216 //console.log( 'Register:' , register ) ;
217
218 return this.root.str.setCursorColor( register , 0 ) ;
219 }
220 } ,
221
222 /*
223 This part is a bit of a nasty hack: originally, escape sequence should produce... well... an escape sequence...
224 Here an empty string is returned, but some underlying actions are performed.
225 This is because the "Linux Console" terminal does not support the mouse, so nothing should be sent to it,
226 however we will try to connect to the GPM daemon if it exists.
227 It is not very clean, ideally this should be an advanced (not chainable) feature, but doing so would break
228 compatibility with other terminal driver.
229 */
230
231 // Mouse 'button' mode
232 mouseButton: {
233 on: '%[mouseButton]F' ,
234 off: '%[mouseButton_off]F' ,
235 handler: function() { gpmMouse.call( this , 'button' ) ; return '' ; } ,
236 offHandler: function() { gpmMouse.call( this , false ) ; return '' ; }
237 } ,
238
239 // Mouse 'drag' mode
240 mouseDrag: {
241 on: '%[mouseDrag]F' ,
242 off: '%[mouseDrag_off]F' ,
243 handler: function() { gpmMouse.call( this , 'drag' ) ; return '' ; } ,
244 offHandler: function() { gpmMouse.call( this , false ) ; return '' ; }
245 } ,
246
247 // Mouse 'motion' mode
248 mouseMotion: {
249 on: '%[mouseMotion]F' ,
250 off: '%[mouseMotion_off]F' ,
251 handler: function() { gpmMouse.call( this , 'motion' ) ; return '' ; } ,
252 offHandler: function() { gpmMouse.call( this , false ) ; return '' ; }
253 } ,
254
255 mouseHilight: { on: '' , off: '' } ,
256 mouseSGR: { on: '' , off: '' } ,
257 focusEvent: { on: '' , off: '' }
258} ) ;
259
260
261
262// This is the code that handle GPM
263function gpmMouse( mode ) {
264 if ( this.root.gpmHandler ) {
265 this.root.gpmHandler.close() ;
266 this.root.gpmHandler = undefined ;
267 }
268
269 if ( ! mode ) {
270 //console.log( '>>>>> off <<<<<' ) ;
271 return ;
272 }
273
274 this.root.gpmHandler = gpm.createHandler( { stdin: this.root.stdin , raw: false , mode: mode } ) ;
275
276 //console.log( '>>>>>' , mode , '<<<<<' ) ;
277
278 // Simply re-emit event
279 this.root.gpmHandler.on( 'mouse' , ( name , data ) => {
280 this.root.emit( 'mouse' , name , data ) ;
281 } ) ;
282 this.root.gpmHandler.on( 'error' , ( /* error */ ) => {
283 //console.log( 'mouseDrag error:' , error ) ;
284 } ) ;
285}
286
287
288
289
290/* Key Mapping */
291
292
293
294const keymap = tree.extend( null , Object.create( xterm.keymap ) , {
295 F1: '\x1b[[A' ,
296 F2: '\x1b[[B' ,
297 F3: '\x1b[[C' ,
298 F4: '\x1b[[D' ,
299 F5: '\x1b[[E' ,
300
301 SHIFT_F1: '\x1b[25~' ,
302 SHIFT_F2: '\x1b[26~' ,
303 SHIFT_F3: '\x1b[28~' ,
304 SHIFT_F4: '\x1b[29~' ,
305 SHIFT_F5: '\x1b[31~' ,
306 SHIFT_F6: '\x1b[32~' ,
307 SHIFT_F7: '\x1b[33~' ,
308 SHIFT_F8: '\x1b[34~' ,
309 // SHIFT F9-F12 is not supported by the Linux console
310
311 // Application Keypad
312 KP_NUMLOCK: '\x1bOP' ,
313 KP_DIVIDE: '\x1bOQ' ,
314 KP_MULTIPLY: '\x1bOR' ,
315 KP_MINUS: '\x1bOS' ,
316 KP_0: '\x1bOp' ,
317 KP_1: '\x1bOq' ,
318 KP_2: '\x1bOr' ,
319 KP_3: '\x1bOs' ,
320 KP_4: '\x1bOt' ,
321 KP_5: '\x1bOu' ,
322 KP_6: '\x1bOv' ,
323 KP_7: '\x1bOw' ,
324 KP_8: '\x1bOx' ,
325 KP_9: '\x1bOy' ,
326 KP_PLUS: '\x1bOl' ,
327 KP_DELETE: '\x1bOn' ,
328 KP_ENTER: '\x1bOM'
329
330} ) ;
331
332
333
334module.exports = {
335 esc: esc ,
336 keymap: keymap ,
337 handler: Object.create( xterm.handler ) ,
338 support: {
339 deltaEscapeSequence: false ,
340 "256colors": false ,
341 "24bitsColors": false , // DEPRECATED
342 "trueColor": false
343 } ,
344
345 // This is the standard VGA palette, used by restorepalette
346 // http://linux.die.net/man/1/restorepalette
347 colorRegister: require( '../colorScheme/linux.json' )
348} ;
349