fix(macos): release overlay when mpv loses focus

This commit is contained in:
2026-05-16 17:41:58 -07:00
parent b6272b229e
commit a36e628512
6 changed files with 462 additions and 30 deletions
+2
View File
@@ -6,3 +6,5 @@ area: overlay
- Opened the stats overlay inactive on macOS so it appears over fullscreen mpv instead of switching back to SubMiner's original desktop.
- Preserved the active mpv focus state through transient macOS helper misses so subtitles do not flicker while mpv remains foreground.
- Kept fullscreen macOS overlays stable when mpv remains frontmost but window geometry temporarily disappears from the macOS window APIs.
- Released the macOS overlay when the helper reports mpv is no longer foreground so other apps are no longer covered.
- Reduced macOS window-tracker background work by preferring the compiled helper and slowing polls while mpv is stably focused.
+17 -4
View File
@@ -7,7 +7,7 @@
// It works with both bundled and unbundled mpv installations.
//
// Usage: swift get-mpv-window-macos.swift
// Output: "x,y,width,height,focused", "minimized", "active", or "not-found"
// Output: "x,y,width,height,focused", "minimized", "active", "inactive", or "not-found"
//
import Cocoa
@@ -34,6 +34,7 @@ private enum WindowLookupResult {
case visible(WindowState)
case minimized
case active
case inactive
}
private let targetMpvSocketPath: String? = {
@@ -176,11 +177,17 @@ private func isFocusedMpvWindow(ownerPid: pid_t, frontmost: FrontmostApplication
}
private func isFrontmostTargetMpv(_ frontmost: FrontmostApplicationState?) -> Bool {
guard let frontmost = frontmost else {
guard let frontmost = frontmost, frontmost.isMpv else {
return false
}
return frontmost.isMpv && windowHasTargetSocket(frontmost.pid)
if windowHasTargetSocket(frontmost.pid) {
return true
}
// When macOS says mpv is frontmost but geometry APIs miss, keep the
// overlay stable even if ps cannot expose the socket argument.
return targetMpvSocketPath != nil
}
private func windowStateFromAccessibilityAPI() -> WindowLookupResult? {
@@ -307,9 +314,13 @@ private let lookupResult: WindowLookupResult? = {
if let cgWindow = windowStateFromCoreGraphics() {
return .visible(cgWindow)
}
if isFrontmostTargetMpv(frontmostApplicationState()) {
let frontmost = frontmostApplicationState()
if isFrontmostTargetMpv(frontmost) {
return .active
}
if frontmost != nil {
return .inactive
}
return nil
}()
@@ -323,6 +334,8 @@ if let result = lookupResult {
print("minimized")
case .active:
print("active")
case .inactive:
print("inactive")
}
} else {
print("not-found")
+23 -3
View File
@@ -59,12 +59,16 @@ test('focused mpv window follows the frontmost mpv app signal', () => {
test('frontmost mpv app emits active state when geometry lookup misses', () => {
assert.ok(
source.includes('case active'),
/case\s+\.active:/.test(source),
'helper should expose an active state without window geometry',
);
assert.ok(
source.includes('frontmost.isMpv && windowHasTargetSocket(frontmost.pid)'),
'active state should be limited to the frontmost target mpv process',
source.includes('if windowHasTargetSocket(frontmost.pid)'),
'active state should still accept a matching target socket when available',
);
assert.ok(
source.includes('return targetMpvSocketPath != nil'),
'active state should preserve frontmost mpv even if command-line socket detection fails',
);
assert.ok(
source.includes('return .active'),
@@ -72,3 +76,19 @@ test('frontmost mpv app emits active state when geometry lookup misses', () => {
);
assert.ok(source.includes('print("active")'), 'active state should be printed for the tracker');
});
test('frontmost non-mpv app emits inactive state when geometry lookup misses', () => {
assert.ok(
/case\s+\.inactive:/.test(source),
'helper should expose an inactive state without window geometry',
);
assert.ok(
source.includes('if frontmost != nil'),
'helper should distinguish a known non-mpv frontmost app from an unknown miss',
);
assert.ok(source.includes('return .inactive'), 'known non-mpv focus should return inactive');
assert.ok(
source.includes('print("inactive")'),
'inactive state should be printed for the tracker',
);
});
@@ -1598,6 +1598,55 @@ test('macOS hides visible overlay during tracker loss after mpv loses foreground
assert.ok(!calls.includes('loading-osd'));
});
test('macOS keeps a focused overlay visible during tracker loss', () => {
const { window, calls, setFocused } = createMainWindowRecorder();
const tracker: WindowTrackerStub = {
isTracking: () => false,
getGeometry: () => null,
isTargetWindowFocused: () => false,
isTargetWindowMinimized: () => false,
};
window.show();
setFocused(true);
calls.length = 0;
updateVisibleOverlayVisibility({
visibleOverlayVisible: true,
mainWindow: window as never,
windowTracker: tracker as never,
trackerNotReadyWarningShown: false,
setTrackerNotReadyWarningShown: () => {},
updateVisibleOverlayBounds: () => {
calls.push('update-bounds');
},
ensureOverlayWindowLevel: () => {
calls.push('ensure-level');
},
syncPrimaryOverlayWindowLayer: () => {
calls.push('sync-layer');
},
enforceOverlayLayerOrder: () => {
calls.push('enforce-order');
},
syncOverlayShortcuts: () => {
calls.push('sync-shortcuts');
},
isMacOSPlatform: true,
showOverlayLoadingOsd: () => {
calls.push('loading-osd');
},
} as never);
assert.ok(calls.includes('sync-layer'));
assert.ok(calls.includes('mouse-ignore:true:forward'));
assert.ok(calls.includes('ensure-level'));
assert.ok(calls.includes('enforce-order'));
assert.ok(calls.includes('sync-shortcuts'));
assert.ok(!calls.includes('hide'));
assert.ok(!calls.includes('loading-osd'));
});
test('macOS suppresses immediate repeat loading OSD after tracker recovery until cooldown expires', () => {
const { window } = createMainWindowRecorder();
const osdMessages: string[] = [];
+233 -4
View File
@@ -1,6 +1,13 @@
import assert from 'node:assert/strict';
import { mkdtempSync, rmSync, utimesSync, writeFileSync } from 'node:fs';
import { tmpdir } from 'node:os';
import { join } from 'node:path';
import test from 'node:test';
import { MacOSWindowTracker, parseMacOSHelperOutput } from './macos-tracker';
import {
isCompiledMacOSHelperCurrent,
MacOSWindowTracker,
parseMacOSHelperOutput,
} from './macos-tracker';
test('parseMacOSHelperOutput parses minimized state', () => {
assert.deepEqual(parseMacOSHelperOutput('minimized'), {
@@ -18,6 +25,91 @@ test('parseMacOSHelperOutput parses active focused state without geometry', () =
});
});
test('parseMacOSHelperOutput parses inactive state without geometry', () => {
assert.deepEqual(parseMacOSHelperOutput('inactive'), {
geometry: null,
focused: false,
inactive: true,
});
});
test('isCompiledMacOSHelperCurrent rejects binaries older than the Swift source', () => {
const tempDir = mkdtempSync(join(tmpdir(), 'subminer-macos-helper-'));
try {
const binaryPath = join(tempDir, 'get-mpv-window-macos');
const sourcePath = join(tempDir, 'get-mpv-window-macos.swift');
writeFileSync(binaryPath, 'binary');
writeFileSync(sourcePath, 'source');
const older = new Date('2026-01-01T00:00:00Z');
const newer = new Date('2026-01-01T00:00:05Z');
utimesSync(binaryPath, older, older);
utimesSync(sourcePath, newer, newer);
assert.equal(isCompiledMacOSHelperCurrent(binaryPath, sourcePath), false);
utimesSync(binaryPath, newer, newer);
utimesSync(sourcePath, older, older);
assert.equal(isCompiledMacOSHelperCurrent(binaryPath, sourcePath), true);
} finally {
rmSync(tempDir, { recursive: true, force: true });
}
});
test('MacOSWindowTracker slows polling while focused target is stable', async () => {
const scheduledDelays: number[] = [];
let callIndex = 0;
const tracker = new MacOSWindowTracker('/tmp/mpv.sock', {
resolveHelper: () => ({
helperPath: 'helper',
helperType: 'binary',
}),
runHelper: async () => {
callIndex += 1;
return { stdout: '10,20,1280,720,1', stderr: '' };
},
fastPollIntervalMs: 250,
stablePollIntervalMs: 1_000,
setPollTimeout: ((_callback: () => void, delayMs: number) => {
scheduledDelays.push(delayMs);
return {} as ReturnType<typeof setTimeout>;
}) as never,
clearPollTimeout: (() => {}) as never,
} as never);
tracker.start();
await new Promise((resolve) => setTimeout(resolve, 0));
tracker.stop();
assert.equal(callIndex, 1);
assert.deepEqual(scheduledDelays, [1_000]);
});
test('MacOSWindowTracker keeps fast polling while target is not focused', async () => {
const scheduledDelays: number[] = [];
const tracker = new MacOSWindowTracker('/tmp/mpv.sock', {
resolveHelper: () => ({
helperPath: 'helper',
helperType: 'binary',
}),
runHelper: async () => ({ stdout: '10,20,1280,720,0', stderr: '' }),
fastPollIntervalMs: 250,
stablePollIntervalMs: 1_000,
setPollTimeout: ((_callback: () => void, delayMs: number) => {
scheduledDelays.push(delayMs);
return {} as ReturnType<typeof setTimeout>;
}) as never,
clearPollTimeout: (() => {}) as never,
} as never);
tracker.start();
await new Promise((resolve) => setTimeout(resolve, 0));
tracker.stop();
assert.deepEqual(scheduledDelays, [250]);
});
test('MacOSWindowTracker keeps the last geometry through a single helper miss', async () => {
let callIndex = 0;
const outputs = [
@@ -63,7 +155,7 @@ test('MacOSWindowTracker keeps the last geometry through a single helper miss',
});
});
test('MacOSWindowTracker preserves target focus during transient helper misses', async () => {
test('MacOSWindowTracker preserves target focus on helper not-found while retaining geometry', async () => {
let callIndex = 0;
const focusChanges: boolean[] = [];
const outputs = [
@@ -144,10 +236,145 @@ test('MacOSWindowTracker keeps focused fullscreen target through active helper m
});
});
test('MacOSWindowTracker keeps previously focused target through repeated not-found misses after grace', async () => {
let callIndex = 0;
let now = 1_000;
const focusChanges: boolean[] = [];
const outputs = [
{ stdout: '10,20,1280,720,1', stderr: '' },
{ stdout: 'not-found', stderr: '' },
{ stdout: 'not-found', stderr: '' },
{ stdout: 'not-found', stderr: '' },
];
const tracker = new MacOSWindowTracker('/tmp/mpv.sock', {
resolveHelper: () => ({
helperPath: 'helper.swift',
helperType: 'swift',
}),
runHelper: async () => outputs[callIndex++] ?? outputs.at(-1)!,
now: () => now,
trackingLossGraceMs: 500,
});
tracker.onWindowFocusChange = (focused) => {
focusChanges.push(focused);
};
(tracker as unknown as { pollGeometry: () => void }).pollGeometry();
await new Promise((resolve) => setTimeout(resolve, 0));
assert.equal(tracker.isTracking(), true);
assert.equal(tracker.isTargetWindowFocused(), true);
now += 1_000;
(tracker as unknown as { pollGeometry: () => void }).pollGeometry();
await new Promise((resolve) => setTimeout(resolve, 0));
now += 1_000;
(tracker as unknown as { pollGeometry: () => void }).pollGeometry();
await new Promise((resolve) => setTimeout(resolve, 0));
now += 1_000;
(tracker as unknown as { pollGeometry: () => void }).pollGeometry();
await new Promise((resolve) => setTimeout(resolve, 0));
assert.equal(tracker.isTracking(), true);
assert.equal(tracker.isTargetWindowFocused(), true);
assert.deepEqual(tracker.getGeometry(), {
x: 10,
y: 20,
width: 1280,
height: 720,
});
assert.deepEqual(focusChanges, [true]);
});
test('MacOSWindowTracker keeps previously focused target through repeated helper execution failures', async () => {
let callIndex = 0;
let now = 1_000;
const focusChanges: boolean[] = [];
const tracker = new MacOSWindowTracker('/tmp/mpv.sock', {
resolveHelper: () => ({
helperPath: 'helper.swift',
helperType: 'swift',
}),
runHelper: async () => {
callIndex += 1;
if (callIndex === 1) {
return { stdout: '10,20,1280,720,1', stderr: '' };
}
throw Object.assign(new Error('helper timed out'), { stderr: 'timeout' });
},
now: () => now,
trackingLossGraceMs: 500,
});
tracker.onWindowFocusChange = (focused) => {
focusChanges.push(focused);
};
(tracker as unknown as { pollGeometry: () => void }).pollGeometry();
await new Promise((resolve) => setTimeout(resolve, 0));
now += 1_000;
(tracker as unknown as { pollGeometry: () => void }).pollGeometry();
await new Promise((resolve) => setTimeout(resolve, 0));
now += 1_000;
(tracker as unknown as { pollGeometry: () => void }).pollGeometry();
await new Promise((resolve) => setTimeout(resolve, 0));
assert.equal(tracker.isTracking(), true);
assert.equal(tracker.isTargetWindowFocused(), true);
assert.deepEqual(tracker.getGeometry(), {
x: 10,
y: 20,
width: 1280,
height: 720,
});
assert.deepEqual(focusChanges, [true]);
});
test('MacOSWindowTracker marks target unfocused on explicit inactive helper signal', async () => {
let callIndex = 0;
const focusChanges: boolean[] = [];
const outputs = [
{ stdout: '10,20,1280,720,1', stderr: '' },
{ stdout: 'inactive', stderr: '' },
];
const tracker = new MacOSWindowTracker('/tmp/mpv.sock', {
resolveHelper: () => ({
helperPath: 'helper.swift',
helperType: 'swift',
}),
runHelper: async () => outputs[callIndex++] ?? outputs.at(-1)!,
trackingLossGraceMs: 1_500,
});
tracker.onWindowFocusChange = (focused) => {
focusChanges.push(focused);
};
(tracker as unknown as { pollGeometry: () => void }).pollGeometry();
await new Promise((resolve) => setTimeout(resolve, 0));
(tracker as unknown as { pollGeometry: () => void }).pollGeometry();
await new Promise((resolve) => setTimeout(resolve, 0));
assert.equal(tracker.isTracking(), true);
assert.deepEqual(tracker.getGeometry(), {
x: 10,
y: 20,
width: 1280,
height: 720,
});
assert.equal(tracker.isTargetWindowFocused(), false);
assert.deepEqual(focusChanges, [true, false]);
});
test('MacOSWindowTracker drops tracking after consecutive helper misses', async () => {
let callIndex = 0;
const outputs = [
{ stdout: '10,20,1280,720,1', stderr: '' },
{ stdout: '10,20,1280,720,0', stderr: '' },
{ stdout: 'not-found', stderr: '' },
{ stdout: 'not-found', stderr: '' },
];
@@ -164,6 +391,7 @@ test('MacOSWindowTracker drops tracking after consecutive helper misses', async
(tracker as unknown as { pollGeometry: () => void }).pollGeometry();
await new Promise((resolve) => setTimeout(resolve, 0));
assert.equal(tracker.isTracking(), true);
assert.equal(tracker.isTargetWindowFocused(), false);
(tracker as unknown as { pollGeometry: () => void }).pollGeometry();
await new Promise((resolve) => setTimeout(resolve, 0));
@@ -227,7 +455,7 @@ test('MacOSWindowTracker drops tracking after grace window expires', async () =>
let callIndex = 0;
let now = 1_000;
const outputs = [
{ stdout: '10,20,1280,720,1', stderr: '' },
{ stdout: '10,20,1280,720,0', stderr: '' },
{ stdout: 'not-found', stderr: '' },
{ stdout: 'not-found', stderr: '' },
{ stdout: 'not-found', stderr: '' },
@@ -246,6 +474,7 @@ test('MacOSWindowTracker drops tracking after grace window expires', async () =>
(tracker as unknown as { pollGeometry: () => void }).pollGeometry();
await new Promise((resolve) => setTimeout(resolve, 0));
assert.equal(tracker.isTracking(), true);
assert.equal(tracker.isTargetWindowFocused(), false);
now += 250;
(tracker as unknown as { pollGeometry: () => void }).pollGeometry();
+138 -19
View File
@@ -25,6 +25,8 @@ import { createLogger } from '../logger';
import type { WindowGeometry } from '../types';
const log = createLogger('tracker').child('macos');
const MACOS_FAST_POLL_INTERVAL_MS = 250;
const MACOS_STABLE_FOCUSED_POLL_INTERVAL_MS = 1_000;
type MacOSTrackerRunnerResult = {
stdout: string;
@@ -42,6 +44,10 @@ type MacOSTrackerDeps = {
trackingLossGraceMs?: number;
minimizedTrackingLossGraceMs?: number;
now?: () => number;
fastPollIntervalMs?: number;
stablePollIntervalMs?: number;
setPollTimeout?: typeof setTimeout;
clearPollTimeout?: typeof clearTimeout;
};
export type MacOSHelperWindowState =
@@ -50,18 +56,28 @@ export type MacOSHelperWindowState =
focused: boolean;
minimized?: false;
active?: false;
inactive?: false;
}
| {
geometry: null;
focused: true;
active: true;
minimized?: false;
inactive?: false;
}
| {
geometry: null;
focused: false;
inactive: true;
active?: false;
minimized?: false;
}
| {
geometry: null;
focused: false;
minimized: true;
active?: false;
inactive?: false;
};
function runHelperWithExecFile(
@@ -98,6 +114,25 @@ function runHelperWithExecFile(
});
}
export function isCompiledMacOSHelperCurrent(
binaryPath: string,
sourcePath: string,
helperFs: Pick<typeof fs, 'existsSync' | 'statSync'> = fs,
): boolean {
if (!helperFs.existsSync(binaryPath)) {
return false;
}
if (!helperFs.existsSync(sourcePath)) {
return true;
}
try {
return helperFs.statSync(binaryPath).mtimeMs >= helperFs.statSync(sourcePath).mtimeMs;
} catch {
return false;
}
}
export function parseMacOSHelperOutput(result: string): MacOSHelperWindowState | null {
const trimmed = result.trim();
if (trimmed === 'minimized') {
@@ -114,6 +149,13 @@ export function parseMacOSHelperOutput(result: string): MacOSHelperWindowState |
active: true,
};
}
if (trimmed === 'inactive') {
return {
geometry: null,
focused: false,
inactive: true,
};
}
if (!trimmed || trimmed === 'not-found') {
return null;
}
@@ -153,8 +195,9 @@ export function parseMacOSHelperOutput(result: string): MacOSHelperWindowState |
}
export class MacOSWindowTracker extends BaseWindowTracker {
private pollInterval: ReturnType<typeof setInterval> | null = null;
private pollTimeout: ReturnType<typeof setTimeout> | null = null;
private pollInFlight = false;
private started = false;
private helperPath: string | null = null;
private helperType: 'binary' | 'swift' | null = null;
private lastExecErrorFingerprint: string | null = null;
@@ -169,6 +212,10 @@ export class MacOSWindowTracker extends BaseWindowTracker {
private readonly trackingLossGraceMs: number;
private readonly minimizedTrackingLossGraceMs: number;
private readonly now: () => number;
private readonly fastPollIntervalMs: number;
private readonly stablePollIntervalMs: number;
private readonly setPollTimeout: typeof setTimeout;
private readonly clearPollTimeout: typeof clearTimeout;
private consecutiveMisses = 0;
private trackingLossStartedAtMs: number | null = null;
private targetWindowMinimized = false;
@@ -184,6 +231,16 @@ export class MacOSWindowTracker extends BaseWindowTracker {
Math.floor(deps.minimizedTrackingLossGraceMs ?? 500),
);
this.now = deps.now ?? (() => Date.now());
this.fastPollIntervalMs = Math.max(
50,
Math.floor(deps.fastPollIntervalMs ?? MACOS_FAST_POLL_INTERVAL_MS),
);
this.stablePollIntervalMs = Math.max(
this.fastPollIntervalMs,
Math.floor(deps.stablePollIntervalMs ?? MACOS_STABLE_FOCUSED_POLL_INTERVAL_MS),
);
this.setPollTimeout = deps.setPollTimeout ?? setTimeout;
this.clearPollTimeout = deps.clearPollTimeout ?? clearTimeout;
const resolvedHelper = deps.resolveHelper?.() ?? null;
if (resolvedHelper) {
this.helperPath = resolvedHelper.helperPath;
@@ -231,15 +288,15 @@ export class MacOSWindowTracker extends BaseWindowTracker {
return true;
}
private detectHelper(): void {
const shouldFilterBySocket = this.targetMpvSocketPath !== null;
// Fall back to Swift helper first when filtering by socket path to avoid
// stale prebuilt binaries that don't support the new socket filter argument.
const swiftPath = path.join(__dirname, '..', '..', 'scripts', 'get-mpv-window-macos.swift');
if (shouldFilterBySocket && this.tryUseHelper(swiftPath, 'swift')) {
return;
private tryUseCompiledHelper(candidatePath: string, sourcePath: string): boolean {
if (!isCompiledMacOSHelperCurrent(candidatePath, sourcePath)) {
return false;
}
return this.tryUseHelper(candidatePath, 'binary');
}
private detectHelper(): void {
const swiftPath = path.join(__dirname, '..', '..', 'scripts', 'get-mpv-window-macos.swift');
// Prefer resources path (outside asar) in packaged apps.
const resourcesPath = process.resourcesPath;
@@ -250,9 +307,21 @@ export class MacOSWindowTracker extends BaseWindowTracker {
}
}
// Dist binary path (development / unpacked installs).
const distBinaryPath = path.join(__dirname, '..', '..', 'scripts', 'get-mpv-window-macos');
if (this.tryUseHelper(distBinaryPath, 'binary')) {
// Built source runs from dist/window-trackers, so the compiled helper is a sibling of dist.
const bundledBinaryPath = path.join(__dirname, '..', 'scripts', 'get-mpv-window-macos');
if (this.tryUseCompiledHelper(bundledBinaryPath, swiftPath)) {
return;
}
// Source-tree/manual helper build path.
const sourceTreeBinaryPath = path.join(
__dirname,
'..',
'..',
'scripts',
'get-mpv-window-macos',
);
if (this.tryUseCompiledHelper(sourceTreeBinaryPath, swiftPath)) {
return;
}
@@ -284,15 +353,16 @@ export class MacOSWindowTracker extends BaseWindowTracker {
}
start(): void {
this.pollInterval = setInterval(() => this.pollGeometry(), 250);
if (this.started) {
return;
}
this.started = true;
this.pollGeometry();
}
stop(): void {
if (this.pollInterval) {
clearInterval(this.pollInterval);
this.pollInterval = null;
}
this.started = false;
this.clearScheduledPoll();
}
override isTargetWindowMinimized(): boolean {
@@ -318,7 +388,16 @@ export class MacOSWindowTracker extends BaseWindowTracker {
return this.now() - this.trackingLossStartedAtMs > graceMs;
}
private shouldPreserveFocusedTargetOnMiss(): boolean {
return this.isTracking() && this.isTargetWindowFocused() && this.getGeometry() !== null;
}
private registerTrackingMiss(graceMs = this.trackingLossGraceMs): void {
if (this.shouldPreserveFocusedTargetOnMiss()) {
this.resetTrackingLossState();
return;
}
this.consecutiveMisses += 1;
if (this.shouldDropTracking(graceMs)) {
this.updateGeometry(null);
@@ -326,6 +405,39 @@ export class MacOSWindowTracker extends BaseWindowTracker {
}
}
private resolveNextPollIntervalMs(): number {
if (
this.isTracking() &&
this.isTargetWindowFocused() &&
!this.targetWindowMinimized &&
this.getGeometry() !== null
) {
return this.stablePollIntervalMs;
}
return this.fastPollIntervalMs;
}
private clearScheduledPoll(): void {
if (!this.pollTimeout) {
return;
}
this.clearPollTimeout(this.pollTimeout);
this.pollTimeout = null;
}
private scheduleNextPoll(): void {
if (!this.started || this.pollTimeout) {
return;
}
this.pollTimeout = this.setPollTimeout(() => {
this.pollTimeout = null;
this.pollGeometry();
}, this.resolveNextPollIntervalMs());
}
private pollGeometry(): void {
if (this.pollInFlight || !this.helperPath || !this.helperType) {
return;
@@ -348,10 +460,16 @@ export class MacOSWindowTracker extends BaseWindowTracker {
this.updateTargetWindowFocused(true);
return;
}
if (parsed.inactive) {
this.targetWindowMinimized = false;
this.updateTargetWindowFocused(false);
this.registerTrackingMiss();
return;
}
this.resetTrackingLossState();
this.targetWindowMinimized = false;
this.updateFocus(parsed.focused);
this.updateGeometry(parsed.geometry);
this.updateGeometry(parsed.geometry, parsed.focused);
this.updateTargetWindowFocused(parsed.focused);
return;
}
@@ -373,6 +491,7 @@ export class MacOSWindowTracker extends BaseWindowTracker {
})
.finally(() => {
this.pollInFlight = false;
this.scheduleNextPoll();
});
}
}