# Global Hotkey Registration and Handling with obs_hotkey Functions in OBS Studio

> Learn how OBS Studio handles global hotkeys using obs_hotkey functions. Discover registration, JSON persistence, and input processing for seamless control.

- Repository: [OBS Project/obs-studio](https://github.com/obsproject/obs-studio)
- Tags: internals
- Published: 2026-03-03

---

**OBS Studio implements a centralized hotkey subsystem in [`libobs/obs-hotkey.c`](https://github.com/obsproject/obs-studio/blob/main/libobs/obs-hotkey.c) that enables global hotkey registration via `obs_hotkey_register_frontend`, persists bindings to JSON, and processes input through a dedicated 40 Hz polling thread.**

The obsproject/obs-studio repository provides a robust global hotkey framework that allows both the core application and third-party plugins to register system-wide keyboard shortcuts. Understanding how to leverage the `obs_hotkey` API is essential for developers extending OBS Studio's functionality.

## How the OBS Hotkey Subsystem Works

The architecture divides responsibilities into three distinct layers: registration, binding persistence, and runtime handling. The public API surface is declared in [`libobs/obs-hotkey.h`](https://github.com/obsproject/obs-studio/blob/main/libobs/obs-hotkey.h), while the core implementation resides in [`libobs/obs-hotkey.c`](https://github.com/obsproject/obs-studio/blob/main/libobs/obs-hotkey.c).

| Layer | Responsibility | Key API | Core Implementation |
|------|----------------|--------|---------------------|
| **Registration** | Create a hotkey object and associate it with a registerer (frontend, encoder, source, etc.) | `obs_hotkey_register_frontend`, `obs_hotkey_register_source` | `obs_hotkey_register_internal` in **libobs/obs-hotkey.c** (lines 189-206) |
| **Binding Persistence** | Load and save key-combination bindings to OBS-style JSON | `obs_hotkey_load_bindings`, `obs_hotkey_save` | Loading helpers in **obs-hotkey.c** (lines 88-100) |
| **Runtime Handling** | Detect key presses, match against registered bindings, and invoke callbacks | `obs_hotkey_inject_event`, `obs_hotkey_thread` | Polling thread and dispatch helpers in **obs-hotkey.c** (lines 1555-1580) |

## Registering Global Hotkeys with obs_hotkey_register_frontend

### The Registration API

To expose a global shortcut to users, call `obs_hotkey_register_frontend` with a unique identifier, human-readable description, and callback function:

```c
obs_hotkey_id id = obs_hotkey_register_frontend(
        "OBSBasic.Screenshot",               // internal identifier
        "Take Screenshot",                   // description shown in UI
        screenshot_cb,                       // callback invoked on press/release
        nullptr);                            // user data passed to callback

```

The returned `obs_hotkey_id` serves as a handle for subsequent operations such as loading bindings or unregistering the hotkey.

### Internal Implementation in obs-hotkey.c

Under the hood, `obs_hotkey_register_frontend` forwards to `obs_hotkey_register_internal` with the `OBS_HOTKEY_REGISTERER_FRONTEND` type. This function, located at lines 189-206 in [`libobs/obs-hotkey.c`](https://github.com/obsproject/obs-studio/blob/main/libobs/obs-hotkey.c), allocates a new `obs_hotkey_t` structure, populates it with the provided metadata, and inserts it into the global hash table `obs->hotkeys.hotkeys`. If saved bindings exist in the current profile context, they are automatically loaded and associated with the new hotkey instance.

## Persisting Hotkey Bindings to JSON

### Loading Bindings with obs_hotkey_load_bindings

The UI widget `OBSHotkeyWidget` calls the atomic update helper to replace bindings safely:

```cpp
obs_hotkey_load_bindings(id,
    combinations.data(), combinations.size());

```

This function acquires the global hotkey lock, removes existing bindings via `remove_bindings`, creates new `obs_hotkey_binding_t` entries through `create_binding`, and emits a `hotkey_bindings_changed` signal. The implementation resides in [`libobs/obs-hotkey.c`](https://github.com/obsproject/obs-studio/blob/main/libobs/obs-hotkey.c) around lines 88-112.

### Saving Configuration with obs_hotkey_save

Saving operates in reverse: `obs_hotkey_save` iterates over all bindings, builds a JSON array via `save_bindings_helper`, and returns the serialized data to the caller (lines 290-314 in [`obs-hotkey.c`](https://github.com/obsproject/obs-studio/blob/main/obs-hotkey.c)). The frontend then writes this JSON to the user’s profile configuration.

## Runtime Handling and the Hotkey Thread

### The 40Hz Polling Loop

OBS runs a dedicated hotkey thread that polls the physical keyboard state at approximately 40 Hz (25 ms intervals) and dispatches events to registered hotkeys:

```c
void *obs_hotkey_thread(void *arg)
{
    while (os_event_timedwait(obs->hotkeys.stop_event, 25) == ETIMEDOUT) {
        if (!lock())
            continue;

        query_hotkeys();          // check current modifier state + pressed keys
        unlock();
    }
    return NULL;
}

```

The `query_hotkeys` function builds a modifier mask (`INTERACT_SHIFT_KEY`, etc.), enumerates every binding via `enum_bindings`, and calls `handle_binding`. This handler decides whether to fire (`press_released_binding`) or release (`release_pressed_binding`) based on current key state and strict modifier settings (lines 1555-1580 in [`obs-hotkey.c`](https://github.com/obsproject/obs-studio/blob/main/obs-hotkey.c)).

### Event Injection with obs_hotkey_inject_event

Plugins can inject synthetic hotkey events programmatically—useful for remote controllers or automation:

```c
obs_hotkey_inject_event(combination, pressed);

```

This walks all bindings and forces them into the pressed state if the supplied combination matches (lines 969-987 in [`obs-hotkey.c`](https://github.com/obsproject/obs-studio/blob/main/obs-hotkey.c)).

## Advanced Callback Routing

OBS can reroute all hotkey callbacks through a central router function, enabling scripts to intercept hotkeys globally:

```c
obs_hotkey_set_callback_routing_func(router_cb, nullptr);
obs_hotkey_enable_callback_rerouting(true);

```

All callbacks now forward to `router_cb` instead of their original handlers. The router receives the hotkey ID and pressed state, allowing the caller to decide whether to forward to the original callback via `obs_hotkey_trigger_routed_callback` (lines 1262-1270 in [`obs-hotkey.c`](https://github.com/obsproject/obs-studio/blob/main/obs-hotkey.c)).

## Complete Code Example: Registering a Screenshot Hotkey

The following pattern demonstrates registering a frontend hotkey that triggers an existing Qt action:

```c
/*-------------------------------------------------------------
 * 1. Define the callback that will be executed on press/release
 *------------------------------------------------------------*/
static void screenshot_cb(void *data, obs_hotkey_id id,
                         obs_hotkey_t *hotkey, bool pressed)
{
    if (!pressed)          // act only on key-down
        return;

    /* Take a screenshot – same as the menu action */
    QAction *act = (QAction *)data;
    QMetaObject::invokeMethod(act, "trigger", Qt::QueuedConnection);
}

/*-------------------------------------------------------------
 * 2. Register the hotkey during application start-up
 *------------------------------------------------------------*/
void register_screenshot_hotkey(void)
{
    QAction *screenshotAction = /* obtain the existing QAction */;
    obs_hotkey_id hk = obs_hotkey_register_frontend(
            "OBSBasic.Screenshot",               // internal name
            "Take Screenshot",                   // description shown in UI
            screenshot_cb,                       // callback
            screenshotAction);                  // user data (Qt action)

    /* Optional: expose the hotkey ID to the UI so it can be edited */
    hotkey_widget->SetId(hk);
}

```

This implementation creates a persistent global shortcut that appears in the OBS Settings → Hotkeys panel, stores its bindings in the user profile, and executes the screenshot action when triggered.

## Key Source Files for Reference

| Path | Role |
|------|------|
| [`libobs/obs-hotkey.h`](https://github.com/obsproject/obs-studio/blob/main/libobs/obs-hotkey.h) | Public declarations (`obs_hotkey_id`, `obs_key_combination_t`, registration APIs). |
| [`libobs/obs-hotkey.c`](https://github.com/obsproject/obs-studio/blob/main/libobs/obs-hotkey.c) | Core implementation: registration, binding storage, hotkey thread, injection, routing. |
| [`frontend/widgets/OBSBasic_Hotkeys.cpp`](https://github.com/obsproject/obs-studio/blob/main/frontend/widgets/OBSBasic_Hotkeys.cpp) | Example of frontend hotkey registration for built-in actions (e.g., screenshot, transition). |
| [`frontend/settings/OBSHotkeyWidget.cpp`](https://github.com/obsproject/obs-studio/blob/main/frontend/settings/OBSHotkeyWidget.cpp) | UI widget that lets the user view/edit bindings; uses `obs_hotkey_load_bindings` & `obs_hotkey_update_atomic`. |
| [`frontend/settings/OBSHotkeyEdit.cpp`](https://github.com/obsproject/obs-studio/blob/main/frontend/settings/OBSHotkeyEdit.cpp) | Handles individual key-combination editing, duplicate detection, and UI feedback. |

## Summary

- **Registration**: Use `obs_hotkey_register_frontend` (or source/encoder variants) to create hotkeys stored in the global `obs->hotkeys.hotkeys` hash table inside [`libobs/obs-hotkey.c`](https://github.com/obsproject/obs-studio/blob/main/libobs/obs-hotkey.c).
- **Persistence**: Bindings are serialized to JSON via `obs_hotkey_save` and restored via `obs_hotkey_load_bindings`, enabling configuration across OBS sessions.
- **Runtime**: A dedicated thread (`obs_hotkey_thread`) polls the keyboard at 40 Hz, invoking `query_hotkeys` and `handle_binding` to match pressed keys against registered combinations.
- **Injection**: Synthetic events can be triggered programmatically using `obs_hotkey_inject_event` for automation or remote control scenarios.
- **Routing**: Advanced use cases can intercept all hotkey traffic via `obs_hotkey_set_callback_routing_func` to implement custom filtering or scripting layers.

## Frequently Asked Questions

### How do I register a global hotkey for a frontend action in OBS Studio?

Call `obs_hotkey_register_frontend` with a unique identifier, human-readable description, callback function, and optional user data. This function, defined in [`libobs/obs-hotkey.c`](https://github.com/obsproject/obs-studio/blob/main/libobs/obs-hotkey.c), allocates an `obs_hotkey_t` structure and inserts it into the global registry, making the shortcut available in Settings → Hotkeys.

### What is the difference between obs_hotkey_register_frontend and obs_hotkey_register_source?

`obs_hotkey_register_frontend` creates hotkeys associated with the main application UI (stored with `OBS_HOTKEY_REGISTERER_FRONTEND`), while `obs_hotkey_register_source` attaches hotkeys to specific OBS sources (scenes, audio inputs, etc.). Both ultimately call `obs_hotkey_register_internal` but tag the hotkey with different registerer types for organizational and cleanup purposes.

### How are hotkey bindings saved and loaded across OBS Studio sessions?

Bindings persist through `obs_hotkey_save`, which serializes active combinations to JSON, and `obs_hotkey_load_bindings`, which restores them from the profile configuration. The UI layer in [`frontend/settings/OBSHotkeyWidget.cpp`](https://github.com/obsproject/obs-studio/blob/main/frontend/settings/OBSHotkeyWidget.cpp) invokes these functions when the user modifies key combinations in the settings dialog, ensuring changes survive application restarts.

### Can plugins inject synthetic hotkey events programmatically?

Yes, plugins can call `obs_hotkey_inject_event` to simulate key presses without physical keyboard input. This function iterates through all registered bindings and forces matching combinations into the pressed state, enabling automation, remote control integrations, or scripted testing of hotkey callbacks.