Skip to content

Latest commit

 

History

2 Commits

Folders and files

NameName
Last commit message
Last commit date
 
 
 
 
 
 
 
 
 
 

Repository files navigation

hyprecall

Snapshot a Hyprland workspace and recall it later — including the layout.

Not just "which apps were open and on which workspace", but the actual dwindle arrangement: which window is split against which, in what direction, at what ratio.

$ hyprecall save comms
saved comms  (3 windows, ws 3)

$ hyprecall show comms
| split
  - slack: Eugenia (DM) - Slack
  - split
    - chrome-x.com__-Profile_3: Home / X
    - foot: dbsps@omarchy:~

$ hyprecall restore comms 4          # onto an empty workspace
  + slack: /usr/lib/slack/slack --gtk-version=3 -s
  + chrome-x.com__-Profile_3: omarchy-launch-webapp https://x.com/
  + foot: foot --working-directory=/home/dbsps
done

Restores are pixel-exact and take about a second:

slack              (12, 38, 1681, 1300)  -> (12, 38, 1681, 1300)   OK
chrome-x.com...    (1707, 38, 681, 1300) -> (1707, 38, 681, 1300)  OK
foot               (1607, 695, 781, 643) -> (1607, 695, 781, 643)  OK

Where this fits

Session save and restore for Hyprland is a crowded and very young field — most of it started within the last few months, with several people solving the same problem in parallel without knowing about each other. hyprecall was written before its author went looking at what else existed, so nothing here is borrowed. These are simply the projects nearest to it, and in a couple of cases better answers than it:

  • hypr-persist and omarchy-desktop-preset also reconstruct the dwindle tree from window geometry and replay it with preselect — the same approach as here, arrived at independently. Three people converging on one trick is reasonable evidence the trick is right.
  • omarchy-session is well ahead of hyprecall on everything to do with relaunching: terminal working directories, browser profile and mode restoration, grouped tabs, undo, and post-restore verification. If you care more about what comes back than about where it lands, use that instead.

What hyprecall adds is that the layout claim is checkable rather than asserted — see Verifying a restore — plus window-class to .desktop resolution, so Chromium web apps come back as themselves rather than as blank browser windows.

How it works

The premise everyone stopped at is wrong. The tree does not need to be exposed.

Capture. Dwindle is a guillotine partition, so the tree is recoverable from the rectangles in hyprctl clients: find the single x- or y- line that cleanly separates the windows into two non-empty groups with nothing straddling it — that line is a split, and its position gives the ratio. Recurse. Ratios are stored as parent-relative fractions, so a snapshot survives a resolution change.

Replay. layoutmsg preselect <l|r|u|d> forces the split direction of the next window to open. Walk the tree in order, preselect, launch exactly one window, wait for it to map, repeat. Then set each split's ratio as it is created, while the new window still spans that node's whole rectangle.

Requirements

  • Hyprland 0.56+
  • Python 3.11+
  • foot and gum, for the name prompt and the picker
  • Built against Omarchy, whose Lua config makes hyprctl dispatch evaluate its argument as Lua. On a stock .conf Hyprland the dispatch layer in bin/hyprecall needs the classic string form instead.

Install

git clone https://github.com/USER/hyprecall.git
cd hyprecall && ./install.sh

This symlinks bin/hyprecall into ~/.local/bin, and adds a managed block to ~/.config/hypr/bindings.lua and ~/.config/hypr/hyprland.lua (both backed up first) binding:

Key Action
SUPER+SHIFT+F5 Snapshot this workspace, prompting for a name
SUPER+SHIFT+F6 Pick a snapshot and rebuild it on the active workspace

./install.sh --uninstall removes all of it.

Usage

hyprecall save NAME [ws]              snapshot a workspace
hyprecall restore NAME [ws] [--force] rebuild it (refuses a non-empty workspace)
hyprecall list                        list snapshots
hyprecall show NAME                   print the saved tree
hyprecall rm NAME                     delete a snapshot

Snapshots are plain JSON in ~/.local/share/hyprecall/, meant to be edited by hand when a command needs adjusting.

Working out how to relaunch an app

In order:

  1. Your override, from ~/.config/hyprecall/overrides.json{"window-class-regex": "command"}.
  2. /proc/<pid>/cmdline, when it can actually identify the window.
  3. The .desktop entry that owns the window class — matched on StartupWMClass, or for a Chromium web app by normalising the class (chrome-192.168.0.120__protect_dashboard_all-Profile_3) against the URL in each entry's Exec line.

Step 3 exists because step 2 fails in exactly the cases you care about: one Chromium process serves every web-app window, so all of them report the plain browser command line.

Verifying a restore

Layout claims are easy to make and hard to keep, so this ships with the check. --verify re-reads the workspace after a restore and diffs every window's rectangle against the snapshot:

$ hyprecall restore prime 4 --verify
chromium           (12, 38, 1581, 1300)  -> (12, 38, 1581, 1300)   OK
foot               (1607, 695, 781, 643) -> (1607, 695, 781, 643)  OK
org.omarchy.agent  (1607, 38, 781, 643)  -> (1607, 38, 781, 643)   OK

EXACT MATCH

Anything other than EXACT MATCH is a bug — please open an issue with the output and the snapshot JSON.

Known limits

  • Dwindle only. Master and scrolling layouts are not guillotine partitions in the same way and are not handled.
  • Grouped (tabbed) windows are not handled yet.
  • Fullscreen windows report the whole monitor as their geometry; a snapshot taken with one on the workspace will warn and infer the wrong tree.
  • Browser tabs are not state. A restored browser window is a fresh one.

Licence

Apache-2.0 - see LICENSE.

About

Snapshot a Hyprland workspace and recall it later — including the dwindle layout, not just which apps were open

Topics

Resources

Stars

0 stars

Watchers

0 watching

Forks

Releases

Packages

Contributors

Languages