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

1//! Seat handling.
2
3use std::sync::Arc;
4
5use ahash::AHashMap;
6use sctk::reexports::client::backend::ObjectId;
7use sctk::reexports::client::protocol::wl_seat::WlSeat;
8use sctk::reexports::client::protocol::wl_touch::WlTouch;
9use sctk::reexports::client::{Connection, Proxy, QueueHandle};
10use sctk::reexports::protocols::wp::relative_pointer::zv1::client::zwp_relative_pointer_v1::ZwpRelativePointerV1;
11use sctk::reexports::protocols::wp::text_input::zv3::client::zwp_text_input_v3::ZwpTextInputV3;
12use sctk::seat::pointer::{ThemeSpec, ThemedPointer};
13use sctk::seat::{Capability as SeatCapability, SeatHandler, SeatState};
14use tracing::warn;
15
16use crate::event::WindowEvent;
17use crate::keyboard::ModifiersState;
18use crate::platform_impl::wayland::state::WinitState;
19
20mod keyboard;
21mod pointer;
22mod text_input;
23mod touch;
24
25use keyboard::{KeyboardData, KeyboardState};
26pub use pointer::relative_pointer::RelativePointerState;
27pub use pointer::{PointerConstraintsState, WinitPointerData, WinitPointerDataExt};
28use text_input::TextInputData;
29pub use text_input::{TextInputState, ZwpTextInputV3Ext};
30use touch::TouchPoint;
31
32#[derive(Debug, Default)]
33pub struct WinitSeatState {
34    /// The pointer bound on the seat.
35    pointer: Option<Arc<ThemedPointer<WinitPointerData>>>,
36
37    /// The touch bound on the seat.
38    touch: Option<WlTouch>,
39
40    /// The mapping from touched points to the surfaces they're present.
41    touch_map: AHashMap<i32, TouchPoint>,
42
43    /// The text input bound on the seat.
44    text_input: Option<Arc<ZwpTextInputV3>>,
45
46    /// The relative pointer bound on the seat.
47    relative_pointer: Option<ZwpRelativePointerV1>,
48
49    /// The keyboard bound on the seat.
50    keyboard_state: Option<KeyboardState>,
51
52    /// The current modifiers state on the seat.
53    modifiers: ModifiersState,
54
55    /// Whether we have pending modifiers.
56    modifiers_pending: bool,
57}
58
59impl WinitSeatState {
60    pub fn new() -> Self {
61        Default::default()
62    }
63}
64
65impl SeatHandler for WinitState {
66    fn seat_state(&mut self) -> &mut SeatState {
67        &mut self.seat_state
68    }
69
70    fn new_capability(
71        &mut self,
72        _: &Connection,
73        queue_handle: &QueueHandle<Self>,
74        seat: WlSeat,
75        capability: SeatCapability,
76    ) {
77        let seat_state = match self.seats.get_mut(&seat.id()) {
78            Some(seat_state) => seat_state,
79            None => {
80                warn!("Received wl_seat::new_capability for unknown seat");
81                return;
82            },
83        };
84
85        match capability {
86            SeatCapability::Touch if seat_state.touch.is_none() => {
87                seat_state.touch = self.seat_state.get_touch(queue_handle, &seat).ok();
88            },
89            SeatCapability::Keyboard if seat_state.keyboard_state.is_none() => {
90                let keyboard = seat.get_keyboard(queue_handle, KeyboardData::new(seat.clone()));
91                seat_state.keyboard_state =
92                    Some(KeyboardState::new(keyboard, self.loop_handle.clone()));
93            },
94            SeatCapability::Pointer if seat_state.pointer.is_none() => {
95                let surface = self.compositor_state.create_surface(queue_handle);
96                let surface_id = surface.id();
97                let pointer_data = WinitPointerData::new(seat.clone());
98                let themed_pointer = self
99                    .seat_state
100                    .get_pointer_with_theme_and_data(
101                        queue_handle,
102                        &seat,
103                        self.shm.wl_shm(),
104                        surface,
105                        ThemeSpec::System,
106                        pointer_data,
107                    )
108                    .expect("failed to create pointer with present capability.");
109
110                seat_state.relative_pointer = self.relative_pointer.as_ref().map(|manager| {
111                    manager.get_relative_pointer(
112                        themed_pointer.pointer(),
113                        queue_handle,
114                        sctk::globals::GlobalData,
115                    )
116                });
117
118                let themed_pointer = Arc::new(themed_pointer);
119
120                // Register cursor surface.
121                self.pointer_surfaces.insert(surface_id, themed_pointer.clone());
122
123                seat_state.pointer = Some(themed_pointer);
124            },
125            _ => (),
126        }
127
128        if let Some(text_input_state) =
129            seat_state.text_input.is_none().then_some(self.text_input_state.as_ref()).flatten()
130        {
131            seat_state.text_input = Some(Arc::new(text_input_state.get_text_input(
132                &seat,
133                queue_handle,
134                TextInputData::default(),
135            )));
136        }
137    }
138
139    fn remove_capability(
140        &mut self,
141        _: &Connection,
142        _queue_handle: &QueueHandle<Self>,
143        seat: WlSeat,
144        capability: SeatCapability,
145    ) {
146        let seat_state = match self.seats.get_mut(&seat.id()) {
147            Some(seat_state) => seat_state,
148            None => {
149                warn!("Received wl_seat::remove_capability for unknown seat");
150                return;
151            },
152        };
153
154        if let Some(text_input) = seat_state.text_input.take() {
155            text_input.destroy();
156        }
157
158        match capability {
159            SeatCapability::Touch => {
160                if let Some(touch) = seat_state.touch.take() {
161                    if touch.version() >= 3 {
162                        touch.release();
163                    }
164                }
165            },
166            SeatCapability::Pointer => {
167                if let Some(relative_pointer) = seat_state.relative_pointer.take() {
168                    relative_pointer.destroy();
169                }
170
171                if let Some(pointer) = seat_state.pointer.take() {
172                    let pointer_data = pointer.pointer().winit_data();
173
174                    // Remove the cursor from the mapping.
175                    let surface_id = pointer.surface().id();
176                    let _ = self.pointer_surfaces.remove(&surface_id);
177
178                    // Remove the inner locks/confines before dropping the pointer.
179                    pointer_data.unlock_pointer();
180                    pointer_data.unconfine_pointer();
181
182                    if pointer.pointer().version() >= 3 {
183                        pointer.pointer().release();
184                    }
185                }
186            },
187            SeatCapability::Keyboard => {
188                seat_state.keyboard_state = None;
189                self.on_keyboard_destroy(&seat.id());
190            },
191            _ => (),
192        }
193    }
194
195    fn new_seat(
196        &mut self,
197        _connection: &Connection,
198        _queue_handle: &QueueHandle<Self>,
199        seat: WlSeat,
200    ) {
201        self.seats.insert(seat.id(), WinitSeatState::new());
202    }
203
204    fn remove_seat(
205        &mut self,
206        _connection: &Connection,
207        _queue_handle: &QueueHandle<Self>,
208        seat: WlSeat,
209    ) {
210        let _ = self.seats.remove(&seat.id());
211        self.on_keyboard_destroy(&seat.id());
212    }
213}
214
215impl WinitState {
216    fn on_keyboard_destroy(&mut self, seat: &ObjectId) {
217        for (window_id, window) in self.windows.get_mut() {
218            let mut window = window.lock().unwrap();
219            let had_focus = window.has_focus();
220            window.remove_seat_focus(seat);
221            if had_focus != window.has_focus() {
222                self.events_sink.push_window_event(WindowEvent::Focused(false), *window_id);
223            }
224        }
225    }
226}
227
228sctk::delegate_seat!(WinitState);