Skip to content

lunguini/latch

Latch

A lightweight macOS menu bar app that remembers window positions and Spaces assignments per display configuration and restores them when you reconnect your screens.

The Problem

Every time you disconnect and reconnect external monitors, macOS shuffles all your windows onto whatever screen it feels like. Fullscreen windows get collapsed. Chrome tabs you had on your left monitor end up stacked on your laptop. IntelliJ disappears into a random Space. You spend 5 minutes dragging everything back.

What This Does

Latch snapshots your complete window arrangement — positions, sizes, and which Space on which display each window belongs to — and restores it automatically when the same monitors reconnect.

How It's Different From Other Window Managers

Most window managers (Rectangle, Magnet, etc.) handle position and size but ignore Spaces entirely. Latch uses private CGS/SkyLight APIs (the same ones powering yabai, Amethyst, and alt-tab-macos) to directly move windows between Spaces — including restoring native fullscreen windows to their correct display.

No SIP changes required. These are undocumented but functional APIs that don't need System Integrity Protection disabled.

Architecture

AppDelegate        →  Menu bar UI + display change listener
DisplayManager     →  Fingerprints connected displays (vendor + model + serial + arrangement)
SpaceManager       →  Wraps private CGS/SkyLight APIs for Space enumeration and window-space ops
WindowManager      →  Captures/restores window positions + space assignments via AX + CGS APIs
LayoutStore        →  Persists layouts as JSON in ~/Library/Application Support/Latch/
PrivateAPIs.h      →  C declarations for undocumented CGS functions

Private APIs Used

Function Purpose
CGSMainConnectionID Get WindowServer connection
CGSCopyManagedDisplaySpaces Enumerate all Spaces per display
CGSManagedDisplayGetCurrentSpace Get active Space for a display
CGSCopySpacesForWindows Which Spaces a window is on
CGSAddWindowsToSpaces Add a window to a Space
CGSRemoveWindowsFromSpaces Remove a window from a Space
_AXUIElementGetWindow Bridge AXUIElement → CGWindowID
CGSCopyBestManagedDisplayForRect Map screen region → display UUID

Space Restoration Logic

  1. Each window's Space is stored as an index per display (e.g., "2nd Space on display ABC"), not a raw Space ID (those change across reboots).
  2. Display matching uses display UUIDs (stable across reconnects for the same physical display).
  3. When restoring, windows are moved using an add-then-remove pattern (add to target Space first, then remove from old Space) to avoid the zero-Spaces state that macOS blocks.
  4. Fullscreen windows are handled by exiting fullscreen → moving to target screen → re-entering fullscreen.

Requirements

  • macOS 13+ (Ventura or later)
  • Xcode Command Line Tools (xcode-select --install)
  • Accessibility permission (prompted on first launch)

Build & Install

chmod +x build.sh
./build.sh
cp -r build/Latch.app /Applications/
open /Applications/Latch.app

The build script auto-detects your architecture (Apple Silicon or Intel).

Usage

Menu Bar Controls

Action What it does
Save Current Layout (⌘S) Snapshots all window positions + Space assignments
Restore Layout (⌘R) Restores everything for the current display config
Auto-Restore Toggle: auto-restore when displays reconnect
Restore Delay Wait time before restore (1–5s, default 2s)

Workflow

  1. Connect your monitors and arrange everything: Chrome fullscreen on left, IntelliJ fullscreen on right, Slack on laptop, etc.
  2. Click the menu bar icon → Save Current Layout
  3. Done. Next time you reconnect the same monitors, it all snaps back — including fullscreen windows on the right Spaces.

Save different layouts for different setups:

  • Home office (laptop + 2 externals)
  • Work office (laptop + ultrawide)
  • Laptop only

Auto-Start on Login

System Settings → General → Login Items → + → select Latch

Caveats

  • Private APIs can break. Apple doesn't document or guarantee these functions. They've been stable for years across macOS 12–15 and are used by major tools (yabai has 25k stars), but a future macOS update could change them. If that happens, Latch falls back to position-only restoration (the AX API part is fully public).
  • Fullscreen restoration has a visual flicker. The window exits fullscreen, moves, and re-enters fullscreen. Takes about 1 second per window. No way around this — macOS Spaces are opaque even to private APIs when it comes to fullscreen.
  • Space count matters. If you saved a layout with 3 Spaces on your left monitor but now only have 2, windows from Space 3 get placed on Space 2 (last available). Create the same number of Spaces before restoring for best results.
  • Amethyst/yabai conflict. If you're already running a tiling window manager, Latch's space moves may fight with it. Disable one or the other for space management.

Data Storage

Human-readable JSON in ~/Library/Application Support/Latch/<config-hash>.json.

License

GPL-3.0 — see LICENSE.

About

No description, website, or topics provided.

Resources

License

Code of conduct

Contributing

Stars

Watchers

Forks

Packages

 
 
 

Contributors