This release includes breaking changes for platform teams planning a safe upgrade.
✓ No known CVEs patched in this version
Affected surfaces
Summary
AI summaryBroad release touches Overview, Linux, macOS, and Client.
Changes in this release
| Type | Severity | Summary | CVE |
|---|---|---|---|
| Performance | Medium |
Reduces Wayland compositor polling timeout from 50 ms to 5 ms for faster pointer‑lock updates. Reduces Wayland compositor polling timeout from 50 ms to 5 ms for faster pointer‑lock updates. Source: llm_adapter@2026-06-08 Confidence: high |
— |
| Performance | Medium |
Eliminates per‑wheel allocation in Linux evdev injection using fixed‑size stack arrays. Eliminates per‑wheel allocation in Linux evdev injection using fixed‑size stack arrays. Source: llm_adapter@2026-06-08 Confidence: high |
— |
| Performance | Medium |
Batches diagonal scroll into a single Windows `SendInput` syscall, halving kernel transitions. Batches diagonal scroll into a single Windows `SendInput` syscall, halving kernel transitions. Source: llm_adapter@2026-06-08 Confidence: high |
— |
| Performance | Medium |
Makes macOS injector thread sleep up to 1 second when idle, reducing wake‑ups from 20 Hz to near‑zero. Makes macOS injector thread sleep up to 1 second when idle, reducing wake‑ups from 20 Hz to near‑zero. Source: llm_adapter@2026-06-08 Confidence: high |
— |
| Performance | Low |
Reduces input latency across all platforms by removing polling delays and unnecessary allocations. Reduces input latency across all platforms by removing polling delays and unnecessary allocations. Source: granite4.1:30b@2026-06-08-audit Confidence: low |
— |
| Performance | Low |
X11 capture now blocks on events instead of busy-waiting, improving efficiency. X11 capture now blocks on events instead of busy-waiting, improving efficiency. Source: granite4.1:30b@2026-06-08-audit Confidence: low |
— |
| Performance | Low |
Server UDP decoding now zero‑copies payloads, avoiding unnecessary allocations. Server UDP decoding now zero‑copies payloads, avoiding unnecessary allocations. Source: granite4.1:30b@2026-06-08-audit Confidence: low |
— |
| Bugfix | Medium |
Fixes phantom key release in games by posting modifier pulses to the session event tap instead of global HID. Fixes phantom key release in games by posting modifier pulses to the session event tap instead of global HID. Source: llm_adapter@2026-06-08 Confidence: high |
— |
| Bugfix | Medium |
Releases held keys on client disconnect, preventing stuck keys after network loss. Releases held keys on client disconnect, preventing stuck keys after network loss. Source: llm_adapter@2026-06-08 Confidence: high |
— |
| Bugfix | Medium |
Tracks all sent keys for release on client disconnect, avoiding duplicate releases. Tracks all sent keys for release on client disconnect, avoiding duplicate releases. Source: llm_adapter@2026-06-08 Confidence: high |
— |
| Bugfix | Medium |
Fixes stuck modifier keys and lost events during X11 auto-repeat detection. Fixes stuck modifier keys and lost events during X11 auto-repeat detection. Source: llm_adapter@2026-06-08 Confidence: low |
— |
| Bugfix | Medium |
Fixes phantom key release during X11 auto-repeat by querying physical keyboard state. Fixes phantom key release during X11 auto-repeat by querying physical keyboard state. Source: llm_adapter@2026-06-08 Confidence: low |
— |
| Bugfix | Medium |
Fixes reconnect hotkey grab failure (`Access` error) in X11 by using efficient polling. Fixes reconnect hotkey grab failure (`Access` error) in X11 by using efficient polling. Source: llm_adapter@2026-06-08 Confidence: low |
— |
| Bugfix | Medium |
Accurately tracks decrypt failures in the server decrypt path. Accurately tracks decrypt failures in the server decrypt path. Source: granite4.1:30b@2026-06-08-audit Confidence: low |
— |
| Bugfix | Low |
Prevents phantom key releases caused by X11 auto-repeat when the event queue is empty. Prevents phantom key releases caused by X11 auto-repeat when the event queue is empty. Source: granite4.1:30b@2026-06-08-audit Confidence: low |
— |
| Bugfix | Low |
Sets correct modifier flags on every injected macOS keyboard event to keep state in sync. Sets correct modifier flags on every injected macOS keyboard event to keep state in sync. Source: granite4.1:30b@2026-06-08-audit Confidence: low |
— |
| Bugfix | Low |
Displays X11 backend error messages (e.g., Access errors) in the client UI for visibility. Displays X11 backend error messages (e.g., Access errors) in the client UI for visibility. Source: granite4.1:30b@2026-06-08-audit Confidence: low |
— |
Full changelog
Spud v1.0.5
Overview
- Reduced input latency across all platforms by removing polling delays and unnecessary allocations.
- X11 capture now blocks on events instead of busy-waiting, and auto-repeat no longer causes phantom key releases.
- Wayland pointer-lock polling timeout reduced from 50ms to 5ms.
- Linux evdev injection no longer allocates per wheel event.
- Windows diagonal scroll now batched into a single syscall.
- macOS injector thread sleeps efficiently when idle, and key-repeat pulses no longer cause phantom releases in games.
- Server UDP path no longer copies unencrypted payloads.
- Client no longer leaks
sent_keysor sends duplicate releases on disconnect.
Changes
Linux (X11)
- Fixed reconnect hotkey grab failure (
Accesserror): When reconnecting, the old X11 input thread was still alive, blocked inwait_for_event()and holding the key grab. The new thread then failed to grab the same hotkey with anAccesserror. The event loop now usespoll_for_event()followed bylibc::poll()on the X11 file descriptor with a 10 ms timeout. This blocks efficiently when idle (no added latency) but allows the old thread to detect when the stream is dropped and exit promptly, releasing the grab before the new connection attempts its own. - Fixed stuck modifier keys / lost events during auto-repeat detection: The X11 backend used
poll_for_event()to peek at the next event after aKeyReleaseto suppress auto-repeat pairs. If the next event was not the expected autorepeatKeyPress(e.g. a mouse motion or another key release), it was silently discarded. This caused modifier key releases to get stuck on the server, which on macOS manifested asFexiting fullscreen and Escape producing a system beep. Non-autorepeat events peeked from the queue are now buffered and re-processed on the next loop iteration instead of being dropped. - Fixed phantom key release during X11 auto-repeat: When X11 auto-repeat is enabled and the event queue is empty after a
KeyRelease, the backend could incorrectly treat the release as real (sending a phantomKeyUp) because the matchingKeyPresshad not yet arrived. It now queries the physical keyboard bitmap viaXQueryKeymapas a fallback: if the key is still physically down, the release is treated as auto-repeat and swallowed. - Ungrab stale hotkey grabs before re-grabbing: As a defensive measure, the X11 backend still calls
ungrab_keyfor all modifier combinations before attempting to grab, handling cases where a previous process crashed without cleaning up.
Linux (Wayland)
- Faster compositor polling: Reduced the
libc::polltimeout in the Wayland event loop from 50ms to 5ms. This was adding unnecessary lag when waiting for pointer lock / confinement state changes from the compositor.
Linux (evdev injection)
- Zero-allocation wheel events:
emit_wheel()previously allocated a temporaryVecon every wheel event. Replaced with fixed-size stack arrays, removing allocator pressure from the hot path.
Windows (injection)
- Batched diagonal scroll: Diagonal scroll (both horizontal and vertical) previously triggered two separate
SendInputsyscalls. Now submitted as a single two-elementSendInputcall, cutting the kernel transition overhead in half.
macOS (injection)
- Idle thread efficiency: The injector worker thread used a fixed 50ms
recv_timeouteven when no keys were held, waking up 20 times per second to do nothing. It now uses a 1-second timeout when idle and only switches to 50ms when keys are actively held for repeat generation. - Faster modifier check: Super+Space detection used an
O(n)iterator scan over held keys. Replaced with twoHashMap::contains_keycalls. - Set modifier flags on every injected keyboard event: Injected
CGEventkeyboard events were posted withCGEventFlags::Null, so macOS had no way to know which modifiers were held. This caused modifier state to desync when events arrived out of order or were dropped. Every injected key event now computes the correct modifier flags from the injector's ownheld_keysstate and sets them viaevent.set_flags()before posting. - Fixed phantom key release in games: The injector pulsed
KeyUp+KeyDownevery 50ms for held keys to reset macOS's accent-menu timer. This caused raw-input games to see a brief key release. The pulse now posts toCGEventTapLocation::Session(application-local) instead ofHID(global). Games monitoring the HID stream no longer see the release blip, while the Text Input system still sees the events and resets the accent-menu timer.
Server (all platforms)
- Zero-copy UDP decoding: The server path for unencrypted UDP packets was copying the payload into a new
Vecbefore decoding. It now decodes directly from the packet buffer slice. - Accurate decrypt failure tracking: Restructured the decrypt path so
decrypt_eventfailures correctly increment the per-session failure counter instead of falling through to a false-positive success path. - Fixed stuck keys after disconnect / lost release events: The
KeyTrackerreturned syntheticreleaseactions for duplicate key-downs (indicating a lost key-up), but the server discarded them withlet _actions = ...and never injected them. Additionally, when a session disconnected, any keys still held in the tracker were not released, so they stayed stuck in the macOS injector's state forever. Both issues are fixed: tracker actions are now injected, and disconnecting triggers arelease_all()flush to the injector.
Client (all platforms)
- Release held keys on disconnect:
DisconnectandConnectionLosthandlers cleared the localpressed_keysset without first sendingKeyUp/MouseButtonrelease events. If the network dropped while keys were held, the server would never receive the releases and those keys would remain stuck. The client now flushes release events before clearing state. - Track all sent keys for release on disconnect: The client only tracked keys currently in
pressed_keysfor the disconnect-release flush. If aKeyUpevent was lost in transit, the key was no longer inpressed_keysand would not be released. Added asent_keysset that tracks every key ever sent asKeyDown; disconnect now releases allsent_keysinstead of justpressed_keys. - Fixed
sent_keysleak causing duplicate releases: Keys were added tosent_keysonKeyDownbut never removed on a successfulKeyUp. This causedrelease_all_held()to send duplicateKeyUpevents for keys that had already been released cleanly. - Display X11 backend errors in UI:
BackendErrormessages from the input backend (e.g. "could not grab hotkey: Access error") were silently ignored. They now appear as a visible error message in the client UI.
Known Issues & Limitations
- The Wayland capture backend still requires compositor support for
pointer-constraintsandrelative-pointerprotocols; minimal compositors may not support these. - macOS input capture requires manual permission grants in System Settings before the app can capture input. Rebuilding or re-signing the binary may revoke previously granted Accessibility permissions.
Weekly OSS security release digest.
The CVE patches and breaking changes that affected production tools this week. One email, every Sunday.
No spam, unsubscribe anytime.
Share this release
About Spud
All releases →Related context
Related tools
Beta — feedback welcome: [email protected]