From e46001a448c71a837d8d4585706c083b7108cc64 Mon Sep 17 00:00:00 2001 From: Robbert van der Helm Date: Wed, 9 Feb 2022 17:30:50 +0100 Subject: [PATCH 1/2] Remove unused mouse click event Since this isn't emitted anywhere, this can only lead to confusion for library consumers. They'd need to implement their own click detection with the button up and button down events. --- src/event.rs | 11 ----------- 1 file changed, 11 deletions(-) diff --git a/src/event.rs b/src/event.rs index 23fedec..a398d7a 100644 --- a/src/event.rs +++ b/src/event.rs @@ -32,14 +32,6 @@ pub enum ScrollDelta { }, } -#[derive(Debug, Copy, Clone, PartialEq)] -pub struct MouseClick { - pub button: MouseButton, - pub click_count: usize, - /// The logical coordinates of the mouse position - pub position: Point, -} - #[derive(Debug, Clone, Copy, PartialEq)] pub enum MouseEvent { /// The mouse cursor was moved @@ -54,9 +46,6 @@ pub enum MouseEvent { /// A mouse button was released. ButtonReleased(MouseButton), - /// A mouse button was clicked. - Click(MouseClick), - /// The mouse wheel was scrolled. WheelScrolled(ScrollDelta), From 5b57af2463ef55c4ac702ef662c3472c328a3c6b Mon Sep 17 00:00:00 2001 From: Robbert van der Helm Date: Wed, 9 Feb 2022 18:20:54 +0100 Subject: [PATCH 2/2] Add the active modifiers to the mouse event This would solve the most important use case for #116. Only the Linux version has been tested, but the Windows should work perfectly fine, and I don't know anything about macOS programming but that version also compiles so it should be fine. --- src/event.rs | 29 +++++++++++++++++++++++++---- src/macos/view.rs | 44 +++++++++++++++++++++++++++++++++++++------- src/win/keyboard.rs | 40 ++++++++++++++++++++++++++++++++-------- src/win/window.rs | 39 +++++++++++++++++++++++++-------------- src/x11/keyboard.rs | 2 +- src/x11/window.rs | 33 +++++++++++++++++++++------------ 6 files changed, 141 insertions(+), 46 deletions(-) diff --git a/src/event.rs b/src/event.rs index a398d7a..3c9ba5b 100644 --- a/src/event.rs +++ b/src/event.rs @@ -1,4 +1,4 @@ -use keyboard_types::KeyboardEvent; +use keyboard_types::{KeyboardEvent, Modifiers}; use crate::{Point, WindowInfo}; @@ -38,21 +38,42 @@ pub enum MouseEvent { CursorMoved { /// The logical coordinates of the mouse position position: Point, + /// The modifiers that were held down just before the event. + modifiers: Modifiers, }, /// A mouse button was pressed. - ButtonPressed(MouseButton), + ButtonPressed { + /// The button that was pressed. + button: MouseButton, + /// The modifiers that were held down just before the event. + modifiers: Modifiers, + }, /// A mouse button was released. - ButtonReleased(MouseButton), + ButtonReleased { + /// The button that was released. + button: MouseButton, + /// The modifiers that were held down just before the event. + modifiers: Modifiers, + }, /// The mouse wheel was scrolled. - WheelScrolled(ScrollDelta), + WheelScrolled { + /// How much was scrolled, in factional lines. + delta: ScrollDelta, + /// The modifiers that were held down just before the event. + modifiers: Modifiers, + }, /// The mouse cursor entered the window. + /// + /// May not be available on all platforms. CursorEntered, /// The mouse cursor left the window. + /// + /// May not be available on all platforms. CursorLeft, } diff --git a/src/macos/view.rs b/src/macos/view.rs index de0e618..c39ba15 100644 --- a/src/macos/view.rs +++ b/src/macos/view.rs @@ -19,6 +19,7 @@ use crate::{ WindowOpenOptions, }; +use super::keyboard::make_modifiers; use super::window::WindowState; /// Name of the field used to store the `WindowState` pointer. @@ -42,6 +43,31 @@ macro_rules! add_simple_mouse_class_method { }; } +/// Similar to [add_simple_mouse_class_method!], but this creates its own event object for the +/// press/release event and adds the active modifier keys to that event. +macro_rules! add_mouse_button_class_method { + ($class:ident, $sel:ident, $event_ty:ident, $button:expr) => { + #[allow(non_snake_case)] + extern "C" fn $sel(this: &Object, _: Sel, event: id){ + let state: &mut WindowState = unsafe { + WindowState::from_field(this) + }; + + let modifiers = unsafe { NSEvent::modifierFlags(event) }; + + state.trigger_event(Event::Mouse($event_ty { + button: $button, + modifiers: make_modifiers(modifiers), + })); + } + + $class.add_method( + sel!($sel:), + $sel as extern "C" fn(&Object, Sel, id), + ); + }; +} + macro_rules! add_simple_keyboard_class_method { ($class:ident, $sel:ident) => { #[allow(non_snake_case)] @@ -126,12 +152,12 @@ unsafe fn create_view_class() -> &'static Class { view_did_change_backing_properties as extern "C" fn(&Object, Sel, id), ); - add_simple_mouse_class_method!(class, mouseDown, ButtonPressed(MouseButton::Left)); - add_simple_mouse_class_method!(class, mouseUp, ButtonReleased(MouseButton::Left)); - add_simple_mouse_class_method!(class, rightMouseDown, ButtonPressed(MouseButton::Right)); - add_simple_mouse_class_method!(class, rightMouseUp, ButtonReleased(MouseButton::Right)); - add_simple_mouse_class_method!(class, otherMouseDown, ButtonPressed(MouseButton::Middle)); - add_simple_mouse_class_method!(class, otherMouseUp, ButtonReleased(MouseButton::Middle)); + add_mouse_button_class_method!(class, mouseDown, ButtonPressed, MouseButton::Left); + add_mouse_button_class_method!(class, mouseUp, ButtonReleased, MouseButton::Left); + add_mouse_button_class_method!(class, rightMouseDown, ButtonPressed, MouseButton::Right); + add_mouse_button_class_method!(class, rightMouseUp, ButtonReleased, MouseButton::Right); + add_mouse_button_class_method!(class, otherMouseDown, ButtonPressed, MouseButton::Middle); + add_mouse_button_class_method!(class, otherMouseUp, ButtonReleased, MouseButton::Middle); add_simple_mouse_class_method!(class, mouseEntered, MouseEvent::CursorEntered); add_simple_mouse_class_method!(class, mouseExited, MouseEvent::CursorLeft); @@ -308,8 +334,12 @@ extern "C" fn mouse_moved(this: &Object, _sel: Sel, event: id) { msg_send![this, convertPoint:point fromView:nil] }; + let modifiers = unsafe { NSEvent::modifierFlags(event) }; let position = Point { x: point.x, y: point.y }; - state.trigger_event(Event::Mouse(MouseEvent::CursorMoved { position })); + state.trigger_event(Event::Mouse(MouseEvent::CursorMoved { + position, + modifiers: make_modifiers(modifiers), + })); } diff --git a/src/win/keyboard.rs b/src/win/keyboard.rs index e4045d1..ed696cc 100644 --- a/src/win/keyboard.rs +++ b/src/win/keyboard.rs @@ -29,14 +29,14 @@ use winapi::shared::ntdef::SHORT; use winapi::shared::windef::HWND; use winapi::um::winuser::{ GetKeyState, GetKeyboardLayout, MapVirtualKeyExW, PeekMessageW, ToUnicodeEx, MAPVK_VK_TO_CHAR, - MAPVK_VSC_TO_VK_EX, PM_NOREMOVE, VK_ACCEPT, VK_ADD, VK_APPS, VK_ATTN, VK_BACK, VK_BROWSER_BACK, - VK_BROWSER_FAVORITES, VK_BROWSER_FORWARD, VK_BROWSER_HOME, VK_BROWSER_REFRESH, - VK_BROWSER_SEARCH, VK_BROWSER_STOP, VK_CANCEL, VK_CAPITAL, VK_CLEAR, VK_CONTROL, VK_CONVERT, - VK_CRSEL, VK_DECIMAL, VK_DELETE, VK_DIVIDE, VK_DOWN, VK_END, VK_EREOF, VK_ESCAPE, VK_EXECUTE, - VK_EXSEL, VK_F1, VK_F10, VK_F11, VK_F12, VK_F2, VK_F3, VK_F4, VK_F5, VK_F6, VK_F7, VK_F8, - VK_F9, VK_FINAL, VK_HELP, VK_HOME, VK_INSERT, VK_JUNJA, VK_KANA, VK_KANJI, VK_LAUNCH_APP1, - VK_LAUNCH_APP2, VK_LAUNCH_MAIL, VK_LAUNCH_MEDIA_SELECT, VK_LCONTROL, VK_LEFT, VK_LMENU, - VK_LSHIFT, VK_LWIN, VK_MEDIA_NEXT_TRACK, VK_MEDIA_PLAY_PAUSE, VK_MEDIA_PREV_TRACK, + MAPVK_VSC_TO_VK_EX, MK_CONTROL, MK_SHIFT, PM_NOREMOVE, VK_ACCEPT, VK_ADD, VK_APPS, VK_ATTN, + VK_BACK, VK_BROWSER_BACK, VK_BROWSER_FAVORITES, VK_BROWSER_FORWARD, VK_BROWSER_HOME, + VK_BROWSER_REFRESH, VK_BROWSER_SEARCH, VK_BROWSER_STOP, VK_CANCEL, VK_CAPITAL, VK_CLEAR, + VK_CONTROL, VK_CONVERT, VK_CRSEL, VK_DECIMAL, VK_DELETE, VK_DIVIDE, VK_DOWN, VK_END, VK_EREOF, + VK_ESCAPE, VK_EXECUTE, VK_EXSEL, VK_F1, VK_F10, VK_F11, VK_F12, VK_F2, VK_F3, VK_F4, VK_F5, + VK_F6, VK_F7, VK_F8, VK_F9, VK_FINAL, VK_HELP, VK_HOME, VK_INSERT, VK_JUNJA, VK_KANA, VK_KANJI, + VK_LAUNCH_APP1, VK_LAUNCH_APP2, VK_LAUNCH_MAIL, VK_LAUNCH_MEDIA_SELECT, VK_LCONTROL, VK_LEFT, + VK_LMENU, VK_LSHIFT, VK_LWIN, VK_MEDIA_NEXT_TRACK, VK_MEDIA_PLAY_PAUSE, VK_MEDIA_PREV_TRACK, VK_MEDIA_STOP, VK_MENU, VK_MODECHANGE, VK_MULTIPLY, VK_NEXT, VK_NONCONVERT, VK_NUMLOCK, VK_NUMPAD0, VK_NUMPAD1, VK_NUMPAD2, VK_NUMPAD3, VK_NUMPAD4, VK_NUMPAD5, VK_NUMPAD6, VK_NUMPAD7, VK_NUMPAD8, VK_NUMPAD9, VK_OEM_ATTN, VK_OEM_CLEAR, VK_PAUSE, VK_PLAY, VK_PRINT, VK_PRIOR, @@ -562,6 +562,30 @@ impl KeyboardState { } } + /// The same as [Self::get_modifiers()], but it reads the Ctrl and Shift state from a mouse + /// event's wParam parameter. Saves two calls to [GetKeyState()]. + pub(crate) fn get_modifiers_from_mouse_wparam(&self, wparam: WPARAM) -> Modifiers { + unsafe { + let mut modifiers = Modifiers::empty(); + for &(vk, modifier, mask) in MODIFIER_MAP { + let modifier_active = match modifier { + Modifiers::CONTROL => wparam & MK_CONTROL != 0, + Modifiers::SHIFT => wparam & MK_SHIFT != 0, + _ => GetKeyState(vk) & mask != 0, + }; + + if modifier_active { + modifiers |= modifier; + } + } + if self.has_altgr && GetKeyState(VK_RMENU) & 0x80 != 0 { + modifiers |= Modifiers::ALT_GRAPH; + modifiers &= !(Modifiers::CONTROL | Modifiers::ALT); + } + modifiers + } + } + /// Load a keyboard layout. /// /// We need to retain a map of virtual key codes in various modifier diff --git a/src/win/window.rs b/src/win/window.rs index 2f4f056..20aa482 100644 --- a/src/win/window.rs +++ b/src/win/window.rs @@ -137,13 +137,14 @@ unsafe extern "system" fn wnd_proc( let y = ((lparam >> 16) & 0xFFFF) as i16 as i32; let physical_pos = PhyPoint { x, y }; - let logical_pos = physical_pos.to_logical(&window_state.window_info); + let event = Event::Mouse(MouseEvent::CursorMoved { + position: logical_pos, + modifiers: window_state.keyboard_state.get_modifiers_from_mouse_wparam(wparam), + }); + + window_state.handler.on_event(&mut window, event); - window_state.handler.on_event( - &mut window, - Event::Mouse(MouseEvent::CursorMoved { position: logical_pos }), - ); return 0; } WM_MOUSEWHEEL => { @@ -155,13 +156,13 @@ unsafe extern "system" fn wnd_proc( let value = value as i32; let value = value as f32 / WHEEL_DELTA as f32; - window_state.handler.on_event( - &mut window, - Event::Mouse(MouseEvent::WheelScrolled(ScrollDelta::Lines { - x: 0.0, - y: value, - })), - ); + let event = Event::Mouse(MouseEvent::WheelScrolled { + delta: ScrollDelta::Lines { x: 0.0, y: value }, + modifiers: window_state.keyboard_state.get_modifiers_from_mouse_wparam(wparam), + }); + + window_state.handler.on_event(&mut window, event); + return 0; } WM_LBUTTONDOWN | WM_LBUTTONUP | WM_MBUTTONDOWN | WM_MBUTTONUP | WM_RBUTTONDOWN @@ -190,7 +191,12 @@ unsafe extern "system" fn wnd_proc( // Capture the mouse cursor on button down mouse_button_counter = mouse_button_counter.saturating_add(1); SetCapture(hwnd); - MouseEvent::ButtonPressed(button) + MouseEvent::ButtonPressed { + button, + modifiers: window_state + .keyboard_state + .get_modifiers_from_mouse_wparam(wparam), + } } WM_LBUTTONUP | WM_MBUTTONUP | WM_RBUTTONUP | WM_XBUTTONUP => { // Release the mouse cursor capture when all buttons are released @@ -199,7 +205,12 @@ unsafe extern "system" fn wnd_proc( ReleaseCapture(); } - MouseEvent::ButtonReleased(button) + MouseEvent::ButtonReleased { + button, + modifiers: window_state + .keyboard_state + .get_modifiers_from_mouse_wparam(wparam), + } } _ => { unreachable!() diff --git a/src/x11/keyboard.rs b/src/x11/keyboard.rs index 3ac64f2..3220128 100644 --- a/src/x11/keyboard.rs +++ b/src/x11/keyboard.rs @@ -362,7 +362,7 @@ fn hardware_keycode_to_code(hw_keycode: u16) -> Code { // Extracts the keyboard modifiers from, e.g., the `state` field of // `xcb::xproto::ButtonPressEvent` -fn key_mods(mods: u16) -> Modifiers { +pub(super) fn key_mods(mods: u16) -> Modifiers { let mut ret = Modifiers::default(); let mut key_masks = [ (xproto::MOD_MASK_SHIFT, Modifiers::SHIFT), diff --git a/src/x11/window.rs b/src/x11/window.rs index bfe9567..f528de9 100644 --- a/src/x11/window.rs +++ b/src/x11/window.rs @@ -16,7 +16,7 @@ use crate::{ WindowHandler, WindowInfo, WindowOpenOptions, WindowScalePolicy, }; -use super::keyboard::{convert_key_press_event, convert_key_release_event}; +use super::keyboard::{convert_key_press_event, convert_key_release_event, key_mods}; #[cfg(feature = "opengl")] use crate::{ @@ -580,7 +580,10 @@ impl Window { handler.on_event( &mut crate::Window::new(self), - Event::Mouse(MouseEvent::CursorMoved { position: logical_pos }), + Event::Mouse(MouseEvent::CursorMoved { + position: logical_pos, + modifiers: key_mods(event.state()), + }), ); } } @@ -593,26 +596,29 @@ impl Window { 4 => { handler.on_event( &mut crate::Window::new(self), - Event::Mouse(MouseEvent::WheelScrolled(ScrollDelta::Lines { - x: 0.0, - y: 1.0, - })), + Event::Mouse(MouseEvent::WheelScrolled { + delta: ScrollDelta::Lines { x: 0.0, y: 1.0 }, + modifiers: key_mods(event.state()), + }), ); } 5 => { handler.on_event( &mut crate::Window::new(self), - Event::Mouse(MouseEvent::WheelScrolled(ScrollDelta::Lines { - x: 0.0, - y: -1.0, - })), + Event::Mouse(MouseEvent::WheelScrolled { + delta: ScrollDelta::Lines { x: 0.0, y: -1.0 }, + modifiers: key_mods(event.state()), + }), ); } detail => { let button_id = mouse_id(detail); handler.on_event( &mut crate::Window::new(self), - Event::Mouse(MouseEvent::ButtonPressed(button_id)), + Event::Mouse(MouseEvent::ButtonPressed { + button: button_id, + modifiers: key_mods(event.state()), + }), ); } } @@ -626,7 +632,10 @@ impl Window { let button_id = mouse_id(detail); handler.on_event( &mut crate::Window::new(self), - Event::Mouse(MouseEvent::ButtonReleased(button_id)), + Event::Mouse(MouseEvent::ButtonReleased { + button: button_id, + modifiers: key_mods(event.state()), + }), ); } }