diff --git a/Cargo.lock b/Cargo.lock index 43f9d054..28eb1033 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -1466,7 +1466,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "33d852cb9b869c2a9b3df2f71a3074817f01e1844f839a144f5fcef059a4eb5d" dependencies = [ "libc", - "windows-sys 0.59.0", + "windows-sys 0.52.0", ] [[package]] @@ -2680,12 +2680,6 @@ dependencies = [ "windows-sys 0.48.0", ] -[[package]] -name = "io-lifetimes" -version = "2.0.4" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "06432fb54d3be7964ecd3649233cddf80db2832f47fec34c01f65b3d9d774983" - [[package]] name = "itoa" version = "1.0.14" @@ -2917,7 +2911,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "fc2f4eb4bc735547cfed7c0a4922cbd04a4655978c09b54f1f7b228750664c34" dependencies = [ "cfg-if", - "windows-targets 0.52.6", + "windows-targets 0.48.5", ] [[package]] @@ -4087,9 +4081,9 @@ dependencies = [ [[package]] name = "quick-xml" -version = "0.36.2" +version = "0.37.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f7649a7b4df05aed9ea7ec6f628c67c9953a43869b8bc50929569b2999d443fe" +checksum = "165859e9e55f79d67b96c5d96f4e88b6f2695a1972849c15a6a3f5c59fc2c003" dependencies = [ "memchr", ] @@ -4391,7 +4385,7 @@ dependencies = [ "errno", "libc", "linux-raw-sys 0.4.15", - "windows-sys 0.59.0", + "windows-sys 0.52.0", ] [[package]] @@ -4991,7 +4985,7 @@ dependencies = [ "getrandom", "once_cell", "rustix", - "windows-sys 0.59.0", + "windows-sys 0.52.0", ] [[package]] @@ -5284,7 +5278,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "69fff37da548239c3bf9e64a12193d261e8b22b660991c6fd2df057c168f435f" dependencies = [ "cc", - "windows-targets 0.52.6", + "windows-targets 0.48.5", ] [[package]] @@ -5326,7 +5320,7 @@ version = "0.9.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "e3d5c197b95f1769931c89f85c33c407801d1fb7a311113bc0b39ad036f1bd81" dependencies = [ - "io-lifetimes 1.0.11", + "io-lifetimes", "libc", "libudev-sys", "pkg-config", @@ -5659,9 +5653,9 @@ dependencies = [ [[package]] name = "wayland-backend" -version = "0.3.7" +version = "0.3.8" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "056535ced7a150d45159d3a8dc30f91a2e2d588ca0b23f70e56033622b8016f6" +checksum = "b7208998eaa3870dad37ec8836979581506e0c5c64c20c9e79e9d2a10d6f47bf" dependencies = [ "cc", "downcast-rs", @@ -5673,9 +5667,9 @@ dependencies = [ [[package]] name = "wayland-client" -version = "0.31.7" +version = "0.31.8" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b66249d3fc69f76fd74c82cc319300faa554e9d865dab1f7cd66cc20db10b280" +checksum = "c2120de3d33638aaef5b9f4472bff75f07c56379cf76ea320bd3a3d65ecaf73f" dependencies = [ "bitflags 2.7.0", "rustix", @@ -5717,9 +5711,9 @@ dependencies = [ [[package]] name = "wayland-protocols" -version = "0.32.5" +version = "0.32.6" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7cd0ade57c4e6e9a8952741325c30bf82f4246885dca8bf561898b86d0c1f58e" +checksum = "0781cf46869b37e36928f7b432273c0995aa8aed9552c556fb18754420541efc" dependencies = [ "bitflags 2.7.0", "wayland-backend", @@ -5770,9 +5764,9 @@ dependencies = [ [[package]] name = "wayland-scanner" -version = "0.31.5" +version = "0.31.6" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "597f2001b2e5fc1121e3d5b9791d3e78f05ba6bfa4641053846248e3a13661c3" +checksum = "896fdafd5d28145fce7958917d69f2fd44469b1d4e861cb5961bcbeebc6d1484" dependencies = [ "proc-macro2", "quick-xml", @@ -5781,13 +5775,12 @@ dependencies = [ [[package]] name = "wayland-server" -version = "0.31.6" +version = "0.31.7" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c89532cc712a2adb119eb4d09694b402576052254d0bb284f82ac1c47fb786ad" +checksum = "97fabd7ed68cff8e7657b8a8a1fbe90cb4a3f0c30d90da4bf179a7a23008a4cb" dependencies = [ "bitflags 2.7.0", "downcast-rs", - "io-lifetimes 2.0.4", "rustix", "wayland-backend", "wayland-scanner", @@ -5795,9 +5788,9 @@ dependencies = [ [[package]] name = "wayland-sys" -version = "0.31.5" +version = "0.31.6" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "efa8ac0d8e8ed3e3b5c9fc92c7881406a268e11555abe36493efabe649a29e09" +checksum = "dbcebb399c77d5aa9fa5db874806ee7b4eba4e73650948e8f93963f128896615" dependencies = [ "dlib", "log", @@ -5965,7 +5958,7 @@ version = "0.1.9" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "cf221c93e13a30d793f7645a0e7762c55d169dbb0a49671918a2319d289b10bb" dependencies = [ - "windows-sys 0.59.0", + "windows-sys 0.48.0", ] [[package]] diff --git a/src/shell/mod.rs b/src/shell/mod.rs index 0e7ff650..825ef351 100644 --- a/src/shell/mod.rs +++ b/src/shell/mod.rs @@ -15,9 +15,7 @@ use cosmic_comp_config::{ workspace::{WorkspaceLayout, WorkspaceMode}, TileBehavior, }; -use cosmic_protocols::workspace::v1::server::zcosmic_workspace_handle_v1::{ - State as WState, TilingState, -}; +use cosmic_protocols::workspace::v1::server::zcosmic_workspace_handle_v1::TilingState; use cosmic_settings_config::shortcuts::action::{Direction, FocusDirection, ResizeDirection}; use cosmic_settings_config::{shortcuts, window_rules::ApplicationException}; use keyframe::{ease, functions::EaseInOutCubic}; @@ -40,7 +38,10 @@ use smithay::{ }, output::Output, reexports::{ - wayland_protocols::ext::session_lock::v1::server::ext_session_lock_v1::ExtSessionLockV1, + wayland_protocols::ext::{ + session_lock::v1::server::ext_session_lock_v1::ExtSessionLockV1, + workspace::v1::server::ext_workspace_handle_v1::State as WState, + }, wayland_server::{protocol::wl_surface::WlSurface, Client}, }, utils::{IsAlive, Logical, Point, Rectangle, Serial, Size}, @@ -350,15 +351,14 @@ fn create_workspace( } else { TilingState::FloatingOnly }, + // TODO Set id for persistent workspaces + None, ) .unwrap(); if active { state.add_workspace_state(&workspace_handle, WState::Active); } - state.set_workspace_capabilities( - &workspace_handle, - [WorkspaceCapabilities::Activate].into_iter(), - ); + state.set_workspace_capabilities(&workspace_handle, WorkspaceCapabilities::Activate); Workspace::new(workspace_handle, output.clone(), tiling, theme.clone()) } @@ -376,12 +376,11 @@ fn move_workspace_to_group( } else { TilingState::FloatingOnly }, + // TODO Set id for persistent workspaces + None, ) .unwrap(); - workspace_state.set_workspace_capabilities( - &workspace.handle, - [WorkspaceCapabilities::Activate].into_iter(), - ); + workspace_state.set_workspace_capabilities(&workspace.handle, WorkspaceCapabilities::Activate); for window in workspace.mapped() { for (surface, _) in window.windows() { toplevel_leave_workspace(&surface, &old_workspace_handle); diff --git a/src/wayland/handlers/xdg_activation.rs b/src/wayland/handlers/xdg_activation.rs index 7240d5c6..2dc70d2f 100644 --- a/src/wayland/handlers/xdg_activation.rs +++ b/src/wayland/handlers/xdg_activation.rs @@ -1,10 +1,12 @@ use crate::{shell::ActivationKey, state::ClientState, utils::prelude::*}; use crate::{state::State, wayland::protocols::workspace::WorkspaceHandle}; -use cosmic_protocols::workspace::v1::server::zcosmic_workspace_handle_v1::State as WState; use smithay::{ delegate_xdg_activation, input::Seat, - reexports::wayland_server::protocol::wl_surface::WlSurface, + reexports::{ + wayland_protocols::ext::workspace::v1::server::ext_workspace_handle_v1::State as WState, + wayland_server::protocol::wl_surface::WlSurface, + }, wayland::xdg_activation::{ XdgActivationHandler, XdgActivationState, XdgActivationToken, XdgActivationTokenData, }, diff --git a/src/wayland/protocols/workspace/cosmic.rs b/src/wayland/protocols/workspace/cosmic.rs index 98d094ae..b19bf211 100644 --- a/src/wayland/protocols/workspace/cosmic.rs +++ b/src/wayland/protocols/workspace/cosmic.rs @@ -1,13 +1,17 @@ // SPDX-License-Identifier: GPL-3.0-only -use smithay::reexports::wayland_server::{ - backend::{ClientData, ClientId}, - Client, DataInit, Dispatch, DisplayHandle, GlobalDispatch, New, Resource, +use smithay::{ + reexports::wayland_protocols::ext::workspace::v1::server::ext_workspace_handle_v1::{self}, + reexports::wayland_server::{ + backend::{ClientData, ClientId}, + Client, DataInit, Dispatch, DisplayHandle, GlobalDispatch, New, Resource, + }, }; use super::{ - Request, Workspace, WorkspaceClientHandler, WorkspaceData, WorkspaceGlobalData, WorkspaceGroup, - WorkspaceGroupData, WorkspaceGroupHandle, WorkspaceHandler, WorkspaceState, + GroupCapabilities, Request, Workspace, WorkspaceCapabilities, WorkspaceClientHandler, + WorkspaceData, WorkspaceGlobalData, WorkspaceGroup, WorkspaceGroupData, WorkspaceGroupHandle, + WorkspaceHandler, WorkspaceState, }; use cosmic_protocols::workspace::v1::server::{ @@ -342,7 +346,11 @@ where let caps = group .capabilities .iter() - .flat_map(|cap| (*cap as u32).to_ne_bytes()) + .filter_map(|cap| match cap { + GroupCapabilities::CreateWorkspace => Some(zcosmic_workspace_group_handle_v1::ZcosmicWorkspaceGroupCapabilitiesV1::CreateWorkspace), + _ => None, + }) + .flat_map(|cap| (cap as u32).to_ne_bytes()) .collect::>(); instance.capabilities(caps); handle_state.capabilities = group.capabilities.clone(); @@ -424,7 +432,25 @@ where let caps = workspace .capabilities .iter() - .flat_map(|cap| (*cap as u32).to_ne_bytes()) + .filter_map(|cap| match cap { + WorkspaceCapabilities::Activate => { + Some(zcosmic_workspace_handle_v1::ZcosmicWorkspaceCapabilitiesV1::Activate) + } + WorkspaceCapabilities::Deactivate => { + Some(zcosmic_workspace_handle_v1::ZcosmicWorkspaceCapabilitiesV1::Deactivate) + } + WorkspaceCapabilities::Remove => { + Some(zcosmic_workspace_handle_v1::ZcosmicWorkspaceCapabilitiesV1::Remove) + } + WorkspaceCapabilities::Rename => { + Some(zcosmic_workspace_handle_v1::ZcosmicWorkspaceCapabilitiesV1::Rename) + } + WorkspaceCapabilities::SetTilingState => Some( + zcosmic_workspace_handle_v1::ZcosmicWorkspaceCapabilitiesV1::SetTilingState, + ), + _ => None, + }) + .flat_map(|cap| (cap as u32).to_ne_bytes()) .collect::>(); instance.capabilities(caps); handle_state.capabilities = workspace.capabilities.clone(); @@ -434,7 +460,19 @@ where let states = workspace .states .iter() - .flat_map(|state| (*state as u32).to_ne_bytes()) + .filter_map(|state| match state { + ext_workspace_handle_v1::State::Active => { + Some(zcosmic_workspace_handle_v1::State::Active) + } + ext_workspace_handle_v1::State::Urgent => { + Some(zcosmic_workspace_handle_v1::State::Urgent) + } + ext_workspace_handle_v1::State::Hidden => { + Some(zcosmic_workspace_handle_v1::State::Hidden) + } + _ => None, + }) + .flat_map(|state| (state as u32).to_ne_bytes()) .collect::>(); instance.state(states); handle_state.states = workspace.states.clone(); diff --git a/src/wayland/protocols/workspace/ext.rs b/src/wayland/protocols/workspace/ext.rs new file mode 100644 index 00000000..78d2d23a --- /dev/null +++ b/src/wayland/protocols/workspace/ext.rs @@ -0,0 +1,454 @@ +// SPDX-License-Identifier: GPL-3.0-only + +use smithay::reexports::wayland_server::{ + backend::{ClientData, ClientId}, + Client, DataInit, Dispatch, DisplayHandle, GlobalDispatch, New, Resource, +}; + +use super::{ + Request, Workspace, WorkspaceCapabilities, WorkspaceClientHandler, WorkspaceData, + WorkspaceGlobalData, WorkspaceGroup, WorkspaceGroupData, WorkspaceGroupHandle, + WorkspaceHandler, WorkspaceState, +}; + +use smithay::reexports::wayland_protocols::ext::workspace::v1::server::{ + ext_workspace_group_handle_v1::{self, ExtWorkspaceGroupHandleV1}, + ext_workspace_handle_v1::{self, ExtWorkspaceHandleV1}, + ext_workspace_manager_v1::{self, ExtWorkspaceManagerV1}, +}; + +impl GlobalDispatch for WorkspaceState +where + D: GlobalDispatch + + Dispatch + + Dispatch + + Dispatch + + WorkspaceHandler + + 'static, + ::Client: ClientData + WorkspaceClientHandler + 'static, +{ + fn bind( + state: &mut D, + dh: &DisplayHandle, + _client: &Client, + resource: New, + _global_data: &WorkspaceGlobalData, + data_init: &mut DataInit<'_, D>, + ) { + let state = state.workspace_state_mut(); + let instance = data_init.init(resource, ()); + for group in &mut state.groups { + send_group_to_client::(dh, &instance, group); + } + instance.done(); + state.ext_instances.push(instance); + } + + fn can_view(client: Client, global_data: &WorkspaceGlobalData) -> bool { + (global_data.filter)(&client) + } +} + +impl Dispatch for WorkspaceState +where + D: GlobalDispatch + + Dispatch + + Dispatch + + Dispatch + + WorkspaceHandler + + 'static, + ::Client: ClientData + WorkspaceClientHandler + 'static, +{ + fn request( + state: &mut D, + client: &Client, + obj: &ExtWorkspaceManagerV1, + request: ext_workspace_manager_v1::Request, + _data: &(), + dh: &DisplayHandle, + _data_init: &mut DataInit<'_, D>, + ) { + match request { + ext_workspace_manager_v1::Request::Commit => { + if state.workspace_state().ext_instances.contains(obj) { + let mut client_state = client + .get_data::<::Client>() + .unwrap() + .workspace_state() + .lock() + .unwrap(); + state.commit_requests(dh, std::mem::take(&mut client_state.requests)); + } + } + ext_workspace_manager_v1::Request::Stop => { + state + .workspace_state_mut() + .ext_instances + .retain(|i| i != obj); + // without an instance, the whole send_group_to_client machinery doesn't work + // so there is no way for the whole clients hierachy to get any new events + } + _ => {} + } + } + + fn destroyed(state: &mut D, _client: ClientId, resource: &ExtWorkspaceManagerV1, _data: &()) { + state + .workspace_state_mut() + .ext_instances + .retain(|i| i != resource); + } +} + +impl Dispatch for WorkspaceState +where + D: GlobalDispatch + + Dispatch + + Dispatch + + Dispatch + + WorkspaceHandler + + 'static, + ::Client: ClientData + WorkspaceClientHandler + 'static, +{ + fn request( + state: &mut D, + client: &Client, + obj: &ExtWorkspaceGroupHandleV1, + request: ext_workspace_group_handle_v1::Request, + _data: &WorkspaceGroupData, + _dh: &DisplayHandle, + _data_init: &mut DataInit<'_, D>, + ) { + match request { + ext_workspace_group_handle_v1::Request::CreateWorkspace { workspace } => { + if let Some(id) = state + .workspace_state() + .groups + .iter() + .find(|g| g.ext_instances.contains(obj)) + .map(|g| g.id) + { + let mut state = client + .get_data::<::Client>() + .unwrap() + .workspace_state() + .lock() + .unwrap(); + state.requests.push(Request::Create { + in_group: WorkspaceGroupHandle { id }, + name: workspace, + }); + } + } + ext_workspace_group_handle_v1::Request::Destroy => { + for group in &mut state.workspace_state_mut().groups { + group.ext_instances.retain(|i| i != obj) + } + } + _ => {} + } + } + + fn destroyed( + state: &mut D, + _client: ClientId, + resource: &ExtWorkspaceGroupHandleV1, + _data: &WorkspaceGroupData, + ) { + for group in &mut state.workspace_state_mut().groups { + group.ext_instances.retain(|i| i != resource) + } + } +} + +impl Dispatch for WorkspaceState +where + D: GlobalDispatch + + Dispatch + + Dispatch + + Dispatch + + WorkspaceHandler + + 'static, + ::Client: ClientData + WorkspaceClientHandler + 'static, +{ + fn request( + state: &mut D, + client: &Client, + obj: &ExtWorkspaceHandleV1, + request: ext_workspace_handle_v1::Request, + _data: &WorkspaceData, + _dh: &DisplayHandle, + _data_init: &mut DataInit<'_, D>, + ) { + match request { + ext_workspace_handle_v1::Request::Activate => { + if let Some(workspace_handle) = + state.workspace_state().get_ext_workspace_handle(obj) + { + let mut state = client + .get_data::<::Client>() + .unwrap() + .workspace_state() + .lock() + .unwrap(); + state.requests.push(Request::Activate(workspace_handle)); + } + } + ext_workspace_handle_v1::Request::Deactivate => { + if let Some(workspace_handle) = + state.workspace_state().get_ext_workspace_handle(obj) + { + let mut state = client + .get_data::<::Client>() + .unwrap() + .workspace_state() + .lock() + .unwrap(); + state.requests.push(Request::Deactivate(workspace_handle)); + } + } + ext_workspace_handle_v1::Request::Remove => { + if let Some(workspace_handle) = + state.workspace_state().get_ext_workspace_handle(obj) + { + let mut state = client + .get_data::<::Client>() + .unwrap() + .workspace_state() + .lock() + .unwrap(); + state.requests.push(Request::Remove(workspace_handle)); + } + } + ext_workspace_handle_v1::Request::Assign { workspace_group } => { + if let Some(workspace_handle) = + state.workspace_state().get_ext_workspace_handle(obj) + { + if let Some(group_id) = state + .workspace_state() + .groups + .iter() + .find(|g| g.ext_instances.contains(&workspace_group)) + .map(|g| g.id) + { + let mut state = client + .get_data::<::Client>() + .unwrap() + .workspace_state() + .lock() + .unwrap(); + state.requests.push(Request::Assign { + workspace: workspace_handle, + group: WorkspaceGroupHandle { id: group_id }, + }); + } + } + } + ext_workspace_handle_v1::Request::Destroy => { + for group in &mut state.workspace_state_mut().groups { + for workspace in &mut group.workspaces { + workspace.ext_instances.retain(|i| i != obj) + } + } + } + _ => {} + } + } + + fn destroyed( + state: &mut D, + _client: ClientId, + resource: &ExtWorkspaceHandleV1, + _data: &WorkspaceData, + ) { + for group in &mut state.workspace_state_mut().groups { + for workspace in &mut group.workspaces { + workspace.ext_instances.retain(|i| i != resource) + } + } + } +} + +pub(super) fn send_group_to_client( + dh: &DisplayHandle, + mngr: &ExtWorkspaceManagerV1, + group: &mut WorkspaceGroup, +) -> bool +where + D: GlobalDispatch + + Dispatch + + Dispatch + + Dispatch + + WorkspaceHandler + + 'static, + ::Client: ClientData + WorkspaceClientHandler + 'static, +{ + let instance = match group + .ext_instances + .iter_mut() + .find(|i| i.id().same_client_as(&mngr.id())) + { + Some(i) => i, + None => { + if let Ok(client) = dh.get_client(mngr.id()) { + if let Ok(handle) = client.create_resource::( + dh, + mngr.version(), + WorkspaceGroupData::default(), + ) { + mngr.workspace_group(&handle); + group.ext_instances.push(handle); + group.ext_instances.last_mut().unwrap() + } else { + return false; + } + } else { + return false; + } + } + }; + + let mut handle_state = instance + .data::() + .unwrap() + .lock() + .unwrap(); + let mut changed = false; + if let Ok(client) = dh.get_client(instance.id()) { + for output in &group.outputs { + for wl_output in output.client_outputs(&client) { + if handle_state.wl_outputs.insert(wl_output.clone()) { + instance.output_enter(&wl_output); + changed = true; + } + } + } + + handle_state.wl_outputs.retain(|wl_output| { + let retain = + wl_output.is_alive() && group.outputs.iter().any(|output| output.owns(wl_output)); + if !retain { + instance.output_leave(&wl_output); + changed = true; + } + retain + }); + + handle_state.outputs = group.outputs.clone(); + } + + if handle_state.capabilities != group.capabilities { + instance.capabilities(group.capabilities); + handle_state.capabilities = group.capabilities.clone(); + changed = true; + } + + if handle_state.workspace_count != group.workspaces.len() { + changed = true; + } + handle_state.workspace_count = group.workspaces.len(); + + for workspace in &mut group.workspaces { + if send_workspace_to_client::(dh, mngr, instance, workspace) { + changed = true; + } + } + + changed +} + +fn send_workspace_to_client( + dh: &DisplayHandle, + mngr: &ExtWorkspaceManagerV1, + group: &ExtWorkspaceGroupHandleV1, + workspace: &mut Workspace, +) -> bool +where + D: GlobalDispatch + + Dispatch + + Dispatch + + Dispatch + + WorkspaceHandler + + 'static, + ::Client: ClientData + WorkspaceClientHandler + 'static, +{ + let instance = match workspace + .ext_instances + .iter_mut() + .find(|i| Resource::id(*i).same_client_as(&mngr.id())) + { + Some(i) => i, + None => { + if let Ok(client) = dh.get_client(mngr.id()) { + if let Ok(handle) = client.create_resource::( + dh, + mngr.version(), + WorkspaceData::default(), + ) { + mngr.workspace(&handle); + group.workspace_enter(&handle); + if let Some(id) = workspace.ext_id.clone() { + handle.id(id); + } + workspace.ext_instances.push(handle); + workspace.ext_instances.last_mut().unwrap() + } else { + return false; + } + } else { + return false; + } + } + }; + + let mut handle_state = instance.data::().unwrap().lock().unwrap(); + let mut changed = false; + + if handle_state.name != workspace.name { + instance.name(workspace.name.clone()); + handle_state.name = workspace.name.clone(); + changed = true; + } + if handle_state.coordinates != workspace.coordinates { + let coords = workspace + .coordinates + .iter() + .flat_map(|coord| coord.to_ne_bytes()) + .collect::>(); + instance.coordinates(coords); + handle_state.coordinates = workspace.coordinates.clone(); + changed = true; + } + if handle_state.capabilities != workspace.capabilities { + let caps = workspace + .capabilities + .iter() + .filter_map(|cap| match cap { + WorkspaceCapabilities::Activate => { + Some(ext_workspace_handle_v1::WorkspaceCapabilities::Activate) + } + WorkspaceCapabilities::Deactivate => { + Some(ext_workspace_handle_v1::WorkspaceCapabilities::Deactivate) + } + WorkspaceCapabilities::Remove => { + Some(ext_workspace_handle_v1::WorkspaceCapabilities::Remove) + } + WorkspaceCapabilities::Assign => { + Some(ext_workspace_handle_v1::WorkspaceCapabilities::Assign) + } + _ => None, + }) + .collect::(); + instance.capabilities(caps); + handle_state.capabilities = workspace.capabilities.clone(); + changed = true; + } + if handle_state.states != workspace.states { + instance.state(workspace.states); + handle_state.states = workspace.states.clone(); + changed = true; + } + // TODO ext_workspace_handle_v1::id + + changed +} diff --git a/src/wayland/protocols/workspace/mod.rs b/src/wayland/protocols/workspace/mod.rs index 03890b92..07d5c5ed 100644 --- a/src/wayland/protocols/workspace/mod.rs +++ b/src/wayland/protocols/workspace/mod.rs @@ -4,10 +4,17 @@ use std::{collections::HashSet, sync::Mutex}; use smithay::{ output::Output, - reexports::wayland_server::{ - backend::{ClientData, GlobalId, ObjectId}, - protocol::wl_output::WlOutput, - Client, Dispatch, DisplayHandle, GlobalDispatch, Resource, + reexports::{ + wayland_protocols::ext::workspace::v1::server::{ + ext_workspace_group_handle_v1::ExtWorkspaceGroupHandleV1, + ext_workspace_handle_v1::{self, ExtWorkspaceHandleV1}, + ext_workspace_manager_v1::ExtWorkspaceManagerV1, + }, + wayland_server::{ + backend::{ClientData, GlobalId, ObjectId}, + protocol::wl_output::WlOutput, + Client, Dispatch, DisplayHandle, GlobalDispatch, Resource, + }, }, }; use wayland_backend::protocol::WEnum; @@ -18,12 +25,25 @@ use cosmic_protocols::workspace::v1::server::{ zcosmic_workspace_manager_v1::ZcosmicWorkspaceManagerV1, }; -pub use cosmic_protocols::workspace::v1::server::{ - zcosmic_workspace_group_handle_v1::ZcosmicWorkspaceGroupCapabilitiesV1 as GroupCapabilities, - zcosmic_workspace_handle_v1::ZcosmicWorkspaceCapabilitiesV1 as WorkspaceCapabilities, -}; - mod cosmic; +mod ext; + +pub use smithay::reexports::wayland_protocols::ext::workspace::v1::server::ext_workspace_group_handle_v1::GroupCapabilities; + +bitflags::bitflags! { + #[derive(Clone, Copy, Debug, Eq, Hash, PartialEq)] + pub struct WorkspaceCapabilities: u32 { + const Activate = 1; + const Deactivate = 2; + const Remove = 4; + /// not in legacy cosmic protocol + const Assign = 8; + /// cosmic specific + const Rename = 16; + /// cosmic specific + const SetTilingState = 32; + } +} #[derive(Debug)] pub struct WorkspaceState @@ -32,13 +52,19 @@ where + Dispatch + Dispatch + Dispatch + + GlobalDispatch + + Dispatch + + Dispatch + + Dispatch + WorkspaceHandler + 'static, ::Client: ClientData + WorkspaceClientHandler + 'static, { dh: DisplayHandle, - global: GlobalId, + cosmic_global: GlobalId, + ext_global: GlobalId, instances: Vec, + ext_instances: Vec, groups: Vec, _marker: std::marker::PhantomData, } @@ -55,14 +81,29 @@ where crate::utils::id_gen!(next_group_id, GROUP_ID, GROUP_IDS); crate::utils::id_gen!(next_workspace_id, WORKSPACE_ID, WORKSPACE_IDS); -#[derive(Debug, Default)] +#[derive(Debug)] pub struct WorkspaceGroup { id: usize, instances: Vec, + ext_instances: Vec, workspaces: Vec, outputs: Vec, - capabilities: Vec, + capabilities: GroupCapabilities, +} + +impl Default for WorkspaceGroup { + fn default() -> Self { + Self { + id: 0, + instances: Vec::new(), + ext_instances: Vec::new(), + workspaces: Vec::new(), + + outputs: Vec::new(), + capabilities: GroupCapabilities::empty(), + } + } } #[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)] @@ -70,25 +111,38 @@ pub struct WorkspaceGroupHandle { id: usize, } -#[derive(Default)] pub struct WorkspaceGroupDataInner { outputs: Vec, wl_outputs: HashSet, - capabilities: Vec, + capabilities: GroupCapabilities, workspace_count: usize, } + +impl Default for WorkspaceGroupDataInner { + fn default() -> Self { + Self { + outputs: Vec::new(), + wl_outputs: HashSet::new(), + capabilities: GroupCapabilities::empty(), + workspace_count: 0, + } + } +} + pub type WorkspaceGroupData = Mutex; #[derive(Debug)] pub struct Workspace { id: usize, instances: Vec, + ext_instances: Vec, name: String, - capabilities: Vec, + capabilities: WorkspaceCapabilities, coordinates: Vec, - states: HashSet, + states: ext_workspace_handle_v1::State, tiling: zcosmic_workspace_handle_v1::TilingState, + ext_id: Option, } #[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)] @@ -96,14 +150,26 @@ pub struct WorkspaceHandle { id: usize, } -#[derive(Default)] pub struct WorkspaceDataInner { name: String, - capabilities: Vec, + capabilities: WorkspaceCapabilities, coordinates: Vec, - states: HashSet, + states: ext_workspace_handle_v1::State, tiling: Option, } + +impl Default for WorkspaceDataInner { + fn default() -> Self { + Self { + name: String::new(), + capabilities: WorkspaceCapabilities::empty(), + coordinates: Vec::new(), + states: ext_workspace_handle_v1::State::empty(), + tiling: None, + } + } +} + pub type WorkspaceData = Mutex; pub trait WorkspaceHandler @@ -112,6 +178,10 @@ where + Dispatch + Dispatch + Dispatch + + GlobalDispatch + + Dispatch + + Dispatch + + Dispatch + Sized + 'static, { @@ -143,6 +213,10 @@ pub enum Request { in_group: WorkspaceGroupHandle, name: String, }, + Assign { + workspace: WorkspaceHandle, + group: WorkspaceGroupHandle, + }, } #[derive(Debug, Default)] @@ -161,16 +235,27 @@ where + Dispatch + Dispatch + Dispatch + + GlobalDispatch + + Dispatch + + Dispatch + + Dispatch + WorkspaceHandler + 'static, ::Client: ClientData + WorkspaceClientHandler + 'static, { pub fn new(dh: &DisplayHandle, client_filter: F) -> WorkspaceState where - F: for<'a> Fn(&'a Client) -> bool + Send + Sync + 'static, + F: for<'a> Fn(&'a Client) -> bool + Clone + Send + Sync + 'static, { - let global = dh.create_global::( + let cosmic_global = dh.create_global::( 2, + WorkspaceGlobalData { + filter: Box::new(client_filter.clone()), + }, + ); + + let ext_global = dh.create_global::( + 1, WorkspaceGlobalData { filter: Box::new(client_filter), }, @@ -178,8 +263,10 @@ where WorkspaceState { dh: dh.clone(), - global, + cosmic_global, + ext_global, instances: Vec::new(), + ext_instances: Vec::new(), groups: Vec::new(), _marker: std::marker::PhantomData, } @@ -197,14 +284,8 @@ where } } - pub fn group_capabilities( - &self, - group: &WorkspaceGroupHandle, - ) -> Option> { - self.groups - .iter() - .find(|g| g.id == group.id) - .map(|g| g.capabilities.iter()) + pub fn group_capabilities(&self, group: &WorkspaceGroupHandle) -> Option { + Some(self.groups.iter().find(|g| g.id == group.id)?.capabilities) } pub fn group_outputs( @@ -220,12 +301,14 @@ where pub fn workspace_capabilities( &self, workspace: &WorkspaceHandle, - ) -> Option> { + ) -> Option { self.groups.iter().find_map(|g| { - g.workspaces - .iter() - .find(|w| w.id == workspace.id) - .map(|w| w.capabilities.iter()) + Some( + g.workspaces + .iter() + .find(|w| w.id == workspace.id)? + .capabilities, + ) }) } @@ -250,13 +333,10 @@ where pub fn workspace_states( &self, workspace: &WorkspaceHandle, - ) -> Option> { - self.groups.iter().find_map(|g| { - g.workspaces - .iter() - .find(|w| w.id == workspace.id) - .map(|w| w.states.iter()) - }) + ) -> Option { + self.groups + .iter() + .find_map(|g| Some(g.workspaces.iter().find(|w| w.id == workspace.id)?.states)) } pub fn workspace_tiling_state( @@ -330,10 +410,20 @@ where } } } + for instance in &self.ext_instances { + for mut group in &mut self.groups { + if ext::send_group_to_client::(&self.dh, instance, &mut group) { + changed = true; + } + } + } if changed { for instance in &self.instances { instance.done(); } + for instance in &self.ext_instances { + instance.done(); + } } } @@ -347,8 +437,26 @@ where .map(|w| WorkspaceHandle { id: w.id }) } - pub fn global_id(&self) -> GlobalId { - self.global.clone() + pub fn get_ext_workspace_handle( + &self, + handle: &ExtWorkspaceHandleV1, + ) -> Option { + self.groups + .iter() + .find_map(|g| { + g.workspaces + .iter() + .find(|w| w.ext_instances.contains(handle)) + }) + .map(|w| WorkspaceHandle { id: w.id }) + } + + pub fn cosmic_global_id(&self) -> GlobalId { + self.cosmic_global.clone() + } + + pub fn ext_global_id(&self) -> GlobalId { + self.ext_global.clone() } } @@ -358,6 +466,9 @@ where + Dispatch + Dispatch + Dispatch + + Dispatch + + Dispatch + + Dispatch + WorkspaceHandler + 'static, ::Client: ClientData + WorkspaceClientHandler + 'static, @@ -376,6 +487,7 @@ where &mut self, group: &WorkspaceGroupHandle, tiling: zcosmic_workspace_handle_v1::TilingState, + ext_id: Option, ) -> Option { if let Some(group) = self.0.groups.iter_mut().find(|g| g.id == group.id) { let id = next_workspace_id(); @@ -383,10 +495,12 @@ where id, tiling, instances: Default::default(), + ext_instances: Default::default(), name: Default::default(), - capabilities: Default::default(), + capabilities: WorkspaceCapabilities::empty(), coordinates: Default::default(), - states: Default::default(), + states: ext_workspace_handle_v1::State::empty(), + ext_id, }; group.workspaces.push(workspace); Some(WorkspaceHandle { id }) @@ -411,7 +525,10 @@ where if let Some(group) = self.0.groups.iter().find(|g| g.id == group.id) { for instance in &group.instances { - instance.remove() + instance.remove(); + } + for instance in &group.ext_instances { + instance.removed(); } } self.0.groups.retain(|g| g.id != group.id); @@ -424,6 +541,13 @@ where for instance in &workspace.instances { instance.remove(); } + for instance in &workspace.ext_instances { + // TODO remove only if it matches the group + for group_instance in &group.ext_instances { + group_instance.workspace_leave(instance); + } + instance.removed(); + } } group.workspaces.retain(|w| w.id != workspace.id); } @@ -441,17 +565,17 @@ where pub fn group_capabilities( &mut self, group: &WorkspaceGroupHandle, - ) -> Option> { + ) -> Option { self.0.group_capabilities(group) } pub fn set_group_capabilities( &mut self, group: &WorkspaceGroupHandle, - capabilities: impl Iterator, + capabilities: GroupCapabilities, ) { if let Some(group) = self.0.groups.iter_mut().find(|g| g.id == group.id) { - group.capabilities = capabilities.collect(); + group.capabilities = capabilities; } } @@ -477,14 +601,14 @@ where pub fn workspace_capabilities( &self, workspace: &WorkspaceHandle, - ) -> Option> { + ) -> Option { self.0.workspace_capabilities(workspace) } pub fn set_workspace_capabilities( &mut self, workspace: &WorkspaceHandle, - capabilities: impl Iterator, + capabilities: WorkspaceCapabilities, ) { if let Some(workspace) = self .0 @@ -492,7 +616,7 @@ where .iter_mut() .find_map(|g| g.workspaces.iter_mut().find(|w| w.id == workspace.id)) { - workspace.capabilities = capabilities.collect(); + workspace.capabilities = capabilities; } } @@ -537,14 +661,14 @@ where pub fn workspace_states( &self, workspace: &WorkspaceHandle, - ) -> Option> { + ) -> Option { self.0.workspace_states(workspace) } pub fn add_workspace_state( &mut self, workspace: &WorkspaceHandle, - state: zcosmic_workspace_handle_v1::State, + state: ext_workspace_handle_v1::State, ) { if let Some(workspace) = self .0 @@ -559,7 +683,7 @@ where pub fn remove_workspace_state( &mut self, workspace: &WorkspaceHandle, - state: zcosmic_workspace_handle_v1::State, + state: ext_workspace_handle_v1::State, ) { if let Some(workspace) = self .0 @@ -567,7 +691,7 @@ where .iter_mut() .find_map(|g| g.workspaces.iter_mut().find(|w| w.id == workspace.id)) { - workspace.states.remove(&state); + workspace.states.remove(state); } } @@ -600,6 +724,10 @@ where + Dispatch + Dispatch + Dispatch + + GlobalDispatch + + Dispatch + + Dispatch + + Dispatch + WorkspaceHandler + 'static, ::Client: ClientData + WorkspaceClientHandler + 'static, @@ -623,6 +751,19 @@ macro_rules! delegate_workspace { smithay::reexports::wayland_server::delegate_dispatch!($(@< $( $lt $( : $clt $(+ $dlt )* )? ),+ >)? $ty: [ cosmic_protocols::workspace::v1::server::zcosmic_workspace_handle_v1::ZcosmicWorkspaceHandleV1: $crate::wayland::protocols::workspace::WorkspaceData ] => $crate::wayland::protocols::workspace::WorkspaceState); + + smithay::reexports::wayland_server::delegate_global_dispatch!($(@< $( $lt $( : $clt $(+ $dlt )* )? ),+ >)? $ty: [ + smithay::reexports::wayland_protocols::ext::workspace::v1::server::ext_workspace_manager_v1::ExtWorkspaceManagerV1: $crate::wayland::protocols::workspace::WorkspaceGlobalData + ] => $crate::wayland::protocols::workspace::WorkspaceState); + smithay::reexports::wayland_server::delegate_dispatch!($(@< $( $lt $( : $clt $(+ $dlt )* )? ),+ >)? $ty: [ + smithay::reexports::wayland_protocols::ext::workspace::v1::server::ext_workspace_manager_v1::ExtWorkspaceManagerV1: () + ] => $crate::wayland::protocols::workspace::WorkspaceState); + smithay::reexports::wayland_server::delegate_dispatch!($(@< $( $lt $( : $clt $(+ $dlt )* )? ),+ >)? $ty: [ + smithay::reexports::wayland_protocols::ext::workspace::v1::server::ext_workspace_group_handle_v1::ExtWorkspaceGroupHandleV1: $crate::wayland::protocols::workspace::WorkspaceGroupData + ] => $crate::wayland::protocols::workspace::WorkspaceState); + smithay::reexports::wayland_server::delegate_dispatch!($(@< $( $lt $( : $clt $(+ $dlt )* )? ),+ >)? $ty: [ + smithay::reexports::wayland_protocols::ext::workspace::v1::server::ext_workspace_handle_v1::ExtWorkspaceHandleV1: $crate::wayland::protocols::workspace::WorkspaceData + ] => $crate::wayland::protocols::workspace::WorkspaceState); }; } pub(crate) use delegate_workspace;