Hyprland
Stream a Hyprland session through xdg-desktop-portal-hyprland, and fix portal routing, permissions and black streams.
Stream a Hyprland session. Install the host first: Arch, Ubuntu or Fedora; on Omarchy, follow Omarchy.
What works
- Each client gets its own headless output at its exact mode. The host captures it straight from Hyprland on the GPU, or through xdg-desktop-portal-hyprland (xdph) when it can't. No share dialog appears.
- Keyboard and mouse go in through the virtual pointer and keyboard protocols.
- Library launches open on their own workspace on the streamed output.
- A reconnect inside the keep-alive window gets the same
output back;
ctl displaylists it. When the output goes, its windows move to a remaining monitor. - A
hyprctl reloadmid-stream keeps the client's mode.
It needs a running Hyprland session and xdph. If another portal backend (gtk, wlr) is installed
too, route ScreenCast to xdph in ~/.config/xdg-desktop-portal/hyprland-portals.conf, then run
systemctl --user restart xdg-desktop-portal:
[preferred]
default=gtk
org.freedesktop.impl.portal.ScreenCast=hyprlandhost.env
Nothing is required: the host finds Hyprland on every connect.
Don't set PUNKTFUNK_COMPOSITOR: a pin turns off detection and session following. Every other
option is in Configuration.
The host adds its picker to ~/.config/hypr/xdph.conf and restarts xdph when that changes. A
picker you configured there still runs for every share the host didn't ask for.
Start the host
From inside your Hyprland session:
systemctl --user enable --now punktfunk-host
journalctl --user -u punktfunk-host -f # watch it start and print its fingerprintOpen the web console to pair a client. To serve Moonlight too, see What the unit starts.
Headless session
There is no packaged headless Hyprland session. Log in on the box, or autologin through your
display manager, and enable lingering so the host starts at boot:
sudo loginctl enable-linger "$USER".
Known limits
- The streamed output runs at scale 1.
- SDR only: HDR on Linux needs gamescope — see HDR.
- Under exclusive topology your monitors come back through
hyprctl reloadwhen the display is released. That reload also drops runtimehyprctl keywordchanges and re-runsexec =lines. A reload during the stream lights them early.
Troubleshooting
"hyprctl not reachable"
The host can't find the Hyprland instance. Start it from inside the session, as above.
Black stream, and the host says the output "stayed 0x0"
Hyprland couldn't allocate a buffer for the headless output; its log shows
GBM: Failed to allocate a GBM buffer. This is a GPU driver or nested-session problem. Run
Hyprland as a real session and check your driver's GBM support.
Black stream, and xdph logs "unavailable cursor mode 4"
A host that predates the cursor-mode check asked xdph for a cursor mode it doesn't offer. Update
the host, or switch the client to game mouse mode meanwhile. If the
pointer misbehaves on an xdph that does offer it, set PUNKTFUNK_PORTAL_CURSOR_MODE=embedded.
Black frames with enforce_permissions on
Hyprland's permission system (0.49+, off by default) denies screen capture silently: the stream
shows black frames. The host logs a warning at startup when it's on. Grant screencopy to the host
and to xdph, which the host falls back to, with the paths your packages installed. In
hyprland.lua:
hl.permission("/usr/bin/punktfunk-host", "screencopy", "allow")
hl.permission("/usr/(lib|libexec|lib64)/xdg-desktop-portal-hyprland", "screencopy", "allow")or in hyprland.conf:
permission = /usr/bin/punktfunk-host, screencopy, allow
permission = /usr/(lib|libexec|lib64)/xdg-desktop-portal-hyprland, screencopy, allowRestart Hyprland afterwards; it reads permissions only at startup. The virtual pointer needs no
grant. Keyboards are allowed by default; a keyboard rule that denies new keyboards also blocks
the host's.
More fixes in Troubleshooting.