1#![cfg_attr(not(feature = "xlib"), allow(dead_code))]
11#![cfg_attr(not(feature = "xlib"), allow(unused_imports))]
12
13extern crate libc;
14extern crate x11;
15
16use std::ffi::CString;
17use std::mem::{transmute, zeroed};
18use std::os::raw::*;
19use std::ptr::{null, null_mut};
20use std::slice::from_raw_parts;
21use x11::{xinput2, xlib};
22
23pub struct DemoWindow {
26 pub display: *mut xlib::Display,
27 pub window: xlib::Window,
28
29 wm_protocols: xlib::Atom,
30 wm_delete_window: xlib::Atom,
31}
32
33impl DemoWindow {
34 pub fn new(title: &str, width: u32, height: u32) -> DemoWindow {
36 unsafe {
37 let display = xlib::XOpenDisplay(null());
39 if display == null_mut() {
40 panic!("can't open display");
41 }
42
43 let wm_delete_window_str = CString::new("WM_DELETE_WINDOW").unwrap();
45 let wm_protocols_str = CString::new("WM_PROTOCOLS").unwrap();
46
47 let wm_delete_window =
48 xlib::XInternAtom(display, wm_delete_window_str.as_ptr(), xlib::False);
49 let wm_protocols = xlib::XInternAtom(display, wm_protocols_str.as_ptr(), xlib::False);
50
51 if wm_delete_window == 0 || wm_protocols == 0 {
52 panic!("can't load atoms");
53 }
54
55 let screen_num = xlib::XDefaultScreen(display);
57 let root = xlib::XRootWindow(display, screen_num);
58 let white_pixel = xlib::XWhitePixel(display, screen_num);
59
60 let mut attributes: xlib::XSetWindowAttributes = zeroed();
61 attributes.background_pixel = white_pixel;
62
63 let window = xlib::XCreateWindow(
64 display,
65 root,
66 0,
67 0,
68 width as c_uint,
69 height as c_uint,
70 0,
71 0,
72 xlib::InputOutput as c_uint,
73 null_mut(),
74 xlib::CWBackPixel,
75 &mut attributes,
76 );
77 let title_str = CString::new(title).unwrap();
79 xlib::XStoreName(display, window, title_str.as_ptr() as *mut _);
80
81 let mut protocols = [wm_delete_window];
83
84 if xlib::XSetWMProtocols(display, window, &mut protocols[0] as *mut xlib::Atom, 1)
85 == xlib::False
86 {
87 panic!("can't set WM protocols");
88 }
89
90 DemoWindow {
91 display: display,
92 window: window,
93 wm_protocols: wm_protocols,
94 wm_delete_window: wm_delete_window,
95 }
96 }
97 }
98
99 pub fn show(&mut self) {
101 unsafe {
102 xlib::XMapWindow(self.display, self.window);
103 }
104 }
105
106 pub fn run_event_loop<EventHandler>(&mut self, mut event_handler: EventHandler)
109 where
110 EventHandler: FnMut(&xlib::XEvent),
111 {
112 let mut event: xlib::XEvent = unsafe { zeroed() };
113 loop {
114 unsafe { xlib::XNextEvent(self.display, &mut event) };
115 match event.get_type() {
116 xlib::ClientMessage => {
117 let xclient: xlib::XClientMessageEvent = From::from(event);
118
119 if xclient.message_type == self.wm_protocols && xclient.format == 32 {
121 let protocol = xclient.data.get_long(0) as xlib::Atom;
122
123 if protocol == self.wm_delete_window {
125 break;
126 }
127 }
128 }
129 _ => event_handler(&event),
130 }
131 }
132 }
133}
134
135impl Drop for DemoWindow {
136 fn drop(&mut self) {
138 unsafe {
139 xlib::XDestroyWindow(self.display, self.window);
140 xlib::XCloseDisplay(self.display);
141 }
142 }
143}
144
145const TITLE: &'static str = "XInput Demo";
146const DEFAULT_WIDTH: c_uint = 640;
147const DEFAULT_HEIGHT: c_uint = 480;
148
149#[derive(Debug)]
150enum AxisType {
151 HorizontalScroll,
152 VerticalScroll,
153 Other,
154}
155
156#[derive(Debug)]
157struct Axis {
158 id: i32,
159 device_id: i32,
160 axis_number: i32,
161 axis_type: AxisType,
162}
163
164#[derive(Debug)]
165struct AxisValue {
166 device_id: i32,
167 axis_number: i32,
168 value: f64,
169}
170
171struct InputState {
172 cursor_pos: (f64, f64),
173 axis_values: Vec<AxisValue>,
174}
175
176fn read_input_axis_info(display: *mut xlib::Display) -> Vec<Axis> {
177 let mut axis_list = Vec::new();
178 let mut device_count = 0;
179
180 let devices =
183 unsafe { xinput2::XIQueryDevice(display, xinput2::XIAllMasterDevices, &mut device_count) };
184 for i in 0..device_count {
185 let device = unsafe { *(devices.offset(i as isize)) };
186 for k in 0..device.num_classes {
187 let class = unsafe { *(device.classes.offset(k as isize)) };
188 match unsafe { (*class)._type } {
189 xinput2::XIScrollClass => {
190 let scroll_class: &xinput2::XIScrollClassInfo = unsafe { transmute(class) };
191 axis_list.push(Axis {
192 id: scroll_class.sourceid,
193 device_id: device.deviceid,
194 axis_number: scroll_class.number,
195 axis_type: match scroll_class.scroll_type {
196 xinput2::XIScrollTypeHorizontal => AxisType::HorizontalScroll,
197 xinput2::XIScrollTypeVertical => AxisType::VerticalScroll,
198 _ => {
199 unreachable!()
200 }
201 },
202 })
203 }
204 xinput2::XIValuatorClass => {
205 let valuator_class: &xinput2::XIValuatorClassInfo = unsafe { transmute(class) };
206 axis_list.push(Axis {
207 id: valuator_class.sourceid,
208 device_id: device.deviceid,
209 axis_number: valuator_class.number,
210 axis_type: AxisType::Other,
211 })
212 }
213 _ => { }
214 }
215 }
216 }
217
218 axis_list.sort_by(|a, b| {
219 if a.device_id != b.device_id {
220 a.device_id.cmp(&b.device_id)
221 } else if a.id != b.id {
222 a.id.cmp(&b.id)
223 } else {
224 a.axis_number.cmp(&b.axis_number)
225 }
226 });
227 axis_list
228}
229
230fn calc_scroll_deltas(
234 event: &xinput2::XIDeviceEvent,
235 axis_id: i32,
236 axis_value: f64,
237 axis_list: &[Axis],
238 prev_axis_values: &mut Vec<AxisValue>,
239) -> (f64, f64) {
240 let prev_value_pos = prev_axis_values.iter().position(|prev_axis| {
241 prev_axis.device_id == event.sourceid && prev_axis.axis_number == axis_id
242 });
243 let delta = match prev_value_pos {
244 Some(idx) => axis_value - prev_axis_values[idx].value,
245 None => 0.0,
246 };
247
248 let new_axis_value = AxisValue {
249 device_id: event.sourceid,
250 axis_number: axis_id,
251 value: axis_value,
252 };
253
254 match prev_value_pos {
255 Some(idx) => prev_axis_values[idx] = new_axis_value,
256 None => prev_axis_values.push(new_axis_value),
257 }
258
259 let mut scroll_delta = (0.0, 0.0);
260
261 for axis in axis_list.iter() {
262 if axis.id == event.sourceid && axis.axis_number == axis_id {
263 match axis.axis_type {
264 AxisType::HorizontalScroll => scroll_delta.0 = delta,
265 AxisType::VerticalScroll => scroll_delta.1 = delta,
266 _ => {}
267 }
268 }
269 }
270
271 scroll_delta
272}
273
274#[cfg(not(all(feature = "xlib", feature = "xinput")))]
275fn main() {
276 panic!("this example requires `--features 'xlib xinput'`");
277}
278
279#[cfg(all(feature = "xlib", feature = "xinput"))]
280fn main() {
281 let mut demo_window = DemoWindow::new(TITLE, DEFAULT_WIDTH, DEFAULT_HEIGHT);
282
283 let mut opcode: c_int = 0;
285 let mut event: c_int = 0;
286 let mut error: c_int = 0;
287 let xinput_str = CString::new("XInputExtension").unwrap();
288 let xinput_available = unsafe {
289 xlib::XQueryExtension(
290 demo_window.display,
291 xinput_str.as_ptr(),
292 &mut opcode,
293 &mut event,
294 &mut error,
295 )
296 };
297 if xinput_available == xlib::False {
298 panic!("XInput not available")
299 }
300
301 let mut xinput_major_ver = xinput2::XI_2_Major;
302 let mut xinput_minor_ver = xinput2::XI_2_Minor;
303 if unsafe {
304 xinput2::XIQueryVersion(
305 demo_window.display,
306 &mut xinput_major_ver,
307 &mut xinput_minor_ver,
308 )
309 } != xlib::Success as c_int
310 {
311 panic!("XInput2 not available");
312 }
313 println!(
314 "XI version available {}.{}",
315 xinput_major_ver, xinput_minor_ver
316 );
317
318 let mut mask: [c_uchar; 1] = [0];
320 let mut input_event_mask = xinput2::XIEventMask {
321 deviceid: xinput2::XIAllMasterDevices,
322 mask_len: mask.len() as i32,
323 mask: mask.as_mut_ptr(),
324 };
325 let events = &[
326 xinput2::XI_ButtonPress,
327 xinput2::XI_ButtonRelease,
328 xinput2::XI_KeyPress,
329 xinput2::XI_KeyRelease,
330 xinput2::XI_Motion,
331 ];
332 for &event in events {
333 xinput2::XISetMask(&mut mask, event);
334 }
335
336 match unsafe {
337 xinput2::XISelectEvents(
338 demo_window.display,
339 demo_window.window,
340 &mut input_event_mask,
341 1,
342 )
343 } {
344 status if status as u8 == xlib::Success => (),
345 err => panic!("Failed to select events {:?}", err),
346 }
347
348 demo_window.show();
350
351 let display = demo_window.display;
353 let axis_list = read_input_axis_info(display);
354
355 let mut prev_state = InputState {
356 cursor_pos: (0.0, 0.0),
357 axis_values: Vec::new(),
358 };
359
360 demo_window.run_event_loop(|event| match event.get_type() {
361 xlib::GenericEvent => {
362 let mut cookie: xlib::XGenericEventCookie = From::from(*event);
363 if unsafe { xlib::XGetEventData(display, &mut cookie) } != xlib::True {
364 println!("Failed to retrieve event data");
365 return;
366 }
367 match cookie.evtype {
368 xinput2::XI_KeyPress | xinput2::XI_KeyRelease => {
369 let event_data: &xinput2::XIDeviceEvent = unsafe { transmute(cookie.data) };
370 if cookie.evtype == xinput2::XI_KeyPress {
371 if event_data.flags & xinput2::XIKeyRepeat == 0 {
372 println!("Key {} pressed", event_data.detail);
373 }
374 } else {
375 println!("Key {} released", event_data.detail);
376 }
377 }
378 xinput2::XI_ButtonPress | xinput2::XI_ButtonRelease => {
379 let event_data: &xinput2::XIDeviceEvent = unsafe { transmute(cookie.data) };
380 if cookie.evtype == xinput2::XI_ButtonPress {
381 println!("Button {} pressed", event_data.detail);
382 } else {
383 println!("Button {} released", event_data.detail);
384 }
385 }
386 xinput2::XI_Motion => {
387 let event_data: &xinput2::XIDeviceEvent = unsafe { transmute(cookie.data) };
388 let axis_state = event_data.valuators;
389 let mask =
390 unsafe { from_raw_parts(axis_state.mask, axis_state.mask_len as usize) };
391 let mut axis_count = 0;
392
393 let mut scroll_delta = (0.0, 0.0);
394 for axis_id in 0..axis_state.mask_len {
395 if xinput2::XIMaskIsSet(&mask, axis_id) {
396 let axis_value = unsafe { *axis_state.values.offset(axis_count) };
397 let delta = calc_scroll_deltas(
398 event_data,
399 axis_id,
400 axis_value,
401 &axis_list,
402 &mut prev_state.axis_values,
403 );
404 scroll_delta.0 += delta.0;
405 scroll_delta.1 += delta.1;
406 axis_count += 1;
407 }
408 }
409
410 if scroll_delta.0.abs() > 0.0 || scroll_delta.1.abs() > 0.0 {
411 println!(
412 "Mouse wheel/trackpad scrolled by ({}, {})",
413 scroll_delta.0, scroll_delta.1
414 );
415 }
416
417 let new_cursor_pos = (event_data.event_x, event_data.event_y);
418 if new_cursor_pos != prev_state.cursor_pos {
419 println!(
420 "Mouse moved to ({}, {})",
421 new_cursor_pos.0, new_cursor_pos.1
422 );
423 prev_state.cursor_pos = new_cursor_pos;
424 }
425 }
426 _ => (),
427 }
428 unsafe { xlib::XFreeEventData(display, &mut cookie) };
429 }
430 _ => (),
431 });
432}