vietc/protocol/src/uinput_client.rs
Khoa Vo d4102088b8 fix: X11 key lookup, bamboo engine port, uinput injection overhaul
- Fix Xutf8LookupString signature (missing XIC param caused all keys to map to \0)
- Port bamboo-core Vietnamese engine to Rust (bamboo.rs, input_method.rs)
- Flexible backtracking for mark/tone keys (scan up to 5 chars back)
- Correct tone placement for io, uâ, yê clusters
- Evdev capture preferred over X11 XRecord (more reliable)
- Uinput injection with correct Linux keycodes
- Vietnamese Unicode via clipboard paste + trailing ASCII via uinput
- Persistent X11 connection for Ctrl+V (no per-call dlopen overhead)
- Consume stale VNI/Telex control keys when no match found
- Fix execute_commands backspace count for evdev grabbing path
- Add vietc-uinputd privileged injection daemon
- AppImage: bundle uinputd, preserve LD_LIBRARY_PATH, fix xrecord build flags
- Remove old generated test files, add 63 focused engine tests
2026-06-26 15:20:03 +07:00

75 lines
2.1 KiB
Rust

use std::io::{BufRead, BufReader, Write};
use std::os::unix::net::UnixStream;
use std::path::PathBuf;
use super::inject::{InjectResult, KeyInjector};
fn socket_path() -> PathBuf {
let home = std::env::var("HOME").unwrap_or_else(|_| "/tmp".into());
PathBuf::from(home).join(".vietc").join("uinput.sock")
}
pub struct UinputClient;
impl UinputClient {
fn send_command(cmd: &str) -> InjectResult {
match UnixStream::connect(socket_path()) {
Ok(mut stream) => {
if writeln!(stream, "{}", cmd).is_err() {
return InjectResult::Failed;
}
let mut reader = BufReader::new(&stream);
let mut response = String::new();
if reader.read_line(&mut response).is_err() {
return InjectResult::Failed;
}
if response.trim() == "OK" {
InjectResult::Success
} else {
InjectResult::Failed
}
}
Err(_) => InjectResult::Failed,
}
}
pub fn is_available() -> bool {
UnixStream::connect(socket_path()).is_ok()
}
}
impl KeyInjector for UinputClient {
fn send_key_event(&self, _keycode: u16, _value: i32) -> InjectResult {
InjectResult::Success
}
fn send_backspace(&self) -> InjectResult {
InjectResult::Success
}
fn send_char(&self, _ch: char) -> InjectResult {
InjectResult::Success
}
fn send_string(&self, s: &str) -> InjectResult {
Self::send_command(&format!("TYPE:{}", s))
}
fn inject_replacement(&self, backspaces: usize, text: &str) -> InjectResult {
if backspaces > 0 {
let _ = Self::send_command(&format!("BACKSPACE:{}", backspaces));
}
if !text.is_empty() {
let _ = Self::send_command(&format!("TYPE:{}", text));
}
InjectResult::Success
}
fn flush(&self) -> InjectResult {
InjectResult::Success
}
fn update_pasted_text(&self, _text: &str) -> InjectResult {
InjectResult::Success
}
}