Commit graph

73 commits

Author SHA1 Message Date
Khoa Vo
be8943bf52 docs: update CHANGELOG for PR #5 merge — ua-horn, spacing, clipboard 2026-06-26 19:22:39 +07:00
vndangkhoa
01ba0c7dde
Merge pull request #5 from vndangkhoa/devin/1782475848-telex-spacing-clipboard
Co-authored-by: Devin AI <158243242+devin-ai-integration[bot]@users.noreply.github.com>
Co-authored-by: vndangkhoa <vonguyendangkhoa@gmail.com>
2026-06-26 19:12:17 +07:00
Devin AI
a5bc2add40 Fix TELEX ua-horn, word-spacing/control-key consumption, and clipboard preservation
Co-Authored-By: vndangkhoa <vonguyendangkhoa@gmail.com>
2026-06-26 12:10:48 +00:00
Khoa Vo
e34fbbc620 docs: update CHANGELOG for v0.1.2
- Flush/spacing fixes, auto-restore English words
- Tone placement for qu/gi/gio and uê/uơ clusters
- Skip auto-repeat, Enter key, clipboard fixes
- 102 tests total across all crates
2026-06-26 17:52:47 +07:00
vndangkhoa
575de7a5a5
Merge pull request #4 from vndangkhoa/devin/1782470733-fix-flush-spacing-v2
The auto-restore merge reintroduced the spacing bug: on every flush char
the engine re-emitted a Replace and the daemon backspaced+retyped the
already-on-screen composed word, racing against the separately-forwarded
flush char and eating spaces (mất sự->mấtsự, đầu ngã xuống->đầungãxuống).

Now flush only backspaces+retypes when auto-restore actually changes the
word (English/invalid Vietnamese -> raw keystrokes). For a normal composed
word the engine returns None and the daemon types only the flush char,
leaving the correct word untouched on screen.

Co-authored-by: Devin AI <158243242+devin-ai-integration[bot]@users.noreply.github.com>
Co-authored-by: vndangkhoa <vonguyendangkhoa@gmail.com>
2026-06-26 17:48:39 +07:00
Devin AI
51949fe02b Fix flush spacing regression while preserving auto-restore
The auto-restore merge reintroduced the spacing bug: on every flush char
the engine re-emitted a Replace and the daemon backspaced+retyped the
already-on-screen composed word, racing against the separately-forwarded
flush char and eating spaces (mất sự->mấtsự, đầu ngã xuống->đầungãxuống).

Now flush only backspaces+retypes when auto-restore actually changes the
word (English/invalid Vietnamese -> raw keystrokes). For a normal composed
word the engine returns None and the daemon types only the flush char,
leaving the correct word untouched on screen.

Co-Authored-By: vndangkhoa <vonguyendangkhoa@gmail.com>
2026-06-26 10:47:43 +00:00
vndangkhoa
4a8d777744
Merge pull request #3 from vndangkhoa/devin/1782470334-fix-flush-spacing
The flush-char handling backspaced and re-typed the already-on-screen
word before/around the forwarded space. In the grabbed-device injection
path this raced against the separately-forwarded space, eating spaces
and merging finished words (e.g. "mất sự" -> "mấtsự",
"đầu ngã xuống" -> "đầungãxuống").

The composed word is already correct on screen, so a non-macro flush
now finalizes state without backspace+retype:
- engine: process_key returns None on flush (macros still Replace)
- daemon replay_and_inject: just types the flush char
- daemon did_flush branch: clears state without retyping

Add regression tests for flush behavior and multi-word spacing.

Co-authored-by: Devin AI <158243242+devin-ai-integration[bot]@users.noreply.github.com>
Co-authored-by: vndangkhoa <vonguyendangkhoa@gmail.com>
Co-authored-by: vndangkhoa <60398697+vndangkhoa@users.noreply.github.com>
2026-06-26 17:44:18 +07:00
vndangkhoa
19589d279a
Merge branch 'main' into devin/1782470334-fix-flush-spacing 2026-06-26 17:44:02 +07:00
Devin AI
bbd273bdd6 Fix spacing bug: stop retyping finished word on flush char
The flush-char handling backspaced and re-typed the already-on-screen
word before/around the forwarded space. In the grabbed-device injection
path this raced against the separately-forwarded space, eating spaces
and merging finished words (e.g. "mất sự" -> "mấtsự",
"đầu ngã xuống" -> "đầungãxuống").

The composed word is already correct on screen, so a non-macro flush
now finalizes state without backspace+retype:
- engine: process_key returns None on flush (macros still Replace)
- daemon replay_and_inject: just types the flush char
- daemon did_flush branch: clears state without retyping

Add regression tests for flush behavior and multi-word spacing.

Co-Authored-By: vndangkhoa <vonguyendangkhoa@gmail.com>
2026-06-26 10:38:54 +00:00
vndangkhoa
141df163e5
Merge pull request #2 from vndangkhoa/devin/1782469883-auto-restore-english
When Vietnamese mode is on, the engine transformed every word including
English (test->tét, cargo->cảgo, status->státu). This wires up the
previously-dead english.rs dictionary and spelling.rs validator so that on
word commit, words that are clearly English or not phonologically valid
Vietnamese are reverted to the raw keystrokes typed. Genuine Vietnamese
(tiếng, việt, quả) is kept. Gated by the existing [auto_restore] enabled
config (default on).

Co-authored-by: Devin AI <158243242+devin-ai-integration[bot]@users.noreply.github.com>
Co-authored-by: vndangkhoa <vonguyendangkhoa@gmail.com>
2026-06-26 17:35:19 +07:00
Devin AI
7569e7e218 feat: auto-restore English words and invalid Vietnamese syllables
When Vietnamese mode is on, the engine transformed every word including
English (test->tét, cargo->cảgo, status->státu). This wires up the
previously-dead english.rs dictionary and spelling.rs validator so that on
word commit, words that are clearly English or not phonologically valid
Vietnamese are reverted to the raw keystrokes typed. Genuine Vietnamese
(tiếng, việt, quả) is kept. Gated by the existing [auto_restore] enabled
config (default on).

Co-Authored-By: vndangkhoa <vonguyendangkhoa@gmail.com>
2026-06-26 10:31:37 +00:00
Khoa Vo
0770cc59cc fix: remove clipboard save/restore — leaked content into text
- xclip -o read triggered unwanted paste in some apps
- Clipboard save/restore was over-engineering the Ctrl+C/V fix
- Simple clipboard write+paste is sufficient
2026-06-26 17:18:04 +07:00
vndangkhoa
a4c83e06b9
Merge pull request #1 from vndangkhoa/devin/1782468642-tone-placement
Co-authored-by: Devin AI <158243242+devin-ai-integration[bot]@users.noreply.github.com>
Co-authored-by: vndangkhoa <vonguyendangkhoa@gmail.com>
2026-06-26 17:13:05 +07:00
Devin AI
6e48d8b2fb fix: correct tone placement for qu/gi onset glides and uê/uơ clusters
Co-Authored-By: vndangkhoa <vonguyendangkhoa@gmail.com>
2026-06-26 10:10:42 +00:00
Khoa Vo
9b8bce4184 fix: save/restore user clipboard around injection
- read_clipboard() saves current content before Vietnamese injection
- Restores after paste so user's Ctrl+C copy isn't overwritten
- Fixes Ctrl+C/V conflict with daemon's clipboard injection
2026-06-26 17:04:47 +07:00
Khoa Vo
118b601d0e fix: update test helper for flush-forward behavior, 67 tests pass
- Engine no longer includes flush char in Replace insert
- Daemon forwards raw flush key after Replace injection
- Test helper simulates this by adding Insert event after Replace
2026-06-26 16:55:57 +07:00
Khoa Vo
758eea45b3 fix: flush char forwarded as raw key — space no longer in clipboard paste
- Engine no longer includes flush char in Replace insert text
- Daemon forwards raw flush key (space/enter/etc) after injection
- Clipboard paste only contains the word, not trailing whitespace
- Fixes 'thịtrâm' → 'thị trâm' (space reliably arrives via raw forward)
2026-06-26 16:54:40 +07:00
Khoa Vo
42e902a501 fix: revert xdotool — broken on US keyboard, backspaces were after type
- xdotool type depends on keyboard layout for Unicode — fails on US layout
- Backspaces were sent AFTER text (wrong order — erased what was just typed)
- Reverted to clipboard paste which is layout-independent
2026-06-26 16:47:23 +07:00
Khoa Vo
540c576591 fix: add xdotool bundling to build-appimage.sh
- Copies xdotool from system or /tmp/xdotool-extract
- Fallback message if not found
- xdotool type is preferred for Unicode injection (no clipboard hacks)
2026-06-26 16:44:42 +07:00
Khoa Vo
01fe7c4f1c feat: bundle xdotool, use xdotool type for Unicode injection
- xdotool types text directly into X11 focus window — no clipboard hack
- More reliable than clipboard paste (no trimming, no timing issues)
- Fallback to clipboard if xdotool not available
- Only on X11 (Wayland uses clipboard fallback)
2026-06-26 16:44:13 +07:00
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