Run commands when Hyprland windows open, close, gain focus, or lose focus. Each rule matches windows by class and/or title (both are regular expressions, AND-ed together).
# ~/.config/hyprhook/config.toml
[[rule]]
class = "^gamescope$"
title = "Counter-Strike 2"
on_open = ["obs-cli", "start-recording"]
on_close = ["obs-cli", "stop-recording"]
on_focus = ["hyprctl", "dispatch", "submap", "gaming"]
on_unfocus = ["hyprctl", "dispatch", "submap", "reset"]All four event types are optional — omit any you don't need.
Rules without class or title match all windows.
Each command is an argv list: the first element is the executable, the rest are its arguments.
Use absolute paths when the executable may not be on PATH.
Commands are queued and run one at a time, preventing IPC socket floods on rapid focus changes.
To run multiple commands on one event, define multiple rules with the same matcher.
All class patterns are compiled into a single RegexSet and all title patterns into another at startup.
Each window event performs one automaton pass over the class string and one over the title string,
then intersects the two hit-index sets to find matching rules.
Matching cost is O(text length) regardless of how many rules are configured.
inputs.hyprhook.url = "github:clemenscodes/hyprhook";Add to your packages:
environment.systemPackages = [inputs.hyprhook.packages.${system}.default];Then create ~/.config/hyprhook/config.toml and launch from Hyprland:
# hyprland.conf
exec-once = hyprhookcargo install --git https://github.com/clemenscodes/hyprhookThe flake exposes a NixOS module at nixosModules.default that:
- Generates the TOML config from Nix options
- Wraps the binary so it picks up the generated config automatically
- Exposes
services.hyprhook.finalPackage— the pre-configured binary ready to launch
{inputs, ...}: {
imports = [inputs.hyprhook.nixosModules.default];
services.hyprhook = {
enable = true;
rules = [
{
class = "^gamescope$";
title = "Counter-Strike 2";
on_focus = ["hyprctl" "dispatch" "submap" "gaming"];
on_unfocus = ["hyprctl" "dispatch" "submap" "reset"];
}
];
};
}services.hyprhook.finalPackage is the wrapped binary. Launch it from Hyprland:
exec-once = ${config.services.hyprhook.finalPackage}/bin/hyprhook| Option | Type | Default | Description |
|---|---|---|---|
services.hyprhook.enable |
bool |
false |
Enable the module |
services.hyprhook.package |
package |
flake default | The hyprhook package |
services.hyprhook.finalPackage |
package |
(read-only) | Binary pre-configured with generated TOML |
services.hyprhook.rules |
list of rules |
[] |
Hook rules (see below) |
Each entry in rules:
| Field | Type | Default | Description |
|---|---|---|---|
class |
str | null |
null |
Regex for window class; null matches any |
title |
str | null |
null |
Regex for window title; null matches any |
on_open |
[binary, ...args] | null |
null |
Command when window is created |
on_close |
[binary, ...args] | null |
null |
Command when window is destroyed |
on_focus |
[binary, ...args] | null |
null |
Command when window gains focus |
on_unfocus |
[binary, ...args] | null |
null |
Command when window loses focus |