release: v0.1.6 — uinput-first injection, window-switch fix, Telex disabled
- uinput injection is now primary on X11 (XTest fallback) - X11 XTest keycode offset +8 fixed for all send_keycode paths - Window switch detection on every keystroke (no more gap > 100ms guard) - Telex greyed out in tray with '(next version)' label - Flatpak and AppImage removed; only .deb packaging - All Cargo.toml versions bumped to 0.1.6
This commit is contained in:
parent
7d0b2e520c
commit
88d39b4475
25 changed files with 260 additions and 927 deletions
20
.github/workflows/build.yml
vendored
20
.github/workflows/build.yml
vendored
|
|
@ -1,9 +1,9 @@
|
||||||
name: Build & Release
|
name: Build & Release
|
||||||
|
|
||||||
# Builds the .deb and AppImage on the CI runner so artifacts are produced
|
# Builds the .deb on the CI runner so artifacts are produced
|
||||||
# without compiling on a local machine:
|
# without compiling on a local machine:
|
||||||
# - every push to main / pull request -> packages uploaded as workflow artifacts
|
# - every push to main / pull request -> packages uploaded as workflow artifacts
|
||||||
# - pushing a `v*` tag -> a GitHub Release with the .deb + AppImage
|
# - pushing a `v*` tag -> a GitHub Release with the .deb
|
||||||
on:
|
on:
|
||||||
push:
|
push:
|
||||||
branches: [main]
|
branches: [main]
|
||||||
|
|
@ -40,7 +40,7 @@ jobs:
|
||||||
run: cargo test --release
|
run: cargo test --release
|
||||||
|
|
||||||
package:
|
package:
|
||||||
name: Build packages
|
name: Build .deb
|
||||||
needs: test
|
needs: test
|
||||||
runs-on: ubuntu-latest
|
runs-on: ubuntu-latest
|
||||||
steps:
|
steps:
|
||||||
|
|
@ -71,31 +71,19 @@ jobs:
|
||||||
echo "short_sha=$(git rev-parse --short HEAD)" >> "$GITHUB_OUTPUT"
|
echo "short_sha=$(git rev-parse --short HEAD)" >> "$GITHUB_OUTPUT"
|
||||||
echo "Building version $VERSION"
|
echo "Building version $VERSION"
|
||||||
|
|
||||||
- name: Fetch appimagetool
|
|
||||||
run: |
|
|
||||||
curl -fsSL -o packaging/appimage/appimagetool \
|
|
||||||
https://github.com/AppImage/AppImageKit/releases/download/continuous/appimagetool-x86_64.AppImage
|
|
||||||
chmod +x packaging/appimage/appimagetool
|
|
||||||
|
|
||||||
- name: Build .deb
|
- name: Build .deb
|
||||||
run: bash packaging/deb/build-deb.sh "${{ steps.ver.outputs.version }}"
|
run: bash packaging/deb/build-deb.sh "${{ steps.ver.outputs.version }}"
|
||||||
|
|
||||||
- name: Build AppImage
|
|
||||||
# appimagetool is invoked with --appimage-extract-and-run by the build
|
|
||||||
# script, so no FUSE is required on the runner.
|
|
||||||
run: bash packaging/appimage/build-appimage.sh "${{ steps.ver.outputs.version }}"
|
|
||||||
|
|
||||||
- name: Collect artifacts
|
- name: Collect artifacts
|
||||||
run: |
|
run: |
|
||||||
mkdir -p dist
|
mkdir -p dist
|
||||||
cp packaging/deb/*.deb dist/
|
cp packaging/deb/*.deb dist/
|
||||||
cp packaging/appimage/*.AppImage dist/
|
|
||||||
ls -la dist
|
ls -la dist
|
||||||
|
|
||||||
- name: Upload artifacts
|
- name: Upload artifacts
|
||||||
uses: actions/upload-artifact@v4
|
uses: actions/upload-artifact@v4
|
||||||
with:
|
with:
|
||||||
name: vietc-packages-${{ steps.ver.outputs.version }}-${{ steps.ver.outputs.short_sha }}
|
name: vietc-deb-${{ steps.ver.outputs.version }}-${{ steps.ver.outputs.short_sha }}
|
||||||
path: dist/*
|
path: dist/*
|
||||||
if-no-files-found: error
|
if-no-files-found: error
|
||||||
|
|
||||||
|
|
|
||||||
25
CHANGELOG.md
25
CHANGELOG.md
|
|
@ -1,5 +1,30 @@
|
||||||
# Changelog
|
# Changelog
|
||||||
|
|
||||||
|
## 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).
|
||||||
|
|
||||||
|
### 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.
|
||||||
|
|
||||||
|
### 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.
|
||||||
|
|
||||||
|
### 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).
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
## v0.1.5 (2026-06-29)
|
## v0.1.5 (2026-06-29)
|
||||||
|
|
||||||
### Window-Switch Engine Reset
|
### Window-Switch Engine Reset
|
||||||
|
|
|
||||||
19
Makefile
19
Makefile
|
|
@ -1,4 +1,4 @@
|
||||||
.PHONY: build build-x11 build-wayland build-all build-ui test test-cli run run-x11 run-wayland clean install install-x11 install-wayland install-ui install-config appimage deb fmt lint tree
|
.PHONY: build build-x11 build-wayland build-all build-ui test test-cli run run-x11 run-wayland clean install install-x11 install-wayland install-ui install-config deb fmt lint tree
|
||||||
|
|
||||||
# Build core crates
|
# Build core crates
|
||||||
build:
|
build:
|
||||||
|
|
@ -76,26 +76,15 @@ install-config:
|
||||||
cp vietc.toml ~/.config/vietc/config.toml
|
cp vietc.toml ~/.config/vietc/config.toml
|
||||||
@echo "Config installed to ~/.config/vietc/config.toml"
|
@echo "Config installed to ~/.config/vietc/config.toml"
|
||||||
|
|
||||||
# Build .deb package (requires dpkg-deb)
|
# Build .deb package
|
||||||
deb:
|
deb:
|
||||||
VERSION=$$(grep '^version' engine/Cargo.toml | head -1 | sed 's/.*"\(.*\)"/\1/') && \
|
VERSION=$$(grep '^version' engine/Cargo.toml | head -1 | sed 's/.*"\(.*\)"/\1/') && \
|
||||||
bash packaging/deb/build-deb.sh "$$VERSION"
|
bash packaging/deb/build-deb.sh "$$VERSION"
|
||||||
|
|
||||||
# Build AppImage (requires appimagetool or linuxdeploy)
|
|
||||||
appimage:
|
|
||||||
VERSION=$$(grep '^version' engine/Cargo.toml | head -1 | sed 's/.*"\(.*\)"/\1/') && \
|
|
||||||
bash packaging/appimage/build-appimage.sh "$$VERSION"
|
|
||||||
|
|
||||||
# Build Debian package
|
|
||||||
deb:
|
|
||||||
VERSION=$$(grep '^version' engine/Cargo.toml | head -1 | sed 's/.*"\(.*\)"/\1/') && \
|
|
||||||
bash packaging/build-deb.sh "$$VERSION"
|
|
||||||
|
|
||||||
# Clean build artifacts
|
# Clean build artifacts
|
||||||
clean:
|
clean:
|
||||||
cargo clean
|
cargo clean
|
||||||
cd ui && cargo clean
|
cd ui && cargo clean
|
||||||
rm -rf packaging/appimage/AppDir packaging/appimage/*.AppImage
|
|
||||||
|
|
||||||
# Format code
|
# Format code
|
||||||
fmt:
|
fmt:
|
||||||
|
|
@ -107,10 +96,6 @@ lint:
|
||||||
cargo clippy -- -D warnings
|
cargo clippy -- -D warnings
|
||||||
cd ui && cargo clippy -- -D warnings
|
cd ui && cargo clippy -- -D warnings
|
||||||
|
|
||||||
# Flatpak build
|
|
||||||
flatpak:
|
|
||||||
cd packaging/flatpak && bash build-flatpak.sh
|
|
||||||
|
|
||||||
# Show project structure
|
# Show project structure
|
||||||
tree:
|
tree:
|
||||||
@find . -type f \( -name "*.rs" -o -name "*.toml" \) | grep -v target | sort
|
@find . -type f \( -name "*.rs" -o -name "*.toml" \) | grep -v target | sort
|
||||||
|
|
|
||||||
103
README.md
103
README.md
|
|
@ -2,7 +2,7 @@
|
||||||
<img src="https://img.shields.io/badge/Platform-Linux-blue?style=for-the-badge" alt="Platform">
|
<img src="https://img.shields.io/badge/Platform-Linux-blue?style=for-the-badge" alt="Platform">
|
||||||
<img src="https://img.shields.io/badge/Language-Rust-orange?style=for-the-badge" alt="Rust">
|
<img src="https://img.shields.io/badge/Language-Rust-orange?style=for-the-badge" alt="Rust">
|
||||||
<img src="https://img.shields.io/badge/License-MIT-green?style=for-the-badge" alt="License">
|
<img src="https://img.shields.io/badge/License-MIT-green?style=for-the-badge" alt="License">
|
||||||
<img src="https://img.shields.io/badge/Version-0.1.5-purple?style=for-the-badge" alt="Version">
|
<img src="https://img.shields.io/badge/Version-0.1.6-purple?style=for-the-badge" alt="Version">
|
||||||
<img src="https://img.shields.io/badge/Tests-106_passing-brightgreen?style=for-the-badge" alt="Tests">
|
<img src="https://img.shields.io/badge/Tests-106_passing-brightgreen?style=for-the-badge" alt="Tests">
|
||||||
<img src="https://img.shields.io/badge/Event_Sourcing-✓-blueviolet?style=for-the-badge" alt="Event Sourcing">
|
<img src="https://img.shields.io/badge/Event_Sourcing-✓-blueviolet?style=for-the-badge" alt="Event Sourcing">
|
||||||
</p>
|
</p>
|
||||||
|
|
@ -50,8 +50,8 @@ Physical Keyboard
|
||||||
│ X11: XRecord passive monitoring (fallback) │
|
│ X11: XRecord passive monitoring (fallback) │
|
||||||
│ │
|
│ │
|
||||||
│ ┌─────────────┐ ┌──────────────┐ ┌──────────────────┐ │
|
│ ┌─────────────┐ ┌──────────────┐ ┌──────────────────┐ │
|
||||||
│ │ evdev grab │ │ X11Capture │ │ FocusIn/FocusOut │ │
|
│ │ evdev grab │ │ X11Capture │ │ Window switch │ │
|
||||||
│ │ (libevdev) │ │ (XRecord) │ │ detection │ │
|
│ │ (libevdev) │ │ (XRecord) │ │ detection (250ms)│ │
|
||||||
│ └─────────────┘ └──────────────┘ └──────────────────┘ │
|
│ └─────────────┘ └──────────────┘ └──────────────────┘ │
|
||||||
└──────────────────────────────────────────────────────────────┘
|
└──────────────────────────────────────────────────────────────┘
|
||||||
│
|
│
|
||||||
|
|
@ -63,7 +63,7 @@ Physical Keyboard
|
||||||
│ Ctrl+Space → toggle Vietnamese ON/OFF │
|
│ Ctrl+Space → toggle Vietnamese ON/OFF │
|
||||||
│ Backspace → replay_backspace() │
|
│ Backspace → replay_backspace() │
|
||||||
│ Characters → replay_and_inject(ch) │
|
│ Characters → replay_and_inject(ch) │
|
||||||
│ VNI/Telex control keys → consume when no match │
|
│ VNI control keys → consume when no match │
|
||||||
└──────────────────────────────────────────────────────────────┘
|
└──────────────────────────────────────────────────────────────┘
|
||||||
│
|
│
|
||||||
▼
|
▼
|
||||||
|
|
@ -81,13 +81,15 @@ Physical Keyboard
|
||||||
┌──────────────────────────────────────────────────────────────┐
|
┌──────────────────────────────────────────────────────────────┐
|
||||||
│ Stage 4: KEY INJECTION │
|
│ 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 │
|
│ ASCII: direct Linux keycodes via /dev/uinput │
|
||||||
│ Backspace: Linux keycode 14 via uinput │
|
│ Backspace: Linux keycode 14 via uinput │
|
||||||
│ Vietnamese Unicode: clipboard paste + trailing ASCII via │
|
│ Vietnamese Unicode: clipboard paste + trailing ASCII via │
|
||||||
│ uinput (split only at whitespace/punctuation boundary) │
|
│ uinput (split only at whitespace/punctuation boundary) │
|
||||||
│ uinput Ctrl+V via /dev/uinput (no X11 dependency) │
|
│ uinput Ctrl+V via /dev/uinput (no X11 dependency) │
|
||||||
│ │
|
│ │
|
||||||
│ Fallback: vietc-uinputd Unix socket daemon (privileged) │
|
│ Fallback: X11 XTest injection (X11 keycodes = evdev + 8) │
|
||||||
└──────────────────────────────────────────────────────────────┘
|
└──────────────────────────────────────────────────────────────┘
|
||||||
│
|
│
|
||||||
▼
|
▼
|
||||||
|
|
@ -133,14 +135,14 @@ vietc/
|
||||||
│ ├── engine.rs # Orchestrator + replay_events(), replay_events_to_commands()
|
│ ├── engine.rs # Orchestrator + replay_events(), replay_events_to_commands()
|
||||||
│ ├── event.rs # Event Sourcing: InputEvent, EventStore, Command
|
│ ├── event.rs # Event Sourcing: InputEvent, EventStore, Command
|
||||||
│ ├── bamboo.rs # Bamboo engine: transformation model, composition, tone placement
|
│ ├── bamboo.rs # Bamboo engine: transformation model, composition, tone placement
|
||||||
│ ├── input_method.rs # Telex/VNI rule definitions
|
│ ├── input_method.rs # VNI rule definitions
|
||||||
│ └── spelling.rs # Vietnamese syllable validation
|
│ └── spelling.rs # Vietnamese syllable validation
|
||||||
│
|
│
|
||||||
├── protocol/ # Keyboard capture & injection
|
├── protocol/ # Keyboard capture & injection
|
||||||
│ ├── inject.rs # KeyInjector trait
|
│ ├── inject.rs # KeyInjector trait
|
||||||
│ ├── x11_capture.rs # XRecord keyboard capture via C helper
|
│ ├── x11_capture.rs # XRecord keyboard capture via C helper
|
||||||
│ ├── x11_inject.rs # XTest injection + direct clipboard
|
│ ├── x11_inject.rs # XTest injection (fallback)
|
||||||
│ ├── uinput_monitor.rs # /dev/uinput injection for ASCII + Unicode
|
│ ├── uinput_monitor.rs # /dev/uinput injection (primary)
|
||||||
│ ├── uinput_client.rs # Unix socket client for vietc-uinputd
|
│ ├── uinput_client.rs # Unix socket client for vietc-uinputd
|
||||||
│ └── wayland_im.rs # Wayland IM protocol
|
│ └── wayland_im.rs # Wayland IM protocol
|
||||||
│
|
│
|
||||||
|
|
@ -157,7 +159,7 @@ vietc/
|
||||||
│ └── main.rs # Tray + daemon launcher
|
│ └── main.rs # Tray + daemon launcher
|
||||||
│
|
│
|
||||||
├── cli/ # Interactive test harness
|
├── cli/ # Interactive test harness
|
||||||
├── packaging/ # AppImage + deb build scripts
|
├── packaging/ # .deb packaging scripts
|
||||||
└── vietc.toml # Default configuration
|
└── vietc.toml # Default configuration
|
||||||
```
|
```
|
||||||
|
|
||||||
|
|
@ -166,12 +168,12 @@ vietc/
|
||||||
```
|
```
|
||||||
┌─────────────────────────────────────────────────────────────┐
|
┌─────────────────────────────────────────────────────────────┐
|
||||||
│ vietc-tray │
|
│ vietc-tray │
|
||||||
│ (System tray icon, daemon launcher, password prompt) │
|
│ (System tray icon, daemon launcher) │
|
||||||
└───────────────────────┬─────────────────────────────────────┘
|
└───────────────────────┬─────────────────────────────────────┘
|
||||||
│ starts
|
│ starts
|
||||||
▼
|
▼
|
||||||
┌─────────────────────────────────────────────────────────────┐
|
┌─────────────────────────────────────────────────────────────┐
|
||||||
│ vietc (daemon) │
|
│ vietc-daemon │
|
||||||
│ │
|
│ │
|
||||||
│ ┌──────────────┐ ┌──────────────┐ ┌──────────────────┐ │
|
│ ┌──────────────┐ ┌──────────────┐ ┌──────────────────┐ │
|
||||||
│ │ Config │ │ App State │ │ Display │ │
|
│ │ Config │ │ App State │ │ Display │ │
|
||||||
|
|
@ -183,7 +185,7 @@ vietc/
|
||||||
│ ┌──────▼──────┐ │
|
│ ┌──────▼──────┐ │
|
||||||
│ │ Event Loop │ │
|
│ │ Event Loop │ │
|
||||||
│ │ │ │
|
│ │ │ │
|
||||||
│ │ X11: grab │ │
|
│ │ evdev: grab │ │
|
||||||
│ │ keyboard │ │
|
│ │ keyboard │ │
|
||||||
│ │ │ │
|
│ │ │ │
|
||||||
│ │ Process │ │
|
│ │ Process │ │
|
||||||
|
|
@ -198,12 +200,12 @@ vietc/
|
||||||
│ │
|
│ │
|
||||||
│ ┌────────────────────────────────────────────────────────┐ │
|
│ ┌────────────────────────────────────────────────────────┐ │
|
||||||
│ │ vietc-engine │ │
|
│ │ vietc-engine │ │
|
||||||
│ │ TelexEngine / VniEngine / EnglishDict / Spelling │ │
|
│ │ VniEngine / EnglishDict / Spelling │ │
|
||||||
│ └────────────────────────────────────────────────────────┘ │
|
│ └────────────────────────────────────────────────────────┘ │
|
||||||
│ │
|
│ │
|
||||||
│ ┌────────────────────────────────────────────────────────┐ │
|
│ ┌────────────────────────────────────────────────────────┐ │
|
||||||
│ │ vietc-protocol │ │
|
│ │ vietc-protocol │ │
|
||||||
│ │ X11Capture / X11Injector / UinputInjector / Wayland │ │
|
│ │ UinputInjector / X11Injector / X11Capture / Wayland │ │
|
||||||
│ └────────────────────────────────────────────────────────┘ │
|
│ └────────────────────────────────────────────────────────┘ │
|
||||||
└─────────────────────────────────────────────────────────────┘
|
└─────────────────────────────────────────────────────────────┘
|
||||||
```
|
```
|
||||||
|
|
@ -212,24 +214,7 @@ vietc/
|
||||||
|
|
||||||
## Input Methods
|
## Input Methods
|
||||||
|
|
||||||
### Telex
|
### VNI (default, Telex coming in next version)
|
||||||
|
|
||||||
| Key | Result | Example |
|
|
||||||
|-----|--------|---------|
|
|
||||||
| `aa` | â | `tan` → `tân` |
|
|
||||||
| `aw` | ă | `tan` → `tăn` |
|
|
||||||
| `ee` | ê | `men` → `mên` |
|
|
||||||
| `oo` | ô | `to` → `tô` |
|
|
||||||
| `ow` | ơ | `to` → `tơ` |
|
|
||||||
| `uw` | ư | `tu` → `tư` |
|
|
||||||
| `s` | á (sắc) | `as` → `á` |
|
|
||||||
| `f` | à (huyền) | `af` → `à` |
|
|
||||||
| `r` | ả (hỏi) | `ar` → `ả` |
|
|
||||||
| `x` | ã (ngã) | `ax` → `ã` |
|
|
||||||
| `j` | ạ (nặng) | `aj` → `ạ` |
|
|
||||||
| `dd` | đ | `dd` → `đ` |
|
|
||||||
|
|
||||||
### VNI
|
|
||||||
|
|
||||||
| Key | Result | Example |
|
| Key | Result | Example |
|
||||||
|-----|--------|---------|
|
|-----|--------|---------|
|
||||||
|
|
@ -251,59 +236,51 @@ Flexible typing: type the full syllable, then add marks/tone keys at the end. Ex
|
||||||
|
|
||||||
| Feature | How It Works |
|
| Feature | How It Works |
|
||||||
|---------|-------------|
|
|---------|-------------|
|
||||||
| **Direct Input** | No pre-edit buffer. Keystrokes instantly become text via uinput/XTest injection |
|
| **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 |
|
| **Bamboo Engine** | Transformation model ported from bamboo-core — composition, marks, tones, flexible backtracking |
|
||||||
| **Flexible Backtrack** | Type tone/modifier at end of syllable (`tranaf` → `trần`). Scans up to 5 chars backward |
|
| **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`) |
|
| **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) |
|
| **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 |
|
| **Macro Expansion** | `ko` → `không`, `dc` → `được`, custom shortcuts |
|
||||||
| **Casing Preservation** | `Tieengs` → `Tiếng`, `TIEENGS` → `TIẾNG` |
|
| **Casing Preservation** | `Tieengs` → `Tiếng`, `TIEENGS` → `TIẾNG` |
|
||||||
| **App Memory** | Per-app Vietnamese/English state, saved to `overrides.toml` |
|
| **App Memory** | Per-app Vietnamese/English state, saved to `overrides.toml` |
|
||||||
| **Hot Reload** | Config changes apply without restart (polls mtime every 1.5s) |
|
| **Hot Reload** | Config changes apply without restart (polls mtime every 1.5s) |
|
||||||
| **Window-Switch Reset** | Alt+Tab clears engine state — no stale composition across apps, even when focus events are missed. Uses `xdotool` or `xprop` fallback to detect window changes |
|
| **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 |
|
| **CPU Priority** | Pins daemon to P-cores (0-3) + nice(-10) for low-latency input |
|
||||||
| **Uinput Daemon** | Privileged `vietc-uinputd` for clean backspace injection (Unix socket, VMK-style) |
|
| **Uinput Injection** | Uses `/dev/uinput` for reliable keyboard injection without X11 dependency. Falls back to XTest on systems without uinput access |
|
||||||
|
|
||||||
---
|
---
|
||||||
|
|
||||||
## Installation
|
## Installation
|
||||||
|
|
||||||
### Flatpak (recommended)
|
### Debian Package (recommended)
|
||||||
|
|
||||||
System tray icon + daemon. Find **"Viet+"** in your app menu to launch, or run from terminal.
|
System tray icon + daemon + desktop entry. Requires user to be in the `input` group for keyboard capture.
|
||||||
|
|
||||||
```bash
|
```bash
|
||||||
# Install
|
# Install
|
||||||
flatpak install --user --bundle VietPlus-x86_64.flatpak
|
sudo dpkg -i vietc_0.1.6-1_amd64.deb
|
||||||
|
|
||||||
# Launch via app menu, or:
|
# Log out and log back in (for input group membership to take effect)
|
||||||
flatpak run io.github.vietc.VietPlus
|
# Then launch "Viet+" from your application menu
|
||||||
|
|
||||||
# Uninstall
|
|
||||||
flatpak uninstall --user io.github.vietc.VietPlus
|
|
||||||
```
|
```
|
||||||
|
|
||||||
Includes daemon + CLI + system tray + uinput daemon. Sandboxed — no system libraries are touched.
|
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 (Flatpak)
|
### Build from Source
|
||||||
|
|
||||||
```bash
|
```bash
|
||||||
git clone https://github.com/vndangkhoa/vietc.git
|
git clone https://github.com/vndangkhoa/vietc.git
|
||||||
cd vietc/packaging/flatpak
|
cd vietc
|
||||||
bash build-flatpak.sh [version]
|
make deb
|
||||||
|
sudo dpkg -i packaging/deb/vietc_0.1.6-1_amd64.deb
|
||||||
```
|
```
|
||||||
|
|
||||||
Requires Flatpak runtimes: `org.gnome.Platform//50`, `org.gnome.Sdk//50`, `org.freedesktop.Sdk.Extension.rust-stable//25.08`
|
Requires Rust toolchain, `pkg-config`, `libx11-dev`, `libxtst-dev`, `libevdev-dev`. See `packaging/deb/build-deb.sh` for details.
|
||||||
|
|
||||||
```bash
|
|
||||||
flatpak install --user flathub org.gnome.Platform//50
|
|
||||||
flatpak install --user flathub org.gnome.Sdk//50
|
|
||||||
flatpak install --user flathub org.freedesktop.Sdk.Extension.rust-stable//25.08
|
|
||||||
```
|
|
||||||
|
|
||||||
The Flatpak bundle includes the system tray and desktop menu entry. Find **"Viet+"** in your app launcher to start it, or search for it to uninstall. Warning-free build — no `#![allow()]` needed.
|
|
||||||
|
|
||||||
See `packaging/flatpak/FLATPAK_BUILD.md` for detailed build instructions.
|
|
||||||
|
|
||||||
---
|
---
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -3,7 +3,7 @@
|
||||||
## When to release
|
## When to release
|
||||||
|
|
||||||
- New feature or bugfix that should be distributed to users
|
- New feature or bugfix that should be distributed to users
|
||||||
- Flatpak build changes validated
|
- .deb packaging changes validated
|
||||||
- All tests passing (`cargo test`)
|
- All tests passing (`cargo test`)
|
||||||
|
|
||||||
---
|
---
|
||||||
|
|
@ -38,25 +38,29 @@ Add a new entry under the version heading:
|
||||||
- behavior changes...
|
- behavior changes...
|
||||||
```
|
```
|
||||||
|
|
||||||
### 3. Build the Flatpak
|
### 3. Build the .deb
|
||||||
|
|
||||||
```bash
|
```bash
|
||||||
cd packaging/flatpak
|
make deb
|
||||||
bash build-flatpak.sh X.Y.Z
|
|
||||||
```
|
```
|
||||||
|
|
||||||
Verify the bundle was created:
|
Verify the package was created:
|
||||||
```bash
|
|
||||||
ls -lh VietPlus-X.Y.Z.flatpak
|
|
||||||
```
|
|
||||||
|
|
||||||
### 4. Test the Flatpak
|
|
||||||
|
|
||||||
```bash
|
```bash
|
||||||
flatpak install --user --bundle VietPlus-X.Y.Z.flatpak
|
ls -lh packaging/deb/vietc_*.deb
|
||||||
flatpak run io.github.vietc.VietPlus
|
|
||||||
```
|
```
|
||||||
|
|
||||||
|
### 4. Install & test
|
||||||
|
|
||||||
|
```bash
|
||||||
|
sudo dpkg -i packaging/deb/vietc_X.Y.Z-1_amd64.deb
|
||||||
|
```
|
||||||
|
|
||||||
|
Test:
|
||||||
|
- Search "Viet+" in the application menu — the tray icon entry should appear
|
||||||
|
- Launch from menu — tray icon should show, Vietnamese input should work (VNI, Ctrl+Space to toggle)
|
||||||
|
- The tray should autostart on next login (XDG autostart installed)
|
||||||
|
|
||||||
### 5. Commit and push
|
### 5. Commit and push
|
||||||
|
|
||||||
```bash
|
```bash
|
||||||
|
|
@ -67,12 +71,7 @@ git push origin main
|
||||||
|
|
||||||
### 6. Create a release on Forgejo/GitHub
|
### 6. Create a release on Forgejo/GitHub
|
||||||
|
|
||||||
Attach the Flatpak bundle (`VietPlus-X.Y.Z.flatpak`) as a release asset.
|
Attach the .deb package (`vietc_X.Y.Z-1_amd64.deb`) as a release asset.
|
||||||
|
|
||||||
```bash
|
|
||||||
# Using forgejo-release (if configured)
|
|
||||||
# Or manually upload via the web UI
|
|
||||||
```
|
|
||||||
|
|
||||||
---
|
---
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -1,6 +1,6 @@
|
||||||
[package]
|
[package]
|
||||||
name = "vietc-cli"
|
name = "vietc-cli"
|
||||||
version = "0.1.0"
|
version = "0.1.6"
|
||||||
edition = "2021"
|
edition = "2021"
|
||||||
description = "Viet+ CLI Test Harness"
|
description = "Viet+ CLI Test Harness"
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -1,6 +1,6 @@
|
||||||
[package]
|
[package]
|
||||||
name = "vietc-daemon"
|
name = "vietc-daemon"
|
||||||
version = "0.1.0"
|
version = "0.1.6"
|
||||||
edition = "2021"
|
edition = "2021"
|
||||||
description = "Viet+ background daemon"
|
description = "Viet+ background daemon"
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -196,7 +196,7 @@ impl Default for Config {
|
||||||
auto_restore: AutoRestoreConfig::default(),
|
auto_restore: AutoRestoreConfig::default(),
|
||||||
app_state: AppStateConfig::default(),
|
app_state: AppStateConfig::default(),
|
||||||
macros,
|
macros,
|
||||||
grab: false,
|
grab: false, // default false so daemon works without root (needs input group for uinput)
|
||||||
debug: false,
|
debug: false,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -1027,14 +1027,13 @@ fn run_with_evdev(
|
||||||
|
|
||||||
if active_window_id != last_active_window {
|
if active_window_id != last_active_window {
|
||||||
new_window = Some(active_window_id.clone());
|
new_window = Some(active_window_id.clone());
|
||||||
} else if gap > std::time::Duration::from_millis(100) {
|
} else {
|
||||||
// Background thread hasn't caught up yet — poll xdotool directly
|
// Always verify active window on every keypress — window
|
||||||
|
// switches under 100ms can leak the old engine buffer.
|
||||||
if let Some(id) = app_state::get_active_window_id() {
|
if let Some(id) = app_state::get_active_window_id() {
|
||||||
if id != active_window_id {
|
if id != active_window_id {
|
||||||
new_window = Some(id);
|
new_window = Some(id);
|
||||||
}
|
}
|
||||||
} else {
|
|
||||||
log_info(&format!("[vietc] gap poll: window ID query failed (gap={:?}, shared='{}')", gap, active_window_id));
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -1264,22 +1263,15 @@ fn execute_commands(
|
||||||
std::thread::sleep(std::time::Duration::from_millis(20));
|
std::thread::sleep(std::time::Duration::from_millis(20));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
fn create_injector(
|
fn create_injector(
|
||||||
display: display::DisplayServer,
|
display: display::DisplayServer,
|
||||||
) -> Result<Box<dyn vietc_protocol::KeyInjector>, Box<dyn std::error::Error>> {
|
) -> Result<Box<dyn vietc_protocol::KeyInjector>, Box<dyn std::error::Error>> {
|
||||||
// Try uinputd socket first
|
// Prefer uinput injection — uses correct Linux keycodes for backspace
|
||||||
if vietc_protocol::uinput_client::UinputClient::is_available() {
|
// and ASCII, works on both X11 and Wayland (uinput devices are routed
|
||||||
log_info("[vietc] Using uinputd socket injection");
|
// through libinput on modern X11).
|
||||||
return Ok(Box::new(vietc_protocol::uinput_client::UinputClient));
|
|
||||||
}
|
|
||||||
|
|
||||||
// Use uinput as primary — correct Linux keycodes for backspace + ASCII.
|
|
||||||
// For Unicode (Vietnamese diacritics), falls back to X11 clipboard via
|
|
||||||
// direct X11 API (not subprocesses), making it work in Flatpak sandboxes.
|
|
||||||
match vietc_protocol::uinput_monitor::UinputInjector::new("vietc") {
|
match vietc_protocol::uinput_monitor::UinputInjector::new("vietc") {
|
||||||
Ok(injector) => {
|
Ok(injector) => {
|
||||||
log_info("[vietc] Using uinput injection (primary)");
|
log_info("[vietc] Using uinput injection");
|
||||||
return Ok(Box::new(injector));
|
return Ok(Box::new(injector));
|
||||||
}
|
}
|
||||||
Err(e) => {
|
Err(e) => {
|
||||||
|
|
@ -1287,9 +1279,15 @@ fn create_injector(
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// Fall back to X11 injection (only if uinput fails)
|
// Try uinputd socket
|
||||||
|
if vietc_protocol::uinput_client::UinputClient::is_available() {
|
||||||
|
log_info("[vietc] Using uinputd socket injection");
|
||||||
|
return Ok(Box::new(vietc_protocol::uinput_client::UinputClient));
|
||||||
|
}
|
||||||
|
|
||||||
|
// Fall back to X11 injection (XTest) — uses X11 keycodes, only for
|
||||||
|
// systems where uinput/unix socket injection is unavailable.
|
||||||
#[cfg(feature = "x11")]
|
#[cfg(feature = "x11")]
|
||||||
{
|
|
||||||
if display != display::DisplayServer::Wayland {
|
if display != display::DisplayServer::Wayland {
|
||||||
match vietc_protocol::x11_inject::X11Injector::new() {
|
match vietc_protocol::x11_inject::X11Injector::new() {
|
||||||
Ok(injector) => {
|
Ok(injector) => {
|
||||||
|
|
@ -1301,7 +1299,6 @@ fn create_injector(
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
|
||||||
|
|
||||||
Err("No injection backend available".into())
|
Err("No injection backend available".into())
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -1,6 +1,6 @@
|
||||||
[package]
|
[package]
|
||||||
name = "vietc-engine"
|
name = "vietc-engine"
|
||||||
version = "0.1.0"
|
version = "0.1.6"
|
||||||
edition = "2021"
|
edition = "2021"
|
||||||
description = "Viet+ Vietnamese IME Core Engine"
|
description = "Viet+ Vietnamese IME Core Engine"
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -1,397 +0,0 @@
|
||||||
#!/usr/bin/env bash
|
|
||||||
set -euo pipefail
|
|
||||||
|
|
||||||
# Ensure cargo is in PATH
|
|
||||||
if ! command -v cargo &>/dev/null; then
|
|
||||||
if [ -f "$HOME/.cargo/bin/cargo" ]; then
|
|
||||||
export PATH="$HOME/.cargo/bin:$PATH"
|
|
||||||
fi
|
|
||||||
fi
|
|
||||||
|
|
||||||
SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)"
|
|
||||||
PROJECT_ROOT="$(cd "$SCRIPT_DIR/../.." && pwd)"
|
|
||||||
APPDIR="$SCRIPT_DIR/AppDir"
|
|
||||||
VERSION="${1:-0.1.1}"
|
|
||||||
|
|
||||||
echo "=== Building Viet+ AppImage v${VERSION} ==="
|
|
||||||
|
|
||||||
# Clean
|
|
||||||
rm -rf "$APPDIR"
|
|
||||||
mkdir -p "$APPDIR/usr/bin"
|
|
||||||
mkdir -p "$APPDIR/usr/share/applications"
|
|
||||||
mkdir -p "$APPDIR/usr/share/icons/hicolor/256x256/apps"
|
|
||||||
mkdir -p "$APPDIR/usr/share/doc/vietc"
|
|
||||||
mkdir -p "$APPDIR/etc/vietc"
|
|
||||||
mkdir -p "$APPDIR/usr/lib/systemd/user"
|
|
||||||
mkdir -p "$APPDIR/usr/share/metainfo"
|
|
||||||
|
|
||||||
# Build binaries
|
|
||||||
echo "[1/5] Building binaries..."
|
|
||||||
if [ ! -f "target/release/vietc" ]; then
|
|
||||||
cargo build --release
|
|
||||||
cd "$PROJECT_ROOT/ui" && cargo build --release && cd "$PROJECT_ROOT"
|
|
||||||
fi
|
|
||||||
echo " Built with x11 + wayland"
|
|
||||||
|
|
||||||
# Copy binaries from deb-build if they exist, otherwise from target/release
|
|
||||||
echo "[2/5] Installing binaries..."
|
|
||||||
if [ -d "deb-build/usr/bin" ]; then
|
|
||||||
cp -r deb-build/usr/bin/* "$APPDIR/usr/bin/"
|
|
||||||
else
|
|
||||||
cp target/release/vietc "$APPDIR/usr/bin/"
|
|
||||||
cp target/release/vietc-cli "$APPDIR/usr/bin/"
|
|
||||||
cp target/release/vietc-uinputd "$APPDIR/usr/bin/"
|
|
||||||
[ -f ui/target/release/vietc-tray ] && cp ui/target/release/vietc-tray "$APPDIR/usr/bin/"
|
|
||||||
fi
|
|
||||||
|
|
||||||
# Bundle xclip as fallback for clipboard operations
|
|
||||||
echo " Bundling xclip..."
|
|
||||||
if command -v xclip &>/dev/null; then
|
|
||||||
cp "$(which xclip)" "$APPDIR/usr/bin/"
|
|
||||||
echo " xclip bundled"
|
|
||||||
else
|
|
||||||
echo " xclip not found on system, skipping"
|
|
||||||
fi
|
|
||||||
|
|
||||||
# Bundle xdotool for reliable Unicode text injection
|
|
||||||
echo " Bundling xdotool..."
|
|
||||||
if command -v xdotool &>/dev/null; then
|
|
||||||
cp "$(which xdotool)" "$APPDIR/usr/bin/"
|
|
||||||
echo " xdotool bundled"
|
|
||||||
elif [ -f /tmp/xdotool-extract/usr/bin/xdotool ]; then
|
|
||||||
cp /tmp/xdotool-extract/usr/bin/xdotool "$APPDIR/usr/bin/"
|
|
||||||
echo " xdotool bundled (from extract)"
|
|
||||||
else
|
|
||||||
echo " xdotool not found, Unicode falls back to clipboard"
|
|
||||||
fi
|
|
||||||
|
|
||||||
# Compile and bundle vietc-xrecord (C helper for XRecord keyboard capture)
|
|
||||||
echo " Compiling vietc-xrecord..."
|
|
||||||
if command -v gcc &>/dev/null; then
|
|
||||||
gcc -O2 -o "$APPDIR/usr/bin/vietc-xrecord" "$SCRIPT_DIR/vietc-xrecord.c" -lX11 -lXtst
|
|
||||||
echo " vietc-xrecord bundled"
|
|
||||||
elif command -v cc &>/dev/null; then
|
|
||||||
cc -O2 -o "$APPDIR/usr/bin/vietc-xrecord" "$SCRIPT_DIR/vietc-xrecord.c" -lX11 -lXtst
|
|
||||||
echo " vietc-xrecord bundled"
|
|
||||||
else
|
|
||||||
echo " WARNING: No C compiler found, vietc-xrecord not bundled — X11 capture will fail"
|
|
||||||
fi
|
|
||||||
|
|
||||||
# Desktop integration
|
|
||||||
echo "[3/5] Installing desktop integration..."
|
|
||||||
if [ -f "deb-build/vietc.desktop" ]; then
|
|
||||||
cp deb-build/vietc.desktop "$APPDIR/usr/share/applications/"
|
|
||||||
else
|
|
||||||
cp "$SCRIPT_DIR/vietc.desktop" "$APPDIR/usr/share/applications/"
|
|
||||||
fi
|
|
||||||
|
|
||||||
# Icons
|
|
||||||
if [ -f "deb-build/vietc.svg" ]; then
|
|
||||||
cp deb-build/vietc.svg "$APPDIR/usr/share/icons/hicolor/256x256/apps/"
|
|
||||||
cp deb-build/vietc.png "$APPDIR/usr/share/icons/hicolor/256x256/apps/"
|
|
||||||
cp deb-build/vietc.png "$APPDIR/"
|
|
||||||
fi
|
|
||||||
|
|
||||||
# AppStream metadata
|
|
||||||
if [ -f "deb-build/usr/share/metainfo/io.github.anomalyco.vietc.appdata.xml" ]; then
|
|
||||||
cp deb-build/usr/share/metainfo/io.github.anomalyco.vietc.appdata.xml "$APPDIR/usr/share/metainfo/"
|
|
||||||
else
|
|
||||||
cat > "$APPDIR/usr/share/metainfo/io.github.anomalyco.vietc.appdata.xml" << 'XML'
|
|
||||||
<?xml version="1.0" encoding="UTF-8"?>
|
|
||||||
<component type="console-application">
|
|
||||||
<id>io.github.anomalyco.vietc</id>
|
|
||||||
<name>Viet+</name>
|
|
||||||
<summary>Vietnamese Input Method for Linux</summary>
|
|
||||||
<description>
|
|
||||||
<p>Zero-configuration Vietnamese input method engine supporting Telex and VNI input methods. Works natively on both X11 and Wayland via evdev uinput injection.</p>
|
|
||||||
</description>
|
|
||||||
<metadata_license>MIT</metadata_license>
|
|
||||||
<project_license>MIT</project_license>
|
|
||||||
<url type="homepage">https://github.com/anomalyco/vietc</url>
|
|
||||||
<provides><binary>vietc</binary></provides>
|
|
||||||
<categories><category>Utility</category></categories>
|
|
||||||
</component>
|
|
||||||
XML
|
|
||||||
fi
|
|
||||||
|
|
||||||
# Config
|
|
||||||
echo "[4/5] Installing config..."
|
|
||||||
if [ -f "deb-build/etc/vietc/config.toml" ]; then
|
|
||||||
cp deb-build/etc/vietc/config.toml "$APPDIR/etc/vietc/"
|
|
||||||
else
|
|
||||||
sed 's/^grab = false/grab = true/' "$PROJECT_ROOT/vietc.toml" > "$APPDIR/etc/vietc/config.toml"
|
|
||||||
fi
|
|
||||||
|
|
||||||
# Docs
|
|
||||||
if [ -f "deb-build/usr/share/doc/vietc/README.md" ]; then
|
|
||||||
cp deb-build/usr/share/doc/vietc/README.md "$APPDIR/usr/share/doc/vietc/"
|
|
||||||
else
|
|
||||||
cp "$PROJECT_ROOT/README.md" "$APPDIR/usr/share/doc/vietc/"
|
|
||||||
fi
|
|
||||||
|
|
||||||
# Systemd service
|
|
||||||
if [ -f "deb-build/usr/lib/systemd/user/vietc.service" ]; then
|
|
||||||
cp deb-build/usr/lib/systemd/user/vietc.service "$APPDIR/usr/lib/systemd/user/"
|
|
||||||
else
|
|
||||||
cp "$PROJECT_ROOT/vietc.service" "$APPDIR/usr/lib/systemd/user/"
|
|
||||||
fi
|
|
||||||
|
|
||||||
# Desktop file in AppDir root
|
|
||||||
if [ -f "deb-build/vietc.desktop" ]; then
|
|
||||||
cp deb-build/vietc.desktop "$APPDIR/"
|
|
||||||
else
|
|
||||||
cp "$APPDIR/usr/share/applications/vietc.desktop" "$APPDIR/"
|
|
||||||
fi
|
|
||||||
|
|
||||||
# Icon — required by appimagetool (desktop file has Icon=vietc)
|
|
||||||
# Use SVG from deb build if available, otherwise generate a keyboard icon
|
|
||||||
if [ -f "deb-build/usr/share/icons/hicolor/256x256/apps/vietc.svg" ]; then
|
|
||||||
cp "deb-build/usr/share/icons/hicolor/256x256/apps/vietc.svg" "$APPDIR/vietc.svg"
|
|
||||||
elif [ -f "deb-build/usr/share/icons/hicolor/256x256/apps/vietc.png" ]; then
|
|
||||||
cp "deb-build/usr/share/icons/hicolor/256x256/apps/vietc.png" "$APPDIR/vietc.png"
|
|
||||||
else
|
|
||||||
# Generate a proper keyboard+VN icon as SVG
|
|
||||||
cat > "$APPDIR/vietc.svg" << 'SVGEOF'
|
|
||||||
<?xml version="1.0" encoding="UTF-8"?>
|
|
||||||
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 256 256" width="256" height="256">
|
|
||||||
<rect x="20" y="60" width="216" height="140" rx="16" fill="#2d2d2d" stroke="#1a1a1a" stroke-width="4"/>
|
|
||||||
<rect x="36" y="76" width="184" height="108" rx="8" fill="#3d3d3d"/>
|
|
||||||
<rect x="48" y="88" width="24" height="20" rx="3" fill="#f0f0f0"/>
|
|
||||||
<rect x="78" y="88" width="24" height="20" rx="3" fill="#f0f0f0"/>
|
|
||||||
<rect x="108" y="88" width="24" height="20" rx="3" fill="#f0f0f0"/>
|
|
||||||
<rect x="138" y="88" width="24" height="20" rx="3" fill="#f0f0f0"/>
|
|
||||||
<rect x="168" y="88" width="24" height="20" rx="3" fill="#f0f0f0"/>
|
|
||||||
<rect x="198" y="88" width="24" height="20" rx="3" fill="#f0f0f0"/>
|
|
||||||
<rect x="54" y="114" width="24" height="20" rx="3" fill="#f0f0f0"/>
|
|
||||||
<rect x="84" y="114" width="24" height="20" rx="3" fill="#f0f0f0"/>
|
|
||||||
<rect x="114" y="114" width="24" height="20" rx="3" fill="#f0f0f0"/>
|
|
||||||
<rect x="144" y="114" width="24" height="20" rx="3" fill="#f0f0f0"/>
|
|
||||||
<rect x="174" y="114" width="24" height="20" rx="3" fill="#f0f0f0"/>
|
|
||||||
<rect x="60" y="140" width="24" height="20" rx="3" fill="#f0f0f0"/>
|
|
||||||
<rect x="90" y="140" width="24" height="20" rx="3" fill="#f0f0f0"/>
|
|
||||||
<rect x="120" y="140" width="24" height="20" rx="3" fill="#f0f0f0"/>
|
|
||||||
<rect x="150" y="140" width="24" height="20" rx="3" fill="#f0f0f0"/>
|
|
||||||
<rect x="180" y="140" width="42" height="20" rx="3" fill="#f0f0f0"/>
|
|
||||||
<rect x="72" y="166" width="112" height="16" rx="3" fill="#f0f0f0"/>
|
|
||||||
<circle cx="216" cy="48" r="28" fill="#da251d"/>
|
|
||||||
<text x="216" y="56" text-anchor="middle" fill="white" font-size="18" font-weight="bold" font-family="sans-serif">VN</text>
|
|
||||||
</svg>
|
|
||||||
SVGEOF
|
|
||||||
fi
|
|
||||||
|
|
||||||
# Convert SVG to PNG for appimagetool (it prefers PNG for the root icon).
|
|
||||||
# This is best-effort: if no converter works, appimagetool falls back to the
|
|
||||||
# SVG, so a conversion failure must never abort the build.
|
|
||||||
if [ -f "$APPDIR/vietc.svg" ] && ! [ -f "$APPDIR/vietc.png" ]; then
|
|
||||||
if command -v rsvg-convert &>/dev/null; then
|
|
||||||
rsvg-convert -w 256 -h 256 "$APPDIR/vietc.svg" -o "$APPDIR/vietc.png" || true
|
|
||||||
elif command -v inkscape &>/dev/null; then
|
|
||||||
inkscape -w 256 -h 256 "$APPDIR/vietc.svg" --export-filename="$APPDIR/vietc.png" 2>/dev/null || true
|
|
||||||
elif command -v convert &>/dev/null; then
|
|
||||||
convert -background none "$APPDIR/vietc.svg" -resize 256x256 "$APPDIR/vietc.png" 2>/dev/null || true
|
|
||||||
elif command -v python3 &>/dev/null; then
|
|
||||||
python3 -c "
|
|
||||||
import subprocess, sys
|
|
||||||
try:
|
|
||||||
subprocess.check_call(['rsvg-convert', '-w', '256', '-h', '256', '$APPDIR/vietc.svg', '-o', '$APPDIR/vietc.png'])
|
|
||||||
except Exception:
|
|
||||||
pass
|
|
||||||
" 2>/dev/null || true
|
|
||||||
fi
|
|
||||||
# If no converter, appimagetool can use SVG directly
|
|
||||||
fi
|
|
||||||
|
|
||||||
# Also put icon in hicolor for system installs via AppImage
|
|
||||||
mkdir -p "$APPDIR/usr/share/icons/hicolor/256x256/apps"
|
|
||||||
[ -f "$APPDIR/vietc.svg" ] && cp "$APPDIR/vietc.svg" "$APPDIR/usr/share/icons/hicolor/256x256/apps/"
|
|
||||||
[ -f "$APPDIR/vietc.png" ] && cp "$APPDIR/vietc.png" "$APPDIR/usr/share/icons/hicolor/256x256/apps/"
|
|
||||||
|
|
||||||
# Create custom AppRun script
|
|
||||||
cat > "$APPDIR/AppRun" << 'EOF'
|
|
||||||
#!/bin/sh
|
|
||||||
HERE="$(dirname "$(readlink -f "${0}")")"
|
|
||||||
|
|
||||||
# Handle --update flag: download latest AppImage from GitHub
|
|
||||||
if [ "$1" = "--update" ]; then
|
|
||||||
echo "Viet+ Self-Updater"
|
|
||||||
RELEASE_URL="https://github.com/vndangkhoa/vietc/releases/latest/download/Viet+-x86_64.AppImage"
|
|
||||||
TEMP="/tmp/Viet+-update.AppImage"
|
|
||||||
echo "Downloading latest release..."
|
|
||||||
if command -v curl >/dev/null 2>&1; then
|
|
||||||
curl -L -o "$TEMP" "$RELEASE_URL" 2>/dev/null
|
|
||||||
elif command -v wget >/dev/null 2>&1; then
|
|
||||||
wget -q -O "$TEMP" "$RELEASE_URL" 2>/dev/null
|
|
||||||
else
|
|
||||||
echo "ERROR: curl or wget required for updates" >&2
|
|
||||||
exit 1
|
|
||||||
fi
|
|
||||||
if [ -s "$TEMP" ]; then
|
|
||||||
chmod +x "$TEMP"
|
|
||||||
mv "$TEMP" "$(readlink -f "${0}")"
|
|
||||||
echo "Updated! Restart the AppImage."
|
|
||||||
else
|
|
||||||
echo "ERROR: Download failed" >&2
|
|
||||||
fi
|
|
||||||
exit 0
|
|
||||||
fi
|
|
||||||
|
|
||||||
# Handle --quit flag: stop daemon and uinputd
|
|
||||||
if [ "$1" = "--quit" ]; then
|
|
||||||
echo "Stopping Viet+..."
|
|
||||||
pkill -x vietc-tray 2>/dev/null
|
|
||||||
pkill -x vietc 2>/dev/null
|
|
||||||
pkill -x vietc-uinputd 2>/dev/null
|
|
||||||
pkill -x vietc-xrecord 2>/dev/null
|
|
||||||
sleep 0.5
|
|
||||||
echo "Viet+ stopped."
|
|
||||||
exit 0
|
|
||||||
fi
|
|
||||||
|
|
||||||
# Handle --restart flag
|
|
||||||
if [ "$1" = "--restart" ]; then
|
|
||||||
pkill -x vietc-tray 2>/dev/null
|
|
||||||
pkill -x vietc 2>/dev/null
|
|
||||||
pkill -x vietc-uinputd 2>/dev/null
|
|
||||||
pkill -x vietc-xrecord 2>/dev/null
|
|
||||||
sleep 0.5
|
|
||||||
# Fall through to normal start below
|
|
||||||
fi
|
|
||||||
|
|
||||||
# Export our bin dir on PATH so child processes can find sibling binaries
|
|
||||||
export PATH="$HERE/usr/bin:$PATH"
|
|
||||||
|
|
||||||
# Build display env prefix for elevation commands.
|
|
||||||
# Capture from current user env (DISPLAY, XAUTHORITY, WAYLAND_DISPLAY, XDG_RUNTIME_DIR)
|
|
||||||
# so they are available inside the root daemon. Without this, xdotool/xclip/wtype
|
|
||||||
# fail silently because sudo/pkexec strip display env vars.
|
|
||||||
ENV_PREFIX="env"
|
|
||||||
[ -n "$DISPLAY" ] && ENV_PREFIX="$ENV_PREFIX DISPLAY=$DISPLAY"
|
|
||||||
[ -n "$XAUTHORITY" ] && ENV_PREFIX="$ENV_PREFIX XAUTHORITY=$XAUTHORITY"
|
|
||||||
[ -n "$WAYLAND_DISPLAY" ] && ENV_PREFIX="$ENV_PREFIX WAYLAND_DISPLAY=$WAYLAND_DISPLAY"
|
|
||||||
[ -n "$XDG_RUNTIME_DIR" ] && ENV_PREFIX="$ENV_PREFIX XDG_RUNTIME_DIR=$XDG_RUNTIME_DIR"
|
|
||||||
|
|
||||||
# Ensure system library paths are available for dlopen (libX11, libXtst, etc.)
|
|
||||||
# AppImage runtime may override LD_LIBRARY_PATH; append system paths as fallback
|
|
||||||
SYSLIB_PATHS="/usr/lib/x86_64-linux-gnu:/usr/lib64:/usr/lib:/lib/x86_64-linux-gnu:/lib64:/lib"
|
|
||||||
if [ -n "$LD_LIBRARY_PATH" ]; then
|
|
||||||
export LD_LIBRARY_PATH="$LD_LIBRARY_PATH:$SYSLIB_PATHS"
|
|
||||||
else
|
|
||||||
export LD_LIBRARY_PATH="$SYSLIB_PATHS"
|
|
||||||
fi
|
|
||||||
|
|
||||||
# Start daemon (kill old non-root one first if we have root)
|
|
||||||
# On X11 we can run without root (XGrabKeyboard + XTest injection needs no special permissions).
|
|
||||||
# On Wayland, evdev requires root (input group) or uinput.
|
|
||||||
NEED_ROOT=""
|
|
||||||
if [ -n "$WAYLAND_DISPLAY" ]; then
|
|
||||||
NEED_ROOT="yes"
|
|
||||||
fi
|
|
||||||
|
|
||||||
if [ -z "$NEED_ROOT" ]; then
|
|
||||||
# X11: no root needed for capture, but uinputd needs root for injection
|
|
||||||
pkill -x vietc-uinputd 2>/dev/null
|
|
||||||
pkill -x vietc 2>/dev/null; sleep 0.3
|
|
||||||
mkdir -p "$HOME/.config/vietc" "$HOME/.vietc"
|
|
||||||
|
|
||||||
# Try to start the uinputd daemon (preferred injection path)
|
|
||||||
if command -v pkexec >/dev/null 2>&1; then
|
|
||||||
pkexec "$HERE/usr/bin/vietc-uinputd" >/dev/null 2>&1 &
|
|
||||||
UINPUTD_PID=$!
|
|
||||||
sleep 0.3
|
|
||||||
elif command -v sudo >/dev/null 2>&1; then
|
|
||||||
if sudo -n true 2>/dev/null; then
|
|
||||||
sudo "$HERE/usr/bin/vietc-uinputd" >/dev/null 2>&1 &
|
|
||||||
UINPUTD_PID=$!
|
|
||||||
sleep 0.3
|
|
||||||
fi
|
|
||||||
fi
|
|
||||||
|
|
||||||
"$HERE/usr/bin/vietc" >"$HOME/.config/vietc/vietc-daemon.log" 2>&1 &
|
|
||||||
DAEMON_PID=$!
|
|
||||||
echo "[vietc] Daemon started (PID=$DAEMON_PID), log: $HOME/.config/vietc/vietc-daemon.log"
|
|
||||||
else
|
|
||||||
# Fix Wayland env for root: sudo resets XDG_RUNTIME_DIR, breaking wtype/wl-copy.
|
|
||||||
if [ "$(id -u)" = "0" ] && [ -z "$XDG_RUNTIME_DIR" ] && [ -n "$SUDO_USER" ]; then
|
|
||||||
USER_UID=$(id -u "$SUDO_USER" 2>/dev/null || echo 1000)
|
|
||||||
export XDG_RUNTIME_DIR="/run/user/$USER_UID"
|
|
||||||
if [ -d "/run/user/$USER_UID" ] && ls "/run/user/$USER_UID/wayland-*" >/dev/null 2>&1; then
|
|
||||||
export WAYLAND_DISPLAY="${WAYLAND_DISPLAY:-wayland-0}"
|
|
||||||
fi
|
|
||||||
fi
|
|
||||||
|
|
||||||
if command -v pkexec >/dev/null; then
|
|
||||||
pkill -x vietc 2>/dev/null; sleep 0.5
|
|
||||||
pkexec $ENV_PREFIX "$HERE/usr/bin/vietc" >/dev/null &
|
|
||||||
DAEMON_PID=$!
|
|
||||||
elif [ -n "$WAYLAND_DISPLAY" ]; then
|
|
||||||
password=""
|
|
||||||
if command -v kdialog >/dev/null; then
|
|
||||||
password=$(kdialog --password "Viet+ needs root privileges to grab the keyboard.") || password=""
|
|
||||||
elif command -v zenity >/dev/null; then
|
|
||||||
password=$(zenity --password --title="Viet+ needs root") || password=""
|
|
||||||
elif command -v ssh-askpass >/dev/null; then
|
|
||||||
password=$(ssh-askpass "Viet+ needs root privileges") || password=""
|
|
||||||
fi
|
|
||||||
if [ -n "$password" ]; then
|
|
||||||
pkill -x vietc 2>/dev/null; sleep 0.5
|
|
||||||
echo "$password" | sudo -S $ENV_PREFIX "$HERE/usr/bin/vietc" >/dev/null &
|
|
||||||
DAEMON_PID=$!
|
|
||||||
fi
|
|
||||||
elif command -v sudo >/dev/null; then
|
|
||||||
pkill -x vietc 2>/dev/null; sleep 0.5
|
|
||||||
sudo $ENV_PREFIX "$HERE/usr/bin/vietc" >/dev/null &
|
|
||||||
DAEMON_PID=$!
|
|
||||||
fi
|
|
||||||
fi
|
|
||||||
|
|
||||||
if [ -z "$DAEMON_PID" ] && ! pgrep -x vietc >/dev/null; then
|
|
||||||
mkdir -p "$HOME/.config/vietc"
|
|
||||||
"$HERE/usr/bin/vietc" >"$HOME/.config/vietc/vietc-daemon.log" 2>&1 &
|
|
||||||
DAEMON_PID=$!
|
|
||||||
echo "[vietc] Daemon fallback started (PID=$DAEMON_PID), log: $HOME/.config/vietc/vietc-daemon.log"
|
|
||||||
fi
|
|
||||||
|
|
||||||
# Keep the AppImage alive with a tray or settings UI.
|
|
||||||
# Run as a child (not exec) so daemon cleanup works on exit.
|
|
||||||
cleanup_daemon() {
|
|
||||||
if [ -n "$DAEMON_PID" ]; then
|
|
||||||
kill "$DAEMON_PID" 2>/dev/null
|
|
||||||
wait "$DAEMON_PID" 2>/dev/null
|
|
||||||
fi
|
|
||||||
if [ -n "$UINPUTD_PID" ]; then
|
|
||||||
kill "$UINPUTD_PID" 2>/dev/null
|
|
||||||
wait "$UINPUTD_PID" 2>/dev/null
|
|
||||||
fi
|
|
||||||
}
|
|
||||||
trap cleanup_daemon EXIT INT TERM
|
|
||||||
|
|
||||||
if [ -f "$HERE/usr/bin/vietc-tray" ]; then
|
|
||||||
"$HERE/usr/bin/vietc-tray" "$@"
|
|
||||||
else
|
|
||||||
echo "[vietc] Daemon running (PID=$DAEMON_PID)."
|
|
||||||
echo "[vietc] To stop: Vietnam+-*.AppImage --quit"
|
|
||||||
# Show a quit dialog if DE is available
|
|
||||||
if command -v zenity >/dev/null 2>&1; then
|
|
||||||
zenity --info --text="Viet+ is running in the background.\n\nTo stop: run this AppImage with --quit" \
|
|
||||||
--title="Viet+ Input Method" --width=350 2>/dev/null &
|
|
||||||
fi
|
|
||||||
# Keep alive until daemon dies or user quits via --quit
|
|
||||||
wait $DAEMON_PID 2>/dev/null
|
|
||||||
fi
|
|
||||||
EOF
|
|
||||||
chmod +x "$APPDIR/AppRun"
|
|
||||||
|
|
||||||
echo "[5/5] AppDir ready at: $APPDIR"
|
|
||||||
echo ""
|
|
||||||
|
|
||||||
# Auto build if appimagetool exists
|
|
||||||
if [ -f "$SCRIPT_DIR/appimagetool" ]; then
|
|
||||||
echo "=== Running appimagetool FUSE build ==="
|
|
||||||
ARCH=x86_64 "$SCRIPT_DIR/appimagetool" --appimage-extract-and-run "$APPDIR" "$SCRIPT_DIR/Viet+-${VERSION}-x86_64.AppImage"
|
|
||||||
elif command -v appimagetool &>/dev/null; then
|
|
||||||
echo "=== Running system appimagetool ==="
|
|
||||||
ARCH=x86_64 appimagetool "$APPDIR" "$SCRIPT_DIR/Viet+-${VERSION}-x86_64.AppImage"
|
|
||||||
else
|
|
||||||
echo "To build AppImage:"
|
|
||||||
echo " appimagetool $APPDIR Viet+-${VERSION}-x86_64.AppImage"
|
|
||||||
fi
|
|
||||||
|
|
@ -3,7 +3,7 @@ set -euo pipefail
|
||||||
|
|
||||||
SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)"
|
SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)"
|
||||||
PROJECT_ROOT="$(cd "$SCRIPT_DIR/../.." && pwd)"
|
PROJECT_ROOT="$(cd "$SCRIPT_DIR/../.." && pwd)"
|
||||||
VERSION="${1:-0.1.0}"
|
VERSION="${1:-0.1.6}"
|
||||||
PACKAGE="vietc_${VERSION}-1_amd64"
|
PACKAGE="vietc_${VERSION}-1_amd64"
|
||||||
STAGING="$SCRIPT_DIR/$PACKAGE"
|
STAGING="$SCRIPT_DIR/$PACKAGE"
|
||||||
|
|
||||||
|
|
@ -12,7 +12,7 @@ echo "=== Building Viet+ .deb package v${VERSION} ==="
|
||||||
# Build binaries (all features: x11 + wayland)
|
# Build binaries (all features: x11 + wayland)
|
||||||
echo "[1/5] Building binaries..."
|
echo "[1/5] Building binaries..."
|
||||||
cargo build --release --features "x11,wayland" --manifest-path "$PROJECT_ROOT/Cargo.toml"
|
cargo build --release --features "x11,wayland" --manifest-path "$PROJECT_ROOT/Cargo.toml"
|
||||||
(cd "$PROJECT_ROOT/ui" && export PKG_CONFIG_PATH="/tmp/dbus-dev/extracted/usr/lib/x86_64-linux-gnu/pkgconfig:${PKG_CONFIG_PATH:-}" && export RUSTFLAGS="-L /tmp/dbus-dev/lib" && cargo build --release) || echo " Warning: UI tray not built (libdbus-1-dev may be missing)"
|
(cd "$PROJECT_ROOT/ui" && cargo build --release)
|
||||||
echo " Done."
|
echo " Done."
|
||||||
|
|
||||||
# Clean and create staging
|
# Clean and create staging
|
||||||
|
|
@ -26,54 +26,39 @@ mkdir -p "$STAGING/usr/share/applications"
|
||||||
mkdir -p "$STAGING/usr/share/icons/hicolor/256x256/apps"
|
mkdir -p "$STAGING/usr/share/icons/hicolor/256x256/apps"
|
||||||
mkdir -p "$STAGING/usr/share/doc/vietc"
|
mkdir -p "$STAGING/usr/share/doc/vietc"
|
||||||
mkdir -p "$STAGING/usr/share/metainfo"
|
mkdir -p "$STAGING/usr/share/metainfo"
|
||||||
|
mkdir -p "$STAGING/etc/xdg/autostart"
|
||||||
|
|
||||||
# Copy binaries
|
# Copy binaries
|
||||||
echo "[3/5] Installing binaries..."
|
echo "[3/5] Installing binaries..."
|
||||||
cp "$PROJECT_ROOT/target/release/vietc" "$STAGING/usr/bin/"
|
cp "$PROJECT_ROOT/target/release/vietc" "$STAGING/usr/bin/vietc-daemon"
|
||||||
cp "$PROJECT_ROOT/target/release/vietc-cli" "$STAGING/usr/bin/"
|
cp "$PROJECT_ROOT/target/release/vietc-cli" "$STAGING/usr/bin/"
|
||||||
# Privileged uinput injection daemon — required for Unicode (Vietnamese) output.
|
|
||||||
cp "$PROJECT_ROOT/target/release/vietc-uinputd" "$STAGING/usr/bin/"
|
cp "$PROJECT_ROOT/target/release/vietc-uinputd" "$STAGING/usr/bin/"
|
||||||
[ -f "$PROJECT_ROOT/ui/target/release/vietc-tray" ] && cp "$PROJECT_ROOT/ui/target/release/vietc-tray" "$STAGING/usr/bin/"
|
cp "$PROJECT_ROOT/ui/target/release/vietc-tray" "$STAGING/usr/bin/"
|
||||||
|
|
||||||
# Compile and bundle vietc-xrecord (C helper for X11 XRecord keyboard capture)
|
# Compile and bundle vietc-xrecord (C helper for X11 XRecord keyboard capture)
|
||||||
if command -v gcc &>/dev/null; then
|
gcc -O2 -o "$STAGING/usr/bin/vietc-xrecord" "$SCRIPT_DIR/vietc-xrecord.c" -lX11 -lXtst
|
||||||
gcc -O2 -o "$STAGING/usr/bin/vietc-xrecord" "$PROJECT_ROOT/packaging/appimage/vietc-xrecord.c" -lX11 -lXtst \
|
|
||||||
&& echo " vietc-xrecord compiled" \
|
# Icons (main app icon + tray status icons)
|
||||||
|| echo " WARNING: vietc-xrecord compile failed (libX11/libXtst dev headers missing)"
|
cp "$PROJECT_ROOT/packaging/icons/vietc.svg" "$STAGING/usr/share/icons/hicolor/256x256/apps/"
|
||||||
else
|
cp "$PROJECT_ROOT/packaging/icons/vietc-vn.svg" "$STAGING/usr/share/icons/hicolor/256x256/apps/"
|
||||||
echo " WARNING: no gcc, vietc-xrecord not bundled"
|
cp "$PROJECT_ROOT/packaging/icons/vietc-en.svg" "$STAGING/usr/share/icons/hicolor/256x256/apps/"
|
||||||
fi
|
|
||||||
|
|
||||||
# Desktop file
|
# Desktop file
|
||||||
cp "$PROJECT_ROOT/packaging/appimage/vietc.desktop" "$STAGING/usr/share/applications/"
|
cp "$SCRIPT_DIR/vietc.desktop" "$STAGING/usr/share/applications/"
|
||||||
|
|
||||||
# Icon (SVG from AppImage build script)
|
# XDG autostart — launches tray on every login for all users
|
||||||
cat > "$STAGING/usr/share/icons/hicolor/256x256/apps/vietc.svg" << 'SVGEOF'
|
cat > "$STAGING/etc/xdg/autostart/vietc-tray.desktop" << 'AUTOSTART'
|
||||||
<?xml version="1.0" encoding="UTF-8"?>
|
[Desktop Entry]
|
||||||
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 256 256" width="256" height="256">
|
Type=Application
|
||||||
<rect x="20" y="60" width="216" height="140" rx="16" fill="#2d2d2d" stroke="#1a1a1a" stroke-width="4"/>
|
Name=Viet+ Tray
|
||||||
<rect x="36" y="76" width="184" height="108" rx="8" fill="#3d3d3d"/>
|
Comment=Vietnamese Input Method Tray
|
||||||
<rect x="48" y="88" width="24" height="20" rx="3" fill="#f0f0f0"/>
|
Exec=vietc-tray
|
||||||
<rect x="78" y="88" width="24" height="20" rx="3" fill="#f0f0f0"/>
|
Icon=vietc
|
||||||
<rect x="108" y="88" width="24" height="20" rx="3" fill="#f0f0f0"/>
|
Terminal=false
|
||||||
<rect x="138" y="88" width="24" height="20" rx="3" fill="#f0f0f0"/>
|
Categories=Utility;
|
||||||
<rect x="168" y="88" width="24" height="20" rx="3" fill="#f0f0f0"/>
|
StartupNotify=false
|
||||||
<rect x="198" y="88" width="24" height="20" rx="3" fill="#f0f0f0"/>
|
NoDisplay=true
|
||||||
<rect x="54" y="114" width="24" height="20" rx="3" fill="#f0f0f0"/>
|
AUTOSTART
|
||||||
<rect x="84" y="114" width="24" height="20" rx="3" fill="#f0f0f0"/>
|
|
||||||
<rect x="114" y="114" width="24" height="20" rx="3" fill="#f0f0f0"/>
|
|
||||||
<rect x="144" y="114" width="24" height="20" rx="3" fill="#f0f0f0"/>
|
|
||||||
<rect x="174" y="114" width="24" height="20" rx="3" fill="#f0f0f0"/>
|
|
||||||
<rect x="60" y="140" width="24" height="20" rx="3" fill="#f0f0f0"/>
|
|
||||||
<rect x="90" y="140" width="24" height="20" rx="3" fill="#f0f0f0"/>
|
|
||||||
<rect x="120" y="140" width="24" height="20" rx="3" fill="#f0f0f0"/>
|
|
||||||
<rect x="150" y="140" width="24" height="20" rx="3" fill="#f0f0f0"/>
|
|
||||||
<rect x="180" y="140" width="42" height="20" rx="3" fill="#f0f0f0"/>
|
|
||||||
<rect x="72" y="166" width="112" height="16" rx="3" fill="#f0f0f0"/>
|
|
||||||
<circle cx="216" cy="48" r="28" fill="#da251d"/>
|
|
||||||
<text x="216" y="56" text-anchor="middle" fill="white" font-size="18" font-weight="bold" font-family="sans-serif">VN</text>
|
|
||||||
</svg>
|
|
||||||
SVGEOF
|
|
||||||
|
|
||||||
# Documentation
|
# Documentation
|
||||||
cp "$PROJECT_ROOT/README.md" "$STAGING/usr/share/doc/vietc/"
|
cp "$PROJECT_ROOT/README.md" "$STAGING/usr/share/doc/vietc/"
|
||||||
|
|
@ -82,8 +67,21 @@ cp "$PROJECT_ROOT/LICENSE" "$STAGING/usr/share/doc/vietc/"
|
||||||
# Config
|
# Config
|
||||||
cp "$PROJECT_ROOT/vietc.toml" "$STAGING/etc/vietc/config.toml"
|
cp "$PROJECT_ROOT/vietc.toml" "$STAGING/etc/vietc/config.toml"
|
||||||
|
|
||||||
# Systemd user service
|
# Systemd user service — tray spawns the daemon internally
|
||||||
cp "$PROJECT_ROOT/vietc.service" "$STAGING/usr/lib/systemd/user/"
|
cat > "$STAGING/usr/lib/systemd/user/vietc.service" << 'SERVICE'
|
||||||
|
[Unit]
|
||||||
|
Description=Viet+ Vietnamese IME Tray
|
||||||
|
PartOf=graphical-session.target
|
||||||
|
|
||||||
|
[Service]
|
||||||
|
Type=simple
|
||||||
|
ExecStart=/usr/bin/vietc-tray
|
||||||
|
Restart=on-failure
|
||||||
|
RestartSec=5
|
||||||
|
|
||||||
|
[Install]
|
||||||
|
WantedBy=default.target
|
||||||
|
SERVICE
|
||||||
|
|
||||||
# AppStream metadata
|
# AppStream metadata
|
||||||
cat > "$STAGING/usr/share/metainfo/io.github.anomalyco.vietc.appdata.xml" << 'XML'
|
cat > "$STAGING/usr/share/metainfo/io.github.anomalyco.vietc.appdata.xml" << 'XML'
|
||||||
|
|
@ -124,7 +122,7 @@ Section: utils
|
||||||
Priority: optional
|
Priority: optional
|
||||||
Architecture: amd64
|
Architecture: amd64
|
||||||
Depends: libc6 (>= 2.31), libevdev2 (>= 1.9.0)
|
Depends: libc6 (>= 2.31), libevdev2 (>= 1.9.0)
|
||||||
Recommends: libwayland-client0 (>= 1.20), libx11-6, libxtst6, xclip
|
Recommends: libwayland-client0 (>= 1.20), libx11-6, libxtst6, libdbus-1-3, xclip
|
||||||
Maintainer: Khoa Vo <vndangkhoa@gmail.com>
|
Maintainer: Khoa Vo <vndangkhoa@gmail.com>
|
||||||
Description: Viet+ — Vietnamese Input Method for Linux
|
Description: Viet+ — Vietnamese Input Method for Linux
|
||||||
Zero-configuration Vietnamese input method engine supporting
|
Zero-configuration Vietnamese input method engine supporting
|
||||||
|
|
@ -140,10 +138,73 @@ echo "/etc/vietc/config.toml" > "$STAGING/DEBIAN/conffiles"
|
||||||
cat > "$STAGING/DEBIAN/postinst" << 'POSTINST'
|
cat > "$STAGING/DEBIAN/postinst" << 'POSTINST'
|
||||||
#!/bin/sh
|
#!/bin/sh
|
||||||
set -e
|
set -e
|
||||||
|
|
||||||
|
show_popup() {
|
||||||
|
local user="$1" msg="$2"
|
||||||
|
local display="${DISPLAY:-:0}"
|
||||||
|
local xauth=""
|
||||||
|
if [ -n "$user" ]; then
|
||||||
|
local home
|
||||||
|
home="$(getent passwd "$user" 2>/dev/null | cut -d: -f6 || true)"
|
||||||
|
if [ -n "$home" ]; then
|
||||||
|
xauth="$home/.Xauthority"
|
||||||
|
fi
|
||||||
|
fi
|
||||||
|
# Try zenity (modal dialog)
|
||||||
|
if command -v zenity >/dev/null 2>&1 && [ -n "$user" ]; then
|
||||||
|
su "$user" -c "DISPLAY='$display' XAUTHORITY='$xauth' \
|
||||||
|
zenity --info --title='Viet+' --text='$msg' --width=400" 2>/dev/null || true
|
||||||
|
fi
|
||||||
|
# Also try notify-send (desktop notification)
|
||||||
|
if command -v notify-send >/dev/null 2>&1 && [ -n "$user" ]; then
|
||||||
|
su "$user" -c "DISPLAY='$display' XAUTHORITY='$xauth' \
|
||||||
|
notify-send 'Viet+' '$msg' -t 10000 -i vietc" 2>/dev/null || true
|
||||||
|
fi
|
||||||
|
}
|
||||||
|
|
||||||
|
cleanup_old_install() {
|
||||||
|
# Remove old binaries from /usr/local/bin/ (shadowed the new /usr/bin/ ones)
|
||||||
|
rm -f /usr/local/bin/vietc-tray /usr/local/bin/vietc /usr/local/bin/vietc-daemon \
|
||||||
|
/usr/local/bin/vietc-cli /usr/local/bin/vietc-uinputd /usr/local/bin/vietc-xrecord 2>/dev/null || true
|
||||||
|
}
|
||||||
|
|
||||||
case "$1" in
|
case "$1" in
|
||||||
configure)
|
configure)
|
||||||
|
# Kill old running daemon/tray so new binaries take effect
|
||||||
|
pkill -x vietc-tray 2>/dev/null || true
|
||||||
|
pkill -x vietc-daemon 2>/dev/null || true
|
||||||
|
pkill -x vietc 2>/dev/null || true
|
||||||
|
|
||||||
|
# Remove old /usr/local/bin/ binaries that shadowed the new ones
|
||||||
|
cleanup_old_install
|
||||||
|
|
||||||
|
# Reload systemd
|
||||||
if command -v systemctl >/dev/null 2>&1; then
|
if command -v systemctl >/dev/null 2>&1; then
|
||||||
systemctl --system daemon-reload >/dev/null 2>&1 || true
|
systemctl --global daemon-reload >/dev/null 2>&1 || true
|
||||||
|
fi
|
||||||
|
|
||||||
|
# Add installing user to input group (needed for /dev/uinput access)
|
||||||
|
INSTALLING_USER="${SUDO_USER:-${USER:-}}"
|
||||||
|
if [ -n "$INSTALLING_USER" ] && [ "$INSTALLING_USER" != "root" ]; then
|
||||||
|
if ! groups "$INSTALLING_USER" 2>/dev/null | grep -qw input; then
|
||||||
|
adduser "$INSTALLING_USER" input 2>/dev/null || true
|
||||||
|
fi
|
||||||
|
# Remove stale user config from previous installs
|
||||||
|
USER_HOME="$(getent passwd "$INSTALLING_USER" 2>/dev/null | cut -d: -f6 || true)"
|
||||||
|
if [ -n "$USER_HOME" ]; then
|
||||||
|
rm -f "$USER_HOME/.config/vietc/config.toml" 2>/dev/null || true
|
||||||
|
rm -f "$USER_HOME/.config/vietc/overrides.toml" 2>/dev/null || true
|
||||||
|
rm -f "$USER_HOME/.config/vietc/.first-launch-done" 2>/dev/null || true
|
||||||
|
fi
|
||||||
|
|
||||||
|
# Show popup
|
||||||
|
show_popup "$INSTALLING_USER" \
|
||||||
|
"Viet+ installed! Please LOG OUT and LOG BACK IN to start typing Vietnamese."
|
||||||
|
fi
|
||||||
|
|
||||||
|
# Update icon cache so the app icon appears in the menu
|
||||||
|
if command -v gtk-update-icon-cache >/dev/null 2>&1; then
|
||||||
|
gtk-update-icon-cache -f /usr/share/icons/hicolor/ >/dev/null 2>&1 || true
|
||||||
fi
|
fi
|
||||||
;;
|
;;
|
||||||
esac
|
esac
|
||||||
|
|
@ -156,7 +217,7 @@ set -e
|
||||||
case "$1" in
|
case "$1" in
|
||||||
remove|upgrade|deconfigure)
|
remove|upgrade|deconfigure)
|
||||||
if command -v systemctl >/dev/null 2>&1; then
|
if command -v systemctl >/dev/null 2>&1; then
|
||||||
systemctl --system daemon-reload >/dev/null 2>&1 || true
|
systemctl --global daemon-reload >/dev/null 2>&1 || true
|
||||||
fi
|
fi
|
||||||
;;
|
;;
|
||||||
esac
|
esac
|
||||||
|
|
|
||||||
|
|
@ -3,9 +3,9 @@ Type=Application
|
||||||
Name=Viet+
|
Name=Viet+
|
||||||
GenericName=Vietnamese Input Method
|
GenericName=Vietnamese Input Method
|
||||||
Comment=Vietnamese Input Method for Linux — Zero underline, native Wayland/X11
|
Comment=Vietnamese Input Method for Linux — Zero underline, native Wayland/X11
|
||||||
Exec=vietc
|
Exec=vietc-tray
|
||||||
Icon=vietc
|
Icon=vietc
|
||||||
Terminal=false
|
Terminal=false
|
||||||
Categories=Utility;
|
Categories=Utility;TextTools;X-GNOME-Utilities;
|
||||||
Keywords=vietnamese;input;ime;keyboard;
|
Keywords=vietnamese;input;ime;keyboard;viet;gõ tiếng việt;
|
||||||
StartupNotify=false
|
StartupNotify=true
|
||||||
|
|
@ -1,112 +0,0 @@
|
||||||
# Building the Viet+ Flatpak
|
|
||||||
|
|
||||||
## Prerequisites
|
|
||||||
|
|
||||||
- Flatpak installed with Flathub remote configured
|
|
||||||
- `org.gnome.Platform//50` runtime installed
|
|
||||||
- `org.gnome.Sdk//50` SDK installed
|
|
||||||
- `org.freedesktop.Sdk.Extension.rust-stable//25.08` installed
|
|
||||||
|
|
||||||
### Install dependencies
|
|
||||||
|
|
||||||
```bash
|
|
||||||
flatpak install --user flathub org.gnome.Platform//50
|
|
||||||
flatpak install --user flathub org.gnome.Sdk//50
|
|
||||||
flatpak install --user flathub org.freedesktop.Sdk.Extension.rust-stable//25.08
|
|
||||||
```
|
|
||||||
|
|
||||||
---
|
|
||||||
|
|
||||||
## Method 1: Quick build script
|
|
||||||
|
|
||||||
```bash
|
|
||||||
cd packaging/flatpak
|
|
||||||
bash build-flatpak.sh [version]
|
|
||||||
# e.g. bash build-flatpak.sh 0.1.5
|
|
||||||
```
|
|
||||||
|
|
||||||
Output: `packaging/flatpak/VietPlus-<version>.flatpak`
|
|
||||||
|
|
||||||
---
|
|
||||||
|
|
||||||
## Method 2: Manual step-by-step
|
|
||||||
|
|
||||||
```bash
|
|
||||||
cd packaging/flatpak
|
|
||||||
|
|
||||||
# 1. Clean previous artifacts
|
|
||||||
rm -rf build-dir repo VietPlus-*.flatpak
|
|
||||||
|
|
||||||
# 2. Initialize build directory
|
|
||||||
# NOTE: arg order is flatpak build-init DIR APPNAME SDK RUNTIME
|
|
||||||
flatpak build-init build-dir io.github.vietc.VietPlus \
|
|
||||||
org.gnome.Sdk//50 org.gnome.Platform//50
|
|
||||||
|
|
||||||
# 3. Copy source code
|
|
||||||
mkdir -p build-dir/files/src/vietc
|
|
||||||
rsync -a /path/to/vietc/ build-dir/files/src/vietc/ --exclude=target --exclude=.git
|
|
||||||
|
|
||||||
# 4. Build Rust binaries
|
|
||||||
flatpak build --share=network build-dir sh -c '
|
|
||||||
export PATH=/usr/lib/sdk/rust-stable/bin:$PATH
|
|
||||||
export CARGO_HOME=/app/cargo
|
|
||||||
cd /app/src/vietc
|
|
||||||
cargo build --release -p vietc-daemon -p vietc-cli -p vietc-uinputd
|
|
||||||
'
|
|
||||||
|
|
||||||
# 5. Install binaries and icons
|
|
||||||
flatpak build build-dir sh -c '
|
|
||||||
install -Dm755 /app/src/vietc/target/release/vietc /app/bin/vietc-daemon
|
|
||||||
install -Dm755 /app/src/vietc/target/release/vietc-cli /app/bin/vietc-cli
|
|
||||||
install -Dm755 /app/src/vietc/target/release/vietc-uinputd /app/bin/vietc-uinputd
|
|
||||||
|
|
||||||
install -Dm644 /app/src/vietc/packaging/icons/vietc.svg \
|
|
||||||
/app/share/icons/hicolor/scalable/apps/io.github.vietc.VietPlus.svg
|
|
||||||
install -Dm644 /app/src/vietc/packaging/icons/vietc-vn.svg \
|
|
||||||
/app/share/icons/hicolor/scalable/apps/io.github.vietc.VietPlus.vietc-vn.svg
|
|
||||||
install -Dm644 /app/src/vietc/packaging/icons/vietc-en.svg \
|
|
||||||
/app/share/icons/hicolor/scalable/apps/io.github.vietc.VietPlus.vietc-en.svg
|
|
||||||
'
|
|
||||||
|
|
||||||
# 6. Finish (set permissions + command)
|
|
||||||
flatpak build-finish build-dir \
|
|
||||||
--socket=x11 \
|
|
||||||
--socket=wayland \
|
|
||||||
--filesystem=home \
|
|
||||||
--share=ipc \
|
|
||||||
--talk-name=org.freedesktop.Notifications \
|
|
||||||
--talk-name=org.a11y.Bus \
|
|
||||||
--command=vietc-daemon
|
|
||||||
|
|
||||||
# 7. Export to local repo
|
|
||||||
flatpak build-export repo build-dir
|
|
||||||
|
|
||||||
# 8. Create bundle
|
|
||||||
flatpak build-bundle repo VietPlus-0.1.5.flatpak io.github.vietc.VietPlus
|
|
||||||
```
|
|
||||||
|
|
||||||
---
|
|
||||||
|
|
||||||
## Installation
|
|
||||||
|
|
||||||
```bash
|
|
||||||
# From bundle
|
|
||||||
flatpak install --user --bundle VietPlus-0.1.5.flatpak
|
|
||||||
|
|
||||||
# From local repo
|
|
||||||
flatpak --user remote-add --no-gpg-verify vietc-repo repo
|
|
||||||
flatpak --user install vietc-repo io.github.vietc.VietPlus
|
|
||||||
|
|
||||||
# Run
|
|
||||||
flatpak run io.github.vietc.VietPlus
|
|
||||||
```
|
|
||||||
|
|
||||||
---
|
|
||||||
|
|
||||||
## Key Notes
|
|
||||||
|
|
||||||
- **SDK/RUNTIME order**: `flatpak build-init` takes `SDK` first, then `RUNTIME` (counterintuitive but important — getting this wrong means `/usr/lib/sdk/` won't be mounted)
|
|
||||||
- **Rust SDK**: must be installed as `org.freedesktop.Sdk.Extension.rust-stable//25.08`; it mounts automatically at `/usr/lib/sdk/rust-stable/`
|
|
||||||
- **Icons**: all icon files in Flatpak must be prefixed with the app ID (`io.github.vietc.VietPlus.*`) or `flatpak build-export` will skip them
|
|
||||||
- **Daemon binary name**: Cargo builds the daemon binary as `vietc` (not `vietc-daemon`) in `target/release/`; rename on install to match the desktop file
|
|
||||||
- **Desktop Categories**: only use registered categories (`Utility`); `InputMethod` is not registered
|
|
||||||
|
|
@ -1,109 +0,0 @@
|
||||||
#!/usr/bin/env bash
|
|
||||||
set -euo pipefail
|
|
||||||
|
|
||||||
SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)"
|
|
||||||
PROJECT_ROOT="$(cd "$SCRIPT_DIR/../.." && pwd)"
|
|
||||||
VERSION="${1:-0.1.4}"
|
|
||||||
|
|
||||||
echo "=== Building Viet+ Flatpak v${VERSION} ==="
|
|
||||||
cd "$SCRIPT_DIR"
|
|
||||||
|
|
||||||
# Clean previous build
|
|
||||||
rm -rf build-dir repo VietPlus-*.flatpak
|
|
||||||
|
|
||||||
# Initialize build directory
|
|
||||||
# NOTE: arg order is flatpak build-init DIR APPNAME SDK RUNTIME
|
|
||||||
flatpak build-init build-dir io.github.vietc.VietPlus \
|
|
||||||
org.gnome.Sdk//50 org.gnome.Platform//50
|
|
||||||
|
|
||||||
# Copy source code
|
|
||||||
mkdir -p build-dir/files/src/vietc
|
|
||||||
rsync -a "$PROJECT_ROOT/" build-dir/files/src/vietc/ --exclude=target --exclude=.git
|
|
||||||
|
|
||||||
BUILD='export PATH=/usr/lib/sdk/rust-stable/bin:$PATH
|
|
||||||
export CARGO_HOME=/app/cargo
|
|
||||||
cd /app/src/vietc'
|
|
||||||
|
|
||||||
# Build daemon + CLI + uinputd + tray
|
|
||||||
echo ""
|
|
||||||
echo "=== Compiling daemon, CLI, uinputd, tray... ==="
|
|
||||||
flatpak build --share=network build-dir sh -c "$BUILD && cargo build --release -p vietc-daemon -p vietc-cli -p vietc-uinputd && cargo build --release --manifest-path ui/Cargo.toml"
|
|
||||||
|
|
||||||
# Install files
|
|
||||||
echo ""
|
|
||||||
echo "=== Installing files... ==="
|
|
||||||
flatpak build build-dir sh -c "
|
|
||||||
set -e
|
|
||||||
install -Dm755 /app/src/vietc/target/release/vietc /app/bin/vietc-daemon
|
|
||||||
install -Dm755 /app/src/vietc/target/release/vietc-cli /app/bin/vietc-cli
|
|
||||||
install -Dm755 /app/src/vietc/target/release/vietc-uinputd /app/bin/vietc-uinputd
|
|
||||||
install -Dm755 /app/src/vietc/ui/target/release/vietc-tray /app/bin/vietc-tray
|
|
||||||
|
|
||||||
install -Dm644 /app/src/vietc/packaging/icons/vietc.svg /app/share/icons/hicolor/scalable/apps/io.github.vietc.VietPlus.svg
|
|
||||||
install -Dm644 /app/src/vietc/packaging/icons/vietc-vn.svg /app/share/icons/hicolor/scalable/apps/io.github.vietc.VietPlus.vietc-vn.svg
|
|
||||||
install -Dm644 /app/src/vietc/packaging/icons/vietc-en.svg /app/share/icons/hicolor/scalable/apps/io.github.vietc.VietPlus.vietc-en.svg
|
|
||||||
|
|
||||||
mkdir -p /app/share/applications
|
|
||||||
cat > /app/share/applications/io.github.vietc.VietPlus.desktop << END
|
|
||||||
[Desktop Entry]
|
|
||||||
Name=Viet+
|
|
||||||
Comment=Vietnamese Input Method
|
|
||||||
Exec=/app/bin/vietc-tray
|
|
||||||
Icon=io.github.vietc.VietPlus
|
|
||||||
Terminal=false
|
|
||||||
Type=Application
|
|
||||||
StartupNotify=true
|
|
||||||
Categories=Utility;TextTools;X-GNOME-Utilities;
|
|
||||||
END
|
|
||||||
|
|
||||||
mkdir -p /app/share/metainfo
|
|
||||||
cat > /app/share/metainfo/io.github.vietc.VietPlus.metainfo.xml << 'XML'
|
|
||||||
<?xml version='1.0' encoding='utf-8'?>
|
|
||||||
<component type='desktop-application'>
|
|
||||||
<id>io.github.vietc.VietPlus</id>
|
|
||||||
<name>Viet+</name>
|
|
||||||
<summary>Vietnamese Input Method for Linux</summary>
|
|
||||||
<description>
|
|
||||||
<p>Zero-configuration Vietnamese input method engine supporting Telex and VNI input methods.</p>
|
|
||||||
</description>
|
|
||||||
<metadata_license>MIT</metadata_license>
|
|
||||||
<project_license>MIT</project_license>
|
|
||||||
<url type='homepage'>https://github.com/vndangkhoa/vietc</url>
|
|
||||||
<provides><binary>vietc-daemon</binary></provides>
|
|
||||||
<categories><category>Utility</category></categories>
|
|
||||||
</component>
|
|
||||||
XML
|
|
||||||
"
|
|
||||||
|
|
||||||
# Finish
|
|
||||||
echo ""
|
|
||||||
echo "=== Finalizing build... ==="
|
|
||||||
flatpak build-finish build-dir \
|
|
||||||
--socket=x11 \
|
|
||||||
--socket=wayland \
|
|
||||||
--socket=session-bus \
|
|
||||||
--device=all \
|
|
||||||
--share=ipc \
|
|
||||||
--talk-name=org.freedesktop.Notifications \
|
|
||||||
--talk-name=org.a11y.Bus \
|
|
||||||
--talk-name=org.freedesktop.portal.Clipboard \
|
|
||||||
--command=vietc-tray
|
|
||||||
|
|
||||||
# Export
|
|
||||||
echo ""
|
|
||||||
echo "=== Exporting to repository... ==="
|
|
||||||
flatpak build-export repo build-dir
|
|
||||||
|
|
||||||
# Bundle
|
|
||||||
echo ""
|
|
||||||
echo "=== Creating bundle... ==="
|
|
||||||
flatpak build-bundle repo "VietPlus-${VERSION}.flatpak" io.github.vietc.VietPlus
|
|
||||||
|
|
||||||
echo ""
|
|
||||||
echo "=== Done ==="
|
|
||||||
echo "Package: $SCRIPT_DIR/VietPlus-${VERSION}.flatpak"
|
|
||||||
echo "Size: $(du -h "$SCRIPT_DIR/VietPlus-${VERSION}.flatpak" | cut -f1)"
|
|
||||||
echo ""
|
|
||||||
echo "Install: flatpak install --user --bundle VietPlus-${VERSION}.flatpak"
|
|
||||||
echo "Run: flatpak run io.github.vietc.VietPlus"
|
|
||||||
echo "Search: 'Viet+' in app menu"
|
|
||||||
|
|
@ -1,55 +0,0 @@
|
||||||
{
|
|
||||||
"app-id": "io.github.vietc.VietPlus",
|
|
||||||
"runtime": "org.gnome.Platform",
|
|
||||||
"runtime-version": "50",
|
|
||||||
"sdk": "org.gnome.Sdk",
|
|
||||||
"sdk-extensions": [
|
|
||||||
"org.freedesktop.Sdk.Extension.rust-stable"
|
|
||||||
],
|
|
||||||
"command": "vietc-daemon",
|
|
||||||
"finish-args": [
|
|
||||||
"--socket=x11",
|
|
||||||
"--socket=wayland",
|
|
||||||
"--socket=session-bus",
|
|
||||||
"--device=all",
|
|
||||||
"--share=ipc",
|
|
||||||
"--talk-name=org.freedesktop.Notifications",
|
|
||||||
"--talk-name=org.a11y.Bus",
|
|
||||||
"--talk-name=org.freedesktop.portal.Clipboard"
|
|
||||||
],
|
|
||||||
"modules": [
|
|
||||||
{
|
|
||||||
"name": "vietc",
|
|
||||||
"buildsystem": "simple",
|
|
||||||
"build-options": {
|
|
||||||
"append-path": "/usr/lib/sdk/rust-stable/bin",
|
|
||||||
"env": {
|
|
||||||
"CARGO_HOME": "/run/build/vietc/cargo"
|
|
||||||
}
|
|
||||||
},
|
|
||||||
"build-commands": [
|
|
||||||
"cargo build --release -p vietc-daemon -p vietc-cli -p vietc-uinputd --manifest-path /run/build/vietc/Cargo.toml",
|
|
||||||
|
|
||||||
"install -Dm755 target/release/vietc /app/bin/vietc-daemon",
|
|
||||||
"install -Dm755 target/release/vietc-cli /app/bin/vietc-cli",
|
|
||||||
"install -Dm755 target/release/vietc-uinputd /app/bin/vietc-uinputd",
|
|
||||||
|
|
||||||
"install -Dm644 packaging/icons/vietc.svg /app/share/icons/hicolor/scalable/apps/io.github.vietc.VietPlus.svg",
|
|
||||||
"install -Dm644 packaging/icons/vietc-vn.svg /app/share/icons/hicolor/scalable/apps/io.github.vietc.VietPlus.vietc-vn.svg",
|
|
||||||
"install -Dm644 packaging/icons/vietc-en.svg /app/share/icons/hicolor/scalable/apps/io.github.vietc.VietPlus.vietc-en.svg",
|
|
||||||
|
|
||||||
"mkdir -p /app/share/applications",
|
|
||||||
"cat > /app/share/applications/io.github.vietc.VietPlus.desktop << END\n[Desktop Entry]\nName=Viet+\nComment=Vietnamese Input Method\nExec=/app/bin/vietc-daemon\nIcon=io.github.vietc.VietPlus\nTerminal=false\nType=Application\nCategories=Utility;\nEND",
|
|
||||||
|
|
||||||
"mkdir -p /app/share/metainfo",
|
|
||||||
"cat > /app/share/metainfo/io.github.vietc.VietPlus.metainfo.xml << 'XML'\n<?xml version='1.0' encoding='utf-8'?>\n<component type='desktop-application'>\n <id>io.github.vietc.VietPlus</id>\n <name>Viet+</name>\n <summary>Vietnamese Input Method for Linux</summary>\n <description>\n <p>Zero-configuration Vietnamese input method engine supporting Telex and VNI input methods.</p>\n </description>\n <metadata_license>MIT</metadata_license>\n <project_license>MIT</project_license>\n <url type='homepage'>https://github.com/vndangkhoa/vietc</url>\n <provides><binary>vietc-daemon</binary></provides>\n <categories><category>Utility</category></categories>\n</component>\nXML"
|
|
||||||
],
|
|
||||||
"sources": [
|
|
||||||
{
|
|
||||||
"type": "dir",
|
|
||||||
"path": "../.."
|
|
||||||
}
|
|
||||||
]
|
|
||||||
}
|
|
||||||
]
|
|
||||||
}
|
|
||||||
|
|
@ -1,39 +0,0 @@
|
||||||
#!/bin/sh
|
|
||||||
# Viet+ Flatpak entry point
|
|
||||||
# Starts the daemon and optionally the system tray interface
|
|
||||||
|
|
||||||
HERE="$(dirname "$(readlink -f "${0}")")"
|
|
||||||
export PATH="$HERE:$PATH"
|
|
||||||
|
|
||||||
CONFIG_DIR="${XDG_CONFIG_HOME:-$HOME/.config}/vietc"
|
|
||||||
mkdir -p "$CONFIG_DIR" "$HOME/.vietc"
|
|
||||||
|
|
||||||
# Kill old processes
|
|
||||||
pkill -x vietc 2>/dev/null || true
|
|
||||||
pkill -x vietc-xrecord 2>/dev/null || true
|
|
||||||
|
|
||||||
# Start daemon in background
|
|
||||||
"$HERE/vietc" > "$CONFIG_DIR/vietc-daemon.log" 2>&1 &
|
|
||||||
DAEMON_PID=$!
|
|
||||||
|
|
||||||
cleanup() {
|
|
||||||
if [ -n "$DAEMON_PID" ]; then
|
|
||||||
kill "$DAEMON_PID" 2>/dev/null
|
|
||||||
wait "$DAEMON_PID" 2>/dev/null
|
|
||||||
fi
|
|
||||||
}
|
|
||||||
trap cleanup EXIT INT TERM
|
|
||||||
|
|
||||||
# Start tray if available
|
|
||||||
if [ -f "$HERE/vietc-tray" ]; then
|
|
||||||
"$HERE/vietc-tray" "$@"
|
|
||||||
exit $?
|
|
||||||
fi
|
|
||||||
|
|
||||||
# No tray: show notification if available
|
|
||||||
if command -v notify-send >/dev/null 2>&1; then
|
|
||||||
notify-send "Viet+" "Input method running in background" -t 3000
|
|
||||||
fi
|
|
||||||
|
|
||||||
echo "[vietc] Running (PID=$DAEMON_PID). Ctrl+C to stop."
|
|
||||||
wait $DAEMON_PID
|
|
||||||
|
|
@ -1,6 +1,6 @@
|
||||||
[package]
|
[package]
|
||||||
name = "vietc-protocol"
|
name = "vietc-protocol"
|
||||||
version = "0.1.0"
|
version = "0.1.6"
|
||||||
edition = "2021"
|
edition = "2021"
|
||||||
description = "Viet+ keystroke injection backends (X11/Wayland)"
|
description = "Viet+ keystroke injection backends (X11/Wayland)"
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -388,20 +388,19 @@ impl X11Injector {
|
||||||
// (unlikely at this point, but be safe)
|
// (unlikely at this point, but be safe)
|
||||||
self.handle_pending_events();
|
self.handle_pending_events();
|
||||||
|
|
||||||
// Send backspaces via XTest
|
// Send backspaces via XTest (X11 keycode 22 = backspace)
|
||||||
if backspaces > 0 {
|
if backspaces > 0 {
|
||||||
for _ in 0..backspaces {
|
for _ in 0..backspaces {
|
||||||
self.send_keycode(14, false); // KEY_BACKSPACE
|
self.send_keycode(22, false);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// Send Ctrl+V via XTest to paste
|
// Send Ctrl+V via XTest to paste (evdev codes + 8 = X11)
|
||||||
unsafe {
|
unsafe {
|
||||||
// X11 keycodes: 37 = Ctrl_L, 55 = V
|
(self.lib.x_test_fake_key_event)(self.display, 29 + 8, 1, 0); // Ctrl_L press
|
||||||
(self.lib.x_test_fake_key_event)(self.display, 37, 1, 0);
|
(self.lib.x_test_fake_key_event)(self.display, 47 + 8, 1, 0); // V press
|
||||||
(self.lib.x_test_fake_key_event)(self.display, 55, 1, 0);
|
(self.lib.x_test_fake_key_event)(self.display, 47 + 8, 0, 0); // V release
|
||||||
(self.lib.x_test_fake_key_event)(self.display, 55, 0, 0);
|
(self.lib.x_test_fake_key_event)(self.display, 29 + 8, 0, 0); // Ctrl_L release
|
||||||
(self.lib.x_test_fake_key_event)(self.display, 37, 0, 0);
|
|
||||||
(self.lib.x_flush)(self.display);
|
(self.lib.x_flush)(self.display);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -416,15 +415,16 @@ impl X11Injector {
|
||||||
true
|
true
|
||||||
}
|
}
|
||||||
|
|
||||||
fn send_keycode(&self, keycode: u32, shift: bool) {
|
fn send_keycode(&self, evdev_keycode: u32, shift: bool) {
|
||||||
|
let x11 = evdev_keycode + 8;
|
||||||
unsafe {
|
unsafe {
|
||||||
if shift {
|
if shift {
|
||||||
(self.lib.x_test_fake_key_event)(self.display, 50, 1, 0);
|
(self.lib.x_test_fake_key_event)(self.display, 42 + 8, 1, 0); // Shift_L
|
||||||
}
|
}
|
||||||
(self.lib.x_test_fake_key_event)(self.display, keycode, 1, 0);
|
(self.lib.x_test_fake_key_event)(self.display, x11, 1, 0);
|
||||||
(self.lib.x_test_fake_key_event)(self.display, keycode, 0, 0);
|
(self.lib.x_test_fake_key_event)(self.display, x11, 0, 0);
|
||||||
if shift {
|
if shift {
|
||||||
(self.lib.x_test_fake_key_event)(self.display, 50, 0, 0);
|
(self.lib.x_test_fake_key_event)(self.display, 42 + 8, 0, 0);
|
||||||
}
|
}
|
||||||
(self.lib.x_flush)(self.display);
|
(self.lib.x_flush)(self.display);
|
||||||
}
|
}
|
||||||
|
|
@ -484,15 +484,17 @@ struct XSelectionNotifyEvent {
|
||||||
|
|
||||||
impl KeyInjector for X11Injector {
|
impl KeyInjector for X11Injector {
|
||||||
fn send_key_event(&self, keycode: u16, value: i32) -> InjectResult {
|
fn send_key_event(&self, keycode: u16, value: i32) -> InjectResult {
|
||||||
|
// X11 keycodes = Linux evdev keycodes + 8
|
||||||
|
let x11_keycode = keycode as u32 + 8;
|
||||||
unsafe {
|
unsafe {
|
||||||
(self.lib.x_test_fake_key_event)(self.display, keycode as u32, value, 0);
|
(self.lib.x_test_fake_key_event)(self.display, x11_keycode, value, 0);
|
||||||
(self.lib.x_flush)(self.display);
|
(self.lib.x_flush)(self.display);
|
||||||
}
|
}
|
||||||
InjectResult::Success
|
InjectResult::Success
|
||||||
}
|
}
|
||||||
|
|
||||||
fn send_backspace(&self) -> InjectResult {
|
fn send_backspace(&self) -> InjectResult {
|
||||||
self.send_keycode(14, false);
|
self.send_keycode(22, false); // X11 keycode 22 = backspace
|
||||||
InjectResult::Success
|
InjectResult::Success
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -1,6 +1,6 @@
|
||||||
[package]
|
[package]
|
||||||
name = "vietc-tray"
|
name = "vietc-tray"
|
||||||
version = "0.1.0"
|
version = "0.1.6"
|
||||||
edition = "2021"
|
edition = "2021"
|
||||||
description = "Viet+ system tray icon"
|
description = "Viet+ system tray icon"
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -71,7 +71,7 @@ pub struct Config {
|
||||||
}
|
}
|
||||||
|
|
||||||
fn default_input_method() -> String {
|
fn default_input_method() -> String {
|
||||||
"telex".into()
|
"vni".into()
|
||||||
}
|
}
|
||||||
fn default_toggle_key() -> String {
|
fn default_toggle_key() -> String {
|
||||||
"space".into()
|
"space".into()
|
||||||
|
|
@ -80,7 +80,7 @@ fn default_start_enabled() -> bool {
|
||||||
true
|
true
|
||||||
}
|
}
|
||||||
fn default_grab() -> bool {
|
fn default_grab() -> bool {
|
||||||
true
|
false
|
||||||
}
|
}
|
||||||
fn default_true() -> bool {
|
fn default_true() -> bool {
|
||||||
true
|
true
|
||||||
|
|
@ -150,6 +150,7 @@ fn config_paths() -> Vec<PathBuf> {
|
||||||
}
|
}
|
||||||
|
|
||||||
paths.push(PathBuf::from("vietc.toml"));
|
paths.push(PathBuf::from("vietc.toml"));
|
||||||
|
paths.push(PathBuf::from("/etc/vietc/config.toml"));
|
||||||
|
|
||||||
paths
|
paths
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -49,6 +49,14 @@ fn needs_root() -> bool {
|
||||||
// Inside Flatpak the sandbox already has device access; sudo won't work.
|
// Inside Flatpak the sandbox already has device access; sudo won't work.
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
// Check if we can access /dev/uinput directly (user in input group or has ACL)
|
||||||
|
let uinput = std::fs::OpenOptions::new()
|
||||||
|
.read(true)
|
||||||
|
.write(true)
|
||||||
|
.open("/dev/uinput");
|
||||||
|
if uinput.is_ok() {
|
||||||
|
return false; // Can grab + inject without root
|
||||||
|
}
|
||||||
let cfg = config::Config::load();
|
let cfg = config::Config::load();
|
||||||
cfg.grab
|
cfg.grab
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -413,7 +413,9 @@ impl Tray for VietTray {
|
||||||
}),
|
}),
|
||||||
options: vec![
|
options: vec![
|
||||||
RadioItem {
|
RadioItem {
|
||||||
label: "Telex".into(),
|
label: "Telex (next version)".into(),
|
||||||
|
enabled: false,
|
||||||
|
disposition: Disposition::Informative,
|
||||||
..Default::default()
|
..Default::default()
|
||||||
},
|
},
|
||||||
RadioItem {
|
RadioItem {
|
||||||
|
|
|
||||||
|
|
@ -1,6 +1,6 @@
|
||||||
[package]
|
[package]
|
||||||
name = "vietc-uinputd"
|
name = "vietc-uinputd"
|
||||||
version = "0.1.0"
|
version = "0.1.6"
|
||||||
edition = "2021"
|
edition = "2021"
|
||||||
description = "Viet+ privileged uinput backspace injection daemon"
|
description = "Viet+ privileged uinput backspace injection daemon"
|
||||||
|
|
||||||
|
|
|
||||||
Loading…
Reference in a new issue