Commit graph

153 commits

Author SHA1 Message Date
Khoa Vo
3ce274c9ae fix: remove space split — paste entire text via clipboard at once
- Splitting spaces into separate uinput events caused them to arrive
  after the user's next keystrokes, resulting in 'thịtrâm' (no space).
- Now paste entire text including spaces via clipboard in one operation.
- Trailing spaces may be trimmed by some apps — but this is rarer than
  the timing-induced space loss from split injection.
2026-06-26 16:29:02 +07:00
Khoa Vo
eb7960cc77 fix: move send_enter to impl UinputInjector block 2026-06-26 16:24:08 +07:00
Khoa Vo
b40c615583 fix: Enter key not sent via uinput, add send_enter helper
- \n char had no keycode mapping — now sends KEY_ENTER via uinput
- Fixed in both ASCII path and trailing-ASCII-after-unicode path
- Enter now works on single press (was being silently consumed)
2026-06-26 16:23:04 +07:00
Khoa Vo
ebfff3db11 fix: skip auto-repeat only (value=2), not real key presses
- skip_count=3 applied only to auto-repeat events, never to press/release
- Prevents rrrrrrrrr while letting spacebar and real typing through
- Reverted broken drain approach that corrupted source file
2026-06-26 16:16:09 +07:00
Khoa Vo
4c9acfe772 fix: skip auto-repeat pile-up after injection, prevent stuck keys
- After each Unicode injection, skip next 10 events (auto-repeat backlog)
- Prevents 'rrrrrrrrrrrrrr' and '555555' from auto-repeat during injection delay
- Also fixes Ctrl+V/Ctrl+C clipboard conflict during injection window

CHANGELOG: document v0.1.1 Telex fixes, injection improvements, AppImage flags
2026-06-26 15:54:03 +07:00
Khoa Vo
12c18b6904 docs: update CHANGELOG for v0.1.1 2026-06-26 15:49:57 +07:00
Khoa Vo
9dfd86248d fix: Telex 'r' (hỏi) consumed as tone key even with no vowel
- Tone keys (f,s,r,x,j) now only apply when composition has a vowel
- Without a vowel, they fall through to normal character append
- Fixes 'r' disappearing in words like 'trời', 'trâm', 'trảm'
- Added test_telex_r_as_normal_char covering 4 scenarios
- Also: 15ms delay between clipboard paste and trailing uinput chars
2026-06-26 15:48:58 +07:00
Khoa Vo
0028c4809f fix: add --quit and --restart flags to AppRun
- --quit: stops daemon, uinputd, xrecord, and tray
- --restart: stops all then re-launches
- GUI launch without tray shows zenity dialog with quit instructions
2026-06-26 15:41:42 +07:00
Khoa Vo
4374d3a804 fix: Telex spacing timing, add --update self-updater, add Telex engine tests
- Add 15ms delay between clipboard paste and trailing uinput ASCII
- Add 3 new Telex tests (Tuaans→Tuấn, nguyeenx→nguyễn, gios→gió)
- AppRun: --update flag downloads latest AppImage from GitHub releases
2026-06-26 15:38:00 +07:00
Khoa Vo
42a0dad026 fix: Telex mode broken — is_vn_control_key consumed normal letters a/e/o/d/u
Only pure control keys (f,s,r,x,j,w) should be consumed silently.
Base letters used in double-letter marks (aa→â, ee→ê, etc.)
are normal typing keys that must be forwarded when no mark triggers.
Removed uppercase variants too — consolidated with to_ascii_lowercase().
2026-06-26 15:32:13 +07:00
Khoa Vo
ac7d5c24ec docs: update README and CHANGELOG for v0.1.0 overhaul
- README: update architecture diagrams, feature table, VNI key table
- CHANGELOG: document engine rewrite, injection overhaul, bug fixes
2026-06-26 15:22:07 +07:00
Khoa Vo
d4102088b8 fix: X11 key lookup, bamboo engine port, uinput injection overhaul
- 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
2026-06-26 15:20:03 +07:00
Khoa Vo
ea5df93bce fix: aggressive drain to prevent feedback loop + clean up debug logging
- 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
2026-06-26 11:45:57 +07:00
Khoa Vo
44d1b0a1d2 fix: XRecordInterceptData layout and data_len check
- Fixed struct to match C: id_base(u64), server_time(u64), client_seq(u64),
  category(i32), client_swapped(i32), data(ptr), data_len(u64)
- data_len is in 4-byte units, not bytes — keyboard events have data_len=1
- Added XRECORD_FROM_SERVER category check
- Removed re-grab logic from event loop
2026-06-26 10:16:58 +07:00
Khoa Vo
d1a5f36606 fix: remove XGrabKeyboard from XRecord path — it blocks event delivery
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().
2026-06-26 10:13:27 +07:00
Khoa Vo
7898768141 debug: add key event logging to trace XRecord flow 2026-06-26 10:08:13 +07:00
Khoa Vo
3510cd7384 fix: correct XRecordRange layout (32 bytes, device_events at offset 18)
- 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
2026-06-26 10:05:22 +07:00
Khoa Vo
12ac8b0c24 fix: XRecordRange struct layout — client_spec (4 bytes) before device_events 2026-06-26 09:59:29 +07:00
Khoa Vo
e35c034157 fix: replace XGrabKeyboard capture with XRecord extension
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.
2026-06-26 09:52:04 +07:00
Khoa Vo
f87e37ebff fix: XEvent was a struct but X11 defines it as a union — all fields at offset 0
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.
2026-06-26 09:38:25 +07:00
Khoa Vo
cca68004ab fix: use select() on X11 fd for reliable event detection
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.
2026-06-26 09:32:47 +07:00
Khoa Vo
d6ded6e706 fix: add XSelectInput on root window so X server delivers key events to our connection
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.
2026-06-26 09:27:15 +07:00
Khoa Vo
82f7c5da9b fix: ensure log dir exists and show daemon PID in AppRun 2026-06-26 09:20:46 +07:00
Khoa Vo
666f1b400e fix: non-blocking XPending event loop + auto re-grab on grab loss
- 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
2026-06-26 09:17:47 +07:00
Khoa Vo
db3d0cefcd fix: start_enabled=true by default, log daemon to file instead of /dev/null 2026-06-26 09:09:04 +07:00
Khoa Vo
1a83773f59 docs: rewrite README with full architecture, data flow, and Backspace-Replay explanation 2026-06-26 09:01:05 +07:00
Khoa Vo
ebf2753906 fix: AppImage now requires vietc-tray to start, exits with error if missing 2026-06-26 08:57:56 +07:00
Khoa Vo
3ea380f6df fix: AppRun exits cleanly when no tray binary available 2026-06-26 08:50:39 +07:00
Khoa Vo
2ab132dd9a docs: rewrite README with Backspace-Replay, add CHANGELOG.md 2026-06-26 08:42:37 +07:00
Khoa Vo
3858aa955c feat: implement Backspace-Replay pattern for perfect engine sync
- Add Engine::replay_keystrokes() — creates fresh engine and replays
  all keystrokes to compute correct screen output from scratch
- Add Daemon::replay_and_inject() — tracks keystroke history and
  screen output, computes diff (backspaces + new text) on each keypress
- Add Daemon::replay_backspace() — pops from history, replays, diffs
- Handle flush chars (space, period, etc.) separately — commit word,
  type char, clear history
- Add FocusIn/FocusOut detection for engine reset on focus loss
- Add CPU pinning (P-cores 0-3) + nice(-10) priority boost
- Clean up 9 dead code warnings (unused fields, constants, types)
- Add replay_keystrokes tests for Telex, VNI, and backspace
- 255 tests pass (was 252)
2026-06-26 08:40:38 +07:00
Khoa Vo
bb0847a38f X11 capture: proper key tracking, direct clipboard, VNI default
- X11 keyboard capture via XGrabKeyboard (no input group needed)
- Direct X11 clipboard for Unicode injection (no xclip/xdotool dependency)
- Proper KeyPress/KeyRelease tracking (fix Ctrl+C, Alt+Tab, held keys)
- Default input_method=vni, start_enabled=false
- AppImage: bundled xclip, proper keyboard+VN SVG icon
- Deb: Recommends libxtst6, xclip
2026-06-26 07:56:52 +07:00
openhands
38f3bca022 Optimize typing performance and preserve casing on replaced syllables 2026-06-25 19:59:46 +07:00
Khoa Vo
da97e945eb Fix X11 keycodes (+8 offset) and VNI apply_pending flexible backtrack 2026-06-25 09:28:03 +07:00
Khoa Vo
ef458cbd39 Update README: add deb packaging, fix make targets, update Quick Start and Installation 2026-06-25 08:33:18 +07:00
Khoa Vo
85df9cbe63 Add .deb packaging with maintainer scripts and lintian overrides 2026-06-25 08:33:18 +07:00
openhands
2dec02abbd Update README.md with comprehensive About section 2026-06-24 21:43:02 +07:00
vndangkhoa
dd9daebc7c Update About menu item to link to GitHub 2026-06-24 21:20:59 +07:00
vndangkhoa
c7c6b8b7e4 Update README with casing preservation and performance features 2026-06-24 21:11:36 +07:00
vndangkhoa
5f8465783a Optimize typing performance and preserve casing on replaced syllables 2026-06-24 21:11:24 +07:00
vndangkhoa
5d7488ea7c Implement fallback pixel-perfect ARGB32 icon rendering and prioritize tray menu items 2026-06-24 21:04:09 +07:00
vndangkhoa
a5c1ab99c3 Fix modifier key bypass (Ctrl, Alt, Meta) and raw key forwarding when no Vietnamese combination exists 2026-06-24 20:57:40 +07:00
vndangkhoa
fb28f785ce Add auto-updating functionality and About menu item to system tray 2026-06-24 20:55:53 +07:00
vndangkhoa
0e79c05df3 Optimize tray startup disk write and clean up settings UI targets in Makefile 2026-06-24 20:45:23 +07:00
vndangkhoa
9f09af63e0 Update README.md with latest features, dependencies, and architecture 2026-06-24 20:40:33 +07:00
vndangkhoa
f618c3a5b5 Fix typing race conditions with unified channel injection, add persistent logging, and align config schemas 2026-06-24 20:30:14 +07:00
vndangkhoa
42595d4bae Fix backspace in grab mode: inject KEY_BACKSPACE via uinput
When grab is enabled, the physical key is intercepted by evdev grab.
The engine's pop() updates the buffer but the KEY_BACKSPACE was never
injected — send_char('\x08') can't emit a keycode and fell through to
paste_string. Now backspace injects KEY_BACKSPACE press+release via
uinput directly.
2026-06-24 18:04:09 +07:00
vndangkhoa
cdb5eb4812 Improve engine: tone placement, backtrack limit, grab default
- Fix tone placement for uâ, uê, uơ → tone on second vowel (e.g. tuấn, thuế, phương)
- Limit flexible backward scan to 3 chars to prevent cross-syllable modification
- Set grab = true as default config
- Add 9 new engine tests for tone placement and backtrack limit
2026-06-24 17:57:24 +07:00
vndangkhoa
44a4b032a6 Fix injection: skip ydotool for Unicode, use xdotool/xclip
- Simplify inject_replacement_atomic: backspaces via uinput always
- paste_string skips ydotool for non-ASCII text (crashes with 'no matching keycode')
- Use xdotool (X11) or clipboard (xclip) for Vietnamese characters
- Add detailed logging at EVERY fallback step
- Remove dead ydotoold auto-start code (not available in Ubuntu package)
2026-06-24 17:47:09 +07:00
vndangkhoa
0f1f08e79f Fix Unicode injection: ydotoold + xclip fallback + input logging
- Start ydotoold automatically so ydotool can handle Vietnamese chars
- Fix xclip clipboard to run as SUDO_USER when daemon is root
- Add detailed input logging for easier debugging
2026-06-24 17:41:04 +07:00
vndangkhoa
96c8006070 Gitignore runtime status file 2026-06-24 17:33:39 +07:00