From 02e240b6bbadda8bc354ee953a95f47fb876656f Mon Sep 17 00:00:00 2001 From: Ashley Wulber Date: Wed, 8 Jun 2022 23:51:33 -0400 Subject: [PATCH] feat: generated workspaces protocol and various improvements --- Cargo.lock | 34 +- Cargo.toml | 1 + applets/cosmic-applet-workspaces/Cargo.toml | 4 +- applets/cosmic-applet-workspaces/build.rs | 23 ++ ...om.system76.CosmicAppletWorkspaces.desktop | 9 +- .../resources/ext-workspace-unstable-v1.xml | 306 ++++++++++++++++++ .../data/resources/resources.gresource.xml | 4 +- applets/cosmic-applet-workspaces/src/main.rs | 42 ++- applets/cosmic-applet-workspaces/src/utils.rs | 15 +- .../cosmic-applet-workspaces/src/wayland.rs | 52 +++ .../src/window/imp.rs | 2 +- .../src/window/mod.rs | 6 +- .../src/workspace_button/imp.rs | 11 +- .../src/workspace_button/mod.rs | 14 +- .../src/workspace_list/imp.rs | 9 +- .../src/workspace_list/mod.rs | 47 ++- .../src/workspace_object/imp.rs | 1 - .../src/workspace_object/mod.rs | 4 +- justfile | 6 + 19 files changed, 480 insertions(+), 110 deletions(-) create mode 100644 applets/cosmic-applet-workspaces/data/resources/ext-workspace-unstable-v1.xml diff --git a/Cargo.lock b/Cargo.lock index 5a8ece17..ed85b549 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -325,13 +325,11 @@ dependencies = [ "gtk4", "libcosmic-widgets", "libpulse-binding", - "mpris2-zbus", "once_cell", "pulsectl-rs", "relm4-macros 0.4.4", "tokio", "tracker", - "zbus", ] [[package]] @@ -393,6 +391,26 @@ dependencies = [ "zvariant", ] +[[package]] +name = "cosmic-applet-workspaces" +version = "0.1.0" +dependencies = [ + "anyhow", + "cascade", + "cosmic-panel-config", + "gio", + "gtk4", + "i18n-embed", + "i18n-embed-fl", + "once_cell", + "pretty_env_logger", + "rust-embed", + "tokio", + "wayland-client", + "wayland-commons", + "wayland-scanner", +] + [[package]] name = "cosmic-dbus-networkmanager" version = "0.1.0" @@ -1632,18 +1650,6 @@ dependencies = [ "windows-sys", ] -[[package]] -name = "mpris2-zbus" -version = "0.1.0" -source = "git+https://github.com/pop-os/mpris2-zbus#bcc8481ea7ccfc08aa870f28272d9093db3b1ba9" -dependencies = [ - "serde", - "thiserror", - "time 0.3.9", - "zbus", - "zvariant", -] - [[package]] name = "nanorand" version = "0.7.0" diff --git a/Cargo.toml b/Cargo.toml index cfa413b3..bee4d6a5 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -4,6 +4,7 @@ members = [ "applets/cosmic-applet-graphics", "applets/cosmic-applet-network", "applets/cosmic-applet-power", + "applets/cosmic-applet-workspaces", "applets/cosmic-applet-status-area", "applets/cosmic-app-list", "applets/cosmic-panel-button", diff --git a/applets/cosmic-applet-workspaces/Cargo.toml b/applets/cosmic-applet-workspaces/Cargo.toml index 3f9ea555..c0f1291f 100644 --- a/applets/cosmic-applet-workspaces/Cargo.toml +++ b/applets/cosmic-applet-workspaces/Cargo.toml @@ -17,7 +17,9 @@ i18n-embed = { version = "0.13.4", features = ["fluent-system", "desktop-request i18n-embed-fl = "0.6.4" rust-embed = "6.3.0" tokio = { version = "1.16.1", features = ["sync"] } - +wayland-client = "0.29.4" +wayland-commons = "0.29.4" [build-dependencies] gio = "0.15.10" +wayland-scanner = "0.29" diff --git a/applets/cosmic-applet-workspaces/build.rs b/applets/cosmic-applet-workspaces/build.rs index 7c42fb5e..040a9eef 100644 --- a/applets/cosmic-applet-workspaces/build.rs +++ b/applets/cosmic-applet-workspaces/build.rs @@ -1,7 +1,30 @@ +extern crate wayland_scanner; + +use std::{env, path::PathBuf, process::Command}; +use wayland_scanner::{generate_code, Side}; + fn main() { + if let Some(output) = Command::new("git") + .args(&["rev-parse", "HEAD"]) + .output() + .ok() +{ + let git_hash = String::from_utf8(output.stdout).unwrap(); + println!("cargo:rustc-env=GIT_HASH={}", git_hash); +} gio::compile_resources( "data/resources", "data/resources/resources.gresource.xml", "compiled.gresource", ); + let dest = PathBuf::from(&env::var("OUT_DIR").unwrap()); + // Location of the xml file, relative to the `Cargo.toml` + let drm_protocol_file = "data/resources/wayland-drm.xml"; + let ext_workspace_protocol_file = "data/resources/ext-workspace-unstable-v1.xml"; + // Target directory for the generate files + generate_code( + ext_workspace_protocol_file, + &dest.join("ext_workspace.rs"), + Side::Client, + ); } diff --git a/applets/cosmic-applet-workspaces/data/com.system76.CosmicAppletWorkspaces.desktop b/applets/cosmic-applet-workspaces/data/com.system76.CosmicAppletWorkspaces.desktop index 7b981b33..ac83c2b4 100644 --- a/applets/cosmic-applet-workspaces/data/com.system76.CosmicAppletWorkspaces.desktop +++ b/applets/cosmic-applet-workspaces/data/com.system76.CosmicAppletWorkspaces.desktop @@ -1,13 +1,12 @@ [Desktop Entry] -Name=Cosmic Applet Power +Name=Cosmic Applet Workspaces Comment=Write a GTK + Rust application Type=Application -Exec=cosmic-applet-power +Exec=cosmic-applet-workspaces Terminal=false Categories=GNOME;GTK; Keywords=Gnome;GTK; -# Translators: Do NOT translate or transliterate this text (this is an icon file name)! -Icon=com.system76.CosmicAppletPower.svg +Icon=com.system76.CosmicAppletWorkspaces.svg StartupNotify=true NoDisplay=true -HostWaylandSocket=true +HostWaylandDisplay=true diff --git a/applets/cosmic-applet-workspaces/data/resources/ext-workspace-unstable-v1.xml b/applets/cosmic-applet-workspaces/data/resources/ext-workspace-unstable-v1.xml new file mode 100644 index 00000000..24410b62 --- /dev/null +++ b/applets/cosmic-applet-workspaces/data/resources/ext-workspace-unstable-v1.xml @@ -0,0 +1,306 @@ + + + + Copyright © 2019 Christopher Billington + Copyright © 2020 Ilia Bozhinov + + Permission to use, copy, modify, distribute, and sell this + software and its documentation for any purpose is hereby granted + without fee, provided that the above copyright notice appear in + all copies and that both that copyright notice and this permission + notice appear in supporting documentation, and that the name of + the copyright holders not be used in advertising or publicity + pertaining to distribution of the software without specific, + written prior permission. The copyright holders make no + representations about the suitability of this software for any + purpose. It is provided "as is" without express or implied + warranty. + + THE COPYRIGHT HOLDERS DISCLAIM ALL WARRANTIES WITH REGARD TO THIS + SOFTWARE, INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND + FITNESS, IN NO EVENT SHALL THE COPYRIGHT HOLDERS BE LIABLE FOR ANY + SPECIAL, INDIRECT OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES + WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN + AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, + ARISING OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF + THIS SOFTWARE. + + + + + Workspaces, also called virtual desktops, are groups of surfaces. A + compositor with a concept of workspaces may only show some such groups of + surfaces (those of 'active' workspaces) at a time. 'Activating' a + workspace is a request for the compositor to display that workspace's + surfaces as normal, whereas the compositor may hide or otherwise + de-emphasise surfaces that are associated only with 'inactive' workspaces. + Workspaces are grouped by which sets of outputs they correspond to, and + may contain surfaces only from those outputs. In this way, it is possible + for each output to have its own set of workspaces, or for all outputs (or + any other arbitrary grouping) to share workspaces. Compositors may + optionally conceptually arrange each group of workspaces in an + N-dimensional grid. + + The purpose of this protocol is to enable the creation of taskbars and + docks by providing them with a list of workspaces and their properties, + and allowing them to activate and deactivate workspaces. + + After a client binds the zext_workspace_manager_v1, each workspace will be + sent via the workspace event. + + + + + This event is emitted whenever a new workspace group has been created. + + All initial details of the workspace group (workspaces, outputs) will be + sent immediately after this event via the corresponding events in + zext_workspace_group_handle_v1. + + + + + + + The client must send this request after it has finished sending other + requests. The compositor must process a series of requests preceding a + commit request atomically. + + This allows changes to the workspace properties to be seen as atomic, + even if they happen via multiple events, and even if they involve + multiple zext_workspace_handle_v1 objects, for example, deactivating one + workspace and activating another. + + + + + + This event is sent after all changes in all workspace groups have been + sent. + + This allows changes to one or more zext_workspace_group_handle_v1 + properties to be seen as atomic, even if they happen via multiple + events. In particular, an output moving from one workspace group to + another sends an output_enter event and an output_leave event to the two + zext_workspace_group_handle_v1 objects in question. The compositor sends + the done event only after updating the output information in both + workspace groups. + + + + + + This event indicates that the compositor is done sending events to the + zext_workspace_manager_v1. The server will destroy the object + immediately after sending this request, so it will become invalid and + the client should free any resources associated with it. + + + + + + Indicates the client no longer wishes to receive events for new + workspace groups. However the compositor may emit further workspace + events, until the finished event is emitted. + + The client must not send any more requests after this one. + + + + + + + A zext_workspace_group_handle_v1 object represents a a workspace group + that is assigned a set of outputs and contains a number of workspaces. + + The set of outputs assigned to the workspace group is conveyed to the client via + output_enter and output_leave events, and its workspaces are conveyed with + workspace events. + + For example, a compositor which has a set of workspaces for each output may + advertise a workspace group (and its workspaces) per output, whereas a compositor + where a workspace spans all outputs may advertise a single workspace group for all + outputs. + + + + + This event is emitted whenever an output is assigned to the workspace + group. + + + + + + + This event is emitted whenever an output is removed from the workspace + group. + + + + + + + This event is emitted whenever a new workspace has been created. + + All initial details of the workspace (name, coordinates, state) will + be sent immediately after this event via the corresponding events in + zext_workspace_handle_v1. + + + + + + + This event means the zext_workspace_group_handle_v1 has been destroyed. + It is guaranteed there won't be any more events for this + zext_workspace_group_handle_v1. The zext_workspace_group_handle_v1 becomes + inert so any requests will be ignored except the destroy request. + + The compositor must remove all workspaces belonging to a workspace group + before removing the workspace group. + + + + + + Request that the compositor create a new workspace with the given name. + + There is no guarantee that the compositor will create a new workspace, + or that the created workspace will have the provided name. + + + + + + + Destroys the zext_workspace_handle_v1 object. + + This request should be called either when the client does not want to + use the workspace object any more or after the remove event to finalize + the destruction of the object. + + + + + + + A zext_workspace_handle_v1 object represents a a workspace that handles a + group of surfaces. + + Each workspace has a name, conveyed to the client with the name event; a + list of states, conveyed to the client with the state event; and + optionally a set of coordinates, conveyed to the client with the + coordinates event. The client may request that the compositor activate or + deactivate the workspace. + + Each workspace can belong to only a single workspace group. + Depepending on the compositor policy, there might be workspaces with + the same name in different workspace groups, but these workspaces are still + separate (e.g. one of them might be active while the other is not). + + + + + This event is emitted immediately after the zext_workspace_handle_v1 is + created and whenever the name of the workspace changes. + + + + + + + This event is used to organize workspaces into an N-dimensional grid + within a workspace group, and if supported, is emitted immediately after + the zext_workspace_handle_v1 is created and whenever the coordinates of + the workspace change. Compositors may not send this event if they do not + conceptually arrange workspaces in this way. If compositors simply + number workspaces, without any geometric interpretation, they may send + 1D coordinates, which clients should not interpret as implying any + geometry. Sending an empty array means that the compositor no longer + orders the workspace geometrically. + + Coordinates have an arbitrary number of dimensions N with an uint32 + position along each dimension. By convention if N > 1, the first + dimension is X, the second Y, the third Z, and so on. The compositor may + chose to utilize these events for a more novel workspace layout + convention, however. No guarantee is made about the grid being filled or + bounded; there may be a workspace at coordinate 1 and another at + coordinate 1000 and none in between. Within a workspace group, however, + workspaces must have unique coordinates of equal dimensionality. + + + + + + + This event is emitted immediately after the zext_workspace_handle_v1 is + created and each time the workspace state changes, either because of a + compositor action or because of a request in this protocol. + + + + + + + The different states that a workspace can have. + + + + + + + The workspace is not visible in its workspace group, and clients + attempting to visualize the compositor workspace state should not + display such workspaces. + + + + + + + This event means the zext_workspace_handle_v1 has been destroyed. It is + guaranteed there won't be any more events for this + zext_workspace_handle_v1. The zext_workspace_handle_v1 becomes inert so + any requests will be ignored except the destroy request. + + + + + + Destroys the zext_workspace_handle_v1 object. + + This request should be called either when the client does not want to + use the workspace object any more or after the remove event to finalize + the destruction of the object. + + + + + + Request that this workspace be activated. + + There is no guarantee the workspace will be actually activated, and + behaviour may be compositor-dependent. For example, activating a + workspace may or may not deactivate all other workspaces in the same + group. + + + + + + Request that this workspace be deactivated. + + There is no guarantee the workspace will be actually deactivated. + + + + + + Request that this workspace be removed. + + There is no guarantee the workspace will be actually removed. + + + + diff --git a/applets/cosmic-applet-workspaces/data/resources/resources.gresource.xml b/applets/cosmic-applet-workspaces/data/resources/resources.gresource.xml index 2cf0970f..8b69c0d6 100644 --- a/applets/cosmic-applet-workspaces/data/resources/resources.gresource.xml +++ b/applets/cosmic-applet-workspaces/data/resources/resources.gresource.xml @@ -1,6 +1,6 @@ - - + + style.css diff --git a/applets/cosmic-applet-workspaces/src/main.rs b/applets/cosmic-applet-workspaces/src/main.rs index df05758b..21eb8dec 100644 --- a/applets/cosmic-applet-workspaces/src/main.rs +++ b/applets/cosmic-applet-workspaces/src/main.rs @@ -1,22 +1,28 @@ // SPDX-License-Identifier: MPL-2.0-only -use gtk4::{gdk::Display, gio::{self, ApplicationFlags}, glib, prelude::*, CssProvider, StyleContext}; +use gtk4::{ + gdk::Display, + gio::{self, ApplicationFlags}, + glib, + prelude::*, + CssProvider, StyleContext, +}; use once_cell::sync::OnceCell; -use window::CosmicWorkspacesWindow; use std::sync::{Arc, Mutex}; use tokio::sync::mpsc; -use utils::{Event, BoxedWorkspaceList}; +use utils::{Activate, Workspace}; +use window::CosmicWorkspacesWindow; +mod localize; +mod utils; +mod wayland; mod window; mod workspace_button; mod workspace_list; -mod wayland; -mod localize; -mod utils; mod workspace_object; const ID: &str = "com.system76.CosmicAppletWorkspaces"; -static TX: OnceCell> = OnceCell::new(); +static TX: OnceCell> = OnceCell::new(); pub fn localize() { let localizer = crate::localize::localizer(); @@ -29,7 +35,7 @@ pub fn localize() { fn load_css() { let provider = CssProvider::new(); - provider.load_from_resource("/com.system76.CosmicAppletWorkspaces/style.css"); + provider.load_from_resource("/com/System76/CosmicAppletWorkspaces/style.css"); StyleContext::add_provider_for_display( &Display::default().unwrap(), @@ -50,24 +56,16 @@ fn main() { app.connect_activate(|app| { load_css(); - let (tx, mut rx) = mpsc::channel(100); + let (tx, mut rx) = mpsc::channel::>(100); - let window = CosmicWorkspacesWindow::new(app, tx.clone()); + let wayland_tx = wayland::spawn_workspaces(tx.clone()); + let window = CosmicWorkspacesWindow::new(app); - let workspace_list = Arc::new(Mutex::new(Vec::::new())); - - TX.set(tx.clone()).unwrap(); + TX.set(wayland_tx).unwrap(); let _ = glib::MainContext::default().spawn_local(async move { - while let Some(event) = rx.recv().await { - match event { - Event::Activate(_) => { - // TODO activate the selected workspace - } - Event::WorkspaceList => { - // TODO update the model with the new workspace list - } - } + while let Some(workspace_list) = rx.recv().await { + // TODO update the model with the new workspace list } }); window.show(); diff --git a/applets/cosmic-applet-workspaces/src/utils.rs b/applets/cosmic-applet-workspaces/src/utils.rs index 4d27e5e9..7dd7f3f8 100644 --- a/applets/cosmic-applet-workspaces/src/utils.rs +++ b/applets/cosmic-applet-workspaces/src/utils.rs @@ -5,26 +5,13 @@ use std::path::PathBuf; use gtk4::glib; use std::future::Future; -#[derive(Debug)] -pub enum Event { - WorkspaceList, - Activate(u32), -} - +pub type Activate = u32; #[derive(Debug, Clone, PartialEq, Eq)] pub struct Workspace { pub(crate) id: u32, pub(crate) active: bool, } -#[derive(Clone, Debug, Default, glib::Boxed)] -#[boxed_type(name = "BoxedWorkspace")] -pub struct BoxedWorkspace(pub Option); - -#[derive(Clone, Debug, Default, glib::Boxed)] -#[boxed_type(name = "BoxedWorkspaceList")] -pub struct BoxedWorkspaceList(pub Vec); - pub fn data_path() -> PathBuf { let mut path = glib::user_data_dir(); path.push(crate::ID); diff --git a/applets/cosmic-applet-workspaces/src/wayland.rs b/applets/cosmic-applet-workspaces/src/wayland.rs index e69de29b..0bced8dc 100644 --- a/applets/cosmic-applet-workspaces/src/wayland.rs +++ b/applets/cosmic-applet-workspaces/src/wayland.rs @@ -0,0 +1,52 @@ +use crate::utils::{Activate, Workspace}; +use std::{ + num::ParseIntError, + os::unix::prelude::RawFd, + sync::{Arc, Mutex}, +}; +use tokio::sync::mpsc; +use wayland_client::{protocol::wl_registry, Display, GlobalManager}; + +mod generated { + // The generated code tends to trigger a lot of warnings + // so we isolate it into a very permissive module + #![allow(dead_code, non_camel_case_types, unused_unsafe, unused_variables)] + #![allow(non_upper_case_globals, non_snake_case, unused_imports)] + + pub mod client { + // These imports are used by the generated code + pub(crate) use wayland_commons::map::{Object, ObjectMetadata}; + pub(crate) use wayland_commons::smallvec; + pub(crate) use wayland_commons::wire::{Argument, ArgumentType, Message, MessageDesc}; + pub(crate) use wayland_commons::{Interface, MessageGroup}; + pub(crate) use wayland_client::protocol::wl_output; + pub(crate) use wayland_client::sys; + pub(crate) use wayland_client::{AnonymousObject, Main, Proxy, ProxyMap}; + include!(concat!(env!("OUT_DIR"), "/ext_workspace.rs")); + } +} + +pub fn spawn_workspaces(tx: mpsc::Sender>) -> mpsc::Sender { + let (workspaces_tx, mut workspaces_rx) = mpsc::channel(100); + if let Ok(display) = std::env::var("HOST_WAYLAND_DISPLAY") + .map_err(anyhow::Error::msg) + .and_then(|fd| Display::connect_to_name(fd).map_err(anyhow::Error::msg)) + { + std::thread::spawn(move || { + let mut event_queue = display.create_event_queue(); + let attached_display = display.attach(event_queue.token()); + let globals = GlobalManager::new(&attached_display); + dbg!(event_queue.sync_roundtrip(&mut (), |_, _, _| unreachable!())); + + println!("Globals: "); + for (name, interface, version) in globals.list() { + println!("{}: {} (version {})", name, interface, version); + } + }); + } else { + eprintln!("ENV variable HOST_WAYLAND_SOCKET is missing. Exiting..."); + std::process::exit(1); + } + + workspaces_tx +} diff --git a/applets/cosmic-applet-workspaces/src/window/imp.rs b/applets/cosmic-applet-workspaces/src/window/imp.rs index d614237c..17364349 100644 --- a/applets/cosmic-applet-workspaces/src/window/imp.rs +++ b/applets/cosmic-applet-workspaces/src/window/imp.rs @@ -1,8 +1,8 @@ // SPDX-License-Identifier: MPL-2.0-only +use crate::workspace_list::WorkspaceList; use gtk4::{glib, subclass::prelude::*}; use once_cell::sync::OnceCell; -use crate::{workspace_list::WorkspaceList}; // Object holding the state #[derive(Default)] diff --git a/applets/cosmic-applet-workspaces/src/window/mod.rs b/applets/cosmic-applet-workspaces/src/window/mod.rs index 585a1e87..4b0035b7 100644 --- a/applets/cosmic-applet-workspaces/src/window/mod.rs +++ b/applets/cosmic-applet-workspaces/src/window/mod.rs @@ -1,6 +1,6 @@ // SPDX-License-Identifier: MPL-2.0-only -use crate::{fl, utils::Event, workspace_list::WorkspaceList}; +use crate::{fl, utils::Activate, workspace_list::WorkspaceList}; use cascade::cascade; use cosmic_panel_config::config::CosmicPanelConfig; use gtk4::{ @@ -21,7 +21,7 @@ glib::wrapper! { } impl CosmicWorkspacesWindow { - pub fn new(app: >k4::Application, tx: mpsc::Sender) -> Self { + pub fn new(app: >k4::Application) -> Self { let self_: Self = Object::new(&[("application", app)]) .expect("Failed to create `CosmicWorkspacesWindow`."); let imp = imp::CosmicWorkspacesWindow::from_instance(&self_); @@ -37,7 +37,7 @@ impl CosmicWorkspacesWindow { }; let config = CosmicPanelConfig::load_from_env().unwrap_or_default(); - let app_list = WorkspaceList::new(tx, config); + let app_list = WorkspaceList::new(config); self_.set_child(Some(&app_list)); imp.inner.set(app_list).unwrap(); diff --git a/applets/cosmic-applet-workspaces/src/workspace_button/imp.rs b/applets/cosmic-applet-workspaces/src/workspace_button/imp.rs index 8d462c6c..483cd249 100644 --- a/applets/cosmic-applet-workspaces/src/workspace_button/imp.rs +++ b/applets/cosmic-applet-workspaces/src/workspace_button/imp.rs @@ -1,13 +1,12 @@ -use std::{rc::Rc, cell::RefCell}; -use gtk4::{ToggleButton, glib, subclass::prelude::*}; -use tokio::sync::mpsc; +use crate::Activate; +use gtk4::{glib, subclass::prelude::*, ToggleButton}; use once_cell::sync::OnceCell; -use crate::Event; +use std::{cell::RefCell, rc::Rc}; +use tokio::sync::mpsc; // Object holding the state #[derive(Default)] pub struct WorkspaceButton { - pub tx: Rc>>, pub button: Rc>, } @@ -26,4 +25,4 @@ impl ObjectImpl for WorkspaceButton {} impl WidgetImpl for WorkspaceButton {} // Trait shared by all buttons -impl BoxImpl for WorkspaceButton {} \ No newline at end of file +impl BoxImpl for WorkspaceButton {} diff --git a/applets/cosmic-applet-workspaces/src/workspace_button/mod.rs b/applets/cosmic-applet-workspaces/src/workspace_button/mod.rs index bf5a4ab8..22dea99b 100644 --- a/applets/cosmic-applet-workspaces/src/workspace_button/mod.rs +++ b/applets/cosmic-applet-workspaces/src/workspace_button/mod.rs @@ -1,9 +1,8 @@ mod imp; +use crate::{workspace_object::WorkspaceObject, Activate, TX}; use glib::Object; use gtk4::{glib, prelude::*, subclass::prelude::*, ToggleButton}; -use tokio::sync::mpsc; -use crate::{Event, workspace_object::WorkspaceObject}; glib::wrapper! { pub struct WorkspaceButton(ObjectSubclass) @@ -12,16 +11,15 @@ glib::wrapper! { } impl WorkspaceButton { - pub fn new(tx: mpsc::Sender) -> Self { + pub fn new() -> Self { let self_ = Object::new(&[]).expect("Failed to create `WorkspaceButton`."); let imp = imp::WorkspaceButton::from_instance(&self_); - imp.tx.set(tx).unwrap(); let tb = ToggleButton::with_label(""); self_.append(&tb); imp.button.replace(tb); - + self_ } @@ -34,9 +32,9 @@ impl WorkspaceButton { let new_button = ToggleButton::with_label(&format!("{}", id)); new_button.set_active(obj.active()); self.append(&new_button); - new_button.connect_clicked(glib::clone!(@weak imp.tx as tx => move |_| { - let _ = tx.get().unwrap().send(Event::Activate(id)); - })); + new_button.connect_clicked(move |_| { + let _ = TX.get().unwrap().send(id); + }); imp.button.replace(new_button); } diff --git a/applets/cosmic-applet-workspaces/src/workspace_list/imp.rs b/applets/cosmic-applet-workspaces/src/workspace_list/imp.rs index e70a1a4b..634c1714 100644 --- a/applets/cosmic-applet-workspaces/src/workspace_list/imp.rs +++ b/applets/cosmic-applet-workspaces/src/workspace_list/imp.rs @@ -1,20 +1,19 @@ // SPDX-License-Identifier: MPL-2.0-only -use cosmic_panel_config::config::{CosmicPanelConfig}; +use cosmic_panel_config::config::CosmicPanelConfig; use gtk4::subclass::prelude::*; use gtk4::{gio, glib}; use gtk4::{Box, ListView}; -use tokio::sync::mpsc; use once_cell::sync::OnceCell; +use tokio::sync::mpsc; -use crate::utils::Event; +use crate::utils::Activate; #[derive(Debug, Default)] pub struct WorkspaceList { pub list_view: OnceCell, pub model: OnceCell, - pub tx: OnceCell>, - pub config: OnceCell + pub config: OnceCell, } #[glib::object_subclass] diff --git a/applets/cosmic-applet-workspaces/src/workspace_list/mod.rs b/applets/cosmic-applet-workspaces/src/workspace_list/mod.rs index 5cd6fe35..e7c031ef 100644 --- a/applets/cosmic-applet-workspaces/src/workspace_list/mod.rs +++ b/applets/cosmic-applet-workspaces/src/workspace_list/mod.rs @@ -1,14 +1,14 @@ // SPDX-License-Identifier: MPL-2.0-only -use crate::utils::{Event, BoxedWorkspace}; +use crate::utils::Activate; use crate::workspace_button::WorkspaceButton; use crate::workspace_object::WorkspaceObject; use cascade::cascade; -use cosmic_panel_config::config::{CosmicPanelConfig}; -use gtk4::{glib, gio, prelude::*, subclass::prelude::*}; +use cosmic_panel_config::config::CosmicPanelConfig; use gtk4::ListView; use gtk4::Orientation; use gtk4::SignalListItemFactory; +use gtk4::{gio, glib, prelude::*, subclass::prelude::*}; use tokio::sync::mpsc::Sender; mod imp; @@ -20,10 +20,9 @@ glib::wrapper! { } impl WorkspaceList { - pub fn new(tx: Sender, config: CosmicPanelConfig) -> Self { + pub fn new(config: CosmicPanelConfig) -> Self { let self_: WorkspaceList = glib::Object::new(&[]).expect("Failed to create WorkspaceList"); let imp = imp::WorkspaceList::from_instance(&self_); - imp.tx.set(tx).unwrap(); imp.config.set(config).unwrap(); self_.layout(); //dnd behavior is different for each type, as well as the data in the model @@ -51,7 +50,7 @@ impl WorkspaceList { fn setup_model(&self) { let imp = imp::WorkspaceList::from_instance(self); - let model = gio::ListStore::new(BoxedWorkspace::static_type()); + let model = gio::ListStore::new(WorkspaceObject::static_type()); let selection_model = gtk4::NoSelection::new(Some(&model)); @@ -65,28 +64,24 @@ impl WorkspaceList { let imp = imp::WorkspaceList::from_instance(self); let factory = SignalListItemFactory::new(); let model = imp.model.get().expect("Failed to get saved app model."); - let tx = imp.tx.get().unwrap().clone(); let icon_size = imp.config.get().unwrap().get_applet_icon_size(); - factory.connect_setup( - glib::clone!(@weak model => move |_, list_item| { - let workspace_button = WorkspaceButton::new(tx.clone()); - list_item.set_child(Some(&workspace_button)); - }), - ); + factory.connect_setup(glib::clone!(@weak model => move |_, list_item| { + let workspace_button = WorkspaceButton::new(); + list_item.set_child(Some(&workspace_button)); + })); factory.connect_bind(|_, list_item| { - let workspace_object = list_item - .item() - .expect("The item has to exist.") - .downcast::() - .expect("The item has to be a `DockObject`"); - let workspace_button = list_item - .child() - .expect("The list item child needs to exist.") - .downcast::() - .expect("The list item type needs to be `DockItem`"); - workspace_button.set_workspace_object(&workspace_object); - }, - ); + let workspace_object = list_item + .item() + .expect("The item has to exist.") + .downcast::() + .expect("The item has to be a `DockObject`"); + let workspace_button = list_item + .child() + .expect("The list item child needs to exist.") + .downcast::() + .expect("The list item type needs to be `DockItem`"); + workspace_button.set_workspace_object(&workspace_object); + }); // Set the factory of the list view imp.list_view.get().unwrap().set_factory(Some(&factory)); } diff --git a/applets/cosmic-applet-workspaces/src/workspace_object/imp.rs b/applets/cosmic-applet-workspaces/src/workspace_object/imp.rs index 30ff60ca..5de4e464 100644 --- a/applets/cosmic-applet-workspaces/src/workspace_object/imp.rs +++ b/applets/cosmic-applet-workspaces/src/workspace_object/imp.rs @@ -53,7 +53,6 @@ impl ObjectImpl for WorkspaceObject { false, ParamFlags::READWRITE, ), - ] }); PROPERTIES.as_ref() diff --git a/applets/cosmic-applet-workspaces/src/workspace_object/mod.rs b/applets/cosmic-applet-workspaces/src/workspace_object/mod.rs index 5fa2f740..7b171a94 100644 --- a/applets/cosmic-applet-workspaces/src/workspace_object/mod.rs +++ b/applets/cosmic-applet-workspaces/src/workspace_object/mod.rs @@ -19,11 +19,11 @@ impl WorkspaceObject { pub fn id(&self) -> u32 { imp::WorkspaceObject::from_instance(&self).id.get() - } + } pub fn active(&self) -> bool { imp::WorkspaceObject::from_instance(&self).active.get() - } + } } #[derive(Clone, Debug, Default, glib::Boxed)] diff --git a/justfile b/justfile index 7028a576..29c7e003 100644 --- a/justfile +++ b/justfile @@ -18,6 +18,7 @@ audio_id := 'com.system76.CosmicAppletAudio' graphics_id := 'com.system76.CosmicAppletGraphics' network_id := 'com.system76.CosmicAppletNetwork' power_id := 'com.system76.CosmicAppletPower' +workspaces_id := 'com.system76.CosmicAppletWorkspaces' status_area_id := 'com.system76.CosmicAppletStatusArea' app_button_id := 'com.system76.CosmicPanelAppButton' workspaces_button_id := 'com.system76.CosmicPanelWorkspacesButton' @@ -54,6 +55,11 @@ install: install -Dm0644 applets/cosmic-applet-power/data/{{power_id}}.desktop {{sharedir}}/applications/{{power_id}}.desktop install -Dm04755 target/release/cosmic-applet-power {{bindir}}/cosmic-applet-power + # workspaces + install -Dm0644 applets/cosmic-applet-workspaces/data/icons/{{workspaces_id}}.svg {{iconsdir}}/{{workspaces_id}}.svg + install -Dm0644 applets/cosmic-applet-workspaces/data/{{workspaces_id}}.desktop {{sharedir}}/applications/{{workspaces_id}}.desktop + install -Dm04755 target/release/cosmic-applet-workspaces {{bindir}}/cosmic-applet-workspaces + # status area install -Dm0644 applets/cosmic-applet-status-area/data/icons/{{status_area_id}}.svg {{iconsdir}}/{{status_area_id}}.svg install -Dm0644 applets/cosmic-applet-status-area/data/{{status_area_id}}.desktop {{sharedir}}/applications/{{status_area_id}}.desktop