diff --git a/Cargo.lock b/Cargo.lock index ec0ee8f..b8f5439 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -134,6 +134,39 @@ dependencies = [ "libloading", ] +[[package]] +name = "async-broadcast" +version = "0.5.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1b19760fa2b7301cf235360ffd6d3558b1ed4249edd16d6cca8d690cee265b95" +dependencies = [ + "event-listener", + "futures-core", + "parking_lot 0.12.1", +] + +[[package]] +name = "async-recursion" +version = "1.0.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3b015a331cc64ebd1774ba119538573603427eaace0a1950c423ab971f903796" +dependencies = [ + "proc-macro2", + "quote", + "syn", +] + +[[package]] +name = "async-trait" +version = "0.1.61" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "705339e0e4a9690e2908d2b3d049d85682cf19fbd5782494498fbf7003a6a282" +dependencies = [ + "proc-macro2", + "quote", + "syn", +] + [[package]] name = "autocfg" version = "1.1.0" @@ -179,6 +212,15 @@ version = "0.1.6" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "0d8c1fef690941d3e7788d328517591fecc684c084084702d6ff1641e993699a" +[[package]] +name = "block-buffer" +version = "0.10.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "69cce20737498f97b993470a6e536b8523f0af7892a4f928cceb1ac5e52ebe7e" +dependencies = [ + "generic-array", +] + [[package]] name = "bumpalo" version = "3.12.0" @@ -211,6 +253,12 @@ version = "1.4.3" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "14c189c53d098945499cdfa7ecc63567cf3886b3332b312a5b4585d8d3a6a610" +[[package]] +name = "bytes" +version = "1.3.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "dfb24e866b15a1af2a1b663f10c6b6b8f397a84aadb828f12e5b289ec23a3a3c" + [[package]] name = "calloop" version = "0.10.5" @@ -423,6 +471,16 @@ dependencies = [ "iced_sctk", "libcosmic", "tokio", + "zbus", +] + +[[package]] +name = "cpufeatures" +version = "0.2.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "28d997bd5e24a5928dd43e46dc529867e207907fe0b239c3477d924f7f2ca320" +dependencies = [ + "libc", ] [[package]] @@ -483,6 +541,16 @@ version = "0.2.2" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "7a81dae078cea95a014a339291cec439d2f232ebe854a9d672b796c6afafa9b7" +[[package]] +name = "crypto-common" +version = "0.1.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1bfb12502f3fc46cca1bb51ac28df9d618d813cdc3d2f25b9fe775a34af26bb3" +dependencies = [ + "generic-array", + "typenum", +] + [[package]] name = "csscolorparser" version = "0.6.2" @@ -554,6 +622,17 @@ dependencies = [ "matches", ] +[[package]] +name = "derivative" +version = "2.2.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "fcc3dd5e9e9c0b295d6e1e4d811fb6f157d5ffd784b8d202fc62eac8035a770b" +dependencies = [ + "proc-macro2", + "quote", + "syn", +] + [[package]] name = "derive_setters" version = "0.1.5" @@ -566,6 +645,16 @@ dependencies = [ "syn", ] +[[package]] +name = "digest" +version = "0.10.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8168378f4e5023e7218c89c891c0fd8ecdb5e5e4f18cb78f38cf245dd021e76f" +dependencies = [ + "block-buffer", + "crypto-common", +] + [[package]] name = "directories" version = "4.0.1" @@ -703,6 +792,27 @@ dependencies = [ "syn", ] +[[package]] +name = "enumflags2" +version = "0.7.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e75d4cd21b95383444831539909fbb14b9dc3fdceb2a6f5d36577329a1f55ccb" +dependencies = [ + "enumflags2_derive", + "serde", +] + +[[package]] +name = "enumflags2_derive" +version = "0.7.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f58dc3c5e468259f19f2d46304a6b28f1c3d034442e14b322d2b850e36f6d5ae" +dependencies = [ + "proc-macro2", + "quote", + "syn", +] + [[package]] name = "euclid" version = "0.22.7" @@ -712,6 +822,12 @@ dependencies = [ "num-traits", ] +[[package]] +name = "event-listener" +version = "2.5.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0206175f82b8d6bf6652ff7d71a1e27fd2e4efde587fd368662814d6ec1d9ce0" + [[package]] name = "exr" version = "1.5.2" @@ -929,6 +1045,16 @@ dependencies = [ "byteorder", ] +[[package]] +name = "generic-array" +version = "0.14.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "bff49e947297f3312447abdca79f45f4738097cc82b06e72054d2223f601f1b9" +dependencies = [ + "typenum", + "version_check", +] + [[package]] name = "gethostname" version = "0.2.3" @@ -1170,6 +1296,12 @@ dependencies = [ "libc", ] +[[package]] +name = "hex" +version = "0.4.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7f24254aa9a54b5c858eaee2f5bccdb46aaf0e486a595ed5fd8f86ba55232a70" + [[package]] name = "hexf-parse" version = "0.2.1" @@ -1625,6 +1757,18 @@ dependencies = [ "adler", ] +[[package]] +name = "mio" +version = "0.8.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e5d732bc30207a6423068df043e3d02e0735b155ad7ce1a6f76fe2baa5b158de" +dependencies = [ + "libc", + "log", + "wasi", + "windows-sys 0.42.0", +] + [[package]] name = "mutate_once" version = "0.1.1" @@ -1683,6 +1827,7 @@ dependencies = [ "cfg-if", "libc", "memoffset 0.6.5", + "pin-utils", ] [[package]] @@ -1793,6 +1938,16 @@ dependencies = [ "hashbrown", ] +[[package]] +name = "ordered-stream" +version = "0.1.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d4eb9ba3f3e42dbdd3b7b122de5ca169c81e93d561eb900da3a8c99bcfcf381a" +dependencies = [ + "futures-core", + "pin-project-lite", +] + [[package]] name = "ouroboros" version = "0.13.0" @@ -2026,6 +2181,17 @@ version = "0.2.17" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "5b40af805b3121feab8a3c29f04d8ad262fa8e0561883e7653e024ae4479e6de" +[[package]] +name = "proc-macro-crate" +version = "1.2.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "eda0fc3b0fb7c975631757e14d9049da17374063edb6ebbcbc54d880d4fe94e9" +dependencies = [ + "once_cell", + "thiserror", + "toml", +] + [[package]] name = "proc-macro-error" version = "1.0.4" @@ -2220,6 +2386,15 @@ version = "0.6.28" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "456c603be3e8d448b072f410900c09faf164fbce2d480456f50eea6e25f9c848" +[[package]] +name = "remove_dir_all" +version = "0.5.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3acd125665422973a33ac9d3dd2df85edad0f4ae9b00dafb1a05e43a9f5ef8e7" +dependencies = [ + "winapi", +] + [[package]] name = "renderdoc-sys" version = "0.7.1" @@ -2373,6 +2548,28 @@ dependencies = [ "syn", ] +[[package]] +name = "serde_repr" +version = "0.1.10" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9a5ec9fa74a20ebbe5d9ac23dac1fc96ba0ecfe9f50f2843b52e537b10fbcb4e" +dependencies = [ + "proc-macro2", + "quote", + "syn", +] + +[[package]] +name = "sha1" +version = "0.10.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f04293dc80c3993519f2d7f6f511707ee7094fe0c6d3406feb330cdb3540eba3" +dependencies = [ + "cfg-if", + "cpufeatures", + "digest", +] + [[package]] name = "simplecss" version = "0.2.1" @@ -2493,6 +2690,16 @@ dependencies = [ "xkbcommon", ] +[[package]] +name = "socket2" +version = "0.4.7" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "02e2d2db9033d13a1567121ddd7a095ee144db4e1ca1b1bda3419bc0da294ebd" +dependencies = [ + "libc", + "winapi", +] + [[package]] name = "softbuffer" version = "0.2.0" @@ -2623,6 +2830,20 @@ dependencies = [ "winapi", ] +[[package]] +name = "tempfile" +version = "3.3.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5cdb1ef4eaeeaddc8fbd371e5017057064af0911902ef36b39801f67cc6d79e4" +dependencies = [ + "cfg-if", + "fastrand", + "libc", + "redox_syscall 0.2.16", + "remove_dir_all", + "winapi", +] + [[package]] name = "termcolor" version = "1.2.0" @@ -2693,8 +2914,14 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "1d9f76183f91ecfb55e1d7d5602bd1d979e38a3a522fe900241cf195624d67ae" dependencies = [ "autocfg", + "bytes", + "libc", + "memchr", + "mio", "num_cpus", "pin-project-lite", + "socket2", + "tracing", "windows-sys 0.42.0", ] @@ -2707,6 +2934,38 @@ dependencies = [ "serde", ] +[[package]] +name = "tracing" +version = "0.1.37" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8ce8c33a8d48bd45d624a6e523445fd21ec13d3653cd51f681abf67418f54eb8" +dependencies = [ + "cfg-if", + "pin-project-lite", + "tracing-attributes", + "tracing-core", +] + +[[package]] +name = "tracing-attributes" +version = "0.1.23" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "4017f8f45139870ca7e672686113917c71c7a6e02d4924eda67186083c03081a" +dependencies = [ + "proc-macro2", + "quote", + "syn", +] + +[[package]] +name = "tracing-core" +version = "0.1.30" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "24eb03ba0eab1fd845050058ce5e616558e8f8d8fca633e6b163fe25c797213a" +dependencies = [ + "once_cell", +] + [[package]] name = "ttf-parser" version = "0.12.3" @@ -2742,6 +3001,22 @@ version = "2.0.2" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "6af6ae20167a9ece4bcb41af5b80f8a1f1df981f6391189ce00fd257af04126a" +[[package]] +name = "typenum" +version = "1.16.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "497961ef93d974e23eb6f433eb5fe1b7930b659f06d12dec6fc44a8f554c0bba" + +[[package]] +name = "uds_windows" +version = "1.0.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ce65604324d3cce9b966701489fbd0cf318cb1f7bd9dd07ac9a4ee6fb791930d" +dependencies = [ + "tempfile", + "winapi", +] + [[package]] name = "unicode-bidi" version = "0.3.8" @@ -3415,8 +3690,94 @@ version = "0.1.4" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "c03b3e19c937b5b9bd8e52b1c88f30cce5c0d33d676cf174866175bb794ff658" +[[package]] +name = "zbus" +version = "3.7.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "379d587c0ccb632d1179cf44082653f682842f0535f0fdfaefffc34849cc855e" +dependencies = [ + "async-broadcast", + "async-recursion", + "async-trait", + "byteorder", + "derivative", + "dirs", + "enumflags2", + "event-listener", + "futures-core", + "futures-sink", + "futures-util", + "hex", + "lazy_static", + "nix 0.25.1", + "once_cell", + "ordered-stream", + "rand", + "serde", + "serde_repr", + "sha1", + "static_assertions", + "tokio", + "tracing", + "uds_windows", + "winapi", + "zbus_macros", + "zbus_names", + "zvariant", +] + +[[package]] +name = "zbus_macros" +version = "3.7.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "66492a2e90c0df7190583eccb8424aa12eb7ff06edea415a4fff6688fae18cf8" +dependencies = [ + "proc-macro-crate", + "proc-macro2", + "quote", + "regex", + "syn", +] + +[[package]] +name = "zbus_names" +version = "2.5.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f34f314916bd89bdb9934154627fab152f4f28acdda03e7c4c68181b214fe7e3" +dependencies = [ + "serde", + "static_assertions", + "zvariant", +] + [[package]] name = "zeno" version = "0.2.2" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "c110ba09c9b3a43edd4803d570df0da2414fed6e822e22b976a4e3ef50860701" + +[[package]] +name = "zvariant" +version = "3.10.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "576cc41e65c7f283e5460f5818073e68fb1f1631502b969ef228c2e03c862efb" +dependencies = [ + "byteorder", + "enumflags2", + "libc", + "serde", + "static_assertions", + "zvariant_derive", +] + +[[package]] +name = "zvariant_derive" +version = "3.10.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0fd4aafc0dee96ae7242a24249ce9babf21e1562822f03df650d4e68c20e41ed" +dependencies = [ + "proc-macro-crate", + "proc-macro2", + "quote", + "syn", +] diff --git a/Cargo.toml b/Cargo.toml index b22a59b..5d59a1e 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -9,3 +9,4 @@ futures-channel = "0.3.25" iced_sctk = { git = "https://github.com/pop-os/libcosmic" } libcosmic = { git = "https://github.com/pop-os/libcosmic", default-features = false, features = ["tokio", "wayland"] } tokio = "1.23.0" +zbus = { version = "3.7.0", default-features = false, features = ["tokio"] } diff --git a/src/main.rs b/src/main.rs index dc0d690..fda4798 100644 --- a/src/main.rs +++ b/src/main.rs @@ -32,6 +32,7 @@ use iced_sctk::{ }; use std::{collections::HashMap, mem, process}; +mod toggle_dbus; mod wayland; #[derive(Clone, Debug)] @@ -44,6 +45,7 @@ enum Msg { CloseWorkspace(zcosmic_workspace_handle_v1::ZcosmicWorkspaceHandleV1), ActivateToplevel(zcosmic_toplevel_handle_v1::ZcosmicToplevelHandleV1), CloseToplevel(zcosmic_toplevel_handle_v1::ZcosmicToplevelHandleV1), + DBus(toggle_dbus::Event), } #[derive(Debug)] @@ -62,7 +64,17 @@ struct Toplevel { img: Option, } +#[derive(Clone)] +struct Output { + // Output, on the `iced_sctk` Wayland connection + handle: wl_output::WlOutput, + name: Option, + width: i32, + height: i32, +} + struct LayerSurface { + // Output, on the `iced_sctk` Wayland connection output: wl_output::WlOutput, output_name: Option, // for transitions, would need windows in more than one workspace? But don't capture all of @@ -73,12 +85,14 @@ struct LayerSurface { struct App { max_surface_id: usize, layer_surfaces: HashMap, + outputs: Vec, workspaces: Vec, toplevels: Vec, conn: Option, workspace_manager: Option, toplevel_manager: Option, seats: Vec, + visible: bool, } impl App { @@ -107,6 +121,82 @@ impl App { ) -> Option<&mut Toplevel> { self.toplevels.iter_mut().find(|i| &i.handle == handle) } + + fn create_surface( + &mut self, + output: wl_output::WlOutput, + output_name: Option, + width: i32, + height: i32, + ) -> Command { + let id = self.next_surface_id(); + self.layer_surfaces.insert( + id.clone(), + LayerSurface { + output: output.clone(), + output_name, + }, + ); + get_layer_surface(SctkLayerSurfaceSettings { + id, + keyboard_interactivity: KeyboardInteractivity::Exclusive, + namespace: "workspaces".into(), + layer: Layer::Overlay, + size: Some((Some(width as _), Some(height as _))), + output: IcedOutput::Output(output), + ..Default::default() + }) + } + + fn destroy_surface(&mut self, output: &wl_output::WlOutput) -> Command { + if let Some((id, _)) = self + .layer_surfaces + .iter() + .find(|(_id, surface)| &surface.output == output) + { + let id = *id; + self.layer_surfaces.remove(&id).unwrap(); + destroy_layer_surface(id) + } else { + Command::none() + } + } + + fn toggle(&mut self) -> Command { + if self.visible { + self.hide() + } else { + self.show() + } + } + + fn show(&mut self) -> Command { + if !self.visible { + self.visible = true; + let outputs = self.outputs.clone(); + Command::batch( + outputs + .into_iter() + .map(|output| { + self.create_surface(output.handle, output.name, output.width, output.height) + }) + .collect::>(), + ) + } else { + Command::none() + } + } + + // Close all shell surfaces + fn hide(&mut self) -> Command { + self.visible = false; + Command::batch( + mem::take(&mut self.layer_surfaces) + .into_keys() + .map(destroy_layer_surface) + .collect::>(), + ) + } } impl Application for App { @@ -126,7 +216,6 @@ impl Application for App { // TODO transparent style? // TODO: show panel and dock? Drag? - // TODO way to activate w/ keybind, button fn update(&mut self, message: Msg) -> Command { match message { @@ -134,33 +223,28 @@ impl Application for App { WaylandEvent::Output(evt, output) => match evt { OutputEvent::Created(Some(info)) => { if let Some((width, height)) = info.logical_size { - let id = self.next_surface_id(); - self.layer_surfaces.insert( - id.clone(), - LayerSurface { - output: output.clone(), - output_name: info.name, - }, - ); - return get_layer_surface(SctkLayerSurfaceSettings { - id, - keyboard_interactivity: KeyboardInteractivity::Exclusive, - namespace: "workspaces".into(), - layer: Layer::Overlay, - size: Some((Some(width as _), Some(height as _))), - output: IcedOutput::Output(output), - ..Default::default() + self.outputs.push(Output { + handle: output.clone(), + name: info.name.clone(), + width, + height, }); + if self.visible { + return self.create_surface( + output.clone(), + info.name, + width, + height, + ); + } } } OutputEvent::Removed => { - if let Some((id, _)) = self - .layer_surfaces - .iter() - .find(|(_id, surface)| &surface.output == &output) - { - let id = *id; - self.layer_surfaces.remove(&id).unwrap(); + if let Some(idx) = self.outputs.iter().position(|x| &x.handle == &output) { + self.outputs.remove(idx); + } + if self.visible { + return self.destroy_surface(&output); } } // TODO handle update/remove @@ -232,7 +316,7 @@ impl Application for App { } } Msg::Close => { - std::process::exit(0); + self.hide(); } Msg::Closed(_) => {} Msg::ActivateWorkspace(workspace_handle) => { @@ -248,7 +332,7 @@ impl Application for App { toplevel_manager.activate(&toplevel_handle, &seat); self.conn.as_ref().unwrap().flush(); } - std::process::exit(0); // Can we assume flush is suficient to ensure this takes effect? + self.hide(); } } } @@ -259,6 +343,9 @@ impl Application for App { toplevel_manager.close(&toplevel_handle); } } + Msg::DBus(toggle_dbus::Event::Toggle) => { + return self.toggle(); + } } Command::none() @@ -279,7 +366,11 @@ impl Application for App { None } }); - iced::Subscription::batch(vec![events, wayland::subscription().map(Msg::Wayland)]) + iced::Subscription::batch(vec![ + events, + toggle_dbus::subscription().map(Msg::DBus), + wayland::subscription().map(Msg::Wayland), + ]) } fn view(&self, id: SurfaceIdWrapper) -> cosmic::Element { diff --git a/src/toggle_dbus.rs b/src/toggle_dbus.rs new file mode 100644 index 0000000..6b97bac --- /dev/null +++ b/src/toggle_dbus.rs @@ -0,0 +1,67 @@ +use cosmic::iced::{self, futures::StreamExt, subscription}; +use futures_channel::mpsc; +use std::{fmt::Debug, hash::Hash}; +use zbus::{dbus_interface, Connection, ConnectionBuilder}; + +pub fn subscription() -> iced::Subscription { + subscription::unfold("workspaces-dbus", State::Ready, move |state| { + start_listening(state) + }) +} + +#[derive(Debug)] +enum State { + Ready, + Waiting(Connection, mpsc::UnboundedReceiver), + Finished, +} + +async fn start_listening(state: State) -> (Option, State) { + match state { + State::Ready => { + let (tx, rx) = mpsc::unbounded(); + if let Some(conn) = ConnectionBuilder::session() + .ok() + .and_then(|conn| conn.name("com.system76.CosmicWorkspaces").ok()) + .and_then(|conn| { + conn.serve_at( + "/com/system76/CosmicWorkspaces", + CosmicWorkspacesServer { tx }, + ) + .ok() + }) + .map(|conn| conn.build()) + { + if let Ok(conn) = conn.await { + return (None, State::Waiting(conn, rx)); + } + } + return (None, State::Finished); + } + State::Waiting(conn, mut rx) => { + if let Some(Event::Toggle) = rx.next().await { + (Some(Event::Toggle), State::Waiting(conn, rx)) + } else { + (None, State::Finished) + } + } + State::Finished => iced::futures::future::pending().await, + } +} + +#[derive(Debug, Clone, Copy)] +pub enum Event { + Toggle, +} + +#[derive(Debug)] +struct CosmicWorkspacesServer { + tx: mpsc::UnboundedSender, +} + +#[dbus_interface(name = "com.system76.CosmicWorkspaces")] +impl CosmicWorkspacesServer { + async fn toggle(&self) { + self.tx.unbounded_send(Event::Toggle).unwrap(); + } +}