From 51d0cab832458aa6dcf4aba18985b61ec850d401 Mon Sep 17 00:00:00 2001 From: Nova Date: Sun, 23 Jul 2023 19:59:35 -0400 Subject: [PATCH] refactor: trait away panel item backends --- src/nodes/items/environment.rs | 9 +- src/nodes/items/mod.rs | 37 +- src/nodes/items/panel.rs | 490 ++++++++++++++++++++ src/wayland/mod.rs | 4 +- src/wayland/panel_item.rs | 805 --------------------------------- src/wayland/seat.rs | 87 ++-- src/wayland/state.rs | 7 + src/wayland/surface.rs | 26 +- src/wayland/xdg_shell.rs | 308 +++++++++++-- src/wayland/xwayland.rs | 214 +++++++-- 10 files changed, 1037 insertions(+), 950 deletions(-) create mode 100644 src/nodes/items/panel.rs delete mode 100644 src/wayland/panel_item.rs diff --git a/src/nodes/items/environment.rs b/src/nodes/items/environment.rs index c81c890..311df36 100644 --- a/src/nodes/items/environment.rs +++ b/src/nodes/items/environment.rs @@ -1,4 +1,4 @@ -use super::{Item, ItemSpecialization, ItemType}; +use super::{Item, ItemType}; use crate::{ core::{ client::{Client, INTERNAL_CLIENT}, @@ -52,10 +52,9 @@ impl EnvironmentItem { }; Ok(flexbuffers::singleton(environment_item.path.as_str())) } -} -impl ItemSpecialization for EnvironmentItem { - fn serialize_start_data(&self, id: &str) -> Option> { - serialize((id, self.path.as_str())).ok() + + pub fn serialize_start_data(&self, id: &str) -> Result> { + serialize((id, self.path.as_str())).map_err(|e| e.into()) } } diff --git a/src/nodes/items/mod.rs b/src/nodes/items/mod.rs index 9887467..eeb2cd4 100644 --- a/src/nodes/items/mod.rs +++ b/src/nodes/items/mod.rs @@ -1,6 +1,8 @@ mod environment; +pub mod panel; use self::environment::{EnvironmentItem, ITEM_TYPE_INFO_ENVIRONMENT}; +use self::panel::{PanelItemTrait, ITEM_TYPE_INFO_PANEL}; use super::fields::Field; use super::spatial::{find_spatial_parent, parse_transform, Spatial}; use super::{Alias, Node}; @@ -9,8 +11,6 @@ use crate::core::node_collections::LifeLinkedNodeMap; use crate::core::registry::Registry; use crate::nodes::alias::AliasInfo; use crate::nodes::fields::find_field; -#[cfg(feature = "wayland")] -use crate::wayland::panel_item::{PanelItem, ITEM_TYPE_INFO_PANEL}; use color_eyre::eyre::{ensure, eyre, Result}; use lazy_static::lazy_static; use nanoid::nanoid; @@ -20,7 +20,6 @@ use serde::Deserialize; use stardust_xr::schemas::flex::{deserialize, flexbuffers, serialize}; use stardust_xr::values::Transform; use std::hash::Hash; -use std::ops::Deref; use std::sync::{Arc, Weak}; lazy_static! { @@ -171,26 +170,28 @@ impl Drop for Item { } } -pub trait ItemSpecialization { - fn serialize_start_data(&self, id: &str) -> Option>; -} - pub enum ItemType { Environment(EnvironmentItem), - #[cfg(feature = "wayland")] - Panel(Arc), + Panel(Arc), } -impl Deref for ItemType { - type Target = dyn ItemSpecialization; - - fn deref(&self) -> &Self::Target { +impl ItemType { + fn serialize_start_data(&self, id: &str) -> Result> { match self { - ItemType::Environment(item) => item, - #[cfg(feature = "wayland")] - ItemType::Panel(item) => item.as_ref(), + ItemType::Environment(e) => e.serialize_start_data(id), + ItemType::Panel(p) => p.serialize_start_data(id), } } } +// impl Deref for ItemType { +// type Target = dyn ItemSpecialization; + +// fn deref(&self) -> &Self::Target { +// match self { +// ItemType::Environment(item) => item, +// ItemType::Panel(item) => item.as_ref(), +// } +// } +// } pub struct ItemUI { node: Weak, @@ -240,7 +241,7 @@ impl ItemUI { self.item_aliases.add(item.uid.clone(), &alias_node); } - let Some(serialized_data) = item.specialization.serialize_start_data(&item.uid) else {return}; + let Ok(serialized_data) = item.specialization.serialize_start_data(&item.uid) else {return}; let _ = node.send_remote_signal("create_item", &serialized_data); } fn handle_destroy_item(&self, item: &Item) { @@ -359,7 +360,7 @@ impl ItemAcceptor { self.accepted_aliases.add(item.uid.clone(), &alias_node); } - let Some(serialized_data) = item.specialization.serialize_start_data(&item.uid) else {return}; + let Ok(serialized_data) = item.specialization.serialize_start_data(&item.uid) else {return}; let _ = node.send_remote_signal("capture", &serialized_data); } fn handle_release(&self, item: &Item) { diff --git a/src/nodes/items/panel.rs b/src/nodes/items/panel.rs new file mode 100644 index 0000000..ddadfc1 --- /dev/null +++ b/src/nodes/items/panel.rs @@ -0,0 +1,490 @@ +use crate::{ + core::{ + client::{get_env, startup_settings, Client, INTERNAL_CLIENT}, + registry::Registry, + }, + nodes::{ + drawable::{model::ModelPart, Drawable}, + items::{self, Item, ItemType, TypeInfo}, + spatial::Spatial, + Node, + }, +}; +use color_eyre::eyre::{bail, eyre, Result}; +use glam::Mat4; +use lazy_static::lazy_static; +use mint::Vector2; +use nanoid::nanoid; +use serde::{ + de::{Deserializer, Error, SeqAccess, Visitor}, + ser::Serializer, + Deserialize, Serialize, +}; +use stardust_xr::schemas::flex::{deserialize, serialize}; +use std::sync::{Arc, Weak}; +use tracing::debug; + +lazy_static! { + pub static ref ITEM_TYPE_INFO_PANEL: TypeInfo = TypeInfo { + type_name: "panel", + aliased_local_signals: vec![ + "apply_surface_material", + "configure_toplevel", + "set_toplevel_capabilities", + "pointer_scroll", + "pointer_button", + "pointer_motion", + "keyboard_key", + "keyboard_set_keymap_names", + "keyboard_set_keymap_string", + "close", + ], + aliased_local_methods: vec![], + aliased_remote_signals: vec![ + "commit_toplevel", + "recommend_toplevel_state", + "set_cursor", + "new_popup", + "reposition_popup", + "drop_popup", + ], + ui: Default::default(), + items: Registry::new(), + acceptors: Registry::new(), + }; +} + +/// An ID for a surface inside this panel item +#[derive(Debug, Clone)] +#[allow(dead_code)] +pub enum SurfaceID { + Cursor, + Toplevel, + Popup(String), +} +impl Default for SurfaceID { + fn default() -> Self { + Self::Toplevel + } +} + +impl<'de> serde::Deserialize<'de> for SurfaceID { + fn deserialize>(deserializer: D) -> Result { + deserializer.deserialize_seq(SurfaceIDVisitor) + } +} + +struct SurfaceIDVisitor; + +impl<'de> Visitor<'de> for SurfaceIDVisitor { + type Value = SurfaceID; + + fn expecting(&self, f: &mut std::fmt::Formatter) -> std::fmt::Result { + f.write_str("idk") + } + + fn visit_seq>(self, mut seq: A) -> Result { + let Some(discrim) = seq.next_element()? else { + return Err(A::Error::missing_field("discrim")); + }; + + // idk if you wanna check for extraneous elements + // I didn't bother + + match discrim { + "Cursor" => Ok(SurfaceID::Cursor), + "Toplevel" => Ok(SurfaceID::Toplevel), + "Popup" => { + let Some(text) = seq.next_element()? else { + return Err(A::Error::missing_field("popup_text")); + }; + Ok(SurfaceID::Popup(text)) + } + _ => Err(A::Error::unknown_variant( + discrim, + &["Cursor", "Toplevel", "Popup"], + )), + } + } +} + +impl serde::Serialize for SurfaceID { + fn serialize(&self, serializer: S) -> Result { + match self { + Self::Cursor => ["Cursor"].serialize(serializer), + Self::Toplevel => ["Toplevel"].serialize(serializer), + Self::Popup(text) => ["Popup", text].serialize(serializer), + } + } +} + +#[derive(Debug, Clone, Copy, Serialize)] +#[serde(tag = "type", content = "content")] +pub enum RecommendedState { + Maximize(bool), + Fullscreen(bool), + Minimize, + Move, + Resize(u32), +} + +pub trait Backend: Send + Sync + 'static { + fn serialize_start_data(&self, id: &str) -> Result>; + fn serialize_toplevel(&self) -> Result>; + fn set_toplevel_capabilities(&self, capabilities: Vec); + fn configure_toplevel( + &self, + size: Option>, + states: Vec, + bounds: Option>, + ); + fn apply_surface_material(&self, surface: SurfaceID, model_part: &Arc); + + fn pointer_motion(&self, surface: &SurfaceID, position: Vector2); + fn pointer_button(&self, surface: &SurfaceID, button: u32, pressed: bool); + fn pointer_scroll( + &self, + surface: &SurfaceID, + scroll_distance: Option>, + scroll_steps: Option>, + ); + + fn keyboard_set_keymap(&self, keymap: &str) -> Result<()>; + fn keyboard_key(&self, surface: &SurfaceID, key: u32, state: bool); +} + +pub fn panel_item_from_node(node: &Node) -> Option> { + let ItemType::Panel(panel_item) = &node.item.get()?.specialization else {return None}; + Some(panel_item.clone()) +} + +pub trait PanelItemTrait: Backend + Send + Sync + 'static { + fn uid(&self) -> &str; + // fn node(&self) -> Option>; +} + +pub struct PanelItem { + pub uid: String, + node: Weak, + pub backend: Box, +} +impl PanelItem { + pub fn create(backend: Box, pid: Option) -> (Arc, Arc>) { + debug!(?pid, "Create panel item"); + + let startup_settings = pid + .and_then(|pid| get_env(pid).ok()) + .and_then(|env| startup_settings(&env)); + + let uid = nanoid!(); + let node = Node::create(&INTERNAL_CLIENT, "/item/panel/item", &uid, true) + .add_to_scenegraph() + .unwrap(); + let spatial = Spatial::add_to(&node, None, Mat4::IDENTITY, false).unwrap(); + if let Some(startup_settings) = &startup_settings { + spatial.set_local_transform( + spatial.global_transform().inverse() * startup_settings.transform, + ); + } + + let panel_item = Arc::new(PanelItem { + uid: uid.clone(), + node: Arc::downgrade(&node), + backend, + }); + + let generic_panel_item: Arc = panel_item.clone(); + let item = Item::add_to( + &node, + uid, + &ITEM_TYPE_INFO_PANEL, + ItemType::Panel(generic_panel_item), + ); + + // panel_item + // .seat_data + // .new_surface(&wl_surface, Arc::downgrade(&panel_item)); + + if let Some(startup_settings) = &startup_settings { + if let Some(acceptor) = startup_settings + .acceptors + .get(&*ITEM_TYPE_INFO_PANEL) + .and_then(|acc| acc.upgrade()) + { + items::capture(&item, &acceptor); + } + } + node.add_local_signal("apply_surface_material", Self::apply_surface_material_flex); + node.add_local_signal("configure_toplevel", Self::configure_toplevel_flex); + node.add_local_signal( + "set_toplevel_capabilities", + Self::set_toplevel_capabilities_flex, + ); + node.add_local_signal("pointer_scroll", Self::pointer_scroll_flex); + node.add_local_signal("pointer_button", Self::pointer_button_flex); + node.add_local_signal("pointer_motion", Self::pointer_motion_flex); + + node.add_local_signal( + "keyboard_set_keymap_string", + Self::keyboard_set_keymap_string_flex, + ); + // node.add_local_signal( + // "keyboard_set_keymap_names", + // Self::keyboard_set_keymap_names_flex, + // ); + node.add_local_signal("keyboard_key", Self::keyboard_key_flex); + + (node, panel_item) + } + + pub fn node(&self) -> Option> { + self.node.upgrade() + } + + fn apply_surface_material_flex( + node: &Node, + calling_client: Arc, + data: &[u8], + ) -> Result<()> { + let Some(panel_item) = panel_item_from_node(node) else { return Ok(()) }; + + #[derive(Debug, Deserialize)] + struct SurfaceMaterialInfo<'a> { + surface: SurfaceID, + model_node_path: &'a str, + } + + let info: SurfaceMaterialInfo = deserialize(data)?; + + let model_node = calling_client + .scenegraph + .get_node(info.model_node_path) + .ok_or_else(|| eyre!("Model node not found"))?; + let Some(Drawable::ModelPart(model_part)) = model_node.drawable.get() else {bail!("Node is not a model")}; + debug!(?info, "Apply surface material"); + + panel_item.apply_surface_material(info.surface, model_part); + + Ok(()) + } + + fn pointer_motion_flex(node: &Node, _calling_client: Arc, data: &[u8]) -> Result<()> { + let Some(panel_item) = panel_item_from_node(node) else { return Ok(()) }; + + let (surface_id, position): (SurfaceID, Vector2) = deserialize(data)?; + debug!(?surface_id, ?position, "Pointer deactivate"); + + panel_item.pointer_motion(&surface_id, position); + + Ok(()) + } + fn pointer_button_flex(node: &Node, _calling_client: Arc, data: &[u8]) -> Result<()> { + let Some(panel_item) = panel_item_from_node(node) else { return Ok(()) }; + + let (surface_id, button, state): (SurfaceID, u32, u32) = deserialize(data)?; + debug!(?surface_id, button, state, "Pointer button"); + + panel_item.pointer_button(&surface_id, button, state == 0); + Ok(()) + } + fn pointer_scroll_flex(node: &Node, _calling_client: Arc, data: &[u8]) -> Result<()> { + let Some(panel_item) = panel_item_from_node(node) else { return Ok(()) }; + + #[derive(Debug, Deserialize)] + struct PointerScrollInfo { + surface_id: SurfaceID, + axis_continuous: Option>, + axis_discrete: Option>, + } + let info: PointerScrollInfo = deserialize(data)?; + debug!(?info, "Pointer scroll"); + + panel_item.pointer_scroll(&info.surface_id, info.axis_continuous, info.axis_discrete); + + Ok(()) + } + + fn keyboard_set_keymap_string_flex( + node: &Node, + _calling_client: Arc, + data: &[u8], + ) -> Result<()> { + let keymap_string: &str = deserialize(data)?; + + let Some(panel_item) = panel_item_from_node(node) else { return Ok(()) }; + debug!("Keyboard set keymap"); + panel_item.keyboard_set_keymap(keymap_string) + + // PanelItem::keyboard_set_keymap_flex(node, &keymap) + } + // fn keyboard_set_keymap_names_flex( + // node: &Node, + // _calling_client: Arc, + // data: &[u8], + // ) -> Result<()> { + // #[derive(Debug, Deserialize)] + // struct Names<'a> { + // rules: &'a str, + // model: &'a str, + // layout: &'a str, + // variant: &'a str, + // options: Option, + // } + // let names: Names = deserialize(data)?; + // let context = xkb::Context::new(0); + // let keymap = Keymap::new_from_names( + // &context, + // names.rules, + // names.model, + // names.layout, + // names.variant, + // names.options, + // XKB_KEYMAP_FORMAT_TEXT_V1, + // ) + // .ok_or_else(|| eyre!("Keymap is not valid"))?; + + // PanelItem::keyboard_set_keymap_flex(node, &keymap) + // } + // fn keyboard_set_keymap_flex(node: &Node, keymap: &str) -> Result<()> { + // let Some(panel_item): Option>> = panel_item_from_node(node) else { return Ok(()) }; + // debug!("Keyboard set keymap"); + + // panel_item.seat_data.set_keymap( + // keymap, + // match &panel_item { + // Backend::Wayland(w) => w.input_surfaces(), + // #[cfg(feature = "xwayland")] + // Backend::X11(_) => panel_item + // .toplevel_wl_surface() + // .map(|s| vec![s]) + // .unwrap_or_default(), + // }, + // ); + + // Ok(()) + // } + fn keyboard_key_flex(node: &Node, _calling_client: Arc, data: &[u8]) -> Result<()> { + let Some(panel_item) = panel_item_from_node(node) else { return Ok(()) }; + let (surface_id, key, state): (SurfaceID, u32, u32) = deserialize(data)?; + debug!(key, state, "Set keyboard key state"); + + panel_item.keyboard_key(&surface_id, key, state == 0); + + Ok(()) + } + pub fn grab_keyboard(&self, sid: Option) { + let Some(node) = self.node.upgrade() else { return }; + + let _ = node.send_remote_signal("grab_keyboard", &serialize(sid).unwrap()); + } + + fn configure_toplevel_flex( + node: &Node, + _calling_client: Arc, + data: &[u8], + ) -> Result<()> { + let Some(panel_item) = panel_item_from_node(node) else { return Ok(()) }; + + #[derive(Debug, Deserialize)] + struct ConfigureToplevelInfo { + size: Option>, + states: Vec, + bounds: Option>, + } + let info: ConfigureToplevelInfo = deserialize(data)?; + + panel_item.configure_toplevel(info.size, info.states, info.bounds); + Ok(()) + } + + fn set_toplevel_capabilities_flex( + node: &Node, + _calling_client: Arc, + data: &[u8], + ) -> Result<()> { + let Some(panel_item) = panel_item_from_node(node) else { return Ok(()) }; + + let capabilities: Vec = deserialize(data)?; + debug!("Set toplevel capabilities"); + panel_item.set_toplevel_capabilities(capabilities); + + Ok(()) + } + + pub fn commit_toplevel(&self) { + debug!("Commit toplevel"); + let Some(node) = self.node.upgrade() else { return }; + let Ok(data) = self.backend.serialize_toplevel() else {return}; + let _ = node.send_remote_signal("commit_toplevel", &data); + } + + pub fn recommend_toplevel_state(&self, state: RecommendedState) { + let Some(node) = self.node.upgrade() else { return }; + let data = serialize(state).unwrap(); + debug!(?state, "Recommend toplevel state"); + + let _ = node.send_remote_signal("recommend_toplevel_state", &data); + } +} +impl PanelItemTrait for PanelItem { + fn uid(&self) -> &str { + &self.uid + } +} +impl Backend for PanelItem { + fn serialize_start_data(&self, id: &str) -> Result> { + self.backend.serialize_start_data(id) + } + + fn serialize_toplevel(&self) -> Result> { + self.backend.serialize_toplevel() + } + + fn set_toplevel_capabilities(&self, capabilities: Vec) { + self.backend.set_toplevel_capabilities(capabilities) + } + + fn configure_toplevel( + &self, + size: Option>, + states: Vec, + bounds: Option>, + ) { + self.backend.configure_toplevel(size, states, bounds) + } + + fn apply_surface_material(&self, surface: SurfaceID, model_part: &Arc) { + self.backend.apply_surface_material(surface, model_part) + } + + fn pointer_motion(&self, surface: &SurfaceID, position: Vector2) { + self.backend.pointer_motion(surface, position) + } + + fn pointer_button(&self, surface: &SurfaceID, button: u32, pressed: bool) { + self.backend.pointer_button(surface, button, pressed) + } + + fn pointer_scroll( + &self, + surface: &SurfaceID, + scroll_distance: Option>, + scroll_steps: Option>, + ) { + self.backend + .pointer_scroll(surface, scroll_distance, scroll_steps) + } + + fn keyboard_set_keymap(&self, keymap: &str) -> Result<()> { + self.backend.keyboard_set_keymap(keymap) + } + + fn keyboard_key(&self, surface: &SurfaceID, key: u32, state: bool) { + self.backend.keyboard_key(surface, key, state) + } +} +impl Drop for PanelItem { + fn drop(&mut self) { + // Dropped panel item, basically just a debug breakpoint place + } +} diff --git a/src/wayland/mod.rs b/src/wayland/mod.rs index 17c91b2..99d3680 100644 --- a/src/wayland/mod.rs +++ b/src/wayland/mod.rs @@ -1,7 +1,6 @@ mod compositor; mod data_device; mod decoration; -pub mod panel_item; mod seat; mod shaders; mod state; @@ -37,7 +36,6 @@ use tokio::{ use tracing::{debug, debug_span, info, instrument}; pub static WAYLAND_DISPLAY: OnceCell = OnceCell::new(); - pub static SERIAL_COUNTER: CounterU32 = CounterU32::new(0); struct EGLRawHandles { @@ -90,7 +88,7 @@ impl Wayland { let (dmabuf_tx, dmabuf_rx) = mpsc::unbounded_channel(); let display = Arc::new(Mutex::new(display)); #[cfg(feature = "xwayland")] - let xwayland_state = xwayland::XWaylandState::create(display.clone(), &display_handle).unwrap(); + let xwayland_state = xwayland::XWaylandState::create(&display_handle).unwrap(); let wayland_state = WaylandState::new(display.clone(), display_handle, &renderer, dmabuf_tx); diff --git a/src/wayland/panel_item.rs b/src/wayland/panel_item.rs deleted file mode 100644 index b9fb4c2..0000000 --- a/src/wayland/panel_item.rs +++ /dev/null @@ -1,805 +0,0 @@ -use super::{ - seat::{Cursor, SeatData}, - surface::CoreSurface, - xdg_shell::{PopupData, ToplevelData, XdgSurfaceData}, - SERIAL_COUNTER, -}; -use crate::{ - core::{ - client::{get_env, startup_settings, Client, INTERNAL_CLIENT}, - registry::Registry, - }, - nodes::{ - drawable::Drawable, - items::{self, Item, ItemSpecialization, ItemType, TypeInfo}, - spatial::Spatial, - Node, - }, - wayland::seat::{KeyboardEvent, PointerEvent}, -}; -use color_eyre::eyre::{bail, eyre, Result}; -use glam::Mat4; -use lazy_static::lazy_static; -use mint::Vector2; -use nanoid::nanoid; -use parking_lot::Mutex; -use rustc_hash::FxHashMap; -use serde::{ - de::{Deserializer, Error, SeqAccess, Visitor}, - ser::Serializer, - Deserialize, Serialize, -}; -use smithay::{ - reexports::{ - wayland_protocols::xdg::shell::server::{ - xdg_popup::XdgPopup, - xdg_surface::XdgSurface, - xdg_toplevel::{XdgToplevel, EVT_CONFIGURE_BOUNDS_SINCE, EVT_WM_CAPABILITIES_SINCE}, - }, - wayland_server::{ - backend::Credentials, protocol::wl_surface::WlSurface, Resource, Weak as WlWeak, - }, - }, - wayland::compositor, -}; -#[cfg(feature = "xwayland")] -use smithay::{ - utils::Rectangle, - xwayland::{xwm::X11SurfaceError, X11Surface}, -}; -use stardust_xr::schemas::flex::{deserialize, serialize}; -use std::sync::{Arc, Weak}; -use tracing::debug; -use xkbcommon::xkb::{self, ffi::XKB_KEYMAP_FORMAT_TEXT_V1, Keymap}; - -lazy_static! { - pub static ref ITEM_TYPE_INFO_PANEL: TypeInfo = TypeInfo { - type_name: "panel", - aliased_local_signals: vec![ - "apply_surface_material", - "configure_toplevel", - "set_toplevel_capabilities", - "pointer_scroll", - "pointer_button", - "pointer_motion", - "keyboard_key", - "keyboard_set_keymap_names", - "keyboard_set_keymap_string", - "close", - ], - aliased_local_methods: vec![], - aliased_remote_signals: vec![ - "commit_toplevel", - "recommend_toplevel_state", - "set_cursor", - "new_popup", - "reposition_popup", - "drop_popup", - ], - ui: Default::default(), - items: Registry::new(), - acceptors: Registry::new(), - }; -} - -/// An ID for a surface inside this panel item -#[derive(Debug, Clone)] -#[allow(dead_code)] -pub enum SurfaceID { - Cursor, - Toplevel, - Popup(String), -} -impl Default for SurfaceID { - fn default() -> Self { - Self::Toplevel - } -} - -impl<'de> serde::Deserialize<'de> for SurfaceID { - fn deserialize>(deserializer: D) -> Result { - deserializer.deserialize_seq(SurfaceIDVisitor) - } -} - -struct SurfaceIDVisitor; - -impl<'de> Visitor<'de> for SurfaceIDVisitor { - type Value = SurfaceID; - - fn expecting(&self, f: &mut std::fmt::Formatter) -> std::fmt::Result { - f.write_str("idk") - } - - fn visit_seq>(self, mut seq: A) -> Result { - let Some(discrim) = seq.next_element()? else { - return Err(A::Error::missing_field("discrim")); - }; - - // idk if you wanna check for extraneous elements - // I didn't bother - - match discrim { - "Cursor" => Ok(SurfaceID::Cursor), - "Toplevel" => Ok(SurfaceID::Toplevel), - "Popup" => { - let Some(text) = seq.next_element()? else { - return Err(A::Error::missing_field("popup_text")); - }; - Ok(SurfaceID::Popup(text)) - } - _ => Err(A::Error::unknown_variant( - discrim, - &["Cursor", "Toplevel", "Popup"], - )), - } - } -} - -impl serde::Serialize for SurfaceID { - fn serialize(&self, serializer: S) -> Result { - match self { - Self::Cursor => ["Cursor"].serialize(serializer), - Self::Toplevel => ["Toplevel"].serialize(serializer), - Self::Popup(text) => ["Popup", text].serialize(serializer), - } - } -} - -#[derive(Debug, Clone, Copy, Serialize)] -#[serde(tag = "type", content = "content")] -pub enum RecommendedState { - Maximize(bool), - Fullscreen(bool), - Minimize, - Move, - Resize(u32), -} - -#[derive(Debug)] -pub struct WaylandBackend { - toplevel: WlWeak, - toplevel_wl_surface: WlWeak, - popups: Mutex>>, - cursor: Mutex>>, -} -impl WaylandBackend { - pub fn create(toplevel: XdgToplevel) -> Option { - let toplevel_wl_surface = - XdgSurfaceData::get(&ToplevelData::get(&toplevel).lock().xdg_surface()?)? - .lock() - .wl_surface()? - .downgrade(); - Some(WaylandBackend { - toplevel: toplevel.downgrade(), - toplevel_wl_surface, - popups: Mutex::new(FxHashMap::default()), - cursor: Mutex::new(None), - }) - } - 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 wl_surface_from_id(&self, id: &SurfaceID) -> Option { - match id { - SurfaceID::Cursor => self.cursor.lock().clone()?.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 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 - } - - 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()); - } - - 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) - } - - fn set_toplevel_capabilities(&self, capabilities: Vec) { - let Ok(xdg_toplevel) = self.toplevel.upgrade() else {return}; - xdg_toplevel.wm_capabilities(capabilities); - let Some(xdg_surface) = self.toplevel_xdg_surface() else {return}; - xdg_surface.configure(SERIAL_COUNTER.inc()); - } - - 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.upgrade() else { return }; - let _ = node.send_remote_signal("new_popup", &serialize(&(&uid, data)).unwrap()); - } - pub fn reposition_popup(&self, panel_item: &PanelItem, popup_state: &PopupData) { - let Some(node) = panel_item.node.upgrade() 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}; - panel_item.seat_data.drop_surface(&wl_surface); - } - - let Some(node) = panel_item.node.upgrade() else { return }; - let _ = node.send_remote_signal("drop_popup", &serialize(uid).unwrap()); - } - - fn popups_data(&self) -> Vec { - self.popups - .lock() - .values() - .filter_map(|v| Some(v.upgrade().ok()?.data::>()?.lock().clone())) - .collect::>() - } - fn cursor_data(&self) -> Option<(Vector2, Vector2)> { - let cursor = self.cursor.lock().as_ref().and_then(|c| c.upgrade().ok()); - let cursor_size = cursor - .as_ref() - .and_then(|c| CoreSurface::from_wl_surface(&c)) - .and_then(|c| c.size()); - let cursor_hotspot = cursor - .and_then(|c| { - compositor::with_states(&c, |data| data.data_map.get::>().cloned()) - }) - .map(|cursor| cursor.hotspot); - - cursor_size.zip(cursor_hotspot) - } - - pub fn set_cursor( - &self, - panel_item: &PanelItem, - surface: Option<&WlSurface>, - hotspot_x: i32, - hotspot_y: i32, - ) { - let Some(node) = panel_item.node.upgrade() else { return }; - debug!(?surface, hotspot_x, hotspot_y, "Set cursor size"); - let mut data = serialize(()).unwrap(); - - let cursor_size = surface - .and_then(|c| CoreSurface::from_wl_surface(c)) - .and_then(|c| c.size()); - - if let Some(size) = cursor_size { - data = serialize((size, (hotspot_x, hotspot_y))).unwrap(); - } - - let _ = node.send_remote_signal("set_cursor", &data); - *self.cursor.lock() = surface.map(|surf| surf.downgrade()); - } -} - -#[cfg(feature = "xwayland")] -#[derive(Debug)] -pub struct X11Backend { - pub toplevel_parent: Option, - pub toplevel: X11Surface, -} -#[cfg(feature = "xwayland")] -impl X11Backend { - fn configure_toplevel( - &self, - size: Option>, - states: Vec, - ) -> Result<(), X11SurfaceError> { - self.toplevel.configure( - size.map(|s| Rectangle::from_loc_and_size((0, 0), (s.x as i32, s.y as i32))), - )?; - self.toplevel.set_maximized(states.contains(&1))?; - Ok(()) - } - - fn serialize_toplevel(&self) -> Result> { - let toplevel_state = ( - None::, - self.toplevel.title(), - None::, - ( - self.toplevel.geometry().size.w, - self.toplevel.geometry().size.h, - ), - self.toplevel.min_size().map(|s| (s.w, s.h)), - self.toplevel.max_size().map(|s| (s.w, s.w)), - ); - let data = serialize(&toplevel_state)?; - Ok(data) - } - fn wl_surface_from_id(&self, id: &SurfaceID) -> Option { - match id { - SurfaceID::Cursor => None, - SurfaceID::Toplevel => self.toplevel.wl_surface(), - SurfaceID::Popup(_) => None, - } - } -} - -// TODO: abstract this away into a trait so other platforms e.g. arcan/android/windows/xrdesktop can also be 2D backends -#[derive(Debug)] -pub enum Backend { - Wayland(WaylandBackend), - #[cfg(feature = "xwayland")] - X11(X11Backend), -} - -pub struct PanelItem { - pub uid: String, - node: Weak, - pub seat_data: Arc, - pub backend: Backend, - pointer_grab: Mutex>, - keyboard_grab: Mutex>, -} -impl PanelItem { - pub fn create( - wl_surface: WlSurface, - backend: Backend, - client_credentials: Option, - seat_data: Arc, - ) -> (Arc, Arc) { - debug!(?backend, ?client_credentials, "Create panel item"); - - let startup_settings = client_credentials - .and_then(|cred| get_env(cred.pid).ok()) - .and_then(|env| startup_settings(&env)); - - let uid = nanoid!(); - let node = Node::create(&INTERNAL_CLIENT, "/item/panel/item", &uid, true) - .add_to_scenegraph() - .unwrap(); - let spatial = Spatial::add_to(&node, None, Mat4::IDENTITY, false).unwrap(); - let panel_item = Arc::new(PanelItem { - uid: uid.clone(), - node: Arc::downgrade(&node), - seat_data, - backend, - pointer_grab: Mutex::new(None), - keyboard_grab: Mutex::new(None), - }); - - if let Some(startup_settings) = &startup_settings { - spatial.set_local_transform( - spatial.global_transform().inverse() * startup_settings.transform, - ); - } - - panel_item - .seat_data - .new_surface(&wl_surface, Arc::downgrade(&panel_item)); - - let item = Item::add_to( - &node, - uid, - &ITEM_TYPE_INFO_PANEL, - ItemType::Panel(panel_item.clone()), - ); - - if let Some(startup_settings) = &startup_settings { - if let Some(acceptor) = startup_settings - .acceptors - .get(&*ITEM_TYPE_INFO_PANEL) - .and_then(|acc| acc.upgrade()) - { - items::capture(&item, &acceptor); - } - } - node.add_local_signal( - "apply_surface_material", - PanelItem::apply_surface_material_flex, - ); - node.add_local_signal("configure_toplevel", PanelItem::configure_toplevel_flex); - node.add_local_signal( - "set_toplevel_capabilities", - PanelItem::set_toplevel_capabilities_flex, - ); - node.add_local_signal("pointer_scroll", PanelItem::pointer_scroll_flex); - node.add_local_signal("pointer_button", PanelItem::pointer_button_flex); - node.add_local_signal("pointer_motion", PanelItem::pointer_motion_flex); - - node.add_local_signal( - "keyboard_set_keymap_string", - PanelItem::keyboard_set_keymap_string_flex, - ); - node.add_local_signal( - "keyboard_set_keymap_names", - PanelItem::keyboard_set_keymap_names_flex, - ); - node.add_local_signal("keyboard_key", PanelItem::keyboard_key_flex); - - (node, panel_item) - } - - pub fn from_node(node: &Node) -> Option> { - let ItemType::Panel(panel_item) = &node.item.get()?.specialization else {return None}; - Some(panel_item.clone()) - } - - fn toplevel_wl_surface(&self) -> Option { - match &self.backend { - Backend::Wayland(w) => w.toplevel_wl_surface(), - #[cfg(feature = "xwayland")] - Backend::X11(x) => x.toplevel.wl_surface(), - } - } - fn core_surface(&self) -> Option> { - compositor::with_states(&self.toplevel_wl_surface()?, |data| { - data.data_map.get::>().cloned() - }) - } - fn flush_clients(&self) { - if let Some(core_surface) = self.core_surface() { - core_surface.flush_clients(); - } - } - fn wl_surface_from_id(&self, id: &SurfaceID) -> Option { - match &self.backend { - Backend::Wayland(w) => w.wl_surface_from_id(id), - #[cfg(feature = "xwayland")] - Backend::X11(x) => x.wl_surface_from_id(id), - } - } - fn wl_surface_from_id_result(&self, id: &SurfaceID) -> Result { - self.wl_surface_from_id(id) - .ok_or(eyre!("Surface with ID not found")) - } - - fn apply_surface_material_flex( - node: &Node, - calling_client: Arc, - data: &[u8], - ) -> Result<()> { - let Some(panel_item) = PanelItem::from_node(node) else { return Ok(()) }; - - #[derive(Debug, Deserialize)] - struct SurfaceMaterialInfo<'a> { - surface: SurfaceID, - model_node_path: &'a str, - } - - let info: SurfaceMaterialInfo = deserialize(data)?; - - let Some(wl_surface) = panel_item.wl_surface_from_id(&info.surface) else { return Ok(()) }; - let Some(core_surface) = CoreSurface::from_wl_surface(&wl_surface) else { return Ok(()) }; - - let model_node = calling_client - .scenegraph - .get_node(info.model_node_path) - .ok_or_else(|| eyre!("Model node not found"))?; - let Some(Drawable::ModelPart(model_node)) = model_node.drawable.get() else {bail!("Node is not a model")}; - debug!(?info, "Apply surface material"); - - core_surface.apply_material(model_node); - - Ok(()) - } - - fn pointer_motion_flex(node: &Node, _calling_client: Arc, data: &[u8]) -> Result<()> { - let Some(panel_item) = PanelItem::from_node(node) else { return Ok(()) }; - - let (surface_id, position): (SurfaceID, Vector2) = deserialize(data)?; - let wl_surface = panel_item.wl_surface_from_id_result(&surface_id)?; - debug!(?surface_id, ?position, "Pointer deactivate"); - - panel_item - .seat_data - .pointer_event(&wl_surface, PointerEvent::Motion(position)); - panel_item.flush_clients(); - - Ok(()) - } - fn pointer_button_flex(node: &Node, _calling_client: Arc, data: &[u8]) -> Result<()> { - let Some(panel_item) = PanelItem::from_node(node) else { return Ok(()) }; - - let (surface_id, button, state): (SurfaceID, u32, u32) = deserialize(data)?; - let wl_surface = panel_item.wl_surface_from_id_result(&surface_id)?; - debug!(?surface_id, button, state, "Pointer button"); - - panel_item - .seat_data - .pointer_event(&wl_surface, PointerEvent::Button { button, state }); - panel_item.flush_clients(); - - Ok(()) - } - fn pointer_scroll_flex(node: &Node, _calling_client: Arc, data: &[u8]) -> Result<()> { - let Some(panel_item) = PanelItem::from_node(node) else { return Ok(()) }; - - #[derive(Debug, Deserialize)] - struct PointerScrollInfo { - surface_id: SurfaceID, - axis_continuous: Option>, - axis_discrete: Option>, - } - let info: PointerScrollInfo = deserialize(data)?; - let wl_surface = panel_item.wl_surface_from_id_result(&info.surface_id)?; - - debug!(?info, "Pointer scroll"); - - panel_item.seat_data.pointer_event( - &wl_surface, - PointerEvent::Scroll { - axis_continuous: info.axis_continuous, - axis_discrete: info.axis_discrete, - }, - ); - panel_item.flush_clients(); - - Ok(()) - } - - fn keyboard_set_keymap_string_flex( - node: &Node, - _calling_client: Arc, - data: &[u8], - ) -> Result<()> { - let context = xkb::Context::new(0); - let keymap = - Keymap::new_from_string(&context, deserialize(data)?, XKB_KEYMAP_FORMAT_TEXT_V1, 0) - .ok_or_else(|| eyre!("Keymap is not valid"))?; - - PanelItem::keyboard_set_keymap_flex(node, &keymap) - } - fn keyboard_set_keymap_names_flex( - node: &Node, - _calling_client: Arc, - data: &[u8], - ) -> Result<()> { - #[derive(Debug, Deserialize)] - struct Names<'a> { - rules: &'a str, - model: &'a str, - layout: &'a str, - variant: &'a str, - options: Option, - } - let names: Names = deserialize(data)?; - let context = xkb::Context::new(0); - let keymap = Keymap::new_from_names( - &context, - names.rules, - names.model, - names.layout, - names.variant, - names.options, - XKB_KEYMAP_FORMAT_TEXT_V1, - ) - .ok_or_else(|| eyre!("Keymap is not valid"))?; - - PanelItem::keyboard_set_keymap_flex(node, &keymap) - } - fn keyboard_set_keymap_flex(node: &Node, keymap: &Keymap) -> Result<()> { - let Some(panel_item) = PanelItem::from_node(node) else { return Ok(()) }; - debug!("Keyboard set keymap"); - - panel_item.seat_data.set_keymap( - keymap, - match &panel_item.backend { - Backend::Wayland(w) => w.input_surfaces(), - #[cfg(feature = "xwayland")] - Backend::X11(_) => panel_item - .toplevel_wl_surface() - .map(|s| vec![s]) - .unwrap_or_default(), - }, - ); - - Ok(()) - } - - fn keyboard_key_flex(node: &Node, _calling_client: Arc, data: &[u8]) -> Result<()> { - let Some(panel_item) = PanelItem::from_node(node) else { return Ok(()) }; - let (surface_id, key, state): (SurfaceID, u32, u32) = deserialize(data)?; - let wl_surface = panel_item.wl_surface_from_id_result(&surface_id)?; - debug!(key, state, "Set keyboard key state"); - - panel_item - .seat_data - .keyboard_event(&wl_surface, KeyboardEvent::Key { key, state }); - - Ok(()) - } - - fn configure_toplevel_flex( - node: &Node, - _calling_client: Arc, - data: &[u8], - ) -> Result<()> { - let Some(panel_item) = PanelItem::from_node(node) else { return Ok(()) }; - - #[derive(Debug, Deserialize)] - struct ConfigureToplevelInfo { - size: Option>, - states: Vec, - bounds: Option>, - } - let info: ConfigureToplevelInfo = deserialize(data)?; - - match &panel_item.backend { - Backend::Wayland(w) => w.configure_toplevel(info.size, info.states, info.bounds), - #[cfg(feature = "xwayland")] - Backend::X11(x) => x.configure_toplevel(info.size, info.states)?, - } - Ok(()) - } - - fn set_toplevel_capabilities_flex( - node: &Node, - _calling_client: Arc, - data: &[u8], - ) -> Result<()> { - let Some(panel_item) = PanelItem::from_node(node) else { return Ok(()) }; - let Some(core_surface) = panel_item.core_surface() else { return Ok(()) }; - if let Backend::Wayland(w) = &panel_item.backend { - let Some(toplevel) = w.toplevel() else {return Ok(())}; - if toplevel.version() < EVT_WM_CAPABILITIES_SINCE { - return Ok(()); - } - } - - let capabilities: Vec = deserialize(data)?; - debug!("Set toplevel capabilities"); - match &panel_item.backend { - Backend::Wayland(w) => w.set_toplevel_capabilities(capabilities), - _ => (), - } - core_surface.flush_clients(); - - Ok(()) - } - - pub fn commit_toplevel(&self) { - debug!("Commit toplevel"); - let Some(node) = self.node.upgrade() else { return }; - let Ok(data) = (match &self.backend { - Backend::Wayland(w) => w.serialize_toplevel(), - #[cfg(feature = "xwayland")] - Backend::X11(x) => x.serialize_toplevel(), - }) else {return}; - let _ = node.send_remote_signal("commit_toplevel", &data); - } - - pub fn recommend_toplevel_state(&self, state: RecommendedState) { - let Some(node) = self.node.upgrade() else { return }; - let data = serialize(state).unwrap(); - debug!(?state, "Recommend toplevel state"); - - let _ = node.send_remote_signal("recommend_toplevel_state", &data); - } - - pub fn grab_keyboard(&self, sid: Option) { - let Some(node) = self.node.upgrade() else { return }; - - let _ = node.send_remote_signal("grab_keyboard", &serialize(sid).unwrap()); - } - - pub fn on_drop(&self) { - let Some(toplevel) = self.toplevel_wl_surface() else {return}; - self.seat_data.drop_surface(&toplevel); - - debug!("Dropped panel item gracefully"); - } -} -impl ItemSpecialization for PanelItem { - fn serialize_start_data(&self, id: &str) -> Option> { - match &self.backend { - Backend::Wayland(w) => { - let toplevel = w.toplevel()?; - let toplevel_state = ToplevelData::get(&toplevel); - let toplevel_state = toplevel_state.lock().clone(); - - let pointer_grab = self.pointer_grab.lock().clone(); - let keyboard_grab = self.keyboard_grab.lock().clone(); - - serialize(( - id, - ( - w.cursor_data(), - toplevel_state, - w.popups_data(), - pointer_grab, - keyboard_grab, - ), - )) - .ok() - } - #[cfg(feature = "xwayland")] - Backend::X11(x) => { - let size = ( - x.toplevel.geometry().size.w as u32, - x.toplevel.geometry().size.h as u32, - ); - let toplevel_state = ( - None::, - x.toplevel.title(), - None::, - ( - x.toplevel.geometry().size.w as u32, - x.toplevel.geometry().size.h as u32, - ), - x.toplevel.min_size().map(|s| (s.w as u32, s.h as u32)), - x.toplevel.max_size().map(|s| (s.w as u32, s.w as u32)), - ((0_i32, 0_i32), size), - vec![0_u32; 0], - ); - let info = ( - None::<(Vector2, Vector2)>, - toplevel_state, - Vec::::new(), - None::, - None::, - ); - serialize((id, info)).ok() - } - } - } -} -impl Drop for PanelItem { - fn drop(&mut self) { - // Dropped panel item, basically just a debug breakpoint place - } -} diff --git a/src/wayland/seat.rs b/src/wayland/seat.rs index cec301a..5a5886e 100644 --- a/src/wayland/seat.rs +++ b/src/wayland/seat.rs @@ -1,11 +1,10 @@ use super::{ - panel_item::{Backend, PanelItem}, - state::WaylandState, + state::{ClientState, WaylandState}, surface::CoreSurface, GLOBAL_DESTROY_QUEUE, SERIAL_COUNTER, }; use crate::core::task; -use color_eyre::eyre::Result; +use color_eyre::eyre::{eyre, Result}; use mint::Vector2; use nanoid::nanoid; use once_cell::sync::OnceCell; @@ -29,11 +28,12 @@ use smithay::{ }; use std::{ collections::VecDeque, - sync::{Arc, Weak}, + sync::Arc, time::{Duration, Instant}, }; +use tokio::sync::watch; use tracing::{debug, warn}; -use xkbcommon::xkb::{self, Keymap}; +use xkbcommon::xkb::{self, ffi::XKB_KEYMAP_FORMAT_TEXT_V1, Keymap}; pub struct KeyboardInfo { keymap: KeymapFile, @@ -89,7 +89,7 @@ unsafe impl Send for KeyboardInfo {} #[derive(Debug, Clone, Copy)] pub enum PointerEvent { - Motion(Vector2), + Motion(Vector2), Button { button: u32, state: u32, @@ -108,23 +108,30 @@ pub enum KeyboardEvent { const POINTER_EVENT_TIMEOUT: Duration = Duration::from_secs(1); struct SurfaceInfo { wl_surface: WlWeak, - panel_item: Weak, + cursor_sender: watch::Sender>, pointer_queue: VecDeque, pointer_latest_event: Instant, keyboard_queue: VecDeque, keyboard_info: Option, } impl SurfaceInfo { - fn new(wl_surface: &WlSurface, panel_item: Weak) -> Self { + fn new(wl_surface: &WlSurface, cursor_sender: watch::Sender>) -> Self { SurfaceInfo { wl_surface: wl_surface.downgrade(), - panel_item, + cursor_sender, pointer_queue: VecDeque::new(), pointer_latest_event: Instant::now(), keyboard_queue: VecDeque::new(), keyboard_info: None, } } + fn flush(&self) { + if let Some(client) = self.wl_surface.upgrade().ok().and_then(|s| s.client()) { + if let Some(client_data) = client.get_data::() { + client_data.flush(); + } + } + } fn handle_pointer_events(&mut self, pointer: &WlPointer, mut locked: bool) -> bool { let Ok(focus) = self.wl_surface.upgrade() else { return false; }; let Some(core_surface) = CoreSurface::from_wl_surface(&focus) else { return false; }; @@ -139,16 +146,16 @@ impl SurfaceInfo { pointer.enter( SERIAL_COUNTER.inc(), &focus, - pos.x.clamp(0.0, focus_size.x as f64), - pos.y.clamp(0.0, focus_size.y as f64), + (pos.x as f64).clamp(0.0, focus_size.x as f64), + (pos.y as f64).clamp(0.0, focus_size.y as f64), ); locked = true; } (true, PointerEvent::Motion(pos)) => { pointer.motion( 0, - pos.x.clamp(0.0, focus_size.x as f64), - pos.y.clamp(0.0, focus_size.y as f64), + (pos.x as f64).clamp(0.0, focus_size.x as f64), + (pos.y as f64).clamp(0.0, focus_size.y as f64), ); if pointer.version() >= wl_pointer::EVT_FRAME_SINCE { pointer.frame(); @@ -206,6 +213,7 @@ impl SurfaceInfo { pointer.leave(SERIAL_COUNTER.inc(), &focus); locked = false; } + self.flush(); locked } @@ -239,6 +247,7 @@ impl SurfaceInfo { } } } + self.flush(); locked } } @@ -270,6 +279,14 @@ impl SeatData { seat_data } + pub fn set_keymap_str(&self, keymap: &str, surfaces: Vec) -> 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.set_keymap(&keymap, surfaces); + Ok(()) + } pub fn set_keymap(&self, keymap: &Keymap, surfaces: Vec) { let mut panels = self.surfaces.lock(); let Some((_, focus)) = self.keyboard.get() else {return}; @@ -358,10 +375,13 @@ impl SeatData { } } - pub fn new_surface(&self, surface: &WlSurface, panel_item: Weak) { + pub fn new_surface(&self, surface: &WlSurface) -> watch::Receiver> { + let (tx, rx) = watch::channel(None); self.surfaces .lock() - .insert(surface.id(), SurfaceInfo::new(surface, panel_item)); + .insert(surface.id(), SurfaceInfo::new(surface, tx)); + + rx } pub fn drop_surface(&self, surface: &WlSurface) { self.surfaces.lock().remove(&surface.id()); @@ -388,6 +408,18 @@ impl Drop for SeatData { } } +pub struct CursorInfo { + pub surface: WlWeak, + pub hotspot_x: i32, + pub hotspot_y: i32, +} +impl CursorInfo { + pub fn cursor_data(&self) -> Option<(Vector2, Vector2)> { + let cursor_size = CoreSurface::from_wl_surface(&self.surface.upgrade().ok()?)?.size()?; + Some((cursor_size, [self.hotspot_x, self.hotspot_y].into())) + } +} + impl GlobalDispatch, WaylandState> for WaylandState { fn bind( _state: &mut WaylandState, @@ -442,12 +474,9 @@ impl Dispatch, WaylandState> for WaylandState { } } -pub struct Cursor { - pub hotspot: Vector2, -} impl Dispatch, WaylandState> for WaylandState { fn request( - state: &mut WaylandState, + _state: &mut WaylandState, _client: &Client, _resource: &WlPointer, request: wl_pointer::Request, @@ -463,16 +492,8 @@ impl Dispatch, WaylandState> for WaylandState { hotspot_y, } => { if let Some(surface) = surface.as_ref() { - CoreSurface::add_to(&state.display, dh.clone(), surface, || (), |_| ()); + CoreSurface::add_to(dh.clone(), surface, || (), |_| ()); compositor::with_states(surface, |data| { - data.data_map.insert_if_missing_threadsafe(|| { - Arc::new(Mutex::new(Cursor { - hotspot: Vector2::from([hotspot_x, hotspot_y]), - })) - }); - let mut cursor = data.data_map.get::>>().unwrap().lock(); - cursor.hotspot = Vector2::from([hotspot_x, hotspot_y]); - if let Some(core_surface) = data.data_map.get::>() { core_surface.set_material_offset(1); } @@ -483,10 +504,12 @@ impl Dispatch, WaylandState> for WaylandState { let focus = focus.lock(); let surfaces = seat_data.surfaces.lock(); let Some(surface_info) = surfaces.get(&focus) else {return}; - let Some(panel_item) = surface_info.panel_item.upgrade() else {return}; - if let Backend::Wayland(w) = &panel_item.backend { - w.set_cursor(&panel_item, surface.as_ref(), hotspot_x, hotspot_y); - } + let cursor_info = surface.map(|surface| CursorInfo { + surface: surface.downgrade(), + hotspot_x, + hotspot_y, + }); + let _ = surface_info.cursor_sender.send_replace(cursor_info); } wl_pointer::Request::Release => (), _ => unreachable!(), diff --git a/src/wayland/state.rs b/src/wayland/state.rs index fa0c996..b29cca4 100644 --- a/src/wayland/state.rs +++ b/src/wayland/state.rs @@ -40,6 +40,13 @@ use tracing::{info, warn}; #[derive(Default)] pub struct ClientState { pub compositor_state: CompositorClientState, + pub display: Weak>>, +} +impl ClientState { + pub fn flush(&self) { + let Some(display) = self.display.upgrade() else {return}; + let _ = display.lock().flush_clients(); + } } impl ClientData for ClientState { fn initialized(&self, client_id: ClientId) { diff --git a/src/wayland/surface.rs b/src/wayland/surface.rs index 4f7388f..e568e3b 100644 --- a/src/wayland/surface.rs +++ b/src/wayland/surface.rs @@ -15,16 +15,10 @@ use smithay::{ }, desktop::utils::send_frames_surface_tree, output::Output, - reexports::wayland_server::{ - self, protocol::wl_surface::WlSurface, Display, DisplayHandle, Resource, - }, + reexports::wayland_server::{self, protocol::wl_surface::WlSurface, DisplayHandle, Resource}, wayland::compositor::{self, SurfaceData}, }; -use std::{ - ffi::c_void, - sync::{Arc, Weak}, - time::Duration, -}; +use std::{ffi::c_void, sync::Arc, time::Duration}; use stereokit::{ Material, StereoKitDraw, Tex, TextureAddress, TextureFormat, TextureSample, TextureType, Transparency, @@ -43,7 +37,6 @@ impl Drop for CoreSurfaceData { } pub struct CoreSurface { - display: Weak>>, pub dh: DisplayHandle, pub weak_surface: wayland_server::Weak, mapped_data: Mutex>, @@ -57,7 +50,6 @@ pub struct CoreSurface { impl CoreSurface { pub fn add_to( - display: &Arc>>, dh: DisplayHandle, surface: &WlSurface, on_mapped: impl Fn() + Send + Sync + 'static, @@ -66,7 +58,6 @@ impl CoreSurface { compositor::with_states(surface, |data| { data.data_map.insert_if_missing_threadsafe(|| { CORE_SURFACES.add(CoreSurface { - display: Arc::downgrade(display), dh, weak_surface: surface.downgrade(), mapped_data: Mutex::new(None), @@ -190,8 +181,8 @@ impl CoreSurface { *self.material_offset.lock().value_mut() = material_offset; } - pub fn apply_material(&self, model_node: &Arc) { - self.pending_material_applications.add_raw(model_node) + pub fn apply_material(&self, model_part: &Arc) { + self.pending_material_applications.add_raw(model_part) } fn apply_surface_materials(&self) { @@ -216,15 +207,6 @@ impl CoreSurface { pub fn size(&self) -> Option> { self.mapped_data.lock().as_ref().map(|d| d.size) } - - pub fn flush_clients(&self) { - self.display - .upgrade() - .unwrap() - .lock() - .flush_clients() - .unwrap(); - } } impl Drop for CoreSurface { fn drop(&mut self) { diff --git a/src/wayland/xdg_shell.rs b/src/wayland/xdg_shell.rs index a9a20de..6faab23 100644 --- a/src/wayland/xdg_shell.rs +++ b/src/wayland/xdg_shell.rs @@ -1,24 +1,26 @@ -use crate::{ - nodes::Node, - wayland::panel_item::{Backend, WaylandBackend}, -}; - use super::{ - panel_item::{PanelItem, RecommendedState, SurfaceID}, - state::WaylandState, + seat::{CursorInfo, KeyboardEvent, PointerEvent, SeatData}, + state::{ClientState, WaylandState}, surface::CoreSurface, SERIAL_COUNTER, }; +use crate::nodes::{ + drawable::model::ModelPart, + items::panel::{Backend, PanelItem, RecommendedState, SurfaceID}, + 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_WM_CAPABILITIES_SINCE}, + xdg_toplevel::{self, XdgToplevel, EVT_CONFIGURE_BOUNDS_SINCE, EVT_WM_CAPABILITIES_SINCE}, xdg_wm_base::{self, XdgWmBase}, }, wayland_server::{ @@ -28,11 +30,14 @@ use smithay::reexports::{ 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( @@ -208,7 +213,7 @@ impl Default for Geometry { pub struct XdgSurfaceData { wl_surface: WlWeak, surface_id: SurfaceID, - panel_item: Weak, + panel_item: Weak>, geometry: Option, } impl XdgSurfaceData { @@ -226,7 +231,7 @@ impl XdgSurfaceData { pub fn wl_surface(&self) -> Option { self.wl_surface.upgrade().ok() } - pub fn panel_item(&self) -> Option> { + pub fn panel_item(&self) -> Option>> { self.panel_item.upgrade() } } @@ -276,7 +281,6 @@ impl Dispatch, WaylandState> for WaylandState let seat_data = state.seats.get(&client.id()).unwrap().clone(); let Some(wl_surface) = xdg_surface_data.lock().wl_surface() else {return}; CoreSurface::add_to( - &state.display, state.display_handle.clone(), &wl_surface, { @@ -286,15 +290,12 @@ impl Dispatch, WaylandState> for WaylandState 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}; - let Some(wl_surface) = xdg_surface_data.lock().wl_surface() else {return}; xdg_surface_data.lock().surface_id = SurfaceID::Toplevel; - let Some(backend) = WaylandBackend::create(toplevel.clone()) else {return}; + let Some(backend) = XDGBackend::create(toplevel.clone(), seat_data.clone()) else {return}; let (node, panel_item) = PanelItem::create( - wl_surface.clone(), - Backend::Wayland(backend), - client_credentials, - seat_data.clone(), + 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); @@ -355,16 +356,11 @@ impl Dispatch, WaylandState> for WaylandState 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); - let Some(wl_surface) = xdg_surface_data.lock().wl_surface() else {return}; - panel_item - .seat_data - .new_surface(&wl_surface, 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, state.display_handle.clone(), &xdg_surface_data.lock().wl_surface.upgrade().unwrap(), move || { @@ -372,8 +368,9 @@ impl Dispatch, WaylandState> for WaylandState let Some(popup_data) = PopupData::get(&xdg_popup) else {return}; let popup_data = popup_data.lock(); // panel_item.commit_popup(popup_data); - let Backend::Wayland(wayland_backend) = &panel_item.backend else {return}; - wayland_backend.new_popup(&panel_item, &xdg_popup, &*popup_data); + panel_item + .backend + .new_popup(&panel_item, &xdg_popup, &*popup_data); }, move |commit_count| { if commit_count == 0 { @@ -448,7 +445,7 @@ impl ToplevelData { pub fn xdg_surface(&self) -> Option { self.xdg_surface.upgrade().ok() } - fn panel_item(&self) -> Option> { + 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() @@ -576,7 +573,7 @@ impl Dispatch, WaylandState> for WaylandState { xdg_toplevel::Request::Destroy => { debug!(?xdg_toplevel, "Destroy XDG Toplevel"); let Some(panel_item) = data.lock().panel_item() else { return }; - panel_item.on_drop(); + panel_item.backend.on_drop(); } _ => unreachable!(), } @@ -613,7 +610,7 @@ impl PopupData { self.xdg_surface.upgrade().ok() } - fn panel_item(&self) -> Option> { + fn panel_item(&self) -> Option>> { XdgSurfaceData::get(&self.xdg_surface()?)? .lock() .panel_item() @@ -680,9 +677,7 @@ impl Dispatch, WaylandState> for WaylandState { data.positioner = positioner; let Some(panel_item) = data.panel_item() else {return}; - if let Backend::Wayland(w) = &panel_item.backend { - w.reposition_popup(&panel_item, &*data) - } + panel_item.backend.reposition_popup(&panel_item, &*data) } xdg_popup::Request::Destroy => { let data = data.lock(); @@ -704,9 +699,258 @@ impl Dispatch, WaylandState> for WaylandState { ) { let data = data.lock(); let Some(panel_item) = data.panel_item() else {return}; + panel_item.backend.drop_popup(&panel_item, &data.uid); + } +} - if let Backend::Wayland(w) = &panel_item.backend { - w.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 _ = node.send_remote_signal("new_popup", &serialize(&(&uid, data)).unwrap()); + } + 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 _ = node.send_remote_signal("drop_popup", &serialize(uid).unwrap()); + } + + 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(); + + serialize(( + id, + ( + self.cursor.borrow().as_ref().and_then(|c| c.cursor_data()), + toplevel_state, + self.popups_data(), + pointer_grab, + keyboard_grab, + ), + )) + .map_err(|e| e.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) + } + + 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 }, + }, + ) + } +} diff --git a/src/wayland/xwayland.rs b/src/wayland/xwayland.rs index e74441b..81836e0 100644 --- a/src/wayland/xwayland.rs +++ b/src/wayland/xwayland.rs @@ -1,16 +1,23 @@ -use super::{panel_item::RecommendedState, seat::SeatData, state::WaylandState}; -use crate::wayland::{ - panel_item::{Backend, PanelItem, X11Backend}, - surface::CoreSurface, +use super::{ + seat::{KeyboardEvent, PointerEvent, SeatData}, + xdg_shell::PopupData, +}; +use crate::{ + nodes::{ + drawable::model::ModelPart, + items::panel::{Backend, PanelItem, RecommendedState, SurfaceID}, + }, + wayland::surface::CoreSurface, }; use color_eyre::eyre::Result; +use mint::Vector2; use once_cell::sync::OnceCell; use parking_lot::Mutex; use smithay::{ reexports::{ calloop::{EventLoop, LoopSignal}, wayland_protocols::xdg::shell::server::xdg_toplevel, - wayland_server::{Display, DisplayHandle, Resource, WEnum}, + wayland_server::{protocol::wl_surface::WlSurface, DisplayHandle, Resource, WEnum}, x11rb::protocol::xproto::Window, }, utils::{Logical, Rectangle}, @@ -20,6 +27,7 @@ use smithay::{ X11Surface, X11Wm, XWayland, XWaylandEvent, XwmHandler, }, }; +use stardust_xr::schemas::flex::serialize; use std::{ffi::OsStr, iter::empty, sync::Arc, time::Duration}; use tokio::sync::oneshot; use tracing::debug; @@ -31,10 +39,7 @@ pub struct XWaylandState { event_loop_signal: LoopSignal, } impl XWaylandState { - pub fn create( - wayland_display: Arc>>, - dh: &DisplayHandle, - ) -> Result { + pub fn create(dh: &DisplayHandle) -> Result { let dh = dh.clone(); let (tx, rx) = oneshot::channel(); @@ -45,18 +50,22 @@ impl XWaylandState { let handle = event_loop.handle(); event_loop .handle() - .insert_source(connection, move |event, _, handler| match event { - XWaylandEvent::Ready { - connection, - client, - client_fd: _, - display: _, - } => { - handler.seat = Some(SeatData::new(&dh, client.id())); - handler.wm = - X11Wm::start_wm(handle.clone(), dh.clone(), connection, client).ok(); + .insert_source(connection, { + let dh = dh.clone(); + move |event, _, handler| match event { + XWaylandEvent::Ready { + connection, + client, + client_fd: _, + display: _, + } => { + handler.seat = Some(SeatData::new(&dh, client.id())); + handler.wm = + X11Wm::start_wm(handle.clone(), dh.clone(), connection, client) + .ok(); + } + XWaylandEvent::Exited => (), } - XWaylandEvent::Exited => (), }) .map_err(|e| e.error)?; @@ -71,10 +80,8 @@ impl XWaylandState { display, event_loop_signal: event_loop.get_signal(), }); - let wayland_display_handle = wayland_display.lock().handle(); let mut handler = XWaylandHandler { - wayland_display, - wayland_display_handle, + wayland_display_handle: dh, wm: None, seat: None, }; @@ -94,15 +101,14 @@ impl Drop for XWaylandState { } struct XWaylandHandler { - wayland_display: Arc>>, wayland_display_handle: DisplayHandle, wm: Option, seat: Option>, } impl XWaylandHandler { - fn panel_item(&self, window: &X11Surface) -> Option> { + fn panel_item(&self, window: &X11Surface) -> Option>> { compositor::with_states(&window.wl_surface()?, |s| { - s.data_map.get::>().cloned() + s.data_map.get::>>().cloned() }) } } @@ -130,8 +136,7 @@ impl XwmHandler for XWaylandHandler { let dh = self.wayland_display_handle.clone(); let seat = self.seat.clone().unwrap(); CoreSurface::add_to( - &self.wayland_display, - self.wayland_display.lock().handle(), + self.wayland_display_handle.clone(), &window.wl_surface().unwrap(), { let window = window.clone(); @@ -140,22 +145,24 @@ impl XwmHandler for XWaylandHandler { let seat = seat.clone(); window.user_data().insert_if_missing_threadsafe(|| { let (_node, panel_item) = PanelItem::create( - wl_surface.clone(), - Backend::X11(X11Backend { + Box::new(X11Backend { toplevel_parent: None, toplevel: window.clone(), + seat, + _pointer_grab: Mutex::new(None), + _keyboard_grab: Mutex::new(None), }), wl_surface .client() - .and_then(|c| c.get_credentials(&dh).ok()), - seat, + .and_then(|c| c.get_credentials(&dh).ok()) + .map(|c| c.pid), ); panel_item }); } }, move |_| { - let Some(panel_item) = window.user_data().get::>() else {return}; + let Some(panel_item) = window.user_data().get::>>() else {return}; panel_item.commit_toplevel(); }, ); @@ -246,3 +253,144 @@ impl XwmHandler for XWaylandHandler { panel_item.recommend_toplevel_state(RecommendedState::Minimize); } } + +pub struct X11Backend { + pub toplevel_parent: Option, + pub toplevel: X11Surface, + pub seat: Arc, + _pointer_grab: Mutex>, + _keyboard_grab: Mutex>, +} +impl X11Backend { + fn wl_surface_from_id(&self, id: &SurfaceID) -> Option { + match id { + SurfaceID::Cursor => None, + SurfaceID::Toplevel => self.toplevel.wl_surface(), + SurfaceID::Popup(_) => None, + } + } + + // 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 X11Backend { + fn serialize_start_data(&self, id: &str) -> Result> { + let size = ( + self.toplevel.geometry().size.w as u32, + self.toplevel.geometry().size.h as u32, + ); + let toplevel_state = ( + None::, + self.toplevel.title(), + None::, + ( + self.toplevel.geometry().size.w as u32, + self.toplevel.geometry().size.h as u32, + ), + self.toplevel.min_size().map(|s| (s.w as u32, s.h as u32)), + self.toplevel.max_size().map(|s| (s.w as u32, s.w as u32)), + ((0_i32, 0_i32), size), + vec![0_u32; 0], + ); + let info = ( + None::<(Vector2, Vector2)>, + toplevel_state, + Vec::::new(), + None::, + None::, + ); + serialize((id, info)).map_err(|e| e.into()) + } + fn serialize_toplevel(&self) -> Result> { + let toplevel_state = ( + None::, + self.toplevel.title(), + None::, + ( + self.toplevel.geometry().size.w, + self.toplevel.geometry().size.h, + ), + self.toplevel.min_size().map(|s| (s.w, s.h)), + self.toplevel.max_size().map(|s| (s.w, s.w)), + ); + let data = serialize(&toplevel_state)?; + Ok(data) + } + + fn set_toplevel_capabilities(&self, _capabilities: Vec) {} + + fn configure_toplevel( + &self, + size: Option>, + states: Vec, + _bounds: Option>, + ) { + let _ = self.toplevel.configure( + size.map(|s| Rectangle::from_loc_and_size((0, 0), (s.x as i32, s.y as i32))), + ); + let _ = self.toplevel.set_maximized(states.contains(&1)); + } + + 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<()> { + self.seat.set_keymap_str( + &keymap, + if let Some(toplevel) = self.toplevel.wl_surface() { + vec![toplevel] + } else { + vec![] + }, + ) + } + 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 }, + }, + ) + } +}