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