Repository navigation
Control the selected machine from the window - #106
Open
avillagran wants to merge 33 commits into
Open
avillagran wants to merge 33 commits into
avillagran wants to merge 33 commits into
Conversation
gliff --stdio shows the gliff-server whose protocol arrives on stdin and leaves on stdout. Another machine runs that server and starts this over ssh with the server's stdout as its stdin, to use this screen as one of its own; only that machine needs ssh access to this one. - stdout is kept for the protocol and fd 1 points at stderr, so nothing that prints can corrupt the stream. - Started by ssh outside the desktop session, it finds the user's Hyprland session the way gliff-server does. - The window shows only that session and remembers nothing; it closes when the stream ends, since only the other machine can start it again. - --fullscreen opens the window fullscreen; the header no longer warns when the window starts that way.
A headless output always went where Hyprland found room. --position puts it right, left, above or below this machine's screens, and --align lines it up with their top or left edges (start), their middles, or their bottom or right edges (end). The position is worked out from the other screens' layout each time the output's mode is set, so a resize keeps it in place beside them.
The header gets a button that adds a headless output here and shows it on the selected machine with `gliff --stdio --fullscreen` over ssh, and a settings button for which side of this computer's screens it stands on and how it lines up. A gliff there without `--stdio` still shows it through a relay in place of its ssh, with Hyprland making its window fullscreen. Stopping, the other window closing or this window closing gives the screen back.
The position comes from the output's logical size, which resize computed with the requested scale. When Hyprland applies a rounded scale instead, place the output again at that scale, so it still meets the other screens on the chosen side and lines up as asked.
The window closes when that stream ends, so a machine opened from its address bar would close with it. A --stdio window shows neither the + nor the address bar, and connecting from the bar does nothing there.
A --stdio window that closes on a connection error or an incompatible server now makes gliff exit with a failure, with the reason as its last stderr line, so the machine that started it can say why. A window the user closes, or a stream the other side ends, still exits successfully.
Showing another machine forgets the lent screen's tab as the selection, and selecting that tab again did not bring it back, so it lost its highlight and the screen buttons asked for a tab once the screen stopped. Selecting a tab whose screen is in use now makes it the selected one, as starting the screen does.
Hyprland places a screen whose position is `auto`, as Omarchy's default configuration does, after every screen with a fixed one. A fixed position for the headless output therefore pushed such a screen aside: on a laptop asked for `--position right`, the laptop screen moved right of the new output. When placing the output moves another screen, the placement goes to Hyprland's `auto-right`, `auto-center-right` and related positions instead. They line up at the start or the center of a side, so `--align end` is centered there, with a warning.
gliff 0.4.1 opens the GPU decoder whenever VA-API lists H.264, and NVIDIA's VA-API driver does, but every picture then fails to export, so the screen lent to such a machine stayed black. That gliff cannot switch to the CPU on its own, so the relay asks for it with GLIFF_VIDEO=cpu when every render node belongs to the nvidia driver, unless GLIFF_VIDEO is already set.
A new crate puts a one-pixel layer surface along chosen outer edges of the layout. When the pointer touches one it locks the pointer there, hides it, takes the keyboard and inhibits shortcuts, and reports relative motion, buttons, the wheel, keys, modifiers and the keymap until told to release. Releasing puts the pointer back just inside that edge, through a new hypr-ipc move_cursor that speaks both config formats.
--control SIDE skips the picture: after the usual Hello it binds a virtual keyboard and pointer, with the client's keymap, to the screen on SIDE of the layout, answers with that screen's size, and injects the client's keys, motion, buttons and wheel until the client goes. The handshake and the setup error report are the session's own.
gliff --control SIDE:HOST, given once for each machine, runs with no window: it keeps a gliff-server --control connection to each machine, reconnecting with backoff, and arms that side's edge only while the machine answers. Pushing the pointer past an armed edge carries it onto that machine's screen, entering at the facing edge where it left this one. Moving back across that edge, or the release hotkey, returns it at the matching point; a held button keeps the pointer there. Keys and buttons still held are let go on the other machine on every return. The release hotkey is matched by keysym in the local keymap, so it means the same keys in any layout.
# Conflicts: # crates/gliff/src/main.rs
A mouse button beside the screen buttons drives the selected machine with this computer's pointer and keyboard across the edge on its side: the same side the screen settings keep, asked for the first time as they are, without the alignment, which only a screen in use needs. Each driven machine runs its control on a thread of its own, so one machine on each side can be driven at once; a second machine on a side already taken is refused. The machine's tab stays selected without its session, says how to cross over or that it is connecting, and its dot shows whether the machine answers. The tab's button or the mouse button, now a stop button, ends the control, which gives the pointer back. A machine is not driven and used as a screen at once. Closing the window ends every control. The control loop now stops on a signal from its owner and reports when a machine connects, drops and why, and when it ends.
While a machine is driven or its screen is in use, the window has nothing to show, so a button beside the screen buttons hides it. A tray icon (a StatusNotifierItem over the session bus, which Omarchy's bar and Waybar show) stands in for it, and a click on the icon brings the window back. Without a tray to come back from, the window stays. The window also comes back by itself once nothing runs in the background any more.
A speaker button, shown while the selected machine is driven, moves that machine's sound to this one: over ssh, pw-record takes what its default output plays, before its volume, as raw 48 kHz stereo, and pw-play plays it here. The other machine's output is muted meanwhile and unmuted however the stream ends. It is muted only once the capture has woken the output, because WirePlumber restores a suspended output's saved mute as it wakes, and once more a second later. The choice is kept per machine and applies the next time it is driven. Stopping the control, or the window, stops the sound too.
[High impact] The PR appears safe to merge; no new actionable issues were found. SummaryAdds window buttons to control the selected machine, forward its sound, and hide the window in the tray.
|
The client works out the pointer's extent from the size in HelloAck, but it got the mode as Hyprland reports it, unrotated, while the server bound the pointer to the rotated screen. On a portrait screen the pointer could not reach its bottom. Swap the size for a 90 or 270 degree transform, as the video session does.
The pointer returned to the edge's screen as it stood when control started. A screen moved or rescaled meanwhile put it somewhere else, possibly on the edge again. Read the layout when releasing, and keep the old one only when it cannot be read.
The script unmuted whichever output was the default when the stream ended, and read the mute of the default rather than of the output it muted. If a headset became the default meanwhile, the speakers stayed muted and the headset was unmuted. Keep the default output at the start and use that one throughout.
A sound stopped and started again, or stopped with its control and started with the next one, ends after the new sound began. Its end cleared the machine's sound by name, which dropped the new one and killed its ssh. Each sound now has an id, and an end from another run is ignored.
If the tray's connection failed, its thread ended and the icon went with it, but the window stayed hidden, leaving no way to stop what ran. A closed channel from the tray now shows the window, as a click does.
Author
|
All five findings are addressed, each in its own commit: 35e5217 (sound mutes and unmutes the output it was taken from), 9e9f357 (an earlier sound's end cannot clear a newer one), 39f9177 (the window comes back if the tray icon goes away), and from #105, merged here in 8164117, ca04948 (rotated screen size in the control handshake) and ea8aa87 (screens read again before giving the pointer back). |
avillagran
added a commit
to avillagran/gliff
that referenced
this pull request
Oct 30, 2026
The other machine's keyboard started from a fresh state and followed only the keys typed into it, so a second layout selected here, or Caps Lock or Num Lock already on, did not apply there. The client now sends its layout in effect and locked modifiers, by name, on entering and whenever they change while captured, in a new KeyboardLocks message. The control server lists the keyboard-locks feature and applies them to its virtual keyboard; a server that does not list it is not sent them, and the video session ignores the message.
Hotkey::parse accepts names such as exclam, but matching looked only at each key's unshifted symbol, so shift+exclam was accepted and never matched. A key now matches by its unshifted symbol or by the one it produces with the modifiers held.
A link whose network went away without closing the connection stayed up: nothing read had a deadline, so its edge kept taking the pointer to a machine that was gone, and input queued for a write that never ended. The link now pings every 2 seconds and is dropped after 6 seconds without hearing from the other machine, or when a write takes that long; it then disarms its edge and reconnects as after any drop.
An edge's screen and rectangle were chosen once, so a screen added farther out left the edge on what was now an inner side, and a moved or rescaled screen kept the old coordinates. hypr-wl's Outputs now binds and forgets outputs that come and go, and hypr-edge reads the layout again after each output's burst, when one goes away, and when the pointer is given back: an edge whose outermost screen changed is put on the new one once Wayland has announced it, or after the pointer it holds is given back. Whether an edge is armed is now kept apart from its surface, so a surface the compositor closes with its screen is given again, and events from an edge's previous surface are ignored.
avillagran
added a commit
to avillagran/gliff
that referenced
this pull request
Oct 30, 2026
Keymaps and locks reach a link by separate ways, so a layout selected in a keymap that had just gained it could arrive first, be dropped by a server still on the old keymap, and not be sent again. The link now writes the last locks again right after each keymap.
avillagran
added a commit
to avillagran/gliff
that referenced
this pull request
Oct 30, 2026
An edge whose screen changed while it held the pointer moved only when the pointer was given back by a release. When the compositor ended the capture instead, by leaving the edge or unlocking the pointer, the edge stayed on the old screen until the next layout change. Losing the capture now reads the layout again, as a release does.
avillagran
added a commit
to avillagran/gliff
that referenced
this pull request
Oct 30, 2026
This was referenced Oct 30, 2026
The stream arrives in bursts, and over WiFi one is sometimes 70 ms late, which left the 30 ms player with nothing to play.
With a machine on each side the screen between them is narrow enough for a fast return to reach the other edge and leave again.
avillagran
added a commit
to avillagran/gliff
that referenced
this pull request
Oct 31, 2026
avillagran
added a commit
to avillagran/gliff
that referenced
this pull request
Oct 31, 2026
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
What this adds
gliff --control(#105) drives another machine from the command line.This puts it in the window, beside the screen buttons from #103.
machine with this computer's pointer and keyboard across the edge on
its side. That is the same side the screen settings keep, asked for
the first time as they are, without the alignment, which only a screen
in use needs. The button turns into a stop button while the machine is
driven.
to cross over, or that it is connecting, and its dot shows whether the
machine answers. The tab's own button also ends the control, which
gives the pointer back.
its own. A second machine on a side already taken is refused, and a
machine is never driven and used as a screen at the same time.
Closing the window ends every control.
use, the window has nothing to show, so a button hides it. A tray icon
(a StatusNotifierItem on the session bus, which Omarchy's bar and
Waybar show) stands in for it; a click brings the window back. Without
a tray the window stays. It also comes back by itself once nothing
runs in the background.
selected machine is driven, moves that machine's sound here: over ssh,
pw-recordtakes what its default output plays, as raw 48 kHz stereo,and
pw-playplays it locally. The other machine's output is mutedmeanwhile and unmuted however the stream ends. The choice is kept per
machine. Stopping the control, or closing the window, stops the sound.
The control loop from #105 now stops on a signal from its owner and
reports when a machine connects, drops and why, and when it ends.
Details
its output, and once more a second later, because WirePlumber restores
a suspended output's saved mute as it wakes.
dbuscrate already in the lockfile.over ssh do not reach
pw-playas cuts.opposite edge, so a fast return across a narrow screen does not carry on
to the machine on the other side. The log says so when this happens.
Testing
scripts/check.shpasses.machine: first-time side settings, crossing over, stopping from the
header button and from the tab.
playing the driven machine's sound here with its output muted, from a
laptop driving a desktop, and then on a third machine.
delay, and pointer movement between two driven machines in daily use.
Screenshots
Captured on a laptop, with a nested Hyprland session standing in for the
other machine.
The mouse button, the first time, asks which side the machine is on:
While the machine is driven, the header shows the hide, stop and speaker
buttons, and the tab says how to cross over:
Hidden, the window leaves its icon in Omarchy's tray (second from the
left). The animated wallpaper that shows through the bar was removed by
taking the median of a few frames.