diff --git a/src/common/InputHandler.test.ts b/src/common/InputHandler.test.ts index f3b0f41f..ff1879bc 100644 --- a/src/common/InputHandler.test.ts +++ b/src/common/InputHandler.test.ts @@ -55,8 +55,8 @@ class TestInputHandler extends InputHandler { } } - public parseXColorName(data: string): void | [number, number, number] { - return this._parseXColorName(data); + public parseColorSpec(data: string): void | [number, number, number] { + return this._parseColorSpec(data); } } @@ -1872,54 +1872,54 @@ describe('InputHandler', () => { describe('parse xcolor names', () => { it('rgb:// scheme in 4/8/12/16 bit', () => { // 4 bit - assert.deepEqual(inputHandler.parseXColorName('rgb:0/0/0'), [0, 0, 0]); - assert.deepEqual(inputHandler.parseXColorName('rgb:f/f/f'), [255, 255, 255]); - assert.deepEqual(inputHandler.parseXColorName('rgb:1/2/3'), [17, 34, 51]); + assert.deepEqual(inputHandler.parseColorSpec('rgb:0/0/0'), [0, 0, 0]); + assert.deepEqual(inputHandler.parseColorSpec('rgb:f/f/f'), [255, 255, 255]); + assert.deepEqual(inputHandler.parseColorSpec('rgb:1/2/3'), [17, 34, 51]); // 8 bit - assert.deepEqual(inputHandler.parseXColorName('rgb:00/00/00'), [0, 0, 0]); - assert.deepEqual(inputHandler.parseXColorName('rgb:ff/ff/ff'), [255, 255, 255]); - assert.deepEqual(inputHandler.parseXColorName('rgb:11/22/33'), [17, 34, 51]); + assert.deepEqual(inputHandler.parseColorSpec('rgb:00/00/00'), [0, 0, 0]); + assert.deepEqual(inputHandler.parseColorSpec('rgb:ff/ff/ff'), [255, 255, 255]); + assert.deepEqual(inputHandler.parseColorSpec('rgb:11/22/33'), [17, 34, 51]); // 12 bit - assert.deepEqual(inputHandler.parseXColorName('rgb:000/000/000'), [0, 0, 0]); - assert.deepEqual(inputHandler.parseXColorName('rgb:fff/fff/fff'), [255, 255, 255]); - assert.deepEqual(inputHandler.parseXColorName('rgb:111/222/333'), [17, 34, 51]); + assert.deepEqual(inputHandler.parseColorSpec('rgb:000/000/000'), [0, 0, 0]); + assert.deepEqual(inputHandler.parseColorSpec('rgb:fff/fff/fff'), [255, 255, 255]); + assert.deepEqual(inputHandler.parseColorSpec('rgb:111/222/333'), [17, 34, 51]); // 16 bit - assert.deepEqual(inputHandler.parseXColorName('rgb:0000/0000/0000'), [0, 0, 0]); - assert.deepEqual(inputHandler.parseXColorName('rgb:ffff/ffff/ffff'), [255, 255, 255]); - assert.deepEqual(inputHandler.parseXColorName('rgb:1111/2222/3333'), [17, 34, 51]); + assert.deepEqual(inputHandler.parseColorSpec('rgb:0000/0000/0000'), [0, 0, 0]); + assert.deepEqual(inputHandler.parseColorSpec('rgb:ffff/ffff/ffff'), [255, 255, 255]); + assert.deepEqual(inputHandler.parseColorSpec('rgb:1111/2222/3333'), [17, 34, 51]); }); it('#RGB scheme in 4/8/12/16 bit', () => { // 4 bit - assert.deepEqual(inputHandler.parseXColorName('#000'), [0, 0, 0]); - assert.deepEqual(inputHandler.parseXColorName('#fff'), [240, 240, 240]); - assert.deepEqual(inputHandler.parseXColorName('#123'), [16, 32, 48]); + assert.deepEqual(inputHandler.parseColorSpec('#000'), [0, 0, 0]); + assert.deepEqual(inputHandler.parseColorSpec('#fff'), [240, 240, 240]); + assert.deepEqual(inputHandler.parseColorSpec('#123'), [16, 32, 48]); // 8 bit - assert.deepEqual(inputHandler.parseXColorName('#000000'), [0, 0, 0]); - assert.deepEqual(inputHandler.parseXColorName('#ffffff'), [255, 255, 255]); - assert.deepEqual(inputHandler.parseXColorName('#112233'), [17, 34, 51]); + assert.deepEqual(inputHandler.parseColorSpec('#000000'), [0, 0, 0]); + assert.deepEqual(inputHandler.parseColorSpec('#ffffff'), [255, 255, 255]); + assert.deepEqual(inputHandler.parseColorSpec('#112233'), [17, 34, 51]); // 12 bit - assert.deepEqual(inputHandler.parseXColorName('#000000000'), [0, 0, 0]); - assert.deepEqual(inputHandler.parseXColorName('#fffffffff'), [255, 255, 255]); - assert.deepEqual(inputHandler.parseXColorName('#111222333'), [17, 34, 51]); + assert.deepEqual(inputHandler.parseColorSpec('#000000000'), [0, 0, 0]); + assert.deepEqual(inputHandler.parseColorSpec('#fffffffff'), [255, 255, 255]); + assert.deepEqual(inputHandler.parseColorSpec('#111222333'), [17, 34, 51]); // 16 bit - assert.deepEqual(inputHandler.parseXColorName('#000000000000'), [0, 0, 0]); - assert.deepEqual(inputHandler.parseXColorName('#ffffffffffff'), [255, 255, 255]); - assert.deepEqual(inputHandler.parseXColorName('#111122223333'), [17, 34, 51]); + assert.deepEqual(inputHandler.parseColorSpec('#000000000000'), [0, 0, 0]); + assert.deepEqual(inputHandler.parseColorSpec('#ffffffffffff'), [255, 255, 255]); + assert.deepEqual(inputHandler.parseColorSpec('#111122223333'), [17, 34, 51]); }); it('supports upper case', () => { - assert.deepEqual(inputHandler.parseXColorName('RGB:0/A/F'), [0, 170, 255]); - assert.deepEqual(inputHandler.parseXColorName('#FFF'), [240, 240, 240]); + assert.deepEqual(inputHandler.parseColorSpec('RGB:0/A/F'), [0, 170, 255]); + assert.deepEqual(inputHandler.parseColorSpec('#FFF'), [240, 240, 240]); }); it('does not parse illegal combinations', () => { // shifting bit width - assert.equal(inputHandler.parseXColorName('rgb:0/11/222'), undefined); + assert.equal(inputHandler.parseColorSpec('rgb:0/11/222'), undefined); // unsupported scheme - assert.equal(inputHandler.parseXColorName('rgbi:00/11/22'), undefined); + assert.equal(inputHandler.parseColorSpec('rgbi:00/11/22'), undefined); // broken # specifier - assert.equal(inputHandler.parseXColorName('#aabbbcc'), undefined); + assert.equal(inputHandler.parseColorSpec('#aabbbcc'), undefined); // out of range - assert.equal(inputHandler.parseXColorName('#aabbgg'), undefined); - assert.equal(inputHandler.parseXColorName('rgb:aa/bb/gg'), undefined); + assert.equal(inputHandler.parseColorSpec('#aabbgg'), undefined); + assert.equal(inputHandler.parseColorSpec('rgb:aa/bb/gg'), undefined); }); }); diff --git a/src/common/InputHandler.ts b/src/common/InputHandler.ts index ba51ebdd..f206ac8b 100644 --- a/src/common/InputHandler.ts +++ b/src/common/InputHandler.ts @@ -2866,7 +2866,7 @@ export class InputHandler extends Disposable implements IInputHandler { } /** - * Parse xcolor name to RGB values (8 bit per channel). + * Parse color spec to RGB values (8 bit per channel). * See `man xparsecolor` for details about certain format specifications. * * Supported formats: @@ -2876,7 +2876,7 @@ export class InputHandler extends Disposable implements IInputHandler { * All other formats like rgbi: or device-independent string specifications * with float numbering are not supported. */ - protected _parseXColorName(data: string): [number, number, number] | void { + protected _parseColorSpec(data: string): [number, number, number] | void { // also handle uppercases data = data.toLowerCase(); if (data.indexOf('rgb:') === 0) { @@ -2898,19 +2898,12 @@ export class InputHandler extends Disposable implements IInputHandler { const rex = /^[\da-f]+$/; if (rex.exec(data) && [3, 6, 9, 12].includes(data.length)) { const adv = data.length / 3; - const r = parseInt(data.slice(0, adv), 16); - const g = parseInt(data.slice(adv, 2 * adv), 16); - const b = parseInt(data.slice(2 * adv, 3 * adv), 16); - switch (adv) { - case 1: - return [r << 4, g << 4, b << 4]; - case 2: - return [r, g, b]; - case 3: - return [r >> 4, g >> 4, b >> 4]; - case 4: - return [r >> 8, g >> 8, b >> 8]; + const result: [number, number, number] = [0, 0, 0]; + for (let i = 0; i < 3; ++i) { + const c = parseInt(data.slice(adv * i, adv * i + adv), 16); + result[i] = adv === 1 ? c << 4 : adv === 2 ? c : adv === 3 ? c >> 4 : c >> 8; } + return result; } } } @@ -2933,20 +2926,36 @@ export class InputHandler extends Disposable implements IInputHandler { return true; } + /** + * OSC 10 ; | ST - set or query default foreground color + * + * @vt: #Y OSC 10 "Set or query default foreground color" "OSC 10 ; Pt BEL" "Set or query default foreground color." + * To set the color, the following color specification formats are supported: + * - `rgb://` for `, , ` in `h | hh | hhh | hhhh`, where + * `h` is a single hexadecimal digit (case insignificant). The different widths scale + * from 4 bit (`h`) to 16 bit (`hhhh`) and get converted to 8 bit (`hh`). + * - `#RGB` - 4 bits per channel, expanded to `#R0G0B0` + * - `#RRGGBB` - 8 bits per channel + * - `#RRRGGGBBB` - 12 bits per channel, truncated to `#RRGGBB` + * - `#RRRRGGGGBBBB` - 16 bits per channel, truncated to `#RRGGBB` + * + * If `Pt` contains `?` instead of a color specification, the terminal + * returns a sequence with the current default foreground color + * (use that sequence to restore the color after changes). + * + * **Note:** Other than xterm, xterm.js does not support OSC 12 - 19. + * Therefore stacking multiple `Pt` separated by `;` only works for the first two entries. + */ public queryOrSetFgColor(data: string): boolean { // note: data may contain multiple ? or color names separated with ; // Multiple values will map through to OSC 10 - 19, but we only support 10 and 11 currently, - // thus truncate to max. 2 occurences. + // thus truncate to max. 2 entries. const slots = data.split(';').slice(0, 2); if (slots[0] === '?') { - // TODO: query FG color - console.log('query FG'); this._onColor.fire({ requests: [{ index: 256, color: '?' }] }); } else { - const color = this._parseXColorName(slots[0]); + const color = this._parseColorSpec(slots[0]); if (color) { - // set new FG color - console.log('set FG', color); this._onColor.fire({ requests: [{ index: 256, color }] }); } } @@ -2956,17 +2965,18 @@ export class InputHandler extends Disposable implements IInputHandler { return true; } + /** + * OSC 11 ; | ST - set or query default background color + * + * @vt: #Y OSC 11 "Set or query default background color" "OSC 11 ; Pt BEL" "Same as OSC 10, but for default background." + */ public queryOrSetBgColor(data: string): boolean { const slots = data.split(';').slice(0, 1); if (slots[0] === '?') { - // TODO: query BG color - console.log('query BG'); this._onColor.fire({ requests: [{ index: 257, color: '?' }] }); } else { - const color = this._parseXColorName(slots[0]); + const color = this._parseColorSpec(slots[0]); if (color) { - // set new BG color - console.log('set BG', color); this._onColor.fire({ requests: [{ index: 257, color }] }); } }