Hyprland Plugin System: Architecture, Loading Mechanism, and Runtime Safety

The Hyprland plugin system is a dynamic shared-library infrastructure that allows third-party developers to extend the Wayland compositor at runtime through C-style entry points, registration macros, and fault-isolated event hooks.

The Hyprland plugin system enables modular extensions without modifying core compositor code. Located in the src/plugins directory of the hyprwm/Hyprland repository, this architecture supports runtime loading of .so files, integration of custom window decorations and hyprctl commands, and crash-resistant execution via advanced fault isolation.

Core Components and File Structure

The implementation spans three primary layers in src/plugins/:

  • Plugin System (PluginSystem.hpp and PluginSystem.cpp): Manages dlopen/dlclose operations, lifecycle tracking, and fault recovery through the CPlugin and CPluginSystem classes.
  • Plugin API (PluginAPI.hpp and PluginAPI.cpp): Exposes registration functions and helper macros (e.g., PLUGIN_REGISTER_DECORATION) via the CPluginAPI class.
  • Hook System (HookSystem.hpp and HookSystem.cpp): Provides event interception capabilities through HOOK_REGISTER macros and the CHookSystem dispatcher.

Discovery and Dynamic Loading

Hyprland reads the plugins configuration entry to determine which shared libraries to load. The CPluginSystem::loadPlugin() method initiates the process by creating a CPromise<CPlugin*> that handles asynchronous loading.

The internal loadPluginInternal() function resolves the library path and attempts to open the .so file using dlopen, storing the resulting handle in the m_handle field of the CPlugin instance. It validates that the library exports two mandatory symbols: plugin_init and plugin_deinit. If either entry point is missing or the dlopen call fails, the promise resolves with an error string and the library is rejected before initialization begins.

Initialization and API Registration

Upon successful loading, Hyprland invokes the exported plugin_init() function, passing a pointer to the global CPluginAPI object. This pointer serves as the primary interface for registering custom functionality with the compositor.

extern "C" void plugin_init(CPluginAPI* api) {
    // Register a custom window decoration
    api->registerWindowDecoration("mydecoration", []() {
        return new MyDecoration();
    });
    
    // Register a hyprctl command
    api->registerHyprctlCommand("mycmd", "Shows plugin info", [](CHyprCtlCommand* cmd) {
        hyprlog::info("Plugin command executed");
        return true;
    });
}

Behind these calls, the PLUGIN_REGISTER_DECORATION, PLUGIN_REGISTER_HYPRCTL_COMMAND, and PLUGIN_REGISTER_CONFIG_VALUE macros push function pointers into vectors stored in the owning CPlugin instance—specifically m_registeredDecorations, m_registeredHyprctlCommands, and analogous containers for config values.

Runtime Hook System and Event Interception

The Hook System allows plugins to intercept internal compositor events without modifying core source code. Defined in src/plugins/HookSystem.hpp, it exposes identifiers such as HOOK_WINDOW_CREATED and HOOK_LAYOUT_CHANGED.

Plugins register callbacks using the HOOK_REGISTER macro or direct API calls:

extern "C" void plugin_init(CPluginAPI* api) {
    api->registerHook(HOOK_WINDOW_CREATED, [](CWindow* w) {
        hyprlog::debug("Window {} created, plugin reacting.", w->m_szTitle);
    });
}

When internal events fire, CHookSystem dispatches these callbacks synchronously, allowing plugins to react to window creation, layout changes, and other lifecycle events in real time.

Unloading and Resource Cleanup

When a user disables a plugin or Hyprland shuts down, CPluginSystem::unloadPlugin() invokes the exported plugin_deinit() function. This triggers the cleanup of all callbacks and resources stored in the CPlugin object, followed by dlclose to release the shared library from memory and invalidate the m_handle.

Safety Mechanisms and Fault Isolation

The Hyprland plugin system implements multiple safeguards to prevent unstable plugins from crashing the compositor:

  • Fault Recovery: Each plugin call is wrapped with setjmp/longjmp using a jmp_buf stored in m_pluginFaultJumpBuf. If a plugin throws an unrecoverable C++ exception or segfaults, the system jumps back to the saved context rather than propagating the error to the main process.
  • Permission Management: The DynamicPermissionManager enforces capability restrictions. The boolean flag m_allowConfigVars specifically controls whether a plugin may register custom configuration variables, preventing unauthorized configuration pollution.
  • Memory Safety: All plugin-related objects use Hyprutils smart pointers (UP<>, WP<>, SP<>) for automatic reference counting and deterministic lifetime management.

Summary

  • The Hyprland plugin system dynamically loads .so files via dlopen using the CPluginSystem class defined in src/plugins/PluginSystem.cpp.
  • Entry points plugin_init and plugin_deinit are mandatory; plugin_init receives a CPluginAPI* pointer for registering decorations, commands, and config values.
  • Registration macros like PLUGIN_REGISTER_DECORATION push callbacks into CPlugin member vectors such as m_registeredDecorations and m_registeredHyprctlCommands.
  • The Hook System (src/plugins/HookSystem.cpp) enables event interception through identifiers like HOOK_WINDOW_CREATED.
  • Fault isolation via setjmp/longjmp and m_pluginFaultJumpBuf prevents plugin crashes from destabilizing the main compositor process.

Frequently Asked Questions

How do I load a plugin in Hyprland?

Add the absolute path to your compiled .so file in the plugins configuration entry within your Hyprland config. When the compositor starts or reloads, CPluginSystem::loadPlugin() automatically validates the library and invokes plugin_init() if the required entry points are present.

What programming language are Hyprland plugins written in?

Plugins must be compiled as C++ shared libraries that export C-linkage symbols using extern "C". The API uses C-style callbacks to ensure Application Binary Interface (ABI) stability across different compiler versions and toolchains.

Can a malfunctioning plugin crash the entire compositor?

No. The system wraps every plugin call with setjmp/longjmp using the m_pluginFaultJumpBuf buffer. If a plugin crashes or throws an uncaught exception, Hyprland catches the fault, prevents the crash from propagating, and safely unloads the offending plugin while keeping the compositor running.

Where are the official Hyprland plugin API headers located?

The public API definitions reside in src/plugins/PluginAPI.hpp, loading logic is implemented in src/plugins/PluginSystem.hpp and .cpp, and hook definitions are found in src/plugins/HookSystem.hpp. These files are maintained in the official hyprwm/Hyprland repository.

Have a question about this repo?

These articles cover the highlights, but your codebase questions are specific. Give your agent direct access to the source. Share this with your agent to get started:

Share the following with your agent to get started:
curl -s "https://instagit.com/install.md"

Works with
Claude Codex Cursor VS Code OpenClaw Any MCP Client

Maintain an open-source project? Get it listed too →