fix: non-blocking XPending event loop + auto re-grab on grab loss

- Use XPending() to check for events before XNextEvent (non-blocking)
- Add is_grabbed() and has_pending_events() public methods
- Auto re-grab keyboard when grab is silently lost (tray, WM focus)
- Fixes AppImage daemon receiving zero keystroke events
This commit is contained in:
Khoa Vo 2026-06-26 09:17:47 +07:00
parent db3d0cefcd
commit 666f1b400e
4 changed files with 27 additions and 2 deletions

View file

@ -814,6 +814,12 @@ fn run_with_x11(
}
}
// Re-grab if the grab was silently lost (tray started, WM took focus, etc.)
if !capture.is_grabbed() {
eprintln!("[vietc] Keyboard grab lost — re-grabbing");
capture.grab_keyboard();
}
thread::sleep(Duration::from_millis(10));
}
}

View file

@ -208,7 +208,7 @@ fi
if [ -z "$NEED_ROOT" ]; then
# X11: no root needed
pkill -x vietc 2>/dev/null; sleep 0.3
"$HERE/usr/bin/vietc" >"$HERE/vietc-daemon.log" 2>&1 &
"$HERE/usr/bin/vietc" >"$HOME/.config/vietc/vietc-daemon.log" 2>&1 &
DAEMON_PID=$!
else
# Fix Wayland env for root: sudo resets XDG_RUNTIME_DIR, breaking wtype/wl-copy.

View file

@ -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" && cargo build --release) || echo " Warning: UI tray not built (libdbus-1-dev may be missing)"
(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)"
echo " Done."
# Clean and create staging

View file

@ -31,6 +31,7 @@ struct X11Lib {
x_default_root_window: unsafe extern "C" fn(*mut Display) -> Window,
x_grab_keyboard: unsafe extern "C" fn(*mut Display, Window, c_int, c_int, c_int, Time) -> c_int,
x_ungrab_keyboard: unsafe extern "C" fn(*mut Display, Time) -> c_int,
x_pending: unsafe extern "C" fn(*mut Display) -> c_int,
x_next_event: unsafe extern "C" fn(*mut Display, *mut XEvent),
x_lookup_string: unsafe extern "C" fn(*mut XKeyEvent, *mut c_char, c_int, *mut KeySym, *mut c_int) -> c_int,
x_utf8_lookup_string: Option<unsafe extern "C" fn(*mut XKeyEvent, *mut c_char, c_int, *mut KeySym, *mut c_int) -> c_int>,
@ -66,6 +67,7 @@ impl X11Lib {
let x_default_root_window = sym!("XDefaultRootWindow");
let x_grab_keyboard = sym!("XGrabKeyboard");
let x_ungrab_keyboard = sym!("XUngrabKeyboard");
let x_pending = sym!("XPending");
let x_next_event = sym!("XNextEvent");
let x_lookup_string = sym!("XLookupString");
let x_utf8_lookup_string = dlsym(handle, b"Xutf8LookupString\0".as_ptr() as *const c_char);
@ -83,6 +85,7 @@ impl X11Lib {
x_default_root_window,
x_grab_keyboard,
x_ungrab_keyboard,
x_pending,
x_next_event,
x_lookup_string,
x_utf8_lookup_string,
@ -212,11 +215,27 @@ impl X11Capture {
}
}
pub fn has_pending_events(&self) -> bool {
if !self.grabbed {
return false;
}
unsafe { (self.lib.x_pending)(self.display) > 0 }
}
pub fn is_grabbed(&self) -> bool {
self.grabbed
}
pub fn next_event(&mut self) -> Option<X11KeyEvent> {
if !self.grabbed {
return None;
}
// Non-blocking: only read if events are pending
if !self.has_pending_events() {
return None;
}
let mut event: XEvent = unsafe { std::mem::zeroed() };
unsafe {
(self.lib.x_next_event)(self.display, &mut event);