use super::{ seat::{CursorInfo, KeyboardEvent, PointerEvent, SeatData}, state::{ClientState, WaylandState}, surface::CoreSurface, SERIAL_COUNTER, }; use crate::nodes::{ drawable::model::ModelPart, items::panel::{Backend, PanelItem, RecommendedState, SurfaceID}, Message, Node, }; use color_eyre::eyre::{eyre, Result}; use mint::Vector2; use nanoid::nanoid; use parking_lot::Mutex; use rustc_hash::FxHashMap; use serde::{ser::SerializeSeq, Serialize, Serializer}; use smithay::reexports::{ wayland_protocols::xdg::shell::server::{ xdg_popup::{self, XdgPopup}, xdg_positioner::{self, Anchor, ConstraintAdjustment, Gravity, XdgPositioner}, xdg_surface::{self, XdgSurface}, xdg_toplevel::{self, XdgToplevel, EVT_CONFIGURE_BOUNDS_SINCE, EVT_WM_CAPABILITIES_SINCE}, xdg_wm_base::{self, XdgWmBase}, }, wayland_server::{ backend::{ClientId, ObjectId}, protocol::wl_surface::WlSurface, Client, DataInit, Dispatch, DisplayHandle, GlobalDispatch, New, Resource, WEnum, Weak as WlWeak, }, }; use stardust_xr::schemas::flex::serialize; use std::{ fmt::Debug, sync::{Arc, Weak}, }; use tokio::sync::watch; use tracing::{debug, warn}; use xkbcommon::xkb::{self, ffi::XKB_KEYMAP_FORMAT_TEXT_V1, Keymap}; impl GlobalDispatch for WaylandState { fn bind( _state: &mut WaylandState, _handle: &DisplayHandle, _client: &Client, resource: New, _global_data: &(), data_init: &mut DataInit<'_, WaylandState>, ) { data_init.init(resource, ()); } } impl Dispatch for WaylandState { fn request( _state: &mut WaylandState, _client: &Client, _resource: &XdgWmBase, request: xdg_wm_base::Request, _data: &(), _dhandle: &DisplayHandle, data_init: &mut DataInit<'_, WaylandState>, ) { match request { xdg_wm_base::Request::CreatePositioner { id } => { let positioner = data_init.init(id, Mutex::new(PositionerData::default())); debug!(?positioner, "Create XDG positioner"); } xdg_wm_base::Request::GetXdgSurface { id, surface } => { let xdg_surface = data_init.init(id, Mutex::new(XdgSurfaceData::new(&surface))); debug!(?xdg_surface, "Create XDG surface"); } xdg_wm_base::Request::Pong { serial } => { debug!(serial, "Client pong"); } xdg_wm_base::Request::Destroy => { debug!("Destroy XDG WM base"); } _ => unreachable!(), } } } #[derive(Debug, Serialize, Clone, Copy)] pub struct PositionerData { size: Vector2, anchor_rect_pos: Vector2, anchor_rect_size: Vector2, anchor: u32, gravity: u32, constraint_adjustment: u32, offset: Vector2, reactive: bool, } impl Default for PositionerData { fn default() -> Self { Self { size: Vector2::from([0; 2]), anchor_rect_pos: Vector2::from([0; 2]), anchor_rect_size: Vector2::from([0; 2]), anchor: Anchor::None as u32, gravity: Gravity::None as u32, constraint_adjustment: ConstraintAdjustment::None.bits(), offset: Vector2::from([0; 2]), reactive: false, } } } impl Dispatch, WaylandState> for WaylandState { fn request( _state: &mut WaylandState, _client: &Client, positioner: &XdgPositioner, request: xdg_positioner::Request, data: &Mutex, _dhandle: &DisplayHandle, _data_init: &mut DataInit<'_, WaylandState>, ) { match request { xdg_positioner::Request::SetSize { width, height } => { debug!(?positioner, width, height, "Set positioner size"); data.lock().size = Vector2::from([width as u32, height as u32]); } xdg_positioner::Request::SetAnchorRect { x, y, width, height, } => { if width < 1 || height < 1 { positioner.post_error( xdg_positioner::Error::InvalidInput, "Invalid size for positioner's anchor rectangle.", ); warn!( ?positioner, width, height, "Invalid size for positioner's anchor rectangle" ); return; } debug!( ?positioner, x, y, width, height, "Set positioner anchor rectangle" ); let mut data = data.lock(); data.anchor_rect_pos = [x, y].into(); data.anchor_rect_size = [width as u32, height as u32].into(); } xdg_positioner::Request::SetAnchor { anchor } => { if let WEnum::Value(anchor) = anchor { debug!(?positioner, ?anchor, "Set positioner anchor"); data.lock().anchor = anchor as u32; } } xdg_positioner::Request::SetGravity { gravity } => { if let WEnum::Value(gravity) = gravity { debug!(?positioner, ?gravity, "Set positioner gravity"); data.lock().gravity = gravity as u32; } } xdg_positioner::Request::SetConstraintAdjustment { constraint_adjustment, } => { debug!( ?positioner, constraint_adjustment, "Set positioner constraint adjustment" ); data.lock().constraint_adjustment = constraint_adjustment; } xdg_positioner::Request::SetOffset { x, y } => { debug!(?positioner, x, y, "Set positioner offset"); data.lock().offset = [x, y].into(); } xdg_positioner::Request::SetReactive => { debug!(?positioner, "Set positioner reactive"); data.lock().reactive = true; } xdg_positioner::Request::SetParentSize { parent_width, parent_height, } => { debug!( ?positioner, parent_width, parent_height, "Set positioner parent size" ); } xdg_positioner::Request::SetParentConfigure { serial } => { debug!(?positioner, serial, "Set positioner parent size"); } xdg_positioner::Request::Destroy => (), _ => unreachable!(), } } } #[derive(Debug, Serialize, Clone, Copy)] pub struct Geometry { pub origin: Vector2, pub size: Vector2, } impl Default for Geometry { fn default() -> Self { Self { origin: Vector2::from([0; 2]), size: Vector2::from([0; 2]), } } } pub struct XdgSurfaceData { wl_surface: WlWeak, surface_id: SurfaceID, panel_item: Weak>, geometry: Option, } impl XdgSurfaceData { pub fn new(wl_surface: &WlSurface) -> Self { XdgSurfaceData { wl_surface: wl_surface.downgrade(), surface_id: SurfaceID::Toplevel, panel_item: Weak::new(), geometry: None, } } pub fn get(xdg_surface: &XdgSurface) -> Option<&Mutex> { xdg_surface.data::>() } pub fn wl_surface(&self) -> Option { self.wl_surface.upgrade().ok() } pub fn panel_item(&self) -> Option>> { self.panel_item.upgrade() } } // impl Clone for XdgSurfaceData { // fn clone(&self) -> Self { // Self { // wl_surface: self.wl_surface.clone(), // geometry: self.geometry.clone(), // surface_type: Mutex::new(self.surface_type.lock().clone()), // } // } // } impl Dispatch, WaylandState> for WaylandState { fn request( state: &mut WaylandState, client: &Client, xdg_surface: &XdgSurface, request: xdg_surface::Request, xdg_surface_data: &Mutex, _dhandle: &DisplayHandle, data_init: &mut DataInit<'_, WaylandState>, ) { match request { xdg_surface::Request::GetToplevel { id } => { let toplevel_state = Mutex::new(ToplevelData::new(xdg_surface)); let toplevel = data_init.init(id, toplevel_state); debug!(?toplevel, ?xdg_surface, "Create XDG toplevel"); if toplevel.version() >= EVT_WM_CAPABILITIES_SINCE { toplevel.wm_capabilities(vec![2, 3, 4]); } toplevel.configure( 0, 0, if toplevel.version() >= 2 { vec![5, 6, 7, 8] .into_iter() .flat_map(u32::to_ne_bytes) .collect() } else { vec![] }, ); xdg_surface.configure(SERIAL_COUNTER.inc()); let client_credentials = client.get_credentials(&state.display_handle).ok(); let Some(seat_data) = client.get_data::().map(|s| s.seat.clone()) else {return}; let Some(wl_surface) = xdg_surface_data.lock().wl_surface() else {return}; CoreSurface::add_to( state.display_handle.clone(), &wl_surface, { let toplevel = toplevel.downgrade(); move || { let toplevel = toplevel.upgrade().unwrap(); let toplevel_data = ToplevelData::get(&toplevel); let Some(xdg_surface) = toplevel_data.lock().xdg_surface() else {return}; let Some(xdg_surface_data) = XdgSurfaceData::get(&xdg_surface) else {return}; xdg_surface_data.lock().surface_id = SurfaceID::Toplevel; let Some(backend) = XDGBackend::create(toplevel.clone(), seat_data.clone()) else {return}; let (node, panel_item) = PanelItem::create( Box::new(backend), client_credentials.map(|c| c.pid), ); toplevel_data.lock().panel_item_node.replace(node); xdg_surface_data.lock().panel_item = Arc::downgrade(&panel_item); } }, { let toplevel = toplevel.downgrade(); move |_| { let toplevel = toplevel.upgrade().unwrap(); let toplevel_data = ToplevelData::get(&toplevel); let Some(panel_item) = toplevel_data.lock().panel_item() else { let Some(xdg_surface) = toplevel_data.lock().xdg_surface() else {return}; // if the wayland toplevel isn't mapped, hammer it again with a configure until it cooperates toplevel.configure( 0, 0, if toplevel.version() >= 2 { vec![5, 6, 7, 8].into_iter().flat_map(u32::to_ne_bytes).collect() } else { vec![] }, ); xdg_surface.configure(SERIAL_COUNTER.inc()); return }; panel_item.commit_toplevel(); } }, ); } xdg_surface::Request::GetPopup { id, parent, positioner, } => { let parent_clone = parent.clone().unwrap(); let parent_data = parent_clone.data::>().unwrap().lock(); // let positioner_data = positioner // .data::>() // .unwrap() // .lock() // .clone(); // let parent = match &*parent_data { // XdgSurfaceType::Toplevel(_) => SurfaceID::Toplevel, // XdgSurfaceType::Popup(p) => { // SurfaceID::Popup(p.upgrade().unwrap().uid.clone()) // } // XdgSurfaceType::Unknown => return, // }; let uid = nanoid!(); let popup_data = Mutex::new(PopupData::new( uid.clone(), xdg_surface, parent_data.surface_id.clone(), positioner, )); let xdg_popup = data_init.init(id, popup_data); xdg_surface_data.lock().surface_id = SurfaceID::Popup(uid); let panel_item = parent_data.panel_item().unwrap(); xdg_surface_data.lock().panel_item = Arc::downgrade(&panel_item); debug!(?xdg_popup, ?xdg_surface, "Create XDG popup"); let xdg_surface = xdg_surface.downgrade(); let xdg_popup = xdg_popup.downgrade(); CoreSurface::add_to( state.display_handle.clone(), &xdg_surface_data.lock().wl_surface.upgrade().unwrap(), move || { let xdg_popup = xdg_popup.upgrade().unwrap(); let Some(popup_data) = PopupData::get(&xdg_popup) else {return}; let popup_data = popup_data.lock(); // panel_item.commit_popup(popup_data); panel_item .backend .new_popup(&panel_item, &xdg_popup, &*popup_data); }, move |commit_count| { if commit_count == 0 { xdg_surface .upgrade() .unwrap() .configure(SERIAL_COUNTER.inc()) } }, ); } xdg_surface::Request::SetWindowGeometry { x, y, width, height, } => { debug!( ?xdg_surface, x, y, width, height, "Set XDG surface geometry" ); let geometry = Geometry { origin: [x, y].into(), size: [width as u32, height as u32].into(), }; xdg_surface_data.lock().geometry.replace(geometry); } xdg_surface::Request::AckConfigure { serial } => { debug!(?xdg_surface, serial, "Acknowledge XDG surface configure"); } xdg_surface::Request::Destroy => { debug!(?xdg_surface, "Destroy XDG surface"); } _ => unreachable!(), } } } fn serde_error(msg: &str) -> Result { Err(serde::ser::Error::custom(msg)) } #[derive(Debug, Clone)] pub struct ToplevelData { panel_item_node: Option>, xdg_surface: WlWeak, parent: Option>, title: Option, app_id: Option, max_size: Option>, min_size: Option>, states: Vec, } impl ToplevelData { fn new(xdg_surface: &XdgSurface) -> Self { ToplevelData { panel_item_node: None, xdg_surface: xdg_surface.downgrade(), parent: None, title: None, app_id: None, max_size: None, min_size: None, states: Vec::new(), } } pub fn get(toplevel: &XdgToplevel) -> &Mutex { toplevel.data::>().unwrap() } pub fn xdg_surface(&self) -> Option { self.xdg_surface.upgrade().ok() } fn panel_item(&self) -> Option>> { let xdg_surface = self.xdg_surface()?; let xdg_surface_data = XdgSurfaceData::get(&xdg_surface)?.lock(); xdg_surface_data.panel_item() } } impl Serialize for ToplevelData { fn serialize(&self, serializer: S) -> Result { let Some(xdg_surface) = self.xdg_surface() else {return serializer.serialize_none()}; let Some(xdg_surface_data) = XdgSurfaceData::get(&xdg_surface) else {return serializer.serialize_none()}; let xdg_surface_data = xdg_surface_data.lock(); let geometry = xdg_surface_data.geometry.clone(); let Some(wl_surface) = xdg_surface_data.wl_surface() else {return serde_error::("Wayland surface not found")}; let Some(core_surface) = CoreSurface::from_wl_surface(&wl_surface) else {return serde_error::("Core surface not found")}; let Some(size) = core_surface.size() else {return serializer.serialize_none()}; let geometry = geometry.unwrap_or_else(|| Geometry { origin: [0; 2].into(), size, }); let mut seq = serializer.serialize_seq(None)?; // Parent UID seq.serialize_element(&self.parent.as_ref().and_then(|p| { Some( ToplevelData::get(&p.upgrade().ok()?) .lock() .panel_item()? .uid .clone(), ) }))?; seq.serialize_element(&self.title)?; seq.serialize_element(&self.app_id)?; seq.serialize_element(&size)?; seq.serialize_element(&self.min_size)?; seq.serialize_element(&self.max_size)?; seq.serialize_element(&geometry)?; seq.serialize_element(&self.states)?; seq.end() } } impl Dispatch, WaylandState> for WaylandState { fn request( _state: &mut WaylandState, _client: &Client, xdg_toplevel: &XdgToplevel, request: xdg_toplevel::Request, data: &Mutex, _dhandle: &DisplayHandle, _data_init: &mut DataInit<'_, WaylandState>, ) { match request { xdg_toplevel::Request::SetParent { parent } => { debug!(?xdg_toplevel, ?parent, "Set XDG Toplevel parent"); data.lock().parent = parent.map(|toplevel| toplevel.downgrade()); } xdg_toplevel::Request::SetTitle { title } => { debug!(?xdg_toplevel, ?title, "Set XDG Toplevel title"); data.lock().title = (!title.is_empty()).then_some(title); } xdg_toplevel::Request::SetAppId { app_id } => { debug!(?xdg_toplevel, ?app_id, "Set XDG Toplevel app ID"); data.lock().app_id = (!app_id.is_empty()).then_some(app_id); } xdg_toplevel::Request::ShowWindowMenu { seat, serial, x, y } => { debug!( ?xdg_toplevel, ?seat, serial, x, y, "Show XDG Toplevel window menu" ); } xdg_toplevel::Request::Move { seat, serial } => { debug!(?xdg_toplevel, ?seat, serial, "XDG Toplevel move request"); let Some(panel_item) = data.lock().panel_item() else {return}; panel_item.recommend_toplevel_state(RecommendedState::Move); } xdg_toplevel::Request::Resize { seat, serial, edges, } => { let WEnum::Value(edges) = edges else {return}; debug!( ?xdg_toplevel, ?seat, serial, ?edges, "XDG Toplevel resize request" ); let Some(panel_item) = data.lock().panel_item() else {return}; panel_item.recommend_toplevel_state(RecommendedState::Resize(edges as u32)); } xdg_toplevel::Request::SetMaxSize { width, height } => { debug!(?xdg_toplevel, width, height, "Set XDG Toplevel max size"); data.lock().max_size = (width > 1 || height > 1) .then_some(Vector2::from([width as u32, height as u32])); } xdg_toplevel::Request::SetMinSize { width, height } => { debug!(?xdg_toplevel, width, height, "Set XDG Toplevel min size"); data.lock().min_size = (width > 1 || height > 1) .then_some(Vector2::from([width as u32, height as u32])); } xdg_toplevel::Request::SetMaximized => { let Some(panel_item) = data.lock().panel_item() else {return}; panel_item.recommend_toplevel_state(RecommendedState::Maximize(true)); } xdg_toplevel::Request::UnsetMaximized => { let Some(panel_item) = data.lock().panel_item() else {return}; panel_item.recommend_toplevel_state(RecommendedState::Maximize(false)); } xdg_toplevel::Request::SetFullscreen { output: _ } => { let Some(panel_item) = data.lock().panel_item() else {return}; panel_item.recommend_toplevel_state(RecommendedState::Fullscreen(true)); } xdg_toplevel::Request::UnsetFullscreen => { let Some(panel_item) = data.lock().panel_item() else {return}; panel_item.recommend_toplevel_state(RecommendedState::Fullscreen(true)); } xdg_toplevel::Request::SetMinimized => { let Some(panel_item) = data.lock().panel_item() else {return}; panel_item.recommend_toplevel_state(RecommendedState::Minimize); } xdg_toplevel::Request::Destroy => { debug!(?xdg_toplevel, "Destroy XDG Toplevel"); let Some(panel_item) = data.lock().panel_item() else {return}; panel_item.backend.on_drop(); } _ => unreachable!(), } } } #[derive(Clone)] pub struct PopupData { pub uid: String, grabbed: bool, parent_id: SurfaceID, positioner: XdgPositioner, xdg_surface: WlWeak, } impl PopupData { fn new( uid: impl ToString, xdg_surface: &XdgSurface, parent_id: SurfaceID, positioner: XdgPositioner, ) -> Self { PopupData { uid: uid.to_string(), grabbed: false, parent_id, positioner, xdg_surface: xdg_surface.downgrade(), } } pub fn get(popup: &XdgPopup) -> Option<&Mutex> { popup.data::>() } pub fn xdg_surface(&self) -> Option { self.xdg_surface.upgrade().ok() } fn panel_item(&self) -> Option>> { XdgSurfaceData::get(&self.xdg_surface()?)? .lock() .panel_item() } // fn get_parent(&self) -> Option { // self.parent.as_ref()?.upgrade().ok() // } pub fn wl_surface(&self) -> Option { XdgSurfaceData::get(&self.xdg_surface()?)? .lock() .wl_surface() } pub fn positioner_data(&self) -> Option { Some( self.positioner .data::>()? .lock() .clone(), ) } } impl Serialize for PopupData { fn serialize(&self, serializer: S) -> Result { let Some(positioner_data) = self.positioner_data() else {return serde_error::("Positioner not found")}; let mut seq = serializer.serialize_seq(None)?; seq.serialize_element(&self.uid)?; seq.serialize_element(&self.parent_id)?; seq.serialize_element(&positioner_data)?; seq.end() } } impl Debug for PopupData { fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { f.debug_struct("XdgPopupData") .field("uid", &self.uid) .field("positioner", &self.positioner) .field("xdg_surface", &self.xdg_surface) .finish() } } impl Dispatch, WaylandState> for WaylandState { fn request( _state: &mut WaylandState, _client: &Client, xdg_popup: &XdgPopup, request: xdg_popup::Request, data: &Mutex, _dhandle: &DisplayHandle, _data_init: &mut DataInit<'_, WaylandState>, ) { match request { xdg_popup::Request::Grab { seat, serial } => { let mut data = data.lock(); data.grabbed = true; debug!(?xdg_popup, ?seat, serial, "XDG popup grab"); let Some(panel_item) = data.panel_item() else {return}; panel_item.grab_keyboard(Some(SurfaceID::Popup(data.uid.clone()))); } xdg_popup::Request::Reposition { positioner, token } => { let mut data = data.lock(); debug!(?xdg_popup, ?positioner, token, "XDG popup reposition"); data.positioner = positioner; let Some(panel_item) = data.panel_item() else {return}; panel_item.backend.reposition_popup(&panel_item, &*data) } xdg_popup::Request::Destroy => { let data = data.lock(); debug!(?xdg_popup, "Destroy XDG popup"); if data.grabbed { let Some(panel_item) = data.panel_item() else {return}; panel_item.grab_keyboard(None); } } _ => unreachable!(), } } fn destroyed( _state: &mut WaylandState, _client: ClientId, _resource: ObjectId, data: &Mutex, ) { let data = data.lock(); let Some(panel_item) = data.panel_item() else {return}; panel_item.backend.drop_popup(&panel_item, &data.uid); } } pub struct XDGBackend { toplevel: WlWeak, toplevel_wl_surface: WlWeak, popups: Mutex>>, cursor: watch::Receiver>, pub seat: Arc, pointer_grab: Mutex>, keyboard_grab: Mutex>, } impl XDGBackend { pub fn create(toplevel: XdgToplevel, seat: Arc) -> Option { let toplevel_wl_surface = XdgSurfaceData::get(&ToplevelData::get(&toplevel).lock().xdg_surface()?)? .lock() .wl_surface()? .downgrade(); let cursor = seat.new_surface(&toplevel_wl_surface.upgrade().ok()?); Some(XDGBackend { toplevel: toplevel.downgrade(), toplevel_wl_surface, popups: Mutex::new(FxHashMap::default()), cursor, seat, pointer_grab: Mutex::new(None), keyboard_grab: Mutex::new(None), }) } fn wl_surface_from_id(&self, id: &SurfaceID) -> Option { match id { SurfaceID::Cursor => self.cursor.borrow().as_ref()?.surface.upgrade().ok(), SurfaceID::Toplevel => self.toplevel_wl_surface(), SurfaceID::Popup(popup) => { let popups = self.popups.lock(); let popup = popups.get(popup)?.upgrade().ok()?; let wl_surface = PopupData::get(&popup)?.lock().wl_surface(); wl_surface } } } fn toplevel(&self) -> Option { self.toplevel.upgrade().ok() } fn toplevel_xdg_surface(&self) -> Option { let toplevel = self.toplevel()?; let data = ToplevelData::get(&toplevel).lock(); data.xdg_surface() } fn toplevel_wl_surface(&self) -> Option { self.toplevel_wl_surface.upgrade().ok() } fn input_surfaces(&self) -> Vec { let mut surfaces = self .toplevel_wl_surface() .map(|s| vec![s]) .unwrap_or_default(); surfaces.extend(self.popups.lock().values().filter_map(|p| { let popup = p.upgrade().ok()?; let popup_data = PopupData::get(&popup)?.lock(); let xdg_surface = popup_data.xdg_surface()?; let xdg_surface_data = XdgSurfaceData::get(&xdg_surface)?.lock(); xdg_surface_data.wl_surface() })); surfaces } pub fn new_popup( &self, panel_item: &PanelItem, popup: &XdgPopup, data: &PopupData, ) { let uid = data.uid.clone(); self.popups.lock().insert(uid.clone(), popup.downgrade()); let Some(node) = panel_item.node() else {return}; let Ok(message) = serialize(&(&uid, data)) else {return}; let _ = node.send_remote_signal("new_popup", message); } pub fn reposition_popup(&self, panel_item: &PanelItem, popup_state: &PopupData) { let Some(node) = panel_item.node() else {return}; let _ = node.send_remote_signal( "reposition_popup", serialize(popup_state.positioner_data().unwrap()).unwrap(), ); } pub fn drop_popup(&self, panel_item: &PanelItem, uid: &str) { 'seat_drop: { let Some(popup) = self .popups .lock() .remove(uid) else {break 'seat_drop}; let Some(popup) = popup.upgrade().ok() else {break 'seat_drop}; let Some(popup) = popup.data::>().cloned() else {break 'seat_drop}; let Some(wl_surface) = popup.wl_surface() else {break 'seat_drop}; self.seat.drop_surface(&wl_surface); } let Some(node) = panel_item.node() else {return}; let Ok(message) = serialize(uid) else {return}; let _ = node.send_remote_signal("drop_popup", message); } fn popups_data(&self) -> Vec { self.popups .lock() .values() .filter_map(|v| Some(v.upgrade().ok()?.data::>()?.lock().clone())) .collect::>() } pub fn on_drop(&self) { let Some(toplevel) = self.toplevel_wl_surface() else {return}; self.seat.drop_surface(&toplevel); debug!("Dropped panel item gracefully"); } fn flush_client(&self) { let Some(client) = self.toplevel_wl_surface().and_then(|s| s.client()) else {return}; if let Some(client_state) = client.get_data::() { client_state.flush(); } } } impl Backend for XDGBackend { fn serialize_start_data(&self, id: &str) -> Result { let toplevel_state = self .toplevel() .map(|t| ToplevelData::get(&t).lock().clone()); let pointer_grab = self.pointer_grab.lock().clone(); let keyboard_grab = self.keyboard_grab.lock().clone(); Ok(serialize(( id, ( self.cursor.borrow().as_ref().and_then(|c| c.cursor_data()), toplevel_state, self.popups_data(), pointer_grab, keyboard_grab, ), ))? .into()) } fn serialize_toplevel(&self) -> Result { let toplevel = self .toplevel() .ok_or_else(|| eyre!("Toplevel does not exist"))?; let state = ToplevelData::get(&toplevel); let data = serialize(&state.lock().clone())?; Ok(data.into()) } fn set_toplevel_capabilities(&self, capabilities: Vec) { let Ok(xdg_toplevel) = self.toplevel.upgrade() else {return}; let Some(xdg_surface) = self.toplevel_xdg_surface() else {return}; if xdg_toplevel.version() < EVT_WM_CAPABILITIES_SINCE { return; } xdg_toplevel.wm_capabilities(capabilities); xdg_surface.configure(SERIAL_COUNTER.inc()); self.flush_client(); } fn configure_toplevel( &self, size: Option>, states: Vec, bounds: Option>, ) { let Ok(xdg_toplevel) = self.toplevel.upgrade() else {return}; let Some(xdg_surface) = self.toplevel_xdg_surface() else {return}; debug!(?size, ?states, ?bounds, "Configure toplevel info"); if let Some(bounds) = bounds { if xdg_toplevel.version() > EVT_CONFIGURE_BOUNDS_SINCE { xdg_toplevel.configure_bounds(bounds.x as i32, bounds.y as i32); } } let size = size.unwrap_or(Vector2::from([0; 2])); xdg_toplevel.configure( size.x as i32, size.y as i32, states .into_iter() .flat_map(|state| state.to_ne_bytes()) .collect(), ); xdg_surface.configure(SERIAL_COUNTER.inc()); self.flush_client(); } fn apply_surface_material(&self, surface: SurfaceID, model_part: &Arc) { let Some(wl_surface) = self.wl_surface_from_id(&surface) else {return}; let Some(core_surface) = CoreSurface::from_wl_surface(&wl_surface) else {return}; core_surface.apply_material(model_part); } fn pointer_motion(&self, surface: &SurfaceID, position: Vector2) { let Some(surface) = self.wl_surface_from_id(surface) else {return}; self.seat .pointer_event(&surface, PointerEvent::Motion(position)); } fn pointer_button(&self, surface: &SurfaceID, button: u32, pressed: bool) { let Some(surface) = self.wl_surface_from_id(surface) else {return}; self.seat.pointer_event( &surface, PointerEvent::Button { button, state: if pressed { 1 } else { 0 }, }, ) } fn pointer_scroll( &self, surface: &SurfaceID, scroll_distance: Option>, scroll_steps: Option>, ) { let Some(surface) = self.wl_surface_from_id(surface) else {return}; self.seat.pointer_event( &surface, PointerEvent::Scroll { axis_continuous: scroll_distance, axis_discrete: scroll_steps, }, ) } fn keyboard_set_keymap(&self, keymap: &str) -> Result<()> { let context = xkb::Context::new(0); let keymap = Keymap::new_from_string(&context, keymap.to_string(), XKB_KEYMAP_FORMAT_TEXT_V1, 0) .ok_or_else(|| eyre!("Keymap is not valid"))?; self.seat.set_keymap(&keymap, self.input_surfaces()); Ok(()) } fn keyboard_key(&self, surface: &SurfaceID, key: u32, state: bool) { let Some(surface) = self.wl_surface_from_id(surface) else {return}; self.seat.keyboard_event( &surface, KeyboardEvent::Key { key, state: if state { 1 } else { 0 }, }, ) } }