x11: Always use correct window ID for XInput2 events (#372)

The `CursorMoved` events that are used to send position updates alongside `Focused` and
`CursorEntered` events were using incorrect values for the window ID. This is a direct
result of the X11 backend being hard to understand, as those values came from variables in
the top-level scope of the function, which one would assume to be valid throughout the
entirety of their scope. In reality, their validity is dependent on the event belonging to
the `XEvent` union, so very surprising things can happen if those variables are read in the
case of XInput2/XKB/etc. events. To prevent future accidents, the aforementioned variables
have been removed, and are now defined per-event instead.

Additionally, the `CursorMoved` event sent alongside `Focused` now uses the correct device
ID; it previously used the ID of a master keyboard, but now uses the ID of the pointer
paired to that keyboard. Note that for those using multi-pointer X, the correctness of this
ID is dependent on the correctness of the window manager's focus model.
This commit is contained in:
Francesca Sunshine 2018-01-08 05:06:02 -05:00 committed by Pierre Krieger
parent 198d9ff230
commit 124b16fcde
2 changed files with 290 additions and 151 deletions

View file

@ -9,14 +9,16 @@ pub mod ffi;
use platform::PlatformSpecificWindowBuilderAttributes; use platform::PlatformSpecificWindowBuilderAttributes;
use {CreationError, Event, EventsLoopClosed, WindowEvent, DeviceEvent, use {CreationError, Event, EventsLoopClosed, WindowEvent, DeviceEvent,
KeyboardInput, ControlFlow}; KeyboardInput, ControlFlow};
use events::ModifiersState;
use std::{mem, ptr, slice}; use std::{mem, ptr, slice};
use std::sync::{Arc, Mutex, Weak}; use std::sync::{Arc, Mutex, Weak};
use std::sync::atomic::{self, AtomicBool}; use std::sync::atomic::{self, AtomicBool};
use std::collections::HashMap; use std::collections::HashMap;
use std::ffi::CStr; use std::ffi::CStr;
use std::os::raw::{c_char, c_int, c_long, c_uchar, c_ulong};
use libc::{self, c_uchar, c_char, c_int, c_ulong, c_long}; use libc;
mod events; mod events;
mod monitor; mod monitor;
@ -202,8 +204,6 @@ impl EventsLoop {
return; return;
} }
let xwindow = { let xev: &ffi::XAnyEvent = xev.as_ref(); xev.window };
let wid = ::WindowId(::platform::WindowId::X(WindowId(xwindow)));
match xev.get_type() { match xev.get_type() {
ffi::MappingNotify => { ffi::MappingNotify => {
unsafe { (xlib.XRefreshKeyboardMapping)(xev.as_mut()); } unsafe { (xlib.XRefreshKeyboardMapping)(xev.as_mut()); }
@ -213,8 +213,11 @@ impl EventsLoop {
ffi::ClientMessage => { ffi::ClientMessage => {
let client_msg: &ffi::XClientMessageEvent = xev.as_ref(); let client_msg: &ffi::XClientMessageEvent = xev.as_ref();
let window = client_msg.window;
let window_id = mkwid(window);
if client_msg.data.get_long(0) as ffi::Atom == self.wm_delete_window { if client_msg.data.get_long(0) as ffi::Atom == self.wm_delete_window {
callback(Event::WindowEvent { window_id: wid, event: WindowEvent::Closed }) callback(Event::WindowEvent { window_id, event: WindowEvent::Closed })
} else if client_msg.message_type == self.dnd.atoms.enter { } else if client_msg.message_type == self.dnd.atoms.enter {
let source_window = client_msg.data.get_long(0) as c_ulong; let source_window = client_msg.data.get_long(0) as c_ulong;
let flags = client_msg.data.get_long(1); let flags = client_msg.data.get_long(1);
@ -274,16 +277,16 @@ impl EventsLoop {
ffi::CurrentTime ffi::CurrentTime
}; };
// This results in the SelectionNotify event below // This results in the SelectionNotify event below
self.dnd.convert_selection(xwindow, time); self.dnd.convert_selection(window, time);
} }
self.dnd.send_status(xwindow, source_window, DndState::Accepted) self.dnd.send_status(window, source_window, DndState::Accepted)
.expect("Failed to send XDnD status message."); .expect("Failed to send XDnD status message.");
} }
} else { } else {
unsafe { unsafe {
self.dnd.send_status(xwindow, source_window, DndState::Rejected) self.dnd.send_status(window, source_window, DndState::Rejected)
.expect("Failed to send XDnD status message."); .expect("Failed to send XDnD status message.");
self.dnd.send_finished(xwindow, source_window, DndState::Rejected) self.dnd.send_finished(window, source_window, DndState::Rejected)
.expect("Failed to send XDnD finished message."); .expect("Failed to send XDnD finished message.");
} }
self.dnd.reset(); self.dnd.reset();
@ -293,13 +296,13 @@ impl EventsLoop {
if let Some(Ok(ref path_list)) = self.dnd.result { if let Some(Ok(ref path_list)) = self.dnd.result {
for path in path_list { for path in path_list {
callback(Event::WindowEvent { callback(Event::WindowEvent {
window_id: wid, window_id,
event: WindowEvent::DroppedFile(path.clone()), event: WindowEvent::DroppedFile(path.clone()),
}); });
} }
} }
unsafe { unsafe {
self.dnd.send_finished(xwindow, source_window, DndState::Accepted) self.dnd.send_finished(window, source_window, DndState::Accepted)
.expect("Failed to send XDnD finished message."); .expect("Failed to send XDnD finished message.");
} }
} }
@ -307,7 +310,7 @@ impl EventsLoop {
} else if client_msg.message_type == self.dnd.atoms.leave { } else if client_msg.message_type == self.dnd.atoms.leave {
self.dnd.reset(); self.dnd.reset();
callback(Event::WindowEvent { callback(Event::WindowEvent {
window_id: wid, window_id,
event: WindowEvent::HoveredFileCancelled, event: WindowEvent::HoveredFileCancelled,
}); });
} else if self.pending_wakeup.load(atomic::Ordering::Relaxed) { } else if self.pending_wakeup.load(atomic::Ordering::Relaxed) {
@ -318,16 +321,20 @@ impl EventsLoop {
ffi::SelectionNotify => { ffi::SelectionNotify => {
let xsel: &ffi::XSelectionEvent = xev.as_ref(); let xsel: &ffi::XSelectionEvent = xev.as_ref();
let window = xsel.requestor;
let window_id = mkwid(window);
if xsel.property == self.dnd.atoms.selection { if xsel.property == self.dnd.atoms.selection {
let mut result = None; let mut result = None;
// This is where we receive data from drag and drop // This is where we receive data from drag and drop
if let Ok(mut data) = unsafe { self.dnd.read_data(xwindow) } { if let Ok(mut data) = unsafe { self.dnd.read_data(window) } {
let parse_result = self.dnd.parse_data(&mut data); let parse_result = self.dnd.parse_data(&mut data);
if let Ok(ref path_list) = parse_result { if let Ok(ref path_list) = parse_result {
for path in path_list { for path in path_list {
callback(Event::WindowEvent { callback(Event::WindowEvent {
window_id: wid, window_id,
event: WindowEvent::HoveredFile(path.clone()), event: WindowEvent::HoveredFile(path.clone()),
}); });
} }
@ -341,42 +348,56 @@ impl EventsLoop {
ffi::ConfigureNotify => { ffi::ConfigureNotify => {
let xev: &ffi::XConfigureEvent = xev.as_ref(); let xev: &ffi::XConfigureEvent = xev.as_ref();
let size = (xev.width, xev.height);
let position = (xev.x, xev.y); let window = xev.window;
let window_id = mkwid(window);
let new_size = (xev.width, xev.height);
let new_position = (xev.x, xev.y);
// Gymnastics to ensure self.windows isn't locked when we invoke callback // Gymnastics to ensure self.windows isn't locked when we invoke callback
let (resized, moved) = { let (resized, moved) = {
let mut windows = self.windows.lock().unwrap(); let mut windows = self.windows.lock().unwrap();
let window_data = windows.get_mut(&WindowId(xwindow)).unwrap(); let window_data = windows.get_mut(&WindowId(window)).unwrap();
if window_data.config.is_none() { if window_data.config.is_none() {
window_data.config = Some(WindowConfig::new(xev)); window_data.config = Some(WindowConfig::new(xev));
(true, true) (true, true)
} else { } else {
let window = window_data.config.as_mut().unwrap(); let window_state = window_data.config.as_mut().unwrap();
(if window.size != size { (if window_state.size != new_size {
window.size = size; window_state.size = new_size;
true true
} else { false }, } else { false },
if window.position != position { if window_state.position != new_position {
window.position = position; window_state.position = new_position;
true true
} else { false }) } else { false })
} }
}; };
if resized { if resized {
callback(Event::WindowEvent { window_id: wid, event: WindowEvent::Resized(xev.width as u32, xev.height as u32) }); callback(Event::WindowEvent {
window_id,
event: WindowEvent::Resized(xev.width as u32, xev.height as u32),
});
} }
if moved { if moved {
callback(Event::WindowEvent { window_id: wid, event: WindowEvent::Moved(xev.x as i32, xev.y as i32) }); callback(Event::WindowEvent {
window_id,
event: WindowEvent::Moved(xev.x as i32, xev.y as i32),
});
} }
} }
ffi::Expose => { ffi::Expose => {
callback(Event::WindowEvent { window_id: wid, event: WindowEvent::Refresh }); let xev: &ffi::XExposeEvent = xev.as_ref();
let window = xev.window;
let window_id = mkwid(window);
callback(Event::WindowEvent { window_id, event: WindowEvent::Refresh });
} }
// FIXME: Use XInput2 + libxkbcommon for keyboard input! // FIXME: Use XInput2 + libxkbcommon for keyboard input!
ffi::KeyPress | ffi::KeyRelease => { ffi::KeyPress | ffi::KeyRelease => {
use events::ModifiersState;
use events::ElementState::{Pressed, Released}; use events::ElementState::{Pressed, Released};
let state; let state;
@ -388,15 +409,14 @@ impl EventsLoop {
let xkev: &mut ffi::XKeyEvent = xev.as_mut(); let xkev: &mut ffi::XKeyEvent = xev.as_mut();
let ev_mods = { let window = xkev.window;
// Translate x event state to mods let window_id = mkwid(window);
let state = xkev.state;
ModifiersState { let modifiers = ModifiersState {
alt: state & ffi::Mod1Mask != 0, alt: xkev.state & ffi::Mod1Mask != 0,
shift: state & ffi::ShiftMask != 0, shift: xkev.state & ffi::ShiftMask != 0,
ctrl: state & ffi::ControlMask != 0, ctrl: xkev.state & ffi::ControlMask != 0,
logo: state & ffi::Mod4Mask != 0, logo: xkev.state & ffi::Mod4Mask != 0,
}
}; };
let keysym = unsafe { let keysym = unsafe {
@ -407,14 +427,14 @@ impl EventsLoop {
let vkey = events::keysym_to_element(keysym as libc::c_uint); let vkey = events::keysym_to_element(keysym as libc::c_uint);
callback(Event::WindowEvent { window_id: wid, event: WindowEvent::KeyboardInput { callback(Event::WindowEvent { window_id, event: WindowEvent::KeyboardInput {
// Typical virtual core keyboard ID. xinput2 needs to be used to get a reliable value. // Typical virtual core keyboard ID. xinput2 needs to be used to get a reliable value.
device_id: mkdid(3), device_id: mkdid(3),
input: KeyboardInput { input: KeyboardInput {
state: state, state: state,
scancode: xkev.keycode - 8, scancode: xkev.keycode - 8,
virtual_keycode: vkey, virtual_keycode: vkey,
modifiers: ev_mods, modifiers,
}, },
}}); }});
@ -424,7 +444,7 @@ impl EventsLoop {
const INIT_BUFF_SIZE: usize = 16; const INIT_BUFF_SIZE: usize = 16;
let mut windows = self.windows.lock().unwrap(); let mut windows = self.windows.lock().unwrap();
let window_data = windows.get_mut(&WindowId(xwindow)).unwrap(); let window_data = windows.get_mut(&WindowId(window)).unwrap();
/* buffer allocated on heap instead of stack, due to the possible /* buffer allocated on heap instead of stack, due to the possible
* reallocation */ * reallocation */
let mut buffer: Vec<u8> = vec![mem::uninitialized(); INIT_BUFF_SIZE]; let mut buffer: Vec<u8> = vec![mem::uninitialized(); INIT_BUFF_SIZE];
@ -448,7 +468,7 @@ impl EventsLoop {
for chr in written.chars() { for chr in written.chars() {
let event = Event::WindowEvent { let event = Event::WindowEvent {
window_id: wid, window_id,
event: WindowEvent::ReceivedCharacter(chr), event: WindowEvent::ReceivedCharacter(chr),
}; };
callback(event); callback(event);
@ -471,26 +491,15 @@ impl EventsLoop {
match xev.evtype { match xev.evtype {
ffi::XI_ButtonPress | ffi::XI_ButtonRelease => { ffi::XI_ButtonPress | ffi::XI_ButtonRelease => {
use events::ModifiersState;
let xev: &ffi::XIDeviceEvent = unsafe { &*(xev.data as *const _) }; let xev: &ffi::XIDeviceEvent = unsafe { &*(xev.data as *const _) };
let wid = mkwid(xev.event); let window_id = mkwid(xev.event);
let did = mkdid(xev.deviceid); let device_id = mkdid(xev.deviceid);
if (xev.flags & ffi::XIPointerEmulated) != 0 && self.windows.lock().unwrap().get(&WindowId(xev.event)).unwrap().multitouch { if (xev.flags & ffi::XIPointerEmulated) != 0 && self.windows.lock().unwrap().get(&WindowId(xev.event)).unwrap().multitouch {
// Deliver multi-touch events instead of emulated mouse events. // Deliver multi-touch events instead of emulated mouse events.
return; return;
} }
let ev_mods = { let modifiers = ModifiersState::from(xev.mods);
// Translate x mod state to mods
let state = xev.mods.effective as u32;
ModifiersState {
alt: state & ffi::Mod1Mask != 0,
shift: state & ffi::ShiftMask != 0,
ctrl: state & ffi::ControlMask != 0,
logo: state & ffi::Mod4Mask != 0,
}
};
let state = if xev.evtype == ffi::XI_ButtonPress { let state = if xev.evtype == ffi::XI_ButtonPress {
Pressed Pressed
@ -498,53 +507,73 @@ impl EventsLoop {
Released Released
}; };
match xev.detail as u32 { match xev.detail as u32 {
ffi::Button1 => callback(Event::WindowEvent { window_id: wid, event: ffi::Button1 => callback(Event::WindowEvent {
MouseInput { device_id: did, state: state, button: Left, modifiers: ev_mods } }), window_id,
ffi::Button2 => callback(Event::WindowEvent { window_id: wid, event: event: MouseInput {
MouseInput { device_id: did, state: state, button: Middle, modifiers: ev_mods} }), device_id,
ffi::Button3 => callback(Event::WindowEvent { window_id: wid, event: state,
MouseInput { device_id: did, state: state, button: Right, modifiers: ev_mods } }), button: Left,
modifiers,
},
}),
ffi::Button2 => callback(Event::WindowEvent {
window_id,
event: MouseInput {
device_id,
state,
button: Middle,
modifiers,
},
}),
ffi::Button3 => callback(Event::WindowEvent {
window_id,
event: MouseInput {
device_id,
state,
button: Right,
modifiers,
},
}),
// Suppress emulated scroll wheel clicks, since we handle the real motion events for those. // Suppress emulated scroll wheel clicks, since we handle the real motion events for those.
// In practice, even clicky scroll wheels appear to be reported by evdev (and XInput2 in // In practice, even clicky scroll wheels appear to be reported by evdev (and XInput2 in
// turn) as axis motion, so we don't otherwise special-case these button presses. // turn) as axis motion, so we don't otherwise special-case these button presses.
4 | 5 | 6 | 7 => if xev.flags & ffi::XIPointerEmulated == 0 { 4 | 5 | 6 | 7 => if xev.flags & ffi::XIPointerEmulated == 0 {
callback(Event::WindowEvent { window_id: wid, event: MouseWheel { callback(Event::WindowEvent {
device_id: did, window_id,
delta: match xev.detail { event: MouseWheel {
4 => LineDelta(0.0, 1.0), device_id,
5 => LineDelta(0.0, -1.0), delta: match xev.detail {
6 => LineDelta(-1.0, 0.0), 4 => LineDelta(0.0, 1.0),
7 => LineDelta(1.0, 0.0), 5 => LineDelta(0.0, -1.0),
_ => unreachable!() 6 => LineDelta(-1.0, 0.0),
7 => LineDelta(1.0, 0.0),
_ => unreachable!(),
},
phase: TouchPhase::Moved,
modifiers,
}, },
phase: TouchPhase::Moved, });
modifiers: ev_mods,
}});
}, },
x => callback(Event::WindowEvent { window_id: wid, x => callback(Event::WindowEvent {
event: MouseInput { device_id: did, state: state, button: Other(x as u8), modifiers: ev_mods } }) window_id,
event: MouseInput {
device_id,
state,
button: Other(x as u8),
modifiers,
},
}),
} }
} }
ffi::XI_Motion => { ffi::XI_Motion => {
use events::ModifiersState;
let xev: &ffi::XIDeviceEvent = unsafe { &*(xev.data as *const _) }; let xev: &ffi::XIDeviceEvent = unsafe { &*(xev.data as *const _) };
let did = mkdid(xev.deviceid); let device_id = mkdid(xev.deviceid);
let wid = mkwid(xev.event); let window_id = mkwid(xev.event);
let new_cursor_pos = (xev.event_x, xev.event_y); let new_cursor_pos = (xev.event_x, xev.event_y);
let ev_mods = { let modifiers = ModifiersState::from(xev.mods);
// Translate x event state to mods
let state = xev.mods.effective as u32;
ModifiersState {
alt: state & ffi::Mod1Mask != 0,
shift: state & ffi::ShiftMask != 0,
ctrl: state & ffi::ControlMask != 0,
logo: state & ffi::Mod4Mask != 0,
}
};
// Gymnastics to ensure self.windows isn't locked when we invoke callback // Gymnastics to ensure self.windows isn't locked when we invoke callback
if { if {
@ -555,11 +584,14 @@ impl EventsLoop {
true true
} else { false } } else { false }
} { } {
callback(Event::WindowEvent { window_id: wid, event: CursorMoved { callback(Event::WindowEvent {
device_id: did, window_id,
position: new_cursor_pos, event: CursorMoved {
modifiers: ev_mods, device_id,
}}); position: new_cursor_pos,
modifiers,
},
});
} }
// More gymnastics, for self.devices // More gymnastics, for self.devices
@ -576,22 +608,28 @@ impl EventsLoop {
if let Some(&mut (_, ref mut info)) = physical_device.scroll_axes.iter_mut().find(|&&mut (axis, _)| axis == i) { if let Some(&mut (_, ref mut info)) = physical_device.scroll_axes.iter_mut().find(|&&mut (axis, _)| axis == i) {
let delta = (x - info.position) / info.increment; let delta = (x - info.position) / info.increment;
info.position = x; info.position = x;
events.push(Event::WindowEvent { window_id: wid, event: MouseWheel { events.push(Event::WindowEvent {
device_id: did, window_id,
delta: match info.orientation { event: MouseWheel {
ScrollOrientation::Horizontal => LineDelta(delta as f32, 0.0), device_id,
// X11 vertical scroll coordinates are opposite to winit's delta: match info.orientation {
ScrollOrientation::Vertical => LineDelta(0.0, -delta as f32), ScrollOrientation::Horizontal => LineDelta(delta as f32, 0.0),
// X11 vertical scroll coordinates are opposite to winit's
ScrollOrientation::Vertical => LineDelta(0.0, -delta as f32),
},
phase: TouchPhase::Moved,
modifiers,
}, },
phase: TouchPhase::Moved, });
modifiers: ev_mods,
}});
} else { } else {
events.push(Event::WindowEvent { window_id: wid, event: AxisMotion { events.push(Event::WindowEvent {
device_id: did, window_id,
axis: i as u32, event: AxisMotion {
value: unsafe { *value }, device_id,
}}); axis: i as u32,
value: unsafe { *value },
},
});
} }
value = unsafe { value.offset(1) }; value = unsafe { value.offset(1) };
} }
@ -603,19 +641,10 @@ impl EventsLoop {
} }
ffi::XI_Enter => { ffi::XI_Enter => {
use events::ModifiersState;
let xev: &ffi::XIEnterEvent = unsafe { &*(xev.data as *const _) }; let xev: &ffi::XIEnterEvent = unsafe { &*(xev.data as *const _) };
let ev_mods = { let window_id = mkwid(xev.event);
// Translate x event state to mods let device_id = mkdid(xev.deviceid);
let state = xev.mods.effective as u32;
ModifiersState {
alt: state & ffi::Mod1Mask != 0,
shift: state & ffi::ShiftMask != 0,
ctrl: state & ffi::ControlMask != 0,
logo: state & ffi::Mod4Mask != 0,
}
};
let mut devices = self.devices.lock().unwrap(); let mut devices = self.devices.lock().unwrap();
let physical_device = devices.get_mut(&DeviceId(xev.sourceid)).unwrap(); let physical_device = devices.get_mut(&DeviceId(xev.sourceid)).unwrap();
@ -624,47 +653,64 @@ impl EventsLoop {
physical_device.reset_scroll_position(info); physical_device.reset_scroll_position(info);
} }
} }
callback(Event::WindowEvent { window_id: mkwid(xev.event), event: CursorEntered { device_id: mkdid(xev.deviceid) } }); callback(Event::WindowEvent {
window_id,
event: CursorEntered { device_id },
});
let new_cursor_pos = (xev.event_x, xev.event_y); let new_cursor_pos = (xev.event_x, xev.event_y);
callback(Event::WindowEvent { window_id: wid, event: CursorMoved { // The mods field on this event isn't actually useful, so we have to
device_id: mkdid(xev.deviceid), // query the pointer device.
let modifiers = unsafe {
util::query_pointer(
&self.display,
xev.event,
xev.deviceid,
).expect("Failed to query pointer device")
}.get_modifier_state();
callback(Event::WindowEvent { window_id, event: CursorMoved {
device_id,
position: new_cursor_pos, position: new_cursor_pos,
modifiers: ev_mods, modifiers,
}}) }})
} }
ffi::XI_Leave => { ffi::XI_Leave => {
let xev: &ffi::XILeaveEvent = unsafe { &*(xev.data as *const _) }; let xev: &ffi::XILeaveEvent = unsafe { &*(xev.data as *const _) };
callback(Event::WindowEvent { window_id: mkwid(xev.event), event: CursorLeft { device_id: mkdid(xev.deviceid) } })
callback(Event::WindowEvent {
window_id: mkwid(xev.event),
event: CursorLeft { device_id: mkdid(xev.deviceid) },
});
} }
ffi::XI_FocusIn => { ffi::XI_FocusIn => {
use events::ModifiersState;
let xev: &ffi::XIFocusInEvent = unsafe { &*(xev.data as *const _) }; let xev: &ffi::XIFocusInEvent = unsafe { &*(xev.data as *const _) };
let ev_mods = { let window_id = mkwid(xev.event);
// Translate x event state to mods
let state = xev.mods.effective as u32;
ModifiersState {
alt: state & ffi::Mod1Mask != 0,
shift: state & ffi::ShiftMask != 0,
ctrl: state & ffi::ControlMask != 0,
logo: state & ffi::Mod4Mask != 0,
}
};
unsafe { unsafe {
let mut windows = self.windows.lock().unwrap(); let mut windows = self.windows.lock().unwrap();
let window_data = windows.get_mut(&WindowId(xev.event)).unwrap(); let window_data = windows.get_mut(&WindowId(xev.event)).unwrap();
(self.display.xlib.XSetICFocus)(window_data.ic); (self.display.xlib.XSetICFocus)(window_data.ic);
} }
callback(Event::WindowEvent { window_id: mkwid(xev.event), event: Focused(true) }); callback(Event::WindowEvent { window_id, event: Focused(true) });
let new_cursor_pos = (xev.event_x, xev.event_y); // The deviceid for this event is for a keyboard instead of a pointer, so
callback(Event::WindowEvent { window_id: wid, event: CursorMoved { // we have to do a little extra work.
device_id: mkdid(xev.deviceid), let device_info = DeviceInfo::get(&self.display, xev.deviceid);
position: new_cursor_pos, // For master devices, the attachment field contains the ID of the paired
modifiers: ev_mods, // master device; for the master keyboard, the attachment is the master
}}) // pointer, and vice versa.
let pointer_id = unsafe { (*device_info.info) }.attachment;
callback(Event::WindowEvent {
window_id,
event: CursorMoved {
device_id: mkdid(pointer_id),
position: (xev.event_x, xev.event_y),
modifiers: ModifiersState::from(xev.mods),
}
});
} }
ffi::XI_FocusOut => { ffi::XI_FocusOut => {
let xev: &ffi::XIFocusOutEvent = unsafe { &*(xev.data as *const _) }; let xev: &ffi::XIFocusOutEvent = unsafe { &*(xev.data as *const _) };
@ -673,24 +719,30 @@ impl EventsLoop {
let window_data = windows.get_mut(&WindowId(xev.event)).unwrap(); let window_data = windows.get_mut(&WindowId(xev.event)).unwrap();
(self.display.xlib.XUnsetICFocus)(window_data.ic); (self.display.xlib.XUnsetICFocus)(window_data.ic);
} }
callback(Event::WindowEvent { window_id: mkwid(xev.event), event: Focused(false) }) callback(Event::WindowEvent {
window_id: mkwid(xev.event),
event: Focused(false),
})
} }
ffi::XI_TouchBegin | ffi::XI_TouchUpdate | ffi::XI_TouchEnd => { ffi::XI_TouchBegin | ffi::XI_TouchUpdate | ffi::XI_TouchEnd => {
let xev: &ffi::XIDeviceEvent = unsafe { &*(xev.data as *const _) }; let xev: &ffi::XIDeviceEvent = unsafe { &*(xev.data as *const _) };
let wid = mkwid(xev.event); let window_id = mkwid(xev.event);
let phase = match xev.evtype { let phase = match xev.evtype {
ffi::XI_TouchBegin => TouchPhase::Started, ffi::XI_TouchBegin => TouchPhase::Started,
ffi::XI_TouchUpdate => TouchPhase::Moved, ffi::XI_TouchUpdate => TouchPhase::Moved,
ffi::XI_TouchEnd => TouchPhase::Ended, ffi::XI_TouchEnd => TouchPhase::Ended,
_ => unreachable!() _ => unreachable!()
}; };
callback(Event::WindowEvent { window_id: wid, event: WindowEvent::Touch(Touch { callback(Event::WindowEvent {
device_id: mkdid(xev.deviceid), window_id,
phase: phase, event: WindowEvent::Touch(Touch {
location: (xev.event_x, xev.event_y), device_id: mkdid(xev.deviceid),
id: xev.detail as u64, phase,
})}) location: (xev.event_x, xev.event_y),
id: xev.detail as u64,
},
)})
} }
ffi::XI_RawButtonPress | ffi::XI_RawButtonRelease => { ffi::XI_RawButtonPress | ffi::XI_RawButtonRelease => {
@ -758,7 +810,7 @@ impl EventsLoop {
ffi::XI_RawKeyRelease => Released, ffi::XI_RawKeyRelease => Released,
_ => unreachable!(), _ => unreachable!(),
}, },
modifiers: ::events::ModifiersState::default(), modifiers: ModifiersState::default(),
})}); })});
} }

View file

@ -1,10 +1,10 @@
use std::mem; use std::mem;
use std::ptr; use std::ptr;
use std::sync::Arc; use std::sync::Arc;
use std::os::raw::{c_char, c_double, c_int, c_long, c_short, c_uchar, c_uint, c_ulong};
use libc::{c_char, c_int, c_long, c_short, c_uchar, c_ulong};
use super::{ffi, XConnection, XError}; use super::{ffi, XConnection, XError};
use events::ModifiersState;
pub unsafe fn get_atom(xconn: &Arc<XConnection>, name: &[u8]) -> Result<ffi::Atom, XError> { pub unsafe fn get_atom(xconn: &Arc<XConnection>, name: &[u8]) -> Result<ffi::Atom, XError> {
let atom_name: *const c_char = name.as_ptr() as _; let atom_name: *const c_char = name.as_ptr() as _;
@ -117,3 +117,90 @@ pub unsafe fn get_property<T>(
Ok(data) Ok(data)
} }
impl From<ffi::XIModifierState> for ModifiersState {
fn from(mods: ffi::XIModifierState) -> Self {
let state = mods.effective as c_uint;
ModifiersState {
alt: state & ffi::Mod1Mask != 0,
shift: state & ffi::ShiftMask != 0,
ctrl: state & ffi::ControlMask != 0,
logo: state & ffi::Mod4Mask != 0,
}
}
}
#[derive(Debug)]
pub struct PointerState {
#[allow(dead_code)]
root: ffi::Window,
#[allow(dead_code)]
child: ffi::Window,
#[allow(dead_code)]
root_x: c_double,
#[allow(dead_code)]
root_y: c_double,
#[allow(dead_code)]
win_x: c_double,
#[allow(dead_code)]
win_y: c_double,
#[allow(dead_code)]
buttons: ffi::XIButtonState,
modifiers: ffi::XIModifierState,
#[allow(dead_code)]
group: ffi::XIGroupState,
#[allow(dead_code)]
relative_to_window: bool,
}
impl PointerState {
pub fn get_modifier_state(&self) -> ModifiersState {
self.modifiers.into()
}
}
pub unsafe fn query_pointer(
xconn: &Arc<XConnection>,
window: ffi::Window,
device_id: c_int,
) -> Result<PointerState, XError> {
let mut root_return = mem::uninitialized();
let mut child_return = mem::uninitialized();
let mut root_x_return = mem::uninitialized();
let mut root_y_return = mem::uninitialized();
let mut win_x_return = mem::uninitialized();
let mut win_y_return = mem::uninitialized();
let mut buttons_return = mem::uninitialized();
let mut modifiers_return = mem::uninitialized();
let mut group_return = mem::uninitialized();
let relative_to_window = (xconn.xinput2.XIQueryPointer)(
xconn.display,
device_id,
window,
&mut root_return,
&mut child_return,
&mut root_x_return,
&mut root_y_return,
&mut win_x_return,
&mut win_y_return,
&mut buttons_return,
&mut modifiers_return,
&mut group_return,
) == ffi::True;
xconn.check_errors()?;
Ok(PointerState {
root: root_return,
child: child_return,
root_x: root_x_return,
root_y: root_y_return,
win_x: win_x_return,
win_y: win_y_return,
buttons: buttons_return,
modifiers: modifiers_return,
group: group_return,
relative_to_window,
})
}