Skip to main content
Glama
window-screenshot-smart-select.md4.69 kB
--- summary: 'Heuristics for filtering CG windows before screenshotting' read_when: - 'touching ImageCommand/SeeCommand window selection logic' - 'plumbing CGWindow metadata into ServiceWindowInfo' - 'debugging why peekaboo image skips or captures overlays' --- # Window Screenshot "Smart Select" Guide Peekaboo’s screenshot tooling (`peekaboo image`, `see`, agent capture flows) must avoid the long tail of junk windows returned by CoreGraphics. This document explains how we map `CGWindow` metadata into `ServiceWindowInfo` and the heuristics every caller should apply before attempting a capture. ## 1. Metadata We Need | Source | Key | Purpose | | --- | --- | --- | | `CGWindowListCopyWindowInfo` | `kCGWindowNumber` | Stable `CGWindowID` for cross-referencing and duplicate suppression. | | `kCGWindowLayer` | Layer filtering (layer 0 = normal app windows). | | `kCGWindowAlpha` | Skip fully transparent/hidden overlays. | | `kCGWindowBounds` | Size thresholds + dedupe by area. | | `kCGWindowIsOnscreen` | Detect off-screen windows when `.optionOnScreenOnly` isn’t in use. | | `kCGWindowOwnerPID` / `Name` | Tie back to AX/Process info; drop background helpers. | | `kCGWindowSharingState` | Respect `NSWindow.sharingType == .none` (system replaces pixels with a “bubble”). | | `SCWindow` (`ScreenCaptureKit`) | `frame`, `isOnScreen`, `layer`, `sharingType`, `alpha`. | | `NSWindow` (our own process) | `isExcludedFromWindowsMenu` so we never export intentionally hidden internal windows. | `ServiceWindowInfo` should store these fields (or derived booleans like `isShareable`) so every CLI/agent feature can make the same decision. ## 2. Filtering Heuristics Apply these checks in order; the first failure removes the candidate window: 1. **Layer:** require `layer == 0` (normal app chrome). Panels, menu bar extras, HUD bubbles use other layers and should be ignored unless specifically requested. 2. **Transparency:** skip if `alpha <= 0.01` — CG tells us the app doesn’t intend this surface to be visible. 3. **Sharing state:** `kCGWindowSharingState == kCGWindowSharingNone` (or `SCWindow.sharingType == .none`) means “don’t capture.” Bail early and surface a helpful error. 4. **Visibility:** require either `.optionOnScreenOnly` or `kCGWindowIsOnscreen == true`. Off-screen or minimized windows produce stale frames. 5. **Dimensions:** default threshold `width >= 120` and `height >= 90`. This filters tooltips, 1 px borders, rainbow bubbles, etc. Adjust per product needs but keep a floor. 6. **Title fallback:** prefer non-empty titles. If an app has multiple windows, accept one empty-titled window only when it is the sole candidate after the prior filters. 7. **Owner policy:** for `NSWindow`s we own, also skip `isExcludedFromWindowsMenu == true` unless a developer explicitly opts into exporting that surface. Wrap this logic in a helper (e.g. `WindowFiltering.isRenderable(_ info: ServiceWindowInfo)`) so every command reuses the same rules. ## 3. Duplicate Handling `CGWindowListCopyWindowInfo` frequently reports multiple entries per “real” window (tab bars, separators, compositing layers). To avoid double-counting: 1. Group entries by `kCGWindowNumber`. 2. Within each group, prefer the entry that is on-screen and has the largest bounding box. 3. Apply the heuristics above to the winner only. This matches Chromium/WebRTC’s strategy (`only_zero_layer` filter) and keeps the noise floor low. ## 4. Capture Pipeline Integration Every capture path should call the filter before touching ScreenCaptureKit/CGWindowList: - `ImageCommand` / `SeeCommand`: when resolving a target window, skip disqualified entries and throw `PeekabooError.windowNotFound` if none remain. For `--mode multi`, silently drop bad windows instead of aborting the batch. - `ScreenCaptureService`: if the selected `ServiceWindowInfo` is not shareable, exit before invoking SK/CG. This prevents rainbow bubbles and makes failures explicit. - `WindowCommand list`: hide disqualified windows (or mark them as “hidden by app”) so agents don’t pick surfaces they can’t capture. ## 5. Testing Strategy 1. **Unit tests** for the filter helper, covering layer, alpha, sharing state, size, and visibility. 2. **Service tests** that feed canned CG dictionaries into `ApplicationService` / `ApplicationServiceWindowsWorkaround` to confirm metadata is preserved. 3. **CLI tests** (`InProcessCommandRunner`) ensuring `peekaboo image` errors when only hidden windows exist, and succeeds when a shareable window is available. Keep fixtures small (two windows per app) so we can reason about why each candidate passes or fails the heuristic chain.

Latest Blog Posts

MCP directory API

We provide all the information about MCP servers via our MCP API.

curl -X GET 'https://glama.ai/api/mcp/v1/servers/steipete/Peekaboo'

If you have feedback or need assistance with the MCP directory API, please join our Discord server