- Fix Xutf8LookupString signature (missing XIC param caused all keys to map to \0)
- Port bamboo-core Vietnamese engine to Rust (bamboo.rs, input_method.rs)
- Flexible backtracking for mark/tone keys (scan up to 5 chars back)
- Correct tone placement for io, uâ, yê clusters
- Evdev capture preferred over X11 XRecord (more reliable)
- Uinput injection with correct Linux keycodes
- Vietnamese Unicode via clipboard paste + trailing ASCII via uinput
- Persistent X11 connection for Ctrl+V (no per-call dlopen overhead)
- Consume stale VNI/Telex control keys when no match found
- Fix execute_commands backspace count for evdev grabbing path
- Add vietc-uinputd privileged injection daemon
- AppImage: bundle uinputd, preserve LD_LIBRARY_PATH, fix xrecord build flags
- Remove old generated test files, add 63 focused engine tests
- Add aggressive drain loop in wait_for_event() when SKIP_RECORD_EVENTS
is true: poll 5ms + drain, repeat until quiet (up to 50ms). This closes
the timing gap where injected events arrived after drain_pipe returned
but before the flag was cleared in the next iteration.
- Remove verbose debug eprintln!/log_info from daemon (process_key,
replay, inject, toggle, window change, etc.)
- Add vietc-xrecord.c (C helper with XRecordEnableContext blocking mode)
- Update build-appimage.sh to compile and bundle C helper
XGrabKeyboard on the same display as XRecord breaks event delivery.
XRecord captures events globally without any grab needed.
Also: use XPending() before select() to check Xlib internal buffer,
and add XFlush before XRecordProcessReplies after select().
- XRecordRange is 32 bytes: core_requests(0), core_replies(2),
ext_requests(4), ext_replies(10), delivered_events(16),
device_events(18), errors(20), client_started(24), client_died(28)
- XRecordClientSpec is XID = unsigned long (8 bytes), not int (4 bytes)
- Use XRecordAllClients=3 instead of XRecordCurrentClients=1
XGrabKeyboard returns success but never delivers events to a windowless
client on modern X11. XRecord is the standard way to capture keyboard
events globally — used by xdotool, xbindkeys, and input methods.
Architecture:
- XRecord: captures all keyboard events (no grab needed)
- XGrabKeyboard: still used to block original events from reaching apps
- XTest: injects modified events
Dynamically loads libXtst.so.6 for XRecord functions.
Events flow: XRecord callback → thread-safe queue → daemon event loop.
CRITICAL BUG: XEvent was { _type, _pad[24], data } (data at offset 28)
but in X11 it's a union where ALL variants start at offset 0. This meant
event.data.key.state/keycode read from completely wrong offsets — every
keystroke produced garbage characters.
Fixed by replacing XEvent with a raw [u8; 192] byte buffer and using
event_type() and key() accessor methods that cast from offset 0.
Also fixed same bug in x11_inject.rs.
XPending returned 0 even with active keyboard grab. Using select() on
the X11 connection file descriptor with 100ms timeout to reliably detect
when the X server sends events. Also adds XSelectInput on root window
and XConnectionNumber for the fd.
Without XSelectInput, XGrabKeyboard grabs the keyboard but the X server
never sends KeyPress/KeyRelease events to our connection. Also flushes
after grab to ensure it takes effect.
- Use XPending() to check for events before XNextEvent (non-blocking)
- Add is_grabbed() and has_pending_events() public methods
- Auto re-grab keyboard when grab is silently lost (tray, WM focus)
- Fixes AppImage daemon receiving zero keystroke events