From e01b277fa35446d14cd06eadb66000f1bd0cc134 Mon Sep 17 00:00:00 2001 From: Ian Douglas Scott Date: Thu, 19 Jun 2025 07:55:11 -0700 Subject: [PATCH] protocols/workspace: Set ext workspace `id` for pinned workspace The `id` is defined to be sent only once, on creation of the handle or later. And only for workspaces that are "likely to be stable across multiple sessions". Set we add an `id` initially for pinned workspaces, and add one when the workspace is pinned. The `id` is not supposed to be human readable, so we just use a random value. --- cosmic-comp-config/src/workspace.rs | 3 ++- src/shell/mod.rs | 3 +-- src/shell/workspace.rs | 8 ++++++-- src/wayland/handlers/workspace.rs | 7 +++++++ src/wayland/protocols/workspace/ext.rs | 12 +++++++----- src/wayland/protocols/workspace/mod.rs | 23 ++++++++++++++++++++++- 6 files changed, 45 insertions(+), 11 deletions(-) diff --git a/cosmic-comp-config/src/workspace.rs b/cosmic-comp-config/src/workspace.rs index 0e8288c61..30ce06a47 100644 --- a/cosmic-comp-config/src/workspace.rs +++ b/cosmic-comp-config/src/workspace.rs @@ -43,5 +43,6 @@ pub struct OutputMatch { pub struct PinnedWorkspace { pub output: OutputMatch, pub tiling_enabled: bool, - // TODO: name, id + pub id: Option, + // TODO: name } diff --git a/src/shell/mod.rs b/src/shell/mod.rs index 1a282df32..da965082c 100644 --- a/src/shell/mod.rs +++ b/src/shell/mod.rs @@ -400,8 +400,7 @@ fn create_workspace_from_pinned( } else { TilingState::FloatingOnly }, - // TODO Set id for persistent workspaces - None, + pinned.id.clone(), ) .unwrap(); state.add_workspace_state(&workspace_handle, WState::Pinned); diff --git a/src/shell/workspace.rs b/src/shell/workspace.rs index 70009d705..a67fcb24c 100644 --- a/src/shell/workspace.rs +++ b/src/shell/workspace.rs @@ -72,8 +72,7 @@ const FULLSCREEN_ANIMATION_DURATION: Duration = Duration::from_millis(200); // For stable workspace id, generate random 24-bit integer, as a hex string // Must be compared with existing workspaces work uniqueness. -// TODO: Assign an id to any workspace that is pinned -pub fn random_id() -> String { +pub fn random_workspace_id() -> String { let id = rand::random_range(0..(2 << 24)); format!("{:x}", id) } @@ -106,6 +105,7 @@ pub struct Workspace { pub tiling_enabled: bool, pub fullscreen: Option, pub pinned: bool, + pub id: Option, pub handle: WorkspaceHandle, pub focus_stack: FocusStacks, @@ -362,6 +362,7 @@ impl Workspace { minimized_windows: Vec::new(), fullscreen: None, pinned: false, + id: None, handle, focus_stack: FocusStacks::default(), screencopy: ScreencopySessions::default(), @@ -393,6 +394,7 @@ impl Workspace { minimized_windows: Vec::new(), fullscreen: None, pinned: true, + id: pinned.id.clone(), handle, focus_stack: FocusStacks::default(), screencopy: ScreencopySessions::default(), @@ -410,6 +412,7 @@ impl Workspace { } pub fn to_pinned(&self) -> Option { + debug_assert!(self.id.is_some()); let output = self.explicit_output().clone(); if self.pinned { Some(PinnedWorkspace { @@ -418,6 +421,7 @@ impl Workspace { edid: output.edid, }, tiling_enabled: self.tiling_enabled, + id: self.id.clone(), }) } else { None diff --git a/src/wayland/handlers/workspace.rs b/src/wayland/handlers/workspace.rs index ab1ff9dec..d7bc6e0b9 100644 --- a/src/wayland/handlers/workspace.rs +++ b/src/wayland/handlers/workspace.rs @@ -61,6 +61,13 @@ impl WorkspaceHandler for State { workspace.pinned = pinned; let mut update = self.common.workspace_state.update(); if pinned { + if workspace.id.is_none() { + let id = crate::shell::random_workspace_id(); + update + .set_id(&workspace.handle, &id) + .expect("workspace already has id"); + workspace.id = Some(id); + } update.add_workspace_state(&workspace.handle, WState::Pinned); // TODO: Also need to update on changing other properties that are saved shell.workspaces.persist(&self.common.config); diff --git a/src/wayland/protocols/workspace/ext.rs b/src/wayland/protocols/workspace/ext.rs index 871edad61..3599ddba2 100644 --- a/src/wayland/protocols/workspace/ext.rs +++ b/src/wayland/protocols/workspace/ext.rs @@ -51,6 +51,7 @@ pub struct WorkspaceDataInner { capabilities: Option, coordinates: Vec, states: Option, + ext_id: Option, pub(super) cosmic_v2_handle: Option>, } @@ -404,9 +405,6 @@ where }, ) { mngr.workspace(&handle); - if let Some(id) = workspace.ext_id.clone() { - handle.id(id); - } workspace.ext_instances.push(handle); workspace.ext_instances.last_mut().unwrap() } else { @@ -492,8 +490,12 @@ where handle_state.states = Some(states); changed = true; } - // TODO ext_workspace_handle_v1::id - // TODO send id if pinned + + if handle_state.ext_id.is_none() { + if let Some(id) = workspace.ext_id.clone() { + instance.id(id); + } + } if let Some(cosmic_v2_handle) = handle_state .cosmic_v2_handle diff --git a/src/wayland/protocols/workspace/mod.rs b/src/wayland/protocols/workspace/mod.rs index f8f958ee8..dc348cc85 100644 --- a/src/wayland/protocols/workspace/mod.rs +++ b/src/wayland/protocols/workspace/mod.rs @@ -356,7 +356,6 @@ where &mut self, group: &WorkspaceGroupHandle, tiling: zcosmic_workspace_handle_v2::TilingState, - // TODO way to add id to workspace that doesn't have it ext_id: Option, ) -> Option { if let Some(group) = self.0.groups.iter_mut().find(|g| g.id == group.id) { @@ -597,6 +596,25 @@ where workspace.tiling = state; } } + + pub fn set_id( + &mut self, + workspace: &WorkspaceHandle, + id: &str, + ) -> Result<(), WorkspaceIdAlreadySetError> { + if let Some(workspace) = self + .0 + .groups + .iter_mut() + .find_map(|g| g.workspaces.iter_mut().find(|w| w.id == workspace.id)) + { + if workspace.ext_id.is_some() { + return Err(WorkspaceIdAlreadySetError); + } + workspace.ext_id = Some(id.to_owned()); + } + Ok(()) + } } impl<'a, D> Drop for WorkspaceUpdateGuard<'a, D> @@ -608,6 +626,9 @@ where } } +#[derive(Clone, Copy, Debug)] +pub struct WorkspaceIdAlreadySetError; + macro_rules! delegate_workspace { ($(@<$( $lt:tt $( : $clt:tt $(+ $dlt:tt )* )? ),+>)? $ty: ty) => { smithay::reexports::wayland_server::delegate_global_dispatch!($(@< $( $lt $( : $clt $(+ $dlt )* )? ),+ >)? $ty: [