diff --git a/Cargo.lock b/Cargo.lock index 79256024..6e12c741 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -61,6 +61,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "2c99f64d1e06488f620f932677e24bc6e2897582980441ae90a671415bd7ec2f" dependencies = [ "cfg-if", + "getrandom", "once_cell", "serde", "version_check", @@ -95,20 +96,23 @@ checksum = "3aa2999eb46af81abb65c2d30d446778d7e613b60bbf4e174a027e80f90a3c14" [[package]] name = "android-activity" -version = "0.4.3" +version = "0.5.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "64529721f27c2314ced0890ce45e469574a73e5e6fdd6e9da1860eb29285f5e0" +checksum = "052ad56e336bcc615a214bffbeca6c181ee9550acec193f0327e0b103b033a4d" dependencies = [ "android-properties", - "bitflags 1.3.2", + "bitflags 2.4.0", "cc", + "cesu8", + "jni", "jni-sys", "libc", "log", "ndk", "ndk-context", "ndk-sys", - "num_enum 0.6.1", + "num_enum", + "thiserror", ] [[package]] @@ -183,6 +187,12 @@ version = "0.7.4" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "96d30a06541fbafbc7f82ed10c06164cfbd2c401138f6addd8404629c4b16711" +[[package]] +name = "as-raw-xcb-connection" +version = "1.0.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "2d5f312b0a56c5cdf967c0aeb67f6289603354951683bc97ddc595ab974ba9aa" + [[package]] name = "ash" version = "0.37.3+1.3.251" @@ -198,6 +208,12 @@ version = "4.4.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "b9441c6b2fe128a7c2bf680a44c34d0df31ce09e5b7e401fcca3faa483dbc921" +[[package]] +name = "atomic-waker" +version = "1.1.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1505bd5d3d116872e7271a6d4e16d81d0c8570876c8de68093a09ac269d8aac0" + [[package]] name = "atomicwrites" version = "0.4.1" @@ -304,21 +320,21 @@ dependencies = [ [[package]] name = "block-sys" -version = "0.1.0-beta.1" +version = "0.2.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "0fa55741ee90902547802152aaf3f8e5248aab7e21468089560d4c8840561146" +checksum = "2dd7cf50912cddc06dc5ea7c08c5e81c1b2c842a70d19def1848d54c586fed92" dependencies = [ "objc-sys", ] [[package]] name = "block2" -version = "0.2.0-alpha.6" +version = "0.3.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "8dd9e63c1744f755c2f60332b88de39d341e5e86239014ad839bd71c106dec42" +checksum = "15b55663a85f33501257357e6421bb33e769d5c9ffb5ba0921c975a123e35e68" dependencies = [ "block-sys", - "objc2-encode", + "objc2", ] [[package]] @@ -354,33 +370,38 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "14c189c53d098945499cdfa7ecc63567cf3886b3332b312a5b4585d8d3a6a610" [[package]] -name = "calloop" -version = "0.10.6" +name = "bytes" +version = "1.5.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "52e0d00eb1ea24371a97d2da6201c6747a633dc6dc1988ef503403b4c59504a8" -dependencies = [ - "bitflags 1.3.2", - "log", - "nix 0.25.1", - "slotmap", - "thiserror", - "vec_map", -] +checksum = "a2bd12c1caf447e69cd4528f47f94d203fd2582878ecb9e9465484c4148a8223" [[package]] name = "calloop" -version = "0.12.2" -source = "git+https://github.com/Smithay/calloop?rev=71b6e633b1#71b6e633b199cd4a2b469f6286970fca7a368e14" +version = "0.12.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7b50b5a44d59a98c55a9eeb518f39bf7499ba19fd98ee7d22618687f3f10adbf" dependencies = [ "async-task", "bitflags 2.4.0", "log", "polling", - "rustix 0.38.17", + "rustix 0.38.20", "slab", "thiserror", ] +[[package]] +name = "calloop-wayland-source" +version = "0.2.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0f0ea9b9476c7fad82841a8dbb380e2eae480c21910feba80725b46931ed8f02" +dependencies = [ + "calloop", + "rustix 0.38.20", + "wayland-backend 0.3.2", + "wayland-client 0.31.1", +] + [[package]] name = "cc" version = "1.0.83" @@ -391,6 +412,12 @@ dependencies = [ "libc", ] +[[package]] +name = "cesu8" +version = "1.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6d43a04d8753f35258c91f8ec639f792891f748a1edbd759cf1dcea3382ad83c" + [[package]] name = "cfg-if" version = "1.0.0" @@ -423,8 +450,8 @@ dependencies = [ "block", "cocoa-foundation", "core-foundation", - "core-graphics", - "foreign-types", + "core-graphics 0.22.3", + "foreign-types 0.3.2", "libc", "objc", ] @@ -465,6 +492,16 @@ version = "0.2.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "bf43edc576402991846b093a7ca18a3477e0ef9c588cde84964b5d3e43016642" +[[package]] +name = "combine" +version = "4.6.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "35ed6e9d84f0b51a7f52daf1c7d71dd136fd7a3f41a8462b8cdb8c78d920fad4" +dependencies = [ + "bytes", + "memchr", +] + [[package]] name = "concurrent-queue" version = "2.3.0" @@ -499,7 +536,20 @@ dependencies = [ "bitflags 1.3.2", "core-foundation", "core-graphics-types", - "foreign-types", + "foreign-types 0.3.2", + "libc", +] + +[[package]] +name = "core-graphics" +version = "0.23.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "970a29baf4110c26fedbc7f82107d42c23f7e88e404c4577ed73fe99ff85a212" +dependencies = [ + "bitflags 1.3.2", + "core-foundation", + "core-graphics-types", + "foreign-types 0.5.0", "libc", ] @@ -521,7 +571,7 @@ dependencies = [ "anyhow", "bitflags 2.4.0", "bytemuck", - "calloop 0.12.2", + "calloop", "cosmic-comp-config", "cosmic-config", "cosmic-protocols", @@ -536,6 +586,7 @@ dependencies = [ "indexmap 2.0.2", "keyframe", "lazy_static", + "libc", "libcosmic", "libsystemd", "log-panics", @@ -562,7 +613,7 @@ dependencies = [ "wayland-scanner 0.31.0", "xcursor", "xdg", - "xkbcommon 0.7.0", + "xkbcommon", ] [[package]] @@ -576,10 +627,10 @@ dependencies = [ [[package]] name = "cosmic-config" version = "0.1.0" -source = "git+https://github.com/pop-os/libcosmic/?rev=f91287d#f91287dec2297df41a339c1106850c4cf179f67f" +source = "git+https://github.com/pop-os/libcosmic/#7cc791a3f5c0e54e091cce800a638aa5567055ef" dependencies = [ "atomicwrites", - "calloop 0.12.2", + "calloop", "cosmic-config-derive", "dirs 5.0.1", "iced_futures", @@ -591,7 +642,7 @@ dependencies = [ [[package]] name = "cosmic-config-derive" version = "0.1.0" -source = "git+https://github.com/pop-os/libcosmic/?rev=f91287d#f91287dec2297df41a339c1106850c4cf179f67f" +source = "git+https://github.com/pop-os/libcosmic/#7cc791a3f5c0e54e091cce800a638aa5567055ef" dependencies = [ "quote", "syn 1.0.109", @@ -604,7 +655,7 @@ source = "git+https://github.com/pop-os/cosmic-protocols?branch=main#5c03417a08e dependencies = [ "bitflags 2.4.0", "wayland-backend 0.3.2", - "wayland-protocols 0.31.0", + "wayland-protocols", "wayland-scanner 0.31.0", "wayland-server", ] @@ -632,7 +683,7 @@ dependencies = [ [[package]] name = "cosmic-theme" version = "0.1.0" -source = "git+https://github.com/pop-os/libcosmic/?rev=f91287d#f91287dec2297df41a339c1106850c4cf179f67f" +source = "git+https://github.com/pop-os/libcosmic/#7cc791a3f5c0e54e091cce800a638aa5567055ef" dependencies = [ "almost", "cosmic-config", @@ -1100,25 +1151,14 @@ checksum = "5443807d6dff69373d433ab9ef5378ad8df50ca6298caf15de6e52e24aaf54d5" [[package]] name = "errno" -version = "0.3.4" +version = "0.3.5" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "add4f07d43996f76ef320709726a556a9d4f965d9410d8d0271132d2f8293480" +checksum = "ac3e13f66a2f95e32a39eaa81f6b95d42878ca0e1db0c7543723dfe12557e860" dependencies = [ - "errno-dragonfly", "libc", "windows-sys 0.48.0", ] -[[package]] -name = "errno-dragonfly" -version = "0.1.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "aa68f1b12764fab894d2755d2518754e71b4fd80ecfb822714a1206c2aab39bf" -dependencies = [ - "cc", - "libc", -] - [[package]] name = "etagere" version = "0.2.8" @@ -1325,7 +1365,28 @@ version = "0.3.2" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "f6f339eb8adc052cd2ca78910fda869aefa38d22d5cb648e6485e4d3fc06f3b1" dependencies = [ - "foreign-types-shared", + "foreign-types-shared 0.1.1", +] + +[[package]] +name = "foreign-types" +version = "0.5.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d737d9aa519fb7b749cbc3b962edcf310a8dd1f4b67c91c4f83975dbdd17d965" +dependencies = [ + "foreign-types-macros", + "foreign-types-shared 0.3.1", +] + +[[package]] +name = "foreign-types-macros" +version = "0.2.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1a5c6c585bc94aaf2c7b51dd4c2ba22680844aba4c687be581871a6f518c5742" +dependencies = [ + "proc-macro2", + "quote", + "syn 2.0.37", ] [[package]] @@ -1334,6 +1395,12 @@ version = "0.1.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "00b0228411908ca8685dba7fc2cdd70ec9990a6e753e89b6ac91a84c40fbaf4b" +[[package]] +name = "foreign-types-shared" +version = "0.3.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "aa9a19cbb55df58761df49b23516a86d432839add4af60fc256da840f66ed35b" + [[package]] name = "form_urlencoded" version = "1.2.0" @@ -1801,7 +1868,7 @@ dependencies = [ [[package]] name = "iced" version = "0.10.0" -source = "git+https://github.com/pop-os/libcosmic/?rev=f91287d#f91287dec2297df41a339c1106850c4cf179f67f" +source = "git+https://github.com/pop-os/libcosmic/#7cc791a3f5c0e54e091cce800a638aa5567055ef" dependencies = [ "iced_core", "iced_futures", @@ -1814,7 +1881,7 @@ dependencies = [ [[package]] name = "iced_core" version = "0.10.0" -source = "git+https://github.com/pop-os/libcosmic/?rev=f91287d#f91287dec2297df41a339c1106850c4cf179f67f" +source = "git+https://github.com/pop-os/libcosmic/#7cc791a3f5c0e54e091cce800a638aa5567055ef" dependencies = [ "bitflags 1.3.2", "instant", @@ -1827,7 +1894,7 @@ dependencies = [ [[package]] name = "iced_futures" version = "0.7.0" -source = "git+https://github.com/pop-os/libcosmic/?rev=f91287d#f91287dec2297df41a339c1106850c4cf179f67f" +source = "git+https://github.com/pop-os/libcosmic/#7cc791a3f5c0e54e091cce800a638aa5567055ef" dependencies = [ "futures", "iced_core", @@ -1839,7 +1906,7 @@ dependencies = [ [[package]] name = "iced_graphics" version = "0.9.0" -source = "git+https://github.com/pop-os/libcosmic/?rev=f91287d#f91287dec2297df41a339c1106850c4cf179f67f" +source = "git+https://github.com/pop-os/libcosmic/#7cc791a3f5c0e54e091cce800a638aa5567055ef" dependencies = [ "bitflags 1.3.2", "bytemuck", @@ -1850,27 +1917,27 @@ dependencies = [ "kamadak-exif", "log", "lyon_path", - "raw-window-handle", + "raw-window-handle 0.5.2", "thiserror", ] [[package]] name = "iced_renderer" version = "0.1.0" -source = "git+https://github.com/pop-os/libcosmic/?rev=f91287d#f91287dec2297df41a339c1106850c4cf179f67f" +source = "git+https://github.com/pop-os/libcosmic/#7cc791a3f5c0e54e091cce800a638aa5567055ef" dependencies = [ "iced_graphics", "iced_tiny_skia", "iced_wgpu", "log", - "raw-window-handle", + "raw-window-handle 0.5.2", "thiserror", ] [[package]] name = "iced_runtime" version = "0.1.1" -source = "git+https://github.com/pop-os/libcosmic/?rev=f91287d#f91287dec2297df41a339c1106850c4cf179f67f" +source = "git+https://github.com/pop-os/libcosmic/#7cc791a3f5c0e54e091cce800a638aa5567055ef" dependencies = [ "iced_core", "iced_futures", @@ -1880,7 +1947,7 @@ dependencies = [ [[package]] name = "iced_style" version = "0.9.0" -source = "git+https://github.com/pop-os/libcosmic/?rev=f91287d#f91287dec2297df41a339c1106850c4cf179f67f" +source = "git+https://github.com/pop-os/libcosmic/#7cc791a3f5c0e54e091cce800a638aa5567055ef" dependencies = [ "iced_core", "once_cell", @@ -1890,14 +1957,14 @@ dependencies = [ [[package]] name = "iced_tiny_skia" version = "0.1.0" -source = "git+https://github.com/pop-os/libcosmic/?rev=f91287d#f91287dec2297df41a339c1106850c4cf179f67f" +source = "git+https://github.com/pop-os/libcosmic/#7cc791a3f5c0e54e091cce800a638aa5567055ef" dependencies = [ "bytemuck", "cosmic-text", "iced_graphics", "kurbo 0.9.5", "log", - "raw-window-handle", + "raw-window-handle 0.5.2", "resvg 0.35.0", "rustc-hash", "softbuffer", @@ -1908,7 +1975,7 @@ dependencies = [ [[package]] name = "iced_wgpu" version = "0.11.1" -source = "git+https://github.com/pop-os/libcosmic/?rev=f91287d#f91287dec2297df41a339c1106850c4cf179f67f" +source = "git+https://github.com/pop-os/libcosmic/#7cc791a3f5c0e54e091cce800a638aa5567055ef" dependencies = [ "bitflags 1.3.2", "bytemuck", @@ -1920,7 +1987,7 @@ dependencies = [ "log", "lyon", "once_cell", - "raw-window-handle", + "raw-window-handle 0.5.2", "resvg 0.35.0", "rustc-hash", "twox-hash", @@ -1930,7 +1997,7 @@ dependencies = [ [[package]] name = "iced_widget" version = "0.1.3" -source = "git+https://github.com/pop-os/libcosmic/?rev=f91287d#f91287dec2297df41a339c1106850c4cf179f67f" +source = "git+https://github.com/pop-os/libcosmic/#7cc791a3f5c0e54e091cce800a638aa5567055ef" dependencies = [ "iced_renderer", "iced_runtime", @@ -1941,6 +2008,17 @@ dependencies = [ "unicode-segmentation", ] +[[package]] +name = "icrate" +version = "0.0.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "99d3aaff8a54577104bafdf686ff18565c3b6903ca5782a2026ef06e2c7aa319" +dependencies = [ + "block2", + "dispatch", + "objc2", +] + [[package]] name = "id_tree" version = "1.8.0" @@ -2067,9 +2145,6 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "7a5bbe824c507c5da5956355e86a746d82e0e1464f65d862cc5e71da70e94b2c" dependencies = [ "cfg-if", - "js-sys", - "wasm-bindgen", - "web-sys", ] [[package]] @@ -2114,6 +2189,22 @@ version = "1.0.9" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "af150ab688ff2122fcef229be89cb50dd66af9e01a4ff320cc137eecc9bacc38" +[[package]] +name = "jni" +version = "0.21.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1a87aa2bb7d2af34197c04845522473242e1aa17c12f4935d5856491a7fb8c97" +dependencies = [ + "cesu8", + "cfg-if", + "combine", + "jni-sys", + "log", + "thiserror", + "walkdir", + "windows-sys 0.45.0", +] + [[package]] name = "jni-sys" version = "0.3.0" @@ -2235,14 +2326,14 @@ checksum = "03087c2bad5e1034e8cace5926dec053fb3790248370865f5117a7d0213354c8" [[package]] name = "libc" -version = "0.2.148" +version = "0.2.149" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9cdc71e17332e86d2e1d38c1f99edcb6288ee11b815fb1a4b049eaa2114d369b" +checksum = "a08173bc88b7955d1b3145aa561539096c421ac8debde8cbc3612ec635fee29b" [[package]] name = "libcosmic" version = "0.1.0" -source = "git+https://github.com/pop-os/libcosmic/?rev=f91287d#f91287dec2297df41a339c1106850c4cf179f67f" +source = "git+https://github.com/pop-os/libcosmic/#7cc791a3f5c0e54e091cce800a638aa5567055ef" dependencies = [ "apply", "cosmic-config", @@ -2485,15 +2576,6 @@ version = "2.6.4" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "f665ee40bc4a3c5590afb1e9677db74a508659dfd71e126420da8274909a0167" -[[package]] -name = "memmap2" -version = "0.5.10" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "83faa42c0a078c393f6b29d5db232d8be22776a891f8f56e5284faee4a20b327" -dependencies = [ - "libc", -] - [[package]] name = "memmap2" version = "0.6.2" @@ -2505,18 +2587,18 @@ dependencies = [ [[package]] name = "memmap2" -version = "0.7.1" +version = "0.8.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f49388d20533534cd19360ad3d6a7dadc885944aa802ba3995040c5ec11288c6" +checksum = "43a5a03cefb0d953ec0be133036f14e109412fa594edc2f77227249db66cc3ed" dependencies = [ "libc", ] [[package]] name = "memmap2" -version = "0.8.0" +version = "0.9.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "43a5a03cefb0d953ec0be133036f14e109412fa594edc2f77227249db66cc3ed" +checksum = "deaba38d7abf1d4cca21cc89e932e542ba2b9258664d2a9ef0e61512039c9375" dependencies = [ "libc", ] @@ -2557,7 +2639,7 @@ dependencies = [ "bitflags 1.3.2", "block", "core-graphics-types", - "foreign-types", + "foreign-types 0.3.2", "log", "objc", ] @@ -2655,15 +2737,16 @@ checksum = "308d96db8debc727c3fd9744aac51751243420e46edf401010908da7f8d5e57c" [[package]] name = "ndk" -version = "0.7.0" +version = "0.8.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "451422b7e4718271c8b5b3aadf5adedba43dc76312454b387e98fae0fc951aa0" +checksum = "2076a31b7010b17a38c01907c45b945e8f11495ee4dd588309718901b1f7a5b7" dependencies = [ - "bitflags 1.3.2", + "bitflags 2.4.0", "jni-sys", + "log", "ndk-sys", - "num_enum 0.5.11", - "raw-window-handle", + "num_enum", + "raw-window-handle 0.6.0", "thiserror", ] @@ -2675,25 +2758,13 @@ checksum = "27b02d87554356db9e9a873add8782d4ea6e3e58ea071a9adb9a2e8ddb884a8b" [[package]] name = "ndk-sys" -version = "0.4.1+23.1.7779620" +version = "0.5.0+25.2.9519653" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "3cf2aae958bd232cac5069850591667ad422d263686d75b52a065f9badeee5a3" +checksum = "8c196769dd60fd4f363e11d948139556a344e79d451aeb2fa2fd040738ef7691" dependencies = [ "jni-sys", ] -[[package]] -name = "nix" -version = "0.24.3" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "fa52e972a9a719cecb6864fb88568781eb706bac2cd1d4f04a648542dbf78069" -dependencies = [ - "bitflags 1.3.2", - "cfg-if", - "libc", - "memoffset 0.6.5", -] - [[package]] name = "nix" version = "0.25.1" @@ -2729,7 +2800,6 @@ dependencies = [ "bitflags 2.4.0", "cfg-if", "libc", - "memoffset 0.9.0", ] [[package]] @@ -2866,39 +2936,18 @@ dependencies = [ [[package]] name = "num_enum" -version = "0.5.11" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1f646caf906c20226733ed5b1374287eb97e3c2a5c227ce668c1f2ce20ae57c9" -dependencies = [ - "num_enum_derive 0.5.11", -] - -[[package]] -name = "num_enum" -version = "0.6.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7a015b430d3c108a207fd776d2e2196aaf8b1cf8cf93253e3a097ff3085076a1" -dependencies = [ - "num_enum_derive 0.6.1", -] - -[[package]] -name = "num_enum_derive" -version = "0.5.11" +version = "0.7.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "dcbff9bc912032c62bf65ef1d5aea88983b420f4f839db1e9b0c281a25c9c799" +checksum = "70bf6736f74634d299d00086f02986875b3c2d924781a6a2cb6c201e73da0ceb" dependencies = [ - "proc-macro-crate", - "proc-macro2", - "quote", - "syn 1.0.109", + "num_enum_derive", ] [[package]] name = "num_enum_derive" -version = "0.6.1" +version = "0.7.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "96667db765a921f7b295ffee8b60472b686a51d4f21c2ee4ffdb94c7013b65a6" +checksum = "56ea360eafe1022f7cc56cd7b869ed57330fb2453d0c7831d99b74c65d2f5597" dependencies = [ "proc-macro-crate", "proc-macro2", @@ -2929,29 +2978,25 @@ dependencies = [ [[package]] name = "objc-sys" -version = "0.2.0-beta.2" +version = "0.3.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "df3b9834c1e95694a05a828b59f55fa2afec6288359cda67146126b3f90a55d7" +checksum = "99e1d07c6eab1ce8b6382b8e3c7246fe117ff3f8b34be065f5ebace6749fe845" [[package]] name = "objc2" -version = "0.3.0-beta.3.patch-leaks.3" +version = "0.4.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7e01640f9f2cb1220bbe80325e179e532cb3379ebcd1bf2279d703c19fe3a468" +checksum = "559c5a40fdd30eb5e344fbceacf7595a81e242529fb4e21cf5f43fb4f11ff98d" dependencies = [ - "block2", "objc-sys", "objc2-encode", ] [[package]] name = "objc2-encode" -version = "2.0.0-pre.2" +version = "3.0.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "abfcac41015b00a120608fdaa6938c44cb983fee294351cc4bac7638b4e50512" -dependencies = [ - "objc-sys", -] +checksum = "d079845b37af429bfe5dfa76e6d087d788031045b25cfc6fd898486fd9847666" [[package]] name = "objc_exception" @@ -3245,7 +3290,7 @@ dependencies = [ "cfg-if", "concurrent-queue", "pin-project-lite", - "rustix 0.38.17", + "rustix 0.38.20", "tracing", "windows-sys 0.48.0", ] @@ -3434,6 +3479,12 @@ version = "0.5.2" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "f2ff9a1f06a88b01621b7ae906ef0211290d1c8a168a15542486a8f61c0833b9" +[[package]] +name = "raw-window-handle" +version = "0.6.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "42a9830a0e1b9fb145ebb365b8bc4ccd75f290f98c0247deafbbe2c75cefb544" + [[package]] name = "rayon" version = "1.8.0" @@ -3696,9 +3747,9 @@ dependencies = [ [[package]] name = "rustix" -version = "0.38.17" +version = "0.38.20" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f25469e9ae0f3d0047ca8b93fc56843f38e6774f0914a107ff8b41be8be8e0b7" +checksum = "67ce50cb2e16c2903e30d1cbccfd8387a74b9d4c938b6a4c5ec6cc7556f7a8a0" dependencies = [ "bitflags 2.4.0", "errno", @@ -3896,12 +3947,12 @@ checksum = "942b4a808e05215192e39f4ab80813e599068285906cc91aa64f923db842bd5a" [[package]] name = "smithay" version = "0.3.0" -source = "git+https://github.com/smithay//smithay?rev=d60b1b83e#d60b1b83e71ae73aecad799025e3ea30e844c229" +source = "git+https://github.com/pop-os/smithay?branch=x11_fixes#15dd1675840295062cee4494fa6d49440ec0f04c" dependencies = [ "appendlist", "ash", "bitflags 2.4.0", - "calloop 0.12.2", + "calloop", "cc", "cgmath", "cursor-icon", @@ -3910,6 +3961,7 @@ dependencies = [ "drm-ffi", "drm-fourcc", "encoding_rs", + "errno", "gbm", "gl_generator", "glow", @@ -3924,6 +3976,7 @@ dependencies = [ "pkg-config", "profiling", "rand", + "rustix 0.38.20", "scan_fmt", "scopeguard", "smallvec", @@ -3933,33 +3986,39 @@ dependencies = [ "udev 0.8.0", "wayland-backend 0.3.2", "wayland-egl", - "wayland-protocols 0.31.0", + "wayland-protocols", "wayland-protocols-misc", "wayland-protocols-wlr", "wayland-server", "wayland-sys 0.31.1", "winit", "x11rb 0.12.0", - "xkbcommon 0.6.0", + "xkbcommon", ] [[package]] name = "smithay-client-toolkit" -version = "0.16.1" +version = "0.18.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "870427e30b8f2cbe64bf43ec4b86e88fe39b0a84b3f15efd9c9c2d020bc86eb9" +checksum = "60e3d9941fa3bacf7c2bf4b065304faa14164151254cd16ce1b1bc8fc381600f" dependencies = [ - "bitflags 1.3.2", - "calloop 0.10.6", - "dlib", - "lazy_static", + "bitflags 2.4.0", + "calloop", + "calloop-wayland-source", + "cursor-icon", + "libc", "log", - "memmap2 0.5.10", - "nix 0.24.3", - "pkg-config", - "wayland-client 0.29.5", + "memmap2 0.9.0", + "rustix 0.38.20", + "thiserror", + "wayland-backend 0.3.2", + "wayland-client 0.31.1", + "wayland-csd-frame", "wayland-cursor", - "wayland-protocols 0.29.5", + "wayland-protocols", + "wayland-protocols-wlr", + "wayland-scanner 0.31.0", + "xkeysym", ] [[package]] @@ -3975,7 +4034,16 @@ dependencies = [ "log", "memoffset 0.9.0", "smithay", - "xkbcommon 0.7.0", + "xkbcommon", +] + +[[package]] +name = "smol_str" +version = "0.2.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "74212e6bbe9a4352329b2f68ba3130c15a3f26fe88ff22dbdc6cdd58fa85e99c" +dependencies = [ + "serde", ] [[package]] @@ -3992,13 +4060,13 @@ dependencies = [ "bytemuck", "cfg_aliases", "cocoa", - "core-graphics", + "core-graphics 0.22.3", "fastrand 1.9.0", - "foreign-types", + "foreign-types 0.3.2", "log", "nix 0.26.4", "objc", - "raw-window-handle", + "raw-window-handle 0.5.2", "redox_syscall 0.3.5", "thiserror", "wasm-bindgen", @@ -4126,7 +4194,7 @@ dependencies = [ [[package]] name = "taffy" version = "0.3.11" -source = "git+https://github.com/DioxusLabs/taffy#120bb7a2e501822b324fd48de955450ebbba1c1a" +source = "git+https://github.com/DioxusLabs/taffy#d4374b93f0ec2c0ace42d3feafec22d3af1940d8" dependencies = [ "arrayvec", "grid", @@ -4143,7 +4211,7 @@ dependencies = [ "cfg-if", "fastrand 2.0.1", "redox_syscall 0.3.5", - "rustix 0.38.17", + "rustix 0.38.20", "windows-sys 0.48.0", ] @@ -4704,12 +4772,6 @@ version = "1.10.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "2bda7c41ca331fe9a1c278a9e7ee055f4be7f5eb1c2b72f079b4ff8b5fce9d5c" -[[package]] -name = "vec_map" -version = "0.8.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f1bddf1187be692e79c5ffeab891132dfb0f236ed36a43c7ed39f1165ee20191" - [[package]] name = "version_check" version = "0.9.4" @@ -4844,52 +4906,47 @@ dependencies = [ [[package]] name = "wayland-client" -version = "0.29.5" +version = "0.30.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "3f3b068c05a039c9f755f881dc50f01732214f5685e379829759088967c46715" +checksum = "489c9654770f674fc7e266b3c579f4053d7551df0ceb392f153adb1f9ed06ac8" dependencies = [ "bitflags 1.3.2", - "downcast-rs", - "libc", - "nix 0.24.3", - "scoped-tls", - "wayland-commons", - "wayland-scanner 0.29.5", - "wayland-sys 0.29.5", + "nix 0.26.4", + "wayland-backend 0.1.2", + "wayland-scanner 0.30.1", ] [[package]] name = "wayland-client" -version = "0.30.2" +version = "0.31.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "489c9654770f674fc7e266b3c579f4053d7551df0ceb392f153adb1f9ed06ac8" +checksum = "1ca7d52347346f5473bf2f56705f360e8440873052e575e55890c4fa57843ed3" dependencies = [ - "bitflags 1.3.2", + "bitflags 2.4.0", "nix 0.26.4", - "wayland-backend 0.1.2", - "wayland-scanner 0.30.1", + "wayland-backend 0.3.2", + "wayland-scanner 0.31.0", ] [[package]] -name = "wayland-commons" -version = "0.29.5" +name = "wayland-csd-frame" +version = "0.3.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "8691f134d584a33a6606d9d717b95c4fa20065605f798a3f350d78dced02a902" +checksum = "625c5029dbd43d25e6aa9615e88b829a5cad13b2819c4ae129fdbb7c31ab4c7e" dependencies = [ - "nix 0.24.3", - "once_cell", - "smallvec", - "wayland-sys 0.29.5", + "bitflags 2.4.0", + "cursor-icon", + "wayland-backend 0.3.2", ] [[package]] name = "wayland-cursor" -version = "0.29.5" +version = "0.31.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "6865c6b66f13d6257bef1cd40cbfe8ef2f150fb8ebbdb1e8e873455931377661" +checksum = "a44aa20ae986659d6c77d64d808a046996a932aa763913864dc40c359ef7ad5b" dependencies = [ - "nix 0.24.3", - "wayland-client 0.29.5", + "nix 0.26.4", + "wayland-client 0.31.1", "xcursor", ] @@ -4903,18 +4960,6 @@ dependencies = [ "wayland-sys 0.31.1", ] -[[package]] -name = "wayland-protocols" -version = "0.29.5" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b950621f9354b322ee817a23474e479b34be96c2e909c14f7bc0100e9a970bc6" -dependencies = [ - "bitflags 1.3.2", - "wayland-client 0.29.5", - "wayland-commons", - "wayland-scanner 0.29.5", -] - [[package]] name = "wayland-protocols" version = "0.31.0" @@ -4923,6 +4968,7 @@ checksum = "e253d7107ba913923dc253967f35e8561a3c65f914543e46843c88ddd729e21c" dependencies = [ "bitflags 2.4.0", "wayland-backend 0.3.2", + "wayland-client 0.31.1", "wayland-scanner 0.31.0", "wayland-server", ] @@ -4935,33 +4981,36 @@ checksum = "bfa5933740b200188c9b4c38601b8212e8c154d7de0d2cb171944e137a77de1e" dependencies = [ "bitflags 2.4.0", "wayland-backend 0.3.2", - "wayland-protocols 0.31.0", + "wayland-protocols", "wayland-scanner 0.31.0", "wayland-server", ] [[package]] -name = "wayland-protocols-wlr" +name = "wayland-protocols-plasma" version = "0.2.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ad1f61b76b6c2d8742e10f9ba5c3737f6530b4c243132c2a2ccc8aa96fe25cd6" +checksum = "23803551115ff9ea9bce586860c5c5a971e360825a0309264102a9495a5ff479" dependencies = [ "bitflags 2.4.0", "wayland-backend 0.3.2", - "wayland-protocols 0.31.0", + "wayland-client 0.31.1", + "wayland-protocols", "wayland-scanner 0.31.0", - "wayland-server", ] [[package]] -name = "wayland-scanner" -version = "0.29.5" +name = "wayland-protocols-wlr" +version = "0.2.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "8f4303d8fa22ab852f789e75a967f0a2cdc430a607751c0499bada3e451cbd53" +checksum = "ad1f61b76b6c2d8742e10f9ba5c3737f6530b4c243132c2a2ccc8aa96fe25cd6" dependencies = [ - "proc-macro2", - "quote", - "xml-rs", + "bitflags 2.4.0", + "wayland-backend 0.3.2", + "wayland-client 0.31.1", + "wayland-protocols", + "wayland-scanner 0.31.0", + "wayland-server", ] [[package]] @@ -5000,17 +5049,6 @@ dependencies = [ "wayland-scanner 0.31.0", ] -[[package]] -name = "wayland-sys" -version = "0.29.5" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "be12ce1a3c39ec7dba25594b97b42cb3195d54953ddb9d3d95a7c3902bc6e9d4" -dependencies = [ - "dlib", - "lazy_static", - "pkg-config", -] - [[package]] name = "wayland-sys" version = "0.30.1" @@ -5033,6 +5071,7 @@ dependencies = [ "libc", "log", "memoffset 0.9.0", + "once_cell", "pkg-config", ] @@ -5076,7 +5115,7 @@ dependencies = [ "naga", "parking_lot 0.12.1", "profiling", - "raw-window-handle", + "raw-window-handle 0.5.2", "smallvec", "static_assertions", "wasm-bindgen", @@ -5101,7 +5140,7 @@ dependencies = [ "naga", "parking_lot 0.12.1", "profiling", - "raw-window-handle", + "raw-window-handle 0.5.2", "rustc-hash", "smallvec", "thiserror", @@ -5124,7 +5163,7 @@ dependencies = [ "block", "core-graphics-types", "d3d12", - "foreign-types", + "foreign-types 0.3.2", "glow", "gpu-alloc", "gpu-allocator", @@ -5141,7 +5180,7 @@ dependencies = [ "parking_lot 0.12.1", "profiling", "range-alloc", - "raw-window-handle", + "raw-window-handle 0.5.2", "renderdoc-sys", "rustc-hash", "smallvec", @@ -5367,36 +5406,49 @@ checksum = "ed94fce61571a4006852b7389a063ab983c02eb1bb37b47f8272ce92d06d9538" [[package]] name = "winit" -version = "0.28.7" +version = "0.29.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9596d90b45384f5281384ab204224876e8e8bf7d58366d9b795ad99aa9894b94" +checksum = "b829f75d02fe1e225b97c91a04c326900147a50234d1141a1cbe215ce8798c11" dependencies = [ + "ahash 0.8.3", "android-activity", - "bitflags 1.3.2", + "atomic-waker", + "bitflags 2.4.0", + "bytemuck", + "calloop", "cfg_aliases", "core-foundation", - "core-graphics", - "dispatch", - "instant", + "core-graphics 0.23.1", + "cursor-icon", + "icrate", + "js-sys", "libc", "log", - "mio", + "memmap2 0.9.0", "ndk", + "ndk-sys", "objc2", "once_cell", "orbclient", "percent-encoding", - "raw-window-handle", + "raw-window-handle 0.6.0", "redox_syscall 0.3.5", + "rustix 0.38.20", "smithay-client-toolkit", + "smol_str", + "unicode-segmentation", "wasm-bindgen", - "wayland-client 0.29.5", - "wayland-commons", - "wayland-protocols 0.29.5", - "wayland-scanner 0.29.5", + "wasm-bindgen-futures", + "wayland-backend 0.3.2", + "wayland-client 0.31.1", + "wayland-protocols", + "wayland-protocols-plasma", "web-sys", - "windows-sys 0.45.0", + "web-time", + "windows-sys 0.48.0", "x11-dl", + "x11rb 0.12.0", + "xkbcommon-dl", ] [[package]] @@ -5450,8 +5502,12 @@ version = "0.12.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "b1641b26d4dec61337c35a1b1aaf9e3cba8f46f0b43636c609ab0291a648040a" dependencies = [ + "as-raw-xcb-connection", "gethostname 0.3.0", + "libc", + "libloading 0.7.4", "nix 0.26.4", + "once_cell", "winapi", "winapi-wsapoll", "x11rb-protocol 0.12.0", @@ -5492,23 +5548,25 @@ checksum = "213b7324336b53d2414b2db8537e56544d981803139155afa84f76eeebb7a546" [[package]] name = "xkbcommon" -version = "0.6.0" +version = "0.7.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c286371c44b3572d19b09196c129a8fff47d7704d6494daefb44fec10f0278ab" +checksum = "13867d259930edc7091a6c41b4ce6eee464328c6ff9659b7e4c668ca20d4c91e" dependencies = [ "libc", - "memmap2 0.7.1", + "memmap2 0.8.0", "xkeysym", ] [[package]] -name = "xkbcommon" -version = "0.7.0" +name = "xkbcommon-dl" +version = "0.4.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "13867d259930edc7091a6c41b4ce6eee464328c6ff9659b7e4c668ca20d4c91e" +checksum = "6924668544c48c0133152e7eec86d644a056ca3d09275eb8d5cdb9855f9d8699" dependencies = [ - "libc", - "memmap2 0.8.0", + "bitflags 2.4.0", + "dlib", + "log", + "once_cell", "xkeysym", ] diff --git a/Cargo.toml b/Cargo.toml index bcaf1e9f..548b8db2 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -36,10 +36,10 @@ libsystemd = { version = "0.6", optional = true } wayland-backend = "0.3.2" wayland-scanner = "0.31.0" cosmic-comp-config = { path = "cosmic-comp-config" } -cosmic-config = { git = "https://github.com/pop-os/libcosmic/", rev = "f91287d", features = ["calloop"] } +cosmic-config = { git = "https://github.com/pop-os/libcosmic/", features = ["calloop", "macro"] } cosmic-protocols = { git = "https://github.com/pop-os/cosmic-protocols", branch = "main", default-features = false, features = ["server"] } -libcosmic = { git = "https://github.com/pop-os/libcosmic/", rev = "f91287d", default-features = false } -iced_tiny_skia = { git = "https://github.com/pop-os/libcosmic/", rev = "f91287d" } +libcosmic = { git = "https://github.com/pop-os/libcosmic/", default-features = false } +iced_tiny_skia = { git = "https://github.com/pop-os/libcosmic/" } tiny-skia = "0.10" ordered-float = "4.0" glow = "0.12.0" @@ -53,6 +53,7 @@ once_cell = "1.18.0" i18n-embed = { version = "0.14", features = ["fluent-system", "desktop-requester"] } i18n-embed-fl = "0.7" rust-embed = "8.0" +libc = "0.2.149" [dependencies.id_tree] git = "https://github.com/Drakulix/id-tree.git" @@ -87,7 +88,4 @@ debug = true lto = "fat" [patch."https://github.com/Smithay/smithay.git"] -smithay = { git = "https://github.com/smithay//smithay", rev = "d60b1b83e" } - -[patch.crates-io] -calloop = { git = "https://github.com/Smithay/calloop", rev = "71b6e633b1" } +smithay = { git = "https://github.com/pop-os/smithay", branch = "x11_fixes" } \ No newline at end of file diff --git a/src/backend/kms/mod.rs b/src/backend/kms/mod.rs index 60eea7ce..3ecf2927 100644 --- a/src/backend/kms/mod.rs +++ b/src/backend/kms/mod.rs @@ -16,6 +16,7 @@ use crate::{ use anyhow::{Context, Result}; use cosmic_protocols::screencopy::v1::server::zcosmic_screencopy_session_v1::FailureReason; +use libc::dev_t; use smithay::{ backend::{ allocator::{ @@ -25,7 +26,7 @@ use smithay::{ Allocator, Format, Fourcc, }, drm::{ - compositor::{BlitFrameResultError, DrmCompositor, FrameError}, + compositor::{BlitFrameResultError, DrmCompositor, FrameError, PrimaryPlaneElement}, DrmDevice, DrmDeviceFd, DrmEvent, DrmEventTime, DrmNode, NodeType, }, egl::{EGLContext, EGLDevice, EGLDisplay}, @@ -58,7 +59,7 @@ use smithay::{ Device as _, }, input::{self, Libinput}, - nix::{fcntl::OFlag, sys::stat::dev_t}, + rustix::fs::OFlags, wayland_protocols::wp::{ linux_dmabuf::zv1::server::zwp_linux_dmabuf_feedback_v1, presentation_time::server::wp_presentation_feedback, @@ -82,7 +83,6 @@ use std::{ collections::{HashMap, HashSet}, ffi::CStr, fmt, - os::unix::io::FromRawFd, path::PathBuf, time::Duration, }; @@ -418,23 +418,21 @@ impl State { return Ok(()); } - let fd = DrmDeviceFd::new(unsafe { - DeviceFd::from_raw_fd( - self.backend - .kms() - .session - .open( - &path, - OFlag::O_RDWR | OFlag::O_CLOEXEC | OFlag::O_NOCTTY | OFlag::O_NONBLOCK, + let fd = DrmDeviceFd::new(DeviceFd::from( + self.backend + .kms() + .session + .open( + &path, + OFlags::RDWR | OFlags::CLOEXEC | OFlags::NOCTTY | OFlags::NONBLOCK, + ) + .with_context(|| { + format!( + "Failed to optain file descriptor for drm device: {}", + path.display() ) - .with_context(|| { - format!( - "Failed to optain file descriptor for drm device: {}", - path.display() - ) - })?, - ) - }); + })?, + )); let (drm, notifier) = DrmDevice::new(fd.clone(), false) .with_context(|| format!("Failed to initialize drm device for: {}", path.display()))?; let drm_node = DrmNode::from_dev_id(dev)?; @@ -1014,8 +1012,14 @@ impl Device { interface, PhysicalProperties { size: (phys_w as i32, phys_h as i32).into(), - // TODO: We need to read that from the connector properties - subpixel: Subpixel::Unknown, + subpixel: match conn_info.subpixel() { + connector::SubPixel::HorizontalRgb => Subpixel::HorizontalRgb, + connector::SubPixel::HorizontalBgr => Subpixel::HorizontalBgr, + connector::SubPixel::VerticalRgb => Subpixel::VerticalRgb, + connector::SubPixel::VerticalBgr => Subpixel::VerticalBgr, + connector::SubPixel::None => Subpixel::None, + _ => Subpixel::Unknown, + }, make: edid_info .as_ref() .map(|info| info.manufacturer.clone()) @@ -1093,8 +1097,8 @@ fn render_node_for_output( ) -> DrmNode { let workspace = shell.active_space(output); let nodes = workspace - .get_fullscreen(output) - .map(|w| vec![w.surface()]) + .get_fullscreen() + .map(|w| vec![w.clone()]) .unwrap_or_else(|| workspace.windows().collect::>()) .into_iter() .flat_map(|w| w.wl_surface().and_then(|s| source_node_for_surface(&s, dh))) @@ -1235,8 +1239,11 @@ impl Surface { })?; self.fps.elements(); - let res = - compositor.render_frame::<_, _, GlesTexture>(&mut renderer, &elements, CLEAR_COLOR); + let res = compositor.render_frame::<_, _, GlesTexture>( + &mut renderer, + &elements, + CLEAR_COLOR, // TODO use a theme neutral color + ); self.fps.render(); match res { @@ -1247,6 +1254,11 @@ impl Surface { None }; + if frame_result.needs_sync() { + if let PrimaryPlaneElement::Swapchain(elem) = &frame_result.primary_element { + elem.sync.wait(); + } + } match compositor.queue_frame(feedback) { Ok(()) | Err(FrameError::EmptyFrame) => {} Err(err) => { @@ -1507,7 +1519,6 @@ impl KmsState { false }; - shell.refresh_outputs(); if recreated { let sessions = output.pending_buffers().collect::>(); if let Err(err) = self.schedule_render( diff --git a/src/backend/render/mod.rs b/src/backend/render/mod.rs index 75cead8a..76c4eb45 100644 --- a/src/backend/render/mod.rs +++ b/src/backend/render/mod.rs @@ -17,7 +17,7 @@ use crate::{ CosmicMapped, CosmicMappedRenderElement, OverviewMode, Trigger, WorkspaceRenderElement, }, state::{Common, Fps}, - utils::prelude::{OutputExt, SeatExt}, + utils::prelude::*, wayland::{ handlers::{ data_device::get_dnd_icon, @@ -79,10 +79,6 @@ pub type GlMultiFrame<'a, 'b, 'frame> = pub type GlMultiError = MultiError, GbmGlesBackend>; pub static CLEAR_COLOR: [f32; 4] = [0.153, 0.161, 0.165, 1.0]; -pub static GROUP_COLOR: [f32; 3] = [0.788, 0.788, 0.788]; -pub static ACTIVE_GROUP_COLOR: [f32; 3] = [0.58, 0.922, 0.922]; -pub static FOCUS_INDICATOR_COLOR: [f32; 3] = [0.580, 0.921, 0.921]; - pub static OUTLINE_SHADER: &str = include_str!("./shaders/rounded_outline.frag"); pub static RECTANGLE_SHADER: &str = include_str!("./shaders/rounded_rectangle.frag"); @@ -160,10 +156,11 @@ impl IndicatorShader { pub fn focus_element( renderer: &R, key: impl Into, - mut element_geo: Rectangle, + mut element_geo: Rectangle, thickness: u8, scale: f64, alpha: f32, + active_window_hint: [f32; 3], ) -> PixelShaderElement { let t = thickness as i32; element_geo.loc -= (t, t).into(); @@ -177,14 +174,14 @@ impl IndicatorShader { thickness * 2, alpha, scale, - FOCUS_INDICATOR_COLOR, + active_window_hint, ) } pub fn element( renderer: &R, key: impl Into, - geo: Rectangle, + geo: Rectangle, thickness: u8, radius: u8, alpha: f32, @@ -223,7 +220,7 @@ impl IndicatorShader { let elem = PixelShaderElement::new( shader, - geo, + geo.as_logical(), None, //TODO alpha, vec![ @@ -240,8 +237,8 @@ impl IndicatorShader { } let elem = &mut cache.get_mut(&key).unwrap().1; - if elem.geometry(1.0.into()).to_logical(1) != geo { - elem.resize(geo, None); + if elem.geometry(1.0.into()).to_logical(1) != geo.as_logical() { + elem.resize(geo.as_logical(), None); } elem.clone() } @@ -271,7 +268,7 @@ impl BackdropShader { pub fn element( renderer: &R, key: impl Into, - geo: Rectangle, + geo: Rectangle, radius: f32, alpha: f32, color: [f32; 3], @@ -304,7 +301,7 @@ impl BackdropShader { let elem = PixelShaderElement::new( shader, - geo, + geo.as_logical(), None, // TODO alpha, vec![ @@ -320,8 +317,8 @@ impl BackdropShader { } let elem = &mut cache.get_mut(&key).unwrap().1; - if elem.geometry(1.0.into()).to_logical(1) != geo { - elem.resize(geo, None); + if elem.geometry(1.0.into()).to_logical(1) != geo.as_logical() { + elem.resize(geo.as_logical(), None); } elem.clone() } @@ -413,13 +410,14 @@ where ); } + let theme = state.theme.cosmic(); if let Some(grab_elements) = seat .user_data() .get::() .unwrap() .borrow() .as_ref() - .map(|state| state.render::(renderer, seat, output)) + .map(|state| state.render::(renderer, seat, output, theme)) { elements.extend(grab_elements); } @@ -449,6 +447,7 @@ where #[cfg(feature = "debug")] puffin::profile_function!(); + let theme = state.theme.cosmic(); let mut elements = cursor_elements(renderer, state, output, cursor_mode); #[cfg(feature = "debug")] @@ -492,15 +491,11 @@ where let (resize_mode, resize_indicator) = state.shell.resize_mode(); let resize_indicator = resize_indicator.map(|indicator| (resize_mode, indicator)); let swap_tree = if let OverviewMode::Started(Trigger::KeyboardSwap(_, desc), _) = &overview.0 { - if let Some(desc_output) = desc.output.upgrade() { - if output != &desc_output || current.0 != desc.handle { - state - .shell - .space_for_handle(&desc.handle) - .and_then(|w| w.tiling_layer.tree_for_output(&desc_output)) - } else { - None - } + if current.0 != desc.handle { + state + .shell + .space_for_handle(&desc.handle) + .map(|w| w.tiling_layer.tree()) } else { None } @@ -530,9 +525,9 @@ where let has_fullscreen = workspace .fullscreen - .get(output) + .as_ref() .filter(|f| !f.is_animating()) - .map(|f| f.exclusive); + .is_some(); let (overlay_elements, overlay_popups) = split_layer_elements(renderer, output, Layer::Overlay, exclude_workspace_overview); @@ -540,7 +535,7 @@ where elements.extend(overlay_popups.into_iter().map(Into::into)); elements.extend(overlay_elements.into_iter().map(Into::into)); - let mut window_elements = if !has_fullscreen.unwrap_or(false) { + let mut window_elements = if !has_fullscreen { let (top_elements, top_popups) = split_layer_elements(renderer, output, Layer::Top, exclude_workspace_overview); elements.extend(top_popups.into_iter().map(Into::into)); @@ -548,6 +543,7 @@ where } else { Vec::new() }; + let active_hint = theme.active_hint as u8; let offset = match previous.as_ref() { Some((previous, previous_idx, start)) => { @@ -557,7 +553,7 @@ where .shell .space_for_handle(&previous) .ok_or(OutputNoMode)?; - let has_fullscreen = workspace.fullscreen.contains_key(output); + let has_fullscreen = workspace.fullscreen.is_some(); let is_active_space = workspace.outputs().any(|o| o == &active_output); let percentage = { @@ -581,15 +577,15 @@ where }); let (w_elements, p_elements) = workspace - .render_output::( + .render::( renderer, - output, &state.shell.override_redirect_windows, state.xwayland_state.as_mut(), (!move_active && is_active_space).then_some(&last_active_seat), overview.clone(), resize_indicator.clone(), - state.config.static_conf.active_hint, + active_hint, + theme, ) .map_err(|_| OutputNoMode)?; elements.extend(p_elements.into_iter().map(|p_element| { @@ -639,15 +635,15 @@ where let is_active_space = workspace.outputs().any(|o| o == &active_output); let (w_elements, p_elements) = workspace - .render_output::( + .render::( renderer, - output, &state.shell.override_redirect_windows, state.xwayland_state.as_mut(), (!move_active && is_active_space).then_some(&last_active_seat), overview, resize_indicator, - state.config.static_conf.active_hint, + active_hint, + theme, ) .map_err(|_| OutputNoMode)?; elements.extend(p_elements.into_iter().map(|p_element| { @@ -665,7 +661,7 @@ where )) })); - if has_fullscreen.is_none() { + if !has_fullscreen { let (w_elements, p_elements) = background_layer_elements(renderer, output, exclude_workspace_overview); @@ -919,7 +915,12 @@ where } renderer.bind(target).map_err(RenderError::Rendering)?; - let res = damage_tracker.render_output(renderer, age, &elements, CLEAR_COLOR); + let res = damage_tracker.render_output( + renderer, + age, + &elements, + CLEAR_COLOR, // TODO use a theme neutral color + ); if let Some(fps) = fps.as_mut() { fps.render(); diff --git a/src/backend/winit.rs b/src/backend/winit.rs index da2cb26d..29432f4c 100644 --- a/src/backend/winit.rs +++ b/src/backend/winit.rs @@ -26,6 +26,7 @@ use smithay::{ calloop::{ping, EventLoop}, wayland_protocols::wp::presentation_time::server::wp_presentation_feedback, wayland_server::DisplayHandle, + winit::platform::pump_events::PumpStatus, }, utils::Transform, wayland::dmabuf::DmabufFeedbackBuilder, @@ -125,12 +126,9 @@ impl WinitState { .get::>() .unwrap() .borrow_mut(); - if dbg!(config.mode.0) != dbg!((size.physical_size.w as i32, size.physical_size.h as i32)) { + if dbg!(config.mode.0) != dbg!((size.w as i32, size.h as i32)) { if !test_only { - config.mode = ( - (size.physical_size.w as i32, size.physical_size.h as i32), - None, - ); + config.mode = ((size.w as i32, size.h as i32), None); } Err(anyhow::anyhow!("Cannot set window size")) } else { @@ -165,7 +163,7 @@ pub fn init_backend( model: name.clone(), }; let mode = Mode { - size: (size.physical_size.w as i32, size.physical_size.h as i32).into(), + size: (size.w as i32, size.h as i32).into(), refresh: 60_000, }; let output = Output::new(name, props); @@ -179,10 +177,7 @@ pub fn init_backend( ); output.user_data().insert_if_missing(|| { RefCell::new(OutputConfig { - mode: ( - (size.physical_size.w as i32, size.physical_size.h as i32), - None, - ), + mode: ((size.w as i32, size.h as i32), None), transform: Transform::Flipped180.into(), ..Default::default() }) @@ -212,11 +207,11 @@ pub fn init_backend( match input .dispatch_new_events(|event| state.process_winit_event(event, &render_ping_handle)) { - Ok(_) => { + PumpStatus::Continue => { event_ping_handle.ping(); render_ping_handle.ping(); } - Err(winit::WinitError::WindowClosed) => { + PumpStatus::Exit(_) => { let output = state.backend.winit().output.clone(); let seats = state.common.seats().cloned().collect::>(); state.common.shell.remove_output(&output, seats.into_iter()); @@ -349,10 +344,9 @@ impl State { output.change_current_state(Some(mode), None, None, None); layer_map_for_output(output).arrange(); self.common.output_configuration_state.update(); - self.common.shell.refresh_outputs(); render_ping.ping(); } - WinitEvent::Refresh => render_ping.ping(), + WinitEvent::Redraw => render_ping.ping(), WinitEvent::Input(event) => self.process_input_event(event, false), _ => {} }; diff --git a/src/backend/x11.rs b/src/backend/x11.rs index 7952eaa7..49dabcd4 100644 --- a/src/backend/x11.rs +++ b/src/backend/x11.rs @@ -448,7 +448,6 @@ pub fn init_backend( output.set_preferred(mode); layer_map_for_output(output).arrange(); state.common.output_configuration_state.update(); - state.common.shell.refresh_outputs(); surface.dirty = true; if !surface.pending { surface.render.ping(); diff --git a/src/config/mod.rs b/src/config/mod.rs index c783a080..8640b6a8 100644 --- a/src/config/mod.rs +++ b/src/config/mod.rs @@ -50,10 +50,6 @@ pub struct StaticConfig { #[serde(default = "default_workspace_layout")] pub workspace_layout: WorkspaceLayout, pub tiling_enabled: bool, - #[serde(default = "default_active_hint")] - pub active_hint: u8, - #[serde(default = "default_gaps")] - pub gaps: (u8, u8), } #[derive(Debug, Deserialize, Clone, Copy, PartialEq, Eq)] @@ -100,14 +96,6 @@ fn default_enabled() -> bool { true } -fn default_active_hint() -> u8 { - 4 -} - -fn default_gaps() -> (u8, u8) { - (0, 4) -} - fn default_workspace_layout() -> WorkspaceLayout { WorkspaceLayout::Vertical } @@ -219,8 +207,6 @@ impl Config { workspace_amount: WorkspaceAmount::Dynamic, workspace_layout: WorkspaceLayout::Vertical, tiling_enabled: false, - active_hint: default_active_hint(), - gaps: default_gaps(), } } diff --git a/src/input/mod.rs b/src/input/mod.rs index 41e1a864..11b4ea1a 100644 --- a/src/input/mod.rs +++ b/src/input/mod.rs @@ -6,7 +6,10 @@ use crate::{ shell::{ focus::{target::PointerFocusTarget, FocusDirection}, grabs::{ResizeEdge, SeatMoveGrabState}, - layout::tiling::{SwapWindowGrab, TilingLayout}, + layout::{ + floating::ResizeGrabMarker, + tiling::{SwapWindowGrab, TilingLayout}, + }, Direction, FocusResult, MoveResult, OverviewMode, ResizeDirection, ResizeMode, Trigger, Workspace, }, @@ -39,7 +42,7 @@ use smithay::{ input::event::pointer::PointerAxisEvent as LibinputPointerAxisEvent, wayland_server::DisplayHandle, }, - utils::{Logical, Point, Rectangle, Serial, SERIAL_COUNTER}, + utils::{Point, Serial, SERIAL_COUNTER}, wayland::{ keyboard_shortcuts_inhibit::KeyboardShortcutsInhibitorSeat, pointer_constraints::{with_pointer_constraint, PointerConstraint}, @@ -214,7 +217,6 @@ impl State { ::PointerAxisEvent: 'static, { use smithay::backend::input::Event; - match event { InputEvent::DeviceAdded { device } => { let seat = &mut self.common.last_active_seat(); @@ -350,7 +352,7 @@ impl State { if let Some(old_workspace) = old_w.get_mut(0) { if let Some(new_workspace) = other_w.iter_mut().find(|w| w.handle == new_workspace) { if new_workspace.tiling_layer.windows().next().is_none() { - if let Some(focus) = TilingLayout::move_tree(&mut old_workspace.tiling_layer, &mut new_workspace.tiling_layer, ¤t_output, &new_workspace.handle, &seat, new_workspace.focus_stack.get(&seat).iter(), old_descriptor, &mut data.common.shell.toplevel_info_state) { + if let Some(focus) = TilingLayout::move_tree(&mut old_workspace.tiling_layer, &mut new_workspace.tiling_layer, &new_workspace.handle, &seat, new_workspace.focus_stack.get(&seat).iter(), old_descriptor, &mut data.common.shell.toplevel_info_state) { let seat = seat.clone(); data.common.event_loop_handle.insert_idle(move |state| { Common::set_focus(state, Some(&focus), &seat, None); @@ -553,24 +555,18 @@ impl State { if let Some(seat) = self.common.seat_with_device(&event.device()).cloned() { let current_output = seat.active_output(); - let mut position = seat.get_pointer().unwrap().current_location(); + let mut position = seat.get_pointer().unwrap().current_location().as_global(); - let relative_pos = self - .common - .shell - .map_global_to_space(position, ¤t_output); let overview = self.common.shell.overview_mode(); - let output_geometry = current_output.geometry(); let workspace = self.common.shell.workspaces.active_mut(¤t_output); let under = State::surface_under( position, - relative_pos, ¤t_output, - output_geometry, &self.common.shell.override_redirect_windows, overview.0.clone(), workspace, - ); + ) + .map(|(target, pos)| (target, pos.as_logical())); let ptr = seat.get_pointer().unwrap(); @@ -618,7 +614,7 @@ impl State { return; } - position += event.delta(); + position += event.delta().as_global(); let output = self .common @@ -628,17 +624,30 @@ impl State { .cloned() .unwrap_or(current_output.clone()); - let workspace = self.common.shell.workspaces.active_mut(&output); + if ptr.is_grabbed() + && seat + .user_data() + .get::() + .map(|marker| marker.get()) + .unwrap_or(false) + { + if output != current_output { + ptr.frame(self); + return; + } + } + let output_geometry = output.geometry(); + + let workspace = self.common.shell.workspaces.active_mut(&output); let new_under = State::surface_under( position, - relative_pos, &output, - output_geometry, &self.common.shell.override_redirect_windows, overview.0, workspace, - ); + ) + .map(|(target, pos)| (target, pos.as_logical())); position.x = position.x.clamp( output_geometry.loc.x as f64, @@ -660,13 +669,17 @@ impl State { } if let PointerFocusTarget::Element(element) = surface { //if !element.is_in_input_region(&(position.to_i32_round() - *surface_loc).to_f64()) { - if !element.is_in_input_region(&(position - surface_loc.to_f64())) { + if !element.is_in_input_region( + &(position.as_logical() - surface_loc.to_f64()), + ) { ptr.frame(self); return; } } if let Some(region) = confine_region { - if !region.contains(position.to_i32_round() - *surface_loc) { + if !region + .contains(position.as_logical().to_i32_round() - *surface_loc) + { ptr.frame(self); return; } @@ -679,7 +692,7 @@ impl State { self, under, &MotionEvent { - location: position, + location: position.as_logical(), serial, time: event.time_msec(), }, @@ -720,17 +733,16 @@ impl State { for session in sessions_for_output(&self.common, &output) { if let Some((geometry, offset)) = seat.cursor_geometry( - position.to_buffer( + position.as_logical().to_buffer( output.current_scale().fractional_scale(), output.current_transform(), - &output.geometry().size.to_f64(), + &output_geometry.size.to_f64().as_logical(), ), self.common.clock.now(), ) { session.cursor_info(&seat, InputType::Pointer, geometry, offset); } } - #[cfg(feature = "debug")] if self.common.seats().position(|x| x == &seat).unwrap() == 0 { let location = if let Some(output) = self.common.shell.outputs.first() { @@ -752,28 +764,27 @@ impl State { let position = geometry.loc.to_f64() + smithay::backend::input::AbsolutePositionEvent::position_transformed( &event, - geometry.size, - ); - let relative_pos = self.common.shell.map_global_to_space(position, &output); + geometry.size.as_logical(), + ) + .as_global(); let overview = self.common.shell.overview_mode(); let workspace = self.common.shell.workspaces.active_mut(&output); let serial = SERIAL_COUNTER.next_serial(); let under = State::surface_under( position, - relative_pos, &output, - geometry, &self.common.shell.override_redirect_windows, overview.0, workspace, - ); + ) + .map(|(target, pos)| (target, pos.as_logical())); for session in sessions_for_output(&self.common, &output) { if let Some((geometry, offset)) = seat.cursor_geometry( - position.to_buffer( + position.as_logical().to_buffer( output.current_scale().fractional_scale(), output.current_transform(), - &output.geometry().size.to_f64(), + &geometry.size.to_f64().as_logical(), ), self.common.clock.now(), ) { @@ -785,7 +796,7 @@ impl State { self, under, &MotionEvent { - location: position, + location: position.as_logical(), serial, time: event.time_msec(), }, @@ -835,22 +846,22 @@ impl State { && !seat.get_keyboard().map(|k| k.is_grabbed()).unwrap_or(false) { let output = seat.active_output(); - let pos = seat.get_pointer().unwrap().current_location(); - let relative_pos = self.common.shell.map_global_to_space(pos, &output); + let pos = seat.get_pointer().unwrap().current_location().as_global(); + let relative_pos = pos.to_local(&output); let overview = self.common.shell.overview_mode(); let workspace = self.common.shell.active_space_mut(&output); let mut under = None; - if let Some(window) = workspace.get_fullscreen(&output) { + if let Some(window) = workspace.get_fullscreen() { let layers = layer_map_for_output(&output); if let Some(layer) = - layers.layer_under(WlrLayer::Overlay, relative_pos) + layers.layer_under(WlrLayer::Overlay, relative_pos.as_logical()) { let layer_loc = layers.layer_geometry(layer).unwrap().loc; if layer.can_receive_keyboard_focus() && layer .surface_under( - relative_pos - layer_loc.to_f64(), + relative_pos.as_logical() - layer_loc.to_f64(), WindowSurfaceType::ALL, ) .is_some() @@ -864,14 +875,19 @@ impl State { let done = { let layers = layer_map_for_output(&output); if let Some(layer) = layers - .layer_under(WlrLayer::Overlay, relative_pos) - .or_else(|| layers.layer_under(WlrLayer::Top, relative_pos)) + .layer_under(WlrLayer::Overlay, relative_pos.as_logical()) + .or_else(|| { + layers.layer_under( + WlrLayer::Top, + relative_pos.as_logical(), + ) + }) { let layer_loc = layers.layer_geometry(layer).unwrap().loc; if layer.can_receive_keyboard_focus() && layer .surface_under( - relative_pos - layer_loc.to_f64(), + relative_pos.as_logical() - layer_loc.to_f64(), WindowSurfaceType::ALL, ) .is_some() @@ -884,35 +900,38 @@ impl State { } }; if !done { - if let Some(surface) = workspace.get_maximized(&output) { - under = Some(surface.clone().into()); + if let Some((target, _)) = + workspace.element_under(pos, overview.0) + { + under = Some(target); } else { - if let Some((target, _)) = - workspace.element_under(relative_pos, overview.0) + let layers = layer_map_for_output(&output); + if let Some(layer) = layers + .layer_under( + WlrLayer::Bottom, + relative_pos.as_logical(), + ) + .or_else(|| { + layers.layer_under( + WlrLayer::Background, + relative_pos.as_logical(), + ) + }) { - under = Some(target); - } else { - let layers = layer_map_for_output(&output); - if let Some(layer) = layers - .layer_under(WlrLayer::Bottom, pos) - .or_else(|| { - layers.layer_under(WlrLayer::Background, pos) - }) + let layer_loc = + layers.layer_geometry(layer).unwrap().loc; + if layer.can_receive_keyboard_focus() + && layer + .surface_under( + relative_pos.as_logical() + - layer_loc.to_f64(), + WindowSurfaceType::ALL, + ) + .is_some() { - let layer_loc = - layers.layer_geometry(layer).unwrap().loc; - if layer.can_receive_keyboard_focus() - && layer - .surface_under( - relative_pos - layer_loc.to_f64(), - WindowSurfaceType::ALL, - ) - .is_some() - { - under = Some(layer.clone().into()); - } - }; - } + under = Some(layer.clone().into()); + } + }; } } } @@ -1330,16 +1349,15 @@ impl State { } Action::NextOutput => { let current_output = seat.active_output(); - if let Some(next_output) = self + let next_output = self .common .shell - .outputs - .iter() + .outputs() .skip_while(|o| *o != ¤t_output) .skip(1) .next() - .cloned() - { + .cloned(); + if let Some(next_output) = next_output { let idx = self.common.shell.workspaces.active_num(&next_output).1; match self.common.shell.activate(&next_output, idx) { Ok(Some(new_pos)) => { @@ -1349,7 +1367,7 @@ impl State { self, None, &MotionEvent { - location: new_pos.to_f64(), + location: new_pos.to_f64().as_logical(), serial, time, }, @@ -1366,17 +1384,16 @@ impl State { } Action::PreviousOutput => { let current_output = seat.active_output(); - if let Some(prev_output) = self + let prev_output = self .common .shell - .outputs - .iter() + .outputs() .rev() .skip_while(|o| *o != ¤t_output) .skip(1) .next() - .cloned() - { + .cloned(); + if let Some(prev_output) = prev_output { let idx = self.common.shell.workspaces.active_num(&prev_output).1; match self.common.shell.activate(&prev_output, idx) { Ok(Some(new_pos)) => { @@ -1386,7 +1403,7 @@ impl State { self, None, &MotionEvent { - location: new_pos.to_f64(), + location: new_pos.to_f64().as_logical(), serial, time, }, @@ -1403,16 +1420,15 @@ impl State { } x @ Action::MoveToNextOutput | x @ Action::SendToNextOutput => { let current_output = seat.active_output(); - if let Some(next_output) = self + let next_output = self .common .shell - .outputs - .iter() + .outputs() .skip_while(|o| *o != ¤t_output) .skip(1) .next() - .cloned() - { + .cloned(); + if let Some(next_output) = next_output { if let Ok(Some(new_pos)) = Shell::move_current_window( self, seat, @@ -1426,7 +1442,7 @@ impl State { self, None, &MotionEvent { - location: new_pos.to_f64(), + location: new_pos.to_f64().as_logical(), serial, time, }, @@ -1438,17 +1454,16 @@ impl State { } x @ Action::MoveToPreviousOutput | x @ Action::SendToPreviousOutput => { let current_output = seat.active_output(); - if let Some(prev_output) = self + let prev_output = self .common .shell - .outputs - .iter() + .outputs() .rev() .skip_while(|o| *o != ¤t_output) .skip(1) .next() - .cloned() - { + .cloned(); + if let Some(prev_output) = prev_output { if let Ok(Some(new_pos)) = Shell::move_current_window( self, seat, @@ -1462,7 +1477,7 @@ impl State { self, None, &MotionEvent { - location: new_pos.to_f64(), + location: new_pos.to_f64().as_logical(), serial, time, }, @@ -1540,9 +1555,6 @@ impl State { Action::Move(direction) => { let current_output = seat.active_output(); let workspace = self.common.shell.active_space_mut(¤t_output); - if workspace.get_fullscreen(¤t_output).is_some() { - return; // TODO, is this what we want? How do we indicate the switch? - } match workspace.move_current_element(direction, seat) { MoveResult::MoveFurther(_move_further) => { @@ -1603,7 +1615,7 @@ impl State { Action::SwapWindow => { let current_output = seat.active_output(); let workspace = self.common.shell.active_space_mut(¤t_output); - if workspace.get_fullscreen(¤t_output).is_some() { + if workspace.get_fullscreen().is_some() { return; // TODO, is this what we want? Maybe disengage fullscreen instead? } @@ -1625,11 +1637,7 @@ impl State { let focus_stack = workspace.focus_stack.get(seat); let focused_window = focus_stack.last(); if let Some(window) = focused_window.map(|f| f.active_window()) { - workspace.maximize_toggle( - &window, - ¤t_output, - self.common.event_loop_handle.clone(), - ); + workspace.maximize_toggle(&window); } } Action::Resizing(direction) => self.common.shell.set_resize_mode( @@ -1705,77 +1713,83 @@ impl State { } pub fn surface_under( - global_pos: Point, - relative_pos: Point, + global_pos: Point, output: &Output, - output_geo: Rectangle, override_redirect_windows: &[X11Surface], overview: OverviewMode, workspace: &mut Workspace, - ) -> Option<(PointerFocusTarget, Point)> { - if let Some(window) = workspace.get_fullscreen(output) { + ) -> Option<(PointerFocusTarget, Point)> { + let relative_pos = global_pos.to_local(output); + let output_geo = output.geometry(); + + if let Some(window) = workspace.get_fullscreen() { let layers = layer_map_for_output(output); - if let Some(layer) = layers.layer_under(WlrLayer::Overlay, relative_pos) { + if let Some(layer) = layers.layer_under(WlrLayer::Overlay, relative_pos.as_logical()) { let layer_loc = layers.layer_geometry(layer).unwrap().loc; if layer - .surface_under(relative_pos - layer_loc.to_f64(), WindowSurfaceType::ALL) + .surface_under( + relative_pos.as_logical() - layer_loc.to_f64(), + WindowSurfaceType::ALL, + ) .is_some() { - return Some((layer.clone().into(), output_geo.loc + layer_loc)); + return Some((layer.clone().into(), output_geo.loc + layer_loc.as_global())); } } - if let Some(or) = override_redirect_windows - .iter() - .find(|or| or.is_in_input_region(&(global_pos - or.geometry().loc.to_f64()))) - { - return Some((or.clone().into(), or.geometry().loc)); + if let Some(or) = override_redirect_windows.iter().find(|or| { + or.is_in_input_region(&(global_pos.as_logical() - or.geometry().loc.to_f64())) + }) { + return Some((or.clone().into(), or.geometry().loc.as_global())); } Some((window.clone().into(), output_geo.loc)) } else { { let layers = layer_map_for_output(output); if let Some(layer) = layers - .layer_under(WlrLayer::Overlay, relative_pos) - .or_else(|| layers.layer_under(WlrLayer::Top, relative_pos)) + .layer_under(WlrLayer::Overlay, relative_pos.as_logical()) + .or_else(|| layers.layer_under(WlrLayer::Top, relative_pos.as_logical())) { let layer_loc = layers.layer_geometry(layer).unwrap().loc; if layer - .surface_under(relative_pos - layer_loc.to_f64(), WindowSurfaceType::ALL) + .surface_under( + relative_pos.as_logical() - layer_loc.to_f64(), + WindowSurfaceType::ALL, + ) .is_some() { - return Some((layer.clone().into(), output_geo.loc + layer_loc)); + return Some(( + layer.clone().into(), + output_geo.loc + layer_loc.as_global(), + )); } } } - if let Some(or) = override_redirect_windows - .iter() - .find(|or| or.is_in_input_region(&(global_pos - or.geometry().loc.to_f64()))) - { - return Some((or.clone().into(), or.geometry().loc)); + if let Some(or) = override_redirect_windows.iter().find(|or| { + or.is_in_input_region(&(global_pos.as_logical() - or.geometry().loc.to_f64())) + }) { + return Some((or.clone().into(), or.geometry().loc.as_global())); } - if let Some(surface) = workspace.get_maximized(output) { - let offset = layer_map_for_output(output).non_exclusive_zone().loc; - return Some((surface.clone().into(), output_geo.loc + offset)); - } else { - if let Some((target, loc)) = workspace.element_under(relative_pos, overview) { - return Some((target, loc + (global_pos - relative_pos).to_i32_round())); - } + if let Some((target, loc)) = workspace.element_under(global_pos, overview) { + return Some((target, loc)); + } + { + let layers = layer_map_for_output(output); + if let Some(layer) = layers + .layer_under(WlrLayer::Bottom, relative_pos.as_logical()) + .or_else(|| layers.layer_under(WlrLayer::Background, relative_pos.as_logical())) { - let layers = layer_map_for_output(output); - if let Some(layer) = layers - .layer_under(WlrLayer::Bottom, relative_pos) - .or_else(|| layers.layer_under(WlrLayer::Background, relative_pos)) + let layer_loc = layers.layer_geometry(layer).unwrap().loc; + if layer + .surface_under( + relative_pos.as_logical() - layer_loc.to_f64(), + WindowSurfaceType::ALL, + ) + .is_some() { - let layer_loc = layers.layer_geometry(layer).unwrap().loc; - if layer - .surface_under( - relative_pos - layer_loc.to_f64(), - WindowSurfaceType::ALL, - ) - .is_some() - { - return Some((layer.clone().into(), output_geo.loc + layer_loc)); - } + return Some(( + layer.clone().into(), + output_geo.loc + layer_loc.as_global(), + )); } } } @@ -1786,7 +1800,7 @@ impl State { fn sessions_for_output(state: &Common, output: &Output) -> impl Iterator { let workspace = state.shell.active_space(&output); - let maybe_fullscreen = workspace.get_fullscreen(&output); + let maybe_fullscreen = workspace.get_fullscreen(); workspace .screencopy_sessions .iter() @@ -1795,7 +1809,7 @@ fn sessions_for_output(state: &Common, output: &Output) -> impl Iterator() { + if let Some(sessions) = w.user_data().get::() { Some( sessions .0 diff --git a/src/main.rs b/src/main.rs index e87ab39d..732998e4 100644 --- a/src/main.rs +++ b/src/main.rs @@ -25,6 +25,7 @@ pub mod shell; pub mod state; #[cfg(feature = "systemd")] pub mod systemd; +pub mod theme; pub mod utils; pub mod wayland; pub mod xwayland; @@ -55,6 +56,10 @@ fn main() -> Result<()> { // potentially tell the session we are setup now session::setup_socket(event_loop.handle(), &state)?; + if let Err(err) = theme::watch_theme(event_loop.handle()) { + warn!(?err, "Failed to watch theme"); + } + // run the event loop event_loop.run(None, &mut state, |state| { // shall we shut down? diff --git a/src/session.rs b/src/session.rs index 0a75977e..c88d6351 100644 --- a/src/session.rs +++ b/src/session.rs @@ -2,7 +2,7 @@ use smithay::reexports::{ calloop::{generic::Generic, Interest, LoopHandle, Mode, PostAction}, - nix::{fcntl, unistd}, + rustix, }; use anyhow::{anyhow, Context, Result}; @@ -50,20 +50,24 @@ impl From for StreamWrapper { } } +unsafe fn set_cloexec(fd: RawFd) -> rustix::io::Result<()> { + if fd == -1 { + return Err(rustix::io::Errno::BADF); + } + let fd = BorrowedFd::borrow_raw(fd); + let flags = rustix::io::fcntl_getfd(fd)?; + rustix::io::fcntl_setfd(fd, flags | rustix::io::FdFlags::CLOEXEC) +} + pub fn setup_socket(handle: LoopHandle, state: &State) -> Result<()> { if let Ok(fd_num) = std::env::var("COSMIC_SESSION_SOCK") { if let Ok(fd) = fd_num.parse::() { - // set CLOEXEC - let flags = fcntl::fcntl(fd, fcntl::FcntlArg::F_GETFD); - let result = flags - .map(|f| fcntl::FdFlag::from_bits(f).unwrap() | fcntl::FdFlag::FD_CLOEXEC) - .and_then(|f| fcntl::fcntl(fd, fcntl::FcntlArg::F_SETFD(f))); - let mut session_socket = match result { + let mut session_socket = match unsafe { set_cloexec(fd) } { // CLOEXEC worked and we can startup with session IPC Ok(_) => unsafe { UnixStream::from_raw_fd(fd) }, // CLOEXEC didn't work, something is wrong with the fd, just close it Err(err) => { - let _ = unistd::close(fd); + unsafe { rustix::io::close(fd) }; return Err(err).with_context(|| "Failed to setup session socket"); } }; diff --git a/src/shell/element/mod.rs b/src/shell/element/mod.rs index 12157b2a..75c70658 100644 --- a/src/shell/element/mod.rs +++ b/src/shell/element/mod.rs @@ -4,7 +4,7 @@ use crate::{ GlMultiError, GlMultiFrame, GlMultiRenderer, }, state::State, - utils::prelude::SeatExt, + utils::prelude::*, }; use calloop::LoopHandle; use id_tree::NodeId; @@ -74,7 +74,7 @@ use tracing::debug; use super::{ focus::FocusDirection, layout::{floating::ResizeState, tiling::NodeDesc}, - Direction, + Direction, ManagedLayer, }; space_elements! { @@ -84,18 +84,25 @@ space_elements! { Stack=CosmicStack, } +#[derive(Debug, Clone)] +pub struct MaximizedState { + pub original_geometry: Rectangle, + pub original_layer: ManagedLayer, +} + #[derive(Clone)] pub struct CosmicMapped { element: CosmicMappedInternal, // associated data last_cursor_position: Arc>>>, + pub maximized_state: Arc>>, //tiling pub tiling_node_id: Arc>>, //floating - pub(super) last_geometry: Arc>>>, pub(super) resize_state: Arc>>, + pub last_geometry: Arc>>>, #[cfg(feature = "debug")] debug: Arc>>, @@ -106,8 +113,10 @@ impl fmt::Debug for CosmicMapped { f.debug_struct("CosmicMapped") .field("element", &self.element) .field("last_cursor_position", &self.last_cursor_position) + .field("maximized_state", &self.maximized_state) .field("tiling_node_id", &self.tiling_node_id) .field("resize_state", &self.resize_state) + .field("last_geometry", &self.last_geometry) .finish() } } @@ -361,7 +370,15 @@ impl CosmicMapped { window.is_activated(pending) } - pub fn set_geometry(&self, geo: Rectangle) { + pub fn pending_size(&self) -> Option> { + match &self.element { + CosmicMappedInternal::Stack(s) => s.pending_size(), + CosmicMappedInternal::Window(w) => w.pending_size(), + _ => unreachable!(), + } + } + + pub fn set_geometry(&self, geo: Rectangle) { match &self.element { CosmicMappedInternal::Stack(s) => s.set_geometry(geo), CosmicMappedInternal::Window(w) => w.set_geometry(geo), @@ -491,7 +508,8 @@ impl CosmicMapped { pub fn convert_to_stack<'a>( &mut self, - outputs: impl Iterator)>, + (output, overlap): (&'a Output, Rectangle), + theme: cosmic::Theme, ) { match &self.element { CosmicMappedInternal::Window(window) => { @@ -499,13 +517,11 @@ impl CosmicMapped { let activated = surface.is_activated(true); let handle = window.loop_handle(); - let stack = CosmicStack::new(std::iter::once(surface), handle); + let stack = CosmicStack::new(std::iter::once(surface), handle, theme); if let Some(geo) = self.last_geometry.lock().unwrap().clone() { - stack.set_geometry(geo); - } - for (output, overlap) in outputs { - stack.output_enter(output, overlap); + stack.set_geometry(geo.to_global(&output)); } + stack.output_enter(output, overlap); stack.set_activate(activated); stack.active().send_configure(); stack.refresh(); @@ -519,19 +535,18 @@ impl CosmicMapped { pub fn convert_to_surface<'a>( &mut self, surface: CosmicSurface, - outputs: impl Iterator)>, + (output, overlap): (&'a Output, Rectangle), + theme: cosmic::Theme, ) { let handle = self.loop_handle(); surface.try_force_undecorated(false); surface.set_tiled(false); - let window = CosmicWindow::new(surface, handle); + let window = CosmicWindow::new(surface, handle, theme); if let Some(geo) = self.last_geometry.lock().unwrap().clone() { - window.set_geometry(geo); - } - for (output, overlap) in outputs { - window.output_enter(output, overlap); + window.set_geometry(geo.to_global(&output)); } + window.output_enter(output, overlap); window.set_activate(self.is_activated(true)); window.surface().send_configure(); window.refresh(); @@ -759,6 +774,22 @@ impl CosmicMapped { popup_elements.into_iter().map(C::from).collect(), ) } + + pub(crate) fn update_theme(&self, theme: cosmic::Theme) { + match &self.element { + CosmicMappedInternal::Window(w) => w.set_theme(theme), + CosmicMappedInternal::Stack(s) => s.set_theme(theme), + CosmicMappedInternal::_GenericCatcher(_) => {} + } + } + + pub(crate) fn force_redraw(&self) { + match &self.element { + CosmicMappedInternal::Window(w) => w.force_redraw(), + CosmicMappedInternal::Stack(s) => s.force_redraw(), + CosmicMappedInternal::_GenericCatcher(_) => {} + } + } } impl IsAlive for CosmicMapped { @@ -1067,9 +1098,10 @@ impl From for CosmicMapped { CosmicMapped { element: CosmicMappedInternal::Window(w), last_cursor_position: Arc::new(Mutex::new(HashMap::new())), + maximized_state: Arc::new(Mutex::new(None)), tiling_node_id: Arc::new(Mutex::new(None)), - last_geometry: Arc::new(Mutex::new(None)), resize_state: Arc::new(Mutex::new(None)), + last_geometry: Arc::new(Mutex::new(None)), #[cfg(feature = "debug")] debug: Arc::new(Mutex::new(None)), } @@ -1081,9 +1113,10 @@ impl From for CosmicMapped { CosmicMapped { element: CosmicMappedInternal::Stack(s), last_cursor_position: Arc::new(Mutex::new(HashMap::new())), + maximized_state: Arc::new(Mutex::new(None)), tiling_node_id: Arc::new(Mutex::new(None)), - last_geometry: Arc::new(Mutex::new(None)), resize_state: Arc::new(Mutex::new(None)), + last_geometry: Arc::new(Mutex::new(None)), #[cfg(feature = "debug")] debug: Arc::new(Mutex::new(None)), } diff --git a/src/shell/element/resize_indicator.rs b/src/shell/element/resize_indicator.rs index 4752a68b..e16cff25 100644 --- a/src/shell/element/resize_indicator.rs +++ b/src/shell/element/resize_indicator.rs @@ -23,6 +23,7 @@ pub fn resize_indicator( direction: ResizeDirection, config: &Config, evlh: LoopHandle<'static, crate::state::State>, + theme: cosmic::Theme, ) -> ResizeIndicator { ResizeIndicator::new( ResizeIndicatorInternal { @@ -49,6 +50,7 @@ pub fn resize_indicator( }, Size::from((1, 1)), evlh, + theme, ) } diff --git a/src/shell/element/stack.rs b/src/shell/element/stack.rs index 04e333ec..1a37f7a6 100644 --- a/src/shell/element/stack.rs +++ b/src/shell/element/stack.rs @@ -5,7 +5,7 @@ use crate::{ }, state::State, utils::iced::{IcedElement, Program}, - utils::prelude::SeatExt, + utils::prelude::*, wayland::handlers::screencopy::ScreencopySessions, }; use calloop::LoopHandle; @@ -91,7 +91,7 @@ pub struct CosmicStackInternal { override_alive: Arc, last_seat: Arc, Serial)>>>, last_location: Arc, Serial, u32)>>>, - geometry: Arc>>>, + geometry: Arc>>>, mask: Arc>>, } @@ -129,6 +129,7 @@ impl CosmicStack { pub fn new>( windows: impl Iterator, handle: LoopHandle<'static, crate::state::State>, + theme: cosmic::Theme, ) -> CosmicStack { let windows = windows.map(Into::into).collect::>(); assert!(!windows.is_empty()); @@ -160,6 +161,7 @@ impl CosmicStack { }, (width, TAB_HEIGHT), handle, + theme, )) } @@ -406,7 +408,17 @@ impl CosmicStack { Point::from((0, TAB_HEIGHT)) } - pub fn set_geometry(&self, geo: Rectangle) { + pub fn pending_size(&self) -> Option> { + self.0.with_program(|p| { + p.geometry + .lock() + .unwrap() + .clone() + .map(|geo| geo.size.as_logical()) + }) + } + + pub fn set_geometry(&self, geo: Rectangle) { self.0.with_program(|p| { let loc = (geo.loc.x, geo.loc.y + TAB_HEIGHT); let size = (geo.size.w, geo.size.h - TAB_HEIGHT); @@ -544,6 +556,14 @@ impl CosmicStack { popup_elements.into_iter().map(C::from).collect(), ) } + + pub(crate) fn set_theme(&self, theme: cosmic::Theme) { + self.0.set_theme(theme); + } + + pub(crate) fn force_redraw(&self) { + self.0.force_redraw(); + } } #[derive(Debug, Clone, Copy)] @@ -1047,20 +1067,21 @@ impl PointerTarget for CosmicStack { if let Some(workspace) = data.common.shell.space_for(stack_mapped) { // TODO: Unify this somehow with Shell::move_request/Workspace::move_request let button = 0x110; // BTN_LEFT - let pos = event.location; + let pos = event.location.as_global(); let start_data = PointerGrabStartData { focus: None, button, - location: pos, + location: pos.as_logical(), }; let mapped = CosmicMapped::from(CosmicWindow::new( surface, self.0.loop_handle(), + data.common.theme.clone(), )); let elem_geo = workspace.element_geometry(stack_mapped).unwrap(); let indicator_thickness = - data.common.config.static_conf.active_hint; + data.common.theme.cosmic().active_hint as u8; let was_tiled = workspace.is_tiled(stack_mapped); self.remove_idx(dragged_out); diff --git a/src/shell/element/stack/tabs.rs b/src/shell/element/stack/tabs.rs index b15e73cb..291e9624 100644 --- a/src/shell/element/stack/tabs.rs +++ b/src/shell/element/stack/tabs.rs @@ -248,6 +248,14 @@ where } impl State { + fn next_tab_animation(&self) -> Option<&TabAnimationState> { + let now = Instant::now(); + + self.tab_animations + .iter() + .find(|anim| now.duration_since(anim.start_time) <= TAB_ANIMATION_DURATION) + } + pub fn offset(&self, bounds: Rectangle, content_bounds: Size) -> Vector { if let Some(animation) = self.scroll_animation { let percentage = { @@ -275,19 +283,41 @@ impl State { } pub fn cleanup_old_animations(&mut self) { + let start_time = Instant::now(); + if let Some(animation) = self.scroll_animation.as_ref() { - if Instant::now().duration_since(animation.start_time) > SCROLL_ANIMATION_DURATION { + if start_time.duration_since(animation.start_time) > SCROLL_ANIMATION_DURATION { self.scroll_animation.take(); } } - if let Some(animation) = self.tab_animations.front() { - if Instant::now().duration_since(animation.start_time) > TAB_ANIMATION_DURATION { - self.tab_animations.pop_front(); - if let Some(next_animation) = self.tab_animations.front_mut() { - next_animation.start_time = Instant::now(); + Self::discard_expired_tab_animations(&mut self.tab_animations, start_time); + } + + /// Remove expired tab animations from the queue. + fn discard_expired_tab_animations( + tab_animations: &mut VecDeque, + start_time: Instant, + ) { + if let Some(mut animation) = tab_animations.pop_front() { + let mut set_next_start = false; + + while start_time.duration_since(animation.start_time) > TAB_ANIMATION_DURATION { + set_next_start = true; + + if let Some(next) = tab_animations.pop_front() { + animation = next; + continue; } + + return; + } + + if set_next_start { + animation.start_time = start_time; } + + tab_animations.push_front(animation); } } } @@ -532,7 +562,9 @@ where renderer.with_layer(bounds, |renderer| { renderer.with_translation(Vector::new(-offset.x, -offset.y), |renderer| { - let percentage = if let Some(animation) = state.tab_animations.front() { + let tab_animation = state.next_tab_animation(); + + let percentage = if let Some(animation) = tab_animation { let percentage = Instant::now() .duration_since(animation.start_time) .as_millis() as f32 @@ -547,7 +579,7 @@ where .zip(tree.children.iter().skip(2)) .zip(layout.children().skip(2)) { - let bounds = if let Some(animation) = state.tab_animations.front() { + let bounds = if let Some(animation) = tab_animation { let id = tab.as_widget().id().unwrap(); let previous = animation @@ -755,11 +787,15 @@ where if unknown_keys || changes.is_some() { if !scrolling || !matches!(changes, Some(Difference::Focus)) { + let start_time = Instant::now(); + + State::discard_expired_tab_animations(&mut state.tab_animations, start_time); + // new tab_animation state.tab_animations.push_back(TabAnimationState { previous_bounds: last_state.clone(), next_bounds: current_state.clone(), - start_time: Instant::now(), + start_time, }); } diff --git a/src/shell/element/stack_hover.rs b/src/shell/element/stack_hover.rs index a84520d3..1877d123 100644 --- a/src/shell/element/stack_hover.rs +++ b/src/shell/element/stack_hover.rs @@ -18,8 +18,9 @@ pub type StackHover = IcedElement; pub fn stack_hover( evlh: LoopHandle<'static, crate::state::State>, size: Size, + theme: cosmic::Theme, ) -> StackHover { - StackHover::new(StackHoverInternal, size, evlh) + StackHover::new(StackHoverInternal, size, evlh, theme) } pub struct StackHoverInternal; diff --git a/src/shell/element/surface.rs b/src/shell/element/surface.rs index 67b88b1b..99ccbef5 100644 --- a/src/shell/element/surface.rs +++ b/src/shell/element/surface.rs @@ -51,6 +51,7 @@ use smithay::{ use crate::{ state::{State, SurfaceDmabufFeedback}, + utils::prelude::*, wayland::handlers::decoration::PreferredDecorationMode, }; @@ -122,13 +123,23 @@ impl CosmicSurface { } } - pub fn set_geometry(&self, geo: Rectangle) { + pub fn pending_size(&self) -> Option> { + match self { + CosmicSurface::Wayland(window) => { + window.toplevel().with_pending_state(|state| state.size) + } + CosmicSurface::X11(surface) => Some(surface.geometry().size), + _ => unreachable!(), + } + } + + pub fn set_geometry(&self, geo: Rectangle) { match self { CosmicSurface::Wayland(window) => window .toplevel() - .with_pending_state(|state| state.size = Some(geo.size)), + .with_pending_state(|state| state.size = Some(geo.size.as_logical())), CosmicSurface::X11(surface) => { - let _ = surface.configure(geo); + let _ = surface.configure(geo.as_logical()); } _ => {} } diff --git a/src/shell/element/swap_indicator.rs b/src/shell/element/swap_indicator.rs index 75b011ef..380bf2e6 100644 --- a/src/shell/element/swap_indicator.rs +++ b/src/shell/element/swap_indicator.rs @@ -15,8 +15,11 @@ use smithay::utils::Size; pub type SwapIndicator = IcedElement; -pub fn swap_indicator(evlh: LoopHandle<'static, crate::state::State>) -> SwapIndicator { - SwapIndicator::new(SwapIndicatorInternal, Size::from((1, 1)), evlh) +pub fn swap_indicator( + evlh: LoopHandle<'static, crate::state::State>, + theme: cosmic::Theme, +) -> SwapIndicator { + SwapIndicator::new(SwapIndicatorInternal, Size::from((1, 1)), evlh, theme) } pub struct SwapIndicatorInternal; diff --git a/src/shell/element/window.rs b/src/shell/element/window.rs index 67856dd1..e56322c4 100644 --- a/src/shell/element/window.rs +++ b/src/shell/element/window.rs @@ -3,7 +3,7 @@ use crate::{ state::State, utils::{ iced::{IcedElement, Program}, - prelude::SeatExt, + prelude::*, }, wayland::handlers::screencopy::ScreencopySessions, }; @@ -114,6 +114,7 @@ impl CosmicWindow { pub fn new( window: impl Into, handle: LoopHandle<'static, crate::state::State>, + theme: cosmic::Theme, ) -> CosmicWindow { let window = window.into(); let width = window.geometry().size.w; @@ -129,10 +130,21 @@ impl CosmicWindow { }, (width, SSD_HEIGHT), handle, + theme, )) } - pub fn set_geometry(&self, geo: Rectangle) { + pub fn pending_size(&self) -> Option> { + self.0.with_program(|p| { + let mut size = p.window.pending_size()?; + if p.has_ssd(true) { + size.h += SSD_HEIGHT; + } + Some(size) + }) + } + + pub fn set_geometry(&self, geo: Rectangle) { self.0.with_program(|p| { let loc = ( geo.loc.x, @@ -213,6 +225,14 @@ impl CosmicWindow { popup_elements.into_iter().map(C::from).collect(), ) } + + pub(crate) fn set_theme(&self, theme: cosmic::Theme) { + self.0.set_theme(theme); + } + + pub(crate) fn force_redraw(&self) { + self.0.force_redraw(); + } } #[derive(Debug, Clone, Copy)] @@ -241,27 +261,20 @@ impl Program for CosmicWindowInternal { } } Message::Maximize => { - if let Some((seat, _serial)) = self.last_seat.lock().unwrap().clone() { - if let Some(surface) = self.window.wl_surface() { - loop_handle.insert_idle(move |state| { - if let Some(mapped) = - state.common.shell.element_for_wl_surface(&surface).cloned() - { - if let Some(workspace) = state.common.shell.space_for_mut(&mapped) { - let output = seat.active_output(); - let (window, _) = mapped - .windows() - .find(|(w, _)| w.wl_surface().as_ref() == Some(&surface)) - .unwrap(); - workspace.maximize_toggle( - &window, - &output, - state.common.event_loop_handle.clone(), - ) - } + if let Some(surface) = self.window.wl_surface() { + loop_handle.insert_idle(move |state| { + if let Some(mapped) = + state.common.shell.element_for_wl_surface(&surface).cloned() + { + if let Some(workspace) = state.common.shell.space_for_mut(&mapped) { + let (window, _) = mapped + .windows() + .find(|(w, _)| w.wl_surface().as_ref() == Some(&surface)) + .unwrap(); + workspace.maximize_toggle(&window) } - }); - } + } + }); } } Message::Close => self.window.close(), diff --git a/src/shell/focus/mod.rs b/src/shell/focus/mod.rs index 07f24deb..653225ab 100644 --- a/src/shell/focus/mod.rs +++ b/src/shell/focus/mod.rs @@ -3,7 +3,6 @@ use crate::{ state::Common, utils::prelude::*, wayland::handlers::xdg_shell::PopupGrabData, - xwayland::XWaylandState, }; use indexmap::IndexSet; use smithay::{ @@ -100,11 +99,9 @@ impl Shell { // update FocusStack and notify layouts about new focus (if any window) let element = match target { Some(KeyboardFocusTarget::Element(mapped)) => Some(mapped.clone()), - Some(KeyboardFocusTarget::Fullscreen(window)) => state - .common - .shell - .element_for_surface(&window.surface()) - .cloned(), + Some(KeyboardFocusTarget::Fullscreen(window)) => { + state.common.shell.element_for_surface(window).cloned() + } _ => None, }; @@ -139,11 +136,7 @@ impl Shell { } } - fn update_active<'a, 'b>( - &mut self, - seats: impl Iterator>, - mut xwm: Option<&'b mut XWaylandState>, - ) { + fn update_active<'a, 'b>(&mut self, seats: impl Iterator>) { // update activate status let focused_windows = seats .flat_map(|seat| { @@ -154,7 +147,7 @@ impl Shell { return None; } - Some(self.outputs.iter().flat_map(|o| { + Some(self.outputs().flat_map(|o| { let space = self.active_space(o); let stack = space.focus_stack.get(seat); stack.last().cloned() @@ -163,14 +156,10 @@ impl Shell { .flatten() .collect::>(); - for output in self.outputs.iter() { - let workspace = self.workspaces.active_mut(output); + for output in self.outputs().cloned().collect::>().into_iter() { + // TODO: Add self.workspaces.active_workspaces() + let workspace = self.workspaces.active_mut(&output); for focused in focused_windows.iter() { - if let CosmicSurface::X11(window) = focused.active_window() { - if let Some(xwm) = xwm.as_mut().and_then(|state| state.xwm.as_mut()) { - let _ = xwm.raise_window(&window); - } - } raise_with_children(&mut workspace.floating_layer, focused); } for window in workspace.mapped() { @@ -213,18 +202,15 @@ impl Common { ) { Shell::set_focus(state, target, active_seat, serial); let seats = state.common.seats().cloned().collect::>(); - state - .common - .shell - .update_active(seats.iter(), state.common.xwayland_state.as_mut()); + state.common.shell.update_active(seats.iter()); } pub fn refresh_focus(state: &mut State) { let seats = state.common.seats().cloned().collect::>(); for seat in seats { let output = seat.active_output(); - if !state.common.shell.outputs.contains(&output) { - seat.set_active_output(&state.common.shell.outputs[0]); + if !state.common.shell.outputs().any(|o| o == &output) { + seat.set_active_output(&state.common.shell.outputs().next().unwrap()); continue; } let last_known_focus = ActiveFocus::get(&seat); @@ -236,7 +222,7 @@ impl Common { let workspace = state.common.shell.active_space(&output); let focus_stack = workspace.focus_stack.get(&seat); if focus_stack.last().map(|m| m == &mapped).unwrap_or(false) - && workspace.get_fullscreen(&output).is_none() + && workspace.get_fullscreen().is_none() { continue; // Focus is valid } else { @@ -248,11 +234,16 @@ impl Common { continue; // Focus is valid } } - KeyboardFocusTarget::Group(WindowGroup { - output: weak_output, - .. - }) => { - if weak_output == output { + KeyboardFocusTarget::Group(WindowGroup { node, .. }) => { + if state + .common + .shell + .workspaces + .active(&output) + .1 + .tiling_layer + .has_node(&node) + { continue; // Focus is valid, } } @@ -262,9 +253,9 @@ impl Common { if focus_stack .last() - .map(|m| m.has_active_window(&window.surface())) + .map(|m| m.has_active_window(&window)) .unwrap_or(false) - && workspace.get_fullscreen(&output).is_some() + && workspace.get_fullscreen().is_some() { continue; // Focus is valid } else { @@ -276,6 +267,31 @@ impl Common { } }; } else { + if let KeyboardFocusTarget::Popup(_) = target { + if let Some(popup_grab) = seat + .user_data() + .get::() + .and_then(|x| x.take()) + { + if !popup_grab.has_ended() { + if let Some(new) = popup_grab.current_grab() { + trace!("restore focus to previous popup grab"); + if let Some(keyboard) = seat.get_keyboard() { + keyboard.set_focus( + state, + Some(new.clone()), + SERIAL_COUNTER.next_serial(), + ); + } + ActiveFocus::set(&seat, Some(new)); + seat.user_data() + .get_or_insert::(PopupGrabData::default) + .set(Some(popup_grab)); + continue; + } + } + } + } trace!("Surface dead, focus fixup"); } } else { @@ -307,7 +323,7 @@ impl Common { .common .shell .active_space(&output) - .get_fullscreen(&output) + .get_fullscreen() .cloned() .map(KeyboardFocusTarget::Fullscreen) .or_else(|| { @@ -330,9 +346,6 @@ impl Common { } let seats = state.common.seats().cloned().collect::>(); - state - .common - .shell - .update_active(seats.iter(), state.common.xwayland_state.as_mut()) + state.common.shell.update_active(seats.iter()) } } diff --git a/src/shell/focus/target.rs b/src/shell/focus/target.rs index 72d41ae7..15a69a7a 100644 --- a/src/shell/focus/target.rs +++ b/src/shell/focus/target.rs @@ -1,10 +1,7 @@ use std::sync::Weak; use crate::{ - shell::{ - element::{CosmicMapped, CosmicWindow}, - layout::tiling::ResizeForkTarget, - }, + shell::{element::CosmicMapped, layout::tiling::ResizeForkTarget, CosmicSurface}, utils::prelude::*, wayland::handlers::xdg_shell::popup::get_popup_toplevel, }; @@ -22,7 +19,6 @@ use smithay::{ }, Seat, }, - output::WeakOutput, reexports::wayland_server::{backend::ObjectId, protocol::wl_surface::WlSurface, Resource}, utils::{IsAlive, Serial}, wayland::seat::WaylandFocus, @@ -32,7 +28,7 @@ use smithay::{ #[derive(Debug, Clone, PartialEq)] pub enum PointerFocusTarget { Element(CosmicMapped), - Fullscreen(CosmicWindow), + Fullscreen(CosmicSurface), LayerSurface(LayerSurface), Popup(PopupKind), OverrideRedirect(X11Surface), @@ -42,7 +38,7 @@ pub enum PointerFocusTarget { #[derive(Debug, Clone, PartialEq)] pub enum KeyboardFocusTarget { Element(CosmicMapped), - Fullscreen(CosmicWindow), + Fullscreen(CosmicSurface), Group(WindowGroup), LayerSurface(LayerSurface), Popup(PopupKind), @@ -87,16 +83,13 @@ impl KeyboardFocusTarget { #[derive(Debug, Clone)] pub struct WindowGroup { pub node: NodeId, - pub output: WeakOutput, pub alive: Weak<()>, pub focus_stack: Vec, } impl PartialEq for WindowGroup { fn eq(&self, other: &Self) -> bool { - self.node == other.node - && self.output == other.output - && Weak::ptr_eq(&self.alive, &other.alive) + self.node == other.node && Weak::ptr_eq(&self.alive, &other.alive) } } @@ -564,8 +557,8 @@ impl From for PointerFocusTarget { } } -impl From for PointerFocusTarget { - fn from(s: CosmicWindow) -> Self { +impl From for PointerFocusTarget { + fn from(s: CosmicSurface) -> Self { PointerFocusTarget::Fullscreen(s) } } @@ -600,8 +593,8 @@ impl From for KeyboardFocusTarget { } } -impl From for KeyboardFocusTarget { - fn from(s: CosmicWindow) -> Self { +impl From for KeyboardFocusTarget { + fn from(s: CosmicSurface) -> Self { KeyboardFocusTarget::Fullscreen(s) } } diff --git a/src/shell/grabs/moving.rs b/src/shell/grabs/moving.rs index e8233b55..0d4a0f93 100644 --- a/src/shell/grabs/moving.rs +++ b/src/shell/grabs/moving.rs @@ -17,6 +17,7 @@ use crate::{ utils::prelude::*, }; +use cosmic::theme::CosmicTheme; use smithay::{ backend::renderer::{ element::{utils::RescaleRenderElement, AsRenderElements, RenderElement}, @@ -58,7 +59,13 @@ pub struct MoveGrabState { } impl MoveGrabState { - pub fn render(&self, renderer: &mut R, seat: &Seat, output: &Output) -> Vec + pub fn render( + &self, + renderer: &mut R, + seat: &Seat, + output: &Output, + theme: &CosmicTheme, + ) -> Vec where R: Renderer + ImportAll + ImportMem + AsGlowRenderer, ::TextureId: 'static, @@ -77,21 +84,33 @@ impl MoveGrabState { } else { 1.0 }; + let alpha = if &seat.active_output() == output { + 1.0 + } else { + 0.4 + }; let cursor_at = seat.get_pointer().unwrap().current_location(); let mut window_geo = self.window.geometry(); window_geo.loc += cursor_at.to_i32_round() + self.window_offset; - if !output.geometry().intersection(window_geo).is_some() { + if !output + .geometry() + .as_logical() + .intersection(window_geo) + .is_some() + { return Vec::new(); } let output_scale: Scale = output.current_scale().fractional_scale().into(); let scaling_offset = self.window_offset - self.window_offset.to_f64().upscale(scale).to_i32_round(); - let render_location = - cursor_at.to_i32_round() - output.geometry().loc + self.window_offset - scaling_offset; + let render_location = cursor_at.to_i32_round() - output.geometry().loc.as_logical() + + self.window_offset + - scaling_offset; + let active_window_hint = crate::theme::active_window_hint(theme); let focus_element = if self.indicator_thickness > 0 { Some( CosmicMappedRenderElement::from(IndicatorShader::focus_element( @@ -105,10 +124,16 @@ impl MoveGrabState { .to_f64() .upscale(scale) .to_i32_round(), - ), + ) + .as_local(), self.indicator_thickness, output_scale.x, - 1.0, + alpha, + [ + active_window_hint.red, + active_window_hint.green, + active_window_hint.blue, + ], )) .into(), ) @@ -123,7 +148,7 @@ impl MoveGrabState { (render_location - self.window.geometry().loc) .to_physical_precise_round(output_scale), output_scale, - 1.0, + alpha, ); self.stacking_indicator @@ -208,6 +233,7 @@ impl PointerGrab for MoveGrab { .find(|output| { output .geometry() + .as_logical() .contains(handle.current_location().to_i32_round()) }) .cloned() @@ -221,7 +247,7 @@ impl PointerGrab for MoveGrab { .workspaces .active_mut(&self.cursor_output) .tiling_layer - .cleanup_drag(&self.cursor_output); + .cleanup_drag(); self.cursor_output = current_output.clone(); } @@ -233,8 +259,8 @@ impl PointerGrab for MoveGrab { if let Some(grab_state) = borrow.as_mut().and_then(|s| s.as_mut()) { let mut window_geo = self.window.geometry(); window_geo.loc += event.location.to_i32_round() + grab_state.window_offset; - for output in &state.common.shell.outputs { - if let Some(overlap) = output.geometry().intersection(window_geo) { + for output in state.common.shell.outputs() { + if let Some(overlap) = output.geometry().as_logical().intersection(window_geo) { if self.window_outputs.insert(output.clone()) { self.window.output_enter(output, overlap); if let Some(indicator) = @@ -261,11 +287,21 @@ impl PointerGrab for MoveGrab { if indicator_location.is_some() != grab_state.stacking_indicator.is_some() { grab_state.stacking_indicator = indicator_location.map(|geo| { - let element = stack_hover(state.common.event_loop_handle.clone(), geo.size); + let element = stack_hover( + state.common.event_loop_handle.clone(), + geo.size.as_logical(), + state.common.theme.clone(), + ); for output in &self.window_outputs { - element.output_enter(output, output.geometry()); + element.output_enter( + output, + Rectangle::from_loc_and_size( + (0, 0), + output.geometry().size.as_logical(), + ), + ); } - (element, geo.loc) + (element, geo.loc.as_logical()) }); } } @@ -397,8 +433,8 @@ impl MoveGrab { start_data: PointerGrabStartData, window: CosmicMapped, seat: &Seat, - initial_cursor_location: Point, - initial_window_location: Point, + initial_cursor_location: Point, + initial_window_location: Point, indicator_thickness: u8, was_tiled: bool, ) -> MoveGrab { @@ -409,7 +445,8 @@ impl MoveGrab { let grab_state = MoveGrabState { window: window.clone(), - window_offset: initial_window_location - initial_cursor_location.to_i32_round(), + window_offset: (initial_window_location - initial_cursor_location.to_i32_round()) + .as_logical(), indicator_thickness, start: Instant::now(), stacking_indicator: None, @@ -451,16 +488,16 @@ impl MoveGrab { // No more buttons are pressed, release the grab. let output = self.seat.active_output(); - let position = if let Some(grab_state) = self + let position: Option<(CosmicMapped, Point)> = if let Some(grab_state) = self .seat .user_data() .get::() .and_then(|s| s.borrow_mut().take()) { if grab_state.window.alive() { - let window_location = handle.current_location().to_i32_round() - - output.geometry().loc - + grab_state.window_offset; + let window_location = (handle.current_location().to_i32_round() + + grab_state.window_offset) + .as_global(); let workspace_handle = state.common.shell.active_space(&output).handle; for old_output in self.window_outputs.iter().filter(|o| *o != &output) { @@ -480,41 +517,26 @@ impl MoveGrab { } if self.tiling { - Some( - state - .common - .shell - .active_space_mut(&output) - .tiling_layer - .drop_window(grab_state.window, &output, handle.current_location()), - ) - } else { - let offset = state + let (window, location) = state .common .shell - .active_space(&output) - .floating_layer - .space - .output_geometry(&output) - .unwrap() - .loc; + .active_space_mut(&output) + .tiling_layer + .drop_window(grab_state.window); + Some((window, location.to_global(&output))) + } else { grab_state.window.set_geometry(Rectangle::from_loc_and_size( - window_location + offset, - grab_state.window.geometry().size, + window_location, + grab_state.window.geometry().size.as_global(), )); - state - .common - .shell - .active_space_mut(&output) - .floating_layer - .map_internal(grab_state.window, &output, Some(window_location + offset)); - - let pointer_pos = handle.current_location(); - let relative_pos = state.common.shell.map_global_to_space(pointer_pos, &output); - Some(( - self.window.clone(), - window_location + offset + (pointer_pos - relative_pos).to_i32_round(), - )) + let workspace = state.common.shell.active_space_mut(&output); + workspace.floating_layer.map_internal( + grab_state.window, + Some(window_location.to_local(&workspace.output)), + None, + ); + + Some((self.window.clone(), window_location)) } } else { None @@ -523,7 +545,7 @@ impl MoveGrab { None }; - handle.unset_grab(state, serial, time); + handle.unset_grab(state, serial, time, true); { let cursor_state = self.seat.user_data().get::().unwrap(); @@ -535,7 +557,7 @@ impl MoveGrab { state, Some(( PointerFocusTarget::from(mapped.clone()), - position - self.window.geometry().loc, + position.as_logical() - self.window.geometry().loc, )), &MotionEvent { location: handle.current_location(), diff --git a/src/shell/layout/floating/grabs/resize.rs b/src/shell/layout/floating/grabs/resize.rs index 530c9a86..d245ef22 100644 --- a/src/shell/layout/floating/grabs/resize.rs +++ b/src/shell/layout/floating/grabs/resize.rs @@ -1,5 +1,7 @@ // SPDX-License-Identifier: GPL-3.0-only +use std::sync::atomic::{AtomicBool, Ordering}; + use crate::{ shell::{ element::CosmicMapped, focus::target::PointerFocusTarget, grabs::ResizeEdge, CosmicSurface, @@ -8,11 +10,15 @@ use crate::{ }; use smithay::{ desktop::space::SpaceElement, - input::pointer::{ - AxisFrame, ButtonEvent, GestureHoldBeginEvent, GestureHoldEndEvent, GesturePinchBeginEvent, - GesturePinchEndEvent, GesturePinchUpdateEvent, GestureSwipeBeginEvent, - GestureSwipeEndEvent, GestureSwipeUpdateEvent, GrabStartData as PointerGrabStartData, - MotionEvent, PointerGrab, PointerInnerHandle, RelativeMotionEvent, + input::{ + pointer::{ + AxisFrame, ButtonEvent, GestureHoldBeginEvent, GestureHoldEndEvent, + GesturePinchBeginEvent, GesturePinchEndEvent, GesturePinchUpdateEvent, + GestureSwipeBeginEvent, GestureSwipeEndEvent, GestureSwipeUpdateEvent, + GrabStartData as PointerGrabStartData, MotionEvent, PointerGrab, PointerInnerHandle, + RelativeMotionEvent, + }, + Seat, }, utils::{IsAlive, Logical, Point, Rectangle, Size}, }; @@ -39,6 +45,7 @@ pub enum ResizeState { pub struct ResizeSurfaceGrab { start_data: PointerGrabStartData, + seat: Seat, window: CosmicMapped, edges: ResizeEdge, initial_window_size: Size, @@ -58,7 +65,13 @@ impl PointerGrab for ResizeSurfaceGrab { // It is impossible to get `min_size` and `max_size` of dead toplevel, so we return early. if !self.window.alive() { - handle.unset_grab(data, event.serial, event.time); + self.seat + .user_data() + .get::() + .unwrap() + .0 + .store(false, Ordering::SeqCst); + handle.unset_grab(data, event.serial, event.time, true); return; } @@ -101,10 +114,10 @@ impl PointerGrab for ResizeSurfaceGrab { self.window.set_resizing(true); self.window.set_geometry(Rectangle::from_loc_and_size( match self.window.active_window() { - CosmicSurface::X11(s) => s.geometry().loc, + CosmicSurface::X11(s) => s.geometry().loc.as_global(), _ => (0, 0).into(), }, - self.last_window_size, + self.last_window_size.as_global(), )); self.window.configure(); } @@ -129,7 +142,13 @@ impl PointerGrab for ResizeSurfaceGrab { handle.button(data, event); if handle.current_pressed().is_empty() { // No more buttons are pressed, release the grab. - handle.unset_grab(data, event.serial, event.time); + self.seat + .user_data() + .get::() + .unwrap() + .0 + .store(false, Ordering::SeqCst); + handle.unset_grab(data, event.serial, event.time, true); // If toplevel is dead, we can't resize it, so we return early. if !self.window.alive() { @@ -139,10 +158,10 @@ impl PointerGrab for ResizeSurfaceGrab { self.window.set_resizing(false); self.window.set_geometry(Rectangle::from_loc_and_size( match self.window.active_window() { - CosmicSurface::X11(s) => s.geometry().loc, + CosmicSurface::X11(s) => s.geometry().loc.as_global(), _ => (0, 0).into(), }, - self.last_window_size, + self.last_window_size.as_global(), )); self.window.configure(); @@ -245,6 +264,14 @@ impl PointerGrab for ResizeSurfaceGrab { } } +pub struct ResizeGrabMarker(AtomicBool); + +impl ResizeGrabMarker { + pub fn get(&self) -> bool { + self.0.load(Ordering::SeqCst) + } +} + impl ResizeSurfaceGrab { pub fn new( start_data: PointerGrabStartData, @@ -252,6 +279,7 @@ impl ResizeSurfaceGrab { edges: ResizeEdge, initial_window_location: Point, initial_window_size: Size, + seat: &Seat, ) -> ResizeSurfaceGrab { let resize_state = ResizeState::Resizing(ResizeData { edges, @@ -260,9 +288,14 @@ impl ResizeSurfaceGrab { }); *mapped.resize_state.lock().unwrap() = Some(resize_state); + seat.user_data() + .get_or_insert::(|| ResizeGrabMarker(AtomicBool::new(true))) + .0 + .store(true, Ordering::SeqCst); ResizeSurfaceGrab { start_data, + seat: seat.clone(), window: mapped, edges, initial_window_size, @@ -271,7 +304,13 @@ impl ResizeSurfaceGrab { } pub fn apply_resize_to_location(window: CosmicMapped, space: &mut Workspace) { - if let Some(location) = space.floating_layer.space.element_location(&window) { + if let Some(location) = space + .floating_layer + .space + .element_location(&window) + .map(PointExt::as_local) + .map(|p| p.to_global(space.output())) + { let mut new_location = None; let mut resize_state = window.resize_state.lock().unwrap(); @@ -316,13 +355,13 @@ impl ResizeSurfaceGrab { CosmicSurface::Wayland(window) => { update_reactive_popups( &window, - new_location + offset, + new_location + offset.as_global(), space.floating_layer.space.outputs(), ); } CosmicSurface::X11(surface) => { let mut geometry = surface.geometry(); - geometry.loc += location - new_location; + geometry.loc += (location - new_location).as_logical(); let _ = surface.configure(geometry); } _ => unreachable!(), @@ -331,7 +370,7 @@ impl ResizeSurfaceGrab { space .floating_layer .space - .map_element(window, new_location, false); + .map_element(window, new_location.as_logical(), false); } } } diff --git a/src/shell/layout/floating/mod.rs b/src/shell/layout/floating/mod.rs index 8595ea9b..75d847d7 100644 --- a/src/shell/layout/floating/mod.rs +++ b/src/shell/layout/floating/mod.rs @@ -11,7 +11,6 @@ use smithay::{ utils::{Logical, Point, Rectangle, Size}, wayland::seat::WaylandFocus, }; -use std::collections::HashMap; use crate::{ backend::render::{element::AsGlowRenderer, IndicatorShader, Key, Usage}, @@ -28,9 +27,7 @@ use crate::{ }, state::State, utils::prelude::*, - wayland::{ - handlers::xdg_shell::popup::get_popup_toplevel, protocols::toplevel_info::ToplevelInfoState, - }, + wayland::handlers::xdg_shell::popup::get_popup_toplevel, }; mod grabs; @@ -42,67 +39,68 @@ pub struct FloatingLayout { } impl FloatingLayout { - pub fn new() -> FloatingLayout { - Default::default() + pub fn new(output: &Output) -> FloatingLayout { + let mut layout = Self::default(); + layout.space.map_output(output, (0, 0)); + layout } - pub fn map_output(&mut self, output: &Output, location: Point) { - self.space.map_output(output, location) - } + pub fn set_output(&mut self, output: &Output) { + let old_output = self.space.outputs().next().unwrap().clone(); + self.space.unmap_output(&old_output); + self.space.map_output(output, (0, 0)); + + /* + TODO: rescale all positions? (evem rescale windows?) + */ - pub fn unmap_output( - &mut self, - output: &Output, - toplevel_info: &mut ToplevelInfoState, - ) { - let windows = self - .space - .elements_for_output(output) - .cloned() - .collect::>(); - for window in &windows { - for (toplevel, _) in window.windows() { - toplevel_info.toplevel_leave_output(&toplevel, output); - } - } - self.space.unmap_output(output); self.refresh(); - for window in &windows { - for output in self.space.outputs_for_element(&window) { - for (toplevel, _) in window.windows() { - toplevel_info.toplevel_enter_output(&toplevel, &output); - } - } - } } pub fn map( &mut self, mapped: impl Into, - seat: &Seat, - position: impl Into>>, + position: impl Into>>, ) { let mapped = mapped.into(); - let output = seat.active_output(); let position = position.into(); - self.map_internal(mapped, &output, position) + self.map_internal(mapped, position, None) + } + + pub fn map_maximized(&mut self, mapped: CosmicMapped) { + let output = self.space.outputs().next().unwrap().clone(); + let layers = layer_map_for_output(&output); + let geometry = layers.non_exclusive_zone().as_local(); + + mapped.set_bounds(geometry.size.as_logical()); + mapped.set_tiled(true); + mapped.set_maximized(true); + mapped.set_geometry(geometry.to_global(&output)); + mapped.configure(); + + self.space + .map_element(mapped, geometry.loc.as_logical(), true); } pub(in crate::shell) fn map_internal( &mut self, mapped: CosmicMapped, - output: &Output, - position: Option>, + position: Option>, + size: Option>, ) { - let mut win_geo = mapped.geometry(); + let mut win_geo = mapped.geometry().as_local(); + let output = self.space.outputs().next().unwrap().clone(); let layers = layer_map_for_output(&output); let geometry = layers.non_exclusive_zone(); mapped.set_bounds(geometry.size); let last_geometry = mapped.last_geometry.lock().unwrap().clone(); - if let Some(size) = last_geometry.map(|g| g.size) { + if let Some(size) = size + .map(SizeExt::as_local) + .or(last_geometry.map(|g| g.size)) + { win_geo.size = size; } else { let (min_size, max_size) = ( @@ -150,30 +148,24 @@ impl FloatingLayout { }); mapped.set_tiled(false); - let offset = output.geometry().loc - - self - .space - .output_geometry(output) - .map(|g| g.loc) - .unwrap_or_default(); - mapped.set_geometry(Rectangle::from_loc_and_size( - position + offset, - win_geo.size, - )); + mapped + .set_geometry(Rectangle::from_loc_and_size(position, win_geo.size).to_global(&output)); mapped.configure(); - self.space.map_element(mapped, position, false); + self.space.map_element(mapped, position.as_logical(), false); } pub fn unmap(&mut self, window: &CosmicMapped) -> bool { - #[allow(irrefutable_let_patterns)] - let is_maximized = window.is_maximized(true); - - if !is_maximized { + if !window.is_maximized(true) || !window.is_fullscreen(true) { if let Some(location) = self.space.element_location(window) { - *window.last_geometry.lock().unwrap() = Some(Rectangle::from_loc_and_size( - location, - window.geometry().size, - )); + *window.last_geometry.lock().unwrap() = Some( + Rectangle::from_loc_and_size( + location, + window + .pending_size() + .unwrap_or_else(|| window.geometry().size), + ) + .as_local(), + ) } } @@ -182,56 +174,8 @@ impl FloatingLayout { was_unmaped } - pub fn element_geometry(&self, elem: &CosmicMapped) -> Option> { - self.space.element_geometry(elem) - } - - pub fn maximize_request(&mut self, window: &CosmicSurface) { - if let Some(mapped) = self - .space - .elements() - .find(|m| m.windows().any(|(w, _)| &w == window)) - { - if let Some(location) = self.space.element_location(mapped) { - *mapped.last_geometry.lock().unwrap() = Some(Rectangle::from_loc_and_size( - location, - mapped.geometry().size, - )); - } - } - } - - pub fn unmaximize_request(&mut self, window: &CosmicSurface) -> Option> { - let maybe_mapped = self - .space - .elements() - .find(|m| m.windows().any(|(w, _)| &w == window)) - .cloned(); - - if let Some(mapped) = maybe_mapped { - let last_geometry = mapped.last_geometry.lock().unwrap().clone(); - let last_size = last_geometry.map(|g| g.size).expect("No previous size?"); - let last_location = last_geometry.map(|g| g.loc).expect("No previous location?"); - let output = self - .space - .output_under(last_location.to_f64()) - .next() - .unwrap_or(self.space.outputs().next().unwrap()); - let offset = output.geometry().loc - - self - .space - .output_geometry(output) - .map(|g| g.loc) - .unwrap_or_default(); - mapped.set_geometry(Rectangle::from_loc_and_size( - last_location + offset, - last_size, - )); - self.space.map_element(mapped, last_location, true); - Some(last_size) - } else { - None - } + pub fn element_geometry(&self, elem: &CosmicMapped) -> Option> { + self.space.element_geometry(elem).map(RectExt::as_local) } pub fn resize_request( @@ -251,6 +195,7 @@ impl FloatingLayout { edges, location, size, + seat, )) } else { None @@ -274,6 +219,9 @@ impl FloatingLayout { else { return false; }; + if mapped.is_maximized(true) { + return false; + } let Some(original_geo) = self.space.element_geometry(mapped) else { return false; // we don't have that window @@ -339,13 +287,10 @@ impl FloatingLayout { })); mapped.set_resizing(true); - mapped.set_geometry(Rectangle::from_loc_and_size( - match mapped.active_window() { - CosmicSurface::X11(s) => s.geometry().loc, - _ => (0, 0).into(), - }, - geo.size, - )); + mapped.set_geometry( + geo.as_local() + .to_global(self.space.outputs().next().unwrap()), + ); mapped.configure(); true @@ -427,6 +372,7 @@ impl FloatingLayout { &mut self, direction: Direction, seat: &Seat, + theme: cosmic::Theme, ) -> MoveResult { let Some(target) = seat.get_keyboard().unwrap().current_focus() else { return MoveResult::None @@ -451,7 +397,7 @@ impl FloatingLayout { match focused.handle_move(direction) { StackMoveResult::Handled => return MoveResult::Done, StackMoveResult::MoveOut(surface, loop_handle) => { - let mapped: CosmicMapped = CosmicWindow::new(surface, loop_handle).into(); + let mapped: CosmicMapped = CosmicWindow::new(surface, loop_handle, theme).into(); let output = seat.active_output(); let pos = self.space.element_geometry(focused).unwrap().loc + match direction { @@ -471,7 +417,7 @@ impl FloatingLayout { }) .then_some(pos); - self.map_internal(mapped.clone(), &output, position); + self.map_internal(mapped.clone(), position.map(PointExt::as_local), None); return MoveResult::ShiftFocus(KeyboardFocusTarget::Element(mapped)); } StackMoveResult::Default => {} @@ -503,87 +449,31 @@ impl FloatingLayout { { // TODO what about windows leaving to the top with no headerbar to drag? can that happen? (Probably if the user is moving outputs down) *element.last_geometry.lock().unwrap() = None; - let output = self.space.outputs().next().unwrap().clone(); - self.map_internal(element, &output, None); - } - } - - pub fn most_overlapped_output_for_element(&self, elem: &CosmicMapped) -> Option { - let elem_geo = self.space.element_geometry(elem)?; - - if self.space.outputs().nth(1).is_none() { - return self.space.outputs().next().cloned(); + self.map_internal(element, None, None); } - - Some( - self.space - .outputs_for_element(elem) - .into_iter() - .max_by_key(|o| { - let output_geo = self.space.output_geometry(o).unwrap(); - if let Some(intersection) = output_geo.intersection(elem_geo) { - intersection.size.w * intersection.size.h - } else { - 0 - } - }) - .unwrap_or(self.space.outputs().next().unwrap().clone()), - ) } pub fn merge(&mut self, other: FloatingLayout) { - let mut output_pos_map = HashMap::new(); - for output in self.space.outputs() { - output_pos_map.insert( - output.clone(), - self.space.output_geometry(output).unwrap().loc - - other - .space - .output_geometry(output) - .map(|geo| geo.loc) - .unwrap_or_else(|| (0, 0).into()), - ); - } for element in other.space.elements() { - let mut elem_geo = other.space.element_geometry(element).unwrap(); - let output = other + let elem_loc = other .space - .outputs_for_element(element) - .into_iter() - .filter(|o| self.space.outputs().any(|o2| o == o2)) - .max_by_key(|o| { - let output_geo = other.space.output_geometry(o).unwrap(); - let intersection = output_geo.intersection(elem_geo).unwrap(); - intersection.size.w * intersection.size.h - }) - .unwrap_or(self.space.outputs().next().unwrap().clone()); - elem_geo.loc += output_pos_map - .get(&output) - .copied() - .unwrap_or_else(|| (0, 0).into()); - let offset = output.geometry().loc - - self - .space - .output_geometry(&output) - .map(|g| g.loc) - .unwrap_or_default(); - element.set_geometry(Rectangle::from_loc_and_size( - elem_geo.loc + offset, - elem_geo.size, - )); - self.space.map_element(element.clone(), elem_geo.loc, false); + .element_geometry(element) + .unwrap() + .loc + .as_local(); + self.map_internal(element.clone(), Some(elem_loc), None); } self.refresh(); //fixup any out of bounds elements } - pub fn render_output( + pub fn render( &self, renderer: &mut R, - output: &Output, focused: Option<&CosmicMapped>, mut resize_indicator: Option<(ResizeMode, ResizeIndicator)>, indicator_thickness: u8, alpha: f32, + theme: &cosmic::theme::CosmicTheme, ) -> ( Vec>, Vec>, @@ -598,68 +488,72 @@ impl FloatingLayout { #[cfg(feature = "debug")] puffin::profile_function!(); + let output = self.space.outputs().next().unwrap(); let output_scale = output.current_scale().fractional_scale(); - let output_geo = self.space.output_geometry(output).unwrap(); let mut window_elements = Vec::new(); let mut popup_elements = Vec::new(); - self.space - .elements_for_output(output) - .rev() - .for_each(|elem| { - let render_location = self.space.element_location(elem).unwrap() - - output_geo.loc - - elem.geometry().loc; - let (w_elements, p_elements) = elem.split_render_elements( - renderer, - render_location.to_physical_precise_round(output_scale), - output_scale.into(), - alpha, - ); - - if focused == Some(elem) { - let mut indicator_geometry = Rectangle::from_loc_and_size( - self.space.element_location(elem).unwrap() - output_geo.loc, - elem.geometry().size, + self.space.elements().rev().for_each(|elem| { + let render_location = self.space.element_location(elem).unwrap() - elem.geometry().loc; + let (w_elements, p_elements) = elem.split_render_elements( + renderer, + render_location.to_physical_precise_round(output_scale), + output_scale.into(), + alpha, + ); + + if focused == Some(elem) && !elem.is_maximized(false) { + let mut indicator_geometry = Rectangle::from_loc_and_size( + self.space.element_location(elem).unwrap(), + elem.geometry().size, + ) + .as_local(); + + if let Some((mode, resize)) = resize_indicator.as_mut() { + indicator_geometry.loc -= (18, 18).into(); + indicator_geometry.size += (36, 36).into(); + resize.resize(indicator_geometry.size.as_logical()); + resize.output_enter(output, Rectangle::default() /* unused */); + window_elements.extend( + resize + .render_elements::>( + renderer, + indicator_geometry + .loc + .as_logical() + .to_physical_precise_round(output_scale), + output_scale.into(), + alpha * mode.alpha().unwrap_or(1.0), + ) + .into_iter() + .map(CosmicMappedRenderElement::Window), ); + } - if let Some((mode, resize)) = resize_indicator.as_mut() { - indicator_geometry.loc -= (18, 18).into(); - indicator_geometry.size += (36, 36).into(); - resize.resize(indicator_geometry.size); - resize.output_enter(output, output_geo); - window_elements.extend( - resize - .render_elements::>( - renderer, - indicator_geometry - .loc - .to_physical_precise_round(output_scale), - output_scale.into(), - alpha * mode.alpha().unwrap_or(1.0), - ) - .into_iter() - .map(CosmicMappedRenderElement::Window), - ); - } - - if indicator_thickness > 0 { - let element = IndicatorShader::focus_element( - renderer, - Key::Window(Usage::FocusIndicator, elem.clone()), - indicator_geometry, - indicator_thickness, - output_scale, - alpha, - ); - window_elements.push(element.into()); - } + let active_window_hint = crate::theme::active_window_hint(theme); + + if indicator_thickness > 0 { + let element = IndicatorShader::focus_element( + renderer, + Key::Window(Usage::FocusIndicator, elem.clone()), + indicator_geometry, + indicator_thickness, + output_scale, + alpha, + [ + active_window_hint.red, + active_window_hint.green, + active_window_hint.blue, + ], + ); + window_elements.push(element.into()); } + } - window_elements.extend(w_elements); - popup_elements.extend(p_elements); - }); + window_elements.extend(w_elements); + popup_elements.extend(p_elements); + }); (window_elements, popup_elements) } diff --git a/src/shell/layout/tiling/grabs/resize.rs b/src/shell/layout/tiling/grabs/resize.rs index b78a9837..aaf7c933 100644 --- a/src/shell/layout/tiling/grabs/resize.rs +++ b/src/shell/layout/tiling/grabs/resize.rs @@ -5,7 +5,7 @@ use crate::{ shell::{focus::target::PointerFocusTarget, layout::Orientation}, utils::prelude::*, }; -use id_tree::NodeId; +use id_tree::{NodeId, Tree}; use smithay::{ backend::input::ButtonState, input::{ @@ -80,6 +80,8 @@ impl PointerTarget for ResizeForkTarget { button, location, }, + old_tree: None, + accumulated_delta: 0.0, last_loc: location, node, output, @@ -116,6 +118,8 @@ impl PointerTarget for ResizeForkTarget { pub struct ResizeForkGrab { start_data: PointerGrabStartData, last_loc: Point, + old_tree: Option>, + accumulated_delta: f64, node: NodeId, output: WeakOutput, left_up_idx: usize, @@ -137,67 +141,104 @@ impl PointerGrab for ResizeForkGrab { if let Some(output) = self.output.upgrade() { let tiling_layer = &mut data.common.shell.active_space_mut(&output).tiling_layer; - if let Some(queue) = tiling_layer.queues.get_mut(&output) { - let tree = &mut queue.trees.back_mut().unwrap().0; - if tree.get(&self.node).is_ok() { - let delta = match self.orientation { - Orientation::Vertical => delta.x, - Orientation::Horizontal => delta.y, - } - .round() as i32; - - // check that we are still alive - let mut iter = tree - .children_ids(&self.node) - .unwrap() - .skip(self.left_up_idx); - let first_elem = iter.next(); - let second_elem = iter.next(); - if first_elem.is_none() || second_elem.is_none() { - return handle.unset_grab(data, event.serial, event.time); - }; - - match tree.get_mut(&self.node).unwrap().data_mut() { - Data::Group { - sizes, orientation, .. - } => { - if sizes[self.left_up_idx] + sizes[self.left_up_idx + 1] - < match orientation { - Orientation::Vertical => 720, - Orientation::Horizontal => 480, - } - { - return; - }; - - let old_size = sizes[self.left_up_idx]; - sizes[self.left_up_idx] = (old_size + delta).max( - if self.orientation == Orientation::Vertical { - 360 - } else { - 240 - }, - ); - let diff = old_size - sizes[self.left_up_idx]; - let next_size = sizes[self.left_up_idx + 1] + diff; - sizes[self.left_up_idx + 1] = - next_size.max(if self.orientation == Orientation::Vertical { - 360 - } else { - 240 - }); - let next_diff = next_size - sizes[self.left_up_idx + 1]; - sizes[self.left_up_idx] += next_diff; + let gaps = tiling_layer.gaps(); + + let tree = &mut tiling_layer.queue.trees.back_mut().unwrap().0; + match &mut self.old_tree { + Some(old_tree) => { + // it would be so nice to just `zip` here, but `zip` just returns `None` once either returns `None`. + let mut iter_a = old_tree + .root_node_id() + .into_iter() + .flat_map(|root_id| old_tree.traverse_pre_order_ids(root_id).unwrap()); + let mut iter_b = tree + .root_node_id() + .into_iter() + .flat_map(|root_id| tree.traverse_pre_order_ids(root_id).unwrap()); + + // so lets do it manually + let mut equal = true; + let mut a = iter_a.next(); + let mut b = iter_b.next(); + while a.is_some() || b.is_some() { + equal = a == b; + if !equal { + break; } - _ => unreachable!(), + a = iter_a.next(); + b = iter_b.next(); } - self.last_loc = event.location; - let blocker = TilingLayout::update_positions(&output, tree, tiling_layer.gaps); - tiling_layer.pending_blockers.extend(blocker); - } else { - handle.unset_grab(data, event.serial, event.time); + if !equal { + *old_tree = tree.copy_clone(); + self.accumulated_delta = 0.0; + } else { + *tree = old_tree.copy_clone(); + } + } + x @ None => { + *x = Some(tree.copy_clone()); + } + }; + if tree.get(&self.node).is_ok() { + let delta = match self.orientation { + Orientation::Vertical => delta.x, + Orientation::Horizontal => delta.y, + } + .round(); + self.accumulated_delta += delta; + + // check that we are still alive + let mut iter = tree + .children_ids(&self.node) + .unwrap() + .skip(self.left_up_idx); + let first_elem = iter.next(); + let second_elem = iter.next(); + if first_elem.is_none() || second_elem.is_none() { + return handle.unset_grab(data, event.serial, event.time, true); + }; + + match tree.get_mut(&self.node).unwrap().data_mut() { + Data::Group { + sizes, orientation, .. + } => { + if sizes[self.left_up_idx] + sizes[self.left_up_idx + 1] + < match orientation { + Orientation::Vertical => 720, + Orientation::Horizontal => 480, + } + { + return; + }; + + let old_size = sizes[self.left_up_idx]; + sizes[self.left_up_idx] = (old_size + + self.accumulated_delta.round() as i32) + .max(if self.orientation == Orientation::Vertical { + 360 + } else { + 240 + }); + let diff = old_size - sizes[self.left_up_idx]; + let next_size = sizes[self.left_up_idx + 1] + diff; + sizes[self.left_up_idx + 1] = + next_size.max(if self.orientation == Orientation::Vertical { + 360 + } else { + 240 + }); + let next_diff = next_size - sizes[self.left_up_idx + 1]; + sizes[self.left_up_idx] += next_diff; + } + _ => unreachable!(), } + + self.last_loc = event.location; + let blocker = TilingLayout::update_positions(&output, tree, gaps); + tiling_layer.pending_blockers.extend(blocker); + } else { + handle.unset_grab(data, event.serial, event.time, true); } } } @@ -222,7 +263,7 @@ impl PointerGrab for ResizeForkGrab { handle.button(data, event); if handle.current_pressed().is_empty() { // No more buttons are pressed, release the grab. - handle.unset_grab(data, event.serial, event.time); + handle.unset_grab(data, event.serial, event.time, true); } } diff --git a/src/shell/layout/tiling/grabs/swap.rs b/src/shell/layout/tiling/grabs/swap.rs index 733d921f..25a4748a 100644 --- a/src/shell/layout/tiling/grabs/swap.rs +++ b/src/shell/layout/tiling/grabs/swap.rs @@ -39,8 +39,7 @@ impl KeyboardGrab for SwapWindowGrab { serial: Serial, time: u32, ) { - if self.desc.output.upgrade().is_none() - || !matches!(&data.common.shell.overview_mode, OverviewMode::Started(Trigger::KeyboardSwap(_, d), _) if d == &self.desc) + if !matches!(&data.common.shell.overview_mode, OverviewMode::Started(Trigger::KeyboardSwap(_, d), _) if d == &self.desc) { handle.unset_grab(data, serial, false); return; diff --git a/src/shell/layout/tiling/mod.rs b/src/shell/layout/tiling/mod.rs index 7e4c4d78..7af788bd 100644 --- a/src/shell/layout/tiling/mod.rs +++ b/src/shell/layout/tiling/mod.rs @@ -1,10 +1,7 @@ // SPDX-License-Identifier: GPL-3.0-only use crate::{ - backend::render::{ - element::AsGlowRenderer, BackdropShader, IndicatorShader, Key, Usage, ACTIVE_GROUP_COLOR, - GROUP_COLOR, - }, + backend::render::{element::AsGlowRenderer, BackdropShader, IndicatorShader, Key, Usage}, shell::{ element::{ resize_indicator::ResizeIndicator, @@ -25,6 +22,7 @@ use crate::{ CosmicSurface, Direction, FocusResult, MoveResult, OutputNotMapped, OverviewMode, ResizeDirection, ResizeMode, Trigger, }, + theme::group_color, utils::{prelude::*, tween::EaseRectangle}, wayland::{ handlers::xdg_shell::popup::get_popup_toplevel, @@ -48,15 +46,13 @@ use smithay::{ }, desktop::{layer_map_for_output, space::SpaceElement, PopupKind}, input::Seat, - output::{Output, WeakOutput}, + output::Output, reexports::wayland_server::Client, utils::{IsAlive, Logical, Point, Rectangle, Scale, Size}, wayland::{compositor::add_blocker, seat::WaylandFocus}, }; use std::{ - borrow::Borrow, collections::{HashMap, VecDeque}, - hash::Hash, sync::{Arc, Weak}, time::{Duration, Instant}, }; @@ -72,42 +68,9 @@ pub const ANIMATION_DURATION: Duration = Duration::from_millis(200); pub const MOUSE_ANIMATION_DELAY: Duration = Duration::from_millis(150); pub const INITIAL_MOUSE_ANIMATION_DELAY: Duration = Duration::from_millis(500); -#[derive(Debug, Clone)] -struct OutputData { - output: Output, - location: Point, -} - -impl Borrow for OutputData { - fn borrow(&self) -> &Output { - &self.output - } -} - -impl PartialEq for OutputData { - fn eq(&self, other: &Self) -> bool { - self.output == other.output - } -} - -impl Eq for OutputData {} - -impl PartialEq for OutputData { - fn eq(&self, other: &Output) -> bool { - &self.output == other - } -} - -impl Hash for OutputData { - fn hash(&self, state: &mut H) { - self.output.hash(state) - } -} - #[derive(Debug, Clone, PartialEq)] pub struct NodeDesc { pub handle: WorkspaceHandle, - pub output: WeakOutput, pub node: NodeId, pub stack_window: Option, } @@ -116,7 +79,7 @@ pub struct NodeDesc { enum TargetZone { Initial, InitialPlaceholder(NodeId), - WindowStack(NodeId, Rectangle), + WindowStack(NodeId, Rectangle), WindowSplit(NodeId, Direction), GroupEdge(NodeId, Direction), GroupInterior(NodeId, usize), @@ -151,13 +114,13 @@ impl TreeQueue { #[derive(Debug, Clone)] pub struct TilingLayout { - gaps: (i32, i32), - queues: HashMap, - standby_tree: Option>, + output: Output, + queue: TreeQueue, pending_blockers: Vec, placeholder_id: Id, swapping_stack_surface_id: Id, last_overview_hover: Option<(Option, TargetZone)>, + pub theme: cosmic::Theme, } #[derive(Debug, Clone, PartialEq)] @@ -171,22 +134,22 @@ pub enum Data { Group { orientation: Orientation, sizes: Vec, - last_geometry: Rectangle, + last_geometry: Rectangle, alive: Arc<()>, pill_indicator: Option, }, Mapped { mapped: CosmicMapped, - last_geometry: Rectangle, + last_geometry: Rectangle, }, Placeholder { - last_geometry: Rectangle, + last_geometry: Rectangle, initial_placeholder: bool, }, } impl Data { - fn new_group(orientation: Orientation, geo: Rectangle) -> Data { + fn new_group(orientation: Orientation, geo: Rectangle) -> Data { Data::Group { orientation, sizes: vec![ @@ -296,7 +259,7 @@ impl Data { } } - fn geometry(&self) -> &Rectangle { + fn geometry(&self) -> &Rectangle { match self { Data::Group { last_geometry, .. } => last_geometry, Data::Mapped { last_geometry, .. } => last_geometry, @@ -304,7 +267,7 @@ impl Data { } } - fn update_geometry(&mut self, geo: Rectangle) { + fn update_geometry(&mut self, geo: Rectangle) { match self { Data::Group { orientation, @@ -326,8 +289,11 @@ impl Data { .round() as i32; }); let sum: i32 = sizes.iter().sum(); - if sum < new_length { - *sizes.last_mut().unwrap() += new_length - sum; + + // fix rounding issues + if sum != new_length { + let diff = new_length - sum; + *sizes.last_mut().unwrap() += diff; } *last_geometry = geo; } @@ -352,129 +318,73 @@ enum FocusedNodeData { } impl TilingLayout { - pub fn new(gaps: (u8, u8)) -> TilingLayout { + pub fn new(theme: cosmic::Theme, output: &Output) -> TilingLayout { TilingLayout { - gaps: (gaps.0 as i32, gaps.1 as i32), - queues: HashMap::new(), - standby_tree: None, + queue: TreeQueue { + trees: { + let mut queue = VecDeque::new(); + queue.push_back((Tree::new(), Duration::ZERO, None)); + queue + }, + animation_start: None, + }, + output: output.clone(), pending_blockers: Vec::new(), placeholder_id: Id::new(), swapping_stack_surface_id: Id::new(), last_overview_hover: None, + theme, } } - pub fn map_output(&mut self, output: &Output, location: Point) { - if !self.queues.contains_key(output) { - self.queues.insert( - OutputData { - output: output.clone(), - location, - }, - TreeQueue { - trees: { - let mut queue = VecDeque::new(); - queue.push_back(( - self.standby_tree.take().unwrap_or_else(Tree::new), - Duration::ZERO, - None, - )); - queue - }, - animation_start: None, - }, - ); - } else { - let tree = self.queues.remove(output).unwrap(); - self.queues.insert( - OutputData { - output: output.clone(), - location, - }, - tree, - ); - } - } - - pub fn unmap_output( - &mut self, - output: &Output, - toplevel_info: &mut ToplevelInfoState, - ) { - if let Some(mut src) = self.queues.remove(output) { - // Operate on last pending tree & unblock queue - for blocker in src - .trees - .iter_mut() - .flat_map(|(_, _, blocker)| blocker.take()) - { - self.pending_blockers.push(blocker); - } - let (src, _, _) = src.trees.pop_back().expect("No tree in queue"); - - let Some((new_output, dst_queue)) = self.queues.iter_mut().next() else { - self.standby_tree = Some(src); - return; - }; + pub fn set_output(&mut self, output: &Output) { + let gaps = self.gaps(); + let mut tree = self.queue.trees.back().unwrap().0.copy_clone(); - let mut dst = dst_queue.trees.back().unwrap().0.copy_clone(); - let orientation = match new_output.output.geometry().size { - x if x.w >= x.h => Orientation::Vertical, - _ => Orientation::Horizontal, - }; - for node in src - .root_node_id() - .and_then(|root_id| src.traverse_pre_order(root_id).ok()) - .into_iter() - .flatten() + for node in tree + .root_node_id() + .and_then(|root_id| tree.traverse_pre_order(root_id).ok()) + .into_iter() + .flatten() + { + if let Data::Mapped { + mapped, + last_geometry: _, + } = node.data() { - if let Data::Mapped { - mapped, - last_geometry: _, - } = node.data() - { - for (toplevel, _) in mapped.windows() { - toplevel_info.toplevel_leave_output(&toplevel, output); - toplevel_info.toplevel_enter_output(&toplevel, &new_output.output); - } - mapped.output_leave(output); - mapped.output_enter(&new_output.output, mapped.bbox()); - } + mapped.output_leave(&self.output); + mapped.output_enter(output, mapped.bbox()); } - TilingLayout::merge_trees(src, &mut dst, orientation); - - let blocker = TilingLayout::update_positions(output, &mut dst, self.gaps); - dst_queue.push_tree(dst, ANIMATION_DURATION, blocker); } + + let blocker = TilingLayout::update_positions(output, &mut tree, gaps); + self.queue.push_tree(tree, None, blocker); + self.output = output.clone(); } pub fn map<'a>( &mut self, window: CosmicMapped, - seat: &Seat, - focus_stack: impl Iterator + 'a, + focus_stack: Option + 'a>, direction: Option, ) { - let output = seat.active_output(); - window.output_enter(&output, window.bbox()); - window.set_bounds(output.geometry().size); - self.map_internal(window, &output, Some(focus_stack), direction); + window.output_enter(&self.output, window.bbox()); + window.set_bounds(self.output.geometry().size.as_logical()); + self.map_internal(window, focus_stack, direction); } - fn map_internal<'a>( + pub fn map_internal<'a>( &mut self, window: impl Into, - output: &Output, focus_stack: Option + 'a>, direction: Option, ) { - let queue = self.queues.get_mut(output).expect("Output not mapped?"); - let mut tree = queue.trees.back().unwrap().0.copy_clone(); - - TilingLayout::map_to_tree(&mut tree, window, output, focus_stack, direction); + let gaps = self.gaps(); - let blocker = TilingLayout::update_positions(output, &mut tree, self.gaps); - queue.push_tree(tree, ANIMATION_DURATION, blocker); + let mut tree = self.queue.trees.back().unwrap().0.copy_clone(); + TilingLayout::map_to_tree(&mut tree, window, &self.output, focus_stack, direction); + let blocker = TilingLayout::update_positions(&self.output, &mut tree, gaps); + self.queue.push_tree(tree, ANIMATION_DURATION, blocker); } fn map_to_tree<'a>( @@ -560,49 +470,42 @@ impl TilingLayout { } pub fn replace_window(&mut self, old: &CosmicMapped, new: &CosmicMapped) { + let gaps = self.gaps(); let Some(old_id) = old.tiling_node_id.lock().unwrap().clone() else { return }; - let Some((output_data, queue)) = self.queues.iter_mut().find(|(_, queue)| queue.trees.back().unwrap().0.get(&old_id).is_ok()) else { return }; - let mut tree = queue.trees.back().unwrap().0.copy_clone(); + let mut tree = self.queue.trees.back().unwrap().0.copy_clone(); - let node = tree.get_mut(&old_id).unwrap(); - let data = node.data_mut(); - match data { - Data::Mapped { mapped, .. } => { - assert_eq!(mapped, old); - *mapped = new.clone(); - *new.tiling_node_id.lock().unwrap() = Some(old_id); - *old.tiling_node_id.lock().unwrap() = None; + if let Ok(node) = tree.get_mut(&old_id) { + let data = node.data_mut(); + match data { + Data::Mapped { mapped, .. } => { + assert_eq!(mapped, old); + *mapped = new.clone(); + *new.tiling_node_id.lock().unwrap() = Some(old_id); + *old.tiling_node_id.lock().unwrap() = None; + } + _ => unreachable!("Tiling id points to group"), } - _ => unreachable!("Tiling id points to group"), - } - old.output_leave(&output_data.output); - new.output_enter(&output_data.output, new.bbox()); + old.output_leave(&self.output); + new.output_enter(&self.output, new.bbox()); - let blocker = TilingLayout::update_positions(&output_data.output, &mut tree, self.gaps); - queue.push_tree(tree, ANIMATION_DURATION, blocker); + let blocker = TilingLayout::update_positions(&self.output, &mut tree, gaps); + self.queue.push_tree(tree, ANIMATION_DURATION, blocker); + } } pub fn move_tree<'a>( this: &mut Self, other: &mut Self, - other_output: &Output, other_handle: &WorkspaceHandle, seat: &Seat, focus_stack: impl Iterator + 'a, desc: NodeDesc, toplevel_info_state: &mut ToplevelInfoState, ) -> Option { - let this_output = desc.output.upgrade()?; let this_handle = &desc.handle; - let mut this_tree = this - .queues - .get(&this_output) - .map(|queue| queue.trees.back().unwrap().0.copy_clone())?; - let mut other_tree = other - .queues - .get(other_output) - .map(|queue| queue.trees.back().unwrap().0.copy_clone())?; + let mut this_tree = this.queue.trees.back().unwrap().0.copy_clone(); + let mut other_tree = other.queue.trees.back().unwrap().0.copy_clone(); match desc.stack_window { Some(stack_surface) => { @@ -615,13 +518,14 @@ impl TilingLayout { } let mapped: CosmicMapped = - CosmicWindow::new(stack_surface, this_stack.loop_handle()).into(); - if &this_output != other_output { - mapped.output_leave(&this_output); - mapped.output_enter(other_output, mapped.bbox()); + CosmicWindow::new(stack_surface, this_stack.loop_handle(), this.theme.clone()) + .into(); + if this.output != other.output { + mapped.output_leave(&this.output); + mapped.output_enter(&other.output, mapped.bbox()); for (ref surface, _) in mapped.windows() { - toplevel_info_state.toplevel_leave_output(surface, &this_output); - toplevel_info_state.toplevel_enter_output(surface, other_output); + toplevel_info_state.toplevel_leave_output(surface, &this.output); + toplevel_info_state.toplevel_enter_output(surface, &other.output); } } for (ref surface, _) in mapped.windows() { @@ -630,7 +534,7 @@ impl TilingLayout { } mapped.set_tiled(true); - other.map(mapped.clone(), seat, focus_stack, None); + other.map(mapped.clone(), Some(focus_stack), None); return Some(KeyboardFocusTarget::Element(mapped)); } None => { @@ -651,11 +555,7 @@ impl TilingLayout { .unwrap() .current_focus() .and_then(|target| { - TilingLayout::currently_focused_node( - &other_tree, - &other_output, - target, - ) + TilingLayout::currently_focused_node(&other_tree, target) }) .map(|(id, _)| id) .unwrap_or(other_tree.root_node_id().unwrap().clone()); @@ -683,12 +583,12 @@ impl TilingLayout { *parent_id = id.clone(); } if let Data::Mapped { mapped, .. } = other_tree.get_mut(&id).unwrap().data_mut() { - if &this_output != other_output { - mapped.output_leave(&this_output); - mapped.output_enter(other_output, mapped.bbox()); + if this.output != other.output { + mapped.output_leave(&this.output); + mapped.output_enter(&other.output, mapped.bbox()); for (ref surface, _) in mapped.windows() { - toplevel_info_state.toplevel_leave_output(surface, &this_output); - toplevel_info_state.toplevel_enter_output(surface, other_output); + toplevel_info_state.toplevel_leave_output(surface, &this.output); + toplevel_info_state.toplevel_enter_output(surface, &other.output); } } for (ref surface, _) in mapped.windows() { @@ -717,14 +617,14 @@ impl TilingLayout { .insert(child_node, InsertBehavior::UnderNode(&parent_id)) .unwrap(); if let Some(mapped) = maybe_mapped { - if &this_output != other_output { - mapped.output_leave(&this_output); - mapped.output_enter(other_output, mapped.bbox()); + if this.output != other.output { + mapped.output_leave(&this.output); + mapped.output_enter(&other.output, mapped.bbox()); for (ref surface, _) in mapped.windows() { toplevel_info_state - .toplevel_leave_output(surface, &this_output); + .toplevel_leave_output(surface, &this.output); toplevel_info_state - .toplevel_enter_output(surface, other_output); + .toplevel_enter_output(surface, &other.output); } } for (ref surface, _) in mapped.windows() { @@ -741,20 +641,21 @@ impl TilingLayout { ); } } + let this_gaps = this.gaps(); + let other_gaps = other.gaps(); - let this_queue = this.queues.get_mut(&this_output).unwrap(); let blocker = - TilingLayout::update_positions(&this_output, &mut this_tree, this.gaps); - this_queue.push_tree(this_tree, ANIMATION_DURATION, blocker); + TilingLayout::update_positions(&this.output, &mut this_tree, this_gaps); + this.queue.push_tree(this_tree, ANIMATION_DURATION, blocker); - let other_queue = other.queues.get_mut(other_output).unwrap(); let blocker = - TilingLayout::update_positions(&other_output, &mut other_tree, other.gaps); - other_queue.push_tree(other_tree, ANIMATION_DURATION, blocker); + TilingLayout::update_positions(&other.output, &mut other_tree, other_gaps); + other + .queue + .push_tree(other_tree, ANIMATION_DURATION, blocker); other.node_desc_to_focus(&NodeDesc { handle: other_handle.clone(), - output: other_output.downgrade(), node: id, stack_window: None, }) @@ -769,10 +670,11 @@ impl TilingLayout { other_desc: &NodeDesc, toplevel_info_state: &mut ToplevelInfoState, ) -> Option { - let this_output = this_desc.output.upgrade()?; - let other_output = other_desc.output.upgrade()?; - - if this_output == other_output + let other_output = other + .as_ref() + .map(|other| other.output.clone()) + .unwrap_or(this.output.clone()); + if this.output == other_output && this_desc.handle == other_desc.handle && this_desc.node == other_desc.node && this_desc.stack_window.is_some() != other_desc.stack_window.is_some() @@ -780,24 +682,12 @@ impl TilingLayout { return None; } - let mut this_tree = this - .queues - .get(&this_output) - .map(|queue| queue.trees.back().unwrap().0.copy_clone())?; + let mut this_tree = this.queue.trees.back().unwrap().0.copy_clone(); let mut other_tree = match other.as_mut() { - Some(other) => Some( - other - .queues - .get(&other_output) - .map(|queue| queue.trees.back().unwrap().0.copy_clone())?, - ), + Some(other) => Some(other.queue.trees.back().unwrap().0.copy_clone()), None => { - if this_output != other_output { - Some( - this.queues - .get(&other_output) - .map(|queue| queue.trees.back().unwrap().0.copy_clone())?, - ) + if this.output != other_output { + Some(this.queue.trees.back().unwrap().0.copy_clone()) } else { None } @@ -807,42 +697,13 @@ impl TilingLayout { match (&this_desc.stack_window, &other_desc.stack_window) { (None, None) if other_tree.is_none() => { if this_desc.node != other_desc.node { - match this_tree.swap_nodes( - &this_desc.node, - &other_desc.node, - id_tree::SwapBehavior::TakeChildren, - ) { - Ok(_) => { - if this_output != other_output { - for node in this_tree.traverse_pre_order(&this_desc.node).unwrap() { - if let Data::Mapped { mapped, .. } = node.data() { - mapped.output_leave(&this_output); - mapped.output_enter(&other_output, mapped.bbox()); - for (ref surface, _) in mapped.windows() { - toplevel_info_state - .toplevel_leave_output(surface, &this_output); - toplevel_info_state - .toplevel_enter_output(surface, &other_output); - } - } - } - for node in this_tree.traverse_pre_order(&other_desc.node).unwrap() - { - if let Data::Mapped { mapped, .. } = node.data() { - mapped.output_leave(&other_output); - mapped.output_enter(&this_output, mapped.bbox()); - for (ref surface, _) in mapped.windows() { - toplevel_info_state - .toplevel_leave_output(surface, &other_output); - toplevel_info_state - .toplevel_enter_output(surface, &this_output); - } - } - } - } - } - Err(_) => return None, // Invalid node-descs, nothing to do here - } + this_tree + .swap_nodes( + &this_desc.node, + &other_desc.node, + id_tree::SwapBehavior::TakeChildren, + ) + .unwrap(); } } (None, None) => { @@ -855,12 +716,12 @@ impl TilingLayout { other_node.replace_data(this_node.replace_data(other_data)); if let Data::Mapped { mapped, .. } = this_node.data_mut() { - if this_output != other_output { + if this.output != other_output { mapped.output_leave(&other_output); - mapped.output_enter(&this_output, mapped.bbox()); + mapped.output_enter(&this.output, mapped.bbox()); for (ref surface, _) in mapped.windows() { toplevel_info_state.toplevel_leave_output(surface, &other_output); - toplevel_info_state.toplevel_enter_output(surface, &this_output); + toplevel_info_state.toplevel_enter_output(surface, &this.output); } } for (ref surface, _) in mapped.windows() { @@ -870,11 +731,11 @@ impl TilingLayout { *mapped.tiling_node_id.lock().unwrap() = Some(this_desc.node.clone()); } if let Data::Mapped { mapped, .. } = other_node.data_mut() { - if this_output != other_output { - mapped.output_leave(&this_output); + if this.output != other_output { + mapped.output_leave(&this.output); mapped.output_enter(&other_output, mapped.bbox()); for (ref surface, _) in mapped.windows() { - toplevel_info_state.toplevel_leave_output(surface, &this_output); + toplevel_info_state.toplevel_leave_output(surface, &this.output); toplevel_info_state.toplevel_enter_output(surface, &other_output); } } @@ -909,12 +770,12 @@ impl TilingLayout { .insert(child_node, InsertBehavior::UnderNode(&parent_id)) .unwrap(); if let Some(mapped) = maybe_mapped { - if this_output != other_output { - mapped.output_leave(&this_output); + if this.output != other_output { + mapped.output_leave(&this.output); mapped.output_enter(&other_output, mapped.bbox()); for (ref surface, _) in mapped.windows() { toplevel_info_state - .toplevel_leave_output(surface, &this_output); + .toplevel_leave_output(surface, &this.output); toplevel_info_state .toplevel_enter_output(surface, &other_output); } @@ -959,14 +820,14 @@ impl TilingLayout { .insert(child_node, InsertBehavior::UnderNode(&parent_id)) .unwrap(); if let Some(mapped) = maybe_mapped { - if this_output != other_output { + if this.output != other_output { mapped.output_leave(&other_output); - mapped.output_enter(&this_output, mapped.bbox()); + mapped.output_enter(&this.output, mapped.bbox()); for (ref surface, _) in mapped.windows() { toplevel_info_state .toplevel_leave_output(surface, &other_output); toplevel_info_state - .toplevel_enter_output(surface, &this_output); + .toplevel_enter_output(surface, &this.output); } } for (ref surface, _) in mapped.windows() { @@ -1024,11 +885,11 @@ impl TilingLayout { .position(|s| &s == this_surface) .unwrap(); for (i, surface) in surfaces.into_iter().enumerate() { - if this_output != other_output { + if this.output != other_output { surface.output_leave(&other_output); - surface.output_enter(&this_output, surface.bbox()); + surface.output_enter(&this.output, surface.bbox()); toplevel_info_state.toplevel_leave_output(&surface, &other_output); - toplevel_info_state.toplevel_enter_output(&surface, &this_output); + toplevel_info_state.toplevel_enter_output(&surface, &this.output); } if this_desc.handle != other_desc.handle { toplevel_info_state.toplevel_leave_workspace(&surface, &other_desc.handle); @@ -1036,9 +897,9 @@ impl TilingLayout { } this_stack.add_window(surface, Some(this_idx + i)); } - if this_output != other_output { - this_surface.output_leave(&this_output); - toplevel_info_state.toplevel_leave_output(&this_surface, &this_output); + if this.output != other_output { + this_surface.output_leave(&this.output); + toplevel_info_state.toplevel_leave_output(&this_surface, &this.output); toplevel_info_state.toplevel_enter_output(&this_surface, &other_output); } if this_desc.handle != other_desc.handle { @@ -1047,11 +908,15 @@ impl TilingLayout { } this_stack.remove_window(&this_surface); - let mapped: CosmicMapped = - CosmicWindow::new(this_surface.clone(), this_stack.loop_handle()).into(); + let mapped: CosmicMapped = CosmicWindow::new( + this_surface.clone(), + this_stack.loop_handle(), + this.theme.clone(), + ) + .into(); mapped.set_tiled(true); mapped.refresh(); - if this_output != other_output { + if this.output != other_output { mapped.output_enter(&other_output, mapped.bbox()); } @@ -1100,10 +965,10 @@ impl TilingLayout { .position(|s| &s == other_surface) .unwrap(); for (i, surface) in surfaces.into_iter().enumerate() { - if this_output != other_output { - surface.output_leave(&this_output); + if this.output != other_output { + surface.output_leave(&this.output); surface.output_enter(&other_output, surface.bbox()); - toplevel_info_state.toplevel_leave_output(&surface, &this_output); + toplevel_info_state.toplevel_leave_output(&surface, &this.output); toplevel_info_state.toplevel_enter_output(&surface, &other_output); } if this_desc.handle != other_desc.handle { @@ -1112,10 +977,10 @@ impl TilingLayout { } other_stack.add_window(surface, Some(other_idx + i)); } - if this_output != other_output { + if this.output != other_output { other_surface.output_leave(&other_output); toplevel_info_state.toplevel_leave_output(&other_surface, &other_output); - toplevel_info_state.toplevel_enter_output(&other_surface, &this_output); + toplevel_info_state.toplevel_enter_output(&other_surface, &this.output); } if this_desc.handle != other_desc.handle { toplevel_info_state @@ -1124,12 +989,16 @@ impl TilingLayout { } other_stack.remove_window(&other_surface); - let mapped: CosmicMapped = - CosmicWindow::new(other_surface.clone(), other_stack.loop_handle()).into(); + let mapped: CosmicMapped = CosmicWindow::new( + other_surface.clone(), + other_stack.loop_handle(), + this.theme.clone(), + ) + .into(); mapped.set_tiled(true); mapped.refresh(); - if this_output != other_output { - mapped.output_enter(&this_output, mapped.bbox()); + if this.output != other_output { + mapped.output_enter(&this.output, mapped.bbox()); } *mapped.tiling_node_id.lock().unwrap() = Some(this_desc.node.clone()); @@ -1181,11 +1050,11 @@ impl TilingLayout { this_stack.remove_window(&this_surface); other_stack.add_window(this_surface.clone(), Some(other_idx)); - if this_output != other_output { - toplevel_info_state.toplevel_leave_output(&this_surface, &this_output); + if this.output != other_output { + toplevel_info_state.toplevel_leave_output(&this_surface, &this.output); toplevel_info_state.toplevel_leave_output(&other_surface, &other_output); toplevel_info_state.toplevel_enter_output(&this_surface, &other_output); - toplevel_info_state.toplevel_enter_output(&other_surface, &this_output); + toplevel_info_state.toplevel_enter_output(&other_surface, &this.output); } if this_desc.handle != other_desc.handle { toplevel_info_state.toplevel_leave_workspace(&this_surface, &this_desc.handle); @@ -1210,16 +1079,17 @@ impl TilingLayout { } } - let this_queue = this.queues.get_mut(&this_output).unwrap(); - let blocker = TilingLayout::update_positions(&this_output, &mut this_tree, this.gaps); - this_queue.push_tree(this_tree, ANIMATION_DURATION, blocker); + let this_gaps = this.gaps(); + let blocker = TilingLayout::update_positions(&this.output, &mut this_tree, this_gaps); + this.queue.push_tree(this_tree, ANIMATION_DURATION, blocker); let has_other_tree = other_tree.is_some(); if let Some(mut other_tree) = other_tree { let (other_queue, gaps) = if let Some(other) = other.as_mut() { - (other.queues.get_mut(&other_output).unwrap(), other.gaps) + let other_gaps = other.gaps(); + (&mut other.queue, other_gaps) } else { - (this.queues.get_mut(&other_output).unwrap(), this.gaps) + (&mut this.queue, this_gaps) }; let blocker = TilingLayout::update_positions(&other_output, &mut other_tree, gaps); other_queue.push_tree(other_tree, ANIMATION_DURATION, blocker); @@ -1236,15 +1106,12 @@ impl TilingLayout { } pub fn node_desc_to_focus(&self, desc: &NodeDesc) -> Option { - let output = desc.output.upgrade()?; - let queue = self.queues.get(&output)?; - let tree = &queue.trees.back().unwrap().0; + let tree = &self.queue.trees.back().unwrap().0; let data = tree.get(&desc.node).ok()?.data(); match data { Data::Mapped { mapped, .. } => Some(KeyboardFocusTarget::Element(mapped.clone())), Data::Group { alive, .. } => Some(KeyboardFocusTarget::Group(WindowGroup { node: desc.node.clone(), - output: desc.output.clone(), alive: Arc::downgrade(alive), focus_stack: tree .children_ids(&desc.node) @@ -1256,60 +1123,26 @@ impl TilingLayout { } } - pub fn tree_for_output(&self, output: &Output) -> Option<&Tree> { - self.queues - .get(output) - .and_then(|queue| queue.trees.back()) - .map(|tree| &tree.0) + pub fn tree(&self) -> &Tree { + &self.queue.trees.back().unwrap().0 } - pub fn unmap(&mut self, window: &CosmicMapped) -> Option { - let output = { - let node_id = window.tiling_node_id.lock().unwrap().clone()?; - self.queues - .iter() - .find(|(_, queue)| { - queue - .trees - .back() - .unwrap() - .0 - .get(&node_id) - .map(|node| node.data().is_mapped(Some(window))) - .unwrap_or(false) - }) - .map(|(o, _)| o.output.clone())? - }; - - self.unmap_window_internal(window); - - window.output_leave(&output); - window.set_tiled(false); - Some(output) + pub fn unmap(&mut self, window: &CosmicMapped) -> bool { + if self.unmap_window_internal(window) { + window.output_leave(&self.output); + window.set_tiled(false); + *window.tiling_node_id.lock().unwrap() = None; + true + } else { + false + } } - pub fn unmap_as_placeholder(&mut self, window: &CosmicMapped) -> Option<(Output, NodeId)> { + pub fn unmap_as_placeholder(&mut self, window: &CosmicMapped) -> Option { let node_id = window.tiling_node_id.lock().unwrap().clone()?; - let output = { - self.queues - .iter() - .find(|(_, queue)| { - queue - .trees - .back() - .unwrap() - .0 - .get(&node_id) - .map(|node| node.data().is_mapped(Some(window))) - .unwrap_or(false) - }) - .map(|(o, _)| o.output.clone())? - }; let data = self - .queues - .get_mut(&output) - .unwrap() + .queue .trees .back_mut() .unwrap() @@ -1322,28 +1155,37 @@ impl TilingLayout { initial_placeholder: true, }; - window.output_leave(&output); + window.output_leave(&self.output); window.set_tiled(false); - Some((output, node_id)) + Some(node_id) } - fn unmap_window_internal(&mut self, mapped: &CosmicMapped) { + fn unmap_window_internal(&mut self, mapped: &CosmicMapped) -> bool { let tiling_node_id = mapped.tiling_node_id.lock().unwrap().as_ref().cloned(); + let gaps = self.gaps(); + if let Some(node_id) = tiling_node_id { - if let Some((output, queue)) = self.queues.iter_mut().find(|(_, queue)| { - let tree = &queue.trees.back().unwrap().0; - tree.get(&node_id) - .map(|node| node.data().is_mapped(Some(mapped))) - .unwrap_or(false) - }) { - let mut tree = queue.trees.back().unwrap().0.copy_clone(); + if self + .queue + .trees + .back() + .unwrap() + .0 + .get(&node_id) + .map(|node| node.data().is_mapped(Some(mapped))) + .unwrap_or(false) + { + let mut tree = self.queue.trees.back().unwrap().0.copy_clone(); TilingLayout::unmap_internal(&mut tree, &node_id); - let blocker = TilingLayout::update_positions(&output.output, &mut tree, self.gaps); - queue.push_tree(tree, ANIMATION_DURATION, blocker); + let blocker = TilingLayout::update_positions(&self.output, &mut tree, gaps); + self.queue.push_tree(tree, ANIMATION_DURATION, blocker); + + return true; } } + false } fn unmap_internal(tree: &mut Tree, node: &NodeId) { @@ -1402,19 +1244,16 @@ impl TilingLayout { } // TODO: Move would needs this to be accurate during animations - pub fn element_geometry(&self, elem: &CosmicMapped) -> Option> { + pub fn element_geometry(&self, elem: &CosmicMapped) -> Option> { if let Some(id) = elem.tiling_node_id.lock().unwrap().as_ref() { - if let Some(output) = self.output_for_element(elem) { - let (output_data, queue) = self.queues.get_key_value(output).unwrap(); - let node = queue.trees.back().unwrap().0.get(id).ok()?; - let data = node.data(); - assert!(data.is_mapped(Some(elem))); - let mut geo = *data.geometry(); - geo.loc += output_data.location; - return Some(geo); - } + let node = self.queue.trees.back().unwrap().0.get(id).ok()?; + let data = node.data(); + assert!(data.is_mapped(Some(elem))); + let geo = *data.geometry(); + Some(geo) + } else { + None } - None } pub fn move_current_node<'a>( @@ -1422,15 +1261,15 @@ impl TilingLayout { direction: Direction, seat: &Seat, ) -> MoveResult { - let output = seat.active_output(); - let queue = self.queues.get_mut(&output).unwrap(); - let mut tree = queue.trees.back().unwrap().0.copy_clone(); + let gaps = self.gaps(); + + let mut tree = self.queue.trees.back().unwrap().0.copy_clone(); let Some(target) = seat.get_keyboard().unwrap().current_focus() else { return MoveResult::None; }; let Some((node_id, data)) = - TilingLayout::currently_focused_node(&mut tree, &seat.active_output(), target) + TilingLayout::currently_focused_node(&mut tree, target) else { return MoveResult::None; }; @@ -1440,8 +1279,9 @@ impl TilingLayout { match window.handle_move(direction) { StackMoveResult::Handled => return MoveResult::Done, StackMoveResult::MoveOut(surface, loop_handle) => { - let mapped: CosmicMapped = CosmicWindow::new(surface, loop_handle).into(); - mapped.output_enter(&output, mapped.bbox()); + let mapped: CosmicMapped = + CosmicWindow::new(surface, loop_handle, self.theme.clone()).into(); + mapped.output_enter(&self.output, mapped.bbox()); let orientation = match direction { Direction::Left | Direction::Right => Orientation::Vertical, Direction::Up | Direction::Down => Orientation::Horizontal, @@ -1463,8 +1303,8 @@ impl TilingLayout { .unwrap(); *mapped.tiling_node_id.lock().unwrap() = Some(new_id); - let blocker = TilingLayout::update_positions(&output, &mut tree, self.gaps); - queue.push_tree(tree, ANIMATION_DURATION, blocker); + let blocker = TilingLayout::update_positions(&self.output, &mut tree, gaps); + self.queue.push_tree(tree, ANIMATION_DURATION, blocker); return MoveResult::ShiftFocus(mapped.into()); } StackMoveResult::Default => {} // continue normally @@ -1479,7 +1319,6 @@ impl TilingLayout { FocusedNodeData::Group(focus_stack, alive) => MoveResult::MoveFurther( WindowGroup { node: node_id, - output: output.downgrade(), alive, focus_stack, } @@ -1540,8 +1379,8 @@ impl TilingLayout { .data_mut() .remove_window(og_idx); - let blocker = TilingLayout::update_positions(&output, &mut tree, self.gaps); - queue.push_tree(tree, ANIMATION_DURATION, blocker); + let blocker = TilingLayout::update_positions(&self.output, &mut tree, gaps); + self.queue.push_tree(tree, ANIMATION_DURATION, blocker); return MoveResult::Done; } @@ -1566,8 +1405,8 @@ impl TilingLayout { .data_mut() .remove_window(og_idx); - let blocker = TilingLayout::update_positions(&output, &mut tree, self.gaps); - queue.push_tree(tree, ANIMATION_DURATION, blocker); + let blocker = TilingLayout::update_positions(&self.output, &mut tree, gaps); + self.queue.push_tree(tree, ANIMATION_DURATION, blocker); return MoveResult::Done; } @@ -1722,8 +1561,8 @@ impl TilingLayout { MoveResult::Done }; - let blocker = TilingLayout::update_positions(&output, &mut tree, self.gaps); - queue.push_tree(tree, ANIMATION_DURATION, blocker); + let blocker = TilingLayout::update_positions(&self.output, &mut tree, gaps); + self.queue.push_tree(tree, ANIMATION_DURATION, blocker); return result; } @@ -1737,7 +1576,6 @@ impl TilingLayout { FocusedNodeData::Group(focus_stack, alive) => MoveResult::MoveFurther( WindowGroup { node: node_id, - output: output.downgrade(), alive, focus_stack, } @@ -1753,14 +1591,13 @@ impl TilingLayout { focus_stack: impl Iterator + 'a, swap_desc: Option, ) -> FocusResult { - let output = seat.active_output(); - let tree = &self.queues.get(&output).unwrap().trees.back().unwrap().0; + let tree = &self.queue.trees.back().unwrap().0; let Some(target) = seat.get_keyboard().unwrap().current_focus() else { return FocusResult::None; }; let Some(focused) = - TilingLayout::currently_focused_node(tree, &seat.active_output(), target).or_else( + TilingLayout::currently_focused_node(tree, target).or_else( || { TilingLayout::last_active_window(tree, focus_stack) .map(|(id, mapped)| (id, FocusedNodeData::Window(mapped))) @@ -1814,7 +1651,6 @@ impl TilingLayout { Data::Group { alive, .. } => FocusResult::Some( WindowGroup { node: id, - output: output.downgrade(), alive: Arc::downgrade(alive), focus_stack: stack, } @@ -1849,7 +1685,6 @@ impl TilingLayout { return FocusResult::Some( WindowGroup { node: group.clone(), - output: output.downgrade(), alive: match group_data { &Data::Group { ref alive, .. } => Arc::downgrade(alive), _ => unreachable!(), @@ -1908,7 +1743,6 @@ impl TilingLayout { Data::Group { alive, .. } => { FocusResult::Some(KeyboardFocusTarget::Group(WindowGroup { node: replacement_id.clone(), - output: output.downgrade(), alive: Arc::downgrade(&alive), focus_stack: tree .children_ids(replacement_id) @@ -2024,18 +1858,14 @@ impl TilingLayout { new_orientation: Option, seat: &Seat, ) { - let output = seat.active_output(); - let Some(queue) = self.queues.get_mut(&output) else { - return; - }; - let mut tree = queue.trees.back().unwrap().0.copy_clone(); + let gaps = self.gaps(); let Some(target) = seat.get_keyboard().unwrap().current_focus() else { return; }; - if let Some((last_active, _)) = - TilingLayout::currently_focused_node(&tree, &seat.active_output(), target) - { + + let mut tree = self.queue.trees.back().unwrap().0.copy_clone(); + if let Some((last_active, _)) = TilingLayout::currently_focused_node(&tree, target) { if let Some(group) = tree.get(&last_active).unwrap().parent().cloned() { if let &mut Data::Group { ref mut orientation, @@ -2065,25 +1895,23 @@ impl TilingLayout { *orientation = new_orientation; - let blocker = TilingLayout::update_positions(&output, &mut tree, self.gaps); - queue.push_tree(tree, ANIMATION_DURATION, blocker); + let blocker = TilingLayout::update_positions(&self.output, &mut tree, gaps); + self.queue.push_tree(tree, ANIMATION_DURATION, blocker); } } } } pub fn toggle_stacking<'a>(&mut self, seat: &Seat, mut focus_stack: FocusStackMut) { - let output = seat.active_output(); - let Some(queue) = self.queues.get_mut(&output) else { - return; - }; - let mut tree = queue.trees.back().unwrap().0.copy_clone(); + let gaps = self.gaps(); let Some(target) = seat.get_keyboard().unwrap().current_focus() else { return; }; + + let mut tree = self.queue.trees.back().unwrap().0.copy_clone(); if let Some((last_active, last_active_data)) = - TilingLayout::currently_focused_node(&tree, &seat.active_output(), target) + TilingLayout::currently_focused_node(&tree, target) { match last_active_data { FocusedNodeData::Window(mapped) => { @@ -2091,7 +1919,10 @@ impl TilingLayout { // if it is just a window match tree.get_mut(&last_active).unwrap().data_mut() { Data::Mapped { mapped, .. } => { - mapped.convert_to_stack(std::iter::once((&output, mapped.bbox()))); + mapped.convert_to_stack( + (&self.output, mapped.bbox()), + self.theme.clone(), + ); focus_stack.append(&mapped); } _ => unreachable!(), @@ -2106,7 +1937,8 @@ impl TilingLayout { let handle = mapped.loop_handle(); mapped.convert_to_surface( first, - std::iter::once((&output, mapped.bbox())), + (&self.output, mapped.bbox()), + self.theme.clone(), ); focus_stack.append(&mapped); handle @@ -2118,15 +1950,22 @@ impl TilingLayout { for other in surfaces { other.try_force_undecorated(false); other.set_tiled(false); - let window = - CosmicMapped::from(CosmicWindow::new(other, handle.clone())); - window.output_enter(&output, window.bbox()); - window.set_bounds(output.geometry().size); + let window = CosmicMapped::from(CosmicWindow::new( + other, + handle.clone(), + self.theme.clone(), + )); + window.output_enter(&self.output, window.bbox()); + + { + let layer_map = layer_map_for_output(&self.output); + window.set_bounds(layer_map.non_exclusive_zone().size); + } TilingLayout::map_to_tree( &mut tree, window, - &output, + &self.output, Some(focus_stack.iter()), None, ) @@ -2156,7 +1995,7 @@ impl TilingLayout { return; } let handle = handle.unwrap(); - let stack = CosmicStack::new(surfaces.into_iter(), handle); + let stack = CosmicStack::new(surfaces.into_iter(), handle, self.theme.clone()); for child in tree .children_ids(&last_active) @@ -2171,8 +2010,8 @@ impl TilingLayout { let data = tree.get_mut(&last_active).unwrap().data_mut(); let geo = *data.geometry(); - stack.set_geometry(geo); - stack.output_enter(&output, stack.bbox()); + //stack.set_geometry(geo.as_local()); + stack.output_enter(&self.output, stack.bbox()); stack.set_activate(true); stack.active().send_configure(); stack.refresh(); @@ -2188,18 +2027,17 @@ impl TilingLayout { } } - let blocker = TilingLayout::update_positions(&output, &mut tree, self.gaps); - queue.push_tree(tree, ANIMATION_DURATION, blocker); + let blocker = TilingLayout::update_positions(&self.output, &mut tree, gaps); + self.queue.push_tree(tree, ANIMATION_DURATION, blocker); } } - pub fn recalculate(&mut self, output: &Output) { - let Some(queue) = self.queues.get_mut(output) else { - return; - }; - let mut tree = queue.trees.back().unwrap().0.copy_clone(); - let blocker = TilingLayout::update_positions(&output, &mut tree, self.gaps); - queue.push_tree(tree, ANIMATION_DURATION, blocker); + pub fn recalculate(&mut self) { + let gaps = self.gaps(); + + let mut tree = self.queue.trees.back().unwrap().0.copy_clone(); + let blocker = TilingLayout::update_positions(&self.output, &mut tree, gaps); + self.queue.push_tree(tree, ANIMATION_DURATION, blocker); } pub fn refresh(&mut self) { @@ -2221,9 +2059,7 @@ impl TilingLayout { } pub fn animations_going(&self) -> bool { - self.queues - .values() - .any(|queue| queue.animation_start.is_some()) + self.queue.animation_start.is_some() } pub fn update_animation_state(&mut self) -> HashMap { @@ -2232,65 +2068,65 @@ impl TilingLayout { clients.extend(blocker.signal_ready()); } - for queue in self.queues.values_mut() { - if let Some(start) = queue.animation_start { - let duration_since_start = Instant::now().duration_since(start); - if duration_since_start - >= queue - .trees - .get(1) - .expect("Animation going without second tree?") - .1 - { - let _ = queue.animation_start.take(); - let _ = queue.trees.pop_front(); - let _ = queue.trees.front_mut().unwrap().2.take(); - } else { - continue; - } + if let Some(start) = self.queue.animation_start { + let duration_since_start = Instant::now().duration_since(start); + if duration_since_start + >= self + .queue + .trees + .get(1) + .expect("Animation going without second tree?") + .1 + { + let _ = self.queue.animation_start.take(); + let _ = self.queue.trees.pop_front(); + let _ = self.queue.trees.front_mut().unwrap().2.take(); + } else { + return clients; } + } + + let ready_trees = self + .queue + .trees + .iter() + .skip(1) + .take_while(|(_, _, blocker)| { + blocker + .as_ref() + .map(|blocker| blocker.is_ready() && blocker.is_signaled()) + .unwrap_or(true) + }) + .count(); - let ready_trees = queue + // merge + let other_duration = if ready_trees > 1 { + self.queue .trees - .iter() - .skip(1) - .take_while(|(_, _, blocker)| { - blocker - .as_ref() - .map(|blocker| blocker.is_ready() && blocker.is_signaled()) - .unwrap_or(true) + .drain(1..ready_trees) + .fold(None, |res, (_, duration, blocker)| { + if let Some(blocker) = blocker { + clients.extend(blocker.signal_ready()); + } + Some( + res.map(|old_duration: Duration| old_duration.max(duration)) + .unwrap_or(duration), + ) }) - .count(); - - // merge - let other_duration = if ready_trees > 1 { - queue - .trees - .drain(1..ready_trees) - .fold(None, |res, (_, duration, blocker)| { - if let Some(blocker) = blocker { - clients.extend(blocker.signal_ready()); - } - Some( - res.map(|old_duration: Duration| old_duration.max(duration)) - .unwrap_or(duration), - ) - }) - } else { - None - }; + } else { + None + }; - // start - if ready_trees > 0 { - let (_, duration, blocker) = queue.trees.get_mut(1).unwrap(); - *duration = other_duration - .map(|other| other.max(*duration)) - .unwrap_or(*duration); - if let Some(blocker) = blocker { - clients.extend(blocker.signal_ready()); - } - queue.animation_start = Some(Instant::now()); + // start + if ready_trees > 0 { + let (_, duration, blocker) = self.queue.trees.get_mut(1).unwrap(); + *duration = other_duration + .map(|other| other.max(*duration)) + .unwrap_or(*duration); + if let Some(blocker) = blocker { + clients.extend(blocker.signal_ready()); } + self.queue.animation_start = Some(Instant::now()); } clients @@ -2337,28 +2173,24 @@ impl TilingLayout { edges: ResizeEdge, amount: i32, ) -> bool { - let Some((output, mut node_id)) = self.queues.iter().find_map(|(output, queue)| { - let tree = &queue.trees.back().unwrap().0; - let root_id = tree.root_node_id()?; - let id = - match TilingLayout::currently_focused_node(tree, &output.output, focused.clone()) { - Some((_id, FocusedNodeData::Window(mapped))) => - // we need to make sure the id belongs to this tree.. - { - tree.traverse_pre_order_ids(root_id) - .unwrap() - .find(|id| tree.get(id).unwrap().data().is_mapped(Some(&mapped))) - } - Some((id, FocusedNodeData::Group(_, _))) => Some(id), // in this case the output was already matched, so the id is to be trusted - _ => None, - }; - id.map(|id| (output.output.clone(), id)) - }) else { - return false; - }; - - let queue = self.queues.get_mut(&output).unwrap(); - let mut tree = queue.trees.back().unwrap().0.copy_clone(); + let gaps = self.gaps(); + + let mut tree = self.queue.trees.back().unwrap().0.copy_clone(); + let Some(root_id) = tree.root_node_id() else { return false }; + let Some(mut node_id) = + (match TilingLayout::currently_focused_node(&tree, focused.clone()) { + Some((_id, FocusedNodeData::Window(mapped))) => + // we need to make sure the id belongs to this tree.. + { + tree.traverse_pre_order_ids(root_id) + .unwrap() + .find(|id| tree.get(id).unwrap().data().is_mapped(Some(&mapped))) + } + Some((id, FocusedNodeData::Group(_, _))) => Some(id), // in this case the workspace handle was already matched, so the id is to be trusted + _ => None, + }) else { + return false + }; while let Some(group_id) = tree.get(&node_id).unwrap().parent().cloned() { let orientation = tree.get(&group_id).unwrap().data().orientation(); @@ -2421,8 +2253,8 @@ impl TilingLayout { } _ => unreachable!(), } - let blocker = TilingLayout::update_positions(&output, &mut tree, self.gaps); - queue.push_tree(tree, Duration::ZERO, blocker); + let blocker = TilingLayout::update_positions(&self.output, &mut tree, gaps); + self.queue.push_tree(tree, None, blocker); return true; } @@ -2430,7 +2262,7 @@ impl TilingLayout { true } - pub fn stacking_indicator(&self) -> Option> { + pub fn stacking_indicator(&self) -> Option> { if let Some(TargetZone::WindowStack(_, geo)) = self.last_overview_hover.as_ref().map(|(_, zone)| zone) { @@ -2440,16 +2272,10 @@ impl TilingLayout { } } - pub fn cleanup_drag(&mut self, output: &Output) { - let mut queue = self.queues.get_mut(output); - let mut owned_tree = None; - let mut tree = if let Some(queue) = queue.as_mut() { - owned_tree = queue.trees.back().map(|x| x.0.copy_clone()); - owned_tree.as_mut() - } else { - self.standby_tree.as_mut() - } - .unwrap(); + pub fn cleanup_drag(&mut self) { + let gaps = self.gaps(); + + let mut tree = self.queue.trees.back().unwrap().0.copy_clone(); if let Some(root) = tree.root_node_id() { for id in tree @@ -2467,35 +2293,21 @@ impl TilingLayout { } } - if let Some(mut tree) = owned_tree { - let blocker = TilingLayout::update_positions(output, &mut tree, self.gaps); - queue.unwrap().push_tree(tree, ANIMATION_DURATION, blocker); - } + let blocker = TilingLayout::update_positions(&self.output, &mut tree, gaps); + self.queue.push_tree(tree, ANIMATION_DURATION, blocker); } } - pub fn drop_window( - &mut self, - window: CosmicMapped, - output: &Output, - _cursor_pos: Point, - ) -> (CosmicMapped, Point) { - let mut queue = if self.queues.contains_key(output) { - self.queues.get_mut(output) - } else { - self.queues.values_mut().next() - }; - let mut owned_tree = None; - let mut tree = if let Some(queue) = queue.as_mut() { - owned_tree = queue.trees.back().map(|x| x.0.copy_clone()); - owned_tree.as_mut() - } else { - self.standby_tree.as_mut() - } - .unwrap(); + pub fn drop_window(&mut self, window: CosmicMapped) -> (CosmicMapped, Point) { + let gaps = self.gaps(); - window.output_enter(&output, window.bbox()); - window.set_bounds(output.geometry().size); + let mut tree = self.queue.trees.back().unwrap().0.copy_clone(); + + window.output_enter(&self.output, window.bbox()); + { + let layer_map = layer_map_for_output(&self.output); + window.set_bounds(layer_map.non_exclusive_zone().size); + } let mapped = match self.last_overview_hover.as_ref().map(|x| &x.1) { Some(TargetZone::GroupEdge(group_id, direction)) if tree.get(&group_id).is_ok() => { @@ -2589,7 +2401,7 @@ impl TilingLayout { Some(TargetZone::WindowStack(window_id, _)) if tree.get(&window_id).is_ok() => { match tree.get_mut(window_id).unwrap().data_mut() { Data::Mapped { mapped, .. } => { - mapped.convert_to_stack(std::iter::once((output, mapped.bbox()))); + mapped.convert_to_stack((&self.output, mapped.bbox()), self.theme.clone()); let Some(stack) = mapped.stack_ref_mut() else { unreachable!() }; @@ -2605,7 +2417,7 @@ impl TilingLayout { TilingLayout::map_to_tree( &mut tree, window.clone(), - output, + &self.output, Option::>::None, None, ); @@ -2630,12 +2442,10 @@ impl TilingLayout { } } - if let Some(mut tree) = owned_tree { - let blocker = TilingLayout::update_positions(output, &mut tree, self.gaps); - queue.unwrap().push_tree(tree, ANIMATION_DURATION, blocker); - } + let blocker = TilingLayout::update_positions(&self.output, &mut tree, gaps); + self.queue.push_tree(tree, ANIMATION_DURATION, blocker); - let location = output.geometry().loc + self.element_geometry(&mapped).unwrap().loc; + let location = self.element_geometry(&mapped).unwrap().loc; (mapped, location) } @@ -2653,7 +2463,6 @@ impl TilingLayout { fn currently_focused_node( tree: &Tree, - output: &Output, mut target: KeyboardFocusTarget, ) -> Option<(NodeId, FocusedNodeData)> { // if the focus is currently on a popup, treat it's toplevel as the target @@ -2689,14 +2498,12 @@ impl TilingLayout { } } KeyboardFocusTarget::Group(window_group) => { - if window_group.output == *output { - let node = tree.get(&window_group.node).ok()?; - if node.data().is_group() { - return Some(( - window_group.node, - FocusedNodeData::Group(window_group.focus_stack, window_group.alive), - )); - } + let node = tree.get(&window_group.node).ok()?; + if node.data().is_group() { + return Some(( + window_group.node, + FocusedNodeData::Group(window_group.focus_stack, window_group.alive), + )); } } _ => {} @@ -2816,7 +2623,9 @@ impl TilingLayout { let mut configures = Vec::new(); let (outer, inner) = gaps; - let mut geo = layer_map_for_output(&output).non_exclusive_zone(); + let mut geo = layer_map_for_output(&output) + .non_exclusive_zone() + .as_local(); geo.loc.x += outer; geo.loc.y += outer; geo.size.w -= outer * 2; @@ -2931,15 +2740,10 @@ impl TilingLayout { Data::Mapped { mapped, .. } => { if !(mapped.is_fullscreen(true) || mapped.is_maximized(true)) { mapped.set_tiled(true); - let internal_geometry = Rectangle::from_loc_and_size( - geo.loc + output.geometry().loc, - geo.size, - ); - if mapped.geometry() != internal_geometry { - mapped.set_geometry(internal_geometry); - if let Some(serial) = mapped.configure() { - configures.push((mapped.active_window(), serial)); - } + let internal_geometry = geo.to_global(&output); + mapped.set_geometry(internal_geometry); + if let Some(serial) = mapped.configure() { + configures.push((mapped.active_window(), serial)); } } } @@ -2964,606 +2768,606 @@ impl TilingLayout { pub fn element_under( &mut self, - location: Point, + location: Point, overview: OverviewMode, - ) -> Option<(PointerFocusTarget, Point)> { + ) -> Option<(PointerFocusTarget, Point)> { + let gaps = self.gaps(); let last_overview_hover = &mut self.last_overview_hover; let placeholder_id = &self.placeholder_id; - let gaps = &self.gaps; - self.queues.iter_mut().find_map(|(output_data, queue)| { - let tree = &queue.trees.back().unwrap().0; - let root = tree.root_node_id()?; - let location = (location - output_data.location.to_f64()).to_i32_round(); + let tree = &self.queue.trees.back().unwrap().0; + let root = tree.root_node_id()?; + let location = location.to_i32_round(); - { - let output_geo = - Rectangle::from_loc_and_size((0, 0), output_data.output.geometry().size); - if !output_geo.contains(location) { - return None; - } + { + let output_geo = + Rectangle::from_loc_and_size((0, 0), self.output.geometry().size.as_logical()) + .as_local(); + if !output_geo.contains(location) { + return None; } + } - if !matches!(overview, OverviewMode::Started(_, _)) { - last_overview_hover.take(); - } + if !matches!(overview, OverviewMode::Started(_, _)) { + last_overview_hover.take(); + } - if matches!(overview, OverviewMode::None) { - let mut result = None; - let mut lookup = Some(root.clone()); - while let Some(node) = lookup { - let data = tree.get(&node).unwrap().data(); - if data.geometry().contains(location) { - result = Some(node.clone()); - } + if matches!(overview, OverviewMode::None) { + let mut result = None; + let mut lookup = Some(root.clone()); + while let Some(node) = lookup { + let data = tree.get(&node).unwrap().data(); + if data.geometry().contains(location) { + result = Some(node.clone()); + } - lookup = None; - if result.is_some() && data.is_group() { - for child_id in tree.children_ids(&node).unwrap() { - if tree - .get(child_id) - .unwrap() - .data() - .geometry() - .contains(location) - { - lookup = Some(child_id.clone()); - break; - } + lookup = None; + if result.is_some() && data.is_group() { + for child_id in tree.children_ids(&node).unwrap() { + if tree + .get(child_id) + .unwrap() + .data() + .geometry() + .contains(location) + { + lookup = Some(child_id.clone()); + break; } } } + } - match result.map(|id| (id.clone(), tree.get(&id).unwrap().data().clone())) { - Some(( - _, - Data::Mapped { - mapped, - last_geometry, - }, - )) => { - let test_point = location.to_f64() - last_geometry.loc.to_f64() - + mapped.geometry().loc.to_f64(); - mapped.is_in_input_region(&test_point).then(|| { - ( - mapped.clone().into(), - last_geometry.loc + output_data.location - mapped.geometry().loc, - ) + match result.map(|id| (id.clone(), tree.get(&id).unwrap().data().clone())) { + Some(( + _, + Data::Mapped { + mapped, + last_geometry, + }, + )) => { + let test_point = (location.to_f64() - last_geometry.loc.to_f64() + + mapped.geometry().loc.to_f64().as_local()) + .as_logical(); + mapped.is_in_input_region(&test_point).then(|| { + ( + mapped.clone().into(), + last_geometry.loc - mapped.geometry().loc.as_local(), + ) + }) + } + Some(( + id, + Data::Group { + orientation, + last_geometry, + .. + }, + )) => { + let idx = tree + .children(&id) + .unwrap() + .position(|node| { + let data = node.data(); + match orientation { + Orientation::Vertical => location.x < data.geometry().loc.x, + Orientation::Horizontal => location.y < data.geometry().loc.y, + } }) - } + .and_then(|x| x.checked_sub(1))?; Some(( - id, - Data::Group { + ResizeForkTarget { + node: id.clone(), + output: self.output.downgrade(), + left_up_idx: idx, orientation, - last_geometry, - .. - }, - )) => { - let idx = tree - .children(&id) - .unwrap() - .position(|node| { - let data = node.data(); - match orientation { - Orientation::Vertical => location.x < data.geometry().loc.x, - Orientation::Horizontal => location.y < data.geometry().loc.y, - } - }) - .and_then(|x| x.checked_sub(1))?; - Some(( - ResizeForkTarget { - node: id.clone(), - output: output_data.output.downgrade(), - left_up_idx: idx, - orientation, - } - .into(), - last_geometry.loc - + output_data.location - + tree - .children(&id) - .unwrap() - .skip(idx) - .next() - .map(|node| { - let geo = node.data().geometry(); - geo.loc + geo.size - }) - .unwrap(), - )) - } - _ => None, + } + .into(), + last_geometry.loc + + tree + .children(&id) + .unwrap() + .skip(idx) + .next() + .map(|node| { + let geo = node.data().geometry(); + geo.loc + geo.size + }) + .unwrap(), + )) + } + _ => None, + } + } else if matches!(overview, OverviewMode::Started(Trigger::Pointer(_), _)) { + let non_exclusive_zone = layer_map_for_output(&self.output) + .non_exclusive_zone() + .as_local(); + let geometries = geometries_for_groupview( + tree, + Option::<&mut GlowRenderer>::None, + non_exclusive_zone, + None, + 1.0, + overview.alpha().unwrap(), + 1.0, + placeholder_id, + Some(None), + None, + None, + self.theme.cosmic(), + ) + .0; + + let mut result = None; + let mut lookup = Some(root.clone()); + while let Some(node) = lookup { + let data = tree.get(&node).unwrap().data(); + if geometries + .get(&node) + .map(|geo| geo.contains(location)) + .unwrap_or(false) + { + result = Some(node.clone()); } - } else if matches!(overview, OverviewMode::Started(Trigger::Pointer(_), _)) { - let non_exclusive_zone = - layer_map_for_output(&output_data.output).non_exclusive_zone(); - let geometries = geometries_for_groupview( - tree, - Option::<&mut GlowRenderer>::None, - non_exclusive_zone, - None, - 1.0, - overview.alpha().unwrap(), - 1.0, - placeholder_id, - Some(None), - None, - None, - ) - .0; - - let mut result = None; - let mut lookup = Some(root.clone()); - while let Some(node) = lookup { - let data = tree.get(&node).unwrap().data(); - if geometries - .get(&node) - .map(|geo| geo.contains(location)) - .unwrap_or(false) - { - result = Some(node.clone()); - } - lookup = None; - if result.is_some() && data.is_group() { - if tree.children(&node).unwrap().any(|child| { - matches!( - child.data(), - Data::Placeholder { - initial_placeholder: false, - .. - } - ) - }) { + lookup = None; + if result.is_some() && data.is_group() { + if tree.children(&node).unwrap().any(|child| { + matches!( + child.data(), + Data::Placeholder { + initial_placeholder: false, + .. + } + ) + }) { + break; + } + for child_id in tree.children_ids(&node).unwrap() { + if geometries + .get(child_id) + .map(|geo| geo.contains(location)) + .unwrap_or(false) + { + lookup = Some(child_id.clone()); break; } - for child_id in tree.children_ids(&node).unwrap() { - if geometries - .get(child_id) - .map(|geo| geo.contains(location)) - .unwrap_or(false) + } + } + } + + if let Some(res_id) = result { + let mut last_geometry = *geometries.get(&res_id)?; + let node = tree.get(&res_id).unwrap(); + let data = node.data().clone(); + + let group_zone = if let Data::Group { orientation, .. } = &data { + if node.children().iter().any(|child_id| { + tree.get(child_id) + .ok() + .map(|child| { + matches!( + child.data(), + Data::Placeholder { + initial_placeholder: false, + .. + } + ) + }) + .unwrap_or(false) + }) { + None + } else { + let left_edge = match &*last_overview_hover { + Some((_, TargetZone::GroupEdge(id, Direction::Left))) + if *id == res_id => + { + let zone = Rectangle::from_loc_and_size( + last_geometry.loc, + (80, last_geometry.size.h), + ); + last_geometry.loc.x += 80; + last_geometry.size.w -= 80; + zone + } + _ => { + let zone = Rectangle::from_loc_and_size( + last_geometry.loc, + (32, last_geometry.size.h), + ); + last_geometry.loc.x += 32; + last_geometry.size.w -= 32; + zone + } + }; + let top_edge = match &*last_overview_hover { + Some((_, TargetZone::GroupEdge(id, Direction::Up))) + if *id == res_id => + { + let zone = Rectangle::from_loc_and_size( + last_geometry.loc, + (last_geometry.size.w, 80), + ); + last_geometry.loc.y += 80; + last_geometry.size.h -= 80; + zone + } + _ => { + let zone = Rectangle::from_loc_and_size( + last_geometry.loc, + (last_geometry.size.w, 32), + ); + last_geometry.loc.y += 32; + last_geometry.size.h -= 32; + zone + } + }; + let right_edge = match &*last_overview_hover { + Some((_, TargetZone::GroupEdge(id, Direction::Right))) + if *id == res_id => { - lookup = Some(child_id.clone()); - break; + let zone = Rectangle::from_loc_and_size( + ( + last_geometry.loc.x + last_geometry.size.w - 80, + last_geometry.loc.y, + ), + (80, last_geometry.size.h), + ); + last_geometry.size.w -= 80; + zone } + _ => { + let zone = Rectangle::from_loc_and_size( + ( + last_geometry.loc.x + last_geometry.size.w - 32, + last_geometry.loc.y, + ), + (32, last_geometry.size.h), + ); + last_geometry.size.w -= 32; + zone + } + }; + let bottom_edge = match &*last_overview_hover { + Some((_, TargetZone::GroupEdge(id, Direction::Down))) + if *id == res_id => + { + let zone = Rectangle::from_loc_and_size( + ( + last_geometry.loc.x, + last_geometry.loc.y + last_geometry.size.h - 80, + ), + (last_geometry.size.w, 80), + ); + last_geometry.size.h -= 80; + zone + } + _ => { + let zone = Rectangle::from_loc_and_size( + ( + last_geometry.loc.x, + last_geometry.loc.y + last_geometry.size.h - 32, + ), + (last_geometry.size.w, 32), + ); + last_geometry.size.h -= 32; + zone + } + }; + + if left_edge.contains(location) { + Some(TargetZone::GroupEdge(res_id.clone(), Direction::Left)) + } else if right_edge.contains(location) { + Some(TargetZone::GroupEdge(res_id.clone(), Direction::Right)) + } else if top_edge.contains(location) { + Some(TargetZone::GroupEdge(res_id.clone(), Direction::Up)) + } else if bottom_edge.contains(location) { + Some(TargetZone::GroupEdge(res_id.clone(), Direction::Down)) + } else { + let idx = tree + .children_ids(&res_id) + .unwrap() + .position(|node| { + let Some(geo) = geometries.get(node) else { + return false; + }; + match orientation { + Orientation::Vertical => location.x < geo.loc.x, + Orientation::Horizontal => location.y < geo.loc.y, + } + }) + .and_then(|x| x.checked_sub(1)) + .unwrap_or(0); + Some(TargetZone::GroupInterior(res_id.clone(), idx)) } } - } + } else { + None + }; - if let Some(res_id) = result { - let mut last_geometry = *geometries.get(&res_id)?; - let node = tree.get(&res_id).unwrap(); - let data = node.data().clone(); + let target_zone = group_zone.unwrap_or_else(|| match &data { + Data::Placeholder { .. } => TargetZone::InitialPlaceholder(res_id), + Data::Group { .. } | Data::Mapped { .. } => { + let id = if data.is_group() { + tree.get(&res_id) + .unwrap() + .children() + .iter() + .find(|child_id| tree.get(child_id).unwrap().data().is_mapped(None)) + .expect("Placeholder group without real window?") + .clone() + } else { + res_id + }; - let group_zone = if let Data::Group { orientation, .. } = &data { - if node.children().iter().any(|child_id| { - tree.get(child_id) - .ok() - .map(|child| { - matches!( - child.data(), - Data::Placeholder { - initial_placeholder: false, - .. - } - ) - }) - .unwrap_or(false) - }) { - None + let third_width = (last_geometry.size.w as f64 / 3.0).round() as i32; + let third_height = (last_geometry.size.h as f64 / 3.0).round() as i32; + let stack_region = Rectangle::from_extemities( + ( + last_geometry.loc.x + third_width, + last_geometry.loc.y + third_height, + ), + ( + last_geometry.loc.x + 2 * third_width, + last_geometry.loc.y + 2 * third_height, + ), + ); + + if stack_region.contains(location) { + TargetZone::WindowStack(id, last_geometry) } else { - let left_edge = match &*last_overview_hover { - Some((_, TargetZone::GroupEdge(id, Direction::Left))) - if *id == res_id => - { - let zone = Rectangle::from_loc_and_size( - last_geometry.loc, - (80, last_geometry.size.h), - ); - last_geometry.loc.x += 80; - last_geometry.size.w -= 80; - zone - } - _ => { - let zone = Rectangle::from_loc_and_size( - last_geometry.loc, - (32, last_geometry.size.h), - ); - last_geometry.loc.x += 32; - last_geometry.size.w -= 32; - zone - } - }; - let top_edge = match &*last_overview_hover { - Some((_, TargetZone::GroupEdge(id, Direction::Up))) - if *id == res_id => - { - let zone = Rectangle::from_loc_and_size( - last_geometry.loc, - (last_geometry.size.w, 80), - ); - last_geometry.loc.y += 80; - last_geometry.size.h -= 80; - zone - } - _ => { - let zone = Rectangle::from_loc_and_size( - last_geometry.loc, - (last_geometry.size.w, 32), - ); - last_geometry.loc.y += 32; - last_geometry.size.h -= 32; - zone - } - }; - let right_edge = match &*last_overview_hover { - Some((_, TargetZone::GroupEdge(id, Direction::Right))) - if *id == res_id => - { - let zone = Rectangle::from_loc_and_size( - ( - last_geometry.loc.x + last_geometry.size.w - 80, - last_geometry.loc.y, - ), - (80, last_geometry.size.h), - ); - last_geometry.size.w -= 80; - zone - } - _ => { - let zone = Rectangle::from_loc_and_size( - ( - last_geometry.loc.x + last_geometry.size.w - 32, - last_geometry.loc.y, - ), - (32, last_geometry.size.h), - ); - last_geometry.size.w -= 32; - zone + let left_right = { + let relative_loc = (location.x - last_geometry.loc.x) as f64; + if relative_loc < last_geometry.size.w as f64 / 2.0 { + (Direction::Left, relative_loc / last_geometry.size.w as f64) + } else { + ( + Direction::Right, + 1.0 - (relative_loc / last_geometry.size.w as f64), + ) } }; - let bottom_edge = match &*last_overview_hover { - Some((_, TargetZone::GroupEdge(id, Direction::Down))) - if *id == res_id => - { - let zone = Rectangle::from_loc_and_size( - ( - last_geometry.loc.x, - last_geometry.loc.y + last_geometry.size.h - 80, - ), - (last_geometry.size.w, 80), - ); - last_geometry.size.h -= 80; - zone - } - _ => { - let zone = Rectangle::from_loc_and_size( - ( - last_geometry.loc.x, - last_geometry.loc.y + last_geometry.size.h - 32, - ), - (last_geometry.size.w, 32), - ); - last_geometry.size.h -= 32; - zone + let up_down = { + let relative_loc = (location.y - last_geometry.loc.y) as f64; + if relative_loc < last_geometry.size.h as f64 / 2.0 { + (Direction::Up, relative_loc / last_geometry.size.h as f64) + } else { + ( + Direction::Down, + 1.0 - (relative_loc / last_geometry.size.h as f64), + ) } }; - if left_edge.contains(location) { - Some(TargetZone::GroupEdge(res_id.clone(), Direction::Left)) - } else if right_edge.contains(location) { - Some(TargetZone::GroupEdge(res_id.clone(), Direction::Right)) - } else if top_edge.contains(location) { - Some(TargetZone::GroupEdge(res_id.clone(), Direction::Up)) - } else if bottom_edge.contains(location) { - Some(TargetZone::GroupEdge(res_id.clone(), Direction::Down)) - } else { - let idx = tree - .children_ids(&res_id) - .unwrap() - .position(|node| { - let Some(geo) = geometries.get(node) else { - return false; - }; - match orientation { - Orientation::Vertical => location.x < geo.loc.x, - Orientation::Horizontal => location.y < geo.loc.y, - } - }) - .and_then(|x| x.checked_sub(1)) - .unwrap_or(0); - Some(TargetZone::GroupInterior(res_id.clone(), idx)) - } - } - } else { - None - }; - - let target_zone = group_zone.unwrap_or_else(|| match &data { - Data::Placeholder { .. } => TargetZone::InitialPlaceholder(res_id), - Data::Group { .. } | Data::Mapped { .. } => { - let id = if data.is_group() { - tree.get(&res_id) - .unwrap() - .children() - .iter() - .find(|child_id| { - tree.get(child_id).unwrap().data().is_mapped(None) - }) - .expect("Placeholder group without real window?") - .clone() + let direction = if left_right.1 < up_down.1 { + left_right.0 } else { - res_id + up_down.0 }; - let third_width = (last_geometry.size.w as f64 / 3.0).round() as i32; - let third_height = (last_geometry.size.h as f64 / 3.0).round() as i32; - let stack_region = Rectangle::from_extemities( - ( - last_geometry.loc.x + third_width, - last_geometry.loc.y + third_height, - ), - ( - last_geometry.loc.x + 2 * third_width, - last_geometry.loc.y + 2 * third_height, - ), - ); + TargetZone::WindowSplit(id, direction) + } + } + }); - if stack_region.contains(location) { - TargetZone::WindowStack(id, last_geometry) - } else { - let left_right = { - let relative_loc = (location.x - last_geometry.loc.x) as f64; - if relative_loc < last_geometry.size.w as f64 / 2.0 { - ( - Direction::Left, - relative_loc / last_geometry.size.w as f64, - ) - } else { - ( - Direction::Right, - 1.0 - (relative_loc / last_geometry.size.w as f64), - ) + match &mut *last_overview_hover { + last_overview_hover @ None => { + *last_overview_hover = Some(( + None, + tree.traverse_pre_order_ids(root) + .unwrap() + .find(|id| match tree.get(id).unwrap().data() { + Data::Placeholder { + initial_placeholder: true, + .. + } => true, + _ => false, + }) + .map(|node_id| TargetZone::InitialPlaceholder(node_id)) + .unwrap_or(TargetZone::Initial), + )); + } + Some((instant, old_target_zone)) => { + if *old_target_zone != target_zone { + let overdue = if let Some(instant) = instant { + match old_target_zone { + TargetZone::InitialPlaceholder(_) => { + Instant::now().duration_since(*instant) + > INITIAL_MOUSE_ANIMATION_DELAY } - }; - let up_down = { - let relative_loc = (location.y - last_geometry.loc.y) as f64; - if relative_loc < last_geometry.size.h as f64 / 2.0 { - (Direction::Up, relative_loc / last_geometry.size.h as f64) - } else { - ( - Direction::Down, - 1.0 - (relative_loc / last_geometry.size.h as f64), - ) + _ => { + Instant::now().duration_since(*instant) + > MOUSE_ANIMATION_DELAY } - }; + } + } else { + *instant = Some(Instant::now()); + false + }; - let direction = if left_right.1 < up_down.1 { - left_right.0 + if overdue { + let duration = if target_zone.is_window_zone() + && !old_target_zone.is_window_zone() + { + ANIMATION_DURATION * 2 } else { - up_down.0 + ANIMATION_DURATION }; - TargetZone::WindowSplit(id, direction) - } - } - }); + let mut tree = tree.copy_clone(); - match &mut *last_overview_hover { - last_overview_hover @ None => { - *last_overview_hover = Some(( - None, - tree.traverse_pre_order_ids(root) - .unwrap() - .find(|id| match tree.get(id).unwrap().data() { - Data::Placeholder { - initial_placeholder: true, - .. - } => true, - _ => false, - }) - .map(|node_id| TargetZone::InitialPlaceholder(node_id)) - .unwrap_or(TargetZone::Initial), - )); - } - Some((instant, old_target_zone)) => { - if *old_target_zone != target_zone { - let overdue = if let Some(instant) = instant { - match old_target_zone { - TargetZone::InitialPlaceholder(_) => { - Instant::now().duration_since(*instant) - > INITIAL_MOUSE_ANIMATION_DELAY - } - _ => { - Instant::now().duration_since(*instant) - > MOUSE_ANIMATION_DELAY - } + // remove old placeholders + let removed = if let TargetZone::InitialPlaceholder(node_id) = + old_target_zone + { + if tree.get(&node_id).is_ok() { + TilingLayout::unmap_internal(&mut tree, &node_id); } - } else { - *instant = Some(Instant::now()); - false - }; - - if overdue { - let duration = if target_zone.is_window_zone() - && !old_target_zone.is_window_zone() - { - ANIMATION_DURATION * 2 - } else { - ANIMATION_DURATION - }; - - let mut tree = tree.copy_clone(); - - // remove old placeholders - let removed = if let TargetZone::InitialPlaceholder(node_id) = - old_target_zone - { - if tree.get(&node_id).is_ok() { - TilingLayout::unmap_internal(&mut tree, &node_id); - } - true - } else if let TargetZone::WindowSplit(node_id, _) = - old_target_zone + true + } else if let TargetZone::WindowSplit(node_id, _) = old_target_zone + { + if let Some(children) = tree + .get(&node_id) + .ok() + .and_then(|node| node.parent()) + .and_then(|parent_id| tree.get(parent_id).ok()) + .map(|node| node.children().clone()) { - if let Some(children) = tree - .get(&node_id) - .ok() - .and_then(|node| node.parent()) - .and_then(|parent_id| tree.get(parent_id).ok()) - .map(|node| node.children().clone()) - { - for id in children { - let matches = matches!( - tree.get(&id).unwrap().data(), - Data::Placeholder { - initial_placeholder: false, - .. - } - ); - - if matches { - TilingLayout::unmap_internal(&mut tree, &id); - break; + for id in children { + let matches = matches!( + tree.get(&id).unwrap().data(), + Data::Placeholder { + initial_placeholder: false, + .. } + ); + + if matches { + TilingLayout::unmap_internal(&mut tree, &id); + break; } } - true - } else if let TargetZone::GroupEdge(node_id, _) = - old_target_zone - { - if let Ok(node) = tree.get_mut(&node_id) { - match node.data_mut() { - Data::Group { pill_indicator, .. } => { - *pill_indicator = None; - } - _ => unreachable!(), + } + true + } else if let TargetZone::GroupEdge(node_id, _) = old_target_zone { + if let Ok(node) = tree.get_mut(&node_id) { + match node.data_mut() { + Data::Group { pill_indicator, .. } => { + *pill_indicator = None; } + _ => unreachable!(), } - true - } else if let TargetZone::GroupInterior(node_id, _) = - old_target_zone - { - if let Ok(node) = tree.get_mut(&node_id) { - match node.data_mut() { - Data::Group { pill_indicator, .. } => { - *pill_indicator = None; - } - _ => unreachable!(), + } + true + } else if let TargetZone::GroupInterior(node_id, _) = + old_target_zone + { + if let Ok(node) = tree.get_mut(&node_id) { + match node.data_mut() { + Data::Group { pill_indicator, .. } => { + *pill_indicator = None; } + _ => unreachable!(), } - true - } else { - false - }; + } + true + } else { + false + }; - // add placeholders - let added = if let TargetZone::WindowSplit(node_id, dir) = - &target_zone - { - let id = tree - .insert( - Node::new(Data::Placeholder { - last_geometry: Rectangle::from_loc_and_size( - (0, 0), - (100, 100), - ), - initial_placeholder: false, - }), - InsertBehavior::UnderNode(node_id), - ) - .unwrap(); - let orientation = - if matches!(dir, Direction::Left | Direction::Right) { - Orientation::Vertical - } else { - Orientation::Horizontal - }; - TilingLayout::new_group( - &mut tree, - &node_id, - &id, - orientation, + // add placeholders + let added = if let TargetZone::WindowSplit(node_id, dir) = + &target_zone + { + let id = tree + .insert( + Node::new(Data::Placeholder { + last_geometry: Rectangle::from_loc_and_size( + (0, 0), + (100, 100), + ), + initial_placeholder: false, + }), + InsertBehavior::UnderNode(node_id), ) .unwrap(); - if matches!(dir, Direction::Left | Direction::Up) { - tree.make_first_sibling(&id).unwrap(); - } + let orientation = + if matches!(dir, Direction::Left | Direction::Right) { + Orientation::Vertical + } else { + Orientation::Horizontal + }; + TilingLayout::new_group(&mut tree, &node_id, &id, orientation) + .unwrap(); + if matches!(dir, Direction::Left | Direction::Up) { + tree.make_first_sibling(&id).unwrap(); + } - true - } else if let TargetZone::GroupEdge(node_id, direction) = - &target_zone - { - if let Ok(node) = tree.get_mut(&node_id) { - match node.data_mut() { - Data::Group { pill_indicator, .. } => { - *pill_indicator = - Some(PillIndicator::Outer(*direction)); - } - _ => unreachable!(), + true + } else if let TargetZone::GroupEdge(node_id, direction) = + &target_zone + { + if let Ok(node) = tree.get_mut(&node_id) { + match node.data_mut() { + Data::Group { pill_indicator, .. } => { + *pill_indicator = + Some(PillIndicator::Outer(*direction)); } - true - } else { - false + _ => unreachable!(), } - } else if let TargetZone::GroupInterior(node_id, idx) = - &target_zone - { - if let Ok(node) = tree.get_mut(&node_id) { - match node.data_mut() { - Data::Group { pill_indicator, .. } => { - *pill_indicator = - Some(PillIndicator::Inner(*idx)); - } - _ => unreachable!(), + true + } else { + false + } + } else if let TargetZone::GroupInterior(node_id, idx) = &target_zone + { + if let Ok(node) = tree.get_mut(&node_id) { + match node.data_mut() { + Data::Group { pill_indicator, .. } => { + *pill_indicator = Some(PillIndicator::Inner(*idx)); } - true - } else { - false + _ => unreachable!(), } + true } else { false - }; - - if removed || added { - let blocker = TilingLayout::update_positions( - &output_data.output, - &mut tree, - *gaps, - ); - queue.push_tree(tree, duration, blocker); } + } else { + false + }; - *instant = None; - *old_target_zone = target_zone; + if removed || added { + let blocker = TilingLayout::update_positions( + &self.output, + &mut tree, + gaps, + ); + self.queue.push_tree(tree, duration, blocker); } - } else { + *instant = None; + *old_target_zone = target_zone; } + } else { + *instant = None; } } } - - None - } else { - None } - }) + + None + } else { + None + } } - pub fn mapped( - &self, - ) -> impl Iterator)> { - self.queues - .iter() - .flat_map(|(output_data, queue)| { - let tree = &queue.trees.back().unwrap().0; - if let Some(root) = tree.root_node_id() { - Some( + pub fn mapped(&self) -> impl Iterator)> { + let tree = &self.queue.trees.back().unwrap().0; + let iter = if let Some(root) = tree.root_node_id() { + Some( + tree.traverse_pre_order(root) + .unwrap() + .filter(|node| node.data().is_mapped(None)) + .filter(|node| match node.data() { + Data::Mapped { mapped, .. } => mapped.is_activated(false), + _ => unreachable!(), + }) + .map(|node| match node.data() { + Data::Mapped { + mapped, + last_geometry, + .. + } => (&self.output, mapped, { + let geo = last_geometry.clone(); + geo.to_global(&self.output) + }), + _ => unreachable!(), + }) + .chain( tree.traverse_pre_order(root) .unwrap() .filter(|node| node.data().is_mapped(None)) .filter(|node| match node.data() { - Data::Mapped { mapped, .. } => mapped.is_activated(false), + Data::Mapped { mapped, .. } => !mapped.is_activated(false), _ => unreachable!(), }) .map(|node| match node.data() { @@ -3571,72 +3375,60 @@ impl TilingLayout { mapped, last_geometry, .. - } => (&output_data.output, mapped, { - let mut geo = last_geometry.clone(); - geo.loc += output_data.location; - geo + } => (&self.output, mapped, { + let geo = last_geometry.clone(); + geo.to_global(&self.output) }), _ => unreachable!(), - }) - .chain( - tree.traverse_pre_order(root) - .unwrap() - .filter(|node| node.data().is_mapped(None)) - .filter(|node| match node.data() { - Data::Mapped { mapped, .. } => !mapped.is_activated(false), - _ => unreachable!(), - }) - .map(|node| match node.data() { - Data::Mapped { - mapped, - last_geometry, - .. - } => (&output_data.output, mapped, { - let mut geo = last_geometry.clone(); - geo.loc += output_data.location; - geo - }), - _ => unreachable!(), - }), - ), - ) - } else { - None - } - }) - .flatten() + }), + ), + ) + } else { + None + }; + iter.into_iter().flatten() } pub fn windows( &self, - ) -> impl Iterator)> + '_ { + ) -> impl Iterator)> + '_ { self.mapped().flat_map(|(output, mapped, geo)| { mapped.windows().map(move |(w, p)| { (output.clone(), w, { let mut geo = geo.clone(); - geo.loc += p; - geo.size -= p.to_size(); + geo.loc += p.as_global(); + geo.size -= p.to_size().as_global(); geo }) }) }) } - pub fn merge(&mut self, other: TilingLayout) { - for (output_data, mut src_queue) in other.queues { - let src = src_queue.trees.pop_back().unwrap().0; - let dst_queue = self.queues.entry(output_data.clone()).or_default(); - let mut dst = dst_queue.trees.back().unwrap().0.copy_clone(); + pub fn has_node(&self, node: &NodeId) -> bool { + let tree = &self.queue.trees.back().unwrap().0; + tree.root_node_id() + .map(|root| { + tree.traverse_pre_order_ids(root) + .unwrap() + .any(|id| &id == node) + }) + .unwrap_or(false) + } - let orientation = match output_data.output.geometry().size { - x if x.w >= x.h => Orientation::Vertical, - _ => Orientation::Horizontal, - }; - TilingLayout::merge_trees(src, &mut dst, orientation); + pub fn merge(&mut self, mut other: TilingLayout) { + let gaps = self.gaps(); - let blocker = TilingLayout::update_positions(&output_data.output, &mut dst, self.gaps); - dst_queue.push_tree(dst, ANIMATION_DURATION, blocker); - } + let src = other.queue.trees.pop_back().unwrap().0; + let mut dst = self.queue.trees.back().unwrap().0.copy_clone(); + + let orientation = match self.output.geometry().size { + x if x.w >= x.h => Orientation::Vertical, + _ => Orientation::Horizontal, + }; + TilingLayout::merge_trees(src, &mut dst, orientation); + + let blocker = TilingLayout::update_positions(&self.output, &mut dst, gaps); + self.queue.push_tree(dst, ANIMATION_DURATION, blocker); } fn merge_trees(src: Tree, dst: &mut Tree, orientation: Orientation) { @@ -3678,15 +3470,15 @@ impl TilingLayout { } } - pub fn render_output( + pub fn render( &self, renderer: &mut R, - output: &Output, seat: Option<&Seat>, - non_exclusive_zone: Rectangle, + non_exclusive_zone: Rectangle, overview: (OverviewMode, Option<(SwapIndicator, Option<&Tree>)>), resize_indicator: Option<(ResizeMode, ResizeIndicator)>, indicator_thickness: u8, + theme: &cosmic::theme::CosmicTheme, ) -> Result< ( Vec>, @@ -3704,27 +3496,23 @@ impl TilingLayout { #[cfg(feature = "debug")] puffin::profile_function!(); - let output_scale = output.current_scale().fractional_scale(); + let output_scale = self.output.current_scale().fractional_scale(); - if !self.queues.contains_key(output) { - return Err(OutputNotMapped); - } - - let queue = self.queues.get(output).unwrap(); - let (target_tree, duration, _) = if queue.animation_start.is_some() { - queue + let (target_tree, duration, _) = if self.queue.animation_start.is_some() { + self.queue .trees .get(1) .expect("Animation ongoing, should have two trees") } else { - queue.trees.front().unwrap() + self.queue.trees.front().unwrap() }; - let reference_tree = queue + let reference_tree = self + .queue .animation_start .is_some() - .then(|| &queue.trees.front().unwrap().0); + .then(|| &self.queue.trees.front().unwrap().0); - let percentage = if let Some(animation_start) = queue.animation_start { + let percentage = if let Some(animation_start) = self.queue.animation_start { let percentage = Instant::now().duration_since(animation_start).as_millis() as f32 / duration.as_millis() as f32; ease(EaseInOutCubic, 0.0, 1.0, percentage) @@ -3761,6 +3549,7 @@ impl TilingLayout { is_mouse_tiling, swap_desc.clone(), overview.1.as_ref().and_then(|(_, tree)| tree.clone()), + theme, )) } else { None @@ -3798,6 +3587,7 @@ impl TilingLayout { is_mouse_tiling, swap_desc.clone(), overview.1.as_ref().and_then(|(_, tree)| tree.clone()), + theme, )) } else { None @@ -3814,7 +3604,7 @@ impl TilingLayout { old_geometries, is_overview, seat, - output, + &self.output, percentage, draw_groups, if let Some(transition) = draw_groups { @@ -3832,6 +3622,7 @@ impl TilingLayout { swap_desc.clone(), &self.swapping_stack_surface_id, &self.placeholder_id, + theme, ); window_elements.extend(w_elements); popup_elements.extend(p_elements); @@ -3843,6 +3634,11 @@ impl TilingLayout { Ok((window_elements, popup_elements)) } + + fn gaps(&self) -> (i32, i32) { + let g = self.theme.cosmic().gaps; + (g.0 as i32, g.1 as i32) + } } const GAP_KEYBOARD: i32 = 8; @@ -3861,8 +3657,8 @@ fn swap_factor(size: Size) -> f64 { fn swap_geometry( size: Size, - relative_to: Rectangle, -) -> Rectangle { + relative_to: Rectangle, +) -> Rectangle { let factor = swap_factor(size); let new_size = Size::from(( @@ -3881,7 +3677,7 @@ fn swap_geometry( fn geometries_for_groupview<'a, R>( tree: &Tree, renderer: impl Into>, - non_exclusive_zone: Rectangle, + non_exclusive_zone: Rectangle, seat: Option<&Seat>, alpha: f32, transition: f32, @@ -3890,8 +3686,9 @@ fn geometries_for_groupview<'a, R>( mouse_tiling: Option>, swap_desc: Option, swap_tree: Option<&Tree>, + theme: &cosmic::theme::CosmicTheme, ) -> ( - HashMap>, + HashMap>, Vec>, ) where @@ -3921,7 +3718,7 @@ where } let mut elements = Vec::new(); - let mut geometries: HashMap> = HashMap::new(); + let mut geometries: HashMap> = HashMap::new(); let alpha = alpha * transition; let focused = seat @@ -3929,9 +3726,7 @@ where seat.get_keyboard() .unwrap() .current_focus() - .and_then(|target| { - TilingLayout::currently_focused_node(&tree, &seat.active_output(), target) - }) + .and_then(|target| TilingLayout::currently_focused_node(&tree, target)) }) .map(|(id, _)| id); let focused_geo = if let Some(focused_id) = focused.as_ref() { @@ -4049,6 +3844,8 @@ where ) }; + let group_color = group_color(theme); + match data { Data::Group { orientation, @@ -4086,7 +3883,7 @@ where if render_active_child { 16 } else { 8 }, alpha * if render_potential_group { 0.40 } else { 1.0 }, output_scale, - GROUP_COLOR, + group_color, ) .into(), ); @@ -4104,7 +3901,7 @@ where 8, alpha * 0.40, output_scale, - GROUP_COLOR, + group_color, ) .into(), ); @@ -4168,7 +3965,7 @@ where 8, alpha * 0.15, output_scale, - GROUP_COLOR, + group_color, ) .into(), ); @@ -4232,7 +4029,7 @@ where pill_geo, 8., alpha * 0.4, - GROUP_COLOR, + group_color, ) .into(), ); @@ -4262,13 +4059,13 @@ where } else { 0.15 }, - GROUP_COLOR, + group_color, ) .into(), ); } let swap_geo = swap_geometry( - geo.size, + geo.size.as_logical(), focused_geo.unwrap_or({ let mut geo = non_exclusive_zone; geo.loc += (WINDOW_BACKDROP_BORDER, WINDOW_BACKDROP_BORDER).into(); @@ -4338,7 +4135,7 @@ where ), 8., alpha * 0.4, - GROUP_COLOR, + group_color, ) .into(), ); @@ -4380,7 +4177,7 @@ where ), 8., alpha * 0.4, - GROUP_COLOR, + group_color, ) .into(), ); @@ -4411,7 +4208,7 @@ where 8, alpha * 0.40, output_scale, - GROUP_COLOR, + group_color, ) .into(), ); @@ -4419,6 +4216,8 @@ where geo.size -= (gap * 2, gap * 2).into(); } + let accent = theme.accent.base; + if focused .as_ref() .map(|focused_id| { @@ -4433,9 +4232,9 @@ where Some(Some(TargetZone::WindowStack(stack_id, _))) if *stack_id == node_id => { - ACTIVE_GROUP_COLOR + [accent.red, accent.green, accent.blue] } - _ => GROUP_COLOR, + _ => group_color, }; geo.loc += (WINDOW_BACKDROP_BORDER, WINDOW_BACKDROP_BORDER).into(); geo.size -= @@ -4450,7 +4249,9 @@ where * if focused .as_ref() .map(|focused_id| focused_id == &node_id) - .unwrap_or(color == ACTIVE_GROUP_COLOR) + .unwrap_or( + color == [accent.red, accent.green, accent.blue], + ) { 0.4 } else { @@ -4469,7 +4270,7 @@ where if matches!(swap_desc, Some(ref desc) if &desc.node == &node_id && desc.stack_window.is_none()) { let swap_geo = swap_geometry( - geo.size, + geo.size.as_logical(), focused_geo.unwrap_or({ let mut geo = non_exclusive_zone; geo.loc += (WINDOW_BACKDROP_BORDER, WINDOW_BACKDROP_BORDER).into(); @@ -4504,7 +4305,7 @@ where geo, 8., alpha * 0.4, - GROUP_COLOR, + group_color, ) .into(), ); @@ -4523,7 +4324,7 @@ fn render_old_tree( reference_tree: &Tree, target_tree: &Tree, renderer: &mut R, - geometries: Option>>, + geometries: Option>>, output_scale: f64, percentage: f32, is_swap_mode: bool, @@ -4585,7 +4386,10 @@ where .unwrap_or(*original_geo); let crop_rect = geo.clone(); - let original_location = original_geo.loc.to_physical_precise_round(output_scale) + let original_location = original_geo + .loc + .as_logical() + .to_physical_precise_round(output_scale) - mapped .geometry() .loc @@ -4605,7 +4409,9 @@ where let cropped = CropRenderElement::from_element( elem, output_scale, - crop_rect.to_physical_precise_round(output_scale), + crop_rect + .as_logical() + .to_physical_precise_round(output_scale), )?; let rescaled = RescaleRenderElement::from_element( cropped, @@ -4615,6 +4421,7 @@ where let relocated = RelocateRenderElement::from_element( rescaled, (geo.loc - original_geo.loc) + .as_logical() .to_physical_precise_round(output_scale), Relocate::Relative, ); @@ -4626,7 +4433,9 @@ where let cropped = CropRenderElement::from_element( elem, output_scale, - crop_rect.to_physical_precise_round(output_scale), + crop_rect + .as_logical() + .to_physical_precise_round(output_scale), )?; let rescaled = RescaleRenderElement::from_element( cropped, @@ -4636,6 +4445,7 @@ where let relocated = RelocateRenderElement::from_element( rescaled, (geo.loc - original_geo.loc) + .as_logical() .to_physical_precise_round(output_scale), Relocate::Relative, ); @@ -4655,9 +4465,9 @@ fn render_new_tree( target_tree: &Tree, reference_tree: Option<&Tree>, renderer: &mut R, - non_exclusive_zone: Rectangle, - geometries: Option>>, - old_geometries: Option>>, + non_exclusive_zone: Rectangle, + geometries: Option>>, + old_geometries: Option>>, is_overview: bool, seat: Option<&Seat>, output: &Output, @@ -4669,6 +4479,7 @@ fn render_new_tree( swap_desc: Option, swapping_stack_surface_id: &Id, placeholder_id: &Id, + theme: &cosmic::theme::CosmicTheme, ) -> ( Vec>, Vec>, @@ -4685,13 +4496,7 @@ where seat.get_keyboard() .unwrap() .current_focus() - .and_then(|target| { - TilingLayout::currently_focused_node( - &target_tree, - &seat.active_output(), - target, - ) - }) + .and_then(|target| TilingLayout::currently_focused_node(&target_tree, target)) }) .map(|(id, _)| id); let focused_geo = if let Some(focused) = focused.as_ref() { @@ -4734,7 +4539,8 @@ where let (swap_indicator, swap_tree) = overview.1.unzip(); let swap_tree = swap_tree.flatten().filter(|_| is_active_output); let swap_desc = swap_desc.filter(|_| is_active_output); - + let window_hint = crate::theme::active_window_hint(theme); + let group_color = group_color(theme); // render placeholder, if we are swapping to an empty workspace if target_tree.root_node_id().is_none() && swap_desc.is_some() { window_elements.push( @@ -4744,7 +4550,7 @@ where focused_geo, 8., transition.unwrap_or(1.0) * 0.4, - GROUP_COLOR, + group_color, ) .into(), ); @@ -4776,9 +4582,11 @@ where 4, output_scale, transition.unwrap_or(1.0), + [window_hint.red, window_hint.green, window_hint.blue], )); - let render_loc = (swap_geo.loc - window_geo.loc).to_physical_precise_round(output_scale); + let render_loc = + (swap_geo.loc.as_logical() - window_geo.loc).to_physical_precise_round(output_scale); swap_elements.extend( window @@ -4792,7 +4600,10 @@ where .map(|window| { CosmicMappedRenderElement::GrabbedWindow(RescaleRenderElement::from_element( window, - swap_geo.loc.to_physical_precise_round(output_scale), + swap_geo + .loc + .as_logical() + .to_physical_precise_round(output_scale), ease( Linear, 1.0, @@ -4933,7 +4744,7 @@ where geo, 8., 0.4, - GROUP_COLOR, + group_color, )); } @@ -4960,6 +4771,7 @@ where }, output_scale, 1.0, + [window_hint.red, window_hint.green, window_hint.blue], )); } @@ -4985,12 +4797,12 @@ where .unwrap_or(false)) { if let Some(swap) = swap_indicator.as_ref() { - swap.resize(geo.size); - swap.output_enter(output, output_geo); + swap.resize(geo.size.as_logical()); + swap.output_enter(output, output_geo.as_logical()); swap_elements.extend( swap.render_elements::>( renderer, - geo.loc.to_physical_precise_round(output_scale), + geo.loc.as_logical().to_physical_precise_round(output_scale), output_scale.into(), alpha * overview.0.alpha().unwrap_or(1.0), ) @@ -5006,8 +4818,8 @@ where geo.loc -= (18, 18).into(); geo.size += (36, 36).into(); - resize.resize(geo.size); - resize.output_enter(output, output_geo); + resize.resize(geo.size.as_logical()); + resize.output_enter(output, output_geo.as_logical()); let possible_edges = TilingLayout::possible_resizes(target_tree, node_id.clone()); if !possible_edges.is_empty() { @@ -5026,7 +4838,7 @@ where resize .render_elements::>( renderer, - geo.loc.to_physical_precise_round(output_scale), + geo.loc.as_logical().to_physical_precise_round(output_scale), output_scale.into(), alpha * mode.alpha().unwrap_or(1.0), ) @@ -5039,7 +4851,7 @@ where } if let Data::Mapped { mapped, .. } = data { - let original_location = (original_geo.loc - mapped.geometry().loc) + let original_location = (original_geo.loc.as_logical() - mapped.geometry().loc) .to_physical_precise_round(output_scale); let (mut w_elements, p_elements) = mapped @@ -5069,7 +4881,7 @@ where }) .unwrap_or(false) { - let mut geo = mapped.active_window_geometry(); + let mut geo = mapped.active_window_geometry().as_local(); geo.loc += original_geo.loc; w_elements.insert( 0, @@ -5079,7 +4891,7 @@ where geo, 0.0, 0.3, - GROUP_COLOR, + group_color, )), ) } @@ -5090,16 +4902,22 @@ where let cropped = CropRenderElement::from_element( elem, output_scale, - crop_rect.to_physical_precise_round(output_scale), + crop_rect + .as_logical() + .to_physical_precise_round(output_scale), )?; let rescaled = RescaleRenderElement::from_element( cropped, - original_geo.loc.to_physical_precise_round(output_scale), + original_geo + .loc + .as_logical() + .to_physical_precise_round(output_scale), scale, ); let relocated = RelocateRenderElement::from_element( rescaled, (geo.loc - original_geo.loc) + .as_logical() .to_physical_precise_round(output_scale), Relocate::Relative, ); @@ -5111,16 +4929,22 @@ where let cropped = CropRenderElement::from_element( elem, output_scale, - crop_rect.to_physical_precise_round(output_scale), + crop_rect + .as_logical() + .to_physical_precise_round(output_scale), )?; let rescaled = RescaleRenderElement::from_element( cropped, - original_geo.loc.to_physical_precise_round(output_scale), + original_geo + .loc + .as_logical() + .to_physical_precise_round(output_scale), scale, ); let relocated = RelocateRenderElement::from_element( rescaled, (geo.loc - original_geo.loc) + .as_logical() .to_physical_precise_round(output_scale), Relocate::Relative, ); @@ -5132,16 +4956,22 @@ where let cropped = CropRenderElement::from_element( elem, output_scale, - crop_rect.to_physical_precise_round(output_scale), + crop_rect + .as_logical() + .to_physical_precise_round(output_scale), )?; let rescaled = RescaleRenderElement::from_element( cropped, - original_geo.loc.to_physical_precise_round(output_scale), + original_geo + .loc + .as_logical() + .to_physical_precise_round(output_scale), scale, ); let relocated = RelocateRenderElement::from_element( rescaled, (geo.loc - original_geo.loc) + .as_logical() .to_physical_precise_round(output_scale), Relocate::Relative, ); @@ -5187,10 +5017,10 @@ where (window_elements, popup_elements) } -fn scale_to_center( - old_geo: &Rectangle, - new_geo: &Rectangle, -) -> (f64, Point) { +fn scale_to_center( + old_geo: &Rectangle, + new_geo: &Rectangle, +) -> (f64, Point) { let scale_w = new_geo.size.w as f64 / old_geo.size.w as f64; let scale_h = new_geo.size.h as f64 / old_geo.size.h as f64; diff --git a/src/shell/mod.rs b/src/shell/mod.rs index 3640353f..4bda1f7a 100644 --- a/src/shell/mod.rs +++ b/src/shell/mod.rs @@ -1,12 +1,11 @@ use calloop::LoopHandle; +use indexmap::IndexMap; use serde::{Deserialize, Serialize}; use std::{ - cell::RefCell, collections::HashMap, sync::atomic::{AtomicBool, Ordering}, time::{Duration, Instant}, }; -use tracing::warn; use wayland_backend::server::ClientId; use cosmic_protocols::workspace::v1::server::zcosmic_workspace_handle_v1::State as WState; @@ -21,7 +20,7 @@ use smithay::{ }, output::Output, reexports::wayland_server::{protocol::wl_surface::WlSurface, Client, DisplayHandle}, - utils::{Logical, Point, Rectangle, Serial, SERIAL_COUNTER}, + utils::{Point, Rectangle, Serial, SERIAL_COUNTER}, wayland::{ compositor::with_states, seat::WaylandFocus, @@ -36,7 +35,7 @@ use smithay::{ }; use crate::{ - config::{Config, KeyModifiers, KeyPattern, OutputConfig, WorkspaceMode as ConfigMode}, + config::{Config, KeyModifiers, KeyPattern, WorkspaceMode}, state::client_has_security_context, utils::prelude::*, wayland::protocols::{ @@ -64,10 +63,7 @@ use self::{ }, focus::target::KeyboardFocusTarget, grabs::ResizeEdge, - layout::{ - floating::{FloatingLayout, ResizeState}, - tiling::{NodeDesc, TilingLayout}, - }, + layout::{floating::ResizeState, tiling::NodeDesc}, }; const ANIMATION_DURATION: Duration = Duration::from_millis(200); @@ -143,13 +139,19 @@ impl ResizeMode { } } +#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)] +pub enum MaximizeMode { + Floating, + OnTop, +} + #[derive(Debug)] pub struct Shell { + pub workspaces: Workspaces, + pub popups: PopupManager, - pub outputs: Vec, - pub workspaces: WorkspaceMode, - pub tiling_enabled: bool, - pub pending_windows: Vec<(CosmicSurface, Seat)>, + pub maximize_mode: MaximizeMode, + pub pending_windows: Vec<(CosmicSurface, Seat, Option)>, pub pending_layers: Vec<(LayerSurface, Output, Seat)>, pub override_redirect_windows: Vec, @@ -160,7 +162,7 @@ pub struct Shell { pub xdg_shell_state: XdgShellState, pub workspace_state: WorkspaceState, - gaps: (u8, u8), + theme: cosmic::Theme, overview_mode: OverviewMode, swap_indicator: Option, resize_mode: ResizeMode, @@ -179,11 +181,11 @@ pub struct Shell { pub struct WorkspaceSet { previously_active: Option<(usize, Instant)>, active: usize, - amount: WorkspaceAmount, group: WorkspaceGroupHandle, idx: usize, tiling_enabled: bool, - gaps: (u8, u8), + output: Output, + theme: cosmic::Theme, pub(crate) workspaces: Vec, } @@ -195,10 +197,11 @@ pub enum WorkspaceAmount { fn create_workspace( state: &mut WorkspaceUpdateGuard<'_, State>, + output: &Output, group_handle: &WorkspaceGroupHandle, active: bool, tiling: bool, - gaps: (u8, u8), + theme: cosmic::Theme, ) -> Workspace { let workspace_handle = state.create_workspace(&group_handle).unwrap(); if active { @@ -208,22 +211,29 @@ fn create_workspace( &workspace_handle, [WorkspaceCapabilities::Activate].into_iter(), ); - Workspace::new(workspace_handle, tiling, gaps) + Workspace::new(workspace_handle, output.clone(), tiling, theme.clone()) } impl WorkspaceSet { fn new( state: &mut WorkspaceUpdateGuard<'_, State>, + output: &Output, amount: WorkspaceAmount, idx: usize, tiling_enabled: bool, - gaps: (u8, u8), + theme: cosmic::Theme, ) -> WorkspaceSet { let group_handle = state.create_workspace_group(); - let workspaces = match amount { WorkspaceAmount::Dynamic => { - let workspace = create_workspace(state, &group_handle, true, tiling_enabled, gaps); + let workspace = create_workspace( + state, + output, + &group_handle, + true, + tiling_enabled, + theme.clone(), + ); workspace_set_idx(state, 1, idx, &workspace.handle); state.set_workspace_capabilities( &workspace.handle, @@ -233,8 +243,14 @@ impl WorkspaceSet { } WorkspaceAmount::Static(len) => (0..len) .map(|i| { - let workspace = - create_workspace(state, &group_handle, i == 0, tiling_enabled, gaps); + let workspace = create_workspace( + state, + output, + &group_handle, + i == 0, + tiling_enabled, + theme.clone(), + ); workspace_set_idx(state, i + 1, idx, &workspace.handle); state.set_workspace_capabilities( &workspace.handle, @@ -248,12 +264,12 @@ impl WorkspaceSet { WorkspaceSet { previously_active: None, active: 0, - amount, group: group_handle, idx, tiling_enabled, - gaps, + theme, workspaces, + output: output.clone(), } } @@ -278,63 +294,59 @@ impl WorkspaceSet { } } - fn refresh<'a>( + fn set_output( &mut self, - state: &mut WorkspaceState, + new_output: &Output, toplevel_info: &mut ToplevelInfoState, - outputs: impl Iterator)>, ) { + for workspace in &mut self.workspaces { + workspace.set_output(new_output, toplevel_info); + } + self.output = new_output.clone(); + } + + fn refresh<'a>(&mut self) { if let Some((_, start)) = self.previously_active { if Instant::now().duration_since(start).as_millis() >= ANIMATION_DURATION.as_millis() { self.previously_active = None; } } else { - match self.amount { - WorkspaceAmount::Dynamic => self.ensure_last_empty(state, outputs), - WorkspaceAmount::Static(len) => { - self.ensure_static(len as usize, state, toplevel_info, outputs) - } - } - self.workspaces[self.active].refresh(); } } - fn ensure_last_empty<'a>( - &mut self, - state: &mut WorkspaceState, - outputs: impl Iterator)>, - ) { - let mut state = state.update(); + fn add_empty_workspace(&mut self, state: &mut WorkspaceUpdateGuard) { + let workspace = create_workspace( + state, + &self.output, + &self.group, + false, + self.tiling_enabled, + self.theme.clone(), + ); + workspace_set_idx( + state, + self.workspaces.len() as u8 + 1, + self.idx, + &workspace.handle, + ); + self.workspaces.push(workspace); + } + fn ensure_last_empty<'a>(&mut self, state: &mut WorkspaceUpdateGuard) { // add empty at the end, if necessary - if self.workspaces.last().unwrap().windows().next().is_some() { - let mut workspace = create_workspace( - &mut state, - &self.group, - false, - self.tiling_enabled, - self.gaps, - ); - workspace_set_idx( - &mut state, - self.workspaces.len() as u8 + 1, - self.idx, - &workspace.handle, - ); - state.set_workspace_capabilities( - &workspace.handle, - [WorkspaceCapabilities::Activate].into_iter(), - ); - for (output, location) in outputs { - workspace.map_output(output, location); - } - self.workspaces.push(workspace); + if self + .workspaces + .last() + .map(|last| last.windows().next().is_some()) + .unwrap_or(true) + { + self.add_empty_workspace(state); } + // remove empty workspaces in between, if they are not active let len = self.workspaces.len(); let mut keep = vec![true; len]; - // remove empty workspaces in between, if they are not active for (i, workspace) in self.workspaces.iter().enumerate() { let has_windows = workspace.windows().next().is_some(); @@ -354,20 +366,18 @@ impl WorkspaceSet { if keep.iter().any(|val| *val == false) { for (i, workspace) in self.workspaces.iter().enumerate() { - workspace_set_idx(&mut state, i as u8 + 1, self.idx, &workspace.handle); + workspace_set_idx(state, i as u8 + 1, self.idx, &workspace.handle); } } } - fn ensure_static<'a>( + fn ensure_static( &mut self, amount: usize, - state: &mut WorkspaceState, + state: &mut WorkspaceUpdateGuard, toplevel_info: &mut ToplevelInfoState, - outputs: impl Iterator)>, ) { if amount < self.workspaces.len() { - let mut state = state.update(); // merge last ones let overflow = self.workspaces.split_off(amount); if self.active >= self.workspaces.len() { @@ -376,7 +386,12 @@ impl WorkspaceSet { } let last_space = self.workspaces.last_mut().unwrap(); - for workspace in overflow { + for mut workspace in overflow { + if last_space.fullscreen.is_some() { + // Don't handle the returned original workspace, for this nieche case. + let _ = workspace.remove_fullscreen(); + } + for element in workspace.mapped() { // fixup toplevel state for (toplevel, _) in element.windows() { @@ -386,27 +401,26 @@ impl WorkspaceSet { } last_space.tiling_layer.merge(workspace.tiling_layer); last_space.floating_layer.merge(workspace.floating_layer); - last_space - .fullscreen - .extend(workspace.fullscreen.into_iter()); + if workspace.fullscreen.is_some() { + last_space.fullscreen = workspace.fullscreen; + } state.remove_workspace(workspace.handle); } last_space.refresh(); } else if amount > self.workspaces.len() { - let mut state = state.update(); // add empty ones - let outputs = outputs.collect::>(); while amount > self.workspaces.len() { - let mut workspace = create_workspace( - &mut state, + let workspace = create_workspace( + state, + &self.output, &self.group, false, self.tiling_enabled, - self.gaps, + self.theme.clone(), ); workspace_set_idx( - &mut state, + state, self.workspaces.len() as u8 + 1, self.idx, &workspace.handle, @@ -415,9 +429,6 @@ impl WorkspaceSet { &workspace.handle, [WorkspaceCapabilities::Activate].into_iter(), ); - for &(output, location) in outputs.iter() { - workspace.map_output(output, location); - } self.workspaces.push(workspace); } } @@ -441,137 +452,395 @@ impl WorkspaceSet { } #[derive(Debug)] -pub enum WorkspaceMode { - OutputBound(HashMap, WorkspaceAmount), - Global(WorkspaceSet), +pub struct Workspaces { + sets: IndexMap, + backup_set: Option, + amount: WorkspaceAmount, + mode: WorkspaceMode, + tiling_enabled: bool, + theme: cosmic::Theme, } -impl WorkspaceMode { - pub fn new( - config: crate::config::WorkspaceMode, - amount: WorkspaceAmount, - state: &mut WorkspaceUpdateGuard<'_, State>, - tiling_enabled: bool, - gaps: (u8, u8), - ) -> WorkspaceMode { - match config { - crate::config::WorkspaceMode::Global => { - WorkspaceMode::Global(WorkspaceSet::new(state, amount, 0, tiling_enabled, gaps)) +impl Workspaces { + pub fn new(config: &Config, theme: cosmic::Theme) -> Workspaces { + Workspaces { + sets: IndexMap::new(), + backup_set: None, + amount: config.static_conf.workspace_amount, + mode: config.static_conf.workspace_mode, + tiling_enabled: config.static_conf.tiling_enabled, + theme, + } + } + + pub fn add_output( + &mut self, + output: &Output, + workspace_state: &mut WorkspaceUpdateGuard<'_, State>, + toplevel_info_state: &mut ToplevelInfoState, + ) { + if self.sets.contains_key(output) { + return; + } + + let set = self + .backup_set + .take() + .map(|mut set| { + set.set_output(output, toplevel_info_state); + set + }) + .unwrap_or_else(|| { + WorkspaceSet::new( + workspace_state, + &output, + self.amount, + self.sets.len(), + self.tiling_enabled, + self.theme.clone(), + ) + }); + workspace_state.add_group_output(&set.group, &output); + + self.sets.insert(output.clone(), set); + for workspace in &mut self.sets.get_mut(output).unwrap().workspaces { + workspace.set_output(output, toplevel_info_state); + } + } + + pub fn remove_output( + &mut self, + output: &Output, + seats: impl Iterator>, + workspace_state: &mut WorkspaceUpdateGuard<'_, State>, + toplevel_info_state: &mut ToplevelInfoState, + ) { + if !self.sets.contains_key(output) { + return; + } + + if let Some(set) = self.sets.remove(output) { + { + let map = layer_map_for_output(output); + for surface in map.layers() { + surface.layer_surface().send_close(); + } } - crate::config::WorkspaceMode::OutputBound => { - WorkspaceMode::OutputBound(HashMap::new(), amount) + + // TODO: Heuristic which output to move to. + // It is supposed to be the *most* internal, we just pick the first one for now + // and hope enumeration order works in our favor. + let new_output = self.sets.get_index(0).map(|(o, _)| o.clone()); + if let Some(new_output) = new_output { + for seat in seats { + if &seat.active_output() == output { + seat.set_active_output(&new_output); + } + } + + let new_set = self.sets.get_mut(&new_output).unwrap(); + let workspace_group = new_set.group; + for mut workspace in set.workspaces { + // update workspace protocol state + workspace_state.remove_workspace(workspace.handle); + let workspace_handle = + workspace_state.create_workspace(&workspace_group).unwrap(); + workspace_state.set_workspace_capabilities( + &workspace_handle, + [WorkspaceCapabilities::Activate].into_iter(), + ); + let old_workspace_handle = workspace.handle; + workspace.handle = workspace_handle; + + for window in workspace.mapped() { + for (surface, _) in window.windows() { + toplevel_info_state + .toplevel_leave_workspace(&surface, &old_workspace_handle); + toplevel_info_state + .toplevel_enter_workspace(&surface, &workspace.handle); + } + } + + // update mapping + workspace.set_output(&new_output, toplevel_info_state); + workspace.refresh(); + + // TODO: merge if mode = static + new_set.workspaces.push(workspace); + } + if self.mode == WorkspaceMode::OutputBound { + workspace_state.remove_workspace_group(set.group); + } else { + workspace_state.remove_group_output(&workspace_group, output); + } + + for (i, set) in self.sets.values_mut().enumerate() { + set.update_idx(workspace_state, i); + } + } else { + workspace_state.remove_group_output(&set.group, output); + self.backup_set = Some(set); } + + self.refresh(workspace_state, toplevel_info_state) } } - pub fn get(&self, num: usize, output: &Output) -> Option<&Workspace> { - match self { - WorkspaceMode::Global(set) => set.workspaces.get(num), - WorkspaceMode::OutputBound(sets, _) => { - sets.get(output).and_then(|set| set.workspaces.get(num)) + pub fn update_config( + &mut self, + config: &Config, + workspace_state: &mut WorkspaceUpdateGuard<'_, State>, + toplevel_info_state: &mut ToplevelInfoState, + ) { + if self.sets.len() <= 1 { + return; + } + + match (self.mode, config.static_conf.workspace_mode) { + (WorkspaceMode::Global, WorkspaceMode::OutputBound) => { + // We basically just unlink the existing spaces, so nothing needs to be updated + } + (WorkspaceMode::OutputBound, WorkspaceMode::Global) => { + // lets construct an iterator of all the pairs of workspaces we have to "merge" + let mut pairs = Vec::new(); + if let Some(max) = self.sets.values().map(|set| set.workspaces.len()).max() { + let offset = self.sets.values().map(|set| set.active).max().unwrap(); + for i in 0..max { + pairs.push( + self.sets + .values() + .map(|set| { + let idx = set.active as isize + i as isize - offset as isize; + if idx < 0 || idx >= set.workspaces.len() as isize { + None + } else { + Some(idx) + } + }) + .collect::>(), + ); + } + } + + for (j, pair) in pairs.iter().enumerate() { + for (i, x) in pair.iter().enumerate() { + // Fill up sets, where necessary + if x.is_none() { + // create missing workspace + let (output, set) = self.sets.get_index_mut(i).unwrap(); + set.workspaces.insert( + j, + create_workspace( + workspace_state, + output, + &set.group, + false, + config.static_conf.tiling_enabled, + self.theme.clone(), + ), + ); + } + // Otherwise we are fine + } + } + // fixup indices + for (i, set) in self.sets.values_mut().enumerate() { + set.update_idx(workspace_state, i); + } + } + _ => {} + }; + + self.refresh(workspace_state, toplevel_info_state) + } + + pub fn refresh( + &mut self, + workspace_state: &mut WorkspaceUpdateGuard<'_, State>, + toplevel_info_state: &mut ToplevelInfoState, + ) { + match self.mode { + WorkspaceMode::Global => { + match self.amount { + WorkspaceAmount::Dynamic => { + // this should never happen + let max = self + .sets + .values() + .map(|set| set.workspaces.len()) + .max() + .unwrap_or_default(); + for set in self + .sets + .values_mut() + .filter(|set| set.workspaces.len() < max) + { + while set.workspaces.len() < max { + set.add_empty_workspace(workspace_state) + } + } + + // add empty at the end, if necessary + if self + .sets + .values() + .flat_map(|set| set.workspaces.last()) + .any(|w| w.mapped().next().is_some()) + { + for set in self.sets.values_mut() { + set.add_empty_workspace(workspace_state); + } + } + + // remove empty workspaces in between, if they are not active + let len = self.sets[0].workspaces.len(); + let mut active = self.sets[0].active; + let mut keep = vec![true; len]; + for i in 0..len { + let has_windows = self + .sets + .values() + .any(|s| s.workspaces[i].windows().next().is_some()); + + if !has_windows && i != active && i != len - 1 { + for workspace in self.sets.values().map(|s| &s.workspaces[i]) { + workspace_state.remove_workspace(workspace.handle); + } + keep[i] = false; + } + } + + self.sets.values_mut().for_each(|s| { + let mut iter = keep.iter(); + s.workspaces.retain(|_| *iter.next().unwrap()); + }); + active -= keep.iter().take(active + 1).filter(|keep| !**keep).count(); + self.sets.values_mut().for_each(|s| { + s.active = active; + }); + + if keep.iter().any(|val| *val == false) { + for set in self.sets.values_mut() { + for (i, workspace) in set.workspaces.iter().enumerate() { + workspace_set_idx( + workspace_state, + i as u8 + 1, + set.idx, + &workspace.handle, + ); + } + } + } + } + WorkspaceAmount::Static(amount) => { + for set in self.sets.values_mut() { + set.ensure_static(amount as usize, workspace_state, toplevel_info_state) + } + } + } } + WorkspaceMode::OutputBound => match self.amount { + WorkspaceAmount::Dynamic => { + for set in self.sets.values_mut() { + set.ensure_last_empty(workspace_state); + } + } + WorkspaceAmount::Static(amount) => { + for set in self.sets.values_mut() { + set.ensure_static(amount as usize, workspace_state, toplevel_info_state) + } + } + }, } + + for set in self.sets.values_mut() { + set.refresh() + } + } + + pub fn get(&self, num: usize, output: &Output) -> Option<&Workspace> { + self.sets + .get(output) + .and_then(|set| set.workspaces.get(num)) } pub fn get_mut(&mut self, num: usize, output: &Output) -> Option<&mut Workspace> { - match self { - WorkspaceMode::Global(set) => set.workspaces.get_mut(num), - WorkspaceMode::OutputBound(sets, _) => sets - .get_mut(output) - .and_then(|set| set.workspaces.get_mut(num)), - } + self.sets + .get_mut(output) + .and_then(|set| set.workspaces.get_mut(num)) } pub fn active(&self, output: &Output) -> (Option<(&Workspace, Instant)>, &Workspace) { - match self { - WorkspaceMode::Global(set) => ( - set.previously_active - .map(|(idx, start)| (&set.workspaces[idx], start)), - &set.workspaces[set.active], - ), - WorkspaceMode::OutputBound(sets, _) => { - let set = sets.get(output).unwrap(); - ( - set.previously_active - .map(|(idx, start)| (&set.workspaces[idx], start)), - &set.workspaces[set.active], - ) - } - } + let set = self.sets.get(output).or(self.backup_set.as_ref()).unwrap(); + ( + set.previously_active + .map(|(idx, start)| (&set.workspaces[idx], start)), + &set.workspaces[set.active], + ) } pub fn active_mut(&mut self, output: &Output) -> &mut Workspace { - match self { - WorkspaceMode::Global(set) => &mut set.workspaces[set.active], - WorkspaceMode::OutputBound(sets, _) => { - let set = sets.get_mut(output).unwrap(); - &mut set.workspaces[set.active] - } - } + let set = self + .sets + .get_mut(output) + .or(self.backup_set.as_mut()) + .unwrap(); + &mut set.workspaces[set.active] } pub fn active_num(&self, output: &Output) -> (Option, usize) { - match self { - WorkspaceMode::Global(set) => (set.previously_active.map(|(idx, _)| idx), set.active), - WorkspaceMode::OutputBound(sets, _) => { - let set = sets.get(output).unwrap(); - (set.previously_active.map(|(idx, _)| idx), set.active) - } - } + let set = self.sets.get(output).or(self.backup_set.as_ref()).unwrap(); + (set.previously_active.map(|(idx, _)| idx), set.active) } pub fn len(&self, output: &Output) -> usize { - match self { - WorkspaceMode::Global(set) => set.workspaces.len(), - WorkspaceMode::OutputBound(sets, _) => { - let set = sets.get(output).unwrap(); - set.workspaces.len() - } - } + let set = self.sets.get(output).unwrap(); + set.workspaces.len() + } + + pub fn iter(&self) -> impl Iterator { + self.sets.iter() } pub fn spaces(&self) -> impl Iterator { - match self { - WorkspaceMode::Global(set) => { - Box::new(set.workspaces.iter()) as Box> - } - WorkspaceMode::OutputBound(sets, _) => { - Box::new(sets.values().flat_map(|set| set.workspaces.iter())) - } - } + self.sets.values().flat_map(|set| set.workspaces.iter()) } pub fn spaces_for_output(&self, output: &Output) -> impl Iterator { - match self { - WorkspaceMode::Global(set) => { - Box::new(set.workspaces.iter()) as Box> - } - WorkspaceMode::OutputBound(sets, _) => Box::new( - sets.get(output) - .into_iter() - .flat_map(|set| set.workspaces.iter()), - ), - } + self.sets + .get(output) + .into_iter() + .flat_map(|set| set.workspaces.iter()) } pub fn spaces_mut(&mut self) -> impl Iterator { - match self { - WorkspaceMode::Global(set) => { - Box::new(set.workspaces.iter_mut()) as Box> - } - WorkspaceMode::OutputBound(sets, _) => { - Box::new(sets.values_mut().flat_map(|set| set.workspaces.iter_mut())) - } - } + Box::new( + self.sets + .values_mut() + .flat_map(|set| set.workspaces.iter_mut()), + ) } pub fn update_tiling_status(&mut self, seat: &Seat, tiling: bool) { - match self { - WorkspaceMode::Global(set) => set.update_tiling_status(seat, tiling), - WorkspaceMode::OutputBound(sets, _) => { - for set in sets.values_mut() { - set.update_tiling_status(seat, tiling) - } + for set in self.sets.values_mut() { + set.update_tiling_status(seat, tiling) + } + } + + pub fn set_theme(&mut self, theme: cosmic::Theme) { + for (_, s) in &mut self.sets { + s.theme = theme.clone(); + for mut w in &mut s.workspaces { + w.tiling_layer.theme = theme.clone(); + + w.mapped().for_each(|m| { + m.update_theme(theme.clone()); + m.force_redraw(); + }); + + w.refresh(); + w.dirty.store(true, Ordering::Relaxed); + w.recalculate(); } } } @@ -598,26 +867,17 @@ impl Shell { //|client| client.get_data::().map_or(false, |s| s.privileged), client_has_security_context, ); - let mut workspace_state = WorkspaceState::new( + let workspace_state = WorkspaceState::new( dh, //|client| client.get_data::().map_or(false, |s| s.privileged), client_has_security_context, ); - - let tiling_enabled = config.static_conf.tiling_enabled; - let mode = WorkspaceMode::new( - config.static_conf.workspace_mode, - config.static_conf.workspace_amount, - &mut workspace_state.update(), - tiling_enabled, - config.static_conf.gaps, - ); + let theme = cosmic::theme::system_preference(); Shell { popups: PopupManager::default(), - outputs: Vec::new(), - workspaces: mode, - tiling_enabled, + workspaces: Workspaces::new(config, theme.clone()), + maximize_mode: MaximizeMode::Floating, pending_windows: Vec::new(), pending_layers: Vec::new(), @@ -629,7 +889,7 @@ impl Shell { xdg_shell_state, workspace_state, - gaps: config.static_conf.gaps, + theme, overview_mode: OverviewMode::None, swap_indicator: None, resize_mode: ResizeMode::None, @@ -639,400 +899,61 @@ impl Shell { } pub fn add_output(&mut self, output: &Output) { - if self.outputs.contains(output) { - return; - } - - self.outputs.push(output.clone()); - let mut state = self.workspace_state.update(); - - match &mut self.workspaces { - WorkspaceMode::OutputBound(sets, amount) => { - // TODO: Restore previously assigned workspaces, if possible! - if !sets.contains_key(output) { - let set = WorkspaceSet::new( - &mut state, - *amount, - sets.len(), - self.tiling_enabled, - self.gaps, - ); - state.add_group_output(&set.group, &output); - sets.insert(output.clone(), set); - } - for workspace in &mut sets.get_mut(output).unwrap().workspaces { - workspace.map_output(output, (0, 0).into()); - } - } - WorkspaceMode::Global(set) => { - // TODO: Restore any window positions from previous outputs ??? - state.add_group_output(&set.group, output); - for workspace in &mut set.workspaces { - workspace.map_output( - output, - output - .user_data() - .get::>() - .unwrap() - .borrow() - .position - .into(), - ); - } - } - } + self.workspaces.add_output( + output, + &mut self.workspace_state.update(), + &mut self.toplevel_info_state, + ); } pub fn remove_output(&mut self, output: &Output, seats: impl Iterator>) { - if let Some(first_output) = self.outputs.get(0) { - for seat in seats { - if &seat.active_output() == output { - seat.set_active_output(first_output); - } - } - } - - if !self.outputs.contains(output) { - return; - } - - { - let map = layer_map_for_output(output); - for surface in map.layers() { - surface.layer_surface().send_close(); - } - } - - let mut state = self.workspace_state.update(); - self.outputs.retain(|o| o != output); - - match &mut self.workspaces { - WorkspaceMode::OutputBound(sets, _) => { - // TODO: - // If amount::static merge them instead of appending - - if let Some(set) = sets.remove(output) { - // TODO: Heuristic which output to move to. - // It is supposed to be the *most* internal, we just pick the first one for now - // and hope enumeration order works in our favor. - if let Some(new_output) = self.outputs.get(0) { - let new_set = sets.get_mut(new_output).unwrap(); - let workspace_group = new_set.group; - for mut workspace in set.workspaces { - // update workspace protocol state - state.remove_workspace(workspace.handle); - let workspace_handle = - state.create_workspace(&workspace_group).unwrap(); - state.set_workspace_capabilities( - &workspace_handle, - [WorkspaceCapabilities::Activate].into_iter(), - ); - workspace.handle = workspace_handle; - for window in workspace.mapped() { - for (surface, _) in window.windows() { - self.toplevel_info_state - .toplevel_enter_workspace(&surface, &workspace.handle); - } - } - - // update mapping - workspace.map_output(new_output, (0, 0).into()); - workspace.unmap_output(output, &mut self.toplevel_info_state); - workspace.refresh(); - - new_set.workspaces.push(workspace); - } - state.remove_workspace_group(set.group); - } - // if there is no output, we are going to quit anyway, just drop the workspace set - } - for (i, set) in sets.values_mut().enumerate() { - set.update_idx(&mut state, i); - } - std::mem::drop(state); - self.refresh(); // cleans up excess of workspaces and empty workspaces - } - WorkspaceMode::Global(set) => { - state.remove_group_output(&set.group, output); - for workspace in &mut set.workspaces { - workspace.unmap_output(output, &mut self.toplevel_info_state); - workspace.refresh(); - } - } - }; - } - - pub fn refresh_outputs(&mut self) { - if let WorkspaceMode::Global(set) = &mut self.workspaces { - for workspace in &mut set.workspaces { - for output in self.outputs.iter() { - workspace.map_output( - output, - output - .user_data() - .get::>() - .unwrap() - .borrow() - .position - .into(), - ); - } - } - } + self.workspaces.remove_output( + output, + seats, + &mut self.workspace_state.update(), + &mut self.toplevel_info_state, + ); + self.refresh(); // cleans up excess of workspaces and empty workspaces } - pub fn set_mode(&mut self, mode: ConfigMode) { - let mut state = self.workspace_state.update(); - - match (&mut self.workspaces, mode) { - (dst @ WorkspaceMode::OutputBound(_, _), ConfigMode::Global) => { - // rustc should really be able to infer that this doesn't need an if. - let (sets, amount) = - if let &mut WorkspaceMode::OutputBound(ref mut sets, ref amount) = dst { - (sets, *amount) - } else { - unreachable!() - }; - - // in this case we have to merge our sets, preserving placing of windows as nicely as possible - let mut new_set = WorkspaceSet::new( - &mut state, - WorkspaceAmount::Static(0), - 0, - self.tiling_enabled, - self.gaps, - ); - for output in &self.outputs { - state.add_group_output(&new_set.group, output); - } - - // lets construct an iterator of all the pairs of workspaces we have to merge - // we first split of the part of the workspaces that contain the currently active one - let mut second_half = sets - .iter_mut() - .map(|(output, set)| (output.clone(), set.workspaces.split_off(set.active))) - .collect::>(); - - let mut first_half = std::iter::repeat(()) - // we continuously pop the last elements from the first half and group them together. - .map(|_| { - sets.iter_mut() - .flat_map(|(o, w)| w.workspaces.pop().map(|w| (o.clone(), w))) - .collect::>() - }) - // we stop once there is no workspace anymore in the entire set - .filter(|vec| !vec.is_empty()) - .fuse() - .collect::>(); - // we reverse those then to get the proper order - first_half.reverse(); - - let mergers = first_half - .into_iter() - // we need to know, which is supposed to be active and we loose that info by chaining, so lets add a bool - .map(|w| (w, false)) - .chain( - (0..) - // here we continuously remove the first element - .map(|i| { - ( - second_half - .iter_mut() - .flat_map(|&mut (ref o, ref mut w)| { - if !w.is_empty() { - Some((o.clone(), w.remove(0))) - } else { - None - } - }) - .collect::>(), - i == 0, - ) - }) - .filter(|(vec, _)| !vec.is_empty()) - .fuse(), - ); - - for (i, (workspaces, active)) in mergers.into_iter().enumerate() { - // and then we can merge each vector into one and put that into our new set. - let workspace_handle = state.create_workspace(&new_set.group).unwrap(); - state.set_workspace_capabilities( - &workspace_handle, - [WorkspaceCapabilities::Activate].into_iter(), - ); - workspace_set_idx(&mut state, i as u8 + 1, 0, &workspace_handle); - - let mut new_workspace = - Workspace::new(workspace_handle, self.tiling_enabled, self.gaps); - for output in self.outputs.iter() { - new_workspace.map_output(output, output.current_location()); - } - new_workspace.tiling_enabled = workspaces.iter().any(|(_, w)| w.tiling_enabled); - - for (_output, workspace) in workspaces.into_iter() { - for toplevel in workspace.windows() { - self.toplevel_info_state - .toplevel_leave_workspace(&toplevel, &workspace.handle); - self.toplevel_info_state - .toplevel_enter_workspace(&toplevel, &new_workspace.handle); - } - new_workspace.tiling_layer.merge(workspace.tiling_layer); - new_workspace.floating_layer.merge(workspace.floating_layer); - new_workspace - .fullscreen - .extend(workspace.fullscreen.into_iter()); - state.remove_workspace(workspace.handle); - } - - if active { - new_set.active = new_set.workspaces.len(); - } - new_set.workspaces.push(new_workspace); - } - - for group in sets.values().map(|set| set.group) { - state.remove_workspace_group(group); - } - - new_set.amount = amount; - *dst = WorkspaceMode::Global(new_set); - } - (dst @ WorkspaceMode::Global(_), ConfigMode::OutputBound) => { - // rustc should really be able to infer that this doesn't need an if. - let set = if let &mut WorkspaceMode::Global(ref mut set) = dst { - set - } else { - unreachable!() - }; - - // split workspaces apart, preserving window positions relative to their outputs - let mut sets = HashMap::new(); - for (i, output) in self.outputs.iter().enumerate() { - let set = WorkspaceSet::new( - &mut state, - WorkspaceAmount::Static(0), - i, - self.tiling_enabled, - self.gaps, - ); - state.add_group_output(&set.group, output); - sets.insert(output.clone(), set); - } - for (i, workspace) in set.workspaces.drain(..).enumerate() { - for (idx, output) in self.outputs.iter().enumerate() { - // copy over everything and then remove other outputs to preserve state - let new_set = sets.get_mut(output).unwrap(); - let new_workspace_handle = state.create_workspace(&new_set.group).unwrap(); - state.set_workspace_capabilities( - &new_workspace_handle, - [WorkspaceCapabilities::Activate].into_iter(), - ); - workspace_set_idx(&mut state, i as u8 + 1, idx, &new_workspace_handle); - - let mut old_tiling_layer = workspace.tiling_layer.clone(); - let mut new_floating_layer = FloatingLayout::new(); - let mut new_tiling_layer = TilingLayout::new(self.gaps); - - for element in workspace.mapped() { - for (toplevel, _) in element.windows() { - self.toplevel_info_state - .toplevel_leave_workspace(&toplevel, &workspace.handle); - } - - if workspace - .floating_layer - .most_overlapped_output_for_element(element) - .as_ref() - == Some(output) - { - if let Some(mut old_mapped_loc) = - workspace.floating_layer.space.element_location(element) - { - let old_output_geo = workspace - .floating_layer - .space - .output_geometry(output) - .unwrap(); - old_mapped_loc -= old_output_geo.loc; - new_floating_layer.map_internal( - element.clone(), - output, - Some(old_mapped_loc), - ); - } - } else { - old_tiling_layer.unmap(element); - } - } - - new_floating_layer.map_output(output, (0, 0).into()); - new_tiling_layer.map_output(output, (0, 0).into()); - new_tiling_layer.merge(old_tiling_layer); - - let mut new_workspace = Workspace { - tiling_layer: new_tiling_layer, - floating_layer: new_floating_layer, - tiling_enabled: workspace.tiling_enabled, - fullscreen: workspace - .fullscreen - .iter() - .filter(|(key, _)| *key == output) - .map(|(o, w)| (o.clone(), w.clone())) - .collect(), - ..Workspace::new(new_workspace_handle, true, self.gaps) - }; - for toplevel in new_workspace.windows() { - self.toplevel_info_state - .toplevel_enter_workspace(&toplevel, &new_workspace_handle); - } - new_workspace.refresh(); - - new_set.workspaces.push(new_workspace); - new_set.active = set.active; - } - state.remove_workspace(workspace.handle); - } - state.remove_workspace_group(set.group); - - for new_set in sets.values_mut() { - new_set.amount = set.amount; - } - *dst = WorkspaceMode::OutputBound(sets, set.amount); - } - _ => {} - } - - std::mem::drop(state); - self.refresh(); // get rid of empty workspaces and enforce potential maximum + pub fn update_config(&mut self, config: &Config) { + let mut workspace_state = self.workspace_state.update(); + let toplevel_info_state = &mut self.toplevel_info_state; + self.workspaces + .update_config(config, &mut workspace_state, toplevel_info_state); } pub fn activate( &mut self, output: &Output, idx: usize, - ) -> Result>, InvalidWorkspaceIndex> { - if match &mut self.workspaces { - WorkspaceMode::OutputBound(sets, _) => { - if let Some(set) = sets.get_mut(output) { + ) -> Result>, InvalidWorkspaceIndex> { + match &mut self.workspaces.mode { + WorkspaceMode::OutputBound => { + if let Some(set) = self.workspaces.sets.get_mut(output) { if matches!( self.overview_mode, OverviewMode::Started(Trigger::Pointer(_), _) ) { - set.workspaces[set.active].tiling_layer.cleanup_drag(output); + set.workspaces[set.active].tiling_layer.cleanup_drag(); } - set.activate(idx, &mut self.workspace_state.update())? + set.activate(idx, &mut self.workspace_state.update())?; + + let output_geo = output.geometry(); + Ok(Some( + output_geo.loc + + Point::from((output_geo.size.w / 2, output_geo.size.h / 2)), + )) } else { - false + Ok(None) } } - WorkspaceMode::Global(set) => set.activate(idx, &mut self.workspace_state.update())?, - } { - let output_geo = output.geometry(); - Ok(Some( - output_geo.loc + Point::from((output_geo.size.w / 2, output_geo.size.h / 2)), - )) - } else { - Ok(None) + WorkspaceMode::Global => { + for set in self.workspaces.sets.values_mut() { + set.activate(idx, &mut self.workspace_state.update())?; + } + Ok(None) + } } } @@ -1049,8 +970,7 @@ impl Shell { surface: &'a WlSurface, ) -> impl Iterator + 'a { match self - .outputs - .iter() + .outputs() .find(|o| { let map = layer_map_for_output(o); map.layer_for_surface(surface, WindowSurfaceType::ALL) @@ -1073,7 +993,10 @@ impl Shell { .filter(|o| { self.override_redirect_windows.iter().any(|or| { if or.wl_surface().as_ref() == Some(surface) { - or.geometry().intersection(o.geometry()).is_some() + or.geometry() + .as_global() + .intersection(o.geometry()) + .is_some() } else { false } @@ -1083,8 +1006,8 @@ impl Shell { .chain(self.outputs().map(|o| self.active_space(o)).flat_map(|w| { w.mapped() .find(|e| e.has_surface(surface, WindowSurfaceType::ALL)) + .map(|_| w.output().clone()) .into_iter() - .flat_map(|e| w.outputs_for_element(e)) })), ), } @@ -1094,7 +1017,7 @@ impl Shell { &self, surface: &WlSurface, ) -> impl Iterator { - match self.outputs.iter().find(|o| { + match self.outputs().find(|o| { let map = layer_map_for_output(o); map.layer_for_surface(surface, WindowSurfaceType::ALL) .is_some() @@ -1102,31 +1025,16 @@ impl Shell { Some(output) => self .workspaces .spaces() - .filter(move |workspace| { - workspace - .floating_layer - .space - .outputs() - .any(|o| o == output) - }) - .map(|w| (w.handle.clone(), output.clone())) - .collect::>(), + .find(move |workspace| workspace.output() == output) + .map(|w| (w.handle.clone(), output.clone())), None => self .workspaces .spaces() - .filter_map(|w| { - if let Some(mapped) = w - .mapped() - .find(|e| e.has_surface(surface, WindowSurfaceType::ALL)) - { - let outputs = w.outputs_for_element(mapped); - Some(std::iter::repeat(w.handle.clone()).zip(outputs).fuse()) - } else { - None - } + .find(|w| { + w.mapped() + .any(|e| e.has_surface(surface, WindowSurfaceType::ALL)) }) - .flatten() - .collect::>(), + .map(|w| (w.handle.clone(), w.output().clone())), } .into_iter() } @@ -1163,44 +1071,34 @@ impl Shell { self.workspaces.spaces_mut().find(|w| &w.handle == handle) } - pub fn outputs(&self) -> impl Iterator { - self.outputs.iter() + pub fn outputs(&self) -> impl DoubleEndedIterator { + self.workspaces.sets.keys().chain( + self.workspaces + .backup_set + .as_ref() + .into_iter() + .map(|set| &set.output), + ) } - pub fn global_space(&self) -> Rectangle { - self.outputs - .iter() + pub fn global_space(&self) -> Rectangle { + self.outputs() .fold( - Option::>::None, + Option::>::None, |maybe_geo, output| match maybe_geo { Some(rect) => Some(rect.merge(output.geometry())), None => Some(output.geometry()), }, ) - .unwrap_or_else(|| Rectangle::from_loc_and_size((0, 0), (0, 0))) - } - - pub fn map_global_to_space( - &self, - global_loc: impl Into>, - output: &Output, - ) -> Point { - match self.workspaces { - WorkspaceMode::Global(_) => global_loc.into(), - WorkspaceMode::OutputBound(_, _) => { - let p = global_loc.into().to_f64() - output.current_location().to_f64(); - (C::from_f64(p.x), C::from_f64(p.y)).into() - } - } + .unwrap_or_else(Rectangle::default) } pub fn animations_going(&self) -> bool { - (match &self.workspaces { - WorkspaceMode::Global(set) => set.previously_active.is_some(), - WorkspaceMode::OutputBound(sets, _) => { - sets.values().any(|set| set.previously_active.is_some()) - } - }) || !matches!(self.overview_mode, OverviewMode::None) + self.workspaces + .sets + .values() + .any(|set| set.previously_active.is_some()) + || !matches!(self.overview_mode, OverviewMode::None) || !matches!(self.resize_mode, ResizeMode::None) || self .workspaces @@ -1224,7 +1122,7 @@ impl Shell { if let Some(trigger) = enabled { if !matches!(self.overview_mode, OverviewMode::Started(_, _)) { if matches!(trigger, Trigger::KeyboardSwap(_, _)) { - self.swap_indicator = Some(swap_indicator(evlh)); + self.swap_indicator = Some(swap_indicator(evlh, self.theme.clone())); } self.overview_mode = OverviewMode::Started(trigger, Instant::now()); } @@ -1270,7 +1168,12 @@ impl Shell { } else { self.resize_mode = ResizeMode::Started(pattern, Instant::now(), direction); } - self.resize_indicator = Some(resize_indicator(direction, config, evlh)); + self.resize_indicator = Some(resize_indicator( + direction, + config, + evlh, + self.theme.clone(), + )); } else { if let ResizeMode::Started(_, _, direction) = &self.resize_mode { self.resize_mode = ResizeMode::Ended(Instant::now(), *direction); @@ -1298,24 +1201,12 @@ impl Shell { self.popups.cleanup(); - match &mut self.workspaces { - WorkspaceMode::OutputBound(sets, _) => { - for (output, set) in sets.iter_mut() { - set.refresh( - &mut self.workspace_state, - &mut self.toplevel_info_state, - std::iter::once((output, (0, 0).into())), - ); - } - } - WorkspaceMode::Global(set) => set.refresh( - &mut self.workspace_state, - &mut self.toplevel_info_state, - self.outputs.iter().map(|o| (o, o.current_location())), - ), - } + self.workspaces.refresh( + &mut self.workspace_state.update(), + &mut self.toplevel_info_state, + ); - for output in &self.outputs { + for output in self.outputs() { let mut map = layer_map_for_output(output); map.cleanup(); } @@ -1329,18 +1220,78 @@ impl Shell { .refresh(Some(&self.workspace_state)); } - pub fn map_window(state: &mut State, window: &CosmicSurface, output: &Output) { + pub fn remap_unfullscreened_window( + &mut self, + mapped: CosmicMapped, + current_workspace: &WorkspaceHandle, + previous_workspace: &WorkspaceHandle, + target_layer: ManagedLayer, + ) { + if self.space_for_handle(previous_workspace).is_none() { + return; + } + + { + let Some(workspace) = self.space_for_handle_mut(¤t_workspace) else { return }; + let _ = workspace.unmap(&mapped); + } + + let new_workspace_output = self + .space_for_handle(&previous_workspace) + .unwrap() + .output() + .clone(); + for (window, _) in mapped.windows() { + self.toplevel_info_state + .toplevel_enter_output(&window, &new_workspace_output); + self.toplevel_info_state + .toplevel_enter_workspace(&window, &previous_workspace); + } + + let new_workspace = self.space_for_handle_mut(&previous_workspace).unwrap(); + match target_layer { + ManagedLayer::Floating => new_workspace.floating_layer.map(mapped, None), + ManagedLayer::Tiling => { + new_workspace + .tiling_layer + .map(mapped, Option::>::None, None) + } + }; + } + + pub fn map_window(state: &mut State, window: &CosmicSurface) { let pos = state .common .shell .pending_windows .iter() - .position(|(w, _)| w == window) + .position(|(w, _, _)| w == window) .unwrap(); - let (window, seat) = state.common.shell.pending_windows.remove(pos); + let (window, seat, output) = state.common.shell.pending_windows.remove(pos); + + let should_be_fullscreen = output.is_some(); + let output = output.unwrap_or_else(|| seat.active_output()); - let workspace = state.common.shell.workspaces.active_mut(output); - workspace.remove_fullscreen(output); + let workspace = state.common.shell.workspaces.active_mut(&output); + if let Some((mapped, layer, previous_workspace)) = workspace.remove_fullscreen() { + let old_handle = workspace.handle.clone(); + let new_workspace_handle = state + .common + .shell + .space_for_handle(&previous_workspace) + .is_some() + .then_some(previous_workspace) + .unwrap_or(old_handle); + + state.common.shell.remap_unfullscreened_window( + mapped, + &old_handle, + &new_workspace_handle, + layer, + ); + }; + + let workspace = state.common.shell.workspaces.active_mut(&output); state.common.shell.toplevel_info_state.new_toplevel(&window); state .common @@ -1356,36 +1307,37 @@ impl Shell { let mapped = CosmicMapped::from(CosmicWindow::new( window.clone(), state.common.event_loop_handle.clone(), + state.common.theme.clone(), )); #[cfg(feature = "debug")] { mapped.set_debug(state.common.egui.active); } if layout::should_be_floating(&window) || !workspace.tiling_enabled { - workspace.floating_layer.map(mapped.clone(), &seat, None); + workspace.floating_layer.map(mapped.clone(), None); } else { + for mapped in workspace + .mapped() + .filter(|m| m.maximized_state.lock().unwrap().is_some()) + .cloned() + .collect::>() + .into_iter() + { + workspace.unmaximize_request(&mapped.active_window()); + } let focus_stack = workspace.focus_stack.get(&seat); workspace .tiling_layer - .map(mapped.clone(), &seat, focus_stack.iter(), None); + .map(mapped.clone(), Some(focus_stack.iter()), None); } - if let CosmicSurface::X11(surface) = window { - if let Some(xwm) = state - .common - .xwayland_state - .as_mut() - .and_then(|state| state.xwm.as_mut()) - { - if let Err(err) = xwm.raise_window(&surface) { - warn!(?err, "Failed to update Xwayland stacking order."); - } - } + if should_be_fullscreen { + workspace.fullscreen_request(&mapped.active_window(), None); } Shell::set_focus(state, Some(&KeyboardFocusTarget::from(mapped)), &seat, None); - let active_space = state.common.shell.active_space(output); + let active_space = state.common.shell.active_space(&output); for mapped in active_space.mapped() { state.common.shell.update_reactive_popups(mapped); } @@ -1393,13 +1345,12 @@ impl Shell { pub fn map_override_redirect(state: &mut State, window: X11Surface) { let geo = window.geometry(); - for (output, overlap) in state - .common - .shell - .outputs() - .cloned() - .filter_map(|o| o.geometry().intersection(geo).map(|overlap| (o, overlap))) - { + for (output, overlap) in state.common.shell.outputs().cloned().filter_map(|o| { + o.geometry() + .as_logical() + .intersection(geo) + .map(|overlap| (o, overlap)) + }) { window.output_enter(&output, overlap); } @@ -1429,7 +1380,7 @@ impl Shell { map.map_layer(&layer_surface).unwrap(); } for workspace in state.common.shell.workspaces.spaces_mut() { - workspace.tiling_layer.recalculate(&output); + workspace.tiling_layer.recalculate(); } if wants_focus { @@ -1444,7 +1395,7 @@ impl Shell { to: (&Output, Option), follow: bool, direction: Option, - ) -> Result>, InvalidWorkspaceIndex> { + ) -> Result>, InvalidWorkspaceIndex> { let (to_output, to_idx) = to; let to_idx = to_idx.unwrap_or(state.common.shell.workspaces.active_num(to_output).1); if state @@ -1499,7 +1450,7 @@ impl Shell { None }; - let to_workspace = state + let mut to_workspace = state .common .shell .workspaces @@ -1507,44 +1458,48 @@ impl Shell { .unwrap(); // checked above let focus_stack = to_workspace.focus_stack.get(&seat); if window_state.layer == ManagedLayer::Floating { - to_workspace.floating_layer.map(mapped.clone(), &seat, None); + to_workspace.floating_layer.map(mapped.clone(), None); } else { to_workspace .tiling_layer - .map(mapped.clone(), &seat, focus_stack.iter(), direction); + .map(mapped.clone(), Some(focus_stack.iter()), direction); } - let focus_target = match window_state.was_fullscreen { - Some(true) => { - to_workspace.fullscreen_request( - &mapped.active_window(), - &to_output, - state.common.event_loop_handle.clone(), - ); - KeyboardFocusTarget::from( - to_workspace - .fullscreen - .get(to_output) - .unwrap() - .window - .clone(), - ) - } - Some(false) => { - to_workspace.maximize_request( - &mapped.active_window(), - &to_output, - state.common.event_loop_handle.clone(), - ); - KeyboardFocusTarget::from( - to_workspace - .fullscreen - .get(to_output) - .unwrap() - .window - .clone(), - ) + let focus_target = if let Some(f) = window_state.was_fullscreen { + if to_workspace.fullscreen.is_some() { + if let Some((mapped, layer, previous_workspace)) = to_workspace.remove_fullscreen() + { + let old_handle = to_workspace.handle.clone(); + let new_workspace_handle = state + .common + .shell + .space_for_handle(&previous_workspace) + .is_some() + .then_some(previous_workspace) + .unwrap_or(old_handle); + + state.common.shell.remap_unfullscreened_window( + mapped, + &old_handle, + &new_workspace_handle, + layer, + ); + to_workspace = state + .common + .shell + .workspaces + .get_mut(to_idx, to_output) + .unwrap(); // checked above + } } - None => KeyboardFocusTarget::from(mapped.clone()), + + to_workspace.fullscreen_request(&mapped.active_window(), f.previously); + to_workspace + .fullscreen + .as_ref() + .map(|f| KeyboardFocusTarget::from(f.surface.clone())) + .unwrap_or_else(|| KeyboardFocusTarget::from(mapped.clone())) + } else { + KeyboardFocusTarget::from(mapped.clone()) }; for (toplevel, _) in mapped.windows() { @@ -1578,14 +1533,18 @@ impl Shell { pub fn update_reactive_popups(&self, mapped: &CosmicMapped) { if let Some(workspace) = self.space_for(mapped) { - let element_loc = workspace.element_geometry(mapped).unwrap().loc; + let element_loc = workspace + .element_geometry(mapped) + .unwrap() + .loc + .to_global(&workspace.output); for (toplevel, offset) in mapped.windows() { if let CosmicSurface::Wayland(toplevel) = toplevel { - let window_geo_offset = toplevel.geometry().loc; + let window_geo_offset = toplevel.geometry().loc.as_global(); update_reactive_popups( &toplevel, - element_loc + offset + window_geo_offset, - self.outputs.iter(), + element_loc + offset.as_global() + window_geo_offset, + self.outputs(), ); } } @@ -1608,12 +1567,13 @@ impl Shell { .find(|(w, _)| w.wl_surface().as_ref() == Some(surface)) .unwrap(); let button = start_data.button; + let active_hint = state.common.theme.cosmic().active_hint as u8; if let Some(grab) = workspace.move_request( &window, &seat, &output, start_data, - state.common.config.static_conf.active_hint, + active_hint as u8, ) { let handle = workspace.handle; state @@ -1713,6 +1673,12 @@ impl Shell { } } } + + pub(crate) fn set_theme(&mut self, theme: cosmic::Theme) { + self.theme = theme.clone(); + self.refresh(); + self.workspaces.set_theme(theme.clone()); + } } fn workspace_set_idx<'a>( diff --git a/src/shell/workspace.rs b/src/shell/workspace.rs index 39486bae..3f1de379 100644 --- a/src/shell/workspace.rs +++ b/src/shell/workspace.rs @@ -21,7 +21,7 @@ use crate::{ xwayland::XWaylandState, }; -use calloop::LoopHandle; +use cosmic::theme::CosmicTheme; use id_tree::Tree; use indexmap::IndexSet; use keyframe::{ease, functions::EaseInOutCubic}; @@ -47,7 +47,7 @@ use smithay::{ xwayland::X11Surface, }; use std::{ - collections::HashMap, + collections::{HashMap, VecDeque}, sync::{ atomic::{AtomicBool, Ordering}, Arc, @@ -61,7 +61,7 @@ use super::{ element::{ resize_indicator::ResizeIndicator, stack::CosmicStackRenderElement, swap_indicator::SwapIndicator, window::CosmicWindowRenderElement, CosmicMapped, - CosmicWindow, + MaximizedState, }, focus::{ target::{KeyboardFocusTarget, PointerFocusTarget, WindowGroup}, @@ -76,28 +76,37 @@ const FULLSCREEN_ANIMATION_DURATION: Duration = Duration::from_millis(200); #[derive(Debug)] pub struct Workspace { + pub output: Output, pub tiling_layer: TilingLayout, pub floating_layer: FloatingLayout, pub tiling_enabled: bool, - pub fullscreen: HashMap, + pub fullscreen: Option, + pub handle: WorkspaceHandle, pub focus_stack: FocusStacks, pub pending_buffers: Vec<(ScreencopySession, BufferParams)>, pub screencopy_sessions: Vec, + pub output_stack: VecDeque, pub(super) backdrop_id: Id, pub dirty: AtomicBool, } #[derive(Debug, Clone)] pub struct FullscreenSurface { - pub window: CosmicWindow, - pub exclusive: bool, - original_size: Size, + pub surface: CosmicSurface, + pub previously: Option<(ManagedLayer, WorkspaceHandle)>, + original_geometry: Rectangle, start_at: Option, ended_at: Option, animation_signal: Option>, } +impl PartialEq for FullscreenSurface { + fn eq(&self, other: &Self) -> bool { + self.surface == other.surface + } +} + struct FullscreenBlocker { signal: Arc, } @@ -120,17 +129,17 @@ impl FullscreenSurface { impl IsAlive for FullscreenSurface { fn alive(&self) -> bool { - self.window.alive() + self.surface.alive() } } #[derive(Debug, Default)] pub struct FocusStacks(HashMap, IndexSet>); -#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)] +#[derive(Debug, Clone, PartialEq)] pub struct ManagedState { pub layer: ManagedLayer, - pub was_fullscreen: Option, + pub was_fullscreen: Option, } #[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)] pub enum ManagedLayer { @@ -198,16 +207,26 @@ impl MoveResult { } impl Workspace { - pub fn new(handle: WorkspaceHandle, tiling_enabled: bool, gaps: (u8, u8)) -> Workspace { + pub fn new( + handle: WorkspaceHandle, + output: Output, + tiling_enabled: bool, + theme: cosmic::Theme, + ) -> Workspace { + let tiling_layer = TilingLayout::new(theme, &output); + let floating_layer = FloatingLayout::new(&output); + Workspace { - tiling_layer: TilingLayout::new(gaps), - floating_layer: FloatingLayout::new(), + output, + tiling_layer, + floating_layer, tiling_enabled, - fullscreen: HashMap::new(), + fullscreen: None, handle, focus_stack: FocusStacks::default(), pending_buffers: Vec::new(), screencopy_sessions: Vec::new(), + output_stack: VecDeque::new(), backdrop_id: Id::new(), dirty: AtomicBool::new(false), } @@ -217,7 +236,11 @@ impl Workspace { #[cfg(feature = "debug")] puffin::profile_function!(); - self.fullscreen.retain(|_, w| w.alive()); + // TODO: `Option::take_if` once stabilitized + if self.fullscreen.as_ref().is_some_and(|w| !w.alive()) { + let _ = self.fullscreen.take(); + }; + self.floating_layer.refresh(); self.tiling_layer.refresh(); } @@ -233,15 +256,15 @@ impl Workspace { self.tiling_layer.animations_going() || self .fullscreen - .values() - .any(|f| f.start_at.is_some() || f.ended_at.is_some()) + .as_ref() + .is_some_and(|f| f.start_at.is_some() || f.ended_at.is_some()) || self.dirty.swap(false, Ordering::SeqCst) } pub fn update_animations(&mut self) -> HashMap { let mut clients = HashMap::new(); - for f in self.fullscreen.values_mut() { + if let Some(f) = self.fullscreen.as_mut() { if let Some(start) = f.start_at.as_ref() { let duration_since = Instant::now().duration_since(*start); if duration_since > FULLSCREEN_ANIMATION_DURATION { @@ -252,36 +275,32 @@ impl Workspace { if let Some(signal) = f.animation_signal.take() { signal.store(true, Ordering::SeqCst); if let Some(client) = - f.window.wl_surface().as_ref().and_then(Resource::client) + f.surface.wl_surface().as_ref().and_then(Resource::client) { clients.insert(client.id(), client); } } } } - } - let len = self.fullscreen.len(); - self.fullscreen.retain(|_, f| match f.ended_at { - None => true, - Some(instant) => { - let duration_since = Instant::now().duration_since(instant); + if let Some(end) = f.ended_at { + let duration_since = Instant::now().duration_since(end); if duration_since * 2 > FULLSCREEN_ANIMATION_DURATION { if let Some(signal) = f.animation_signal.take() { signal.store(true, Ordering::SeqCst); if let Some(client) = - f.window.wl_surface().as_ref().and_then(Resource::client) + f.surface.wl_surface().as_ref().and_then(Resource::client) { clients.insert(client.id(), client); } } } - duration_since < FULLSCREEN_ANIMATION_DURATION + if duration_since >= FULLSCREEN_ANIMATION_DURATION { + let _ = self.fullscreen.take(); + self.dirty.store(true, Ordering::SeqCst); + } } - }); - if len != self.fullscreen.len() { - self.dirty.store(true, Ordering::SeqCst); } clients.extend(self.tiling_layer.update_animation_state()); @@ -299,37 +318,46 @@ impl Workspace { } } - pub fn map_output(&mut self, output: &Output, position: Point) { - self.tiling_layer.map_output(output, position); - self.floating_layer.map_output(output, position); + pub fn output(&self) -> &Output { + &self.output } - pub fn unmap_output( + pub fn set_output( &mut self, output: &Output, toplevel_info: &mut ToplevelInfoState, ) { - if let Some(dead_output_fullscreen) = self.fullscreen.remove(output) { - self.unfullscreen_request(&dead_output_fullscreen.window.surface()); + self.tiling_layer.set_output(output); + self.floating_layer.set_output(output); + for mapped in self.mapped() { + for (surface, _) in mapped.windows() { + toplevel_info.toplevel_leave_output(&surface, &self.output); + toplevel_info.toplevel_enter_output(&surface, output); + } } - self.tiling_layer.unmap_output(output, toplevel_info); - self.floating_layer.unmap_output(output, toplevel_info); - self.refresh(); + self.output = output.clone(); } pub fn unmap(&mut self, mapped: &CosmicMapped) -> Option { + let was_fullscreen = self + .fullscreen + .as_ref() + .filter(|f| f.ended_at.is_none()) + .map(|f| mapped.windows().any(|(w, _)| w == f.surface)) + .unwrap_or(false) + .then(|| self.fullscreen.take().unwrap()); + + if mapped.maximized_state.lock().unwrap().is_some() { + // If surface is maximized then unmaximize it, so it is assigned to only one layer + let _ = self.unmaximize_request(&mapped.active_window()); + } + let was_floating = self.floating_layer.unmap(&mapped); - let was_tiling = self.tiling_layer.unmap(&mapped).is_some(); + let was_tiling = self.tiling_layer.unmap(&mapped); if was_floating || was_tiling { assert!(was_floating != was_tiling); } - let was_maximized = mapped.is_maximized(true); - let was_fullscreen = mapped.is_fullscreen(true); - if was_maximized || was_fullscreen { - self.unmaximize_request(&mapped.active_window()); - } - self.focus_stack .0 .values_mut() @@ -337,12 +365,12 @@ impl Workspace { if was_floating { Some(ManagedState { layer: ManagedLayer::Floating, - was_fullscreen: (was_fullscreen || was_maximized).then_some(was_fullscreen), + was_fullscreen, }) } else if was_tiling { Some(ManagedState { layer: ManagedLayer::Tiling, - was_fullscreen: (was_fullscreen || was_maximized).then_some(was_fullscreen), + was_fullscreen, }) } else { None @@ -366,140 +394,103 @@ impl Workspace { }) } - pub fn outputs_for_element(&self, elem: &CosmicMapped) -> impl Iterator { - self.floating_layer - .space - .outputs_for_element(elem) - .into_iter() - .chain(self.tiling_layer.output_for_element(elem).cloned()) - } - - pub fn output_under(&self, point: Point) -> Option<&Output> { - let space = &self.floating_layer.space; - space.outputs().find(|o| { - let internal_output_geo = space.output_geometry(o).unwrap(); - let external_output_geo = o.geometry(); - internal_output_geo.contains(point - external_output_geo.loc + internal_output_geo.loc) - }) - } - pub fn element_under( &mut self, - location: Point, + location: Point, overview: OverviewMode, - ) -> Option<(PointerFocusTarget, Point)> { + ) -> Option<(PointerFocusTarget, Point)> { + let location = location.to_local(&self.output); self.floating_layer .space - .element_under(location) - .map(|(mapped, p)| (mapped.clone().into(), p)) + .element_under(location.as_logical()) + .map(|(mapped, p)| (mapped.clone().into(), p.as_local())) .or_else(|| self.tiling_layer.element_under(location, overview)) + .map(|(m, p)| (m, p.to_global(&self.output))) } - pub fn element_geometry(&self, elem: &CosmicMapped) -> Option> { - let space = &self.floating_layer.space; - let outputs = space.outputs().collect::>(); - let offset = if outputs.len() == 1 - && space.output_geometry(&outputs[0]).unwrap().loc == Point::from((0, 0)) - { - outputs[0].geometry().loc - } else { - (0, 0).into() - }; - + pub fn element_geometry(&self, elem: &CosmicMapped) -> Option> { self.floating_layer - .space .element_geometry(elem) .or_else(|| self.tiling_layer.element_geometry(elem)) - .map(|mut geo| { - geo.loc += offset; - geo - }) } - pub fn recalculate(&mut self, output: &Output) { - if let Some(f) = self.fullscreen.get(output) { - if !f.exclusive { - f.window - .set_geometry(layer_map_for_output(output).non_exclusive_zone()); - } - } - self.tiling_layer.recalculate(output); + pub fn recalculate(&mut self) { + self.tiling_layer.recalculate(); + self.floating_layer.refresh(); } - pub fn maximize_request( - &mut self, - window: &CosmicSurface, - output: &Output, - evlh: LoopHandle<'static, crate::state::State>, - ) { - if self.fullscreen.contains_key(output) { + pub fn maximize_request(&mut self, window: &CosmicSurface) { + if self.fullscreen.is_some() { return; } - self.floating_layer.maximize_request(window); - - window.set_fullscreen(false); - window.set_maximized(true); - self.set_fullscreen(window, output, false, evlh) + if let Some(elem) = self.element_for_surface(window).cloned() { + let mut state = elem.maximized_state.lock().unwrap(); + if state.is_none() { + *state = Some(MaximizedState { + original_geometry: self.element_geometry(&elem).unwrap(), + original_layer: if self.is_floating(&elem) { + ManagedLayer::Floating + } else { + ManagedLayer::Tiling + }, + }); + std::mem::drop(state); + self.floating_layer.map_maximized(elem); + } + } } pub fn unmaximize_request(&mut self, window: &CosmicSurface) -> Option> { - if self - .fullscreen - .values() - .any(|f| &f.window.surface() == window) - { - self.unfullscreen_request(window) - } else { - None + if let Some(elem) = self.element_for_surface(window).cloned() { + let mut state = elem.maximized_state.lock().unwrap(); + if let Some(state) = state.take() { + match state.original_layer { + ManagedLayer::Tiling => { + // should still be mapped in tiling + self.floating_layer.unmap(&elem); + elem.output_enter(&self.output, elem.bbox()); + elem.set_maximized(false); + elem.set_geometry(state.original_geometry.to_global(&self.output)); + elem.configure(); + self.tiling_layer.recalculate(); + return self + .tiling_layer + .element_geometry(&elem) + .map(|geo| geo.size.as_logical()); + } + ManagedLayer::Floating => { + elem.set_maximized(false); + self.floating_layer.map_internal( + elem.clone(), + Some(state.original_geometry.loc), + Some(state.original_geometry.size.as_logical()), + ); + return Some(state.original_geometry.size.as_logical()); + } + } + } } + None } pub fn fullscreen_request( &mut self, window: &CosmicSurface, - output: &Output, - evlh: LoopHandle<'static, crate::state::State>, + previously: Option<(ManagedLayer, WorkspaceHandle)>, ) { if self .fullscreen - .get(output) - .map(|f| &f.window.surface() != window) - .unwrap_or(false) + .as_ref() + .filter(|f| f.ended_at.is_none()) + .is_some() { return; } - if !window.is_maximized(true) { - self.floating_layer.maximize_request(window); - } - - window.set_maximized(false); window.set_fullscreen(true); - self.set_fullscreen(window, output, true, evlh) - } - - fn set_fullscreen<'a>( - &mut self, - window: &'a CosmicSurface, - output: &Output, - exclusive: bool, - evlh: LoopHandle<'static, crate::state::State>, - ) { - if let Some(mapped) = self - .mapped() - .find(|m| m.windows().any(|(w, _)| &w == window)) - { - mapped.set_active(window); - } - - let window = CosmicWindow::new(window.clone(), evlh); - let geo = if exclusive { - output.geometry() - } else { - layer_map_for_output(output).non_exclusive_zone() - }; - let original_size = window.geometry().size; + let geo = self.output.geometry(); + let original_geometry = window.geometry().as_global(); let signal = if let Some(surface) = window.wl_surface() { let signal = Arc::new(AtomicBool::new(false)); add_blocker( @@ -513,35 +504,35 @@ impl Workspace { None }; window.set_geometry(geo); - window.output_enter(output, Rectangle::from_loc_and_size((0, 0), geo.size)); - window.surface().send_configure(); - - self.fullscreen.insert( - output.clone(), - FullscreenSurface { - window: window.clone(), - exclusive, - original_size, - start_at: Some(Instant::now()), - ended_at: None, - animation_signal: signal, - }, - ); + window.send_configure(); + + self.fullscreen = Some(FullscreenSurface { + surface: window.clone(), + previously, + original_geometry, + start_at: Some(Instant::now()), + ended_at: None, + animation_signal: signal, + }); } - pub fn unfullscreen_request(&mut self, window: &CosmicSurface) -> Option> { - if let Some((output, f)) = self + #[must_use] + pub fn unfullscreen_request( + &mut self, + window: &CosmicSurface, + ) -> Option<(ManagedLayer, WorkspaceHandle)> { + if let Some(f) = self .fullscreen - .iter_mut() - .find(|(_, f)| &f.window.surface() == window) + .as_mut() + .filter(|f| &f.surface == window && f.ended_at.is_none()) { - f.window.output_leave(output); - window.set_maximized(false); window.set_fullscreen(false); - let result = self.floating_layer.unmaximize_request(window); + window.set_geometry(f.original_geometry); + self.floating_layer.refresh(); - self.tiling_layer.recalculate(output); + self.tiling_layer.recalculate(); self.tiling_layer.refresh(); + let signal = if let Some(surface) = window.wl_surface() { let signal = Arc::new(AtomicBool::new(false)); add_blocker( @@ -574,90 +565,36 @@ impl Workspace { } } - result + f.previously } else { None } } - pub fn remove_fullscreen(&mut self, output: &Output) { - if let Some(FullscreenSurface { - window, - ended_at, - start_at, - animation_signal, - .. - }) = self.fullscreen.get_mut(output) - { - window.output_leave(output); - let surface = window.surface(); - surface.set_maximized(false); - surface.set_fullscreen(false); - self.floating_layer.unmaximize_request(&surface); - self.floating_layer.refresh(); - self.tiling_layer.recalculate(output); - self.tiling_layer.refresh(); - let signal = if let Some(surface) = surface.wl_surface() { - let signal = Arc::new(AtomicBool::new(false)); - add_blocker( - &surface, - FullscreenBlocker { - signal: signal.clone(), - }, - ); - Some(signal) - } else { - None - }; - surface.send_configure(); - - *ended_at = Some( - Instant::now() - - (FULLSCREEN_ANIMATION_DURATION - - start_at - .take() - .map(|earlier| { - Instant::now() - .duration_since(earlier) - .min(FULLSCREEN_ANIMATION_DURATION) - }) - .unwrap_or(FULLSCREEN_ANIMATION_DURATION)), - ); - if let Some(new_signal) = signal { - if let Some(old_signal) = animation_signal.replace(new_signal) { - old_signal.store(true, Ordering::SeqCst); - } - } + #[must_use] + pub fn remove_fullscreen(&mut self) -> Option<(CosmicMapped, ManagedLayer, WorkspaceHandle)> { + if let Some(surface) = self.fullscreen.as_ref().map(|f| f.surface.clone()) { + self.unfullscreen_request(&surface) + .map(|(l, h)| (self.element_for_surface(&surface).unwrap().clone(), l, h)) + } else { + None } } - pub fn maximize_toggle( - &mut self, - window: &CosmicSurface, - output: &Output, - evlh: LoopHandle<'static, crate::state::State>, - ) { - if self.fullscreen.contains_key(output) { + pub fn maximize_toggle(&mut self, window: &CosmicSurface) { + if window.is_maximized(true) { self.unmaximize_request(window); } else { - self.maximize_request(window, output, evlh); + self.maximize_request(window); } } - pub fn get_fullscreen(&self, output: &Output) -> Option<&CosmicWindow> { - self.fullscreen - .get(output) - .filter(|f| f.alive() && f.exclusive) - .filter(|f| f.ended_at.is_none() && f.start_at.is_none()) - .map(|f| &f.window) - } - - pub fn get_maximized(&self, output: &Output) -> Option<&CosmicWindow> { + pub fn get_fullscreen(&self) -> Option<&CosmicSurface> { self.fullscreen - .get(output) - .filter(|f| f.alive() && !f.exclusive) + .as_ref() + .filter(|f| f.alive()) .filter(|f| f.ended_at.is_none() && f.start_at.is_none()) - .map(|f| &f.window) + .map(|f| &f.surface) } pub fn resize_request( @@ -690,8 +627,8 @@ impl Workspace { if let Some(toplevel) = focused.toplevel() { if self .fullscreen - .values() - .any(|f| f.window.surface().wl_surface().as_ref() == Some(&toplevel)) + .as_ref() + .is_some_and(|f| f.surface.wl_surface().as_ref() == Some(&toplevel)) { return false; } @@ -713,15 +650,27 @@ impl Workspace { indicator_thickness: u8, ) -> Option { let pointer = seat.get_pointer().unwrap(); - let pos = pointer.current_location(); + let pos = pointer.current_location().as_global(); + + if self + .fullscreen + .as_ref() + .is_some_and(|f| &f.surface == window) + { + let _ = self.remove_fullscreen(); // We are moving this window, we don't need to send it back to it's original workspace + } let mapped = self.element_for_surface(&window)?.clone(); - let mut initial_window_location = self.element_geometry(&mapped).unwrap().loc; + let mut initial_window_location = self + .element_geometry(&mapped) + .unwrap() + .loc + .to_global(&self.output); - if mapped.is_fullscreen(true) || mapped.is_maximized(true) { + if mapped.maximized_state.lock().unwrap().is_some() { // If surface is maximized then unmaximize it let new_size = self.unmaximize_request(window); - let ratio = pos.x / output.geometry().size.w as f64; + let ratio = pos.to_local(&self.output).x / output.geometry().size.w as f64; initial_window_location = new_size .map(|size| (pos.x - (size.w as f64 * ratio), pos.y).into()) @@ -754,7 +703,7 @@ impl Workspace { .into_iter() { self.tiling_layer.unmap(&window); - self.floating_layer.map(window, seat, None); + self.floating_layer.map(window, None); } self.tiling_enabled = false; } else { @@ -768,7 +717,7 @@ impl Workspace { { self.floating_layer.unmap(&window); self.tiling_layer - .map(window, seat, focus_stack.iter(), None) + .map(window, Some(focus_stack.iter()), None) } self.tiling_enabled = true; } @@ -779,12 +728,12 @@ impl Workspace { if let Some(window) = self.focus_stack.get(seat).iter().next().cloned() { if self.tiling_layer.mapped().any(|(_, m, _)| m == &window) { self.tiling_layer.unmap(&window); - self.floating_layer.map(window, seat, None); + self.floating_layer.map(window, None); } else if self.floating_layer.mapped().any(|w| w == &window) { let focus_stack = self.focus_stack.get(seat); self.floating_layer.unmap(&window); self.tiling_layer - .map(window, seat, focus_stack.iter(), None) + .map(window, Some(focus_stack.iter()), None) } } } @@ -808,37 +757,23 @@ impl Workspace { pub fn is_fullscreen(&self, mapped: &CosmicMapped) -> bool { self.fullscreen - .values() - .any(|f| f.exclusive && f.window.surface() == mapped.active_window()) - } - - pub fn is_maximized(&self, mapped: &CosmicMapped) -> bool { - self.fullscreen - .values() - .any(|f| !f.exclusive && f.window.surface() == mapped.active_window()) + .as_ref() + .is_some_and(|f| f.surface == mapped.active_window()) } pub fn is_floating(&self, mapped: &CosmicMapped) -> bool { - !self - .fullscreen - .values() - .any(|f| f.window.surface() == mapped.active_window()) - && self.floating_layer.mapped().any(|m| m == mapped) + !self.is_fullscreen(mapped) && self.floating_layer.mapped().any(|m| m == mapped) } pub fn is_tiled(&self, mapped: &CosmicMapped) -> bool { - !self - .fullscreen - .values() - .any(|f| f.window.surface() == mapped.active_window()) - && self.tiling_layer.mapped().any(|(_, m, _)| m == mapped) + !self.is_fullscreen(mapped) && self.tiling_layer.mapped().any(|(_, m, _)| m == mapped) } pub fn node_desc(&self, focus: KeyboardFocusTarget) -> Option { match focus { KeyboardFocusTarget::Element(mapped) => { - self.tiling_layer.mapped().find_map(|(output, m, _)| { - (m == &mapped).then_some(output.clone()).and_then(|output| { + self.tiling_layer.mapped().find_map(|(_, m, _)| { + if m == &mapped { mapped .tiling_node_id .lock() @@ -846,7 +781,6 @@ impl Workspace { .clone() .map(|node_id| NodeDesc { handle: self.handle.clone(), - output: output.downgrade(), node: node_id, stack_window: if mapped .stack_ref() @@ -858,12 +792,13 @@ impl Workspace { None }, }) - }) + } else { + None + } }) } - KeyboardFocusTarget::Group(WindowGroup { output, node, .. }) => Some(NodeDesc { + KeyboardFocusTarget::Group(WindowGroup { node, .. }) => Some(NodeDesc { handle: self.handle.clone(), - output, node, stack_window: None, }), @@ -877,7 +812,7 @@ impl Workspace { seat: &Seat, swap_desc: Option, ) -> FocusResult { - if self.fullscreen.contains_key(&seat.active_output()) { + if self.fullscreen.is_some() { return FocusResult::None; } @@ -895,25 +830,25 @@ impl Workspace { direction: Direction, seat: &Seat, ) -> MoveResult { - if let Some(f) = self.fullscreen.get(&seat.active_output()) { - MoveResult::MoveFurther(KeyboardFocusTarget::Fullscreen(f.window.clone())) + if let Some(f) = self.fullscreen.as_ref() { + MoveResult::MoveFurther(KeyboardFocusTarget::Fullscreen(f.surface.clone())) } else { self.floating_layer - .move_current_element(direction, seat) + .move_current_element(direction, seat, self.tiling_layer.theme.clone()) .or_else(|| self.tiling_layer.move_current_node(direction, seat)) } } - pub fn render_output<'a, R>( + pub fn render<'a, R>( &self, renderer: &mut R, - output: &Output, override_redirect_windows: &[X11Surface], xwm_state: Option<&'a mut XWaylandState>, draw_focus_indicator: Option<&Seat>, overview: (OverviewMode, Option<(SwapIndicator, Option<&Tree>)>), resize_indicator: Option<(ResizeMode, ResizeIndicator)>, indicator_thickness: u8, + theme: &CosmicTheme, ) -> Result< ( Vec>, @@ -935,20 +870,28 @@ impl Workspace { let mut window_elements = Vec::new(); let mut popup_elements = Vec::new(); - let output_scale = output.current_scale().fractional_scale(); - let layer_map = layer_map_for_output(output); - let zone = layer_map.non_exclusive_zone(); + let output_scale = self.output.current_scale().fractional_scale(); + let zone = { + let layer_map = layer_map_for_output(&self.output); + layer_map.non_exclusive_zone().as_local() + }; // OR windows above all popup_elements.extend( override_redirect_windows .iter() - .filter(|or| (*or).geometry().intersection(output.geometry()).is_some()) + .filter(|or| { + (*or) + .geometry() + .as_global() + .intersection(self.output.geometry()) + .is_some() + }) .flat_map(|or| { AsRenderElements::::render_elements::>( or, renderer, - (or.geometry().loc - output.geometry().loc) + (or.geometry().loc - self.output.geometry().loc.as_logical()) .to_physical_precise_round(output_scale), Scale::from(output_scale), 1.0, @@ -956,32 +899,27 @@ impl Workspace { }), ); - if let Some(fullscreen) = self.fullscreen.get(output) { + if let Some(fullscreen) = self.fullscreen.as_ref() { // fullscreen window - let bbox = fullscreen.window.bbox(); + let bbox = fullscreen.surface.bbox().as_local(); let element_geo = Rectangle::from_loc_and_size( - self.element_for_surface(&fullscreen.window.surface()) + self.element_for_surface(&fullscreen.surface) .and_then(|elem| { self.floating_layer - .space .element_geometry(elem) .or_else(|| self.tiling_layer.element_geometry(elem)) .map(|mut geo| { - geo.loc -= elem.geometry().loc; + geo.loc -= elem.geometry().loc.as_local(); geo }) }) .unwrap_or(bbox) .loc, - fullscreen.original_size, + fullscreen.original_geometry.size.as_local(), ); - let mut full_geo = if fullscreen.exclusive { - Rectangle::from_loc_and_size((0, 0), output.geometry().size) - } else { - layer_map.non_exclusive_zone() - }; - + let mut full_geo = + Rectangle::from_loc_and_size((0, 0), self.output.geometry().size.as_local()); if fullscreen.start_at.is_none() { if bbox != full_geo { if bbox.size.w < full_geo.size.w { @@ -1027,14 +965,17 @@ impl Workspace { (None, None) => (full_geo, 1.0), }; - let render_loc = target_geo.loc.to_physical_precise_round(output_scale); + let render_loc = target_geo + .loc + .as_logical() + .to_physical_precise_round(output_scale); let scale = Scale { x: target_geo.size.w as f64 / bbox.size.w as f64, y: target_geo.size.h as f64 / bbox.size.h as f64, }; let (w_elements, p_elements) = fullscreen - .window + .surface .split_render_elements::>( renderer, render_loc, @@ -1052,12 +993,12 @@ impl Workspace { if self .fullscreen - .get(output) + .as_ref() .map(|f| f.start_at.is_some() || f.ended_at.is_some()) .unwrap_or(true) { let focused = draw_focus_indicator - .filter(|_| !self.fullscreen.contains_key(output)) + .filter(|_| !self.fullscreen.is_some()) .and_then(|seat| self.focus_stack.get(seat).last().cloned()); // floating surfaces @@ -1078,13 +1019,13 @@ impl Workspace { OverviewMode::None => 1.0, }; - let (w_elements, p_elements) = self.floating_layer.render_output::( + let (w_elements, p_elements) = self.floating_layer.render::( renderer, - output, focused.as_ref(), resize_indicator.clone(), indicator_thickness, alpha, + theme, ); popup_elements.extend(p_elements.into_iter().map(WorkspaceRenderElement::from)); window_elements.extend(w_elements.into_iter().map(WorkspaceRenderElement::from)); @@ -1102,14 +1043,14 @@ impl Workspace { }; //tiling surfaces - let (w_elements, p_elements) = self.tiling_layer.render_output::( + let (w_elements, p_elements) = self.tiling_layer.render::( renderer, - output, draw_focus_indicator, - layer_map.non_exclusive_zone(), + zone, overview, resize_indicator, indicator_thickness, + theme, )?; popup_elements.extend(p_elements.into_iter().map(WorkspaceRenderElement::from)); window_elements.extend(w_elements.into_iter().map(WorkspaceRenderElement::from)); @@ -1131,7 +1072,7 @@ impl Workspace { if let Some(xwm) = xwm_state.and_then(|state| state.xwm.as_mut()) { if let Err(err) = - xwm.update_stacking_order_upwards(window_elements.iter().map(|e| e.id())) + xwm.update_stacking_order_upwards(window_elements.iter().rev().map(|e| e.id())) { warn!( wm_id = ?xwm.id(), diff --git a/src/state.rs b/src/state.rs index 8d4feeba..4ba974ca 100644 --- a/src/state.rs +++ b/src/state.rs @@ -137,6 +137,8 @@ pub struct Common { pub clock: Clock, pub should_stop: bool, + pub theme: cosmic::Theme, + #[cfg(feature = "debug")] pub egui: Egui, @@ -283,7 +285,7 @@ impl State { .with_context(|| "Failed to load languages") .unwrap(); - let clock = Clock::new().expect("Failed to initialize clock"); + let clock = Clock::new(); let config = Config::load(&handle); let compositor_state = CompositorState::new::(dh); let data_device_state = DataDeviceState::new::(dh); @@ -330,6 +332,8 @@ impl State { clock, should_stop: false, + theme: cosmic::theme::system_preference(), + #[cfg(feature = "debug")] egui: Egui { active: false, @@ -511,50 +515,34 @@ impl Common { let active = self.shell.active_space(output); active.mapped().for_each(|mapped| { - let outputs_for_element: Vec<_> = active.outputs_for_element(mapped).collect(); - if outputs_for_element.contains(&output) { - let window = mapped.active_window(); - window.with_surfaces(|surface, states| { - let primary_scanout_output = update_surface_primary_scanout_output( - surface, - output, - states, - render_element_states, - |current_output, current_state, next_output, next_state| { - if outputs_for_element.contains(current_output) { - default_primary_scanout_output_compare( - current_output, - current_state, - next_output, - next_state, - ) - } else { - next_output - } - }, - ); - if let Some(output) = primary_scanout_output { - with_fractional_scale(states, |fraction_scale| { - fraction_scale - .set_preferred_scale(output.current_scale().fractional_scale()); - }); - } - }); - window.send_frame(output, time, throttle, surface_primary_scanout_output); - if let Some(feedback) = window - .wl_surface() - .and_then(|wl_surface| { - source_node_for_surface(&wl_surface, &self.display_handle) - }) - .and_then(|source| dmabuf_feedback(source)) - { - window.send_dmabuf_feedback( - output, - &feedback, - render_element_states, - surface_primary_scanout_output, - ); + let window = mapped.active_window(); + window.with_surfaces(|surface, states| { + let primary_scanout_output = update_surface_primary_scanout_output( + surface, + output, + states, + render_element_states, + |_current_output, _current_state, next_output, _next_state| next_output, + ); + if let Some(output) = primary_scanout_output { + with_fractional_scale(states, |fraction_scale| { + fraction_scale + .set_preferred_scale(output.current_scale().fractional_scale()); + }); } + }); + window.send_frame(output, time, throttle, surface_primary_scanout_output); + if let Some(feedback) = window + .wl_surface() + .and_then(|wl_surface| source_node_for_surface(&wl_surface, &self.display_handle)) + .and_then(|source| dmabuf_feedback(source)) + { + window.send_dmabuf_feedback( + output, + &feedback, + render_element_states, + surface_primary_scanout_output, + ); } }); @@ -565,10 +553,8 @@ impl Common { .filter(|w| w.handle != active.handle) { space.mapped().for_each(|mapped| { - if space.outputs_for_element(mapped).any(|o| &o == output) { - let window = mapped.active_window(); - window.send_frame(output, time, throttle, |_, _| None); - } + let window = mapped.active_window(); + window.send_frame(space.output(), time, throttle, |_, _| None); }); } diff --git a/src/theme.rs b/src/theme.rs new file mode 100644 index 00000000..4336caa4 --- /dev/null +++ b/src/theme.rs @@ -0,0 +1,67 @@ +// insert into the event loop, a watcher for the theme & theme mode for changes + +// update a Arc> in the state on change of the theme and mark all interfaces for a redraw. + +use calloop::LoopHandle; +use cosmic::cosmic_theme::{ + palette::{self, Srgba}, + Theme, ThemeMode, +}; + +use crate::state::State; + +pub(crate) fn group_color(theme: &Theme) -> [f32; 3] { + let neutral_8 = theme.palette.neutral_8; + [neutral_8.red, neutral_8.green, neutral_8.blue] +} + +pub(crate) fn active_window_hint(theme: &Theme) -> palette::Srgba { + if let Some(hint) = theme.window_hint { + palette::Srgba::from(hint) + } else { + theme.accent_color() + } +} + +pub fn watch_theme(handle: LoopHandle<'_, State>) -> Result<(), cosmic_config::Error> { + let (ping_tx, ping_rx) = calloop::ping::make_ping().unwrap(); + let config_mode_helper = ThemeMode::config()?; + let config_dark_helper = Theme::::dark_config()?; + let config_light_helper = Theme::::light_config()?; + + if let Err(e) = handle.insert_source(ping_rx, move |_, _, state| { + let new_theme = cosmic::theme::system_preference(); + let theme = &mut state.common.theme; + + if theme.theme_type != new_theme.theme_type { + *theme = new_theme; + state.common.shell.set_theme(theme.clone()); + state.common.shell.workspaces.spaces().for_each(|s| { + s.mapped().for_each(|m| { + m.update_theme(theme.clone()); + m.force_redraw(); + }) + }); + } + }) { + tracing::error!("{e}"); + }; + + let ping_tx_clone = ping_tx.clone(); + let theme_watcher_mode = config_mode_helper.watch(move |_, _keys| { + ping_tx_clone.ping(); + })?; + let ping_tx_clone = ping_tx.clone(); + let theme_watcher_light = config_light_helper.watch(move |_, _keys| { + ping_tx_clone.ping(); + })?; + let theme_watcher_dark = config_dark_helper.watch(move |_, _keys| { + ping_tx.ping(); + })?; + + std::mem::forget(theme_watcher_dark); + std::mem::forget(theme_watcher_light); + std::mem::forget(theme_watcher_mode); + + Ok(()) +} diff --git a/src/utils/geometry.rs b/src/utils/geometry.rs new file mode 100644 index 00000000..19e6f6ed --- /dev/null +++ b/src/utils/geometry.rs @@ -0,0 +1,149 @@ +use smithay::{ + output::Output, + utils::{Coordinate, Logical, Point, Rectangle, Size}, +}; + +use super::prelude::OutputExt; + +/// Marker type for coordinates in global space +#[derive(Debug)] +pub struct Global; + +/// Marker type for coordinates in workspace local space +#[derive(Debug)] +pub struct Local; + +pub trait PointExt { + fn as_global(self) -> Point; + fn as_local(self) -> Point; +} + +pub trait PointGlobalExt { + fn to_local(self, output: &Output) -> Point; + fn as_logical(self) -> Point; +} + +pub trait PointLocalExt { + fn to_global(self, output: &Output) -> Point; + fn as_logical(self) -> Point; +} + +pub trait SizeExt { + fn as_logical(self) -> Size; + fn as_local(self) -> Size; + fn as_global(self) -> Size; +} + +pub trait RectExt { + fn as_global(self) -> Rectangle; + fn as_local(self) -> Rectangle; +} + +pub trait RectGlobalExt { + fn to_local(self, output: &Output) -> Rectangle; + fn as_logical(self) -> Rectangle; +} + +pub trait RectLocalExt { + fn to_global(self, output: &Output) -> Rectangle; + fn as_logical(self) -> Rectangle; +} + +impl PointExt for Point { + fn as_global(self) -> Point { + (self.x, self.y).into() + } + + fn as_local(self) -> Point { + (self.x, self.y).into() + } +} + +impl PointGlobalExt for Point { + fn to_local(self, output: &Output) -> Point { + let point = (self.to_f64() - output.geometry().loc.to_f64()).as_logical(); + (C::from_f64(point.x), C::from_f64(point.y)).into() + } + + fn as_logical(self) -> Point { + (self.x, self.y).into() + } +} + +impl PointLocalExt for Point { + fn to_global(self, output: &Output) -> Point { + let point = + (self.to_f64().as_logical() + output.geometry().loc.to_f64().as_logical()).as_global(); + (C::from_f64(point.x), C::from_f64(point.y)).into() + } + + fn as_logical(self) -> Point { + (self.x, self.y).into() + } +} + +impl SizeExt for Size { + fn as_logical(self) -> Size { + (self.w, self.h).into() + } + fn as_global(self) -> Size { + self + } + fn as_local(self) -> Size { + (self.w, self.h).into() + } +} + +impl SizeExt for Size { + fn as_logical(self) -> Size { + (self.w, self.h).into() + } + fn as_global(self) -> Size { + (self.w, self.h).into() + } + fn as_local(self) -> Size { + self + } +} + +impl SizeExt for Size { + fn as_logical(self) -> Size { + self + } + fn as_global(self) -> Size { + (self.w, self.h).into() + } + fn as_local(self) -> Size { + (self.w, self.h).into() + } +} + +impl RectExt for Rectangle { + fn as_global(self) -> Rectangle { + Rectangle::from_loc_and_size(self.loc.as_global(), (self.size.w, self.size.h)) + } + + fn as_local(self) -> Rectangle { + Rectangle::from_loc_and_size(self.loc.as_local(), (self.size.w, self.size.h)) + } +} + +impl RectGlobalExt for Rectangle { + fn to_local(self, output: &Output) -> Rectangle { + Rectangle::from_loc_and_size(self.loc.to_local(output), (self.size.w, self.size.h)) + } + + fn as_logical(self) -> Rectangle { + Rectangle::from_loc_and_size(self.loc.as_logical(), self.size.as_logical()) + } +} + +impl RectLocalExt for Rectangle { + fn to_global(self, output: &Output) -> Rectangle { + Rectangle::from_loc_and_size(self.loc.to_global(output), (self.size.w, self.size.h)) + } + + fn as_logical(self) -> Rectangle { + Rectangle::from_loc_and_size(self.loc.as_logical(), self.size.as_logical()) + } +} diff --git a/src/utils/iced.rs b/src/utils/iced.rs index 614d943d..a3a0cf13 100644 --- a/src/utils/iced.rs +++ b/src/utils/iced.rs @@ -229,6 +229,7 @@ impl IcedElement

{ program: P, size: impl Into>, handle: LoopHandle<'static, crate::state::State>, + theme: cosmic::Theme, ) -> IcedElement

{ let size = size.into(); let mut renderer = @@ -257,7 +258,7 @@ impl IcedElement

{ pending_update: None, size, cursor_pos: None, - theme: Theme::dark(), // TODO + theme, renderer, state, debug, @@ -308,6 +309,11 @@ impl IcedElement

{ self.0.lock().unwrap().update(true); } + pub fn set_theme(&self, theme: cosmic::Theme) { + let mut guard = self.0.lock().unwrap(); + guard.theme = theme.clone(); + } + pub fn force_redraw(&self) { let mut internal = self.0.lock().unwrap(); for (_buffer, ref mut old_primitives) in internal.buffers.values_mut() { diff --git a/src/utils/mod.rs b/src/utils/mod.rs index ff39a61d..6531f067 100644 --- a/src/utils/mod.rs +++ b/src/utils/mod.rs @@ -2,6 +2,7 @@ mod ids; pub(crate) use self::ids::id_gen; +pub mod geometry; pub mod iced; pub mod prelude; pub mod tween; diff --git a/src/utils/prelude.rs b/src/utils/prelude.rs index d5e8f42b..bbedcba4 100644 --- a/src/utils/prelude.rs +++ b/src/utils/prelude.rs @@ -11,20 +11,21 @@ use smithay::{ Seat, }, output::Output, - utils::{Buffer, IsAlive, Logical, Monotonic, Point, Rectangle, Time, Transform}, + utils::{Buffer, IsAlive, Monotonic, Point, Rectangle, Time, Transform}, wayland::compositor::with_states, }; +pub use super::geometry::*; pub use crate::shell::{Shell, Workspace}; pub use crate::state::{Common, State}; pub use crate::wayland::handlers::xdg_shell::popup::update_reactive_popups; pub trait OutputExt { - fn geometry(&self) -> Rectangle; + fn geometry(&self) -> Rectangle; } impl OutputExt for Output { - fn geometry(&self) -> Rectangle { + fn geometry(&self) -> Rectangle { Rectangle::from_loc_and_size(self.current_location(), { Transform::from(self.current_transform()) .transform_size( @@ -36,6 +37,7 @@ impl OutputExt for Output { .to_logical(self.current_scale().fractional_scale()) .to_i32_round() }) + .as_global() } } diff --git a/src/wayland/handlers/compositor.rs b/src/wayland/handlers/compositor.rs index 3746d57e..e725c1fe 100644 --- a/src/wayland/handlers/compositor.rs +++ b/src/wayland/handlers/compositor.rs @@ -171,12 +171,12 @@ impl CompositorHandler for State { on_commit_buffer_handler::(surface); // then handle initial configure events and map windows if necessary - if let Some((window, seat)) = self + if let Some((window, _, _)) = self .common .shell .pending_windows .iter() - .find(|(window, _)| window.wl_surface().as_ref() == Some(surface)) + .find(|(window, _, _)| window.wl_surface().as_ref() == Some(surface)) .cloned() { match window { @@ -185,9 +185,8 @@ impl CompositorHandler for State { if self.toplevel_ensure_initial_configure(&toplevel) && with_renderer_surface_state(&surface, |state| state.buffer().is_some()) { - let output = seat.active_output(); window.on_commit(); - Shell::map_window(self, &window, &output); + Shell::map_window(self, &window); } else { return; } @@ -254,7 +253,7 @@ impl CompositorHandler for State { let changed = layer_map_for_output(&output).arrange(); if changed { for workspace in self.common.shell.workspaces.spaces_mut() { - workspace.recalculate(&output); + workspace.recalculate(); } } } diff --git a/src/wayland/handlers/layer_shell.rs b/src/wayland/handlers/layer_shell.rs index f2726921..70acd77a 100644 --- a/src/wayland/handlers/layer_shell.rs +++ b/src/wayland/handlers/layer_shell.rs @@ -41,8 +41,7 @@ impl WlrLayerShellHandler for State { } fn new_popup(&mut self, _parent: WlrLayerSurface, popup: PopupSurface) { - let positioner = popup.with_pending_state(|state| state.positioner); - self.common.shell.unconstrain_popup(&popup, &positioner); + self.common.shell.unconstrain_popup(&popup); if popup.send_configure().is_ok() { self.common @@ -76,7 +75,7 @@ impl WlrLayerShellHandler for State { } for workspace in self.common.shell.workspaces.spaces_mut() { - workspace.recalculate(&output); + workspace.recalculate(); } // collect screencopy sessions needing an update diff --git a/src/wayland/handlers/screencopy.rs b/src/wayland/handlers/screencopy.rs index dd775448..16a37b19 100644 --- a/src/wayland/handlers/screencopy.rs +++ b/src/wayland/handlers/screencopy.rs @@ -47,7 +47,7 @@ use crate::{ }, shell::{CosmicMappedRenderElement, CosmicSurface, WorkspaceRenderElement}, state::{BackendData, ClientState, Common, State}, - utils::prelude::OutputExt, + utils::prelude::{OutputExt, PointExt}, wayland::protocols::{ screencopy::{ delegate_screencopy, BufferInfo, BufferParams, CursorMode as ScreencopyCursorMode, @@ -106,7 +106,7 @@ impl ScreencopyHandler for State { if let Some(pointer) = seat.get_pointer() { if output .geometry() - .contains(pointer.current_location().to_i32_round()) + .contains(pointer.current_location().to_i32_round().as_global()) { session.cursor_enter(seat, InputType::Pointer); } @@ -1015,7 +1015,12 @@ pub fn render_window_to_buffer( renderer.bind(render_buffer).map_err(DTError::Rendering)?; } - dt.render_output(renderer, age, &elements, CLEAR_COLOR) + dt.render_output( + renderer, + age, + &elements, + CLEAR_COLOR, // TODO use a theme neutral color + ) } let node = node_from_params(¶ms, &mut state.backend, None); @@ -1296,7 +1301,7 @@ pub fn schedule_offscreen_workspace_session( if !session.alive() { return; } - if !state.common.shell.outputs.contains(&output) { + if !state.common.shell.outputs().any(|o| o == &output) { return; } match render_workspace_to_buffer( diff --git a/src/wayland/handlers/workspace.rs b/src/wayland/handlers/workspace.rs index 56b268b7..dee16c74 100644 --- a/src/wayland/handlers/workspace.rs +++ b/src/wayland/handlers/workspace.rs @@ -1,7 +1,6 @@ // SPDX-License-Identifier: GPL-3.0-only use crate::{ - shell::WorkspaceMode, state::ClientState, utils::prelude::*, wayland::protocols::workspace::{ @@ -30,19 +29,12 @@ impl WorkspaceHandler for State { for request in requests.into_iter() { match request { Request::Activate(handle) => { - let maybe = match &self.common.shell.workspaces { - WorkspaceMode::Global(set) => set - .workspaces + let maybe = self.common.shell.workspaces.iter().find_map(|(o, set)| { + set.workspaces .iter() .position(|w| w.handle == handle) - .map(|i| (self.common.last_active_seat().active_output(), i)), - WorkspaceMode::OutputBound(sets, _) => sets.iter().find_map(|(o, set)| { - set.workspaces - .iter() - .position(|w| w.handle == handle) - .map(|i| (o.clone(), i)) - }), - }; + .map(|i| (o.clone(), i)) + }); if let Some((output, idx)) = maybe { let _ = self.common.shell.activate(&output, idx); // TODO: move cursor? diff --git a/src/wayland/handlers/xdg_shell/mod.rs b/src/wayland/handlers/xdg_shell/mod.rs index ec0ec87f..bc8b2390 100644 --- a/src/wayland/handlers/xdg_shell/mod.rs +++ b/src/wayland/handlers/xdg_shell/mod.rs @@ -1,6 +1,10 @@ // SPDX-License-Identifier: GPL-3.0-only -use crate::{shell::CosmicSurface, utils::prelude::*, wayland::protocols::screencopy::SessionType}; +use crate::{ + shell::{element::CosmicWindow, CosmicMapped, CosmicSurface, ManagedLayer}, + utils::prelude::*, + wayland::protocols::screencopy::SessionType, +}; use smithay::{ delegate_xdg_shell, desktop::{ @@ -38,7 +42,7 @@ impl XdgShellHandler for State { fn new_toplevel(&mut self, surface: ToplevelSurface) { let seat = self.common.last_active_seat().clone(); let window = CosmicSurface::Wayland(Window::new(surface)); - self.common.shell.pending_windows.push((window, seat)); + self.common.shell.pending_windows.push((window, seat, None)); // We will position the window after the first commit, when we know its size hints } @@ -50,7 +54,7 @@ impl XdgShellHandler for State { if surface.get_parent_surface().is_some() { // let other shells deal with their popups - self.common.shell.unconstrain_popup(&surface, &positioner); + self.common.shell.unconstrain_popup(&surface); if surface.send_configure().is_ok() { self.common @@ -123,7 +127,7 @@ impl XdgShellHandler for State { state.positioner = positioner; }); - self.common.shell.unconstrain_popup(&surface, &positioner); + self.common.shell.unconstrain_popup(&surface); surface.send_repositioned(token); if let Err(err) = surface.send_configure() { warn!( @@ -150,9 +154,6 @@ impl XdgShellHandler for State { } fn maximize_request(&mut self, surface: ToplevelSurface) { - let seat = self.common.last_active_seat(); - let output = seat.active_output(); - if let Some(mapped) = self .common .shell @@ -164,7 +165,7 @@ impl XdgShellHandler for State { .windows() .find(|(w, _)| w.wl_surface().as_ref() == Some(surface.wl_surface())) .unwrap(); - workspace.maximize_request(&window, &output, self.common.event_loop_handle.clone()) + workspace.maximize_request(&window) } } } @@ -187,13 +188,14 @@ impl XdgShellHandler for State { } fn fullscreen_request(&mut self, surface: ToplevelSurface, output: Option) { + let active_output = { + let seat = self.common.last_active_seat(); + seat.active_output() + }; let output = output .as_ref() .and_then(Output::from_resource) - .unwrap_or_else(|| { - let seat = self.common.last_active_seat(); - seat.active_output() - }); + .unwrap_or_else(|| active_output.clone()); if let Some(mapped) = self .common @@ -202,15 +204,70 @@ impl XdgShellHandler for State { .cloned() { if let Some(workspace) = self.common.shell.space_for_mut(&mapped) { - let (window, _) = mapped - .windows() - .find(|(w, _)| w.wl_surface().as_ref() == Some(surface.wl_surface())) - .unwrap(); - workspace.fullscreen_request( - &window, - &output, - self.common.event_loop_handle.clone(), - ) + if workspace.output != output { + let (mapped, layer) = if mapped + .stack_ref() + .map(|stack| stack.len() > 1) + .unwrap_or(false) + { + let stack = mapped.stack_ref().unwrap(); + let surface = stack + .surfaces() + .find(|s| s.wl_surface().as_ref() == Some(surface.wl_surface())) + .unwrap(); + stack.remove_window(&surface); + ( + CosmicMapped::from(CosmicWindow::new( + surface, + self.common.event_loop_handle.clone(), + self.common.theme.clone(), + )), + if workspace.is_tiled(&mapped) { + ManagedLayer::Tiling + } else { + ManagedLayer::Floating + }, + ) + } else { + let layer = workspace.unmap(&mapped).unwrap().layer; + (mapped, layer) + }; + let handle = workspace.handle.clone(); + std::mem::drop(workspace); + + let workspace_handle = self.common.shell.active_space(&output).handle.clone(); + for (window, _) in mapped.windows() { + self.common + .shell + .toplevel_info_state + .toplevel_enter_output(&window, &output); + self.common + .shell + .toplevel_info_state + .toplevel_enter_workspace(&window, &workspace_handle); + } + + let workspace = self.common.shell.active_space_mut(&output); + workspace.floating_layer.map(mapped.clone(), None); + workspace.fullscreen_request(&mapped.active_window(), Some((layer, handle))); + } else { + let (window, _) = mapped + .windows() + .find(|(w, _)| w.wl_surface().as_ref() == Some(surface.wl_surface())) + .unwrap(); + workspace.fullscreen_request(&window, None) + } + } + } else { + if let Some(o) = self + .common + .shell + .pending_windows + .iter_mut() + .find(|(s, _, _)| s.wl_surface().as_ref() == Some(surface.wl_surface())) + .map(|(_, _, o)| o) + { + *o = Some(output); } } } @@ -227,7 +284,23 @@ impl XdgShellHandler for State { .windows() .find(|(w, _)| w.wl_surface().as_ref() == Some(surface.wl_surface())) .unwrap(); - workspace.unfullscreen_request(&window); + if let Some((layer, previous_workspace)) = workspace.unfullscreen_request(&window) { + let old_handle = workspace.handle.clone(); + let new_workspace_handle = self + .common + .shell + .space_for_handle(&previous_workspace) + .is_some() + .then_some(previous_workspace) + .unwrap_or(old_handle); // if the workspace doesn't exist anymore, we can still remap on the right layer + + self.common.shell.remap_unfullscreened_window( + mapped, + &old_handle, + &new_workspace_handle, + layer, + ); + } } } } diff --git a/src/wayland/handlers/xdg_shell/popup.rs b/src/wayland/handlers/xdg_shell/popup.rs index 99842a3a..3500962a 100644 --- a/src/wayland/handlers/xdg_shell/popup.rs +++ b/src/wayland/handlers/xdg_shell/popup.rs @@ -27,27 +27,28 @@ use std::sync::Mutex; use tracing::{trace, warn}; impl Shell { - pub fn unconstrain_popup(&self, surface: &PopupSurface, positioner: &PositionerState) { + pub fn unconstrain_popup(&self, surface: &PopupSurface) { if let Some(parent) = get_popup_toplevel(&surface) { if let Some(elem) = self.element_for_wl_surface(&parent) { let workspace = self.space_for(elem).unwrap(); - let mut element_geo = workspace.element_geometry(elem).unwrap(); + let mut element_geo = workspace + .element_geometry(elem) + .unwrap() + .to_global(workspace.output()); let (window, offset) = elem .windows() .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; + let window_loc: Point = + element_geo.loc + offset.as_global() + window_geo_offset.as_global(); if workspace.is_tiled(elem) { - element_geo.loc = (0, 0).into(); //-= window_loc; - 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()); - } + element_geo.loc = (0, 0).into(); + if !unconstrain_xdg_popup_tile(surface, element_geo.as_logical()) { + unconstrain_xdg_popup(surface, window_loc, workspace.output().geometry()); } - } else if let Some(output) = workspace.output_under(anchor_point) { - unconstrain_xdg_popup(surface, window_loc, output.geometry()); + } else { + unconstrain_xdg_popup(surface, window_loc, workspace.output().geometry()); } } else if let Some((output, layer_surface)) = self.outputs().find_map(|o| { let map = layer_map_for_output(o); @@ -62,7 +63,7 @@ impl Shell { pub fn update_reactive_popups<'a>( window: &Window, - loc: Point, + loc: Point, outputs: impl Iterator, ) { let output_geo = outputs.map(|o| o.geometry()).collect::>(); @@ -79,7 +80,7 @@ pub fn update_reactive_popups<'a>( attributes.current.positioner.clone() }); if positioner.reactive { - let anchor_point = get_anchor_point(&positioner) + loc; + let anchor_point = loc + get_anchor_point(&positioner).as_global(); if let Some(rect) = output_geo .iter() .find(|geo| geo.contains(anchor_point)) @@ -101,7 +102,9 @@ pub fn update_reactive_popups<'a>( } fn unconstrain_xdg_popup_tile(surface: &PopupSurface, rect: Rectangle) -> bool { - let geometry = surface.with_pending_state(|state| state.positioner.get_geometry()); + let toplevel_offset = get_popup_toplevel_coords(surface); + let mut geometry = surface.with_pending_state(|state| state.positioner.get_geometry()); + geometry.loc += toplevel_offset; let offset = check_constrained(geometry, rect); if offset.x != 0 || offset.y != 0 { @@ -116,12 +119,14 @@ fn unconstrain_xdg_popup_tile(surface: &PopupSurface, rect: Rectangle, - rect: Rectangle, + window_loc: Point, + mut rect: Rectangle, ) { - let mut relative = rect; - relative.loc -= window_loc; - let geometry = surface.with_pending_state(|state| state.positioner.get_geometry()); + rect.loc -= window_loc; + let relative = rect.as_logical(); + let toplevel_offset = get_popup_toplevel_coords(surface); + let mut geometry = surface.with_pending_state(|state| state.positioner.get_geometry()); + geometry.loc += toplevel_offset; let offset = check_constrained(geometry, relative); if offset.x != 0 || offset.y != 0 { @@ -139,9 +144,11 @@ fn unconstrain_layer_popup(surface: &PopupSurface, output: &Output, layer_surfac let layer_geo = map.layer_geometry(layer_surface).unwrap(); // the output_rect represented relative to the parents coordinate system - let mut relative = Rectangle::from_loc_and_size((0, 0), output.geometry().size); + let mut relative = Rectangle::from_loc_and_size((0, 0), output.geometry().size).as_logical(); relative.loc -= layer_geo.loc; - let geometry = surface.with_pending_state(|state| state.positioner.get_geometry()); + let toplevel_offset = get_popup_toplevel_coords(surface); + let mut geometry = surface.with_pending_state(|state| state.positioner.get_geometry()); + geometry.loc += toplevel_offset; let offset = check_constrained(geometry, relative); if offset.x != 0 || offset.y != 0 { @@ -155,8 +162,11 @@ fn unconstrain_layer_popup(surface: &PopupSurface, output: &Output, layer_surfac } fn unconstrain_flip(popup: &PopupSurface, toplevel_box: Rectangle) -> bool { + let toplevel_offset = get_popup_toplevel_coords(popup); let positioner = popup.with_pending_state(|state| state.positioner.clone()); - let offset = check_constrained(positioner.get_geometry(), toplevel_box); + let mut geometry = positioner.get_geometry(); + geometry.loc += toplevel_offset; + let offset = check_constrained(geometry, toplevel_box); if offset.x == 0 && offset.y == 0 { return true; } @@ -176,7 +186,9 @@ fn unconstrain_flip(popup: &PopupSurface, toplevel_box: Rectangle) let old_positioner = positioner.clone(); positioner.anchor_edges = invert_anchor_x(positioner.anchor_edges); positioner.gravity = invert_gravity_x(positioner.gravity); - let new_offset = check_constrained(positioner.get_geometry(), toplevel_box); + geometry = positioner.get_geometry(); + geometry.loc += toplevel_offset; + let new_offset = check_constrained(geometry, toplevel_box); if !(new_offset.x.abs() < offset.x.abs()) { positioner = old_positioner; } @@ -185,13 +197,17 @@ fn unconstrain_flip(popup: &PopupSurface, toplevel_box: Rectangle) let old_positioner = positioner.clone(); positioner.anchor_edges = invert_anchor_y(positioner.anchor_edges); positioner.gravity = invert_gravity_y(positioner.gravity); - let new_offset = check_constrained(positioner.get_geometry(), toplevel_box); + geometry = positioner.get_geometry(); + geometry.loc += toplevel_offset; + let new_offset = check_constrained(geometry, toplevel_box); if !(new_offset.y.abs() < offset.y.abs()) { positioner = old_positioner; } } - let new_offset = check_constrained(positioner.get_geometry(), toplevel_box); + geometry = positioner.get_geometry(); + geometry.loc += toplevel_offset; + let new_offset = check_constrained(geometry, toplevel_box); if new_offset.x.abs() < offset.x.abs() || new_offset.y.abs() < offset.y.abs() { popup.with_pending_state(|state| { state.geometry = positioner.get_geometry(); @@ -203,8 +219,11 @@ fn unconstrain_flip(popup: &PopupSurface, toplevel_box: Rectangle) } fn unconstrain_slide(popup: &PopupSurface, toplevel_box: Rectangle) -> bool { + let toplevel_offset = get_popup_toplevel_coords(popup); let positioner = popup.with_pending_state(|state| state.positioner.clone()); - let offset = check_constrained(positioner.get_geometry(), toplevel_box); + let mut geometry = positioner.get_geometry(); + geometry.loc += toplevel_offset; + let offset = check_constrained(geometry, toplevel_box); if offset.x == 0 && offset.y == 0 { return true; } @@ -234,7 +253,9 @@ fn unconstrain_slide(popup: &PopupSurface, toplevel_box: Rectangle geometry.loc.y += toplevel_box.loc.y - toplevel.y; } - let new_offset = check_constrained(geometry, toplevel_box); + let mut check_geometry = geometry.clone(); + check_geometry.loc += toplevel; + let new_offset = check_constrained(check_geometry, toplevel_box); if new_offset.x.abs() < offset.x.abs() || new_offset.y.abs() < offset.y.abs() { popup.with_pending_state(|state| { state.geometry = geometry; @@ -245,8 +266,11 @@ fn unconstrain_slide(popup: &PopupSurface, toplevel_box: Rectangle } fn unconstrain_resize(popup: &PopupSurface, toplevel_box: Rectangle) -> bool { + let toplevel_offset = get_popup_toplevel_coords(popup); let positioner = popup.with_pending_state(|state| state.positioner.clone()); - let offset = check_constrained(positioner.get_geometry(), toplevel_box); + let mut geometry = positioner.get_geometry(); + geometry.loc += toplevel_offset; + let offset = check_constrained(geometry, toplevel_box); if offset.x == 0 && offset.y == 0 { return true; } @@ -268,6 +292,8 @@ fn unconstrain_resize(popup: &PopupSurface, toplevel_box: Rectangle