mirror of
https://github.com/wavetermdev/xterm.js.git
synced 2026-08-05 13:43:48 -07:00
implement OSC 104|110|111|112
This commit is contained in:
@@ -7,6 +7,7 @@ import { IColorManager, IColor, IColorSet, IColorContrastCache } from 'browser/T
|
||||
import { ITheme } from 'common/services/Services';
|
||||
import { channels, color, css } from 'browser/Color';
|
||||
import { ColorContrastCache } from 'browser/ColorContrastCache';
|
||||
import { ColorIndex } from 'common/Types';
|
||||
|
||||
const DEFAULT_FOREGROUND = css.toColor('#ffffff');
|
||||
const DEFAULT_BACKGROUND = css.toColor('#000000');
|
||||
@@ -65,6 +66,13 @@ export const DEFAULT_ANSI_COLORS = Object.freeze((() => {
|
||||
return colors;
|
||||
})());
|
||||
|
||||
interface IRestoreColorSet {
|
||||
foreground: IColor;
|
||||
background: IColor;
|
||||
cursor: IColor;
|
||||
ansi: IColor[];
|
||||
}
|
||||
|
||||
/**
|
||||
* Manages the source of truth for a terminal's colors.
|
||||
*/
|
||||
@@ -73,6 +81,7 @@ export class ColorManager implements IColorManager {
|
||||
private _ctx: CanvasRenderingContext2D;
|
||||
private _litmusColor: CanvasGradient;
|
||||
private _contrastCache: IColorContrastCache;
|
||||
private _restoreColors!: IRestoreColorSet;
|
||||
|
||||
constructor(document: Document, public allowTransparency: boolean) {
|
||||
const canvas = document.createElement('canvas');
|
||||
@@ -96,6 +105,7 @@ export class ColorManager implements IColorManager {
|
||||
ansi: DEFAULT_ANSI_COLORS.slice(),
|
||||
contrastCache: this._contrastCache
|
||||
};
|
||||
this._updateRestoreColors();
|
||||
}
|
||||
|
||||
public onOptionsChange(key: string): void {
|
||||
@@ -142,6 +152,39 @@ export class ColorManager implements IColorManager {
|
||||
this.colors.ansi[15] = this._parseColor(theme.brightWhite, DEFAULT_ANSI_COLORS[15]);
|
||||
// Clear our the cache
|
||||
this._contrastCache.clear();
|
||||
this._updateRestoreColors();
|
||||
}
|
||||
|
||||
public restoreColor(slot?: ColorIndex): void {
|
||||
// unset slot restores all ansi colors
|
||||
if (slot === undefined) {
|
||||
for (let i = 0; i < this._restoreColors.ansi.length; ++i) {
|
||||
this.colors.ansi[i] = this._restoreColors.ansi[i];
|
||||
}
|
||||
return;
|
||||
}
|
||||
switch (slot) {
|
||||
case ColorIndex.FOREGROUND:
|
||||
this.colors.foreground = this._restoreColors.foreground;
|
||||
break;
|
||||
case ColorIndex.BACKGROUND:
|
||||
this.colors.background = this._restoreColors.background;
|
||||
break;
|
||||
case ColorIndex.CURSOR:
|
||||
this.colors.cursor = this._restoreColors.cursor;
|
||||
break;
|
||||
default:
|
||||
this.colors.ansi[slot] = this._restoreColors.ansi[slot];
|
||||
}
|
||||
}
|
||||
|
||||
private _updateRestoreColors(): void {
|
||||
this._restoreColors = {
|
||||
foreground: this.colors.foreground,
|
||||
background: this.colors.background,
|
||||
cursor: this.colors.cursor,
|
||||
ansi: [...this.colors.ansi]
|
||||
};
|
||||
}
|
||||
|
||||
private _parseColor(
|
||||
|
||||
+23
-16
@@ -39,7 +39,7 @@ import { MouseZoneManager } from 'browser/MouseZoneManager';
|
||||
import { AccessibilityManager } from './AccessibilityManager';
|
||||
import { ITheme, IMarker, IDisposable, ISelectionPosition, ILinkProvider } from 'xterm';
|
||||
import { DomRenderer } from 'browser/renderer/dom/DomRenderer';
|
||||
import { IKeyboardEvent, KeyboardResultType, CoreMouseEventType, CoreMouseButton, CoreMouseAction, ITerminalOptions, ScrollSource, IColorEvent, ColorIndex, IColorRGB } from 'common/Types';
|
||||
import { IKeyboardEvent, KeyboardResultType, CoreMouseEventType, CoreMouseButton, CoreMouseAction, ITerminalOptions, ScrollSource, IColorEvent, ColorIndex, IColorRGB, ColorRequestType } from 'common/Types';
|
||||
import { evaluateKeyboardEvent } from 'common/input/Keyboard';
|
||||
import { EventEmitter, IEvent, forwardEvent } from 'common/EventEmitter';
|
||||
import { DEFAULT_ATTR_DATA } from 'common/buffer/BufferLine';
|
||||
@@ -179,9 +179,10 @@ export class Terminal extends CoreTerminal implements ITerminal {
|
||||
}
|
||||
|
||||
/**
|
||||
* Handle color event from inputhandler for OSC 4 | 10 | 11 | 12.
|
||||
* An event from OSC 4 may contain multiple set or report requests,
|
||||
* while OSC 10 | 11 | 12 create single requests.
|
||||
* Handle color event from inputhandler for OSC 4|104 | 10|110 | 11|111 | 12|112.
|
||||
* An event from OSC 4|104 may contain multiple set or report requests, and multiple
|
||||
* or none restore requests (restting all),
|
||||
* while an event from OSC 10|110 | 11|111 | 12|112 always contains a single request.
|
||||
*/
|
||||
private _handleColorEvent(event: IColorEvent): void {
|
||||
if (!this._colorManager) return;
|
||||
@@ -189,32 +190,38 @@ export class Terminal extends CoreTerminal implements ITerminal {
|
||||
let acc: 'foreground' | 'background' | 'cursor' | 'ansi' | undefined = undefined;
|
||||
let ident = '';
|
||||
switch (req.index) {
|
||||
case ColorIndex.FOREGROUND: // OSC 10
|
||||
case ColorIndex.FOREGROUND: // OSC 10 | 110
|
||||
acc = 'foreground';
|
||||
ident = '10';
|
||||
break;
|
||||
case ColorIndex.BACKGROUND: // OSC 11
|
||||
case ColorIndex.BACKGROUND: // OSC 11 | 111
|
||||
acc = 'background';
|
||||
ident = '11';
|
||||
break;
|
||||
case ColorIndex.CURSOR: // OSC 12
|
||||
case ColorIndex.CURSOR: // OSC 12 | 112
|
||||
acc = 'cursor';
|
||||
ident = '12';
|
||||
break;
|
||||
default: // OSC 4
|
||||
default: // OSC 4 | 104
|
||||
// we can skip the [0..255] range check here (already done in inputhandler)
|
||||
acc = 'ansi';
|
||||
ident = '4;' + req.index;
|
||||
}
|
||||
if (acc) {
|
||||
if (req.color) {
|
||||
if (acc === 'ansi') this._colorManager.colors.ansi[req.index] = rgba.toColor(...req.color);
|
||||
else this._colorManager.colors[acc] = rgba.toColor(...req.color);
|
||||
} else {
|
||||
const channels = color.toColorRGB(acc === 'ansi'
|
||||
? this._colorManager.colors.ansi[req.index]
|
||||
: this._colorManager.colors[acc]);
|
||||
this.coreService.triggerDataEvent(`${C0.ESC}]${ident};${toRgbString(channels)}${C0.BEL}`);
|
||||
switch (req.type) {
|
||||
case ColorRequestType.REPORT:
|
||||
const channels = color.toColorRGB(acc === 'ansi'
|
||||
? this._colorManager.colors.ansi[req.index]
|
||||
: this._colorManager.colors[acc]);
|
||||
this.coreService.triggerDataEvent(`${C0.ESC}]${ident};${toRgbString(channels)}${C0.BEL}`);
|
||||
break;
|
||||
case ColorRequestType.SET:
|
||||
if (acc === 'ansi') this._colorManager.colors.ansi[req.index] = rgba.toColor(...req.color);
|
||||
else this._colorManager.colors[acc] = rgba.toColor(...req.color);
|
||||
break;
|
||||
case ColorRequestType.RESTORE:
|
||||
this._colorManager.restoreColor(req.index);
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -5,7 +5,7 @@
|
||||
|
||||
import { assert } from 'chai';
|
||||
import { InputHandler } from 'common/InputHandler';
|
||||
import { IBufferLine, IAttributeData, IColorEvent, ColorIndex } from 'common/Types';
|
||||
import { IBufferLine, IAttributeData, IColorEvent, ColorIndex, ColorRequestType } from 'common/Types';
|
||||
import { DEFAULT_ATTR_DATA } from 'common/buffer/BufferLine';
|
||||
import { CellData } from 'common/buffer/CellData';
|
||||
import { Attributes, UnderlineStyle } from 'common/buffer/Constants';
|
||||
@@ -1869,14 +1869,14 @@ describe('InputHandler', () => {
|
||||
inputHandler.onColor(ev => stack.push(ev));
|
||||
// single color query
|
||||
await inputHandler.parseP('\x1b]4;0;?\x07');
|
||||
assert.deepEqual(stack, [[{ index: 0 }]]);
|
||||
assert.deepEqual(stack, [[{ type: ColorRequestType.REPORT, index: 0 }]]);
|
||||
stack.length = 0;
|
||||
await inputHandler.parseP('\x1b]4;123;?\x07');
|
||||
assert.deepEqual(stack, [[{ index: 123 }]]);
|
||||
assert.deepEqual(stack, [[{ type: ColorRequestType.REPORT, index: 123 }]]);
|
||||
stack.length = 0;
|
||||
// multiple queries
|
||||
await inputHandler.parseP('\x1b]4;0;?;123;?\x07');
|
||||
assert.deepEqual(stack, [[{ index: 0 }, { index: 123 }]]);
|
||||
assert.deepEqual(stack, [[{ type: ColorRequestType.REPORT, index: 0 }, { type: ColorRequestType.REPORT, index: 123 }]]);
|
||||
stack.length = 0;
|
||||
});
|
||||
it('4: set color events', async () => {
|
||||
@@ -1884,92 +1884,124 @@ describe('InputHandler', () => {
|
||||
inputHandler.onColor(ev => stack.push(ev));
|
||||
// single color query
|
||||
await inputHandler.parseP('\x1b]4;0;rgb:01/02/03\x07');
|
||||
assert.deepEqual(stack, [[{ index: 0, color: [1, 2, 3] }]]);
|
||||
assert.deepEqual(stack, [[{ type: ColorRequestType.SET, index: 0, color: [1, 2, 3] }]]);
|
||||
stack.length = 0;
|
||||
await inputHandler.parseP('\x1b]4;123;#aabbcc\x07');
|
||||
assert.deepEqual(stack, [[{ index: 123, color: [170, 187, 204] }]]);
|
||||
assert.deepEqual(stack, [[{ type: ColorRequestType.SET, index: 123, color: [170, 187, 204] }]]);
|
||||
stack.length = 0;
|
||||
// multiple queries
|
||||
await inputHandler.parseP('\x1b]4;0;rgb:aa/bb/cc;123;#001122\x07');
|
||||
assert.deepEqual(stack, [[{ index: 0, color: [170, 187, 204] }, { index: 123, color: [0, 17, 34] }]]);
|
||||
assert.deepEqual(stack, [[{ type: ColorRequestType.SET, index: 0, color: [170, 187, 204] }, { type: ColorRequestType.SET, index: 123, color: [0, 17, 34] }]]);
|
||||
stack.length = 0;
|
||||
});
|
||||
it('4: should ignore invalid values', async () => {
|
||||
const stack: IColorEvent[] = [];
|
||||
inputHandler.onColor(ev => stack.push(ev));
|
||||
await inputHandler.parseP('\x1b]4;0;rgb:aa/bb/cc;45;rgb:1/22/333;123;#001122\x07');
|
||||
assert.deepEqual(stack, [[{ index: 0, color: [170, 187, 204] }, { index: 123, color: [0, 17, 34] }]]);
|
||||
assert.deepEqual(stack, [[{ type: ColorRequestType.SET, index: 0, color: [170, 187, 204] }, { type: ColorRequestType.SET, index: 123, color: [0, 17, 34] }]]);
|
||||
stack.length = 0;
|
||||
});
|
||||
it('104: restore events', async () => {
|
||||
const stack: IColorEvent[] = [];
|
||||
inputHandler.onColor(ev => stack.push(ev));
|
||||
await inputHandler.parseP('\x1b]104;0\x07\x1b]104;43\x07');
|
||||
assert.deepEqual(stack, [[{ type: ColorRequestType.RESTORE, index: 0 }], [{ type: ColorRequestType.RESTORE, index: 43 }]]);
|
||||
stack.length = 0;
|
||||
// multiple in one command
|
||||
await inputHandler.parseP('\x1b]104;0;43\x07');
|
||||
assert.deepEqual(stack, [[{ type: ColorRequestType.RESTORE, index: 0 }, { type: ColorRequestType.RESTORE, index: 43 }]]);
|
||||
stack.length = 0;
|
||||
// full ANSI table restore
|
||||
await inputHandler.parseP('\x1b]104\x07');
|
||||
assert.deepEqual(stack, [[{ type: ColorRequestType.RESTORE}]]);
|
||||
});
|
||||
|
||||
it('10: FG set & query events', async () => {
|
||||
const stack: IColorEvent[] = [];
|
||||
inputHandler.onColor(ev => stack.push(ev));
|
||||
// single foreground query --> color undefined
|
||||
await inputHandler.parseP('\x1b]10;?\x07');
|
||||
assert.deepEqual(stack, [[{ index: ColorIndex.FOREGROUND }]]);
|
||||
assert.deepEqual(stack, [[{ type: ColorRequestType.REPORT, index: ColorIndex.FOREGROUND }]]);
|
||||
stack.length = 0;
|
||||
// OSC with multiple values maps to OSC 10 & OSC 11 & OSC 12
|
||||
await inputHandler.parseP('\x1b]10;?;?;?;?\x07');
|
||||
assert.deepEqual(stack, [[{ index: ColorIndex.FOREGROUND }], [{ index: ColorIndex.BACKGROUND }], [{ index: ColorIndex.CURSOR }]]);
|
||||
assert.deepEqual(stack, [[{ type: ColorRequestType.REPORT, index: ColorIndex.FOREGROUND }], [{ type: ColorRequestType.REPORT, index: ColorIndex.BACKGROUND }], [{ type: ColorRequestType.REPORT, index: ColorIndex.CURSOR }]]);
|
||||
stack.length = 0;
|
||||
// set foreground color events
|
||||
await inputHandler.parseP('\x1b]10;rgb:01/02/03\x07');
|
||||
assert.deepEqual(stack, [[{ index: ColorIndex.FOREGROUND, color: [1, 2, 3] }]]);
|
||||
assert.deepEqual(stack, [[{ type: ColorRequestType.SET, index: ColorIndex.FOREGROUND, color: [1, 2, 3] }]]);
|
||||
stack.length = 0;
|
||||
await inputHandler.parseP('\x1b]10;#aabbcc\x07');
|
||||
assert.deepEqual(stack, [[{ index: ColorIndex.FOREGROUND, color: [170, 187, 204] }]]);
|
||||
assert.deepEqual(stack, [[{ type: ColorRequestType.SET, index: ColorIndex.FOREGROUND, color: [170, 187, 204] }]]);
|
||||
stack.length = 0;
|
||||
// set FG, BG and cursor color at once
|
||||
await inputHandler.parseP('\x1b]10;rgb:aa/bb/cc;#001122;rgb:12/34/56\x07');
|
||||
assert.deepEqual(stack, [
|
||||
[{ index: ColorIndex.FOREGROUND, color: [170, 187, 204] }],
|
||||
[{ index: ColorIndex.BACKGROUND, color: [0, 17, 34] }],
|
||||
[{ index: ColorIndex.CURSOR, color: [18, 52, 86] }]
|
||||
[{ type: ColorRequestType.SET, index: ColorIndex.FOREGROUND, color: [170, 187, 204] }],
|
||||
[{ type: ColorRequestType.SET, index: ColorIndex.BACKGROUND, color: [0, 17, 34] }],
|
||||
[{ type: ColorRequestType.SET, index: ColorIndex.CURSOR, color: [18, 52, 86] }]
|
||||
]);
|
||||
});
|
||||
it('110: restore FG color', async () => {
|
||||
const stack: IColorEvent[] = [];
|
||||
inputHandler.onColor(ev => stack.push(ev));
|
||||
await inputHandler.parseP('\x1b]110\x07');
|
||||
assert.deepEqual(stack, [[{ type: ColorRequestType.RESTORE, index: ColorIndex.FOREGROUND }]]);
|
||||
});
|
||||
it('11: BG set & query events', async () => {
|
||||
const stack: IColorEvent[] = [];
|
||||
inputHandler.onColor(ev => stack.push(ev));
|
||||
// single background query --> color undefined
|
||||
await inputHandler.parseP('\x1b]11;?\x07');
|
||||
assert.deepEqual(stack, [[{ index: ColorIndex.BACKGROUND }]]);
|
||||
assert.deepEqual(stack, [[{ type: ColorRequestType.REPORT, index: ColorIndex.BACKGROUND }]]);
|
||||
stack.length = 0;
|
||||
// OSC 11 with multiple values creates only BG and cursor event
|
||||
await inputHandler.parseP('\x1b]11;?;?;?;?\x07');
|
||||
assert.deepEqual(stack, [[{ index: ColorIndex.BACKGROUND }], [{ index: ColorIndex.CURSOR }]]);
|
||||
assert.deepEqual(stack, [[{ type: ColorRequestType.REPORT, index: ColorIndex.BACKGROUND }], [{ type: ColorRequestType.REPORT, index: ColorIndex.CURSOR }]]);
|
||||
stack.length = 0;
|
||||
// set background color events
|
||||
await inputHandler.parseP('\x1b]11;rgb:01/02/03\x07');
|
||||
assert.deepEqual(stack, [[{ index: ColorIndex.BACKGROUND, color: [1, 2, 3] }]]);
|
||||
assert.deepEqual(stack, [[{ type: ColorRequestType.SET, index: ColorIndex.BACKGROUND, color: [1, 2, 3] }]]);
|
||||
stack.length = 0;
|
||||
await inputHandler.parseP('\x1b]11;#aabbcc\x07');
|
||||
assert.deepEqual(stack, [[{ index: ColorIndex.BACKGROUND, color: [170, 187, 204] }]]);
|
||||
assert.deepEqual(stack, [[{ type: ColorRequestType.SET, index: ColorIndex.BACKGROUND, color: [170, 187, 204] }]]);
|
||||
stack.length = 0;
|
||||
// set BG and cursor color at once
|
||||
await inputHandler.parseP('\x1b]11;#001122;rgb:12/34/56\x07');
|
||||
assert.deepEqual(stack, [
|
||||
[{ index: ColorIndex.BACKGROUND, color: [0, 17, 34] }],
|
||||
[{ index: ColorIndex.CURSOR, color: [18, 52, 86] }]
|
||||
[{ type: ColorRequestType.SET, index: ColorIndex.BACKGROUND, color: [0, 17, 34] }],
|
||||
[{ type: ColorRequestType.SET, index: ColorIndex.CURSOR, color: [18, 52, 86] }]
|
||||
]);
|
||||
});
|
||||
it('111: restore BG color', async () => {
|
||||
const stack: IColorEvent[] = [];
|
||||
inputHandler.onColor(ev => stack.push(ev));
|
||||
await inputHandler.parseP('\x1b]111\x07');
|
||||
assert.deepEqual(stack, [[{ type: ColorRequestType.RESTORE, index: ColorIndex.BACKGROUND }]]);
|
||||
});
|
||||
it('12: cursor color set & query events', async () => {
|
||||
const stack: IColorEvent[] = [];
|
||||
inputHandler.onColor(ev => stack.push(ev));
|
||||
// single cursor query --> color undefined
|
||||
await inputHandler.parseP('\x1b]12;?\x07');
|
||||
assert.deepEqual(stack, [[{ index: ColorIndex.CURSOR }]]);
|
||||
assert.deepEqual(stack, [[{ type: ColorRequestType.REPORT, index: ColorIndex.CURSOR }]]);
|
||||
stack.length = 0;
|
||||
// OSC 12 with multiple values creates only cursor event
|
||||
await inputHandler.parseP('\x1b]12;?;?;?;?\x07');
|
||||
assert.deepEqual(stack, [[{ index: ColorIndex.CURSOR }]]);
|
||||
assert.deepEqual(stack, [[{ type: ColorRequestType.REPORT, index: ColorIndex.CURSOR }]]);
|
||||
stack.length = 0;
|
||||
// set cursor color events
|
||||
await inputHandler.parseP('\x1b]12;rgb:01/02/03\x07');
|
||||
assert.deepEqual(stack, [[{ index: ColorIndex.CURSOR, color: [1, 2, 3] }]]);
|
||||
assert.deepEqual(stack, [[{ type: ColorRequestType.SET, index: ColorIndex.CURSOR, color: [1, 2, 3] }]]);
|
||||
stack.length = 0;
|
||||
await inputHandler.parseP('\x1b]12;#aabbcc\x07');
|
||||
assert.deepEqual(stack, [[{ index: ColorIndex.CURSOR, color: [170, 187, 204] }]]);
|
||||
assert.deepEqual(stack, [[{ type: ColorRequestType.SET, index: ColorIndex.CURSOR, color: [170, 187, 204] }]]);
|
||||
});
|
||||
it('112: restore cursor color', async () => {
|
||||
const stack: IColorEvent[] = [];
|
||||
inputHandler.onColor(ev => stack.push(ev));
|
||||
await inputHandler.parseP('\x1b]112\x07');
|
||||
assert.deepEqual(stack, [[{ type: ColorRequestType.RESTORE, index: ColorIndex.CURSOR }]]);
|
||||
});
|
||||
});
|
||||
|
||||
|
||||
@@ -4,7 +4,7 @@
|
||||
* @license MIT
|
||||
*/
|
||||
|
||||
import { IInputHandler, IAttributeData, IDisposable, IWindowOptions, IColorEvent, IParseStack, ColorIndex } 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';
|
||||
@@ -421,11 +421,15 @@ export class InputHandler extends Disposable implements IInputHandler {
|
||||
// 51 - reserved for Emacs shell.
|
||||
// 52 - Manipulate Selection Data.
|
||||
// 104 ; c - Reset Color Number c.
|
||||
this._parser.registerOscHandler(104, new OscHandler(data => this.restoreIndexedColor(data)));
|
||||
// 105 ; c - Reset Special Color Number c.
|
||||
// 106 ; c; f - Enable/disable Special Color Number c.
|
||||
// 110 - Reset VT100 text foreground color.
|
||||
this._parser.registerOscHandler(110, new OscHandler(data => this.restoreFgColor(data)));
|
||||
// 111 - Reset VT100 text background color.
|
||||
this._parser.registerOscHandler(111, new OscHandler(data => this.restoreBgColor(data)));
|
||||
// 112 - Reset text cursor color.
|
||||
this._parser.registerOscHandler(112, new OscHandler(data => this.restoreCursorColor(data)));
|
||||
// 113 - Reset mouse foreground color.
|
||||
// 114 - Reset mouse background color.
|
||||
// 115 - Reset Tektronix foreground color.
|
||||
@@ -2861,11 +2865,11 @@ export class InputHandler extends Disposable implements IInputHandler {
|
||||
const index = parseInt(idx);
|
||||
if (0 <= index && index < 256) {
|
||||
if (spec === '?') {
|
||||
event.push({ index });
|
||||
event.push({ type: ColorRequestType.REPORT, index });
|
||||
} else {
|
||||
const color = parseColor(spec);
|
||||
if (color) {
|
||||
event.push({ index, color });
|
||||
event.push({ type: ColorRequestType.SET, index, color });
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -2890,11 +2894,11 @@ export class InputHandler extends Disposable implements IInputHandler {
|
||||
for (let i = 0; i < slots.length; ++i, ++offset) {
|
||||
if (offset >= this._specialColors.length) break;
|
||||
if (slots[i] === '?') {
|
||||
this._onColor.fire([{ index: this._specialColors[offset] }]);
|
||||
this._onColor.fire([{ type: ColorRequestType.REPORT, index: this._specialColors[offset] }]);
|
||||
} else {
|
||||
const color = parseColor(slots[i]);
|
||||
if (color) {
|
||||
this._onColor.fire([{ index: this._specialColors[offset], color }]);
|
||||
this._onColor.fire([{ type: ColorRequestType.SET, index: this._specialColors[offset], color }]);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -2945,6 +2949,65 @@ export class InputHandler extends Disposable implements IInputHandler {
|
||||
return this._setOrReportSpecialColor(data, 2);
|
||||
}
|
||||
|
||||
/**
|
||||
* OSC 104 ; <num> ST - restore ANSI color <num>
|
||||
*
|
||||
* @vt: #Y OSC 104 "Reset ANSI color" "OSC 104 ; c BEL" "Reset color number `c` to themed color."
|
||||
* `c` is the color index between 0 and 255. This function restores the default color for `c` as
|
||||
* specified by the loaded theme. Any number of `c` parameters may be given.
|
||||
* If no parameters are given, the entire indexed color table will be reset.
|
||||
*/
|
||||
public restoreIndexedColor(data: string): boolean {
|
||||
if (!data) {
|
||||
this._onColor.fire([{ type: ColorRequestType.RESTORE }]);
|
||||
return true;
|
||||
}
|
||||
const event: IColorEvent = [];
|
||||
const slots = data.split(';');
|
||||
for (let i = 0; i < slots.length; ++i) {
|
||||
if (/^\d+$/.exec(slots[i])) {
|
||||
const index = parseInt(slots[i]);
|
||||
if (0 <= index && index < 256) {
|
||||
event.push({ type: ColorRequestType.RESTORE, index });
|
||||
}
|
||||
}
|
||||
}
|
||||
if (event.length) {
|
||||
this._onColor.fire(event);
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
/**
|
||||
* OSC 110 ST - restore default foreground color
|
||||
*
|
||||
* @vt: #Y OSC 110 "Restore default foreground color" "OSC 110 BEL" "Restore default foreground to themed color."
|
||||
*/
|
||||
public restoreFgColor(data: string): boolean {
|
||||
this._onColor.fire([{ type: ColorRequestType.RESTORE, index: ColorIndex.FOREGROUND }]);
|
||||
return true;
|
||||
}
|
||||
|
||||
/**
|
||||
* OSC 111 ST - restore default background color
|
||||
*
|
||||
* @vt: #Y OSC 111 "Restore default background color" "OSC 111 BEL" "Restore default background to themed color."
|
||||
*/
|
||||
public restoreBgColor(data: string): boolean {
|
||||
this._onColor.fire([{ type: ColorRequestType.RESTORE, index: ColorIndex.BACKGROUND }]);
|
||||
return true;
|
||||
}
|
||||
|
||||
/**
|
||||
* OSC 112 ST - restore default cursor color
|
||||
*
|
||||
* @vt: #Y OSC 112 "Restore default cursor color" "OSC 112 BEL" "Restore default cursor to themed color."
|
||||
*/
|
||||
public restoreCursorColor(data: string): boolean {
|
||||
this._onColor.fire([{ type: ColorRequestType.RESTORE, index: ColorIndex.CURSOR }]);
|
||||
return true;
|
||||
}
|
||||
|
||||
/**
|
||||
* ESC E
|
||||
* C1.NEL
|
||||
|
||||
Vendored
+19
-4
@@ -348,20 +348,31 @@ export interface IWindowOptions {
|
||||
setWinLines?: boolean;
|
||||
}
|
||||
|
||||
// color events from common, used for OSC 4/10/11/12
|
||||
// color events from common, used for OSC 4/10/11/12 and 104/110/111/112
|
||||
export const enum ColorRequestType {
|
||||
REPORT = 0,
|
||||
SET = 1,
|
||||
RESTORE = 2
|
||||
}
|
||||
export const enum ColorIndex {
|
||||
FOREGROUND = 256,
|
||||
BACKGROUND = 257,
|
||||
CURSOR = 258
|
||||
}
|
||||
export interface IColorReportRequest {
|
||||
type: ColorRequestType.REPORT;
|
||||
index: ColorIndex;
|
||||
color?: IColorRGB;
|
||||
}
|
||||
export interface IColorSetRequest extends IColorReportRequest {
|
||||
export interface IColorSetRequest {
|
||||
type: ColorRequestType.SET;
|
||||
index: ColorIndex;
|
||||
color: IColorRGB;
|
||||
}
|
||||
export type IColorEvent = (IColorReportRequest | IColorSetRequest)[];
|
||||
export interface IColorRestoreRequest {
|
||||
type: ColorRequestType.RESTORE;
|
||||
index?: ColorIndex;
|
||||
}
|
||||
export type IColorEvent = (IColorReportRequest | IColorSetRequest | IColorRestoreRequest)[];
|
||||
|
||||
|
||||
/**
|
||||
@@ -433,6 +444,10 @@ export interface IInputHandler {
|
||||
/** OSC 10 */ setOrReportFgColor(data: string): boolean;
|
||||
/** OSC 11 */ setOrReportBgColor(data: string): boolean;
|
||||
/** OSC 12 */ setOrReportCursorColor(data: string): boolean;
|
||||
/** OSC 104 */ restoreIndexedColor(data: string): boolean;
|
||||
/** OSC 110 */ restoreFgColor(data: string): boolean;
|
||||
/** OSC 111 */ restoreBgColor(data: string): boolean;
|
||||
/** OSC 112 */ restoreCursorColor(data: string): boolean;
|
||||
|
||||
/** ESC E */ nextLine(): boolean;
|
||||
/** ESC = */ keypadApplicationMode(): boolean;
|
||||
|
||||
@@ -435,7 +435,51 @@ describe('InputHandler Integration Tests', function(): void {
|
||||
assert.deepEqual(await page.evaluate('window._recordedData'), restore);
|
||||
});
|
||||
});
|
||||
describe('OSC 10 & 11', () => {
|
||||
describe('OSC 4 & 104', () => {
|
||||
before(async () => {
|
||||
await page.evaluate('(() => {window._recordedData = []; window._h = term.onData(d => window._recordedData.push(d));})()');
|
||||
});
|
||||
after(async () => {
|
||||
await page.evaluate('window._h.dispose()');
|
||||
});
|
||||
beforeEach(async () => {
|
||||
await page.evaluate('window._recordedData.length = 0;');
|
||||
});
|
||||
it('change & restore single color', async () => {
|
||||
// test for some random color slots
|
||||
for (const i of [0, 43, 77, 255]) {
|
||||
await writeSync(page, `\x1b]4;${i};?\x07`);
|
||||
const restore: string[] = await page.evaluate('window._recordedData');
|
||||
await writeSync(page, `\x1b]4;${i};rgb:01/02/03\x07\x1b]4;${i};?\x07`);
|
||||
assert.deepEqual(await page.evaluate('window._recordedData'), [restore[0], `\x1b]4;${i};rgb:0101/0202/0303\x07`]);
|
||||
// restore slot color
|
||||
await writeSync(page, `\x1b]104;${i}\x07\x1b]4;${i};?\x07`);
|
||||
assert.deepEqual(await page.evaluate('window._recordedData'), [restore[0], `\x1b]4;${i};rgb:0101/0202/0303\x07`, restore[0]]);
|
||||
await page.evaluate('window._recordedData.length = 0;');
|
||||
}
|
||||
});
|
||||
it('restore multiple at once', async () => {
|
||||
// change 3 random slots
|
||||
await writeSync(page, `\x1b]4;0;?;43;?;77;?\x07`);
|
||||
const restore: string[] = await page.evaluate('window._recordedData');
|
||||
await page.evaluate('window._recordedData.length = 0;');
|
||||
await writeSync(page, `\x1b]4;0;rgb:01/02/03;43;#aabbcc;77;#123456\x07`);
|
||||
// restore specific slots
|
||||
await writeSync(page, `\x1b]104;0;43;77\x07` + `\x1b]4;0;?;43;?;77;?\x07`);
|
||||
assert.deepEqual(await page.evaluate('window._recordedData'), restore);
|
||||
});
|
||||
it('restore full table', async () => {
|
||||
// change 3 random slots
|
||||
await writeSync(page, `\x1b]4;0;?;43;?;77;?\x07`);
|
||||
const restore: string[] = await page.evaluate('window._recordedData');
|
||||
await page.evaluate('window._recordedData.length = 0;');
|
||||
await writeSync(page, `\x1b]4;0;rgb:01/02/03;43;#aabbcc;77;#123456\x07`);
|
||||
// restore all
|
||||
await writeSync(page, `\x1b]104\x07` + `\x1b]4;0;?;43;?;77;?\x07`);
|
||||
assert.deepEqual(await page.evaluate('window._recordedData'), restore);
|
||||
});
|
||||
});
|
||||
describe('OSC 10 & 11 + 110 | 111 | 112', () => {
|
||||
before(async () => {
|
||||
await page.evaluate('(() => {window._recordedData = []; window._h = term.onData(d => window._recordedData.push(d));})()');
|
||||
});
|
||||
@@ -480,8 +524,31 @@ describe('InputHandler Integration Tests', function(): void {
|
||||
assert.deepEqual(await page.evaluate('window._recordedData'), ['\x1b]10;rgb:1212/3434/5656\x07', '\x1b]11;rgb:aaaa/bbbb/cccc\x07']);
|
||||
await writeSync(page, '\x1b]10;#ffffff;#000000\x07');
|
||||
});
|
||||
it('OSC 110: restore FG color', async () => {
|
||||
await writeSync(page, '\x1b]10;rgb:1/2/3\x07\x1b]10;?\x07');
|
||||
assert.deepEqual(await page.evaluate('window._recordedData'), ['\x1b]10;rgb:1111/2222/3333\x07']);
|
||||
await page.evaluate('window._recordedData.length = 0;');
|
||||
// restore
|
||||
await writeSync(page, '\x1b]110\x07\x1b]10;?\x07');
|
||||
assert.deepEqual(await page.evaluate('window._recordedData'), ['\x1b]10;rgb:ffff/ffff/ffff\x07']);
|
||||
});
|
||||
it('OSC 111: restore BG color', async () => {
|
||||
await writeSync(page, '\x1b]11;rgb:1/2/3\x07\x1b]11;?\x07');
|
||||
assert.deepEqual(await page.evaluate('window._recordedData'), ['\x1b]11;rgb:1111/2222/3333\x07']);
|
||||
await page.evaluate('window._recordedData.length = 0;');
|
||||
// restore
|
||||
await writeSync(page, '\x1b]111\x07\x1b]11;?\x07');
|
||||
assert.deepEqual(await page.evaluate('window._recordedData'), ['\x1b]11;rgb:0000/0000/0000\x07']);
|
||||
});
|
||||
it('OSC 112: restore cursor color', async () => {
|
||||
await writeSync(page, '\x1b]12;rgb:1/2/3\x07\x1b]12;?\x07');
|
||||
assert.deepEqual(await page.evaluate('window._recordedData'), ['\x1b]12;rgb:1111/2222/3333\x07']);
|
||||
await page.evaluate('window._recordedData.length = 0;');
|
||||
// restore
|
||||
await writeSync(page, '\x1b]112\x07\x1b]12;?\x07');
|
||||
assert.deepEqual(await page.evaluate('window._recordedData'), ['\x1b]12;rgb:ffff/ffff/ffff\x07']);
|
||||
});
|
||||
});
|
||||
|
||||
});
|
||||
|
||||
describe('ESC', () => {
|
||||
|
||||
Reference in New Issue
Block a user