Skip to content

Commit 3289188

Browse files
authored
linux: Simplify scrolling implementation (zed-industries#10497)
This PR adjusts our scrolling implementation to delay the generation of ScrollWheel events until we receive a complete frame. Note that our implementation is still a bit off-spec, as we don't delay any other kind of events. But it's been working so far on a variety of compositors and the other events contain complete data; so I'll hold off on that refactor for now. Release Notes: - N/A
1 parent 5e4f707 commit 3289188

File tree

1 file changed

+135
-66
lines changed
  • crates/gpui/src/platform/linux/wayland

1 file changed

+135
-66
lines changed

crates/gpui/src/platform/linux/wayland/client.rs

Lines changed: 135 additions & 66 deletions
Original file line numberDiff line numberDiff line change
@@ -103,9 +103,13 @@ pub(crate) struct WaylandClientState {
103103
click: ClickState,
104104
repeat: KeyRepeat,
105105
modifiers: Modifiers,
106-
scroll_direction: f64,
107106
axis_source: AxisSource,
108107
mouse_location: Option<Point<Pixels>>,
108+
continuous_scroll_delta: Option<Point<Pixels>>,
109+
discrete_scroll_delta: Option<Point<f32>>,
110+
vertical_modifier: f32,
111+
horizontal_modifier: f32,
112+
scroll_event_received: bool,
109113
enter_token: Option<()>,
110114
button_pressed: Option<MouseButton>,
111115
mouse_focused_window: Option<WaylandWindowStatePtr>,
@@ -164,20 +168,21 @@ impl WaylandClientStatePtr {
164168
#[derive(Clone)]
165169
pub struct WaylandClient(Rc<RefCell<WaylandClientState>>);
166170

167-
const WL_SEAT_MIN_VERSION: u32 = 4;
168171
const WL_OUTPUT_VERSION: u32 = 2;
169172

170173
fn wl_seat_version(version: u32) -> u32 {
171-
if version >= wl_pointer::EVT_AXIS_VALUE120_SINCE {
172-
wl_pointer::EVT_AXIS_VALUE120_SINCE
173-
} else if version >= WL_SEAT_MIN_VERSION {
174-
WL_SEAT_MIN_VERSION
175-
} else {
174+
// We rely on the wl_pointer.frame event
175+
const WL_SEAT_MIN_VERSION: u32 = 5;
176+
const WL_SEAT_MAX_VERSION: u32 = 9;
177+
178+
if version < WL_SEAT_MIN_VERSION {
176179
panic!(
177180
"wl_seat below required version: {} < {}",
178181
version, WL_SEAT_MIN_VERSION
179182
);
180183
}
184+
185+
version.clamp(WL_SEAT_MIN_VERSION, WL_SEAT_MAX_VERSION)
181186
}
182187

183188
impl WaylandClient {
@@ -257,9 +262,13 @@ impl WaylandClient {
257262
function: false,
258263
platform: false,
259264
},
260-
scroll_direction: -1.0,
265+
scroll_event_received: false,
261266
axis_source: AxisSource::Wheel,
262267
mouse_location: None,
268+
continuous_scroll_delta: None,
269+
discrete_scroll_delta: None,
270+
vertical_modifier: -1.0,
271+
horizontal_modifier: -1.0,
263272
button_pressed: None,
264273
mouse_focused_window: None,
265274
keyboard_focused_window: None,
@@ -887,77 +896,137 @@ impl Dispatch<wl_pointer::WlPointer, ()> for WaylandClientStatePtr {
887896
_ => {}
888897
}
889898
}
890-
wl_pointer::Event::AxisRelativeDirection {
891-
direction: WEnum::Value(direction),
892-
..
893-
} => {
894-
state.scroll_direction = match direction {
895-
AxisRelativeDirection::Identical => -1.0,
896-
AxisRelativeDirection::Inverted => 1.0,
897-
_ => -1.0,
898-
}
899-
}
899+
900+
// Axis Events
900901
wl_pointer::Event::AxisSource {
901902
axis_source: WEnum::Value(axis_source),
902903
} => {
903904
state.axis_source = axis_source;
904905
}
905-
wl_pointer::Event::AxisValue120 {
906-
axis: WEnum::Value(axis),
907-
value120,
908-
} => {
909-
if let Some(focused_window) = state.mouse_focused_window.clone() {
910-
let value = value120 as f64 * state.scroll_direction;
911-
912-
let input = PlatformInput::ScrollWheel(ScrollWheelEvent {
913-
position: state.mouse_location.unwrap(),
914-
delta: match axis {
915-
wl_pointer::Axis::VerticalScroll => {
916-
ScrollDelta::Pixels(point(px(0.0), px(value as f32)))
917-
}
918-
wl_pointer::Axis::HorizontalScroll => {
919-
ScrollDelta::Pixels(point(px(value as f32), px(0.0)))
920-
}
921-
_ => unimplemented!(),
922-
},
923-
modifiers: state.modifiers,
924-
touch_phase: TouchPhase::Moved,
925-
});
926-
drop(state);
927-
focused_window.handle_input(input)
928-
}
929-
}
930906
wl_pointer::Event::Axis {
931907
time,
932908
axis: WEnum::Value(axis),
933909
value,
934910
..
935911
} => {
936-
// We handle discrete scroll events with `AxisValue120`.
937-
if wl_pointer.version() >= wl_pointer::EVT_AXIS_VALUE120_SINCE
938-
&& state.axis_source == AxisSource::Wheel
939-
{
940-
return;
912+
let axis_source = state.axis_source;
913+
let axis_modifier = match axis {
914+
wl_pointer::Axis::VerticalScroll => state.vertical_modifier,
915+
wl_pointer::Axis::HorizontalScroll => state.horizontal_modifier,
916+
_ => 1.0,
917+
};
918+
let supports_relative_direction =
919+
wl_pointer.version() >= wl_pointer::EVT_AXIS_RELATIVE_DIRECTION_SINCE;
920+
state.scroll_event_received = true;
921+
let scroll_delta = state
922+
.continuous_scroll_delta
923+
.get_or_insert(point(px(0.0), px(0.0)));
924+
// TODO: Make nice feeling kinetic scrolling that integrates with the platform's scroll settings
925+
let modifier = 3.0;
926+
match axis {
927+
wl_pointer::Axis::VerticalScroll => {
928+
scroll_delta.y += px(value as f32 * modifier * axis_modifier);
929+
}
930+
wl_pointer::Axis::HorizontalScroll => {
931+
scroll_delta.x += px(value as f32 * modifier * axis_modifier);
932+
}
933+
_ => unreachable!(),
941934
}
942-
if let Some(focused_window) = state.mouse_focused_window.clone() {
943-
let value = value * state.scroll_direction;
935+
}
936+
wl_pointer::Event::AxisDiscrete {
937+
axis: WEnum::Value(axis),
938+
discrete,
939+
} => {
940+
state.scroll_event_received = true;
941+
let axis_modifier = match axis {
942+
wl_pointer::Axis::VerticalScroll => state.vertical_modifier,
943+
wl_pointer::Axis::HorizontalScroll => state.horizontal_modifier,
944+
_ => 1.0,
945+
};
944946

945-
let input = PlatformInput::ScrollWheel(ScrollWheelEvent {
946-
position: state.mouse_location.unwrap(),
947-
delta: match axis {
948-
wl_pointer::Axis::VerticalScroll => {
949-
ScrollDelta::Pixels(point(px(0.0), px(value as f32)))
950-
}
951-
wl_pointer::Axis::HorizontalScroll => {
952-
ScrollDelta::Pixels(point(px(value as f32), px(0.0)))
953-
}
954-
_ => unimplemented!(),
955-
},
956-
modifiers: state.modifiers,
957-
touch_phase: TouchPhase::Moved,
958-
});
959-
drop(state);
960-
focused_window.handle_input(input)
947+
// TODO: Make nice feeling kinetic scrolling that integrates with the platform's scroll settings
948+
let modifier = 3.0;
949+
950+
let scroll_delta = state.discrete_scroll_delta.get_or_insert(point(0.0, 0.0));
951+
match axis {
952+
wl_pointer::Axis::VerticalScroll => {
953+
scroll_delta.y += discrete as f32 * axis_modifier * modifier;
954+
}
955+
wl_pointer::Axis::HorizontalScroll => {
956+
scroll_delta.x += discrete as f32 * axis_modifier * modifier;
957+
}
958+
_ => unreachable!(),
959+
}
960+
}
961+
wl_pointer::Event::AxisRelativeDirection {
962+
axis: WEnum::Value(axis),
963+
direction: WEnum::Value(direction),
964+
} => match (axis, direction) {
965+
(wl_pointer::Axis::VerticalScroll, AxisRelativeDirection::Identical) => {
966+
state.vertical_modifier = -1.0
967+
}
968+
(wl_pointer::Axis::VerticalScroll, AxisRelativeDirection::Inverted) => {
969+
state.vertical_modifier = 1.0
970+
}
971+
(wl_pointer::Axis::HorizontalScroll, AxisRelativeDirection::Identical) => {
972+
state.horizontal_modifier = -1.0
973+
}
974+
(wl_pointer::Axis::HorizontalScroll, AxisRelativeDirection::Inverted) => {
975+
state.horizontal_modifier = 1.0
976+
}
977+
_ => unreachable!(),
978+
},
979+
wl_pointer::Event::AxisValue120 {
980+
axis: WEnum::Value(axis),
981+
value120,
982+
} => {
983+
state.scroll_event_received = true;
984+
let axis_modifier = match axis {
985+
wl_pointer::Axis::VerticalScroll => state.vertical_modifier,
986+
wl_pointer::Axis::HorizontalScroll => state.horizontal_modifier,
987+
_ => unreachable!(),
988+
};
989+
990+
let scroll_delta = state.discrete_scroll_delta.get_or_insert(point(0.0, 0.0));
991+
let wheel_percent = value120 as f32 / 120.0;
992+
match axis {
993+
wl_pointer::Axis::VerticalScroll => {
994+
scroll_delta.y += wheel_percent * axis_modifier;
995+
}
996+
wl_pointer::Axis::HorizontalScroll => {
997+
scroll_delta.x += wheel_percent * axis_modifier;
998+
}
999+
_ => unreachable!(),
1000+
}
1001+
}
1002+
wl_pointer::Event::Frame => {
1003+
if state.scroll_event_received {
1004+
state.scroll_event_received = false;
1005+
let continuous = state.continuous_scroll_delta.take();
1006+
let discrete = state.discrete_scroll_delta.take();
1007+
if let Some(continuous) = continuous {
1008+
if let Some(window) = state.mouse_focused_window.clone() {
1009+
let input = PlatformInput::ScrollWheel(ScrollWheelEvent {
1010+
position: state.mouse_location.unwrap(),
1011+
delta: ScrollDelta::Pixels(continuous),
1012+
modifiers: state.modifiers,
1013+
touch_phase: TouchPhase::Moved,
1014+
});
1015+
drop(state);
1016+
window.handle_input(input);
1017+
}
1018+
} else if let Some(discrete) = discrete {
1019+
if let Some(window) = state.mouse_focused_window.clone() {
1020+
let input = PlatformInput::ScrollWheel(ScrollWheelEvent {
1021+
position: state.mouse_location.unwrap(),
1022+
delta: ScrollDelta::Lines(discrete),
1023+
modifiers: state.modifiers,
1024+
touch_phase: TouchPhase::Moved,
1025+
});
1026+
drop(state);
1027+
window.handle_input(input);
1028+
}
1029+
}
9611030
}
9621031
}
9631032
_ => {}

0 commit comments

Comments
 (0)