Skip to main content

j-input

Windows global input for Python, implemented in Rust.

One native thread owns the low-level mouse and keyboard hooks and a foreground-window event hook. Every hook callback runs a plain state machine and decides on the spot whether to swallow the event; it never calls into Python and never waits for the GIL. A busy Python main thread therefore cannot stall the system's mouse or keyboard. Only the few events the application cares about are queued for Python.

import inputhub

hub = inputhub.Hub()                                   # one per process
hub.configure_gestures([(["ctrl"], "left"), (["win"], "right")], True)
hub.configure_side_buttons(True, ["x1"])               # swallow Back, report both side buttons
hub.bind_hotkey("clipboard", ["win"], 0x56)            # take Win+V from the system
hub.watch_wheel("stitch", (0, 0, 1920, 1080))          # wheel events inside this rectangle only
hub.watch_keys("stitch", [0x10, 0xA0, 0xA1])           # Shift
hub.watch_foreground("clipboard")                      # every foreground change, including this process

event = hub.next_event()           # blocks with the GIL released; next_event(100) waits at most 100 ms
# ("gesture", "start", id, x, y) / ("moved", id) / ("side", "x1") / ("hotkey", "clipboard") /
# ("wheel", watcher, x, y, delta, horizontal) / ("key", watcher, vk, pressed) /
# ("foreground", hwnd) / ("failure", message)

hub.take_position(gesture_id)      # latest cursor position of a drag, consumed once per ("moved", id)
hub.close()                        # a pending next_event() returns None once queued events are drained

Gestures

A gesture binds one or two modifiers to a mouse button. It starts when the button is pressed while exactly those modifiers are held, and finishes when the button is released. The press and release are swallowed as a pair, so the window below never starts a drag. While a gesture is active, wheel events and other clicks are swallowed; Escape or releasing one of its modifiers cancels it. Releasing Win or Alt after a gesture sends a paired unassigned key first, so the Start menu or menu bar does not open. accepts(id) tells whether a queued start or finish is still current after a cancel or a reconfiguration.

A matching gesture takes precedence over a side-button hotkey on the same button.

Gestures accept injected mouse input and keyboard cancellation/release events, allowing remote-control software to drag, cancel with Esc, and release Win/Alt without opening menus. The configured modifier/button combination must still match exactly.

Hotkeys

A hotkey binds modifiers to a non-modifier key, and fires when that key is pressed while exactly those modifiers are held. Unlike RegisterHotKey, it can take combinations the system already owns, such as Win+V. The key's press, auto-repeats and release are swallowed together, and only the press is reported. A key already held before the modifiers is left alone. Releasing Win or Alt afterwards is masked the same way as after a gesture. Injected input is accepted so remote-control shortcuts also work. unbind_hotkey(name) gives the combination back; a release still owed for a swallowed press is swallowed as well.

Hook lifetime

The low-level hooks are installed only while something needs them: enabled gestures or side buttons, a hotkey, a wheel or key watcher, or a swallowed press still waiting for its release. The out-of-context foreground-window hook is installed only while a foreground watcher exists. Foreground events are not filtered; the caller decides which windows count.

inputhub.Engine drives the same state machine without installing hooks, with held keys supplied by the caller; it exists for tests and may be called from any thread.

The distribution is j-input; the module imports as inputhub. Without the default python feature the crate is a plain Rust library.

Metadata

Release files for j-input 0.1.2

For a detailed explanation of source distributions (sdists) and built distributions (wheels), please see the package formats documentation.

Built distributions (wheels)

Table of built distributions (wheels) for j-input 0.1.2
File Interpreter ABI Platform
j_input-0.1.2-cp311-abi3-win_arm64.whl CPython 3.11 abi3 Windows ARM64 Details
j_input-0.1.2-cp311-abi3-win_amd64.whl CPython 3.11 abi3 Windows x86-64 Details

Total release size: 418.2 kB

Release files / j_input-0.1.2-cp311-abi3-win_arm64.whl

Download URL j_input-0.1.2-cp311-abi3-win_arm64.whl
Size 205.3 kB
Tags CPython 3.11 Windows ARM64 abi3
SHA-256 checksum
How to use checksums
e5c7b98ce4aae21b9a01080bd3ae07f3cae60016eea24474c40b6a55cb1be064
BLAKE2b-256 checksum
How to use checksums
70c667c23d2af356c3c8b4b024f5d61dbce73ddc6a7883cbabd69befbc01d1f4
Upload date
Uploaded using Trusted Publishing?
What is trusted publishing?
No
Uploaded via twine/7.0.0 CPython/3.13.14

Release files / j_input-0.1.2-cp311-abi3-win_amd64.whl

Download URL j_input-0.1.2-cp311-abi3-win_amd64.whl
Size 212.9 kB
Tags CPython 3.11 Windows x86-64 abi3
SHA-256 checksum
How to use checksums
7a9b1fb3e6e0b659679302a5ac59235fbd34122167d2362f5a1a23eb6a0a6857
BLAKE2b-256 checksum
How to use checksums
859884d140aa28d2d2eed219e8e69e51293d2befe4b90dc35d76af6056f45ce9
Upload date
Uploaded using Trusted Publishing?
What is trusted publishing?
No
Uploaded via twine/7.0.0 CPython/3.13.14

Release history Release notifications | RSS feed

This release

0.1.2 This release

2 release files

0.1.1

2 release files

0.1.0

2 release files

Anthropic, PBC Visionary sponsor Bloomberg Visionary sponsor Hudson River Trading Visionary sponsor Meta Visionary sponsor NVIDIA Visionary sponsor Microsoft Sustainability sponsor Depot Continuous Integration AWS Cloud computing and Security Sponsor Datadog Monitoring Fastly CDN Google Download Analytics Sentry Error logging StatusPage Status page