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

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