1use std::{slice, str};
2use x11rb::protocol::{
3 xinput::{self, ConnectionExt as _},
4 xkb,
5};
6
7use super::*;
8
9pub const VIRTUAL_CORE_POINTER: u16 = 2;
10pub 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.
15const TEXT_BUFFER_SIZE: usize = 1024;
16
17impl 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