From 0b8e385f366e74232e90c104d073da4a1951ace9 Mon Sep 17 00:00:00 2001 From: Ashley Wulber <48420062+wash2@users.noreply.github.com> Date: Fri, 3 Feb 2023 12:35:13 -0500 Subject: [PATCH] feat(plugins): add cosmic toplevel plugin --- Cargo.lock | 499 +++++++++++++++++- bin/src/main.rs | 1 + debian/control | 4 +- debian/pop-launcher.links | 1 + justfile | 2 +- plugins/Cargo.toml | 3 + plugins/src/cosmic_toplevel/mod.rs | 199 +++++++ plugins/src/cosmic_toplevel/plugin.ron | 7 + .../src/cosmic_toplevel/toplevel_handler.rs | 201 +++++++ plugins/src/lib.rs | 1 + plugins/src/web/mod.rs | 1 - service/src/client.rs | 1 + src/lib.rs | 2 +- 13 files changed, 916 insertions(+), 6 deletions(-) create mode 100644 plugins/src/cosmic_toplevel/mod.rs create mode 100644 plugins/src/cosmic_toplevel/plugin.ron create mode 100644 plugins/src/cosmic_toplevel/toplevel_handler.rs diff --git a/Cargo.lock b/Cargo.lock index 999f365..bea4557 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -26,6 +26,27 @@ version = "1.0.66" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "216261ddc8289130e551ddcd5ce8a064710c0d064a4d2895c67151c92b5443f6" +[[package]] +name = "appendlist" +version = "1.4.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e149dc73cd30538307e7ffa2acd3d2221148eaeed4871f246657b1c3eaa1cbd2" + +[[package]] +name = "approx" +version = "0.4.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3f2a05fd1bd10b2527e20a2cd32d8873d115b8b39fe219ee25f42a8aca6ba278" +dependencies = [ + "num-traits", +] + +[[package]] +name = "arc-swap" +version = "1.6.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "bddcadddf5e9015d310179a59bb28c4d4b9920ad0f11e8e14dbadf654890c9a6" + [[package]] name = "async-broadcast" version = "0.4.1" @@ -178,6 +199,26 @@ version = "3.11.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "572f695136211188308f16ad2ca5c851a712c464060ae6974944458eb83880ba" +[[package]] +name = "bytemuck" +version = "1.12.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "aaa3a8d9a1ca92e282c96a32d6511b695d7d994d1d102ba85d279f9b2756947f" +dependencies = [ + "bytemuck_derive", +] + +[[package]] +name = "bytemuck_derive" +version = "1.3.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5fe233b960f12f8007e3db2d136e3cb1c291bfd7396e384ee76025fc1a3932b4" +dependencies = [ + "proc-macro2", + "quote", + "syn", +] + [[package]] name = "byteorder" version = "1.4.3" @@ -202,6 +243,19 @@ version = "1.2.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "c1db59621ec70f09c5e9b597b220c7a2b43611f4710dc03ceb8748637775692c" +[[package]] +name = "calloop" +version = "0.10.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a22a6a8f622f797120d452c630b0ab12e1331a1a753e2039ce7868d4ac77b4ee" +dependencies = [ + "log", + "nix 0.24.3", + "slotmap", + "thiserror", + "vec_map", +] + [[package]] name = "cc" version = "1.0.74" @@ -214,6 +268,16 @@ version = "1.0.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "baf1de4339761588bc0619e3cbc0120ee582ebb74b53b4efbf79117bd2da40fd" +[[package]] +name = "cgmath" +version = "0.18.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1a98d30140e3296250832bbaaff83b27dcd6fa3cc70fb6f1f3e5c9c0023b5317" +dependencies = [ + "approx", + "num-traits", +] + [[package]] name = "chrono" version = "0.4.22" @@ -272,6 +336,31 @@ version = "0.8.3" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "5827cebf4670468b8772dd191856768aedcb1b0278a04f989f7766351917b9dc" +[[package]] +name = "cosmic-client-toolkit" +version = "0.1.0" +source = "git+https://github.com/pop-os/cosmic-protocols#1615d9048198d3beac93184259046cf546d63657" +dependencies = [ + "cosmic-protocols", + "gl_generator", + "smithay", + "smithay-client-toolkit", + "wayland-client", +] + +[[package]] +name = "cosmic-protocols" +version = "0.1.0" +source = "git+https://github.com/pop-os/cosmic-protocols#1615d9048198d3beac93184259046cf546d63657" +dependencies = [ + "bitflags", + "wayland-backend", + "wayland-client", + "wayland-protocols", + "wayland-scanner", + "wayland-server", +] + [[package]] name = "cpufeatures" version = "0.2.5" @@ -420,6 +509,59 @@ dependencies = [ "winapi", ] +[[package]] +name = "dlib" +version = "0.5.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ac1b7517328c04c2aa68422fc60a41b92208182142ed04a25879c26c8f878794" +dependencies = [ + "libloading", +] + +[[package]] +name = "downcast-rs" +version = "1.2.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9ea835d29036a4087793836fa931b08837ad5e957da9e23886b29586fb9b6650" + +[[package]] +name = "drm" +version = "0.7.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "01406fcc6bf5180bf107c2922c8f37a875440c56a4d436b4c4cc8c09f433cc2d" +dependencies = [ + "bitflags", + "bytemuck", + "drm-ffi", + "drm-fourcc", + "nix 0.24.3", +] + +[[package]] +name = "drm-ffi" +version = "0.3.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e538131f5ca07f76483b6bcdb18f540fdfe4bb0c95742f64e7d2c5649c61bca5" +dependencies = [ + "drm-sys", + "nix 0.24.3", +] + +[[package]] +name = "drm-fourcc" +version = "2.2.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0aafbcdb8afc29c1a7ee5fbe53b5d62f4565b35a042a662ca9fecd0b54dae6f4" + +[[package]] +name = "drm-sys" +version = "0.2.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "abe573f73b81a2127a5d5989e12a4ea4a4dab7800e53c73c663494d6c342a338" +dependencies = [ + "libc", +] + [[package]] name = "encoding_rs" version = "0.8.31" @@ -692,6 +834,17 @@ dependencies = [ "temp-dir", ] +[[package]] +name = "gl_generator" +version = "0.14.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1a95dfc23a2b4a9a2f5ab41d194f8bfda3cabec42af4e39f08c339eb2a0c124d" +dependencies = [ + "khronos_api", + "log", + "xml-rs", +] + [[package]] name = "h2" version = "0.3.15" @@ -875,6 +1028,16 @@ dependencies = [ "cfg-if", ] +[[package]] +name = "io-lifetimes" +version = "1.0.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "46112a93252b123d31a119a8d1a1ac19deac4fac6e0e8b0df58f0d4e5870e63c" +dependencies = [ + "libc", + "windows-sys", +] + [[package]] name = "ipnet" version = "2.5.0" @@ -896,6 +1059,12 @@ dependencies = [ "wasm-bindgen", ] +[[package]] +name = "khronos_api" +version = "3.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e2db585e1d738fc771bf08a151420d3ed193d9d895a36df7f6f8a9456b911ddc" + [[package]] name = "lazy_static" version = "1.4.0" @@ -908,6 +1077,16 @@ version = "0.2.137" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "fc7fcc620a3bff7cdd7a365be3376c97191aeaccc2a603e600951e452615bf89" +[[package]] +name = "libloading" +version = "0.7.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b67380fd3b2fbe7527a606e18729d21c6f3951633d0500574c4dc22d2d638b9f" +dependencies = [ + "cfg-if", + "winapi", +] + [[package]] name = "libmimalloc-sys" version = "0.1.26" @@ -982,6 +1161,15 @@ version = "2.5.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "2dffe52ecf27772e601905b7522cb4ef790d2cc203488bbd0e2fe85fcb74566d" +[[package]] +name = "memmap2" +version = "0.5.8" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "4b182332558b18d807c4ce1ca8ca983b34c3ee32765e47b3f0f69b90355cc1dc" +dependencies = [ + "libc", +] + [[package]] name = "memoffset" version = "0.6.5" @@ -991,6 +1179,15 @@ dependencies = [ "autocfg", ] +[[package]] +name = "memoffset" +version = "0.7.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5de893c32cde5f383baa4c04c5d6dbdd735cfd4a794b0debdb2bb1b421da5ff4" +dependencies = [ + "autocfg", +] + [[package]] name = "mimalloc" version = "0.1.30" @@ -1006,6 +1203,12 @@ version = "0.3.16" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "2a60c7ce501c71e03a9c9c0d35b861413ae925bd979cc7a4e30d060069aaac8d" +[[package]] +name = "minimal-lexical" +version = "0.2.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "68354c5c6bd36d73ff3feceb05efa59b6acb7626617f4962be322a825e61f79a" + [[package]] name = "mio" version = "0.8.5" @@ -1037,6 +1240,18 @@ dependencies = [ "unicase", ] +[[package]] +name = "nix" +version = "0.24.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "fa52e972a9a719cecb6864fb88568781eb706bac2cd1d4f04a648542dbf78069" +dependencies = [ + "bitflags", + "cfg-if", + "libc", + "memoffset 0.6.5", +] + [[package]] name = "nix" version = "0.25.0" @@ -1047,10 +1262,33 @@ dependencies = [ "bitflags", "cfg-if", "libc", - "memoffset", + "memoffset 0.6.5", "pin-utils", ] +[[package]] +name = "nix" +version = "0.26.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "46a58d1d356c6597d08cde02c2f09d785b09e28711837b1ed667dc652c08a694" +dependencies = [ + "bitflags", + "cfg-if", + "libc", + "memoffset 0.7.1", + "static_assertions", +] + +[[package]] +name = "nom" +version = "7.1.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e5507769c4919c998e69e49c839d9dc6e693ede4cc4290d6ad8b41d4f09c548c" +dependencies = [ + "memchr", + "minimal-lexical", +] + [[package]] name = "num-integer" version = "0.1.45" @@ -1227,6 +1465,12 @@ version = "0.1.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "8b870d8c151b6f2fb93e84a13146138f05d02ed11c7e7c54f8826aaaf7c9f184" +[[package]] +name = "pkg-config" +version = "0.3.26" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6ac9a59f73473f1b8d852421e59e64809f025994837ef743615c6d0c5b305160" + [[package]] name = "polling" version = "2.4.0" @@ -1274,6 +1518,7 @@ dependencies = [ "anyhow", "async-pidfd", "bytes 1.2.1", + "cosmic-client-toolkit", "dirs 4.0.0", "flume", "fork", @@ -1371,6 +1616,15 @@ dependencies = [ "unicode-ident", ] +[[package]] +name = "quick-xml" +version = "0.23.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "11bafc859c6815fbaffbbbf4229ecb767ac913fecb27f9ad4343662e9ef099ea" +dependencies = [ + "memchr", +] + [[package]] name = "quote" version = "1.0.21" @@ -1569,6 +1823,18 @@ version = "1.0.11" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "4501abdff3ae82a1c1b477a17252eb69cee9e66eb915c1abaa4f44d873df9f09" +[[package]] +name = "scan_fmt" +version = "0.2.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0b53b0a5db882a8e2fdaae0a43f7b39e7e9082389e978398bdf223a55b581248" + +[[package]] +name = "scoped-tls" +version = "1.0.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e1cf6437eb19a8f4a6cc0f7dca544973b0b78843adbfeb3683d1a94a0024a294" + [[package]] name = "scopeguard" version = "1.1.0" @@ -1723,12 +1989,102 @@ dependencies = [ "autocfg", ] +[[package]] +name = "slog" +version = "2.7.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8347046d4ebd943127157b94d63abb990fcf729dc4e9978927fdf4ac3c998d06" + +[[package]] +name = "slog-scope" +version = "4.4.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "2f95a4b4c3274cd2869549da82b57ccc930859bdbf5bcea0424bc5f140b3c786" +dependencies = [ + "arc-swap", + "lazy_static", + "slog", +] + +[[package]] +name = "slog-stdlog" +version = "4.1.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6706b2ace5bbae7291d3f8d2473e2bfab073ccd7d03670946197aec98471fa3e" +dependencies = [ + "log", + "slog", + "slog-scope", +] + +[[package]] +name = "slotmap" +version = "1.0.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e1e08e261d0e8f5c43123b7adf3e4ca1690d655377ac93a03b2c9d3e98de1342" +dependencies = [ + "version_check", +] + [[package]] name = "smallvec" version = "1.10.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "a507befe795404456341dfab10cef66ead4c041f62b8b11bbb92bffe5d0953e0" +[[package]] +name = "smithay" +version = "0.3.0" +source = "git+https://github.com/Smithay/smithay?rev=c85e1e5#c85e1e520d14aaaadff1369bcbf88ea8204c0827" +dependencies = [ + "appendlist", + "bitflags", + "calloop", + "cgmath", + "downcast-rs", + "drm", + "drm-ffi", + "drm-fourcc", + "gl_generator", + "indexmap", + "lazy_static", + "libc", + "libloading", + "nix 0.24.3", + "once_cell", + "rand", + "scan_fmt", + "slog", + "slog-stdlog", + "thiserror", + "wayland-protocols-misc", + "wayland-protocols-wlr", + "xkbcommon", +] + +[[package]] +name = "smithay-client-toolkit" +version = "0.16.0" +source = "git+https://github.com/Smithay/client-toolkit?rev=3776d4a#3776d4aecf5bf94bbe0dc3bae3e64d5a07db7fd6" +dependencies = [ + "bitflags", + "calloop", + "dlib", + "lazy_static", + "log", + "memmap2", + "nix 0.25.0", + "pkg-config", + "thiserror", + "wayland-backend", + "wayland-client", + "wayland-cursor", + "wayland-protocols", + "wayland-protocols-wlr", + "wayland-scanner", + "xkbcommon", +] + [[package]] name = "socket2" version = "0.4.7" @@ -2102,6 +2458,12 @@ version = "2.1.2" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "e8db7427f936968176eaa7cdf81b7f98b980b18495ec28f1b5791ac3bfe3eea9" +[[package]] +name = "vec_map" +version = "0.8.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f1bddf1187be692e79c5ffeab891132dfb0f236ed36a43c7ed39f1165ee20191" + [[package]] name = "version_check" version = "0.9.4" @@ -2202,6 +2564,120 @@ version = "0.2.83" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "1c38c045535d93ec4f0b4defec448e4291638ee608530863b1e2ba115d4fff7f" +[[package]] +name = "wayland-backend" +version = "0.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "fb23bfea266c92bb051ea36cce0eb1a52b743dc1c5f168021947eda79764656d" +dependencies = [ + "cc", + "downcast-rs", + "io-lifetimes", + "nix 0.26.1", + "scoped-tls", + "smallvec", + "wayland-sys", +] + +[[package]] +name = "wayland-client" +version = "0.30.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1a925bd68c8b652af4e6f11a32410bd31bf84061c5ef279ed081520c60f203b4" +dependencies = [ + "bitflags", + "nix 0.26.1", + "wayland-backend", + "wayland-scanner", +] + +[[package]] +name = "wayland-cursor" +version = "0.30.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "2d0c3a0d5b4b688b07b0442362d3ed6bf04724fcc16cd69ab6285b90dbc487aa" +dependencies = [ + "nix 0.26.1", + "wayland-client", + "xcursor", +] + +[[package]] +name = "wayland-protocols" +version = "0.30.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7fefbeb8a360abe67ab7c2efe1d297a1a50ee011f5460791bc18870c26bb84e2" +dependencies = [ + "bitflags", + "wayland-backend", + "wayland-client", + "wayland-scanner", + "wayland-server", +] + +[[package]] +name = "wayland-protocols-misc" +version = "0.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "897d4e99645e1ed9245e9e6b5efa78828d2b23b661016d63d55251243d812f8b" +dependencies = [ + "bitflags", + "wayland-backend", + "wayland-protocols", + "wayland-scanner", + "wayland-server", +] + +[[package]] +name = "wayland-protocols-wlr" +version = "0.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "fce991093320e4a6a525876e6b629ab24da25f9baef0c2e0080ad173ec89588a" +dependencies = [ + "bitflags", + "wayland-backend", + "wayland-client", + "wayland-protocols", + "wayland-scanner", + "wayland-server", +] + +[[package]] +name = "wayland-scanner" +version = "0.30.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "4834c14b3edf1d9986c83ca79b1e7e3afbe9874c7c144702f6467063259ce45d" +dependencies = [ + "proc-macro2", + "quick-xml", + "quote", +] + +[[package]] +name = "wayland-server" +version = "0.30.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9062def387c1b1d80e366d8243c2b3bd6d9e4f343032a3e5da8d4aa03866cf89" +dependencies = [ + "bitflags", + "downcast-rs", + "io-lifetimes", + "nix 0.26.1", + "wayland-backend", + "wayland-scanner", +] + +[[package]] +name = "wayland-sys" +version = "0.30.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "96b2a02ac608e07132978689a6f9bf4214949c85998c247abadd4f4129b1aa06" +dependencies = [ + "dlib", + "log", + "pkg-config", +] + [[package]] name = "web-sys" version = "0.3.60" @@ -2337,6 +2813,15 @@ dependencies = [ "winapi", ] +[[package]] +name = "xcursor" +version = "0.3.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "463705a63313cd4301184381c5e8042f0a7e9b4bb63653f216311d4ae74690b7" +dependencies = [ + "nom", +] + [[package]] name = "xdg" version = "2.4.1" @@ -2346,6 +2831,16 @@ dependencies = [ "dirs 4.0.0", ] +[[package]] +name = "xkbcommon" +version = "0.5.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "acbee136714379ab22da0280207fdb7f47e0bb940adea97731b65598b8c7a92e" +dependencies = [ + "libc", + "memmap2", +] + [[package]] name = "xml-rs" version = "0.8.4" @@ -2375,7 +2870,7 @@ dependencies = [ "futures-sink", "futures-util", "hex", - "nix", + "nix 0.25.0", "once_cell", "ordered-stream", "rand", diff --git a/bin/src/main.rs b/bin/src/main.rs index 20076ce..f9b37ac 100644 --- a/bin/src/main.rs +++ b/bin/src/main.rs @@ -29,6 +29,7 @@ async fn main() { "scripts" => plugins::scripts::main().await, "terminal" => plugins::terminal::main().await, "web" => plugins::web::main().await, + "cosmic-toplevel" => plugins::cosmic_toplevel::main().await, unknown => { eprintln!("unknown cmd: {}", unknown); } diff --git a/debian/control b/debian/control index 8c6cd3a..13b4c8d 100644 --- a/debian/control +++ b/debian/control @@ -7,7 +7,9 @@ Build-Depends: debhelper-compat (=10), just, pkgconf, - rustc (>=1.47), + rustc (>=1.65), + libxkbcommon-dev, + libegl-dev, Standards-Version: 4.1.1 Homepage: https://github.com/pop-os/launcher diff --git a/debian/pop-launcher.links b/debian/pop-launcher.links index 46d6267..99b5032 100644 --- a/debian/pop-launcher.links +++ b/debian/pop-launcher.links @@ -8,3 +8,4 @@ /usr/bin/pop-launcher /usr/lib/pop-launcher/plugins/scripts/scripts /usr/bin/pop-launcher /usr/lib/pop-launcher/plugins/terminal/terminal /usr/bin/pop-launcher /usr/lib/pop-launcher/plugins/web/web +/usr/bin/pop-launcher /usr/lib/pop-launcher/plugins/cosmic_toplevel/cosmic-toplevel diff --git a/justfile b/justfile index ec08ae0..f4d035f 100644 --- a/justfile +++ b/justfile @@ -6,7 +6,7 @@ vendor_args := if vendor == '1' { '--frozen --offline' } else { '' } debug_args := if debug == '1' { '' } else { '--release' } cargo_args := vendor_args + ' ' + debug_args -plugins := 'calc desktop_entries files find pop_shell pulse recent scripts terminal web' +plugins := 'calc desktop_entries files find pop_shell pulse recent scripts terminal web cosmic_toplevel' ID := 'pop-launcher' diff --git a/plugins/Cargo.toml b/plugins/Cargo.toml index 0bf7010..5307309 100644 --- a/plugins/Cargo.toml +++ b/plugins/Cargo.toml @@ -34,6 +34,9 @@ futures = "0.3.25" bytes = "1.2.1" recently-used-xbel = "1.0.0" +# dependencies cosmic toplevel +cctk = { git = "https://github.com/pop-os/cosmic-protocols", package = "cosmic-client-toolkit" } + [dependencies.reqwest] version = "0.11.12" default-features = false diff --git a/plugins/src/cosmic_toplevel/mod.rs b/plugins/src/cosmic_toplevel/mod.rs new file mode 100644 index 0000000..8ac47f2 --- /dev/null +++ b/plugins/src/cosmic_toplevel/mod.rs @@ -0,0 +1,199 @@ +mod toplevel_handler; + +use cctk::wayland_client::Proxy; +use cctk::{cosmic_protocols, sctk::reexports::calloop, toplevel_info::ToplevelInfo}; +use cosmic_protocols::toplevel_info::v1::client::zcosmic_toplevel_handle_v1::ZcosmicToplevelHandleV1; + +use crate::send; +use freedesktop_desktop_entry as fde; +use futures::{ + channel::mpsc, + future::{select, Either}, + StreamExt, +}; +use pop_launcher::{ + async_stdin, async_stdout, json_input_stream, IconSource, PluginResponse, PluginSearchResult, + Request, +}; +use std::borrow::Cow; +use std::{ffi::OsString, fs, path::PathBuf}; +use tokio::io::{AsyncWrite, AsyncWriteExt}; + +use self::toplevel_handler::{toplevel_handler, ToplevelAction, ToplevelEvent}; + +pub async fn main() { + tracing::info!("starting cosmic-toplevel"); + + let (mut app, mut toplevel_rx) = App::new(async_stdout()); + + let mut requests = json_input_stream(async_stdin()); + let mut next_request = requests.next(); + let mut next_event = toplevel_rx.next(); + loop { + let event = select(next_request, next_event).await; + match event { + Either::Left((Some(request), second_to_next_event)) => { + next_event = second_to_next_event; + next_request = requests.next(); + match request { + Ok(request) => match request { + Request::Activate(id) => { + tracing::info!("activating {id}"); + app.activate(id); + } + Request::Quit(id) => app.quit(id), + Request::Search(query) => { + tracing::info!("searching {query}"); + app.search(&query).await; + // clear the ids to ignore, as all just sent are valid + app.ids_to_ignore.clear(); + } + Request::Exit => break, + _ => (), + }, + Err(why) => { + tracing::error!("malformed JSON request: {}", why); + } + }; + } + Either::Right((Some(event), second_to_next_request)) => { + next_event = toplevel_rx.next(); + next_request = second_to_next_request; + match event { + ToplevelEvent::Add(handle, info) => { + tracing::info!("{}", &info.app_id); + app.toplevels.retain(|t| t.0 != handle); + app.toplevels.push((handle, info)); + } + ToplevelEvent::Remove(handle) => { + app.toplevels.retain(|t| t.0 != handle); + // ignore requests for this id until after the next search + app.ids_to_ignore.push(handle.id().protocol_id()); + } + ToplevelEvent::Update(handle, info) => { + if let Some(t) = app.toplevels.iter_mut().find(|t| t.0 == handle) { + t.1 = info; + } + } + } + } + _ => break, + } + } +} + +struct App { + desktop_entries: Vec<(fde::PathSource, PathBuf)>, + ids_to_ignore: Vec, + toplevels: Vec<(ZcosmicToplevelHandleV1, ToplevelInfo)>, + calloop_tx: calloop::channel::Sender, + tx: W, +} + +impl App { + fn new(tx: W) -> (Self, mpsc::UnboundedReceiver) { + let (toplevels_tx, toplevel_rx) = mpsc::unbounded(); + let (calloop_tx, calloop_rx) = calloop::channel::channel(); + let _ = std::thread::spawn(move || toplevel_handler(toplevels_tx, calloop_rx)); + + ( + Self { + ids_to_ignore: Vec::new(), + desktop_entries: fde::Iter::new(fde::default_paths()) + .map(|path| (fde::PathSource::guess_from(&path), path)) + .collect(), + toplevels: Vec::new(), + calloop_tx, + tx, + }, + toplevel_rx, + ) + } + + fn activate(&mut self, id: u32) { + tracing::info!("requested to activate: {id}"); + if self.ids_to_ignore.contains(&id) { + return; + } + if let Some(handle) = self.toplevels.iter().find_map(|t| { + if t.0.id().protocol_id() == id { + Some(t.0.clone()) + } else { + None + } + }) { + tracing::info!("activating: {id}"); + let _ = self.calloop_tx.send(ToplevelAction::Activate(handle)); + } + } + + fn quit(&mut self, id: u32) { + if self.ids_to_ignore.contains(&id) { + return; + } + if let Some(handle) = self.toplevels.iter().find_map(|t| { + if t.0.id().protocol_id() == id { + Some(t.0.clone()) + } else { + None + } + }) { + let _ = self.calloop_tx.send(ToplevelAction::Close(handle)); + } + } + + async fn search(&mut self, query: &str) { + fn contains_pattern(needle: &str, haystack: &[&str]) -> bool { + let needle = needle.to_ascii_lowercase(); + haystack.iter().all(|h| needle.contains(h)) + } + + let query = query.to_ascii_lowercase(); + let haystack = query.split_ascii_whitespace().collect::>(); + + for item in &self.toplevels { + let retain = query.is_empty() + || contains_pattern(&item.1.app_id, &haystack) + || contains_pattern(&item.1.title, &haystack); + + if !retain { + continue; + } + + let mut icon_name = Cow::Borrowed("application-x-executable"); + + for (_, path) in &self.desktop_entries { + if let Some(name) = path.file_stem() { + let app_id: OsString = item.1.app_id.clone().into(); + if app_id == name { + if let Ok(data) = fs::read_to_string(path) { + if let Ok(entry) = fde::DesktopEntry::decode(path, &data) { + if let Some(icon) = entry.icon() { + icon_name = Cow::Owned(icon.to_owned()); + } + } + } + + break; + } + } + } + + send( + &mut self.tx, + PluginResponse::Append(PluginSearchResult { + // XXX protocol id may be re-used later + id: item.0.id().protocol_id(), + name: item.1.app_id.clone(), + description: item.1.title.clone(), + icon: Some(IconSource::Name(icon_name)), + ..Default::default() + }), + ) + .await; + } + + send(&mut self.tx, PluginResponse::Finished).await; + let _ = self.tx.flush(); + } +} diff --git a/plugins/src/cosmic_toplevel/plugin.ron b/plugins/src/cosmic_toplevel/plugin.ron new file mode 100644 index 0000000..5648270 --- /dev/null +++ b/plugins/src/cosmic_toplevel/plugin.ron @@ -0,0 +1,7 @@ +( + name: "COSMIC Windows", + description: "Active windows controllable via Cosmic", + query: (persistent: true), + bin: (path: "cosmic-toplevel"), + icon: Name("focus-windows-symbolic"), +) \ No newline at end of file diff --git a/plugins/src/cosmic_toplevel/toplevel_handler.rs b/plugins/src/cosmic_toplevel/toplevel_handler.rs new file mode 100644 index 0000000..755c73e --- /dev/null +++ b/plugins/src/cosmic_toplevel/toplevel_handler.rs @@ -0,0 +1,201 @@ +use cctk::{ + cosmic_protocols, + sctk::{ + self, + event_loop::WaylandSource, + reexports::{calloop, client::protocol::wl_seat::WlSeat}, + seat::{SeatHandler, SeatState}, + }, + toplevel_info::{ToplevelInfo, ToplevelInfoHandler, ToplevelInfoState}, + toplevel_management::{ToplevelManagerHandler, ToplevelManagerState}, + wayland_client::{self, protocol::wl_output::WlOutput, WEnum}, +}; +use cosmic_protocols::{ + toplevel_info::v1::client::zcosmic_toplevel_handle_v1::{self, ZcosmicToplevelHandleV1}, + toplevel_management::v1::client::zcosmic_toplevel_manager_v1, + workspace::v1::server::zcosmic_workspace_handle_v1::ZcosmicWorkspaceHandleV1, +}; +use futures::channel::mpsc::UnboundedSender; +use sctk::registry::{ProvidesRegistryState, RegistryState}; +use wayland_client::{globals::registry_queue_init, Connection, QueueHandle}; + +#[derive(Debug, Clone)] +pub enum ToplevelAction { + Activate(ZcosmicToplevelHandleV1), + Close(ZcosmicToplevelHandleV1), +} + +#[derive(Debug, Clone)] +pub enum ToplevelEvent { + Add(ZcosmicToplevelHandleV1, ToplevelInfo), + Remove(ZcosmicToplevelHandleV1), + Update(ZcosmicToplevelHandleV1, ToplevelInfo), +} + +#[derive(Debug, Clone)] +pub struct Toplevel { + pub name: String, + pub app_id: String, + pub toplevel_handle: ZcosmicToplevelHandleV1, + pub states: Vec, + pub output: Option, + pub workspace: Option, +} + +struct AppData { + exit: bool, + tx: UnboundedSender, + registry_state: RegistryState, + toplevel_info_state: ToplevelInfoState, + toplevel_manager_state: ToplevelManagerState, + seat_state: SeatState, +} + +impl ProvidesRegistryState for AppData { + fn registry(&mut self) -> &mut RegistryState { + &mut self.registry_state + } + + sctk::registry_handlers!(); +} + +impl SeatHandler for AppData { + fn seat_state(&mut self) -> &mut sctk::seat::SeatState { + &mut self.seat_state + } + + fn new_seat(&mut self, _: &Connection, _: &QueueHandle, _: WlSeat) {} + + fn new_capability( + &mut self, + _: &Connection, + _: &QueueHandle, + _: WlSeat, + _: sctk::seat::Capability, + ) { + } + + fn remove_capability( + &mut self, + _: &Connection, + _: &QueueHandle, + _: WlSeat, + _: sctk::seat::Capability, + ) { + } + + fn remove_seat(&mut self, _: &Connection, _: &QueueHandle, _: WlSeat) {} +} + +impl ToplevelManagerHandler for AppData { + fn toplevel_manager_state(&mut self) -> &mut cctk::toplevel_management::ToplevelManagerState { + &mut self.toplevel_manager_state + } + + fn capabilities( + &mut self, + _: &Connection, + _: &QueueHandle, + _: Vec>, + ) { + } +} + +impl ToplevelInfoHandler for AppData { + fn toplevel_info_state(&mut self) -> &mut ToplevelInfoState { + &mut self.toplevel_info_state + } + + fn new_toplevel( + &mut self, + _conn: &Connection, + _qh: &QueueHandle, + toplevel: &zcosmic_toplevel_handle_v1::ZcosmicToplevelHandleV1, + ) { + if let Some(info) = self.toplevel_info_state.info(toplevel) { + let _ = self + .tx + .unbounded_send(ToplevelEvent::Add(toplevel.clone(), info.clone())); + } + } + + fn update_toplevel( + &mut self, + _conn: &Connection, + _qh: &QueueHandle, + toplevel: &zcosmic_toplevel_handle_v1::ZcosmicToplevelHandleV1, + ) { + if let Some(info) = self.toplevel_info_state.info(toplevel) { + let _ = self + .tx + .unbounded_send(ToplevelEvent::Update(toplevel.clone(), info.clone())); + } + } + + fn toplevel_closed( + &mut self, + _conn: &Connection, + _qh: &QueueHandle, + toplevel: &zcosmic_toplevel_handle_v1::ZcosmicToplevelHandleV1, + ) { + let _ = self + .tx + .unbounded_send(ToplevelEvent::Remove(toplevel.clone())); + } +} + +pub(crate) fn toplevel_handler( + tx: UnboundedSender, + rx: calloop::channel::Channel, +) -> anyhow::Result<()> { + let conn = Connection::connect_to_env()?; + let (globals, event_queue) = registry_queue_init(&conn)?; + let mut event_loop = calloop::EventLoop::::try_new()?; + let qh = event_queue.handle(); + let wayland_source = WaylandSource::new(event_queue)?; + let handle = event_loop.handle(); + + handle.insert_source(wayland_source, |_, q, state| q.dispatch_pending(state))?; + + let _ = handle.insert_source(rx, |event, _, state| match event { + calloop::channel::Event::Msg(req) => match req { + ToplevelAction::Activate(handle) => { + let manager = &state.toplevel_manager_state.manager; + let state = &state.seat_state; + // TODO Ashley how to choose the seat in a multi-seat setup? + for s in state.seats() { + manager.activate(&handle, &s); + } + } + ToplevelAction::Close(handle) => { + let manager = &state.toplevel_manager_state.manager; + manager.close(&handle); + } + }, + calloop::channel::Event::Closed => { + state.exit = true; + } + }); + + let registry_state = RegistryState::new(&globals); + let mut app_data = AppData { + exit: false, + tx, + seat_state: SeatState::new(&globals, &qh), + toplevel_info_state: ToplevelInfoState::new(®istry_state, &qh), + toplevel_manager_state: ToplevelManagerState::new(®istry_state, &qh), + registry_state, + }; + + loop { + if app_data.exit { + break Ok(()); + } + event_loop.dispatch(None, &mut app_data)?; + } +} + +sctk::delegate_seat!(AppData); +sctk::delegate_registry!(AppData); +cctk::delegate_toplevel_info!(AppData); +cctk::delegate_toplevel_manager!(AppData); diff --git a/plugins/src/lib.rs b/plugins/src/lib.rs index add02f7..b2f5977 100644 --- a/plugins/src/lib.rs +++ b/plugins/src/lib.rs @@ -2,6 +2,7 @@ // Copyright © 2021 System76 pub mod calc; +pub mod cosmic_toplevel; pub mod desktop_entries; pub mod files; pub mod find; diff --git a/plugins/src/web/mod.rs b/plugins/src/web/mod.rs index 700797f..941157c 100644 --- a/plugins/src/web/mod.rs +++ b/plugins/src/web/mod.rs @@ -76,7 +76,6 @@ impl Default for App { impl App { pub async fn activate(&mut self, id: u32) { if let Some(query) = self.queries.get(id as usize) { - eprintln!("got query: {}", query); crate::xdg_open(query); } diff --git a/service/src/client.rs b/service/src/client.rs index 8163e7b..c442bd1 100644 --- a/service/src/client.rs +++ b/service/src/client.rs @@ -8,6 +8,7 @@ use tokio::io::{AsyncBufReadExt, AsyncWriteExt}; use tokio::process; use tokio_stream::wrappers::LinesStream; +#[derive(Debug)] pub struct IpcClient { pub child: process::Child, pub stdin: process::ChildStdin, diff --git a/src/lib.rs b/src/lib.rs index f2a710d..c7697da 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -50,7 +50,7 @@ pub struct ContextOption { pub name: String, } -#[derive(Debug, Deserialize, Serialize, Clone)] +#[derive(Debug, Deserialize, Serialize, Clone, Copy)] pub enum GpuPreference { Default, NonDefault,