Skip to content

fiffeek/hyprwhenthen

Repository files navigation

hyprwhenthen logo

HyprWhenThen


Event-driven automation for Hyprland. HyprWhenThen listens to Hyprland events and executes actions based on configurable rules, enabling dynamic window management and workspace automation.

Demo

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

Documentation

Features

  • 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

Installation

Binary Release

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 | bash

AUR

For 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 -si

Build from Source

Requires 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 install

Quick start

Basic Configuration

Create ~/.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"

Run service

# Start the service
hyprwhenthen run

# Validate configuration
hyprwhenthen validate

# Run with debug logging
hyprwhenthen run --debug

Run under Hyprland

You can run it directly by editing ~/.config/hypr/hyprland.conf:

exec-once = hyprwhenthen run

For a more sophisticated setup, see Running with systemd.

Configuration

General Section

[general]
timeout = "15s"                      # Global timeout for all handlers
hot_reload_debounce_timer = "100ms"  # Debounce time for config reloading, defaults to 1s

Handlers

Each 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 order

Supported Events

HyprWhenThen supports any Hyprland event without requiring specific parsing logic. Events follow the format ${TYPE}>>${CONTEXT} as defined in the Hyprland IPC specification.

  • handler.on matches against ${TYPE} (the event name)
  • handler.when regex pattern matches against ${CONTEXT} (the event data)

Common Event Types:

  • windowtitlev2 - Window title changes
  • openwindow - New window opens
  • closewindow - Window closes
  • workspace - Workspace changes
  • focusedmon - Monitor focus changes
  • activewindow - Active window changes
  • fullscreen - Fullscreen state changes
  • monitorremoved / monitoradded - Monitor connection changes
  • createworkspace / destroyworkspace - Workspace lifecycle
  • moveworkspace - 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.

Template Variables

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.

Routing Keys

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 address

You can use any known environment variables in the routing_key or a plain string. Omitting the routing_key results in random worker allocation.

Examples

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.

Window Management

# 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"

Dynamic Workspace Switching

# Follow Firefox windows to their workspace
[[handler]]
on = "windowtitlev2"
when = "(.*),.*Mozilla Firefox"
then = "hyprctl dispatch focuswindow address:0x${REGEX_GROUP_1}"

Notifications

# Notify when specific apps open
[[handler]]
on = "openwindow"
when = "(.*)"
then = "notify-send 'App opened' \"$REGEX_GROUP_1\""

Serial Processing

# 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"

Command Line Options

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.

Run

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

Processing all events serially

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

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

Running with systemd

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.

Hyprland under 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.target

Enable and start the service:

systemctl --user daemon-reload
systemctl --user enable hyprwhenthen
systemctl --user start hyprwhenthen

Run on boot with automatic restarts

You 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.target

The service will restart until Hyprland is ready and environment variables are properly propagated.

Custom systemd target

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.target

And:

❯ 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.target

Architecture

HyprWhenThen uses a multi-worker architecture for concurrent event processing.

Routing and Concurrency

  • Events without routing keys are distributed randomly across workers
  • Events with the same routing key are processed serially by the same worker

Development

Prerequisites

  • asdf (for development environment)

Building

# Set up development environment
make dev

# Run tests
make test/integration

# Build binary
make build/test

# Lint and format
make lint
make fmt

Requirements

  • Hyprland: Version with IPC support

License

MIT License - see LICENSE for details.

Related Projects

Event Automation Tools

  • 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

Advantages of hyprwhenthen

  • 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

About

A lightweight service to react to hyprland events.

Topics

Resources

License

Stars

Watchers

Forks

Packages

No packages published