Commit graph

148 commits

Author SHA1 Message Date
Khoa Vo
5f0f059139 fix: non-grabbed evdev on X11 — switch to XTest injection for instant correction
On X11, uinput injection has high latency (kernel → libinput → X server)
compared to XTest' which writes events directly into the X11 event queue.
Physical key events reach the app before the uinput backspace correction,
causing the VNI control key digit to flash on screen.

Fix: when non-grabbed mode is entered on X11 (grab failed or ungrab),
replace the uinput injector with X11Injector (XTest). XTest events
are processed synchronously by X server and take effect before
subsequent physical events.
2026-07-04 18:20:53 +07:00
Khoa Vo
6756340cb0 fix: VM keyboard EV_KEY+EV_MSC double-processing in non-grabbed mode
VM keyboards emit both EV_KEY and EV_MSC/MSC_SCAN events for each
physical key press. The non-grabbed evdev loop was processing both,
causing each character to be processed twice by the engine. This
doubled every character in the engine buffer, corrupting the output.

Fix: collect EV_KEY keycodes first, then skip MSC_SCAN events that
map to already-processed EV_KEY keycodes. Only VMs that emit
MSC_SCAN without EV_KEY still process the MSC fallback.

Also removed spammy 'evdev: N device(s) have events after ungrab' log
that flooded the terminal on every poll iteration.
2026-07-04 18:20:53 +07:00
Khoa Vo
ed23d6bc35 fix: X11 injector sends 'u' instead of backspace in paste_via_clipboard
paste_via_clipboard() called send_keycode(22, false) which sends evdev
keycode 22 = KEY_U (letter 'u'), not KEY_BACKSPACE (evdev 14). This
caused garbled output whenever Vietnamese Unicode text was pasted via
clipboard (every VNI correction with accented characters).

send_backspace() had the same wrong evdev keycode.
2026-07-04 18:20:53 +07:00
Khoa Vo
a13c192d65 v0.1.19 2026-07-04 18:20:53 +07:00
Khoa Vo
b06035c216 production: download prebuilt binaries instead of building from source
- install.sh: rewritten to download prebuilt tarball from GitHub releases
  (or fallback to .deb extraction), removing verbose cargo build output
- release.yml: new CI workflow to build & upload tarball on tag push
- uninstall.sh: add systemctl --global daemon-reload after removing service
- daemon/src/main.rs: fix VNI backspace offset in X11 keymap capture path
  (missing +1 adjustment for control keys that reach the app directly)
2026-07-04 18:20:53 +07:00
VietC
143ba5ca58 feat: add website as web/ subfolder 2026-07-04 17:18:22 +07:00
Khoa Vo
0495c7cbd7 debug: add evdev event logging + non-grabbed backspace fix
Some checks failed
Build & Release / Build & test (push) Has been cancelled
Build & Release / Build .deb (push) Has been cancelled
2026-07-02 19:41:34 +07:00
Khoa Vo
6d9e8ba4f9 docs: announce terminal support in Features table + Configuration 2026-07-02 16:25:42 +07:00
Khoa Vo
035aeca997 fix: VNI/Telex control key flicker in non-grabbed mode
When a VNI/Telex control key (e.g. digit 6 for ô, w for â/ê/ô) is
pressed, the engine absorbs it in-place without emitting an event. In
non-grabbed mode the raw character already reached the terminal. Fix:
capture buf_before, compare with buf_after, and synthesize
Backspace(len+1) + Type(buf_after) when the buffer changed.
2026-07-02 15:35:53 +07:00
Khoa Vo
2553f20466 password: call check_password_field() instead of stale cached is_password_field()
The keymap and evdev paths were calling is_password_field() which returns
the cached value from AppStateManager. But check_password_field() (the
fresh AT-SPI2 check) was never called in these paths, so password
detection always returned false — engine remained enabled in password
fields, causing VNI processing of password input.
2026-07-02 14:54:22 +07:00
Khoa Vo
fbe0baf7ab password: reset engine buffer and replay on password field transition
When switching between a text field and a password field within the same
window, the engine buffer and event store retained stale content from the
previous field, causing old text to bleed into the password input.

- Add daemon.engine.reset() + daemon.replay_reset() on window change
- Add same resets when password detection fires (both keymap and evdev
  non-grabbed paths)
- Add same resets on periodic password re-check (XRecord path)
- Add same resets when re-enabling engine after leaving password field
2026-07-02 14:49:35 +07:00
Khoa Vo
6b2b42639f evdev: poll all keyboard devices simultaneously; x11: replace XRecord capture with XQueryKeymap polling
- open_keyboard_device() -> open_keyboard_devices(): returns Vec of all
  keyboard-capable evdev devices instead of just the first one
- run_with_evdev() polls all device FDs via single libc::poll() call
- Each device maintains independent key_state tracking
- Added XQueryKeymap/XLookupString to X11Lib in protocol crate
- X11KeymapCapture: new struct that polls X11 keymap every 10ms via
  XQueryKeymap, diffs consecutive polls for press/release detection,
  and uses XLookupString/Xutf8LookupString for char conversion
- run_with_x11_keymap(): replaces segfaulting XRecord-based run_with_x11
  as the primary X11 fallback path
2026-07-02 14:10:54 +07:00
Khoa Vo
88a64224b6 x11_capture: pass shared_window_class to run_with_x11; fix app change detection
The X11 capture path was calling check_app_change_with X11 window IDs
instead of class names (gnome-terminal-server), corrupting the app
state. Also pass shared_window_class to run_with_x11 so it can use
the correct class for app detection.
2026-07-02 13:41:44 +07:00
Khoa Vo
8d68edb321 daemon: fast grab fallback (300ms) to non-grabbed evdev when grab produces no events
In VM environments, EVIOCGRAB on the AT keyboard device succeeds but
produces no events — the kernel/VM routing prevents event delivery
to the grabber. Previously the daemon waited 30 seconds then exited.

Now: after 3 consecutive 100ms poll timeouts (~300ms) with no events
received, the grab is released and the daemon continues in non-grabbed
evdev mode. In this mode events reach both X (characters appear on
screen) and the daemon simultaneously; the daemon applies backspace
corrections via uinput.

Also removes the 30-second-exit behavior (which locked the keyboard
for 30 seconds unnecessarily) and replaces it with the fast fallback.
2026-07-02 13:41:01 +07:00
Khoa Vo
24f9bc8c7e daemon: fall back to X11 capture when evdev produces no events
When evdev's EVIOCGRAB works (returns success) but no keyboard events
arrive (common in VMs where input bypasses the grabbed device), the
daemon previously exited silently after the 30-second safety timeout.
Now it falls through to X11 XRecord capture as a fallback, which works
reliably in VMs by intercepting keystrokes at the X11 protocol level
rather than the evdev level.

- run_with_evdev no longer uses 'return', so main() continues to X11
  capture after evdev exits (timeout or error)
2026-07-02 13:33:41 +07:00
Khoa Vo
41ecc48b0a daemon: use poll() with 100ms timeout for evdev reads instead of blocking fetch_events
The original fetch_events() call blocked indefinitely on the evdev device.
In VM environments, the grabbed keyboard device may not deliver events
after the initial batch, causing the 30-second safety timeout to trigger
silently — the daemon exits, the grab is released, and subsequent
keystrokes bypass the IME entirely.

Replace with libc::poll() with a 100ms timeout so the event loop stays
responsive. When poll returns 0 (timeout), the loop checks signals, the
30-second grab-safety timeout, and also polls for background window
changes. This ensures the safety timeout actually fires as expected,
and the daemon correctly detects and handles the no-event condition.

Also check for background window class changes during idle periods
(no keypress events) so app detection works consistently.
2026-07-02 13:32:54 +07:00
Khoa Vo
8f8b4abf6d daemon: add diagnostics logging + guard VNI control key consumption behind engine.is_enabled()
- Add 'Event loop started' log at beginning of run_with_evdev
- Add reason for non-interrupted fetch_events errors
- Log each injected key with engine state, character, buffer length, and commands
- Fix VNI control digits being silently consumed when engine is disabled
2026-07-02 13:19:18 +07:00
Khoa Vo
fcd465c2b0 fix: exclude daemon's own sudo ancestor from password detection
When the daemon is started via `sudo vietc-daemon` from a terminal,
is_sudo_process would see sudo( in the terminal's process tree and
disable the engine, making all keystrokes pass through untransformed.

Now is_sudo_process builds the daemon's ancestor PID chain and skips
any sudo process that is an ancestor of the daemon itself.
2026-07-02 12:12:32 +07:00
Khoa Vo
4baa460562 fix: use xdotool for Unicode injection instead of clipboard paste
On X11 (Linux Mint, Ubuntu), clipboard-based Unicode injection was
failing — backspace was sent but the Vietnamese character never
appeared because xclip paste via Ctrl+V wasn't reliable from a
root uinput process.

Now send_string tries xdotool type first (XTest-based, doesn't touch
the user's clipboard), falling back to clipboard paste only on Wayland
or when xdotool is unavailable.
2026-07-02 12:03:21 +07:00
Khoa Vo
d48bccd531 feat: add test VM setup script for Linux Mint/Ubuntu 2026-07-02 11:56:01 +07:00
Khoa Vo
3ccf243f52 feat: terminal VNI input — force VNI in terminals, remove from bypass_apps
Some checks are pending
Build & Release / Build & test (push) Waiting to run
Build & Release / Build .deb (push) Blocked by required conditions
- Add terminal_apps / terminal_input_method to config
- AppStateManager tracks global vs effective method
- Engine uses effective method (VNI in terminals, global elsewhere)
- Terminals removed from bypass_apps, moved to terminal_apps
- Tray still shows global method (user's setting)
- NOTE: NOTES/terminal-vni.md documents the design
2026-07-02 08:57:17 +07:00
Khoa Vo
7e5281244b docs: update changelog for distro support, roadmap, deps fixes
Some checks failed
Build & Release / Build & test (push) Has been cancelled
Build & Release / Build .deb (push) Has been cancelled
2026-07-01 17:07:40 +07:00
Khoa Vo
473773abf2 docs: add distro support table, fix deps and config typo
Some checks are pending
Build & Release / Build & test (push) Waiting to run
Build & Release / Build .deb (push) Blocked by required conditions
2026-07-01 17:05:41 +07:00
Khoa Vo
a09ba8ed63 chore: remove RELEASE_CHECKLIST.md, add roadmap to README
Some checks are pending
Build & Release / Build & test (push) Waiting to run
Build & Release / Build .deb (push) Blocked by required conditions
2026-07-01 16:43:46 +07:00
Khoa Vo
d6e1f4d89c uninstall script supports curl pipe, update README with GitHub/Forgejo commands
Some checks are pending
Build & Release / Build & test (push) Waiting to run
Build & Release / Build .deb (push) Blocked by required conditions
2026-07-01 16:38:46 +07:00
Khoa Vo
83c3e3d1fa simplify install/uninstall scripts, update README
Some checks are pending
Build & Release / Build & test (push) Waiting to run
Build & Release / Build .deb (push) Blocked by required conditions
2026-07-01 16:35:56 +07:00
Khoa Vo
7a8f409d20 docs: rewrite README and CHANGELOG for v0.1.7 release
Some checks failed
Build & Release / Build & test (push) Has been cancelled
Build & Release / Build .deb (push) Has been cancelled
2026-07-01 16:25:36 +07:00
Khoa Vo
a9844221a7 Revert "debug: trace space handling in grab mode"
Some checks are pending
Build & Release / Build & test (push) Waiting to run
Build & Release / Build .deb (push) Blocked by required conditions
This reverts commit efed6f7e30.
2026-07-01 16:17:47 +07:00
Khoa Vo
efed6f7e30 debug: trace space handling in grab mode
Some checks are pending
Build & Release / Build & test (push) Waiting to run
Build & Release / Build .deb (push) Blocked by required conditions
2026-07-01 16:11:45 +07:00
Khoa Vo
3c12aa3233 fix: auto-load uinput kernel module in injector
Some checks are pending
Build & Release / Build & test (push) Waiting to run
Build & Release / Build .deb (push) Blocked by required conditions
2026-07-01 16:06:00 +07:00
Khoa Vo
58ff9e145e fix: handle EINTR in evdev loop, silence SelectionRequest log spam
Some checks are pending
Build & Release / Build & test (push) Waiting to run
Build & Release / Build .deb (push) Blocked by required conditions
2026-07-01 16:01:15 +07:00
Khoa Vo
db140c3ca6 fix: use sigaction without SA_RESTART so Ctrl+C interrupts blocking fetch_events
Some checks are pending
Build & Release / Build & test (push) Waiting to run
Build & Release / Build .deb (push) Blocked by required conditions
2026-07-01 15:56:34 +07:00
Khoa Vo
9e073714f1 fix: non-blocking evdev poll (200ms timeout) so Ctrl+C ungrabs reliably
Some checks are pending
Build & Release / Build & test (push) Waiting to run
Build & Release / Build .deb (push) Blocked by required conditions
2026-07-01 15:53:25 +07:00
Khoa Vo
ffd0bc26c8 debug: add keystroke tracing for space forwarding in grab mode
Some checks are pending
Build & Release / Build & test (push) Waiting to run
Build & Release / Build .deb (push) Blocked by required conditions
2026-07-01 15:31:56 +07:00
Khoa Vo
e7b7864937 fix: revert non-grab mode to process_key with +1 backspace for control keys
Some checks are pending
Build & Release / Build & test (push) Waiting to run
Build & Release / Build .deb (push) Blocked by required conditions
2026-07-01 15:02:17 +07:00
Khoa Vo
3612939643 fix: non-grab mode uses event sourcing (replay_and_inject) to avoid double-letter race conditions
Some checks are pending
Build & Release / Build & test (push) Waiting to run
Build & Release / Build .deb (push) Blocked by required conditions
2026-07-01 13:42:02 +07:00
Khoa Vo
82d0796059 fix: non-grabbing mode double-injection — extra backspace for control keys on screen
Some checks are pending
Build & Release / Build & test (push) Waiting to run
Build & Release / Build .deb (push) Blocked by required conditions
2026-07-01 13:34:11 +07:00
Khoa Vo
7fe03b7f44 fix: detect Wayland window switches via class change (not just X11 window ID)
Some checks are pending
Build & Release / Build & test (push) Waiting to run
Build & Release / Build .deb (push) Blocked by required conditions
2026-07-01 13:28:18 +07:00
Khoa Vo
19ee25784d fix: add SIGINT/SIGTERM handler to release keyboard grab before exit
Some checks are pending
Build & Release / Build & test (push) Waiting to run
Build & Release / Build .deb (push) Blocked by required conditions
2026-07-01 13:21:50 +07:00
Khoa Vo
36a6426894 fix: disable auto-restore by default (fixes space consumption on valid Vietnamese)
Some checks are pending
Build & Release / Build & test (push) Waiting to run
Build & Release / Build .deb (push) Blocked by required conditions
2026-07-01 13:11:51 +07:00
Khoa Vo
63c495894e fix: GNOME Shell D-Bus queries via gdbus subprocess as original user
Some checks are pending
Build & Release / Build & test (push) Waiting to run
Build & Release / Build .deb (push) Blocked by required conditions
2026-07-01 13:05:28 +07:00
Khoa Vo
e025ead244 fix: add GNOME Shell D-Bus PID query for Wayland password detection
Some checks are pending
Build & Release / Build & test (push) Waiting to run
Build & Release / Build .deb (push) Blocked by required conditions
2026-07-01 12:59:00 +07:00
Khoa Vo
ddf9f34ad0 fix: add process-based sudo/passwd detection for terminal password prompts
Some checks are pending
Build & Release / Build & test (push) Waiting to run
Build & Release / Build .deb (push) Blocked by required conditions
2026-07-01 12:52:07 +07:00
Khoa Vo
f77b7ea682 fix: recover DBUS_SESSION_BUS_ADDRESS when running as root for AT-SPI2 password detection
Some checks are pending
Build & Release / Build & test (push) Waiting to run
Build & Release / Build .deb (push) Blocked by required conditions
2026-07-01 12:46:38 +07:00
Khoa Vo
cc05e02559 fix: wl-copy --paste-once for fast clipboard on Wayland/GNOME
Some checks are pending
Build & Release / Build & test (push) Waiting to run
Build & Release / Build .deb (push) Blocked by required conditions
2026-07-01 12:41:56 +07:00
Khoa Vo
800d33e6a7 docs: update CHANGELOG with recent fixes, update test count
Some checks are pending
Build & Release / Build & test (push) Waiting to run
Build & Release / Build .deb (push) Blocked by required conditions
2026-07-01 12:11:14 +07:00
Khoa Vo
7ac73485e4 fix: add xprop/wmctrl fallbacks for window detection when xdotool is not installed
Some checks are pending
Build & Release / Build & test (push) Waiting to run
Build & Release / Build .deb (push) Blocked by required conditions
2026-07-01 12:05:20 +07:00
Khoa Vo
81b483e7ac fix: AT-SPI2 connects to a11y bus, not session bus (password detection was silently failing)
Some checks are pending
Build & Release / Build & test (push) Waiting to run
Build & Release / Build .deb (push) Blocked by required conditions
2026-07-01 11:59:40 +07:00
Khoa Vo
94c08bb0da fix: periodic password field re-check every 30 keystrokes for in-terminal prompts
Some checks are pending
Build & Release / Build & test (push) Waiting to run
Build & Release / Build .deb (push) Blocked by required conditions
2026-07-01 11:51:57 +07:00
Khoa Vo
ff607f0559 fix: double space on Ctrl+Space toggle (flush char forwarded twice when engine disabled)
Some checks are pending
Build & Release / Build & test (push) Waiting to run
Build & Release / Build .deb (push) Blocked by required conditions
2026-07-01 11:34:25 +07:00