use super::{state::WaylandState, surface::CoreSurface, GLOBAL_DESTROY_QUEUE}; use crate::nodes::items::Item; use anyhow::Result; use mint::Vector2; use nanoid::nanoid; use once_cell::sync::OnceCell; use parking_lot::Mutex; use smithay::{ input::keyboard::{KeymapFile, ModifiersState}, reexports::wayland_server::{ backend::{ClientId, GlobalId}, delegate_dispatch, delegate_global_dispatch, protocol::{ wl_keyboard::{self, KeyState, WlKeyboard}, wl_pointer::{self, WlPointer}, wl_seat::{self, Capability, WlSeat, EVT_NAME_SINCE}, wl_touch::{self, WlTouch}, }, Client, DataInit, Dispatch, DisplayHandle, GlobalDispatch, New, Resource, }, wayland::compositor, }; use std::sync::Arc; use std::{ops::Deref, sync::Weak}; use xkbcommon::xkb::{self, Keymap}; pub struct Cursor { pub core_surface: Weak, pub hotspot: Vector2, } pub struct KeyboardInfo { pub keymap: KeymapFile, pub state: xkb::State, pub mods: ModifiersState, } impl KeyboardInfo { pub fn new(keymap: &Keymap) -> Self { KeyboardInfo { state: xkb::State::new(keymap), keymap: KeymapFile::new(keymap, None), mods: ModifiersState::default(), } } pub fn process(&mut self, key: u32, state: u32, keyboard: &WlKeyboard) -> Result<()> { let wl_key_state = match state { 0 => KeyState::Released, 1 => KeyState::Pressed, _ => anyhow::bail!("Invalid key state!"), }; let xkb_key_state = match state { 0 => xkb::KeyDirection::Up, 1 => xkb::KeyDirection::Down, _ => anyhow::bail!("Invalid key state!"), }; let state_components = self.state.update_key(key + 8, xkb_key_state); if state_components != 0 { self.mods.update_with(&self.state); keyboard.modifiers( 0, self.mods.serialized.depressed, self.mods.serialized.latched, self.mods.serialized.locked, 0, ); } keyboard.key(0, 0, key, wl_key_state); Ok(()) } } unsafe impl Send for KeyboardInfo {} pub struct SeatDelegate; #[derive(Clone)] pub struct SeatData(Arc); impl SeatData { pub fn new(dh: &DisplayHandle, client: ClientId) -> Self { let seat_data = SeatData(Arc::new(SeatDataInner { client, global_id: OnceCell::new(), panel_item: OnceCell::new(), cursor: Mutex::new(None), cursor_changed: Mutex::new(false), pointer: OnceCell::new(), pointer_active: Mutex::new(false), keyboard: OnceCell::new(), keyboard_info: Mutex::new(None), touch: OnceCell::new(), })); seat_data .global_id .set(dh.create_global::(7, seat_data.clone())) .unwrap(); seat_data } } impl Deref for SeatData { type Target = SeatDataInner; fn deref(&self) -> &Self::Target { &self.0 } } pub struct SeatDataInner { client: ClientId, pub global_id: OnceCell, pub panel_item: OnceCell>, pub cursor: Mutex>>>, pub cursor_changed: Mutex, pointer: OnceCell, pub pointer_active: Mutex, keyboard: OnceCell, pub keyboard_info: Mutex>, touch: OnceCell, } impl SeatDataInner { pub fn pointer(&self) -> Option<&WlPointer> { self.pointer.get() } pub fn pointer_active(&self) -> bool { *self.pointer_active.lock() } pub fn keyboard(&self) -> Option<&WlKeyboard> { self.keyboard.get() } #[allow(dead_code)] pub fn touch(&self) -> Option<&WlTouch> { self.touch.get() } } impl Drop for SeatDataInner { fn drop(&mut self) { let id = self.global_id.take().unwrap(); tokio::spawn(async move { GLOBAL_DESTROY_QUEUE.get().unwrap().send(id).await }); } } impl GlobalDispatch for SeatDelegate { fn bind( _state: &mut WaylandState, _handle: &DisplayHandle, _client: &Client, resource: New, data: &SeatData, data_init: &mut DataInit<'_, WaylandState>, ) { let resource = data_init.init(resource, data.clone()); if resource.version() >= EVT_NAME_SINCE { resource.name(nanoid!()); } resource.capabilities(Capability::Pointer | Capability::Keyboard); } fn can_view(client: Client, data: &SeatData) -> bool { client.id() == data.0.client } } delegate_global_dispatch!(WaylandState: [WlSeat: SeatData] => SeatDelegate); impl Dispatch for SeatDelegate { fn request( _state: &mut WaylandState, _client: &Client, _resource: &WlSeat, request: ::Request, data: &SeatData, _dh: &DisplayHandle, data_init: &mut DataInit<'_, WaylandState>, ) { match request { wl_seat::Request::GetPointer { id } => { let _ = data.0.pointer.set(data_init.init(id, data.clone())); } wl_seat::Request::GetKeyboard { id } => { let keyboard = data_init.init(id, data.clone()); keyboard.repeat_info(0, 0); let _ = data.0.keyboard.set(keyboard); } wl_seat::Request::GetTouch { id } => { let _ = data.0.touch.set(data_init.init(id, data.clone())); } wl_seat::Request::Release => (), _ => unreachable!(), } } } delegate_dispatch!(WaylandState: [WlSeat: SeatData] => SeatDelegate); impl Dispatch for SeatDelegate { fn request( state: &mut WaylandState, _client: &Client, _resource: &WlPointer, request: ::Request, seat_data: &SeatData, dh: &DisplayHandle, _data_init: &mut DataInit<'_, WaylandState>, ) { match request { wl_pointer::Request::SetCursor { serial: _, surface, hotspot_x, hotspot_y, } => { if !*seat_data.pointer_active.lock() { return; } *seat_data.0.cursor_changed.lock() = true; if let Some(surface) = surface.as_ref() { compositor::with_states(surface, |data| { data.data_map.insert_if_missing_threadsafe(|| { CoreSurface::new( &state.weak_ref.upgrade().unwrap(), &state.display, dh.clone(), surface, ) }); if !data.data_map.insert_if_missing_threadsafe(|| { Arc::new(Mutex::new(Cursor { core_surface: Arc::downgrade( data.data_map.get::>().unwrap(), ), hotspot: Vector2::from([hotspot_x, hotspot_y]), })) }) { let mut cursor = data.data_map.get::>>().unwrap().lock(); cursor.hotspot = Vector2::from([hotspot_x, hotspot_y]); } }) } *seat_data.cursor.lock() = surface.and_then(|surf| { compositor::with_states(&surf, |data| { data.data_map.get::>>().cloned() }) }); } wl_pointer::Request::Release => (), _ => unreachable!(), } } } delegate_dispatch!(WaylandState: [WlPointer: SeatData] => SeatDelegate); impl Dispatch for SeatDelegate { fn request( _state: &mut WaylandState, _client: &Client, _resource: &WlKeyboard, request: ::Request, _data: &SeatData, _dh: &DisplayHandle, _data_init: &mut DataInit<'_, WaylandState>, ) { match request { wl_keyboard::Request::Release => (), _ => unreachable!(), } } } delegate_dispatch!(WaylandState: [WlKeyboard: SeatData] => SeatDelegate); impl Dispatch for SeatDelegate { fn request( _state: &mut WaylandState, _client: &Client, _resource: &WlTouch, request: ::Request, _data: &SeatData, _dh: &DisplayHandle, _data_init: &mut DataInit<'_, WaylandState>, ) { match request { wl_touch::Request::Release => (), _ => unreachable!(), } } } delegate_dispatch!(WaylandState: [WlTouch: SeatData] => SeatDelegate);