fix: XRecordInterceptData layout and data_len check

- Fixed struct to match C: id_base(u64), server_time(u64), client_seq(u64),
  category(i32), client_swapped(i32), data(ptr), data_len(u64)
- data_len is in 4-byte units, not bytes — keyboard events have data_len=1
- Added XRECORD_FROM_SERVER category check
- Removed re-grab logic from event loop
This commit is contained in:
Khoa Vo 2026-06-26 10:16:58 +07:00
parent d1a5f36606
commit 44d1b0a1d2
2 changed files with 19 additions and 18 deletions

View file

@ -743,21 +743,12 @@ fn run_with_x11(
capture.focus_lost = false; capture.focus_lost = false;
} }
// Wait for events with 100ms timeout, then re-grab if needed // Wait for events with 100ms timeout
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 (loop={})", unsafe { LOOP_COUNT }); eprintln!("[vietc] DEBUG: select said data but no event in queue");
}
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() {
eprintln!("[vietc] Keyboard grab lost — re-grabbing");
capture.grab_keyboard();
} }
continue; continue;
} }

View file

@ -18,6 +18,9 @@ const MOD4_MASK: c_int = 64;
// Grab modes // Grab modes
const GRAB_MODE_ASYNC: c_int = 1; const GRAB_MODE_ASYNC: c_int = 1;
// XRecord categories
const XRECORD_FROM_SERVER: c_int = 1;
extern "C" { extern "C" {
fn dlopen(filename: *const c_char, flag: c_int) -> *mut c_void; fn dlopen(filename: *const c_char, flag: c_int) -> *mut c_void;
fn dlsym(handle: *mut c_void, symbol: *const c_char) -> *mut c_void; fn dlsym(handle: *mut c_void, symbol: *const c_char) -> *mut c_void;
@ -56,14 +59,18 @@ struct XRecordRange {
type XRecordCallback = unsafe extern "C" fn(*mut c_void, *mut XRecordInterceptData); type XRecordCallback = unsafe extern "C" fn(*mut c_void, *mut XRecordInterceptData);
// XRecordInterceptData: matches C layout exactly
// C: { XID id_base; Time server_time; unsigned long client_seq;
// int category; Bool client_swapped; unsigned char *data; unsigned long data_len; }
#[repr(C)] #[repr(C)]
struct XRecordInterceptData { struct XRecordInterceptData {
id: u64, id_base: u64, // XID
server_time: u64, server_time: u64, // Time
client_seq: u64, // unsigned long
category: c_int,
client_swapped: c_int, client_swapped: c_int,
_pad: c_int,
data_len: c_int,
data: *mut u8, data: *mut u8,
data_len: u64, // unsigned long
} }
#[repr(C)] #[repr(C)]
@ -248,8 +255,11 @@ unsafe extern "C" fn record_callback(_closure: *mut c_void, data: *mut XRecordIn
if data.is_null() { if data.is_null() {
return; return;
} }
let data_len = (*data).data_len; if (*data).category != XRECORD_FROM_SERVER {
if data_len < 2 { return;
}
let data_len_bytes = (*data).data_len * 4; // data_len is in 4-byte units
if data_len_bytes < 2 {
return; return;
} }
let data_bytes = (*data).data; let data_bytes = (*data).data;
@ -264,7 +274,7 @@ unsafe extern "C" fn record_callback(_closure: *mut c_void, data: *mut XRecordIn
} }
// XRecord data layout for keyboard events: type(1) + keycode(1) + state(2) // XRecord data layout for keyboard events: type(1) + keycode(1) + state(2)
let state: c_int = if data_len >= 4 { let state: c_int = if data_len_bytes >= 4 {
*(data_bytes.add(2) as *const u16) as c_int *(data_bytes.add(2) as *const u16) as c_int
} else { } else {
0 0