diff --git a/src/event.rs b/src/event.rs index a398d7af..3c9ba5bc 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 de0e6182..c39ba154 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 e4045d15..ed696ccd 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 163f3d7a..d55e3f4d 100644 --- a/src/win/window.rs +++ b/src/win/window.rs @@ -140,13 +140,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 => { @@ -158,13 +159,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 @@ -193,7 +194,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 @@ -202,7 +208,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 3ac64f2a..32201282 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 62fb9eec..43548b3e 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()), + }), ); } }