Add Forward/Back mouse buttons

This commit is contained in:
i509VCB 2021-10-27 12:23:00 -05:00
parent e1400a1926
commit 3283010d2c
8 changed files with 147 additions and 146 deletions

View File

@ -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>`.
- `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

View File

@ -86,12 +86,7 @@ impl<Backend> AnvilState<Backend> {
fn on_pointer_button<B: InputBackend>(&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

View File

@ -117,6 +117,7 @@ impl<B: InputBackend> KeyboardKeyEvent<B> 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<B: InputBackend>: Event<B> {
/// 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<MouseButton> {
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<B: InputBackend> PointerButtonEvent<B> for UnusedEvent {
fn button(&self) -> MouseButton {
fn button_code(&self) -> u32 {
match *self {}
}
@ -620,3 +644,17 @@ pub enum InputEvent<B: InputBackend> {
/// 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
}
}

View File

@ -188,13 +188,8 @@ impl backend::Event<LibinputInputBackend> for event::pointer::PointerButtonEvent
}
impl backend::PointerButtonEvent<LibinputInputBackend> 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 {

View File

@ -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<WinitInput> for WinitMouseInputEvent {
@ -183,8 +184,19 @@ impl Event<WinitInput> for WinitMouseInputEvent {
}
impl PointerButtonEvent<WinitInput> 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<WinitInput> for WinitTouchCancelledEvent {
}
}
impl From<WinitMouseButton> 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<ElementState> for KeyState {
fn from(state: ElementState) -> Self {
match state {

View File

@ -106,6 +106,8 @@ pub struct WinitEventLoop {
initialized: bool,
size: Rc<RefCell<WindowSize>>,
resize_notification: Rc<Cell<Option<Size<i32, Physical>>>>,
/// 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,
},
}));
}

View File

@ -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<X11Input> 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<X11Input> for X11MouseInputEvent {
}
impl PointerButtonEvent<X11Input> for X11MouseInputEvent {
fn button(&self) -> MouseButton {
self.button
fn button_code(&self) -> u32 {
input::xorg_mouse_to_libinput(self.raw)
}
fn state(&self) -> ButtonState {

View File

@ -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,
);
}
}