Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

wayland: Add support for PinchGesture and RotationGesture #3656

Open
wants to merge 2 commits into
base: master
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Jump to
Jump to file
Failed to load files.
Diff view
Diff view
4 changes: 4 additions & 0 deletions src/changelog/unreleased.md
Original file line number Diff line number Diff line change
Expand Up @@ -39,3 +39,7 @@ The migration guide could reference other migration examples in the current
changelog entry.

## Unreleased

### Added

- On Wayland, add `PinchGesture`, `PanGesture` and `RotationGesture`.
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

https://www.grammarbook.com/blog/commas/oxford-comma/

Suggested change
- On Wayland, add `PinchGesture`, `PanGesture` and `RotationGesture`.
- On Wayland, add `PinchGesture`, `PanGesture`, and `RotationGesture`.

6 changes: 3 additions & 3 deletions src/event.rs
Original file line number Diff line number Diff line change
Expand Up @@ -281,7 +281,7 @@ pub enum WindowEvent {
///
/// ## Platform-specific
///
/// - Only available on **macOS** and **iOS**.
/// - Only available on **macOS**, **iOS** and **Wayland**.
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Suggested change
/// - Only available on **macOS**, **iOS** and **Wayland**.
/// - Only available on **macOS**, **iOS**, and **Wayland**.

/// - On iOS, not recognized by default. It must be enabled when needed.
PinchGesture {
device_id: DeviceId,
Expand All @@ -297,7 +297,7 @@ pub enum WindowEvent {
///
/// ## Platform-specific
///
/// - Only available on **iOS**.
/// - Only available on **iOS** and **Wayland**.
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Suggested change
/// - Only available on **iOS** and **Wayland**.
/// - Only available on **iOS**, and **Wayland**.

/// - On iOS, not recognized by default. It must be enabled when needed.
PanGesture {
device_id: DeviceId,
Expand Down Expand Up @@ -333,7 +333,7 @@ pub enum WindowEvent {
///
/// ## Platform-specific
///
/// - Only available on **macOS** and **iOS**.
/// - Only available on **macOS**, **iOS** and **Wayland**.
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Suggested change
/// - Only available on **macOS**, **iOS** and **Wayland**.
/// - Only available on **macOS**, **iOS**, and **Wayland**.

/// - On iOS, not recognized by default. It must be enabled when needed.
RotationGesture {
device_id: DeviceId,
Expand Down
16 changes: 15 additions & 1 deletion src/platform_impl/linux/wayland/seat/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,7 @@ use sctk::reexports::client::backend::ObjectId;
use sctk::reexports::client::protocol::wl_seat::WlSeat;
use sctk::reexports::client::protocol::wl_touch::WlTouch;
use sctk::reexports::client::{Connection, Proxy, QueueHandle};
use sctk::reexports::protocols::wp::pointer_gestures::zv1::client::zwp_pointer_gesture_pinch_v1::ZwpPointerGesturePinchV1;
use sctk::reexports::protocols::wp::relative_pointer::zv1::client::zwp_relative_pointer_v1::ZwpRelativePointerV1;
use sctk::reexports::protocols::wp::text_input::zv3::client::zwp_text_input_v3::ZwpTextInputV3;

Expand All @@ -24,7 +25,9 @@ mod text_input;
mod touch;

pub use pointer::relative_pointer::RelativePointerState;
pub use pointer::{PointerConstraintsState, WinitPointerData, WinitPointerDataExt};
pub use pointer::{
PointerConstraintsState, PointerGesturesState, WinitPointerData, WinitPointerDataExt,
};
pub use text_input::{TextInputState, ZwpTextInputV3Ext};

use keyboard::{KeyboardData, KeyboardState};
Expand All @@ -48,6 +51,9 @@ pub struct WinitSeatState {
/// The relative pointer bound on the seat.
relative_pointer: Option<ZwpRelativePointerV1>,

/// The pinch gesture of the pointer bound on the seat.
pinch_gesture: Option<ZwpPointerGesturePinchV1>,

/// The keyboard bound on the seat.
keyboard_state: Option<KeyboardState>,

Expand Down Expand Up @@ -111,6 +117,14 @@ impl SeatHandler for WinitState {
)
});

seat_state.pinch_gesture = self.pointer_gestures.as_ref().map(|gestures| {
gestures.get_pinch_gesture(
themed_pointer.pointer(),
queue_handle,
sctk::globals::GlobalData,
)
});

let themed_pointer = Arc::new(themed_pointer);

// Register cursor surface.
Expand Down
100 changes: 100 additions & 0 deletions src/platform_impl/linux/wayland/seat/pointer/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -14,6 +14,8 @@ use sctk::reexports::protocols::wp::pointer_constraints::zv1::client::zwp_locked
use sctk::reexports::protocols::wp::cursor_shape::v1::client::wp_cursor_shape_device_v1::WpCursorShapeDeviceV1;
use sctk::reexports::protocols::wp::cursor_shape::v1::client::wp_cursor_shape_manager_v1::WpCursorShapeManagerV1;
use sctk::reexports::protocols::wp::pointer_constraints::zv1::client::zwp_pointer_constraints_v1::{Lifetime, ZwpPointerConstraintsV1};
use sctk::reexports::protocols::wp::pointer_gestures::zv1::client::zwp_pointer_gestures_v1::ZwpPointerGesturesV1;
use sctk::reexports::protocols::wp::pointer_gestures::zv1::client::zwp_pointer_gesture_pinch_v1::{ZwpPointerGesturePinchV1, Event};
use sctk::reexports::client::globals::{BindError, GlobalList};
use sctk::reexports::csd_frame::FrameClick;

Expand Down Expand Up @@ -467,9 +469,107 @@ impl Dispatch<WpCursorShapeManagerV1, GlobalData, WinitState> for SeatState {
}
}

pub struct PointerGesturesState {
pointer_gestures: ZwpPointerGesturesV1,
}

impl PointerGesturesState {
pub fn new(
globals: &GlobalList,
queue_handle: &QueueHandle<WinitState>,
) -> Result<Self, BindError> {
let pointer_gestures = globals.bind(queue_handle, 1..=1, GlobalData)?;
Ok(Self { pointer_gestures })
}
}

impl Deref for PointerGesturesState {
type Target = ZwpPointerGesturesV1;

fn deref(&self) -> &Self::Target {
&self.pointer_gestures
}
}

impl Dispatch<ZwpPointerGesturesV1, GlobalData, WinitState> for PointerGesturesState {
fn event(
_state: &mut WinitState,
_proxy: &ZwpPointerGesturesV1,
_event: <ZwpPointerGesturesV1 as wayland_client::Proxy>::Event,
_data: &GlobalData,
_conn: &Connection,
_qhandle: &QueueHandle<WinitState>,
) {
unreachable!("zwp_pointer_gestures_v1 has no events")
}
}

impl Dispatch<ZwpPointerGesturePinchV1, GlobalData, WinitState> for PointerGesturesState {
fn event(
state: &mut WinitState,
_proxy: &ZwpPointerGesturePinchV1,
event: <ZwpPointerGesturePinchV1 as wayland_client::Proxy>::Event,
_data: &GlobalData,
_conn: &Connection,
_qhandle: &QueueHandle<WinitState>,
) {
let device_id = crate::event::DeviceId(crate::platform_impl::DeviceId::Wayland(DeviceId));
let (phase, pan_delta, scale_delta, rotation_delta) = match event {
Event::Begin { time: _, serial: _, surface, fingers } => {
if fingers != 2 {
// We only support two fingers for now.
return;
}
// The parent surface.
let parent_surface = match surface.data::<SurfaceData>() {
Some(data) => data.parent_surface().unwrap_or(&surface),
None => return,
};

let pan_delta = PhysicalPosition::new(0., 0.);
state.window_id = wayland::make_wid(parent_surface);
state.previous_scale = 1.;

(TouchPhase::Started, pan_delta, 0., 0.)
},
Event::Update { time: _, dx, dy, scale, rotation } => {
let pan_delta = PhysicalPosition::new(dx as f32, dy as f32);
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

It's in surface coordinates, but PhysicalPosition is buffer, you should convert from logical to physical with the function like in wl_pointer events above.

let scale_delta = scale - state.previous_scale;
state.previous_scale = scale;
(TouchPhase::Moved, pan_delta, scale_delta, -rotation as f32)
},
Event::End { time: _, serial: _, cancelled } => {
let pan_delta = PhysicalPosition::new(0., 0.);
state.previous_scale = 1.;
(
if cancelled == 0 { TouchPhase::Ended } else { TouchPhase::Cancelled },
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Define it on a separate line, so it'll end up more compact.

pan_delta,
0.,
0.,
)
},
_ => unreachable!("Unknown event {event:?}"),
};
state.events_sink.push_window_event(
WindowEvent::PanGesture { device_id, delta: pan_delta, phase },
state.window_id,
);
Comment on lines +553 to +556
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Isn't Pan basically a swipe?

@madsmtm do you know about it? Also, it's strange that we don't have amount of fingers in Pan gesture if it's so.

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The terminology is probably different elsewhere, but I think a pan gesture (docs) is similar, but distinct from a swipe gesture (docs); the pan is more general (e.g. moving a map around), a swipe is usually short and only in one direction (e.g. dismissing a pop-up).

It would make sense to have the number of fingers in the event.

state.events_sink.push_window_event(
WindowEvent::PinchGesture { device_id, delta: scale_delta, phase },
state.window_id,
);
state.events_sink.push_window_event(
WindowEvent::RotationGesture { device_id, delta: rotation_delta, phase },
state.window_id,
);
Comment on lines +557 to +564
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

You should only send updates if there was a change compared to previous state, so if there was not pinch, but just rotation, only rotation should be send.

}
}

delegate_dispatch!(WinitState: [ WlPointer: WinitPointerData] => SeatState);
delegate_dispatch!(WinitState: [ WpCursorShapeManagerV1: GlobalData] => SeatState);
delegate_dispatch!(WinitState: [ WpCursorShapeDeviceV1: GlobalData] => SeatState);
delegate_dispatch!(WinitState: [ZwpPointerConstraintsV1: GlobalData] => PointerConstraintsState);
delegate_dispatch!(WinitState: [ZwpLockedPointerV1: GlobalData] => PointerConstraintsState);
delegate_dispatch!(WinitState: [ZwpConfinedPointerV1: GlobalData] => PointerConstraintsState);
delegate_dispatch!(WinitState: [ZwpPointerGesturesV1: GlobalData] => PointerGesturesState);
delegate_dispatch!(WinitState: [ZwpPointerGesturePinchV1: GlobalData] => PointerGesturesState);
17 changes: 15 additions & 2 deletions src/platform_impl/linux/wayland/state.rs
Original file line number Diff line number Diff line change
Expand Up @@ -26,8 +26,8 @@ use sctk::subcompositor::SubcompositorState;
use crate::platform_impl::wayland::event_loop::sink::EventSink;
use crate::platform_impl::wayland::output::MonitorHandle;
use crate::platform_impl::wayland::seat::{
PointerConstraintsState, RelativePointerState, TextInputState, WinitPointerData,
WinitPointerDataExt, WinitSeatState,
PointerConstraintsState, PointerGesturesState, RelativePointerState, TextInputState,
WinitPointerData, WinitPointerDataExt, WinitSeatState,
};
use crate::platform_impl::wayland::types::kwin_blur::KWinBlurManager;
use crate::platform_impl::wayland::types::wp_fractional_scaling::FractionalScalingManager;
Expand Down Expand Up @@ -100,6 +100,15 @@ pub struct WinitState {
/// Pointer constraints to handle pointer locking and confining.
pub pointer_constraints: Option<Arc<PointerConstraintsState>>,

/// Pointer gestures to handle swipe, pinch and hold.
pub pointer_gestures: Option<PointerGesturesState>,

/// XXX: Is this really meant to stay here?
pub window_id: WindowId,
Comment on lines +106 to +107
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

No, it should be like with keyboard focus, on user data.


/// XXX: Is this really meant to stay here?
pub previous_scale: f64,
Comment on lines +109 to +110
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

No, should be on user data as well.


/// Viewporter state on the given window.
pub viewporter_state: Option<ViewporterState>,

Expand Down Expand Up @@ -185,8 +194,12 @@ impl WinitState {
pointer_constraints: PointerConstraintsState::new(globals, queue_handle)
.map(Arc::new)
.ok(),
pointer_gestures: PointerGesturesState::new(globals, queue_handle).ok(),
pointer_surfaces: Default::default(),

window_id: WindowId(0),
previous_scale: 1.,

monitors: Arc::new(Mutex::new(monitors)),
events_sink: EventSink::new(),
loop_handle,
Expand Down