Update to workspace v2, based on ext-workspace

Requires https://github.com/pop-os/cosmic-protocols/pull/51,
https://github.com/pop-os/cosmic-comp/pull/1225.

The new protocol version is a hard requirement, and this will panic
without it.
This commit is contained in:
Ian Douglas Scott 2025-02-20 14:28:56 -08:00 committed by Ian Douglas Scott
parent 7f877f72a3
commit 6b6bf454db
11 changed files with 63 additions and 68 deletions

View file

@ -3,14 +3,12 @@
use cosmic::{
cctk::{
cosmic_protocols::{
toplevel_info::v1::client::zcosmic_toplevel_handle_v1,
workspace::v1::client::zcosmic_workspace_handle_v1,
},
cosmic_protocols::toplevel_info::v1::client::zcosmic_toplevel_handle_v1,
wayland_client::{
protocol::{wl_output, wl_shm},
Connection, WEnum,
},
wayland_protocols::ext::workspace::v1::client::ext_workspace_handle_v1,
},
iced::{
self,
@ -80,9 +78,9 @@ impl MockObjectId {
}
#[derive(Eq, PartialEq, Clone, Debug, Hash)]
pub struct ZcosmicWorkspaceHandleV1(MockObjectId);
pub struct ExtWorkspaceHandleV1(MockObjectId);
impl ZcosmicWorkspaceHandleV1 {
impl ExtWorkspaceHandleV1 {
pub fn id(&self) -> MockObjectId {
self.0.clone()
}
@ -91,27 +89,21 @@ impl ZcosmicWorkspaceHandleV1 {
#[derive(Eq, PartialEq, Clone, Debug, Hash)]
pub struct ExtForeignToplevelHandleV1(MockObjectId);
#[derive(Clone, Debug, Default)]
pub struct CaptureFilter {
pub workspaces_on_outputs: Vec<wl_output::WlOutput>,
pub toplevels_on_workspaces: Vec<ZcosmicWorkspaceHandleV1>,
}
#[derive(Clone, Debug, Default)]
pub struct ToplevelInfo {
pub title: String,
pub app_id: String,
pub state: HashSet<zcosmic_toplevel_handle_v1::State>,
pub output: HashSet<wl_output::WlOutput>,
pub workspace: HashSet<ZcosmicWorkspaceHandleV1>,
pub workspace: HashSet<ExtWorkspaceHandleV1>,
}
#[derive(Clone, Debug)]
pub struct Workspace {
pub handle: ZcosmicWorkspaceHandleV1,
pub handle: ExtWorkspaceHandleV1,
pub name: String,
// pub coordinates: Vec<u32>,
pub state: Vec<WEnum<zcosmic_workspace_handle_v1::State>>,
pub state: ext_workspace_handle_v1::State,
// pub capabilities: Vec<WEnum<zcosmic_workspace_handle_v1::ZcosmicWorkspaceCapabilitiesV1>>,
// pub tiling: Option<WEnum<zcosmic_workspace_handle_v1::TilingState>>,
}
@ -135,14 +127,14 @@ impl AppData {
// Add four workspaces for each output
let mut new_workspaces = Vec::new();
for i in 0..=4 {
let workspace_handle = ZcosmicWorkspaceHandleV1(MockObjectId::new());
let workspace_handle = ExtWorkspaceHandleV1(MockObjectId::new());
let workspace = Workspace {
handle: workspace_handle.clone(),
name: format!("Workspace {i}"),
state: if i == 0 {
vec![WEnum::Value(zcosmic_workspace_handle_v1::State::Active)]
ext_workspace_handle_v1::State::Active
} else {
Vec::new()
ext_workspace_handle_v1::State::empty()
},
};
// Add three toplevels for each workspace

View file

@ -17,12 +17,12 @@ use std::collections::HashSet;
#[cfg(not(feature = "mock-backend"))]
mod wayland;
#[cfg(not(feature = "mock-backend"))]
pub use cosmic::cctk::{
cosmic_protocols::workspace::v1::client::zcosmic_workspace_handle_v1::ZcosmicWorkspaceHandleV1,
toplevel_info::ToplevelInfo, workspace::Workspace,
};
pub use cosmic::cctk::{toplevel_info::ToplevelInfo, workspace::Workspace};
#[cfg(not(feature = "mock-backend"))]
pub use wayland_protocols::ext::foreign_toplevel_list::v1::client::ext_foreign_toplevel_handle_v1::ExtForeignToplevelHandleV1;
pub use wayland_protocols::ext::{
foreign_toplevel_list::v1::client::ext_foreign_toplevel_handle_v1::ExtForeignToplevelHandleV1,
workspace::v1::client::ext_workspace_handle_v1::ExtWorkspaceHandleV1,
};
#[cfg(not(feature = "mock-backend"))]
pub use wayland::subscription;
@ -32,13 +32,13 @@ pub use wayland::subscription;
mod mock;
#[cfg(feature = "mock-backend")]
pub use mock::{
subscription, ExtForeignToplevelHandleV1, ToplevelInfo, Workspace, ZcosmicWorkspaceHandleV1,
subscription, ExtForeignToplevelHandleV1, ExtWorkspaceHandleV1, ToplevelInfo, Workspace,
};
#[derive(Clone, Debug, Default)]
pub struct CaptureFilter {
pub workspaces_on_outputs: Vec<wl_output::WlOutput>,
pub toplevels_on_workspaces: Vec<ZcosmicWorkspaceHandleV1>,
pub toplevels_on_workspaces: Vec<ExtWorkspaceHandleV1>,
}
#[derive(Clone, Debug)]
@ -57,7 +57,7 @@ pub struct CaptureImage {
pub enum Event {
CmdSender(calloop::channel::Sender<Cmd>),
Workspaces(Vec<(HashSet<wl_output::WlOutput>, Workspace)>),
WorkspaceCapture(ZcosmicWorkspaceHandleV1, CaptureImage),
WorkspaceCapture(ExtWorkspaceHandleV1, CaptureImage),
NewToplevel(ExtForeignToplevelHandleV1, ToplevelInfo),
UpdateToplevel(ExtForeignToplevelHandleV1, ToplevelInfo),
CloseToplevel(ExtForeignToplevelHandleV1),
@ -71,8 +71,8 @@ pub enum Cmd {
CloseToplevel(ExtForeignToplevelHandleV1),
MoveToplevelToWorkspace(
ExtForeignToplevelHandleV1,
ZcosmicWorkspaceHandleV1,
ExtWorkspaceHandleV1,
wl_output::WlOutput,
),
ActivateWorkspace(ZcosmicWorkspaceHandleV1),
ActivateWorkspace(ExtWorkspaceHandleV1),
}

View file

@ -94,7 +94,7 @@ impl AppData {
let info = self.toplevel_info_state.info(&toplevel_handle);
if let Some(cosmic_toplevel) = info.and_then(|x| x.cosmic_toplevel.as_ref()) {
if self.toplevel_manager_state.manager.version() >= 2 {
self.toplevel_manager_state.manager.move_to_workspace(
self.toplevel_manager_state.manager.move_to_ext_workspace(
&cosmic_toplevel,
&workspace_handle,
&output,
@ -113,11 +113,11 @@ impl AppData {
fn matches_capture_filter(&self, source: &CaptureSource) -> bool {
match source {
CaptureSource::CosmicToplevel(toplevel) => {
CaptureSource::Toplevel(toplevel) => {
let info = self
.toplevel_info_state
.toplevels()
.find(|info| info.cosmic_toplevel.as_ref() == Some(&toplevel));
.find(|info| info.foreign_toplevel == *toplevel);
if let Some(info) = info {
info.workspace.iter().any(|workspace| {
self.capture_filter
@ -128,18 +128,16 @@ impl AppData {
false
}
}
CaptureSource::CosmicWorkspace(workspace) => self
CaptureSource::Workspace(workspace) => self
.workspace_state
.workspace_groups()
.iter()
.find(|g| g.workspaces.iter().any(|w| w.handle == *workspace))
.find(|g| g.workspaces.iter().any(|w| w == workspace))
.map_or(false, |group| {
self.capture_filter
.workspaces_on_outputs
.iter()
.any(|o| group.outputs.contains(o))
}),
CaptureSource::Toplevel(_) => false,
CaptureSource::Output(_) => false,
}
}

View file

@ -192,19 +192,19 @@ impl ScreencopyHandler for AppData {
),
};
match &capture.source {
CaptureSource::CosmicToplevel(toplevel) => {
CaptureSource::Toplevel(toplevel) => {
let info = self
.toplevel_info_state
.toplevels()
.find(|info| info.cosmic_toplevel.as_ref() == Some(&toplevel));
.find(|info| info.foreign_toplevel == *toplevel);
if let Some(info) = info {
self.send_event(Event::ToplevelCapture(info.foreign_toplevel.clone(), image))
}
}
CaptureSource::CosmicWorkspace(workspace) => {
CaptureSource::Workspace(workspace) => {
self.send_event(Event::WorkspaceCapture(workspace.clone(), image));
}
CaptureSource::Output(_) | CaptureSource::Toplevel(_) => {
CaptureSource::Output(_) => {
unreachable!()
}
};

View file

@ -22,10 +22,9 @@ impl ToplevelInfoHandler for AppData {
toplevel: &ExtForeignToplevelHandleV1,
) {
let info = self.toplevel_info_state.info(toplevel).unwrap();
let cosmic_toplevel = info.cosmic_toplevel.clone().unwrap();
self.send_event(Event::NewToplevel(toplevel.clone(), info.clone()));
self.add_capture_source(CaptureSource::CosmicToplevel(cosmic_toplevel));
self.add_capture_source(CaptureSource::Toplevel(toplevel.clone()));
}
fn update_toplevel(
@ -44,11 +43,9 @@ impl ToplevelInfoHandler for AppData {
_qh: &QueueHandle<Self>,
toplevel: &ExtForeignToplevelHandleV1,
) {
let info = self.toplevel_info_state.info(toplevel).unwrap();
let cosmic_toplevel = info.cosmic_toplevel.clone().unwrap();
self.send_event(Event::CloseToplevel(toplevel.clone()));
self.remove_capture_source(CaptureSource::CosmicToplevel(cosmic_toplevel));
self.remove_capture_source(CaptureSource::Toplevel(toplevel.clone()));
}
}

View file

@ -15,11 +15,13 @@ impl WorkspaceHandler for AppData {
// Handle move to another output
for group in self.workspace_state.workspace_groups() {
for workspace in &group.workspaces {
workspaces.push((group.outputs.iter().cloned().collect(), workspace.clone()));
for workspace_handle in &group.workspaces {
if let Some(workspace) = self.workspace_state.workspace_info(workspace_handle) {
workspaces.push((group.outputs.iter().cloned().collect(), workspace.clone()));
// TODO one capture per output on workspace?
self.add_capture_source(CaptureSource::CosmicWorkspace(workspace.handle.clone()));
// TODO one capture per output on workspace?
self.add_capture_source(CaptureSource::Workspace(workspace_handle.clone()));
}
}
}

View file

@ -6,7 +6,7 @@ use cosmic::{
};
use std::{borrow::Cow, sync::LazyLock};
use crate::backend::{ExtForeignToplevelHandleV1, ZcosmicWorkspaceHandleV1};
use crate::backend::{ExtForeignToplevelHandleV1, ExtWorkspaceHandleV1};
// Include `pid` in mime. Want to drag between our surfaces, but not another
// process, if we use Wayland object ids.
@ -19,7 +19,7 @@ static TOPLEVEL_MIME: LazyLock<String> =
#[derive(Clone, Debug)]
pub enum DragSurface {
#[allow(dead_code)]
Workspace(ZcosmicWorkspaceHandleV1),
Workspace(ExtWorkspaceHandleV1),
Toplevel(ExtForeignToplevelHandleV1),
}
@ -95,8 +95,8 @@ impl TryFrom<(Vec<u8>, std::string::String)> for DragWorkspace {
#[derive(Clone, Debug, PartialEq)]
#[repr(u8)]
pub enum DropTarget {
WorkspaceSidebarEntry(ZcosmicWorkspaceHandleV1, wl_output::WlOutput),
OutputToplevels(ZcosmicWorkspaceHandleV1, wl_output::WlOutput),
WorkspaceSidebarEntry(ExtWorkspaceHandleV1, wl_output::WlOutput),
OutputToplevels(ExtWorkspaceHandleV1, wl_output::WlOutput),
#[allow(dead_code)]
WorkspacesBar(wl_output::WlOutput),
}

View file

@ -4,9 +4,9 @@
#![allow(clippy::single_match)]
use cctk::{
cosmic_protocols::workspace::v1::client::zcosmic_workspace_handle_v1,
sctk::shell::wlr_layer::{Anchor, KeyboardInteractivity, Layer},
wayland_client::{protocol::wl_output, Connection, Proxy, WEnum},
wayland_client::{protocol::wl_output, Connection, Proxy},
wayland_protocols::ext::workspace::v1::client::ext_workspace_handle_v1,
};
use clap::Parser;
use cosmic::{
@ -43,7 +43,7 @@ mod desktop_info;
mod localize;
mod backend;
mod view;
use backend::{ExtForeignToplevelHandleV1, ToplevelInfo, ZcosmicWorkspaceHandleV1};
use backend::{ExtForeignToplevelHandleV1, ExtWorkspaceHandleV1, ToplevelInfo};
mod dnd;
mod utils;
mod widgets;
@ -89,9 +89,9 @@ enum Msg {
WaylandEvent(WaylandEvent),
Wayland(backend::Event),
Close,
ActivateWorkspace(ZcosmicWorkspaceHandleV1),
ActivateWorkspace(ExtWorkspaceHandleV1),
#[allow(dead_code)]
CloseWorkspace(ZcosmicWorkspaceHandleV1),
CloseWorkspace(ExtWorkspaceHandleV1),
ActivateToplevel(ExtForeignToplevelHandleV1),
CloseToplevel(ExtForeignToplevelHandleV1),
StartDrag(DragSurface),
@ -118,7 +118,7 @@ struct Workspace {
name: String,
// img_for_output: HashMap<wl_output::WlOutput, backend::CaptureImage>,
img: Option<backend::CaptureImage>,
handle: ZcosmicWorkspaceHandleV1,
handle: ExtWorkspaceHandleV1,
outputs: HashSet<wl_output::WlOutput>,
is_active: bool,
}
@ -170,13 +170,13 @@ struct App {
}
impl App {
fn workspace_for_handle(&self, handle: &ZcosmicWorkspaceHandleV1) -> Option<&Workspace> {
fn workspace_for_handle(&self, handle: &ExtWorkspaceHandleV1) -> Option<&Workspace> {
self.workspaces.iter().find(|i| &i.handle == handle)
}
fn workspace_for_handle_mut(
&mut self,
handle: &ZcosmicWorkspaceHandleV1,
handle: &ExtWorkspaceHandleV1,
) -> Option<&mut Workspace> {
self.workspaces.iter_mut().find(|i| &i.handle == handle)
}
@ -377,13 +377,14 @@ impl Application for App {
backend::Event::CmdSender(sender) => {
self.wayland_cmd_sender = Some(sender);
}
backend::Event::Workspaces(workspaces) => {
backend::Event::Workspaces(mut workspaces) => {
workspaces.sort_by(|(_, w1), (_, w2)| w1.coordinates.cmp(&w2.coordinates));
let old_workspaces = mem::take(&mut self.workspaces);
self.workspaces = Vec::new();
for (outputs, workspace) in workspaces {
let is_active = workspace.state.contains(&WEnum::Value(
zcosmic_workspace_handle_v1::State::Active,
));
let is_active = workspace
.state
.contains(ext_workspace_handle_v1::State::Active);
// XXX efficiency
#[allow(clippy::mutable_key_type)]

View file

@ -236,7 +236,7 @@ fn workspaces_sidebar<'a>(
workspaces: impl Iterator<Item = &'a Workspace>,
output: &'a wl_output::WlOutput,
layout: WorkspaceLayout,
drop_target: Option<&backend::ZcosmicWorkspaceHandleV1>,
drop_target: Option<&backend::ExtWorkspaceHandleV1>,
) -> cosmic::Element<'a, Msg> {
let sidebar_entries = workspaces
.map(|w| workspace_sidebar_entry(w, output, drop_target == Some(&w.handle)))