fix: remove XGrabKeyboard from XRecord path — it blocks event delivery
XGrabKeyboard on the same display as XRecord breaks event delivery. XRecord captures events globally without any grab needed. Also: use XPending() before select() to check Xlib internal buffer, and add XFlush before XRecordProcessReplies after select().
This commit is contained in:
parent
7898768141
commit
d1a5f36606
2 changed files with 43 additions and 20 deletions
|
|
@ -577,8 +577,9 @@ fn main() -> Result<(), Box<dyn std::error::Error>> {
|
||||||
#[cfg(feature = "x11")]
|
#[cfg(feature = "x11")]
|
||||||
if display != display::DisplayServer::Wayland {
|
if display != display::DisplayServer::Wayland {
|
||||||
if let Some(mut capture) = X11Capture::new() {
|
if let Some(mut capture) = X11Capture::new() {
|
||||||
if capture.grab_keyboard() {
|
// XRecord captures events globally — no grab needed for capture.
|
||||||
log_info("[vietc] X11 keyboard grabbed — using X11 capture/injection");
|
// XGrabKeyboard on the same display as XRecord breaks event delivery.
|
||||||
|
log_info("[vietc] X11 XRecord capture active — using X11 capture/injection");
|
||||||
return run_with_x11(
|
return run_with_x11(
|
||||||
capture,
|
capture,
|
||||||
&mut daemon,
|
&mut daemon,
|
||||||
|
|
@ -587,9 +588,6 @@ fn main() -> Result<(), Box<dyn std::error::Error>> {
|
||||||
status_changed,
|
status_changed,
|
||||||
engine_enabled,
|
engine_enabled,
|
||||||
);
|
);
|
||||||
} else {
|
|
||||||
log_info("[vietc] X11 grab failed, falling back to evdev");
|
|
||||||
}
|
|
||||||
} else {
|
} else {
|
||||||
log_info("[vietc] X11 not available, falling back to evdev");
|
log_info("[vietc] X11 not available, falling back to evdev");
|
||||||
}
|
}
|
||||||
|
|
@ -710,6 +708,8 @@ fn run_with_x11(
|
||||||
// press+release immediately, breaking held-key combos (Ctrl+C, Alt+Tab…).
|
// press+release immediately, breaking held-key combos (Ctrl+C, Alt+Tab…).
|
||||||
let mut pressed_keys: HashSet<u32> = HashSet::new();
|
let mut pressed_keys: HashSet<u32> = HashSet::new();
|
||||||
|
|
||||||
|
eprintln!("[vietc] X11 event loop starting");
|
||||||
|
|
||||||
loop {
|
loop {
|
||||||
if status_changed.load(Ordering::SeqCst) {
|
if status_changed.load(Ordering::SeqCst) {
|
||||||
daemon.sync_status_file();
|
daemon.sync_status_file();
|
||||||
|
|
@ -747,8 +747,13 @@ fn run_with_x11(
|
||||||
let got_data = capture.wait_for_event(100);
|
let got_data = capture.wait_for_event(100);
|
||||||
let evt = capture.next_event();
|
let evt = capture.next_event();
|
||||||
if evt.is_none() {
|
if evt.is_none() {
|
||||||
|
static mut LOOP_COUNT: u64 = 0;
|
||||||
|
unsafe { LOOP_COUNT += 1; }
|
||||||
if got_data {
|
if got_data {
|
||||||
eprintln!("[vietc] DEBUG: select said data but no event in queue");
|
eprintln!("[vietc] DEBUG: select said data but no event in queue (loop={})", unsafe { LOOP_COUNT });
|
||||||
|
}
|
||||||
|
if unsafe { LOOP_COUNT } <= 3 || unsafe { LOOP_COUNT } % 50 == 0 {
|
||||||
|
eprintln!("[vietc] DEBUG: no event, grabbed={}, got_data={}", capture.is_grabbed(), got_data);
|
||||||
}
|
}
|
||||||
if !capture.is_grabbed() {
|
if !capture.is_grabbed() {
|
||||||
eprintln!("[vietc] Keyboard grab lost — re-grabbing");
|
eprintln!("[vietc] Keyboard grab lost — re-grabbing");
|
||||||
|
|
|
||||||
|
|
@ -35,6 +35,7 @@ struct X11Lib {
|
||||||
x_utf8_lookup_string: Option<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>,
|
||||||
x_flush: unsafe extern "C" fn(*mut Display) -> c_int,
|
x_flush: unsafe extern "C" fn(*mut Display) -> c_int,
|
||||||
x_connection_number: unsafe extern "C" fn(*mut Display) -> c_int,
|
x_connection_number: unsafe extern "C" fn(*mut Display) -> c_int,
|
||||||
|
x_pending: unsafe extern "C" fn(*mut Display) -> c_int,
|
||||||
// XRecord
|
// XRecord
|
||||||
x_record_query_version: unsafe extern "C" fn(*mut Display, *mut c_int, *mut c_int) -> i32,
|
x_record_query_version: unsafe extern "C" fn(*mut Display, *mut c_int, *mut c_int) -> i32,
|
||||||
x_record_alloc_range: unsafe extern "C" fn() -> *mut XRecordRange,
|
x_record_alloc_range: unsafe extern "C" fn() -> *mut XRecordRange,
|
||||||
|
|
@ -153,6 +154,7 @@ impl X11Lib {
|
||||||
};
|
};
|
||||||
let x_flush = sym!("XFlush");
|
let x_flush = sym!("XFlush");
|
||||||
let x_connection_number = sym!("XConnectionNumber");
|
let x_connection_number = sym!("XConnectionNumber");
|
||||||
|
let x_pending = sym!("XPending");
|
||||||
|
|
||||||
if xtst_handle.is_null() {
|
if xtst_handle.is_null() {
|
||||||
return Err("Failed to load libXtst.so.6 — install libxtst6".into());
|
return Err("Failed to load libXtst.so.6 — install libxtst6".into());
|
||||||
|
|
@ -184,6 +186,7 @@ impl X11Lib {
|
||||||
x_utf8_lookup_string,
|
x_utf8_lookup_string,
|
||||||
x_flush,
|
x_flush,
|
||||||
x_connection_number,
|
x_connection_number,
|
||||||
|
x_pending,
|
||||||
x_record_query_version,
|
x_record_query_version,
|
||||||
x_record_alloc_range,
|
x_record_alloc_range,
|
||||||
x_record_create_context,
|
x_record_create_context,
|
||||||
|
|
@ -416,11 +419,21 @@ impl X11Capture {
|
||||||
self.grabbed
|
self.grabbed
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Wait for XRecord data to arrive on the X11 connection fd, with timeout.
|
/// Wait for XRecord data to arrive, with timeout.
|
||||||
|
/// Uses XPending() first (checks Xlib internal buffer), then select() on fd.
|
||||||
pub fn wait_for_event(&mut self, timeout_ms: u64) -> bool {
|
pub fn wait_for_event(&mut self, timeout_ms: u64) -> bool {
|
||||||
unsafe {
|
unsafe {
|
||||||
(self.lib.x_flush)(self.display);
|
(self.lib.x_flush)(self.display);
|
||||||
|
|
||||||
|
// First check: XPending reads from Xlib's internal buffer.
|
||||||
|
// XRecord data may already be buffered there by a previous read.
|
||||||
|
let pending = (self.lib.x_pending)(self.display);
|
||||||
|
if pending > 0 {
|
||||||
|
(self.lib.x_record_process_replies)(self.display);
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Second check: select() on the X11 socket fd
|
||||||
let fd = (self.lib.x_connection_number)(self.display);
|
let fd = (self.lib.x_connection_number)(self.display);
|
||||||
let mut readfds: FdSet = std::mem::zeroed();
|
let mut readfds: FdSet = std::mem::zeroed();
|
||||||
fd_zero(&mut readfds);
|
fd_zero(&mut readfds);
|
||||||
|
|
@ -431,7 +444,8 @@ impl X11Capture {
|
||||||
};
|
};
|
||||||
let n = select(fd + 1, &mut readfds, std::ptr::null_mut(), std::ptr::null_mut(), &mut timeout);
|
let n = select(fd + 1, &mut readfds, std::ptr::null_mut(), std::ptr::null_mut(), &mut timeout);
|
||||||
if n > 0 && fd_isset(fd, &readfds) {
|
if n > 0 && fd_isset(fd, &readfds) {
|
||||||
// Process XRecord replies — this fires the callback
|
// Flush to move data from socket into Xlib buffer
|
||||||
|
(self.lib.x_flush)(self.display);
|
||||||
(self.lib.x_record_process_replies)(self.display);
|
(self.lib.x_record_process_replies)(self.display);
|
||||||
true
|
true
|
||||||
} else {
|
} else {
|
||||||
|
|
@ -470,10 +484,14 @@ impl X11Capture {
|
||||||
where
|
where
|
||||||
F: FnOnce() -> T,
|
F: FnOnce() -> T,
|
||||||
{
|
{
|
||||||
|
if self.grabbed {
|
||||||
self.ungrab_keyboard();
|
self.ungrab_keyboard();
|
||||||
let result = f();
|
let result = f();
|
||||||
self.grab_keyboard();
|
self.grab_keyboard();
|
||||||
result
|
result
|
||||||
|
} else {
|
||||||
|
f()
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn lookup_keycode(&self, keycode: u32, state: c_int) -> Option<char> {
|
pub fn lookup_keycode(&self, keycode: u32, state: c_int) -> Option<char> {
|
||||||
|
|
|
||||||
Loading…
Reference in a new issue