//! Seat handling. use std::cell::Cell; use std::sync::Arc; use foldhash::HashMap; use sctk::data_device_manager::data_device::DataDevice; use sctk::reexports::client::backend::ObjectId; use sctk::reexports::client::protocol::wl_seat::WlSeat; use sctk::reexports::client::protocol::wl_touch::WlTouch; use sctk::reexports::client::{Connection, Proxy, QueueHandle}; use sctk::reexports::protocols::wp::relative_pointer::zv1::client::zwp_relative_pointer_v1::ZwpRelativePointerV1; use sctk::reexports::protocols::wp::text_input::zv3::client::zwp_text_input_v3::ZwpTextInputV3; use sctk::seat::pointer::{PointerData, ThemeSpec, ThemedPointer}; use sctk::seat::{Capability as SeatCapability, SeatHandler, SeatState}; use tracing::warn; use wayland_protocols::wp::pointer_gestures::zv1::client::zwp_pointer_gesture_hold_v1::ZwpPointerGestureHoldV1; use wayland_protocols::wp::pointer_gestures::zv1::client::zwp_pointer_gesture_pinch_v1::ZwpPointerGesturePinchV1; use wayland_protocols::wp::tablet::zv2::client::zwp_tablet_seat_v2::ZwpTabletSeatV2; use winit_core::event::WindowEvent; use winit_core::keyboard::ModifiersState; use crate::state::WinitState; mod keyboard; mod pointer; mod text_input; mod touch; use keyboard::{KeyboardData, KeyboardState}; pub use pointer::pointer_gesture::{PointerGestureData, PointerGesturesState}; pub use pointer::relative_pointer::RelativePointerState; pub use pointer::{PointerConstraintsState, WinitPointerData, WinitPointerDataExt}; use text_input::TextInputData; pub use text_input::{ClientState as TextInputClientState, TextInputState}; use touch::TouchPoint; pub(crate) use crate::seat::text_input::ZwpTextInputV3Ext; #[derive(Debug, Default)] pub struct WinitSeatState { /// The pointer bound on the seat. pointer: Option>>, /// The touch bound on the seat. touch: Option, /// The mapping from touched points to the surfaces they're present. touch_map: HashMap, /// Id of the first touch event. first_touch_id: Option, /// The text input bound on the seat. text_input: Option>, /// The tablet input bound on the seat. tablet: Option>, /// The relative pointer bound on the seat. relative_pointer: Option, /// The pinch pointer gesture bound on the seat. pointer_gesture_pinch: Option, /// The hold pointer gesture bound on the seat. pointer_gesture_hold: Option, /// The drag-and-drop state data_device: Option, /// The keyboard bound on the seat. keyboard_state: Option, /// The current modifiers state on the seat. modifiers: ModifiersState, /// Whether we have pending modifiers. modifiers_pending: bool, /// Serial of the most recent input event (keyboard or pointer) on this seat. /// Written by both keyboard and pointer handlers through a shared reference, /// so interior mutability is required. pub(crate) latest_input_serial: Cell>, } impl WinitSeatState { pub fn new() -> Self { Default::default() } /// Returns the serial of the most recent input event on this seat, or `None` if none has /// been received yet. pub fn latest_serial(&self) -> Option { self.latest_input_serial.get() } pub(crate) fn data_device(&self) -> Option<&DataDevice> { self.data_device.as_ref() } pub(crate) fn pointer_data(&self) -> Option<&PointerData> { self.pointer.as_ref().and_then(|pointer| pointer.pointer().data()) } } impl SeatHandler for WinitState { fn seat_state(&mut self) -> &mut SeatState { &mut self.seat_state } fn new_capability( &mut self, _: &Connection, queue_handle: &QueueHandle, seat: WlSeat, capability: SeatCapability, ) { let seat_state = match self.seats.get_mut(&seat.id()) { Some(seat_state) => seat_state, None => { warn!("Received wl_seat::new_capability for unknown seat"); return; }, }; match capability { SeatCapability::Touch if seat_state.touch.is_none() => { seat_state.touch = self.seat_state.get_touch(queue_handle, &seat).ok(); }, SeatCapability::Keyboard if seat_state.keyboard_state.is_none() => { let keyboard = seat.get_keyboard(queue_handle, KeyboardData::new(seat.clone())); seat_state.keyboard_state = Some(KeyboardState::new(keyboard, self.loop_handle.clone())); }, SeatCapability::Pointer if seat_state.pointer.is_none() => { let surface = self.compositor_state.create_surface(queue_handle); let viewport = self .viewporter_state .as_ref() .map(|state| state.get_viewport(&surface, queue_handle)); let surface_id = surface.id(); let pointer_data = WinitPointerData::new(viewport); let themed_pointer = self .seat_state .get_pointer_with_theme_and_data( queue_handle, &seat, self.shm.wl_shm(), surface, ThemeSpec::System, pointer_data, ) .expect("failed to create pointer with present capability."); seat_state.data_device = self .data_device_manager_state .as_ref() .map(|device| device.get_data_device(queue_handle, &seat)); seat_state.relative_pointer = self.relative_pointer.as_ref().map(|manager| { manager.get_relative_pointer( themed_pointer.pointer(), queue_handle, sctk::globals::GlobalData, ) }); seat_state.pointer_gesture_pinch = self.pointer_gestures.as_ref().map(|manager| { manager.get_pinch_gesture( themed_pointer.pointer(), queue_handle, PointerGestureData::default(), ) }); seat_state.pointer_gesture_hold = self.pointer_gestures.as_ref().map(|manager| { manager.get_hold_gesture( themed_pointer.pointer(), queue_handle, PointerGestureData::default(), ) }); let themed_pointer = Arc::new(themed_pointer); // Register cursor surface. self.pointer_surfaces.insert(surface_id, themed_pointer.clone()); seat_state.pointer = Some(themed_pointer); }, _ => (), } if let Some(text_input_state) = seat_state.text_input.is_none().then_some(self.text_input_state.as_ref()).flatten() { seat_state.text_input = Some(Arc::new(text_input_state.get_text_input( &seat, queue_handle, TextInputData::default(), ))); } if let Some(tablet_state) = seat_state.tablet.is_none().then_some(self.tablet_state.as_ref()).flatten() { seat_state.tablet = Some(Arc::new(tablet_state.get_tablet_seat(&seat, queue_handle))); } } fn remove_capability( &mut self, _: &Connection, _queue_handle: &QueueHandle, seat: WlSeat, capability: SeatCapability, ) { let seat_state = match self.seats.get_mut(&seat.id()) { Some(seat_state) => seat_state, None => { warn!("Received wl_seat::remove_capability for unknown seat"); return; }, }; if let Some(text_input) = seat_state.text_input.take() { text_input.destroy(); } // NOTE: figure out when this should actually be destroyed. if let Some(tablet) = seat_state.tablet.take() { tablet.destroy(); } match capability { SeatCapability::Touch => { if let Some(touch) = seat_state.touch.take() { if touch.version() >= 3 { touch.release(); } } }, SeatCapability::Pointer => { if let Some(relative_pointer) = seat_state.relative_pointer.take() { relative_pointer.destroy(); } if let Some(pointer_gesture_pinch) = seat_state.pointer_gesture_pinch.take() { pointer_gesture_pinch.destroy(); } if let Some(pointer_gesture_hold) = seat_state.pointer_gesture_hold.take() { pointer_gesture_hold.destroy(); } seat_state.data_device = None; if let Some(pointer) = seat_state.pointer.take() { let pointer_data = pointer.pointer().winit_data(); // Remove the cursor from the mapping. let surface_id = pointer.surface().id(); let _ = self.pointer_surfaces.remove(&surface_id); // Remove the inner locks/confines before dropping the pointer. pointer_data.data().unlock_pointer(); pointer_data.data().unconfine_pointer(); if pointer.pointer().version() >= 3 { pointer.pointer().release(); } } }, SeatCapability::Keyboard => { seat_state.keyboard_state = None; self.on_keyboard_destroy(&seat.id()); }, _ => (), } } fn new_seat( &mut self, _connection: &Connection, _queue_handle: &QueueHandle, seat: WlSeat, ) { self.seats.insert(seat.id(), WinitSeatState::new()); } fn remove_seat( &mut self, _connection: &Connection, _queue_handle: &QueueHandle, seat: WlSeat, ) { let _ = self.seats.remove(&seat.id()); self.on_keyboard_destroy(&seat.id()); } } impl WinitState { fn on_keyboard_destroy(&mut self, seat: &ObjectId) { for (window_id, window) in self.windows.get_mut() { let mut window = window.lock().unwrap(); let had_focus = window.has_focus(); window.remove_seat_focus(seat); if had_focus != window.has_focus() { self.events_sink.push_window_event(WindowEvent::Focused(false), *window_id); } } } }