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 viewport = self
100                    .viewporter_state
101                    .as_ref()
102                    .map(|state| state.get_viewport(&surface, queue_handle));
103                let surface_id = surface.id();
104                let pointer_data = WinitPointerData::new(seat.clone(), viewport);
105                let themed_pointer = self
106                    .seat_state
107                    .get_pointer_with_theme_and_data(
108                        queue_handle,
109                        &seat,
110                        self.shm.wl_shm(),
111                        surface,
112                        ThemeSpec::System,
113                        pointer_data,
114                    )
115                    .expect("failed to create pointer with present capability.");
116
117                seat_state.relative_pointer = self.relative_pointer.as_ref().map(|manager| {
118                    manager.get_relative_pointer(
119                        themed_pointer.pointer(),
120                        queue_handle,
121                        sctk::globals::GlobalData,
122                    )
123                });
124
125                let themed_pointer = Arc::new(themed_pointer);
126
127                // Register cursor surface.
128                self.pointer_surfaces.insert(surface_id, themed_pointer.clone());
129
130                seat_state.pointer = Some(themed_pointer);
131            },
132            _ => (),
133        }
134
135        if let Some(text_input_state) =
136            seat_state.text_input.is_none().then_some(self.text_input_state.as_ref()).flatten()
137        {
138            seat_state.text_input = Some(Arc::new(text_input_state.get_text_input(
139                &seat,
140                queue_handle,
141                TextInputData::default(),
142            )));
143        }
144    }
145
146    fn remove_capability(
147        &mut self,
148        _: &Connection,
149        _queue_handle: &QueueHandle<Self>,
150        seat: WlSeat,
151        capability: SeatCapability,
152    ) {
153        let seat_state = match self.seats.get_mut(&seat.id()) {
154            Some(seat_state) => seat_state,
155            None => {
156                warn!("Received wl_seat::remove_capability for unknown seat");
157                return;
158            },
159        };
160
161        if let Some(text_input) = seat_state.text_input.take() {
162            text_input.destroy();
163        }
164
165        match capability {
166            SeatCapability::Touch => {
167                if let Some(touch) = seat_state.touch.take() {
168                    if touch.version() >= 3 {
169                        touch.release();
170                    }
171                }
172            },
173            SeatCapability::Pointer => {
174                if let Some(relative_pointer) = seat_state.relative_pointer.take() {
175                    relative_pointer.destroy();
176                }
177
178                if let Some(pointer) = seat_state.pointer.take() {
179                    let pointer_data = pointer.pointer().winit_data();
180
181                    // Remove the cursor from the mapping.
182                    let surface_id = pointer.surface().id();
183                    let _ = self.pointer_surfaces.remove(&surface_id);
184
185                    // Remove the inner locks/confines before dropping the pointer.
186                    pointer_data.unlock_pointer();
187                    pointer_data.unconfine_pointer();
188
189                    if pointer.pointer().version() >= 3 {
190                        pointer.pointer().release();
191                    }
192                }
193            },
194            SeatCapability::Keyboard => {
195                seat_state.keyboard_state = None;
196                self.on_keyboard_destroy(&seat.id());
197            },
198            _ => (),
199        }
200    }
201
202    fn new_seat(
203        &mut self,
204        _connection: &Connection,
205        _queue_handle: &QueueHandle<Self>,
206        seat: WlSeat,
207    ) {
208        self.seats.insert(seat.id(), WinitSeatState::new());
209    }
210
211    fn remove_seat(
212        &mut self,
213        _connection: &Connection,
214        _queue_handle: &QueueHandle<Self>,
215        seat: WlSeat,
216    ) {
217        let _ = self.seats.remove(&seat.id());
218        self.on_keyboard_destroy(&seat.id());
219    }
220}
221
222impl WinitState {
223    fn on_keyboard_destroy(&mut self, seat: &ObjectId) {
224        for (window_id, window) in self.windows.get_mut() {
225            let mut window = window.lock().unwrap();
226            let had_focus = window.has_focus();
227            window.remove_seat_focus(seat);
228            if had_focus != window.has_focus() {
229                self.events_sink.push_window_event(WindowEvent::Focused(false), *window_id);
230            }
231        }
232    }
233}
234
235sctk::delegate_seat!(WinitState);