1use std::ops::Deref;
4use std::sync::{Arc, Mutex};
5use std::time::Duration;
6
7use tracing::warn;
8
9use sctk::reexports::client::delegate_dispatch;
10use sctk::reexports::client::protocol::wl_pointer::WlPointer;
11use sctk::reexports::client::protocol::wl_seat::WlSeat;
12use sctk::reexports::client::protocol::wl_surface::WlSurface;
13use sctk::reexports::client::{Connection, Proxy, QueueHandle, Dispatch};
14use sctk::reexports::protocols::wp::pointer_constraints::zv1::client::zwp_confined_pointer_v1::ZwpConfinedPointerV1;
15use sctk::reexports::protocols::wp::pointer_constraints::zv1::client::zwp_locked_pointer_v1::ZwpLockedPointerV1;
16use sctk::reexports::protocols::wp::cursor_shape::v1::client::wp_cursor_shape_device_v1::WpCursorShapeDeviceV1;
17use sctk::reexports::protocols::wp::cursor_shape::v1::client::wp_cursor_shape_manager_v1::WpCursorShapeManagerV1;
18use sctk::reexports::protocols::wp::pointer_constraints::zv1::client::zwp_pointer_constraints_v1::{Lifetime, ZwpPointerConstraintsV1};
19use sctk::reexports::client::globals::{BindError, GlobalList};
20use sctk::reexports::csd_frame::FrameClick;
21use sctk::reexports::protocols::wp::viewporter::client::wp_viewport::WpViewport;
22
23use sctk::compositor::SurfaceData;
24use sctk::globals::GlobalData;
25use sctk::seat::pointer::{
26 PointerData, PointerDataExt, PointerEvent, PointerEventKind, PointerHandler,
27};
28use sctk::seat::SeatState;
29
30use crate::dpi::{LogicalPosition, PhysicalPosition};
31use crate::event::{ElementState, MouseButton, MouseScrollDelta, TouchPhase, WindowEvent};
32
33use crate::platform_impl::wayland::state::WinitState;
34use crate::platform_impl::wayland::{self, DeviceId, WindowId};
35
36pub mod relative_pointer;
37
38impl PointerHandler for WinitState {
39 fn pointer_frame(
40 &mut self,
41 connection: &Connection,
42 _: &QueueHandle<Self>,
43 pointer: &WlPointer,
44 events: &[PointerEvent],
45 ) {
46 let seat = pointer.winit_data().seat();
47 let seat_state = match self.seats.get(&seat.id()) {
48 Some(seat_state) => seat_state,
49 None => {
50 warn!("Received pointer event without seat");
51 return;
52 },
53 };
54
55 let themed_pointer = match seat_state.pointer.as_ref() {
56 Some(pointer) => pointer,
57 None => {
58 warn!("Received pointer event without pointer");
59 return;
60 },
61 };
62
63 let device_id = crate::event::DeviceId(crate::platform_impl::DeviceId::Wayland(DeviceId));
64
65 for event in events {
66 let surface = &event.surface;
67
68 let parent_surface = match event.surface.data::<SurfaceData>() {
70 Some(data) => data.parent_surface().unwrap_or(surface),
71 None => continue,
72 };
73
74 let window_id = wayland::make_wid(parent_surface);
75
76 let mut window = match self.windows.get_mut().get_mut(&window_id) {
78 Some(window) => window.lock().unwrap(),
79 None => continue,
80 };
81
82 let scale_factor = window.scale_factor();
83 let position: PhysicalPosition<f64> =
84 LogicalPosition::new(event.position.0, event.position.1).to_physical(scale_factor);
85
86 match event.kind {
87 PointerEventKind::Enter { .. } | PointerEventKind::Motion { .. }
89 if parent_surface != surface =>
90 {
91 if let Some(icon) = window.frame_point_moved(
92 seat,
93 surface,
94 Duration::ZERO,
95 event.position.0,
96 event.position.1,
97 ) {
98 let _ = themed_pointer.set_cursor(connection, icon);
99 }
100 },
101 PointerEventKind::Leave { .. } if parent_surface != surface => {
102 window.frame_point_left();
103 },
104 ref kind @ PointerEventKind::Press { button, serial, time }
105 | ref kind @ PointerEventKind::Release { button, serial, time }
106 if parent_surface != surface =>
107 {
108 let click = match wayland_button_to_winit(button) {
109 MouseButton::Left => FrameClick::Normal,
110 MouseButton::Right => FrameClick::Alternate,
111 _ => continue,
112 };
113 let pressed = matches!(kind, PointerEventKind::Press { .. });
114
115 window.frame_click(
117 click,
118 pressed,
119 seat,
120 serial,
121 Duration::from_millis(time as u64),
122 window_id,
123 &mut self.window_compositor_updates,
124 );
125 },
126 PointerEventKind::Enter { .. } => {
128 self.events_sink
129 .push_window_event(WindowEvent::CursorEntered { device_id }, window_id);
130
131 window.pointer_entered(Arc::downgrade(themed_pointer));
132
133 pointer.winit_data().inner.lock().unwrap().surface = Some(window_id);
135
136 self.events_sink.push_window_event(
137 WindowEvent::CursorMoved { device_id, position },
138 window_id,
139 );
140 },
141 PointerEventKind::Leave { .. } => {
142 window.pointer_left(Arc::downgrade(themed_pointer));
143
144 pointer.winit_data().inner.lock().unwrap().surface = None;
146
147 self.events_sink
148 .push_window_event(WindowEvent::CursorLeft { device_id }, window_id);
149 },
150 PointerEventKind::Motion { .. } => {
151 self.events_sink.push_window_event(
152 WindowEvent::CursorMoved { device_id, position },
153 window_id,
154 );
155 },
156 ref kind @ PointerEventKind::Press { button, serial, .. }
157 | ref kind @ PointerEventKind::Release { button, serial, .. } => {
158 pointer.winit_data().inner.lock().unwrap().latest_button_serial = serial;
160
161 let button = wayland_button_to_winit(button);
162 let state = if matches!(kind, PointerEventKind::Press { .. }) {
163 ElementState::Pressed
164 } else {
165 ElementState::Released
166 };
167 self.events_sink.push_window_event(
168 WindowEvent::MouseInput { device_id, state, button },
169 window_id,
170 );
171 },
172 PointerEventKind::Axis { horizontal, vertical, .. } => {
173 let mut pointer_data = pointer.winit_data().inner.lock().unwrap();
175
176 let has_discrete_scroll = horizontal.discrete != 0 || vertical.discrete != 0;
177
178 let phase = if horizontal.stop || vertical.stop {
183 TouchPhase::Ended
184 } else {
185 match pointer_data.phase {
186 _ if has_discrete_scroll => TouchPhase::Moved,
188 TouchPhase::Started | TouchPhase::Moved => TouchPhase::Moved,
189 _ => TouchPhase::Started,
190 }
191 };
192
193 pointer_data.phase = phase;
195
196 let delta = if has_discrete_scroll {
199 MouseScrollDelta::LineDelta(
201 (-horizontal.discrete) as f32,
202 (-vertical.discrete) as f32,
203 )
204 } else {
205 MouseScrollDelta::PixelDelta(
207 LogicalPosition::new(-horizontal.absolute, -vertical.absolute)
208 .to_physical(scale_factor),
209 )
210 };
211
212 self.events_sink.push_window_event(
213 WindowEvent::MouseWheel { device_id, delta, phase },
214 window_id,
215 )
216 },
217 }
218 }
219 }
220}
221
222#[derive(Debug)]
223pub struct WinitPointerData {
224 inner: Mutex<WinitPointerDataInner>,
226
227 sctk_data: PointerData,
229
230 viewport: Option<WpViewport>,
232}
233
234impl WinitPointerData {
235 pub fn new(seat: WlSeat, viewport: Option<WpViewport>) -> Self {
236 Self {
237 inner: Mutex::new(WinitPointerDataInner::default()),
238 sctk_data: PointerData::new(seat),
239 viewport,
240 }
241 }
242
243 pub fn lock_pointer(
244 &self,
245 pointer_constraints: &PointerConstraintsState,
246 surface: &WlSurface,
247 pointer: &WlPointer,
248 queue_handle: &QueueHandle<WinitState>,
249 ) {
250 let mut inner = self.inner.lock().unwrap();
251 if inner.locked_pointer.is_none() {
252 inner.locked_pointer = Some(pointer_constraints.lock_pointer(
253 surface,
254 pointer,
255 None,
256 Lifetime::Persistent,
257 queue_handle,
258 GlobalData,
259 ));
260 }
261 }
262
263 pub fn unlock_pointer(&self) {
264 let mut inner = self.inner.lock().unwrap();
265 if let Some(locked_pointer) = inner.locked_pointer.take() {
266 locked_pointer.destroy();
267 }
268 }
269
270 pub fn confine_pointer(
271 &self,
272 pointer_constraints: &PointerConstraintsState,
273 surface: &WlSurface,
274 pointer: &WlPointer,
275 queue_handle: &QueueHandle<WinitState>,
276 ) {
277 self.inner.lock().unwrap().confined_pointer = Some(pointer_constraints.confine_pointer(
278 surface,
279 pointer,
280 None,
281 Lifetime::Persistent,
282 queue_handle,
283 GlobalData,
284 ));
285 }
286
287 pub fn unconfine_pointer(&self) {
288 let inner = self.inner.lock().unwrap();
289 if let Some(confined_pointer) = inner.confined_pointer.as_ref() {
290 confined_pointer.destroy();
291 }
292 }
293
294 pub fn seat(&self) -> &WlSeat {
296 self.sctk_data.seat()
297 }
298
299 pub fn focused_window(&self) -> Option<WindowId> {
301 self.inner.lock().unwrap().surface
302 }
303
304 pub fn latest_button_serial(&self) -> u32 {
306 self.sctk_data.latest_button_serial().unwrap_or_default()
307 }
308
309 pub fn latest_enter_serial(&self) -> u32 {
311 self.sctk_data.latest_enter_serial().unwrap_or_default()
312 }
313
314 pub fn set_locked_cursor_position(&self, surface_x: f64, surface_y: f64) {
315 let inner = self.inner.lock().unwrap();
316 if let Some(locked_pointer) = inner.locked_pointer.as_ref() {
317 locked_pointer.set_cursor_position_hint(surface_x, surface_y);
318 }
319 }
320
321 pub fn viewport(&self) -> Option<&WpViewport> {
322 self.viewport.as_ref()
323 }
324}
325
326impl Drop for WinitPointerData {
327 fn drop(&mut self) {
328 if let Some(viewport) = self.viewport.take() {
329 viewport.destroy();
330 }
331 }
332}
333
334impl PointerDataExt for WinitPointerData {
335 fn pointer_data(&self) -> &PointerData {
336 &self.sctk_data
337 }
338}
339
340#[derive(Debug)]
341pub struct WinitPointerDataInner {
342 locked_pointer: Option<ZwpLockedPointerV1>,
344
345 confined_pointer: Option<ZwpConfinedPointerV1>,
347
348 latest_button_serial: u32,
350
351 surface: Option<WindowId>,
353
354 phase: TouchPhase,
356}
357
358impl Drop for WinitPointerDataInner {
359 fn drop(&mut self) {
360 if let Some(locked_pointer) = self.locked_pointer.take() {
361 locked_pointer.destroy();
362 }
363
364 if let Some(confined_pointer) = self.confined_pointer.take() {
365 confined_pointer.destroy();
366 }
367 }
368}
369
370impl Default for WinitPointerDataInner {
371 fn default() -> Self {
372 Self {
373 surface: None,
374 locked_pointer: None,
375 confined_pointer: None,
376 latest_button_serial: 0,
377 phase: TouchPhase::Ended,
378 }
379 }
380}
381
382fn wayland_button_to_winit(button: u32) -> MouseButton {
384 const BTN_LEFT: u32 = 0x110;
386 const BTN_RIGHT: u32 = 0x111;
387 const BTN_MIDDLE: u32 = 0x112;
388 const BTN_SIDE: u32 = 0x113;
389 const BTN_EXTRA: u32 = 0x114;
390 const BTN_FORWARD: u32 = 0x115;
391 const BTN_BACK: u32 = 0x116;
392
393 match button {
394 BTN_LEFT => MouseButton::Left,
395 BTN_RIGHT => MouseButton::Right,
396 BTN_MIDDLE => MouseButton::Middle,
397 BTN_BACK | BTN_SIDE => MouseButton::Back,
398 BTN_FORWARD | BTN_EXTRA => MouseButton::Forward,
399 button => MouseButton::Other(button as u16),
400 }
401}
402
403pub trait WinitPointerDataExt {
404 fn winit_data(&self) -> &WinitPointerData;
405}
406
407impl WinitPointerDataExt for WlPointer {
408 fn winit_data(&self) -> &WinitPointerData {
409 self.data::<WinitPointerData>().expect("failed to get pointer data.")
410 }
411}
412
413pub struct PointerConstraintsState {
414 pointer_constraints: ZwpPointerConstraintsV1,
415}
416
417impl PointerConstraintsState {
418 pub fn new(
419 globals: &GlobalList,
420 queue_handle: &QueueHandle<WinitState>,
421 ) -> Result<Self, BindError> {
422 let pointer_constraints = globals.bind(queue_handle, 1..=1, GlobalData)?;
423 Ok(Self { pointer_constraints })
424 }
425}
426
427impl Deref for PointerConstraintsState {
428 type Target = ZwpPointerConstraintsV1;
429
430 fn deref(&self) -> &Self::Target {
431 &self.pointer_constraints
432 }
433}
434
435impl Dispatch<ZwpPointerConstraintsV1, GlobalData, WinitState> for PointerConstraintsState {
436 fn event(
437 _state: &mut WinitState,
438 _proxy: &ZwpPointerConstraintsV1,
439 _event: <ZwpPointerConstraintsV1 as wayland_client::Proxy>::Event,
440 _data: &GlobalData,
441 _conn: &Connection,
442 _qhandle: &QueueHandle<WinitState>,
443 ) {
444 }
445}
446
447impl Dispatch<ZwpLockedPointerV1, GlobalData, WinitState> for PointerConstraintsState {
448 fn event(
449 _state: &mut WinitState,
450 _proxy: &ZwpLockedPointerV1,
451 _event: <ZwpLockedPointerV1 as wayland_client::Proxy>::Event,
452 _data: &GlobalData,
453 _conn: &Connection,
454 _qhandle: &QueueHandle<WinitState>,
455 ) {
456 }
457}
458
459impl Dispatch<ZwpConfinedPointerV1, GlobalData, WinitState> for PointerConstraintsState {
460 fn event(
461 _state: &mut WinitState,
462 _proxy: &ZwpConfinedPointerV1,
463 _event: <ZwpConfinedPointerV1 as wayland_client::Proxy>::Event,
464 _data: &GlobalData,
465 _conn: &Connection,
466 _qhandle: &QueueHandle<WinitState>,
467 ) {
468 }
469}
470
471impl Dispatch<WpCursorShapeDeviceV1, GlobalData, WinitState> for SeatState {
472 fn event(
473 _: &mut WinitState,
474 _: &WpCursorShapeDeviceV1,
475 _: <WpCursorShapeDeviceV1 as Proxy>::Event,
476 _: &GlobalData,
477 _: &Connection,
478 _: &QueueHandle<WinitState>,
479 ) {
480 unreachable!("wp_cursor_shape_manager has no events")
481 }
482}
483
484impl Dispatch<WpCursorShapeManagerV1, GlobalData, WinitState> for SeatState {
485 fn event(
486 _: &mut WinitState,
487 _: &WpCursorShapeManagerV1,
488 _: <WpCursorShapeManagerV1 as Proxy>::Event,
489 _: &GlobalData,
490 _: &Connection,
491 _: &QueueHandle<WinitState>,
492 ) {
493 unreachable!("wp_cursor_device_manager has no events")
494 }
495}
496
497delegate_dispatch!(WinitState: [ WlPointer: WinitPointerData] => SeatState);
498delegate_dispatch!(WinitState: [ WpCursorShapeManagerV1: GlobalData] => SeatState);
499delegate_dispatch!(WinitState: [ WpCursorShapeDeviceV1: GlobalData] => SeatState);
500delegate_dispatch!(WinitState: [ZwpPointerConstraintsV1: GlobalData] => PointerConstraintsState);
501delegate_dispatch!(WinitState: [ZwpLockedPointerV1: GlobalData] => PointerConstraintsState);
502delegate_dispatch!(WinitState: [ZwpConfinedPointerV1: GlobalData] => PointerConstraintsState);