From a1b438cea6aec91726c526e263ea5d32079356b5 Mon Sep 17 00:00:00 2001 From: Asem Dreibati Date: Tue, 29 Oct 2024 10:31:57 +0300 Subject: [PATCH 01/29] Store the DI decorator's id in specific property (#5131). --- src/common/services/InstantiationService.ts | 2 +- src/common/services/ServiceRegistry.ts | 2 +- src/common/services/Services.ts | 1 + 3 files changed, 3 insertions(+), 2 deletions(-) diff --git a/src/common/services/InstantiationService.ts b/src/common/services/InstantiationService.ts index 375e442def..7e76954856 100644 --- a/src/common/services/InstantiationService.ts +++ b/src/common/services/InstantiationService.ts @@ -67,7 +67,7 @@ export class InstantiationService implements IInstantiationService { for (const dependency of serviceDependencies) { const service = this._services.get(dependency.id); if (!service) { - throw new Error(`[createInstance] ${ctor.name} depends on UNKNOWN service ${dependency.id}.`); + throw new Error(`[createInstance] ${ctor.name} depends on UNKNOWN service ${dependency.id._id}.`); } serviceArgs.push(service); } diff --git a/src/common/services/ServiceRegistry.ts b/src/common/services/ServiceRegistry.ts index 6510fb8e04..7d887bc612 100644 --- a/src/common/services/ServiceRegistry.ts +++ b/src/common/services/ServiceRegistry.ts @@ -33,7 +33,7 @@ export function createDecorator(id: string): IServiceIdentifier { storeServiceDependency(decorator, target, index); }; - decorator.toString = () => id; + decorator._id = id; serviceRegistry.set(id, decorator); return decorator; diff --git a/src/common/services/Services.ts b/src/common/services/Services.ts index 0ceff36c4e..9d5ca64b27 100644 --- a/src/common/services/Services.ts +++ b/src/common/services/Services.ts @@ -124,6 +124,7 @@ export interface ICharsetService { export interface IServiceIdentifier { (...args: any[]): void; type: T; + _id: string; } export interface IBrandedService { From 1978399259365a3d7e65b575068c58e0751e09f4 Mon Sep 17 00:00:00 2001 From: Jacob Bandes-Storch Date: Fri, 13 Dec 2024 13:57:17 -0800 Subject: [PATCH 02/29] Fix click event bug caused by DomRenderer replaceChildren behavior --- src/browser/renderer/dom/DomRenderer.ts | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/src/browser/renderer/dom/DomRenderer.ts b/src/browser/renderer/dom/DomRenderer.ts index f6fda22e6b..2274d043d0 100644 --- a/src/browser/renderer/dom/DomRenderer.ts +++ b/src/browser/renderer/dom/DomRenderer.ts @@ -161,6 +161,10 @@ export class DomRenderer extends Disposable implements IRenderer { // Base CSS let styles = `${this._terminalSelector} .${ROW_CONTAINER_CLASS} {` + + // Disabling pointer events circumvents a browser behavior that prevents `click` events from + // being delivered if the target element is replaced during the click. This happened due to + // refresh() being called during the mousedown handler to start a selection. + ` pointer-events: none;` + ` color: ${colors.foreground.css};` + ` font-family: ${this._optionsService.rawOptions.fontFamily};` + ` font-size: ${this._optionsService.rawOptions.fontSize}px;` + From 9f9bb3c11c0d6d88c4d9d16ce70e9e1f3be73038 Mon Sep 17 00:00:00 2001 From: An Phi Date: Wed, 18 Dec 2024 01:26:47 -0500 Subject: [PATCH 03/29] bug: properly render the terminal when open() is called again --- src/browser/CoreBrowserTerminal.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/browser/CoreBrowserTerminal.ts b/src/browser/CoreBrowserTerminal.ts index 2e15b5f379..33ee94bc84 100644 --- a/src/browser/CoreBrowserTerminal.ts +++ b/src/browser/CoreBrowserTerminal.ts @@ -400,7 +400,7 @@ export class CoreBrowserTerminal extends CoreTerminal implements ITerminal { } // If the terminal is already opened - if (this.element?.ownerDocument.defaultView && this._coreBrowserService) { + if (this.element?.ownerDocument.defaultView && this._coreBrowserService && this.element?.isConnected) { // Adjust the window if needed if (this.element.ownerDocument.defaultView !== this._coreBrowserService.window) { this._coreBrowserService.window = this.element.ownerDocument.defaultView; From b74ec267e2bcfd1c3582958cf7be29fff29cbd80 Mon Sep 17 00:00:00 2001 From: Daniel Imms <2193314+Tyriar@users.noreply.github.com> Date: Fri, 20 Dec 2024 07:37:12 -0800 Subject: [PATCH 04/29] Blend cursor with background to support alpha in webgl Fixes #5241 --- demo/client.ts | 4 ++-- src/browser/services/ThemeService.ts | 2 +- test/playwright/SharedRendererTests.ts | 8 ++++++++ 3 files changed, 11 insertions(+), 3 deletions(-) diff --git a/demo/client.ts b/demo/client.ts index 6e0be22f57..02b9f7bedf 100644 --- a/demo/client.ts +++ b/demo/client.ts @@ -16,7 +16,7 @@ if ('WebAssembly' in window) { ImageAddon = imageAddon.ImageAddon; } -import { Terminal, ITerminalOptions, type IDisposable } from '@xterm/xterm'; +import { Terminal, ITerminalOptions, type IDisposable, type ITheme } from '@xterm/xterm'; import { AttachAddon } from '@xterm/addon-attach'; import { ClipboardAddon } from '@xterm/addon-clipboard'; import { FitAddon } from '@xterm/addon-fit'; @@ -131,7 +131,7 @@ const xtermjsTheme = { brightCyan: '#72F0FF', white: '#F8F8F8', brightWhite: '#FFFFFF' -}; +} satisfies ITheme; function setPadding(): void { term.element.style.padding = parseInt(paddingElement.value, 10).toString() + 'px'; addons.fit.instance.fit(); diff --git a/src/browser/services/ThemeService.ts b/src/browser/services/ThemeService.ts index 88ffd99d5f..0dc7fcbf0d 100644 --- a/src/browser/services/ThemeService.ts +++ b/src/browser/services/ThemeService.ts @@ -82,7 +82,7 @@ export class ThemeService extends Disposable implements IThemeService { const colors = this._colors; colors.foreground = parseColor(theme.foreground, DEFAULT_FOREGROUND); colors.background = parseColor(theme.background, DEFAULT_BACKGROUND); - colors.cursor = parseColor(theme.cursor, DEFAULT_CURSOR); + colors.cursor = color.blend(colors.background, parseColor(theme.cursor, DEFAULT_CURSOR)); colors.cursorAccent = parseColor(theme.cursorAccent, DEFAULT_CURSOR_ACCENT); colors.selectionBackgroundTransparent = parseColor(theme.selectionBackground, DEFAULT_SELECTION); colors.selectionBackgroundOpaque = color.blend(colors.background, colors.selectionBackgroundTransparent); diff --git a/test/playwright/SharedRendererTests.ts b/test/playwright/SharedRendererTests.ts index 4a1798ce64..b0c5c49986 100644 --- a/test/playwright/SharedRendererTests.ts +++ b/test/playwright/SharedRendererTests.ts @@ -1248,6 +1248,14 @@ export function injectSharedRendererTests(ctx: ISharedRendererTestContext): void await ctx.value.proxy.scrollLines(-2); await pollFor(ctx.value.page, () => getCellColor(ctx.value, 1, 1), [0, 0, 0, 255]); }); + test('#5241 cursor with alpha should blend color with background color', async () => { + const theme: ITheme = { + cursor: '#FF000080' + }; + await ctx.value.page.evaluate(`window.term.options.theme = ${JSON.stringify(theme)};`); + await ctx.value.proxy.focus(); + await pollFor(ctx.value.page, () => getCellColor(ctx.value, 1, 1), [128, 0, 0, 255]); + }); }); } From 3e998387fe61910b75e3e549c5d84e8e322c6978 Mon Sep 17 00:00:00 2001 From: Daniel Imms <2193314+Tyriar@users.noreply.github.com> Date: Fri, 20 Dec 2024 08:51:17 -0800 Subject: [PATCH 05/29] Blend cursorAccent with background too Fixes #5241 --- src/browser/services/ThemeService.ts | 2 +- test/playwright/SharedRendererTests.ts | 10 ++++++++++ 2 files changed, 11 insertions(+), 1 deletion(-) diff --git a/src/browser/services/ThemeService.ts b/src/browser/services/ThemeService.ts index 0dc7fcbf0d..cd85e0fff8 100644 --- a/src/browser/services/ThemeService.ts +++ b/src/browser/services/ThemeService.ts @@ -83,7 +83,7 @@ export class ThemeService extends Disposable implements IThemeService { colors.foreground = parseColor(theme.foreground, DEFAULT_FOREGROUND); colors.background = parseColor(theme.background, DEFAULT_BACKGROUND); colors.cursor = color.blend(colors.background, parseColor(theme.cursor, DEFAULT_CURSOR)); - colors.cursorAccent = parseColor(theme.cursorAccent, DEFAULT_CURSOR_ACCENT); + colors.cursorAccent = color.blend(colors.background, parseColor(theme.cursorAccent, DEFAULT_CURSOR_ACCENT)); colors.selectionBackgroundTransparent = parseColor(theme.selectionBackground, DEFAULT_SELECTION); colors.selectionBackgroundOpaque = color.blend(colors.background, colors.selectionBackgroundTransparent); colors.selectionInactiveBackgroundTransparent = parseColor(theme.selectionInactiveBackground, colors.selectionBackgroundTransparent); diff --git a/test/playwright/SharedRendererTests.ts b/test/playwright/SharedRendererTests.ts index b0c5c49986..998d51a637 100644 --- a/test/playwright/SharedRendererTests.ts +++ b/test/playwright/SharedRendererTests.ts @@ -1256,6 +1256,16 @@ export function injectSharedRendererTests(ctx: ISharedRendererTestContext): void await ctx.value.proxy.focus(); await pollFor(ctx.value.page, () => getCellColor(ctx.value, 1, 1), [128, 0, 0, 255]); }); + test.only('#5241 cursorAccent with alpha should blend color with background color', async () => { + const theme: ITheme = { + cursorAccent: '#FF000080' + }; + await ctx.value.page.evaluate(`window.term.options.theme = ${JSON.stringify(theme)};`); + await ctx.value.proxy.focus(); + await ctx.value.proxy.write('■'); + await ctx.value.proxy.write('\x1b[1D'); + await pollFor(ctx.value.page, () => getCellColor(ctx.value, 1, 1), [128, 0, 0, 255]); + }); }); } From da22b0308ef6f53f62f5eb78b19fce735aa1a15f Mon Sep 17 00:00:00 2001 From: Daniel Imms <2193314+Tyriar@users.noreply.github.com> Date: Fri, 20 Dec 2024 09:00:05 -0800 Subject: [PATCH 06/29] Make textarea readonly when disableStdin is set Fixes #5256 --- src/browser/CoreBrowserTerminal.ts | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/src/browser/CoreBrowserTerminal.ts b/src/browser/CoreBrowserTerminal.ts index 2e15b5f379..58da93445e 100644 --- a/src/browser/CoreBrowserTerminal.ts +++ b/src/browser/CoreBrowserTerminal.ts @@ -437,7 +437,7 @@ export class CoreBrowserTerminal extends CoreTerminal implements ITerminal { this.screenElement.appendChild(this._helperContainer); fragment.appendChild(this.screenElement); - this.textarea = this._document.createElement('textarea'); + const textarea = this.textarea = this._document.createElement('textarea'); this.textarea.classList.add('xterm-helper-textarea'); this.textarea.setAttribute('aria-label', Strings.promptLabel.get()); if (!Browser.isChromeOS) { @@ -449,6 +449,8 @@ export class CoreBrowserTerminal extends CoreTerminal implements ITerminal { this.textarea.setAttribute('autocapitalize', 'off'); this.textarea.setAttribute('spellcheck', 'false'); this.textarea.tabIndex = 0; + this._register(this.optionsService.onSpecificOptionChange('disableStdin', () => textarea.readOnly = this.optionsService.rawOptions.disableStdin)); + this.textarea.readOnly = this.optionsService.rawOptions.disableStdin; // Register the core browser service before the generic textarea handlers are registered so it // handles them first. Otherwise the renderers may use the wrong focus state. From 18c9eb196010ec082ad56adf7040ed2dca5fd391 Mon Sep 17 00:00:00 2001 From: Daniel Imms <2193314+Tyriar@users.noreply.github.com> Date: Fri, 20 Dec 2024 09:01:30 -0800 Subject: [PATCH 07/29] Remove .only --- test/playwright/SharedRendererTests.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/test/playwright/SharedRendererTests.ts b/test/playwright/SharedRendererTests.ts index 998d51a637..1d37b5c169 100644 --- a/test/playwright/SharedRendererTests.ts +++ b/test/playwright/SharedRendererTests.ts @@ -1256,7 +1256,7 @@ export function injectSharedRendererTests(ctx: ISharedRendererTestContext): void await ctx.value.proxy.focus(); await pollFor(ctx.value.page, () => getCellColor(ctx.value, 1, 1), [128, 0, 0, 255]); }); - test.only('#5241 cursorAccent with alpha should blend color with background color', async () => { + test('#5241 cursorAccent with alpha should blend color with background color', async () => { const theme: ITheme = { cursorAccent: '#FF000080' }; From fb2e96e1b0693809ea6221113506033b4184a41c Mon Sep 17 00:00:00 2001 From: Daniel Imms <2193314+Tyriar@users.noreply.github.com> Date: Fri, 20 Dec 2024 10:21:06 -0800 Subject: [PATCH 08/29] Add test button for DECSCUSR Part of #3293 --- demo/client.ts | 44 ++++++++++++++++++++++++-------------------- 1 file changed, 24 insertions(+), 20 deletions(-) diff --git a/demo/client.ts b/demo/client.ts index 02b9f7bedf..ceb3fab141 100644 --- a/demo/client.ts +++ b/demo/client.ts @@ -1266,10 +1266,13 @@ function addVtButtons(): void { const element = document.createElement('button'); element.textContent = name; - writeCsi.split(''); - const prefix = writeCsi.length === 2 ? writeCsi[0] : ''; - const suffix = writeCsi[writeCsi.length - 1]; - element.addEventListener(`click`, () => term.write(csi(`${prefix}${inputs.map(e => e.value).join(';')}${suffix}`))); + const writeCsiSplit = writeCsi.split('|'); + const prefix = writeCsiSplit.length === 2 ? writeCsiSplit[0] : ''; + const suffix = writeCsiSplit[writeCsiSplit.length - 1]; + element.addEventListener(`click`, () => { + debugger; + term.write(csi(`${prefix}${inputs.map(e => e.value).join(';')}${suffix}`)); + }); const desc = document.createElement('span'); desc.textContent = description; @@ -1281,22 +1284,23 @@ function addVtButtons(): void { } const vtFragment = document.createDocumentFragment(); const buttonSpecs: { [key: string]: { label: string, description: string, paramCount?: number }} = { - A: { label: 'CUU ↑', description: 'Cursor Up Ps Times' }, - B: { label: 'CUD ↓', description: 'Cursor Down Ps Times' }, - C: { label: 'CUF →', description: 'Cursor Forward Ps Times' }, - D: { label: 'CUB ←', description: 'Cursor Backward Ps Times' }, - E: { label: 'CNL', description: 'Cursor Next Line Ps Times' }, - F: { label: 'CPL', description: 'Cursor Preceding Line Ps Times' }, - G: { label: 'CHA', description: 'Cursor Character Absolute' }, - H: { label: 'CUP', description: 'Cursor Position [row;column]', paramCount: 2 }, - I: { label: 'CHT', description: 'Cursor Forward Tabulation Ps tab stops' }, - J: { label: 'ED', description: 'Erase in Display' }, - '?J': { label: 'DECSED', description: 'Erase in Display' }, - K: { label: 'EL', description: 'Erase in Line' }, - '?K': { label: 'DECSEL', description: 'Erase in Line' }, - L: { label: 'IL', description: 'Insert Ps Line(s)' }, - M: { label: 'DL', description: 'Delete Ps Line(s)' }, - P: { label: 'DCH', description: 'Delete Ps Character(s)' } + A: { label: 'CUU ↑', description: 'Cursor Up Ps Times' }, + B: { label: 'CUD ↓', description: 'Cursor Down Ps Times' }, + C: { label: 'CUF →', description: 'Cursor Forward Ps Times' }, + D: { label: 'CUB ←', description: 'Cursor Backward Ps Times' }, + E: { label: 'CNL', description: 'Cursor Next Line Ps Times' }, + F: { label: 'CPL', description: 'Cursor Preceding Line Ps Times' }, + G: { label: 'CHA', description: 'Cursor Character Absolute' }, + H: { label: 'CUP', description: 'Cursor Position [row;column]', paramCount: 2 }, + I: { label: 'CHT', description: 'Cursor Forward Tabulation Ps tab stops' }, + J: { label: 'ED', description: 'Erase in Display' }, + '?|J': { label: 'DECSED', description: 'Erase in Display' }, + K: { label: 'EL', description: 'Erase in Line' }, + '?|K': { label: 'DECSEL', description: 'Erase in Line' }, + L: { label: 'IL', description: 'Insert Ps Line(s)' }, + M: { label: 'DL', description: 'Delete Ps Line(s)' }, + P: { label: 'DCH', description: 'Delete Ps Character(s)' }, + ' q': { label: 'DECSCUSR', description: 'Set Cursor Style', paramCount: 1 } }; for (const s of Object.keys(buttonSpecs)) { const spec = buttonSpecs[s]; From b579649a392d6e178cb08948ef888316b4b91e3a Mon Sep 17 00:00:00 2001 From: Daniel Imms <2193314+Tyriar@users.noreply.github.com> Date: Fri, 20 Dec 2024 10:34:05 -0800 Subject: [PATCH 09/29] Revert to cursor options after DECSCUSR 0 Fixes #3293 --- addons/addon-webgl/src/WebglRenderer.ts | 11 ++++--- demo/client.ts | 5 +-- src/browser/renderer/dom/DomRenderer.ts | 7 ++-- src/common/InputHandler.ts | 44 +++++++++++++++---------- src/common/TestUtils.test.ts | 2 ++ src/common/Types.ts | 2 ++ src/common/services/CoreService.ts | 2 ++ 7 files changed, 43 insertions(+), 30 deletions(-) diff --git a/addons/addon-webgl/src/WebglRenderer.ts b/addons/addon-webgl/src/WebglRenderer.ts index 216b8404f8..6c270d1272 100644 --- a/addons/addon-webgl/src/WebglRenderer.ts +++ b/addons/addon-webgl/src/WebglRenderer.ts @@ -354,7 +354,7 @@ export class WebglRenderer extends Disposable implements IRenderer { } private _updateCursorBlink(): void { - if (this._terminal.options.cursorBlink) { + if (this._coreService.decPrivateModes.cursorBlink ?? this._terminal.options.cursorBlink) { this._cursorBlinkStateManager.value = new CursorBlinkStateManager(() => { this._requestRedrawCursor(); }, this._coreBrowserService); @@ -387,6 +387,7 @@ export class WebglRenderer extends Disposable implements IRenderer { let j: number; start = clamp(start, terminal.rows - 1, 0); end = clamp(end, terminal.rows - 1, 0); + const cursorStyle = this._coreService.decPrivateModes.cursorStyle ?? terminal.options.cursorStyle ?? 'block'; const cursorY = this._terminal.buffer.active.baseY + this._terminal.buffer.active.cursorY; const viewportRelativeCursorY = cursorY - terminal.buffer.ydisp; @@ -450,8 +451,7 @@ export class WebglRenderer extends Disposable implements IRenderer { x: cursorX, y: viewportRelativeCursorY, width: cell.getWidth(), - style: this._coreBrowserService.isFocused ? - (terminal.options.cursorStyle || 'block') : terminal.options.cursorInactiveStyle, + style: this._coreBrowserService.isFocused ? cursorStyle : terminal.options.cursorInactiveStyle, cursorWidth: terminal.options.cursorWidth, dpr: this._devicePixelRatio }; @@ -459,9 +459,10 @@ export class WebglRenderer extends Disposable implements IRenderer { } if (x >= cursorX && x <= lastCursorX && ((this._coreBrowserService.isFocused && - (terminal.options.cursorStyle || 'block') === 'block') || + cursorStyle === 'block') || (this._coreBrowserService.isFocused === false && - terminal.options.cursorInactiveStyle === 'block'))) { + terminal.options.cursorInactiveStyle === 'block')) + ) { this._cellColorResolver.result.fg = Attributes.CM_RGB | (this._themeService.colors.cursorAccent.rgba >> 8 & Attributes.RGB_MASK); this._cellColorResolver.result.bg = diff --git a/demo/client.ts b/demo/client.ts index ceb3fab141..f750321015 100644 --- a/demo/client.ts +++ b/demo/client.ts @@ -1269,10 +1269,7 @@ function addVtButtons(): void { const writeCsiSplit = writeCsi.split('|'); const prefix = writeCsiSplit.length === 2 ? writeCsiSplit[0] : ''; const suffix = writeCsiSplit[writeCsiSplit.length - 1]; - element.addEventListener(`click`, () => { - debugger; - term.write(csi(`${prefix}${inputs.map(e => e.value).join(';')}${suffix}`)); - }); + element.addEventListener(`click`, () => term.write(csi(`${prefix}${inputs.map(e => e.value).join(';')}${suffix}`))); const desc = document.createElement('span'); desc.textContent = description; diff --git a/src/browser/renderer/dom/DomRenderer.ts b/src/browser/renderer/dom/DomRenderer.ts index f6fda22e6b..1de446c40f 100644 --- a/src/browser/renderer/dom/DomRenderer.ts +++ b/src/browser/renderer/dom/DomRenderer.ts @@ -13,7 +13,7 @@ import { ICharSizeService, ICoreBrowserService, IThemeService } from 'browser/se import { ILinkifier2, ILinkifierEvent, ITerminal, ReadonlyColorSet } from 'browser/Types'; import { color } from 'common/Color'; import { Disposable, toDisposable } from 'vs/base/common/lifecycle'; -import { IBufferService, IInstantiationService, IOptionsService } from 'common/services/Services'; +import { IBufferService, ICoreService, IInstantiationService, IOptionsService } from 'common/services/Services'; import { Emitter } from 'vs/base/common/event'; @@ -59,6 +59,7 @@ export class DomRenderer extends Disposable implements IRenderer { @ICharSizeService private readonly _charSizeService: ICharSizeService, @IOptionsService private readonly _optionsService: IOptionsService, @IBufferService private readonly _bufferService: IBufferService, + @ICoreService private readonly _coreService: ICoreService, @ICoreBrowserService private readonly _coreBrowserService: ICoreBrowserService, @IThemeService private readonly _themeService: IThemeService ) { @@ -437,8 +438,8 @@ export class DomRenderer extends Disposable implements IRenderer { const buffer = this._bufferService.buffer; const cursorAbsoluteY = buffer.ybase + buffer.y; const cursorX = Math.min(buffer.x, this._bufferService.cols - 1); - const cursorBlink = this._optionsService.rawOptions.cursorBlink; - const cursorStyle = this._optionsService.rawOptions.cursorStyle; + const cursorBlink = this._coreService.decPrivateModes.cursorBlink ?? this._optionsService.rawOptions.cursorBlink; + const cursorStyle = this._coreService.decPrivateModes.cursorStyle ?? this._optionsService.rawOptions.cursorStyle; const cursorInactiveStyle = this._optionsService.rawOptions.cursorInactiveStyle; for (let y = start; y <= end; y++) { diff --git a/src/common/InputHandler.ts b/src/common/InputHandler.ts index b94d785544..e9b9913830 100644 --- a/src/common/InputHandler.ts +++ b/src/common/InputHandler.ts @@ -2714,7 +2714,7 @@ export class InputHandler extends Disposable implements IInputHandler { /** * CSI Ps SP q Set cursor style (DECSCUSR, VT520). - * Ps = 0 -> blinking block. + * Ps = 0 -> reset to option. * Ps = 1 -> blinking block (default). * Ps = 2 -> steady block. * Ps = 3 -> blinking underline. @@ -2724,7 +2724,8 @@ export class InputHandler extends Disposable implements IInputHandler { * * @vt: #Y CSI DECSCUSR "Set Cursor Style" "CSI Ps SP q" "Set cursor style." * Supported cursor styles: - * - empty, 0 or 1: steady block + * - empty, 0: reset to option + * - 1: steady block * - 2: blink block * - 3: steady underline * - 4: blink underline @@ -2732,23 +2733,30 @@ export class InputHandler extends Disposable implements IInputHandler { * - 6: blink bar */ public setCursorStyle(params: IParams): boolean { - const param = params.params[0] || 1; - switch (param) { - case 1: - case 2: - this._optionsService.options.cursorStyle = 'block'; - break; - case 3: - case 4: - this._optionsService.options.cursorStyle = 'underline'; - break; - case 5: - case 6: - this._optionsService.options.cursorStyle = 'bar'; - break; + const param = params.params[0] ?? 1; + if (param === 0) { + this._coreService.decPrivateModes.cursorStyle = undefined; + this._coreService.decPrivateModes.cursorBlink = undefined; + } else { + switch (param) { + case 0: + break; + case 1: + case 2: + this._coreService.decPrivateModes.cursorStyle = 'block'; + break; + case 3: + case 4: + this._coreService.decPrivateModes.cursorStyle = 'underline'; + break; + case 5: + case 6: + this._coreService.decPrivateModes.cursorStyle = 'bar'; + break; + } + const isBlinking = param % 2 === 1; + this._coreService.decPrivateModes.cursorBlink = isBlinking; } - const isBlinking = param % 2 === 1; - this._optionsService.options.cursorBlink = isBlinking; return true; } diff --git a/src/common/TestUtils.test.ts b/src/common/TestUtils.test.ts index 8c9634db61..127e1f24b8 100644 --- a/src/common/TestUtils.test.ts +++ b/src/common/TestUtils.test.ts @@ -89,6 +89,8 @@ export class MockCoreService implements ICoreService { applicationCursorKeys: false, applicationKeypad: false, bracketedPasteMode: false, + cursorBlink: undefined, + cursorStyle: undefined, origin: false, reverseWraparound: false, sendFocus: false, diff --git a/src/common/Types.ts b/src/common/Types.ts index 289aa1f692..c254d330d1 100644 --- a/src/common/Types.ts +++ b/src/common/Types.ts @@ -268,6 +268,8 @@ export interface IDecPrivateModes { applicationCursorKeys: boolean; applicationKeypad: boolean; bracketedPasteMode: boolean; + cursorBlink: boolean | undefined; + cursorStyle: CursorStyle | undefined; origin: boolean; reverseWraparound: boolean; sendFocus: boolean; diff --git a/src/common/services/CoreService.ts b/src/common/services/CoreService.ts index 9c41fc1a82..5bee635697 100644 --- a/src/common/services/CoreService.ts +++ b/src/common/services/CoreService.ts @@ -17,6 +17,8 @@ const DEFAULT_DEC_PRIVATE_MODES: IDecPrivateModes = Object.freeze({ applicationCursorKeys: false, applicationKeypad: false, bracketedPasteMode: false, + cursorBlink: undefined, + cursorStyle: undefined, origin: false, reverseWraparound: false, sendFocus: false, From 82598e94a9cf766172c24b683a49020e5b749fc0 Mon Sep 17 00:00:00 2001 From: Daniel Imms <2193314+Tyriar@users.noreply.github.com> Date: Fri, 20 Dec 2024 10:36:32 -0800 Subject: [PATCH 10/29] Fix default param DECSCUSR handling --- src/common/InputHandler.ts | 4 +--- 1 file changed, 1 insertion(+), 3 deletions(-) diff --git a/src/common/InputHandler.ts b/src/common/InputHandler.ts index e9b9913830..de8664e9dd 100644 --- a/src/common/InputHandler.ts +++ b/src/common/InputHandler.ts @@ -2733,14 +2733,12 @@ export class InputHandler extends Disposable implements IInputHandler { * - 6: blink bar */ public setCursorStyle(params: IParams): boolean { - const param = params.params[0] ?? 1; + const param = params.length === 0 ? 1 : params.params[0]; if (param === 0) { this._coreService.decPrivateModes.cursorStyle = undefined; this._coreService.decPrivateModes.cursorBlink = undefined; } else { switch (param) { - case 0: - break; case 1: case 2: this._coreService.decPrivateModes.cursorStyle = 'block'; From f824083324052ae0387008da999a1f8c3df404ae Mon Sep 17 00:00:00 2001 From: Daniel Imms <2193314+Tyriar@users.noreply.github.com> Date: Fri, 20 Dec 2024 10:37:48 -0800 Subject: [PATCH 11/29] Correct @vt docs --- src/common/InputHandler.ts | 14 +++++++------- 1 file changed, 7 insertions(+), 7 deletions(-) diff --git a/src/common/InputHandler.ts b/src/common/InputHandler.ts index de8664e9dd..8308e6def1 100644 --- a/src/common/InputHandler.ts +++ b/src/common/InputHandler.ts @@ -2724,13 +2724,13 @@ export class InputHandler extends Disposable implements IInputHandler { * * @vt: #Y CSI DECSCUSR "Set Cursor Style" "CSI Ps SP q" "Set cursor style." * Supported cursor styles: - * - empty, 0: reset to option - * - 1: steady block - * - 2: blink block - * - 3: steady underline - * - 4: blink underline - * - 5: steady bar - * - 6: blink bar + * - 0: reset to option + * - empty, 1: blinking block + * - 2: steady block + * - 3: blinking underline + * - 4: steady underline + * - 5: blinking bar + * - 6: steady bar */ public setCursorStyle(params: IParams): boolean { const param = params.length === 0 ? 1 : params.params[0]; From d44f7818c4bfbcdeb006f4c16174462795052047 Mon Sep 17 00:00:00 2001 From: Daniel Imms <2193314+Tyriar@users.noreply.github.com> Date: Fri, 20 Dec 2024 10:43:38 -0800 Subject: [PATCH 12/29] Fix test to assert new behavior --- src/common/InputHandler.test.ts | 28 ++++++++++++++-------------- 1 file changed, 14 insertions(+), 14 deletions(-) diff --git a/src/common/InputHandler.test.ts b/src/common/InputHandler.test.ts index baae735c94..1280417bd5 100644 --- a/src/common/InputHandler.test.ts +++ b/src/common/InputHandler.test.ts @@ -202,38 +202,38 @@ describe('InputHandler', () => { describe('setCursorStyle', () => { it('should call Terminal.setOption with correct params', () => { inputHandler.setCursorStyle(Params.fromArray([0])); - assert.equal(optionsService.options['cursorStyle'], 'block'); - assert.equal(optionsService.options['cursorBlink'], true); + assert.equal(coreService.decPrivateModes.cursorStyle, undefined); + assert.equal(coreService.decPrivateModes.cursorBlink, undefined); optionsService.options = clone(DEFAULT_OPTIONS); inputHandler.setCursorStyle(Params.fromArray([1])); - assert.equal(optionsService.options['cursorStyle'], 'block'); - assert.equal(optionsService.options['cursorBlink'], true); + assert.equal(coreService.decPrivateModes.cursorStyle, 'block'); + assert.equal(coreService.decPrivateModes.cursorBlink, true); optionsService.options = clone(DEFAULT_OPTIONS); inputHandler.setCursorStyle(Params.fromArray([2])); - assert.equal(optionsService.options['cursorStyle'], 'block'); - assert.equal(optionsService.options['cursorBlink'], false); + assert.equal(coreService.decPrivateModes.cursorStyle, 'block'); + assert.equal(coreService.decPrivateModes.cursorBlink, false); optionsService.options = clone(DEFAULT_OPTIONS); inputHandler.setCursorStyle(Params.fromArray([3])); - assert.equal(optionsService.options['cursorStyle'], 'underline'); - assert.equal(optionsService.options['cursorBlink'], true); + assert.equal(coreService.decPrivateModes.cursorStyle, 'underline'); + assert.equal(coreService.decPrivateModes.cursorBlink, true); optionsService.options = clone(DEFAULT_OPTIONS); inputHandler.setCursorStyle(Params.fromArray([4])); - assert.equal(optionsService.options['cursorStyle'], 'underline'); - assert.equal(optionsService.options['cursorBlink'], false); + assert.equal(coreService.decPrivateModes.cursorStyle, 'underline'); + assert.equal(coreService.decPrivateModes.cursorBlink, false); optionsService.options = clone(DEFAULT_OPTIONS); inputHandler.setCursorStyle(Params.fromArray([5])); - assert.equal(optionsService.options['cursorStyle'], 'bar'); - assert.equal(optionsService.options['cursorBlink'], true); + assert.equal(coreService.decPrivateModes.cursorStyle, 'bar'); + assert.equal(coreService.decPrivateModes.cursorBlink, true); optionsService.options = clone(DEFAULT_OPTIONS); inputHandler.setCursorStyle(Params.fromArray([6])); - assert.equal(optionsService.options['cursorStyle'], 'bar'); - assert.equal(optionsService.options['cursorBlink'], false); + assert.equal(coreService.decPrivateModes.cursorStyle, 'bar'); + assert.equal(coreService.decPrivateModes.cursorBlink, false); }); }); describe('setMode', () => { From ef67b42fbd80a584f3897c5614cda8bf13ffadc9 Mon Sep 17 00:00:00 2001 From: Daniel Imms <2193314+Tyriar@users.noreply.github.com> Date: Mon, 6 Jan 2025 09:59:00 -0800 Subject: [PATCH 13/29] Demo test button for common ligatures Part of #5231 --- demo/client.ts | 13 +++++++++++++ demo/index.html | 3 +++ 2 files changed, 16 insertions(+) diff --git a/demo/client.ts b/demo/client.ts index f750321015..7a1ac72b13 100644 --- a/demo/client.ts +++ b/demo/client.ts @@ -240,6 +240,7 @@ if (document.location.pathname === '/test') { document.getElementById('add-decoration').addEventListener('click', addDecoration); document.getElementById('add-overview-ruler').addEventListener('click', addOverviewRuler); document.getElementById('decoration-stress-test').addEventListener('click', decorationStressTest); + document.getElementById('ligatures-test').addEventListener('click', ligaturesTest); document.getElementById('weblinks-test').addEventListener('click', testWeblinks); document.getElementById('bce').addEventListener('click', coloredErase); addVtButtons(); @@ -1307,6 +1308,18 @@ function addVtButtons(): void { document.querySelector('#vt-container').appendChild(vtFragment); } +function ligaturesTest(): void { + term.write([ + '', + '-<< -< -<- <-- <--- <<- <- -> ->> --> ---> ->- >- >>-', + '=<< =< =<= <== <=== <<= <= => =>> ==> ===> =>= >= >>=', + '<-> <--> <---> <----> <=> <==> <===> <====> :: ::: __', + '<~~ /> ~~> == != /= ~= <> === !== !=== =/= =!=', + '<: := *= *+ <* <*> *> <| <|> |> <. <.> .> +* =* =: :>', + '(* *) /* */ [| |] {| |} ++ +++ \/ /\ |- -| <---> <----> <=> <==> <===> <====> :: ::: __', '<~~ /> ~~> == != /= ~= <> === !== !=== =/= =!=', '<: := *= *+ <* <*> *> <| <|> |> <. <.> .> +* =* =: :>', - '(* *) /* */ [| |] {| |} ++ +++ \/ /\ |- -|