From 413a885b5eab4cfea654b237e962acadf70ffb6f Mon Sep 17 00:00:00 2001 From: Ian K Date: Mon, 8 Aug 2022 12:41:12 -0500 Subject: [PATCH 01/10] handle if the newValue.length is less than the old value by deleting --- src/browser/input/CompositionHelper.ts | 11 +++++++++-- 1 file changed, 9 insertions(+), 2 deletions(-) diff --git a/src/browser/input/CompositionHelper.ts b/src/browser/input/CompositionHelper.ts index 61051b58..09d69d3a 100644 --- a/src/browser/input/CompositionHelper.ts +++ b/src/browser/input/CompositionHelper.ts @@ -5,6 +5,7 @@ import { IRenderService } from 'browser/services/Services'; import { IBufferService, ICoreService, IOptionsService } from 'common/services/Services'; +import { C0 } from 'common/data/EscapeSequences'; interface IPosition { start: number; @@ -186,11 +187,17 @@ export class CompositionHelper { // Ignore if a composition has started since the timeout if (!this._isComposing) { const newValue = this._textarea.value; + const diff = newValue.replace(oldValue, ''); - if (diff.length > 0) { - this._dataAlreadySent = diff; + + this._dataAlreadySent = diff; + + if (newValue.length > oldValue.length) { this._coreService.triggerDataEvent(diff, true); + } else if (newValue.length < oldValue.length) { + this._coreService.triggerDataEvent(`${C0.DEL}`, true); } + } }, 0); } From 531260269245f7c1bba32836474625cbf0520abc Mon Sep 17 00:00:00 2001 From: Ian K Date: Mon, 8 Aug 2022 17:02:21 -0500 Subject: [PATCH 02/10] handle same length case --- src/browser/input/CompositionHelper.ts | 2 ++ 1 file changed, 2 insertions(+) diff --git a/src/browser/input/CompositionHelper.ts b/src/browser/input/CompositionHelper.ts index 09d69d3a..39ccaa23 100644 --- a/src/browser/input/CompositionHelper.ts +++ b/src/browser/input/CompositionHelper.ts @@ -196,6 +196,8 @@ export class CompositionHelper { this._coreService.triggerDataEvent(diff, true); } else if (newValue.length < oldValue.length) { this._coreService.triggerDataEvent(`${C0.DEL}`, true); + } else if ((newValue.length === oldValue.length) && (newValue !== oldValue)) { + this._coreService.triggerDataEvent(newValue, true); } } From bb4fbf265991b5dacda8c41903acfa781375968d Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?J=C3=B6rg=20Breitbart?= Date: Fri, 2 Sep 2022 01:43:31 +0200 Subject: [PATCH 03/10] DECRQM impl --- src/common/InputHandler.ts | 64 +++++++++++++++++++++++++++++++++++++- 1 file changed, 63 insertions(+), 1 deletion(-) diff --git a/src/common/InputHandler.ts b/src/common/InputHandler.ts index 8d55f1bb..606d0246 100644 --- a/src/common/InputHandler.ts +++ b/src/common/InputHandler.ts @@ -369,6 +369,8 @@ 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: 'p' }, params => this.requestMode(params, true)); + this._parser.registerCsiHandler({ prefix: '?', intermediates: '$', final: 'p' }, params => this.requestMode(params, false)); /** * execute handler @@ -2215,7 +2217,7 @@ export class InputHandler extends Disposable implements IInputHandler { case 1015: // urxvt ext mode mouse - removed in #2507 this._logService.debug('DECRST 1015 not supported (see #2507)'); break; - case 1006: // sgr pixels mode mouse + case 1016: // sgr pixels mode mouse this._coreMouseService.activeEncoding = 'DEFAULT'; break; case 25: // hide cursor @@ -2245,6 +2247,66 @@ export class InputHandler extends Disposable implements IInputHandler { return true; } + /** + * CSI Ps $ p Request ANSI Mode (DECRQM). + * + * Reports CSI Ps; Pm $ y (DECRPM), where Ps is the mode number as in SM/RM, + * and Pm is the mode value: + * 0 - not recognized + * 1 - set + * 2 - reset + * 3 - permanently set + * 4 - permanently reset + * + * @vt: #Y CSI DECRQM "Request Mode" "CSI Ps $p" "Request mode state." + */ + public requestMode(params: IParams, ansi: boolean): boolean { + const f = (m: number, v: boolean | number) => this._coreService.triggerDataEvent(`${C0.ESC}[?${m};${2 - +v}$y`); + const p = params.params[0]; + + if (ansi) { + switch (p) { + case 2: f(p, -1); break; + case 4: f(p, this._coreService.modes.insertMode); break; + case 12: f(p, -2); break; + case 20: f(p, -2); break; + default: f(p, 2); + } + return true; + } + + // dec privates + switch (p) { + case 1: f(p, this._coreService.decPrivateModes.applicationCursorKeys); break; + case 3: + const cols = this._bufferService.cols; + f(p, this._optionsService.rawOptions.windowOptions.setWinLines ? (cols === 80 ? 0 : cols === 132 ? 1 : 2) : 2); + break; + case 6: f(p, this._coreService.decPrivateModes.origin); break; + case 7: f(p, this._coreService.decPrivateModes.wraparound); break; + case 9: f(p, this._coreMouseService.activeProtocol === 'X10'); break; + case 12: f(p, -2); break; + case 25: f(p, !this._coreService.isCursorHidden); break; + case 45: f(p, this._coreService.decPrivateModes.reverseWraparound); break; + case 66: f(p, this._coreService.decPrivateModes.applicationKeypad); break; + case 1000: f(p, this._coreMouseService.activeProtocol === 'VT200'); break; + case 1002: f(p, this._coreMouseService.activeProtocol === 'DRAG'); break; + case 1003: f(p, this._coreMouseService.activeProtocol === 'ANY'); break; + case 1004: f(p, this._coreService.decPrivateModes.sendFocus); break; + case 1005: f(p, -2); break; + case 1006: f(p, this._coreMouseService.activeEncoding === 'SGR'); break; + case 1015: f(p, -2); break; + case 1016: f(p, this._coreMouseService.activeEncoding === 'SGR_PIXELS'); break; + case 47: + case 1047: + case 1048: + case 1049: f(p, this._bufferService.buffers.active === this._bufferService.buffers.alt); break; + case 2004: f(p, this._coreService.decPrivateModes.bracketedPasteMode); break; + default: f(p, 2); + } + return true; + } + /** * Helper to write color information packed with color mode. */ From df1d72aee50beaf54a35c7c4c5b79458c10fff3c Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?J=C3=B6rg=20Breitbart?= Date: Fri, 2 Sep 2022 01:51:27 +0200 Subject: [PATCH 04/10] make linter happy --- src/common/InputHandler.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/common/InputHandler.ts b/src/common/InputHandler.ts index 606d0246..5d3d5d54 100644 --- a/src/common/InputHandler.ts +++ b/src/common/InputHandler.ts @@ -2261,7 +2261,7 @@ export class InputHandler extends Disposable implements IInputHandler { * @vt: #Y CSI DECRQM "Request Mode" "CSI Ps $p" "Request mode state." */ public requestMode(params: IParams, ansi: boolean): boolean { - const f = (m: number, v: boolean | number) => this._coreService.triggerDataEvent(`${C0.ESC}[?${m};${2 - +v}$y`); + const f = (m: number, v: boolean | number): void => this._coreService.triggerDataEvent(`${C0.ESC}[?${m};${2 - +v}$y`); const p = params.params[0]; if (ansi) { From 65f0f9a1d4127b4a54f1fecc79bcb99b53cfe57d Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?J=C3=B6rg=20Breitbart?= Date: Sat, 3 Sep 2022 12:14:53 +0200 Subject: [PATCH 05/10] restructure code, enable DECSET 12 and SM 20 --- src/common/InputHandler.ts | 120 +++++++++++++++++++++++-------------- 1 file changed, 75 insertions(+), 45 deletions(-) diff --git a/src/common/InputHandler.ts b/src/common/InputHandler.ts index 5d3d5d54..18db078f 100644 --- a/src/common/InputHandler.ts +++ b/src/common/InputHandler.ts @@ -1789,7 +1789,7 @@ export class InputHandler extends Disposable implements IInputHandler { * | 2 | Keyboard Action Mode (KAM). Always on. | #N | * | 4 | Insert Mode (IRM). | #Y | * | 12 | Send/receive (SRM). Always off. | #N | - * | 20 | Automatic Newline (LNM). Always off. | #N | + * | 20 | Automatic Newline (LNM). | #Y | */ public setMode(params: IParams): boolean { for (let i = 0; i < params.length; i++) { @@ -1798,7 +1798,7 @@ export class InputHandler extends Disposable implements IInputHandler { this._coreService.modes.insertMode = true; break; case 20: - // this._t.convertEol = true; + this._optionsService.options.convertEol = true; break; } } @@ -1951,7 +1951,7 @@ export class InputHandler extends Disposable implements IInputHandler { this._coreService.decPrivateModes.wraparound = true; break; case 12: - // this.cursorBlink = true; + this._optionsService.options.cursorBlink = true; break; case 45: this._coreService.decPrivateModes.reverseWraparound = true; @@ -2035,7 +2035,7 @@ export class InputHandler extends Disposable implements IInputHandler { * | 2 | Keyboard Action Mode (KAM). Always on. | #N | * | 4 | Replace Mode (IRM). (default) | #Y | * | 12 | Send/receive (SRM). Always off. | #N | - * | 20 | Normal Linefeed (LNM). Always off. | #N | + * | 20 | Normal Linefeed (LNM). | #Y | * * * FIXME: why is LNM commented out? @@ -2047,7 +2047,7 @@ export class InputHandler extends Disposable implements IInputHandler { this._coreService.modes.insertMode = false; break; case 20: - // this._t.convertEol = false; + this._optionsService.options.convertEol = false; break; } } @@ -2189,7 +2189,7 @@ export class InputHandler extends Disposable implements IInputHandler { this._coreService.decPrivateModes.wraparound = false; break; case 12: - // this.cursorBlink = false; + this._optionsService.options.cursorBlink = false; break; case 45: this._coreService.decPrivateModes.reverseWraparound = false; @@ -2259,52 +2259,82 @@ export class InputHandler extends Disposable implements IInputHandler { * 4 - permanently reset * * @vt: #Y CSI DECRQM "Request Mode" "CSI Ps $p" "Request mode state." + * Returns a report as `CSI Ps; Pm $ y` (DECRPM), where `Ps` is the mode number as in SM/RM + * or DECSET/DECRST, and `Pm` is the mode value: + * - 0: not recognized + * - 1: set + * - 2: reset + * - 3: permanently set + * - 4: permanently reset + * + * For modes not understood xterm.js always returns `notRecognized`. In general this means, + * that a certain operation mode is not implemented and cannot be used. + * + * Modes changing the active terminal buffer (47, 1047, 1048, 1049) are not subqueried + * and only report, whether the alternate buffer is set. + * + * Mouse encodings and mouse protocols are handled mutual exclusive, + * thus only one of each of those can be set at a given time. + * + * There is a chance, that some mode reports are not fully in line with xterm.js' behavior, + * e.g. if the default implementation already exposes a certain behavior. If you find + * discrepancies in the mode reports, please file a bug. */ public requestMode(params: IParams, ansi: boolean): boolean { - const f = (m: number, v: boolean | number): void => this._coreService.triggerDataEvent(`${C0.ESC}[?${m};${2 - +v}$y`); + // return value as in DECRPM + const enum V { + NOT_RECOGNIZED = 0, + SET = 1, + RESET = 2, + PERMANENTLY_SET = 3, + PERMANENTLY_RESET = 4 + } + + // access helpers + const dm = this._coreService.decPrivateModes; + const { activeProtocol: mouseProtocol, activeEncoding: mouseEncoding } = this._coreMouseService; + const cs = this._coreService; + const { buffers, cols } = this._bufferService; + const { active, alt } = buffers; + const opts = this._optionsService.rawOptions; + + const f = (m: number, v: V): boolean => { + cs.triggerDataEvent(`${C0.ESC}[${ansi ? '' : '?'}${m};${v}$y`); + return true; + }; + const b2v = (value: boolean): V => value ? V.SET : V.RESET; + const p = params.params[0]; if (ansi) { - switch (p) { - case 2: f(p, -1); break; - case 4: f(p, this._coreService.modes.insertMode); break; - case 12: f(p, -2); break; - case 20: f(p, -2); break; - default: f(p, 2); - } - return true; + if (p === 2) return f(p, V.PERMANENTLY_SET); + if (p === 4) return f(p, b2v(cs.modes.insertMode)); + if (p === 12) return f(p, V.PERMANENTLY_RESET); + if (p === 20) return f(p, b2v(opts.convertEol)); + return f(p, V.NOT_RECOGNIZED); } - // dec privates - switch (p) { - case 1: f(p, this._coreService.decPrivateModes.applicationCursorKeys); break; - case 3: - const cols = this._bufferService.cols; - f(p, this._optionsService.rawOptions.windowOptions.setWinLines ? (cols === 80 ? 0 : cols === 132 ? 1 : 2) : 2); - break; - case 6: f(p, this._coreService.decPrivateModes.origin); break; - case 7: f(p, this._coreService.decPrivateModes.wraparound); break; - case 9: f(p, this._coreMouseService.activeProtocol === 'X10'); break; - case 12: f(p, -2); break; - case 25: f(p, !this._coreService.isCursorHidden); break; - case 45: f(p, this._coreService.decPrivateModes.reverseWraparound); break; - case 66: f(p, this._coreService.decPrivateModes.applicationKeypad); break; - case 1000: f(p, this._coreMouseService.activeProtocol === 'VT200'); break; - case 1002: f(p, this._coreMouseService.activeProtocol === 'DRAG'); break; - case 1003: f(p, this._coreMouseService.activeProtocol === 'ANY'); break; - case 1004: f(p, this._coreService.decPrivateModes.sendFocus); break; - case 1005: f(p, -2); break; - case 1006: f(p, this._coreMouseService.activeEncoding === 'SGR'); break; - case 1015: f(p, -2); break; - case 1016: f(p, this._coreMouseService.activeEncoding === 'SGR_PIXELS'); break; - case 47: - case 1047: - case 1048: - case 1049: f(p, this._bufferService.buffers.active === this._bufferService.buffers.alt); break; - case 2004: f(p, this._coreService.decPrivateModes.bracketedPasteMode); break; - default: f(p, 2); - } - return true; + if (p === 1) return f(p, b2v(dm.applicationCursorKeys)); + if (p === 3) return f(p, opts.windowOptions.setWinLines ? (cols === 80 ? V.RESET : cols === 132 ? V.SET : V.NOT_RECOGNIZED) : V.NOT_RECOGNIZED); + if (p === 6) return f(p, b2v(dm.origin)); + if (p === 7) return f(p, b2v(dm.wraparound)); + if (p === 8) return f(p, V.PERMANENTLY_SET); + if (p === 9) return f(p, b2v(mouseProtocol === 'X10')); + if (p === 12) return f(p, b2v(opts.cursorBlink)); + if (p === 25) return f(p, b2v(!cs.isCursorHidden)); + if (p === 45) return f(p, b2v(dm.reverseWraparound)); + if (p === 66) return f(p, b2v(dm.applicationKeypad)); + if (p === 1000) return f(p, b2v(mouseProtocol === 'VT200')); + if (p === 1002) return f(p, b2v(mouseProtocol === 'DRAG')); + if (p === 1003) return f(p, b2v(mouseProtocol === 'ANY')); + if (p === 1004) return f(p, b2v(dm.sendFocus)); + if (p === 1005) return f(p, V.PERMANENTLY_RESET); + if (p === 1006) return f(p, b2v(mouseEncoding === 'SGR')); + if (p === 1015) return f(p, V.PERMANENTLY_RESET); + if (p === 1016) return f(p, b2v(mouseEncoding === 'SGR_PIXELS')); + if (p === 47 || p === 1047 || p === 1048 || p === 1049) return f(p, b2v(active === alt)); + if (p === 2004) return f(p, b2v(dm.bracketedPasteMode)); + return f(p, V.NOT_RECOGNIZED); } /** From f08ad87e7a243086b1db96b57fd2faaed1ed2298 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?J=C3=B6rg=20Breitbart?= Date: Sat, 3 Sep 2022 13:25:20 +0200 Subject: [PATCH 06/10] unit tests --- src/common/InputHandler.test.ts | 73 +++++++++++++++++++++++++++++++++ src/common/InputHandler.ts | 5 ++- 2 files changed, 76 insertions(+), 2 deletions(-) diff --git a/src/common/InputHandler.test.ts b/src/common/InputHandler.test.ts index 2fa4ac2f..143686a1 100644 --- a/src/common/InputHandler.test.ts +++ b/src/common/InputHandler.test.ts @@ -2146,6 +2146,79 @@ describe('InputHandler', () => { }); }); }); + describe('DECRQM', () => { + const reportStack: string[] = []; + beforeEach(() => { + reportStack.length = 0; + coreService.onData(data => reportStack.push(data)); + }); + it('ANSI 2 (keyboard action mode)', async () => { + await inputHandler.parseP('\x1b[2$p'); + assert.deepEqual(reportStack.pop(), '\x1b[2;3$y'); // always set + }); + it('ANSI 4 (insert mode)', async () => { + await inputHandler.parseP('\x1b[4$p'); + assert.deepEqual(reportStack.pop(), '\x1b[4;2$y'); // reset by default + await inputHandler.parseP('\x1b[4h'); + await inputHandler.parseP('\x1b[4$p'); + assert.deepEqual(reportStack.pop(), '\x1b[4;1$y'); // now active + await inputHandler.parseP('\x1b[4l'); + await inputHandler.parseP('\x1b[4$p'); + assert.deepEqual(reportStack.pop(), '\x1b[4;2$y'); // again reset + }); + it('ANSI 12 (send/receive)', async () => { + await inputHandler.parseP('\x1b[12$p'); + assert.deepEqual(reportStack.pop(), '\x1b[12;4$y'); // always reset + }); + it('ANSI 20 (newline mode)', async () => { + await inputHandler.parseP('\x1b[20$p'); + assert.deepEqual(reportStack.pop(), '\x1b[20;2$y'); // reset by default + await inputHandler.parseP('\x1b[20h'); + await inputHandler.parseP('\x1b[20$p'); + assert.deepEqual(reportStack.pop(), '\x1b[20;1$y'); // now active + await inputHandler.parseP('\x1b[20l'); + await inputHandler.parseP('\x1b[20$p'); + assert.deepEqual(reportStack.pop(), '\x1b[20;2$y'); // again reset + }); + it('ANSI unknown', async () => { + await inputHandler.parseP('\x1b[1234$p'); + assert.deepEqual(reportStack.pop(), '\x1b[1234;0$y'); // not recognized + }); + it('DEC privates with set/reset semantic', async () => { + // initially reset + const reset = [1, 6, 9, 12, 45, 66, 1000, 1002, 1003, 1004, 1006, 1016, 47, 1047, 1049, 2004]; + for (const mode of reset) { + await inputHandler.parseP(`\x1b[?${mode}$p`); + assert.deepEqual(reportStack.pop(), `\x1b[?${mode};2$y`); // initial reset + await inputHandler.parseP(`\x1b[?${mode}h`); + await inputHandler.parseP(`\x1b[?${mode}$p`); + assert.deepEqual(reportStack.pop(), `\x1b[?${mode};1$y`); // now active + await inputHandler.parseP(`\x1b[?${mode}l`); + await inputHandler.parseP(`\x1b[?${mode}$p`); + assert.deepEqual(reportStack.pop(), `\x1b[?${mode};2$y`); // again reset + } + // initially set + const set = [7, 25]; + for (const mode of set) { + await inputHandler.parseP(`\x1b[?${mode}$p`); + assert.deepEqual(reportStack.pop(), `\x1b[?${mode};1$y`); // initial set + await inputHandler.parseP(`\x1b[?${mode}l`); + await inputHandler.parseP(`\x1b[?${mode}$p`); + assert.deepEqual(reportStack.pop(), `\x1b[?${mode};2$y`); // now inactive + await inputHandler.parseP(`\x1b[?${mode}h`); + await inputHandler.parseP(`\x1b[?${mode}$p`); + assert.deepEqual(reportStack.pop(), `\x1b[?${mode};1$y`); // again set + } + }); + it('DEC privates perma modes', async () => { + // [mode number, state value] + const perma = [[3, 0], [8, 3], [1005, 4], [1015, 4], [1048, 1]]; + for (const [mode, value] of perma) { + await inputHandler.parseP(`\x1b[?${mode}$p`); + assert.deepEqual(reportStack.pop(), `\x1b[?${mode};${value}$y`); + } + }); + }); }); diff --git a/src/common/InputHandler.ts b/src/common/InputHandler.ts index 18db078f..6b3801a8 100644 --- a/src/common/InputHandler.ts +++ b/src/common/InputHandler.ts @@ -2270,7 +2270,7 @@ export class InputHandler extends Disposable implements IInputHandler { * For modes not understood xterm.js always returns `notRecognized`. In general this means, * that a certain operation mode is not implemented and cannot be used. * - * Modes changing the active terminal buffer (47, 1047, 1048, 1049) are not subqueried + * Modes changing the active terminal buffer (47, 1047, 1049) are not subqueried * and only report, whether the alternate buffer is set. * * Mouse encodings and mouse protocols are handled mutual exclusive, @@ -2332,7 +2332,8 @@ export class InputHandler extends Disposable implements IInputHandler { if (p === 1006) return f(p, b2v(mouseEncoding === 'SGR')); if (p === 1015) return f(p, V.PERMANENTLY_RESET); if (p === 1016) return f(p, b2v(mouseEncoding === 'SGR_PIXELS')); - if (p === 47 || p === 1047 || p === 1048 || p === 1049) return f(p, b2v(active === alt)); + if (p === 1048) return f(p, V.SET); // xterm always returns SET here + if (p === 47 || p === 1047 || p === 1049) return f(p, b2v(active === alt)); if (p === 2004) return f(p, b2v(dm.bracketedPasteMode)); return f(p, V.NOT_RECOGNIZED); } From faf5f72c1ff25a21a60eaa07f205a3eedd31a286 Mon Sep 17 00:00:00 2001 From: Jason Lin Date: Mon, 5 Sep 2022 13:17:45 +1000 Subject: [PATCH 07/10] Clear glyph render when the webgl model is cleared --- addons/xterm-addon-webgl/src/GlyphRenderer.ts | 26 +++++++++---------- addons/xterm-addon-webgl/src/WebglRenderer.ts | 24 ++++++++++++----- 2 files changed, 30 insertions(+), 20 deletions(-) diff --git a/addons/xterm-addon-webgl/src/GlyphRenderer.ts b/addons/xterm-addon-webgl/src/GlyphRenderer.ts index 85ef25f6..a7f4a700 100644 --- a/addons/xterm-addon-webgl/src/GlyphRenderer.ts +++ b/addons/xterm-addon-webgl/src/GlyphRenderer.ts @@ -238,21 +238,24 @@ export class GlyphRenderer extends Disposable { // a_cellpos only changes on resize } - public clear(force?: boolean): void { + public clear(): void { const terminal = this._terminal; const newCount = terminal.cols * terminal.rows * INDICES_PER_CELL; - // Don't clear if not forced and the array length is correct - if (!force && this._vertices.count === newCount) { - return; - } - // Clear vertices - this._vertices.count = newCount; - this._vertices.attributes = new Float32Array(newCount); - for (let i = 0; i < this._vertices.attributesBuffers.length; i++) { - this._vertices.attributesBuffers[i] = new Float32Array(newCount); + if (this._vertices.count !== newCount) { + this._vertices.attributes = new Float32Array(newCount); + } else { + this._vertices.attributes.fill(0); } + for (let i = 0; i < this._vertices.attributesBuffers.length; i++) { + if (this._vertices.count !== newCount) { + this._vertices.attributesBuffers[i] = new Float32Array(newCount); + } else { + this._vertices.attributesBuffers[i].fill(0); + } + } + this._vertices.count = newCount; let i = 0; for (let y = 0; y < terminal.rows; y++) { for (let x = 0; x < terminal.cols; x++) { @@ -269,9 +272,6 @@ export class GlyphRenderer extends Disposable { this.clear(); } - public setColors(): void { - } - public render(renderModel: IRenderModel): void { if (!this._atlas) { return; diff --git a/addons/xterm-addon-webgl/src/WebglRenderer.ts b/addons/xterm-addon-webgl/src/WebglRenderer.ts index 3ff0c237..b48ec7eb 100644 --- a/addons/xterm-addon-webgl/src/WebglRenderer.ts +++ b/addons/xterm-addon-webgl/src/WebglRenderer.ts @@ -163,12 +163,11 @@ export class WebglRenderer extends Disposable implements IRenderer { } this._rectangleRenderer.setColors(); - this._glyphRenderer.setColors(); this._refreshCharAtlas(); // Force a full refresh - this._model.clear(); + this._clearModel(true); } public onDevicePixelRatioChange(): void { @@ -209,7 +208,7 @@ export class WebglRenderer extends Disposable implements IRenderer { this._refreshCharAtlas(); // Force a full refresh - this._model.clear(); + this._clearModel(false); } public onCharSizeChanged(): void { @@ -293,16 +292,27 @@ export class WebglRenderer extends Disposable implements IRenderer { this._glyphRenderer.setAtlas(this._charAtlas); } + /** + * Clear the model. + * @param clearGlyphRenderer Whether to also clear the glyph renderer. This + * should be true generally to make sure it is in the same state as the model. + */ + private _clearModel(clearGlyphRenderer: boolean): void { + this._model.clear(); + if (clearGlyphRenderer) { + this._glyphRenderer.clear(); + } + } + public clearCharAtlas(): void { this._charAtlas?.clearTexture(); - this._model.clear(); + this._clearModel(true); this._updateModel(0, this._terminal.rows - 1); this._requestRedrawViewport(); } public clear(): void { - this._model.clear(); - this._glyphRenderer.clear(true); + this._clearModel(true); for (const l of this._renderLayers) { l.reset(this._terminal); } @@ -334,7 +344,7 @@ export class WebglRenderer extends Disposable implements IRenderer { // Tell renderer the frame is beginning if (this._glyphRenderer.beginFrame()) { - this._model.clear(); + this._clearModel(true); this._updateSelectionModel(undefined, undefined); } From e5ad3ce9b98954a990895bf0115ff2aede682fdc Mon Sep 17 00:00:00 2001 From: Jason Lin Date: Mon, 5 Sep 2022 17:32:02 +1000 Subject: [PATCH 08/10] Polish --- addons/xterm-addon-webgl/src/WebglRenderer.ts | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/addons/xterm-addon-webgl/src/WebglRenderer.ts b/addons/xterm-addon-webgl/src/WebglRenderer.ts index b48ec7eb..6420e812 100644 --- a/addons/xterm-addon-webgl/src/WebglRenderer.ts +++ b/addons/xterm-addon-webgl/src/WebglRenderer.ts @@ -207,7 +207,8 @@ export class WebglRenderer extends Disposable implements IRenderer { this._refreshCharAtlas(); - // Force a full refresh + // Force a full refresh. Resizing `_glyphRenderer` should clear it already, + // so there is no need to clear it again here. this._clearModel(false); } From 4752f99e03379a0d17f2e308bfe44e31bfb4b832 Mon Sep 17 00:00:00 2001 From: Jason Lin Date: Tue, 6 Sep 2022 14:12:15 +1000 Subject: [PATCH 09/10] Webgl cursor renderer makes sure a char is not drawn outside of the cell --- .../src/renderLayer/BaseRenderLayer.ts | 13 +++++++------ 1 file changed, 7 insertions(+), 6 deletions(-) diff --git a/addons/xterm-addon-webgl/src/renderLayer/BaseRenderLayer.ts b/addons/xterm-addon-webgl/src/renderLayer/BaseRenderLayer.ts index 0aa2049c..9d920773 100644 --- a/addons/xterm-addon-webgl/src/renderLayer/BaseRenderLayer.ts +++ b/addons/xterm-addon-webgl/src/renderLayer/BaseRenderLayer.ts @@ -226,7 +226,7 @@ export abstract class BaseRenderLayer implements IRenderLayer { protected _fillCharTrueColor(terminal: Terminal, cell: CellData, x: number, y: number): void { this._ctx.font = this._getFont(terminal, false, false); this._ctx.textBaseline = TEXT_BASELINE; - this._clipRow(terminal, y); + this._clipCell(x, y, cell.getWidth()); this._ctx.fillText( cell.getChars(), x * this._scaledCellWidth + this._scaledCharLeft, @@ -234,16 +234,17 @@ export abstract class BaseRenderLayer implements IRenderLayer { } /** - * Clips a row to ensure no pixels will be drawn outside the cells in the row. - * @param terminal The terminal. + * Clips a cell to ensure no pixels will be drawn outside of it. + * @param x The column to clip. * @param y The row to clip. + * @param width The number of columns to clip. */ - private _clipRow(terminal: Terminal, y: number): void { + private _clipCell(x: number, y: number, width: number): void { this._ctx.beginPath(); this._ctx.rect( - 0, + x * this._scaledCellWidth, y * this._scaledCellHeight, - terminal.cols * this._scaledCellWidth, + width * this._scaledCellWidth, this._scaledCellHeight); this._ctx.clip(); } From 3953db0d681544b390ac8f1f91f9cbc02b893b1f Mon Sep 17 00:00:00 2001 From: Jean Pierre Date: Wed, 7 Sep 2022 11:56:52 -0500 Subject: [PATCH 10/10] Fixes https://github.com/microsoft/vscode/issues/160070 --- src/browser/renderer/DevicePixelObserver.ts | 7 ++++++- 1 file changed, 6 insertions(+), 1 deletion(-) diff --git a/src/browser/renderer/DevicePixelObserver.ts b/src/browser/renderer/DevicePixelObserver.ts index caf1b21d..3aea61f6 100644 --- a/src/browser/renderer/DevicePixelObserver.ts +++ b/src/browser/renderer/DevicePixelObserver.ts @@ -31,6 +31,11 @@ export function observeDevicePixelDimensions(element: HTMLElement, callback: (de callback(width, height); } }); - observer.observe(element, { box: ['device-pixel-content-box'] } as any); + try { + observer.observe(element, { box: ['device-pixel-content-box'] } as any); + } catch { + observer.disconnect(); + observer = undefined; + } return toDisposable(() => observer?.disconnect()); }