From 4c60cf34950b1171d31d4325abf6398173b7b520 Mon Sep 17 00:00:00 2001 From: Michael Sloan Date: Tue, 24 Sep 2024 21:33:03 -0600 Subject: [PATCH] Add support for xinput scrolling via button press events --- crates/gpui/src/platform/linux/platform.rs | 2 +- .../gpui/src/platform/linux/wayland/client.rs | 8 +- crates/gpui/src/platform/linux/x11/client.rs | 125 ++++++++++++------ crates/gpui/src/platform/linux/x11/event.rs | 28 +++- 4 files changed, 112 insertions(+), 51 deletions(-) diff --git a/crates/gpui/src/platform/linux/platform.rs b/crates/gpui/src/platform/linux/platform.rs index 67f1a43cbe322..6e09badb493a6 100644 --- a/crates/gpui/src/platform/linux/platform.rs +++ b/crates/gpui/src/platform/linux/platform.rs @@ -45,7 +45,7 @@ use crate::{ use super::x11::X11Client; -pub(crate) const SCROLL_LINES: f64 = 3.0; +pub(crate) const SCROLL_LINES: f32 = 3.0; // Values match the defaults on GTK. // Taken from https://github.com/GNOME/gtk/blob/main/gtk/gtksettings.c#L320 diff --git a/crates/gpui/src/platform/linux/wayland/client.rs b/crates/gpui/src/platform/linux/wayland/client.rs index 4b7816a73ac36..f0015a7e5820b 100644 --- a/crates/gpui/src/platform/linux/wayland/client.rs +++ b/crates/gpui/src/platform/linux/wayland/client.rs @@ -1634,10 +1634,10 @@ impl Dispatch for WaylandClientStatePtr { let scroll_delta = state.discrete_scroll_delta.get_or_insert(point(0.0, 0.0)); match axis { wl_pointer::Axis::VerticalScroll => { - scroll_delta.y += discrete as f32 * axis_modifier * SCROLL_LINES as f32; + scroll_delta.y += discrete as f32 * axis_modifier * SCROLL_LINES; } wl_pointer::Axis::HorizontalScroll => { - scroll_delta.x += discrete as f32 * axis_modifier * SCROLL_LINES as f32; + scroll_delta.x += discrete as f32 * axis_modifier * SCROLL_LINES; } _ => unreachable!(), } @@ -1662,10 +1662,10 @@ impl Dispatch for WaylandClientStatePtr { let wheel_percent = value120 as f32 / 120.0; match axis { wl_pointer::Axis::VerticalScroll => { - scroll_delta.y += wheel_percent * axis_modifier * SCROLL_LINES as f32; + scroll_delta.y += wheel_percent * axis_modifier * SCROLL_LINES; } wl_pointer::Axis::HorizontalScroll => { - scroll_delta.x += wheel_percent * axis_modifier * SCROLL_LINES as f32; + scroll_delta.x += wheel_percent * axis_modifier * SCROLL_LINES; } _ => unreachable!(), } diff --git a/crates/gpui/src/platform/linux/x11/client.rs b/crates/gpui/src/platform/linux/x11/client.rs index 5339cc95fd1b1..8f9b7e3570723 100644 --- a/crates/gpui/src/platform/linux/x11/client.rs +++ b/crates/gpui/src/platform/linux/x11/client.rs @@ -42,7 +42,10 @@ use crate::{ WindowParams, X11Window, }; -use super::{button_of_key, modifiers_from_state, pressed_button_from_mask}; +use super::{ + button_or_scroll_from_event_detail, modifiers_from_state, pressed_button_from_mask, + ButtonOrScroll, ScrollDirection, +}; use super::{X11Display, X11WindowStatePtr, XcbAtoms}; use super::{XimCallbackEvent, XimHandler}; use crate::platform::linux::platform::{DOUBLE_CLICK_INTERVAL, SCROLL_LINES}; @@ -950,35 +953,73 @@ impl X11Client { window.handle_ime_commit(text); state = self.0.borrow_mut(); } - if let Some(button) = button_of_key(event.detail.try_into().unwrap()) { - let click_elapsed = state.last_click.elapsed(); - - if click_elapsed < DOUBLE_CLICK_INTERVAL - && state - .last_mouse_button - .is_some_and(|prev_button| prev_button == button) - && is_within_click_distance(state.last_location, position) - { - state.current_count += 1; - } else { - state.current_count = 1; - } - - state.last_click = Instant::now(); - state.last_mouse_button = Some(button); - state.last_location = position; - let current_count = state.current_count; + match button_or_scroll_from_event_detail(event.detail) { + Some(ButtonOrScroll::Button(button)) => { + let click_elapsed = state.last_click.elapsed(); + if click_elapsed < DOUBLE_CLICK_INTERVAL + && state + .last_mouse_button + .is_some_and(|prev_button| prev_button == button) + && is_within_click_distance(state.last_location, position) + { + state.current_count += 1; + } else { + state.current_count = 1; + } - drop(state); - window.handle_input(PlatformInput::MouseDown(crate::MouseDownEvent { - button, - position, - modifiers, - click_count: current_count, - first_mouse: false, - })); - } else { - log::warn!("Unknown button press: {event:?}"); + state.last_click = Instant::now(); + state.last_mouse_button = Some(button); + state.last_location = position; + let current_count = state.current_count; + + drop(state); + window.handle_input(PlatformInput::MouseDown(crate::MouseDownEvent { + button, + position, + modifiers, + click_count: current_count, + first_mouse: false, + })); + } + Some(ButtonOrScroll::Scroll(direction)) => { + drop(state); + // Emulated scroll button presses are sent simultaneously with smooth scrolling XinputMotion events. + // Since handling those events does the scrolling, they are skipped here. + if !event + .flags + .contains(xinput::PointerEventFlags::POINTER_EMULATED) + { + let delta = match direction { + ScrollDirection::Up => { + if modifiers.shift { + Point::new(SCROLL_LINES, 0.0) + } else { + Point::new(0.0, SCROLL_LINES) + } + } + ScrollDirection::Down => { + if modifiers.shift { + Point::new(-SCROLL_LINES, 0.0) + } else { + Point::new(0.0, -SCROLL_LINES) + } + } + ScrollDirection::Left => Point::new(SCROLL_LINES, 0.0), + ScrollDirection::Right => Point::new(-SCROLL_LINES, 0.0), + }; + window.handle_input(PlatformInput::ScrollWheel( + crate::ScrollWheelEvent { + position, + delta: ScrollDelta::Lines(delta), + modifiers, + touch_phase: TouchPhase::default(), + }, + )); + } + } + None => { + log::error!("Unknown x11 button: {}", event.detail); + } } } Event::XinputButtonRelease(event) => { @@ -991,15 +1032,19 @@ impl X11Client { px(event.event_x as f32 / u16::MAX as f32 / state.scale_factor), px(event.event_y as f32 / u16::MAX as f32 / state.scale_factor), ); - if let Some(button) = button_of_key(event.detail.try_into().unwrap()) { - let click_count = state.current_count; - drop(state); - window.handle_input(PlatformInput::MouseUp(crate::MouseUpEvent { - button, - position, - modifiers, - click_count, - })); + match button_or_scroll_from_event_detail(event.detail) { + Some(ButtonOrScroll::Button(button)) => { + let click_count = state.current_count; + drop(state); + window.handle_input(PlatformInput::MouseUp(crate::MouseUpEvent { + button, + position, + modifiers, + click_count, + })); + } + Some(ButtonOrScroll::Scroll(_)) => {} + None => {} } } Event::XinputMotion(event) => { @@ -1041,7 +1086,7 @@ impl X11Client { { let new_scroll = axisvalues[valuator_idx] / fp3232_to_f32(scroll_class.increment) - * SCROLL_LINES as f32; + * SCROLL_LINES; let old_scroll = self.0.borrow().scroll_x; self.0.borrow_mut().scroll_x = Some(new_scroll); @@ -1062,7 +1107,7 @@ impl X11Client { // the `increment` is the valuator delta equivalent to one positive unit of scrolling. Here that means SCROLL_LINES lines. let new_scroll = axisvalues[valuator_idx] / fp3232_to_f32(scroll_class.increment) - * SCROLL_LINES as f32; + * SCROLL_LINES; let old_scroll = self.0.borrow().scroll_y; self.0.borrow_mut().scroll_y = Some(new_scroll); diff --git a/crates/gpui/src/platform/linux/x11/event.rs b/crates/gpui/src/platform/linux/x11/event.rs index 18ec392fc657e..4a4a179b48ab9 100644 --- a/crates/gpui/src/platform/linux/x11/event.rs +++ b/crates/gpui/src/platform/linux/x11/event.rs @@ -5,13 +5,29 @@ use x11rb::protocol::{ use crate::{Modifiers, MouseButton, NavigationDirection}; -pub(crate) fn button_of_key(detail: xproto::Button) -> Option { +pub(crate) enum ButtonOrScroll { + Button(MouseButton), + Scroll(ScrollDirection), +} + +pub(crate) enum ScrollDirection { + Up, + Down, + Left, + Right, +} + +pub(crate) fn button_or_scroll_from_event_detail(detail: u32) -> Option { Some(match detail { - 1 => MouseButton::Left, - 2 => MouseButton::Middle, - 3 => MouseButton::Right, - 8 => MouseButton::Navigate(NavigationDirection::Back), - 9 => MouseButton::Navigate(NavigationDirection::Forward), + 1 => ButtonOrScroll::Button(MouseButton::Left), + 2 => ButtonOrScroll::Button(MouseButton::Middle), + 3 => ButtonOrScroll::Button(MouseButton::Right), + 4 => ButtonOrScroll::Scroll(ScrollDirection::Up), + 5 => ButtonOrScroll::Scroll(ScrollDirection::Down), + 6 => ButtonOrScroll::Scroll(ScrollDirection::Left), + 7 => ButtonOrScroll::Scroll(ScrollDirection::Right), + 8 => ButtonOrScroll::Button(MouseButton::Navigate(NavigationDirection::Back)), + 9 => ButtonOrScroll::Button(MouseButton::Navigate(NavigationDirection::Forward)), _ => return None, }) }