mirror of
https://github.com/italicsjenga/winit-sonoma-fix.git
synced 2024-12-25 23:01:30 +11:00
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:
parent
198d9ff230
commit
124b16fcde
|
@ -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(),
|
||||||
})});
|
})});
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -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,
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
Loading…
Reference in a new issue