1 | use std::{slice, str}; |
2 | use x11rb::protocol::xinput::{self, ConnectionExt as _}; |
3 | use x11rb::protocol::xkb; |
4 | |
5 | use super::*; |
6 | |
7 | pub const VIRTUAL_CORE_POINTER: u16 = 2; |
8 | pub const VIRTUAL_CORE_KEYBOARD: u16 = 3; |
9 | |
10 | // A base buffer size of 1kB uses a negligible amount of RAM while preventing us from having to |
11 | // re-allocate (and make another round-trip) in the *vast* majority of cases. |
12 | // To test if `lookup_utf8` works correctly, set this to 1. |
13 | const TEXT_BUFFER_SIZE: usize = 1024; |
14 | |
15 | impl XConnection { |
16 | pub fn select_xinput_events( |
17 | &self, |
18 | window: xproto::Window, |
19 | device_id: u16, |
20 | mask: xinput::XIEventMask, |
21 | ) -> Result<VoidCookie<'_>, X11Error> { |
22 | self.xcb_connection() |
23 | .xinput_xi_select_events(window, &[xinput::EventMask { |
24 | deviceid: device_id, |
25 | mask: vec![mask], |
26 | }]) |
27 | .map_err(Into::into) |
28 | } |
29 | |
30 | pub fn select_xkb_events( |
31 | &self, |
32 | device_id: xkb::DeviceSpec, |
33 | mask: xkb::EventType, |
34 | ) -> Result<bool, X11Error> { |
35 | let mask = u16::from(mask) as _; |
36 | let status = |
37 | unsafe { (self.xlib.XkbSelectEvents)(self.display, device_id as _, mask, mask) }; |
38 | |
39 | if status == ffi::True { |
40 | self.flush_requests()?; |
41 | Ok(true) |
42 | } else { |
43 | tracing::error!("Could not select XKB events: The XKB extension is not initialized!" ); |
44 | Ok(false) |
45 | } |
46 | } |
47 | |
48 | pub fn query_pointer( |
49 | &self, |
50 | window: xproto::Window, |
51 | device_id: u16, |
52 | ) -> Result<xinput::XIQueryPointerReply, X11Error> { |
53 | self.xcb_connection() |
54 | .xinput_xi_query_pointer(window, device_id)? |
55 | .reply() |
56 | .map_err(Into::into) |
57 | } |
58 | |
59 | fn lookup_utf8_inner( |
60 | &self, |
61 | ic: ffi::XIC, |
62 | key_event: &mut ffi::XKeyEvent, |
63 | buffer: *mut u8, |
64 | size: usize, |
65 | ) -> (ffi::KeySym, ffi::Status, c_int) { |
66 | let mut keysym: ffi::KeySym = 0; |
67 | let mut status: ffi::Status = 0; |
68 | let count = unsafe { |
69 | (self.xlib.Xutf8LookupString)( |
70 | ic, |
71 | key_event, |
72 | buffer as *mut c_char, |
73 | size as c_int, |
74 | &mut keysym, |
75 | &mut status, |
76 | ) |
77 | }; |
78 | (keysym, status, count) |
79 | } |
80 | |
81 | pub fn lookup_utf8(&self, ic: ffi::XIC, key_event: &mut ffi::XKeyEvent) -> String { |
82 | // `assume_init` is safe here because the array consists of `MaybeUninit` values, |
83 | // which do not require initialization. |
84 | let mut buffer: [MaybeUninit<u8>; TEXT_BUFFER_SIZE] = |
85 | unsafe { MaybeUninit::uninit().assume_init() }; |
86 | // If the buffer overflows, we'll make a new one on the heap. |
87 | let mut vec; |
88 | |
89 | let (_, status, count) = |
90 | self.lookup_utf8_inner(ic, key_event, buffer.as_mut_ptr() as *mut u8, buffer.len()); |
91 | |
92 | let bytes = if status == ffi::XBufferOverflow { |
93 | vec = Vec::with_capacity(count as usize); |
94 | let (_, _, new_count) = |
95 | self.lookup_utf8_inner(ic, key_event, vec.as_mut_ptr(), vec.capacity()); |
96 | debug_assert_eq!(count, new_count); |
97 | |
98 | unsafe { vec.set_len(count as usize) }; |
99 | &vec[..count as usize] |
100 | } else { |
101 | unsafe { slice::from_raw_parts(buffer.as_ptr() as *const u8, count as usize) } |
102 | }; |
103 | |
104 | str::from_utf8(bytes).unwrap_or("" ).to_string() |
105 | } |
106 | } |
107 | |