From 4be42634801928814a165c4642f3543fa0ce11d7 Mon Sep 17 00:00:00 2001 From: Hosted Weblate Date: Sat, 27 Jun 2026 23:01:22 +0200 Subject: [PATCH 01/92] i18n: translation updates from weblate Co-authored-by: CYAXXX <85353920+CYAXXX@users.noreply.github.com> Co-authored-by: Ettore Atalan Co-authored-by: Hosted Weblate Translate-URL: https://hosted.weblate.org/projects/pop-os/cosmic-comp/de/ Translate-URL: https://hosted.weblate.org/projects/pop-os/cosmic-comp/kmr/ Translation: Pop OS/COSMIC Comp --- resources/i18n/de/cosmic_comp.ftl | 2 +- resources/i18n/kmr/cosmic_comp.ftl | 28 ++++++++++++++++++++++++++++ 2 files changed, 29 insertions(+), 1 deletion(-) diff --git a/resources/i18n/de/cosmic_comp.ftl b/resources/i18n/de/cosmic_comp.ftl index f1644936..4ca5d5ea 100644 --- a/resources/i18n/de/cosmic_comp.ftl +++ b/resources/i18n/de/cosmic_comp.ftl @@ -1,7 +1,7 @@ a11y-zoom-move-continuously = Ansicht bewegt sich kontinuierlich mit dem Cursor a11y-zoom-move-onedge = Ansicht bewegt sich, sobald der Cursor den Rand erreicht a11y-zoom-move-centered = Ansicht bewegt sich, um den Cursor zentiert zu halten -a11y-zoom-settings = Vergrößerungsglas-Einstellungen… +a11y-zoom-settings = Vergrößerungsglas-Einstellungen ... grow-window = Vergrößern shrink-window = Verkleinern swap-windows = Fenster tauschen diff --git a/resources/i18n/kmr/cosmic_comp.ftl b/resources/i18n/kmr/cosmic_comp.ftl index e69de29b..8f207a8e 100644 --- a/resources/i18n/kmr/cosmic_comp.ftl +++ b/resources/i18n/kmr/cosmic_comp.ftl @@ -0,0 +1,28 @@ +grow-window = Mezin bike +shrink-window = Biçûk bike +window-menu-move = Bilivîne +window-menu-close = Bigire +window-menu-resize-edge-top = Jor +window-menu-resize-edge-left = Çep +window-menu-resize-edge-right = Rast +window-menu-resize-edge-bottom = Jêr +unknown-keybinding = +window-menu-minimize = Biçûk bike +window-menu-maximize = Mezin bike +window-menu-fullscreen = Dîmendera tije +window-menu-tiled = Çarçoveya herikbar +window-menu-screenshot = Wêneya dîmenderê bigire +window-menu-resize = Mezinahiyê ji nû ve saz bike +window-menu-move-prev-workspace = Bilivîne bo qada xebatê ya paş +window-menu-move-next-workspace = Bilivîne bo qada xebatê ya pêş +window-menu-stack = Komkirina çarçoveyê biafrîne +window-menu-unstack-all = Çarçoveyan kom neke +window-menu-unstack = Çarçoveyê kom neke +window-menu-sticky = Çarçoveya daliqandî +window-menu-close-all = Hemû çarçoveyan bigire +a11y-zoom-move-continuously = Dîtin bi nîşanker re bi awayekî domdar dilive +a11y-zoom-move-onedge = Dîtin dema nîşanker dikeve qeraxê dilive +a11y-zoom-move-centered = Dîtin dilive da ku nîşanker navîn bimîne +a11y-zoom-settings = Sazkariyên mezinkerê... +swap-windows = Çarçoveyan biguhêrîne +stack-windows = Çarçoveyan dalqîne From a5c0d7a17f637e31b9710607a8992f764eb04a3e Mon Sep 17 00:00:00 2001 From: Lionel DARNIS Date: Sun, 24 May 2026 11:40:51 +0200 Subject: [PATCH 02/92] chore: align compositor with local cosmic stack --- Cargo.toml | 51 ++++++++++++++++++++++++++++------- cosmic-comp-config/Cargo.toml | 4 +-- 2 files changed, 43 insertions(+), 12 deletions(-) diff --git a/Cargo.toml b/Cargo.toml index f1a04f02..a13c3fb9 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -17,18 +17,18 @@ cosmic-comp-config = { path = "cosmic-comp-config", features = [ "libdisplay-info", "output", ] } -cosmic-config = { git = "https://github.com/pop-os/libcosmic", features = [ +cosmic-config = { path = "../libcosmic/cosmic-config", features = [ "calloop", "macro", ] } -cosmic-protocols = { git = "https://github.com/pop-os/cosmic-protocols", rev = "160b086", default-features = false, features = [ +cosmic-protocols = { path = "../cosmic-protocols", default-features = false, features = [ "server", ] } -cosmic-settings-config = { git = "https://github.com/pop-os/cosmic-settings-daemon" } -cosmic-settings-daemon-config = { git = "https://github.com/pop-os/cosmic-settings-daemon", features = [ +cosmic-settings-config = { path = "../cosmic-settings-daemon/config" } +cosmic-settings-daemon-config = { path = "../cosmic-settings-daemon/cosmic-settings-daemon-config", features = [ "greeter", ] } -cosmic-text = { git = "https://github.com/pop-os/cosmic-text.git", features = [ +cosmic-text = { git = "https://forge.aditua.com/leyoda/cosmic-text.git", branch = "local/pr-503", features = [ "shape-run-cache", ] } libdisplay-info = "0.3.0" @@ -39,10 +39,14 @@ i18n-embed = { version = "0.16", features = [ "desktop-requester", ] } i18n-embed-fl = "0.10" -iced_tiny_skia = { git = "https://github.com/pop-os/libcosmic" } +iced_tiny_skia = { path = "../libcosmic/iced/tiny_skia" } indexmap = "2.13" keyframe = "1.1.1" -libcosmic = { git = "https://github.com/pop-os/libcosmic", default-features = false } +cosmic = { package = "libcosmic-yoda", path = "../libcosmic", default-features = false, features = [ + "tokio", + "wayland", + "multi-window", +] } libsystemd = { version = "0.7", optional = true } log-panics = { version = "2", features = ["with-backtrace"] } ordered-float = "5.1" @@ -138,9 +142,36 @@ inherits = "release" [profile.release] lto = "fat" -[patch."https://github.com/pop-os/cosmic-protocols"] -cosmic-protocols = { git = "https://github.com/pop-os//cosmic-protocols", branch = "main" } -cosmic-client-toolkit = { git = "https://github.com/pop-os//cosmic-protocols", branch = "main" } +[patch.'https://github.com/pop-os/libcosmic'] +cosmic-config = { path = "../libcosmic/cosmic-config" } +cosmic-theme = { path = "../libcosmic/cosmic-theme" } +iced = { path = "../libcosmic/iced" } +iced_accessibility = { path = "../libcosmic/iced/accessibility" } +iced_core = { path = "../libcosmic/iced/core" } +iced_futures = { path = "../libcosmic/iced/futures" } +iced_graphics = { path = "../libcosmic/iced/graphics" } +iced_renderer = { path = "../libcosmic/iced/renderer" } +iced_runtime = { path = "../libcosmic/iced/runtime" } +iced_tiny_skia = { path = "../libcosmic/iced/tiny_skia" } +iced_wgpu = { path = "../libcosmic/iced/wgpu" } +iced_widget = { path = "../libcosmic/iced/widget" } +iced_winit = { path = "../libcosmic/iced/winit" } + +[patch.'https://github.com/pop-os/cosmic-protocols'] +cosmic-protocols = { path = "../cosmic-protocols" } +cosmic-client-toolkit = { path = "../cosmic-protocols/client-toolkit" } + +[patch.'https://github.com/pop-os/cosmic-settings-daemon'] +cosmic-settings-config = { path = "../cosmic-settings-daemon/config" } +cosmic-settings-daemon-config = { path = "../cosmic-settings-daemon/cosmic-settings-daemon-config" } + +[patch.'https://github.com/pop-os/cosmic-text.git'] +cosmic-text = { git = "https://forge.aditua.com/leyoda/cosmic-text.git", branch = "local/pr-503" } + +[patch.'https://forge.aditua.com/leyoda/window_clipboard.git'] +window_clipboard = { path = "../window_clipboard" } +dnd = { path = "../window_clipboard/dnd" } +mime = { path = "../window_clipboard/mime" } [patch.crates-io] smithay = { git = "https://github.com/smithay/smithay.git", rev = "8eb4076" } diff --git a/cosmic-comp-config/Cargo.toml b/cosmic-comp-config/Cargo.toml index f01a4bb3..d0225691 100644 --- a/cosmic-comp-config/Cargo.toml +++ b/cosmic-comp-config/Cargo.toml @@ -4,8 +4,8 @@ version = "1.0.0" edition = "2024" [dependencies] -cosmic-config = { git = "https://github.com/pop-os/libcosmic/" } -cosmic-randr-shell = { git = "https://github.com/pop-os/cosmic-randr/", optional = true } +cosmic-config = { path = "../../libcosmic/cosmic-config" } +cosmic-randr-shell = { path = "../../cosmic-randr/shell", optional = true } input = "0.10.0" libdisplay-info = { version = "0.3.0", optional = true } serde = { version = "1", features = ["derive"] } From a005fddefeb20c7458a486172840b65d60cd7285 Mon Sep 17 00:00:00 2001 From: Lionel DARNIS Date: Sun, 24 May 2026 16:08:29 +0200 Subject: [PATCH 03/92] fix: keep tokio runtime available for compositor --- Cargo.lock | 1 + Cargo.toml | 1 + src/main.rs | 9 +++++++++ 3 files changed, 11 insertions(+) diff --git a/Cargo.lock b/Cargo.lock index 1bc58958..262260b7 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -845,6 +845,7 @@ dependencies = [ "smithay-egui", "thiserror 2.0.18", "tiny-skia", + "tokio", "tracing", "tracing-journald", "tracing-subscriber", diff --git a/Cargo.toml b/Cargo.toml index a13c3fb9..943c6872 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -77,6 +77,7 @@ xdg = "^3.0" xdg-user = "0.2.1" xkbcommon = "0.9" zbus = "5.14.0" +tokio = { version = "1", features = ["rt-multi-thread"] } profiling = { version = "1.0" } rustix = { version = "1.1.4", features = ["process"] } rand = "0.10" diff --git a/src/main.rs b/src/main.rs index 7ae05991..ffa37236 100644 --- a/src/main.rs +++ b/src/main.rs @@ -1,6 +1,15 @@ // SPDX-License-Identifier: GPL-3.0-only fn main() { + // libcosmic-yoda enables zbus' `tokio` feature, so zbus 5.14 expects an + // ambient Tokio runtime via Handle::current(). cosmic-comp's loop is not + // async, so hold a runtime guard for the lifetime of run(). + let runtime = tokio::runtime::Builder::new_multi_thread() + .enable_all() + .build() + .expect("failed to build tokio runtime"); + let _guard = runtime.enter(); + if let Err(err) = cosmic_comp::run(Default::default()) { tracing::error!("Error occured in main(): {}", err); std::process::exit(1); From 6e72e4c1710b2887bdd2c11047e45a830808ce0c Mon Sep 17 00:00:00 2001 From: Lionel DARNIS Date: Mon, 25 May 2026 09:55:23 +0200 Subject: [PATCH 04/92] yoda: refresh local dbus binding lock --- Cargo.lock | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index 262260b7..7669d8af 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -955,8 +955,7 @@ dependencies = [ [[package]] name = "cosmic-settings-daemon" -version = "0.1.0" -source = "git+https://github.com/pop-os/dbus-settings-bindings#507e342c21d3ce6ae41b1d4f3fa2f0ad5ee23e75" +version = "0.1.1-yoda.1" dependencies = [ "zbus", ] From 7d24cd2e6ab929d9ad39df200e481608b2ea0dab Mon Sep 17 00:00:00 2001 From: Lionel DARNIS Date: Mon, 25 May 2026 11:34:38 +0200 Subject: [PATCH 05/92] chore: use local cosmic-text checkout --- Cargo.toml | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/Cargo.toml b/Cargo.toml index 943c6872..277655e7 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -28,7 +28,7 @@ cosmic-settings-config = { path = "../cosmic-settings-daemon/config" } cosmic-settings-daemon-config = { path = "../cosmic-settings-daemon/cosmic-settings-daemon-config", features = [ "greeter", ] } -cosmic-text = { git = "https://forge.aditua.com/leyoda/cosmic-text.git", branch = "local/pr-503", features = [ +cosmic-text = { path = "../cosmic-text", features = [ "shape-run-cache", ] } libdisplay-info = "0.3.0" @@ -167,7 +167,7 @@ cosmic-settings-config = { path = "../cosmic-settings-daemon/config" } cosmic-settings-daemon-config = { path = "../cosmic-settings-daemon/cosmic-settings-daemon-config" } [patch.'https://github.com/pop-os/cosmic-text.git'] -cosmic-text = { git = "https://forge.aditua.com/leyoda/cosmic-text.git", branch = "local/pr-503" } +cosmic-text = { path = "../cosmic-text" } [patch.'https://forge.aditua.com/leyoda/window_clipboard.git'] window_clipboard = { path = "../window_clipboard" } From 58b0598de8d95298eaea440435649bdd281494f9 Mon Sep 17 00:00:00 2001 From: Lionel DARNIS Date: Mon, 25 May 2026 18:28:56 +0200 Subject: [PATCH 06/92] chore: use local glyphon lockfile --- Cargo.lock | 1 - 1 file changed, 1 deletion(-) diff --git a/Cargo.lock b/Cargo.lock index 7669d8af..890b86ff 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -1052,7 +1052,6 @@ checksum = "460fbee9c2c2f33933d720630a6a0bac33ba7053db5344fac858d4b8952d77d5" [[package]] name = "cryoglyph" version = "0.1.0" -source = "git+https://github.com/pop-os/glyphon.git?tag=cosmic-0.14#c49de15bce4d8254ac136d1be9911960cc85ce12" dependencies = [ "cosmic-text", "etagere", From 9141780b3036268e38d4d58345cbe892e951f6d8 Mon Sep 17 00:00:00 2001 From: Lionel DARNIS Date: Fri, 3 Jul 2026 12:36:51 +0200 Subject: [PATCH 07/92] fix: absorb libcosmic theme-v2 API (private containers, text::Style, wgpu 28) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Leyoda 2026 – GPLv3 --- src/shell/element/stack.rs | 4 ++-- src/shell/element/stack/tab.rs | 4 ++-- src/shell/element/window.rs | 2 +- src/shell/grabs/menu/mod.rs | 8 +++++--- src/shell/zoom.rs | 2 +- 5 files changed, 11 insertions(+), 9 deletions(-) diff --git a/src/shell/element/stack.rs b/src/shell/element/stack.rs index 9f8584f5..058719e3 100644 --- a/src/shell/element/stack.rs +++ b/src/shell/element/stack.rs @@ -1364,8 +1364,8 @@ impl Decorations for DefaultDecorations { iced_widget::container::Style { snap: true, - icon_color: Some(cosmic_theme.background.on.into()), - text_color: Some(cosmic_theme.background.on.into()), + icon_color: Some(cosmic_theme.background(false).on.into()), + text_color: Some(cosmic_theme.background(false).on.into()), background: Some(Background::Color(background.into())), border: Border { radius, diff --git a/src/shell/element/stack/tab.rs b/src/shell/element/stack/tab.rs index 99b0c247..ea48a585 100644 --- a/src/shell/element/stack/tab.rs +++ b/src/shell/element/stack/tab.rs @@ -65,7 +65,7 @@ impl From for theme::Container<'_> { icon_color: Some(Color::from(theme.cosmic().accent_text_color())), text_color: Some(Color::from(theme.cosmic().accent_text_color())), background: Some(Background::Color( - theme.cosmic().primary.component.selected.into(), + theme.cosmic().primary(false).component.selected.into(), )), border: Border { radius: 0.0.into(), @@ -81,7 +81,7 @@ impl From for theme::Container<'_> { icon_color: None, text_color: None, background: Some(Background::Color( - theme.cosmic().primary.component.base.into(), + theme.cosmic().primary(false).component.base.into(), )), border: Border { radius: 0.0.into(), diff --git a/src/shell/element/window.rs b/src/shell/element/window.rs index 0adbfab7..ea20bb6a 100644 --- a/src/shell/element/window.rs +++ b/src/shell/element/window.rs @@ -826,7 +826,7 @@ impl Program for CosmicWindowInternal { fn background_color(&self, theme: &cosmic::Theme) -> Color { if self.window.is_maximized(false) { - theme.cosmic().background.base.into() + theme.cosmic().background(false).base.into() } else { Color::TRANSPARENT } diff --git a/src/shell/grabs/menu/mod.rs b/src/shell/grabs/menu/mod.rs index 14a9f917..671e004a 100644 --- a/src/shell/grabs/menu/mod.rs +++ b/src/shell/grabs/menu/mod.rs @@ -428,10 +428,11 @@ impl Program for ContextMenu { .width(mode) .class(if *disabled { theme::Text::Custom(|theme| { - let mut color = theme.cosmic().background.component.on; + let mut color = theme.cosmic().background(false).component.on; color.alpha *= 0.5; TextStyle { color: Some(color.into()), + ..Default::default() } }) } else { @@ -446,10 +447,11 @@ impl Program for ContextMenu { .align_x(Horizontal::Right) .width(Length::Shrink) .class(theme::Text::Custom(|theme| { - let mut color = theme.cosmic().background.component.on; + let mut color = theme.cosmic().background(false).component.on; color.alpha *= 0.75; TextStyle { color: Some(color.into()), + ..Default::default() } })) .into(), @@ -474,7 +476,7 @@ impl Program for ContextMenu { .padding(1) .class(theme::Container::custom(|theme| { let cosmic = theme.cosmic(); - let component = &cosmic.background.component; + let component = &cosmic.background(false).component; iced_widget::container::Style { snap: true, icon_color: Some(cosmic.accent.base.into()), diff --git a/src/shell/zoom.rs b/src/shell/zoom.rs index ffeed63b..f72ad8e8 100644 --- a/src/shell/zoom.rs +++ b/src/shell/zoom.rs @@ -505,7 +505,7 @@ impl Program for ZoomProgram { .padding(8) .class(theme::Container::custom(|theme| { let cosmic = theme.cosmic(); - let component = &cosmic.background.component; + let component = &cosmic.background(false).component; iced_widget::container::Style { snap: true, icon_color: Some(component.on.into()), From e65b97abb8c37efc6d83ada3824c006fdfdcd96e Mon Sep 17 00:00:00 2001 From: Lionel DARNIS Date: Fri, 3 Jul 2026 15:56:20 +0200 Subject: [PATCH 08/92] chore: restore rust-toolchain 1.93 + refresh lockfile post-rebase MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Leyoda 2026 – GPLv3 --- Cargo.lock | 1413 ++++++++++++++++++++++++++++++++++++------- rust-toolchain.toml | 2 +- 2 files changed, 1193 insertions(+), 222 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index 890b86ff..5edc17e0 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -18,6 +18,89 @@ version = "0.1.10" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "366ffbaa4442f4684d91e2cd7c5ea7c4ed8add41959a31447066e279e432b618" +[[package]] +name = "accesskit" +version = "0.22.0" +source = "git+https://github.com/wash2/accesskit?tag=cosmic-0.14#f0599eed5f18111228266fe3f28991cc48b5964f" +dependencies = [ + "uuid", +] + +[[package]] +name = "accesskit_atspi_common" +version = "0.15.0" +source = "git+https://github.com/wash2/accesskit?tag=cosmic-0.14#f0599eed5f18111228266fe3f28991cc48b5964f" +dependencies = [ + "accesskit", + "accesskit_consumer", + "atspi-common", + "serde", + "zvariant", +] + +[[package]] +name = "accesskit_consumer" +version = "0.32.0" +source = "git+https://github.com/wash2/accesskit?tag=cosmic-0.14#f0599eed5f18111228266fe3f28991cc48b5964f" +dependencies = [ + "accesskit", + "hashbrown 0.16.1", +] + +[[package]] +name = "accesskit_macos" +version = "0.23.0" +source = "git+https://github.com/wash2/accesskit?tag=cosmic-0.14#f0599eed5f18111228266fe3f28991cc48b5964f" +dependencies = [ + "accesskit", + "accesskit_consumer", + "hashbrown 0.16.1", + "objc2 0.5.2", + "objc2-app-kit 0.2.2", + "objc2-foundation 0.2.2", +] + +[[package]] +name = "accesskit_unix" +version = "0.18.0" +source = "git+https://github.com/wash2/accesskit?tag=cosmic-0.14#f0599eed5f18111228266fe3f28991cc48b5964f" +dependencies = [ + "accesskit", + "accesskit_atspi_common", + "atspi", + "futures-lite", + "serde", + "tokio", + "tokio-stream", + "zbus", +] + +[[package]] +name = "accesskit_windows" +version = "0.30.0" +source = "git+https://github.com/wash2/accesskit?tag=cosmic-0.14#f0599eed5f18111228266fe3f28991cc48b5964f" +dependencies = [ + "accesskit", + "accesskit_consumer", + "hashbrown 0.16.1", + "static_assertions", + "windows 0.61.3", + "windows-core 0.61.2", +] + +[[package]] +name = "accesskit_winit" +version = "0.30.0" +source = "git+https://github.com/wash2/accesskit?tag=cosmic-0.14#f0599eed5f18111228266fe3f28991cc48b5964f" +dependencies = [ + "accesskit", + "accesskit_macos", + "accesskit_unix", + "accesskit_windows", + "raw-window-handle", + "winit 0.31.0-beta.2", +] + [[package]] name = "addr2line" version = "0.25.1" @@ -61,6 +144,12 @@ version = "0.1.3" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "250f629c0161ad8107cf89319e990051fae62832fd343083bea452d93e2205fd" +[[package]] +name = "allocator-api2" +version = "0.2.21" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "683d7910e743518b0e34f1186f92494becacb047c7b6bf616c96772180fef923" + [[package]] name = "almost" version = "0.2.0" @@ -175,6 +264,44 @@ dependencies = [ "libloading", ] +[[package]] +name = "ashpd" +version = "0.11.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d2f3f79755c74fd155000314eb349864caa787c6592eace6c6882dad873d9c39" +dependencies = [ + "enumflags2", + "futures-channel", + "futures-util", + "rand 0.9.2", + "raw-window-handle", + "serde", + "serde_repr", + "tokio", + "url", + "zbus", +] + +[[package]] +name = "ashpd" +version = "0.12.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "33a3c86f3fd70c0ffa500ed189abfa90b5a52398a45d5dc372fcc38ebeb7a645" +dependencies = [ + "enumflags2", + "futures-channel", + "futures-util", + "rand 0.9.2", + "serde", + "serde_repr", + "tokio", + "url", + "wayland-backend", + "wayland-client", + "wayland-protocols", + "zbus", +] + [[package]] name = "async-broadcast" version = "0.7.2" @@ -329,10 +456,47 @@ dependencies = [ ] [[package]] -name = "auto_enums" -version = "0.8.7" +name = "atspi" +version = "0.29.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9c170965892137a3a9aeb000b4524aa3cc022a310e709d848b6e1cdce4ab4781" +checksum = "c77886257be21c9cd89a4ae7e64860c6f0eefca799bb79127913052bd0eefb3d" +dependencies = [ + "atspi-common", + "atspi-proxies", +] + +[[package]] +name = "atspi-common" +version = "0.13.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "20c5617155740c98003016429ad13fe43ce7a77b007479350a9f8bf95a29f63d" +dependencies = [ + "enumflags2", + "serde", + "static_assertions", + "zbus", + "zbus-lockstep", + "zbus-lockstep-macros", + "zbus_names", + "zvariant", +] + +[[package]] +name = "atspi-proxies" +version = "0.13.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "2230e48787ed3eb4088996eab66a32ca20c0b67bbd4fd6cdfe79f04f1f04c9fc" +dependencies = [ + "atspi-common", + "serde", + "zbus", +] + +[[package]] +name = "auto_enums" +version = "0.8.9" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "2e4487600931c9a89f8db7ffbdf3fbdd45bb7bd85e26861f659a463cd0dff966" dependencies = [ "derive_utils", "proc-macro2", @@ -358,7 +522,7 @@ dependencies = [ "miniz_oxide", "object", "rustc-demangle", - "windows-link", + "windows-link 0.2.1", ] [[package]] @@ -427,13 +591,22 @@ dependencies = [ "generic-array", ] +[[package]] +name = "block2" +version = "0.5.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "2c132eebf10f5cad5289222520a4a058514204aed6d791f1cf4fe8088b82d15f" +dependencies = [ + "objc2 0.5.2", +] + [[package]] name = "block2" version = "0.6.2" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "cdeb9d870516001442e364c5220d3574d2da8dc765554b4a617230d33fa58ef5" dependencies = [ - "objc2", + "objc2 0.6.4", ] [[package]] @@ -619,7 +792,7 @@ dependencies = [ "iana-time-zone", "num-traits", "serde", - "windows-link", + "windows-link 0.2.1", ] [[package]] @@ -640,7 +813,6 @@ dependencies = [ [[package]] name = "clipboard_macos" version = "0.1.0" -source = "git+https://github.com/pop-os/window_clipboard.git?tag=sctk-0.20#f68595ee0e62fbd6589f4709b5aaa5c3c7ea5f6c" dependencies = [ "objc", "objc-foundation", @@ -650,22 +822,12 @@ dependencies = [ [[package]] name = "clipboard_wayland" version = "0.2.2" -source = "git+https://github.com/pop-os/window_clipboard.git?tag=sctk-0.20#f68595ee0e62fbd6589f4709b5aaa5c3c7ea5f6c" dependencies = [ "dnd", "mime 0.1.0", "smithay-clipboard", ] -[[package]] -name = "clipboard_x11" -version = "0.4.2" -source = "git+https://github.com/pop-os/window_clipboard.git?tag=sctk-0.20#f68595ee0e62fbd6589f4709b5aaa5c3c7ea5f6c" -dependencies = [ - "thiserror 1.0.69", - "x11rb", -] - [[package]] name = "cocoa" version = "0.25.0" @@ -675,7 +837,7 @@ dependencies = [ "bitflags 1.3.2", "block", "cocoa-foundation", - "core-foundation", + "core-foundation 0.9.4", "core-graphics", "foreign-types", "libc", @@ -690,8 +852,8 @@ checksum = "8c6234cbb2e4c785b456c0644748b1ac416dd045799740356f8363dfe00c93f7" dependencies = [ "bitflags 1.3.2", "block", - "core-foundation", - "core-graphics-types", + "core-foundation 0.9.4", + "core-graphics-types 0.1.3", "libc", "objc", ] @@ -702,7 +864,9 @@ version = "0.12.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "fe6d2e5af09e8c8ad56c969f2157a3d4238cebc7c55f0a517728c38f7b200f81" dependencies = [ - "unicode-width", + "serde", + "termcolor", + "unicode-width 0.1.14", ] [[package]] @@ -746,6 +910,16 @@ dependencies = [ "libc", ] +[[package]] +name = "core-foundation" +version = "0.10.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b2a6cd9ae233e7f62ba4e9353e81a88df7fc8a5987b8d445b4d90c879bd156f6" +dependencies = [ + "core-foundation-sys", + "libc", +] + [[package]] name = "core-foundation-sys" version = "0.8.7" @@ -759,8 +933,8 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "c07782be35f9e1140080c6b96f0d44b739e2278479f64e02fdab4e32dfd8b081" dependencies = [ "bitflags 1.3.2", - "core-foundation", - "core-graphics-types", + "core-foundation 0.9.4", + "core-graphics-types 0.1.3", "foreign-types", "libc", ] @@ -772,7 +946,18 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "45390e6114f68f718cc7a830514a96f903cccd70d02a8f6d9f643ac4ba45afaf" dependencies = [ "bitflags 1.3.2", - "core-foundation", + "core-foundation 0.9.4", + "libc", +] + +[[package]] +name = "core-graphics-types" +version = "0.2.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3d44a101f213f6c4cdc1853d4b78aef6db6bdfa3468798cc1d9912f4735013eb" +dependencies = [ + "bitflags 2.11.0", + "core-foundation 0.10.1", "libc", ] @@ -788,7 +973,6 @@ dependencies = [ [[package]] name = "cosmic-client-toolkit" version = "0.2.0" -source = "git+https://github.com/pop-os//cosmic-protocols?branch=main#160b086abe03cd34a8a375d7fbe47b24308d1f38" dependencies = [ "bitflags 2.11.0", "cosmic-protocols", @@ -824,7 +1008,7 @@ dependencies = [ "indexmap 2.13.0", "jiff", "keyframe", - "libcosmic", + "libcosmic-yoda", "libdisplay-info", "libsystemd", "log-panics", @@ -875,7 +1059,6 @@ dependencies = [ [[package]] name = "cosmic-config" version = "1.0.0" -source = "git+https://github.com/pop-os/libcosmic#0bb006c5bbf7eb89491891d45bfc8f21f8eb1305" dependencies = [ "atomicwrites", "calloop", @@ -888,6 +1071,7 @@ dependencies = [ "notify", "ron 0.12.0", "serde", + "tokio", "tracing", "xdg", "zbus", @@ -896,7 +1080,6 @@ dependencies = [ [[package]] name = "cosmic-config-derive" version = "1.0.0" -source = "git+https://github.com/pop-os/libcosmic#0bb006c5bbf7eb89491891d45bfc8f21f8eb1305" dependencies = [ "quote", "syn", @@ -905,12 +1088,11 @@ dependencies = [ [[package]] name = "cosmic-freedesktop-icons" version = "0.4.0" -source = "git+https://github.com/pop-os/freedesktop-icons#7a61a704f6d1ec41f71cbe766e3cc484858523fa" dependencies = [ "bstr", "btoi", "memchr", - "memmap2", + "memmap2 0.9.10", "thiserror 2.0.18", "tracing", "xdg", @@ -919,7 +1101,6 @@ dependencies = [ [[package]] name = "cosmic-protocols" version = "0.2.0" -source = "git+https://github.com/pop-os//cosmic-protocols?branch=main#160b086abe03cd34a8a375d7fbe47b24308d1f38" dependencies = [ "bitflags 2.11.0", "wayland-backend", @@ -933,7 +1114,6 @@ dependencies = [ [[package]] name = "cosmic-randr-shell" version = "0.1.0" -source = "git+https://github.com/pop-os/cosmic-randr/#6e8e795970fa06d434af22775e415b517f7552d3" dependencies = [ "kdl", "slotmap", @@ -943,7 +1123,6 @@ dependencies = [ [[package]] name = "cosmic-settings-config" version = "0.1.0" -source = "git+https://github.com/pop-os/cosmic-settings-daemon#e37160f14d1e7ee428f973cd2848b4e95f83dfe1" dependencies = [ "cosmic-config", "ron 0.11.0", @@ -963,7 +1142,6 @@ dependencies = [ [[package]] name = "cosmic-settings-daemon-config" version = "0.1.0" -source = "git+https://github.com/pop-os/cosmic-settings-daemon#e37160f14d1e7ee428f973cd2848b4e95f83dfe1" dependencies = [ "cosmic-config", "cosmic-theme", @@ -972,8 +1150,7 @@ dependencies = [ [[package]] name = "cosmic-text" -version = "0.18.2" -source = "git+https://github.com/pop-os/cosmic-text.git#d5a972a2b63649fad11ea3a7e80f7dc4c592f01a" +version = "0.19.0" dependencies = [ "bitflags 2.11.0", "fontdb", @@ -991,18 +1168,19 @@ dependencies = [ "unicode-linebreak", "unicode-script", "unicode-segmentation", + "unicode-width 0.2.2", ] [[package]] name = "cosmic-theme" version = "1.0.0" -source = "git+https://github.com/pop-os/libcosmic#0bb006c5bbf7eb89491891d45bfc8f21f8eb1305" dependencies = [ "almost", "configparser", "cosmic-config", "csscolorparser", "dirs", + "hex_color", "palette", "ron 0.12.0", "serde", @@ -1078,9 +1256,9 @@ checksum = "42aaeae719fd78ce501d77c6cdf01f7e96f26bcd5617a4903a1c2b97e388543a" [[package]] name = "csscolorparser" -version = "0.8.2" +version = "0.8.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "64a878cfd6075649977f548ed439564cfdbe1f190a6656a00b48af7740f2f148" +checksum = "199f851bd3cb5004c09474252c7f74e7c047441ed0979bf3688a7106a13da952" dependencies = [ "num-traits", "phf 0.13.1", @@ -1213,7 +1391,9 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "1e0e367e4e7da84520dedcac1901e4da967309406d1e51017ae1abfb97adbd38" dependencies = [ "bitflags 2.11.0", - "objc2", + "block2 0.6.2", + "libc", + "objc2 0.6.4", ] [[package]] @@ -1239,7 +1419,6 @@ dependencies = [ [[package]] name = "dnd" version = "0.1.0" -source = "git+https://github.com/pop-os/window_clipboard.git?tag=sctk-0.20#f68595ee0e62fbd6589f4709b5aaa5c3c7ea5f6c" dependencies = [ "bitflags 2.11.0", "mime 0.1.0", @@ -1266,21 +1445,12 @@ checksum = "75b325c5dbd37f80359721ad39aca5a29fb04c89279657cffdda8736d0c0b9d2" [[package]] name = "dpi" version = "0.1.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d8b14ccef22fc6f5a8f4d7d768562a182c04ce9a3b3157b91390b52ddfdf1a76" [[package]] -name = "drm" -version = "0.11.1" +name = "dpi" +version = "0.1.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a0f8a69e60d75ae7dab4ef26a59ca99f2a89d4c142089b537775ae0c198bdcde" -dependencies = [ - "bitflags 2.11.0", - "bytemuck", - "drm-ffi 0.7.1", - "drm-fourcc", - "rustix 0.38.44", -] +checksum = "d8b14ccef22fc6f5a8f4d7d768562a182c04ce9a3b3157b91390b52ddfdf1a76" [[package]] name = "drm" @@ -1290,29 +1460,19 @@ checksum = "80bc8c5c6c2941f70a55c15f8d9f00f9710ebda3ffda98075f996a0e6c92756f" dependencies = [ "bitflags 2.11.0", "bytemuck", - "drm-ffi 0.9.0", + "drm-ffi", "drm-fourcc", "libc", "rustix 0.38.44", ] -[[package]] -name = "drm-ffi" -version = "0.7.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "41334f8405792483e32ad05fbb9c5680ff4e84491883d2947a4757dc54cb2ac6" -dependencies = [ - "drm-sys 0.6.1", - "rustix 0.38.44", -] - [[package]] name = "drm-ffi" version = "0.9.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "d8e41459d99a9b529845f6d2c909eb9adf3b6d2f82635ae40be8de0601726e8b" dependencies = [ - "drm-sys 0.8.0", + "drm-sys", "rustix 0.38.44", ] @@ -1322,16 +1482,6 @@ version = "2.2.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "0aafbcdb8afc29c1a7ee5fbe53b5d62f4565b35a042a662ca9fecd0b54dae6f4" -[[package]] -name = "drm-sys" -version = "0.6.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "2d09ff881f92f118b11105ba5e34ff8f4adf27b30dae8f12e28c193af1c83176" -dependencies = [ - "libc", - "linux-raw-sys 0.6.5", -] - [[package]] name = "drm-sys" version = "0.8.0" @@ -1731,7 +1881,7 @@ checksum = "457e789b3d1202543297a350643cf459f836cade38934e7a4cf6a39e7cde2905" dependencies = [ "fontconfig-parser", "log", - "memmap2", + "memmap2 0.9.10", "slotmap", "tinyvec", "ttf-parser", @@ -1890,7 +2040,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "ce852e998d3ca5e4a97014fb31c940dc5ef344ec7d364984525fd11e8a547e6a" dependencies = [ "bitflags 2.11.0", - "drm 0.14.1", + "drm", "drm-fourcc", "gbm-sys", "libc", @@ -1916,8 +2066,8 @@ dependencies = [ "libc", "log", "rustversion", - "windows-link", - "windows-result", + "windows-link 0.2.1", + "windows-result 0.4.1", ] [[package]] @@ -1937,7 +2087,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "1bd49230192a3797a9a4d6abe9b3eed6f7fa4c8a8a4947977c6f80025f92cbd8" dependencies = [ "rustix 1.1.4", - "windows-link", + "windows-link 0.2.1", ] [[package]] @@ -2022,6 +2172,49 @@ dependencies = [ "web-sys", ] +[[package]] +name = "glutin_wgl_sys" +version = "0.6.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "2c4ee00b289aba7a9e5306d57c2d05499b2e5dc427f84ac708bd2c090212cf3e" +dependencies = [ + "gl_generator", +] + +[[package]] +name = "gpu-allocator" +version = "0.28.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "51255ea7cfaadb6c5f1528d43e92a82acb2b96c43365989a28b2d44ee38f8795" +dependencies = [ + "ash", + "hashbrown 0.16.1", + "log", + "presser", + "thiserror 2.0.18", + "windows 0.62.2", +] + +[[package]] +name = "gpu-descriptor" +version = "0.3.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b89c83349105e3732062a895becfc71a8f921bb71ecbbdd8ff99263e3b53a0ca" +dependencies = [ + "bitflags 2.11.0", + "gpu-descriptor-types", + "hashbrown 0.15.5", +] + +[[package]] +name = "gpu-descriptor-types" +version = "0.2.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "fdf242682df893b86f33a73828fb09ca4b2d3bb6cc95249707fc684d27484b91" +dependencies = [ + "bitflags 2.11.0", +] + [[package]] name = "grid" version = "1.0.0" @@ -2084,6 +2277,8 @@ version = "0.16.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "841d1cc9bed7f9236f321df977030373f4a4163ae1a7dbfe1a51a2c1a51d9100" dependencies = [ + "allocator-api2", + "equivalent", "foldhash 0.2.0", ] @@ -2117,6 +2312,17 @@ version = "0.4.3" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "7f24254aa9a54b5c858eaee2f5bccdb46aaf0e486a595ed5fd8f86ba55232a70" +[[package]] +name = "hex_color" +version = "3.0.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d37f101bf4c633f7ca2e4b5e136050314503dd198e78e325ea602c327c484ef0" +dependencies = [ + "arrayvec", + "rand 0.8.5", + "serde", +] + [[package]] name = "hexf-parse" version = "0.2.1" @@ -2220,7 +2426,7 @@ dependencies = [ "js-sys", "log", "wasm-bindgen", - "windows-core", + "windows-core 0.62.2", ] [[package]] @@ -2235,9 +2441,9 @@ dependencies = [ [[package]] name = "iced" version = "0.14.0" -source = "git+https://github.com/pop-os/libcosmic#0bb006c5bbf7eb89491891d45bfc8f21f8eb1305" dependencies = [ "dnd", + "iced_accessibility", "iced_core", "iced_debug", "iced_futures", @@ -2245,19 +2451,28 @@ dependencies = [ "iced_renderer", "iced_runtime", "iced_widget", + "iced_winit", "image", "mime 0.1.0", "thiserror 2.0.18", "window_clipboard", ] +[[package]] +name = "iced_accessibility" +version = "0.1.0" +dependencies = [ + "accesskit", + "accesskit_winit", +] + [[package]] name = "iced_core" version = "0.14.0" -source = "git+https://github.com/pop-os/libcosmic#0bb006c5bbf7eb89491891d45bfc8f21f8eb1305" dependencies = [ "bitflags 2.11.0", "bytes", + "cosmic-client-toolkit", "dnd", "glam", "lilt", @@ -2270,6 +2485,7 @@ dependencies = [ "serde", "smol_str", "thiserror 2.0.18", + "unicode-segmentation", "web-time", "window_clipboard", ] @@ -2277,7 +2493,6 @@ dependencies = [ [[package]] name = "iced_debug" version = "0.14.0" -source = "git+https://github.com/pop-os/libcosmic#0bb006c5bbf7eb89491891d45bfc8f21f8eb1305" dependencies = [ "iced_core", "iced_futures", @@ -2287,12 +2502,12 @@ dependencies = [ [[package]] name = "iced_futures" version = "0.14.0" -source = "git+https://github.com/pop-os/libcosmic#0bb006c5bbf7eb89491891d45bfc8f21f8eb1305" dependencies = [ "futures", "iced_core", "log", "rustc-hash 2.1.1", + "tokio", "wasm-bindgen-futures", "wasmtimer", ] @@ -2300,7 +2515,6 @@ dependencies = [ [[package]] name = "iced_graphics" version = "0.14.0" -source = "git+https://github.com/pop-os/libcosmic#0bb006c5bbf7eb89491891d45bfc8f21f8eb1305" dependencies = [ "bitflags 2.11.0", "bytemuck", @@ -2321,7 +2535,6 @@ dependencies = [ [[package]] name = "iced_program" version = "0.14.0" -source = "git+https://github.com/pop-os/libcosmic#0bb006c5bbf7eb89491891d45bfc8f21f8eb1305" dependencies = [ "iced_graphics", "iced_runtime", @@ -2330,7 +2543,6 @@ dependencies = [ [[package]] name = "iced_renderer" version = "0.14.0" -source = "git+https://github.com/pop-os/libcosmic#0bb006c5bbf7eb89491891d45bfc8f21f8eb1305" dependencies = [ "iced_graphics", "iced_tiny_skia", @@ -2342,9 +2554,9 @@ dependencies = [ [[package]] name = "iced_runtime" version = "0.14.0" -source = "git+https://github.com/pop-os/libcosmic#0bb006c5bbf7eb89491891d45bfc8f21f8eb1305" dependencies = [ "bytes", + "cosmic-client-toolkit", "dnd", "iced_core", "iced_futures", @@ -2356,7 +2568,6 @@ dependencies = [ [[package]] name = "iced_tiny_skia" version = "0.14.0" -source = "git+https://github.com/pop-os/libcosmic#0bb006c5bbf7eb89491891d45bfc8f21f8eb1305" dependencies = [ "bytemuck", "cosmic-text", @@ -2373,9 +2584,7 @@ dependencies = [ [[package]] name = "iced_wgpu" version = "0.14.0" -source = "git+https://github.com/pop-os/libcosmic#0bb006c5bbf7eb89491891d45bfc8f21f8eb1305" dependencies = [ - "as-raw-xcb-connection", "bitflags 2.11.0", "bytemuck", "cosmic-client-toolkit", @@ -2392,20 +2601,18 @@ dependencies = [ "rustc-hash 2.1.1", "rustix 0.38.44", "thiserror 2.0.18", - "tiny-xlib", "wayland-backend", "wayland-client", "wayland-protocols", "wayland-sys", "wgpu", - "x11rb", ] [[package]] name = "iced_widget" version = "0.14.2" -source = "git+https://github.com/pop-os/libcosmic#0bb006c5bbf7eb89491891d45bfc8f21f8eb1305" dependencies = [ + "cosmic-client-toolkit", "dnd", "iced_renderer", "iced_runtime", @@ -2418,6 +2625,37 @@ dependencies = [ "window_clipboard", ] +[[package]] +name = "iced_winit" +version = "0.14.0" +dependencies = [ + "cosmic-client-toolkit", + "cursor-icon", + "dnd", + "iced_debug", + "iced_futures", + "iced_graphics", + "iced_program", + "iced_runtime", + "log", + "raw-window-handle", + "rustc-hash 2.1.1", + "rustix 0.38.44", + "thiserror 2.0.18", + "wasm-bindgen-futures", + "wayland-backend", + "wayland-client", + "wayland-protocols", + "web-sys", + "winapi", + "window_clipboard", + "winit 0.31.0-beta.2", + "winit-core 0.31.0-beta.2", + "xkbcommon 0.7.0", + "xkbcommon-dl", + "xkeysym", +] + [[package]] name = "icu_collections" version = "2.1.1" @@ -2542,9 +2780,9 @@ dependencies = [ [[package]] name = "image" -version = "0.25.9" +version = "0.25.10" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e6506c6c10786659413faa717ceebcb8f70731c0a60cbae39795fdf114519c1a" +checksum = "85ab80394333c02fe689eaf900ab500fbd0c2213da414687ebf995a65d5a6104" dependencies = [ "bytemuck", "byteorder-lite", @@ -2798,6 +3036,17 @@ dependencies = [ "num-traits", ] +[[package]] +name = "khronos-egl" +version = "6.0.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6aae1df220ece3c0ada96b8153459b67eebe9ae9212258bb0134ae60416fdf76" +dependencies = [ + "libc", + "libloading", + "pkg-config", +] + [[package]] name = "khronos_api" version = "3.1.0" @@ -2806,9 +3055,9 @@ checksum = "e2db585e1d738fc771bf08a151420d3ed193d9d895a36df7f6f8a9456b911ddc" [[package]] name = "known-folders" -version = "1.4.1" +version = "1.4.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "770919970f7d2f74fea948900d35e2ef64f44129e8ae4015f59de1f0aca7c2a5" +checksum = "7a1886916523694cd6ea3d175f03a1e5010699a2a4cc13696d83d7bea1d80638" dependencies = [ "windows-sys 0.61.2", ] @@ -2877,23 +3126,25 @@ checksum = "09edd9e8b54e49e587e4f6295a7d29c3ea94d469cb40ab8ca70b288248a81db2" [[package]] name = "libc" -version = "0.2.182" +version = "0.2.186" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "6800badb6cb2082ffd7b6a67e6125bb39f18782f793520caee8cb8846be06112" +checksum = "68ab91017fe16c622486840e4c83c9a37afeff978bd239b5293d61ece587de66" [[package]] -name = "libcosmic" -version = "1.0.0" -source = "git+https://github.com/pop-os/libcosmic#0bb006c5bbf7eb89491891d45bfc8f21f8eb1305" +name = "libcosmic-yoda" +version = "0.1.0-yoda.2" dependencies = [ "apply", + "ashpd 0.12.3", "auto_enums", + "cosmic-client-toolkit", "cosmic-config", "cosmic-freedesktop-icons", "cosmic-settings-daemon", "cosmic-theme", "css-color", "derive_setters", + "enumflags2", "float-cmp 0.10.0", "futures", "i18n-embed", @@ -2904,18 +3155,21 @@ dependencies = [ "iced_renderer", "iced_runtime", "iced_tiny_skia", + "iced_wgpu", "iced_widget", + "iced_winit", "image", "jiff", "log", "palette", "phf 0.13.1", - "raw-window-handle", + "rfd", "rust-embed", "serde", "slotmap", "taffy", "thiserror 2.0.18", + "tokio", "tracing", "unicode-segmentation", "url", @@ -2963,7 +3217,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "d7c4b02199fee7c5d21a5ae7d8cfa79a6ef5bb2fc834d6e9058e89c825efdc55" dependencies = [ "cfg-if", - "windows-link", + "windows-link 0.2.1", ] [[package]] @@ -2974,13 +3228,14 @@ checksum = "b6d2cec3eae94f9f509c767b45932f1ada8350c4bdb85af2fcab4a3c14807981" [[package]] name = "libredox" -version = "0.1.12" +version = "0.1.18" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "3d0b95e02c851351f877147b7deea7b1afb1df71b63aa5f8270716e0c5720616" +checksum = "c943259e342f1e06ff2da7a83eabdfe7f92ce10262688dbf1895ff0b3e6e4652" dependencies = [ "bitflags 2.11.0", "libc", - "redox_syscall 0.7.2", + "plain", + "redox_syscall 0.9.0", ] [[package]] @@ -3206,6 +3461,15 @@ version = "2.8.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "f8ca58f447f06ed17d5fc4043ce1b10dd205e060fb3ce5b979b8ed8e59ff3f79" +[[package]] +name = "memmap2" +version = "0.8.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "43a5a03cefb0d953ec0be133036f14e109412fa594edc2f77227249db66cc3ed" +dependencies = [ + "libc", +] + [[package]] name = "memmap2" version = "0.9.10" @@ -3224,6 +3488,21 @@ dependencies = [ "autocfg", ] +[[package]] +name = "metal" +version = "0.33.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c7047791b5bc903b8cd963014b355f71dc9864a9a0b727057676c1dcae5cbc15" +dependencies = [ + "bitflags 2.11.0", + "block", + "core-graphics-types 0.2.0", + "foreign-types", + "log", + "objc", + "paste", +] + [[package]] name = "miette" version = "7.6.0" @@ -3231,13 +3510,12 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "5f98efec8807c63c752b5bd61f862c165c115b0a35685bdcfd9238c7aeb592b7" dependencies = [ "cfg-if", - "unicode-width", + "unicode-width 0.1.14", ] [[package]] name = "mime" version = "0.1.0" -source = "git+https://github.com/pop-os/window_clipboard.git?tag=sctk-0.20#f68595ee0e62fbd6589f4709b5aaa5c3c7ea5f6c" dependencies = [ "smithay-clipboard", ] @@ -3278,9 +3556,9 @@ checksum = "e53debba6bda7a793e5f99b8dacf19e626084f525f7829104ba9898f367d85ff" [[package]] name = "mio" -version = "1.1.1" +version = "1.2.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a69bcab0ad47271a0234d9422b131806bf3968021e5dc9328caf2d4cd58557fc" +checksum = "02bd0af71c67b473010cbbc60715ee815645a4dc942899111f494b4b737d6fda" dependencies = [ "libc", "log", @@ -3290,9 +3568,9 @@ dependencies = [ [[package]] name = "moxcms" -version = "0.7.11" +version = "0.8.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ac9557c559cd6fc9867e122e20d2cbefc9ca29d80d027a8e39310920ed2f0a97" +checksum = "bb85c154ba489f01b25c0d36ae69a87e4a1c73a72631fc6c0eb6dde34a73e44b" dependencies = [ "num-traits", "pxfm", @@ -3306,9 +3584,9 @@ checksum = "13d2233c9842d08cfe13f9eac96e207ca6a2ea10b80259ebe8ad0268be27d2af" [[package]] name = "naga" -version = "27.0.3" +version = "28.0.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "066cf25f0e8b11ee0df221219010f213ad429855f57c494f995590c861a9a7d8" +checksum = "618f667225063219ddfc61251087db8a9aec3c3f0950c916b614e403486f1135" dependencies = [ "arrayvec", "bit-set", @@ -3325,6 +3603,7 @@ dependencies = [ "num-traits", "once_cell", "rustc-hash 1.1.0", + "spirv", "thiserror 2.0.18", "unicode-ident", ] @@ -3545,6 +3824,22 @@ dependencies = [ "objc_id", ] +[[package]] +name = "objc-sys" +version = "0.3.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "cdb91bdd390c7ce1a8607f35f3ca7151b65afc0ff5ff3b34fa350f7d7c7e4310" + +[[package]] +name = "objc2" +version = "0.5.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "46a785d4eeff09c14c487497c162e92766fbb3e4059a71840cecc03d9a50b804" +dependencies = [ + "objc-sys", + "objc2-encode", +] + [[package]] name = "objc2" version = "0.6.4" @@ -3554,6 +3849,22 @@ dependencies = [ "objc2-encode", ] +[[package]] +name = "objc2-app-kit" +version = "0.2.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e4e89ad9e3d7d297152b17d39ed92cd50ca8063a89a9fa569046d41568891eff" +dependencies = [ + "bitflags 2.11.0", + "block2 0.5.1", + "libc", + "objc2 0.5.2", + "objc2-core-data", + "objc2-core-image", + "objc2-foundation 0.2.2", + "objc2-quartz-core", +] + [[package]] name = "objc2-app-kit" version = "0.3.2" @@ -3561,9 +3872,22 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "d49e936b501e5c5bf01fda3a9452ff86dc3ea98ad5f283e1455153142d97518c" dependencies = [ "bitflags 2.11.0", - "objc2", + "block2 0.6.2", + "objc2 0.6.4", "objc2-core-foundation", - "objc2-foundation", + "objc2-foundation 0.3.2", +] + +[[package]] +name = "objc2-core-data" +version = "0.2.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "617fbf49e071c178c0b24c080767db52958f716d9eabdf0890523aeae54773ef" +dependencies = [ + "bitflags 2.11.0", + "block2 0.5.1", + "objc2 0.5.2", + "objc2-foundation 0.2.2", ] [[package]] @@ -3573,9 +3897,9 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "2a180dd8642fa45cdb7dd721cd4c11b1cadd4929ce112ebd8b9f5803cc79d536" dependencies = [ "bitflags 2.11.0", - "block2", + "block2 0.6.2", "dispatch2", - "objc2", + "objc2 0.6.4", ] [[package]] @@ -3589,6 +3913,18 @@ dependencies = [ "objc2-core-foundation", ] +[[package]] +name = "objc2-core-image" +version = "0.2.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "55260963a527c99f1819c4f8e3b47fe04f9650694ef348ffd2227e8196d34c80" +dependencies = [ + "block2 0.5.1", + "objc2 0.5.2", + "objc2-foundation 0.2.2", + "objc2-metal", +] + [[package]] name = "objc2-core-video" version = "0.3.2" @@ -3606,6 +3942,18 @@ version = "4.1.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "ef25abbcd74fb2609453eb695bd2f860d389e457f67dc17cafc8b8cbc89d0c33" +[[package]] +name = "objc2-foundation" +version = "0.2.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0ee638a5da3799329310ad4cfa62fbf045d5f56e3ef5ba4149e7452dcf89d5a8" +dependencies = [ + "bitflags 2.11.0", + "block2 0.5.1", + "libc", + "objc2 0.5.2", +] + [[package]] name = "objc2-foundation" version = "0.3.2" @@ -3613,11 +3961,36 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "e3e0adef53c21f888deb4fa59fc59f7eb17404926ee8a6f59f5df0fd7f9f3272" dependencies = [ "bitflags 2.11.0", - "block2", - "objc2", + "block2 0.6.2", + "objc2 0.6.4", "objc2-core-foundation", ] +[[package]] +name = "objc2-metal" +version = "0.2.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "dd0cba1276f6023976a406a14ffa85e1fdd19df6b0f737b063b95f6c8c7aadd6" +dependencies = [ + "bitflags 2.11.0", + "block2 0.5.1", + "objc2 0.5.2", + "objc2-foundation 0.2.2", +] + +[[package]] +name = "objc2-quartz-core" +version = "0.2.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e42bee7bff906b14b167da2bac5efe6b6a07e6f7c0a21a7308d40c960242dc7a" +dependencies = [ + "bitflags 2.11.0", + "block2 0.5.1", + "objc2 0.5.2", + "objc2-foundation 0.2.2", + "objc2-metal", +] + [[package]] name = "objc2-ui-kit" version = "0.3.2" @@ -3625,9 +3998,9 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "d87d638e33c06f577498cbcc50491496a3ed4246998a7fbba7ccb98b1e7eab22" dependencies = [ "bitflags 2.11.0", - "objc2", + "objc2 0.6.4", "objc2-core-foundation", - "objc2-foundation", + "objc2-foundation 0.3.2", ] [[package]] @@ -3773,7 +4146,7 @@ dependencies = [ "libc", "redox_syscall 0.5.18", "smallvec", - "windows-link", + "windows-link 0.2.1", ] [[package]] @@ -3950,6 +4323,12 @@ version = "0.3.32" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "7edddbd0b52d732b21ad9a5fab5c704c14cd949e5e9a1ec5929a24fded1b904c" +[[package]] +name = "plain" +version = "0.2.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b4596b6d070b27117e987119b4dac604f3c58cfb0b191112e24771b2faeac1a6" + [[package]] name = "png" version = "0.17.16" @@ -3990,6 +4369,12 @@ dependencies = [ "windows-sys 0.61.2", ] +[[package]] +name = "pollster" +version = "0.4.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "2f3a9f18d041e6d0e102a0a46750538147e5e8992d3b4873aaafee2520b00ce3" + [[package]] name = "portable-atomic" version = "1.13.1" @@ -4029,6 +4414,12 @@ dependencies = [ "zerocopy", ] +[[package]] +name = "presser" +version = "0.3.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e8cf8e6a8aa66ce33f63993ffc4ea4271eb5b0530a9002db8455ea6050c77bfa" + [[package]] name = "prettyplease" version = "0.2.37" @@ -4134,6 +4525,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "958f21e8e7ceb5a1aa7fa87fab28e7c75976e0bfe7e23ff069e0a260f894067d" dependencies = [ "memchr", + "serde", ] [[package]] @@ -4157,6 +4549,8 @@ version = "0.8.5" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "34af8d1a0e25924bc5b7c43c079c942339d8f0a8b57c39049bef581b46327404" dependencies = [ + "libc", + "rand_chacha 0.3.1", "rand_core 0.6.4", ] @@ -4166,7 +4560,7 @@ version = "0.9.2" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "6db2770f06117d490610c7488547d543617b21bfa07796d7a12f6f1bd53850d1" dependencies = [ - "rand_chacha", + "rand_chacha 0.9.0", "rand_core 0.9.5", ] @@ -4181,6 +4575,16 @@ dependencies = [ "rand_core 0.10.0", ] +[[package]] +name = "rand_chacha" +version = "0.3.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e6c10a63a0fa32252be49d21e7709d4d4baf8d231c2dbce1eaa8141b9b127d88" +dependencies = [ + "ppv-lite86", + "rand_core 0.6.4", +] + [[package]] name = "rand_chacha" version = "0.9.0" @@ -4196,6 +4600,9 @@ name = "rand_core" version = "0.6.4" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "ec0be4795e2f6a28069bec0b5ff3e2ac9bafc99e6a9a7dc3547996c5c816922c" +dependencies = [ + "getrandom 0.2.17", +] [[package]] name = "rand_core" @@ -4212,6 +4619,12 @@ version = "0.10.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "0c8d0fd677905edcbeedbf2edb6494d676f0e98d54d5cf9bda0b061cb8fb8aba" +[[package]] +name = "range-alloc" +version = "0.1.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ca45419789ae5a7899559e9512e58ca889e41f04f1f2445e9f4b290ceccd1d08" + [[package]] name = "rangemap" version = "1.7.1" @@ -4251,6 +4664,16 @@ dependencies = [ "font-types 0.11.0", ] +[[package]] +name = "redox_event" +version = "0.4.8" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c5018d583d6d2f5499352aea8d177e9067d1eb03ab17c78169d5ba7a30001b15" +dependencies = [ + "bitflags 2.11.0", + "libredox", +] + [[package]] name = "redox_syscall" version = "0.5.18" @@ -4262,9 +4685,9 @@ dependencies = [ [[package]] name = "redox_syscall" -version = "0.7.2" +version = "0.9.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "6d94dd2f7cd932d4dc02cc8b2b50dfd38bd079a4e5d79198b99743d7fcf9a4b4" +checksum = "c5102a6aaa05aa011a238e178e6bca86d2cb56fc9f586d37cb80f5bca6e07759" dependencies = [ "bitflags 2.11.0", ] @@ -4378,6 +4801,30 @@ dependencies = [ "zune-jpeg 0.4.21", ] +[[package]] +name = "rfd" +version = "0.16.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a15ad77d9e70a92437d8f74c35d99b4e4691128df018833e99f90bcd36152672" +dependencies = [ + "ashpd 0.11.1", + "block2 0.6.2", + "dispatch2", + "js-sys", + "log", + "objc2 0.6.4", + "objc2-app-kit 0.3.2", + "objc2-core-foundation", + "objc2-foundation 0.3.2", + "pollster", + "raw-window-handle", + "urlencoding", + "wasm-bindgen", + "wasm-bindgen-futures", + "web-sys", + "windows-sys 0.60.2", +] + [[package]] name = "rgb" version = "0.8.52" @@ -4582,6 +5029,19 @@ version = "1.2.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "94143f37725109f92c262ed2cf5e59bce7498c01bcc1502d7b9afe439a4e9f49" +[[package]] +name = "sctk-adwaita" +version = "0.11.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1dd3accc0f3f4bbaf2c9e1957a030dc582028130c67660d44c0a0345a22ca69b" +dependencies = [ + "ab_glyph", + "log", + "memmap2 0.9.10", + "smithay-client-toolkit", + "tiny-skia", +] + [[package]] name = "self_cell" version = "1.2.2" @@ -4808,8 +5268,8 @@ dependencies = [ "cgmath", "cursor-icon", "downcast-rs", - "drm 0.14.1", - "drm-ffi 0.9.0", + "drm", + "drm-ffi", "drm-fourcc", "encoding_rs", "errno", @@ -4842,7 +5302,7 @@ dependencies = [ "wayland-protocols-misc", "wayland-protocols-wlr", "wayland-server", - "winit", + "winit 0.31.0-beta.2 (registry+https://github.com/rust-lang/crates.io-index)", "x11rb", "xkbcommon 0.9.0", ] @@ -4860,7 +5320,7 @@ dependencies = [ "cursor-icon", "libc", "log", - "memmap2", + "memmap2 0.9.10", "pkg-config", "rustix 1.1.4", "thiserror 2.0.18", @@ -4880,7 +5340,6 @@ dependencies = [ [[package]] name = "smithay-clipboard" version = "0.8.0" -source = "git+https://github.com/pop-os/smithay-clipboard?tag=sctk-0.20#859b02c88f45c554049a67c6ddeec1692ce0e20b" dependencies = [ "libc", "raw-window-handle", @@ -4921,22 +5380,30 @@ version = "1.3.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "27207bb65232eda1f588cf46db2fee75c0808d557f6b3cf19a75f5d6d7c94df1" +[[package]] +name = "socket2" +version = "0.6.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "52d1cfed4120b4d927bf7c0f86d2087a4a7d6027c906d9f9d525a80573b9be51" +dependencies = [ + "libc", + "windows-sys 0.61.2", +] + [[package]] name = "softbuffer" version = "0.4.1" -source = "git+https://github.com/pop-os/softbuffer?tag=cosmic-4.0#a3f77e251e7422803f693df6e3fc313c010c4dcb" dependencies = [ "as-raw-xcb-connection", "bytemuck", "cfg_aliases", "cocoa", "core-graphics", - "drm 0.11.1", "fastrand", "foreign-types", "js-sys", "log", - "memmap2", + "memmap2 0.9.10", "objc", "raw-window-handle", "redox_syscall 0.5.18", @@ -4951,6 +5418,15 @@ dependencies = [ "x11rb", ] +[[package]] +name = "spirv" +version = "0.3.0+sdk-1.3.268.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "eda41003dc44290527a59b13432d4a0379379fa074b70174882adfbdfd917844" +dependencies = [ + "bitflags 2.11.0", +] + [[package]] name = "stable_deref_trait" version = "1.2.1" @@ -5096,6 +5572,15 @@ dependencies = [ "windows-sys 0.61.2", ] +[[package]] +name = "termcolor" +version = "1.4.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "06794f8f6c5c898b3275aebefa6b8a1cb24cd2c6c79397ab15774837a0bc5755" +dependencies = [ + "winapi-util", +] + [[package]] name = "thiserror" version = "1.0.69" @@ -5241,6 +5726,45 @@ version = "0.1.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "1f3ccbac311fea05f86f61904b462b55fb3df8837a366dfc601a0161d0532f20" +[[package]] +name = "tokio" +version = "1.52.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8fc7f01b389ac15039e4dc9531aa973a135d7a4135281b12d7c1bc79fd57fffe" +dependencies = [ + "bytes", + "libc", + "mio", + "pin-project-lite", + "signal-hook-registry", + "socket2", + "tokio-macros", + "tracing", + "windows-sys 0.61.2", +] + +[[package]] +name = "tokio-macros" +version = "2.7.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "385a6cb71ab9ab790c5fe8d67f1645e6c450a7ce006a33de03daa956cf70a496" +dependencies = [ + "proc-macro2", + "quote", + "syn", +] + +[[package]] +name = "tokio-stream" +version = "0.1.18" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "32da49809aab5c3bc678af03902d4ccddea2a87d028d86392a4b1560c6906c70" +dependencies = [ + "futures-core", + "pin-project-lite", + "tokio", +] + [[package]] name = "toml" version = "0.5.11" @@ -5526,9 +6050,9 @@ checksum = "383ad40bb927465ec0ce7720e033cb4ca06912855fc35db31b5755d0de75b1ee" [[package]] name = "unicode-segmentation" -version = "1.12.0" +version = "1.13.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f6ccf251212114b54433ec949fd6a7841275f9ada20dddd2f29e9ceea4501493" +checksum = "c6f5d3c3b1bf09027a88a6bc961fc00497d651009560b5463668dc81b0fa87a8" [[package]] name = "unicode-vo" @@ -5542,6 +6066,12 @@ version = "0.1.14" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "7dd6e30e90baa6f72411720665d41d89b9a3d039dc45b8faea1ddd07f617f6af" +[[package]] +name = "unicode-width" +version = "0.2.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b4ac048d71ede7ee76d585517add45da530660ef4390e49b098733c6e897f254" + [[package]] name = "unicode-xid" version = "0.2.6" @@ -5558,8 +6088,15 @@ dependencies = [ "idna", "percent-encoding", "serde", + "serde_derive", ] +[[package]] +name = "urlencoding" +version = "2.1.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "daf8dba3b7eb870caf1ddeed7bc9d2a049f3cfdfae7cb521b087cc33ae4c49da" + [[package]] name = "usvg" version = "0.37.0" @@ -5996,22 +6533,29 @@ checksum = "a28ac98ddc8b9274cb41bb4d9d4d5c425b6020c50c46f25559911905610b4a88" [[package]] name = "wgpu" -version = "27.0.1" +version = "28.0.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "bfe68bac7cde125de7a731c3400723cadaaf1703795ad3f4805f187459cd7a77" +checksum = "f9cb534d5ffd109c7d1135f34cdae29e60eab94855a625dcfe1705f8bc7ad79f" dependencies = [ "arrayvec", "bitflags 2.11.0", + "bytemuck", "cfg-if", "cfg_aliases", "document-features", "hashbrown 0.16.1", + "js-sys", "log", + "naga", + "parking_lot", "portable-atomic", "profiling", "raw-window-handle", "smallvec", "static_assertions", + "wasm-bindgen", + "wasm-bindgen-futures", + "web-sys", "wgpu-core", "wgpu-hal", "wgpu-types", @@ -6019,9 +6563,9 @@ dependencies = [ [[package]] name = "wgpu-core" -version = "27.0.3" +version = "28.0.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "27a75de515543b1897b26119f93731b385a19aea165a1ec5f0e3acecc229cae7" +checksum = "d23f4642f53f666adcfd2d3218ab174d1e6681101aef18696b90cbe64d1c10f9" dependencies = [ "arrayvec", "bit-set", @@ -6042,51 +6586,98 @@ dependencies = [ "rustc-hash 1.1.0", "smallvec", "thiserror 2.0.18", + "wgpu-core-deps-apple", + "wgpu-core-deps-emscripten", "wgpu-core-deps-windows-linux-android", "wgpu-hal", "wgpu-types", ] [[package]] -name = "wgpu-core-deps-windows-linux-android" -version = "27.0.0" +name = "wgpu-core-deps-apple" +version = "28.0.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "71197027d61a71748e4120f05a9242b2ad142e3c01f8c1b47707945a879a03c3" +checksum = "87b7b696b918f337c486bf93142454080a32a37832ba8a31e4f48221890047da" +dependencies = [ + "wgpu-hal", +] + +[[package]] +name = "wgpu-core-deps-emscripten" +version = "28.0.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "34b251c331f84feac147de3c4aa3aa45112622a95dd7ee1b74384fa0458dbd79" +dependencies = [ + "wgpu-hal", +] + +[[package]] +name = "wgpu-core-deps-windows-linux-android" +version = "28.0.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "68ca976e72b2c9964eb243e281f6ce7f14a514e409920920dcda12ae40febaae" dependencies = [ "wgpu-hal", ] [[package]] name = "wgpu-hal" -version = "27.0.4" +version = "28.0.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5b21cb61c57ee198bc4aff71aeadff4cbb80b927beb912506af9c780d64313ce" +checksum = "44d6cb474beb218824dcc9e1ce679d973f719262789bfb27407da560cac20eeb" dependencies = [ + "android_system_properties", + "arrayvec", + "ash", + "bit-set", "bitflags 2.11.0", + "block", + "bytemuck", "cfg-if", "cfg_aliases", + "core-graphics-types 0.2.0", + "glow", + "glutin_wgl_sys", + "gpu-allocator", + "gpu-descriptor", + "hashbrown 0.16.1", + "js-sys", + "khronos-egl", + "libc", "libloading", "log", + "metal", "naga", + "ndk-sys", + "objc", + "once_cell", + "ordered-float", + "parking_lot", "portable-atomic", "portable-atomic-util", + "profiling", + "range-alloc", "raw-window-handle", "renderdoc-sys", + "smallvec", "thiserror 2.0.18", + "wasm-bindgen", + "web-sys", "wgpu-types", + "windows 0.62.2", + "windows-core 0.62.2", ] [[package]] name = "wgpu-types" -version = "27.0.1" +version = "28.0.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "afdcf84c395990db737f2dd91628706cb31e86d72e53482320d368e52b5da5eb" +checksum = "e18308757e594ed2cd27dddbb16a139c42a683819d32a2e0b1b0167552f5840c" dependencies = [ "bitflags 2.11.0", "bytemuck", "js-sys", "log", - "thiserror 2.0.18", "web-sys", ] @@ -6124,18 +6715,72 @@ checksum = "712e227841d057c1ee1cd2fb22fa7e5a5461ae8e48fa2ca79ec42cfc1931183f" [[package]] name = "window_clipboard" version = "0.4.1" -source = "git+https://github.com/pop-os/window_clipboard.git?tag=sctk-0.20#f68595ee0e62fbd6589f4709b5aaa5c3c7ea5f6c" dependencies = [ "clipboard-win", "clipboard_macos", "clipboard_wayland", - "clipboard_x11", "dnd", "mime 0.1.0", "raw-window-handle", "thiserror 1.0.69", ] +[[package]] +name = "windows" +version = "0.61.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9babd3a767a4c1aef6900409f85f5d53ce2544ccdfaa86dad48c91782c6d6893" +dependencies = [ + "windows-collections 0.2.0", + "windows-core 0.61.2", + "windows-future 0.2.1", + "windows-link 0.1.3", + "windows-numerics 0.2.0", +] + +[[package]] +name = "windows" +version = "0.62.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "527fadee13e0c05939a6a05d5bd6eec6cd2e3dbd648b9f8e447c6518133d8580" +dependencies = [ + "windows-collections 0.3.2", + "windows-core 0.62.2", + "windows-future 0.3.2", + "windows-numerics 0.3.1", +] + +[[package]] +name = "windows-collections" +version = "0.2.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3beeceb5e5cfd9eb1d76b381630e82c4241ccd0d27f1a39ed41b2760b255c5e8" +dependencies = [ + "windows-core 0.61.2", +] + +[[package]] +name = "windows-collections" +version = "0.3.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "23b2d95af1a8a14a3c7367e1ed4fc9c20e0a26e79551b1454d72583c97cc6610" +dependencies = [ + "windows-core 0.62.2", +] + +[[package]] +name = "windows-core" +version = "0.61.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c0fdd3ddb90610c7638aa2b3a3ab2904fb9e5cdbecc643ddb3647212781c4ae3" +dependencies = [ + "windows-implement", + "windows-interface", + "windows-link 0.1.3", + "windows-result 0.3.4", + "windows-strings 0.4.2", +] + [[package]] name = "windows-core" version = "0.62.2" @@ -6144,9 +6789,31 @@ checksum = "b8e83a14d34d0623b51dce9581199302a221863196a1dde71a7663a4c2be9deb" dependencies = [ "windows-implement", "windows-interface", - "windows-link", - "windows-result", - "windows-strings", + "windows-link 0.2.1", + "windows-result 0.4.1", + "windows-strings 0.5.1", +] + +[[package]] +name = "windows-future" +version = "0.2.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "fc6a41e98427b19fe4b73c550f060b59fa592d7d686537eebf9385621bfbad8e" +dependencies = [ + "windows-core 0.61.2", + "windows-link 0.1.3", + "windows-threading 0.1.0", +] + +[[package]] +name = "windows-future" +version = "0.3.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e1d6f90251fe18a279739e78025bd6ddc52a7e22f921070ccdc67dde84c605cb" +dependencies = [ + "windows-core 0.62.2", + "windows-link 0.2.1", + "windows-threading 0.2.1", ] [[package]] @@ -6171,19 +6838,63 @@ dependencies = [ "syn", ] +[[package]] +name = "windows-link" +version = "0.1.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5e6ad25900d524eaabdbbb96d20b4311e1e7ae1699af4fb28c17ae66c80d798a" + [[package]] name = "windows-link" version = "0.2.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "f0805222e57f7521d6a62e36fa9163bc891acd422f971defe97d64e70d0a4fe5" +[[package]] +name = "windows-numerics" +version = "0.2.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9150af68066c4c5c07ddc0ce30421554771e528bde427614c61038bc2c92c2b1" +dependencies = [ + "windows-core 0.61.2", + "windows-link 0.1.3", +] + +[[package]] +name = "windows-numerics" +version = "0.3.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6e2e40844ac143cdb44aead537bbf727de9b044e107a0f1220392177d15b0f26" +dependencies = [ + "windows-core 0.62.2", + "windows-link 0.2.1", +] + +[[package]] +name = "windows-result" +version = "0.3.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "56f42bd332cc6c8eac5af113fc0c1fd6a8fd2aa08a0119358686e5160d0586c6" +dependencies = [ + "windows-link 0.1.3", +] + [[package]] name = "windows-result" version = "0.4.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "7781fa89eaf60850ac3d2da7af8e5242a5ea78d1a11c49bf2910bb5a73853eb5" dependencies = [ - "windows-link", + "windows-link 0.2.1", +] + +[[package]] +name = "windows-strings" +version = "0.4.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "56e6c93f3a0c3b36176cb1327a4958a0353d5d166c2a35cb268ace15e91d3b57" +dependencies = [ + "windows-link 0.1.3", ] [[package]] @@ -6192,7 +6903,7 @@ version = "0.5.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "7837d08f69c77cf6b07689544538e017c1bfcf57e34b4c0ff58e6c2cd3b37091" dependencies = [ - "windows-link", + "windows-link 0.2.1", ] [[package]] @@ -6246,7 +6957,7 @@ version = "0.61.2" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "ae137229bcbd6cdf0f7b80a31df61766145077ddf49416a728b02cb3921ff3fc" dependencies = [ - "windows-link", + "windows-link 0.2.1", ] [[package]] @@ -6301,7 +7012,7 @@ version = "0.53.5" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "4945f9f551b88e0d65f3db0bc25c33b8acea4d9e41163edf90dcd0b19f9069f3" dependencies = [ - "windows-link", + "windows-link 0.2.1", "windows_aarch64_gnullvm 0.53.1", "windows_aarch64_msvc 0.53.1", "windows_i686_gnu 0.53.1", @@ -6312,6 +7023,24 @@ dependencies = [ "windows_x86_64_msvc 0.53.1", ] +[[package]] +name = "windows-threading" +version = "0.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b66463ad2e0ea3bbf808b7f1d371311c80e115c0b71d60efc142cafbcfb057a6" +dependencies = [ + "windows-link 0.1.3", +] + +[[package]] +name = "windows-threading" +version = "0.2.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3949bd5b99cafdf1c7ca86b43ca564028dfe27d66958f2470940f73d86d75b37" +dependencies = [ + "windows-link 0.2.1", +] + [[package]] name = "windows_aarch64_gnullvm" version = "0.42.2" @@ -6492,6 +7221,30 @@ version = "0.53.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "d6bbff5f0aada427a1e5a6da5f1f98158182f26556f345ac9e04d36d0ebed650" +[[package]] +name = "winit" +version = "0.31.0-beta.2" +dependencies = [ + "bitflags 2.11.0", + "cfg_aliases", + "cursor-icon", + "dpi 0.1.2", + "libc", + "raw-window-handle", + "rustix 1.1.4", + "smol_str", + "tracing", + "winit-android 0.31.0-beta.2", + "winit-appkit 0.31.0-beta.2", + "winit-common 0.31.0-beta.2", + "winit-core 0.31.0-beta.2", + "winit-orbital 0.31.0-beta.2", + "winit-uikit 0.31.0-beta.2", + "winit-wayland 0.31.0-beta.2", + "winit-web 0.31.0-beta.2", + "winit-win32 0.31.0-beta.2", +] + [[package]] name = "winit" version = "0.31.0-beta.2" @@ -6501,24 +7254,38 @@ dependencies = [ "bitflags 2.11.0", "cfg_aliases", "cursor-icon", - "dpi", + "dpi 0.1.2 (registry+https://github.com/rust-lang/crates.io-index)", "libc", "raw-window-handle", "rustix 1.1.4", "smol_str", "tracing", - "winit-android", - "winit-appkit", - "winit-common", - "winit-core", - "winit-orbital", - "winit-uikit", - "winit-wayland", - "winit-web", - "winit-win32", + "winit-android 0.31.0-beta.2 (registry+https://github.com/rust-lang/crates.io-index)", + "winit-appkit 0.31.0-beta.2 (registry+https://github.com/rust-lang/crates.io-index)", + "winit-common 0.31.0-beta.2 (registry+https://github.com/rust-lang/crates.io-index)", + "winit-core 0.31.0-beta.2 (registry+https://github.com/rust-lang/crates.io-index)", + "winit-orbital 0.31.0-beta.2 (registry+https://github.com/rust-lang/crates.io-index)", + "winit-uikit 0.31.0-beta.2 (registry+https://github.com/rust-lang/crates.io-index)", + "winit-wayland 0.31.0-beta.2 (registry+https://github.com/rust-lang/crates.io-index)", + "winit-web 0.31.0-beta.2 (registry+https://github.com/rust-lang/crates.io-index)", + "winit-win32 0.31.0-beta.2 (registry+https://github.com/rust-lang/crates.io-index)", "winit-x11", ] +[[package]] +name = "winit-android" +version = "0.31.0-beta.2" +dependencies = [ + "android-activity", + "bitflags 2.11.0", + "dpi 0.1.2", + "ndk", + "raw-window-handle", + "smol_str", + "tracing", + "winit-core 0.31.0-beta.2", +] + [[package]] name = "winit-android" version = "0.31.0-beta.2" @@ -6527,12 +7294,33 @@ checksum = "51d9c0d2cd93efec3a9f9ad819cfaf0834782403af7c0d248c784ec0c61761df" dependencies = [ "android-activity", "bitflags 2.11.0", - "dpi", + "dpi 0.1.2 (registry+https://github.com/rust-lang/crates.io-index)", "ndk", "raw-window-handle", "smol_str", "tracing", - "winit-core", + "winit-core 0.31.0-beta.2 (registry+https://github.com/rust-lang/crates.io-index)", +] + +[[package]] +name = "winit-appkit" +version = "0.31.0-beta.2" +dependencies = [ + "bitflags 2.11.0", + "block2 0.6.2", + "dispatch2", + "dpi 0.1.2", + "objc2 0.6.4", + "objc2-app-kit 0.3.2", + "objc2-core-foundation", + "objc2-core-graphics", + "objc2-core-video", + "objc2-foundation 0.3.2", + "raw-window-handle", + "smol_str", + "tracing", + "winit-common 0.31.0-beta.2", + "winit-core 0.31.0-beta.2", ] [[package]] @@ -6542,20 +7330,33 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "21310ca07851a49c348e0c2cc768e36b52ca65afda2c2354d78ed4b90074d8aa" dependencies = [ "bitflags 2.11.0", - "block2", + "block2 0.6.2", "dispatch2", - "dpi", - "objc2", - "objc2-app-kit", + "dpi 0.1.2 (registry+https://github.com/rust-lang/crates.io-index)", + "objc2 0.6.4", + "objc2-app-kit 0.3.2", "objc2-core-foundation", "objc2-core-graphics", "objc2-core-video", - "objc2-foundation", + "objc2-foundation 0.3.2", "raw-window-handle", "smol_str", "tracing", - "winit-common", - "winit-core", + "winit-common 0.31.0-beta.2 (registry+https://github.com/rust-lang/crates.io-index)", + "winit-core 0.31.0-beta.2 (registry+https://github.com/rust-lang/crates.io-index)", +] + +[[package]] +name = "winit-common" +version = "0.31.0-beta.2" +dependencies = [ + "memmap2 0.9.10", + "objc2 0.6.4", + "objc2-core-foundation", + "smol_str", + "tracing", + "winit-core 0.31.0-beta.2", + "xkbcommon-dl", ] [[package]] @@ -6564,16 +7365,29 @@ version = "0.31.0-beta.2" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "45375fbac4cbb77260d83a30b1f9d8105880dbac99a9ae97f56656694680ff69" dependencies = [ - "memmap2", - "objc2", + "memmap2 0.9.10", + "objc2 0.6.4", "objc2-core-foundation", "smol_str", "tracing", - "winit-core", + "winit-core 0.31.0-beta.2 (registry+https://github.com/rust-lang/crates.io-index)", "x11-dl", "xkbcommon-dl", ] +[[package]] +name = "winit-core" +version = "0.31.0-beta.2" +dependencies = [ + "bitflags 2.11.0", + "cursor-icon", + "dpi 0.1.2", + "keyboard-types", + "raw-window-handle", + "smol_str", + "web-time", +] + [[package]] name = "winit-core" version = "0.31.0-beta.2" @@ -6582,13 +7396,28 @@ checksum = "e4f0ccd7abb43740e2c6124ac7cae7d865ecec74eec63783e8922577ac232583" dependencies = [ "bitflags 2.11.0", "cursor-icon", - "dpi", + "dpi 0.1.2 (registry+https://github.com/rust-lang/crates.io-index)", "keyboard-types", "raw-window-handle", "smol_str", "web-time", ] +[[package]] +name = "winit-orbital" +version = "0.31.0-beta.2" +dependencies = [ + "bitflags 2.11.0", + "dpi 0.1.2", + "libredox", + "orbclient", + "raw-window-handle", + "redox_event", + "smol_str", + "tracing", + "winit-core 0.31.0-beta.2", +] + [[package]] name = "winit-orbital" version = "0.31.0-beta.2" @@ -6596,13 +7425,32 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "51ea1fb262e7209f265f12bd0cc792c399b14355675e65531e9c8a87db287d46" dependencies = [ "bitflags 2.11.0", - "dpi", + "dpi 0.1.2 (registry+https://github.com/rust-lang/crates.io-index)", "orbclient", "raw-window-handle", "redox_syscall 0.5.18", "smol_str", "tracing", - "winit-core", + "winit-core 0.31.0-beta.2 (registry+https://github.com/rust-lang/crates.io-index)", +] + +[[package]] +name = "winit-uikit" +version = "0.31.0-beta.2" +dependencies = [ + "bitflags 2.11.0", + "block2 0.6.2", + "dispatch2", + "dpi 0.1.2", + "objc2 0.6.4", + "objc2-core-foundation", + "objc2-foundation 0.3.2", + "objc2-ui-kit", + "raw-window-handle", + "smol_str", + "tracing", + "winit-common 0.31.0-beta.2", + "winit-core 0.31.0-beta.2", ] [[package]] @@ -6612,18 +7460,43 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "680a356e798837d8eb274d4556e83bceaf81698194e31aafc5cfb8a9f2fab643" dependencies = [ "bitflags 2.11.0", - "block2", + "block2 0.6.2", "dispatch2", - "dpi", - "objc2", + "dpi 0.1.2 (registry+https://github.com/rust-lang/crates.io-index)", + "objc2 0.6.4", "objc2-core-foundation", - "objc2-foundation", + "objc2-foundation 0.3.2", "objc2-ui-kit", "raw-window-handle", "smol_str", "tracing", - "winit-common", - "winit-core", + "winit-common 0.31.0-beta.2 (registry+https://github.com/rust-lang/crates.io-index)", + "winit-core 0.31.0-beta.2 (registry+https://github.com/rust-lang/crates.io-index)", +] + +[[package]] +name = "winit-wayland" +version = "0.31.0-beta.2" +dependencies = [ + "ahash", + "bitflags 2.11.0", + "calloop", + "cursor-icon", + "dpi 0.1.2", + "libc", + "memmap2 0.9.10", + "raw-window-handle", + "rustix 1.1.4", + "sctk-adwaita", + "smithay-client-toolkit", + "smol_str", + "tracing", + "wayland-backend", + "wayland-client", + "wayland-protocols", + "wayland-protocols-plasma", + "winit-common 0.31.0-beta.2", + "winit-core 0.31.0-beta.2", ] [[package]] @@ -6636,9 +7509,9 @@ dependencies = [ "bitflags 2.11.0", "calloop", "cursor-icon", - "dpi", + "dpi 0.1.2 (registry+https://github.com/rust-lang/crates.io-index)", "libc", - "memmap2", + "memmap2 0.9.10", "raw-window-handle", "rustix 1.1.4", "smithay-client-toolkit", @@ -6648,8 +7521,29 @@ dependencies = [ "wayland-client", "wayland-protocols", "wayland-protocols-plasma", - "winit-common", - "winit-core", + "winit-common 0.31.0-beta.2 (registry+https://github.com/rust-lang/crates.io-index)", + "winit-core 0.31.0-beta.2 (registry+https://github.com/rust-lang/crates.io-index)", +] + +[[package]] +name = "winit-web" +version = "0.31.0-beta.2" +dependencies = [ + "atomic-waker", + "bitflags 2.11.0", + "concurrent-queue", + "cursor-icon", + "dpi 0.1.2", + "js-sys", + "pin-project", + "raw-window-handle", + "smol_str", + "tracing", + "wasm-bindgen", + "wasm-bindgen-futures", + "web-sys", + "web-time", + "winit-core 0.31.0-beta.2", ] [[package]] @@ -6662,7 +7556,7 @@ dependencies = [ "bitflags 2.11.0", "concurrent-queue", "cursor-icon", - "dpi", + "dpi 0.1.2 (registry+https://github.com/rust-lang/crates.io-index)", "js-sys", "pin-project", "raw-window-handle", @@ -6672,7 +7566,22 @@ dependencies = [ "wasm-bindgen-futures", "web-sys", "web-time", - "winit-core", + "winit-core 0.31.0-beta.2 (registry+https://github.com/rust-lang/crates.io-index)", +] + +[[package]] +name = "winit-win32" +version = "0.31.0-beta.2" +dependencies = [ + "bitflags 2.11.0", + "cursor-icon", + "dpi 0.1.2", + "raw-window-handle", + "smol_str", + "tracing", + "unicode-segmentation", + "windows-sys 0.59.0", + "winit-core 0.31.0-beta.2", ] [[package]] @@ -6683,13 +7592,13 @@ checksum = "644ea78af0e858aa3b092e5d1c67c41995a98220c81813f1353b28bc8bb91eaa" dependencies = [ "bitflags 2.11.0", "cursor-icon", - "dpi", + "dpi 0.1.2 (registry+https://github.com/rust-lang/crates.io-index)", "raw-window-handle", "smol_str", "tracing", "unicode-segmentation", "windows-sys 0.59.0", - "winit-core", + "winit-core 0.31.0-beta.2 (registry+https://github.com/rust-lang/crates.io-index)", ] [[package]] @@ -6702,15 +7611,15 @@ dependencies = [ "bytemuck", "calloop", "cursor-icon", - "dpi", + "dpi 0.1.2 (registry+https://github.com/rust-lang/crates.io-index)", "libc", "percent-encoding", "raw-window-handle", "rustix 1.1.4", "smol_str", "tracing", - "winit-common", - "winit-core", + "winit-common 0.31.0-beta.2 (registry+https://github.com/rust-lang/crates.io-index)", + "winit-core 0.31.0-beta.2 (registry+https://github.com/rust-lang/crates.io-index)", "x11-dl", "x11rb", "xkbcommon-dl", @@ -6734,6 +7643,15 @@ dependencies = [ "memchr", ] +[[package]] +name = "winnow" +version = "1.0.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0592e1c9d151f854e6fd382574c3a0855250e1d9b2f99d9281c6e6391af352f1" +dependencies = [ + "memchr", +] + [[package]] name = "wit-bindgen" version = "0.51.0" @@ -6883,6 +7801,17 @@ dependencies = [ "libc", ] +[[package]] +name = "xkbcommon" +version = "0.7.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "13867d259930edc7091a6c41b4ce6eee464328c6ff9659b7e4c668ca20d4c91e" +dependencies = [ + "libc", + "memmap2 0.8.0", + "xkeysym", +] + [[package]] name = "xkbcommon" version = "0.8.0" @@ -6890,7 +7819,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "8d66ca9352cbd4eecbbc40871d8a11b4ac8107cfc528a6e14d7c19c69d0e1ac9" dependencies = [ "libc", - "memmap2", + "memmap2 0.9.10", "xkeysym", ] @@ -6901,7 +7830,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "a7a974f48060a14e95705c01f24ad9c3345022f4d97441b8a36beb7ed5c4a02d" dependencies = [ "libc", - "memmap2", + "memmap2 0.9.10", "xkeysym", ] @@ -6976,9 +7905,9 @@ dependencies = [ [[package]] name = "zbus" -version = "5.14.0" +version = "5.16.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ca82f95dbd3943a40a53cfded6c2d0a2ca26192011846a1810c4256ef92c60bc" +checksum = "eee682d202a77e4a9f3b2c2bdf48a7b28af5c08c34ddf66f98c93e5e39464285" dependencies = [ "async-broadcast", "async-executor", @@ -6999,21 +7928,46 @@ dependencies = [ "rustix 1.1.4", "serde", "serde_repr", + "tokio", "tracing", "uds_windows", "uuid", "windows-sys 0.61.2", - "winnow 0.7.14", + "winnow 1.0.3", "zbus_macros", "zbus_names", "zvariant", ] [[package]] -name = "zbus_macros" -version = "5.14.0" +name = "zbus-lockstep" +version = "0.5.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "897e79616e84aac4b2c46e9132a4f63b93105d54fe8c0e8f6bffc21fa8d49222" +checksum = "6998de05217a084b7578728a9443d04ea4cd80f2a0839b8d78770b76ccd45863" +dependencies = [ + "zbus_xml", + "zvariant", +] + +[[package]] +name = "zbus-lockstep-macros" +version = "0.5.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "10da05367f3a7b7553c8cdf8fa91aee6b64afebe32b51c95177957efc47ca3a0" +dependencies = [ + "proc-macro2", + "quote", + "syn", + "zbus-lockstep", + "zbus_xml", + "zvariant", +] + +[[package]] +name = "zbus_macros" +version = "5.16.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "adf1bd45a81a103745b1757754762a26e8cd01e4532e4d6c8ec431624b80d1d6" dependencies = [ "proc-macro-crate", "proc-macro2", @@ -7026,12 +7980,24 @@ dependencies = [ [[package]] name = "zbus_names" -version = "4.3.1" +version = "4.3.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ffd8af6d5b78619bab301ff3c560a5bd22426150253db278f164d6cf3b72c50f" +checksum = "7074f3e50b894eac91750142016d30d0a89be8e67dbfd9704fb875825760e52d" dependencies = [ "serde", - "winnow 0.7.14", + "winnow 1.0.3", + "zvariant", +] + +[[package]] +name = "zbus_xml" +version = "5.1.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a8067892e940ed1727dea64690378601603b31d62dfde019a5335fbb7c0e0ed9" +dependencies = [ + "quick-xml", + "serde", + "zbus_names", "zvariant", ] @@ -7154,23 +8120,24 @@ dependencies = [ [[package]] name = "zvariant" -version = "5.10.0" +version = "5.12.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5708299b21903bbe348e94729f22c49c55d04720a004aa350f1f9c122fd2540b" +checksum = "a192a0bde63360d77a7523c833d4b4ce6070a927e2c53246e4c540b1a3e27be0" dependencies = [ "endi", "enumflags2", "serde", - "winnow 0.7.14", + "url", + "winnow 1.0.3", "zvariant_derive", "zvariant_utils", ] [[package]] name = "zvariant_derive" -version = "5.10.0" +version = "5.12.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5b59b012ebe9c46656f9cc08d8da8b4c726510aef12559da3e5f1bf72780752c" +checksum = "90bc6cde9c01c511074be97f7ccb6c19d0da89e3f8662e812e999dcfd4638737" dependencies = [ "proc-macro-crate", "proc-macro2", @@ -7181,13 +8148,17 @@ dependencies = [ [[package]] name = "zvariant_utils" -version = "3.3.0" +version = "3.4.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f75c23a64ef8f40f13a6989991e643554d9bef1d682a281160cf0c1bc389c5e9" +checksum = "1e8535915cfa75547e559d8c68e8139909a4aeee076831e4ef7fc59d8172c4d6" dependencies = [ "proc-macro2", "quote", "serde", "syn", - "winnow 0.7.14", + "winnow 1.0.3", ] + +[[patch.unused]] +name = "winit-x11" +version = "0.31.0-beta.2" diff --git a/rust-toolchain.toml b/rust-toolchain.toml index 9c8eecdc..ae16192b 100644 --- a/rust-toolchain.toml +++ b/rust-toolchain.toml @@ -1,3 +1,3 @@ [toolchain] -channel = "1.90" +channel = "1.93" components = [ "rust-src" ] From 012c77ec81f55f8d149639f77ea7254b43f9e20f Mon Sep 17 00:00:00 2001 From: Ericky Dos Santos Date: Mon, 29 Jun 2026 10:25:28 -0400 Subject: [PATCH 09/92] fix(kms): gate overlay scanout per-element by buffer GPU node --- src/backend/kms/surface/mod.rs | 35 +++++------ src/shell/element/surface.rs | 102 ++++++++++++++++++++++++--------- 2 files changed, 95 insertions(+), 42 deletions(-) diff --git a/src/backend/kms/surface/mod.rs b/src/backend/kms/surface/mod.rs index 62ba7c1f..fa196b87 100644 --- a/src/backend/kms/surface/mod.rs +++ b/src/backend/kms/surface/mod.rs @@ -8,7 +8,7 @@ use crate::{ init_shaders, output_elements, }, config::ScreenFilter, - shell::Shell, + shell::{Shell, element::surface::ScanoutTargetNodeGuard}, state::SurfaceDmabufFeedback, utils::prelude::*, wayland::handlers::{ @@ -1057,21 +1057,24 @@ impl SurfaceThreadState { vrr = has_active_fullscreen; } - let mut elements = output_elements( - Some(&render_node), - &mut renderer, - &self.shell, - self.clock.now(), - self.mirroring.as_ref().unwrap_or(&self.output), - CursorMode::All, - #[cfg(not(feature = "debug"))] - None, - #[cfg(feature = "debug")] - Some((&self.egui, &self.timings)), - ) - .map_err(|err| { - anyhow::format_err!("Failed to accumulate elements for rendering: {:?}", err) - })?; + let mut elements = { + let _scanout_node = ScanoutTargetNodeGuard::new(Some(self.target_node)); + output_elements( + Some(&render_node), + &mut renderer, + &self.shell, + self.clock.now(), + self.mirroring.as_ref().unwrap_or(&self.output), + CursorMode::All, + #[cfg(not(feature = "debug"))] + None, + #[cfg(feature = "debug")] + Some((&self.egui, &self.timings)), + ) + .map_err(|err| { + anyhow::format_err!("Failed to accumulate elements for rendering: {:?}", err) + })? + }; if vrr && fullscreen_drives_refresh_rate && !self.timings.past_min_render_time(&self.clock) { diff --git a/src/shell/element/surface.rs b/src/shell/element/surface.rs index 8495fe30..8b0a606e 100644 --- a/src/shell/element/surface.rs +++ b/src/shell/element/surface.rs @@ -6,6 +6,7 @@ use crate::{ }; use std::{ borrow::Cow, + cell::Cell, sync::{ Mutex, atomic::{AtomicBool, Ordering}, @@ -14,11 +15,17 @@ use std::{ }; use smithay::{ - backend::renderer::{ - ImportAll, Renderer, - element::{ - AsRenderElements, Kind, RenderElementStates, - surface::{WaylandSurfaceRenderElement, render_elements_from_surface_tree}, + backend::{ + drm::DrmNode, + renderer::{ + ImportAll, Renderer, + element::{ + AsRenderElements, Kind, RenderElementStates, + surface::{ + KindEvaluation, WaylandSurfaceRenderElement, render_elements_from_surface_tree, + }, + }, + utils::RendererSurfaceStateUserData, }, }, desktop::{ @@ -49,6 +56,7 @@ use smithay::{ SubsurfaceCachedState, SurfaceData, TraversalAction, get_parent, with_states, with_surface_tree_downward, }, + dmabuf::get_dmabuf, seat::WaylandFocus, shell::xdg::{ SurfaceCachedState, ToplevelCachedState, ToplevelSurface, XdgToplevelSurfaceData, @@ -67,6 +75,66 @@ use crate::{ }, }; +thread_local! { + /// The scan-out target [`DrmNode`] of the surface thread that is currently accumulating + /// render elements, if any. + static SCANOUT_TARGET_NODE: Cell> = const { Cell::new(None) }; +} + +/// RAII guard that sets the current thread's scan-out target node and restores the previous value +/// on drop. Hold it only across render-element accumulation so the gate can't outlive a render pass. +/// See [`SCANOUT_TARGET_NODE`]. +pub struct ScanoutTargetNodeGuard(Option); + +impl ScanoutTargetNodeGuard { + pub fn new(node: Option) -> Self { + ScanoutTargetNodeGuard(SCANOUT_TARGET_NODE.with(|n| n.replace(node))) + } +} + +impl Drop for ScanoutTargetNodeGuard { + fn drop(&mut self) { + SCANOUT_TARGET_NODE.with(|n| n.set(self.0)); + } +} + +/// The [`DrmNode`] the surface's currently committed buffer was allocated on, if it is a dmabuf. +fn buffer_node(data: &SurfaceData) -> Option { + let surface_state = data.data_map.get::()?; + let surface_state = surface_state.lock().unwrap(); + surface_state + .buffer() + .and_then(|buffer| get_dmabuf(buffer).ok()) + .and_then(|dmabuf| dmabuf.node()) +} + +/// Build the [`KindEvaluation`] for a window's surface tree. +fn scanout_kind_eval(scanout_override: Option) -> KindEvaluation { + match (scanout_override, SCANOUT_TARGET_NODE.with(|n| n.get())) { + // Forced off. + (Some(false), _) => Kind::Unspecified.into(), + // No node restriction: preserve the previous behaviour exactly. + (Some(true), None) => Kind::ScanoutCandidate.into(), + (None, None) => FRAME_TIME_FILTER, + // Node restriction in effect: only buffers on the scan-out node may be candidates. + (Some(true), Some(node)) => KindEvaluation::Closure(Box::new(move |data| { + if buffer_node(data) == Some(node) { + Kind::ScanoutCandidate + } else { + Kind::Unspecified + } + })), + (None, Some(node)) => KindEvaluation::Closure(Box::new(move |data| { + if buffer_node(data) == Some(node) + { + frame_time_filter_fn(data) + } else { + Kind::Unspecified + } + })), + } +} + #[derive(Debug, Clone, PartialEq, Hash, Eq)] pub struct CosmicSurface(pub Window); @@ -836,7 +904,7 @@ impl CosmicSurface { location + offset, scale, alpha, - FRAME_TIME_FILTER, + scanout_kind_eval(None), ) }) .collect() @@ -868,16 +936,7 @@ impl CosmicSurface { location, scale, alpha, - scanout_override - .map(|val| { - if val { - Kind::ScanoutCandidate - } else { - Kind::Unspecified - } - .into() - }) - .unwrap_or(FRAME_TIME_FILTER), + scanout_kind_eval(scanout_override), ) } WindowSurface::X11(surface) => { @@ -891,16 +950,7 @@ impl CosmicSurface { location, scale, alpha, - scanout_override - .map(|val| { - if val { - Kind::ScanoutCandidate - } else { - Kind::Unspecified - } - .into() - }) - .unwrap_or(FRAME_TIME_FILTER), + scanout_kind_eval(scanout_override), ) } } From 031fdc389aaa4dac8546e0fc429bbea2295cc00d Mon Sep 17 00:00:00 2001 From: Ericky Dos Santos Date: Wed, 1 Jul 2026 11:05:56 -0400 Subject: [PATCH 10/92] kms: change from thread_local approach to wiring scanout node through function calls to detect scanout candidates --- src/backend/kms/device.rs | 1 + src/backend/kms/mod.rs | 3 +++ src/backend/kms/surface/mod.rs | 36 ++++++++++++------------- src/backend/render/mod.rs | 20 ++++++++++++-- src/shell/element/mod.rs | 17 ++++++++++-- src/shell/element/stack.rs | 10 ++++++- src/shell/element/surface.rs | 46 +++++++++++--------------------- src/shell/element/window.rs | 10 ++++++- src/shell/grabs/moving.rs | 11 +++++++- src/shell/layout/floating/mod.rs | 13 ++++++--- src/shell/layout/tiling/mod.rs | 31 ++++++++++++++++----- src/shell/workspace.rs | 10 ++++++- 12 files changed, 140 insertions(+), 68 deletions(-) diff --git a/src/backend/kms/device.rs b/src/backend/kms/device.rs index 2b0e8f9d..44682dc5 100644 --- a/src/backend/kms/device.rs +++ b/src/backend/kms/device.rs @@ -943,6 +943,7 @@ impl LockedDevice<'_> { output, CursorMode::All, None, + None, ) .with_context(|| "Failed to render outputs")?, None => Vec::new(), diff --git a/src/backend/kms/mod.rs b/src/backend/kms/mod.rs index 13832da8..16559e4e 100644 --- a/src/backend/kms/mod.rs +++ b/src/backend/kms/mod.rs @@ -978,6 +978,7 @@ impl KmsGuard<'_> { output, CursorMode::All, None, + None, ) .with_context(|| "Failed to render outputs")?; @@ -1088,6 +1089,7 @@ impl KmsGuard<'_> { output, CursorMode::All, None, + None, ) .with_context(|| "Failed to render outputs")?; @@ -1132,6 +1134,7 @@ impl KmsGuard<'_> { output, CursorMode::All, None, + None, ) .with_context(|| "Failed to render outputs")?; diff --git a/src/backend/kms/surface/mod.rs b/src/backend/kms/surface/mod.rs index fa196b87..a277af2e 100644 --- a/src/backend/kms/surface/mod.rs +++ b/src/backend/kms/surface/mod.rs @@ -8,7 +8,7 @@ use crate::{ init_shaders, output_elements, }, config::ScreenFilter, - shell::{Shell, element::surface::ScanoutTargetNodeGuard}, + shell::Shell, state::SurfaceDmabufFeedback, utils::prelude::*, wayland::handlers::{ @@ -1057,24 +1057,22 @@ impl SurfaceThreadState { vrr = has_active_fullscreen; } - let mut elements = { - let _scanout_node = ScanoutTargetNodeGuard::new(Some(self.target_node)); - output_elements( - Some(&render_node), - &mut renderer, - &self.shell, - self.clock.now(), - self.mirroring.as_ref().unwrap_or(&self.output), - CursorMode::All, - #[cfg(not(feature = "debug"))] - None, - #[cfg(feature = "debug")] - Some((&self.egui, &self.timings)), - ) - .map_err(|err| { - anyhow::format_err!("Failed to accumulate elements for rendering: {:?}", err) - })? - }; + let mut elements = output_elements( + Some(&render_node), + &mut renderer, + &self.shell, + self.clock.now(), + self.mirroring.as_ref().unwrap_or(&self.output), + CursorMode::All, + #[cfg(not(feature = "debug"))] + None, + #[cfg(feature = "debug")] + Some((&self.egui, &self.timings)), + Some(self.target_node), + ) + .map_err(|err| { + anyhow::format_err!("Failed to accumulate elements for rendering: {:?}", err) + })?; if vrr && fullscreen_drives_refresh_rate && !self.timings.past_min_render_time(&self.clock) { diff --git a/src/backend/render/mod.rs b/src/backend/render/mod.rs index bfbb21a3..d847f533 100644 --- a/src/backend/render/mod.rs +++ b/src/backend/render/mod.rs @@ -475,6 +475,7 @@ pub fn cursor_elements<'a, 'frame, R>( output: &Output, mode: CursorMode, exclude_dnd_icon: bool, + scanout_node: Option, ) -> Vec> where R: AsGlowRenderer, @@ -549,7 +550,14 @@ where .lock() .unwrap() .as_ref() - .map(|state| state.render::, R>(renderer, output, theme)) + .map(|state| { + state.render::, R>( + renderer, + output, + theme, + scanout_node, + ) + }) { elements.extend(grab_elements.into_iter().map(|elem| { CosmicElement::MoveGrab(RescaleRenderElement::from_element( @@ -615,6 +623,7 @@ pub fn output_elements( output: &Output, cursor_mode: CursorMode, _fps: Option<(&EguiState, &Timings)>, + scanout_node: Option, ) -> Result>, RenderError> where R: AsGlowRenderer, @@ -690,6 +699,7 @@ where workspace, cursor_mode, element_filter, + scanout_node, )?; #[cfg(feature = "debug")] @@ -713,6 +723,7 @@ pub fn workspace_elements( current: (WorkspaceHandle, usize), cursor_mode: CursorMode, element_filter: ElementFilter, + scanout_node: Option, ) -> Result>, RenderError> where R: AsGlowRenderer, @@ -743,6 +754,7 @@ where output, cursor_mode, element_filter == ElementFilter::ExcludeWorkspaceOverview, + scanout_node, )); let shell = shell.read(); @@ -907,7 +919,7 @@ where elements.extend( layout - .render_popups(renderer, alpha) + .render_popups(renderer, alpha, scanout_node) .into_iter() .map(Into::into) .flat_map(crop_to_output) @@ -951,6 +963,7 @@ where active_hint, alpha, theme.cosmic(), + scanout_node, ) .into_iter() .map(Into::into) @@ -966,6 +979,7 @@ where !move_active && is_active_space, overview.clone(), theme.cosmic(), + scanout_node, ) { Ok(elements) => { elements @@ -995,6 +1009,7 @@ where resize_indicator.clone(), active_hint, theme.cosmic(), + scanout_node, ) { Ok(elements) => { elements @@ -1511,6 +1526,7 @@ where current, cursor_mode, element_filter, + None, )?; if let Some(additional_damage) = additional_damage { diff --git a/src/shell/element/mod.rs b/src/shell/element/mod.rs index f58259bf..8639aeb6 100644 --- a/src/shell/element/mod.rs +++ b/src/shell/element/mod.rs @@ -8,6 +8,7 @@ use cosmic_comp_config::AppearanceConfig; use id_tree::NodeId; use smithay::{ backend::{ + drm::DrmNode, input::KeyState, renderer::{ element::{ @@ -589,6 +590,7 @@ impl CosmicMapped { location: smithay::utils::Point, scale: smithay::utils::Scale, alpha: f32, + scanout_node: Option, ) -> Vec where R: AsGlowRenderer, @@ -599,11 +601,19 @@ impl CosmicMapped { match &self.element { CosmicMappedInternal::Stack(s) => s .popup_render_elements::>( - renderer, location, scale, alpha, + renderer, + location, + scale, + alpha, + scanout_node, ), CosmicMappedInternal::Window(w) => w .popup_render_elements::>( - renderer, location, scale, alpha, + renderer, + location, + scale, + alpha, + scanout_node, ), _ => unreachable!(), } @@ -664,6 +674,7 @@ impl CosmicMapped { scale: smithay::utils::Scale, alpha: f32, scanout_override: Option, + scanout_node: Option, ) -> Vec where R: AsGlowRenderer, @@ -854,6 +865,7 @@ impl CosmicMapped { scale, alpha, scanout_override, + scanout_node, ), CosmicMappedInternal::Window(w) => w .render_elements::>( @@ -863,6 +875,7 @@ impl CosmicMapped { scale, alpha, scanout_override, + scanout_node, ), _ => unreachable!(), }); diff --git a/src/shell/element/stack.rs b/src/shell/element/stack.rs index 9f8584f5..17df320f 100644 --- a/src/shell/element/stack.rs +++ b/src/shell/element/stack.rs @@ -37,6 +37,7 @@ use cosmic_settings_config::shortcuts; use shortcuts::action::{Direction, FocusDirection}; use smithay::{ backend::{ + drm::DrmNode, input::KeyState, renderer::{ ImportAll, ImportMem, Renderer, @@ -654,6 +655,7 @@ impl CosmicStack { location: Point, scale: Scale, alpha: f32, + scanout_node: Option, ) -> Vec where R: AsGlowRenderer, @@ -667,7 +669,11 @@ impl CosmicStack { windows[active] .popup_render_elements::>( - renderer, window_loc, scale, alpha, + renderer, + window_loc, + scale, + alpha, + scanout_node, ) .into_iter() .map(C::from) @@ -750,6 +756,7 @@ impl CosmicStack { scale: Scale, alpha: f32, scanout_override: Option, + scanout_node: Option, ) -> Vec where R: AsGlowRenderer, @@ -823,6 +830,7 @@ impl CosmicStack { scale, alpha, scanout_override, + scanout_node, ) .into_iter() .map(move |elem| { diff --git a/src/shell/element/surface.rs b/src/shell/element/surface.rs index 8b0a606e..2bd72f25 100644 --- a/src/shell/element/surface.rs +++ b/src/shell/element/surface.rs @@ -6,7 +6,6 @@ use crate::{ }; use std::{ borrow::Cow, - cell::Cell, sync::{ Mutex, atomic::{AtomicBool, Ordering}, @@ -75,29 +74,6 @@ use crate::{ }, }; -thread_local! { - /// The scan-out target [`DrmNode`] of the surface thread that is currently accumulating - /// render elements, if any. - static SCANOUT_TARGET_NODE: Cell> = const { Cell::new(None) }; -} - -/// RAII guard that sets the current thread's scan-out target node and restores the previous value -/// on drop. Hold it only across render-element accumulation so the gate can't outlive a render pass. -/// See [`SCANOUT_TARGET_NODE`]. -pub struct ScanoutTargetNodeGuard(Option); - -impl ScanoutTargetNodeGuard { - pub fn new(node: Option) -> Self { - ScanoutTargetNodeGuard(SCANOUT_TARGET_NODE.with(|n| n.replace(node))) - } -} - -impl Drop for ScanoutTargetNodeGuard { - fn drop(&mut self) { - SCANOUT_TARGET_NODE.with(|n| n.set(self.0)); - } -} - /// The [`DrmNode`] the surface's currently committed buffer was allocated on, if it is a dmabuf. fn buffer_node(data: &SurfaceData) -> Option { let surface_state = data.data_map.get::()?; @@ -109,8 +85,15 @@ fn buffer_node(data: &SurfaceData) -> Option { } /// Build the [`KindEvaluation`] for a window's surface tree. -fn scanout_kind_eval(scanout_override: Option) -> KindEvaluation { - match (scanout_override, SCANOUT_TARGET_NODE.with(|n| n.get())) { +/// +/// `scanout_node`, when set, is the scan-out target [`DrmNode`] of the output currently being +/// rendered: only buffers allocated on that node may be promoted to overlay scan-out candidates. +/// It is `None` for render passes that never scan out to a plane (e.g. screen-copy). +fn scanout_kind_eval( + scanout_override: Option, + scanout_node: Option, +) -> KindEvaluation { + match (scanout_override, scanout_node) { // Forced off. (Some(false), _) => Kind::Unspecified.into(), // No node restriction: preserve the previous behaviour exactly. @@ -125,8 +108,7 @@ fn scanout_kind_eval(scanout_override: Option) -> KindEvaluation { } })), (None, Some(node)) => KindEvaluation::Closure(Box::new(move |data| { - if buffer_node(data) == Some(node) - { + if buffer_node(data) == Some(node) { frame_time_filter_fn(data) } else { Kind::Unspecified @@ -884,6 +866,7 @@ impl CosmicSurface { location: Point, scale: Scale, alpha: f32, + scanout_node: Option, ) -> Vec where R: Renderer + ImportAll, @@ -904,7 +887,7 @@ impl CosmicSurface { location + offset, scale, alpha, - scanout_kind_eval(None), + scanout_kind_eval(None, scanout_node), ) }) .collect() @@ -920,6 +903,7 @@ impl CosmicSurface { scale: Scale, alpha: f32, scanout_override: Option, + scanout_node: Option, ) -> Vec where R: Renderer + ImportAll, @@ -936,7 +920,7 @@ impl CosmicSurface { location, scale, alpha, - scanout_kind_eval(scanout_override), + scanout_kind_eval(scanout_override, scanout_node), ) } WindowSurface::X11(surface) => { @@ -950,7 +934,7 @@ impl CosmicSurface { location, scale, alpha, - scanout_kind_eval(scanout_override), + scanout_kind_eval(scanout_override, scanout_node), ) } } diff --git a/src/shell/element/window.rs b/src/shell/element/window.rs index 0adbfab7..52ffd16d 100644 --- a/src/shell/element/window.rs +++ b/src/shell/element/window.rs @@ -23,6 +23,7 @@ use cosmic::iced::{Color, Task}; use cosmic_comp_config::AppearanceConfig; use smithay::{ backend::{ + drm::DrmNode, input::KeyState, renderer::{ ImportAll, ImportMem, Renderer, @@ -357,6 +358,7 @@ impl CosmicWindow { location: Point, scale: Scale, alpha: f32, + scanout_node: Option, ) -> Vec where R: Renderer + ImportAll + ImportMem, @@ -374,7 +376,11 @@ impl CosmicWindow { self.0.with_program(|p| { p.window .popup_render_elements::>( - renderer, window_loc, scale, alpha, + renderer, + window_loc, + scale, + alpha, + scanout_node, ) .into_iter() .map(C::from) @@ -470,6 +476,7 @@ impl CosmicWindow { scale: Scale, alpha: f32, scanout_override: Option, + scanout_node: Option, ) -> Vec where R: AsGlowRenderer, @@ -553,6 +560,7 @@ impl CosmicWindow { scale, alpha, scanout_override, + scanout_node, ) }); if window_elements.is_empty() { diff --git a/src/shell/grabs/moving.rs b/src/shell/grabs/moving.rs index e73b108d..1fd8a9b6 100644 --- a/src/shell/grabs/moving.rs +++ b/src/shell/grabs/moving.rs @@ -21,6 +21,7 @@ use calloop::LoopHandle; use cosmic::theme::CosmicTheme; use smithay::{ backend::{ + drm::DrmNode, input::ButtonState, renderer::{ ImportAll, ImportMem, Renderer, @@ -68,7 +69,13 @@ pub struct MoveGrabState { impl MoveGrabState { #[profiling::function] - pub fn render(&self, renderer: &mut R, output: &Output, theme: &CosmicTheme) -> Vec + pub fn render( + &self, + renderer: &mut R, + output: &Output, + theme: &CosmicTheme, + scanout_node: Option, + ) -> Vec where R: Renderer + ImportAll + ImportMem + AsGlowRenderer, R::TextureId: Send + Clone + 'static, @@ -198,6 +205,7 @@ impl MoveGrabState { output_scale, alpha, Some(false), + scanout_node, ); let p_elements = self .window @@ -207,6 +215,7 @@ impl MoveGrabState { .to_physical_precise_round(output_scale), output_scale, alpha, + scanout_node, ); let shadow_element = self.window.shadow_render_element( renderer, diff --git a/src/shell/layout/floating/mod.rs b/src/shell/layout/floating/mod.rs index e3829e58..84677188 100644 --- a/src/shell/layout/floating/mod.rs +++ b/src/shell/layout/floating/mod.rs @@ -10,9 +10,12 @@ use cosmic_comp_config::AppearanceConfig; use cosmic_settings_config::shortcuts::action::ResizeDirection; use keyframe::{ease, functions::EaseInOutCubic}; use smithay::{ - backend::renderer::element::{ - AsRenderElements, RenderElement, - utils::{Relocate, RelocateRenderElement, RescaleRenderElement}, + backend::{ + drm::DrmNode, + renderer::element::{ + AsRenderElements, RenderElement, + utils::{Relocate, RelocateRenderElement, RescaleRenderElement}, + }, }, desktop::{PopupKind, Space, WindowSurfaceType, layer_map_for_output, space::SpaceElement}, input::Seat, @@ -1439,6 +1442,7 @@ impl FloatingLayout { &self, renderer: &mut R, alpha: f32, + scanout_node: Option, ) -> Vec> where R: AsGlowRenderer, @@ -1474,6 +1478,7 @@ impl FloatingLayout { .to_physical_precise_round(output_scale), output_scale.into(), alpha, + scanout_node, ), ); } @@ -1490,6 +1495,7 @@ impl FloatingLayout { indicator_thickness: u8, alpha: f32, theme: &cosmic::theme::CosmicTheme, + scanout_node: Option, ) -> Vec> where R: AsGlowRenderer, @@ -1530,6 +1536,7 @@ impl FloatingLayout { output_scale.into(), alpha, None, + scanout_node, ); window_elements.extend( elem.shadow_render_element( diff --git a/src/shell/layout/tiling/mod.rs b/src/shell/layout/tiling/mod.rs index e7a7f0f1..45ca985e 100644 --- a/src/shell/layout/tiling/mod.rs +++ b/src/shell/layout/tiling/mod.rs @@ -46,15 +46,18 @@ use keyframe::{ functions::{EaseInOutCubic, Linear}, }; use smithay::{ - backend::renderer::{ - element::{ - AsRenderElements, Id, RenderElement, - utils::{ - ConstrainAlign, ConstrainScaleBehavior, RescaleRenderElement, - constrain_render_elements, + backend::{ + drm::DrmNode, + renderer::{ + element::{ + AsRenderElements, Id, RenderElement, + utils::{ + ConstrainAlign, ConstrainScaleBehavior, RescaleRenderElement, + constrain_render_elements, + }, }, + glow::GlowRenderer, }, - glow::GlowRenderer, }, desktop::{PopupKind, WindowSurfaceType, layer_map_for_output, space::SpaceElement}, input::Seat, @@ -4007,6 +4010,7 @@ impl TilingLayout { resize_indicator: Option<(ResizeMode, ResizeIndicator)>, indicator_thickness: u8, theme: &cosmic::theme::CosmicTheme, + scanout_node: Option, ) -> Result>, OutputNotMapped> where R: AsGlowRenderer, @@ -4093,6 +4097,7 @@ impl TilingLayout { indicator_thickness, swap_desc.is_some(), theme, + scanout_node, )); geometries @@ -4149,6 +4154,7 @@ impl TilingLayout { &self.swapping_stack_surface_id, &self.backdrop_id, theme, + scanout_node, )); // tiling hints @@ -4167,6 +4173,7 @@ impl TilingLayout { non_exclusive_zone: Rectangle, overview: (OverviewMode, Option<(SwapIndicator, Option<&Tree>)>), theme: &cosmic::theme::CosmicTheme, + scanout_node: Option, ) -> Result>, OutputNotMapped> where R: AsGlowRenderer, @@ -4242,6 +4249,7 @@ impl TilingLayout { output_scale, percentage, swap_desc.is_some(), + scanout_node, )); geometries @@ -4281,6 +4289,7 @@ impl TilingLayout { percentage, overview, swap_desc.clone(), + scanout_node, )); Ok(elements) @@ -4973,6 +4982,7 @@ fn render_old_tree_popups( output_scale: f64, percentage: f32, is_swap_mode: bool, + scanout_node: Option, ) -> Vec> where R: AsGlowRenderer, @@ -4998,6 +5008,7 @@ where - elem_geometry.loc, Scale::from(output_scale), alpha, + scanout_node, ), ); }, @@ -5016,6 +5027,7 @@ fn render_old_tree_windows( indicator_thickness: u8, is_swap_mode: bool, theme: &cosmic::theme::CosmicTheme, + scanout_node: Option, ) -> Vec> where R: AsGlowRenderer, @@ -5052,6 +5064,7 @@ where Scale::from(output_scale), alpha, None, + scanout_node, ); elements.extend(window_elements.into_iter().flat_map(|element| { @@ -5196,6 +5209,7 @@ fn render_new_tree_popups( percentage: f32, overview: (OverviewMode, Option<(SwapIndicator, Option<&Tree>)>), swap_desc: Option, + scanout_node: Option, ) -> Vec> where R: AsGlowRenderer, @@ -5234,6 +5248,7 @@ where - elem_geometry.loc, Scale::from(output_scale), alpha, + scanout_node, ), ); } @@ -5262,6 +5277,7 @@ fn render_new_tree_windows( swapping_stack_surface_id: &Id, backdrop_id: &Id, theme: &cosmic::theme::CosmicTheme, + scanout_node: Option, ) -> Vec> where R: AsGlowRenderer, @@ -5597,6 +5613,7 @@ where Scale::from(output_scale), alpha, None, + scanout_node, ); if swap_desc diff --git a/src/shell/workspace.rs b/src/shell/workspace.rs index 2b4db98c..f4e3dcab 100644 --- a/src/shell/workspace.rs +++ b/src/shell/workspace.rs @@ -31,6 +31,7 @@ use cosmic_protocols::workspace::v2::server::zcosmic_workspace_handle_v2::Tiling use id_tree::Tree; use indexmap::IndexSet; use keyframe::{ease, functions::EaseInOutCubic}; +use smithay::backend::drm::DrmNode; use smithay::backend::renderer::element::Kind; use smithay::output::WeakOutput; use smithay::utils::user_data::UserDataMap; @@ -1612,6 +1613,7 @@ impl Workspace { resize_indicator: Option<(ResizeMode, ResizeIndicator)>, indicator_thickness: u8, theme: &CosmicTheme, + scanout_node: Option, ) -> Result>, OutputNotMapped> where R: AsGlowRenderer, @@ -1700,6 +1702,7 @@ impl Workspace { output_scale.into(), alpha, Some(true), + scanout_node, ) .into_iter() .map(animation_rescale) @@ -1772,6 +1775,7 @@ impl Workspace { indicator_thickness, alpha, theme, + scanout_node, ) .into_iter() .map(WorkspaceRenderElement::from), @@ -1801,6 +1805,7 @@ impl Workspace { resize_indicator, indicator_thickness, theme, + scanout_node, )? .into_iter() .map(WorkspaceRenderElement::from), @@ -1836,6 +1841,7 @@ impl Workspace { render_focus: bool, overview: (OverviewMode, Option<(SwapIndicator, Option<&Tree>)>), theme: &CosmicTheme, + scanout_node: Option, ) -> Result>, OutputNotMapped> where R: AsGlowRenderer, @@ -1909,6 +1915,7 @@ impl Workspace { render_loc, output_scale.into(), alpha, + scanout_node, ) .into_iter() .map(Into::into), @@ -1947,7 +1954,7 @@ impl Workspace { elements.extend( self.floating_layer - .render_popups::(renderer, alpha) + .render_popups::(renderer, alpha, scanout_node) .into_iter() .map(WorkspaceRenderElement::from), ); @@ -1961,6 +1968,7 @@ impl Workspace { zone, overview, theme, + scanout_node, )? .into_iter() .map(WorkspaceRenderElement::from), From 29320bdab5d8ae651bd6c445368aff27745c63c2 Mon Sep 17 00:00:00 2001 From: Ian Douglas Scott Date: Mon, 29 Jul 2024 13:56:42 -0700 Subject: [PATCH 11/92] Add `cosmic-keyboard-layout-unstable-v1` protocol https://github.com/pop-os/cosmic-protocols/pull/28 Allows clients like `cosmic-applet-input-sources` to show which layout group is active and update it. Currently this is implemented by update the `xkb_config` to change the layout order. --- Cargo.lock | 20 +-- src/lib.rs | 5 +- src/state.rs | 4 + src/wayland/handlers/keyboard_layout.rs | 10 ++ src/wayland/handlers/mod.rs | 1 + src/wayland/protocols/keyboard_layout.rs | 185 +++++++++++++++++++++++ src/wayland/protocols/mod.rs | 1 + 7 files changed, 215 insertions(+), 11 deletions(-) create mode 100644 src/wayland/handlers/keyboard_layout.rs create mode 100644 src/wayland/protocols/keyboard_layout.rs diff --git a/Cargo.lock b/Cargo.lock index 1bc58958..9f664669 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -788,7 +788,7 @@ dependencies = [ [[package]] name = "cosmic-client-toolkit" version = "0.2.0" -source = "git+https://github.com/pop-os//cosmic-protocols?branch=main#160b086abe03cd34a8a375d7fbe47b24308d1f38" +source = "git+https://github.com/pop-os//cosmic-protocols?branch=main#0d0b0b573fe4836ede4c237dadd365033cd28296" dependencies = [ "bitflags 2.11.0", "cosmic-protocols", @@ -918,7 +918,7 @@ dependencies = [ [[package]] name = "cosmic-protocols" version = "0.2.0" -source = "git+https://github.com/pop-os//cosmic-protocols?branch=main#160b086abe03cd34a8a375d7fbe47b24308d1f38" +source = "git+https://github.com/pop-os//cosmic-protocols?branch=main#0d0b0b573fe4836ede4c237dadd365033cd28296" dependencies = [ "bitflags 2.11.0", "wayland-backend", @@ -1204,7 +1204,7 @@ dependencies = [ "libc", "option-ext", "redox_users", - "windows-sys 0.61.2", + "windows-sys 0.59.0", ] [[package]] @@ -1518,7 +1518,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "39cab71617ae0d63f51a36d69f866391735b51691dbda63cf6f96d042b63efeb" dependencies = [ "libc", - "windows-sys 0.61.2", + "windows-sys 0.52.0", ] [[package]] @@ -2688,7 +2688,7 @@ dependencies = [ "portable-atomic", "portable-atomic-util", "serde_core", - "windows-sys 0.61.2", + "windows-sys 0.52.0", ] [[package]] @@ -2811,7 +2811,7 @@ version = "1.4.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "770919970f7d2f74fea948900d35e2ef64f44129e8ae4015f59de1f0aca7c2a5" dependencies = [ - "windows-sys 0.61.2", + "windows-sys 0.59.0", ] [[package]] @@ -3421,7 +3421,7 @@ version = "0.50.3" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "7957b9740744892f114936ab4a57b3f487491bbeafaf8083688b16841a4240e5" dependencies = [ - "windows-sys 0.61.2", + "windows-sys 0.59.0", ] [[package]] @@ -4502,7 +4502,7 @@ dependencies = [ "errno", "libc", "linux-raw-sys 0.12.1", - "windows-sys 0.61.2", + "windows-sys 0.52.0", ] [[package]] @@ -5094,7 +5094,7 @@ dependencies = [ "getrandom 0.4.1", "once_cell", "rustix 1.1.4", - "windows-sys 0.61.2", + "windows-sys 0.52.0", ] [[package]] @@ -6113,7 +6113,7 @@ version = "0.1.11" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "c2a7b1c03c876122aa43f3020e6c3c3ee5c05081c9a00739faf7503aeba10d22" dependencies = [ - "windows-sys 0.61.2", + "windows-sys 0.48.0", ] [[package]] diff --git a/src/lib.rs b/src/lib.rs index 09d1b961..4f4e5f83 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -26,7 +26,9 @@ use std::{ time::{Duration, Instant}, }; use tracing::{error, info, warn}; -use wayland::protocols::overlap_notify::OverlapNotifyState; +use wayland::protocols::{ + keyboard_layout::KeyboardLayoutState, overlap_notify::OverlapNotifyState, +}; use crate::wayland::handlers::compositor::client_compositor_state; @@ -331,5 +333,6 @@ fn refresh(state: &mut State) { state::Common::refresh_focus(state); OverlapNotifyState::refresh(state); state.common.update_x11_stacking_order(); + KeyboardLayoutState::refresh(state); state.last_refresh = LastRefresh::At(Instant::now()); } diff --git a/src/state.rs b/src/state.rs index 18de36ba..68824ea7 100644 --- a/src/state.rs +++ b/src/state.rs @@ -19,6 +19,7 @@ use crate::{ corner_radius::CornerRadiusState, drm::WlDrmState, image_capture_source::CosmicImageCaptureSourceState, + keyboard_layout::KeyboardLayoutState, output_configuration::OutputConfigurationState, output_power::OutputPowerState, overlap_notify::OverlapNotifyState, @@ -279,6 +280,7 @@ pub struct Common { pub overlap_notify_state: OverlapNotifyState, pub a11y_state: A11yState, pub dbus_state: DBusState, + pub keyboard_layout_state: KeyboardLayoutState, // shell-related wayland state pub xdg_shell_state: XdgShellState, @@ -682,6 +684,7 @@ impl State { AlphaModifierState::new::(dh); SinglePixelBufferState::new::(dh); FixesState::new::(dh); + let keyboard_layout_state = KeyboardLayoutState::new::(&dh, client_not_sandboxed); let idle_notifier_state = IdleNotifierState::::new(dh, handle.clone()); let idle_inhibit_manager_state = IdleInhibitManagerState::new::(dh); @@ -792,6 +795,7 @@ impl State { xwayland_shell_state, pointer_focus_state: None, dbus_state, + keyboard_layout_state, #[cfg(feature = "logind")] inhibit_lid_fd: None, diff --git a/src/wayland/handlers/keyboard_layout.rs b/src/wayland/handlers/keyboard_layout.rs new file mode 100644 index 00000000..ac1fda87 --- /dev/null +++ b/src/wayland/handlers/keyboard_layout.rs @@ -0,0 +1,10 @@ +// SPDX-License-Identifier: GPL-3.0-only + +use crate::state::State; +use crate::wayland::protocols::keyboard_layout::{KeyboardLayoutHandler, KeyboardLayoutState}; + +impl KeyboardLayoutHandler for State { + fn keyboard_layout_state(&mut self) -> &mut KeyboardLayoutState { + &mut self.common.keyboard_layout_state + } +} diff --git a/src/wayland/handlers/mod.rs b/src/wayland/handlers/mod.rs index 34d063d0..c6461a2e 100644 --- a/src/wayland/handlers/mod.rs +++ b/src/wayland/handlers/mod.rs @@ -18,6 +18,7 @@ pub mod idle_notify; pub mod image_capture_source; pub mod image_copy_capture; pub mod input_method; +pub mod keyboard_layout; pub mod keyboard_shortcuts_inhibit; pub mod layer_shell; pub mod output; diff --git a/src/wayland/protocols/keyboard_layout.rs b/src/wayland/protocols/keyboard_layout.rs new file mode 100644 index 00000000..0d674f88 --- /dev/null +++ b/src/wayland/protocols/keyboard_layout.rs @@ -0,0 +1,185 @@ +// SPDX-License-Identifier: GPL-3.0-only + +use cosmic_protocols::keyboard_layout::v1::server::{ + zcosmic_keyboard_layout_manager_v1::{self, ZcosmicKeyboardLayoutManagerV1}, + zcosmic_keyboard_layout_v1::{self, ZcosmicKeyboardLayoutV1}, +}; +use smithay::{ + input::{ + SeatHandler, + keyboard::{KeyboardHandle, Layout}, + }, + reexports::wayland_server::{ + Client, DataInit, Dispatch, DisplayHandle, GlobalDispatch, New, Resource, + }, + wayland::{Dispatch2, GlobalDispatch2}, +}; +use std::mem; +use wayland_backend::server::{ClientId, GlobalId}; + +pub trait KeyboardLayoutHandler { + fn keyboard_layout_state(&mut self) -> &mut KeyboardLayoutState; +} + +#[derive(Debug)] +pub struct KeyboardLayoutState { + pub global: GlobalId, + keyboard_layouts: Vec<(ZcosmicKeyboardLayoutV1, Option)>, +} + +impl KeyboardLayoutState { + pub fn new(dh: &DisplayHandle, client_filter: F) -> Self + where + D: GlobalDispatch + 'static, + F: for<'a> Fn(&'a Client) -> bool + Send + Sync + 'static, + { + let global = dh.create_global::( + 1, + LayoutGlobalData { + filter: Box::new(client_filter), + }, + ); + KeyboardLayoutState { + global, + keyboard_layouts: Vec::new(), + } + } + + pub fn refresh(state: &mut D) + where + D: SeatHandler + KeyboardLayoutHandler + 'static, + { + let mut keyboard_layouts = mem::take(&mut state.keyboard_layout_state().keyboard_layouts); + for (keyboard_layout, last_layout) in &mut keyboard_layouts { + if let Some(data) = keyboard_layout.data::>() { + if let Some(handle) = &data.handle { + let active_layout = handle.with_xkb_state(state, |context| { + context.xkb().lock().unwrap().active_layout() + }); + if *last_layout != Some(active_layout) { + keyboard_layout.group(active_layout.0); + *last_layout = Some(active_layout); + } + } + } + } + state.keyboard_layout_state().keyboard_layouts = keyboard_layouts; + } +} + +pub struct LayoutGlobalData { + filter: Box Fn(&'a Client) -> bool + Send + Sync>, +} + +struct LayoutManagerData; + +impl GlobalDispatch2 for LayoutGlobalData +where + D: Dispatch + 'static, +{ + fn bind( + &self, + _state: &mut D, + _handle: &DisplayHandle, + _client: &Client, + resource: New, + data_init: &mut DataInit<'_, D>, + ) { + data_init.init(resource, LayoutManagerData); + } + + fn can_view(&self, client: &Client) -> bool { + (self.filter)(client) + } +} + +impl Dispatch2 for LayoutManagerData +where + D: Dispatch>, + D: 'static, + D: SeatHandler, + D: KeyboardLayoutHandler, +{ + fn request( + &self, + state: &mut D, + _client: &Client, + _resource: &ZcosmicKeyboardLayoutManagerV1, + request: zcosmic_keyboard_layout_manager_v1::Request, + _dhandle: &DisplayHandle, + data_init: &mut DataInit<'_, D>, + ) { + match request { + zcosmic_keyboard_layout_manager_v1::Request::GetKeyboardLayout { + keyboard_layout, + keyboard, + } => { + let handle = KeyboardHandle::::from_resource(&keyboard); + let active_layout = handle.as_ref().map(|handle| { + handle.with_xkb_state(state, |context| { + context.xkb().lock().unwrap().active_layout() + }) + }); + let keyboard_layout = data_init.init(keyboard_layout, LayoutUserData { handle }); + if let Some(layout) = active_layout { + keyboard_layout.group(layout.0); + } + state + .keyboard_layout_state() + .keyboard_layouts + .push((keyboard_layout, active_layout)); + } + zcosmic_keyboard_layout_manager_v1::Request::Destroy => {} + _ => unreachable!(), + } + } +} + +#[doc(hidden)] +struct LayoutUserData { + handle: Option>, +} + +impl Dispatch2 for LayoutUserData +where + D: 'static, + D: SeatHandler, + D: KeyboardLayoutHandler, +{ + fn request( + &self, + state: &mut D, + _client: &Client, + _resource: &ZcosmicKeyboardLayoutV1, + request: zcosmic_keyboard_layout_v1::Request, + _dhandle: &DisplayHandle, + _data_init: &mut DataInit<'_, D>, + ) { + match request { + zcosmic_keyboard_layout_v1::Request::SetGroup { group } => { + if let Some(handle) = self.handle.as_ref() { + handle.with_xkb_state(state, |mut context| { + context.set_layout(Layout(group)); + }); + } + } + zcosmic_keyboard_layout_v1::Request::Destroy => {} + _ => unreachable!(), + } + } + + fn destroyed( + &self, + state: &mut D, + _client: ClientId, + keyboard_layout: &ZcosmicKeyboardLayoutV1, + ) { + let keyboard_layouts = &mut state.keyboard_layout_state().keyboard_layouts; + if let Some(idx) = keyboard_layouts + .iter() + .position(|(x, _)| x == keyboard_layout) + { + keyboard_layouts.remove(idx); + } + } +} diff --git a/src/wayland/protocols/mod.rs b/src/wayland/protocols/mod.rs index 57e05b8a..c6c0234a 100644 --- a/src/wayland/protocols/mod.rs +++ b/src/wayland/protocols/mod.rs @@ -4,6 +4,7 @@ pub mod a11y; pub mod corner_radius; pub mod drm; pub mod image_capture_source; +pub mod keyboard_layout; pub mod output_configuration; pub mod output_power; pub mod overlap_notify; From 26ee83aaa7a5dc9016d0d871df53bec04ceec3a5 Mon Sep 17 00:00:00 2001 From: Ian Douglas Scott Date: Mon, 6 Jul 2026 12:34:21 -0700 Subject: [PATCH 12/92] Implement `Debug` and comparison with `CosmicMapped` for `CosmicMappedKey` --- src/shell/element/mod.rs | 18 ++++++++++++++++-- 1 file changed, 16 insertions(+), 2 deletions(-) diff --git a/src/shell/element/mod.rs b/src/shell/element/mod.rs index 8639aeb6..4c8ca171 100644 --- a/src/shell/element/mod.rs +++ b/src/shell/element/mod.rs @@ -125,9 +125,9 @@ impl fmt::Debug for CosmicMapped { } } -#[derive(Clone)] +#[derive(Clone, Debug)] pub struct CosmicMappedKey(CosmicMappedKeyInner); -#[derive(Clone)] +#[derive(Clone, Debug)] enum CosmicMappedKeyInner { Window(Weak>>), Stack(Weak>>), @@ -166,6 +166,20 @@ impl PartialEq for CosmicMappedKey { } impl Eq for CosmicMappedKey {} +impl PartialEq for CosmicMapped { + fn eq(&self, other: &CosmicMappedKey) -> bool { + match (&self.element, &other.0) { + (CosmicMappedInternal::Window(window), CosmicMappedKeyInner::Window(weak)) => { + Arc::as_ptr(&window.0.0) == weak.as_ptr() + } + (CosmicMappedInternal::Stack(stack), CosmicMappedKeyInner::Stack(weak)) => { + Arc::as_ptr(&stack.0.0) == weak.as_ptr() + } + _ => false, + } + } +} + impl PartialEq for CosmicMapped { fn eq(&self, other: &Self) -> bool { self.element == other.element From f7656968348ebd4508da8c817b39b22016b02cc9 Mon Sep 17 00:00:00 2001 From: Ian Douglas Scott Date: Thu, 18 Jun 2026 19:43:45 -0700 Subject: [PATCH 13/92] Avoid unnecessarily unwrapping `stack_ref()` after `is_stack()` In a lot of cases, matching on `stack_ref()` also works. --- src/shell/layout/tiling/mod.rs | 12 +++++------ src/shell/mod.rs | 32 +++++++++++------------------- src/shell/workspace.rs | 3 +-- src/wayland/handlers/compositor.rs | 3 +-- 4 files changed, 20 insertions(+), 30 deletions(-) diff --git a/src/shell/layout/tiling/mod.rs b/src/shell/layout/tiling/mod.rs index 45ca985e..1c48ab73 100644 --- a/src/shell/layout/tiling/mod.rs +++ b/src/shell/layout/tiling/mod.rs @@ -1877,8 +1877,8 @@ impl TilingLayout { if let Some(id) = id { return match tree.get(&id).unwrap().data() { Data::Mapped { mapped, .. } => { - if mapped.is_stack() { - mapped.stack_ref().unwrap().focus_stack(); + if let Some(stack) = mapped.stack_ref() { + stack.focus_stack(); } FocusResult::Some(mapped.clone().into()) } @@ -1986,11 +1986,11 @@ impl TilingLayout { })) } Data::Mapped { mapped, .. } => { - if mapped.is_stack() + if let Some(stack) = mapped.stack_ref() && desc.stack_window.is_none() && replacement_id == &desc.node { - mapped.stack_ref().unwrap().focus_stack(); + stack.focus_stack(); } FocusResult::Some(KeyboardFocusTarget::Element(mapped.clone())) } @@ -2062,7 +2062,7 @@ impl TilingLayout { }); } Data::Mapped { mapped, .. } => { - if mapped.is_stack() + if let Some(stack) = mapped.stack_ref() && swap_desc .as_ref() .map(|desc| { @@ -2071,7 +2071,7 @@ impl TilingLayout { }) .unwrap_or(false) { - mapped.stack_ref().unwrap().focus_stack(); + stack.focus_stack(); } return FocusResult::Some(mapped.clone().into()); } diff --git a/src/shell/mod.rs b/src/shell/mod.rs index 89309e88..add9b67a 100644 --- a/src/shell/mod.rs +++ b/src/shell/mod.rs @@ -2856,10 +2856,12 @@ impl Shell { let maybe_focused = workspace.focus_stack.get(&seat).iter().next().cloned(); if let Some(FocusTarget::Window(focused)) = maybe_focused - && (focused.is_stack() && !is_dialog && !should_be_maximized) + && let Some(stack) = focused.stack_ref() + && !is_dialog + && !should_be_maximized && !(workspace.is_tiled(&focused.active_window()) && floating_exception) { - focused.stack_ref().unwrap().add_window(window, None, None); + stack.add_window(window, None, None); if was_activated { workspace_state.add_workspace_state(&workspace_handle, WState::Urgent); } @@ -2985,8 +2987,8 @@ impl Shell { .map(|idx| (idx, m.clone())) }); let surface = if let Some((idx, mapped)) = sticky_res { - if mapped.is_stack() { - mapped.stack_ref().unwrap().remove_idx(idx) + if let Some(stack) = mapped.stack_ref() { + stack.remove_idx(idx) } else { set.sticky_layer.unmap(&mapped, None); Some(mapped.active_window()) @@ -2996,20 +2998,13 @@ impl Shell { .iter() .position(|w| w.windows().any(|s| &s == surface)) { - if set + if let Some(stack) = set .minimized_windows - .get(idx) + .get_mut(idx) .unwrap() - .mapped() - .is_some_and(CosmicMapped::is_stack) + .mapped_mut() + .and_then(|m| m.stack_ref()) { - let window = set - .minimized_windows - .get_mut(idx) - .unwrap() - .mapped_mut() - .unwrap(); - let stack = window.stack_ref().unwrap(); let idx = stack.surfaces().position(|s| &s == surface); idx.and_then(|idx| stack.remove_idx(idx)) } else { @@ -4796,12 +4791,9 @@ impl Shell { { let mut from = set.sticky_layer.element_geometry(&mapped).unwrap(); let mut was_maximized = false; - window = if mapped - .stack_ref() - .map(|stack| stack.len() > 1) - .unwrap_or(false) + window = if let Some(stack) = mapped.stack_ref() + && stack.len() > 1 { - let stack = mapped.stack_ref().unwrap(); let surface = stack.surfaces().find(|s| s == surface).unwrap(); stack.remove_window(&surface); surface diff --git a/src/shell/workspace.rs b/src/shell/workspace.rs index f4e3dcab..4445b1be 100644 --- a/src/shell/workspace.rs +++ b/src/shell/workspace.rs @@ -713,8 +713,7 @@ impl Workspace { } let mapped = self.element_for_surface(surface)?; - let maybe_stack = mapped.stack_ref().filter(|s| s.len() > 1); - if let Some(stack) = maybe_stack + if let Some(stack) = mapped.stack_ref() && stack.len() > 1 { let idx = stack.surfaces().position(|s| &s == surface); diff --git a/src/wayland/handlers/compositor.rs b/src/wayland/handlers/compositor.rs index 92b51e58..12bc2e56 100644 --- a/src/wayland/handlers/compositor.rs +++ b/src/wayland/handlers/compositor.rs @@ -314,8 +314,7 @@ impl CompositorHandler for State { .then(|| state.element()) }); if let Some(window) = moved_window { - if window.is_stack() { - let stack = window.stack_ref().unwrap(); + if let Some(stack) = window.stack_ref() { if let Some(i) = stack.surfaces().position(|s| { s.wl_surface() .as_deref() From 9e6d46c17e70484ee486e31e798ea01eb51a7062 Mon Sep 17 00:00:00 2001 From: Ian Douglas Scott Date: Thu, 18 Jun 2026 20:41:28 -0700 Subject: [PATCH 14/92] shell: Restore fullscreen surface to stack Previously, a surface that was in a stack that is fullscreened would be removed from the stack, and on unfullscreening, restored to the floating/tiling layer as though it were a new window outside the stack. Instead, if the stack still exists, add the window to it at the same position it previously occupied. --- src/shell/mod.rs | 25 ++++++++++++++++++++++--- src/shell/workspace.rs | 40 ++++++++++++++++++++-------------------- 2 files changed, 42 insertions(+), 23 deletions(-) diff --git a/src/shell/mod.rs b/src/shell/mod.rs index add9b67a..8e2740a5 100644 --- a/src/shell/mod.rs +++ b/src/shell/mod.rs @@ -2592,9 +2592,21 @@ impl Shell { pub fn remap_unfullscreened_window( &mut self, surface: CosmicSurface, - state: Option, + mut state: Option, loop_handle: &LoopHandle<'static, State>, ) -> CosmicMapped { + if let Some(FullscreenRestoreState::Stack { state: stack_state }) = &state { + if let Some(mapped) = self.mapped().find(|m| **m == stack_state.stack) + && let Some(stack) = mapped.stack_ref() + { + let idx = stack_state.idx.min(stack.len()); + stack.add_window(surface, Some(idx), None); + return mapped.clone(); + } else { + state = None; + } + } + let window = CosmicMapped::from(CosmicWindow::new( surface, loop_handle.clone(), @@ -2637,7 +2649,9 @@ impl Shell { workspace } None => self.workspaces.active_mut(&seat.active_output()).unwrap(), - Some(FullscreenRestoreState::Sticky { .. }) => unreachable!(), + Some(FullscreenRestoreState::Sticky { .. } | FullscreenRestoreState::Stack { .. }) => { + unreachable!() + } }; let fullscreen_geometry = workspace.output.geometry().to_local(&workspace.output); @@ -2752,7 +2766,9 @@ impl Shell { } } } - Some(FullscreenRestoreState::Sticky { .. }) => unreachable!(), + Some(FullscreenRestoreState::Sticky { .. } | FullscreenRestoreState::Stack { .. }) => { + unreachable!() + } } window @@ -4878,6 +4894,9 @@ impl Shell { state, }) } + WorkspaceRestoreData::Stack(stack_state) => { + Some(FullscreenRestoreState::Stack { state: stack_state }) + } WorkspaceRestoreData::Fullscreen(_) => unreachable!(), }, Some(from), diff --git a/src/shell/workspace.rs b/src/shell/workspace.rs index 4445b1be..bc5071da 100644 --- a/src/shell/workspace.rs +++ b/src/shell/workspace.rs @@ -62,7 +62,7 @@ use wayland_backend::server::ClientId; use super::{ CosmicMappedRenderElement, CosmicSurface, ResizeDirection, ResizeMode, element::{ - CosmicMapped, MaximizedState, resize_indicator::ResizeIndicator, + CosmicMapped, CosmicMappedKey, MaximizedState, resize_indicator::ResizeIndicator, stack::CosmicStackRenderElement, swap_indicator::SwapIndicator, window::CosmicWindowRenderElement, }, @@ -263,6 +263,9 @@ pub enum FullscreenRestoreState { output: WeakOutput, state: FloatingRestoreData, }, + Stack { + state: StackRestoreData, + }, } impl FullscreenRestoreState { @@ -271,6 +274,7 @@ impl FullscreenRestoreState { FullscreenRestoreState::Floating { state, .. } | FullscreenRestoreState::Sticky { state, .. } => state.was_maximized, FullscreenRestoreState::Tiling { state, .. } => state.was_maximized, + FullscreenRestoreState::Stack { .. } => false, } } } @@ -280,16 +284,7 @@ pub enum WorkspaceRestoreData { Fullscreen(Option), Tiling(Option), Floating(Option), -} - -impl From for WorkspaceRestoreData { - fn from(value: ManagedLayer) -> Self { - match value { - ManagedLayer::Floating | ManagedLayer::Sticky => WorkspaceRestoreData::Floating(None), - ManagedLayer::Tiling => WorkspaceRestoreData::Tiling(None), - ManagedLayer::Fullscreen => WorkspaceRestoreData::Fullscreen(None), - } - } + Stack(StackRestoreData), } #[derive(Debug, Clone)] @@ -321,6 +316,12 @@ pub struct TilingRestoreData { pub was_maximized: bool, } +#[derive(Debug, Clone)] +pub struct StackRestoreData { + pub stack: CosmicMappedKey, + pub idx: usize, +} + #[derive(Debug, Clone)] pub struct FullscreenRestoreData { pub previous_state: FullscreenRestoreState, @@ -716,15 +717,14 @@ impl Workspace { if let Some(stack) = mapped.stack_ref() && stack.len() > 1 { - let idx = stack.surfaces().position(|s| &s == surface); - let layer = if self.is_tiled(surface) { - ManagedLayer::Tiling - } else { - ManagedLayer::Floating - }; - return idx - .and_then(|idx| stack.remove_idx(idx)) - .map(|s| (s, layer.into())); + let idx = stack.surfaces().position(|s| &s == surface)?; + return Some(( + stack.remove_idx(idx)?, + WorkspaceRestoreData::Stack(StackRestoreData { + stack: mapped.key(), + idx, + }), + )); } // we know mapped is no stack with more than one element now, From 5c0c22988e79aac5ee66b76ae5bf22e8055fa97b Mon Sep 17 00:00:00 2001 From: Ian Douglas Scott Date: Tue, 23 Jun 2026 14:49:11 -0700 Subject: [PATCH 15/92] Don't unnecessarily use `Option` in `WorkspaceRestoreData` Unless we expect to need this again in the future, it's best not to have an `Option` here unnecessarily. --- src/shell/mod.rs | 18 +++++++++--------- src/shell/workspace.rs | 18 ++++++++---------- 2 files changed, 17 insertions(+), 19 deletions(-) diff --git a/src/shell/mod.rs b/src/shell/mod.rs index 8e2740a5..1206b338 100644 --- a/src/shell/mod.rs +++ b/src/shell/mod.rs @@ -3251,7 +3251,7 @@ impl Shell { toplevel_enter_workspace(window, to); // we can't restore to a given position - if let WorkspaceRestoreData::Tiling(Some(state)) = &mut window_state { + if let WorkspaceRestoreData::Tiling(state) = &mut window_state { state.state.take(); } // update fullscreen state to restore to the new workspace @@ -3278,7 +3278,7 @@ impl Shell { if is_minimized { let to_workspace = self.workspaces.space_for_handle_mut(to).unwrap(); // checked above let minimized_window = match window_state { - WorkspaceRestoreData::Floating(Some(previous)) => { + WorkspaceRestoreData::Floating(previous) => { let window = CosmicMapped::from(CosmicWindow::new( window.clone(), evlh.clone(), @@ -3288,7 +3288,7 @@ impl Shell { window.set_minimized(true); MinimizedWindow::Floating { window, previous } } - WorkspaceRestoreData::Tiling(Some(previous)) => { + WorkspaceRestoreData::Tiling(previous) => { let window = CosmicMapped::from(CosmicWindow::new( window.clone(), evlh.clone(), @@ -3355,7 +3355,7 @@ impl Shell { self.appearance_conf, )); let position = match window_state { - WorkspaceRestoreData::Floating(Some(data)) => Some( + WorkspaceRestoreData::Floating(data) => Some( data.position_relative(to_workspace.output.geometry().size.as_logical()), ), _ => None, @@ -3455,7 +3455,7 @@ impl Shell { let to_workspace = self.workspaces.space_for_handle_mut(to).unwrap(); // checked above if !to_workspace.tiling_enabled { let (position, was_maximized, was_snapped) = match &window_state { - WorkspaceRestoreData::Floating(Some(data)) => ( + WorkspaceRestoreData::Floating(data) => ( Some(data.position_relative(to_workspace.output.geometry().size.as_logical())), data.was_maximized, data.was_snapped, @@ -4883,15 +4883,15 @@ impl Shell { &seat, match state { WorkspaceRestoreData::Floating(floating_state) => { - floating_state.map(|state| FullscreenRestoreState::Floating { + Some(FullscreenRestoreState::Floating { workspace: handle, - state, + state: floating_state, }) } WorkspaceRestoreData::Tiling(tiling_state) => { - tiling_state.map(|state| FullscreenRestoreState::Tiling { + Some(FullscreenRestoreState::Tiling { workspace: handle, - state, + state: tiling_state, }) } WorkspaceRestoreData::Stack(stack_state) => { diff --git a/src/shell/workspace.rs b/src/shell/workspace.rs index bc5071da..c22daf07 100644 --- a/src/shell/workspace.rs +++ b/src/shell/workspace.rs @@ -282,8 +282,8 @@ impl FullscreenRestoreState { #[derive(Debug, Clone)] pub enum WorkspaceRestoreData { Fullscreen(Option), - Tiling(Option), - Floating(Option), + Tiling(TilingRestoreData), + Floating(FloatingRestoreData), Stack(StackRestoreData), } @@ -643,32 +643,30 @@ impl Workspace { mapped.set_minimized(false); return Some(match state { MinimizedWindow::Floating { previous, .. } => { - WorkspaceRestoreData::Floating(Some(previous)) - } - MinimizedWindow::Tiling { previous, .. } => { - WorkspaceRestoreData::Tiling(Some(previous)) + WorkspaceRestoreData::Floating(previous) } + MinimizedWindow::Tiling { previous, .. } => WorkspaceRestoreData::Tiling(previous), MinimizedWindow::Fullscreen { .. } => unreachable!(), }); } if let Ok(state) = self.tiling_layer.unmap(mapped, None) { - return Some(WorkspaceRestoreData::Tiling(Some(TilingRestoreData { + return Some(WorkspaceRestoreData::Tiling(TilingRestoreData { state, was_maximized: was_maximized.is_some(), - }))); + })); } let was_snapped = *mapped.floating_tiled.lock().unwrap(); // unmaximize_request might have triggered a `floating_layer.refresh()`, // which may have already removed a non-alive surface. if let Some(floating_geometry) = self.floating_layer.unmap(mapped, None).or(was_maximized) { - return Some(WorkspaceRestoreData::Floating(Some(FloatingRestoreData { + return Some(WorkspaceRestoreData::Floating(FloatingRestoreData { geometry: floating_geometry, output_size: self.output.geometry().size.as_logical(), was_maximized: was_maximized.is_some(), was_snapped, - }))); + })); }; None From 98e1dd7f6b209b801f0c8586225f73a730837248 Mon Sep 17 00:00:00 2001 From: Ian Douglas Scott Date: Tue, 23 Jun 2026 15:04:39 -0700 Subject: [PATCH 16/92] shell: Also restore stack element on sticky layer to stack --- src/shell/mod.rs | 15 +++++++++++---- 1 file changed, 11 insertions(+), 4 deletions(-) diff --git a/src/shell/mod.rs b/src/shell/mod.rs index 1206b338..97e2e9de 100644 --- a/src/shell/mod.rs +++ b/src/shell/mod.rs @@ -4807,11 +4807,18 @@ impl Shell { { let mut from = set.sticky_layer.element_geometry(&mapped).unwrap(); let mut was_maximized = false; + let mut restore_state = None; window = if let Some(stack) = mapped.stack_ref() && stack.len() > 1 { - let surface = stack.surfaces().find(|s| s == surface).unwrap(); - stack.remove_window(&surface); + let idx = stack.surfaces().position(|s| &s == surface)?; + let surface = stack.remove_idx(idx)?; + restore_state = Some(FullscreenRestoreState::Stack { + state: StackRestoreData { + stack: mapped.key(), + idx, + }, + }); surface } else { // Must be set before `map_internal`/`unmap` below, as both may call @@ -4844,7 +4851,7 @@ impl Shell { workspace.map_fullscreen( &window, &seat, - Some(FullscreenRestoreState::Sticky { + Some(restore_state.unwrap_or(FullscreenRestoreState::Sticky { output: old_output, state: FloatingRestoreData { geometry: from, @@ -4852,7 +4859,7 @@ impl Shell { was_maximized, was_snapped: None, }, - }), + })), Some(from), ); } else if let Some(workspace) = self.space_for_mut(&mapped) { From 373a44d43105a2c5563ee80657dae3d312d7f508 Mon Sep 17 00:00:00 2001 From: Ian Douglas Scott Date: Tue, 23 Jun 2026 15:54:37 -0700 Subject: [PATCH 17/92] shell: Restore fullscreen to single-element stack If a surface was previously a single-element stack, restore it normally using the floating/tiling restore data, but map as a single element stack instead of a plain window. --- src/shell/mod.rs | 31 ++++++++++++++++++++++++------- src/shell/workspace.rs | 14 ++++++++++++++ 2 files changed, 38 insertions(+), 7 deletions(-) diff --git a/src/shell/mod.rs b/src/shell/mod.rs index 97e2e9de..60b3d1e1 100644 --- a/src/shell/mod.rs +++ b/src/shell/mod.rs @@ -12,7 +12,10 @@ use std::{ use wayland_backend::server::ClientId; use crate::{ - shell::{focus::FocusTarget, grabs::fullscreen_items, layout::tiling::PlaceholderType}, + shell::{ + element::CosmicStack, focus::FocusTarget, grabs::fullscreen_items, + layout::tiling::PlaceholderType, + }, wayland::{ handlers::data_device::{self, get_dnd_icon}, protocols::workspace::{State as WState, WorkspaceCapabilities}, @@ -2607,12 +2610,21 @@ impl Shell { } } - let window = CosmicMapped::from(CosmicWindow::new( - surface, - loop_handle.clone(), - self.theme.clone(), - self.appearance_conf, - )); + let window = if state.as_ref().is_some_and(|s| s.was_stack()) { + CosmicMapped::from(CosmicStack::new( + std::iter::once(surface), + loop_handle.clone(), + self.theme.clone(), + self.appearance_conf, + )) + } else { + CosmicMapped::from(CosmicWindow::new( + surface, + loop_handle.clone(), + self.theme.clone(), + self.appearance_conf, + )) + }; if let Some(FullscreenRestoreState::Sticky { output, state, .. }) = &state { let output = output @@ -3267,6 +3279,7 @@ impl Shell { state: None, was_maximized: previous.was_maximized(), }, + was_stack: previous.was_stack(), }; } else if let FullscreenRestoreState::Tiling { workspace, .. } | FullscreenRestoreState::Floating { workspace, .. } = previous @@ -4808,6 +4821,7 @@ impl Shell { let mut from = set.sticky_layer.element_geometry(&mapped).unwrap(); let mut was_maximized = false; let mut restore_state = None; + let was_stack = mapped.is_stack(); window = if let Some(stack) = mapped.stack_ref() && stack.len() > 1 { @@ -4859,6 +4873,7 @@ impl Shell { was_maximized, was_snapped: None, }, + was_stack, })), Some(from), ); @@ -4893,12 +4908,14 @@ impl Shell { Some(FullscreenRestoreState::Floating { workspace: handle, state: floating_state, + was_stack: mapped.is_stack(), }) } WorkspaceRestoreData::Tiling(tiling_state) => { Some(FullscreenRestoreState::Tiling { workspace: handle, state: tiling_state, + was_stack: mapped.is_stack(), }) } WorkspaceRestoreData::Stack(stack_state) => { diff --git a/src/shell/workspace.rs b/src/shell/workspace.rs index c22daf07..e3dcec5a 100644 --- a/src/shell/workspace.rs +++ b/src/shell/workspace.rs @@ -254,14 +254,17 @@ pub enum FullscreenRestoreState { Tiling { workspace: WorkspaceHandle, state: TilingRestoreData, + was_stack: bool, }, Floating { workspace: WorkspaceHandle, state: FloatingRestoreData, + was_stack: bool, }, Sticky { output: WeakOutput, state: FloatingRestoreData, + was_stack: bool, }, Stack { state: StackRestoreData, @@ -277,6 +280,17 @@ impl FullscreenRestoreState { FullscreenRestoreState::Stack { .. } => false, } } + + // Surface was previously a single-window stack + pub fn was_stack(&self) -> bool { + match self { + FullscreenRestoreState::Floating { was_stack, .. } + | FullscreenRestoreState::Sticky { was_stack, .. } => *was_stack, + FullscreenRestoreState::Tiling { was_stack, .. } => *was_stack, + // Stack wasn't removed; surface was removed from the stack + FullscreenRestoreState::Stack { .. } => false, + } + } } #[derive(Debug, Clone)] From 067be10474ff8fc8d0053061e4f8a5f355883de7 Mon Sep 17 00:00:00 2001 From: maskedsyntax Date: Mon, 6 Jul 2026 08:43:27 +0530 Subject: [PATCH 18/92] fix: fall back to legacy X11 names when loading pointer themes Smithay requests modern shape-v1 names while themes like DMZ-White only provide legacy X11 aliases. Try known aliases before failing to load. --- src/backend/render/cursor.rs | 57 ++++++++++++++++++++++++++++++------ 1 file changed, 48 insertions(+), 9 deletions(-) diff --git a/src/backend/render/cursor.rs b/src/backend/render/cursor.rs index 3c75996d..671a32a2 100644 --- a/src/backend/render/cursor.rs +++ b/src/backend/render/cursor.rs @@ -114,18 +114,57 @@ enum Error { NoDefaultCursor, #[error("Error opening xcursor file: {0}")] File(#[from] std::io::Error), - #[error("Failed to parse XCursor file")] - Parse, +} + +fn cursor_aliases(name: &str) -> &[&str] { + match name { + "default" => &["default", "left_ptr", "arrow"], + "pointer" => &["pointer", "hand2", "hand"], + "text" => &["text", "xterm"], + "wait" => &["wait", "watch"], + "progress" => &["progress", "left_ptr_watch"], + + "ew-resize" => &["ew-resize", "h_double_arrow", "sb_h_double_arrow"], + "ns-resize" => &["ns-resize", "v_double_arrow", "sb_v_double_arrow"], + "nw-resize" => &["nw-resize", "top_left_corner"], + "ne-resize" => &["ne-resize", "top_right_corner"], + "sw-resize" => &["sw-resize", "bottom_left_corner"], + "se-resize" => &["se-resize", "bottom_right_corner"], + + "w-resize" => &["w-resize", "left_side"], + "e-resize" => &["e-resize", "right_side"], + "n-resize" => &["n-resize", "top_side"], + "s-resize" => &["s-resize", "bottom_side"], + + "move" => &["move", "fleur"], + "not-allowed" => &["not-allowed", "crossed_circle"], + "crosshair" => &["crosshair", "cross"], + "help" => &["help", "question_arrow", "left_ptr_help"], + + _ => &[], + } } fn load_icon(theme: &CursorTheme, shape: CursorIcon) -> Result, Error> { - let icon_path = theme - .load_icon(&shape.to_string()) - .ok_or(Error::NoDefaultCursor)?; - let mut cursor_file = std::fs::File::open(&icon_path)?; - let mut cursor_data = Vec::new(); - cursor_file.read_to_end(&mut cursor_data)?; - parse_xcursor(&cursor_data).ok_or(Error::Parse) + let shape_name = shape.to_string(); + + for name in cursor_aliases(&shape_name) + .iter() + .copied() + .chain(std::iter::once(shape_name.as_str())) + { + if let Some(icon_path) = theme.load_icon(name) { + let mut cursor_file = std::fs::File::open(&icon_path)?; + let mut cursor_data = Vec::new(); + cursor_file.read_to_end(&mut cursor_data)?; + + if let Some(images) = parse_xcursor(&cursor_data) { + return Ok(images); + } + } + } + + Err(Error::NoDefaultCursor) } render_elements! { From 0ce7f1144505da7da93e696f05b98e33e923f7a2 Mon Sep 17 00:00:00 2001 From: Victoria Brekenfeld Date: Thu, 22 Jan 2026 16:13:52 +0100 Subject: [PATCH 19/92] element/surface: Fix reported corner radius --- src/shell/element/surface.rs | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/src/shell/element/surface.rs b/src/shell/element/surface.rs index 2bd72f25..4838bd61 100644 --- a/src/shell/element/surface.rs +++ b/src/shell/element/surface.rs @@ -204,10 +204,10 @@ impl CosmicSurface { let corners = guard.current().0?; Some([ - corners.bottom_right.min(half_min_dim), - corners.top_right.min(half_min_dim), - corners.bottom_left.min(half_min_dim), corners.top_left.min(half_min_dim), + corners.top_right.min(half_min_dim), + corners.bottom_right.min(half_min_dim), + corners.bottom_left.min(half_min_dim), ]) }) }) From 8b990e565807e16005a66fbcba70a818127d6156 Mon Sep 17 00:00:00 2001 From: Victoria Brekenfeld Date: Wed, 4 Mar 2026 16:57:17 +0100 Subject: [PATCH 20/92] render: Merge FromGlesError into AsGlowRenderer --- src/backend/kms/render/gles.rs | 17 +--------- src/backend/render/element.rs | 31 +++++++++---------- src/backend/render/mod.rs | 7 +---- src/shell/element/mod.rs | 19 ++++++------ src/shell/element/stack.rs | 12 +++---- src/shell/element/window.rs | 12 +++---- src/shell/workspace.rs | 10 ++---- .../handlers/image_copy_capture/render.rs | 11 ++----- 8 files changed, 40 insertions(+), 79 deletions(-) diff --git a/src/backend/kms/render/gles.rs b/src/backend/kms/render/gles.rs index 4a75c0ab..f6e44861 100644 --- a/src/backend/kms/render/gles.rs +++ b/src/backend/kms/render/gles.rs @@ -12,10 +12,9 @@ use smithay::{ }, drm::{CreateDrmNodeError, DrmNode}, renderer::{ - RendererSuper, gles::{GlesError, GlesRenderer, ffi}, glow::GlowRenderer, - multigpu::{ApiDevice, Error as MultiError, GraphicsApi}, + multigpu::{ApiDevice, GraphicsApi}, }, }, reexports::drm::control::Device, @@ -32,8 +31,6 @@ use std::{ sync::atomic::{AtomicBool, Ordering}, }; -use crate::backend::render::element::FromGlesError; - /// Errors raised by the [`GbmGlesBackend`] #[derive(Debug, thiserror::Error)] pub enum Error { @@ -213,15 +210,3 @@ impl ApiDevice for GbmGlowDevice { !self.intel_export_quirk } } - -impl FromGlesError - for MultiError, T> -where - T::Error: 'static, - <::Renderer as RendererSuper>::Error: 'static, -{ - #[inline] - fn from_gles_error(err: GlesError) -> MultiError, T> { - MultiError::Render(err) - } -} diff --git a/src/backend/render/element.rs b/src/backend/render/element.rs index 496702d0..04f7fd9e 100644 --- a/src/backend/render/element.rs +++ b/src/backend/render/element.rs @@ -1,4 +1,7 @@ -use crate::shell::{CosmicMappedRenderElement, WorkspaceRenderElement}; +use crate::{ + backend::render::GlMultiError, + shell::{CosmicMappedRenderElement, WorkspaceRenderElement}, +}; #[cfg(feature = "debug")] use smithay::backend::renderer::element::texture::TextureRenderElement; @@ -203,7 +206,6 @@ impl RenderElement for CosmicElement where R: AsGlowRenderer, R::TextureId: 'static, - R::Error: FromGlesError, CosmicMappedRenderElement: RenderElement, { fn draw( @@ -237,7 +239,7 @@ where opaque_regions, cache, ) - .map_err(FromGlesError::from_gles_error) + .map_err(R::from_gles_error) } CosmicElement::Zoom(elem) => elem.draw(frame, src, dst, damage, opaque_regions, cache), #[cfg(feature = "debug")] @@ -252,7 +254,7 @@ where opaque_regions, cache, ) - .map_err(FromGlesError::from_gles_error) + .map_err(R::from_gles_error) } } } @@ -293,7 +295,7 @@ where RenderElement::::capture_framebuffer( elem, glow_frame, src, dst, cache, ) - .map_err(FromGlesError::from_gles_error) + .map_err(R::from_gles_error) } CosmicElement::Zoom(elem) => elem.capture_framebuffer(frame, src, dst, cache), #[cfg(feature = "debug")] @@ -302,7 +304,7 @@ where RenderElement::::capture_framebuffer( elem, glow_frame, src, dst, cache, ) - .map_err(FromGlesError::from_gles_error) + .map_err(R::from_gles_error) } } } @@ -365,6 +367,7 @@ pub trait AsGlowRenderer: fn glow_frame_mut<'a, 'frame, 'buffer>( frame: &'a mut Self::Frame<'frame, 'buffer>, ) -> &'a mut GlowFrame<'frame, 'buffer>; + fn from_gles_error(err: GlesError) -> Self::Error; } impl AsGlowRenderer for GlowRenderer { @@ -384,6 +387,9 @@ impl AsGlowRenderer for GlowRenderer { ) -> &'a mut GlowFrame<'frame, 'buffer> { frame } + fn from_gles_error(err: GlesError) -> Self::Error { + err + } } impl AsGlowRenderer for GlMultiRenderer<'_> { @@ -403,6 +409,9 @@ impl AsGlowRenderer for GlMultiRenderer<'_> { ) -> &'b mut GlowFrame<'frame, 'buffer> { frame.as_mut() } + fn from_gles_error(err: GlesError) -> Self::Error { + GlMultiError::Render(err) + } } pub struct DamageElement { @@ -458,13 +467,3 @@ impl RenderElement for DamageElement { Ok(()) } } - -pub trait FromGlesError { - fn from_gles_error(err: GlesError) -> Self; -} - -impl FromGlesError for GlesError { - fn from_gles_error(err: GlesError) -> Self { - err - } -} diff --git a/src/backend/render/mod.rs b/src/backend/render/mod.rs index d847f533..6a2f02da 100644 --- a/src/backend/render/mod.rs +++ b/src/backend/render/mod.rs @@ -42,7 +42,6 @@ use crate::{ }; use cosmic::Theme; -use element::FromGlesError; use smithay::{ backend::{ allocator::Fourcc, @@ -628,7 +627,6 @@ pub fn output_elements( where R: AsGlowRenderer, R::TextureId: Send + Clone + 'static, - R::Error: FromGlesError, CosmicMappedRenderElement: RenderElement, WorkspaceRenderElement: RenderElement, { @@ -655,7 +653,7 @@ where ), scale, ) - .map_err(FromGlesError::from_gles_error) + .map_err(R::from_gles_error) .map_err(RenderError::Rendering)? .into(), ] @@ -728,7 +726,6 @@ pub fn workspace_elements( where R: AsGlowRenderer, R::TextureId: Send + Clone + 'static, - R::Error: FromGlesError, CosmicMappedRenderElement: RenderElement, WorkspaceRenderElement: RenderElement, { @@ -1205,7 +1202,6 @@ pub fn render_output<'d, R>( where R: AsGlowRenderer, R::TextureId: Send + Clone + 'static, - R::Error: FromGlesError, CosmicElement: RenderElement, CosmicMappedRenderElement: RenderElement, WorkspaceRenderElement: RenderElement, @@ -1510,7 +1506,6 @@ pub fn render_workspace<'d, R>( where R: AsGlowRenderer, R::TextureId: Send + Clone + 'static, - R::Error: FromGlesError, CosmicElement: RenderElement, CosmicMappedRenderElement: RenderElement, WorkspaceRenderElement: RenderElement, diff --git a/src/shell/element/mod.rs b/src/shell/element/mod.rs index 4c8ca171..69a8391f 100644 --- a/src/shell/element/mod.rs +++ b/src/shell/element/mod.rs @@ -1,5 +1,5 @@ use crate::{ - backend::render::element::{AsGlowRenderer, FromGlesError}, + backend::render::element::AsGlowRenderer, state::State, utils::{iced::IcedElementInternal, prelude::*}, }; @@ -1366,7 +1366,6 @@ impl RenderElement for CosmicMappedRenderElement where R: AsGlowRenderer, R::TextureId: 'static, - R::Error: FromGlesError, { fn draw( &self, @@ -1399,7 +1398,7 @@ where opaque_regions, cache, ) - .map_err(FromGlesError::from_gles_error), + .map_err(R::from_gles_error), CosmicMappedRenderElement::MovingStack(elem) => { elem.draw(frame, src, dst, damage, opaque_regions, cache) } @@ -1421,7 +1420,7 @@ where opaque_regions, cache, ) - .map_err(FromGlesError::from_gles_error), + .map_err(R::from_gles_error), CosmicMappedRenderElement::Overlay(elem) => RenderElement::::draw( elem, R::glow_frame_mut(frame), @@ -1431,7 +1430,7 @@ where opaque_regions, cache, ) - .map_err(FromGlesError::from_gles_error), + .map_err(R::from_gles_error), CosmicMappedRenderElement::StackHoverIndicator(elem) => { elem.draw(frame, src, dst, damage, opaque_regions, cache) } @@ -1447,7 +1446,7 @@ where opaque_regions, cache, ) - .map_err(FromGlesError::from_gles_error) + .map_err(R::from_gles_error) } } } @@ -1510,7 +1509,7 @@ where dst, cache, ) - .map_err(FromGlesError::from_gles_error) + .map_err(R::from_gles_error) } CosmicMappedRenderElement::MovingStack(elem) => { elem.capture_framebuffer(frame, src, dst, cache) @@ -1532,7 +1531,7 @@ where dst, cache, ) - .map_err(FromGlesError::from_gles_error) + .map_err(R::from_gles_error) } CosmicMappedRenderElement::Overlay(elem) => { RenderElement::::capture_framebuffer( @@ -1542,7 +1541,7 @@ where dst, cache, ) - .map_err(FromGlesError::from_gles_error) + .map_err(R::from_gles_error) } CosmicMappedRenderElement::StackHoverIndicator(elem) => { elem.capture_framebuffer(frame, src, dst, cache) @@ -1553,7 +1552,7 @@ where RenderElement::::capture_framebuffer( elem, glow_frame, src, dst, cache, ) - .map_err(FromGlesError::from_gles_error) + .map_err(R::from_gles_error) } } } diff --git a/src/shell/element/stack.rs b/src/shell/element/stack.rs index 17df320f..f3ae21ad 100644 --- a/src/shell/element/stack.rs +++ b/src/shell/element/stack.rs @@ -4,11 +4,8 @@ use super::{ }; use crate::{ backend::render::{ - IndicatorShader, Key, Usage, - clipped_surface::ClippedSurfaceRenderElement, - cursor::CursorState, - element::{AsGlowRenderer, FromGlesError}, - shadow::ShadowShader, + IndicatorShader, Key, Usage, clipped_surface::ClippedSurfaceRenderElement, + cursor::CursorState, element::AsGlowRenderer, shadow::ShadowShader, }, hooks::{Decorations, HOOKS}, shell::{ @@ -2039,7 +2036,6 @@ impl RenderElement for CosmicStackRenderElement where R: AsGlowRenderer, R::TextureId: 'static, - R::Error: FromGlesError, { fn draw( &self, @@ -2064,7 +2060,7 @@ where opaque_regions, cache, ) - .map_err(FromGlesError::from_gles_error) + .map_err(R::from_gles_error) } CosmicStackRenderElement::Window(elem) => { elem.draw(frame, src, dst, damage, opaque_regions, cache) @@ -2105,7 +2101,7 @@ where dst, cache, ) - .map_err(FromGlesError::from_gles_error) + .map_err(R::from_gles_error) } CosmicStackRenderElement::Window(elem) => { elem.capture_framebuffer(frame, src, dst, cache) diff --git a/src/shell/element/window.rs b/src/shell/element/window.rs index 52ffd16d..6ebbafa0 100644 --- a/src/shell/element/window.rs +++ b/src/shell/element/window.rs @@ -1,10 +1,7 @@ use crate::{ backend::render::{ - IndicatorShader, Key, Usage, - clipped_surface::ClippedSurfaceRenderElement, - cursor::CursorState, - element::{AsGlowRenderer, FromGlesError}, - shadow::ShadowShader, + IndicatorShader, Key, Usage, clipped_surface::ClippedSurfaceRenderElement, + cursor::CursorState, element::AsGlowRenderer, shadow::ShadowShader, }, hooks::{Decorations, HOOKS}, shell::{ @@ -1406,7 +1403,6 @@ impl RenderElement for CosmicWindowRenderElement where R: AsGlowRenderer, R::TextureId: 'static, - R::Error: FromGlesError, { fn draw( &self, @@ -1431,7 +1427,7 @@ where opaque_regions, cache, ) - .map_err(FromGlesError::from_gles_error) + .map_err(R::from_gles_error) } CosmicWindowRenderElement::Window(elem) => { elem.draw(frame, src, dst, damage, opaque_regions, cache) @@ -1472,7 +1468,7 @@ where dst, cache, ) - .map_err(FromGlesError::from_gles_error) + .map_err(R::from_gles_error) } CosmicWindowRenderElement::Window(elem) => { elem.capture_framebuffer(frame, src, dst, cache) diff --git a/src/shell/workspace.rs b/src/shell/workspace.rs index e3dcec5a..18ec24cc 100644 --- a/src/shell/workspace.rs +++ b/src/shell/workspace.rs @@ -2,10 +2,7 @@ use crate::shell::focus::FocusTarget; use crate::shell::layout::tiling::RestoreTilingState; use crate::wayland::handlers::xdg_activation::ActivationContext; use crate::{ - backend::render::{ - BackdropShader, - element::{AsGlowRenderer, FromGlesError}, - }, + backend::render::{BackdropShader, element::AsGlowRenderer}, shell::{ ANIMATION_DURATION, OverviewMode, SeatMoveGrabState, layout::{ @@ -2138,7 +2135,6 @@ impl RenderElement for WorkspaceRenderElement where R: AsGlowRenderer, R::TextureId: 'static, - R::Error: FromGlesError, { fn draw( &self, @@ -2171,7 +2167,7 @@ where opaque_regions, cache, ) - .map_err(FromGlesError::from_gles_error), + .map_err(R::from_gles_error), } } @@ -2218,7 +2214,7 @@ where dst, cache, ) - .map_err(FromGlesError::from_gles_error) + .map_err(R::from_gles_error) } } } diff --git a/src/wayland/handlers/image_copy_capture/render.rs b/src/wayland/handlers/image_copy_capture/render.rs index 21370f31..aa358b7f 100644 --- a/src/wayland/handlers/image_copy_capture/render.rs +++ b/src/wayland/handlers/image_copy_capture/render.rs @@ -41,7 +41,7 @@ use tracing::warn; use crate::{ backend::render::{ CursorMode, ElementFilter, RendererRef, cursor, - element::{AsGlowRenderer, CosmicElement, DamageElement, FromGlesError}, + element::{AsGlowRenderer, CosmicElement, DamageElement}, render_workspace, }, shell::{CosmicMappedRenderElement, CosmicSurface, WorkspaceRenderElement}, @@ -105,8 +105,7 @@ pub fn submit_buffer( mut sync: SyncPoint, ) -> Result, R::Error> where - R: ExportMem, - R::Error: FromGlesError, + R: ExportMem + AsGlowRenderer, { let Some(damage) = damage else { frame.success( @@ -164,7 +163,7 @@ where Ok(()) }) - .map_err(|err| R::Error::from_gles_error(GlesError::BufferAccessError(err))) + .map_err(|err| R::from_gles_error(GlesError::BufferAccessError(err))) .and_then(|x| x) { frame.fail(CaptureFailureReason::Unknown); @@ -194,7 +193,6 @@ pub fn render_session( ) -> Result, DTError> where R: AsGlowRenderer, - R::Error: FromGlesError, F: for<'d> FnOnce( &WlBuffer, &mut R, @@ -321,7 +319,6 @@ pub fn render_workspace_to_buffer( where R: AsGlowRenderer, R::TextureId: Send + Clone + 'static, - R::Error: FromGlesError, CosmicElement: RenderElement, CosmicMappedRenderElement: RenderElement, WorkspaceRenderElement: RenderElement, @@ -554,7 +551,6 @@ pub fn render_window_to_buffer( where R: AsGlowRenderer, R::TextureId: Send + Clone + 'static, - R::Error: FromGlesError, CosmicElement: RenderElement, CosmicMappedRenderElement: RenderElement, { @@ -793,7 +789,6 @@ pub fn render_cursor_to_buffer( where R: AsGlowRenderer, R::TextureId: Send + Clone + 'static, - R::Error: FromGlesError, CosmicElement: RenderElement, CosmicMappedRenderElement: RenderElement, { From 178c6593dcf3d56006d287208f7f901ccb1c9e26 Mon Sep 17 00:00:00 2001 From: Victoria Brekenfeld Date: Wed, 4 Mar 2026 18:16:48 +0100 Subject: [PATCH 21/92] element: Introduce SurfaceRenderElement --- src/backend/render/cursor.rs | 29 +++-- src/backend/render/element.rs | 5 +- src/backend/render/mod.rs | 4 +- .../render/{ => wayland}/clipped_surface.rs | 16 ++- src/backend/render/wayland/mod.rs | 111 ++++++++++++++++++ src/shell/element/stack.rs | 79 +++---------- src/shell/element/surface.rs | 53 ++++----- src/shell/element/window.rs | 86 ++++---------- src/shell/layout/tiling/mod.rs | 50 ++++---- src/shell/workspace.rs | 2 + src/utils/screenshot.rs | 15 +-- .../handlers/image_copy_capture/render.rs | 17 +-- 12 files changed, 254 insertions(+), 213 deletions(-) rename src/backend/render/{ => wayland}/clipped_surface.rs (95%) create mode 100644 src/backend/render/wayland/mod.rs diff --git a/src/backend/render/cursor.rs b/src/backend/render/cursor.rs index 671a32a2..8373e714 100644 --- a/src/backend/render/cursor.rs +++ b/src/backend/render/cursor.rs @@ -1,6 +1,13 @@ // SPDX-License-Identifier: GPL-3.0-only -use crate::{utils::prelude::*, wayland::handlers::compositor::FRAME_TIME_FILTER}; +use crate::{ + backend::render::{ + element::AsGlowRenderer, + wayland::{SurfaceRenderElement, render_elements_from_surface_tree}, + }, + utils::prelude::*, + wayland::handlers::compositor::FRAME_TIME_FILTER, +}; use smithay::{ backend::{ allocator::Fourcc, @@ -9,10 +16,10 @@ use smithay::{ element::{ Kind, memory::{MemoryRenderBuffer, MemoryRenderBufferRenderElement}, - surface::{WaylandSurfaceRenderElement, render_elements_from_surface_tree}, }, }, }, + desktop::utils::bbox_from_surface_tree, input::{ Seat, pointer::{CursorIcon, CursorImageAttributes, CursorImageStatus}, @@ -168,9 +175,9 @@ fn load_icon(theme: &CursorTheme, shape: CursorIcon) -> Result, Error } render_elements! { - pub CursorRenderElement where R: ImportAll + ImportMem; + pub CursorRenderElement where R: ImportAll + ImportMem + AsGlowRenderer; Static=MemoryRenderBufferRenderElement, - Surface=WaylandSurfaceRenderElement, + Surface=SurfaceRenderElement, } pub fn draw_surface_cursor( @@ -180,7 +187,7 @@ pub fn draw_surface_cursor( scale: impl Into>, ) -> Vec<(CursorRenderElement, Point)> where - R: Renderer + ImportAll, + R: Renderer + ImportAll + AsGlowRenderer, R::TextureId: Clone + 'static, { let scale = scale.into(); @@ -199,8 +206,11 @@ where renderer, surface, location.to_physical(scale).to_i32_round(), + bbox_from_surface_tree(surface, location.to_i32_round()).to_f64(), scale, 1.0, + false, + [0; 4], Kind::Cursor, ) .into_iter() @@ -214,9 +224,9 @@ pub fn draw_dnd_icon( surface: &wl_surface::WlSurface, location: Point, scale: impl Into>, -) -> Vec> +) -> Vec> where - R: Renderer + ImportAll, + R: Renderer + ImportAll + AsGlowRenderer, R::TextureId: Clone + 'static, { if get_role(surface) != Some("dnd_icon") { @@ -230,8 +240,11 @@ where renderer, surface, location.to_physical(scale).to_i32_round(), + bbox_from_surface_tree(surface, location.to_i32_round()).to_f64(), scale, 1.0, + false, + [0; 4], FRAME_TIME_FILTER, ) } @@ -319,7 +332,7 @@ pub fn draw_cursor( draw_default: bool, ) -> Vec<(CursorRenderElement, Point)> where - R: Renderer + ImportMem + ImportAll, + R: Renderer + ImportMem + ImportAll + AsGlowRenderer, R::TextureId: Send + Clone + 'static, { // draw the cursor as relevant diff --git a/src/backend/render/element.rs b/src/backend/render/element.rs index 04f7fd9e..34a351c9 100644 --- a/src/backend/render/element.rs +++ b/src/backend/render/element.rs @@ -1,5 +1,5 @@ use crate::{ - backend::render::GlMultiError, + backend::render::{GlMultiError, wayland::SurfaceRenderElement}, shell::{CosmicMappedRenderElement, WorkspaceRenderElement}, }; @@ -13,7 +13,6 @@ use smithay::{ element::{ Element, Id, Kind, RenderElement, UnderlyingStorage, memory::MemoryRenderBufferRenderElement, - surface::WaylandSurfaceRenderElement, utils::{CropRenderElement, Relocate, RelocateRenderElement, RescaleRenderElement}, }, gles::{GlesError, GlesRenderbuffer, GlesTexture, element::TextureShaderElement}, @@ -38,7 +37,7 @@ where RelocateRenderElement>>>, ), Cursor(RescaleRenderElement>>), - Dnd(WaylandSurfaceRenderElement), + Dnd(SurfaceRenderElement), MoveGrab(RescaleRenderElement>), Postprocess( CropRenderElement>>, diff --git a/src/backend/render/mod.rs b/src/backend/render/mod.rs index 6a2f02da..458000a5 100644 --- a/src/backend/render/mod.rs +++ b/src/backend/render/mod.rs @@ -15,9 +15,9 @@ use crate::{ backend::{ kms::render::gles::GbmGlowBackend, render::{ - clipped_surface::{CLIPPING_SHADER, ClippingShader}, element::DamageElement, shadow::{SHADOW_SHADER, ShadowShader}, + wayland::clipped_surface::{CLIPPING_SHADER, ClippingShader}, }, }, config::ScreenFilter, @@ -80,10 +80,10 @@ use smithay::{ use smithay_egui::EguiState; pub mod animations; -pub mod clipped_surface; pub mod cursor; pub mod element; pub mod shadow; +pub mod wayland; use self::element::{AsGlowRenderer, CosmicElement}; use super::kms::Timings; diff --git a/src/backend/render/clipped_surface.rs b/src/backend/render/wayland/clipped_surface.rs similarity index 95% rename from src/backend/render/clipped_surface.rs rename to src/backend/render/wayland/clipped_surface.rs index 8705051e..37eee7f8 100644 --- a/src/backend/render/clipped_surface.rs +++ b/src/backend/render/wayland/clipped_surface.rs @@ -6,7 +6,7 @@ use cgmath::{Matrix3, Vector2}; use smithay::utils::{Buffer, Logical, Physical, Point, Rectangle, Scale, Size, Transform}; use smithay::{ backend::renderer::{ - ImportAll, ImportMem, Renderer, + ImportAll, Renderer, element::{ Element, Id, Kind, RenderElement, UnderlyingStorage, surface::WaylandSurfaceRenderElement, @@ -19,7 +19,7 @@ use smithay::{ use crate::backend::render::element::AsGlowRenderer; -pub static CLIPPING_SHADER: &str = include_str!("./shaders/clipped_surface.frag"); +pub static CLIPPING_SHADER: &str = include_str!("../shaders/clipped_surface.frag"); pub struct ClippingShader(pub GlesTexProgram); impl ClippingShader { @@ -35,10 +35,7 @@ impl ClippingShader { } #[derive(Debug)] -pub struct ClippedSurfaceRenderElement -where - R: Renderer + ImportAll + ImportMem, -{ +pub struct ClippedSurfaceRenderElement { inner: WaylandSurfaceRenderElement, program: GlesTexProgram, radius: [u8; 4], @@ -48,7 +45,7 @@ where impl ClippedSurfaceRenderElement where - R: Renderer + ImportAll + ImportMem, + R: Renderer + ImportAll, { pub fn new( renderer: &mut R, @@ -174,7 +171,8 @@ where impl Element for ClippedSurfaceRenderElement where - R: Renderer + ImportAll + ImportMem, + R: Renderer + ImportAll + AsGlowRenderer, + R::TextureId: 'static, { fn id(&self) -> &Id { self.inner.id() @@ -247,7 +245,7 @@ where impl RenderElement for ClippedSurfaceRenderElement where - R: AsGlowRenderer + Renderer + ImportAll + ImportMem, + R: AsGlowRenderer + Renderer + ImportAll, R::TextureId: 'static, { fn draw( diff --git a/src/backend/render/wayland/mod.rs b/src/backend/render/wayland/mod.rs new file mode 100644 index 00000000..64c5945f --- /dev/null +++ b/src/backend/render/wayland/mod.rs @@ -0,0 +1,111 @@ +use smithay::{ + backend::renderer::{ + ImportAll, Renderer, + element::surface::{KindEvaluation, WaylandSurfaceRenderElement}, + utils::RendererSurfaceStateUserData, + }, + reexports::wayland_server::protocol::wl_surface, + render_elements, + utils::{Logical, Physical, Point, Rectangle, Scale}, + wayland::compositor::{self, TraversalAction}, +}; +use tracing::warn; + +use crate::backend::render::{ + element::AsGlowRenderer, wayland::clipped_surface::ClippedSurfaceRenderElement, +}; + +pub mod clipped_surface; + +render_elements! { + pub SurfaceRenderElement where R: AsGlowRenderer + ImportAll; + Clipped=ClippedSurfaceRenderElement, + Wayland=WaylandSurfaceRenderElement, +} + +pub fn render_elements_from_surface_tree( + renderer: &mut R, + main_surface: &wl_surface::WlSurface, + location: impl Into>, + geometry: impl Into>, + scale: impl Into>, + alpha: f32, + should_clip: bool, + radii: [u8; 4], + kind: impl Into, +) -> Vec +where + R: Renderer + ImportAll + AsGlowRenderer, + R::TextureId: Clone + 'static, + E: From>, +{ + let location = location.into().to_f64(); + let geometry = geometry.into().to_f64(); + let scale = scale.into(); + let kind = kind.into(); + let mut surfaces: Vec = Vec::new(); + + compositor::with_surface_tree_downward( + main_surface, + location, + |_, states, location| { + let mut location = *location; + let data = states.data_map.get::(); + + if let Some(data) = data { + if let Some(view) = data.lock().unwrap().view() { + location += view.offset.to_f64().to_physical(scale); + TraversalAction::DoChildren(location) + } else { + TraversalAction::SkipChildren + } + } else { + TraversalAction::SkipChildren + } + }, + |surface, states, location| { + let mut location = *location; + let kind = kind.eval(states); + let data = states.data_map.get::(); + + if let Some(data) = data { + let has_view = if let Some(view) = data.lock().unwrap().view() { + location += view.offset.to_f64().to_physical(scale); + + true + } else { + false + }; + + if has_view { + match WaylandSurfaceRenderElement::from_surface( + renderer, surface, states, location, alpha, kind, + ) { + Ok(Some(surface)) => { + let elem: SurfaceRenderElement = if radii.iter().any(|r| *r != 0) + && should_clip + && ClippedSurfaceRenderElement::will_clip( + &surface, scale, geometry, radii, + ) { + ClippedSurfaceRenderElement::new( + renderer, surface, scale, geometry, radii, + ) + .into() + } else { + surface.into() + }; + surfaces.push(elem.into()); + } + Ok(None) => {} // surface is not mapped + Err(err) => { + warn!("Failed to import surface: {}", err); + } + }; + } + } + }, + |_, _, _| true, + ); + + surfaces +} diff --git a/src/shell/element/stack.rs b/src/shell/element/stack.rs index f3ae21ad..fe25a4e6 100644 --- a/src/shell/element/stack.rs +++ b/src/shell/element/stack.rs @@ -4,8 +4,8 @@ use super::{ }; use crate::{ backend::render::{ - IndicatorShader, Key, Usage, clipped_surface::ClippedSurfaceRenderElement, - cursor::CursorState, element::AsGlowRenderer, shadow::ShadowShader, + IndicatorShader, Key, Usage, cursor::CursorState, element::AsGlowRenderer, + shadow::ShadowShader, wayland::SurfaceRenderElement, }, hooks::{Decorations, HOOKS}, shell::{ @@ -41,7 +41,6 @@ use smithay::{ element::{ AsRenderElements, Element, Id as RendererId, Kind, RenderElement, UnderlyingStorage, memory::MemoryRenderBufferRenderElement, - surface::WaylandSurfaceRenderElement, }, gles::element::PixelShaderElement, glow::GlowRenderer, @@ -820,32 +819,16 @@ impl CosmicStack { }); border.into_iter().chain( - windows[active] - .render_elements::>( - renderer, - window_loc, - scale, - alpha, - scanout_override, - scanout_node, - ) - .into_iter() - .map(move |elem| { - let radii = radii.map(|[a, _, c, _]| [a, 0, c, 0]); - if radii.is_some_and(|radii| { - ClippedSurfaceRenderElement::will_clip(&elem, scale, geo, radii) - }) { - CosmicStackRenderElement::Clipped(ClippedSurfaceRenderElement::new( - renderer, - elem, - scale, - geo, - radii.unwrap(), - )) - } else { - CosmicStackRenderElement::Window(elem) - } - }), + windows[active].render_elements::>( + renderer, + window_loc, + scale, + alpha, + scanout_override, + scanout_node, + radii.is_some(), + radii.unwrap_or([0; 4]), + ), ) })); @@ -1885,8 +1868,7 @@ pub enum CosmicStackRenderElement { Header(MemoryRenderBufferRenderElement), Shadow(PixelShaderElement), Border(PixelShaderElement), - Window(WaylandSurfaceRenderElement), - Clipped(ClippedSurfaceRenderElement), + Window(SurfaceRenderElement), } impl From> @@ -1897,25 +1879,18 @@ impl From From> +impl From> for CosmicStackRenderElement { - fn from(value: WaylandSurfaceRenderElement) -> Self { + fn from(value: SurfaceRenderElement) -> Self { Self::Window(value) } } -impl From> - for CosmicStackRenderElement -{ - fn from(value: ClippedSurfaceRenderElement) -> Self { - Self::Clipped(value) - } -} - impl Element for CosmicStackRenderElement where - R: Renderer + ImportAll + ImportMem, + R: Renderer + ImportAll + ImportMem + AsGlowRenderer, + R::TextureId: 'static, { fn id(&self) -> &RendererId { match self { @@ -1923,7 +1898,6 @@ where CosmicStackRenderElement::Shadow(elem) => elem.id(), CosmicStackRenderElement::Border(elem) => elem.id(), CosmicStackRenderElement::Window(elem) => elem.id(), - CosmicStackRenderElement::Clipped(elem) => elem.id(), } } @@ -1933,7 +1907,6 @@ where CosmicStackRenderElement::Shadow(elem) => elem.current_commit(), CosmicStackRenderElement::Border(elem) => elem.current_commit(), CosmicStackRenderElement::Window(elem) => elem.current_commit(), - CosmicStackRenderElement::Clipped(elem) => elem.current_commit(), } } @@ -1943,7 +1916,6 @@ where CosmicStackRenderElement::Shadow(elem) => elem.src(), CosmicStackRenderElement::Border(elem) => elem.src(), CosmicStackRenderElement::Window(elem) => elem.src(), - CosmicStackRenderElement::Clipped(elem) => elem.src(), } } @@ -1953,7 +1925,6 @@ where CosmicStackRenderElement::Shadow(elem) => elem.geometry(scale), CosmicStackRenderElement::Border(elem) => elem.geometry(scale), CosmicStackRenderElement::Window(elem) => elem.geometry(scale), - CosmicStackRenderElement::Clipped(elem) => elem.geometry(scale), } } @@ -1963,7 +1934,6 @@ where CosmicStackRenderElement::Shadow(elem) => elem.location(scale), CosmicStackRenderElement::Border(elem) => elem.location(scale), CosmicStackRenderElement::Window(elem) => elem.location(scale), - CosmicStackRenderElement::Clipped(elem) => elem.location(scale), } } @@ -1973,7 +1943,6 @@ where CosmicStackRenderElement::Shadow(elem) => elem.transform(), CosmicStackRenderElement::Border(elem) => elem.transform(), CosmicStackRenderElement::Window(elem) => elem.transform(), - CosmicStackRenderElement::Clipped(elem) => elem.transform(), } } @@ -1987,7 +1956,6 @@ where CosmicStackRenderElement::Shadow(elem) => elem.damage_since(scale, commit), CosmicStackRenderElement::Border(elem) => elem.damage_since(scale, commit), CosmicStackRenderElement::Window(elem) => elem.damage_since(scale, commit), - CosmicStackRenderElement::Clipped(elem) => elem.damage_since(scale, commit), } } @@ -1997,7 +1965,6 @@ where CosmicStackRenderElement::Shadow(elem) => elem.opaque_regions(scale), CosmicStackRenderElement::Border(elem) => elem.opaque_regions(scale), CosmicStackRenderElement::Window(elem) => elem.opaque_regions(scale), - CosmicStackRenderElement::Clipped(elem) => elem.opaque_regions(scale), } } @@ -2007,7 +1974,6 @@ where CosmicStackRenderElement::Shadow(elem) => elem.alpha(), CosmicStackRenderElement::Border(elem) => elem.alpha(), CosmicStackRenderElement::Window(elem) => elem.alpha(), - CosmicStackRenderElement::Clipped(elem) => elem.alpha(), } } @@ -2017,7 +1983,6 @@ where CosmicStackRenderElement::Shadow(elem) => elem.kind(), CosmicStackRenderElement::Border(elem) => elem.kind(), CosmicStackRenderElement::Window(elem) => elem.kind(), - CosmicStackRenderElement::Clipped(elem) => elem.kind(), } } @@ -2027,7 +1992,6 @@ where CosmicStackRenderElement::Shadow(elem) => elem.is_framebuffer_effect(), CosmicStackRenderElement::Border(elem) => elem.is_framebuffer_effect(), CosmicStackRenderElement::Window(elem) => elem.is_framebuffer_effect(), - CosmicStackRenderElement::Clipped(elem) => elem.is_framebuffer_effect(), } } } @@ -2039,7 +2003,7 @@ where { fn draw( &self, - frame: &mut ::Frame<'_, '_>, + frame: &mut R::Frame<'_, '_>, src: Rectangle, dst: Rectangle, damage: &[Rectangle], @@ -2065,9 +2029,6 @@ where CosmicStackRenderElement::Window(elem) => { elem.draw(frame, src, dst, damage, opaque_regions, cache) } - CosmicStackRenderElement::Clipped(elem) => { - elem.draw(frame, src, dst, damage, opaque_regions, cache) - } } } @@ -2078,7 +2039,6 @@ where elem.underlying_storage(renderer.glow_renderer_mut()) } CosmicStackRenderElement::Window(elem) => elem.underlying_storage(renderer), - CosmicStackRenderElement::Clipped(elem) => elem.underlying_storage(renderer), } } @@ -2106,9 +2066,6 @@ where CosmicStackRenderElement::Window(elem) => { elem.capture_framebuffer(frame, src, dst, cache) } - CosmicStackRenderElement::Clipped(elem) => { - elem.capture_framebuffer(frame, src, dst, cache) - } } } } diff --git a/src/shell/element/surface.rs b/src/shell/element/surface.rs index 4838bd61..374b8ea4 100644 --- a/src/shell/element/surface.rs +++ b/src/shell/element/surface.rs @@ -1,4 +1,8 @@ use crate::{ + backend::render::{ + element::AsGlowRenderer, + wayland::{SurfaceRenderElement, render_elements_from_surface_tree}, + }, shell::focus::target::PointerFocusTarget, wayland::{ handlers::compositor::frame_time_filter_fn, protocols::corner_radius::CacheableCorners, @@ -18,12 +22,7 @@ use smithay::{ drm::DrmNode, renderer::{ ImportAll, Renderer, - element::{ - AsRenderElements, Kind, RenderElementStates, - surface::{ - KindEvaluation, WaylandSurfaceRenderElement, render_elements_from_surface_tree, - }, - }, + element::{Kind, RenderElementStates, surface::KindEvaluation}, utils::RendererSurfaceStateUserData, }, }, @@ -869,9 +868,9 @@ impl CosmicSurface { scanout_node: Option, ) -> Vec where - R: Renderer + ImportAll, + R: Renderer + ImportAll + AsGlowRenderer, R::TextureId: Clone + 'static, - C: From>, + C: From>, { match self.0.underlying_surface() { WindowSurface::Wayland(toplevel) => { @@ -880,13 +879,18 @@ impl CosmicSurface { .flat_map(move |(popup, popup_offset)| { let offset = (self.0.geometry().loc + popup_offset - popup.geometry().loc) .to_physical_precise_round(scale); + let mut geometry = popup.geometry().to_f64(); + geometry.loc += location.to_f64().to_logical(scale) + popup_offset.to_f64(); render_elements_from_surface_tree( renderer, popup.wl_surface(), location + offset, + geometry, scale, alpha, + false, + [0; 4], scanout_kind_eval(None, scanout_node), ) }) @@ -904,12 +908,17 @@ impl CosmicSurface { alpha: f32, scanout_override: Option, scanout_node: Option, + should_clip: bool, + radii: [u8; 4], ) -> Vec where - R: Renderer + ImportAll, + R: Renderer + ImportAll + AsGlowRenderer, R::TextureId: Clone + 'static, - C: From>, + C: From>, { + let mut geometry = self.0.geometry().to_f64(); + geometry.loc += location.to_f64().to_logical(scale); + match self.0.underlying_surface() { WindowSurface::Wayland(toplevel) => { let surface = toplevel.wl_surface(); @@ -918,8 +927,11 @@ impl CosmicSurface { renderer, surface, location, + geometry, scale, alpha, + should_clip, + radii, scanout_kind_eval(scanout_override, scanout_node), ) } @@ -932,8 +944,11 @@ impl CosmicSurface { renderer, &surface, location, + geometry, scale, alpha, + should_clip, + radii, scanout_kind_eval(scanout_override, scanout_node), ) } @@ -1078,24 +1093,6 @@ impl X11Relatable for CosmicSurface { } } -impl AsRenderElements for CosmicSurface -where - R: Renderer + ImportAll, - R::TextureId: Clone + 'static, -{ - type RenderElement = WaylandSurfaceRenderElement; - - fn render_elements>( - &self, - renderer: &mut R, - location: Point, - scale: Scale, - alpha: f32, - ) -> Vec { - self.0.render_elements(renderer, location, scale, alpha) - } -} - fn with_toplevel_state) -> T>( toplevel: &ToplevelSurface, pending: bool, diff --git a/src/shell/element/window.rs b/src/shell/element/window.rs index 6ebbafa0..cc12e0f9 100644 --- a/src/shell/element/window.rs +++ b/src/shell/element/window.rs @@ -1,7 +1,7 @@ use crate::{ backend::render::{ - IndicatorShader, Key, Usage, clipped_surface::ClippedSurfaceRenderElement, - cursor::CursorState, element::AsGlowRenderer, shadow::ShadowShader, + IndicatorShader, Key, Usage, cursor::CursorState, element::AsGlowRenderer, + shadow::ShadowShader, wayland::SurfaceRenderElement, }, hooks::{Decorations, HOOKS}, shell::{ @@ -27,7 +27,6 @@ use smithay::{ element::{ AsRenderElements, Element, Id as RendererId, Kind, RenderElement, UnderlyingStorage, memory::MemoryRenderBufferRenderElement, - surface::WaylandSurfaceRenderElement, }, gles::element::PixelShaderElement, glow::GlowRenderer, @@ -358,7 +357,7 @@ impl CosmicWindow { scanout_node: Option, ) -> Vec where - R: Renderer + ImportAll + ImportMem, + R: Renderer + ImportAll + ImportMem + AsGlowRenderer, R::TextureId: Send + Clone + 'static, C: From>, { @@ -549,36 +548,21 @@ impl CosmicWindow { elements.push(elem); } - let window_elements = self.0.with_program(|p| { - p.window - .render_elements::>( - renderer, - window_loc, - scale, - alpha, - scanout_override, - scanout_node, - ) - }); - if window_elements.is_empty() { - return Vec::new(); + if has_ssd { + radii[1] = 0; + radii[3] = 0; } - - elements.extend(window_elements.into_iter().map(|elem| { - if has_ssd { - radii[1] = 0; - radii[3] = 0; - } - if radii.iter().any(|x| *x != 0) - && clip - && ClippedSurfaceRenderElement::will_clip(&elem, scale, geo, radii) - { - CosmicWindowRenderElement::Clipped(ClippedSurfaceRenderElement::new( - renderer, elem, scale, geo, radii, - )) - } else { - CosmicWindowRenderElement::Window(elem) - } + elements.extend(self.0.with_program(|p| { + p.window.render_elements::>( + renderer, + window_loc, + scale, + alpha, + scanout_override, + scanout_node, + clip, + radii, + ) })); if has_ssd { @@ -1252,8 +1236,7 @@ pub enum CosmicWindowRenderElement { Header(MemoryRenderBufferRenderElement), Shadow(PixelShaderElement), Border(PixelShaderElement), - Window(WaylandSurfaceRenderElement), - Clipped(ClippedSurfaceRenderElement), + Window(SurfaceRenderElement), } impl From> @@ -1264,25 +1247,18 @@ impl From From> +impl From> for CosmicWindowRenderElement { - fn from(value: WaylandSurfaceRenderElement) -> Self { + fn from(value: SurfaceRenderElement) -> Self { Self::Window(value) } } -impl From> - for CosmicWindowRenderElement -{ - fn from(value: ClippedSurfaceRenderElement) -> Self { - Self::Clipped(value) - } -} - impl Element for CosmicWindowRenderElement where - R: Renderer + ImportAll + ImportMem, + R: Renderer + ImportAll + ImportMem + AsGlowRenderer, + R::TextureId: 'static, { fn id(&self) -> &RendererId { match self { @@ -1290,7 +1266,6 @@ where CosmicWindowRenderElement::Shadow(elem) => elem.id(), CosmicWindowRenderElement::Border(elem) => elem.id(), CosmicWindowRenderElement::Window(elem) => elem.id(), - CosmicWindowRenderElement::Clipped(elem) => elem.id(), } } @@ -1300,7 +1275,6 @@ where CosmicWindowRenderElement::Shadow(elem) => elem.current_commit(), CosmicWindowRenderElement::Border(elem) => elem.current_commit(), CosmicWindowRenderElement::Window(elem) => elem.current_commit(), - CosmicWindowRenderElement::Clipped(elem) => elem.current_commit(), } } @@ -1310,7 +1284,6 @@ where CosmicWindowRenderElement::Shadow(elem) => elem.src(), CosmicWindowRenderElement::Border(elem) => elem.src(), CosmicWindowRenderElement::Window(elem) => elem.src(), - CosmicWindowRenderElement::Clipped(elem) => elem.src(), } } @@ -1320,7 +1293,6 @@ where CosmicWindowRenderElement::Shadow(elem) => elem.geometry(scale), CosmicWindowRenderElement::Border(elem) => elem.geometry(scale), CosmicWindowRenderElement::Window(elem) => elem.geometry(scale), - CosmicWindowRenderElement::Clipped(elem) => elem.geometry(scale), } } @@ -1330,7 +1302,6 @@ where CosmicWindowRenderElement::Shadow(elem) => elem.location(scale), CosmicWindowRenderElement::Border(elem) => elem.location(scale), CosmicWindowRenderElement::Window(elem) => elem.location(scale), - CosmicWindowRenderElement::Clipped(elem) => elem.location(scale), } } @@ -1340,7 +1311,6 @@ where CosmicWindowRenderElement::Shadow(elem) => elem.transform(), CosmicWindowRenderElement::Border(elem) => elem.transform(), CosmicWindowRenderElement::Window(elem) => elem.transform(), - CosmicWindowRenderElement::Clipped(elem) => elem.transform(), } } @@ -1354,7 +1324,6 @@ where CosmicWindowRenderElement::Shadow(elem) => elem.damage_since(scale, commit), CosmicWindowRenderElement::Border(elem) => elem.damage_since(scale, commit), CosmicWindowRenderElement::Window(elem) => elem.damage_since(scale, commit), - CosmicWindowRenderElement::Clipped(elem) => elem.damage_since(scale, commit), } } @@ -1364,7 +1333,6 @@ where CosmicWindowRenderElement::Shadow(elem) => elem.opaque_regions(scale), CosmicWindowRenderElement::Border(elem) => elem.opaque_regions(scale), CosmicWindowRenderElement::Window(elem) => elem.opaque_regions(scale), - CosmicWindowRenderElement::Clipped(elem) => elem.opaque_regions(scale), } } @@ -1374,7 +1342,6 @@ where CosmicWindowRenderElement::Shadow(elem) => elem.alpha(), CosmicWindowRenderElement::Border(elem) => elem.alpha(), CosmicWindowRenderElement::Window(elem) => elem.alpha(), - CosmicWindowRenderElement::Clipped(elem) => elem.alpha(), } } @@ -1384,7 +1351,6 @@ where CosmicWindowRenderElement::Shadow(elem) => elem.kind(), CosmicWindowRenderElement::Border(elem) => elem.kind(), CosmicWindowRenderElement::Window(elem) => elem.kind(), - CosmicWindowRenderElement::Clipped(elem) => elem.kind(), } } @@ -1394,7 +1360,6 @@ where CosmicWindowRenderElement::Shadow(elem) => elem.is_framebuffer_effect(), CosmicWindowRenderElement::Border(elem) => elem.is_framebuffer_effect(), CosmicWindowRenderElement::Window(elem) => elem.is_framebuffer_effect(), - CosmicWindowRenderElement::Clipped(elem) => elem.is_framebuffer_effect(), } } } @@ -1432,9 +1397,6 @@ where CosmicWindowRenderElement::Window(elem) => { elem.draw(frame, src, dst, damage, opaque_regions, cache) } - CosmicWindowRenderElement::Clipped(elem) => { - elem.draw(frame, src, dst, damage, opaque_regions, cache) - } } } @@ -1445,7 +1407,6 @@ where elem.underlying_storage(renderer.glow_renderer_mut()) } CosmicWindowRenderElement::Window(elem) => elem.underlying_storage(renderer), - CosmicWindowRenderElement::Clipped(elem) => elem.underlying_storage(renderer), } } @@ -1473,9 +1434,6 @@ where CosmicWindowRenderElement::Window(elem) => { elem.capture_framebuffer(frame, src, dst, cache) } - CosmicWindowRenderElement::Clipped(elem) => { - elem.capture_framebuffer(frame, src, dst, cache) - } } } } diff --git a/src/shell/layout/tiling/mod.rs b/src/shell/layout/tiling/mod.rs index 1c48ab73..14a65ea7 100644 --- a/src/shell/layout/tiling/mod.rs +++ b/src/shell/layout/tiling/mod.rs @@ -5395,29 +5395,33 @@ where (swap_geo.loc.as_logical() - window_geo.loc).to_physical_precise_round(output_scale); swap_elements.extend( - AsRenderElements::render_elements::>( - &window, - renderer, - render_loc, - output_scale.into(), - 1.0, - ) - .into_iter() - .map(|window| { - CosmicMappedRenderElement::GrabbedWindow(RescaleRenderElement::from_element( - window, - swap_geo - .loc - .as_logical() - .to_physical_precise_round(output_scale), - ease( - Linear, - 1.0, - swap_factor(window_geo.size), - transition.unwrap_or(1.0), - ), - )) - }), + window + .render_elements( + renderer, + render_loc, + output_scale.into(), + 1.0, + None, + scanout_node, + false, + [0; 4], + ) + .into_iter() + .map(|window| { + CosmicMappedRenderElement::GrabbedWindow(RescaleRenderElement::from_element( + window, + swap_geo + .loc + .as_logical() + .to_physical_precise_round(output_scale), + ease( + Linear, + 1.0, + swap_factor(window_geo.size), + transition.unwrap_or(1.0), + ), + )) + }), ) } diff --git a/src/shell/workspace.rs b/src/shell/workspace.rs index 18ec24cc..c5f5cb90 100644 --- a/src/shell/workspace.rs +++ b/src/shell/workspace.rs @@ -1711,6 +1711,8 @@ impl Workspace { alpha, Some(true), scanout_node, + false, + [0; 4], ) .into_iter() .map(animation_rescale) diff --git a/src/utils/screenshot.rs b/src/utils/screenshot.rs index e90fb532..541371cf 100644 --- a/src/utils/screenshot.rs +++ b/src/utils/screenshot.rs @@ -3,9 +3,7 @@ use smithay::{ backend::{ allocator::Fourcc, renderer::{ - ExportMem, ImportAll, Offscreen, Renderer, - damage::OutputDamageTracker, - element::{AsRenderElements, surface::WaylandSurfaceRenderElement}, + ExportMem, ImportAll, Offscreen, Renderer, damage::OutputDamageTracker, gles::GlesRenderbuffer, }, }, @@ -16,7 +14,7 @@ use smithay::{ use tracing::warn; use crate::{ - backend::render::RendererRef, + backend::render::{RendererRef, element::AsGlowRenderer, wayland::SurfaceRenderElement}, shell::element::CosmicSurface, state::{State, advertised_node_for_surface}, }; @@ -24,17 +22,20 @@ use crate::{ pub fn screenshot_window(state: &mut State, surface: &CosmicSurface) { fn render_window(renderer: &mut R, window: &CosmicSurface) -> anyhow::Result<()> where - R: Renderer + ImportAll + Offscreen + ExportMem, + R: Renderer + ImportAll + Offscreen + ExportMem + AsGlowRenderer, R::TextureId: Clone + 'static, R::Error: Send + Sync + 'static, { let bbox = bbox_from_surface_tree(&window.wl_surface().unwrap(), (0, 0)); - let elements = AsRenderElements::::render_elements::>( - window, + let elements = window.render_elements::>( renderer, (-bbox.loc.x, -bbox.loc.y).into(), Scale::from(1.0), 1.0, + None, + None, + false, + [0; 4], ); // TODO: 10-bit diff --git a/src/wayland/handlers/image_copy_capture/render.rs b/src/wayland/handlers/image_copy_capture/render.rs index aa358b7f..c640a6d7 100644 --- a/src/wayland/handlers/image_copy_capture/render.rs +++ b/src/wayland/handlers/image_copy_capture/render.rs @@ -9,8 +9,7 @@ use smithay::{ buffer_dimensions, buffer_type, damage::{Error as DTError, OutputDamageTracker, RenderOutputResult}, element::{ - AsRenderElements, RenderElement, - surface::WaylandSurfaceRenderElement, + RenderElement, utils::{Relocate, RelocateRenderElement}, }, gles::{GlesError, GlesRenderbuffer}, @@ -43,6 +42,7 @@ use crate::{ CursorMode, ElementFilter, RendererRef, cursor, element::{AsGlowRenderer, CosmicElement, DamageElement}, render_workspace, + wayland::SurfaceRenderElement, }, shell::{CosmicMappedRenderElement, CosmicSurface, WorkspaceRenderElement}, state::{Common, KmsNodes, State}, @@ -501,8 +501,8 @@ pub fn render_workspace_to_buffer( } smithay::render_elements! { - pub WindowCaptureElement where R: ImportAll + ImportMem; - WaylandElement=WaylandSurfaceRenderElement, + pub WindowCaptureElement where R: ImportAll + ImportMem + AsGlowRenderer; + WaylandElement=SurfaceRenderElement, CursorElement=RelocateRenderElement>, } @@ -628,14 +628,15 @@ pub fn render_window_to_buffer( } } - elements.extend(AsRenderElements::::render_elements::< - WindowCaptureElement, - >( - toplevel, + elements.extend(toplevel.render_elements::>( renderer, (-geometry.loc.x, -geometry.loc.y).into(), Scale::from(1.0), 1.0, + None, + None, + false, + [0; 4], )); if let Ok(dmabuf) = get_dmabuf(buffer) { From e4c0716951fd69984c582720c4c4572c327b2493 Mon Sep 17 00:00:00 2001 From: Victoria Brekenfeld Date: Wed, 4 Mar 2026 18:57:37 +0100 Subject: [PATCH 22/92] element: Refactor element acquisition to be push based --- Cargo.lock | 5 +- Cargo.toml | 1 + src/backend/render/cursor.rs | 41 +- src/backend/render/mod.rs | 356 ++++++------ src/backend/render/wayland/mod.rs | 24 +- src/shell/element/mod.rs | 91 ++- src/shell/element/stack.rs | 86 ++- src/shell/element/surface.rs | 67 +-- src/shell/element/window.rs | 69 ++- src/shell/grabs/menu/mod.rs | 42 +- src/shell/grabs/moving.rs | 231 ++++---- src/shell/layout/floating/mod.rs | 274 +++++---- src/shell/layout/tiling/mod.rs | 521 +++++++++--------- src/shell/workspace.rs | 283 +++++----- src/shell/zoom.rs | 30 +- src/utils/iced/mod.rs | 22 +- src/utils/screenshot.rs | 7 +- .../handlers/image_copy_capture/render.rs | 83 +-- 18 files changed, 1096 insertions(+), 1137 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index 9f664669..9030c5e0 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -841,6 +841,7 @@ dependencies = [ "sanitize-filename", "serde", "serde_json", + "smallvec", "smithay", "smithay-egui", "thiserror 2.0.18", @@ -4790,9 +4791,9 @@ dependencies = [ [[package]] name = "smallvec" -version = "1.15.1" +version = "1.15.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "67b1b7a3b5fe4f1376887184045fcf45c69e92af734b7aaddc05fb777b6fbd03" +checksum = "8ed6a63f02c8539c91a8685a86f4099661ba3da017932f6ebbea6de3f0fa7c90" [[package]] name = "smithay" diff --git a/Cargo.toml b/Cargo.toml index f1a04f02..a8c2dc2f 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -83,6 +83,7 @@ logind-zbus = { version = "5.3.2", optional = true } futures-executor = { version = "0.3.32" } futures-util = "0.3.32" cgmath = "0.18.0" +smallvec = "1.15.2" [dependencies.id_tree] branch = "feature/copy_clone" diff --git a/src/backend/render/cursor.rs b/src/backend/render/cursor.rs index 8373e714..e43d99e5 100644 --- a/src/backend/render/cursor.rs +++ b/src/backend/render/cursor.rs @@ -3,7 +3,7 @@ use crate::{ backend::render::{ element::AsGlowRenderer, - wayland::{SurfaceRenderElement, render_elements_from_surface_tree}, + wayland::{SurfaceRenderElement, push_render_elements_from_surface_tree}, }, utils::prelude::*, wayland::handlers::compositor::FRAME_TIME_FILTER, @@ -185,8 +185,8 @@ pub fn draw_surface_cursor( surface: &wl_surface::WlSurface, location: Point, scale: impl Into>, -) -> Vec<(CursorRenderElement, Point)> -where + push: &mut dyn FnMut(CursorRenderElement, Point), +) where R: Renderer + ImportAll + AsGlowRenderer, R::TextureId: Clone + 'static, { @@ -202,7 +202,7 @@ where .to_physical_precise_round(scale) }); - render_elements_from_surface_tree( + push_render_elements_from_surface_tree( renderer, surface, location.to_physical(scale).to_i32_round(), @@ -212,10 +212,9 @@ where false, [0; 4], Kind::Cursor, - ) - .into_iter() - .map(|elem| (elem, h)) - .collect() + &mut |elem| push(elem.into(), h), + None, + ); } #[profiling::function] @@ -224,8 +223,8 @@ pub fn draw_dnd_icon( surface: &wl_surface::WlSurface, location: Point, scale: impl Into>, -) -> Vec> -where + push: &mut dyn FnMut(SurfaceRenderElement), +) where R: Renderer + ImportAll + AsGlowRenderer, R::TextureId: Clone + 'static, { @@ -236,7 +235,7 @@ where ); } let scale = scale.into(); - render_elements_from_surface_tree( + push_render_elements_from_surface_tree( renderer, surface, location.to_physical(scale).to_i32_round(), @@ -246,7 +245,9 @@ where false, [0; 4], FRAME_TIME_FILTER, - ) + push, + None, + ); } pub type CursorState = Mutex; @@ -330,8 +331,8 @@ pub fn draw_cursor( buffer_scale: f64, time: Time, draw_default: bool, -) -> Vec<(CursorRenderElement, Point)> -where + push: &mut dyn FnMut(CursorRenderElement, Point), +) where R: Renderer + ImportMem + ImportAll + AsGlowRenderer, R::TextureId: Send + Clone + 'static, { @@ -343,7 +344,7 @@ where let state = &mut *state_ref; if state.hidden { - return Vec::new(); + return; } let named_cursor = state.current_cursor.or(match cursor_status { @@ -352,7 +353,7 @@ where }); if let Some(current_cursor) = named_cursor { if !draw_default && current_cursor == CursorIcon::Default { - return Vec::new(); + return; } let integer_scale = (scale.x.max(scale.y) * buffer_scale).ceil() as u32; @@ -389,7 +390,7 @@ where ); state.current_image = Some(frame); - return vec![( + push( CursorRenderElement::Static( MemoryRenderBufferRenderElement::from_buffer( renderer, @@ -403,11 +404,9 @@ where .expect("Failed to import cursor bitmap"), ), hotspot.to_physical_precise_round(scale), - )]; + ); } else if let CursorImageStatus::Surface(ref wl_surface) = cursor_status { - return draw_surface_cursor(renderer, wl_surface, location, scale); - } else { - Vec::new() + draw_surface_cursor(renderer, wl_surface, location, scale, push); } } diff --git a/src/backend/render/mod.rs b/src/backend/render/mod.rs index 458000a5..717fe54e 100644 --- a/src/backend/render/mod.rs +++ b/src/backend/render/mod.rs @@ -17,7 +17,11 @@ use crate::{ render::{ element::DamageElement, shadow::{SHADOW_SHADER, ShadowShader}, - wayland::clipped_surface::{CLIPPING_SHADER, ClippingShader}, + wayland::{ + SurfaceRenderElement, + clipped_surface::{CLIPPING_SHADER, ClippingShader}, + push_render_elements_from_surface_tree, + }, }, }, config::ScreenFilter, @@ -47,11 +51,10 @@ use smithay::{ allocator::Fourcc, drm::{DrmDeviceFd, DrmNode}, renderer::{ - Color32F, ImportAll, Offscreen, Renderer, Texture, TextureFilter, + Color32F, Offscreen, Texture, TextureFilter, damage::{Error as RenderError, OutputDamageTracker, RenderOutputResult}, element::{ Element, Id, Kind, RenderElement, WeakId, - surface::{WaylandSurfaceRenderElement, render_elements_from_surface_tree}, texture::{TextureRenderBuffer, TextureRenderElement}, utils::{ ConstrainAlign, ConstrainScaleBehavior, CropRenderElement, Relocate, @@ -68,6 +71,7 @@ use smithay::{ sync::SyncPoint, }, }, + desktop::utils::bbox_from_surface_tree, input::Seat, output::{Output, OutputModeSource, OutputNoMode}, utils::{ @@ -475,8 +479,8 @@ pub fn cursor_elements<'a, 'frame, R>( mode: CursorMode, exclude_dnd_icon: bool, scanout_node: Option, -) -> Vec> -where + push: &mut dyn FnMut(CosmicElement), +) where R: AsGlowRenderer, R::TextureId: Send + Clone + 'static, CosmicMappedRenderElement: RenderElement, @@ -490,7 +494,6 @@ where ) }) .unwrap_or_else(|| ((0., 0.).into(), 1.)); - let mut elements = Vec::new(); for seat in seats { let pointer = match seat.get_pointer() { @@ -500,19 +503,16 @@ where let location = pointer.current_location() - output.current_location().to_f64(); if mode != CursorMode::None { - elements.extend( - cursor::draw_cursor( - renderer, - seat, - location, - scale.into(), - zoom_scale, - now, - mode != CursorMode::NotDefault, - ) - .into_iter() - .map(|(elem, hotspot)| { - CosmicElement::Cursor(RescaleRenderElement::from_element( + cursor::draw_cursor( + renderer, + seat, + location, + scale.into(), + zoom_scale, + now, + mode != CursorMode::NotDefault, + &mut |elem, hotspot| { + push(CosmicElement::Cursor(RescaleRenderElement::from_element( RelocateRenderElement::from_element( elem, Point::from((-hotspot.x, -hotspot.y)), @@ -523,70 +523,54 @@ where .to_physical(output.current_scale().fractional_scale()) .to_i32_round(), zoom_scale, - )) - }), + ))) + }, ); } if !exclude_dnd_icon && let Some(dnd_icon) = get_dnd_icon(seat) { - elements.extend( - cursor::draw_dnd_icon( - renderer, - &dnd_icon.surface, - (location + dnd_icon.offset.to_f64()).to_i32_round(), - scale, - ) - .into_iter() - .map(CosmicElement::Dnd), + cursor::draw_dnd_icon( + renderer, + &dnd_icon.surface, + (location + dnd_icon.offset.to_f64()).to_i32_round(), + scale, + &mut |elem| push(CosmicElement::Dnd(elem)), ); } let theme = theme.cosmic(); - if let Some(grab_elements) = seat + if let Some(grab_state) = seat .user_data() .get::() .unwrap() .lock() .unwrap() .as_ref() - .map(|state| { - state.render::, R>( - renderer, - output, - theme, - scanout_node, - ) - }) { - elements.extend(grab_elements.into_iter().map(|elem| { - CosmicElement::MoveGrab(RescaleRenderElement::from_element( + grab_state.render(renderer, output, theme, scanout_node, &mut |elem| { + push(CosmicElement::MoveGrab(RescaleRenderElement::from_element( elem, focal_point .as_logical() .to_physical(output.current_scale().fractional_scale()) .to_i32_round(), zoom_scale, - )) - })); + ))); + }) } - if let Some((grab_elements, should_scale)) = seat + if let Some(grab_state) = seat .user_data() .get::() .unwrap() .lock() .unwrap() .as_ref() - .map(|state| { - ( - state.render::, R>(renderer, output), - !state.is_in_screen_space(), - ) - }) { - elements.extend(grab_elements.into_iter().map(|elem| { - CosmicElement::MoveGrab(RescaleRenderElement::from_element( - elem, + let should_scale = !grab_state.is_in_screen_space(); + grab_state.render(renderer, output, &mut |elem| { + push(CosmicElement::MoveGrab(RescaleRenderElement::from_element( + elem.into(), if should_scale { focal_point .as_logical() @@ -596,12 +580,10 @@ where Point::from((0, 0)) }, if should_scale { zoom_scale } else { 1.0 }, - )) - })); + ))); + }) } } - - elements } #[cfg(not(feature = "debug"))] @@ -729,7 +711,7 @@ where CosmicMappedRenderElement: RenderElement, WorkspaceRenderElement: RenderElement, { - let mut elements = Vec::new(); + let mut elements = Vec::>::new(); let shell_ref = shell.read(); let seats = shell_ref.seats.iter().cloned().collect::>(); @@ -742,7 +724,7 @@ where // that is prone to deadlock with the main-thread on some grabs. std::mem::drop(shell_ref); - elements.extend(cursor_elements( + cursor_elements( renderer, seats.iter(), zoom_level, @@ -752,7 +734,8 @@ where cursor_mode, element_filter == ElementFilter::ExcludeWorkspaceOverview, scanout_node, - )); + &mut |elem| elements.push(elem), + ); let shell = shell.read(); let overview = shell.overview_mode(); @@ -828,72 +811,81 @@ where render_input_order::<()>(&shell, output, previous, current, element_filter, |stage| { match stage { Stage::ZoomUI => { - elements.extend(ZoomState::render(renderer, output)); + ZoomState::render(renderer, output, &mut |elem| { + elements.push(CosmicElement::Zoom(elem)) + }); } Stage::SessionLock(lock_surface) => { - elements.extend( - session_lock_elements(renderer, output, lock_surface) - .into_iter() - .map(Into::into) - .flat_map(crop_to_output) - .map(Into::into), - ); + session_lock_elements(renderer, output, lock_surface, &mut |elem| { + elements.extend(crop_to_output(elem.into()).map(Into::into)) + }) } Stage::LayerPopup { popup, location, .. } => { - elements.extend( - render_elements_from_surface_tree::<_, WorkspaceRenderElement<_>>( - renderer, - popup.wl_surface(), - location - .to_local(output) - .as_logical() - .to_physical_precise_round(scale), - Scale::from(scale), - 1.0, - FRAME_TIME_FILTER, - ) - .into_iter() - .flat_map(crop_to_output) - .map(Into::into), - ); + let mut geometry = popup.geometry().as_global(); + geometry.loc += location; + + push_render_elements_from_surface_tree( + renderer, + popup.wl_surface(), + location + .to_local(output) + .as_logical() + .to_physical_precise_round(scale), + geometry.to_local(output).as_logical().to_f64(), + Scale::from(scale), + 1.0, + false, + [0; 4], + FRAME_TIME_FILTER, + &mut |elem| elements.extend(crop_to_output(elem.into()).map(Into::into)), + None, + ) } Stage::LayerSurface { layer, location } => { - elements.extend( - render_elements_from_surface_tree::<_, WorkspaceRenderElement<_>>( - renderer, - layer.wl_surface(), - location - .to_local(output) - .as_logical() - .to_physical_precise_round(scale), - Scale::from(scale), - 1.0, - FRAME_TIME_FILTER, - ) - .into_iter() - .flat_map(crop_to_output) - .map(Into::into), + let mut geometry = layer.geometry().as_global(); + geometry.loc += location; + + push_render_elements_from_surface_tree( + renderer, + layer.wl_surface(), + location + .to_local(output) + .as_logical() + .to_physical_precise_round(scale), + geometry.to_local(output).as_logical().to_f64(), + Scale::from(scale), + 1.0, + false, + [0; 4], + FRAME_TIME_FILTER, + &mut |elem| elements.extend(crop_to_output(elem.into()).map(Into::into)), + None, ); } Stage::OverrideRedirect { surface, location } => { - elements.extend(surface.wl_surface().into_iter().flat_map(|surface| { - render_elements_from_surface_tree::<_, WorkspaceRenderElement<_>>( + if let Some(wl_surface) = surface.wl_surface() { + let mut geometry = surface.geometry().as_global(); + geometry.loc += location; + + push_render_elements_from_surface_tree( renderer, - &surface, + &wl_surface, location .to_local(output) .as_logical() .to_physical_precise_round(scale), + geometry.to_local(output).as_logical().to_f64(), Scale::from(scale), 1.0, + false, + [0; 4], FRAME_TIME_FILTER, - ) - .into_iter() - .flat_map(crop_to_output) - .map(Into::into) - })); + &mut |elem| elements.extend(crop_to_output(elem.into()).map(Into::into)), + None, + ); + } } Stage::StickyPopups(layout) => { let alpha = match &overview.0 { @@ -914,14 +906,11 @@ where OverviewMode::None => 1.0, }; - elements.extend( - layout - .render_popups(renderer, alpha, scanout_node) - .into_iter() - .map(Into::into) - .flat_map(crop_to_output) - .map(Into::into), - ); + layout.render_popups(renderer, alpha, scanout_node, &mut |elem| { + if let Some(elem) = crop_to_output(elem.into()) { + elements.push(elem.into()) + } + }); } Stage::Sticky(layout) => { let alpha = match &overview.0 { @@ -946,82 +935,66 @@ where .then_some(last_active_seat) .map(|seat| workspace.focus_stack.get(seat)); - elements.extend( - layout - .render( - renderer, - current_focus.as_ref().and_then(|stack| { - stack.last().and_then(|t| match t { - FocusTarget::Window(w) => Some(w), - _ => None, - }) - }), - resize_indicator.clone(), - active_hint, - alpha, - theme.cosmic(), - scanout_node, - ) - .into_iter() - .map(Into::into) - .flat_map(crop_to_output) - .map(Into::into), - ) + layout.render( + renderer, + current_focus.as_ref().and_then(|stack| { + stack.last().and_then(|t| match t { + FocusTarget::Window(w) => Some(w), + _ => None, + }) + }), + resize_indicator.clone(), + active_hint, + alpha, + theme.cosmic(), + scanout_node, + &mut |elem| { + if let Some(elem) = crop_to_output(elem.into()) { + elements.push(elem.into()) + } + }, + ); } Stage::WorkspacePopups { workspace, offset } => { - elements.extend( - match workspace.render_popups( - renderer, - last_active_seat, - !move_active && is_active_space, - overview.clone(), - theme.cosmic(), - scanout_node, - ) { - Ok(elements) => { - elements - .into_iter() - .flat_map(crop_to_output) - .map(|element| { - CosmicElement::Workspace(RelocateRenderElement::from_element( - element, - offset.to_physical_precise_round(scale), - Relocate::Relative, - )) - }) - } - Err(_) => { - return ControlFlow::Break(Err(OutputNoMode)); + workspace.render_popups( + renderer, + last_active_seat, + !move_active && is_active_space, + overview.clone(), + theme.cosmic(), + scanout_node, + &mut |elem| { + if let Some(elem) = crop_to_output(elem) { + elements.push(CosmicElement::Workspace( + RelocateRenderElement::from_element( + elem, + offset.to_physical_precise_round(scale), + Relocate::Relative, + ), + )); } }, ); } Stage::Workspace { workspace, offset } => { - elements.extend( - match workspace.render( - renderer, - last_active_seat, - !move_active && is_active_space, - overview.clone(), - resize_indicator.clone(), - active_hint, - theme.cosmic(), - scanout_node, - ) { - Ok(elements) => { - elements - .into_iter() - .flat_map(crop_to_output) - .map(|element| { - CosmicElement::Workspace(RelocateRenderElement::from_element( - element, - offset.to_physical_precise_round(scale), - Relocate::Relative, - )) - }) - } - Err(_) => { - return ControlFlow::Break(Err(OutputNoMode)); + workspace.render( + renderer, + last_active_seat, + !move_active && is_active_space, + overview.clone(), + resize_indicator.clone(), + active_hint, + theme.cosmic(), + scanout_node, + &mut |elem| { + if let Some(elem) = crop_to_output(elem) { + elements.push(CosmicElement::Workspace( + RelocateRenderElement::from_element( + elem, + offset.to_physical_precise_round(scale), + Relocate::Relative, + ), + )); } }, ); @@ -1038,23 +1011,26 @@ fn session_lock_elements( renderer: &mut R, output: &Output, lock_surface: Option<&LockSurface>, -) -> Vec> -where - R: Renderer + ImportAll, + push: &mut dyn FnMut(SurfaceRenderElement), +) where + R: AsGlowRenderer, R::TextureId: Clone + 'static, { if let Some(surface) = lock_surface { let scale = Scale::from(output.current_scale().fractional_scale()); - render_elements_from_surface_tree( + push_render_elements_from_surface_tree( renderer, surface.wl_surface(), (0, 0), + bbox_from_surface_tree(surface.wl_surface(), (0, 0)).to_f64(), scale, 1.0, + false, + [0; 4], FRAME_TIME_FILTER, + push, + None, ) - } else { - Vec::new() } } diff --git a/src/backend/render/wayland/mod.rs b/src/backend/render/wayland/mod.rs index 64c5945f..d766552a 100644 --- a/src/backend/render/wayland/mod.rs +++ b/src/backend/render/wayland/mod.rs @@ -23,7 +23,7 @@ render_elements! { Wayland=WaylandSurfaceRenderElement, } -pub fn render_elements_from_surface_tree( +pub fn push_render_elements_from_surface_tree( renderer: &mut R, main_surface: &wl_surface::WlSurface, location: impl Into>, @@ -33,17 +33,17 @@ pub fn render_elements_from_surface_tree( should_clip: bool, radii: [u8; 4], kind: impl Into, -) -> Vec -where + push_above: &mut dyn FnMut(SurfaceRenderElement), + mut push_below: Option<&mut dyn FnMut(SurfaceRenderElement)>, +) where R: Renderer + ImportAll + AsGlowRenderer, R::TextureId: Clone + 'static, - E: From>, { let location = location.into().to_f64(); let geometry = geometry.into().to_f64(); let scale = scale.into(); let kind = kind.into(); - let mut surfaces: Vec = Vec::new(); + let mut passed_main = false; compositor::with_surface_tree_downward( main_surface, @@ -94,7 +94,13 @@ where } else { surface.into() }; - surfaces.push(elem.into()); + if let Some(push_below) = push_below.as_mut() + && passed_main + { + push_below(elem); + } else { + push_above(elem); + } } Ok(None) => {} // surface is not mapped Err(err) => { @@ -103,9 +109,11 @@ where }; } } + + if surface == main_surface { + passed_main = true; + } }, |_, _, _| true, ); - - surfaces } diff --git a/src/shell/element/mod.rs b/src/shell/element/mod.rs index 69a8391f..11861591 100644 --- a/src/shell/element/mod.rs +++ b/src/shell/element/mod.rs @@ -598,42 +598,38 @@ impl CosmicMapped { } } - pub fn popup_render_elements( + pub fn push_popup_render_elements( &self, renderer: &mut R, location: smithay::utils::Point, scale: smithay::utils::Scale, alpha: f32, scanout_node: Option, - ) -> Vec - where + push: &mut dyn FnMut(CosmicMappedRenderElement), + ) where R: AsGlowRenderer, R::TextureId: Send + Clone + 'static, CosmicMappedRenderElement: RenderElement, - C: From>, { match &self.element { - CosmicMappedInternal::Stack(s) => s - .popup_render_elements::>( - renderer, - location, - scale, - alpha, - scanout_node, - ), - CosmicMappedInternal::Window(w) => w - .popup_render_elements::>( - renderer, - location, - scale, - alpha, - scanout_node, - ), + CosmicMappedInternal::Stack(s) => s.push_popup_render_elements( + renderer, + location, + scale, + alpha, + scanout_node, + &mut |elem| push(elem.into()), + ), + CosmicMappedInternal::Window(w) => w.push_popup_render_elements( + renderer, + location, + scale, + alpha, + scanout_node, + &mut |elem| push(elem.into()), + ), _ => unreachable!(), } - .into_iter() - .map(C::from) - .collect() } pub fn shadow_render_element( @@ -680,7 +676,7 @@ impl CosmicMapped { } } - pub fn render_elements( + pub fn push_render_elements( &self, renderer: &mut R, location: smithay::utils::Point, @@ -689,15 +685,15 @@ impl CosmicMapped { alpha: f32, scanout_override: Option, scanout_node: Option, - ) -> Vec - where + push_above: &mut dyn FnMut(CosmicMappedRenderElement), + push_below: &mut dyn FnMut(CosmicMappedRenderElement), + ) where R: AsGlowRenderer, R::TextureId: Send + Clone + 'static, CosmicMappedRenderElement: RenderElement, - C: From>, { #[cfg(feature = "debug")] - let mut elements = if let Some(debug) = self.debug.lock().unwrap().as_mut() { + if let Some(debug) = self.debug.lock().unwrap().as_mut() { let window = self.active_window(); let window_geo = window.geometry(); let (min_size, max_size, size) = ( @@ -858,21 +854,15 @@ impl CosmicMapped { scale.x, 0.8, ) { - Ok(element) => vec![CosmicMappedRenderElement::from(element)], + Ok(element) => push_above(element.into()), Err(err) => { debug!(?err, "Error rendering debug overlay."); - Vec::new() } } - } else { - Vec::new() }; - #[cfg(not(feature = "debug"))] - let mut elements = Vec::new(); - #[cfg_attr(not(feature = "debug"), allow(unused_mut))] - elements.extend(match &self.element { - CosmicMappedInternal::Stack(s) => s.render_elements::>( + match &self.element { + CosmicMappedInternal::Stack(s) => s.push_render_elements( renderer, location, max_size, @@ -880,21 +870,22 @@ impl CosmicMapped { alpha, scanout_override, scanout_node, + &mut |elem| push_above(elem.into()), + &mut |elem| push_below(elem.into()), + ), + CosmicMappedInternal::Window(w) => w.push_render_elements( + renderer, + location, + max_size, + scale, + alpha, + scanout_override, + scanout_node, + &mut |elem| push_above(elem.into()), + &mut |elem| push_below(elem.into()), ), - CosmicMappedInternal::Window(w) => w - .render_elements::>( - renderer, - location, - max_size, - scale, - alpha, - scanout_override, - scanout_node, - ), _ => unreachable!(), - }); - - elements.into_iter().map(C::from).collect() + } } pub(crate) fn update_theme(&self, theme: cosmic::Theme) { diff --git a/src/shell/element/stack.rs b/src/shell/element/stack.rs index fe25a4e6..58e28753 100644 --- a/src/shell/element/stack.rs +++ b/src/shell/element/stack.rs @@ -39,8 +39,8 @@ use smithay::{ renderer::{ ImportAll, ImportMem, Renderer, element::{ - AsRenderElements, Element, Id as RendererId, Kind, RenderElement, - UnderlyingStorage, memory::MemoryRenderBufferRenderElement, + Element, Id as RendererId, Kind, RenderElement, UnderlyingStorage, + memory::MemoryRenderBufferRenderElement, }, gles::element::PixelShaderElement, glow::GlowRenderer, @@ -645,35 +645,31 @@ impl CosmicStack { self.0.loop_handle() } - pub fn popup_render_elements( + pub fn push_popup_render_elements( &self, renderer: &mut R, location: Point, scale: Scale, alpha: f32, scanout_node: Option, - ) -> Vec - where + push: &mut dyn FnMut(CosmicStackRenderElement), + ) where R: AsGlowRenderer, R::TextureId: Send + Clone + 'static, - C: From>, { let window_loc = location + Point::from((0, (TAB_HEIGHT as f64 * scale.y) as i32)); self.0.with_program(|p| { let windows = p.windows.lock().unwrap(); let active = p.active.load(Ordering::SeqCst); - windows[active] - .popup_render_elements::>( - renderer, - window_loc, - scale, - alpha, - scanout_node, - ) - .into_iter() - .map(C::from) - .collect() + windows[active].push_popup_render_elements( + renderer, + window_loc, + scale, + alpha, + scanout_node, + &mut |elem| push(elem.into()), + ) }) } @@ -744,7 +740,7 @@ impl CosmicStack { }) } - pub fn render_elements( + pub fn push_render_elements( &self, renderer: &mut R, location: Point, @@ -753,17 +749,17 @@ impl CosmicStack { alpha: f32, scanout_override: Option, scanout_node: Option, - ) -> Vec - where + push_above: &mut dyn FnMut(CosmicStackRenderElement), + push_below: &mut dyn FnMut(CosmicStackRenderElement), + ) where R: AsGlowRenderer, R::TextureId: Send + Clone + 'static, - C: From>, { if !self .0 .with_program(|p| p.override_alive.load(Ordering::Acquire)) { - return Vec::new(); + return; } let geometry = self @@ -773,11 +769,12 @@ impl CosmicStack { let stack_loc = location + geometry.loc; let window_loc = location + Point::from((0, (TAB_HEIGHT as f64 * scale.y) as i32)); - let mut elements = AsRenderElements::::render_elements::>( - &self.0, renderer, stack_loc, scale, alpha, - ); + self.0 + .push_render_elements(renderer, stack_loc, scale, alpha, &mut |elem| { + push_above(elem.into()) + }); - elements.extend(self.0.with_program(|p| { + self.0.with_program(|p| { let windows = p.windows.lock().unwrap(); let active = p.active.load(Ordering::SeqCst); let theme = p.theme.lock().unwrap(); @@ -804,9 +801,9 @@ impl CosmicStack { let window_key = CosmicMappedKey(CosmicMappedKeyInner::Stack(Arc::downgrade(&self.0.0))); - let border = (!maximized).then(|| { + if !maximized { let (r, g, b, a) = theme.cosmic().bg_divider().into_components(); - CosmicStackRenderElement::Border(IndicatorShader::element( + push_above(CosmicStackRenderElement::Border(IndicatorShader::element( renderer, Key::Window(Usage::Border, window_key.clone()), geo.to_i32_round().as_local(), @@ -815,24 +812,23 @@ impl CosmicStack { a * alpha, scale.x, [r, g, b], - )) - }); + ))); + }; - border.into_iter().chain( - windows[active].render_elements::>( - renderer, - window_loc, - scale, - alpha, - scanout_override, - scanout_node, - radii.is_some(), - radii.unwrap_or([0; 4]), - ), - ) - })); - - elements.into_iter().map(C::from).collect() + let radii = radii.map(|[a, _, c, _]| [a, 0, c, 0]); + windows[active].push_render_elements( + renderer, + window_loc, + scale, + alpha, + scanout_override, + scanout_node, + radii.is_some(), + radii.unwrap_or([0; 4]), + &mut |elem| push_above(elem.into()), + Some(&mut |elem| push_below(elem.into())), + ); + }); } pub(crate) fn set_theme(&self, theme: cosmic::Theme) { diff --git a/src/shell/element/surface.rs b/src/shell/element/surface.rs index 374b8ea4..53c3d4bd 100644 --- a/src/shell/element/surface.rs +++ b/src/shell/element/surface.rs @@ -1,7 +1,7 @@ use crate::{ backend::render::{ element::AsGlowRenderer, - wayland::{SurfaceRenderElement, render_elements_from_surface_tree}, + wayland::{SurfaceRenderElement, push_render_elements_from_surface_tree}, }, shell::focus::target::PointerFocusTarget, wayland::{ @@ -859,48 +859,47 @@ impl CosmicSurface { self.0.user_data() } - pub fn popup_render_elements( + pub fn push_popup_render_elements( &self, renderer: &mut R, location: Point, scale: Scale, alpha: f32, scanout_node: Option, - ) -> Vec - where + push: &mut dyn FnMut(SurfaceRenderElement), + ) where R: Renderer + ImportAll + AsGlowRenderer, R::TextureId: Clone + 'static, - C: From>, { match self.0.underlying_surface() { WindowSurface::Wayland(toplevel) => { let surface = toplevel.wl_surface(); - PopupManager::popups_for_surface(surface) - .flat_map(move |(popup, popup_offset)| { - let offset = (self.0.geometry().loc + popup_offset - popup.geometry().loc) - .to_physical_precise_round(scale); - let mut geometry = popup.geometry().to_f64(); - geometry.loc += location.to_f64().to_logical(scale) + popup_offset.to_f64(); + for (popup, popup_offset) in PopupManager::popups_for_surface(surface) { + let offset = (self.0.geometry().loc + popup_offset - popup.geometry().loc) + .to_physical_precise_round(scale); + let mut geometry = popup.geometry().to_f64(); + geometry.loc += location.to_f64().to_logical(scale) + popup_offset.to_f64(); - render_elements_from_surface_tree( - renderer, - popup.wl_surface(), - location + offset, - geometry, - scale, - alpha, - false, - [0; 4], - scanout_kind_eval(None, scanout_node), - ) - }) - .collect() + push_render_elements_from_surface_tree( + renderer, + popup.wl_surface(), + location + offset, + geometry, + scale, + alpha, + false, + [0; 4], + scanout_kind_eval(None, scanout_node), + push, + None, + ) + } } - WindowSurface::X11(_) => Vec::new(), + WindowSurface::X11(_) => {} } } - pub fn render_elements( + pub fn push_render_elements( &self, renderer: &mut R, location: Point, @@ -910,11 +909,11 @@ impl CosmicSurface { scanout_node: Option, should_clip: bool, radii: [u8; 4], - ) -> Vec - where + push_above: &mut dyn FnMut(SurfaceRenderElement), + push_below: Option<&mut dyn FnMut(SurfaceRenderElement)>, + ) where R: Renderer + ImportAll + AsGlowRenderer, R::TextureId: Clone + 'static, - C: From>, { let mut geometry = self.0.geometry().to_f64(); geometry.loc += location.to_f64().to_logical(scale); @@ -923,7 +922,7 @@ impl CosmicSurface { WindowSurface::Wayland(toplevel) => { let surface = toplevel.wl_surface(); - render_elements_from_surface_tree( + push_render_elements_from_surface_tree( renderer, surface, location, @@ -933,14 +932,16 @@ impl CosmicSurface { should_clip, radii, scanout_kind_eval(scanout_override, scanout_node), + push_above, + push_below, ) } WindowSurface::X11(surface) => { let Some(surface) = surface.wl_surface() else { - return Vec::new(); + return; }; - render_elements_from_surface_tree( + push_render_elements_from_surface_tree( renderer, &surface, location, @@ -950,6 +951,8 @@ impl CosmicSurface { should_clip, radii, scanout_kind_eval(scanout_override, scanout_node), + push_above, + push_below, ) } } diff --git a/src/shell/element/window.rs b/src/shell/element/window.rs index cc12e0f9..48949127 100644 --- a/src/shell/element/window.rs +++ b/src/shell/element/window.rs @@ -25,8 +25,8 @@ use smithay::{ renderer::{ ImportAll, ImportMem, Renderer, element::{ - AsRenderElements, Element, Id as RendererId, Kind, RenderElement, - UnderlyingStorage, memory::MemoryRenderBufferRenderElement, + Element, Id as RendererId, Kind, RenderElement, UnderlyingStorage, + memory::MemoryRenderBufferRenderElement, }, gles::element::PixelShaderElement, glow::GlowRenderer, @@ -348,18 +348,17 @@ impl CosmicWindow { self.0.loop_handle() } - pub fn popup_render_elements( + pub fn push_popup_render_elements( &self, renderer: &mut R, location: Point, scale: Scale, alpha: f32, scanout_node: Option, - ) -> Vec - where - R: Renderer + ImportAll + ImportMem + AsGlowRenderer, + push: &mut dyn FnMut(CosmicWindowRenderElement), + ) where + R: Renderer + AsGlowRenderer + ImportAll + ImportMem, R::TextureId: Send + Clone + 'static, - C: From>, { let has_ssd = self.0.with_program(|p| p.has_ssd(false)); @@ -370,17 +369,14 @@ impl CosmicWindow { }; self.0.with_program(|p| { - p.window - .popup_render_elements::>( - renderer, - window_loc, - scale, - alpha, - scanout_node, - ) - .into_iter() - .map(C::from) - .collect() + p.window.push_popup_render_elements( + renderer, + window_loc, + scale, + alpha, + scanout_node, + &mut |elem| push(elem.into()), + ) }) } @@ -464,7 +460,7 @@ impl CosmicWindow { }) } - pub fn render_elements( + pub fn push_render_elements( &self, renderer: &mut R, location: Point, @@ -473,11 +469,11 @@ impl CosmicWindow { alpha: f32, scanout_override: Option, scanout_node: Option, - ) -> Vec - where + push_above: &mut dyn FnMut(CosmicWindowRenderElement), + push_below: &mut dyn FnMut(CosmicWindowRenderElement), + ) where R: AsGlowRenderer, R::TextureId: Send + Clone + 'static, - C: From>, { let (has_ssd, is_tiled, is_maximized, mut radii, appearance) = self.0.with_program(|p| { ( @@ -514,8 +510,6 @@ impl CosmicWindow { location }; - let mut elements = Vec::new(); - let (mut geo, bg_divider) = self.0.with_program(|p| { ( SpaceElement::geometry(&p.window).to_f64(), @@ -545,15 +539,15 @@ impl CosmicWindow { scale.x, [r, g, b], )); - elements.push(elem); + push_above(elem); } - if has_ssd { - radii[1] = 0; - radii[3] = 0; - } - elements.extend(self.0.with_program(|p| { - p.window.render_elements::>( + self.0.with_program(|p| { + if has_ssd { + radii[1] = 0; + radii[3] = 0; + } + p.window.push_render_elements( renderer, window_loc, scale, @@ -562,8 +556,10 @@ impl CosmicWindow { scanout_node, clip, radii, + &mut |elem| push_above(elem.into()), + Some(&mut |elem| push_below(elem.into())), ) - })); + }); if has_ssd { let ssd_loc = location @@ -571,12 +567,11 @@ impl CosmicWindow { .0 .with_program(|p| p.window.geometry().loc) .to_physical_precise_round(scale); - elements.extend(AsRenderElements::::render_elements::< - CosmicWindowRenderElement, - >(&self.0, renderer, ssd_loc, scale, alpha)) + self.0 + .push_render_elements(renderer, ssd_loc, scale, alpha, &mut |elem| { + push_above(elem.into()) + }); } - - elements.into_iter().map(C::from).collect() } pub(crate) fn set_theme(&self, theme: cosmic::Theme) { diff --git a/src/shell/grabs/menu/mod.rs b/src/shell/grabs/menu/mod.rs index 14a9f917..e79734e9 100644 --- a/src/shell/grabs/menu/mod.rs +++ b/src/shell/grabs/menu/mod.rs @@ -18,10 +18,7 @@ use cosmic::{ use smithay::{ backend::{ input::{ButtonState, TouchSlot}, - renderer::{ - ImportMem, Renderer, - element::{AsRenderElements, memory::MemoryRenderBufferRenderElement}, - }, + renderer::{ImportMem, Renderer, element::memory::MemoryRenderBufferRenderElement}, }, desktop::space::SpaceElement, input::{ @@ -65,29 +62,28 @@ pub struct MenuGrabState { pub type SeatMenuGrabState = Mutex>; impl MenuGrabState { - pub fn render(&self, renderer: &mut R, output: &Output) -> Vec - where + pub fn render( + &self, + renderer: &mut R, + output: &Output, + push: &mut dyn FnMut(MemoryRenderBufferRenderElement), + ) where R: Renderer + ImportMem, R::TextureId: Send + Clone + 'static, - I: From>, { let scale = output.current_scale().fractional_scale(); - self.elements - .lock() - .unwrap() - .iter() - .flat_map(|elem| { - elem.iced.render_elements( - renderer, - elem.position - .to_local(output) - .as_logical() - .to_physical_precise_round(scale), - scale.into(), - 1.0, - ) - }) - .collect() + for elem in self.elements.lock().unwrap().iter() { + elem.iced.push_render_elements( + renderer, + elem.position + .to_local(output) + .as_logical() + .to_physical_precise_round(scale), + scale.into(), + 1.0, + push, + ) + } } pub fn is_in_screen_space(&self) -> bool { diff --git a/src/shell/grabs/moving.rs b/src/shell/grabs/moving.rs index 1fd8a9b6..48ebd4ba 100644 --- a/src/shell/grabs/moving.rs +++ b/src/shell/grabs/moving.rs @@ -19,13 +19,14 @@ use crate::{ use calloop::LoopHandle; use cosmic::theme::CosmicTheme; +use smallvec::SmallVec; use smithay::{ backend::{ drm::DrmNode, input::ButtonState, renderer::{ ImportAll, ImportMem, Renderer, - element::{AsRenderElements, RenderElement, utils::RescaleRenderElement}, + element::{RenderElement, utils::RescaleRenderElement}, }, }, desktop::{WindowSurfaceType, layer_map_for_output, space::SpaceElement}, @@ -69,18 +70,17 @@ pub struct MoveGrabState { impl MoveGrabState { #[profiling::function] - pub fn render( + pub fn render( &self, renderer: &mut R, output: &Output, theme: &CosmicTheme, scanout_node: Option, - ) -> Vec - where + push: &mut dyn FnMut(CosmicMappedRenderElement), + ) where R: Renderer + ImportAll + ImportMem + AsGlowRenderer, R::TextureId: Send + Clone + 'static, CosmicMappedRenderElement: RenderElement, - I: From>, { let scale = if self.previous == ManagedLayer::Tiling { 0.6 + ((1.0 @@ -105,7 +105,7 @@ impl MoveGrabState { .intersection(window_geo) .is_none() { - return Vec::new(); + return; } let output_scale: Scale = output.current_scale().fractional_scale().into(); @@ -115,13 +115,32 @@ impl MoveGrabState { + self.window_offset - scaling_offset; + for (indicator, location) in self.stacking_indicator.iter() { + indicator.push_render_elements( + renderer, + location.to_physical_precise_round(output_scale), + output_scale, + 1.0, + &mut |elem| push(elem.into()), + ); + } + + self.window.push_popup_render_elements::( + renderer, + (render_location - self.window.geometry().loc).to_physical_precise_round(output_scale), + output_scale, + alpha, + scanout_node, + push, + ); + let active_window_hint = crate::theme::active_window_hint(theme); let radius = self .element() .corner_radius(window_geo.size, self.indicator_thickness); - let focus_element = if self.indicator_thickness > 0 { - Some(CosmicMappedRenderElement::from( + if self.indicator_thickness > 0 { + push( IndicatorShader::focus_element( renderer, Key::Window(Usage::MoveGrabIndicator, self.window.key()), @@ -144,12 +163,57 @@ impl MoveGrabState { active_window_hint.green, active_window_hint.blue, ], - ), - )) - } else { - None + ) + .into(), + ) + } + + let map_window_element = |elem| match elem { + CosmicMappedRenderElement::Stack(stack) => { + CosmicMappedRenderElement::GrabbedStack(RescaleRenderElement::from_element( + stack, + render_location + .to_physical_precise_round(output.current_scale().fractional_scale()), + scale, + )) + } + CosmicMappedRenderElement::Window(window) => { + CosmicMappedRenderElement::GrabbedWindow(RescaleRenderElement::from_element( + window, + render_location + .to_physical_precise_round(output.current_scale().fractional_scale()), + scale, + )) + } + x => x, }; + let mut lower_elements = SmallVec::<[CosmicMappedRenderElement; 4]>::new_const(); + self.window.push_render_elements( + renderer, + (render_location - self.window.geometry().loc).to_physical_precise_round(output_scale), + None, + output_scale, + alpha, + Some(false), + scanout_node, + &mut |elem| push(map_window_element(elem)), + &mut |elem| lower_elements.push(map_window_element(elem)), + ); + if let Some(shadow_element) = self.window.shadow_render_element( + renderer, + (render_location - self.window.geometry().loc).to_physical_precise_round(output_scale), + None, + output_scale, + scale, + alpha, + ) { + push(shadow_element); + } + for elem in lower_elements.into_iter() { + push(elem); + } + let non_exclusive_geometry = { let layers = layer_map_for_output(output); layers.non_exclusive_zone() @@ -158,119 +222,46 @@ impl MoveGrabState { let gaps = (theme.gaps.0 as i32, theme.gaps.1 as i32); let thickness = self.indicator_thickness.max(1); - let snapping_indicator = match &self.snapping_zone { - Some(t) if &self.cursor_output == output => { - let base_color = theme.palette.neutral_9; - let overlay_geometry = t.overlay_geometry(non_exclusive_geometry, gaps); - vec![ - CosmicMappedRenderElement::from(IndicatorShader::element( - renderer, - Key::Window(Usage::SnappingIndicator, self.window.key()), - overlay_geometry, - thickness, - [ - theme.radius_s()[0] as u8, - theme.radius_s()[1] as u8, - theme.radius_s()[2] as u8, - theme.radius_s()[3] as u8, - ], - 1.0, - output_scale.x, - [ - active_window_hint.red, - active_window_hint.green, - active_window_hint.blue, - ], - )), - CosmicMappedRenderElement::from(BackdropShader::element( - renderer, - Key::Window(Usage::SnappingIndicator, self.window.key()), - t.overlay_geometry(non_exclusive_geometry, gaps), - theme.radius_s()[0], // TODO: Fix once shaders support 4 corner radii customization - 0.4, - [base_color.red, base_color.green, base_color.blue], - )), - ] - } - _ => vec![], - }; + if let Some(t) = &self.snapping_zone + && &self.cursor_output == output + { + let base_color = theme.palette.neutral_9; + let overlay_geometry = t.overlay_geometry(non_exclusive_geometry, gaps); - let w_elements = self - .window - .render_elements::>( - renderer, - (render_location - self.window.geometry().loc) - .to_physical_precise_round(output_scale), - None, - output_scale, - alpha, - Some(false), - scanout_node, - ); - let p_elements = self - .window - .popup_render_elements::>( - renderer, - (render_location - self.window.geometry().loc) - .to_physical_precise_round(output_scale), - output_scale, - alpha, - scanout_node, - ); - let shadow_element = self.window.shadow_render_element( - renderer, - (render_location - self.window.geometry().loc).to_physical_precise_round(output_scale), - None, - output_scale, - scale, - alpha, - ); - - self.stacking_indicator - .iter() - .flat_map(|(indicator, location)| { - indicator.render_elements( + push( + IndicatorShader::element( renderer, - location.to_physical_precise_round(output_scale), - output_scale, + Key::Window(Usage::SnappingIndicator, self.window.key()), + overlay_geometry, + thickness, + [ + theme.radius_s()[0] as u8, + theme.radius_s()[1] as u8, + theme.radius_s()[2] as u8, + theme.radius_s()[3] as u8, + ], 1.0, + output_scale.x, + [ + active_window_hint.red, + active_window_hint.green, + active_window_hint.blue, + ], ) - }) - .chain(p_elements) - .chain(focus_element) - .chain( - w_elements - .into_iter() - .chain(shadow_element) - .map(|elem| match elem { - CosmicMappedRenderElement::Stack(stack) => { - CosmicMappedRenderElement::GrabbedStack( - RescaleRenderElement::from_element( - stack, - render_location.to_physical_precise_round( - output.current_scale().fractional_scale(), - ), - scale, - ), - ) - } - CosmicMappedRenderElement::Window(window) => { - CosmicMappedRenderElement::GrabbedWindow( - RescaleRenderElement::from_element( - window, - render_location.to_physical_precise_round( - output.current_scale().fractional_scale(), - ), - scale, - ), - ) - } - x => x, - }), + .into(), + ); + push( + BackdropShader::element( + renderer, + Key::Window(Usage::SnappingIndicator, self.window.key()), + t.overlay_geometry(non_exclusive_geometry, gaps), + theme.radius_s()[0], // TODO: Fix once shaders support 4 corner radii customization + 0.4, + [base_color.red, base_color.green, base_color.blue], + ) + .into(), ) - .chain(snapping_indicator) - .map(I::from) - .collect() + } } pub fn element(&self) -> CosmicMapped { diff --git a/src/shell/layout/floating/mod.rs b/src/shell/layout/floating/mod.rs index 84677188..aad738f8 100644 --- a/src/shell/layout/floating/mod.rs +++ b/src/shell/layout/floating/mod.rs @@ -9,11 +9,12 @@ use std::{ use cosmic_comp_config::AppearanceConfig; use cosmic_settings_config::shortcuts::action::ResizeDirection; use keyframe::{ease, functions::EaseInOutCubic}; +use smallvec::SmallVec; use smithay::{ backend::{ drm::DrmNode, renderer::element::{ - AsRenderElements, RenderElement, + RenderElement, utils::{Relocate, RelocateRenderElement, RescaleRenderElement}, }, }, @@ -1443,8 +1444,8 @@ impl FloatingLayout { renderer: &mut R, alpha: f32, scanout_node: Option, - ) -> Vec> - where + push: &mut dyn FnMut(CosmicMappedRenderElement), + ) where R: AsGlowRenderer, R::TextureId: Send + Clone + 'static, CosmicMappedRenderElement: RenderElement, @@ -1454,8 +1455,6 @@ impl FloatingLayout { let output = self.space.outputs().next().unwrap(); let output_scale = output.current_scale().fractional_scale(); - let mut elements = Vec::default(); - for elem in self .animations .iter() @@ -1470,20 +1469,17 @@ impl FloatingLayout { .unwrap_or_else(|| (self.space.element_geometry(elem).unwrap().as_local(), alpha)); let render_location = geometry.loc - elem.geometry().loc.as_local(); - elements.extend( - elem.popup_render_elements( - renderer, - render_location - .as_logical() - .to_physical_precise_round(output_scale), - output_scale.into(), - alpha, - scanout_node, - ), + elem.push_popup_render_elements( + renderer, + render_location + .as_logical() + .to_physical_precise_round(output_scale), + output_scale.into(), + alpha, + scanout_node, + push, ); } - - elements } #[profiling::function] @@ -1496,8 +1492,8 @@ impl FloatingLayout { alpha: f32, theme: &cosmic::theme::CosmicTheme, scanout_node: Option, - ) -> Vec> - where + push: &mut dyn FnMut(CosmicMappedRenderElement), + ) where R: AsGlowRenderer, R::TextureId: Send + Clone + 'static, CosmicMappedRenderElement: RenderElement, @@ -1510,8 +1506,7 @@ impl FloatingLayout { layers.non_exclusive_zone() }; let output_scale = output.current_scale().fractional_scale(); - - let mut elements = Vec::default(); + let mut lower_elements = SmallVec::<[_; 4]>::new_const(); for elem in self .animations @@ -1525,122 +1520,9 @@ impl FloatingLayout { .get(elem) .map(|anim| (*anim.previous_geometry(), alpha * anim.alpha())) .unwrap_or_else(|| (self.space.element_geometry(elem).unwrap().as_local(), alpha)); - let render_location = geometry.loc - elem.geometry().loc.as_local(); - let mut window_elements = elem.render_elements( - renderer, - render_location - .as_logical() - .to_physical_precise_round(output_scale), - None, - output_scale.into(), - alpha, - None, - scanout_node, - ); - window_elements.extend( - elem.shadow_render_element( - renderer, - render_location - .as_logical() - .to_physical_precise_round(output_scale), - None, - output_scale.into(), - 1., - alpha, - ), - ); - - if let Some(anim) = self.animations.get(elem) { - let original_geo = anim.previous_geometry(); - geometry = anim.geometry( - output_geometry, - self.space - .element_geometry(elem) - .map(RectExt::as_local) - .unwrap_or(geometry), - elem.floating_tiled.lock().unwrap().as_ref(), - self.gaps(), - ); - - let buffer_size = elem.geometry().size; - let scale = Scale { - x: geometry.size.w as f64 / buffer_size.w as f64, - y: geometry.size.h as f64 / buffer_size.h as f64, - }; - - window_elements = window_elements - .into_iter() - .map(|element| match element { - CosmicMappedRenderElement::Stack(elem) => { - CosmicMappedRenderElement::MovingStack({ - let rescaled = RescaleRenderElement::from_element( - elem, - original_geo - .loc - .as_logical() - .to_physical_precise_round(output_scale), - scale, - ); - - RelocateRenderElement::from_element( - rescaled, - (geometry.loc - original_geo.loc) - .as_logical() - .to_physical_precise_round(output_scale), - Relocate::Relative, - ) - }) - } - CosmicMappedRenderElement::Window(elem) => { - CosmicMappedRenderElement::MovingWindow({ - let rescaled = RescaleRenderElement::from_element( - elem, - original_geo - .loc - .as_logical() - .to_physical_precise_round(output_scale), - scale, - ); - - RelocateRenderElement::from_element( - rescaled, - (geometry.loc - original_geo.loc) - .as_logical() - .to_physical_precise_round(output_scale), - Relocate::Relative, - ) - }) - } - x => x, - }) - .collect(); - } if focused == Some(elem) && !elem.is_maximized(false) { - if let Some((mode, resize)) = resize_indicator.as_mut() { - let mut resize_geometry = geometry; - resize_geometry.loc -= (18, 18).into(); - resize_geometry.size += (36, 36).into(); - - resize.resize(resize_geometry.size.as_logical()); - resize.output_enter(output, Rectangle::default() /* unused */); - window_elements = resize - .render_elements::>( - renderer, - resize_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) - .chain(window_elements.into_iter()) - .collect(); - } - let active_window_hint = crate::theme::active_window_hint(theme); let radius = elem.corner_radius(geometry.size.as_logical(), indicator_thickness); if indicator_thickness > 0 { @@ -1658,14 +1540,130 @@ impl FloatingLayout { active_window_hint.blue, ], ); - window_elements.insert(0, element.into()); + push(element.into()); + } + + if let Some((mode, resize)) = resize_indicator.as_mut() { + let mut resize_geometry = geometry; + resize_geometry.loc -= (18, 18).into(); + resize_geometry.size += (36, 36).into(); + + resize.resize(resize_geometry.size.as_logical()); + resize.output_enter(output, Rectangle::default() /* unused */); + resize.push_render_elements( + renderer, + resize_geometry + .loc + .as_logical() + .to_physical_precise_round(output_scale), + output_scale.into(), + alpha * mode.alpha().unwrap_or(1.0), + &mut |elem| push(CosmicMappedRenderElement::Window(elem.into())), + ); } } - elements.extend(window_elements); - } + let maybe_map = if let Some(anim) = self.animations.get(elem) { + let original_geo = anim.previous_geometry(); + geometry = anim.geometry( + output_geometry, + self.space + .element_geometry(elem) + .map(RectExt::as_local) + .unwrap_or(geometry), + elem.floating_tiled.lock().unwrap().as_ref(), + self.gaps(), + ); - elements + let buffer_size = elem.geometry().size; + let scale = Scale { + x: geometry.size.w as f64 / buffer_size.w as f64, + y: geometry.size.h as f64 / buffer_size.h as f64, + }; + + Some(move |element| match element { + CosmicMappedRenderElement::Stack(elem) => { + CosmicMappedRenderElement::MovingStack({ + let rescaled = RescaleRenderElement::from_element( + elem, + original_geo + .loc + .as_logical() + .to_physical_precise_round(output_scale), + scale, + ); + + RelocateRenderElement::from_element( + rescaled, + (geometry.loc - original_geo.loc) + .as_logical() + .to_physical_precise_round(output_scale), + Relocate::Relative, + ) + }) + } + CosmicMappedRenderElement::Window(elem) => { + CosmicMappedRenderElement::MovingWindow({ + let rescaled = RescaleRenderElement::from_element( + elem, + original_geo + .loc + .as_logical() + .to_physical_precise_round(output_scale), + scale, + ); + + RelocateRenderElement::from_element( + rescaled, + (geometry.loc - original_geo.loc) + .as_logical() + .to_physical_precise_round(output_scale), + Relocate::Relative, + ) + }) + } + x => x, + }) + } else { + None + }; + let map_anim = |elem| { + if let Some(map) = maybe_map { + map(elem) + } else { + elem + } + }; + + elem.push_render_elements( + renderer, + render_location + .as_logical() + .to_physical_precise_round(output_scale), + None, + output_scale.into(), + alpha, + None, + scanout_node, + &mut |elem| push(map_anim(elem)), + &mut |elem| lower_elements.push(map_anim(elem)), + ); + if let Some(shadow_element) = elem.shadow_render_element( + renderer, + render_location + .as_logical() + .to_physical_precise_round(output_scale), + None, + output_scale.into(), + 1., + alpha, + ) { + push(map_anim(shadow_element)); + } + for elem in lower_elements.drain(..) { + push(elem); + } + } } pub fn snap_to_corner(&self, mapped: &CosmicMapped, corners: &TiledCorners) { diff --git a/src/shell/layout/tiling/mod.rs b/src/shell/layout/tiling/mod.rs index 14a65ea7..fbcec105 100644 --- a/src/shell/layout/tiling/mod.rs +++ b/src/shell/layout/tiling/mod.rs @@ -6,8 +6,7 @@ use crate::{ element::AsGlowRenderer, }, shell::{ - CosmicSurface, Direction, FocusResult, MoveResult, OutputNotMapped, OverviewMode, - ResizeMode, Trigger, + CosmicSurface, Direction, FocusResult, MoveResult, OverviewMode, ResizeMode, Trigger, element::{ CosmicMapped, CosmicMappedRenderElement, CosmicStack, CosmicWindow, resize_indicator::ResizeIndicator, @@ -45,12 +44,13 @@ use keyframe::{ ease, functions::{EaseInOutCubic, Linear}, }; +use smallvec::SmallVec; use smithay::{ backend::{ drm::DrmNode, renderer::{ element::{ - AsRenderElements, Id, RenderElement, + Id, RenderElement, utils::{ ConstrainAlign, ConstrainScaleBehavior, RescaleRenderElement, constrain_render_elements, @@ -3424,8 +3424,8 @@ impl TilingLayout { None, None, self.theme.cosmic(), - ) - .0; + &mut |_| {}, + ); let mut result = None; let mut lookup = Some(root.clone()); @@ -4011,8 +4011,8 @@ impl TilingLayout { indicator_thickness: u8, theme: &cosmic::theme::CosmicTheme, scanout_node: Option, - ) -> Result>, OutputNotMapped> - where + push: &mut dyn FnMut(CosmicMappedRenderElement), + ) where R: AsGlowRenderer, R::TextureId: Send + Clone + 'static, CosmicMappedRenderElement: RenderElement, @@ -4052,8 +4052,6 @@ impl TilingLayout { }; let draw_groups = overview.0.alpha(); - let mut elements = Vec::default(); - let is_overview = !matches!(overview.0, OverviewMode::None); let is_mouse_tiling = (matches!(overview.0.trigger(), Some(Trigger::Pointer(_)))) .then(|| self.last_overview_hover.as_ref().map(|(_, zone)| zone)); @@ -4065,7 +4063,7 @@ impl TilingLayout { // all gone windows and fade them out let old_geometries = if let Some(reference_tree) = reference_tree.as_ref() { - let (geometries, _) = if let Some(transition) = draw_groups { + let geometries = if let Some(transition) = draw_groups { Some(geometries_for_groupview( reference_tree, &mut *renderer, @@ -4080,14 +4078,14 @@ impl TilingLayout { swap_desc.clone(), overview.1.as_ref().and_then(|(_, tree)| *tree), theme, + &mut |_elem| {}, )) } else { None - } - .unzip(); + }; // all old windows we want to fade out - elements.extend(render_old_tree_windows( + render_old_tree_windows( reference_tree, target_tree, renderer, @@ -4098,14 +4096,16 @@ impl TilingLayout { swap_desc.is_some(), theme, scanout_node, - )); + push, + ); geometries } else { None }; - let (geometries, group_elements) = if let Some(transition) = draw_groups { + let mut group_elements = SmallVec::<[_; 4]>::new_const(); + let geometries = if let Some(transition) = draw_groups { Some(geometries_for_groupview( target_tree, &mut *renderer, @@ -4119,14 +4119,14 @@ impl TilingLayout { swap_desc.clone(), overview.1.as_ref().and_then(|(_, tree)| *tree), theme, + &mut |elem| group_elements.push(elem), )) } else { None - } - .unzip(); + }; // all alive windows - elements.extend(render_new_tree_windows( + render_new_tree_windows( target_tree, reference_tree, renderer, @@ -4155,14 +4155,13 @@ impl TilingLayout { &self.backdrop_id, theme, scanout_node, - )); + push, + ); // tiling hints - if let Some(group_elements) = group_elements { - elements.extend(group_elements); + for elem in group_elements.into_iter() { + push(elem); } - - Ok(elements) } #[profiling::function] @@ -4174,8 +4173,8 @@ impl TilingLayout { overview: (OverviewMode, Option<(SwapIndicator, Option<&Tree>)>), theme: &cosmic::theme::CosmicTheme, scanout_node: Option, - ) -> Result>, OutputNotMapped> - where + push: &mut dyn FnMut(CosmicMappedRenderElement), + ) where R: AsGlowRenderer, R::TextureId: Send + Clone + 'static, CosmicMappedRenderElement: RenderElement, @@ -4207,8 +4206,6 @@ impl TilingLayout { }; let draw_groups = overview.0.alpha(); - let mut elements = Vec::default(); - let is_mouse_tiling = (matches!(overview.0.trigger(), Some(Trigger::Pointer(_)))) .then(|| self.last_overview_hover.as_ref().map(|(_, zone)| zone)); let swap_desc = if let Some(Trigger::KeyboardSwap(_, desc)) = overview.0.trigger() { @@ -4219,7 +4216,7 @@ impl TilingLayout { // all gone windows and fade them out let old_geometries = if let Some(reference_tree) = reference_tree.as_ref() { - let (geometries, _) = if let Some(transition) = draw_groups { + let geometries = if let Some(transition) = draw_groups { Some(geometries_for_groupview( reference_tree, &mut *renderer, @@ -4234,14 +4231,14 @@ impl TilingLayout { swap_desc.clone(), overview.1.as_ref().and_then(|(_, tree)| *tree), theme, + &mut |_| {}, )) } else { None - } - .unzip(); + }; // all old windows we want to fade out - elements.extend(render_old_tree_popups( + render_old_tree_popups( reference_tree, target_tree, renderer, @@ -4250,14 +4247,15 @@ impl TilingLayout { percentage, swap_desc.is_some(), scanout_node, - )); + push, + ); geometries } else { None }; - let (geometries, _) = if let Some(transition) = draw_groups { + let geometries = if let Some(transition) = draw_groups { Some(geometries_for_groupview( target_tree, &mut *renderer, @@ -4271,14 +4269,14 @@ impl TilingLayout { swap_desc.clone(), overview.1.as_ref().and_then(|(_, tree)| *tree), theme, + &mut |_| {}, )) } else { None - } - .unzip(); + }; // all alive windows - elements.extend(render_new_tree_popups( + render_new_tree_popups( target_tree, reference_tree, renderer, @@ -4290,9 +4288,8 @@ impl TilingLayout { overview, swap_desc.clone(), scanout_node, - )); - - Ok(elements) + push, + ); } fn gaps(&self) -> (i32, i32) { @@ -4347,10 +4344,8 @@ fn geometries_for_groupview<'a, R>( swap_desc: Option, swap_tree: Option<&Tree>, _theme: &cosmic::theme::CosmicTheme, -) -> ( - HashMap>, - Vec>, -) + push: &mut dyn FnMut(CosmicMappedRenderElement), +) -> HashMap> where R: AsGlowRenderer + 'a, R::TextureId: 'static, @@ -4373,11 +4368,18 @@ where // push bogos value, that will get ignored anyway stack.push((Rectangle::from_size((320, 240).into()), 0)); } - if root.is_some() { + + let has_root = root.is_some(); + if has_root { stack.push((non_exclusive_zone, 0)); } - let mut elements = Vec::new(); + let mut push = |elem| { + if has_root { + push(elem) + } + }; + let mut geometries: HashMap> = HashMap::new(); let alpha = alpha * transition; @@ -4532,7 +4534,7 @@ where if let Some(renderer) = renderer.as_mut() { if (render_potential_group || render_active_child) && Some(&node_id) != root { - elements.push( + push( IndicatorShader::element( *renderer, Key::Group(Arc::downgrade(alive)), @@ -4550,7 +4552,7 @@ where && pill_indicator.is_some() && Some(&node_id) != root { - elements.push( + push( IndicatorShader::element( *renderer, Key::Group(Arc::downgrade(alive)), @@ -4614,7 +4616,7 @@ where }; if draw_outline { - elements.push( + push( IndicatorShader::element( *renderer, Key::Group(Arc::downgrade(alive)), @@ -4675,7 +4677,7 @@ where }; if let Some(renderer) = renderer.as_mut() { - elements.push( + push( BackdropShader::element( *renderer, backdrop_id.clone(), @@ -4693,7 +4695,7 @@ where if matches!(swap_desc, Some(ref desc) if desc.node == node_id) { if let Some(renderer) = renderer.as_mut() { - elements.push( + push( BackdropShader::element( *renderer, Key::Group(Arc::downgrade(alive)), @@ -4778,7 +4780,7 @@ where .unwrap_or(false) { if let Some(renderer) = renderer.as_mut() { - elements.push( + push( BackdropShader::element( *renderer, backdrop_id.clone(), @@ -4820,7 +4822,7 @@ where .unwrap_or(false) { if let Some(renderer) = renderer.as_mut() { - elements.push( + push( BackdropShader::element( *renderer, backdrop_id.clone(), @@ -4851,7 +4853,7 @@ where if let Some(renderer) = renderer.as_mut() { if render_potential_group { - elements.push( + push( IndicatorShader::element( *renderer, Key::Window(Usage::PotentialGroupIndicator, mapped.key()), @@ -4891,7 +4893,7 @@ where geo.loc += (WINDOW_BACKDROP_BORDER, WINDOW_BACKDROP_BORDER).into(); geo.size -= (WINDOW_BACKDROP_BORDER * 2, WINDOW_BACKDROP_BORDER * 2).into(); - elements.push( + push( BackdropShader::element( *renderer, Key::Window(Usage::OverviewBackdrop, mapped.key()), @@ -4948,7 +4950,7 @@ where if let Some(renderer) = renderer.as_mut() { geo.loc += (WINDOW_BACKDROP_BORDER, WINDOW_BACKDROP_BORDER).into(); geo.size -= (WINDOW_BACKDROP_BORDER * 2, WINDOW_BACKDROP_BORDER * 2).into(); - elements.push( + push( BackdropShader::element( *renderer, id.clone(), @@ -4967,11 +4969,7 @@ where } } - if root.is_none() { - elements.clear(); - } - - (geometries, elements) + geometries } fn render_old_tree_popups( @@ -4983,16 +4981,14 @@ fn render_old_tree_popups( percentage: f32, is_swap_mode: bool, scanout_node: Option, -) -> Vec> -where + push: &mut dyn FnMut(CosmicMappedRenderElement), +) where R: AsGlowRenderer, R::TextureId: Send + Clone + 'static, CosmicMappedRenderElement: RenderElement, CosmicWindowRenderElement: RenderElement, CosmicStackRenderElement: RenderElement, { - let mut elements = Vec::default(); - render_old_tree( reference_tree, target_tree, @@ -5001,20 +4997,16 @@ where percentage, is_swap_mode, |mapped, elem_geometry, geo, alpha, _| { - elements.extend( - mapped.popup_render_elements::>( - renderer, - geo.loc.as_logical().to_physical_precise_round(output_scale) - - elem_geometry.loc, - Scale::from(output_scale), - alpha, - scanout_node, - ), - ); + mapped.push_popup_render_elements( + renderer, + geo.loc.as_logical().to_physical_precise_round(output_scale) - elem_geometry.loc, + Scale::from(output_scale), + alpha, + scanout_node, + push, + ) }, - ); - - elements + ) } fn render_old_tree_windows( @@ -5028,8 +5020,8 @@ fn render_old_tree_windows( is_swap_mode: bool, theme: &cosmic::theme::CosmicTheme, scanout_node: Option, -) -> Vec> -where + push: &mut dyn FnMut(CosmicMappedRenderElement), +) where R: AsGlowRenderer, R::TextureId: Send + Clone + 'static, CosmicMappedRenderElement: RenderElement, @@ -5037,8 +5029,35 @@ where CosmicStackRenderElement: RenderElement, { let window_hint = crate::theme::active_window_hint(theme); - let mut elements = Vec::default(); - let mut shadow_elements = Vec::default(); + let mut lower_elements = Vec::default(); + let mut shadow_elements = SmallVec::<[_; 4]>::new_const(); + + let window_map = + |elem, geo: Rectangle, elem_geometry: Rectangle| match elem { + CosmicMappedRenderElement::Stack(elem) => constrain_render_elements( + std::iter::once(elem), + geo.loc.as_logical().to_physical_precise_round(output_scale) - elem_geometry.loc, + geo.as_logical().to_physical_precise_round(output_scale), + elem_geometry, + ConstrainScaleBehavior::Stretch, + ConstrainAlign::CENTER, + output_scale, + ) + .next() + .map(CosmicMappedRenderElement::TiledStack), + CosmicMappedRenderElement::Window(elem) => constrain_render_elements( + std::iter::once(elem), + geo.loc.as_logical().to_physical_precise_round(output_scale) - elem_geometry.loc, + geo.as_logical().to_physical_precise_round(output_scale), + elem_geometry, + ConstrainScaleBehavior::Stretch, + ConstrainAlign::CENTER, + output_scale, + ) + .next() + .map(CosmicMappedRenderElement::TiledWindow), + x => Some(x), + }; render_old_tree( reference_tree, @@ -5048,57 +5067,9 @@ where percentage, is_swap_mode, |mapped, elem_geometry, geo, alpha, is_minimizing| { - shadow_elements.extend(mapped.shadow_render_element( - renderer, - geo.loc.as_logical().to_physical_precise_round(output_scale) - elem_geometry.loc, - Some(geo.size.as_logical()), - Scale::from(output_scale), - 1., - alpha, - )); - - let window_elements = mapped.render_elements::>( - renderer, - geo.loc.as_logical().to_physical_precise_round(output_scale) - elem_geometry.loc, - Some(geo.size.as_logical()), - Scale::from(output_scale), - alpha, - None, - scanout_node, - ); - - elements.extend(window_elements.into_iter().flat_map(|element| { - match element { - CosmicMappedRenderElement::Stack(elem) => constrain_render_elements( - std::iter::once(elem), - geo.loc.as_logical().to_physical_precise_round(output_scale) - - elem_geometry.loc, - geo.as_logical().to_physical_precise_round(output_scale), - elem_geometry, - ConstrainScaleBehavior::Stretch, - ConstrainAlign::CENTER, - output_scale, - ) - .next() - .map(CosmicMappedRenderElement::TiledStack), - CosmicMappedRenderElement::Window(elem) => constrain_render_elements( - std::iter::once(elem), - geo.loc.as_logical().to_physical_precise_round(output_scale) - - elem_geometry.loc, - geo.as_logical().to_physical_precise_round(output_scale), - elem_geometry, - ConstrainScaleBehavior::Stretch, - ConstrainAlign::CENTER, - output_scale, - ) - .next() - .map(CosmicMappedRenderElement::TiledWindow), - x => Some(x), - } - })); let radius = mapped.corner_radius(geo.size.as_logical(), indicator_thickness); if is_minimizing && indicator_thickness > 0 { - elements.push(CosmicMappedRenderElement::FocusIndicator( + push(CosmicMappedRenderElement::FocusIndicator( IndicatorShader::focus_element( renderer, Key::Window(Usage::FocusIndicator, mapped.clone().key()), @@ -5111,10 +5082,44 @@ where ), )); } + + mapped.push_render_elements( + renderer, + geo.loc.as_logical().to_physical_precise_round(output_scale) - elem_geometry.loc, + Some(geo.size.as_logical()), + Scale::from(output_scale), + alpha, + None, + scanout_node, + &mut |elem| { + if let Some(elem) = window_map(elem, geo, elem_geometry) { + push(elem); + } + }, + &mut |elem| { + if let Some(elem) = window_map(elem, geo, elem_geometry) { + lower_elements.push(elem); + } + }, + ); + + shadow_elements.extend(mapped.shadow_render_element( + renderer, + geo.loc.as_logical().to_physical_precise_round(output_scale) - elem_geometry.loc, + Some(geo.size.as_logical()), + Scale::from(output_scale), + 1., + alpha, + )); }, ); - shadow_elements.into_iter().chain(elements).collect() + for elem in shadow_elements { + push(elem); + } + for elem in lower_elements { + push(elem); + } } fn render_old_tree( @@ -5210,15 +5215,14 @@ fn render_new_tree_popups( overview: (OverviewMode, Option<(SwapIndicator, Option<&Tree>)>), swap_desc: Option, scanout_node: Option, -) -> Vec> -where + push: &mut dyn FnMut(CosmicMappedRenderElement), +) where R: AsGlowRenderer, R::TextureId: Send + Clone + 'static, CosmicMappedRenderElement: RenderElement, CosmicWindowRenderElement: RenderElement, CosmicStackRenderElement: RenderElement, { - let mut popup_elements = Vec::new(); let output_scale = output.current_scale().fractional_scale(); let is_active_output = seat @@ -5241,21 +5245,18 @@ where if let Data::Mapped { mapped, .. } = data { let elem_geometry = mapped.geometry().to_physical_precise_round(output_scale); - popup_elements.extend( - mapped.popup_render_elements::>( - renderer, - geo.loc.as_logical().to_physical_precise_round(output_scale) - - elem_geometry.loc, - Scale::from(output_scale), - alpha, - scanout_node, - ), + mapped.push_popup_render_elements( + renderer, + geo.loc.as_logical().to_physical_precise_round(output_scale) + - elem_geometry.loc, + Scale::from(output_scale), + alpha, + scanout_node, + push, ); } }, ); - - popup_elements } fn render_new_tree_windows( @@ -5278,8 +5279,8 @@ fn render_new_tree_windows( backdrop_id: &Id, theme: &cosmic::theme::CosmicTheme, scanout_node: Option, -) -> Vec> -where + push: &mut dyn FnMut(CosmicMappedRenderElement), +) where R: AsGlowRenderer, R::TextureId: Send + Clone + 'static, CosmicMappedRenderElement: RenderElement, @@ -5319,14 +5320,18 @@ where .map(|seat| &seat.active_output() == output) .unwrap_or(false); - let mut animating_window_elements = Vec::new(); - let mut window_elements = Vec::new(); + let mut animating_window_upper_elements = Vec::new(); + let mut animating_window_lower_elements = Vec::new(); + let mut animating_shadow_elements = SmallVec::<[CosmicMappedRenderElement; 4]>::new_const(); + + let mut window_upper_elements = Vec::new(); + let mut window_lower_elements = Vec::new(); + let mut shadow_elements = SmallVec::<[CosmicMappedRenderElement; 4]>::new_const(); let mut group_backdrop = None; - let mut indicators = Vec::new(); - let mut resize_elements = None; - let mut swap_elements = Vec::new(); - let mut shadow_elements = Vec::new(); + let mut indicators = SmallVec::<[CosmicMappedRenderElement; 2]>::new_const(); + let mut resize_element = None; + let mut swap_elements = SmallVec::<[CosmicMappedRenderElement; 4]>::new_const(); let output_geo = output.geometry(); let output_scale = output.current_scale().fractional_scale(); @@ -5339,7 +5344,7 @@ where // 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( + window_upper_elements.push( BackdropShader::element( renderer, backdrop_id.clone(), @@ -5394,22 +5399,19 @@ where let render_loc = (swap_geo.loc.as_logical() - window_geo.loc).to_physical_precise_round(output_scale); - swap_elements.extend( - window - .render_elements( - renderer, - render_loc, - output_scale.into(), - 1.0, - None, - scanout_node, - false, - [0; 4], - ) - .into_iter() - .map(|window| { - CosmicMappedRenderElement::GrabbedWindow(RescaleRenderElement::from_element( - window, + window.push_render_elements( + renderer, + render_loc, + output_scale.into(), + 1.0, + None, + scanout_node, + false, + [0, 0, 0, 0], + &mut |elem| { + swap_elements.push(CosmicMappedRenderElement::GrabbedWindow( + RescaleRenderElement::from_element( + elem.into(), swap_geo .loc .as_logical() @@ -5420,9 +5422,11 @@ where swap_factor(window_geo.size), transition.unwrap_or(1.0), ), - )) - }), - ) + ), + )); + }, + None, + ); } // render actual tree nodes @@ -5538,15 +5542,12 @@ where { swap.resize(geo.size.as_logical()); swap.output_enter(output, output_geo.as_logical()); - swap_elements.extend( - swap.render_elements::>( - renderer, - geo.loc.as_logical().to_physical_precise_round(output_scale), - output_scale.into(), - alpha * overview.0.alpha().unwrap_or(1.0), - ) - .into_iter() - .map(CosmicMappedRenderElement::from), + swap.push_render_elements( + renderer, + geo.loc.as_logical().to_physical_precise_round(output_scale), + output_scale.into(), + alpha * overview.0.alpha().unwrap_or(1.0), + &mut |elem| swap_elements.push(elem.into()), ); } } @@ -5572,17 +5573,14 @@ where }) { resize.force_update(); } - resize_elements = Some( - resize - .render_elements::>( - renderer, - geo.loc.as_logical().to_physical_precise_round(output_scale), - output_scale.into(), - alpha * mode.alpha().unwrap_or(1.0), - ) - .into_iter() - .map(CosmicMappedRenderElement::from) - .collect::>(), + resize.push_render_elements( + renderer, + geo.loc.as_logical().to_physical_precise_round(output_scale), + output_scale.into(), + alpha * mode.alpha().unwrap_or(1.0), + &mut |elem| { + resize_element = Some(elem.into()); + }, ); } } @@ -5608,52 +5606,6 @@ where scale.x.min(scale.y), alpha, ); - let mut elements = mapped.render_elements::>( - renderer, - //original_location, - geo.loc.as_logical().to_physical_precise_round(output_scale) - - elem_geometry.loc, - max_size, - Scale::from(output_scale), - alpha, - None, - scanout_node, - ); - - if swap_desc - .as_ref() - .filter(|swap_desc| swap_desc.node == node_id) - .and_then(|swap_desc| swap_desc.stack_window.as_ref()) - .zip(focused.as_ref()) - .map(|(stack_window, focused_id)| { - target_tree - .get(focused_id) - .ok() - .map(|focused| match focused.data() { - Data::Mapped { mapped, .. } => mapped - .stack_ref() - .map(|stack| &stack.active() == stack_window) - .unwrap_or(false), - _ => false, - }) - .unwrap_or(false) - }) - .unwrap_or(false) - { - let mut active_geo = mapped.active_window_geometry().as_local(); - active_geo.loc += geo.loc - mapped.geometry().loc.as_local(); - elements.insert( - 0, - CosmicMappedRenderElement::Overlay(BackdropShader::element( - renderer, - Key::Window(Usage::Overlay, mapped.key()), - active_geo, - 0.0, - 0.3, - group_color, - )), - ) - } let (behavior, align) = if is_overview { (ConstrainScaleBehavior::Fit, ConstrainAlign::CENTER) @@ -5663,7 +5615,7 @@ where (ConstrainScaleBehavior::CutOff, ConstrainAlign::TOP_LEFT) }; - let elements = elements.into_iter().flat_map(|element| match element { + let map_elem = |element| match element { CosmicMappedRenderElement::Stack(elem) => constrain_render_elements( std::iter::once(elem), geo.loc.as_logical().to_physical_precise_round(output_scale) @@ -5701,7 +5653,66 @@ where .next() .map(CosmicMappedRenderElement::TiledOverlay), x => Some(x), - }); + }; + + let mut upper_elements = SmallVec::<[CosmicMappedRenderElement; 4]>::new_const(); + let mut lower_elements = SmallVec::<[CosmicMappedRenderElement; 4]>::new_const(); + mapped.push_render_elements( + renderer, + //original_location, + geo.loc.as_logical().to_physical_precise_round(output_scale) + - elem_geometry.loc, + max_size, + Scale::from(output_scale), + alpha, + None, + scanout_node, + &mut |elem| { + if let Some(elem) = map_elem(elem) { + upper_elements.push(elem) + } + }, + &mut |elem| { + if let Some(elem) = map_elem(elem) { + lower_elements.push(elem) + } + }, + ); + + if swap_desc + .as_ref() + .filter(|swap_desc| swap_desc.node == node_id) + .and_then(|swap_desc| swap_desc.stack_window.as_ref()) + .zip(focused.as_ref()) + .map(|(stack_window, focused_id)| { + target_tree + .get(focused_id) + .ok() + .map(|focused| match focused.data() { + Data::Mapped { mapped, .. } => mapped + .stack_ref() + .map(|stack| &stack.active() == stack_window) + .unwrap_or(false), + _ => false, + }) + .unwrap_or(false) + }) + .unwrap_or(false) + { + let mut active_geo = mapped.active_window_geometry().as_local(); + active_geo.loc += geo.loc - mapped.geometry().loc.as_local(); + upper_elements.insert( + 0, + CosmicMappedRenderElement::Overlay(BackdropShader::element( + renderer, + Key::Window(Usage::Overlay, mapped.key()), + active_geo, + 0.0, + 0.3, + group_color, + )), + ) + } if swap_desc .as_ref() @@ -5714,30 +5725,36 @@ where }) .unwrap_or(false) { + swap_elements.extend(upper_elements); swap_elements.extend(shadow_element); - swap_elements.extend(elements); + swap_elements.extend(lower_elements); + } else if animating { + animating_window_upper_elements.extend(upper_elements); + animating_shadow_elements.extend(shadow_element); + animating_window_lower_elements.extend(lower_elements); } else { + window_upper_elements.extend(upper_elements); shadow_elements.extend(shadow_element); - if animating { - animating_window_elements.extend(elements); - } else { - window_elements.extend(elements); - } + window_lower_elements.extend(lower_elements); } } }, ); - resize_elements + for elem in resize_element .into_iter() - .flatten() .chain(swap_elements) .chain(indicators) - .chain(window_elements) - .chain(animating_window_elements) + .chain(window_upper_elements) .chain(shadow_elements) + .chain(window_lower_elements) + .chain(animating_window_upper_elements) + .chain(animating_shadow_elements) + .chain(animating_window_lower_elements) .chain(group_backdrop.into_iter().map(Into::into)) - .collect() + { + push(elem); + } } fn render_new_tree( diff --git a/src/shell/workspace.rs b/src/shell/workspace.rs index c5f5cb90..6407935d 100644 --- a/src/shell/workspace.rs +++ b/src/shell/workspace.rs @@ -1,3 +1,4 @@ +use crate::backend::render::wayland::SurfaceRenderElement; use crate::shell::focus::FocusTarget; use crate::shell::layout::tiling::RestoreTilingState; use crate::wayland::handlers::xdg_activation::ActivationContext; @@ -28,6 +29,7 @@ use cosmic_protocols::workspace::v2::server::zcosmic_workspace_handle_v2::Tiling use id_tree::Tree; use indexmap::IndexSet; use keyframe::{ease, functions::EaseInOutCubic}; +use smallvec::SmallVec; use smithay::backend::drm::DrmNode; use smithay::backend::renderer::element::Kind; use smithay::output::WeakOutput; @@ -35,8 +37,7 @@ use smithay::utils::user_data::UserDataMap; use smithay::{ backend::renderer::{ element::{ - Element, Id, RenderElement, surface::WaylandSurfaceRenderElement, - texture::TextureRenderElement, utils::RescaleRenderElement, + Element, Id, RenderElement, texture::TextureRenderElement, utils::RescaleRenderElement, }, gles::GlesTexture, glow::GlowRenderer, @@ -1622,8 +1623,8 @@ impl Workspace { indicator_thickness: u8, theme: &CosmicTheme, scanout_node: Option, - ) -> Result>, OutputNotMapped> - where + push: &mut dyn FnMut(WorkspaceRenderElement), + ) where R: AsGlowRenderer, R::TextureId: Send + Clone + 'static, CosmicMappedRenderElement: RenderElement, @@ -1631,80 +1632,83 @@ impl Workspace { CosmicStackRenderElement: RenderElement, WorkspaceRenderElement: RenderElement, { - let mut elements = Vec::default(); - 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() }; let focused = self.focus_stack.get(last_active_seat).last().cloned(); + let fullscreen_focused = matches!(focused, Some(FocusTarget::Fullscreen(_))); - let render_fullscreen = |fullscreen: &FullscreenSurface, - renderer: &mut R, - output_scale: f64| - -> Vec> { - let fullscreen_geo = self.fullscreen_geometry_for(fullscreen); - let previous_geo = fullscreen - .previous_geometry - .as_ref() - .unwrap_or(&fullscreen_geo); + let mut fullscreen_elements = SmallVec::<[WorkspaceRenderElement; 2]>::new_const(); + let mut render_fullscreen = + |fullscreen: &FullscreenSurface, renderer: &mut R, output_scale: f64| { + let fullscreen_geo = self.fullscreen_geometry_for(fullscreen); + let previous_geo = fullscreen + .previous_geometry + .as_ref() + .unwrap_or(&fullscreen_geo); - let (target_geo, alpha) = match (fullscreen.start_at, fullscreen.ended_at) { - (Some(started), _) => { - let duration = Instant::now().duration_since(started).as_secs_f64() - / FULLSCREEN_ANIMATION_DURATION.as_secs_f64(); - ( - ease( - EaseInOutCubic, - EaseRectangle(*previous_geo), - EaseRectangle(fullscreen_geo), - duration, + let (target_geo, alpha) = match (fullscreen.start_at, fullscreen.ended_at) { + (Some(started), _) => { + let duration = Instant::now().duration_since(started).as_secs_f64() + / FULLSCREEN_ANIMATION_DURATION.as_secs_f64(); + ( + ease( + EaseInOutCubic, + EaseRectangle(*previous_geo), + EaseRectangle(fullscreen_geo), + duration, + ) + .0, + ease(EaseInOutCubic, 0.0, 1.0, duration), ) - .0, - ease(EaseInOutCubic, 0.0, 1.0, duration), - ) - } - (_, Some(ended)) => { - let duration = Instant::now().duration_since(ended).as_secs_f64() - / FULLSCREEN_ANIMATION_DURATION.as_secs_f64(); - ( - ease( - EaseInOutCubic, - EaseRectangle(fullscreen_geo), - EaseRectangle(*previous_geo), - duration, + } + (_, Some(ended)) => { + let duration = Instant::now().duration_since(ended).as_secs_f64() + / FULLSCREEN_ANIMATION_DURATION.as_secs_f64(); + ( + ease( + EaseInOutCubic, + EaseRectangle(fullscreen_geo), + EaseRectangle(*previous_geo), + duration, + ) + .0, + ease(EaseInOutCubic, 1.0, 0.0, duration), ) - .0, - ease(EaseInOutCubic, 1.0, 0.0, duration), - ) - } - (None, None) => (fullscreen_geo, 1.0), - }; + } + (None, None) => (fullscreen_geo, 1.0), + }; - let render_loc = target_geo - .loc - .as_logical() - .to_physical_precise_round(output_scale); + let render_loc = target_geo + .loc + .as_logical() + .to_physical_precise_round(output_scale); - // Only rescale geometry when animating - let animation_rescale = |elem| { - if fullscreen.is_animating() { - let fullscreen_geo = fullscreen.surface.0.geometry(); - let scale = Scale { - x: target_geo.size.w as f64 / fullscreen_geo.size.w as f64, - y: target_geo.size.h as f64 / fullscreen_geo.size.h as f64, - }; + // Only rescale geometry when animating + let animation_rescale = |elem| { + if fullscreen.is_animating() { + let fullscreen_geo = fullscreen.surface.0.geometry(); + let scale = Scale { + x: target_geo.size.w as f64 / fullscreen_geo.size.w as f64, + y: target_geo.size.h as f64 / fullscreen_geo.size.h as f64, + }; - RescaleRenderElement::from_element(elem, render_loc, scale).into() - } else { - Into::>::into(elem) - } - }; + RescaleRenderElement::from_element(elem, render_loc, scale).into() + } else { + Into::>::into(elem) + } + }; - fullscreen - .surface - .render_elements::>( + let mut fullscreen_push = |elem: SurfaceRenderElement| { + if fullscreen_focused { + push(animation_rescale(elem.into())) + } else { + fullscreen_elements.push(animation_rescale(elem.into())) + } + }; + fullscreen.surface.push_render_elements( renderer, render_loc, output_scale.into(), @@ -1712,30 +1716,24 @@ impl Workspace { Some(true), scanout_node, false, - [0; 4], - ) - .into_iter() - .map(animation_rescale) - .collect::>() - }; + [0, 0, 0, 0], + &mut fullscreen_push, + None, + ); + }; let top_fullscreen = self.get_fullscreen(last_active_seat); - let mut fullscreen_elements: Vec> = Vec::new(); if let Some(fs) = top_fullscreen { - fullscreen_elements.extend(render_fullscreen(fs, renderer, output_scale)); + render_fullscreen(fs, renderer, output_scale) } // Also render any animating (entering/exiting) fullscreens for fs in self.fullscreen_surfaces.iter().filter(|f| f.is_animating()) { if top_fullscreen.is_none_or(|top| top.surface != fs.surface) { - fullscreen_elements.extend(render_fullscreen(fs, renderer, output_scale)); + render_fullscreen(fs, renderer, output_scale); }; } - if matches!(focused, Some(FocusTarget::Fullscreen(_))) { - elements.append(&mut fullscreen_elements); - } - let any_fullscreen_animating = self .fullscreen_surfaces .iter() @@ -1766,29 +1764,21 @@ impl Workspace { OverviewMode::None => 1.0, }; - elements.extend( - self.floating_layer - .render::( - renderer, - render_focus - .then(|| { - focused.as_ref().and_then(|target| { - if let FocusTarget::Window(mapped) = target { - Some(mapped) - } else { - None - } - }) - }) - .flatten(), - resize_indicator.clone(), - indicator_thickness, - alpha, - theme, - scanout_node, - ) - .into_iter() - .map(WorkspaceRenderElement::from), + self.floating_layer.render( + renderer, + focused.as_ref().and_then(|target| { + if let FocusTarget::Window(mapped) = target { + Some(mapped) + } else { + None + } + }), + resize_indicator.clone(), + indicator_thickness, + alpha, + theme, + scanout_node, + &mut |elem| push(elem.into()), ); let alpha = match &overview.0 { @@ -1805,24 +1795,20 @@ impl Workspace { }; //tiling surfaces - elements.extend( - self.tiling_layer - .render::( - renderer, - render_focus.then_some(last_active_seat), - zone, - overview, - resize_indicator, - indicator_thickness, - theme, - scanout_node, - )? - .into_iter() - .map(WorkspaceRenderElement::from), + self.tiling_layer.render( + renderer, + render_focus.then_some(last_active_seat), + zone, + overview, + resize_indicator, + indicator_thickness, + theme, + scanout_node, + &mut |elem| push(elem.into()), ); if let Some(alpha) = alpha { - elements.push( + push( Into::>::into(BackdropShader::element( renderer, self.backdrop_id.clone(), @@ -1836,11 +1822,9 @@ impl Workspace { } } - if !matches!(focused, Some(FocusTarget::Fullscreen(_))) { - elements.extend(fullscreen_elements.into_iter()); + for elem in fullscreen_elements { + push(elem); } - - Ok(elements) } #[profiling::function] @@ -1852,8 +1836,8 @@ impl Workspace { overview: (OverviewMode, Option<(SwapIndicator, Option<&Tree>)>), theme: &CosmicTheme, scanout_node: Option, - ) -> Result>, OutputNotMapped> - where + push: &mut dyn FnMut(WorkspaceRenderElement), + ) where R: AsGlowRenderer, R::TextureId: Send + Clone + 'static, CosmicMappedRenderElement: RenderElement, @@ -1861,8 +1845,6 @@ impl Workspace { CosmicStackRenderElement: RenderElement, WorkspaceRenderElement: RenderElement, { - let mut elements = Vec::default(); - let output_scale = self.output.current_scale().fractional_scale(); let zone = { let layer_map = layer_map_for_output(&self.output); @@ -1917,18 +1899,13 @@ impl Workspace { .as_logical() .to_physical_precise_round(output_scale); - elements.extend( - fullscreen - .surface - .popup_render_elements::>( - renderer, - render_loc, - output_scale.into(), - alpha, - scanout_node, - ) - .into_iter() - .map(Into::into), + fullscreen.surface.push_popup_render_elements( + renderer, + render_loc, + output_scale.into(), + alpha, + scanout_node, + &mut |elem| push(WorkspaceRenderElement::FullscreenPopup(elem.into())), ); } @@ -1962,30 +1939,20 @@ impl Workspace { OverviewMode::None => 1.0, }; - elements.extend( - self.floating_layer - .render_popups::(renderer, alpha, scanout_node) - .into_iter() - .map(WorkspaceRenderElement::from), - ); + self.floating_layer + .render_popups(renderer, alpha, scanout_node, &mut |elem| push(elem.into())); //tiling surfaces - elements.extend( - self.tiling_layer - .render_popups::( - renderer, - render_focus.then_some(last_active_seat), - zone, - overview, - theme, - scanout_node, - )? - .into_iter() - .map(WorkspaceRenderElement::from), + self.tiling_layer.render_popups( + renderer, + render_focus.then_some(last_active_seat), + zone, + overview, + theme, + scanout_node, + &mut |elem| push(elem.into()), ); } - - Ok(elements) } } @@ -2006,7 +1973,7 @@ where R: AsGlowRenderer, R::TextureId: 'static, { - OverrideRedirect(WaylandSurfaceRenderElement), + OverrideRedirect(SurfaceRenderElement), Fullscreen(RescaleRenderElement>), FullscreenPopup(CosmicWindowRenderElement), Window(CosmicMappedRenderElement), @@ -2244,13 +2211,13 @@ where } } -impl From> for WorkspaceRenderElement +impl From> for WorkspaceRenderElement where R: AsGlowRenderer, R::TextureId: 'static, CosmicMappedRenderElement: RenderElement, { - fn from(elem: WaylandSurfaceRenderElement) -> Self { + fn from(elem: SurfaceRenderElement) -> Self { WorkspaceRenderElement::OverrideRedirect(elem) } } diff --git a/src/shell/zoom.rs b/src/shell/zoom.rs index ffeed63b..02d1be55 100644 --- a/src/shell/zoom.rs +++ b/src/shell/zoom.rs @@ -11,7 +11,7 @@ use cosmic_comp_config::ZoomMovement; use cosmic_config::ConfigSet; use keyframe::{ease, functions::Linear}; use smithay::{ - backend::renderer::{ImportMem, Renderer, element::AsRenderElements}, + backend::renderer::{ImportMem, Renderer, element::memory::MemoryRenderBufferRenderElement}, desktop::space::SpaceElement, input::{ Seat, @@ -192,9 +192,12 @@ impl OutputZoomState { }); } - fn render(&mut self, renderer: &mut R, output: &Output) -> Vec - where - C: From< as AsRenderElements>::RenderElement>, + fn render( + &mut self, + renderer: &mut R, + output: &Output, + push: &mut dyn FnMut(MemoryRenderBufferRenderElement), + ) where R: Renderer + ImportMem, R::TextureId: Send + Clone + 'static, { @@ -208,8 +211,13 @@ impl OutputZoomState { .to_physical(scale.fractional_scale()) .to_i32_round(); - self.element - .render_elements(renderer, location, scale.fractional_scale().into(), 1.0) + self.element.push_render_elements( + renderer, + location, + scale.fractional_scale().into(), + 1.0, + push, + ) } } @@ -382,14 +390,16 @@ impl ZoomState { None } - pub fn render(renderer: &mut R, output: &Output) -> Vec - where - C: From< as AsRenderElements>::RenderElement>, + pub fn render( + renderer: &mut R, + output: &Output, + push: &mut dyn FnMut(MemoryRenderBufferRenderElement), + ) where R: Renderer + ImportMem, R::TextureId: Send + Clone + 'static, { let output_state = output.user_data().get::>().unwrap(); - output_state.lock().unwrap().render(renderer, output) + output_state.lock().unwrap().render(renderer, output, push) } } diff --git a/src/utils/iced/mod.rs b/src/utils/iced/mod.rs index 44a722db..2ee79733 100644 --- a/src/utils/iced/mod.rs +++ b/src/utils/iced/mod.rs @@ -35,7 +35,7 @@ use smithay::{ renderer::{ ImportMem, Renderer, element::{ - AsRenderElements, Kind, + Kind, memory::{MemoryRenderBuffer, MemoryRenderBufferRenderElement}, }, }, @@ -910,21 +910,18 @@ impl SpaceElement for IcedElement

{ } } -impl AsRenderElements for IcedElement

-where - P: Program + Send + 'static, - R: Renderer + ImportMem, - R::TextureId: Send + Clone + 'static, -{ - type RenderElement = MemoryRenderBufferRenderElement; - - fn render_elements>( +impl IcedElement

{ + pub fn push_render_elements( &self, renderer: &mut R, location: Point, mut scale: Scale, alpha: f32, - ) -> Vec { + push: &mut dyn FnMut(MemoryRenderBufferRenderElement), + ) where + R: Renderer + ImportMem, + R::TextureId: Send + Clone + 'static, + { let mut internal = self.0.lock().unwrap(); // makes partial borrows easier let internal_ref = &mut *internal; @@ -1055,11 +1052,10 @@ where Kind::Unspecified, ) { Ok(buffer) => { - return vec![C::from(buffer)]; + push(buffer); } Err(err) => tracing::warn!("What? {:?}", err), } } - Vec::new() } } diff --git a/src/utils/screenshot.rs b/src/utils/screenshot.rs index 541371cf..eb7730c7 100644 --- a/src/utils/screenshot.rs +++ b/src/utils/screenshot.rs @@ -14,7 +14,7 @@ use smithay::{ use tracing::warn; use crate::{ - backend::render::{RendererRef, element::AsGlowRenderer, wayland::SurfaceRenderElement}, + backend::render::{RendererRef, element::AsGlowRenderer}, shell::element::CosmicSurface, state::{State, advertised_node_for_surface}, }; @@ -27,7 +27,8 @@ pub fn screenshot_window(state: &mut State, surface: &CosmicSurface) { R::Error: Send + Sync + 'static, { let bbox = bbox_from_surface_tree(&window.wl_surface().unwrap(), (0, 0)); - let elements = window.render_elements::>( + let mut elements = Vec::new(); + window.push_render_elements( renderer, (-bbox.loc.x, -bbox.loc.y).into(), Scale::from(1.0), @@ -36,6 +37,8 @@ pub fn screenshot_window(state: &mut State, surface: &CosmicSurface) { None, false, [0; 4], + &mut |elem| elements.push(elem), + None, ); // TODO: 10-bit diff --git a/src/wayland/handlers/image_copy_capture/render.rs b/src/wayland/handlers/image_copy_capture/render.rs index c640a6d7..85759165 100644 --- a/src/wayland/handlers/image_copy_capture/render.rs +++ b/src/wayland/handlers/image_copy_capture/render.rs @@ -1,6 +1,7 @@ // SPDX-License-Identifier: GPL-3.0-only use calloop::LoopHandle; +use smallvec::SmallVec; use smithay::{ backend::{ allocator::{Buffer, Fourcc, format::get_transparent}, @@ -39,7 +40,8 @@ use tracing::warn; use crate::{ backend::render::{ - CursorMode, ElementFilter, RendererRef, cursor, + CursorMode, ElementFilter, RendererRef, + cursor::{self, CursorRenderElement}, element::{AsGlowRenderer, CosmicElement, DamageElement}, render_workspace, wayland::SurfaceRenderElement, @@ -591,44 +593,49 @@ pub fn render_window_to_buffer( if let Some(location) = location { if draw_cursor { - elements.extend( - cursor::draw_cursor( - renderer, - &seat, - location, - 1.0.into(), - 1.0, - common.clock.now(), - true, - ) - .into_iter() - .map(|(elem, hotspot)| { - WindowCaptureElement::CursorElement(RelocateRenderElement::from_element( - elem, - Point::from((-hotspot.x, -hotspot.y)), - Relocate::Relative, - )) - }), + cursor::draw_cursor( + renderer, + &seat, + location, + 1.0.into(), + 1.0, + common.clock.now(), + true, + &mut |elem, hotspot| { + elements.push(WindowCaptureElement::CursorElement( + RelocateRenderElement::from_element( + elem, + Point::from((-hotspot.x, -hotspot.y)), + Relocate::Relative, + ), + )); + }, ); } // TODO cosmic-workspaces wants to omit, but metadata cursor capture in portal should // still include dnd surface in window capture buffer? if draw_cursor && let Some(dnd_icon) = get_dnd_icon(&seat) { - elements.extend( - cursor::draw_dnd_icon( - renderer, - &dnd_icon.surface, - (location + dnd_icon.offset.to_f64()).to_i32_round(), - 1.0, - ) - .into_iter() - .map(WindowCaptureElement::from), + cursor::draw_dnd_icon( + renderer, + &dnd_icon.surface, + (location + dnd_icon.offset.to_f64()).to_i32_round(), + 1.0, + &mut |elem| { + elements.push( + RelocateRenderElement::from_element( + CursorRenderElement::Surface(elem), + Point::new(0, 0), + Relocate::Relative, + ) + .into(), + ) + }, ); } } - elements.extend(toplevel.render_elements::>( + toplevel.push_render_elements( renderer, (-geometry.loc.x, -geometry.loc.y).into(), Scale::from(1.0), @@ -637,7 +644,9 @@ pub fn render_window_to_buffer( None, false, [0; 4], - )); + &mut |elem| elements.push(elem.into()), + None, + ); if let Ok(dmabuf) = get_dmabuf(buffer) { let mut dmabuf_clone = dmabuf.clone(); @@ -803,7 +812,8 @@ pub fn render_cursor_to_buffer( .collect(); dt.damage_output(age, &additional_damage_elements)?; - let elements = cursor::draw_cursor( + let mut elements = SmallVec::<[WindowCaptureElement; 4]>::new_const(); + cursor::draw_cursor( renderer, seat, Point::from((0.0, 0.0)), @@ -811,11 +821,12 @@ pub fn render_cursor_to_buffer( 1.0, common.clock.now(), true, - ) - .into_iter() - .map(|(elem, _)| RelocateRenderElement::from_element(elem, (0, 0), Relocate::Relative)) - .map(WindowCaptureElement::from) - .collect::>(); + &mut |elem, _| { + elements.push( + RelocateRenderElement::from_element(elem, (0, 0), Relocate::Relative).into(), + ) + }, + ); if let Ok(dmabuf) = get_dmabuf(buffer) { let mut dmabuf_clone = dmabuf.clone(); From a9bf89d8b2676119ac444ed10edbb4dcbb0d242c Mon Sep 17 00:00:00 2001 From: Victoria Brekenfeld Date: Wed, 4 Mar 2026 19:02:29 +0100 Subject: [PATCH 23/92] element/wayland: Add blur background-effect --- src/backend/render/mod.rs | 4 + .../render/shaders/blur_downsample.frag | 26 + src/backend/render/shaders/blur_upsample.frag | 27 + .../render/shaders/clipped_surface.frag | 32 +- src/backend/render/wayland/blur_effect.rs | 578 ++++++++++++++++++ src/backend/render/wayland/clipped_surface.rs | 1 + src/backend/render/wayland/mod.rs | 19 +- src/state.rs | 5 + src/wayland/handlers/background_effect.rs | 59 ++ src/wayland/handlers/mod.rs | 1 + 10 files changed, 747 insertions(+), 5 deletions(-) create mode 100644 src/backend/render/shaders/blur_downsample.frag create mode 100644 src/backend/render/shaders/blur_upsample.frag create mode 100644 src/backend/render/wayland/blur_effect.rs create mode 100644 src/wayland/handlers/background_effect.rs diff --git a/src/backend/render/mod.rs b/src/backend/render/mod.rs index 717fe54e..f737afe1 100644 --- a/src/backend/render/mod.rs +++ b/src/backend/render/mod.rs @@ -19,6 +19,7 @@ use crate::{ shadow::{SHADOW_SHADER, ShadowShader}, wayland::{ SurfaceRenderElement, + blur_effect::BlurShaders, clipped_surface::{CLIPPING_SHADER, ClippingShader}, push_render_elements_from_surface_tree, }, @@ -425,6 +426,7 @@ pub fn init_shaders(renderer: &mut GlesRenderer) -> Result<(), GlesError> { UniformName::new("geo_size", UniformType::_2f), UniformName::new("corner_radius", UniformType::_4f), UniformName::new("input_to_geo", UniformType::Matrix3x3), + UniformName::new("noise", UniformType::_1f), ], )?; let shadow_shader = renderer.compile_custom_pixel_shader( @@ -440,6 +442,7 @@ pub fn init_shaders(renderer: &mut GlesRenderer) -> Result<(), GlesError> { UniformName::new("window_corner_radius", UniformType::_4f), ], )?; + let blur_shaders = BlurShaders::compile(renderer)?; let egl_context = renderer.egl_context(); egl_context @@ -457,6 +460,7 @@ pub fn init_shaders(renderer: &mut GlesRenderer) -> Result<(), GlesError> { egl_context .user_data() .insert_if_missing(|| ShadowShader(shadow_shader)); + egl_context.user_data().insert_if_missing(|| blur_shaders); Ok(()) } diff --git a/src/backend/render/shaders/blur_downsample.frag b/src/backend/render/shaders/blur_downsample.frag new file mode 100644 index 00000000..1fa24beb --- /dev/null +++ b/src/backend/render/shaders/blur_downsample.frag @@ -0,0 +1,26 @@ +#version 100 + +//_DEFINES_ + +precision highp float; + +varying vec2 v_coords; + +uniform sampler2D tex; +uniform vec2 half_pixel; +uniform float offset; + +uniform float alpha; +#if defined(DEBUG_FLAGS) +uniform float tint; +#endif + +void main() { + vec4 sum = texture2D(tex, v_coords) * 4.0; + sum += texture2D(tex, v_coords - half_pixel * offset); + sum += texture2D(tex, v_coords + half_pixel * offset); + sum += texture2D(tex, v_coords + vec2(half_pixel.x, -half_pixel.y) * offset); + sum += texture2D(tex, v_coords - vec2(half_pixel.x, -half_pixel.y) * offset); + + gl_FragColor = sum / sum.a; +} diff --git a/src/backend/render/shaders/blur_upsample.frag b/src/backend/render/shaders/blur_upsample.frag new file mode 100644 index 00000000..ac026d19 --- /dev/null +++ b/src/backend/render/shaders/blur_upsample.frag @@ -0,0 +1,27 @@ +#version 100 + +precision highp float; + +varying vec2 v_coords; + +uniform sampler2D tex; +uniform vec2 half_pixel; +uniform float offset; + +uniform float alpha; +#if defined(DEBUG_FLAGS) +uniform float tint; +#endif + +void main() { + vec4 sum = texture2D(tex, v_coords + vec2(-half_pixel.x * 2.0, 0.0) * offset); + sum += texture2D(tex, v_coords + vec2(-half_pixel.x, half_pixel.y) * offset) * 2.0; + sum += texture2D(tex, v_coords + vec2(0.0, half_pixel.y * 2.0) * offset); + sum += texture2D(tex, v_coords + vec2(half_pixel.x, half_pixel.y) * offset) * 2.0; + sum += texture2D(tex, v_coords + vec2(half_pixel.x * 2.0, 0.0) * offset); + sum += texture2D(tex, v_coords + vec2(half_pixel.x, -half_pixel.y) * offset) * 2.0; + sum += texture2D(tex, v_coords + vec2(0.0, -half_pixel.y * 2.0) * offset); + sum += texture2D(tex, v_coords + vec2(-half_pixel.x, -half_pixel.y) * offset) * 2.0; + + gl_FragColor = sum / sum.a; +} diff --git a/src/backend/render/shaders/clipped_surface.frag b/src/backend/render/shaders/clipped_surface.frag index dd863d23..d99d1a56 100644 --- a/src/backend/render/shaders/clipped_surface.frag +++ b/src/backend/render/shaders/clipped_surface.frag @@ -25,6 +25,7 @@ uniform float tint; uniform vec2 geo_size; uniform vec4 corner_radius; uniform mat3 input_to_geo; +uniform float noise; float rounding_alpha(vec2 coords, vec2 size) { vec2 center; @@ -51,15 +52,26 @@ float rounding_alpha(vec2 coords, vec2 size) { return 1.0 - smoothstep(radius - half_px, radius + half_px, dist); } -void main() { - vec3 coords_geo = input_to_geo * vec3(v_coords, 1.0); +float hash(vec2 p) { + vec3 p3 = fract(vec3(p.xyx) * 727.727); + p3 += dot(p3, p3.xyz + 33.33); + return fract((p3.x + p3.y) * p3.z); +} + +void main() { + if (alpha <= 0.0) { + discard; + } - // Sample the texture. vec4 color = texture2D(tex, v_coords); #if defined(NO_ALPHA) color = vec4(color.rgb, 1.0); #endif + if (color.a <= 0.0) { + discard; + } + vec3 coords_geo = input_to_geo * vec3(v_coords, 1.0); if (coords_geo.x < 0.0 || 1.0 < coords_geo.x || coords_geo.y < 0.0 || 1.0 < coords_geo.y) { // Clip outside geometry. color = vec4(0.0); @@ -68,8 +80,20 @@ void main() { color = color * rounding_alpha(coords_geo.xy * geo_size, geo_size); } + if (color.a <= 0.0) { + discard; + } + + if (noise > 0.0) { + // Add noise fx + // This can be used to achieve a glass look + float noiseHash = hash(v_coords); + float noiseAmount = (mod(noiseHash, 1.0) - 0.5); + color.rgb += noiseAmount * noise; + } + // Apply final alpha and tint. - color = color * alpha; + color *= alpha; #if defined(DEBUG_FLAGS) if (tint == 1.0) diff --git a/src/backend/render/wayland/blur_effect.rs b/src/backend/render/wayland/blur_effect.rs new file mode 100644 index 00000000..895cd19e --- /dev/null +++ b/src/backend/render/wayland/blur_effect.rs @@ -0,0 +1,578 @@ +use std::{ + borrow::{Borrow, BorrowMut}, + sync::{ + Arc, Mutex, + atomic::{AtomicBool, Ordering}, + }, +}; + +use cgmath::{Matrix3, SquareMatrix, Vector2}; +use smithay::{ + backend::{ + allocator::Fourcc, + renderer::{ + Bind, BlitFrame, Color32F, ContextId, Frame, FrameContext, ImportAll, Offscreen, + Renderer, Texture, TextureFilter, + element::{Element, Id, Kind, RenderElement}, + gles::{ + GlesError, GlesFrame, GlesRenderer, GlesTexProgram, GlesTexture, Uniform, + UniformName, UniformType, UniformValue, ffi, + }, + sync::SyncPoint, + utils::{CommitCounter, DamageSet}, + }, + }, + utils::{ + Buffer, Logical, Physical, Point, Rectangle, Scale, Size, Transform, user_data::UserDataMap, + }, + wayland::compositor::SurfaceData, +}; + +use crate::{ + backend::render::{element::AsGlowRenderer, wayland::clipped_surface::ClippingShader}, + wayland::handlers::background_effect::ComputedBlurRegionCachedState, +}; + +pub static BLUR_DOWNSAMPLE_SHADER: &str = include_str!("../shaders/blur_downsample.frag"); +pub static BLUR_UPSAMPLE_SHADER: &str = include_str!("../shaders/blur_upsample.frag"); + +const PASSES: usize = 3; +const OFFSET: f64 = 1.1; +const NOISE: f32 = 0.03; + +#[derive(Debug, Clone)] +pub struct BlurShaders { + down: GlesTexProgram, + up: GlesTexProgram, +} + +impl BlurShaders { + pub fn compile(renderer: &mut GlesRenderer) -> Result { + let up = renderer.compile_custom_texture_shader( + BLUR_UPSAMPLE_SHADER, + &[ + UniformName::new("half_pixel", UniformType::_2f), + UniformName::new("offset", UniformType::_1f), + ], + )?; + let down = renderer.compile_custom_texture_shader( + BLUR_DOWNSAMPLE_SHADER, + &[ + UniformName::new("half_pixel", UniformType::_2f), + UniformName::new("offset", UniformType::_1f), + ], + )?; + + Ok(BlurShaders { up, down }) + } + + pub fn get(renderer: &R) -> Self { + Borrow::::borrow(renderer.glow_renderer()) + .egl_context() + .user_data() + .get::() + .expect("Custom Shaders not initialized") + .clone() + } +} + +type BlurTexture = Mutex>; + +struct BlurState { + id: Id, + renderer_id: Option>, + src: Size, + offset: f64, + passes: usize, + region: Vec>, + dirty: Arc, +} + +unsafe impl Send for BlurState {} +unsafe impl Sync for BlurState {} + +impl BlurState { + fn new() -> Self { + BlurState { + id: Id::new(), + renderer_id: None, + src: Size::new(0., 0.), + offset: 0., + passes: 0, + region: Vec::new(), + dirty: Arc::new(AtomicBool::new(true)), + } + } +} + +pub struct BlurElement { + id: Id, + src: Size, + geometry: Rectangle, + scaling_shaders: BlurShaders, + render_shader: GlesTexProgram, + dirty: Arc, + region: Vec>, + offset: f64, + passes: usize, + uniforms: Vec>, +} + +impl BlurElement { + pub fn from_surface( + renderer: &mut R, + states: &SurfaceData, + geometry: Rectangle, + output_scale: f64, + radii: [u8; 4], + ) -> Result, R::Error> { + let mut blur_region_state = states.cached_state.get::(); + let Some(region) = blur_region_state.current().blur_region.as_ref() else { + return Ok(None); + }; + + let geo = geometry.to_physical_precise_round(output_scale); + let mut extended_geo = geo; + let radius = OFFSET * 2.0f64.powf(PASSES as f64); + extended_geo.loc -= Point::::new(radius, radius); + extended_geo.size += Size::::new(radius, radius).upscale(2.); + + // compute input_to_geo so that it crops the extended capture radius + let geo_scale = { + let Scale { x, y } = geo.size / extended_geo.size; + Matrix3::from_nonuniform_scale(x as f32, y as f32) + .invert() + .unwrap() + }; + let geo_translation = { + let offset = geo.loc - extended_geo.loc; + Matrix3::from_translation(Vector2::new( + (offset.x / extended_geo.size.w) as f32, + (offset.y / extended_geo.size.h) as f32, + )) + .invert() + .unwrap() + }; + let input_to_geo = geo_scale * geo_translation; + + let uniforms = vec![ + Uniform::new("geo_size", (geometry.size.w as f32, geometry.size.h as f32)), + Uniform::new( + "corner_radius", + [ + radii[3] as f32, + radii[1] as f32, + radii[0] as f32, + radii[2] as f32, + ], + ), + Uniform::new( + "input_to_geo", + UniformValue::Matrix3x3 { + matrices: vec![*AsRef::<[f32; 9]>::as_ref(&input_to_geo)], + transpose: false, + }, + ), + Uniform::new("noise", UniformValue::_1f(NOISE)), + ]; + + let state = states + .data_map + .get_or_insert_threadsafe::, _>(|| Mutex::new(BlurState::new())); + + Ok(Some(Self::from_state( + renderer, + extended_geo.to_logical(output_scale), + ®ion, + output_scale, + &mut *state.lock().unwrap(), + uniforms, + )?)) + } + + fn from_state( + renderer: &mut R, + geometry: Rectangle, + region: &Vec>, + output_scale: f64, + state: &mut BlurState, + uniforms: Vec>, + ) -> Result { + let renderer_id = renderer.glow_renderer().context_id(); + let src = geometry.size.to_buffer(output_scale, Transform::Normal); + + let dirty = !(state + .renderer_id + .as_ref() + .is_some_and(|id| id == &renderer_id) + && &state.region == region + && state.src == src); + state.renderer_id = Some(renderer_id); + state.offset = OFFSET * output_scale; // we want the blur result to be stable across scaling; + state.passes = PASSES; + state.region = region.clone(); + state.src = src; + if dirty { + state.dirty.store(dirty, Ordering::Release); + } + + Ok(BlurElement { + id: state.id.clone(), + src, + geometry, + scaling_shaders: BlurShaders::get(renderer), + render_shader: ClippingShader::get(renderer), + dirty: state.dirty.clone(), + offset: state.offset, + passes: state.passes, + region: region + .iter() + .map(|rect| rect.to_physical_precise_round(output_scale)) + .collect(), + uniforms, + }) + } +} + +impl Element for BlurElement { + fn id(&self) -> &Id { + &self.id + } + + fn current_commit(&self) -> CommitCounter { + Default::default() + } + + fn src(&self) -> Rectangle { + Rectangle::from_size(self.src) + } + + fn geometry(&self, scale: Scale) -> Rectangle { + self.geometry.to_physical_precise_round(scale) + } + + fn transform(&self) -> Transform { + Transform::Normal + } + + fn damage_since( + &self, + scale: Scale, + commit: Option, + ) -> DamageSet { + if commit.is_none() || self.dirty.load(Ordering::Acquire) { + DamageSet::from_slice(&[self.geometry.to_physical_precise_round(scale)]) + } else { + DamageSet::default() + } + } + + fn alpha(&self) -> f32 { + 1.0 + } + + fn kind(&self) -> Kind { + Kind::default() + } + + fn is_framebuffer_effect(&self) -> bool { + true + } +} + +impl RenderElement for BlurElement { + fn capture_framebuffer( + &self, + frame: &mut ::Frame<'_, '_>, + src: Rectangle, + dst: Rectangle, + cache: &UserDataMap, + ) -> Result<(), ::Error> { + let glow_frame = ::glow_frame_mut(frame); + let gles_frame = BorrowMut::>::borrow_mut(glow_frame); + let transform = gles_frame.transformation(); + let tex_size = self.src.to_i32_round(); + + let texture_ref = cache.get_or_insert_threadsafe(BlurTexture::default); + let mut texture_entry = texture_ref.lock().unwrap(); + if self.dirty.swap(false, Ordering::Acquire) { + texture_entry.take(); + } + + let mut renderer = gles_frame.renderer(); + if texture_entry.is_none() { + *texture_entry = Some( + renderer + .as_mut() + .create_buffer(Fourcc::Abgr8888, tex_size) + .map_err(R::from_gles_error)?, + ); + } + let texture = texture_entry.as_mut().unwrap(); + let mut off_texture = renderer + .as_mut() + .create_buffer(Fourcc::Abgr8888, tex_size) + .map_err(R::from_gles_error)?; + std::mem::drop(renderer); + + let sync = blit_from_active_fb(gles_frame, src, dst, transform, texture) + .map_err(R::from_gles_error)?; + gles_frame.wait(&sync).map_err(R::from_gles_error)?; + + let mut textures = [texture, &mut off_texture]; + render_blur( + gles_frame.renderer().as_mut(), + &self.scaling_shaders, + &mut textures, + self.offset, + self.passes, + ) + .map_err(R::from_gles_error)?; + + Ok(()) + } + + fn draw( + &self, + frame: &mut R::Frame<'_, '_>, + src: Rectangle, + dst: Rectangle, + damage: &[Rectangle], + opaque_regions: &[Rectangle], + cache: Option<&UserDataMap>, + ) -> Result<(), R::Error> { + let glow_frame = ::glow_frame_mut(frame); + let damage = self + .region + .iter() + .flat_map(|rect| damage.iter().flat_map(|r| r.intersection(*rect))) + .collect::>(); + let cache = cache.expect("Framebuffer element without cache?"); + let Some(texture) = cache.get::() else { + return Err(R::from_gles_error(GlesError::BlitError)); + }; + let texture_ref = texture.lock().unwrap(); + + BorrowMut::>::borrow_mut(glow_frame) + .render_texture_from_to( + texture_ref + .as_ref() + .ok_or(R::from_gles_error(GlesError::BlitError))?, + src, + dst, + &damage, + opaque_regions, + Transform::Normal, + 1.0, + Some(&self.render_shader), + &self.uniforms, + ) + .map_err(R::from_gles_error) + } +} + +fn blit_from_active_fb( + frame: &mut GlesFrame<'_, '_>, + src: Rectangle, + dst: Rectangle, + transform: Transform, + to_texture: &mut GlesTexture, +) -> Result { + if transform != Transform::Normal { + let tex_size = to_texture + .size() + .to_logical(1, Transform::Normal) + .to_physical(1); + let mut renderer = frame.renderer(); + let mut tmp_texture = renderer.as_mut().create_buffer( + Fourcc::Abgr8888, + dst.size.to_logical(1).to_buffer(1, Transform::Normal), + )?; + let mut fb_tmp = renderer.as_mut().bind(&mut tmp_texture)?; + let mut fb = renderer.as_mut().bind(to_texture)?; + std::mem::drop(renderer); + + frame.blit_to( + &mut fb_tmp, + dst, + Rectangle::from_size(dst.size), + TextureFilter::Linear, + )?; + std::mem::drop(fb_tmp); + + let mut renderer = frame.renderer(); + let mut frame = renderer + .as_mut() + .render(&mut fb, tex_size, Transform::Normal)?; + Frame::render_texture_from_to( + &mut frame, + &tmp_texture, + Rectangle::from_size( + dst.size + .to_logical(1) + .to_buffer(1, Transform::Normal) + .to_f64(), + ), + src.to_logical(1., Transform::Normal, &src.size) + .to_physical(1.) + .to_i32_round(), + &[Rectangle::from_size(dst.size)], + &[Rectangle::from_size(dst.size)], + transform, + 1.0, + )?; + std::mem::drop(tmp_texture); + frame.finish() + } else { + let mut fb = frame.renderer().as_mut().bind(to_texture)?; + + frame + .blit_to( + &mut fb, + dst, + src.to_logical(1., Transform::Normal, &src.size) + .to_physical(1.) + .to_i32_round(), + TextureFilter::Linear, + ) + .map(|_| SyncPoint::signaled()) + } +} + +fn render_blur( + renderer: &mut GlesRenderer, + shaders: &BlurShaders, + textures: &mut [&mut GlesTexture; 2], + offset: f64, + passes: usize, +) -> Result<(), GlesError> { + for i in 0..passes { + let tex_size = textures[0].size(); + let [src_tex, target_tex] = textures; + let mut fb = renderer.bind(*target_tex)?; + + let adjusted_tex_size = tex_size.downscale(1 << i); + let target_tex_size = tex_size + .downscale(1 << i + 1) + .to_logical(1, Transform::Normal) + .to_physical(1); + let half_pixel = [ + 0.5 / (adjusted_tex_size.w as f32), + 0.5 / (adjusted_tex_size.h as f32), + ]; + + let mut frame = renderer.render( + &mut fb, + tex_size.to_logical(1, Transform::Normal).to_physical(1), + Transform::Normal, + )?; + frame.clear( + Color32F::new(0., 0., 0., 0.), + &[Rectangle::from_size( + tex_size.to_logical(1, Transform::Normal).to_physical(1), + )], + )?; + frame.with_context(|gl| unsafe { + gl.TexParameteri( + ffi::TEXTURE_2D, + ffi::TEXTURE_WRAP_S, + ffi::CLAMP_TO_EDGE as i32, + ); + gl.TexParameteri( + ffi::TEXTURE_2D, + ffi::TEXTURE_WRAP_T, + ffi::CLAMP_TO_EDGE as i32, + ); + })?; + frame.render_texture_from_to( + src_tex, + Rectangle::from_size(adjusted_tex_size.to_f64()), + Rectangle::from_size(target_tex_size), + &[Rectangle::from_size(target_tex_size)], + &[Rectangle::from_size(target_tex_size)], + Transform::Normal, + 1.0, + Some(&shaders.down), + &[ + Uniform::new("half_pixel", half_pixel), + Uniform::new("offset", (offset / (1 << i) as f64) as f32), + ], + )?; + frame.with_context(|gl| unsafe { + gl.TexParameteri(ffi::TEXTURE_2D, ffi::TEXTURE_WRAP_S, ffi::REPEAT as i32); + gl.TexParameteri(ffi::TEXTURE_2D, ffi::TEXTURE_WRAP_T, ffi::REPEAT as i32); + })?; + let sync = frame.finish()?; + std::mem::drop(fb); + renderer.wait(&sync)?; + + textures.swap(0, 1); + } + + for i in 0..passes { + let tex_size = textures[0].size(); + let [src_tex, target_tex] = textures; + let mut fb = renderer.bind(*target_tex)?; + + let adjusted_tex_size = tex_size.downscale(1 << (passes - i)); + let target_tex_size = tex_size + .downscale(1 << (passes - i - 1)) + .to_logical(1, Transform::Normal) + .to_physical(1); + let half_pixel = [ + 0.5 / (adjusted_tex_size.w as f32), + 0.5 / (adjusted_tex_size.h as f32), + ]; + + let mut frame = renderer.render( + &mut fb, + tex_size.to_logical(1, Transform::Normal).to_physical(1), + Transform::Normal, + )?; + frame.clear( + Color32F::new(0., 0., 0., 0.), + &[Rectangle::from_size( + tex_size.to_logical(1, Transform::Normal).to_physical(1), + )], + )?; + frame.with_context(|gl| unsafe { + gl.TexParameteri( + ffi::TEXTURE_2D, + ffi::TEXTURE_WRAP_S, + ffi::CLAMP_TO_EDGE as i32, + ); + gl.TexParameteri( + ffi::TEXTURE_2D, + ffi::TEXTURE_WRAP_T, + ffi::CLAMP_TO_EDGE as i32, + ); + })?; + frame.render_texture_from_to( + src_tex, + Rectangle::from_size(adjusted_tex_size.to_f64()), + Rectangle::from_size(target_tex_size), + &[Rectangle::from_size(target_tex_size)], + &[Rectangle::from_size(target_tex_size)], + Transform::Normal, + 1.0, + Some(&shaders.up), + &[ + Uniform::new("half_pixel", half_pixel), + Uniform::new("offset", (offset / (1 << (passes - i)) as f64) as f32), + ], + )?; + frame.with_context(|gl| unsafe { + gl.TexParameteri(ffi::TEXTURE_2D, ffi::TEXTURE_WRAP_S, ffi::REPEAT as i32); + gl.TexParameteri(ffi::TEXTURE_2D, ffi::TEXTURE_WRAP_T, ffi::REPEAT as i32); + })?; + let sync = frame.finish()?; + std::mem::drop(fb); + renderer.wait(&sync)?; + + textures.swap(0, 1); + } + + // textures always end up the right way around with `self.texture` containing our final render, + // since we render PASSES * 2 (downscale and upscale), so the number of swaps is always even. + Ok(()) +} diff --git a/src/backend/render/wayland/clipped_surface.rs b/src/backend/render/wayland/clipped_surface.rs index 37eee7f8..92e64ad0 100644 --- a/src/backend/render/wayland/clipped_surface.rs +++ b/src/backend/render/wayland/clipped_surface.rs @@ -111,6 +111,7 @@ where transpose: false, }, ), + Uniform::new("noise", UniformValue::_1f(0.0)), ]; Self { diff --git a/src/backend/render/wayland/mod.rs b/src/backend/render/wayland/mod.rs index d766552a..b9e07a80 100644 --- a/src/backend/render/wayland/mod.rs +++ b/src/backend/render/wayland/mod.rs @@ -12,13 +12,16 @@ use smithay::{ use tracing::warn; use crate::backend::render::{ - element::AsGlowRenderer, wayland::clipped_surface::ClippedSurfaceRenderElement, + element::AsGlowRenderer, + wayland::{blur_effect::BlurElement, clipped_surface::ClippedSurfaceRenderElement}, }; +pub mod blur_effect; pub mod clipped_surface; render_elements! { pub SurfaceRenderElement where R: AsGlowRenderer + ImportAll; + Blur=BlurElement, Clipped=ClippedSurfaceRenderElement, Wayland=WaylandSurfaceRenderElement, } @@ -67,6 +70,7 @@ pub fn push_render_elements_from_surface_tree( let mut location = *location; let kind = kind.eval(states); let data = states.data_map.get::(); + let mut blur = Ok(None); if let Some(data) = data { let has_view = if let Some(view) = data.lock().unwrap().view() { @@ -82,6 +86,9 @@ pub fn push_render_elements_from_surface_tree( renderer, surface, states, location, alpha, kind, ) { Ok(Some(surface)) => { + blur = BlurElement::from_surface( + renderer, states, geometry, scale.x, radii, + ); let elem: SurfaceRenderElement = if radii.iter().any(|r| *r != 0) && should_clip && ClippedSurfaceRenderElement::will_clip( @@ -113,6 +120,16 @@ pub fn push_render_elements_from_surface_tree( if surface == main_surface { passed_main = true; } + + if let Ok(Some(elem)) = blur { + if let Some(push_below) = push_below.as_mut() + && passed_main + { + push_below(elem.into()); + } else { + push_above(elem.into()); + } + } }, |_, _, _| true, ); diff --git a/src/state.rs b/src/state.rs index 68824ea7..f377010c 100644 --- a/src/state.rs +++ b/src/state.rs @@ -73,6 +73,7 @@ use smithay::{ utils::{Clock, Monotonic, Point}, wayland::{ alpha_modifier::AlphaModifierState, + background_effect::BackgroundEffectState, compositor::{CompositorClientState, CompositorState, SurfaceData}, cursor_shape::CursorShapeManagerState, dmabuf::{DmabufFeedback, DmabufGlobal, DmabufState}, @@ -281,6 +282,7 @@ pub struct Common { pub a11y_state: A11yState, pub dbus_state: DBusState, pub keyboard_layout_state: KeyboardLayoutState, + pub background_effect_state: BackgroundEffectState, // shell-related wayland state pub xdg_shell_state: XdgShellState, @@ -686,6 +688,8 @@ impl State { FixesState::new::(dh); let keyboard_layout_state = KeyboardLayoutState::new::(&dh, client_not_sandboxed); + let background_effect_state = BackgroundEffectState::new::(dh); + let idle_notifier_state = IdleNotifierState::::new(dh, handle.clone()); let idle_inhibit_manager_state = IdleInhibitManagerState::new::(dh); let idle_inhibiting_surfaces = HashSet::new(); @@ -789,6 +793,7 @@ impl State { xdg_activation_state, xdg_foreign_state, workspace_state, + background_effect_state, a11y_state, xwayland_scale: None, xwayland_state: None, diff --git a/src/wayland/handlers/background_effect.rs b/src/wayland/handlers/background_effect.rs new file mode 100644 index 00000000..28682e03 --- /dev/null +++ b/src/wayland/handlers/background_effect.rs @@ -0,0 +1,59 @@ +use smithay::{ + reexports::wayland_server::{DisplayHandle, protocol::wl_surface::WlSurface}, + utils::{Logical, Rectangle}, + wayland::{ + background_effect::{Capability, ExtBackgroundEffectHandler}, + compositor::{Cacheable, RectangleKind, RegionAttributes, with_states}, + }, +}; + +use crate::state::State; + +#[derive(Debug, Clone, Default)] +pub struct ComputedBlurRegionCachedState { + /// Region of the surface that will have its background blurred. + pub blur_region: Option>>, +} + +impl Cacheable for ComputedBlurRegionCachedState { + fn commit(&mut self, _dh: &DisplayHandle) -> Self { + self.clone() + } + + fn merge_into(self, into: &mut Self, _dh: &DisplayHandle) { + *into = self; + } +} + +impl ExtBackgroundEffectHandler for State { + fn capabilities(&self) -> Capability { + Capability::Blur + } + + fn set_blur_region(&mut self, surface: WlSurface, region: RegionAttributes) { + with_states(&surface, |states| { + let mut blur_state = states.cached_state.get::(); + + blur_state.pending().blur_region = Some({ + let (added, subtracted) = region + .rects + .iter() + .cloned() + .partition::, _>(|(op, _)| matches!(op, RectangleKind::Add)); + let added = added.into_iter().map(|(_, rect)| rect).collect::>(); + Rectangle::subtract_rects_many_in_place( + added, + subtracted.into_iter().map(|(_, rect)| rect), + ) + }) + }) + } + + fn unset_blur_region(&mut self, surface: WlSurface) { + with_states(&surface, |states| { + let mut blur_state = states.cached_state.get::(); + + blur_state.pending().blur_region.take(); + }) + } +} diff --git a/src/wayland/handlers/mod.rs b/src/wayland/handlers/mod.rs index c6461a2e..651eadc9 100644 --- a/src/wayland/handlers/mod.rs +++ b/src/wayland/handlers/mod.rs @@ -1,6 +1,7 @@ // SPDX-License-Identifier: GPL-3.0-only pub mod a11y; +pub mod background_effect; pub mod buffer; pub mod compositor; pub mod corner_radius; From eb41b076ed4b79811a45b524e66d72f53444d9b3 Mon Sep 17 00:00:00 2001 From: Victoria Brekenfeld Date: Fri, 6 Mar 2026 17:19:12 +0100 Subject: [PATCH 24/92] render: Use NamespaceElements to fix blurred lower layer-shell surfaces --- src/backend/render/mod.rs | 27 ++++++++++++++---- src/input/mod.rs | 9 ++++-- src/shell/focus/order.rs | 58 ++++++++++++++++++++++++++++++--------- src/shell/mod.rs | 3 +- src/shell/workspace.rs | 32 ++++++++++++++++++++- src/state.rs | 50 +++++++++++++++++---------------- 6 files changed, 134 insertions(+), 45 deletions(-) diff --git a/src/backend/render/mod.rs b/src/backend/render/mod.rs index f737afe1..a22289f4 100644 --- a/src/backend/render/mod.rs +++ b/src/backend/render/mod.rs @@ -55,7 +55,7 @@ use smithay::{ Color32F, Offscreen, Texture, TextureFilter, damage::{Error as RenderError, OutputDamageTracker, RenderOutputResult}, element::{ - Element, Id, Kind, RenderElement, WeakId, + Element, Id, Kind, NamespacedElement, RenderElement, WeakId, texture::{TextureRenderBuffer, TextureRenderElement}, utils::{ ConstrainAlign, ConstrainScaleBehavior, CropRenderElement, Relocate, @@ -825,7 +825,10 @@ where }) } Stage::LayerPopup { - popup, location, .. + popup, + location, + workspace_idx, + .. } => { let mut geometry = popup.geometry().as_global(); geometry.loc += location; @@ -843,11 +846,20 @@ where false, [0; 4], FRAME_TIME_FILTER, - &mut |elem| elements.extend(crop_to_output(elem.into()).map(Into::into)), + &mut |elem| { + elements.extend( + crop_to_output(NamespacedElement::new(elem, workspace_idx).into()) + .map(Into::into), + ) + }, None, ) } - Stage::LayerSurface { layer, location } => { + Stage::LayerSurface { + layer, + location, + workspace_idx, + } => { let mut geometry = layer.geometry().as_global(); geometry.loc += location; @@ -864,7 +876,12 @@ where false, [0; 4], FRAME_TIME_FILTER, - &mut |elem| elements.extend(crop_to_output(elem.into()).map(Into::into)), + &mut |elem| { + elements.extend( + crop_to_output(NamespacedElement::new(elem, workspace_idx).into()) + .map(Into::into), + ) + }, None, ); } diff --git a/src/input/mod.rs b/src/input/mod.rs index da3014ae..69b5064c 100644 --- a/src/input/mod.rs +++ b/src/input/mod.rs @@ -2132,6 +2132,7 @@ impl State { layer, popup, location, + .. } => { if layer.can_receive_keyboard_focus() { let surface = popup.wl_surface(); @@ -2149,7 +2150,9 @@ impl State { } } } - Stage::LayerSurface { layer, location } => { + Stage::LayerSurface { + layer, location, .. + } => { if under_from_surface_tree( layer.wl_surface(), global_pos.as_logical(), @@ -2301,7 +2304,9 @@ impl State { )))); } } - Stage::LayerSurface { layer, location } => { + Stage::LayerSurface { + layer, location, .. + } => { let surface = layer.wl_surface(); if let Some((surface, surface_loc)) = under_from_surface_tree( surface, diff --git a/src/shell/focus/order.rs b/src/shell/focus/order.rs index d02b310e..fb904c01 100644 --- a/src/shell/focus/order.rs +++ b/src/shell/focus/order.rs @@ -32,10 +32,12 @@ pub enum Stage<'a> { layer: LayerSurface, popup: &'a PopupKind, location: Point, + workspace_idx: usize, }, LayerSurface { layer: LayerSurface, location: Point, + workspace_idx: usize, }, OverrideRedirect { surface: &'a X11Surface, @@ -95,10 +97,15 @@ fn render_input_order_internal( layer, popup: &popup, location, + workspace_idx: current.1, })?; } for (layer, location) in layer_surfaces(output, Layer::Overlay, element_filter) { - callback(Stage::LayerSurface { layer, location })?; + callback(Stage::LayerSurface { + layer, + location, + workspace_idx: current.1, + })?; } // calculate a bunch of stuff for workspace transitions @@ -187,7 +194,7 @@ fn render_input_order_internal( }); ( - Some((previous, has_fullscreen, offset)), + Some((previous, previous_idx, has_fullscreen, offset)), Point::::from(match (layout, forward) { (WorkspaceLayout::Vertical, true) => (0, output_size.h + offset.y), (WorkspaceLayout::Vertical, false) => (0, -(output_size.h - offset.y)), @@ -206,6 +213,7 @@ fn render_input_order_internal( layer, popup: &popup, location, + workspace_idx: current.1, })?; } } @@ -236,7 +244,7 @@ fn render_input_order_internal( if element_filter != ElementFilter::LayerShellOnly { // previous workspace popups - if let Some((previous_handle, _, offset)) = previous.as_ref() { + if let Some((previous_handle, _, _, offset)) = previous.as_ref() { let Some(workspace) = shell.workspaces.space_for_handle(previous_handle) else { return ControlFlow::Break(Err(OutputNoMode)); }; @@ -265,11 +273,12 @@ fn render_input_order_internal( layer, popup: &popup, location, + workspace_idx: current.1, })?; } } - if let Some((_, has_fullscreen, offset)) = previous.as_ref() + if let Some((_, idx, has_fullscreen, offset)) = previous.as_ref() && !has_fullscreen { // previous bottom layer popups @@ -278,6 +287,7 @@ fn render_input_order_internal( layer, popup: &popup, location: location + offset.as_global(), + workspace_idx: **idx, })?; } } @@ -289,11 +299,12 @@ fn render_input_order_internal( layer, popup: &popup, location, + workspace_idx: current.1, })?; } } - if let Some((_, has_fullscreen, offset)) = previous.as_ref() + if let Some((_, idx, has_fullscreen, offset)) = previous.as_ref() && !has_fullscreen { // previous background layer popups @@ -302,6 +313,7 @@ fn render_input_order_internal( layer, popup: &popup, location: location + offset.as_global(), + workspace_idx: **idx, })?; } } @@ -309,7 +321,11 @@ fn render_input_order_internal( if !has_focused_fullscreen { // top-layer shell for (layer, location) in layer_surfaces(output, Layer::Top, element_filter) { - callback(Stage::LayerSurface { layer, location })?; + callback(Stage::LayerSurface { + layer, + location, + workspace_idx: current.1, + })?; } } @@ -326,7 +342,7 @@ fn render_input_order_internal( })?; // previous workspace windows - if let Some((previous_handle, _, offset)) = previous.as_ref() { + if let Some((previous_handle, _, _, offset)) = previous.as_ref() { let Some(workspace) = shell.workspaces.space_for_handle(previous_handle) else { return ControlFlow::Break(Err(OutputNoMode)); }; @@ -341,17 +357,25 @@ fn render_input_order_internal( // bottom layer for (layer, mut location) in layer_surfaces(output, Layer::Bottom, element_filter) { location += current_offset.as_global(); - callback(Stage::LayerSurface { layer, location })?; + callback(Stage::LayerSurface { + layer, + location, + workspace_idx: current.1, + })?; } } - if let Some((_, has_fullscreen, offset)) = previous.as_ref() + if let Some((_, idx, has_fullscreen, offset)) = previous.as_ref() && !has_fullscreen { // previous bottom layer for (layer, mut location) in layer_surfaces(output, Layer::Bottom, element_filter) { location += offset.as_global(); - callback(Stage::LayerSurface { layer, location })?; + callback(Stage::LayerSurface { + layer, + location, + workspace_idx: **idx, + })?; } } @@ -359,17 +383,25 @@ fn render_input_order_internal( // background layer for (layer, mut location) in layer_surfaces(output, Layer::Background, element_filter) { location += current_offset.as_global(); - callback(Stage::LayerSurface { layer, location })?; + callback(Stage::LayerSurface { + layer, + location, + workspace_idx: current.1, + })?; } } - if let Some((_, has_fullscreen, offset)) = previous.as_ref() + if let Some((_, idx, has_fullscreen, offset)) = previous.as_ref() && !has_fullscreen { // previous background layer for (layer, mut location) in layer_surfaces(output, Layer::Background, element_filter) { location += offset.as_global(); - callback(Stage::LayerSurface { layer, location })?; + callback(Stage::LayerSurface { + layer, + location, + workspace_idx: **idx, + })?; } } diff --git a/src/shell/mod.rs b/src/shell/mod.rs index 60b3d1e1..b4559dce 100644 --- a/src/shell/mod.rs +++ b/src/shell/mod.rs @@ -5038,13 +5038,14 @@ impl Shell { let map = smithay::desktop::layer_map_for_output(output); for layer_surface in map.layers() { + let namespace = self.workspaces.active_num(output).1; layer_surface.take_presentation_feedback( &mut output_presentation_feedback, surface_primary_scanout_output, |surface, _| { surface_presentation_feedback_flags_from_states( surface, - None, + Some(namespace), render_element_states, ) }, diff --git a/src/shell/workspace.rs b/src/shell/workspace.rs index 6407935d..b8b104c5 100644 --- a/src/shell/workspace.rs +++ b/src/shell/workspace.rs @@ -31,7 +31,7 @@ use indexmap::IndexSet; use keyframe::{ease, functions::EaseInOutCubic}; use smallvec::SmallVec; use smithay::backend::drm::DrmNode; -use smithay::backend::renderer::element::Kind; +use smithay::backend::renderer::element::{Kind, NamespacedElement}; use smithay::output::WeakOutput; use smithay::utils::user_data::UserDataMap; use smithay::{ @@ -1974,6 +1974,7 @@ where R::TextureId: 'static, { OverrideRedirect(SurfaceRenderElement), + LowerLayerShell(NamespacedElement>), Fullscreen(RescaleRenderElement>), FullscreenPopup(CosmicWindowRenderElement), Window(CosmicMappedRenderElement), @@ -1988,6 +1989,7 @@ where fn id(&self) -> &smithay::backend::renderer::element::Id { match self { WorkspaceRenderElement::OverrideRedirect(elem) => elem.id(), + WorkspaceRenderElement::LowerLayerShell(elem) => elem.id(), WorkspaceRenderElement::Fullscreen(elem) => elem.id(), WorkspaceRenderElement::FullscreenPopup(elem) => elem.id(), WorkspaceRenderElement::Window(elem) => elem.id(), @@ -1998,6 +2000,7 @@ where fn current_commit(&self) -> smithay::backend::renderer::utils::CommitCounter { match self { WorkspaceRenderElement::OverrideRedirect(elem) => elem.current_commit(), + WorkspaceRenderElement::LowerLayerShell(elem) => elem.current_commit(), WorkspaceRenderElement::Fullscreen(elem) => elem.current_commit(), WorkspaceRenderElement::FullscreenPopup(elem) => elem.current_commit(), WorkspaceRenderElement::Window(elem) => elem.current_commit(), @@ -2008,6 +2011,7 @@ where fn src(&self) -> Rectangle { match self { WorkspaceRenderElement::OverrideRedirect(elem) => elem.src(), + WorkspaceRenderElement::LowerLayerShell(elem) => elem.src(), WorkspaceRenderElement::Fullscreen(elem) => elem.src(), WorkspaceRenderElement::FullscreenPopup(elem) => elem.src(), WorkspaceRenderElement::Window(elem) => elem.src(), @@ -2018,6 +2022,7 @@ where fn geometry(&self, scale: Scale) -> Rectangle { match self { WorkspaceRenderElement::OverrideRedirect(elem) => elem.geometry(scale), + WorkspaceRenderElement::LowerLayerShell(elem) => elem.geometry(scale), WorkspaceRenderElement::Fullscreen(elem) => elem.geometry(scale), WorkspaceRenderElement::FullscreenPopup(elem) => elem.geometry(scale), WorkspaceRenderElement::Window(elem) => elem.geometry(scale), @@ -2028,6 +2033,7 @@ where fn location(&self, scale: Scale) -> Point { match self { WorkspaceRenderElement::OverrideRedirect(elem) => elem.location(scale), + WorkspaceRenderElement::LowerLayerShell(elem) => elem.location(scale), WorkspaceRenderElement::Fullscreen(elem) => elem.location(scale), WorkspaceRenderElement::FullscreenPopup(elem) => elem.location(scale), WorkspaceRenderElement::Window(elem) => elem.location(scale), @@ -2038,6 +2044,7 @@ where fn transform(&self) -> smithay::utils::Transform { match self { WorkspaceRenderElement::OverrideRedirect(elem) => elem.transform(), + WorkspaceRenderElement::LowerLayerShell(elem) => elem.transform(), WorkspaceRenderElement::Fullscreen(elem) => elem.transform(), WorkspaceRenderElement::FullscreenPopup(elem) => elem.transform(), WorkspaceRenderElement::Window(elem) => elem.transform(), @@ -2052,6 +2059,7 @@ where ) -> DamageSet { match self { WorkspaceRenderElement::OverrideRedirect(elem) => elem.damage_since(scale, commit), + WorkspaceRenderElement::LowerLayerShell(elem) => elem.damage_since(scale, commit), WorkspaceRenderElement::Fullscreen(elem) => elem.damage_since(scale, commit), WorkspaceRenderElement::FullscreenPopup(elem) => elem.damage_since(scale, commit), WorkspaceRenderElement::Window(elem) => elem.damage_since(scale, commit), @@ -2062,6 +2070,7 @@ where fn opaque_regions(&self, scale: Scale) -> OpaqueRegions { match self { WorkspaceRenderElement::OverrideRedirect(elem) => elem.opaque_regions(scale), + WorkspaceRenderElement::LowerLayerShell(elem) => elem.opaque_regions(scale), WorkspaceRenderElement::Fullscreen(elem) => elem.opaque_regions(scale), WorkspaceRenderElement::FullscreenPopup(elem) => elem.opaque_regions(scale), WorkspaceRenderElement::Window(elem) => elem.opaque_regions(scale), @@ -2072,6 +2081,7 @@ where fn alpha(&self) -> f32 { match self { WorkspaceRenderElement::OverrideRedirect(elem) => elem.alpha(), + WorkspaceRenderElement::LowerLayerShell(elem) => elem.alpha(), WorkspaceRenderElement::Fullscreen(elem) => elem.alpha(), WorkspaceRenderElement::FullscreenPopup(elem) => elem.alpha(), WorkspaceRenderElement::Window(elem) => elem.alpha(), @@ -2082,6 +2092,7 @@ where fn kind(&self) -> Kind { match self { WorkspaceRenderElement::OverrideRedirect(elem) => elem.kind(), + WorkspaceRenderElement::LowerLayerShell(elem) => elem.kind(), WorkspaceRenderElement::Fullscreen(elem) => elem.kind(), WorkspaceRenderElement::FullscreenPopup(elem) => elem.kind(), WorkspaceRenderElement::Window(elem) => elem.kind(), @@ -2092,6 +2103,7 @@ where fn is_framebuffer_effect(&self) -> bool { match self { WorkspaceRenderElement::OverrideRedirect(elem) => elem.is_framebuffer_effect(), + WorkspaceRenderElement::LowerLayerShell(elem) => elem.is_framebuffer_effect(), WorkspaceRenderElement::Fullscreen(elem) => elem.is_framebuffer_effect(), WorkspaceRenderElement::FullscreenPopup(elem) => elem.is_framebuffer_effect(), WorkspaceRenderElement::Window(elem) => elem.is_framebuffer_effect(), @@ -2118,6 +2130,9 @@ where WorkspaceRenderElement::OverrideRedirect(elem) => { elem.draw(frame, src, dst, damage, opaque_regions, cache) } + WorkspaceRenderElement::LowerLayerShell(elem) => { + elem.draw(frame, src, dst, damage, opaque_regions, cache) + } WorkspaceRenderElement::Fullscreen(elem) => { elem.draw(frame, src, dst, damage, opaque_regions, cache) } @@ -2146,6 +2161,7 @@ where ) -> Option> { match self { WorkspaceRenderElement::OverrideRedirect(elem) => elem.underlying_storage(renderer), + WorkspaceRenderElement::LowerLayerShell(elem) => elem.underlying_storage(renderer), WorkspaceRenderElement::Fullscreen(elem) => elem.underlying_storage(renderer), WorkspaceRenderElement::FullscreenPopup(elem) => elem.underlying_storage(renderer), WorkspaceRenderElement::Window(elem) => elem.underlying_storage(renderer), @@ -2166,6 +2182,9 @@ where WorkspaceRenderElement::OverrideRedirect(elem) => { elem.capture_framebuffer(frame, src, dst, cache) } + WorkspaceRenderElement::LowerLayerShell(elem) => { + elem.capture_framebuffer(frame, src, dst, cache) + } WorkspaceRenderElement::Fullscreen(elem) => { elem.capture_framebuffer(frame, src, dst, cache) } @@ -2222,6 +2241,17 @@ where } } +impl From>> for WorkspaceRenderElement +where + R: AsGlowRenderer, + R::TextureId: 'static, + CosmicMappedRenderElement: RenderElement, +{ + fn from(elem: NamespacedElement>) -> Self { + WorkspaceRenderElement::LowerLayerShell(elem) + } +} + impl From> for WorkspaceRenderElement where R: AsGlowRenderer, diff --git a/src/state.rs b/src/state.rs index f377010c..cf66aeaa 100644 --- a/src/state.rs +++ b/src/state.rs @@ -939,19 +939,22 @@ impl Common { render_element_states: &RenderElementStates, ) { let shell = self.shell.read(); - let processor = |surface: &WlSurface, states: &SurfaceData| { - let primary_scanout_output = update_surface_primary_scanout_output( - surface, - output, - states, - None, - render_element_states, - primary_scanout_output_compare, - ); - if let Some(output) = primary_scanout_output { - with_fractional_scale(states, |fraction_scale| { - fraction_scale.set_preferred_scale(output.current_scale().fractional_scale()); - }); + let processor = |namespace: Option| { + move |surface: &WlSurface, states: &SurfaceData| { + let primary_scanout_output = update_surface_primary_scanout_output( + surface, + output, + states, + namespace, + render_element_states, + primary_scanout_output_compare, + ); + if let Some(output) = primary_scanout_output { + with_fractional_scale(states, |fraction_scale| { + fraction_scale + .set_preferred_scale(output.current_scale().fractional_scale()); + }); + } } }; @@ -959,7 +962,7 @@ impl Common { if let Some(session_lock) = shell.session_lock.as_ref() && let Some(lock_surface) = session_lock.surfaces.get(output) { - with_surfaces_surface_tree(lock_surface.wl_surface(), processor) + with_surfaces_surface_tree(lock_surface.wl_surface(), processor(None)) } for seat in shell @@ -971,7 +974,7 @@ impl Common { // cursor ... if let CursorImageStatus::Surface(wl_surface) = cursor_status { - with_surfaces_surface_tree(&wl_surface, processor); + with_surfaces_surface_tree(&wl_surface, processor(None)); } // grabs @@ -979,12 +982,12 @@ impl Common { && let Some(grab_state) = move_grab.lock().unwrap().as_ref() { for (window, _) in grab_state.element().windows() { - window.with_surfaces(processor); + window.with_surfaces(processor(None)); } } if let Some(icon) = get_dnd_icon(seat) { - with_surfaces_surface_tree(&icon.surface, processor); + with_surfaces_surface_tree(&icon.surface, processor(None)); } } @@ -992,7 +995,7 @@ impl Common { for set in shell.workspaces.sets.values() { set.sticky_layer.mapped().for_each(|mapped| { for (window, _) in mapped.windows() { - window.with_surfaces(processor); + window.with_surfaces(processor(None)); } }); } @@ -1000,16 +1003,16 @@ impl Common { // normal windows for space in shell.workspaces.spaces() { if let Some(fs) = space.get_fullscreen(shell.seats.last_active()) { - fs.surface.with_surfaces(processor); + fs.surface.with_surfaces(processor(None)); } space.mapped().for_each(|mapped| { for (window, _) in mapped.windows() { - window.with_surfaces(processor); + window.with_surfaces(processor(None)); } }); space.minimized_windows.iter().for_each(|m| { for window in m.windows() { - window.with_surfaces(processor); + window.with_surfaces(processor(None)); } }) } @@ -1017,15 +1020,16 @@ impl Common { // OR windows shell.override_redirect_windows.iter().for_each(|or| { if let Some(wl_surface) = or.wl_surface() { - with_surfaces_surface_tree(&wl_surface, processor); + with_surfaces_surface_tree(&wl_surface, processor(None)); } }); // layer surfaces for o in shell.outputs() { + let namespace = shell.workspaces.active_num(o).1; let map = smithay::desktop::layer_map_for_output(o); for layer_surface in map.layers() { - layer_surface.with_surfaces(processor); + layer_surface.with_surfaces(processor(Some(namespace))); } } } From 1c5ef576f413631a3ec5fc7b195c92624d7db69b Mon Sep 17 00:00:00 2001 From: Victoria Brekenfeld Date: Tue, 10 Mar 2026 17:10:38 +0100 Subject: [PATCH 25/92] render: Don't use separate `damage_output` calls --- src/backend/render/element.rs | 30 +++++++++++++++++++ .../handlers/image_copy_capture/render.rs | 13 ++++---- 2 files changed, 35 insertions(+), 8 deletions(-) diff --git a/src/backend/render/element.rs b/src/backend/render/element.rs index 34a351c9..103fa52f 100644 --- a/src/backend/render/element.rs +++ b/src/backend/render/element.rs @@ -43,6 +43,7 @@ where CropRenderElement>>, ), Zoom(MemoryRenderBufferRenderElement), + Damage(DamageElement), #[cfg(feature = "debug")] Egui(TextureRenderElement), } @@ -61,6 +62,7 @@ where CosmicElement::MoveGrab(elem) => elem.id(), CosmicElement::Postprocess(elem) => elem.id(), CosmicElement::Zoom(elem) => elem.id(), + CosmicElement::Damage(elem) => elem.id(), #[cfg(feature = "debug")] CosmicElement::Egui(elem) => elem.id(), } @@ -74,6 +76,7 @@ where CosmicElement::MoveGrab(elem) => elem.current_commit(), CosmicElement::Postprocess(elem) => elem.current_commit(), CosmicElement::Zoom(elem) => elem.current_commit(), + CosmicElement::Damage(elem) => elem.current_commit(), #[cfg(feature = "debug")] CosmicElement::Egui(elem) => elem.current_commit(), } @@ -87,6 +90,7 @@ where CosmicElement::MoveGrab(elem) => elem.src(), CosmicElement::Postprocess(elem) => elem.src(), CosmicElement::Zoom(elem) => elem.src(), + CosmicElement::Damage(elem) => elem.src(), #[cfg(feature = "debug")] CosmicElement::Egui(elem) => elem.src(), } @@ -100,6 +104,7 @@ where CosmicElement::MoveGrab(elem) => elem.geometry(scale), CosmicElement::Postprocess(elem) => elem.geometry(scale), CosmicElement::Zoom(elem) => elem.geometry(scale), + CosmicElement::Damage(elem) => elem.geometry(scale), #[cfg(feature = "debug")] CosmicElement::Egui(elem) => elem.geometry(scale), } @@ -113,6 +118,7 @@ where CosmicElement::MoveGrab(elem) => elem.location(scale), CosmicElement::Postprocess(elem) => elem.location(scale), CosmicElement::Zoom(elem) => elem.location(scale), + CosmicElement::Damage(elem) => elem.location(scale), #[cfg(feature = "debug")] CosmicElement::Egui(elem) => elem.location(scale), } @@ -126,6 +132,7 @@ where CosmicElement::MoveGrab(elem) => elem.transform(), CosmicElement::Postprocess(elem) => elem.transform(), CosmicElement::Zoom(elem) => elem.transform(), + CosmicElement::Damage(elem) => elem.transform(), #[cfg(feature = "debug")] CosmicElement::Egui(elem) => elem.transform(), } @@ -143,6 +150,7 @@ where CosmicElement::MoveGrab(elem) => elem.damage_since(scale, commit), CosmicElement::Postprocess(elem) => elem.damage_since(scale, commit), CosmicElement::Zoom(elem) => elem.damage_since(scale, commit), + CosmicElement::Damage(elem) => elem.damage_since(scale, commit), #[cfg(feature = "debug")] CosmicElement::Egui(elem) => elem.damage_since(scale, commit), } @@ -156,6 +164,7 @@ where CosmicElement::MoveGrab(elem) => elem.opaque_regions(scale), CosmicElement::Postprocess(elem) => elem.opaque_regions(scale), CosmicElement::Zoom(elem) => elem.opaque_regions(scale), + CosmicElement::Damage(elem) => elem.opaque_regions(scale), #[cfg(feature = "debug")] CosmicElement::Egui(elem) => elem.opaque_regions(scale), } @@ -169,6 +178,7 @@ where CosmicElement::MoveGrab(elem) => elem.alpha(), CosmicElement::Postprocess(elem) => elem.alpha(), CosmicElement::Zoom(elem) => elem.alpha(), + CosmicElement::Damage(elem) => elem.alpha(), #[cfg(feature = "debug")] CosmicElement::Egui(elem) => elem.alpha(), } @@ -182,6 +192,7 @@ where CosmicElement::MoveGrab(elem) => elem.kind(), CosmicElement::Postprocess(elem) => elem.kind(), CosmicElement::Zoom(elem) => elem.kind(), + CosmicElement::Damage(elem) => elem.kind(), #[cfg(feature = "debug")] CosmicElement::Egui(elem) => elem.kind(), } @@ -195,6 +206,7 @@ where CosmicElement::MoveGrab(elem) => elem.is_framebuffer_effect(), CosmicElement::Postprocess(elem) => elem.is_framebuffer_effect(), CosmicElement::Zoom(elem) => elem.is_framebuffer_effect(), + CosmicElement::Damage(elem) => elem.is_framebuffer_effect(), #[cfg(feature = "debug")] CosmicElement::Egui(elem) => elem.is_framebuffer_effect(), } @@ -241,6 +253,9 @@ where .map_err(R::from_gles_error) } CosmicElement::Zoom(elem) => elem.draw(frame, src, dst, damage, opaque_regions, cache), + CosmicElement::Damage(elem) => { + RenderElement::::draw(elem, frame, src, dst, damage, opaque_regions, cache) + } #[cfg(feature = "debug")] CosmicElement::Egui(elem) => { let glow_frame = R::glow_frame_mut(frame); @@ -269,6 +284,7 @@ where elem.underlying_storage(glow_renderer) } CosmicElement::Zoom(elem) => elem.underlying_storage(renderer), + CosmicElement::Damage(elem) => elem.underlying_storage(renderer), #[cfg(feature = "debug")] CosmicElement::Egui(elem) => { let glow_renderer = renderer.glow_renderer_mut(); @@ -297,6 +313,9 @@ where .map_err(R::from_gles_error) } CosmicElement::Zoom(elem) => elem.capture_framebuffer(frame, src, dst, cache), + CosmicElement::Damage(elem) => { + RenderElement::::capture_framebuffer(elem, frame, src, dst, cache) + } #[cfg(feature = "debug")] CosmicElement::Egui(elem) => { let glow_frame = R::glow_frame_mut(frame); @@ -336,6 +355,17 @@ where } } +impl From for CosmicElement +where + R: Renderer + ImportAll + ImportMem + AsGlowRenderer, + R::TextureId: 'static, + CosmicMappedRenderElement: RenderElement, +{ + fn from(value: DamageElement) -> Self { + Self::Damage(value) + } +} + #[cfg(feature = "debug")] impl From> for CosmicElement where diff --git a/src/wayland/handlers/image_copy_capture/render.rs b/src/wayland/handlers/image_copy_capture/render.rs index 85759165..4b249a36 100644 --- a/src/wayland/handlers/image_copy_capture/render.rs +++ b/src/wayland/handlers/image_copy_capture/render.rs @@ -1,7 +1,6 @@ // SPDX-License-Identifier: GPL-3.0-only use calloop::LoopHandle; -use smallvec::SmallVec; use smithay::{ backend::{ allocator::{Buffer, Fourcc, format::get_transparent}, @@ -506,6 +505,7 @@ smithay::render_elements! { pub WindowCaptureElement where R: ImportAll + ImportMem + AsGlowRenderer; WaylandElement=SurfaceRenderElement, CursorElement=RelocateRenderElement>, + DamageElement=DamageElement, } pub fn render_window_to_buffer( @@ -556,7 +556,7 @@ pub fn render_window_to_buffer( CosmicElement: RenderElement, CosmicMappedRenderElement: RenderElement, { - let additional_damage_elements: Vec<_> = additional_damage + let mut elements: Vec<_> = additional_damage .into_iter() .filter_map(|rect| { let logical_rect = rect.to_logical( @@ -567,8 +567,8 @@ pub fn render_window_to_buffer( logical_rect.intersection(Rectangle::from_size(geometry.size)) }) .map(DamageElement::new) + .map(WindowCaptureElement::::from) .collect(); - dt.damage_output(age, &additional_damage_elements)?; let shell = common.shell.read(); let seat = shell.seats.last_active().clone(); @@ -589,8 +589,6 @@ pub fn render_window_to_buffer( }; std::mem::drop(shell); - let mut elements = Vec::new(); - if let Some(location) = location { if draw_cursor { cursor::draw_cursor( @@ -802,17 +800,16 @@ pub fn render_cursor_to_buffer( CosmicElement: RenderElement, CosmicMappedRenderElement: RenderElement, { - let additional_damage_elements: Vec<_> = additional_damage + let mut elements: Vec<_> = additional_damage .into_iter() .filter_map(|rect| { let logical_rect = rect.to_logical(1, Transform::Normal, &Size::from((64, 64))); logical_rect.intersection(Rectangle::from_size((64, 64).into())) }) .map(DamageElement::new) + .map(WindowCaptureElement::from) .collect(); - dt.damage_output(age, &additional_damage_elements)?; - let mut elements = SmallVec::<[WindowCaptureElement; 4]>::new_const(); cursor::draw_cursor( renderer, seat, From de8b47839c8b6e7cb6b547e96ac15b821e4da9fc Mon Sep 17 00:00:00 2001 From: Victoria Brekenfeld Date: Tue, 10 Mar 2026 17:16:48 +0100 Subject: [PATCH 26/92] render: Make blur_strength configurable --- src/backend/render/cursor.rs | 7 ++- src/backend/render/mod.rs | 35 +++++++---- src/backend/render/wayland/blur_effect.rs | 61 +++++++++++++++++-- src/backend/render/wayland/mod.rs | 8 ++- src/shell/element/stack.rs | 2 + src/shell/element/surface.rs | 5 ++ src/shell/element/window.rs | 2 + src/shell/layout/tiling/mod.rs | 2 + src/shell/workspace.rs | 2 + src/utils/screenshot.rs | 1 + .../handlers/image_copy_capture/render.rs | 21 ++++--- 11 files changed, 119 insertions(+), 27 deletions(-) diff --git a/src/backend/render/cursor.rs b/src/backend/render/cursor.rs index e43d99e5..360991de 100644 --- a/src/backend/render/cursor.rs +++ b/src/backend/render/cursor.rs @@ -185,6 +185,7 @@ pub fn draw_surface_cursor( surface: &wl_surface::WlSurface, location: Point, scale: impl Into>, + blur_strength: usize, push: &mut dyn FnMut(CursorRenderElement, Point), ) where R: Renderer + ImportAll + AsGlowRenderer, @@ -211,6 +212,7 @@ pub fn draw_surface_cursor( 1.0, false, [0; 4], + blur_strength, Kind::Cursor, &mut |elem| push(elem.into(), h), None, @@ -223,6 +225,7 @@ pub fn draw_dnd_icon( surface: &wl_surface::WlSurface, location: Point, scale: impl Into>, + blur_strength: usize, push: &mut dyn FnMut(SurfaceRenderElement), ) where R: Renderer + ImportAll + AsGlowRenderer, @@ -244,6 +247,7 @@ pub fn draw_dnd_icon( 1.0, false, [0; 4], + blur_strength, FRAME_TIME_FILTER, push, None, @@ -330,6 +334,7 @@ pub fn draw_cursor( scale: Scale, buffer_scale: f64, time: Time, + blur_strength: usize, draw_default: bool, push: &mut dyn FnMut(CursorRenderElement, Point), ) where @@ -406,7 +411,7 @@ pub fn draw_cursor( hotspot.to_physical_precise_round(scale), ); } else if let CursorImageStatus::Surface(ref wl_surface) = cursor_status { - draw_surface_cursor(renderer, wl_surface, location, scale, push); + draw_surface_cursor(renderer, wl_surface, location, scale, blur_strength, push); } } diff --git a/src/backend/render/mod.rs b/src/backend/render/mod.rs index a22289f4..7c1c1404 100644 --- a/src/backend/render/mod.rs +++ b/src/backend/render/mod.rs @@ -478,6 +478,7 @@ pub fn cursor_elements<'a, 'frame, R>( seats: impl Iterator>, zoom_state: Option<&ZoomState>, theme: &Theme, + blur_strength: usize, now: Time, output: &Output, mode: CursorMode, @@ -514,6 +515,7 @@ pub fn cursor_elements<'a, 'frame, R>( scale.into(), zoom_scale, now, + blur_strength, mode != CursorMode::NotDefault, &mut |elem, hotspot| { push(CosmicElement::Cursor(RescaleRenderElement::from_element( @@ -538,6 +540,7 @@ pub fn cursor_elements<'a, 'frame, R>( &dnd_icon.surface, (location + dnd_icon.offset.to_f64()).to_i32_round(), scale, + blur_strength, &mut |elem| push(CosmicElement::Dnd(elem)), ); } @@ -723,6 +726,7 @@ where return Ok(Vec::new()); } let theme = shell_ref.theme().clone(); + let blur_strength = 9; // TODO let scale = output.current_scale().fractional_scale(); // we don't want to hold a shell lock across `cursor_elements`, // that is prone to deadlock with the main-thread on some grabs. @@ -733,6 +737,7 @@ where seats.iter(), zoom_level, &theme, + blur_strength, now, output, cursor_mode, @@ -845,6 +850,7 @@ where 1.0, false, [0; 4], + blur_strength, FRAME_TIME_FILTER, &mut |elem| { elements.extend( @@ -875,6 +881,7 @@ where 1.0, false, [0; 4], + blur_strength, FRAME_TIME_FILTER, &mut |elem| { elements.extend( @@ -902,6 +909,7 @@ where 1.0, false, [0; 4], + blur_strength, FRAME_TIME_FILTER, &mut |elem| elements.extend(crop_to_output(elem.into()).map(Into::into)), None, @@ -1048,6 +1056,7 @@ fn session_lock_elements( 1.0, false, [0; 4], + 0, FRAME_TIME_FILTER, push, None, @@ -1507,7 +1516,19 @@ where CosmicMappedRenderElement: RenderElement, WorkspaceRenderElement: RenderElement, { - let elements: Vec> = workspace_elements( + let mut elements: Vec> = if let Some(additional_damage) = additional_damage { + let output_geo = output.geometry().to_local(output).as_logical(); + additional_damage + .into_iter() + .filter_map(|rect| rect.intersection(output_geo)) + .map(DamageElement::new) + .map(CosmicElement::from) + .collect() + } else { + Vec::new() + }; + + elements.extend(workspace_elements( gpu, renderer, shell, @@ -1519,17 +1540,7 @@ where cursor_mode, element_filter, None, - )?; - - if let Some(additional_damage) = additional_damage { - let output_geo = output.geometry().to_local(output).as_logical(); - let additional_damage_elements: Vec<_> = additional_damage - .into_iter() - .filter_map(|rect| rect.intersection(output_geo)) - .map(DamageElement::new) - .collect(); - damage_tracker.damage_output(age, &additional_damage_elements)?; - } + )?); let res = damage_tracker.render_output( renderer, diff --git a/src/backend/render/wayland/blur_effect.rs b/src/backend/render/wayland/blur_effect.rs index 895cd19e..2a0be977 100644 --- a/src/backend/render/wayland/blur_effect.rs +++ b/src/backend/render/wayland/blur_effect.rs @@ -1,7 +1,7 @@ use std::{ borrow::{Borrow, BorrowMut}, sync::{ - Arc, Mutex, + Arc, LazyLock, Mutex, atomic::{AtomicBool, Ordering}, }, }; @@ -27,6 +27,7 @@ use smithay::{ }, wayland::compositor::SurfaceData, }; +use tracing::info; use crate::{ backend::render::{element::AsGlowRenderer, wayland::clipped_surface::ClippingShader}, @@ -36,9 +37,50 @@ use crate::{ pub static BLUR_DOWNSAMPLE_SHADER: &str = include_str!("../shaders/blur_downsample.frag"); pub static BLUR_UPSAMPLE_SHADER: &str = include_str!("../shaders/blur_upsample.frag"); -const PASSES: usize = 3; -const OFFSET: f64 = 1.1; const NOISE: f32 = 0.03; +const MAX_STEPS: usize = 15; + +#[derive(Debug, Clone, Copy, PartialEq)] +struct BlurParameters { + passes: usize, + offset: f64, + extended_radius: i32, +} + +static BLUR_PARAMS: LazyLock> = LazyLock::new(|| { + let mut params = Vec::new(); + + let mut remaining_steps = MAX_STEPS as isize; + let offsets = [ + // min offset, max offset, extended radius to avoid artifacts + (1.0, 2.0, 10), + (2.0, 3.0, 20), + (2.0, 5.0, 50), + (3.0, 8.0, 150), + ]; + + let sum = offsets.iter().map(|(min, max, _)| *max - *min).sum::(); + for (i, (min, max, extended_radius)) in offsets.into_iter().enumerate() { + let mut iter_num = f64::ceil((max - min) / sum * (MAX_STEPS as f64)) as usize; + remaining_steps -= iter_num as isize; + + if remaining_steps < 0 { + iter_num = iter_num.saturating_add_signed(remaining_steps); + } + + let diff = max - min; + for j in 1..=iter_num { + params.push(BlurParameters { + passes: i + 1, + offset: min + (diff / iter_num as f64) * j as f64, + extended_radius, + }); + } + } + + info!("Computed blur values: {:#?}", ¶ms); + params +}); #[derive(Debug, Clone)] pub struct BlurShaders { @@ -125,6 +167,7 @@ impl BlurElement { geometry: Rectangle, output_scale: f64, radii: [u8; 4], + strength: usize, ) -> Result, R::Error> { let mut blur_region_state = states.cached_state.get::(); let Some(region) = blur_region_state.current().blur_region.as_ref() else { @@ -133,7 +176,7 @@ impl BlurElement { let geo = geometry.to_physical_precise_round(output_scale); let mut extended_geo = geo; - let radius = OFFSET * 2.0f64.powf(PASSES as f64); + let radius = BLUR_PARAMS[strength.min(MAX_STEPS - 1)].extended_radius as f64; extended_geo.loc -= Point::::new(radius, radius); extended_geo.size += Size::::new(radius, radius).upscale(2.); @@ -187,6 +230,7 @@ impl BlurElement { output_scale, &mut *state.lock().unwrap(), uniforms, + strength, )?)) } @@ -197,19 +241,24 @@ impl BlurElement { output_scale: f64, state: &mut BlurState, uniforms: Vec>, + strength: usize, ) -> Result { let renderer_id = renderer.glow_renderer().context_id(); let src = geometry.size.to_buffer(output_scale, Transform::Normal); + let params = &BLUR_PARAMS[strength.min(MAX_STEPS - 1)]; let dirty = !(state .renderer_id .as_ref() .is_some_and(|id| id == &renderer_id) + && state.offset == params.offset + && state.passes == params.passes && &state.region == region && state.src == src); + state.renderer_id = Some(renderer_id); - state.offset = OFFSET * output_scale; // we want the blur result to be stable across scaling; - state.passes = PASSES; + state.offset = params.offset; + state.passes = params.passes; state.region = region.clone(); state.src = src; if dirty { diff --git a/src/backend/render/wayland/mod.rs b/src/backend/render/wayland/mod.rs index b9e07a80..9ac1109c 100644 --- a/src/backend/render/wayland/mod.rs +++ b/src/backend/render/wayland/mod.rs @@ -35,6 +35,7 @@ pub fn push_render_elements_from_surface_tree( alpha: f32, should_clip: bool, radii: [u8; 4], + blur_strength: usize, kind: impl Into, push_above: &mut dyn FnMut(SurfaceRenderElement), mut push_below: Option<&mut dyn FnMut(SurfaceRenderElement)>, @@ -87,7 +88,12 @@ pub fn push_render_elements_from_surface_tree( ) { Ok(Some(surface)) => { blur = BlurElement::from_surface( - renderer, states, geometry, scale.x, radii, + renderer, + states, + geometry, + scale.x, + radii, + blur_strength, ); let elem: SurfaceRenderElement = if radii.iter().any(|r| *r != 0) && should_clip diff --git a/src/shell/element/stack.rs b/src/shell/element/stack.rs index 58e28753..0980facb 100644 --- a/src/shell/element/stack.rs +++ b/src/shell/element/stack.rs @@ -668,6 +668,7 @@ impl CosmicStack { scale, alpha, scanout_node, + 9, // TODO &mut |elem| push(elem.into()), ) }) @@ -825,6 +826,7 @@ impl CosmicStack { scanout_node, radii.is_some(), radii.unwrap_or([0; 4]), + 9, // as TODO &mut |elem| push_above(elem.into()), Some(&mut |elem| push_below(elem.into())), ); diff --git a/src/shell/element/surface.rs b/src/shell/element/surface.rs index 53c3d4bd..4a88d2df 100644 --- a/src/shell/element/surface.rs +++ b/src/shell/element/surface.rs @@ -866,6 +866,7 @@ impl CosmicSurface { scale: Scale, alpha: f32, scanout_node: Option, + blur_strength: usize, push: &mut dyn FnMut(SurfaceRenderElement), ) where R: Renderer + ImportAll + AsGlowRenderer, @@ -889,6 +890,7 @@ impl CosmicSurface { alpha, false, [0; 4], + blur_strength, scanout_kind_eval(None, scanout_node), push, None, @@ -909,6 +911,7 @@ impl CosmicSurface { scanout_node: Option, should_clip: bool, radii: [u8; 4], + blur_strength: usize, push_above: &mut dyn FnMut(SurfaceRenderElement), push_below: Option<&mut dyn FnMut(SurfaceRenderElement)>, ) where @@ -931,6 +934,7 @@ impl CosmicSurface { alpha, should_clip, radii, + blur_strength, scanout_kind_eval(scanout_override, scanout_node), push_above, push_below, @@ -950,6 +954,7 @@ impl CosmicSurface { alpha, should_clip, radii, + blur_strength, scanout_kind_eval(scanout_override, scanout_node), push_above, push_below, diff --git a/src/shell/element/window.rs b/src/shell/element/window.rs index 48949127..de949b8e 100644 --- a/src/shell/element/window.rs +++ b/src/shell/element/window.rs @@ -375,6 +375,7 @@ impl CosmicWindow { scale, alpha, scanout_node, + 9, // TODO &mut |elem| push(elem.into()), ) }) @@ -556,6 +557,7 @@ impl CosmicWindow { scanout_node, clip, radii, + 9, // TODO &mut |elem| push_above(elem.into()), Some(&mut |elem| push_below(elem.into())), ) diff --git a/src/shell/layout/tiling/mod.rs b/src/shell/layout/tiling/mod.rs index fbcec105..3a3633db 100644 --- a/src/shell/layout/tiling/mod.rs +++ b/src/shell/layout/tiling/mod.rs @@ -5408,6 +5408,8 @@ fn render_new_tree_windows( scanout_node, false, [0, 0, 0, 0], + // TODO + 0, &mut |elem| { swap_elements.push(CosmicMappedRenderElement::GrabbedWindow( RescaleRenderElement::from_element( diff --git a/src/shell/workspace.rs b/src/shell/workspace.rs index b8b104c5..dc5d9f63 100644 --- a/src/shell/workspace.rs +++ b/src/shell/workspace.rs @@ -1717,6 +1717,7 @@ impl Workspace { scanout_node, false, [0, 0, 0, 0], + 0, &mut fullscreen_push, None, ); @@ -1905,6 +1906,7 @@ impl Workspace { output_scale.into(), alpha, scanout_node, + 0, &mut |elem| push(WorkspaceRenderElement::FullscreenPopup(elem.into())), ); } diff --git a/src/utils/screenshot.rs b/src/utils/screenshot.rs index eb7730c7..de544b72 100644 --- a/src/utils/screenshot.rs +++ b/src/utils/screenshot.rs @@ -37,6 +37,7 @@ pub fn screenshot_window(state: &mut State, surface: &CosmicSurface) { None, false, [0; 4], + 0, &mut |elem| elements.push(elem), None, ); diff --git a/src/wayland/handlers/image_copy_capture/render.rs b/src/wayland/handlers/image_copy_capture/render.rs index 4b249a36..9018f8ce 100644 --- a/src/wayland/handlers/image_copy_capture/render.rs +++ b/src/wayland/handlers/image_copy_capture/render.rs @@ -571,6 +571,7 @@ pub fn render_window_to_buffer( .collect(); let shell = common.shell.read(); + let blur_strength = 9; // TODO let seat = shell.seats.last_active().clone(); let pointer = seat.get_pointer().unwrap(); let pointer_loc = pointer.current_location().to_i32_round().as_global(); @@ -598,6 +599,7 @@ pub fn render_window_to_buffer( 1.0.into(), 1.0, common.clock.now(), + blur_strength, true, &mut |elem, hotspot| { elements.push(WindowCaptureElement::CursorElement( @@ -619,15 +621,18 @@ pub fn render_window_to_buffer( &dnd_icon.surface, (location + dnd_icon.offset.to_f64()).to_i32_round(), 1.0, + blur_strength, &mut |elem| { - elements.push( - RelocateRenderElement::from_element( - CursorRenderElement::Surface(elem), - Point::new(0, 0), - Relocate::Relative, + elements + .push( + RelocateRenderElement::from_element( + CursorRenderElement::Surface(elem), + Point::new(0, 0), + Relocate::Relative, + ) + .into(), ) - .into(), - ) + .into() }, ); } @@ -642,6 +647,7 @@ pub fn render_window_to_buffer( None, false, [0; 4], + blur_strength, &mut |elem| elements.push(elem.into()), None, ); @@ -817,6 +823,7 @@ pub fn render_cursor_to_buffer( 1.0.into(), 1.0, common.clock.now(), + 0, true, &mut |elem, _| { elements.push( From cfed174e9bc86533dc26c167af030ccbd9e9398e Mon Sep 17 00:00:00 2001 From: Victoria Brekenfeld Date: Mon, 30 Mar 2026 14:17:22 +0200 Subject: [PATCH 27/92] chore: Fix clippy lints --- src/backend/render/wayland/blur_effect.rs | 6 +++--- .../handlers/image_copy_capture/render.rs | 16 +++++++--------- 2 files changed, 10 insertions(+), 12 deletions(-) diff --git a/src/backend/render/wayland/blur_effect.rs b/src/backend/render/wayland/blur_effect.rs index 2a0be977..a2937b7b 100644 --- a/src/backend/render/wayland/blur_effect.rs +++ b/src/backend/render/wayland/blur_effect.rs @@ -226,9 +226,9 @@ impl BlurElement { Ok(Some(Self::from_state( renderer, extended_geo.to_logical(output_scale), - ®ion, + region, output_scale, - &mut *state.lock().unwrap(), + &mut state.lock().unwrap(), uniforms, strength, )?)) @@ -502,7 +502,7 @@ fn render_blur( let adjusted_tex_size = tex_size.downscale(1 << i); let target_tex_size = tex_size - .downscale(1 << i + 1) + .downscale(1 << (i + 1)) .to_logical(1, Transform::Normal) .to_physical(1); let half_pixel = [ diff --git a/src/wayland/handlers/image_copy_capture/render.rs b/src/wayland/handlers/image_copy_capture/render.rs index 9018f8ce..0b77cbf5 100644 --- a/src/wayland/handlers/image_copy_capture/render.rs +++ b/src/wayland/handlers/image_copy_capture/render.rs @@ -623,16 +623,14 @@ pub fn render_window_to_buffer( 1.0, blur_strength, &mut |elem| { - elements - .push( - RelocateRenderElement::from_element( - CursorRenderElement::Surface(elem), - Point::new(0, 0), - Relocate::Relative, - ) - .into(), + elements.push( + RelocateRenderElement::from_element( + CursorRenderElement::Surface(elem), + Point::new(0, 0), + Relocate::Relative, ) - .into() + .into(), + ) }, ); } From d4cd9b674c0e3eb266ebf9dc09d9f6c1a9f5e214 Mon Sep 17 00:00:00 2001 From: Victoria Brekenfeld Date: Fri, 10 Apr 2026 17:48:45 +0200 Subject: [PATCH 28/92] chore: Adjust log level for blur debugging --- src/backend/render/wayland/blur_effect.rs | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/backend/render/wayland/blur_effect.rs b/src/backend/render/wayland/blur_effect.rs index a2937b7b..977f82f3 100644 --- a/src/backend/render/wayland/blur_effect.rs +++ b/src/backend/render/wayland/blur_effect.rs @@ -27,7 +27,7 @@ use smithay::{ }, wayland::compositor::SurfaceData, }; -use tracing::info; +use tracing::trace; use crate::{ backend::render::{element::AsGlowRenderer, wayland::clipped_surface::ClippingShader}, @@ -78,7 +78,7 @@ static BLUR_PARAMS: LazyLock> = LazyLock::new(|| { } } - info!("Computed blur values: {:#?}", ¶ms); + trace!("Computed blur values: {:#?}", ¶ms); params }); From 6868c245014c53f19c709aaac750919bb656bee2 Mon Sep 17 00:00:00 2001 From: Victoria Brekenfeld Date: Fri, 10 Apr 2026 17:50:18 +0200 Subject: [PATCH 29/92] renderer/blur: Use proper commit counter --- src/backend/render/wayland/blur_effect.rs | 24 +++++++++++------------ 1 file changed, 12 insertions(+), 12 deletions(-) diff --git a/src/backend/render/wayland/blur_effect.rs b/src/backend/render/wayland/blur_effect.rs index 977f82f3..ca236f76 100644 --- a/src/backend/render/wayland/blur_effect.rs +++ b/src/backend/render/wayland/blur_effect.rs @@ -1,9 +1,6 @@ use std::{ borrow::{Borrow, BorrowMut}, - sync::{ - Arc, LazyLock, Mutex, - atomic::{AtomicBool, Ordering}, - }, + sync::{LazyLock, Mutex}, }; use cgmath::{Matrix3, SquareMatrix, Vector2}; @@ -127,7 +124,7 @@ struct BlurState { offset: f64, passes: usize, region: Vec>, - dirty: Arc, + commit: CommitCounter, } unsafe impl Send for BlurState {} @@ -142,18 +139,18 @@ impl BlurState { offset: 0., passes: 0, region: Vec::new(), - dirty: Arc::new(AtomicBool::new(true)), + commit: CommitCounter::default(), } } } pub struct BlurElement { id: Id, + commit: CommitCounter, src: Size, geometry: Rectangle, scaling_shaders: BlurShaders, render_shader: GlesTexProgram, - dirty: Arc, region: Vec>, offset: f64, passes: usize, @@ -262,16 +259,16 @@ impl BlurElement { state.region = region.clone(); state.src = src; if dirty { - state.dirty.store(dirty, Ordering::Release); + state.commit.increment(); } Ok(BlurElement { id: state.id.clone(), + commit: state.commit, src, geometry, scaling_shaders: BlurShaders::get(renderer), render_shader: ClippingShader::get(renderer), - dirty: state.dirty.clone(), offset: state.offset, passes: state.passes, region: region @@ -289,7 +286,7 @@ impl Element for BlurElement { } fn current_commit(&self) -> CommitCounter { - Default::default() + self.commit } fn src(&self) -> Rectangle { @@ -309,7 +306,7 @@ impl Element for BlurElement { scale: Scale, commit: Option, ) -> DamageSet { - if commit.is_none() || self.dirty.load(Ordering::Acquire) { + if self.commit.distance(commit).is_none_or(|d| d > 0) { DamageSet::from_slice(&[self.geometry.to_physical_precise_round(scale)]) } else { DamageSet::default() @@ -344,7 +341,10 @@ impl RenderElement for BlurElement { let texture_ref = cache.get_or_insert_threadsafe(BlurTexture::default); let mut texture_entry = texture_ref.lock().unwrap(); - if self.dirty.swap(false, Ordering::Acquire) { + if texture_entry + .as_ref() + .is_some_and(|tex| tex.size() != tex_size) + { texture_entry.take(); } From 667414bd922d50a82d8da70c6655459d275b4ef1 Mon Sep 17 00:00:00 2001 From: Victoria Brekenfeld Date: Fri, 10 Apr 2026 17:50:58 +0200 Subject: [PATCH 30/92] renderer/blur: Fix incorrectly offset blur region --- src/backend/render/wayland/blur_effect.rs | 35 ++++++++++++++++++++--- 1 file changed, 31 insertions(+), 4 deletions(-) diff --git a/src/backend/render/wayland/blur_effect.rs b/src/backend/render/wayland/blur_effect.rs index ca236f76..e7c52395 100644 --- a/src/backend/render/wayland/blur_effect.rs +++ b/src/backend/render/wayland/blur_effect.rs @@ -148,10 +148,11 @@ pub struct BlurElement { id: Id, commit: CommitCounter, src: Size, + extended_offset: Point, geometry: Rectangle, scaling_shaders: BlurShaders, render_shader: GlesTexProgram, - region: Vec>, + region: Vec>, offset: f64, passes: usize, uniforms: Vec>, @@ -223,6 +224,7 @@ impl BlurElement { Ok(Some(Self::from_state( renderer, extended_geo.to_logical(output_scale), + Point::::new(radius, radius).to_logical(output_scale), region, output_scale, &mut state.lock().unwrap(), @@ -234,6 +236,7 @@ impl BlurElement { fn from_state( renderer: &mut R, geometry: Rectangle, + extended_offset: Point, region: &Vec>, output_scale: f64, state: &mut BlurState, @@ -267,13 +270,18 @@ impl BlurElement { commit: state.commit, src, geometry, + extended_offset, scaling_shaders: BlurShaders::get(renderer), render_shader: ClippingShader::get(renderer), offset: state.offset, passes: state.passes, region: region .iter() - .map(|rect| rect.to_physical_precise_round(output_scale)) + .cloned() + .map(|mut rect| { + rect.loc += extended_offset.to_i32_round(); + rect + }) .collect(), uniforms, }) @@ -307,7 +315,15 @@ impl Element for BlurElement { commit: Option, ) -> DamageSet { if self.commit.distance(commit).is_none_or(|d| d > 0) { - DamageSet::from_slice(&[self.geometry.to_physical_precise_round(scale)]) + DamageSet::from_slice(&[Rectangle::new( + self.extended_offset.to_physical_precise_round(scale), + self.geometry.size.to_physical_precise_round(scale) + - self + .extended_offset + .to_size() + .upscale(2.) + .to_physical_precise_round(scale), + )]) } else { DamageSet::default() } @@ -391,10 +407,21 @@ impl RenderElement for BlurElement { cache: Option<&UserDataMap>, ) -> Result<(), R::Error> { let glow_frame = ::glow_frame_mut(frame); + let src_to_geo = self.geometry.size / self.src; + let src_log = src + .upscale(src_to_geo) + .to_logical(1., Transform::Normal, &Size::default()); + let scale = dst.size.to_f64() / src_log.size; + let damage = self .region .iter() - .flat_map(|rect| damage.iter().flat_map(|r| r.intersection(*rect))) + .flat_map(|rect| { + let mut rect = rect.to_f64().intersection(src_log)?; + rect.loc -= src_log.loc; + Some(rect.to_physical_precise_round(scale)) + }) + .flat_map(|rect| damage.iter().flat_map(move |r| r.intersection(rect))) .collect::>(); let cache = cache.expect("Framebuffer element without cache?"); let Some(texture) = cache.get::() else { From 55d57ddba2188f9b14b9ebd13cb2a5621e418fad Mon Sep 17 00:00:00 2001 From: Victoria Brekenfeld Date: Fri, 17 Apr 2026 11:48:11 +0200 Subject: [PATCH 31/92] chore: theme-v2 libcosmic update --- Cargo.lock | 187 ++++++++++++---------- Cargo.toml | 5 +- examples/customized-window-decorations.rs | 24 +-- rust-toolchain.toml | 2 +- src/backend/render/mod.rs | 2 +- src/backend/render/wayland/blur_effect.rs | 124 ++++++++------ src/shell/element/resize_indicator.rs | 186 +++++++++------------ src/shell/element/stack.rs | 137 +++++++++++++--- src/shell/element/stack/tab.rs | 55 +++++-- src/shell/element/stack/tabs.rs | 44 ++--- src/shell/element/stack_hover.rs | 34 ++-- src/shell/element/swap_indicator.rs | 38 +++-- src/shell/element/window.rs | 81 ++++++++-- src/shell/grabs/menu/item.rs | 14 +- src/shell/grabs/menu/mod.rs | 16 +- src/shell/workspace.rs | 12 +- src/shell/zoom.rs | 6 +- src/utils/iced/mod.rs | 4 +- src/utils/iced/state.rs | 24 +-- 19 files changed, 609 insertions(+), 386 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index 9030c5e0..36146511 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -330,9 +330,9 @@ dependencies = [ [[package]] name = "auto_enums" -version = "0.8.7" +version = "0.8.8" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9c170965892137a3a9aeb000b4524aa3cc022a310e709d848b6e1cdce4ab4781" +checksum = "65398a2893f41bce5c9259f6e1a4f03fbae40637c1bdc755b4f387f48c613b03" dependencies = [ "derive_utils", "proc-macro2", @@ -785,19 +785,6 @@ dependencies = [ "libm", ] -[[package]] -name = "cosmic-client-toolkit" -version = "0.2.0" -source = "git+https://github.com/pop-os//cosmic-protocols?branch=main#0d0b0b573fe4836ede4c237dadd365033cd28296" -dependencies = [ - "bitflags 2.11.0", - "cosmic-protocols", - "libc", - "smithay-client-toolkit", - "wayland-client", - "wayland-protocols", -] - [[package]] name = "cosmic-comp" version = "1.0.0" @@ -875,7 +862,7 @@ dependencies = [ [[package]] name = "cosmic-config" version = "1.0.0" -source = "git+https://github.com/pop-os/libcosmic#0bb006c5bbf7eb89491891d45bfc8f21f8eb1305" +source = "git+https://github.com/pop-os/libcosmic#d922ac380134c00208d2acf93947d44fb217eec5" dependencies = [ "atomicwrites", "calloop", @@ -896,7 +883,7 @@ dependencies = [ [[package]] name = "cosmic-config-derive" version = "1.0.0" -source = "git+https://github.com/pop-os/libcosmic#0bb006c5bbf7eb89491891d45bfc8f21f8eb1305" +source = "git+https://github.com/pop-os/libcosmic#d922ac380134c00208d2acf93947d44fb217eec5" dependencies = [ "quote", "syn", @@ -905,7 +892,7 @@ dependencies = [ [[package]] name = "cosmic-freedesktop-icons" version = "0.4.0" -source = "git+https://github.com/pop-os/freedesktop-icons#7a61a704f6d1ec41f71cbe766e3cc484858523fa" +source = "git+https://github.com/pop-os/freedesktop-icons#ab4c57b8e416c6af9297cb04d101889896fd9a92" dependencies = [ "bstr", "btoi", @@ -923,7 +910,6 @@ source = "git+https://github.com/pop-os//cosmic-protocols?branch=main#0d0b0b573f dependencies = [ "bitflags 2.11.0", "wayland-backend", - "wayland-client", "wayland-protocols", "wayland-protocols-wlr", "wayland-scanner", @@ -956,7 +942,7 @@ dependencies = [ [[package]] name = "cosmic-settings-daemon" version = "0.1.0" -source = "git+https://github.com/pop-os/dbus-settings-bindings#507e342c21d3ce6ae41b1d4f3fa2f0ad5ee23e75" +source = "git+https://github.com/pop-os/dbus-settings-bindings#eed01dd3609e90e3c8cd043656734c500956c793" dependencies = [ "zbus", ] @@ -973,8 +959,9 @@ dependencies = [ [[package]] name = "cosmic-text" -version = "0.18.2" -source = "git+https://github.com/pop-os/cosmic-text.git#d5a972a2b63649fad11ea3a7e80f7dc4c592f01a" +version = "0.19.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "be17b688510d934ce13f48a2beba700e11583e281e0fda99c22bb256a14eda73" dependencies = [ "bitflags 2.11.0", "fontdb", @@ -997,13 +984,14 @@ dependencies = [ [[package]] name = "cosmic-theme" version = "1.0.0" -source = "git+https://github.com/pop-os/libcosmic#0bb006c5bbf7eb89491891d45bfc8f21f8eb1305" +source = "git+https://github.com/pop-os/libcosmic#d922ac380134c00208d2acf93947d44fb217eec5" dependencies = [ "almost", "configparser", "cosmic-config", "csscolorparser", "dirs", + "hex_color", "palette", "ron 0.12.0", "serde", @@ -1053,7 +1041,7 @@ checksum = "460fbee9c2c2f33933d720630a6a0bac33ba7053db5344fac858d4b8952d77d5" [[package]] name = "cryoglyph" version = "0.1.0" -source = "git+https://github.com/pop-os/glyphon.git?tag=cosmic-0.14#c49de15bce4d8254ac136d1be9911960cc85ce12" +source = "git+https://github.com/iced-rs/cryoglyph.git?rev=e429a025df36ab8145708acb309080ae3deec17a#e429a025df36ab8145708acb309080ae3deec17a" dependencies = [ "cosmic-text", "etagere", @@ -1080,9 +1068,9 @@ checksum = "42aaeae719fd78ce501d77c6cdf01f7e96f26bcd5617a4903a1c2b97e388543a" [[package]] name = "csscolorparser" -version = "0.8.2" +version = "0.8.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "64a878cfd6075649977f548ed439564cfdbe1f190a6656a00b48af7740f2f148" +checksum = "199f851bd3cb5004c09474252c7f74e7c047441ed0979bf3688a7106a13da952" dependencies = [ "num-traits", "phf 0.13.1", @@ -2119,6 +2107,17 @@ version = "0.4.3" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "7f24254aa9a54b5c858eaee2f5bccdb46aaf0e486a595ed5fd8f86ba55232a70" +[[package]] +name = "hex_color" +version = "3.0.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d37f101bf4c633f7ca2e4b5e136050314503dd198e78e325ea602c327c484ef0" +dependencies = [ + "arrayvec", + "rand 0.8.5", + "serde", +] + [[package]] name = "hexf-parse" version = "0.2.1" @@ -2237,7 +2236,7 @@ dependencies = [ [[package]] name = "iced" version = "0.14.0" -source = "git+https://github.com/pop-os/libcosmic#0bb006c5bbf7eb89491891d45bfc8f21f8eb1305" +source = "git+https://github.com/pop-os/libcosmic#d922ac380134c00208d2acf93947d44fb217eec5" dependencies = [ "dnd", "iced_core", @@ -2256,7 +2255,7 @@ dependencies = [ [[package]] name = "iced_core" version = "0.14.0" -source = "git+https://github.com/pop-os/libcosmic#0bb006c5bbf7eb89491891d45bfc8f21f8eb1305" +source = "git+https://github.com/pop-os/libcosmic#d922ac380134c00208d2acf93947d44fb217eec5" dependencies = [ "bitflags 2.11.0", "bytes", @@ -2272,6 +2271,7 @@ dependencies = [ "serde", "smol_str", "thiserror 2.0.18", + "unicode-segmentation", "web-time", "window_clipboard", ] @@ -2279,7 +2279,7 @@ dependencies = [ [[package]] name = "iced_debug" version = "0.14.0" -source = "git+https://github.com/pop-os/libcosmic#0bb006c5bbf7eb89491891d45bfc8f21f8eb1305" +source = "git+https://github.com/pop-os/libcosmic#d922ac380134c00208d2acf93947d44fb217eec5" dependencies = [ "iced_core", "iced_futures", @@ -2289,7 +2289,7 @@ dependencies = [ [[package]] name = "iced_futures" version = "0.14.0" -source = "git+https://github.com/pop-os/libcosmic#0bb006c5bbf7eb89491891d45bfc8f21f8eb1305" +source = "git+https://github.com/pop-os/libcosmic#d922ac380134c00208d2acf93947d44fb217eec5" dependencies = [ "futures", "iced_core", @@ -2302,7 +2302,7 @@ dependencies = [ [[package]] name = "iced_graphics" version = "0.14.0" -source = "git+https://github.com/pop-os/libcosmic#0bb006c5bbf7eb89491891d45bfc8f21f8eb1305" +source = "git+https://github.com/pop-os/libcosmic#d922ac380134c00208d2acf93947d44fb217eec5" dependencies = [ "bitflags 2.11.0", "bytemuck", @@ -2323,7 +2323,7 @@ dependencies = [ [[package]] name = "iced_program" version = "0.14.0" -source = "git+https://github.com/pop-os/libcosmic#0bb006c5bbf7eb89491891d45bfc8f21f8eb1305" +source = "git+https://github.com/pop-os/libcosmic#d922ac380134c00208d2acf93947d44fb217eec5" dependencies = [ "iced_graphics", "iced_runtime", @@ -2332,7 +2332,7 @@ dependencies = [ [[package]] name = "iced_renderer" version = "0.14.0" -source = "git+https://github.com/pop-os/libcosmic#0bb006c5bbf7eb89491891d45bfc8f21f8eb1305" +source = "git+https://github.com/pop-os/libcosmic#d922ac380134c00208d2acf93947d44fb217eec5" dependencies = [ "iced_graphics", "iced_tiny_skia", @@ -2344,7 +2344,7 @@ dependencies = [ [[package]] name = "iced_runtime" version = "0.14.0" -source = "git+https://github.com/pop-os/libcosmic#0bb006c5bbf7eb89491891d45bfc8f21f8eb1305" +source = "git+https://github.com/pop-os/libcosmic#d922ac380134c00208d2acf93947d44fb217eec5" dependencies = [ "bytes", "dnd", @@ -2358,7 +2358,7 @@ dependencies = [ [[package]] name = "iced_tiny_skia" version = "0.14.0" -source = "git+https://github.com/pop-os/libcosmic#0bb006c5bbf7eb89491891d45bfc8f21f8eb1305" +source = "git+https://github.com/pop-os/libcosmic#d922ac380134c00208d2acf93947d44fb217eec5" dependencies = [ "bytemuck", "cosmic-text", @@ -2375,12 +2375,11 @@ dependencies = [ [[package]] name = "iced_wgpu" version = "0.14.0" -source = "git+https://github.com/pop-os/libcosmic#0bb006c5bbf7eb89491891d45bfc8f21f8eb1305" +source = "git+https://github.com/pop-os/libcosmic#d922ac380134c00208d2acf93947d44fb217eec5" dependencies = [ "as-raw-xcb-connection", "bitflags 2.11.0", "bytemuck", - "cosmic-client-toolkit", "cryoglyph", "futures", "glam", @@ -2406,7 +2405,7 @@ dependencies = [ [[package]] name = "iced_widget" version = "0.14.2" -source = "git+https://github.com/pop-os/libcosmic#0bb006c5bbf7eb89491891d45bfc8f21f8eb1305" +source = "git+https://github.com/pop-os/libcosmic#d922ac380134c00208d2acf93947d44fb217eec5" dependencies = [ "dnd", "iced_renderer", @@ -2544,9 +2543,9 @@ dependencies = [ [[package]] name = "image" -version = "0.25.9" +version = "0.25.10" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e6506c6c10786659413faa717ceebcb8f70731c0a60cbae39795fdf114519c1a" +checksum = "85ab80394333c02fe689eaf900ab500fbd0c2213da414687ebf995a65d5a6104" dependencies = [ "bytemuck", "byteorder-lite", @@ -2808,9 +2807,9 @@ checksum = "e2db585e1d738fc771bf08a151420d3ed193d9d895a36df7f6f8a9456b911ddc" [[package]] name = "known-folders" -version = "1.4.1" +version = "1.4.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "770919970f7d2f74fea948900d35e2ef64f44129e8ae4015f59de1f0aca7c2a5" +checksum = "7a1886916523694cd6ea3d175f03a1e5010699a2a4cc13696d83d7bea1d80638" dependencies = [ "windows-sys 0.59.0", ] @@ -2886,7 +2885,7 @@ checksum = "6800badb6cb2082ffd7b6a67e6125bb39f18782f793520caee8cb8846be06112" [[package]] name = "libcosmic" version = "1.0.0" -source = "git+https://github.com/pop-os/libcosmic#0bb006c5bbf7eb89491891d45bfc8f21f8eb1305" +source = "git+https://github.com/pop-os/libcosmic#d922ac380134c00208d2acf93947d44fb217eec5" dependencies = [ "apply", "auto_enums", @@ -2896,6 +2895,7 @@ dependencies = [ "cosmic-theme", "css-color", "derive_setters", + "enumflags2", "float-cmp 0.10.0", "futures", "i18n-embed", @@ -2912,7 +2912,6 @@ dependencies = [ "log", "palette", "phf 0.13.1", - "raw-window-handle", "rust-embed", "serde", "slotmap", @@ -3292,9 +3291,9 @@ dependencies = [ [[package]] name = "moxcms" -version = "0.7.11" +version = "0.8.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ac9557c559cd6fc9867e122e20d2cbefc9ca29d80d027a8e39310920ed2f0a97" +checksum = "bb85c154ba489f01b25c0d36ae69a87e4a1c73a72631fc6c0eb6dde34a73e44b" dependencies = [ "num-traits", "pxfm", @@ -3308,9 +3307,9 @@ checksum = "13d2233c9842d08cfe13f9eac96e207ca6a2ea10b80259ebe8ad0268be27d2af" [[package]] name = "naga" -version = "27.0.3" +version = "28.0.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "066cf25f0e8b11ee0df221219010f213ad429855f57c494f995590c861a9a7d8" +checksum = "618f667225063219ddfc61251087db8a9aec3c3f0950c916b614e403486f1135" dependencies = [ "arrayvec", "bit-set", @@ -4159,6 +4158,8 @@ version = "0.8.5" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "34af8d1a0e25924bc5b7c43c079c942339d8f0a8b57c39049bef581b46327404" dependencies = [ + "libc", + "rand_chacha 0.3.1", "rand_core 0.6.4", ] @@ -4168,7 +4169,7 @@ version = "0.9.2" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "6db2770f06117d490610c7488547d543617b21bfa07796d7a12f6f1bd53850d1" dependencies = [ - "rand_chacha", + "rand_chacha 0.9.0", "rand_core 0.9.5", ] @@ -4183,6 +4184,16 @@ dependencies = [ "rand_core 0.10.0", ] +[[package]] +name = "rand_chacha" +version = "0.3.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e6c10a63a0fa32252be49d21e7709d4d4baf8d231c2dbce1eaa8141b9b127d88" +dependencies = [ + "ppv-lite86", + "rand_core 0.6.4", +] + [[package]] name = "rand_chacha" version = "0.9.0" @@ -4198,6 +4209,9 @@ name = "rand_core" version = "0.6.4" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "ec0be4795e2f6a28069bec0b5ff3e2ac9bafc99e6a9a7dc3547996c5c816922c" +dependencies = [ + "getrandom 0.2.17", +] [[package]] name = "rand_core" @@ -4856,14 +4870,12 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "0512da38f5e2b31201a93524adb8d3136276fa4fe4aafab4e1f727a82b534cc0" dependencies = [ "bitflags 2.11.0", - "bytemuck", "calloop", "calloop-wayland-source", "cursor-icon", "libc", "log", "memmap2", - "pkg-config", "rustix 1.1.4", "thiserror 2.0.18", "wayland-backend", @@ -4875,7 +4887,6 @@ dependencies = [ "wayland-protocols-misc", "wayland-protocols-wlr", "wayland-scanner", - "xkbcommon 0.8.0", "xkeysym", ] @@ -4926,7 +4937,7 @@ checksum = "27207bb65232eda1f588cf46db2fee75c0808d557f6b3cf19a75f5d6d7c94df1" [[package]] name = "softbuffer" version = "0.4.1" -source = "git+https://github.com/pop-os/softbuffer?tag=cosmic-4.0#a3f77e251e7422803f693df6e3fc313c010c4dcb" +source = "git+https://github.com/pop-os/softbuffer?tag=cosmic-4.0#c2b2c19ddb38ff17495643699f97cb1f2064a1be" dependencies = [ "as-raw-xcb-connection", "bytemuck", @@ -5528,9 +5539,9 @@ checksum = "383ad40bb927465ec0ce7720e033cb4ca06912855fc35db31b5755d0de75b1ee" [[package]] name = "unicode-segmentation" -version = "1.12.0" +version = "1.13.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f6ccf251212114b54433ec949fd6a7841275f9ada20dddd2f29e9ceea4501493" +checksum = "c6f5d3c3b1bf09027a88a6bc961fc00497d651009560b5463668dc81b0fa87a8" [[package]] name = "unicode-vo" @@ -5998,12 +6009,13 @@ checksum = "a28ac98ddc8b9274cb41bb4d9d4d5c425b6020c50c46f25559911905610b4a88" [[package]] name = "wgpu" -version = "27.0.1" +version = "28.0.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "bfe68bac7cde125de7a731c3400723cadaaf1703795ad3f4805f187459cd7a77" +checksum = "f9cb534d5ffd109c7d1135f34cdae29e60eab94855a625dcfe1705f8bc7ad79f" dependencies = [ "arrayvec", "bitflags 2.11.0", + "bytemuck", "cfg-if", "cfg_aliases", "document-features", @@ -6021,9 +6033,9 @@ dependencies = [ [[package]] name = "wgpu-core" -version = "27.0.3" +version = "28.0.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "27a75de515543b1897b26119f93731b385a19aea165a1ec5f0e3acecc229cae7" +checksum = "d23f4642f53f666adcfd2d3218ab174d1e6681101aef18696b90cbe64d1c10f9" dependencies = [ "arrayvec", "bit-set", @@ -6051,18 +6063,18 @@ dependencies = [ [[package]] name = "wgpu-core-deps-windows-linux-android" -version = "27.0.0" +version = "28.0.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "71197027d61a71748e4120f05a9242b2ad142e3c01f8c1b47707945a879a03c3" +checksum = "68ca976e72b2c9964eb243e281f6ce7f14a514e409920920dcda12ae40febaae" dependencies = [ "wgpu-hal", ] [[package]] name = "wgpu-hal" -version = "27.0.4" +version = "28.0.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5b21cb61c57ee198bc4aff71aeadff4cbb80b927beb912506af9c780d64313ce" +checksum = "44d6cb474beb218824dcc9e1ce679d973f719262789bfb27407da560cac20eeb" dependencies = [ "bitflags 2.11.0", "cfg-if", @@ -6080,15 +6092,14 @@ dependencies = [ [[package]] name = "wgpu-types" -version = "27.0.1" +version = "28.0.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "afdcf84c395990db737f2dd91628706cb31e86d72e53482320d368e52b5da5eb" +checksum = "e18308757e594ed2cd27dddbb16a139c42a683819d32a2e0b1b0167552f5840c" dependencies = [ "bitflags 2.11.0", "bytemuck", "js-sys", "log", - "thiserror 2.0.18", "web-sys", ] @@ -6736,6 +6747,15 @@ dependencies = [ "memchr", ] +[[package]] +name = "winnow" +version = "1.0.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0592e1c9d151f854e6fd382574c3a0855250e1d9b2f99d9281c6e6391af352f1" +dependencies = [ + "memchr", +] + [[package]] name = "wit-bindgen" version = "0.51.0" @@ -6925,9 +6945,6 @@ name = "xkeysym" version = "0.2.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "b9cc00251562a284751c9973bace760d86c0276c471b4be569fe6b068ee97a56" -dependencies = [ - "bytemuck", -] [[package]] name = "xml-rs" @@ -6978,9 +6995,9 @@ dependencies = [ [[package]] name = "zbus" -version = "5.14.0" +version = "5.16.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ca82f95dbd3943a40a53cfded6c2d0a2ca26192011846a1810c4256ef92c60bc" +checksum = "eee682d202a77e4a9f3b2c2bdf48a7b28af5c08c34ddf66f98c93e5e39464285" dependencies = [ "async-broadcast", "async-executor", @@ -7005,7 +7022,7 @@ dependencies = [ "uds_windows", "uuid", "windows-sys 0.61.2", - "winnow 0.7.14", + "winnow 1.0.3", "zbus_macros", "zbus_names", "zvariant", @@ -7013,9 +7030,9 @@ dependencies = [ [[package]] name = "zbus_macros" -version = "5.14.0" +version = "5.16.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "897e79616e84aac4b2c46e9132a4f63b93105d54fe8c0e8f6bffc21fa8d49222" +checksum = "adf1bd45a81a103745b1757754762a26e8cd01e4532e4d6c8ec431624b80d1d6" dependencies = [ "proc-macro-crate", "proc-macro2", @@ -7028,12 +7045,12 @@ dependencies = [ [[package]] name = "zbus_names" -version = "4.3.1" +version = "4.3.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ffd8af6d5b78619bab301ff3c560a5bd22426150253db278f164d6cf3b72c50f" +checksum = "7074f3e50b894eac91750142016d30d0a89be8e67dbfd9704fb875825760e52d" dependencies = [ "serde", - "winnow 0.7.14", + "winnow 1.0.3", "zvariant", ] @@ -7156,23 +7173,23 @@ dependencies = [ [[package]] name = "zvariant" -version = "5.10.0" +version = "5.12.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5708299b21903bbe348e94729f22c49c55d04720a004aa350f1f9c122fd2540b" +checksum = "a192a0bde63360d77a7523c833d4b4ce6070a927e2c53246e4c540b1a3e27be0" dependencies = [ "endi", "enumflags2", "serde", - "winnow 0.7.14", + "winnow 1.0.3", "zvariant_derive", "zvariant_utils", ] [[package]] name = "zvariant_derive" -version = "5.10.0" +version = "5.12.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5b59b012ebe9c46656f9cc08d8da8b4c726510aef12559da3e5f1bf72780752c" +checksum = "90bc6cde9c01c511074be97f7ccb6c19d0da89e3f8662e812e999dcfd4638737" dependencies = [ "proc-macro-crate", "proc-macro2", @@ -7183,13 +7200,13 @@ dependencies = [ [[package]] name = "zvariant_utils" -version = "3.3.0" +version = "3.4.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f75c23a64ef8f40f13a6989991e643554d9bef1d682a281160cf0c1bc389c5e9" +checksum = "1e8535915cfa75547e559d8c68e8139909a4aeee076831e4ef7fc59d8172c4d6" dependencies = [ "proc-macro2", "quote", "serde", "syn", - "winnow 0.7.14", + "winnow 1.0.3", ] diff --git a/Cargo.toml b/Cargo.toml index a8c2dc2f..7023d1d7 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -4,7 +4,7 @@ edition = "2024" license = "GPL-3.0-only" name = "cosmic-comp" version = "1.0.0" -rust-version = "1.90" +rust-version = "1.93" [workspace] members = ["cosmic-comp-config"] @@ -28,7 +28,7 @@ cosmic-settings-config = { git = "https://github.com/pop-os/cosmic-settings-daem cosmic-settings-daemon-config = { git = "https://github.com/pop-os/cosmic-settings-daemon", features = [ "greeter", ] } -cosmic-text = { git = "https://github.com/pop-os/cosmic-text.git", features = [ +cosmic-text = { version = "0.19", features = [ "shape-run-cache", ] } libdisplay-info = "0.3.0" @@ -141,7 +141,6 @@ lto = "fat" [patch."https://github.com/pop-os/cosmic-protocols"] cosmic-protocols = { git = "https://github.com/pop-os//cosmic-protocols", branch = "main" } -cosmic-client-toolkit = { git = "https://github.com/pop-os//cosmic-protocols", branch = "main" } [patch.crates-io] smithay = { git = "https://github.com/smithay/smithay.git", rev = "8eb4076" } diff --git a/examples/customized-window-decorations.rs b/examples/customized-window-decorations.rs index ffe677f1..364221fe 100644 --- a/examples/customized-window-decorations.rs +++ b/examples/customized-window-decorations.rs @@ -23,7 +23,7 @@ struct Circle { color: Color, } -impl +impl canvas::Program for Circle { type State = (); @@ -50,17 +50,17 @@ impl cosmic::Element<'_, Message> { let orig = self.lower.view(window); - widget::row() - .push( - widget::column() - .push(canvas(Circle { - radius: (self.height as f32 / 2.) * 0.8, - color: Color::from_rgba(1.0, 0.0, 0.0, 1.0), - })) - .width(self.height as f32), - ) - .push(orig) - .into() + widget::row([ + widget::column([canvas(Circle { + radius: (self.height as f32 / 2.) * 0.8, + color: Color::from_rgba(1.0, 0.0, 0.0, 1.0), + }) + .into()]) + .width(self.height as f32) + .into(), + orig, + ]) + .into() } } diff --git a/rust-toolchain.toml b/rust-toolchain.toml index 9c8eecdc..ae16192b 100644 --- a/rust-toolchain.toml +++ b/rust-toolchain.toml @@ -1,3 +1,3 @@ [toolchain] -channel = "1.90" +channel = "1.93" components = [ "rust-src" ] diff --git a/src/backend/render/mod.rs b/src/backend/render/mod.rs index 7c1c1404..60917112 100644 --- a/src/backend/render/mod.rs +++ b/src/backend/render/mod.rs @@ -726,7 +726,7 @@ where return Ok(Vec::new()); } let theme = shell_ref.theme().clone(); - let blur_strength = 9; // TODO + let blur_strength = (theme.cosmic().frosted as u8 + 1) as usize; let scale = output.current_scale().fractional_scale(); // we don't want to hold a shell lock across `cursor_elements`, // that is prone to deadlock with the main-thread on some grabs. diff --git a/src/backend/render/wayland/blur_effect.rs b/src/backend/render/wayland/blur_effect.rs index e7c52395..47f5be88 100644 --- a/src/backend/render/wayland/blur_effect.rs +++ b/src/backend/render/wayland/blur_effect.rs @@ -117,21 +117,22 @@ impl BlurShaders { type BlurTexture = Mutex>; -struct BlurState { - id: Id, - renderer_id: Option>, - src: Size, - offset: f64, - passes: usize, - region: Vec>, - commit: CommitCounter, +#[derive(Debug)] +pub struct BlurState { + pub id: Id, + pub renderer_id: Option>, + pub src: Size, + pub offset: f64, + pub passes: usize, + pub region: Vec>, + pub commit: CommitCounter, } unsafe impl Send for BlurState {} unsafe impl Sync for BlurState {} -impl BlurState { - fn new() -> Self { +impl Default for BlurState { + fn default() -> Self { BlurState { id: Id::new(), renderer_id: None, @@ -159,6 +160,27 @@ pub struct BlurElement { } impl BlurElement { + pub fn from_state( + renderer: &mut R, + state: &mut BlurState, + geometry: Rectangle, + output_scale: f64, + radii: [u8; 4], + strength: usize, + ) -> Result, R::Error> { + let region = vec![Rectangle::from_size(geometry.size.to_i32_round())]; + + Self::internal( + renderer, + state, + geometry, + ®ion, + output_scale, + radii, + strength, + ) + } + pub fn from_surface( renderer: &mut R, states: &SurfaceData, @@ -172,9 +194,37 @@ impl BlurElement { return Ok(None); }; + let state = states + .data_map + .get_or_insert_threadsafe::, _>(Default::default); + + Self::internal( + renderer, + &mut state.lock().unwrap(), + geometry, + region, + output_scale, + radii, + strength, + ) + } + + pub fn internal( + renderer: &mut R, + state: &mut BlurState, + geometry: Rectangle, + region: &Vec>, + output_scale: f64, + radii: [u8; 4], + strength: usize, + ) -> Result, R::Error> { + if strength == 0 { + return Ok(None); + } + let geo = geometry.to_physical_precise_round(output_scale); let mut extended_geo = geo; - let radius = BLUR_PARAMS[strength.min(MAX_STEPS - 1)].extended_radius as f64; + let radius = BLUR_PARAMS[(strength + 2).min(MAX_STEPS - 1)].extended_radius as f64; extended_geo.loc -= Point::::new(radius, radius); extended_geo.size += Size::::new(radius, radius).upscale(2.); @@ -217,32 +267,9 @@ impl BlurElement { Uniform::new("noise", UniformValue::_1f(NOISE)), ]; - let state = states - .data_map - .get_or_insert_threadsafe::, _>(|| Mutex::new(BlurState::new())); + let geometry = extended_geo.to_logical(output_scale); + let extended_offset = Point::::new(radius, radius).to_logical(output_scale); - Ok(Some(Self::from_state( - renderer, - extended_geo.to_logical(output_scale), - Point::::new(radius, radius).to_logical(output_scale), - region, - output_scale, - &mut state.lock().unwrap(), - uniforms, - strength, - )?)) - } - - fn from_state( - renderer: &mut R, - geometry: Rectangle, - extended_offset: Point, - region: &Vec>, - output_scale: f64, - state: &mut BlurState, - uniforms: Vec>, - strength: usize, - ) -> Result { let renderer_id = renderer.glow_renderer().context_id(); let src = geometry.size.to_buffer(output_scale, Transform::Normal); let params = &BLUR_PARAMS[strength.min(MAX_STEPS - 1)]; @@ -265,7 +292,7 @@ impl BlurElement { state.commit.increment(); } - Ok(BlurElement { + Ok(Some(BlurElement { id: state.id.clone(), commit: state.commit, src, @@ -284,7 +311,7 @@ impl BlurElement { }) .collect(), uniforms, - }) + })) } } @@ -468,7 +495,7 @@ fn blit_from_active_fb( let mut fb = renderer.as_mut().bind(to_texture)?; std::mem::drop(renderer); - frame.blit_to( + let sync = frame.blit_to( &mut fb_tmp, dst, Rectangle::from_size(dst.size), @@ -480,6 +507,7 @@ fn blit_from_active_fb( let mut frame = renderer .as_mut() .render(&mut fb, tex_size, Transform::Normal)?; + frame.wait(&sync)?; Frame::render_texture_from_to( &mut frame, &tmp_texture, @@ -502,16 +530,14 @@ fn blit_from_active_fb( } else { let mut fb = frame.renderer().as_mut().bind(to_texture)?; - frame - .blit_to( - &mut fb, - dst, - src.to_logical(1., Transform::Normal, &src.size) - .to_physical(1.) - .to_i32_round(), - TextureFilter::Linear, - ) - .map(|_| SyncPoint::signaled()) + frame.blit_to( + &mut fb, + dst, + src.to_logical(1., Transform::Normal, &src.size) + .to_physical(1.) + .to_i32_round(), + TextureFilter::Linear, + ) } } diff --git a/src/shell/element/resize_indicator.rs b/src/shell/element/resize_indicator.rs index 49dc4781..d1839e6e 100644 --- a/src/shell/element/resize_indicator.rs +++ b/src/shell/element/resize_indicator.rs @@ -1,4 +1,4 @@ -use std::sync::Mutex; +use std::sync::{Arc, Mutex}; use crate::{ config::Config, @@ -12,9 +12,9 @@ use cosmic::{ Apply, iced::{ Alignment, - widget::{column, container, row, space}, + core::{Background, Border, Color, Length}, + widget::{container, row, space}, }, - iced_core::{Background, Border, Color, Length}, theme, widget::{icon::from_name, text}, }; @@ -67,120 +67,90 @@ impl Program for ResizeIndicatorInternal { type Message = (); fn view(&self) -> cosmic::Element<'_, Self::Message> { - let edges = self.edges.lock().unwrap(); - let icon_container_style = || { - theme::Container::custom(|theme| container::Style { + row(vec![ + text::heading(&self.shortcut1).into(), + text::body(fl!("grow-window")).into(), + space::horizontal().width(40).into(), + text::heading(&self.shortcut2).into(), + text::body(fl!("shrink-window")).into(), + ]) + .apply(container) + .align_x(Alignment::Center) + .align_y(Alignment::Center) + .padding(16) + .apply(container) + .class(theme::Container::custom(|theme| { + let mut background = theme.cosmic().accent_color(); + if theme.transparent { + background.alpha = theme + .cosmic() + .alpha_map + .blurred_alpha(theme.cosmic().frosted); + } + + container::Style { snap: true, icon_color: Some(Color::from(theme.cosmic().accent.on)), text_color: Some(Color::from(theme.cosmic().accent.on)), - background: Some(Background::Color(theme.cosmic().accent_color().into())), + background: Some(Background::Color(background.into())), border: Border { radius: 18.0.into(), width: 0.0, color: Color::TRANSPARENT, }, shadow: Default::default(), - }) - }; - - column(vec![ - if edges.contains(ResizeEdge::TOP) { - from_name(if self.direction == ResizeDirection::Outwards { - "go-up-symbolic" - } else { - "go-down-symbolic" - }) - .size(20) - .prefer_svg(true) - .apply(container) - .padding(8) - .class(icon_container_style()) - .width(Length::Shrink) - .apply(container) - .center_x(Length::Fill) - .into() - } else { - space::vertical().height(36).into() - }, - row(vec![ - if edges.contains(ResizeEdge::LEFT) { - from_name(if self.direction == ResizeDirection::Outwards { - "go-previous-symbolic" - } else { - "go-next-symbolic" - }) - .size(20) - .prefer_svg(true) - .apply(container) - .padding(8) - .class(icon_container_style()) - .width(Length::Shrink) - .apply(container) - .center_y(Length::Fill) - .into() - } else { - space::horizontal().width(36).into() - }, - row(vec![ - text::heading(&self.shortcut1).into(), - text::body(fl!("grow-window")).into(), - space::horizontal().width(40).into(), - text::heading(&self.shortcut2).into(), - text::body(fl!("shrink-window")).into(), - ]) - .apply(container) - .align_x(Alignment::Center) - .align_y(Alignment::Center) - .padding(16) - .apply(container) - .class(icon_container_style()) - .width(Length::Shrink) - .height(Length::Shrink) - .apply(container) - .center_x(Length::Fill) - .center_y(Length::Fill) - .into(), - if edges.contains(ResizeEdge::RIGHT) { - from_name(if self.direction == ResizeDirection::Outwards { - "go-next-symbolic" - } else { - "go-previous-symbolic" - }) - .size(20) - .prefer_svg(true) - .apply(container) - .padding(8) - .class(icon_container_style()) - .height(Length::Shrink) - .apply(container) - .center_y(Length::Fill) - .into() - } else { - space::horizontal().width(36).into() - }, - ]) - .width(Length::Fill) - .height(Length::Fill) - .into(), - if edges.contains(ResizeEdge::BOTTOM) { - from_name(if self.direction == ResizeDirection::Outwards { - "go-down-symbolic" - } else { - "go-up-symbolic" - }) - .size(20) - .prefer_svg(true) - .apply(container) - .padding(8) - .class(icon_container_style()) - .width(Length::Shrink) - .apply(container) - .center_x(Length::Fill) - .into() - } else { - space::vertical().height(36).into() - }, - ]) + } + })) + .width(Length::Shrink) + .height(Length::Shrink) + .into() + } +} + +pub struct ResizeIndicatorArrow { + direction: Arc>, + icon_outwards: &'static str, + icon_inwards: &'static str, +} + +impl Program for ResizeIndicatorArrow { + type Message = (); + + fn view(&self) -> cosmic::Element<'_, Self::Message> { + from_name( + if *self.direction.lock().unwrap() == ResizeDirection::Outwards { + self.icon_outwards + } else { + self.icon_inwards + }, + ) + .size(20) + .prefer_svg(true) + .apply(container) + .padding(8) + .class(theme::Container::custom(|theme| { + let mut background = theme.cosmic().accent_color(); + if theme.transparent { + background.alpha = theme + .cosmic() + .alpha_map + .blurred_alpha(theme.cosmic().frosted); + } + + container::Style { + snap: true, + icon_color: Some(Color::from(theme.cosmic().accent.on)), + text_color: Some(Color::from(theme.cosmic().accent.on)), + background: Some(Background::Color(background.into())), + border: Border { + radius: theme.cosmic().radius_s().into(), + width: 0.0, + color: Color::TRANSPARENT, + }, + shadow: Default::default(), + } + })) + .width(Length::Shrink) .into() } } diff --git a/src/shell/element/stack.rs b/src/shell/element/stack.rs index 0980facb..3b1aab87 100644 --- a/src/shell/element/stack.rs +++ b/src/shell/element/stack.rs @@ -4,8 +4,14 @@ use super::{ }; use crate::{ backend::render::{ - IndicatorShader, Key, Usage, cursor::CursorState, element::AsGlowRenderer, - shadow::ShadowShader, wayland::SurfaceRenderElement, + IndicatorShader, Key, Usage, + cursor::CursorState, + element::AsGlowRenderer, + shadow::ShadowShader, + wayland::{ + SurfaceRenderElement, + blur_effect::{BlurElement, BlurState}, + }, }, hooks::{Decorations, HOOKS}, shell::{ @@ -23,10 +29,13 @@ use crate::{ use calloop::LoopHandle; use cosmic::{ Apply, Element as CosmicElement, Theme, - iced::{Alignment, id::Id, widget as iced_widget}, - iced_core::{Background, Border, Color, Length, border::Radius}, - iced_runtime::Task, - iced_widget::scrollable::AbsoluteOffset, + iced::{ + Alignment, + core::{Background, Border, Color, Length, border::Radius}, + id::Id, + runtime::Task, + widget::{self as iced_widget, scrollable::AbsoluteOffset}, + }, theme, widget as cosmic_widget, }; use cosmic_comp_config::AppearanceConfig; @@ -120,6 +129,7 @@ pub struct CosmicStackInternal { tiled: AtomicBool, theme: Mutex, appearance_conf: Mutex, + blur_state: Mutex, } impl CosmicStackInternal { @@ -178,6 +188,7 @@ impl CosmicStack { tiled: AtomicBool::new(false), theme: Mutex::new(theme.clone()), appearance_conf: Mutex::new(appearance), + blur_state: Default::default(), }, (width, TAB_HEIGHT), handle, @@ -661,6 +672,12 @@ impl CosmicStack { self.0.with_program(|p| { let windows = p.windows.lock().unwrap(); let active = p.active.load(Ordering::SeqCst); + let theme = p.theme.lock().unwrap(); + let frosted = if theme.cosmic().frosted_windows { + (theme.cosmic().frosted as u8 + 1) as usize + } else { + 0 + }; windows[active].push_popup_render_elements( renderer, @@ -668,7 +685,7 @@ impl CosmicStack { scale, alpha, scanout_node, - 9, // TODO + frosted, &mut |elem| push(elem.into()), ) }) @@ -769,6 +786,30 @@ impl CosmicStack { .to_physical_precise_round(scale); let stack_loc = location + geometry.loc; let window_loc = location + Point::from((0, (TAB_HEIGHT as f64 * scale.y) as i32)); + let frosted = self.0.with_program(|p| { + let theme = p.theme.lock().unwrap(); + if theme.cosmic().frosted_windows { + (theme.cosmic().frosted as u8 + 1) as usize + } else { + 0 + } + }); + let radii = self.0.with_program(|p| { + let windows = p.windows.lock().unwrap(); + let active = p.active.load(Ordering::SeqCst); + let appearance = p.appearance_conf.lock().unwrap(); + let theme = p.theme.lock().unwrap(); + let tiled = p.tiled.load(Ordering::Acquire); + let maximized = windows[active].is_maximized(false); + let round = (appearance.clip_tiled_windows || !tiled) && !maximized; + round.then(|| { + theme + .cosmic() + .radius_s() + .map(|x| if x < 4.0 { x } else { x + 4.0 }) + .map(|x| x.round() as u8) + }) + }); self.0 .push_render_elements(renderer, stack_loc, scale, alpha, &mut |elem| { @@ -779,19 +820,8 @@ impl CosmicStack { let windows = p.windows.lock().unwrap(); let active = p.active.load(Ordering::SeqCst); let theme = p.theme.lock().unwrap(); - let appearance = p.appearance_conf.lock().unwrap(); - let tiled = p.tiled.load(Ordering::Acquire); let maximized = windows[active].is_maximized(false); - let round = (appearance.clip_tiled_windows || !tiled) && !maximized; - let radii = round.then(|| { - theme - .cosmic() - .radius_s() - .map(|x| if x < 4.0 { x } else { x + 4.0 }) - .map(|x| x.round() as u8) - }); - let mut geo = SpaceElement::geometry(&windows[active]).to_f64(); geo.loc += location.to_f64().to_logical(scale); geo.size.h += TAB_HEIGHT as f64; @@ -809,7 +839,7 @@ impl CosmicStack { Key::Window(Usage::Border, window_key.clone()), geo.to_i32_round().as_local(), 1, - radii.unwrap_or([0, 0, 0, 0]), + radii.unwrap_or([0; 4]), a * alpha, scale.x, [r, g, b], @@ -826,11 +856,34 @@ impl CosmicStack { scanout_node, radii.is_some(), radii.unwrap_or([0; 4]), - 9, // as TODO + frosted, &mut |elem| push_above(elem.into()), Some(&mut |elem| push_below(elem.into())), ); }); + + if frosted > 0 { + let geometry = Rectangle::new( + stack_loc.to_f64().to_logical(scale), + Size::new( + geometry.size.to_f64().to_logical(scale).w, + TAB_HEIGHT as f64, + ), + ); + if let Ok(Some(elem)) = self.0.with_program(|p| { + let mut state = p.blur_state.lock().unwrap(); + BlurElement::from_state( + renderer, + &mut state, + geometry, + scale.x, + radii.map(|[_, b, _, d]| [0, b, 0, d]).unwrap_or([0; 4]), + frosted, + ) + }) { + push_below(elem.into()); + } + } } pub(crate) fn set_theme(&self, theme: cosmic::Theme) { @@ -1342,16 +1395,29 @@ impl Decorations for DefaultDecorations { .class(theme::Container::custom(move |theme| { let cosmic_theme = theme.cosmic(); - let background = if group_focused { + let mut background = if group_focused { cosmic_theme.accent_color() } else { cosmic_theme.primary_container_color() }; + if cosmic_theme.frosted_windows { + background.alpha = cosmic_theme.alpha_map.blurred_alpha(cosmic_theme.frosted); + } iced_widget::container::Style { snap: true, - icon_color: Some(cosmic_theme.background.on.into()), - text_color: Some(cosmic_theme.background.on.into()), + icon_color: Some( + cosmic_theme + .background(cosmic_theme.frosted_windows) + .on + .into(), + ), + text_color: Some( + cosmic_theme + .background(cosmic_theme.frosted_windows) + .on + .into(), + ), background: Some(Background::Color(background.into())), border: Border { radius, @@ -1864,6 +1930,7 @@ impl TouchTarget for CosmicStack { pub enum CosmicStackRenderElement { Header(MemoryRenderBufferRenderElement), + HeaderBlur(BlurElement), Shadow(PixelShaderElement), Border(PixelShaderElement), Window(SurfaceRenderElement), @@ -1877,6 +1944,12 @@ impl From From for CosmicStackRenderElement { + fn from(value: BlurElement) -> Self { + Self::HeaderBlur(value) + } +} + impl From> for CosmicStackRenderElement { @@ -1893,6 +1966,7 @@ where fn id(&self) -> &RendererId { match self { CosmicStackRenderElement::Header(elem) => elem.id(), + CosmicStackRenderElement::HeaderBlur(elem) => elem.id(), CosmicStackRenderElement::Shadow(elem) => elem.id(), CosmicStackRenderElement::Border(elem) => elem.id(), CosmicStackRenderElement::Window(elem) => elem.id(), @@ -1902,6 +1976,7 @@ where fn current_commit(&self) -> CommitCounter { match self { CosmicStackRenderElement::Header(elem) => elem.current_commit(), + CosmicStackRenderElement::HeaderBlur(elem) => elem.current_commit(), CosmicStackRenderElement::Shadow(elem) => elem.current_commit(), CosmicStackRenderElement::Border(elem) => elem.current_commit(), CosmicStackRenderElement::Window(elem) => elem.current_commit(), @@ -1911,6 +1986,7 @@ where fn src(&self) -> Rectangle { match self { CosmicStackRenderElement::Header(elem) => elem.src(), + CosmicStackRenderElement::HeaderBlur(elem) => elem.src(), CosmicStackRenderElement::Shadow(elem) => elem.src(), CosmicStackRenderElement::Border(elem) => elem.src(), CosmicStackRenderElement::Window(elem) => elem.src(), @@ -1920,6 +1996,7 @@ where fn geometry(&self, scale: Scale) -> Rectangle { match self { CosmicStackRenderElement::Header(elem) => elem.geometry(scale), + CosmicStackRenderElement::HeaderBlur(elem) => elem.geometry(scale), CosmicStackRenderElement::Shadow(elem) => elem.geometry(scale), CosmicStackRenderElement::Border(elem) => elem.geometry(scale), CosmicStackRenderElement::Window(elem) => elem.geometry(scale), @@ -1929,6 +2006,7 @@ where fn location(&self, scale: Scale) -> Point { match self { CosmicStackRenderElement::Header(elem) => elem.location(scale), + CosmicStackRenderElement::HeaderBlur(elem) => elem.location(scale), CosmicStackRenderElement::Shadow(elem) => elem.location(scale), CosmicStackRenderElement::Border(elem) => elem.location(scale), CosmicStackRenderElement::Window(elem) => elem.location(scale), @@ -1938,6 +2016,7 @@ where fn transform(&self) -> Transform { match self { CosmicStackRenderElement::Header(elem) => elem.transform(), + CosmicStackRenderElement::HeaderBlur(elem) => elem.transform(), CosmicStackRenderElement::Shadow(elem) => elem.transform(), CosmicStackRenderElement::Border(elem) => elem.transform(), CosmicStackRenderElement::Window(elem) => elem.transform(), @@ -1951,6 +2030,7 @@ where ) -> DamageSet { match self { CosmicStackRenderElement::Header(elem) => elem.damage_since(scale, commit), + CosmicStackRenderElement::HeaderBlur(elem) => elem.damage_since(scale, commit), CosmicStackRenderElement::Shadow(elem) => elem.damage_since(scale, commit), CosmicStackRenderElement::Border(elem) => elem.damage_since(scale, commit), CosmicStackRenderElement::Window(elem) => elem.damage_since(scale, commit), @@ -1960,6 +2040,7 @@ where fn opaque_regions(&self, scale: Scale) -> OpaqueRegions { match self { CosmicStackRenderElement::Header(elem) => elem.opaque_regions(scale), + CosmicStackRenderElement::HeaderBlur(elem) => elem.opaque_regions(scale), CosmicStackRenderElement::Shadow(elem) => elem.opaque_regions(scale), CosmicStackRenderElement::Border(elem) => elem.opaque_regions(scale), CosmicStackRenderElement::Window(elem) => elem.opaque_regions(scale), @@ -1969,6 +2050,7 @@ where fn alpha(&self) -> f32 { match self { CosmicStackRenderElement::Header(elem) => elem.alpha(), + CosmicStackRenderElement::HeaderBlur(elem) => elem.alpha(), CosmicStackRenderElement::Shadow(elem) => elem.alpha(), CosmicStackRenderElement::Border(elem) => elem.alpha(), CosmicStackRenderElement::Window(elem) => elem.alpha(), @@ -1978,6 +2060,7 @@ where fn kind(&self) -> Kind { match self { CosmicStackRenderElement::Header(elem) => elem.kind(), + CosmicStackRenderElement::HeaderBlur(elem) => elem.kind(), CosmicStackRenderElement::Shadow(elem) => elem.kind(), CosmicStackRenderElement::Border(elem) => elem.kind(), CosmicStackRenderElement::Window(elem) => elem.kind(), @@ -1987,6 +2070,7 @@ where fn is_framebuffer_effect(&self) -> bool { match self { CosmicStackRenderElement::Header(elem) => elem.is_framebuffer_effect(), + CosmicStackRenderElement::HeaderBlur(elem) => elem.is_framebuffer_effect(), CosmicStackRenderElement::Shadow(elem) => elem.is_framebuffer_effect(), CosmicStackRenderElement::Border(elem) => elem.is_framebuffer_effect(), CosmicStackRenderElement::Window(elem) => elem.is_framebuffer_effect(), @@ -2012,6 +2096,9 @@ where CosmicStackRenderElement::Header(elem) => { elem.draw(frame, src, dst, damage, opaque_regions, cache) } + CosmicStackRenderElement::HeaderBlur(elem) => { + RenderElement::::draw(elem, frame, src, dst, damage, opaque_regions, cache) + } CosmicStackRenderElement::Shadow(elem) | CosmicStackRenderElement::Border(elem) => { RenderElement::::draw( elem, @@ -2033,6 +2120,7 @@ where fn underlying_storage(&self, renderer: &mut R) -> Option> { match self { CosmicStackRenderElement::Header(elem) => elem.underlying_storage(renderer), + CosmicStackRenderElement::HeaderBlur(elem) => elem.underlying_storage(renderer), CosmicStackRenderElement::Shadow(elem) | CosmicStackRenderElement::Border(elem) => { elem.underlying_storage(renderer.glow_renderer_mut()) } @@ -2051,6 +2139,9 @@ where CosmicStackRenderElement::Header(elem) => { elem.capture_framebuffer(frame, src, dst, cache) } + CosmicStackRenderElement::HeaderBlur(elem) => { + RenderElement::::capture_framebuffer(elem, frame, src, dst, cache) + } CosmicStackRenderElement::Shadow(elem) | CosmicStackRenderElement::Border(elem) => { RenderElement::::capture_framebuffer( elem, diff --git a/src/shell/element/stack/tab.rs b/src/shell/element/stack/tab.rs index 99b0c247..f8fda525 100644 --- a/src/shell/element/stack/tab.rs +++ b/src/shell/element/stack/tab.rs @@ -3,16 +3,15 @@ use cosmic::{ font::Font, iced::{ Background, - widget::{self, container::draw_background, rule::FillMode}, + core::{ + Border, Clipboard, Color, Length, Rectangle, Shell, Size, alignment, event, + layout::{Layout, Limits, Node}, + mouse, overlay, renderer, + text::{Ellipsize, EllipsizeHeightLimit, Shaping, Wrapping}, + widget::{Id, Widget, operation::Operation, tree::Tree}, + }, + widget::{self, container::draw_background, rule::FillMode, scrollable::AbsoluteOffset}, }, - iced_core::{ - Border, Clipboard, Color, Length, Rectangle, Shell, Size, alignment, event, - layout::{Layout, Limits, Node}, - mouse, overlay, renderer, - text::{Ellipsize, EllipsizeHeightLimit, Shaping, Wrapping}, - widget::{Id, Widget, operation::Operation, tree::Tree}, - }, - iced_widget::scrollable::AbsoluteOffset, theme, widget::{Icon, icon::from_name, text}, }; @@ -64,9 +63,20 @@ impl From for theme::Container<'_> { snap: true, icon_color: Some(Color::from(theme.cosmic().accent_text_color())), text_color: Some(Color::from(theme.cosmic().accent_text_color())), - background: Some(Background::Color( - theme.cosmic().primary.component.selected.into(), - )), + background: Some(Background::Color({ + let mut color = theme + .cosmic() + .primary(theme.cosmic().frosted_windows) + .component + .selected; + if theme.cosmic().frosted_windows { + color.alpha = theme + .cosmic() + .alpha_map + .blurred_alpha(theme.cosmic().frosted); + } + color.into() + })), border: Border { radius: 0.0.into(), width: 0.0, @@ -80,9 +90,20 @@ impl From for theme::Container<'_> { snap: true, icon_color: None, text_color: None, - background: Some(Background::Color( - theme.cosmic().primary.component.base.into(), - )), + background: Some(Background::Color({ + let mut color = theme + .cosmic() + .primary(theme.cosmic().frosted_windows) + .component + .base; + if theme.cosmic().frosted_windows { + color.alpha = theme + .cosmic() + .alpha_map + .blurred_alpha(theme.cosmic().frosted); + } + color.into() + })), border: Border { radius: 0.0.into(), width: 0.0, @@ -289,8 +310,8 @@ where .min_height(size.height) .width(size.width) .height(size.height); - cosmic::iced_core::layout::flex::resolve( - cosmic::iced_core::layout::flex::Axis::Horizontal, + cosmic::iced::core::layout::flex::resolve( + cosmic::iced::core::layout::flex::Axis::Horizontal, renderer, &limits, Length::Fill, diff --git a/src/shell/element/stack/tabs.rs b/src/shell/element/stack/tabs.rs index f411fe26..86218db6 100644 --- a/src/shell/element/stack/tabs.rs +++ b/src/shell/element/stack/tabs.rs @@ -1,24 +1,30 @@ use super::tab::{MIN_ACTIVE_TAB_WIDTH, Tab, TabBackgroundTheme, TabMessage, TabRuleTheme}; use cosmic::{ Apply, - iced::{Element, id::Id, widget}, - iced_core::{ - Background, Border, Clipboard, Color, Length, Point, Rectangle, Renderer, Shell, Size, - Vector, event, - layout::{Layout, Limits, Node}, - mouse, overlay, renderer, - widget::{ - Widget, - operation::{ - Operation, Scrollable, - scrollable::{AbsoluteOffset, RelativeOffset}, + iced::{ + Element, + core::{ + Background, Border, Clipboard, Color, Length, Point, Rectangle, Renderer, Shell, Size, + Vector, event, + layout::{Layout, Limits, Node}, + mouse, overlay, renderer, + widget::{ + Widget, + operation::{ + Operation, Scrollable, + scrollable::{AbsoluteOffset, RelativeOffset}, + }, + tree::{self, Tree}, }, - tree::{self, Tree}, + }, + id::Id, + widget::{ + self, + container::{Catalog, draw_background}, }, }, - iced_widget::container::draw_background, theme, - widget::{container::Catalog, icon::from_name}, + widget::icon::from_name, }; use keyframe::{ ease, @@ -379,8 +385,8 @@ where >= MIN_ACTIVE_TAB_WIDTH { // just use a flex layout - cosmic::iced_core::layout::flex::resolve( - cosmic::iced_core::layout::flex::Axis::Horizontal, + cosmic::iced::core::layout::flex::resolve( + cosmic::iced::core::layout::flex::Axis::Horizontal, renderer, &limits, self.width, @@ -513,13 +519,11 @@ where let background_style = Catalog::style( theme, - &theme::Container::custom(|theme| widget::container::Style { + &theme::Container::custom(|_theme| widget::container::Style { snap: true, icon_color: None, text_color: None, - background: Some(Background::Color( - theme.cosmic().primary_container_color().into(), - )), + background: Some(Background::Color(Color::TRANSPARENT)), border: Border { radius: 0.0.into(), width: 0.0, diff --git a/src/shell/element/stack_hover.rs b/src/shell/element/stack_hover.rs index 9a86709c..d6a057c4 100644 --- a/src/shell/element/stack_hover.rs +++ b/src/shell/element/stack_hover.rs @@ -8,9 +8,9 @@ use cosmic::{ Apply, iced::{ Alignment, + core::{Background, Border, Color, Length}, widget::{container, row}, }, - iced_core::{Background, Border, Color, Length}, theme, widget::{icon::from_name, space, text}, }; @@ -47,17 +47,27 @@ impl Program for StackHoverInternal { .align_y(Alignment::Center) .padding(16) .apply(container) - .class(theme::Container::custom(|theme| container::Style { - snap: true, - icon_color: Some(Color::from(theme.cosmic().accent.on)), - text_color: Some(Color::from(theme.cosmic().accent.on)), - background: Some(Background::Color(theme.cosmic().accent_color().into())), - border: Border { - radius: 18.0.into(), - width: 0.0, - color: Color::TRANSPARENT, - }, - shadow: Default::default(), + .class(theme::Container::custom(|theme| { + let mut background = theme.cosmic().accent_color(); + if theme.transparent { + background.alpha = theme + .cosmic() + .alpha_map + .blurred_alpha(theme.cosmic().frosted); + } + + container::Style { + snap: true, + icon_color: Some(Color::from(theme.cosmic().accent.on)), + text_color: Some(Color::from(theme.cosmic().accent.on)), + background: Some(Background::Color(background.into())), + border: Border { + radius: theme.cosmic().radius_s().into(), + width: 0.0, + color: Color::TRANSPARENT, + }, + shadow: Default::default(), + } })) .width(Length::Shrink) .height(Length::Shrink) diff --git a/src/shell/element/swap_indicator.rs b/src/shell/element/swap_indicator.rs index 0db66cda..e6fb0197 100644 --- a/src/shell/element/swap_indicator.rs +++ b/src/shell/element/swap_indicator.rs @@ -6,8 +6,10 @@ use crate::{ use calloop::LoopHandle; use cosmic::{ Apply, - iced::widget::{container, row, space}, - iced_core::{Alignment, Background, Border, Color, Length}, + iced::{ + core::{Alignment, Background, Border, Color, Length}, + widget::{container, row, space}, + }, theme, widget::{icon::from_name, text}, }; @@ -43,17 +45,27 @@ impl Program for SwapIndicatorInternal { .align_y(Alignment::Center) .padding(16) .apply(container) - .class(theme::Container::custom(|theme| container::Style { - snap: true, - icon_color: Some(Color::from(theme.cosmic().accent.on)), - text_color: Some(Color::from(theme.cosmic().accent.on)), - background: Some(Background::Color(theme.cosmic().accent_color().into())), - border: Border { - radius: 18.0.into(), - width: 0.0, - color: Color::TRANSPARENT, - }, - shadow: Default::default(), + .class(theme::Container::custom(|theme| { + let mut background = theme.cosmic().accent_color(); + if theme.transparent { + background.alpha = theme + .cosmic() + .alpha_map + .blurred_alpha(theme.cosmic().frosted); + } + + container::Style { + snap: true, + icon_color: Some(Color::from(theme.cosmic().accent.on)), + text_color: Some(Color::from(theme.cosmic().accent.on)), + background: Some(Background::Color(background.into())), + border: Border { + radius: theme.cosmic().radius_s().into(), + width: 0.0, + color: Color::TRANSPARENT, + }, + shadow: Default::default(), + } })) .width(Length::Shrink) .height(Length::Shrink) diff --git a/src/shell/element/window.rs b/src/shell/element/window.rs index de949b8e..b978cfd3 100644 --- a/src/shell/element/window.rs +++ b/src/shell/element/window.rs @@ -1,7 +1,13 @@ use crate::{ backend::render::{ - IndicatorShader, Key, Usage, cursor::CursorState, element::AsGlowRenderer, - shadow::ShadowShader, wayland::SurfaceRenderElement, + IndicatorShader, Key, Usage, + cursor::CursorState, + element::AsGlowRenderer, + shadow::ShadowShader, + wayland::{ + SurfaceRenderElement, + blur_effect::{BlurElement, BlurState}, + }, }, hooks::{Decorations, HOOKS}, shell::{ @@ -93,6 +99,7 @@ pub struct CosmicWindowInternal { tiled: AtomicBool, theme: Mutex, appearance_conf: Mutex, + blur_state: Mutex, } #[repr(u8)] @@ -213,6 +220,7 @@ impl CosmicWindow { tiled: AtomicBool::new(false), theme: Mutex::new(theme.clone()), appearance_conf: Mutex::new(appearance), + blur_state: Mutex::default(), }, (width, SSD_HEIGHT), handle, @@ -369,13 +377,20 @@ impl CosmicWindow { }; self.0.with_program(|p| { + let theme = p.theme.lock().unwrap(); + let frosted = if theme.cosmic().frosted_windows { + (theme.cosmic().frosted as u8 + 1) as usize + } else { + 0 + }; + p.window.push_popup_render_elements( renderer, window_loc, scale, alpha, scanout_node, - 9, // TODO + frosted, &mut |elem| push(elem.into()), ) }) @@ -543,11 +558,17 @@ impl CosmicWindow { push_above(elem); } - self.0.with_program(|p| { + let frosted = self.0.with_program(|p| { if has_ssd { radii[1] = 0; radii[3] = 0; } + let theme = p.theme.lock().unwrap(); + let frosted = if theme.cosmic().frosted_windows { + (theme.cosmic().frosted as u8 + 1) as usize + } else { + 0 + }; p.window.push_render_elements( renderer, window_loc, @@ -557,18 +578,31 @@ impl CosmicWindow { scanout_node, clip, radii, - 9, // TODO + frosted, &mut |elem| push_above(elem.into()), Some(&mut |elem| push_below(elem.into())), - ) + ); + + frosted }); if has_ssd { let ssd_loc = location + self .0 - .with_program(|p| p.window.geometry().loc) - .to_physical_precise_round(scale); + .with_program(|p| p.window.geometry().loc.to_physical_precise_round(scale)); + if frosted > 0 { + let geometry = Rectangle::new( + ssd_loc.to_f64().to_logical(scale), + Size::new(geo.size.w, SSD_HEIGHT as f64), + ); + if let Ok(Some(elem)) = self.0.with_program(|p| { + let mut state = p.blur_state.lock().unwrap(); + BlurElement::from_state(renderer, &mut state, geometry, scale.x, radii, frosted) + }) { + push_below(elem.into()); + } + } self.0 .push_render_elements(renderer, ssd_loc, scale, alpha, &mut |elem| { push_above(elem.into()) @@ -812,7 +846,11 @@ impl Program for CosmicWindowInternal { fn background_color(&self, theme: &cosmic::Theme) -> Color { if self.window.is_maximized(false) { - theme.cosmic().background.base.into() + theme + .cosmic() + .background(theme.cosmic().frosted_windows) + .base + .into() } else { Color::TRANSPARENT } @@ -1231,6 +1269,7 @@ impl WaylandFocus for CosmicWindow { pub enum CosmicWindowRenderElement { Header(MemoryRenderBufferRenderElement), + HeaderBlur(BlurElement), Shadow(PixelShaderElement), Border(PixelShaderElement), Window(SurfaceRenderElement), @@ -1244,6 +1283,12 @@ impl From From for CosmicWindowRenderElement { + fn from(value: BlurElement) -> Self { + Self::HeaderBlur(value) + } +} + impl From> for CosmicWindowRenderElement { @@ -1260,6 +1305,7 @@ where fn id(&self) -> &RendererId { match self { CosmicWindowRenderElement::Header(elem) => elem.id(), + CosmicWindowRenderElement::HeaderBlur(elem) => elem.id(), CosmicWindowRenderElement::Shadow(elem) => elem.id(), CosmicWindowRenderElement::Border(elem) => elem.id(), CosmicWindowRenderElement::Window(elem) => elem.id(), @@ -1269,6 +1315,7 @@ where fn current_commit(&self) -> CommitCounter { match self { CosmicWindowRenderElement::Header(elem) => elem.current_commit(), + CosmicWindowRenderElement::HeaderBlur(elem) => elem.current_commit(), CosmicWindowRenderElement::Shadow(elem) => elem.current_commit(), CosmicWindowRenderElement::Border(elem) => elem.current_commit(), CosmicWindowRenderElement::Window(elem) => elem.current_commit(), @@ -1278,6 +1325,7 @@ where fn src(&self) -> Rectangle { match self { CosmicWindowRenderElement::Header(elem) => elem.src(), + CosmicWindowRenderElement::HeaderBlur(elem) => elem.src(), CosmicWindowRenderElement::Shadow(elem) => elem.src(), CosmicWindowRenderElement::Border(elem) => elem.src(), CosmicWindowRenderElement::Window(elem) => elem.src(), @@ -1287,6 +1335,7 @@ where fn geometry(&self, scale: Scale) -> Rectangle { match self { CosmicWindowRenderElement::Header(elem) => elem.geometry(scale), + CosmicWindowRenderElement::HeaderBlur(elem) => elem.geometry(scale), CosmicWindowRenderElement::Shadow(elem) => elem.geometry(scale), CosmicWindowRenderElement::Border(elem) => elem.geometry(scale), CosmicWindowRenderElement::Window(elem) => elem.geometry(scale), @@ -1296,6 +1345,7 @@ where fn location(&self, scale: Scale) -> Point { match self { CosmicWindowRenderElement::Header(elem) => elem.location(scale), + CosmicWindowRenderElement::HeaderBlur(elem) => elem.location(scale), CosmicWindowRenderElement::Shadow(elem) => elem.location(scale), CosmicWindowRenderElement::Border(elem) => elem.location(scale), CosmicWindowRenderElement::Window(elem) => elem.location(scale), @@ -1305,6 +1355,7 @@ where fn transform(&self) -> Transform { match self { CosmicWindowRenderElement::Header(elem) => elem.transform(), + CosmicWindowRenderElement::HeaderBlur(elem) => elem.transform(), CosmicWindowRenderElement::Shadow(elem) => elem.transform(), CosmicWindowRenderElement::Border(elem) => elem.transform(), CosmicWindowRenderElement::Window(elem) => elem.transform(), @@ -1318,6 +1369,7 @@ where ) -> DamageSet { match self { CosmicWindowRenderElement::Header(elem) => elem.damage_since(scale, commit), + CosmicWindowRenderElement::HeaderBlur(elem) => elem.damage_since(scale, commit), CosmicWindowRenderElement::Shadow(elem) => elem.damage_since(scale, commit), CosmicWindowRenderElement::Border(elem) => elem.damage_since(scale, commit), CosmicWindowRenderElement::Window(elem) => elem.damage_since(scale, commit), @@ -1327,6 +1379,7 @@ where fn opaque_regions(&self, scale: Scale) -> OpaqueRegions { match self { CosmicWindowRenderElement::Header(elem) => elem.opaque_regions(scale), + CosmicWindowRenderElement::HeaderBlur(elem) => elem.opaque_regions(scale), CosmicWindowRenderElement::Shadow(elem) => elem.opaque_regions(scale), CosmicWindowRenderElement::Border(elem) => elem.opaque_regions(scale), CosmicWindowRenderElement::Window(elem) => elem.opaque_regions(scale), @@ -1336,6 +1389,7 @@ where fn alpha(&self) -> f32 { match self { CosmicWindowRenderElement::Header(elem) => elem.alpha(), + CosmicWindowRenderElement::HeaderBlur(elem) => elem.alpha(), CosmicWindowRenderElement::Shadow(elem) => elem.alpha(), CosmicWindowRenderElement::Border(elem) => elem.alpha(), CosmicWindowRenderElement::Window(elem) => elem.alpha(), @@ -1345,6 +1399,7 @@ where fn kind(&self) -> Kind { match self { CosmicWindowRenderElement::Header(elem) => elem.kind(), + CosmicWindowRenderElement::HeaderBlur(elem) => elem.kind(), CosmicWindowRenderElement::Shadow(elem) => elem.kind(), CosmicWindowRenderElement::Border(elem) => elem.kind(), CosmicWindowRenderElement::Window(elem) => elem.kind(), @@ -1354,6 +1409,7 @@ where fn is_framebuffer_effect(&self) -> bool { match self { CosmicWindowRenderElement::Header(elem) => elem.is_framebuffer_effect(), + CosmicWindowRenderElement::HeaderBlur(elem) => elem.is_framebuffer_effect(), CosmicWindowRenderElement::Shadow(elem) => elem.is_framebuffer_effect(), CosmicWindowRenderElement::Border(elem) => elem.is_framebuffer_effect(), CosmicWindowRenderElement::Window(elem) => elem.is_framebuffer_effect(), @@ -1379,6 +1435,9 @@ where CosmicWindowRenderElement::Header(elem) => { elem.draw(frame, src, dst, damage, opaque_regions, cache) } + CosmicWindowRenderElement::HeaderBlur(elem) => { + RenderElement::::draw(elem, frame, src, dst, damage, opaque_regions, cache) + } CosmicWindowRenderElement::Shadow(elem) | CosmicWindowRenderElement::Border(elem) => { RenderElement::::draw( elem, @@ -1400,6 +1459,7 @@ where fn underlying_storage(&self, renderer: &mut R) -> Option> { match self { CosmicWindowRenderElement::Header(elem) => elem.underlying_storage(renderer), + CosmicWindowRenderElement::HeaderBlur(elem) => elem.underlying_storage(renderer), CosmicWindowRenderElement::Shadow(elem) | CosmicWindowRenderElement::Border(elem) => { elem.underlying_storage(renderer.glow_renderer_mut()) } @@ -1418,6 +1478,9 @@ where CosmicWindowRenderElement::Header(elem) => { elem.capture_framebuffer(frame, src, dst, cache) } + CosmicWindowRenderElement::HeaderBlur(elem) => { + RenderElement::::capture_framebuffer(elem, frame, src, dst, cache) + } CosmicWindowRenderElement::Shadow(elem) | CosmicWindowRenderElement::Border(elem) => { RenderElement::::capture_framebuffer( elem, diff --git a/src/shell/grabs/menu/item.rs b/src/shell/grabs/menu/item.rs index baa61fde..90614f6b 100644 --- a/src/shell/grabs/menu/item.rs +++ b/src/shell/grabs/menu/item.rs @@ -1,10 +1,12 @@ use cosmic::{ - iced::Element, - iced_core::{ - Background, Border, Clipboard, Color, Event, Layout, Length, Rectangle, - Renderer as IcedRenderer, Shell, Size, layout, mouse, overlay, - renderer::{Quad, Style}, - widget::{Id, Tree, Widget, tree}, + iced::{ + Element, + core::{ + Background, Border, Clipboard, Color, Event, Layout, Length, Rectangle, + Renderer as IcedRenderer, Shell, Size, layout, mouse, overlay, + renderer::{Quad, Style}, + widget::{Id, Tree, Widget, tree}, + }, }, widget::button::Catalog, }; diff --git a/src/shell/grabs/menu/mod.rs b/src/shell/grabs/menu/mod.rs index e79734e9..ec82bb07 100644 --- a/src/shell/grabs/menu/mod.rs +++ b/src/shell/grabs/menu/mod.rs @@ -9,9 +9,11 @@ use std::{ use calloop::LoopHandle; use cosmic::{ Apply as _, Task, - iced::{Alignment, Background}, - iced_core::{Border, Length, Rectangle as IcedRectangle, alignment::Horizontal}, - iced_widget::{self, Column, Row, text::Style as TextStyle}, + iced::{ + Alignment, Background, + core::{Border, Length, Rectangle as IcedRectangle, alignment::Horizontal}, + widget::{self as iced_widget, Column, Row, text::Style as TextStyle}, + }, theme, widget::{button, divider, icon::from_name, space, text}, }; @@ -424,10 +426,11 @@ impl Program for ContextMenu { .width(mode) .class(if *disabled { theme::Text::Custom(|theme| { - let mut color = theme.cosmic().background.component.on; + let mut color = theme.cosmic().background(false).component.on; color.alpha *= 0.5; TextStyle { color: Some(color.into()), + ..Default::default() } }) } else { @@ -442,10 +445,11 @@ impl Program for ContextMenu { .align_x(Horizontal::Right) .width(Length::Shrink) .class(theme::Text::Custom(|theme| { - let mut color = theme.cosmic().background.component.on; + let mut color = theme.cosmic().background(false).component.on; color.alpha *= 0.75; TextStyle { color: Some(color.into()), + ..Default::default() } })) .into(), @@ -470,7 +474,7 @@ impl Program for ContextMenu { .padding(1) .class(theme::Container::custom(|theme| { let cosmic = theme.cosmic(); - let component = &cosmic.background.component; + let component = &cosmic.background(false).component; iced_widget::container::Style { snap: true, icon_color: Some(cosmic.accent.base.into()), diff --git a/src/shell/workspace.rs b/src/shell/workspace.rs index dc5d9f63..ea5dbad7 100644 --- a/src/shell/workspace.rs +++ b/src/shell/workspace.rs @@ -1686,15 +1686,15 @@ impl Workspace { .as_logical() .to_physical_precise_round(output_scale); + let fullscreen_geo = fullscreen.surface.0.geometry(); + let scale = Scale { + x: target_geo.size.w as f64 / fullscreen_geo.size.w as f64, + y: target_geo.size.h as f64 / fullscreen_geo.size.h as f64, + }; + // Only rescale geometry when animating let animation_rescale = |elem| { if fullscreen.is_animating() { - let fullscreen_geo = fullscreen.surface.0.geometry(); - let scale = Scale { - x: target_geo.size.w as f64 / fullscreen_geo.size.w as f64, - y: target_geo.size.h as f64 / fullscreen_geo.size.h as f64, - }; - RescaleRenderElement::from_element(elem, render_loc, scale).into() } else { Into::>::into(elem) diff --git a/src/shell/zoom.rs b/src/shell/zoom.rs index 02d1be55..e0d3f1d1 100644 --- a/src/shell/zoom.rs +++ b/src/shell/zoom.rs @@ -3,8 +3,8 @@ use std::{sync::Mutex, time::Instant}; use calloop::LoopHandle; use cosmic::{ Apply, - iced::{Alignment, Background, Border, Length, alignment::Vertical}, - iced_widget, theme, + iced::{Alignment, Background, Border, Length, alignment::Vertical, widget as iced_widget}, + theme, widget::{self, icon::Named}, }; use cosmic_comp_config::ZoomMovement; @@ -515,7 +515,7 @@ impl Program for ZoomProgram { .padding(8) .class(theme::Container::custom(|theme| { let cosmic = theme.cosmic(); - let component = &cosmic.background.component; + let component = &cosmic.background(false).component; iced_widget::container::Style { snap: true, icon_color: Some(component.on.into()), diff --git a/src/utils/iced/mod.rs b/src/utils/iced/mod.rs index 2ee79733..9d57b46d 100644 --- a/src/utils/iced/mod.rs +++ b/src/utils/iced/mod.rs @@ -12,15 +12,15 @@ use cosmic::{ iced::{ Limits, Point as IcedPoint, Size as IcedSize, Task, advanced::{graphics::text::font_system, widget::Tree}, + core::{Color, Length, Pixels, clipboard::Null as NullClipboard, id::Id, renderer::Style}, event::Event, futures::{FutureExt, StreamExt}, keyboard::{Event as KeyboardEvent, Modifiers as IcedModifiers}, mouse::{Button as MouseButton, Cursor, Event as MouseEvent, ScrollDelta}, + runtime::{Action, task::into_stream}, touch::{Event as TouchEvent, Finger}, window::Event as WindowEvent, }, - iced_core::{Color, Length, Pixels, clipboard::Null as NullClipboard, id::Id, renderer::Style}, - iced_runtime::{Action, task::into_stream}, }; use iced_tiny_skia::{ Layer, diff --git a/src/utils/iced/state.rs b/src/utils/iced/state.rs index db67d8f3..2af3a993 100644 --- a/src/utils/iced/state.rs +++ b/src/utils/iced/state.rs @@ -1,12 +1,16 @@ use super::IcedProgram as Program; -use cosmic::iced::core::event::{self, Event}; -use cosmic::iced::core::mouse; -use cosmic::iced::core::renderer; -use cosmic::iced::core::widget::operation::{self, Operation}; -use cosmic::iced::core::{Clipboard, Size}; -use cosmic::iced_core; -use cosmic::iced_runtime::Task; -use cosmic::iced_runtime::user_interface::{self, UserInterface}; +use cosmic::iced::{ + core::{ + self as iced_core, Clipboard, Size, + event::{self, Event}, + mouse, renderer, + widget::operation::{self, Operation}, + }, + runtime::{ + Task, + user_interface::{self, UserInterface}, + }, +}; /// The execution state of a [`Program`]. It leverages caching, event /// processing, and rendering primitive storage. @@ -128,7 +132,7 @@ where messages.append(&mut self.queued_messages); let task = if messages.is_empty() { - if let cosmic::iced_runtime::user_interface::State::Updated { + if let cosmic::iced::runtime::user_interface::State::Updated { mouse_interaction, .. } = state { @@ -154,7 +158,7 @@ where let mut user_interface = build_user_interface(id, &mut self.program, temp_cache, renderer, bounds); - if let cosmic::iced_runtime::user_interface::State::Updated { + if let cosmic::iced::runtime::user_interface::State::Updated { mouse_interaction, .. } = state { From 5ee2fa911a760d63dbe777a12c061060f873ab7d Mon Sep 17 00:00:00 2001 From: Victoria Brekenfeld Date: Fri, 17 Apr 2026 17:57:48 +0200 Subject: [PATCH 32/92] wayland: Support cosmic-corner-radius v2 --- Cargo.lock | 2 +- src/backend/render/cursor.rs | 2 + src/backend/render/mod.rs | 29 +- src/backend/render/wayland/mod.rs | 7 +- src/shell/element/surface.rs | 30 +- src/wayland/handlers/corner_radius.rs | 109 ++++- src/wayland/protocols/corner_radius.rs | 647 +++++++++++++++++++++---- 7 files changed, 679 insertions(+), 147 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index 36146511..ed27a859 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -906,7 +906,7 @@ dependencies = [ [[package]] name = "cosmic-protocols" version = "0.2.0" -source = "git+https://github.com/pop-os//cosmic-protocols?branch=main#0d0b0b573fe4836ede4c237dadd365033cd28296" +source = "git+https://github.com/pop-os//cosmic-protocols?branch=main#32283d76a8d0342da74c4cc022a533c52dcf378f" dependencies = [ "bitflags 2.11.0", "wayland-backend", diff --git a/src/backend/render/cursor.rs b/src/backend/render/cursor.rs index 360991de..0585e664 100644 --- a/src/backend/render/cursor.rs +++ b/src/backend/render/cursor.rs @@ -212,6 +212,7 @@ pub fn draw_surface_cursor( 1.0, false, [0; 4], + None, blur_strength, Kind::Cursor, &mut |elem| push(elem.into(), h), @@ -247,6 +248,7 @@ pub fn draw_dnd_icon( 1.0, false, [0; 4], + None, blur_strength, FRAME_TIME_FILTER, push, diff --git a/src/backend/render/mod.rs b/src/backend/render/mod.rs index 60917112..ac49af7d 100644 --- a/src/backend/render/mod.rs +++ b/src/backend/render/mod.rs @@ -39,6 +39,7 @@ use crate::{ wayland::{ handlers::{ compositor::FRAME_TIME_FILTER, + corner_radius::{pad_rect, surface_corners, surface_padding}, data_device::get_dnd_icon, image_copy_capture::{FrameHolder, SessionData, render_session}, }, @@ -78,7 +79,7 @@ use smithay::{ utils::{ IsAlive, Logical, Monotonic, Physical, Point, Rectangle, Scale, Size, Time, Transform, }, - wayland::{dmabuf::get_dmabuf, session_lock::LockSurface}, + wayland::{compositor::with_states, dmabuf::get_dmabuf, session_lock::LockSurface}, }; #[cfg(feature = "debug")] @@ -838,6 +839,11 @@ where let mut geometry = popup.geometry().as_global(); geometry.loc += location; + let radii = with_states(popup.wl_surface(), |states| { + surface_corners(states, geometry.size.as_logical()) + }) + .unwrap_or([0; 4]); + push_render_elements_from_surface_tree( renderer, popup.wl_surface(), @@ -849,7 +855,8 @@ where Scale::from(scale), 1.0, false, - [0; 4], + radii, + None, blur_strength, FRAME_TIME_FILTER, &mut |elem| { @@ -868,6 +875,17 @@ where } => { let mut geometry = layer.geometry().as_global(); geometry.loc += location; + let geometry = geometry.to_local(output).as_logical(); + + let padded = with_states(layer.wl_surface(), |states| { + surface_padding(states, geometry.size) + .and_then(|padding| pad_rect(geometry, &padding)) + }) + .unwrap_or(geometry); + let radii = with_states(layer.wl_surface(), |states| { + surface_corners(states, padded.size) + }) + .unwrap_or([0; 4]); push_render_elements_from_surface_tree( renderer, @@ -876,11 +894,12 @@ where .to_local(output) .as_logical() .to_physical_precise_round(scale), - geometry.to_local(output).as_logical().to_f64(), + geometry.to_f64(), Scale::from(scale), 1.0, false, - [0; 4], + radii, + padded.to_f64(), blur_strength, FRAME_TIME_FILTER, &mut |elem| { @@ -909,6 +928,7 @@ where 1.0, false, [0; 4], + None, blur_strength, FRAME_TIME_FILTER, &mut |elem| elements.extend(crop_to_output(elem.into()).map(Into::into)), @@ -1056,6 +1076,7 @@ fn session_lock_elements( 1.0, false, [0; 4], + None, 0, FRAME_TIME_FILTER, push, diff --git a/src/backend/render/wayland/mod.rs b/src/backend/render/wayland/mod.rs index 9ac1109c..9f07d08b 100644 --- a/src/backend/render/wayland/mod.rs +++ b/src/backend/render/wayland/mod.rs @@ -35,6 +35,7 @@ pub fn push_render_elements_from_surface_tree( alpha: f32, should_clip: bool, radii: [u8; 4], + blur_geometry: impl Into>>, blur_strength: usize, kind: impl Into, push_above: &mut dyn FnMut(SurfaceRenderElement), @@ -44,7 +45,8 @@ pub fn push_render_elements_from_surface_tree( R::TextureId: Clone + 'static, { let location = location.into().to_f64(); - let geometry = geometry.into().to_f64(); + let geometry = geometry.into(); + let blur_geometry = blur_geometry.into(); let scale = scale.into(); let kind = kind.into(); let mut passed_main = false; @@ -87,10 +89,11 @@ pub fn push_render_elements_from_surface_tree( renderer, surface, states, location, alpha, kind, ) { Ok(Some(surface)) => { + let blur_geo = blur_geometry.unwrap_or(geometry); blur = BlurElement::from_surface( renderer, states, - geometry, + blur_geo, scale.x, radii, blur_strength, diff --git a/src/shell/element/surface.rs b/src/shell/element/surface.rs index 4a88d2df..4db3e60a 100644 --- a/src/shell/element/surface.rs +++ b/src/shell/element/surface.rs @@ -4,9 +4,7 @@ use crate::{ wayland::{SurfaceRenderElement, push_render_elements_from_surface_tree}, }, shell::focus::target::PointerFocusTarget, - wayland::{ - handlers::compositor::frame_time_filter_fn, protocols::corner_radius::CacheableCorners, - }, + wayland::handlers::{compositor::frame_time_filter_fn, corner_radius::surface_corners}, }; use std::{ borrow::Cow, @@ -193,22 +191,7 @@ impl CosmicSurface { pub fn corner_radius(&self, geometry_size: Size) -> Option<[u8; 4]> { self.wl_surface().and_then(|surface| { - with_states(&surface, |states| { - let mut guard = states.cached_state.get::(); - - // guard against corner radius being too large, potentially disconnecting the outline - let half_min_dim = - u8::try_from(geometry_size.w.min(geometry_size.h) / 2).unwrap_or(u8::MAX); - - let corners = guard.current().0?; - - Some([ - corners.top_left.min(half_min_dim), - corners.top_right.min(half_min_dim), - corners.bottom_right.min(half_min_dim), - corners.bottom_left.min(half_min_dim), - ]) - }) + with_states(&surface, |states| surface_corners(states, geometry_size)) }) } @@ -880,6 +863,10 @@ impl CosmicSurface { .to_physical_precise_round(scale); let mut geometry = popup.geometry().to_f64(); geometry.loc += location.to_f64().to_logical(scale) + popup_offset.to_f64(); + let radii = with_states(popup.wl_surface(), |states| { + surface_corners(states, geometry.size.to_i32_round()) + }) + .unwrap_or([0; 4]); push_render_elements_from_surface_tree( renderer, @@ -889,7 +876,8 @@ impl CosmicSurface { scale, alpha, false, - [0; 4], + radii, + None, blur_strength, scanout_kind_eval(None, scanout_node), push, @@ -934,6 +922,7 @@ impl CosmicSurface { alpha, should_clip, radii, + None, blur_strength, scanout_kind_eval(scanout_override, scanout_node), push_above, @@ -954,6 +943,7 @@ impl CosmicSurface { alpha, should_clip, radii, + None, blur_strength, scanout_kind_eval(scanout_override, scanout_node), push_above, diff --git a/src/wayland/handlers/corner_radius.rs b/src/wayland/handlers/corner_radius.rs index dbcae2dc..140b86ff 100644 --- a/src/wayland/handlers/corner_radius.rs +++ b/src/wayland/handlers/corner_radius.rs @@ -1,7 +1,9 @@ -use cosmic_protocols::corner_radius::v1::server::cosmic_corner_radius_toplevel_v1; +use smithay::utils::{Logical, Rectangle, Size}; +use smithay::wayland::compositor::SurfaceData; use crate::wayland::protocols::corner_radius::{ - CornerRadiusData, CornerRadiusHandler, CornerRadiusState, delegate_corner_radius, + CacheableCorners, CacheablePadding, CornerRadiusData, CornerRadiusHandler, CornerRadiusState, + CornerRadiusSurface, delegate_corner_radius, }; use crate::state::State; @@ -11,21 +13,25 @@ impl CornerRadiusHandler for State { &mut self.common.corner_radius_state } - fn set_corner_radius( - &mut self, - _: &cosmic_corner_radius_toplevel_v1::CosmicCornerRadiusToplevelV1, - data: &CornerRadiusData, - ) { + fn set_corner_radius(&mut self, data: &CornerRadiusData) { if force_redraw(self, data).is_none() { tracing::warn!("Failed to force redraw for corner radius change."); } } - fn unset_corner_radius( - &mut self, - _: &cosmic_corner_radius_toplevel_v1::CosmicCornerRadiusToplevelV1, - data: &CornerRadiusData, - ) { + fn unset_corner_radius(&mut self, data: &CornerRadiusData) { + if force_redraw(self, data).is_none() { + tracing::warn!("Failed to force redraw for corner radius reset."); + } + } + + fn set_padding(&mut self, data: &CornerRadiusData) { + if force_redraw(self, data).is_none() { + tracing::warn!("Failed to force redraw for corner radius change."); + } + } + + fn unset_padding(&mut self, data: &CornerRadiusData) { if force_redraw(self, data).is_none() { tracing::warn!("Failed to force redraw for corner radius reset."); } @@ -34,16 +40,83 @@ impl CornerRadiusHandler for State { fn force_redraw(state: &mut State, data: &CornerRadiusData) -> Option<()> { let guard = data.lock().unwrap(); + let shell = state.common.shell.read(); - let toplevel = guard.toplevel.upgrade().ok()?; - - let surface = state.common.xdg_shell_state.get_toplevel(&toplevel)?; - - let guard = state.common.shell.read(); - let output = guard.visible_output_for_surface(surface.wl_surface())?; + let output = match &guard.surface { + CornerRadiusSurface::Toplevel(toplevel) => { + let toplevel = toplevel.upgrade().ok()?; + let surface = state.common.xdg_shell_state.get_toplevel(&toplevel)?; + shell.visible_output_for_surface(surface.wl_surface())? + } + CornerRadiusSurface::Popup(popup) => { + let popup = popup.upgrade().ok()?; + let surface = state.common.xdg_shell_state.get_popup(&popup)?; + shell.visible_output_for_surface(surface.wl_surface())? + } + CornerRadiusSurface::Layer(layer) => { + let layer = layer.upgrade().ok()?; + let surface = state + .common + .layer_shell_state + .layer_surfaces() + .find(|l| l.shell_surface() == &layer)?; + shell.visible_output_for_surface(surface.wl_surface())? + } + }; state.backend.schedule_render(output); Some(()) } +pub fn surface_corners(states: &SurfaceData, size: Size) -> Option<[u8; 4]> { + let mut guard = states.cached_state.get::(); + + // guard against corner radius being too large, potentially disconnecting the outline + let half_min_dim = u8::try_from(size.w.min(size.h) / 2).unwrap_or(u8::MAX); + let corners = guard.current().0?; + + Some([ + u8::try_from(corners.top_left) + .unwrap_or(u8::MAX) + .min(half_min_dim), + u8::try_from(corners.top_right) + .unwrap_or(u8::MAX) + .min(half_min_dim), + u8::try_from(corners.bottom_right) + .unwrap_or(u8::MAX) + .min(half_min_dim), + u8::try_from(corners.bottom_left) + .unwrap_or(u8::MAX) + .min(half_min_dim), + ]) +} + +pub fn surface_padding(states: &SurfaceData, size: Size) -> Option<[i32; 4]> { + let mut guard = states.cached_state.get::(); + + // guard against padding being too large + let half_min_dim = size.w.min(size.h) / 2; + let padding = guard.current().0?; + + Some([ + padding.top.min(half_min_dim), + padding.right.min(half_min_dim), + padding.bottom.min(half_min_dim), + padding.left.min(half_min_dim), + ]) +} + +pub fn pad_rect( + mut rect: Rectangle, + padding: &[i32; 4], +) -> Option> { + rect.size.h = rect.size.h.checked_sub(padding[0])?; + rect.loc.x += padding[0]; + rect.size.w = rect.size.w.checked_sub(padding[1])?; + rect.size.h = rect.size.h.checked_sub(padding[2])?; + rect.size.w = rect.size.w.checked_sub(padding[3])?; + rect.loc.y += padding[3]; + Some(rect) +} + delegate_corner_radius!(State); diff --git a/src/wayland/protocols/corner_radius.rs b/src/wayland/protocols/corner_radius.rs index 5baafa23..f0edf343 100644 --- a/src/wayland/protocols/corner_radius.rs +++ b/src/wayland/protocols/corner_radius.rs @@ -1,12 +1,21 @@ +use cosmic_protocols::corner_radius::v1::server::cosmic_corner_radius_layer_v1::{ + self, CosmicCornerRadiusLayerV1, +}; use cosmic_protocols::corner_radius::v1::server::cosmic_corner_radius_toplevel_v1::CosmicCornerRadiusToplevelV1; use cosmic_protocols::corner_radius::v1::server::{ cosmic_corner_radius_manager_v1, cosmic_corner_radius_toplevel_v1, }; -use smithay::utils::HookId; +use smithay::desktop::utils::bbox_from_surface_tree; +use smithay::reexports::wayland_protocols::xdg::shell::server::xdg_popup::XdgPopup; +use smithay::reexports::wayland_protocols_wlr::layer_shell::v1::server::zwlr_layer_surface_v1::ZwlrLayerSurfaceV1; +use smithay::reexports::wayland_server::New; +use smithay::reexports::wayland_server::protocol::wl_surface::WlSurface; +use smithay::utils::{HookId, Logical, Point, Rectangle}; use smithay::wayland::compositor::Cacheable; use smithay::wayland::compositor::add_pre_commit_hook; use smithay::wayland::compositor::with_states; -use smithay::wayland::shell::xdg::SurfaceCachedState; +use smithay::wayland::shell::wlr_layer::WlrLayerShellHandler; +use smithay::wayland::shell::xdg::{SurfaceCachedState, XdgShellSurfaceUserData}; use smithay::{ reexports::{ wayland_protocols::xdg::shell::server::xdg_toplevel::XdgToplevel, @@ -18,6 +27,7 @@ use std::sync::Mutex; use wayland_backend::server::GlobalId; type ToplevelHookId = Mutex)>>; +type LayerHookId = Mutex)>>; #[derive(Debug)] pub struct CornerRadiusState { @@ -33,12 +43,13 @@ impl CornerRadiusState { + Dispatch< cosmic_corner_radius_toplevel_v1::CosmicCornerRadiusToplevelV1, CornerRadiusData, - > + CornerRadiusHandler + > + Dispatch + + CornerRadiusHandler + 'static, { let global = dh .create_global::( - 1, + 2, (), ); CornerRadiusState { @@ -52,18 +63,12 @@ impl CornerRadiusState { } } -pub trait CornerRadiusHandler: XdgShellHandler { +pub trait CornerRadiusHandler: XdgShellHandler + WlrLayerShellHandler { fn corner_radius_state(&mut self) -> &mut CornerRadiusState; - fn set_corner_radius( - &mut self, - toplevel: &cosmic_corner_radius_toplevel_v1::CosmicCornerRadiusToplevelV1, - data: &CornerRadiusData, - ); - fn unset_corner_radius( - &mut self, - toplevel: &cosmic_corner_radius_toplevel_v1::CosmicCornerRadiusToplevelV1, - data: &CornerRadiusData, - ); + fn set_corner_radius(&mut self, data: &CornerRadiusData); + fn unset_corner_radius(&mut self, data: &CornerRadiusData); + fn set_padding(&mut self, data: &CornerRadiusData); + fn unset_padding(&mut self, data: &CornerRadiusData); } impl GlobalDispatch @@ -72,6 +77,7 @@ where D: GlobalDispatch + Dispatch + Dispatch + + Dispatch + CornerRadiusHandler + 'static, { @@ -96,6 +102,7 @@ where D: GlobalDispatch + Dispatch + Dispatch + + Dispatch + CornerRadiusHandler + 'static, { @@ -115,6 +122,71 @@ where } cosmic_corner_radius_manager_v1::Request::GetCornerRadius { id, toplevel } => { if let Some(surface) = state.xdg_shell_state().get_toplevel(&toplevel) { + new_xdg( + surface.wl_surface(), + CornerRadiusSurface::Toplevel(surface.xdg_toplevel().downgrade()), + resource, + data_init, + id, + ) + } // TODO: can this fail? + } + cosmic_corner_radius_manager_v1::Request::GetCornerRadiusSurface { id, surface } => { + if let Some(toplevel) = + state + .xdg_shell_state() + .toplevel_surfaces() + .iter() + .find(|toplevel| { + toplevel + .xdg_toplevel() + .data::() + .unwrap() + .xdg_surface() + == &surface + }) + { + new_xdg( + toplevel.wl_surface(), + CornerRadiusSurface::Toplevel(toplevel.xdg_toplevel().downgrade()), + resource, + data_init, + id, + ) + } else if let Some(popup) = + state + .xdg_shell_state() + .popup_surfaces() + .iter() + .find(|popup| { + popup + .xdg_popup() + .data::() + .unwrap() + .xdg_surface() + == &surface + }) + { + new_xdg( + popup.wl_surface(), + CornerRadiusSurface::Popup(popup.xdg_popup().downgrade()), + resource, + data_init, + id, + ) + } else { + resource.post_error( + cosmic_corner_radius_manager_v1::Error::NoRole as u32, + "xdg_surface has no known role object".to_string(), + ); + } + } + cosmic_corner_radius_manager_v1::Request::GetCornerRadiusLayer { id, layer } => { + if let Some(surface) = state + .shell_state() + .layer_surfaces() + .find(|surface| surface.shell_surface() == &layer) + { let radius_exists = with_states(surface.wl_surface(), |surface_data| { let hook_id = surface_data .data_map @@ -124,63 +196,33 @@ where }); if radius_exists.unwrap_or_default() { resource.post_error( - cosmic_corner_radius_manager_v1::Error::CornerRadiusExists as u32, - format!("{resource:?} CosmicCornerRadiusToplevelV1 object already exists for the surface"), - ); + cosmic_corner_radius_manager_v1::Error::CornerRadiusExists as u32, + format!( + "{resource:?} CosmicCornerRadiusToplevelV1 object already exists for the surface" + ), + ); } let data = Mutex::new(CornerRadiusInternal { - toplevel: toplevel.downgrade(), + surface: CornerRadiusSurface::Layer(layer.downgrade()), corners: None, + padding: None, }); let obj = data_init.init(id, data); let obj_downgrade = obj.downgrade(); let needs_hook = radius_exists.is_none(); if needs_hook { - let hook_id = add_pre_commit_hook::( - surface.wl_surface(), - move |_, _dh, surface| { - let corner_radii_too_big = with_states(surface, |surface_data| { - let corners = *surface_data - .cached_state - .get::() - .pending(); - surface_data - .cached_state - .get::() - .pending() - .geometry - .zip(corners.0.as_ref()) - .is_some_and(|(geo, corners)| { - let half_min_dim = - u8::try_from(geo.size.w.min(geo.size.h) / 2) - .unwrap_or(u8::MAX); - corners.top_right > half_min_dim - || corners.top_left > half_min_dim - || corners.bottom_right > half_min_dim - || corners.bottom_left > half_min_dim - }) - }); - - if corner_radii_too_big { - obj.post_error( - cosmic_corner_radius_toplevel_v1::Error::RadiusTooLarge - as u32, - format!("{obj:?} corner radius too large"), - ); - } - }, - ); - + let hook_id = + add_pre_commit_hook::(surface.wl_surface(), layer_radius_hook); with_states(surface.wl_surface(), |surface_data| { let hook_ids = surface_data .data_map - .get_or_insert_threadsafe(|| ToplevelHookId::new(None)); + .get_or_insert_threadsafe(|| LayerHookId::new(None)); let mut guard = hook_ids.lock().unwrap(); *guard = Some((hook_id, obj_downgrade)); }); } - } + } // TODO: can this fail? } _ => unimplemented!(), } @@ -197,6 +239,55 @@ where } } +fn new_xdg( + wl_surface: &WlSurface, + surface: CornerRadiusSurface, + resource: &cosmic_corner_radius_manager_v1::CosmicCornerRadiusManagerV1, + data_init: &mut smithay::reexports::wayland_server::DataInit<'_, D>, + id: New, +) where + D: GlobalDispatch + + Dispatch + + Dispatch + + CornerRadiusHandler + + 'static, +{ + let radius_exists = with_states(wl_surface, |surface_data| { + let hook_id = surface_data + .data_map + .get_or_insert_threadsafe(|| ToplevelHookId::new(None)); + let guard = hook_id.lock().unwrap(); + guard.as_ref().map(|(_, t)| t.upgrade().is_ok()) + }); + if radius_exists.unwrap_or_default() { + resource.post_error( + cosmic_corner_radius_manager_v1::Error::CornerRadiusExists as u32, + format!( + "{resource:?} CosmicCornerRadiusToplevelV1 object already exists for the surface" + ), + ); + } + let data = Mutex::new(CornerRadiusInternal { + surface, + corners: None, + padding: None, + }); + let obj = data_init.init(id, data); + let obj_downgrade = obj.downgrade(); + + let needs_hook = radius_exists.is_none(); + if needs_hook { + let hook_id = add_pre_commit_hook::(wl_surface, xdg_radius_hook); + with_states(wl_surface, |surface_data| { + let hook_ids = surface_data + .data_map + .get_or_insert_threadsafe(|| ToplevelHookId::new(None)); + let mut guard = hook_ids.lock().unwrap(); + *guard = Some((hook_id, obj_downgrade)); + }); + } +} + impl Dispatch for CornerRadiusState @@ -221,30 +312,35 @@ where let mut guard = data.lock().unwrap(); guard.corners = None; - let Ok(toplevel) = guard.toplevel.upgrade() else { + let Some(wl_surface) = (match &guard.surface { + CornerRadiusSurface::Toplevel(toplevel) => toplevel + .upgrade() + .ok() + .and_then(|toplevel| state.xdg_shell_state().get_toplevel(&toplevel)) + .map(|toplevel| toplevel.wl_surface().clone()), + CornerRadiusSurface::Popup(popup) => popup + .upgrade() + .ok() + .and_then(|popup| state.xdg_shell_state().get_popup(&popup)) + .map(|popup| popup.wl_surface().clone()), + CornerRadiusSurface::Layer(_) => unreachable!(), + }) else { return; }; - if let Some(surface) = state.xdg_shell_state().get_toplevel(&toplevel) { - with_states(surface.wl_surface(), |surface_data| { - if let Some(hook_ids_mutex) = surface_data.data_map.get::() - { - let mut hook_id = hook_ids_mutex.lock().unwrap(); - *hook_id = None; - } - }); - } + with_states(&wl_surface, |surface_data| { + if let Some(hook_ids_mutex) = surface_data.data_map.get::() { + let mut hook_id = hook_ids_mutex.lock().unwrap(); + *hook_id = None; + } - if let Some(surface) = state.xdg_shell_state().get_toplevel(&toplevel) { - with_states(surface.wl_surface(), |s| { - let mut cached = s.cached_state.get::(); - let pending = cached.pending(); - *pending = CacheableCorners(None); - }); - } + let mut cached = surface_data.cached_state.get::(); + let pending = cached.pending(); + *pending = CacheableCorners(None); + }); drop(guard); - state.unset_corner_radius(resource, data); + state.unset_corner_radius(data); } cosmic_corner_radius_toplevel_v1::Request::SetRadius { top_left, @@ -254,66 +350,388 @@ where } => { let mut guard = data.lock().unwrap(); guard.set_corner_radius(top_left, top_right, bottom_right, bottom_left); - let Ok(toplevel) = guard.toplevel.upgrade() else { + + let Some(wl_surface) = (match &guard.surface { + CornerRadiusSurface::Toplevel(toplevel) => toplevel + .upgrade() + .ok() + .and_then(|toplevel| state.xdg_shell_state().get_toplevel(&toplevel)) + .map(|toplevel| toplevel.wl_surface().clone()), + CornerRadiusSurface::Popup(popup) => popup + .upgrade() + .ok() + .and_then(|popup| state.xdg_shell_state().get_popup(&popup)) + .map(|popup| popup.wl_surface().clone()), + CornerRadiusSurface::Layer(_) => unreachable!(), + }) else { resource.post_error( cosmic_corner_radius_toplevel_v1::Error::ToplevelDestroyed as u32, format!("{:?} No toplevel found", resource), ); return; }; - - if let Some(surface) = state.xdg_shell_state().get_toplevel(&toplevel) { - with_states(surface.wl_surface(), |s| { - let mut cached = s.cached_state.get::(); - let pending = cached.pending(); - *pending = CacheableCorners(guard.corners); - }); - } + with_states(&wl_surface, |s| { + let mut cached = s.cached_state.get::(); + let pending = cached.pending(); + *pending = CacheableCorners(guard.corners); + }); drop(guard); - state.set_corner_radius(resource, data); + state.set_corner_radius(data); } cosmic_corner_radius_toplevel_v1::Request::UnsetRadius => { let mut guard = data.lock().unwrap(); guard.corners = None; - let Ok(toplevel) = guard.toplevel.upgrade() else { + + let Some(wl_surface) = (match &guard.surface { + CornerRadiusSurface::Toplevel(toplevel) => toplevel + .upgrade() + .ok() + .and_then(|toplevel| state.xdg_shell_state().get_toplevel(&toplevel)) + .map(|toplevel| toplevel.wl_surface().clone()), + CornerRadiusSurface::Popup(popup) => popup + .upgrade() + .ok() + .and_then(|popup| state.xdg_shell_state().get_popup(&popup)) + .map(|popup| popup.wl_surface().clone()), + CornerRadiusSurface::Layer(_) => unreachable!(), + }) else { resource.post_error( cosmic_corner_radius_toplevel_v1::Error::ToplevelDestroyed as u32, format!("{:?} No toplevel found", resource), ); + return; }; - if let Some(surface) = state.xdg_shell_state().get_toplevel(&toplevel) { - with_states(surface.wl_surface(), |s| { - let mut cached = s.cached_state.get::(); - let pending = cached.pending(); - *pending = CacheableCorners(None); - }); - } + with_states(&wl_surface, |s| { + let mut cached = s.cached_state.get::(); + let pending = cached.pending(); + *pending = CacheableCorners(None); + }); drop(guard); - state.unset_corner_radius(resource, data); + state.unset_corner_radius(data); } _ => unimplemented!(), } } } +impl Dispatch + for CornerRadiusState +where + D: GlobalDispatch + + Dispatch + + Dispatch + + CornerRadiusHandler + + 'static, +{ + fn request( + state: &mut D, + _client: &Client, + resource: &cosmic_corner_radius_layer_v1::CosmicCornerRadiusLayerV1, + request: ::Request, + data: &CornerRadiusData, + _dhandle: &DisplayHandle, + _data_init: &mut smithay::reexports::wayland_server::DataInit<'_, D>, + ) { + match request { + cosmic_corner_radius_layer_v1::Request::Destroy => { + let mut guard = data.lock().unwrap(); + guard.corners = None; + + let CornerRadiusSurface::Layer(layer_surface) = &guard.surface else { + unreachable!("corner_radius_layer without layer shell?"); + }; + let Some(layer_surface) = layer_surface.upgrade().ok().and_then(|layer| { + state + .shell_state() + .layer_surfaces() + .find(|s| s.shell_surface() == &layer) + }) else { + resource.post_error( + cosmic_corner_radius_layer_v1::Error::LayerDestroyed as u32, + format!("{:?} No layer found", resource), + ); + return; + }; + + with_states(layer_surface.wl_surface(), |surface_data| { + if let Some(hook_ids_mutex) = surface_data.data_map.get::() { + let mut hook_id = hook_ids_mutex.lock().unwrap(); + *hook_id = None; + } + + let mut cached = surface_data.cached_state.get::(); + let pending = cached.pending(); + *pending = CacheableCorners(None); + + let mut cached = surface_data.cached_state.get::(); + let pending = cached.pending(); + *pending = CacheablePadding(None); + }); + drop(guard); + + state.unset_corner_radius(data); + state.unset_padding(data); + } + cosmic_corner_radius_layer_v1::Request::SetRadius { + top_left, + top_right, + bottom_right, + bottom_left, + } => { + let mut guard = data.lock().unwrap(); + guard.set_corner_radius(top_left, top_right, bottom_right, bottom_left); + + let CornerRadiusSurface::Layer(layer_surface) = &guard.surface else { + unreachable!("corner_radius_layer without layer shell?"); + }; + let Some(layer_surface) = layer_surface.upgrade().ok().and_then(|layer| { + state + .shell_state() + .layer_surfaces() + .find(|s| s.shell_surface() == &layer) + }) else { + resource.post_error( + cosmic_corner_radius_layer_v1::Error::LayerDestroyed as u32, + format!("{:?} No layer found", resource), + ); + return; + }; + + with_states(layer_surface.wl_surface(), |s| { + let mut cached = s.cached_state.get::(); + let pending = cached.pending(); + *pending = CacheableCorners(guard.corners); + }); + drop(guard); + + state.set_corner_radius(data); + } + cosmic_corner_radius_layer_v1::Request::UnsetRadius => { + let mut guard = data.lock().unwrap(); + guard.corners = None; + + let CornerRadiusSurface::Layer(layer_surface) = &guard.surface else { + unreachable!("corner_radius_layer without layer shell?"); + }; + let Some(layer_surface) = layer_surface.upgrade().ok().and_then(|layer| { + state + .shell_state() + .layer_surfaces() + .find(|s| s.shell_surface() == &layer) + }) else { + resource.post_error( + cosmic_corner_radius_layer_v1::Error::LayerDestroyed as u32, + format!("{:?} No layer found", resource), + ); + return; + }; + + with_states(layer_surface.wl_surface(), |s| { + let mut cached = s.cached_state.get::(); + let pending = cached.pending(); + *pending = CacheableCorners(None); + }); + drop(guard); + + state.unset_corner_radius(data); + } + cosmic_corner_radius_layer_v1::Request::SetPadding { + top, + right, + bottom, + left, + } => { + let mut guard = data.lock().unwrap(); + guard.set_padding(top, right, bottom, left); + + let CornerRadiusSurface::Layer(layer_surface) = &guard.surface else { + unreachable!("corner_radius_layer without layer shell?"); + }; + let Some(layer_surface) = layer_surface.upgrade().ok().and_then(|layer| { + state + .shell_state() + .layer_surfaces() + .find(|s| s.shell_surface() == &layer) + }) else { + resource.post_error( + cosmic_corner_radius_layer_v1::Error::LayerDestroyed as u32, + format!("{:?} No layer found", resource), + ); + return; + }; + + with_states(layer_surface.wl_surface(), |s| { + let mut cached = s.cached_state.get::(); + let pending = cached.pending(); + *pending = CacheablePadding(guard.padding); + }); + drop(guard); + + state.set_padding(data); + } + cosmic_corner_radius_layer_v1::Request::UnsetPadding => { + let mut guard = data.lock().unwrap(); + guard.corners = None; + + let CornerRadiusSurface::Layer(layer_surface) = &guard.surface else { + unreachable!("corner_radius_layer without layer shell?"); + }; + let Some(layer_surface) = layer_surface.upgrade().ok().and_then(|layer| { + state + .shell_state() + .layer_surfaces() + .find(|s| s.shell_surface() == &layer) + }) else { + resource.post_error( + cosmic_corner_radius_layer_v1::Error::LayerDestroyed as u32, + format!("{:?} No layer found", resource), + ); + return; + }; + + with_states(layer_surface.wl_surface(), |s| { + let mut cached = s.cached_state.get::(); + let pending = cached.pending(); + *pending = CacheablePadding(None); + }); + drop(guard); + + state.unset_padding(data); + } + _ => unimplemented!(), + } + } +} + +fn xdg_radius_hook(_state: &mut D, _dh: &DisplayHandle, surface: &WlSurface) { + with_states(surface, |surface_data| { + let corners = *surface_data + .cached_state + .get::() + .pending(); + if surface_data + .cached_state + .get::() + .pending() + .geometry + .zip(corners.0.as_ref()) + .is_some_and(|(geo, corners)| { + let half_min_dim = (geo.size.w.min(geo.size.h) / 2) as u32; + corners.top_right > half_min_dim + || corners.top_left > half_min_dim + || corners.bottom_right > half_min_dim + || corners.bottom_left > half_min_dim + }) + && let Some(hook) = surface_data.data_map.get::() + { + let hook_ref = hook.lock().unwrap(); + if let Some((_, obj)) = hook_ref.as_ref() + && let Ok(obj) = obj.upgrade() + { + obj.post_error( + cosmic_corner_radius_toplevel_v1::Error::RadiusTooLarge as u32, + format!("{obj:?} corner radius too large"), + ); + } + } + }); +} + +fn pad_rect( + mut rect: Rectangle, + padding: &Padding, +) -> Option> { + rect.size.h = rect.size.h.checked_sub(padding.top)?; + rect.loc.x += padding.top; + rect.size.w = rect.size.w.checked_sub(padding.left)?; + rect.loc.y += padding.left; + rect.size.h = rect.size.h.checked_sub(padding.bottom)?; + rect.size.w = rect.size.w.checked_sub(padding.right)?; + Some(rect) +} + +fn layer_radius_hook(_state: &mut D, _dh: &DisplayHandle, surface: &WlSurface) { + let bbox = bbox_from_surface_tree(surface, Point::default()); + with_states(surface, |surface_data| { + let corners = *surface_data + .cached_state + .get::() + .pending(); + let padding = *surface_data + .cached_state + .get::() + .pending(); + let empty = Padding::default(); + let Some(padded_box) = pad_rect(bbox, padding.0.as_ref().unwrap_or(&empty)) else { + if let Some(hook) = surface_data.data_map.get::() { + let hook_ref = hook.lock().unwrap(); + if let Some((_, obj)) = hook_ref.as_ref() + && let Ok(obj) = obj.upgrade() + { + obj.post_error( + cosmic_corner_radius_layer_v1::Error::PaddingTooLarge as u32, + format!("{obj:?} padding too large"), + ); + } + } + return; + }; + + if corners.0.as_ref().is_some_and(|corners| { + let half_min_dim = (padded_box.size.w.min(padded_box.size.h) / 2) as u32; + corners.top_right > half_min_dim + || corners.top_left > half_min_dim + || corners.bottom_right > half_min_dim + || corners.bottom_left > half_min_dim + }) && let Some(hook) = surface_data.data_map.get::() + { + let hook_ref = hook.lock().unwrap(); + if let Some((_, obj)) = hook_ref.as_ref() + && let Ok(obj) = obj.upgrade() + { + obj.post_error( + cosmic_corner_radius_layer_v1::Error::RadiusTooLarge as u32, + format!("{obj:?} corner radius too large"), + ); + } + } + }); +} + pub type CornerRadiusData = Mutex; +#[derive(Debug)] +pub enum CornerRadiusSurface { + Toplevel(Weak), + Popup(Weak), + Layer(Weak), +} + #[derive(Debug)] pub struct CornerRadiusInternal { - pub toplevel: Weak, + pub surface: CornerRadiusSurface, pub corners: Option, + pub padding: Option, } #[derive(Debug, Copy, Clone)] pub struct Corners { - pub top_left: u8, - pub top_right: u8, - pub bottom_right: u8, - pub bottom_left: u8, + pub top_left: u32, + pub top_right: u32, + pub bottom_right: u32, + pub bottom_left: u32, +} + +#[derive(Debug, Copy, Clone, Default)] +pub struct Padding { + pub top: i32, + pub right: i32, + pub bottom: i32, + pub left: i32, } #[derive(Default, Debug, Copy, Clone)] @@ -328,6 +746,18 @@ impl Cacheable for CacheableCorners { } } +#[derive(Default, Debug, Copy, Clone)] +pub struct CacheablePadding(pub Option); + +impl Cacheable for CacheablePadding { + fn commit(&mut self, _dh: &DisplayHandle) -> Self { + *self + } + fn merge_into(self, into: &mut Self, _dh: &DisplayHandle) { + *into = self; + } +} + impl CornerRadiusInternal { fn set_corner_radius( &mut self, @@ -337,13 +767,23 @@ impl CornerRadiusInternal { bottom_left: u32, ) { let corners = Corners { - top_left: top_left.clamp(u8::MIN as u32, u8::MAX as u32) as u8, - top_right: top_right.clamp(u8::MIN as u32, u8::MAX as u32) as u8, - bottom_right: bottom_right.clamp(u8::MIN as u32, u8::MAX as u32) as u8, - bottom_left: bottom_left.clamp(u8::MIN as u32, u8::MAX as u32) as u8, + top_left, + top_right, + bottom_right, + bottom_left, }; self.corners = Some(corners); } + + fn set_padding(&mut self, top: i32, right: i32, bottom: i32, left: i32) { + let padding = Padding { + top, + right, + bottom, + left, + }; + self.padding = Some(padding); + } } macro_rules! delegate_corner_radius { @@ -357,6 +797,9 @@ macro_rules! delegate_corner_radius { smithay::reexports::wayland_server::delegate_dispatch!($(@< $( $lt $( : $clt $(+ $dlt )* )? ),+ >)? $ty: [ cosmic_protocols::corner_radius::v1::server::cosmic_corner_radius_toplevel_v1::CosmicCornerRadiusToplevelV1: CornerRadiusData ] => $crate::wayland::protocols::corner_radius::CornerRadiusState); + smithay::reexports::wayland_server::delegate_dispatch!($(@< $( $lt $( : $clt $(+ $dlt )* )? ),+ >)? $ty: [ + cosmic_protocols::corner_radius::v1::server::cosmic_corner_radius_layer_v1::CosmicCornerRadiusLayerV1: CornerRadiusData + ] => $crate::wayland::protocols::corner_radius::CornerRadiusState); }; } pub(crate) use delegate_corner_radius; From ab9f13b36f7fd5ba5378e7d6c4d55ebfe3639d46 Mon Sep 17 00:00:00 2001 From: Victoria Brekenfeld Date: Tue, 21 Apr 2026 18:16:43 +0200 Subject: [PATCH 33/92] iced: Handle blur internally --- src/backend/render/element.rs | 8 ++-- src/shell/element/mod.rs | 12 +++--- src/utils/iced/mod.rs | 69 ++++++++++++++++++++++++++++++++--- 3 files changed, 74 insertions(+), 15 deletions(-) diff --git a/src/backend/render/element.rs b/src/backend/render/element.rs index 103fa52f..51d282c1 100644 --- a/src/backend/render/element.rs +++ b/src/backend/render/element.rs @@ -1,6 +1,7 @@ use crate::{ backend::render::{GlMultiError, wayland::SurfaceRenderElement}, shell::{CosmicMappedRenderElement, WorkspaceRenderElement}, + utils::iced::IcedRenderElement, }; #[cfg(feature = "debug")] @@ -12,7 +13,6 @@ use smithay::{ Bind, Blit, ExportMem, ImportAll, ImportMem, Offscreen, Renderer, element::{ Element, Id, Kind, RenderElement, UnderlyingStorage, - memory::MemoryRenderBufferRenderElement, utils::{CropRenderElement, Relocate, RelocateRenderElement, RescaleRenderElement}, }, gles::{GlesError, GlesRenderbuffer, GlesTexture, element::TextureShaderElement}, @@ -42,7 +42,7 @@ where Postprocess( CropRenderElement>>, ), - Zoom(MemoryRenderBufferRenderElement), + Zoom(IcedRenderElement), Damage(DamageElement), #[cfg(feature = "debug")] Egui(TextureRenderElement), @@ -344,13 +344,13 @@ where } } -impl From> for CosmicElement +impl From> for CosmicElement where R: AsGlowRenderer, R::TextureId: 'static, CosmicMappedRenderElement: RenderElement, { - fn from(value: MemoryRenderBufferRenderElement) -> Self { + fn from(value: IcedRenderElement) -> Self { Self::Zoom(value) } } diff --git a/src/shell/element/mod.rs b/src/shell/element/mod.rs index 11861591..f5691b29 100644 --- a/src/shell/element/mod.rs +++ b/src/shell/element/mod.rs @@ -1,7 +1,10 @@ use crate::{ backend::render::element::AsGlowRenderer, state::State, - utils::{iced::IcedElementInternal, prelude::*}, + utils::{ + iced::{IcedElementInternal, IcedRenderElement}, + prelude::*, + }, }; use calloop::LoopHandle; use cosmic_comp_config::AppearanceConfig; @@ -13,7 +16,6 @@ use smithay::{ renderer::{ element::{ Element, Kind, RenderElement, UnderlyingStorage, - memory::MemoryRenderBufferRenderElement, utils::{CropRenderElement, RelocateRenderElement, RescaleRenderElement}, }, gles::element::PixelShaderElement, @@ -1127,7 +1129,7 @@ where GrabbedWindow(RescaleRenderElement>), FocusIndicator(PixelShaderElement), Overlay(PixelShaderElement), - StackHoverIndicator(MemoryRenderBufferRenderElement), + StackHoverIndicator(IcedRenderElement), #[cfg(feature = "debug")] Egui(TextureRenderElement), } @@ -1581,13 +1583,13 @@ where } } -impl From> for CosmicMappedRenderElement +impl From> for CosmicMappedRenderElement where R: AsGlowRenderer, R::TextureId: 'static, CosmicMappedRenderElement: RenderElement, { - fn from(elem: MemoryRenderBufferRenderElement) -> Self { + fn from(elem: IcedRenderElement) -> Self { CosmicMappedRenderElement::StackHoverIndicator(elem) } } diff --git a/src/utils/iced/mod.rs b/src/utils/iced/mod.rs index 9d57b46d..b03e1682 100644 --- a/src/utils/iced/mod.rs +++ b/src/utils/iced/mod.rs @@ -33,7 +33,7 @@ use smithay::{ allocator::Fourcc, input::{ButtonState, KeyState}, renderer::{ - ImportMem, Renderer, + ImportMem, element::{ Kind, memory::{MemoryRenderBuffer, MemoryRenderBufferRenderElement}, @@ -57,13 +57,20 @@ use smithay::{ }, output::Output, reexports::calloop::{self, LoopHandle, RegistrationToken, futures::Scheduler}, + render_elements, utils::{ Buffer as BufferCoords, IsAlive, Logical, Physical, Point, Rectangle, Scale, Serial, Size, Transform, }, }; -use crate::utils::iced::state::State; +use crate::{ + backend::render::{ + element::AsGlowRenderer, + wayland::blur_effect::{BlurElement, BlurState}, + }, + utils::iced::state::State, +}; static ID: LazyLock = LazyLock::new(|| Id::new("Program")); @@ -173,6 +180,7 @@ pub(crate) struct IcedElementInternal { outputs: HashSet, buffers: HashMap, (MemoryRenderBuffer, Option<(Vec, Color)>)>, pending_realloc: bool, + blur: BlurState, // state size: Size, @@ -225,6 +233,7 @@ impl Clone for IcedElementInternal

{ outputs: self.outputs.clone(), buffers: self.buffers.clone(), pending_realloc: self.pending_realloc, + blur: BlurState::default(), size: self.size, last_seat: self.last_seat.clone(), cursor_pos: self.cursor_pos, @@ -310,6 +319,7 @@ impl IcedElement

{ outputs: HashSet::new(), buffers: HashMap::new(), pending_realloc: false, + blur: BlurState::default(), size, cursor_pos: None, last_seat, @@ -342,7 +352,6 @@ impl IcedElement

{ let node = element .as_widget_mut() .layout( - // TODO Avoid creating a new tree here? &mut tree, &internal.renderer, &Limits::new(IcedSize::ZERO, IcedSize::INFINITE) @@ -386,6 +395,11 @@ impl IcedElement

{ self.0.lock().unwrap().update(true); } + pub fn with_theme(&self, f: impl FnOnce(&Theme) -> R) -> R { + let guard = self.0.lock().unwrap(); + f(&guard.theme) + } + pub fn set_theme(&self, theme: cosmic::Theme) { let mut guard = self.0.lock().unwrap(); guard.theme = theme.clone(); @@ -917,9 +931,11 @@ impl IcedElement

{ location: Point, mut scale: Scale, alpha: f32, - push: &mut dyn FnMut(MemoryRenderBufferRenderElement), + mut radii: [u8; 4], + push_above: &mut dyn FnMut(IcedRenderElement), + push_below: Option<&mut dyn FnMut(IcedRenderElement)>, ) where - R: Renderer + ImportMem, + R: AsGlowRenderer + ImportMem, R::TextureId: Send + Clone + 'static, { let mut internal = self.0.lock().unwrap(); @@ -1052,10 +1068,51 @@ impl IcedElement

{ Kind::Unspecified, ) { Ok(buffer) => { - push(buffer); + push_above(buffer.into()); } Err(err) => tracing::warn!("What? {:?}", err), } + + if internal_ref.theme.transparent { + for radius in radii.iter_mut() { + *radius = ((*radius as f64) * internal_ref.additional_scale).round() as u8; + } + + match BlurElement::from_state( + renderer, + &mut internal_ref.blur, + Rectangle::new( + location + .to_f64() + .to_logical(scale) + .upscale(internal_ref.additional_scale), + internal_ref + .size + .to_f64() + .upscale(internal_ref.additional_scale) + .to_i32_round(), + ), + scale.x, + radii, + (internal_ref.theme.cosmic().frosted as u8 + 1) as usize, + ) { + Ok(Some(elem)) => { + if let Some(push_below) = push_below { + push_below(elem.into()) + } else { + push_above(elem.into()) + } + } + Ok(None) => {} + Err(err) => tracing::warn!("Blur elem error: {:?}", err), + } + } } } } + +render_elements! { + pub IcedRenderElement where R: ImportMem + AsGlowRenderer; + UI=MemoryRenderBufferRenderElement, + Blur=BlurElement, +} From 6c76fdaa79b8f3ba05bfe0a012b243fe6714f55f Mon Sep 17 00:00:00 2001 From: Victoria Brekenfeld Date: Tue, 21 Apr 2026 18:18:39 +0200 Subject: [PATCH 34/92] element/window/stack: Use new IcedRenderElement --- src/shell/element/stack.rs | 92 ++++++++----------------------------- src/shell/element/window.rs | 89 ++++++++++------------------------- 2 files changed, 43 insertions(+), 138 deletions(-) diff --git a/src/shell/element/stack.rs b/src/shell/element/stack.rs index 3b1aab87..92d29b75 100644 --- a/src/shell/element/stack.rs +++ b/src/shell/element/stack.rs @@ -4,14 +4,8 @@ use super::{ }; use crate::{ backend::render::{ - IndicatorShader, Key, Usage, - cursor::CursorState, - element::AsGlowRenderer, - shadow::ShadowShader, - wayland::{ - SurfaceRenderElement, - blur_effect::{BlurElement, BlurState}, - }, + IndicatorShader, Key, Usage, cursor::CursorState, element::AsGlowRenderer, + shadow::ShadowShader, wayland::SurfaceRenderElement, }, hooks::{Decorations, HOOKS}, shell::{ @@ -22,7 +16,7 @@ use crate::{ }, state::State, utils::{ - iced::{IcedElement, Program}, + iced::{IcedElement, IcedRenderElement, Program}, prelude::*, }, }; @@ -47,10 +41,7 @@ use smithay::{ input::KeyState, renderer::{ ImportAll, ImportMem, Renderer, - element::{ - Element, Id as RendererId, Kind, RenderElement, UnderlyingStorage, - memory::MemoryRenderBufferRenderElement, - }, + element::{Element, Id as RendererId, Kind, RenderElement, UnderlyingStorage}, gles::element::PixelShaderElement, glow::GlowRenderer, utils::{CommitCounter, DamageSet, OpaqueRegions}, @@ -129,7 +120,6 @@ pub struct CosmicStackInternal { tiled: AtomicBool, theme: Mutex, appearance_conf: Mutex, - blur_state: Mutex, } impl CosmicStackInternal { @@ -156,9 +146,10 @@ impl CosmicStack { pub fn new>( windows: impl Iterator, handle: LoopHandle<'static, crate::state::State>, - theme: cosmic::Theme, + mut theme: cosmic::Theme, appearance: AppearanceConfig, ) -> CosmicStack { + theme.transparent = theme.cosmic().frosted_windows; let windows = windows.map(Into::into).collect::>(); assert!(!windows.is_empty()); @@ -188,7 +179,6 @@ impl CosmicStack { tiled: AtomicBool::new(false), theme: Mutex::new(theme.clone()), appearance_conf: Mutex::new(appearance), - blur_state: Default::default(), }, (width, TAB_HEIGHT), handle, @@ -811,11 +801,6 @@ impl CosmicStack { }) }); - self.0 - .push_render_elements(renderer, stack_loc, scale, alpha, &mut |elem| { - push_above(elem.into()) - }); - self.0.with_program(|p| { let windows = p.windows.lock().unwrap(); let active = p.active.load(Ordering::SeqCst); @@ -862,31 +847,19 @@ impl CosmicStack { ); }); - if frosted > 0 { - let geometry = Rectangle::new( - stack_loc.to_f64().to_logical(scale), - Size::new( - geometry.size.to_f64().to_logical(scale).w, - TAB_HEIGHT as f64, - ), - ); - if let Ok(Some(elem)) = self.0.with_program(|p| { - let mut state = p.blur_state.lock().unwrap(); - BlurElement::from_state( - renderer, - &mut state, - geometry, - scale.x, - radii.map(|[_, b, _, d]| [0, b, 0, d]).unwrap_or([0; 4]), - frosted, - ) - }) { - push_below(elem.into()); - } - } + self.0.push_render_elements( + renderer, + stack_loc, + scale, + alpha, + radii.map(|[_, b, _, d]| [0, b, 0, d]).unwrap_or([0; 4]), + &mut |elem| push_above(elem.into()), + Some(&mut |elem| push_below(elem.into())), + ); } - pub(crate) fn set_theme(&self, theme: cosmic::Theme) { + pub(crate) fn set_theme(&self, mut theme: cosmic::Theme) { + theme.transparent = theme.cosmic().frosted_windows; self.0.with_program(|p| { *p.theme.lock().unwrap() = theme.clone(); }); @@ -1929,27 +1902,20 @@ impl TouchTarget for CosmicStack { } pub enum CosmicStackRenderElement { - Header(MemoryRenderBufferRenderElement), - HeaderBlur(BlurElement), + Header(IcedRenderElement), Shadow(PixelShaderElement), Border(PixelShaderElement), Window(SurfaceRenderElement), } -impl From> +impl From> for CosmicStackRenderElement { - fn from(value: MemoryRenderBufferRenderElement) -> Self { + fn from(value: IcedRenderElement) -> Self { Self::Header(value) } } -impl From for CosmicStackRenderElement { - fn from(value: BlurElement) -> Self { - Self::HeaderBlur(value) - } -} - impl From> for CosmicStackRenderElement { @@ -1966,7 +1932,6 @@ where fn id(&self) -> &RendererId { match self { CosmicStackRenderElement::Header(elem) => elem.id(), - CosmicStackRenderElement::HeaderBlur(elem) => elem.id(), CosmicStackRenderElement::Shadow(elem) => elem.id(), CosmicStackRenderElement::Border(elem) => elem.id(), CosmicStackRenderElement::Window(elem) => elem.id(), @@ -1976,7 +1941,6 @@ where fn current_commit(&self) -> CommitCounter { match self { CosmicStackRenderElement::Header(elem) => elem.current_commit(), - CosmicStackRenderElement::HeaderBlur(elem) => elem.current_commit(), CosmicStackRenderElement::Shadow(elem) => elem.current_commit(), CosmicStackRenderElement::Border(elem) => elem.current_commit(), CosmicStackRenderElement::Window(elem) => elem.current_commit(), @@ -1986,7 +1950,6 @@ where fn src(&self) -> Rectangle { match self { CosmicStackRenderElement::Header(elem) => elem.src(), - CosmicStackRenderElement::HeaderBlur(elem) => elem.src(), CosmicStackRenderElement::Shadow(elem) => elem.src(), CosmicStackRenderElement::Border(elem) => elem.src(), CosmicStackRenderElement::Window(elem) => elem.src(), @@ -1996,7 +1959,6 @@ where fn geometry(&self, scale: Scale) -> Rectangle { match self { CosmicStackRenderElement::Header(elem) => elem.geometry(scale), - CosmicStackRenderElement::HeaderBlur(elem) => elem.geometry(scale), CosmicStackRenderElement::Shadow(elem) => elem.geometry(scale), CosmicStackRenderElement::Border(elem) => elem.geometry(scale), CosmicStackRenderElement::Window(elem) => elem.geometry(scale), @@ -2006,7 +1968,6 @@ where fn location(&self, scale: Scale) -> Point { match self { CosmicStackRenderElement::Header(elem) => elem.location(scale), - CosmicStackRenderElement::HeaderBlur(elem) => elem.location(scale), CosmicStackRenderElement::Shadow(elem) => elem.location(scale), CosmicStackRenderElement::Border(elem) => elem.location(scale), CosmicStackRenderElement::Window(elem) => elem.location(scale), @@ -2016,7 +1977,6 @@ where fn transform(&self) -> Transform { match self { CosmicStackRenderElement::Header(elem) => elem.transform(), - CosmicStackRenderElement::HeaderBlur(elem) => elem.transform(), CosmicStackRenderElement::Shadow(elem) => elem.transform(), CosmicStackRenderElement::Border(elem) => elem.transform(), CosmicStackRenderElement::Window(elem) => elem.transform(), @@ -2030,7 +1990,6 @@ where ) -> DamageSet { match self { CosmicStackRenderElement::Header(elem) => elem.damage_since(scale, commit), - CosmicStackRenderElement::HeaderBlur(elem) => elem.damage_since(scale, commit), CosmicStackRenderElement::Shadow(elem) => elem.damage_since(scale, commit), CosmicStackRenderElement::Border(elem) => elem.damage_since(scale, commit), CosmicStackRenderElement::Window(elem) => elem.damage_since(scale, commit), @@ -2040,7 +1999,6 @@ where fn opaque_regions(&self, scale: Scale) -> OpaqueRegions { match self { CosmicStackRenderElement::Header(elem) => elem.opaque_regions(scale), - CosmicStackRenderElement::HeaderBlur(elem) => elem.opaque_regions(scale), CosmicStackRenderElement::Shadow(elem) => elem.opaque_regions(scale), CosmicStackRenderElement::Border(elem) => elem.opaque_regions(scale), CosmicStackRenderElement::Window(elem) => elem.opaque_regions(scale), @@ -2050,7 +2008,6 @@ where fn alpha(&self) -> f32 { match self { CosmicStackRenderElement::Header(elem) => elem.alpha(), - CosmicStackRenderElement::HeaderBlur(elem) => elem.alpha(), CosmicStackRenderElement::Shadow(elem) => elem.alpha(), CosmicStackRenderElement::Border(elem) => elem.alpha(), CosmicStackRenderElement::Window(elem) => elem.alpha(), @@ -2060,7 +2017,6 @@ where fn kind(&self) -> Kind { match self { CosmicStackRenderElement::Header(elem) => elem.kind(), - CosmicStackRenderElement::HeaderBlur(elem) => elem.kind(), CosmicStackRenderElement::Shadow(elem) => elem.kind(), CosmicStackRenderElement::Border(elem) => elem.kind(), CosmicStackRenderElement::Window(elem) => elem.kind(), @@ -2070,7 +2026,6 @@ where fn is_framebuffer_effect(&self) -> bool { match self { CosmicStackRenderElement::Header(elem) => elem.is_framebuffer_effect(), - CosmicStackRenderElement::HeaderBlur(elem) => elem.is_framebuffer_effect(), CosmicStackRenderElement::Shadow(elem) => elem.is_framebuffer_effect(), CosmicStackRenderElement::Border(elem) => elem.is_framebuffer_effect(), CosmicStackRenderElement::Window(elem) => elem.is_framebuffer_effect(), @@ -2096,9 +2051,6 @@ where CosmicStackRenderElement::Header(elem) => { elem.draw(frame, src, dst, damage, opaque_regions, cache) } - CosmicStackRenderElement::HeaderBlur(elem) => { - RenderElement::::draw(elem, frame, src, dst, damage, opaque_regions, cache) - } CosmicStackRenderElement::Shadow(elem) | CosmicStackRenderElement::Border(elem) => { RenderElement::::draw( elem, @@ -2120,7 +2072,6 @@ where fn underlying_storage(&self, renderer: &mut R) -> Option> { match self { CosmicStackRenderElement::Header(elem) => elem.underlying_storage(renderer), - CosmicStackRenderElement::HeaderBlur(elem) => elem.underlying_storage(renderer), CosmicStackRenderElement::Shadow(elem) | CosmicStackRenderElement::Border(elem) => { elem.underlying_storage(renderer.glow_renderer_mut()) } @@ -2139,9 +2090,6 @@ where CosmicStackRenderElement::Header(elem) => { elem.capture_framebuffer(frame, src, dst, cache) } - CosmicStackRenderElement::HeaderBlur(elem) => { - RenderElement::::capture_framebuffer(elem, frame, src, dst, cache) - } CosmicStackRenderElement::Shadow(elem) | CosmicStackRenderElement::Border(elem) => { RenderElement::::capture_framebuffer( elem, diff --git a/src/shell/element/window.rs b/src/shell/element/window.rs index b978cfd3..09c59f90 100644 --- a/src/shell/element/window.rs +++ b/src/shell/element/window.rs @@ -1,13 +1,7 @@ use crate::{ backend::render::{ - IndicatorShader, Key, Usage, - cursor::CursorState, - element::AsGlowRenderer, - shadow::ShadowShader, - wayland::{ - SurfaceRenderElement, - blur_effect::{BlurElement, BlurState}, - }, + IndicatorShader, Key, Usage, cursor::CursorState, element::AsGlowRenderer, + shadow::ShadowShader, wayland::SurfaceRenderElement, }, hooks::{Decorations, HOOKS}, shell::{ @@ -17,7 +11,7 @@ use crate::{ }, state::State, utils::{ - iced::{IcedElement, Program}, + iced::{IcedElement, IcedRenderElement, Program}, prelude::*, }, }; @@ -30,10 +24,7 @@ use smithay::{ input::KeyState, renderer::{ ImportAll, ImportMem, Renderer, - element::{ - Element, Id as RendererId, Kind, RenderElement, UnderlyingStorage, - memory::MemoryRenderBufferRenderElement, - }, + element::{Element, Id as RendererId, Kind, RenderElement, UnderlyingStorage}, gles::element::PixelShaderElement, glow::GlowRenderer, utils::{CommitCounter, DamageSet, OpaqueRegions}, @@ -99,7 +90,6 @@ pub struct CosmicWindowInternal { tiled: AtomicBool, theme: Mutex, appearance_conf: Mutex, - blur_state: Mutex, } #[repr(u8)] @@ -200,12 +190,13 @@ impl CosmicWindow { pub fn new( window: impl Into, handle: LoopHandle<'static, crate::state::State>, - theme: cosmic::Theme, + mut theme: cosmic::Theme, appearance: AppearanceConfig, ) -> CosmicWindow { let window = window.into(); let width = window.geometry().size.w; let last_title = window.title(); + theme.transparent = theme.cosmic().frosted_windows; if appearance.clip_floating_windows { window.set_tiled(true); @@ -220,7 +211,6 @@ impl CosmicWindow { tiled: AtomicBool::new(false), theme: Mutex::new(theme.clone()), appearance_conf: Mutex::new(appearance), - blur_state: Mutex::default(), }, (width, SSD_HEIGHT), handle, @@ -558,7 +548,7 @@ impl CosmicWindow { push_above(elem); } - let frosted = self.0.with_program(|p| { + self.0.with_program(|p| { if has_ssd { radii[1] = 0; radii[3] = 0; @@ -582,8 +572,6 @@ impl CosmicWindow { &mut |elem| push_above(elem.into()), Some(&mut |elem| push_below(elem.into())), ); - - frosted }); if has_ssd { @@ -591,26 +579,20 @@ impl CosmicWindow { + self .0 .with_program(|p| p.window.geometry().loc.to_physical_precise_round(scale)); - if frosted > 0 { - let geometry = Rectangle::new( - ssd_loc.to_f64().to_logical(scale), - Size::new(geo.size.w, SSD_HEIGHT as f64), - ); - if let Ok(Some(elem)) = self.0.with_program(|p| { - let mut state = p.blur_state.lock().unwrap(); - BlurElement::from_state(renderer, &mut state, geometry, scale.x, radii, frosted) - }) { - push_below(elem.into()); - } - } - self.0 - .push_render_elements(renderer, ssd_loc, scale, alpha, &mut |elem| { - push_above(elem.into()) - }); + self.0.push_render_elements( + renderer, + ssd_loc, + scale, + alpha, + radii, + &mut |elem| push_above(elem.into()), + Some(&mut |elem| push_below(elem.into())), + ); } } - pub(crate) fn set_theme(&self, theme: cosmic::Theme) { + pub(crate) fn set_theme(&self, mut theme: cosmic::Theme) { + theme.transparent = theme.cosmic().frosted_windows; self.0.with_program(|p| { *p.theme.lock().unwrap() = theme.clone(); }); @@ -1267,29 +1249,22 @@ impl WaylandFocus for CosmicWindow { } } -pub enum CosmicWindowRenderElement { - Header(MemoryRenderBufferRenderElement), - HeaderBlur(BlurElement), +pub enum CosmicWindowRenderElement { + Header(IcedRenderElement), Shadow(PixelShaderElement), Border(PixelShaderElement), Window(SurfaceRenderElement), } -impl From> +impl From> for CosmicWindowRenderElement { - fn from(value: MemoryRenderBufferRenderElement) -> Self { + fn from(value: IcedRenderElement) -> Self { Self::Header(value) } } -impl From for CosmicWindowRenderElement { - fn from(value: BlurElement) -> Self { - Self::HeaderBlur(value) - } -} - -impl From> +impl From> for CosmicWindowRenderElement { fn from(value: SurfaceRenderElement) -> Self { @@ -1305,7 +1280,6 @@ where fn id(&self) -> &RendererId { match self { CosmicWindowRenderElement::Header(elem) => elem.id(), - CosmicWindowRenderElement::HeaderBlur(elem) => elem.id(), CosmicWindowRenderElement::Shadow(elem) => elem.id(), CosmicWindowRenderElement::Border(elem) => elem.id(), CosmicWindowRenderElement::Window(elem) => elem.id(), @@ -1315,7 +1289,6 @@ where fn current_commit(&self) -> CommitCounter { match self { CosmicWindowRenderElement::Header(elem) => elem.current_commit(), - CosmicWindowRenderElement::HeaderBlur(elem) => elem.current_commit(), CosmicWindowRenderElement::Shadow(elem) => elem.current_commit(), CosmicWindowRenderElement::Border(elem) => elem.current_commit(), CosmicWindowRenderElement::Window(elem) => elem.current_commit(), @@ -1325,7 +1298,6 @@ where fn src(&self) -> Rectangle { match self { CosmicWindowRenderElement::Header(elem) => elem.src(), - CosmicWindowRenderElement::HeaderBlur(elem) => elem.src(), CosmicWindowRenderElement::Shadow(elem) => elem.src(), CosmicWindowRenderElement::Border(elem) => elem.src(), CosmicWindowRenderElement::Window(elem) => elem.src(), @@ -1335,7 +1307,6 @@ where fn geometry(&self, scale: Scale) -> Rectangle { match self { CosmicWindowRenderElement::Header(elem) => elem.geometry(scale), - CosmicWindowRenderElement::HeaderBlur(elem) => elem.geometry(scale), CosmicWindowRenderElement::Shadow(elem) => elem.geometry(scale), CosmicWindowRenderElement::Border(elem) => elem.geometry(scale), CosmicWindowRenderElement::Window(elem) => elem.geometry(scale), @@ -1345,7 +1316,6 @@ where fn location(&self, scale: Scale) -> Point { match self { CosmicWindowRenderElement::Header(elem) => elem.location(scale), - CosmicWindowRenderElement::HeaderBlur(elem) => elem.location(scale), CosmicWindowRenderElement::Shadow(elem) => elem.location(scale), CosmicWindowRenderElement::Border(elem) => elem.location(scale), CosmicWindowRenderElement::Window(elem) => elem.location(scale), @@ -1355,7 +1325,6 @@ where fn transform(&self) -> Transform { match self { CosmicWindowRenderElement::Header(elem) => elem.transform(), - CosmicWindowRenderElement::HeaderBlur(elem) => elem.transform(), CosmicWindowRenderElement::Shadow(elem) => elem.transform(), CosmicWindowRenderElement::Border(elem) => elem.transform(), CosmicWindowRenderElement::Window(elem) => elem.transform(), @@ -1369,7 +1338,6 @@ where ) -> DamageSet { match self { CosmicWindowRenderElement::Header(elem) => elem.damage_since(scale, commit), - CosmicWindowRenderElement::HeaderBlur(elem) => elem.damage_since(scale, commit), CosmicWindowRenderElement::Shadow(elem) => elem.damage_since(scale, commit), CosmicWindowRenderElement::Border(elem) => elem.damage_since(scale, commit), CosmicWindowRenderElement::Window(elem) => elem.damage_since(scale, commit), @@ -1379,7 +1347,6 @@ where fn opaque_regions(&self, scale: Scale) -> OpaqueRegions { match self { CosmicWindowRenderElement::Header(elem) => elem.opaque_regions(scale), - CosmicWindowRenderElement::HeaderBlur(elem) => elem.opaque_regions(scale), CosmicWindowRenderElement::Shadow(elem) => elem.opaque_regions(scale), CosmicWindowRenderElement::Border(elem) => elem.opaque_regions(scale), CosmicWindowRenderElement::Window(elem) => elem.opaque_regions(scale), @@ -1389,7 +1356,6 @@ where fn alpha(&self) -> f32 { match self { CosmicWindowRenderElement::Header(elem) => elem.alpha(), - CosmicWindowRenderElement::HeaderBlur(elem) => elem.alpha(), CosmicWindowRenderElement::Shadow(elem) => elem.alpha(), CosmicWindowRenderElement::Border(elem) => elem.alpha(), CosmicWindowRenderElement::Window(elem) => elem.alpha(), @@ -1399,7 +1365,6 @@ where fn kind(&self) -> Kind { match self { CosmicWindowRenderElement::Header(elem) => elem.kind(), - CosmicWindowRenderElement::HeaderBlur(elem) => elem.kind(), CosmicWindowRenderElement::Shadow(elem) => elem.kind(), CosmicWindowRenderElement::Border(elem) => elem.kind(), CosmicWindowRenderElement::Window(elem) => elem.kind(), @@ -1409,7 +1374,6 @@ where fn is_framebuffer_effect(&self) -> bool { match self { CosmicWindowRenderElement::Header(elem) => elem.is_framebuffer_effect(), - CosmicWindowRenderElement::HeaderBlur(elem) => elem.is_framebuffer_effect(), CosmicWindowRenderElement::Shadow(elem) => elem.is_framebuffer_effect(), CosmicWindowRenderElement::Border(elem) => elem.is_framebuffer_effect(), CosmicWindowRenderElement::Window(elem) => elem.is_framebuffer_effect(), @@ -1435,9 +1399,6 @@ where CosmicWindowRenderElement::Header(elem) => { elem.draw(frame, src, dst, damage, opaque_regions, cache) } - CosmicWindowRenderElement::HeaderBlur(elem) => { - RenderElement::::draw(elem, frame, src, dst, damage, opaque_regions, cache) - } CosmicWindowRenderElement::Shadow(elem) | CosmicWindowRenderElement::Border(elem) => { RenderElement::::draw( elem, @@ -1459,7 +1420,6 @@ where fn underlying_storage(&self, renderer: &mut R) -> Option> { match self { CosmicWindowRenderElement::Header(elem) => elem.underlying_storage(renderer), - CosmicWindowRenderElement::HeaderBlur(elem) => elem.underlying_storage(renderer), CosmicWindowRenderElement::Shadow(elem) | CosmicWindowRenderElement::Border(elem) => { elem.underlying_storage(renderer.glow_renderer_mut()) } @@ -1478,9 +1438,6 @@ where CosmicWindowRenderElement::Header(elem) => { elem.capture_framebuffer(frame, src, dst, cache) } - CosmicWindowRenderElement::HeaderBlur(elem) => { - RenderElement::::capture_framebuffer(elem, frame, src, dst, cache) - } CosmicWindowRenderElement::Shadow(elem) | CosmicWindowRenderElement::Border(elem) => { RenderElement::::capture_framebuffer( elem, From db06770a6b9156d662b0d0b7c0c4e798f465b788 Mon Sep 17 00:00:00 2001 From: Victoria Brekenfeld Date: Tue, 21 Apr 2026 18:19:01 +0200 Subject: [PATCH 35/92] element: Refactor remaining libcosmic elements for blur --- src/shell/element/resize_indicator.rs | 264 ++++++++++++++++++++++---- src/shell/element/stack_hover.rs | 78 ++++++-- src/shell/element/swap_indicator.rs | 80 ++++++-- src/shell/grabs/menu/mod.rs | 19 +- src/shell/grabs/moving.rs | 22 +-- src/shell/layout/floating/mod.rs | 40 ++-- src/shell/layout/tiling/mod.rs | 32 ++-- src/shell/mod.rs | 13 +- src/shell/zoom.rs | 38 ++-- 9 files changed, 444 insertions(+), 142 deletions(-) diff --git a/src/shell/element/resize_indicator.rs b/src/shell/element/resize_indicator.rs index d1839e6e..fb5441c3 100644 --- a/src/shell/element/resize_indicator.rs +++ b/src/shell/element/resize_indicator.rs @@ -1,10 +1,11 @@ use std::sync::{Arc, Mutex}; use crate::{ + backend::render::element::AsGlowRenderer, config::Config, fl, shell::grabs::ResizeEdge, - utils::iced::{IcedElement, Program}, + utils::iced::{IcedElement, IcedRenderElement, Program}, }; use calloop::LoopHandle; @@ -19,48 +20,233 @@ use cosmic::{ widget::{icon::from_name, text}, }; use cosmic_settings_config::shortcuts::action::{Action, ResizeDirection}; -use smithay::utils::Size; +use smithay::{ + backend::renderer::ImportMem, + desktop::space::SpaceElement, + output::Output, + utils::{Logical, Physical, Point, Rectangle, Scale, Size}, +}; -pub type ResizeIndicator = IcedElement; +#[derive(Debug, Clone)] +pub struct ResizeIndicator { + edges: ResizeEdge, + direction: Arc>, + size: Size, -pub fn resize_indicator( - direction: ResizeDirection, - config: &Config, - evlh: LoopHandle<'static, crate::state::State>, - theme: cosmic::Theme, -) -> ResizeIndicator { - ResizeIndicator::new( - ResizeIndicatorInternal { - edges: Mutex::new(ResizeEdge::all()), - direction, - shortcut1: config - .shortcuts - .iter() - .find_map(|(pattern, action)| { - (*action == Action::Resizing(ResizeDirection::Outwards)).then_some(pattern) - }) - .map(|pattern| format!("{}: ", pattern.to_string())) - .unwrap_or_else(|| crate::fl!("unknown-keybinding")), - shortcut2: config - .shortcuts - .iter() - .find_map(|(pattern, action)| { - (*action == Action::Resizing(ResizeDirection::Inwards)).then_some(pattern) - }) - .map(|pattern| format!("{}: ", pattern.to_string())) - .unwrap_or_else(|| crate::fl!("unknown-keybinding")), - }, - Size::from((1, 1)), - evlh, - theme, - ) + center_elem: IcedElement, + left_elem: IcedElement, + top_elem: IcedElement, + right_elem: IcedElement, + down_elem: IcedElement, +} + +const ARROW_SIZE: i32 = 36; + +impl ResizeIndicator { + pub fn new( + direction: ResizeDirection, + config: &Config, + evlh: LoopHandle<'static, crate::state::State>, + mut theme: cosmic::Theme, + ) -> ResizeIndicator { + theme.transparent = theme.cosmic().frosted_system_interface; + let direction = Arc::new(Mutex::new(direction)); + + ResizeIndicator { + edges: ResizeEdge::all(), + direction: direction.clone(), + size: Size::default(), + center_elem: IcedElement::new( + ResizeIndicatorInternal { + shortcut1: config + .shortcuts + .iter() + .find_map(|(pattern, action)| { + (*action == Action::Resizing(ResizeDirection::Outwards)) + .then_some(pattern) + }) + .map(|pattern| format!("{}: ", pattern.to_string())) + .unwrap_or_else(|| crate::fl!("unknown-keybinding")), + shortcut2: config + .shortcuts + .iter() + .find_map(|(pattern, action)| { + (*action == Action::Resizing(ResizeDirection::Inwards)) + .then_some(pattern) + }) + .map(|pattern| format!("{}: ", pattern.to_string())) + .unwrap_or_else(|| crate::fl!("unknown-keybinding")), + }, + Size::from((1, 1)), + evlh.clone(), + theme.clone(), + ), + left_elem: IcedElement::new( + ResizeIndicatorArrow { + direction: direction.clone(), + icon_outwards: "go-previous-symbolic", + icon_inwards: "go-next-symbolic", + }, + Size::from((ARROW_SIZE, ARROW_SIZE)), + evlh.clone(), + theme.clone(), + ), + top_elem: IcedElement::new( + ResizeIndicatorArrow { + direction: direction.clone(), + icon_outwards: "go-up-symbolic", + icon_inwards: "go-down-symbolic", + }, + Size::from((ARROW_SIZE, ARROW_SIZE)), + evlh.clone(), + theme.clone(), + ), + right_elem: IcedElement::new( + ResizeIndicatorArrow { + direction: direction.clone(), + icon_outwards: "go-next-symbolic", + icon_inwards: "go-previous-symbolic", + }, + Size::from((ARROW_SIZE, ARROW_SIZE)), + evlh.clone(), + theme.clone(), + ), + down_elem: IcedElement::new( + ResizeIndicatorArrow { + direction, + icon_outwards: "go-down-symbolic", + icon_inwards: "go-up-symbolic", + }, + Size::from((ARROW_SIZE, ARROW_SIZE)), + evlh.clone(), + theme.clone(), + ), + } + } + + pub fn resize(&mut self, size: Size) { + let minimum = self.center_elem.minimum_size(); + let new_size = Size::::new(size.w.min(minimum.w), size.h.min(minimum.h)); + self.center_elem.resize(new_size); + self.size = size; + } + + pub fn push_render_elements( + &self, + renderer: &mut R, + location: Point, + scale: Scale, + alpha: f32, + push_above: &mut dyn FnMut(IcedRenderElement), + mut push_below: Option<&mut dyn FnMut(IcedRenderElement)>, + ) where + R: AsGlowRenderer + ImportMem, + R::TextureId: Send + Clone + 'static, + { + let elem_size = self.center_elem.current_size(); + let center_location = Point::new( + self.size.w.saturating_sub(elem_size.w) / 2, + self.size.h.saturating_sub(elem_size.h) / 2, + ); + let left_location = Point::new(0, self.size.h.saturating_sub(ARROW_SIZE) / 2); + let top_location = Point::new(self.size.w.saturating_sub(ARROW_SIZE) / 2, 0); + let right_location = Point::new( + self.size.w.saturating_sub(ARROW_SIZE), + self.size.h.saturating_sub(ARROW_SIZE) / 2, + ); + let down_location = Point::new( + self.size.w.saturating_sub(ARROW_SIZE) / 2, + self.size.h.saturating_sub(ARROW_SIZE), + ); + let radii = self + .center_elem + .with_theme(|theme| theme.cosmic().radius_s()) + .map(|x| x.round() as u8); + + self.center_elem.push_render_elements( + renderer, + location + center_location.to_physical_precise_round(scale), + scale, + alpha, + radii, + push_above, + if let Some(push_below) = push_below.as_mut() { + Some(&mut **push_below) + } else { + None + }, + ); + let mut render = move |elem: &IcedElement, + loc: Point| { + elem.push_render_elements( + renderer, + location + loc.to_physical_precise_round(scale), + scale, + alpha, + radii, + push_above, + if let Some(push_below) = push_below.as_mut() { + Some(&mut **push_below) + } else { + None + }, + ); + }; + + if self.edges.contains(ResizeEdge::LEFT) { + render(&self.left_elem, left_location); + } + if self.edges.contains(ResizeEdge::TOP) { + render(&self.top_elem, top_location); + } + if self.edges.contains(ResizeEdge::RIGHT) { + render(&self.right_elem, right_location); + } + if self.edges.contains(ResizeEdge::BOTTOM) { + render(&self.down_elem, down_location); + } + } + + pub fn set_edges(&mut self, edges: ResizeEdge) { + self.edges = edges; + } + + pub fn set_direction(&self, direction: ResizeDirection) { + let mut dir_ref = self.direction.lock().unwrap(); + if *dir_ref != direction { + *dir_ref = direction; + self.left_elem.force_redraw(); + self.top_elem.force_redraw(); + self.right_elem.force_redraw(); + self.down_elem.force_redraw(); + } + } + + pub fn output_enter(&self, output: &Output) { + self.center_elem + .output_enter(output, Rectangle::default() /*unused*/); + self.left_elem + .output_enter(output, Rectangle::default() /*unused*/); + self.top_elem + .output_enter(output, Rectangle::default() /*unused*/); + self.right_elem + .output_enter(output, Rectangle::default() /*unused*/); + self.down_elem + .output_enter(output, Rectangle::default() /*unused*/); + } + + pub fn output_leave(&self, output: &Output) { + self.center_elem.output_leave(output); + self.left_elem.output_leave(output); + self.top_elem.output_leave(output); + self.right_elem.output_leave(output); + self.down_elem.output_leave(output); + } } pub struct ResizeIndicatorInternal { - pub edges: Mutex, - pub direction: ResizeDirection, - pub shortcut1: String, - pub shortcut2: String, + shortcut1: String, + shortcut2: String, } impl Program for ResizeIndicatorInternal { @@ -94,7 +280,7 @@ impl Program for ResizeIndicatorInternal { text_color: Some(Color::from(theme.cosmic().accent.on)), background: Some(Background::Color(background.into())), border: Border { - radius: 18.0.into(), + radius: theme.cosmic().radius_s().into(), width: 0.0, color: Color::TRANSPARENT, }, diff --git a/src/shell/element/stack_hover.rs b/src/shell/element/stack_hover.rs index d6a057c4..f1238404 100644 --- a/src/shell/element/stack_hover.rs +++ b/src/shell/element/stack_hover.rs @@ -1,6 +1,7 @@ use crate::{ + backend::render::element::AsGlowRenderer, fl, - utils::iced::{IcedElement, Program}, + utils::iced::{IcedElement, IcedRenderElement, Program}, }; use calloop::LoopHandle; @@ -14,16 +15,72 @@ use cosmic::{ theme, widget::{icon::from_name, space, text}, }; -use smithay::utils::{Logical, Size}; +use smithay::{ + backend::renderer::ImportMem, + desktop::space::SpaceElement, + output::Output, + utils::{Logical, Physical, Point, Rectangle, Scale, Size}, +}; -pub type StackHover = IcedElement; +#[derive(Debug)] +pub struct StackHover { + location: Point, + elem: IcedElement, +} -pub fn stack_hover( - evlh: LoopHandle<'static, crate::state::State>, - size: Size, - theme: cosmic::Theme, -) -> StackHover { - StackHover::new(StackHoverInternal, size, evlh, theme) +impl StackHover { + pub fn new( + evlh: LoopHandle<'static, crate::state::State>, + size: Size, + mut theme: cosmic::Theme, + ) -> StackHover { + theme.transparent = theme.cosmic().frosted_system_interface; + + let elem = IcedElement::new(StackHoverInternal, size, evlh, theme); + let minimum = elem.minimum_size(); + let new_size = Size::::new(size.w.min(minimum.w), size.h.min(minimum.h)); + let location = Point::new( + size.w.saturating_sub(new_size.w) / 2, + size.h.saturating_sub(new_size.h) / 2, + ); + elem.resize(new_size); + + StackHover { location, elem } + } + + pub fn push_render_elements( + &self, + renderer: &mut R, + location: Point, + scale: Scale, + alpha: f32, + push_above: &mut dyn FnMut(IcedRenderElement), + push_below: Option<&mut dyn FnMut(IcedRenderElement)>, + ) where + R: AsGlowRenderer + ImportMem, + R::TextureId: Send + Clone + 'static, + { + self.elem.push_render_elements( + renderer, + location + self.location.to_physical_precise_round(scale), + scale, + alpha, + self.elem + .with_theme(|theme| theme.cosmic().radius_s()) + .map(|x| x.round() as u8), + push_above, + push_below, + ); + } + + pub fn output_enter(&self, output: &Output) { + self.elem + .output_enter(output, Rectangle::default() /*unused*/); + } + + pub fn output_leave(&self, output: &Output) { + self.elem.output_leave(output); + } } pub struct StackHoverInternal; @@ -71,9 +128,6 @@ impl Program for StackHoverInternal { })) .width(Length::Shrink) .height(Length::Shrink) - .apply(container) - .center_x(Length::Fill) - .center_y(Length::Fill) .into() } } diff --git a/src/shell/element/swap_indicator.rs b/src/shell/element/swap_indicator.rs index e6fb0197..6f9498da 100644 --- a/src/shell/element/swap_indicator.rs +++ b/src/shell/element/swap_indicator.rs @@ -1,6 +1,7 @@ use crate::{ + backend::render::element::AsGlowRenderer, fl, - utils::iced::{IcedElement, Program}, + utils::iced::{IcedElement, IcedRenderElement, Program}, }; use calloop::LoopHandle; @@ -13,15 +14,75 @@ use cosmic::{ theme, widget::{icon::from_name, text}, }; -use smithay::utils::Size; +use smithay::{ + backend::renderer::ImportMem, + desktop::space::SpaceElement, + output::Output, + utils::{Logical, Physical, Point, Rectangle, Scale, Size}, +}; -pub type SwapIndicator = IcedElement; +#[derive(Debug, Clone)] +pub struct SwapIndicator { + location: Point, + elem: IcedElement, +} -pub fn swap_indicator( - evlh: LoopHandle<'static, crate::state::State>, - theme: cosmic::Theme, -) -> SwapIndicator { - SwapIndicator::new(SwapIndicatorInternal, Size::from((1, 1)), evlh, theme) +impl SwapIndicator { + pub fn new( + evlh: LoopHandle<'static, crate::state::State>, + mut theme: cosmic::Theme, + ) -> SwapIndicator { + theme.transparent = theme.cosmic().frosted_system_interface; + SwapIndicator { + location: Point::default(), + elem: IcedElement::new(SwapIndicatorInternal, Size::from((1, 1)), evlh, theme), + } + } + + pub fn resize(&mut self, size: Size) { + let minimum = self.elem.minimum_size(); + let new_size = Size::::new(size.w.min(minimum.w), size.h.min(minimum.h)); + let location = Point::new( + size.w.saturating_sub(new_size.w) / 2, + size.h.saturating_sub(new_size.h) / 2, + ); + self.elem.resize(new_size); + self.location = location; + } + + pub fn push_render_elements( + &self, + renderer: &mut R, + location: Point, + scale: Scale, + alpha: f32, + push_above: &mut dyn FnMut(IcedRenderElement), + push_below: Option<&mut dyn FnMut(IcedRenderElement)>, + ) where + R: AsGlowRenderer + ImportMem, + R::TextureId: Send + Clone + 'static, + { + self.elem.push_render_elements( + renderer, + location + self.location.to_physical_precise_round(scale), + scale, + alpha, + self.elem + .with_theme(|theme| theme.cosmic().radius_s()) + .map(|x| x.round() as u8), + push_above, + push_below, + ); + } + + pub fn output_enter(&self, output: &Output) { + self.elem + .output_enter(output, Rectangle::default() /*unused*/); + } + + pub fn output_leave(&self, output: &Output) { + self.elem.output_leave(output); + } } pub struct SwapIndicatorInternal; @@ -69,9 +130,6 @@ impl Program for SwapIndicatorInternal { })) .width(Length::Shrink) .height(Length::Shrink) - .apply(container) - .center_x(Length::Fill) - .center_y(Length::Fill) .into() } } diff --git a/src/shell/grabs/menu/mod.rs b/src/shell/grabs/menu/mod.rs index ec82bb07..efd66886 100644 --- a/src/shell/grabs/menu/mod.rs +++ b/src/shell/grabs/menu/mod.rs @@ -20,7 +20,7 @@ use cosmic::{ use smithay::{ backend::{ input::{ButtonState, TouchSlot}, - renderer::{ImportMem, Renderer, element::memory::MemoryRenderBufferRenderElement}, + renderer::ImportMem, }, desktop::space::SpaceElement, input::{ @@ -42,10 +42,11 @@ use smithay::{ }; use crate::{ + backend::render::element::AsGlowRenderer, shell::{SeatExt, focus::target::PointerFocusTarget}, state::State, utils::{ - iced::{IcedElement, Program}, + iced::{IcedElement, IcedRenderElement, Program}, prelude::*, }, }; @@ -68,9 +69,9 @@ impl MenuGrabState { &self, renderer: &mut R, output: &Output, - push: &mut dyn FnMut(MemoryRenderBufferRenderElement), + push: &mut dyn FnMut(IcedRenderElement), ) where - R: Renderer + ImportMem, + R: AsGlowRenderer + ImportMem, R::TextureId: Send + Clone + 'static, { let scale = output.current_scale().fractional_scale(); @@ -83,7 +84,11 @@ impl MenuGrabState { .to_physical_precise_round(scale), scale.into(), 1.0, + elem.iced + .with_theme(|theme| theme.cosmic().radius_s()) + .map(|x| x.round() as u8), push, + None, ) } } @@ -274,11 +279,13 @@ impl Program for ContextMenu { let mut elements = grab_state.elements.lock().unwrap(); let position = elements.last().unwrap().position; + let mut theme = state.common.theme.clone(); + theme.transparent = theme.cosmic().frosted_system_interface; let element = IcedElement::new( ContextMenu::new(items), Size::default(), state.common.event_loop_handle.clone(), - state.common.theme.clone(), + theme, ); let min_size = element.minimum_size(); @@ -474,7 +481,7 @@ impl Program for ContextMenu { .padding(1) .class(theme::Container::custom(|theme| { let cosmic = theme.cosmic(); - let component = &cosmic.background(false).component; + let component = &cosmic.background(theme.cosmic().frosted_windows).component; iced_widget::container::Style { snap: true, icon_color: Some(cosmic.accent.base.into()), diff --git a/src/shell/grabs/moving.rs b/src/shell/grabs/moving.rs index 48ebd4ba..d8714830 100644 --- a/src/shell/grabs/moving.rs +++ b/src/shell/grabs/moving.rs @@ -6,10 +6,7 @@ use crate::{ }, shell::{ CosmicMapped, CosmicSurface, Direction, ManagedLayer, - element::{ - CosmicMappedRenderElement, - stack_hover::{StackHover, stack_hover}, - }, + element::{CosmicMappedRenderElement, stack_hover::StackHover}, focus::target::{KeyboardFocusTarget, PointerFocusTarget}, layout::floating::TiledCorners, }, @@ -25,7 +22,7 @@ use smithay::{ drm::DrmNode, input::ButtonState, renderer::{ - ImportAll, ImportMem, Renderer, + ImportAll, ImportMem, element::{RenderElement, utils::RescaleRenderElement}, }, }, @@ -78,7 +75,7 @@ impl MoveGrabState { scanout_node: Option, push: &mut dyn FnMut(CosmicMappedRenderElement), ) where - R: Renderer + ImportAll + ImportMem + AsGlowRenderer, + R: AsGlowRenderer + ImportAll + ImportMem, R::TextureId: Send + Clone + 'static, CosmicMappedRenderElement: RenderElement, { @@ -122,6 +119,7 @@ impl MoveGrabState { output_scale, 1.0, &mut |elem| push(elem.into()), + None, ); } @@ -441,7 +439,7 @@ impl MoveGrab { if let Some(indicator) = grab_state.stacking_indicator.as_ref().map(|x| &x.0) { - indicator.output_enter(output, overlap); + indicator.output_enter(output); } } } else if self.window_outputs.remove(output) { @@ -455,16 +453,14 @@ impl MoveGrab { let indicator_location = shell.stacking_indicator(¤t_output, self.previous); if indicator_location.is_some() != grab_state.stacking_indicator.is_some() { grab_state.stacking_indicator = indicator_location.map(|geo| { - let element = stack_hover( + let size = geo.size.as_logical(); + let element = StackHover::new( state.common.event_loop_handle.clone(), - geo.size.as_logical(), + size, state.common.theme.clone(), ); for output in &self.window_outputs { - element.output_enter( - output, - Rectangle::from_size(output.geometry().size.as_logical()), - ); + element.output_enter(output); } (element, geo.loc.as_logical()) }); diff --git a/src/shell/layout/floating/mod.rs b/src/shell/layout/floating/mod.rs index aad738f8..07ce73cf 100644 --- a/src/shell/layout/floating/mod.rs +++ b/src/shell/layout/floating/mod.rs @@ -1525,6 +1525,27 @@ impl FloatingLayout { if focused == Some(elem) && !elem.is_maximized(false) { let active_window_hint = crate::theme::active_window_hint(theme); let radius = elem.corner_radius(geometry.size.as_logical(), indicator_thickness); + + if let Some((mode, resize)) = resize_indicator.as_mut() { + let mut resize_geometry = geometry; + resize_geometry.loc -= (18, 18).into(); + resize_geometry.size += (36, 36).into(); + + resize.resize(resize_geometry.size.as_logical()); + resize.output_enter(output); + resize.push_render_elements( + renderer, + resize_geometry + .loc + .as_logical() + .to_physical_precise_round(output_scale), + output_scale.into(), + alpha * mode.alpha().unwrap_or(1.0), + &mut |elem| push(CosmicMappedRenderElement::Window(elem.into())), + None, + ); + } + if indicator_thickness > 0 { let element = IndicatorShader::focus_element( renderer, @@ -1542,25 +1563,6 @@ impl FloatingLayout { ); push(element.into()); } - - if let Some((mode, resize)) = resize_indicator.as_mut() { - let mut resize_geometry = geometry; - resize_geometry.loc -= (18, 18).into(); - resize_geometry.size += (36, 36).into(); - - resize.resize(resize_geometry.size.as_logical()); - resize.output_enter(output, Rectangle::default() /* unused */); - resize.push_render_elements( - renderer, - resize_geometry - .loc - .as_logical() - .to_physical_precise_round(output_scale), - output_scale.into(), - alpha * mode.alpha().unwrap_or(1.0), - &mut |elem| push(CosmicMappedRenderElement::Window(elem.into())), - ); - } } let maybe_map = if let Some(anim) = self.animations.get(elem) { diff --git a/src/shell/layout/tiling/mod.rs b/src/shell/layout/tiling/mod.rs index 3a3633db..5b425948 100644 --- a/src/shell/layout/tiling/mod.rs +++ b/src/shell/layout/tiling/mod.rs @@ -5330,13 +5330,12 @@ fn render_new_tree_windows( let mut group_backdrop = None; let mut indicators = SmallVec::<[CosmicMappedRenderElement; 2]>::new_const(); - let mut resize_element = None; + let mut resize_elements = SmallVec::<[CosmicMappedRenderElement; 10]>::new_const(); let mut swap_elements = SmallVec::<[CosmicMappedRenderElement; 4]>::new_const(); - let output_geo = output.geometry(); let output_scale = output.current_scale().fractional_scale(); - let (swap_indicator, swap_tree) = overview.1.unzip(); + let (mut swap_indicator, swap_tree) = overview.1.unzip(); let swap_desc = swap_desc.filter(|_| is_active_output); let swap_tree = swap_tree.flatten().filter(|_| is_active_output); let window_hint = crate::theme::active_window_hint(theme); @@ -5540,16 +5539,18 @@ fn render_new_tree_windows( .unwrap_or(false) }) .unwrap_or(false)) - && let Some(swap) = swap_indicator.as_ref() + && let Some(swap) = swap_indicator.as_mut() { - swap.resize(geo.size.as_logical()); - swap.output_enter(output, output_geo.as_logical()); + let size = geo.size.as_logical(); + swap.resize(size); + swap.output_enter(output); swap.push_render_elements( renderer, geo.loc.as_logical().to_physical_precise_round(output_scale), output_scale.into(), alpha * overview.0.alpha().unwrap_or(1.0), &mut |elem| swap_elements.push(elem.into()), + None, ); } } @@ -5560,29 +5561,20 @@ fn render_new_tree_windows( geo.size += (36, 36).into(); resize.resize(geo.size.as_logical()); - resize.output_enter(output, output_geo.as_logical()); + resize.output_enter(output); let possible_edges = TilingLayout::possible_resizes(target_tree, node_id.clone()); if !possible_edges.is_empty() { - if resize.with_program(|internal| { - let mut edges = internal.edges.lock().unwrap(); - if *edges != possible_edges { - *edges = possible_edges; - true - } else { - false - } - }) { - resize.force_update(); - } + resize.set_edges(possible_edges); resize.push_render_elements( renderer, geo.loc.as_logical().to_physical_precise_round(output_scale), output_scale.into(), alpha * mode.alpha().unwrap_or(1.0), &mut |elem| { - resize_element = Some(elem.into()); + resize_elements.push(elem.into()); }, + None, ); } } @@ -5743,7 +5735,7 @@ fn render_new_tree_windows( }, ); - for elem in resize_element + for elem in resize_elements .into_iter() .chain(swap_elements) .chain(indicators) diff --git a/src/shell/mod.rs b/src/shell/mod.rs index b4559dce..1dfc528c 100644 --- a/src/shell/mod.rs +++ b/src/shell/mod.rs @@ -102,9 +102,8 @@ use self::zoom::{OutputZoomState, ZoomState}; use self::{ element::{ - CosmicWindow, MaximizedState, - resize_indicator::{ResizeIndicator, resize_indicator}, - swap_indicator::{SwapIndicator, swap_indicator}, + CosmicWindow, MaximizedState, resize_indicator::ResizeIndicator, + swap_indicator::SwapIndicator, }, focus::target::{KeyboardFocusTarget, PointerFocusTarget}, grabs::{ @@ -2291,7 +2290,7 @@ impl Shell { OverviewMode::Started(_, _) | OverviewMode::Active(_) ) { if matches!(trigger, Trigger::KeyboardSwap(_, _)) { - self.swap_indicator = Some(swap_indicator(evlh, self.theme.clone())); + self.swap_indicator = Some(SwapIndicator::new(evlh, self.theme.clone())); } self.overview_mode = OverviewMode::Started(trigger, Instant::now()); } @@ -2344,7 +2343,7 @@ impl Shell { } else { self.resize_mode = ResizeMode::Started(pattern, Instant::now(), direction); } - self.resize_indicator = Some(resize_indicator( + self.resize_indicator = Some(ResizeIndicator::new( direction, config, evlh, @@ -3668,6 +3667,8 @@ impl Shell { return None; }; + let mut theme = self.theme.clone(); + theme.transparent = theme.cosmic().frosted_windows; let grab = MenuGrab::new( GrabStartData::Pointer(start_data), seat, @@ -3676,7 +3677,7 @@ impl Shell { MenuAlignment::CORNER, None, evlh.clone(), - self.theme.clone(), + theme, ); Some((grab, Focus::Keep)) diff --git a/src/shell/zoom.rs b/src/shell/zoom.rs index e0d3f1d1..11114e2d 100644 --- a/src/shell/zoom.rs +++ b/src/shell/zoom.rs @@ -11,7 +11,7 @@ use cosmic_comp_config::ZoomMovement; use cosmic_config::ConfigSet; use keyframe::{ease, functions::Linear}; use smithay::{ - backend::renderer::{ImportMem, Renderer, element::memory::MemoryRenderBufferRenderElement}, + backend::renderer::ImportMem, desktop::space::SpaceElement, input::{ Seat, @@ -32,9 +32,10 @@ use smithay::{ use tracing::error; use crate::{ + backend::render::element::AsGlowRenderer, state::State, utils::{ - iced::{IcedElement, Program}, + iced::{IcedElement, IcedRenderElement, Program}, prelude::*, tween::EasePoint, }, @@ -71,8 +72,9 @@ impl OutputZoomState { increment: u32, movement: ZoomMovement, loop_handle: LoopHandle<'static, State>, - theme: cosmic::Theme, + mut theme: cosmic::Theme, ) -> OutputZoomState { + theme.transparent = theme.cosmic().frosted_system_interface; let cursor_position = seat.get_pointer().unwrap().current_location().as_global(); let output_geometry = output.geometry().to_f64(); let focal_point = if output_geometry.contains(cursor_position) { @@ -108,8 +110,7 @@ impl OutputZoomState { let program = ZoomProgram::new(level, movement, increment); let element = IcedElement::new(program, Size::default(), loop_handle, theme); let mut size = element.minimum_size(); - size.w = (size.w + 32/*TODO: figure out why iced is calculating too little*/) - .min(output_geometry.size.w.round() as i32); + size.w = size.w.min(output_geometry.size.w.round() as i32); element.set_activate(true); element.resize(size); element.output_enter(output, Rectangle::new(Point::from((0, 0)), size)); @@ -196,9 +197,9 @@ impl OutputZoomState { &mut self, renderer: &mut R, output: &Output, - push: &mut dyn FnMut(MemoryRenderBufferRenderElement), + push: &mut dyn FnMut(IcedRenderElement), ) where - R: Renderer + ImportMem, + R: AsGlowRenderer + ImportMem, R::TextureId: Send + Clone + 'static, { let size = self.element.current_size().to_f64(); @@ -216,7 +217,11 @@ impl OutputZoomState { location, scale.fractional_scale().into(), 1.0, + self.element + .with_theme(|theme| theme.cosmic().radius_s()) + .map(|x| x.round() as u8), push, + None, ) } } @@ -390,12 +395,9 @@ impl ZoomState { None } - pub fn render( - renderer: &mut R, - output: &Output, - push: &mut dyn FnMut(MemoryRenderBufferRenderElement), - ) where - R: Renderer + ImportMem, + pub fn render(renderer: &mut R, output: &Output, push: &mut dyn FnMut(IcedRenderElement)) + where + R: AsGlowRenderer + ImportMem, R::TextureId: Send + Clone + 'static, { let output_state = output.user_data().get::>().unwrap(); @@ -515,7 +517,7 @@ impl Program for ZoomProgram { .padding(8) .class(theme::Container::custom(|theme| { let cosmic = theme.cosmic(); - let component = &cosmic.background(false).component; + let component = &cosmic.background(theme.transparent).component; iced_widget::container::Style { snap: true, icon_color: Some(component.on.into()), @@ -591,6 +593,8 @@ impl Program for ZoomProgram { let level = output_state_ref.level; std::mem::drop(output_state_ref); + let mut theme = state.common.theme.clone(); + theme.transparent = theme.cosmic().frosted_system_interface; let grab = MenuGrab::new( start_data, &seat, @@ -703,7 +707,7 @@ impl Program for ZoomProgram { ), Some(level.min(4.)), state.common.event_loop_handle.clone(), - state.common.theme.clone(), + theme, ); std::mem::drop(shell); @@ -756,6 +760,8 @@ impl Program for ZoomProgram { let level = output_state_ref.level; std::mem::drop(output_state_ref); + let mut theme = state.common.theme.clone(); + theme.transparent = theme.cosmic().frosted_system_interface; let grab = MenuGrab::new( start_data, &seat, @@ -788,7 +794,7 @@ impl Program for ZoomProgram { MenuAlignment::PREFER_CENTERED, Some(level.min(4.)), state.common.event_loop_handle.clone(), - state.common.theme.clone(), + theme, ); std::mem::drop(shell); From 77f57a656d1c03d412f135dd3e5fa647926d1b3d Mon Sep 17 00:00:00 2001 From: Ashley Wulber Date: Mon, 27 Apr 2026 18:57:48 -0400 Subject: [PATCH 36/92] fix: pad_rect typos --- src/wayland/handlers/corner_radius.rs | 4 ++-- src/wayland/protocols/corner_radius.rs | 4 ++-- 2 files changed, 4 insertions(+), 4 deletions(-) diff --git a/src/wayland/handlers/corner_radius.rs b/src/wayland/handlers/corner_radius.rs index 140b86ff..406cbf48 100644 --- a/src/wayland/handlers/corner_radius.rs +++ b/src/wayland/handlers/corner_radius.rs @@ -111,11 +111,11 @@ pub fn pad_rect( padding: &[i32; 4], ) -> Option> { rect.size.h = rect.size.h.checked_sub(padding[0])?; - rect.loc.x += padding[0]; + rect.loc.x += padding[3]; rect.size.w = rect.size.w.checked_sub(padding[1])?; rect.size.h = rect.size.h.checked_sub(padding[2])?; rect.size.w = rect.size.w.checked_sub(padding[3])?; - rect.loc.y += padding[3]; + rect.loc.y += padding[0]; Some(rect) } diff --git a/src/wayland/protocols/corner_radius.rs b/src/wayland/protocols/corner_radius.rs index f0edf343..be6f9ca0 100644 --- a/src/wayland/protocols/corner_radius.rs +++ b/src/wayland/protocols/corner_radius.rs @@ -646,9 +646,9 @@ fn pad_rect( padding: &Padding, ) -> Option> { rect.size.h = rect.size.h.checked_sub(padding.top)?; - rect.loc.x += padding.top; + rect.loc.x += padding.left; rect.size.w = rect.size.w.checked_sub(padding.left)?; - rect.loc.y += padding.left; + rect.loc.y += padding.top; rect.size.h = rect.size.h.checked_sub(padding.bottom)?; rect.size.w = rect.size.w.checked_sub(padding.right)?; Some(rect) From b890f7b0fa86d087ba2ae4d6cd64ec3a1f8b7cb9 Mon Sep 17 00:00:00 2001 From: Ashley Wulber Date: Mon, 27 Apr 2026 18:58:19 -0400 Subject: [PATCH 37/92] fix(corner_radius): loosen padding size constraint --- src/wayland/handlers/corner_radius.rs | 11 +++++------ 1 file changed, 5 insertions(+), 6 deletions(-) diff --git a/src/wayland/handlers/corner_radius.rs b/src/wayland/handlers/corner_radius.rs index 406cbf48..9618e77e 100644 --- a/src/wayland/handlers/corner_radius.rs +++ b/src/wayland/handlers/corner_radius.rs @@ -94,15 +94,14 @@ pub fn surface_corners(states: &SurfaceData, size: Size) -> Option pub fn surface_padding(states: &SurfaceData, size: Size) -> Option<[i32; 4]> { let mut guard = states.cached_state.get::(); - // guard against padding being too large - let half_min_dim = size.w.min(size.h) / 2; let padding = guard.current().0?; + // guard against padding being too large Some([ - padding.top.min(half_min_dim), - padding.right.min(half_min_dim), - padding.bottom.min(half_min_dim), - padding.left.min(half_min_dim), + padding.top.min(size.h / 2), + padding.right.min(size.w / 2), + padding.bottom.min(size.h / 2), + padding.left.min(size.w / 2), ]) } From 58938ef8ee60e320561535e6ec5e86089617c937 Mon Sep 17 00:00:00 2001 From: Victoria Brekenfeld Date: Fri, 22 May 2026 16:09:53 +0200 Subject: [PATCH 38/92] render/blur: Fix transformed outputs --- src/backend/render/wayland/blur_effect.rs | 32 ++++++++++++----------- 1 file changed, 17 insertions(+), 15 deletions(-) diff --git a/src/backend/render/wayland/blur_effect.rs b/src/backend/render/wayland/blur_effect.rs index 47f5be88..aa30e474 100644 --- a/src/backend/render/wayland/blur_effect.rs +++ b/src/backend/render/wayland/blur_effect.rs @@ -482,23 +482,30 @@ fn blit_from_active_fb( to_texture: &mut GlesTexture, ) -> Result { if transform != Transform::Normal { + let dst = dst + .to_logical(1) + .to_buffer(1, transform, &frame.output_size().to_logical(1)); let tex_size = to_texture .size() .to_logical(1, Transform::Normal) .to_physical(1); let mut renderer = frame.renderer(); - let mut tmp_texture = renderer.as_mut().create_buffer( - Fourcc::Abgr8888, - dst.size.to_logical(1).to_buffer(1, Transform::Normal), - )?; + let mut tmp_texture = renderer + .as_mut() + .create_buffer(Fourcc::Abgr8888, dst.size)?; let mut fb_tmp = renderer.as_mut().bind(&mut tmp_texture)?; let mut fb = renderer.as_mut().bind(to_texture)?; std::mem::drop(renderer); + // TODO: Why does blit not take buffer coordinates? + let dst_phys = Rectangle::<_, Physical>::new( + Point::new(dst.loc.x, dst.loc.y), + dst.size.to_logical(1, Transform::Normal).to_physical(1), + ); let sync = frame.blit_to( &mut fb_tmp, - dst, - Rectangle::from_size(dst.size), + dst_phys, + Rectangle::from_size(dst_phys.size), TextureFilter::Linear, )?; std::mem::drop(fb_tmp); @@ -511,18 +518,13 @@ fn blit_from_active_fb( Frame::render_texture_from_to( &mut frame, &tmp_texture, - Rectangle::from_size( - dst.size - .to_logical(1) - .to_buffer(1, Transform::Normal) - .to_f64(), - ), + Rectangle::from_size(dst.size.to_f64()), src.to_logical(1., Transform::Normal, &src.size) .to_physical(1.) .to_i32_round(), - &[Rectangle::from_size(dst.size)], - &[Rectangle::from_size(dst.size)], - transform, + &[Rectangle::from_size(dst_phys.size)], + &[Rectangle::from_size(dst_phys.size)], + transform.invert(), 1.0, )?; std::mem::drop(tmp_texture); From 43d6145afd5e870a8789107462a645262bf6158b Mon Sep 17 00:00:00 2001 From: Victoria Brekenfeld Date: Fri, 22 May 2026 16:50:35 +0200 Subject: [PATCH 39/92] chore: fix warnings with no features --- src/input/mod.rs | 12 +++++++----- 1 file changed, 7 insertions(+), 5 deletions(-) diff --git a/src/input/mod.rs b/src/input/mod.rs index 69b5064c..1114d50b 100644 --- a/src/input/mod.rs +++ b/src/input/mod.rs @@ -35,14 +35,15 @@ use calloop::{ use cosmic_comp_config::{NumlockState, workspace::WorkspaceLayout}; use cosmic_settings_config::shortcuts; use cosmic_settings_config::shortcuts::action::{Direction, ResizeDirection}; +#[cfg(feature = "systemd")] +use smithay::backend::input::{Switch, SwitchState, SwitchToggleEvent}; use smithay::{ backend::input::{ AbsolutePositionEvent, Axis, AxisRelativeDirection, AxisSource, Device, DeviceCapability, GestureBeginEvent, GestureEndEvent, GesturePinchUpdateEvent as _, GestureSwipeUpdateEvent as _, InputBackend, InputEvent, KeyState, KeyboardKeyEvent, - PointerAxisEvent, ProximityState, Switch, SwitchState, SwitchToggleEvent, - TabletToolButtonEvent, TabletToolEvent, TabletToolProximityEvent, TabletToolTipEvent, - TabletToolTipState, TouchEvent, + PointerAxisEvent, ProximityState, TabletToolButtonEvent, TabletToolEvent, + TabletToolProximityEvent, TabletToolTipEvent, TabletToolTipState, TouchEvent, }, desktop::{PopupKeyboardGrab, WindowSurfaceType, utils::under_from_surface_tree}, input::{ @@ -74,7 +75,7 @@ use smithay::{ tablet_manager::{TabletDescriptor, TabletSeatTrait}, }, }; -use tracing::{error, trace, warn}; +use tracing::{error, trace}; use xkbcommon::xkb::{Keycode, Keysym}; use std::{ @@ -1599,6 +1600,7 @@ impl State { } } InputEvent::Special(_) => {} + #[allow(unused_variables)] InputEvent::SwitchToggle { event } => { #[cfg(feature = "logind")] if event.switch() == Some(Switch::Lid) && self.common.inhibit_lid_fd.is_some() { @@ -1620,7 +1622,7 @@ impl State { if let Err(err) = self.refresh_output_config() { if !closed { - warn!(?err, "Failed to re-enable internal connector"); + tracing::warn!(?err, "Failed to re-enable internal connector"); if let Some(output) = output { use cosmic_comp_config::output::comp::OutputState; From f424abbc298b37dc8bf99441c7f6e1877bc2983b Mon Sep 17 00:00:00 2001 From: Victoria Brekenfeld Date: Fri, 22 May 2026 17:15:16 +0200 Subject: [PATCH 40/92] render/blur: Fix div-by-zero in shaders --- src/backend/render/shaders/blur_downsample.frag | 6 +++++- src/backend/render/shaders/blur_upsample.frag | 6 +++++- 2 files changed, 10 insertions(+), 2 deletions(-) diff --git a/src/backend/render/shaders/blur_downsample.frag b/src/backend/render/shaders/blur_downsample.frag index 1fa24beb..1fff7522 100644 --- a/src/backend/render/shaders/blur_downsample.frag +++ b/src/backend/render/shaders/blur_downsample.frag @@ -22,5 +22,9 @@ void main() { sum += texture2D(tex, v_coords + vec2(half_pixel.x, -half_pixel.y) * offset); sum += texture2D(tex, v_coords - vec2(half_pixel.x, -half_pixel.y) * offset); - gl_FragColor = sum / sum.a; + if (sum.a == 0.0) { + gl_FragColor = vec4(0.0, 0.0, 0.0, 0.0); + } else { + gl_FragColor = sum / sum.a; + } } diff --git a/src/backend/render/shaders/blur_upsample.frag b/src/backend/render/shaders/blur_upsample.frag index ac026d19..588fcb65 100644 --- a/src/backend/render/shaders/blur_upsample.frag +++ b/src/backend/render/shaders/blur_upsample.frag @@ -23,5 +23,9 @@ void main() { sum += texture2D(tex, v_coords + vec2(0.0, -half_pixel.y * 2.0) * offset); sum += texture2D(tex, v_coords + vec2(-half_pixel.x, -half_pixel.y) * offset) * 2.0; - gl_FragColor = sum / sum.a; + if (sum.a == 0.0) { + gl_FragColor = vec4(0.0, 0.0, 0.0, 0.0); + } else { + gl_FragColor = sum / sum.a; + } } From e0b6784eb5608c72f94c6b52d1bba154e426bd0a Mon Sep 17 00:00:00 2001 From: Victoria Brekenfeld Date: Fri, 22 May 2026 18:25:05 +0200 Subject: [PATCH 41/92] render/blur: Wipe blur_texture before blit --- src/backend/render/wayland/blur_effect.rs | 30 ++++++++++++++++------- 1 file changed, 21 insertions(+), 9 deletions(-) diff --git a/src/backend/render/wayland/blur_effect.rs b/src/backend/render/wayland/blur_effect.rs index aa30e474..8ceb1704 100644 --- a/src/backend/render/wayland/blur_effect.rs +++ b/src/backend/render/wayland/blur_effect.rs @@ -481,21 +481,32 @@ fn blit_from_active_fb( transform: Transform, to_texture: &mut GlesTexture, ) -> Result { + let tex_size = to_texture + .size() + .to_logical(1, Transform::Normal) + .to_physical(1); + let fb_size = frame.output_size(); + + let mut renderer = frame.renderer(); + let mut fb = renderer.as_mut().bind(to_texture)?; + let sync = { + let mut subframe = renderer + .as_mut() + .render(&mut fb, tex_size, Transform::Normal)?; + subframe.clear(Color32F::TRANSPARENT, &[Rectangle::from_size(tex_size)])?; + subframe.finish()? + }; + if transform != Transform::Normal { let dst = dst .to_logical(1) - .to_buffer(1, transform, &frame.output_size().to_logical(1)); - let tex_size = to_texture - .size() - .to_logical(1, Transform::Normal) - .to_physical(1); - let mut renderer = frame.renderer(); + .to_buffer(1, transform, &fb_size.to_logical(1)); let mut tmp_texture = renderer .as_mut() .create_buffer(Fourcc::Abgr8888, dst.size)?; let mut fb_tmp = renderer.as_mut().bind(&mut tmp_texture)?; - let mut fb = renderer.as_mut().bind(to_texture)?; std::mem::drop(renderer); + frame.wait(&sync)?; // TODO: Why does blit not take buffer coordinates? let dst_phys = Rectangle::<_, Physical>::new( @@ -508,6 +519,7 @@ fn blit_from_active_fb( Rectangle::from_size(dst_phys.size), TextureFilter::Linear, )?; + frame.wait(&sync)?; std::mem::drop(fb_tmp); let mut renderer = frame.renderer(); @@ -530,8 +542,8 @@ fn blit_from_active_fb( std::mem::drop(tmp_texture); frame.finish() } else { - let mut fb = frame.renderer().as_mut().bind(to_texture)?; - + std::mem::drop(renderer); + frame.wait(&sync)?; frame.blit_to( &mut fb, dst, From f6d3dafda7790023059237bc717c21a288d552df Mon Sep 17 00:00:00 2001 From: Ashley Wulber Date: Tue, 26 May 2026 18:24:33 -0400 Subject: [PATCH 42/92] fix: use configured blur strength for lock surface and its subsurfaces --- src/backend/render/mod.rs | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/src/backend/render/mod.rs b/src/backend/render/mod.rs index ac49af7d..2c26550a 100644 --- a/src/backend/render/mod.rs +++ b/src/backend/render/mod.rs @@ -826,7 +826,7 @@ where }); } Stage::SessionLock(lock_surface) => { - session_lock_elements(renderer, output, lock_surface, &mut |elem| { + session_lock_elements(renderer, output, lock_surface, blur_strength, &mut |elem| { elements.extend(crop_to_output(elem.into()).map(Into::into)) }) } @@ -1060,6 +1060,7 @@ fn session_lock_elements( renderer: &mut R, output: &Output, lock_surface: Option<&LockSurface>, + blur_strength: usize, push: &mut dyn FnMut(SurfaceRenderElement), ) where R: AsGlowRenderer, @@ -1077,7 +1078,7 @@ fn session_lock_elements( false, [0; 4], None, - 0, + blur_strength, FRAME_TIME_FILTER, push, None, From 361a2282c7d2849fc050d16b2ecc815fa31c28bb Mon Sep 17 00:00:00 2001 From: Victoria Brekenfeld Date: Wed, 10 Jun 2026 18:05:14 +0200 Subject: [PATCH 43/92] output: Add increasing output id --- src/shell/mod.rs | 20 ++++++++++++++++++++ 1 file changed, 20 insertions(+) diff --git a/src/shell/mod.rs b/src/shell/mod.rs index 1dfc528c..8e628985 100644 --- a/src/shell/mod.rs +++ b/src/shell/mod.rs @@ -16,6 +16,7 @@ use crate::{ element::CosmicStack, focus::FocusTarget, grabs::fullscreen_items, layout::tiling::PlaceholderType, }, + utils, wayland::{ handlers::data_device::{self, get_dnd_icon}, protocols::workspace::{State as WState, WorkspaceCapabilities}, @@ -1510,6 +1511,21 @@ impl Workspaces { #[derive(Debug)] pub struct InvalidWorkspaceIndex; +utils::id_gen!(next_output_id, OUTPUT_ID, OUTPUT_IDS); +pub struct OutputId(usize); + +impl OutputId { + pub fn namespace_for_workspace(&self, idx: usize) -> usize { + self.0 | (idx << 32) + } +} + +impl Drop for OutputId { + fn drop(&mut self) { + OUTPUT_IDS.lock().unwrap().remove(&self.0); + } +} + impl Common { pub fn add_output(&mut self, output: &Output) { let mut shell = self.shell.write(); @@ -1517,6 +1533,10 @@ impl Common { .workspaces .add_output(output, &mut self.workspace_state.update()); + output + .user_data() + .insert_if_missing_threadsafe(|| OutputId(next_output_id())); + if let Some(state) = shell.zoom_state.as_ref() { output.user_data().insert_if_missing_threadsafe(|| { Mutex::new(OutputZoomState::new( From 1f0456a5af114fe2fc33c130555a2a3e87e1fd33 Mon Sep 17 00:00:00 2001 From: Victoria Brekenfeld Date: Wed, 10 Jun 2026 18:05:57 +0200 Subject: [PATCH 44/92] render: Include OutputId in namespaces --- src/backend/render/mod.rs | 18 +++++++++++++++--- 1 file changed, 15 insertions(+), 3 deletions(-) diff --git a/src/backend/render/mod.rs b/src/backend/render/mod.rs index 2c26550a..472abb8e 100644 --- a/src/backend/render/mod.rs +++ b/src/backend/render/mod.rs @@ -27,7 +27,7 @@ use crate::{ }, config::ScreenFilter, shell::{ - CosmicMappedRenderElement, OverviewMode, SeatExt, Trigger, WorkspaceDelta, + CosmicMappedRenderElement, OutputId, OverviewMode, SeatExt, Trigger, WorkspaceDelta, WorkspaceRenderElement, element::CosmicMappedKey, focus::{FocusTarget, Stage, render_input_order, target::WindowGroup}, @@ -844,6 +844,12 @@ where }) .unwrap_or([0; 4]); + let namespace = output + .user_data() + .get::() + .map(|id| id.namespace_for_workspace(workspace_idx)) + .unwrap_or(workspace_idx); + push_render_elements_from_surface_tree( renderer, popup.wl_surface(), @@ -861,7 +867,7 @@ where FRAME_TIME_FILTER, &mut |elem| { elements.extend( - crop_to_output(NamespacedElement::new(elem, workspace_idx).into()) + crop_to_output(NamespacedElement::new(elem, namespace).into()) .map(Into::into), ) }, @@ -887,6 +893,12 @@ where }) .unwrap_or([0; 4]); + let namespace = output + .user_data() + .get::() + .map(|id| id.namespace_for_workspace(workspace_idx)) + .unwrap_or(workspace_idx); + push_render_elements_from_surface_tree( renderer, layer.wl_surface(), @@ -904,7 +916,7 @@ where FRAME_TIME_FILTER, &mut |elem| { elements.extend( - crop_to_output(NamespacedElement::new(elem, workspace_idx).into()) + crop_to_output(NamespacedElement::new(elem, namespace).into()) .map(Into::into), ) }, From c92b91221c1fcbe6e6ff707ce5d5f3561682c1b7 Mon Sep 17 00:00:00 2001 From: Victoria Brekenfeld Date: Thu, 11 Jun 2026 13:17:11 +0200 Subject: [PATCH 45/92] render/element: Support converting gl-textures --- src/backend/render/element.rs | 36 ++++++++++++++++++++++++++++++++--- 1 file changed, 33 insertions(+), 3 deletions(-) diff --git a/src/backend/render/element.rs b/src/backend/render/element.rs index 51d282c1..27179045 100644 --- a/src/backend/render/element.rs +++ b/src/backend/render/element.rs @@ -8,20 +8,27 @@ use crate::{ use smithay::backend::renderer::element::texture::TextureRenderElement; use smithay::{ backend::{ - allocator::dmabuf::Dmabuf, + allocator::{Fourcc, dmabuf::Dmabuf}, + drm::DrmDeviceFd, renderer::{ - Bind, Blit, ExportMem, ImportAll, ImportMem, Offscreen, Renderer, + Bind, Blit, ContextId, ExportMem, Frame as _, FrameContext, ImportAll, ImportMem, + Offscreen, Renderer, RendererSuper, element::{ Element, Id, Kind, RenderElement, UnderlyingStorage, utils::{CropRenderElement, Relocate, RelocateRenderElement, RescaleRenderElement}, }, gles::{GlesError, GlesRenderbuffer, GlesTexture, element::TextureShaderElement}, glow::{GlowFrame, GlowRenderer}, + multigpu::{ + MultiTexture, + gbm::{GbmGlesBackend, GbmGlesDevice}, + }, utils::{CommitCounter, DamageSet, OpaqueRegions}, }, }, utils::{ - Buffer as BufferCoords, Logical, Physical, Point, Rectangle, Scale, user_data::UserDataMap, + Buffer as BufferCoords, Logical, Physical, Point, Rectangle, Scale, Size, + user_data::UserDataMap, }, }; @@ -396,6 +403,11 @@ pub trait AsGlowRenderer: fn glow_frame_mut<'a, 'frame, 'buffer>( frame: &'a mut Self::Frame<'frame, 'buffer>, ) -> &'a mut GlowFrame<'frame, 'buffer>; + fn tex_from_gl(context: &ContextId, texture: GlesTexture) -> Self::TextureId; + fn tex_to_gl( + context: &ContextId, + texture: &Self::TextureId, + ) -> Option; fn from_gles_error(err: GlesError) -> Self::Error; } @@ -416,6 +428,15 @@ impl AsGlowRenderer for GlowRenderer { ) -> &'a mut GlowFrame<'frame, 'buffer> { frame } + fn tex_from_gl(_context: &ContextId, texture: GlesTexture) -> Self::TextureId { + texture + } + fn tex_to_gl( + _context: &ContextId, + texture: &Self::TextureId, + ) -> Option { + Some(texture.clone()) + } fn from_gles_error(err: GlesError) -> Self::Error { err } @@ -438,6 +459,15 @@ impl AsGlowRenderer for GlMultiRenderer<'_> { ) -> &'b mut GlowFrame<'frame, 'buffer> { frame.as_mut() } + fn tex_from_gl(context: &ContextId, texture: GlesTexture) -> Self::TextureId { + MultiTexture::from_native_texture::>(context, texture).unwrap() + } + fn tex_to_gl( + context: &ContextId, + texture: &Self::TextureId, + ) -> Option { + texture.get::>(context) + } fn from_gles_error(err: GlesError) -> Self::Error { GlMultiError::Render(err) } From f6cb4519a0ef4124aeea1b41da4810f5a27ea8e3 Mon Sep 17 00:00:00 2001 From: Victoria Brekenfeld Date: Thu, 11 Jun 2026 13:18:41 +0200 Subject: [PATCH 46/92] render/blur: Cache native texture type --- src/backend/render/cursor.rs | 2 +- src/backend/render/element.rs | 19 +++-- src/backend/render/wayland/blur_effect.rs | 76 +++++++++++-------- src/backend/render/wayland/mod.rs | 2 +- src/shell/element/mod.rs | 4 +- src/shell/element/stack.rs | 4 +- src/shell/element/window.rs | 4 +- src/shell/workspace.rs | 18 ++--- src/utils/iced/mod.rs | 2 +- src/utils/screenshot.rs | 2 +- .../handlers/image_copy_capture/render.rs | 2 +- 11 files changed, 74 insertions(+), 61 deletions(-) diff --git a/src/backend/render/cursor.rs b/src/backend/render/cursor.rs index 0585e664..da01b270 100644 --- a/src/backend/render/cursor.rs +++ b/src/backend/render/cursor.rs @@ -175,7 +175,7 @@ fn load_icon(theme: &CursorTheme, shape: CursorIcon) -> Result, Error } render_elements! { - pub CursorRenderElement where R: ImportAll + ImportMem + AsGlowRenderer; + pub CursorRenderElement where R: ImportAll + ImportMem + AsGlowRenderer, R::TextureId: Send; Static=MemoryRenderBufferRenderElement, Surface=SurfaceRenderElement, } diff --git a/src/backend/render/element.rs b/src/backend/render/element.rs index 27179045..706a542b 100644 --- a/src/backend/render/element.rs +++ b/src/backend/render/element.rs @@ -1,5 +1,8 @@ use crate::{ - backend::render::{GlMultiError, wayland::SurfaceRenderElement}, + backend::{ + kms::render::gles::GbmGlowBackend, + render::{GlMultiError, wayland::SurfaceRenderElement}, + }, shell::{CosmicMappedRenderElement, WorkspaceRenderElement}, utils::iced::IcedRenderElement, }; @@ -37,7 +40,7 @@ use super::{GlMultiRenderer, cursor::CursorRenderElement}; pub enum CosmicElement where R: AsGlowRenderer, - R::TextureId: 'static, + R::TextureId: Send + 'static, CosmicMappedRenderElement: RenderElement, { Workspace( @@ -58,7 +61,7 @@ where impl Element for CosmicElement where R: AsGlowRenderer, - R::TextureId: 'static, + R::TextureId: Send + 'static, CosmicMappedRenderElement: RenderElement, { fn id(&self) -> &Id { @@ -223,7 +226,7 @@ where impl RenderElement for CosmicElement where R: AsGlowRenderer, - R::TextureId: 'static, + R::TextureId: Send + 'static, CosmicMappedRenderElement: RenderElement, { fn draw( @@ -339,7 +342,7 @@ impl From>>> for CosmicElement where R: AsGlowRenderer, - R::TextureId: 'static, + R::TextureId: Send + 'static, CosmicMappedRenderElement: RenderElement, { fn from(elem: CropRenderElement>>) -> Self { @@ -354,7 +357,7 @@ where impl From> for CosmicElement where R: AsGlowRenderer, - R::TextureId: 'static, + R::TextureId: Send + 'static, CosmicMappedRenderElement: RenderElement, { fn from(value: IcedRenderElement) -> Self { @@ -365,7 +368,7 @@ where impl From for CosmicElement where R: Renderer + ImportAll + ImportMem + AsGlowRenderer, - R::TextureId: 'static, + R::TextureId: Send + 'static, CosmicMappedRenderElement: RenderElement, { fn from(value: DamageElement) -> Self { @@ -377,7 +380,7 @@ where impl From> for CosmicElement where R: AsGlowRenderer, - R::TextureId: 'static, + R::TextureId: Send + 'static, CosmicMappedRenderElement: RenderElement, { fn from(elem: TextureRenderElement) -> Self { diff --git a/src/backend/render/wayland/blur_effect.rs b/src/backend/render/wayland/blur_effect.rs index 8ceb1704..9b3145bc 100644 --- a/src/backend/render/wayland/blur_effect.rs +++ b/src/backend/render/wayland/blur_effect.rs @@ -9,7 +9,7 @@ use smithay::{ allocator::Fourcc, renderer::{ Bind, BlitFrame, Color32F, ContextId, Frame, FrameContext, ImportAll, Offscreen, - Renderer, Texture, TextureFilter, + Renderer, RendererSuper, Texture, TextureFilter, element::{Element, Id, Kind, RenderElement}, gles::{ GlesError, GlesFrame, GlesRenderer, GlesTexProgram, GlesTexture, Uniform, @@ -115,7 +115,7 @@ impl BlurShaders { } } -type BlurTexture = Mutex>; +type BlurTexture = Mutex>; #[derive(Debug)] pub struct BlurState { @@ -369,7 +369,10 @@ impl Element for BlurElement { } } -impl RenderElement for BlurElement { +impl RenderElement for BlurElement +where + R::TextureId: Send + 'static, +{ fn capture_framebuffer( &self, frame: &mut ::Frame<'_, '_>, @@ -377,41 +380,48 @@ impl RenderElement for BlurElement { dst: Rectangle, cache: &UserDataMap, ) -> Result<(), ::Error> { + let transform = frame.transformation(); + let tex_size = self.src.to_i32_round(); let glow_frame = ::glow_frame_mut(frame); let gles_frame = BorrowMut::>::borrow_mut(glow_frame); - let transform = gles_frame.transformation(); - let tex_size = self.src.to_i32_round(); + let mut renderer = gles_frame.renderer(); - let texture_ref = cache.get_or_insert_threadsafe(BlurTexture::default); + let texture_ref = cache.get_or_insert_threadsafe(BlurTexture::::default); let mut texture_entry = texture_ref.lock().unwrap(); - if texture_entry - .as_ref() - .is_some_and(|tex| tex.size() != tex_size) - { + if texture_entry.as_ref().is_some_and(|tex| { + tex.size() != tex_size + || R::tex_to_gl( + &renderer.as_ref().context_id(), + texture_entry.as_ref().unwrap(), + ) + .is_none() + }) { texture_entry.take(); } - - let mut renderer = gles_frame.renderer(); if texture_entry.is_none() { - *texture_entry = Some( - renderer - .as_mut() - .create_buffer(Fourcc::Abgr8888, tex_size) - .map_err(R::from_gles_error)?, - ); + let gl_texture = renderer + .as_mut() + .create_buffer(Fourcc::Abgr8888, tex_size) + .map_err(R::from_gles_error)?; + *texture_entry = Some(R::tex_from_gl(&renderer.as_ref().context_id(), gl_texture)); } - let texture = texture_entry.as_mut().unwrap(); + + let mut texture = R::tex_to_gl( + &renderer.as_ref().context_id(), + texture_entry.as_ref().unwrap(), + ) + .unwrap(); let mut off_texture = renderer .as_mut() .create_buffer(Fourcc::Abgr8888, tex_size) .map_err(R::from_gles_error)?; std::mem::drop(renderer); - let sync = blit_from_active_fb(gles_frame, src, dst, transform, texture) + let sync = blit_from_active_fb(gles_frame, src, dst, transform, &mut texture) .map_err(R::from_gles_error)?; gles_frame.wait(&sync).map_err(R::from_gles_error)?; - let mut textures = [texture, &mut off_texture]; + let mut textures = [&mut texture, &mut off_texture]; render_blur( gles_frame.renderer().as_mut(), &self.scaling_shaders, @@ -433,7 +443,6 @@ impl RenderElement for BlurElement { opaque_regions: &[Rectangle], cache: Option<&UserDataMap>, ) -> Result<(), R::Error> { - let glow_frame = ::glow_frame_mut(frame); let src_to_geo = self.geometry.size / self.src; let src_log = src .upscale(src_to_geo) @@ -451,26 +460,27 @@ impl RenderElement for BlurElement { .flat_map(|rect| damage.iter().flat_map(move |r| r.intersection(rect))) .collect::>(); let cache = cache.expect("Framebuffer element without cache?"); - let Some(texture) = cache.get::() else { + let Some(texture) = cache.get::>() else { return Err(R::from_gles_error(GlesError::BlitError)); }; let texture_ref = texture.lock().unwrap(); - BorrowMut::>::borrow_mut(glow_frame) - .render_texture_from_to( - texture_ref - .as_ref() - .ok_or(R::from_gles_error(GlesError::BlitError))?, + if let Some(tex) = texture_ref.as_ref() { + BorrowMut::::borrow_mut(::glow_frame_mut(frame)) + .override_default_tex_program(self.render_shader.clone(), self.uniforms.clone()); + frame.render_texture_from_to( + tex, src, dst, &damage, - opaque_regions, + &opaque_regions, Transform::Normal, 1.0, - Some(&self.render_shader), - &self.uniforms, - ) - .map_err(R::from_gles_error) + )?; + BorrowMut::::borrow_mut(::glow_frame_mut(frame)) + .clear_tex_program_override(); + } + Ok(()) } } diff --git a/src/backend/render/wayland/mod.rs b/src/backend/render/wayland/mod.rs index 9f07d08b..c2b7d4ec 100644 --- a/src/backend/render/wayland/mod.rs +++ b/src/backend/render/wayland/mod.rs @@ -20,7 +20,7 @@ pub mod blur_effect; pub mod clipped_surface; render_elements! { - pub SurfaceRenderElement where R: AsGlowRenderer + ImportAll; + pub SurfaceRenderElement where R: AsGlowRenderer + ImportAll, R::TextureId: Send; Blur=BlurElement, Clipped=ClippedSurfaceRenderElement, Wayland=WaylandSurfaceRenderElement, diff --git a/src/shell/element/mod.rs b/src/shell/element/mod.rs index f5691b29..14736719 100644 --- a/src/shell/element/mod.rs +++ b/src/shell/element/mod.rs @@ -1137,7 +1137,7 @@ where impl Element for CosmicMappedRenderElement where R: AsGlowRenderer, - R::TextureId: 'static, + R::TextureId: Send + 'static, { fn id(&self) -> &smithay::backend::renderer::element::Id { match self { @@ -1358,7 +1358,7 @@ where impl RenderElement for CosmicMappedRenderElement where R: AsGlowRenderer, - R::TextureId: 'static, + R::TextureId: Send + 'static, { fn draw( &self, diff --git a/src/shell/element/stack.rs b/src/shell/element/stack.rs index 92d29b75..fabb87bd 100644 --- a/src/shell/element/stack.rs +++ b/src/shell/element/stack.rs @@ -1927,7 +1927,7 @@ impl From> impl Element for CosmicStackRenderElement where R: Renderer + ImportAll + ImportMem + AsGlowRenderer, - R::TextureId: 'static, + R::TextureId: Send + 'static, { fn id(&self) -> &RendererId { match self { @@ -2036,7 +2036,7 @@ where impl RenderElement for CosmicStackRenderElement where R: AsGlowRenderer, - R::TextureId: 'static, + R::TextureId: Send + 'static, { fn draw( &self, diff --git a/src/shell/element/window.rs b/src/shell/element/window.rs index 09c59f90..f6c9e1c4 100644 --- a/src/shell/element/window.rs +++ b/src/shell/element/window.rs @@ -1275,7 +1275,7 @@ impl From> impl Element for CosmicWindowRenderElement where R: Renderer + ImportAll + ImportMem + AsGlowRenderer, - R::TextureId: 'static, + R::TextureId: Send + 'static, { fn id(&self) -> &RendererId { match self { @@ -1384,7 +1384,7 @@ where impl RenderElement for CosmicWindowRenderElement where R: AsGlowRenderer, - R::TextureId: 'static, + R::TextureId: Send + 'static, { fn draw( &self, diff --git a/src/shell/workspace.rs b/src/shell/workspace.rs index ea5dbad7..e771e502 100644 --- a/src/shell/workspace.rs +++ b/src/shell/workspace.rs @@ -1973,7 +1973,7 @@ pub struct OutputNotMapped; pub enum WorkspaceRenderElement where R: AsGlowRenderer, - R::TextureId: 'static, + R::TextureId: Send + 'static, { OverrideRedirect(SurfaceRenderElement), LowerLayerShell(NamespacedElement>), @@ -1986,7 +1986,7 @@ where impl Element for WorkspaceRenderElement where R: AsGlowRenderer, - R::TextureId: 'static, + R::TextureId: Send + 'static, { fn id(&self) -> &smithay::backend::renderer::element::Id { match self { @@ -2117,7 +2117,7 @@ where impl RenderElement for WorkspaceRenderElement where R: AsGlowRenderer, - R::TextureId: 'static, + R::TextureId: Send + 'static, { fn draw( &self, @@ -2213,7 +2213,7 @@ where impl From>> for WorkspaceRenderElement where R: AsGlowRenderer, - R::TextureId: 'static, + R::TextureId: Send + 'static, CosmicMappedRenderElement: RenderElement, { fn from(elem: RescaleRenderElement>) -> Self { @@ -2224,7 +2224,7 @@ where impl From> for WorkspaceRenderElement where R: AsGlowRenderer, - R::TextureId: 'static, + R::TextureId: Send + 'static, CosmicMappedRenderElement: RenderElement, { fn from(elem: CosmicWindowRenderElement) -> Self { @@ -2235,7 +2235,7 @@ where impl From> for WorkspaceRenderElement where R: AsGlowRenderer, - R::TextureId: 'static, + R::TextureId: Send + 'static, CosmicMappedRenderElement: RenderElement, { fn from(elem: SurfaceRenderElement) -> Self { @@ -2246,7 +2246,7 @@ where impl From>> for WorkspaceRenderElement where R: AsGlowRenderer, - R::TextureId: 'static, + R::TextureId: Send + 'static, CosmicMappedRenderElement: RenderElement, { fn from(elem: NamespacedElement>) -> Self { @@ -2257,7 +2257,7 @@ where impl From> for WorkspaceRenderElement where R: AsGlowRenderer, - R::TextureId: 'static, + R::TextureId: Send + 'static, CosmicMappedRenderElement: RenderElement, { fn from(elem: CosmicMappedRenderElement) -> Self { @@ -2268,7 +2268,7 @@ where impl From> for WorkspaceRenderElement where R: AsGlowRenderer, - R::TextureId: 'static, + R::TextureId: Send + 'static, CosmicMappedRenderElement: RenderElement, { fn from(elem: TextureRenderElement) -> Self { diff --git a/src/utils/iced/mod.rs b/src/utils/iced/mod.rs index b03e1682..2bfe888d 100644 --- a/src/utils/iced/mod.rs +++ b/src/utils/iced/mod.rs @@ -1112,7 +1112,7 @@ impl IcedElement

{ } render_elements! { - pub IcedRenderElement where R: ImportMem + AsGlowRenderer; + pub IcedRenderElement where R: ImportMem + AsGlowRenderer, R::TextureId: Send; UI=MemoryRenderBufferRenderElement, Blur=BlurElement, } diff --git a/src/utils/screenshot.rs b/src/utils/screenshot.rs index de544b72..f1f1f5f4 100644 --- a/src/utils/screenshot.rs +++ b/src/utils/screenshot.rs @@ -23,7 +23,7 @@ pub fn screenshot_window(state: &mut State, surface: &CosmicSurface) { fn render_window(renderer: &mut R, window: &CosmicSurface) -> anyhow::Result<()> where R: Renderer + ImportAll + Offscreen + ExportMem + AsGlowRenderer, - R::TextureId: Clone + 'static, + R::TextureId: Send + Clone + 'static, R::Error: Send + Sync + 'static, { let bbox = bbox_from_surface_tree(&window.wl_surface().unwrap(), (0, 0)); diff --git a/src/wayland/handlers/image_copy_capture/render.rs b/src/wayland/handlers/image_copy_capture/render.rs index 0b77cbf5..ae429b4d 100644 --- a/src/wayland/handlers/image_copy_capture/render.rs +++ b/src/wayland/handlers/image_copy_capture/render.rs @@ -502,7 +502,7 @@ pub fn render_workspace_to_buffer( } smithay::render_elements! { - pub WindowCaptureElement where R: ImportAll + ImportMem + AsGlowRenderer; + pub WindowCaptureElement where R: ImportAll + ImportMem + AsGlowRenderer, R::TextureId: Send; WaylandElement=SurfaceRenderElement, CursorElement=RelocateRenderElement>, DamageElement=DamageElement, From 7fbeb8787f2f2df0509d547c652d583c20da096f Mon Sep 17 00:00:00 2001 From: Victoria Brekenfeld Date: Thu, 11 Jun 2026 15:46:58 +0200 Subject: [PATCH 47/92] examples: Fix new libcosmic build --- Cargo.lock | 1 + Cargo.toml | 1 + examples/customized-window-decorations.rs | 2 +- 3 files changed, 3 insertions(+), 1 deletion(-) diff --git a/Cargo.lock b/Cargo.lock index ed27a859..b58cd62c 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -806,6 +806,7 @@ dependencies = [ "futures-util", "i18n-embed", "i18n-embed-fl", + "iced_graphics", "iced_tiny_skia", "id_tree", "indexmap 2.13.0", diff --git a/Cargo.toml b/Cargo.toml index 7023d1d7..27f79056 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -40,6 +40,7 @@ i18n-embed = { version = "0.16", features = [ ] } i18n-embed-fl = "0.10" iced_tiny_skia = { git = "https://github.com/pop-os/libcosmic" } +iced_graphics = { git = "https://github.com/pop-os/libcosmic/", features = ["geometry"] } indexmap = "2.13" keyframe = "1.1.1" libcosmic = { git = "https://github.com/pop-os/libcosmic", default-features = false } diff --git a/examples/customized-window-decorations.rs b/examples/customized-window-decorations.rs index 364221fe..6b3baf8f 100644 --- a/examples/customized-window-decorations.rs +++ b/examples/customized-window-decorations.rs @@ -23,7 +23,7 @@ struct Circle { color: Color, } -impl +impl canvas::Program for Circle { type State = (); From 0c805d9b30c02fc79d9586efac3d208135d7ec6a Mon Sep 17 00:00:00 2001 From: Victoria Brekenfeld Date: Thu, 11 Jun 2026 15:47:16 +0200 Subject: [PATCH 48/92] chore: cleanup new blur code --- src/backend/render/element.rs | 13 ++++--------- src/backend/render/wayland/blur_effect.rs | 6 +++--- 2 files changed, 7 insertions(+), 12 deletions(-) diff --git a/src/backend/render/element.rs b/src/backend/render/element.rs index 706a542b..e1259dc1 100644 --- a/src/backend/render/element.rs +++ b/src/backend/render/element.rs @@ -11,27 +11,22 @@ use crate::{ use smithay::backend::renderer::element::texture::TextureRenderElement; use smithay::{ backend::{ - allocator::{Fourcc, dmabuf::Dmabuf}, + allocator::dmabuf::Dmabuf, drm::DrmDeviceFd, renderer::{ - Bind, Blit, ContextId, ExportMem, Frame as _, FrameContext, ImportAll, ImportMem, - Offscreen, Renderer, RendererSuper, + Bind, Blit, ContextId, ExportMem, ImportAll, ImportMem, Offscreen, Renderer, element::{ Element, Id, Kind, RenderElement, UnderlyingStorage, utils::{CropRenderElement, Relocate, RelocateRenderElement, RescaleRenderElement}, }, gles::{GlesError, GlesRenderbuffer, GlesTexture, element::TextureShaderElement}, glow::{GlowFrame, GlowRenderer}, - multigpu::{ - MultiTexture, - gbm::{GbmGlesBackend, GbmGlesDevice}, - }, + multigpu::MultiTexture, utils::{CommitCounter, DamageSet, OpaqueRegions}, }, }, utils::{ - Buffer as BufferCoords, Logical, Physical, Point, Rectangle, Scale, Size, - user_data::UserDataMap, + Buffer as BufferCoords, Logical, Physical, Point, Rectangle, Scale, user_data::UserDataMap, }, }; diff --git a/src/backend/render/wayland/blur_effect.rs b/src/backend/render/wayland/blur_effect.rs index 9b3145bc..3cf8ea9e 100644 --- a/src/backend/render/wayland/blur_effect.rs +++ b/src/backend/render/wayland/blur_effect.rs @@ -9,7 +9,7 @@ use smithay::{ allocator::Fourcc, renderer::{ Bind, BlitFrame, Color32F, ContextId, Frame, FrameContext, ImportAll, Offscreen, - Renderer, RendererSuper, Texture, TextureFilter, + Renderer, Texture, TextureFilter, element::{Element, Id, Kind, RenderElement}, gles::{ GlesError, GlesFrame, GlesRenderer, GlesTexProgram, GlesTexture, Uniform, @@ -115,7 +115,7 @@ impl BlurShaders { } } -type BlurTexture = Mutex>; +type BlurTexture = Mutex>; #[derive(Debug)] pub struct BlurState { @@ -473,7 +473,7 @@ where src, dst, &damage, - &opaque_regions, + opaque_regions, Transform::Normal, 1.0, )?; From 4f8aed4149449430575419dd232b1e5856eda1fa Mon Sep 17 00:00:00 2001 From: Victoria Brekenfeld Date: Thu, 11 Jun 2026 16:41:04 +0200 Subject: [PATCH 49/92] blur: Fix invalid matrix inversions --- src/backend/render/wayland/blur_effect.rs | 6 ++---- 1 file changed, 2 insertions(+), 4 deletions(-) diff --git a/src/backend/render/wayland/blur_effect.rs b/src/backend/render/wayland/blur_effect.rs index 3cf8ea9e..243cd510 100644 --- a/src/backend/render/wayland/blur_effect.rs +++ b/src/backend/render/wayland/blur_effect.rs @@ -218,7 +218,7 @@ impl BlurElement { radii: [u8; 4], strength: usize, ) -> Result, R::Error> { - if strength == 0 { + if strength == 0 || geometry.size.w == 0. || geometry.size.h == 0. { return Ok(None); } @@ -237,12 +237,10 @@ impl BlurElement { }; let geo_translation = { let offset = geo.loc - extended_geo.loc; - Matrix3::from_translation(Vector2::new( + Matrix3::from_translation(-Vector2::new( (offset.x / extended_geo.size.w) as f32, (offset.y / extended_geo.size.h) as f32, )) - .invert() - .unwrap() }; let input_to_geo = geo_scale * geo_translation; From 1f48a5e86621d161c47b96565d504c3eff8d8e35 Mon Sep 17 00:00:00 2001 From: Victoria Brekenfeld Date: Fri, 12 Jun 2026 18:18:34 +0200 Subject: [PATCH 50/92] shell/menu: Use new MenuColumn widget --- src/shell/grabs/menu/item.rs | 21 ++++++++++++++++++--- src/shell/grabs/menu/mod.rs | 29 +++++++++++++++++------------ 2 files changed, 35 insertions(+), 15 deletions(-) diff --git a/src/shell/grabs/menu/item.rs b/src/shell/grabs/menu/item.rs index 90614f6b..f254df72 100644 --- a/src/shell/grabs/menu/item.rs +++ b/src/shell/grabs/menu/item.rs @@ -1,6 +1,6 @@ use cosmic::{ iced::{ - Element, + Alignment, Element, core::{ Background, Border, Clipboard, Color, Event, Layout, Length, Rectangle, Renderer as IcedRenderer, Shell, Size, layout, mouse, overlay, @@ -75,11 +75,26 @@ where viewport: &Rectangle, ) { let widget_state = state.state.downcast_ref::(); - let styling = if widget_state.cursor_over { + let mut styling = if widget_state.cursor_over { theme.hovered(true, false, &self.styling) } else { theme.active(true, false, &self.styling) }; + if matches!(self.styling, cosmic::theme::Button::MenuItem) { + match theme.list_item_position { + Some((Alignment::Start, _)) => { + styling.border_radius = + styling.border_radius.bottom(theme.cosmic().radius_0()[3]); + } + Some((Alignment::End, _)) => { + styling.border_radius = styling.border_radius.top(theme.cosmic().radius_0()[0]); + } + Some((Alignment::Center, _)) => {} + None => { + styling.border_radius = theme.cosmic().radius_0().into(); + } + }; + } renderer.fill_quad( Quad { @@ -87,7 +102,7 @@ where bounds: layout.bounds(), border: Border { radius: styling.border_radius, - width: styling.border_width, + width: 0., color: styling.border_color, }, shadow: Default::default(), diff --git a/src/shell/grabs/menu/mod.rs b/src/shell/grabs/menu/mod.rs index efd66886..46639631 100644 --- a/src/shell/grabs/menu/mod.rs +++ b/src/shell/grabs/menu/mod.rs @@ -12,10 +12,10 @@ use cosmic::{ iced::{ Alignment, Background, core::{Border, Length, Rectangle as IcedRectangle, alignment::Horizontal}, - widget::{self as iced_widget, Column, Row, text::Style as TextStyle}, + widget::{self as iced_widget, Row, text::Style as TextStyle}, }, theme, - widget::{button, divider, icon::from_name, space, text}, + widget::{button, divider, icon::from_name, menu::menu_column::MenuColumn, space, text}, }; use smithay::{ backend::{ @@ -300,7 +300,7 @@ impl Program for ContextMenu { Rectangle::new( position + Point::from(( - bounds.width.ceil() as i32, + bounds.width.floor() as i32, bounds.y.ceil() as i32, )), min_size.as_global(), @@ -309,7 +309,7 @@ impl Program for ContextMenu { Rectangle::new( position + Point::from(( - bounds.width.ceil() as i32, + bounds.width.floor() as i32, bounds.y.ceil() as i32 + bounds.height.ceil() as i32 - min_size.h, )), @@ -317,14 +317,15 @@ impl Program for ContextMenu { ), // to the left -> down Rectangle::new( - position + Point::from((-min_size.w, bounds.y.ceil() as i32)), + position + + Point::from((-min_size.w + 1, bounds.y.ceil() as i32)), min_size.as_global(), ), // to the left -> up Rectangle::new( position + Point::from(( - -min_size.w, + -min_size.w + 1, bounds.y.ceil() as i32 + bounds.height.ceil() as i32 - min_size.h, )), @@ -390,9 +391,11 @@ impl Program for ContextMenu { _ => Length::Fill, }; - Column::with_children(self.items.iter().enumerate().map(|(idx, item)| { + MenuColumn::with_children(self.items.iter().enumerate().map(|(idx, item)| { match item { - Item::Separator => divider::horizontal::light().into(), + Item::Separator => divider::horizontal::light() + .class(theme::Rule::Default) + .into(), Item::Submenu { title, .. } => Row::with_children(vec![ space::horizontal().width(16).into(), text::body(title).width(mode).into(), @@ -561,8 +564,11 @@ impl PointerGrab for MenuGrab { PointerTarget::motion(&element.iced, &self.seat, state, &new_event); } } else { - elements.iter_mut().for_each(|element| { - if element.pointer_entered { + elements + .iter_mut() + .filter(|element| element.pointer_entered) + .skip(1) + .for_each(|element| { PointerTarget::leave( &element.iced, &self.seat, @@ -571,8 +577,7 @@ impl PointerGrab for MenuGrab { event.time, ); element.pointer_entered = false; - } - }) + }) } } handle.motion(state, None, event); From 3e17157ccf60c41d35904564151e667b6270da00 Mon Sep 17 00:00:00 2001 From: Victoria Brekenfeld Date: Mon, 6 Jul 2026 16:22:18 +0200 Subject: [PATCH 51/92] chore: update smithay --- Cargo.lock | 2 +- Cargo.toml | 2 +- src/wayland/protocols/drm.rs | 2 +- 3 files changed, 3 insertions(+), 3 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index b58cd62c..27e8287e 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -4813,7 +4813,7 @@ checksum = "8ed6a63f02c8539c91a8685a86f4099661ba3da017932f6ebbea6de3f0fa7c90" [[package]] name = "smithay" version = "0.7.0" -source = "git+https://github.com/smithay/smithay.git?rev=8eb4076#8eb4076cad8705c56f5e3bac8577b83576b6ce76" +source = "git+https://github.com/smithay/smithay.git?rev=efeb597#efeb59768eb9a2d8dae9f71300a56aef06b0c1c4" dependencies = [ "aliasable", "appendlist", diff --git a/Cargo.toml b/Cargo.toml index 27f79056..b5ebbb06 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -144,4 +144,4 @@ lto = "fat" cosmic-protocols = { git = "https://github.com/pop-os//cosmic-protocols", branch = "main" } [patch.crates-io] -smithay = { git = "https://github.com/smithay/smithay.git", rev = "8eb4076" } +smithay = { git = "https://github.com/smithay/smithay.git", rev = "efeb597" } diff --git a/src/wayland/protocols/drm.rs b/src/wayland/protocols/drm.rs index 87eeb0db..c86e5ff0 100644 --- a/src/wayland/protocols/drm.rs +++ b/src/wayland/protocols/drm.rs @@ -180,7 +180,7 @@ where Modifier::Invalid, DmabufFlags::empty(), ); - dma.add_plane(name, 0, offset0 as u32, stride0 as u32); + dma.add_plane(name, offset0 as u32, stride0 as u32); match dma.build() { Some(dmabuf) => { match state.dmabuf_imported(&data.dmabuf_global, dmabuf.clone()) { From 2971845d400371034ec8ab48b1f060294c1220dd Mon Sep 17 00:00:00 2001 From: Victoria Brekenfeld Date: Wed, 8 Jul 2026 14:53:10 +0200 Subject: [PATCH 52/92] render/blur: Fix rotated outputs having cut off blur regions --- src/backend/render/wayland/blur_effect.rs | 45 +++++++++++++---------- 1 file changed, 25 insertions(+), 20 deletions(-) diff --git a/src/backend/render/wayland/blur_effect.rs b/src/backend/render/wayland/blur_effect.rs index 243cd510..bb667af1 100644 --- a/src/backend/render/wayland/blur_effect.rs +++ b/src/backend/render/wayland/blur_effect.rs @@ -489,10 +489,8 @@ fn blit_from_active_fb( transform: Transform, to_texture: &mut GlesTexture, ) -> Result { - let tex_size = to_texture - .size() - .to_logical(1, Transform::Normal) - .to_physical(1); + let tex_size = to_texture.size(); + let tex_size_phys = tex_size.to_logical(1, Transform::Normal).to_physical(1); let fb_size = frame.output_size(); let mut renderer = frame.renderer(); @@ -500,27 +498,31 @@ fn blit_from_active_fb( let sync = { let mut subframe = renderer .as_mut() - .render(&mut fb, tex_size, Transform::Normal)?; - subframe.clear(Color32F::TRANSPARENT, &[Rectangle::from_size(tex_size)])?; + .render(&mut fb, tex_size_phys, Transform::Normal)?; + subframe.clear( + Color32F::TRANSPARENT, + &[Rectangle::from_size(tex_size_phys)], + )?; subframe.finish()? }; if transform != Transform::Normal { - let dst = dst + // We need to copy to a temporary texture to do an actual + // render pass with `render_texture_from_to` to do the rotation. + // dst is in screen space, but we just want to do a 1:1 copy in + // buffer space from dst of the current fb, so we need to undo any + // transforms for the blit. + let dst_phys = transform.transform_rect_in(dst, &fb_size); + let dst_buffer = dst_phys .to_logical(1) - .to_buffer(1, transform, &fb_size.to_logical(1)); + .to_buffer(1, Transform::Normal, &Size::default()); let mut tmp_texture = renderer .as_mut() - .create_buffer(Fourcc::Abgr8888, dst.size)?; + .create_buffer(Fourcc::Abgr8888, dst_buffer.size)?; let mut fb_tmp = renderer.as_mut().bind(&mut tmp_texture)?; std::mem::drop(renderer); frame.wait(&sync)?; - // TODO: Why does blit not take buffer coordinates? - let dst_phys = Rectangle::<_, Physical>::new( - Point::new(dst.loc.x, dst.loc.y), - dst.size.to_logical(1, Transform::Normal).to_physical(1), - ); let sync = frame.blit_to( &mut fb_tmp, dst_phys, @@ -530,20 +532,23 @@ fn blit_from_active_fb( frame.wait(&sync)?; std::mem::drop(fb_tmp); + // now we bind the target texture with `Transform::Normal` + // and render the temporary texture with inverse transform + // into src. let mut renderer = frame.renderer(); let mut frame = renderer .as_mut() - .render(&mut fb, tex_size, Transform::Normal)?; + .render(&mut fb, tex_size_phys, Transform::Normal)?; frame.wait(&sync)?; Frame::render_texture_from_to( &mut frame, &tmp_texture, - Rectangle::from_size(dst.size.to_f64()), - src.to_logical(1., Transform::Normal, &src.size) + Rectangle::from_size(dst_buffer.size.to_f64()), + src.to_logical(1., Transform::Normal, &Size::default()) .to_physical(1.) .to_i32_round(), - &[Rectangle::from_size(dst_phys.size)], - &[Rectangle::from_size(dst_phys.size)], + &[Rectangle::from_size(dst.size)], + &[Rectangle::from_size(dst.size)], transform.invert(), 1.0, )?; @@ -555,7 +560,7 @@ fn blit_from_active_fb( frame.blit_to( &mut fb, dst, - src.to_logical(1., Transform::Normal, &src.size) + src.to_logical(1., Transform::Normal, &Size::default()) .to_physical(1.) .to_i32_round(), TextureFilter::Linear, From 693d1ad775193deb95a33810b5eac59684ca2ac0 Mon Sep 17 00:00:00 2001 From: Victoria Brekenfeld Date: Wed, 8 Jul 2026 15:23:00 +0200 Subject: [PATCH 53/92] shell/window: Fix radii for server-side decorations --- src/shell/element/window.rs | 3 +++ 1 file changed, 3 insertions(+) diff --git a/src/shell/element/window.rs b/src/shell/element/window.rs index f6c9e1c4..324299e0 100644 --- a/src/shell/element/window.rs +++ b/src/shell/element/window.rs @@ -549,6 +549,7 @@ impl CosmicWindow { } self.0.with_program(|p| { + let mut radii = radii; if has_ssd { radii[1] = 0; radii[3] = 0; @@ -575,6 +576,8 @@ impl CosmicWindow { }); if has_ssd { + radii[0] = 0; + radii[2] = 0; let ssd_loc = location + self .0 From 01c0cb020e9fce991254b1d1e6a6485be6046dde Mon Sep 17 00:00:00 2001 From: Tom Grushka Date: Tue, 7 Jul 2026 19:45:41 -0600 Subject: [PATCH 54/92] fix: a11y: magnifier: correct Centered/OnEdge focal-point math and refresh focal point at zoom-in - I used AI to assist in research of the focal point computation formulae for OnEdge and Centered modes. - I used the formulas found in [KDE's zoom magnifier](https://invent.kde.org/plasma/kwin/-/blob/master/src/plugins/zoom/zoom.cpp) and converted them to Rust and the COSMIC focal point system. - Remove unnecessary focal-point precomputation in `OutputZoomState::new`, because it is now updated on first zoom in anyway. - Do focal point computation in Local coordinates in update_focal_point to eliminate local-global conversion confusion/ issues. - ZoomMovement::OnEdge - single code path rewrite - old two-branch logic caused jumping and unpredictability at high zoom. Fixes [#2683](https://github.com/pop-os/cosmic-epoch/issues/2683), partially addresses [#2688](https://github.com/pop-os/cosmic-epoch/issues/2688). - ZoomMovement::Centered - translation/clamp/invert - prevents "blow up" of calculation at zoom level > 2.0 that caused cursor to disappear off bottom right of screen. Fixes [#1503](https://github.com/pop-os/cosmic-comp/issues/1503). - `is_level_change` - sets `previous_point` so that `animating_focal_point` interpolates instead of jumping. Still room for improvement (needs focal point recomputation per-frame during animation to prevent "boomerang" feel), but this is still an improvement over jumping. - In `trigger_zoom`, call `update_focal_point` when `level > 1.0` - overlaps proposal in [#1859](https://github.com/pop-os/cosmic-comp/pull/1859) + the level gate to prevent stale focal point on zoom in. Fixes [#2511](https://github.com/pop-os/cosmic-comp/issues/2511) and [#1883](https://github.com/pop-os/cosmic-epoch/issues/1883). Partially addresses [#1420](https://github.com/pop-os/cosmic-comp/issues/1420) (item 1 only). --- src/shell/mod.rs | 8 +++ src/shell/zoom.rs | 165 +++++++++++++++++++++------------------------- 2 files changed, 83 insertions(+), 90 deletions(-) diff --git a/src/shell/mod.rs b/src/shell/mod.rs index 8e628985..83845598 100644 --- a/src/shell/mod.rs +++ b/src/shell/mod.rs @@ -2490,6 +2490,14 @@ impl Shell { increment: zoom_config.increment, movement: zoom_config.view_moves, }); + + if level > 1.0 { + self.update_focal_point( + seat, + seat.get_pointer().unwrap().current_location().as_global(), + zoom_config.view_moves, + ); + } } pub fn update_focal_point( diff --git a/src/shell/zoom.rs b/src/shell/zoom.rs index 11114e2d..1d5bbf1e 100644 --- a/src/shell/zoom.rs +++ b/src/shell/zoom.rs @@ -27,7 +27,7 @@ use smithay::{ }, }, output::Output, - utils::{IsAlive, Point, Rectangle, Serial, Size}, + utils::{FrameExtents, IsAlive, Point, Rectangle, Serial, Size}, }; use tracing::error; @@ -76,36 +76,8 @@ impl OutputZoomState { ) -> OutputZoomState { theme.transparent = theme.cosmic().frosted_system_interface; let cursor_position = seat.get_pointer().unwrap().current_location().as_global(); + let focal_point = cursor_position.to_local(output); let output_geometry = output.geometry().to_f64(); - let focal_point = if output_geometry.contains(cursor_position) { - match movement { - ZoomMovement::Continuously | ZoomMovement::OnEdge => { - cursor_position.to_local(output) - } - ZoomMovement::Centered => { - let mut zoomed_output_geometry = output.geometry().to_f64().downscale(level); - zoomed_output_geometry.loc = - cursor_position - zoomed_output_geometry.size.downscale(2.).to_point(); - - let mut focal_point = zoomed_output_geometry - .loc - .to_local(output) - .upscale(level) - .to_global(output); - focal_point.x = focal_point.x.clamp( - output_geometry.loc.x, - (output_geometry.loc.x + output_geometry.size.w).next_down(), - ); - focal_point.y = focal_point.y.clamp( - output_geometry.loc.y, - (output_geometry.loc.y + output_geometry.size.h).next_down(), - ); - focal_point.to_local(output) - } - } - } else { - (output_geometry.size.w / 2., output_geometry.size.h / 2.).into() - }; let program = ZoomProgram::new(level, movement, increment); let element = IcedElement::new(program, Size::default(), loop_handle, theme); @@ -269,86 +241,99 @@ impl ZoomState { &mut self, output: &Output, cursor_position: Point, - original_position: Point, + _original_position: Point, movement: ZoomMovement, ) { - let output_geometry = output.geometry().to_f64(); - let mut zoomed_output_geometry = output.zoomed_geometry().unwrap().to_f64(); + let output_geometry = output.geometry().to_f64().to_local(output); + let zoomed_output_geometry = output.zoomed_geometry().unwrap().to_f64().to_local(output); let output_state = output.user_data().get::>().unwrap(); let mut output_state_ref = output_state.lock().unwrap(); - // animate movement type changes - if self.movement != movement { + let level = output_state_ref.current_level(); + + let is_level_change = output_state_ref + .previous_level + .is_some_and(|prev| prev.0 != level); + + // animate level and movement type changes + if is_level_change || self.movement != movement { output_state_ref.previous_point = Some((output_state_ref.focal_point, Instant::now())); self.movement = movement; } let cursor_position = cursor_position.to_local(output); match movement { - ZoomMovement::Continuously => output_state_ref.focal_point = cursor_position, - ZoomMovement::OnEdge => { - if !zoomed_output_geometry - .overlaps_or_touches(Rectangle::new(original_position, Size::from((16., 16.)))) - { - zoomed_output_geometry.loc = cursor_position.to_global(output) - - zoomed_output_geometry.size.downscale(2.).to_point(); - let mut focal_point = zoomed_output_geometry - .loc - .to_local(output) - .upscale(output_state_ref.level) - .to_global(output); - focal_point.x = focal_point.x.clamp( - output_geometry.loc.x, - output_geometry.loc.x + output_geometry.size.w - 1., - ); - focal_point.y = focal_point.y.clamp( - output_geometry.loc.y, - output_geometry.loc.y + output_geometry.size.h - 1., - ); - output_state_ref.previous_point = - Some((output_state_ref.focal_point, Instant::now())); - output_state_ref.focal_point = focal_point.to_local(output); - } else if !zoomed_output_geometry.contains(cursor_position.to_global(output)) { - let mut diff = output_state_ref.focal_point.to_global(output) - + (cursor_position.to_global(output) - original_position) - .upscale(output_state_ref.level); - diff.x = diff.x.clamp( - output_geometry.loc.x, - (output_geometry.loc.x + output_geometry.size.w).next_down(), - ); - diff.y = diff.y.clamp( - output_geometry.loc.y, - (output_geometry.loc.y + output_geometry.size.h).next_down(), - ); - diff -= output_state_ref.focal_point.to_global(output); - - output_state_ref.focal_point += diff.as_logical().as_local(); - } + ZoomMovement::Continuously => { + // Focal point is the cursor position + output_state_ref.focal_point = cursor_position; } - ZoomMovement::Centered => { - zoomed_output_geometry.loc = cursor_position.to_global(output) - - zoomed_output_geometry.size.downscale(2.).to_point(); + ZoomMovement::OnEdge => { + if is_level_change { + output_state_ref.focal_point = cursor_position; + return; + } - let mut focal_point = zoomed_output_geometry - .loc - .to_local(output) - .upscale( - (output_geometry.size.w - / (output_geometry.size.w - zoomed_output_geometry.size.w) - .max(f64::EPSILON)) - .max(1.), - ) - .to_global(output); + // Compute small margin relative to zoomed output to keep cursor within + let margin_size = zoomed_output_geometry.size.h * 0.02; + let margins = FrameExtents::new(margin_size, margin_size, margin_size, margin_size); + let inner_rect = zoomed_output_geometry - margins; + + if inner_rect.contains(cursor_position) { + // Do not move if cursor within margins + return; + } + + // Compute dx and dy to move the zoomed output based on cursor distance outside margin(s) + let dx = if cursor_position.x < inner_rect.loc.x { + cursor_position.x - inner_rect.loc.x + } else if cursor_position.x > inner_rect.loc.x + inner_rect.size.w { + cursor_position.x - (inner_rect.loc.x + inner_rect.size.w) + } else { + 0.0 + }; + let dy = if cursor_position.y < inner_rect.loc.y { + cursor_position.y - inner_rect.loc.y + } else if cursor_position.y > inner_rect.loc.y + inner_rect.size.h { + cursor_position.y - (inner_rect.loc.y + inner_rect.size.h) + } else { + 0.0 + }; + + let mut focal_point = output_state_ref.focal_point + Point::new(dx, dy); + + // Clamp the final focal point to output geometry focal_point.x = focal_point.x.clamp( output_geometry.loc.x, - output_geometry.loc.x + output_geometry.size.w - 1., + output_geometry.loc.x + output_geometry.size.w - 1.0, ); focal_point.y = focal_point.y.clamp( output_geometry.loc.y, - output_geometry.loc.y + output_geometry.size.h - 1., + output_geometry.loc.y + output_geometry.size.h - 1.0, ); - output_state_ref.focal_point = focal_point.to_local(output); + + output_state_ref.focal_point = focal_point; + } + ZoomMovement::Centered => { + let center = (output_geometry.size / 2.).to_point(); + + if level <= 1.0 + f64::EPSILON { + // Without this break, focal point will jump to (0, 0) on zoom out + output_state_ref.focal_point = center; + return; + } + + // Compute translation to keep cursor at center of screen + let mut tx = center.x - cursor_position.x * level; + let mut ty = center.y - cursor_position.y * level; + + // Clamp translation to keep viewport within screen bounds + tx = tx.clamp(output_geometry.size.w * (1.0 - level), 0.0); + ty = ty.clamp(output_geometry.size.h * (1.0 - level), 0.0); + + // Convert translation back to focal point: T = F * (1 - level) + output_state_ref.focal_point = + Point::from((tx / (1.0 - level), ty / (1.0 - level))); } } } From 4d14f5568fcfda61c1c9aa01d4a305d0de89cfb8 Mon Sep 17 00:00:00 2001 From: Tom Grushka Date: Tue, 7 Jul 2026 20:03:28 -0600 Subject: [PATCH 55/92] improve comments --- src/shell/zoom.rs | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/src/shell/zoom.rs b/src/shell/zoom.rs index 1d5bbf1e..a8da3561 100644 --- a/src/shell/zoom.rs +++ b/src/shell/zoom.rs @@ -270,11 +270,13 @@ impl ZoomState { } ZoomMovement::OnEdge => { if is_level_change { + // Ensure the cursor doesn't disappear off screen output_state_ref.focal_point = cursor_position; return; } // Compute small margin relative to zoomed output to keep cursor within + // (can be user-configurable in the future) let margin_size = zoomed_output_geometry.size.h * 0.02; let margins = FrameExtents::new(margin_size, margin_size, margin_size, margin_size); let inner_rect = zoomed_output_geometry - margins; @@ -318,7 +320,7 @@ impl ZoomState { let center = (output_geometry.size / 2.).to_point(); if level <= 1.0 + f64::EPSILON { - // Without this break, focal point will jump to (0, 0) on zoom out + // Prevent focal point jumping to top-left corner (0, 0) on zoom out output_state_ref.focal_point = center; return; } From 0596cce40d93df7518738ae9f8c631fa413ac4a9 Mon Sep 17 00:00:00 2001 From: Tom Grushka Date: Wed, 8 Jul 2026 14:43:32 -0600 Subject: [PATCH 56/92] remove redundant level condition; simplify level early return on ZoomMovement::Centered --- src/shell/mod.rs | 12 +++++------- src/shell/zoom.rs | 2 +- 2 files changed, 6 insertions(+), 8 deletions(-) diff --git a/src/shell/mod.rs b/src/shell/mod.rs index 83845598..326b064b 100644 --- a/src/shell/mod.rs +++ b/src/shell/mod.rs @@ -2491,13 +2491,11 @@ impl Shell { movement: zoom_config.view_moves, }); - if level > 1.0 { - self.update_focal_point( - seat, - seat.get_pointer().unwrap().current_location().as_global(), - zoom_config.view_moves, - ); - } + self.update_focal_point( + seat, + seat.get_pointer().unwrap().current_location().as_global(), + zoom_config.view_moves, + ); } pub fn update_focal_point( diff --git a/src/shell/zoom.rs b/src/shell/zoom.rs index a8da3561..ef81053f 100644 --- a/src/shell/zoom.rs +++ b/src/shell/zoom.rs @@ -319,7 +319,7 @@ impl ZoomState { ZoomMovement::Centered => { let center = (output_geometry.size / 2.).to_point(); - if level <= 1.0 + f64::EPSILON { + if level == 1.0 { // Prevent focal point jumping to top-left corner (0, 0) on zoom out output_state_ref.focal_point = center; return; From 7a7c6b7fd42e0941b0742314541dcefdacb08fa4 Mon Sep 17 00:00:00 2001 From: Ilia Malanin Date: Sun, 21 Jun 2026 13:06:09 +0200 Subject: [PATCH 57/92] fix: take into account pending maximized state when recalculating window geometry --- src/shell/layout/floating/mod.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/shell/layout/floating/mod.rs b/src/shell/layout/floating/mod.rs index 07ce73cf..2102ddcd 100644 --- a/src/shell/layout/floating/mod.rs +++ b/src/shell/layout/floating/mod.rs @@ -1350,7 +1350,7 @@ impl FloatingLayout { mapped.set_bounds(geometry.size.as_logical()); let prev = self.space.element_geometry(&mapped).map(RectExt::as_local); - let window_geometry = if mapped.is_maximized(false) { + let window_geometry = if mapped.is_maximized(true) { geometry } else { prev.map(|mut rect| { From 7ee78d9c1369033aa4f963e22d2864bbcda1fbff Mon Sep 17 00:00:00 2001 From: Erik Date: Sun, 14 Jun 2026 02:54:22 +0600 Subject: [PATCH 58/92] Forward axis relative direction to Wayland clients. MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Libinput reports natural scrolling via AxisRelativeDirection. Qt sets QWheelEvent::inverted() from wl_pointer.axis_relative_direction. COSMIC was forwarding axis values but not the direction hint. Tested on Fedora 44 COSMIC Wayland with Telegram Desktop (native Wayland), natural scrolling enabled — horizontal touchpad swipes fixed. Assisted-by: AI (root-cause analysis) Signed-off-by: Erik --- src/input/mod.rs | 12 ++++++++++-- 1 file changed, 10 insertions(+), 2 deletions(-) diff --git a/src/input/mod.rs b/src/input/mod.rs index 1114d50b..30e5d4f8 100644 --- a/src/input/mod.rs +++ b/src/input/mod.rs @@ -1003,6 +1003,10 @@ impl State { if let Some(horizontal_amount) = event.amount(Axis::Horizontal) { if horizontal_amount != 0.0 { frame = frame + .relative_direction( + Axis::Horizontal, + event.relative_direction(Axis::Horizontal), + ) .value(Axis::Horizontal, scroll_factor * horizontal_amount); if let Some(discrete) = event.amount_v120(Axis::Horizontal) { frame = frame.v120( @@ -1016,8 +1020,12 @@ impl State { } if let Some(vertical_amount) = event.amount(Axis::Vertical) { if vertical_amount != 0.0 { - frame = - frame.value(Axis::Vertical, scroll_factor * vertical_amount); + frame = frame + .relative_direction( + Axis::Vertical, + event.relative_direction(Axis::Vertical), + ) + .value(Axis::Vertical, scroll_factor * vertical_amount); if let Some(discrete) = event.amount_v120(Axis::Vertical) { frame = frame.v120( Axis::Vertical, From 603c90b103ba8eed1a27b0e176d62fa642703618 Mon Sep 17 00:00:00 2001 From: Victoria Brekenfeld Date: Tue, 14 Jul 2026 13:17:06 +0200 Subject: [PATCH 59/92] element/surface: Avoid scanning out translucent surfaces --- src/shell/element/surface.rs | 52 +++++++++++++++++++++++++++++++----- 1 file changed, 45 insertions(+), 7 deletions(-) diff --git a/src/shell/element/surface.rs b/src/shell/element/surface.rs index 4db3e60a..182138fc 100644 --- a/src/shell/element/surface.rs +++ b/src/shell/element/surface.rs @@ -4,7 +4,10 @@ use crate::{ wayland::{SurfaceRenderElement, push_render_elements_from_surface_tree}, }, shell::focus::target::PointerFocusTarget, - wayland::handlers::{compositor::frame_time_filter_fn, corner_radius::surface_corners}, + wayland::handlers::{ + background_effect::ComputedBlurRegionCachedState, compositor::frame_time_filter_fn, + corner_radius::surface_corners, + }, }; use std::{ borrow::Cow, @@ -19,7 +22,7 @@ use smithay::{ backend::{ drm::DrmNode, renderer::{ - ImportAll, Renderer, + ImportAll, Renderer, buffer_has_alpha, element::{Kind, RenderElementStates, surface::KindEvaluation}, utils::RendererSurfaceStateUserData, }, @@ -48,6 +51,7 @@ use smithay::{ IsAlive, Logical, Physical, Point, Rectangle, Scale, Serial, Size, user_data::UserDataMap, }, wayland::{ + alpha_modifier::AlphaModifierSurfaceCachedState, compositor::{ SubsurfaceCachedState, SurfaceData, TraversalAction, get_parent, with_states, with_surface_tree_downward, @@ -81,6 +85,39 @@ fn buffer_node(data: &SurfaceData) -> Option { .and_then(|dmabuf| dmabuf.node()) } +fn is_likely_translucent(alpha: f32, data: &SurfaceData) -> bool { + if alpha < 1.0 { + return true; + } + + let mut alpha_modifier_state = data.cached_state.get::(); + let alpha_multiplier = alpha_modifier_state + .current() + .multiplier_f32() + .unwrap_or(1.0); + if alpha_multiplier < 1.0 { + return true; + } + + let Some(surface_state) = data.data_map.get::() else { + return false; + }; + let surface_state = surface_state.lock().unwrap(); + if surface_state + .buffer() + .is_none_or(|buffer| !buffer_has_alpha(buffer).unwrap_or(true)) + { + return false; + } + + let mut blur_state = data.cached_state.get::(); + blur_state + .current() + .blur_region + .as_ref() + .is_none_or(|region| region.is_empty()) +} + /// Build the [`KindEvaluation`] for a window's surface tree. /// /// `scanout_node`, when set, is the scan-out target [`DrmNode`] of the output currently being @@ -89,6 +126,7 @@ fn buffer_node(data: &SurfaceData) -> Option { fn scanout_kind_eval( scanout_override: Option, scanout_node: Option, + alpha: f32, ) -> KindEvaluation { match (scanout_override, scanout_node) { // Forced off. @@ -98,14 +136,14 @@ fn scanout_kind_eval( (None, None) => FRAME_TIME_FILTER, // Node restriction in effect: only buffers on the scan-out node may be candidates. (Some(true), Some(node)) => KindEvaluation::Closure(Box::new(move |data| { - if buffer_node(data) == Some(node) { + if buffer_node(data) == Some(node) && !is_likely_translucent(alpha, data) { Kind::ScanoutCandidate } else { Kind::Unspecified } })), (None, Some(node)) => KindEvaluation::Closure(Box::new(move |data| { - if buffer_node(data) == Some(node) { + if buffer_node(data) == Some(node) && !is_likely_translucent(alpha, data) { frame_time_filter_fn(data) } else { Kind::Unspecified @@ -879,7 +917,7 @@ impl CosmicSurface { radii, None, blur_strength, - scanout_kind_eval(None, scanout_node), + scanout_kind_eval(None, scanout_node, alpha), push, None, ) @@ -924,7 +962,7 @@ impl CosmicSurface { radii, None, blur_strength, - scanout_kind_eval(scanout_override, scanout_node), + scanout_kind_eval(scanout_override, scanout_node, alpha), push_above, push_below, ) @@ -945,7 +983,7 @@ impl CosmicSurface { radii, None, blur_strength, - scanout_kind_eval(scanout_override, scanout_node), + scanout_kind_eval(scanout_override, scanout_node, alpha), push_above, push_below, ) From dec1ee863368737c6900e1ae75424813e27e24af Mon Sep 17 00:00:00 2001 From: Victoria Brekenfeld <4404502+Drakulix@users.noreply.github.com> Date: Tue, 14 Jul 2026 17:14:28 +0200 Subject: [PATCH 60/92] fixup: logic error --- src/shell/element/surface.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/shell/element/surface.rs b/src/shell/element/surface.rs index 182138fc..e146efeb 100644 --- a/src/shell/element/surface.rs +++ b/src/shell/element/surface.rs @@ -115,7 +115,7 @@ fn is_likely_translucent(alpha: f32, data: &SurfaceData) -> bool { .current() .blur_region .as_ref() - .is_none_or(|region| region.is_empty()) + .is_some_and(|region| !region.is_empty()) } /// Build the [`KindEvaluation`] for a window's surface tree. From 8f82cade680410938dc74170c5633311a8faa90a Mon Sep 17 00:00:00 2001 From: Ashley Wulber Date: Fri, 10 Jul 2026 13:46:56 -0400 Subject: [PATCH 61/92] cleanup: remove attempts to force redraw They seem to always fail anyways and it has no negative effect as far as I can tell --- src/wayland/handlers/corner_radius.rs | 54 ++------------------------- 1 file changed, 4 insertions(+), 50 deletions(-) diff --git a/src/wayland/handlers/corner_radius.rs b/src/wayland/handlers/corner_radius.rs index 9618e77e..8a316d2a 100644 --- a/src/wayland/handlers/corner_radius.rs +++ b/src/wayland/handlers/corner_radius.rs @@ -13,59 +13,13 @@ impl CornerRadiusHandler for State { &mut self.common.corner_radius_state } - fn set_corner_radius(&mut self, data: &CornerRadiusData) { - if force_redraw(self, data).is_none() { - tracing::warn!("Failed to force redraw for corner radius change."); - } - } + fn set_corner_radius(&mut self, _data: &CornerRadiusData) {} - fn unset_corner_radius(&mut self, data: &CornerRadiusData) { - if force_redraw(self, data).is_none() { - tracing::warn!("Failed to force redraw for corner radius reset."); - } - } + fn unset_corner_radius(&mut self, _data: &CornerRadiusData) {} - fn set_padding(&mut self, data: &CornerRadiusData) { - if force_redraw(self, data).is_none() { - tracing::warn!("Failed to force redraw for corner radius change."); - } - } + fn set_padding(&mut self, _data: &CornerRadiusData) {} - fn unset_padding(&mut self, data: &CornerRadiusData) { - if force_redraw(self, data).is_none() { - tracing::warn!("Failed to force redraw for corner radius reset."); - } - } -} - -fn force_redraw(state: &mut State, data: &CornerRadiusData) -> Option<()> { - let guard = data.lock().unwrap(); - let shell = state.common.shell.read(); - - let output = match &guard.surface { - CornerRadiusSurface::Toplevel(toplevel) => { - let toplevel = toplevel.upgrade().ok()?; - let surface = state.common.xdg_shell_state.get_toplevel(&toplevel)?; - shell.visible_output_for_surface(surface.wl_surface())? - } - CornerRadiusSurface::Popup(popup) => { - let popup = popup.upgrade().ok()?; - let surface = state.common.xdg_shell_state.get_popup(&popup)?; - shell.visible_output_for_surface(surface.wl_surface())? - } - CornerRadiusSurface::Layer(layer) => { - let layer = layer.upgrade().ok()?; - let surface = state - .common - .layer_shell_state - .layer_surfaces() - .find(|l| l.shell_surface() == &layer)?; - shell.visible_output_for_surface(surface.wl_surface())? - } - }; - - state.backend.schedule_render(output); - Some(()) + fn unset_padding(&mut self, _data: &CornerRadiusData) {} } pub fn surface_corners(states: &SurfaceData, size: Size) -> Option<[u8; 4]> { From b8807a541f151b430d820f26a58f1f1b168ca818 Mon Sep 17 00:00:00 2001 From: Graham Walker Date: Tue, 14 Jul 2026 21:05:01 -0400 Subject: [PATCH 62/92] Fix detached active window hint --- src/shell/layout/floating/mod.rs | 87 ++++++++++++++++---------------- 1 file changed, 44 insertions(+), 43 deletions(-) diff --git a/src/shell/layout/floating/mod.rs b/src/shell/layout/floating/mod.rs index 2102ddcd..95d7264d 100644 --- a/src/shell/layout/floating/mod.rs +++ b/src/shell/layout/floating/mod.rs @@ -1522,49 +1522,6 @@ impl FloatingLayout { .unwrap_or_else(|| (self.space.element_geometry(elem).unwrap().as_local(), alpha)); let render_location = geometry.loc - elem.geometry().loc.as_local(); - if focused == Some(elem) && !elem.is_maximized(false) { - let active_window_hint = crate::theme::active_window_hint(theme); - let radius = elem.corner_radius(geometry.size.as_logical(), indicator_thickness); - - if let Some((mode, resize)) = resize_indicator.as_mut() { - let mut resize_geometry = geometry; - resize_geometry.loc -= (18, 18).into(); - resize_geometry.size += (36, 36).into(); - - resize.resize(resize_geometry.size.as_logical()); - resize.output_enter(output); - resize.push_render_elements( - renderer, - resize_geometry - .loc - .as_logical() - .to_physical_precise_round(output_scale), - output_scale.into(), - alpha * mode.alpha().unwrap_or(1.0), - &mut |elem| push(CosmicMappedRenderElement::Window(elem.into())), - None, - ); - } - - if indicator_thickness > 0 { - let element = IndicatorShader::focus_element( - renderer, - Key::Window(Usage::FocusIndicator, elem.key()), - geometry, - indicator_thickness, - radius, - alpha, - output_scale, - [ - active_window_hint.red, - active_window_hint.green, - active_window_hint.blue, - ], - ); - push(element.into()); - } - } - let maybe_map = if let Some(anim) = self.animations.get(elem) { let original_geo = anim.previous_geometry(); geometry = anim.geometry( @@ -1629,6 +1586,50 @@ impl FloatingLayout { } else { None }; + + if focused == Some(elem) && !elem.is_maximized(false) { + let active_window_hint = crate::theme::active_window_hint(theme); + let radius = elem.corner_radius(geometry.size.as_logical(), indicator_thickness); + + if let Some((mode, resize)) = resize_indicator.as_mut() { + let mut resize_geometry = geometry; + resize_geometry.loc -= (18, 18).into(); + resize_geometry.size += (36, 36).into(); + + resize.resize(resize_geometry.size.as_logical()); + resize.output_enter(output); + resize.push_render_elements( + renderer, + resize_geometry + .loc + .as_logical() + .to_physical_precise_round(output_scale), + output_scale.into(), + alpha * mode.alpha().unwrap_or(1.0), + &mut |elem| push(CosmicMappedRenderElement::Window(elem.into())), + None, + ); + } + + if indicator_thickness > 0 { + let element = IndicatorShader::focus_element( + renderer, + Key::Window(Usage::FocusIndicator, elem.key()), + geometry, + indicator_thickness, + radius, + alpha, + output_scale, + [ + active_window_hint.red, + active_window_hint.green, + active_window_hint.blue, + ], + ); + push(element.into()); + } + } + let map_anim = |elem| { if let Some(map) = maybe_map { map(elem) From 2329cea5c9f27145cb45aa0d004f14d5020357ea Mon Sep 17 00:00:00 2001 From: Hojjat Date: Fri, 10 Jul 2026 18:50:45 -0600 Subject: [PATCH 63/92] fix: input to cropped tiled windows should be bound to the tile --- src/input/mod.rs | 7 ++++++- src/shell/layout/tiling/mod.rs | 8 ++++++++ 2 files changed, 14 insertions(+), 1 deletion(-) diff --git a/src/input/mod.rs b/src/input/mod.rs index 30e5d4f8..6398e184 100644 --- a/src/input/mod.rs +++ b/src/input/mod.rs @@ -788,7 +788,12 @@ impl State { let shell = self.common.shell.read(); State::element_under(global_position, &output, &shell, &seat) }; - if let Some(target) = under { + // Grabbing a tiling resize handle (the gap between tiles) must not change keyboard focus + let on_resize_fork = matches!( + seat.get_pointer().unwrap().current_focus(), + Some(PointerFocusTarget::ResizeFork(_)) + ); + if let Some(target) = under.filter(|_| !on_resize_fork) { if let Some(surface) = target.toplevel().map(Cow::into_owned) && seat.get_keyboard().unwrap().modifier_state().logo && !shortcuts_inhibited diff --git a/src/shell/layout/tiling/mod.rs b/src/shell/layout/tiling/mod.rs index 5b425948..67bf947f 100644 --- a/src/shell/layout/tiling/mod.rs +++ b/src/shell/layout/tiling/mod.rs @@ -3180,6 +3180,10 @@ impl TilingLayout { let location = location_f64.to_i32_round(); for (mapped, geo) in self.mapped() { + // Tiled windows are rendered cropped to their tile (`geo`), so input must be bound to the tile as well + if !geo.contains(location) { + continue; + } if !mapped.bbox().contains((location - geo.loc).as_logical()) { continue; } @@ -3244,6 +3248,10 @@ impl TilingLayout { if matches!(overview, OverviewMode::None) { for (mapped, geo) in self.mapped() { + // Tiled windows are rendered cropped to their tile (`geo`), so input must be bound to the tile as well + if !geo.contains(location) { + continue; + } if !mapped.bbox().contains((location - geo.loc).as_logical()) { continue; } From 2b360a8dd7723f4da1db2e20c2eb7e7ca3f2a96b Mon Sep 17 00:00:00 2001 From: Alyssa Ross Date: Fri, 10 Jul 2026 17:35:23 +0200 Subject: [PATCH 64/92] main: notify readiness on non-systemd systems Other service supervisors implement systemd's readiness notification protocol. For example, s6 implements it with the s6-notify-fd-from-socket[1] program. libsystemd::daemon::notify already checks for NOTIFY_SOCKET being set, which should be sufficient to tell if systemd-style readiness notification is wanted. Link: https://skarnet.org/software/s6/s6-notify-fd-from-socket.html [1] --- src/systemd.rs | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/src/systemd.rs b/src/systemd.rs index 422d91c9..36720d9c 100644 --- a/src/systemd.rs +++ b/src/systemd.rs @@ -27,9 +27,9 @@ pub fn ready(common: &Common) { ), Err(err) => error!(?err, "Failed to run systemctl although booted with systemd",), }; + } - if let Err(err) = notify(false, &[NotifyState::Ready]) { - error!(?err, "Failed to notify systemd"); - } + if let Err(err) = notify(false, &[NotifyState::Ready]) { + error!(?err, "Failed to notify systemd"); } } From 9feaa865ab0aa16c5461734b324d0827981d7483 Mon Sep 17 00:00:00 2001 From: Alyssa Ross Date: Fri, 10 Jul 2026 19:44:16 +0200 Subject: [PATCH 65/92] main: fix kiosk command with command line options We were just running whatever was given on the command line as the kiosk command, which meant that if --no-xwayland was given, we'd try execing --no-xwayland. Preserve the processed command line from the option parser, and run only what's left over as the kiosk command to fix this. --- src/lib.rs | 16 ++++++++++------ src/state.rs | 5 ++++- 2 files changed, 14 insertions(+), 7 deletions(-) diff --git a/src/lib.rs b/src/lib.rs index 4f4e5f83..542162eb 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -81,11 +81,8 @@ impl State { warn!(?err, "Failed to setup cosmic-session communication"); } - let mut args = env::args().skip(1); - self.common.kiosk_child = if let Some(exec) = args.next() { + self.common.kiosk_child = if let Some(mut command) = self.kiosk_command.take() { // Run command in kiosk mode - let mut command = process::Command::new(&exec); - command.args(args); command.envs( session::get_env(&self.common).expect("WAYLAND_DISPLAY should be valid UTF-8"), ); @@ -96,7 +93,7 @@ impl State { }) }; - info!("Running {:?}", exec); + info!("Running {:?}", command.get_program()); command .spawn() .map_err(|err| { @@ -115,8 +112,10 @@ impl State { pub fn run(hooks: crate::hooks::Hooks) -> Result<(), Box> { let raw_args = RawArgs::from_args(); let mut cursor = raw_args.cursor(); + raw_args.next_os(&mut cursor); let git_hash = option_env!("GIT_HASH").unwrap_or("unknown"); + let mut kiosk_command = None; let mut with_xwayland = true; // Parse the arguments while let Some(arg) = raw_args.next_os(&mut cursor) { @@ -137,7 +136,11 @@ pub fn run(hooks: crate::hooks::Hooks) -> Result<(), Box> { ); return Ok(()); } - _ => {} + _ => { + let mut cmd = process::Command::new(arg); + cmd.args(raw_args.remaining(&mut cursor)); + kiosk_command = Some(cmd); + } } } @@ -171,6 +174,7 @@ pub fn run(hooks: crate::hooks::Hooks) -> Result<(), Box> { event_loop.handle(), event_loop.get_signal(), with_xwayland, + kiosk_command, ); // init backend backend::init_backend_auto(&display, &mut event_loop, &mut state)?; diff --git a/src/state.rs b/src/state.rs index cf66aeaa..bef2c0ba 100644 --- a/src/state.rs +++ b/src/state.rs @@ -127,7 +127,7 @@ use std::{ cmp::min, collections::HashSet, ffi::OsString, - process::Child, + process::{Child, Command}, sync::{Arc, LazyLock, Once, atomic::AtomicBool}, time::{Duration, Instant}, }; @@ -225,6 +225,7 @@ pub struct State { pub common: Common, pub ready: Once, pub last_refresh: LastRefresh, + pub kiosk_command: Option, } smithay::delegate_dispatch2!(State); @@ -633,6 +634,7 @@ impl State { handle: LoopHandle<'static, State>, signal: LoopSignal, with_xwayland: bool, + kiosk_command: Option, ) -> State { let requested_languages = DesktopLanguageRequester::requested_languages(); i18n_embed::select(&*LANG_LOADER, &Localizations, &requested_languages) @@ -810,6 +812,7 @@ impl State { backend: BackendData::Unset, ready: Once::new(), last_refresh: LastRefresh::None, + kiosk_command, } } From 8ab946f10a97833cb5a89b75019cd143a8ddef05 Mon Sep 17 00:00:00 2001 From: Victoria Brekenfeld Date: Wed, 15 Jul 2026 17:50:33 +0200 Subject: [PATCH 66/92] chore: clippy fixes --- src/backend/kms/socket.rs | 2 +- src/shell/mod.rs | 33 ++++++++++-------------- src/state.rs | 2 +- src/wayland/protocols/keyboard_layout.rs | 5 ++-- 4 files changed, 17 insertions(+), 25 deletions(-) diff --git a/src/backend/kms/socket.rs b/src/backend/kms/socket.rs index 3babd35a..f1c2b0eb 100644 --- a/src/backend/kms/socket.rs +++ b/src/backend/kms/socket.rs @@ -36,7 +36,7 @@ impl Common { ) -> Result { let socket_name = format!( "{}-{}", - &self.socket.to_string_lossy(), + self.socket.to_string_lossy(), render_node .dev_path() .unwrap() diff --git a/src/shell/mod.rs b/src/shell/mod.rs index 326b064b..fda349e1 100644 --- a/src/shell/mod.rs +++ b/src/shell/mod.rs @@ -3878,13 +3878,13 @@ impl Shell { } (initial_window_location, layer, workspace.handle) - } else if let Some(sticky_layer) = self + } else { + let sticky_layer = self .workspaces .sets .get_mut(&cursor_output) .filter(|set| set.sticky_layer.mapped().any(|m| m == &old_mapped)) - .map(|set| &mut set.sticky_layer) - { + .map(|set| &mut set.sticky_layer)?; let elem_geo = sticky_layer.element_geometry(&old_mapped).unwrap(); let mut initial_window_location = elem_geo.loc.to_global(&cursor_output); @@ -3927,8 +3927,6 @@ impl Shell { ManagedLayer::Sticky, self.active_space(&cursor_output).unwrap().handle, ) - } else { - return None; }; toplevel_leave_workspace(&window, &workspace_handle); @@ -4222,14 +4220,13 @@ impl Shell { .unwrap() .to_global(&set.output); (&mut set.sticky_layer, geometry) - } else if let Some(workspace) = self.space_for_mut(mapped) { + } else { + let workspace = self.space_for_mut(mapped)?; let geometry = workspace .element_geometry(mapped) .unwrap() .to_global(workspace.output()); (&mut workspace.floating_layer, geometry) - } else { - return None; }; let new_loc = if edge.contains(ResizeEdge::LEFT) { @@ -4266,7 +4263,8 @@ impl Shell { ReleaseMode::Click, ) { grab.into() - } else if let Some(ws) = self.space_for_mut(mapped) { + } else { + let ws = self.space_for_mut(mapped)?; let node_id = mapped.tiling_node_id.lock().unwrap().clone()?; let (node, left_up_idx, orientation) = ws.tiling_layer.resize_request(node_id, edge)?; ResizeForkGrab::new( @@ -4279,8 +4277,6 @@ impl Shell { ReleaseMode::Click, ) .into() - } else { - return None; }; Some(((focus, new_loc), (grab, Focus::Keep))) @@ -4517,10 +4513,9 @@ impl Shell { .find(|set| set.sticky_layer.mapped().any(|m| m == &mapped)) { &mut set.sticky_layer - } else if let Some(workspace) = self.space_for_mut(&mapped) { - &mut workspace.floating_layer } else { - return None; + let workspace = self.space_for_mut(&mapped)?; + &mut workspace.floating_layer }; let grab: ResizeGrab = if let Some(grab) = floating_layer.resize_request( @@ -4532,7 +4527,8 @@ impl Shell { ReleaseMode::NoMouseButtons, ) { grab.into() - } else if let Some(ws) = self.space_for_mut(&mapped) { + } else { + let ws = self.space_for_mut(&mapped)?; let node_id = mapped.tiling_node_id.lock().unwrap().clone()?; let (node, left_up_idx, orientation) = ws.tiling_layer.resize_request(node_id, edges)?; @@ -4546,8 +4542,6 @@ impl Shell { ReleaseMode::NoMouseButtons, ) .into() - } else { - return None; }; Some((grab, Focus::Clear)) @@ -4904,7 +4898,8 @@ impl Shell { })), Some(from), ); - } else if let Some(workspace) = self.space_for_mut(&mapped) { + } else { + let workspace = self.space_for_mut(&mapped)?; if mapped.is_minimized() { // TODO: Rewrite the `MinimizedWindow` to restore to fullscreen return None; @@ -4952,8 +4947,6 @@ impl Shell { }, Some(from), ); - } else { - return None; }; Some(KeyboardFocusTarget::Fullscreen(window)) diff --git a/src/state.rs b/src/state.rs index bef2c0ba..21cbb400 100644 --- a/src/state.rs +++ b/src/state.rs @@ -688,7 +688,7 @@ impl State { AlphaModifierState::new::(dh); SinglePixelBufferState::new::(dh); FixesState::new::(dh); - let keyboard_layout_state = KeyboardLayoutState::new::(&dh, client_not_sandboxed); + let keyboard_layout_state = KeyboardLayoutState::new::(dh, client_not_sandboxed); let background_effect_state = BackgroundEffectState::new::(dh); diff --git a/src/wayland/protocols/keyboard_layout.rs b/src/wayland/protocols/keyboard_layout.rs index 0d674f88..51324658 100644 --- a/src/wayland/protocols/keyboard_layout.rs +++ b/src/wayland/protocols/keyboard_layout.rs @@ -51,8 +51,8 @@ impl KeyboardLayoutState { { let mut keyboard_layouts = mem::take(&mut state.keyboard_layout_state().keyboard_layouts); for (keyboard_layout, last_layout) in &mut keyboard_layouts { - if let Some(data) = keyboard_layout.data::>() { - if let Some(handle) = &data.handle { + if let Some(data) = keyboard_layout.data::>() + && let Some(handle) = &data.handle { let active_layout = handle.with_xkb_state(state, |context| { context.xkb().lock().unwrap().active_layout() }); @@ -61,7 +61,6 @@ impl KeyboardLayoutState { *last_layout = Some(active_layout); } } - } } state.keyboard_layout_state().keyboard_layouts = keyboard_layouts; } From 2d95d46a3705db19eb84cdd471b1daf701c4f975 Mon Sep 17 00:00:00 2001 From: Victoria Brekenfeld Date: Wed, 15 Jul 2026 18:23:06 +0200 Subject: [PATCH 67/92] chore: formatting fixes --- src/shell/mod.rs | 10 +++++----- src/wayland/protocols/keyboard_layout.rs | 17 +++++++++-------- 2 files changed, 14 insertions(+), 13 deletions(-) diff --git a/src/shell/mod.rs b/src/shell/mod.rs index fda349e1..39d692e2 100644 --- a/src/shell/mod.rs +++ b/src/shell/mod.rs @@ -3880,11 +3880,11 @@ impl Shell { (initial_window_location, layer, workspace.handle) } else { let sticky_layer = self - .workspaces - .sets - .get_mut(&cursor_output) - .filter(|set| set.sticky_layer.mapped().any(|m| m == &old_mapped)) - .map(|set| &mut set.sticky_layer)?; + .workspaces + .sets + .get_mut(&cursor_output) + .filter(|set| set.sticky_layer.mapped().any(|m| m == &old_mapped)) + .map(|set| &mut set.sticky_layer)?; let elem_geo = sticky_layer.element_geometry(&old_mapped).unwrap(); let mut initial_window_location = elem_geo.loc.to_global(&cursor_output); diff --git a/src/wayland/protocols/keyboard_layout.rs b/src/wayland/protocols/keyboard_layout.rs index 51324658..f3a44f0a 100644 --- a/src/wayland/protocols/keyboard_layout.rs +++ b/src/wayland/protocols/keyboard_layout.rs @@ -52,15 +52,16 @@ impl KeyboardLayoutState { let mut keyboard_layouts = mem::take(&mut state.keyboard_layout_state().keyboard_layouts); for (keyboard_layout, last_layout) in &mut keyboard_layouts { if let Some(data) = keyboard_layout.data::>() - && let Some(handle) = &data.handle { - let active_layout = handle.with_xkb_state(state, |context| { - context.xkb().lock().unwrap().active_layout() - }); - if *last_layout != Some(active_layout) { - keyboard_layout.group(active_layout.0); - *last_layout = Some(active_layout); - } + && let Some(handle) = &data.handle + { + let active_layout = handle.with_xkb_state(state, |context| { + context.xkb().lock().unwrap().active_layout() + }); + if *last_layout != Some(active_layout) { + keyboard_layout.group(active_layout.0); + *last_layout = Some(active_layout); } + } } state.keyboard_layout_state().keyboard_layouts = keyboard_layouts; } From 160bd39ba4e9b509eaa5536f0b68aa8fb084c3b5 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Vuka=C5=A1in=20Vojinovi=C4=87?= <150025636+git-f0x@users.noreply.github.com> Date: Thu, 16 Jul 2026 12:46:49 +0200 Subject: [PATCH 68/92] render: `cgmath` -> `glam` --- Cargo.lock | 32 +++++++++++-------- Cargo.toml | 4 +-- src/backend/render/shadow.rs | 24 ++++++-------- src/backend/render/wayland/blur_effect.rs | 8 ++--- src/backend/render/wayland/clipped_surface.rs | 14 ++++---- 5 files changed, 41 insertions(+), 41 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index 27e8287e..6ee49633 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -792,7 +792,6 @@ dependencies = [ "anyhow", "bitflags 2.11.0", "calloop", - "cgmath", "clap_lex", "cosmic-comp-config", "cosmic-config", @@ -804,6 +803,7 @@ dependencies = [ "egui_plot", "futures-executor", "futures-util", + "glam 0.33.2", "i18n-embed", "i18n-embed-fl", "iced_graphics", @@ -1194,7 +1194,7 @@ dependencies = [ "libc", "option-ext", "redox_users", - "windows-sys 0.59.0", + "windows-sys 0.61.2", ] [[package]] @@ -1508,7 +1508,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "39cab71617ae0d63f51a36d69f866391735b51691dbda63cf6f96d042b63efeb" dependencies = [ "libc", - "windows-sys 0.52.0", + "windows-sys 0.61.2", ] [[package]] @@ -2001,6 +2001,12 @@ version = "0.25.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "151665d9be52f9bb40fc7966565d39666f2d1e69233571b71b87791c7e0528b3" +[[package]] +name = "glam" +version = "0.33.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7f22fb22f065b308be0d8724e3706c7fa3fc2a6c7d6899df4cad7860e7a75436" + [[package]] name = "glow" version = "0.16.0" @@ -2261,7 +2267,7 @@ dependencies = [ "bitflags 2.11.0", "bytes", "dnd", - "glam", + "glam 0.25.0", "lilt", "log", "mime 0.1.0", @@ -2383,7 +2389,7 @@ dependencies = [ "bytemuck", "cryoglyph", "futures", - "glam", + "glam 0.25.0", "guillotiere", "iced_debug", "iced_graphics", @@ -2689,7 +2695,7 @@ dependencies = [ "portable-atomic", "portable-atomic-util", "serde_core", - "windows-sys 0.52.0", + "windows-sys 0.61.2", ] [[package]] @@ -2812,7 +2818,7 @@ version = "1.4.2" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "7a1886916523694cd6ea3d175f03a1e5010699a2a4cc13696d83d7bea1d80638" dependencies = [ - "windows-sys 0.59.0", + "windows-sys 0.61.2", ] [[package]] @@ -3422,7 +3428,7 @@ version = "0.50.3" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "7957b9740744892f114936ab4a57b3f487491bbeafaf8083688b16841a4240e5" dependencies = [ - "windows-sys 0.59.0", + "windows-sys 0.61.2", ] [[package]] @@ -4518,7 +4524,7 @@ dependencies = [ "errno", "libc", "linux-raw-sys 0.12.1", - "windows-sys 0.52.0", + "windows-sys 0.61.2", ] [[package]] @@ -4813,7 +4819,7 @@ checksum = "8ed6a63f02c8539c91a8685a86f4099661ba3da017932f6ebbea6de3f0fa7c90" [[package]] name = "smithay" version = "0.7.0" -source = "git+https://github.com/smithay/smithay.git?rev=efeb597#efeb59768eb9a2d8dae9f71300a56aef06b0c1c4" +source = "git+https://github.com/smithay/smithay.git?rev=812bd33#812bd33259ff58810dadef6086d8385eeac1ca55" dependencies = [ "aliasable", "appendlist", @@ -4822,7 +4828,6 @@ dependencies = [ "bitflags 2.11.0", "calloop", "cc", - "cgmath", "cursor-icon", "downcast-rs", "drm 0.14.1", @@ -4832,6 +4837,7 @@ dependencies = [ "errno", "gbm", "gl_generator", + "glam 0.33.2", "glow", "indexmap 2.13.0", "input", @@ -5107,7 +5113,7 @@ dependencies = [ "getrandom 0.4.1", "once_cell", "rustix 1.1.4", - "windows-sys 0.52.0", + "windows-sys 0.61.2", ] [[package]] @@ -6126,7 +6132,7 @@ version = "0.1.11" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "c2a7b1c03c876122aa43f3020e6c3c3ee5c05081c9a00739faf7503aeba10d22" dependencies = [ - "windows-sys 0.48.0", + "windows-sys 0.61.2", ] [[package]] diff --git a/Cargo.toml b/Cargo.toml index b5ebbb06..89b3116f 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -83,7 +83,7 @@ parking_lot = "0.12.5" logind-zbus = { version = "5.3.2", optional = true } futures-executor = { version = "0.3.32" } futures-util = "0.3.32" -cgmath = "0.18.0" +glam = { version = "0.33", default-features = false, features = ["std"] } smallvec = "1.15.2" [dependencies.id_tree] @@ -144,4 +144,4 @@ lto = "fat" cosmic-protocols = { git = "https://github.com/pop-os//cosmic-protocols", branch = "main" } [patch.crates-io] -smithay = { git = "https://github.com/smithay/smithay.git", rev = "efeb597" } +smithay = { git = "https://github.com/smithay/smithay.git", rev = "812bd33" } diff --git a/src/backend/render/shadow.rs b/src/backend/render/shadow.rs index edb80634..c5fdc99c 100644 --- a/src/backend/render/shadow.rs +++ b/src/backend/render/shadow.rs @@ -1,6 +1,6 @@ use std::{borrow::Borrow, cell::RefCell, collections::HashMap}; -use cgmath::{Matrix3, Vector2}; +use glam::{Mat3, Vec2}; use smithay::{ backend::renderer::{ element::Kind, @@ -113,26 +113,22 @@ impl ShadowShader { let mut shader_geo = Rectangle::new(Point::from((-width, -width)), shader_size); let window_geo = Rectangle::new(Point::new(0., 0.) - offset - shader_geo.loc, geo.size); - let area_size = Vector2::new(shader_geo.size.w, shader_geo.size.h); - let geo_loc = Vector2::new(-shader_geo.loc.x, -shader_geo.loc.y); + let area_size = Vec2::new(shader_geo.size.w as f32, shader_geo.size.h as f32); + let geo_loc = Vec2::new(-shader_geo.loc.x as f32, -shader_geo.loc.y as f32); shader_geo.loc += offset + geo.loc; - let input_to_geo = (Matrix3::from_nonuniform_scale(area_size.x, area_size.y) - * Matrix3::from_translation(Vector2::new( + let input_to_geo = Mat3::from_scale(area_size) + * Mat3::from_translation(Vec2::new( -geo_loc.x / area_size.x, -geo_loc.y / area_size.y, - ))) - .cast::() - .unwrap(); + )); - let window_geo_loc = Vector2::new(window_geo.loc.x, window_geo.loc.y); - let window_input_to_geo = (Matrix3::from_nonuniform_scale(area_size.x, area_size.y) - * Matrix3::from_translation(Vector2::new( + let window_geo_loc = Vec2::new(window_geo.loc.x as f32, window_geo.loc.y as f32); + let window_input_to_geo = Mat3::from_scale(area_size) + * Mat3::from_translation(Vec2::new( -window_geo_loc.x / area_size.x, -window_geo_loc.y / area_size.y, - ))) - .cast::() - .unwrap(); + )); let element = PixelShaderElement::new( shader, diff --git a/src/backend/render/wayland/blur_effect.rs b/src/backend/render/wayland/blur_effect.rs index bb667af1..a083b134 100644 --- a/src/backend/render/wayland/blur_effect.rs +++ b/src/backend/render/wayland/blur_effect.rs @@ -3,7 +3,7 @@ use std::{ sync::{LazyLock, Mutex}, }; -use cgmath::{Matrix3, SquareMatrix, Vector2}; +use glam::{Mat3, Vec2}; use smithay::{ backend::{ allocator::Fourcc, @@ -231,13 +231,11 @@ impl BlurElement { // compute input_to_geo so that it crops the extended capture radius let geo_scale = { let Scale { x, y } = geo.size / extended_geo.size; - Matrix3::from_nonuniform_scale(x as f32, y as f32) - .invert() - .unwrap() + Mat3::from_scale(Vec2::new(x as f32, y as f32)).inverse() }; let geo_translation = { let offset = geo.loc - extended_geo.loc; - Matrix3::from_translation(-Vector2::new( + Mat3::from_translation(-Vec2::new( (offset.x / extended_geo.size.w) as f32, (offset.y / extended_geo.size.h) as f32, )) diff --git a/src/backend/render/wayland/clipped_surface.rs b/src/backend/render/wayland/clipped_surface.rs index 92e64ad0..75c26c9a 100644 --- a/src/backend/render/wayland/clipped_surface.rs +++ b/src/backend/render/wayland/clipped_surface.rs @@ -2,7 +2,7 @@ use std::borrow::{Borrow, BorrowMut}; -use cgmath::{Matrix3, Vector2}; +use glam::{Mat3, Vec2}; use smithay::utils::{Buffer, Logical, Physical, Point, Rectangle, Scale, Size, Transform}; use smithay::{ backend::renderer::{ @@ -63,18 +63,18 @@ where let view = elem.view(); let transform = elem.transform(); - let transform_matrix = Matrix3::::from_translation(Vector2::new(0.5, 0.5)) + let transform_matrix = Mat3::from_translation(Vec2::new(0.5, 0.5)) * transform.matrix() - * Matrix3::::from_translation(-Vector2::new(0.5, 0.5)); + * Mat3::from_translation(-Vec2::new(0.5, 0.5)); let geo_scale = { let Scale { x, y } = elem_geo.size.to_f64() / geo.size.to_f64(); - Matrix3::from_nonuniform_scale(x as f32, y as f32) + Mat3::from_scale(Vec2::new(x as f32, y as f32)) }; let geo_translation = { let offset = (elem_geo.loc - geo.loc).to_f64(); - Matrix3::from_translation(Vector2::new( + Mat3::from_translation(Vec2::new( (offset.x / elem_geo.size.w as f64) as f32, (offset.y / elem_geo.size.h as f64) as f32, )) @@ -82,10 +82,10 @@ where let buf_scale = { let Scale { x, y } = buf_size.to_f64() / view.src.size.to_f64(); - Matrix3::from_nonuniform_scale(x as f32, y as f32) + Mat3::from_scale(Vec2::new(x as f32, y as f32)) }; - let buf_translation = Matrix3::from_translation(Vector2::new( + let buf_translation = Mat3::from_translation(Vec2::new( (view.src.loc.x / buf_size.w as f64) as f32, (view.src.loc.y / buf_size.h as f64) as f32, )); From d41124a51d2284566f137c01151b837b97d092a1 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Vuka=C5=A1in=20Vojinovi=C4=87?= <150025636+git-f0x@users.noreply.github.com> Date: Fri, 17 Jul 2026 16:11:49 +0200 Subject: [PATCH 69/92] chore: clippy fixes --- src/wayland/handlers/corner_radius.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/wayland/handlers/corner_radius.rs b/src/wayland/handlers/corner_radius.rs index 8a316d2a..ded8d923 100644 --- a/src/wayland/handlers/corner_radius.rs +++ b/src/wayland/handlers/corner_radius.rs @@ -3,7 +3,7 @@ use smithay::wayland::compositor::SurfaceData; use crate::wayland::protocols::corner_radius::{ CacheableCorners, CacheablePadding, CornerRadiusData, CornerRadiusHandler, CornerRadiusState, - CornerRadiusSurface, delegate_corner_radius, + delegate_corner_radius, }; use crate::state::State; From 9b5a2a0f83b71383cd31c2d3cc135b55e7beea78 Mon Sep 17 00:00:00 2001 From: Victoria Brekenfeld Date: Thu, 16 Jul 2026 11:40:58 +0200 Subject: [PATCH 70/92] chore: Fix feature gate for logind --- src/input/mod.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/input/mod.rs b/src/input/mod.rs index 6398e184..1c35a97f 100644 --- a/src/input/mod.rs +++ b/src/input/mod.rs @@ -35,7 +35,7 @@ use calloop::{ use cosmic_comp_config::{NumlockState, workspace::WorkspaceLayout}; use cosmic_settings_config::shortcuts; use cosmic_settings_config::shortcuts::action::{Direction, ResizeDirection}; -#[cfg(feature = "systemd")] +#[cfg(feature = "logind")] use smithay::backend::input::{Switch, SwitchState, SwitchToggleEvent}; use smithay::{ backend::input::{ From aabf1cdc08c844ca05339f53fe6c2d56aeeaf59a Mon Sep 17 00:00:00 2001 From: Hojjat Date: Thu, 16 Jul 2026 15:58:54 -0600 Subject: [PATCH 71/92] fix: flicker on wayland context menu pop up fix --- src/shell/layout/tiling/mod.rs | 24 ++++++++++++++++-------- src/shell/workspace.rs | 11 +++++++++++ 2 files changed, 27 insertions(+), 8 deletions(-) diff --git a/src/shell/layout/tiling/mod.rs b/src/shell/layout/tiling/mod.rs index 67bf947f..65e901cb 100644 --- a/src/shell/layout/tiling/mod.rs +++ b/src/shell/layout/tiling/mod.rs @@ -4013,6 +4013,7 @@ impl TilingLayout { &self, renderer: &mut R, seat: Option<&Seat>, + focused: Option<&CosmicMapped>, non_exclusive_zone: Rectangle, overview: (OverviewMode, Option<(SwapIndicator, Option<&Tree>)>), resize_indicator: Option<(ResizeMode, ResizeIndicator)>, @@ -4143,6 +4144,7 @@ impl TilingLayout { old_geometries, is_overview, seat, + focused, &self.output, percentage, draw_groups, @@ -5276,6 +5278,7 @@ fn render_new_tree_windows( old_geometries: Option>>, is_overview: bool, seat: Option<&Seat>, + focused: Option<&CosmicMapped>, output: &Output, percentage: f32, transition: Option, @@ -5295,14 +5298,19 @@ fn render_new_tree_windows( CosmicWindowRenderElement: RenderElement, CosmicStackRenderElement: RenderElement, { - let focused = seat - .and_then(|seat| { - seat.get_keyboard() - .unwrap() - .current_focus() - .and_then(|target| TilingLayout::currently_focused_node(target_tree, target)) - }) - .map(|(id, _)| id); + let focused = match seat.and_then(|seat| seat.get_keyboard().unwrap().current_focus()) { + Some(target @ KeyboardFocusTarget::Group(_)) => { + TilingLayout::currently_focused_node(target_tree, target).map(|(id, _)| id) + } + _ => focused.and_then(|mapped| { + let node_id = mapped.tiling_node_id.lock().unwrap().clone()?; + target_tree + .get(&node_id) + .ok() + .filter(|node| node.data().is_mapped(Some(mapped))) + .map(|_| node_id) + }), + }; let focused_geo = if let Some(focused) = focused.as_ref() { geometries .as_ref() diff --git a/src/shell/workspace.rs b/src/shell/workspace.rs index e771e502..0cc0a407 100644 --- a/src/shell/workspace.rs +++ b/src/shell/workspace.rs @@ -1799,6 +1799,17 @@ impl Workspace { self.tiling_layer.render( renderer, render_focus.then_some(last_active_seat), + render_focus + .then(|| { + focused.as_ref().and_then(|target| { + if let FocusTarget::Window(mapped) = target { + Some(mapped) + } else { + None + } + }) + }) + .flatten(), zone, overview, resize_indicator, From 9789187416810f851b72e75f11b9648515e76794 Mon Sep 17 00:00:00 2001 From: RipleyTom Date: Wed, 8 Jul 2026 11:39:38 +0200 Subject: [PATCH 72/92] fix: map_window_request should update pending state --- src/xwayland.rs | 14 +++++++++++--- 1 file changed, 11 insertions(+), 3 deletions(-) diff --git a/src/xwayland.rs b/src/xwayland.rs index aba65ef5..25b8b0e0 100644 --- a/src/xwayland.rs +++ b/src/xwayland.rs @@ -807,9 +807,17 @@ impl XwmHandler for State { *context, ); } - if !shell.pending_windows.iter().any(|w| w.surface == window) { - let fullscreen = window.is_fullscreen().then(|| seat.active_output()); - let maximized = window.is_maximized(); + let fullscreen = window.is_fullscreen().then(|| seat.active_output()); + let maximized = window.is_maximized(); + if let Some(pending) = shell + .pending_windows + .iter_mut() + .find(|w| w.surface == window) + { + pending.seat = seat; + pending.fullscreen = fullscreen; + pending.maximized = maximized; + } else { let surface = CosmicSurface::from(window); shell.pending_windows.push(PendingWindow { surface, From 8955024bff48b89ef59e47ad458cad78e959a3b8 Mon Sep 17 00:00:00 2001 From: RipleyTom Date: Tue, 21 Jul 2026 10:32:09 +0200 Subject: [PATCH 73/92] Fix: improve edge pointer accuracy --- src/input/mod.rs | 39 +++++++++++++-------- src/shell/element/stack.rs | 2 +- src/shell/element/window.rs | 4 +-- src/shell/grabs/menu/mod.rs | 4 +-- src/shell/layout/tiling/mod.rs | 16 ++++----- src/shell/workspace.rs | 8 ++--- src/wayland/handlers/pointer_constraints.rs | 2 +- 7 files changed, 43 insertions(+), 32 deletions(-) diff --git a/src/input/mod.rs b/src/input/mod.rs index 1c35a97f..48db97db 100644 --- a/src/input/mod.rs +++ b/src/input/mod.rs @@ -337,7 +337,7 @@ impl State { // Constraint does not apply if not within region if !constraint.region().is_none_or(|x| { x.contains( - (ptr.current_location() - *surface_loc).to_i32_round(), + (ptr.current_location() - *surface_loc).to_i32_floor(), ) }) { return; @@ -382,14 +382,25 @@ impl State { .unwrap_or(current_output.clone()); drop(shell); let output_geometry = output.geometry(); - position.x = position.x.clamp( - output_geometry.loc.x as f64, - (output_geometry.loc.x + output_geometry.size.w - 1) as f64, - ); - position.y = position.y.clamp( - output_geometry.loc.y as f64, - (output_geometry.loc.y + output_geometry.size.h - 1) as f64, - ); + + let scale = output.current_scale().fractional_scale(); + let physical = output + .current_mode() + .map(|mode| output.current_transform().transform_size(mode.size)) + .unwrap_or_default(); + let logical = physical.to_f64().to_logical(scale); + let output_geometry_loc = output_geometry.loc.to_f64(); + // output_geometry.size is a rounded value and may undershoot/overshoot the accurate logical size + // We constrain the position with: + // - output_geometry.size so that we don't send leave events to a fullscreen app + // - logical size so that the position doesn't end up outside the actual size of the output + // See https://github.com/pop-os/cosmic-comp/pull/2568 + let max_x = output_geometry_loc.x + + logical.w.min(output_geometry.size.w as f64).next_down(); + let max_y = output_geometry_loc.y + + logical.h.min(output_geometry.size.h as f64).next_down(); + position.x = position.x.clamp(output_geometry_loc.x, max_x); + position.y = position.y.clamp(output_geometry_loc.y, max_y); if ptr.is_grabbed() { if seat @@ -543,7 +554,7 @@ impl State { if let Some(region) = &confine_region && !region - .contains((pos.as_logical() - *surface_loc).to_i32_round()) + .contains((pos.as_logical() - *surface_loc).to_i32_floor()) { return (false, None); } @@ -614,7 +625,7 @@ impl State { PointerConstraint::Confined(confined) => confined.region(), }; let point = - (ptr.current_location() - surface_location).to_i32_round(); + (ptr.current_location() - surface_location).to_i32_floor(); if region.is_none_or(|region| region.contains(point)) { constraint.activate(); } @@ -2210,7 +2221,7 @@ impl State { if Rectangle::new(offset.as_local(), output_geo.size) .intersection(output_geo) .is_some_and(|geometry| { - geometry.contains(global_pos.to_local(output).to_i32_round()) + geometry.contains(global_pos.to_local(output).to_i32_floor()) }) && let Some(element) = workspace.popup_element_under(location, seat) { @@ -2224,7 +2235,7 @@ impl State { if Rectangle::new(offset.as_local(), output_geo.size) .intersection(output_geo) .is_some_and(|geometry| { - geometry.contains(global_pos.to_local(output).to_i32_round()) + geometry.contains(global_pos.to_local(output).to_i32_floor()) }) && let Some(element) = workspace.toplevel_element_under(location, seat) { @@ -2427,7 +2438,7 @@ impl State { if let Some(constraint) = constraint && let Some(region) = constraint.region() { - let point_in_surface = (p - surface_offset.to_f64()).to_i32_round(); + let point_in_surface = (p - surface_offset.to_f64()).to_i32_floor(); return region.contains(point_in_surface); } true diff --git a/src/shell/element/stack.rs b/src/shell/element/stack.rs index fabb87bd..d466f4e0 100644 --- a/src/shell/element/stack.rs +++ b/src/shell/element/stack.rs @@ -530,7 +530,7 @@ impl CosmicStack { let geo = p.windows.lock().unwrap()[p.active.load(Ordering::SeqCst)].geometry(); if surface_type.contains(WindowSurfaceType::TOPLEVEL) { - let point_i32 = relative_pos.to_i32_round::(); + let point_i32 = relative_pos.to_i32_floor::(); if (point_i32.x - geo.loc.x >= -RESIZE_BORDER && point_i32.x - geo.loc.x < 0) || (point_i32.y - geo.loc.y >= -RESIZE_BORDER && point_i32.y - geo.loc.y < 0) || (point_i32.x - geo.loc.x >= geo.size.w diff --git a/src/shell/element/window.rs b/src/shell/element/window.rs index 324299e0..86c2ce7f 100644 --- a/src/shell/element/window.rs +++ b/src/shell/element/window.rs @@ -113,7 +113,7 @@ impl Focus { location: Point, ) -> Option { let geo = surface.geometry(); - let loc = location.to_i32_round::() - geo.loc; + let loc = location.to_i32_floor::() - geo.loc; if loc.y < 0 && loc.x < 0 { Some(Focus::ResizeTopLeft) } else if loc.y < 0 && loc.x >= geo.size.w { @@ -292,7 +292,7 @@ impl CosmicWindow { { let geo = p.window.geometry(); - let point_i32 = relative_pos.to_i32_round::(); + let point_i32 = relative_pos.to_i32_floor::(); let ssd_height = if has_ssd { SSD_HEIGHT } else { 0 }; if (point_i32.x - geo.loc.x >= -RESIZE_BORDER && point_i32.x - geo.loc.x < 0) diff --git a/src/shell/grabs/menu/mod.rs b/src/shell/grabs/menu/mod.rs index 46639631..72ea065b 100644 --- a/src/shell/grabs/menu/mod.rs +++ b/src/shell/grabs/menu/mod.rs @@ -548,7 +548,7 @@ impl PointerGrab for MenuGrab { let mut bbox = elem.iced.bbox(); bbox.loc = elem.position.as_logical(); - bbox.contains(event_location.to_i32_round()) + bbox.contains(event_location.to_i32_floor()) }) { let element = &mut elements[i]; @@ -753,7 +753,7 @@ impl TouchGrab for MenuGrab { let mut bbox = elem.iced.bbox(); bbox.loc = elem.position.as_logical(); - bbox.contains(event_location.to_i32_round()) + bbox.contains(event_location.to_i32_floor()) }) { let element = &mut elements[i]; diff --git a/src/shell/layout/tiling/mod.rs b/src/shell/layout/tiling/mod.rs index 65e901cb..2d95cd27 100644 --- a/src/shell/layout/tiling/mod.rs +++ b/src/shell/layout/tiling/mod.rs @@ -3150,7 +3150,7 @@ impl TilingLayout { location_f64: Point, seat: &Seat, ) -> Option { - let location = location_f64.to_i32_round(); + let location = location_f64.to_i32_floor(); for (mapped, geo) in self.mapped() { if !mapped.bbox().contains((location - geo.loc).as_logical()) { @@ -3177,7 +3177,7 @@ impl TilingLayout { location_f64: Point, seat: &Seat, ) -> Option { - let location = location_f64.to_i32_round(); + let location = location_f64.to_i32_floor(); for (mapped, geo) in self.mapped() { // Tiled windows are rendered cropped to their tile (`geo`), so input must be bound to the tile as well @@ -3209,7 +3209,7 @@ impl TilingLayout { overview: OverviewMode, seat: &Seat, ) -> Option<(PointerFocusTarget, Point)> { - let location = location_f64.to_i32_round(); + let location = location_f64.to_i32_floor(); if matches!(overview, OverviewMode::None) { for (mapped, geo) in self.mapped() { @@ -3244,7 +3244,7 @@ impl TilingLayout { ) -> Option<(PointerFocusTarget, Point)> { let tree = &self.queue.trees.back().unwrap().0; let root = tree.root_node_id()?; - let location = location_f64.to_i32_round(); + let location = location_f64.to_i32_floor(); if matches!(overview, OverviewMode::None) { for (mapped, geo) in self.mapped() { @@ -3305,7 +3305,7 @@ impl TilingLayout { .. }, )) => { - let test_point = (location.to_f64() - last_geometry.loc.to_f64() + let test_point = (location_f64 - last_geometry.loc.to_f64() + mapped.geometry().loc.to_f64().as_local()) .as_logical(); mapped @@ -3410,7 +3410,7 @@ impl TilingLayout { } let location_f64 = location_f64.unwrap(); - let location = location_f64.to_i32_round(); + let location = location_f64.to_i32_floor(); if matches!( overview.active_trigger(), @@ -3660,7 +3660,7 @@ impl TilingLayout { TargetZone::WindowStack(id, last_geometry) } else { let left_right = { - let relative_loc = (location.x - last_geometry.loc.x) as f64; + let relative_loc = location_f64.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 { @@ -3671,7 +3671,7 @@ impl TilingLayout { } }; let up_down = { - let relative_loc = (location.y - last_geometry.loc.y) as f64; + let relative_loc = location_f64.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 { diff --git a/src/shell/workspace.rs b/src/shell/workspace.rs index 0cc0a407..84a020ff 100644 --- a/src/shell/workspace.rs +++ b/src/shell/workspace.rs @@ -785,7 +785,7 @@ impl Workspace { location: Point, seat: &Seat, ) -> Option { - if !self.output.geometry().contains(location.to_i32_round()) { + if !self.output.geometry().contains(location.to_i32_floor()) { return None; } let location = location.to_local(&self.output); @@ -834,7 +834,7 @@ impl Workspace { location: Point, seat: &Seat, ) -> Option { - if !self.output.geometry().contains(location.to_i32_round()) { + if !self.output.geometry().contains(location.to_i32_floor()) { return None; } let location = location.to_local(&self.output); @@ -884,7 +884,7 @@ impl Workspace { overview: OverviewMode, seat: &Seat, ) -> Option<(PointerFocusTarget, Point)> { - if !self.output.geometry().contains(location.to_i32_round()) { + if !self.output.geometry().contains(location.to_i32_floor()) { return None; } let location = location.to_local(&self.output); @@ -934,7 +934,7 @@ impl Workspace { overview: OverviewMode, seat: &Seat, ) -> Option<(PointerFocusTarget, Point)> { - if !self.output.geometry().contains(location.to_i32_round()) { + if !self.output.geometry().contains(location.to_i32_floor()) { return None; } let location = location.to_local(&self.output); diff --git a/src/wayland/handlers/pointer_constraints.rs b/src/wayland/handlers/pointer_constraints.rs index e17c1961..9ce5f63a 100644 --- a/src/wayland/handlers/pointer_constraints.rs +++ b/src/wayland/handlers/pointer_constraints.rs @@ -63,7 +63,7 @@ impl PointerConstraintsHandler for State { if let Some(region) = constraint.region() { if let Some(surface_location) = surface_location && let position = pointer.current_location() - && let point = (position - surface_location.as_logical()).to_i32_round() + && let point = (position - surface_location.as_logical()).to_i32_floor() && region.contains(point) { constraint.activate(); From 60137c0d9e58d8490a4bb56653ad8c5d60ecbdef Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Vuka=C5=A1in=20Vojinovi=C4=87?= <150025636+git-f0x@users.noreply.github.com> Date: Mon, 20 Jul 2026 15:57:59 +0200 Subject: [PATCH 74/92] chore(render): use `Affine2` where possible --- Cargo.lock | 2 +- Cargo.toml | 2 +- src/backend/render/shadow.rs | 26 +++++++++++-------- src/backend/render/wayland/blur_effect.rs | 8 +++--- src/backend/render/wayland/clipped_surface.rs | 19 +++++++------- 5 files changed, 31 insertions(+), 26 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index 6ee49633..fd733056 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -4819,7 +4819,7 @@ checksum = "8ed6a63f02c8539c91a8685a86f4099661ba3da017932f6ebbea6de3f0fa7c90" [[package]] name = "smithay" version = "0.7.0" -source = "git+https://github.com/smithay/smithay.git?rev=812bd33#812bd33259ff58810dadef6086d8385eeac1ca55" +source = "git+https://github.com/smithay/smithay.git?rev=1ed69cb#1ed69cb55e9d0e9f38887c3b7d53d24365c4b14f" dependencies = [ "aliasable", "appendlist", diff --git a/Cargo.toml b/Cargo.toml index 89b3116f..3f8a9e73 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -144,4 +144,4 @@ lto = "fat" cosmic-protocols = { git = "https://github.com/pop-os//cosmic-protocols", branch = "main" } [patch.crates-io] -smithay = { git = "https://github.com/smithay/smithay.git", rev = "812bd33" } +smithay = { git = "https://github.com/smithay/smithay.git", rev = "1ed69cb" } diff --git a/src/backend/render/shadow.rs b/src/backend/render/shadow.rs index c5fdc99c..37b1fdec 100644 --- a/src/backend/render/shadow.rs +++ b/src/backend/render/shadow.rs @@ -1,6 +1,6 @@ use std::{borrow::Borrow, cell::RefCell, collections::HashMap}; -use glam::{Mat3, Vec2}; +use glam::{Affine2, Mat3, Vec2}; use smithay::{ backend::renderer::{ element::Kind, @@ -117,18 +117,22 @@ impl ShadowShader { let geo_loc = Vec2::new(-shader_geo.loc.x as f32, -shader_geo.loc.y as f32); shader_geo.loc += offset + geo.loc; - let input_to_geo = Mat3::from_scale(area_size) - * Mat3::from_translation(Vec2::new( - -geo_loc.x / area_size.x, - -geo_loc.y / area_size.y, - )); + let input_to_geo = Mat3::from( + Affine2::from_scale(area_size) + * Affine2::from_translation(Vec2::new( + -geo_loc.x / area_size.x, + -geo_loc.y / area_size.y, + )), + ); let window_geo_loc = Vec2::new(window_geo.loc.x as f32, window_geo.loc.y as f32); - let window_input_to_geo = Mat3::from_scale(area_size) - * Mat3::from_translation(Vec2::new( - -window_geo_loc.x / area_size.x, - -window_geo_loc.y / area_size.y, - )); + let window_input_to_geo = Mat3::from( + Affine2::from_scale(area_size) + * Affine2::from_translation(Vec2::new( + -window_geo_loc.x / area_size.x, + -window_geo_loc.y / area_size.y, + )), + ); let element = PixelShaderElement::new( shader, diff --git a/src/backend/render/wayland/blur_effect.rs b/src/backend/render/wayland/blur_effect.rs index a083b134..c4d27fac 100644 --- a/src/backend/render/wayland/blur_effect.rs +++ b/src/backend/render/wayland/blur_effect.rs @@ -3,7 +3,7 @@ use std::{ sync::{LazyLock, Mutex}, }; -use glam::{Mat3, Vec2}; +use glam::{Affine2, Mat3, Vec2}; use smithay::{ backend::{ allocator::Fourcc, @@ -231,16 +231,16 @@ impl BlurElement { // compute input_to_geo so that it crops the extended capture radius let geo_scale = { let Scale { x, y } = geo.size / extended_geo.size; - Mat3::from_scale(Vec2::new(x as f32, y as f32)).inverse() + Affine2::from_scale(Vec2::new(x as f32, y as f32)).inverse() }; let geo_translation = { let offset = geo.loc - extended_geo.loc; - Mat3::from_translation(-Vec2::new( + Affine2::from_translation(-Vec2::new( (offset.x / extended_geo.size.w) as f32, (offset.y / extended_geo.size.h) as f32, )) }; - let input_to_geo = geo_scale * geo_translation; + let input_to_geo = Mat3::from(geo_scale * geo_translation); let uniforms = vec![ Uniform::new("geo_size", (geometry.size.w as f32, geometry.size.h as f32)), diff --git a/src/backend/render/wayland/clipped_surface.rs b/src/backend/render/wayland/clipped_surface.rs index 75c26c9a..8322079e 100644 --- a/src/backend/render/wayland/clipped_surface.rs +++ b/src/backend/render/wayland/clipped_surface.rs @@ -2,7 +2,7 @@ use std::borrow::{Borrow, BorrowMut}; -use glam::{Mat3, Vec2}; +use glam::{Affine2, Mat3, Vec2}; use smithay::utils::{Buffer, Logical, Physical, Point, Rectangle, Scale, Size, Transform}; use smithay::{ backend::renderer::{ @@ -63,18 +63,18 @@ where let view = elem.view(); let transform = elem.transform(); - let transform_matrix = Mat3::from_translation(Vec2::new(0.5, 0.5)) + let transform_matrix = Affine2::from_translation(Vec2::new(0.5, 0.5)) * transform.matrix() - * Mat3::from_translation(-Vec2::new(0.5, 0.5)); + * Affine2::from_translation(-Vec2::new(0.5, 0.5)); let geo_scale = { let Scale { x, y } = elem_geo.size.to_f64() / geo.size.to_f64(); - Mat3::from_scale(Vec2::new(x as f32, y as f32)) + Affine2::from_scale(Vec2::new(x as f32, y as f32)) }; let geo_translation = { let offset = (elem_geo.loc - geo.loc).to_f64(); - Mat3::from_translation(Vec2::new( + Affine2::from_translation(Vec2::new( (offset.x / elem_geo.size.w as f64) as f32, (offset.y / elem_geo.size.h as f64) as f32, )) @@ -82,16 +82,17 @@ where let buf_scale = { let Scale { x, y } = buf_size.to_f64() / view.src.size.to_f64(); - Mat3::from_scale(Vec2::new(x as f32, y as f32)) + Affine2::from_scale(Vec2::new(x as f32, y as f32)) }; - let buf_translation = Mat3::from_translation(Vec2::new( + let buf_translation = Affine2::from_translation(Vec2::new( (view.src.loc.x / buf_size.w as f64) as f32, (view.src.loc.y / buf_size.h as f64) as f32, )); - let input_to_geo = - transform_matrix * geo_scale * geo_translation * buf_scale * buf_translation; + let input_to_geo = Mat3::from( + transform_matrix * geo_scale * geo_translation * buf_scale * buf_translation, + ); let uniforms = vec![ Uniform::new("geo_size", (geometry.size.w as f32, geometry.size.h as f32)), From d84d5d46cebaf70e2b1bacd00a6a3d84ef95ceca Mon Sep 17 00:00:00 2001 From: Ian Douglas Scott Date: Thu, 2 Apr 2026 13:00:02 -0700 Subject: [PATCH 75/92] image-copy: Use seat from `WlPointer` instead of `last_active()` --- .../handlers/image_copy_capture/mod.rs | 56 +++++++++++-------- 1 file changed, 34 insertions(+), 22 deletions(-) diff --git a/src/wayland/handlers/image_copy_capture/mod.rs b/src/wayland/handlers/image_copy_capture/mod.rs index 733e05ef..3f08b2dd 100644 --- a/src/wayland/handlers/image_copy_capture/mod.rs +++ b/src/wayland/handlers/image_copy_capture/mod.rs @@ -14,6 +14,7 @@ use smithay::{ }, }, desktop::space::SpaceElement, + input::{Seat, pointer::PointerHandle}, output::Output, reexports::wayland_server::protocol::{wl_pointer::WlPointer, wl_shm::Format as ShmFormat}, utils::{Buffer as BufferCoords, Point, Size, Transform}, @@ -30,7 +31,7 @@ use smithay::{ }; use crate::{ - shell::CosmicSurface, + shell::{CosmicSurface, Shell}, state::{BackendData, State}, utils::prelude::{ OutputExt, PointExt, PointGlobalExt, PointLocalExt, RectExt, RectLocalExt, SeatExt, @@ -44,6 +45,14 @@ pub use self::render::*; use self::user_data::*; pub use self::user_data::{FrameHolder, ImageCopySessions, SessionData, SessionHolder}; +fn seat_for_wl_pointer<'a>(shell: &'a Shell, pointer: &WlPointer) -> Option<&'a Seat> { + let pointer_handle = PointerHandle::::from_resource(pointer)?; + shell + .seats + .iter() + .find(|seat| seat.get_pointer().is_some_and(|p| p == pointer_handle)) +} + impl ImageCopyCaptureHandler for State { fn image_copy_capture_state(&mut self) -> &mut ImageCopyCaptureState { &mut self.common.image_copy_capture_state @@ -73,15 +82,12 @@ impl ImageCopyCaptureHandler for State { fn cursor_capture_constraints( &mut self, _source: &ImageCaptureSource, - _pointer: &WlPointer, + pointer: &WlPointer, ) -> Option { - let size = if let Some((geometry, _)) = self - .common - .shell - .read() - .seats - .last_active() - .cursor_geometry((0.0, 0.0), self.common.clock.now()) + let shell = self.common.shell.read(); + let seat = seat_for_wl_pointer(&shell, pointer)?; + let size = if let Some((geometry, _)) = + seat.cursor_geometry((0.0, 0.0), self.common.clock.now()) { geometry.size } else { @@ -149,20 +155,23 @@ impl ImageCopyCaptureHandler for State { fn new_cursor_session(&mut self, session: CursorSession) { let (pointer_loc, pointer_size, hotspot) = { - let seat = self.common.shell.read().seats.last_active().clone(); + let shell = self.common.shell.read(); + if let Some(seat) = seat_for_wl_pointer(&shell, &session.pointer()) { + let pointer = seat.get_pointer().unwrap(); + let pointer_loc = pointer.current_location().to_i32_round().as_global(); - let pointer = seat.get_pointer().unwrap(); - let pointer_loc = pointer.current_location().to_i32_round().as_global(); + let (pointer_size, hotspot) = if let Some((geometry, hotspot)) = + seat.cursor_geometry((0.0, 0.0), self.common.clock.now()) + { + (geometry.size, hotspot) + } else { + (Size::from((64, 64)), Point::from((0, 0))) + }; - let (pointer_size, hotspot) = if let Some((geometry, hotspot)) = - seat.cursor_geometry((0.0, 0.0), self.common.clock.now()) - { - (geometry.size, hotspot) + (pointer_loc, pointer_size, hotspot) } else { - (Size::from((64, 64)), Point::from((0, 0))) - }; - - (pointer_loc, pointer_size, hotspot) + (Point::new(0, 0), Size::from((64, 64)), Point::from((0, 0))) + } }; session.user_data().insert_if_missing_threadsafe(|| { @@ -299,8 +308,11 @@ impl ImageCopyCaptureHandler for State { return; } - let seat = self.common.shell.read().seats.last_active().clone(); - render_cursor_to_buffer(self, session, frame, &seat); + let shell = self.common.shell.read(); + if let Some(seat) = seat_for_wl_pointer(&shell, &session.pointer()).cloned() { + drop(shell); + render_cursor_to_buffer(self, session, frame, &seat); + } } fn frame_aborted(&mut self, frame: FrameRef) { From dd6ebd03c933926dc6bdcecbaf3a7cf0a1ba23c1 Mon Sep 17 00:00:00 2001 From: Ian Douglas Scott Date: Tue, 7 Jul 2026 11:46:42 -0700 Subject: [PATCH 76/92] image-copy: Hold references to `Buffer`s to block release The `wl_buffer` now won't be released until sync fence polls ready. --- src/backend/kms/surface/mod.rs | 4 +- src/backend/render/mod.rs | 19 ++- .../handlers/image_copy_capture/render.rs | 123 +++++++++++++----- 3 files changed, 103 insertions(+), 43 deletions(-) diff --git a/src/backend/kms/surface/mod.rs b/src/backend/kms/surface/mod.rs index a277af2e..bb28bc3c 100644 --- a/src/backend/kms/surface/mod.rs +++ b/src/backend/kms/surface/mod.rs @@ -1674,7 +1674,7 @@ fn send_screencopy_result<'a>( pre_postprocess_data: &mut PrePostprocessData, tx: &std::sync::mpsc::Sender, frame_result: &RenderFrameResult>>, - elements: &[CosmicElement], + elements: &[CosmicElement>], (session, frame, res): ( &ScreencopySessionRef, ScreencopyFrame, @@ -1855,6 +1855,8 @@ fn send_screencopy_result<'a>( transform, damage.as_deref(), sync, + // Don't reference `Buffer`s since we blit from framebuffer/postprocess buffer + vec![], )? { if frame_result.is_empty { data.frame diff --git a/src/backend/render/mod.rs b/src/backend/render/mod.rs index 472abb8e..154ad986 100644 --- a/src/backend/render/mod.rs +++ b/src/backend/render/mod.rs @@ -41,7 +41,9 @@ use crate::{ compositor::FRAME_TIME_FILTER, corner_radius::{pad_rect, surface_corners, surface_padding}, data_device::get_dnd_icon, - image_copy_capture::{FrameHolder, SessionData, render_session}, + image_copy_capture::{ + FrameHolder, SessionData, render_element_buffers, render_session, + }, }, protocols::workspace::WorkspaceHandle, }, @@ -1505,11 +1507,16 @@ where } } - Ok(RenderOutputResult { - damage: res.0, - sync, - states: res.1, - }) + let buffers = render_element_buffers(renderer, &elements); + + Ok(( + RenderOutputResult { + damage: res.0, + sync, + states: res.1, + }, + buffers, + )) }, )? { pending_image_copy_data.send_success_when_ready( diff --git a/src/wayland/handlers/image_copy_capture/render.rs b/src/wayland/handlers/image_copy_capture/render.rs index ae429b4d..0cb62529 100644 --- a/src/wayland/handlers/image_copy_capture/render.rs +++ b/src/wayland/handlers/image_copy_capture/render.rs @@ -9,7 +9,7 @@ use smithay::{ buffer_dimensions, buffer_type, damage::{Error as DTError, OutputDamageTracker, RenderOutputResult}, element::{ - RenderElement, + RenderElement, UnderlyingStorage, utils::{Relocate, RelocateRenderElement}, }, gles::{GlesError, GlesRenderbuffer}, @@ -58,10 +58,30 @@ use crate::{ use super::{super::data_device::get_dnd_icon, user_data::SessionHolder}; +pub fn render_element_buffers( + renderer: &mut R, + elements: &[E], +) -> Vec +where + R: AsGlowRenderer, + E: RenderElement, +{ + elements + .iter() + .filter_map(|elem| match elem.underlying_storage(renderer) { + Some(UnderlyingStorage::Wayland(buffer)) => Some(buffer.clone()), + Some(UnderlyingStorage::Memory(_)) | None => None, + }) + .collect() +} + pub struct PendingImageCopyData { pub frame: Frame, pub damage: Vec>, pub sync: SyncPoint, + // Hold reference so `wl_buffer` isn't released and sync point isn't signaled + // until image copy completes. + _buffers: Vec, } impl PendingImageCopyData { @@ -104,6 +124,7 @@ pub fn submit_buffer( transform: Transform, damage: Option<&[Rectangle]>, mut sync: SyncPoint, + buffers: Vec, ) -> Result, R::Error> where R: ExportMem + AsGlowRenderer, @@ -182,6 +203,7 @@ where }) .collect(), sync, + _buffers: buffers, })) } @@ -201,7 +223,13 @@ where &'d mut OutputDamageTracker, usize, Vec>, - ) -> Result, DTError>, + ) -> Result< + ( + RenderOutputResult<'d>, + Vec, + ), + DTError, + >, { let mut session_user_data = session.lock().unwrap(); @@ -245,30 +273,25 @@ where .as_mut() .map(|(_, tex)| renderer.bind(tex).map_err(DTError::Rendering)) .transpose()?; - let res = render_fn( + let (result, buffers) = render_fn( &frame.buffer(), renderer, fb.as_mut(), dt, age, frame.damage(), - ); + )?; - match res { - Ok(result) => submit_buffer( - frame, - renderer, - fb.as_mut(), - transform, - result.damage.map(|x| x.as_slice()), - result.sync, - ) - .map_err(DTError::Rendering), - Err(err) => { - frame.fail(CaptureFailureReason::Unknown); - Err(err) - } - } + submit_buffer( + frame, + renderer, + fb.as_mut(), + transform, + result.damage.map(|x| x.as_slice()), + result.sync, + buffers, + ) + .map_err(DTError::Rendering) } pub fn render_workspace_to_buffer( @@ -316,7 +339,13 @@ pub fn render_workspace_to_buffer( common: &mut Common, output: &Output, handle: (WorkspaceHandle, usize), - ) -> Result, DTError> + ) -> Result< + ( + RenderOutputResult<'d>, + Vec, + ), + DTError, + > where R: AsGlowRenderer, R::TextureId: Send + Clone + 'static, @@ -356,7 +385,7 @@ pub fn render_workspace_to_buffer( .collect() }); - if let Ok(dmabuf) = get_dmabuf(buffer) { + let (res, elements) = if let Ok(dmabuf) = get_dmabuf(buffer) { let mut dmabuf = dmabuf.clone(); let mut fb = renderer.bind(&mut dmabuf).map_err(DTError::Rendering)?; render_workspace( @@ -374,8 +403,7 @@ pub fn render_workspace_to_buffer( handle, cursor_mode, ElementFilter::ExcludeWorkspaceOverview, - ) - .map(|res| res.0) + )? } else { let target = offscreen.expect("shm buffers should have an offscreen target"); render_workspace( @@ -393,9 +421,12 @@ pub fn render_workspace_to_buffer( handle, cursor_mode, ElementFilter::ExcludeWorkspaceOverview, - ) - .map(|res| res.0) - } + )? + }; + + let buffers = render_element_buffers(renderer, &elements); + + Ok((res, buffers)) } let draw_cursor = session.draw_cursor(); @@ -549,7 +580,13 @@ pub fn render_window_to_buffer( common: &mut Common, toplevel: &CosmicSurface, geometry: Rectangle, - ) -> Result, DTError> + ) -> Result< + ( + RenderOutputResult<'d>, + Vec, + ), + DTError, + > where R: AsGlowRenderer, R::TextureId: Send + Clone + 'static, @@ -650,16 +687,20 @@ pub fn render_window_to_buffer( None, ); - if let Ok(dmabuf) = get_dmabuf(buffer) { + let res = if let Ok(dmabuf) = get_dmabuf(buffer) { let mut dmabuf_clone = dmabuf.clone(); let mut fb = renderer .bind(&mut dmabuf_clone) .map_err(DTError::Rendering)?; - dt.render_output(renderer, &mut fb, age, &elements, Color32F::TRANSPARENT) + dt.render_output(renderer, &mut fb, age, &elements, Color32F::TRANSPARENT)? } else { let fb = offscreen.expect("shm buffer should have an offscreen target"); - dt.render_output(renderer, fb, age, &elements, Color32F::TRANSPARENT) - } + dt.render_output(renderer, fb, age, &elements, Color32F::TRANSPARENT)? + }; + + let buffers = render_element_buffers(renderer, &elements); + + Ok((res, buffers)) } let common = &mut state.common; @@ -797,7 +838,13 @@ pub fn render_cursor_to_buffer( additional_damage: Vec>, common: &mut Common, seat: &Seat, - ) -> Result, DTError> + ) -> Result< + ( + RenderOutputResult<'d>, + Vec, + ), + DTError, + > where R: AsGlowRenderer, R::TextureId: Send + Clone + 'static, @@ -830,16 +877,20 @@ pub fn render_cursor_to_buffer( }, ); - if let Ok(dmabuf) = get_dmabuf(buffer) { + let res = if let Ok(dmabuf) = get_dmabuf(buffer) { let mut dmabuf_clone = dmabuf.clone(); let mut fb = renderer .bind(&mut dmabuf_clone) .map_err(DTError::Rendering)?; - dt.render_output(renderer, &mut fb, age, &elements, [0.0, 0.0, 0.0, 0.0]) + dt.render_output(renderer, &mut fb, age, &elements, [0.0, 0.0, 0.0, 0.0])? } else { let fb = offscreen.expect("shm buffers should have offscreen target"); - dt.render_output(renderer, fb, age, &elements, [0.0, 0.0, 0.0, 0.0]) - } + dt.render_output(renderer, fb, age, &elements, [0.0, 0.0, 0.0, 0.0])? + }; + + let buffers = render_element_buffers(renderer, &elements); + + Ok((res, buffers)) } let common = &mut state.common; From 27956e6e007c2efa96ce6cc25df494216d0d4b2e Mon Sep 17 00:00:00 2001 From: Ian Douglas Scott Date: Mon, 15 Jun 2026 13:52:32 -0700 Subject: [PATCH 77/92] Make `cursor_geometry()` return a `CursorGeometry` struct A bit better than just a tuple. --- src/input/mod.rs | 14 +++++----- src/shell/focus/target.rs | 4 +-- src/shell/seats.rs | 26 ++++++++++++++----- .../handlers/image_copy_capture/mod.rs | 6 ++--- .../handlers/image_copy_capture/render.rs | 4 +-- 5 files changed, 33 insertions(+), 21 deletions(-) diff --git a/src/input/mod.rs b/src/input/mod.rs index 48db97db..a9351310 100644 --- a/src/input/mod.rs +++ b/src/input/mod.rs @@ -11,7 +11,7 @@ use crate::{ }, input::gestures::{GestureState, SwipeAction}, shell::{ - LastModifierChange, SeatExt, Trigger, + CursorGeometry, LastModifierChange, SeatExt, Trigger, focus::{ Stage, render_input_order, target::{KeyboardFocusTarget, PointerFocusTarget}, @@ -651,7 +651,7 @@ impl State { } for session in cursor_sessions_for_output(&shell, &output) { - if let Some((geometry, offset)) = seat.cursor_geometry( + if let Some(CursorGeometry { geometry, hotspot }) = seat.cursor_geometry( (position - output_geometry.loc.to_f64()) .as_logical() .to_buffer( @@ -677,7 +677,7 @@ impl State { dma: None, }); } - session.set_cursor_hotspot(offset); + session.set_cursor_hotspot(hotspot); session.set_cursor_pos(Some(geometry.loc)); } } @@ -720,7 +720,7 @@ impl State { let shell = self.common.shell.read(); for session in cursor_sessions_for_output(&shell, &output) { - if let Some((geometry, offset)) = seat.cursor_geometry( + if let Some(CursorGeometry { geometry, hotspot }) = seat.cursor_geometry( (position - output_geometry.loc.to_f64()) .as_logical() .to_buffer( @@ -746,7 +746,7 @@ impl State { dma: None, }); } - session.set_cursor_hotspot(offset); + session.set_cursor_hotspot(hotspot); session.set_cursor_pos(Some(geometry.loc)); } } @@ -2482,7 +2482,7 @@ impl State { let output_geometry = output.geometry(); for session in cursor_sessions_for_output(&shell, &output) { - if let Some((geometry, offset)) = seat.cursor_geometry( + if let Some(CursorGeometry { geometry, hotspot }) = seat.cursor_geometry( point.to_buffer( output.current_scale().fractional_scale(), output.current_transform(), @@ -2501,7 +2501,7 @@ impl State { dma: None, }); } - session.set_cursor_hotspot(offset); + session.set_cursor_hotspot(hotspot); session.set_cursor_pos(Some(geometry.loc)); } } diff --git a/src/shell/focus/target.rs b/src/shell/focus/target.rs index fff77282..929cd92e 100644 --- a/src/shell/focus/target.rs +++ b/src/shell/focus/target.rs @@ -6,7 +6,7 @@ use std::{ use crate::{ shell::{ - CosmicSurface, SeatExt, + CosmicSurface, CursorGeometry, SeatExt, element::{CosmicMapped, CosmicStack, CosmicWindow}, layout::tiling::ResizeForkTarget, zoom::ZoomFocusTarget, @@ -272,7 +272,7 @@ impl PointerFocusTarget { None }; - let cursor_hotspot = if let Some((_, hotspot)) = + let cursor_hotspot = if let Some(CursorGeometry { hotspot, .. }) = seat.cursor_geometry((0.0, 0.0), Duration::from_millis(event.time as u64).into()) { hotspot diff --git a/src/shell/seats.rs b/src/shell/seats.rs index 5273e97f..11211e90 100644 --- a/src/shell/seats.rs +++ b/src/shell/seats.rs @@ -243,6 +243,12 @@ pub fn create_seat( seat } +#[derive(Debug, Copy, Clone)] +pub struct CursorGeometry { + pub geometry: Rectangle, + pub hotspot: Point, +} + pub trait SeatExt { fn id(&self) -> usize; @@ -266,7 +272,7 @@ pub trait SeatExt { &self, loc: impl Into>, time: Time, - ) -> Option<(Rectangle, Point)>; + ) -> Option; fn cursor_image_status(&self) -> CursorImageStatus; fn set_cursor_image_status(&self, status: CursorImageStatus); } @@ -367,7 +373,7 @@ impl SeatExt for Seat { &self, loc: impl Into>, time: Time, - ) -> Option<(Rectangle, Point)> { + ) -> Option { let location = loc.into().to_i32_round(); match self.cursor_image_status() { @@ -386,7 +392,10 @@ impl SeatExt for Seat { (geo.loc.x, geo.loc.y).into(), geo.size.to_buffer(1, Transform::Normal), ); - Some((buffer_geo, (hotspot.x, hotspot.y).into())) + Some(CursorGeometry { + geometry: buffer_geo, + hotspot: (hotspot.x, hotspot.y).into(), + }) } CursorImageStatus::Named(cursor_icon) => { let seat_userdata = self.user_data(); @@ -398,10 +407,13 @@ impl SeatExt for Seat { .get_named_cursor(cursor_icon) .get_image(1, time.as_millis()); - Some(( - Rectangle::new(location, (frame.width as i32, frame.height as i32).into()), - (frame.xhot as i32, frame.yhot as i32).into(), - )) + Some(CursorGeometry { + geometry: Rectangle::new( + location, + (frame.width as i32, frame.height as i32).into(), + ), + hotspot: (frame.xhot as i32, frame.yhot as i32).into(), + }) } CursorImageStatus::Hidden => None, } diff --git a/src/wayland/handlers/image_copy_capture/mod.rs b/src/wayland/handlers/image_copy_capture/mod.rs index 3f08b2dd..f76e96b5 100644 --- a/src/wayland/handlers/image_copy_capture/mod.rs +++ b/src/wayland/handlers/image_copy_capture/mod.rs @@ -31,7 +31,7 @@ use smithay::{ }; use crate::{ - shell::{CosmicSurface, Shell}, + shell::{CosmicSurface, CursorGeometry, Shell}, state::{BackendData, State}, utils::prelude::{ OutputExt, PointExt, PointGlobalExt, PointLocalExt, RectExt, RectLocalExt, SeatExt, @@ -86,7 +86,7 @@ impl ImageCopyCaptureHandler for State { ) -> Option { let shell = self.common.shell.read(); let seat = seat_for_wl_pointer(&shell, pointer)?; - let size = if let Some((geometry, _)) = + let size = if let Some(CursorGeometry { geometry, .. }) = seat.cursor_geometry((0.0, 0.0), self.common.clock.now()) { geometry.size @@ -160,7 +160,7 @@ impl ImageCopyCaptureHandler for State { let pointer = seat.get_pointer().unwrap(); let pointer_loc = pointer.current_location().to_i32_round().as_global(); - let (pointer_size, hotspot) = if let Some((geometry, hotspot)) = + let (pointer_size, hotspot) = if let Some(CursorGeometry { geometry, hotspot }) = seat.cursor_geometry((0.0, 0.0), self.common.clock.now()) { (geometry.size, hotspot) diff --git a/src/wayland/handlers/image_copy_capture/render.rs b/src/wayland/handlers/image_copy_capture/render.rs index 0cb62529..21976fcb 100644 --- a/src/wayland/handlers/image_copy_capture/render.rs +++ b/src/wayland/handlers/image_copy_capture/render.rs @@ -45,7 +45,7 @@ use crate::{ render_workspace, wayland::SurfaceRenderElement, }, - shell::{CosmicMappedRenderElement, CosmicSurface, WorkspaceRenderElement}, + shell::{CosmicMappedRenderElement, CosmicSurface, CursorGeometry, WorkspaceRenderElement}, state::{Common, KmsNodes, State}, utils::prelude::{PointExt, PointGlobalExt, RectExt, RectLocalExt, SeatExt}, wayland::{ @@ -804,7 +804,7 @@ pub fn render_cursor_to_buffer( let buffer = frame.buffer(); let mut cursor_size = seat .cursor_geometry((0.0, 0.0), state.common.clock.now()) - .map(|(geo, _hotspot)| geo.size) + .map(|CursorGeometry { geometry, .. }| geometry.size) .unwrap_or_else(|| Size::from((64, 64))); let buffer_size = buffer_dimensions(&buffer).unwrap(); // Client shouldn't try to allocate 0x0 buffer From 2648aea0df5cd735e06f9f2ec4c75ed6cef85357 Mon Sep 17 00:00:00 2001 From: Ian Douglas Scott Date: Mon, 15 Jun 2026 16:23:31 -0700 Subject: [PATCH 78/92] image-copy: Move duplicate code to a `cursor_capture_constraints()` This ensures we avoid advertising a `0,0` buffer size (which will protocol error to actually allocate), and avoids defining the supported formats for cursor capture buffers in a bunch of places. --- src/input/mod.rs | 73 +++++++------------ .../handlers/image_copy_capture/mod.rs | 41 +++++++---- .../handlers/image_copy_capture/render.rs | 36 ++++----- 3 files changed, 68 insertions(+), 82 deletions(-) diff --git a/src/input/mod.rs b/src/input/mod.rs index a9351310..4cd7cd8b 100644 --- a/src/input/mod.rs +++ b/src/input/mod.rs @@ -11,7 +11,7 @@ use crate::{ }, input::gestures::{GestureState, SwipeAction}, shell::{ - CursorGeometry, LastModifierChange, SeatExt, Trigger, + LastModifierChange, SeatExt, Trigger, focus::{ Stage, render_input_order, target::{KeyboardFocusTarget, PointerFocusTarget}, @@ -25,7 +25,8 @@ use crate::{ }, utils::{prelude::*, quirks::workspace_overview_is_open}, wayland::handlers::{ - image_copy_capture::SessionHolder, xwayland_keyboard_grab::XWaylandGrabSeat, + image_copy_capture::{SessionHolder, cursor_capture_constraints}, + xwayland_keyboard_grab::XWaylandGrabSeat, }, }; use calloop::{ @@ -60,15 +61,12 @@ use smithay::{ output::Output, reexports::{ input::Device as InputDevice, - wayland_server::{ - Resource as _, - protocol::{wl_shm::Format as ShmFormat, wl_surface::WlSurface}, - }, + wayland_server::{Resource as _, protocol::wl_surface::WlSurface}, }, - utils::{Logical, Point, Rectangle, SERIAL_COUNTER, Serial, Size}, + utils::{Logical, Point, Rectangle, SERIAL_COUNTER, Serial}, wayland::{ compositor::CompositorHandler, - image_copy_capture::{BufferConstraints, CursorSessionRef}, + image_copy_capture::CursorSessionRef, keyboard_shortcuts_inhibit::KeyboardShortcutsInhibitorSeat, pointer_constraints::{PointerConstraint, with_pointer_constraint}, seat::WaylandFocus, @@ -651,7 +649,7 @@ impl State { } for session in cursor_sessions_for_output(&shell, &output) { - if let Some(CursorGeometry { geometry, hotspot }) = seat.cursor_geometry( + if let Some(cursor_geometry) = seat.cursor_geometry( (position - output_geometry.loc.to_f64()) .as_logical() .to_buffer( @@ -661,24 +659,18 @@ impl State { ), self.common.clock.now(), ) { + let constraints = cursor_capture_constraints(Some(cursor_geometry)); if session .current_constraints() - .map(|constraint| constraint.size != geometry.size) + .map(|current_constraints| { + current_constraints.size != constraints.size + }) .unwrap_or(true) { - let mut cursor_size = geometry.size; - // Client shouldn't try to allocate 0x0 buffer - if cursor_size == Size::new(0, 0) { - cursor_size = Size::new(1, 1); - } - session.update_constraints(BufferConstraints { - size: cursor_size, - shm: vec![ShmFormat::Argb8888], - dma: None, - }); + session.update_constraints(constraints); } - session.set_cursor_hotspot(hotspot); - session.set_cursor_pos(Some(geometry.loc)); + session.set_cursor_hotspot(cursor_geometry.hotspot); + session.set_cursor_pos(Some(cursor_geometry.geometry.loc)); } } } @@ -720,7 +712,7 @@ impl State { let shell = self.common.shell.read(); for session in cursor_sessions_for_output(&shell, &output) { - if let Some(CursorGeometry { geometry, hotspot }) = seat.cursor_geometry( + if let Some(cursor_geometry) = seat.cursor_geometry( (position - output_geometry.loc.to_f64()) .as_logical() .to_buffer( @@ -730,24 +722,18 @@ impl State { ), self.common.clock.now(), ) { + let constraints = cursor_capture_constraints(Some(cursor_geometry)); if session .current_constraints() - .map(|constraint| constraint.size != geometry.size) + .map(|current_constraints| { + current_constraints.size != constraints.size + }) .unwrap_or(true) { - let mut cursor_size = geometry.size; - // Client shouldn't try to allocate 0x0 buffer - if cursor_size == Size::new(0, 0) { - cursor_size = Size::new(1, 1); - } - session.update_constraints(BufferConstraints { - size: cursor_size, - shm: vec![ShmFormat::Argb8888], - dma: None, - }); + session.update_constraints(constraints); } - session.set_cursor_hotspot(hotspot); - session.set_cursor_pos(Some(geometry.loc)); + session.set_cursor_hotspot(cursor_geometry.hotspot); + session.set_cursor_pos(Some(cursor_geometry.geometry.loc)); } } } @@ -2482,7 +2468,7 @@ impl State { let output_geometry = output.geometry(); for session in cursor_sessions_for_output(&shell, &output) { - if let Some(CursorGeometry { geometry, hotspot }) = seat.cursor_geometry( + if let Some(cursor_geometry) = seat.cursor_geometry( point.to_buffer( output.current_scale().fractional_scale(), output.current_transform(), @@ -2490,19 +2476,16 @@ impl State { ), self.common.clock.now(), ) { + let constraints = cursor_capture_constraints(Some(cursor_geometry)); if session .current_constraints() - .map(|constraint| constraint.size != geometry.size) + .map(|current_constraints| current_constraints.size != constraints.size) .unwrap_or(true) { - session.update_constraints(BufferConstraints { - size: geometry.size, - shm: vec![ShmFormat::Argb8888], - dma: None, - }); + session.update_constraints(constraints); } - session.set_cursor_hotspot(hotspot); - session.set_cursor_pos(Some(geometry.loc)); + session.set_cursor_hotspot(cursor_geometry.hotspot); + session.set_cursor_pos(Some(cursor_geometry.geometry.loc)); } } } diff --git a/src/wayland/handlers/image_copy_capture/mod.rs b/src/wayland/handlers/image_copy_capture/mod.rs index f76e96b5..85a01c9a 100644 --- a/src/wayland/handlers/image_copy_capture/mod.rs +++ b/src/wayland/handlers/image_copy_capture/mod.rs @@ -45,6 +45,10 @@ pub use self::render::*; use self::user_data::*; pub use self::user_data::{FrameHolder, ImageCopySessions, SessionData, SessionHolder}; +fn default_cursor_size() -> Size { + Size::new(64, 64) +} + fn seat_for_wl_pointer<'a>(shell: &'a Shell, pointer: &WlPointer) -> Option<&'a Seat> { let pointer_handle = PointerHandle::::from_resource(pointer)?; shell @@ -53,6 +57,24 @@ fn seat_for_wl_pointer<'a>(shell: &'a Shell, pointer: &WlPointer) -> Option<&'a .find(|seat| seat.get_pointer().is_some_and(|p| p == pointer_handle)) } +pub fn cursor_capture_constraints(cursor_geometry: Option) -> BufferConstraints { + let size = if let Some(cursor_geometry) = cursor_geometry { + let mut size = cursor_geometry.geometry.size; + // Client shouldn't try to allocate 0x0 buffer + if size == Size::new(0, 0) { + size = Size::new(1, 1); + } + size + } else { + default_cursor_size() + }; + BufferConstraints { + size, + shm: vec![ShmFormat::Argb8888], + dma: None, + } +} + impl ImageCopyCaptureHandler for State { fn image_copy_capture_state(&mut self) -> &mut ImageCopyCaptureState { &mut self.common.image_copy_capture_state @@ -86,19 +108,8 @@ impl ImageCopyCaptureHandler for State { ) -> Option { let shell = self.common.shell.read(); let seat = seat_for_wl_pointer(&shell, pointer)?; - let size = if let Some(CursorGeometry { geometry, .. }) = - seat.cursor_geometry((0.0, 0.0), self.common.clock.now()) - { - geometry.size - } else { - Size::from((64, 64)) - }; - - Some(BufferConstraints { - size, - shm: vec![ShmFormat::Argb8888], - dma: None, - }) + let cursor_geometry = seat.cursor_geometry((0.0, 0.0), self.common.clock.now()); + Some(cursor_capture_constraints(cursor_geometry)) } fn new_session(&mut self, session: Session) { @@ -165,12 +176,12 @@ impl ImageCopyCaptureHandler for State { { (geometry.size, hotspot) } else { - (Size::from((64, 64)), Point::from((0, 0))) + (default_cursor_size(), Point::from((0, 0))) }; (pointer_loc, pointer_size, hotspot) } else { - (Point::new(0, 0), Size::from((64, 64)), Point::from((0, 0))) + (Point::new(0, 0), default_cursor_size(), Point::from((0, 0))) } }; diff --git a/src/wayland/handlers/image_copy_capture/render.rs b/src/wayland/handlers/image_copy_capture/render.rs index 21976fcb..b2ebc054 100644 --- a/src/wayland/handlers/image_copy_capture/render.rs +++ b/src/wayland/handlers/image_copy_capture/render.rs @@ -20,16 +20,14 @@ use smithay::{ desktop::space::SpaceElement, input::Seat, output::{Output, OutputNoMode}, - reexports::wayland_server::protocol::{wl_buffer::WlBuffer, wl_shm::Format as ShmFormat}, + reexports::wayland_server::protocol::wl_buffer::WlBuffer, utils::{ Buffer as BufferCoords, IsAlive, Logical, Physical, Point, Rectangle, Scale, Size, Transform, }, wayland::{ dmabuf::get_dmabuf, - image_copy_capture::{ - BufferConstraints, CaptureFailureReason, CursorSessionRef, Frame, SessionRef, - }, + image_copy_capture::{CaptureFailureReason, CursorSessionRef, Frame, SessionRef}, seat::WaylandFocus, shm::{shm_format_to_fourcc, with_buffer_contents, with_buffer_contents_mut}, }, @@ -45,7 +43,7 @@ use crate::{ render_workspace, wayland::SurfaceRenderElement, }, - shell::{CosmicMappedRenderElement, CosmicSurface, CursorGeometry, WorkspaceRenderElement}, + shell::{CosmicMappedRenderElement, CosmicSurface, WorkspaceRenderElement}, state::{Common, KmsNodes, State}, utils::prelude::{PointExt, PointGlobalExt, RectExt, RectLocalExt, SeatExt}, wayland::{ @@ -56,7 +54,9 @@ use crate::{ }, }; -use super::{super::data_device::get_dnd_icon, user_data::SessionHolder}; +use super::{ + super::data_device::get_dnd_icon, cursor_capture_constraints, user_data::SessionHolder, +}; pub fn render_element_buffers( renderer: &mut R, @@ -802,25 +802,17 @@ pub fn render_cursor_to_buffer( seat: &Seat, ) { let buffer = frame.buffer(); - let mut cursor_size = seat - .cursor_geometry((0.0, 0.0), state.common.clock.now()) - .map(|CursorGeometry { geometry, .. }| geometry.size) - .unwrap_or_else(|| Size::from((64, 64))); + let cursor_geometry = seat.cursor_geometry((0.0, 0.0), state.common.clock.now()); + let constraints = cursor_capture_constraints(cursor_geometry); let buffer_size = buffer_dimensions(&buffer).unwrap(); - // Client shouldn't try to allocate 0x0 buffer - if cursor_size == Size::new(0, 0) { - cursor_size = Size::new(1, 1); - } - if buffer_size != cursor_size { - let constraints = BufferConstraints { - size: cursor_size, - shm: vec![ShmFormat::Argb8888], - dma: None, - }; - session.update_constraints(constraints); + if buffer_size != constraints.size { + session.update_constraints(constraints.clone()); if let Some(data) = session.user_data().get::() { *data.lock().unwrap() = SessionUserData::new(OutputDamageTracker::new( - cursor_size.to_logical(1, Transform::Normal).to_physical(1), + constraints + .size + .to_logical(1, Transform::Normal) + .to_physical(1), 1.0, Transform::Normal, )); From c92c18e09e46d24a2ec9dcca030f6004a914dbd2 Mon Sep 17 00:00:00 2001 From: Ian Douglas Scott Date: Tue, 14 Jul 2026 15:04:53 -0700 Subject: [PATCH 79/92] Use `Global` coordinate instead of inferring as `Logical` Inference behavior can be confusing, and the coordinate here is global, so use `Global` like the `PointerMotion*` event handlers. --- src/input/mod.rs | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/src/input/mod.rs b/src/input/mod.rs index 4cd7cd8b..38363cbc 100644 --- a/src/input/mod.rs +++ b/src/input/mod.rs @@ -2437,7 +2437,7 @@ impl State { if is_legal(pos_in_element) { let x = workspace_origin.x + origin.x + pos_in_element.x; let y = workspace_origin.y + origin.y + pos_in_element.y; - Some((Point::new(x, y), output.clone())) + Some((Point::<_, Global>::new(x, y), output.clone())) } else { None } @@ -2448,10 +2448,10 @@ impl State { if let Some((point, output)) = point_and_output { let original_position = pointer.current_location(); - pointer.set_location(point); + pointer.set_location(point.as_logical()); let mut shell = self.common.shell.write(); - shell.update_pointer_position(point.as_global().to_local(&output), &output); + shell.update_pointer_position(point.to_local(&output), &output); let seat = shell .seats @@ -2469,7 +2469,7 @@ impl State { let output_geometry = output.geometry(); for session in cursor_sessions_for_output(&shell, &output) { if let Some(cursor_geometry) = seat.cursor_geometry( - point.to_buffer( + point.as_logical().to_buffer( output.current_scale().fractional_scale(), output.current_transform(), &output_geometry.size.to_f64().as_logical(), From dda2d148328d84fe7c1e0e5c4bf126c11a3d1f1b Mon Sep 17 00:00:00 2001 From: Ian Douglas Scott Date: Mon, 13 Jul 2026 19:35:14 -0700 Subject: [PATCH 80/92] input: Send `wl_pointer.motion` on warp, and applying constraint hint Gnome seems to send `motion` in these cases. The spec isn't 100% explicit, but it seems `motion` events are needed for the client to have the same understanding of where the pointer is as the compositor, which generally seems to be desired. This is also needed for toplevel cursor image-copy to get the position change. Likewise, we want image-copy to have the same understanding of where the cursor is as the compositor and client. --- src/input/mod.rs | 25 ++++++++++++++++++++++++- 1 file changed, 24 insertions(+), 1 deletion(-) diff --git a/src/input/mod.rs b/src/input/mod.rs index 38363cbc..e0e1cccb 100644 --- a/src/input/mod.rs +++ b/src/input/mod.rs @@ -2447,8 +2447,31 @@ impl State { }; if let Some((point, output)) = point_and_output { + // TODO: Replace with `wl_pointer.warp` let original_position = pointer.current_location(); - pointer.set_location(point.as_logical()); + let serial = SERIAL_COUNTER.next_serial(); + let under = State::surface_under(point, &output, &self.common.shell.write()) + .map(|(target, pos)| (target, pos.as_logical())); + let time = self.common.clock.now(); + pointer.relative_motion( + self, + under.clone(), + &RelativeMotionEvent { + delta: (0., 0.).into(), + delta_unaccel: (0., 0.).into(), + utime: time.as_micros(), + }, + ); + pointer.motion( + self, + under, + &MotionEvent { + location: point.as_logical(), + serial, + time: time.as_millis(), + }, + ); + pointer.frame(self); let mut shell = self.common.shell.write(); shell.update_pointer_position(point.to_local(&output), &output); From 6c6b56a7f3873050d891393bb2a1d943c3b054f7 Mon Sep 17 00:00:00 2001 From: Ian Douglas Scott Date: Wed, 17 Jun 2026 14:10:51 -0700 Subject: [PATCH 81/92] input: Deduplicate into `update_output_image_copy_cursor_position` --- src/input/mod.rs | 128 ++++++++++++++++++++--------------------------- 1 file changed, 55 insertions(+), 73 deletions(-) diff --git a/src/input/mod.rs b/src/input/mod.rs index e0e1cccb..33fb3148 100644 --- a/src/input/mod.rs +++ b/src/input/mod.rs @@ -63,7 +63,7 @@ use smithay::{ input::Device as InputDevice, wayland_server::{Resource as _, protocol::wl_surface::WlSurface}, }, - utils::{Logical, Point, Rectangle, SERIAL_COUNTER, Serial}, + utils::{Clock, Logical, Monotonic, Point, Rectangle, SERIAL_COUNTER, Serial}, wayland::{ compositor::CompositorHandler, image_copy_capture::CursorSessionRef, @@ -648,31 +648,13 @@ impl State { seat.set_active_output(&output); } - for session in cursor_sessions_for_output(&shell, &output) { - if let Some(cursor_geometry) = seat.cursor_geometry( - (position - output_geometry.loc.to_f64()) - .as_logical() - .to_buffer( - output.current_scale().fractional_scale(), - output.current_transform(), - &output_geometry.size.to_f64().as_logical(), - ), - self.common.clock.now(), - ) { - let constraints = cursor_capture_constraints(Some(cursor_geometry)); - if session - .current_constraints() - .map(|current_constraints| { - current_constraints.size != constraints.size - }) - .unwrap_or(true) - { - session.update_constraints(constraints); - } - session.set_cursor_hotspot(cursor_geometry.hotspot); - session.set_cursor_pos(Some(cursor_geometry.geometry.loc)); - } - } + update_output_image_copy_cursor_position( + &shell, + &self.common.clock, + &output, + &seat, + position, + ); } } InputEvent::PointerMotionAbsolute { event, .. } => { @@ -711,31 +693,13 @@ impl State { ptr.frame(self); let shell = self.common.shell.read(); - for session in cursor_sessions_for_output(&shell, &output) { - if let Some(cursor_geometry) = seat.cursor_geometry( - (position - output_geometry.loc.to_f64()) - .as_logical() - .to_buffer( - output.current_scale().fractional_scale(), - output.current_transform(), - &output_geometry.size.to_f64().as_logical(), - ), - self.common.clock.now(), - ) { - let constraints = cursor_capture_constraints(Some(cursor_geometry)); - if session - .current_constraints() - .map(|current_constraints| { - current_constraints.size != constraints.size - }) - .unwrap_or(true) - { - session.update_constraints(constraints); - } - session.set_cursor_hotspot(cursor_geometry.hotspot); - session.set_cursor_pos(Some(cursor_geometry.geometry.loc)); - } - } + update_output_image_copy_cursor_position( + &shell, + &self.common.clock, + &output, + &seat, + position, + ); } } InputEvent::PointerButton { event, .. } => { @@ -2489,28 +2453,13 @@ impl State { self.common.config.cosmic_conf.accessibility_zoom.view_moves, ); - let output_geometry = output.geometry(); - for session in cursor_sessions_for_output(&shell, &output) { - if let Some(cursor_geometry) = seat.cursor_geometry( - point.as_logical().to_buffer( - output.current_scale().fractional_scale(), - output.current_transform(), - &output_geometry.size.to_f64().as_logical(), - ), - self.common.clock.now(), - ) { - let constraints = cursor_capture_constraints(Some(cursor_geometry)); - if session - .current_constraints() - .map(|current_constraints| current_constraints.size != constraints.size) - .unwrap_or(true) - { - session.update_constraints(constraints); - } - session.set_cursor_hotspot(cursor_geometry.hotspot); - session.set_cursor_pos(Some(cursor_geometry.geometry.loc)); - } - } + update_output_image_copy_cursor_position( + &shell, + &self.common.clock, + &output, + &seat, + point, + ); } } } @@ -2571,3 +2520,36 @@ fn mapped_output_for_device<'a, D: Device + 'static>( }; map_to_output.or_else(|| shell.builtin_output()) } + +fn update_output_image_copy_cursor_position( + shell: &Shell, + clock: &Clock, + output: &Output, + seat: &Seat, + position: Point, +) { + let output_geometry = output.geometry(); + for session in cursor_sessions_for_output(&shell, &output) { + if let Some(cursor_geometry) = seat.cursor_geometry( + (position - output_geometry.loc.to_f64()) + .as_logical() + .to_buffer( + output.current_scale().fractional_scale(), + output.current_transform(), + &output_geometry.size.to_f64().as_logical(), + ), + clock.now(), + ) { + let constraints = cursor_capture_constraints(Some(cursor_geometry)); + if session + .current_constraints() + .map(|current_constraints| current_constraints.size != constraints.size) + .unwrap_or(true) + { + session.update_constraints(constraints); + } + session.set_cursor_hotspot(cursor_geometry.hotspot); + session.set_cursor_pos(Some(cursor_geometry.geometry.loc)); + } + } +} From 270ba7bf802b884291d5794261d0c6cf66b0224c Mon Sep 17 00:00:00 2001 From: Ian Douglas Scott Date: Tue, 14 Jul 2026 15:33:05 -0700 Subject: [PATCH 82/92] image-copy: Update toplevel cursor position for `cursor_follows_focus` --- src/input/mod.rs | 2 +- src/shell/focus/mod.rs | 8 ++++++++ 2 files changed, 9 insertions(+), 1 deletion(-) diff --git a/src/input/mod.rs b/src/input/mod.rs index 33fb3148..0cb26a9d 100644 --- a/src/input/mod.rs +++ b/src/input/mod.rs @@ -2521,7 +2521,7 @@ fn mapped_output_for_device<'a, D: Device + 'static>( map_to_output.or_else(|| shell.builtin_output()) } -fn update_output_image_copy_cursor_position( +pub fn update_output_image_copy_cursor_position( shell: &Shell, clock: &Clock, output: &Output, diff --git a/src/shell/focus/mod.rs b/src/shell/focus/mod.rs index 9374230a..4e88c499 100644 --- a/src/shell/focus/mod.rs +++ b/src/shell/focus/mod.rs @@ -378,6 +378,14 @@ fn update_focus_state( .cloned() .unwrap_or(seat.active_output()); + crate::input::update_output_image_copy_cursor_position( + &shell, + &state.common.clock, + &output, + seat, + new_pos, + ); + let focus = State::surface_under(new_pos, &output, &shell) .map(|(focus, loc)| (focus, loc.as_logical())); //drop here to avoid multiple borrows From d7d6be3cdbff61395cf3de3d71593328d961f0ba Mon Sep 17 00:00:00 2001 From: Ian Douglas Scott Date: Tue, 14 Jul 2026 16:52:14 -0700 Subject: [PATCH 83/92] image-copy: Only embed cursor in toplevel capture if focused --- src/wayland/handlers/image_copy_capture/render.rs | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/src/wayland/handlers/image_copy_capture/render.rs b/src/wayland/handlers/image_copy_capture/render.rs index b2ebc054..f8b308e2 100644 --- a/src/wayland/handlers/image_copy_capture/render.rs +++ b/src/wayland/handlers/image_copy_capture/render.rs @@ -614,6 +614,11 @@ pub fn render_window_to_buffer( let pointer_loc = pointer.current_location().to_i32_round().as_global(); let mut location = None; if let Some(element) = shell.element_for_surface(toplevel) + && pointer + .current_focus() + .and_then(|f| f.toplevel(&shell)) + .as_ref() + == Some(toplevel) && element.has_active_window(toplevel) && let Some(workspace) = shell.space_for(element) && let Some(geometry) = workspace.element_geometry(element) From 1d72227a30144c96cd2cd1583fd726b9c9dd717d Mon Sep 17 00:00:00 2001 From: littlezabi Date: Thu, 16 Jul 2026 23:36:13 +0500 Subject: [PATCH 84/92] fix: release active popup grabs on toplevel destruction If a window with an active popup grab is closed or crashes unexpectedly, the input grab state can get stuck, freezing keyboard and mouse clicks. This patch iterates over all seats and releases the active seat grab if it is owned by the destroyed window or one of its child popups. Signed-off-by: littlezabi --- src/wayland/handlers/xdg_shell/mod.rs | 37 +++++++++++++++++++++++++++ 1 file changed, 37 insertions(+) diff --git a/src/wayland/handlers/xdg_shell/mod.rs b/src/wayland/handlers/xdg_shell/mod.rs index 98b8acf6..b8288019 100644 --- a/src/wayland/handlers/xdg_shell/mod.rs +++ b/src/wayland/handlers/xdg_shell/mod.rs @@ -317,8 +317,45 @@ impl XdgShellHandler for State { } fn toplevel_destroyed(&mut self, surface: ToplevelSurface) { + for (popup, _) in smithay::desktop::PopupManager::popups_for_surface(surface.wl_surface()) { + if let smithay::desktop::PopupKind::Xdg(ref xdg_popup) = popup { + xdg_popup.send_popup_done(); + } + } + let (output, clients) = { let mut shell = self.common.shell.write(); + + for seat in shell.seats.iter() { + if let Some(data) = seat.user_data().get::() { + let mut should_ungrab = false; + let grab = data.take(); + if let Some(ref grab) = grab { + if grab.has_ended() { + should_ungrab = true; + } else if let Some(target) = grab.current_grab() { + if let Some(wl_surface) = target.wl_surface() { + if wl_surface.as_ref() == surface.wl_surface() + || smithay::desktop::PopupManager::popups_for_surface( + surface.wl_surface(), + ) + .any(|(p, _)| p.wl_surface() == wl_surface.as_ref()) + { + should_ungrab = true; + } + } + } + } + if should_ungrab { + if let Some(mut grab) = grab { + grab.ungrab(PopupUngrabStrategy::All); + } + } else { + data.set(grab); + } + } + } + let seat = shell.seats.last_active().clone(); // Clean up pending_windows for surfaces that were never mapped. From 4d370cdf92b5d96d78032593947f4ad9eae793bf Mon Sep 17 00:00:00 2001 From: Ashley Wulber Date: Tue, 14 Jul 2026 16:16:43 -0400 Subject: [PATCH 85/92] fix: no ceil for scaled outline thickness --- src/backend/render/mod.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/backend/render/mod.rs b/src/backend/render/mod.rs index 154ad986..e9cf651f 100644 --- a/src/backend/render/mod.rs +++ b/src/backend/render/mod.rs @@ -269,7 +269,7 @@ impl IndicatorShader { .filter(|(old_settings, _)| &settings == old_settings) .is_none() { - let thickness: f32 = ((thickness as f64 * scale).ceil() / scale) as f32; + let thickness: f32 = ((thickness as f64 * scale) / scale) as f32; let shader = Self::get(renderer); let elem = PixelShaderElement::new( From 3e87e769a77646a397bcdcd893caa682aedb45f7 Mon Sep 17 00:00:00 2001 From: Lionel DARNIS Date: Sun, 24 May 2026 11:40:51 +0200 Subject: [PATCH 86/92] chore: align compositor with local cosmic stack --- Cargo.toml | 52 ++++++++++++++++++++++++++++------- cosmic-comp-config/Cargo.toml | 4 +-- 2 files changed, 44 insertions(+), 12 deletions(-) diff --git a/Cargo.toml b/Cargo.toml index 3f8a9e73..083c3b20 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -17,18 +17,18 @@ cosmic-comp-config = { path = "cosmic-comp-config", features = [ "libdisplay-info", "output", ] } -cosmic-config = { git = "https://github.com/pop-os/libcosmic", features = [ +cosmic-config = { path = "../libcosmic/cosmic-config", features = [ "calloop", "macro", ] } -cosmic-protocols = { git = "https://github.com/pop-os/cosmic-protocols", rev = "160b086", default-features = false, features = [ +cosmic-protocols = { path = "../cosmic-protocols", default-features = false, features = [ "server", ] } -cosmic-settings-config = { git = "https://github.com/pop-os/cosmic-settings-daemon" } -cosmic-settings-daemon-config = { git = "https://github.com/pop-os/cosmic-settings-daemon", features = [ +cosmic-settings-config = { path = "../cosmic-settings-daemon/config" } +cosmic-settings-daemon-config = { path = "../cosmic-settings-daemon/cosmic-settings-daemon-config", features = [ "greeter", ] } -cosmic-text = { version = "0.19", features = [ +cosmic-text = { path = "../cosmic-text", features = [ "shape-run-cache", ] } libdisplay-info = "0.3.0" @@ -39,11 +39,15 @@ i18n-embed = { version = "0.16", features = [ "desktop-requester", ] } i18n-embed-fl = "0.10" -iced_tiny_skia = { git = "https://github.com/pop-os/libcosmic" } -iced_graphics = { git = "https://github.com/pop-os/libcosmic/", features = ["geometry"] } +iced_tiny_skia = { path = "../libcosmic/iced/tiny_skia" } +iced_graphics = { path = "../libcosmic/iced/graphics", features = ["geometry"] } indexmap = "2.13" keyframe = "1.1.1" -libcosmic = { git = "https://github.com/pop-os/libcosmic", default-features = false } +cosmic = { package = "libcosmic-yoda", path = "../libcosmic", default-features = false, features = [ + "tokio", + "wayland", + "multi-window", +] } libsystemd = { version = "0.7", optional = true } log-panics = { version = "2", features = ["with-backtrace"] } ordered-float = "5.1" @@ -140,8 +144,36 @@ inherits = "release" [profile.release] lto = "fat" -[patch."https://github.com/pop-os/cosmic-protocols"] -cosmic-protocols = { git = "https://github.com/pop-os//cosmic-protocols", branch = "main" } +[patch('https://github.com/pop-os/libcosmic')] +cosmic-config = { path = "../libcosmic/cosmic-config" } +cosmic-theme = { path = "../libcosmic/cosmic-theme" } +iced = { path = "../libcosmic/iced" } +iced_accessibility = { path = "../libcosmic/iced/accessibility" } +iced_core = { path = "../libcosmic/iced/core" } +iced_futures = { path = "../libcosmic/iced/futures" } +iced_graphics = { path = "../libcosmic/iced/graphics" } +iced_renderer = { path = "../libcosmic/iced/renderer" } +iced_runtime = { path = "../libcosmic/iced/runtime" } +iced_tiny_skia = { path = "../libcosmic/iced/tiny_skia" } +iced_wgpu = { path = "../libcosmic/iced/wgpu" } +iced_widget = { path = "../libcosmic/iced/widget" } +iced_winit = { path = "../libcosmic/iced/winit" } + +[patch('https://github.com/pop-os/cosmic-protocols')] +cosmic-protocols = { path = "../cosmic-protocols" } +cosmic-client-toolkit = { path = "../cosmic-protocols/client-toolkit" } + +[patch('https://github.com/pop-os/cosmic-settings-daemon')] +cosmic-settings-config = { path = "../cosmic-settings-daemon/config" } +cosmic-settings-daemon-config = { path = "../cosmic-settings-daemon/cosmic-settings-daemon-config" } + +[patch('https://forge.aditua.com/leyoda/cosmic-text.git')] +cosmic-text = { git = "https://forge.aditua.com/leyoda/cosmic-text.git", branch = "local/pr-503" } + +[patch('https://forge.aditua.com/leyoda/window_clipboard.git')] +window_clipboard = { path = "../window_clipboard" } +dnd = { path = "../window_clipboard/dnd" } +mime = { path = "../window_clipboard/mime" } [patch.crates-io] smithay = { git = "https://github.com/smithay/smithay.git", rev = "1ed69cb" } diff --git a/cosmic-comp-config/Cargo.toml b/cosmic-comp-config/Cargo.toml index f01a4bb3..d0225691 100644 --- a/cosmic-comp-config/Cargo.toml +++ b/cosmic-comp-config/Cargo.toml @@ -4,8 +4,8 @@ version = "1.0.0" edition = "2024" [dependencies] -cosmic-config = { git = "https://github.com/pop-os/libcosmic/" } -cosmic-randr-shell = { git = "https://github.com/pop-os/cosmic-randr/", optional = true } +cosmic-config = { path = "../../libcosmic/cosmic-config" } +cosmic-randr-shell = { path = "../../cosmic-randr/shell", optional = true } input = "0.10.0" libdisplay-info = { version = "0.3.0", optional = true } serde = { version = "1", features = ["derive"] } From 3d01a21900e909e9b3e1cc704ae8cdcc31b04f63 Mon Sep 17 00:00:00 2001 From: Lionel DARNIS Date: Sun, 24 May 2026 16:08:29 +0200 Subject: [PATCH 87/92] fix: keep tokio runtime available for compositor --- Cargo.lock | 1 + Cargo.toml | 1 + src/main.rs | 9 +++++++++ 3 files changed, 11 insertions(+) diff --git a/Cargo.lock b/Cargo.lock index fd733056..21ad2010 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -834,6 +834,7 @@ dependencies = [ "smithay-egui", "thiserror 2.0.18", "tiny-skia", + "tokio", "tracing", "tracing-journald", "tracing-subscriber", diff --git a/Cargo.toml b/Cargo.toml index 083c3b20..225dd24f 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -78,6 +78,7 @@ xdg = "^3.0" xdg-user = "0.2.1" xkbcommon = "0.9" zbus = "5.14.0" +tokio = { version = "1", features = ["rt-multi-thread"] } profiling = { version = "1.0" } rustix = { version = "1.1.4", features = ["process"] } rand = "0.10" diff --git a/src/main.rs b/src/main.rs index 7ae05991..ffa37236 100644 --- a/src/main.rs +++ b/src/main.rs @@ -1,6 +1,15 @@ // SPDX-License-Identifier: GPL-3.0-only fn main() { + // libcosmic-yoda enables zbus' `tokio` feature, so zbus 5.14 expects an + // ambient Tokio runtime via Handle::current(). cosmic-comp's loop is not + // async, so hold a runtime guard for the lifetime of run(). + let runtime = tokio::runtime::Builder::new_multi_thread() + .enable_all() + .build() + .expect("failed to build tokio runtime"); + let _guard = runtime.enter(); + if let Err(err) = cosmic_comp::run(Default::default()) { tracing::error!("Error occured in main(): {}", err); std::process::exit(1); From bbf7effda2e4bf147292100f0558cfd59d20e370 Mon Sep 17 00:00:00 2001 From: Lionel DARNIS Date: Mon, 25 May 2026 09:55:23 +0200 Subject: [PATCH 88/92] yoda: refresh local dbus binding lock --- Cargo.lock | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index 21ad2010..f8768f60 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -943,8 +943,7 @@ dependencies = [ [[package]] name = "cosmic-settings-daemon" -version = "0.1.0" -source = "git+https://github.com/pop-os/dbus-settings-bindings#eed01dd3609e90e3c8cd043656734c500956c793" +version = "0.1.1-yoda.1" dependencies = [ "zbus", ] From 4fa39e5ea325f761e75c27ce727555625b9dff71 Mon Sep 17 00:00:00 2001 From: Lionel DARNIS Date: Mon, 25 May 2026 11:34:38 +0200 Subject: [PATCH 89/92] chore: use local cosmic-text checkout --- Cargo.toml | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/Cargo.toml b/Cargo.toml index 225dd24f..2723131e 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -168,8 +168,10 @@ cosmic-client-toolkit = { path = "../cosmic-protocols/client-toolkit" } cosmic-settings-config = { path = "../cosmic-settings-daemon/config" } cosmic-settings-daemon-config = { path = "../cosmic-settings-daemon/cosmic-settings-daemon-config" } -[patch('https://forge.aditua.com/leyoda/cosmic-text.git')] -cosmic-text = { git = "https://forge.aditua.com/leyoda/cosmic-text.git", branch = "local/pr-503" } +[patch('https://github.com/pop-os/cosmic-text.git')] +cosmic-text = { path = "../cosmic-text" } +[patch.'https://github.com/pop-os/cosmic-text.git'] +cosmic-text = { path = "../cosmic-text" } [patch('https://forge.aditua.com/leyoda/window_clipboard.git')] window_clipboard = { path = "../window_clipboard" } From ba1232cffc69a2cc8161a849aba15da0ad5c7842 Mon Sep 17 00:00:00 2001 From: Lionel DARNIS Date: Fri, 3 Jul 2026 12:36:51 +0200 Subject: [PATCH 90/92] fix: absorb libcosmic theme-v2 API (private containers, text::Style, wgpu 28) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Leyoda 2026 – GPLv3 --- src/shell/element/stack.rs | 16 ++++---------- src/shell/element/stack/tab.rs | 40 ++++++++++------------------------ src/shell/element/window.rs | 7 ++---- src/shell/grabs/menu/mod.rs | 3 ++- src/shell/zoom.rs | 3 ++- 5 files changed, 22 insertions(+), 47 deletions(-) diff --git a/src/shell/element/stack.rs b/src/shell/element/stack.rs index d466f4e0..7d1d9b51 100644 --- a/src/shell/element/stack.rs +++ b/src/shell/element/stack.rs @@ -1379,18 +1379,10 @@ impl Decorations for DefaultDecorations { iced_widget::container::Style { snap: true, - icon_color: Some( - cosmic_theme - .background(cosmic_theme.frosted_windows) - .on - .into(), - ), - text_color: Some( - cosmic_theme - .background(cosmic_theme.frosted_windows) - .on - .into(), - ), + icon_color: Some(cosmic_theme.background(false).on.into()), + text_color: Some(cosmic_theme.background(false).on.into()), + icon_color: Some(cosmic_theme.background(false).on.into()), + text_color: Some(cosmic_theme.background(false).on.into()), background: Some(Background::Color(background.into())), border: Border { radius, diff --git a/src/shell/element/stack/tab.rs b/src/shell/element/stack/tab.rs index f8fda525..c501698f 100644 --- a/src/shell/element/stack/tab.rs +++ b/src/shell/element/stack/tab.rs @@ -63,20 +63,12 @@ impl From for theme::Container<'_> { snap: true, icon_color: Some(Color::from(theme.cosmic().accent_text_color())), text_color: Some(Color::from(theme.cosmic().accent_text_color())), - background: Some(Background::Color({ - let mut color = theme - .cosmic() - .primary(theme.cosmic().frosted_windows) - .component - .selected; - if theme.cosmic().frosted_windows { - color.alpha = theme - .cosmic() - .alpha_map - .blurred_alpha(theme.cosmic().frosted); - } - color.into() - })), + background: Some(Background::Color( + theme.cosmic().primary(false).component.selected.into(), + )), + background: Some(Background::Color( + theme.cosmic().primary(false).component.selected.into(), + )), border: Border { radius: 0.0.into(), width: 0.0, @@ -90,20 +82,12 @@ impl From for theme::Container<'_> { snap: true, icon_color: None, text_color: None, - background: Some(Background::Color({ - let mut color = theme - .cosmic() - .primary(theme.cosmic().frosted_windows) - .component - .base; - if theme.cosmic().frosted_windows { - color.alpha = theme - .cosmic() - .alpha_map - .blurred_alpha(theme.cosmic().frosted); - } - color.into() - })), + background: Some(Background::Color( + theme.cosmic().primary(false).component.base.into(), + )), + background: Some(Background::Color( + theme.cosmic().primary(false).component.base.into(), + )), border: Border { radius: 0.0.into(), width: 0.0, diff --git a/src/shell/element/window.rs b/src/shell/element/window.rs index 86c2ce7f..06b34188 100644 --- a/src/shell/element/window.rs +++ b/src/shell/element/window.rs @@ -831,11 +831,8 @@ impl Program for CosmicWindowInternal { fn background_color(&self, theme: &cosmic::Theme) -> Color { if self.window.is_maximized(false) { - theme - .cosmic() - .background(theme.cosmic().frosted_windows) - .base - .into() + theme.cosmic().background(false).base.into() + theme.cosmic().background(false).base.into() } else { Color::TRANSPARENT } diff --git a/src/shell/grabs/menu/mod.rs b/src/shell/grabs/menu/mod.rs index 72ea065b..1eb571f1 100644 --- a/src/shell/grabs/menu/mod.rs +++ b/src/shell/grabs/menu/mod.rs @@ -484,7 +484,8 @@ impl Program for ContextMenu { .padding(1) .class(theme::Container::custom(|theme| { let cosmic = theme.cosmic(); - let component = &cosmic.background(theme.cosmic().frosted_windows).component; + let component = &cosmic.background(false).component; + let component = &cosmic.background(false).component; iced_widget::container::Style { snap: true, icon_color: Some(cosmic.accent.base.into()), diff --git a/src/shell/zoom.rs b/src/shell/zoom.rs index ef81053f..8363510a 100644 --- a/src/shell/zoom.rs +++ b/src/shell/zoom.rs @@ -504,7 +504,8 @@ impl Program for ZoomProgram { .padding(8) .class(theme::Container::custom(|theme| { let cosmic = theme.cosmic(); - let component = &cosmic.background(theme.transparent).component; + let component = &cosmic.background(false).component; + let component = &cosmic.background(false).component; iced_widget::container::Style { snap: true, icon_color: Some(component.on.into()), From e6173b93cab6b05ff523b007279934da58be471a Mon Sep 17 00:00:00 2001 From: Lionel DARNIS Date: Fri, 3 Jul 2026 15:56:20 +0200 Subject: [PATCH 91/92] chore: restore rust-toolchain 1.93 + refresh lockfile post-rebase MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Leyoda 2026 – GPLv3 --- Cargo.lock | 1331 ++++++++++++++++++++++++++++++++++++++++++++-------- 1 file changed, 1138 insertions(+), 193 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index f8768f60..5edc17e0 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -18,6 +18,89 @@ version = "0.1.10" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "366ffbaa4442f4684d91e2cd7c5ea7c4ed8add41959a31447066e279e432b618" +[[package]] +name = "accesskit" +version = "0.22.0" +source = "git+https://github.com/wash2/accesskit?tag=cosmic-0.14#f0599eed5f18111228266fe3f28991cc48b5964f" +dependencies = [ + "uuid", +] + +[[package]] +name = "accesskit_atspi_common" +version = "0.15.0" +source = "git+https://github.com/wash2/accesskit?tag=cosmic-0.14#f0599eed5f18111228266fe3f28991cc48b5964f" +dependencies = [ + "accesskit", + "accesskit_consumer", + "atspi-common", + "serde", + "zvariant", +] + +[[package]] +name = "accesskit_consumer" +version = "0.32.0" +source = "git+https://github.com/wash2/accesskit?tag=cosmic-0.14#f0599eed5f18111228266fe3f28991cc48b5964f" +dependencies = [ + "accesskit", + "hashbrown 0.16.1", +] + +[[package]] +name = "accesskit_macos" +version = "0.23.0" +source = "git+https://github.com/wash2/accesskit?tag=cosmic-0.14#f0599eed5f18111228266fe3f28991cc48b5964f" +dependencies = [ + "accesskit", + "accesskit_consumer", + "hashbrown 0.16.1", + "objc2 0.5.2", + "objc2-app-kit 0.2.2", + "objc2-foundation 0.2.2", +] + +[[package]] +name = "accesskit_unix" +version = "0.18.0" +source = "git+https://github.com/wash2/accesskit?tag=cosmic-0.14#f0599eed5f18111228266fe3f28991cc48b5964f" +dependencies = [ + "accesskit", + "accesskit_atspi_common", + "atspi", + "futures-lite", + "serde", + "tokio", + "tokio-stream", + "zbus", +] + +[[package]] +name = "accesskit_windows" +version = "0.30.0" +source = "git+https://github.com/wash2/accesskit?tag=cosmic-0.14#f0599eed5f18111228266fe3f28991cc48b5964f" +dependencies = [ + "accesskit", + "accesskit_consumer", + "hashbrown 0.16.1", + "static_assertions", + "windows 0.61.3", + "windows-core 0.61.2", +] + +[[package]] +name = "accesskit_winit" +version = "0.30.0" +source = "git+https://github.com/wash2/accesskit?tag=cosmic-0.14#f0599eed5f18111228266fe3f28991cc48b5964f" +dependencies = [ + "accesskit", + "accesskit_macos", + "accesskit_unix", + "accesskit_windows", + "raw-window-handle", + "winit 0.31.0-beta.2", +] + [[package]] name = "addr2line" version = "0.25.1" @@ -61,6 +144,12 @@ version = "0.1.3" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "250f629c0161ad8107cf89319e990051fae62832fd343083bea452d93e2205fd" +[[package]] +name = "allocator-api2" +version = "0.2.21" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "683d7910e743518b0e34f1186f92494becacb047c7b6bf616c96772180fef923" + [[package]] name = "almost" version = "0.2.0" @@ -175,6 +264,44 @@ dependencies = [ "libloading", ] +[[package]] +name = "ashpd" +version = "0.11.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d2f3f79755c74fd155000314eb349864caa787c6592eace6c6882dad873d9c39" +dependencies = [ + "enumflags2", + "futures-channel", + "futures-util", + "rand 0.9.2", + "raw-window-handle", + "serde", + "serde_repr", + "tokio", + "url", + "zbus", +] + +[[package]] +name = "ashpd" +version = "0.12.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "33a3c86f3fd70c0ffa500ed189abfa90b5a52398a45d5dc372fcc38ebeb7a645" +dependencies = [ + "enumflags2", + "futures-channel", + "futures-util", + "rand 0.9.2", + "serde", + "serde_repr", + "tokio", + "url", + "wayland-backend", + "wayland-client", + "wayland-protocols", + "zbus", +] + [[package]] name = "async-broadcast" version = "0.7.2" @@ -329,10 +456,47 @@ dependencies = [ ] [[package]] -name = "auto_enums" -version = "0.8.8" +name = "atspi" +version = "0.29.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "65398a2893f41bce5c9259f6e1a4f03fbae40637c1bdc755b4f387f48c613b03" +checksum = "c77886257be21c9cd89a4ae7e64860c6f0eefca799bb79127913052bd0eefb3d" +dependencies = [ + "atspi-common", + "atspi-proxies", +] + +[[package]] +name = "atspi-common" +version = "0.13.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "20c5617155740c98003016429ad13fe43ce7a77b007479350a9f8bf95a29f63d" +dependencies = [ + "enumflags2", + "serde", + "static_assertions", + "zbus", + "zbus-lockstep", + "zbus-lockstep-macros", + "zbus_names", + "zvariant", +] + +[[package]] +name = "atspi-proxies" +version = "0.13.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "2230e48787ed3eb4088996eab66a32ca20c0b67bbd4fd6cdfe79f04f1f04c9fc" +dependencies = [ + "atspi-common", + "serde", + "zbus", +] + +[[package]] +name = "auto_enums" +version = "0.8.9" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "2e4487600931c9a89f8db7ffbdf3fbdd45bb7bd85e26861f659a463cd0dff966" dependencies = [ "derive_utils", "proc-macro2", @@ -358,7 +522,7 @@ dependencies = [ "miniz_oxide", "object", "rustc-demangle", - "windows-link", + "windows-link 0.2.1", ] [[package]] @@ -427,13 +591,22 @@ dependencies = [ "generic-array", ] +[[package]] +name = "block2" +version = "0.5.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "2c132eebf10f5cad5289222520a4a058514204aed6d791f1cf4fe8088b82d15f" +dependencies = [ + "objc2 0.5.2", +] + [[package]] name = "block2" version = "0.6.2" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "cdeb9d870516001442e364c5220d3574d2da8dc765554b4a617230d33fa58ef5" dependencies = [ - "objc2", + "objc2 0.6.4", ] [[package]] @@ -619,7 +792,7 @@ dependencies = [ "iana-time-zone", "num-traits", "serde", - "windows-link", + "windows-link 0.2.1", ] [[package]] @@ -640,7 +813,6 @@ dependencies = [ [[package]] name = "clipboard_macos" version = "0.1.0" -source = "git+https://github.com/pop-os/window_clipboard.git?tag=sctk-0.20#f68595ee0e62fbd6589f4709b5aaa5c3c7ea5f6c" dependencies = [ "objc", "objc-foundation", @@ -650,22 +822,12 @@ dependencies = [ [[package]] name = "clipboard_wayland" version = "0.2.2" -source = "git+https://github.com/pop-os/window_clipboard.git?tag=sctk-0.20#f68595ee0e62fbd6589f4709b5aaa5c3c7ea5f6c" dependencies = [ "dnd", "mime 0.1.0", "smithay-clipboard", ] -[[package]] -name = "clipboard_x11" -version = "0.4.2" -source = "git+https://github.com/pop-os/window_clipboard.git?tag=sctk-0.20#f68595ee0e62fbd6589f4709b5aaa5c3c7ea5f6c" -dependencies = [ - "thiserror 1.0.69", - "x11rb", -] - [[package]] name = "cocoa" version = "0.25.0" @@ -675,7 +837,7 @@ dependencies = [ "bitflags 1.3.2", "block", "cocoa-foundation", - "core-foundation", + "core-foundation 0.9.4", "core-graphics", "foreign-types", "libc", @@ -690,8 +852,8 @@ checksum = "8c6234cbb2e4c785b456c0644748b1ac416dd045799740356f8363dfe00c93f7" dependencies = [ "bitflags 1.3.2", "block", - "core-foundation", - "core-graphics-types", + "core-foundation 0.9.4", + "core-graphics-types 0.1.3", "libc", "objc", ] @@ -702,7 +864,9 @@ version = "0.12.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "fe6d2e5af09e8c8ad56c969f2157a3d4238cebc7c55f0a517728c38f7b200f81" dependencies = [ - "unicode-width", + "serde", + "termcolor", + "unicode-width 0.1.14", ] [[package]] @@ -746,6 +910,16 @@ dependencies = [ "libc", ] +[[package]] +name = "core-foundation" +version = "0.10.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b2a6cd9ae233e7f62ba4e9353e81a88df7fc8a5987b8d445b4d90c879bd156f6" +dependencies = [ + "core-foundation-sys", + "libc", +] + [[package]] name = "core-foundation-sys" version = "0.8.7" @@ -759,8 +933,8 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "c07782be35f9e1140080c6b96f0d44b739e2278479f64e02fdab4e32dfd8b081" dependencies = [ "bitflags 1.3.2", - "core-foundation", - "core-graphics-types", + "core-foundation 0.9.4", + "core-graphics-types 0.1.3", "foreign-types", "libc", ] @@ -772,7 +946,18 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "45390e6114f68f718cc7a830514a96f903cccd70d02a8f6d9f643ac4ba45afaf" dependencies = [ "bitflags 1.3.2", - "core-foundation", + "core-foundation 0.9.4", + "libc", +] + +[[package]] +name = "core-graphics-types" +version = "0.2.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3d44a101f213f6c4cdc1853d4b78aef6db6bdfa3468798cc1d9912f4735013eb" +dependencies = [ + "bitflags 2.11.0", + "core-foundation 0.10.1", "libc", ] @@ -785,6 +970,18 @@ dependencies = [ "libm", ] +[[package]] +name = "cosmic-client-toolkit" +version = "0.2.0" +dependencies = [ + "bitflags 2.11.0", + "cosmic-protocols", + "libc", + "smithay-client-toolkit", + "wayland-client", + "wayland-protocols", +] + [[package]] name = "cosmic-comp" version = "1.0.0" @@ -792,6 +989,7 @@ dependencies = [ "anyhow", "bitflags 2.11.0", "calloop", + "cgmath", "clap_lex", "cosmic-comp-config", "cosmic-config", @@ -803,16 +1001,14 @@ dependencies = [ "egui_plot", "futures-executor", "futures-util", - "glam 0.33.2", "i18n-embed", "i18n-embed-fl", - "iced_graphics", "iced_tiny_skia", "id_tree", "indexmap 2.13.0", "jiff", "keyframe", - "libcosmic", + "libcosmic-yoda", "libdisplay-info", "libsystemd", "log-panics", @@ -829,7 +1025,6 @@ dependencies = [ "sanitize-filename", "serde", "serde_json", - "smallvec", "smithay", "smithay-egui", "thiserror 2.0.18", @@ -864,7 +1059,6 @@ dependencies = [ [[package]] name = "cosmic-config" version = "1.0.0" -source = "git+https://github.com/pop-os/libcosmic#d922ac380134c00208d2acf93947d44fb217eec5" dependencies = [ "atomicwrites", "calloop", @@ -877,6 +1071,7 @@ dependencies = [ "notify", "ron 0.12.0", "serde", + "tokio", "tracing", "xdg", "zbus", @@ -885,7 +1080,6 @@ dependencies = [ [[package]] name = "cosmic-config-derive" version = "1.0.0" -source = "git+https://github.com/pop-os/libcosmic#d922ac380134c00208d2acf93947d44fb217eec5" dependencies = [ "quote", "syn", @@ -894,12 +1088,11 @@ dependencies = [ [[package]] name = "cosmic-freedesktop-icons" version = "0.4.0" -source = "git+https://github.com/pop-os/freedesktop-icons#ab4c57b8e416c6af9297cb04d101889896fd9a92" dependencies = [ "bstr", "btoi", "memchr", - "memmap2", + "memmap2 0.9.10", "thiserror 2.0.18", "tracing", "xdg", @@ -908,10 +1101,10 @@ dependencies = [ [[package]] name = "cosmic-protocols" version = "0.2.0" -source = "git+https://github.com/pop-os//cosmic-protocols?branch=main#32283d76a8d0342da74c4cc022a533c52dcf378f" dependencies = [ "bitflags 2.11.0", "wayland-backend", + "wayland-client", "wayland-protocols", "wayland-protocols-wlr", "wayland-scanner", @@ -921,7 +1114,6 @@ dependencies = [ [[package]] name = "cosmic-randr-shell" version = "0.1.0" -source = "git+https://github.com/pop-os/cosmic-randr/#6e8e795970fa06d434af22775e415b517f7552d3" dependencies = [ "kdl", "slotmap", @@ -931,7 +1123,6 @@ dependencies = [ [[package]] name = "cosmic-settings-config" version = "0.1.0" -source = "git+https://github.com/pop-os/cosmic-settings-daemon#e37160f14d1e7ee428f973cd2848b4e95f83dfe1" dependencies = [ "cosmic-config", "ron 0.11.0", @@ -951,7 +1142,6 @@ dependencies = [ [[package]] name = "cosmic-settings-daemon-config" version = "0.1.0" -source = "git+https://github.com/pop-os/cosmic-settings-daemon#e37160f14d1e7ee428f973cd2848b4e95f83dfe1" dependencies = [ "cosmic-config", "cosmic-theme", @@ -961,8 +1151,6 @@ dependencies = [ [[package]] name = "cosmic-text" version = "0.19.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "be17b688510d934ce13f48a2beba700e11583e281e0fda99c22bb256a14eda73" dependencies = [ "bitflags 2.11.0", "fontdb", @@ -980,12 +1168,12 @@ dependencies = [ "unicode-linebreak", "unicode-script", "unicode-segmentation", + "unicode-width 0.2.2", ] [[package]] name = "cosmic-theme" version = "1.0.0" -source = "git+https://github.com/pop-os/libcosmic#d922ac380134c00208d2acf93947d44fb217eec5" dependencies = [ "almost", "configparser", @@ -1042,7 +1230,6 @@ checksum = "460fbee9c2c2f33933d720630a6a0bac33ba7053db5344fac858d4b8952d77d5" [[package]] name = "cryoglyph" version = "0.1.0" -source = "git+https://github.com/iced-rs/cryoglyph.git?rev=e429a025df36ab8145708acb309080ae3deec17a#e429a025df36ab8145708acb309080ae3deec17a" dependencies = [ "cosmic-text", "etagere", @@ -1204,7 +1391,9 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "1e0e367e4e7da84520dedcac1901e4da967309406d1e51017ae1abfb97adbd38" dependencies = [ "bitflags 2.11.0", - "objc2", + "block2 0.6.2", + "libc", + "objc2 0.6.4", ] [[package]] @@ -1230,7 +1419,6 @@ dependencies = [ [[package]] name = "dnd" version = "0.1.0" -source = "git+https://github.com/pop-os/window_clipboard.git?tag=sctk-0.20#f68595ee0e62fbd6589f4709b5aaa5c3c7ea5f6c" dependencies = [ "bitflags 2.11.0", "mime 0.1.0", @@ -1257,21 +1445,12 @@ checksum = "75b325c5dbd37f80359721ad39aca5a29fb04c89279657cffdda8736d0c0b9d2" [[package]] name = "dpi" version = "0.1.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d8b14ccef22fc6f5a8f4d7d768562a182c04ce9a3b3157b91390b52ddfdf1a76" [[package]] -name = "drm" -version = "0.11.1" +name = "dpi" +version = "0.1.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a0f8a69e60d75ae7dab4ef26a59ca99f2a89d4c142089b537775ae0c198bdcde" -dependencies = [ - "bitflags 2.11.0", - "bytemuck", - "drm-ffi 0.7.1", - "drm-fourcc", - "rustix 0.38.44", -] +checksum = "d8b14ccef22fc6f5a8f4d7d768562a182c04ce9a3b3157b91390b52ddfdf1a76" [[package]] name = "drm" @@ -1281,29 +1460,19 @@ checksum = "80bc8c5c6c2941f70a55c15f8d9f00f9710ebda3ffda98075f996a0e6c92756f" dependencies = [ "bitflags 2.11.0", "bytemuck", - "drm-ffi 0.9.0", + "drm-ffi", "drm-fourcc", "libc", "rustix 0.38.44", ] -[[package]] -name = "drm-ffi" -version = "0.7.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "41334f8405792483e32ad05fbb9c5680ff4e84491883d2947a4757dc54cb2ac6" -dependencies = [ - "drm-sys 0.6.1", - "rustix 0.38.44", -] - [[package]] name = "drm-ffi" version = "0.9.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "d8e41459d99a9b529845f6d2c909eb9adf3b6d2f82635ae40be8de0601726e8b" dependencies = [ - "drm-sys 0.8.0", + "drm-sys", "rustix 0.38.44", ] @@ -1313,16 +1482,6 @@ version = "2.2.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "0aafbcdb8afc29c1a7ee5fbe53b5d62f4565b35a042a662ca9fecd0b54dae6f4" -[[package]] -name = "drm-sys" -version = "0.6.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "2d09ff881f92f118b11105ba5e34ff8f4adf27b30dae8f12e28c193af1c83176" -dependencies = [ - "libc", - "linux-raw-sys 0.6.5", -] - [[package]] name = "drm-sys" version = "0.8.0" @@ -1722,7 +1881,7 @@ checksum = "457e789b3d1202543297a350643cf459f836cade38934e7a4cf6a39e7cde2905" dependencies = [ "fontconfig-parser", "log", - "memmap2", + "memmap2 0.9.10", "slotmap", "tinyvec", "ttf-parser", @@ -1881,7 +2040,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "ce852e998d3ca5e4a97014fb31c940dc5ef344ec7d364984525fd11e8a547e6a" dependencies = [ "bitflags 2.11.0", - "drm 0.14.1", + "drm", "drm-fourcc", "gbm-sys", "libc", @@ -1907,8 +2066,8 @@ dependencies = [ "libc", "log", "rustversion", - "windows-link", - "windows-result", + "windows-link 0.2.1", + "windows-result 0.4.1", ] [[package]] @@ -1928,7 +2087,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "1bd49230192a3797a9a4d6abe9b3eed6f7fa4c8a8a4947977c6f80025f92cbd8" dependencies = [ "rustix 1.1.4", - "windows-link", + "windows-link 0.2.1", ] [[package]] @@ -2001,12 +2160,6 @@ version = "0.25.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "151665d9be52f9bb40fc7966565d39666f2d1e69233571b71b87791c7e0528b3" -[[package]] -name = "glam" -version = "0.33.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7f22fb22f065b308be0d8724e3706c7fa3fc2a6c7d6899df4cad7860e7a75436" - [[package]] name = "glow" version = "0.16.0" @@ -2019,6 +2172,49 @@ dependencies = [ "web-sys", ] +[[package]] +name = "glutin_wgl_sys" +version = "0.6.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "2c4ee00b289aba7a9e5306d57c2d05499b2e5dc427f84ac708bd2c090212cf3e" +dependencies = [ + "gl_generator", +] + +[[package]] +name = "gpu-allocator" +version = "0.28.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "51255ea7cfaadb6c5f1528d43e92a82acb2b96c43365989a28b2d44ee38f8795" +dependencies = [ + "ash", + "hashbrown 0.16.1", + "log", + "presser", + "thiserror 2.0.18", + "windows 0.62.2", +] + +[[package]] +name = "gpu-descriptor" +version = "0.3.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b89c83349105e3732062a895becfc71a8f921bb71ecbbdd8ff99263e3b53a0ca" +dependencies = [ + "bitflags 2.11.0", + "gpu-descriptor-types", + "hashbrown 0.15.5", +] + +[[package]] +name = "gpu-descriptor-types" +version = "0.2.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "fdf242682df893b86f33a73828fb09ca4b2d3bb6cc95249707fc684d27484b91" +dependencies = [ + "bitflags 2.11.0", +] + [[package]] name = "grid" version = "1.0.0" @@ -2081,6 +2277,8 @@ version = "0.16.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "841d1cc9bed7f9236f321df977030373f4a4163ae1a7dbfe1a51a2c1a51d9100" dependencies = [ + "allocator-api2", + "equivalent", "foldhash 0.2.0", ] @@ -2228,7 +2426,7 @@ dependencies = [ "js-sys", "log", "wasm-bindgen", - "windows-core", + "windows-core 0.62.2", ] [[package]] @@ -2243,9 +2441,9 @@ dependencies = [ [[package]] name = "iced" version = "0.14.0" -source = "git+https://github.com/pop-os/libcosmic#d922ac380134c00208d2acf93947d44fb217eec5" dependencies = [ "dnd", + "iced_accessibility", "iced_core", "iced_debug", "iced_futures", @@ -2253,21 +2451,30 @@ dependencies = [ "iced_renderer", "iced_runtime", "iced_widget", + "iced_winit", "image", "mime 0.1.0", "thiserror 2.0.18", "window_clipboard", ] +[[package]] +name = "iced_accessibility" +version = "0.1.0" +dependencies = [ + "accesskit", + "accesskit_winit", +] + [[package]] name = "iced_core" version = "0.14.0" -source = "git+https://github.com/pop-os/libcosmic#d922ac380134c00208d2acf93947d44fb217eec5" dependencies = [ "bitflags 2.11.0", "bytes", + "cosmic-client-toolkit", "dnd", - "glam 0.25.0", + "glam", "lilt", "log", "mime 0.1.0", @@ -2286,7 +2493,6 @@ dependencies = [ [[package]] name = "iced_debug" version = "0.14.0" -source = "git+https://github.com/pop-os/libcosmic#d922ac380134c00208d2acf93947d44fb217eec5" dependencies = [ "iced_core", "iced_futures", @@ -2296,12 +2502,12 @@ dependencies = [ [[package]] name = "iced_futures" version = "0.14.0" -source = "git+https://github.com/pop-os/libcosmic#d922ac380134c00208d2acf93947d44fb217eec5" dependencies = [ "futures", "iced_core", "log", "rustc-hash 2.1.1", + "tokio", "wasm-bindgen-futures", "wasmtimer", ] @@ -2309,7 +2515,6 @@ dependencies = [ [[package]] name = "iced_graphics" version = "0.14.0" -source = "git+https://github.com/pop-os/libcosmic#d922ac380134c00208d2acf93947d44fb217eec5" dependencies = [ "bitflags 2.11.0", "bytemuck", @@ -2330,7 +2535,6 @@ dependencies = [ [[package]] name = "iced_program" version = "0.14.0" -source = "git+https://github.com/pop-os/libcosmic#d922ac380134c00208d2acf93947d44fb217eec5" dependencies = [ "iced_graphics", "iced_runtime", @@ -2339,7 +2543,6 @@ dependencies = [ [[package]] name = "iced_renderer" version = "0.14.0" -source = "git+https://github.com/pop-os/libcosmic#d922ac380134c00208d2acf93947d44fb217eec5" dependencies = [ "iced_graphics", "iced_tiny_skia", @@ -2351,9 +2554,9 @@ dependencies = [ [[package]] name = "iced_runtime" version = "0.14.0" -source = "git+https://github.com/pop-os/libcosmic#d922ac380134c00208d2acf93947d44fb217eec5" dependencies = [ "bytes", + "cosmic-client-toolkit", "dnd", "iced_core", "iced_futures", @@ -2365,7 +2568,6 @@ dependencies = [ [[package]] name = "iced_tiny_skia" version = "0.14.0" -source = "git+https://github.com/pop-os/libcosmic#d922ac380134c00208d2acf93947d44fb217eec5" dependencies = [ "bytemuck", "cosmic-text", @@ -2382,14 +2584,13 @@ dependencies = [ [[package]] name = "iced_wgpu" version = "0.14.0" -source = "git+https://github.com/pop-os/libcosmic#d922ac380134c00208d2acf93947d44fb217eec5" dependencies = [ - "as-raw-xcb-connection", "bitflags 2.11.0", "bytemuck", + "cosmic-client-toolkit", "cryoglyph", "futures", - "glam 0.25.0", + "glam", "guillotiere", "iced_debug", "iced_graphics", @@ -2400,20 +2601,18 @@ dependencies = [ "rustc-hash 2.1.1", "rustix 0.38.44", "thiserror 2.0.18", - "tiny-xlib", "wayland-backend", "wayland-client", "wayland-protocols", "wayland-sys", "wgpu", - "x11rb", ] [[package]] name = "iced_widget" version = "0.14.2" -source = "git+https://github.com/pop-os/libcosmic#d922ac380134c00208d2acf93947d44fb217eec5" dependencies = [ + "cosmic-client-toolkit", "dnd", "iced_renderer", "iced_runtime", @@ -2426,6 +2625,37 @@ dependencies = [ "window_clipboard", ] +[[package]] +name = "iced_winit" +version = "0.14.0" +dependencies = [ + "cosmic-client-toolkit", + "cursor-icon", + "dnd", + "iced_debug", + "iced_futures", + "iced_graphics", + "iced_program", + "iced_runtime", + "log", + "raw-window-handle", + "rustc-hash 2.1.1", + "rustix 0.38.44", + "thiserror 2.0.18", + "wasm-bindgen-futures", + "wayland-backend", + "wayland-client", + "wayland-protocols", + "web-sys", + "winapi", + "window_clipboard", + "winit 0.31.0-beta.2", + "winit-core 0.31.0-beta.2", + "xkbcommon 0.7.0", + "xkbcommon-dl", + "xkeysym", +] + [[package]] name = "icu_collections" version = "2.1.1" @@ -2806,6 +3036,17 @@ dependencies = [ "num-traits", ] +[[package]] +name = "khronos-egl" +version = "6.0.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6aae1df220ece3c0ada96b8153459b67eebe9ae9212258bb0134ae60416fdf76" +dependencies = [ + "libc", + "libloading", + "pkg-config", +] + [[package]] name = "khronos_api" version = "3.1.0" @@ -2885,17 +3126,18 @@ checksum = "09edd9e8b54e49e587e4f6295a7d29c3ea94d469cb40ab8ca70b288248a81db2" [[package]] name = "libc" -version = "0.2.182" +version = "0.2.186" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "6800badb6cb2082ffd7b6a67e6125bb39f18782f793520caee8cb8846be06112" +checksum = "68ab91017fe16c622486840e4c83c9a37afeff978bd239b5293d61ece587de66" [[package]] -name = "libcosmic" -version = "1.0.0" -source = "git+https://github.com/pop-os/libcosmic#d922ac380134c00208d2acf93947d44fb217eec5" +name = "libcosmic-yoda" +version = "0.1.0-yoda.2" dependencies = [ "apply", + "ashpd 0.12.3", "auto_enums", + "cosmic-client-toolkit", "cosmic-config", "cosmic-freedesktop-icons", "cosmic-settings-daemon", @@ -2913,17 +3155,21 @@ dependencies = [ "iced_renderer", "iced_runtime", "iced_tiny_skia", + "iced_wgpu", "iced_widget", + "iced_winit", "image", "jiff", "log", "palette", "phf 0.13.1", + "rfd", "rust-embed", "serde", "slotmap", "taffy", "thiserror 2.0.18", + "tokio", "tracing", "unicode-segmentation", "url", @@ -2971,7 +3217,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "d7c4b02199fee7c5d21a5ae7d8cfa79a6ef5bb2fc834d6e9058e89c825efdc55" dependencies = [ "cfg-if", - "windows-link", + "windows-link 0.2.1", ] [[package]] @@ -2982,13 +3228,14 @@ checksum = "b6d2cec3eae94f9f509c767b45932f1ada8350c4bdb85af2fcab4a3c14807981" [[package]] name = "libredox" -version = "0.1.12" +version = "0.1.18" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "3d0b95e02c851351f877147b7deea7b1afb1df71b63aa5f8270716e0c5720616" +checksum = "c943259e342f1e06ff2da7a83eabdfe7f92ce10262688dbf1895ff0b3e6e4652" dependencies = [ "bitflags 2.11.0", "libc", - "redox_syscall 0.7.2", + "plain", + "redox_syscall 0.9.0", ] [[package]] @@ -3214,6 +3461,15 @@ version = "2.8.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "f8ca58f447f06ed17d5fc4043ce1b10dd205e060fb3ce5b979b8ed8e59ff3f79" +[[package]] +name = "memmap2" +version = "0.8.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "43a5a03cefb0d953ec0be133036f14e109412fa594edc2f77227249db66cc3ed" +dependencies = [ + "libc", +] + [[package]] name = "memmap2" version = "0.9.10" @@ -3232,6 +3488,21 @@ dependencies = [ "autocfg", ] +[[package]] +name = "metal" +version = "0.33.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c7047791b5bc903b8cd963014b355f71dc9864a9a0b727057676c1dcae5cbc15" +dependencies = [ + "bitflags 2.11.0", + "block", + "core-graphics-types 0.2.0", + "foreign-types", + "log", + "objc", + "paste", +] + [[package]] name = "miette" version = "7.6.0" @@ -3239,13 +3510,12 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "5f98efec8807c63c752b5bd61f862c165c115b0a35685bdcfd9238c7aeb592b7" dependencies = [ "cfg-if", - "unicode-width", + "unicode-width 0.1.14", ] [[package]] name = "mime" version = "0.1.0" -source = "git+https://github.com/pop-os/window_clipboard.git?tag=sctk-0.20#f68595ee0e62fbd6589f4709b5aaa5c3c7ea5f6c" dependencies = [ "smithay-clipboard", ] @@ -3286,9 +3556,9 @@ checksum = "e53debba6bda7a793e5f99b8dacf19e626084f525f7829104ba9898f367d85ff" [[package]] name = "mio" -version = "1.1.1" +version = "1.2.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a69bcab0ad47271a0234d9422b131806bf3968021e5dc9328caf2d4cd58557fc" +checksum = "02bd0af71c67b473010cbbc60715ee815645a4dc942899111f494b4b737d6fda" dependencies = [ "libc", "log", @@ -3333,6 +3603,7 @@ dependencies = [ "num-traits", "once_cell", "rustc-hash 1.1.0", + "spirv", "thiserror 2.0.18", "unicode-ident", ] @@ -3553,6 +3824,22 @@ dependencies = [ "objc_id", ] +[[package]] +name = "objc-sys" +version = "0.3.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "cdb91bdd390c7ce1a8607f35f3ca7151b65afc0ff5ff3b34fa350f7d7c7e4310" + +[[package]] +name = "objc2" +version = "0.5.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "46a785d4eeff09c14c487497c162e92766fbb3e4059a71840cecc03d9a50b804" +dependencies = [ + "objc-sys", + "objc2-encode", +] + [[package]] name = "objc2" version = "0.6.4" @@ -3562,6 +3849,22 @@ dependencies = [ "objc2-encode", ] +[[package]] +name = "objc2-app-kit" +version = "0.2.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e4e89ad9e3d7d297152b17d39ed92cd50ca8063a89a9fa569046d41568891eff" +dependencies = [ + "bitflags 2.11.0", + "block2 0.5.1", + "libc", + "objc2 0.5.2", + "objc2-core-data", + "objc2-core-image", + "objc2-foundation 0.2.2", + "objc2-quartz-core", +] + [[package]] name = "objc2-app-kit" version = "0.3.2" @@ -3569,9 +3872,22 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "d49e936b501e5c5bf01fda3a9452ff86dc3ea98ad5f283e1455153142d97518c" dependencies = [ "bitflags 2.11.0", - "objc2", + "block2 0.6.2", + "objc2 0.6.4", "objc2-core-foundation", - "objc2-foundation", + "objc2-foundation 0.3.2", +] + +[[package]] +name = "objc2-core-data" +version = "0.2.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "617fbf49e071c178c0b24c080767db52958f716d9eabdf0890523aeae54773ef" +dependencies = [ + "bitflags 2.11.0", + "block2 0.5.1", + "objc2 0.5.2", + "objc2-foundation 0.2.2", ] [[package]] @@ -3581,9 +3897,9 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "2a180dd8642fa45cdb7dd721cd4c11b1cadd4929ce112ebd8b9f5803cc79d536" dependencies = [ "bitflags 2.11.0", - "block2", + "block2 0.6.2", "dispatch2", - "objc2", + "objc2 0.6.4", ] [[package]] @@ -3597,6 +3913,18 @@ dependencies = [ "objc2-core-foundation", ] +[[package]] +name = "objc2-core-image" +version = "0.2.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "55260963a527c99f1819c4f8e3b47fe04f9650694ef348ffd2227e8196d34c80" +dependencies = [ + "block2 0.5.1", + "objc2 0.5.2", + "objc2-foundation 0.2.2", + "objc2-metal", +] + [[package]] name = "objc2-core-video" version = "0.3.2" @@ -3614,6 +3942,18 @@ version = "4.1.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "ef25abbcd74fb2609453eb695bd2f860d389e457f67dc17cafc8b8cbc89d0c33" +[[package]] +name = "objc2-foundation" +version = "0.2.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0ee638a5da3799329310ad4cfa62fbf045d5f56e3ef5ba4149e7452dcf89d5a8" +dependencies = [ + "bitflags 2.11.0", + "block2 0.5.1", + "libc", + "objc2 0.5.2", +] + [[package]] name = "objc2-foundation" version = "0.3.2" @@ -3621,11 +3961,36 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "e3e0adef53c21f888deb4fa59fc59f7eb17404926ee8a6f59f5df0fd7f9f3272" dependencies = [ "bitflags 2.11.0", - "block2", - "objc2", + "block2 0.6.2", + "objc2 0.6.4", "objc2-core-foundation", ] +[[package]] +name = "objc2-metal" +version = "0.2.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "dd0cba1276f6023976a406a14ffa85e1fdd19df6b0f737b063b95f6c8c7aadd6" +dependencies = [ + "bitflags 2.11.0", + "block2 0.5.1", + "objc2 0.5.2", + "objc2-foundation 0.2.2", +] + +[[package]] +name = "objc2-quartz-core" +version = "0.2.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e42bee7bff906b14b167da2bac5efe6b6a07e6f7c0a21a7308d40c960242dc7a" +dependencies = [ + "bitflags 2.11.0", + "block2 0.5.1", + "objc2 0.5.2", + "objc2-foundation 0.2.2", + "objc2-metal", +] + [[package]] name = "objc2-ui-kit" version = "0.3.2" @@ -3633,9 +3998,9 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "d87d638e33c06f577498cbcc50491496a3ed4246998a7fbba7ccb98b1e7eab22" dependencies = [ "bitflags 2.11.0", - "objc2", + "objc2 0.6.4", "objc2-core-foundation", - "objc2-foundation", + "objc2-foundation 0.3.2", ] [[package]] @@ -3781,7 +4146,7 @@ dependencies = [ "libc", "redox_syscall 0.5.18", "smallvec", - "windows-link", + "windows-link 0.2.1", ] [[package]] @@ -3958,6 +4323,12 @@ version = "0.3.32" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "7edddbd0b52d732b21ad9a5fab5c704c14cd949e5e9a1ec5929a24fded1b904c" +[[package]] +name = "plain" +version = "0.2.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b4596b6d070b27117e987119b4dac604f3c58cfb0b191112e24771b2faeac1a6" + [[package]] name = "png" version = "0.17.16" @@ -3998,6 +4369,12 @@ dependencies = [ "windows-sys 0.61.2", ] +[[package]] +name = "pollster" +version = "0.4.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "2f3a9f18d041e6d0e102a0a46750538147e5e8992d3b4873aaafee2520b00ce3" + [[package]] name = "portable-atomic" version = "1.13.1" @@ -4037,6 +4414,12 @@ dependencies = [ "zerocopy", ] +[[package]] +name = "presser" +version = "0.3.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e8cf8e6a8aa66ce33f63993ffc4ea4271eb5b0530a9002db8455ea6050c77bfa" + [[package]] name = "prettyplease" version = "0.2.37" @@ -4142,6 +4525,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "958f21e8e7ceb5a1aa7fa87fab28e7c75976e0bfe7e23ff069e0a260f894067d" dependencies = [ "memchr", + "serde", ] [[package]] @@ -4235,6 +4619,12 @@ version = "0.10.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "0c8d0fd677905edcbeedbf2edb6494d676f0e98d54d5cf9bda0b061cb8fb8aba" +[[package]] +name = "range-alloc" +version = "0.1.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ca45419789ae5a7899559e9512e58ca889e41f04f1f2445e9f4b290ceccd1d08" + [[package]] name = "rangemap" version = "1.7.1" @@ -4274,6 +4664,16 @@ dependencies = [ "font-types 0.11.0", ] +[[package]] +name = "redox_event" +version = "0.4.8" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c5018d583d6d2f5499352aea8d177e9067d1eb03ab17c78169d5ba7a30001b15" +dependencies = [ + "bitflags 2.11.0", + "libredox", +] + [[package]] name = "redox_syscall" version = "0.5.18" @@ -4285,9 +4685,9 @@ dependencies = [ [[package]] name = "redox_syscall" -version = "0.7.2" +version = "0.9.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "6d94dd2f7cd932d4dc02cc8b2b50dfd38bd079a4e5d79198b99743d7fcf9a4b4" +checksum = "c5102a6aaa05aa011a238e178e6bca86d2cb56fc9f586d37cb80f5bca6e07759" dependencies = [ "bitflags 2.11.0", ] @@ -4401,6 +4801,30 @@ dependencies = [ "zune-jpeg 0.4.21", ] +[[package]] +name = "rfd" +version = "0.16.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a15ad77d9e70a92437d8f74c35d99b4e4691128df018833e99f90bcd36152672" +dependencies = [ + "ashpd 0.11.1", + "block2 0.6.2", + "dispatch2", + "js-sys", + "log", + "objc2 0.6.4", + "objc2-app-kit 0.3.2", + "objc2-core-foundation", + "objc2-foundation 0.3.2", + "pollster", + "raw-window-handle", + "urlencoding", + "wasm-bindgen", + "wasm-bindgen-futures", + "web-sys", + "windows-sys 0.60.2", +] + [[package]] name = "rgb" version = "0.8.52" @@ -4605,6 +5029,19 @@ version = "1.2.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "94143f37725109f92c262ed2cf5e59bce7498c01bcc1502d7b9afe439a4e9f49" +[[package]] +name = "sctk-adwaita" +version = "0.11.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1dd3accc0f3f4bbaf2c9e1957a030dc582028130c67660d44c0a0345a22ca69b" +dependencies = [ + "ab_glyph", + "log", + "memmap2 0.9.10", + "smithay-client-toolkit", + "tiny-skia", +] + [[package]] name = "self_cell" version = "1.2.2" @@ -4812,14 +5249,14 @@ dependencies = [ [[package]] name = "smallvec" -version = "1.15.2" +version = "1.15.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "8ed6a63f02c8539c91a8685a86f4099661ba3da017932f6ebbea6de3f0fa7c90" +checksum = "67b1b7a3b5fe4f1376887184045fcf45c69e92af734b7aaddc05fb777b6fbd03" [[package]] name = "smithay" version = "0.7.0" -source = "git+https://github.com/smithay/smithay.git?rev=1ed69cb#1ed69cb55e9d0e9f38887c3b7d53d24365c4b14f" +source = "git+https://github.com/smithay/smithay.git?rev=8eb4076#8eb4076cad8705c56f5e3bac8577b83576b6ce76" dependencies = [ "aliasable", "appendlist", @@ -4828,16 +5265,16 @@ dependencies = [ "bitflags 2.11.0", "calloop", "cc", + "cgmath", "cursor-icon", "downcast-rs", - "drm 0.14.1", - "drm-ffi 0.9.0", + "drm", + "drm-ffi", "drm-fourcc", "encoding_rs", "errno", "gbm", "gl_generator", - "glam 0.33.2", "glow", "indexmap 2.13.0", "input", @@ -4865,7 +5302,7 @@ dependencies = [ "wayland-protocols-misc", "wayland-protocols-wlr", "wayland-server", - "winit", + "winit 0.31.0-beta.2 (registry+https://github.com/rust-lang/crates.io-index)", "x11rb", "xkbcommon 0.9.0", ] @@ -4877,12 +5314,14 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "0512da38f5e2b31201a93524adb8d3136276fa4fe4aafab4e1f727a82b534cc0" dependencies = [ "bitflags 2.11.0", + "bytemuck", "calloop", "calloop-wayland-source", "cursor-icon", "libc", "log", - "memmap2", + "memmap2 0.9.10", + "pkg-config", "rustix 1.1.4", "thiserror 2.0.18", "wayland-backend", @@ -4894,13 +5333,13 @@ dependencies = [ "wayland-protocols-misc", "wayland-protocols-wlr", "wayland-scanner", + "xkbcommon 0.8.0", "xkeysym", ] [[package]] name = "smithay-clipboard" version = "0.8.0" -source = "git+https://github.com/pop-os/smithay-clipboard?tag=sctk-0.20#859b02c88f45c554049a67c6ddeec1692ce0e20b" dependencies = [ "libc", "raw-window-handle", @@ -4941,22 +5380,30 @@ version = "1.3.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "27207bb65232eda1f588cf46db2fee75c0808d557f6b3cf19a75f5d6d7c94df1" +[[package]] +name = "socket2" +version = "0.6.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "52d1cfed4120b4d927bf7c0f86d2087a4a7d6027c906d9f9d525a80573b9be51" +dependencies = [ + "libc", + "windows-sys 0.61.2", +] + [[package]] name = "softbuffer" version = "0.4.1" -source = "git+https://github.com/pop-os/softbuffer?tag=cosmic-4.0#c2b2c19ddb38ff17495643699f97cb1f2064a1be" dependencies = [ "as-raw-xcb-connection", "bytemuck", "cfg_aliases", "cocoa", "core-graphics", - "drm 0.11.1", "fastrand", "foreign-types", "js-sys", "log", - "memmap2", + "memmap2 0.9.10", "objc", "raw-window-handle", "redox_syscall 0.5.18", @@ -4971,6 +5418,15 @@ dependencies = [ "x11rb", ] +[[package]] +name = "spirv" +version = "0.3.0+sdk-1.3.268.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "eda41003dc44290527a59b13432d4a0379379fa074b70174882adfbdfd917844" +dependencies = [ + "bitflags 2.11.0", +] + [[package]] name = "stable_deref_trait" version = "1.2.1" @@ -5116,6 +5572,15 @@ dependencies = [ "windows-sys 0.61.2", ] +[[package]] +name = "termcolor" +version = "1.4.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "06794f8f6c5c898b3275aebefa6b8a1cb24cd2c6c79397ab15774837a0bc5755" +dependencies = [ + "winapi-util", +] + [[package]] name = "thiserror" version = "1.0.69" @@ -5261,6 +5726,45 @@ version = "0.1.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "1f3ccbac311fea05f86f61904b462b55fb3df8837a366dfc601a0161d0532f20" +[[package]] +name = "tokio" +version = "1.52.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8fc7f01b389ac15039e4dc9531aa973a135d7a4135281b12d7c1bc79fd57fffe" +dependencies = [ + "bytes", + "libc", + "mio", + "pin-project-lite", + "signal-hook-registry", + "socket2", + "tokio-macros", + "tracing", + "windows-sys 0.61.2", +] + +[[package]] +name = "tokio-macros" +version = "2.7.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "385a6cb71ab9ab790c5fe8d67f1645e6c450a7ce006a33de03daa956cf70a496" +dependencies = [ + "proc-macro2", + "quote", + "syn", +] + +[[package]] +name = "tokio-stream" +version = "0.1.18" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "32da49809aab5c3bc678af03902d4ccddea2a87d028d86392a4b1560c6906c70" +dependencies = [ + "futures-core", + "pin-project-lite", + "tokio", +] + [[package]] name = "toml" version = "0.5.11" @@ -5562,6 +6066,12 @@ version = "0.1.14" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "7dd6e30e90baa6f72411720665d41d89b9a3d039dc45b8faea1ddd07f617f6af" +[[package]] +name = "unicode-width" +version = "0.2.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b4ac048d71ede7ee76d585517add45da530660ef4390e49b098733c6e897f254" + [[package]] name = "unicode-xid" version = "0.2.6" @@ -5578,8 +6088,15 @@ dependencies = [ "idna", "percent-encoding", "serde", + "serde_derive", ] +[[package]] +name = "urlencoding" +version = "2.1.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "daf8dba3b7eb870caf1ddeed7bc9d2a049f3cfdfae7cb521b087cc33ae4c49da" + [[package]] name = "usvg" version = "0.37.0" @@ -6027,12 +6544,18 @@ dependencies = [ "cfg_aliases", "document-features", "hashbrown 0.16.1", + "js-sys", "log", + "naga", + "parking_lot", "portable-atomic", "profiling", "raw-window-handle", "smallvec", "static_assertions", + "wasm-bindgen", + "wasm-bindgen-futures", + "web-sys", "wgpu-core", "wgpu-hal", "wgpu-types", @@ -6063,11 +6586,31 @@ dependencies = [ "rustc-hash 1.1.0", "smallvec", "thiserror 2.0.18", + "wgpu-core-deps-apple", + "wgpu-core-deps-emscripten", "wgpu-core-deps-windows-linux-android", "wgpu-hal", "wgpu-types", ] +[[package]] +name = "wgpu-core-deps-apple" +version = "28.0.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "87b7b696b918f337c486bf93142454080a32a37832ba8a31e4f48221890047da" +dependencies = [ + "wgpu-hal", +] + +[[package]] +name = "wgpu-core-deps-emscripten" +version = "28.0.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "34b251c331f84feac147de3c4aa3aa45112622a95dd7ee1b74384fa0458dbd79" +dependencies = [ + "wgpu-hal", +] + [[package]] name = "wgpu-core-deps-windows-linux-android" version = "28.0.0" @@ -6083,18 +6626,46 @@ version = "28.0.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "44d6cb474beb218824dcc9e1ce679d973f719262789bfb27407da560cac20eeb" dependencies = [ + "android_system_properties", + "arrayvec", + "ash", + "bit-set", "bitflags 2.11.0", + "block", + "bytemuck", "cfg-if", "cfg_aliases", + "core-graphics-types 0.2.0", + "glow", + "glutin_wgl_sys", + "gpu-allocator", + "gpu-descriptor", + "hashbrown 0.16.1", + "js-sys", + "khronos-egl", + "libc", "libloading", "log", + "metal", "naga", + "ndk-sys", + "objc", + "once_cell", + "ordered-float", + "parking_lot", "portable-atomic", "portable-atomic-util", + "profiling", + "range-alloc", "raw-window-handle", "renderdoc-sys", + "smallvec", "thiserror 2.0.18", + "wasm-bindgen", + "web-sys", "wgpu-types", + "windows 0.62.2", + "windows-core 0.62.2", ] [[package]] @@ -6144,18 +6715,72 @@ checksum = "712e227841d057c1ee1cd2fb22fa7e5a5461ae8e48fa2ca79ec42cfc1931183f" [[package]] name = "window_clipboard" version = "0.4.1" -source = "git+https://github.com/pop-os/window_clipboard.git?tag=sctk-0.20#f68595ee0e62fbd6589f4709b5aaa5c3c7ea5f6c" dependencies = [ "clipboard-win", "clipboard_macos", "clipboard_wayland", - "clipboard_x11", "dnd", "mime 0.1.0", "raw-window-handle", "thiserror 1.0.69", ] +[[package]] +name = "windows" +version = "0.61.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9babd3a767a4c1aef6900409f85f5d53ce2544ccdfaa86dad48c91782c6d6893" +dependencies = [ + "windows-collections 0.2.0", + "windows-core 0.61.2", + "windows-future 0.2.1", + "windows-link 0.1.3", + "windows-numerics 0.2.0", +] + +[[package]] +name = "windows" +version = "0.62.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "527fadee13e0c05939a6a05d5bd6eec6cd2e3dbd648b9f8e447c6518133d8580" +dependencies = [ + "windows-collections 0.3.2", + "windows-core 0.62.2", + "windows-future 0.3.2", + "windows-numerics 0.3.1", +] + +[[package]] +name = "windows-collections" +version = "0.2.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3beeceb5e5cfd9eb1d76b381630e82c4241ccd0d27f1a39ed41b2760b255c5e8" +dependencies = [ + "windows-core 0.61.2", +] + +[[package]] +name = "windows-collections" +version = "0.3.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "23b2d95af1a8a14a3c7367e1ed4fc9c20e0a26e79551b1454d72583c97cc6610" +dependencies = [ + "windows-core 0.62.2", +] + +[[package]] +name = "windows-core" +version = "0.61.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c0fdd3ddb90610c7638aa2b3a3ab2904fb9e5cdbecc643ddb3647212781c4ae3" +dependencies = [ + "windows-implement", + "windows-interface", + "windows-link 0.1.3", + "windows-result 0.3.4", + "windows-strings 0.4.2", +] + [[package]] name = "windows-core" version = "0.62.2" @@ -6164,9 +6789,31 @@ checksum = "b8e83a14d34d0623b51dce9581199302a221863196a1dde71a7663a4c2be9deb" dependencies = [ "windows-implement", "windows-interface", - "windows-link", - "windows-result", - "windows-strings", + "windows-link 0.2.1", + "windows-result 0.4.1", + "windows-strings 0.5.1", +] + +[[package]] +name = "windows-future" +version = "0.2.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "fc6a41e98427b19fe4b73c550f060b59fa592d7d686537eebf9385621bfbad8e" +dependencies = [ + "windows-core 0.61.2", + "windows-link 0.1.3", + "windows-threading 0.1.0", +] + +[[package]] +name = "windows-future" +version = "0.3.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e1d6f90251fe18a279739e78025bd6ddc52a7e22f921070ccdc67dde84c605cb" +dependencies = [ + "windows-core 0.62.2", + "windows-link 0.2.1", + "windows-threading 0.2.1", ] [[package]] @@ -6191,19 +6838,63 @@ dependencies = [ "syn", ] +[[package]] +name = "windows-link" +version = "0.1.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5e6ad25900d524eaabdbbb96d20b4311e1e7ae1699af4fb28c17ae66c80d798a" + [[package]] name = "windows-link" version = "0.2.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "f0805222e57f7521d6a62e36fa9163bc891acd422f971defe97d64e70d0a4fe5" +[[package]] +name = "windows-numerics" +version = "0.2.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9150af68066c4c5c07ddc0ce30421554771e528bde427614c61038bc2c92c2b1" +dependencies = [ + "windows-core 0.61.2", + "windows-link 0.1.3", +] + +[[package]] +name = "windows-numerics" +version = "0.3.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6e2e40844ac143cdb44aead537bbf727de9b044e107a0f1220392177d15b0f26" +dependencies = [ + "windows-core 0.62.2", + "windows-link 0.2.1", +] + +[[package]] +name = "windows-result" +version = "0.3.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "56f42bd332cc6c8eac5af113fc0c1fd6a8fd2aa08a0119358686e5160d0586c6" +dependencies = [ + "windows-link 0.1.3", +] + [[package]] name = "windows-result" version = "0.4.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "7781fa89eaf60850ac3d2da7af8e5242a5ea78d1a11c49bf2910bb5a73853eb5" dependencies = [ - "windows-link", + "windows-link 0.2.1", +] + +[[package]] +name = "windows-strings" +version = "0.4.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "56e6c93f3a0c3b36176cb1327a4958a0353d5d166c2a35cb268ace15e91d3b57" +dependencies = [ + "windows-link 0.1.3", ] [[package]] @@ -6212,7 +6903,7 @@ version = "0.5.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "7837d08f69c77cf6b07689544538e017c1bfcf57e34b4c0ff58e6c2cd3b37091" dependencies = [ - "windows-link", + "windows-link 0.2.1", ] [[package]] @@ -6266,7 +6957,7 @@ version = "0.61.2" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "ae137229bcbd6cdf0f7b80a31df61766145077ddf49416a728b02cb3921ff3fc" dependencies = [ - "windows-link", + "windows-link 0.2.1", ] [[package]] @@ -6321,7 +7012,7 @@ version = "0.53.5" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "4945f9f551b88e0d65f3db0bc25c33b8acea4d9e41163edf90dcd0b19f9069f3" dependencies = [ - "windows-link", + "windows-link 0.2.1", "windows_aarch64_gnullvm 0.53.1", "windows_aarch64_msvc 0.53.1", "windows_i686_gnu 0.53.1", @@ -6332,6 +7023,24 @@ dependencies = [ "windows_x86_64_msvc 0.53.1", ] +[[package]] +name = "windows-threading" +version = "0.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b66463ad2e0ea3bbf808b7f1d371311c80e115c0b71d60efc142cafbcfb057a6" +dependencies = [ + "windows-link 0.1.3", +] + +[[package]] +name = "windows-threading" +version = "0.2.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3949bd5b99cafdf1c7ca86b43ca564028dfe27d66958f2470940f73d86d75b37" +dependencies = [ + "windows-link 0.2.1", +] + [[package]] name = "windows_aarch64_gnullvm" version = "0.42.2" @@ -6512,6 +7221,30 @@ version = "0.53.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "d6bbff5f0aada427a1e5a6da5f1f98158182f26556f345ac9e04d36d0ebed650" +[[package]] +name = "winit" +version = "0.31.0-beta.2" +dependencies = [ + "bitflags 2.11.0", + "cfg_aliases", + "cursor-icon", + "dpi 0.1.2", + "libc", + "raw-window-handle", + "rustix 1.1.4", + "smol_str", + "tracing", + "winit-android 0.31.0-beta.2", + "winit-appkit 0.31.0-beta.2", + "winit-common 0.31.0-beta.2", + "winit-core 0.31.0-beta.2", + "winit-orbital 0.31.0-beta.2", + "winit-uikit 0.31.0-beta.2", + "winit-wayland 0.31.0-beta.2", + "winit-web 0.31.0-beta.2", + "winit-win32 0.31.0-beta.2", +] + [[package]] name = "winit" version = "0.31.0-beta.2" @@ -6521,24 +7254,38 @@ dependencies = [ "bitflags 2.11.0", "cfg_aliases", "cursor-icon", - "dpi", + "dpi 0.1.2 (registry+https://github.com/rust-lang/crates.io-index)", "libc", "raw-window-handle", "rustix 1.1.4", "smol_str", "tracing", - "winit-android", - "winit-appkit", - "winit-common", - "winit-core", - "winit-orbital", - "winit-uikit", - "winit-wayland", - "winit-web", - "winit-win32", + "winit-android 0.31.0-beta.2 (registry+https://github.com/rust-lang/crates.io-index)", + "winit-appkit 0.31.0-beta.2 (registry+https://github.com/rust-lang/crates.io-index)", + "winit-common 0.31.0-beta.2 (registry+https://github.com/rust-lang/crates.io-index)", + "winit-core 0.31.0-beta.2 (registry+https://github.com/rust-lang/crates.io-index)", + "winit-orbital 0.31.0-beta.2 (registry+https://github.com/rust-lang/crates.io-index)", + "winit-uikit 0.31.0-beta.2 (registry+https://github.com/rust-lang/crates.io-index)", + "winit-wayland 0.31.0-beta.2 (registry+https://github.com/rust-lang/crates.io-index)", + "winit-web 0.31.0-beta.2 (registry+https://github.com/rust-lang/crates.io-index)", + "winit-win32 0.31.0-beta.2 (registry+https://github.com/rust-lang/crates.io-index)", "winit-x11", ] +[[package]] +name = "winit-android" +version = "0.31.0-beta.2" +dependencies = [ + "android-activity", + "bitflags 2.11.0", + "dpi 0.1.2", + "ndk", + "raw-window-handle", + "smol_str", + "tracing", + "winit-core 0.31.0-beta.2", +] + [[package]] name = "winit-android" version = "0.31.0-beta.2" @@ -6547,12 +7294,33 @@ checksum = "51d9c0d2cd93efec3a9f9ad819cfaf0834782403af7c0d248c784ec0c61761df" dependencies = [ "android-activity", "bitflags 2.11.0", - "dpi", + "dpi 0.1.2 (registry+https://github.com/rust-lang/crates.io-index)", "ndk", "raw-window-handle", "smol_str", "tracing", - "winit-core", + "winit-core 0.31.0-beta.2 (registry+https://github.com/rust-lang/crates.io-index)", +] + +[[package]] +name = "winit-appkit" +version = "0.31.0-beta.2" +dependencies = [ + "bitflags 2.11.0", + "block2 0.6.2", + "dispatch2", + "dpi 0.1.2", + "objc2 0.6.4", + "objc2-app-kit 0.3.2", + "objc2-core-foundation", + "objc2-core-graphics", + "objc2-core-video", + "objc2-foundation 0.3.2", + "raw-window-handle", + "smol_str", + "tracing", + "winit-common 0.31.0-beta.2", + "winit-core 0.31.0-beta.2", ] [[package]] @@ -6562,20 +7330,33 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "21310ca07851a49c348e0c2cc768e36b52ca65afda2c2354d78ed4b90074d8aa" dependencies = [ "bitflags 2.11.0", - "block2", + "block2 0.6.2", "dispatch2", - "dpi", - "objc2", - "objc2-app-kit", + "dpi 0.1.2 (registry+https://github.com/rust-lang/crates.io-index)", + "objc2 0.6.4", + "objc2-app-kit 0.3.2", "objc2-core-foundation", "objc2-core-graphics", "objc2-core-video", - "objc2-foundation", + "objc2-foundation 0.3.2", "raw-window-handle", "smol_str", "tracing", - "winit-common", - "winit-core", + "winit-common 0.31.0-beta.2 (registry+https://github.com/rust-lang/crates.io-index)", + "winit-core 0.31.0-beta.2 (registry+https://github.com/rust-lang/crates.io-index)", +] + +[[package]] +name = "winit-common" +version = "0.31.0-beta.2" +dependencies = [ + "memmap2 0.9.10", + "objc2 0.6.4", + "objc2-core-foundation", + "smol_str", + "tracing", + "winit-core 0.31.0-beta.2", + "xkbcommon-dl", ] [[package]] @@ -6584,16 +7365,29 @@ version = "0.31.0-beta.2" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "45375fbac4cbb77260d83a30b1f9d8105880dbac99a9ae97f56656694680ff69" dependencies = [ - "memmap2", - "objc2", + "memmap2 0.9.10", + "objc2 0.6.4", "objc2-core-foundation", "smol_str", "tracing", - "winit-core", + "winit-core 0.31.0-beta.2 (registry+https://github.com/rust-lang/crates.io-index)", "x11-dl", "xkbcommon-dl", ] +[[package]] +name = "winit-core" +version = "0.31.0-beta.2" +dependencies = [ + "bitflags 2.11.0", + "cursor-icon", + "dpi 0.1.2", + "keyboard-types", + "raw-window-handle", + "smol_str", + "web-time", +] + [[package]] name = "winit-core" version = "0.31.0-beta.2" @@ -6602,13 +7396,28 @@ checksum = "e4f0ccd7abb43740e2c6124ac7cae7d865ecec74eec63783e8922577ac232583" dependencies = [ "bitflags 2.11.0", "cursor-icon", - "dpi", + "dpi 0.1.2 (registry+https://github.com/rust-lang/crates.io-index)", "keyboard-types", "raw-window-handle", "smol_str", "web-time", ] +[[package]] +name = "winit-orbital" +version = "0.31.0-beta.2" +dependencies = [ + "bitflags 2.11.0", + "dpi 0.1.2", + "libredox", + "orbclient", + "raw-window-handle", + "redox_event", + "smol_str", + "tracing", + "winit-core 0.31.0-beta.2", +] + [[package]] name = "winit-orbital" version = "0.31.0-beta.2" @@ -6616,13 +7425,32 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "51ea1fb262e7209f265f12bd0cc792c399b14355675e65531e9c8a87db287d46" dependencies = [ "bitflags 2.11.0", - "dpi", + "dpi 0.1.2 (registry+https://github.com/rust-lang/crates.io-index)", "orbclient", "raw-window-handle", "redox_syscall 0.5.18", "smol_str", "tracing", - "winit-core", + "winit-core 0.31.0-beta.2 (registry+https://github.com/rust-lang/crates.io-index)", +] + +[[package]] +name = "winit-uikit" +version = "0.31.0-beta.2" +dependencies = [ + "bitflags 2.11.0", + "block2 0.6.2", + "dispatch2", + "dpi 0.1.2", + "objc2 0.6.4", + "objc2-core-foundation", + "objc2-foundation 0.3.2", + "objc2-ui-kit", + "raw-window-handle", + "smol_str", + "tracing", + "winit-common 0.31.0-beta.2", + "winit-core 0.31.0-beta.2", ] [[package]] @@ -6632,18 +7460,43 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "680a356e798837d8eb274d4556e83bceaf81698194e31aafc5cfb8a9f2fab643" dependencies = [ "bitflags 2.11.0", - "block2", + "block2 0.6.2", "dispatch2", - "dpi", - "objc2", + "dpi 0.1.2 (registry+https://github.com/rust-lang/crates.io-index)", + "objc2 0.6.4", "objc2-core-foundation", - "objc2-foundation", + "objc2-foundation 0.3.2", "objc2-ui-kit", "raw-window-handle", "smol_str", "tracing", - "winit-common", - "winit-core", + "winit-common 0.31.0-beta.2 (registry+https://github.com/rust-lang/crates.io-index)", + "winit-core 0.31.0-beta.2 (registry+https://github.com/rust-lang/crates.io-index)", +] + +[[package]] +name = "winit-wayland" +version = "0.31.0-beta.2" +dependencies = [ + "ahash", + "bitflags 2.11.0", + "calloop", + "cursor-icon", + "dpi 0.1.2", + "libc", + "memmap2 0.9.10", + "raw-window-handle", + "rustix 1.1.4", + "sctk-adwaita", + "smithay-client-toolkit", + "smol_str", + "tracing", + "wayland-backend", + "wayland-client", + "wayland-protocols", + "wayland-protocols-plasma", + "winit-common 0.31.0-beta.2", + "winit-core 0.31.0-beta.2", ] [[package]] @@ -6656,9 +7509,9 @@ dependencies = [ "bitflags 2.11.0", "calloop", "cursor-icon", - "dpi", + "dpi 0.1.2 (registry+https://github.com/rust-lang/crates.io-index)", "libc", - "memmap2", + "memmap2 0.9.10", "raw-window-handle", "rustix 1.1.4", "smithay-client-toolkit", @@ -6668,8 +7521,29 @@ dependencies = [ "wayland-client", "wayland-protocols", "wayland-protocols-plasma", - "winit-common", - "winit-core", + "winit-common 0.31.0-beta.2 (registry+https://github.com/rust-lang/crates.io-index)", + "winit-core 0.31.0-beta.2 (registry+https://github.com/rust-lang/crates.io-index)", +] + +[[package]] +name = "winit-web" +version = "0.31.0-beta.2" +dependencies = [ + "atomic-waker", + "bitflags 2.11.0", + "concurrent-queue", + "cursor-icon", + "dpi 0.1.2", + "js-sys", + "pin-project", + "raw-window-handle", + "smol_str", + "tracing", + "wasm-bindgen", + "wasm-bindgen-futures", + "web-sys", + "web-time", + "winit-core 0.31.0-beta.2", ] [[package]] @@ -6682,7 +7556,7 @@ dependencies = [ "bitflags 2.11.0", "concurrent-queue", "cursor-icon", - "dpi", + "dpi 0.1.2 (registry+https://github.com/rust-lang/crates.io-index)", "js-sys", "pin-project", "raw-window-handle", @@ -6692,7 +7566,22 @@ dependencies = [ "wasm-bindgen-futures", "web-sys", "web-time", - "winit-core", + "winit-core 0.31.0-beta.2 (registry+https://github.com/rust-lang/crates.io-index)", +] + +[[package]] +name = "winit-win32" +version = "0.31.0-beta.2" +dependencies = [ + "bitflags 2.11.0", + "cursor-icon", + "dpi 0.1.2", + "raw-window-handle", + "smol_str", + "tracing", + "unicode-segmentation", + "windows-sys 0.59.0", + "winit-core 0.31.0-beta.2", ] [[package]] @@ -6703,13 +7592,13 @@ checksum = "644ea78af0e858aa3b092e5d1c67c41995a98220c81813f1353b28bc8bb91eaa" dependencies = [ "bitflags 2.11.0", "cursor-icon", - "dpi", + "dpi 0.1.2 (registry+https://github.com/rust-lang/crates.io-index)", "raw-window-handle", "smol_str", "tracing", "unicode-segmentation", "windows-sys 0.59.0", - "winit-core", + "winit-core 0.31.0-beta.2 (registry+https://github.com/rust-lang/crates.io-index)", ] [[package]] @@ -6722,15 +7611,15 @@ dependencies = [ "bytemuck", "calloop", "cursor-icon", - "dpi", + "dpi 0.1.2 (registry+https://github.com/rust-lang/crates.io-index)", "libc", "percent-encoding", "raw-window-handle", "rustix 1.1.4", "smol_str", "tracing", - "winit-common", - "winit-core", + "winit-common 0.31.0-beta.2 (registry+https://github.com/rust-lang/crates.io-index)", + "winit-core 0.31.0-beta.2 (registry+https://github.com/rust-lang/crates.io-index)", "x11-dl", "x11rb", "xkbcommon-dl", @@ -6912,6 +7801,17 @@ dependencies = [ "libc", ] +[[package]] +name = "xkbcommon" +version = "0.7.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "13867d259930edc7091a6c41b4ce6eee464328c6ff9659b7e4c668ca20d4c91e" +dependencies = [ + "libc", + "memmap2 0.8.0", + "xkeysym", +] + [[package]] name = "xkbcommon" version = "0.8.0" @@ -6919,7 +7819,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "8d66ca9352cbd4eecbbc40871d8a11b4ac8107cfc528a6e14d7c19c69d0e1ac9" dependencies = [ "libc", - "memmap2", + "memmap2 0.9.10", "xkeysym", ] @@ -6930,7 +7830,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "a7a974f48060a14e95705c01f24ad9c3345022f4d97441b8a36beb7ed5c4a02d" dependencies = [ "libc", - "memmap2", + "memmap2 0.9.10", "xkeysym", ] @@ -6952,6 +7852,9 @@ name = "xkeysym" version = "0.2.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "b9cc00251562a284751c9973bace760d86c0276c471b4be569fe6b068ee97a56" +dependencies = [ + "bytemuck", +] [[package]] name = "xml-rs" @@ -7025,6 +7928,7 @@ dependencies = [ "rustix 1.1.4", "serde", "serde_repr", + "tokio", "tracing", "uds_windows", "uuid", @@ -7035,6 +7939,30 @@ dependencies = [ "zvariant", ] +[[package]] +name = "zbus-lockstep" +version = "0.5.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6998de05217a084b7578728a9443d04ea4cd80f2a0839b8d78770b76ccd45863" +dependencies = [ + "zbus_xml", + "zvariant", +] + +[[package]] +name = "zbus-lockstep-macros" +version = "0.5.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "10da05367f3a7b7553c8cdf8fa91aee6b64afebe32b51c95177957efc47ca3a0" +dependencies = [ + "proc-macro2", + "quote", + "syn", + "zbus-lockstep", + "zbus_xml", + "zvariant", +] + [[package]] name = "zbus_macros" version = "5.16.0" @@ -7061,6 +7989,18 @@ dependencies = [ "zvariant", ] +[[package]] +name = "zbus_xml" +version = "5.1.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a8067892e940ed1727dea64690378601603b31d62dfde019a5335fbb7c0e0ed9" +dependencies = [ + "quick-xml", + "serde", + "zbus_names", + "zvariant", +] + [[package]] name = "zeno" version = "0.3.3" @@ -7187,6 +8127,7 @@ dependencies = [ "endi", "enumflags2", "serde", + "url", "winnow 1.0.3", "zvariant_derive", "zvariant_utils", @@ -7217,3 +8158,7 @@ dependencies = [ "syn", "winnow 1.0.3", ] + +[[patch.unused]] +name = "winit-x11" +version = "0.31.0-beta.2" From d1849f639bf33968abd35592c6ada8dc96b97c09 Mon Sep 17 00:00:00 2001 From: Lionel DARNIS Date: Wed, 22 Jul 2026 15:21:58 +0200 Subject: [PATCH 92/92] fix: resolve rebase conflicts and fix package name --- Cargo.lock | 28 ++++++++++++++++++---------- Cargo.toml | 14 ++++++-------- src/shell/element/stack.rs | 2 -- src/shell/element/stack/tab.rs | 6 ------ src/shell/element/window.rs | 1 - 5 files changed, 24 insertions(+), 27 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index 5edc17e0..0d489aa2 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -866,7 +866,7 @@ checksum = "fe6d2e5af09e8c8ad56c969f2157a3d4238cebc7c55f0a517728c38f7b200f81" dependencies = [ "serde", "termcolor", - "unicode-width 0.1.14", + "unicode-width 0.2.2", ] [[package]] @@ -989,7 +989,6 @@ dependencies = [ "anyhow", "bitflags 2.11.0", "calloop", - "cgmath", "clap_lex", "cosmic-comp-config", "cosmic-config", @@ -1001,14 +1000,16 @@ dependencies = [ "egui_plot", "futures-executor", "futures-util", + "glam 0.33.2", "i18n-embed", "i18n-embed-fl", + "iced_graphics", "iced_tiny_skia", "id_tree", "indexmap 2.13.0", "jiff", "keyframe", - "libcosmic-yoda", + "libcosmic", "libdisplay-info", "libsystemd", "log-panics", @@ -1025,6 +1026,7 @@ dependencies = [ "sanitize-filename", "serde", "serde_json", + "smallvec", "smithay", "smithay-egui", "thiserror 2.0.18", @@ -2160,6 +2162,12 @@ version = "0.25.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "151665d9be52f9bb40fc7966565d39666f2d1e69233571b71b87791c7e0528b3" +[[package]] +name = "glam" +version = "0.33.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7f22fb22f065b308be0d8724e3706c7fa3fc2a6c7d6899df4cad7860e7a75436" + [[package]] name = "glow" version = "0.16.0" @@ -2474,7 +2482,7 @@ dependencies = [ "bytes", "cosmic-client-toolkit", "dnd", - "glam", + "glam 0.25.0", "lilt", "log", "mime 0.1.0", @@ -2590,7 +2598,7 @@ dependencies = [ "cosmic-client-toolkit", "cryoglyph", "futures", - "glam", + "glam 0.25.0", "guillotiere", "iced_debug", "iced_graphics", @@ -3131,7 +3139,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "68ab91017fe16c622486840e4c83c9a37afeff978bd239b5293d61ece587de66" [[package]] -name = "libcosmic-yoda" +name = "libcosmic" version = "0.1.0-yoda.2" dependencies = [ "apply", @@ -5249,14 +5257,14 @@ dependencies = [ [[package]] name = "smallvec" -version = "1.15.1" +version = "1.15.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "67b1b7a3b5fe4f1376887184045fcf45c69e92af734b7aaddc05fb777b6fbd03" +checksum = "8ed6a63f02c8539c91a8685a86f4099661ba3da017932f6ebbea6de3f0fa7c90" [[package]] name = "smithay" version = "0.7.0" -source = "git+https://github.com/smithay/smithay.git?rev=8eb4076#8eb4076cad8705c56f5e3bac8577b83576b6ce76" +source = "git+https://github.com/smithay/smithay.git?rev=1ed69cb#1ed69cb55e9d0e9f38887c3b7d53d24365c4b14f" dependencies = [ "aliasable", "appendlist", @@ -5265,7 +5273,6 @@ dependencies = [ "bitflags 2.11.0", "calloop", "cc", - "cgmath", "cursor-icon", "downcast-rs", "drm", @@ -5275,6 +5282,7 @@ dependencies = [ "errno", "gbm", "gl_generator", + "glam 0.33.2", "glow", "indexmap 2.13.0", "input", diff --git a/Cargo.toml b/Cargo.toml index 2723131e..fa3747f6 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -43,7 +43,7 @@ iced_tiny_skia = { path = "../libcosmic/iced/tiny_skia" } iced_graphics = { path = "../libcosmic/iced/graphics", features = ["geometry"] } indexmap = "2.13" keyframe = "1.1.1" -cosmic = { package = "libcosmic-yoda", path = "../libcosmic", default-features = false, features = [ +cosmic = { package = "libcosmic", path = "../libcosmic", default-features = false, features = [ "tokio", "wayland", "multi-window", @@ -145,7 +145,7 @@ inherits = "release" [profile.release] lto = "fat" -[patch('https://github.com/pop-os/libcosmic')] +[patch."https://github.com/pop-os/libcosmic"] cosmic-config = { path = "../libcosmic/cosmic-config" } cosmic-theme = { path = "../libcosmic/cosmic-theme" } iced = { path = "../libcosmic/iced" } @@ -160,20 +160,18 @@ iced_wgpu = { path = "../libcosmic/iced/wgpu" } iced_widget = { path = "../libcosmic/iced/widget" } iced_winit = { path = "../libcosmic/iced/winit" } -[patch('https://github.com/pop-os/cosmic-protocols')] +[patch."https://github.com/pop-os/cosmic-protocols"] cosmic-protocols = { path = "../cosmic-protocols" } cosmic-client-toolkit = { path = "../cosmic-protocols/client-toolkit" } -[patch('https://github.com/pop-os/cosmic-settings-daemon')] +[patch."https://github.com/pop-os/cosmic-settings-daemon"] cosmic-settings-config = { path = "../cosmic-settings-daemon/config" } cosmic-settings-daemon-config = { path = "../cosmic-settings-daemon/cosmic-settings-daemon-config" } -[patch('https://github.com/pop-os/cosmic-text.git')] -cosmic-text = { path = "../cosmic-text" } -[patch.'https://github.com/pop-os/cosmic-text.git'] +[patch."https://github.com/pop-os/cosmic-text.git"] cosmic-text = { path = "../cosmic-text" } -[patch('https://forge.aditua.com/leyoda/window_clipboard.git')] +[patch."https://forge.aditua.com/leyoda/window_clipboard.git"] window_clipboard = { path = "../window_clipboard" } dnd = { path = "../window_clipboard/dnd" } mime = { path = "../window_clipboard/mime" } diff --git a/src/shell/element/stack.rs b/src/shell/element/stack.rs index 7d1d9b51..7ce26204 100644 --- a/src/shell/element/stack.rs +++ b/src/shell/element/stack.rs @@ -1381,8 +1381,6 @@ impl Decorations for DefaultDecorations { snap: true, icon_color: Some(cosmic_theme.background(false).on.into()), text_color: Some(cosmic_theme.background(false).on.into()), - icon_color: Some(cosmic_theme.background(false).on.into()), - text_color: Some(cosmic_theme.background(false).on.into()), background: Some(Background::Color(background.into())), border: Border { radius, diff --git a/src/shell/element/stack/tab.rs b/src/shell/element/stack/tab.rs index c501698f..a92c13c5 100644 --- a/src/shell/element/stack/tab.rs +++ b/src/shell/element/stack/tab.rs @@ -66,9 +66,6 @@ impl From for theme::Container<'_> { background: Some(Background::Color( theme.cosmic().primary(false).component.selected.into(), )), - background: Some(Background::Color( - theme.cosmic().primary(false).component.selected.into(), - )), border: Border { radius: 0.0.into(), width: 0.0, @@ -85,9 +82,6 @@ impl From for theme::Container<'_> { background: Some(Background::Color( theme.cosmic().primary(false).component.base.into(), )), - background: Some(Background::Color( - theme.cosmic().primary(false).component.base.into(), - )), border: Border { radius: 0.0.into(), width: 0.0, diff --git a/src/shell/element/window.rs b/src/shell/element/window.rs index 06b34188..838efd24 100644 --- a/src/shell/element/window.rs +++ b/src/shell/element/window.rs @@ -832,7 +832,6 @@ impl Program for CosmicWindowInternal { fn background_color(&self, theme: &cosmic::Theme) -> Color { if self.window.is_maximized(false) { theme.cosmic().background(false).base.into() - theme.cosmic().background(false).base.into() } else { Color::TRANSPARENT }