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