diff --git a/crates/gpui/src/platform/linux/wayland/client.rs b/crates/gpui/src/platform/linux/wayland/client.rs index 69395945bfe4df..7439faaacb042d 100644 --- a/crates/gpui/src/platform/linux/wayland/client.rs +++ b/crates/gpui/src/platform/linux/wayland/client.rs @@ -103,9 +103,13 @@ pub(crate) struct WaylandClientState { click: ClickState, repeat: KeyRepeat, modifiers: Modifiers, - scroll_direction: f64, axis_source: AxisSource, mouse_location: Option>, + continuous_scroll_delta: Option>, + discrete_scroll_delta: Option>, + vertical_modifier: f32, + horizontal_modifier: f32, + scroll_event_received: bool, enter_token: Option<()>, button_pressed: Option, mouse_focused_window: Option, @@ -164,20 +168,21 @@ impl WaylandClientStatePtr { #[derive(Clone)] pub struct WaylandClient(Rc>); -const WL_SEAT_MIN_VERSION: u32 = 4; 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 { + // We rely on the wl_pointer.frame event + const WL_SEAT_MIN_VERSION: u32 = 5; + const WL_SEAT_MAX_VERSION: u32 = 9; + + if version < WL_SEAT_MIN_VERSION { panic!( "wl_seat below required version: {} < {}", version, WL_SEAT_MIN_VERSION ); } + + version.clamp(WL_SEAT_MIN_VERSION, WL_SEAT_MAX_VERSION) } impl WaylandClient { @@ -257,9 +262,13 @@ impl WaylandClient { function: false, platform: false, }, - scroll_direction: -1.0, + scroll_event_received: false, axis_source: AxisSource::Wheel, mouse_location: None, + continuous_scroll_delta: None, + discrete_scroll_delta: None, + vertical_modifier: -1.0, + horizontal_modifier: -1.0, button_pressed: None, mouse_focused_window: None, keyboard_focused_window: None, @@ -887,77 +896,137 @@ impl Dispatch for WaylandClientStatePtr { _ => {} } } - wl_pointer::Event::AxisRelativeDirection { - direction: WEnum::Value(direction), - .. - } => { - state.scroll_direction = match direction { - AxisRelativeDirection::Identical => -1.0, - AxisRelativeDirection::Inverted => 1.0, - _ => -1.0, - } - } + + // Axis Events wl_pointer::Event::AxisSource { axis_source: WEnum::Value(axis_source), } => { state.axis_source = axis_source; } - wl_pointer::Event::AxisValue120 { - axis: WEnum::Value(axis), - value120, - } => { - if let Some(focused_window) = state.mouse_focused_window.clone() { - let value = value120 as f64 * state.scroll_direction; - - let input = PlatformInput::ScrollWheel(ScrollWheelEvent { - position: state.mouse_location.unwrap(), - delta: match axis { - wl_pointer::Axis::VerticalScroll => { - ScrollDelta::Pixels(point(px(0.0), px(value as f32))) - } - wl_pointer::Axis::HorizontalScroll => { - ScrollDelta::Pixels(point(px(value as f32), px(0.0))) - } - _ => unimplemented!(), - }, - modifiers: state.modifiers, - touch_phase: TouchPhase::Moved, - }); - drop(state); - focused_window.handle_input(input) - } - } wl_pointer::Event::Axis { time, axis: WEnum::Value(axis), value, .. } => { - // We handle discrete scroll events with `AxisValue120`. - if wl_pointer.version() >= wl_pointer::EVT_AXIS_VALUE120_SINCE - && state.axis_source == AxisSource::Wheel - { - return; + let axis_source = state.axis_source; + let axis_modifier = match axis { + wl_pointer::Axis::VerticalScroll => state.vertical_modifier, + wl_pointer::Axis::HorizontalScroll => state.horizontal_modifier, + _ => 1.0, + }; + let supports_relative_direction = + wl_pointer.version() >= wl_pointer::EVT_AXIS_RELATIVE_DIRECTION_SINCE; + state.scroll_event_received = true; + let scroll_delta = state + .continuous_scroll_delta + .get_or_insert(point(px(0.0), px(0.0))); + // TODO: Make nice feeling kinetic scrolling that integrates with the platform's scroll settings + let modifier = 3.0; + match axis { + wl_pointer::Axis::VerticalScroll => { + scroll_delta.y += px(value as f32 * modifier * axis_modifier); + } + wl_pointer::Axis::HorizontalScroll => { + scroll_delta.x += px(value as f32 * modifier * axis_modifier); + } + _ => unreachable!(), } - if let Some(focused_window) = state.mouse_focused_window.clone() { - let value = value * state.scroll_direction; + } + wl_pointer::Event::AxisDiscrete { + axis: WEnum::Value(axis), + discrete, + } => { + state.scroll_event_received = true; + let axis_modifier = match axis { + wl_pointer::Axis::VerticalScroll => state.vertical_modifier, + wl_pointer::Axis::HorizontalScroll => state.horizontal_modifier, + _ => 1.0, + }; - let input = PlatformInput::ScrollWheel(ScrollWheelEvent { - position: state.mouse_location.unwrap(), - delta: match axis { - wl_pointer::Axis::VerticalScroll => { - ScrollDelta::Pixels(point(px(0.0), px(value as f32))) - } - wl_pointer::Axis::HorizontalScroll => { - ScrollDelta::Pixels(point(px(value as f32), px(0.0))) - } - _ => unimplemented!(), - }, - modifiers: state.modifiers, - touch_phase: TouchPhase::Moved, - }); - drop(state); - focused_window.handle_input(input) + // TODO: Make nice feeling kinetic scrolling that integrates with the platform's scroll settings + let modifier = 3.0; + + 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 * modifier; + } + wl_pointer::Axis::HorizontalScroll => { + scroll_delta.x += discrete as f32 * axis_modifier * modifier; + } + _ => unreachable!(), + } + } + wl_pointer::Event::AxisRelativeDirection { + axis: WEnum::Value(axis), + direction: WEnum::Value(direction), + } => match (axis, direction) { + (wl_pointer::Axis::VerticalScroll, AxisRelativeDirection::Identical) => { + state.vertical_modifier = -1.0 + } + (wl_pointer::Axis::VerticalScroll, AxisRelativeDirection::Inverted) => { + state.vertical_modifier = 1.0 + } + (wl_pointer::Axis::HorizontalScroll, AxisRelativeDirection::Identical) => { + state.horizontal_modifier = -1.0 + } + (wl_pointer::Axis::HorizontalScroll, AxisRelativeDirection::Inverted) => { + state.horizontal_modifier = 1.0 + } + _ => unreachable!(), + }, + wl_pointer::Event::AxisValue120 { + axis: WEnum::Value(axis), + value120, + } => { + state.scroll_event_received = true; + let axis_modifier = match axis { + wl_pointer::Axis::VerticalScroll => state.vertical_modifier, + wl_pointer::Axis::HorizontalScroll => state.horizontal_modifier, + _ => unreachable!(), + }; + + let scroll_delta = state.discrete_scroll_delta.get_or_insert(point(0.0, 0.0)); + let wheel_percent = value120 as f32 / 120.0; + match axis { + wl_pointer::Axis::VerticalScroll => { + scroll_delta.y += wheel_percent * axis_modifier; + } + wl_pointer::Axis::HorizontalScroll => { + scroll_delta.x += wheel_percent * axis_modifier; + } + _ => unreachable!(), + } + } + wl_pointer::Event::Frame => { + if state.scroll_event_received { + state.scroll_event_received = false; + let continuous = state.continuous_scroll_delta.take(); + let discrete = state.discrete_scroll_delta.take(); + if let Some(continuous) = continuous { + if let Some(window) = state.mouse_focused_window.clone() { + let input = PlatformInput::ScrollWheel(ScrollWheelEvent { + position: state.mouse_location.unwrap(), + delta: ScrollDelta::Pixels(continuous), + modifiers: state.modifiers, + touch_phase: TouchPhase::Moved, + }); + drop(state); + window.handle_input(input); + } + } else if let Some(discrete) = discrete { + if let Some(window) = state.mouse_focused_window.clone() { + let input = PlatformInput::ScrollWheel(ScrollWheelEvent { + position: state.mouse_location.unwrap(), + delta: ScrollDelta::Lines(discrete), + modifiers: state.modifiers, + touch_phase: TouchPhase::Moved, + }); + drop(state); + window.handle_input(input); + } + } } } _ => {}