mirror of
https://github.com/ksyasuda/SubMiner.git
synced 2026-05-26 12:55:16 -07:00
upgrade Electron 39→42 and fix Hyprland overlay z-order/placement (#79)
This commit is contained in:
@@ -11,6 +11,7 @@ export interface OverlayVisibilityRuntimeDeps {
|
||||
getModalActive: () => boolean;
|
||||
getVisibleOverlayVisible: () => boolean;
|
||||
getForceMousePassthrough: () => boolean;
|
||||
getSuspendVisibleOverlay?: () => boolean;
|
||||
getOverlayInteractionActive?: () => boolean;
|
||||
getWindowTracker: () => BaseWindowTracker | null;
|
||||
getLastKnownWindowsForegroundProcessName?: () => string | null;
|
||||
@@ -43,6 +44,7 @@ export function createOverlayVisibilityRuntimeService(
|
||||
updateVisibleOverlayVisibility(): void {
|
||||
const visibleOverlayVisible = deps.getVisibleOverlayVisible();
|
||||
const forceMousePassthrough = deps.getForceMousePassthrough();
|
||||
const suspendVisibleOverlay = deps.getSuspendVisibleOverlay?.() ?? false;
|
||||
const windowTracker = deps.getWindowTracker();
|
||||
const mainWindow = deps.getMainWindow();
|
||||
|
||||
@@ -50,6 +52,7 @@ export function createOverlayVisibilityRuntimeService(
|
||||
visibleOverlayVisible,
|
||||
modalActive: deps.getModalActive(),
|
||||
forceMousePassthrough,
|
||||
suspendVisibleOverlay,
|
||||
overlayInteractionActive: deps.getOverlayInteractionActive?.() ?? false,
|
||||
mainWindow,
|
||||
windowTracker,
|
||||
|
||||
@@ -50,7 +50,7 @@ test('linux mpv fullscreen overlay refresh burst schedules overlay refresh work
|
||||
}
|
||||
});
|
||||
|
||||
test('linux mpv fullscreen overlay refresh update cancels burst when fullscreen exits', async () => {
|
||||
test('linux mpv fullscreen overlay refresh update schedules a fresh burst when fullscreen exits', async () => {
|
||||
const originalPlatformDescriptor = Object.getOwnPropertyDescriptor(process, 'platform');
|
||||
Object.defineProperty(process, 'platform', {
|
||||
configurable: true,
|
||||
@@ -82,8 +82,11 @@ test('linux mpv fullscreen overlay refresh update cancels burst when fullscreen
|
||||
|
||||
await new Promise((resolve) => setTimeout(resolve, 80));
|
||||
|
||||
assert.equal(nextCancel, null);
|
||||
assert.deepEqual(calls, []);
|
||||
assert.equal(typeof nextCancel, 'function');
|
||||
assert.ok(calls.includes('updateVisibleOverlayVisibility'));
|
||||
assert.ok(calls.includes('hide'));
|
||||
assert.ok(calls.includes('showInactive'));
|
||||
assert.ok(calls.includes('ensureOverlayWindowLevel'));
|
||||
} finally {
|
||||
clearLinuxMpvFullscreenOverlayRefreshTimeouts();
|
||||
if (originalPlatformDescriptor) {
|
||||
|
||||
@@ -68,14 +68,11 @@ export function scheduleLinuxVisibleOverlayFullscreenRefreshBurst(
|
||||
}
|
||||
|
||||
export function updateLinuxMpvFullscreenOverlayRefreshBurst(
|
||||
isFullscreen: boolean,
|
||||
_isFullscreen: boolean,
|
||||
deps: LinuxMpvFullscreenOverlayRefreshDeps,
|
||||
cancelCurrentBurst: CancelLinuxMpvFullscreenOverlayRefreshBurst | null,
|
||||
): CancelLinuxMpvFullscreenOverlayRefreshBurst | null {
|
||||
cancelCurrentBurst?.();
|
||||
if (!isFullscreen) {
|
||||
return null;
|
||||
}
|
||||
|
||||
return scheduleLinuxVisibleOverlayFullscreenRefreshBurst(deps);
|
||||
}
|
||||
|
||||
@@ -15,6 +15,7 @@ test('overlay visibility runtime main deps builder maps state and geometry callb
|
||||
getModalActive: () => true,
|
||||
getVisibleOverlayVisible: () => true,
|
||||
getForceMousePassthrough: () => true,
|
||||
getSuspendVisibleOverlay: () => true,
|
||||
getOverlayInteractionActive: () => true,
|
||||
getWindowTracker: () => tracker,
|
||||
getLastKnownWindowsForegroundProcessName: () => 'mpv',
|
||||
@@ -41,6 +42,7 @@ test('overlay visibility runtime main deps builder maps state and geometry callb
|
||||
assert.equal(deps.getModalActive(), true);
|
||||
assert.equal(deps.getVisibleOverlayVisible(), true);
|
||||
assert.equal(deps.getForceMousePassthrough(), true);
|
||||
assert.equal(deps.getSuspendVisibleOverlay?.(), true);
|
||||
assert.equal(deps.getOverlayInteractionActive?.(), true);
|
||||
assert.equal(deps.getLastKnownWindowsForegroundProcessName?.(), 'mpv');
|
||||
assert.equal(deps.getWindowsOverlayProcessName?.(), 'subminer');
|
||||
|
||||
@@ -10,6 +10,7 @@ export function createBuildOverlayVisibilityRuntimeMainDepsHandler(
|
||||
getModalActive: () => deps.getModalActive(),
|
||||
getVisibleOverlayVisible: () => deps.getVisibleOverlayVisible(),
|
||||
getForceMousePassthrough: () => deps.getForceMousePassthrough(),
|
||||
getSuspendVisibleOverlay: () => deps.getSuspendVisibleOverlay?.() ?? false,
|
||||
getOverlayInteractionActive: () => deps.getOverlayInteractionActive?.() ?? false,
|
||||
getWindowTracker: () => deps.getWindowTracker(),
|
||||
getLastKnownWindowsForegroundProcessName: () =>
|
||||
|
||||
@@ -15,9 +15,16 @@ test('overlay window layout main deps builders map callbacks', () => {
|
||||
visible.setOverlayWindowBounds({ x: 0, y: 0, width: 1, height: 1 });
|
||||
|
||||
const level = createBuildEnsureOverlayWindowLevelMainDepsHandler({
|
||||
shouldSuppressOverlayWindowLevel: () => {
|
||||
calls.push('ensure-suppressed-check');
|
||||
return false;
|
||||
},
|
||||
ensureOverlayWindowLevelCore: () => calls.push('ensure'),
|
||||
afterEnsureOverlayWindowLevel: () => calls.push('ensure-after'),
|
||||
})();
|
||||
assert.equal(level.shouldSuppressOverlayWindowLevel?.({}), false);
|
||||
level.ensureOverlayWindowLevelCore({});
|
||||
level.afterEnsureOverlayWindowLevel?.({});
|
||||
|
||||
const order = createBuildEnforceOverlayLayerOrderMainDepsHandler({
|
||||
enforceOverlayLayerOrderCore: () => calls.push('order'),
|
||||
@@ -34,5 +41,12 @@ test('overlay window layout main deps builders map callbacks', () => {
|
||||
assert.deepEqual(order.getMainWindow(), { kind: 'main' });
|
||||
order.ensureOverlayWindowLevel({});
|
||||
|
||||
assert.deepEqual(calls, ['visible', 'ensure', 'order', 'order-level']);
|
||||
assert.deepEqual(calls, [
|
||||
'visible',
|
||||
'ensure-suppressed-check',
|
||||
'ensure',
|
||||
'ensure-after',
|
||||
'order',
|
||||
'order-level',
|
||||
]);
|
||||
});
|
||||
|
||||
@@ -23,7 +23,11 @@ export function createBuildEnsureOverlayWindowLevelMainDepsHandler(
|
||||
deps: EnsureOverlayWindowLevelMainDeps,
|
||||
) {
|
||||
return (): EnsureOverlayWindowLevelMainDeps => ({
|
||||
shouldSuppressOverlayWindowLevel: (window: unknown) =>
|
||||
deps.shouldSuppressOverlayWindowLevel?.(window) ?? false,
|
||||
ensureOverlayWindowLevelCore: (window: unknown) => deps.ensureOverlayWindowLevelCore(window),
|
||||
afterEnsureOverlayWindowLevel: (window: unknown) =>
|
||||
deps.afterEnsureOverlayWindowLevel?.(window),
|
||||
});
|
||||
}
|
||||
|
||||
|
||||
@@ -36,9 +36,28 @@ test('ensure overlay window level handler delegates to core', () => {
|
||||
const calls: string[] = [];
|
||||
const ensureLevel = createEnsureOverlayWindowLevelHandler({
|
||||
ensureOverlayWindowLevelCore: () => calls.push('core'),
|
||||
afterEnsureOverlayWindowLevel: () => calls.push('after'),
|
||||
});
|
||||
ensureLevel({});
|
||||
assert.deepEqual(calls, ['core']);
|
||||
assert.deepEqual(calls, ['core', 'after']);
|
||||
});
|
||||
|
||||
test('ensure overlay window level handler skips while top reassertion is suppressed', () => {
|
||||
const calls: string[] = [];
|
||||
const window = {};
|
||||
const ensureLevel = createEnsureOverlayWindowLevelHandler({
|
||||
shouldSuppressOverlayWindowLevel: (nextWindow) => {
|
||||
assert.equal(nextWindow, window);
|
||||
calls.push('suppress-check');
|
||||
return true;
|
||||
},
|
||||
ensureOverlayWindowLevelCore: () => calls.push('core'),
|
||||
afterEnsureOverlayWindowLevel: () => calls.push('after'),
|
||||
});
|
||||
|
||||
ensureLevel(window);
|
||||
|
||||
assert.deepEqual(calls, ['suppress-check']);
|
||||
});
|
||||
|
||||
test('enforce overlay layer order handler forwards resolved state', () => {
|
||||
|
||||
@@ -11,10 +11,16 @@ export function createUpdateVisibleOverlayBoundsHandler(deps: {
|
||||
}
|
||||
|
||||
export function createEnsureOverlayWindowLevelHandler(deps: {
|
||||
shouldSuppressOverlayWindowLevel?: (window: unknown) => boolean;
|
||||
ensureOverlayWindowLevelCore: (window: unknown) => void;
|
||||
afterEnsureOverlayWindowLevel?: (window: unknown) => void;
|
||||
}) {
|
||||
return (window: unknown): void => {
|
||||
if (deps.shouldSuppressOverlayWindowLevel?.(window) === true) {
|
||||
return;
|
||||
}
|
||||
deps.ensureOverlayWindowLevelCore(window);
|
||||
deps.afterEnsureOverlayWindowLevel?.(window);
|
||||
};
|
||||
}
|
||||
|
||||
|
||||
@@ -0,0 +1,56 @@
|
||||
import assert from 'node:assert/strict';
|
||||
import test from 'node:test';
|
||||
|
||||
import { createStatsOverlayVisibilityChangeHandler } from './stats-overlay-visibility';
|
||||
|
||||
test('stats overlay visibility handler makes overlay mouse-passive before opening stats', () => {
|
||||
const calls: string[] = [];
|
||||
const handler = createStatsOverlayVisibilityChangeHandler({
|
||||
setStatsOverlayVisibleState: (visible) => calls.push(`state:${visible}`),
|
||||
resetVisibleOverlayInteraction: () => calls.push('reset-interaction'),
|
||||
getMainWindow: () =>
|
||||
({
|
||||
isDestroyed: () => false,
|
||||
isVisible: () => true,
|
||||
setIgnoreMouseEvents: (ignore: boolean, options?: { forward?: boolean }) => {
|
||||
calls.push(`mouse-ignore:${ignore}:${options?.forward === true ? 'forward' : 'plain'}`);
|
||||
},
|
||||
}) as never,
|
||||
updateVisibleOverlayVisibility: () => calls.push('update-visible'),
|
||||
});
|
||||
|
||||
handler(true);
|
||||
|
||||
assert.deepEqual(calls, [
|
||||
'state:true',
|
||||
'reset-interaction',
|
||||
'mouse-ignore:true:forward',
|
||||
'update-visible',
|
||||
]);
|
||||
});
|
||||
|
||||
test('stats overlay visibility handler restores overlay then leaves mpv mouse-responsive after close', () => {
|
||||
const calls: string[] = [];
|
||||
const handler = createStatsOverlayVisibilityChangeHandler({
|
||||
setStatsOverlayVisibleState: (visible) => calls.push(`state:${visible}`),
|
||||
resetVisibleOverlayInteraction: () => calls.push('reset-interaction'),
|
||||
getMainWindow: () =>
|
||||
({
|
||||
isDestroyed: () => false,
|
||||
isVisible: () => true,
|
||||
setIgnoreMouseEvents: (ignore: boolean, options?: { forward?: boolean }) => {
|
||||
calls.push(`mouse-ignore:${ignore}:${options?.forward === true ? 'forward' : 'plain'}`);
|
||||
},
|
||||
}) as never,
|
||||
updateVisibleOverlayVisibility: () => calls.push('update-visible'),
|
||||
});
|
||||
|
||||
handler(false);
|
||||
|
||||
assert.deepEqual(calls, [
|
||||
'state:false',
|
||||
'reset-interaction',
|
||||
'update-visible',
|
||||
'mouse-ignore:true:forward',
|
||||
]);
|
||||
});
|
||||
@@ -0,0 +1,33 @@
|
||||
type StatsOverlayVisibilityWindow = {
|
||||
isDestroyed: () => boolean;
|
||||
isVisible: () => boolean;
|
||||
setIgnoreMouseEvents: (ignore: boolean, options?: { forward?: boolean }) => void;
|
||||
};
|
||||
|
||||
function makeOverlayMousePassive(window: StatsOverlayVisibilityWindow | null): void {
|
||||
if (!window || window.isDestroyed() || !window.isVisible()) {
|
||||
return;
|
||||
}
|
||||
window.setIgnoreMouseEvents(true, { forward: true });
|
||||
}
|
||||
|
||||
export function createStatsOverlayVisibilityChangeHandler(deps: {
|
||||
setStatsOverlayVisibleState: (visible: boolean) => void;
|
||||
resetVisibleOverlayInteraction: () => void;
|
||||
getMainWindow: () => StatsOverlayVisibilityWindow | null;
|
||||
updateVisibleOverlayVisibility: () => void;
|
||||
}) {
|
||||
return (visible: boolean): void => {
|
||||
deps.setStatsOverlayVisibleState(visible);
|
||||
deps.resetVisibleOverlayInteraction();
|
||||
|
||||
if (visible) {
|
||||
makeOverlayMousePassive(deps.getMainWindow());
|
||||
deps.updateVisibleOverlayVisibility();
|
||||
return;
|
||||
}
|
||||
|
||||
deps.updateVisibleOverlayVisibility();
|
||||
makeOverlayMousePassive(deps.getMainWindow());
|
||||
};
|
||||
}
|
||||
Reference in New Issue
Block a user