From 490f693361fce62835b159fbbf1520cbe33a7f3a Mon Sep 17 00:00:00 2001 From: sudacode Date: Mon, 27 Apr 2026 20:31:00 -0700 Subject: [PATCH] Cancel pending Linux MPV fullscreen overlay refresh bursts - return a cancel handle from the Linux refresh burst scheduler - clear pending refresh bursts when overlays hide or windows close - tighten the burst test polling to wait for the async refresh --- src/main.ts | 43 ++++++++++++++----- ...nux-mpv-fullscreen-overlay-refresh.test.ts | 5 ++- .../linux-mpv-fullscreen-overlay-refresh.ts | 6 ++- 3 files changed, 40 insertions(+), 14 deletions(-) diff --git a/src/main.ts b/src/main.ts index 20045373..1417eda4 100644 --- a/src/main.ts +++ b/src/main.ts @@ -34,6 +34,7 @@ import { applyControllerConfigUpdate } from './main/controller-config-update.js' import { openPlaylistBrowser as openPlaylistBrowserRuntime } from './main/runtime/playlist-browser-open'; import { createDiscordRpcClient } from './main/runtime/discord-rpc-client.js'; import { + type CancelLinuxMpvFullscreenOverlayRefreshBurst, clearLinuxMpvFullscreenOverlayRefreshTimeouts, scheduleLinuxVisibleOverlayFullscreenRefreshBurst, } from './main/runtime/linux-mpv-fullscreen-overlay-refresh'; @@ -1399,6 +1400,9 @@ const subtitleProcessingController = createSubtitleProcessingController( let subtitlePrefetchService: SubtitlePrefetchService | null = null; let subtitlePrefetchRefreshTimer: ReturnType | null = null; let lastObservedTimePos = 0; +let cancelLinuxMpvFullscreenOverlayRefreshBurst: + | CancelLinuxMpvFullscreenOverlayRefreshBurst + | null = null; const SEEK_THRESHOLD_SECONDS = 3; function clearScheduledSubtitlePrefetchRefresh(): void { @@ -1408,6 +1412,11 @@ function clearScheduledSubtitlePrefetchRefresh(): void { } } +function cancelPendingLinuxMpvFullscreenOverlayRefreshBurst(): void { + cancelLinuxMpvFullscreenOverlayRefreshBurst?.(); + cancelLinuxMpvFullscreenOverlayRefreshBurst = null; +} + const subtitlePrefetchInitController = createSubtitlePrefetchInitController({ getCurrentService: () => subtitlePrefetchService, setCurrentService: (service) => { @@ -3106,8 +3115,10 @@ const { stopTexthookerService: () => texthookerService.stop(), clearWindowsVisibleOverlayForegroundPollLoop: () => clearWindowsVisibleOverlayForegroundPollLoop(), - clearLinuxMpvFullscreenOverlayRefreshTimeouts: () => - clearLinuxMpvFullscreenOverlayRefreshTimeouts(), + clearLinuxMpvFullscreenOverlayRefreshTimeouts: () => { + cancelLinuxMpvFullscreenOverlayRefreshBurst = null; + clearLinuxMpvFullscreenOverlayRefreshTimeouts(); + }, getMainOverlayWindow: () => overlayManager.getMainWindow(), clearMainOverlayWindow: () => overlayManager.setMainWindow(null), getModalOverlayWindow: () => overlayManager.getModalWindow(), @@ -3813,14 +3824,15 @@ const { lastObservedTimePos = time; }, onFullscreenChange: () => { - scheduleLinuxVisibleOverlayFullscreenRefreshBurst({ - overlayManager: { - getMainWindow: () => overlayManager.getMainWindow(), - getVisibleOverlayVisible: () => overlayManager.getVisibleOverlayVisible(), - }, - overlayVisibilityRuntime, - ensureOverlayWindowLevel: (window) => ensureOverlayWindowLevel(window), - }); + cancelLinuxMpvFullscreenOverlayRefreshBurst = + scheduleLinuxVisibleOverlayFullscreenRefreshBurst({ + overlayManager: { + getMainWindow: () => overlayManager.getMainWindow(), + getVisibleOverlayVisible: () => overlayManager.getVisibleOverlayVisible(), + }, + overlayVisibilityRuntime, + ensureOverlayWindowLevel: (window) => ensureOverlayWindowLevel(window), + }); }, onSubtitleTrackChange: (sid) => { scheduleSubtitlePrefetchRefresh(); @@ -5149,6 +5161,7 @@ const { createMainWindow: createMainWindowHandler, createModalWindow: createModa onWindowContentReady: () => overlayVisibilityRuntime.updateVisibleOverlayVisibility(), onWindowClosed: (windowKind) => { if (windowKind === 'visible') { + cancelPendingLinuxMpvFullscreenOverlayRefreshBurst(); overlayManager.setMainWindow(null); } else { overlayManager.setModalWindow(null); @@ -5396,6 +5409,9 @@ function ensureOverlayWindowsReadyForVisibilityActions(): void { function setVisibleOverlayVisible(visible: boolean): void { ensureOverlayWindowsReadyForVisibilityActions(); + if (!visible) { + cancelPendingLinuxMpvFullscreenOverlayRefreshBurst(); + } if (visible) { void ensureOverlayMpvSubtitlesHidden(); } @@ -5405,13 +5421,18 @@ function setVisibleOverlayVisible(visible: boolean): void { function toggleVisibleOverlay(): void { ensureOverlayWindowsReadyForVisibilityActions(); - if (!overlayManager.getVisibleOverlayVisible()) { + if (overlayManager.getVisibleOverlayVisible()) { + cancelPendingLinuxMpvFullscreenOverlayRefreshBurst(); + } else { void ensureOverlayMpvSubtitlesHidden(); } toggleVisibleOverlayHandler(); syncOverlayMpvSubtitleSuppression(); } function setOverlayVisible(visible: boolean): void { + if (!visible) { + cancelPendingLinuxMpvFullscreenOverlayRefreshBurst(); + } if (visible) { void ensureOverlayMpvSubtitlesHidden(); } diff --git a/src/main/runtime/linux-mpv-fullscreen-overlay-refresh.test.ts b/src/main/runtime/linux-mpv-fullscreen-overlay-refresh.test.ts index 48a8215f..a466dce4 100644 --- a/src/main/runtime/linux-mpv-fullscreen-overlay-refresh.test.ts +++ b/src/main/runtime/linux-mpv-fullscreen-overlay-refresh.test.ts @@ -32,7 +32,10 @@ test('linux mpv fullscreen overlay refresh burst schedules overlay refresh work ensureOverlayWindowLevel: () => calls.push('ensureOverlayWindowLevel'), }); - await new Promise((resolve) => setTimeout(resolve, 10)); + const deadline = Date.now() + 200; + while (!calls.includes('updateVisibleOverlayVisibility') && Date.now() < deadline) { + await new Promise((resolve) => setTimeout(resolve, 5)); + } assert.ok(calls.includes('updateVisibleOverlayVisibility')); assert.ok(calls.includes('hide')); diff --git a/src/main/runtime/linux-mpv-fullscreen-overlay-refresh.ts b/src/main/runtime/linux-mpv-fullscreen-overlay-refresh.ts index a0287fc3..a3c5be5b 100644 --- a/src/main/runtime/linux-mpv-fullscreen-overlay-refresh.ts +++ b/src/main/runtime/linux-mpv-fullscreen-overlay-refresh.ts @@ -15,6 +15,7 @@ export type LinuxMpvFullscreenOverlayRefreshDeps = { }; ensureOverlayWindowLevel: (window: LinuxMpvFullscreenOverlayWindow) => void; }; +export type CancelLinuxMpvFullscreenOverlayRefreshBurst = () => void; const LINUX_MPV_FULLSCREEN_OVERLAY_REFRESH_DELAYS_MS = [0, 50, 150, 300, 600] as const; let linuxMpvFullscreenOverlayRefreshTimeouts: Array> = []; @@ -47,9 +48,9 @@ function refreshLinuxVisibleOverlayAfterMpvFullscreenChange( export function scheduleLinuxVisibleOverlayFullscreenRefreshBurst( deps: LinuxMpvFullscreenOverlayRefreshDeps, -): void { +): CancelLinuxMpvFullscreenOverlayRefreshBurst { if (process.platform !== 'linux') { - return; + return () => {}; } clearLinuxMpvFullscreenOverlayRefreshTimeouts(); @@ -63,6 +64,7 @@ export function scheduleLinuxVisibleOverlayFullscreenRefreshBurst( refreshTimeout.unref?.(); linuxMpvFullscreenOverlayRefreshTimeouts.push(refreshTimeout); } + return clearLinuxMpvFullscreenOverlayRefreshTimeouts; } export { clearLinuxMpvFullscreenOverlayRefreshTimeouts };