From 7992ad67f606a8404e818833d5019e2d1fc0f2eb Mon Sep 17 00:00:00 2001 From: Victoria Brekenfeld Date: Mon, 16 Jan 2023 15:12:25 +0100 Subject: [PATCH] update smithay, iced; reworked window for xwayland --- Cargo.lock | 226 ++++--- Cargo.toml | 5 +- src/backend/kms/mod.rs | 4 +- src/backend/render/element.rs | 14 +- src/logger/mod.rs | 24 +- src/shell/element/mod.rs | 298 +++------ src/shell/element/stack.rs | 670 ++++++++++++------- src/shell/element/surface.rs | 579 ++++++++++++++++ src/shell/element/window.rs | 484 ++++++++++---- src/shell/focus/target.rs | 23 +- src/shell/layout/floating/grabs/moving.rs | 4 +- src/shell/layout/floating/grabs/resize.rs | 34 +- src/shell/layout/floating/mod.rs | 27 +- src/shell/layout/mod.rs | 76 ++- src/shell/layout/tiling/mod.rs | 15 +- src/shell/mod.rs | 33 +- src/shell/workspace.rs | 112 ++-- src/utils/iced.rs | 311 ++++++--- src/wayland/handlers/compositor.rs | 35 +- src/wayland/handlers/screencopy.rs | 25 +- src/wayland/handlers/toplevel_info.rs | 43 +- src/wayland/handlers/toplevel_management.rs | 28 +- src/wayland/handlers/xdg_shell/mod.rs | 16 +- src/wayland/handlers/xdg_shell/popup.rs | 8 +- src/wayland/protocols/screencopy.rs | 14 +- src/wayland/protocols/toplevel_info.rs | 234 +++---- src/wayland/protocols/toplevel_management.rs | 49 +- 27 files changed, 2285 insertions(+), 1106 deletions(-) create mode 100644 src/shell/element/surface.rs diff --git a/Cargo.lock b/Cargo.lock index ceb708eb..1d601894 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -10,9 +10,9 @@ checksum = "fe438c63458706e03479442743baae6c88256498e6431708f6dfc520a26515d3" [[package]] name = "ab_glyph" -version = "0.2.18" +version = "0.2.19" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "4dcdbc68024b653943864d436fe8a24b028095bc1cf91a8926f8241e4aaffe59" +checksum = "e5568a4aa5ba8adf5175c5c460b030e27d8893412976cc37bef0e4fbc16cfbba" dependencies = [ "ab_glyph_rasterizer", "owned_ttf_parser", @@ -20,9 +20,9 @@ dependencies = [ [[package]] name = "ab_glyph_rasterizer" -version = "0.1.7" +version = "0.1.8" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "330223a1aecc308757b9926e9391c9b47f8ef2dbd8aea9df88312aea18c5e8d6" +checksum = "c71b1793ee61086797f5c80b6efa2b8ffa6d5dd703f118545808a7f2e27f7046" [[package]] name = "addr2line" @@ -151,18 +151,18 @@ checksum = "8da52d66c7071e2e3fa2a1e5c6d088fec47b593032b254f5e980de8ea54454d6" [[package]] name = "ash" -version = "0.37.1+1.3.235" +version = "0.37.2+1.3.238" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "911015c962d56e2e4052f40182ca5462ba60a3d2ff04e827c365a0ab3d65726d" +checksum = "28bf19c1f0a470be5fbf7522a308a05df06610252c5bcf5143e1b23f629a9a03" dependencies = [ "libloading", ] [[package]] name = "atomic_refcell" -version = "0.1.8" +version = "0.1.9" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "73b5e5f48b927f04e952dedc932f31995a65a0bf65ec971c74436e51bf6e970d" +checksum = "857253367827bd9d0fd973f0ef15506a96e79e41b0ad7aa691203a4e3214f6c8" [[package]] name = "atty" @@ -432,14 +432,15 @@ dependencies = [ "cosmic-protocols", "edid-rs", "egui", - "iced_swbuf", + "iced_softbuffer", "id_tree", "indexmap", "lazy_static", - "log-panics", "libcosmic", - "libsystemd", + "libsystemd 0.5.0", "log", + "log-panics", + "ordered-float", "png", "regex", "renderdoc", @@ -466,7 +467,7 @@ dependencies = [ [[package]] name = "cosmic-protocols" version = "0.1.0" -source = "git+https://github.com/pop-os/cosmic-protocols?branch=main#c0754ff393898d15616f165fdd1e308b4100c971" +source = "git+https://github.com/pop-os/cosmic-protocols?branch=main#1615d9048198d3beac93184259046cf546d63657" dependencies = [ "bitflags", "wayland-backend", @@ -498,7 +499,7 @@ dependencies = [ [[package]] name = "cosmic-theme" version = "0.1.0" -source = "git+https://github.com/pop-os/cosmic-theme.git#77aad06a6e3e2996ef24fc12e5f59d6da7226692" +source = "git+https://github.com/pop-os/cosmic-theme.git#5c84b81c67a69e3752bc2fdc451f9d49857ceabf" dependencies = [ "anyhow", "csscolorparser", @@ -596,6 +597,16 @@ dependencies = [ "subtle", ] +[[package]] +name = "csscolorparser" +version = "0.6.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "eb2a7d3066da2de787b7f032c736763eb7ae5d355f81a68bab2675a96008b0bf" +dependencies = [ + "phf", + "serde", +] + [[package]] name = "cty" version = "0.2.2" @@ -727,7 +738,7 @@ dependencies = [ [[package]] name = "directories" version = "4.0.1" -source = "git+https://github.com/edfloreshz/directories-rs#b93c018bc319f066fbeadcd8e3b865f1fccaaa8c" +source = "git+https://github.com/edfloreshz/directories-rs#6a6d83d853a35ee3273034215c4defaf61286fe5" dependencies = [ "anyhow", "dirs-sys", @@ -1176,9 +1187,9 @@ checksum = "00b0228411908ca8685dba7fc2cdd70ec9990a6e753e89b6ac91a84c40fbaf4b" [[package]] name = "freedesktop-icons" -version = "0.2.2" +version = "0.2.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e31823094643eabe14030d2f1b3f0aa10164f27b1a31e2b938ffe8ea9c9fc91b" +checksum = "00e61ac115df4632b592d36b71fda3c259f4c8061c70b7fa429bac145890e880" dependencies = [ "dirs", "once_cell", @@ -1488,9 +1499,9 @@ dependencies = [ [[package]] name = "half" -version = "2.2.0" +version = "2.2.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "6c467d36af040b7b2681f5fddd27427f6da8d3d072f575a265e181d2f8e8d157" +checksum = "02b4af3693f1b705df946e9fe5631932443781d0aabb423b62fcd4d73f6d2fd0" dependencies = [ "crunchy", ] @@ -1550,13 +1561,14 @@ dependencies = [ [[package]] name = "iced" version = "0.6.0" +source = "git+https://github.com/pop-os/libcosmic?branch=ci_add_without_shell#99842258b4d77dcb0657b7c299bae45579e139a1" dependencies = [ "iced_core", "iced_futures", "iced_glow", "iced_graphics", "iced_native", - "iced_swbuf", + "iced_softbuffer", "iced_wgpu", "image", "thiserror", @@ -1565,6 +1577,7 @@ dependencies = [ [[package]] name = "iced_core" version = "0.6.2" +source = "git+https://github.com/pop-os/libcosmic?branch=ci_add_without_shell#99842258b4d77dcb0657b7c299bae45579e139a1" dependencies = [ "bitflags", "palette", @@ -1574,6 +1587,7 @@ dependencies = [ [[package]] name = "iced_futures" version = "0.5.1" +source = "git+https://github.com/pop-os/libcosmic?branch=ci_add_without_shell#99842258b4d77dcb0657b7c299bae45579e139a1" dependencies = [ "futures", "log", @@ -1584,6 +1598,7 @@ dependencies = [ [[package]] name = "iced_glow" version = "0.5.1" +source = "git+https://github.com/pop-os/libcosmic?branch=ci_add_without_shell#99842258b4d77dcb0657b7c299bae45579e139a1" dependencies = [ "bytemuck", "euclid", @@ -1598,6 +1613,7 @@ dependencies = [ [[package]] name = "iced_graphics" version = "0.5.0" +source = "git+https://github.com/pop-os/libcosmic?branch=ci_add_without_shell#99842258b4d77dcb0657b7c299bae45579e139a1" dependencies = [ "bitflags", "bytemuck", @@ -1617,6 +1633,7 @@ dependencies = [ [[package]] name = "iced_lazy" version = "0.3.0" +source = "git+https://github.com/pop-os/libcosmic?branch=ci_add_without_shell#99842258b4d77dcb0657b7c299bae45579e139a1" dependencies = [ "iced_native", "ouroboros 0.13.0", @@ -1625,6 +1642,7 @@ dependencies = [ [[package]] name = "iced_native" version = "0.7.0" +source = "git+https://github.com/pop-os/libcosmic?branch=ci_add_without_shell#99842258b4d77dcb0657b7c299bae45579e139a1" dependencies = [ "iced_core", "iced_futures", @@ -1635,17 +1653,9 @@ dependencies = [ ] [[package]] -name = "iced_style" -version = "0.5.1" -dependencies = [ - "iced_core", - "once_cell", - "palette", -] - -[[package]] -name = "iced_swbuf" +name = "iced_softbuffer" version = "0.1.0" +source = "git+https://github.com/pop-os/libcosmic?branch=ci_add_without_shell#99842258b4d77dcb0657b7c299bae45579e139a1" dependencies = [ "cosmic-text", "iced_graphics", @@ -1657,9 +1667,20 @@ dependencies = [ "softbuffer", ] +[[package]] +name = "iced_style" +version = "0.5.1" +source = "git+https://github.com/pop-os/libcosmic?branch=ci_add_without_shell#99842258b4d77dcb0657b7c299bae45579e139a1" +dependencies = [ + "iced_core", + "once_cell", + "palette", +] + [[package]] name = "iced_wgpu" version = "0.7.0" +source = "git+https://github.com/pop-os/libcosmic?branch=ci_add_without_shell#99842258b4d77dcb0657b7c299bae45579e139a1" dependencies = [ "bitflags", "bytemuck", @@ -1755,9 +1776,9 @@ dependencies = [ [[package]] name = "io-lifetimes" -version = "1.0.3" +version = "1.0.4" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "46112a93252b123d31a119a8d1a1ac19deac4fac6e0e8b0df58f0d4e5870e63c" +checksum = "e7d6c6f8c91b4b9ed43484ad1a938e393caf35960fce7f82a040497207bd8e9e" dependencies = [ "libc", "windows-sys 0.42.0", @@ -1861,6 +1882,7 @@ checksum = "201de327520df007757c1f0adce6e827fe8562fbc28bfd9c15571c66ca1f5f79" [[package]] name = "libcosmic" version = "0.1.0" +source = "git+https://github.com/pop-os/libcosmic?branch=ci_add_without_shell#99842258b4d77dcb0657b7c299bae45579e139a1" dependencies = [ "apply", "cosmic-theme", @@ -1870,8 +1892,8 @@ dependencies = [ "iced_core", "iced_lazy", "iced_native", + "iced_softbuffer", "iced_style", - "iced_swbuf", "lazy_static", "palette", "slotmap", @@ -1924,7 +1946,7 @@ dependencies = [ "hmac 0.11.0", "libc", "log", - "nix 0.23.1", + "nix 0.23.2", "once_cell", "serde", "sha2 0.9.9", @@ -1995,6 +2017,17 @@ dependencies = [ "log", ] +[[package]] +name = "lyon_geom" +version = "0.16.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6ce4e12203c428a58200b8cf1c0a3aad1cda907008ea11310bb3729593e5f933" +dependencies = [ + "arrayvec 0.5.2", + "euclid", + "num-traits", +] + [[package]] name = "malloc_buf" version = "0.0.6" @@ -2246,9 +2279,9 @@ checksum = "2bf50223579dc7cdcfb3bfcacf7069ff68243f8c363f62ffa99cf000a6b9c451" [[package]] name = "nom" -version = "7.1.2" +version = "7.1.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e5507769c4919c998e69e49c839d9dc6e693ede4cc4290d6ad8b41d4f09c548c" +checksum = "d273983c5a657a70a3e8f2a01329822f3b8c8172b73826411a55751e404a0a4a" dependencies = [ "memchr", "minimal-lexical", @@ -2345,9 +2378,9 @@ dependencies = [ [[package]] name = "object" -version = "0.30.0" +version = "0.30.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "239da7f290cfa979f43f85a8efeee9a8a76d0827c356d37f9d3d7254d6b537fb" +checksum = "2b8c786513eb403643f2a88c244c2aaa270ef2153f55094587d0c48a3cf22a83" dependencies = [ "memchr", ] @@ -2358,6 +2391,12 @@ version = "1.17.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "6f61fba1741ea2b3d6a1e3178721804bb716a68a6aeba1149b5d52e3d464ea66" +[[package]] +name = "opaque-debug" +version = "0.3.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "624a8340c38c1b80fd549087862da4ba43e08858af025b236e509b6649fc13d5" + [[package]] name = "ordered-float" version = "3.4.0" @@ -2424,19 +2463,13 @@ dependencies = [ "syn", ] -[[package]] -name = "opaque-debug" -version = "0.3.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "624a8340c38c1b80fd549087862da4ba43e08858af025b236e509b6649fc13d5" - [[package]] name = "owned_ttf_parser" -version = "0.17.1" +version = "0.18.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "18904d3c65493a9f0d7542293d1a7f69bfdc309a6b9ef4f46dc3e58b0577edc5" +checksum = "2a5f3c7ca08b6879e7965fb25e24d1f5eeb32ea73f9ad99b3854778a38c57e93" dependencies = [ - "ttf-parser 0.17.1", + "ttf-parser 0.18.1", ] [[package]] @@ -2482,7 +2515,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "3742b2c103b9f06bc9fff0a37ff4912935851bee6d36f3c02bcc755bcfec228f" dependencies = [ "lock_api", - "parking_lot_core 0.9.5", + "parking_lot_core 0.9.6", ] [[package]] @@ -2501,9 +2534,9 @@ dependencies = [ [[package]] name = "parking_lot_core" -version = "0.9.5" +version = "0.9.6" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7ff9f3fef3968a3ec5945535ed654cb38ff72d7495a25619e2247fb15a2ed9ba" +checksum = "ba1ef8814b5c993410bb3adfad7a5ed269563e4a2f90c41f5d85be7fb47133bf" dependencies = [ "cfg-if", "libc", @@ -2827,9 +2860,9 @@ dependencies = [ [[package]] name = "regex" -version = "1.7.0" +version = "1.7.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e076559ef8e241f2ae3479e36f97bd5741c0330689e217ad51ce2c76808b868a" +checksum = "48aaa5748ba571fb95cd2c85c09f629215d3a6ece942baa100950af03a34f733" dependencies = [ "aho-corasick", "memchr", @@ -3280,7 +3313,7 @@ dependencies = [ "wayland-server", "wayland-sys 0.30.1", "winit", - "x11rb", + "x11rb 0.10.1", "xkbcommon 0.5.0", ] @@ -3326,12 +3359,17 @@ checksum = "27207bb65232eda1f588cf46db2fee75c0808d557f6b3cf19a75f5d6d7c94df1" [[package]] name = "softbuffer" -version = "0.1.1" -source = "git+https://github.com/rust-windowing/softbuffer?rev=d5bb2c1#d5bb2c1c78811854d11225ff7cc29f0062781333" +version = "0.2.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3177eca2c15033e254b9b70c4915150200b1cf6fa777de18be9977ae5850077f" dependencies = [ + "bytemuck", + "cfg_aliases", "cocoa", "core-graphics", + "fastrand", "foreign-types", + "log", "nix 0.26.1", "objc", "raw-window-handle 0.5.0", @@ -3344,6 +3382,7 @@ dependencies = [ "web-sys", "windows-sys 0.42.0", "x11-dl", + "x11rb 0.11.1", ] [[package]] @@ -3493,9 +3532,9 @@ dependencies = [ [[package]] name = "termcolor" -version = "1.1.3" +version = "1.2.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "bab24d30b911b2376f3a13cc2cd443142f0c81dda04c118693e35b3835757755" +checksum = "be55cf8942feac5c765c2c993422806843c9a9a45d4d5c407ad6dd2ea95eb9b6" dependencies = [ "winapi-util", ] @@ -3639,6 +3678,12 @@ version = "0.17.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "375812fa44dab6df41c195cd2f7fecb488f6c09fbaafb62807488cefab642bff" +[[package]] +name = "ttf-parser" +version = "0.18.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0609f771ad9c6155384897e1df4d948e692667cc0588548b68eb44d052b27633" + [[package]] name = "twox-hash" version = "1.6.3" @@ -3652,9 +3697,9 @@ dependencies = [ [[package]] name = "typed-arena" -version = "2.0.1" +version = "2.0.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "0685c84d5d54d1c26f7d3eb96cd41550adb97baed141a761cf335d3d33bcd0ae" +checksum = "6af6ae20167a9ece4bcb41af5b80f8a1f1df981f6391189ce00fd257af04126a" [[package]] name = "typenum" @@ -4280,19 +4325,19 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "5a3e1820f08b8513f676f7ab6c1f99ff312fb97b553d30ff4dd86f9f15728aa7" dependencies = [ "windows_aarch64_gnullvm", - "windows_aarch64_msvc 0.42.0", - "windows_i686_gnu 0.42.0", - "windows_i686_msvc 0.42.0", - "windows_x86_64_gnu 0.42.0", + "windows_aarch64_msvc 0.42.1", + "windows_i686_gnu 0.42.1", + "windows_i686_msvc 0.42.1", + "windows_x86_64_gnu 0.42.1", "windows_x86_64_gnullvm", - "windows_x86_64_msvc 0.42.0", + "windows_x86_64_msvc 0.42.1", ] [[package]] name = "windows_aarch64_gnullvm" -version = "0.42.0" +version = "0.42.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "41d2aa71f6f0cbe00ae5167d90ef3cfe66527d6f613ca78ac8024c3ccab9a19e" +checksum = "8c9864e83243fdec7fc9c5444389dcbbfd258f745e7853198f365e3c4968a608" [[package]] name = "windows_aarch64_msvc" @@ -4302,9 +4347,9 @@ checksum = "9bb8c3fd39ade2d67e9874ac4f3db21f0d710bee00fe7cab16949ec184eeaa47" [[package]] name = "windows_aarch64_msvc" -version = "0.42.0" +version = "0.42.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "dd0f252f5a35cac83d6311b2e795981f5ee6e67eb1f9a7f64eb4500fbc4dcdb4" +checksum = "4c8b1b673ffc16c47a9ff48570a9d85e25d265735c503681332589af6253c6c7" [[package]] name = "windows_i686_gnu" @@ -4314,9 +4359,9 @@ checksum = "180e6ccf01daf4c426b846dfc66db1fc518f074baa793aa7d9b9aaeffad6a3b6" [[package]] name = "windows_i686_gnu" -version = "0.42.0" +version = "0.42.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "fbeae19f6716841636c28d695375df17562ca208b2b7d0dc47635a50ae6c5de7" +checksum = "de3887528ad530ba7bdbb1faa8275ec7a1155a45ffa57c37993960277145d640" [[package]] name = "windows_i686_msvc" @@ -4326,9 +4371,9 @@ checksum = "e2e7917148b2812d1eeafaeb22a97e4813dfa60a3f8f78ebe204bcc88f12f024" [[package]] name = "windows_i686_msvc" -version = "0.42.0" +version = "0.42.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "84c12f65daa39dd2babe6e442988fc329d6243fdce47d7d2d155b8d874862246" +checksum = "bf4d1122317eddd6ff351aa852118a2418ad4214e6613a50e0191f7004372605" [[package]] name = "windows_x86_64_gnu" @@ -4338,15 +4383,15 @@ checksum = "4dcd171b8776c41b97521e5da127a2d86ad280114807d0b2ab1e462bc764d9e1" [[package]] name = "windows_x86_64_gnu" -version = "0.42.0" +version = "0.42.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "bf7b1b21b5362cbc318f686150e5bcea75ecedc74dd157d874d754a2ca44b0ed" +checksum = "c1040f221285e17ebccbc2591ffdc2d44ee1f9186324dd3e84e99ac68d699c45" [[package]] name = "windows_x86_64_gnullvm" -version = "0.42.0" +version = "0.42.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "09d525d2ba30eeb3297665bd434a54297e4170c7f1a44cad4ef58095b4cd2028" +checksum = "628bfdf232daa22b0d64fdb62b09fcc36bb01f05a3939e20ab73aaf9470d0463" [[package]] name = "windows_x86_64_msvc" @@ -4356,9 +4401,9 @@ checksum = "c811ca4a8c853ef420abd8592ba53ddbbac90410fab6903b3e79972a631f7680" [[package]] name = "windows_x86_64_msvc" -version = "0.42.0" +version = "0.42.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f40009d85759725a34da6d89a94e63d7bdc50a862acf0dbc7c8e488f1edcb6f5" +checksum = "447660ad36a13288b1db4d4248e857b510e8c3a225c822ba4fb748c0aafecffd" [[package]] name = "winit" @@ -4422,7 +4467,23 @@ dependencies = [ "nix 0.24.3", "winapi", "winapi-wsapoll", - "x11rb-protocol", + "x11rb-protocol 0.10.0", +] + +[[package]] +name = "x11rb" +version = "0.11.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "cdf3c79412dd91bae7a7366b8ad1565a85e35dd049affc3a6a2c549e97419617" +dependencies = [ + "gethostname", + "libc", + "libloading", + "nix 0.25.1", + "once_cell", + "winapi", + "winapi-wsapoll", + "x11rb-protocol 0.11.1", ] [[package]] @@ -4434,6 +4495,15 @@ dependencies = [ "nix 0.24.3", ] +[[package]] +name = "x11rb-protocol" +version = "0.11.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e0b1513b141123073ce54d5bb1d33f801f17508fbd61e02060b1214e96d39c56" +dependencies = [ + "nix 0.25.1", +] + [[package]] name = "xcursor" version = "0.3.4" diff --git a/Cargo.toml b/Cargo.toml index dc7cf880..d9cec7cd 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -38,8 +38,9 @@ libsystemd = "0.5" wayland-backend = "0.1.0" wayland-scanner = "0.30.0" cosmic-protocols = { git = "https://github.com/pop-os/cosmic-protocols", branch = "main", default-features = false, features = ["server"] } -libcosmic = { path = "../libcosmic", default-features = false, features = ["swbuf"] } #{ git = "https://github.com/pop-os/libcosmic", rev = "444e389496", default-features = false, features = ["swbuf"] } -iced_swbuf = { path = "../libcosmic/iced/swbuf" } +libcosmic = { git = "https://github.com/pop-os/libcosmic", branch = "ci_add_without_shell", default-features = false, features = ["softbuffer"] } +iced_softbuffer = { git = "https://github.com/pop-os/libcosmic", branch = "ci_add_without_shell" } +ordered-float = "3.0" [dependencies.smithay] version = "0.3" diff --git a/src/backend/kms/mod.rs b/src/backend/kms/mod.rs index 1f93c067..bd8680f7 100644 --- a/src/backend/kms/mod.rs +++ b/src/backend/kms/mod.rs @@ -46,7 +46,7 @@ use smithay::{ wayland_server::{protocol::wl_surface::WlSurface, DisplayHandle, Resource}, }, utils::{DeviceFd, Size, Transform}, - wayland::dmabuf::DmabufGlobal, + wayland::{dmabuf::DmabufGlobal, seat::WaylandFocus}, }; use std::{ @@ -787,7 +787,7 @@ fn render_node_for_output( .unwrap_or_else(|| workspace.windows().collect::>()) .into_iter() .flat_map(|w| { - dh.get_client(w.toplevel().wl_surface().id()) + dh.get_client(w.wl_surface()?.id()) .ok()? .get_data::() .unwrap() diff --git a/src/backend/render/element.rs b/src/backend/render/element.rs index f1776e90..c5a10e65 100644 --- a/src/backend/render/element.rs +++ b/src/backend/render/element.rs @@ -4,7 +4,7 @@ use smithay::{ backend::renderer::{ element::{Element, RenderElement, UnderlyingStorage}, glow::{GlowFrame, GlowRenderer}, - Frame, ImportAll, Renderer, + Frame, ImportAll, ImportMem, Renderer, }, utils::{Buffer as BufferCoords, Physical, Point, Rectangle, Scale}, }; @@ -18,7 +18,7 @@ use super::{cursor::CursorRenderElement, GlMultiFrame, GlMultiRenderer}; pub enum CosmicElement where - R: AsGlowRenderer + Renderer + ImportAll, + R: AsGlowRenderer + Renderer + ImportAll + ImportMem, ::TextureId: 'static, CosmicMappedRenderElement: RenderElement, { @@ -31,7 +31,7 @@ where impl Element for CosmicElement where - R: AsGlowRenderer + Renderer + ImportAll, + R: AsGlowRenderer + Renderer + ImportAll + ImportMem, ::TextureId: 'static, CosmicMappedRenderElement: RenderElement, { @@ -203,7 +203,7 @@ impl<'a> RenderElement> for CosmicElement From> for CosmicElement where - R: Renderer + ImportAll + AsGlowRenderer, + R: Renderer + ImportAll + ImportMem + AsGlowRenderer, ::TextureId: 'static, CosmicMappedRenderElement: RenderElement, { @@ -214,7 +214,7 @@ where impl From> for CosmicElement where - R: Renderer + ImportAll + AsGlowRenderer, + R: Renderer + ImportAll + ImportMem + AsGlowRenderer, ::TextureId: 'static, CosmicMappedRenderElement: RenderElement, { @@ -225,7 +225,7 @@ where impl From> for CosmicElement where - R: Renderer + ImportAll + AsGlowRenderer, + R: Renderer + ImportAll + ImportMem + AsGlowRenderer, ::TextureId: 'static, CosmicMappedRenderElement: RenderElement, { @@ -237,7 +237,7 @@ where #[cfg(feature = "debug")] impl From> for CosmicElement where - R: Renderer + ImportAll + AsGlowRenderer, + R: Renderer + ImportAll + ImportMem + AsGlowRenderer, ::TextureId: 'static, CosmicMappedRenderElement: RenderElement, { diff --git a/src/logger/mod.rs b/src/logger/mod.rs index 08a0c009..ddefa19f 100644 --- a/src/logger/mod.rs +++ b/src/logger/mod.rs @@ -1,7 +1,7 @@ // SPDX-License-Identifier: GPL-3.0-only use anyhow::Result; -use slog::Drain; +use slog::{Drain, Level}; pub struct LogState { _guard: slog_scope::GlobalLoggerGuard, @@ -16,7 +16,27 @@ pub fn init_logger() -> Result { let drain = slog::Duplicate::new(term_drain, journald_drain); // usually we would not want to use a Mutex here, but this is usefull for a prototype, // to make sure we do not miss any in-flight messages, when we crash. - let logger = slog::Logger::root(std::sync::Mutex::new(drain).fuse(), slog::o!()); + let logger = slog::Logger::root( + std::sync::Mutex::new(drain.filter(|record| { + if record.module().starts_with("smithay") || record.module().starts_with("cosmic_comp") + { + return true; + } + + if record.module().contains("cosmic_text") { + // cosmic-text is very chatty + return record.level().is_at_least(Level::Error); + } + + if cfg!(debug_assertions) { + record.level().is_at_least(Level::Warning) + } else { + record.level().is_at_least(Level::Error) + } + })) + .fuse(), + slog::o!(), + ); let _guard = slog_scope::set_global_logger(logger); slog_stdlog::init_with_level(if cfg!(debug_assertions) { diff --git a/src/shell/element/mod.rs b/src/shell/element/mod.rs index abf8b259..e2d50992 100644 --- a/src/shell/element/mod.rs +++ b/src/shell/element/mod.rs @@ -10,28 +10,24 @@ use smithay::{ renderer::{ element::{AsRenderElements, Element, RenderElement, UnderlyingStorage}, glow::GlowRenderer, - ImportAll, Renderer, + ImportAll, ImportMem, Renderer, }, }, - desktop::{space::SpaceElement, PopupManager, Window, WindowSurfaceType}, + desktop::{space::SpaceElement, PopupManager, WindowSurfaceType}, input::{ keyboard::{KeyboardTarget, KeysymHandle, ModifiersState}, pointer::{AxisFrame, ButtonEvent, MotionEvent, PointerTarget}, Seat, }, output::Output, - reexports::{ - wayland_protocols::xdg::shell::server::xdg_toplevel::State as XdgState, - wayland_server::{backend::ObjectId, protocol::wl_surface::WlSurface}, - }, + reexports::wayland_server::{backend::ObjectId, protocol::wl_surface::WlSurface}, space_elements, utils::{ Buffer as BufferCoords, IsAlive, Logical, Physical, Point, Rectangle, Scale, Serial, Size, }, wayland::{ - compositor::{with_states, with_surface_tree_downward, TraversalAction}, + compositor::{with_surface_tree_downward, TraversalAction}, seat::WaylandFocus, - shell::xdg::XdgToplevelSurfaceRoleAttributes, }, }; use std::{ @@ -41,6 +37,8 @@ use std::{ sync::{Arc, Mutex}, }; +pub mod surface; +pub use self::surface::CosmicSurface; pub mod stack; pub use self::stack::CosmicStack; pub mod window; @@ -110,68 +108,48 @@ impl Hash for CosmicMapped { } impl CosmicMapped { - pub fn windows(&self) -> impl Iterator)> + '_ { + pub fn windows(&self) -> impl Iterator)> + '_ { match &self.element { - CosmicMappedInternal::Stack(stack) => Box::new(stack.windows().map(|w| { - ( - w, - stack - .header - .lock() - .unwrap() - .as_ref() - .map(|header| Point::from((0, header.height() as i32))) - .unwrap_or(Point::from((0, 0))), - ) - })) - as Box)>>, - CosmicMappedInternal::Window(window) => Box::new(std::iter::once(( - window.window.clone(), - window - .header - .lock() - .unwrap() - .as_ref() - .map(|header| Point::from((0, header.height() as i32))) - .unwrap_or(Point::from((0, 0))), - ))), + CosmicMappedInternal::Stack(stack) => { + Box::new(stack.surfaces().map(|w| (w, stack.offset()))) + as Box)>> + } + CosmicMappedInternal::Window(window) => { + Box::new(std::iter::once((window.surface(), window.offset()))) + } _ => Box::new(std::iter::empty()), } } - pub fn active_window(&self) -> Window { + pub fn active_window(&self) -> CosmicSurface { match &self.element { CosmicMappedInternal::Stack(stack) => stack.active(), - CosmicMappedInternal::Window(win) => win.window.clone(), + CosmicMappedInternal::Window(win) => win.surface(), _ => unreachable!(), } } - pub fn active_window_offset(&self) -> Rectangle { + pub fn active_window_offset(&self) -> Point { + match &self.element { + CosmicMappedInternal::Stack(stack) => stack.offset(), + CosmicMappedInternal::Window(window) => window.offset(), + _ => unreachable!(), + } + } + + pub fn active_window_geometry(&self) -> Rectangle { match &self.element { CosmicMappedInternal::Stack(stack) => { - let location = ( - 0, - stack - .header - .lock() - .unwrap() - .as_ref() - .map_or(0, |header| header.height()), - ); - let size = stack.active().geometry().size; + let win = stack.active(); + let location = stack.offset(); + let mut size = win.geometry().size; + size -= location.to_size(); Rectangle::from_loc_and_size(location, size) } CosmicMappedInternal::Window(win) => { - let location = ( - 0, - win.header - .lock() - .unwrap() - .as_ref() - .map_or(0, |header| header.height()), - ); - let size = win.window.geometry().size; + let location = win.offset(); + let mut size = win.geometry().size; + size -= location.to_size(); Rectangle::from_loc_and_size(location, size) } _ => unreachable!(), @@ -186,13 +164,13 @@ impl CosmicMapped { .cloned() } - pub fn set_active(&self, window: &Window) { + pub fn set_active(&self, window: &CosmicSurface) { if let CosmicMappedInternal::Stack(stack) = &self.element { stack.set_active(window); } } - pub fn focus_window(&self, window: &Window) { + pub fn focus_window(&self, window: &CosmicSurface) { match &self.element { CosmicMappedInternal::Stack(stack) => stack.set_active(window), _ => {} @@ -201,10 +179,10 @@ impl CosmicMapped { pub fn has_surface(&self, surface: &WlSurface, surface_type: WindowSurfaceType) -> bool { self.windows().any(|(w, _)| { - let toplevel = w.toplevel().wl_surface(); + let Some(toplevel ) = w.wl_surface() else { return false }; if surface_type.contains(WindowSurfaceType::TOPLEVEL) { - if toplevel == surface { + if toplevel == *surface { return true; } } @@ -214,7 +192,7 @@ impl CosmicMapped { let found = AtomicBool::new(false); with_surface_tree_downward( - toplevel, + &toplevel, surface, |_, _, search| TraversalAction::DoChildren(search), |s, _, search| { @@ -228,7 +206,7 @@ impl CosmicMapped { } if surface_type.contains(WindowSurfaceType::POPUP) { - PopupManager::popups_for_surface(toplevel).any(|(p, _)| p.wl_surface() == surface) + PopupManager::popups_for_surface(&toplevel).any(|(p, _)| p.wl_surface() == surface) } else { false } @@ -247,158 +225,110 @@ impl CosmicMapped { pub fn set_resizing(&self, resizing: bool) { for window in match &self.element { CosmicMappedInternal::Stack(s) => { - Box::new(s.windows()) as Box> + Box::new(s.surfaces()) as Box> } - CosmicMappedInternal::Window(w) => Box::new(std::iter::once(w.window.clone())), + CosmicMappedInternal::Window(w) => Box::new(std::iter::once(w.surface())), _ => unreachable!(), } { - window.toplevel().with_pending_state(|state| { - if resizing { - state.states.set(XdgState::Resizing); - } else { - state.states.unset(XdgState::Resizing); - } - }); + window.set_resizing(resizing); } } - pub fn is_resizing(&self) -> bool { + pub fn is_resizing(&self) -> Option { let window = match &self.element { CosmicMappedInternal::Stack(s) => s.active(), - CosmicMappedInternal::Window(w) => w.window.clone(), + CosmicMappedInternal::Window(w) => w.surface(), _ => unreachable!(), }; - let xdg = window.toplevel(); - xdg.current_state().states.contains(XdgState::Resizing) - || xdg.with_pending_state(|states| states.states.contains(XdgState::Resizing)) + window.is_resizing() } pub fn set_tiled(&self, tiled: bool) { - for xdg in match &self.element { + for window in match &self.element { // we use the tiled state of stack windows anyway to get rid of decorations CosmicMappedInternal::Stack(_) => None, - CosmicMappedInternal::Window(w) => Some(w.window.toplevel()), + CosmicMappedInternal::Window(w) => Some(w.surface()), _ => unreachable!(), } { - xdg.with_pending_state(|state| { - if tiled { - state.states.set(XdgState::TiledLeft); - state.states.set(XdgState::TiledRight); - state.states.set(XdgState::TiledTop); - state.states.set(XdgState::TiledBottom); - } else { - state.states.unset(XdgState::TiledLeft); - state.states.unset(XdgState::TiledRight); - state.states.unset(XdgState::TiledTop); - state.states.unset(XdgState::TiledBottom); - } - }); + window.set_tiled(tiled) } } - pub fn is_tiled(&self) -> bool { + pub fn is_tiled(&self) -> Option { let window = match &self.element { CosmicMappedInternal::Stack(s) => s.active(), - CosmicMappedInternal::Window(w) => w.window.clone(), + CosmicMappedInternal::Window(w) => w.surface(), _ => unreachable!(), }; - window - .toplevel() - .current_state() - .states - .contains(XdgState::TiledLeft) + window.is_tiled() } pub fn set_fullscreen(&self, fullscreen: bool) { for window in match &self.element { CosmicMappedInternal::Stack(s) => { - Box::new(s.windows()) as Box> + Box::new(s.surfaces()) as Box> } - CosmicMappedInternal::Window(w) => Box::new(std::iter::once(w.window.clone())), + CosmicMappedInternal::Window(w) => Box::new(std::iter::once(w.surface())), _ => unreachable!(), } { - window.toplevel().with_pending_state(|state| { - if fullscreen { - state.states.set(XdgState::Fullscreen); - } else { - state.states.unset(XdgState::Fullscreen); - } - }); + window.set_fullscreen(fullscreen); } } pub fn is_fullscreen(&self) -> bool { let window = match &self.element { CosmicMappedInternal::Stack(s) => s.active(), - CosmicMappedInternal::Window(w) => w.window.clone(), + CosmicMappedInternal::Window(w) => w.surface(), _ => unreachable!(), }; - let xdg = window.toplevel(); - xdg.current_state().states.contains(XdgState::Fullscreen) - || xdg.with_pending_state(|states| states.states.contains(XdgState::Fullscreen)) + window.is_fullscreen() } pub fn set_maximized(&self, maximized: bool) { for window in match &self.element { CosmicMappedInternal::Stack(s) => { - Box::new(s.windows()) as Box> + Box::new(s.surfaces()) as Box> } - CosmicMappedInternal::Window(w) => Box::new(std::iter::once(w.window.clone())), + CosmicMappedInternal::Window(w) => Box::new(std::iter::once(w.surface())), _ => unreachable!(), } { - window.toplevel().with_pending_state(|state| { - if maximized { - state.states.set(XdgState::Maximized); - } else { - state.states.unset(XdgState::Maximized); - } - }); + window.set_maximized(maximized) } } pub fn is_maximized(&self) -> bool { let window = match &self.element { CosmicMappedInternal::Stack(s) => s.active(), - CosmicMappedInternal::Window(w) => w.window.clone(), + CosmicMappedInternal::Window(w) => w.surface(), _ => unreachable!(), }; - let xdg = window.toplevel(); - xdg.current_state().states.contains(XdgState::Maximized) - || xdg.with_pending_state(|states| states.states.contains(XdgState::Maximized)) + window.is_maximized() } pub fn set_activated(&self, activated: bool) { for window in match &self.element { CosmicMappedInternal::Stack(s) => { - Box::new(s.windows()) as Box> + Box::new(s.surfaces()) as Box> } - CosmicMappedInternal::Window(w) => Box::new(std::iter::once(w.window.clone())), + CosmicMappedInternal::Window(w) => Box::new(std::iter::once(w.surface())), _ => unreachable!(), } { - window.toplevel().with_pending_state(|state| { - if activated { - state.states.set(XdgState::Activated); - } else { - state.states.unset(XdgState::Activated); - } - }); + window.set_activated(activated) } } pub fn is_activated(&self) -> bool { let window = match &self.element { CosmicMappedInternal::Stack(s) => s.active(), - CosmicMappedInternal::Window(w) => w.window.clone(), + CosmicMappedInternal::Window(w) => w.surface(), _ => unreachable!(), }; - let xdg = window.toplevel(); - xdg.current_state().states.contains(XdgState::Activated) - || xdg.with_pending_state(|states| states.states.contains(XdgState::Activated)) + window.is_activated() } pub fn set_size(&self, size: Size) { @@ -409,57 +339,34 @@ impl CosmicMapped { } } - pub fn min_size(&self) -> Size { + pub fn min_size(&self) -> Option> { match &self.element { - CosmicMappedInternal::Stack(stack) => stack - .windows() - .fold(None, |min_size, window| { - let win_min_size = with_states(window.toplevel().wl_surface(), |states| { - let attrs = states - .data_map - .get::>() - .unwrap() - .lock() - .unwrap(); - attrs.min_size - }); + CosmicMappedInternal::Stack(stack) => { + stack.surfaces().fold(None, |min_size, window| { + let win_min_size = window.min_size(); match (min_size, win_min_size) { - (None, x) => Some(x), - (Some(min1), min2) => Some((min1.w.max(min2.w), min1.h.max(min2.h)).into()), + (None, None) => None, + (None, x) | (x, None) => x, + (Some(min1), Some(min2)) => { + Some((min1.w.max(min2.w), min1.h.max(min2.h)).into()) + } } }) - .expect("Empty stack?"), - CosmicMappedInternal::Window(window) => { - with_states(window.window.toplevel().wl_surface(), |states| { - let attrs = states - .data_map - .get::>() - .unwrap() - .lock() - .unwrap(); - attrs.min_size - }) } + CosmicMappedInternal::Window(window) => window.surface().min_size(), _ => unreachable!(), } } - pub fn max_size(&self) -> Size { + pub fn max_size(&self) -> Option> { match &self.element { CosmicMappedInternal::Stack(stack) => { - let theoretical_max = stack.windows().fold(None, |max_size, window| { - let win_max_size = with_states(window.toplevel().wl_surface(), |states| { - let attrs = states - .data_map - .get::>() - .unwrap() - .lock() - .unwrap(); - attrs.max_size - }); + let theoretical_max = stack.surfaces().fold(None, |max_size, window| { + let win_max_size = window.max_size(); match (max_size, win_max_size) { - (None, x) => Some(x), - (Some(max1), max2) => Some( + (None, None) => None, + (None, x) | (x, None) => x, + (Some(max1), Some(max2)) => Some( ( if max1.w == 0 { max2.w @@ -483,21 +390,12 @@ impl CosmicMapped { // The problem is, with accumulated sizes, the minimum size could be larger than our maximum... let min_size = self.min_size(); match (theoretical_max, min_size) { - (None, _) => (0, 0).into(), - (Some(max), min) => (max.w.max(min.w), max.h.max(min.h)).into(), + (None, _) => None, + (Some(max), None) => Some(max), + (Some(max), Some(min)) => Some((max.w.max(min.w), max.h.max(min.h)).into()), } } - CosmicMappedInternal::Window(window) => { - with_states(window.window.toplevel().wl_surface(), |states| { - let attrs = states - .data_map - .get::>() - .unwrap() - .lock() - .unwrap(); - attrs.max_size - }) - } + CosmicMappedInternal::Window(window) => window.surface().max_size(), _ => unreachable!(), } } @@ -505,23 +403,23 @@ impl CosmicMapped { pub fn configure(&self) { for window in match &self.element { CosmicMappedInternal::Stack(s) => { - Box::new(s.windows()) as Box> + Box::new(s.surfaces()) as Box> } - CosmicMappedInternal::Window(w) => Box::new(std::iter::once(w.window.clone())), + CosmicMappedInternal::Window(w) => Box::new(std::iter::once(w.surface())), _ => unreachable!(), } { - window.toplevel().send_configure(); + window.send_configure(); } } pub fn send_close(&self) { let window = match &self.element { CosmicMappedInternal::Stack(s) => s.active(), - CosmicMappedInternal::Window(w) => w.window.clone(), + CosmicMappedInternal::Window(w) => w.surface(), _ => unreachable!(), }; - window.toplevel().send_close(); + window.close(); } #[cfg(feature = "debug")] @@ -680,7 +578,7 @@ impl PointerTarget for CosmicMapped { impl WaylandFocus for CosmicMapped { fn wl_surface(&self) -> Option { match &self.element { - CosmicMappedInternal::Window(w) => w.window.wl_surface().clone(), + CosmicMappedInternal::Window(w) => w.surface().wl_surface().clone(), CosmicMappedInternal::Stack(s) => s.active().wl_surface().clone(), _ => None, } @@ -688,8 +586,8 @@ impl WaylandFocus for CosmicMapped { fn same_client_as(&self, object_id: &ObjectId) -> bool { match &self.element { - CosmicMappedInternal::Window(w) => w.window.same_client_as(object_id), - CosmicMappedInternal::Stack(s) => s.windows().any(|w| w.same_client_as(object_id)), + CosmicMappedInternal::Window(w) => w.surface().same_client_as(object_id), + CosmicMappedInternal::Stack(s) => s.surfaces().any(|w| w.same_client_as(object_id)), _ => false, } } @@ -725,7 +623,7 @@ impl From for CosmicMapped { pub enum CosmicMappedRenderElement where - R: AsGlowRenderer + Renderer + ImportAll, + R: AsGlowRenderer + Renderer + ImportAll + ImportMem, ::TextureId: 'static, { Stack(self::stack::CosmicStackRenderElement), @@ -736,7 +634,7 @@ where impl Element for CosmicMappedRenderElement where - R: AsGlowRenderer + Renderer + ImportAll, + R: AsGlowRenderer + Renderer + ImportAll + ImportMem, ::TextureId: 'static, { fn id(&self) -> &smithay::backend::renderer::element::Id { @@ -892,7 +790,7 @@ impl<'a> RenderElement> for CosmicMappedRenderElement From> for CosmicMappedRenderElement where - R: Renderer + ImportAll + AsGlowRenderer, + R: Renderer + ImportAll + ImportMem + AsGlowRenderer, ::TextureId: 'static, CosmicMappedRenderElement: RenderElement, { @@ -902,7 +800,7 @@ where } impl From> for CosmicMappedRenderElement where - R: Renderer + ImportAll + AsGlowRenderer, + R: Renderer + ImportAll + ImportMem + AsGlowRenderer, ::TextureId: 'static, CosmicMappedRenderElement: RenderElement, { @@ -924,7 +822,7 @@ where impl AsRenderElements for CosmicMapped where - R: Renderer + ImportAll + AsGlowRenderer, + R: Renderer + ImportAll + ImportMem + AsGlowRenderer, ::TextureId: 'static, CosmicMappedRenderElement: RenderElement, { diff --git a/src/shell/element/stack.rs b/src/shell/element/stack.rs index 3e14e326..6034bae7 100644 --- a/src/shell/element/stack.rs +++ b/src/shell/element/stack.rs @@ -1,16 +1,24 @@ use crate::{ - state::State, utils::prelude::SeatExt, wayland::handlers::screencopy::ScreencopySessions, + state::State, + utils::iced::{IcedElement, Program}, + utils::prelude::SeatExt, + wayland::handlers::screencopy::ScreencopySessions, }; +use calloop::LoopHandle; +use cosmic::Element; use cosmic_protocols::screencopy::v1::server::zcosmic_screencopy_session_v1::InputType; use smithay::{ backend::{ input::KeyState, renderer::{ - element::{surface::WaylandSurfaceRenderElement, AsRenderElements}, - ImportAll, Renderer, + element::{ + memory::MemoryRenderBufferRenderElement, surface::WaylandSurfaceRenderElement, + AsRenderElements, + }, + ImportAll, ImportMem, Renderer, }, }, - desktop::{space::SpaceElement, Window}, + desktop::space::SpaceElement, input::{ keyboard::{KeyboardTarget, KeysymHandle, ModifiersState}, pointer::{AxisFrame, ButtonEvent, MotionEvent, PointerTarget}, @@ -21,84 +29,136 @@ use smithay::{ utils::{IsAlive, Logical, Physical, Point, Rectangle, Scale, Serial, Size}, }; use std::{ + fmt, hash::Hash, sync::{ - atomic::{AtomicUsize, Ordering}, + atomic::{AtomicU8, AtomicUsize, Ordering}, Arc, Mutex, }, }; +use super::CosmicSurface; + +#[derive(Clone, PartialEq, Eq, Hash)] +pub struct CosmicStack(IcedElement); + +impl fmt::Debug for CosmicStack { + fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { + self.0.with_program(|stack| { + f.debug_struct("CosmicStack") + .field("internal", stack) + .finish_non_exhaustive() + }) + } +} + #[derive(Debug, Clone)] -pub struct CosmicStack { - windows: Arc>>, +pub struct CosmicStackInternal { + windows: Arc>>, active: Arc, - last_location: Arc, Serial, u32)>>>, previous_keyboard: Arc, + pointer_entered: Option>, previous_pointer: Arc, - pub(super) header: Arc>>, + last_location: Arc, Serial, u32)>>>, } -impl PartialEq for CosmicStack { - fn eq(&self, other: &Self) -> bool { - *self.windows.lock().unwrap() == *other.windows.lock().unwrap() +impl CosmicStackInternal { + pub fn swap_focus(&self, focus: Focus) -> Focus { + if let Some(pointer_entered) = self.pointer_entered.as_ref() { + unsafe { + std::mem::transmute::( + pointer_entered.swap(focus as u8, Ordering::SeqCst), + ) + } + } else { + Focus::Window + } + } + + pub fn current_focus(&self) -> Focus { + if let Some(pointer_entered) = self.pointer_entered.as_ref() { + unsafe { std::mem::transmute::(pointer_entered.load(Ordering::SeqCst)) } + } else { + Focus::Window + } } } -impl Eq for CosmicStack {} +const TAB_HEIGHT: i32 = 24; -impl Hash for CosmicStack { - fn hash(&self, state: &mut H) { - Arc::as_ptr(&self.windows).hash(state) - } -} - -#[derive(Debug)] -pub struct HeaderBar {} - -impl HeaderBar { - pub fn height(&self) -> i32 { - 0 - } +#[repr(u8)] +#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)] +pub enum Focus { + None, + Header, + Window, } impl CosmicStack { - pub fn active(&self) -> Window { - self.windows.lock().unwrap()[self.active.load(Ordering::SeqCst)].clone() + pub fn new( + window: impl Into, + handle: LoopHandle<'static, crate::state::Data>, + ) -> CosmicStack { + let window = window.into(); + let width = window.geometry().size.w; + CosmicStack(IcedElement::new( + CosmicStackInternal { + windows: Arc::new(Mutex::new(vec![window])), + active: Arc::new(AtomicUsize::new(0)), + previous_keyboard: Arc::new(AtomicUsize::new(0)), + pointer_entered: None, + previous_pointer: Arc::new(AtomicUsize::new(0)), + last_location: Arc::new(Mutex::new(None)), + }, + (width, TAB_HEIGHT), + handle, + )) } - pub fn set_active(&self, window: &Window) { - if let Some(val) = self - .windows - .lock() - .unwrap() - .iter() - .position(|w| w == window) - { - let old = self.active.swap(val, Ordering::SeqCst); - self.previous_keyboard.store(old, Ordering::SeqCst); - self.previous_pointer.store(old, Ordering::SeqCst); - } + //pub fn add_window() + //pub fn remove_window() + //pub fn len + + pub fn active(&self) -> CosmicSurface { + self.0 + .with_program(|p| p.windows.lock().unwrap()[p.active.load(Ordering::SeqCst)].clone()) + } + + pub fn set_active(&self, window: &CosmicSurface) { + self.0.with_program(|p| { + if let Some(val) = p.windows.lock().unwrap().iter().position(|w| w == window) { + let old = p.active.swap(val, Ordering::SeqCst); + p.previous_keyboard.store(old, Ordering::SeqCst); + p.previous_pointer.store(old, Ordering::SeqCst); + } + }) + } + + pub fn surfaces(&self) -> impl Iterator { + self.0.with_program(|p| { + p.windows + .lock() + .unwrap() + .iter() + .cloned() + .collect::>() + .into_iter() + }) + } + + pub fn offset(&self) -> Point { + Point::from((0, TAB_HEIGHT)) } pub fn set_size(&self, size: Size) { - let surface_size = ( - size.w, - size.h - - self - .header - .lock() - .unwrap() - .as_ref() - .map(|h| h.height()) - .unwrap_or(0), - ) - .into(); + self.0.with_program(|p| { + let surface_size = (size.w, size.h - TAB_HEIGHT).into(); - for window in self.windows.lock().unwrap().iter() { - window - .toplevel() - .with_pending_state(|state| state.size = Some(surface_size)); - } + for window in p.windows.lock().unwrap().iter() { + window.set_size(surface_size); + } + }); + self.0.resize(Size::from((size.w, TAB_HEIGHT))); } fn keyboard_leave_if_previous( @@ -107,13 +167,21 @@ impl CosmicStack { data: &mut State, serial: Serial, ) -> usize { - let active = self.active.load(Ordering::SeqCst); - let previous = self.previous_keyboard.swap(active, Ordering::SeqCst); - if previous != active { - KeyboardTarget::leave(&self.windows.lock().unwrap()[previous], seat, data, serial); - // TODO: KeyboardTarget::enter(&self.windows.lock().unwrap()[active], seat, data, serial, seat.keys()) - } - active + self.0.with_program(|p| { + let active = p.active.load(Ordering::SeqCst); + let previous = p.previous_keyboard.swap(active, Ordering::SeqCst); + if previous != active { + KeyboardTarget::leave(&p.windows.lock().unwrap()[previous], seat, data, serial); + KeyboardTarget::enter( + &p.windows.lock().unwrap()[active], + seat, + data, + Vec::new(), //seat.keys(), + serial, + ) + } + active + }) } fn pointer_leave_if_previous( @@ -124,100 +192,141 @@ impl CosmicStack { time: u32, location: Point, ) -> usize { - let active = self.active.load(Ordering::SeqCst); - let previous = self.previous_pointer.swap(active, Ordering::SeqCst); - if previous != active { - if let Some(sessions) = self.windows.lock().unwrap()[previous] - .user_data() - .get::() - { - for session in &*sessions.0.borrow() { - session.cursor_leave(seat, InputType::Pointer) + self.0.with_program(|p| { + let active = p.active.load(Ordering::SeqCst); + let previous = p.previous_pointer.swap(active, Ordering::SeqCst); + if previous != active { + if let Some(sessions) = p.windows.lock().unwrap()[previous] + .user_data() + .get::() + { + for session in &*sessions.0.borrow() { + session.cursor_leave(seat, InputType::Pointer) + } } - } - PointerTarget::leave( - &self.windows.lock().unwrap()[previous], - seat, - data, - serial, - time, - ); - if let Some(sessions) = self.windows.lock().unwrap()[active] - .user_data() - .get::() - { - for session in &*sessions.0.borrow() { - session.cursor_enter(seat, InputType::Pointer) - } - } - PointerTarget::enter( - &self.windows.lock().unwrap()[active], - seat, - data, - &MotionEvent { - location, + PointerTarget::leave( + &p.windows.lock().unwrap()[previous], + seat, + data, serial, time, - }, - ); - } - active + ); + if let Some(sessions) = p.windows.lock().unwrap()[active] + .user_data() + .get::() + { + for session in &*sessions.0.borrow() { + session.cursor_enter(seat, InputType::Pointer) + } + } + PointerTarget::enter( + &p.windows.lock().unwrap()[active], + seat, + data, + &MotionEvent { + location, + serial, + time, + }, + ); + } + active + }) + } +} + +impl Program for CosmicStackInternal { + type Message = (); + + fn view(&self) -> Element<'_, Self::Message> { + cosmic::iced::widget::text("TODO").into() } } impl IsAlive for CosmicStack { fn alive(&self) -> bool { - self.windows.lock().unwrap().iter().any(IsAlive::alive) + self.0 + .with_program(|p| p.windows.lock().unwrap().iter().any(IsAlive::alive)) } } impl SpaceElement for CosmicStack { fn bbox(&self) -> Rectangle { - SpaceElement::bbox(&self.windows.lock().unwrap()[self.active.load(Ordering::SeqCst)]) + self.0.with_program(|p| { + let mut bbox = + SpaceElement::bbox(&p.windows.lock().unwrap()[p.active.load(Ordering::SeqCst)]); + bbox.size.h += TAB_HEIGHT; + bbox + }) } fn is_in_input_region(&self, point: &Point) -> bool { - SpaceElement::is_in_input_region( - &self.windows.lock().unwrap()[self.active.load(Ordering::SeqCst)], - point, - ) + let mut point = *point; + if point.y < TAB_HEIGHT as f64 { + return true; + } + point.y -= TAB_HEIGHT as f64; + self.0.with_program(|p| { + SpaceElement::is_in_input_region( + &p.windows.lock().unwrap()[p.active.load(Ordering::SeqCst)], + &point, + ) + }) } fn set_activate(&self, activated: bool) { - self.windows - .lock() - .unwrap() - .iter() - .for_each(|w| SpaceElement::set_activate(w, activated)) + SpaceElement::set_activate(&self.0, activated); + self.0.with_program(|p| { + p.windows + .lock() + .unwrap() + .iter() + .for_each(|w| SpaceElement::set_activate(w, activated)) + }) } fn output_enter(&self, output: &Output, overlap: Rectangle) { - self.windows - .lock() - .unwrap() - .iter() - .for_each(|w| SpaceElement::output_enter(w, output, overlap)) + SpaceElement::output_enter(&self.0, output, overlap); + self.0.with_program(|p| { + p.windows + .lock() + .unwrap() + .iter() + .for_each(|w| SpaceElement::output_enter(w, output, overlap)) + }) } fn output_leave(&self, output: &Output) { - self.windows - .lock() - .unwrap() - .iter() - .for_each(|w| SpaceElement::output_leave(w, output)) + SpaceElement::output_leave(&self.0, output); + self.0.with_program(|p| { + p.windows + .lock() + .unwrap() + .iter() + .for_each(|w| SpaceElement::output_leave(w, output)) + }) } fn geometry(&self) -> Rectangle { - SpaceElement::geometry(&self.windows.lock().unwrap()[self.active.load(Ordering::SeqCst)]) + self.0.with_program(|p| { + let mut geo = + SpaceElement::geometry(&p.windows.lock().unwrap()[p.active.load(Ordering::SeqCst)]); + geo.size.h += TAB_HEIGHT; + geo + }) } fn z_index(&self) -> u8 { - SpaceElement::z_index(&self.windows.lock().unwrap()[self.active.load(Ordering::SeqCst)]) + self.0.with_program(|p| { + SpaceElement::z_index(&p.windows.lock().unwrap()[p.active.load(Ordering::SeqCst)]) + }) } fn refresh(&self) { - let mut windows = self.windows.lock().unwrap(); - windows.retain(IsAlive::alive); - let len = windows.len(); - let _ = self - .active - .fetch_update(Ordering::SeqCst, Ordering::SeqCst, |active| { - (active > len).then_some(len - 1) - }); - windows.iter().for_each(|w| SpaceElement::refresh(w)) + self.0.with_program(|p| { + let mut windows = p.windows.lock().unwrap(); + windows.retain(IsAlive::alive); // TODO: We don't handle empty stacks properly + let len = windows.len(); + let _ = p + .active + .fetch_update(Ordering::SeqCst, Ordering::SeqCst, |active| { + (active > len).then_some(len - 1) + }); + windows.iter().for_each(|w| SpaceElement::refresh(w)) + }) } } @@ -229,19 +338,23 @@ impl KeyboardTarget for CosmicStack { keys: Vec>, serial: Serial, ) { - let active = self.active.load(Ordering::SeqCst); - self.previous_keyboard.store(active, Ordering::SeqCst); - KeyboardTarget::enter( - &self.windows.lock().unwrap()[self.active.load(Ordering::SeqCst)], - seat, - data, - keys, - serial, - ) + self.0.with_program(|p| { + let active = p.active.load(Ordering::SeqCst); + p.previous_keyboard.store(active, Ordering::SeqCst); + KeyboardTarget::enter( + &p.windows.lock().unwrap()[p.active.load(Ordering::SeqCst)], + seat, + data, + keys, + serial, + ) + }) } fn leave(&self, seat: &Seat, data: &mut State, serial: Serial) { let active = self.keyboard_leave_if_previous(seat, data, serial); - KeyboardTarget::leave(&self.windows.lock().unwrap()[active], seat, data, serial) + self.0.with_program(|p| { + KeyboardTarget::leave(&p.windows.lock().unwrap()[active], seat, data, serial) + }) } fn key( &self, @@ -253,15 +366,17 @@ impl KeyboardTarget for CosmicStack { time: u32, ) { let active = self.keyboard_leave_if_previous(seat, data, serial); - KeyboardTarget::key( - &self.windows.lock().unwrap()[active], - seat, - data, - key, - state, - serial, - time, - ) + self.0.with_program(|p| { + KeyboardTarget::key( + &p.windows.lock().unwrap()[active], + seat, + data, + key, + state, + serial, + time, + ) + }) } fn modifiers( &self, @@ -271,132 +386,225 @@ impl KeyboardTarget for CosmicStack { serial: Serial, ) { let active = self.keyboard_leave_if_previous(seat, data, serial); - KeyboardTarget::modifiers( - &self.windows.lock().unwrap()[active], - seat, - data, - modifiers, - serial, - ) + self.0.with_program(|p| { + KeyboardTarget::modifiers( + &p.windows.lock().unwrap()[active], + seat, + data, + modifiers, + serial, + ) + }) } } impl PointerTarget for CosmicStack { fn enter(&self, seat: &Seat, data: &mut State, event: &MotionEvent) { - if let Some(sessions) = self.active().user_data().get::() { - for session in &*sessions.0.borrow() { - session.cursor_enter(seat, InputType::Pointer) + if self.0.with_program(|p| { + if let Some(sessions) = p.windows.lock().unwrap()[p.active.load(Ordering::SeqCst)] + .user_data() + .get::() + { + for session in &*sessions.0.borrow() { + session.cursor_enter(seat, InputType::Pointer) + } } + + if event.location.y < TAB_HEIGHT as f64 { + let focus = p.swap_focus(Focus::Header); + assert_eq!(focus, Focus::None); + true + } else { + let focus = p.swap_focus(Focus::Window); + assert_eq!(focus, Focus::None); + + *p.last_location.lock().unwrap() = Some((event.location, event.serial, event.time)); + let active = p.active.load(Ordering::SeqCst); + p.previous_pointer.store(active, Ordering::SeqCst); + + PointerTarget::enter( + &p.windows.lock().unwrap()[p.active.load(Ordering::SeqCst)], + seat, + data, + event, + ); + false + } + }) { + PointerTarget::enter(&self.0, seat, data, event) } - - *self.last_location.lock().unwrap() = Some((event.location, event.serial, event.time)); - let active = self.active.load(Ordering::SeqCst); - self.previous_pointer.store(active, Ordering::SeqCst); - - PointerTarget::enter( - &self.windows.lock().unwrap()[self.active.load(Ordering::SeqCst)], - seat, - data, - event, - ) } + fn motion(&self, seat: &Seat, data: &mut State, event: &MotionEvent) { let active = self.pointer_leave_if_previous(seat, data, event.serial, event.time, event.location); - - if let Some(sessions) = self.active().user_data().get::() { - for session in &*sessions.0.borrow() { - let buffer_loc = (event.location.x, event.location.y); // we always screencast windows at 1x1 scale - if let Some((geo, hotspot)) = - seat.cursor_geometry(buffer_loc, data.common.clock.now()) - { - session.cursor_info(seat, InputType::Pointer, geo, hotspot); + if let Some((previous, next)) = self.0.with_program(|p| { + if let Some(sessions) = p.windows.lock().unwrap()[p.active.load(Ordering::SeqCst)] + .user_data() + .get::() + { + for session in &*sessions.0.borrow() { + let buffer_loc = (event.location.x, event.location.y); // we always screencast windows at 1x1 scale + if let Some((geo, hotspot)) = + seat.cursor_geometry(buffer_loc, data.common.clock.now()) + { + session.cursor_info(seat, InputType::Pointer, geo, hotspot); + } } } - } - PointerTarget::motion(&self.windows.lock().unwrap()[active], seat, data, event) + if event.location.y < TAB_HEIGHT as f64 { + let previous = p.swap_focus(Focus::Header); + if previous == Focus::Window { + PointerTarget::leave( + &p.windows.lock().unwrap()[active], + seat, + data, + event.serial, + event.time, + ); + } + Some((previous, Focus::Header)) + } else { + let mut event = event.clone(); + event.location.y -= TAB_HEIGHT as f64; + + let previous = p.swap_focus(Focus::Window); + if previous != Focus::Window { + PointerTarget::enter(&p.windows.lock().unwrap()[active], seat, data, &event); + } else { + PointerTarget::motion(&p.windows.lock().unwrap()[active], seat, data, &event); + } + + Some((previous, Focus::Window)) + } + }) { + match (previous, next) { + (Focus::Header, Focus::Header) => PointerTarget::motion(&self.0, seat, data, event), + (_, Focus::Header) => PointerTarget::enter(&self.0, seat, data, event), + (Focus::Header, _) => { + PointerTarget::leave(&self.0, seat, data, event.serial, event.time) + } + _ => {} + } + } } + fn button(&self, seat: &Seat, data: &mut State, event: &ButtonEvent) { - if let Some((location, _serial, _time)) = self.last_location.lock().unwrap().clone() { + if let Some((location, _serial, _time)) = self + .0 + .with_program(|p| p.last_location.lock().unwrap().clone()) + { self.pointer_leave_if_previous(seat, data, event.serial, event.time, location); } - PointerTarget::button( - &self.windows.lock().unwrap()[self.active.load(Ordering::SeqCst)], - seat, - data, - event, - ) + match self.0.with_program(|p| p.current_focus()) { + Focus::Header => PointerTarget::button(&self.0, seat, data, event), + Focus::Window => self.0.with_program(|p| { + PointerTarget::button( + &p.windows.lock().unwrap()[p.active.load(Ordering::SeqCst)], + seat, + data, + event, + ) + }), + _ => {} + } } + fn axis(&self, seat: &Seat, data: &mut State, frame: AxisFrame) { - if let Some((location, serial, time)) = self.last_location.lock().unwrap().clone() { + if let Some((location, serial, time)) = self + .0 + .with_program(|p| p.last_location.lock().unwrap().clone()) + { self.pointer_leave_if_previous(seat, data, serial, time, location); } - PointerTarget::axis( - &self.windows.lock().unwrap()[self.active.load(Ordering::SeqCst)], - seat, - data, - frame, - ) + match self.0.with_program(|p| p.current_focus()) { + Focus::Header => PointerTarget::axis(&self.0, seat, data, frame), + Focus::Window => self.0.with_program(|p| { + PointerTarget::axis( + &p.windows.lock().unwrap()[p.active.load(Ordering::SeqCst)], + seat, + data, + frame, + ) + }), + _ => {} + } } + fn leave(&self, seat: &Seat, data: &mut State, serial: Serial, time: u32) { - if let Some((location, serial, time)) = self.last_location.lock().unwrap().clone() { + if let Some((location, serial, time)) = self + .0 + .with_program(|p| p.last_location.lock().unwrap().clone()) + { self.pointer_leave_if_previous(seat, data, serial, time, location); } - if let Some(sessions) = self.active().user_data().get::() { - for session in &*sessions.0.borrow() { - session.cursor_leave(seat, InputType::Pointer) - } - } - PointerTarget::leave( - &self.windows.lock().unwrap()[self.active.load(Ordering::SeqCst)], - seat, - data, - serial, - time, - ) + let previous = self.0.with_program(|p| { + if let Some(sessions) = p.windows.lock().unwrap()[p.active.load(Ordering::SeqCst)] + .user_data() + .get::() + { + for session in &*sessions.0.borrow() { + session.cursor_leave(seat, InputType::Pointer) + } + } + + p.swap_focus(Focus::None) + }); + assert!(previous != Focus::None); + + match previous { + Focus::Header => PointerTarget::leave(&self.0, seat, data, serial, time), + Focus::Window => self.0.with_program(|p| { + PointerTarget::leave( + &p.windows.lock().unwrap()[p.active.load(Ordering::SeqCst)], + seat, + data, + serial, + time, + ) + }), + _ => {} + } } } render_elements! { - pub CosmicStackRenderElement where R: ImportAll; + pub CosmicStackRenderElement where R: ImportAll + ImportMem; + Header=MemoryRenderBufferRenderElement, Window=WaylandSurfaceRenderElement, } impl AsRenderElements for CosmicStack where - R: Renderer + ImportAll, + R: Renderer + ImportAll + ImportMem, ::TextureId: 'static, { type RenderElement = CosmicStackRenderElement; fn render_elements>( &self, renderer: &mut R, - location: Point, + mut location: Point, scale: Scale, ) -> Vec { - AsRenderElements::::render_elements::>( - &self.windows.lock().unwrap()[self.active.load(Ordering::SeqCst)], - renderer, - location, - scale, - ) - .into_iter() - .map(C::from) - .collect() - } -} -impl CosmicStack { - pub fn windows(&self) -> impl Iterator { - self.windows - .lock() - .unwrap() - .iter() - .cloned() - .collect::>() - .into_iter() + let mut elements = AsRenderElements::::render_elements::>( + &self.0, renderer, location, scale, + ); + location.y += TAB_HEIGHT; + + elements.extend(self.0.with_program(|p| { + let elements = AsRenderElements::::render_elements::>( + &p.windows.lock().unwrap()[p.active.load(Ordering::SeqCst)], + renderer, + location, + scale, + ); + elements + })); + + elements.into_iter().map(C::from).collect() } } diff --git a/src/shell/element/surface.rs b/src/shell/element/surface.rs new file mode 100644 index 00000000..2f919124 --- /dev/null +++ b/src/shell/element/surface.rs @@ -0,0 +1,579 @@ +use std::time::Duration; + +use smithay::{ + backend::renderer::{ + element::{surface::WaylandSurfaceRenderElement, AsRenderElements}, + ImportAll, Renderer, + }, + desktop::{ + utils::{ + send_frames_surface_tree, take_presentation_feedback_surface_tree, + with_surfaces_surface_tree, OutputPresentationFeedback, + }, + Window, + }, + input::{keyboard::KeyboardTarget, pointer::PointerTarget}, + output::Output, + reexports::{ + wayland_protocols::{ + wp::presentation_time::server::wp_presentation_feedback::Kind, + xdg::{ + decoration::zv1::server::zxdg_toplevel_decoration_v1::Mode as DecorationMode, + shell::server::xdg_toplevel::State as ToplevelState, + }, + }, + wayland_server::protocol::wl_surface::WlSurface, + }, + space_elements, + utils::{user_data::UserDataMap, Logical, Rectangle, Size}, + wayland::{ + compositor::{with_states, SurfaceData}, + seat::WaylandFocus, + shell::xdg::XdgToplevelSurfaceData, + }, + xwayland::X11Surface, +}; + +space_elements! { + #[derive(Debug, Clone, PartialEq)] + pub CosmicSurface; + Wayland=Window, + X11=X11Surface, +} + +pub const SSD_HEIGHT: i32 = 48; + +impl CosmicSurface { + pub fn title(&self) -> String { + match self { + CosmicSurface::Wayland(window) => { + with_states(window.toplevel().wl_surface(), |states| { + states + .data_map + .get::() + .unwrap() + .lock() + .unwrap() + .title + .clone() + .unwrap_or_default() + }) + } + CosmicSurface::X11(surface) => surface.title(), + _ => unreachable!(), + } + } + + pub fn app_id(&self) -> String { + match self { + CosmicSurface::Wayland(window) => { + with_states(window.toplevel().wl_surface(), |states| { + states + .data_map + .get::() + .unwrap() + .lock() + .unwrap() + .app_id + .clone() + .unwrap_or_default() + }) + } + CosmicSurface::X11(surface) => surface.class(), + _ => unreachable!(), + } + } + + pub fn set_size(&self, size: Size) { + match self { + CosmicSurface::Wayland(window) => window + .toplevel() + .with_pending_state(|state| state.size = Some(size)), + CosmicSurface::X11(surface) => { + let rect = Rectangle::from_loc_and_size(surface.geometry().loc, size); + let _ = surface.configure(rect); + } + _ => {} + } + } + + pub fn is_activated(&self) -> bool { + match self { + CosmicSurface::Wayland(window) => window + .toplevel() + .current_state() + .states + .contains(ToplevelState::Activated), + CosmicSurface::X11(surface) => surface.is_activated(), + _ => unreachable!(), + } + } + + pub fn set_activated(&self, activated: bool) { + match self { + CosmicSurface::Wayland(window) => window.toplevel().with_pending_state(|state| { + if activated { + state.states.set(ToplevelState::Activated); + } else { + state.states.unset(ToplevelState::Activated); + } + }), + CosmicSurface::X11(surface) => { + let _ = surface.set_activated(activated); + } + _ => unreachable!(), + } + } + + pub fn is_decorated(&self) -> bool { + match self { + CosmicSurface::Wayland(window) => window + .toplevel() + .current_state() + .decoration_mode + .map(|mode| mode == DecorationMode::ClientSide) + .unwrap_or(true), + CosmicSurface::X11(surface) => surface.is_decorated(), + _ => unreachable!(), + } + } + + pub fn is_resizing(&self) -> Option { + match self { + CosmicSurface::Wayland(window) => { + let xdg = window.toplevel(); + Some( + xdg.current_state().states.contains(ToplevelState::Resizing) + || xdg.with_pending_state(|states| { + states.states.contains(ToplevelState::Resizing) + }), + ) + } + _ => None, + } + } + + pub fn set_resizing(&self, resizing: bool) { + match self { + CosmicSurface::Wayland(window) => window.toplevel().with_pending_state(|state| { + if resizing { + state.states.set(ToplevelState::Resizing); + } else { + state.states.unset(ToplevelState::Resizing); + } + }), + _ => {} + } + } + + pub fn is_tiled(&self) -> Option { + match self { + CosmicSurface::Wayland(window) => Some( + window + .toplevel() + .current_state() + .states + .contains(ToplevelState::TiledLeft), + ), + _ => None, + } + } + + pub fn set_tiled(&self, tiled: bool) { + match self { + CosmicSurface::Wayland(window) => window.toplevel().with_pending_state(|state| { + if tiled { + state.states.set(ToplevelState::TiledLeft); + state.states.set(ToplevelState::TiledRight); + state.states.set(ToplevelState::TiledTop); + state.states.set(ToplevelState::TiledBottom); + } else { + state.states.unset(ToplevelState::TiledLeft); + state.states.unset(ToplevelState::TiledRight); + state.states.unset(ToplevelState::TiledTop); + state.states.unset(ToplevelState::TiledBottom); + } + }), + _ => {} + } + } + + pub fn is_fullscreen(&self) -> bool { + match self { + CosmicSurface::Wayland(window) => { + let xdg = window.toplevel(); + xdg.current_state() + .states + .contains(ToplevelState::Fullscreen) + || xdg.with_pending_state(|state| { + state.states.contains(ToplevelState::Fullscreen) + }) + } + CosmicSurface::X11(surface) => surface.is_fullscreen(), + _ => unreachable!(), + } + } + + pub fn set_fullscreen(&self, fullscreen: bool) { + match self { + CosmicSurface::Wayland(window) => window.toplevel().with_pending_state(|state| { + if fullscreen { + state.states.set(ToplevelState::Fullscreen); + } else { + state.states.unset(ToplevelState::Fullscreen); + } + }), + CosmicSurface::X11(surface) => { + let _ = surface.set_fullscreen(fullscreen); + } + _ => unreachable!(), + } + } + + pub fn is_maximized(&self) -> bool { + match self { + CosmicSurface::Wayland(window) => { + let xdg = window.toplevel(); + xdg.current_state() + .states + .contains(ToplevelState::Maximized) + || xdg + .with_pending_state(|state| state.states.contains(ToplevelState::Maximized)) + } + CosmicSurface::X11(surface) => surface.is_maximized(), + _ => unreachable!(), + } + } + + pub fn set_maximized(&self, maximized: bool) { + match self { + CosmicSurface::Wayland(window) => window.toplevel().with_pending_state(|state| { + if maximized { + state.states.set(ToplevelState::Maximized); + } else { + state.states.unset(ToplevelState::Maximized); + } + }), + CosmicSurface::X11(surface) => { + let _ = surface.set_maximized(maximized); + } + _ => unreachable!(), + } + } + + pub fn min_size(&self) -> Option> { + match self { + CosmicSurface::Wayland(window) => { + Some(with_states(window.toplevel().wl_surface(), |states| { + let attrs = states + .data_map + .get::() + .unwrap() + .lock() + .unwrap(); + attrs.min_size + })) + .filter(|size| !(size.w == 0 && size.h == 0)) + } + CosmicSurface::X11(surface) => surface.min_size(), + _ => unreachable!(), + } + } + + pub fn max_size(&self) -> Option> { + match self { + CosmicSurface::Wayland(window) => { + Some(with_states(window.toplevel().wl_surface(), |states| { + let attrs = states + .data_map + .get::() + .unwrap() + .lock() + .unwrap(); + attrs.max_size + })) + .filter(|size| !(size.w == 0 && size.h == 0)) + } + CosmicSurface::X11(surface) => surface.max_size(), + _ => unreachable!(), + } + } + + pub fn send_configure(&self) { + match self { + CosmicSurface::Wayland(window) => window.toplevel().send_configure(), + CosmicSurface::X11(surface) => { + let _ = surface.configure(None); + } + _ => unreachable!(), + } + } + + pub fn close(&self) { + match self { + CosmicSurface::Wayland(window) => window.toplevel().send_close(), + CosmicSurface::X11(surface) => { + let _ = surface.close(); + } + _ => unreachable!(), + } + } + + pub fn on_commit(&self) { + match self { + CosmicSurface::Wayland(window) => window.on_commit(), + _ => {} + } + } + + pub fn send_frame( + &self, + output: &Output, + time: T, + throttle: Option, + primary_scan_out_output: F, + ) where + T: Into, + F: FnMut(&WlSurface, &SurfaceData) -> Option + Copy, + { + match self { + CosmicSurface::Wayland(window) => { + window.send_frame(output, time, throttle, primary_scan_out_output) + } + CosmicSurface::X11(surface) => { + if let Some(wl_surface) = surface.wl_surface() { + send_frames_surface_tree( + &wl_surface, + output, + time, + throttle, + primary_scan_out_output, + ) + } + } + _ => unreachable!(), + } + } + + pub fn take_presentation_feedback( + &self, + output_feedback: &mut OutputPresentationFeedback, + primary_scan_out_output: F1, + presentation_feedback_flags: F2, + ) where + F1: FnMut(&WlSurface, &SurfaceData) -> Option + Copy, + F2: FnMut(&WlSurface, &SurfaceData) -> Kind + Copy, + { + match self { + CosmicSurface::Wayland(window) => window.take_presentation_feedback( + output_feedback, + primary_scan_out_output, + presentation_feedback_flags, + ), + CosmicSurface::X11(surface) => { + if let Some(wl_surface) = surface.wl_surface() { + take_presentation_feedback_surface_tree( + &wl_surface, + output_feedback, + primary_scan_out_output, + presentation_feedback_flags, + ) + } + } + _ => unreachable!(), + } + } + + pub fn with_surfaces(&self, processor: F) + where + F: FnMut(&WlSurface, &SurfaceData) + Copy, + { + match self { + CosmicSurface::Wayland(window) => window.with_surfaces(processor), + CosmicSurface::X11(surface) => { + if let Some(wl_surface) = surface.wl_surface() { + with_surfaces_surface_tree(&wl_surface, processor) + } + } + _ => unreachable!(), + } + } + + pub fn user_data(&self) -> &UserDataMap { + match self { + CosmicSurface::Wayland(window) => window.user_data(), + CosmicSurface::X11(surface) => surface.user_data(), + _ => unreachable!(), + } + } +} + +impl KeyboardTarget for CosmicSurface { + fn enter( + &self, + seat: &smithay::input::Seat, + data: &mut crate::state::State, + keys: Vec>, + serial: smithay::utils::Serial, + ) { + match self { + CosmicSurface::Wayland(window) => { + KeyboardTarget::enter(window, seat, data, keys, serial) + } + CosmicSurface::X11(surface) => KeyboardTarget::enter(surface, seat, data, keys, serial), + _ => unreachable!(), + } + } + + fn leave( + &self, + seat: &smithay::input::Seat, + data: &mut crate::state::State, + serial: smithay::utils::Serial, + ) { + match self { + CosmicSurface::Wayland(window) => KeyboardTarget::leave(window, seat, data, serial), + CosmicSurface::X11(surface) => KeyboardTarget::leave(surface, seat, data, serial), + _ => unreachable!(), + } + } + + fn key( + &self, + seat: &smithay::input::Seat, + data: &mut crate::state::State, + key: smithay::input::keyboard::KeysymHandle<'_>, + state: smithay::backend::input::KeyState, + serial: smithay::utils::Serial, + time: u32, + ) { + match self { + CosmicSurface::Wayland(window) => { + KeyboardTarget::key(window, seat, data, key, state, serial, time) + } + CosmicSurface::X11(surface) => { + KeyboardTarget::key(surface, seat, data, key, state, serial, time) + } + _ => unreachable!(), + } + } + + fn modifiers( + &self, + seat: &smithay::input::Seat, + data: &mut crate::state::State, + modifiers: smithay::input::keyboard::ModifiersState, + serial: smithay::utils::Serial, + ) { + match self { + CosmicSurface::Wayland(window) => { + KeyboardTarget::modifiers(window, seat, data, modifiers, serial) + } + CosmicSurface::X11(surface) => { + KeyboardTarget::modifiers(surface, seat, data, modifiers, serial) + } + _ => unreachable!(), + } + } +} + +impl PointerTarget for CosmicSurface { + fn enter( + &self, + seat: &smithay::input::Seat, + data: &mut crate::state::State, + event: &smithay::input::pointer::MotionEvent, + ) { + match self { + CosmicSurface::Wayland(window) => PointerTarget::enter(window, seat, data, event), + CosmicSurface::X11(surface) => PointerTarget::enter(surface, seat, data, event), + _ => unreachable!(), + } + } + + fn motion( + &self, + seat: &smithay::input::Seat, + data: &mut crate::state::State, + event: &smithay::input::pointer::MotionEvent, + ) { + match self { + CosmicSurface::Wayland(window) => PointerTarget::motion(window, seat, data, event), + CosmicSurface::X11(surface) => PointerTarget::motion(surface, seat, data, event), + _ => unreachable!(), + } + } + + fn button( + &self, + seat: &smithay::input::Seat, + data: &mut crate::state::State, + event: &smithay::input::pointer::ButtonEvent, + ) { + match self { + CosmicSurface::Wayland(window) => PointerTarget::button(window, seat, data, event), + CosmicSurface::X11(surface) => PointerTarget::button(surface, seat, data, event), + _ => unreachable!(), + } + } + + fn axis( + &self, + seat: &smithay::input::Seat, + data: &mut crate::state::State, + frame: smithay::input::pointer::AxisFrame, + ) { + match self { + CosmicSurface::Wayland(window) => PointerTarget::axis(window, seat, data, frame), + CosmicSurface::X11(surface) => PointerTarget::axis(surface, seat, data, frame), + _ => unreachable!(), + } + } + + fn leave( + &self, + seat: &smithay::input::Seat, + data: &mut crate::state::State, + serial: smithay::utils::Serial, + time: u32, + ) { + match self { + CosmicSurface::Wayland(window) => { + PointerTarget::leave(window, seat, data, serial, time) + } + CosmicSurface::X11(surface) => PointerTarget::leave(surface, seat, data, serial, time), + _ => unreachable!(), + } + } +} + +impl WaylandFocus for CosmicSurface { + fn wl_surface(&self) -> Option { + match self { + CosmicSurface::Wayland(window) => window.wl_surface(), + CosmicSurface::X11(surface) => surface.wl_surface(), + _ => unreachable!(), + } + } +} + +impl AsRenderElements for CosmicSurface +where + R: Renderer + ImportAll, + ::TextureId: 'static, +{ + type RenderElement = WaylandSurfaceRenderElement; + + fn render_elements>( + &self, + renderer: &mut R, + location: smithay::utils::Point, + scale: smithay::utils::Scale, + ) -> Vec { + match self { + CosmicSurface::Wayland(window) => window.render_elements(renderer, location, scale), + CosmicSurface::X11(surface) => surface.render_elements(renderer, location, scale), + _ => unreachable!(), + } + } +} diff --git a/src/shell/element/window.rs b/src/shell/element/window.rs index 0ad25372..8a963933 100644 --- a/src/shell/element/window.rs +++ b/src/shell/element/window.rs @@ -1,152 +1,280 @@ use crate::{ - state::State, utils::prelude::SeatExt, wayland::handlers::screencopy::ScreencopySessions, + state::State, + utils::{ + iced::{IcedElement, Program}, + prelude::SeatExt, + }, + wayland::handlers::screencopy::ScreencopySessions, }; +use calloop::LoopHandle; +use cosmic::{iced_native::Command, Element}; +use cosmic_protocols::screencopy::v1::server::zcosmic_screencopy_session_v1::InputType; +use iced_softbuffer::native::raqote::{DrawOptions, DrawTarget, PathBuilder, SolidSource, Source}; use smithay::{ backend::{ input::KeyState, renderer::{ - element::{surface::WaylandSurfaceRenderElement, AsRenderElements}, - ImportAll, Renderer, + element::{ + memory::MemoryRenderBufferRenderElement, surface::WaylandSurfaceRenderElement, + AsRenderElements, + }, + ImportAll, ImportMem, Renderer, }, }, - desktop::{space::SpaceElement, Window}, + desktop::space::SpaceElement, input::{ keyboard::{KeyboardTarget, KeysymHandle, ModifiersState}, pointer::{AxisFrame, ButtonEvent, MotionEvent, PointerTarget}, Seat, }, output::Output, - reexports::wayland_protocols::xdg::decoration::zv1::server::zxdg_toplevel_decoration_v1::Mode as DecorationMode, render_elements, utils::{IsAlive, Logical, Physical, Point, Rectangle, Scale, Serial, Size}, - wayland::shell::xdg::ToplevelSurface, }; use std::{ + fmt, hash::Hash, - sync::{Arc, Mutex}, + sync::{ + atomic::{AtomicU8, Ordering}, + Arc, + }, }; +use super::{surface::SSD_HEIGHT, CosmicSurface}; + +#[derive(Clone, PartialEq, Eq, Hash)] +pub struct CosmicWindow(IcedElement); + +impl fmt::Debug for CosmicWindow { + fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { + self.0.with_program(|window| { + f.debug_struct("CosmicWindow") + .field("internal", window) + .finish_non_exhaustive() + }) + } +} + #[derive(Debug, Clone)] -pub struct CosmicWindow { - pub(super) window: Window, - pub(super) header: Arc>>, +pub struct CosmicWindowInternal { + pub(super) window: CosmicSurface, + pointer_entered: Option>, } -impl PartialEq for CosmicWindow { - fn eq(&self, other: &Window) -> bool { - &self.window == other +#[repr(u8)] +#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)] +pub enum Focus { + None, + Header, + Window, +} + +impl CosmicWindowInternal { + pub fn swap_focus(&self, focus: Focus) -> Focus { + if let Some(pointer_entered) = self.pointer_entered.as_ref() { + unsafe { + std::mem::transmute::( + pointer_entered.swap(focus as u8, Ordering::SeqCst), + ) + } + } else { + Focus::Window + } } -} -impl PartialEq for Window { - fn eq(&self, other: &CosmicWindow) -> bool { - self == &other.window + pub fn current_focus(&self) -> Focus { + if let Some(pointer_entered) = self.pointer_entered.as_ref() { + unsafe { std::mem::transmute::(pointer_entered.load(Ordering::SeqCst)) } + } else { + Focus::Window + } } -} -impl PartialEq for CosmicWindow { - fn eq(&self, other: &Self) -> bool { - self.window == other.window - } -} - -impl Eq for CosmicWindow {} - -impl Hash for CosmicWindow { - fn hash(&self, state: &mut H) { - self.window.hash(state) + pub fn has_ssd(&self) -> bool { + self.pointer_entered.is_some() } } impl CosmicWindow { + pub fn new( + window: impl Into, + handle: LoopHandle<'static, crate::state::Data>, + ) -> CosmicWindow { + let window = window.into(); + let needs_ssd = !window.is_decorated(); + let width = window.geometry().size.w; + CosmicWindow(IcedElement::new( + CosmicWindowInternal { + window, + pointer_entered: if needs_ssd { + Some(Arc::new(AtomicU8::new(Focus::None as u8))) + } else { + None + }, + }, + (width, SSD_HEIGHT), + handle, + )) + } + pub fn set_size(&self, size: Size) { - let surface_size = ( - size.w, - size.h - - self - .header - .lock() - .unwrap() - .as_ref() - .map(|h| h.height()) - .unwrap_or(0), - ) - .into(); - self.window - .toplevel() - .with_pending_state(|state| state.size = Some(surface_size)); + self.0.with_program(|p| { + let surface_size = (size.w, size.h - if p.has_ssd() { SSD_HEIGHT } else { 0 }).into(); + p.window.set_size(surface_size) + }); + self.0.resize(Size::from((size.w, SSD_HEIGHT))); } -} -impl From for CosmicWindow { - fn from(window: Window) -> Self { - let is_ssd = matches!( - window.toplevel().current_state().decoration_mode, - Some(DecorationMode::ServerSide) - ); - CosmicWindow { - window, - header: Arc::new(Mutex::new(is_ssd.then_some(HeaderBar::default()))), + pub fn surface(&self) -> CosmicSurface { + self.0.with_program(|p| p.window.clone()) + } + + pub fn offset(&self) -> Point { + let has_ssd = self.0.with_program(|p| p.has_ssd()); + if has_ssd { + Point::from((0, SSD_HEIGHT)) + } else { + Point::from((0, 0)) } } } -impl From for CosmicWindow { - fn from(surf: ToplevelSurface) -> Self { - let is_ssd = matches!( - surf.current_state().decoration_mode, - Some(DecorationMode::ServerSide) - ); - CosmicWindow { - window: Window::new(surf), - header: Arc::new(Mutex::new(is_ssd.then_some(HeaderBar::default()))), +#[derive(Debug, Clone, Copy)] +pub enum Message { + DragStart, + Maximize, + Close, +} + +impl Program for CosmicWindowInternal { + type Message = Message; + + fn update(&mut self, message: Self::Message) -> Command { + /* + match message { + Message::DragStart => match &self.window { + CosmicWindowSurface::Wayland(window) => self + .with_program(|internal| internal.loop_handle()) + .insert_idle(|data| {}), + CosmicWindowSurface::X11(surface) => {} + _ => unreachable!(), + }, + } + */ + Command::none() + } + + fn background(&self, target: &mut DrawTarget<&mut [u32]>) { + let radius = 8.; + let (w, h) = (target.width() as f32, target.height() as f32); + + let mut pb = PathBuilder::new(); + pb.move_to(0., h); // lower-left + + // upper-left rounded corner + pb.line_to(0., radius); + pb.quad_to(0., 0., radius, 0.); + + // upper-right rounded corner + pb.line_to(w - radius, 0.); + pb.quad_to(w, 0., w, radius); + + pb.line_to(w, h); // lower-right + + let path = pb.finish(); + target.push_clip(&path); + + if self.window.is_activated() { + target.clear(SolidSource::from_unpremultiplied_argb(u8::MAX, 30, 30, 30)); + } else { + target.clear(SolidSource::from_unpremultiplied_argb(u8::MAX, 39, 39, 39)); } } -} -#[derive(Debug, Default, PartialEq)] -pub(super) struct HeaderBar { - pointer_loc: Option>, - close_button_hover: bool, - maximize_button_hover: bool, -} + fn view(&self) -> Element<'_, Self::Message> { + cosmic::widget::header_bar() + .title(self.window.title()) + .on_drag(Message::DragStart) + .on_maximize(Message::Maximize) + .on_close(Message::Close) + .into_element() + } -impl HeaderBar { - pub fn height(&self) -> i32 { - 0 + fn foreground(&self, target: &mut DrawTarget<&mut [u32]>) { + if !self.window.is_activated() { + let (w, h) = (target.width() as f32, target.height() as f32); + let mut options = DrawOptions::new(); + options.alpha = 0.4; + target.fill_rect( + 0., + 0., + w, + h, + &Source::Solid(SolidSource::from_unpremultiplied_argb(u8::MAX, 0, 0, 0)), + &options, + ); + } } } impl IsAlive for CosmicWindow { fn alive(&self) -> bool { - self.window.alive() + self.0.with_program(|p| p.window.alive()) } } impl SpaceElement for CosmicWindow { fn bbox(&self) -> Rectangle { - SpaceElement::bbox(&self.window) + self.0.with_program(|p| { + let mut bbox = SpaceElement::bbox(&p.window); + if p.has_ssd() { + bbox.size.h += SSD_HEIGHT; + } + bbox + }) } fn is_in_input_region(&self, point: &Point) -> bool { - SpaceElement::is_in_input_region(&self.window, point) + let mut point = *point; + self.0.with_program(|p| { + if p.has_ssd() { + if point.y < SSD_HEIGHT as f64 { + return true; + } else { + point.y -= SSD_HEIGHT as f64; + } + } + SpaceElement::is_in_input_region(&p.window, &point) + }) } fn set_activate(&self, activated: bool) { - SpaceElement::set_activate(&self.window, activated) + SpaceElement::set_activate(&self.0, activated); + self.0 + .with_program(|p| SpaceElement::set_activate(&p.window, activated)); } fn output_enter(&self, output: &Output, overlap: Rectangle) { - SpaceElement::output_enter(&self.window, output, overlap) + SpaceElement::output_enter(&self.0, output, overlap); + self.0 + .with_program(|p| SpaceElement::output_enter(&p.window, output, overlap)); } fn output_leave(&self, output: &Output) { - SpaceElement::output_leave(&self.window, output) + SpaceElement::output_leave(&self.0, output); + self.0 + .with_program(|p| SpaceElement::output_leave(&p.window, output)); } fn geometry(&self) -> Rectangle { - SpaceElement::geometry(&self.window) + self.0.with_program(|p| { + let mut geo = SpaceElement::geometry(&p.window); + if p.has_ssd() { + geo.size.h += SSD_HEIGHT; + } + geo + }) } fn z_index(&self) -> u8 { - SpaceElement::z_index(&self.window) + self.0.with_program(|p| SpaceElement::z_index(&p.window)) } fn refresh(&self) { - SpaceElement::refresh(&self.window) + self.0.with_program(|p| SpaceElement::refresh(&p.window)) } } @@ -158,10 +286,12 @@ impl KeyboardTarget for CosmicWindow { keys: Vec>, serial: Serial, ) { - KeyboardTarget::enter(&self.window, seat, data, keys, serial) + self.0 + .with_program(|p| KeyboardTarget::enter(&p.window, seat, data, keys, serial)) } fn leave(&self, seat: &Seat, data: &mut State, serial: Serial) { - KeyboardTarget::leave(&self.window, seat, data, serial) + self.0 + .with_program(|p| KeyboardTarget::leave(&p.window, seat, data, serial)) } fn key( &self, @@ -172,7 +302,8 @@ impl KeyboardTarget for CosmicWindow { serial: Serial, time: u32, ) { - KeyboardTarget::key(&self.window, seat, data, key, state, serial, time) + self.0 + .with_program(|p| KeyboardTarget::key(&p.window, seat, data, key, state, serial, time)) } fn modifiers( &self, @@ -181,82 +312,169 @@ impl KeyboardTarget for CosmicWindow { modifiers: ModifiersState, serial: Serial, ) { - KeyboardTarget::modifiers(&self.window, seat, data, modifiers, serial) + self.0 + .with_program(|p| KeyboardTarget::modifiers(&p.window, seat, data, modifiers, serial)) } } impl PointerTarget for CosmicWindow { fn enter(&self, seat: &Seat, data: &mut State, event: &MotionEvent) { - use cosmic_protocols::screencopy::v1::server::zcosmic_screencopy_session_v1::InputType; - - if let Some(sessions) = self.window.user_data().get::() { - for session in &*sessions.0.borrow() { - session.cursor_enter(seat, InputType::Pointer) - } - } - - PointerTarget::enter(&self.window, seat, data, event) - } - fn motion(&self, seat: &Seat, data: &mut State, event: &MotionEvent) { - use cosmic_protocols::screencopy::v1::server::zcosmic_screencopy_session_v1::InputType; - - if let Some(sessions) = self.window.user_data().get::() { - for session in &*sessions.0.borrow() { - let buffer_loc = (event.location.x, event.location.y); // we always screencast windows at 1x1 scale - if let Some((geo, hotspot)) = - seat.cursor_geometry(buffer_loc, data.common.clock.now()) - { - session.cursor_info(seat, InputType::Pointer, geo, hotspot); + if self.0.with_program(|p| { + if let Some(sessions) = p.window.user_data().get::() { + for session in &*sessions.0.borrow() { + session.cursor_enter(seat, InputType::Pointer) } } - } - PointerTarget::motion(&self.window, seat, data, event) - } - fn button(&self, seat: &Seat, data: &mut State, event: &ButtonEvent) { - PointerTarget::button(&self.window, seat, data, event) - } - fn axis(&self, seat: &Seat, data: &mut State, frame: AxisFrame) { - PointerTarget::axis(&self.window, seat, data, frame) - } - fn leave(&self, seat: &Seat, data: &mut State, serial: Serial, time: u32) { - use cosmic_protocols::screencopy::v1::server::zcosmic_screencopy_session_v1::InputType; + if p.has_ssd() { + if event.location.y < SSD_HEIGHT as f64 { + let focus = p.swap_focus(Focus::Header); + assert_eq!(focus, Focus::None); + return true; + } else { + let focus = p.swap_focus(Focus::Window); + assert_eq!(focus, Focus::None); - if let Some(sessions) = self.window.user_data().get::() { - for session in &*sessions.0.borrow() { - session.cursor_leave(seat, InputType::Pointer) + let mut event = event.clone(); + event.location.y -= SSD_HEIGHT as f64; + PointerTarget::enter(&p.window, seat, data, &event) + } + } else { + PointerTarget::enter(&p.window, seat, data, event) } + false + }) { + PointerTarget::enter(&self.0, seat, data, event) } + } - PointerTarget::leave(&self.window, seat, data, serial, time) + fn motion(&self, seat: &Seat, data: &mut State, event: &MotionEvent) { + if let Some((previous, next)) = self.0.with_program(|p| { + if let Some(sessions) = p.window.user_data().get::() { + for session in &*sessions.0.borrow() { + let buffer_loc = (event.location.x, event.location.y); // we always screencast windows at 1x1 scale + if let Some((geo, hotspot)) = + seat.cursor_geometry(buffer_loc, data.common.clock.now()) + { + session.cursor_info(seat, InputType::Pointer, geo, hotspot); + } + } + } + + if p.has_ssd() { + if event.location.y < SSD_HEIGHT as f64 { + let previous = p.swap_focus(Focus::Header); + if previous == Focus::Window { + PointerTarget::leave(&p.window, seat, data, event.serial, event.time); + } + Some((previous, Focus::Header)) + } else { + let mut event = event.clone(); + event.location.y -= SSD_HEIGHT as f64; + + let previous = p.swap_focus(Focus::Window); + if previous != Focus::Window { + PointerTarget::enter(&p.window, seat, data, &event); + } else { + PointerTarget::motion(&p.window, seat, data, &event); + } + + Some((previous, Focus::Window)) + } + } else { + PointerTarget::motion(&p.window, seat, data, event); + None + } + }) { + match (previous, next) { + (Focus::Header, Focus::Header) => PointerTarget::motion(&self.0, seat, data, event), + (_, Focus::Header) => PointerTarget::enter(&self.0, seat, data, event), + (Focus::Header, _) => { + PointerTarget::leave(&self.0, seat, data, event.serial, event.time) + } + _ => {} + }; + } + } + + fn button(&self, seat: &Seat, data: &mut State, event: &ButtonEvent) { + match self.0.with_program(|p| p.current_focus()) { + Focus::Header => PointerTarget::button(&self.0, seat, data, event), + Focus::Window => self + .0 + .with_program(|p| PointerTarget::button(&p.window, seat, data, event)), + _ => {} + } + } + + fn axis(&self, seat: &Seat, data: &mut State, frame: AxisFrame) { + match self.0.with_program(|p| p.current_focus()) { + Focus::Header => PointerTarget::axis(&self.0, seat, data, frame), + Focus::Window => self + .0 + .with_program(|p| PointerTarget::axis(&p.window, seat, data, frame)), + _ => {} + } + } + + fn leave(&self, seat: &Seat, data: &mut State, serial: Serial, time: u32) { + let previous = self.0.with_program(|p| { + if let Some(sessions) = p.window.user_data().get::() { + for session in &*sessions.0.borrow() { + session.cursor_leave(seat, InputType::Pointer) + } + } + + p.swap_focus(Focus::None) + }); + assert!(previous != Focus::None); + match previous { + Focus::Header => PointerTarget::leave(&self.0, seat, data, serial, time), + Focus::Window => self + .0 + .with_program(|p| PointerTarget::leave(&p.window, seat, data, serial, time)), + _ => {} + } } } render_elements! { - pub CosmicWindowRenderElement where R: ImportAll; + pub CosmicWindowRenderElement where R: ImportAll + ImportMem; + Header=MemoryRenderBufferRenderElement, Window=WaylandSurfaceRenderElement, } impl AsRenderElements for CosmicWindow where - R: Renderer + ImportAll, + R: Renderer + ImportAll + ImportMem, ::TextureId: 'static, { type RenderElement = CosmicWindowRenderElement; fn render_elements>( &self, renderer: &mut R, - location: Point, + mut location: Point, scale: Scale, ) -> Vec { - AsRenderElements::::render_elements::>( - &self.window, - renderer, - location, - scale, - ) - .into_iter() - .map(C::from) - .collect() + let has_ssd = self.0.with_program(|p| p.has_ssd()); + + let mut elements = if has_ssd { + let elements = AsRenderElements::::render_elements::>( + &self.0, renderer, location, scale, + ); + location.y += SSD_HEIGHT; + elements + } else { + Vec::new() + }; + + elements.extend(self.0.with_program(|p| { + AsRenderElements::::render_elements::>( + &p.window, renderer, location, scale, + ) + .into_iter() + })); + + elements.into_iter().map(C::from).collect() } } diff --git a/src/shell/focus/target.rs b/src/shell/focus/target.rs index ef337347..97391d77 100644 --- a/src/shell/focus/target.rs +++ b/src/shell/focus/target.rs @@ -1,10 +1,13 @@ use std::sync::Weak; -use crate::{shell::element::CosmicMapped, utils::prelude::*}; +use crate::{ + shell::{element::CosmicMapped, CosmicSurface}, + utils::prelude::*, +}; use id_tree::NodeId; use smithay::{ backend::input::KeyState, - desktop::{LayerSurface, PopupKind, Window}, + desktop::{LayerSurface, PopupKind}, input::{ keyboard::{KeyboardTarget, KeysymHandle, ModifiersState}, pointer::{AxisFrame, ButtonEvent, MotionEvent, PointerTarget}, @@ -19,7 +22,7 @@ use smithay::{ #[derive(Debug, Clone, PartialEq)] pub enum PointerFocusTarget { Element(CosmicMapped), - Fullscreen(Window), + Fullscreen(CosmicSurface), LayerSurface(LayerSurface), Popup(PopupKind), } @@ -27,7 +30,7 @@ pub enum PointerFocusTarget { #[derive(Debug, Clone, PartialEq)] pub enum KeyboardFocusTarget { Element(CosmicMapped), - Fullscreen(Window), + Fullscreen(CosmicSurface), Group(WindowGroup), LayerSurface(LayerSurface), Popup(PopupKind), @@ -263,9 +266,9 @@ impl From for PointerFocusTarget { } } -impl From for PointerFocusTarget { - fn from(w: Window) -> Self { - PointerFocusTarget::Fullscreen(w) +impl From for PointerFocusTarget { + fn from(s: CosmicSurface) -> Self { + PointerFocusTarget::Fullscreen(s) } } @@ -287,9 +290,9 @@ impl From for KeyboardFocusTarget { } } -impl From for KeyboardFocusTarget { - fn from(w: Window) -> Self { - KeyboardFocusTarget::Fullscreen(w) +impl From for KeyboardFocusTarget { + fn from(s: CosmicSurface) -> Self { + KeyboardFocusTarget::Fullscreen(s) } } diff --git a/src/shell/layout/floating/grabs/moving.rs b/src/shell/layout/floating/grabs/moving.rs index a150d113..a56a278d 100644 --- a/src/shell/layout/floating/grabs/moving.rs +++ b/src/shell/layout/floating/grabs/moving.rs @@ -12,7 +12,7 @@ use crate::{ use smithay::{ backend::renderer::{ element::{AsRenderElements, RenderElement}, - ImportAll, Renderer, + ImportAll, ImportMem, Renderer, }, desktop::space::SpaceElement, input::{ @@ -38,7 +38,7 @@ pub struct MoveGrabState { impl MoveGrabState { pub fn render(&self, renderer: &mut R, seat: &Seat, output: &Output) -> Vec where - R: Renderer + ImportAll + AsGlowRenderer, + R: Renderer + ImportAll + ImportMem + AsGlowRenderer, ::TextureId: 'static, CosmicMappedRenderElement: RenderElement, I: From>, diff --git a/src/shell/layout/floating/grabs/resize.rs b/src/shell/layout/floating/grabs/resize.rs index d228d0ee..897c0bba 100644 --- a/src/shell/layout/floating/grabs/resize.rs +++ b/src/shell/layout/floating/grabs/resize.rs @@ -1,7 +1,9 @@ // SPDX-License-Identifier: GPL-3.0-only use crate::{ - shell::{element::CosmicMapped, focus::target::PointerFocusTarget, grabs::ResizeEdge}, + shell::{ + element::CosmicMapped, focus::target::PointerFocusTarget, grabs::ResizeEdge, CosmicSurface, + }, utils::prelude::*, }; use smithay::{ @@ -84,18 +86,10 @@ impl PointerGrab for ResizeSurfaceGrab { let (min_size, max_size) = (self.window.min_size(), self.window.max_size()); - let min_width = min_size.w.max(1); - let min_height = min_size.h.max(1); - let max_width = if max_size.w == 0 { - i32::max_value() - } else { - max_size.w - }; - let max_height = if max_size.h == 0 { - i32::max_value() - } else { - max_size.h - }; + let min_width = min_size.map(|s| s.w).unwrap_or(1); + let min_height = min_size.map(|s| s.h).unwrap_or(1); + let max_width = max_size.map(|s| s.w).unwrap_or(i32::max_value()); + let max_height = max_size.map(|s| s.h).unwrap_or(i32::max_value()); new_window_width = new_window_width.max(min_width).min(max_width); new_window_height = new_window_height.max(min_height).min(max_height); @@ -210,7 +204,7 @@ impl ResizeSurfaceGrab { // Finish resizing. if let Some(ResizeState::WaitingForCommit(_)) = *resize_state { - if !window.is_resizing() { + if !window.is_resizing().unwrap_or(false) { *resize_state = None; } } @@ -218,11 +212,13 @@ impl ResizeSurfaceGrab { if let Some(new_location) = new_location { for (window, offset) in window.windows() { - update_reactive_popups( - &window, - new_location + offset, - space.floating_layer.space.outputs(), - ); + if let CosmicSurface::Wayland(window) = window { + update_reactive_popups( + &window, + new_location + offset, + space.floating_layer.space.outputs(), + ); + } } space .floating_layer diff --git a/src/shell/layout/floating/mod.rs b/src/shell/layout/floating/mod.rs index 22805ee1..c09f4ac9 100644 --- a/src/shell/layout/floating/mod.rs +++ b/src/shell/layout/floating/mod.rs @@ -1,11 +1,11 @@ // SPDX-License-Identifier: GPL-3.0-only use smithay::{ - backend::renderer::{element::RenderElement, ImportAll, Renderer}, - desktop::{layer_map_for_output, space::SpaceElement, Space, Window}, + backend::renderer::{element::RenderElement, ImportAll, ImportMem, Renderer}, + desktop::{layer_map_for_output, space::SpaceElement, Space}, input::{pointer::GrabStartData as PointerGrabStartData, Seat}, output::Output, - utils::{Logical, Point, Rectangle, Serial}, + utils::{Logical, Point, Rectangle, Serial, Size}, }; use std::collections::HashMap; @@ -14,7 +14,7 @@ use crate::{ shell::{ element::{CosmicMapped, CosmicMappedRenderElement}, grabs::ResizeEdge, - OutputNotMapped, + CosmicSurface, OutputNotMapped, }, state::State, utils::prelude::*, @@ -81,7 +81,10 @@ impl FloatingLayout { win_geo.size = size; } { - let (min_size, max_size) = (mapped.min_size(), mapped.max_size()); + let (min_size, max_size) = ( + mapped.min_size().unwrap_or((0, 0).into()), + mapped.max_size().unwrap_or((0, 0).into()), + ); if win_geo.size.w > geometry.size.w / 3 * 2 { // try a more reasonable size let mut width = geometry.size.w / 3 * 2; @@ -155,7 +158,7 @@ impl FloatingLayout { self.space.element_geometry(elem) } - pub fn maximize_request(&mut self, window: &Window) { + pub fn maximize_request(&mut self, window: &CosmicSurface) { if let Some(mapped) = self .space .elements() @@ -170,7 +173,7 @@ impl FloatingLayout { } } - pub fn unmaximize_request(&mut self, window: &Window) { + pub fn unmaximize_request(&mut self, window: &CosmicSurface) -> Option> { let maybe_mapped = self .space .elements() @@ -179,9 +182,13 @@ impl FloatingLayout { if let Some(mapped) = maybe_mapped { let last_geometry = mapped.last_geometry.lock().unwrap().clone(); - mapped.set_size(last_geometry.map(|g| g.size).expect("No previous size?")); + let last_size = last_geometry.map(|g| g.size).expect("No previous size?"); + mapped.set_size(last_size); let last_location = last_geometry.map(|g| g.loc).expect("No previous location?"); self.space.map_element(mapped, last_location, true); + Some(last_size) + } else { + None } } @@ -213,7 +220,7 @@ impl FloatingLayout { self.space.elements() } - pub fn windows(&self) -> impl Iterator + '_ { + pub fn windows(&self) -> impl Iterator + '_ { self.mapped().flat_map(|e| e.windows().map(|(w, _)| w)) } @@ -298,7 +305,7 @@ impl FloatingLayout { output: &Output, ) -> Result>, OutputNotMapped> where - R: Renderer + ImportAll + AsGlowRenderer, + R: Renderer + ImportAll + ImportMem + AsGlowRenderer, ::TextureId: 'static, CosmicMappedRenderElement: RenderElement, { diff --git a/src/shell/layout/mod.rs b/src/shell/layout/mod.rs index 83083d9f..bbfe215b 100644 --- a/src/shell/layout/mod.rs +++ b/src/shell/layout/mod.rs @@ -2,10 +2,11 @@ use regex::RegexSet; use smithay::{ - desktop::Window, - wayland::{compositor::with_states, shell::xdg::XdgToplevelSurfaceRoleAttributes}, + wayland::{compositor::with_states, shell::xdg::XdgToplevelSurfaceData}, + xwayland::xwm::WmWindowType, }; -use std::sync::Mutex; + +use super::CosmicSurface; pub mod floating; pub mod tiling; @@ -95,33 +96,52 @@ lazy_static::lazy_static! { ]).unwrap(); } -pub fn should_be_floating(window: &Window) -> bool { - let surface = window.toplevel().wl_surface(); - with_states(surface, |states| { - let attrs = states - .data_map - .get::>() - .unwrap() - .lock() - .unwrap(); - - // simple heuristic taken from - // sway/desktop/xdg_shell.c:188 @ 0ee54a52 - if attrs.parent.is_some() - || (attrs.min_size.w != 0 && attrs.min_size.h != 0 && attrs.min_size == attrs.max_size) - { - return true; - } - - // else take a look at our exceptions - let appid_matches = EXCEPTIONS_APPID.matches(attrs.app_id.as_deref().unwrap_or("")); - let title_matches = EXCEPTIONS_TITLE.matches(attrs.app_id.as_deref().unwrap_or("")); - for idx in appid_matches.into_iter() { - if title_matches.matched(idx) { +pub fn should_be_floating(window: &CosmicSurface) -> bool { + // Check "window type" + match window { + CosmicSurface::Wayland(window) => { + if with_states(window.toplevel().wl_surface(), |states| { + let attrs = states + .data_map + .get::() + .unwrap() + .lock() + .unwrap(); + attrs.parent.is_some() + }) { return true; } } + CosmicSurface::X11(surface) => { + if surface.is_override_redirect() + || surface.is_popup() + || !matches!( + surface.window_type(), + None | Some(WmWindowType::Normal) | Some(WmWindowType::Utility) + ) + { + return true; + } + } + _ => {} + }; - false - }) + // Check if sizing suggest dialog + let max_size = window.max_size(); + let min_size = window.min_size(); + + if min_size.is_some() && min_size == max_size { + return true; + } + + // else take a look at our exceptions + let appid_matches = EXCEPTIONS_APPID.matches(&window.app_id()); + let title_matches = EXCEPTIONS_TITLE.matches(&window.title()); + for idx in appid_matches.into_iter() { + if title_matches.matched(idx) { + return true; + } + } + + false } diff --git a/src/shell/layout/tiling/mod.rs b/src/shell/layout/tiling/mod.rs index 9c62408f..99dde897 100644 --- a/src/shell/layout/tiling/mod.rs +++ b/src/shell/layout/tiling/mod.rs @@ -10,7 +10,7 @@ use crate::{ }, grabs::ResizeEdge, layout::Orientation, - OutputNotMapped, + CosmicSurface, OutputNotMapped, }, utils::prelude::*, wayland::handlers::xdg_shell::popup::get_popup_toplevel, @@ -20,12 +20,13 @@ use id_tree::{InsertBehavior, MoveBehavior, Node, NodeId, NodeIdError, RemoveBeh use smithay::{ backend::renderer::{ element::{AsRenderElements, RenderElement}, - ImportAll, Renderer, + ImportAll, ImportMem, Renderer, }, - desktop::{layer_map_for_output, space::SpaceElement, PopupKind, Window}, + desktop::{layer_map_for_output, space::SpaceElement, PopupKind}, input::{pointer::GrabStartData as PointerGrabStartData, Seat}, output::Output, utils::{IsAlive, Logical, Point, Rectangle, Scale, Serial}, + wayland::seat::WaylandFocus, }; use std::{borrow::Borrow, collections::HashMap, hash::Hash, sync::Arc}; @@ -1003,7 +1004,7 @@ impl TilingLayout { .find(|node| match node.data() { Data::Mapped { mapped, .. } => mapped .windows() - .any(|(w, _)| w.toplevel().wl_surface() == &toplevel_surface), + .any(|(w, _)| w.wl_surface().as_ref() == Some(&toplevel_surface)), _ => false, })?; @@ -1200,7 +1201,9 @@ impl TilingLayout { .flatten() } - pub fn windows(&self) -> impl Iterator)> + '_ { + pub fn windows( + &self, + ) -> impl Iterator)> + '_ { self.mapped().flat_map(|(output, mapped, loc)| { mapped .windows() @@ -1265,7 +1268,7 @@ impl TilingLayout { output: &Output, ) -> Result>, OutputNotMapped> where - R: Renderer + ImportAll + AsGlowRenderer, + R: Renderer + ImportAll + ImportMem + AsGlowRenderer, ::TextureId: 'static, CosmicMappedRenderElement: RenderElement, { diff --git a/src/shell/mod.rs b/src/shell/mod.rs index 926aaf4d..d61fd240 100644 --- a/src/shell/mod.rs +++ b/src/shell/mod.rs @@ -3,7 +3,7 @@ use std::{cell::RefCell, collections::HashMap}; use cosmic_protocols::workspace::v1::server::zcosmic_workspace_handle_v1::State as WState; use smithay::{ - desktop::{layer_map_for_output, LayerSurface, PopupManager, Window, WindowSurfaceType}, + desktop::{layer_map_for_output, LayerSurface, PopupManager, WindowSurfaceType}, input::Seat, output::Output, reexports::wayland_server::{protocol::wl_surface::WlSurface, DisplayHandle}, @@ -37,7 +37,7 @@ pub mod focus; pub mod grabs; pub mod layout; mod workspace; -pub use self::element::CosmicMappedRenderElement; +pub use self::element::{CosmicMappedRenderElement, CosmicSurface}; pub use self::workspace::*; use self::{ element::{CosmicMapped, CosmicWindow}, @@ -50,12 +50,12 @@ pub struct Shell { pub outputs: Vec, pub workspaces: WorkspaceMode, pub floating_default: bool, - pub pending_windows: Vec<(Window, Seat)>, + pub pending_windows: Vec<(CosmicSurface, Seat)>, pub pending_layers: Vec<(LayerSurface, Output, Seat)>, // wayland_state pub layer_shell_state: WlrLayerShellState, - pub toplevel_info_state: ToplevelInfoState, + pub toplevel_info_state: ToplevelInfoState, pub toplevel_management_state: ToplevelManagementState, pub xdg_shell_state: XdgShellState, pub workspace_state: WorkspaceState, @@ -144,7 +144,7 @@ impl WorkspaceSet { fn refresh<'a>( &mut self, state: &mut WorkspaceState, - toplevel_info: &mut ToplevelInfoState, + toplevel_info: &mut ToplevelInfoState, outputs: impl Iterator)>, ) { match self.amount { @@ -213,7 +213,7 @@ impl WorkspaceSet { &mut self, amount: usize, state: &mut WorkspaceState, - toplevel_info: &mut ToplevelInfoState, + toplevel_info: &mut ToplevelInfoState, outputs: impl Iterator)>, ) { if amount < self.workspaces.len() { @@ -957,7 +957,7 @@ impl Shell { .refresh(Some(&self.workspace_state)); } - pub fn map_window(state: &mut State, window: &Window, output: &Output) { + pub fn map_window(state: &mut State, window: &CosmicSurface, output: &Output) { let pos = state .common .shell @@ -979,7 +979,10 @@ impl Shell { .toplevel_info_state .toplevel_enter_workspace(&window, &workspace.handle); - let mapped = CosmicMapped::from(CosmicWindow::from(window.clone())); + let mapped = CosmicMapped::from(CosmicWindow::new( + window.clone(), + state.common.event_loop_handle.clone(), + )); #[cfg(feature = "debug")] { mapped.set_debug(state.common.egui.active); @@ -1111,12 +1114,14 @@ impl Shell { if let Some(workspace) = self.space_for(mapped) { let element_loc = workspace.element_geometry(mapped).unwrap().loc; for (toplevel, offset) in mapped.windows() { - let window_geo_offset = toplevel.geometry().loc; - update_reactive_popups( - &toplevel, - element_loc + offset + window_geo_offset, - self.outputs.iter(), - ); + if let CosmicSurface::Wayland(toplevel) = toplevel { + let window_geo_offset = toplevel.geometry().loc; + update_reactive_popups( + &toplevel, + element_loc + offset + window_geo_offset, + self.outputs.iter(), + ); + } } } } diff --git a/src/shell/workspace.rs b/src/shell/workspace.rs index 09c38a03..3a010cbe 100644 --- a/src/shell/workspace.rs +++ b/src/shell/workspace.rs @@ -19,17 +19,19 @@ use indexmap::IndexSet; use smithay::{ backend::renderer::{ element::{surface::WaylandSurfaceRenderElement, AsRenderElements, Element, RenderElement}, - ImportAll, Renderer, + ImportAll, ImportMem, Renderer, }, - desktop::{layer_map_for_output, space::SpaceElement, LayerSurface, Window}, + desktop::{layer_map_for_output, space::SpaceElement, LayerSurface}, input::{pointer::GrabStartData as PointerGrabStartData, Seat}, output::Output, reexports::{ - wayland_protocols::xdg::shell::server::xdg_toplevel::{self, ResizeEdge}, + wayland_protocols::xdg::shell::server::xdg_toplevel::ResizeEdge, wayland_server::protocol::wl_surface::WlSurface, }, - utils::{Buffer as BufferCoords, IsAlive, Logical, Physical, Point, Rectangle, Scale, Serial}, - wayland::shell::wlr_layer::Layer, + utils::{ + Buffer as BufferCoords, IsAlive, Logical, Physical, Point, Rectangle, Scale, Serial, Size, + }, + wayland::{seat::WaylandFocus, shell::wlr_layer::Layer}, }; use std::collections::HashMap; @@ -37,7 +39,7 @@ use super::{ element::CosmicMapped, focus::{FocusStack, FocusStackMut}, grabs::ResizeGrab, - CosmicMappedRenderElement, + CosmicMappedRenderElement, CosmicSurface, }; #[derive(Debug)] @@ -45,7 +47,7 @@ pub struct Workspace { pub tiling_layer: TilingLayout, pub floating_layer: FloatingLayout, pub tiling_enabled: bool, - pub fullscreen: HashMap, + pub fullscreen: HashMap, pub handle: WorkspaceHandle, pub focus_stack: FocusStacks, pub pending_buffers: Vec<(ScreencopySession, BufferParams)>, @@ -85,7 +87,7 @@ impl Workspace { if let Some(mapped) = self.element_for_surface(surface) { mapped .windows() - .find(|(w, _)| w.toplevel().wl_surface() == surface) + .find(|(w, _)| w.wl_surface().as_ref() == Some(surface)) .unwrap() .0 .on_commit(); @@ -131,7 +133,7 @@ impl Workspace { .chain(self.tiling_layer.mapped().map(|(_, w, _)| w)) .find(|e| { e.windows() - .any(|(w, _)| w.toplevel().wl_surface() == surface) + .any(|(w, _)| w.wl_surface().as_ref() == Some(surface)) }) } @@ -190,41 +192,37 @@ impl Workspace { }) } - pub fn maximize_request(&mut self, window: &Window, output: &Output) { + pub fn maximize_request(&mut self, window: &CosmicSurface, output: &Output) { if self.fullscreen.contains_key(output) { return; } self.floating_layer.maximize_request(window); - window.toplevel().with_pending_state(|state| { - state.states.set(xdg_toplevel::State::Maximized); - state.states.unset(xdg_toplevel::State::Fullscreen); - }); - + window.set_fullscreen(false); + window.set_maximized(true); self.set_fullscreen(window, output) } - pub fn unmaximize_request(&mut self, window: &Window) { + pub fn unmaximize_request(&mut self, window: &CosmicSurface) -> Option> { if self.fullscreen.values().any(|w| w == window) { self.unfullscreen_request(window); - self.floating_layer.unmaximize_request(window); + self.floating_layer.unmaximize_request(window) + } else { + None } } - pub fn fullscreen_request(&mut self, window: &Window, output: &Output) { + pub fn fullscreen_request(&mut self, window: &CosmicSurface, output: &Output) { if self.fullscreen.contains_key(output) { return; } - window.toplevel().with_pending_state(|state| { - state.states.set(xdg_toplevel::State::Fullscreen); - state.states.unset(xdg_toplevel::State::Maximized); - }); - + window.set_maximized(false); + window.set_fullscreen(true); self.set_fullscreen(window, output) } - fn set_fullscreen(&mut self, window: &Window, output: &Output) { + fn set_fullscreen(&mut self, window: &CosmicSurface, output: &Output) { if let Some(mapped) = self .mapped() .find(|m| m.windows().any(|(w, _)| &w == window)) @@ -232,46 +230,39 @@ impl Workspace { mapped.set_active(window); } - let xdg = window.toplevel(); - xdg.with_pending_state(|state| { - state.size = Some( - output - .current_mode() - .map(|m| m.size) - .unwrap_or((0, 0).into()) - .to_f64() - .to_logical(output.current_scale().fractional_scale()) - .to_i32_round(), - ); - }); - xdg.send_configure(); + window.set_size( + output + .current_mode() + .map(|m| m.size) + .unwrap_or((0, 0).into()) + .to_f64() + .to_logical(output.current_scale().fractional_scale()) + .to_i32_round(), + ); + window.send_configure(); self.fullscreen.insert(output.clone(), window.clone()); } - pub fn unfullscreen_request(&mut self, window: &Window) { + pub fn unfullscreen_request(&mut self, window: &CosmicSurface) { if self.fullscreen.values().any(|w| w == window) { - let xdg = window.toplevel(); - xdg.with_pending_state(|state| { - state.states.unset(xdg_toplevel::State::Fullscreen); - state.states.unset(xdg_toplevel::State::Maximized); - state.size = None; - }); + window.set_maximized(false); + window.set_fullscreen(false); self.floating_layer.refresh(); self.tiling_layer.refresh(); - xdg.send_configure(); + window.send_configure(); self.fullscreen.retain(|_, w| w != window); } } - pub fn maximize_toggle(&mut self, window: &Window, output: &Output) { + pub fn maximize_toggle(&mut self, window: &CosmicSurface, output: &Output) { if self.fullscreen.contains_key(output) { - self.unmaximize_request(window) + self.unmaximize_request(window); } else { - self.maximize_request(window, output) + self.maximize_request(window, output); } } - pub fn get_fullscreen(&self, output: &Output) -> Option<&Window> { + pub fn get_fullscreen(&self, output: &Output) -> Option<&CosmicSurface> { self.fullscreen.get(output).filter(|w| w.alive()) } @@ -303,7 +294,7 @@ impl Workspace { pub fn move_request( &mut self, - window: &Window, + window: &CosmicSurface, seat: &Seat, output: &Output, _serial: Serial, @@ -312,15 +303,12 @@ impl Workspace { let pointer = seat.get_pointer().unwrap(); let pos = pointer.current_location(); - let mapped = self - .element_for_surface(window.toplevel().wl_surface())? - .clone(); + let mapped = self.element_for_surface(&window.wl_surface()?)?.clone(); let mut initial_window_location = self.element_geometry(&mapped).unwrap().loc; if mapped.is_fullscreen() || mapped.is_maximized() { // If surface is maximized then unmaximize it - self.unmaximize_request(window); - let new_size = window.toplevel().with_pending_state(|state| state.size); + let new_size = self.unmaximize_request(window); let ratio = pos.x / output.geometry().size.w as f64; initial_window_location = new_size @@ -396,7 +384,7 @@ impl Workspace { .chain(self.tiling_layer.mapped().map(|(_, w, _)| w)) } - pub fn windows(&self) -> impl Iterator + '_ { + pub fn windows(&self) -> impl Iterator + '_ { self.floating_layer .windows() .chain(self.tiling_layer.windows().map(|(_, w, _)| w)) @@ -408,7 +396,7 @@ impl Workspace { output: &Output, ) -> Result>, OutputNotMapped> where - R: Renderer + ImportAll + AsGlowRenderer, + R: Renderer + ImportAll + ImportMem + AsGlowRenderer, ::TextureId: 'static, CosmicMappedRenderElement: RenderElement, { @@ -534,7 +522,7 @@ pub struct OutputNotMapped; pub enum WorkspaceRenderElement where - R: Renderer + ImportAll + AsGlowRenderer, + R: Renderer + ImportAll + ImportMem + AsGlowRenderer, ::TextureId: 'static, { Wayland(WaylandSurfaceRenderElement), @@ -543,7 +531,7 @@ where impl Element for WorkspaceRenderElement where - R: Renderer + ImportAll + AsGlowRenderer, + R: Renderer + ImportAll + ImportMem + AsGlowRenderer, ::TextureId: 'static, { fn id(&self) -> &smithay::backend::renderer::element::Id { @@ -609,7 +597,7 @@ where impl RenderElement for WorkspaceRenderElement where - R: Renderer + ImportAll + AsGlowRenderer, + R: Renderer + ImportAll + ImportMem + AsGlowRenderer, ::TextureId: 'static, CosmicMappedRenderElement: RenderElement, { @@ -640,7 +628,7 @@ where impl From> for WorkspaceRenderElement where - R: Renderer + ImportAll + AsGlowRenderer, + R: Renderer + ImportAll + ImportMem + AsGlowRenderer, ::TextureId: 'static, CosmicMappedRenderElement: RenderElement, { @@ -651,7 +639,7 @@ where impl From> for WorkspaceRenderElement where - R: Renderer + ImportAll + AsGlowRenderer, + R: Renderer + ImportAll + ImportMem + AsGlowRenderer, ::TextureId: 'static, CosmicMappedRenderElement: RenderElement, { diff --git a/src/utils/iced.rs b/src/utils/iced.rs index bb32c1d7..7da9132f 100644 --- a/src/utils/iced.rs +++ b/src/utils/iced.rs @@ -1,22 +1,31 @@ use std::{ + collections::HashMap, fmt, + hash::{Hash, Hasher}, sync::{mpsc::Receiver, Arc, Mutex}, }; -use cosmic::iced_native::{ - command::Action, - event::Event, - keyboard::{Event as KeyboardEvent, Modifiers as IcedModifiers}, - mouse::{Button as MouseButton, Event as MouseEvent, ScrollDelta}, - program::{Program, State}, - renderer::Style, - window::{Event as WindowEvent, Id}, - Debug, Point as IcedPoint, Size as IcedSize, -}; pub use cosmic::Renderer as IcedRenderer; use cosmic::Theme; -use iced_swbuf::{native::*, Backend}; +use cosmic::{ + iced_native::{ + command::Action, + event::Event, + keyboard::{Event as KeyboardEvent, Modifiers as IcedModifiers}, + mouse::{Button as MouseButton, Event as MouseEvent, ScrollDelta}, + program::{Program as IcedProgram, State}, + renderer::Style, + window::{Event as WindowEvent, Id}, + Command, Debug, Point as IcedPoint, Size as IcedSize, + }, + Element, +}; +use iced_softbuffer::{ + native::{raqote::DrawTarget, *}, + Backend, +}; +use ordered_float::OrderedFloat; use smithay::{ backend::{ input::{ButtonState, KeyState}, @@ -41,30 +50,64 @@ use smithay::{ }; #[derive(Debug)] -pub struct IcedElement + Send + 'static>( - Arc>>, -); +pub struct IcedElement(Arc>>); // SAFETY: We cannot really be sure about `iced_native::program::State` sadly, // but the rest should be fine. -unsafe impl + Send + 'static> Send for IcedElementInternal

{} +unsafe impl Send for IcedElementInternal

{} -impl + Send + 'static> Clone for IcedElement

{ +impl Clone for IcedElement

{ fn clone(&self) -> Self { IcedElement(self.0.clone()) } } -impl + Send + 'static> PartialEq for IcedElement

{ +impl PartialEq for IcedElement

{ fn eq(&self, other: &Self) -> bool { Arc::ptr_eq(&self.0, &other.0) } } +impl Eq for IcedElement

{} -struct IcedElementInternal + Send + 'static> { +impl Hash for IcedElement

{ + fn hash(&self, state: &mut H) { + (Arc::as_ptr(&self.0) as usize).hash(state) + } +} + +pub trait Program { + type Message: std::fmt::Debug + Send; + fn update(&mut self, message: Self::Message) -> Command { + let _ = message; + Command::none() + } + fn view(&self) -> Element<'_, Self::Message>; + + fn background(&self, target: &mut DrawTarget<&mut [u32]>) { + let _ = target; + } + fn foreground(&self, target: &mut DrawTarget<&mut [u32]>) { + let _ = target; + } +} + +struct ProgramWrapper(P); +impl IcedProgram for ProgramWrapper

{ + type Message =

::Message; + type Renderer = IcedRenderer; + + fn update(&mut self, message: Self::Message) -> Command { + self.0.update(message) + } + + fn view(&self) -> Element<'_, Self::Message> { + self.0.view() + } +} + +struct IcedElementInternal { // draw buffer - memory: MemoryRenderBuffer, - needs_redraw: bool, + buffers: HashMap, (MemoryRenderBuffer, bool)>, // state size: Size, @@ -73,7 +116,7 @@ struct IcedElementInternal + Send + 'static> // iced theme: Theme, renderer: IcedRenderer, - state: State

, + state: State>, debug: Debug, // futures @@ -83,53 +126,57 @@ struct IcedElementInternal + Send + 'static> rx: Receiver<

::Message>, } -impl + Send + 'static> fmt::Debug for IcedElementInternal

{ +impl fmt::Debug for IcedElementInternal

{ fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { f.debug_struct("IcedElementInternal") - .field("memory", &self.memory) - .field("needs_redraw", &self.needs_redraw) + .field("buffers", &"...") .field("size", &self.size) .field("cursor_pos", &self.cursor_pos) .field("theme", &self.theme) - .finish_non_exhaustive() + .field("renderer", &"...") + .field("state", &"...") + .field("debug", &self.debug) + .field("handle", &self.handle) + .field("scheduler", &self.scheduler) + .field("executor_token", &self.executor_token) + .field("rx", &self.rx) + .finish() } } -impl + Send + 'static> Drop for IcedElementInternal

{ +impl Drop for IcedElementInternal

{ fn drop(&mut self) { self.handle.remove(self.executor_token.take().unwrap()); } } -impl + Send + 'static> IcedElement

{ +impl IcedElement

{ pub fn new( program: P, size: impl Into>, - scale: i32, handle: LoopHandle<'static, crate::state::Data>, - ) -> calloop::error::Result> { + ) -> IcedElement

{ let size = size.into(); - let buffer_size = size.to_buffer(scale, Transform::Normal); - let mut renderer = IcedRenderer::new(Backend::new()); let mut debug = Debug::new(); let state = State::new( - program, + ProgramWrapper(program), IcedSize::new(size.w as f32, size.h as f32), &mut renderer, &mut debug, ); - let (executor, scheduler) = calloop::futures::executor()?; + let (executor, scheduler) = calloop::futures::executor().expect("Out of file descriptors"); let (tx, rx) = std::sync::mpsc::channel(); - let executor_token = handle.insert_source(executor, |message, _, _| { - tx.send(message); - })?; + let executor_token = handle + .insert_source(executor, move |message, _, _| { + let _ = tx.send(message); + }) + .ok(); let mut internal = IcedElementInternal { - memory: MemoryRenderBuffer::new(buffer_size, scale, Transform::Normal, None), - needs_redraw: true, + buffers: HashMap::new(), size, cursor_pos: None, theme: Theme::Dark, // TODO @@ -138,16 +185,41 @@ impl + Send + 'static> IcedElement

{ debug, handle, scheduler, - executor_token: Some(executor_token), + executor_token, rx, }; let _ = internal.update(true); - Ok(IcedElement(Arc::new(Mutex::new(internal)))) + IcedElement(Arc::new(Mutex::new(internal))) + } + + pub fn with_program(&self, func: impl FnOnce(&P) -> R) -> R { + let internal = self.0.lock().unwrap(); + func(&internal.state.program().0) + } + + pub fn loop_handle(&self) -> LoopHandle<'static, crate::state::Data> { + self.0.lock().unwrap().handle.clone() + } + + pub fn resize(&self, size: Size) { + let mut internal = self.0.lock().unwrap(); + let internal_ref = &mut *internal; + internal_ref.size = size; + for (scale, (buffer, needs_redraw)) in internal_ref.buffers.iter_mut() { + let buffer_size = internal_ref + .size + .to_f64() + .to_buffer(**scale, Transform::Normal) + .to_i32_round(); + *buffer = MemoryRenderBuffer::new(buffer_size, 1, Transform::Normal, None); + *needs_redraw = true; + } + let _ = internal_ref.update(true); } } -impl + Send + 'static> IcedElementInternal

{ +impl IcedElementInternal

{ fn update(&mut self, mut force: bool) -> Vec::Message>> { let cursor_pos = self.cursor_pos.unwrap_or(Point::from((-1.0, -1.0))); @@ -177,7 +249,9 @@ impl + Send + 'static> IcedElementInternal

+ Send + 'static> IcedElementInternal

+ Send + 'static> PointerTarget - for IcedElement

-{ +impl PointerTarget for IcedElement

{ fn enter( &self, _seat: &Seat, @@ -290,9 +362,7 @@ impl + Send + 'static> PointerTarget + Send + 'static> KeyboardTarget - for IcedElement

-{ +impl KeyboardTarget for IcedElement

{ fn enter( &self, _seat: &Seat, @@ -352,13 +422,13 @@ impl + Send + 'static> KeyboardTarget + Send + 'static> IsAlive for IcedElement

{ +impl IsAlive for IcedElement

{ fn alive(&self) -> bool { true } } -impl + Send + 'static> SpaceElement for IcedElement

{ +impl SpaceElement for IcedElement

{ fn bbox(&self) -> Rectangle { Rectangle::from_loc_and_size((0, 0), self.0.lock().unwrap().size) } @@ -380,12 +450,29 @@ impl + Send + 'static> SpaceElement for Iced let _ = internal.update(true); // TODO } - fn output_enter(&self, _output: &Output, _overlap: Rectangle) { - // TODO: Update scale, once supported to the highest one + fn output_enter(&self, output: &Output, _overlap: Rectangle) { + let mut internal = self.0.lock().unwrap(); + let scale = output.current_scale().fractional_scale(); + if !internal.buffers.contains_key(&OrderedFloat(scale)) { + let buffer_size = internal + .size + .to_f64() + .to_buffer(scale, Transform::Normal) + .to_i32_round(); + internal.buffers.insert( + OrderedFloat(scale), + ( + MemoryRenderBuffer::new(buffer_size, 1, Transform::Normal, None), + true, + ), + ); + } } - fn output_leave(&self, _output: &Output) { - // TODO: Update scale, once supported to the highest one + fn output_leave(&self, output: &Output) { + let mut internal = self.0.lock().unwrap(); + let scale = output.current_scale().fractional_scale(); + internal.buffers.remove(&OrderedFloat(scale)); } fn z_index(&self) -> u8 { @@ -398,7 +485,7 @@ impl + Send + 'static> SpaceElement for Iced impl AsRenderElements for IcedElement

where - P: Program + Send + 'static, + P: Program + Send + 'static, R: Renderer + ImportMem, ::TextureId: 'static, { @@ -408,69 +495,79 @@ where &self, renderer: &mut R, location: Point, - _scale: Scale, + scale: Scale, ) -> Vec { let mut internal = self.0.lock().unwrap(); let _ = internal.update(false); // TODO - - // makes partial borrows easier + // makes partial borrows easier let internal_ref = &mut *internal; - if internal_ref.needs_redraw { - let renderer = &mut internal_ref.renderer; - let size = internal_ref.size; - internal_ref - .memory - .render() - .draw(move |buf| { - let mut target = raqote::DrawTarget::from_backing( - size.w, - size.h, - bytemuck::cast_slice_mut::<_, u32>(buf), - ); + if let Some((buffer, ref mut needs_redraw)) = + internal_ref.buffers.get_mut(&OrderedFloat(scale.x)) + { + if *needs_redraw { + let renderer = &mut internal_ref.renderer; + let size = internal_ref + .size + .to_f64() + .to_buffer(scale.x, Transform::Normal) + .to_i32_round(); + let state_ref = &internal_ref.state; + buffer + .render() + .draw(move |buf| { + let mut target = raqote::DrawTarget::from_backing( + size.w, + size.h, + bytemuck::cast_slice_mut::<_, u32>(buf), + ); - target.clear(raqote::SolidSource::from_unpremultiplied_argb(0, 0, 0, 0)); + target.clear(raqote::SolidSource::from_unpremultiplied_argb(0, 0, 0, 0)); + state_ref.program().0.background(&mut target); - let draw_options = raqote::DrawOptions { - // Default to antialiasing off for now - antialias: raqote::AntialiasMode::None, - ..Default::default() - }; + let draw_options = raqote::DrawOptions { + // Default to antialiasing off for now + antialias: raqote::AntialiasMode::None, + ..Default::default() + }; - // Having at least one clip fixes some font rendering issues - target.push_clip_rect(raqote::IntRect::new( - raqote::IntPoint::new(0, 0), - raqote::IntPoint::new(size.w, size.h), - )); + // Having at least one clip fixes some font rendering issues + target.push_clip_rect(raqote::IntRect::new( + raqote::IntPoint::new(0, 0), + raqote::IntPoint::new(size.w, size.h), + )); - renderer.with_primitives(|backend, primitives| { - for primitive in primitives.iter() { - draw_primitive(&mut target, &draw_options, backend, primitive); - } - }); + renderer.with_primitives(|backend, primitives| { + for primitive in primitives.iter() { + draw_primitive( + &mut target, + &draw_options, + backend, + scale.x as f32, + primitive, + ); + } + }); - target.pop_clip(); + state_ref.program().0.foreground(&mut target); + Result::<_, ()>::Ok(vec![Rectangle::from_loc_and_size((0, 0), size)]) + }) + .unwrap(); + *needs_redraw = false; + } - Result::<_, ()>::Ok(vec![Rectangle::from_loc_and_size( - (0, 0), - size.to_buffer(1, Transform::Normal), - )]) - }) - .unwrap(); - internal_ref.needs_redraw = false; + if let Ok(buffer) = MemoryRenderBufferRenderElement::from_buffer( + renderer, + location.to_f64(), + &buffer, + None, + None, + None, + slog_scope::logger(), + ) { + return vec![C::from(buffer)]; + } } - - let Ok(buffer) = MemoryRenderBufferRenderElement::from_buffer( - renderer, - location.to_f64(), - &internal.memory, - None, - None, - None, - slog_scope::logger(), - ) else { - return Vec::new() - }; - vec![C::from(buffer)] + Vec::new() } } diff --git a/src/wayland/handlers/compositor.rs b/src/wayland/handlers/compositor.rs index 658d1111..b8ede805 100644 --- a/src/wayland/handlers/compositor.rs +++ b/src/wayland/handlers/compositor.rs @@ -1,6 +1,9 @@ // SPDX-License-Identifier: GPL-3.0-only -use crate::{state::BackendData, utils::prelude::*, wayland::protocols::screencopy::SessionType}; +use crate::{ + shell::CosmicSurface, state::BackendData, utils::prelude::*, + wayland::protocols::screencopy::SessionType, +}; use smithay::{ backend::renderer::utils::{on_commit_buffer_handler, with_renderer_surface_state}, delegate_compositor, @@ -8,6 +11,7 @@ use smithay::{ reexports::wayland_server::protocol::wl_surface::WlSurface, wayland::{ compositor::{with_states, CompositorHandler, CompositorState}, + seat::WaylandFocus, shell::{ wlr_layer::LayerSurfaceAttributes, xdg::{ @@ -113,17 +117,28 @@ impl CompositorHandler for State { .shell .pending_windows .iter() - .find(|(window, _)| window.toplevel().wl_surface() == surface) + .find(|(window, _)| window.wl_surface().as_ref() == Some(surface)) .cloned() { - let toplevel = window.toplevel(); - if self.toplevel_ensure_initial_configure(&toplevel) - && with_renderer_surface_state(&surface, |state| state.wl_buffer().is_some()) - { - let output = seat.active_output(); - Shell::map_window(self, &window, &output); - } else { - return; + match window { + CosmicSurface::Wayland(ref wl_window) => { + let toplevel = wl_window.toplevel(); + if self.toplevel_ensure_initial_configure(&toplevel) + && with_renderer_surface_state(&surface, |state| { + state.wl_buffer().is_some() + }) + { + let output = seat.active_output(); + Shell::map_window(self, &window, &output); + } else { + return; + } + } + CosmicSurface::X11(_) => { + let output = seat.active_output(); + Shell::map_window(self, &window, &output); + } + _ => unreachable!(), } } diff --git a/src/wayland/handlers/screencopy.rs b/src/wayland/handlers/screencopy.rs index 3a2edfb6..7ef0dd04 100644 --- a/src/wayland/handlers/screencopy.rs +++ b/src/wayland/handlers/screencopy.rs @@ -24,7 +24,7 @@ use smithay::{ Bind, Blit, BufferType, ExportMem, ImportAll, ImportMem, Offscreen, Renderer, }, }, - desktop::Window, + desktop::space::SpaceElement, output::Output, reexports::wayland_server::{ protocol::{wl_buffer::WlBuffer, wl_shm::Format as ShmFormat, wl_surface::WlSurface}, @@ -33,6 +33,7 @@ use smithay::{ utils::{IsAlive, Logical, Physical, Rectangle, Scale, Transform}, wayland::{ dmabuf::get_dmabuf, + seat::WaylandFocus, shm::{with_buffer_contents, with_buffer_contents_mut}, }, }; @@ -43,7 +44,7 @@ use crate::{ element::{AsGlowRenderer, CosmicElement}, render_output, render_workspace, CursorMode, CLEAR_COLOR, }, - shell::CosmicMappedRenderElement, + shell::{CosmicMappedRenderElement, CosmicSurface}, state::{BackendData, ClientState, Common, State}, utils::prelude::OutputExt, wayland::protocols::{ @@ -159,8 +160,8 @@ impl ScreencopyHandler for State { formats } - fn capture_toplevel(&mut self, toplevel: Window, session: Session) -> Vec { - let surface = toplevel.toplevel().wl_surface(); + fn capture_toplevel(&mut self, toplevel: CosmicSurface, session: Session) -> Vec { + let Some(surface) = toplevel.wl_surface() else { return Vec::new() }; let size = toplevel.geometry().size.to_buffer(1, Transform::Normal); let mut _kms_renderer = None; @@ -843,7 +844,7 @@ pub fn render_window_to_buffer( state: &mut State, session: &Session, params: BufferParams, - window: &Window, + window: &CosmicSurface, ) -> Result { let geometry = window.geometry(); let buffer_size = buffer_dimensions(¶ms.buffer).unwrap(); @@ -858,7 +859,7 @@ pub fn render_window_to_buffer( age: usize, session: &Session, common: &mut Common, - window: &Window, + window: &CosmicSurface, geometry: Rectangle, ) -> Result< (Option>>, RenderElementStates), @@ -889,12 +890,12 @@ pub fn render_window_to_buffer( for seat in common.seats() { if let Some(location) = { // we need to find the mapped element in that case - if let Some(mapped) = common - .shell - .element_for_surface(window.toplevel().wl_surface()) + if let Some(mapped) = window + .wl_surface() + .and_then(|surf| common.shell.element_for_surface(&surf)) { mapped.cursor_position(seat).and_then(|mut p| { - p -= mapped.active_window_offset().loc.to_f64(); + p -= mapped.active_window_offset().to_f64(); if p.x < 0. || p.y < 0. { None } else { @@ -1076,7 +1077,7 @@ impl UserdataExt for Output { } } -impl UserdataExt for Window { +impl UserdataExt for CosmicSurface { fn sessions(&self) -> Vec { self.user_data() .get::() @@ -1101,7 +1102,7 @@ impl State { pub fn schedule_window_session(&mut self, surface: &WlSurface) { if let Some(element) = self.common.shell.element_for_surface(surface).cloned() { let active = element.active_window(); - if active.toplevel().wl_surface() == surface { + if active.wl_surface().as_ref() == Some(surface) { for (session, params) in active.pending_buffers() { let window = active.clone(); self.common.event_loop_handle.insert_idle(move |data| { diff --git a/src/wayland/handlers/toplevel_info.rs b/src/wayland/handlers/toplevel_info.rs index 30fc99b9..40905c66 100644 --- a/src/wayland/handlers/toplevel_info.rs +++ b/src/wayland/handlers/toplevel_info.rs @@ -1,19 +1,54 @@ // SPDX-License-Identifier: GPL-3.0-only +use smithay::utils::user_data::UserDataMap; + use crate::{ + shell::CosmicSurface, state::State, wayland::protocols::toplevel_info::{ - delegate_toplevel_info, ToplevelInfoHandler, ToplevelInfoState, + delegate_toplevel_info, ToplevelInfoHandler, ToplevelInfoState, Window, }, }; impl ToplevelInfoHandler for State { - fn toplevel_info_state(&self) -> &ToplevelInfoState { + type Window = CosmicSurface; + + fn toplevel_info_state(&self) -> &ToplevelInfoState { &self.common.shell.toplevel_info_state } - fn toplevel_info_state_mut(&mut self) -> &mut ToplevelInfoState { + fn toplevel_info_state_mut(&mut self) -> &mut ToplevelInfoState { &mut self.common.shell.toplevel_info_state } } -delegate_toplevel_info!(State); +impl Window for CosmicSurface { + fn title(&self) -> String { + CosmicSurface::title(self) + } + + fn app_id(&self) -> String { + CosmicSurface::app_id(self) + } + + fn is_activated(&self) -> bool { + CosmicSurface::is_activated(self) + } + + fn is_maximized(&self) -> bool { + CosmicSurface::is_maximized(self) + } + + fn is_fullscreen(&self) -> bool { + CosmicSurface::is_fullscreen(self) + } + + fn is_minimized(&self) -> bool { + false // TODO + } + + fn user_data(&self) -> &UserDataMap { + CosmicSurface::user_data(self) + } +} + +delegate_toplevel_info!(State, CosmicSurface); diff --git a/src/wayland/handlers/toplevel_management.rs b/src/wayland/handlers/toplevel_management.rs index 373c7bf3..55ff08e9 100644 --- a/src/wayland/handlers/toplevel_management.rs +++ b/src/wayland/handlers/toplevel_management.rs @@ -1,11 +1,16 @@ // SPDX-License-Identifier: GPL-3.0-only -use smithay::{desktop::Window, input::Seat, reexports::wayland_server::DisplayHandle}; +use smithay::{input::Seat, reexports::wayland_server::DisplayHandle}; use crate::{ + shell::CosmicSurface, utils::prelude::*, - wayland::protocols::toplevel_management::{ - delegate_toplevel_management, ToplevelManagementHandler, ToplevelManagementState, + wayland::protocols::{ + toplevel_info::ToplevelInfoHandler, + toplevel_management::{ + delegate_toplevel_management, ManagementWindow, ToplevelManagementHandler, + ToplevelManagementState, + }, }, }; @@ -14,7 +19,12 @@ impl ToplevelManagementHandler for State { &mut self.common.shell.toplevel_management_state } - fn activate(&mut self, _dh: &DisplayHandle, window: &Window, seat: Option>) { + fn activate( + &mut self, + _dh: &DisplayHandle, + window: &::Window, + seat: Option>, + ) { for output in self .common .shell @@ -47,8 +57,14 @@ impl ToplevelManagementHandler for State { } } - fn close(&mut self, _dh: &DisplayHandle, window: &Window) { - window.toplevel().send_close(); + fn close(&mut self, _dh: &DisplayHandle, window: &::Window) { + window.close(); + } +} + +impl ManagementWindow for CosmicSurface { + fn close(&self) { + CosmicSurface::close(self) } } diff --git a/src/wayland/handlers/xdg_shell/mod.rs b/src/wayland/handlers/xdg_shell/mod.rs index 0d361802..c0ca0851 100644 --- a/src/wayland/handlers/xdg_shell/mod.rs +++ b/src/wayland/handlers/xdg_shell/mod.rs @@ -1,6 +1,6 @@ // SPDX-License-Identifier: GPL-3.0-only -use crate::{utils::prelude::*, wayland::protocols::screencopy::SessionType}; +use crate::{shell::CosmicSurface, utils::prelude::*, wayland::protocols::screencopy::SessionType}; use smithay::{ delegate_xdg_shell, desktop::{ @@ -39,7 +39,7 @@ impl XdgShellHandler for State { fn new_toplevel(&mut self, surface: ToplevelSurface) { let seat = self.common.last_active_seat().clone(); - let window = Window::new(surface); + let window = CosmicSurface::Wayland(Window::new(surface)); self.common.shell.toplevel_info_state.new_toplevel(&window); self.common.shell.pending_windows.push((window, seat)); // We will position the window after the first commit, when we know its size hints @@ -149,7 +149,7 @@ impl XdgShellHandler for State { let output = seat.active_output(); let (window, _) = mapped .windows() - .find(|(w, _)| w.toplevel() == &surface) + .find(|(w, _)| w.wl_surface().as_ref() == Some(surface.wl_surface())) .unwrap(); if let Some(grab) = workspace.move_request(&window, &seat, &output, serial, start_data) @@ -213,7 +213,7 @@ impl XdgShellHandler for State { if let Some(workspace) = self.common.shell.space_for_mut(&mapped) { let (window, _) = mapped .windows() - .find(|(w, _)| w.toplevel() == &surface) + .find(|(w, _)| w.wl_surface().as_ref() == Some(surface.wl_surface())) .unwrap(); workspace.maximize_request(&window, &output) } @@ -230,9 +230,9 @@ impl XdgShellHandler for State { if let Some(workspace) = self.common.shell.space_for_mut(&mapped) { let (window, _) = mapped .windows() - .find(|(w, _)| w.toplevel() == &surface) + .find(|(w, _)| w.wl_surface().as_ref() == Some(surface.wl_surface())) .unwrap(); - workspace.unmaximize_request(&window) + workspace.unmaximize_request(&window); } } } @@ -255,7 +255,7 @@ impl XdgShellHandler for State { if let Some(workspace) = self.common.shell.space_for_mut(&mapped) { let (window, _) = mapped .windows() - .find(|(w, _)| w.toplevel() == &surface) + .find(|(w, _)| w.wl_surface().as_ref() == Some(surface.wl_surface())) .unwrap(); workspace.fullscreen_request(&window, &output) } @@ -272,7 +272,7 @@ impl XdgShellHandler for State { if let Some(workspace) = self.common.shell.space_for_mut(&mapped) { let (window, _) = mapped .windows() - .find(|(w, _)| w.toplevel() == &surface) + .find(|(w, _)| w.wl_surface().as_ref() == Some(surface.wl_surface())) .unwrap(); workspace.unfullscreen_request(&window) } diff --git a/src/wayland/handlers/xdg_shell/popup.rs b/src/wayland/handlers/xdg_shell/popup.rs index f3574e4a..641d191e 100644 --- a/src/wayland/handlers/xdg_shell/popup.rs +++ b/src/wayland/handlers/xdg_shell/popup.rs @@ -3,7 +3,8 @@ use crate::{shell::Shell, utils::prelude::*}; use smithay::{ desktop::{ - layer_map_for_output, LayerSurface, PopupKind, PopupManager, Window, WindowSurfaceType, + layer_map_for_output, space::SpaceElement, LayerSurface, PopupKind, PopupManager, Window, + WindowSurfaceType, }, output::Output, reexports::{ @@ -15,6 +16,7 @@ use smithay::{ utils::{Logical, Point, Rectangle}, wayland::{ compositor::{get_role, with_states}, + seat::WaylandFocus, shell::xdg::{ PopupSurface, PositionerState, SurfaceCachedState, XdgPopupSurfaceRoleAttributes, XDG_POPUP_ROLE, @@ -31,12 +33,12 @@ impl Shell { let element_geo = workspace.element_geometry(elem).unwrap(); let (window, offset) = elem .windows() - .find(|(w, _)| w.toplevel().wl_surface() == &parent) + .find(|(w, _)| w.wl_surface().as_ref() == Some(&parent)) .unwrap(); let window_geo_offset = window.geometry().loc; let window_loc = element_geo.loc + offset + window_geo_offset; let anchor_point = get_anchor_point(&positioner) + window_loc; - if elem.is_tiled() { + if elem.is_tiled().unwrap() { if !unconstrain_xdg_popup_tile(surface, element_geo) { if let Some(output) = workspace.output_under(anchor_point) { unconstrain_xdg_popup(surface, window_loc, output.geometry()); diff --git a/src/wayland/protocols/screencopy.rs b/src/wayland/protocols/screencopy.rs index fe1b8ac8..b560e8d6 100644 --- a/src/wayland/protocols/screencopy.rs +++ b/src/wayland/protocols/screencopy.rs @@ -16,7 +16,6 @@ use smithay::{ allocator::Fourcc as DrmFourcc, drm::{DrmNode, NodeType}, }, - desktop::Window, input::{Seat, SeatHandler}, output::Output, reexports::wayland_server::{ @@ -30,10 +29,10 @@ use wayland_backend::{ server::{GlobalId, ObjectId}, }; -use crate::state::State; +use crate::{shell::CosmicSurface, state::State}; use super::{ - toplevel_info::window_from_handle, + toplevel_info::{window_from_handle, ToplevelInfoHandler}, workspace::{WorkspaceHandle, WorkspaceHandler}, }; @@ -112,11 +111,11 @@ impl SessionDataInnerInner { } } -#[derive(Debug, Clone, PartialEq, Eq, Hash)] +#[derive(Debug, Clone, PartialEq)] pub enum SessionType { Output(Output), Workspace(Output, WorkspaceHandle), - Window(Window), + Window(CosmicSurface), Cursor(Seat), #[doc(hidden)] Unknown, @@ -490,7 +489,7 @@ pub trait ScreencopyHandler { session: Session, ) -> Vec; - fn capture_toplevel(&mut self, toplevel: Window, session: Session) -> Vec; + fn capture_toplevel(&mut self, toplevel: CosmicSurface, session: Session) -> Vec; fn capture_cursor(&mut self, session: CursorSession) -> Vec; @@ -605,6 +604,7 @@ where D: GlobalDispatch + Dispatch> + Dispatch + + ToplevelInfoHandler + ScreencopyHandler + WorkspaceHandler + 'static, @@ -664,7 +664,7 @@ where } => { let Some(cursor) = check_cursor(cursor, &data, resource) else { return; }; - match window_from_handle(toplevel) { + match window_from_handle::<::Window>(toplevel) { Some(window) => { let session = match init_session( data_init, diff --git a/src/wayland/protocols/toplevel_info.rs b/src/wayland/protocols/toplevel_info.rs index 79cc8848..3f988deb 100644 --- a/src/wayland/protocols/toplevel_info.rs +++ b/src/wayland/protocols/toplevel_info.rs @@ -3,18 +3,13 @@ use std::{collections::HashMap, sync::Mutex}; use smithay::{ - desktop::Window, output::Output, - reexports::{ - wayland_protocols::xdg::shell::server::xdg_toplevel, - wayland_server::{ - backend::{ClientId, GlobalId, ObjectId}, - protocol::wl_surface::WlSurface, - Client, DataInit, Dispatch, DisplayHandle, GlobalDispatch, New, Resource, - }, + reexports::wayland_server::{ + backend::{ClientId, GlobalId, ObjectId}, + protocol::wl_surface::WlSurface, + Client, DataInit, Dispatch, DisplayHandle, GlobalDispatch, New, Resource, }, - utils::{IsAlive, Logical, Rectangle}, - wayland::{compositor::with_states, shell::xdg::XdgToplevelSurfaceRoleAttributes}, + utils::{user_data::UserDataMap, IsAlive, Logical, Rectangle}, }; use super::workspace::{WorkspaceHandle, WorkspaceHandler, WorkspaceState}; @@ -24,17 +19,28 @@ use cosmic_protocols::toplevel_info::v1::server::{ zcosmic_toplevel_info_v1::{self, ZcosmicToplevelInfoV1}, }; -pub struct ToplevelInfoState { +pub trait Window: IsAlive + Clone + Send { + fn title(&self) -> String; + fn app_id(&self) -> String; + fn is_activated(&self) -> bool; + fn is_maximized(&self) -> bool; + fn is_fullscreen(&self) -> bool; + fn is_minimized(&self) -> bool; + fn user_data(&self) -> &UserDataMap; +} + +pub struct ToplevelInfoState { dh: DisplayHandle, - pub(super) toplevels: Vec, + pub(super) toplevels: Vec, instances: Vec, global: GlobalId, _dispatch_data: std::marker::PhantomData, } pub trait ToplevelInfoHandler: WorkspaceHandler + Sized { - fn toplevel_info_state(&self) -> &ToplevelInfoState; - fn toplevel_info_state_mut(&mut self) -> &mut ToplevelInfoState; + type Window: Window; + fn toplevel_info_state(&self) -> &ToplevelInfoState; + fn toplevel_info_state_mut(&mut self) -> &mut ToplevelInfoState; } pub struct ToplevelInfoGlobalData { @@ -46,23 +52,22 @@ pub(super) struct ToplevelStateInner { instances: Vec, outputs: Vec, workspaces: Vec, - minimized: bool, pub(super) rectangles: HashMap)>, } pub(super) type ToplevelState = Mutex; -pub struct ToplevelHandleStateInner { +pub struct ToplevelHandleStateInner { outputs: Vec, workspaces: Vec, title: String, app_id: String, states: Vec, - pub(super) window: Window, + pub(super) window: W, } -pub type ToplevelHandleState = Mutex; +pub type ToplevelHandleState = Mutex>; -impl ToplevelHandleStateInner { - fn from_window(window: &Window) -> ToplevelHandleState { +impl ToplevelHandleStateInner { + fn from_window(window: &W) -> ToplevelHandleState { ToplevelHandleState::new(ToplevelHandleStateInner { outputs: Vec::new(), workspaces: Vec::new(), @@ -74,13 +79,15 @@ impl ToplevelHandleStateInner { } } -impl GlobalDispatch for ToplevelInfoState +impl GlobalDispatch + for ToplevelInfoState where D: GlobalDispatch + Dispatch - + Dispatch - + ToplevelInfoHandler + + Dispatch> + + ToplevelInfoHandler + 'static, + W: Window + 'static, { fn bind( state: &mut D, @@ -92,7 +99,7 @@ where ) { let instance = data_init.init(resource, ()); for window in &state.toplevel_info_state().toplevels { - send_toplevel_to_client::(dh, Some(state.workspace_state()), &instance, window); + send_toplevel_to_client::(dh, Some(state.workspace_state()), &instance, window); } state.toplevel_info_state_mut().instances.push(instance); } @@ -102,13 +109,14 @@ where } } -impl Dispatch for ToplevelInfoState +impl Dispatch for ToplevelInfoState where D: GlobalDispatch + Dispatch - + Dispatch - + ToplevelInfoHandler + + Dispatch> + + ToplevelInfoHandler + 'static, + W: Window, { fn request( state: &mut D, @@ -138,20 +146,21 @@ where } } -impl Dispatch for ToplevelInfoState +impl Dispatch, D> for ToplevelInfoState where D: GlobalDispatch + Dispatch - + Dispatch - + ToplevelInfoHandler + + Dispatch> + + ToplevelInfoHandler + 'static, + W: Window, { fn request( _state: &mut D, _client: &Client, _obj: &ZcosmicToplevelHandleV1, request: zcosmic_toplevel_handle_v1::Request, - _data: &ToplevelHandleState, + _data: &ToplevelHandleState, _dh: &DisplayHandle, _data_init: &mut DataInit<'_, D>, ) { @@ -165,7 +174,7 @@ where state: &mut D, _client: ClientId, resource: ObjectId, - _data: &ToplevelHandleState, + _data: &ToplevelHandleState, ) { for toplevel in &state.toplevel_info_state_mut().toplevels { if let Some(state) = toplevel.user_data().get::() { @@ -179,15 +188,16 @@ where } } -impl ToplevelInfoState +impl ToplevelInfoState where D: GlobalDispatch + Dispatch - + Dispatch - + ToplevelInfoHandler + + Dispatch> + + ToplevelInfoHandler + 'static, + W: Window + 'static, { - pub fn new(dh: &DisplayHandle, client_filter: F) -> ToplevelInfoState + pub fn new(dh: &DisplayHandle, client_filter: F) -> ToplevelInfoState where F: for<'a> Fn(&'a Client) -> bool + Send + Sync + 'static, { @@ -206,35 +216,35 @@ where } } - pub fn new_toplevel(&mut self, toplevel: &Window) { + pub fn new_toplevel(&mut self, toplevel: &W) { toplevel .user_data() .insert_if_missing(ToplevelState::default); for instance in &self.instances { - send_toplevel_to_client::(&self.dh, None, instance, toplevel); + send_toplevel_to_client::(&self.dh, None, instance, toplevel); } self.toplevels.push(toplevel.clone()); } - pub fn toplevel_enter_output(&mut self, toplevel: &Window, output: &Output) { + pub fn toplevel_enter_output(&mut self, toplevel: &W, output: &Output) { if let Some(state) = toplevel.user_data().get::() { state.lock().unwrap().outputs.push(output.clone()); } } - pub fn toplevel_leave_output(&mut self, toplevel: &Window, output: &Output) { + pub fn toplevel_leave_output(&mut self, toplevel: &W, output: &Output) { if let Some(state) = toplevel.user_data().get::() { state.lock().unwrap().outputs.retain(|o| o != output); } } - pub fn toplevel_enter_workspace(&mut self, toplevel: &Window, workspace: &WorkspaceHandle) { + pub fn toplevel_enter_workspace(&mut self, toplevel: &W, workspace: &WorkspaceHandle) { if let Some(state) = toplevel.user_data().get::() { state.lock().unwrap().workspaces.push(workspace.clone()); } } - pub fn toplevel_leave_workspace(&mut self, toplevel: &Window, workspace: &WorkspaceHandle) { + pub fn toplevel_leave_workspace(&mut self, toplevel: &W, workspace: &WorkspaceHandle) { if let Some(state) = toplevel.user_data().get::() { state.lock().unwrap().workspaces.retain(|w| w != workspace); } @@ -252,7 +262,7 @@ where if window.alive() { std::mem::drop(state); for instance in &self.instances { - send_toplevel_to_client::(&self.dh, workspace_state, instance, window); + send_toplevel_to_client::(&self.dh, workspace_state, instance, window); } true } else { @@ -276,17 +286,18 @@ where } } -fn send_toplevel_to_client( +fn send_toplevel_to_client( dh: &DisplayHandle, workspace_state: Option<&WorkspaceState>, info: &ZcosmicToplevelInfoV1, - window: &Window, + window: &W, ) where D: GlobalDispatch + Dispatch - + Dispatch - + ToplevelInfoHandler + + Dispatch> + + ToplevelInfoHandler + 'static, + W: Window, { let mut state = window .user_data() @@ -322,90 +333,53 @@ fn send_toplevel_to_client( }; let mut handle_state = instance - .data::() + .data::>() .unwrap() .lock() .unwrap(); let mut changed = false; - with_states(window.toplevel().wl_surface(), |states| { - let attributes = states - .data_map - .get::>() - .unwrap() - .lock() - .unwrap(); + if handle_state.title != window.title() { + handle_state.title = window.title(); + instance.title(handle_state.title.clone()); + changed = true; + } + if handle_state.app_id != window.app_id() { + handle_state.app_id = window.app_id(); + instance.app_id(handle_state.app_id.clone()); + changed = true; + } - if handle_state.title != attributes.title.as_deref().unwrap_or(&"") { - handle_state.title = attributes.title.clone().unwrap_or_else(String::new); - instance.title(handle_state.title.clone()); - changed = true; + if (handle_state.states.contains(&States::Maximized) != window.is_maximized()) + || (handle_state.states.contains(&States::Fullscreen) != window.is_fullscreen()) + || (handle_state.states.contains(&States::Activated) != window.is_activated()) + || (handle_state.states.contains(&States::Minimized) != window.is_minimized()) + { + let mut states = Vec::new(); + if window.is_maximized() { + states.push(States::Maximized); } - if handle_state.app_id != attributes.app_id.as_deref().unwrap_or(&"") { - handle_state.app_id = attributes.app_id.clone().unwrap_or_else(String::new); - instance.app_id(handle_state.app_id.clone()); - changed = true; + if window.is_fullscreen() { + states.push(States::Fullscreen); } + if window.is_activated() { + states.push(States::Activated); + } + if window.is_minimized() { + states.push(States::Minimized); + } + handle_state.states = states.clone(); - if (handle_state.states.contains(&States::Maximized) - != attributes - .current - .states - .contains(xdg_toplevel::State::Maximized)) - || (handle_state.states.contains(&States::Fullscreen) - != attributes - .current - .states - .contains(xdg_toplevel::State::Fullscreen)) - || (handle_state.states.contains(&States::Activated) - != attributes - .current - .states - .contains(xdg_toplevel::State::Activated)) - || (handle_state.states.contains(&States::Minimized) != state.minimized) - { - let mut states = Vec::new(); - if attributes - .current - .states - .contains(xdg_toplevel::State::Maximized) - { - states.push(States::Maximized); - } - if attributes - .current - .states - .contains(xdg_toplevel::State::Fullscreen) - { - states.push(States::Fullscreen); - } - if attributes - .current - .states - .contains(xdg_toplevel::State::Activated) - { - states.push(States::Activated); - } - if attributes - .current - .states - .contains(xdg_toplevel::State::Maximized) - { - states.push(States::Maximized); - } - handle_state.states = states.clone(); - - let states: Vec = { - let ratio = std::mem::size_of::() / std::mem::size_of::(); - let ptr = states.as_mut_ptr() as *mut u8; - let len = states.len() * ratio; - let cap = states.capacity() * ratio; - std::mem::forget(states); - unsafe { Vec::from_raw_parts(ptr, len, cap) } - }; - instance.state(states); - changed = true; - } - }); + let states: Vec = { + let ratio = std::mem::size_of::() / std::mem::size_of::(); + let ptr = states.as_mut_ptr() as *mut u8; + let len = states.len() * ratio; + let cap = states.capacity() * ratio; + std::mem::forget(states); + unsafe { Vec::from_raw_parts(ptr, len, cap) } + }; + instance.state(states); + changed = true; + } if let Ok(client) = dh.get_client(instance.id()) { for new_output in state @@ -464,23 +438,23 @@ fn send_toplevel_to_client( } } -pub fn window_from_handle(handle: ZcosmicToplevelHandleV1) -> Option { +pub fn window_from_handle(handle: ZcosmicToplevelHandleV1) -> Option { handle - .data::() + .data::>() .map(|state| state.lock().unwrap().window.clone()) } macro_rules! delegate_toplevel_info { - ($(@<$( $lt:tt $( : $clt:tt $(+ $dlt:tt )* )? ),+>)? $ty: ty) => { + ($(@<$( $lt:tt $( : $clt:tt $(+ $dlt:tt )* )? ),+>)? $ty: ty, $window: ty) => { smithay::reexports::wayland_server::delegate_global_dispatch!($(@< $( $lt $( : $clt $(+ $dlt )* )? ),+ >)? $ty: [ cosmic_protocols::toplevel_info::v1::server::zcosmic_toplevel_info_v1::ZcosmicToplevelInfoV1: $crate::wayland::protocols::toplevel_info::ToplevelInfoGlobalData - ] => $crate::wayland::protocols::toplevel_info::ToplevelInfoState); + ] => $crate::wayland::protocols::toplevel_info::ToplevelInfoState); smithay::reexports::wayland_server::delegate_dispatch!($(@< $( $lt $( : $clt $(+ $dlt )* )? ),+ >)? $ty: [ cosmic_protocols::toplevel_info::v1::server::zcosmic_toplevel_info_v1::ZcosmicToplevelInfoV1: () - ] => $crate::wayland::protocols::toplevel_info::ToplevelInfoState); + ] => $crate::wayland::protocols::toplevel_info::ToplevelInfoState); smithay::reexports::wayland_server::delegate_dispatch!($(@< $( $lt $( : $clt $(+ $dlt )* )? ),+ >)? $ty: [ - cosmic_protocols::toplevel_info::v1::server::zcosmic_toplevel_handle_v1::ZcosmicToplevelHandleV1: $crate::wayland::protocols::toplevel_info::ToplevelHandleState - ] => $crate::wayland::protocols::toplevel_info::ToplevelInfoState); + cosmic_protocols::toplevel_info::v1::server::zcosmic_toplevel_handle_v1::ZcosmicToplevelHandleV1: $crate::wayland::protocols::toplevel_info::ToplevelHandleState<$window> + ] => $crate::wayland::protocols::toplevel_info::ToplevelInfoState); }; } pub(crate) use delegate_toplevel_info; diff --git a/src/wayland/protocols/toplevel_management.rs b/src/wayland/protocols/toplevel_management.rs index 4dbad39f..ffb807c0 100644 --- a/src/wayland/protocols/toplevel_management.rs +++ b/src/wayland/protocols/toplevel_management.rs @@ -1,7 +1,6 @@ // SPDX-License-Identifier: GPL-3.0-only use smithay::{ - desktop::Window, input::{Seat, SeatHandler}, output::Output, reexports::wayland_server::{ @@ -17,7 +16,7 @@ use cosmic_protocols::toplevel_management::v1::server::zcosmic_toplevel_manager_ self, ZcosmicToplevelManagerV1, }; -use super::toplevel_info::{window_from_handle, ToplevelInfoHandler, ToplevelState}; +use super::toplevel_info::{window_from_handle, ToplevelInfoHandler, ToplevelState, Window}; pub struct ToplevelManagementState { instances: Vec, @@ -25,19 +24,39 @@ pub struct ToplevelManagementState { global: GlobalId, } +pub trait ManagementWindow: Window { + fn close(&self); +} + #[allow(unused_variables)] -pub trait ToplevelManagementHandler: ToplevelInfoHandler + SeatHandler { +pub trait ToplevelManagementHandler: ToplevelInfoHandler + SeatHandler +where + ::Window: ManagementWindow, +{ fn toplevel_management_state(&mut self) -> &mut ToplevelManagementState; - fn activate(&mut self, dh: &DisplayHandle, window: &Window, seat: Option>) {} - fn close(&mut self, dh: &DisplayHandle, window: &Window) {} + fn activate( + &mut self, + dh: &DisplayHandle, + window: &::Window, + seat: Option>, + ) { + } + fn close(&mut self, dh: &DisplayHandle, window: &::Window) {} - fn fullscreen(&mut self, dh: &DisplayHandle, window: &Window, output: Option) {} - fn unfullscreen(&mut self, dh: &DisplayHandle, window: &Window) {} - fn maximize(&mut self, dh: &DisplayHandle, window: &Window) {} - fn unmaximize(&mut self, dh: &DisplayHandle, window: &Window) {} - fn minimize(&mut self, dh: &DisplayHandle, window: &Window) {} - fn unminimize(&mut self, dh: &DisplayHandle, window: &Window) {} + fn fullscreen( + &mut self, + dh: &DisplayHandle, + window: &::Window, + output: Option, + ) { + } + fn unfullscreen(&mut self, dh: &DisplayHandle, window: &::Window) { + } + fn maximize(&mut self, dh: &DisplayHandle, window: &::Window) {} + fn unmaximize(&mut self, dh: &DisplayHandle, window: &::Window) {} + fn minimize(&mut self, dh: &DisplayHandle, window: &::Window) {} + fn unminimize(&mut self, dh: &DisplayHandle, window: &::Window) {} } pub struct ToplevelManagerGlobalData { @@ -55,6 +74,7 @@ impl ToplevelManagementState { + Dispatch + ToplevelManagementHandler + 'static, + ::Window: ManagementWindow, F: for<'a> Fn(&'a Client) -> bool + Send + Sync + 'static, { let global = dh.create_global::( @@ -72,7 +92,7 @@ impl ToplevelManagementState { pub fn rectangle_for( &mut self, - window: &Window, + window: &impl ManagementWindow, client: &ClientId, ) -> Option<(WlSurface, Rectangle)> { if let Some(state) = window.user_data().get::() { @@ -94,6 +114,7 @@ where + Dispatch + ToplevelManagementHandler + 'static, + ::Window: ManagementWindow, { fn bind( state: &mut D, @@ -128,6 +149,7 @@ where + Dispatch + ToplevelManagementHandler + 'static, + ::Window: ManagementWindow, { fn request( state: &mut D, @@ -179,7 +201,8 @@ where width, height, } => { - let window = window_from_handle(toplevel).unwrap(); + let window = + window_from_handle::<::Window>(toplevel).unwrap(); if let Some(toplevel_state) = window.user_data().get::() { let mut toplevel_state = toplevel_state.lock().unwrap(); if let Some(client) = surface.client() {