Skip to content

windo: cut idle and hidden CPU/energy use - #4

Merged
zackbart merged 2 commits into
mainfrom
agent/windo-perf
Aug 19, 2026
Merged

windo: cut idle and hidden CPU/energy use#4
zackbart merged 2 commits into
mainfrom
agent/windo-perf

Conversation

@zackbart

Copy link
Copy Markdown
Owner

Problem

Windo showed "significant energy" in the battery menu, including while hidden. Hiding did not stop the work: video kept decoding, and the tint sampler (takeSnapshot 3×/s) and a 22 Hz titlebar redraw ran forever while the window was on screen, even when fully covered.

Changes

  • Tint sampler runs only while on-screen, not occluded, not compact, not in page fullscreen. 1 s interval, one snapshot in flight, stale-tab results dropped. Lerp timer runs only until the color converges.
  • Hide (⌃⌘H / menu), red close button, and minimize pause playing media in all tabs; show resumes the active tab. Tab switch pauses the outgoing tab. New menu toggle Pause When Hidden (default on). Media the user paused stays paused.
  • Tab bar rebuild coalesced (150 ms) and skipped when unchanged.
  • Fullscreen state tracked per web view; cleared on navigation.
  • Opacity save debounced in .common run-loop mode; saved on quit.
  • Tab close removes script handlers, nils the delegate, stops loading.
  • README: hotkey corrected to ⌃⌘H; Pause When Hidden documented.

Measured (A/B vs main, Big Buck Bunny 4K60, % of one core, Windo + WebKit helpers)

State main this PR
Static page 7.5 1.0
Visible, playing 34.4 23.5
Hidden ⌃⌘H 8.1 1.5
Minimized 1.3
Second tab active 3.2

Zero crashes, no new log errors, no timer leak after 30 rapid toggles, tint still tracks the video.

Behavior change

The red close button now hides the window (same as ⌃⌘H) instead of closing it. Matches the hide/show model and applicationShouldTerminateAfterLastWindowClosed == false.

Not tested

True occlusion (window covered) — Windo floats over everything, so it could not be produced. Same code path as minimize, which passed. powermetrics needs sudo; skipped.

🤖 Generated with Claude Code

zackbart and others added 2 commits August 18, 2026 19:53
- Tint sampler runs only while on-screen, not occluded, not compact, not in
  page fullscreen. 1 s interval, one snapshot in flight, stale-tab results
  dropped. Lerp timer runs only until the color converges.
- Hide, close button, and minimize pause playing media in all tabs; show
  resumes the active tab. Tab switch pauses the outgoing tab. New menu
  toggle "Pause When Hidden" (default on).
- Tab bar rebuild coalesced and skipped when unchanged.
- Fullscreen state tracked per web view, cleared on navigation.
- Opacity save debounced in .common mode; saved on quit.
- Tab close removes handlers, delegate, stops loading.
- README: hotkey is ⌃⌘H; document Pause When Hidden.

Measured (A/B vs main, 4K60 video, % of one core): static 7.5→1.0,
playing 34→23.5, hidden 8.1→1.5.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Parked tabs re-pause media that starts after the scan (autoplay-next, SPA
nav) via a capture-phase play listener, keep the resume tag until play()
actually takes, drop superseded snapshots by generation, and apply the
Pause When Hidden toggle while the window is already hidden.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
@zackbart

Copy link
Copy Markdown
Owner Author

Second-round review (Codex, gpt-5.6-sol) — 6 findings, disposition:

Fixed in e33d13a:

  1. Media started after a tab is parked (autoplay-next, SPA nav) played while hidden → added kParkShim user script: capture-phase play listener re-pauses and tags while window.__windoParked; didCommit re-parks new documents in hidden/background tabs.
  2. Resume tag was cleared before play() resolved → cleared in .then.
  3. Quick hide/show could overlap two snapshots → snapshotGen token; superseded completions are dropped.
  4. Toggling Pause When Hidden while hidden did nothing → now pauses all / resumes active immediately.

Not fixed (known limitations, pre-existing for mute too):
2. Pause/resume evaluateJavaScript runs in the main frame only; cross-origin iframe players are not paused. The park shim is injected in all frames, but the parked flag is only set in the main frame.
4. Fullscreen state is per web view, not per frame; an iframe can clear another frame's state.

Runtime re-check after e33d13a: hidden 15 s → +0 s CPU time for Windo and both WebContent processes; resumes on show.

@zackbart
zackbart merged commit 769576d into main Aug 19, 2026
@zackbart
zackbart deleted the agent/windo-perf branch August 19, 2026 00:07
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant