Fix garbled text: correct AppRun Wayland detection + atomic injection

- AppRun no longer sets WAYLAND_DISPLAY on X11 (checks for wayland socket)
- execute_commands now uses ydotool for both backspaces+text (same device)
- Merged execute_commands and execute_commands_with_grab into one function
- grab defaults to true in AppImage config
This commit is contained in:
vndangkhoa 2026-06-24 17:33:13 +07:00
parent 95f661aaa0
commit c3bc35ddce
2 changed files with 17 additions and 28 deletions

View file

@ -428,7 +428,7 @@ fn run_with_evdev(
} }
if let Some(ch) = key_to_char(key) { if let Some(ch) = key_to_char(key) {
let commands = daemon.process_key(ch); let commands = daemon.process_key(ch);
execute_commands(&*injector, &commands); execute_commands(&*injector, &commands, false);
} }
} else { } else {
// Grabbing mode: all output goes through uinput only. // Grabbing mode: all output goes through uinput only.
@ -445,7 +445,7 @@ fn run_with_evdev(
let commands = daemon.process_key(ch); let commands = daemon.process_key(ch);
if !commands.is_empty() { if !commands.is_empty() {
consumed_keys.insert(keycode); consumed_keys.insert(keycode);
execute_commands_with_grab(&*injector, &commands); execute_commands(&*injector, &commands, true);
} else { } else {
injector.send_char(ch); injector.send_char(ch);
} }
@ -554,7 +554,7 @@ fn run_stdin_mode(
Ok(_) => { Ok(_) => {
let ch = buffer[0] as char; let ch = buffer[0] as char;
let commands = daemon.process_key(ch); let commands = daemon.process_key(ch);
execute_commands(&*injector, &commands); execute_commands(&*injector, &commands, false);
} }
Err(e) => { Err(e) => {
eprintln!("[vietc] Read error: {}", e); eprintln!("[vietc] Read error: {}", e);
@ -566,35 +566,21 @@ fn run_stdin_mode(
Ok(()) Ok(())
} }
fn execute_commands(injector: &dyn vietc_protocol::KeyInjector, commands: &[OutputCommand]) { /// Execute commands — accumulate backspaces and text, then inject through
for cmd in commands { /// a single channel (ydotool or wtype) to avoid reordering between backspaces
match cmd { /// (uinput) and text (ydotool).
OutputCommand::Type(text) => { fn execute_commands(injector: &dyn vietc_protocol::KeyInjector, commands: &[OutputCommand], grabbed: bool) {
injector.send_string(text);
}
OutputCommand::Backspace(count) => {
injector.send_backspaces(*count);
}
}
}
injector.flush();
}
/// Execute commands with keyboard grabbing active.
/// Uses inject_replacement to send backspaces + text through a single
/// injection call (wtype), avoiding compositor reordering between
/// uinput (backspaces) and wtype (text).
fn execute_commands_with_grab(injector: &dyn vietc_protocol::KeyInjector, commands: &[OutputCommand]) {
let mut pending_backspaces: usize = 0; let mut pending_backspaces: usize = 0;
let mut pending_text = String::new(); let mut pending_text = String::new();
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, // The engine adds +1 to account for the current character key.
// but with grabbing that key was never forwarded to the app, // With grabbing that key was never forwarded to the app, so
// so we subtract 1. // we subtract 1. Without grab, the key WAS forwarded, so we
let adjusted = count.saturating_sub(1); // use the full count.
let adjusted = if grabbed { count.saturating_sub(1) } else { *count };
pending_backspaces += adjusted; pending_backspaces += adjusted;
} }
OutputCommand::Type(text) => { OutputCommand::Type(text) => {

View file

@ -139,11 +139,14 @@ export PATH="$HERE/usr/bin:$PATH"
# Start daemon (kill old non-root one first if we have root) # Start daemon (kill old non-root one first if we have root)
SUDO_CMD="" SUDO_CMD=""
# Fix Wayland env for root: sudo resets XDG_RUNTIME_DIR, breaking wl-copy # Fix Wayland env for root: sudo resets XDG_RUNTIME_DIR, breaking wtype/wl-copy.
# Only set WAYLAND_DISPLAY if the user actually has a Wayland session.
if [ "$(id -u)" = "0" ] && [ -z "$XDG_RUNTIME_DIR" ] && [ -n "$SUDO_USER" ]; then if [ "$(id -u)" = "0" ] && [ -z "$XDG_RUNTIME_DIR" ] && [ -n "$SUDO_USER" ]; then
USER_UID=$(id -u "$SUDO_USER" 2>/dev/null || echo 1000) USER_UID=$(id -u "$SUDO_USER" 2>/dev/null || echo 1000)
export XDG_RUNTIME_DIR="/run/user/$USER_UID" 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}" export WAYLAND_DISPLAY="${WAYLAND_DISPLAY:-wayland-0}"
fi
fi fi
if command -v pkexec >/dev/null && [ -z "$WAYLAND_DISPLAY" ]; then if command -v pkexec >/dev/null && [ -z "$WAYLAND_DISPLAY" ]; then