From 7ba2ed0c53fcd4dd09cbab835b3eed8a1c062785 Mon Sep 17 00:00:00 2001 From: Ian Douglas Scott Date: Thu, 6 Mar 2025 14:13:01 -0800 Subject: [PATCH] Update to workspace v2, based on ext-workspace In the workspace applet, this now uses `Workspace` in the front-end code instead of a tuple with unnamed fields. Handling of scrolling is also moved to the frontend, which uses less code and seems more natural. It would be good to have a helper in libcosmic for this. It also changes `ObjectId` to `ExtWorkspaceHandleV1`, which is a little simpler and I see no reason here to avoid the more strongly typed object. At some point we may want a shared subscription for workspaces in multiple applets. As well as a higher-level abstraction for screen capture. --- Cargo.lock | 15 +- Cargo.toml | 4 +- cosmic-app-list/src/app.rs | 12 +- cosmic-app-list/src/wayland_handler.rs | 24 ++- cosmic-app-list/src/wayland_subscription.rs | 8 +- cosmic-applet-minimize/src/wayland_handler.rs | 9 +- cosmic-applet-tiling/src/wayland.rs | 87 +++++---- .../src/wayland_subscription.rs | 2 +- cosmic-applet-tiling/src/window.rs | 2 +- .../src/components/app.rs | 122 +++++++++---- cosmic-applet-workspaces/src/wayland.rs | 170 +++--------------- .../src/wayland_subscription.rs | 10 +- 12 files changed, 196 insertions(+), 269 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index c754fca3..e5acc3e9 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -1395,8 +1395,9 @@ dependencies = [ [[package]] name = "cosmic-client-toolkit" version = "0.1.0" -source = "git+https://github.com/pop-os//cosmic-protocols?rev=ed2a481#ed2a48143cd6dc63274aa04461de3d341e50b16b" +source = "git+https://github.com/pop-os/cosmic-protocols//?branch=main#6b05c2a157118979cb472a38455ba78ca9729196" dependencies = [ + "bitflags 2.8.0", "cosmic-protocols", "libc", "smithay-client-toolkit", @@ -1534,7 +1535,7 @@ dependencies = [ [[package]] name = "cosmic-protocols" version = "0.1.0" -source = "git+https://github.com/pop-os//cosmic-protocols?rev=ed2a481#ed2a48143cd6dc63274aa04461de3d341e50b16b" +source = "git+https://github.com/pop-os/cosmic-protocols//?branch=main#6b05c2a157118979cb472a38455ba78ca9729196" dependencies = [ "bitflags 2.8.0", "wayland-backend", @@ -2148,7 +2149,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]] @@ -3946,7 +3947,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]] @@ -5530,7 +5531,7 @@ dependencies = [ "errno", "libc", "linux-raw-sys 0.4.15", - "windows-sys 0.59.0", + "windows-sys 0.52.0", ] [[package]] @@ -6055,7 +6056,7 @@ dependencies = [ "getrandom 0.3.1", "once_cell", "rustix 0.38.44", - "windows-sys 0.59.0", + "windows-sys 0.52.0", ] [[package]] @@ -6988,7 +6989,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/Cargo.toml b/Cargo.toml index ef8dbd0b..9970d1bd 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -86,5 +86,5 @@ ignored = ["libcosmic"] sctk = { package = "smithay-client-toolkit", version = "=0.19.2" } [patch."https://github.com/pop-os/cosmic-protocols"] -cctk = { git = "https://github.com/pop-os//cosmic-protocols", package = "cosmic-client-toolkit", rev = "ed2a481" } -cosmic-protocols = { git = "https://github.com/pop-os//cosmic-protocols", rev = "ed2a481" } +cosmic-protocols = { git = "https://github.com/pop-os/cosmic-protocols//", branch = "main" } +cosmic-client-toolkit = { git = "https://github.com/pop-os/cosmic-protocols//", branch = "main" } diff --git a/cosmic-app-list/src/app.rs b/cosmic-app-list/src/app.rs index e22c02a7..9ed7ea48 100755 --- a/cosmic-app-list/src/app.rs +++ b/cosmic-app-list/src/app.rs @@ -14,7 +14,10 @@ use cctk::{ wayland_client::protocol::{ wl_data_device_manager::DndAction, wl_output::WlOutput, wl_seat::WlSeat, }, - wayland_protocols::ext::foreign_toplevel_list::v1::client::ext_foreign_toplevel_handle_v1::ExtForeignToplevelHandleV1, + wayland_protocols::ext::{ + foreign_toplevel_list::v1::client::ext_foreign_toplevel_handle_v1::ExtForeignToplevelHandleV1, + workspace::v1::client::ext_workspace_handle_v1::ExtWorkspaceHandleV1, + }, }; use cosmic::{ applet::{ @@ -44,10 +47,7 @@ use cosmic::{ Apply, Element, Task, }; use cosmic_app_list_config::{AppListConfig, APP_ID}; -use cosmic_protocols::{ - toplevel_info::v1::client::zcosmic_toplevel_handle_v1::State, - workspace::v1::client::zcosmic_workspace_handle_v1::ZcosmicWorkspaceHandleV1, -}; +use cosmic_protocols::toplevel_info::v1::client::zcosmic_toplevel_handle_v1::State; use freedesktop_desktop_entry as fde; use freedesktop_desktop_entry::{get_languages_from_env, DesktopEntry}; use futures::future::pending; @@ -327,7 +327,7 @@ struct CosmicAppList { dnd_offer: Option, is_listening_for_dnd: bool, gpus: Option>, - active_workspaces: Vec, + active_workspaces: Vec, output_list: HashMap, locales: Vec, overflow_favorites_popup: Option, diff --git a/cosmic-app-list/src/wayland_handler.rs b/cosmic-app-list/src/wayland_handler.rs index 54b55f18..1635afbb 100644 --- a/cosmic-app-list/src/wayland_handler.rs +++ b/cosmic-app-list/src/wayland_handler.rs @@ -39,13 +39,15 @@ use cctk::{ }, Connection, Dispatch, QueueHandle, WEnum, }, - wayland_protocols::ext::foreign_toplevel_list::v1::client::ext_foreign_toplevel_handle_v1::ExtForeignToplevelHandleV1, + wayland_protocols::ext::{ + foreign_toplevel_list::v1::client::ext_foreign_toplevel_handle_v1::ExtForeignToplevelHandleV1, + workspace::v1::client::ext_workspace_handle_v1::State as WorkspaceUpdateState, + }, workspace::{WorkspaceHandler, WorkspaceState}, }; use cosmic_protocols::{ toplevel_info::v1::client::zcosmic_toplevel_handle_v1::ZcosmicToplevelHandleV1, toplevel_management::v1::client::zcosmic_toplevel_manager_v1, - workspace::v1::client::zcosmic_workspace_handle_v1::State as WorkspaceUpdateState, }; use futures::channel::mpsc::UnboundedSender; use sctk::{ @@ -129,12 +131,11 @@ impl WorkspaceHandler for AppData { let active_workspaces = self .workspace_state .workspace_groups() - .iter() .filter_map(|x| { - x.workspaces.iter().find(|w| { - w.state - .contains(&WEnum::Value(WorkspaceUpdateState::Active)) - }) + x.workspaces + .iter() + .filter_map(|handle| self.workspace_state.workspace_info(handle)) + .find(|w| w.state.contains(WorkspaceUpdateState::Active)) }) .map(|workspace| workspace.handle.clone()) .collect::>(); @@ -364,7 +365,7 @@ impl CaptureData { pub fn capture_source_shm_fd( &self, overlay_cursor: bool, - source: ZcosmicToplevelHandleV1, + source: &ExtForeignToplevelHandleV1, fd: Fd, len: Option, ) -> Option> { @@ -379,7 +380,7 @@ impl CaptureData { let capture_session = self .capturer .create_session( - &CaptureSource::CosmicToplevel(source), + &CaptureSource::Toplevel(source.clone()), CaptureOptions::empty(), &self.qh, SessionData { @@ -494,9 +495,6 @@ impl AppData { wl_shm: self.shm_state.wl_shm().clone(), capturer: self.screencopy_state.capturer().clone(), }; - let Some(cosmic_toplevel) = self.cosmic_toplevel(&handle) else { - return; - }; std::thread::spawn(move || { use std::ffi::CStr; let name = unsafe { CStr::from_bytes_with_nul_unchecked(b"app-list-screencopy\0") }; @@ -506,7 +504,7 @@ impl AppData { }; // XXX is this going to use to much memory? - let img = capture_data.capture_source_shm_fd(false, cosmic_toplevel, fd, None); + let img = capture_data.capture_source_shm_fd(false, &handle, fd, None); if let Some(img) = img { let Ok(img) = img.image() else { tracing::error!("Failed to get RgbaImage"); diff --git a/cosmic-app-list/src/wayland_subscription.rs b/cosmic-app-list/src/wayland_subscription.rs index 967c91e2..31448e93 100644 --- a/cosmic-app-list/src/wayland_subscription.rs +++ b/cosmic-app-list/src/wayland_subscription.rs @@ -6,13 +6,15 @@ use cctk::{ sctk::{output::OutputInfo, reexports::calloop}, toplevel_info::ToplevelInfo, wayland_client::protocol::wl_output::WlOutput, - wayland_protocols::ext::foreign_toplevel_list::v1::client::ext_foreign_toplevel_handle_v1::ExtForeignToplevelHandleV1, + wayland_protocols::ext::{ + foreign_toplevel_list::v1::client::ext_foreign_toplevel_handle_v1::ExtForeignToplevelHandleV1, + workspace::v1::client::ext_workspace_handle_v1::ExtWorkspaceHandleV1, + }, }; use cosmic::{ iced::{self, stream, Subscription}, iced_core::image::Bytes, }; -use cosmic_protocols::workspace::v1::client::zcosmic_workspace_handle_v1::ZcosmicWorkspaceHandleV1; use image::EncodableLayout; use futures::{ @@ -110,7 +112,7 @@ pub enum WaylandUpdate { Init(calloop::channel::Sender), Finished, Toplevel(ToplevelUpdate), - Workspace(Vec), + Workspace(Vec), Output(OutputUpdate), ActivationToken { token: Option, diff --git a/cosmic-applet-minimize/src/wayland_handler.rs b/cosmic-applet-minimize/src/wayland_handler.rs index 7084b9e4..70ad88d5 100644 --- a/cosmic-applet-minimize/src/wayland_handler.rs +++ b/cosmic-applet-minimize/src/wayland_handler.rs @@ -131,7 +131,7 @@ impl CaptureData { pub fn capture_source_shm_fd( &self, overlay_cursor: bool, - source: ZcosmicToplevelHandleV1, + source: ExtForeignToplevelHandleV1, fd: Fd, len: Option, ) -> Option> { @@ -145,7 +145,7 @@ impl CaptureData { let capture_session = self .capturer .create_session( - &CaptureSource::CosmicToplevel(source), + &CaptureSource::Toplevel(source), CaptureOptions::empty(), &self.qh, SessionData { @@ -310,9 +310,6 @@ impl AppData { wl_shm: self.shm_state.wl_shm().clone(), capturer: self.screencopy_state.capturer().clone(), }; - let Some(cosmic_toplevel) = self.cosmic_toplevel(&handle) else { - return; - }; std::thread::spawn(move || { use std::ffi::CStr; let name = @@ -323,7 +320,7 @@ impl AppData { }; // XXX is this going to use to much memory? - let img = capure_data.capture_source_shm_fd(false, cosmic_toplevel, fd, None); + let img = capure_data.capture_source_shm_fd(false, handle.clone(), fd, None); if let Some(img) = img { let Ok(mut img) = img.image() else { tracing::error!("Failed to get RgbaImage"); diff --git a/cosmic-applet-tiling/src/wayland.rs b/cosmic-applet-tiling/src/wayland.rs index 316b586f..be842def 100644 --- a/cosmic-applet-tiling/src/wayland.rs +++ b/cosmic-applet-tiling/src/wayland.rs @@ -6,7 +6,10 @@ use cctk::{ sctk::{ self, output::{OutputHandler, OutputState}, - reexports::{calloop, calloop_wayland_source::WaylandSource, client as wayland_client}, + reexports::{ + calloop, calloop_wayland_source::WaylandSource, client as wayland_client, + protocols::ext::workspace::v1::client::ext_workspace_handle_v1, + }, registry::{ProvidesRegistryState, RegistryState}, }, toplevel_info::{ToplevelInfoHandler, ToplevelInfoState}, @@ -15,7 +18,7 @@ use cctk::{ workspace::{WorkspaceHandler, WorkspaceState}, }; use cosmic::iced::futures; -use cosmic_protocols::workspace::v1::client::zcosmic_workspace_handle_v1::{self, TilingState}; +use cosmic_protocols::workspace::v2::client::zcosmic_workspace_handle_v2::TilingState; use futures::{channel::mpsc, executor::block_on, SinkExt}; use std::{ collections::HashSet, @@ -87,44 +90,41 @@ pub fn spawn_workspaces(tx: mpsc::Sender) -> SyncSender loop_handle .insert_source(workspaces_rx, |e, _, state| match e { Event::Msg(AppRequest::TilingState(autotile)) => { - if let Some(w) = - state - .workspace_state - .workspace_groups() + if let Some(w) = state.workspace_state.workspace_groups().find_map(|g| { + if let Some(o) = state.expected_output.as_ref() { + if !g.outputs.contains(o) { + return None; + } + } + g.workspaces .iter() - .find_map(|g| { - if let Some(o) = state.expected_output.as_ref() { - if !g.outputs.contains(o) { - return None; - } - } - g.workspaces.iter().find(|w| { - w.state.contains(&WEnum::Value( - zcosmic_workspace_handle_v1::State::Active, - )) - }) - }) - { - w.handle.set_tiling_state(autotile); - state - .workspace_state - .workspace_manager() - .get() - .unwrap() - .commit(); + .filter_map(|handle| state.workspace_state.workspace_info(handle)) + .find(|w| w.state.contains(ext_workspace_handle_v1::State::Active)) + }) { + if let Some(cosmic_handle) = &w.cosmic_handle { + cosmic_handle.set_tiling_state(autotile); + state + .workspace_state + .workspace_manager() + .get() + .unwrap() + .commit(); + } } } Event::Msg(AppRequest::DefaultBehavior(tiling)) => { for w in state .workspace_state .workspace_groups() - .iter() .flat_map(|g| g.workspaces.iter()) + .filter_map(|handle| state.workspace_state.workspace_info(handle)) .filter(|w| { !state.workspaces_with_previous_toplevel.contains(&w.handle) }) { - w.handle.set_tiling_state(tiling); + if let Some(cosmic_handle) = &w.cosmic_handle { + cosmic_handle.set_tiling_state(tiling); + } } state .workspace_state @@ -167,25 +167,22 @@ pub struct State { registry_state: RegistryState, workspace_state: WorkspaceState, toplevel_info_state: ToplevelInfoState, - workspaces_with_previous_toplevel: - HashSet, + workspaces_with_previous_toplevel: HashSet, have_workspaces: bool, } impl State { pub fn tiling_state(&self) -> Option { - self.workspace_state - .workspace_groups() - .iter() - .find_map(|g| { - if g.outputs + self.workspace_state.workspace_groups().find_map(|g| { + if g.outputs + .iter() + .any(|o| Some(o) == self.expected_output.as_ref()) + { + g.workspaces .iter() - .any(|o| Some(o) == self.expected_output.as_ref()) - { - g.workspaces.iter().find_map(|w| { - if w.state - .contains(&WEnum::Value(zcosmic_workspace_handle_v1::State::Active)) - { + .filter_map(|handle| self.workspace_state.workspace_info(handle)) + .find_map(|w| { + if w.state.contains(ext_workspace_handle_v1::State::Active) { w.tiling.and_then(|e| match e { WEnum::Value(v) => Some(v), _ => { @@ -197,10 +194,10 @@ impl State { None } }) - } else { - None - } - }) + } else { + None + } + }) } } diff --git a/cosmic-applet-tiling/src/wayland_subscription.rs b/cosmic-applet-tiling/src/wayland_subscription.rs index acd5d13e..dbc4edb2 100644 --- a/cosmic-applet-tiling/src/wayland_subscription.rs +++ b/cosmic-applet-tiling/src/wayland_subscription.rs @@ -8,7 +8,7 @@ use cosmic::iced::{ futures::{self, channel::mpsc, SinkExt, StreamExt}, stream, Subscription, }; -use cosmic_protocols::workspace::v1::client::zcosmic_workspace_handle_v1::TilingState; +use cosmic_protocols::workspace::v2::client::zcosmic_workspace_handle_v2::TilingState; use once_cell::sync::Lazy; use tokio::sync::Mutex; diff --git a/cosmic-applet-tiling/src/window.rs b/cosmic-applet-tiling/src/window.rs index b68c6206..c433d766 100644 --- a/cosmic-applet-tiling/src/window.rs +++ b/cosmic-applet-tiling/src/window.rs @@ -25,7 +25,7 @@ use cosmic::{ Element, }; use cosmic_comp_config::{CosmicCompConfig, TileBehavior}; -use cosmic_protocols::workspace::v1::client::zcosmic_workspace_handle_v1::TilingState; +use cosmic_protocols::workspace::v2::client::zcosmic_workspace_handle_v2::TilingState; use cosmic_time::{anim, chain, id, Timeline}; use std::{thread, time::Instant}; use tracing::error; diff --git a/cosmic-applet-workspaces/src/components/app.rs b/cosmic-applet-workspaces/src/components/app.rs index a5357974..4ee917c6 100644 --- a/cosmic-applet-workspaces/src/components/app.rs +++ b/cosmic-applet-workspaces/src/components/app.rs @@ -1,7 +1,15 @@ // Copyright 2023 System76 // SPDX-License-Identifier: GPL-3.0-only -use cctk::sctk::reexports::{calloop::channel::SyncSender, client::backend::ObjectId}; +use cctk::{ + sctk::reexports::{ + calloop::channel::SyncSender, + protocols::ext::workspace::v1::client::ext_workspace_handle_v1::{ + self, ExtWorkspaceHandleV1, + }, + }, + workspace::Workspace, +}; use cosmic::{ applet::cosmic_panel_config::PanelAnchor, iced::{ @@ -17,17 +25,18 @@ use cosmic::{ Element, Task, Theme, }; -use cosmic_protocols::workspace::v1::client::zcosmic_workspace_handle_v1; use once_cell::sync::Lazy; -use std::cmp::Ordering; use crate::{ config, - wayland::{WorkspaceEvent, WorkspaceList}, + wayland::WorkspaceEvent, wayland_subscription::{workspaces, WorkspacesUpdate}, }; -use std::process::Command as ShellCommand; +use std::{ + process::Command as ShellCommand, + time::{Duration, Instant}, +}; static AUTOSIZE_MAIN_ID: Lazy = Lazy::new(|| Id::new("autosize-main")); @@ -43,9 +52,12 @@ pub enum Layout { struct IcedWorkspacesApplet { core: cosmic::app::Core, - workspaces: WorkspaceList, + workspaces: Vec, workspace_tx: Option>, layout: Layout, + scroll: f64, + next_scroll: Option, + last_scroll: Instant, } impl IcedWorkspacesApplet { @@ -78,7 +90,7 @@ impl IcedWorkspacesApplet { #[derive(Debug, Clone)] enum Message { WorkspaceUpdate(WorkspacesUpdate), - WorkspacePressed(ObjectId), + WorkspacePressed(ExtWorkspaceHandleV1), WheelScrolled(ScrollDelta), WorkspaceOverview, } @@ -105,6 +117,9 @@ impl cosmic::Application for IcedWorkspacesApplet { core, workspaces: Vec::new(), workspace_tx: Default::default(), + scroll: 0.0, + next_scroll: None, + last_scroll: Instant::now(), }, Task::none(), ) @@ -125,14 +140,8 @@ impl cosmic::Application for IcedWorkspacesApplet { match message { Message::WorkspaceUpdate(msg) => match msg { WorkspacesUpdate::Workspaces(mut list) => { - list.retain(|w| { - !matches!(w.1, Some(zcosmic_workspace_handle_v1::State::Hidden)) - }); - list.sort_by(|a, b| match a.0.len().cmp(&b.0.len()) { - Ordering::Equal => a.0.cmp(&b.0), - Ordering::Less => Ordering::Less, - Ordering::Greater => Ordering::Greater, - }); + list.retain(|w| !w.state.contains(ext_workspace_handle_v1::State::Hidden)); + list.sort_by(|w1, w2| w1.coordinates.cmp(&w2.coordinates)); self.workspaces = list; } WorkspacesUpdate::Started(tx) => { @@ -152,8 +161,55 @@ impl cosmic::Application for IcedWorkspacesApplet { ScrollDelta::Lines { x, y } => ((x + y) as f64, false), ScrollDelta::Pixels { x, y } => ((x + y) as f64, true), }; - if let Some(tx) = self.workspace_tx.as_mut() { - let _ = tx.try_send(WorkspaceEvent::Scroll(delta, debounce)); + + let dur = if debounce { + Duration::from_millis(350) + } else { + Duration::from_millis(200) + }; + if self.last_scroll.elapsed() > Duration::from_millis(100) + || self.scroll * delta < 0.0 + { + self.next_scroll = None; + self.scroll = 0.0; + } + self.last_scroll = Instant::now(); + + self.scroll += delta; + if let Some(next) = self.next_scroll { + if next > Instant::now() { + return cosmic::iced::Task::none(); + } + self.next_scroll = None; + } + + if self.scroll.abs() < 1.0 { + return cosmic::iced::Task::none(); + } + self.next_scroll = Some(Instant::now() + dur); + if let Some(w_i) = self + .workspaces + .iter() + .position(|w| w.state.contains(ext_workspace_handle_v1::State::Active)) + { + let max_w = self.workspaces.len().wrapping_sub(1); + let d_i = if self.scroll > 0.0 { + if w_i == 0 { + max_w + } else { + w_i.wrapping_sub(1) + } + } else if w_i == max_w { + 0 + } else { + w_i.wrapping_add(1) + }; + self.scroll = 0.0; + if let Some(w) = self.workspaces.get(d_i) { + if let Some(tx) = self.workspace_tx.as_mut() { + let _ = tx.try_send(WorkspaceEvent::Activate(w.handle.clone())); + } + } } } Message::WorkspaceOverview => { @@ -177,11 +233,7 @@ impl cosmic::Application for IcedWorkspacesApplet { let popup_index = self.popup_index().unwrap_or(self.workspaces.len()); let buttons = self.workspaces[..popup_index].iter().filter_map(|w| { - let content = self - .core - .applet - .text(w.0.clone()) - .font(cosmic::font::bold()); + let content = self.core.applet.text(&w.name).font(cosmic::font::bold()); let (width, height) = if self.core.applet.is_horizontal() { (suggested_total as f32, suggested_window_size.1.get() as f32) @@ -205,18 +257,20 @@ impl cosmic::Application for IcedWorkspacesApplet { } else { [self.core.applet.suggested_padding(true), 0] }) - .on_press(match w.1 { - Some(zcosmic_workspace_handle_v1::State::Active) => Message::WorkspaceOverview, - _ => Message::WorkspacePressed(w.2.clone()), - }) + .on_press( + if w.state.contains(ext_workspace_handle_v1::State::Active) { + Message::WorkspaceOverview + } else { + Message::WorkspacePressed(w.handle.clone()) + }, + ) .padding(0); Some( - btn.class(match w.1 { - Some(zcosmic_workspace_handle_v1::State::Active) => { + btn.class( + if w.state.contains(ext_workspace_handle_v1::State::Active) { cosmic::theme::iced::Button::Primary - } - Some(zcosmic_workspace_handle_v1::State::Urgent) => { + } else if w.state.contains(ext_workspace_handle_v1::State::Urgent) { let appearance = |theme: &Theme| { let cosmic = theme.cosmic(); button::Style { @@ -249,8 +303,7 @@ impl cosmic::Application for IcedWorkspacesApplet { button::Status::Disabled => appearance(theme), } })) - } - None => { + } else { let appearance = |theme: &Theme| { let cosmic = theme.cosmic(); button::Style { @@ -282,9 +335,8 @@ impl cosmic::Application for IcedWorkspacesApplet { } } })) - } - _ => return None, - }) + }, + ) .into(), ) }); diff --git a/cosmic-applet-workspaces/src/wayland.rs b/cosmic-applet-workspaces/src/wayland.rs index 8c5f6a3e..708a889f 100644 --- a/cosmic-applet-workspaces/src/wayland.rs +++ b/cosmic-applet-workspaces/src/wayland.rs @@ -10,35 +10,29 @@ use cctk::{ calloop, calloop_wayland_source::WaylandSource, client::{self as wayland_client}, + protocols::ext::workspace::v1::client::ext_workspace_handle_v1::ExtWorkspaceHandleV1, }, registry::{ProvidesRegistryState, RegistryState}, }, - workspace::{WorkspaceHandler, WorkspaceState}, + workspace::{Workspace, WorkspaceHandler, WorkspaceState}, }; -use cosmic_protocols::workspace::v1::client::zcosmic_workspace_handle_v1; use futures::{channel::mpsc, executor::block_on, SinkExt}; -use std::{ - os::{ - fd::{FromRawFd, RawFd}, - unix::net::UnixStream, - }, - time::{Duration, Instant}, +use std::os::{ + fd::{FromRawFd, RawFd}, + unix::net::UnixStream, }; use wayland_client::{ - backend::ObjectId, globals::registry_queue_init, protocol::wl_output::{self, WlOutput}, - Connection, Proxy, QueueHandle, WEnum, + Connection, QueueHandle, }; #[derive(Debug, Clone)] pub enum WorkspaceEvent { - Activate(ObjectId), - Scroll(f64, bool), + Activate(ExtWorkspaceHandleV1), } -pub type WorkspaceList = Vec<(String, Option, ObjectId)>; -pub fn spawn_workspaces(tx: mpsc::Sender) -> SyncSender { +pub fn spawn_workspaces(tx: mpsc::Sender>) -> SyncSender { let (workspaces_tx, workspaces_rx) = calloop::channel::sync_channel(100); let socket = std::env::var("X_PRIVILEGED_WAYLAND_SOCKET") @@ -81,100 +75,18 @@ pub fn spawn_workspaces(tx: mpsc::Sender) -> SyncSender { - if let Some(w) = state + Event::Msg(WorkspaceEvent::Activate(handle)) => { + handle.activate(); + state .workspace_state - .workspace_groups() - .iter() - .find_map(|g| g.workspaces.iter().find(|w| w.handle.id() == id)) - { - w.handle.activate(); - state - .workspace_state - .workspace_manager() - .get() - .unwrap() - .commit(); - } - } - Event::Msg(WorkspaceEvent::Scroll(v, debounce)) => { - let dur = if debounce { - Duration::from_millis(350) - } else { - Duration::from_millis(200) - }; - if state.last_scroll.elapsed() > Duration::from_millis(100) - || state.scroll * v < 0.0 - { - state.next_scroll = None; - state.scroll = 0.0; - } - state.last_scroll = Instant::now(); - - state.scroll += v; - if let Some(next) = state.next_scroll { - if next > Instant::now() { - return; - } - state.next_scroll = None; - } - - if state.scroll.abs() < 1.0 { - return; - } - state.next_scroll = Some(Instant::now() + dur); - if let Some((w_g, w_i)) = state - .workspace_state - .workspace_groups() - .iter() - .find_map(|g| { - if !g - .outputs - .iter() - .any(|o| Some(o) == state.expected_output.as_ref()) - { - return None; - } - g.workspaces - .iter() - .position(|w| { - w.state.contains(&WEnum::Value( - zcosmic_workspace_handle_v1::State::Active, - )) - }) - .map(|w_i| (g, w_i)) - }) - { - let max_w = w_g.workspaces.len().wrapping_sub(1); - let d_i = if state.scroll > 0.0 { - if w_i == 0 { - max_w - } else { - w_i.wrapping_sub(1) - } - } else if w_i == max_w { - 0 - } else { - w_i.wrapping_add(1) - }; - state.scroll = 0.0; - if let Some(w) = w_g.workspaces.get(d_i) { - w.handle.activate(); - state - .workspace_state - .workspace_manager() - .get() - .unwrap() - .commit(); - } - } + .workspace_manager() + .get() + .unwrap() + .commit(); } Event::Closed => { if let Ok(workspace_manager) = @@ -203,62 +115,30 @@ pub fn spawn_workspaces(tx: mpsc::Sender) -> SyncSender, + tx: mpsc::Sender>, configured_output: String, expected_output: Option, output_state: OutputState, registry_state: RegistryState, workspace_state: WorkspaceState, have_workspaces: bool, - scroll: f64, - next_scroll: Option, - last_scroll: Instant, } impl State { - pub fn workspace_list( - &self, - ) -> Vec<(String, Option, ObjectId)> { + pub fn workspace_list(&self) -> Vec { self.workspace_state .workspace_groups() - .iter() - .filter_map(|g| { - if g.outputs + .filter(|g| { + g.outputs .iter() .any(|o| Some(o) == self.expected_output.as_ref()) - { - Some(g.workspaces.iter().map(|w| { - ( - w.name.clone(), - match &w.state { - x if x.contains(&WEnum::Value( - zcosmic_workspace_handle_v1::State::Active, - )) => - { - Some(zcosmic_workspace_handle_v1::State::Active) - } - x if x.contains(&WEnum::Value( - zcosmic_workspace_handle_v1::State::Urgent, - )) => - { - Some(zcosmic_workspace_handle_v1::State::Urgent) - } - x if x.contains(&WEnum::Value( - zcosmic_workspace_handle_v1::State::Hidden, - )) => - { - Some(zcosmic_workspace_handle_v1::State::Hidden) - } - _ => None, - }, - w.handle.id(), - ) - })) - } else { - None - } }) - .flatten() + .flat_map(|g| { + g.workspaces + .iter() + .filter_map(|handle| self.workspace_state.workspace_info(handle)) + }) + .cloned() .collect() } } diff --git a/cosmic-applet-workspaces/src/wayland_subscription.rs b/cosmic-applet-workspaces/src/wayland_subscription.rs index 41908256..8cb05bcf 100644 --- a/cosmic-applet-workspaces/src/wayland_subscription.rs +++ b/cosmic-applet-workspaces/src/wayland_subscription.rs @@ -1,8 +1,8 @@ // Copyright 2023 System76 // SPDX-License-Identifier: GPL-3.0-only -use crate::wayland::{self, WorkspaceEvent, WorkspaceList}; -use cctk::sctk::reexports::calloop::channel::SyncSender; +use crate::wayland::{self, WorkspaceEvent}; +use cctk::{sctk::reexports::calloop::channel::SyncSender, workspace::Workspace}; use cosmic::iced::{ self, futures::{channel::mpsc, SinkExt, StreamExt}, @@ -11,12 +11,12 @@ use cosmic::iced::{ use once_cell::sync::Lazy; use tokio::sync::Mutex; -pub static WAYLAND_RX: Lazy>>> = +pub static WAYLAND_RX: Lazy>>>> = Lazy::new(|| Mutex::new(None)); #[derive(Debug, Clone)] pub enum WorkspacesUpdate { - Workspaces(WorkspaceList), + Workspaces(Vec), Started(SyncSender), Errored, } @@ -71,7 +71,7 @@ pub enum State { } pub struct WorkspacesWatcher { - rx: mpsc::Receiver, + rx: mpsc::Receiver>, tx: SyncSender, }