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

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