xterm
[VSoRC/.git] / node_modules / xterm / src / InputHandler.ts
1 /**
2  * Copyright (c) 2014 The xterm.js authors. All rights reserved.
3  * Copyright (c) 2012-2013, Christopher Jeffrey (MIT License)
4  * @license MIT
5  */
6
7 import { IInputHandler, IInputHandlingTerminal } from './Types';
8 import { C0, C1 } from 'common/data/EscapeSequences';
9 import { CHARSETS, DEFAULT_CHARSET } from 'common/data/Charsets';
10 import { wcwidth } from 'common/CharWidth';
11 import { EscapeSequenceParser } from 'common/parser/EscapeSequenceParser';
12 import { Disposable } from 'common/Lifecycle';
13 import { concat } from 'common/TypedArrayUtils';
14 import { StringToUtf32, stringFromCodePoint, utf32ToString, Utf8ToUtf32 } from 'common/input/TextDecoder';
15 import { DEFAULT_ATTR_DATA } from 'common/buffer/BufferLine';
16 import { EventEmitter, IEvent } from 'common/EventEmitter';
17 import { IParsingState, IDcsHandler, IEscapeSequenceParser, IParams, IFunctionIdentifier } from 'common/parser/Types';
18 import { NULL_CELL_CODE, NULL_CELL_WIDTH, Attributes, FgFlags, BgFlags, Content } from 'common/buffer/Constants';
19 import { CellData } from 'common/buffer/CellData';
20 import { AttributeData } from 'common/buffer/AttributeData';
21 import { IAttributeData, IDisposable } from 'common/Types';
22 import { ICoreService, IBufferService, IOptionsService, ILogService, IDirtyRowService, ICoreMouseService } from 'common/services/Services';
23 import { OscHandler } from 'common/parser/OscParser';
24 import { DcsHandler } from 'common/parser/DcsParser';
25
26 /**
27  * Map collect to glevel. Used in `selectCharset`.
28  */
29 const GLEVEL: {[key: string]: number} = {'(': 0, ')': 1, '*': 2, '+': 3, '-': 1, '.': 2};
30
31 /**
32  * Max length of the UTF32 input buffer. Real memory consumption is 4 times higher.
33  */
34 const MAX_PARSEBUFFER_LENGTH = 131072;
35
36
37 /**
38  * DCS subparser implementations
39  */
40
41 /**
42  * DCS $ q Pt ST
43  *   DECRQSS (https://vt100.net/docs/vt510-rm/DECRQSS.html)
44  *   Request Status String (DECRQSS), VT420 and up.
45  *   Response: DECRPSS (https://vt100.net/docs/vt510-rm/DECRPSS.html)
46  */
47 class DECRQSS implements IDcsHandler {
48   private _data: Uint32Array = new Uint32Array(0);
49
50   constructor(
51     private _bufferService: IBufferService,
52     private _coreService: ICoreService,
53     private _logService: ILogService,
54     private _optionsService: IOptionsService
55   ) { }
56
57   hook(params: IParams): void {
58     this._data = new Uint32Array(0);
59   }
60
61   put(data: Uint32Array, start: number, end: number): void {
62     this._data = concat(this._data, data.subarray(start, end));
63   }
64
65   unhook(success: boolean): void {
66     if (!success) {
67       this._data = new Uint32Array(0);
68       return;
69     }
70     const data = utf32ToString(this._data);
71     this._data = new Uint32Array(0);
72     switch (data) {
73       // valid: DCS 1 $ r Pt ST (xterm)
74       case '"q': // DECSCA
75         return this._coreService.triggerDataEvent(`${C0.ESC}P1$r0"q${C0.ESC}\\`);
76       case '"p': // DECSCL
77         return this._coreService.triggerDataEvent(`${C0.ESC}P1$r61"p${C0.ESC}\\`);
78       case 'r': // DECSTBM
79         const pt = '' + (this._bufferService.buffer.scrollTop + 1) +
80                 ';' + (this._bufferService.buffer.scrollBottom + 1) + 'r';
81         return this._coreService.triggerDataEvent(`${C0.ESC}P1$r${pt}${C0.ESC}\\`);
82       case 'm': // SGR
83         // TODO: report real settings instead of 0m
84         return this._coreService.triggerDataEvent(`${C0.ESC}P1$r0m${C0.ESC}\\`);
85       case ' q': // DECSCUSR
86         const STYLES: {[key: string]: number} = {'block': 2, 'underline': 4, 'bar': 6};
87         let style = STYLES[this._optionsService.options.cursorStyle];
88         style -= this._optionsService.options.cursorBlink ? 1 : 0;
89         return this._coreService.triggerDataEvent(`${C0.ESC}P1$r${style} q${C0.ESC}\\`);
90       default:
91         // invalid: DCS 0 $ r Pt ST (xterm)
92         this._logService.debug('Unknown DCS $q %s', data);
93         this._coreService.triggerDataEvent(`${C0.ESC}P0$r${C0.ESC}\\`);
94     }
95   }
96 }
97
98 /**
99  * DCS Ps; Ps| Pt ST
100  *   DECUDK (https://vt100.net/docs/vt510-rm/DECUDK.html)
101  *   not supported
102  */
103
104 /**
105  * DCS + q Pt ST (xterm)
106  *   Request Terminfo String
107  *   not implemented
108  */
109
110 /**
111  * DCS + p Pt ST (xterm)
112  *   Set Terminfo Data
113  *   not supported
114  */
115
116
117
118 /**
119  * The terminal's standard implementation of IInputHandler, this handles all
120  * input from the Parser.
121  *
122  * Refer to http://invisible-island.net/xterm/ctlseqs/ctlseqs.html to understand
123  * each function's header comment.
124  */
125 export class InputHandler extends Disposable implements IInputHandler {
126   private _parseBuffer: Uint32Array = new Uint32Array(4096);
127   private _stringDecoder: StringToUtf32 = new StringToUtf32();
128   private _utf8Decoder: Utf8ToUtf32 = new Utf8ToUtf32();
129   private _workCell: CellData = new CellData();
130
131   private _onCursorMove = new EventEmitter<void>();
132   public get onCursorMove(): IEvent<void> { return this._onCursorMove.event; }
133   private _onLineFeed = new EventEmitter<void>();
134   public get onLineFeed(): IEvent<void> { return this._onLineFeed.event; }
135   private _onScroll = new EventEmitter<number>();
136   public get onScroll(): IEvent<number> { return this._onScroll.event; }
137
138   constructor(
139     protected _terminal: IInputHandlingTerminal,
140     private readonly _bufferService: IBufferService,
141     private readonly _coreService: ICoreService,
142     private readonly _dirtyRowService: IDirtyRowService,
143     private readonly _logService: ILogService,
144     private readonly _optionsService: IOptionsService,
145     private readonly _coreMouseService: ICoreMouseService,
146     private readonly _parser: IEscapeSequenceParser = new EscapeSequenceParser())
147   {
148     super();
149
150     this.register(this._parser);
151
152     /**
153      * custom fallback handlers
154      */
155     this._parser.setCsiHandlerFallback((ident, params) => {
156       this._logService.debug('Unknown CSI code: ', { identifier: this._parser.identToString(ident), params: params.toArray() });
157     });
158     this._parser.setEscHandlerFallback(ident => {
159       this._logService.debug('Unknown ESC code: ', { identifier: this._parser.identToString(ident) });
160     });
161     this._parser.setExecuteHandlerFallback(code => {
162       this._logService.debug('Unknown EXECUTE code: ', { code });
163     });
164     this._parser.setOscHandlerFallback((identifier, action, data) => {
165       this._logService.debug('Unknown OSC code: ', { identifier, action, data });
166     });
167     this._parser.setDcsHandlerFallback((ident, action, payload) => {
168       if (action === 'HOOK') {
169         payload = payload.toArray();
170       }
171       this._logService.debug('Unknown DCS code: ', { identifier: this._parser.identToString(ident), action, payload });
172     });
173
174     /**
175      * print handler
176      */
177     this._parser.setPrintHandler((data, start, end) => this.print(data, start, end));
178
179     /**
180      * CSI handler
181      */
182     this._parser.setCsiHandler({final: '@'}, params => this.insertChars(params));
183     this._parser.setCsiHandler({intermediates: ' ', final: '@'}, params => this.scrollLeft(params));
184     this._parser.setCsiHandler({final: 'A'}, params => this.cursorUp(params));
185     this._parser.setCsiHandler({intermediates: ' ', final: 'A'}, params => this.scrollRight(params));
186     this._parser.setCsiHandler({final: 'B'}, params => this.cursorDown(params));
187     this._parser.setCsiHandler({final: 'C'}, params => this.cursorForward(params));
188     this._parser.setCsiHandler({final: 'D'}, params => this.cursorBackward(params));
189     this._parser.setCsiHandler({final: 'E'}, params => this.cursorNextLine(params));
190     this._parser.setCsiHandler({final: 'F'}, params => this.cursorPrecedingLine(params));
191     this._parser.setCsiHandler({final: 'G'}, params => this.cursorCharAbsolute(params));
192     this._parser.setCsiHandler({final: 'H'}, params => this.cursorPosition(params));
193     this._parser.setCsiHandler({final: 'I'}, params => this.cursorForwardTab(params));
194     this._parser.setCsiHandler({final: 'J'}, params => this.eraseInDisplay(params));
195     this._parser.setCsiHandler({prefix: '?', final: 'J'}, params => this.eraseInDisplay(params));
196     this._parser.setCsiHandler({final: 'K'}, params => this.eraseInLine(params));
197     this._parser.setCsiHandler({prefix: '?', final: 'K'}, params => this.eraseInLine(params));
198     this._parser.setCsiHandler({final: 'L'}, params => this.insertLines(params));
199     this._parser.setCsiHandler({final: 'M'}, params => this.deleteLines(params));
200     this._parser.setCsiHandler({final: 'P'}, params => this.deleteChars(params));
201     this._parser.setCsiHandler({final: 'S'}, params => this.scrollUp(params));
202     this._parser.setCsiHandler({final: 'T'}, params => this.scrollDown(params));
203     this._parser.setCsiHandler({final: 'X'}, params => this.eraseChars(params));
204     this._parser.setCsiHandler({final: 'Z'}, params => this.cursorBackwardTab(params));
205     this._parser.setCsiHandler({final: '`'}, params => this.charPosAbsolute(params));
206     this._parser.setCsiHandler({final: 'a'}, params => this.hPositionRelative(params));
207     this._parser.setCsiHandler({final: 'b'}, params => this.repeatPrecedingCharacter(params));
208     this._parser.setCsiHandler({final: 'c'}, params => this.sendDeviceAttributesPrimary(params));
209     this._parser.setCsiHandler({prefix: '>', final: 'c'}, params => this.sendDeviceAttributesSecondary(params));
210     this._parser.setCsiHandler({final: 'd'}, params => this.linePosAbsolute(params));
211     this._parser.setCsiHandler({final: 'e'}, params => this.vPositionRelative(params));
212     this._parser.setCsiHandler({final: 'f'}, params => this.hVPosition(params));
213     this._parser.setCsiHandler({final: 'g'}, params => this.tabClear(params));
214     this._parser.setCsiHandler({final: 'h'}, params => this.setMode(params));
215     this._parser.setCsiHandler({prefix: '?', final: 'h'}, params => this.setModePrivate(params));
216     this._parser.setCsiHandler({final: 'l'}, params => this.resetMode(params));
217     this._parser.setCsiHandler({prefix: '?', final: 'l'}, params => this.resetModePrivate(params));
218     this._parser.setCsiHandler({final: 'm'}, params => this.charAttributes(params));
219     this._parser.setCsiHandler({final: 'n'}, params => this.deviceStatus(params));
220     this._parser.setCsiHandler({prefix: '?', final: 'n'}, params => this.deviceStatusPrivate(params));
221     this._parser.setCsiHandler({intermediates: '!', final: 'p'}, params => this.softReset(params));
222     this._parser.setCsiHandler({intermediates: ' ', final: 'q'}, params => this.setCursorStyle(params));
223     this._parser.setCsiHandler({final: 'r'}, params => this.setScrollRegion(params));
224     this._parser.setCsiHandler({final: 's'}, params => this.saveCursor(params));
225     this._parser.setCsiHandler({final: 'u'}, params => this.restoreCursor(params));
226     this._parser.setCsiHandler({intermediates: '\'', final: '}'}, params => this.insertColumns(params));
227     this._parser.setCsiHandler({intermediates: '\'', final: '~'}, params => this.deleteColumns(params));
228
229     /**
230      * execute handler
231      */
232     this._parser.setExecuteHandler(C0.BEL, () => this.bell());
233     this._parser.setExecuteHandler(C0.LF, () => this.lineFeed());
234     this._parser.setExecuteHandler(C0.VT, () => this.lineFeed());
235     this._parser.setExecuteHandler(C0.FF, () => this.lineFeed());
236     this._parser.setExecuteHandler(C0.CR, () => this.carriageReturn());
237     this._parser.setExecuteHandler(C0.BS, () => this.backspace());
238     this._parser.setExecuteHandler(C0.HT, () => this.tab());
239     this._parser.setExecuteHandler(C0.SO, () => this.shiftOut());
240     this._parser.setExecuteHandler(C0.SI, () => this.shiftIn());
241     // FIXME:   What do to with missing? Old code just added those to print.
242
243     this._parser.setExecuteHandler(C1.IND, () => this.index());
244     this._parser.setExecuteHandler(C1.NEL, () => this.nextLine());
245     this._parser.setExecuteHandler(C1.HTS, () => this.tabSet());
246
247     /**
248      * OSC handler
249      */
250     //   0 - icon name + title
251     this._parser.setOscHandler(0, new OscHandler((data: string) => this.setTitle(data)));
252     //   1 - icon name
253     //   2 - title
254     this._parser.setOscHandler(2, new OscHandler((data: string) => this.setTitle(data)));
255     //   3 - set property X in the form "prop=value"
256     //   4 - Change Color Number
257     //   5 - Change Special Color Number
258     //   6 - Enable/disable Special Color Number c
259     //   7 - current directory? (not in xterm spec, see https://gitlab.com/gnachman/iterm2/issues/3939)
260     //  10 - Change VT100 text foreground color to Pt.
261     //  11 - Change VT100 text background color to Pt.
262     //  12 - Change text cursor color to Pt.
263     //  13 - Change mouse foreground color to Pt.
264     //  14 - Change mouse background color to Pt.
265     //  15 - Change Tektronix foreground color to Pt.
266     //  16 - Change Tektronix background color to Pt.
267     //  17 - Change highlight background color to Pt.
268     //  18 - Change Tektronix cursor color to Pt.
269     //  19 - Change highlight foreground color to Pt.
270     //  46 - Change Log File to Pt.
271     //  50 - Set Font to Pt.
272     //  51 - reserved for Emacs shell.
273     //  52 - Manipulate Selection Data.
274     // 104 ; c - Reset Color Number c.
275     // 105 ; c - Reset Special Color Number c.
276     // 106 ; c; f - Enable/disable Special Color Number c.
277     // 110 - Reset VT100 text foreground color.
278     // 111 - Reset VT100 text background color.
279     // 112 - Reset text cursor color.
280     // 113 - Reset mouse foreground color.
281     // 114 - Reset mouse background color.
282     // 115 - Reset Tektronix foreground color.
283     // 116 - Reset Tektronix background color.
284     // 117 - Reset highlight color.
285     // 118 - Reset Tektronix cursor color.
286     // 119 - Reset highlight foreground color.
287
288     /**
289      * ESC handlers
290      */
291     this._parser.setEscHandler({final: '7'}, () => this.saveCursor());
292     this._parser.setEscHandler({final: '8'}, () => this.restoreCursor());
293     this._parser.setEscHandler({final: 'D'}, () => this.index());
294     this._parser.setEscHandler({final: 'E'}, () => this.nextLine());
295     this._parser.setEscHandler({final: 'H'}, () => this.tabSet());
296     this._parser.setEscHandler({final: 'M'}, () => this.reverseIndex());
297     this._parser.setEscHandler({final: '='}, () => this.keypadApplicationMode());
298     this._parser.setEscHandler({final: '>'}, () => this.keypadNumericMode());
299     this._parser.setEscHandler({final: 'c'}, () => this.reset());
300     this._parser.setEscHandler({final: 'n'}, () => this.setgLevel(2));
301     this._parser.setEscHandler({final: 'o'}, () => this.setgLevel(3));
302     this._parser.setEscHandler({final: '|'}, () => this.setgLevel(3));
303     this._parser.setEscHandler({final: '}'}, () => this.setgLevel(2));
304     this._parser.setEscHandler({final: '~'}, () => this.setgLevel(1));
305     this._parser.setEscHandler({intermediates: '%', final: '@'}, () => this.selectDefaultCharset());
306     this._parser.setEscHandler({intermediates: '%', final: 'G'}, () => this.selectDefaultCharset());
307     for (const flag in CHARSETS) {
308       this._parser.setEscHandler({intermediates: '(', final: flag}, () => this.selectCharset('(' + flag));
309       this._parser.setEscHandler({intermediates: ')', final: flag}, () => this.selectCharset(')' + flag));
310       this._parser.setEscHandler({intermediates: '*', final: flag}, () => this.selectCharset('*' + flag));
311       this._parser.setEscHandler({intermediates: '+', final: flag}, () => this.selectCharset('+' + flag));
312       this._parser.setEscHandler({intermediates: '-', final: flag}, () => this.selectCharset('-' + flag));
313       this._parser.setEscHandler({intermediates: '.', final: flag}, () => this.selectCharset('.' + flag));
314       this._parser.setEscHandler({intermediates: '/', final: flag}, () => this.selectCharset('/' + flag)); // TODO: supported?
315     }
316     this._parser.setEscHandler({intermediates: '#', final: '8'}, () => this.screenAlignmentPattern());
317
318     /**
319      * error handler
320      */
321     this._parser.setErrorHandler((state: IParsingState) => {
322       this._logService.error('Parsing error: ', state);
323       return state;
324     });
325
326     /**
327      * DCS handler
328      */
329     this._parser.setDcsHandler({intermediates: '$', final: 'q'}, new DECRQSS(this._bufferService, this._coreService, this._logService, this._optionsService));
330   }
331
332   public dispose(): void {
333     super.dispose();
334   }
335
336   public parse(data: string | Uint8Array): void {
337     let buffer = this._bufferService.buffer;
338     const cursorStartX = buffer.x;
339     const cursorStartY = buffer.y;
340
341     this._logService.debug('parsing data', data);
342
343     // resize input buffer if needed
344     if (this._parseBuffer.length < data.length) {
345       if (this._parseBuffer.length < MAX_PARSEBUFFER_LENGTH) {
346         this._parseBuffer = new Uint32Array(Math.min(data.length, MAX_PARSEBUFFER_LENGTH));
347       }
348     }
349
350     // process big data in smaller chunks
351     if (data.length > MAX_PARSEBUFFER_LENGTH) {
352       for (let i = 0; i < data.length; i += MAX_PARSEBUFFER_LENGTH) {
353         const end = i + MAX_PARSEBUFFER_LENGTH < data.length ? i + MAX_PARSEBUFFER_LENGTH : data.length;
354         const len = (typeof data === 'string')
355           ? this._stringDecoder.decode(data.substring(i, end), this._parseBuffer)
356           : this._utf8Decoder.decode(data.subarray(i, end), this._parseBuffer);
357         this._parser.parse(this._parseBuffer, len);
358       }
359     } else {
360       const len = (typeof data === 'string')
361         ? this._stringDecoder.decode(data, this._parseBuffer)
362         : this._utf8Decoder.decode(data, this._parseBuffer);
363       this._parser.parse(this._parseBuffer, len);
364     }
365
366     buffer = this._bufferService.buffer;
367     if (buffer.x !== cursorStartX || buffer.y !== cursorStartY) {
368       this._onCursorMove.fire();
369     }
370     this._terminal.refresh(this._dirtyRowService.start, this._dirtyRowService.end);
371   }
372
373   public print(data: Uint32Array, start: number, end: number): void {
374     let code: number;
375     let chWidth: number;
376     const buffer = this._bufferService.buffer;
377     const charset = this._terminal.charset;
378     const screenReaderMode = this._optionsService.options.screenReaderMode;
379     const cols = this._bufferService.cols;
380     const wraparoundMode = this._terminal.wraparoundMode;
381     const insertMode = this._terminal.insertMode;
382     const curAttr = this._terminal.curAttrData;
383     let bufferRow = buffer.lines.get(buffer.y + buffer.ybase);
384
385     this._dirtyRowService.markDirty(buffer.y);
386     for (let pos = start; pos < end; ++pos) {
387       code = data[pos];
388
389       // calculate print space
390       // expensive call, therefore we save width in line buffer
391       chWidth = wcwidth(code);
392
393       // get charset replacement character
394       // charset is only defined for ASCII, therefore we only
395       // search for an replacement char if code < 127
396       if (code < 127 && charset) {
397         const ch = charset[String.fromCharCode(code)];
398         if (ch) {
399           code = ch.charCodeAt(0);
400         }
401       }
402
403       if (screenReaderMode) {
404         this._terminal.onA11yCharEmitter.fire(stringFromCodePoint(code));
405       }
406
407       // insert combining char at last cursor position
408       // FIXME: needs handling after cursor jumps
409       // buffer.x should never be 0 for a combining char
410       // since they always follow a cell consuming char
411       // therefore we can test for buffer.x to avoid overflow left
412       if (!chWidth && buffer.x) {
413         if (!bufferRow.getWidth(buffer.x - 1)) {
414           // found empty cell after fullwidth, need to go 2 cells back
415           // it is save to step 2 cells back here
416           // since an empty cell is only set by fullwidth chars
417           bufferRow.addCodepointToCell(buffer.x - 2, code);
418         } else {
419           bufferRow.addCodepointToCell(buffer.x - 1, code);
420         }
421         continue;
422       }
423
424       // goto next line if ch would overflow
425       // TODO: needs a global min terminal width of 2
426       // FIXME: additionally ensure chWidth fits into a line
427       //   -->  maybe forbid cols<xy at higher level as it would
428       //        introduce a bad runtime penalty here
429       if (buffer.x + chWidth - 1 >= cols) {
430         // autowrap - DECAWM
431         // automatically wraps to the beginning of the next line
432         if (wraparoundMode) {
433           buffer.x = 0;
434           buffer.y++;
435           if (buffer.y === buffer.scrollBottom + 1) {
436             buffer.y--;
437             this._terminal.scroll(true);
438           } else {
439             if (buffer.y >= this._bufferService.rows) {
440               buffer.y = this._bufferService.rows - 1;
441             }
442             // The line already exists (eg. the initial viewport), mark it as a
443             // wrapped line
444             buffer.lines.get(buffer.y).isWrapped = true;
445           }
446           // row changed, get it again
447           bufferRow = buffer.lines.get(buffer.y + buffer.ybase);
448         } else {
449           buffer.x = cols - 1;
450           if (chWidth === 2) {
451             // FIXME: check for xterm behavior
452             // What to do here? We got a wide char that does not fit into last cell
453             continue;
454           }
455         }
456       }
457
458       // insert mode: move characters to right
459       if (insertMode) {
460         // right shift cells according to the width
461         bufferRow.insertCells(buffer.x, chWidth, buffer.getNullCell(curAttr));
462         // test last cell - since the last cell has only room for
463         // a halfwidth char any fullwidth shifted there is lost
464         // and will be set to empty cell
465         if (bufferRow.getWidth(cols - 1) === 2) {
466           bufferRow.setCellFromCodePoint(cols - 1, NULL_CELL_CODE, NULL_CELL_WIDTH, curAttr.fg, curAttr.bg);
467         }
468       }
469
470       // write current char to buffer and advance cursor
471       bufferRow.setCellFromCodePoint(buffer.x++, code, chWidth, curAttr.fg, curAttr.bg);
472
473       // fullwidth char - also set next cell to placeholder stub and advance cursor
474       // for graphemes bigger than fullwidth we can simply loop to zero
475       // we already made sure above, that buffer.x + chWidth will not overflow right
476       if (chWidth > 0) {
477         while (--chWidth) {
478           // other than a regular empty cell a cell following a wide char has no width
479           bufferRow.setCellFromCodePoint(buffer.x++, 0, 0, curAttr.fg, curAttr.bg);
480         }
481       }
482     }
483     // store last char in Parser.precedingCodepoint for REP to work correctly
484     // This needs to check whether:
485     //  - fullwidth + surrogates: reset
486     //  - combining: only base char gets carried on (bug in xterm?)
487     if (end) {
488       bufferRow.loadCell(buffer.x - 1, this._workCell);
489       if (this._workCell.getWidth() === 2 || this._workCell.getCode() > 0xFFFF) {
490         this._parser.precedingCodepoint = 0;
491       } else if (this._workCell.isCombined()) {
492         this._parser.precedingCodepoint = this._workCell.getChars().charCodeAt(0);
493       } else {
494         this._parser.precedingCodepoint = this._workCell.content;
495       }
496     }
497     this._dirtyRowService.markDirty(buffer.y);
498   }
499
500   /**
501    * Forward addCsiHandler from parser.
502    */
503   public addCsiHandler(id: IFunctionIdentifier, callback: (params: IParams) => boolean): IDisposable {
504     return this._parser.addCsiHandler(id, callback);
505   }
506
507   /**
508    * Forward addDcsHandler from parser.
509    */
510   public addDcsHandler(id: IFunctionIdentifier, callback: (data: string, param: IParams) => boolean): IDisposable {
511     return this._parser.addDcsHandler(id, new DcsHandler(callback));
512   }
513
514   /**
515    * Forward addEscHandler from parser.
516    */
517   public addEscHandler(id: IFunctionIdentifier, callback: () => boolean): IDisposable {
518     return this._parser.addEscHandler(id, callback);
519   }
520
521   /**
522    * Forward addOscHandler from parser.
523    */
524   public addOscHandler(ident: number, callback: (data: string) => boolean): IDisposable {
525     return this._parser.addOscHandler(ident, new OscHandler(callback));
526   }
527
528   /**
529    * BEL
530    * Bell (Ctrl-G).
531    */
532   public bell(): void {
533     this._terminal.bell();
534   }
535
536   /**
537    * LF
538    * Line Feed or New Line (NL).  (LF  is Ctrl-J).
539    */
540   public lineFeed(): void {
541     // make buffer local for faster access
542     const buffer = this._bufferService.buffer;
543
544     if (this._optionsService.options.convertEol) {
545       buffer.x = 0;
546     }
547     buffer.y++;
548     if (buffer.y === buffer.scrollBottom + 1) {
549       buffer.y--;
550       this._terminal.scroll();
551     } else if (buffer.y >= this._bufferService.rows) {
552       buffer.y = this._bufferService.rows - 1;
553     }
554     // If the end of the line is hit, prevent this action from wrapping around to the next line.
555     if (buffer.x >= this._bufferService.cols) {
556       buffer.x--;
557     }
558
559     this._onLineFeed.fire();
560   }
561
562   /**
563    * CR
564    * Carriage Return (Ctrl-M).
565    */
566   public carriageReturn(): void {
567     this._bufferService.buffer.x = 0;
568   }
569
570   /**
571    * BS
572    * Backspace (Ctrl-H).
573    */
574   public backspace(): void {
575     this._restrictCursor();
576     if (this._bufferService.buffer.x > 0) {
577       this._bufferService.buffer.x--;
578     }
579   }
580
581   /**
582    * TAB
583    * Horizontal Tab (HT) (Ctrl-I).
584    */
585   public tab(): void {
586     if (this._bufferService.buffer.x >= this._bufferService.cols) {
587       return;
588     }
589     const originalX = this._bufferService.buffer.x;
590     this._bufferService.buffer.x = this._bufferService.buffer.nextStop();
591     if (this._optionsService.options.screenReaderMode) {
592       this._terminal.onA11yTabEmitter.fire(this._bufferService.buffer.x - originalX);
593     }
594   }
595
596   /**
597    * SO
598    * Shift Out (Ctrl-N) -> Switch to Alternate Character Set.  This invokes the
599    * G1 character set.
600    */
601   public shiftOut(): void {
602     this._terminal.setgLevel(1);
603   }
604
605   /**
606    * SI
607    * Shift In (Ctrl-O) -> Switch to Standard Character Set.  This invokes the G0
608    * character set (the default).
609    */
610   public shiftIn(): void {
611     this._terminal.setgLevel(0);
612   }
613
614   /**
615    * Restrict cursor to viewport size / scroll margin (origin mode).
616    */
617   private _restrictCursor(): void {
618     this._bufferService.buffer.x = Math.min(this._bufferService.cols - 1, Math.max(0, this._bufferService.buffer.x));
619     this._bufferService.buffer.y = this._terminal.originMode
620       ? Math.min(this._bufferService.buffer.scrollBottom, Math.max(this._bufferService.buffer.scrollTop, this._bufferService.buffer.y))
621       : Math.min(this._bufferService.rows - 1, Math.max(0, this._bufferService.buffer.y));
622   }
623
624   /**
625    * Set absolute cursor position.
626    */
627   private _setCursor(x: number, y: number): void {
628     if (this._terminal.originMode) {
629       this._bufferService.buffer.x = x;
630       this._bufferService.buffer.y = this._bufferService.buffer.scrollTop + y;
631     } else {
632       this._bufferService.buffer.x = x;
633       this._bufferService.buffer.y = y;
634     }
635     this._restrictCursor();
636   }
637
638   /**
639    * Set relative cursor position.
640    */
641   private _moveCursor(x: number, y: number): void {
642     // for relative changes we have to make sure we are within 0 .. cols/rows - 1
643     // before calculating the new position
644     this._restrictCursor();
645     this._setCursor(this._bufferService.buffer.x + x, this._bufferService.buffer.y + y);
646   }
647
648   /**
649    * CSI Ps A
650    * Cursor Up Ps Times (default = 1) (CUU).
651    */
652   public cursorUp(params: IParams): void {
653     // stop at scrollTop
654     const diffToTop = this._bufferService.buffer.y - this._bufferService.buffer.scrollTop;
655     if (diffToTop >= 0) {
656       this._moveCursor(0, -Math.min(diffToTop, params.params[0] || 1));
657     } else {
658       this._moveCursor(0, -(params.params[0] || 1));
659     }
660   }
661
662   /**
663    * CSI Ps B
664    * Cursor Down Ps Times (default = 1) (CUD).
665    */
666   public cursorDown(params: IParams): void {
667     // stop at scrollBottom
668     const diffToBottom = this._bufferService.buffer.scrollBottom - this._bufferService.buffer.y;
669     if (diffToBottom >= 0) {
670       this._moveCursor(0, Math.min(diffToBottom, params.params[0] || 1));
671     } else {
672       this._moveCursor(0, params.params[0] || 1);
673     }
674   }
675
676   /**
677    * CSI Ps C
678    * Cursor Forward Ps Times (default = 1) (CUF).
679    */
680   public cursorForward(params: IParams): void {
681     this._moveCursor(params.params[0] || 1, 0);
682   }
683
684   /**
685    * CSI Ps D
686    * Cursor Backward Ps Times (default = 1) (CUB).
687    */
688   public cursorBackward(params: IParams): void {
689     this._moveCursor(-(params.params[0] || 1), 0);
690   }
691
692   /**
693    * CSI Ps E
694    * Cursor Next Line Ps Times (default = 1) (CNL).
695    * Other than cursorDown (CUD) also set the cursor to first column.
696    */
697   public cursorNextLine(params: IParams): void {
698     this.cursorDown(params);
699     this._bufferService.buffer.x = 0;
700   }
701
702   /**
703    * CSI Ps F
704    * Cursor Previous Line Ps Times (default = 1) (CPL).
705    * Other than cursorUp (CUU) also set the cursor to first column.
706    */
707   public cursorPrecedingLine(params: IParams): void {
708     this.cursorUp(params);
709     this._bufferService.buffer.x = 0;
710   }
711
712   /**
713    * CSI Ps G
714    * Cursor Character Absolute  [column] (default = [row,1]) (CHA).
715    */
716   public cursorCharAbsolute(params: IParams): void {
717     this._setCursor((params.params[0] || 1) - 1, this._bufferService.buffer.y);
718   }
719
720   /**
721    * CSI Ps ; Ps H
722    * Cursor Position [row;column] (default = [1,1]) (CUP).
723    */
724   public cursorPosition(params: IParams): void {
725     this._setCursor(
726       // col
727       (params.length >= 2) ? (params.params[1] || 1) - 1 : 0,
728       // row
729       (params.params[0] || 1) - 1);
730   }
731
732   /**
733    * CSI Pm `  Character Position Absolute
734    *   [column] (default = [row,1]) (HPA).
735    * Currently same functionality as CHA.
736    */
737   public charPosAbsolute(params: IParams): void {
738     this._setCursor((params.params[0] || 1) - 1, this._bufferService.buffer.y);
739   }
740
741   /**
742    * CSI Pm a  Character Position Relative
743    *   [columns] (default = [row,col+1]) (HPR)
744    * Currently same functionality as CUF.
745    */
746   public hPositionRelative(params: IParams): void {
747     this._moveCursor(params.params[0] || 1, 0);
748   }
749
750   /**
751    * CSI Pm d  Vertical Position Absolute (VPA)
752    *   [row] (default = [1,column])
753    */
754   public linePosAbsolute(params: IParams): void {
755     this._setCursor(this._bufferService.buffer.x, (params.params[0] || 1) - 1);
756   }
757
758   /**
759    * CSI Pm e  Vertical Position Relative (VPR)
760    *   [rows] (default = [row+1,column])
761    * reuse CSI Ps B ?
762    */
763   public vPositionRelative(params: IParams): void {
764     this._moveCursor(0, params.params[0] || 1);
765   }
766
767   /**
768    * CSI Ps ; Ps f
769    *   Horizontal and Vertical Position [row;column] (default =
770    *   [1,1]) (HVP).
771    *   Same as CUP.
772    */
773   public hVPosition(params: IParams): void {
774     this.cursorPosition(params);
775   }
776
777   /**
778    * CSI Ps g  Tab Clear (TBC).
779    *     Ps = 0  -> Clear Current Column (default).
780    *     Ps = 3  -> Clear All.
781    * Potentially:
782    *   Ps = 2  -> Clear Stops on Line.
783    *   http://vt100.net/annarbor/aaa-ug/section6.html
784    */
785   public tabClear(params: IParams): void {
786     const param = params.params[0];
787     if (param === 0) {
788       delete this._bufferService.buffer.tabs[this._bufferService.buffer.x];
789     } else if (param === 3) {
790       this._bufferService.buffer.tabs = {};
791     }
792   }
793
794   /**
795    * CSI Ps I
796    *   Cursor Forward Tabulation Ps tab stops (default = 1) (CHT).
797    */
798   public cursorForwardTab(params: IParams): void {
799     if (this._bufferService.buffer.x >= this._bufferService.cols) {
800       return;
801     }
802     let param = params.params[0] || 1;
803     while (param--) {
804       this._bufferService.buffer.x = this._bufferService.buffer.nextStop();
805     }
806   }
807
808   /**
809    * CSI Ps Z  Cursor Backward Tabulation Ps tab stops (default = 1) (CBT).
810    */
811   public cursorBackwardTab(params: IParams): void {
812     if (this._bufferService.buffer.x >= this._bufferService.cols) {
813       return;
814     }
815     let param = params.params[0] || 1;
816
817     // make buffer local for faster access
818     const buffer = this._bufferService.buffer;
819
820     while (param--) {
821       buffer.x = buffer.prevStop();
822     }
823   }
824
825
826   /**
827    * Helper method to erase cells in a terminal row.
828    * The cell gets replaced with the eraseChar of the terminal.
829    * @param y row index
830    * @param start first cell index to be erased
831    * @param end   end - 1 is last erased cell
832    */
833   private _eraseInBufferLine(y: number, start: number, end: number, clearWrap: boolean = false): void {
834     const line = this._bufferService.buffer.lines.get(this._bufferService.buffer.ybase + y);
835     line.replaceCells(
836       start,
837       end,
838       this._bufferService.buffer.getNullCell(this._terminal.eraseAttrData())
839     );
840     if (clearWrap) {
841       line.isWrapped = false;
842     }
843   }
844
845   /**
846    * Helper method to reset cells in a terminal row.
847    * The cell gets replaced with the eraseChar of the terminal and the isWrapped property is set to false.
848    * @param y row index
849    */
850   private _resetBufferLine(y: number): void {
851     const line = this._bufferService.buffer.lines.get(this._bufferService.buffer.ybase + y);
852     line.fill(this._bufferService.buffer.getNullCell(this._terminal.eraseAttrData()));
853     line.isWrapped = false;
854   }
855
856   /**
857    * CSI Ps J  Erase in Display (ED).
858    *     Ps = 0  -> Erase Below (default).
859    *     Ps = 1  -> Erase Above.
860    *     Ps = 2  -> Erase All.
861    *     Ps = 3  -> Erase Saved Lines (xterm).
862    * CSI ? Ps J
863    *   Erase in Display (DECSED).
864    *     Ps = 0  -> Selective Erase Below (default).
865    *     Ps = 1  -> Selective Erase Above.
866    *     Ps = 2  -> Selective Erase All.
867    */
868   public eraseInDisplay(params: IParams): void {
869     this._restrictCursor();
870     let j;
871     switch (params.params[0]) {
872       case 0:
873         j = this._bufferService.buffer.y;
874         this._dirtyRowService.markDirty(j);
875         this._eraseInBufferLine(j++, this._bufferService.buffer.x, this._bufferService.cols, this._bufferService.buffer.x === 0);
876         for (; j < this._bufferService.rows; j++) {
877           this._resetBufferLine(j);
878         }
879         this._dirtyRowService.markDirty(j);
880         break;
881       case 1:
882         j = this._bufferService.buffer.y;
883         this._dirtyRowService.markDirty(j);
884         // Deleted front part of line and everything before. This line will no longer be wrapped.
885         this._eraseInBufferLine(j, 0, this._bufferService.buffer.x + 1, true);
886         if (this._bufferService.buffer.x + 1 >= this._bufferService.cols) {
887           // Deleted entire previous line. This next line can no longer be wrapped.
888           this._bufferService.buffer.lines.get(j + 1).isWrapped = false;
889         }
890         while (j--) {
891           this._resetBufferLine(j);
892         }
893         this._dirtyRowService.markDirty(0);
894         break;
895       case 2:
896         j = this._bufferService.rows;
897         this._dirtyRowService.markDirty(j - 1);
898         while (j--) {
899           this._resetBufferLine(j);
900         }
901         this._dirtyRowService.markDirty(0);
902         break;
903       case 3:
904         // Clear scrollback (everything not in viewport)
905         const scrollBackSize = this._bufferService.buffer.lines.length - this._bufferService.rows;
906         if (scrollBackSize > 0) {
907           this._bufferService.buffer.lines.trimStart(scrollBackSize);
908           this._bufferService.buffer.ybase = Math.max(this._bufferService.buffer.ybase - scrollBackSize, 0);
909           this._bufferService.buffer.ydisp = Math.max(this._bufferService.buffer.ydisp - scrollBackSize, 0);
910           // Force a scroll event to refresh viewport
911           this._onScroll.fire(0);
912         }
913         break;
914     }
915   }
916
917   /**
918    * CSI Ps K  Erase in Line (EL).
919    *     Ps = 0  -> Erase to Right (default).
920    *     Ps = 1  -> Erase to Left.
921    *     Ps = 2  -> Erase All.
922    * CSI ? Ps K
923    *   Erase in Line (DECSEL).
924    *     Ps = 0  -> Selective Erase to Right (default).
925    *     Ps = 1  -> Selective Erase to Left.
926    *     Ps = 2  -> Selective Erase All.
927    */
928   public eraseInLine(params: IParams): void {
929     this._restrictCursor();
930     switch (params.params[0]) {
931       case 0:
932         this._eraseInBufferLine(this._bufferService.buffer.y, this._bufferService.buffer.x, this._bufferService.cols);
933         break;
934       case 1:
935         this._eraseInBufferLine(this._bufferService.buffer.y, 0, this._bufferService.buffer.x + 1);
936         break;
937       case 2:
938         this._eraseInBufferLine(this._bufferService.buffer.y, 0, this._bufferService.cols);
939         break;
940     }
941     this._dirtyRowService.markDirty(this._bufferService.buffer.y);
942   }
943
944   /**
945    * CSI Ps L
946    * Insert Ps Line(s) (default = 1) (IL).
947    */
948   public insertLines(params: IParams): void {
949     this._restrictCursor();
950     let param = params.params[0] || 1;
951
952     // make buffer local for faster access
953     const buffer = this._bufferService.buffer;
954
955     if (buffer.y > buffer.scrollBottom || buffer.y < buffer.scrollTop) {
956       return;
957     }
958
959     const row: number = buffer.y + buffer.ybase;
960
961     const scrollBottomRowsOffset = this._bufferService.rows - 1 - buffer.scrollBottom;
962     const scrollBottomAbsolute = this._bufferService.rows - 1 + buffer.ybase - scrollBottomRowsOffset + 1;
963     while (param--) {
964       // test: echo -e '\e[44m\e[1L\e[0m'
965       // blankLine(true) - xterm/linux behavior
966       buffer.lines.splice(scrollBottomAbsolute - 1, 1);
967       buffer.lines.splice(row, 0, buffer.getBlankLine(this._terminal.eraseAttrData()));
968     }
969
970     this._dirtyRowService.markRangeDirty(buffer.y, buffer.scrollBottom);
971     buffer.x = 0; // see https://vt100.net/docs/vt220-rm/chapter4.html - vt220 only?
972   }
973
974   /**
975    * CSI Ps M
976    * Delete Ps Line(s) (default = 1) (DL).
977    */
978   public deleteLines(params: IParams): void {
979     this._restrictCursor();
980     let param = params.params[0] || 1;
981
982     // make buffer local for faster access
983     const buffer = this._bufferService.buffer;
984
985     if (buffer.y > buffer.scrollBottom || buffer.y < buffer.scrollTop) {
986       return;
987     }
988
989     const row: number = buffer.y + buffer.ybase;
990
991     let j: number;
992     j = this._bufferService.rows - 1 - buffer.scrollBottom;
993     j = this._bufferService.rows - 1 + buffer.ybase - j;
994     while (param--) {
995       // test: echo -e '\e[44m\e[1M\e[0m'
996       // blankLine(true) - xterm/linux behavior
997       buffer.lines.splice(row, 1);
998       buffer.lines.splice(j, 0, buffer.getBlankLine(this._terminal.eraseAttrData()));
999     }
1000
1001     this._dirtyRowService.markRangeDirty(buffer.y, buffer.scrollBottom);
1002     buffer.x = 0; // see https://vt100.net/docs/vt220-rm/chapter4.html - vt220 only?
1003   }
1004
1005   /**
1006    * CSI Ps @
1007    * Insert Ps (Blank) Character(s) (default = 1) (ICH).
1008    */
1009   public insertChars(params: IParams): void {
1010     this._restrictCursor();
1011     const line = this._bufferService.buffer.lines.get(this._bufferService.buffer.y + this._bufferService.buffer.ybase);
1012     if (line) {
1013       line.insertCells(
1014         this._bufferService.buffer.x,
1015         params.params[0] || 1,
1016         this._bufferService.buffer.getNullCell(this._terminal.eraseAttrData())
1017       );
1018       this._dirtyRowService.markDirty(this._bufferService.buffer.y);
1019     }
1020   }
1021
1022   /**
1023    * CSI Ps P
1024    * Delete Ps Character(s) (default = 1) (DCH).
1025    */
1026   public deleteChars(params: IParams): void {
1027     this._restrictCursor();
1028     const line = this._bufferService.buffer.lines.get(this._bufferService.buffer.y + this._bufferService.buffer.ybase);
1029     if (line) {
1030       line.deleteCells(
1031         this._bufferService.buffer.x,
1032         params.params[0] || 1,
1033         this._bufferService.buffer.getNullCell(this._terminal.eraseAttrData())
1034       );
1035       this._dirtyRowService.markDirty(this._bufferService.buffer.y);
1036     }
1037   }
1038
1039   /**
1040    * CSI Ps S  Scroll up Ps lines (default = 1) (SU).
1041    */
1042   public scrollUp(params: IParams): void {
1043     let param = params.params[0] || 1;
1044
1045     // make buffer local for faster access
1046     const buffer = this._bufferService.buffer;
1047
1048     while (param--) {
1049       buffer.lines.splice(buffer.ybase + buffer.scrollTop, 1);
1050       buffer.lines.splice(buffer.ybase + buffer.scrollBottom, 0, buffer.getBlankLine(this._terminal.eraseAttrData()));
1051     }
1052     this._dirtyRowService.markRangeDirty(buffer.scrollTop, buffer.scrollBottom);
1053   }
1054
1055   /**
1056    * CSI Ps T  Scroll down Ps lines (default = 1) (SD).
1057    */
1058   public scrollDown(params: IParams): void {
1059     let param = params.params[0] || 1;
1060
1061     // make buffer local for faster access
1062     const buffer = this._bufferService.buffer;
1063
1064     while (param--) {
1065       buffer.lines.splice(buffer.ybase + buffer.scrollBottom, 1);
1066       buffer.lines.splice(buffer.ybase + buffer.scrollTop, 0, buffer.getBlankLine(DEFAULT_ATTR_DATA));
1067     }
1068     this._dirtyRowService.markRangeDirty(buffer.scrollTop, buffer.scrollBottom);
1069   }
1070
1071   /**
1072    * CSI Ps SP @  Scroll left Ps columns (default = 1) (SL) ECMA-48
1073    *
1074    * Notation: (Pn)
1075    * Representation: CSI Pn 02/00 04/00
1076    * Parameter default value: Pn = 1
1077    * SL causes the data in the presentation component to be moved by n character positions
1078    * if the line orientation is horizontal, or by n line positions if the line orientation
1079    * is vertical, such that the data appear to move to the left; where n equals the value of Pn.
1080    * The active presentation position is not affected by this control function.
1081    *
1082    * Supported:
1083    *   - always left shift (no line orientation setting respected)
1084    */
1085   public scrollLeft(params: IParams): void {
1086     const buffer = this._bufferService.buffer;
1087     if (buffer.y > buffer.scrollBottom || buffer.y < buffer.scrollTop) {
1088       return;
1089     }
1090     const param = params.params[0] || 1;
1091     for (let y = buffer.scrollTop; y <= buffer.scrollBottom; ++y) {
1092       const line = buffer.lines.get(buffer.ybase + y);
1093       line.deleteCells(0, param, buffer.getNullCell(this._terminal.eraseAttrData()));
1094       line.isWrapped = false;
1095     }
1096     this._dirtyRowService.markRangeDirty(buffer.scrollTop, buffer.scrollBottom);
1097   }
1098
1099   /**
1100    * CSI Ps SP A  Scroll right Ps columns (default = 1) (SR) ECMA-48
1101    *
1102    * Notation: (Pn)
1103    * Representation: CSI Pn 02/00 04/01
1104    * Parameter default value: Pn = 1
1105    * SR causes the data in the presentation component to be moved by n character positions
1106    * if the line orientation is horizontal, or by n line positions if the line orientation
1107    * is vertical, such that the data appear to move to the right; where n equals the value of Pn.
1108    * The active presentation position is not affected by this control function.
1109    *
1110    * Supported:
1111    *   - always right shift (no line orientation setting respected)
1112    */
1113   public scrollRight(params: IParams): void {
1114     const buffer = this._bufferService.buffer;
1115     if (buffer.y > buffer.scrollBottom || buffer.y < buffer.scrollTop) {
1116       return;
1117     }
1118     const param = params.params[0] || 1;
1119     for (let y = buffer.scrollTop; y <= buffer.scrollBottom; ++y) {
1120       const line = buffer.lines.get(buffer.ybase + y);
1121       line.insertCells(0, param, buffer.getNullCell(this._terminal.eraseAttrData()));
1122       line.isWrapped = false;
1123     }
1124     this._dirtyRowService.markRangeDirty(buffer.scrollTop, buffer.scrollBottom);
1125   }
1126
1127   /**
1128    * CSI Pm ' }
1129    * Insert Ps Column(s) (default = 1) (DECIC), VT420 and up.
1130    */
1131   public insertColumns(params: IParams): void {
1132     const buffer = this._bufferService.buffer;
1133     if (buffer.y > buffer.scrollBottom || buffer.y < buffer.scrollTop) {
1134       return;
1135     }
1136     const param = params.params[0] || 1;
1137     for (let y = buffer.scrollTop; y <= buffer.scrollBottom; ++y) {
1138       const line = this._bufferService.buffer.lines.get(buffer.ybase + y);
1139       line.insertCells(buffer.x, param, buffer.getNullCell(this._terminal.eraseAttrData()));
1140       line.isWrapped = false;
1141     }
1142     this._dirtyRowService.markRangeDirty(buffer.scrollTop, buffer.scrollBottom);
1143   }
1144
1145   /**
1146    * CSI Pm ' ~
1147    * Delete Ps Column(s) (default = 1) (DECDC), VT420 and up.
1148    */
1149   public deleteColumns(params: IParams): void {
1150     const buffer = this._bufferService.buffer;
1151     if (buffer.y > buffer.scrollBottom || buffer.y < buffer.scrollTop) {
1152       return;
1153     }
1154     const param = params.params[0] || 1;
1155     for (let y = buffer.scrollTop; y <= buffer.scrollBottom; ++y) {
1156       const line = buffer.lines.get(buffer.ybase + y);
1157       line.deleteCells(buffer.x, param, buffer.getNullCell(this._terminal.eraseAttrData()));
1158       line.isWrapped = false;
1159     }
1160     this._dirtyRowService.markRangeDirty(buffer.scrollTop, buffer.scrollBottom);
1161   }
1162
1163   /**
1164    * CSI Ps X
1165    * Erase Ps Character(s) (default = 1) (ECH).
1166    */
1167   public eraseChars(params: IParams): void {
1168     this._restrictCursor();
1169     const line = this._bufferService.buffer.lines.get(this._bufferService.buffer.y + this._bufferService.buffer.ybase);
1170     if (line) {
1171       line.replaceCells(
1172         this._bufferService.buffer.x,
1173         this._bufferService.buffer.x + (params.params[0] || 1),
1174         this._bufferService.buffer.getNullCell(this._terminal.eraseAttrData())
1175       );
1176       this._dirtyRowService.markDirty(this._bufferService.buffer.y);
1177     }
1178   }
1179
1180   /**
1181    * CSI Ps b  Repeat the preceding graphic character Ps times (REP).
1182    * From ECMA 48 (@see http://www.ecma-international.org/publications/files/ECMA-ST/Ecma-048.pdf)
1183    *    Notation: (Pn)
1184    *    Representation: CSI Pn 06/02
1185    *    Parameter default value: Pn = 1
1186    *    REP is used to indicate that the preceding character in the data stream,
1187    *    if it is a graphic character (represented by one or more bit combinations) including SPACE,
1188    *    is to be repeated n times, where n equals the value of Pn.
1189    *    If the character preceding REP is a control function or part of a control function,
1190    *    the effect of REP is not defined by this Standard.
1191    *
1192    * Since we propagate the terminal as xterm-256color we have to follow xterm's behavior:
1193    *    - fullwidth + surrogate chars are ignored
1194    *    - for combining chars only the base char gets repeated
1195    *    - text attrs are applied normally
1196    *    - wrap around is respected
1197    *    - any valid sequence resets the carried forward char
1198    *
1199    * Note: To get reset on a valid sequence working correctly without much runtime penalty,
1200    * the preceding codepoint is stored on the parser in `this.print` and reset during `parser.parse`.
1201    */
1202   public repeatPrecedingCharacter(params: IParams): void {
1203     if (!this._parser.precedingCodepoint) {
1204       return;
1205     }
1206     // call print to insert the chars and handle correct wrapping
1207     const length = params.params[0] || 1;
1208     const data = new Uint32Array(length);
1209     for (let i = 0; i < length; ++i) {
1210       data[i] = this._parser.precedingCodepoint;
1211     }
1212     this.print(data, 0, data.length);
1213   }
1214
1215   /**
1216    * CSI Ps c  Send Device Attributes (Primary DA).
1217    *     Ps = 0  or omitted -> request attributes from terminal.  The
1218    *     response depends on the decTerminalID resource setting.
1219    *     -> CSI ? 1 ; 2 c  (``VT100 with Advanced Video Option'')
1220    *     -> CSI ? 1 ; 0 c  (``VT101 with No Options'')
1221    *     -> CSI ? 6 c  (``VT102'')
1222    *     -> CSI ? 6 0 ; 1 ; 2 ; 6 ; 8 ; 9 ; 1 5 ; c  (``VT220'')
1223    *   The VT100-style response parameters do not mean anything by
1224    *   themselves.  VT220 parameters do, telling the host what fea-
1225    *   tures the terminal supports:
1226    *     Ps = 1  -> 132-columns.
1227    *     Ps = 2  -> Printer.
1228    *     Ps = 6  -> Selective erase.
1229    *     Ps = 8  -> User-defined keys.
1230    *     Ps = 9  -> National replacement character sets.
1231    *     Ps = 1 5  -> Technical characters.
1232    *     Ps = 2 2  -> ANSI color, e.g., VT525.
1233    *     Ps = 2 9  -> ANSI text locator (i.e., DEC Locator mode).
1234    * CSI > Ps c
1235    *   Send Device Attributes (Secondary DA).
1236    *     Ps = 0  or omitted -> request the terminal's identification
1237    *     code.  The response depends on the decTerminalID resource set-
1238    *     ting.  It should apply only to VT220 and up, but xterm extends
1239    *     this to VT100.
1240    *     -> CSI  > Pp ; Pv ; Pc c
1241    *   where Pp denotes the terminal type
1242    *     Pp = 0  -> ``VT100''.
1243    *     Pp = 1  -> ``VT220''.
1244    *   and Pv is the firmware version (for xterm, this was originally
1245    *   the XFree86 patch number, starting with 95).  In a DEC termi-
1246    *   nal, Pc indicates the ROM cartridge registration number and is
1247    *   always zero.
1248    * More information:
1249    *   xterm/charproc.c - line 2012, for more information.
1250    *   vim responds with ^[[?0c or ^[[?1c after the terminal's response (?)
1251    */
1252   public sendDeviceAttributesPrimary(params: IParams): void {
1253     if (params.params[0] > 0) {
1254       return;
1255     }
1256     if (this._terminal.is('xterm') || this._terminal.is('rxvt-unicode') || this._terminal.is('screen')) {
1257       this._coreService.triggerDataEvent(C0.ESC + '[?1;2c');
1258     } else if (this._terminal.is('linux')) {
1259       this._coreService.triggerDataEvent(C0.ESC + '[?6c');
1260     }
1261   }
1262   public sendDeviceAttributesSecondary(params: IParams): void {
1263     if (params.params[0] > 0) {
1264       return;
1265     }
1266     // xterm and urxvt
1267     // seem to spit this
1268     // out around ~370 times (?).
1269     if (this._terminal.is('xterm')) {
1270       this._coreService.triggerDataEvent(C0.ESC + '[>0;276;0c');
1271     } else if (this._terminal.is('rxvt-unicode')) {
1272       this._coreService.triggerDataEvent(C0.ESC + '[>85;95;0c');
1273     } else if (this._terminal.is('linux')) {
1274       // not supported by linux console.
1275       // linux console echoes parameters.
1276       this._coreService.triggerDataEvent(params.params[0] + 'c');
1277     } else if (this._terminal.is('screen')) {
1278       this._coreService.triggerDataEvent(C0.ESC + '[>83;40003;0c');
1279     }
1280   }
1281
1282   /**
1283    * CSI Pm h  Set Mode (SM).
1284    *     Ps = 2  -> Keyboard Action Mode (AM).
1285    *     Ps = 4  -> Insert Mode (IRM).
1286    *     Ps = 1 2  -> Send/receive (SRM).
1287    *     Ps = 2 0  -> Automatic Newline (LNM).
1288    * CSI ? Pm h
1289    *   DEC Private Mode Set (DECSET).
1290    *     Ps = 1  -> Application Cursor Keys (DECCKM).
1291    *     Ps = 2  -> Designate USASCII for character sets G0-G3
1292    *     (DECANM), and set VT100 mode.
1293    *     Ps = 3  -> 132 Column Mode (DECCOLM).
1294    *     Ps = 4  -> Smooth (Slow) Scroll (DECSCLM).
1295    *     Ps = 5  -> Reverse Video (DECSCNM).
1296    *     Ps = 6  -> Origin Mode (DECOM).
1297    *     Ps = 7  -> Wraparound Mode (DECAWM).
1298    *     Ps = 8  -> Auto-repeat Keys (DECARM).
1299    *     Ps = 9  -> Send Mouse X & Y on button press.  See the sec-
1300    *     tion Mouse Tracking.
1301    *     Ps = 1 0  -> Show toolbar (rxvt).
1302    *     Ps = 1 2  -> Start Blinking Cursor (att610).
1303    *     Ps = 1 8  -> Print form feed (DECPFF).
1304    *     Ps = 1 9  -> Set print extent to full screen (DECPEX).
1305    *     Ps = 2 5  -> Show Cursor (DECTCEM).
1306    *     Ps = 3 0  -> Show scrollbar (rxvt).
1307    *     Ps = 3 5  -> Enable font-shifting functions (rxvt).
1308    *     Ps = 3 8  -> Enter Tektronix Mode (DECTEK).
1309    *     Ps = 4 0  -> Allow 80 -> 132 Mode.
1310    *     Ps = 4 1  -> more(1) fix (see curses resource).
1311    *     Ps = 4 2  -> Enable Nation Replacement Character sets (DECN-
1312    *     RCM).
1313    *     Ps = 4 4  -> Turn On Margin Bell.
1314    *     Ps = 4 5  -> Reverse-wraparound Mode.
1315    *     Ps = 4 6  -> Start Logging.  This is normally disabled by a
1316    *     compile-time option.
1317    *     Ps = 4 7  -> Use Alternate Screen Buffer.  (This may be dis-
1318    *     abled by the titeInhibit resource).
1319    *     Ps = 6 6  -> Application keypad (DECNKM).
1320    *     Ps = 6 7  -> Backarrow key sends backspace (DECBKM).
1321    *     Ps = 1 0 0 0  -> Send Mouse X & Y on button press and
1322    *     release.  See the section Mouse Tracking.
1323    *     Ps = 1 0 0 1  -> Use Hilite Mouse Tracking.
1324    *     Ps = 1 0 0 2  -> Use Cell Motion Mouse Tracking.
1325    *     Ps = 1 0 0 3  -> Use All Motion Mouse Tracking.
1326    *     Ps = 1 0 0 4  -> Send FocusIn/FocusOut events.
1327    *     Ps = 1 0 0 5  -> Enable Extended Mouse Mode.
1328    *     Ps = 1 0 1 0  -> Scroll to bottom on tty output (rxvt).
1329    *     Ps = 1 0 1 1  -> Scroll to bottom on key press (rxvt).
1330    *     Ps = 1 0 3 4  -> Interpret "meta" key, sets eighth bit.
1331    *     (enables the eightBitInput resource).
1332    *     Ps = 1 0 3 5  -> Enable special modifiers for Alt and Num-
1333    *     Lock keys.  (This enables the numLock resource).
1334    *     Ps = 1 0 3 6  -> Send ESC   when Meta modifies a key.  (This
1335    *     enables the metaSendsEscape resource).
1336    *     Ps = 1 0 3 7  -> Send DEL from the editing-keypad Delete
1337    *     key.
1338    *     Ps = 1 0 3 9  -> Send ESC  when Alt modifies a key.  (This
1339    *     enables the altSendsEscape resource).
1340    *     Ps = 1 0 4 0  -> Keep selection even if not highlighted.
1341    *     (This enables the keepSelection resource).
1342    *     Ps = 1 0 4 1  -> Use the CLIPBOARD selection.  (This enables
1343    *     the selectToClipboard resource).
1344    *     Ps = 1 0 4 2  -> Enable Urgency window manager hint when
1345    *     Control-G is received.  (This enables the bellIsUrgent
1346    *     resource).
1347    *     Ps = 1 0 4 3  -> Enable raising of the window when Control-G
1348    *     is received.  (enables the popOnBell resource).
1349    *     Ps = 1 0 4 7  -> Use Alternate Screen Buffer.  (This may be
1350    *     disabled by the titeInhibit resource).
1351    *     Ps = 1 0 4 8  -> Save cursor as in DECSC.  (This may be dis-
1352    *     abled by the titeInhibit resource).
1353    *     Ps = 1 0 4 9  -> Save cursor as in DECSC and use Alternate
1354    *     Screen Buffer, clearing it first.  (This may be disabled by
1355    *     the titeInhibit resource).  This combines the effects of the 1
1356    *     0 4 7  and 1 0 4 8  modes.  Use this with terminfo-based
1357    *     applications rather than the 4 7  mode.
1358    *     Ps = 1 0 5 0  -> Set terminfo/termcap function-key mode.
1359    *     Ps = 1 0 5 1  -> Set Sun function-key mode.
1360    *     Ps = 1 0 5 2  -> Set HP function-key mode.
1361    *     Ps = 1 0 5 3  -> Set SCO function-key mode.
1362    *     Ps = 1 0 6 0  -> Set legacy keyboard emulation (X11R6).
1363    *     Ps = 1 0 6 1  -> Set VT220 keyboard emulation.
1364    *     Ps = 2 0 0 4  -> Set bracketed paste mode.
1365    * Modes:
1366    *   http: *vt100.net/docs/vt220-rm/chapter4.html
1367    */
1368   public setMode(params: IParams): void {
1369     for (let i = 0; i < params.length; i++) {
1370       switch (params.params[i]) {
1371         case 4:
1372           this._terminal.insertMode = true;
1373           break;
1374         case 20:
1375           // this._t.convertEol = true;
1376           break;
1377       }
1378     }
1379   }
1380   public setModePrivate(params: IParams): void {
1381     for (let i = 0; i < params.length; i++) {
1382       switch (params.params[i]) {
1383         case 1:
1384           this._coreService.decPrivateModes.applicationCursorKeys = true;
1385           break;
1386         case 2:
1387           this._terminal.setgCharset(0, DEFAULT_CHARSET);
1388           this._terminal.setgCharset(1, DEFAULT_CHARSET);
1389           this._terminal.setgCharset(2, DEFAULT_CHARSET);
1390           this._terminal.setgCharset(3, DEFAULT_CHARSET);
1391           // set VT100 mode here
1392           break;
1393         case 3: // 132 col mode
1394           // TODO: move DECCOLM into compat addon
1395           this._terminal.savedCols = this._bufferService.cols;
1396           this._terminal.resize(132, this._bufferService.rows);
1397           this._terminal.reset();
1398           break;
1399         case 6:
1400           this._terminal.originMode = true;
1401           this._setCursor(0, 0);
1402           break;
1403         case 7:
1404           this._terminal.wraparoundMode = true;
1405           break;
1406         case 12:
1407           // this.cursorBlink = true;
1408           break;
1409         case 66:
1410           this._logService.debug('Serial port requested application keypad.');
1411           this._terminal.applicationKeypad = true;
1412           if (this._terminal.viewport) {
1413             this._terminal.viewport.syncScrollArea();
1414           }
1415           break;
1416         case 9: // X10 Mouse
1417           // no release, no motion, no wheel, no modifiers.
1418           this._coreMouseService.activeProtocol = 'X10';
1419           break;
1420         case 1000: // vt200 mouse
1421           // no motion.
1422           this._coreMouseService.activeProtocol = 'VT200';
1423           break;
1424         case 1002: // button event mouse
1425           this._coreMouseService.activeProtocol = 'DRAG';
1426           break;
1427         case 1003: // any event mouse
1428           // any event - sends motion events,
1429           // even if there is no button held down.
1430           this._coreMouseService.activeProtocol = 'ANY';
1431           break;
1432         case 1004: // send focusin/focusout events
1433           // focusin: ^[[I
1434           // focusout: ^[[O
1435           this._terminal.sendFocus = true;
1436           break;
1437         case 1005: // utf8 ext mode mouse - removed in #2507
1438           this._logService.debug('DECSET 1005 not supported (see #2507)');
1439           break;
1440         case 1006: // sgr ext mode mouse
1441           this._coreMouseService.activeEncoding = 'SGR';
1442           break;
1443         case 1015: // urxvt ext mode mouse - removed in #2507
1444           this._logService.debug('DECSET 1015 not supported (see #2507)');
1445           break;
1446         case 25: // show cursor
1447           this._terminal.cursorHidden = false;
1448           break;
1449         case 1048: // alt screen cursor
1450           this.saveCursor();
1451           break;
1452         case 1049: // alt screen buffer cursor
1453           this.saveCursor();
1454           // FALL-THROUGH
1455         case 47: // alt screen buffer
1456         case 1047: // alt screen buffer
1457           this._bufferService.buffers.activateAltBuffer(this._terminal.eraseAttrData());
1458           this._terminal.refresh(0, this._bufferService.rows - 1);
1459           if (this._terminal.viewport) {
1460             this._terminal.viewport.syncScrollArea();
1461           }
1462           this._terminal.showCursor();
1463           break;
1464         case 2004: // bracketed paste mode (https://cirw.in/blog/bracketed-paste)
1465           this._terminal.bracketedPasteMode = true;
1466           break;
1467       }
1468     }
1469   }
1470
1471
1472   /**
1473    * CSI Pm l  Reset Mode (RM).
1474    *     Ps = 2  -> Keyboard Action Mode (AM).
1475    *     Ps = 4  -> Replace Mode (IRM).
1476    *     Ps = 1 2  -> Send/receive (SRM).
1477    *     Ps = 2 0  -> Normal Linefeed (LNM).
1478    * CSI ? Pm l
1479    *   DEC Private Mode Reset (DECRST).
1480    *     Ps = 1  -> Normal Cursor Keys (DECCKM).
1481    *     Ps = 2  -> Designate VT52 mode (DECANM).
1482    *     Ps = 3  -> 80 Column Mode (DECCOLM).
1483    *     Ps = 4  -> Jump (Fast) Scroll (DECSCLM).
1484    *     Ps = 5  -> Normal Video (DECSCNM).
1485    *     Ps = 6  -> Normal Cursor Mode (DECOM).
1486    *     Ps = 7  -> No Wraparound Mode (DECAWM).
1487    *     Ps = 8  -> No Auto-repeat Keys (DECARM).
1488    *     Ps = 9  -> Don't send Mouse X & Y on button press.
1489    *     Ps = 1 0  -> Hide toolbar (rxvt).
1490    *     Ps = 1 2  -> Stop Blinking Cursor (att610).
1491    *     Ps = 1 8  -> Don't print form feed (DECPFF).
1492    *     Ps = 1 9  -> Limit print to scrolling region (DECPEX).
1493    *     Ps = 2 5  -> Hide Cursor (DECTCEM).
1494    *     Ps = 3 0  -> Don't show scrollbar (rxvt).
1495    *     Ps = 3 5  -> Disable font-shifting functions (rxvt).
1496    *     Ps = 4 0  -> Disallow 80 -> 132 Mode.
1497    *     Ps = 4 1  -> No more(1) fix (see curses resource).
1498    *     Ps = 4 2  -> Disable Nation Replacement Character sets (DEC-
1499    *     NRCM).
1500    *     Ps = 4 4  -> Turn Off Margin Bell.
1501    *     Ps = 4 5  -> No Reverse-wraparound Mode.
1502    *     Ps = 4 6  -> Stop Logging.  (This is normally disabled by a
1503    *     compile-time option).
1504    *     Ps = 4 7  -> Use Normal Screen Buffer.
1505    *     Ps = 6 6  -> Numeric keypad (DECNKM).
1506    *     Ps = 6 7  -> Backarrow key sends delete (DECBKM).
1507    *     Ps = 1 0 0 0  -> Don't send Mouse X & Y on button press and
1508    *     release.  See the section Mouse Tracking.
1509    *     Ps = 1 0 0 1  -> Don't use Hilite Mouse Tracking.
1510    *     Ps = 1 0 0 2  -> Don't use Cell Motion Mouse Tracking.
1511    *     Ps = 1 0 0 3  -> Don't use All Motion Mouse Tracking.
1512    *     Ps = 1 0 0 4  -> Don't send FocusIn/FocusOut events.
1513    *     Ps = 1 0 0 5  -> Disable Extended Mouse Mode.
1514    *     Ps = 1 0 1 0  -> Don't scroll to bottom on tty output
1515    *     (rxvt).
1516    *     Ps = 1 0 1 1  -> Don't scroll to bottom on key press (rxvt).
1517    *     Ps = 1 0 3 4  -> Don't interpret "meta" key.  (This disables
1518    *     the eightBitInput resource).
1519    *     Ps = 1 0 3 5  -> Disable special modifiers for Alt and Num-
1520    *     Lock keys.  (This disables the numLock resource).
1521    *     Ps = 1 0 3 6  -> Don't send ESC  when Meta modifies a key.
1522    *     (This disables the metaSendsEscape resource).
1523    *     Ps = 1 0 3 7  -> Send VT220 Remove from the editing-keypad
1524    *     Delete key.
1525    *     Ps = 1 0 3 9  -> Don't send ESC  when Alt modifies a key.
1526    *     (This disables the altSendsEscape resource).
1527    *     Ps = 1 0 4 0  -> Do not keep selection when not highlighted.
1528    *     (This disables the keepSelection resource).
1529    *     Ps = 1 0 4 1  -> Use the PRIMARY selection.  (This disables
1530    *     the selectToClipboard resource).
1531    *     Ps = 1 0 4 2  -> Disable Urgency window manager hint when
1532    *     Control-G is received.  (This disables the bellIsUrgent
1533    *     resource).
1534    *     Ps = 1 0 4 3  -> Disable raising of the window when Control-
1535    *     G is received.  (This disables the popOnBell resource).
1536    *     Ps = 1 0 4 7  -> Use Normal Screen Buffer, clearing screen
1537    *     first if in the Alternate Screen.  (This may be disabled by
1538    *     the titeInhibit resource).
1539    *     Ps = 1 0 4 8  -> Restore cursor as in DECRC.  (This may be
1540    *     disabled by the titeInhibit resource).
1541    *     Ps = 1 0 4 9  -> Use Normal Screen Buffer and restore cursor
1542    *     as in DECRC.  (This may be disabled by the titeInhibit
1543    *     resource).  This combines the effects of the 1 0 4 7  and 1 0
1544    *     4 8  modes.  Use this with terminfo-based applications rather
1545    *     than the 4 7  mode.
1546    *     Ps = 1 0 5 0  -> Reset terminfo/termcap function-key mode.
1547    *     Ps = 1 0 5 1  -> Reset Sun function-key mode.
1548    *     Ps = 1 0 5 2  -> Reset HP function-key mode.
1549    *     Ps = 1 0 5 3  -> Reset SCO function-key mode.
1550    *     Ps = 1 0 6 0  -> Reset legacy keyboard emulation (X11R6).
1551    *     Ps = 1 0 6 1  -> Reset keyboard emulation to Sun/PC style.
1552    *     Ps = 2 0 0 4  -> Reset bracketed paste mode.
1553    */
1554   public resetMode(params: IParams): void {
1555     for (let i = 0; i < params.length; i++) {
1556       switch (params.params[i]) {
1557         case 4:
1558           this._terminal.insertMode = false;
1559           break;
1560         case 20:
1561           // this._t.convertEol = false;
1562           break;
1563       }
1564     }
1565   }
1566   public resetModePrivate(params: IParams): void {
1567     for (let i = 0; i < params.length; i++) {
1568       switch (params.params[i]) {
1569         case 1:
1570           this._coreService.decPrivateModes.applicationCursorKeys = false;
1571           break;
1572         case 3:
1573           // TODO: move DECCOLM into compat addon
1574           // Note: This impl currently does not enforce col 80, instead reverts
1575           // to previous terminal width before entering DECCOLM 132
1576           if (this._bufferService.cols === 132 && this._terminal.savedCols) {
1577             this._terminal.resize(this._terminal.savedCols, this._bufferService.rows);
1578           }
1579           delete this._terminal.savedCols;
1580           this._terminal.reset();
1581           break;
1582         case 6:
1583           this._terminal.originMode = false;
1584           this._setCursor(0, 0);
1585           break;
1586         case 7:
1587           this._terminal.wraparoundMode = false;
1588           break;
1589         case 12:
1590           // this.cursorBlink = false;
1591           break;
1592         case 66:
1593           this._logService.debug('Switching back to normal keypad.');
1594           this._terminal.applicationKeypad = false;
1595           if (this._terminal.viewport) {
1596             this._terminal.viewport.syncScrollArea();
1597           }
1598           break;
1599         case 9: // X10 Mouse
1600         case 1000: // vt200 mouse
1601         case 1002: // button event mouse
1602         case 1003: // any event mouse
1603           this._coreMouseService.activeProtocol = 'NONE';
1604           break;
1605         case 1004: // send focusin/focusout events
1606           this._terminal.sendFocus = false;
1607           break;
1608         case 1005: // utf8 ext mode mouse - removed in #2507
1609           this._logService.debug('DECRST 1005 not supported (see #2507)');
1610           break;
1611         case 1006: // sgr ext mode mouse
1612           this._coreMouseService.activeEncoding = 'DEFAULT';
1613           break;
1614         case 1015: // urxvt ext mode mouse - removed in #2507
1615         this._logService.debug('DECRST 1015 not supported (see #2507)');
1616           break;
1617         case 25: // hide cursor
1618           this._terminal.cursorHidden = true;
1619           break;
1620         case 1048: // alt screen cursor
1621           this.restoreCursor();
1622           break;
1623         case 1049: // alt screen buffer cursor
1624            // FALL-THROUGH
1625         case 47: // normal screen buffer
1626         case 1047: // normal screen buffer - clearing it first
1627           // Ensure the selection manager has the correct buffer
1628           this._bufferService.buffers.activateNormalBuffer();
1629           if (params.params[i] === 1049) {
1630             this.restoreCursor();
1631           }
1632           this._terminal.refresh(0, this._bufferService.rows - 1);
1633           if (this._terminal.viewport) {
1634             this._terminal.viewport.syncScrollArea();
1635           }
1636           this._terminal.showCursor();
1637           break;
1638         case 2004: // bracketed paste mode (https://cirw.in/blog/bracketed-paste)
1639           this._terminal.bracketedPasteMode = false;
1640           break;
1641       }
1642     }
1643   }
1644
1645   /**
1646    * Helper to extract and apply color params/subparams.
1647    * Returns advance for params index.
1648    */
1649   private _extractColor(params: IParams, pos: number, attr: IAttributeData): number {
1650     // normalize params
1651     // meaning: [target, CM, ign, val, val, val]
1652     // RGB    : [ 38/48,  2, ign,   r,   g,   b]
1653     // P256   : [ 38/48,  5, ign,   v, ign, ign]
1654     const accu = [0, 0, -1, 0, 0, 0];
1655
1656     // alignment placeholder for non color space sequences
1657     let cSpace = 0;
1658
1659     // return advance we took in params
1660     let advance = 0;
1661
1662     do {
1663       accu[advance + cSpace] = params.params[pos + advance];
1664       if (params.hasSubParams(pos + advance)) {
1665         const subparams = params.getSubParams(pos + advance);
1666         let i = 0;
1667         do {
1668           if (accu[1] === 5) {
1669             cSpace = 1;
1670           }
1671           accu[advance + i + 1 + cSpace] = subparams[i];
1672         } while (++i < subparams.length && i + advance + 1 + cSpace < accu.length);
1673         break;
1674       }
1675       // exit early if can decide color mode with semicolons
1676       if ((accu[1] === 5 && advance + cSpace >= 2)
1677           || (accu[1] === 2 && advance + cSpace >= 5)) {
1678         break;
1679       }
1680       // offset colorSpace slot for semicolon mode
1681       if (accu[1]) {
1682         cSpace = 1;
1683       }
1684     } while (++advance + pos < params.length && advance + cSpace < accu.length);
1685
1686     // set default values to 0
1687     for (let i = 2; i < accu.length; ++i) {
1688       if (accu[i] === -1) {
1689         accu[i] = 0;
1690       }
1691     }
1692
1693     // apply colors
1694     if (accu[0] === 38) {
1695       if (accu[1] === 2) {
1696         attr.fg |= Attributes.CM_RGB;
1697         attr.fg &= ~Attributes.RGB_MASK;
1698         attr.fg |= AttributeData.fromColorRGB([accu[3], accu[4], accu[5]]);
1699       } else if (accu[1] === 5) {
1700         attr.fg &= ~(Attributes.CM_MASK | Attributes.PCOLOR_MASK);
1701         attr.fg |= Attributes.CM_P256 | (accu[3] & 0xff);
1702       }
1703     } else if (accu[0] === 48) {
1704       if (accu[1] === 2) {
1705         attr.bg |= Attributes.CM_RGB;
1706         attr.bg &= ~Attributes.RGB_MASK;
1707         attr.bg |= AttributeData.fromColorRGB([accu[3], accu[4], accu[5]]);
1708       } else if (accu[1] === 5) {
1709         attr.bg &= ~(Attributes.CM_MASK | Attributes.PCOLOR_MASK);
1710         attr.bg |= Attributes.CM_P256 | (accu[3] & 0xff);
1711       }
1712     }
1713
1714     return advance;
1715   }
1716
1717   /**
1718    * CSI Pm m  Character Attributes (SGR).
1719    *     Ps = 0  -> Normal (default).
1720    *     Ps = 1  -> Bold.
1721    *     Ps = 2  -> Faint, decreased intensity (ISO 6429).
1722    *     Ps = 4  -> Underlined.
1723    *     Ps = 5  -> Blink (appears as Bold).
1724    *     Ps = 7  -> Inverse.
1725    *     Ps = 8  -> Invisible, i.e., hidden (VT300).
1726    *     Ps = 2 2  -> Normal (neither bold nor faint).
1727    *     Ps = 2 4  -> Not underlined.
1728    *     Ps = 2 5  -> Steady (not blinking).
1729    *     Ps = 2 7  -> Positive (not inverse).
1730    *     Ps = 2 8  -> Visible, i.e., not hidden (VT300).
1731    *     Ps = 3 0  -> Set foreground color to Black.
1732    *     Ps = 3 1  -> Set foreground color to Red.
1733    *     Ps = 3 2  -> Set foreground color to Green.
1734    *     Ps = 3 3  -> Set foreground color to Yellow.
1735    *     Ps = 3 4  -> Set foreground color to Blue.
1736    *     Ps = 3 5  -> Set foreground color to Magenta.
1737    *     Ps = 3 6  -> Set foreground color to Cyan.
1738    *     Ps = 3 7  -> Set foreground color to White.
1739    *     Ps = 3 9  -> Set foreground color to default (original).
1740    *     Ps = 4 0  -> Set background color to Black.
1741    *     Ps = 4 1  -> Set background color to Red.
1742    *     Ps = 4 2  -> Set background color to Green.
1743    *     Ps = 4 3  -> Set background color to Yellow.
1744    *     Ps = 4 4  -> Set background color to Blue.
1745    *     Ps = 4 5  -> Set background color to Magenta.
1746    *     Ps = 4 6  -> Set background color to Cyan.
1747    *     Ps = 4 7  -> Set background color to White.
1748    *     Ps = 4 9  -> Set background color to default (original).
1749    *
1750    *   If 16-color support is compiled, the following apply.  Assume
1751    *   that xterm's resources are set so that the ISO color codes are
1752    *   the first 8 of a set of 16.  Then the aixterm colors are the
1753    *   bright versions of the ISO colors:
1754    *     Ps = 9 0  -> Set foreground color to Black.
1755    *     Ps = 9 1  -> Set foreground color to Red.
1756    *     Ps = 9 2  -> Set foreground color to Green.
1757    *     Ps = 9 3  -> Set foreground color to Yellow.
1758    *     Ps = 9 4  -> Set foreground color to Blue.
1759    *     Ps = 9 5  -> Set foreground color to Magenta.
1760    *     Ps = 9 6  -> Set foreground color to Cyan.
1761    *     Ps = 9 7  -> Set foreground color to White.
1762    *     Ps = 1 0 0  -> Set background color to Black.
1763    *     Ps = 1 0 1  -> Set background color to Red.
1764    *     Ps = 1 0 2  -> Set background color to Green.
1765    *     Ps = 1 0 3  -> Set background color to Yellow.
1766    *     Ps = 1 0 4  -> Set background color to Blue.
1767    *     Ps = 1 0 5  -> Set background color to Magenta.
1768    *     Ps = 1 0 6  -> Set background color to Cyan.
1769    *     Ps = 1 0 7  -> Set background color to White.
1770    *
1771    *   If xterm is compiled with the 16-color support disabled, it
1772    *   supports the following, from rxvt:
1773    *     Ps = 1 0 0  -> Set foreground and background color to
1774    *     default.
1775    *
1776    *   If 88- or 256-color support is compiled, the following apply.
1777    *     Ps = 3 8  ; 5  ; Ps -> Set foreground color to the second
1778    *     Ps.
1779    *     Ps = 4 8  ; 5  ; Ps -> Set background color to the second
1780    *     Ps.
1781    */
1782   public charAttributes(params: IParams): void {
1783     // Optimize a single SGR0.
1784     if (params.length === 1 && params.params[0] === 0) {
1785       this._terminal.curAttrData.fg = DEFAULT_ATTR_DATA.fg;
1786       this._terminal.curAttrData.bg = DEFAULT_ATTR_DATA.bg;
1787       return;
1788     }
1789
1790     const l = params.length;
1791     let p;
1792     const attr = this._terminal.curAttrData;
1793
1794     for (let i = 0; i < l; i++) {
1795       p = params.params[i];
1796       if (p >= 30 && p <= 37) {
1797         // fg color 8
1798         attr.fg &= ~(Attributes.CM_MASK | Attributes.PCOLOR_MASK);
1799         attr.fg |= Attributes.CM_P16 | (p - 30);
1800       } else if (p >= 40 && p <= 47) {
1801         // bg color 8
1802         attr.bg &= ~(Attributes.CM_MASK | Attributes.PCOLOR_MASK);
1803         attr.bg |= Attributes.CM_P16 | (p - 40);
1804       } else if (p >= 90 && p <= 97) {
1805         // fg color 16
1806         attr.fg &= ~(Attributes.CM_MASK | Attributes.PCOLOR_MASK);
1807         attr.fg |= Attributes.CM_P16 | (p - 90) | 8;
1808       } else if (p >= 100 && p <= 107) {
1809         // bg color 16
1810         attr.bg &= ~(Attributes.CM_MASK | Attributes.PCOLOR_MASK);
1811         attr.bg |= Attributes.CM_P16 | (p - 100) | 8;
1812       } else if (p === 0) {
1813         // default
1814         attr.fg = DEFAULT_ATTR_DATA.fg;
1815         attr.bg = DEFAULT_ATTR_DATA.bg;
1816       } else if (p === 1) {
1817         // bold text
1818         attr.fg |= FgFlags.BOLD;
1819       } else if (p === 3) {
1820         // italic text
1821         attr.bg |= BgFlags.ITALIC;
1822       } else if (p === 4) {
1823         // underlined text
1824         attr.fg |= FgFlags.UNDERLINE;
1825       } else if (p === 5) {
1826         // blink
1827         attr.fg |= FgFlags.BLINK;
1828       } else if (p === 7) {
1829         // inverse and positive
1830         // test with: echo -e '\e[31m\e[42mhello\e[7mworld\e[27mhi\e[m'
1831         attr.fg |= FgFlags.INVERSE;
1832       } else if (p === 8) {
1833         // invisible
1834         attr.fg |= FgFlags.INVISIBLE;
1835       } else if (p === 2) {
1836         // dimmed text
1837         attr.bg |= BgFlags.DIM;
1838       } else if (p === 22) {
1839         // not bold nor faint
1840         attr.fg &= ~FgFlags.BOLD;
1841         attr.bg &= ~BgFlags.DIM;
1842       } else if (p === 23) {
1843         // not italic
1844         attr.bg &= ~BgFlags.ITALIC;
1845       } else if (p === 24) {
1846         // not underlined
1847         attr.fg &= ~FgFlags.UNDERLINE;
1848       } else if (p === 25) {
1849         // not blink
1850         attr.fg &= ~FgFlags.BLINK;
1851       } else if (p === 27) {
1852         // not inverse
1853         attr.fg &= ~FgFlags.INVERSE;
1854       } else if (p === 28) {
1855         // not invisible
1856         attr.fg &= ~FgFlags.INVISIBLE;
1857       } else if (p === 39) {
1858         // reset fg
1859         attr.fg &= ~(Attributes.CM_MASK | Attributes.RGB_MASK);
1860         attr.fg |= DEFAULT_ATTR_DATA.fg & (Attributes.PCOLOR_MASK | Attributes.RGB_MASK);
1861       } else if (p === 49) {
1862         // reset bg
1863         attr.bg &= ~(Attributes.CM_MASK | Attributes.RGB_MASK);
1864         attr.bg |= DEFAULT_ATTR_DATA.bg & (Attributes.PCOLOR_MASK | Attributes.RGB_MASK);
1865       } else if (p === 38 || p === 48) {
1866         // fg color 256 and RGB
1867         i += this._extractColor(params, i, attr);
1868       } else if (p === 100) {
1869         // reset fg/bg
1870         attr.fg &= ~(Attributes.CM_MASK | Attributes.RGB_MASK);
1871         attr.fg |= DEFAULT_ATTR_DATA.fg & (Attributes.PCOLOR_MASK | Attributes.RGB_MASK);
1872         attr.bg &= ~(Attributes.CM_MASK | Attributes.RGB_MASK);
1873         attr.bg |= DEFAULT_ATTR_DATA.bg & (Attributes.PCOLOR_MASK | Attributes.RGB_MASK);
1874       } else {
1875         this._logService.debug('Unknown SGR attribute: %d.', p);
1876       }
1877     }
1878   }
1879
1880   /**
1881    * CSI Ps n  Device Status Report (DSR).
1882    *     Ps = 5  -> Status Report.  Result (``OK'') is
1883    *   CSI 0 n
1884    *     Ps = 6  -> Report Cursor Position (CPR) [row;column].
1885    *   Result is
1886    *   CSI r ; c R
1887    * CSI ? Ps n
1888    *   Device Status Report (DSR, DEC-specific).
1889    *     Ps = 6  -> Report Cursor Position (CPR) [row;column] as CSI
1890    *     ? r ; c R (assumes page is zero).
1891    *     Ps = 1 5  -> Report Printer status as CSI ? 1 0  n  (ready).
1892    *     or CSI ? 1 1  n  (not ready).
1893    *     Ps = 2 5  -> Report UDK status as CSI ? 2 0  n  (unlocked)
1894    *     or CSI ? 2 1  n  (locked).
1895    *     Ps = 2 6  -> Report Keyboard status as
1896    *   CSI ? 2 7  ;  1  ;  0  ;  0  n  (North American).
1897    *   The last two parameters apply to VT400 & up, and denote key-
1898    *   board ready and LK01 respectively.
1899    *     Ps = 5 3  -> Report Locator status as
1900    *   CSI ? 5 3  n  Locator available, if compiled-in, or
1901    *   CSI ? 5 0  n  No Locator, if not.
1902    */
1903   public deviceStatus(params: IParams): void {
1904     switch (params.params[0]) {
1905       case 5:
1906         // status report
1907         this._coreService.triggerDataEvent(`${C0.ESC}[0n`);
1908         break;
1909       case 6:
1910         // cursor position
1911         const y = this._bufferService.buffer.y + 1;
1912         const x = this._bufferService.buffer.x + 1;
1913         this._coreService.triggerDataEvent(`${C0.ESC}[${y};${x}R`);
1914         break;
1915     }
1916   }
1917
1918   public deviceStatusPrivate(params: IParams): void {
1919     // modern xterm doesnt seem to
1920     // respond to any of these except ?6, 6, and 5
1921     switch (params.params[0]) {
1922       case 6:
1923         // cursor position
1924         const y = this._bufferService.buffer.y + 1;
1925         const x = this._bufferService.buffer.x + 1;
1926         this._coreService.triggerDataEvent(`${C0.ESC}[?${y};${x}R`);
1927         break;
1928       case 15:
1929         // no printer
1930         // this.handler(C0.ESC + '[?11n');
1931         break;
1932       case 25:
1933         // dont support user defined keys
1934         // this.handler(C0.ESC + '[?21n');
1935         break;
1936       case 26:
1937         // north american keyboard
1938         // this.handler(C0.ESC + '[?27;1;0;0n');
1939         break;
1940       case 53:
1941         // no dec locator/mouse
1942         // this.handler(C0.ESC + '[?50n');
1943         break;
1944     }
1945   }
1946
1947   /**
1948    * CSI ! p   Soft terminal reset (DECSTR).
1949    * http://vt100.net/docs/vt220-rm/table4-10.html
1950    */
1951   public softReset(params: IParams): void {
1952     this._terminal.cursorHidden = false;
1953     this._terminal.insertMode = false;
1954     this._terminal.originMode = false;
1955     this._terminal.wraparoundMode = true;  // defaults: xterm - true, vt100 - false
1956     this._terminal.applicationKeypad = false; // ?
1957     if (this._terminal.viewport) {
1958       this._terminal.viewport.syncScrollArea();
1959     }
1960     this._coreService.decPrivateModes.applicationCursorKeys = false;
1961     this._bufferService.buffer.scrollTop = 0;
1962     this._bufferService.buffer.scrollBottom = this._bufferService.rows - 1;
1963     this._terminal.curAttrData = DEFAULT_ATTR_DATA.clone();
1964     this._bufferService.buffer.x = this._bufferService.buffer.y = 0; // ?
1965     this._terminal.charset = null;
1966     this._terminal.glevel = 0; // ??
1967     this._terminal.charsets = [null]; // ??
1968   }
1969
1970   /**
1971    * CSI Ps SP q  Set cursor style (DECSCUSR, VT520).
1972    *   Ps = 0  -> blinking block.
1973    *   Ps = 1  -> blinking block (default).
1974    *   Ps = 2  -> steady block.
1975    *   Ps = 3  -> blinking underline.
1976    *   Ps = 4  -> steady underline.
1977    *   Ps = 5  -> blinking bar (xterm).
1978    *   Ps = 6  -> steady bar (xterm).
1979    */
1980   public setCursorStyle(params: IParams): void {
1981     const param = params.params[0] || 1;
1982     switch (param) {
1983       case 1:
1984       case 2:
1985         this._optionsService.options.cursorStyle = 'block';
1986         break;
1987       case 3:
1988       case 4:
1989         this._optionsService.options.cursorStyle = 'underline';
1990         break;
1991       case 5:
1992       case 6:
1993         this._optionsService.options.cursorStyle = 'bar';
1994         break;
1995     }
1996     const isBlinking = param % 2 === 1;
1997     this._optionsService.options.cursorBlink = isBlinking;
1998   }
1999
2000   /**
2001    * CSI Ps ; Ps r
2002    *   Set Scrolling Region [top;bottom] (default = full size of win-
2003    *   dow) (DECSTBM).
2004    */
2005   public setScrollRegion(params: IParams): void {
2006     const top = params.params[0] || 1;
2007     let bottom: number;
2008
2009     if (params.length < 2 || (bottom = params.params[1]) >  this._bufferService.rows || bottom === 0) {
2010       bottom = this._bufferService.rows;
2011     }
2012
2013     if (bottom > top) {
2014       this._bufferService.buffer.scrollTop = top - 1;
2015       this._bufferService.buffer.scrollBottom = bottom - 1;
2016       this._setCursor(0, 0);
2017     }
2018   }
2019
2020
2021   /**
2022    * CSI s
2023    * ESC 7
2024    *   Save cursor (ANSI.SYS).
2025    */
2026   public saveCursor(params?: IParams): void {
2027     this._bufferService.buffer.savedX = this._bufferService.buffer.x;
2028     this._bufferService.buffer.savedY = this._bufferService.buffer.ybase + this._bufferService.buffer.y;
2029     this._bufferService.buffer.savedCurAttrData.fg = this._terminal.curAttrData.fg;
2030     this._bufferService.buffer.savedCurAttrData.bg = this._terminal.curAttrData.bg;
2031     this._bufferService.buffer.savedCharset = this._terminal.charset;
2032   }
2033
2034
2035   /**
2036    * CSI u
2037    * ESC 8
2038    *   Restore cursor (ANSI.SYS).
2039    */
2040   public restoreCursor(params?: IParams): void {
2041     this._bufferService.buffer.x = this._bufferService.buffer.savedX || 0;
2042     this._bufferService.buffer.y = Math.max(this._bufferService.buffer.savedY - this._bufferService.buffer.ybase, 0);
2043     this._terminal.curAttrData.fg = this._bufferService.buffer.savedCurAttrData.fg;
2044     this._terminal.curAttrData.bg = this._bufferService.buffer.savedCurAttrData.bg;
2045     this._terminal.charset = (this as any)._savedCharset;
2046     if (this._bufferService.buffer.savedCharset) {
2047       this._terminal.charset = this._bufferService.buffer.savedCharset;
2048     }
2049     this._restrictCursor();
2050   }
2051
2052
2053   /**
2054    * OSC 0; <data> ST (set icon name + window title)
2055    * OSC 2; <data> ST (set window title)
2056    *   Proxy to set window title. Icon name is not supported.
2057    */
2058   public setTitle(data: string): void {
2059     this._terminal.handleTitle(data);
2060   }
2061
2062   /**
2063    * ESC E
2064    * C1.NEL
2065    *   DEC mnemonic: NEL (https://vt100.net/docs/vt510-rm/NEL)
2066    *   Moves cursor to first position on next line.
2067    */
2068   public nextLine(): void {
2069     this._bufferService.buffer.x = 0;
2070     this.index();
2071   }
2072
2073   /**
2074    * ESC =
2075    *   DEC mnemonic: DECKPAM (https://vt100.net/docs/vt510-rm/DECKPAM.html)
2076    *   Enables the numeric keypad to send application sequences to the host.
2077    */
2078   public keypadApplicationMode(): void {
2079     this._logService.debug('Serial port requested application keypad.');
2080     this._terminal.applicationKeypad = true;
2081     if (this._terminal.viewport) {
2082       this._terminal.viewport.syncScrollArea();
2083     }
2084   }
2085
2086   /**
2087    * ESC >
2088    *   DEC mnemonic: DECKPNM (https://vt100.net/docs/vt510-rm/DECKPNM.html)
2089    *   Enables the keypad to send numeric characters to the host.
2090    */
2091   public keypadNumericMode(): void {
2092     this._logService.debug('Switching back to normal keypad.');
2093     this._terminal.applicationKeypad = false;
2094     if (this._terminal.viewport) {
2095       this._terminal.viewport.syncScrollArea();
2096     }
2097   }
2098
2099   /**
2100    * ESC % @
2101    * ESC % G
2102    *   Select default character set. UTF-8 is not supported (string are unicode anyways)
2103    *   therefore ESC % G does the same.
2104    */
2105   public selectDefaultCharset(): void {
2106     this._terminal.setgLevel(0);
2107     this._terminal.setgCharset(0, DEFAULT_CHARSET); // US (default)
2108   }
2109
2110   /**
2111    * ESC ( C
2112    *   Designate G0 Character Set, VT100, ISO 2022.
2113    * ESC ) C
2114    *   Designate G1 Character Set (ISO 2022, VT100).
2115    * ESC * C
2116    *   Designate G2 Character Set (ISO 2022, VT220).
2117    * ESC + C
2118    *   Designate G3 Character Set (ISO 2022, VT220).
2119    * ESC - C
2120    *   Designate G1 Character Set (VT300).
2121    * ESC . C
2122    *   Designate G2 Character Set (VT300).
2123    * ESC / C
2124    *   Designate G3 Character Set (VT300). C = A  -> ISO Latin-1 Supplemental. - Supported?
2125    */
2126   public selectCharset(collectAndFlag: string): void {
2127     if (collectAndFlag.length !== 2) {
2128       this.selectDefaultCharset();
2129       return;
2130     }
2131     if (collectAndFlag[0] === '/') {
2132       return;  // TODO: Is this supported?
2133     }
2134     this._terminal.setgCharset(GLEVEL[collectAndFlag[0]], CHARSETS[collectAndFlag[1]] || DEFAULT_CHARSET);
2135     return;
2136   }
2137
2138   /**
2139    * ESC D
2140    * C1.IND
2141    *   DEC mnemonic: IND (https://vt100.net/docs/vt510-rm/IND.html)
2142    *   Moves the cursor down one line in the same column.
2143    */
2144   public index(): void {
2145     this._restrictCursor();
2146     const buffer = this._bufferService.buffer;
2147     this._bufferService.buffer.y++;
2148     if (buffer.y === buffer.scrollBottom + 1) {
2149       buffer.y--;
2150       this._terminal.scroll();
2151     } else if (buffer.y >= this._bufferService.rows) {
2152       buffer.y = this._bufferService.rows - 1;
2153     }
2154     this._restrictCursor();
2155   }
2156
2157   /**
2158    * ESC H
2159    * C1.HTS
2160    *   DEC mnemonic: HTS (https://vt100.net/docs/vt510-rm/HTS.html)
2161    *   Sets a horizontal tab stop at the column position indicated by
2162    *   the value of the active column when the terminal receives an HTS.
2163    */
2164   public tabSet(): void {
2165     this._bufferService.buffer.tabs[this._bufferService.buffer.x] = true;
2166   }
2167
2168   /**
2169    * ESC M
2170    * C1.RI
2171    *   DEC mnemonic: HTS
2172    *   Moves the cursor up one line in the same column. If the cursor is at the top margin,
2173    *   the page scrolls down.
2174    */
2175   public reverseIndex(): void {
2176     this._restrictCursor();
2177     const buffer = this._bufferService.buffer;
2178     if (buffer.y === buffer.scrollTop) {
2179       // possibly move the code below to term.reverseScroll();
2180       // test: echo -ne '\e[1;1H\e[44m\eM\e[0m'
2181       // blankLine(true) is xterm/linux behavior
2182       const scrollRegionHeight = buffer.scrollBottom - buffer.scrollTop;
2183       buffer.lines.shiftElements(buffer.y + buffer.ybase, scrollRegionHeight, 1);
2184       buffer.lines.set(buffer.y + buffer.ybase, buffer.getBlankLine(this._terminal.eraseAttrData()));
2185       this._dirtyRowService.markRangeDirty(buffer.scrollTop, buffer.scrollBottom);
2186     } else {
2187       buffer.y--;
2188       this._restrictCursor(); // quickfix to not run out of bounds
2189     }
2190   }
2191
2192   /**
2193    * ESC c
2194    *   DEC mnemonic: RIS (https://vt100.net/docs/vt510-rm/RIS.html)
2195    *   Reset to initial state.
2196    */
2197   public reset(): void {
2198     this._parser.reset();
2199     this._terminal.reset();  // TODO: save to move from terminal?
2200   }
2201
2202   /**
2203    * ESC n
2204    * ESC o
2205    * ESC |
2206    * ESC }
2207    * ESC ~
2208    *   DEC mnemonic: LS (https://vt100.net/docs/vt510-rm/LS.html)
2209    *   When you use a locking shift, the character set remains in GL or GR until
2210    *   you use another locking shift. (partly supported)
2211    */
2212   public setgLevel(level: number): void {
2213     this._terminal.setgLevel(level);  // TODO: save to move from terminal?
2214   }
2215
2216   /**
2217    * ESC # 8
2218    *   DEC mnemonic: DECALN (https://vt100.net/docs/vt510-rm/DECALN.html)
2219    *   This control function fills the complete screen area with
2220    *   a test pattern (E) used for adjusting screen alignment.
2221    *
2222    * TODO: move DECALN into compat addon
2223    */
2224   public screenAlignmentPattern(): void {
2225     // prepare cell data
2226     const cell = new CellData();
2227     cell.content = 1 << Content.WIDTH_SHIFT | 'E'.charCodeAt(0);
2228     cell.fg = this._terminal.curAttrData.fg;
2229     cell.bg = this._terminal.curAttrData.bg;
2230
2231     const buffer = this._bufferService.buffer;
2232
2233     this._setCursor(0, 0);
2234     for (let yOffset = 0; yOffset < this._bufferService.rows; ++yOffset) {
2235       const row = buffer.y + buffer.ybase + yOffset;
2236       buffer.lines.get(row).fill(cell);
2237       buffer.lines.get(row).isWrapped = false;
2238     }
2239     this._dirtyRowService.markAllDirty();
2240     this._setCursor(0, 0);
2241   }
2242 }