Merge pull request #4098 from jerch/protected

protection flag support
This commit is contained in:
Daniel Imms
2022-09-12 05:52:43 -07:00
committed by GitHub
7 changed files with 190 additions and 156 deletions
+8 -5
View File
@@ -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) => `<span title="${p1}" style="text-decoration: underline">Partial</span>`],
// #B[reason] - supported but broken in a certain way, reason in title
[/#B\[(.*?)\]/g, (s, p1) => `<span title="${p1}" style="text-decoration: underline">Broken</span>`]
[/#B\[(.*?)\]/g, (s, p1) => `<span title="${p1}" style="text-decoration: underline">Broken</span>`],
// #E[notes] - support via external resource, eg. addon
[/#E\[(.*?)\]/g, (s, p1) => `<span title="${p1}" style="text-decoration: underline">External</span>`]
];
function applyMacros(s) {
+43
View File
@@ -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(() => {
+99 -145
View File
@@ -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`);
}
}
+3 -2
View File
@@ -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;
+1
View File
@@ -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; }
+33 -2
View File
@@ -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) {
+3 -2
View File
@@ -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 {