From 011720848a43c52e7f0444302494c7b3ce39033c Mon Sep 17 00:00:00 2001 From: Bryan Gilbert Date: Tue, 26 Dec 2017 16:46:28 -0500 Subject: [PATCH] Added modifier support to mouse events (#328) --- CHANGELOG.md | 1 + src/events.rs | 6 ++- src/platform/emscripten/mod.rs | 13 ++++- src/platform/linux/wayland/pointer.rs | 15 +++++- src/platform/linux/x11/mod.rs | 74 ++++++++++++++++++++++++--- src/platform/macos/events_loop.rs | 18 +++---- src/platform/windows/events_loop.rs | 22 ++++---- 7 files changed, 116 insertions(+), 33 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index b2e57ae8..b04835f4 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -4,6 +4,7 @@ - Added support for `DroppedFile`, `HoveredFile`, and `HoveredFileCancelled` to X11 backend. - **Breaking:** `unix::WindowExt` no longer returns pointers for things that aren't actually pointers; `get_xlib_window` now returns `Option` and `get_xlib_screen_id` returns `Option`. Additionally, methods that previously returned `libc::c_void` have been changed to return `std::os::raw::c_void`, which are not interchangeable types, so users wanting the former will need to explicitly cast. - Added `set_decorations` method to `Window` to allow decorations to be toggled after the window is built. Presently only implemented on X11. +- Added `modifiers` field to `MouseInput`, `MouseWheel`, and `CursorMoved` events to track the modifiers state (`ModifiersState`). # Version 0.9.0 (2017-12-01) diff --git a/src/events.rs b/src/events.rs index 8845e3e1..2947db17 100644 --- a/src/events.rs +++ b/src/events.rs @@ -61,6 +61,7 @@ pub enum WindowEvent { /// limited by the display area and it may have been transformed by the OS to implement effects such as cursor /// acceleration, it should not be used to implement non-cursor-like interactions such as 3D camera control. position: (f64, f64), + modifiers: ModifiersState }, /// The cursor has entered the window. @@ -70,10 +71,11 @@ pub enum WindowEvent { CursorLeft { device_id: DeviceId }, /// A mouse wheel movement or touchpad scroll occurred. - MouseWheel { device_id: DeviceId, delta: MouseScrollDelta, phase: TouchPhase }, + MouseWheel { device_id: DeviceId, delta: MouseScrollDelta, phase: TouchPhase, modifiers: ModifiersState }, /// An mouse button press has been received. - MouseInput { device_id: DeviceId, state: ElementState, button: MouseButton }, + MouseInput { device_id: DeviceId, state: ElementState, button: MouseButton, modifiers: ModifiersState }, + /// Touchpad pressure event. /// diff --git a/src/platform/emscripten/mod.rs b/src/platform/emscripten/mod.rs index 5b06417b..41e9045d 100644 --- a/src/platform/emscripten/mod.rs +++ b/src/platform/emscripten/mod.rs @@ -178,6 +178,13 @@ extern "C" fn mouse_callback( unsafe { let queue: &RefCell> = mem::transmute(event_queue); + let modifiers = ::ModifiersState { + shift: (*event).shiftKey == ffi::EM_TRUE, + ctrl: (*event).ctrlKey == ffi::EM_TRUE, + alt: (*event).altKey == ffi::EM_TRUE, + logo: (*event).metaKey == ffi::EM_TRUE, + }; + match event_type { ffi::EMSCRIPTEN_EVENT_MOUSEMOVE => { queue.borrow_mut().push_back(::Event::WindowEvent { @@ -185,6 +192,7 @@ extern "C" fn mouse_callback( event: ::WindowEvent::CursorMoved { device_id: ::DeviceId(DeviceId), position: ((*event).canvasX as f64, (*event).canvasY as f64), + modifiers: modifiers, } }); queue.borrow_mut().push_back(::Event::DeviceEvent { @@ -211,8 +219,9 @@ extern "C" fn mouse_callback( window_id: ::WindowId(WindowId(0)), event: ::WindowEvent::MouseInput { device_id: ::DeviceId(DeviceId), - state, - button, + state: state, + button: button, + modifiers: modifiers, } }) }, diff --git a/src/platform/linux/wayland/pointer.rs b/src/platform/linux/wayland/pointer.rs index 47b712a2..ba1ae3e6 100644 --- a/src/platform/linux/wayland/pointer.rs +++ b/src/platform/linux/wayland/pointer.rs @@ -1,6 +1,7 @@ use std::sync::{Arc, Mutex}; use {WindowEvent as Event, ElementState, MouseButton, MouseScrollDelta, TouchPhase}; +use events::ModifiersState; use super::{WindowId, DeviceId}; use super::event_loop::EventsLoopSink; @@ -50,6 +51,8 @@ pub fn pointer_implementation() -> wl_pointer::Implementation { Event::CursorMoved { device_id: ::DeviceId(::platform::DeviceId::Wayland(DeviceId)), position: (x, y), + // TODO: replace dummy value with actual modifier state + modifiers: ModifiersState::default(), }, wid, ); @@ -73,7 +76,9 @@ pub fn pointer_implementation() -> wl_pointer::Implementation { idata.sink.lock().unwrap().send_event( Event::CursorMoved { device_id: ::DeviceId(::platform::DeviceId::Wayland(DeviceId)), - position: (x, y) + position: (x, y), + // TODO: replace dummy value with actual modifier state + modifiers: ModifiersState::default(), }, wid ); @@ -97,6 +102,8 @@ pub fn pointer_implementation() -> wl_pointer::Implementation { device_id: ::DeviceId(::platform::DeviceId::Wayland(DeviceId)), state: state, button: button, + // TODO: replace dummy value with actual modifier state + modifiers: ModifiersState::default(), }, wid ); @@ -117,6 +124,8 @@ pub fn pointer_implementation() -> wl_pointer::Implementation { device_id: ::DeviceId(::platform::DeviceId::Wayland(DeviceId)), delta: MouseScrollDelta::PixelDelta(x as f32, y as f32), phase: TouchPhase::Moved, + // TODO: replace dummy value with actual modifier state + modifiers: ModifiersState::default(), }, wid ); @@ -145,6 +154,8 @@ pub fn pointer_implementation() -> wl_pointer::Implementation { device_id: ::DeviceId(::platform::DeviceId::Wayland(DeviceId)), delta: MouseScrollDelta::LineDelta(x as f32, y as f32), phase: idata.axis_state, + // TODO: replace dummy value with actual modifier state + modifiers: ModifiersState::default(), }, wid ); @@ -154,6 +165,8 @@ pub fn pointer_implementation() -> wl_pointer::Implementation { device_id: ::DeviceId(::platform::DeviceId::Wayland(DeviceId)), delta: MouseScrollDelta::PixelDelta(x as f32, y as f32), phase: idata.axis_state, + // TODO: replace dummy value with actual modifier state + modifiers: ModifiersState::default(), }, wid ); diff --git a/src/platform/linux/x11/mod.rs b/src/platform/linux/x11/mod.rs index 80833432..ed108d22 100644 --- a/src/platform/linux/x11/mod.rs +++ b/src/platform/linux/x11/mod.rs @@ -471,6 +471,8 @@ impl EventsLoop { match xev.evtype { ffi::XI_ButtonPress | ffi::XI_ButtonRelease => { + use events::ModifiersState; + let xev: &ffi::XIDeviceEvent = unsafe { &*(xev.data as *const _) }; let wid = mkwid(xev.event); let did = mkdid(xev.deviceid); @@ -478,6 +480,18 @@ impl EventsLoop { // Deliver multi-touch events instead of emulated mouse events. return; } + + let ev_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 { Pressed } else { @@ -485,11 +499,11 @@ impl EventsLoop { }; match xev.detail as u32 { ffi::Button1 => callback(Event::WindowEvent { window_id: wid, event: - MouseInput { device_id: did, state: state, button: Left } }), + MouseInput { device_id: did, state: state, button: Left, modifiers: ev_mods } }), ffi::Button2 => callback(Event::WindowEvent { window_id: wid, event: - MouseInput { device_id: did, state: state, button: Middle } }), + MouseInput { device_id: did, state: state, button: Middle, modifiers: ev_mods} }), ffi::Button3 => callback(Event::WindowEvent { window_id: wid, event: - MouseInput { device_id: did, state: state, button: Right } }), + MouseInput { device_id: did, state: state, button: Right, modifiers: ev_mods } }), // 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 @@ -505,18 +519,33 @@ impl EventsLoop { _ => unreachable!() }, phase: TouchPhase::Moved, + modifiers: ev_mods, }}); }, - x => callback(Event::WindowEvent { window_id: wid, event: MouseInput { device_id: did, state: state, button: Other(x as u8) } }) + x => callback(Event::WindowEvent { window_id: wid, + event: MouseInput { device_id: did, state: state, button: Other(x as u8), modifiers: ev_mods } }) } } ffi::XI_Motion => { + use events::ModifiersState; + let xev: &ffi::XIDeviceEvent = unsafe { &*(xev.data as *const _) }; let did = mkdid(xev.deviceid); let wid = mkwid(xev.event); let new_cursor_pos = (xev.event_x, xev.event_y); - + + let ev_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 if { let mut windows = self.windows.lock().unwrap(); @@ -528,7 +557,8 @@ impl EventsLoop { } { callback(Event::WindowEvent { window_id: wid, event: CursorMoved { device_id: did, - position: new_cursor_pos + position: new_cursor_pos, + modifiers: ev_mods, }}); } @@ -554,6 +584,7 @@ impl EventsLoop { ScrollOrientation::Vertical => LineDelta(0.0, -delta as f32), }, phase: TouchPhase::Moved, + modifiers: ev_mods, }}); } else { events.push(Event::WindowEvent { window_id: wid, event: AxisMotion { @@ -572,8 +603,20 @@ impl EventsLoop { } ffi::XI_Enter => { + use events::ModifiersState; let xev: &ffi::XIEnterEvent = unsafe { &*(xev.data as *const _) }; + let ev_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, + } + }; + let mut devices = self.devices.lock().unwrap(); let physical_device = devices.get_mut(&DeviceId(xev.sourceid)).unwrap(); for info in DeviceInfo::get(&self.display, ffi::XIAllDevices).iter() { @@ -586,7 +629,8 @@ impl EventsLoop { let new_cursor_pos = (xev.event_x, xev.event_y); callback(Event::WindowEvent { window_id: wid, event: CursorMoved { device_id: mkdid(xev.deviceid), - position: new_cursor_pos + position: new_cursor_pos, + modifiers: ev_mods, }}) } ffi::XI_Leave => { @@ -594,7 +638,20 @@ impl EventsLoop { callback(Event::WindowEvent { window_id: mkwid(xev.event), event: CursorLeft { device_id: mkdid(xev.deviceid) } }) } ffi::XI_FocusIn => { + use events::ModifiersState; let xev: &ffi::XIFocusInEvent = unsafe { &*(xev.data as *const _) }; + + let ev_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, + } + }; + unsafe { let mut windows = self.windows.lock().unwrap(); let window_data = windows.get_mut(&WindowId(xev.event)).unwrap(); @@ -605,7 +662,8 @@ impl EventsLoop { let new_cursor_pos = (xev.event_x, xev.event_y); callback(Event::WindowEvent { window_id: wid, event: CursorMoved { device_id: mkdid(xev.deviceid), - position: new_cursor_pos + position: new_cursor_pos, + modifiers: ev_mods, }}) } ffi::XI_FocusOut => { diff --git a/src/platform/macos/events_loop.rs b/src/platform/macos/events_loop.rs index 7be58c81..dc3df4fd 100644 --- a/src/platform/macos/events_loop.rs +++ b/src/platform/macos/events_loop.rs @@ -435,12 +435,12 @@ impl EventsLoop { event }, - appkit::NSLeftMouseDown => { Some(into_event(WindowEvent::MouseInput { device_id: DEVICE_ID, state: ElementState::Pressed, button: MouseButton::Left })) }, - appkit::NSLeftMouseUp => { Some(into_event(WindowEvent::MouseInput { device_id: DEVICE_ID, state: ElementState::Released, button: MouseButton::Left })) }, - appkit::NSRightMouseDown => { Some(into_event(WindowEvent::MouseInput { device_id: DEVICE_ID, state: ElementState::Pressed, button: MouseButton::Right })) }, - appkit::NSRightMouseUp => { Some(into_event(WindowEvent::MouseInput { device_id: DEVICE_ID, state: ElementState::Released, button: MouseButton::Right })) }, - appkit::NSOtherMouseDown => { Some(into_event(WindowEvent::MouseInput { device_id: DEVICE_ID, state: ElementState::Pressed, button: MouseButton::Middle })) }, - appkit::NSOtherMouseUp => { Some(into_event(WindowEvent::MouseInput { device_id: DEVICE_ID, state: ElementState::Released, button: MouseButton::Middle })) }, + appkit::NSLeftMouseDown => { Some(into_event(WindowEvent::MouseInput { device_id: DEVICE_ID, state: ElementState::Pressed, button: MouseButton::Left, modifiers: event_mods(ns_event) })) }, + appkit::NSLeftMouseUp => { Some(into_event(WindowEvent::MouseInput { device_id: DEVICE_ID, state: ElementState::Released, button: MouseButton::Left, modifiers: event_mods(ns_event) })) }, + appkit::NSRightMouseDown => { Some(into_event(WindowEvent::MouseInput { device_id: DEVICE_ID, state: ElementState::Pressed, button: MouseButton::Right, modifiers: event_mods(ns_event) })) }, + appkit::NSRightMouseUp => { Some(into_event(WindowEvent::MouseInput { device_id: DEVICE_ID, state: ElementState::Released, button: MouseButton::Right, modifiers: event_mods(ns_event) })) }, + appkit::NSOtherMouseDown => { Some(into_event(WindowEvent::MouseInput { device_id: DEVICE_ID, state: ElementState::Pressed, button: MouseButton::Middle, modifiers: event_mods(ns_event) })) }, + appkit::NSOtherMouseUp => { Some(into_event(WindowEvent::MouseInput { device_id: DEVICE_ID, state: ElementState::Released, button: MouseButton::Middle, modifiers: event_mods(ns_event) })) }, appkit::NSMouseEntered => { let window = match maybe_window.or_else(maybe_key_window) { @@ -462,7 +462,7 @@ impl EventsLoop { let x = (scale_factor * view_point.x as f32) as f64; let y = (scale_factor * (view_rect.size.height - view_point.y) as f32) as f64; - let window_event = WindowEvent::CursorMoved { device_id: DEVICE_ID, position: (x, y) }; + let window_event = WindowEvent::CursorMoved { device_id: DEVICE_ID, position: (x, y), modifiers: event_mods(ns_event) }; let event = Event::WindowEvent { window_id: ::WindowId(window.id()), event: window_event }; self.shared.pending_events.lock().unwrap().push_back(event); @@ -499,7 +499,7 @@ impl EventsLoop { { let x = (scale_factor * view_point.x as f32) as f64; let y = (scale_factor * (view_rect.size.height - view_point.y) as f32) as f64; - let window_event = WindowEvent::CursorMoved { device_id: DEVICE_ID, position: (x, y) }; + let window_event = WindowEvent::CursorMoved { device_id: DEVICE_ID, position: (x, y), modifiers: event_mods(ns_event) }; let event = Event::WindowEvent { window_id: ::WindowId(window.id()), event: window_event }; events.push_back(event); } @@ -562,7 +562,7 @@ impl EventsLoop { }, } }); - let window_event = WindowEvent::MouseWheel { device_id: DEVICE_ID, delta, phase }; + let window_event = WindowEvent::MouseWheel { device_id: DEVICE_ID, delta: delta, phase: phase, modifiers: event_mods(ns_event) }; Some(into_event(window_event)) }, diff --git a/src/platform/windows/events_loop.rs b/src/platform/windows/events_loop.rs index 04849f79..4eb9290d 100644 --- a/src/platform/windows/events_loop.rs +++ b/src/platform/windows/events_loop.rs @@ -442,7 +442,7 @@ pub unsafe extern "system" fn callback(window: HWND, msg: UINT, send_event(Event::WindowEvent { window_id: SuperWindowId(WindowId(window)), - event: CursorMoved { device_id: DEVICE_ID, position: (x, y) }, + event: CursorMoved { device_id: DEVICE_ID, position: (x, y), modifiers: event::get_key_mods() }, }); 0 @@ -486,7 +486,7 @@ pub unsafe extern "system" fn callback(window: HWND, msg: UINT, send_event(Event::WindowEvent { window_id: SuperWindowId(WindowId(window)), - event: WindowEvent::MouseWheel { device_id: DEVICE_ID, delta: LineDelta(0.0, value), phase: TouchPhase::Moved }, + event: WindowEvent::MouseWheel { device_id: DEVICE_ID, delta: LineDelta(0.0, value), phase: TouchPhase::Moved, modifiers: event::get_key_mods() }, }); send_event(Event::DeviceEvent { @@ -552,7 +552,7 @@ pub unsafe extern "system" fn callback(window: HWND, msg: UINT, use events::ElementState::Pressed; send_event(Event::WindowEvent { window_id: SuperWindowId(WindowId(window)), - event: MouseInput { device_id: DEVICE_ID, state: Pressed, button: Left } + event: MouseInput { device_id: DEVICE_ID, state: Pressed, button: Left, modifiers: event::get_key_mods() } }); 0 }, @@ -563,7 +563,7 @@ pub unsafe extern "system" fn callback(window: HWND, msg: UINT, use events::ElementState::Released; send_event(Event::WindowEvent { window_id: SuperWindowId(WindowId(window)), - event: MouseInput { device_id: DEVICE_ID, state: Released, button: Left } + event: MouseInput { device_id: DEVICE_ID, state: Released, button: Left, modifiers: event::get_key_mods() } }); 0 }, @@ -574,7 +574,7 @@ pub unsafe extern "system" fn callback(window: HWND, msg: UINT, use events::ElementState::Pressed; send_event(Event::WindowEvent { window_id: SuperWindowId(WindowId(window)), - event: MouseInput { device_id: DEVICE_ID, state: Pressed, button: Right } + event: MouseInput { device_id: DEVICE_ID, state: Pressed, button: Right, modifiers: event::get_key_mods() } }); 0 }, @@ -585,7 +585,7 @@ pub unsafe extern "system" fn callback(window: HWND, msg: UINT, use events::ElementState::Released; send_event(Event::WindowEvent { window_id: SuperWindowId(WindowId(window)), - event: MouseInput { device_id: DEVICE_ID, state: Released, button: Right } + event: MouseInput { device_id: DEVICE_ID, state: Released, button: Right, modifiers: event::get_key_mods() } }); 0 }, @@ -596,7 +596,7 @@ pub unsafe extern "system" fn callback(window: HWND, msg: UINT, use events::ElementState::Pressed; send_event(Event::WindowEvent { window_id: SuperWindowId(WindowId(window)), - event: MouseInput { device_id: DEVICE_ID, state: Pressed, button: Middle } + event: MouseInput { device_id: DEVICE_ID, state: Pressed, button: Middle, modifiers: event::get_key_mods() } }); 0 }, @@ -607,7 +607,7 @@ pub unsafe extern "system" fn callback(window: HWND, msg: UINT, use events::ElementState::Released; send_event(Event::WindowEvent { window_id: SuperWindowId(WindowId(window)), - event: MouseInput { device_id: DEVICE_ID, state: Released, button: Middle } + event: MouseInput { device_id: DEVICE_ID, state: Released, button: Middle, modifiers: event::get_key_mods() } }); 0 }, @@ -619,7 +619,7 @@ pub unsafe extern "system" fn callback(window: HWND, msg: UINT, let xbutton = winuser::GET_XBUTTON_WPARAM(wparam); send_event(Event::WindowEvent { window_id: SuperWindowId(WindowId(window)), - event: MouseInput { device_id: DEVICE_ID, state: Pressed, button: Other(xbutton as u8) } + event: MouseInput { device_id: DEVICE_ID, state: Pressed, button: Other(xbutton as u8), modifiers: event::get_key_mods() } }); 0 }, @@ -631,7 +631,7 @@ pub unsafe extern "system" fn callback(window: HWND, msg: UINT, let xbutton = winuser::GET_XBUTTON_WPARAM(wparam); send_event(Event::WindowEvent { window_id: SuperWindowId(WindowId(window)), - event: MouseInput { device_id: DEVICE_ID, state: Released, button: Other(xbutton as u8) } + event: MouseInput { device_id: DEVICE_ID, state: Released, button: Other(xbutton as u8), modifiers: event::get_key_mods() } }); 0 }, @@ -690,7 +690,7 @@ pub unsafe extern "system" fn callback(window: HWND, msg: UINT, send_event(Event::WindowEvent { window_id: SuperWindowId(WindowId(window)), - event: CursorMoved { device_id: DEVICE_ID, position: (x, y) }, + event: CursorMoved { device_id: DEVICE_ID, position: (x, y), modifiers: event::get_key_mods() }, }); 0 },