# Configuring Hyprland Keybinds and Input Events: A Complete Technical Guide

> Master Hyprland keybinds and input events with this complete guide. Learn to configure binds from hyprland.conf and understand raw input event handling for seamless workflow customization.

- Repository: [Hypr Development/Hyprland](https://github.com/hyprwm/Hyprland)
- Tags: how-to-guide
- Published: 2026-07-26

---

**Hyprland’s input system splits configuration parsing and event handling between the KeybindManager, which stores and dispatches binds from [`hyprland.conf`](https://github.com/hyprwm/Hyprland/blob/main/hyprland.conf), and the InputManager, which translates raw libinput events into compositor actions through a coordinated pipeline in [`src/managers/KeybindManager.cpp`](https://github.com/hyprwm/Hyprland/blob/main/src/managers/KeybindManager.cpp) and [`src/managers/input/InputManager.cpp`](https://github.com/hyprwm/Hyprland/blob/main/src/managers/input/InputManager.cpp).**

Configuring Hyprland keybinds and input events requires understanding how the compositor bridges user configuration with low-level hardware input. This guide examines the actual source code implementation in the hyprwm/Hyprland repository, revealing how keyboard shortcuts, mouse bindings, and special input features are parsed, stored, and executed.

## Architecture Overview

Hyprland delegates input responsibilities between two cooperating managers that handle distinct phases of the input lifecycle.

### The Keybind Manager

The **KeybindManager** ([`src/managers/KeybindManager.cpp`](https://github.com/hyprwm/Hyprland/blob/main/src/managers/KeybindManager.cpp)) serves as the configuration authority. It parses `bind…` entries from the user’s config file, stores them as `SKeybind` objects in `m_keybinds`, tracks modifier state across all keyboards, resolves binding conflicts, and dispatches Lua callbacks when matches occur.

### The Input Manager

The **InputManager** ([`src/managers/input/InputManager.cpp`](https://github.com/hyprwm/Hyprland/blob/main/src/managers/input/InputManager.cpp)) acts as the hardware interface. It receives raw libinput and Wayland events from keyboards, mice, touchscreens, and tablets, translates them into Hyprland-friendly structures, updates global modifier masks, and forwards processed events to the KeybindManager for action resolution.

## How Keybinds Are Created

The journey from configuration text to executable bind follows a four-stage pipeline.

### Configuration Parsing

When Hyprland loads, `CConfigValue` reads the user’s [`hyprland.conf`](https://github.com/hyprwm/Hyprland/blob/main/hyprland.conf) and identifies lines beginning with `bind`.

```cpp
// From ConfigValue.hpp
static auto P = CConfigValue<std::string>("bind");

```

### Bind Registration

For each valid bind line, `CKeybindManager::addKeybind` constructs a `SKeybind` struct, wraps it in a `std::shared_ptr`, and appends it to the `m_keybinds` vector.

```cpp
SP<SKeybind> CKeybindManager::addKeybind(SKeybind kb) {
    const auto KEYBIND = makeShared<SKeybind>(kb);
    m_keybinds.emplace_back(KEYBIND);
    // ...
}

```

### Modifier Mask Resolution

Textual modifiers like `SUPER`, `CTRL`, `ALT`, and `SHIFT` convert to bit-masks via `CKeybindManager::stringToModMask`. This mask is later compared against the current modifier state retrieved from `g_pInputManager->getModsFromAllKBs()`.

```cpp
uint32_t CKeybindManager::stringToModMask(std::string mods) { 
    // Returns XKB modifier mask
}

```

### Key Symbol Resolution

Hyprland supports two key identification methods:

- **Keysym binds**: Names like `Q` or `ENTER` resolve via `xkb_keysym_from_name`
- **Keycode binds**: Raw XKB keycodes prefixed with `code:` (e.g., `code:30`) store the numeric value directly

## Keyboard Event Flow

When you press a key, the compositor processes it through a seven-stage pipeline before deciding whether to consume the event or forward it to the focused client.

1. **Raw Event Reception**: `InputManager::onKeyEvent` receives an `IKeyboard::SKeyEvent` from libinput.

2. **State Validation**: The manager checks if the session is active and whether the keyboard has `allowBinds` disabled. It lazily initializes the XKB translation state via `updateXKBTranslationState`.

3. **Translation**: `xkb_state_key_get_one_sym` converts the hardware scancode to a keysym, while `getModsFromAllKBs()` aggregates modifier states across all connected keyboards.

4. **Internal Shortcuts**: `handleInternalKeybinds` intercepts critical system shortcuts like VT switching (`CTRL+ALT+F1`) before evaluating user binds.

5. **Bind Matching**: The system calls `handleKeybinds(modmask, pressedKey, pressed, ...)`, which iterates over `m_keybinds` checking:
   - Mod mask equality (unless `ignoreMods` is set)
   - Submap matching (`k->submap.name`)
   - Device and tag inclusion (`k->devices`)
   - Multi-key chord status (`mkBindMatches`)
   - Release-only, long-press, and repeat flags

6. **Dispatch**: Upon match, `callBindDispatcher(k)` invokes the bound action, typically through `Config::Lua::mgr()->callLuaFnBind`. This returns an `SDispatchResult` indicating whether to suppress the original event.

7. **Client Forwarding**: `onKeyEvent` returns `!suppressEvent && !mouseBindWasActive`, determining if the key event propagates to the Wayland client.

## Mouse and Axis Handling

Pointer input follows a separate but related path through the InputManager.

### Mouse Movement

`CInputManager::onMouseMoved` updates the cursor position, forwards relative motion via the relative-pointer protocol (`PROTO::relativePointer->sendRelativeMotion`), and calls `mouseMoveUnified`. The unified handler performs hit-testing across layer-shell surfaces and windows, updating pointer focus through `g_pSeatManager->setPointerFocus`.

### Mouse Buttons

`CInputManager::onMouseButton` routes button events to `g_pKeybindManager->onMouseEvent`. If no bind consumes the click, the event forwards to the focused client via `g_pSeatManager->sendPointerButton`.

### Scroll Wheel

`CInputManager::onMouseWheel` normalizes scroll deltas, applies the user-defined `input:scroll_factor`, synthesizes discrete steps when necessary, and dispatches axis events through `g_pSeatManager->sendPointerAxis`.

## Special Bind Features

Hyprland extends basic keybinding with several advanced features implemented directly in the KeybindManager.

### Long-Press Binds

When a key presses, `m_longPressTimer` arms with a configurable timeout. If the timer expires before release, `callBindDispatcher` triggers the stored `m_lastLongPressKeybind`.

### Repeat Handling

After initial press detection, `m_repeatKeyTimer` starts with a period based on the keyboard’s `repeatRate`. Each tick re-invokes `handleKeybinds` for the active bind, enabling volume or brightness adjustments while holding keys.

### Multi-Key Chords

For binds requiring simultaneous keys (`binds: s`), `mkKeysymSetMatches` constructs a bipartite-matching graph between currently pressed keys and the bind’s required set. The system distinguishes between `MK_FULL_MATCH`, partial matches, and no match to determine trigger eligibility.

### Bind Shadowing

Once a bind fires, `shadowKeybinds` disables conflicting binds sharing the same modifier/key combination, preventing accidental double-triggers. Global and transparent handlers remain active during shadowing.

### VT Switching

`handleVT` in [`KeybindManager.cpp`](https://github.com/hyprwm/Hyprland/blob/main/KeybindManager.cpp) intercepts `CTRL+ALT+F[1-12]` combinations (XKB codes `XKB_KEY_XF86Switch_VT_...`) and requests backend terminal switches before user binds are evaluated.

## Configuration Syntax

Hyprland configuration resides at `$XDG_CONFIG_HOME/hypr/hyprland.conf`. A bind declaration follows this pattern:

```

bind = <MODIFIER>, <KEY>, <HANDLER>, <ARGUMENT>

```

- **MODIFIER**: Space or comma-separated list (`SUPER`, `CTRL`, `ALT`, `SHIFT`, `META`, `CAPS`)
- **KEY**: Keysym name (`Q`, `ENTER`) or raw keycode (`code:30`)
- **HANDLER**: Action type (`exec`, `workspace`, `submap`, `global`, `pass`, `mouse`)
- **ARGUMENT**: Handler-specific data (command, workspace ID, or Lua function reference)

### Practical Examples

```conf

# Launch terminal

bind = SUPER, ENTER, exec, alacritty

# Switch to workspace 3

bind = SUPER, 3, workspace, 3

# Multi-key bind (requires both modifiers)

bind = SUPER+SHIFT, Q, exec, hyprctl dispatch killactive

# Long-press notification (hold Super for 800ms)

bind = SUPER, SUPER, longpress, exec, notify-send "Super held!"

# Repeat bind for volume control

bind = NONE, VOLUME_UP, repeat, exec, pactl set-sink-volume @DEFAULT_SINK@ +5%

```

### Lua-Based Binds

Advanced configurations can bind directly to Lua functions:

```conf

# In hyprland.conf.lua

local myFn = function()
    hyprland.notify("Hello from Lua!")
end
hyprland.registerLuaFunction("notifyFn", myFn)

# In hyprland.conf

bind = SUPER, H, exec, lua:notifyFn

```

The `callBindDispatcher` function resolves the `lua:` prefix and invokes `Config::Lua::mgr()->callLuaFnBind` with the stored function index.

## Extending and Debugging Keybinds

### Runtime Inspection

Retrieve current bind states via `hyprctl`:

```bash

# JSON output of all binds and devices

hyprctl devices -j

# Force configuration reload

hyprctl reload

```

### Debug Logging

Enable verbose logging with `debug:log_level = 2` to trace bind execution:

```

[DEBUG] Keybind triggered, calling dispatcher (modmask, keyName, keysym)
[DEBUG] Long press timeout passed, calling dispatcher.

```

### Programmatic Registration

Plugins can register binds at runtime:

```cpp
extern "C" void register_my_bind() {
    SKeybind kb;
    kb.modmask = CKeybindManager::stringToModMask("SUPER");
    kb.key = "F";
    kb.handler = "exec";
    kb.arg = "notify-send \"Plugin active!\"";
    kb.enabled = true;
    g_pKeybindManager->addKeybind(kb);
}

```

## Summary

- **Input architecture**: Hyprland separates concerns between `KeybindManager` (configuration and dispatch) and `InputManager` (hardware abstraction) to handle configuring Hyprland keybinds and input events efficiently.
- **Event pipeline**: Raw libinput events undergo translation (XKB), modifier aggregation, internal shortcut checking, bind matching against `m_keybinds`, and potential suppression before reaching Wayland clients.
- **Advanced features**: Long-press timers, repeat intervals, multi-key matching graphs, and shadowing logic extend basic keybinding capabilities.
- **Configuration methods**: Users define binds in [`hyprland.conf`](https://github.com/hyprwm/Hyprland/blob/main/hyprland.conf) using keysyms or keycodes, with optional Lua function integration via `lua:` prefixes.
- **Debugging tools**: Use `hyprctl devices -j` for inspection, `hyprctl reload` for updates, and `debug:log_level = 2` for detailed event tracing.

## Frequently Asked Questions

### How does Hyprland resolve conflicts when multiple keybinds match?

Hyprland evaluates binds in order using `handleKeybinds`, which checks mod masks, submaps, device constraints, and multi-key states. Once a bind triggers, the `shadowKeybinds` mechanism disables subsequent matches for the same key combination unless they are marked as global or transparent, ensuring deterministic behavior.

### What is the difference between keycode and keysym binds in Hyprland?

Keysym binds (e.g., `Q`, `ENTER`) use `xkb_keysym_from_name` and resolve based on the current keyboard layout, making them layout-dependent. Keycode binds prefixed with `code:` (e.g., `code:30`) use raw XKB scancodes and remain consistent regardless of layout changes, ideal for gaming or layout-switching scenarios.

### How can I debug why a keybind is not working?

Enable debug logging with `debug:log_level = 2` to trace the event flow through `onKeyEvent` and `handleKeybinds`. Verify your bind appears in `hyprctl devices -j` output, check that the correct submap is active, and ensure no shadowed binds are consuming the input before your target bind is evaluated.

### Can I use Lua to create custom keybind handlers?

Yes. Define functions in your Lua configuration using `hyprland.registerLuaFunction`, then reference them in binds with the `lua:` prefix (e.g., `bind = SUPER, X, exec, lua:myFunction`). When triggered, `callBindDispatcher` resolves the function index and executes it through `Config::Lua::mgr()->callLuaFnBind`, allowing complex conditional logic beyond standard dispatchers.