From 3283010d2cdd5889138b599c478b3a1c76792be1 Mon Sep 17 00:00:00 2001 From: i509VCB Date: Wed, 27 Oct 2021 12:23:00 -0500 Subject: [PATCH] Add Forward/Back mouse buttons --- CHANGELOG.md | 4 ++ anvil/src/input_handler.rs | 7 +- src/backend/input/mod.rs | 48 +++++++++++-- src/backend/libinput/mod.rs | 9 +-- src/backend/winit/input.rs | 33 ++++----- src/backend/winit/mod.rs | 51 +++++++++----- src/backend/x11/input.rs | 9 ++- src/backend/x11/mod.rs | 132 ++++++++++++------------------------ 8 files changed, 147 insertions(+), 146 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index fae31a8..bbf6942 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -10,6 +10,9 @@ - `XdgPositionerState` moved to `XdgPopupState` and added to `XdgRequest::NewPopup` - `PopupSurface::send_configure` now checks the protocol version and returns an `Result` - `KeyboardHandle::input` filter closure now receives a `KeysymHandle` instead of a `Keysym` and returns a `FilterResult`. +- `PointerButtonEvent::button` now returns an `Option`. +- `MouseButton` is now non-exhaustive. +- Remove `Other` and add `Forward` and `Back` variants to `MouseButton`. Use the new `PointerButtonEvent::button_code` in place of `Other`. #### Backends @@ -40,6 +43,7 @@ - `x11rb` event source integration used in anvil's XWayland implementation is now part of smithay at `utils::x11rb`. Enabled through the `x11rb_event_source` feature. - `KeyState`, `MouseButton`, `ButtonState` and `Axis` in `backend::input` now derive `Hash`. - New `DrmNode` type in drm backend. This is primarily for use a backend which needs to run as client inside another session. +- The button code for a `PointerButtonEvent` may now be obtained using `PointerButtonEvent::button_code`. ### Bugfixes diff --git a/anvil/src/input_handler.rs b/anvil/src/input_handler.rs index 2ea9c41..2f8d290 100644 --- a/anvil/src/input_handler.rs +++ b/anvil/src/input_handler.rs @@ -86,12 +86,7 @@ impl AnvilState { fn on_pointer_button(&mut self, evt: B::PointerButtonEvent) { let serial = SCOUNTER.next_serial(); - let button = match evt.button() { - input::MouseButton::Left => 0x110, - input::MouseButton::Right => 0x111, - input::MouseButton::Middle => 0x112, - input::MouseButton::Other(b) => b as u32, - }; + let button = evt.button_code(); let state = match evt.state() { input::ButtonState::Pressed => { // change the keyboard focus unless the pointer is grabbed diff --git a/src/backend/input/mod.rs b/src/backend/input/mod.rs index c9e24a5..97129f7 100644 --- a/src/backend/input/mod.rs +++ b/src/backend/input/mod.rs @@ -117,6 +117,7 @@ impl KeyboardKeyEvent for UnusedEvent { } /// A particular mouse button +#[non_exhaustive] #[derive(Debug, PartialEq, Eq, Clone, Copy, Hash)] pub enum MouseButton { /// Left mouse button @@ -125,8 +126,10 @@ pub enum MouseButton { Middle, /// Right mouse button Right, - /// Other mouse button with index - Other(u8), + /// Forward mouse button. + Forward, + /// Back mouse button. + Back, } /// State of a button on a pointer device, like mouse or tablet tool. Either pressed or released @@ -140,14 +143,35 @@ pub enum ButtonState { /// Common methods pointer event generated by pressed buttons do implement pub trait PointerButtonEvent: Event { - /// Pressed button of the event - fn button(&self) -> MouseButton; + /// Pressed button of the event. + /// + /// This may return [`None`] if the button pressed in the event is not a standard mouse button. You may + /// obtain the button code using [`PointerButtonEvent::button_code`]. + fn button(&self) -> Option { + match self.button_code() { + 0x110 => Some(MouseButton::Left), + 0x111 => Some(MouseButton::Right), + 0x112 => Some(MouseButton::Middle), + 0x115 => Some(MouseButton::Forward), + 0x116 => Some(MouseButton::Back), + _ => None, + } + } + + /// Returns the numerical button code of the mouse button. + /// + /// The value will correspond to one `BTN_` constants from the Linux [input event codes] inside + /// `input-event-codes.h`. + /// + /// [input event codes]: https://gitlab.freedesktop.org/libinput/libinput/-/blob/main/include/linux/linux/input-event-codes.h + fn button_code(&self) -> u32; + /// State of the button fn state(&self) -> ButtonState; } impl PointerButtonEvent for UnusedEvent { - fn button(&self) -> MouseButton { + fn button_code(&self) -> u32 { match *self {} } @@ -620,3 +644,17 @@ pub enum InputEvent { /// Special event specific of this backend Special(B::SpecialEvent), } + +/// Converts an xorg mouse button to the format used by libinput. +/// +/// Taken from https://sources.debian.org/src/xserver-xorg-input-libinput/1.1.0-1/src/xf86libinput.c/?hl=1508#L236-L252 +#[cfg(any(feature = "backend_winit", feature = "backend_x11"))] +pub(crate) fn xorg_mouse_to_libinput(xorg: u32) -> u32 { + match xorg { + 0 => 0, + 1 => 0x110, // BTN_LEFT + 2 => 0x112, // BTN_MIDDLE + 3 => 0x111, // BTN_RIGHT + _ => xorg - 8 + 0x113, // BTN_SIZE + } +} diff --git a/src/backend/libinput/mod.rs b/src/backend/libinput/mod.rs index 1677695..98fac9c 100644 --- a/src/backend/libinput/mod.rs +++ b/src/backend/libinput/mod.rs @@ -188,13 +188,8 @@ impl backend::Event for event::pointer::PointerButtonEvent } impl backend::PointerButtonEvent for event::pointer::PointerButtonEvent { - fn button(&self) -> backend::MouseButton { - match self.button() { - 0x110 => backend::MouseButton::Left, - 0x111 => backend::MouseButton::Right, - 0x112 => backend::MouseButton::Middle, - x => backend::MouseButton::Other(x as u8), - } + fn button_code(&self) -> u32 { + self.button() } fn state(&self) -> backend::ButtonState { diff --git a/src/backend/winit/input.rs b/src/backend/winit/input.rs index 3debeb9..3fd7c6f 100644 --- a/src/backend/winit/input.rs +++ b/src/backend/winit/input.rs @@ -6,9 +6,9 @@ use winit::{ }; use crate::backend::input::{ - Axis, AxisSource, ButtonState, Device, DeviceCapability, Event, InputBackend, InputEvent, KeyState, - KeyboardKeyEvent, MouseButton, PointerAxisEvent, PointerButtonEvent, PointerMotionAbsoluteEvent, - TouchCancelEvent, TouchDownEvent, TouchMotionEvent, TouchSlot, TouchUpEvent, UnusedEvent, + self, Axis, AxisSource, ButtonState, Device, DeviceCapability, Event, InputBackend, InputEvent, KeyState, + KeyboardKeyEvent, PointerAxisEvent, PointerButtonEvent, PointerMotionAbsoluteEvent, TouchCancelEvent, + TouchDownEvent, TouchMotionEvent, TouchSlot, TouchUpEvent, UnusedEvent, }; use super::{WindowSize, WinitError}; @@ -170,6 +170,7 @@ pub struct WinitMouseInputEvent { pub(crate) time: u32, pub(crate) button: WinitMouseButton, pub(crate) state: ElementState, + pub(crate) is_x11: bool, } impl Event for WinitMouseInputEvent { @@ -183,8 +184,19 @@ impl Event for WinitMouseInputEvent { } impl PointerButtonEvent for WinitMouseInputEvent { - fn button(&self) -> MouseButton { - self.button.into() + fn button_code(&self) -> u32 { + match self.button { + WinitMouseButton::Left => 0x110, + WinitMouseButton::Right => 0x111, + WinitMouseButton::Middle => 0x112, + WinitMouseButton::Other(b) => { + if self.is_x11 { + input::xorg_mouse_to_libinput(b as u32) + } else { + b as u32 + } + } + } } fn state(&self) -> ButtonState { @@ -332,17 +344,6 @@ impl TouchCancelEvent for WinitTouchCancelledEvent { } } -impl From for MouseButton { - fn from(button: WinitMouseButton) -> MouseButton { - match button { - WinitMouseButton::Left => MouseButton::Left, - WinitMouseButton::Right => MouseButton::Right, - WinitMouseButton::Middle => MouseButton::Middle, - WinitMouseButton::Other(num) => MouseButton::Other(num as u8), - } - } -} - impl From for KeyState { fn from(state: ElementState) -> Self { match state { diff --git a/src/backend/winit/mod.rs b/src/backend/winit/mod.rs index f32e9a4..9a5148b 100644 --- a/src/backend/winit/mod.rs +++ b/src/backend/winit/mod.rs @@ -106,6 +106,8 @@ pub struct WinitEventLoop { initialized: bool, size: Rc>, resize_notification: Rc>>>, + /// Whether winit is using Wayland or X11 as it's backend. + is_x11: bool, } /// Create a new [`WinitGraphicsBackend`], which implements the [`Renderer`] trait and a corresponding @@ -164,41 +166,47 @@ where debug!(log, "Window created"); let reqs = Default::default(); - let (display, context, surface) = { + let (display, context, surface, is_x11) = { let display = EGLDisplay::new(&winit_window, log.clone())?; let context = EGLContext::new_with_config(&display, attributes, reqs, log.clone())?; - let surface = if let Some(wl_surface) = winit_window.wayland_surface() { + let (surface, is_x11) = if let Some(wl_surface) = winit_window.wayland_surface() { debug!(log, "Winit backend: Wayland"); let size = winit_window.inner_size(); let surface = unsafe { wegl::WlEglSurface::new_from_raw(wl_surface as *mut _, size.width as i32, size.height as i32) }; - EGLSurface::new( - &display, - context.pixel_format().unwrap(), - context.config_id(), - surface, - log.clone(), + ( + EGLSurface::new( + &display, + context.pixel_format().unwrap(), + context.config_id(), + surface, + log.clone(), + ) + .map_err(EGLError::CreationFailed)?, + false, ) - .map_err(EGLError::CreationFailed)? } else if let Some(xlib_window) = winit_window.xlib_window().map(native::XlibWindow) { debug!(log, "Winit backend: X11"); - EGLSurface::new( - &display, - context.pixel_format().unwrap(), - context.config_id(), - xlib_window, - log.clone(), + ( + EGLSurface::new( + &display, + context.pixel_format().unwrap(), + context.config_id(), + xlib_window, + log.clone(), + ) + .map_err(EGLError::CreationFailed)?, + true, ) - .map_err(EGLError::CreationFailed)? } else { unreachable!("No backends for winit other then Wayland and X11 are supported") }; let _ = context.unbind(); - (display, context, surface) + (display, context, surface, is_x11) }; let (w, h): (u32, u32) = winit_window.inner_size().into(); @@ -230,6 +238,7 @@ where initialized: false, logger: log.new(o!("smithay_winit_component" => "event_loop")), size, + is_x11, }, )) } @@ -338,6 +347,7 @@ impl WinitEventLoop { let resize_notification = &self.resize_notification; let logger = &self.logger; let window_size = &self.size; + let is_x11 = self.is_x11; if !self.initialized { callback(Input(InputEvent::DeviceAdded { @@ -428,7 +438,12 @@ impl WinitEventLoop { } WindowEvent::MouseInput { state, button, .. } => { callback(Input(InputEvent::PointerButton { - event: WinitMouseInputEvent { time, button, state }, + event: WinitMouseInputEvent { + time, + button, + state, + is_x11, + }, })); } diff --git a/src/backend/x11/input.rs b/src/backend/x11/input.rs index 3a5ce20..547ab8b 100644 --- a/src/backend/x11/input.rs +++ b/src/backend/x11/input.rs @@ -4,8 +4,7 @@ use super::X11Error; use crate::{ backend::input::{ self, Axis, AxisSource, ButtonState, Device, DeviceCapability, InputBackend, InputEvent, KeyState, - KeyboardKeyEvent, MouseButton, PointerAxisEvent, PointerButtonEvent, PointerMotionAbsoluteEvent, - UnusedEvent, + KeyboardKeyEvent, PointerAxisEvent, PointerButtonEvent, PointerMotionAbsoluteEvent, UnusedEvent, }, utils::{Logical, Size}, }; @@ -120,7 +119,7 @@ impl PointerAxisEvent for X11MouseWheelEvent { #[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)] pub struct X11MouseInputEvent { pub(crate) time: u32, - pub(crate) button: MouseButton, + pub(crate) raw: u32, pub(crate) state: ButtonState, } @@ -135,8 +134,8 @@ impl input::Event for X11MouseInputEvent { } impl PointerButtonEvent for X11MouseInputEvent { - fn button(&self) -> MouseButton { - self.button + fn button_code(&self) -> u32 { + input::xorg_mouse_to_libinput(self.raw) } fn state(&self) -> ButtonState { diff --git a/src/backend/x11/mod.rs b/src/backend/x11/mod.rs index d5cab01..a00b160 100644 --- a/src/backend/x11/mod.rs +++ b/src/backend/x11/mod.rs @@ -59,7 +59,7 @@ use crate::{ backend::{ allocator::dmabuf::{AsDmabuf, Dmabuf}, drm::{DrmNode, NodeType}, - input::{Axis, ButtonState, InputEvent, KeyState, MouseButton}, + input::{Axis, ButtonState, InputEvent, KeyState}, }, utils::{x11rb::X11Source, Logical, Size}, }; @@ -625,115 +625,69 @@ impl EventSource for X11Backend { // 6 => Axis::Horizontal -1.0 // 7 => Axis::Horizontal +1.0 // Others => ?? - match button_press.detail { - 1..=3 => { - // Clicking a button. - callback( - Input(InputEvent::PointerButton { - event: X11MouseInputEvent { - time: button_press.time, - button: match button_press.detail { - 1 => MouseButton::Left, - // Confusion: XCB docs for ButtonIndex and what plasma does don't match? - 2 => MouseButton::Middle, + // Scrolling + if button_press.detail >= 4 && button_press.detail <= 7 { + callback( + Input(InputEvent::PointerAxis { + event: X11MouseWheelEvent { + time: button_press.time, + axis: match button_press.detail { + // Up | Down + 4 | 5 => Axis::Vertical, - 3 => MouseButton::Right, + // Right | Left + 6 | 7 => Axis::Horizontal, - _ => unreachable!(), - }, - state: ButtonState::Pressed, + _ => unreachable!(), }, - }), - &mut event_window, - ) - } + amount: match button_press.detail { + // Up | Right + 4 | 7 => 1.0, - 4..=7 => { - // Scrolling - callback( - Input(InputEvent::PointerAxis { - event: X11MouseWheelEvent { - time: button_press.time, - axis: match button_press.detail { - // Up | Down - 4 | 5 => Axis::Vertical, + // Down | Left + 5 | 6 => -1.0, - // Right | Left - 6 | 7 => Axis::Horizontal, - - _ => unreachable!(), - }, - amount: match button_press.detail { - // Up | Right - 4 | 7 => 1.0, - - // Down | Left - 5 | 6 => -1.0, - - _ => unreachable!(), - }, + _ => unreachable!(), }, - }), - &mut event_window, - ) - } - - // Unknown mouse button - _ => callback( + }, + }), + &mut event_window, + ) + } else { + callback( Input(InputEvent::PointerButton { event: X11MouseInputEvent { time: button_press.time, - button: MouseButton::Other(button_press.detail), + raw: button_press.detail as u32, state: ButtonState::Pressed, }, }), &mut event_window, - ), + ) } } } x11::Event::ButtonRelease(button_release) => { if button_release.event == window.id { - match button_release.detail { - 1..=3 => { - // Releasing a button. - callback( - Input(InputEvent::PointerButton { - event: X11MouseInputEvent { - time: button_release.time, - button: match button_release.detail { - 1 => MouseButton::Left, - - 2 => MouseButton::Middle, - - 3 => MouseButton::Right, - - _ => unreachable!(), - }, - state: ButtonState::Released, - }, - }), - &mut event_window, - ) - } - - // We may ignore the release tick for scrolling, as the X server will - // always emit this immediately after press. - 4..=7 => (), - - _ => callback( - Input(InputEvent::PointerButton { - event: X11MouseInputEvent { - time: button_release.time, - button: MouseButton::Other(button_release.detail), - state: ButtonState::Released, - }, - }), - &mut event_window, - ), + // Ignore release tick because this event is always sent immediately after the press + // tick for scrolling and the backend will dispatch release event automatically during + // the press event. + if button_release.detail >= 4 && button_release.detail <= 7 { + return; } + + callback( + Input(InputEvent::PointerButton { + event: X11MouseInputEvent { + time: button_release.time, + raw: button_release.detail as u32, + state: ButtonState::Released, + }, + }), + &mut event_window, + ); } }