diff --git a/src/renderer/src/lib/pane-manager/pane-lifecycle.test.ts b/src/renderer/src/lib/pane-manager/pane-lifecycle.test.ts index cb41bca09b8..6e895a3ca4c 100644 --- a/src/renderer/src/lib/pane-manager/pane-lifecycle.test.ts +++ b/src/renderer/src/lib/pane-manager/pane-lifecycle.test.ts @@ -6,7 +6,7 @@ import { markComplexScriptOutput, resetTerminalWebglSuggestion } from './pane-webgl-renderer' -import { attachLigatures, openTerminal } from './pane-lifecycle' +import { attachLigatures, openTerminal, setLigaturesEnabled } from './pane-lifecycle' import { buildDefaultTerminalOptions, DEFAULT_TERMINAL_FAST_SCROLL_SENSITIVITY, @@ -410,6 +410,71 @@ describe('attachLigatures', () => { }) }) +describe('setLigaturesEnabled with a stale attach backoff', () => { + beforeEach(() => { + webglMock.contextLossHandler = null + webglMock.dispose.mockClear() + vi.mocked(WebglAddon).mockClear() + resetTerminalWebglSuggestion() + vi.spyOn(console, 'warn').mockImplementation(() => {}) + vi.stubGlobal('navigator', { + platform: 'MacIntel', + userAgent: 'Mozilla/5.0 (Macintosh; Intel Mac OS X 10_15_7)' + }) + vi.stubGlobal('requestAnimationFrame', (callback: FrameRequestCallback) => { + callback(16) + return 1 + }) + }) + + afterEach(() => { + vi.unstubAllGlobals() + vi.restoreAllMocks() + }) + + // Why: a live WebGL pane must survive a ligature toggle even when another + // pane latched the module-global attach backoff on a transient failure. The + // reattach here has the same "live addon proves context creation works" + // precondition as rebuildAttachedWebgl, so it must clear the backoff too. + function latchAttachBackoffFromUnrelatedPane(): void { + vi.mocked(WebglAddon).mockImplementationOnce(() => { + throw new Error('WebGL2 not supported null') + }) + const failingPane = createPane() + failingPane.terminalGpuAcceleration = 'on' + attachWebgl(failingPane) + expect(failingPane.webglAddon).toBeNull() + } + + it('keeps a live pane on WebGL when enabling ligatures', () => { + const pane = createPane() + pane.terminalGpuAcceleration = 'on' + attachWebgl(pane) + expect(pane.webglAddon).not.toBeNull() + + latchAttachBackoffFromUnrelatedPane() + + setLigaturesEnabled(pane, true) + + expect(pane.webglAddon).not.toBeNull() + }) + + it('keeps a live pane on WebGL when disabling ligatures', () => { + const pane = createPane() + pane.terminalGpuAcceleration = 'on' + attachWebgl(pane) + attachLigatures(pane) + expect(pane.webglAddon).not.toBeNull() + expect(pane.ligaturesAddon).not.toBeNull() + + latchAttachBackoffFromUnrelatedPane() + + setLigaturesEnabled(pane, false) + + expect(pane.webglAddon).not.toBeNull() + }) +}) + describe('openTerminal — Unicode 11 ordering', () => { beforeEach(() => { vi.stubGlobal('requestAnimationFrame', () => 1) diff --git a/src/renderer/src/lib/pane-manager/pane-lifecycle.ts b/src/renderer/src/lib/pane-manager/pane-lifecycle.ts index eb296a1d675..1cf09d67cf9 100644 --- a/src/renderer/src/lib/pane-manager/pane-lifecycle.ts +++ b/src/renderer/src/lib/pane-manager/pane-lifecycle.ts @@ -16,7 +16,12 @@ import { activateOrcaTerminalUnicodeProvider } from './pane-terminal-unicode-pro import { attachTerminalMouseWheelMultiplier } from './pane-terminal-mouse-wheel' import { attachTerminalScrollIntentTracking } from './terminal-scroll-intent' import { attachDomRendererFocusClassSync } from './pane-dom-focus-class-sync' -import { attachWebgl, cancelPendingWebglRefresh, disposeWebgl } from './pane-webgl-renderer' +import { + attachWebgl, + cancelPendingWebglRefresh, + clearTerminalWebglAttachBackoff, + disposeWebgl +} from './pane-webgl-renderer' // --------------------------------------------------------------------------- // Pane creation, terminal open/close, addon management @@ -150,6 +155,9 @@ export function attachLigatures(pane: ManagedPaneInternal): void { // forces a fresh atlas that includes the ligated glyphs. if (pane.webglAddon) { disposeWebgl(pane) + // Why: the live addon just proved context creation works, so a stale + // attach backoff from an unrelated pane must not downgrade this one to DOM. + clearTerminalWebglAttachBackoff() attachWebgl(pane) } } catch (err) { @@ -170,6 +178,9 @@ export function setLigaturesEnabled(pane: ManagedPaneInternal, enabled: boolean) // renders as the non-ligated fallback immediately. if (pane.webglAddon) { disposeWebgl(pane) + // Why: the live addon just proved context creation works, so a stale + // attach backoff from an unrelated pane must not downgrade this one to DOM. + clearTerminalWebglAttachBackoff() attachWebgl(pane) } }