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

This commit is contained in:
Khoa Vo 2026-07-01 16:25:36 +07:00
parent a9844221a7
commit 7a8f409d20
2 changed files with 161 additions and 313 deletions

View file

@ -4,68 +4,88 @@
### Password Auto-Detection
- **AT-SPI2 D-Bus integration**: Queries `org.a11y.atspi.Registry.GetFocus` + `GetRole` to detect password fields (role 62 = `PASSWORD_TEXT`). Automatically disables Vietnamese input when typing into a password field, re-enables when focus moves away.
- **Window-class fallback**: Password dialogs (pinentry, polkit, kwallet, ssh-askpass) are detected via `password_apps` config list.
- **Window-title fallback**: Window titles containing "password", "passphrase", "sudo", "mật khẩu" trigger automatic English mode.
- **AT-SPI2 D-Bus integration**: Queries `org.a11y.atspi.Accessible.GetRole` on the a11y bus (not session bus) to detect password fields. Works in GUI password dialogs and a11y-enabled apps.
- **Process-tree sudo detection**: Scans `pstree` for `sudo`/`passwd` processes — auto-disables Vietnamese when sudo prompts in terminals.
- **Window-title fallback**: Window titles containing "password", "sudo", "mật khẩu" trigger automatic English mode.
- **Window-class fallback**: Known password dialogs (pinentry, polkit, kwallet) detected via `password_apps` config.
- **Periodic re-check**: Re-evaluates password status every 30 keystrokes (catches in-terminal prompts).
### Telex Input Method
- **Telex now fully enabled**: Both VNI and Telex are supported. Switch via Ctrl+Shift hotkey or tray menu "Input Method > Telex / VNI".
- **Method status file** (`~/.config/vietc/method`): Daemon writes the current method so the tray can display it.
- **Telex now fully enabled**: Both VNI and Telex are fully supported. Switch via Ctrl+Shift or tray menu "Input Method > Telex / VNI".
- **Method status file** (`~/.config/vietc/method`): Daemon writes the current method; tray reads it to display.
- **Tray indicator**: Red "VN" for VNI, Blue "TLX" for Telex, Gray "EN" for English mode.
- **Config option**: `toggle_method_key = "shift"` configures the Ctrl+Shift method toggle combo.
- **Config option**: `toggle_method_key = "shift"` configures the method toggle combo.
### GNOME/Wayland Support
- **Native GNOME Shell D-Bus integration**: Queries `org.gnome.Shell.Eval` for focused window class, ID, and title — works on Wayland GNOME where xdotool/xprop are unavailable.
- **Window detection chain**: GNOME Shell D-Bus → wlrctl → xdotool → /proc — ensures window tracking works across all environments.
- **Compositor detection**: Added GNOME/Mutter detection via `pgrep gnome-shell` and `XDG_CURRENT_DESKTOP`.
- **Dependencies**: `dbus` crate (0.9) added for both AT-SPI2 and GNOME Shell D-Bus queries.
- **GNOME Shell D-Bus integration**: Queries `org.gnome.Shell.Eval` for focused window class, ID, title, and PID — works on Wayland GNOME where xdotool/xprop are unavailable.
- **Window detection chain**: GNOME Shell D-Bus → xprop → wlrctl → xdotool → wmctrl → /proc — works across all environments.
- **Compositor detection**: GNOME/Mutter detected via `pgrep gnome-shell` and `XDG_CURRENT_DESKTOP`.
- **Dependencies**: `dbus` crate (0.9) for AT-SPI2 and GNOME Shell D-Bus.
### Keyboard Grab Safety
- **sigaction without SA_RESTART**: Ctrl+C and SIGTERM now properly interrupt the blocking evdev read, releasing the grab before exit.
- **uinput auto-load**: The injector runs `modprobe uinput` before opening `/dev/uinput`.
- **EINTR handling**: Interrupted system calls are caught and re-check the signal flag.
- **30-second safety timeout**: Auto-releases grab if no events arrive (prevents permanent lockout).
### Clipboard & Injection
- **`wl-copy --paste-once`**: Keeps the clipboard process alive until pasted, eliminating 300-900ms delays on Wayland/GNOME.
- **X11 SelectionRequest log silenced**: No more clipboard spam in the terminal.
- **uinput priority**: uinput is always preferred over X11 XTest injection.
### Config Changes
- **Auto-restore disabled by default**: Prevents space consumption on valid Vietnamese words. Enable via `[auto_restore] enabled = true` if desired.
### CLI Enhancements
- **Pass-through characters**: All characters now appear in output (not just those that emit engine events).
- **Screen display**: Backspace characters are properly applied to show what would appear on screen.
- **Pass-through characters**: All characters appear in output (not just engine events).
- **Screen display**: Backspaces properly applied for realistic on-screen view.
- **State reset**: Each input line starts with a clean engine state.
- **New commands**: `:help`, `:status`, `:vi`, `:en`, `:ar on|off`, `:macros`, `:macro add/rm/clear`, `:events`, `:events clear`.
- **New commands**: `:help`, `:status`, `:vi`, `:en`, `:ar on|off`, `:macros`, `:macro add/rm/clear`, `:events`.
### Bug Fixes
- **Engine state correctly reset between input lines** in CLI test harness.
- **Flush characters forwarded** after macro expansion / auto-restore replacement to preserve spacing.
- **AT-SPI2 connected to wrong D-Bus bus**: Was connecting to session bus instead of the private accessibility bus. Now queries `org.a11y.Bus.GetAddress` to find the correct bus.
- **Password detection now periodic**: Re-checks every 30 keystrokes even without window change (catches in-terminal sudo prompts).
- **Double space on Ctrl+Space toggle**: Raw key forwarding now checks if engine is enabled before forwarding flush chars.
- **xprop/wmctrl fallbacks**: Window class, title, and ID detection now work without `xdotool` installed (uses `xprop` + `wmctrl`).
- **Single-instance lock improved**: Writes PID to lock file; detects and cleans up stale locks automatically.
- **Double space on Ctrl+Space toggle**: Raw key forwarding now checks engine enabled state.
- **Single-instance lock**: PID written to lock file; stale locks auto-detected and cleaned.
- **xprop/wmctrl fallbacks**: Window detection works without `xdotool` installed.
- **AT-SPI2 a11y bus connection**: Was connecting to session bus; now correctly queries the private a11y bus.
- **Engine state reset between CLI input lines**.
---
## v0.1.6 (2026-06-29)
### uinput-First Injection
- **Injection priority reversed**: uinput (`/dev/uinput`) is now the primary injection backend on X11, with X11 XTest as fallback. uinput sends evdev keycodes that route correctly through libinput — no X11 keycode offset needed.
- **X11 XTest keycode fix**: X11 injector was sending evdev keycodes directly to `XTestFakeKeyEvent`, which expects X11 keycodes (evdev + 8). Backspace sent keycode 14 (evdev) = X11 keycode 14 = "5" key. Fixed by adding +8 offset in all `send_keycode` paths.
- **`paste_via_clipboard()` backspace fixed**: was hardcoded to X11 keycode 14 (actually "5"), now uses evdev 14 + 8 = 22 (correct X11 backspace).
- **Injection priority reversed**: uinput (`/dev/uinput`) is now the primary injection backend on X11, with X11 XTest as fallback.
- **X11 XTest keycode fix**: +8 offset applied to all evdev keycodes for XTest compatibility.
- **`paste_via_clipboard()` backspace fixed**: was sending X11 keycode 14 (= "5"), now sends correct keycode 22.
### Window-Switch Detection
- **Active window ID verified on every keystroke**: removed the `gap > 100ms` guard — the daemon now polls `xdotool`/`xprop` directly for every character keypress. This catches window switches that complete in under 100ms, preventing old engine buffer from leaking into the new window.
- **Active window ID verified on every keystroke**: removed the 100ms guard — catches sub-100ms window switches.
### Input Method
- **Telex disabled in tray**: greyed out with "(next version)" label and `Disposition::Informative`. Only VNI is functional.
- **Default input method changed** from `"telex"` to `"vni"` in config fallback.
- **Telex disabled in tray**: greyed out as "(next version)". Only VNI was functional.
- **Default input method changed** to `"vni"`.
### Packaging
- **Flatpak and AppImage removed**: only `.deb` packaging is maintained. `packaging/flatpak/` and `packaging/appimage/` directories deleted.
- **Postinst improvements**: removes stale `/usr/local/bin/vietc*` binaries, deletes old `~/.config/vietc/config.toml` + `overrides.toml` + `.first-launch-done`, shows logout popup (notify-send + zenity).
- **CI workflow**: only `.deb` artifact collected (no AppImage).
- **Flatpak and AppImage removed**: only `.deb` packaging is maintained.
- **Postinst improvements**: cleans stale binaries, config files; shows logout popup.
---
## v0.1.5 (2026-06-29)
## v0.1.5 (2026-06-29)
### Window-Switch Engine Reset
- **Engine state now clears on window switch** — when Alt+Tab'ing between apps, the composition buffer is properly reset before the next keystroke. Previously, keystrokes could still apply Vietnamese tone/mark rules across app boundaries, producing corrupted text.
- **`last_key_time` only on character key presses** — modifier-only events (Alt, Ctrl, Shift) no longer update the gap timer, so the 100ms inline xprop poll fires reliably after every window switch, regardless of held modifiers.

386
README.md
View file

@ -24,203 +24,36 @@
Viet+ is a Vietnamese input method for Linux that takes a fundamentally different approach from every other IME: **Direct Input**.
Most Vietnamese IMEs use a **pre-edit buffer** — you type into a temporary buffer with an ugly underline, and the text only becomes real Vietnamese when you commit it. This causes:
- Duplicate text (buffer + committed)
- Underline distraction
- Broken copy/paste
- Desync between engine state and what's on screen
Most Vietnamese IMEs use a **pre-edit buffer** — you type into a temporary buffer with an ugly underline, and the text only becomes real Vietnamese when you commit it. This causes duplicate text, underline distraction, broken copy/paste, and desync between the engine state and what's on screen.
Viet+ eliminates all of this. Keystrokes are **instantly converted to Unicode** — what you type is what you see. No buffer. No underline. No duplication.
---
## How It Works
## Features
### Data Flow: Keypress to Screen
```
Physical Keyboard
┌──────────────────────────────────────────────────────────────┐
│ Stage 1: KEY CAPTURE │
│ │
│ evdev: /dev/input/event* grabs keyboard (primary, reliable) │
│ X11: XRecord passive monitoring (fallback) │
│ │
│ ┌─────────────┐ ┌──────────────┐ ┌──────────────────┐ │
│ │ evdev grab │ │ X11Capture │ │ Window switch │ │
│ │ (libevdev) │ │ (XRecord) │ │ detection (250ms)│ │
│ └─────────────┘ └──────────────┘ └──────────────────┘ │
└──────────────────────────────────────────────────────────────┘
┌──────────────────────────────────────────────────────────────┐
│ Stage 2: KEY ROUTING │
│ │
│ Modifier keys (Ctrl/Alt/Super) → forward directly │
│ Ctrl+Space → toggle Vietnamese ON/OFF │
│ Ctrl+Shift → toggle VNI/Telex input method │
│ Password detected → auto-disable Vietnamese │
│ Backspace → replay_backspace() │
│ Characters → replay_and_inject(ch) │
│ VNI/Telex control keys → consume when no match │
└──────────────────────────────────────────────────────────────┘
┌──────────────────────────────────────────────────────────────┐
│ Stage 3: BAMBOO ENGINE │
│ │
│ 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: KEY INJECTION │
│ │
│ Primary: uinput injection (evdev keycodes, correct on all │
│ display servers — routed through libinput on modern X11) │
│ 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) │
│ uinput Ctrl+V via /dev/uinput (no X11 dependency) │
│ │
│ Fallback: X11 XTest injection (X11 keycodes = evdev + 8) │
└──────────────────────────────────────────────────────────────┘
Application receives keystrokes
and renders Vietnamese text on screen
```
### Event Sourcing + Backspace-Replay
This is Viet+'s core innovation. Traditional IMEs track state incrementally — each keystroke updates an internal buffer. But this buffer can **desync** from what's actually on screen (due to focus changes, external pastes, etc.).
Viet+ uses **Event Sourcing**: every input action is recorded as a typed `InputEvent` (`KeyTyped`, `Backspace`, `Flush`, `Paste`) in an `EventStore`. On every keystroke, the entire event history is **replayed from scratch** through a fresh engine to compute the correct diff — no incremental state to desync.
```
Traditional IME:
keystroke → update buffer → emit event → hope it matches screen
Viet+ (Event Sourcing):
keystroke → append InputEvent → replay ALL events in fresh engine → compute diff
```
On every keystroke:
1. The keystroke is appended as an `InputEvent` to the `EventStore`
2. A **brand new** `Engine` is created
3. The **entire** event history is replayed through it via `Engine::replay_events()`
4. The engine's buffer is the **correct** screen output
5. Viet+ computes the diff: `Engine::replay_events_to_commands()` returns Type/Backspace commands
This means:
- **Zero state desync** — always recomputed from scratch
- **Self-healing** — if anything goes wrong, the next keystroke fixes it
- **Privacy-safe**`EventStore::pattern_hash()` provides a sha256 of the event type sequence for pattern detection without any ability to recover original text
- **Simple** — no complex state tracking or synchronization
---
## Architecture
```
vietc/
├── engine/ # Vietnamese composition engine (bamboo-core Rust port)
│ ├── engine.rs # Orchestrator + replay_events(), replay_events_to_commands()
│ ├── event.rs # Event Sourcing: InputEvent, EventStore, Command
│ ├── bamboo.rs # Bamboo engine: transformation model, composition, tone placement
│ ├── input_method.rs # VNI rule definitions
│ └── spelling.rs # Vietnamese syllable validation
├── protocol/ # Keyboard capture & injection
│ ├── inject.rs # KeyInjector trait
│ ├── x11_capture.rs # XRecord keyboard capture via C helper
│ ├── x11_inject.rs # XTest injection (fallback)
│ ├── uinput_monitor.rs # /dev/uinput injection (primary)
│ ├── uinput_client.rs # Unix socket client for vietc-uinputd
│ └── wayland_im.rs # Wayland IM protocol
├── daemon/ # Main daemon process
│ ├── main.rs # Event loops, Backspace-Replay, CPU pinning
│ ├── config.rs # TOML config loader + hot reload
│ ├── app_state.rs # Per-app VN/EN memory + password detection
│ ├── password_detector.rs # AT-SPI2 D-Bus password field detection
│ └── 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
│ └── tray.rs # Tray with VN/TLX/EN mode display
├── cli/ # Interactive test harness
├── packaging/ # .deb packaging scripts
└── vietc.toml # Default configuration
```
### Component Interaction
```
┌─────────────────────────────────────────────────────────────┐
│ vietc-tray │
│ (System tray icon, daemon launcher) │
└───────────────────────┬─────────────────────────────────────┘
│ starts
┌─────────────────────────────────────────────────────────────┐
│ vietc-daemon │
│ │
│ ┌──────────────┐ ┌──────────────┐ ┌──────────────────┐ │
│ │ Config │ │ App State │ │ Display │ │
│ │ (hot reload) │ │ (per-app) │ │ (X11/Wayland) │ │
│ └──────┬───────┘ └──────┬───────┘ └────────┬─────────┘ │
│ │ │ │ │
│ └─────────────────┼────────────────────┘ │
│ │ │
│ ┌──────▼──────┐ │
│ │ Event Loop │ │
│ │ │ │
│ │ evdev: grab │ │
│ │ keyboard │ │
│ │ │ │
│ │ Process │ │
│ │ keystroke │ │
│ │ │ │
│ │ Replay all │ │
│ │ history │ │
│ │ │ │
│ │ Inject │ │
│ │ diff │ │
│ └─────────────┘ │
│ │
│ ┌────────────────────────────────────────────────────────┐ │
│ │ vietc-engine │ │
│ │ VniEngine / EnglishDict / Spelling │ │
│ └────────────────────────────────────────────────────────┘ │
│ │
│ ┌────────────────────────────────────────────────────────┐ │
│ │ vietc-protocol │ │
│ │ UinputInjector / X11Injector / X11Capture / Wayland │ │
│ └────────────────────────────────────────────────────────┘ │
└─────────────────────────────────────────────────────────────┘
```
| Feature | How It Works |
|---------|-------------|
| **Direct Input** | No pre-edit buffer. Keystrokes instantly become text via uinput injection |
| **VNI & Telex** | Both input methods fully supported, switchable at runtime via Ctrl+Shift |
| **Bamboo Engine** | Transformation model — composition, marks, tones, flexible backtracking |
| **Smart Clusters** | `uo→ươ` with backtrack, `ua→ưa` horn placement |
| **Macro Expansion** | `ko → không`, `dc → được`, add your own |
| **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 |
| **Window-Switch Reset** | Engine clears automatically on Alt+Tab |
| **CPU Priority** | Pinned to P-cores (0-3) + nice(-10) for low-latency input |
| **Uinput Injection** | `/dev/uinput` for reliable injection on X11 and Wayland |
| **Password Auto-Detection** | 4 layers: AT-SPI2 → sudo process → window-title → window-class |
| **Tray Icon** | Shows current mode: Red VN / Blue TLX / Gray EN |
| **GNOME/Wayland** | Native GNOME Shell D-Bus integration |
---
## Input Methods
Both **VNI** and **Telex** are fully supported. Switch between them via:
- **Ctrl+Shift** hotkey (toggle at runtime)
- **System tray** menu: "Input Method > Telex / VNI"
- **Config file**: `input_method = "vni"` or `"telex"`
Both **VNI** and **Telex** are fully supported. Switch via **Ctrl+LeftShift** or the tray menu.
### VNI
@ -231,114 +64,89 @@ Both **VNI** and **Telex** are fully supported. Switch between them via:
| `3` | ả (hỏi) | `a3``ả` |
| `4` | ã (ngã) | `a4``ã` |
| `5` | ạ (nặng) | `a5``ạ` |
| `6` | â/ê/ô | `a6` → `â`, `e6``ê`, `o6``ô` |
| `7` | ơ/ư | `o7` → `ơ`, `u7``ư` |
| `8` | ă | `a8` → `ă` |
| `9` | đ | `d9` → `đ` |
| `6` | â/ê/ô | `a6→â`, `e6→ê`, `o6→ô` |
| `7` | ơ/ư | `o7→ơ`, `u7→ư` |
| `8` | ă | `a8ă` |
| `9` | đ | `d9đ` |
### Telex
| Key | Result | Example |
|-----|--------|---------|
| `s` | á (sắc) | `as``á` |
| `f` | à (huyền) | `af``à` |
| `r` | ả (hỏi) | `ar``ả` |
| `x` | ã (ngã) | `ax``ã` |
| `j` | ạ (nặng) | `aj``ạ` |
| `aa` | â | `aa``â` |
| `ee` | ê | `ee``ê` |
| `oo` | ô | `oo``ô` |
| `ow` | ơ | `ow``ơ` |
| `aw` | ă | `aw``ă` |
| `uw` | ư | `uw``ư` |
| `dd` | đ | `dd``đ` |
| `w` | ươ (uo cluster) | `chuongw``chương` |
Flexible typing: type the full syllable, then add marks/tone keys at the end. Examples: `tieengs``tiếng`, `nguyeexn``nguyễn`, `chafo``chào`. The engine scans backward up to 5 characters to find the target vowel.
| `s` | á (sắc) | `as→á` |
| `f` | à (huyền) | `af→à` |
| `r` | ả (hỏi) | `ar→ả` |
| `x` | ã (ngã) | `ax→ã` |
| `j` | ạ (nặng) | `aj→ạ` |
| `aa` | â | `aa→â` |
| `ee` | ê | `ee→ê` |
| `oo` | ô | `oo→ô` |
| `ow` | ơ | `ow→ơ` |
| `aw` | ă | `aw→ă` |
| `uw` | ư | `uw→ư` |
| `dd` | đ | `dd→đ` |
| `w` | ươ | `chuongw→chương` |
---
## Features
## Key Bindings
| Feature | How It Works |
|---------|-------------|
| **Direct Input** | No pre-edit buffer. Keystrokes instantly become text via uinput injection |
| **Bamboo Engine** | Transformation model ported from bamboo-core — composition, marks, tones, flexible backtracking |
| **Flexible Backtrack** | Type tone/modifier at end of syllable (`tran5` → `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** | `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) |
| **Window-Switch Reset** | Active window ID verified on every keystroke — Alt+Tab instantly clears engine state. No stale composition across apps |
| **CPU Priority** | Pins daemon to P-cores (0-3) + nice(-10) for low-latency input |
| **Uinput Injection** | Uses `/dev/uinput` for reliable keyboard injection without X11 dependency. Falls back to XTest on systems without uinput access |
| **Password Auto-Detection** | AT-SPI2 + window-class + window-title — automatically disables Vietnamese when typing into password fields |
| **Method Toggle** | Ctrl+Shift switches between VNI and Telex at runtime; tray icon shows current mode (VN/TLX/EN) |
| **GNOME/Wayland Support** | Native GNOME Shell D-Bus integration for window detection, app memory, and password detection on Wayland |
| **VNI & Telex** | Both input methods fully supported, switchable at runtime |
| Combo | Action |
|-------|--------|
| **Ctrl+Space** | Toggle Vietnamese ON/OFF |
| **Ctrl+LeftShift** | Toggle VNI ↔ Telex |
---
## Password Detection
4-layer automatic detection. When a password field is detected, Vietnamese is automatically disabled:
| Layer | Method | Detects |
|-------|--------|---------|
| 1 | AT-SPI2 D-Bus (a11y role check) | Password fields in accessible apps |
| 2 | Process tree (pstree) | `sudo` / `passwd` in terminal |
| 3 | Window title keywords | `password`, `sudo` in title |
| 4 | Window class matching | pinentry, polkit, kwallet dialogs |
---
## Installation
### Single Command (from Source)
Depending on which repository you prefer to clone from, you can use one of the following commands to install or update Viet+ in a single step:
#### From GitHub (Recommended)
**Install / Update:**
```bash
git clone https://github.com/vndangkhoa/vietc.git /tmp/vietc && cd /tmp/vietc && sudo ./install.sh
```
**Uninstall:**
```bash
curl -sSL https://raw.githubusercontent.com/vndangkhoa/vietc/main/uninstall.sh | sudo bash
```
#### From Forgejo (Self-Hosted)
**Install / Update:**
```bash
git clone https://git.khoavo.myds.me/vndangkhoa/vietc.git /tmp/vietc && cd /tmp/vietc && sudo ./install.sh
```
**Uninstall:**
```bash
curl -sSL https://git.khoavo.myds.me/vndangkhoa/vietc/raw/branch/main/uninstall.sh | sudo bash
```
### Debian Package (recommended)
System tray icon + daemon + desktop entry. Requires user to be in the `input` group for keyboard capture.
```bash
# Install
sudo dpkg -i vietc_0.1.7-1_amd64.deb
# Log out and log back in (for input group membership to take effect)
# Then launch "Viet+" from your application menu
```
The post-install script will:
- Kill any running tray/daemon processes
- Remove stale binaries from `/usr/local/bin/`
- Add your user to the `input` group
- Prompt you to log out and back in
### Build from Source
```bash
# Dependencies (Ubuntu/Debian)
sudo apt install git curl build-essential pkg-config \
libx11-dev libxtst-dev libevdev-dev libdbus-1-dev
# Clone and build
git clone https://github.com/vndangkhoa/vietc.git
cd vietc
make deb
sudo dpkg -i packaging/deb/vietc_0.1.6-1_amd64.deb
cargo build --release
# Add user to input group (for keyboard capture)
sudo usermod -aG input $USER
# Log out and log back in
# Run
./target/release/vietc
```
Requires Rust toolchain, `pkg-config`, `libx11-dev`, `libxtst-dev`, `libevdev-dev`. See `packaging/deb/build-deb.sh` for details.
### Wayland (Ubuntu 24.04+) — Additional steps
```bash
sudo apt install wl-clipboard
gsettings set org.gnome.desktop.a11y.applications screen-reader-enabled true
```
### uinput Access (recommended)
```bash
sudo modprobe uinput
echo 'KERNEL=="uinput", GROUP="input", MODE="0660"' | sudo tee /etc/udev/rules.d/99-uinput.rules
sudo udevadm control --reload-rules && sudo udevadm trigger
```
---
@ -353,13 +161,9 @@ toggle_method_key = "shift" # Ctrl+Shift to toggle VNI/Telex
start_enabled = true # Vietnamese by default
grab = true # grab keyboard (evdev)
[auto_restore]
enabled = true
trigger_keys = ["space", "escape"]
[password_detection]
enabled = true
check_atspi2 = true # AT-SPI2 accessibility bus detection
check_atspi2 = true
check_window_title = true
title_keywords = ["password", "passphrase", "secret", "mật khẩu", "sudo"]
password_apps = ["pinentry", "pinentry-gtk-2", "pinentry-qt",
@ -371,12 +175,36 @@ password_apps = ["pinentry", "pinentry-gtk-2", "pinentry-qt",
enabled = true
english_apps = ["code", "vim", "kitty", "foot"]
vietnamese_apps = ["telegram", "discord", "firefox"]
bypass_apps = ["kitty", "alacritty", "steam"]
[macros]
ko = "không"
dc = "được"
vs = "với"
lm = "làm"
```
---
## Architecture
```
vietc/
├── engine/ # Vietnamese composition engine (bamboo-core port)
├── protocol/ # Keyboard capture & injection
│ ├── uinput_monitor.rs # /dev/uinput injection (primary)
│ ├── x11_inject.rs # XTest injection (fallback)
│ ├── x11_capture.rs # XRecord key capture
│ └── wayland_im.rs # Wayland IM protocol (stub)
├── daemon/ # Main daemon process
│ ├── main.rs # Event loops, grab, signal handling
│ ├── config.rs # TOML config loader + hot reload
│ ├── app_state.rs # Per-app VN/EN memory + password detection
│ ├── password_detector.rs # AT-SPI2 D-Bus password field detection
│ └── display.rs # X11/Wayland/compositor detection
├── ui/ # System tray icon (ksni)
│ └── tray.rs # Tray with VN/TLX/EN mode display
├── cli/ # Interactive test harness
└── uinputd/ # Privileged uinput socket daemon
```
---