]> git.proxmox.com Git - mirror_xterm.js.git/blob - src/InputHandler.ts
51c6a2d9f92cba035bbddf8853483ad29c542ea1
[mirror_xterm.js.git] / src / InputHandler.ts
1 import { IInputHandler, ITerminal } from './Interfaces';
2 import { C0 } from './EscapeSequences';
3
4 export class InputHandler implements IInputHandler {
5 // TODO: We want to type _terminal when it's pulled into TS
6 constructor(private _terminal: any) { }
7
8 /**
9 * BEL
10 * Bell (Ctrl-G).
11 */
12 public bell(): void {
13 if (!this._terminal.visualBell) {
14 return;
15 }
16 this._terminal.element.style.borderColor = 'white';
17 setTimeout(() => this._terminal.element.style.borderColor = '', 10);
18 if (this._terminal.popOnBell) {
19 this._terminal.focus();
20 }
21 }
22
23 /**
24 * LF
25 * Line Feed or New Line (NL). (LF is Ctrl-J).
26 */
27 public lineFeed(): void {
28 if (this._terminal.convertEol) {
29 this._terminal.x = 0;
30 }
31 this._terminal.y++;
32 if (this._terminal.y > this._terminal.scrollBottom) {
33 this._terminal.y--;
34 this._terminal.scroll();
35 }
36 }
37
38 /**
39 * CR
40 * Carriage Return (Ctrl-M).
41 */
42 public carriageReturn(): void {
43 this._terminal.x = 0;
44 }
45
46 /**
47 * BS
48 * Backspace (Ctrl-H).
49 */
50 public backspace(): void {
51 if (this._terminal.x > 0) {
52 this._terminal.x--;
53 }
54 }
55
56 /**
57 * TAB
58 * Horizontal Tab (HT) (Ctrl-I).
59 */
60 public tab(): void {
61 this._terminal.x = this._terminal.nextStop();
62 }
63
64 /**
65 * SO
66 * Shift Out (Ctrl-N) -> Switch to Alternate Character Set. This invokes the
67 * G1 character set.
68 */
69 public shiftOut(): void {
70 this._terminal.setgLevel(1);
71 }
72
73 /**
74 * SI
75 * Shift In (Ctrl-O) -> Switch to Standard Character Set. This invokes the G0
76 * character set (the default).
77 */
78 public shiftIn(): void {
79 this._terminal.setgLevel(0);
80 }
81
82 /**
83 * CSI Ps @
84 * Insert Ps (Blank) Character(s) (default = 1) (ICH).
85 */
86 public insertChars(params: number[]): void {
87 let param, row, j, ch;
88
89 param = params[0];
90 if (param < 1) param = 1;
91
92 row = this._terminal.y + this._terminal.ybase;
93 j = this._terminal.x;
94 ch = [this._terminal.eraseAttr(), ' ', 1]; // xterm
95
96 while (param-- && j < this._terminal.cols) {
97 this._terminal.lines.get(row).splice(j++, 0, ch);
98 this._terminal.lines.get(row).pop();
99 }
100 }
101
102 /**
103 * CSI Ps A
104 * Cursor Up Ps Times (default = 1) (CUU).
105 */
106 public cursorUp(params: number[]): void {
107 let param = params[0];
108 if (param < 1) {
109 param = 1;
110 }
111 this._terminal.y -= param;
112 if (this._terminal.y < 0) {
113 this._terminal.y = 0;
114 }
115 }
116
117 /**
118 * CSI Ps B
119 * Cursor Down Ps Times (default = 1) (CUD).
120 */
121 public cursorDown(params: number[]) {
122 let param = params[0];
123 if (param < 1) {
124 param = 1;
125 }
126 this._terminal.y += param;
127 if (this._terminal.y >= this._terminal.rows) {
128 this._terminal.y = this._terminal.rows - 1;
129 }
130 }
131
132 /**
133 * CSI Ps C
134 * Cursor Forward Ps Times (default = 1) (CUF).
135 */
136 public cursorForward(params: number[]) {
137 let param = params[0];
138 if (param < 1) {
139 param = 1;
140 }
141 this._terminal.x += param;
142 if (this._terminal.x >= this._terminal.cols) {
143 this._terminal.x = this._terminal.cols - 1;
144 }
145 }
146
147 /**
148 * CSI Ps D
149 * Cursor Backward Ps Times (default = 1) (CUB).
150 */
151 public cursorBackward(params: number[]) {
152 let param = params[0];
153 if (param < 1) {
154 param = 1;
155 }
156 this._terminal.x -= param;
157 if (this._terminal.x < 0) {
158 this._terminal.x = 0;
159 }
160 }
161
162 /**
163 * CSI Ps E
164 * Cursor Next Line Ps Times (default = 1) (CNL).
165 * same as CSI Ps B ?
166 */
167 public cursorNextLine(params: number[]): void {
168 let param = params[0];
169 if (param < 1) {
170 param = 1;
171 }
172 this._terminal.y += param;
173 if (this._terminal.y >= this._terminal.rows) {
174 this._terminal.y = this._terminal.rows - 1;
175 }
176 this._terminal.x = 0;
177 };
178
179
180 /**
181 * CSI Ps F
182 * Cursor Preceding Line Ps Times (default = 1) (CNL).
183 * reuse CSI Ps A ?
184 */
185 public cursorPrecedingLine(params: number[]): void {
186 let param = params[0];
187 if (param < 1) {
188 param = 1;
189 }
190 this._terminal.y -= param;
191 if (this._terminal.y < 0) {
192 this._terminal.y = 0;
193 }
194 this._terminal.x = 0;
195 };
196
197
198 /**
199 * CSI Ps G
200 * Cursor Character Absolute [column] (default = [row,1]) (CHA).
201 */
202 public cursorCharAbsolute(params: number[]): void {
203 let param = params[0];
204 if (param < 1) {
205 param = 1;
206 }
207 this._terminal.x = param - 1;
208 }
209
210 /**
211 * CSI Ps ; Ps H
212 * Cursor Position [row;column] (default = [1,1]) (CUP).
213 */
214 public cursorPosition(params: number[]): void {
215 let row, col;
216
217 row = params[0] - 1;
218
219 if (params.length >= 2) {
220 col = params[1] - 1;
221 } else {
222 col = 0;
223 }
224
225 if (row < 0) {
226 row = 0;
227 } else if (row >= this._terminal.rows) {
228 row = this._terminal.rows - 1;
229 }
230
231 if (col < 0) {
232 col = 0;
233 } else if (col >= this._terminal.cols) {
234 col = this._terminal.cols - 1;
235 }
236
237 this._terminal.x = col;
238 this._terminal.y = row;
239 }
240
241 /**
242 * CSI Ps J Erase in Display (ED).
243 * Ps = 0 -> Erase Below (default).
244 * Ps = 1 -> Erase Above.
245 * Ps = 2 -> Erase All.
246 * Ps = 3 -> Erase Saved Lines (xterm).
247 * CSI ? Ps J
248 * Erase in Display (DECSED).
249 * Ps = 0 -> Selective Erase Below (default).
250 * Ps = 1 -> Selective Erase Above.
251 * Ps = 2 -> Selective Erase All.
252 */
253 public eraseInDisplay(params: number[]): void {
254 let j;
255 switch (params[0]) {
256 case 0:
257 this._terminal.eraseRight(this._terminal.x, this._terminal.y);
258 j = this._terminal.y + 1;
259 for (; j < this._terminal.rows; j++) {
260 this._terminal.eraseLine(j);
261 }
262 break;
263 case 1:
264 this._terminal.eraseLeft(this._terminal.x, this._terminal.y);
265 j = this._terminal.y;
266 while (j--) {
267 this._terminal.eraseLine(j);
268 }
269 break;
270 case 2:
271 j = this._terminal.rows;
272 while (j--) this._terminal.eraseLine(j);
273 break;
274 case 3:
275 ; // no saved lines
276 break;
277 }
278 }
279
280 /**
281 * CSI Ps K Erase in Line (EL).
282 * Ps = 0 -> Erase to Right (default).
283 * Ps = 1 -> Erase to Left.
284 * Ps = 2 -> Erase All.
285 * CSI ? Ps K
286 * Erase in Line (DECSEL).
287 * Ps = 0 -> Selective Erase to Right (default).
288 * Ps = 1 -> Selective Erase to Left.
289 * Ps = 2 -> Selective Erase All.
290 */
291 public eraseInLine(params: number[]): void {
292 switch (params[0]) {
293 case 0:
294 this._terminal.eraseRight(this._terminal.x, this._terminal.y);
295 break;
296 case 1:
297 this._terminal.eraseLeft(this._terminal.x, this._terminal.y);
298 break;
299 case 2:
300 this._terminal.eraseLine(this._terminal.y);
301 break;
302 }
303 }
304
305 /**
306 * CSI Pm m Character Attributes (SGR).
307 * Ps = 0 -> Normal (default).
308 * Ps = 1 -> Bold.
309 * Ps = 4 -> Underlined.
310 * Ps = 5 -> Blink (appears as Bold).
311 * Ps = 7 -> Inverse.
312 * Ps = 8 -> Invisible, i.e., hidden (VT300).
313 * Ps = 2 2 -> Normal (neither bold nor faint).
314 * Ps = 2 4 -> Not underlined.
315 * Ps = 2 5 -> Steady (not blinking).
316 * Ps = 2 7 -> Positive (not inverse).
317 * Ps = 2 8 -> Visible, i.e., not hidden (VT300).
318 * Ps = 3 0 -> Set foreground color to Black.
319 * Ps = 3 1 -> Set foreground color to Red.
320 * Ps = 3 2 -> Set foreground color to Green.
321 * Ps = 3 3 -> Set foreground color to Yellow.
322 * Ps = 3 4 -> Set foreground color to Blue.
323 * Ps = 3 5 -> Set foreground color to Magenta.
324 * Ps = 3 6 -> Set foreground color to Cyan.
325 * Ps = 3 7 -> Set foreground color to White.
326 * Ps = 3 9 -> Set foreground color to default (original).
327 * Ps = 4 0 -> Set background color to Black.
328 * Ps = 4 1 -> Set background color to Red.
329 * Ps = 4 2 -> Set background color to Green.
330 * Ps = 4 3 -> Set background color to Yellow.
331 * Ps = 4 4 -> Set background color to Blue.
332 * Ps = 4 5 -> Set background color to Magenta.
333 * Ps = 4 6 -> Set background color to Cyan.
334 * Ps = 4 7 -> Set background color to White.
335 * Ps = 4 9 -> Set background color to default (original).
336 *
337 * If 16-color support is compiled, the following apply. Assume
338 * that xterm's resources are set so that the ISO color codes are
339 * the first 8 of a set of 16. Then the aixterm colors are the
340 * bright versions of the ISO colors:
341 * Ps = 9 0 -> Set foreground color to Black.
342 * Ps = 9 1 -> Set foreground color to Red.
343 * Ps = 9 2 -> Set foreground color to Green.
344 * Ps = 9 3 -> Set foreground color to Yellow.
345 * Ps = 9 4 -> Set foreground color to Blue.
346 * Ps = 9 5 -> Set foreground color to Magenta.
347 * Ps = 9 6 -> Set foreground color to Cyan.
348 * Ps = 9 7 -> Set foreground color to White.
349 * Ps = 1 0 0 -> Set background color to Black.
350 * Ps = 1 0 1 -> Set background color to Red.
351 * Ps = 1 0 2 -> Set background color to Green.
352 * Ps = 1 0 3 -> Set background color to Yellow.
353 * Ps = 1 0 4 -> Set background color to Blue.
354 * Ps = 1 0 5 -> Set background color to Magenta.
355 * Ps = 1 0 6 -> Set background color to Cyan.
356 * Ps = 1 0 7 -> Set background color to White.
357 *
358 * If xterm is compiled with the 16-color support disabled, it
359 * supports the following, from rxvt:
360 * Ps = 1 0 0 -> Set foreground and background color to
361 * default.
362 *
363 * If 88- or 256-color support is compiled, the following apply.
364 * Ps = 3 8 ; 5 ; Ps -> Set foreground color to the second
365 * Ps.
366 * Ps = 4 8 ; 5 ; Ps -> Set background color to the second
367 * Ps.
368 */
369 public charAttributes(params: number[]): void {
370 // Optimize a single SGR0.
371 if (params.length === 1 && params[0] === 0) {
372 this._terminal.curAttr = this._terminal.defAttr;
373 return;
374 }
375
376 let l = params.length
377 , i = 0
378 , flags = this._terminal.curAttr >> 18
379 , fg = (this._terminal.curAttr >> 9) & 0x1ff
380 , bg = this._terminal.curAttr & 0x1ff
381 , p;
382
383 for (; i < l; i++) {
384 p = params[i];
385 if (p >= 30 && p <= 37) {
386 // fg color 8
387 fg = p - 30;
388 } else if (p >= 40 && p <= 47) {
389 // bg color 8
390 bg = p - 40;
391 } else if (p >= 90 && p <= 97) {
392 // fg color 16
393 p += 8;
394 fg = p - 90;
395 } else if (p >= 100 && p <= 107) {
396 // bg color 16
397 p += 8;
398 bg = p - 100;
399 } else if (p === 0) {
400 // default
401 flags = this._terminal.defAttr >> 18;
402 fg = (this._terminal.defAttr >> 9) & 0x1ff;
403 bg = this._terminal.defAttr & 0x1ff;
404 // flags = 0;
405 // fg = 0x1ff;
406 // bg = 0x1ff;
407 } else if (p === 1) {
408 // bold text
409 flags |= 1;
410 } else if (p === 4) {
411 // underlined text
412 flags |= 2;
413 } else if (p === 5) {
414 // blink
415 flags |= 4;
416 } else if (p === 7) {
417 // inverse and positive
418 // test with: echo -e '\e[31m\e[42mhello\e[7mworld\e[27mhi\e[m'
419 flags |= 8;
420 } else if (p === 8) {
421 // invisible
422 flags |= 16;
423 } else if (p === 22) {
424 // not bold
425 flags &= ~1;
426 } else if (p === 24) {
427 // not underlined
428 flags &= ~2;
429 } else if (p === 25) {
430 // not blink
431 flags &= ~4;
432 } else if (p === 27) {
433 // not inverse
434 flags &= ~8;
435 } else if (p === 28) {
436 // not invisible
437 flags &= ~16;
438 } else if (p === 39) {
439 // reset fg
440 fg = (this._terminal.defAttr >> 9) & 0x1ff;
441 } else if (p === 49) {
442 // reset bg
443 bg = this._terminal.defAttr & 0x1ff;
444 } else if (p === 38) {
445 // fg color 256
446 if (params[i + 1] === 2) {
447 i += 2;
448 fg = this._terminal.matchColor(
449 params[i] & 0xff,
450 params[i + 1] & 0xff,
451 params[i + 2] & 0xff);
452 if (fg === -1) fg = 0x1ff;
453 i += 2;
454 } else if (params[i + 1] === 5) {
455 i += 2;
456 p = params[i] & 0xff;
457 fg = p;
458 }
459 } else if (p === 48) {
460 // bg color 256
461 if (params[i + 1] === 2) {
462 i += 2;
463 bg = this._terminal.matchColor(
464 params[i] & 0xff,
465 params[i + 1] & 0xff,
466 params[i + 2] & 0xff);
467 if (bg === -1) bg = 0x1ff;
468 i += 2;
469 } else if (params[i + 1] === 5) {
470 i += 2;
471 p = params[i] & 0xff;
472 bg = p;
473 }
474 } else if (p === 100) {
475 // reset fg/bg
476 fg = (this._terminal.defAttr >> 9) & 0x1ff;
477 bg = this._terminal.defAttr & 0x1ff;
478 } else {
479 this._terminal.error('Unknown SGR attribute: %d.', p);
480 }
481 }
482
483 this._terminal.curAttr = (flags << 18) | (fg << 9) | bg;
484 }
485
486 /**
487 * CSI Ps n Device Status Report (DSR).
488 * Ps = 5 -> Status Report. Result (``OK'') is
489 * CSI 0 n
490 * Ps = 6 -> Report Cursor Position (CPR) [row;column].
491 * Result is
492 * CSI r ; c R
493 * CSI ? Ps n
494 * Device Status Report (DSR, DEC-specific).
495 * Ps = 6 -> Report Cursor Position (CPR) [row;column] as CSI
496 * ? r ; c R (assumes page is zero).
497 * Ps = 1 5 -> Report Printer status as CSI ? 1 0 n (ready).
498 * or CSI ? 1 1 n (not ready).
499 * Ps = 2 5 -> Report UDK status as CSI ? 2 0 n (unlocked)
500 * or CSI ? 2 1 n (locked).
501 * Ps = 2 6 -> Report Keyboard status as
502 * CSI ? 2 7 ; 1 ; 0 ; 0 n (North American).
503 * The last two parameters apply to VT400 & up, and denote key-
504 * board ready and LK01 respectively.
505 * Ps = 5 3 -> Report Locator status as
506 * CSI ? 5 3 n Locator available, if compiled-in, or
507 * CSI ? 5 0 n No Locator, if not.
508 */
509 public deviceStatus(params: number[]): void {
510 if (!this._terminal.prefix) {
511 switch (params[0]) {
512 case 5:
513 // status report
514 this._terminal.send(C0.ESC + '[0n');
515 break;
516 case 6:
517 // cursor position
518 this._terminal.send(C0.ESC + '['
519 + (this._terminal.y + 1)
520 + ';'
521 + (this._terminal.x + 1)
522 + 'R');
523 break;
524 }
525 } else if (this._terminal.prefix === '?') {
526 // modern xterm doesnt seem to
527 // respond to any of these except ?6, 6, and 5
528 switch (params[0]) {
529 case 6:
530 // cursor position
531 this._terminal.send(C0.ESC + '[?'
532 + (this._terminal.y + 1)
533 + ';'
534 + (this._terminal.x + 1)
535 + 'R');
536 break;
537 case 15:
538 // no printer
539 // this.send(C0.ESC + '[?11n');
540 break;
541 case 25:
542 // dont support user defined keys
543 // this.send(C0.ESC + '[?21n');
544 break;
545 case 26:
546 // north american keyboard
547 // this.send(C0.ESC + '[?27;1;0;0n');
548 break;
549 case 53:
550 // no dec locator/mouse
551 // this.send(C0.ESC + '[?50n');
552 break;
553 }
554 }
555 }
556
557 }