Fix Unicode injection: ydotoold + xclip fallback + input logging
- Start ydotoold automatically so ydotool can handle Vietnamese chars - Fix xclip clipboard to run as SUDO_USER when daemon is root - Add detailed input logging for easier debugging
This commit is contained in:
parent
96c8006070
commit
0f1f08e79f
2 changed files with 64 additions and 16 deletions
|
|
@ -130,6 +130,7 @@ impl Daemon {
|
||||||
let mut commands = Vec::new();
|
let mut commands = Vec::new();
|
||||||
|
|
||||||
if let Some(event) = self.engine.process_key(ch) {
|
if let Some(event) = self.engine.process_key(ch) {
|
||||||
|
eprintln!("[vietc] key='{}' buf='{}' -> {:?}", ch, self.engine.buffer(), event);
|
||||||
match event {
|
match event {
|
||||||
EngineEvent::Flush(text) => {
|
EngineEvent::Flush(text) => {
|
||||||
commands.push(OutputCommand::Type(text));
|
commands.push(OutputCommand::Type(text));
|
||||||
|
|
@ -151,6 +152,8 @@ impl Daemon {
|
||||||
commands.push(OutputCommand::Type(restored));
|
commands.push(OutputCommand::Type(restored));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
} else {
|
||||||
|
eprintln!("[vietc] key='{}' -> (no event, buf='{}')", ch, self.engine.buffer());
|
||||||
}
|
}
|
||||||
|
|
||||||
commands
|
commands
|
||||||
|
|
@ -576,20 +579,19 @@ fn execute_commands(injector: &dyn vietc_protocol::KeyInjector, commands: &[Outp
|
||||||
for cmd in commands {
|
for cmd in commands {
|
||||||
match cmd {
|
match cmd {
|
||||||
OutputCommand::Backspace(count) => {
|
OutputCommand::Backspace(count) => {
|
||||||
// The engine adds +1 to account for the current character key.
|
|
||||||
// With grabbing that key was never forwarded to the app, so
|
|
||||||
// we subtract 1. Without grab, the key WAS forwarded, so we
|
|
||||||
// use the full count.
|
|
||||||
let adjusted = if grabbed { count.saturating_sub(1) } else { *count };
|
let adjusted = if grabbed { count.saturating_sub(1) } else { *count };
|
||||||
|
eprintln!("[vietc] cmd: Backspace({}) -> adjusted={}", count, adjusted);
|
||||||
pending_backspaces += adjusted;
|
pending_backspaces += adjusted;
|
||||||
}
|
}
|
||||||
OutputCommand::Type(text) => {
|
OutputCommand::Type(text) => {
|
||||||
|
eprintln!("[vietc] cmd: Type(\"{}\")", text);
|
||||||
pending_text.push_str(text);
|
pending_text.push_str(text);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
if pending_backspaces > 0 || !pending_text.is_empty() {
|
if pending_backspaces > 0 || !pending_text.is_empty() {
|
||||||
|
eprintln!("[vietc] inject: BS={} text=\"{}\"", pending_backspaces, pending_text);
|
||||||
injector.inject_replacement(pending_backspaces, &pending_text);
|
injector.inject_replacement(pending_backspaces, &pending_text);
|
||||||
}
|
}
|
||||||
injector.flush();
|
injector.flush();
|
||||||
|
|
|
||||||
|
|
@ -24,7 +24,23 @@ unsafe impl Send for UinputInjector {}
|
||||||
unsafe impl Sync for UinputInjector {}
|
unsafe impl Sync for UinputInjector {}
|
||||||
|
|
||||||
impl UinputInjector {
|
impl UinputInjector {
|
||||||
|
fn start_ydotoold() {
|
||||||
|
// ydotoold must be running for ydotool to handle Unicode characters.
|
||||||
|
// ydotool in direct mode crashes with "no matching keycode" for
|
||||||
|
// non-ASCII chars. Start it once; ignore failure (daemon may already
|
||||||
|
// exist).
|
||||||
|
let _ = std::process::Command::new("ydotoold")
|
||||||
|
.arg("--fork")
|
||||||
|
.stdout(std::process::Stdio::null())
|
||||||
|
.stderr(std::process::Stdio::null())
|
||||||
|
.spawn();
|
||||||
|
// Give it a moment to start
|
||||||
|
std::thread::sleep(std::time::Duration::from_millis(200));
|
||||||
|
}
|
||||||
|
|
||||||
pub fn new(name: &str) -> Result<Self, Box<dyn std::error::Error>> {
|
pub fn new(name: &str) -> Result<Self, Box<dyn std::error::Error>> {
|
||||||
|
Self::start_ydotoold();
|
||||||
|
|
||||||
let file = OpenOptions::new()
|
let file = OpenOptions::new()
|
||||||
.read(true)
|
.read(true)
|
||||||
.write(true)
|
.write(true)
|
||||||
|
|
@ -332,18 +348,48 @@ impl UinputInjector {
|
||||||
{
|
{
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
// Try xclip (X11)
|
// Try xclip (X11). When root, run as SUDO_USER so it can connect to X.
|
||||||
std::process::Command::new("xclip")
|
let xclip_result = if is_root {
|
||||||
.args(["-selection", "clipboard"])
|
if let Ok(sudo_user) = std::env::var("SUDO_USER") {
|
||||||
.stdin(std::process::Stdio::piped())
|
let display = std::env::var("DISPLAY").unwrap_or_default();
|
||||||
.spawn()
|
let mut cmd = std::process::Command::new("sudo");
|
||||||
.and_then(|mut child| {
|
cmd.args(["-u", &sudo_user, "env"]);
|
||||||
use std::io::Write;
|
if !display.is_empty() {
|
||||||
child.stdin.take().unwrap().write_all(s.as_bytes())?;
|
cmd.arg(format!("DISPLAY={}", display));
|
||||||
child.wait()
|
}
|
||||||
})
|
cmd.arg("xclip");
|
||||||
.map(|status| status.success())
|
cmd.args(["-selection", "clipboard"]);
|
||||||
.unwrap_or(false)
|
cmd.stdin(std::process::Stdio::piped())
|
||||||
|
.spawn()
|
||||||
|
.and_then(|mut child| {
|
||||||
|
use std::io::Write;
|
||||||
|
child.stdin.take().unwrap().write_all(s.as_bytes())?;
|
||||||
|
child.wait()
|
||||||
|
})
|
||||||
|
.map(|status| status.success())
|
||||||
|
.unwrap_or(false)
|
||||||
|
} else {
|
||||||
|
false
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
std::process::Command::new("xclip")
|
||||||
|
.args(["-selection", "clipboard"])
|
||||||
|
.stdin(std::process::Stdio::piped())
|
||||||
|
.spawn()
|
||||||
|
.and_then(|mut child| {
|
||||||
|
use std::io::Write;
|
||||||
|
child.stdin.take().unwrap().write_all(s.as_bytes())?;
|
||||||
|
child.wait()
|
||||||
|
})
|
||||||
|
.map(|status| status.success())
|
||||||
|
.unwrap_or(false)
|
||||||
|
};
|
||||||
|
|
||||||
|
if xclip_result {
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
false
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Send Ctrl+V through our uinput device.
|
/// Send Ctrl+V through our uinput device.
|
||||||
|
|
|
||||||
Loading…
Reference in a new issue