Hyprland HDMI Revive
Automatically recover a Wayland/Hyprland desktop after switching a physical monitor input away from an Arch Linux workstation and back again.
This project was built for a setup where one external HDMI monitor is shared between:
- an Arch Linux workstation running Hyprland/Wayland
- a Windows laptop connected to another HDMI input on the same monitor
On some monitors/GPU combinations, switching back to the Arch input leaves the display black or showing No source, even though Hyprland still reports the monitor as enabled, focused, and DPMS-on.
The workaround that reliably restores the image is a short VT switch away from the active Hyprland VT and back again. This repository automates that workaround only when the kernel reports a DRM hotplug event and only while an active Wayland session exists.
How it works
Monitor input switch
|
v
Kernel DRM HOTPLUG=1 event
|
v
User service receives the event
|
v
Is there an active Wayland session for this user?
| |
no yes
| |
ignore detect active VT
|
v
wait for HDMI sync
|
v
Hyprland DPMS/focus refresh
|
v
temporary VT switch
|
v
switch back to Hyprland
The important detail is that the recovery is event-driven. There is no polling timer and no periodic DPMS off/on cycle, so the screen does not blink during normal desktop use.
Repository layout
.
├── config/
│ └── revive-monitor.env.example
├── docs/
│ ├── architecture.md
│ └── troubleshooting.md
├── scripts/
│ ├── install.sh
│ ├── revive-monitor-drm-watch.sh
│ ├── revive-monitor-fix.sh
│ └── uninstall.sh
├── sudoers/
│ ├── revive-monitor-chvt.example
│ └── revive-monitor-chvt.strict.example
├── systemd/
│ └── user/
│ └── revive-monitor-drm-watch.service
├── .gitea/
│ ├── issue_template/
│ └── pull_request_template.md
├── .editorconfig
├── .gitignore
├── CHANGELOG.md
├── LICENSE
├── SECURITY.md
└── README.md
Requirements
- Arch Linux or another systemd-based Linux distribution
- Hyprland
systemd/systemd-logindudevadmhyprctlflockfromutil-linuxchvtfromkbdsudo
On Arch Linux, the relevant packages are typically already present. If needed:
sudo pacman -S --needed hyprland systemd util-linux kbd sudo
1. Clone the repository
Example for Gitea:
git clone ssh://git@gitea.example.com/your-user/hyprland-hdmi-revive.git
cd hyprland-hdmi-revive
2. Configure the monitor
Copy the example configuration:
mkdir -p ~/.config/revive-monitor
cp config/revive-monitor.env.example ~/.config/revive-monitor/config.env
Determine the Hyprland output name:
hyprctl monitors
Example:
Monitor HDMI-A-2 (ID 0):
Then edit:
nano ~/.config/revive-monitor/config.env
Example:
MONITOR=HDMI-A-2
DEBOUNCE_SECONDS=30
HDMI_SETTLE_SECONDS=5
VT_SWITCH_DELAY_SECONDS=1
VT_RETURN_SETTLE_SECONDS=2
3. Install the scripts and systemd user service
Run:
./scripts/install.sh
The installer copies the scripts to:
~/.local/bin/revive-monitor-fix
~/.local/bin/revive-monitor-drm-watch
and installs the user service at:
~/.config/systemd/user/revive-monitor-drm-watch.service
The service is intentionally attached to default.target, because not every Hyprland setup activates graphical-session.target.
The watcher itself performs an additional safety check and refuses to switch VTs unless it finds an active Wayland session for the current user. This prevents DRM hotplug events during boot from switching the display manager away from its VT.
4. Configure sudo safely
chvt requires root privileges. Do not give the script unrestricted passwordless sudo.
Recommended general rule
Open a dedicated sudoers file:
sudo visudo -f /etc/sudoers.d/revive-monitor-chvt
Add:
YOUR_USER ALL=(root) NOPASSWD: /usr/bin/chvt
Replace YOUR_USER with your username.
This grants passwordless sudo only for /usr/bin/chvt, not for arbitrary root commands.
More restrictive rule
If your graphical session always uses VT1 and the temporary VT is always VT2, use:
YOUR_USER ALL=(root) NOPASSWD: /usr/bin/chvt 1, /usr/bin/chvt 2
A template is included in:
sudoers/revive-monitor-chvt.strict.example
The scripts use sudo -n, so they never block waiting for a password prompt.
Test the rule manually:
sudo -n /usr/bin/chvt 2
sleep 1
sudo -n /usr/bin/chvt 1
5. Start the service
The installer enables it automatically. To do it manually:
systemctl --user daemon-reload
systemctl --user enable --now revive-monitor-drm-watch.service
Check status:
systemctl --user status revive-monitor-drm-watch.service
Watch logs:
journalctl --user -u revive-monitor-drm-watch.service -f
Why not xrandr?
xrandr is an X11 tool. Under Hyprland/Wayland it is not the correct interface for controlling physical outputs and may only see Xwayland state. Typical symptoms include:
Authorization required, but no authorization protocol specified
Can't open display :0
This project uses hyprctl for compositor-side state and DRM/udev events for the actual hotplug trigger.
Why not poll every 20 or 30 seconds?
A polling approach can accidentally run disruptive operations while the desktop is already working. In early versions, a periodic DPMS toggle caused the monitor to go dark and come back every few seconds.
This implementation does not use a timer. Recovery is triggered only by:
SUBSYSTEM=drm
HOTPLUG=1
and only if an active Wayland session exists.
Why the active Wayland-session check matters
During boot, GPUs and displays can generate DRM hotplug events before the display manager or Hyprland session is ready.
Without a session guard, a recovery script that always runs:
chvt 2
chvt 1
can leave the machine on tty1 while the display manager is actually running on tty2.
The watcher therefore queries systemd-logind via loginctl and only runs recovery when it finds a session matching all of these conditions:
Name=<current user>
Type=wayland
Active=yes
VTNr>0
The current graphical VT is detected dynamically and passed to the recovery script. No VT is hard-coded.
Example successful log
revive-monitor-drm-watch: DRM hotplug detected - Wayland session 3 on VT 1
revive-monitor-fix: Recovery started for HDMI-A-2 on VT 1
revive-monitor-fix: VT refresh: 2 -> 1
sudo: COMMAND=/usr/bin/chvt 2
sudo: COMMAND=/usr/bin/chvt 1
revive-monitor-fix: Recovery completed for HDMI-A-2
revive-monitor-drm-watch: Recovery after DRM hotplug finished
Verify the boot-safety fix
After rebooting, check:
journalctl -b --no-pager | grep -E 'revive-monitor|COMMAND=/usr/bin/chvt|chvt'
Before logging into Hyprland, you should either see no recovery at all or a message similar to:
No active Wayland session - DRM hotplug ignored
There should be no chvt call before the graphical user session exists.
Uninstall
Run:
./scripts/uninstall.sh
Then remove the sudoers file manually:
sudo rm /etc/sudoers.d/revive-monitor-chvt
The uninstall script intentionally does not remove sudoers configuration automatically.
Troubleshooting
Security notes
- The service runs as a normal systemd user service.
- Only
chvtrequires root privileges. sudo -nprevents unattended password prompts.- A lock file prevents overlapping recovery runs.
- Debouncing suppresses repeated DRM hotplug events.
- The recovery runs only for an active Wayland session belonging to the current user.
- The sudoers rule should be limited to
/usr/bin/chvt, or ideally only the specific VTs used on the machine.
License
MIT. See LICENSE.