From 5d24866042f2ced4854c58c718588790d40c91c0 Mon Sep 17 00:00:00 2001 From: Daniel Imms <2193314+Tyriar@users.noreply.github.com> Date: Wed, 24 Dec 2025 08:35:06 -0800 Subject: [PATCH] Move customGlyphs option into webgl addon Fixes #5480 --- addons/addon-webgl/src/CharAtlasCache.ts | 5 +- addons/addon-webgl/src/CharAtlasUtils.ts | 4 +- addons/addon-webgl/src/WebglAddon.ts | 12 +++-- addons/addon-webgl/src/WebglRenderer.ts | 4 +- addons/addon-webgl/typings/addon-webgl.d.ts | 27 ++++++++++- demo/client.ts | 54 ++++++++++++++++----- src/browser/services/RenderService.ts | 1 - src/common/services/OptionsService.ts | 1 - src/common/services/Services.ts | 1 - typings/xterm-headless.d.ts | 17 ------- typings/xterm.d.ts | 17 ------- 11 files changed, 83 insertions(+), 60 deletions(-) diff --git a/addons/addon-webgl/src/CharAtlasCache.ts b/addons/addon-webgl/src/CharAtlasCache.ts index ea02581b..62ff4ff9 100644 --- a/addons/addon-webgl/src/CharAtlasCache.ts +++ b/addons/addon-webgl/src/CharAtlasCache.ts @@ -32,9 +32,10 @@ export function acquireTextureAtlas( deviceCharWidth: number, deviceCharHeight: number, devicePixelRatio: number, - deviceMaxTextureSize: number + deviceMaxTextureSize: number, + customGlyphs: boolean = true ): ITextureAtlas { - const newConfig = generateConfig(deviceCellWidth, deviceCellHeight, deviceCharWidth, deviceCharHeight, options, colors, devicePixelRatio, deviceMaxTextureSize); + const newConfig = generateConfig(deviceCellWidth, deviceCellHeight, deviceCharWidth, deviceCharHeight, options, colors, devicePixelRatio, deviceMaxTextureSize, customGlyphs); // Check to see if the terminal already owns this config for (let i = 0; i < charAtlasCache.length; i++) { diff --git a/addons/addon-webgl/src/CharAtlasUtils.ts b/addons/addon-webgl/src/CharAtlasUtils.ts index db601383..4ce103e9 100644 --- a/addons/addon-webgl/src/CharAtlasUtils.ts +++ b/addons/addon-webgl/src/CharAtlasUtils.ts @@ -9,7 +9,7 @@ import { ITerminalOptions } from '@xterm/xterm'; import { IColorSet, ReadonlyColorSet } from 'browser/Types'; import { NULL_COLOR } from 'common/Color'; -export function generateConfig(deviceCellWidth: number, deviceCellHeight: number, deviceCharWidth: number, deviceCharHeight: number, options: Required, colors: ReadonlyColorSet, devicePixelRatio: number, deviceMaxTextureSize: number): ICharAtlasConfig { +export function generateConfig(deviceCellWidth: number, deviceCellHeight: number, deviceCharWidth: number, deviceCharHeight: number, options: Required, colors: ReadonlyColorSet, devicePixelRatio: number, deviceMaxTextureSize: number, customGlyphs: boolean = true): ICharAtlasConfig { // null out some fields that don't matter const clonedColors: IColorSet = { foreground: colors.foreground, @@ -32,7 +32,7 @@ export function generateConfig(deviceCellWidth: number, deviceCellHeight: number halfContrastCache: colors.halfContrastCache }; return { - customGlyphs: options.customGlyphs, + customGlyphs, devicePixelRatio, deviceMaxTextureSize, letterSpacing: options.letterSpacing, diff --git a/addons/addon-webgl/src/WebglAddon.ts b/addons/addon-webgl/src/WebglAddon.ts index 49a796de..72e0835e 100644 --- a/addons/addon-webgl/src/WebglAddon.ts +++ b/addons/addon-webgl/src/WebglAddon.ts @@ -4,7 +4,7 @@ */ import type { ITerminalAddon, Terminal } from '@xterm/xterm'; -import type { WebglAddon as IWebglApi } from '@xterm/addon-webgl'; +import type { IWebglAddonOptions, WebglAddon as IWebglApi } from '@xterm/addon-webgl'; import { ICharacterJoinerService, ICharSizeService, ICoreBrowserService, IRenderService, IThemeService } from 'browser/services/Services'; import { ITerminal } from 'browser/Types'; import { Disposable, toDisposable } from 'vs/base/common/lifecycle'; @@ -28,9 +28,10 @@ export class WebglAddon extends Disposable implements ITerminalAddon , IWebglApi private readonly _onContextLoss = this._register(new Emitter()); public readonly onContextLoss = this._onContextLoss.event; - constructor( - private _preserveDrawingBuffer?: boolean - ) { + private readonly _customGlyphs: boolean; + private readonly _preserveDrawingBuffer?: boolean; + + constructor(options?: IWebglAddonOptions) { if (isSafari && getSafariVersion() < 16) { // Perform an extra check to determine if Webgl2 is manually enabled in developer settings const contextAttributes = { @@ -44,6 +45,8 @@ export class WebglAddon extends Disposable implements ITerminalAddon , IWebglApi } } super(); + this._customGlyphs = options?.customGlyphs ?? true; + this._preserveDrawingBuffer = options?.preserveDrawingBuffer; } public activate(terminal: Terminal): void { @@ -79,6 +82,7 @@ export class WebglAddon extends Disposable implements ITerminalAddon , IWebglApi decorationService, optionsService, themeService, + this._customGlyphs, this._preserveDrawingBuffer )); this._register(Event.forward(this._renderer.onContextLoss, this._onContextLoss)); diff --git a/addons/addon-webgl/src/WebglRenderer.ts b/addons/addon-webgl/src/WebglRenderer.ts index e3a2d0c6..fb14b3c0 100644 --- a/addons/addon-webgl/src/WebglRenderer.ts +++ b/addons/addon-webgl/src/WebglRenderer.ts @@ -72,6 +72,7 @@ export class WebglRenderer extends Disposable implements IRenderer { private readonly _decorationService: IDecorationService, private readonly _optionsService: IOptionsService, private readonly _themeService: IThemeService, + private readonly _customGlyphs: boolean = true, preserveDrawingBuffer?: boolean ) { super(); @@ -278,7 +279,8 @@ export class WebglRenderer extends Disposable implements IRenderer { this.dimensions.device.char.width, this.dimensions.device.char.height, this._coreBrowserService.dpr, - this._deviceMaxTextureSize + this._deviceMaxTextureSize, + this._customGlyphs ); if (this._charAtlas !== atlas) { this._onChangeTextureAtlas.fire(atlas.pages[0].canvas); diff --git a/addons/addon-webgl/typings/addon-webgl.d.ts b/addons/addon-webgl/typings/addon-webgl.d.ts index 1d7e1510..1f09776f 100644 --- a/addons/addon-webgl/typings/addon-webgl.d.ts +++ b/addons/addon-webgl/typings/addon-webgl.d.ts @@ -32,7 +32,7 @@ declare module '@xterm/addon-webgl' { */ public readonly onRemoveTextureAtlasCanvas: IEvent; - constructor(preserveDrawingBuffer?: boolean); + constructor(options?: IWebglAddonOptions); /** * Activates the addon. @@ -50,4 +50,29 @@ declare module '@xterm/addon-webgl' { */ public clearTextureAtlas(): void; } + + export interface IWebglAddonOptions { + /** + * Whether to draw custom glyphs instead of using the font for the following + * unicode ranges: + * + * - Box Drawing (U+2500-U+257F) + * - Box Elements (U+2580-U+259F) + * - Braille Patterns (U+2800-U+28FF) + * - Powerline Symbols (U+E0A0–U+E0D4) + * - Symbols for Legacy Computing (U+1FB00–U+1FBFF) + * + * This will typically result in better rendering with continuous lines, + * even when line height and letter spacing is used. Note that this doesn't + * work with the DOM renderer which renders all characters using the font. + * The default is true. + */ + customGlyphs?: boolean; + + /** + * Whether to enable the preserveDrawingBuffer flag when creating the WebGL + * context. This may be useful in tests. This defaults to false. + */ + preserveDrawingBuffer?: boolean + } } diff --git a/demo/client.ts b/demo/client.ts index 75e9b4e2..644d82f1 100644 --- a/demo/client.ts +++ b/demo/client.ts @@ -617,6 +617,20 @@ function initOptions(term: Terminal): void { function initAddons(term: Terminal): void { const fragment = document.createDocumentFragment(); + + function postInitWebgl(): void { + setTimeout(() => { + setTextureAtlas(addons.webgl.instance.textureAtlas); + addons.webgl.instance.onChangeTextureAtlas(e => setTextureAtlas(e)); + addons.webgl.instance.onAddTextureAtlasCanvas(e => appendTextureAtlas(e)); + }, 500); + } + function preDisposeWebgl(): void { + if (addons.webgl.instance.textureAtlas) { + addons.webgl.instance.textureAtlas.remove(); + } + } + Object.keys(addons).forEach((name: AddonType) => { const addon = addons[name]; const checkbox = document.createElement('input') as HTMLInputElement; @@ -648,18 +662,6 @@ function initAddons(term: Terminal): void { } return; } - function postInitWebgl(): void { - setTimeout(() => { - setTextureAtlas(addons.webgl.instance.textureAtlas); - addons.webgl.instance.onChangeTextureAtlas(e => setTextureAtlas(e)); - addons.webgl.instance.onAddTextureAtlasCanvas(e => appendTextureAtlas(e)); - }, 500); - } - function preDisposeWebgl(): void { - if (addons.webgl.instance.textureAtlas) { - addons.webgl.instance.textureAtlas.remove(); - } - } if (checkbox.checked) { // HACK: Manually remove addons that cannot be changes addon.instance = new (addon as IDemoAddon>).ctor(); @@ -695,7 +697,8 @@ function initAddons(term: Terminal): void { if (addons.webgl.instance) { preDisposeWebgl(); addons.webgl.instance.dispose(); - addons.webgl.instance = new addons.webgl.ctor(); + const customGlyphsCheckbox = document.getElementById('webgl-custom-glyphs') as HTMLInputElement; + addons.webgl.instance = new addons.webgl.ctor({ customGlyphs: customGlyphsCheckbox?.checked ?? true }); term.loadAddon(addons.webgl.instance); postInitWebgl(); } @@ -711,6 +714,31 @@ function initAddons(term: Terminal): void { const wrapper = document.createElement('div'); wrapper.classList.add('addon'); wrapper.appendChild(label); + + // Add customGlyphs sub-checkbox for webgl addon + if (name === 'webgl') { + const customGlyphsCheckbox = document.createElement('input') as HTMLInputElement; + customGlyphsCheckbox.type = 'checkbox'; + customGlyphsCheckbox.checked = true; // Default to enabled + customGlyphsCheckbox.id = 'webgl-custom-glyphs'; + addDomListener(customGlyphsCheckbox, 'change', () => { + if (addons.webgl.instance) { + preDisposeWebgl(); + addons.webgl.instance.dispose(); + addons.webgl.instance = new addons.webgl.ctor({ customGlyphs: customGlyphsCheckbox.checked }); + term.loadAddon(addons.webgl.instance); + postInitWebgl(); + } + }); + const customGlyphsLabel = document.createElement('label'); + customGlyphsLabel.classList.add('addon'); + customGlyphsLabel.style.display = 'block'; + customGlyphsLabel.style.marginLeft = '20px'; + customGlyphsLabel.appendChild(customGlyphsCheckbox); + customGlyphsLabel.appendChild(document.createTextNode('customGlyphs')); + wrapper.appendChild(customGlyphsLabel); + } + fragment.appendChild(wrapper); }); const container = document.getElementById('addons-container'); diff --git a/src/browser/services/RenderService.ts b/src/browser/services/RenderService.ts index e9acbeb3..2348ac14 100644 --- a/src/browser/services/RenderService.ts +++ b/src/browser/services/RenderService.ts @@ -92,7 +92,6 @@ export class RenderService extends Disposable implements IRenderService { // Clear the renderer when the a change that could affect glyphs occurs this._register(this._optionsService.onMultipleOptionChange([ - 'customGlyphs', 'drawBoldTextInBrightColors', 'letterSpacing', 'lineHeight', diff --git a/src/common/services/OptionsService.ts b/src/common/services/OptionsService.ts index 9203a892..b33a0856 100644 --- a/src/common/services/OptionsService.ts +++ b/src/common/services/OptionsService.ts @@ -16,7 +16,6 @@ export const DEFAULT_OPTIONS: Readonly> = { cursorStyle: 'block', cursorWidth: 1, cursorInactiveStyle: 'outline', - customGlyphs: true, drawBoldTextInBrightColors: true, documentOverride: null, fastScrollSensitivity: 5, diff --git a/src/common/services/Services.ts b/src/common/services/Services.ts index 1f4b53ce..849abcd5 100644 --- a/src/common/services/Services.ts +++ b/src/common/services/Services.ts @@ -230,7 +230,6 @@ export interface ITerminalOptions { cursorStyle?: CursorStyle; cursorWidth?: number; cursorInactiveStyle?: CursorInactiveStyle; - customGlyphs?: boolean; disableStdin?: boolean; documentOverride?: any | null; drawBoldTextInBrightColors?: boolean; diff --git a/typings/xterm-headless.d.ts b/typings/xterm-headless.d.ts index 306c1d9f..2c6f971e 100644 --- a/typings/xterm-headless.d.ts +++ b/typings/xterm-headless.d.ts @@ -63,23 +63,6 @@ declare module '@xterm/headless' { */ cursorWidth?: number; - /** - * Whether to draw custom glyphs instead of using the font for the following - * unicode ranges: - * - * - Box Drawing (U+2500-U+257F) - * - Box Elements (U+2580-U+259F) - * - Braille Patterns (U+2800-U+28FF) - * - Powerline Symbols (U+E0A0–U+E0D4) - * - Symbols for Legacy Computing (U+1FB00–U+1FBFF) - * - * This will typically result in better rendering with continuous lines, - * even when line height and letter spacing is used. Note that this doesn't - * work with the DOM renderer which renders all characters using the font. - * The default is true. - */ - customGlyphs?: boolean; - /** * Whether input should be disabled. */ diff --git a/typings/xterm.d.ts b/typings/xterm.d.ts index f6bffee4..1009f5c7 100644 --- a/typings/xterm.d.ts +++ b/typings/xterm.d.ts @@ -77,23 +77,6 @@ declare module '@xterm/xterm' { */ cursorInactiveStyle?: 'outline' | 'block' | 'bar' | 'underline' | 'none'; - /** - * Whether to draw custom glyphs instead of using the font for the following - * unicode ranges: - * - * - Box Drawing (U+2500-U+257F) - * - Box Elements (U+2580-U+259F) - * - Braille Patterns (U+2800-U+28FF) - * - Powerline Symbols (U+E0A0–U+E0D4) - * - Symbols for Legacy Computing (U+1FB00–U+1FBFF) - * - * This will typically result in better rendering with continuous lines, - * even when line height and letter spacing is used. Note that this doesn't - * work with the DOM renderer which renders all characters using the font. - * The default is true. - */ - customGlyphs?: boolean; - /** * Whether input should be disabled. */