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;
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            // The parent surface.
68            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            // Ensure that window exists.
76            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                // Pointer movements on decorations.
87                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                    // Emulate click on the frame.
115                    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                // Regular events on the main surface.
126                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                    // Set the currently focused surface.
133                    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                    // Remove the active surface.
144                    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                    // Update the last button serial.
158                    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                    // Get the current phase.
173                    let mut pointer_data = pointer.winit_data().inner.lock().unwrap();
174
175                    let has_discrete_scroll = horizontal.discrete != 0 || vertical.discrete != 0;
176
177                    // Figure out what to do about start/ended phases here.
178                    //
179                    // Figure out how to deal with `Started`. Also the `Ended` is not guaranteed
180                    // to be sent for mouse wheels.
181                    let phase = if horizontal.stop || vertical.stop {
182                        TouchPhase::Ended
183                    } else {
184                        match pointer_data.phase {
185                            // Discrete scroll only results in moved events.
186                            _ if has_discrete_scroll => TouchPhase::Moved,
187                            TouchPhase::Started | TouchPhase::Moved => TouchPhase::Moved,
188                            _ => TouchPhase::Started,
189                        }
190                    };
191
192                    // Update the phase.
193                    pointer_data.phase = phase;
194
195                    // Mice events have both pixel and discrete delta's at the same time. So prefer
196                    // the descrite values if they are present.
197                    let delta = if has_discrete_scroll {
198                        // NOTE: Wayland sign convention is the inverse of winit.
199                        MouseScrollDelta::LineDelta(
200                            (-horizontal.discrete) as f32,
201                            (-vertical.discrete) as f32,
202                        )
203                    } else {
204                        // NOTE: Wayland sign convention is the inverse of winit.
205                        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    /// The inner winit data associated with the pointer.
224    inner: Mutex<WinitPointerDataInner>,
225
226    /// The data required by the sctk.
227    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    /// Seat associated with this pointer.
290    pub fn seat(&self) -> &WlSeat {
291        self.sctk_data.seat()
292    }
293
294    /// Active window.
295    pub fn focused_window(&self) -> Option<WindowId> {
296        self.inner.lock().unwrap().surface
297    }
298
299    /// Last button serial.
300    pub fn latest_button_serial(&self) -> u32 {
301        self.sctk_data.latest_button_serial().unwrap_or_default()
302    }
303
304    /// Last enter serial.
305    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    /// The associated locked pointer.
326    locked_pointer: Option<ZwpLockedPointerV1>,
327
328    /// The associated confined pointer.
329    confined_pointer: Option<ZwpConfinedPointerV1>,
330
331    /// Serial of the last button event.
332    latest_button_serial: u32,
333
334    /// Currently focused window.
335    surface: Option<WindowId>,
336
337    /// Current axis phase.
338    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
365/// Convert the Wayland button into winit.
366fn wayland_button_to_winit(button: u32) -> MouseButton {
367    // These values are coming from <linux/input-event-codes.h>.
368    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);