2 * Copyright (c) 2014 The xterm.js authors. All rights reserved.
3 * Copyright (c) 2012-2013, Christopher Jeffrey (MIT License)
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';
27 * Map collect to glevel. Used in `selectCharset`.
29 const GLEVEL: {[key: string]: number} = {'(': 0, ')': 1, '*': 2, '+': 3, '-': 1, '.': 2};
32 * Max length of the UTF32 input buffer. Real memory consumption is 4 times higher.
34 const MAX_PARSEBUFFER_LENGTH = 131072;
38 * DCS subparser implementations
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)
47 class DECRQSS implements IDcsHandler {
48 private _data: Uint32Array = new Uint32Array(0);
51 private _bufferService: IBufferService,
52 private _coreService: ICoreService,
53 private _logService: ILogService,
54 private _optionsService: IOptionsService
57 hook(params: IParams): void {
58 this._data = new Uint32Array(0);
61 put(data: Uint32Array, start: number, end: number): void {
62 this._data = concat(this._data, data.subarray(start, end));
65 unhook(success: boolean): void {
67 this._data = new Uint32Array(0);
70 const data = utf32ToString(this._data);
71 this._data = new Uint32Array(0);
73 // valid: DCS 1 $ r Pt ST (xterm)
75 return this._coreService.triggerDataEvent(`${C0.ESC}P1$r0"q${C0.ESC}\\`);
77 return this._coreService.triggerDataEvent(`${C0.ESC}P1$r61"p${C0.ESC}\\`);
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}\\`);
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}\\`);
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}\\`);
100 * DECUDK (https://vt100.net/docs/vt510-rm/DECUDK.html)
105 * DCS + q Pt ST (xterm)
106 * Request Terminfo String
111 * DCS + p Pt ST (xterm)
119 * The terminal's standard implementation of IInputHandler, this handles all
120 * input from the Parser.
122 * Refer to http://invisible-island.net/xterm/ctlseqs/ctlseqs.html to understand
123 * each function's header comment.
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();
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; }
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())
150 this.register(this._parser);
153 * custom fallback handlers
155 this._parser.setCsiHandlerFallback((ident, params) => {
156 this._logService.debug('Unknown CSI code: ', { identifier: this._parser.identToString(ident), params: params.toArray() });
158 this._parser.setEscHandlerFallback(ident => {
159 this._logService.debug('Unknown ESC code: ', { identifier: this._parser.identToString(ident) });
161 this._parser.setExecuteHandlerFallback(code => {
162 this._logService.debug('Unknown EXECUTE code: ', { code });
164 this._parser.setOscHandlerFallback((identifier, action, data) => {
165 this._logService.debug('Unknown OSC code: ', { identifier, action, data });
167 this._parser.setDcsHandlerFallback((ident, action, payload) => {
168 if (action === 'HOOK') {
169 payload = payload.toArray();
171 this._logService.debug('Unknown DCS code: ', { identifier: this._parser.identToString(ident), action, payload });
177 this._parser.setPrintHandler((data, start, end) => this.print(data, start, end));
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));
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.
243 this._parser.setExecuteHandler(C1.IND, () => this.index());
244 this._parser.setExecuteHandler(C1.NEL, () => this.nextLine());
245 this._parser.setExecuteHandler(C1.HTS, () => this.tabSet());
250 // 0 - icon name + title
251 this._parser.setOscHandler(0, new OscHandler((data: string) => this.setTitle(data)));
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.
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?
316 this._parser.setEscHandler({intermediates: '#', final: '8'}, () => this.screenAlignmentPattern());
321 this._parser.setErrorHandler((state: IParsingState) => {
322 this._logService.error('Parsing error: ', state);
329 this._parser.setDcsHandler({intermediates: '$', final: 'q'}, new DECRQSS(this._bufferService, this._coreService, this._logService, this._optionsService));
332 public dispose(): void {
336 public parse(data: string | Uint8Array): void {
337 let buffer = this._bufferService.buffer;
338 const cursorStartX = buffer.x;
339 const cursorStartY = buffer.y;
341 this._logService.debug('parsing data', data);
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));
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);
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);
366 buffer = this._bufferService.buffer;
367 if (buffer.x !== cursorStartX || buffer.y !== cursorStartY) {
368 this._onCursorMove.fire();
370 this._terminal.refresh(this._dirtyRowService.start, this._dirtyRowService.end);
373 public print(data: Uint32Array, start: number, end: number): void {
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);
385 this._dirtyRowService.markDirty(buffer.y);
386 for (let pos = start; pos < end; ++pos) {
389 // calculate print space
390 // expensive call, therefore we save width in line buffer
391 chWidth = wcwidth(code);
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)];
399 code = ch.charCodeAt(0);
403 if (screenReaderMode) {
404 this._terminal.onA11yCharEmitter.fire(stringFromCodePoint(code));
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);
419 bufferRow.addCodepointToCell(buffer.x - 1, code);
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) {
431 // automatically wraps to the beginning of the next line
432 if (wraparoundMode) {
435 if (buffer.y === buffer.scrollBottom + 1) {
437 this._terminal.scroll(true);
439 if (buffer.y >= this._bufferService.rows) {
440 buffer.y = this._bufferService.rows - 1;
442 // The line already exists (eg. the initial viewport), mark it as a
444 buffer.lines.get(buffer.y).isWrapped = true;
446 // row changed, get it again
447 bufferRow = buffer.lines.get(buffer.y + buffer.ybase);
451 // FIXME: check for xterm behavior
452 // What to do here? We got a wide char that does not fit into last cell
458 // insert mode: move characters to right
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);
470 // write current char to buffer and advance cursor
471 bufferRow.setCellFromCodePoint(buffer.x++, code, chWidth, curAttr.fg, curAttr.bg);
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
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);
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?)
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);
494 this._parser.precedingCodepoint = this._workCell.content;
497 this._dirtyRowService.markDirty(buffer.y);
501 * Forward addCsiHandler from parser.
503 public addCsiHandler(id: IFunctionIdentifier, callback: (params: IParams) => boolean): IDisposable {
504 return this._parser.addCsiHandler(id, callback);
508 * Forward addDcsHandler from parser.
510 public addDcsHandler(id: IFunctionIdentifier, callback: (data: string, param: IParams) => boolean): IDisposable {
511 return this._parser.addDcsHandler(id, new DcsHandler(callback));
515 * Forward addEscHandler from parser.
517 public addEscHandler(id: IFunctionIdentifier, callback: () => boolean): IDisposable {
518 return this._parser.addEscHandler(id, callback);
522 * Forward addOscHandler from parser.
524 public addOscHandler(ident: number, callback: (data: string) => boolean): IDisposable {
525 return this._parser.addOscHandler(ident, new OscHandler(callback));
532 public bell(): void {
533 this._terminal.bell();
538 * Line Feed or New Line (NL). (LF is Ctrl-J).
540 public lineFeed(): void {
541 // make buffer local for faster access
542 const buffer = this._bufferService.buffer;
544 if (this._optionsService.options.convertEol) {
548 if (buffer.y === buffer.scrollBottom + 1) {
550 this._terminal.scroll();
551 } else if (buffer.y >= this._bufferService.rows) {
552 buffer.y = this._bufferService.rows - 1;
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) {
559 this._onLineFeed.fire();
564 * Carriage Return (Ctrl-M).
566 public carriageReturn(): void {
567 this._bufferService.buffer.x = 0;
572 * Backspace (Ctrl-H).
574 public backspace(): void {
575 this._restrictCursor();
576 if (this._bufferService.buffer.x > 0) {
577 this._bufferService.buffer.x--;
583 * Horizontal Tab (HT) (Ctrl-I).
586 if (this._bufferService.buffer.x >= this._bufferService.cols) {
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);
598 * Shift Out (Ctrl-N) -> Switch to Alternate Character Set. This invokes the
601 public shiftOut(): void {
602 this._terminal.setgLevel(1);
607 * Shift In (Ctrl-O) -> Switch to Standard Character Set. This invokes the G0
608 * character set (the default).
610 public shiftIn(): void {
611 this._terminal.setgLevel(0);
615 * Restrict cursor to viewport size / scroll margin (origin mode).
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));
625 * Set absolute cursor position.
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;
632 this._bufferService.buffer.x = x;
633 this._bufferService.buffer.y = y;
635 this._restrictCursor();
639 * Set relative cursor position.
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);
650 * Cursor Up Ps Times (default = 1) (CUU).
652 public cursorUp(params: IParams): void {
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));
658 this._moveCursor(0, -(params.params[0] || 1));
664 * Cursor Down Ps Times (default = 1) (CUD).
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));
672 this._moveCursor(0, params.params[0] || 1);
678 * Cursor Forward Ps Times (default = 1) (CUF).
680 public cursorForward(params: IParams): void {
681 this._moveCursor(params.params[0] || 1, 0);
686 * Cursor Backward Ps Times (default = 1) (CUB).
688 public cursorBackward(params: IParams): void {
689 this._moveCursor(-(params.params[0] || 1), 0);
694 * Cursor Next Line Ps Times (default = 1) (CNL).
695 * Other than cursorDown (CUD) also set the cursor to first column.
697 public cursorNextLine(params: IParams): void {
698 this.cursorDown(params);
699 this._bufferService.buffer.x = 0;
704 * Cursor Previous Line Ps Times (default = 1) (CPL).
705 * Other than cursorUp (CUU) also set the cursor to first column.
707 public cursorPrecedingLine(params: IParams): void {
708 this.cursorUp(params);
709 this._bufferService.buffer.x = 0;
714 * Cursor Character Absolute [column] (default = [row,1]) (CHA).
716 public cursorCharAbsolute(params: IParams): void {
717 this._setCursor((params.params[0] || 1) - 1, this._bufferService.buffer.y);
722 * Cursor Position [row;column] (default = [1,1]) (CUP).
724 public cursorPosition(params: IParams): void {
727 (params.length >= 2) ? (params.params[1] || 1) - 1 : 0,
729 (params.params[0] || 1) - 1);
733 * CSI Pm ` Character Position Absolute
734 * [column] (default = [row,1]) (HPA).
735 * Currently same functionality as CHA.
737 public charPosAbsolute(params: IParams): void {
738 this._setCursor((params.params[0] || 1) - 1, this._bufferService.buffer.y);
742 * CSI Pm a Character Position Relative
743 * [columns] (default = [row,col+1]) (HPR)
744 * Currently same functionality as CUF.
746 public hPositionRelative(params: IParams): void {
747 this._moveCursor(params.params[0] || 1, 0);
751 * CSI Pm d Vertical Position Absolute (VPA)
752 * [row] (default = [1,column])
754 public linePosAbsolute(params: IParams): void {
755 this._setCursor(this._bufferService.buffer.x, (params.params[0] || 1) - 1);
759 * CSI Pm e Vertical Position Relative (VPR)
760 * [rows] (default = [row+1,column])
763 public vPositionRelative(params: IParams): void {
764 this._moveCursor(0, params.params[0] || 1);
769 * Horizontal and Vertical Position [row;column] (default =
773 public hVPosition(params: IParams): void {
774 this.cursorPosition(params);
778 * CSI Ps g Tab Clear (TBC).
779 * Ps = 0 -> Clear Current Column (default).
780 * Ps = 3 -> Clear All.
782 * Ps = 2 -> Clear Stops on Line.
783 * http://vt100.net/annarbor/aaa-ug/section6.html
785 public tabClear(params: IParams): void {
786 const param = params.params[0];
788 delete this._bufferService.buffer.tabs[this._bufferService.buffer.x];
789 } else if (param === 3) {
790 this._bufferService.buffer.tabs = {};
796 * Cursor Forward Tabulation Ps tab stops (default = 1) (CHT).
798 public cursorForwardTab(params: IParams): void {
799 if (this._bufferService.buffer.x >= this._bufferService.cols) {
802 let param = params.params[0] || 1;
804 this._bufferService.buffer.x = this._bufferService.buffer.nextStop();
809 * CSI Ps Z Cursor Backward Tabulation Ps tab stops (default = 1) (CBT).
811 public cursorBackwardTab(params: IParams): void {
812 if (this._bufferService.buffer.x >= this._bufferService.cols) {
815 let param = params.params[0] || 1;
817 // make buffer local for faster access
818 const buffer = this._bufferService.buffer;
821 buffer.x = buffer.prevStop();
827 * Helper method to erase cells in a terminal row.
828 * The cell gets replaced with the eraseChar of the terminal.
830 * @param start first cell index to be erased
831 * @param end end - 1 is last erased cell
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);
838 this._bufferService.buffer.getNullCell(this._terminal.eraseAttrData())
841 line.isWrapped = false;
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.
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;
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).
863 * Erase in Display (DECSED).
864 * Ps = 0 -> Selective Erase Below (default).
865 * Ps = 1 -> Selective Erase Above.
866 * Ps = 2 -> Selective Erase All.
868 public eraseInDisplay(params: IParams): void {
869 this._restrictCursor();
871 switch (params.params[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);
879 this._dirtyRowService.markDirty(j);
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;
891 this._resetBufferLine(j);
893 this._dirtyRowService.markDirty(0);
896 j = this._bufferService.rows;
897 this._dirtyRowService.markDirty(j - 1);
899 this._resetBufferLine(j);
901 this._dirtyRowService.markDirty(0);
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);
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.
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.
928 public eraseInLine(params: IParams): void {
929 this._restrictCursor();
930 switch (params.params[0]) {
932 this._eraseInBufferLine(this._bufferService.buffer.y, this._bufferService.buffer.x, this._bufferService.cols);
935 this._eraseInBufferLine(this._bufferService.buffer.y, 0, this._bufferService.buffer.x + 1);
938 this._eraseInBufferLine(this._bufferService.buffer.y, 0, this._bufferService.cols);
941 this._dirtyRowService.markDirty(this._bufferService.buffer.y);
946 * Insert Ps Line(s) (default = 1) (IL).
948 public insertLines(params: IParams): void {
949 this._restrictCursor();
950 let param = params.params[0] || 1;
952 // make buffer local for faster access
953 const buffer = this._bufferService.buffer;
955 if (buffer.y > buffer.scrollBottom || buffer.y < buffer.scrollTop) {
959 const row: number = buffer.y + buffer.ybase;
961 const scrollBottomRowsOffset = this._bufferService.rows - 1 - buffer.scrollBottom;
962 const scrollBottomAbsolute = this._bufferService.rows - 1 + buffer.ybase - scrollBottomRowsOffset + 1;
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()));
970 this._dirtyRowService.markRangeDirty(buffer.y, buffer.scrollBottom);
971 buffer.x = 0; // see https://vt100.net/docs/vt220-rm/chapter4.html - vt220 only?
976 * Delete Ps Line(s) (default = 1) (DL).
978 public deleteLines(params: IParams): void {
979 this._restrictCursor();
980 let param = params.params[0] || 1;
982 // make buffer local for faster access
983 const buffer = this._bufferService.buffer;
985 if (buffer.y > buffer.scrollBottom || buffer.y < buffer.scrollTop) {
989 const row: number = buffer.y + buffer.ybase;
992 j = this._bufferService.rows - 1 - buffer.scrollBottom;
993 j = this._bufferService.rows - 1 + buffer.ybase - j;
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()));
1001 this._dirtyRowService.markRangeDirty(buffer.y, buffer.scrollBottom);
1002 buffer.x = 0; // see https://vt100.net/docs/vt220-rm/chapter4.html - vt220 only?
1007 * Insert Ps (Blank) Character(s) (default = 1) (ICH).
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);
1014 this._bufferService.buffer.x,
1015 params.params[0] || 1,
1016 this._bufferService.buffer.getNullCell(this._terminal.eraseAttrData())
1018 this._dirtyRowService.markDirty(this._bufferService.buffer.y);
1024 * Delete Ps Character(s) (default = 1) (DCH).
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);
1031 this._bufferService.buffer.x,
1032 params.params[0] || 1,
1033 this._bufferService.buffer.getNullCell(this._terminal.eraseAttrData())
1035 this._dirtyRowService.markDirty(this._bufferService.buffer.y);
1040 * CSI Ps S Scroll up Ps lines (default = 1) (SU).
1042 public scrollUp(params: IParams): void {
1043 let param = params.params[0] || 1;
1045 // make buffer local for faster access
1046 const buffer = this._bufferService.buffer;
1049 buffer.lines.splice(buffer.ybase + buffer.scrollTop, 1);
1050 buffer.lines.splice(buffer.ybase + buffer.scrollBottom, 0, buffer.getBlankLine(this._terminal.eraseAttrData()));
1052 this._dirtyRowService.markRangeDirty(buffer.scrollTop, buffer.scrollBottom);
1056 * CSI Ps T Scroll down Ps lines (default = 1) (SD).
1058 public scrollDown(params: IParams): void {
1059 let param = params.params[0] || 1;
1061 // make buffer local for faster access
1062 const buffer = this._bufferService.buffer;
1065 buffer.lines.splice(buffer.ybase + buffer.scrollBottom, 1);
1066 buffer.lines.splice(buffer.ybase + buffer.scrollTop, 0, buffer.getBlankLine(DEFAULT_ATTR_DATA));
1068 this._dirtyRowService.markRangeDirty(buffer.scrollTop, buffer.scrollBottom);
1072 * CSI Ps SP @ Scroll left Ps columns (default = 1) (SL) ECMA-48
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.
1083 * - always left shift (no line orientation setting respected)
1085 public scrollLeft(params: IParams): void {
1086 const buffer = this._bufferService.buffer;
1087 if (buffer.y > buffer.scrollBottom || buffer.y < buffer.scrollTop) {
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;
1096 this._dirtyRowService.markRangeDirty(buffer.scrollTop, buffer.scrollBottom);
1100 * CSI Ps SP A Scroll right Ps columns (default = 1) (SR) ECMA-48
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.
1111 * - always right shift (no line orientation setting respected)
1113 public scrollRight(params: IParams): void {
1114 const buffer = this._bufferService.buffer;
1115 if (buffer.y > buffer.scrollBottom || buffer.y < buffer.scrollTop) {
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;
1124 this._dirtyRowService.markRangeDirty(buffer.scrollTop, buffer.scrollBottom);
1129 * Insert Ps Column(s) (default = 1) (DECIC), VT420 and up.
1131 public insertColumns(params: IParams): void {
1132 const buffer = this._bufferService.buffer;
1133 if (buffer.y > buffer.scrollBottom || buffer.y < buffer.scrollTop) {
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;
1142 this._dirtyRowService.markRangeDirty(buffer.scrollTop, buffer.scrollBottom);
1147 * Delete Ps Column(s) (default = 1) (DECDC), VT420 and up.
1149 public deleteColumns(params: IParams): void {
1150 const buffer = this._bufferService.buffer;
1151 if (buffer.y > buffer.scrollBottom || buffer.y < buffer.scrollTop) {
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;
1160 this._dirtyRowService.markRangeDirty(buffer.scrollTop, buffer.scrollBottom);
1165 * Erase Ps Character(s) (default = 1) (ECH).
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);
1172 this._bufferService.buffer.x,
1173 this._bufferService.buffer.x + (params.params[0] || 1),
1174 this._bufferService.buffer.getNullCell(this._terminal.eraseAttrData())
1176 this._dirtyRowService.markDirty(this._bufferService.buffer.y);
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)
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.
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
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`.
1202 public repeatPrecedingCharacter(params: IParams): void {
1203 if (!this._parser.precedingCodepoint) {
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;
1212 this.print(data, 0, data.length);
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).
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
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
1249 * xterm/charproc.c - line 2012, for more information.
1250 * vim responds with ^[[?0c or ^[[?1c after the terminal's response (?)
1252 public sendDeviceAttributesPrimary(params: IParams): void {
1253 if (params.params[0] > 0) {
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');
1262 public sendDeviceAttributesSecondary(params: IParams): void {
1263 if (params.params[0] > 0) {
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');
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).
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-
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
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
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.
1366 * http: *vt100.net/docs/vt220-rm/chapter4.html
1368 public setMode(params: IParams): void {
1369 for (let i = 0; i < params.length; i++) {
1370 switch (params.params[i]) {
1372 this._terminal.insertMode = true;
1375 // this._t.convertEol = true;
1380 public setModePrivate(params: IParams): void {
1381 for (let i = 0; i < params.length; i++) {
1382 switch (params.params[i]) {
1384 this._coreService.decPrivateModes.applicationCursorKeys = true;
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
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();
1400 this._terminal.originMode = true;
1401 this._setCursor(0, 0);
1404 this._terminal.wraparoundMode = true;
1407 // this.cursorBlink = true;
1410 this._logService.debug('Serial port requested application keypad.');
1411 this._terminal.applicationKeypad = true;
1412 if (this._terminal.viewport) {
1413 this._terminal.viewport.syncScrollArea();
1416 case 9: // X10 Mouse
1417 // no release, no motion, no wheel, no modifiers.
1418 this._coreMouseService.activeProtocol = 'X10';
1420 case 1000: // vt200 mouse
1422 this._coreMouseService.activeProtocol = 'VT200';
1424 case 1002: // button event mouse
1425 this._coreMouseService.activeProtocol = 'DRAG';
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';
1432 case 1004: // send focusin/focusout events
1435 this._terminal.sendFocus = true;
1437 case 1005: // utf8 ext mode mouse - removed in #2507
1438 this._logService.debug('DECSET 1005 not supported (see #2507)');
1440 case 1006: // sgr ext mode mouse
1441 this._coreMouseService.activeEncoding = 'SGR';
1443 case 1015: // urxvt ext mode mouse - removed in #2507
1444 this._logService.debug('DECSET 1015 not supported (see #2507)');
1446 case 25: // show cursor
1447 this._terminal.cursorHidden = false;
1449 case 1048: // alt screen cursor
1452 case 1049: // alt screen buffer cursor
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();
1462 this._terminal.showCursor();
1464 case 2004: // bracketed paste mode (https://cirw.in/blog/bracketed-paste)
1465 this._terminal.bracketedPasteMode = true;
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).
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-
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
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
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
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.
1554 public resetMode(params: IParams): void {
1555 for (let i = 0; i < params.length; i++) {
1556 switch (params.params[i]) {
1558 this._terminal.insertMode = false;
1561 // this._t.convertEol = false;
1566 public resetModePrivate(params: IParams): void {
1567 for (let i = 0; i < params.length; i++) {
1568 switch (params.params[i]) {
1570 this._coreService.decPrivateModes.applicationCursorKeys = false;
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);
1579 delete this._terminal.savedCols;
1580 this._terminal.reset();
1583 this._terminal.originMode = false;
1584 this._setCursor(0, 0);
1587 this._terminal.wraparoundMode = false;
1590 // this.cursorBlink = false;
1593 this._logService.debug('Switching back to normal keypad.');
1594 this._terminal.applicationKeypad = false;
1595 if (this._terminal.viewport) {
1596 this._terminal.viewport.syncScrollArea();
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';
1605 case 1004: // send focusin/focusout events
1606 this._terminal.sendFocus = false;
1608 case 1005: // utf8 ext mode mouse - removed in #2507
1609 this._logService.debug('DECRST 1005 not supported (see #2507)');
1611 case 1006: // sgr ext mode mouse
1612 this._coreMouseService.activeEncoding = 'DEFAULT';
1614 case 1015: // urxvt ext mode mouse - removed in #2507
1615 this._logService.debug('DECRST 1015 not supported (see #2507)');
1617 case 25: // hide cursor
1618 this._terminal.cursorHidden = true;
1620 case 1048: // alt screen cursor
1621 this.restoreCursor();
1623 case 1049: // alt screen buffer cursor
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();
1632 this._terminal.refresh(0, this._bufferService.rows - 1);
1633 if (this._terminal.viewport) {
1634 this._terminal.viewport.syncScrollArea();
1636 this._terminal.showCursor();
1638 case 2004: // bracketed paste mode (https://cirw.in/blog/bracketed-paste)
1639 this._terminal.bracketedPasteMode = false;
1646 * Helper to extract and apply color params/subparams.
1647 * Returns advance for params index.
1649 private _extractColor(params: IParams, pos: number, attr: IAttributeData): number {
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];
1656 // alignment placeholder for non color space sequences
1659 // return advance we took in params
1663 accu[advance + cSpace] = params.params[pos + advance];
1664 if (params.hasSubParams(pos + advance)) {
1665 const subparams = params.getSubParams(pos + advance);
1668 if (accu[1] === 5) {
1671 accu[advance + i + 1 + cSpace] = subparams[i];
1672 } while (++i < subparams.length && i + advance + 1 + cSpace < accu.length);
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)) {
1680 // offset colorSpace slot for semicolon mode
1684 } while (++advance + pos < params.length && advance + cSpace < accu.length);
1686 // set default values to 0
1687 for (let i = 2; i < accu.length; ++i) {
1688 if (accu[i] === -1) {
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);
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);
1718 * CSI Pm m Character Attributes (SGR).
1719 * Ps = 0 -> Normal (default).
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).
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.
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
1776 * If 88- or 256-color support is compiled, the following apply.
1777 * Ps = 3 8 ; 5 ; Ps -> Set foreground color to the second
1779 * Ps = 4 8 ; 5 ; Ps -> Set background color to the second
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;
1790 const l = params.length;
1792 const attr = this._terminal.curAttrData;
1794 for (let i = 0; i < l; i++) {
1795 p = params.params[i];
1796 if (p >= 30 && p <= 37) {
1798 attr.fg &= ~(Attributes.CM_MASK | Attributes.PCOLOR_MASK);
1799 attr.fg |= Attributes.CM_P16 | (p - 30);
1800 } else if (p >= 40 && p <= 47) {
1802 attr.bg &= ~(Attributes.CM_MASK | Attributes.PCOLOR_MASK);
1803 attr.bg |= Attributes.CM_P16 | (p - 40);
1804 } else if (p >= 90 && p <= 97) {
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) {
1810 attr.bg &= ~(Attributes.CM_MASK | Attributes.PCOLOR_MASK);
1811 attr.bg |= Attributes.CM_P16 | (p - 100) | 8;
1812 } else if (p === 0) {
1814 attr.fg = DEFAULT_ATTR_DATA.fg;
1815 attr.bg = DEFAULT_ATTR_DATA.bg;
1816 } else if (p === 1) {
1818 attr.fg |= FgFlags.BOLD;
1819 } else if (p === 3) {
1821 attr.bg |= BgFlags.ITALIC;
1822 } else if (p === 4) {
1824 attr.fg |= FgFlags.UNDERLINE;
1825 } else if (p === 5) {
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) {
1834 attr.fg |= FgFlags.INVISIBLE;
1835 } else if (p === 2) {
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) {
1844 attr.bg &= ~BgFlags.ITALIC;
1845 } else if (p === 24) {
1847 attr.fg &= ~FgFlags.UNDERLINE;
1848 } else if (p === 25) {
1850 attr.fg &= ~FgFlags.BLINK;
1851 } else if (p === 27) {
1853 attr.fg &= ~FgFlags.INVERSE;
1854 } else if (p === 28) {
1856 attr.fg &= ~FgFlags.INVISIBLE;
1857 } else if (p === 39) {
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) {
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) {
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);
1875 this._logService.debug('Unknown SGR attribute: %d.', p);
1881 * CSI Ps n Device Status Report (DSR).
1882 * Ps = 5 -> Status Report. Result (``OK'') is
1884 * Ps = 6 -> Report Cursor Position (CPR) [row;column].
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.
1903 public deviceStatus(params: IParams): void {
1904 switch (params.params[0]) {
1907 this._coreService.triggerDataEvent(`${C0.ESC}[0n`);
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`);
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]) {
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`);
1930 // this.handler(C0.ESC + '[?11n');
1933 // dont support user defined keys
1934 // this.handler(C0.ESC + '[?21n');
1937 // north american keyboard
1938 // this.handler(C0.ESC + '[?27;1;0;0n');
1941 // no dec locator/mouse
1942 // this.handler(C0.ESC + '[?50n');
1948 * CSI ! p Soft terminal reset (DECSTR).
1949 * http://vt100.net/docs/vt220-rm/table4-10.html
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();
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]; // ??
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).
1980 public setCursorStyle(params: IParams): void {
1981 const param = params.params[0] || 1;
1985 this._optionsService.options.cursorStyle = 'block';
1989 this._optionsService.options.cursorStyle = 'underline';
1993 this._optionsService.options.cursorStyle = 'bar';
1996 const isBlinking = param % 2 === 1;
1997 this._optionsService.options.cursorBlink = isBlinking;
2002 * Set Scrolling Region [top;bottom] (default = full size of win-
2005 public setScrollRegion(params: IParams): void {
2006 const top = params.params[0] || 1;
2009 if (params.length < 2 || (bottom = params.params[1]) > this._bufferService.rows || bottom === 0) {
2010 bottom = this._bufferService.rows;
2014 this._bufferService.buffer.scrollTop = top - 1;
2015 this._bufferService.buffer.scrollBottom = bottom - 1;
2016 this._setCursor(0, 0);
2024 * Save cursor (ANSI.SYS).
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;
2038 * Restore cursor (ANSI.SYS).
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;
2049 this._restrictCursor();
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.
2058 public setTitle(data: string): void {
2059 this._terminal.handleTitle(data);
2065 * DEC mnemonic: NEL (https://vt100.net/docs/vt510-rm/NEL)
2066 * Moves cursor to first position on next line.
2068 public nextLine(): void {
2069 this._bufferService.buffer.x = 0;
2075 * DEC mnemonic: DECKPAM (https://vt100.net/docs/vt510-rm/DECKPAM.html)
2076 * Enables the numeric keypad to send application sequences to the host.
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();
2088 * DEC mnemonic: DECKPNM (https://vt100.net/docs/vt510-rm/DECKPNM.html)
2089 * Enables the keypad to send numeric characters to the host.
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();
2102 * Select default character set. UTF-8 is not supported (string are unicode anyways)
2103 * therefore ESC % G does the same.
2105 public selectDefaultCharset(): void {
2106 this._terminal.setgLevel(0);
2107 this._terminal.setgCharset(0, DEFAULT_CHARSET); // US (default)
2112 * Designate G0 Character Set, VT100, ISO 2022.
2114 * Designate G1 Character Set (ISO 2022, VT100).
2116 * Designate G2 Character Set (ISO 2022, VT220).
2118 * Designate G3 Character Set (ISO 2022, VT220).
2120 * Designate G1 Character Set (VT300).
2122 * Designate G2 Character Set (VT300).
2124 * Designate G3 Character Set (VT300). C = A -> ISO Latin-1 Supplemental. - Supported?
2126 public selectCharset(collectAndFlag: string): void {
2127 if (collectAndFlag.length !== 2) {
2128 this.selectDefaultCharset();
2131 if (collectAndFlag[0] === '/') {
2132 return; // TODO: Is this supported?
2134 this._terminal.setgCharset(GLEVEL[collectAndFlag[0]], CHARSETS[collectAndFlag[1]] || DEFAULT_CHARSET);
2141 * DEC mnemonic: IND (https://vt100.net/docs/vt510-rm/IND.html)
2142 * Moves the cursor down one line in the same column.
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) {
2150 this._terminal.scroll();
2151 } else if (buffer.y >= this._bufferService.rows) {
2152 buffer.y = this._bufferService.rows - 1;
2154 this._restrictCursor();
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.
2164 public tabSet(): void {
2165 this._bufferService.buffer.tabs[this._bufferService.buffer.x] = true;
2172 * Moves the cursor up one line in the same column. If the cursor is at the top margin,
2173 * the page scrolls down.
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);
2188 this._restrictCursor(); // quickfix to not run out of bounds
2194 * DEC mnemonic: RIS (https://vt100.net/docs/vt510-rm/RIS.html)
2195 * Reset to initial state.
2197 public reset(): void {
2198 this._parser.reset();
2199 this._terminal.reset(); // TODO: save to move from terminal?
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)
2212 public setgLevel(level: number): void {
2213 this._terminal.setgLevel(level); // TODO: save to move from terminal?
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.
2222 * TODO: move DECALN into compat addon
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;
2231 const buffer = this._bufferService.buffer;
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;
2239 this._dirtyRowService.markAllDirty();
2240 this._setCursor(0, 0);