winit/platform_impl/linux/wayland/seat/pointer/
mod.rs

1//! The pointer events.
2
3use 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            // The parent surface.
69            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            // Ensure that window exists.
77            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                // Pointer movements on decorations.
88                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                    // Emulate click on the frame.
116                    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                // Regular events on the main surface.
127                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                    // Set the currently focused surface.
134                    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                    // Remove the active surface.
145                    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                    // Update the last button serial.
159                    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                    // Get the current phase.
174                    let mut pointer_data = pointer.winit_data().inner.lock().unwrap();
175
176                    let has_discrete_scroll = horizontal.discrete != 0 || vertical.discrete != 0;
177
178                    // Figure out what to do about start/ended phases here.
179                    //
180                    // Figure out how to deal with `Started`. Also the `Ended` is not guaranteed
181                    // to be sent for mouse wheels.
182                    let phase = if horizontal.stop || vertical.stop {
183                        TouchPhase::Ended
184                    } else {
185                        match pointer_data.phase {
186                            // Discrete scroll only results in moved events.
187                            _ if has_discrete_scroll => TouchPhase::Moved,
188                            TouchPhase::Started | TouchPhase::Moved => TouchPhase::Moved,
189                            _ => TouchPhase::Started,
190                        }
191                    };
192
193                    // Update the phase.
194                    pointer_data.phase = phase;
195
196                    // Mice events have both pixel and discrete delta's at the same time. So prefer
197                    // the discrete values if they are present.
198                    let delta = if has_discrete_scroll {
199                        // NOTE: Wayland sign convention is the inverse of winit.
200                        MouseScrollDelta::LineDelta(
201                            (-horizontal.discrete) as f32,
202                            (-vertical.discrete) as f32,
203                        )
204                    } else {
205                        // NOTE: Wayland sign convention is the inverse of winit.
206                        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    /// The inner winit data associated with the pointer.
225    inner: Mutex<WinitPointerDataInner>,
226
227    /// The data required by the sctk.
228    sctk_data: PointerData,
229
230    /// Viewport for fractional cursor.
231    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    /// Seat associated with this pointer.
295    pub fn seat(&self) -> &WlSeat {
296        self.sctk_data.seat()
297    }
298
299    /// Active window.
300    pub fn focused_window(&self) -> Option<WindowId> {
301        self.inner.lock().unwrap().surface
302    }
303
304    /// Last button serial.
305    pub fn latest_button_serial(&self) -> u32 {
306        self.sctk_data.latest_button_serial().unwrap_or_default()
307    }
308
309    /// Last enter serial.
310    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    /// The associated locked pointer.
343    locked_pointer: Option<ZwpLockedPointerV1>,
344
345    /// The associated confined pointer.
346    confined_pointer: Option<ZwpConfinedPointerV1>,
347
348    /// Serial of the last button event.
349    latest_button_serial: u32,
350
351    /// Currently focused window.
352    surface: Option<WindowId>,
353
354    /// Current axis phase.
355    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
382/// Convert the Wayland button into winit.
383fn wayland_button_to_winit(button: u32) -> MouseButton {
384    // These values are coming from <linux/input-event-codes.h>.
385    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);