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
This commit is contained in:
Khoa Vo 2026-06-26 15:22:07 +07:00
parent d4102088b8
commit ac7d5c24ec
2 changed files with 83 additions and 96 deletions

View file

@ -2,35 +2,45 @@
## v0.1.0 (2026-06-26)
Initial release.
Initial release and major overhaul.
### Engine
- Direct Input engine — no pre-edit buffer, no underline, no text duplication
- Telex and VNI input methods
- Flexible diacritic placement (tone/modifier at end of syllable)
- Auto-restore English words on space/ESC
- ESC undo (strip all tones from current word)
- Macro expansion with custom shortcuts
- Casing preservation (titlecase, uppercase)
### Engine (major rewrite)
### Injection
- X11 keyboard capture via XGrabKeyboard (no root, no /dev/input)
- Direct X11 clipboard injection (XSetSelectionOwner + XTest Ctrl+V)
- Bundled xclip + wl-copy for Wayland fallback
- Unified injection channel to prevent ordering race conditions
- **Bamboo engine port** — Replaced custom Telex/VNI state machines with a Rust port of bamboo-core's transformation model. Marks and tones are applied to characters in a composition buffer, with proper tone placement for all Vietnamese diphthongs.
- **Flexible backtracking** — Mark/tone keys scan up to 5 characters backward to find the target vowel. Type the full syllable, then add marks at the end: `nguye6n4``nguyễn`.
- **Smart uo→ươ cluster** — Single `w`/`7` key after a `uo` pair converts both to `ươ`, even through consonants: `chuong7``chương`.
- **Correct tone placement** — Fixed tone positioning for `io` (gió), `uâ` (xuất), `yê` (nguyễn), `oa`/`oe`, `uy`, `iê`, `uô`, `ươ` clusters.
- **Consume stale marks** — VNI/Telex control keys (digits, `f`/`s`/`r`/`x`/`j`/`w`) are consumed silently when they produce no change (e.g., pressing `5` on an already-toned `ạ`).
- **63 focused unit tests** covering Telex, VNI, tone placement, marks, macros, and uppercase.
### Daemon
- **Backspace-Replay pattern** — replays entire keystroke history through a fresh engine on every keypress, eliminating state desync
- FocusIn/FocusOut detection for automatic engine reset
- CPU pinning to P-cores (0-3) + nice(-10) priority boost
- Hot-reload config without restart
- Smart app memory (per-application Vietnamese/English)
- Persistent logging with 10MB rotation
### Injection (major overhaul)
- **Uinput injection** — ASCII and backspace via Linux evdev keycodes (`/dev/uinput`). Correct keycodes per keyboard hardware, no X11 keycode mismatches.
- **Vietnamese Unicode** — Clipboard paste via persistent X11 connection + XTest Ctrl+V. Text is split only at trailing whitespace/punctuation boundary (no mid-word splitting). Persistent X11 display opened once and reused.
- **Uinput daemon** (`vietc-uinputd`) — Privileged Unix socket server for `/dev/uinput` injection. VMK-style architecture with capability separation. The main daemon communicates via socket, falling back to in-process uinput.
- **X11Injector** uses `XKeysymToKeycode` for Ctrl+V keycodes, adapting to the actual keyboard layout.
### Capture
- **Evdev preferred** — Keyboard capture via `/dev/input/event*` with device grab is now the primary path. More reliable than X11 XRecord.
- **X11 XRecord fallback** — X11 passive monitoring via C helper (`vietc-xrecord`) as fallback when evdev is unavailable.
### Bug Fixes
- **Fix `Xutf8LookupString` signature** — Missing `XIC` parameter caused all keycodes to map to `\0`. Fixed by adding `*mut c_void` as first argument and passing `NULL`.
- **Fix `execute_commands` backspace count** — The X11 path incorrectly passed `grabbed=true`, subtracting 1 from every backspace. Changed to `false` so full backspace count is used.
- **Fix flush backspace overcount**`prev_len + 1` erased one character beyond the word. Fixed to `prev_len`.
- **Fix `apply_mark` char removal** — Removed `pattern.len()` chars from composition, but the current key hadn't been appended yet. Fixed to `pattern.len() - 1`.
- **Fix mark backtrack position** — Marks were applied at the end of composition instead of at the found position. Added position-aware `apply_mark_at`.
### Packaging
- AppImage with bundled xclip (no manual setup needed)
- Debian package with proper conffiles, maintainer scripts, and lintian overrides
- Systemd user service
- AppImage bundles `vietc-uinputd`, `vietc-xrecord`, `xclip`.
- AppRun preserves `LD_LIBRARY_PATH` with system library paths for `dlopen`.
- AppRun auto-starts `vietc-uinputd` via `pkexec`/`sudo` when available.
- Cleaned up `vietc-xrecord` compilation flags (only `-lX11 -lXtst` needed).
### Testing
- 255+ unit tests across engine, protocol, daemon config, and replay
- 63 focused engine tests covering Telex, VNI, marks, tones, macros, casing.
- Removed old auto-generated bulk tests (850+ tests for deprecated engine).

119
README.md
View file

@ -44,12 +44,12 @@ Physical Keyboard
┌──────────────────────────────────────────────────────────────┐
│ Stage 1: KEY CAPTURE │
│ │
X11: XGrabKeyboard intercepts all key events
evdev: /dev/input/event* reads kernel events
evdev: /dev/input/event* grabs keyboard (primary, reliable)
X11: XRecord passive monitoring (fallback)
│ │
│ ┌─────────────┐ ┌──────────────┐ ┌──────────────────┐ │
│ │ X11Capture │ │ evdev grab │ │ FocusIn/FocusOut │ │
│ │ (libX11.so) │ │ (libevdev) │ │ detection │ │
│ │ evdev grab │ │ X11Capture │ │ FocusIn/FocusOut │ │
│ │ (libevdev) │ │ (XRecord) │ │ detection │ │
│ └─────────────┘ └──────────────┘ └──────────────────┘ │
└──────────────────────────────────────────────────────────────┘
@ -61,58 +61,31 @@ Physical Keyboard
│ Ctrl+Space → toggle Vietnamese ON/OFF │
│ Backspace → replay_backspace() │
│ Characters → replay_and_inject(ch) │
│ VNI/Telex control keys → consume when no match │
└──────────────────────────────────────────────────────────────┘
┌──────────────────────────────────────────────────────────────┐
│ Stage 3: BACKSPACE-REPLAY
│ Stage 3: BAMBOO ENGINE
│ │
│ keystroke_history = ['c', 'h', 'a', 'o', 's'] │
│ │ │
│ ▼ │
│ ┌──────────────────────────────────────────────┐ │
│ │ Create FRESH engine │ │
│ │ Replay ALL keystrokes through it │ │
│ │ engine.buffer() = "cháo" ← correct output │ │
│ └──────────────────────────────────────────────┘ │
│ │ │
│ ▼ │
│ screen_output = "cháo" │
│ diff = backspaces(0) + type("cháo") │
│ (or no change if screen already shows "cháo") │
│ Transformation model: keystrokes produce composition │
│ changes. Marks and tones modify existing characters. │
│ Flexible backtracking scans up to 5 chars for vowels. │
│ Smart uo→ươ cluster with backtrack. │
│ Only emits Replace events when output actually changes. │
└──────────────────────────────────────────────────────────────┘
┌──────────────────────────────────────────────────────────────┐
│ Stage 4: OUTPUT COMMANDS
│ Stage 4: KEY INJECTION │
│ │
│ EngineEvent::Replace { backspaces: 4, insert: "cháo" } │
│ │ │
│ ▼ │
│ OutputCommand::Backspace(4) │
│ OutputCommand::Type("cháo") │
└──────────────────────────────────────────────────────────────┘
┌──────────────────────────────────────────────────────────────┐
│ Stage 5: KEY INJECTION │
│ ASCII: direct Linux keycodes via /dev/uinput │
│ Backspace: Linux keycode 14 via uinput │
│ Vietnamese Unicode: clipboard paste + trailing ASCII via │
│ uinput (split only at whitespace/punctuation boundary) │
│ Persistent X11 connection for Ctrl+V (no per-call overhead) │
│ │
│ X11 path: │
│ ┌─────────────────────────────────────────────────────┐ │
│ │ 1. Ungrab keyboard (XUngrabKeyboard) │ │
│ │ 2. Send backspaces via XTestFakeKeyEvent │ │
│ │ 3. Set clipboard via XChangeProperty │ │
│ │ 4. Handle SelectionRequest events │ │
│ │ 5. Send Ctrl+V via XTestFakeKeyEvent │ │
│ │ 6. Regrab keyboard (XGrabKeyboard) │ │
│ └─────────────────────────────────────────────────────┘ │
│ │
│ uinput path (Wayland): │
│ ┌─────────────────────────────────────────────────────┐ │
│ │ 1. Send backspaces via /dev/uinput (EV_KEY 14) │ │
│ │ 2. For ASCII: send keycodes via uinput │ │
│ │ 3. For Unicode: wl-copy + Ctrl+V via uinput │ │
│ └─────────────────────────────────────────────────────┘ │
│ Fallback: vietc-uinputd Unix socket daemon (privileged) │
└──────────────────────────────────────────────────────────────┘
@ -153,17 +126,18 @@ This means:
```
vietc/
├── engine/ # Core Vietnamese composition engine
├── engine/ # Vietnamese composition engine (bamboo-core Rust port)
│ ├── engine.rs # Orchestrator + replay_keystrokes()
│ ├── telex.rs # Telex state machine (688 lines)
│ ├── vni.rs # VNI state machine (593 lines)
│ ├── english.rs # English auto-restore dictionary
│ ├── bamboo.rs # Bamboo engine: transformation model, composition, tone placement
│ ├── input_method.rs # Telex/VNI rule definitions
│ └── spelling.rs # Vietnamese syllable validation
├── protocol/ # Keyboard capture & injection
│ ├── inject.rs # KeyInjector trait
│ ├── x11_capture.rs # XGrabKeyboard + XNextEvent loop
│ ├── x11_capture.rs # XRecord keyboard capture via C helper
│ ├── x11_inject.rs # XTest injection + direct clipboard
│ ├── uinput_monitor.rs # /dev/uinput injection for ASCII + Unicode
│ ├── uinput_client.rs # Unix socket client for vietc-uinputd
│ └── wayland_im.rs # Wayland IM protocol
├── daemon/ # Main daemon process
@ -172,6 +146,9 @@ vietc/
│ ├── app_state.rs # Per-app Vietnamese/English memory
│ └── display.rs # X11/Wayland/compositor detection
├── uinputd/ # Privileged uinput backspace daemon (VMK-style)
│ └── main.rs # Unix socket server for /dev/uinput injection
├── ui/ # System tray icon
│ └── main.rs # Tray + daemon launcher
@ -250,19 +227,19 @@ vietc/
### VNI
| Key | Result |
|-----|--------|
| `a1` | á |
| `a2` | à |
| `a3` | ả |
| `a4` | ã |
| `a5` | ạ |
| `a6` | â |
| `a8` | ă |
| `e6` | ê |
| `o6` | ô |
| `o7` | ơ |
| `u7` | ư |
| Key | Result | Example |
|-----|--------|---------|
| `1` | á (sắc) | `a1``á` |
| `2` | à (huyền) | `a2``à` |
| `3` | ả (hỏi) | `a3``ả` |
| `4` | ã (ngã) | `a4``ã` |
| `5` | ạ (nặng) | `a5``ạ` |
| `6` | â/ê/ô | `a6``â`, `e6``ê`, `o6``ô` |
| `7` | ơ/ư | `o7``ơ`, `u7``ư` |
| `8` | ă | `a8``ă` |
| `9` | đ | `d9``đ` |
Flexible typing: type the full syllable, then add marks/tone keys at the end. Example: `nguye6n4``nguyễn`. The engine scans backward up to 5 characters to find the target vowel.
---
@ -270,18 +247,18 @@ vietc/
| Feature | How It Works |
|---------|-------------|
| **Direct Input** | No pre-edit buffer. Keystrokes instantly become Unicode via XTest/uinput injection |
| **Backspace-Replay** | Replays entire keystroke history in a fresh engine on every keypress — zero state desync |
| **Flexible Placement** | Type tone/modifier at end of syllable (`tranaf` → `trần`) — engine scans backward to find the vowel |
| **Smart Clusters** | `uo``ươ`, `ươ` + `o``uô`, shape modifier overriding (â↔ă, ô↔ơ) |
| **Auto-Restore** | ~250 English words recognized — typing `hello` won't become Vietnamese. Triggered on space/ESC |
| **ESC Undo** | Strip all tones from current word instantly |
| **Direct Input** | No pre-edit buffer. Keystrokes instantly become text via uinput/XTest injection |
| **Bamboo Engine** | Transformation model ported from bamboo-core — composition, marks, tones, flexible backtracking |
| **Flexible Backtrack** | Type tone/modifier at end of syllable (`tranaf` → `trần`). Scans up to 5 chars backward |
| **Smart Clusters** | `uo``ươ` with backtrack (`chuong7` → `chương`) |
| **Tone Placement** | Correct tone positioning for all Vietnamese diphthongs (io→gió, uâ→xuất, yê→nguyễn) |
| **Macro Expansion** | `ko``không`, `dc``được`, custom shortcuts |
| **Casing Preservation** | `SATS` → `SÁT`, `Saa``Sả` — matches your typing pattern |
| **Casing Preservation** | `Tieengs` → `Tiếng`, `TIEENGS``TIẾNG` |
| **App Memory** | Per-app Vietnamese/English state, saved to `overrides.toml` |
| **Hot Reload** | Config changes apply without restart (polls mtime every 1.5s) |
| **Focus Reset** | FocusIn/FocusOut clears engine state — no stale injection on window switch |
| **Focus Reset** | Focus change clears engine state — no stale injection on window switch |
| **CPU Priority** | Pins daemon to P-cores (0-3) + nice(-10) for low-latency input |
| **Uinput Daemon** | Privileged `vietc-uinputd` for clean backspace injection (Unix socket, VMK-style) |
---