| 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 | |