linux: Simplify scrolling implementation (#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
This commit is contained in:
parent
5e4f707951
commit
3289188e0a
1 changed files with 135 additions and 66 deletions
|
@ -103,9 +103,13 @@ pub(crate) struct WaylandClientState {
|
|||
click: ClickState,
|
||||
repeat: KeyRepeat,
|
||||
modifiers: Modifiers,
|
||||
scroll_direction: f64,
|
||||
axis_source: AxisSource,
|
||||
mouse_location: Option<Point<Pixels>>,
|
||||
continuous_scroll_delta: Option<Point<Pixels>>,
|
||||
discrete_scroll_delta: Option<Point<f32>>,
|
||||
vertical_modifier: f32,
|
||||
horizontal_modifier: f32,
|
||||
scroll_event_received: bool,
|
||||
enter_token: Option<()>,
|
||||
button_pressed: Option<MouseButton>,
|
||||
mouse_focused_window: Option<WaylandWindowStatePtr>,
|
||||
|
@ -164,20 +168,21 @@ impl WaylandClientStatePtr {
|
|||
#[derive(Clone)]
|
||||
pub struct WaylandClient(Rc<RefCell<WaylandClientState>>);
|
||||
|
||||
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<wl_pointer::WlPointer, ()> 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);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
_ => {}
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue