fix(macos): drop target after grace period on repeated tracking misses

- registerTrackingMiss was resetting grace state on every miss, so focus was never released; now starts timer on first miss and drops after grace elapses
- update two tests to assert focus is dropped (not preserved) once grace expires
- add IPC test for setIgnoreMouseEvents → onOverlayMouseInteractionChanged mapping
This commit is contained in:
2026-05-16 19:04:16 -07:00
parent fe201a2d2f
commit f044877c83
3 changed files with 44 additions and 22 deletions
+22
View File
@@ -311,6 +311,28 @@ test('createIpcDepsRuntime wires AniList handlers', async () => {
assert.equal(deps.getPlaybackPaused(), true);
});
test('registerIpcHandlers maps setIgnoreMouseEvents to overlay interaction active state', () => {
const { registrar, handlers } = createFakeIpcRegistrar();
const calls: string[] = [];
registerIpcHandlers(
createRegisterIpcDeps({
onOverlayMouseInteractionChanged: (active) => {
calls.push(`overlay-interaction:${active}`);
},
}),
registrar,
);
const handler = handlers.on.get(IPC_CHANNELS.command.setIgnoreMouseEvents);
assert.equal(typeof handler, 'function');
handler?.({}, true, { forward: true });
handler?.({}, false, {});
assert.deepEqual(calls, ['overlay-interaction:false', 'overlay-interaction:true']);
});
test('registerIpcHandlers runs AniList update after manual mark watched succeeds', async () => {
const { registrar, handlers } = createFakeIpcRegistrar();
const calls: string[] = [];
+15 -20
View File
@@ -236,7 +236,7 @@ test('MacOSWindowTracker keeps focused fullscreen target through active helper m
});
});
test('MacOSWindowTracker keeps previously focused target through repeated not-found misses after grace', async () => {
test('MacOSWindowTracker drops previously focused target after repeated not-found misses exceed grace', async () => {
let callIndex = 0;
let now = 1_000;
const focusChanges: boolean[] = [];
@@ -244,7 +244,6 @@ test('MacOSWindowTracker keeps previously focused target through repeated not-fo
{ 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', {
@@ -269,14 +268,6 @@ test('MacOSWindowTracker keeps previously focused target through repeated not-fo
(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(), {
@@ -286,9 +277,18 @@ test('MacOSWindowTracker keeps previously focused target through repeated not-fo
height: 720,
});
assert.deepEqual(focusChanges, [true]);
now += 1_000;
(tracker as unknown as { pollGeometry: () => void }).pollGeometry();
await new Promise((resolve) => setTimeout(resolve, 0));
assert.equal(tracker.isTracking(), false);
assert.equal(tracker.isTargetWindowFocused(), false);
assert.equal(tracker.getGeometry(), null);
assert.deepEqual(focusChanges, [true, false]);
});
test('MacOSWindowTracker keeps previously focused target through repeated helper execution failures', async () => {
test('MacOSWindowTracker drops previously focused target after repeated helper execution failures exceed grace', async () => {
let callIndex = 0;
let now = 1_000;
const focusChanges: boolean[] = [];
@@ -323,15 +323,10 @@ test('MacOSWindowTracker keeps previously focused target through repeated helper
(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]);
assert.equal(tracker.isTracking(), false);
assert.equal(tracker.isTargetWindowFocused(), false);
assert.equal(tracker.getGeometry(), null);
assert.deepEqual(focusChanges, [true, false]);
});
test('MacOSWindowTracker marks target unfocused on explicit inactive helper signal', async () => {
+7 -2
View File
@@ -394,8 +394,13 @@ export class MacOSWindowTracker extends BaseWindowTracker {
private registerTrackingMiss(graceMs = this.trackingLossGraceMs): void {
if (this.shouldPreserveFocusedTargetOnMiss()) {
this.resetTrackingLossState();
return;
if (this.trackingLossStartedAtMs === null) {
this.trackingLossStartedAtMs = this.now();
return;
}
if (this.now() - this.trackingLossStartedAtMs <= graceMs) {
return;
}
}
this.consecutiveMisses += 1;