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