From de0295fbf817e0943f95be7a787b631ed8432530 Mon Sep 17 00:00:00 2001 From: Nova Date: Tue, 22 Aug 2023 05:22:08 -0400 Subject: [PATCH] refactor: panel items --- Cargo.lock | 5 +- Cargo.toml | 1 + src/core/client.rs | 2 +- src/nodes/items/panel.rs | 457 ++++++++++++++++++++++------------- src/nodes/mod.rs | 13 + src/wayland/seat.rs | 74 +++--- src/wayland/xdg_shell.rs | 509 +++++++++++++++++++++++---------------- src/wayland/xwayland.rs | 238 +++++++++++------- 8 files changed, 784 insertions(+), 515 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index 617e0ce..b113b63 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -1918,9 +1918,9 @@ dependencies = [ [[package]] name = "serde_repr" -version = "0.1.15" +version = "0.1.16" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e168eaaf71e8f9bd6037feb05190485708e019f4fd87d161b3c0a0d37daf85e5" +checksum = "8725e1dfadb3a50f7e5ce0b1a540466f6ed3fe7a0fca2ac2b8b831d31316bd00" dependencies = [ "proc-macro2", "quote", @@ -2081,6 +2081,7 @@ dependencies = [ "rustc-hash", "send_wrapper", "serde", + "serde_repr", "smithay", "stardust-xr", "stereokit", diff --git a/Cargo.toml b/Cargo.toml index 7480e55..c05f20f 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -55,6 +55,7 @@ xkbcommon = { version = "0.5.0", default-features = false, optional = true } stardust-xr = "0.14.0" directories = "5.0.0" serde = { version = "1.0.160", features = ["derive"] } +serde_repr = "0.1.16" tracing = "0.1.37" tracing-subscriber = { version = "0.3.17", features = ["env-filter"] } global_counter = "0.2.2" diff --git a/src/core/client.rs b/src/core/client.rs index 986fa9c..a71d0b6 100644 --- a/src/core/client.rs +++ b/src/core/client.rs @@ -52,7 +52,7 @@ pub fn startup_settings(env: &FxHashMap) -> Option, + pub pid: Option, // env: Option>, exe: Option, dispatch_join_handle: OnceCell>>, diff --git a/src/nodes/items/panel.rs b/src/nodes/items/panel.rs index a15e1c6..bff61f0 100644 --- a/src/nodes/items/panel.rs +++ b/src/nodes/items/panel.rs @@ -20,6 +20,7 @@ use serde::{ ser::Serializer, Deserialize, Serialize, }; +use serde_repr::{Deserialize_repr, Serialize_repr}; use stardust_xr::schemas::flex::{deserialize, serialize}; use std::sync::{Arc, Weak}; use tracing::debug; @@ -29,20 +30,31 @@ lazy_static! { type_name: "panel", aliased_local_signals: vec![ "apply_surface_material", - "configure_toplevel", - "set_toplevel_capabilities", + "close_toplevel", + "auto_size_toplevel", + "set_toplevel_size_changed", + "set_toplevel_state", + "set_toplevel_tiling", + "set_toplevel_bounds", + "set_maximize_enabled", + "set_minimize_enabled", + "set_fullscreen_enabled", + "set_window_menu_enabled", "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", + "toplevel_parent_changed", + "toplevel_title_changed", + "toplevel_app_id_changed", + "toplevel_window_menu", "recommend_toplevel_state", + "toplevel_move_request", + "toplevel_resize_request", + "toplevel_size_changed", "set_cursor", "new_child", "reposition_child", @@ -118,28 +130,101 @@ impl serde::Serialize for SurfaceID { } } -#[derive(Debug, Clone, Copy, Serialize)] -#[serde(tag = "type", content = "content")] -pub enum RecommendedState { - Maximize(bool), - Fullscreen(bool), - Minimize, - Move, - Resize(u32), +/// The origin and size of the surface's "solid" part. +#[derive(Debug, Serialize, Clone, Copy)] +pub struct Geometry { + pub origin: Vector2, + pub size: Vector2, +} +/// The state of the panel item's toplevel. +#[derive(Debug, Clone, Serialize)] +pub struct ToplevelInfo { + /// The UID of the panel item of the parent of this toplevel, if it exists + pub parent: Option, + /// Equivalent to the window title + pub title: Option, + /// Application identifier, see + pub app_id: Option, + /// Current size in pixels + pub size: Vector2, + /// Recommended minimum size in pixels + pub min_size: Option>, + /// Recommended maximum size in pixels + pub max_size: Option>, + /// Surface geometry + pub logical_rectangle: Geometry, +} + +#[repr(u32)] +#[derive(Debug, Clone, Copy, Serialize_repr, Deserialize_repr)] +pub enum ToplevelState { + Floating, + Minimized, + UnMinimized, + Maximized, + UnMaximized, + Fullscreen, + UnFullscreen, +} + +/// Data on positioning a child +#[derive(Debug, Clone, Serialize)] +pub struct ChildInfo { + pub uid: String, + pub parent: SurfaceID, + pub geometry: Geometry, +} + +#[derive(Debug, Clone, Deserialize_repr)] +#[repr(u32)] +pub enum Edge { + None = 0, + Top = 1, + Bottom = 2, + Left = 4, + TopLeft = 5, + BottomLeft = 6, + Right = 8, + TopRight = 9, + BottomRight = 10, +} + +/// The init data for the panel item. +#[derive(Debug, Clone, Serialize)] +pub struct PanelItemInitData { + /// The cursor, if applicable. + pub cursor: Option, + /// Size of the toplevel surface in pixels. + pub toplevel: ToplevelInfo, + /// Vector of childs that already exist + pub children: Vec, + /// The surface, if any, that has exclusive input to the pointer. + pub pointer_grab: Option, + /// The surface, if any, that has exclusive input to the keyboard. + pub keyboard_grab: Option, } 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 start_data(&self) -> Result; + fn apply_surface_material(&self, surface: SurfaceID, model_part: &Arc); + fn close_toplevel(&self); + fn auto_size_toplevel(&self); + fn set_toplevel_size(&self, size: Vector2); + fn toplevel_maximize(&self); + fn toplevel_unmaximize(&self); + fn toplevel_fullscreen(&self); + fn toplevel_unfullscreen(&self); + fn set_toplevel_tiling(&self, up: bool, down: bool, left: bool, right: bool); + fn set_toplevel_bounds(&self, bounds: Option>); + fn set_toplevel_focused_visuals(&self, focused: bool); + + fn set_maximize_enabled(&self, enabled: bool); + fn set_minimize_enabled(&self, enabled: bool); + fn set_fullscreen_enabled(&self, enabled: bool); + fn set_window_menu_enabled(&self, enabled: bool); + fn pointer_motion(&self, surface: &SurfaceID, position: Vector2); fn pointer_button(&self, surface: &SurfaceID, button: u32, pressed: bool); fn pointer_scroll( @@ -149,7 +234,6 @@ pub trait Backend: Send + Sync + 'static { scroll_steps: Option>, ); - fn keyboard_set_keymap(&self, keymap: &str) -> Result<()>; fn keyboard_key(&self, surface: &SurfaceID, key: u32, state: bool); } @@ -160,7 +244,7 @@ pub fn panel_item_from_node(node: &Node) -> Option> { pub trait PanelItemTrait: Backend + Send + Sync + 'static { fn uid(&self) -> &str; - // fn node(&self) -> Option>; + fn serialize_start_data(&self, id: &str) -> Result; } pub struct PanelItem { @@ -210,20 +294,34 @@ impl PanelItem { 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("apply_surface_material", Self::apply_surface_material_flex); + node.add_local_signal("close_toplevel", Self::close_toplevel_flex); + node.add_local_signal("auto_size_toplevel", Self::auto_size_toplevel_flex); + node.add_local_signal("set_toplevel_size_changed", Self::set_toplevel_size_changed); + node.add_local_signal("toplevel_maximize", Self::toplevel_maximize_flex); + node.add_local_signal("toplevel_unmaximize", Self::toplevel_unmaximize_flex); + node.add_local_signal("toplevel_fullscreen", Self::toplevel_fullscreen_flex); + node.add_local_signal("toplevel_unfullscreen", Self::toplevel_unfullscreen_flex); + node.add_local_signal("set_toplevel_tiling", Self::set_toplevel_tiling_flex); + node.add_local_signal("set_toplevel_bounds", Self::set_toplevel_bounds_flex); + + node.add_local_signal("set_maximize_enabled", Self::set_maximize_enabled_flex); + node.add_local_signal("set_minimize_enabled", Self::set_minimize_enabled_flex); + node.add_local_signal("set_fullscreen_enabled", Self::set_fullscreen_enabled_flex); node.add_local_signal( - "keyboard_set_keymap_string", - Self::keyboard_set_keymap_string_flex, + "set_window_menu_enabled", + Self::set_window_menu_enabled_flex, ); + + node.add_local_signal("pointer_motion", Self::pointer_motion_flex); + node.add_local_signal("pointer_button", Self::pointer_button_flex); + node.add_local_signal("pointer_scroll", Self::pointer_scroll_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, @@ -232,11 +330,88 @@ impl PanelItem { (node, panel_item) } + pub fn drop_toplevel(&self) { + let Some(node) = self.node.upgrade() else {return}; + node.destroy(); + } +} - pub fn node(&self) -> Option> { - self.node.upgrade() +// Remote signals +impl PanelItem { + pub fn toplevel_parent_changed(&self, parent: &str) { + let Some(node) = self.node.upgrade() else {return}; + let _ = node.send_remote_signal("toplevel_parent_changed", serialize(parent).unwrap()); + } + pub fn toplevel_title_changed(&self, title: &str) { + let Some(node) = self.node.upgrade() else {return}; + let _ = node.send_remote_signal("toplevel_title_changed", serialize(title).unwrap()); + } + pub fn toplevel_app_id_changed(&self, app_id: &str) { + let Some(node) = self.node.upgrade() else {return}; + let _ = node.send_remote_signal("toplevel_app_id_changed", serialize(app_id).unwrap()); + } + pub fn toplevel_window_menu(&self, offset: Vector2) { + let Some(node) = self.node.upgrade() else {return}; + let _ = node.send_remote_signal("toplevel_window_menu", serialize(offset).unwrap()); + } + pub fn recommend_toplevel_state(&self, state: ToplevelState) { + let Some(node) = self.node.upgrade() else {return}; + let _ = node.send_remote_signal("recommend_toplevel_state", serialize(state).unwrap()); + } + pub fn toplevel_move_request(&self) { + let Some(node) = self.node.upgrade() else {return}; + let _ = node.send_remote_signal("toplevel_move_request", Vec::::new()); + } + pub fn toplevel_resize_request(&self, up: bool, down: bool, left: bool, right: bool) { + let Some(node) = self.node.upgrade() else {return}; + let _ = node.send_remote_signal( + "toplevel_resize_request", + serialize((up, down, left, right)).unwrap(), + ); + } + pub fn toplevel_size_changed(&self, size: Vector2) { + let Some(node) = self.node.upgrade() else {return}; + let _ = node.send_remote_signal("toplevel_size_changed", serialize(size).unwrap()); } + pub fn set_cursor(&self, geometry: Option) { + let Some(node) = self.node.upgrade() else {return}; + let _ = node.send_remote_signal("set_cursor", serialize(geometry).unwrap()); + } + + pub fn new_child(&self, info: ChildInfo) { + let Some(node) = self.node.upgrade() else {return}; + let _ = node.send_remote_signal("new_child", serialize(info).unwrap()); + } + pub fn reposition_child(&self, uid: &str, geometry: Geometry) { + let Some(node) = self.node.upgrade() else {return}; + let _ = node.send_remote_signal("reposition_child", serialize((uid, geometry)).unwrap()); + } + pub fn drop_child(&self, uid: &str) { + let Some(node) = self.node.upgrade() else {return}; + let _ = node.send_remote_signal("drop_child", serialize(uid).unwrap()); + } +} +// Local signals +macro_rules! flex_no_args { + ($fn_name: ident, $trait_fn: ident) => { + fn $fn_name(node: &Node, _calling_client: Arc, _message: Message) -> Result<()> { + let Some(panel_item) = panel_item_from_node(node) else { return Ok(()) }; + panel_item.$trait_fn(); + Ok(()) + } + }; +} +macro_rules! flex_deserialize { + ($fn_name: ident, $trait_fn: ident) => { + fn $fn_name(node: &Node, _calling_client: Arc, message: Message) -> Result<()> { + let Some(panel_item) = panel_item_from_node(node) else { return Ok(()) }; + panel_item.$trait_fn(deserialize(message.as_ref())?); + Ok(()) + } + }; +} +impl PanelItem { fn apply_surface_material_flex( node: &Node, calling_client: Arc, @@ -264,6 +439,30 @@ impl PanelItem { Ok(()) } + flex_no_args!(close_toplevel_flex, close_toplevel); + flex_no_args!(auto_size_toplevel_flex, auto_size_toplevel); + flex_deserialize!(set_toplevel_size_changed, set_toplevel_size); + flex_no_args!(toplevel_maximize_flex, toplevel_maximize); + flex_no_args!(toplevel_unmaximize_flex, toplevel_unmaximize); + flex_no_args!(toplevel_fullscreen_flex, toplevel_fullscreen); + flex_no_args!(toplevel_unfullscreen_flex, toplevel_unfullscreen); + flex_deserialize!(set_toplevel_bounds_flex, set_toplevel_bounds); + fn set_toplevel_tiling_flex( + node: &Node, + _calling_client: Arc, + message: Message, + ) -> Result<()> { + let Some(panel_item) = panel_item_from_node(node) else { return Ok(()) }; + let (up, down, left, right) = deserialize(message.as_ref())?; + panel_item.set_toplevel_tiling(up, down, left, right); + Ok(()) + } + + flex_deserialize!(set_maximize_enabled_flex, set_maximize_enabled); + flex_deserialize!(set_minimize_enabled_flex, set_minimize_enabled); + flex_deserialize!(set_fullscreen_enabled_flex, set_fullscreen_enabled); + flex_deserialize!(set_window_menu_enabled_flex, set_window_menu_enabled); + fn pointer_motion_flex( node: &Node, _calling_client: Arc, @@ -312,65 +511,6 @@ impl PanelItem { Ok(()) } - fn keyboard_set_keymap_string_flex( - node: &Node, - _calling_client: Arc, - message: Message, - ) -> Result<()> { - let keymap_string: &str = deserialize(message.as_ref())?; - - 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, - // message: Message, - // ) -> 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(message.as_ref())?; - // 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, @@ -390,89 +530,69 @@ impl PanelItem { let Ok(message) = serialize(sid) else {return}; let _ = node.send_remote_signal("grab_keyboard", message); } - - fn configure_toplevel_flex( - node: &Node, - _calling_client: Arc, - message: Message, - ) -> 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(message.as_ref())?; - - panel_item.configure_toplevel(info.size, info.states, info.bounds); - Ok(()) - } - fn set_toplevel_capabilities_flex( - node: &Node, - _calling_client: Arc, - message: Message, - ) -> Result<()> { - let Some(panel_item) = panel_item_from_node(node) else { return Ok(()) }; - - let capabilities: Vec = deserialize(message.as_ref())?; - 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); - } - - pub fn drop_toplevel(&self) { - let Some(node) = self.node.upgrade() else {return}; - node.destroy(); - } } impl PanelItemTrait for PanelItem { fn uid(&self) -> &str { &self.uid } + + fn serialize_start_data(&self, id: &str) -> Result { + Ok(serialize((id, self.start_data()?))?.into()) + } } 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 start_data(&self) -> Result { + self.backend.start_data() } fn apply_surface_material(&self, surface: SurfaceID, model_part: &Arc) { self.backend.apply_surface_material(surface, model_part) } + fn close_toplevel(&self) { + self.backend.close_toplevel() + } + fn auto_size_toplevel(&self) { + self.backend.auto_size_toplevel() + } + fn set_toplevel_size(&self, size: Vector2) { + self.backend.set_toplevel_size(size) + } + + fn set_toplevel_tiling(&self, up: bool, down: bool, left: bool, right: bool) { + self.backend.set_toplevel_tiling(up, down, left, right) + } + fn set_toplevel_bounds(&self, bounds: Option>) { + self.backend.set_toplevel_bounds(bounds) + } + fn set_toplevel_focused_visuals(&self, focused: bool) { + self.backend.set_toplevel_focused_visuals(focused) + } + fn toplevel_maximize(&self) { + self.backend.toplevel_maximize() + } + fn toplevel_unmaximize(&self) { + self.backend.toplevel_unmaximize() + } + fn toplevel_fullscreen(&self) { + self.backend.toplevel_fullscreen() + } + fn toplevel_unfullscreen(&self) { + self.backend.toplevel_unfullscreen() + } + fn set_maximize_enabled(&self, enabled: bool) { + self.backend.set_maximize_enabled(enabled) + } + fn set_minimize_enabled(&self, enabled: bool) { + self.backend.set_minimize_enabled(enabled) + } + fn set_fullscreen_enabled(&self, enabled: bool) { + self.backend.set_fullscreen_enabled(enabled) + } + fn set_window_menu_enabled(&self, enabled: bool) { + self.backend.set_window_menu_enabled(enabled) + } + fn pointer_motion(&self, surface: &SurfaceID, position: Vector2) { self.backend.pointer_motion(surface, position) } @@ -489,9 +609,6 @@ impl Backend for PanelItem { .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) } diff --git a/src/nodes/mod.rs b/src/nodes/mod.rs index f9c3457..fb0906c 100644 --- a/src/nodes/mod.rs +++ b/src/nodes/mod.rs @@ -173,6 +173,19 @@ impl Node { .store(deserialize(message.as_ref())?, Ordering::Relaxed); Ok(()) } + // very much up for debate if we should allow this, as you can match objects using this + // pub fn get_client_pid_flex( + // node: &Node, + // _calling_client: Arc, + // _message: Message, + // ) -> Result { + // let client = node + // .client + // .upgrade() + // .ok_or_else(|| eyre!("Could not get client for node?"))?; + // let pid = client.pid.ok_or_else(|| eyre!("Client PID is unknown"))?; + // Ok(serialize(pid)?.into()) + // } pub fn destroy_flex( node: &Node, _calling_client: Arc, diff --git a/src/wayland/seat.rs b/src/wayland/seat.rs index b17704b..2217f9a 100644 --- a/src/wayland/seat.rs +++ b/src/wayland/seat.rs @@ -3,8 +3,11 @@ use super::{ surface::CoreSurface, GLOBAL_DESTROY_QUEUE, SERIAL_COUNTER, }; -use crate::core::task; -use color_eyre::eyre::{eyre, Result}; +use crate::{ + core::task, + nodes::items::panel::{Backend, Geometry, PanelItem}, +}; +use color_eyre::eyre::Result; use mint::Vector2; use nanoid::nanoid; use once_cell::sync::OnceCell; @@ -12,7 +15,7 @@ use parking_lot::Mutex; use rand::{seq::IteratorRandom, thread_rng}; use rustc_hash::{FxHashMap, FxHashSet}; use smithay::{ - input::keyboard::{KeymapFile, ModifiersState}, + input::keyboard::ModifiersState, reexports::wayland_server::{ backend::{ClientId, GlobalId, ObjectId}, protocol::{ @@ -33,10 +36,23 @@ use std::{ }; use tokio::sync::watch; use tracing::{debug, warn}; -use xkbcommon::xkb::{self, ffi::XKB_KEYMAP_FORMAT_TEXT_V1, Keymap}; +use xkbcommon::xkb::{self, Context, Keymap}; + +pub fn handle_cursor( + panel_item: &Arc>, + mut cursor: watch::Receiver>, +) { + let panel_item_weak = Arc::downgrade(panel_item); + let _ = task::new(|| "cursor handler", async move { + while cursor.changed().await.is_ok() { + let Some(panel_item) = panel_item_weak.upgrade() else {continue}; + let cursor_info = cursor.borrow(); + panel_item.set_cursor(cursor_info.as_ref().and_then(CursorInfo::cursor_data)); + } + }); +} pub struct KeyboardInfo { - keymap: KeymapFile, state: xkb::State, mods: ModifiersState, keys: FxHashSet, @@ -45,7 +61,6 @@ impl KeyboardInfo { pub fn new(keymap: &Keymap) -> Self { KeyboardInfo { state: xkb::State::new(keymap), - keymap: KeymapFile::new(keymap), mods: ModifiersState::default(), keys: FxHashSet::default(), } @@ -101,7 +116,6 @@ pub enum PointerEvent { } #[derive(Debug, Clone)] pub enum KeyboardEvent { - Keymap, Key { key: u32, state: u32 }, } @@ -112,7 +126,7 @@ struct SurfaceInfo { pointer_queue: VecDeque, pointer_latest_event: Instant, keyboard_queue: VecDeque, - keyboard_info: Option, + keyboard_info: KeyboardInfo, } impl SurfaceInfo { fn new(wl_surface: &WlSurface, cursor_sender: watch::Sender>) -> Self { @@ -122,7 +136,9 @@ impl SurfaceInfo { pointer_queue: VecDeque::new(), pointer_latest_event: Instant::now(), keyboard_queue: VecDeque::new(), - keyboard_info: None, + keyboard_info: KeyboardInfo::new( + &Keymap::new_from_names(&Context::new(0), "", "", "", "", None, 0).unwrap(), + ), } } fn flush(&self) { @@ -217,25 +233,20 @@ impl SurfaceInfo { locked } - fn handle_keyboard_events(&mut self, keyboard: &WlKeyboard, mut locked: bool) -> bool { + fn handle_keyboard_events(&mut self, keyboard: &WlKeyboard, locked: bool) -> bool { let Ok(focus) = self.wl_surface.upgrade() else { return false; }; - let Some(info) = self.keyboard_info.as_mut() else { return true; }; if !locked { keyboard.enter(0, &focus, vec![]); if keyboard.version() >= wl_keyboard::EVT_REPEAT_INFO_SINCE { keyboard.repeat_info(0, 0); } - locked = info.keymap.send(keyboard).is_ok(); } while let Some(event) = self.keyboard_queue.pop_front() { debug!(locked, ?event, "Process keyboard event"); match (locked, event) { - (true, KeyboardEvent::Keymap) => { - let _ = info.keymap.send(keyboard); - } (true, KeyboardEvent::Key { key, state }) => { - if let Ok(key_count) = info.process(key, state, keyboard) { + if let Ok(key_count) = self.keyboard_info.process(key, state, keyboard) { if key_count == 0 { keyboard.leave(SERIAL_COUNTER.inc(), &focus); return false; @@ -279,29 +290,6 @@ 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}; - for surface in surfaces { - let Some(surface_info) = panels.get_mut(&surface.id()) else {continue}; - surface_info - .keyboard_info - .replace(KeyboardInfo::new(keymap)); - - if *focus.lock() == surface.id() { - surface_info.keyboard_queue.push_back(KeyboardEvent::Keymap); - } - } - } - pub fn pointer_event(&self, surface: &WlSurface, event: PointerEvent) { let mut surfaces = self.surfaces.lock(); let Some(surface_info) = surfaces.get_mut(&surface.id()) else {return}; @@ -357,7 +345,6 @@ impl SeatData { if keyboard_focus.is_null() { *keyboard_focus = surfaces .iter() - .filter(|(_k, v)| v.keyboard_info.is_some()) .filter(|(_k, v)| !v.keyboard_queue.is_empty()) .map(|(k, _v)| k) .choose(&mut thread_rng()) @@ -414,9 +401,12 @@ pub struct CursorInfo { pub hotspot_y: i32, } impl CursorInfo { - pub fn cursor_data(&self) -> Option<(Vector2, Vector2)> { + pub fn cursor_data(&self) -> Option { let cursor_size = CoreSurface::from_wl_surface(&self.surface.upgrade().ok()?)?.size()?; - Some((cursor_size, [self.hotspot_x, self.hotspot_y].into())) + Some(Geometry { + origin: [self.hotspot_x, self.hotspot_y].into(), + size: cursor_size, + }) } } diff --git a/src/wayland/xdg_shell.rs b/src/wayland/xdg_shell.rs index f5ccf94..cd85e9b 100644 --- a/src/wayland/xdg_shell.rs +++ b/src/wayland/xdg_shell.rs @@ -4,23 +4,30 @@ use super::{ surface::CoreSurface, SERIAL_COUNTER, }; -use crate::nodes::{ - drawable::model::ModelPart, - items::panel::{Backend, PanelItem, RecommendedState, SurfaceID}, - Message, Node, +use crate::{ + nodes::{ + drawable::model::ModelPart, + items::panel::{ + Backend, ChildInfo, Geometry, PanelItem, PanelItemInitData, SurfaceID, ToplevelInfo, + ToplevelState, + }, + Node, + }, + wayland::seat::handle_cursor, }; -use color_eyre::eyre::{eyre, Result}; +use color_eyre::eyre::{bail, 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_toplevel::{ + self, ResizeEdge, XdgToplevel, EVT_CONFIGURE_BOUNDS_SINCE, EVT_WM_CAPABILITIES_SINCE, + }, xdg_wm_base::{self, XdgWmBase}, }, wayland_server::{ @@ -30,14 +37,12 @@ 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( @@ -195,6 +200,14 @@ impl PositionerData { pos } } +impl From for Geometry { + fn from(value: PositionerData) -> Self { + Geometry { + origin: value.get_pos(), + size: value.size, + } + } +} impl Dispatch, WaylandState> for WaylandState { fn request( @@ -285,20 +298,6 @@ impl Dispatch, WaylandState> for WaylandSta } } -#[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, @@ -388,6 +387,7 @@ impl Dispatch, WaylandState> for WaylandState ); toplevel_data.lock().panel_item_node.replace(node); xdg_surface_data.lock().panel_item = Arc::downgrade(&panel_item); + handle_cursor(&panel_item, panel_item.backend.cursor.clone()); } }, { @@ -410,7 +410,12 @@ impl Dispatch, WaylandState> for WaylandState xdg_surface.configure(SERIAL_COUNTER.inc()); return }; - panel_item.commit_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}; + let Some(core_surface) = CoreSurface::from_wl_surface(&wl_surface) else {return}; + let Some(size) = core_surface.size() else {return}; + panel_item.toplevel_size_changed(size); } }, ); @@ -441,9 +446,17 @@ impl Dispatch, WaylandState> for WaylandState parent_data.surface_id.clone(), positioner, )); + let panel_item = parent_data.panel_item().unwrap(); + handle_cursor( + &panel_item, + panel_item + .backend + .seat + .new_surface(&popup_data.lock().wl_surface().unwrap()), + ); let xdg_popup = data_init.init(id, popup_data); xdg_surface_data.lock().surface_id = SurfaceID::Child(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"); @@ -456,7 +469,6 @@ impl Dispatch, WaylandState> for WaylandState 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); @@ -498,10 +510,6 @@ impl Dispatch, WaylandState> for WaylandState } } -fn serde_error(msg: &str) -> Result { - Err(serde::ser::Error::custom(msg)) -} - #[derive(Debug, Clone)] pub struct ToplevelData { panel_item_node: Option>, @@ -511,7 +519,6 @@ pub struct ToplevelData { app_id: Option, max_size: Option>, min_size: Option>, - states: Vec, } impl ToplevelData { fn new(xdg_surface: &XdgSurface) -> Self { @@ -523,7 +530,6 @@ impl ToplevelData { app_id: None, max_size: None, min_size: None, - states: Vec::new(), } } @@ -540,41 +546,6 @@ impl ToplevelData { 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, @@ -588,30 +559,41 @@ impl Dispatch, WaylandState> for WaylandState { match request { xdg_toplevel::Request::SetParent { parent } => { debug!(?xdg_toplevel, ?parent, "Set XDG Toplevel parent"); - data.lock().parent = parent.map(|toplevel| toplevel.downgrade()); + data.lock().parent = parent.clone().map(|toplevel| toplevel.downgrade()); + let Some(panel_item) = data.lock().panel_item() else {return}; + if let Some(parent) = parent { + panel_item.toplevel_parent_changed( + &ToplevelData::get(&parent).lock().panel_item().unwrap().uid, + ); + } } xdg_toplevel::Request::SetTitle { title } => { debug!(?xdg_toplevel, ?title, "Set XDG Toplevel title"); - data.lock().title = (!title.is_empty()).then_some(title); + data.lock().title = (!title.is_empty()).then_some(title.clone()); + + let Some(panel_item) = data.lock().panel_item() else {return}; + panel_item.toplevel_title_changed(&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); + data.lock().app_id = (!app_id.is_empty()).then_some(app_id.clone()); + + let Some(panel_item) = data.lock().panel_item() else {return}; + panel_item.toplevel_app_id_changed(&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::ShowWindowMenu { + seat: _, + serial: _, + x, + y, + } => { + let Some(panel_item) = data.lock().panel_item() else {return}; + panel_item.toplevel_window_menu([x, y].into()); } 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); + panel_item.toplevel_move_request(); } xdg_toplevel::Request::Resize { seat, @@ -627,7 +609,18 @@ impl Dispatch, WaylandState> for WaylandState { "XDG Toplevel resize request" ); let Some(panel_item) = data.lock().panel_item() else {return}; - panel_item.recommend_toplevel_state(RecommendedState::Resize(edges as u32)); + let (up, down, left, right) = match edges { + ResizeEdge::Top => (true, false, false, false), + ResizeEdge::Bottom => (false, true, false, false), + ResizeEdge::Left => (false, false, true, false), + ResizeEdge::TopLeft => (true, false, true, false), + ResizeEdge::BottomLeft => (false, true, true, false), + ResizeEdge::Right => (false, false, false, true), + ResizeEdge::TopRight => (true, false, false, true), + ResizeEdge::BottomRight => (false, true, false, true), + _ => (false, false, false, false), + }; + panel_item.toplevel_resize_request(up, down, left, right) } xdg_toplevel::Request::SetMaxSize { width, height } => { debug!(?xdg_toplevel, width, height, "Set XDG Toplevel max size"); @@ -641,28 +634,28 @@ impl Dispatch, WaylandState> for WaylandState { } xdg_toplevel::Request::SetMaximized => { let Some(panel_item) = data.lock().panel_item() else {return}; - panel_item.recommend_toplevel_state(RecommendedState::Maximize(true)); + panel_item.recommend_toplevel_state(ToplevelState::Maximized); } xdg_toplevel::Request::UnsetMaximized => { let Some(panel_item) = data.lock().panel_item() else {return}; - panel_item.recommend_toplevel_state(RecommendedState::Maximize(false)); + panel_item.recommend_toplevel_state(ToplevelState::UnMaximized); } xdg_toplevel::Request::SetFullscreen { output: _ } => { let Some(panel_item) = data.lock().panel_item() else {return}; - panel_item.recommend_toplevel_state(RecommendedState::Fullscreen(true)); + panel_item.recommend_toplevel_state(ToplevelState::Fullscreen); } xdg_toplevel::Request::UnsetFullscreen => { let Some(panel_item) = data.lock().panel_item() else {return}; - panel_item.recommend_toplevel_state(RecommendedState::Fullscreen(true)); + panel_item.recommend_toplevel_state(ToplevelState::UnFullscreen); } xdg_toplevel::Request::SetMinimized => { let Some(panel_item) = data.lock().panel_item() else {return}; - panel_item.recommend_toplevel_state(RecommendedState::Minimize); + panel_item.recommend_toplevel_state(ToplevelState::Minimized); } 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(); + panel_item.drop_toplevel(); } _ => unreachable!(), } @@ -723,17 +716,6 @@ impl PopupData { } } -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.get_pos())?; - seq.serialize_element(&positioner_data.size)?; - seq.end() - } -} impl Debug for PopupData { fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { f.debug_struct("XdgPopupData") @@ -793,12 +775,27 @@ impl Dispatch, WaylandState> for WaylandState { } } +struct XdgToplevelState { + fullscreen: bool, + maximized: bool, + activated: bool, + tiled_up: bool, + tiled_down: bool, + tiled_left: bool, + tiled_right: bool, + capability_window_menu: bool, + capability_maximize: bool, + capability_fullscreen: bool, + capability_minimize: bool, +} + pub struct XDGBackend { toplevel: WlWeak, toplevel_wl_surface: WlWeak, + toplevel_state: Mutex, popups: Mutex>>, cursor: watch::Receiver>, - pub seat: Arc, + seat: Arc, pointer_grab: Mutex>, keyboard_grab: Mutex>, } @@ -814,6 +811,19 @@ impl XDGBackend { Some(XDGBackend { toplevel: toplevel.downgrade(), toplevel_wl_surface, + toplevel_state: Mutex::new(XdgToplevelState { + fullscreen: false, + maximized: false, + activated: false, + tiled_up: false, + tiled_down: false, + tiled_left: false, + tiled_right: false, + capability_window_menu: false, + capability_maximize: false, + capability_fullscreen: false, + capability_minimize: false, + }), popups: Mutex::new(FxHashMap::default()), cursor, seat, @@ -844,19 +854,50 @@ impl XDGBackend { 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 + + fn configure(&self, size: Option>) { + let Ok(xdg_toplevel) = self.toplevel.upgrade() else {return}; + let Some(xdg_surface) = self.toplevel_xdg_surface() else {return}; + let Some(wl_surface) = self.toplevel_wl_surface() else {return}; + let Some(core_surface) = CoreSurface::from_wl_surface(&wl_surface) else {return}; + let Some(surface_size) = core_surface.size() else {return}; + + xdg_toplevel.configure( + size.unwrap_or(surface_size).x as i32, + size.unwrap_or(surface_size).y as i32, + self.states() + .into_iter() + .flat_map(|state| state.to_ne_bytes()) + .collect(), + ); + xdg_surface.configure(SERIAL_COUNTER.inc()); + self.flush_client(); + } + fn states(&self) -> Vec { + let mut states = Vec::new(); + let toplevel_state = self.toplevel_state.lock(); + if toplevel_state.maximized { + states.push(1); + } + if toplevel_state.fullscreen { + states.push(2); + } + if toplevel_state.activated { + states.push(4); + } + if toplevel_state.tiled_left { + states.push(5); + } + if toplevel_state.tiled_right { + states.push(6); + } + if toplevel_state.tiled_up { + states.push(7); + } + if toplevel_state.tiled_down { + states.push(8); + } + states } pub fn new_popup( @@ -869,53 +910,46 @@ impl XDGBackend { 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_child", message); + let Some(positioner_data) = data.positioner_data() else {return}; + panel_item.new_child(ChildInfo { + uid, + parent: data.parent_id.clone(), + geometry: positioner_data.into(), + }) } pub fn reposition_popup(&self, panel_item: &PanelItem, popup_state: &PopupData) { - let Some(node) = panel_item.node() else {return}; let Some(positioner_data) = popup_state.positioner_data() else {return}; - - let _ = node.send_remote_signal( - "reposition_child", - serialize((positioner_data.get_pos(), positioner_data.size)).unwrap(), - ); + panel_item.reposition_child(&popup_state.uid, positioner_data.into()) } pub fn drop_popup(&self, panel_item: &PanelItem, uid: &str) { - 'seat_drop: { - let Some(popup) = self + panel_item.drop_child(uid); + 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_child", message); + .remove(uid) else {return}; + let Some(popup) = popup.upgrade().ok() else {return}; + let Some(popup) = popup.data::>().cloned() else {return}; + let Some(wl_surface) = popup.wl_surface() else {return}; + self.seat.drop_surface(&wl_surface); } - fn popups_data(&self) -> Vec { + fn child_data(&self) -> Vec { self.popups .lock() .values() - .filter_map(|v| Some(v.upgrade().ok()?.data::>()?.lock().clone())) + .filter_map(|v| { + let popup = v.upgrade().ok()?; + let data = PopupData::get(&popup)?; + let data_lock = data.lock(); + Some(ChildInfo { + uid: data_lock.uid.clone(), + parent: data_lock.parent_id.clone(), + geometry: data_lock.positioner_data()?.into(), + }) + }) .collect::>() } - pub fn on_drop(&self) { - let Some(toplevel_popup) = self.toplevel_xdg_surface().and_then(|s| XdgSurfaceData::get(&s).and_then(|s| s.lock().panel_item.upgrade())) else {return}; - toplevel_popup.drop_toplevel(); - 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::() { @@ -923,73 +957,54 @@ impl XDGBackend { } } } +impl Drop for XDGBackend { + fn drop(&mut self) { + let Some(toplevel) = self.toplevel_wl_surface() else {return}; + self.seat.drop_surface(&toplevel); + debug!("Dropped panel item gracefully"); + } +} impl Backend for XDGBackend { - fn serialize_start_data(&self, id: &str) -> Result { - let toplevel_state = self + fn start_data(&self) -> Result { + let toplevel_data = self .toplevel() - .map(|t| ToplevelData::get(&t).lock().clone()); + .map(|t| ToplevelData::get(&t).lock().clone()) + .ok_or_else(|| eyre!("Could not get toplevel"))?; 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()) - } + let Some(wl_surface) = self.toplevel_wl_surface() else {bail!("Wayland surface not found")}; + let Some(core_surface) = CoreSurface::from_wl_surface(&wl_surface) else {bail!("Core surface not found")}; + let Some(size) = core_surface.size() else {bail!("Surface size not found")}; - 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}; + let toplevel = ToplevelInfo { + parent: toplevel_data + .parent + .as_ref() + .and_then(|p| p.upgrade().ok()) + .and_then(|p| ToplevelData::get(&p).lock().panel_item()) + .map(|p| p.uid.clone()), + title: toplevel_data.title.clone(), + app_id: toplevel_data.app_id.clone(), + size, + min_size: toplevel_data.min_size.clone(), + max_size: toplevel_data.max_size.clone(), + logical_rectangle: XdgSurfaceData::get(&self.toplevel_xdg_surface().unwrap()) + .unwrap() + .lock() + .geometry + .clone() + .unwrap(), + }; - 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(); + Ok(PanelItemInitData { + cursor: self.cursor.borrow().as_ref().and_then(|c| c.cursor_data()), + toplevel, + children: self.child_data(), + pointer_grab, + keyboard_grab, + }) } fn apply_surface_material(&self, surface: SurfaceID, model_part: &Arc) { @@ -999,6 +1014,86 @@ impl Backend for XDGBackend { core_surface.apply_material(model_part); } + // 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 close_toplevel(&self) { + let Ok(xdg_toplevel) = self.toplevel.upgrade() else {return}; + xdg_toplevel.close(); + } + fn auto_size_toplevel(&self) { + self.configure(Some([0, 0].into())); + } + fn set_toplevel_size(&self, size: Vector2) { + self.configure(Some(size)); + } + fn toplevel_maximize(&self) { + self.toplevel_state.lock().maximized = true; + self.configure(None); + } + fn toplevel_unmaximize(&self) { + self.toplevel_state.lock().maximized = false; + self.configure(None); + } + fn toplevel_fullscreen(&self) { + self.toplevel_state.lock().fullscreen = true; + self.configure(None); + } + fn toplevel_unfullscreen(&self) { + self.toplevel_state.lock().fullscreen = false; + self.configure(None); + } + fn set_toplevel_focused_visuals(&self, focused: bool) { + self.toplevel_state.lock().activated = focused; + self.configure(None); + } + fn set_toplevel_tiling(&self, up: bool, down: bool, left: bool, right: bool) { + self.toplevel_state.lock().tiled_up = up; + self.toplevel_state.lock().tiled_down = down; + self.toplevel_state.lock().tiled_left = left; + self.toplevel_state.lock().tiled_right = right; + self.configure(None); + } + fn set_toplevel_bounds(&self, bounds: Option>) { + let Ok(xdg_toplevel) = self.toplevel.upgrade() else {return}; + let Some(xdg_surface) = self.toplevel_xdg_surface() else {return}; + if xdg_toplevel.version() <= EVT_CONFIGURE_BOUNDS_SINCE { + return; + } + xdg_toplevel.configure_bounds( + bounds.map(|b| b.x as i32).unwrap_or_default(), + bounds.map(|b| b.y as i32).unwrap_or_default(), + ); + xdg_surface.configure(SERIAL_COUNTER.inc()); + self.flush_client(); + } + + fn set_maximize_enabled(&self, enabled: bool) { + self.toplevel_state.lock().capability_maximize = enabled; + self.configure(None); + } + fn set_minimize_enabled(&self, enabled: bool) { + self.toplevel_state.lock().capability_minimize = enabled; + self.configure(None); + } + fn set_fullscreen_enabled(&self, enabled: bool) { + self.toplevel_state.lock().capability_fullscreen = enabled; + self.configure(None); + } + fn set_window_menu_enabled(&self, enabled: bool) { + self.toplevel_state.lock().capability_window_menu = enabled; + self.configure(None); + } + fn pointer_motion(&self, surface: &SurfaceID, position: Vector2) { let Some(surface) = self.wl_surface_from_id(surface) else {return}; self.seat @@ -1030,14 +1125,6 @@ impl Backend for XDGBackend { ) } - 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( diff --git a/src/wayland/xwayland.rs b/src/wayland/xwayland.rs index 3437139..551e9f9 100644 --- a/src/wayland/xwayland.rs +++ b/src/wayland/xwayland.rs @@ -1,13 +1,13 @@ use super::{ seat::{KeyboardEvent, PointerEvent, SeatData}, state::ClientState, - xdg_shell::PopupData, }; use crate::{ nodes::{ drawable::model::ModelPart, - items::panel::{Backend, PanelItem, RecommendedState, SurfaceID}, - Message, + items::panel::{ + Backend, Geometry, PanelItem, PanelItemInitData, SurfaceID, ToplevelInfo, ToplevelState, + }, }, wayland::surface::CoreSurface, }; @@ -18,8 +18,7 @@ use parking_lot::Mutex; use smithay::{ reexports::{ calloop::{EventLoop, LoopSignal}, - wayland_protocols::xdg::shell::server::xdg_toplevel, - wayland_server::{protocol::wl_surface::WlSurface, DisplayHandle, Resource, WEnum}, + wayland_server::{protocol::wl_surface::WlSurface, DisplayHandle, Resource}, x11rb::protocol::xproto::Window, }, utils::{Logical, Rectangle}, @@ -29,7 +28,6 @@ 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; @@ -165,7 +163,13 @@ impl XwmHandler for XWaylandHandler { }, move |_| { let Some(panel_item) = window.user_data().get::>>() else {return}; - panel_item.commit_toplevel(); + panel_item.toplevel_size_changed( + [ + window.geometry().size.w as u32, + window.geometry().size.h as u32, + ] + .into(), + ); }, ); } @@ -208,7 +212,7 @@ impl XwmHandler for XWaylandHandler { fn move_request(&mut self, _xwm: XwmId, window: X11Surface, button: u32) { let Some(panel_item) = self.panel_item(&window) else {return}; debug!(?window, button, "X window requests move"); - panel_item.recommend_toplevel_state(RecommendedState::Move); + panel_item.toplevel_move_request(); } fn resize_request( &mut self, @@ -219,40 +223,39 @@ impl XwmHandler for XWaylandHandler { ) { let Some(panel_item) = self.panel_item(&window) else {return}; debug!(?window, button, ?resize_edge, "X window requests resize"); - panel_item.recommend_toplevel_state(RecommendedState::Resize( - WEnum::Value(match resize_edge { - ResizeEdge::Top => xdg_toplevel::ResizeEdge::Top, - ResizeEdge::Bottom => xdg_toplevel::ResizeEdge::Bottom, - ResizeEdge::Left => xdg_toplevel::ResizeEdge::Left, - ResizeEdge::TopLeft => xdg_toplevel::ResizeEdge::TopLeft, - ResizeEdge::BottomLeft => xdg_toplevel::ResizeEdge::BottomLeft, - ResizeEdge::Right => xdg_toplevel::ResizeEdge::Right, - ResizeEdge::TopRight => xdg_toplevel::ResizeEdge::TopRight, - ResizeEdge::BottomRight => xdg_toplevel::ResizeEdge::BottomRight, - }) - .into(), - )); + let (up, down, left, right) = match resize_edge { + ResizeEdge::Top => (true, false, false, false), + ResizeEdge::Bottom => (false, true, false, false), + ResizeEdge::Left => (false, false, true, false), + ResizeEdge::TopLeft => (true, false, true, false), + ResizeEdge::BottomLeft => (false, true, true, false), + ResizeEdge::Right => (false, false, false, true), + ResizeEdge::TopRight => (true, false, false, true), + ResizeEdge::BottomRight => (false, true, false, true), + // _ => (false, false, false, false), + }; + panel_item.toplevel_resize_request(up, down, left, right) } fn maximize_request(&mut self, _xwm: XwmId, window: X11Surface) { let Some(panel_item) = self.panel_item(&window) else {return}; - panel_item.recommend_toplevel_state(RecommendedState::Maximize(true)); + panel_item.recommend_toplevel_state(ToplevelState::Maximized); } fn unmaximize_request(&mut self, _xwm: XwmId, window: X11Surface) { let Some(panel_item) = self.panel_item(&window) else {return}; - panel_item.recommend_toplevel_state(RecommendedState::Maximize(false)); + panel_item.recommend_toplevel_state(ToplevelState::UnMaximized); } fn fullscreen_request(&mut self, _xwm: XwmId, window: X11Surface) { let Some(panel_item) = self.panel_item(&window) else {return}; - panel_item.recommend_toplevel_state(RecommendedState::Fullscreen(true)); + panel_item.recommend_toplevel_state(ToplevelState::Fullscreen); } fn unfullscreen_request(&mut self, _xwm: XwmId, window: X11Surface) { let Some(panel_item) = self.panel_item(&window) else {return}; - panel_item.recommend_toplevel_state(RecommendedState::Fullscreen(true)); + panel_item.recommend_toplevel_state(ToplevelState::UnFullscreen); } fn minimize_request(&mut self, _xwm: XwmId, window: X11Surface) { let Some(panel_item) = self.panel_item(&window) else {return}; - panel_item.recommend_toplevel_state(RecommendedState::Minimize); + panel_item.recommend_toplevel_state(ToplevelState::Minimized); } } @@ -280,62 +283,129 @@ impl X11Backend { // } } 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::, - ); - Ok(serialize((id, info))?.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.into()) - } + // fn 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::, + // ); + // Ok(serialize((id, info))?.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.into()) + // } - fn set_toplevel_capabilities(&self, _capabilities: Vec) {} + // 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 set_toplevel_size( + // &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 start_data(&self) -> Result { + Ok(PanelItemInitData { + cursor: None, + toplevel: ToplevelInfo { + parent: None, + title: Some(self.toplevel.title()), + app_id: Some(self.toplevel.instance()), + size: [ + self.toplevel.geometry().size.w as u32, + self.toplevel.geometry().size.h as u32, + ] + .into(), + min_size: self + .toplevel + .min_size() + .map(|s| [s.w as u32, s.h as u32].into()), + max_size: self + .toplevel + .max_size() + .map(|s| [s.w as u32, s.h as u32].into()), + logical_rectangle: Geometry { + origin: [0, 0].into(), + size: [ + self.toplevel.geometry().size.w as u32, + self.toplevel.geometry().size.h as u32, + ] + .into(), + }, + }, + children: vec![], + pointer_grab: self._pointer_grab.lock().clone(), + keyboard_grab: self._keyboard_grab.lock().clone(), + }) } + fn close_toplevel(&self) {} + + fn auto_size_toplevel(&self) { + let _ = self.toplevel.configure(None); + } + fn set_toplevel_size(&self, size: Vector2) { + let _ = self.toplevel.configure(Some(Rectangle { + loc: self.toplevel.geometry().loc, + size: (size.x as i32, size.y as i32).into(), + })); + } + fn toplevel_maximize(&self) { + let _ = self.toplevel.set_maximized(true); + } + fn toplevel_unmaximize(&self) { + let _ = self.toplevel.set_maximized(false); + } + fn toplevel_fullscreen(&self) { + let _ = self.toplevel.set_fullscreen(true); + } + fn toplevel_unfullscreen(&self) { + let _ = self.toplevel.set_fullscreen(false); + } + fn set_toplevel_tiling(&self, _up: bool, _down: bool, _left: bool, _right: bool) {} + fn set_toplevel_bounds(&self, _bounds: Option>) {} + fn set_toplevel_focused_visuals(&self, focused: bool) { + let _ = self.toplevel.set_activated(focused); + } + fn set_maximize_enabled(&self, _enabled: bool) {} + fn set_minimize_enabled(&self, _enabled: bool) {} + fn set_fullscreen_enabled(&self, _enabled: bool) {} + fn set_window_menu_enabled(&self, _enabled: bool) {} fn apply_surface_material(&self, surface: SurfaceID, model_part: &Arc) { let Some(wl_surface) = self.wl_surface_from_id(&surface) else {return}; @@ -375,16 +445,6 @@ impl Backend for X11Backend { ) } - 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(