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 ### 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. - **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.
- **Window-class fallback**: Password dialogs (pinentry, polkit, kwallet, ssh-askpass) are detected via `password_apps` config list. - **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", "passphrase", "sudo", "mật khẩu" trigger automatic English mode. - **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 Input Method
- **Telex now fully enabled**: Both VNI and Telex are supported. Switch via Ctrl+Shift hotkey or tray menu "Input Method > Telex / VNI". - **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 so the tray can display it. - **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. - **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 ### 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. - **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 → wlrctl → xdotool → /proc — ensures window tracking works across all environments. - **Window detection chain**: GNOME Shell D-Bus → xprop → wlrctl → xdotool → wmctrl → /proc — works across all environments.
- **Compositor detection**: Added GNOME/Mutter detection via `pgrep gnome-shell` and `XDG_CURRENT_DESKTOP`. - **Compositor detection**: GNOME/Mutter detected via `pgrep gnome-shell` and `XDG_CURRENT_DESKTOP`.
- **Dependencies**: `dbus` crate (0.9) added for both AT-SPI2 and GNOME Shell D-Bus queries. - **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 ### CLI Enhancements
- **Pass-through characters**: All characters now appear in output (not just those that emit engine events). - **Pass-through characters**: All characters appear in output (not just engine events).
- **Screen display**: Backspace characters are properly applied to show what would appear on screen. - **Screen display**: Backspaces properly applied for realistic on-screen view.
- **State reset**: Each input line starts with a clean engine state. - **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 ### Bug Fixes
- **Engine state correctly reset between input lines** in CLI test harness. - **Double space on Ctrl+Space toggle**: Raw key forwarding now checks engine enabled state.
- **Flush characters forwarded** after macro expansion / auto-restore replacement to preserve spacing. - **Single-instance lock**: PID written to lock file; stale locks auto-detected and cleaned.
- **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. - **xprop/wmctrl fallbacks**: Window detection works without `xdotool` installed.
- **Password detection now periodic**: Re-checks every 30 keystrokes even without window change (catches in-terminal sudo prompts). - **AT-SPI2 a11y bus connection**: Was connecting to session bus; now correctly queries the private a11y bus.
- **Double space on Ctrl+Space toggle**: Raw key forwarding now checks if engine is enabled before forwarding flush chars. - **Engine state reset between CLI input lines**.
- **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.
--- ---
## v0.1.6 (2026-06-29)
### uinput-First Injection ### 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. - **Injection priority reversed**: uinput (`/dev/uinput`) is now the primary injection backend on X11, with X11 XTest as fallback.
- **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. - **X11 XTest keycode fix**: +8 offset applied to all evdev keycodes for XTest compatibility.
- **`paste_via_clipboard()` backspace fixed**: was hardcoded to X11 keycode 14 (actually "5"), now uses evdev 14 + 8 = 22 (correct X11 backspace). - **`paste_via_clipboard()` backspace fixed**: was sending X11 keycode 14 (= "5"), now sends correct keycode 22.
### Window-Switch Detection ### 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 ### Input Method
- **Telex disabled in tray**: greyed out with "(next version)" label and `Disposition::Informative`. Only VNI is functional. - **Telex disabled in tray**: greyed out as "(next version)". Only VNI was functional.
- **Default input method changed** from `"telex"` to `"vni"` in config fallback. - **Default input method changed** to `"vni"`.
### Packaging ### Packaging
- **Flatpak and AppImage removed**: only `.deb` packaging is maintained. `packaging/flatpak/` and `packaging/appimage/` directories deleted. - **Flatpak and AppImage removed**: only `.deb` packaging is maintained.
- **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). - **Postinst improvements**: cleans stale binaries, config files; shows logout popup.
- **CI workflow**: only `.deb` artifact collected (no AppImage).
--- ---
## v0.1.5 (2026-06-29) ## v0.1.5 (2026-06-29)
## v0.1.5 (2026-06-29)
### Window-Switch Engine Reset ### 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. - **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. - **`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**. 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: 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.
- Duplicate text (buffer + committed)
- Underline distraction
- Broken copy/paste
- Desync between 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. 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 | Feature | How It Works |
|---------|-------------|
``` | **Direct Input** | No pre-edit buffer. Keystrokes instantly become text via uinput injection |
Physical Keyboard | **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 |
│ Stage 1: KEY CAPTURE │ | **Casing Preservation** | `Tieengs → Tiếng`, `TIEENGS → TIẾNG` |
│ │ | **App Memory** | Per-app Vietnamese/English state, saved to `overrides.toml` |
│ evdev: /dev/input/event* grabs keyboard (primary, reliable) │ | **Hot Reload** | Config changes apply without restart |
│ X11: XRecord passive monitoring (fallback) │ | **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 |
│ │ evdev grab │ │ X11Capture │ │ Window switch │ │ | **Password Auto-Detection** | 4 layers: AT-SPI2 → sudo process → window-title → window-class |
│ │ (libevdev) │ │ (XRecord) │ │ detection (250ms)│ │ | **Tray Icon** | Shows current mode: Red VN / Blue TLX / Gray EN |
│ └─────────────┘ └──────────────┘ └──────────────────┘ │ | **GNOME/Wayland** | Native GNOME Shell D-Bus integration |
└──────────────────────────────────────────────────────────────┘
┌──────────────────────────────────────────────────────────────┐
│ 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 │ │
│ └────────────────────────────────────────────────────────┘ │
└─────────────────────────────────────────────────────────────┘
```
--- ---
## Input Methods ## Input Methods
Both **VNI** and **Telex** are fully supported. Switch between them via: Both **VNI** and **Telex** are fully supported. Switch via **Ctrl+LeftShift** or the tray menu.
- **Ctrl+Shift** hotkey (toggle at runtime)
- **System tray** menu: "Input Method > Telex / VNI"
- **Config file**: `input_method = "vni"` or `"telex"`
### VNI ### VNI
@ -231,114 +64,89 @@ Both **VNI** and **Telex** are fully supported. Switch between them via:
| `3` | ả (hỏi) | `a3``ả` | | `3` | ả (hỏi) | `a3``ả` |
| `4` | ã (ngã) | `a4``ã` | | `4` | ã (ngã) | `a4``ã` |
| `5` | ạ (nặng) | `a5``ạ` | | `5` | ạ (nặng) | `a5``ạ` |
| `6` | â/ê/ô | `a6` → `â`, `e6``ê`, `o6``ô` | | `6` | â/ê/ô | `a6→â`, `e6→ê`, `o6→ô` |
| `7` | ơ/ư | `o7` → `ơ`, `u7``ư` | | `7` | ơ/ư | `o7→ơ`, `u7→ư` |
| `8` | ă | `a8` → `ă` | | `8` | ă | `a8ă` |
| `9` | đ | `d9` → `đ` | | `9` | đ | `d9đ` |
### Telex ### Telex
| Key | Result | Example | | Key | Result | Example |
|-----|--------|---------| |-----|--------|---------|
| `s` | á (sắc) | `as``á` | | `s` | á (sắc) | `as→á` |
| `f` | à (huyền) | `af``à` | | `f` | à (huyền) | `af→à` |
| `r` | ả (hỏi) | `ar``ả` | | `r` | ả (hỏi) | `ar→ả` |
| `x` | ã (ngã) | `ax``ã` | | `x` | ã (ngã) | `ax→ã` |
| `j` | ạ (nặng) | `aj``ạ` | | `j` | ạ (nặng) | `aj→ạ` |
| `aa` | â | `aa``â` | | `aa` | â | `aa→â` |
| `ee` | ê | `ee``ê` | | `ee` | ê | `ee→ê` |
| `oo` | ô | `oo``ô` | | `oo` | ô | `oo→ô` |
| `ow` | ơ | `ow``ơ` | | `ow` | ơ | `ow→ơ` |
| `aw` | ă | `aw``ă` | | `aw` | ă | `aw→ă` |
| `uw` | ư | `uw``ư` | | `uw` | ư | `uw→ư` |
| `dd` | đ | `dd``đ` | | `dd` | đ | `dd→đ` |
| `w` | ươ (uo cluster) | `chuongw``chương` | | `w` | ươ | `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.
--- ---
## Features ## Key Bindings
| Feature | How It Works | | Combo | Action |
|---------|-------------| |-------|--------|
| **Direct Input** | No pre-edit buffer. Keystrokes instantly become text via uinput injection | | **Ctrl+Space** | Toggle Vietnamese ON/OFF |
| **Bamboo Engine** | Transformation model ported from bamboo-core — composition, marks, tones, flexible backtracking | | **Ctrl+LeftShift** | Toggle VNI ↔ Telex |
| **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 | ## Password Detection
| **Casing Preservation** | `Tieengs``Tiếng`, `TIEENGS``TIẾNG` |
| **App Memory** | Per-app Vietnamese/English state, saved to `overrides.toml` | 4-layer automatic detection. When a password field is detected, Vietnamese is automatically disabled:
| **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 | | Layer | Method | Detects |
| **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 | | 1 | AT-SPI2 D-Bus (a11y role check) | Password fields in accessible apps |
| **Password Auto-Detection** | AT-SPI2 + window-class + window-title — automatically disables Vietnamese when typing into password fields | | 2 | Process tree (pstree) | `sudo` / `passwd` in terminal |
| **Method Toggle** | Ctrl+Shift switches between VNI and Telex at runtime; tray icon shows current mode (VN/TLX/EN) | | 3 | Window title keywords | `password`, `sudo` in title |
| **GNOME/Wayland Support** | Native GNOME Shell D-Bus integration for window detection, app memory, and password detection on Wayland | | 4 | Window class matching | pinentry, polkit, kwallet dialogs |
| **VNI & Telex** | Both input methods fully supported, switchable at runtime |
--- ---
## Installation ## 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 ### Build from Source
```bash ```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 git clone https://github.com/vndangkhoa/vietc.git
cd vietc cd vietc
make deb cargo build --release
sudo dpkg -i packaging/deb/vietc_0.1.6-1_amd64.deb
# 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 start_enabled = true # Vietnamese by default
grab = true # grab keyboard (evdev) grab = true # grab keyboard (evdev)
[auto_restore]
enabled = true
trigger_keys = ["space", "escape"]
[password_detection] [password_detection]
enabled = true enabled = true
check_atspi2 = true # AT-SPI2 accessibility bus detection check_atspi2 = true
check_window_title = true check_window_title = true
title_keywords = ["password", "passphrase", "secret", "mật khẩu", "sudo"] title_keywords = ["password", "passphrase", "secret", "mật khẩu", "sudo"]
password_apps = ["pinentry", "pinentry-gtk-2", "pinentry-qt", password_apps = ["pinentry", "pinentry-gtk-2", "pinentry-qt",
@ -371,12 +175,36 @@ password_apps = ["pinentry", "pinentry-gtk-2", "pinentry-qt",
enabled = true enabled = true
english_apps = ["code", "vim", "kitty", "foot"] english_apps = ["code", "vim", "kitty", "foot"]
vietnamese_apps = ["telegram", "discord", "firefox"] vietnamese_apps = ["telegram", "discord", "firefox"]
bypass_apps = ["kitty", "alacritty", "steam"]
[macros] [macros]
ko = "không" ko = "không"
dc = "được" dc = "được"
vs = "với" 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
``` ```
--- ---