Event-driven automation for Hyprland. HyprWhenThen listens to Hyprland events and executes actions based on configurable rules, enabling dynamic window management and workspace automation.
hyprthenwhen-demo.mp4
This demo showcases HyprWhenThen's core capabilities:
- React to any Hyprland event, silly examples:
- Real-time notifications when applications change fullscreen status
- Window tracking with instant alerts on window opening
- Live configuration reloading without service restart
- HyprWhenThen
- Event-driven automation: React to any Hyprland event (window title changes, workspace switches, etc.)
- Regex pattern matching: Flexible event filtering with capture groups for dynamic actions
- Concurrent execution: Multi-worker architecture with configurable parallelism
- Routing keys: Control execution order for related events
- Hot configuration reloading: Update rules without restarting the service
- Timeout management: Configurable timeouts for both global and per-handler execution
- Template variables: Use regex capture groups in your action commands
Download the latest binary from GitHub releases:
# optionally override the destination directory, defaults to ~/.local/bin/
export DESTDIR="$HOME/.bin"
curl -o- https://raw.githubusercontent.com/fiffeek/hyprwhenthen/refs/heads/main/scripts/install.sh | bashFor Arch Linux users, install from the AUR:
# Using your preferred AUR helper (replace 'aurHelper' with your choice)
aurHelper="yay" # or paru, trizen, etc.
$aurHelper -S hyprwhenthen-bin
# Or using makepkg:
git clone https://aur.archlinux.org/hyprwhenthen-bin.git
cd hyprwhenthen-bin
makepkg -siRequires asdf to manage the Go toolchain:
# Build the binary (output goes to ./dest/)
make
# Install to custom location
make DESTDIR=$HOME/binaries install
# Uninstall from custom location
make DESTDIR=$HOME/binaries uninstall
# Install system-wide (may require sudo)
sudo make DESTDIR=/usr/bin installCreate ~/.config/hyprwhenthen/config.toml:
[general]
timeout = "1s"
# Float Google login window automatically
[[handler]]
on = "windowtitlev2"
when = "(.*),Sign In - Google Account"
then = "hyprctl dispatch togglefloating address:0x${REGEX_GROUP_1}"
# Switch to workspace when specific app opens
[[handler]]
on = "openwindow"
when = "firefox"
then = "hyprctl dispatch workspace 2"# Start the service
hyprwhenthen run
# Validate configuration
hyprwhenthen validate
# Run with debug logging
hyprwhenthen run --debugYou can run it directly by editing ~/.config/hypr/hyprland.conf:
exec-once = hyprwhenthen run
For a more sophisticated setup, see Running with systemd.
[general]
timeout = "15s" # Global timeout for all handlers
hot_reload_debounce_timer = "100ms" # Debounce time for config reloading, defaults to 1sEach handler defines an event-action rule:
[[handler]]
on = "windowtitlev2" # Hyprland event type
when = "(.*),Mozilla Firefox" # Regex pattern to match event data
then = "notify-send 'Firefox: $REGEX_GROUP_1'" # Command to execute
timeout = "5s" # Optional: override global timeout
routing_key = "$REGEX_GROUP_1" # Optional: control execution orderHyprWhenThen supports any Hyprland event without requiring specific parsing logic. Events follow the format ${TYPE}>>${CONTEXT} as defined in the Hyprland IPC specification.
handler.onmatches against${TYPE}(the event name)handler.whenregex pattern matches against${CONTEXT}(the event data)
Common Event Types:
windowtitlev2- Window title changesopenwindow- New window opensclosewindow- Window closesworkspace- Workspace changesfocusedmon- Monitor focus changesactivewindow- Active window changesfullscreen- Fullscreen state changesmonitorremoved/monitoradded- Monitor connection changescreateworkspace/destroyworkspace- Workspace lifecyclemoveworkspace- Workspace moves between monitors
Examples:
# Window management
[[handler]]
on = "openwindow"
when = "firefox"
then = "hyprctl dispatch workspace 2"
# Monitor events
[[handler]]
on = "monitoradded"
when = "(.*)"
then = "notify-send \"Monitor connected: $REGEX_GROUP_1\""
# Custom events (future-proof)
[[handler]]
on = "monitordisabled" # hypothetical future event
when = "(.*),(.*)" # capture name and description
then = "notify-send \"Monitor disabled: $REGEX_GROUP_1 ($REGEX_GROUP_2)\""For a complete list of current events, see the Hyprland IPC documentation.
Use regex capture groups in your commands:
$REGEX_GROUP_0- Full matched string$REGEX_GROUP_1- First capture group$REGEX_GROUP_2- Second capture group- etc.
The environment for the commands is the same as the one that the service is running in, plus all the above variables from pattern matching.
Control execution order for related events by using routing keys. Events with the same routing key are processed serially:
# All events for the same window address are processed in order
[[handler]]
on = "windowtitlev2"
when = "(.*),.*"
then = "echo 'Window title changed: $REGEX_GROUP_1'"
routing_key = "$REGEX_GROUP_1" # Window addressYou can use any known environment variables in the routing_key or a plain string.
Omitting the routing_key results in random worker allocation.
Some of these can be achieved with pure hyprland configuration. If you want to see a more comprehensive example that can't be achieved purely by hypr config, see how to float a window that changes title runtime.
# Auto-float specific windows
[[handler]]
on = "openwindow"
when = "pavucontrol|calculator"
then = "hyprctl dispatch togglefloating"
# Move Discord to workspace 9
[[handler]]
on = "openwindow"
when = "discord"
then = "hyprctl dispatch movetoworkspacesilent 9"# Follow Firefox windows to their workspace
[[handler]]
on = "windowtitlev2"
when = "(.*),.*Mozilla Firefox"
then = "hyprctl dispatch focuswindow address:0x${REGEX_GROUP_1}"# Notify when specific apps open
[[handler]]
on = "openwindow"
when = "(.*)"
then = "notify-send 'App opened' \"$REGEX_GROUP_1\""# Process all window title changes for the same window in order
[[handler]]
on = "windowtitlev2"
when = "(.*),.*"
then = "echo \"Processing window $REGEX_GROUP_1\" >> /tmp/window.log"
routing_key = "$REGEX_GROUP_1"HyprWhenThen is an automation tool that listens to Hyprland events and executes actions based on configured rules.
Usage:
hyprwhenthen [command]
Available Commands:
completion Generate the autocompletion script for the specified shell
help Help about any command
run Start the HyprWhenThen service
validate Validate configuration file
Flags:
--config string Path to configuration file (default "$HOME/.config/hyprwhenthen/config.toml")
--debug Enable debug logging
-h, --help help for hyprwhenthen
-v, --version version for hyprwhenthen
Use "hyprwhenthen [command] --help" for more information about a command.
Start the HyprWhenThen service to listen for Hyprland events and execute configured actions.
Usage:
hyprwhenthen run [flags]
Flags:
-h, --help help for run
--queue int Events are queued for each worker, this defines the queue size; the dispatcher will wait for a free slot when the worker is running behind (default 10)
--workers int Number of background workers (default 2)
Global Flags:
--config string Path to configuration file (default "$HOME/.config/hyprwhenthen/config.toml")
--debug Enable debug logging
If you want to process all events serially you could either give all of them the same routing_key or run
the binary with --workers 1. The latter ensures that only 1 event is processed at any given time.
Validate the syntax and structure of the HyprWhenThen configuration file.
Usage:
hyprwhenthen validate [flags]
Flags:
-h, --help help for validate
Global Flags:
--config string Path to configuration file (default "$HOME/.config/hyprwhenthen/config.toml")
--debug Enable debug logging
For production use, it's recommended to run HyprWhenThen as a systemd user service. This ensures automatic restart on failures and proper integration with session management.
Important: Ensure you're properly pushing environment variables to systemd.
If you run Hyprland under systemd, setup is straightforward.
Create ~/.config/systemd/user/hyprwhenthen.service:
[Unit]
Description=HyprWhenThen - React to hypr events
After=graphical-session.target
Wants=graphical-session.target
PartOf=hyprland-session.target
[Service]
Type=exec
ExecStart=/usr/bin/hyprwhenthen run
Restart=on-failure
RestartSec=5
[Install]
WantedBy=hyprland-session.targetEnable and start the service:
systemctl --user daemon-reload
systemctl --user enable hyprwhenthen
systemctl --user start hyprwhenthenYou can run it on boot and rely on automatic restarts, e.g.:
[Unit]
Description=HyprWhenThen - React to hypr events
After=default.target
[Service]
Type=exec
ExecStart=/usr/bin/hyprwhenthen run
Restart=on-failure
RestartSec=5
[Install]
WantedBy=default.targetThe service will restart until Hyprland is ready and environment variables are properly propagated.
You can also add a custom systemd target that will be started by Hyprland (see this example), e.g., start a custom graphical-session target in your hyprland config:
exec-once = systemctl --user start hyprland-custom-session.target
Then:
❯ cat ~/.config/systemd/user/hyprland-custom-session.target
[Unit]
Description=A target for other services when hyprland becomes ready
After=graphical-session-pre.target
Wants=graphical-session-pre.target
BindsTo=graphical-session.targetAnd:
❯ cat ~/.config/systemd/user/hyprwhenthen.service
[Unit]
Description=Run hyprwhenthen daemon
After=hyprland-custom-session.target
PartOf=hyprland-custom-session.target
StartLimitBurst=60
StartLimitIntervalSec=5
[Service]
Type=exec
ExecStart=/home/fmikina/.bin/hyprwhenthen run
Restart=on-failure
RestartSec=5
[Install]
WantedBy=hyprland-custom-session.targetHyprWhenThen uses a multi-worker architecture for concurrent event processing.
- Events without routing keys are distributed randomly across workers
- Events with the same routing key are processed serially by the same worker
- asdf (for development environment)
# Set up development environment
make dev
# Run tests
make test/integration
# Build binary
make build/test
# Lint and format
make lint
make fmt- Hyprland: Version with IPC support
MIT License - see LICENSE for details.
- Pyprland - Full-featured Python automation framework with plugins for Hyprland window management, scratchpads, and workspace management
- Shellevents - Lightweight bash script that invokes shell functions in response to Hyprland socket2 events
- hyprevents - Enhanced fork of shellevents with additional features and improvements
- Regex pattern matching: Flexible filtering with capture groups for dynamic actions
- Concurrent processing: Multi-worker architecture for high-performance automation
- Minimal dependencies: Single binary with no runtime dependencies
- Events processing ordering: You can define ordering dependencies
- Run any script: It is up to the user what to run, can be any script, the event context can be automatically captured