From 570ee741973782eb7159a1d95b8b19343454b5e7 Mon Sep 17 00:00:00 2001 From: bbb651 Date: Fri, 14 Apr 2023 23:17:33 +0300 Subject: [PATCH 1/9] Added MouseButton::{Back, Forward} to mouse events This is a breaking change because MouseButton isn't #[non_exhaustive], it's supported on wayland, x11, windows, macos and web, all other platforms (orbital doesn't support these buttons at all, android and ios don't have mouse support in winit). --- src/event.rs | 2 + .../linux/wayland/seat/pointer/handlers.rs | 331 ++++++++++++++++++ .../linux/x11/event_processor.rs | 19 +- src/platform_impl/macos/view.rs | 2 + src/platform_impl/web/web_sys/event.rs | 15 +- src/platform_impl/windows/event_loop.rs | 18 +- 6 files changed, 379 insertions(+), 8 deletions(-) create mode 100644 src/platform_impl/linux/wayland/seat/pointer/handlers.rs diff --git a/src/event.rs b/src/event.rs index a658dc4d6c..70926471ee 100644 --- a/src/event.rs +++ b/src/event.rs @@ -1208,6 +1208,8 @@ pub enum MouseButton { Left, Right, Middle, + Back, + Forward, Other(u16), } diff --git a/src/platform_impl/linux/wayland/seat/pointer/handlers.rs b/src/platform_impl/linux/wayland/seat/pointer/handlers.rs new file mode 100644 index 0000000000..af5630b090 --- /dev/null +++ b/src/platform_impl/linux/wayland/seat/pointer/handlers.rs @@ -0,0 +1,331 @@ +//! Handlers for the pointers we're using. + +use std::cell::RefCell; +use std::rc::Rc; + +use sctk::reexports::client::protocol::wl_pointer::{self, Event as PointerEvent}; +use sctk::reexports::client::protocol::wl_seat::WlSeat; +use sctk::reexports::protocols::unstable::relative_pointer::v1::client::zwp_relative_pointer_v1::Event as RelativePointerEvent; + +use sctk::seat::pointer::ThemedPointer; + +use crate::dpi::LogicalPosition; +use crate::event::{ + DeviceEvent, ElementState, MouseButton, MouseScrollDelta, TouchPhase, WindowEvent, +}; +use crate::platform_impl::wayland::event_loop::WinitState; +use crate::platform_impl::wayland::{self, DeviceId}; + +use super::{PointerData, WinitPointer}; + +// These values are comming from . +const BTN_LEFT: u32 = 0x110; +const BTN_RIGHT: u32 = 0x111; +const BTN_MIDDLE: u32 = 0x112; +const BTN_FORWARD: u32 = 0x115; +const BTN_BACK: u32 = 0x116; + +#[inline] +pub(super) fn handle_pointer( + pointer: ThemedPointer, + event: PointerEvent, + pointer_data: &Rc>, + winit_state: &mut WinitState, + seat: WlSeat, +) { + let event_sink = &mut winit_state.event_sink; + let mut pointer_data = pointer_data.borrow_mut(); + match event { + PointerEvent::Enter { + surface, + surface_x, + surface_y, + serial, + .. + } => { + pointer_data.latest_serial.replace(serial); + pointer_data.latest_enter_serial.replace(serial); + + let window_id = wayland::make_wid(&surface); + let window_handle = match winit_state.window_map.get_mut(&window_id) { + Some(window_handle) => window_handle, + None => return, + }; + + let scale_factor = window_handle.scale_factor(); + pointer_data.surface = Some(surface); + + // Notify window that pointer entered the surface. + let winit_pointer = WinitPointer { + pointer, + confined_pointer: Rc::downgrade(&pointer_data.confined_pointer), + locked_pointer: Rc::downgrade(&pointer_data.locked_pointer), + pointer_constraints: pointer_data.pointer_constraints.clone(), + latest_serial: pointer_data.latest_serial.clone(), + latest_enter_serial: pointer_data.latest_enter_serial.clone(), + seat, + }; + window_handle.pointer_entered(winit_pointer); + + event_sink.push_window_event( + WindowEvent::CursorEntered { + device_id: crate::event::DeviceId(crate::platform_impl::DeviceId::Wayland( + DeviceId, + )), + }, + window_id, + ); + + let position = LogicalPosition::new(surface_x, surface_y).to_physical(scale_factor); + + event_sink.push_window_event( + WindowEvent::CursorMoved { + device_id: crate::event::DeviceId(crate::platform_impl::DeviceId::Wayland( + DeviceId, + )), + position, + modifiers: *pointer_data.modifiers_state.borrow(), + }, + window_id, + ); + } + PointerEvent::Leave { surface, serial } => { + pointer_data.surface = None; + pointer_data.latest_serial.replace(serial); + + let window_id = wayland::make_wid(&surface); + + let window_handle = match winit_state.window_map.get_mut(&window_id) { + Some(window_handle) => window_handle, + None => return, + }; + + // Notify a window that pointer is no longer observing it. + let winit_pointer = WinitPointer { + pointer, + confined_pointer: Rc::downgrade(&pointer_data.confined_pointer), + locked_pointer: Rc::downgrade(&pointer_data.locked_pointer), + pointer_constraints: pointer_data.pointer_constraints.clone(), + latest_serial: pointer_data.latest_serial.clone(), + latest_enter_serial: pointer_data.latest_enter_serial.clone(), + seat, + }; + window_handle.pointer_left(winit_pointer); + + event_sink.push_window_event( + WindowEvent::CursorLeft { + device_id: crate::event::DeviceId(crate::platform_impl::DeviceId::Wayland( + DeviceId, + )), + }, + window_id, + ); + } + PointerEvent::Motion { + surface_x, + surface_y, + .. + } => { + let surface = match pointer_data.surface.as_ref() { + Some(surface) => surface, + None => return, + }; + + let window_id = wayland::make_wid(surface); + let window_handle = match winit_state.window_map.get(&window_id) { + Some(w) => w, + _ => return, + }; + + let scale_factor = window_handle.scale_factor(); + let position = LogicalPosition::new(surface_x, surface_y).to_physical(scale_factor); + + event_sink.push_window_event( + WindowEvent::CursorMoved { + device_id: crate::event::DeviceId(crate::platform_impl::DeviceId::Wayland( + DeviceId, + )), + position, + modifiers: *pointer_data.modifiers_state.borrow(), + }, + window_id, + ); + } + PointerEvent::Button { + button, + state, + serial, + .. + } => { + pointer_data.latest_serial.replace(serial); + let window_id = match pointer_data.surface.as_ref().map(wayland::make_wid) { + Some(window_id) => window_id, + None => return, + }; + + let state = match state { + wl_pointer::ButtonState::Pressed => ElementState::Pressed, + wl_pointer::ButtonState::Released => ElementState::Released, + _ => unreachable!(), + }; + + let button = match button { + BTN_LEFT => MouseButton::Left, + BTN_RIGHT => MouseButton::Right, + BTN_MIDDLE => MouseButton::Middle, + BTN_FORWARD => MouseButton::Forward, + BTN_BACK => MouseButton::Back, + button => MouseButton::Other(button as u16), + }; + + event_sink.push_window_event( + WindowEvent::MouseInput { + device_id: crate::event::DeviceId(crate::platform_impl::DeviceId::Wayland( + DeviceId, + )), + state, + button, + modifiers: *pointer_data.modifiers_state.borrow(), + }, + window_id, + ); + } + PointerEvent::Axis { axis, value, .. } => { + let surface = match pointer_data.surface.as_ref() { + Some(surface) => surface, + None => return, + }; + + let window_id = wayland::make_wid(surface); + let window_handle = match winit_state.window_map.get(&window_id) { + Some(w) => w, + _ => return, + }; + + if pointer.as_ref().version() < 5 { + let (mut x, mut y) = (0.0, 0.0); + + // Old seat compatibility. + match axis { + // Wayland sign convention is the inverse of winit. + wl_pointer::Axis::VerticalScroll => y -= value as f32, + wl_pointer::Axis::HorizontalScroll => x -= value as f32, + _ => unreachable!(), + } + + let scale_factor = window_handle.scale_factor(); + let delta = LogicalPosition::new(x as f64, y as f64).to_physical(scale_factor); + + event_sink.push_window_event( + WindowEvent::MouseWheel { + device_id: crate::event::DeviceId(crate::platform_impl::DeviceId::Wayland( + DeviceId, + )), + delta: MouseScrollDelta::PixelDelta(delta), + phase: TouchPhase::Moved, + modifiers: *pointer_data.modifiers_state.borrow(), + }, + window_id, + ); + } else { + let (mut x, mut y) = pointer_data.axis_data.axis_buffer.unwrap_or((0.0, 0.0)); + match axis { + // Wayland sign convention is the inverse of winit. + wl_pointer::Axis::VerticalScroll => y -= value as f32, + wl_pointer::Axis::HorizontalScroll => x -= value as f32, + _ => unreachable!(), + } + + pointer_data.axis_data.axis_buffer = Some((x, y)); + + pointer_data.axis_data.axis_state = match pointer_data.axis_data.axis_state { + TouchPhase::Started | TouchPhase::Moved => TouchPhase::Moved, + _ => TouchPhase::Started, + } + } + } + PointerEvent::AxisDiscrete { axis, discrete } => { + let (mut x, mut y) = pointer_data + .axis_data + .axis_discrete_buffer + .unwrap_or((0., 0.)); + + match axis { + // Wayland sign convention is the inverse of winit. + wl_pointer::Axis::VerticalScroll => y -= discrete as f32, + wl_pointer::Axis::HorizontalScroll => x -= discrete as f32, + _ => unreachable!(), + } + + pointer_data.axis_data.axis_discrete_buffer = Some((x, y)); + + pointer_data.axis_data.axis_state = match pointer_data.axis_data.axis_state { + TouchPhase::Started | TouchPhase::Moved => TouchPhase::Moved, + _ => TouchPhase::Started, + } + } + PointerEvent::AxisSource { .. } => (), + PointerEvent::AxisStop { .. } => { + pointer_data.axis_data.axis_state = TouchPhase::Ended; + } + PointerEvent::Frame => { + let axis_buffer = pointer_data.axis_data.axis_buffer.take(); + let axis_discrete_buffer = pointer_data.axis_data.axis_discrete_buffer.take(); + + let surface = match pointer_data.surface.as_ref() { + Some(surface) => surface, + None => return, + }; + let window_id = wayland::make_wid(surface); + let window_handle = match winit_state.window_map.get(&window_id) { + Some(w) => w, + _ => return, + }; + + let window_event = if let Some((x, y)) = axis_discrete_buffer { + WindowEvent::MouseWheel { + device_id: crate::event::DeviceId(crate::platform_impl::DeviceId::Wayland( + DeviceId, + )), + delta: MouseScrollDelta::LineDelta(x, y), + phase: pointer_data.axis_data.axis_state, + modifiers: *pointer_data.modifiers_state.borrow(), + } + } else if let Some((x, y)) = axis_buffer { + let scale_factor = window_handle.scale_factor(); + let delta = LogicalPosition::new(x, y).to_physical(scale_factor); + + WindowEvent::MouseWheel { + device_id: crate::event::DeviceId(crate::platform_impl::DeviceId::Wayland( + DeviceId, + )), + delta: MouseScrollDelta::PixelDelta(delta), + phase: pointer_data.axis_data.axis_state, + modifiers: *pointer_data.modifiers_state.borrow(), + } + } else { + return; + }; + + event_sink.push_window_event(window_event, window_id); + } + _ => (), + } +} + +#[inline] +pub(super) fn handle_relative_pointer(event: RelativePointerEvent, winit_state: &mut WinitState) { + if let RelativePointerEvent::RelativeMotion { + dx_unaccel, + dy_unaccel, + .. + } = event + { + winit_state.event_sink.push_device_event( + DeviceEvent::MouseMotion { + delta: (dx_unaccel, dy_unaccel), + }, + DeviceId, + ) + } +} diff --git a/src/platform_impl/linux/x11/event_processor.rs b/src/platform_impl/linux/x11/event_processor.rs index a32f3d9e3e..67e321b406 100644 --- a/src/platform_impl/linux/x11/event_processor.rs +++ b/src/platform_impl/linux/x11/event_processor.rs @@ -579,7 +579,7 @@ impl EventProcessor { use crate::event::{ ElementState::{Pressed, Released}, - MouseButton::{Left, Middle, Other, Right}, + MouseButton::{Back, Forward, Left, Middle, Other, Right}, MouseScrollDelta::LineDelta, Touch, WindowEvent::{ @@ -651,6 +651,23 @@ impl EventProcessor { } } + 8 => callback(Event::WindowEvent { + window_id, + event: MouseInput { + device_id, + state, + button: Back, + }, + }), + 9 => callback(Event::WindowEvent { + window_id, + event: MouseInput { + device_id, + state, + button: Forward, + }, + }), + x => callback(Event::WindowEvent { window_id, event: MouseInput { diff --git a/src/platform_impl/macos/view.rs b/src/platform_impl/macos/view.rs index 314dc2cfbb..5acfe7c00e 100644 --- a/src/platform_impl/macos/view.rs +++ b/src/platform_impl/macos/view.rs @@ -1031,6 +1031,8 @@ fn mouse_button(event: &NSEvent) -> MouseButton { 0 => MouseButton::Left, 1 => MouseButton::Right, 2 => MouseButton::Middle, + 3 => MouseButton::Back, + 4 => MouseButton::Forward, n => MouseButton::Other(n as u16), } } diff --git a/src/platform_impl/web/web_sys/event.rs b/src/platform_impl/web/web_sys/event.rs index 920204637a..dd2329126c 100644 --- a/src/platform_impl/web/web_sys/event.rs +++ b/src/platform_impl/web/web_sys/event.rs @@ -12,9 +12,11 @@ use web_sys::{HtmlCanvasElement, KeyboardEvent, MouseEvent, PointerEvent, WheelE bitflags! { #[derive(Debug, Clone, Copy, PartialEq, Eq)] pub struct ButtonsState: u16 { - const LEFT = 0b001; - const RIGHT = 0b010; - const MIDDLE = 0b100; + const LEFT = 0b00001; + const RIGHT = 0b00010; + const MIDDLE = 0b00100; + const BACK = 0b01000; + const FORWARD = 0b10000; } } @@ -24,6 +26,8 @@ impl From for MouseButton { ButtonsState::LEFT => MouseButton::Left, ButtonsState::RIGHT => MouseButton::Right, ButtonsState::MIDDLE => MouseButton::Middle, + ButtonsState::BACK => MouseButton::Back, + ButtonsState::FORWARD => MouseButton::Forward, _ => MouseButton::Other(value.bits()), } } @@ -35,6 +39,8 @@ impl From for ButtonsState { MouseButton::Left => ButtonsState::LEFT, MouseButton::Right => ButtonsState::RIGHT, MouseButton::Middle => ButtonsState::MIDDLE, + MouseButton::Back => ButtonsState::BACK, + MouseButton::Forward => ButtonsState::FORWARD, MouseButton::Other(value) => ButtonsState::from_bits_retain(value), } } @@ -45,11 +51,14 @@ pub fn mouse_buttons(event: &MouseEvent) -> ButtonsState { } pub fn mouse_button(event: &MouseEvent) -> Option { + // https://w3c.github.io/uievents/#dom-mouseevent-button match event.button() { -1 => None, 0 => Some(MouseButton::Left), 1 => Some(MouseButton::Middle), 2 => Some(MouseButton::Right), + 3 => Some(MouseButton::Back), + 4 => Some(MouseButton::Forward), i => Some(MouseButton::Other( i.try_into() .expect("unexpected negative mouse button value"), diff --git a/src/platform_impl/windows/event_loop.rs b/src/platform_impl/windows/event_loop.rs index 7ed5b820db..d3b4bf6a82 100644 --- a/src/platform_impl/windows/event_loop.rs +++ b/src/platform_impl/windows/event_loop.rs @@ -1647,7 +1647,8 @@ unsafe fn public_window_callback_inner( WM_XBUTTONDOWN => { use crate::event::{ - ElementState::Pressed, MouseButton::Other, WindowEvent::MouseInput, + ElementState::Pressed, MouseButton::Back, MouseButton::Forward, MouseButton::Other, + WindowEvent::MouseInput, }; let xbutton = super::get_xbutton_wparam(wparam as u32); @@ -1660,7 +1661,11 @@ unsafe fn public_window_callback_inner( event: MouseInput { device_id: DEVICE_ID, state: Pressed, - button: Other(xbutton), + button: match xbutton { + 1 => Back, + 2 => Forward, + _ => Other(xbutton), + }, }, }); result = ProcResult::Value(0); @@ -1668,7 +1673,8 @@ unsafe fn public_window_callback_inner( WM_XBUTTONUP => { use crate::event::{ - ElementState::Released, MouseButton::Other, WindowEvent::MouseInput, + ElementState::Released, MouseButton::Back, MouseButton::Forward, + MouseButton::Other, WindowEvent::MouseInput, }; let xbutton = super::get_xbutton_wparam(wparam as u32); @@ -1681,7 +1687,11 @@ unsafe fn public_window_callback_inner( event: MouseInput { device_id: DEVICE_ID, state: Released, - button: Other(xbutton), + button: match xbutton { + 1 => Back, + 2 => Forward, + _ => Other(xbutton), + }, }, }); result = ProcResult::Value(0); From 786aaa1d128db50bf0e1caa291ec9eb0bfcb92de Mon Sep 17 00:00:00 2001 From: bbb651 Date: Fri, 14 Apr 2023 23:27:42 +0300 Subject: [PATCH 2/9] Updated changelog for MouseButton::{Back, Forward} --- CHANGELOG.md | 1 + 1 file changed, 1 insertion(+) diff --git a/CHANGELOG.md b/CHANGELOG.md index eb1d4fe934..dbeffcb151 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -85,6 +85,7 @@ And please only add new entries to the top of this list, right below the `# Unre - On Web, fix `DeviceEvent::MouseMotion` only being emitted for each canvas instead of the whole window. - On Web, add `DeviceEvent::Motion`, `DeviceEvent::MouseWheel`, `DeviceEvent::Button` and `DeviceEvent::Key` support. +- **Breaking** `MouseButton` now supports `Back` and `Forward` variants, emitted from mouse events on Wayland, X11, Windows, macOS and Web. # 0.28.6 From 8bdeb567b99ac8f5a9a4ba0723fd678544ff8435 Mon Sep 17 00:00:00 2001 From: bbb651 Date: Fri, 14 Apr 2023 23:40:03 +0300 Subject: [PATCH 3/9] Added mouse buttons example --- examples/mouse_buttons.rs | 36 ++++++++++++++++++++++++++++++++++++ 1 file changed, 36 insertions(+) create mode 100644 examples/mouse_buttons.rs diff --git a/examples/mouse_buttons.rs b/examples/mouse_buttons.rs new file mode 100644 index 0000000000..d2e1608830 --- /dev/null +++ b/examples/mouse_buttons.rs @@ -0,0 +1,36 @@ +use simple_logger::SimpleLogger; +use winit::{ + event::{Event, WindowEvent}, + event_loop::EventLoop, + window::WindowBuilder, +}; + +fn main() { + SimpleLogger::new().init().unwrap(); + let event_loop = EventLoop::new(); + + let _window = WindowBuilder::new() + .with_title("Mouse Wheel events") + .build(&event_loop) + .unwrap(); + + event_loop.run(move |event, _, control_flow| { + control_flow.set_wait(); + + match event { + Event::WindowEvent { event, .. } => match event { + WindowEvent::CloseRequested => control_flow.set_exit(), + WindowEvent::MouseInput { + device_id, + state, + button, + .. + } => { + println!("Device {device_id:?}: Mouse button {button:?} is {state:?}"); + } + _ => (), + }, + _ => (), + } + }); +} From b789e99c3cc56130a0940a034715522b59194f53 Mon Sep 17 00:00:00 2001 From: bbb651 Date: Sat, 15 Apr 2023 00:39:31 +0300 Subject: [PATCH 4/9] Wayland: Add BTN_SIDE and BTN_EXTRA to implementation of MouseButton::{Back, Forward} This seems to be what [chromium does](https://chromium.googlesource.com/chromium/src/+/refs/heads/main/ui/ozone/platform/wayland/host/wayland_pointer.cc). --- src/platform_impl/linux/wayland/seat/pointer/handlers.rs | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/src/platform_impl/linux/wayland/seat/pointer/handlers.rs b/src/platform_impl/linux/wayland/seat/pointer/handlers.rs index af5630b090..b9817abb39 100644 --- a/src/platform_impl/linux/wayland/seat/pointer/handlers.rs +++ b/src/platform_impl/linux/wayland/seat/pointer/handlers.rs @@ -22,6 +22,8 @@ use super::{PointerData, WinitPointer}; const BTN_LEFT: u32 = 0x110; const BTN_RIGHT: u32 = 0x111; const BTN_MIDDLE: u32 = 0x112; +const BTN_SIDE: u32 = 0x113; +const BTN_EXTRA: u32 = 0x114; const BTN_FORWARD: u32 = 0x115; const BTN_BACK: u32 = 0x116; @@ -173,8 +175,8 @@ pub(super) fn handle_pointer( BTN_LEFT => MouseButton::Left, BTN_RIGHT => MouseButton::Right, BTN_MIDDLE => MouseButton::Middle, - BTN_FORWARD => MouseButton::Forward, - BTN_BACK => MouseButton::Back, + BTN_BACK | BTN_SIDE => MouseButton::Back, + BTN_FORWARD | BTN_EXTRA => MouseButton::Forward, button => MouseButton::Other(button as u16), }; From c4fc998bc08d390fc0538791a1fb40b6e7b805b7 Mon Sep 17 00:00:00 2001 From: bbb651 Date: Tue, 18 Apr 2023 18:12:54 +0300 Subject: [PATCH 5/9] Improve documentation and changelog for MouseButton::{Forward, Back} --- CHANGELOG.md | 2 ++ src/event.rs | 7 +++++++ 2 files changed, 9 insertions(+) diff --git a/CHANGELOG.md b/CHANGELOG.md index dbeffcb151..d7e3cfd614 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -101,6 +101,8 @@ And please only add new entries to the top of this list, right below the `# Unre - On macOS, fix empty marked text blocking regular input. - On macOS, fix potential panic when getting refresh rate. - On macOS, fix crash when calling `Window::set_ime_position` from another thread. +- Bump MSRV from `1.60` to `1.64`. This is a **breaking** change. +- On macOS, fixed potential panic when getting refresh rate. # 0.28.3 diff --git a/src/event.rs b/src/event.rs index 70926471ee..31dc8b53c8 100644 --- a/src/event.rs +++ b/src/event.rs @@ -1202,6 +1202,13 @@ pub enum ElementState { } /// Describes a button of a mouse controller. +/// +/// ## Platform-specific +/// +/// **macOS:** does not offically define `Back` and `Forward`, but supports 29 generic mouse buttons from 3..=31. +/// `3` and `4` is commonly used for `Back` and `Forward` respectivly by driver vendor and applications such as browsers, +/// this follows this convention. Not guaranteed to work with all hardware and configurations. +/// **orbital:** `Back` and `Forward` are unsupported due to orbital not supporting them. #[derive(Debug, Hash, PartialEq, Eq, Clone, Copy)] #[cfg_attr(feature = "serde", derive(Serialize, Deserialize))] pub enum MouseButton { From 595e1e5b0983e74fd2f65c147b2ea7c04db3813a Mon Sep 17 00:00:00 2001 From: bbb651 <53972231+bbb651@users.noreply.github.com> Date: Fri, 2 Jun 2023 21:05:49 +0300 Subject: [PATCH 6/9] Link to W3C instead of editors draft in web mouse button handling Co-authored-by: daxpedda --- src/platform_impl/web/web_sys/event.rs | 45 ++------------------------ 1 file changed, 2 insertions(+), 43 deletions(-) diff --git a/src/platform_impl/web/web_sys/event.rs b/src/platform_impl/web/web_sys/event.rs index dd2329126c..5c8a377cf0 100644 --- a/src/platform_impl/web/web_sys/event.rs +++ b/src/platform_impl/web/web_sys/event.rs @@ -9,49 +9,8 @@ use wasm_bindgen::prelude::wasm_bindgen; use wasm_bindgen::{JsCast, JsValue}; use web_sys::{HtmlCanvasElement, KeyboardEvent, MouseEvent, PointerEvent, WheelEvent}; -bitflags! { - #[derive(Debug, Clone, Copy, PartialEq, Eq)] - pub struct ButtonsState: u16 { - const LEFT = 0b00001; - const RIGHT = 0b00010; - const MIDDLE = 0b00100; - const BACK = 0b01000; - const FORWARD = 0b10000; - } -} - -impl From for MouseButton { - fn from(value: ButtonsState) -> Self { - match value { - ButtonsState::LEFT => MouseButton::Left, - ButtonsState::RIGHT => MouseButton::Right, - ButtonsState::MIDDLE => MouseButton::Middle, - ButtonsState::BACK => MouseButton::Back, - ButtonsState::FORWARD => MouseButton::Forward, - _ => MouseButton::Other(value.bits()), - } - } -} - -impl From for ButtonsState { - fn from(value: MouseButton) -> Self { - match value { - MouseButton::Left => ButtonsState::LEFT, - MouseButton::Right => ButtonsState::RIGHT, - MouseButton::Middle => ButtonsState::MIDDLE, - MouseButton::Back => ButtonsState::BACK, - MouseButton::Forward => ButtonsState::FORWARD, - MouseButton::Other(value) => ButtonsState::from_bits_retain(value), - } - } -} - -pub fn mouse_buttons(event: &MouseEvent) -> ButtonsState { - ButtonsState::from_bits_retain(event.buttons()) -} - -pub fn mouse_button(event: &MouseEvent) -> Option { - // https://w3c.github.io/uievents/#dom-mouseevent-button +pub fn mouse_button(event: &MouseEvent) -> MouseButton { + // https://www.w3.org/TR/uievents/#dom-mouseevent-button match event.button() { -1 => None, 0 => Some(MouseButton::Left), From c8a0c3e0907e4024ce2be552fb61c4a6419c6c43 Mon Sep 17 00:00:00 2001 From: bbb651 Date: Fri, 2 Jun 2023 21:22:02 +0300 Subject: [PATCH 7/9] Improve Back and Forward mouse button docs and comments --- src/event.rs | 6 ++---- src/platform_impl/macos/view.rs | 2 ++ 2 files changed, 4 insertions(+), 4 deletions(-) diff --git a/src/event.rs b/src/event.rs index 31dc8b53c8..4111e1f64a 100644 --- a/src/event.rs +++ b/src/event.rs @@ -1205,10 +1205,8 @@ pub enum ElementState { /// /// ## Platform-specific /// -/// **macOS:** does not offically define `Back` and `Forward`, but supports 29 generic mouse buttons from 3..=31. -/// `3` and `4` is commonly used for `Back` and `Forward` respectivly by driver vendor and applications such as browsers, -/// this follows this convention. Not guaranteed to work with all hardware and configurations. -/// **orbital:** `Back` and `Forward` are unsupported due to orbital not supporting them. +/// **macOS:** `Back` and `Forward` might not work with all hardware. +/// **Orbital:** `Back` and `Forward` are unsupported due to orbital not supporting them. #[derive(Debug, Hash, PartialEq, Eq, Clone, Copy)] #[cfg_attr(feature = "serde", derive(Serialize, Deserialize))] pub enum MouseButton { diff --git a/src/platform_impl/macos/view.rs b/src/platform_impl/macos/view.rs index 5acfe7c00e..faecdd1593 100644 --- a/src/platform_impl/macos/view.rs +++ b/src/platform_impl/macos/view.rs @@ -1027,6 +1027,8 @@ 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. + // MacOS only defines the left, right and middle buttons, 3..=31 are left as generic buttons, + // but 3 and 4 are very commonly used as Back and Forward by hardware vendors and applications. match event.buttonNumber() { 0 => MouseButton::Left, 1 => MouseButton::Right, From 61f6cea2a4a14a435af9286e6adad1a345342dd3 Mon Sep 17 00:00:00 2001 From: dAxpeDDa Date: Sat, 3 Jun 2023 12:42:32 +0200 Subject: [PATCH 8/9] Implement Web backend --- src/platform_impl/web/web_sys/event.rs | 48 ++++++++++++++++++++++++-- 1 file changed, 46 insertions(+), 2 deletions(-) diff --git a/src/platform_impl/web/web_sys/event.rs b/src/platform_impl/web/web_sys/event.rs index 5c8a377cf0..70ece5fea8 100644 --- a/src/platform_impl/web/web_sys/event.rs +++ b/src/platform_impl/web/web_sys/event.rs @@ -9,8 +9,50 @@ use wasm_bindgen::prelude::wasm_bindgen; use wasm_bindgen::{JsCast, JsValue}; use web_sys::{HtmlCanvasElement, KeyboardEvent, MouseEvent, PointerEvent, WheelEvent}; -pub fn mouse_button(event: &MouseEvent) -> MouseButton { - // https://www.w3.org/TR/uievents/#dom-mouseevent-button +bitflags! { + // https://www.w3.org/TR/pointerevents3/#the-buttons-property + #[derive(Debug, Clone, Copy, PartialEq, Eq)] + pub struct ButtonsState: u16 { + const LEFT = 0b00001; + const RIGHT = 0b00010; + const MIDDLE = 0b00100; + const BACK = 0b01000; + const FORWARD = 0b10000; + } +} + +impl From for MouseButton { + fn from(value: ButtonsState) -> Self { + match value { + ButtonsState::LEFT => MouseButton::Left, + ButtonsState::RIGHT => MouseButton::Right, + ButtonsState::MIDDLE => MouseButton::Middle, + ButtonsState::BACK => MouseButton::Back, + ButtonsState::FORWARD => MouseButton::Forward, + _ => MouseButton::Other(value.bits()), + } + } +} + +impl From for ButtonsState { + fn from(value: MouseButton) -> Self { + match value { + MouseButton::Left => ButtonsState::LEFT, + MouseButton::Right => ButtonsState::RIGHT, + MouseButton::Middle => ButtonsState::MIDDLE, + MouseButton::Back => ButtonsState::BACK, + MouseButton::Forward => ButtonsState::FORWARD, + MouseButton::Other(value) => ButtonsState::from_bits_retain(value), + } + } +} + +pub fn mouse_buttons(event: &MouseEvent) -> ButtonsState { + ButtonsState::from_bits_retain(event.buttons()) +} + +pub fn mouse_button(event: &MouseEvent) -> Option { + // https://www.w3.org/TR/pointerevents3/#the-button-property match event.button() { -1 => None, 0 => Some(MouseButton::Left), @@ -31,6 +73,8 @@ impl MouseButton { MouseButton::Left => 0, MouseButton::Right => 1, MouseButton::Middle => 2, + MouseButton::Back => 3, + MouseButton::Forward => 4, MouseButton::Other(value) => value.into(), } } From 0cf60e1a9f9b11f780fcaf29cc5c9d3ebbcce4ae Mon Sep 17 00:00:00 2001 From: dAxpeDDa Date: Thu, 15 Jun 2023 21:17:03 +0200 Subject: [PATCH 9/9] Address review --- CHANGELOG.md | 5 +- examples/mouse_buttons.rs | 36 -- .../linux/wayland/seat/pointer/handlers.rs | 333 ------------------ .../linux/wayland/seat/pointer/mod.rs | 8 +- 4 files changed, 9 insertions(+), 373 deletions(-) delete mode 100644 examples/mouse_buttons.rs delete mode 100644 src/platform_impl/linux/wayland/seat/pointer/handlers.rs diff --git a/CHANGELOG.md b/CHANGELOG.md index d7e3cfd614..9229c5f24c 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -85,7 +85,8 @@ And please only add new entries to the top of this list, right below the `# Unre - On Web, fix `DeviceEvent::MouseMotion` only being emitted for each canvas instead of the whole window. - On Web, add `DeviceEvent::Motion`, `DeviceEvent::MouseWheel`, `DeviceEvent::Button` and `DeviceEvent::Key` support. -- **Breaking** `MouseButton` now supports `Back` and `Forward` variants, emitted from mouse events on Wayland, X11, Windows, macOS and Web. +- **Breaking** `MouseButton` now supports `Back` and `Forward` variants, emitted from mouse events + on Wayland, X11, Windows, macOS and Web. # 0.28.6 @@ -101,8 +102,6 @@ And please only add new entries to the top of this list, right below the `# Unre - On macOS, fix empty marked text blocking regular input. - On macOS, fix potential panic when getting refresh rate. - On macOS, fix crash when calling `Window::set_ime_position` from another thread. -- Bump MSRV from `1.60` to `1.64`. This is a **breaking** change. -- On macOS, fixed potential panic when getting refresh rate. # 0.28.3 diff --git a/examples/mouse_buttons.rs b/examples/mouse_buttons.rs deleted file mode 100644 index d2e1608830..0000000000 --- a/examples/mouse_buttons.rs +++ /dev/null @@ -1,36 +0,0 @@ -use simple_logger::SimpleLogger; -use winit::{ - event::{Event, WindowEvent}, - event_loop::EventLoop, - window::WindowBuilder, -}; - -fn main() { - SimpleLogger::new().init().unwrap(); - let event_loop = EventLoop::new(); - - let _window = WindowBuilder::new() - .with_title("Mouse Wheel events") - .build(&event_loop) - .unwrap(); - - event_loop.run(move |event, _, control_flow| { - control_flow.set_wait(); - - match event { - Event::WindowEvent { event, .. } => match event { - WindowEvent::CloseRequested => control_flow.set_exit(), - WindowEvent::MouseInput { - device_id, - state, - button, - .. - } => { - println!("Device {device_id:?}: Mouse button {button:?} is {state:?}"); - } - _ => (), - }, - _ => (), - } - }); -} diff --git a/src/platform_impl/linux/wayland/seat/pointer/handlers.rs b/src/platform_impl/linux/wayland/seat/pointer/handlers.rs deleted file mode 100644 index b9817abb39..0000000000 --- a/src/platform_impl/linux/wayland/seat/pointer/handlers.rs +++ /dev/null @@ -1,333 +0,0 @@ -//! Handlers for the pointers we're using. - -use std::cell::RefCell; -use std::rc::Rc; - -use sctk::reexports::client::protocol::wl_pointer::{self, Event as PointerEvent}; -use sctk::reexports::client::protocol::wl_seat::WlSeat; -use sctk::reexports::protocols::unstable::relative_pointer::v1::client::zwp_relative_pointer_v1::Event as RelativePointerEvent; - -use sctk::seat::pointer::ThemedPointer; - -use crate::dpi::LogicalPosition; -use crate::event::{ - DeviceEvent, ElementState, MouseButton, MouseScrollDelta, TouchPhase, WindowEvent, -}; -use crate::platform_impl::wayland::event_loop::WinitState; -use crate::platform_impl::wayland::{self, DeviceId}; - -use super::{PointerData, WinitPointer}; - -// These values are comming from . -const BTN_LEFT: u32 = 0x110; -const BTN_RIGHT: u32 = 0x111; -const BTN_MIDDLE: u32 = 0x112; -const BTN_SIDE: u32 = 0x113; -const BTN_EXTRA: u32 = 0x114; -const BTN_FORWARD: u32 = 0x115; -const BTN_BACK: u32 = 0x116; - -#[inline] -pub(super) fn handle_pointer( - pointer: ThemedPointer, - event: PointerEvent, - pointer_data: &Rc>, - winit_state: &mut WinitState, - seat: WlSeat, -) { - let event_sink = &mut winit_state.event_sink; - let mut pointer_data = pointer_data.borrow_mut(); - match event { - PointerEvent::Enter { - surface, - surface_x, - surface_y, - serial, - .. - } => { - pointer_data.latest_serial.replace(serial); - pointer_data.latest_enter_serial.replace(serial); - - let window_id = wayland::make_wid(&surface); - let window_handle = match winit_state.window_map.get_mut(&window_id) { - Some(window_handle) => window_handle, - None => return, - }; - - let scale_factor = window_handle.scale_factor(); - pointer_data.surface = Some(surface); - - // Notify window that pointer entered the surface. - let winit_pointer = WinitPointer { - pointer, - confined_pointer: Rc::downgrade(&pointer_data.confined_pointer), - locked_pointer: Rc::downgrade(&pointer_data.locked_pointer), - pointer_constraints: pointer_data.pointer_constraints.clone(), - latest_serial: pointer_data.latest_serial.clone(), - latest_enter_serial: pointer_data.latest_enter_serial.clone(), - seat, - }; - window_handle.pointer_entered(winit_pointer); - - event_sink.push_window_event( - WindowEvent::CursorEntered { - device_id: crate::event::DeviceId(crate::platform_impl::DeviceId::Wayland( - DeviceId, - )), - }, - window_id, - ); - - let position = LogicalPosition::new(surface_x, surface_y).to_physical(scale_factor); - - event_sink.push_window_event( - WindowEvent::CursorMoved { - device_id: crate::event::DeviceId(crate::platform_impl::DeviceId::Wayland( - DeviceId, - )), - position, - modifiers: *pointer_data.modifiers_state.borrow(), - }, - window_id, - ); - } - PointerEvent::Leave { surface, serial } => { - pointer_data.surface = None; - pointer_data.latest_serial.replace(serial); - - let window_id = wayland::make_wid(&surface); - - let window_handle = match winit_state.window_map.get_mut(&window_id) { - Some(window_handle) => window_handle, - None => return, - }; - - // Notify a window that pointer is no longer observing it. - let winit_pointer = WinitPointer { - pointer, - confined_pointer: Rc::downgrade(&pointer_data.confined_pointer), - locked_pointer: Rc::downgrade(&pointer_data.locked_pointer), - pointer_constraints: pointer_data.pointer_constraints.clone(), - latest_serial: pointer_data.latest_serial.clone(), - latest_enter_serial: pointer_data.latest_enter_serial.clone(), - seat, - }; - window_handle.pointer_left(winit_pointer); - - event_sink.push_window_event( - WindowEvent::CursorLeft { - device_id: crate::event::DeviceId(crate::platform_impl::DeviceId::Wayland( - DeviceId, - )), - }, - window_id, - ); - } - PointerEvent::Motion { - surface_x, - surface_y, - .. - } => { - let surface = match pointer_data.surface.as_ref() { - Some(surface) => surface, - None => return, - }; - - let window_id = wayland::make_wid(surface); - let window_handle = match winit_state.window_map.get(&window_id) { - Some(w) => w, - _ => return, - }; - - let scale_factor = window_handle.scale_factor(); - let position = LogicalPosition::new(surface_x, surface_y).to_physical(scale_factor); - - event_sink.push_window_event( - WindowEvent::CursorMoved { - device_id: crate::event::DeviceId(crate::platform_impl::DeviceId::Wayland( - DeviceId, - )), - position, - modifiers: *pointer_data.modifiers_state.borrow(), - }, - window_id, - ); - } - PointerEvent::Button { - button, - state, - serial, - .. - } => { - pointer_data.latest_serial.replace(serial); - let window_id = match pointer_data.surface.as_ref().map(wayland::make_wid) { - Some(window_id) => window_id, - None => return, - }; - - let state = match state { - wl_pointer::ButtonState::Pressed => ElementState::Pressed, - wl_pointer::ButtonState::Released => ElementState::Released, - _ => unreachable!(), - }; - - let button = match button { - BTN_LEFT => MouseButton::Left, - BTN_RIGHT => MouseButton::Right, - BTN_MIDDLE => MouseButton::Middle, - BTN_BACK | BTN_SIDE => MouseButton::Back, - BTN_FORWARD | BTN_EXTRA => MouseButton::Forward, - button => MouseButton::Other(button as u16), - }; - - event_sink.push_window_event( - WindowEvent::MouseInput { - device_id: crate::event::DeviceId(crate::platform_impl::DeviceId::Wayland( - DeviceId, - )), - state, - button, - modifiers: *pointer_data.modifiers_state.borrow(), - }, - window_id, - ); - } - PointerEvent::Axis { axis, value, .. } => { - let surface = match pointer_data.surface.as_ref() { - Some(surface) => surface, - None => return, - }; - - let window_id = wayland::make_wid(surface); - let window_handle = match winit_state.window_map.get(&window_id) { - Some(w) => w, - _ => return, - }; - - if pointer.as_ref().version() < 5 { - let (mut x, mut y) = (0.0, 0.0); - - // Old seat compatibility. - match axis { - // Wayland sign convention is the inverse of winit. - wl_pointer::Axis::VerticalScroll => y -= value as f32, - wl_pointer::Axis::HorizontalScroll => x -= value as f32, - _ => unreachable!(), - } - - let scale_factor = window_handle.scale_factor(); - let delta = LogicalPosition::new(x as f64, y as f64).to_physical(scale_factor); - - event_sink.push_window_event( - WindowEvent::MouseWheel { - device_id: crate::event::DeviceId(crate::platform_impl::DeviceId::Wayland( - DeviceId, - )), - delta: MouseScrollDelta::PixelDelta(delta), - phase: TouchPhase::Moved, - modifiers: *pointer_data.modifiers_state.borrow(), - }, - window_id, - ); - } else { - let (mut x, mut y) = pointer_data.axis_data.axis_buffer.unwrap_or((0.0, 0.0)); - match axis { - // Wayland sign convention is the inverse of winit. - wl_pointer::Axis::VerticalScroll => y -= value as f32, - wl_pointer::Axis::HorizontalScroll => x -= value as f32, - _ => unreachable!(), - } - - pointer_data.axis_data.axis_buffer = Some((x, y)); - - pointer_data.axis_data.axis_state = match pointer_data.axis_data.axis_state { - TouchPhase::Started | TouchPhase::Moved => TouchPhase::Moved, - _ => TouchPhase::Started, - } - } - } - PointerEvent::AxisDiscrete { axis, discrete } => { - let (mut x, mut y) = pointer_data - .axis_data - .axis_discrete_buffer - .unwrap_or((0., 0.)); - - match axis { - // Wayland sign convention is the inverse of winit. - wl_pointer::Axis::VerticalScroll => y -= discrete as f32, - wl_pointer::Axis::HorizontalScroll => x -= discrete as f32, - _ => unreachable!(), - } - - pointer_data.axis_data.axis_discrete_buffer = Some((x, y)); - - pointer_data.axis_data.axis_state = match pointer_data.axis_data.axis_state { - TouchPhase::Started | TouchPhase::Moved => TouchPhase::Moved, - _ => TouchPhase::Started, - } - } - PointerEvent::AxisSource { .. } => (), - PointerEvent::AxisStop { .. } => { - pointer_data.axis_data.axis_state = TouchPhase::Ended; - } - PointerEvent::Frame => { - let axis_buffer = pointer_data.axis_data.axis_buffer.take(); - let axis_discrete_buffer = pointer_data.axis_data.axis_discrete_buffer.take(); - - let surface = match pointer_data.surface.as_ref() { - Some(surface) => surface, - None => return, - }; - let window_id = wayland::make_wid(surface); - let window_handle = match winit_state.window_map.get(&window_id) { - Some(w) => w, - _ => return, - }; - - let window_event = if let Some((x, y)) = axis_discrete_buffer { - WindowEvent::MouseWheel { - device_id: crate::event::DeviceId(crate::platform_impl::DeviceId::Wayland( - DeviceId, - )), - delta: MouseScrollDelta::LineDelta(x, y), - phase: pointer_data.axis_data.axis_state, - modifiers: *pointer_data.modifiers_state.borrow(), - } - } else if let Some((x, y)) = axis_buffer { - let scale_factor = window_handle.scale_factor(); - let delta = LogicalPosition::new(x, y).to_physical(scale_factor); - - WindowEvent::MouseWheel { - device_id: crate::event::DeviceId(crate::platform_impl::DeviceId::Wayland( - DeviceId, - )), - delta: MouseScrollDelta::PixelDelta(delta), - phase: pointer_data.axis_data.axis_state, - modifiers: *pointer_data.modifiers_state.borrow(), - } - } else { - return; - }; - - event_sink.push_window_event(window_event, window_id); - } - _ => (), - } -} - -#[inline] -pub(super) fn handle_relative_pointer(event: RelativePointerEvent, winit_state: &mut WinitState) { - if let RelativePointerEvent::RelativeMotion { - dx_unaccel, - dy_unaccel, - .. - } = event - { - winit_state.event_sink.push_device_event( - DeviceEvent::MouseMotion { - delta: (dx_unaccel, dy_unaccel), - }, - DeviceId, - ) - } -} diff --git a/src/platform_impl/linux/wayland/seat/pointer/mod.rs b/src/platform_impl/linux/wayland/seat/pointer/mod.rs index 1b933028e8..607b0e1b12 100644 --- a/src/platform_impl/linux/wayland/seat/pointer/mod.rs +++ b/src/platform_impl/linux/wayland/seat/pointer/mod.rs @@ -397,15 +397,21 @@ impl Default for WinitPointerDataInner { /// Convert the Wayland button into winit. fn wayland_button_to_winit(button: u32) -> MouseButton { - // These values are comming from . + // These values are coming from . const BTN_LEFT: u32 = 0x110; const BTN_RIGHT: u32 = 0x111; const BTN_MIDDLE: u32 = 0x112; + const BTN_SIDE: u32 = 0x113; + const BTN_EXTRA: u32 = 0x114; + const BTN_FORWARD: u32 = 0x115; + const BTN_BACK: u32 = 0x116; match button { BTN_LEFT => MouseButton::Left, BTN_RIGHT => MouseButton::Right, BTN_MIDDLE => MouseButton::Middle, + BTN_BACK | BTN_SIDE => MouseButton::Back, + BTN_FORWARD | BTN_EXTRA => MouseButton::Forward, button => MouseButton::Other(button as u16), } }