# libuiohook: global keyboard and mouse hooks from userland

> libUIOHook is a C library that installs global keyboard and mouse hooks on Windows, macOS and Linux so an application can be told about system-wide input events, with per-platform backends selected at build time. It suits a developer building a global hotkey utility or an input automation tool, and it is the wrong choice if you need the same feature from a language with an existing cross-platform input library.

**kwhat/libuiohook** — A multi-platform C library to provide global keyboard and mouse hooks from userland.

- Repository: https://github.com/kwhat/libuiohook
- Stars: 613 · Forks: 146
- Language: C
- License: NOASSERTION
- Published: 2026-10-22 · Updated: 2026-10-22 · Language: en
- Canonical page: https://hysenlabs.com/projects/kwhat-libuiohook

## One interface over three different input subsystems

libUIOHook is described as a multi-platform C library to provide global keyboard and mouse hooks from userland, and the readme's title puts it more plainly: cross-platform keyboard and mouse hooking from userland.

The problem it solves is that a global hook, meaning a hook that fires regardless of which application has focus, is implemented completely differently on each desktop. On macOS it means the Carbon event manager or the application services framework or IOKit. On Windows it means a low-level keyboard hook installed into the message loop. On X11 it means the XTest extension, or XRecord, or reading the raw event stream through evdev. A library that hides that behind one C interface is doing a genuinely useful job, because that layer is where the platform-specific code normally lives.

The scope is narrow in a way that is worth stating. This is a hook library, not an automation framework. It delivers input events to your program; it does not decide which keys matter, it does not register global hotkeys for you, and it does not keep a history of what was typed. The feature list of the project topics names global hotkeys, keyboard hooks and mouse hooks, but the repository's own demos are named for what the library does: a hook demo, an async hook demo, an event post demo and a properties demo, with the public interface declared in include/uiohook.h and the function documentation in the man pages.

That combination of a narrow, well-defined job and a per-platform mess of backends is the whole value proposition, and it is also the boundary: if you want hotkey registration, you build it on top.

## Building it, and the dependencies Linux needs

The build is CMake, and the prerequisites are listed plainly: cmake, and gcc, clang or msvc. On Linux there is a longer list of X11 development packages, since the X11 backends link against them:

* libx11-dev
* libxtst-dev
* libxt-dev
* libxinerama-dev
* libx11-xcb-dev
* libxkbcommon-dev
* libxkbcommon-x11-dev
* libxkbfile-dev

The build sequence is a clone, a build directory, a configure and a build:

```bash
$ git clone https://github.com/kwhat/libuiohook
$ cd libuiohook
$ mkdir build && cd build
$ cmake -S .. -D BUILD_SHARED_LIBS=ON -D BUILD_DEMO=ON -DCMAKE_INSTALL_PREFIX=../dist
$ cmake --build . --parallel 2 --target install
```

Two of those flags matter for what you get. BUILD_SHARED_LIBS=ON builds a shared library rather than a static one, and BUILD_DEMO=ON builds the demo applications, which are the fastest way to see whether the library works on your machine before you write any code against it. The install target puts the result under the prefix you named, and the --parallel flag with a core count is the normal way to build it.

The source tree is small and conventional. There is a CMakeLists.txt at the root, src/ for the implementation, include/ for the public interface, demo/ for the sample programs, test/ for the tests, man/ for the manual pages, pc/ for the pkg-config file, and an AUTHORS file. The default branch is named 1.2, which suggests the project organises its work by version branch.

## The configuration table, which is the real documentation

The build options are where the platform knowledge lives, and the table is worth reading in full before you configure anything.

The global options are three. BUILD_DEMO is off by default and turns on the demo applications. BUILD_SHARED_LIBS is on by default and controls whether you get a shared library. ENABLE_TEST is off by default and turns on the testing build.

The macOS options are four frameworks, each on by default except the legacy one. USE_APPLICATION_SERVICES, USE_IOKIT, and USE_OBJC are on, and USE_CARBON_LEGACY is off. The Objc option is described as the obj-c api, which tells you the C library can hand back Objective-C objects, and the Carbon option is the pre-macOS 10.4 mechanism, kept for older systems.

Linux has the most options and they describe the X11 extension set in miniature. USE_EVDEV is on and is the generic input driver, which is the mechanism that reads the raw event stream rather than going through an X extension. USE_XF86MISC is off. USE_XINERAMA, USE_XKB_COMMON, USE_XKB_FILE and USE_XT are on, while USE_XRANDR and USE_XRECORD_ASYNC are off, and USE_XTEST is on.

The pattern to notice is that every option is on or off by default in a way that reflects a preferred backend, and the X11 options are individually switchable because they correspond to separate extensions that may or may not be present on a given distribution. A machine without Xinerama, which is common in a minimal desktop, needs the corresponding option turned off or the link fails.

So the build configuration is not a formality. It is where you declare which parts of each platform you depend on, and a Linux build on a minimal distribution is a configuration exercise rather than a single command.

## Where libuiohook is the wrong choice

The first limitation is the language. This is a C library, so a Python, JavaScript or Rust application that needs global input has to bind to it, and a binding is a per-language project with its own build story and its own release cycle. Many languages already ship a cross-platform input library with the hotkey handling included, and for those the C library is work rather than a shortcut.

The second is what the library does not do. There is no hotkey registration, so a global shortcut means you subscribe to every key event and compare them yourself. There is no filtering, so the callback receives everything the OS delivers, and on a busy machine that is a lot of events to discard. There is no modifier tracking, no key release versus press distinction at the API level as far as the visible documentation shows, and no text or clipboard handling. The demos are named for the raw capabilities, and the public header is the only contract, so anything above the raw event is yours to build.

The third is the platform coverage asymmetry. The macOS and Windows paths are complete, but the Linux path is an X11 story, and X11 is only one of the two display server models in wide use. The configuration table lists nothing for Wayland, and the XTest and XRecord extensions are X11 mechanisms. On a Wayland session, a global hook either does not work or goes through an XWayland compatibility layer where it may be restricted, and the readme does not document a Wayland path.

The fourth is the packaging. The repository has a pkg-config file, which is good news for a Unix build, and a Makefile for Windows under the pc/ directory, but the readme describes only building from source with CMake. There is no package in the usual distributions, so every consumer of the library builds it, and every consumer has to answer the backend configuration questions for their own platform.

The fifth is the release story. The repository publishes no GitHub releases, the default branch is named 1.2, and the last push was on 2026-07-27. The COPYING file at the root and a separate COPYING.LESSER.md suggest a dual licence with an LGPL-style component, so the terms of what you are linking against need reading rather than assuming.

## Conclusion

libuiohook fits a C or C++ developer who needs to observe or intercept system-wide input on more than one desktop platform and is prepared to deal with the platform differences itself, since the library exposes one interface and leaves the backend selection to the build configuration. It is the wrong choice for a Python or JavaScript application that already has a cross-platform input library, and it is the wrong choice if you need event filtering, hotkey registration or modifier tracking, since the library's job ends at delivering raw input events. Before adopting it, read the configuration table and turn off the backends your target does not need, and note that the last push was on 2026-07-27, the default branch is named 1.2, and the repository publishes no tagged releases.

## FAQ

### What does libuiohook provide?

It is a multi-platform C library to provide global keyboard and mouse hooks from userland, with a public interface in include/uiohook.h and man pages for the function documentation. The demos cover a hook demo, an async hook demo, an event post demo and a properties demo.

### How do I build libuiohook?

Install cmake and a compiler, plus the listed X11 development packages on Linux, then clone the repository, create a build directory and run cmake -S .. with BUILD_SHARED_LIBS=ON, BUILD_DEMO=ON and a CMAKE_INSTALL_PREFIX, followed by cmake --build . --parallel 2 --target install.

### Which backends does libuiohook use on macOS and Linux?

On macOS the options are USE_APPLICATION_SERVICES, USE_IOKIT, USE_OBJC and USE_CARBON_LEGACY. On Linux there is a long list of X11 extensions including USE_XTEST, USE_XKB_COMMON, USE_XINERAMA, USE_XKB_FILE and USE_XT, plus USE_EVDEV for the generic input driver, and each can be turned on or off at configure time.

### Does libuiohook handle global hotkey registration for me?

No. The library delivers input events to your program and leaves interpretation to you, so hotkey registration, filtering and modifier tracking are things you build on top of the raw event interface declared in include/uiohook.h.

### What licence is libuiohook released under?

The project records a custom licence that GitHub cannot classify, and the repository carries both a COPYING.md and a COPYING.LESSER.md, so the terms of what you link against need reading directly. The last push was on 2026-07-27, the default branch is named 1.2, and there are no GitHub releases.

## Sources

- [Issues](https://github.com/kwhat/libuiohook/issues)
- [kwhat/libuiohook on GitHub](https://github.com/kwhat/libuiohook)
- [README](https://github.com/kwhat/libuiohook/blob/1.2/README.md)

---

Hysen Labs editorial analysis, written from the project's own repository and release notes. Cite the canonical page: https://hysenlabs.com/projects/kwhat-libuiohook
