Skip to content

Move the workspaces onto a headless output with --move-workspaces - #58

Open
Unayung wants to merge 12 commits into
omacom:mainfrom
Unayung:move-workspaces
Open

Unayung wants to merge 12 commits into
omacom:mainfrom
Unayung:move-workspaces

Conversation

@Unayung

@Unayung Unayung commented Oct 15, 2026

Copy link
Copy Markdown
Contributor

Motivation

A --headless session starts on an empty workspace of its own. The windows already open on the remote stay on its physical screens, out of view, so the session does not show the desktop you left at home. Mirroring the physical screen instead shows nothing when that monitor is asleep.

Details

  • --move-workspaces (with --headless) is a new server flag. The client takes the same flag and passes it on.
    • When the session starts, every regular workspace moves onto the headless output, and the output shows the workspace the main monitor was showing.
    • When the session ends, each workspace goes back to the monitor it came from, and each monitor shows what it showed before. This happens before the output is removed; left to itself, Hyprland would pile every workspace onto one monitor.
  • hypr-ipc gains:
    • workspaces, and Monitor::active_workspace;
    • move_workspace_to_monitor and show_workspace, which go through the same Lua-aware dispatcher helper.

This builds on #57 for that dispatcher helper, so its first commit is that change. Once that merges, only the second commit remains.

Additional information

scripts/check.sh passes. I have not run scripts/e2e.sh. Tested by hand on Hyprland 0.56.2 with a Lua config, a 3440x1440 monitor and a client on macOS: workspaces 1–3 and their windows move onto the headless output and go back to DP-2 on disconnect, and DP-2 shows the workspace it showed before.

🤖 Generated with Claude Code

A headless session starts on an empty workspace of its own, so the
windows open on the remote stay on its physical screens, out of view;
and on a machine whose monitor is asleep, mirroring it shows nothing.

`--move-workspaces` (server, and the client, which passes it on) moves
every regular workspace onto the headless output when the session starts
and shows there the one the main monitor was showing. At the end each
goes back to the monitor it came from and each monitor shows what it
did, before the output is removed; left to itself, Hyprland would pile
them all onto one monitor.

hypr-ipc gains workspace listing, each monitor's active workspace, and
the two dispatchers this needs, with the Lua fallback.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
@greptile-apps

greptile-apps Bot commented Oct 22, 2026 •

Copy link
Copy Markdown

RetriggerView in GreptileConfidence Score: 5/5

[Medium impact] The PR appears safe to merge; no blocking issue remains.

Summary

Adds --move-workspaces to bring existing workspaces onto a headless session and return them on disconnect.

  • Headless sessions can show remote workspaces and return them afterward.
  • Hyprland callers can query and move workspaces through shared helpers.

Diagram

%%{init: {'theme': 'neutral'}}%%
flowchart TD
  A[Create headless output] --> B{Workspace lock available?}
  B -- No --> C[Start without moving workspaces]
  B -- Yes --> D[Save workspace homes and visible workspaces]
  D --> E[Move regular workspaces onto session output]
  E --> F[Session ends]
  F --> G[Stop capture and input]
  G --> H[Return borrowed workspaces and restore visible workspaces]
  H --> I[Remove headless output]
Loading

Reviews (6) · Last reviewed commit: "Read a special workspace without an id f..." · Reviewed by Greptile

Comment thread crates/gliff-server/src/session.rs Resolved
Comment thread crates/gliff-server/src/session.rs Resolved
Unayung and others added 3 commits October 23, 2026 03:09
Hyprland gives named workspaces negative IDs, like the scratchpads, so
skipping every ID at or below zero left named workspaces and their
windows on the physical screens. Scratchpads are now told apart by their
"special" name, and named workspaces are moved and shown by
"name:<name>".

A workspace's home was recorded only after the move answered ok. A move
whose answer timed out could still happen, and then nothing put that
workspace back. The home is now recorded before the move; restoration
already moves only the workspaces still on the session's output.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01JauiFwn6qx2NBFZJ7FvEva
Restoration retried its snapshot but sent each move once. If IPC stalled
then, the move failed silently and the output was removed anyway,
leaving that workspace wherever Hyprland put it. Each move is now
retried like the snapshot, and one that still fails is logged with the
monitor it should have gone back to.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01JauiFwn6qx2NBFZJ7FvEva
Comment thread crates/hypr-ipc/src/lib.rs Resolved
Unayung and others added 2 commits October 23, 2026 04:36
A named workspace reaches the Lua dispatchers as "name:<name>", and the
name went into the Lua string unescaped. A quote or backslash in it broke
the call or changed the name Hyprland read, so that workspace was neither
moved nor returned. Both calls now quote the selector as a Lua string.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01JauiFwn6qx2NBFZJ7FvEva
Main's keep-awake cancellation gave the dispatcher a third argument, the
check that lets teardown skip a request. Workspace moves and focus are
never cancelled, so they pass `|| true`, as set_dpms does.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01NWLtaPFL1Y2jfFD7qm2QjZ
Unayung added a commit to Unayung/gliff that referenced this pull request Oct 27, 2026
Brings in current main through omacom#58. Main's session test still passed a
bare VideoMode; it now passes Wants, without moving workspaces.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01NWLtaPFL1Y2jfFD7qm2QjZ
Comment thread crates/gliff-server/src/session.rs Resolved
Unayung and others added 2 commits October 27, 2026 12:10
Showing the restored workspace now retries like the moves before it,
and warns if it still fails. Special workspaces are recognized by the
"special:" prefix Hyprland gives them, so a regular named workspace
such as "special-mail" is moved like any other.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01NWLtaPFL1Y2jfFD7qm2QjZ
Hyprland 0.57 leaves `id` out of a special workspace in `j/workspaces`.
With a scratchpad open the whole list failed to parse (`missing field
`id``), so a session moved no workspaces. A missing id reads as 0, which
selects by name as the negative ids of special workspaces did.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01CMMntUgWn4YwdAk17sViei
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.

2 participants