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
|
||||
|
||||
# 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:
|
||||
# - 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:
|
||||
push:
|
||||
branches: [main]
|
||||
|
|
@ -40,7 +40,7 @@ jobs:
|
|||
run: cargo test --release
|
||||
|
||||
package:
|
||||
name: Build packages
|
||||
name: Build .deb
|
||||
needs: test
|
||||
runs-on: ubuntu-latest
|
||||
steps:
|
||||
|
|
@ -71,31 +71,19 @@ jobs:
|
|||
echo "short_sha=$(git rev-parse --short HEAD)" >> "$GITHUB_OUTPUT"
|
||||
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
|
||||
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
|
||||
run: |
|
||||
mkdir -p dist
|
||||
cp packaging/deb/*.deb dist/
|
||||
cp packaging/appimage/*.AppImage dist/
|
||||
ls -la dist
|
||||
|
||||
- name: Upload artifacts
|
||||
uses: actions/upload-artifact@v4
|
||||
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/*
|
||||
if-no-files-found: error
|
||||
|
||||
|
|
|
|||
25
CHANGELOG.md
25
CHANGELOG.md
|
|
@ -1,5 +1,30 @@
|
|||
# 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)
|
||||
|
||||
### 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:
|
||||
|
|
@ -76,26 +76,15 @@ install-config:
|
|||
cp vietc.toml ~/.config/vietc/config.toml
|
||||
@echo "Config installed to ~/.config/vietc/config.toml"
|
||||
|
||||
# Build .deb package (requires dpkg-deb)
|
||||
# Build .deb package
|
||||
deb:
|
||||
VERSION=$$(grep '^version' engine/Cargo.toml | head -1 | sed 's/.*"\(.*\)"/\1/') && \
|
||||
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:
|
||||
cargo clean
|
||||
cd ui && cargo clean
|
||||
rm -rf packaging/appimage/AppDir packaging/appimage/*.AppImage
|
||||
|
||||
# Format code
|
||||
fmt:
|
||||
|
|
@ -107,10 +96,6 @@ lint:
|
|||
cargo clippy -- -D warnings
|
||||
cd ui && cargo clippy -- -D warnings
|
||||
|
||||
# Flatpak build
|
||||
flatpak:
|
||||
cd packaging/flatpak && bash build-flatpak.sh
|
||||
|
||||
# Show project structure
|
||||
tree:
|
||||
@find . -type f \( -name "*.rs" -o -name "*.toml" \) | grep -v target | sort
|
||||
|
|
|
|||
105
README.md
105
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/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/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/Event_Sourcing-✓-blueviolet?style=for-the-badge" alt="Event Sourcing">
|
||||
</p>
|
||||
|
|
@ -47,11 +47,11 @@ Physical Keyboard
|
|||
│ Stage 1: KEY CAPTURE │
|
||||
│ │
|
||||
│ evdev: /dev/input/event* grabs keyboard (primary, reliable) │
|
||||
│ X11: XRecord passive monitoring (fallback) │
|
||||
│ X11: XRecord passive monitoring (fallback) │
|
||||
│ │
|
||||
│ ┌─────────────┐ ┌──────────────┐ ┌──────────────────┐ │
|
||||
│ │ evdev grab │ │ X11Capture │ │ FocusIn/FocusOut │ │
|
||||
│ │ (libevdev) │ │ (XRecord) │ │ detection │ │
|
||||
│ │ evdev grab │ │ X11Capture │ │ Window switch │ │
|
||||
│ │ (libevdev) │ │ (XRecord) │ │ detection (250ms)│ │
|
||||
│ └─────────────┘ └──────────────┘ └──────────────────┘ │
|
||||
└──────────────────────────────────────────────────────────────┘
|
||||
│
|
||||
|
|
@ -63,7 +63,7 @@ Physical Keyboard
|
|||
│ Ctrl+Space → toggle Vietnamese ON/OFF │
|
||||
│ Backspace → replay_backspace() │
|
||||
│ 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 │
|
||||
│ │
|
||||
│ Primary: uinput injection (evdev keycodes, correct on all │
|
||||
│ display servers — routed through libinput on modern X11) │
|
||||
│ ASCII: direct Linux keycodes via /dev/uinput │
|
||||
│ Backspace: Linux keycode 14 via uinput │
|
||||
│ Vietnamese Unicode: clipboard paste + trailing ASCII via │
|
||||
│ uinput (split only at whitespace/punctuation boundary) │
|
||||
│ uinput Ctrl+V via /dev/uinput (no X11 dependency) │
|
||||
│ Backspace: Linux keycode 14 via uinput │
|
||||
│ Vietnamese Unicode: clipboard paste + trailing ASCII via │
|
||||
│ uinput (split only at whitespace/punctuation boundary) │
|
||||
│ uinput Ctrl+V via /dev/uinput (no X11 dependency) │
|
||||
│ │
|
||||
│ Fallback: 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()
|
||||
│ ├── event.rs # Event Sourcing: InputEvent, EventStore, Command
|
||||
│ ├── 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
|
||||
│
|
||||
├── protocol/ # Keyboard capture & injection
|
||||
│ ├── inject.rs # KeyInjector trait
|
||||
│ ├── x11_capture.rs # XRecord keyboard capture via C helper
|
||||
│ ├── x11_inject.rs # XTest injection + direct clipboard
|
||||
│ ├── uinput_monitor.rs # /dev/uinput injection for ASCII + Unicode
|
||||
│ ├── x11_inject.rs # XTest injection (fallback)
|
||||
│ ├── uinput_monitor.rs # /dev/uinput injection (primary)
|
||||
│ ├── uinput_client.rs # Unix socket client for vietc-uinputd
|
||||
│ └── wayland_im.rs # Wayland IM protocol
|
||||
│
|
||||
|
|
@ -157,7 +159,7 @@ vietc/
|
|||
│ └── main.rs # Tray + daemon launcher
|
||||
│
|
||||
├── cli/ # Interactive test harness
|
||||
├── packaging/ # AppImage + deb build scripts
|
||||
├── packaging/ # .deb packaging scripts
|
||||
└── vietc.toml # Default configuration
|
||||
```
|
||||
|
||||
|
|
@ -166,12 +168,12 @@ vietc/
|
|||
```
|
||||
┌─────────────────────────────────────────────────────────────┐
|
||||
│ vietc-tray │
|
||||
│ (System tray icon, daemon launcher, password prompt) │
|
||||
│ (System tray icon, daemon launcher) │
|
||||
└───────────────────────┬─────────────────────────────────────┘
|
||||
│ starts
|
||||
▼
|
||||
┌─────────────────────────────────────────────────────────────┐
|
||||
│ vietc (daemon) │
|
||||
│ vietc-daemon │
|
||||
│ │
|
||||
│ ┌──────────────┐ ┌──────────────┐ ┌──────────────────┐ │
|
||||
│ │ Config │ │ App State │ │ Display │ │
|
||||
|
|
@ -183,7 +185,7 @@ vietc/
|
|||
│ ┌──────▼──────┐ │
|
||||
│ │ Event Loop │ │
|
||||
│ │ │ │
|
||||
│ │ X11: grab │ │
|
||||
│ │ evdev: grab │ │
|
||||
│ │ keyboard │ │
|
||||
│ │ │ │
|
||||
│ │ Process │ │
|
||||
|
|
@ -198,12 +200,12 @@ vietc/
|
|||
│ │
|
||||
│ ┌────────────────────────────────────────────────────────┐ │
|
||||
│ │ vietc-engine │ │
|
||||
│ │ TelexEngine / VniEngine / EnglishDict / Spelling │ │
|
||||
│ │ VniEngine / EnglishDict / Spelling │ │
|
||||
│ └────────────────────────────────────────────────────────┘ │
|
||||
│ │
|
||||
│ ┌────────────────────────────────────────────────────────┐ │
|
||||
│ │ vietc-protocol │ │
|
||||
│ │ X11Capture / X11Injector / UinputInjector / Wayland │ │
|
||||
│ │ UinputInjector / X11Injector / X11Capture / Wayland │ │
|
||||
│ └────────────────────────────────────────────────────────┘ │
|
||||
└─────────────────────────────────────────────────────────────┘
|
||||
```
|
||||
|
|
@ -212,24 +214,7 @@ vietc/
|
|||
|
||||
## Input Methods
|
||||
|
||||
### Telex
|
||||
|
||||
| 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
|
||||
### VNI (default, Telex coming in next version)
|
||||
|
||||
| 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 |
|
||||
|---------|-------------|
|
||||
| **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 |
|
||||
| **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`) |
|
||||
| **Tone Placement** | Correct tone positioning for all Vietnamese diphthongs (io→gió, uâ→xuất, yê→nguyễn) |
|
||||
| **Macro Expansion** | `ko` → `không`, `dc` → `được`, custom shortcuts |
|
||||
| **Casing Preservation** | `Tieengs` → `Tiếng`, `TIEENGS` → `TIẾNG` |
|
||||
| **App Memory** | Per-app Vietnamese/English state, saved to `overrides.toml` |
|
||||
| **Hot Reload** | Config changes apply without restart (polls mtime every 1.5s) |
|
||||
| **Window-Switch Reset** | 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 |
|
||||
| **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
|
||||
|
||||
### 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
|
||||
# Install
|
||||
flatpak install --user --bundle VietPlus-x86_64.flatpak
|
||||
sudo dpkg -i vietc_0.1.6-1_amd64.deb
|
||||
|
||||
# Launch via app menu, or:
|
||||
flatpak run io.github.vietc.VietPlus
|
||||
|
||||
# Uninstall
|
||||
flatpak uninstall --user io.github.vietc.VietPlus
|
||||
# Log out and log back in (for input group membership to take effect)
|
||||
# Then launch "Viet+" from your application menu
|
||||
```
|
||||
|
||||
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
|
||||
git clone https://github.com/vndangkhoa/vietc.git
|
||||
cd vietc/packaging/flatpak
|
||||
bash build-flatpak.sh [version]
|
||||
cd vietc
|
||||
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`
|
||||
|
||||
```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.
|
||||
Requires Rust toolchain, `pkg-config`, `libx11-dev`, `libxtst-dev`, `libevdev-dev`. See `packaging/deb/build-deb.sh` for details.
|
||||
|
||||
---
|
||||
|
||||
|
|
|
|||
|
|
@ -3,7 +3,7 @@
|
|||
## When to release
|
||||
|
||||
- New feature or bugfix that should be distributed to users
|
||||
- Flatpak build changes validated
|
||||
- .deb packaging changes validated
|
||||
- All tests passing (`cargo test`)
|
||||
|
||||
---
|
||||
|
|
@ -38,25 +38,29 @@ Add a new entry under the version heading:
|
|||
- behavior changes...
|
||||
```
|
||||
|
||||
### 3. Build the Flatpak
|
||||
### 3. Build the .deb
|
||||
|
||||
```bash
|
||||
cd packaging/flatpak
|
||||
bash build-flatpak.sh X.Y.Z
|
||||
make deb
|
||||
```
|
||||
|
||||
Verify the bundle was created:
|
||||
```bash
|
||||
ls -lh VietPlus-X.Y.Z.flatpak
|
||||
```
|
||||
|
||||
### 4. Test the Flatpak
|
||||
Verify the package was created:
|
||||
|
||||
```bash
|
||||
flatpak install --user --bundle VietPlus-X.Y.Z.flatpak
|
||||
flatpak run io.github.vietc.VietPlus
|
||||
ls -lh packaging/deb/vietc_*.deb
|
||||
```
|
||||
|
||||
### 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
|
||||
|
||||
```bash
|
||||
|
|
@ -67,12 +71,7 @@ git push origin main
|
|||
|
||||
### 6. Create a release on Forgejo/GitHub
|
||||
|
||||
Attach the Flatpak bundle (`VietPlus-X.Y.Z.flatpak`) as a release asset.
|
||||
|
||||
```bash
|
||||
# Using forgejo-release (if configured)
|
||||
# Or manually upload via the web UI
|
||||
```
|
||||
Attach the .deb package (`vietc_X.Y.Z-1_amd64.deb`) as a release asset.
|
||||
|
||||
---
|
||||
|
||||
|
|
|
|||
|
|
@ -1,6 +1,6 @@
|
|||
[package]
|
||||
name = "vietc-cli"
|
||||
version = "0.1.0"
|
||||
version = "0.1.6"
|
||||
edition = "2021"
|
||||
description = "Viet+ CLI Test Harness"
|
||||
|
||||
|
|
|
|||
|
|
@ -1,6 +1,6 @@
|
|||
[package]
|
||||
name = "vietc-daemon"
|
||||
version = "0.1.0"
|
||||
version = "0.1.6"
|
||||
edition = "2021"
|
||||
description = "Viet+ background daemon"
|
||||
|
||||
|
|
|
|||
|
|
@ -196,7 +196,7 @@ impl Default for Config {
|
|||
auto_restore: AutoRestoreConfig::default(),
|
||||
app_state: AppStateConfig::default(),
|
||||
macros,
|
||||
grab: false,
|
||||
grab: false, // default false so daemon works without root (needs input group for uinput)
|
||||
debug: false,
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -1027,14 +1027,13 @@ fn run_with_evdev(
|
|||
|
||||
if active_window_id != last_active_window {
|
||||
new_window = Some(active_window_id.clone());
|
||||
} else if gap > std::time::Duration::from_millis(100) {
|
||||
// Background thread hasn't caught up yet — poll xdotool directly
|
||||
} else {
|
||||
// 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 id != active_window_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));
|
||||
}
|
||||
}
|
||||
|
||||
fn create_injector(
|
||||
display: display::DisplayServer,
|
||||
) -> Result<Box<dyn vietc_protocol::KeyInjector>, Box<dyn std::error::Error>> {
|
||||
// Try uinputd socket first
|
||||
if vietc_protocol::uinput_client::UinputClient::is_available() {
|
||||
log_info("[vietc] Using uinputd socket injection");
|
||||
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.
|
||||
// Prefer uinput injection — uses correct Linux keycodes for backspace
|
||||
// and ASCII, works on both X11 and Wayland (uinput devices are routed
|
||||
// through libinput on modern X11).
|
||||
match vietc_protocol::uinput_monitor::UinputInjector::new("vietc") {
|
||||
Ok(injector) => {
|
||||
log_info("[vietc] Using uinput injection (primary)");
|
||||
log_info("[vietc] Using uinput injection");
|
||||
return Ok(Box::new(injector));
|
||||
}
|
||||
Err(e) => {
|
||||
|
|
@ -1287,18 +1279,23 @@ 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")]
|
||||
{
|
||||
if display != display::DisplayServer::Wayland {
|
||||
match vietc_protocol::x11_inject::X11Injector::new() {
|
||||
Ok(injector) => {
|
||||
log_info("[vietc] Using X11 injection (fallback)");
|
||||
return Ok(Box::new(injector));
|
||||
}
|
||||
Err(e) => {
|
||||
log_info(&format!("[vietc] X11 not available: {}", e));
|
||||
}
|
||||
if display != display::DisplayServer::Wayland {
|
||||
match vietc_protocol::x11_inject::X11Injector::new() {
|
||||
Ok(injector) => {
|
||||
log_info("[vietc] Using X11 injection (fallback)");
|
||||
return Ok(Box::new(injector));
|
||||
}
|
||||
Err(e) => {
|
||||
log_info(&format!("[vietc] X11 not available: {}", e));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -1,6 +1,6 @@
|
|||
[package]
|
||||
name = "vietc-engine"
|
||||
version = "0.1.0"
|
||||
version = "0.1.6"
|
||||
edition = "2021"
|
||||
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)"
|
||||
PROJECT_ROOT="$(cd "$SCRIPT_DIR/../.." && pwd)"
|
||||
VERSION="${1:-0.1.0}"
|
||||
VERSION="${1:-0.1.6}"
|
||||
PACKAGE="vietc_${VERSION}-1_amd64"
|
||||
STAGING="$SCRIPT_DIR/$PACKAGE"
|
||||
|
||||
|
|
@ -12,7 +12,7 @@ echo "=== Building Viet+ .deb package v${VERSION} ==="
|
|||
# Build binaries (all features: x11 + wayland)
|
||||
echo "[1/5] Building binaries..."
|
||||
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."
|
||||
|
||||
# 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/doc/vietc"
|
||||
mkdir -p "$STAGING/usr/share/metainfo"
|
||||
mkdir -p "$STAGING/etc/xdg/autostart"
|
||||
|
||||
# Copy 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/"
|
||||
# Privileged uinput injection daemon — required for Unicode (Vietnamese) output.
|
||||
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)
|
||||
if command -v gcc &>/dev/null; then
|
||||
gcc -O2 -o "$STAGING/usr/bin/vietc-xrecord" "$PROJECT_ROOT/packaging/appimage/vietc-xrecord.c" -lX11 -lXtst \
|
||||
&& echo " vietc-xrecord compiled" \
|
||||
|| echo " WARNING: vietc-xrecord compile failed (libX11/libXtst dev headers missing)"
|
||||
else
|
||||
echo " WARNING: no gcc, vietc-xrecord not bundled"
|
||||
fi
|
||||
gcc -O2 -o "$STAGING/usr/bin/vietc-xrecord" "$SCRIPT_DIR/vietc-xrecord.c" -lX11 -lXtst
|
||||
|
||||
# Icons (main app icon + tray status icons)
|
||||
cp "$PROJECT_ROOT/packaging/icons/vietc.svg" "$STAGING/usr/share/icons/hicolor/256x256/apps/"
|
||||
cp "$PROJECT_ROOT/packaging/icons/vietc-vn.svg" "$STAGING/usr/share/icons/hicolor/256x256/apps/"
|
||||
cp "$PROJECT_ROOT/packaging/icons/vietc-en.svg" "$STAGING/usr/share/icons/hicolor/256x256/apps/"
|
||||
|
||||
# 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)
|
||||
cat > "$STAGING/usr/share/icons/hicolor/256x256/apps/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
|
||||
# XDG autostart — launches tray on every login for all users
|
||||
cat > "$STAGING/etc/xdg/autostart/vietc-tray.desktop" << 'AUTOSTART'
|
||||
[Desktop Entry]
|
||||
Type=Application
|
||||
Name=Viet+ Tray
|
||||
Comment=Vietnamese Input Method Tray
|
||||
Exec=vietc-tray
|
||||
Icon=vietc
|
||||
Terminal=false
|
||||
Categories=Utility;
|
||||
StartupNotify=false
|
||||
NoDisplay=true
|
||||
AUTOSTART
|
||||
|
||||
# Documentation
|
||||
cp "$PROJECT_ROOT/README.md" "$STAGING/usr/share/doc/vietc/"
|
||||
|
|
@ -82,8 +67,21 @@ cp "$PROJECT_ROOT/LICENSE" "$STAGING/usr/share/doc/vietc/"
|
|||
# Config
|
||||
cp "$PROJECT_ROOT/vietc.toml" "$STAGING/etc/vietc/config.toml"
|
||||
|
||||
# Systemd user service
|
||||
cp "$PROJECT_ROOT/vietc.service" "$STAGING/usr/lib/systemd/user/"
|
||||
# Systemd user service — tray spawns the daemon internally
|
||||
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
|
||||
cat > "$STAGING/usr/share/metainfo/io.github.anomalyco.vietc.appdata.xml" << 'XML'
|
||||
|
|
@ -124,7 +122,7 @@ Section: utils
|
|||
Priority: optional
|
||||
Architecture: amd64
|
||||
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>
|
||||
Description: Viet+ — Vietnamese Input Method for Linux
|
||||
Zero-configuration Vietnamese input method engine supporting
|
||||
|
|
@ -140,10 +138,73 @@ echo "/etc/vietc/config.toml" > "$STAGING/DEBIAN/conffiles"
|
|||
cat > "$STAGING/DEBIAN/postinst" << 'POSTINST'
|
||||
#!/bin/sh
|
||||
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
|
||||
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
|
||||
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
|
||||
;;
|
||||
esac
|
||||
|
|
@ -156,7 +217,7 @@ set -e
|
|||
case "$1" in
|
||||
remove|upgrade|deconfigure)
|
||||
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
|
||||
;;
|
||||
esac
|
||||
|
|
|
|||
|
|
@ -3,9 +3,9 @@ Type=Application
|
|||
Name=Viet+
|
||||
GenericName=Vietnamese Input Method
|
||||
Comment=Vietnamese Input Method for Linux — Zero underline, native Wayland/X11
|
||||
Exec=vietc
|
||||
Exec=vietc-tray
|
||||
Icon=vietc
|
||||
Terminal=false
|
||||
Categories=Utility;
|
||||
Keywords=vietnamese;input;ime;keyboard;
|
||||
StartupNotify=false
|
||||
Categories=Utility;TextTools;X-GNOME-Utilities;
|
||||
Keywords=vietnamese;input;ime;keyboard;viet;gõ tiếng việt;
|
||||
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]
|
||||
name = "vietc-protocol"
|
||||
version = "0.1.0"
|
||||
version = "0.1.6"
|
||||
edition = "2021"
|
||||
description = "Viet+ keystroke injection backends (X11/Wayland)"
|
||||
|
||||
|
|
|
|||
|
|
@ -388,20 +388,19 @@ impl X11Injector {
|
|||
// (unlikely at this point, but be safe)
|
||||
self.handle_pending_events();
|
||||
|
||||
// Send backspaces via XTest
|
||||
// Send backspaces via XTest (X11 keycode 22 = backspace)
|
||||
if backspaces > 0 {
|
||||
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 {
|
||||
// X11 keycodes: 37 = Ctrl_L, 55 = V
|
||||
(self.lib.x_test_fake_key_event)(self.display, 37, 1, 0);
|
||||
(self.lib.x_test_fake_key_event)(self.display, 55, 1, 0);
|
||||
(self.lib.x_test_fake_key_event)(self.display, 55, 0, 0);
|
||||
(self.lib.x_test_fake_key_event)(self.display, 37, 0, 0);
|
||||
(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, 47 + 8, 1, 0); // V press
|
||||
(self.lib.x_test_fake_key_event)(self.display, 47 + 8, 0, 0); // V release
|
||||
(self.lib.x_test_fake_key_event)(self.display, 29 + 8, 0, 0); // Ctrl_L release
|
||||
(self.lib.x_flush)(self.display);
|
||||
}
|
||||
|
||||
|
|
@ -416,15 +415,16 @@ impl X11Injector {
|
|||
true
|
||||
}
|
||||
|
||||
fn send_keycode(&self, keycode: u32, shift: bool) {
|
||||
fn send_keycode(&self, evdev_keycode: u32, shift: bool) {
|
||||
let x11 = evdev_keycode + 8;
|
||||
unsafe {
|
||||
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, keycode, 0, 0);
|
||||
(self.lib.x_test_fake_key_event)(self.display, x11, 1, 0);
|
||||
(self.lib.x_test_fake_key_event)(self.display, x11, 0, 0);
|
||||
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);
|
||||
}
|
||||
|
|
@ -484,15 +484,17 @@ struct XSelectionNotifyEvent {
|
|||
|
||||
impl KeyInjector for X11Injector {
|
||||
fn send_key_event(&self, keycode: u16, value: i32) -> InjectResult {
|
||||
// X11 keycodes = Linux evdev keycodes + 8
|
||||
let x11_keycode = keycode as u32 + 8;
|
||||
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);
|
||||
}
|
||||
InjectResult::Success
|
||||
}
|
||||
|
||||
fn send_backspace(&self) -> InjectResult {
|
||||
self.send_keycode(14, false);
|
||||
self.send_keycode(22, false); // X11 keycode 22 = backspace
|
||||
InjectResult::Success
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -1,6 +1,6 @@
|
|||
[package]
|
||||
name = "vietc-tray"
|
||||
version = "0.1.0"
|
||||
version = "0.1.6"
|
||||
edition = "2021"
|
||||
description = "Viet+ system tray icon"
|
||||
|
||||
|
|
|
|||
|
|
@ -71,7 +71,7 @@ pub struct Config {
|
|||
}
|
||||
|
||||
fn default_input_method() -> String {
|
||||
"telex".into()
|
||||
"vni".into()
|
||||
}
|
||||
fn default_toggle_key() -> String {
|
||||
"space".into()
|
||||
|
|
@ -80,7 +80,7 @@ fn default_start_enabled() -> bool {
|
|||
true
|
||||
}
|
||||
fn default_grab() -> bool {
|
||||
true
|
||||
false
|
||||
}
|
||||
fn default_true() -> bool {
|
||||
true
|
||||
|
|
@ -150,6 +150,7 @@ fn config_paths() -> Vec<PathBuf> {
|
|||
}
|
||||
|
||||
paths.push(PathBuf::from("vietc.toml"));
|
||||
paths.push(PathBuf::from("/etc/vietc/config.toml"));
|
||||
|
||||
paths
|
||||
}
|
||||
|
|
|
|||
|
|
@ -49,6 +49,14 @@ fn needs_root() -> bool {
|
|||
// Inside Flatpak the sandbox already has device access; sudo won't work.
|
||||
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();
|
||||
cfg.grab
|
||||
}
|
||||
|
|
|
|||
|
|
@ -413,7 +413,9 @@ impl Tray for VietTray {
|
|||
}),
|
||||
options: vec![
|
||||
RadioItem {
|
||||
label: "Telex".into(),
|
||||
label: "Telex (next version)".into(),
|
||||
enabled: false,
|
||||
disposition: Disposition::Informative,
|
||||
..Default::default()
|
||||
},
|
||||
RadioItem {
|
||||
|
|
|
|||
|
|
@ -1,6 +1,6 @@
|
|||
[package]
|
||||
name = "vietc-uinputd"
|
||||
version = "0.1.0"
|
||||
version = "0.1.6"
|
||||
edition = "2021"
|
||||
description = "Viet+ privileged uinput backspace injection daemon"
|
||||
|
||||
|
|
|
|||
Loading…
Reference in a new issue