diff --git a/CHANGELOG.md b/CHANGELOG.md index 9d4c614..ecc3a4a 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -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 100 ms inline xprop poll fires reliably after every window switch, regardless of held modifiers. diff --git a/README.md b/README.md index 452f94b..beed8db 100644 --- a/README.md +++ b/README.md @@ -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 +``` --- @@ -347,19 +155,15 @@ Requires Rust toolchain, `pkg-config`, `libx11-dev`, `libxtst-dev`, `libevdev-de Config file: `~/.config/vietc/config.toml` or `./vietc.toml` ```toml -input_method = "vni" # "vni" or "telex" -toggle_key = "space" # Ctrl+Space to toggle VN/EN -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"] +input_method = "vni" # "vni" or "telex" +toggle_key = "space" # Ctrl+Space to toggle VN/EN +toggle_method_key = "shift" # Ctrl+Shift to toggle VNI/Telex +start_enabled = true # Vietnamese by default +grab = true # grab keyboard (evdev) [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 ``` ---