wayland: support for pinch, rotation, and pan gestures
Co-Authored-By: linkmauve <linkmauve@linkmauve.fr> Co-authored-by: Kirill Chibisov <contact@kchibisov.com>
This commit is contained in:
parent
a68f1a664b
commit
3be30affe4
6 changed files with 187 additions and 5 deletions
|
|
@ -264,7 +264,7 @@ pub enum WindowEvent {
|
||||||
///
|
///
|
||||||
/// ## Platform-specific
|
/// ## Platform-specific
|
||||||
///
|
///
|
||||||
/// - Only available on **macOS** and **iOS**.
|
/// - Only available on **macOS**, **iOS**, and **Wayland**.
|
||||||
/// - On iOS, not recognized by default. It must be enabled when needed.
|
/// - On iOS, not recognized by default. It must be enabled when needed.
|
||||||
PinchGesture {
|
PinchGesture {
|
||||||
device_id: Option<DeviceId>,
|
device_id: Option<DeviceId>,
|
||||||
|
|
@ -280,7 +280,7 @@ pub enum WindowEvent {
|
||||||
///
|
///
|
||||||
/// ## Platform-specific
|
/// ## Platform-specific
|
||||||
///
|
///
|
||||||
/// - Only available on **iOS**.
|
/// - Only available on **iOS** and **Wayland**.
|
||||||
/// - On iOS, not recognized by default. It must be enabled when needed.
|
/// - On iOS, not recognized by default. It must be enabled when needed.
|
||||||
PanGesture {
|
PanGesture {
|
||||||
device_id: Option<DeviceId>,
|
device_id: Option<DeviceId>,
|
||||||
|
|
@ -316,7 +316,7 @@ pub enum WindowEvent {
|
||||||
///
|
///
|
||||||
/// ## Platform-specific
|
/// ## Platform-specific
|
||||||
///
|
///
|
||||||
/// - Only available on **macOS** and **iOS**.
|
/// - Only available on **macOS**, **iOS**, and **Wayland**.
|
||||||
/// - On iOS, not recognized by default. It must be enabled when needed.
|
/// - On iOS, not recognized by default. It must be enabled when needed.
|
||||||
RotationGesture {
|
RotationGesture {
|
||||||
device_id: Option<DeviceId>,
|
device_id: Option<DeviceId>,
|
||||||
|
|
|
||||||
|
|
@ -12,6 +12,7 @@ use sctk::reexports::protocols::wp::text_input::zv3::client::zwp_text_input_v3::
|
||||||
use sctk::seat::pointer::{ThemeSpec, ThemedPointer};
|
use sctk::seat::pointer::{ThemeSpec, ThemedPointer};
|
||||||
use sctk::seat::{Capability as SeatCapability, SeatHandler, SeatState};
|
use sctk::seat::{Capability as SeatCapability, SeatHandler, SeatState};
|
||||||
use tracing::warn;
|
use tracing::warn;
|
||||||
|
use wayland_protocols::wp::pointer_gestures::zv1::client::zwp_pointer_gesture_pinch_v1::ZwpPointerGesturePinchV1;
|
||||||
use winit_core::event::WindowEvent;
|
use winit_core::event::WindowEvent;
|
||||||
use winit_core::keyboard::ModifiersState;
|
use winit_core::keyboard::ModifiersState;
|
||||||
|
|
||||||
|
|
@ -23,6 +24,7 @@ mod text_input;
|
||||||
mod touch;
|
mod touch;
|
||||||
|
|
||||||
use keyboard::{KeyboardData, KeyboardState};
|
use keyboard::{KeyboardData, KeyboardState};
|
||||||
|
pub use pointer::pointer_gesture::{PointerGestureData, PointerGesturesState};
|
||||||
pub use pointer::relative_pointer::RelativePointerState;
|
pub use pointer::relative_pointer::RelativePointerState;
|
||||||
pub use pointer::{PointerConstraintsState, WinitPointerData, WinitPointerDataExt};
|
pub use pointer::{PointerConstraintsState, WinitPointerData, WinitPointerDataExt};
|
||||||
use text_input::TextInputData;
|
use text_input::TextInputData;
|
||||||
|
|
@ -51,6 +53,9 @@ pub struct WinitSeatState {
|
||||||
/// The relative pointer bound on the seat.
|
/// The relative pointer bound on the seat.
|
||||||
relative_pointer: Option<ZwpRelativePointerV1>,
|
relative_pointer: Option<ZwpRelativePointerV1>,
|
||||||
|
|
||||||
|
/// The pinch pointer gesture bound on the seat.
|
||||||
|
pointer_gesture_pinch: Option<ZwpPointerGesturePinchV1>,
|
||||||
|
|
||||||
/// The keyboard bound on the seat.
|
/// The keyboard bound on the seat.
|
||||||
keyboard_state: Option<KeyboardState>,
|
keyboard_state: Option<KeyboardState>,
|
||||||
|
|
||||||
|
|
@ -124,6 +129,14 @@ impl SeatHandler for WinitState {
|
||||||
)
|
)
|
||||||
});
|
});
|
||||||
|
|
||||||
|
seat_state.pointer_gesture_pinch = self.pointer_gestures.as_ref().map(|manager| {
|
||||||
|
manager.get_pinch_gesture(
|
||||||
|
themed_pointer.pointer(),
|
||||||
|
queue_handle,
|
||||||
|
PointerGestureData::default(),
|
||||||
|
)
|
||||||
|
});
|
||||||
|
|
||||||
let themed_pointer = Arc::new(themed_pointer);
|
let themed_pointer = Arc::new(themed_pointer);
|
||||||
|
|
||||||
// Register cursor surface.
|
// Register cursor surface.
|
||||||
|
|
@ -177,6 +190,10 @@ impl SeatHandler for WinitState {
|
||||||
relative_pointer.destroy();
|
relative_pointer.destroy();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if let Some(pointer_gesture_pinch) = seat_state.pointer_gesture_pinch.take() {
|
||||||
|
pointer_gesture_pinch.destroy();
|
||||||
|
}
|
||||||
|
|
||||||
if let Some(pointer) = seat_state.pointer.take() {
|
if let Some(pointer) = seat_state.pointer.take() {
|
||||||
let pointer_data = pointer.pointer().winit_data();
|
let pointer_data = pointer.pointer().winit_data();
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -36,6 +36,7 @@ use winit_core::event::{
|
||||||
use crate::state::WinitState;
|
use crate::state::WinitState;
|
||||||
use crate::WindowId;
|
use crate::WindowId;
|
||||||
|
|
||||||
|
pub mod pointer_gesture;
|
||||||
pub mod relative_pointer;
|
pub mod relative_pointer;
|
||||||
|
|
||||||
impl PointerHandler for WinitState {
|
impl PointerHandler for WinitState {
|
||||||
|
|
|
||||||
159
winit-wayland/src/seat/pointer/pointer_gesture.rs
Normal file
159
winit-wayland/src/seat/pointer/pointer_gesture.rs
Normal file
|
|
@ -0,0 +1,159 @@
|
||||||
|
use std::ops::Deref;
|
||||||
|
use std::sync::Mutex;
|
||||||
|
|
||||||
|
use dpi::{LogicalPosition, PhysicalPosition};
|
||||||
|
use sctk::compositor::SurfaceData;
|
||||||
|
use sctk::globals::GlobalData;
|
||||||
|
use sctk::reexports::client::globals::{BindError, GlobalList};
|
||||||
|
use sctk::reexports::client::{delegate_dispatch, Connection, Dispatch, Proxy, QueueHandle};
|
||||||
|
use sctk::reexports::protocols::wp::pointer_gestures::zv1::client::zwp_pointer_gesture_pinch_v1::{
|
||||||
|
Event, ZwpPointerGesturePinchV1,
|
||||||
|
};
|
||||||
|
use sctk::reexports::protocols::wp::pointer_gestures::zv1::client::zwp_pointer_gestures_v1::ZwpPointerGesturesV1;
|
||||||
|
use winit_core::event::{TouchPhase, WindowEvent};
|
||||||
|
use winit_core::window::WindowId;
|
||||||
|
|
||||||
|
use crate::state::WinitState;
|
||||||
|
|
||||||
|
/// Wrapper around the pointer gesture.
|
||||||
|
#[derive(Debug)]
|
||||||
|
pub struct PointerGesturesState {
|
||||||
|
pointer_gestures: ZwpPointerGesturesV1,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl PointerGesturesState {
|
||||||
|
/// Create a new pointer gesture
|
||||||
|
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 })
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#[derive(Debug, Default)]
|
||||||
|
pub struct PointerGestureData {
|
||||||
|
inner: Mutex<PointerGestureDataInner>,
|
||||||
|
}
|
||||||
|
|
||||||
|
#[derive(Debug)]
|
||||||
|
pub struct PointerGestureDataInner {
|
||||||
|
window_id: Option<WindowId>,
|
||||||
|
previous_pinch: f64,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl Default for PointerGestureDataInner {
|
||||||
|
fn default() -> Self {
|
||||||
|
Self { window_id: Default::default(), previous_pinch: 1.0 }
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
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, PointerGestureData, WinitState> for PointerGesturesState {
|
||||||
|
fn event(
|
||||||
|
state: &mut WinitState,
|
||||||
|
_proxy: &ZwpPointerGesturePinchV1,
|
||||||
|
event: <ZwpPointerGesturePinchV1 as Proxy>::Event,
|
||||||
|
data: &PointerGestureData,
|
||||||
|
_conn: &Connection,
|
||||||
|
_qhandle: &QueueHandle<WinitState>,
|
||||||
|
) {
|
||||||
|
let mut pointer_gesture_data = data.inner.lock().unwrap();
|
||||||
|
let (window_id, phase, pan_delta, pinch_delta, rotation_delta) = match event {
|
||||||
|
Event::Begin { surface, fingers, .. } => {
|
||||||
|
// We only support two fingers for now.
|
||||||
|
if fingers != 2 {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Don't handle events from a subsurface.
|
||||||
|
if !surface
|
||||||
|
.data::<SurfaceData>()
|
||||||
|
.is_some_and(|data| data.parent_surface().is_none())
|
||||||
|
{
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
let window_id = crate::make_wid(&surface);
|
||||||
|
|
||||||
|
pointer_gesture_data.window_id = Some(window_id);
|
||||||
|
pointer_gesture_data.previous_pinch = 1.;
|
||||||
|
|
||||||
|
(window_id, TouchPhase::Started, PhysicalPosition::new(0., 0.), 0., 0.)
|
||||||
|
},
|
||||||
|
Event::Update { dx, dy, scale: pinch, rotation, .. } => {
|
||||||
|
let window_id = match pointer_gesture_data.window_id {
|
||||||
|
Some(window_id) => window_id,
|
||||||
|
_ => return,
|
||||||
|
};
|
||||||
|
|
||||||
|
let scale_factor = match state.windows.get_mut().get_mut(&window_id) {
|
||||||
|
Some(window) => window.lock().unwrap().scale_factor(),
|
||||||
|
None => return,
|
||||||
|
};
|
||||||
|
|
||||||
|
let pan_delta =
|
||||||
|
LogicalPosition::new(dx as f32, dy as f32).to_physical(scale_factor);
|
||||||
|
|
||||||
|
let pinch_delta = pinch - pointer_gesture_data.previous_pinch;
|
||||||
|
pointer_gesture_data.previous_pinch = pinch;
|
||||||
|
|
||||||
|
// Wayland provides rotation in degrees cw, opposite of winit's degrees ccw.
|
||||||
|
let rotation_delta = -rotation as f32;
|
||||||
|
(window_id, TouchPhase::Moved, pan_delta, pinch_delta, rotation_delta)
|
||||||
|
},
|
||||||
|
Event::End { cancelled, .. } => {
|
||||||
|
let window_id = match pointer_gesture_data.window_id {
|
||||||
|
Some(window_id) => window_id,
|
||||||
|
_ => return,
|
||||||
|
};
|
||||||
|
|
||||||
|
// Reset the state.
|
||||||
|
*pointer_gesture_data = Default::default();
|
||||||
|
|
||||||
|
let phase = if cancelled == 0 { TouchPhase::Ended } else { TouchPhase::Cancelled };
|
||||||
|
(window_id, phase, PhysicalPosition::new(0., 0.), 0., 0.)
|
||||||
|
},
|
||||||
|
_ => unreachable!("Unknown event {event:?}"),
|
||||||
|
};
|
||||||
|
|
||||||
|
// The chance of only one of these events being necessary is extremely small,
|
||||||
|
// so it is easier to just send all three
|
||||||
|
state.events_sink.push_window_event(
|
||||||
|
WindowEvent::PanGesture { device_id: None, delta: pan_delta, phase },
|
||||||
|
window_id,
|
||||||
|
);
|
||||||
|
state.events_sink.push_window_event(
|
||||||
|
WindowEvent::PinchGesture { device_id: None, delta: pinch_delta, phase },
|
||||||
|
window_id,
|
||||||
|
);
|
||||||
|
state.events_sink.push_window_event(
|
||||||
|
WindowEvent::RotationGesture { device_id: None, delta: rotation_delta, phase },
|
||||||
|
window_id,
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
delegate_dispatch!(WinitState: [ZwpPointerGesturesV1: GlobalData] => PointerGesturesState);
|
||||||
|
delegate_dispatch!(WinitState: [ZwpPointerGesturePinchV1: PointerGestureData] => PointerGesturesState);
|
||||||
|
|
@ -25,8 +25,8 @@ use winit_core::error::OsError;
|
||||||
use crate::event_loop::sink::EventSink;
|
use crate::event_loop::sink::EventSink;
|
||||||
use crate::output::MonitorHandle;
|
use crate::output::MonitorHandle;
|
||||||
use crate::seat::{
|
use crate::seat::{
|
||||||
PointerConstraintsState, RelativePointerState, TextInputState, WinitPointerData,
|
PointerConstraintsState, PointerGesturesState, RelativePointerState, TextInputState,
|
||||||
WinitPointerDataExt, WinitSeatState,
|
WinitPointerData, WinitPointerDataExt, WinitSeatState,
|
||||||
};
|
};
|
||||||
use crate::types::kwin_blur::KWinBlurManager;
|
use crate::types::kwin_blur::KWinBlurManager;
|
||||||
use crate::types::wp_fractional_scaling::FractionalScalingManager;
|
use crate::types::wp_fractional_scaling::FractionalScalingManager;
|
||||||
|
|
@ -103,6 +103,9 @@ pub struct WinitState {
|
||||||
/// Pointer constraints to handle pointer locking and confining.
|
/// Pointer constraints to handle pointer locking and confining.
|
||||||
pub pointer_constraints: Option<Arc<PointerConstraintsState>>,
|
pub pointer_constraints: Option<Arc<PointerConstraintsState>>,
|
||||||
|
|
||||||
|
/// Pointer gestures to handle pinch, rotate, and pan
|
||||||
|
pub pointer_gestures: Option<PointerGesturesState>,
|
||||||
|
|
||||||
/// Viewporter state on the given window.
|
/// Viewporter state on the given window.
|
||||||
pub viewporter_state: Option<ViewporterState>,
|
pub viewporter_state: Option<ViewporterState>,
|
||||||
|
|
||||||
|
|
@ -195,6 +198,7 @@ impl WinitState {
|
||||||
.map(Arc::new)
|
.map(Arc::new)
|
||||||
.ok(),
|
.ok(),
|
||||||
pointer_surfaces: Default::default(),
|
pointer_surfaces: Default::default(),
|
||||||
|
pointer_gestures: PointerGesturesState::new(globals, queue_handle).ok(),
|
||||||
|
|
||||||
monitors: Arc::new(Mutex::new(monitors)),
|
monitors: Arc::new(Mutex::new(monitors)),
|
||||||
events_sink: EventSink::new(),
|
events_sink: EventSink::new(),
|
||||||
|
|
|
||||||
|
|
@ -82,6 +82,7 @@ changelog entry.
|
||||||
- `ActivationToken::as_raw` to get a ref to raw token.
|
- `ActivationToken::as_raw` to get a ref to raw token.
|
||||||
- Each platform now has corresponding `WindowAttributes` struct instead of trait extension.
|
- Each platform now has corresponding `WindowAttributes` struct instead of trait extension.
|
||||||
- On Wayland, added implementation for `Window::set_window_icon`
|
- On Wayland, added implementation for `Window::set_window_icon`
|
||||||
|
- On Wayland, added `PanGesture`, `PinchGesture`, and `RotationGesture`
|
||||||
- Add `Window::request_ime_update` to atomically apply set of IME changes.
|
- Add `Window::request_ime_update` to atomically apply set of IME changes.
|
||||||
- Add `Ime::DeleteSurrounding` to let the input method delete text.
|
- Add `Ime::DeleteSurrounding` to let the input method delete text.
|
||||||
- Add more `ImePurpose` values.
|
- Add more `ImePurpose` values.
|
||||||
|
|
|
||||||
Loading…
Add table
Add a link
Reference in a new issue