Files
alan 1ed57291f9 Screencast: cast the desktop to Cast, DLNA and AirPlay receivers
An Omarchy shell plugin: a Python/asyncio daemon that discovers network
displays and serves an encoded screen capture for them to pull, plus a
Quickshell bar widget and panel to drive it.

The screen comes from the xdg-desktop-portal ScreenCast interface (asked
for every time, never remembered), goes through GStreamer, and is served
from a local HTTP port. Desktop audio is mixed in from the default
output's monitor. The container follows the receiver: WebM/VP8 for Cast,
MPEG-TS/H.264 for DLNA, HLS for AirPlay video.

The stream port has to be reachable from the LAN, and none of these
protocols can carry a credential, so the capability is the URL: a fresh
random path per session, refused to anything off the LAN, capped at four
concurrent readers.
2026-08-29 19:57:54 +01:00

3.5 KiB

Screencast

An Omarchy shell plugin that finds the displays on your network and casts this desktop to them, with the controls in the bar.

Google Cast (Chromecast, Google TV, Nest), DLNA/UPnP TVs, and AirPlay video. The screen is captured through the xdg-desktop-portal ScreenCast interface, encoded with GStreamer, and served from a local HTTP port that the receiver pulls from. Desktop audio is mixed in from the default output's monitor.

Status: early. Looking for testers. It works here on Cast; DLNA and AirPlay have had far less exercise. Bug reports welcome.

Install

omarchy plugin add https://github.com/alanfortlink/omarchy-screencast --enable

On first run the plugin builds a private virtualenv under ~/.local/lib/screencast for its daemon. That needs no password. It does need these Arch packages, and it will tell you in the panel if any are missing:

gstreamer gst-plugins-base gst-plugins-good gst-plugins-bad gst-plugin-pipewire
gst-libav xdg-desktop-portal xdg-desktop-portal-hyprland

Remove with omarchy plugin remove io.github.alanfortlink.screencast.

Using it

Click the cast icon (󰄘), click a display. The portal asks which screen to share every time — the choice is deliberately never remembered. While casting you get stop, pause, change-screen, a live preview and the receiver's volume. Quality, bitrate, desktop audio and pointer visibility are at the bottom of the panel.

Keys while the panel is open: / and Enter to cast, r rescan, s stop, p pause, Esc close.

For a keybinding:

bind = SUPER SHIFT, C, exec, omarchy-shell io.github.alanfortlink.screencast cast "TV name"
bind = SUPER SHIFT, X, exec, omarchy-shell io.github.alanfortlink.screencast stop

The same from a terminal: screencast-server devices | cast "TV name" | status | stop | pair "TV name".

Settings live in ~/.config/screencast/config.json (fps, maxHeight, bitrate, encoder, audio, cursor, port, autoReconnect) and the panel writes the same file.

Two things to know

It is not low latency. Cast, DLNA and AirPlay video are pull protocols: you hand the device a URL and it buffers. Expect one to three seconds, same as casting a tab from a phone. Fine for a film, a talk or a dashboard; wrong for playing a game on the TV. Low-latency mirroring means Miracast or real AirPlay mirroring, neither of which has a working Linux sender.

The receiver connects to you, so a default-deny inbound firewall — which Omarchy sets up — silently drops it. The panel notices and offers a button that opens the port for your subnet only, through one polkit password dialog. Nothing to type.

AirPlay here is AirPlay video, not mirroring (mirroring needs Apple's FairPlay handshake). Apple TVs and most AirPlay 2 TVs accept it; some want pairing first, via the Pair button on the row. Support comes from pyatv, which is installed best-effort — if it fails, Cast and DLNA carry on.

Troubleshooting

  • Nothing found. mDNS and SSDP do not cross subnets, VPNs, or the client isolation some routers apply to Wi-Fi. Compare with avahi-browse -rt _googlecast._tcp.
  • The TV spins forever. Almost always the firewall above.
  • Stutter. Lower the bitrate or resolution; Wi-Fi is usually the limit. Cast is the one path that encodes VP8 on the CPU.
  • Logs. ~/.local/state/screencast/daemon.log, and ~/.cache/screencast/install.log for the install.

Licence

MIT — see LICENSE.