diff --git a/apps/desktop/main/electron-window-backend.ts b/apps/desktop/main/electron-window-backend.ts index a81ea768..4ead9310 100644 --- a/apps/desktop/main/electron-window-backend.ts +++ b/apps/desktop/main/electron-window-backend.ts @@ -9,15 +9,13 @@ // A machine-initiated operation must not feed its own side effect back in as // fresh OS evidence. -import { app, BaseWindow, BrowserWindow, globalShortcut, screen, systemPreferences, webContents } from 'electron'; +import { app, BrowserWindow, globalShortcut, screen, systemPreferences, webContents } from 'electron'; import type { WebContents } from 'electron'; import { FailSafeDesktopProvider, - type Placement, type PlatformWindowId, type ProviderDisplayId, - type ProviderLayout, } from './desktop-provider.js'; import { FallbackDesktopProvider } from './desktop-provider-fallback.js'; import { MacDesktopProvider, macHelperPath, windowNumberFromSourceId } from './desktop-provider-mac.js'; @@ -88,27 +86,12 @@ function requestGuestDestroy(guest: TeardownGuest): void { const APP_RESIGN_SETTLE_MS = 500; -// Temporary diagnostic mode for the macOS provider's live acceptance check -// (specs/kernel-desktops.md, 7.2): logs `desktop-read` on every trigger and -// binds the write test to Control+Option+Command+1. No behaviour change. -const DESKTOP_PROBE = process.env.PEEK_DESKTOP_PROBE === '1'; - // A screen is an Electron display, named by its id; the providers name displays // by the same number, so the mapping is the identity. A provider screen that // stands for every display at once (`null`) is the one screen `shared`. function screenIdOf(display: ProviderDisplayId | null): ScreenId { return display ?? 'shared'; } -const DESKTOP_PROBE_WRITE_ACCELERATOR = 'Control+Alt+Command+1'; -// Time for the window server to take in a freshly shown test window before reading it. -const DESKTOP_PROBE_SETTLE_MS = 300; -// `probePlace` bypasses the fail-safe wrapper's request timeout, so it gets its own. -const DESKTOP_PROBE_PLACE_TIMEOUT_MS = 2000; - -interface DesktopProbeWindows { - red: BaseWindow; - placeholder: BaseWindow; -} // STACKING_TAKES_FOCUS is a claim never checked off macOS — see the design // doc's "Open design decisions" section. @@ -240,10 +223,6 @@ export class ElectronWindowBackend implements WindowBackend, BackendMigrationShi private desktopProvider: FailSafeDesktopProvider | null = null; // The tail of the serialized provider reads. private desktopSync: Promise = Promise.resolve(); - // The same macOS provider the fail-safe wrapper holds, kept for the probe's - // write test, which the provider interface does not offer. - private macDesktopProvider: MacDesktopProvider | null = null; - private desktopProbeWindows: DesktopProbeWindows | null = null; private readonly escGrabHandler = (): void => { // Reaches the same decision the before-input-event path does, so a stale @@ -357,7 +336,6 @@ export class ElectronWindowBackend implements WindowBackend, BackendMigrationShi process.platform === 'darwin' ? new MacDesktopProvider({ command: macHelperPath(app.isPackaged, process.resourcesPath) }) : null; - this.macDesktopProvider = mac; const provider = new FailSafeDesktopProvider({ primary: mac, fallback }); this.desktopProvider = provider; for (const event of ['display-added', 'display-removed', 'display-metrics-changed'] as const) { @@ -365,27 +343,12 @@ export class ElectronWindowBackend implements WindowBackend, BackendMigrationShi } void provider .start((change) => this.onDesktopTrigger(`provider-${change.kind}`)) - .then(() => { - if (!DESKTOP_PROBE) { - this.onDesktopTrigger('start'); - return; - } - const hello = mac?.hello; - if (hello && !provider.onFallback) { - const symbols = Object.entries(hello.symbols) - .map(([name, ok]) => `${name}:${ok ? 'y' : 'n'}`) - .join(','); - wmLog('desktop-probe-hello', { version: hello.version, symbols }); - } - this.installDesktopProbeShortcut(); - this.onDesktopTrigger('start'); - }); + .then(() => this.onDesktopTrigger('start')); } private stopDesktopProvider(): void { this.desktopProvider?.stop(); this.desktopProvider = null; - this.macDesktopProvider = null; } private electronDisplayIds(): string[] { @@ -423,14 +386,7 @@ export class ElectronWindowBackend implements WindowBackend, BackendMigrationShi private nativeWindowForPlatformId(pid: PlatformWindowId): NativeWindow | null { const id = this.windowIdForPlatformId(pid); - if (id !== null) return this.getNativeWindowFor(id); - const probe = this.desktopProbeWindows; - if (probe) { - for (const w of [probe.red, probe.placeholder]) { - if (this.platformWindowIdOf(w as unknown as NativeWindow) === pid) return w as unknown as NativeWindow; - } - } - return null; + return id === null ? null : this.getNativeWindowFor(id); } // Every trigger reads the provider and reports what changed to the kernel @@ -438,7 +394,6 @@ export class ElectronWindowBackend implements WindowBackend, BackendMigrationShi // in trigger order, so a slow read never reports over a newer one. private onDesktopTrigger(trigger: string): void { this.desktopSync = this.desktopSync.then(() => this.syncDesktops(trigger)); - if (DESKTOP_PROBE) void this.desktopSync.then(() => this.probeRead(trigger)); } // Every path ends with `kernelDesktopsRead`, a failed read included: a switch @@ -492,147 +447,6 @@ export class ElectronWindowBackend implements WindowBackend, BackendMigrationShi } } - // ─── Temporary probe (PEEK_DESKTOP_PROBE=1) ─────────────────────────────── - - // `id#platformId` for every registered window and the probe's test windows. - private probeWindowLabels(): Map { - const labels = new Map(); - for (const [id, { win }] of this.windows) { - const pid = this.platformWindowIdOf(win); - if (pid !== null) labels.set(pid, `${String(id)}#${pid}`); - } - const probe = this.desktopProbeWindows; - if (probe) { - const red = this.platformWindowIdOf(probe.red as unknown as NativeWindow); - const placeholder = this.platformWindowIdOf(probe.placeholder as unknown as NativeWindow); - if (red !== null) labels.set(red, `red#${red}`); - if (placeholder !== null) labels.set(placeholder, `placeholder#${placeholder}`); - } - return labels; - } - - private static describeLayout(layout: ProviderLayout, electronDisplays: readonly string[]): WmLogFields { - const known = new Set(electronDisplays); - return { - screens: layout.screens - .map((s) => `${s.display ?? 'shared'}:${s.current}:${s.display === null || known.has(s.display) ? 'mapped' : 'unmapped'}`) - .join(','), - desktops: layout.desktops.map((d) => `${d.id}@${d.display ?? 'shared'}`).join(','), - electronDisplays: electronDisplays.join(','), - }; - } - - private static describePlacements(placements: readonly Placement[], labels: Map): string { - return placements - .map((p) => `${labels.get(p.window) ?? `?#${p.window}`}:${p.display ?? '?'}/${p.onEveryDesktop ? '*' : (p.desktop ?? '?')}`) - .join(','); - } - - private async probeRead(trigger: string, extra: WmLogFields = {}): Promise { - const provider = this.desktopProvider; - if (!provider) return; - try { - const layout = await provider.layout(); - const labels = this.probeWindowLabels(); - const placements = await provider.placements([...labels.keys()]); - wmLog('desktop-read', { - trigger, - provider: provider.name, - ...ElectronWindowBackend.describeLayout(layout, this.electronDisplayIds()), - windows: ElectronWindowBackend.describePlacements(placements, labels), - ...extra, - }); - } catch (err) { - wmLog('desktop-read-failed', { trigger, error: err instanceof Error ? err.message : String(err) }); - } - } - - private installDesktopProbeShortcut(): void { - try { - const ok = globalShortcut.register(DESKTOP_PROBE_WRITE_ACCELERATOR, () => void this.probeWriteTest()); - wmLog('desktop-probe-shortcut', { accelerator: DESKTOP_PROBE_WRITE_ACCELERATOR, registered: ok }); - } catch (err) { - wmLog('desktop-probe-shortcut', { accelerator: DESKTOP_PROBE_WRITE_ACCELERATOR, registered: false, error: String(err) }); - } - } - - // The write test of spec 7.2 step 4: a small opaque red window and a - // placeholder window (not a panel, zero opacity, click-through) shown on the - // current desktop, both read; then the helper is asked to move the red - // window to another desktop of its screen and it is read again. BaseWindows, - // so they never pass through the app's window registration. A repeat press - // only reads again. - private async probeWriteTest(): Promise { - if (this.desktopProbeWindows) { - await this.probeRead('probe-hotkey'); - return; - } - const area = screen.getPrimaryDisplay().workArea; - const red = new BaseWindow({ - x: area.x + 40, - y: area.y + 40, - width: 120, - height: 120, - frame: false, - resizable: false, - focusable: false, - hasShadow: false, - backgroundColor: '#ff0000', - show: false, - }); - const placeholder = new BaseWindow({ - x: area.x + Math.floor(area.width / 2) - 240, - y: area.y + Math.floor(area.height / 2) - 180, - width: 480, - height: 360, - frame: false, - focusable: false, - hasShadow: false, - show: false, - }); - placeholder.setOpacity(0); - placeholder.setIgnoreMouseEvents(true); - this.desktopProbeWindows = { red, placeholder }; - red.showInactive(); - placeholder.showInactive(); - await new Promise((resolve) => setTimeout(resolve, DESKTOP_PROBE_SETTLE_MS)); - await this.probeRead('probe-write-before'); - - const provider = this.desktopProvider; - const mac = this.macDesktopProvider; - const redPid = this.platformWindowIdOf(red as unknown as NativeWindow); - if (!provider || !mac || provider.onFallback || redPid === null) { - wmLog('desktop-probe-write', { attempted: false, reason: provider?.onFallback ? 'fallback' : 'no-mac-provider-or-window-number' }); - return; - } - const [placement] = await provider.placements([redPid]); - const layout = await provider.layout(); - const onScreen = layout.screens.find((s) => s.display === (placement?.display ?? null)) ?? layout.screens[0]; - const target = layout.desktops.find((d) => d.display === (onScreen?.display ?? null) && d.id !== onScreen?.current); - if (!target) { - wmLog('desktop-probe-write', { attempted: false, reason: 'no-other-desktop', from: placement?.desktop ?? null }); - return; - } - try { - const result = await Promise.race([ - mac.probePlace(redPid, target.id), - new Promise((_, reject) => - setTimeout(() => reject(new Error('place-timeout')), DESKTOP_PROBE_PLACE_TIMEOUT_MS), - ), - ]); - wmLog('desktop-probe-write', { - window: redPid, - from: placement?.desktop ?? null, - to: target.id, - attempted: result.attempted, - desktopsAfter: result.desktops.join(','), - }); - } catch (err) { - wmLog('desktop-probe-write', { window: redPid, to: target.id, error: err instanceof Error ? err.message : String(err) }); - } - await this.probeRead('probe-write-after'); - } - // ─────────────────────────────────────────────────────────────────────────── // Migration shims (see `BackendMigrationShims` for the absorption plan). // ─────────────────────────────────────────────────────────────────────────── diff --git a/docs/design/kernel-desktops-cheatsheet.md b/docs/design/kernel-desktops-cheatsheet.md index 8ddb09ad..a5769646 100644 --- a/docs/design/kernel-desktops-cheatsheet.md +++ b/docs/design/kernel-desktops-cheatsheet.md @@ -66,9 +66,10 @@ rules in `docs/design/window-rules.md`. Anchors are `file` + symbol. `CurrentDesktopChanged`, then `WindowPlacementReported` per changed window, then `DesktopsRead`. - Between the notice and the read (5–30 ms) `desktopUnread` holds, and only on-every-desktop windows count as on a current desktop. -- `space-changed screensBefore=` in the log is the desktop left; `desktop-read` (only with - `PEEK_DESKTOP_PROBE=1`) is the helper's truth. In `desktop-read`, `wm-N#:/`, - `*` = every desktop. +- `space-changed screensBefore=` in the log is the desktop left. `desktop-provider started + name=mac` with no `desktop-provider fallback` line means the helper is in use; nothing logs + its per-window placements since the `PEEK_DESKTOP_PROBE` mode was removed (reverting that + removal commit brings back its `desktop-read` line). ## Automation (no person needed) @@ -85,7 +86,7 @@ rules in `docs/design/window-rules.md`. Anchors are `file` + symbol. ## Running the app from this worktree - Build: `yarn workspace @peek/desktop build` (also builds the desktop helper). -- Start: `PEEK_DESKTOP_PROBE=1 PROFILE=kernel-desktops PEEK_PROFILE=kernel-desktops +- Start: `PROFILE=kernel-desktops PEEK_PROFILE=kernel-desktops TMPDIR=/.tmp yarn start`, stdout to `/.tmp/.log`. - Stop: kill the `Electron.app` process under this worktree's `node_modules/electron` (`pgrep -f`), then confirm no `peek-desktop-helper` is left. Never `pkill -f electron`. diff --git a/docs/design/kernel-desktops-implementation.md b/docs/design/kernel-desktops-implementation.md index 6b51709c..ec4ae92c 100644 --- a/docs/design/kernel-desktops-implementation.md +++ b/docs/design/kernel-desktops-implementation.md @@ -153,8 +153,9 @@ helper's reads; if they fail, nothing has yet been built on them. Verify: the `tracks.json` verify, `node scripts/check-platform-leak.mjs`, and the grep above returning nothing. -3. **Remove the first probe's hooks**: `installDesktopProbe` and its `probe*` members in - `electron-window-backend.ts`, own commit. +3. Done after the live checks: the `PEEK_DESKTOP_PROBE` hooks (`installDesktopProbeShortcut`, + `probeRead`, `probeWriteTest` and their test windows) are removed from + `electron-window-backend.ts`. `MacDesktopProvider.probePlace` and the helper's `place` op stay. 4. **Providers and the macOS helper, no kernel change**: `desktop-provider.ts`, `desktop-provider-fallback.ts`, `main.swift`, `build-desktop-helper.mjs`, the `extraResources` entry, `desktop-provider-mac.ts`, the fail-safe, the `check-platform-leak.mjs` additions, and @@ -267,8 +268,8 @@ The macOS provider acceptance check runs after commit 4; the rule acceptance che Run from the `kernel-desktops` worktree with `PROFILE=kernel-desktops PEEK_PROFILE=kernel-desktops TMPDIR=/.tmp` (create `.tmp` first). The agent builds, starts the app, reads `main.log`, -and asks the person only for the gestures. For the probe add `PEEK_DESKTOP_PROBE=1`, stdout to -`/.tmp/probe.log`. +and asks the person only for the gestures, stdout to `/.tmp/.log`. The +`PEEK_DESKTOP_PROBE=1` mode that logged the helper's placements is removed (step 3). ## Done when diff --git a/specs/kernel-desktops.md b/specs/kernel-desktops.md index 77cc96df..383698da 100644 --- a/specs/kernel-desktops.md +++ b/specs/kernel-desktops.md @@ -588,7 +588,8 @@ flagged). A person performs only the gestures; the app is built and started in an isolated profile, and `main.log` is read to verify each step. A diagnostic mode (`PEEK_DESKTOP_PROBE=1`, no behaviour change) logs `desktop-read` with the helper's full layout and every registered window's placement, -read from the provider directly, on every switch and screen change. +read from the provider directly, on every switch and screen change. The mode is removed once +this check has passed; re-running the check means restoring it from history. Precondition, checked in the log: `desktop-provider started name=mac` and a `hello` with every private symbol resolved. If the fallback line appears instead, the check stops and the logged