winit/platform_impl/linux/x11/util/
input.rs

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