mirror of
https://github.com/wavetermdev/xterm.js.git
synced 2026-08-05 13:43:48 -07:00
extend logic to OSC 12 cursor color
This commit is contained in:
@@ -217,13 +217,6 @@ export namespace rgba {
|
||||
rgba: channels.toRgba(r, g, b)
|
||||
};
|
||||
}
|
||||
|
||||
/**
|
||||
* convert 0xRRGGBBAA to 0xAABBGGRR (32-bit representation on LE systems)
|
||||
*/
|
||||
export function toABGR32(rgba: number): number {
|
||||
return ((rgba & 0xFF) << 24 | (rgba >>> 8 & 0xFF) << 16 | (rgba >>> 16 & 0xFF) << 8 | rgba >>> 24 & 0xFF) >>> 0;
|
||||
}
|
||||
}
|
||||
|
||||
export function toPaddedHex(c: number): string {
|
||||
|
||||
+29
-8
@@ -178,23 +178,44 @@ export class Terminal extends CoreTerminal implements ITerminal {
|
||||
this.register(this._bufferService.onResize(e => this._afterResize(e.cols, e.rows)));
|
||||
}
|
||||
|
||||
/**
|
||||
* 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.
|
||||
*/
|
||||
private _handleColorEvent(event: IColorEvent): void {
|
||||
if (!this._colorManager) return;
|
||||
for (const req of event) {
|
||||
let acc: 'foreground' | 'background' | 'cursor' | 'ansi' | undefined = undefined;
|
||||
let ident = '';
|
||||
switch (req.index) {
|
||||
case ColorIndex.FOREGROUND: // OSC 10
|
||||
if (req.color) this._colorManager.colors.foreground = rgba.toColor(...req.color);
|
||||
else this.coreService.triggerDataEvent(`${C0.ESC}]10;${toRgbString(color.toColorRGB(this._colorManager.colors.foreground))}${C0.BEL}`);
|
||||
acc = 'foreground';
|
||||
ident = '10';
|
||||
break;
|
||||
case ColorIndex.BACKGROUND: // OSC 11
|
||||
if (req.color) this._colorManager.colors.background = rgba.toColor(...req.color);
|
||||
else this.coreService.triggerDataEvent(`${C0.ESC}]11;${toRgbString(color.toColorRGB(this._colorManager.colors.background))}${C0.BEL}`);
|
||||
acc = 'background';
|
||||
ident = '11';
|
||||
break;
|
||||
case ColorIndex.CURSOR: // OSC 12
|
||||
acc = 'cursor';
|
||||
ident = '12';
|
||||
break;
|
||||
default: // OSC 4
|
||||
if (0 <= req.index && req.index < 256) {
|
||||
if (req.color) this._colorManager.colors.ansi[req.index] = rgba.toColor(...req.color);
|
||||
else this.coreService.triggerDataEvent(`${C0.ESC}]4;${req.index};${toRgbString(color.toColorRGB(this._colorManager.colors.ansi[req.index]))}${C0.BEL}`);
|
||||
}
|
||||
// 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}`);
|
||||
}
|
||||
}
|
||||
}
|
||||
this._renderService?.setColors(this._colorManager.colors);
|
||||
|
||||
@@ -1902,16 +1902,16 @@ describe('InputHandler', () => {
|
||||
stack.length = 0;
|
||||
});
|
||||
|
||||
it('10: should create appropriate events', async () => {
|
||||
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 }]]);
|
||||
stack.length = 0;
|
||||
// OSC with multiple values maps to OSC 10 & OSC 11
|
||||
// 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 }]]);
|
||||
assert.deepEqual(stack, [[{ index: ColorIndex.FOREGROUND }], [{ index: ColorIndex.BACKGROUND }], [{ index: ColorIndex.CURSOR }]]);
|
||||
stack.length = 0;
|
||||
// set foreground color events
|
||||
await inputHandler.parseP('\x1b]10;rgb:01/02/03\x07');
|
||||
@@ -1920,23 +1920,24 @@ describe('InputHandler', () => {
|
||||
await inputHandler.parseP('\x1b]10;#aabbcc\x07');
|
||||
assert.deepEqual(stack, [[{ index: ColorIndex.FOREGROUND, color: [170, 187, 204] }]]);
|
||||
stack.length = 0;
|
||||
// set FG and BG at once
|
||||
await inputHandler.parseP('\x1b]10;rgb:aa/bb/cc;#001122\x07');
|
||||
// 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.BACKGROUND, color: [0, 17, 34] }],
|
||||
[{ index: ColorIndex.CURSOR, color: [18, 52, 86] }]
|
||||
]);
|
||||
});
|
||||
it('11: should create appropriate events', async () => {
|
||||
it('11: BG set & query events', async () => {
|
||||
const stack: IColorEvent[] = [];
|
||||
inputHandler.onColor(ev => stack.push(ev));
|
||||
// single foreground query --> color undefined
|
||||
// single background query --> color undefined
|
||||
await inputHandler.parseP('\x1b]11;?\x07');
|
||||
assert.deepEqual(stack, [[{ index: ColorIndex.BACKGROUND }]]);
|
||||
stack.length = 0;
|
||||
// OSC 11 with multiple values creates only one BG event
|
||||
// OSC 11 with multiple values creates only BG and cursor event
|
||||
await inputHandler.parseP('\x1b]11;?;?;?;?\x07');
|
||||
assert.deepEqual(stack, [[{ index: ColorIndex.BACKGROUND }]]);
|
||||
assert.deepEqual(stack, [[{ index: ColorIndex.BACKGROUND }], [{ index: ColorIndex.CURSOR }]]);
|
||||
stack.length = 0;
|
||||
// set background color events
|
||||
await inputHandler.parseP('\x1b]11;rgb:01/02/03\x07');
|
||||
@@ -1944,6 +1945,31 @@ describe('InputHandler', () => {
|
||||
stack.length = 0;
|
||||
await inputHandler.parseP('\x1b]11;#aabbcc\x07');
|
||||
assert.deepEqual(stack, [[{ 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] }]
|
||||
]);
|
||||
});
|
||||
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 }]]);
|
||||
stack.length = 0;
|
||||
// OSC 12 with multiple values creates only cursor event
|
||||
await inputHandler.parseP('\x1b]12;?;?;?;?\x07');
|
||||
assert.deepEqual(stack, [[{ 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] }]]);
|
||||
stack.length = 0;
|
||||
await inputHandler.parseP('\x1b]12;#aabbcc\x07');
|
||||
assert.deepEqual(stack, [[{ index: ColorIndex.CURSOR, color: [170, 187, 204] }]]);
|
||||
});
|
||||
});
|
||||
|
||||
|
||||
+48
-33
@@ -408,6 +408,7 @@ export class InputHandler extends Disposable implements IInputHandler {
|
||||
// 11 - Change VT100 text background color to Pt.
|
||||
this._parser.registerOscHandler(11, new OscHandler(data => this.setOrReportBgColor(data)));
|
||||
// 12 - Change text cursor color to Pt.
|
||||
this._parser.registerOscHandler(12, new OscHandler(data => this.setOrReportCursorColor(data)));
|
||||
// 13 - Change mouse foreground color to Pt.
|
||||
// 14 - Change mouse background color to Pt.
|
||||
// 15 - Change Tektronix foreground color to Pt.
|
||||
@@ -2846,8 +2847,9 @@ export class InputHandler extends Disposable implements IInputHandler {
|
||||
* OSC 4; <num> ; <text> ST (set ANSI color <num> to <text>)
|
||||
*
|
||||
* @vt: #Y OSC 4 "Set ANSI color" "OSC 4 ; c ; spec BEL" "Change color number `c` to the color specified by `spec`."
|
||||
* `c` is the color index between 0 and 255. `spec` color format is 'rgb:hh/hh/hh' where `h` are hexadecimal digits.
|
||||
* There may be multipe c ; spec elements present in the same instruction, e.g. 1;rgb:10/20/30;2;rgb:a0/b0/c0.
|
||||
* `c` is the color index between 0 and 255. The color format of `spec` is derived from `XParseColor` (see OSC 10 for supported formats).
|
||||
* There may be multipe `c ; spec` pairs present in the same instruction.
|
||||
* If `spec` contains `?` the terminal returns a sequence with the currently set color.
|
||||
*/
|
||||
public setOrReportIndexedColor(data: string): boolean {
|
||||
const event: IColorEvent = [];
|
||||
@@ -2856,12 +2858,15 @@ export class InputHandler extends Disposable implements IInputHandler {
|
||||
const idx = slots.shift() as string;
|
||||
const spec = slots.shift() as string;
|
||||
if (/^\d+$/.exec(idx)) {
|
||||
if (spec === '?') {
|
||||
event.push({ index: parseInt(idx) });
|
||||
} else {
|
||||
const color = parseColor(spec);
|
||||
if (color) {
|
||||
event.push({ index: parseInt(idx), color });
|
||||
const index = parseInt(idx);
|
||||
if (0 <= index && index < 256) {
|
||||
if (spec === '?') {
|
||||
event.push({ index });
|
||||
} else {
|
||||
const color = parseColor(spec);
|
||||
if (color) {
|
||||
event.push({ index, color });
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -2872,6 +2877,30 @@ export class InputHandler extends Disposable implements IInputHandler {
|
||||
return true;
|
||||
}
|
||||
|
||||
// special colors - OSC 10 | 11 | 12
|
||||
private _specialColors = [ColorIndex.FOREGROUND, ColorIndex.BACKGROUND, ColorIndex.CURSOR];
|
||||
|
||||
/**
|
||||
* Apply colors requests for special colors in OSC 10 | 11 | 12.
|
||||
* Since these commands are stacking from multiple parameters,
|
||||
* we handle them in a loop with an entry offset to `_specialColors`.
|
||||
*/
|
||||
private _setOrReportSpecialColor(data: string, offset: number): boolean {
|
||||
const slots = data.split(';');
|
||||
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] }]);
|
||||
} else {
|
||||
const color = parseColor(slots[i]);
|
||||
if (color) {
|
||||
this._onColor.fire([{ index: this._specialColors[offset], color }]);
|
||||
}
|
||||
}
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
/**
|
||||
* OSC 10 ; <xcolor name>|<?> ST - set or query default foreground color
|
||||
*
|
||||
@@ -2895,21 +2924,7 @@ export class InputHandler extends Disposable implements IInputHandler {
|
||||
* Therefore stacking multiple `Pt` separated by `;` only works for the first two entries.
|
||||
*/
|
||||
public setOrReportFgColor(data: string): boolean {
|
||||
// Note: data may contain multiple values separated with ; mapping to OSC 10 - 19
|
||||
const slots = data.split(';');
|
||||
if (slots[0] === '?') {
|
||||
this._onColor.fire([{ index: ColorIndex.FOREGROUND }]);
|
||||
} else {
|
||||
const color = parseColor(slots[0]);
|
||||
if (color) {
|
||||
this._onColor.fire([{ index: ColorIndex.FOREGROUND, color }]);
|
||||
}
|
||||
}
|
||||
// forward second slot to OSC 11 (higher slots are not supported)
|
||||
if (slots.length > 1) {
|
||||
this.setOrReportBgColor(slots[1]);
|
||||
}
|
||||
return true;
|
||||
return this._setOrReportSpecialColor(data, 0);
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -2918,16 +2933,16 @@ export class InputHandler extends Disposable implements IInputHandler {
|
||||
* @vt: #Y OSC 11 "Set or query default background color" "OSC 11 ; Pt BEL" "Same as OSC 10, but for default background."
|
||||
*/
|
||||
public setOrReportBgColor(data: string): boolean {
|
||||
const slots = data.split(';');
|
||||
if (slots[0] === '?') {
|
||||
this._onColor.fire([{ index: ColorIndex.BACKGROUND }]);
|
||||
} else {
|
||||
const color = parseColor(slots[0]);
|
||||
if (color) {
|
||||
this._onColor.fire([{ index: ColorIndex.BACKGROUND, color }]);
|
||||
}
|
||||
}
|
||||
return true;
|
||||
return this._setOrReportSpecialColor(data, 1);
|
||||
}
|
||||
|
||||
/**
|
||||
* OSC 12 ; <xcolor name>|<?> ST - set or query default cursor color
|
||||
*
|
||||
* @vt: #Y OSC 12 "Set or query default cursor color" "OSC 12 ; Pt BEL" "Same as OSC 10, but for default cursor color."
|
||||
*/
|
||||
public setOrReportCursorColor(data: string): boolean {
|
||||
return this._setOrReportSpecialColor(data, 2);
|
||||
}
|
||||
|
||||
/**
|
||||
|
||||
Vendored
+4
-2
@@ -348,10 +348,11 @@ export interface IWindowOptions {
|
||||
setWinLines?: boolean;
|
||||
}
|
||||
|
||||
// color events from common, used for OSC 4/10/11
|
||||
// color events from common, used for OSC 4/10/11/12
|
||||
export const enum ColorIndex {
|
||||
FOREGROUND = 256,
|
||||
BACKGROUND = 257
|
||||
BACKGROUND = 257,
|
||||
CURSOR = 258
|
||||
}
|
||||
export interface IColorReportRequest {
|
||||
index: ColorIndex;
|
||||
@@ -431,6 +432,7 @@ export interface IInputHandler {
|
||||
/** OSC 4 */ setOrReportIndexedColor(data: string): boolean;
|
||||
/** OSC 10 */ setOrReportFgColor(data: string): boolean;
|
||||
/** OSC 11 */ setOrReportBgColor(data: string): boolean;
|
||||
/** OSC 12 */ setOrReportCursorColor(data: string): boolean;
|
||||
|
||||
/** ESC E */ nextLine(): boolean;
|
||||
/** ESC = */ keypadApplicationMode(): boolean;
|
||||
|
||||
@@ -469,6 +469,12 @@ describe('InputHandler Integration Tests', function(): void {
|
||||
await writeSync(page, '\x1b]11;#000000\x07\x1b]11;?\x07');
|
||||
assert.deepEqual(await page.evaluate('window._recordedData'), ['\x1b]11;rgb:1111/2222/3333\x07', '\x1b]11;rgb:0000/0000/0000\x07']);
|
||||
});
|
||||
it('set & query 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 writeSync(page, '\x1b]12;#ffffff\x07\x1b]12;?\x07');
|
||||
assert.deepEqual(await page.evaluate('window._recordedData'), ['\x1b]12;rgb:1111/2222/3333\x07', '\x1b]12;rgb:ffff/ffff/ffff\x07']);
|
||||
});
|
||||
it('set & query FG & BG color in one call', async () => {
|
||||
await writeSync(page, '\x1b]10;#123456;rgb:aa/bb/cc\x07\x1b]10;?;?\x07');
|
||||
assert.deepEqual(await page.evaluate('window._recordedData'), ['\x1b]10;rgb:1212/3434/5656\x07', '\x1b]11;rgb:aaaa/bbbb/cccc\x07']);
|
||||
|
||||
Reference in New Issue
Block a user