linux: scrolling improvements (#9103)

This PR adjusts scrolling to be a lot faster on Linux and also makes
terminal scrolling work.

For Wayland, it makes scrolling faster by handling the `AxisValue120`
event (which also allows high-resolution scrolling on supported mice)
On X11, changed the 1 line per scroll to 3.

### Different solutions

I tried replicating Chromium's scrolling behaviour, but it was
inconsistent in X11/Wayland and found it too fast on Wayland. Plus, it
also didn't match VSCode, since it seems that they do something
different.

Release Notes:

- Made scrolling faster on Linux
- Made terminal scroll on Linux
This commit is contained in:
apricotbucket28 2024-03-18 18:50:29 -03:00 committed by GitHub
parent c0f8581b29
commit d8e32c3e3c
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
4 changed files with 68 additions and 10 deletions

View file

@ -30,6 +30,8 @@ use crate::{
use super::x11::X11Client; use super::x11::X11Client;
pub(super) const SCROLL_LINES: f64 = 3.0;
#[derive(Default)] #[derive(Default)]
pub(crate) struct Callbacks { pub(crate) struct Callbacks {
open_urls: Option<Box<dyn FnMut(Vec<String>)>>, open_urls: Option<Box<dyn FnMut(Vec<String>)>>,

View file

@ -14,7 +14,7 @@ use wayland_backend::protocol::WEnum;
use wayland_client::globals::{registry_queue_init, GlobalListContents}; use wayland_client::globals::{registry_queue_init, GlobalListContents};
use wayland_client::protocol::wl_callback::WlCallback; use wayland_client::protocol::wl_callback::WlCallback;
use wayland_client::protocol::wl_output; use wayland_client::protocol::wl_output;
use wayland_client::protocol::wl_pointer::AxisRelativeDirection; use wayland_client::protocol::wl_pointer::{AxisRelativeDirection, AxisSource};
use wayland_client::{ use wayland_client::{
delegate_noop, delegate_noop,
protocol::{ protocol::{
@ -64,6 +64,7 @@ pub(crate) struct WaylandClientStateInner {
repeat: KeyRepeat, repeat: KeyRepeat,
modifiers: Modifiers, modifiers: Modifiers,
scroll_direction: f64, scroll_direction: f64,
axis_source: AxisSource,
mouse_location: Option<Point<Pixels>>, mouse_location: Option<Point<Pixels>>,
button_pressed: Option<MouseButton>, button_pressed: Option<MouseButton>,
mouse_focused_window: Option<Rc<WaylandWindowState>>, mouse_focused_window: Option<Rc<WaylandWindowState>>,
@ -97,9 +98,22 @@ pub(crate) struct WaylandClient {
qh: Arc<QueueHandle<WaylandClientState>>, qh: Arc<QueueHandle<WaylandClientState>>,
} }
const WL_SEAT_VERSION: u32 = 4; const WL_SEAT_MIN_VERSION: u32 = 4;
const WL_OUTPUT_VERSION: u32 = 2; const WL_OUTPUT_VERSION: u32 = 2;
fn wl_seat_version(version: u32) -> u32 {
if version >= wl_pointer::EVT_AXIS_VALUE120_SINCE {
wl_pointer::EVT_AXIS_VALUE120_SINCE
} else if version >= WL_SEAT_MIN_VERSION {
WL_SEAT_MIN_VERSION
} else {
panic!(
"wl_seat below required version: {} < {}",
version, WL_SEAT_MIN_VERSION
);
}
}
impl WaylandClient { impl WaylandClient {
pub(crate) fn new(linux_platform_inner: Rc<LinuxPlatformInner>) -> Self { pub(crate) fn new(linux_platform_inner: Rc<LinuxPlatformInner>) -> Self {
let conn = Connection::connect_to_env().unwrap(); let conn = Connection::connect_to_env().unwrap();
@ -114,7 +128,7 @@ impl WaylandClient {
"wl_seat" => { "wl_seat" => {
globals.registry().bind::<wl_seat::WlSeat, _, _>( globals.registry().bind::<wl_seat::WlSeat, _, _>(
global.name, global.name,
WL_SEAT_VERSION, wl_seat_version(global.version),
&qh, &qh,
(), (),
); );
@ -163,6 +177,7 @@ impl WaylandClient {
command: false, command: false,
}, },
scroll_direction: -1.0, scroll_direction: -1.0,
axis_source: AxisSource::Wheel,
mouse_location: None, mouse_location: None,
button_pressed: None, button_pressed: None,
mouse_focused_window: None, mouse_focused_window: None,
@ -325,10 +340,10 @@ impl Dispatch<wl_registry::WlRegistry, GlobalListContents> for WaylandClientStat
wl_registry::Event::Global { wl_registry::Event::Global {
name, name,
interface, interface,
version: _, version,
} => match &interface[..] { } => match &interface[..] {
"wl_seat" => { "wl_seat" => {
registry.bind::<wl_seat::WlSeat, _, _>(name, WL_SEAT_VERSION, qh, ()); registry.bind::<wl_seat::WlSeat, _, _>(name, wl_seat_version(version), qh, ());
} }
"wl_output" => { "wl_output" => {
state.outputs.push(( state.outputs.push((
@ -914,12 +929,49 @@ impl Dispatch<wl_pointer::WlPointer, ()> for WaylandClientState {
_ => -1.0, _ => -1.0,
} }
} }
wl_pointer::Event::AxisSource {
axis_source: WEnum::Value(axis_source),
} => {
state.axis_source = axis_source;
}
wl_pointer::Event::AxisValue120 {
axis: WEnum::Value(axis),
value120,
} => {
let focused_window = &state.mouse_focused_window;
let mouse_location = &state.mouse_location;
if let (Some(focused_window), Some(mouse_location)) =
(focused_window, mouse_location)
{
let value = value120 as f64 * state.scroll_direction;
focused_window.handle_input(PlatformInput::ScrollWheel(ScrollWheelEvent {
position: *mouse_location,
delta: match axis {
wl_pointer::Axis::VerticalScroll => {
ScrollDelta::Pixels(Point::new(Pixels(0.0), Pixels(value as f32)))
}
wl_pointer::Axis::HorizontalScroll => {
ScrollDelta::Pixels(Point::new(Pixels(value as f32), Pixels(0.0)))
}
_ => unimplemented!(),
},
modifiers: state.modifiers,
touch_phase: TouchPhase::Moved,
}))
}
}
wl_pointer::Event::Axis { wl_pointer::Event::Axis {
time, time,
axis: WEnum::Value(axis), axis: WEnum::Value(axis),
value, value,
.. ..
} => { } => {
if wl_pointer.version() >= wl_pointer::EVT_AXIS_VALUE120_SINCE
&& state.axis_source != AxisSource::Continuous
{
return;
}
let focused_window = &state.mouse_focused_window; let focused_window = &state.mouse_focused_window;
let mouse_location = &state.mouse_location; let mouse_location = &state.mouse_location;
if let (Some(focused_window), Some(mouse_location)) = if let (Some(focused_window), Some(mouse_location)) =
@ -938,7 +990,7 @@ impl Dispatch<wl_pointer::WlPointer, ()> for WaylandClientState {
_ => unimplemented!(), _ => unimplemented!(),
}, },
modifiers: state.modifiers, modifiers: state.modifiers,
touch_phase: TouchPhase::Started, touch_phase: TouchPhase::Moved,
})) }))
} }
} }

View file

@ -23,7 +23,7 @@ use crate::{
ScrollDelta, Size, TouchPhase, WindowParams, ScrollDelta, Size, TouchPhase, WindowParams,
}; };
use super::{X11Display, X11Window, X11WindowState, XcbAtoms}; use super::{super::SCROLL_LINES, X11Display, X11Window, X11WindowState, XcbAtoms};
use calloop::{ use calloop::{
generic::{FdWrapper, Generic}, generic::{FdWrapper, Generic},
RegistrationToken, RegistrationToken,
@ -221,12 +221,13 @@ impl X11Client {
})); }));
} else if event.detail >= 4 && event.detail <= 5 { } else if event.detail >= 4 && event.detail <= 5 {
// https://stackoverflow.com/questions/15510472/scrollwheel-event-in-x11 // https://stackoverflow.com/questions/15510472/scrollwheel-event-in-x11
let delta_x = if event.detail == 4 { 1.0 } else { -1.0 }; let scroll_direction = if event.detail == 4 { 1.0 } else { -1.0 };
let scroll_y = SCROLL_LINES * scroll_direction;
window.handle_input(PlatformInput::ScrollWheel(crate::ScrollWheelEvent { window.handle_input(PlatformInput::ScrollWheel(crate::ScrollWheelEvent {
position, position,
delta: ScrollDelta::Lines(Point::new(0.0, delta_x)), delta: ScrollDelta::Lines(Point::new(0.0, scroll_y as f32)),
modifiers, modifiers,
touch_phase: TouchPhase::default(), touch_phase: TouchPhase::Moved,
})); }));
} else { } else {
log::warn!("Unknown button press: {event:?}"); log::warn!("Unknown button press: {event:?}");

View file

@ -70,7 +70,10 @@ actions!(
///Scrolling is unbearably sluggish by default. Alacritty supports a configurable ///Scrolling is unbearably sluggish by default. Alacritty supports a configurable
///Scroll multiplier that is set to 3 by default. This will be removed when I ///Scroll multiplier that is set to 3 by default. This will be removed when I
///Implement scroll bars. ///Implement scroll bars.
#[cfg(target_os = "macos")]
const SCROLL_MULTIPLIER: f32 = 4.; const SCROLL_MULTIPLIER: f32 = 4.;
#[cfg(not(target_os = "macos"))]
const SCROLL_MULTIPLIER: f32 = 1.;
const MAX_SEARCH_LINES: usize = 100; const MAX_SEARCH_LINES: usize = 100;
const DEBUG_TERMINAL_WIDTH: Pixels = px(500.); const DEBUG_TERMINAL_WIDTH: Pixels = px(500.);
const DEBUG_TERMINAL_HEIGHT: Pixels = px(30.); const DEBUG_TERMINAL_HEIGHT: Pixels = px(30.);