diff --git a/winit-appkit/src/app.rs b/winit-appkit/src/app.rs index 25cd38392a..019aa0aa06 100644 --- a/winit-appkit/src/app.rs +++ b/winit-appkit/src/app.rs @@ -9,9 +9,10 @@ use objc2::runtime::{Imp, Sel}; use objc2::sel; use objc2_app_kit::{NSApplication, NSEvent, NSEventModifierFlags, NSEventType}; use objc2_foundation::MainThreadMarker; -use winit_core::event::{DeviceEvent, ElementState}; +use winit_core::event::{ButtonSource, DeviceEvent, ElementState}; use super::app_state::AppState; +use super::view::mouse_button; type SendEvent = extern "C-unwind" fn(&NSApplication, Sel, &NSEvent); @@ -117,19 +118,19 @@ fn maybe_dispatch_device_event(app_state: &Rc, event: &NSEvent) { } }, NSEventType::LeftMouseDown | NSEventType::RightMouseDown | NSEventType::OtherMouseDown => { - let button = event.buttonNumber() as u32; + let source = ButtonSource::Mouse(mouse_button(event)); app_state.maybe_queue_with_handler(move |app, event_loop| { - app.device_event(event_loop, None, DeviceEvent::Button { - button, + app.device_event(event_loop, None, DeviceEvent::PointerButton { + button: source, state: ElementState::Pressed, }); }); }, NSEventType::LeftMouseUp | NSEventType::RightMouseUp | NSEventType::OtherMouseUp => { - let button = event.buttonNumber() as u32; + let source = ButtonSource::Mouse(mouse_button(event)); app_state.maybe_queue_with_handler(move |app, event_loop| { - app.device_event(event_loop, None, DeviceEvent::Button { - button, + app.device_event(event_loop, None, DeviceEvent::PointerButton { + button: source, state: ElementState::Released, }); }); diff --git a/winit-appkit/src/view.rs b/winit-appkit/src/view.rs index 6fce2bdbff..9d408e9da1 100644 --- a/winit-appkit/src/view.rs +++ b/winit-appkit/src/view.rs @@ -1089,7 +1089,7 @@ impl WinitView { } /// Get the mouse button from the NSEvent. -fn mouse_button(event: &NSEvent) -> MouseButton { +pub fn mouse_button(event: &NSEvent) -> MouseButton { // The buttonNumber property only makes sense for the mouse events: // NSLeftMouse.../NSRightMouse.../NSOtherMouse... // For the other events, it's always set to 0. diff --git a/winit-core/src/event.rs b/winit-core/src/event.rs index 24a80990bc..04b7746fca 100644 --- a/winit-core/src/event.rs +++ b/winit-core/src/event.rs @@ -510,7 +510,8 @@ impl From for PointerKind { } } -/// Represents the pointer type of a [`WindowEvent::PointerButton`]. +/// Represents the pointer type of [`WindowEvent::PointerButton`] and +/// [`DeviceEvent::PointerButton`]. /// /// **Wayland/X11:** [`Unknown`](Self::Unknown) device types are converted to known variants by the /// system. @@ -615,7 +616,7 @@ impl FingerId { /// Note that these events are delivered regardless of input focus. /// /// [window events]: WindowEvent -#[derive(Clone, Copy, Debug, PartialEq)] +#[derive(Clone, Debug, PartialEq)] pub enum DeviceEvent { /// Change in physical position of a pointing device. /// @@ -640,8 +641,8 @@ pub enum DeviceEvent { delta: MouseScrollDelta, }, - Button { - button: ButtonId, + PointerButton { + button: ButtonSource, state: ElementState, }, @@ -1047,9 +1048,6 @@ impl Force { /// Identifier for a specific analog axis on some device. pub type AxisId = u32; -/// Identifier for a specific button on some device. -pub type ButtonId = u32; - /// Tablet of the tablet tool. #[derive(Default, Clone, Copy, Debug, PartialEq, Eq, Hash)] #[cfg_attr(feature = "serde", derive(Deserialize, Serialize))] @@ -1621,7 +1619,7 @@ mod tests { with_device_event(PointerMotion { delta: (0.0, 0.0).into() }); with_device_event(MouseWheel { delta: event::MouseScrollDelta::LineDelta(0.0, 0.0) }); - with_device_event(Button { button: 0, state: event::ElementState::Pressed }); + with_device_event(PointerButton { button: event::ButtonSource::Unknown(0), state: event::ElementState::Pressed }); }}; } diff --git a/winit-web/src/event_loop/runner.rs b/winit-web/src/event_loop/runner.rs index 74bddb7e23..3210ad10fe 100644 --- a/winit-web/src/event_loop/runner.rs +++ b/winit-web/src/event_loop/runner.rs @@ -12,7 +12,8 @@ use web_sys::{Document, KeyboardEvent, Navigator, PageTransitionEvent, PointerEv use web_time::{Duration, Instant}; use winit_core::application::ApplicationHandler; use winit_core::event::{ - DeviceEvent, DeviceId, ElementState, RawKeyEvent, StartCause, WindowEvent, + ButtonSource, DeviceEvent, DeviceId, ElementState, PointerSource, RawKeyEvent, StartCause, + WindowEvent, }; use winit_core::event_loop::{ControlFlow, DeviceEvents}; use winit_core::window::WindowId; @@ -315,9 +316,10 @@ impl Shared { return; } - // chorded button event - let device_id = event::mkdid(event.pointer_id()); + let pointer_id = event.pointer_id(); + let device_id = event::mkdid(pointer_id); + // chorded button event if let Some(button) = backend::event::raw_button(&event) { let state = if backend::event::pointer_buttons(&event) .contains(ButtonsState::from_bits_retain(button)) @@ -327,9 +329,23 @@ impl Shared { ElementState::Released }; + let kind = backend::event::pointer_kind(&event, pointer_id); + let source = match backend::event::pointer_source(&event, kind) { + PointerSource::Mouse => backend::event::mouse_button(button), + PointerSource::Touch { finger_id, force } => { + ButtonSource::Touch { finger_id, force } + }, + PointerSource::TabletTool { kind, data } => ButtonSource::TabletTool { + kind, + button: backend::event::tool_button(button), + data, + }, + PointerSource::Unknown => ButtonSource::Unknown(button), + }; + runner.send_event(Event::DeviceEvent { device_id, - event: DeviceEvent::Button { button: button.into(), state }, + event: DeviceEvent::PointerButton { button: source, state }, }); return; @@ -376,11 +392,26 @@ impl Shared { return; } + let pointer_id = event.pointer_id(); + let kind = backend::event::pointer_kind(&event, pointer_id); let button = backend::event::raw_button(&event).expect("no pointer button pressed"); + let source = match backend::event::pointer_source(&event, kind) { + PointerSource::Mouse => backend::event::mouse_button(button), + PointerSource::Touch { finger_id, force } => { + ButtonSource::Touch { finger_id, force } + }, + PointerSource::TabletTool { kind, data } => ButtonSource::TabletTool { + kind, + button: backend::event::tool_button(button), + data, + }, + PointerSource::Unknown => ButtonSource::Unknown(button), + }; + runner.send_event(Event::DeviceEvent { - device_id: event::mkdid(event.pointer_id()), - event: DeviceEvent::Button { - button: button.into(), + device_id: event::mkdid(pointer_id), + event: DeviceEvent::PointerButton { + button: source, state: ElementState::Pressed, }, }); @@ -395,11 +426,26 @@ impl Shared { return; } + let pointer_id = event.pointer_id(); + let kind = backend::event::pointer_kind(&event, pointer_id); let button = backend::event::raw_button(&event).expect("no pointer button pressed"); + let source = match backend::event::pointer_source(&event, kind) { + PointerSource::Mouse => backend::event::mouse_button(button), + PointerSource::Touch { finger_id, force } => { + ButtonSource::Touch { finger_id, force } + }, + PointerSource::TabletTool { kind, data } => ButtonSource::TabletTool { + kind, + button: backend::event::tool_button(button), + data, + }, + PointerSource::Unknown => ButtonSource::Unknown(button), + }; + runner.send_event(Event::DeviceEvent { device_id: event::mkdid(event.pointer_id()), - event: DeviceEvent::Button { - button: button.into(), + event: DeviceEvent::PointerButton { + button: source, state: ElementState::Released, }, }); diff --git a/winit-win32/src/event_loop.rs b/winit-win32/src/event_loop.rs index 3a5720ec7b..11b82e33e8 100644 --- a/winit-win32/src/event_loop.rs +++ b/winit-win32/src/event_loop.rs @@ -65,8 +65,8 @@ use winit_core::application::ApplicationHandler; use winit_core::cursor::{CustomCursor, CustomCursorSource}; use winit_core::error::{EventLoopError, NotSupportedError, RequestError}; use winit_core::event::{ - DeviceEvent, DeviceId, FingerId, Force, Ime, RawKeyEvent, SurfaceSizeWriter, TabletToolButton, - TabletToolData, TabletToolKind, TabletToolTilt, TouchPhase, WindowEvent, + ButtonSource, DeviceEvent, DeviceId, FingerId, Force, Ime, RawKeyEvent, SurfaceSizeWriter, + TabletToolButton, TabletToolData, TabletToolKind, TabletToolTilt, TouchPhase, WindowEvent, }; use winit_core::event_loop::pump_events::PumpStatus; use winit_core::event_loop::{ @@ -2416,7 +2416,7 @@ unsafe extern "system" fn thread_event_target_callback( } unsafe fn handle_raw_input(userdata: &ThreadMsgTargetData, data: RAWINPUT) { - use winit_core::event::DeviceEvent::{Button, Key, MouseWheel, PointerMotion}; + use winit_core::event::DeviceEvent::{Key, MouseWheel, PointerButton, PointerMotion}; use winit_core::event::ElementState::{Pressed, Released}; use winit_core::event::MouseScrollDelta::LineDelta; @@ -2447,10 +2447,11 @@ unsafe fn handle_raw_input(userdata: &ThreadMsgTargetData, data: RAWINPUT) { } let button_state = raw_input::get_raw_mouse_button_state(button_flags as u32); - for (button, state) in button_state.iter().enumerate() { - if let Some(state) = *state { - userdata.send_device_event(device_id, Button { button: button as _, state }); - } + for (mouse_button, state) in button_state.into_iter().flatten() { + userdata.send_device_event(device_id, PointerButton { + button: ButtonSource::Mouse(mouse_button), + state, + }); } } else if data.header.dwType == RIM_TYPEKEYBOARD { let keyboard = unsafe { data.data.keyboard }; diff --git a/winit-win32/src/raw_input.rs b/winit-win32/src/raw_input.rs index c3cd815ce3..b4f4c12873 100644 --- a/winit-win32/src/raw_input.rs +++ b/winit-win32/src/raw_input.rs @@ -20,7 +20,7 @@ use windows_sys::Win32::UI::WindowsAndMessaging::{ RI_MOUSE_BUTTON_2_UP, RI_MOUSE_BUTTON_3_DOWN, RI_MOUSE_BUTTON_3_UP, RI_MOUSE_BUTTON_4_DOWN, RI_MOUSE_BUTTON_4_UP, RI_MOUSE_BUTTON_5_DOWN, RI_MOUSE_BUTTON_5_UP, }; -use winit_core::event::ElementState; +use winit_core::event::{ElementState, MouseButton}; use winit_core::event_loop::DeviceEvents; use winit_core::keyboard::{KeyCode, PhysicalKey}; @@ -180,28 +180,36 @@ pub fn get_raw_input_data(handle: HRAWINPUT) -> Option { Some(data) } -fn button_flags_to_element_state( +fn button_flags_to_mouse_button_state( button_flags: u32, - down_flag: u32, - up_flag: u32, -) -> Option { + mouse_button: MouseButton, +) -> Option<(MouseButton, ElementState)> { + let (down_flag, up_flag) = match mouse_button { + MouseButton::Left => (RI_MOUSE_BUTTON_1_DOWN, RI_MOUSE_BUTTON_1_UP), + MouseButton::Right => (RI_MOUSE_BUTTON_2_DOWN, RI_MOUSE_BUTTON_2_UP), + MouseButton::Middle => (RI_MOUSE_BUTTON_3_DOWN, RI_MOUSE_BUTTON_3_UP), + MouseButton::Back => (RI_MOUSE_BUTTON_4_DOWN, RI_MOUSE_BUTTON_4_UP), + MouseButton::Forward => (RI_MOUSE_BUTTON_5_DOWN, RI_MOUSE_BUTTON_5_UP), + _ => unreachable!("only 5 mouse buttons are supported by windows raw input"), + }; + // We assume the same button won't be simultaneously pressed and released. if util::has_flag(button_flags, down_flag) { - Some(ElementState::Pressed) + Some((mouse_button, ElementState::Pressed)) } else if util::has_flag(button_flags, up_flag) { - Some(ElementState::Released) + Some((mouse_button, ElementState::Released)) } else { None } } -pub fn get_raw_mouse_button_state(button_flags: u32) -> [Option; 5] { +pub fn get_raw_mouse_button_state(button_flags: u32) -> [Option<(MouseButton, ElementState)>; 5] { [ - button_flags_to_element_state(button_flags, RI_MOUSE_BUTTON_1_DOWN, RI_MOUSE_BUTTON_1_UP), - button_flags_to_element_state(button_flags, RI_MOUSE_BUTTON_2_DOWN, RI_MOUSE_BUTTON_2_UP), - button_flags_to_element_state(button_flags, RI_MOUSE_BUTTON_3_DOWN, RI_MOUSE_BUTTON_3_UP), - button_flags_to_element_state(button_flags, RI_MOUSE_BUTTON_4_DOWN, RI_MOUSE_BUTTON_4_UP), - button_flags_to_element_state(button_flags, RI_MOUSE_BUTTON_5_DOWN, RI_MOUSE_BUTTON_5_UP), + button_flags_to_mouse_button_state(button_flags, MouseButton::Left), + button_flags_to_mouse_button_state(button_flags, MouseButton::Right), + button_flags_to_mouse_button_state(button_flags, MouseButton::Middle), + button_flags_to_mouse_button_state(button_flags, MouseButton::Back), + button_flags_to_mouse_button_state(button_flags, MouseButton::Forward), ] } diff --git a/winit-x11/src/event_processor.rs b/winit-x11/src/event_processor.rs index 72c1c295f7..374a42e32d 100644 --- a/winit-x11/src/event_processor.rs +++ b/winit-x11/src/event_processor.rs @@ -1385,7 +1385,23 @@ impl EventProcessor { self.target.xconn.set_timestamp(xev.time as xproto::Timestamp); if xev.flags & xinput2::XIPointerEmulated == 0 { - let event = DeviceEvent::Button { state, button: xev.detail as u32 }; + let button = xev.detail as u32; + let source = match button { + xlib::Button1 => ButtonSource::Mouse(MouseButton::Left), + xlib::Button2 => ButtonSource::Mouse(MouseButton::Middle), + xlib::Button3 => ButtonSource::Mouse(MouseButton::Right), + + // Scroll inputs + 4..=7 => return, + + 8 => ButtonSource::Mouse(MouseButton::Back), + 9 => ButtonSource::Mouse(MouseButton::Forward), + _ => match MouseButton::try_from_u8(button as u8) { + Some(mouse_button) => ButtonSource::Mouse(mouse_button), + None => return, + }, + }; + let event = DeviceEvent::PointerButton { button: source, state }; app.device_event(&self.target, Some(mkdid(xev.deviceid as xinput::DeviceId)), event); } } diff --git a/winit/src/changelog/unreleased.md b/winit/src/changelog/unreleased.md index 63db7610fc..b049881069 100644 --- a/winit/src/changelog/unreleased.md +++ b/winit/src/changelog/unreleased.md @@ -168,7 +168,9 @@ changelog entry. type to a generic mouse button. - New `FingerId` added to `PointerKind::Touch` and `PointerSource::Touch` able to uniquely identify a finger in a multi-touch interaction. Replaces the old `Touch::id`. - - In the same spirit rename `DeviceEvent::MouseMotion` to `PointerMotion`. + - In the same spirit rename `DeviceEvent::MouseMotion` and `DeviceEvent::Button` to + `PointerMotion` and `PointerButton` respectively. + - Change `DeviceEvent::PointerButton` to use `ButtonSource`. - Remove `Force::Calibrated::altitude_angle`. - On X11, use bottom-right corner for IME hotspot in `Window::set_ime_cursor_area`. - On macOS and iOS, no longer emit `ScaleFactorChanged` upon window creation.