From 94c31e42a44b2621ae1ddc02b36ee5f0ee0bc16a Mon Sep 17 00:00:00 2001 From: Robert Knight Date: Mon, 22 Jun 2015 22:49:48 +0100 Subject: Use XInput2 for event handling This provides smooth scrolling for touchpad devices and will enable support for touch events etc. in future. --- src/api/x11/ffi.rs | 2 + src/api/x11/input.rs | 337 ++++++++++++++++++++++++++++++++++++++++++++++++ src/api/x11/mod.rs | 1 + src/api/x11/window.rs | 159 +++++++++++------------ src/api/x11/xdisplay.rs | 3 + 5 files changed, 416 insertions(+), 86 deletions(-) create mode 100644 src/api/x11/input.rs (limited to 'src') diff --git a/src/api/x11/ffi.rs b/src/api/x11/ffi.rs index 8c9a35d..3d4f0ed 100644 --- a/src/api/x11/ffi.rs +++ b/src/api/x11/ffi.rs @@ -2,6 +2,8 @@ pub use x11_dl::keysym::*; pub use x11_dl::xcursor::*; pub use x11_dl::xf86vmode::*; pub use x11_dl::xlib::*; +pub use x11_dl::xinput::*; +pub use x11_dl::xinput2::*; pub use self::glx::types::GLXContext; diff --git a/src/api/x11/input.rs b/src/api/x11/input.rs new file mode 100644 index 0000000..6b6d24d --- /dev/null +++ b/src/api/x11/input.rs @@ -0,0 +1,337 @@ +use std::sync::Arc; + +use libc; +use std::{mem, ptr}; +use std::ffi::CString; +use std::slice::from_raw_parts; + +use events::Event; + +use super::{events, ffi}; +use super::XConnection; + +#[derive(Debug)] +enum AxisType { + HorizontalScroll, + VerticalScroll, + Other +} + +#[derive(Debug)] +struct Axis { + id: i32, + device_id: i32, + axis_number: i32, + axis_type: AxisType +} + +#[derive(Debug)] +struct AxisValue { + device_id: i32, + axis_number: i32, + value: f64 +} + +struct InputState { + cursor_pos: (f64, f64), + axis_values: Vec +} + +pub struct XInputEventHandler { + display: Arc, + window: ffi::Window, + ic: ffi::XIC, + axis_list: Vec, + current_state: InputState +} + +impl XInputEventHandler { + pub fn new(display: &Arc, window: ffi::Window, ic: ffi::XIC) -> XInputEventHandler { + // query XInput support + let mut opcode: libc::c_int = 0; + let mut event: libc::c_int = 0; + let mut error: libc::c_int = 0; + let xinput_str = CString::new("XInputExtension").unwrap(); + + unsafe { + if (display.xlib.XQueryExtension)(display.display, xinput_str.as_ptr(), &mut opcode, &mut event, &mut error) == ffi::False { + panic!("XInput not available") + } + } + + let mut xinput_major_ver = ffi::XI_2_Major; + let mut xinput_minor_ver = ffi::XI_2_Minor; + + unsafe { + if (display.xinput2.XIQueryVersion)(display.display, &mut xinput_major_ver, &mut xinput_minor_ver) != ffi::Success as libc::c_int { + panic!("Unable to determine XInput version"); + } + } + + // specify the XInput events we want to receive + let mut mask: [libc::c_uchar; 1] = [0]; + let mut input_event_mask = ffi::XIEventMask { + deviceid: ffi::XIAllDevices, + mask_len: mask.len() as i32, + mask: mask.as_mut_ptr() + }; + let events = &[ + ffi::XI_ButtonPress, + ffi::XI_ButtonRelease, + ffi::XI_Motion + ]; + for event in events { + ffi::XISetMask(&mut mask, *event); + } + + unsafe { + match (display.xinput2.XISelectEvents)(display.display, window, &mut input_event_mask, 1) { + status if status as u8 == ffi::Success => (), + err => panic!("Failed to select events {:?}", err) + } + } + + XInputEventHandler { + display: display.clone(), + window: window, + ic: ic, + axis_list: read_input_axis_info(display), + current_state: InputState { + cursor_pos: (0.0, 0.0), + axis_values: Vec::new() + } + } + } + + pub fn translate_key_event(&self, event: &mut ffi::XKeyEvent) -> Vec { + use events::Event::{KeyboardInput, ReceivedCharacter}; + use events::ElementState::{Pressed, Released}; + + let mut translated_events = Vec::new(); + + let state; + if event.type_ == ffi::KeyPress { + let raw_ev: *mut ffi::XKeyEvent = event; + unsafe { (self.display.xlib.XFilterEvent)(mem::transmute(raw_ev), self.window) }; + state = Pressed; + } else { + state = Released; + } + + let mut kp_keysym = 0; + + let written = unsafe { + use std::str; + + let mut buffer: [u8; 16] = [mem::uninitialized(); 16]; + let raw_ev: *mut ffi::XKeyEvent = event; + let count = (self.display.xlib.Xutf8LookupString)(self.ic, mem::transmute(raw_ev), + mem::transmute(buffer.as_mut_ptr()), + buffer.len() as libc::c_int, &mut kp_keysym, ptr::null_mut()); + + str::from_utf8(&buffer[..count as usize]).unwrap_or("").to_string() + }; + + { + for chr in written.chars() { + translated_events.push(ReceivedCharacter(chr)); + } + } + + let mut keysym = unsafe { + (self.display.xlib.XKeycodeToKeysym)(self.display.display, event.keycode as ffi::KeyCode, 0) + }; + + if (ffi::XK_KP_Space as libc::c_ulong <= keysym) && (keysym <= ffi::XK_KP_9 as libc::c_ulong) { + keysym = kp_keysym + }; + + let vkey = events::keycode_to_element(keysym as libc::c_uint); + + translated_events.push(KeyboardInput(state, event.keycode as u8, vkey)); + translated_events + } + + pub fn translate_event(&mut self, cookie: &ffi::XGenericEventCookie) -> Option { + use events::Event::{MouseInput, MouseMoved, MouseWheel}; + use events::ElementState::{Pressed, Released}; + use events::MouseButton::{Left, Right, Middle}; + use events::MouseScrollDelta::{PixelDelta, LineDelta}; + + match cookie.evtype { + ffi::XI_KeyPress | ffi::XI_KeyRelease => { + let event_data: &ffi::XIDeviceEvent = unsafe{mem::transmute(cookie.data)}; + if cookie.evtype == ffi::XI_KeyPress { + if event_data.flags & ffi::XIKeyRepeat == 0 { + println!("XInput Key {} pressed", event_data.detail); + None + } else { + None + } + } else { + None + } + }, + ffi::XI_ButtonPress | ffi::XI_ButtonRelease => { + let event_data: &ffi::XIDeviceEvent = unsafe{mem::transmute(cookie.data)}; + let state = if cookie.evtype == ffi::XI_ButtonPress { + Pressed + } else { + Released + }; + match event_data.detail as u32 { + ffi::Button1 => Some(MouseInput(state, Left)), + ffi::Button2 => Some(MouseInput(state, Middle)), + ffi::Button3 => Some(MouseInput(state, Right)), + ffi::Button4 => { + if event_data.flags & ffi::XIPointerEmulated == 0 { + Some(MouseWheel(LineDelta(0.0, 1.0))) + } else { + // emulated button event from a touch/smooth-scroll + // event. Scrolling is instead reported via the + // XI_Motion event handler + None + } + }, + ffi::Button5 => { + if event_data.flags & ffi::XIPointerEmulated == 0 { + Some(MouseWheel(LineDelta(0.0, -1.0))) + } else { + None + } + }, + _ => None + } + }, + ffi::XI_Motion => { + let event_data: &ffi::XIDeviceEvent = unsafe{mem::transmute(cookie.data)}; + let axis_state = event_data.valuators; + let mask = unsafe{ from_raw_parts(axis_state.mask, axis_state.mask_len as usize) }; + let mut axis_count = 0; + + let mut scroll_delta = (0.0, 0.0); + for axis_id in 0..axis_state.mask_len { + if ffi::XIMaskIsSet(&mask, axis_id) { + let axis_value = unsafe{*axis_state.values.offset(axis_count)}; + let delta = calc_scroll_deltas(event_data, axis_id, axis_value, &self.axis_list, + &mut self.current_state.axis_values); + scroll_delta.0 += delta.0; + scroll_delta.1 += delta.1; + axis_count += 1; + } + } + + if scroll_delta.0.abs() > 0.0 || scroll_delta.1.abs() > 0.0 { + Some(MouseWheel(PixelDelta(scroll_delta.0 as f32, scroll_delta.1 as f32))) + } else { + let new_cursor_pos = (event_data.event_x, event_data.event_y); + if new_cursor_pos != self.current_state.cursor_pos { + self.current_state.cursor_pos = new_cursor_pos; + Some(MouseMoved((new_cursor_pos.0 as i32, new_cursor_pos.1 as i32))) + } else { + None + } + } + }, + _ => None + } + } +} + +fn read_input_axis_info(display: &Arc) -> Vec { + let mut axis_list = Vec::new(); + let mut device_count = 0; + + // only get events from the master devices which are 'attached' + // to the keyboard or cursor + let devices = unsafe{ + (display.xinput2.XIQueryDevice)(display.display, ffi::XIAllMasterDevices, &mut device_count) + }; + for i in 0..device_count { + let device = unsafe { *(devices.offset(i as isize)) }; + for k in 0..device.num_classes { + let class = unsafe { *(device.classes.offset(k as isize)) }; + match unsafe { (*class)._type } { + ffi::XIScrollClass => { + let scroll_class: &ffi::XIScrollClassInfo = unsafe{mem::transmute(class)}; + axis_list.push(Axis{ + id: scroll_class.sourceid, + device_id: device.deviceid, + axis_number: scroll_class.number, + axis_type: match scroll_class.scroll_type { + ffi::XIScrollTypeHorizontal => AxisType::HorizontalScroll, + ffi::XIScrollTypeVertical => AxisType::VerticalScroll, + _ => { unreachable!() } + } + }) + }, + ffi::XIValuatorClass => { + let valuator_class: &ffi::XIValuatorClassInfo = unsafe{mem::transmute(class)}; + axis_list.push(Axis{ + id: valuator_class.sourceid, + device_id: device.deviceid, + axis_number: valuator_class.number, + axis_type: AxisType::Other + }) + }, + _ => {} + } + } + } + + axis_list.sort_by(|a, b| { + if a.device_id != b.device_id { + a.device_id.cmp(&b.device_id) + } else if a.id != b.id { + a.id.cmp(&b.id) + } else { + a.axis_number.cmp(&b.axis_number) + } + }); + axis_list +} + +/// Given an input motion event for an axis and the previous +/// state of the axises, return the horizontal/vertical +/// scroll deltas +fn calc_scroll_deltas(event: &ffi::XIDeviceEvent, + axis_id: i32, + axis_value: f64, + axis_list: &[Axis], + prev_axis_values: &mut Vec) -> (f64, f64) { + let prev_value_pos = prev_axis_values.iter().position(|prev_axis| { + prev_axis.device_id == event.sourceid && + prev_axis.axis_number == axis_id + }); + let delta = match prev_value_pos { + Some(idx) => axis_value - prev_axis_values[idx].value, + None => 0.0 + }; + + let new_axis_value = AxisValue{ + device_id: event.sourceid, + axis_number: axis_id, + value: axis_value + }; + + match prev_value_pos { + Some(idx) => prev_axis_values[idx] = new_axis_value, + None => prev_axis_values.push(new_axis_value) + } + + let mut scroll_delta = (0.0, 0.0); + + for axis in axis_list.iter() { + if axis.id == event.sourceid && + axis.axis_number == axis_id { + match axis.axis_type { + AxisType::HorizontalScroll => scroll_delta.0 = delta, + AxisType::VerticalScroll => scroll_delta.1 = delta, + _ => {} + } + } + } + + scroll_delta +} + diff --git a/src/api/x11/mod.rs b/src/api/x11/mod.rs index e900583..1ba6bc7 100644 --- a/src/api/x11/mod.rs +++ b/src/api/x11/mod.rs @@ -7,6 +7,7 @@ pub use self::xdisplay::XConnection; pub mod ffi; mod events; +mod input; mod monitor; mod window; mod xdisplay; diff --git a/src/api/x11/window.rs b/src/api/x11/window.rs index 15e27ce..300fdb5 100644 --- a/src/api/x11/window.rs +++ b/src/api/x11/window.rs @@ -2,10 +2,12 @@ use {Event, BuilderAttribs, MouseCursor}; use CreationError; use CreationError::OsError; use libc; +use std::borrow::Borrow; use std::{mem, ptr}; use std::cell::Cell; use std::sync::atomic::AtomicBool; use std::collections::VecDeque; +use std::slice::from_raw_parts; use std::sync::{Arc, Mutex}; use Api; @@ -20,6 +22,7 @@ use api::egl::Context as EglContext; use platform::MonitorID as PlatformMonitorID; +use super::input::XInputEventHandler; use super::{events, ffi}; use super::{MonitorID, XConnection}; @@ -106,8 +109,64 @@ impl WindowProxy { } } +// XEvents of type GenericEvent store their actual data +// in an XGenericEventCookie data structure. This is a wrapper +// to extract the cookie from a GenericEvent XEvent and release +// the cookie data once it has been processed +struct GenericEventCookie<'a> { + display: &'a XConnection, + cookie: ffi::XGenericEventCookie +} + +impl<'a> GenericEventCookie<'a> { + fn from_event<'b>(display: &'b XConnection, event: ffi::XEvent) -> Option> { + unsafe { + let mut cookie: ffi::XGenericEventCookie = From::from(event); + if (display.xlib.XGetEventData)(display.display, &mut cookie) == ffi::True { + Some(GenericEventCookie{display: display, cookie: cookie}) + } else { + None + } + } + } +} + +impl<'a> Drop for GenericEventCookie<'a> { + fn drop(&mut self) { + unsafe { + let xlib = &self.display.xlib; + (xlib.XFreeEventData)(self.display.display, &mut self.cookie); + } + } +} + pub struct PollEventsIterator<'a> { - window: &'a Window, + window: &'a Window +} + +impl<'a> PollEventsIterator<'a> { + fn queue_event(&mut self, event: Event) { + self.window.pending_events.lock().unwrap().push_back(event); + } + + fn process_generic_event(&mut self, event: &ffi::XEvent) { + if let Some(cookie) = GenericEventCookie::from_event(self.window.x.display.borrow(), *event) { + match cookie.cookie.evtype { + ffi::XI_DeviceChanged...ffi::XI_LASTEVENT => { + match self.window.input_handler.lock() { + Ok(mut handler) => { + match handler.translate_event(&cookie.cookie) { + Some(event) => self.queue_event(event), + None => {} + } + }, + Err(_) => {} + } + }, + _ => {} + } + } + } } impl<'a> Iterator for PollEventsIterator<'a> { @@ -126,7 +185,10 @@ impl<'a> Iterator for PollEventsIterator<'a> { let res = unsafe { (self.window.x.display.xlib.XCheckTypedEvent)(self.window.x.display.display, ffi::ClientMessage, &mut xev) }; if res == 0 { - return None; + let res = unsafe { (self.window.x.display.xlib.XCheckTypedEvent)(self.window.x.display.display, ffi::GenericEvent, &mut xev) }; + if res == 0 { + return None; + } } } @@ -164,93 +226,16 @@ impl<'a> Iterator for PollEventsIterator<'a> { return Some(Refresh); }, - ffi::MotionNotify => { - use events::Event::MouseMoved; - let event: &ffi::XMotionEvent = unsafe { mem::transmute(&xev) }; - return Some(MouseMoved((event.x as i32, event.y as i32))); - }, - - ffi::KeyPress | ffi::KeyRelease => { - use events::Event::{KeyboardInput, ReceivedCharacter}; - use events::ElementState::{Pressed, Released}; - let event: &mut ffi::XKeyEvent = unsafe { mem::transmute(&mut xev) }; - - if event.type_ == ffi::KeyPress { - let raw_ev: *mut ffi::XKeyEvent = event; - unsafe { (self.window.x.display.xlib.XFilterEvent)(mem::transmute(raw_ev), self.window.x.window) }; + ffi::KeyPress | ffi::KeyRelease => { + let mut event: &mut ffi::XKeyEvent = unsafe { mem::transmute(&mut xev) }; + let events = self.window.input_handler.lock().unwrap().translate_key_event(&mut event); + for event in events { + self.queue_event(event); } - - let state = if xev.get_type() == ffi::KeyPress { Pressed } else { Released }; - - let mut kp_keysym = 0; - - let written = unsafe { - use std::str; - - let mut buffer: [u8; 16] = [mem::uninitialized(); 16]; - let raw_ev: *mut ffi::XKeyEvent = event; - let count = (self.window.x.display.xlib.Xutf8LookupString)(self.window.x.ic, mem::transmute(raw_ev), - mem::transmute(buffer.as_mut_ptr()), - buffer.len() as libc::c_int, &mut kp_keysym, ptr::null_mut()); - - str::from_utf8(&buffer[..count as usize]).unwrap_or("").to_string() - }; - - { - let mut pending = self.window.pending_events.lock().unwrap(); - for chr in written.chars() { - pending.push_back(ReceivedCharacter(chr)); - } - } - - let mut keysym = unsafe { - (self.window.x.display.xlib.XKeycodeToKeysym)(self.window.x.display.display, event.keycode as ffi::KeyCode, 0) - }; - - if (ffi::XK_KP_Space as libc::c_ulong <= keysym) && (keysym <= ffi::XK_KP_9 as libc::c_ulong) { - keysym = kp_keysym - }; - - let vkey = events::keycode_to_element(keysym as libc::c_uint); - - return Some(KeyboardInput(state, event.keycode as u8, vkey)); - }, - - ffi::ButtonPress | ffi::ButtonRelease => { - use events::Event::{MouseInput, MouseWheel}; - use events::ElementState::{Pressed, Released}; - use events::MouseButton::{Left, Right, Middle}; - use events::MouseScrollDelta::{LineDelta}; - - let event: &ffi::XButtonEvent = unsafe { mem::transmute(&xev) }; - - let state = if xev.get_type() == ffi::ButtonPress { Pressed } else { Released }; - - let button = match event.button { - ffi::Button1 => Some(Left), - ffi::Button2 => Some(Middle), - ffi::Button3 => Some(Right), - ffi::Button4 => { - let delta = LineDelta(0.0, 1.0); - self.window.pending_events.lock().unwrap().push_back(MouseWheel(delta)); - None - } - ffi::Button5 => { - let delta = LineDelta(0.0, -1.0); - self.window.pending_events.lock().unwrap().push_back(MouseWheel(delta)); - None - } - _ => None - }; - - match button { - Some(button) => - return Some(MouseInput(state, button)), - None => () - }; }, + ffi::GenericEvent => { self.process_generic_event(&mut xev); } - _ => () + _ => {} }; } } @@ -296,6 +281,7 @@ pub struct Window { /// Events that have been retreived with XLib but not dispatched with iterators yet pending_events: Mutex>, cursor_state: Mutex, + input_handler: Mutex } impl Window { @@ -608,6 +594,7 @@ impl Window { pixel_format: pixel_format, pending_events: Mutex::new(VecDeque::new()), cursor_state: Mutex::new(CursorState::Normal), + input_handler: Mutex::new(XInputEventHandler::new(display, window, ic)) }; // returning diff --git a/src/api/x11/xdisplay.rs b/src/api/x11/xdisplay.rs index 9037155..2576b74 100644 --- a/src/api/x11/xdisplay.rs +++ b/src/api/x11/xdisplay.rs @@ -12,6 +12,7 @@ pub struct XConnection { pub xlib: ffi::Xlib, pub xf86vmode: ffi::Xf86vmode, pub xcursor: ffi::Xcursor, + pub xinput2: ffi::XInput2, pub glx: Option, pub egl: Option, pub display: *mut ffi::Display, @@ -30,6 +31,7 @@ impl XConnection { let xlib = try!(ffi::Xlib::open().map_err(|_| XNotSupported)); let xcursor = try!(ffi::Xcursor::open().map_err(|_| XNotSupported)); let xf86vmode = try!(ffi::Xf86vmode::open().map_err(|_| XNotSupported)); + let xinput2 = try!(ffi::XInput2::open().map_err(|_| XNotSupported)); unsafe extern "C" fn x_error_callback(_: *mut ffi::Display, event: *mut ffi::XErrorEvent) -> libc::c_int @@ -86,6 +88,7 @@ impl XConnection { xlib: xlib, xf86vmode: xf86vmode, xcursor: xcursor, + xinput2: xinput2, glx: glx, egl: egl, display: display, -- cgit v1.2.3 From cb08d9b05bb904e82a44bc67c818af9ba8a94568 Mon Sep 17 00:00:00 2001 From: Robert Knight Date: Sun, 28 Jun 2015 21:55:54 +0100 Subject: Remove XInput2 code for handling keyboard events * For the moment we're still using plain core X11 events for handling keyboard activity, so remove the XInput2 code for that * Small refactoring of X11 input handling and documentation fixes --- src/api/x11/input.rs | 83 ++++++++++++++++++--------------------------------- src/api/x11/window.rs | 10 ++++--- 2 files changed, 35 insertions(+), 58 deletions(-) (limited to 'src') diff --git a/src/api/x11/input.rs b/src/api/x11/input.rs index 6b6d24d..131f080 100644 --- a/src/api/x11/input.rs +++ b/src/api/x11/input.rs @@ -13,8 +13,7 @@ use super::XConnection; #[derive(Debug)] enum AxisType { HorizontalScroll, - VerticalScroll, - Other + VerticalScroll } #[derive(Debug)] @@ -33,7 +32,11 @@ struct AxisValue { } struct InputState { + /// Last-seen cursor position within a window in (x, y) + /// coordinates cursor_pos: (f64, f64), + /// Last-seen positions of axes, used to report delta + /// movements when a new absolute axis value is received axis_values: Vec } @@ -68,7 +71,10 @@ impl XInputEventHandler { } } - // specify the XInput events we want to receive + // specify the XInput events we want to receive. + // Button clicks and mouse events are handled via XInput + // events. Key presses are still handled via plain core + // X11 events. let mut mask: [libc::c_uchar; 1] = [0]; let mut input_event_mask = ffi::XIEventMask { deviceid: ffi::XIAllDevices, @@ -132,10 +138,8 @@ impl XInputEventHandler { str::from_utf8(&buffer[..count as usize]).unwrap_or("").to_string() }; - { - for chr in written.chars() { - translated_events.push(ReceivedCharacter(chr)); - } + for chr in written.chars() { + translated_events.push(ReceivedCharacter(chr)); } let mut keysym = unsafe { @@ -159,19 +163,6 @@ impl XInputEventHandler { use events::MouseScrollDelta::{PixelDelta, LineDelta}; match cookie.evtype { - ffi::XI_KeyPress | ffi::XI_KeyRelease => { - let event_data: &ffi::XIDeviceEvent = unsafe{mem::transmute(cookie.data)}; - if cookie.evtype == ffi::XI_KeyPress { - if event_data.flags & ffi::XIKeyRepeat == 0 { - println!("XInput Key {} pressed", event_data.detail); - None - } else { - None - } - } else { - None - } - }, ffi::XI_ButtonPress | ffi::XI_ButtonRelease => { let event_data: &ffi::XIDeviceEvent = unsafe{mem::transmute(cookie.data)}; let state = if cookie.evtype == ffi::XI_ButtonPress { @@ -183,23 +174,23 @@ impl XInputEventHandler { ffi::Button1 => Some(MouseInput(state, Left)), ffi::Button2 => Some(MouseInput(state, Middle)), ffi::Button3 => Some(MouseInput(state, Right)), - ffi::Button4 => { + ffi::Button4 | ffi::Button5 => { if event_data.flags & ffi::XIPointerEmulated == 0 { - Some(MouseWheel(LineDelta(0.0, 1.0))) + // scroll event from a traditional wheel with + // distinct 'clicks' + let delta = if event_data.detail as u32 == ffi::Button4 { + 1.0 + } else { + -1.0 + }; + Some(MouseWheel(LineDelta(0.0, delta))) } else { // emulated button event from a touch/smooth-scroll - // event. Scrolling is instead reported via the - // XI_Motion event handler - None - } - }, - ffi::Button5 => { - if event_data.flags & ffi::XIPointerEmulated == 0 { - Some(MouseWheel(LineDelta(0.0, -1.0))) - } else { + // event. Ignore these events and handle scrolling + // via XI_Motion event handler instead None } - }, + } _ => None } }, @@ -252,6 +243,9 @@ fn read_input_axis_info(display: &Arc) -> Vec { for k in 0..device.num_classes { let class = unsafe { *(device.classes.offset(k as isize)) }; match unsafe { (*class)._type } { + // Note that scroll axis + // are reported both as 'XIScrollClass' and 'XIValuatorClass' + // axes. For the moment we only care about scrolling axes. ffi::XIScrollClass => { let scroll_class: &ffi::XIScrollClassInfo = unsafe{mem::transmute(class)}; axis_list.push(Axis{ @@ -265,34 +259,16 @@ fn read_input_axis_info(display: &Arc) -> Vec { } }) }, - ffi::XIValuatorClass => { - let valuator_class: &ffi::XIValuatorClassInfo = unsafe{mem::transmute(class)}; - axis_list.push(Axis{ - id: valuator_class.sourceid, - device_id: device.deviceid, - axis_number: valuator_class.number, - axis_type: AxisType::Other - }) - }, _ => {} } } } - - axis_list.sort_by(|a, b| { - if a.device_id != b.device_id { - a.device_id.cmp(&b.device_id) - } else if a.id != b.id { - a.id.cmp(&b.id) - } else { - a.axis_number.cmp(&b.axis_number) - } - }); + axis_list } /// Given an input motion event for an axis and the previous -/// state of the axises, return the horizontal/vertical +/// state of the axes, return the horizontal/vertical /// scroll deltas fn calc_scroll_deltas(event: &ffi::XIDeviceEvent, axis_id: i32, @@ -326,8 +302,7 @@ fn calc_scroll_deltas(event: &ffi::XIDeviceEvent, axis.axis_number == axis_id { match axis.axis_type { AxisType::HorizontalScroll => scroll_delta.0 = delta, - AxisType::VerticalScroll => scroll_delta.1 = delta, - _ => {} + AxisType::VerticalScroll => scroll_delta.1 = delta } } } diff --git a/src/api/x11/window.rs b/src/api/x11/window.rs index 300fdb5..b430ec9 100644 --- a/src/api/x11/window.rs +++ b/src/api/x11/window.rs @@ -177,15 +177,17 @@ impl<'a> Iterator for PollEventsIterator<'a> { return Some(ev); } + let xlib = &self.window.x.display.xlib; + loop { let mut xev = unsafe { mem::uninitialized() }; - let res = unsafe { (self.window.x.display.xlib.XCheckMaskEvent)(self.window.x.display.display, -1, &mut xev) }; + let res = unsafe { (xlib.XCheckMaskEvent)(self.window.x.display.display, -1, &mut xev) }; if res == 0 { - let res = unsafe { (self.window.x.display.xlib.XCheckTypedEvent)(self.window.x.display.display, ffi::ClientMessage, &mut xev) }; + let res = unsafe { (xlib.XCheckTypedEvent)(self.window.x.display.display, ffi::ClientMessage, &mut xev) }; if res == 0 { - let res = unsafe { (self.window.x.display.xlib.XCheckTypedEvent)(self.window.x.display.display, ffi::GenericEvent, &mut xev) }; + let res = unsafe { (xlib.XCheckTypedEvent)(self.window.x.display.display, ffi::GenericEvent, &mut xev) }; if res == 0 { return None; } @@ -194,7 +196,7 @@ impl<'a> Iterator for PollEventsIterator<'a> { match xev.get_type() { ffi::KeymapNotify => { - unsafe { (self.window.x.display.xlib.XRefreshKeyboardMapping)(mem::transmute(&xev)); } + unsafe { (xlib.XRefreshKeyboardMapping)(mem::transmute(&xev)); } }, ffi::ClientMessage => { -- cgit v1.2.3 From edc95d554dd530a810240261d44013f39bef1c6d Mon Sep 17 00:00:00 2001 From: Robert Knight Date: Sun, 28 Jun 2015 22:09:26 +0100 Subject: X11 - Ignore scroll events that happen outside of Glutin windows Scroll deltas are calculated in X11 by comparing the current and previous absolute values for the scroll axis when a scroll motion event is received. If the user scrolls whilst the cursor is outside of the window then an incorrect delta is reported when the cursor re-enters the window. Fix this by resetting the last-seen axis values whenever the cursor re-enters the window. --- src/api/x11/input.rs | 16 ++++++++++++++-- src/api/x11/window.rs | 3 +-- 2 files changed, 15 insertions(+), 4 deletions(-) (limited to 'src') diff --git a/src/api/x11/input.rs b/src/api/x11/input.rs index 131f080..2448c08 100644 --- a/src/api/x11/input.rs +++ b/src/api/x11/input.rs @@ -75,7 +75,7 @@ impl XInputEventHandler { // Button clicks and mouse events are handled via XInput // events. Key presses are still handled via plain core // X11 events. - let mut mask: [libc::c_uchar; 1] = [0]; + let mut mask: [libc::c_uchar; 2] = [0, 0]; let mut input_event_mask = ffi::XIEventMask { deviceid: ffi::XIAllDevices, mask_len: mask.len() as i32, @@ -84,7 +84,9 @@ impl XInputEventHandler { let events = &[ ffi::XI_ButtonPress, ffi::XI_ButtonRelease, - ffi::XI_Motion + ffi::XI_Motion, + ffi::XI_Enter, + ffi::XI_Leave ]; for event in events { ffi::XISetMask(&mut mask, *event); @@ -224,6 +226,16 @@ impl XInputEventHandler { } } }, + ffi::XI_Enter => { + // axis movements whilst the cursor is outside the window + // will alter the absolute value of the axes. We only want to + // report changes in the axis value whilst the cursor is above + // our window however, so clear the previous axis state whenever + // the cursor re-enters the window + self.current_state.axis_values.clear(); + None + }, + ffi::XI_Leave => None, _ => None } } diff --git a/src/api/x11/window.rs b/src/api/x11/window.rs index b430ec9..9a01e17 100644 --- a/src/api/x11/window.rs +++ b/src/api/x11/window.rs @@ -7,7 +7,6 @@ use std::{mem, ptr}; use std::cell::Cell; use std::sync::atomic::AtomicBool; use std::collections::VecDeque; -use std::slice::from_raw_parts; use std::sync::{Arc, Mutex}; use Api; @@ -23,7 +22,7 @@ use api::egl::Context as EglContext; use platform::MonitorID as PlatformMonitorID; use super::input::XInputEventHandler; -use super::{events, ffi}; +use super::{ffi}; use super::{MonitorID, XConnection}; // XOpenIM doesn't seem to be thread-safe -- cgit v1.2.3 From 573a7aeaf1e38f53e830b914da4f71f904ca8c9e Mon Sep 17 00:00:00 2001 From: Robert Knight Date: Sun, 28 Jun 2015 22:16:43 +0100 Subject: Report focus in/out events under X11 Fixes #377 --- src/api/x11/input.rs | 8 ++++++-- 1 file changed, 6 insertions(+), 2 deletions(-) (limited to 'src') diff --git a/src/api/x11/input.rs b/src/api/x11/input.rs index 2448c08..25add95 100644 --- a/src/api/x11/input.rs +++ b/src/api/x11/input.rs @@ -86,7 +86,9 @@ impl XInputEventHandler { ffi::XI_ButtonRelease, ffi::XI_Motion, ffi::XI_Enter, - ffi::XI_Leave + ffi::XI_Leave, + ffi::XI_FocusIn, + ffi::XI_FocusOut ]; for event in events { ffi::XISetMask(&mut mask, *event); @@ -159,7 +161,7 @@ impl XInputEventHandler { } pub fn translate_event(&mut self, cookie: &ffi::XGenericEventCookie) -> Option { - use events::Event::{MouseInput, MouseMoved, MouseWheel}; + use events::Event::{Focused, MouseInput, MouseMoved, MouseWheel}; use events::ElementState::{Pressed, Released}; use events::MouseButton::{Left, Right, Middle}; use events::MouseScrollDelta::{PixelDelta, LineDelta}; @@ -236,6 +238,8 @@ impl XInputEventHandler { None }, ffi::XI_Leave => None, + ffi::XI_FocusIn => Some(Focused(true)), + ffi::XI_FocusOut => Some(Focused(false)), _ => None } } -- cgit v1.2.3 From d960753360fdc70e94ce2633db1e03d5ddc7b657 Mon Sep 17 00:00:00 2001 From: Robert Knight Date: Mon, 13 Jul 2015 07:26:07 +0100 Subject: Address code review feedback * Fix an issue where PollEventsIterator::next() would fail to return keyboard input and mouse events immediately but instead only return them on the next call to next() * Inline process_generic_event() and queue_event() --- src/api/x11/window.rs | 55 ++++++++++++++++++++++----------------------------- 1 file changed, 24 insertions(+), 31 deletions(-) (limited to 'src') diff --git a/src/api/x11/window.rs b/src/api/x11/window.rs index 9a01e17..b13a94a 100644 --- a/src/api/x11/window.rs +++ b/src/api/x11/window.rs @@ -143,42 +143,17 @@ pub struct PollEventsIterator<'a> { window: &'a Window } -impl<'a> PollEventsIterator<'a> { - fn queue_event(&mut self, event: Event) { - self.window.pending_events.lock().unwrap().push_back(event); - } - - fn process_generic_event(&mut self, event: &ffi::XEvent) { - if let Some(cookie) = GenericEventCookie::from_event(self.window.x.display.borrow(), *event) { - match cookie.cookie.evtype { - ffi::XI_DeviceChanged...ffi::XI_LASTEVENT => { - match self.window.input_handler.lock() { - Ok(mut handler) => { - match handler.translate_event(&cookie.cookie) { - Some(event) => self.queue_event(event), - None => {} - } - }, - Err(_) => {} - } - }, - _ => {} - } - } - } -} - impl<'a> Iterator for PollEventsIterator<'a> { type Item = Event; fn next(&mut self) -> Option { - if let Some(ev) = self.window.pending_events.lock().unwrap().pop_front() { - return Some(ev); - } - let xlib = &self.window.x.display.xlib; loop { + if let Some(ev) = self.window.pending_events.lock().unwrap().pop_front() { + return Some(ev); + } + let mut xev = unsafe { mem::uninitialized() }; let res = unsafe { (xlib.XCheckMaskEvent)(self.window.x.display.display, -1, &mut xev) }; @@ -231,10 +206,28 @@ impl<'a> Iterator for PollEventsIterator<'a> { let mut event: &mut ffi::XKeyEvent = unsafe { mem::transmute(&mut xev) }; let events = self.window.input_handler.lock().unwrap().translate_key_event(&mut event); for event in events { - self.queue_event(event); + self.window.pending_events.lock().unwrap().push_back(event); } }, - ffi::GenericEvent => { self.process_generic_event(&mut xev); } + + ffi::GenericEvent => { + if let Some(cookie) = GenericEventCookie::from_event(self.window.x.display.borrow(), xev) { + match cookie.cookie.evtype { + ffi::XI_DeviceChanged...ffi::XI_LASTEVENT => { + match self.window.input_handler.lock() { + Ok(mut handler) => { + match handler.translate_event(&cookie.cookie) { + Some(event) => self.window.pending_events.lock().unwrap().push_back(event), + None => {} + } + }, + Err(_) => {} + } + }, + _ => {} + } + } + } _ => {} }; -- cgit v1.2.3