fix: XRecordRange struct layout — client_spec (4 bytes) before device_events

This commit is contained in:
Khoa Vo 2026-06-26 09:59:29 +07:00
parent e35c034157
commit 12ac8b0c24

View file

@ -47,11 +47,13 @@ struct X11Lib {
} }
// XRecordRange - must match C layout exactly // XRecordRange - must match C layout exactly
// C layout: { int client_spec; struct { u8 first; u8 last; } device_events; ... }
#[repr(C)] #[repr(C)]
struct XRecordRange { struct XRecordRange {
client_spec: c_int,
device_events_first: u8, device_events_first: u8,
device_events_last: u8, device_events_last: u8,
_pad: [u8; 254], _pad: [u8; 250],
} }
type XRecordCallback = unsafe extern "C" fn(*mut c_void, *mut XRecordInterceptData); type XRecordCallback = unsafe extern "C" fn(*mut c_void, *mut XRecordInterceptData);
@ -243,13 +245,17 @@ struct EventQueue {
static mut EVENT_QUEUE: Option<Arc<Mutex<EventQueue>>> = None; static mut EVENT_QUEUE: Option<Arc<Mutex<EventQueue>>> = None;
unsafe extern "C" fn record_callback(_closure: *mut c_void, data: *mut XRecordInterceptData) { unsafe extern "C" fn record_callback(_closure: *mut c_void, data: *mut XRecordInterceptData) {
if (*data).id == 0 { if data.is_null() {
return; // This is our own XRecord data from the init, skip return;
} }
if (*data).data_len < 2 { let data_len = (*data).data_len;
if data_len < 2 {
return; return;
} }
let data_bytes = (*data).data; let data_bytes = (*data).data;
if data_bytes.is_null() {
return;
}
let event_type: c_int = *data_bytes as c_int; let event_type: c_int = *data_bytes as c_int;
let keycode: u8 = *data_bytes.add(1); let keycode: u8 = *data_bytes.add(1);
@ -257,10 +263,8 @@ unsafe extern "C" fn record_callback(_closure: *mut c_void, data: *mut XRecordIn
return; return;
} }
// For XRecord events, we get the raw keycode and event type. // XRecord data layout for keyboard events: type(1) + keycode(1) + state(2)
// We need to construct a fake XKeyEvent to pass to XLookupString for the character. let state: c_int = if data_len >= 4 {
// The state (modifier bits) is at offset 28-31 in the XRecord data for keyboard events.
let state: c_int = if (*data).data_len >= 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
@ -331,16 +335,17 @@ impl X11Capture {
// Set range: KeyPress (2) through KeyRelease (3) // Set range: KeyPress (2) through KeyRelease (3)
(*range).device_events_first = KEY_PRESS as u8; (*range).device_events_first = KEY_PRESS as u8;
(*range).device_events_last = KEY_RELEASE as u8; (*range).device_events_last = KEY_RELEASE as u8;
eprintln!("[vietc] X11Capture: range set (KeyPress={}, KeyRelease={})", KEY_PRESS, KEY_RELEASE);
// Create XRecord context // Create XRecord context
let mut spec: c_int = 1; // XRecordAllClients = 1 let mut spec: c_int = 1; // XRecordAllClients = 1
let range_ptr = range as *mut *mut XRecordRange; let mut range_ptr: *mut XRecordRange = range;
let ctx = (lib.x_record_create_context)( let ctx = (lib.x_record_create_context)(
display, display,
0, // own_client 0, // own_client
&mut spec, // clients &mut spec, // clients
1, // nclients 1, // nclients
range_ptr, // ranges &mut range_ptr, // ranges (pointer to array of range pointers)
1, // nranges 1, // nranges
); );
(lib.x_free)(range as *mut c_void); (lib.x_free)(range as *mut c_void);
@ -350,6 +355,7 @@ impl X11Capture {
(lib.x_close_display)(display); (lib.x_close_display)(display);
return None; return None;
} }
eprintln!("[vietc] X11Capture: XRecord context created (ctx={})", ctx);
// Initialize event queue // Initialize event queue
EVENT_QUEUE = Some(Arc::new(Mutex::new(EventQueue { EVENT_QUEUE = Some(Arc::new(Mutex::new(EventQueue {