1extern crate x11;
2use crate::keycodes::linux::code_from_key;
3use crate::rdev::{EventType, KeyboardState, UnicodeInfo};
4use std::convert::TryInto;
5use std::ffi::{CStr, CString};
6use std::os::raw::{c_char, c_int, c_uint, c_ulong, c_void};
7use std::ptr::{null, null_mut, NonNull};
8use x11::xlib::{self, KeySym, XKeyEvent, XKeysymToString, XSupportsLocale};
9
10#[derive(Debug)]
11pub struct MyXIM(xlib::XIM);
12unsafe impl Sync for MyXIM {}
13unsafe impl Send for MyXIM {}
14
15#[derive(Debug)]
16pub struct MyXIC(xlib::XIC);
17unsafe impl Sync for MyXIC {}
18unsafe impl Send for MyXIC {}
19
20#[derive(Debug)]
21pub struct MyDisplay(*mut xlib::Display);
22unsafe impl Sync for MyDisplay {}
23unsafe impl Send for MyDisplay {}
24
25#[derive(Debug)]
26pub struct Keyboard {
27 pub xim: Box<MyXIM>,
28 pub xic: Box<MyXIC>,
29 pub display: Box<MyDisplay>,
30 window: Box<xlib::Window>,
31 keysym: Box<c_ulong>,
32 status: Box<i32>,
33 serial: c_ulong,
34}
35
36impl Drop for Keyboard {
37 fn drop(&mut self) {
38 unsafe {
39 let MyDisplay(display) = *self.display;
40 xlib::XCloseDisplay(display);
41 }
42 }
43}
44
45impl Keyboard {
46 pub fn new() -> Option<Keyboard> {
47 unsafe {
48 let dpy = xlib::XOpenDisplay(null());
49 if dpy.is_null() {
50 return None;
51 }
52 let string = CString::new("").ok()?;
55 libc::setlocale(libc::LC_ALL, string.as_ptr());
56 if XSupportsLocale() == 0 {
58 let string = CString::new("C.UTF-8").ok()?;
59 libc::setlocale(libc::LC_ALL, string.as_ptr());
60 }
61 if XSupportsLocale() == 0 {
62 let string = CString::new("C").ok()?;
63 libc::setlocale(libc::LC_ALL, string.as_ptr());
64 }
65 let string = CString::new("@im=none").ok()?;
66 let ret = xlib::XSetLocaleModifiers(string.as_ptr());
67 NonNull::new(ret)?;
68
69 let xim = xlib::XOpenIM(dpy, null_mut(), null_mut(), null_mut());
70 NonNull::new(xim)?;
71
72 let mut win_attr = xlib::XSetWindowAttributes {
73 background_pixel: 0,
74 background_pixmap: 0,
75 border_pixel: 0,
76 border_pixmap: 0,
77 bit_gravity: 0,
78 win_gravity: 0,
79 backing_store: 0,
80 backing_planes: 0,
81 backing_pixel: 0,
82 event_mask: 0,
83 save_under: 0,
84 do_not_propagate_mask: 0,
85 override_redirect: 0,
86 colormap: 0,
87 cursor: 0,
88 };
89
90 let window = xlib::XCreateWindow(
91 dpy,
92 xlib::XDefaultRootWindow(dpy),
93 0,
94 0,
95 1,
96 1,
97 0,
98 xlib::CopyFromParent,
99 xlib::InputOnly as c_uint,
100 null_mut(),
101 xlib::CWOverrideRedirect,
102 &mut win_attr,
103 );
104
105 let input_style = CString::new(xlib::XNInputStyle).ok()?;
106 let window_client = CString::new(xlib::XNClientWindow).ok()?;
107 let style = xlib::XIMPreeditNothing | xlib::XIMStatusNothing;
108
109 let xic = xlib::XCreateIC(
110 xim,
111 input_style.as_ptr(),
112 style,
113 window_client.as_ptr(),
114 window,
115 null::<c_void>(),
116 );
117 NonNull::new(xic)?;
118
119 xlib::XSetICFocus(xic);
120
121 Some(Keyboard {
122 xim: Box::new(MyXIM(xim)),
123 xic: Box::new(MyXIC(xic)),
124 display: Box::new(MyDisplay(dpy)),
125 window: Box::new(window),
126 keysym: Box::new(0),
127 status: Box::new(0),
128 serial: 0,
129 })
130 }
131 }
132
133 pub(crate) unsafe fn get_current_modifiers(&mut self) -> Option<u32> {
134 let MyDisplay(display) = *self.display;
135 let screen_number = xlib::XDefaultScreen(display);
136 let screen = xlib::XScreenOfDisplay(display, screen_number);
137 let window = xlib::XRootWindowOfScreen(screen);
138 let mut root_return: xlib::Window = 0;
141 let mut child_return: xlib::Window = 0;
142 let mut root_x_return = 0;
143 let mut root_y_return = 0;
144 let mut win_x_return = 0;
145 let mut win_y_return = 0;
146 let mut mask_return = 0;
147 xlib::XQueryPointer(
148 display,
149 window,
150 &mut root_return,
151 &mut child_return,
152 &mut root_x_return,
153 &mut root_y_return,
154 &mut win_x_return,
155 &mut win_y_return,
156 &mut mask_return,
157 );
158 Some(mask_return)
159 }
160
161 pub(crate) unsafe fn unicode_from_code(
162 &mut self,
163 keycode: c_uint,
164 state: c_uint,
165 ) -> Option<UnicodeInfo> {
166 let MyDisplay(display) = *self.display;
167 let MyXIC(xic) = *self.xic;
168 if display.is_null() || xic.is_null() {
169 println!("We don't seem to have a display or a xic");
170 return None;
171 }
172 const BUF_LEN: usize = 4;
173 let mut buf = [0_u8; BUF_LEN];
174 let MyDisplay(display) = *self.display;
175 let mut key = xlib::XKeyEvent {
176 display,
177 root: 0,
178 window: *self.window,
179 subwindow: 0,
180 x: 0,
181 y: 0,
182 x_root: 0,
183 y_root: 0,
184 state,
185 keycode,
186 same_screen: 0,
187 send_event: 0,
188 serial: self.serial,
189 type_: xlib::KeyPress,
190 time: xlib::CurrentTime,
191 };
192 self.serial += 1;
193
194 let mut event = xlib::XEvent { key };
195
196 xlib::XFilterEvent(&mut event, 0);
202
203 let MyXIC(xic) = *self.xic;
204 let ret = xlib::Xutf8LookupString(
205 xic,
206 &mut event.key,
207 buf.as_mut_ptr() as *mut c_char,
208 BUF_LEN as c_int,
209 &mut *self.keysym,
210 &mut *self.status,
211 );
212
213 let keysym = xlookup_string(&mut key);
214 self.keysym = Box::new(keysym);
215 if self.is_dead() {
216 return Some(UnicodeInfo {
217 name: None,
218 unicode: Vec::new(),
219 is_dead: true,
220 });
221 }
222 if ret == xlib::NoSymbol {
223 return None;
224 }
225
226 let len = buf.iter().position(|ch| ch == &0).unwrap_or(BUF_LEN);
227
228 if len == 1 {
230 match String::from_utf8(buf[..len].to_vec()) {
231 Ok(s) => {
232 if let Some(c) = s.chars().next() {
233 if ('\u{1}'..='\u{1f}').contains(&c) {
234 return None;
235 }
236 }
237 }
238 Err(_) => {}
239 }
240 }
241
242 Some(UnicodeInfo {
243 name: String::from_utf8(buf[..len].to_vec()).ok(),
244 unicode: Vec::new(),
245 is_dead: false,
246 })
247 }
248
249 pub fn is_dead(&mut self) -> bool {
250 let ptr = unsafe { XKeysymToString(*self.keysym) };
251 if ptr.is_null() {
252 false
253 } else {
254 let res = unsafe { CStr::from_ptr(ptr).to_str() };
255 res.unwrap_or_default().to_owned().starts_with("dead")
256 }
257 }
258
259 pub fn keysym(&self) -> u32 {
260 (*self.keysym).try_into().unwrap_or_default()
261 }
262}
263
264impl KeyboardState for Keyboard {
265 fn add(&mut self, event_type: &EventType) -> Option<UnicodeInfo> {
266 match event_type {
267 EventType::KeyPress(key) => {
268 let keycode = code_from_key(*key)?;
269 let state = unsafe { self.get_current_modifiers().unwrap_or_default() };
271 let state = state & 0xFFFB;
273 unsafe { self.unicode_from_code(keycode, state) }
274 }
275 EventType::KeyRelease(_key) => None,
276 _ => None,
277 }
278 }
279}
280
281pub fn xlookup_string(event: &mut XKeyEvent) -> KeySym {
285 let mut buf = [0u8; 64];
286
287 let mut ksym: KeySym = 0;
288 let _len = unsafe {
289 xlib::XLookupString(
290 event,
291 buf.as_mut_ptr() as *mut _,
292 buf.len() as _,
293 &mut ksym,
294 null_mut(),
295 )
296 };
297 ksym
298}
299
300#[cfg(test)]
301mod tests {
302 use super::*;
303
304 #[test]
305 #[ignore]
306 fn test_thread_safety() {
311 let mut keyboard = Keyboard::new().unwrap();
312 let char_s = keyboard
313 .add(&EventType::KeyPress(crate::rdev::Key::KeyS))
314 .unwrap()
315 .name
316 .unwrap();
317 assert_eq!(
318 char_s,
319 "s".to_string(),
320 "This test should pass only on Qwerty layout !"
321 );
322 }
323
324 #[test]
325 #[ignore]
326 fn test_thread_safety_2() {
327 let mut keyboard = Keyboard::new().unwrap();
328 let char_s = keyboard
329 .add(&EventType::KeyPress(crate::rdev::Key::KeyS))
330 .unwrap()
331 .name
332 .unwrap();
333 assert_eq!(
334 char_s,
335 "s".to_string(),
336 "This test should pass only on Qwerty layout !"
337 );
338 }
339}