diff --git a/addons/addon-webgl/src/WebglRenderer.ts b/addons/addon-webgl/src/WebglRenderer.ts index 216b8404..6c270d12 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 02b9f7be..f7503210 100644 --- a/demo/client.ts +++ b/demo/client.ts @@ -1266,9 +1266,9 @@ 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]; + const writeCsiSplit = writeCsi.split('|'); + const prefix = writeCsiSplit.length === 2 ? writeCsiSplit[0] : ''; + const suffix = writeCsiSplit[writeCsiSplit.length - 1]; element.addEventListener(`click`, () => term.write(csi(`${prefix}${inputs.map(e => e.value).join(';')}${suffix}`))); const desc = document.createElement('span'); @@ -1281,22 +1281,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]; diff --git a/src/browser/renderer/dom/DomRenderer.ts b/src/browser/renderer/dom/DomRenderer.ts index f6fda22e..1de446c4 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.test.ts b/src/common/InputHandler.test.ts index baae735c..1280417b 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', () => { diff --git a/src/common/InputHandler.ts b/src/common/InputHandler.ts index b94d7855..8308e6de 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,31 +2724,37 @@ 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 - * - 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.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.length === 0 ? 1 : params.params[0]; + if (param === 0) { + this._coreService.decPrivateModes.cursorStyle = undefined; + this._coreService.decPrivateModes.cursorBlink = undefined; + } else { + switch (param) { + 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 8c9634db..127e1f24 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 289aa1f6..c254d330 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 9c41fc1a..5bee6356 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,