docs: rewrite README with Backspace-Replay, add CHANGELOG.md
This commit is contained in:
parent
3858aa955c
commit
2ab132dd9a
2 changed files with 132 additions and 145 deletions
36
CHANGELOG.md
Normal file
36
CHANGELOG.md
Normal file
|
|
@ -0,0 +1,36 @@
|
||||||
|
# Changelog
|
||||||
|
|
||||||
|
## v0.1.0 (2026-06-26)
|
||||||
|
|
||||||
|
Initial release.
|
||||||
|
|
||||||
|
### 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)
|
||||||
|
|
||||||
|
### 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
|
||||||
|
|
||||||
|
### 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
|
||||||
|
|
||||||
|
### Packaging
|
||||||
|
- AppImage with bundled xclip (no manual setup needed)
|
||||||
|
- Debian package with proper conffiles, maintainer scripts, and lintian overrides
|
||||||
|
- Systemd user service
|
||||||
|
|
||||||
|
### Testing
|
||||||
|
- 255+ unit tests across engine, protocol, daemon config, and replay
|
||||||
195
README.md
195
README.md
|
|
@ -13,7 +13,7 @@
|
||||||
|
|
||||||
<p align="center">
|
<p align="center">
|
||||||
<b>Vietnamese Input Method for Linux</b><br>
|
<b>Vietnamese Input Method for Linux</b><br>
|
||||||
<sub>Zero underline • Native Wayland/X11 • Built in Rust</sub>
|
<sub>Zero underline • Native X11 • Backspace-Replay sync • Built in Rust</sub>
|
||||||
</p>
|
</p>
|
||||||
|
|
||||||
## About Viet+
|
## About Viet+
|
||||||
|
|
@ -23,45 +23,7 @@ Viet+ is a modern Vietnamese input method for Linux that eliminates the **underl
|
||||||
- **No pre-edit buffer** — keystrokes are instantly converted to Unicode
|
- **No pre-edit buffer** — keystrokes are instantly converted to Unicode
|
||||||
- **No underline** — clean, distraction-free typing
|
- **No underline** — clean, distraction-free typing
|
||||||
- **No text duplication** — just pure Vietnamese
|
- **No text duplication** — just pure Vietnamese
|
||||||
- **Smart modifier overriding** — most recently typed shape modifier key takes precedence
|
- **Backspace-Replay sync** — engine state never desyncs from what's on screen
|
||||||
- **Tone preservation** — tones are preserved perfectly when overriding shape modifiers
|
|
||||||
- **Both Telex and VNI** — support for both Vietnamese input methods
|
|
||||||
|
|
||||||
The engine handles complex linguistic rules including:
|
|
||||||
- Shape modifier overriding (â↔ă, ô↔ơ, ơ→ô, ă→â)
|
|
||||||
- Smart cluster handling (uô+w→ươ, ươ+o→uô)
|
|
||||||
- VNI digit modifiers (6/7/8) following the same override logic
|
|
||||||
- Flexible end-of-word placement for tones and modifiers
|
|
||||||
- Complex consonant handling (ngh, ngh, đ)
|
|
||||||
|
|
||||||
The injection layer ensures Vietnamese characters appear correctly by running injection tools (xdotool, wtype, wl-copy, xclip) as the original user, not root, to access the display. This works on both X11 (pkexec) and Wayland (sudo) without requiring password dialogs.
|
|
||||||
|
|
||||||
Viet+ is designed for high performance with native setuid/setgid user context switching to avoid slow sudo/PAM overhead, zero telemetry, and full FOSS licensing.
|
|
||||||
|
|
||||||
## Why Viet+?
|
|
||||||
|
|
||||||
Most Vietnamese input methods on Linux suffer from **underline hell** — pre-edit buffers that duplicate text, show ugly underlines, and break your flow. Viet+ takes a different approach:
|
|
||||||
|
|
||||||
> **Direct Input** — keystrokes are instantly converted to Unicode. No pre-edit buffer. No underline. No text duplication. Just pure Vietnamese.
|
|
||||||
|
|
||||||
---
|
|
||||||
|
|
||||||
<p align="center">
|
|
||||||
<a href="#features">Features</a> •
|
|
||||||
<a href="#quick-start">Quick Start</a> •
|
|
||||||
<a href="#input-methods">Input Methods</a> •
|
|
||||||
<a href="#configuration">Configuration</a> •
|
|
||||||
<a href="#installation">Installation</a> •
|
|
||||||
<a href="#building">Building</a>
|
|
||||||
</p>
|
|
||||||
|
|
||||||
---
|
|
||||||
|
|
||||||
## Why Viet+?
|
|
||||||
|
|
||||||
Most Vietnamese input methods on Linux suffer from **underline hell** — pre-edit buffers that duplicate text, show ugly underlines, and break your flow. Viet+ takes a different approach:
|
|
||||||
|
|
||||||
> **Direct Input** — keystrokes are instantly converted to Unicode. No pre-edit buffer. No underline. No text duplication. Just pure Vietnamese.
|
|
||||||
|
|
||||||
---
|
---
|
||||||
|
|
||||||
|
|
@ -70,22 +32,30 @@ Most Vietnamese input methods on Linux suffer from **underline hell** — pre-ed
|
||||||
| Feature | Description |
|
| Feature | Description |
|
||||||
|---------|-------------|
|
|---------|-------------|
|
||||||
| **Direct Input Engine** | No pre-edit buffer, no underline, no text duplication |
|
| **Direct Input Engine** | No pre-edit buffer, no underline, no text duplication |
|
||||||
|
| **Backspace-Replay** | Replays entire keystroke history through a fresh engine on every keypress — eliminates state desync |
|
||||||
| **Telex & VNI** | Both input methods fully supported |
|
| **Telex & VNI** | Both input methods fully supported |
|
||||||
| **Flexible Diacritic Placement** | Type modifiers/tone marks at end of syllable (e.g., `tranaf` → `trần`) |
|
| **Flexible Diacritic Placement** | Type modifiers/tone marks at end of syllable (e.g., `tranaf` -> `trần`) |
|
||||||
| **Auto-Restore English** | Hit space/ESC to undo accidental Vietnamese conversion |
|
| **Auto-Restore English** | Hit space/ESC to undo accidental Vietnamese conversion |
|
||||||
| **ESC Undo** | Strip all tones from the current word instantly |
|
| **ESC Undo** | Strip all tones from the current word instantly |
|
||||||
| **Smart App Memory** | Remembers Vietnamese/English per application |
|
| **Smart App Memory** | Remembers Vietnamese/English per application |
|
||||||
| **Macro Expansion** | Custom shortcuts (e.g., `ko` → `không`) |
|
| **Macro Expansion** | Custom shortcuts (e.g., `ko` -> `không`) |
|
||||||
| **Unified Injection** | Unified channel backspace and typing injection to prevent ordering race conditions |
|
| **Focus Reset** | Automatically clears engine state on focus change between apps |
|
||||||
| **Focus Buffer Auto-Reset** | Automatically clears the engine's compose buffer on focus change between apps |
|
| **Casing Preservation** | Syllable substitutions preserve your exact casing (e.g. `Saa` -> `Sả`, `SAA` -> `SẢ`) |
|
||||||
| **Logging & Rotation** | Persistent logging at `~/.config/vietc/vietc.log` with automatic 10MB rotation |
|
| **CPU Priority** | Pins daemon to P-cores + nice(-10) for low-latency input |
|
||||||
| **Hot Reload** | Config changes apply without restart |
|
|
||||||
| **Casing Preservation** | Syllable substitutions preserve your exact typing casing (e.g. `Saa` → `Sả`, `SAA` → `SẢ`) |
|
|
||||||
| **High-Performance Injection** | Direct native setuid/setgid user context switching to run injection tools instantly with no slow sudo/PAM overhead |
|
|
||||||
| **Zero Telemetry** | No keylogging, no network calls, fully FOSS |
|
| **Zero Telemetry** | No keylogging, no network calls, fully FOSS |
|
||||||
|
|
||||||
---
|
---
|
||||||
|
|
||||||
|
## Why Viet+?
|
||||||
|
|
||||||
|
Most Vietnamese input methods on Linux suffer from **underline hell** — pre-edit buffers that duplicate text, show ugly underlines, and break your flow. Viet+ takes a different approach:
|
||||||
|
|
||||||
|
> **Direct Input** — keystrokes are instantly converted to Unicode. No pre-edit buffer. No underline. No text duplication. Just pure Vietnamese.
|
||||||
|
|
||||||
|
The **Backspace-Replay** pattern keeps the engine perfectly in sync: instead of tracking state incrementally (which can desync), Viet+ replays the entire keystroke history through a fresh engine on every keypress. The screen output is always recomputed from scratch.
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
## Quick Start
|
## Quick Start
|
||||||
|
|
||||||
```bash
|
```bash
|
||||||
|
|
@ -97,8 +67,8 @@ make build-all
|
||||||
# Test the engine interactively
|
# Test the engine interactively
|
||||||
cargo run --bin vietc-cli
|
cargo run --bin vietc-cli
|
||||||
|
|
||||||
# Run the daemon (requires root for keyboard grab + uinput)
|
# Run the daemon
|
||||||
sudo make run
|
cargo run --bin vietc
|
||||||
|
|
||||||
# Or download a package from the releases page
|
# Or download a package from the releases page
|
||||||
# AppImage: ./Viet+-0.1.0-x86_64.AppImage
|
# AppImage: ./Viet+-0.1.0-x86_64.AppImage
|
||||||
|
|
@ -109,23 +79,22 @@ sudo make run
|
||||||
|
|
||||||
## Input Methods
|
## Input Methods
|
||||||
|
|
||||||
### Telex (Default)
|
### Telex
|
||||||
|
|
||||||
| Key | Result | Example |
|
| Key | Result | Example |
|
||||||
|-----|--------|---------|
|
|-----|--------|---------|
|
||||||
| `aa` | â | `tan` → `tân` |
|
| `aa` | â | `tan` -> `tân` |
|
||||||
| `aw` | ă | `tan` → `tăn` |
|
| `aw` | ă | `tan` -> `tăn` |
|
||||||
| `ee` | ê | `men` → `mên` |
|
| `ee` | ê | `men` -> `mên` |
|
||||||
| `oo` | ô | `to` → `tô` |
|
| `oo` | ô | `to` -> `tô` |
|
||||||
| `ow` | ơ | `to` → `tơ` |
|
| `ow` | ơ | `to` -> `tơ` |
|
||||||
| `ew` | ê | `en` → `ên` |
|
| `uw` | ư | `tu` -> `tư` |
|
||||||
| `uw` | ư | `tu` → `tư` |
|
| `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` → `ạ` |
|
| `dd` | đ | `dd` -> `đ` |
|
||||||
| `dd` | đ | `dd` → `đ` |
|
|
||||||
|
|
||||||
### VNI
|
### VNI
|
||||||
|
|
||||||
|
|
@ -150,13 +119,14 @@ sudo make run
|
||||||
Config file: `~/.config/vietc/config.toml` or `./vietc.toml`
|
Config file: `~/.config/vietc/config.toml` or `./vietc.toml`
|
||||||
|
|
||||||
```toml
|
```toml
|
||||||
input_method = "telex"
|
input_method = "vni"
|
||||||
toggle_key = "space"
|
toggle_key = "space"
|
||||||
start_enabled = true
|
start_enabled = false
|
||||||
debug = false
|
grab = true
|
||||||
|
|
||||||
[auto_restore]
|
[auto_restore]
|
||||||
enabled = true
|
enabled = true
|
||||||
|
trigger_keys = ["space", "escape"]
|
||||||
|
|
||||||
[app_state]
|
[app_state]
|
||||||
enabled = true
|
enabled = true
|
||||||
|
|
@ -175,11 +145,13 @@ lm = "làm"
|
||||||
## Architecture
|
## Architecture
|
||||||
|
|
||||||
```
|
```
|
||||||
┌──────────────┐ ┌──────────────┐ ┌────────────────┐
|
┌──────────────────┐ ┌──────────────────┐ ┌────────────────┐
|
||||||
│ evdev │────▶│ Viet+ │────▶│ uinput/X11 │
|
│ X11 Keyboard │────▶│ Viet+ Daemon │────▶│ X11/XTEST │
|
||||||
│ keyboard │ │ Engine │ │ injection │
|
│ Grab (XGrabKb) │ │ │ │ Injection │
|
||||||
│ monitor │ │ (Telex/VNI) │ │ │
|
│ │ │ Backspace-Replay│ │ │
|
||||||
└──────────────┘ └──────────────┘ └────────────────┘
|
│ FocusIn/Out │ │ Engine │ │ Direct │
|
||||||
|
│ Detection │ │ (Telex/VNI) │ │ Clipboard │
|
||||||
|
└──────────────────┘ └──────────────────┘ └────────────────┘
|
||||||
│
|
│
|
||||||
┌─────┴─────┐
|
┌─────┴─────┐
|
||||||
│ App State │
|
│ App State │
|
||||||
|
|
@ -187,45 +159,41 @@ lm = "làm"
|
||||||
└───────────┘
|
└───────────┘
|
||||||
```
|
```
|
||||||
|
|
||||||
|
### How Backspace-Replay Works
|
||||||
|
|
||||||
|
1. All keystrokes in the current word are stored in `keystroke_history`
|
||||||
|
2. On each keypress, a **fresh engine** is created and the entire history is replayed through it
|
||||||
|
3. The engine's buffer IS what should be on screen
|
||||||
|
4. Viet+ calculates the diff: backspaces to erase old text + new text to type
|
||||||
|
5. On flush (space/period/etc.), history is cleared for the next word
|
||||||
|
|
||||||
|
This eliminates the state desync bugs that plague incremental engines.
|
||||||
|
|
||||||
---
|
---
|
||||||
|
|
||||||
## Installation
|
## Installation
|
||||||
|
|
||||||
### System Dependencies
|
|
||||||
|
|
||||||
| Component | Ubuntu/Debian | Fedora | Arch |
|
|
||||||
|-----------|--------------|--------|------|
|
|
||||||
| Core daemon | *(none)* | *(none)* | *(none)* |
|
|
||||||
| Tray icon | `libdbus-1-dev pkg-config` | `dbus-devel pkgconf` | `dbus pkgconf` |
|
|
||||||
|
|
||||||
### Debian/Ubuntu Package
|
### Debian/Ubuntu Package
|
||||||
|
|
||||||
```bash
|
```bash
|
||||||
sudo dpkg -i vietc_0.1.0-1_amd64.deb
|
sudo dpkg -i vietc_0.1.0-1_amd64.deb
|
||||||
# Or build from source:
|
|
||||||
make deb
|
|
||||||
```
|
```
|
||||||
|
|
||||||
The .deb installs the daemon, CLI, tray icon, systemd user service, and config.
|
Recommends: `libxtst6`, `xclip` (for clipboard injection)
|
||||||
|
|
||||||
### AppImage
|
### AppImage
|
||||||
|
|
||||||
```bash
|
```bash
|
||||||
make appimage
|
./Viet+-0.1.0-x86_64.AppImage
|
||||||
# Requires appimagetool
|
|
||||||
```
|
```
|
||||||
|
|
||||||
Run with `sudo` for keyboard grab:
|
No special permissions needed on X11 — uses XGrabKeyboard + XTest injection.
|
||||||
|
|
||||||
```bash
|
|
||||||
sudo ./Viet+-0.1.0-x86_64.AppImage
|
|
||||||
```
|
|
||||||
|
|
||||||
### Manual Install
|
### Manual Install
|
||||||
|
|
||||||
```bash
|
```bash
|
||||||
|
make build-all
|
||||||
sudo make install
|
sudo make install
|
||||||
sudo make install-ui # tray icon (optional)
|
|
||||||
```
|
```
|
||||||
|
|
||||||
---
|
---
|
||||||
|
|
@ -233,10 +201,10 @@ sudo make install-ui # tray icon (optional)
|
||||||
## Building
|
## Building
|
||||||
|
|
||||||
```bash
|
```bash
|
||||||
# Build all backends (uinput + X11 + Wayland)
|
# Build all backends (X11 + Wayland)
|
||||||
make build-all
|
make build-all
|
||||||
|
|
||||||
# Run tests (162+ engine tests)
|
# Run tests (255+ tests)
|
||||||
make test
|
make test
|
||||||
|
|
||||||
# Run interactive test harness
|
# Run interactive test harness
|
||||||
|
|
@ -249,57 +217,34 @@ make appimage # AppImage
|
||||||
|
|
||||||
---
|
---
|
||||||
|
|
||||||
## Make Targets
|
|
||||||
|
|
||||||
| Target | Description |
|
|
||||||
|--------|-------------|
|
|
||||||
| `make build-all` | Build all backends (uinput + X11 + Wayland) |
|
|
||||||
| `make build-ui` | Build tray icon UI |
|
|
||||||
| `make test` | Run all tests |
|
|
||||||
| `make run` | Run daemon (debug, requires root) |
|
|
||||||
| `make deb` | Build .deb package |
|
|
||||||
| `make appimage` | Build AppImage package |
|
|
||||||
| `make install` | Install binaries to `/usr/local/bin` |
|
|
||||||
| `make install-ui` | Install tray icon |
|
|
||||||
| `make clean` | Clean build artifacts |
|
|
||||||
| `make fmt` | Format code |
|
|
||||||
| `make lint` | Run clippy |
|
|
||||||
|
|
||||||
---
|
|
||||||
|
|
||||||
## Project Structure
|
## Project Structure
|
||||||
|
|
||||||
```
|
```
|
||||||
viet+/
|
vietc/
|
||||||
├── engine/ # Core IME engine (Telex + VNI)
|
├── engine/ # Core IME engine (Telex + VNI)
|
||||||
│ ├── src/
|
│ ├── src/
|
||||||
│ │ ├── engine.rs # Main engine orchestrator
|
│ │ ├── engine.rs # Main engine + replay_keystrokes()
|
||||||
│ │ ├── telex.rs # Telex state machine
|
│ │ ├── telex.rs # Telex state machine
|
||||||
│ │ ├── vni.rs # VNI engine
|
│ │ ├── vni.rs # VNI engine
|
||||||
│ │ ├── english.rs # English auto-restore dictionary
|
│ │ ├── english.rs # English auto-restore dictionary
|
||||||
│ │ └── tests.rs # 162+ unit tests
|
│ │ └── tests/ # 255+ unit tests
|
||||||
│ └── Cargo.toml
|
│ └── Cargo.toml
|
||||||
├── protocol/ # Injection backends
|
├── protocol/ # Injection backends
|
||||||
│ ├── src/
|
│ ├── src/
|
||||||
│ │ ├── inject.rs # KeyInjector trait
|
│ │ ├── inject.rs # KeyInjector trait
|
||||||
│ │ ├── uinput_monitor.rs # Universal uinput+ydotool backend
|
│ │ ├── x11_capture.rs # X11 keyboard capture (XGrabKeyboard)
|
||||||
│ │ ├── x11_inject.rs # X11 XTEST fallback
|
│ │ ├── x11_inject.rs # Direct X11 clipboard + XTest injection
|
||||||
│ │ └── wayland_im.rs # Wayland IM context
|
│ │ └── wayland_im.rs # Wayland IM protocol
|
||||||
│ └── Cargo.toml
|
│ └── Cargo.toml
|
||||||
├── daemon/ # Background daemon
|
├── daemon/ # Background daemon
|
||||||
│ ├── src/
|
│ ├── src/
|
||||||
│ │ ├── main.rs # Evdev loop, hot-reload
|
│ │ ├── main.rs # Event loop, Backspace-Replay integration
|
||||||
│ │ ├── config.rs # TOML config loader
|
│ │ ├── config.rs # TOML config loader
|
||||||
│ │ ├── app_state.rs # Per-app state manager
|
│ │ ├── app_state.rs # Per-app state manager
|
||||||
│ │ └── display.rs # Display server detection
|
│ │ └── display.rs # Display server detection
|
||||||
│ └── Cargo.toml
|
│ └── Cargo.toml
|
||||||
├── cli/ # Interactive test harness
|
├── cli/ # Interactive test harness
|
||||||
├── ui/ # Tray icon application
|
├── ui/ # Tray icon application
|
||||||
│ ├── src/
|
|
||||||
│ │ ├── main.rs # Tray app entry point
|
|
||||||
│ │ ├── tray.rs # System tray icon implementation
|
|
||||||
│ │ └── config.rs # UI config reader
|
|
||||||
│ └── Cargo.toml
|
|
||||||
├── packaging/ # Distribution packages
|
├── packaging/ # Distribution packages
|
||||||
│ ├── appimage/ # AppImage build scripts
|
│ ├── appimage/ # AppImage build scripts
|
||||||
│ └── deb/ # .deb package build scripts
|
│ └── deb/ # .deb package build scripts
|
||||||
|
|
@ -311,6 +256,12 @@ viet+/
|
||||||
|
|
||||||
---
|
---
|
||||||
|
|
||||||
|
## Changelog
|
||||||
|
|
||||||
|
See [CHANGELOG.md](CHANGELOG.md) for release history.
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
## License
|
## License
|
||||||
|
|
||||||
MIT License - see [LICENSE](LICENSE) for details.
|
MIT License - see [LICENSE](LICENSE) for details.
|
||||||
|
|
|
||||||
Loading…
Reference in a new issue