diff --git a/bin/extract_vtfeatures.js b/bin/extract_vtfeatures.js
index 9b5ab898..60979cce 100644
--- a/bin/extract_vtfeatures.js
+++ b/bin/extract_vtfeatures.js
@@ -82,14 +82,15 @@ This document lists xterm.js' support of terminal sequences. The sequences are g
- OSC - Operating System Command: sequence starting with \`ESC ]\` (7bit) or OSC (\`\\x9D\`, 8bit)
Application Program Command (APC), Privacy Message (PM) and Start of String (SOS) are recognized but not supported,
-any sequence of these types will be ignored. They are also not hookable by the API.
+any sequence of these types will be silently ignored. They are also not hookable by the API.
-Note that the list only contains sequences implemented in xterm.js' core codebase. Missing sequences are either
-not supported or unstable/experimental. Furthermore addons or integrations can provide additional custom sequences.
+Note that the list only marks sequences implemented in xterm.js' core codebase as supported. Missing sequences are either
+not supported or unstable/experimental. Furthermore addons or integrations can provide additional custom sequences
+(denoted as "External" where known).
To denote the sequences the tables use the same abbreviations as xterm does:
- \`Ps\`: A single (usually optional) numeric parameter, composed of one or more decimal digits.
-- \`Pm\`: A multiple numeric parameter composed of any number of single numeric parameters, separated by ; character(s),
+- \`Pm\`: Multiple numeric parameters composed of any number of single numeric parameters, separated by ; character(s),
e.g. \` Ps ; Ps ; ... \`.
- \`Pt\`: A text parameter composed of printable characters. Note that for most commands with \`Pt\` only
ASCII printables are specified to work. Additionally the parser will let pass any codepoint greater than C1 as printable.
@@ -334,7 +335,9 @@ const MACRO = [
// #P[reason] - partial support with a reason as title
[/#P\[(.*?)\]/g, (s, p1) => `Partial`],
// #B[reason] - supported but broken in a certain way, reason in title
- [/#B\[(.*?)\]/g, (s, p1) => `Broken`]
+ [/#B\[(.*?)\]/g, (s, p1) => `Broken`],
+ // #E[notes] - support via external resource, eg. addon
+ [/#E\[(.*?)\]/g, (s, p1) => `External`]
];
function applyMacros(s) {
diff --git a/src/common/InputHandler.test.ts b/src/common/InputHandler.test.ts
index 143686a1..f734b002 100644
--- a/src/common/InputHandler.test.ts
+++ b/src/common/InputHandler.test.ts
@@ -2146,6 +2146,49 @@ describe('InputHandler', () => {
});
});
});
+
+ describe('DECSCA and DECSED/DECSEL', () => {
+ it('default is unprotected', async () => {
+ await inputHandler.parseP('some text');
+ await inputHandler.parseP('\x1b[?2K');
+ assert.deepEqual(getLines(bufferService, 2), ['', '']);
+ await inputHandler.parseP('some text');
+ await inputHandler.parseP('\x1b[?2J');
+ assert.deepEqual(getLines(bufferService, 2), ['', '']);
+ });
+ it('DECSCA 1 with DECSEL', async () => {
+ await inputHandler.parseP('###\x1b[1"qlineerase\x1b[0"q***');
+ await inputHandler.parseP('\x1b[?2K');
+ assert.deepEqual(getLines(bufferService, 2), [' lineerase', '']);
+ // normal EL works as before
+ await inputHandler.parseP('\x1b[2K');
+ assert.deepEqual(getLines(bufferService, 2), ['', '']);
+ });
+ it('DECSCA 1 with DECSED', async () => {
+ await inputHandler.parseP('###\x1b[1"qdisplayerase\x1b[0"q***');
+ await inputHandler.parseP('\x1b[?2J');
+ assert.deepEqual(getLines(bufferService, 2), [' displayerase', '']);
+ // normal ED works as before
+ await inputHandler.parseP('\x1b[2J');
+ assert.deepEqual(getLines(bufferService, 2), ['', '']);
+ });
+ it('DECRQSS reports correct DECSCA state', async () => {
+ const sendStack: string[] = [];
+ coreService.onData(d => sendStack.push(d));
+ // DCS $ q " q ST
+ await inputHandler.parseP('\x1bP$q"q\x1b\\');
+ // default - DECSCA unset (0 or 2)
+ assert.deepEqual(sendStack.pop(), '\x1bP1$r0"q\x1b\\');
+ // DECSCA 1 - protected set
+ await inputHandler.parseP('###\x1b[1"q');
+ await inputHandler.parseP('\x1bP$q"q\x1b\\');
+ assert.deepEqual(sendStack.pop(), '\x1bP1$r1"q\x1b\\');
+ // DECSCA 2 - protected reset (same as 0)
+ await inputHandler.parseP('###\x1b[2"q');
+ await inputHandler.parseP('\x1bP$q"q\x1b\\');
+ assert.deepEqual(sendStack.pop(), '\x1bP1$r0"q\x1b\\'); // reported as DECSCA 0
+ });
+ });
describe('DECRQM', () => {
const reportStack: string[] = [];
beforeEach(() => {
diff --git a/src/common/InputHandler.ts b/src/common/InputHandler.ts
index 6b3801a8..b599bb7e 100644
--- a/src/common/InputHandler.ts
+++ b/src/common/InputHandler.ts
@@ -4,13 +4,12 @@
* @license MIT
*/
-import { IInputHandler, IAttributeData, IDisposable, IWindowOptions, IColorEvent, IParseStack, ColorIndex, ColorRequestType, IOscLinkData } from 'common/Types';
+import { IInputHandler, IAttributeData, IDisposable, IWindowOptions, IColorEvent, IParseStack, ColorIndex, ColorRequestType } from 'common/Types';
import { C0, C1 } from 'common/data/EscapeSequences';
import { CHARSETS, DEFAULT_CHARSET } from 'common/data/Charsets';
import { EscapeSequenceParser } from 'common/parser/EscapeSequenceParser';
import { Disposable } from 'common/Lifecycle';
-import { concat } from 'common/TypedArrayUtils';
-import { StringToUtf32, stringFromCodePoint, utf32ToString, Utf8ToUtf32 } from 'common/input/TextDecoder';
+import { StringToUtf32, stringFromCodePoint, Utf8ToUtf32 } from 'common/input/TextDecoder';
import { DEFAULT_ATTR_DATA } from 'common/buffer/BufferLine';
import { EventEmitter, IEvent } from 'common/EventEmitter';
import { IParsingState, IDcsHandler, IEscapeSequenceParser, IParams, IFunctionIdentifier } from 'common/parser/Types';
@@ -47,10 +46,13 @@ const GLEVEL: { [key: string]: number } = { '(': 0, ')': 1, '*': 2, '+': 3, '-':
// @vt: #Y C0 ESC "Escape" "\e, \x1B" "Start of a sequence. Cancels any other sequence."
/**
- * Document common VT features here that are currently unsupported
+ * Document xterm VT features here that are currently unsupported
*/
-// @vt: #N DCS SIXEL "SIXEL Graphics" "DCS Ps ; Ps ; Ps ; q Pt ST" "Draw SIXEL image starting at cursor position."
-// @vt: #N OSC 1 "Set Icon Name" "OSC 1 ; Pt BEL" "Set icon name."
+// @vt: #E[Supported via xterm-addon-image.] DCS SIXEL "SIXEL Graphics" "DCS Ps ; Ps ; Ps ; q Pt ST" "Draw SIXEL image."
+// @vt: #N DCS DECUDK "User Defined Keys" "DCS Ps ; Ps \| Pt ST" "Definitions for user-defined keys."
+// @vt: #N DCS XTGETTCAP "Request Terminfo String" "DCS + q Pt ST" "Request Terminfo String."
+// @vt: #N DCS XTSETTCAP "Set Terminfo Data" "DCS + p Pt ST" "Set Terminfo Data."
+// @vt: #N OSC 1 "Set Icon Name" "OSC 1 ; Pt BEL" "Set icon name."
/**
* Max length of the UTF32 input buffer. Real memory consumption is 4 times higher.
@@ -102,117 +104,6 @@ export enum WindowsOptionsReportType {
// create a warning log if an async handler takes longer than the limit (in ms)
const SLOW_ASYNC_LIMIT = 5000;
-/**
- * DCS subparser implementations
- */
-
-/**
- * DCS $ q Pt ST
- * DECRQSS (https://vt100.net/docs/vt510-rm/DECRQSS.html)
- * Request Status String (DECRQSS), VT420 and up.
- * Response: DECRPSS (https://vt100.net/docs/vt510-rm/DECRPSS.html)
- *
- * @vt: #P[See limited support below.] DCS DECRQSS "Request Selection or Setting" "DCS $ q Pt ST" "Request several terminal settings."
- * Response is in the form `ESC P 1 $ r Pt ST` for valid requests, where `Pt` contains the corresponding CSI string,
- * `ESC P 0 ST` for invalid requests.
- *
- * Supported requests and responses:
- *
- * | Type | Request | Response (`Pt`) |
- * | -------------------------------- | ----------------- | ----------------------------------------------------- |
- * | Graphic Rendition (SGR) | `DCS $ q m ST` | always reporting `0m` (currently broken) |
- * | Top and Bottom Margins (DECSTBM) | `DCS $ q r ST` | `Ps ; Ps r` |
- * | Cursor Style (DECSCUSR) | `DCS $ q SP q ST` | `Ps SP q` |
- * | Protection Attribute (DECSCA) | `DCS $ q " q ST` | always reporting `0 " q` (DECSCA is unsupported) |
- * | Conformance Level (DECSCL) | `DCS $ q " p ST` | always reporting `61 ; 1 " p` (DECSCL is unsupported) |
- *
- *
- * TODO:
- * - fix SGR report
- * - either implement DECSCA or remove the report
- * - either check which conformance is better suited or remove the report completely
- * --> we are currently a mixture of all up to VT400 but dont follow anyone strictly
- */
-class DECRQSS implements IDcsHandler {
- private _data: Uint32Array = new Uint32Array(0);
-
- constructor(
- private _bufferService: IBufferService,
- private _coreService: ICoreService,
- private _logService: ILogService,
- private _optionsService: IOptionsService
- ) { }
-
- public hook(params: IParams): void {
- this._data = new Uint32Array(0);
- }
-
- public put(data: Uint32Array, start: number, end: number): void {
- this._data = concat(this._data, data.subarray(start, end));
- }
-
- public unhook(success: boolean): boolean {
- if (!success) {
- this._data = new Uint32Array(0);
- return true;
- }
- const data = utf32ToString(this._data);
- this._data = new Uint32Array(0);
- switch (data) {
- // valid: DCS 1 $ r Pt ST (xterm)
- case '"q': // DECSCA
- this._coreService.triggerDataEvent(`${C0.ESC}P1$r0"q${C0.ESC}\\`);
- break;
- case '"p': // DECSCL
- this._coreService.triggerDataEvent(`${C0.ESC}P1$r61;1"p${C0.ESC}\\`);
- break;
- case 'r': // DECSTBM
- const pt = '' + (this._bufferService.buffer.scrollTop + 1) +
- ';' + (this._bufferService.buffer.scrollBottom + 1) + 'r';
- this._coreService.triggerDataEvent(`${C0.ESC}P1$r${pt}${C0.ESC}\\`);
- break;
- case 'm': // SGR
- // TODO: report real settings instead of 0m
- this._coreService.triggerDataEvent(`${C0.ESC}P1$r0m${C0.ESC}\\`);
- break;
- case ' q': // DECSCUSR
- const STYLES: { [key: string]: number } = { 'block': 2, 'underline': 4, 'bar': 6 };
- let style = STYLES[this._optionsService.rawOptions.cursorStyle];
- style -= this._optionsService.rawOptions.cursorBlink ? 1 : 0;
- this._coreService.triggerDataEvent(`${C0.ESC}P1$r${style} q${C0.ESC}\\`);
- break;
- default:
- // invalid: DCS 0 $ r Pt ST (xterm)
- this._logService.debug('Unknown DCS $q %s', data);
- this._coreService.triggerDataEvent(`${C0.ESC}P0$r${C0.ESC}\\`);
- }
- return true;
- }
-}
-
-/**
- * DCS Ps; Ps| Pt ST
- * DECUDK (https://vt100.net/docs/vt510-rm/DECUDK.html)
- * not supported
- *
- * @vt: #N DCS DECUDK "User Defined Keys" "DCS Ps ; Ps | Pt ST" "Definitions for user-defined keys."
- */
-
-/**
- * DCS + q Pt ST (xterm)
- * Request Terminfo String
- * not implemented
- *
- * @vt: #N DCS XTGETTCAP "Request Terminfo String" "DCS + q Pt ST" "Request Terminfo String."
- */
-
-/**
- * DCS + p Pt ST (xterm)
- * Set Terminfo Data
- * not supported
- *
- * @vt: #N DCS XTSETTCAP "Set Terminfo Data" "DCS + p Pt ST" "Set Terminfo Data."
- */
/**
* The terminal's standard implementation of IInputHandler, this handles all
@@ -233,6 +124,7 @@ export class InputHandler extends Disposable implements IInputHandler {
protected _iconNameStack: string[] = [];
private _curAttrData: IAttributeData = DEFAULT_ATTR_DATA.clone();
+ public getAttrData(): IAttributeData { return this._curAttrData; }
private _eraseAttrDataInternal: IAttributeData = DEFAULT_ATTR_DATA.clone();
private _activeBuffer: IBuffer;
@@ -334,10 +226,10 @@ export class InputHandler extends Disposable implements IInputHandler {
this._parser.registerCsiHandler({ final: 'G' }, params => this.cursorCharAbsolute(params));
this._parser.registerCsiHandler({ final: 'H' }, params => this.cursorPosition(params));
this._parser.registerCsiHandler({ final: 'I' }, params => this.cursorForwardTab(params));
- this._parser.registerCsiHandler({ final: 'J' }, params => this.eraseInDisplay(params));
- this._parser.registerCsiHandler({ prefix: '?', final: 'J' }, params => this.eraseInDisplay(params));
- this._parser.registerCsiHandler({ final: 'K' }, params => this.eraseInLine(params));
- this._parser.registerCsiHandler({ prefix: '?', final: 'K' }, params => this.eraseInLine(params));
+ this._parser.registerCsiHandler({ final: 'J' }, params => this.eraseInDisplay(params, false));
+ this._parser.registerCsiHandler({ prefix: '?', final: 'J' }, params => this.eraseInDisplay(params, true));
+ this._parser.registerCsiHandler({ final: 'K' }, params => this.eraseInLine(params, false));
+ this._parser.registerCsiHandler({ prefix: '?', final: 'K' }, params => this.eraseInLine(params, true));
this._parser.registerCsiHandler({ final: 'L' }, params => this.insertLines(params));
this._parser.registerCsiHandler({ final: 'M' }, params => this.deleteLines(params));
this._parser.registerCsiHandler({ final: 'P' }, params => this.deleteChars(params));
@@ -369,6 +261,7 @@ export class InputHandler extends Disposable implements IInputHandler {
this._parser.registerCsiHandler({ final: 'u' }, params => this.restoreCursor(params));
this._parser.registerCsiHandler({ intermediates: '\'', final: '}' }, params => this.insertColumns(params));
this._parser.registerCsiHandler({ intermediates: '\'', final: '~' }, params => this.deleteColumns(params));
+ this._parser.registerCsiHandler({ intermediates: '"', final: 'q' }, params => this.selectProtected(params));
this._parser.registerCsiHandler({ intermediates: '$', final: 'p' }, params => this.requestMode(params, true));
this._parser.registerCsiHandler({ prefix: '?', intermediates: '$', final: 'p' }, params => this.requestMode(params, false));
@@ -483,7 +376,7 @@ export class InputHandler extends Disposable implements IInputHandler {
/**
* DCS handler
*/
- this._parser.registerDcsHandler({ intermediates: '$', final: 'q' }, new DECRQSS(this._bufferService, this._coreService, this._logService, this._optionsService));
+ this._parser.registerDcsHandler({ intermediates: '$', final: 'q' }, new DcsHandler((data, params) => this.requestStatusString(data, params)));
}
public dispose(): void {
@@ -1208,6 +1101,18 @@ export class InputHandler extends Disposable implements IInputHandler {
return true;
}
+ /**
+ * CSI Ps " q Select Character Protection Attribute (DECSCA).
+ *
+ * @vt: #Y CSI DECSCA "Select Character Protection Attribute" "CSI Ps " q" "Whether DECSED and DECSEL can erase (0=default, 2) or not (1)."
+ */
+ public selectProtected(params: IParams): boolean {
+ const p = params.params[0];
+ if (p === 1) this._curAttrData.bg |= BgFlags.PROTECTED;
+ if (p === 2 || p === 0) this._curAttrData.bg &= ~BgFlags.PROTECTED;
+ return true;
+ }
+
/**
* Helper method to erase cells in a terminal row.
@@ -1217,13 +1122,14 @@ export class InputHandler extends Disposable implements IInputHandler {
* @param end end - 1 is last erased cell
* @param cleanWrap clear the isWrapped flag
*/
- private _eraseInBufferLine(y: number, start: number, end: number, clearWrap: boolean = false): void {
+ private _eraseInBufferLine(y: number, start: number, end: number, clearWrap: boolean = false, respectProtect: boolean = false): void {
const line = this._activeBuffer.lines.get(this._activeBuffer.ybase + y)!;
line.replaceCells(
start,
end,
this._activeBuffer.getNullCell(this._eraseAttrData()),
- this._eraseAttrData()
+ this._eraseAttrData(),
+ respectProtect
);
if (clearWrap) {
line.isWrapped = false;
@@ -1235,9 +1141,9 @@ export class InputHandler extends Disposable implements IInputHandler {
* The cell gets replaced with the eraseChar of the terminal and the isWrapped property is set to false.
* @param y row index
*/
- private _resetBufferLine(y: number): void {
+ private _resetBufferLine(y: number, respectProtect: boolean = false): void {
const line = this._activeBuffer.lines.get(this._activeBuffer.ybase + y)!;
- line.fill(this._activeBuffer.getNullCell(this._eraseAttrData()));
+ line.fill(this._activeBuffer.getNullCell(this._eraseAttrData()), respectProtect);
this._bufferService.buffer.clearMarkers(this._activeBuffer.ybase + y);
line.isWrapped = false;
}
@@ -1264,18 +1170,18 @@ export class InputHandler extends Disposable implements IInputHandler {
* | 2 | Erase complete viewport. |
* | 3 | Erase scrollback. |
*
- * @vt: #P[Protection attributes are not supported.] CSI DECSED "Selective Erase In Display" "CSI ? Ps J" "Currently the same as ED."
+ * @vt: #Y CSI DECSED "Selective Erase In Display" "CSI ? Ps J" "Same as ED with respecting protection flag."
*/
- public eraseInDisplay(params: IParams): boolean {
+ public eraseInDisplay(params: IParams, respectProtect: boolean = false): boolean {
this._restrictCursor(this._bufferService.cols);
let j;
switch (params.params[0]) {
case 0:
j = this._activeBuffer.y;
this._dirtyRowService.markDirty(j);
- this._eraseInBufferLine(j++, this._activeBuffer.x, this._bufferService.cols, this._activeBuffer.x === 0);
+ this._eraseInBufferLine(j++, this._activeBuffer.x, this._bufferService.cols, this._activeBuffer.x === 0, respectProtect);
for (; j < this._bufferService.rows; j++) {
- this._resetBufferLine(j);
+ this._resetBufferLine(j, respectProtect);
}
this._dirtyRowService.markDirty(j);
break;
@@ -1283,13 +1189,13 @@ export class InputHandler extends Disposable implements IInputHandler {
j = this._activeBuffer.y;
this._dirtyRowService.markDirty(j);
// Deleted front part of line and everything before. This line will no longer be wrapped.
- this._eraseInBufferLine(j, 0, this._activeBuffer.x + 1, true);
+ this._eraseInBufferLine(j, 0, this._activeBuffer.x + 1, true, respectProtect);
if (this._activeBuffer.x + 1 >= this._bufferService.cols) {
// Deleted entire previous line. This next line can no longer be wrapped.
this._activeBuffer.lines.get(j + 1)!.isWrapped = false;
}
while (j--) {
- this._resetBufferLine(j);
+ this._resetBufferLine(j, respectProtect);
}
this._dirtyRowService.markDirty(0);
break;
@@ -1297,7 +1203,7 @@ export class InputHandler extends Disposable implements IInputHandler {
j = this._bufferService.rows;
this._dirtyRowService.markDirty(j - 1);
while (j--) {
- this._resetBufferLine(j);
+ this._resetBufferLine(j, respectProtect);
}
this._dirtyRowService.markDirty(0);
break;
@@ -1336,19 +1242,19 @@ export class InputHandler extends Disposable implements IInputHandler {
* | 1 | Erase from the beginning of the line through the cursor. |
* | 2 | Erase complete line. |
*
- * @vt: #P[Protection attributes are not supported.] CSI DECSEL "Selective Erase In Line" "CSI ? Ps K" "Currently the same as EL."
+ * @vt: #Y CSI DECSEL "Selective Erase In Line" "CSI ? Ps K" "Same as EL with respecting protecting flag."
*/
- public eraseInLine(params: IParams): boolean {
+ public eraseInLine(params: IParams, respectProtect: boolean = false): boolean {
this._restrictCursor(this._bufferService.cols);
switch (params.params[0]) {
case 0:
- this._eraseInBufferLine(this._activeBuffer.y, this._activeBuffer.x, this._bufferService.cols, this._activeBuffer.x === 0);
+ this._eraseInBufferLine(this._activeBuffer.y, this._activeBuffer.x, this._bufferService.cols, this._activeBuffer.x === 0, respectProtect);
break;
case 1:
- this._eraseInBufferLine(this._activeBuffer.y, 0, this._activeBuffer.x + 1, false);
+ this._eraseInBufferLine(this._activeBuffer.y, 0, this._activeBuffer.x + 1, false, respectProtect);
break;
case 2:
- this._eraseInBufferLine(this._activeBuffer.y, 0, this._bufferService.cols, true);
+ this._eraseInBufferLine(this._activeBuffer.y, 0, this._bufferService.cols, true, respectProtect);
break;
}
this._dirtyRowService.markDirty(this._activeBuffer.y);
@@ -2155,7 +2061,7 @@ export class InputHandler extends Disposable implements IInputHandler {
* | 1005 | Disable UTF-8 Mouse Mode. | #N |
* | 1006 | Disable SGR Mouse Mode. | #Y |
* | 1015 | Disable urxvt Mouse Mode. | #N |
- * | 1006 | Disable SGR-Pixels Mouse Mode. | #Y |
+ * | 1016 | Disable SGR-Pixels Mouse Mode. | #Y |
* | 1047 | Use Normal Screen Buffer (clearing screen if in alt). | #Y |
* | 1048 | Restore cursor as in DECRC. | #Y |
* | 1049 | Use Normal Screen Buffer and restore cursor. | #Y |
@@ -2470,7 +2376,7 @@ export class InputHandler extends Disposable implements IInputHandler {
* | 7 | Inverse. Flips foreground and background color. | #Y |
* | 8 | Invisible (hidden). | #Y |
* | 9 | Crossed-out characters (strikethrough). | #Y |
- * | 21 | Doubly underlined. | #P[Currently outputs a single underline.] |
+ * | 21 | Doubly underlined. | #Y |
* | 22 | Normal (neither bold nor faint). | #Y |
* | 23 | No italic. | #Y |
* | 24 | Not underlined. | #Y |
@@ -2498,6 +2404,7 @@ export class InputHandler extends Disposable implements IInputHandler {
* | 47 | Background color: White. | #Y |
* | 48 | Background color: Extended color. | #P[Support for RGB and indexed colors, see below.] |
* | 49 | Background color: Default (original). | #Y |
+ * | 58 | Underline color: Extended color. | #P[Support for RGB and indexed colors, see below.] |
* | 90 - 97 | Bright foreground color (analogous to 30 - 37). | #Y |
* | 100 - 107 | Bright background color (analogous to 40 - 47). | #Y |
*
@@ -2507,13 +2414,13 @@ export class InputHandler extends Disposable implements IInputHandler {
* | ------ | ------------------------------------------------------------- | ------- |
* | 0 | No underline. Same as `SGR 24 m`. | #Y |
* | 1 | Single underline. Same as `SGR 4 m`. | #Y |
- * | 2 | Double underline. | #P[Currently outputs a single underline.] |
- * | 3 | Curly underline. | #P[Currently outputs a single underline.] |
- * | 4 | Dotted underline. | #P[Currently outputs a single underline.] |
- * | 5 | Dashed underline. | #P[Currently outputs a single underline.] |
+ * | 2 | Double underline. | #Y |
+ * | 3 | Curly underline. | #Y |
+ * | 4 | Dotted underline. | #Y |
+ * | 5 | Dashed underline. | #Y |
* | other | Single underline. Same as `SGR 4 m`. | #Y |
*
- * Extended colors are supported for foreground (Ps=38) and background (Ps=48) as follows:
+ * Extended colors are supported for foreground (Ps=38), background (Ps=48) and underline (Ps=58) as follows:
*
* | Ps + 1 | Meaning | Support |
* | ------ | ------------------------------------------------------------- | ------- |
@@ -3390,4 +3297,51 @@ export class InputHandler extends Disposable implements IInputHandler {
this._setCursor(0, 0);
return true;
}
+
+
+ /**
+ * DCS $ q Pt ST
+ * DECRQSS (https://vt100.net/docs/vt510-rm/DECRQSS.html)
+ * Request Status String (DECRQSS), VT420 and up.
+ * Response: DECRPSS (https://vt100.net/docs/vt510-rm/DECRPSS.html)
+ *
+ * @vt: #P[Limited support, see below.] DCS DECRQSS "Request Selection or Setting" "DCS $ q Pt ST" "Request several terminal settings."
+ * Response is in the form `ESC P 1 $ r Pt ST` for valid requests, where `Pt` contains the corresponding CSI string,
+ * `ESC P 0 ST` for invalid requests.
+ *
+ * Supported requests and responses:
+ *
+ * | Type | Request | Response (`Pt`) |
+ * | -------------------------------- | ----------------- | ----------------------------------------------------- |
+ * | Graphic Rendition (SGR) | `DCS $ q m ST` | always reporting `0m` (currently broken) |
+ * | Top and Bottom Margins (DECSTBM) | `DCS $ q r ST` | `Ps ; Ps r` |
+ * | Cursor Style (DECSCUSR) | `DCS $ q SP q ST` | `Ps SP q` |
+ * | Protection Attribute (DECSCA) | `DCS $ q " q ST` | `Ps " q` (DECSCA 2 is reported as Ps = 0) |
+ * | Conformance Level (DECSCL) | `DCS $ q " p ST` | always reporting `61 ; 1 " p` (DECSCL is unsupported) |
+ *
+ *
+ * TODO:
+ * - fix SGR report
+ * - either check which conformance is better suited or remove the report completely
+ * --> we are currently a mixture of all up to VT400 but dont follow anyone strictly
+ */
+ public requestStatusString(data: string, params: IParams): boolean {
+ const f = (s: string): boolean => {
+ this._coreService.triggerDataEvent(`${C0.ESC}${s}${C0.ESC}\\`);
+ return true;
+ };
+
+ // access helpers
+ const b = this._bufferService.buffer;
+ const opts = this._optionsService.rawOptions;
+ const STYLES: { [key: string]: number } = { 'block': 2, 'underline': 4, 'bar': 6 };
+
+ if (data === '"q') return f(`P1$r${this._curAttrData.isProtected() ? 1 : 0}"q`);
+ if (data === '"p') return f(`P1$r61;1"p`);
+ if (data === 'r') return f(`P1$r${b.scrollTop + 1};${b.scrollBottom + 1}r`);
+ // FIXME: report real SGR settings instead of 0m
+ if (data === 'm') return f(`P1$r0m`);
+ if (data === ' q') return f(`P1$r${STYLES[opts.cursorStyle] - (opts.cursorBlink ? 1 : 0)} q`);
+ return f(`P0$r`);
+ }
}
diff --git a/src/common/Types.d.ts b/src/common/Types.d.ts
index 7d15d6a1..d44bb197 100644
--- a/src/common/Types.d.ts
+++ b/src/common/Types.d.ts
@@ -150,6 +150,7 @@ export interface IAttributeData {
isItalic(): number;
isDim(): number;
isStrikethrough(): number;
+ isProtected(): number;
// color modes
getFgColorMode(): number;
@@ -203,9 +204,9 @@ export interface IBufferLine {
addCodepointToCell(index: number, codePoint: number): void;
insertCells(pos: number, n: number, ch: ICellData, eraseAttr?: IAttributeData): void;
deleteCells(pos: number, n: number, fill: ICellData, eraseAttr?: IAttributeData): void;
- replaceCells(start: number, end: number, fill: ICellData, eraseAttr?: IAttributeData): void;
+ replaceCells(start: number, end: number, fill: ICellData, eraseAttr?: IAttributeData, respectProtect?: boolean): void;
resize(cols: number, fill: ICellData): void;
- fill(fillCellData: ICellData): void;
+ fill(fillCellData: ICellData, respectProtect?: boolean): void;
copyFrom(line: IBufferLine): void;
clone(): IBufferLine;
getTrimmedLength(): number;
diff --git a/src/common/buffer/AttributeData.ts b/src/common/buffer/AttributeData.ts
index 3af3d293..c9f4cd61 100644
--- a/src/common/buffer/AttributeData.ts
+++ b/src/common/buffer/AttributeData.ts
@@ -46,6 +46,7 @@ export class AttributeData implements IAttributeData {
public isItalic(): number { return this.bg & BgFlags.ITALIC; }
public isDim(): number { return this.bg & BgFlags.DIM; }
public isStrikethrough(): number { return this.fg & FgFlags.STRIKETHROUGH; }
+ public isProtected(): number { return this.bg & BgFlags.PROTECTED; }
// color modes
public getFgColorMode(): number { return this.fg & Attributes.CM_MASK; }
diff --git a/src/common/buffer/BufferLine.ts b/src/common/buffer/BufferLine.ts
index a1c7f257..43e89839 100644
--- a/src/common/buffer/BufferLine.ts
+++ b/src/common/buffer/BufferLine.ts
@@ -168,6 +168,11 @@ export class BufferLine implements IBufferLine {
return '';
}
+ /** Get state of protected flag. */
+ public isProtected(index: number): number {
+ return this._data[index * CELL_SIZE + Cell.BG] & BgFlags.PROTECTED;
+ }
+
/**
* Load data at `index` into `cell`. This is used to access cells in a way that's more friendly
* to GC as it significantly reduced the amount of new objects/references needed.
@@ -298,7 +303,24 @@ export class BufferLine implements IBufferLine {
}
}
- public replaceCells(start: number, end: number, fillCellData: ICellData, eraseAttr?: IAttributeData): void {
+ public replaceCells(start: number, end: number, fillCellData: ICellData, eraseAttr?: IAttributeData, respectProtect: boolean = false): void {
+ // full branching on respectProtect==true, hopefully getting fast JIT for standard case
+ if (respectProtect) {
+ if (start && this.getWidth(start - 1) === 2 && !this.isProtected(start - 1)) {
+ this.setCellFromCodePoint(start - 1, 0, 1, eraseAttr?.fg || 0, eraseAttr?.bg || 0, eraseAttr?.extended || new ExtendedAttrs());
+ }
+ if (end < this.length && this.getWidth(end - 1) === 2 && !this.isProtected(end)) {
+ this.setCellFromCodePoint(end, 0, 1, eraseAttr?.fg || 0, eraseAttr?.bg || 0, eraseAttr?.extended || new ExtendedAttrs());
+ }
+ while (start < end && start < this.length) {
+ if (!this.isProtected(start)) {
+ this.setCell(start, fillCellData);
+ }
+ start++;
+ }
+ return;
+ }
+
// handle fullwidth at start: reset cell one to the left if start is second cell of a wide char
if (start && this.getWidth(start - 1) === 2) {
this.setCellFromCodePoint(start - 1, 0, 1, eraseAttr?.fg || 0, eraseAttr?.bg || 0, eraseAttr?.extended || new ExtendedAttrs());
@@ -352,7 +374,16 @@ export class BufferLine implements IBufferLine {
}
/** fill a line with fillCharData */
- public fill(fillCellData: ICellData): void {
+ public fill(fillCellData: ICellData, respectProtect: boolean = false): void {
+ // full branching on respectProtect==true, hopefully getting fast JIT for standard case
+ if (respectProtect) {
+ for (let i = 0; i < this.length; ++i) {
+ if (!this.isProtected(i)) {
+ this.setCell(i, fillCellData);
+ }
+ }
+ return;
+ }
this._combined = {};
this._extendedAttrs = {};
for (let i = 0; i < this.length; ++i) {
diff --git a/src/common/buffer/Constants.ts b/src/common/buffer/Constants.ts
index 0dfa86fd..25999bcd 100644
--- a/src/common/buffer/Constants.ts
+++ b/src/common/buffer/Constants.ts
@@ -123,11 +123,12 @@ export const enum FgFlags {
export const enum BgFlags {
/**
- * bit 27..32 (upper 3 unused)
+ * bit 27..32 (upper 2 unused)
*/
ITALIC = 0x4000000,
DIM = 0x8000000,
- HAS_EXTENDED = 0x10000000
+ HAS_EXTENDED = 0x10000000,
+ PROTECTED = 0x20000000
}
export const enum ExtFlags {