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.1

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.1
File Interpreter ABI Platform
j_input-0.1.1-cp311-abi3-win_arm64.whl CPython 3.11 abi3 Windows ARM64 Details
j_input-0.1.1-cp311-abi3-win_amd64.whl CPython 3.11 abi3 Windows x86-64 Details

Total release size: 418.6 kB

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

Download URL j_input-0.1.1-cp311-abi3-win_arm64.whl
Size 206.0 kB
Tags CPython 3.11 Windows ARM64 abi3
SHA-256 checksum
How to use checksums
bfafcbf3bdcfca805564233cae214624bd8c231a8f6b1259e3d262195293daf9
BLAKE2b-256 checksum
How to use checksums
0f8b59d5823b460e4cc2ca95919ee12f9814589c3c22261f8aac637b4b00c7e2
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.1-cp311-abi3-win_amd64.whl

Download URL j_input-0.1.1-cp311-abi3-win_amd64.whl
Size 212.6 kB
Tags CPython 3.11 Windows x86-64 abi3
SHA-256 checksum
How to use checksums
9671ef6f7c05635608dded1eb626b98858de74c49b923cd9b6af82184d0f346f
BLAKE2b-256 checksum
How to use checksums
e81a5927dd4cee87cc81d6ac861775ce4f0ff2e85b6678a13f020eb1fc046b68
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.1 This release

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