Loading blogs…
Linux Desktop5 min read
Screen sharing black, empty or missing on Hyprland? Work through the portal chain step by step — packages, services, the WAYLAND_DISPLAY race, permissions and XWayland apps.
Full-stack engineer, New Delhi
On Wayland, apps aren't allowed to read your screen directly, which is good for security and annoying when you need to share your screen in a call in five minutes. Sharing goes through a chain of services, and when any link is missing you get a black window, an empty picker, or a share button that does nothing.
Work through this top to bottom. Each step takes under a minute.
App (browser, OBS, Discord)
→ xdg-desktop-portal (the generic portal)
→ xdg-desktop-portal-hyprland (the Hyprland backend + picker)
→ PipeWire (carries the video stream back to the app)Every failure I've seen is one of these links not running, or the wrong backend answering.
sudo pacman -S pipewire wireplumber xdg-desktop-portal \
xdg-desktop-portal-hyprland xdg-desktop-portal-gtkxdg-desktop-portal-gtk isn't for screen sharing. It provides the file-open dialogs that the Hyprland backend doesn't implement. It's harmless to have, and apps hang on "Open file" without it.
Remove xdg-desktop-portal-wlr if you have it from an old Sway install. It's the most common cause of "the picker never appears", because it can answer instead of the Hyprland backend. Other backends (-gnome, -kde) are fine to keep if you also use those desktops. Arch ships /usr/share/xdg-desktop-portal/hyprland-portals.conf, which tells the portal to prefer Hyprland's backend in a Hyprland session.
systemctl --user status pipewire wireplumber \
xdg-desktop-portal xdg-desktop-portal-hyprlandAll four should be active (running). If xdg-desktop-portal-hyprland says "condition failed" or inactive (dead), you've hit the most common problem, step 3.
A quick reset that often fixes it on the spot:
systemctl --user restart xdg-desktop-portal-hyprland xdg-desktop-portalThe Hyprland portal unit has this line:
ConditionEnvironment=WAYLAND_DISPLAYIf anything asks for the portal before Hyprland has exported WAYLAND_DISPLAY into the systemd user environment, systemd skips the service, and it stays skipped. Something you autostart can do this, and so can a leftover session from your display manager.
Check whether the variable is there:
systemctl --user show-environment | grep -E "WAYLAND_DISPLAY|XDG_CURRENT_DESKTOP"You should see WAYLAND_DISPLAY=wayland-1 (or similar) and XDG_CURRENT_DESKTOP=Hyprland.
Hyprland imports these itself at startup, so the dbus-update-activation-environment lines many old guides tell you to add are usually redundant. The fixes that actually help:
XDG_CURRENT_DESKTOP. Some configs set it to GNOME or sway to make an app happy. The portal uses this value to choose its backend, so a wrong value means the wrong backend, or none.HYPRLAND_NO_SD_VARS. It turns off the environment import.uwsm installed, choose "Hyprland (uwsm-managed)" at your login screen instead of plain "Hyprland". uwsm starts Hyprland as a proper systemd session, so the environment is set and graphical-session.target is active before any portal starts. This fixes the race for good.If you'd rather not use uwsm, a small user target activates graphical-session.target for a plain session (this is the pattern systemd.special(7) describes for session targets):
# ~/.config/systemd/user/hyprland-session.target
[Unit]
Description=Hyprland session
BindsTo=graphical-session.target
Wants=graphical-session-pre.target
After=graphical-session-pre.targetStart it from your Hyprland config:
hl.on("hyprland.start", function()
hl.exec_cmd("systemctl --user start hyprland-session.target")
end)If you've turned on Hyprland's permission system (ecosystem.enforce_permissions = true), the portal needs explicit permission to capture the screen. Otherwise sharing silently returns black frames:
hl.permission("/usr/(lib|libexec|lib64)/xdg-desktop-portal-hyprland", "screencopy", "allow")
hl.permission("/usr/(bin|local/bin)/grim", "screencopy", "allow")Permission changes need a full Hyprland restart, not a reload.
Firefox. Works out of the box on Wayland. Share a window or screen and the Hyprland picker appears.
Chrome / Chromium / Brave. Chrome has picked Wayland automatically since version 140, so sharing works through the portal out of the box. If an old launcher or ~/.config/chrome-flags.conf forces --ozone-platform=x11, remove it.
Electron apps (VS Code, Slack, Obsidian…). Set hl.env("ELECTRON_OZONE_PLATFORM_HINT", "auto") so they run natively on Wayland and use the portal.
OBS Studio. Add a Screen Capture (PipeWire) source, not "Display Capture (XSHM)". If the PipeWire source is missing, the portal isn't running (step 2).
Discord. The web version in a Wayland browser is the most reliable. For the desktop app, make sure it runs on Wayland with the Electron hint above. If it runs under XWayland it can only see other XWayland windows.
Zoom. Set enableWaylandShare=true in ~/.config/zoomus.conf, then set Settings → Share Screen → Advanced → Screen capture mode to PipeWire. Zoom still runs a legacy desktop check, so if sharing is greyed out, launch it with the variable overridden for Zoom only: XDG_CURRENT_DESKTOP=gnome zoom. Don't set that globally (see step 3).
Any XWayland-only app. X11 apps can't capture Wayland windows. That's by design, not a bug. Use the app's web version in a Wayland browser, or a native Wayland build if one exists.
journalctl --user -b -u xdg-desktop-portal-hyprland -u xdg-desktop-portalTypical messages and what they mean:
Condition check resulted in … being skipped: the race from step 3Failed to connect to PipeWire: run systemctl --user restart pipewire wireplumberPermission denied / screencopy: Hyprland permissions, step 4On NVIDIA, a black stream with no errors usually means an old driver. Check my Hyprland on NVIDIA guide. If you need help reading service logs generally, see my systemd & journalctl guide.
xdg-desktop-portal-hyprland installed, xdg-desktop-portal-wlr removedWAYLAND_DISPLAY and XDG_CURRENT_DESKTOP=Hyprland in systemctl --user show-environmentLinux Desktop
Hyprland 0.55 moved to Lua configs and hyprlang is being dropped. A side-by-side cheat sheet for binds, rules, exec-once and env, plus the hyprctl dispatch gotcha.
Linux Desktop
Run Hyprland on an NVIDIA GPU on Arch: picking nvidia-open vs nvidia-580xx, what's already enabled for you, the env vars that matter, suspend, and hybrid laptops.
Linux Desktop
Set up a lock screen and idle timers on Hyprland: dim, lock, screen off and suspend with hypridle, a clean hyprlock config, lock-on-lid-close, and the Lua-era dpms fix.