Compare commits
87 commits
e65b97abb8
...
d1849f639b
| Author | SHA1 | Date | |
|---|---|---|---|
| d1849f639b | |||
| e6173b93ca | |||
| ba1232cffc | |||
| 4fa39e5ea3 | |||
| bbf7effda2 | |||
| 3d01a21900 | |||
| 3e87e769a7 | |||
|
|
4d370cdf92 | ||
|
|
1d72227a30 | ||
|
|
d7d6be3cdb | ||
|
|
270ba7bf80 | ||
|
|
6c6b56a7f3 | ||
|
|
dda2d14832 | ||
|
|
c92c18e09e | ||
|
|
2648aea0df | ||
|
|
27956e6e00 | ||
|
|
dd6ebd03c9 | ||
|
|
d84d5d46ce | ||
|
|
60137c0d9e | ||
|
|
8955024bff | ||
|
|
9789187416 | ||
|
|
aabf1cdc08 | ||
|
|
9b5a2a0f83 | ||
|
|
d41124a51d | ||
|
|
160bd39ba4 | ||
|
|
2d95d46a37 | ||
|
|
8ab946f10a | ||
|
|
9feaa865ab | ||
|
|
2b360a8dd7 | ||
|
|
2329cea5c9 | ||
|
|
b8807a541f | ||
|
|
8f82cade68 | ||
|
|
dec1ee8633 | ||
|
|
603c90b103 | ||
|
|
7ee78d9c13 | ||
|
|
a69d8d2dba | ||
|
|
7a7c6b7fd4 | ||
|
|
80a440fcb0 | ||
|
|
0596cce40d | ||
|
|
4d14f5568f | ||
|
|
01c0cb020e | ||
|
|
693d1ad775 | ||
|
|
2971845d40 | ||
|
|
3e17157ccf | ||
|
|
1f48a5e866 | ||
|
|
4f8aed4149 | ||
|
|
0c805d9b30 | ||
|
|
7fbeb8787f | ||
|
|
f6cb4519a0 | ||
|
|
c92b91221c | ||
|
|
1f0456a5af | ||
|
|
361a2282c7 | ||
|
|
f6d3dafda7 | ||
|
|
e0b6784eb5 | ||
|
|
f424abbc29 | ||
|
|
43d6145afd | ||
|
|
58938ef8ee | ||
|
|
b890f7b0fa | ||
|
|
77f57a656d | ||
|
|
db06770a6b | ||
|
|
6c76fdaa79 | ||
|
|
ab9f13b36f | ||
|
|
5ee2fa911a | ||
|
|
55d57ddba2 | ||
|
|
667414bd92 | ||
|
|
6868c24501 | ||
|
|
d4cd9b674c | ||
|
|
cfed174e9b | ||
|
|
de8b47839c | ||
|
|
1c5ef576f4 | ||
|
|
eb41b076ed | ||
|
|
a9bf89d8b2 | ||
|
|
e4c0716951 | ||
|
|
178c6593dc | ||
|
|
8b990e5658 | ||
|
|
0ce7f11445 | ||
|
|
067be10474 | ||
|
|
373a44d431 | ||
|
|
98e1dd7f6b | ||
|
|
5c0c22988e | ||
|
|
9e6d46c17e | ||
|
|
f765696834 | ||
|
|
26ee83aaa7 | ||
|
|
29320bdab5 | ||
|
|
031fdc389a | ||
|
|
012c77ec81 | ||
|
|
4be4263480 |
62 changed files with 5096 additions and 2439 deletions
28
Cargo.lock
generated
28
Cargo.lock
generated
|
|
@ -866,7 +866,7 @@ checksum = "fe6d2e5af09e8c8ad56c969f2157a3d4238cebc7c55f0a517728c38f7b200f81"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"serde",
|
"serde",
|
||||||
"termcolor",
|
"termcolor",
|
||||||
"unicode-width 0.1.14",
|
"unicode-width 0.2.2",
|
||||||
]
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
|
|
@ -989,7 +989,6 @@ dependencies = [
|
||||||
"anyhow",
|
"anyhow",
|
||||||
"bitflags 2.11.0",
|
"bitflags 2.11.0",
|
||||||
"calloop",
|
"calloop",
|
||||||
"cgmath",
|
|
||||||
"clap_lex",
|
"clap_lex",
|
||||||
"cosmic-comp-config",
|
"cosmic-comp-config",
|
||||||
"cosmic-config",
|
"cosmic-config",
|
||||||
|
|
@ -1001,14 +1000,16 @@ dependencies = [
|
||||||
"egui_plot",
|
"egui_plot",
|
||||||
"futures-executor",
|
"futures-executor",
|
||||||
"futures-util",
|
"futures-util",
|
||||||
|
"glam 0.33.2",
|
||||||
"i18n-embed",
|
"i18n-embed",
|
||||||
"i18n-embed-fl",
|
"i18n-embed-fl",
|
||||||
|
"iced_graphics",
|
||||||
"iced_tiny_skia",
|
"iced_tiny_skia",
|
||||||
"id_tree",
|
"id_tree",
|
||||||
"indexmap 2.13.0",
|
"indexmap 2.13.0",
|
||||||
"jiff",
|
"jiff",
|
||||||
"keyframe",
|
"keyframe",
|
||||||
"libcosmic-yoda",
|
"libcosmic",
|
||||||
"libdisplay-info",
|
"libdisplay-info",
|
||||||
"libsystemd",
|
"libsystemd",
|
||||||
"log-panics",
|
"log-panics",
|
||||||
|
|
@ -1025,6 +1026,7 @@ dependencies = [
|
||||||
"sanitize-filename",
|
"sanitize-filename",
|
||||||
"serde",
|
"serde",
|
||||||
"serde_json",
|
"serde_json",
|
||||||
|
"smallvec",
|
||||||
"smithay",
|
"smithay",
|
||||||
"smithay-egui",
|
"smithay-egui",
|
||||||
"thiserror 2.0.18",
|
"thiserror 2.0.18",
|
||||||
|
|
@ -2160,6 +2162,12 @@ version = "0.25.0"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "151665d9be52f9bb40fc7966565d39666f2d1e69233571b71b87791c7e0528b3"
|
checksum = "151665d9be52f9bb40fc7966565d39666f2d1e69233571b71b87791c7e0528b3"
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "glam"
|
||||||
|
version = "0.33.2"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "7f22fb22f065b308be0d8724e3706c7fa3fc2a6c7d6899df4cad7860e7a75436"
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "glow"
|
name = "glow"
|
||||||
version = "0.16.0"
|
version = "0.16.0"
|
||||||
|
|
@ -2474,7 +2482,7 @@ dependencies = [
|
||||||
"bytes",
|
"bytes",
|
||||||
"cosmic-client-toolkit",
|
"cosmic-client-toolkit",
|
||||||
"dnd",
|
"dnd",
|
||||||
"glam",
|
"glam 0.25.0",
|
||||||
"lilt",
|
"lilt",
|
||||||
"log",
|
"log",
|
||||||
"mime 0.1.0",
|
"mime 0.1.0",
|
||||||
|
|
@ -2590,7 +2598,7 @@ dependencies = [
|
||||||
"cosmic-client-toolkit",
|
"cosmic-client-toolkit",
|
||||||
"cryoglyph",
|
"cryoglyph",
|
||||||
"futures",
|
"futures",
|
||||||
"glam",
|
"glam 0.25.0",
|
||||||
"guillotiere",
|
"guillotiere",
|
||||||
"iced_debug",
|
"iced_debug",
|
||||||
"iced_graphics",
|
"iced_graphics",
|
||||||
|
|
@ -3131,7 +3139,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "68ab91017fe16c622486840e4c83c9a37afeff978bd239b5293d61ece587de66"
|
checksum = "68ab91017fe16c622486840e4c83c9a37afeff978bd239b5293d61ece587de66"
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "libcosmic-yoda"
|
name = "libcosmic"
|
||||||
version = "0.1.0-yoda.2"
|
version = "0.1.0-yoda.2"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"apply",
|
"apply",
|
||||||
|
|
@ -5249,14 +5257,14 @@ dependencies = [
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "smallvec"
|
name = "smallvec"
|
||||||
version = "1.15.1"
|
version = "1.15.2"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "67b1b7a3b5fe4f1376887184045fcf45c69e92af734b7aaddc05fb777b6fbd03"
|
checksum = "8ed6a63f02c8539c91a8685a86f4099661ba3da017932f6ebbea6de3f0fa7c90"
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "smithay"
|
name = "smithay"
|
||||||
version = "0.7.0"
|
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 = [
|
dependencies = [
|
||||||
"aliasable",
|
"aliasable",
|
||||||
"appendlist",
|
"appendlist",
|
||||||
|
|
@ -5265,7 +5273,6 @@ dependencies = [
|
||||||
"bitflags 2.11.0",
|
"bitflags 2.11.0",
|
||||||
"calloop",
|
"calloop",
|
||||||
"cc",
|
"cc",
|
||||||
"cgmath",
|
|
||||||
"cursor-icon",
|
"cursor-icon",
|
||||||
"downcast-rs",
|
"downcast-rs",
|
||||||
"drm",
|
"drm",
|
||||||
|
|
@ -5275,6 +5282,7 @@ dependencies = [
|
||||||
"errno",
|
"errno",
|
||||||
"gbm",
|
"gbm",
|
||||||
"gl_generator",
|
"gl_generator",
|
||||||
|
"glam 0.33.2",
|
||||||
"glow",
|
"glow",
|
||||||
"indexmap 2.13.0",
|
"indexmap 2.13.0",
|
||||||
"input",
|
"input",
|
||||||
|
|
|
||||||
20
Cargo.toml
20
Cargo.toml
|
|
@ -4,7 +4,7 @@ edition = "2024"
|
||||||
license = "GPL-3.0-only"
|
license = "GPL-3.0-only"
|
||||||
name = "cosmic-comp"
|
name = "cosmic-comp"
|
||||||
version = "1.0.0"
|
version = "1.0.0"
|
||||||
rust-version = "1.90"
|
rust-version = "1.93"
|
||||||
|
|
||||||
[workspace]
|
[workspace]
|
||||||
members = ["cosmic-comp-config"]
|
members = ["cosmic-comp-config"]
|
||||||
|
|
@ -40,9 +40,10 @@ i18n-embed = { version = "0.16", features = [
|
||||||
] }
|
] }
|
||||||
i18n-embed-fl = "0.10"
|
i18n-embed-fl = "0.10"
|
||||||
iced_tiny_skia = { path = "../libcosmic/iced/tiny_skia" }
|
iced_tiny_skia = { path = "../libcosmic/iced/tiny_skia" }
|
||||||
|
iced_graphics = { path = "../libcosmic/iced/graphics", features = ["geometry"] }
|
||||||
indexmap = "2.13"
|
indexmap = "2.13"
|
||||||
keyframe = "1.1.1"
|
keyframe = "1.1.1"
|
||||||
cosmic = { package = "libcosmic-yoda", path = "../libcosmic", default-features = false, features = [
|
cosmic = { package = "libcosmic", path = "../libcosmic", default-features = false, features = [
|
||||||
"tokio",
|
"tokio",
|
||||||
"wayland",
|
"wayland",
|
||||||
"multi-window",
|
"multi-window",
|
||||||
|
|
@ -87,7 +88,8 @@ parking_lot = "0.12.5"
|
||||||
logind-zbus = { version = "5.3.2", optional = true }
|
logind-zbus = { version = "5.3.2", optional = true }
|
||||||
futures-executor = { version = "0.3.32" }
|
futures-executor = { version = "0.3.32" }
|
||||||
futures-util = "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]
|
[dependencies.id_tree]
|
||||||
branch = "feature/copy_clone"
|
branch = "feature/copy_clone"
|
||||||
|
|
@ -143,7 +145,7 @@ inherits = "release"
|
||||||
[profile.release]
|
[profile.release]
|
||||||
lto = "fat"
|
lto = "fat"
|
||||||
|
|
||||||
[patch.'https://github.com/pop-os/libcosmic']
|
[patch."https://github.com/pop-os/libcosmic"]
|
||||||
cosmic-config = { path = "../libcosmic/cosmic-config" }
|
cosmic-config = { path = "../libcosmic/cosmic-config" }
|
||||||
cosmic-theme = { path = "../libcosmic/cosmic-theme" }
|
cosmic-theme = { path = "../libcosmic/cosmic-theme" }
|
||||||
iced = { path = "../libcosmic/iced" }
|
iced = { path = "../libcosmic/iced" }
|
||||||
|
|
@ -158,21 +160,21 @@ iced_wgpu = { path = "../libcosmic/iced/wgpu" }
|
||||||
iced_widget = { path = "../libcosmic/iced/widget" }
|
iced_widget = { path = "../libcosmic/iced/widget" }
|
||||||
iced_winit = { path = "../libcosmic/iced/winit" }
|
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-protocols = { path = "../cosmic-protocols" }
|
||||||
cosmic-client-toolkit = { path = "../cosmic-protocols/client-toolkit" }
|
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-config = { path = "../cosmic-settings-daemon/config" }
|
||||||
cosmic-settings-daemon-config = { path = "../cosmic-settings-daemon/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']
|
[patch."https://github.com/pop-os/cosmic-text.git"]
|
||||||
cosmic-text = { path = "../cosmic-text" }
|
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" }
|
window_clipboard = { path = "../window_clipboard" }
|
||||||
dnd = { path = "../window_clipboard/dnd" }
|
dnd = { path = "../window_clipboard/dnd" }
|
||||||
mime = { path = "../window_clipboard/mime" }
|
mime = { path = "../window_clipboard/mime" }
|
||||||
|
|
||||||
[patch.crates-io]
|
[patch.crates-io]
|
||||||
smithay = { git = "https://github.com/smithay/smithay.git", rev = "8eb4076" }
|
smithay = { git = "https://github.com/smithay/smithay.git", rev = "1ed69cb" }
|
||||||
|
|
|
||||||
|
|
@ -23,7 +23,7 @@ struct Circle {
|
||||||
color: Color,
|
color: Color,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl<Message, Theme, Renderer: cosmic::iced_renderer::geometry::Renderer>
|
impl<Message, Theme, Renderer: iced_graphics::geometry::Renderer>
|
||||||
canvas::Program<Message, Theme, Renderer> for Circle
|
canvas::Program<Message, Theme, Renderer> for Circle
|
||||||
{
|
{
|
||||||
type State = ();
|
type State = ();
|
||||||
|
|
@ -50,17 +50,17 @@ impl<Internal, Message: std::clone::Clone + 'static, Lower: Decorations<Internal
|
||||||
{
|
{
|
||||||
fn view(&self, window: &Internal) -> cosmic::Element<'_, Message> {
|
fn view(&self, window: &Internal) -> cosmic::Element<'_, Message> {
|
||||||
let orig = self.lower.view(window);
|
let orig = self.lower.view(window);
|
||||||
widget::row()
|
widget::row([
|
||||||
.push(
|
widget::column([canvas(Circle {
|
||||||
widget::column()
|
radius: (self.height as f32 / 2.) * 0.8,
|
||||||
.push(canvas(Circle {
|
color: Color::from_rgba(1.0, 0.0, 0.0, 1.0),
|
||||||
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)
|
||||||
.width(self.height as f32),
|
.into(),
|
||||||
)
|
orig,
|
||||||
.push(orig)
|
])
|
||||||
.into()
|
.into()
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -1,7 +1,7 @@
|
||||||
a11y-zoom-move-continuously = Ansicht bewegt sich kontinuierlich mit dem Cursor
|
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-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-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
|
grow-window = Vergrößern
|
||||||
shrink-window = Verkleinern
|
shrink-window = Verkleinern
|
||||||
swap-windows = Fenster tauschen
|
swap-windows = Fenster tauschen
|
||||||
|
|
|
||||||
|
|
@ -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 = <unset>
|
||||||
|
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
|
||||||
|
|
@ -943,6 +943,7 @@ impl LockedDevice<'_> {
|
||||||
output,
|
output,
|
||||||
CursorMode::All,
|
CursorMode::All,
|
||||||
None,
|
None,
|
||||||
|
None,
|
||||||
)
|
)
|
||||||
.with_context(|| "Failed to render outputs")?,
|
.with_context(|| "Failed to render outputs")?,
|
||||||
None => Vec::new(),
|
None => Vec::new(),
|
||||||
|
|
|
||||||
|
|
@ -978,6 +978,7 @@ impl KmsGuard<'_> {
|
||||||
output,
|
output,
|
||||||
CursorMode::All,
|
CursorMode::All,
|
||||||
None,
|
None,
|
||||||
|
None,
|
||||||
)
|
)
|
||||||
.with_context(|| "Failed to render outputs")?;
|
.with_context(|| "Failed to render outputs")?;
|
||||||
|
|
||||||
|
|
@ -1088,6 +1089,7 @@ impl KmsGuard<'_> {
|
||||||
output,
|
output,
|
||||||
CursorMode::All,
|
CursorMode::All,
|
||||||
None,
|
None,
|
||||||
|
None,
|
||||||
)
|
)
|
||||||
.with_context(|| "Failed to render outputs")?;
|
.with_context(|| "Failed to render outputs")?;
|
||||||
|
|
||||||
|
|
@ -1132,6 +1134,7 @@ impl KmsGuard<'_> {
|
||||||
output,
|
output,
|
||||||
CursorMode::All,
|
CursorMode::All,
|
||||||
None,
|
None,
|
||||||
|
None,
|
||||||
)
|
)
|
||||||
.with_context(|| "Failed to render outputs")?;
|
.with_context(|| "Failed to render outputs")?;
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -12,10 +12,9 @@ use smithay::{
|
||||||
},
|
},
|
||||||
drm::{CreateDrmNodeError, DrmNode},
|
drm::{CreateDrmNodeError, DrmNode},
|
||||||
renderer::{
|
renderer::{
|
||||||
RendererSuper,
|
|
||||||
gles::{GlesError, GlesRenderer, ffi},
|
gles::{GlesError, GlesRenderer, ffi},
|
||||||
glow::GlowRenderer,
|
glow::GlowRenderer,
|
||||||
multigpu::{ApiDevice, Error as MultiError, GraphicsApi},
|
multigpu::{ApiDevice, GraphicsApi},
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
reexports::drm::control::Device,
|
reexports::drm::control::Device,
|
||||||
|
|
@ -32,8 +31,6 @@ use std::{
|
||||||
sync::atomic::{AtomicBool, Ordering},
|
sync::atomic::{AtomicBool, Ordering},
|
||||||
};
|
};
|
||||||
|
|
||||||
use crate::backend::render::element::FromGlesError;
|
|
||||||
|
|
||||||
/// Errors raised by the [`GbmGlesBackend`]
|
/// Errors raised by the [`GbmGlesBackend`]
|
||||||
#[derive(Debug, thiserror::Error)]
|
#[derive(Debug, thiserror::Error)]
|
||||||
pub enum Error {
|
pub enum Error {
|
||||||
|
|
@ -213,15 +210,3 @@ impl ApiDevice for GbmGlowDevice {
|
||||||
!self.intel_export_quirk
|
!self.intel_export_quirk
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
impl<T: GraphicsApi, A: AsFd + Device + Clone + 'static> FromGlesError
|
|
||||||
for MultiError<GbmGlowBackend<A>, T>
|
|
||||||
where
|
|
||||||
T::Error: 'static,
|
|
||||||
<<T::Device as ApiDevice>::Renderer as RendererSuper>::Error: 'static,
|
|
||||||
{
|
|
||||||
#[inline]
|
|
||||||
fn from_gles_error(err: GlesError) -> MultiError<GbmGlowBackend<A>, T> {
|
|
||||||
MultiError::Render(err)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
|
||||||
|
|
@ -36,7 +36,7 @@ impl Common {
|
||||||
) -> Result<Socket> {
|
) -> Result<Socket> {
|
||||||
let socket_name = format!(
|
let socket_name = format!(
|
||||||
"{}-{}",
|
"{}-{}",
|
||||||
&self.socket.to_string_lossy(),
|
self.socket.to_string_lossy(),
|
||||||
render_node
|
render_node
|
||||||
.dev_path()
|
.dev_path()
|
||||||
.unwrap()
|
.unwrap()
|
||||||
|
|
|
||||||
|
|
@ -1068,6 +1068,7 @@ impl SurfaceThreadState {
|
||||||
None,
|
None,
|
||||||
#[cfg(feature = "debug")]
|
#[cfg(feature = "debug")]
|
||||||
Some((&self.egui, &self.timings)),
|
Some((&self.egui, &self.timings)),
|
||||||
|
Some(self.target_node),
|
||||||
)
|
)
|
||||||
.map_err(|err| {
|
.map_err(|err| {
|
||||||
anyhow::format_err!("Failed to accumulate elements for rendering: {:?}", err)
|
anyhow::format_err!("Failed to accumulate elements for rendering: {:?}", err)
|
||||||
|
|
@ -1673,7 +1674,7 @@ fn send_screencopy_result<'a>(
|
||||||
pre_postprocess_data: &mut PrePostprocessData,
|
pre_postprocess_data: &mut PrePostprocessData,
|
||||||
tx: &std::sync::mpsc::Sender<PendingImageCopyData>,
|
tx: &std::sync::mpsc::Sender<PendingImageCopyData>,
|
||||||
frame_result: &RenderFrameResult<GbmBuffer, GbmFramebuffer, CosmicElement<GlMultiRenderer<'a>>>,
|
frame_result: &RenderFrameResult<GbmBuffer, GbmFramebuffer, CosmicElement<GlMultiRenderer<'a>>>,
|
||||||
elements: &[CosmicElement<GlMultiRenderer>],
|
elements: &[CosmicElement<GlMultiRenderer<'a>>],
|
||||||
(session, frame, res): (
|
(session, frame, res): (
|
||||||
&ScreencopySessionRef,
|
&ScreencopySessionRef,
|
||||||
ScreencopyFrame,
|
ScreencopyFrame,
|
||||||
|
|
@ -1854,6 +1855,8 @@ fn send_screencopy_result<'a>(
|
||||||
transform,
|
transform,
|
||||||
damage.as_deref(),
|
damage.as_deref(),
|
||||||
sync,
|
sync,
|
||||||
|
// Don't reference `Buffer`s since we blit from framebuffer/postprocess buffer
|
||||||
|
vec![],
|
||||||
)? {
|
)? {
|
||||||
if frame_result.is_empty {
|
if frame_result.is_empty {
|
||||||
data.frame
|
data.frame
|
||||||
|
|
|
||||||
|
|
@ -1,6 +1,13 @@
|
||||||
// SPDX-License-Identifier: GPL-3.0-only
|
// 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, push_render_elements_from_surface_tree},
|
||||||
|
},
|
||||||
|
utils::prelude::*,
|
||||||
|
wayland::handlers::compositor::FRAME_TIME_FILTER,
|
||||||
|
};
|
||||||
use smithay::{
|
use smithay::{
|
||||||
backend::{
|
backend::{
|
||||||
allocator::Fourcc,
|
allocator::Fourcc,
|
||||||
|
|
@ -9,10 +16,10 @@ use smithay::{
|
||||||
element::{
|
element::{
|
||||||
Kind,
|
Kind,
|
||||||
memory::{MemoryRenderBuffer, MemoryRenderBufferRenderElement},
|
memory::{MemoryRenderBuffer, MemoryRenderBufferRenderElement},
|
||||||
surface::{WaylandSurfaceRenderElement, render_elements_from_surface_tree},
|
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
|
desktop::utils::bbox_from_surface_tree,
|
||||||
input::{
|
input::{
|
||||||
Seat,
|
Seat,
|
||||||
pointer::{CursorIcon, CursorImageAttributes, CursorImageStatus},
|
pointer::{CursorIcon, CursorImageAttributes, CursorImageStatus},
|
||||||
|
|
@ -114,24 +121,63 @@ enum Error {
|
||||||
NoDefaultCursor,
|
NoDefaultCursor,
|
||||||
#[error("Error opening xcursor file: {0}")]
|
#[error("Error opening xcursor file: {0}")]
|
||||||
File(#[from] std::io::Error),
|
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<Vec<Image>, Error> {
|
fn load_icon(theme: &CursorTheme, shape: CursorIcon) -> Result<Vec<Image>, Error> {
|
||||||
let icon_path = theme
|
let shape_name = shape.to_string();
|
||||||
.load_icon(&shape.to_string())
|
|
||||||
.ok_or(Error::NoDefaultCursor)?;
|
for name in cursor_aliases(&shape_name)
|
||||||
let mut cursor_file = std::fs::File::open(&icon_path)?;
|
.iter()
|
||||||
let mut cursor_data = Vec::new();
|
.copied()
|
||||||
cursor_file.read_to_end(&mut cursor_data)?;
|
.chain(std::iter::once(shape_name.as_str()))
|
||||||
parse_xcursor(&cursor_data).ok_or(Error::Parse)
|
{
|
||||||
|
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! {
|
render_elements! {
|
||||||
pub CursorRenderElement<R> where R: ImportAll + ImportMem;
|
pub CursorRenderElement<R> where R: ImportAll + ImportMem + AsGlowRenderer, R::TextureId: Send;
|
||||||
Static=MemoryRenderBufferRenderElement<R>,
|
Static=MemoryRenderBufferRenderElement<R>,
|
||||||
Surface=WaylandSurfaceRenderElement<R>,
|
Surface=SurfaceRenderElement<R>,
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn draw_surface_cursor<R>(
|
pub fn draw_surface_cursor<R>(
|
||||||
|
|
@ -139,9 +185,10 @@ pub fn draw_surface_cursor<R>(
|
||||||
surface: &wl_surface::WlSurface,
|
surface: &wl_surface::WlSurface,
|
||||||
location: Point<f64, Logical>,
|
location: Point<f64, Logical>,
|
||||||
scale: impl Into<Scale<f64>>,
|
scale: impl Into<Scale<f64>>,
|
||||||
) -> Vec<(CursorRenderElement<R>, Point<i32, Physical>)>
|
blur_strength: usize,
|
||||||
where
|
push: &mut dyn FnMut(CursorRenderElement<R>, Point<i32, Physical>),
|
||||||
R: Renderer + ImportAll,
|
) where
|
||||||
|
R: Renderer + ImportAll + AsGlowRenderer,
|
||||||
R::TextureId: Clone + 'static,
|
R::TextureId: Clone + 'static,
|
||||||
{
|
{
|
||||||
let scale = scale.into();
|
let scale = scale.into();
|
||||||
|
|
@ -156,17 +203,21 @@ where
|
||||||
.to_physical_precise_round(scale)
|
.to_physical_precise_round(scale)
|
||||||
});
|
});
|
||||||
|
|
||||||
render_elements_from_surface_tree(
|
push_render_elements_from_surface_tree(
|
||||||
renderer,
|
renderer,
|
||||||
surface,
|
surface,
|
||||||
location.to_physical(scale).to_i32_round(),
|
location.to_physical(scale).to_i32_round(),
|
||||||
|
bbox_from_surface_tree(surface, location.to_i32_round()).to_f64(),
|
||||||
scale,
|
scale,
|
||||||
1.0,
|
1.0,
|
||||||
|
false,
|
||||||
|
[0; 4],
|
||||||
|
None,
|
||||||
|
blur_strength,
|
||||||
Kind::Cursor,
|
Kind::Cursor,
|
||||||
)
|
&mut |elem| push(elem.into(), h),
|
||||||
.into_iter()
|
None,
|
||||||
.map(|elem| (elem, h))
|
);
|
||||||
.collect()
|
|
||||||
}
|
}
|
||||||
|
|
||||||
#[profiling::function]
|
#[profiling::function]
|
||||||
|
|
@ -175,9 +226,10 @@ pub fn draw_dnd_icon<R>(
|
||||||
surface: &wl_surface::WlSurface,
|
surface: &wl_surface::WlSurface,
|
||||||
location: Point<f64, Logical>,
|
location: Point<f64, Logical>,
|
||||||
scale: impl Into<Scale<f64>>,
|
scale: impl Into<Scale<f64>>,
|
||||||
) -> Vec<WaylandSurfaceRenderElement<R>>
|
blur_strength: usize,
|
||||||
where
|
push: &mut dyn FnMut(SurfaceRenderElement<R>),
|
||||||
R: Renderer + ImportAll,
|
) where
|
||||||
|
R: Renderer + ImportAll + AsGlowRenderer,
|
||||||
R::TextureId: Clone + 'static,
|
R::TextureId: Clone + 'static,
|
||||||
{
|
{
|
||||||
if get_role(surface) != Some("dnd_icon") {
|
if get_role(surface) != Some("dnd_icon") {
|
||||||
|
|
@ -187,14 +239,21 @@ where
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
let scale = scale.into();
|
let scale = scale.into();
|
||||||
render_elements_from_surface_tree(
|
push_render_elements_from_surface_tree(
|
||||||
renderer,
|
renderer,
|
||||||
surface,
|
surface,
|
||||||
location.to_physical(scale).to_i32_round(),
|
location.to_physical(scale).to_i32_round(),
|
||||||
|
bbox_from_surface_tree(surface, location.to_i32_round()).to_f64(),
|
||||||
scale,
|
scale,
|
||||||
1.0,
|
1.0,
|
||||||
|
false,
|
||||||
|
[0; 4],
|
||||||
|
None,
|
||||||
|
blur_strength,
|
||||||
FRAME_TIME_FILTER,
|
FRAME_TIME_FILTER,
|
||||||
)
|
push,
|
||||||
|
None,
|
||||||
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
pub type CursorState = Mutex<CursorStateInner>;
|
pub type CursorState = Mutex<CursorStateInner>;
|
||||||
|
|
@ -277,10 +336,11 @@ pub fn draw_cursor<R>(
|
||||||
scale: Scale<f64>,
|
scale: Scale<f64>,
|
||||||
buffer_scale: f64,
|
buffer_scale: f64,
|
||||||
time: Time<Monotonic>,
|
time: Time<Monotonic>,
|
||||||
|
blur_strength: usize,
|
||||||
draw_default: bool,
|
draw_default: bool,
|
||||||
) -> Vec<(CursorRenderElement<R>, Point<i32, Physical>)>
|
push: &mut dyn FnMut(CursorRenderElement<R>, Point<i32, Physical>),
|
||||||
where
|
) where
|
||||||
R: Renderer + ImportMem + ImportAll,
|
R: Renderer + ImportMem + ImportAll + AsGlowRenderer,
|
||||||
R::TextureId: Send + Clone + 'static,
|
R::TextureId: Send + Clone + 'static,
|
||||||
{
|
{
|
||||||
// draw the cursor as relevant
|
// draw the cursor as relevant
|
||||||
|
|
@ -291,7 +351,7 @@ where
|
||||||
let state = &mut *state_ref;
|
let state = &mut *state_ref;
|
||||||
|
|
||||||
if state.hidden {
|
if state.hidden {
|
||||||
return Vec::new();
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
let named_cursor = state.current_cursor.or(match cursor_status {
|
let named_cursor = state.current_cursor.or(match cursor_status {
|
||||||
|
|
@ -300,7 +360,7 @@ where
|
||||||
});
|
});
|
||||||
if let Some(current_cursor) = named_cursor {
|
if let Some(current_cursor) = named_cursor {
|
||||||
if !draw_default && current_cursor == CursorIcon::Default {
|
if !draw_default && current_cursor == CursorIcon::Default {
|
||||||
return Vec::new();
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
let integer_scale = (scale.x.max(scale.y) * buffer_scale).ceil() as u32;
|
let integer_scale = (scale.x.max(scale.y) * buffer_scale).ceil() as u32;
|
||||||
|
|
@ -337,7 +397,7 @@ where
|
||||||
);
|
);
|
||||||
state.current_image = Some(frame);
|
state.current_image = Some(frame);
|
||||||
|
|
||||||
return vec![(
|
push(
|
||||||
CursorRenderElement::Static(
|
CursorRenderElement::Static(
|
||||||
MemoryRenderBufferRenderElement::from_buffer(
|
MemoryRenderBufferRenderElement::from_buffer(
|
||||||
renderer,
|
renderer,
|
||||||
|
|
@ -351,11 +411,9 @@ where
|
||||||
.expect("Failed to import cursor bitmap"),
|
.expect("Failed to import cursor bitmap"),
|
||||||
),
|
),
|
||||||
hotspot.to_physical_precise_round(scale),
|
hotspot.to_physical_precise_round(scale),
|
||||||
)];
|
);
|
||||||
} else if let CursorImageStatus::Surface(ref wl_surface) = cursor_status {
|
} else if let CursorImageStatus::Surface(ref wl_surface) = cursor_status {
|
||||||
return draw_surface_cursor(renderer, wl_surface, location, scale);
|
draw_surface_cursor(renderer, wl_surface, location, scale, blur_strength, push);
|
||||||
} else {
|
|
||||||
Vec::new()
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -1,20 +1,27 @@
|
||||||
use crate::shell::{CosmicMappedRenderElement, WorkspaceRenderElement};
|
use crate::{
|
||||||
|
backend::{
|
||||||
|
kms::render::gles::GbmGlowBackend,
|
||||||
|
render::{GlMultiError, wayland::SurfaceRenderElement},
|
||||||
|
},
|
||||||
|
shell::{CosmicMappedRenderElement, WorkspaceRenderElement},
|
||||||
|
utils::iced::IcedRenderElement,
|
||||||
|
};
|
||||||
|
|
||||||
#[cfg(feature = "debug")]
|
#[cfg(feature = "debug")]
|
||||||
use smithay::backend::renderer::element::texture::TextureRenderElement;
|
use smithay::backend::renderer::element::texture::TextureRenderElement;
|
||||||
use smithay::{
|
use smithay::{
|
||||||
backend::{
|
backend::{
|
||||||
allocator::dmabuf::Dmabuf,
|
allocator::dmabuf::Dmabuf,
|
||||||
|
drm::DrmDeviceFd,
|
||||||
renderer::{
|
renderer::{
|
||||||
Bind, Blit, ExportMem, ImportAll, ImportMem, Offscreen, Renderer,
|
Bind, Blit, ContextId, ExportMem, ImportAll, ImportMem, Offscreen, Renderer,
|
||||||
element::{
|
element::{
|
||||||
Element, Id, Kind, RenderElement, UnderlyingStorage,
|
Element, Id, Kind, RenderElement, UnderlyingStorage,
|
||||||
memory::MemoryRenderBufferRenderElement,
|
|
||||||
surface::WaylandSurfaceRenderElement,
|
|
||||||
utils::{CropRenderElement, Relocate, RelocateRenderElement, RescaleRenderElement},
|
utils::{CropRenderElement, Relocate, RelocateRenderElement, RescaleRenderElement},
|
||||||
},
|
},
|
||||||
gles::{GlesError, GlesRenderbuffer, GlesTexture, element::TextureShaderElement},
|
gles::{GlesError, GlesRenderbuffer, GlesTexture, element::TextureShaderElement},
|
||||||
glow::{GlowFrame, GlowRenderer},
|
glow::{GlowFrame, GlowRenderer},
|
||||||
|
multigpu::MultiTexture,
|
||||||
utils::{CommitCounter, DamageSet, OpaqueRegions},
|
utils::{CommitCounter, DamageSet, OpaqueRegions},
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
|
|
@ -28,19 +35,20 @@ use super::{GlMultiRenderer, cursor::CursorRenderElement};
|
||||||
pub enum CosmicElement<R>
|
pub enum CosmicElement<R>
|
||||||
where
|
where
|
||||||
R: AsGlowRenderer,
|
R: AsGlowRenderer,
|
||||||
R::TextureId: 'static,
|
R::TextureId: Send + 'static,
|
||||||
CosmicMappedRenderElement<R>: RenderElement<R>,
|
CosmicMappedRenderElement<R>: RenderElement<R>,
|
||||||
{
|
{
|
||||||
Workspace(
|
Workspace(
|
||||||
RelocateRenderElement<CropRenderElement<RescaleRenderElement<WorkspaceRenderElement<R>>>>,
|
RelocateRenderElement<CropRenderElement<RescaleRenderElement<WorkspaceRenderElement<R>>>>,
|
||||||
),
|
),
|
||||||
Cursor(RescaleRenderElement<RelocateRenderElement<CursorRenderElement<R>>>),
|
Cursor(RescaleRenderElement<RelocateRenderElement<CursorRenderElement<R>>>),
|
||||||
Dnd(WaylandSurfaceRenderElement<R>),
|
Dnd(SurfaceRenderElement<R>),
|
||||||
MoveGrab(RescaleRenderElement<CosmicMappedRenderElement<R>>),
|
MoveGrab(RescaleRenderElement<CosmicMappedRenderElement<R>>),
|
||||||
Postprocess(
|
Postprocess(
|
||||||
CropRenderElement<RelocateRenderElement<RescaleRenderElement<TextureShaderElement>>>,
|
CropRenderElement<RelocateRenderElement<RescaleRenderElement<TextureShaderElement>>>,
|
||||||
),
|
),
|
||||||
Zoom(MemoryRenderBufferRenderElement<R>),
|
Zoom(IcedRenderElement<R>),
|
||||||
|
Damage(DamageElement),
|
||||||
#[cfg(feature = "debug")]
|
#[cfg(feature = "debug")]
|
||||||
Egui(TextureRenderElement<GlesTexture>),
|
Egui(TextureRenderElement<GlesTexture>),
|
||||||
}
|
}
|
||||||
|
|
@ -48,7 +56,7 @@ where
|
||||||
impl<R> Element for CosmicElement<R>
|
impl<R> Element for CosmicElement<R>
|
||||||
where
|
where
|
||||||
R: AsGlowRenderer,
|
R: AsGlowRenderer,
|
||||||
R::TextureId: 'static,
|
R::TextureId: Send + 'static,
|
||||||
CosmicMappedRenderElement<R>: RenderElement<R>,
|
CosmicMappedRenderElement<R>: RenderElement<R>,
|
||||||
{
|
{
|
||||||
fn id(&self) -> &Id {
|
fn id(&self) -> &Id {
|
||||||
|
|
@ -59,6 +67,7 @@ where
|
||||||
CosmicElement::MoveGrab(elem) => elem.id(),
|
CosmicElement::MoveGrab(elem) => elem.id(),
|
||||||
CosmicElement::Postprocess(elem) => elem.id(),
|
CosmicElement::Postprocess(elem) => elem.id(),
|
||||||
CosmicElement::Zoom(elem) => elem.id(),
|
CosmicElement::Zoom(elem) => elem.id(),
|
||||||
|
CosmicElement::Damage(elem) => elem.id(),
|
||||||
#[cfg(feature = "debug")]
|
#[cfg(feature = "debug")]
|
||||||
CosmicElement::Egui(elem) => elem.id(),
|
CosmicElement::Egui(elem) => elem.id(),
|
||||||
}
|
}
|
||||||
|
|
@ -72,6 +81,7 @@ where
|
||||||
CosmicElement::MoveGrab(elem) => elem.current_commit(),
|
CosmicElement::MoveGrab(elem) => elem.current_commit(),
|
||||||
CosmicElement::Postprocess(elem) => elem.current_commit(),
|
CosmicElement::Postprocess(elem) => elem.current_commit(),
|
||||||
CosmicElement::Zoom(elem) => elem.current_commit(),
|
CosmicElement::Zoom(elem) => elem.current_commit(),
|
||||||
|
CosmicElement::Damage(elem) => elem.current_commit(),
|
||||||
#[cfg(feature = "debug")]
|
#[cfg(feature = "debug")]
|
||||||
CosmicElement::Egui(elem) => elem.current_commit(),
|
CosmicElement::Egui(elem) => elem.current_commit(),
|
||||||
}
|
}
|
||||||
|
|
@ -85,6 +95,7 @@ where
|
||||||
CosmicElement::MoveGrab(elem) => elem.src(),
|
CosmicElement::MoveGrab(elem) => elem.src(),
|
||||||
CosmicElement::Postprocess(elem) => elem.src(),
|
CosmicElement::Postprocess(elem) => elem.src(),
|
||||||
CosmicElement::Zoom(elem) => elem.src(),
|
CosmicElement::Zoom(elem) => elem.src(),
|
||||||
|
CosmicElement::Damage(elem) => elem.src(),
|
||||||
#[cfg(feature = "debug")]
|
#[cfg(feature = "debug")]
|
||||||
CosmicElement::Egui(elem) => elem.src(),
|
CosmicElement::Egui(elem) => elem.src(),
|
||||||
}
|
}
|
||||||
|
|
@ -98,6 +109,7 @@ where
|
||||||
CosmicElement::MoveGrab(elem) => elem.geometry(scale),
|
CosmicElement::MoveGrab(elem) => elem.geometry(scale),
|
||||||
CosmicElement::Postprocess(elem) => elem.geometry(scale),
|
CosmicElement::Postprocess(elem) => elem.geometry(scale),
|
||||||
CosmicElement::Zoom(elem) => elem.geometry(scale),
|
CosmicElement::Zoom(elem) => elem.geometry(scale),
|
||||||
|
CosmicElement::Damage(elem) => elem.geometry(scale),
|
||||||
#[cfg(feature = "debug")]
|
#[cfg(feature = "debug")]
|
||||||
CosmicElement::Egui(elem) => elem.geometry(scale),
|
CosmicElement::Egui(elem) => elem.geometry(scale),
|
||||||
}
|
}
|
||||||
|
|
@ -111,6 +123,7 @@ where
|
||||||
CosmicElement::MoveGrab(elem) => elem.location(scale),
|
CosmicElement::MoveGrab(elem) => elem.location(scale),
|
||||||
CosmicElement::Postprocess(elem) => elem.location(scale),
|
CosmicElement::Postprocess(elem) => elem.location(scale),
|
||||||
CosmicElement::Zoom(elem) => elem.location(scale),
|
CosmicElement::Zoom(elem) => elem.location(scale),
|
||||||
|
CosmicElement::Damage(elem) => elem.location(scale),
|
||||||
#[cfg(feature = "debug")]
|
#[cfg(feature = "debug")]
|
||||||
CosmicElement::Egui(elem) => elem.location(scale),
|
CosmicElement::Egui(elem) => elem.location(scale),
|
||||||
}
|
}
|
||||||
|
|
@ -124,6 +137,7 @@ where
|
||||||
CosmicElement::MoveGrab(elem) => elem.transform(),
|
CosmicElement::MoveGrab(elem) => elem.transform(),
|
||||||
CosmicElement::Postprocess(elem) => elem.transform(),
|
CosmicElement::Postprocess(elem) => elem.transform(),
|
||||||
CosmicElement::Zoom(elem) => elem.transform(),
|
CosmicElement::Zoom(elem) => elem.transform(),
|
||||||
|
CosmicElement::Damage(elem) => elem.transform(),
|
||||||
#[cfg(feature = "debug")]
|
#[cfg(feature = "debug")]
|
||||||
CosmicElement::Egui(elem) => elem.transform(),
|
CosmicElement::Egui(elem) => elem.transform(),
|
||||||
}
|
}
|
||||||
|
|
@ -141,6 +155,7 @@ where
|
||||||
CosmicElement::MoveGrab(elem) => elem.damage_since(scale, commit),
|
CosmicElement::MoveGrab(elem) => elem.damage_since(scale, commit),
|
||||||
CosmicElement::Postprocess(elem) => elem.damage_since(scale, commit),
|
CosmicElement::Postprocess(elem) => elem.damage_since(scale, commit),
|
||||||
CosmicElement::Zoom(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")]
|
#[cfg(feature = "debug")]
|
||||||
CosmicElement::Egui(elem) => elem.damage_since(scale, commit),
|
CosmicElement::Egui(elem) => elem.damage_since(scale, commit),
|
||||||
}
|
}
|
||||||
|
|
@ -154,6 +169,7 @@ where
|
||||||
CosmicElement::MoveGrab(elem) => elem.opaque_regions(scale),
|
CosmicElement::MoveGrab(elem) => elem.opaque_regions(scale),
|
||||||
CosmicElement::Postprocess(elem) => elem.opaque_regions(scale),
|
CosmicElement::Postprocess(elem) => elem.opaque_regions(scale),
|
||||||
CosmicElement::Zoom(elem) => elem.opaque_regions(scale),
|
CosmicElement::Zoom(elem) => elem.opaque_regions(scale),
|
||||||
|
CosmicElement::Damage(elem) => elem.opaque_regions(scale),
|
||||||
#[cfg(feature = "debug")]
|
#[cfg(feature = "debug")]
|
||||||
CosmicElement::Egui(elem) => elem.opaque_regions(scale),
|
CosmicElement::Egui(elem) => elem.opaque_regions(scale),
|
||||||
}
|
}
|
||||||
|
|
@ -167,6 +183,7 @@ where
|
||||||
CosmicElement::MoveGrab(elem) => elem.alpha(),
|
CosmicElement::MoveGrab(elem) => elem.alpha(),
|
||||||
CosmicElement::Postprocess(elem) => elem.alpha(),
|
CosmicElement::Postprocess(elem) => elem.alpha(),
|
||||||
CosmicElement::Zoom(elem) => elem.alpha(),
|
CosmicElement::Zoom(elem) => elem.alpha(),
|
||||||
|
CosmicElement::Damage(elem) => elem.alpha(),
|
||||||
#[cfg(feature = "debug")]
|
#[cfg(feature = "debug")]
|
||||||
CosmicElement::Egui(elem) => elem.alpha(),
|
CosmicElement::Egui(elem) => elem.alpha(),
|
||||||
}
|
}
|
||||||
|
|
@ -180,6 +197,7 @@ where
|
||||||
CosmicElement::MoveGrab(elem) => elem.kind(),
|
CosmicElement::MoveGrab(elem) => elem.kind(),
|
||||||
CosmicElement::Postprocess(elem) => elem.kind(),
|
CosmicElement::Postprocess(elem) => elem.kind(),
|
||||||
CosmicElement::Zoom(elem) => elem.kind(),
|
CosmicElement::Zoom(elem) => elem.kind(),
|
||||||
|
CosmicElement::Damage(elem) => elem.kind(),
|
||||||
#[cfg(feature = "debug")]
|
#[cfg(feature = "debug")]
|
||||||
CosmicElement::Egui(elem) => elem.kind(),
|
CosmicElement::Egui(elem) => elem.kind(),
|
||||||
}
|
}
|
||||||
|
|
@ -193,6 +211,7 @@ where
|
||||||
CosmicElement::MoveGrab(elem) => elem.is_framebuffer_effect(),
|
CosmicElement::MoveGrab(elem) => elem.is_framebuffer_effect(),
|
||||||
CosmicElement::Postprocess(elem) => elem.is_framebuffer_effect(),
|
CosmicElement::Postprocess(elem) => elem.is_framebuffer_effect(),
|
||||||
CosmicElement::Zoom(elem) => elem.is_framebuffer_effect(),
|
CosmicElement::Zoom(elem) => elem.is_framebuffer_effect(),
|
||||||
|
CosmicElement::Damage(elem) => elem.is_framebuffer_effect(),
|
||||||
#[cfg(feature = "debug")]
|
#[cfg(feature = "debug")]
|
||||||
CosmicElement::Egui(elem) => elem.is_framebuffer_effect(),
|
CosmicElement::Egui(elem) => elem.is_framebuffer_effect(),
|
||||||
}
|
}
|
||||||
|
|
@ -202,8 +221,7 @@ where
|
||||||
impl<R> RenderElement<R> for CosmicElement<R>
|
impl<R> RenderElement<R> for CosmicElement<R>
|
||||||
where
|
where
|
||||||
R: AsGlowRenderer,
|
R: AsGlowRenderer,
|
||||||
R::TextureId: 'static,
|
R::TextureId: Send + 'static,
|
||||||
R::Error: FromGlesError,
|
|
||||||
CosmicMappedRenderElement<R>: RenderElement<R>,
|
CosmicMappedRenderElement<R>: RenderElement<R>,
|
||||||
{
|
{
|
||||||
fn draw(
|
fn draw(
|
||||||
|
|
@ -237,9 +255,12 @@ where
|
||||||
opaque_regions,
|
opaque_regions,
|
||||||
cache,
|
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),
|
CosmicElement::Zoom(elem) => elem.draw(frame, src, dst, damage, opaque_regions, cache),
|
||||||
|
CosmicElement::Damage(elem) => {
|
||||||
|
RenderElement::<R>::draw(elem, frame, src, dst, damage, opaque_regions, cache)
|
||||||
|
}
|
||||||
#[cfg(feature = "debug")]
|
#[cfg(feature = "debug")]
|
||||||
CosmicElement::Egui(elem) => {
|
CosmicElement::Egui(elem) => {
|
||||||
let glow_frame = R::glow_frame_mut(frame);
|
let glow_frame = R::glow_frame_mut(frame);
|
||||||
|
|
@ -252,7 +273,7 @@ where
|
||||||
opaque_regions,
|
opaque_regions,
|
||||||
cache,
|
cache,
|
||||||
)
|
)
|
||||||
.map_err(FromGlesError::from_gles_error)
|
.map_err(R::from_gles_error)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
@ -268,6 +289,7 @@ where
|
||||||
elem.underlying_storage(glow_renderer)
|
elem.underlying_storage(glow_renderer)
|
||||||
}
|
}
|
||||||
CosmicElement::Zoom(elem) => elem.underlying_storage(renderer),
|
CosmicElement::Zoom(elem) => elem.underlying_storage(renderer),
|
||||||
|
CosmicElement::Damage(elem) => elem.underlying_storage(renderer),
|
||||||
#[cfg(feature = "debug")]
|
#[cfg(feature = "debug")]
|
||||||
CosmicElement::Egui(elem) => {
|
CosmicElement::Egui(elem) => {
|
||||||
let glow_renderer = renderer.glow_renderer_mut();
|
let glow_renderer = renderer.glow_renderer_mut();
|
||||||
|
|
@ -293,16 +315,19 @@ where
|
||||||
RenderElement::<GlowRenderer>::capture_framebuffer(
|
RenderElement::<GlowRenderer>::capture_framebuffer(
|
||||||
elem, glow_frame, src, dst, cache,
|
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),
|
CosmicElement::Zoom(elem) => elem.capture_framebuffer(frame, src, dst, cache),
|
||||||
|
CosmicElement::Damage(elem) => {
|
||||||
|
RenderElement::<R>::capture_framebuffer(elem, frame, src, dst, cache)
|
||||||
|
}
|
||||||
#[cfg(feature = "debug")]
|
#[cfg(feature = "debug")]
|
||||||
CosmicElement::Egui(elem) => {
|
CosmicElement::Egui(elem) => {
|
||||||
let glow_frame = R::glow_frame_mut(frame);
|
let glow_frame = R::glow_frame_mut(frame);
|
||||||
RenderElement::<GlowRenderer>::capture_framebuffer(
|
RenderElement::<GlowRenderer>::capture_framebuffer(
|
||||||
elem, glow_frame, src, dst, cache,
|
elem, glow_frame, src, dst, cache,
|
||||||
)
|
)
|
||||||
.map_err(FromGlesError::from_gles_error)
|
.map_err(R::from_gles_error)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
@ -312,7 +337,7 @@ impl<R> From<CropRenderElement<RescaleRenderElement<WorkspaceRenderElement<R>>>>
|
||||||
for CosmicElement<R>
|
for CosmicElement<R>
|
||||||
where
|
where
|
||||||
R: AsGlowRenderer,
|
R: AsGlowRenderer,
|
||||||
R::TextureId: 'static,
|
R::TextureId: Send + 'static,
|
||||||
CosmicMappedRenderElement<R>: RenderElement<R>,
|
CosmicMappedRenderElement<R>: RenderElement<R>,
|
||||||
{
|
{
|
||||||
fn from(elem: CropRenderElement<RescaleRenderElement<WorkspaceRenderElement<R>>>) -> Self {
|
fn from(elem: CropRenderElement<RescaleRenderElement<WorkspaceRenderElement<R>>>) -> Self {
|
||||||
|
|
@ -324,22 +349,33 @@ where
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
impl<R> From<MemoryRenderBufferRenderElement<R>> for CosmicElement<R>
|
impl<R> From<IcedRenderElement<R>> for CosmicElement<R>
|
||||||
where
|
where
|
||||||
R: AsGlowRenderer,
|
R: AsGlowRenderer,
|
||||||
R::TextureId: 'static,
|
R::TextureId: Send + 'static,
|
||||||
CosmicMappedRenderElement<R>: RenderElement<R>,
|
CosmicMappedRenderElement<R>: RenderElement<R>,
|
||||||
{
|
{
|
||||||
fn from(value: MemoryRenderBufferRenderElement<R>) -> Self {
|
fn from(value: IcedRenderElement<R>) -> Self {
|
||||||
Self::Zoom(value)
|
Self::Zoom(value)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
impl<R> From<DamageElement> for CosmicElement<R>
|
||||||
|
where
|
||||||
|
R: Renderer + ImportAll + ImportMem + AsGlowRenderer,
|
||||||
|
R::TextureId: Send + 'static,
|
||||||
|
CosmicMappedRenderElement<R>: RenderElement<R>,
|
||||||
|
{
|
||||||
|
fn from(value: DamageElement) -> Self {
|
||||||
|
Self::Damage(value)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
#[cfg(feature = "debug")]
|
#[cfg(feature = "debug")]
|
||||||
impl<R> From<TextureRenderElement<GlesTexture>> for CosmicElement<R>
|
impl<R> From<TextureRenderElement<GlesTexture>> for CosmicElement<R>
|
||||||
where
|
where
|
||||||
R: AsGlowRenderer,
|
R: AsGlowRenderer,
|
||||||
R::TextureId: 'static,
|
R::TextureId: Send + 'static,
|
||||||
CosmicMappedRenderElement<R>: RenderElement<R>,
|
CosmicMappedRenderElement<R>: RenderElement<R>,
|
||||||
{
|
{
|
||||||
fn from(elem: TextureRenderElement<GlesTexture>) -> Self {
|
fn from(elem: TextureRenderElement<GlesTexture>) -> Self {
|
||||||
|
|
@ -365,6 +401,12 @@ pub trait AsGlowRenderer:
|
||||||
fn glow_frame_mut<'a, 'frame, 'buffer>(
|
fn glow_frame_mut<'a, 'frame, 'buffer>(
|
||||||
frame: &'a mut Self::Frame<'frame, 'buffer>,
|
frame: &'a mut Self::Frame<'frame, 'buffer>,
|
||||||
) -> &'a mut GlowFrame<'frame, 'buffer>;
|
) -> &'a mut GlowFrame<'frame, 'buffer>;
|
||||||
|
fn tex_from_gl(context: &ContextId<GlesTexture>, texture: GlesTexture) -> Self::TextureId;
|
||||||
|
fn tex_to_gl(
|
||||||
|
context: &ContextId<GlesTexture>,
|
||||||
|
texture: &Self::TextureId,
|
||||||
|
) -> Option<GlesTexture>;
|
||||||
|
fn from_gles_error(err: GlesError) -> Self::Error;
|
||||||
}
|
}
|
||||||
|
|
||||||
impl AsGlowRenderer for GlowRenderer {
|
impl AsGlowRenderer for GlowRenderer {
|
||||||
|
|
@ -384,6 +426,18 @@ impl AsGlowRenderer for GlowRenderer {
|
||||||
) -> &'a mut GlowFrame<'frame, 'buffer> {
|
) -> &'a mut GlowFrame<'frame, 'buffer> {
|
||||||
frame
|
frame
|
||||||
}
|
}
|
||||||
|
fn tex_from_gl(_context: &ContextId<GlesTexture>, texture: GlesTexture) -> Self::TextureId {
|
||||||
|
texture
|
||||||
|
}
|
||||||
|
fn tex_to_gl(
|
||||||
|
_context: &ContextId<GlesTexture>,
|
||||||
|
texture: &Self::TextureId,
|
||||||
|
) -> Option<GlesTexture> {
|
||||||
|
Some(texture.clone())
|
||||||
|
}
|
||||||
|
fn from_gles_error(err: GlesError) -> Self::Error {
|
||||||
|
err
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
impl AsGlowRenderer for GlMultiRenderer<'_> {
|
impl AsGlowRenderer for GlMultiRenderer<'_> {
|
||||||
|
|
@ -403,6 +457,18 @@ impl AsGlowRenderer for GlMultiRenderer<'_> {
|
||||||
) -> &'b mut GlowFrame<'frame, 'buffer> {
|
) -> &'b mut GlowFrame<'frame, 'buffer> {
|
||||||
frame.as_mut()
|
frame.as_mut()
|
||||||
}
|
}
|
||||||
|
fn tex_from_gl(context: &ContextId<GlesTexture>, texture: GlesTexture) -> Self::TextureId {
|
||||||
|
MultiTexture::from_native_texture::<GbmGlowBackend<DrmDeviceFd>>(context, texture).unwrap()
|
||||||
|
}
|
||||||
|
fn tex_to_gl(
|
||||||
|
context: &ContextId<GlesTexture>,
|
||||||
|
texture: &Self::TextureId,
|
||||||
|
) -> Option<GlesTexture> {
|
||||||
|
texture.get::<GbmGlowBackend<DrmDeviceFd>>(context)
|
||||||
|
}
|
||||||
|
fn from_gles_error(err: GlesError) -> Self::Error {
|
||||||
|
GlMultiError::Render(err)
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
pub struct DamageElement {
|
pub struct DamageElement {
|
||||||
|
|
@ -458,13 +524,3 @@ impl<R: Renderer> RenderElement<R> for DamageElement {
|
||||||
Ok(())
|
Ok(())
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
pub trait FromGlesError {
|
|
||||||
fn from_gles_error(err: GlesError) -> Self;
|
|
||||||
}
|
|
||||||
|
|
||||||
impl FromGlesError for GlesError {
|
|
||||||
fn from_gles_error(err: GlesError) -> Self {
|
|
||||||
err
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
|
||||||
|
|
@ -15,14 +15,19 @@ use crate::{
|
||||||
backend::{
|
backend::{
|
||||||
kms::render::gles::GbmGlowBackend,
|
kms::render::gles::GbmGlowBackend,
|
||||||
render::{
|
render::{
|
||||||
clipped_surface::{CLIPPING_SHADER, ClippingShader},
|
|
||||||
element::DamageElement,
|
element::DamageElement,
|
||||||
shadow::{SHADOW_SHADER, ShadowShader},
|
shadow::{SHADOW_SHADER, ShadowShader},
|
||||||
|
wayland::{
|
||||||
|
SurfaceRenderElement,
|
||||||
|
blur_effect::BlurShaders,
|
||||||
|
clipped_surface::{CLIPPING_SHADER, ClippingShader},
|
||||||
|
push_render_elements_from_surface_tree,
|
||||||
|
},
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
config::ScreenFilter,
|
config::ScreenFilter,
|
||||||
shell::{
|
shell::{
|
||||||
CosmicMappedRenderElement, OverviewMode, SeatExt, Trigger, WorkspaceDelta,
|
CosmicMappedRenderElement, OutputId, OverviewMode, SeatExt, Trigger, WorkspaceDelta,
|
||||||
WorkspaceRenderElement,
|
WorkspaceRenderElement,
|
||||||
element::CosmicMappedKey,
|
element::CosmicMappedKey,
|
||||||
focus::{FocusTarget, Stage, render_input_order, target::WindowGroup},
|
focus::{FocusTarget, Stage, render_input_order, target::WindowGroup},
|
||||||
|
|
@ -34,25 +39,26 @@ use crate::{
|
||||||
wayland::{
|
wayland::{
|
||||||
handlers::{
|
handlers::{
|
||||||
compositor::FRAME_TIME_FILTER,
|
compositor::FRAME_TIME_FILTER,
|
||||||
|
corner_radius::{pad_rect, surface_corners, surface_padding},
|
||||||
data_device::get_dnd_icon,
|
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,
|
protocols::workspace::WorkspaceHandle,
|
||||||
},
|
},
|
||||||
};
|
};
|
||||||
|
|
||||||
use cosmic::Theme;
|
use cosmic::Theme;
|
||||||
use element::FromGlesError;
|
|
||||||
use smithay::{
|
use smithay::{
|
||||||
backend::{
|
backend::{
|
||||||
allocator::Fourcc,
|
allocator::Fourcc,
|
||||||
drm::{DrmDeviceFd, DrmNode},
|
drm::{DrmDeviceFd, DrmNode},
|
||||||
renderer::{
|
renderer::{
|
||||||
Color32F, ImportAll, Offscreen, Renderer, Texture, TextureFilter,
|
Color32F, Offscreen, Texture, TextureFilter,
|
||||||
damage::{Error as RenderError, OutputDamageTracker, RenderOutputResult},
|
damage::{Error as RenderError, OutputDamageTracker, RenderOutputResult},
|
||||||
element::{
|
element::{
|
||||||
Element, Id, Kind, RenderElement, WeakId,
|
Element, Id, Kind, NamespacedElement, RenderElement, WeakId,
|
||||||
surface::{WaylandSurfaceRenderElement, render_elements_from_surface_tree},
|
|
||||||
texture::{TextureRenderBuffer, TextureRenderElement},
|
texture::{TextureRenderBuffer, TextureRenderElement},
|
||||||
utils::{
|
utils::{
|
||||||
ConstrainAlign, ConstrainScaleBehavior, CropRenderElement, Relocate,
|
ConstrainAlign, ConstrainScaleBehavior, CropRenderElement, Relocate,
|
||||||
|
|
@ -69,22 +75,23 @@ use smithay::{
|
||||||
sync::SyncPoint,
|
sync::SyncPoint,
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
|
desktop::utils::bbox_from_surface_tree,
|
||||||
input::Seat,
|
input::Seat,
|
||||||
output::{Output, OutputModeSource, OutputNoMode},
|
output::{Output, OutputModeSource, OutputNoMode},
|
||||||
utils::{
|
utils::{
|
||||||
IsAlive, Logical, Monotonic, Physical, Point, Rectangle, Scale, Size, Time, Transform,
|
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")]
|
#[cfg(feature = "debug")]
|
||||||
use smithay_egui::EguiState;
|
use smithay_egui::EguiState;
|
||||||
|
|
||||||
pub mod animations;
|
pub mod animations;
|
||||||
pub mod clipped_surface;
|
|
||||||
pub mod cursor;
|
pub mod cursor;
|
||||||
pub mod element;
|
pub mod element;
|
||||||
pub mod shadow;
|
pub mod shadow;
|
||||||
|
pub mod wayland;
|
||||||
use self::element::{AsGlowRenderer, CosmicElement};
|
use self::element::{AsGlowRenderer, CosmicElement};
|
||||||
|
|
||||||
use super::kms::Timings;
|
use super::kms::Timings;
|
||||||
|
|
@ -262,7 +269,7 @@ impl IndicatorShader {
|
||||||
.filter(|(old_settings, _)| &settings == old_settings)
|
.filter(|(old_settings, _)| &settings == old_settings)
|
||||||
.is_none()
|
.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 shader = Self::get(renderer);
|
||||||
|
|
||||||
let elem = PixelShaderElement::new(
|
let elem = PixelShaderElement::new(
|
||||||
|
|
@ -422,6 +429,7 @@ pub fn init_shaders(renderer: &mut GlesRenderer) -> Result<(), GlesError> {
|
||||||
UniformName::new("geo_size", UniformType::_2f),
|
UniformName::new("geo_size", UniformType::_2f),
|
||||||
UniformName::new("corner_radius", UniformType::_4f),
|
UniformName::new("corner_radius", UniformType::_4f),
|
||||||
UniformName::new("input_to_geo", UniformType::Matrix3x3),
|
UniformName::new("input_to_geo", UniformType::Matrix3x3),
|
||||||
|
UniformName::new("noise", UniformType::_1f),
|
||||||
],
|
],
|
||||||
)?;
|
)?;
|
||||||
let shadow_shader = renderer.compile_custom_pixel_shader(
|
let shadow_shader = renderer.compile_custom_pixel_shader(
|
||||||
|
|
@ -437,6 +445,7 @@ pub fn init_shaders(renderer: &mut GlesRenderer) -> Result<(), GlesError> {
|
||||||
UniformName::new("window_corner_radius", UniformType::_4f),
|
UniformName::new("window_corner_radius", UniformType::_4f),
|
||||||
],
|
],
|
||||||
)?;
|
)?;
|
||||||
|
let blur_shaders = BlurShaders::compile(renderer)?;
|
||||||
|
|
||||||
let egl_context = renderer.egl_context();
|
let egl_context = renderer.egl_context();
|
||||||
egl_context
|
egl_context
|
||||||
|
|
@ -454,6 +463,7 @@ pub fn init_shaders(renderer: &mut GlesRenderer) -> Result<(), GlesError> {
|
||||||
egl_context
|
egl_context
|
||||||
.user_data()
|
.user_data()
|
||||||
.insert_if_missing(|| ShadowShader(shadow_shader));
|
.insert_if_missing(|| ShadowShader(shadow_shader));
|
||||||
|
egl_context.user_data().insert_if_missing(|| blur_shaders);
|
||||||
|
|
||||||
Ok(())
|
Ok(())
|
||||||
}
|
}
|
||||||
|
|
@ -471,12 +481,14 @@ pub fn cursor_elements<'a, 'frame, R>(
|
||||||
seats: impl Iterator<Item = &'a Seat<State>>,
|
seats: impl Iterator<Item = &'a Seat<State>>,
|
||||||
zoom_state: Option<&ZoomState>,
|
zoom_state: Option<&ZoomState>,
|
||||||
theme: &Theme,
|
theme: &Theme,
|
||||||
|
blur_strength: usize,
|
||||||
now: Time<Monotonic>,
|
now: Time<Monotonic>,
|
||||||
output: &Output,
|
output: &Output,
|
||||||
mode: CursorMode,
|
mode: CursorMode,
|
||||||
exclude_dnd_icon: bool,
|
exclude_dnd_icon: bool,
|
||||||
) -> Vec<CosmicElement<R>>
|
scanout_node: Option<DrmNode>,
|
||||||
where
|
push: &mut dyn FnMut(CosmicElement<R>),
|
||||||
|
) where
|
||||||
R: AsGlowRenderer,
|
R: AsGlowRenderer,
|
||||||
R::TextureId: Send + Clone + 'static,
|
R::TextureId: Send + Clone + 'static,
|
||||||
CosmicMappedRenderElement<R>: RenderElement<R>,
|
CosmicMappedRenderElement<R>: RenderElement<R>,
|
||||||
|
|
@ -490,7 +502,6 @@ where
|
||||||
)
|
)
|
||||||
})
|
})
|
||||||
.unwrap_or_else(|| ((0., 0.).into(), 1.));
|
.unwrap_or_else(|| ((0., 0.).into(), 1.));
|
||||||
let mut elements = Vec::new();
|
|
||||||
|
|
||||||
for seat in seats {
|
for seat in seats {
|
||||||
let pointer = match seat.get_pointer() {
|
let pointer = match seat.get_pointer() {
|
||||||
|
|
@ -500,19 +511,17 @@ where
|
||||||
let location = pointer.current_location() - output.current_location().to_f64();
|
let location = pointer.current_location() - output.current_location().to_f64();
|
||||||
|
|
||||||
if mode != CursorMode::None {
|
if mode != CursorMode::None {
|
||||||
elements.extend(
|
cursor::draw_cursor(
|
||||||
cursor::draw_cursor(
|
renderer,
|
||||||
renderer,
|
seat,
|
||||||
seat,
|
location,
|
||||||
location,
|
scale.into(),
|
||||||
scale.into(),
|
zoom_scale,
|
||||||
zoom_scale,
|
now,
|
||||||
now,
|
blur_strength,
|
||||||
mode != CursorMode::NotDefault,
|
mode != CursorMode::NotDefault,
|
||||||
)
|
&mut |elem, hotspot| {
|
||||||
.into_iter()
|
push(CosmicElement::Cursor(RescaleRenderElement::from_element(
|
||||||
.map(|(elem, hotspot)| {
|
|
||||||
CosmicElement::Cursor(RescaleRenderElement::from_element(
|
|
||||||
RelocateRenderElement::from_element(
|
RelocateRenderElement::from_element(
|
||||||
elem,
|
elem,
|
||||||
Point::from((-hotspot.x, -hotspot.y)),
|
Point::from((-hotspot.x, -hotspot.y)),
|
||||||
|
|
@ -523,63 +532,55 @@ where
|
||||||
.to_physical(output.current_scale().fractional_scale())
|
.to_physical(output.current_scale().fractional_scale())
|
||||||
.to_i32_round(),
|
.to_i32_round(),
|
||||||
zoom_scale,
|
zoom_scale,
|
||||||
))
|
)))
|
||||||
}),
|
},
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
if !exclude_dnd_icon && let Some(dnd_icon) = get_dnd_icon(seat) {
|
if !exclude_dnd_icon && let Some(dnd_icon) = get_dnd_icon(seat) {
|
||||||
elements.extend(
|
cursor::draw_dnd_icon(
|
||||||
cursor::draw_dnd_icon(
|
renderer,
|
||||||
renderer,
|
&dnd_icon.surface,
|
||||||
&dnd_icon.surface,
|
(location + dnd_icon.offset.to_f64()).to_i32_round(),
|
||||||
(location + dnd_icon.offset.to_f64()).to_i32_round(),
|
scale,
|
||||||
scale,
|
blur_strength,
|
||||||
)
|
&mut |elem| push(CosmicElement::Dnd(elem)),
|
||||||
.into_iter()
|
|
||||||
.map(CosmicElement::Dnd),
|
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
let theme = theme.cosmic();
|
let theme = theme.cosmic();
|
||||||
if let Some(grab_elements) = seat
|
if let Some(grab_state) = seat
|
||||||
.user_data()
|
.user_data()
|
||||||
.get::<SeatMoveGrabState>()
|
.get::<SeatMoveGrabState>()
|
||||||
.unwrap()
|
.unwrap()
|
||||||
.lock()
|
.lock()
|
||||||
.unwrap()
|
.unwrap()
|
||||||
.as_ref()
|
.as_ref()
|
||||||
.map(|state| state.render::<CosmicMappedRenderElement<R>, R>(renderer, output, theme))
|
|
||||||
{
|
{
|
||||||
elements.extend(grab_elements.into_iter().map(|elem| {
|
grab_state.render(renderer, output, theme, scanout_node, &mut |elem| {
|
||||||
CosmicElement::MoveGrab(RescaleRenderElement::from_element(
|
push(CosmicElement::MoveGrab(RescaleRenderElement::from_element(
|
||||||
elem,
|
elem,
|
||||||
focal_point
|
focal_point
|
||||||
.as_logical()
|
.as_logical()
|
||||||
.to_physical(output.current_scale().fractional_scale())
|
.to_physical(output.current_scale().fractional_scale())
|
||||||
.to_i32_round(),
|
.to_i32_round(),
|
||||||
zoom_scale,
|
zoom_scale,
|
||||||
))
|
)));
|
||||||
}));
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
if let Some((grab_elements, should_scale)) = seat
|
if let Some(grab_state) = seat
|
||||||
.user_data()
|
.user_data()
|
||||||
.get::<SeatMenuGrabState>()
|
.get::<SeatMenuGrabState>()
|
||||||
.unwrap()
|
.unwrap()
|
||||||
.lock()
|
.lock()
|
||||||
.unwrap()
|
.unwrap()
|
||||||
.as_ref()
|
.as_ref()
|
||||||
.map(|state| {
|
|
||||||
(
|
|
||||||
state.render::<CosmicMappedRenderElement<R>, R>(renderer, output),
|
|
||||||
!state.is_in_screen_space(),
|
|
||||||
)
|
|
||||||
})
|
|
||||||
{
|
{
|
||||||
elements.extend(grab_elements.into_iter().map(|elem| {
|
let should_scale = !grab_state.is_in_screen_space();
|
||||||
CosmicElement::MoveGrab(RescaleRenderElement::from_element(
|
grab_state.render(renderer, output, &mut |elem| {
|
||||||
elem,
|
push(CosmicElement::MoveGrab(RescaleRenderElement::from_element(
|
||||||
|
elem.into(),
|
||||||
if should_scale {
|
if should_scale {
|
||||||
focal_point
|
focal_point
|
||||||
.as_logical()
|
.as_logical()
|
||||||
|
|
@ -589,12 +590,10 @@ where
|
||||||
Point::from((0, 0))
|
Point::from((0, 0))
|
||||||
},
|
},
|
||||||
if should_scale { zoom_scale } else { 1.0 },
|
if should_scale { zoom_scale } else { 1.0 },
|
||||||
))
|
)));
|
||||||
}));
|
})
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
elements
|
|
||||||
}
|
}
|
||||||
|
|
||||||
#[cfg(not(feature = "debug"))]
|
#[cfg(not(feature = "debug"))]
|
||||||
|
|
@ -615,11 +614,11 @@ pub fn output_elements<R>(
|
||||||
output: &Output,
|
output: &Output,
|
||||||
cursor_mode: CursorMode,
|
cursor_mode: CursorMode,
|
||||||
_fps: Option<(&EguiState, &Timings)>,
|
_fps: Option<(&EguiState, &Timings)>,
|
||||||
|
scanout_node: Option<DrmNode>,
|
||||||
) -> Result<Vec<CosmicElement<R>>, RenderError<R::Error>>
|
) -> Result<Vec<CosmicElement<R>>, RenderError<R::Error>>
|
||||||
where
|
where
|
||||||
R: AsGlowRenderer,
|
R: AsGlowRenderer,
|
||||||
R::TextureId: Send + Clone + 'static,
|
R::TextureId: Send + Clone + 'static,
|
||||||
R::Error: FromGlesError,
|
|
||||||
CosmicMappedRenderElement<R>: RenderElement<R>,
|
CosmicMappedRenderElement<R>: RenderElement<R>,
|
||||||
WorkspaceRenderElement<R>: RenderElement<R>,
|
WorkspaceRenderElement<R>: RenderElement<R>,
|
||||||
{
|
{
|
||||||
|
|
@ -646,7 +645,7 @@ where
|
||||||
),
|
),
|
||||||
scale,
|
scale,
|
||||||
)
|
)
|
||||||
.map_err(FromGlesError::from_gles_error)
|
.map_err(R::from_gles_error)
|
||||||
.map_err(RenderError::Rendering)?
|
.map_err(RenderError::Rendering)?
|
||||||
.into(),
|
.into(),
|
||||||
]
|
]
|
||||||
|
|
@ -690,6 +689,7 @@ where
|
||||||
workspace,
|
workspace,
|
||||||
cursor_mode,
|
cursor_mode,
|
||||||
element_filter,
|
element_filter,
|
||||||
|
scanout_node,
|
||||||
)?;
|
)?;
|
||||||
|
|
||||||
#[cfg(feature = "debug")]
|
#[cfg(feature = "debug")]
|
||||||
|
|
@ -713,15 +713,15 @@ pub fn workspace_elements<R>(
|
||||||
current: (WorkspaceHandle, usize),
|
current: (WorkspaceHandle, usize),
|
||||||
cursor_mode: CursorMode,
|
cursor_mode: CursorMode,
|
||||||
element_filter: ElementFilter,
|
element_filter: ElementFilter,
|
||||||
|
scanout_node: Option<DrmNode>,
|
||||||
) -> Result<Vec<CosmicElement<R>>, RenderError<R::Error>>
|
) -> Result<Vec<CosmicElement<R>>, RenderError<R::Error>>
|
||||||
where
|
where
|
||||||
R: AsGlowRenderer,
|
R: AsGlowRenderer,
|
||||||
R::TextureId: Send + Clone + 'static,
|
R::TextureId: Send + Clone + 'static,
|
||||||
R::Error: FromGlesError,
|
|
||||||
CosmicMappedRenderElement<R>: RenderElement<R>,
|
CosmicMappedRenderElement<R>: RenderElement<R>,
|
||||||
WorkspaceRenderElement<R>: RenderElement<R>,
|
WorkspaceRenderElement<R>: RenderElement<R>,
|
||||||
{
|
{
|
||||||
let mut elements = Vec::new();
|
let mut elements = Vec::<CosmicElement<R>>::new();
|
||||||
|
|
||||||
let shell_ref = shell.read();
|
let shell_ref = shell.read();
|
||||||
let seats = shell_ref.seats.iter().cloned().collect::<Vec<_>>();
|
let seats = shell_ref.seats.iter().cloned().collect::<Vec<_>>();
|
||||||
|
|
@ -729,21 +729,25 @@ where
|
||||||
return Ok(Vec::new());
|
return Ok(Vec::new());
|
||||||
}
|
}
|
||||||
let theme = shell_ref.theme().clone();
|
let theme = shell_ref.theme().clone();
|
||||||
|
let blur_strength = (theme.cosmic().frosted as u8 + 1) as usize;
|
||||||
let scale = output.current_scale().fractional_scale();
|
let scale = output.current_scale().fractional_scale();
|
||||||
// we don't want to hold a shell lock across `cursor_elements`,
|
// we don't want to hold a shell lock across `cursor_elements`,
|
||||||
// that is prone to deadlock with the main-thread on some grabs.
|
// that is prone to deadlock with the main-thread on some grabs.
|
||||||
std::mem::drop(shell_ref);
|
std::mem::drop(shell_ref);
|
||||||
|
|
||||||
elements.extend(cursor_elements(
|
cursor_elements(
|
||||||
renderer,
|
renderer,
|
||||||
seats.iter(),
|
seats.iter(),
|
||||||
zoom_level,
|
zoom_level,
|
||||||
&theme,
|
&theme,
|
||||||
|
blur_strength,
|
||||||
now,
|
now,
|
||||||
output,
|
output,
|
||||||
cursor_mode,
|
cursor_mode,
|
||||||
element_filter == ElementFilter::ExcludeWorkspaceOverview,
|
element_filter == ElementFilter::ExcludeWorkspaceOverview,
|
||||||
));
|
scanout_node,
|
||||||
|
&mut |elem| elements.push(elem),
|
||||||
|
);
|
||||||
|
|
||||||
let shell = shell.read();
|
let shell = shell.read();
|
||||||
let overview = shell.overview_mode();
|
let overview = shell.overview_mode();
|
||||||
|
|
@ -819,72 +823,132 @@ where
|
||||||
render_input_order::<()>(&shell, output, previous, current, element_filter, |stage| {
|
render_input_order::<()>(&shell, output, previous, current, element_filter, |stage| {
|
||||||
match stage {
|
match stage {
|
||||||
Stage::ZoomUI => {
|
Stage::ZoomUI => {
|
||||||
elements.extend(ZoomState::render(renderer, output));
|
ZoomState::render(renderer, output, &mut |elem| {
|
||||||
|
elements.push(CosmicElement::Zoom(elem))
|
||||||
|
});
|
||||||
}
|
}
|
||||||
Stage::SessionLock(lock_surface) => {
|
Stage::SessionLock(lock_surface) => {
|
||||||
elements.extend(
|
session_lock_elements(renderer, output, lock_surface, blur_strength, &mut |elem| {
|
||||||
session_lock_elements(renderer, output, lock_surface)
|
elements.extend(crop_to_output(elem.into()).map(Into::into))
|
||||||
.into_iter()
|
})
|
||||||
.map(Into::into)
|
|
||||||
.flat_map(crop_to_output)
|
|
||||||
.map(Into::into),
|
|
||||||
);
|
|
||||||
}
|
}
|
||||||
Stage::LayerPopup {
|
Stage::LayerPopup {
|
||||||
popup, location, ..
|
popup,
|
||||||
|
location,
|
||||||
|
workspace_idx,
|
||||||
|
..
|
||||||
} => {
|
} => {
|
||||||
elements.extend(
|
let mut geometry = popup.geometry().as_global();
|
||||||
render_elements_from_surface_tree::<_, WorkspaceRenderElement<_>>(
|
geometry.loc += location;
|
||||||
renderer,
|
|
||||||
popup.wl_surface(),
|
let radii = with_states(popup.wl_surface(), |states| {
|
||||||
location
|
surface_corners(states, geometry.size.as_logical())
|
||||||
.to_local(output)
|
})
|
||||||
.as_logical()
|
.unwrap_or([0; 4]);
|
||||||
.to_physical_precise_round(scale),
|
|
||||||
Scale::from(scale),
|
let namespace = output
|
||||||
1.0,
|
.user_data()
|
||||||
FRAME_TIME_FILTER,
|
.get::<OutputId>()
|
||||||
)
|
.map(|id| id.namespace_for_workspace(workspace_idx))
|
||||||
.into_iter()
|
.unwrap_or(workspace_idx);
|
||||||
.flat_map(crop_to_output)
|
|
||||||
.map(Into::into),
|
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,
|
||||||
|
radii,
|
||||||
|
None,
|
||||||
|
blur_strength,
|
||||||
|
FRAME_TIME_FILTER,
|
||||||
|
&mut |elem| {
|
||||||
|
elements.extend(
|
||||||
|
crop_to_output(NamespacedElement::new(elem, namespace).into())
|
||||||
|
.map(Into::into),
|
||||||
|
)
|
||||||
|
},
|
||||||
|
None,
|
||||||
|
)
|
||||||
}
|
}
|
||||||
Stage::LayerSurface { layer, location } => {
|
Stage::LayerSurface {
|
||||||
elements.extend(
|
layer,
|
||||||
render_elements_from_surface_tree::<_, WorkspaceRenderElement<_>>(
|
location,
|
||||||
renderer,
|
workspace_idx,
|
||||||
layer.wl_surface(),
|
} => {
|
||||||
location
|
let mut geometry = layer.geometry().as_global();
|
||||||
.to_local(output)
|
geometry.loc += location;
|
||||||
.as_logical()
|
let geometry = geometry.to_local(output).as_logical();
|
||||||
.to_physical_precise_round(scale),
|
|
||||||
Scale::from(scale),
|
let padded = with_states(layer.wl_surface(), |states| {
|
||||||
1.0,
|
surface_padding(states, geometry.size)
|
||||||
FRAME_TIME_FILTER,
|
.and_then(|padding| pad_rect(geometry, &padding))
|
||||||
)
|
})
|
||||||
.into_iter()
|
.unwrap_or(geometry);
|
||||||
.flat_map(crop_to_output)
|
let radii = with_states(layer.wl_surface(), |states| {
|
||||||
.map(Into::into),
|
surface_corners(states, padded.size)
|
||||||
|
})
|
||||||
|
.unwrap_or([0; 4]);
|
||||||
|
|
||||||
|
let namespace = output
|
||||||
|
.user_data()
|
||||||
|
.get::<OutputId>()
|
||||||
|
.map(|id| id.namespace_for_workspace(workspace_idx))
|
||||||
|
.unwrap_or(workspace_idx);
|
||||||
|
|
||||||
|
push_render_elements_from_surface_tree(
|
||||||
|
renderer,
|
||||||
|
layer.wl_surface(),
|
||||||
|
location
|
||||||
|
.to_local(output)
|
||||||
|
.as_logical()
|
||||||
|
.to_physical_precise_round(scale),
|
||||||
|
geometry.to_f64(),
|
||||||
|
Scale::from(scale),
|
||||||
|
1.0,
|
||||||
|
false,
|
||||||
|
radii,
|
||||||
|
padded.to_f64(),
|
||||||
|
blur_strength,
|
||||||
|
FRAME_TIME_FILTER,
|
||||||
|
&mut |elem| {
|
||||||
|
elements.extend(
|
||||||
|
crop_to_output(NamespacedElement::new(elem, namespace).into())
|
||||||
|
.map(Into::into),
|
||||||
|
)
|
||||||
|
},
|
||||||
|
None,
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
Stage::OverrideRedirect { surface, location } => {
|
Stage::OverrideRedirect { surface, location } => {
|
||||||
elements.extend(surface.wl_surface().into_iter().flat_map(|surface| {
|
if let Some(wl_surface) = surface.wl_surface() {
|
||||||
render_elements_from_surface_tree::<_, WorkspaceRenderElement<_>>(
|
let mut geometry = surface.geometry().as_global();
|
||||||
|
geometry.loc += location;
|
||||||
|
|
||||||
|
push_render_elements_from_surface_tree(
|
||||||
renderer,
|
renderer,
|
||||||
&surface,
|
&wl_surface,
|
||||||
location
|
location
|
||||||
.to_local(output)
|
.to_local(output)
|
||||||
.as_logical()
|
.as_logical()
|
||||||
.to_physical_precise_round(scale),
|
.to_physical_precise_round(scale),
|
||||||
|
geometry.to_local(output).as_logical().to_f64(),
|
||||||
Scale::from(scale),
|
Scale::from(scale),
|
||||||
1.0,
|
1.0,
|
||||||
|
false,
|
||||||
|
[0; 4],
|
||||||
|
None,
|
||||||
|
blur_strength,
|
||||||
FRAME_TIME_FILTER,
|
FRAME_TIME_FILTER,
|
||||||
)
|
&mut |elem| elements.extend(crop_to_output(elem.into()).map(Into::into)),
|
||||||
.into_iter()
|
None,
|
||||||
.flat_map(crop_to_output)
|
);
|
||||||
.map(Into::into)
|
}
|
||||||
}));
|
|
||||||
}
|
}
|
||||||
Stage::StickyPopups(layout) => {
|
Stage::StickyPopups(layout) => {
|
||||||
let alpha = match &overview.0 {
|
let alpha = match &overview.0 {
|
||||||
|
|
@ -905,14 +969,11 @@ where
|
||||||
OverviewMode::None => 1.0,
|
OverviewMode::None => 1.0,
|
||||||
};
|
};
|
||||||
|
|
||||||
elements.extend(
|
layout.render_popups(renderer, alpha, scanout_node, &mut |elem| {
|
||||||
layout
|
if let Some(elem) = crop_to_output(elem.into()) {
|
||||||
.render_popups(renderer, alpha)
|
elements.push(elem.into())
|
||||||
.into_iter()
|
}
|
||||||
.map(Into::into)
|
});
|
||||||
.flat_map(crop_to_output)
|
|
||||||
.map(Into::into),
|
|
||||||
);
|
|
||||||
}
|
}
|
||||||
Stage::Sticky(layout) => {
|
Stage::Sticky(layout) => {
|
||||||
let alpha = match &overview.0 {
|
let alpha = match &overview.0 {
|
||||||
|
|
@ -937,79 +998,66 @@ where
|
||||||
.then_some(last_active_seat)
|
.then_some(last_active_seat)
|
||||||
.map(|seat| workspace.focus_stack.get(seat));
|
.map(|seat| workspace.focus_stack.get(seat));
|
||||||
|
|
||||||
elements.extend(
|
layout.render(
|
||||||
layout
|
renderer,
|
||||||
.render(
|
current_focus.as_ref().and_then(|stack| {
|
||||||
renderer,
|
stack.last().and_then(|t| match t {
|
||||||
current_focus.as_ref().and_then(|stack| {
|
FocusTarget::Window(w) => Some(w),
|
||||||
stack.last().and_then(|t| match t {
|
_ => None,
|
||||||
FocusTarget::Window(w) => Some(w),
|
})
|
||||||
_ => None,
|
}),
|
||||||
})
|
resize_indicator.clone(),
|
||||||
}),
|
active_hint,
|
||||||
resize_indicator.clone(),
|
alpha,
|
||||||
active_hint,
|
theme.cosmic(),
|
||||||
alpha,
|
scanout_node,
|
||||||
theme.cosmic(),
|
&mut |elem| {
|
||||||
)
|
if let Some(elem) = crop_to_output(elem.into()) {
|
||||||
.into_iter()
|
elements.push(elem.into())
|
||||||
.map(Into::into)
|
}
|
||||||
.flat_map(crop_to_output)
|
},
|
||||||
.map(Into::into),
|
);
|
||||||
)
|
|
||||||
}
|
}
|
||||||
Stage::WorkspacePopups { workspace, offset } => {
|
Stage::WorkspacePopups { workspace, offset } => {
|
||||||
elements.extend(
|
workspace.render_popups(
|
||||||
match workspace.render_popups(
|
renderer,
|
||||||
renderer,
|
last_active_seat,
|
||||||
last_active_seat,
|
!move_active && is_active_space,
|
||||||
!move_active && is_active_space,
|
overview.clone(),
|
||||||
overview.clone(),
|
theme.cosmic(),
|
||||||
theme.cosmic(),
|
scanout_node,
|
||||||
) {
|
&mut |elem| {
|
||||||
Ok(elements) => {
|
if let Some(elem) = crop_to_output(elem) {
|
||||||
elements
|
elements.push(CosmicElement::Workspace(
|
||||||
.into_iter()
|
RelocateRenderElement::from_element(
|
||||||
.flat_map(crop_to_output)
|
elem,
|
||||||
.map(|element| {
|
offset.to_physical_precise_round(scale),
|
||||||
CosmicElement::Workspace(RelocateRenderElement::from_element(
|
Relocate::Relative,
|
||||||
element,
|
),
|
||||||
offset.to_physical_precise_round(scale),
|
));
|
||||||
Relocate::Relative,
|
|
||||||
))
|
|
||||||
})
|
|
||||||
}
|
|
||||||
Err(_) => {
|
|
||||||
return ControlFlow::Break(Err(OutputNoMode));
|
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
Stage::Workspace { workspace, offset } => {
|
Stage::Workspace { workspace, offset } => {
|
||||||
elements.extend(
|
workspace.render(
|
||||||
match workspace.render(
|
renderer,
|
||||||
renderer,
|
last_active_seat,
|
||||||
last_active_seat,
|
!move_active && is_active_space,
|
||||||
!move_active && is_active_space,
|
overview.clone(),
|
||||||
overview.clone(),
|
resize_indicator.clone(),
|
||||||
resize_indicator.clone(),
|
active_hint,
|
||||||
active_hint,
|
theme.cosmic(),
|
||||||
theme.cosmic(),
|
scanout_node,
|
||||||
) {
|
&mut |elem| {
|
||||||
Ok(elements) => {
|
if let Some(elem) = crop_to_output(elem) {
|
||||||
elements
|
elements.push(CosmicElement::Workspace(
|
||||||
.into_iter()
|
RelocateRenderElement::from_element(
|
||||||
.flat_map(crop_to_output)
|
elem,
|
||||||
.map(|element| {
|
offset.to_physical_precise_round(scale),
|
||||||
CosmicElement::Workspace(RelocateRenderElement::from_element(
|
Relocate::Relative,
|
||||||
element,
|
),
|
||||||
offset.to_physical_precise_round(scale),
|
));
|
||||||
Relocate::Relative,
|
|
||||||
))
|
|
||||||
})
|
|
||||||
}
|
|
||||||
Err(_) => {
|
|
||||||
return ControlFlow::Break(Err(OutputNoMode));
|
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
);
|
);
|
||||||
|
|
@ -1026,23 +1074,29 @@ fn session_lock_elements<R>(
|
||||||
renderer: &mut R,
|
renderer: &mut R,
|
||||||
output: &Output,
|
output: &Output,
|
||||||
lock_surface: Option<&LockSurface>,
|
lock_surface: Option<&LockSurface>,
|
||||||
) -> Vec<WaylandSurfaceRenderElement<R>>
|
blur_strength: usize,
|
||||||
where
|
push: &mut dyn FnMut(SurfaceRenderElement<R>),
|
||||||
R: Renderer + ImportAll,
|
) where
|
||||||
|
R: AsGlowRenderer,
|
||||||
R::TextureId: Clone + 'static,
|
R::TextureId: Clone + 'static,
|
||||||
{
|
{
|
||||||
if let Some(surface) = lock_surface {
|
if let Some(surface) = lock_surface {
|
||||||
let scale = Scale::from(output.current_scale().fractional_scale());
|
let scale = Scale::from(output.current_scale().fractional_scale());
|
||||||
render_elements_from_surface_tree(
|
push_render_elements_from_surface_tree(
|
||||||
renderer,
|
renderer,
|
||||||
surface.wl_surface(),
|
surface.wl_surface(),
|
||||||
(0, 0),
|
(0, 0),
|
||||||
|
bbox_from_surface_tree(surface.wl_surface(), (0, 0)).to_f64(),
|
||||||
scale,
|
scale,
|
||||||
1.0,
|
1.0,
|
||||||
|
false,
|
||||||
|
[0; 4],
|
||||||
|
None,
|
||||||
|
blur_strength,
|
||||||
FRAME_TIME_FILTER,
|
FRAME_TIME_FILTER,
|
||||||
|
push,
|
||||||
|
None,
|
||||||
)
|
)
|
||||||
} else {
|
|
||||||
Vec::new()
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -1190,7 +1244,6 @@ pub fn render_output<'d, R>(
|
||||||
where
|
where
|
||||||
R: AsGlowRenderer,
|
R: AsGlowRenderer,
|
||||||
R::TextureId: Send + Clone + 'static,
|
R::TextureId: Send + Clone + 'static,
|
||||||
R::Error: FromGlesError,
|
|
||||||
CosmicElement<R>: RenderElement<R>,
|
CosmicElement<R>: RenderElement<R>,
|
||||||
CosmicMappedRenderElement<R>: RenderElement<R>,
|
CosmicMappedRenderElement<R>: RenderElement<R>,
|
||||||
WorkspaceRenderElement<R>: RenderElement<R>,
|
WorkspaceRenderElement<R>: RenderElement<R>,
|
||||||
|
|
@ -1454,11 +1507,16 @@ where
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
Ok(RenderOutputResult {
|
let buffers = render_element_buffers(renderer, &elements);
|
||||||
damage: res.0,
|
|
||||||
sync,
|
Ok((
|
||||||
states: res.1,
|
RenderOutputResult {
|
||||||
})
|
damage: res.0,
|
||||||
|
sync,
|
||||||
|
states: res.1,
|
||||||
|
},
|
||||||
|
buffers,
|
||||||
|
))
|
||||||
},
|
},
|
||||||
)? {
|
)? {
|
||||||
pending_image_copy_data.send_success_when_ready(
|
pending_image_copy_data.send_success_when_ready(
|
||||||
|
|
@ -1495,12 +1553,23 @@ pub fn render_workspace<'d, R>(
|
||||||
where
|
where
|
||||||
R: AsGlowRenderer,
|
R: AsGlowRenderer,
|
||||||
R::TextureId: Send + Clone + 'static,
|
R::TextureId: Send + Clone + 'static,
|
||||||
R::Error: FromGlesError,
|
|
||||||
CosmicElement<R>: RenderElement<R>,
|
CosmicElement<R>: RenderElement<R>,
|
||||||
CosmicMappedRenderElement<R>: RenderElement<R>,
|
CosmicMappedRenderElement<R>: RenderElement<R>,
|
||||||
WorkspaceRenderElement<R>: RenderElement<R>,
|
WorkspaceRenderElement<R>: RenderElement<R>,
|
||||||
{
|
{
|
||||||
let elements: Vec<CosmicElement<R>> = workspace_elements(
|
let mut elements: Vec<CosmicElement<R>> = 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,
|
gpu,
|
||||||
renderer,
|
renderer,
|
||||||
shell,
|
shell,
|
||||||
|
|
@ -1511,17 +1580,8 @@ where
|
||||||
current,
|
current,
|
||||||
cursor_mode,
|
cursor_mode,
|
||||||
element_filter,
|
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(
|
let res = damage_tracker.render_output(
|
||||||
renderer,
|
renderer,
|
||||||
|
|
|
||||||
30
src/backend/render/shaders/blur_downsample.frag
Normal file
30
src/backend/render/shaders/blur_downsample.frag
Normal file
|
|
@ -0,0 +1,30 @@
|
||||||
|
#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);
|
||||||
|
|
||||||
|
if (sum.a == 0.0) {
|
||||||
|
gl_FragColor = vec4(0.0, 0.0, 0.0, 0.0);
|
||||||
|
} else {
|
||||||
|
gl_FragColor = sum / sum.a;
|
||||||
|
}
|
||||||
|
}
|
||||||
31
src/backend/render/shaders/blur_upsample.frag
Normal file
31
src/backend/render/shaders/blur_upsample.frag
Normal file
|
|
@ -0,0 +1,31 @@
|
||||||
|
#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;
|
||||||
|
|
||||||
|
if (sum.a == 0.0) {
|
||||||
|
gl_FragColor = vec4(0.0, 0.0, 0.0, 0.0);
|
||||||
|
} else {
|
||||||
|
gl_FragColor = sum / sum.a;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
@ -25,6 +25,7 @@ uniform float tint;
|
||||||
uniform vec2 geo_size;
|
uniform vec2 geo_size;
|
||||||
uniform vec4 corner_radius;
|
uniform vec4 corner_radius;
|
||||||
uniform mat3 input_to_geo;
|
uniform mat3 input_to_geo;
|
||||||
|
uniform float noise;
|
||||||
|
|
||||||
float rounding_alpha(vec2 coords, vec2 size) {
|
float rounding_alpha(vec2 coords, vec2 size) {
|
||||||
vec2 center;
|
vec2 center;
|
||||||
|
|
@ -51,15 +52,26 @@ float rounding_alpha(vec2 coords, vec2 size) {
|
||||||
return 1.0 - smoothstep(radius - half_px, radius + half_px, dist);
|
return 1.0 - smoothstep(radius - half_px, radius + half_px, dist);
|
||||||
}
|
}
|
||||||
|
|
||||||
void main() {
|
float hash(vec2 p) {
|
||||||
vec3 coords_geo = input_to_geo * vec3(v_coords, 1.0);
|
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);
|
vec4 color = texture2D(tex, v_coords);
|
||||||
#if defined(NO_ALPHA)
|
#if defined(NO_ALPHA)
|
||||||
color = vec4(color.rgb, 1.0);
|
color = vec4(color.rgb, 1.0);
|
||||||
#endif
|
#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) {
|
if (coords_geo.x < 0.0 || 1.0 < coords_geo.x || coords_geo.y < 0.0 || 1.0 < coords_geo.y) {
|
||||||
// Clip outside geometry.
|
// Clip outside geometry.
|
||||||
color = vec4(0.0);
|
color = vec4(0.0);
|
||||||
|
|
@ -68,8 +80,20 @@ void main() {
|
||||||
color = color * rounding_alpha(coords_geo.xy * geo_size, geo_size);
|
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.
|
// Apply final alpha and tint.
|
||||||
color = color * alpha;
|
color *= alpha;
|
||||||
|
|
||||||
#if defined(DEBUG_FLAGS)
|
#if defined(DEBUG_FLAGS)
|
||||||
if (tint == 1.0)
|
if (tint == 1.0)
|
||||||
|
|
|
||||||
|
|
@ -1,6 +1,6 @@
|
||||||
use std::{borrow::Borrow, cell::RefCell, collections::HashMap};
|
use std::{borrow::Borrow, cell::RefCell, collections::HashMap};
|
||||||
|
|
||||||
use cgmath::{Matrix3, Vector2};
|
use glam::{Affine2, Mat3, Vec2};
|
||||||
use smithay::{
|
use smithay::{
|
||||||
backend::renderer::{
|
backend::renderer::{
|
||||||
element::Kind,
|
element::Kind,
|
||||||
|
|
@ -113,26 +113,26 @@ impl ShadowShader {
|
||||||
let mut shader_geo = Rectangle::new(Point::from((-width, -width)), shader_size);
|
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 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 area_size = Vec2::new(shader_geo.size.w as f32, shader_geo.size.h as f32);
|
||||||
let geo_loc = Vector2::new(-shader_geo.loc.x, -shader_geo.loc.y);
|
let geo_loc = Vec2::new(-shader_geo.loc.x as f32, -shader_geo.loc.y as f32);
|
||||||
shader_geo.loc += offset + geo.loc;
|
shader_geo.loc += offset + geo.loc;
|
||||||
|
|
||||||
let input_to_geo = (Matrix3::from_nonuniform_scale(area_size.x, area_size.y)
|
let input_to_geo = Mat3::from(
|
||||||
* Matrix3::from_translation(Vector2::new(
|
Affine2::from_scale(area_size)
|
||||||
-geo_loc.x / area_size.x,
|
* Affine2::from_translation(Vec2::new(
|
||||||
-geo_loc.y / area_size.y,
|
-geo_loc.x / area_size.x,
|
||||||
)))
|
-geo_loc.y / area_size.y,
|
||||||
.cast::<f32>()
|
)),
|
||||||
.unwrap();
|
);
|
||||||
|
|
||||||
let window_geo_loc = Vector2::new(window_geo.loc.x, window_geo.loc.y);
|
let window_geo_loc = Vec2::new(window_geo.loc.x as f32, window_geo.loc.y as f32);
|
||||||
let window_input_to_geo = (Matrix3::from_nonuniform_scale(area_size.x, area_size.y)
|
let window_input_to_geo = Mat3::from(
|
||||||
* Matrix3::from_translation(Vector2::new(
|
Affine2::from_scale(area_size)
|
||||||
-window_geo_loc.x / area_size.x,
|
* Affine2::from_translation(Vec2::new(
|
||||||
-window_geo_loc.y / area_size.y,
|
-window_geo_loc.x / area_size.x,
|
||||||
)))
|
-window_geo_loc.y / area_size.y,
|
||||||
.cast::<f32>()
|
)),
|
||||||
.unwrap();
|
);
|
||||||
|
|
||||||
let element = PixelShaderElement::new(
|
let element = PixelShaderElement::new(
|
||||||
shader,
|
shader,
|
||||||
|
|
|
||||||
705
src/backend/render/wayland/blur_effect.rs
Normal file
705
src/backend/render/wayland/blur_effect.rs
Normal file
|
|
@ -0,0 +1,705 @@
|
||||||
|
use std::{
|
||||||
|
borrow::{Borrow, BorrowMut},
|
||||||
|
sync::{LazyLock, Mutex},
|
||||||
|
};
|
||||||
|
|
||||||
|
use glam::{Affine2, Mat3, Vec2};
|
||||||
|
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 tracing::trace;
|
||||||
|
|
||||||
|
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 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<Vec<BlurParameters>> = 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::<f64>();
|
||||||
|
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,
|
||||||
|
});
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
trace!("Computed blur values: {:#?}", ¶ms);
|
||||||
|
params
|
||||||
|
});
|
||||||
|
|
||||||
|
#[derive(Debug, Clone)]
|
||||||
|
pub struct BlurShaders {
|
||||||
|
down: GlesTexProgram,
|
||||||
|
up: GlesTexProgram,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl BlurShaders {
|
||||||
|
pub fn compile(renderer: &mut GlesRenderer) -> Result<BlurShaders, GlesError> {
|
||||||
|
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<R: AsGlowRenderer>(renderer: &R) -> Self {
|
||||||
|
Borrow::<GlesRenderer>::borrow(renderer.glow_renderer())
|
||||||
|
.egl_context()
|
||||||
|
.user_data()
|
||||||
|
.get::<BlurShaders>()
|
||||||
|
.expect("Custom Shaders not initialized")
|
||||||
|
.clone()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
type BlurTexture<T> = Mutex<Option<T>>;
|
||||||
|
|
||||||
|
#[derive(Debug)]
|
||||||
|
pub struct BlurState {
|
||||||
|
pub id: Id,
|
||||||
|
pub renderer_id: Option<ContextId<GlesTexture>>,
|
||||||
|
pub src: Size<f64, Buffer>,
|
||||||
|
pub offset: f64,
|
||||||
|
pub passes: usize,
|
||||||
|
pub region: Vec<Rectangle<i32, Logical>>,
|
||||||
|
pub commit: CommitCounter,
|
||||||
|
}
|
||||||
|
|
||||||
|
unsafe impl Send for BlurState {}
|
||||||
|
unsafe impl Sync for BlurState {}
|
||||||
|
|
||||||
|
impl Default for BlurState {
|
||||||
|
fn default() -> Self {
|
||||||
|
BlurState {
|
||||||
|
id: Id::new(),
|
||||||
|
renderer_id: None,
|
||||||
|
src: Size::new(0., 0.),
|
||||||
|
offset: 0.,
|
||||||
|
passes: 0,
|
||||||
|
region: Vec::new(),
|
||||||
|
commit: CommitCounter::default(),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
pub struct BlurElement {
|
||||||
|
id: Id,
|
||||||
|
commit: CommitCounter,
|
||||||
|
src: Size<f64, Buffer>,
|
||||||
|
extended_offset: Point<f64, Logical>,
|
||||||
|
geometry: Rectangle<f64, Logical>,
|
||||||
|
scaling_shaders: BlurShaders,
|
||||||
|
render_shader: GlesTexProgram,
|
||||||
|
region: Vec<Rectangle<i32, Logical>>,
|
||||||
|
offset: f64,
|
||||||
|
passes: usize,
|
||||||
|
uniforms: Vec<Uniform<'static>>,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl BlurElement {
|
||||||
|
pub fn from_state<R: ImportAll + AsGlowRenderer>(
|
||||||
|
renderer: &mut R,
|
||||||
|
state: &mut BlurState,
|
||||||
|
geometry: Rectangle<f64, Logical>,
|
||||||
|
output_scale: f64,
|
||||||
|
radii: [u8; 4],
|
||||||
|
strength: usize,
|
||||||
|
) -> Result<Option<Self>, 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<R: ImportAll + AsGlowRenderer>(
|
||||||
|
renderer: &mut R,
|
||||||
|
states: &SurfaceData,
|
||||||
|
geometry: Rectangle<f64, Logical>,
|
||||||
|
output_scale: f64,
|
||||||
|
radii: [u8; 4],
|
||||||
|
strength: usize,
|
||||||
|
) -> Result<Option<Self>, R::Error> {
|
||||||
|
let mut blur_region_state = states.cached_state.get::<ComputedBlurRegionCachedState>();
|
||||||
|
let Some(region) = blur_region_state.current().blur_region.as_ref() else {
|
||||||
|
return Ok(None);
|
||||||
|
};
|
||||||
|
|
||||||
|
let state = states
|
||||||
|
.data_map
|
||||||
|
.get_or_insert_threadsafe::<Mutex<BlurState>, _>(Default::default);
|
||||||
|
|
||||||
|
Self::internal(
|
||||||
|
renderer,
|
||||||
|
&mut state.lock().unwrap(),
|
||||||
|
geometry,
|
||||||
|
region,
|
||||||
|
output_scale,
|
||||||
|
radii,
|
||||||
|
strength,
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn internal<R: ImportAll + AsGlowRenderer>(
|
||||||
|
renderer: &mut R,
|
||||||
|
state: &mut BlurState,
|
||||||
|
geometry: Rectangle<f64, Logical>,
|
||||||
|
region: &Vec<Rectangle<i32, Logical>>,
|
||||||
|
output_scale: f64,
|
||||||
|
radii: [u8; 4],
|
||||||
|
strength: usize,
|
||||||
|
) -> Result<Option<Self>, R::Error> {
|
||||||
|
if strength == 0 || geometry.size.w == 0. || geometry.size.h == 0. {
|
||||||
|
return Ok(None);
|
||||||
|
}
|
||||||
|
|
||||||
|
let geo = geometry.to_physical_precise_round(output_scale);
|
||||||
|
let mut extended_geo = geo;
|
||||||
|
let radius = BLUR_PARAMS[(strength + 2).min(MAX_STEPS - 1)].extended_radius as f64;
|
||||||
|
extended_geo.loc -= Point::<f64, Physical>::new(radius, radius);
|
||||||
|
extended_geo.size += Size::<f64, Physical>::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;
|
||||||
|
Affine2::from_scale(Vec2::new(x as f32, y as f32)).inverse()
|
||||||
|
};
|
||||||
|
let geo_translation = {
|
||||||
|
let offset = geo.loc - extended_geo.loc;
|
||||||
|
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 = Mat3::from(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 geometry = extended_geo.to_logical(output_scale);
|
||||||
|
let extended_offset = Point::<f64, Physical>::new(radius, radius).to_logical(output_scale);
|
||||||
|
|
||||||
|
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 = params.offset;
|
||||||
|
state.passes = params.passes;
|
||||||
|
state.region = region.clone();
|
||||||
|
state.src = src;
|
||||||
|
if dirty {
|
||||||
|
state.commit.increment();
|
||||||
|
}
|
||||||
|
|
||||||
|
Ok(Some(BlurElement {
|
||||||
|
id: state.id.clone(),
|
||||||
|
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()
|
||||||
|
.cloned()
|
||||||
|
.map(|mut rect| {
|
||||||
|
rect.loc += extended_offset.to_i32_round();
|
||||||
|
rect
|
||||||
|
})
|
||||||
|
.collect(),
|
||||||
|
uniforms,
|
||||||
|
}))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl Element for BlurElement {
|
||||||
|
fn id(&self) -> &Id {
|
||||||
|
&self.id
|
||||||
|
}
|
||||||
|
|
||||||
|
fn current_commit(&self) -> CommitCounter {
|
||||||
|
self.commit
|
||||||
|
}
|
||||||
|
|
||||||
|
fn src(&self) -> Rectangle<f64, Buffer> {
|
||||||
|
Rectangle::from_size(self.src)
|
||||||
|
}
|
||||||
|
|
||||||
|
fn geometry(&self, scale: Scale<f64>) -> Rectangle<i32, Physical> {
|
||||||
|
self.geometry.to_physical_precise_round(scale)
|
||||||
|
}
|
||||||
|
|
||||||
|
fn transform(&self) -> Transform {
|
||||||
|
Transform::Normal
|
||||||
|
}
|
||||||
|
|
||||||
|
fn damage_since(
|
||||||
|
&self,
|
||||||
|
scale: Scale<f64>,
|
||||||
|
commit: Option<CommitCounter>,
|
||||||
|
) -> DamageSet<i32, Physical> {
|
||||||
|
if self.commit.distance(commit).is_none_or(|d| d > 0) {
|
||||||
|
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()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fn alpha(&self) -> f32 {
|
||||||
|
1.0
|
||||||
|
}
|
||||||
|
|
||||||
|
fn kind(&self) -> Kind {
|
||||||
|
Kind::default()
|
||||||
|
}
|
||||||
|
|
||||||
|
fn is_framebuffer_effect(&self) -> bool {
|
||||||
|
true
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl<R: Renderer + AsGlowRenderer> RenderElement<R> for BlurElement
|
||||||
|
where
|
||||||
|
R::TextureId: Send + 'static,
|
||||||
|
{
|
||||||
|
fn capture_framebuffer(
|
||||||
|
&self,
|
||||||
|
frame: &mut <R>::Frame<'_, '_>,
|
||||||
|
src: Rectangle<f64, Buffer>,
|
||||||
|
dst: Rectangle<i32, Physical>,
|
||||||
|
cache: &UserDataMap,
|
||||||
|
) -> Result<(), <R>::Error> {
|
||||||
|
let transform = frame.transformation();
|
||||||
|
let tex_size = self.src.to_i32_round();
|
||||||
|
let glow_frame = <R as AsGlowRenderer>::glow_frame_mut(frame);
|
||||||
|
let gles_frame = BorrowMut::<GlesFrame<'_, '_>>::borrow_mut(glow_frame);
|
||||||
|
let mut renderer = gles_frame.renderer();
|
||||||
|
|
||||||
|
let texture_ref = cache.get_or_insert_threadsafe(BlurTexture::<R::TextureId>::default);
|
||||||
|
let mut texture_entry = texture_ref.lock().unwrap();
|
||||||
|
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();
|
||||||
|
}
|
||||||
|
if texture_entry.is_none() {
|
||||||
|
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 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, &mut texture)
|
||||||
|
.map_err(R::from_gles_error)?;
|
||||||
|
gles_frame.wait(&sync).map_err(R::from_gles_error)?;
|
||||||
|
|
||||||
|
let mut textures = [&mut 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<f64, Buffer>,
|
||||||
|
dst: Rectangle<i32, Physical>,
|
||||||
|
damage: &[Rectangle<i32, Physical>],
|
||||||
|
opaque_regions: &[Rectangle<i32, Physical>],
|
||||||
|
cache: Option<&UserDataMap>,
|
||||||
|
) -> Result<(), R::Error> {
|
||||||
|
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| {
|
||||||
|
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::<Vec<_>>();
|
||||||
|
let cache = cache.expect("Framebuffer element without cache?");
|
||||||
|
let Some(texture) = cache.get::<BlurTexture<R::TextureId>>() else {
|
||||||
|
return Err(R::from_gles_error(GlesError::BlitError));
|
||||||
|
};
|
||||||
|
let texture_ref = texture.lock().unwrap();
|
||||||
|
|
||||||
|
if let Some(tex) = texture_ref.as_ref() {
|
||||||
|
BorrowMut::<GlesFrame>::borrow_mut(<R as AsGlowRenderer>::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,
|
||||||
|
Transform::Normal,
|
||||||
|
1.0,
|
||||||
|
)?;
|
||||||
|
BorrowMut::<GlesFrame>::borrow_mut(<R as AsGlowRenderer>::glow_frame_mut(frame))
|
||||||
|
.clear_tex_program_override();
|
||||||
|
}
|
||||||
|
Ok(())
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fn blit_from_active_fb(
|
||||||
|
frame: &mut GlesFrame<'_, '_>,
|
||||||
|
src: Rectangle<f64, Buffer>,
|
||||||
|
dst: Rectangle<i32, Physical>,
|
||||||
|
transform: Transform,
|
||||||
|
to_texture: &mut GlesTexture,
|
||||||
|
) -> Result<SyncPoint, GlesError> {
|
||||||
|
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();
|
||||||
|
let mut fb = renderer.as_mut().bind(to_texture)?;
|
||||||
|
let sync = {
|
||||||
|
let mut subframe = renderer
|
||||||
|
.as_mut()
|
||||||
|
.render(&mut fb, tex_size_phys, Transform::Normal)?;
|
||||||
|
subframe.clear(
|
||||||
|
Color32F::TRANSPARENT,
|
||||||
|
&[Rectangle::from_size(tex_size_phys)],
|
||||||
|
)?;
|
||||||
|
subframe.finish()?
|
||||||
|
};
|
||||||
|
|
||||||
|
if transform != Transform::Normal {
|
||||||
|
// 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::Normal, &Size::default());
|
||||||
|
let mut tmp_texture = renderer
|
||||||
|
.as_mut()
|
||||||
|
.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)?;
|
||||||
|
|
||||||
|
let sync = frame.blit_to(
|
||||||
|
&mut fb_tmp,
|
||||||
|
dst_phys,
|
||||||
|
Rectangle::from_size(dst_phys.size),
|
||||||
|
TextureFilter::Linear,
|
||||||
|
)?;
|
||||||
|
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_phys, Transform::Normal)?;
|
||||||
|
frame.wait(&sync)?;
|
||||||
|
Frame::render_texture_from_to(
|
||||||
|
&mut frame,
|
||||||
|
&tmp_texture,
|
||||||
|
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.size)],
|
||||||
|
&[Rectangle::from_size(dst.size)],
|
||||||
|
transform.invert(),
|
||||||
|
1.0,
|
||||||
|
)?;
|
||||||
|
std::mem::drop(tmp_texture);
|
||||||
|
frame.finish()
|
||||||
|
} else {
|
||||||
|
std::mem::drop(renderer);
|
||||||
|
frame.wait(&sync)?;
|
||||||
|
frame.blit_to(
|
||||||
|
&mut fb,
|
||||||
|
dst,
|
||||||
|
src.to_logical(1., Transform::Normal, &Size::default())
|
||||||
|
.to_physical(1.)
|
||||||
|
.to_i32_round(),
|
||||||
|
TextureFilter::Linear,
|
||||||
|
)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
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(())
|
||||||
|
}
|
||||||
|
|
@ -2,11 +2,11 @@
|
||||||
|
|
||||||
use std::borrow::{Borrow, BorrowMut};
|
use std::borrow::{Borrow, BorrowMut};
|
||||||
|
|
||||||
use cgmath::{Matrix3, Vector2};
|
use glam::{Affine2, Mat3, Vec2};
|
||||||
use smithay::utils::{Buffer, Logical, Physical, Point, Rectangle, Scale, Size, Transform};
|
use smithay::utils::{Buffer, Logical, Physical, Point, Rectangle, Scale, Size, Transform};
|
||||||
use smithay::{
|
use smithay::{
|
||||||
backend::renderer::{
|
backend::renderer::{
|
||||||
ImportAll, ImportMem, Renderer,
|
ImportAll, Renderer,
|
||||||
element::{
|
element::{
|
||||||
Element, Id, Kind, RenderElement, UnderlyingStorage,
|
Element, Id, Kind, RenderElement, UnderlyingStorage,
|
||||||
surface::WaylandSurfaceRenderElement,
|
surface::WaylandSurfaceRenderElement,
|
||||||
|
|
@ -19,7 +19,7 @@ use smithay::{
|
||||||
|
|
||||||
use crate::backend::render::element::AsGlowRenderer;
|
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);
|
pub struct ClippingShader(pub GlesTexProgram);
|
||||||
|
|
||||||
impl ClippingShader {
|
impl ClippingShader {
|
||||||
|
|
@ -35,10 +35,7 @@ impl ClippingShader {
|
||||||
}
|
}
|
||||||
|
|
||||||
#[derive(Debug)]
|
#[derive(Debug)]
|
||||||
pub struct ClippedSurfaceRenderElement<R>
|
pub struct ClippedSurfaceRenderElement<R: Renderer> {
|
||||||
where
|
|
||||||
R: Renderer + ImportAll + ImportMem,
|
|
||||||
{
|
|
||||||
inner: WaylandSurfaceRenderElement<R>,
|
inner: WaylandSurfaceRenderElement<R>,
|
||||||
program: GlesTexProgram,
|
program: GlesTexProgram,
|
||||||
radius: [u8; 4],
|
radius: [u8; 4],
|
||||||
|
|
@ -48,7 +45,7 @@ where
|
||||||
|
|
||||||
impl<R> ClippedSurfaceRenderElement<R>
|
impl<R> ClippedSurfaceRenderElement<R>
|
||||||
where
|
where
|
||||||
R: Renderer + ImportAll + ImportMem,
|
R: Renderer + ImportAll,
|
||||||
{
|
{
|
||||||
pub fn new(
|
pub fn new(
|
||||||
renderer: &mut R,
|
renderer: &mut R,
|
||||||
|
|
@ -66,18 +63,18 @@ where
|
||||||
let view = elem.view();
|
let view = elem.view();
|
||||||
|
|
||||||
let transform = elem.transform();
|
let transform = elem.transform();
|
||||||
let transform_matrix = Matrix3::<f32>::from_translation(Vector2::new(0.5, 0.5))
|
let transform_matrix = Affine2::from_translation(Vec2::new(0.5, 0.5))
|
||||||
* transform.matrix()
|
* transform.matrix()
|
||||||
* Matrix3::<f32>::from_translation(-Vector2::new(0.5, 0.5));
|
* Affine2::from_translation(-Vec2::new(0.5, 0.5));
|
||||||
|
|
||||||
let geo_scale = {
|
let geo_scale = {
|
||||||
let Scale { x, y } = elem_geo.size.to_f64() / geo.size.to_f64();
|
let Scale { x, y } = elem_geo.size.to_f64() / geo.size.to_f64();
|
||||||
Matrix3::from_nonuniform_scale(x as f32, y as f32)
|
Affine2::from_scale(Vec2::new(x as f32, y as f32))
|
||||||
};
|
};
|
||||||
|
|
||||||
let geo_translation = {
|
let geo_translation = {
|
||||||
let offset = (elem_geo.loc - geo.loc).to_f64();
|
let offset = (elem_geo.loc - geo.loc).to_f64();
|
||||||
Matrix3::from_translation(Vector2::new(
|
Affine2::from_translation(Vec2::new(
|
||||||
(offset.x / elem_geo.size.w as f64) as f32,
|
(offset.x / elem_geo.size.w as f64) as f32,
|
||||||
(offset.y / elem_geo.size.h as f64) as f32,
|
(offset.y / elem_geo.size.h as f64) as f32,
|
||||||
))
|
))
|
||||||
|
|
@ -85,16 +82,17 @@ where
|
||||||
|
|
||||||
let buf_scale = {
|
let buf_scale = {
|
||||||
let Scale { x, y } = buf_size.to_f64() / view.src.size.to_f64();
|
let Scale { x, y } = buf_size.to_f64() / view.src.size.to_f64();
|
||||||
Matrix3::from_nonuniform_scale(x as f32, y as f32)
|
Affine2::from_scale(Vec2::new(x as f32, y as f32))
|
||||||
};
|
};
|
||||||
|
|
||||||
let buf_translation = Matrix3::from_translation(Vector2::new(
|
let buf_translation = Affine2::from_translation(Vec2::new(
|
||||||
(view.src.loc.x / buf_size.w as f64) as f32,
|
(view.src.loc.x / buf_size.w as f64) as f32,
|
||||||
(view.src.loc.y / buf_size.h as f64) as f32,
|
(view.src.loc.y / buf_size.h as f64) as f32,
|
||||||
));
|
));
|
||||||
|
|
||||||
let input_to_geo =
|
let input_to_geo = Mat3::from(
|
||||||
transform_matrix * geo_scale * geo_translation * buf_scale * buf_translation;
|
transform_matrix * geo_scale * geo_translation * buf_scale * buf_translation,
|
||||||
|
);
|
||||||
|
|
||||||
let uniforms = vec![
|
let uniforms = vec![
|
||||||
Uniform::new("geo_size", (geometry.size.w as f32, geometry.size.h as f32)),
|
Uniform::new("geo_size", (geometry.size.w as f32, geometry.size.h as f32)),
|
||||||
|
|
@ -114,6 +112,7 @@ where
|
||||||
transpose: false,
|
transpose: false,
|
||||||
},
|
},
|
||||||
),
|
),
|
||||||
|
Uniform::new("noise", UniformValue::_1f(0.0)),
|
||||||
];
|
];
|
||||||
|
|
||||||
Self {
|
Self {
|
||||||
|
|
@ -174,7 +173,8 @@ where
|
||||||
|
|
||||||
impl<R> Element for ClippedSurfaceRenderElement<R>
|
impl<R> Element for ClippedSurfaceRenderElement<R>
|
||||||
where
|
where
|
||||||
R: Renderer + ImportAll + ImportMem,
|
R: Renderer + ImportAll + AsGlowRenderer,
|
||||||
|
R::TextureId: 'static,
|
||||||
{
|
{
|
||||||
fn id(&self) -> &Id {
|
fn id(&self) -> &Id {
|
||||||
self.inner.id()
|
self.inner.id()
|
||||||
|
|
@ -247,7 +247,7 @@ where
|
||||||
|
|
||||||
impl<R> RenderElement<R> for ClippedSurfaceRenderElement<R>
|
impl<R> RenderElement<R> for ClippedSurfaceRenderElement<R>
|
||||||
where
|
where
|
||||||
R: AsGlowRenderer + Renderer + ImportAll + ImportMem,
|
R: AsGlowRenderer + Renderer + ImportAll,
|
||||||
R::TextureId: 'static,
|
R::TextureId: 'static,
|
||||||
{
|
{
|
||||||
fn draw(
|
fn draw(
|
||||||
145
src/backend/render/wayland/mod.rs
Normal file
145
src/backend/render/wayland/mod.rs
Normal file
|
|
@ -0,0 +1,145 @@
|
||||||
|
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::{blur_effect::BlurElement, clipped_surface::ClippedSurfaceRenderElement},
|
||||||
|
};
|
||||||
|
|
||||||
|
pub mod blur_effect;
|
||||||
|
pub mod clipped_surface;
|
||||||
|
|
||||||
|
render_elements! {
|
||||||
|
pub SurfaceRenderElement<R> where R: AsGlowRenderer + ImportAll, R::TextureId: Send;
|
||||||
|
Blur=BlurElement,
|
||||||
|
Clipped=ClippedSurfaceRenderElement<R>,
|
||||||
|
Wayland=WaylandSurfaceRenderElement<R>,
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn push_render_elements_from_surface_tree<R>(
|
||||||
|
renderer: &mut R,
|
||||||
|
main_surface: &wl_surface::WlSurface,
|
||||||
|
location: impl Into<Point<i32, Physical>>,
|
||||||
|
geometry: impl Into<Rectangle<f64, Logical>>,
|
||||||
|
scale: impl Into<Scale<f64>>,
|
||||||
|
alpha: f32,
|
||||||
|
should_clip: bool,
|
||||||
|
radii: [u8; 4],
|
||||||
|
blur_geometry: impl Into<Option<Rectangle<f64, Logical>>>,
|
||||||
|
blur_strength: usize,
|
||||||
|
kind: impl Into<KindEvaluation>,
|
||||||
|
push_above: &mut dyn FnMut(SurfaceRenderElement<R>),
|
||||||
|
mut push_below: Option<&mut dyn FnMut(SurfaceRenderElement<R>)>,
|
||||||
|
) where
|
||||||
|
R: Renderer + ImportAll + AsGlowRenderer,
|
||||||
|
R::TextureId: Clone + 'static,
|
||||||
|
{
|
||||||
|
let location = location.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;
|
||||||
|
|
||||||
|
compositor::with_surface_tree_downward(
|
||||||
|
main_surface,
|
||||||
|
location,
|
||||||
|
|_, states, location| {
|
||||||
|
let mut location = *location;
|
||||||
|
let data = states.data_map.get::<RendererSurfaceStateUserData>();
|
||||||
|
|
||||||
|
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::<RendererSurfaceStateUserData>();
|
||||||
|
let mut blur = Ok(None);
|
||||||
|
|
||||||
|
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 blur_geo = blur_geometry.unwrap_or(geometry);
|
||||||
|
blur = BlurElement::from_surface(
|
||||||
|
renderer,
|
||||||
|
states,
|
||||||
|
blur_geo,
|
||||||
|
scale.x,
|
||||||
|
radii,
|
||||||
|
blur_strength,
|
||||||
|
);
|
||||||
|
let elem: SurfaceRenderElement<R> = 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()
|
||||||
|
};
|
||||||
|
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) => {
|
||||||
|
warn!("Failed to import surface: {}", err);
|
||||||
|
}
|
||||||
|
};
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
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,
|
||||||
|
);
|
||||||
|
}
|
||||||
261
src/input/mod.rs
261
src/input/mod.rs
|
|
@ -25,7 +25,8 @@ use crate::{
|
||||||
},
|
},
|
||||||
utils::{prelude::*, quirks::workspace_overview_is_open},
|
utils::{prelude::*, quirks::workspace_overview_is_open},
|
||||||
wayland::handlers::{
|
wayland::handlers::{
|
||||||
image_copy_capture::SessionHolder, xwayland_keyboard_grab::XWaylandGrabSeat,
|
image_copy_capture::{SessionHolder, cursor_capture_constraints},
|
||||||
|
xwayland_keyboard_grab::XWaylandGrabSeat,
|
||||||
},
|
},
|
||||||
};
|
};
|
||||||
use calloop::{
|
use calloop::{
|
||||||
|
|
@ -35,14 +36,15 @@ use calloop::{
|
||||||
use cosmic_comp_config::{NumlockState, workspace::WorkspaceLayout};
|
use cosmic_comp_config::{NumlockState, workspace::WorkspaceLayout};
|
||||||
use cosmic_settings_config::shortcuts;
|
use cosmic_settings_config::shortcuts;
|
||||||
use cosmic_settings_config::shortcuts::action::{Direction, ResizeDirection};
|
use cosmic_settings_config::shortcuts::action::{Direction, ResizeDirection};
|
||||||
|
#[cfg(feature = "logind")]
|
||||||
|
use smithay::backend::input::{Switch, SwitchState, SwitchToggleEvent};
|
||||||
use smithay::{
|
use smithay::{
|
||||||
backend::input::{
|
backend::input::{
|
||||||
AbsolutePositionEvent, Axis, AxisRelativeDirection, AxisSource, Device, DeviceCapability,
|
AbsolutePositionEvent, Axis, AxisRelativeDirection, AxisSource, Device, DeviceCapability,
|
||||||
GestureBeginEvent, GestureEndEvent, GesturePinchUpdateEvent as _,
|
GestureBeginEvent, GestureEndEvent, GesturePinchUpdateEvent as _,
|
||||||
GestureSwipeUpdateEvent as _, InputBackend, InputEvent, KeyState, KeyboardKeyEvent,
|
GestureSwipeUpdateEvent as _, InputBackend, InputEvent, KeyState, KeyboardKeyEvent,
|
||||||
PointerAxisEvent, ProximityState, Switch, SwitchState, SwitchToggleEvent,
|
PointerAxisEvent, ProximityState, TabletToolButtonEvent, TabletToolEvent,
|
||||||
TabletToolButtonEvent, TabletToolEvent, TabletToolProximityEvent, TabletToolTipEvent,
|
TabletToolProximityEvent, TabletToolTipEvent, TabletToolTipState, TouchEvent,
|
||||||
TabletToolTipState, TouchEvent,
|
|
||||||
},
|
},
|
||||||
desktop::{PopupKeyboardGrab, WindowSurfaceType, utils::under_from_surface_tree},
|
desktop::{PopupKeyboardGrab, WindowSurfaceType, utils::under_from_surface_tree},
|
||||||
input::{
|
input::{
|
||||||
|
|
@ -59,22 +61,19 @@ use smithay::{
|
||||||
output::Output,
|
output::Output,
|
||||||
reexports::{
|
reexports::{
|
||||||
input::Device as InputDevice,
|
input::Device as InputDevice,
|
||||||
wayland_server::{
|
wayland_server::{Resource as _, protocol::wl_surface::WlSurface},
|
||||||
Resource as _,
|
|
||||||
protocol::{wl_shm::Format as ShmFormat, wl_surface::WlSurface},
|
|
||||||
},
|
|
||||||
},
|
},
|
||||||
utils::{Logical, Point, Rectangle, SERIAL_COUNTER, Serial, Size},
|
utils::{Clock, Logical, Monotonic, Point, Rectangle, SERIAL_COUNTER, Serial},
|
||||||
wayland::{
|
wayland::{
|
||||||
compositor::CompositorHandler,
|
compositor::CompositorHandler,
|
||||||
image_copy_capture::{BufferConstraints, CursorSessionRef},
|
image_copy_capture::CursorSessionRef,
|
||||||
keyboard_shortcuts_inhibit::KeyboardShortcutsInhibitorSeat,
|
keyboard_shortcuts_inhibit::KeyboardShortcutsInhibitorSeat,
|
||||||
pointer_constraints::{PointerConstraint, with_pointer_constraint},
|
pointer_constraints::{PointerConstraint, with_pointer_constraint},
|
||||||
seat::WaylandFocus,
|
seat::WaylandFocus,
|
||||||
tablet_manager::{TabletDescriptor, TabletSeatTrait},
|
tablet_manager::{TabletDescriptor, TabletSeatTrait},
|
||||||
},
|
},
|
||||||
};
|
};
|
||||||
use tracing::{error, trace, warn};
|
use tracing::{error, trace};
|
||||||
use xkbcommon::xkb::{Keycode, Keysym};
|
use xkbcommon::xkb::{Keycode, Keysym};
|
||||||
|
|
||||||
use std::{
|
use std::{
|
||||||
|
|
@ -336,7 +335,7 @@ impl State {
|
||||||
// Constraint does not apply if not within region
|
// Constraint does not apply if not within region
|
||||||
if !constraint.region().is_none_or(|x| {
|
if !constraint.region().is_none_or(|x| {
|
||||||
x.contains(
|
x.contains(
|
||||||
(ptr.current_location() - *surface_loc).to_i32_round(),
|
(ptr.current_location() - *surface_loc).to_i32_floor(),
|
||||||
)
|
)
|
||||||
}) {
|
}) {
|
||||||
return;
|
return;
|
||||||
|
|
@ -381,14 +380,25 @@ impl State {
|
||||||
.unwrap_or(current_output.clone());
|
.unwrap_or(current_output.clone());
|
||||||
drop(shell);
|
drop(shell);
|
||||||
let output_geometry = output.geometry();
|
let output_geometry = output.geometry();
|
||||||
position.x = position.x.clamp(
|
|
||||||
output_geometry.loc.x as f64,
|
let scale = output.current_scale().fractional_scale();
|
||||||
(output_geometry.loc.x + output_geometry.size.w - 1) as f64,
|
let physical = output
|
||||||
);
|
.current_mode()
|
||||||
position.y = position.y.clamp(
|
.map(|mode| output.current_transform().transform_size(mode.size))
|
||||||
output_geometry.loc.y as f64,
|
.unwrap_or_default();
|
||||||
(output_geometry.loc.y + output_geometry.size.h - 1) as f64,
|
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 ptr.is_grabbed() {
|
||||||
if seat
|
if seat
|
||||||
|
|
@ -542,7 +552,7 @@ impl State {
|
||||||
|
|
||||||
if let Some(region) = &confine_region
|
if let Some(region) = &confine_region
|
||||||
&& !region
|
&& !region
|
||||||
.contains((pos.as_logical() - *surface_loc).to_i32_round())
|
.contains((pos.as_logical() - *surface_loc).to_i32_floor())
|
||||||
{
|
{
|
||||||
return (false, None);
|
return (false, None);
|
||||||
}
|
}
|
||||||
|
|
@ -613,7 +623,7 @@ impl State {
|
||||||
PointerConstraint::Confined(confined) => confined.region(),
|
PointerConstraint::Confined(confined) => confined.region(),
|
||||||
};
|
};
|
||||||
let point =
|
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)) {
|
if region.is_none_or(|region| region.contains(point)) {
|
||||||
constraint.activate();
|
constraint.activate();
|
||||||
}
|
}
|
||||||
|
|
@ -638,37 +648,13 @@ impl State {
|
||||||
seat.set_active_output(&output);
|
seat.set_active_output(&output);
|
||||||
}
|
}
|
||||||
|
|
||||||
for session in cursor_sessions_for_output(&shell, &output) {
|
update_output_image_copy_cursor_position(
|
||||||
if let Some((geometry, offset)) = seat.cursor_geometry(
|
&shell,
|
||||||
(position - output_geometry.loc.to_f64())
|
&self.common.clock,
|
||||||
.as_logical()
|
&output,
|
||||||
.to_buffer(
|
&seat,
|
||||||
output.current_scale().fractional_scale(),
|
position,
|
||||||
output.current_transform(),
|
);
|
||||||
&output_geometry.size.to_f64().as_logical(),
|
|
||||||
),
|
|
||||||
self.common.clock.now(),
|
|
||||||
) {
|
|
||||||
if session
|
|
||||||
.current_constraints()
|
|
||||||
.map(|constraint| constraint.size != geometry.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.set_cursor_hotspot(offset);
|
|
||||||
session.set_cursor_pos(Some(geometry.loc));
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
InputEvent::PointerMotionAbsolute { event, .. } => {
|
InputEvent::PointerMotionAbsolute { event, .. } => {
|
||||||
|
|
@ -707,37 +693,13 @@ impl State {
|
||||||
ptr.frame(self);
|
ptr.frame(self);
|
||||||
|
|
||||||
let shell = self.common.shell.read();
|
let shell = self.common.shell.read();
|
||||||
for session in cursor_sessions_for_output(&shell, &output) {
|
update_output_image_copy_cursor_position(
|
||||||
if let Some((geometry, offset)) = seat.cursor_geometry(
|
&shell,
|
||||||
(position - output_geometry.loc.to_f64())
|
&self.common.clock,
|
||||||
.as_logical()
|
&output,
|
||||||
.to_buffer(
|
&seat,
|
||||||
output.current_scale().fractional_scale(),
|
position,
|
||||||
output.current_transform(),
|
);
|
||||||
&output_geometry.size.to_f64().as_logical(),
|
|
||||||
),
|
|
||||||
self.common.clock.now(),
|
|
||||||
) {
|
|
||||||
if session
|
|
||||||
.current_constraints()
|
|
||||||
.map(|constraint| constraint.size != geometry.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.set_cursor_hotspot(offset);
|
|
||||||
session.set_cursor_pos(Some(geometry.loc));
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
InputEvent::PointerButton { event, .. } => {
|
InputEvent::PointerButton { event, .. } => {
|
||||||
|
|
@ -787,7 +749,12 @@ impl State {
|
||||||
let shell = self.common.shell.read();
|
let shell = self.common.shell.read();
|
||||||
State::element_under(global_position, &output, &shell, &seat)
|
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)
|
if let Some(surface) = target.toplevel().map(Cow::into_owned)
|
||||||
&& seat.get_keyboard().unwrap().modifier_state().logo
|
&& seat.get_keyboard().unwrap().modifier_state().logo
|
||||||
&& !shortcuts_inhibited
|
&& !shortcuts_inhibited
|
||||||
|
|
@ -1002,6 +969,10 @@ impl State {
|
||||||
if let Some(horizontal_amount) = event.amount(Axis::Horizontal) {
|
if let Some(horizontal_amount) = event.amount(Axis::Horizontal) {
|
||||||
if horizontal_amount != 0.0 {
|
if horizontal_amount != 0.0 {
|
||||||
frame = frame
|
frame = frame
|
||||||
|
.relative_direction(
|
||||||
|
Axis::Horizontal,
|
||||||
|
event.relative_direction(Axis::Horizontal),
|
||||||
|
)
|
||||||
.value(Axis::Horizontal, scroll_factor * horizontal_amount);
|
.value(Axis::Horizontal, scroll_factor * horizontal_amount);
|
||||||
if let Some(discrete) = event.amount_v120(Axis::Horizontal) {
|
if let Some(discrete) = event.amount_v120(Axis::Horizontal) {
|
||||||
frame = frame.v120(
|
frame = frame.v120(
|
||||||
|
|
@ -1015,8 +986,12 @@ impl State {
|
||||||
}
|
}
|
||||||
if let Some(vertical_amount) = event.amount(Axis::Vertical) {
|
if let Some(vertical_amount) = event.amount(Axis::Vertical) {
|
||||||
if vertical_amount != 0.0 {
|
if vertical_amount != 0.0 {
|
||||||
frame =
|
frame = frame
|
||||||
frame.value(Axis::Vertical, scroll_factor * vertical_amount);
|
.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) {
|
if let Some(discrete) = event.amount_v120(Axis::Vertical) {
|
||||||
frame = frame.v120(
|
frame = frame.v120(
|
||||||
Axis::Vertical,
|
Axis::Vertical,
|
||||||
|
|
@ -1599,6 +1574,7 @@ impl State {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
InputEvent::Special(_) => {}
|
InputEvent::Special(_) => {}
|
||||||
|
#[allow(unused_variables)]
|
||||||
InputEvent::SwitchToggle { event } => {
|
InputEvent::SwitchToggle { event } => {
|
||||||
#[cfg(feature = "logind")]
|
#[cfg(feature = "logind")]
|
||||||
if event.switch() == Some(Switch::Lid) && self.common.inhibit_lid_fd.is_some() {
|
if event.switch() == Some(Switch::Lid) && self.common.inhibit_lid_fd.is_some() {
|
||||||
|
|
@ -1620,7 +1596,7 @@ impl State {
|
||||||
|
|
||||||
if let Err(err) = self.refresh_output_config() {
|
if let Err(err) = self.refresh_output_config() {
|
||||||
if !closed {
|
if !closed {
|
||||||
warn!(?err, "Failed to re-enable internal connector");
|
tracing::warn!(?err, "Failed to re-enable internal connector");
|
||||||
if let Some(output) = output {
|
if let Some(output) = output {
|
||||||
use cosmic_comp_config::output::comp::OutputState;
|
use cosmic_comp_config::output::comp::OutputState;
|
||||||
|
|
||||||
|
|
@ -2132,6 +2108,7 @@ impl State {
|
||||||
layer,
|
layer,
|
||||||
popup,
|
popup,
|
||||||
location,
|
location,
|
||||||
|
..
|
||||||
} => {
|
} => {
|
||||||
if layer.can_receive_keyboard_focus() {
|
if layer.can_receive_keyboard_focus() {
|
||||||
let surface = popup.wl_surface();
|
let surface = popup.wl_surface();
|
||||||
|
|
@ -2149,7 +2126,9 @@ impl State {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
Stage::LayerSurface { layer, location } => {
|
Stage::LayerSurface {
|
||||||
|
layer, location, ..
|
||||||
|
} => {
|
||||||
if under_from_surface_tree(
|
if under_from_surface_tree(
|
||||||
layer.wl_surface(),
|
layer.wl_surface(),
|
||||||
global_pos.as_logical(),
|
global_pos.as_logical(),
|
||||||
|
|
@ -2192,7 +2171,7 @@ impl State {
|
||||||
if Rectangle::new(offset.as_local(), output_geo.size)
|
if Rectangle::new(offset.as_local(), output_geo.size)
|
||||||
.intersection(output_geo)
|
.intersection(output_geo)
|
||||||
.is_some_and(|geometry| {
|
.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)
|
&& let Some(element) = workspace.popup_element_under(location, seat)
|
||||||
{
|
{
|
||||||
|
|
@ -2206,7 +2185,7 @@ impl State {
|
||||||
if Rectangle::new(offset.as_local(), output_geo.size)
|
if Rectangle::new(offset.as_local(), output_geo.size)
|
||||||
.intersection(output_geo)
|
.intersection(output_geo)
|
||||||
.is_some_and(|geometry| {
|
.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)
|
&& let Some(element) = workspace.toplevel_element_under(location, seat)
|
||||||
{
|
{
|
||||||
|
|
@ -2301,7 +2280,9 @@ impl State {
|
||||||
))));
|
))));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
Stage::LayerSurface { layer, location } => {
|
Stage::LayerSurface {
|
||||||
|
layer, location, ..
|
||||||
|
} => {
|
||||||
let surface = layer.wl_surface();
|
let surface = layer.wl_surface();
|
||||||
if let Some((surface, surface_loc)) = under_from_surface_tree(
|
if let Some((surface, surface_loc)) = under_from_surface_tree(
|
||||||
surface,
|
surface,
|
||||||
|
|
@ -2407,7 +2388,7 @@ impl State {
|
||||||
if let Some(constraint) = constraint
|
if let Some(constraint) = constraint
|
||||||
&& let Some(region) = constraint.region()
|
&& 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);
|
return region.contains(point_in_surface);
|
||||||
}
|
}
|
||||||
true
|
true
|
||||||
|
|
@ -2420,7 +2401,7 @@ impl State {
|
||||||
if is_legal(pos_in_element) {
|
if is_legal(pos_in_element) {
|
||||||
let x = workspace_origin.x + origin.x + pos_in_element.x;
|
let x = workspace_origin.x + origin.x + pos_in_element.x;
|
||||||
let y = workspace_origin.y + origin.y + pos_in_element.y;
|
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 {
|
} else {
|
||||||
None
|
None
|
||||||
}
|
}
|
||||||
|
|
@ -2430,11 +2411,34 @@ impl State {
|
||||||
};
|
};
|
||||||
|
|
||||||
if let Some((point, output)) = point_and_output {
|
if let Some((point, output)) = point_and_output {
|
||||||
|
// TODO: Replace with `wl_pointer.warp`
|
||||||
let original_position = pointer.current_location();
|
let original_position = pointer.current_location();
|
||||||
pointer.set_location(point);
|
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();
|
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
|
let seat = shell
|
||||||
.seats
|
.seats
|
||||||
|
|
@ -2449,31 +2453,13 @@ impl State {
|
||||||
self.common.config.cosmic_conf.accessibility_zoom.view_moves,
|
self.common.config.cosmic_conf.accessibility_zoom.view_moves,
|
||||||
);
|
);
|
||||||
|
|
||||||
let output_geometry = output.geometry();
|
update_output_image_copy_cursor_position(
|
||||||
for session in cursor_sessions_for_output(&shell, &output) {
|
&shell,
|
||||||
if let Some((geometry, offset)) = seat.cursor_geometry(
|
&self.common.clock,
|
||||||
point.to_buffer(
|
&output,
|
||||||
output.current_scale().fractional_scale(),
|
&seat,
|
||||||
output.current_transform(),
|
point,
|
||||||
&output_geometry.size.to_f64().as_logical(),
|
);
|
||||||
),
|
|
||||||
self.common.clock.now(),
|
|
||||||
) {
|
|
||||||
if session
|
|
||||||
.current_constraints()
|
|
||||||
.map(|constraint| constraint.size != geometry.size)
|
|
||||||
.unwrap_or(true)
|
|
||||||
{
|
|
||||||
session.update_constraints(BufferConstraints {
|
|
||||||
size: geometry.size,
|
|
||||||
shm: vec![ShmFormat::Argb8888],
|
|
||||||
dma: None,
|
|
||||||
});
|
|
||||||
}
|
|
||||||
session.set_cursor_hotspot(offset);
|
|
||||||
session.set_cursor_pos(Some(geometry.loc));
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
@ -2534,3 +2520,36 @@ fn mapped_output_for_device<'a, D: Device + 'static>(
|
||||||
};
|
};
|
||||||
map_to_output.or_else(|| shell.builtin_output())
|
map_to_output.or_else(|| shell.builtin_output())
|
||||||
}
|
}
|
||||||
|
|
||||||
|
pub fn update_output_image_copy_cursor_position(
|
||||||
|
shell: &Shell,
|
||||||
|
clock: &Clock<Monotonic>,
|
||||||
|
output: &Output,
|
||||||
|
seat: &Seat<State>,
|
||||||
|
position: Point<f64, Global>,
|
||||||
|
) {
|
||||||
|
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));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
|
||||||
21
src/lib.rs
21
src/lib.rs
|
|
@ -26,7 +26,9 @@ use std::{
|
||||||
time::{Duration, Instant},
|
time::{Duration, Instant},
|
||||||
};
|
};
|
||||||
use tracing::{error, info, warn};
|
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;
|
use crate::wayland::handlers::compositor::client_compositor_state;
|
||||||
|
|
||||||
|
|
@ -79,11 +81,8 @@ impl State {
|
||||||
warn!(?err, "Failed to setup cosmic-session communication");
|
warn!(?err, "Failed to setup cosmic-session communication");
|
||||||
}
|
}
|
||||||
|
|
||||||
let mut args = env::args().skip(1);
|
self.common.kiosk_child = if let Some(mut command) = self.kiosk_command.take() {
|
||||||
self.common.kiosk_child = if let Some(exec) = args.next() {
|
|
||||||
// Run command in kiosk mode
|
// Run command in kiosk mode
|
||||||
let mut command = process::Command::new(&exec);
|
|
||||||
command.args(args);
|
|
||||||
command.envs(
|
command.envs(
|
||||||
session::get_env(&self.common).expect("WAYLAND_DISPLAY should be valid UTF-8"),
|
session::get_env(&self.common).expect("WAYLAND_DISPLAY should be valid UTF-8"),
|
||||||
);
|
);
|
||||||
|
|
@ -94,7 +93,7 @@ impl State {
|
||||||
})
|
})
|
||||||
};
|
};
|
||||||
|
|
||||||
info!("Running {:?}", exec);
|
info!("Running {:?}", command.get_program());
|
||||||
command
|
command
|
||||||
.spawn()
|
.spawn()
|
||||||
.map_err(|err| {
|
.map_err(|err| {
|
||||||
|
|
@ -113,8 +112,10 @@ impl State {
|
||||||
pub fn run(hooks: crate::hooks::Hooks) -> Result<(), Box<dyn Error>> {
|
pub fn run(hooks: crate::hooks::Hooks) -> Result<(), Box<dyn Error>> {
|
||||||
let raw_args = RawArgs::from_args();
|
let raw_args = RawArgs::from_args();
|
||||||
let mut cursor = raw_args.cursor();
|
let mut cursor = raw_args.cursor();
|
||||||
|
raw_args.next_os(&mut cursor);
|
||||||
let git_hash = option_env!("GIT_HASH").unwrap_or("unknown");
|
let git_hash = option_env!("GIT_HASH").unwrap_or("unknown");
|
||||||
|
|
||||||
|
let mut kiosk_command = None;
|
||||||
let mut with_xwayland = true;
|
let mut with_xwayland = true;
|
||||||
// Parse the arguments
|
// Parse the arguments
|
||||||
while let Some(arg) = raw_args.next_os(&mut cursor) {
|
while let Some(arg) = raw_args.next_os(&mut cursor) {
|
||||||
|
|
@ -135,7 +136,11 @@ pub fn run(hooks: crate::hooks::Hooks) -> Result<(), Box<dyn Error>> {
|
||||||
);
|
);
|
||||||
return Ok(());
|
return Ok(());
|
||||||
}
|
}
|
||||||
_ => {}
|
_ => {
|
||||||
|
let mut cmd = process::Command::new(arg);
|
||||||
|
cmd.args(raw_args.remaining(&mut cursor));
|
||||||
|
kiosk_command = Some(cmd);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -169,6 +174,7 @@ pub fn run(hooks: crate::hooks::Hooks) -> Result<(), Box<dyn Error>> {
|
||||||
event_loop.handle(),
|
event_loop.handle(),
|
||||||
event_loop.get_signal(),
|
event_loop.get_signal(),
|
||||||
with_xwayland,
|
with_xwayland,
|
||||||
|
kiosk_command,
|
||||||
);
|
);
|
||||||
// init backend
|
// init backend
|
||||||
backend::init_backend_auto(&display, &mut event_loop, &mut state)?;
|
backend::init_backend_auto(&display, &mut event_loop, &mut state)?;
|
||||||
|
|
@ -331,5 +337,6 @@ fn refresh(state: &mut State) {
|
||||||
state::Common::refresh_focus(state);
|
state::Common::refresh_focus(state);
|
||||||
OverlapNotifyState::refresh(state);
|
OverlapNotifyState::refresh(state);
|
||||||
state.common.update_x11_stacking_order();
|
state.common.update_x11_stacking_order();
|
||||||
|
KeyboardLayoutState::refresh(state);
|
||||||
state.last_refresh = LastRefresh::At(Instant::now());
|
state.last_refresh = LastRefresh::At(Instant::now());
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -1,18 +1,21 @@
|
||||||
use crate::{
|
use crate::{
|
||||||
backend::render::element::{AsGlowRenderer, FromGlesError},
|
backend::render::element::AsGlowRenderer,
|
||||||
state::State,
|
state::State,
|
||||||
utils::{iced::IcedElementInternal, prelude::*},
|
utils::{
|
||||||
|
iced::{IcedElementInternal, IcedRenderElement},
|
||||||
|
prelude::*,
|
||||||
|
},
|
||||||
};
|
};
|
||||||
use calloop::LoopHandle;
|
use calloop::LoopHandle;
|
||||||
use cosmic_comp_config::AppearanceConfig;
|
use cosmic_comp_config::AppearanceConfig;
|
||||||
use id_tree::NodeId;
|
use id_tree::NodeId;
|
||||||
use smithay::{
|
use smithay::{
|
||||||
backend::{
|
backend::{
|
||||||
|
drm::DrmNode,
|
||||||
input::KeyState,
|
input::KeyState,
|
||||||
renderer::{
|
renderer::{
|
||||||
element::{
|
element::{
|
||||||
Element, Kind, RenderElement, UnderlyingStorage,
|
Element, Kind, RenderElement, UnderlyingStorage,
|
||||||
memory::MemoryRenderBufferRenderElement,
|
|
||||||
utils::{CropRenderElement, RelocateRenderElement, RescaleRenderElement},
|
utils::{CropRenderElement, RelocateRenderElement, RescaleRenderElement},
|
||||||
},
|
},
|
||||||
gles::element::PixelShaderElement,
|
gles::element::PixelShaderElement,
|
||||||
|
|
@ -124,9 +127,9 @@ impl fmt::Debug for CosmicMapped {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
#[derive(Clone)]
|
#[derive(Clone, Debug)]
|
||||||
pub struct CosmicMappedKey(CosmicMappedKeyInner);
|
pub struct CosmicMappedKey(CosmicMappedKeyInner);
|
||||||
#[derive(Clone)]
|
#[derive(Clone, Debug)]
|
||||||
enum CosmicMappedKeyInner {
|
enum CosmicMappedKeyInner {
|
||||||
Window(Weak<Mutex<IcedElementInternal<CosmicWindowInternal>>>),
|
Window(Weak<Mutex<IcedElementInternal<CosmicWindowInternal>>>),
|
||||||
Stack(Weak<Mutex<IcedElementInternal<CosmicStackInternal>>>),
|
Stack(Weak<Mutex<IcedElementInternal<CosmicStackInternal>>>),
|
||||||
|
|
@ -165,6 +168,20 @@ impl PartialEq for CosmicMappedKey {
|
||||||
}
|
}
|
||||||
impl Eq for CosmicMappedKey {}
|
impl Eq for CosmicMappedKey {}
|
||||||
|
|
||||||
|
impl PartialEq<CosmicMappedKey> 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 {
|
impl PartialEq for CosmicMapped {
|
||||||
fn eq(&self, other: &Self) -> bool {
|
fn eq(&self, other: &Self) -> bool {
|
||||||
self.element == other.element
|
self.element == other.element
|
||||||
|
|
@ -583,33 +600,38 @@ impl CosmicMapped {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn popup_render_elements<R, C>(
|
pub fn push_popup_render_elements<R>(
|
||||||
&self,
|
&self,
|
||||||
renderer: &mut R,
|
renderer: &mut R,
|
||||||
location: smithay::utils::Point<i32, smithay::utils::Physical>,
|
location: smithay::utils::Point<i32, smithay::utils::Physical>,
|
||||||
scale: smithay::utils::Scale<f64>,
|
scale: smithay::utils::Scale<f64>,
|
||||||
alpha: f32,
|
alpha: f32,
|
||||||
) -> Vec<C>
|
scanout_node: Option<DrmNode>,
|
||||||
where
|
push: &mut dyn FnMut(CosmicMappedRenderElement<R>),
|
||||||
|
) where
|
||||||
R: AsGlowRenderer,
|
R: AsGlowRenderer,
|
||||||
R::TextureId: Send + Clone + 'static,
|
R::TextureId: Send + Clone + 'static,
|
||||||
CosmicMappedRenderElement<R>: RenderElement<R>,
|
CosmicMappedRenderElement<R>: RenderElement<R>,
|
||||||
C: From<CosmicMappedRenderElement<R>>,
|
|
||||||
{
|
{
|
||||||
match &self.element {
|
match &self.element {
|
||||||
CosmicMappedInternal::Stack(s) => s
|
CosmicMappedInternal::Stack(s) => s.push_popup_render_elements(
|
||||||
.popup_render_elements::<R, CosmicMappedRenderElement<R>>(
|
renderer,
|
||||||
renderer, location, scale, alpha,
|
location,
|
||||||
),
|
scale,
|
||||||
CosmicMappedInternal::Window(w) => w
|
alpha,
|
||||||
.popup_render_elements::<R, CosmicMappedRenderElement<R>>(
|
scanout_node,
|
||||||
renderer, location, scale, alpha,
|
&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!(),
|
_ => unreachable!(),
|
||||||
}
|
}
|
||||||
.into_iter()
|
|
||||||
.map(C::from)
|
|
||||||
.collect()
|
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn shadow_render_element<R, C>(
|
pub fn shadow_render_element<R, C>(
|
||||||
|
|
@ -656,7 +678,7 @@ impl CosmicMapped {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn render_elements<R, C>(
|
pub fn push_render_elements<R>(
|
||||||
&self,
|
&self,
|
||||||
renderer: &mut R,
|
renderer: &mut R,
|
||||||
location: smithay::utils::Point<i32, smithay::utils::Physical>,
|
location: smithay::utils::Point<i32, smithay::utils::Physical>,
|
||||||
|
|
@ -664,15 +686,16 @@ impl CosmicMapped {
|
||||||
scale: smithay::utils::Scale<f64>,
|
scale: smithay::utils::Scale<f64>,
|
||||||
alpha: f32,
|
alpha: f32,
|
||||||
scanout_override: Option<bool>,
|
scanout_override: Option<bool>,
|
||||||
) -> Vec<C>
|
scanout_node: Option<DrmNode>,
|
||||||
where
|
push_above: &mut dyn FnMut(CosmicMappedRenderElement<R>),
|
||||||
|
push_below: &mut dyn FnMut(CosmicMappedRenderElement<R>),
|
||||||
|
) where
|
||||||
R: AsGlowRenderer,
|
R: AsGlowRenderer,
|
||||||
R::TextureId: Send + Clone + 'static,
|
R::TextureId: Send + Clone + 'static,
|
||||||
CosmicMappedRenderElement<R>: RenderElement<R>,
|
CosmicMappedRenderElement<R>: RenderElement<R>,
|
||||||
C: From<CosmicMappedRenderElement<R>>,
|
|
||||||
{
|
{
|
||||||
#[cfg(feature = "debug")]
|
#[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 = self.active_window();
|
||||||
let window_geo = window.geometry();
|
let window_geo = window.geometry();
|
||||||
let (min_size, max_size, size) = (
|
let (min_size, max_size, size) = (
|
||||||
|
|
@ -833,41 +856,38 @@ impl CosmicMapped {
|
||||||
scale.x,
|
scale.x,
|
||||||
0.8,
|
0.8,
|
||||||
) {
|
) {
|
||||||
Ok(element) => vec![CosmicMappedRenderElement::from(element)],
|
Ok(element) => push_above(element.into()),
|
||||||
Err(err) => {
|
Err(err) => {
|
||||||
debug!(?err, "Error rendering debug overlay.");
|
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))]
|
match &self.element {
|
||||||
elements.extend(match &self.element {
|
CosmicMappedInternal::Stack(s) => s.push_render_elements(
|
||||||
CosmicMappedInternal::Stack(s) => s.render_elements::<R, CosmicMappedRenderElement<R>>(
|
|
||||||
renderer,
|
renderer,
|
||||||
location,
|
location,
|
||||||
max_size,
|
max_size,
|
||||||
scale,
|
scale,
|
||||||
alpha,
|
alpha,
|
||||||
scanout_override,
|
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::<R, CosmicMappedRenderElement<R>>(
|
|
||||||
renderer,
|
|
||||||
location,
|
|
||||||
max_size,
|
|
||||||
scale,
|
|
||||||
alpha,
|
|
||||||
scanout_override,
|
|
||||||
),
|
|
||||||
_ => unreachable!(),
|
_ => unreachable!(),
|
||||||
});
|
}
|
||||||
|
|
||||||
elements.into_iter().map(C::from).collect()
|
|
||||||
}
|
}
|
||||||
|
|
||||||
pub(crate) fn update_theme(&self, theme: cosmic::Theme) {
|
pub(crate) fn update_theme(&self, theme: cosmic::Theme) {
|
||||||
|
|
@ -1109,7 +1129,7 @@ where
|
||||||
GrabbedWindow(RescaleRenderElement<self::window::CosmicWindowRenderElement<R>>),
|
GrabbedWindow(RescaleRenderElement<self::window::CosmicWindowRenderElement<R>>),
|
||||||
FocusIndicator(PixelShaderElement),
|
FocusIndicator(PixelShaderElement),
|
||||||
Overlay(PixelShaderElement),
|
Overlay(PixelShaderElement),
|
||||||
StackHoverIndicator(MemoryRenderBufferRenderElement<R>),
|
StackHoverIndicator(IcedRenderElement<R>),
|
||||||
#[cfg(feature = "debug")]
|
#[cfg(feature = "debug")]
|
||||||
Egui(TextureRenderElement<GlesTexture>),
|
Egui(TextureRenderElement<GlesTexture>),
|
||||||
}
|
}
|
||||||
|
|
@ -1117,7 +1137,7 @@ where
|
||||||
impl<R> Element for CosmicMappedRenderElement<R>
|
impl<R> Element for CosmicMappedRenderElement<R>
|
||||||
where
|
where
|
||||||
R: AsGlowRenderer,
|
R: AsGlowRenderer,
|
||||||
R::TextureId: 'static,
|
R::TextureId: Send + 'static,
|
||||||
{
|
{
|
||||||
fn id(&self) -> &smithay::backend::renderer::element::Id {
|
fn id(&self) -> &smithay::backend::renderer::element::Id {
|
||||||
match self {
|
match self {
|
||||||
|
|
@ -1338,8 +1358,7 @@ where
|
||||||
impl<R> RenderElement<R> for CosmicMappedRenderElement<R>
|
impl<R> RenderElement<R> for CosmicMappedRenderElement<R>
|
||||||
where
|
where
|
||||||
R: AsGlowRenderer,
|
R: AsGlowRenderer,
|
||||||
R::TextureId: 'static,
|
R::TextureId: Send + 'static,
|
||||||
R::Error: FromGlesError,
|
|
||||||
{
|
{
|
||||||
fn draw(
|
fn draw(
|
||||||
&self,
|
&self,
|
||||||
|
|
@ -1372,7 +1391,7 @@ where
|
||||||
opaque_regions,
|
opaque_regions,
|
||||||
cache,
|
cache,
|
||||||
)
|
)
|
||||||
.map_err(FromGlesError::from_gles_error),
|
.map_err(R::from_gles_error),
|
||||||
CosmicMappedRenderElement::MovingStack(elem) => {
|
CosmicMappedRenderElement::MovingStack(elem) => {
|
||||||
elem.draw(frame, src, dst, damage, opaque_regions, cache)
|
elem.draw(frame, src, dst, damage, opaque_regions, cache)
|
||||||
}
|
}
|
||||||
|
|
@ -1394,7 +1413,7 @@ where
|
||||||
opaque_regions,
|
opaque_regions,
|
||||||
cache,
|
cache,
|
||||||
)
|
)
|
||||||
.map_err(FromGlesError::from_gles_error),
|
.map_err(R::from_gles_error),
|
||||||
CosmicMappedRenderElement::Overlay(elem) => RenderElement::<GlowRenderer>::draw(
|
CosmicMappedRenderElement::Overlay(elem) => RenderElement::<GlowRenderer>::draw(
|
||||||
elem,
|
elem,
|
||||||
R::glow_frame_mut(frame),
|
R::glow_frame_mut(frame),
|
||||||
|
|
@ -1404,7 +1423,7 @@ where
|
||||||
opaque_regions,
|
opaque_regions,
|
||||||
cache,
|
cache,
|
||||||
)
|
)
|
||||||
.map_err(FromGlesError::from_gles_error),
|
.map_err(R::from_gles_error),
|
||||||
CosmicMappedRenderElement::StackHoverIndicator(elem) => {
|
CosmicMappedRenderElement::StackHoverIndicator(elem) => {
|
||||||
elem.draw(frame, src, dst, damage, opaque_regions, cache)
|
elem.draw(frame, src, dst, damage, opaque_regions, cache)
|
||||||
}
|
}
|
||||||
|
|
@ -1420,7 +1439,7 @@ where
|
||||||
opaque_regions,
|
opaque_regions,
|
||||||
cache,
|
cache,
|
||||||
)
|
)
|
||||||
.map_err(FromGlesError::from_gles_error)
|
.map_err(R::from_gles_error)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
@ -1483,7 +1502,7 @@ where
|
||||||
dst,
|
dst,
|
||||||
cache,
|
cache,
|
||||||
)
|
)
|
||||||
.map_err(FromGlesError::from_gles_error)
|
.map_err(R::from_gles_error)
|
||||||
}
|
}
|
||||||
CosmicMappedRenderElement::MovingStack(elem) => {
|
CosmicMappedRenderElement::MovingStack(elem) => {
|
||||||
elem.capture_framebuffer(frame, src, dst, cache)
|
elem.capture_framebuffer(frame, src, dst, cache)
|
||||||
|
|
@ -1505,7 +1524,7 @@ where
|
||||||
dst,
|
dst,
|
||||||
cache,
|
cache,
|
||||||
)
|
)
|
||||||
.map_err(FromGlesError::from_gles_error)
|
.map_err(R::from_gles_error)
|
||||||
}
|
}
|
||||||
CosmicMappedRenderElement::Overlay(elem) => {
|
CosmicMappedRenderElement::Overlay(elem) => {
|
||||||
RenderElement::<GlowRenderer>::capture_framebuffer(
|
RenderElement::<GlowRenderer>::capture_framebuffer(
|
||||||
|
|
@ -1515,7 +1534,7 @@ where
|
||||||
dst,
|
dst,
|
||||||
cache,
|
cache,
|
||||||
)
|
)
|
||||||
.map_err(FromGlesError::from_gles_error)
|
.map_err(R::from_gles_error)
|
||||||
}
|
}
|
||||||
CosmicMappedRenderElement::StackHoverIndicator(elem) => {
|
CosmicMappedRenderElement::StackHoverIndicator(elem) => {
|
||||||
elem.capture_framebuffer(frame, src, dst, cache)
|
elem.capture_framebuffer(frame, src, dst, cache)
|
||||||
|
|
@ -1526,7 +1545,7 @@ where
|
||||||
RenderElement::<GlowRenderer>::capture_framebuffer(
|
RenderElement::<GlowRenderer>::capture_framebuffer(
|
||||||
elem, glow_frame, src, dst, cache,
|
elem, glow_frame, src, dst, cache,
|
||||||
)
|
)
|
||||||
.map_err(FromGlesError::from_gles_error)
|
.map_err(R::from_gles_error)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
@ -1564,13 +1583,13 @@ where
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
impl<R> From<MemoryRenderBufferRenderElement<R>> for CosmicMappedRenderElement<R>
|
impl<R> From<IcedRenderElement<R>> for CosmicMappedRenderElement<R>
|
||||||
where
|
where
|
||||||
R: AsGlowRenderer,
|
R: AsGlowRenderer,
|
||||||
R::TextureId: 'static,
|
R::TextureId: 'static,
|
||||||
CosmicMappedRenderElement<R>: RenderElement<R>,
|
CosmicMappedRenderElement<R>: RenderElement<R>,
|
||||||
{
|
{
|
||||||
fn from(elem: MemoryRenderBufferRenderElement<R>) -> Self {
|
fn from(elem: IcedRenderElement<R>) -> Self {
|
||||||
CosmicMappedRenderElement::StackHoverIndicator(elem)
|
CosmicMappedRenderElement::StackHoverIndicator(elem)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -1,10 +1,11 @@
|
||||||
use std::sync::Mutex;
|
use std::sync::{Arc, Mutex};
|
||||||
|
|
||||||
use crate::{
|
use crate::{
|
||||||
|
backend::render::element::AsGlowRenderer,
|
||||||
config::Config,
|
config::Config,
|
||||||
fl,
|
fl,
|
||||||
shell::grabs::ResizeEdge,
|
shell::grabs::ResizeEdge,
|
||||||
utils::iced::{IcedElement, Program},
|
utils::iced::{IcedElement, IcedRenderElement, Program},
|
||||||
};
|
};
|
||||||
|
|
||||||
use calloop::LoopHandle;
|
use calloop::LoopHandle;
|
||||||
|
|
@ -12,175 +13,330 @@ use cosmic::{
|
||||||
Apply,
|
Apply,
|
||||||
iced::{
|
iced::{
|
||||||
Alignment,
|
Alignment,
|
||||||
widget::{column, container, row, space},
|
core::{Background, Border, Color, Length},
|
||||||
|
widget::{container, row, space},
|
||||||
},
|
},
|
||||||
iced_core::{Background, Border, Color, Length},
|
|
||||||
theme,
|
theme,
|
||||||
widget::{icon::from_name, text},
|
widget::{icon::from_name, text},
|
||||||
};
|
};
|
||||||
use cosmic_settings_config::shortcuts::action::{Action, ResizeDirection};
|
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<ResizeIndicatorInternal>;
|
#[derive(Debug, Clone)]
|
||||||
|
pub struct ResizeIndicator {
|
||||||
|
edges: ResizeEdge,
|
||||||
|
direction: Arc<Mutex<ResizeDirection>>,
|
||||||
|
size: Size<i32, Logical>,
|
||||||
|
|
||||||
pub fn resize_indicator(
|
center_elem: IcedElement<ResizeIndicatorInternal>,
|
||||||
direction: ResizeDirection,
|
left_elem: IcedElement<ResizeIndicatorArrow>,
|
||||||
config: &Config,
|
top_elem: IcedElement<ResizeIndicatorArrow>,
|
||||||
evlh: LoopHandle<'static, crate::state::State>,
|
right_elem: IcedElement<ResizeIndicatorArrow>,
|
||||||
theme: cosmic::Theme,
|
down_elem: IcedElement<ResizeIndicatorArrow>,
|
||||||
) -> ResizeIndicator {
|
}
|
||||||
ResizeIndicator::new(
|
|
||||||
ResizeIndicatorInternal {
|
const ARROW_SIZE: i32 = 36;
|
||||||
edges: Mutex::new(ResizeEdge::all()),
|
|
||||||
direction,
|
impl ResizeIndicator {
|
||||||
shortcut1: config
|
pub fn new(
|
||||||
.shortcuts
|
direction: ResizeDirection,
|
||||||
.iter()
|
config: &Config,
|
||||||
.find_map(|(pattern, action)| {
|
evlh: LoopHandle<'static, crate::state::State>,
|
||||||
(*action == Action::Resizing(ResizeDirection::Outwards)).then_some(pattern)
|
mut theme: cosmic::Theme,
|
||||||
})
|
) -> ResizeIndicator {
|
||||||
.map(|pattern| format!("{}: ", pattern.to_string()))
|
theme.transparent = theme.cosmic().frosted_system_interface;
|
||||||
.unwrap_or_else(|| crate::fl!("unknown-keybinding")),
|
let direction = Arc::new(Mutex::new(direction));
|
||||||
shortcut2: config
|
|
||||||
.shortcuts
|
ResizeIndicator {
|
||||||
.iter()
|
edges: ResizeEdge::all(),
|
||||||
.find_map(|(pattern, action)| {
|
direction: direction.clone(),
|
||||||
(*action == Action::Resizing(ResizeDirection::Inwards)).then_some(pattern)
|
size: Size::default(),
|
||||||
})
|
center_elem: IcedElement::new(
|
||||||
.map(|pattern| format!("{}: ", pattern.to_string()))
|
ResizeIndicatorInternal {
|
||||||
.unwrap_or_else(|| crate::fl!("unknown-keybinding")),
|
shortcut1: config
|
||||||
},
|
.shortcuts
|
||||||
Size::from((1, 1)),
|
.iter()
|
||||||
evlh,
|
.find_map(|(pattern, action)| {
|
||||||
theme,
|
(*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<i32, Logical>) {
|
||||||
|
let minimum = self.center_elem.minimum_size();
|
||||||
|
let new_size = Size::<i32, Logical>::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<R>(
|
||||||
|
&self,
|
||||||
|
renderer: &mut R,
|
||||||
|
location: Point<i32, Physical>,
|
||||||
|
scale: Scale<f64>,
|
||||||
|
alpha: f32,
|
||||||
|
push_above: &mut dyn FnMut(IcedRenderElement<R>),
|
||||||
|
mut push_below: Option<&mut dyn FnMut(IcedRenderElement<R>)>,
|
||||||
|
) 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<ResizeIndicatorArrow>,
|
||||||
|
loc: Point<i32, Logical>| {
|
||||||
|
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 struct ResizeIndicatorInternal {
|
||||||
pub edges: Mutex<ResizeEdge>,
|
shortcut1: String,
|
||||||
pub direction: ResizeDirection,
|
shortcut2: String,
|
||||||
pub shortcut1: String,
|
|
||||||
pub shortcut2: String,
|
|
||||||
}
|
}
|
||||||
|
|
||||||
impl Program for ResizeIndicatorInternal {
|
impl Program for ResizeIndicatorInternal {
|
||||||
type Message = ();
|
type Message = ();
|
||||||
|
|
||||||
fn view(&self) -> cosmic::Element<'_, Self::Message> {
|
fn view(&self) -> cosmic::Element<'_, Self::Message> {
|
||||||
let edges = self.edges.lock().unwrap();
|
row(vec![
|
||||||
let icon_container_style = || {
|
text::heading(&self.shortcut1).into(),
|
||||||
theme::Container::custom(|theme| container::Style {
|
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,
|
snap: true,
|
||||||
icon_color: Some(Color::from(theme.cosmic().accent.on)),
|
icon_color: Some(Color::from(theme.cosmic().accent.on)),
|
||||||
text_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 {
|
border: Border {
|
||||||
radius: 18.0.into(),
|
radius: theme.cosmic().radius_s().into(),
|
||||||
width: 0.0,
|
width: 0.0,
|
||||||
color: Color::TRANSPARENT,
|
color: Color::TRANSPARENT,
|
||||||
},
|
},
|
||||||
shadow: Default::default(),
|
shadow: Default::default(),
|
||||||
})
|
}
|
||||||
};
|
}))
|
||||||
|
.width(Length::Shrink)
|
||||||
column(vec![
|
.height(Length::Shrink)
|
||||||
if edges.contains(ResizeEdge::TOP) {
|
.into()
|
||||||
from_name(if self.direction == ResizeDirection::Outwards {
|
}
|
||||||
"go-up-symbolic"
|
}
|
||||||
} else {
|
|
||||||
"go-down-symbolic"
|
pub struct ResizeIndicatorArrow {
|
||||||
})
|
direction: Arc<Mutex<ResizeDirection>>,
|
||||||
.size(20)
|
icon_outwards: &'static str,
|
||||||
.prefer_svg(true)
|
icon_inwards: &'static str,
|
||||||
.apply(container)
|
}
|
||||||
.padding(8)
|
|
||||||
.class(icon_container_style())
|
impl Program for ResizeIndicatorArrow {
|
||||||
.width(Length::Shrink)
|
type Message = ();
|
||||||
.apply(container)
|
|
||||||
.center_x(Length::Fill)
|
fn view(&self) -> cosmic::Element<'_, Self::Message> {
|
||||||
.into()
|
from_name(
|
||||||
} else {
|
if *self.direction.lock().unwrap() == ResizeDirection::Outwards {
|
||||||
space::vertical().height(36).into()
|
self.icon_outwards
|
||||||
},
|
} else {
|
||||||
row(vec![
|
self.icon_inwards
|
||||||
if edges.contains(ResizeEdge::LEFT) {
|
},
|
||||||
from_name(if self.direction == ResizeDirection::Outwards {
|
)
|
||||||
"go-previous-symbolic"
|
.size(20)
|
||||||
} else {
|
.prefer_svg(true)
|
||||||
"go-next-symbolic"
|
.apply(container)
|
||||||
})
|
.padding(8)
|
||||||
.size(20)
|
.class(theme::Container::custom(|theme| {
|
||||||
.prefer_svg(true)
|
let mut background = theme.cosmic().accent_color();
|
||||||
.apply(container)
|
if theme.transparent {
|
||||||
.padding(8)
|
background.alpha = theme
|
||||||
.class(icon_container_style())
|
.cosmic()
|
||||||
.width(Length::Shrink)
|
.alpha_map
|
||||||
.apply(container)
|
.blurred_alpha(theme.cosmic().frosted);
|
||||||
.center_y(Length::Fill)
|
}
|
||||||
.into()
|
|
||||||
} else {
|
container::Style {
|
||||||
space::horizontal().width(36).into()
|
snap: true,
|
||||||
},
|
icon_color: Some(Color::from(theme.cosmic().accent.on)),
|
||||||
row(vec![
|
text_color: Some(Color::from(theme.cosmic().accent.on)),
|
||||||
text::heading(&self.shortcut1).into(),
|
background: Some(Background::Color(background.into())),
|
||||||
text::body(fl!("grow-window")).into(),
|
border: Border {
|
||||||
space::horizontal().width(40).into(),
|
radius: theme.cosmic().radius_s().into(),
|
||||||
text::heading(&self.shortcut2).into(),
|
width: 0.0,
|
||||||
text::body(fl!("shrink-window")).into(),
|
color: Color::TRANSPARENT,
|
||||||
])
|
},
|
||||||
.apply(container)
|
shadow: Default::default(),
|
||||||
.align_x(Alignment::Center)
|
}
|
||||||
.align_y(Alignment::Center)
|
}))
|
||||||
.padding(16)
|
.width(Length::Shrink)
|
||||||
.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()
|
|
||||||
},
|
|
||||||
])
|
|
||||||
.into()
|
.into()
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -4,11 +4,8 @@ use super::{
|
||||||
};
|
};
|
||||||
use crate::{
|
use crate::{
|
||||||
backend::render::{
|
backend::render::{
|
||||||
IndicatorShader, Key, Usage,
|
IndicatorShader, Key, Usage, cursor::CursorState, element::AsGlowRenderer,
|
||||||
clipped_surface::ClippedSurfaceRenderElement,
|
shadow::ShadowShader, wayland::SurfaceRenderElement,
|
||||||
cursor::CursorState,
|
|
||||||
element::{AsGlowRenderer, FromGlesError},
|
|
||||||
shadow::ShadowShader,
|
|
||||||
},
|
},
|
||||||
hooks::{Decorations, HOOKS},
|
hooks::{Decorations, HOOKS},
|
||||||
shell::{
|
shell::{
|
||||||
|
|
@ -19,17 +16,20 @@ use crate::{
|
||||||
},
|
},
|
||||||
state::State,
|
state::State,
|
||||||
utils::{
|
utils::{
|
||||||
iced::{IcedElement, Program},
|
iced::{IcedElement, IcedRenderElement, Program},
|
||||||
prelude::*,
|
prelude::*,
|
||||||
},
|
},
|
||||||
};
|
};
|
||||||
use calloop::LoopHandle;
|
use calloop::LoopHandle;
|
||||||
use cosmic::{
|
use cosmic::{
|
||||||
Apply, Element as CosmicElement, Theme,
|
Apply, Element as CosmicElement, Theme,
|
||||||
iced::{Alignment, id::Id, widget as iced_widget},
|
iced::{
|
||||||
iced_core::{Background, Border, Color, Length, border::Radius},
|
Alignment,
|
||||||
iced_runtime::Task,
|
core::{Background, Border, Color, Length, border::Radius},
|
||||||
iced_widget::scrollable::AbsoluteOffset,
|
id::Id,
|
||||||
|
runtime::Task,
|
||||||
|
widget::{self as iced_widget, scrollable::AbsoluteOffset},
|
||||||
|
},
|
||||||
theme, widget as cosmic_widget,
|
theme, widget as cosmic_widget,
|
||||||
};
|
};
|
||||||
use cosmic_comp_config::AppearanceConfig;
|
use cosmic_comp_config::AppearanceConfig;
|
||||||
|
|
@ -37,14 +37,11 @@ use cosmic_settings_config::shortcuts;
|
||||||
use shortcuts::action::{Direction, FocusDirection};
|
use shortcuts::action::{Direction, FocusDirection};
|
||||||
use smithay::{
|
use smithay::{
|
||||||
backend::{
|
backend::{
|
||||||
|
drm::DrmNode,
|
||||||
input::KeyState,
|
input::KeyState,
|
||||||
renderer::{
|
renderer::{
|
||||||
ImportAll, ImportMem, Renderer,
|
ImportAll, ImportMem, Renderer,
|
||||||
element::{
|
element::{Element, Id as RendererId, Kind, RenderElement, UnderlyingStorage},
|
||||||
AsRenderElements, Element, Id as RendererId, Kind, RenderElement,
|
|
||||||
UnderlyingStorage, memory::MemoryRenderBufferRenderElement,
|
|
||||||
surface::WaylandSurfaceRenderElement,
|
|
||||||
},
|
|
||||||
gles::element::PixelShaderElement,
|
gles::element::PixelShaderElement,
|
||||||
glow::GlowRenderer,
|
glow::GlowRenderer,
|
||||||
utils::{CommitCounter, DamageSet, OpaqueRegions},
|
utils::{CommitCounter, DamageSet, OpaqueRegions},
|
||||||
|
|
@ -149,9 +146,10 @@ impl CosmicStack {
|
||||||
pub fn new<I: Into<CosmicSurface>>(
|
pub fn new<I: Into<CosmicSurface>>(
|
||||||
windows: impl Iterator<Item = I>,
|
windows: impl Iterator<Item = I>,
|
||||||
handle: LoopHandle<'static, crate::state::State>,
|
handle: LoopHandle<'static, crate::state::State>,
|
||||||
theme: cosmic::Theme,
|
mut theme: cosmic::Theme,
|
||||||
appearance: AppearanceConfig,
|
appearance: AppearanceConfig,
|
||||||
) -> CosmicStack {
|
) -> CosmicStack {
|
||||||
|
theme.transparent = theme.cosmic().frosted_windows;
|
||||||
let windows = windows.map(Into::into).collect::<Vec<_>>();
|
let windows = windows.map(Into::into).collect::<Vec<_>>();
|
||||||
assert!(!windows.is_empty());
|
assert!(!windows.is_empty());
|
||||||
|
|
||||||
|
|
@ -532,7 +530,7 @@ impl CosmicStack {
|
||||||
let geo = p.windows.lock().unwrap()[p.active.load(Ordering::SeqCst)].geometry();
|
let geo = p.windows.lock().unwrap()[p.active.load(Ordering::SeqCst)].geometry();
|
||||||
|
|
||||||
if surface_type.contains(WindowSurfaceType::TOPLEVEL) {
|
if surface_type.contains(WindowSurfaceType::TOPLEVEL) {
|
||||||
let point_i32 = relative_pos.to_i32_round::<i32>();
|
let point_i32 = relative_pos.to_i32_floor::<i32>();
|
||||||
if (point_i32.x - geo.loc.x >= -RESIZE_BORDER && point_i32.x - geo.loc.x < 0)
|
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.y - geo.loc.y >= -RESIZE_BORDER && point_i32.y - geo.loc.y < 0)
|
||||||
|| (point_i32.x - geo.loc.x >= geo.size.w
|
|| (point_i32.x - geo.loc.x >= geo.size.w
|
||||||
|
|
@ -648,30 +646,38 @@ impl CosmicStack {
|
||||||
self.0.loop_handle()
|
self.0.loop_handle()
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn popup_render_elements<R, C>(
|
pub fn push_popup_render_elements<R>(
|
||||||
&self,
|
&self,
|
||||||
renderer: &mut R,
|
renderer: &mut R,
|
||||||
location: Point<i32, Physical>,
|
location: Point<i32, Physical>,
|
||||||
scale: Scale<f64>,
|
scale: Scale<f64>,
|
||||||
alpha: f32,
|
alpha: f32,
|
||||||
) -> Vec<C>
|
scanout_node: Option<DrmNode>,
|
||||||
where
|
push: &mut dyn FnMut(CosmicStackRenderElement<R>),
|
||||||
|
) where
|
||||||
R: AsGlowRenderer,
|
R: AsGlowRenderer,
|
||||||
R::TextureId: Send + Clone + 'static,
|
R::TextureId: Send + Clone + 'static,
|
||||||
C: From<CosmicStackRenderElement<R>>,
|
|
||||||
{
|
{
|
||||||
let window_loc = location + Point::from((0, (TAB_HEIGHT as f64 * scale.y) as i32));
|
let window_loc = location + Point::from((0, (TAB_HEIGHT as f64 * scale.y) as i32));
|
||||||
self.0.with_program(|p| {
|
self.0.with_program(|p| {
|
||||||
let windows = p.windows.lock().unwrap();
|
let windows = p.windows.lock().unwrap();
|
||||||
let active = p.active.load(Ordering::SeqCst);
|
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]
|
windows[active].push_popup_render_elements(
|
||||||
.popup_render_elements::<R, CosmicStackRenderElement<R>>(
|
renderer,
|
||||||
renderer, window_loc, scale, alpha,
|
window_loc,
|
||||||
)
|
scale,
|
||||||
.into_iter()
|
alpha,
|
||||||
.map(C::from)
|
scanout_node,
|
||||||
.collect()
|
frosted,
|
||||||
|
&mut |elem| push(elem.into()),
|
||||||
|
)
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -742,7 +748,7 @@ impl CosmicStack {
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn render_elements<R, C>(
|
pub fn push_render_elements<R>(
|
||||||
&self,
|
&self,
|
||||||
renderer: &mut R,
|
renderer: &mut R,
|
||||||
location: Point<i32, Physical>,
|
location: Point<i32, Physical>,
|
||||||
|
|
@ -750,17 +756,18 @@ impl CosmicStack {
|
||||||
scale: Scale<f64>,
|
scale: Scale<f64>,
|
||||||
alpha: f32,
|
alpha: f32,
|
||||||
scanout_override: Option<bool>,
|
scanout_override: Option<bool>,
|
||||||
) -> Vec<C>
|
scanout_node: Option<DrmNode>,
|
||||||
where
|
push_above: &mut dyn FnMut(CosmicStackRenderElement<R>),
|
||||||
|
push_below: &mut dyn FnMut(CosmicStackRenderElement<R>),
|
||||||
|
) where
|
||||||
R: AsGlowRenderer,
|
R: AsGlowRenderer,
|
||||||
R::TextureId: Send + Clone + 'static,
|
R::TextureId: Send + Clone + 'static,
|
||||||
C: From<CosmicStackRenderElement<R>>,
|
|
||||||
{
|
{
|
||||||
if !self
|
if !self
|
||||||
.0
|
.0
|
||||||
.with_program(|p| p.override_alive.load(Ordering::Acquire))
|
.with_program(|p| p.override_alive.load(Ordering::Acquire))
|
||||||
{
|
{
|
||||||
return Vec::new();
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
let geometry = self
|
let geometry = self
|
||||||
|
|
@ -769,27 +776,36 @@ impl CosmicStack {
|
||||||
.to_physical_precise_round(scale);
|
.to_physical_precise_round(scale);
|
||||||
let stack_loc = location + geometry.loc;
|
let stack_loc = location + geometry.loc;
|
||||||
let window_loc = location + Point::from((0, (TAB_HEIGHT as f64 * scale.y) as i32));
|
let window_loc = location + Point::from((0, (TAB_HEIGHT as f64 * scale.y) as i32));
|
||||||
|
let frosted = self.0.with_program(|p| {
|
||||||
let mut elements = AsRenderElements::<R>::render_elements::<CosmicStackRenderElement<R>>(
|
let theme = p.theme.lock().unwrap();
|
||||||
&self.0, renderer, stack_loc, scale, alpha,
|
if theme.cosmic().frosted_windows {
|
||||||
);
|
(theme.cosmic().frosted as u8 + 1) as usize
|
||||||
|
} else {
|
||||||
elements.extend(self.0.with_program(|p| {
|
0
|
||||||
|
}
|
||||||
|
});
|
||||||
|
let radii = self.0.with_program(|p| {
|
||||||
let windows = p.windows.lock().unwrap();
|
let windows = p.windows.lock().unwrap();
|
||||||
let active = p.active.load(Ordering::SeqCst);
|
let active = p.active.load(Ordering::SeqCst);
|
||||||
let theme = p.theme.lock().unwrap();
|
|
||||||
let appearance = p.appearance_conf.lock().unwrap();
|
let appearance = p.appearance_conf.lock().unwrap();
|
||||||
|
let theme = p.theme.lock().unwrap();
|
||||||
let tiled = p.tiled.load(Ordering::Acquire);
|
let tiled = p.tiled.load(Ordering::Acquire);
|
||||||
let maximized = windows[active].is_maximized(false);
|
let maximized = windows[active].is_maximized(false);
|
||||||
|
|
||||||
let round = (appearance.clip_tiled_windows || !tiled) && !maximized;
|
let round = (appearance.clip_tiled_windows || !tiled) && !maximized;
|
||||||
let radii = round.then(|| {
|
round.then(|| {
|
||||||
theme
|
theme
|
||||||
.cosmic()
|
.cosmic()
|
||||||
.radius_s()
|
.radius_s()
|
||||||
.map(|x| if x < 4.0 { x } else { x + 4.0 })
|
.map(|x| if x < 4.0 { x } else { x + 4.0 })
|
||||||
.map(|x| x.round() as u8)
|
.map(|x| x.round() as u8)
|
||||||
});
|
})
|
||||||
|
});
|
||||||
|
|
||||||
|
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 maximized = windows[active].is_maximized(false);
|
||||||
|
|
||||||
let mut geo = SpaceElement::geometry(&windows[active]).to_f64();
|
let mut geo = SpaceElement::geometry(&windows[active]).to_f64();
|
||||||
geo.loc += location.to_f64().to_logical(scale);
|
geo.loc += location.to_f64().to_logical(scale);
|
||||||
|
|
@ -801,53 +817,49 @@ impl CosmicStack {
|
||||||
let window_key =
|
let window_key =
|
||||||
CosmicMappedKey(CosmicMappedKeyInner::Stack(Arc::downgrade(&self.0.0)));
|
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();
|
let (r, g, b, a) = theme.cosmic().bg_divider().into_components();
|
||||||
CosmicStackRenderElement::Border(IndicatorShader::element(
|
push_above(CosmicStackRenderElement::Border(IndicatorShader::element(
|
||||||
renderer,
|
renderer,
|
||||||
Key::Window(Usage::Border, window_key.clone()),
|
Key::Window(Usage::Border, window_key.clone()),
|
||||||
geo.to_i32_round().as_local(),
|
geo.to_i32_round().as_local(),
|
||||||
1,
|
1,
|
||||||
radii.unwrap_or([0, 0, 0, 0]),
|
radii.unwrap_or([0; 4]),
|
||||||
a * alpha,
|
a * alpha,
|
||||||
scale.x,
|
scale.x,
|
||||||
[r, g, b],
|
[r, g, b],
|
||||||
))
|
)));
|
||||||
});
|
};
|
||||||
|
|
||||||
border.into_iter().chain(
|
let radii = radii.map(|[a, _, c, _]| [a, 0, c, 0]);
|
||||||
windows[active]
|
windows[active].push_render_elements(
|
||||||
.render_elements::<R, WaylandSurfaceRenderElement<R>>(
|
renderer,
|
||||||
renderer,
|
window_loc,
|
||||||
window_loc,
|
scale,
|
||||||
scale,
|
alpha,
|
||||||
alpha,
|
scanout_override,
|
||||||
scanout_override,
|
scanout_node,
|
||||||
)
|
radii.is_some(),
|
||||||
.into_iter()
|
radii.unwrap_or([0; 4]),
|
||||||
.map(move |elem| {
|
frosted,
|
||||||
let radii = radii.map(|[a, _, c, _]| [a, 0, c, 0]);
|
&mut |elem| push_above(elem.into()),
|
||||||
if radii.is_some_and(|radii| {
|
Some(&mut |elem| push_below(elem.into())),
|
||||||
ClippedSurfaceRenderElement::will_clip(&elem, scale, geo, radii)
|
);
|
||||||
}) {
|
});
|
||||||
CosmicStackRenderElement::Clipped(ClippedSurfaceRenderElement::new(
|
|
||||||
renderer,
|
|
||||||
elem,
|
|
||||||
scale,
|
|
||||||
geo,
|
|
||||||
radii.unwrap(),
|
|
||||||
))
|
|
||||||
} else {
|
|
||||||
CosmicStackRenderElement::Window(elem)
|
|
||||||
}
|
|
||||||
}),
|
|
||||||
)
|
|
||||||
}));
|
|
||||||
|
|
||||||
elements.into_iter().map(C::from).collect()
|
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| {
|
self.0.with_program(|p| {
|
||||||
*p.theme.lock().unwrap() = theme.clone();
|
*p.theme.lock().unwrap() = theme.clone();
|
||||||
});
|
});
|
||||||
|
|
@ -1356,11 +1368,14 @@ impl Decorations<CosmicStackInternal, Message> for DefaultDecorations {
|
||||||
.class(theme::Container::custom(move |theme| {
|
.class(theme::Container::custom(move |theme| {
|
||||||
let cosmic_theme = theme.cosmic();
|
let cosmic_theme = theme.cosmic();
|
||||||
|
|
||||||
let background = if group_focused {
|
let mut background = if group_focused {
|
||||||
cosmic_theme.accent_color()
|
cosmic_theme.accent_color()
|
||||||
} else {
|
} else {
|
||||||
cosmic_theme.primary_container_color()
|
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 {
|
iced_widget::container::Style {
|
||||||
snap: true,
|
snap: true,
|
||||||
|
|
@ -1877,40 +1892,32 @@ impl TouchTarget<State> for CosmicStack {
|
||||||
}
|
}
|
||||||
|
|
||||||
pub enum CosmicStackRenderElement<R: Renderer + ImportAll + ImportMem> {
|
pub enum CosmicStackRenderElement<R: Renderer + ImportAll + ImportMem> {
|
||||||
Header(MemoryRenderBufferRenderElement<R>),
|
Header(IcedRenderElement<R>),
|
||||||
Shadow(PixelShaderElement),
|
Shadow(PixelShaderElement),
|
||||||
Border(PixelShaderElement),
|
Border(PixelShaderElement),
|
||||||
Window(WaylandSurfaceRenderElement<R>),
|
Window(SurfaceRenderElement<R>),
|
||||||
Clipped(ClippedSurfaceRenderElement<R>),
|
|
||||||
}
|
}
|
||||||
|
|
||||||
impl<R: Renderer + ImportAll + ImportMem> From<MemoryRenderBufferRenderElement<R>>
|
impl<R: Renderer + ImportAll + ImportMem> From<IcedRenderElement<R>>
|
||||||
for CosmicStackRenderElement<R>
|
for CosmicStackRenderElement<R>
|
||||||
{
|
{
|
||||||
fn from(value: MemoryRenderBufferRenderElement<R>) -> Self {
|
fn from(value: IcedRenderElement<R>) -> Self {
|
||||||
Self::Header(value)
|
Self::Header(value)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
impl<R: Renderer + ImportAll + ImportMem> From<WaylandSurfaceRenderElement<R>>
|
impl<R: Renderer + ImportAll + ImportMem> From<SurfaceRenderElement<R>>
|
||||||
for CosmicStackRenderElement<R>
|
for CosmicStackRenderElement<R>
|
||||||
{
|
{
|
||||||
fn from(value: WaylandSurfaceRenderElement<R>) -> Self {
|
fn from(value: SurfaceRenderElement<R>) -> Self {
|
||||||
Self::Window(value)
|
Self::Window(value)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
impl<R: Renderer + ImportAll + ImportMem> From<ClippedSurfaceRenderElement<R>>
|
|
||||||
for CosmicStackRenderElement<R>
|
|
||||||
{
|
|
||||||
fn from(value: ClippedSurfaceRenderElement<R>) -> Self {
|
|
||||||
Self::Clipped(value)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
impl<R> Element for CosmicStackRenderElement<R>
|
impl<R> Element for CosmicStackRenderElement<R>
|
||||||
where
|
where
|
||||||
R: Renderer + ImportAll + ImportMem,
|
R: Renderer + ImportAll + ImportMem + AsGlowRenderer,
|
||||||
|
R::TextureId: Send + 'static,
|
||||||
{
|
{
|
||||||
fn id(&self) -> &RendererId {
|
fn id(&self) -> &RendererId {
|
||||||
match self {
|
match self {
|
||||||
|
|
@ -1918,7 +1925,6 @@ where
|
||||||
CosmicStackRenderElement::Shadow(elem) => elem.id(),
|
CosmicStackRenderElement::Shadow(elem) => elem.id(),
|
||||||
CosmicStackRenderElement::Border(elem) => elem.id(),
|
CosmicStackRenderElement::Border(elem) => elem.id(),
|
||||||
CosmicStackRenderElement::Window(elem) => elem.id(),
|
CosmicStackRenderElement::Window(elem) => elem.id(),
|
||||||
CosmicStackRenderElement::Clipped(elem) => elem.id(),
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -1928,7 +1934,6 @@ where
|
||||||
CosmicStackRenderElement::Shadow(elem) => elem.current_commit(),
|
CosmicStackRenderElement::Shadow(elem) => elem.current_commit(),
|
||||||
CosmicStackRenderElement::Border(elem) => elem.current_commit(),
|
CosmicStackRenderElement::Border(elem) => elem.current_commit(),
|
||||||
CosmicStackRenderElement::Window(elem) => elem.current_commit(),
|
CosmicStackRenderElement::Window(elem) => elem.current_commit(),
|
||||||
CosmicStackRenderElement::Clipped(elem) => elem.current_commit(),
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -1938,7 +1943,6 @@ where
|
||||||
CosmicStackRenderElement::Shadow(elem) => elem.src(),
|
CosmicStackRenderElement::Shadow(elem) => elem.src(),
|
||||||
CosmicStackRenderElement::Border(elem) => elem.src(),
|
CosmicStackRenderElement::Border(elem) => elem.src(),
|
||||||
CosmicStackRenderElement::Window(elem) => elem.src(),
|
CosmicStackRenderElement::Window(elem) => elem.src(),
|
||||||
CosmicStackRenderElement::Clipped(elem) => elem.src(),
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -1948,7 +1952,6 @@ where
|
||||||
CosmicStackRenderElement::Shadow(elem) => elem.geometry(scale),
|
CosmicStackRenderElement::Shadow(elem) => elem.geometry(scale),
|
||||||
CosmicStackRenderElement::Border(elem) => elem.geometry(scale),
|
CosmicStackRenderElement::Border(elem) => elem.geometry(scale),
|
||||||
CosmicStackRenderElement::Window(elem) => elem.geometry(scale),
|
CosmicStackRenderElement::Window(elem) => elem.geometry(scale),
|
||||||
CosmicStackRenderElement::Clipped(elem) => elem.geometry(scale),
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -1958,7 +1961,6 @@ where
|
||||||
CosmicStackRenderElement::Shadow(elem) => elem.location(scale),
|
CosmicStackRenderElement::Shadow(elem) => elem.location(scale),
|
||||||
CosmicStackRenderElement::Border(elem) => elem.location(scale),
|
CosmicStackRenderElement::Border(elem) => elem.location(scale),
|
||||||
CosmicStackRenderElement::Window(elem) => elem.location(scale),
|
CosmicStackRenderElement::Window(elem) => elem.location(scale),
|
||||||
CosmicStackRenderElement::Clipped(elem) => elem.location(scale),
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -1968,7 +1970,6 @@ where
|
||||||
CosmicStackRenderElement::Shadow(elem) => elem.transform(),
|
CosmicStackRenderElement::Shadow(elem) => elem.transform(),
|
||||||
CosmicStackRenderElement::Border(elem) => elem.transform(),
|
CosmicStackRenderElement::Border(elem) => elem.transform(),
|
||||||
CosmicStackRenderElement::Window(elem) => elem.transform(),
|
CosmicStackRenderElement::Window(elem) => elem.transform(),
|
||||||
CosmicStackRenderElement::Clipped(elem) => elem.transform(),
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -1982,7 +1983,6 @@ where
|
||||||
CosmicStackRenderElement::Shadow(elem) => elem.damage_since(scale, commit),
|
CosmicStackRenderElement::Shadow(elem) => elem.damage_since(scale, commit),
|
||||||
CosmicStackRenderElement::Border(elem) => elem.damage_since(scale, commit),
|
CosmicStackRenderElement::Border(elem) => elem.damage_since(scale, commit),
|
||||||
CosmicStackRenderElement::Window(elem) => elem.damage_since(scale, commit),
|
CosmicStackRenderElement::Window(elem) => elem.damage_since(scale, commit),
|
||||||
CosmicStackRenderElement::Clipped(elem) => elem.damage_since(scale, commit),
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -1992,7 +1992,6 @@ where
|
||||||
CosmicStackRenderElement::Shadow(elem) => elem.opaque_regions(scale),
|
CosmicStackRenderElement::Shadow(elem) => elem.opaque_regions(scale),
|
||||||
CosmicStackRenderElement::Border(elem) => elem.opaque_regions(scale),
|
CosmicStackRenderElement::Border(elem) => elem.opaque_regions(scale),
|
||||||
CosmicStackRenderElement::Window(elem) => elem.opaque_regions(scale),
|
CosmicStackRenderElement::Window(elem) => elem.opaque_regions(scale),
|
||||||
CosmicStackRenderElement::Clipped(elem) => elem.opaque_regions(scale),
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -2002,7 +2001,6 @@ where
|
||||||
CosmicStackRenderElement::Shadow(elem) => elem.alpha(),
|
CosmicStackRenderElement::Shadow(elem) => elem.alpha(),
|
||||||
CosmicStackRenderElement::Border(elem) => elem.alpha(),
|
CosmicStackRenderElement::Border(elem) => elem.alpha(),
|
||||||
CosmicStackRenderElement::Window(elem) => elem.alpha(),
|
CosmicStackRenderElement::Window(elem) => elem.alpha(),
|
||||||
CosmicStackRenderElement::Clipped(elem) => elem.alpha(),
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -2012,7 +2010,6 @@ where
|
||||||
CosmicStackRenderElement::Shadow(elem) => elem.kind(),
|
CosmicStackRenderElement::Shadow(elem) => elem.kind(),
|
||||||
CosmicStackRenderElement::Border(elem) => elem.kind(),
|
CosmicStackRenderElement::Border(elem) => elem.kind(),
|
||||||
CosmicStackRenderElement::Window(elem) => elem.kind(),
|
CosmicStackRenderElement::Window(elem) => elem.kind(),
|
||||||
CosmicStackRenderElement::Clipped(elem) => elem.kind(),
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -2022,7 +2019,6 @@ where
|
||||||
CosmicStackRenderElement::Shadow(elem) => elem.is_framebuffer_effect(),
|
CosmicStackRenderElement::Shadow(elem) => elem.is_framebuffer_effect(),
|
||||||
CosmicStackRenderElement::Border(elem) => elem.is_framebuffer_effect(),
|
CosmicStackRenderElement::Border(elem) => elem.is_framebuffer_effect(),
|
||||||
CosmicStackRenderElement::Window(elem) => elem.is_framebuffer_effect(),
|
CosmicStackRenderElement::Window(elem) => elem.is_framebuffer_effect(),
|
||||||
CosmicStackRenderElement::Clipped(elem) => elem.is_framebuffer_effect(),
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
@ -2030,12 +2026,11 @@ where
|
||||||
impl<R> RenderElement<R> for CosmicStackRenderElement<R>
|
impl<R> RenderElement<R> for CosmicStackRenderElement<R>
|
||||||
where
|
where
|
||||||
R: AsGlowRenderer,
|
R: AsGlowRenderer,
|
||||||
R::TextureId: 'static,
|
R::TextureId: Send + 'static,
|
||||||
R::Error: FromGlesError,
|
|
||||||
{
|
{
|
||||||
fn draw(
|
fn draw(
|
||||||
&self,
|
&self,
|
||||||
frame: &mut <R>::Frame<'_, '_>,
|
frame: &mut R::Frame<'_, '_>,
|
||||||
src: Rectangle<f64, Buffer>,
|
src: Rectangle<f64, Buffer>,
|
||||||
dst: Rectangle<i32, Physical>,
|
dst: Rectangle<i32, Physical>,
|
||||||
damage: &[Rectangle<i32, Physical>],
|
damage: &[Rectangle<i32, Physical>],
|
||||||
|
|
@ -2056,14 +2051,11 @@ where
|
||||||
opaque_regions,
|
opaque_regions,
|
||||||
cache,
|
cache,
|
||||||
)
|
)
|
||||||
.map_err(FromGlesError::from_gles_error)
|
.map_err(R::from_gles_error)
|
||||||
}
|
}
|
||||||
CosmicStackRenderElement::Window(elem) => {
|
CosmicStackRenderElement::Window(elem) => {
|
||||||
elem.draw(frame, src, dst, damage, opaque_regions, cache)
|
elem.draw(frame, src, dst, damage, opaque_regions, cache)
|
||||||
}
|
}
|
||||||
CosmicStackRenderElement::Clipped(elem) => {
|
|
||||||
elem.draw(frame, src, dst, damage, opaque_regions, cache)
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -2074,7 +2066,6 @@ where
|
||||||
elem.underlying_storage(renderer.glow_renderer_mut())
|
elem.underlying_storage(renderer.glow_renderer_mut())
|
||||||
}
|
}
|
||||||
CosmicStackRenderElement::Window(elem) => elem.underlying_storage(renderer),
|
CosmicStackRenderElement::Window(elem) => elem.underlying_storage(renderer),
|
||||||
CosmicStackRenderElement::Clipped(elem) => elem.underlying_storage(renderer),
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -2097,14 +2088,11 @@ where
|
||||||
dst,
|
dst,
|
||||||
cache,
|
cache,
|
||||||
)
|
)
|
||||||
.map_err(FromGlesError::from_gles_error)
|
.map_err(R::from_gles_error)
|
||||||
}
|
}
|
||||||
CosmicStackRenderElement::Window(elem) => {
|
CosmicStackRenderElement::Window(elem) => {
|
||||||
elem.capture_framebuffer(frame, src, dst, cache)
|
elem.capture_framebuffer(frame, src, dst, cache)
|
||||||
}
|
}
|
||||||
CosmicStackRenderElement::Clipped(elem) => {
|
|
||||||
elem.capture_framebuffer(frame, src, dst, cache)
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -3,16 +3,15 @@ use cosmic::{
|
||||||
font::Font,
|
font::Font,
|
||||||
iced::{
|
iced::{
|
||||||
Background,
|
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,
|
theme,
|
||||||
widget::{Icon, icon::from_name, text},
|
widget::{Icon, icon::from_name, text},
|
||||||
};
|
};
|
||||||
|
|
@ -289,8 +288,8 @@ where
|
||||||
.min_height(size.height)
|
.min_height(size.height)
|
||||||
.width(size.width)
|
.width(size.width)
|
||||||
.height(size.height);
|
.height(size.height);
|
||||||
cosmic::iced_core::layout::flex::resolve(
|
cosmic::iced::core::layout::flex::resolve(
|
||||||
cosmic::iced_core::layout::flex::Axis::Horizontal,
|
cosmic::iced::core::layout::flex::Axis::Horizontal,
|
||||||
renderer,
|
renderer,
|
||||||
&limits,
|
&limits,
|
||||||
Length::Fill,
|
Length::Fill,
|
||||||
|
|
|
||||||
|
|
@ -1,24 +1,30 @@
|
||||||
use super::tab::{MIN_ACTIVE_TAB_WIDTH, Tab, TabBackgroundTheme, TabMessage, TabRuleTheme};
|
use super::tab::{MIN_ACTIVE_TAB_WIDTH, Tab, TabBackgroundTheme, TabMessage, TabRuleTheme};
|
||||||
use cosmic::{
|
use cosmic::{
|
||||||
Apply,
|
Apply,
|
||||||
iced::{Element, id::Id, widget},
|
iced::{
|
||||||
iced_core::{
|
Element,
|
||||||
Background, Border, Clipboard, Color, Length, Point, Rectangle, Renderer, Shell, Size,
|
core::{
|
||||||
Vector, event,
|
Background, Border, Clipboard, Color, Length, Point, Rectangle, Renderer, Shell, Size,
|
||||||
layout::{Layout, Limits, Node},
|
Vector, event,
|
||||||
mouse, overlay, renderer,
|
layout::{Layout, Limits, Node},
|
||||||
widget::{
|
mouse, overlay, renderer,
|
||||||
Widget,
|
widget::{
|
||||||
operation::{
|
Widget,
|
||||||
Operation, Scrollable,
|
operation::{
|
||||||
scrollable::{AbsoluteOffset, RelativeOffset},
|
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,
|
theme,
|
||||||
widget::{container::Catalog, icon::from_name},
|
widget::icon::from_name,
|
||||||
};
|
};
|
||||||
use keyframe::{
|
use keyframe::{
|
||||||
ease,
|
ease,
|
||||||
|
|
@ -379,8 +385,8 @@ where
|
||||||
>= MIN_ACTIVE_TAB_WIDTH
|
>= MIN_ACTIVE_TAB_WIDTH
|
||||||
{
|
{
|
||||||
// just use a flex layout
|
// just use a flex layout
|
||||||
cosmic::iced_core::layout::flex::resolve(
|
cosmic::iced::core::layout::flex::resolve(
|
||||||
cosmic::iced_core::layout::flex::Axis::Horizontal,
|
cosmic::iced::core::layout::flex::Axis::Horizontal,
|
||||||
renderer,
|
renderer,
|
||||||
&limits,
|
&limits,
|
||||||
self.width,
|
self.width,
|
||||||
|
|
@ -513,13 +519,11 @@ where
|
||||||
|
|
||||||
let background_style = Catalog::style(
|
let background_style = Catalog::style(
|
||||||
theme,
|
theme,
|
||||||
&theme::Container::custom(|theme| widget::container::Style {
|
&theme::Container::custom(|_theme| widget::container::Style {
|
||||||
snap: true,
|
snap: true,
|
||||||
icon_color: None,
|
icon_color: None,
|
||||||
text_color: None,
|
text_color: None,
|
||||||
background: Some(Background::Color(
|
background: Some(Background::Color(Color::TRANSPARENT)),
|
||||||
theme.cosmic().primary_container_color().into(),
|
|
||||||
)),
|
|
||||||
border: Border {
|
border: Border {
|
||||||
radius: 0.0.into(),
|
radius: 0.0.into(),
|
||||||
width: 0.0,
|
width: 0.0,
|
||||||
|
|
|
||||||
|
|
@ -1,6 +1,7 @@
|
||||||
use crate::{
|
use crate::{
|
||||||
|
backend::render::element::AsGlowRenderer,
|
||||||
fl,
|
fl,
|
||||||
utils::iced::{IcedElement, Program},
|
utils::iced::{IcedElement, IcedRenderElement, Program},
|
||||||
};
|
};
|
||||||
|
|
||||||
use calloop::LoopHandle;
|
use calloop::LoopHandle;
|
||||||
|
|
@ -8,22 +9,78 @@ use cosmic::{
|
||||||
Apply,
|
Apply,
|
||||||
iced::{
|
iced::{
|
||||||
Alignment,
|
Alignment,
|
||||||
|
core::{Background, Border, Color, Length},
|
||||||
widget::{container, row},
|
widget::{container, row},
|
||||||
},
|
},
|
||||||
iced_core::{Background, Border, Color, Length},
|
|
||||||
theme,
|
theme,
|
||||||
widget::{icon::from_name, space, text},
|
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<StackHoverInternal>;
|
#[derive(Debug)]
|
||||||
|
pub struct StackHover {
|
||||||
|
location: Point<i32, Logical>,
|
||||||
|
elem: IcedElement<StackHoverInternal>,
|
||||||
|
}
|
||||||
|
|
||||||
pub fn stack_hover(
|
impl StackHover {
|
||||||
evlh: LoopHandle<'static, crate::state::State>,
|
pub fn new(
|
||||||
size: Size<i32, Logical>,
|
evlh: LoopHandle<'static, crate::state::State>,
|
||||||
theme: cosmic::Theme,
|
size: Size<i32, Logical>,
|
||||||
) -> StackHover {
|
mut theme: cosmic::Theme,
|
||||||
StackHover::new(StackHoverInternal, size, evlh, 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::<i32, Logical>::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<R>(
|
||||||
|
&self,
|
||||||
|
renderer: &mut R,
|
||||||
|
location: Point<i32, Physical>,
|
||||||
|
scale: Scale<f64>,
|
||||||
|
alpha: f32,
|
||||||
|
push_above: &mut dyn FnMut(IcedRenderElement<R>),
|
||||||
|
push_below: Option<&mut dyn FnMut(IcedRenderElement<R>)>,
|
||||||
|
) 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;
|
pub struct StackHoverInternal;
|
||||||
|
|
@ -47,23 +104,30 @@ impl Program for StackHoverInternal {
|
||||||
.align_y(Alignment::Center)
|
.align_y(Alignment::Center)
|
||||||
.padding(16)
|
.padding(16)
|
||||||
.apply(container)
|
.apply(container)
|
||||||
.class(theme::Container::custom(|theme| container::Style {
|
.class(theme::Container::custom(|theme| {
|
||||||
snap: true,
|
let mut background = theme.cosmic().accent_color();
|
||||||
icon_color: Some(Color::from(theme.cosmic().accent.on)),
|
if theme.transparent {
|
||||||
text_color: Some(Color::from(theme.cosmic().accent.on)),
|
background.alpha = theme
|
||||||
background: Some(Background::Color(theme.cosmic().accent_color().into())),
|
.cosmic()
|
||||||
border: Border {
|
.alpha_map
|
||||||
radius: 18.0.into(),
|
.blurred_alpha(theme.cosmic().frosted);
|
||||||
width: 0.0,
|
}
|
||||||
color: Color::TRANSPARENT,
|
|
||||||
},
|
container::Style {
|
||||||
shadow: Default::default(),
|
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)
|
.width(Length::Shrink)
|
||||||
.height(Length::Shrink)
|
.height(Length::Shrink)
|
||||||
.apply(container)
|
|
||||||
.center_x(Length::Fill)
|
|
||||||
.center_y(Length::Fill)
|
|
||||||
.into()
|
.into()
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -1,7 +1,12 @@
|
||||||
use crate::{
|
use crate::{
|
||||||
|
backend::render::{
|
||||||
|
element::AsGlowRenderer,
|
||||||
|
wayland::{SurfaceRenderElement, push_render_elements_from_surface_tree},
|
||||||
|
},
|
||||||
shell::focus::target::PointerFocusTarget,
|
shell::focus::target::PointerFocusTarget,
|
||||||
wayland::{
|
wayland::handlers::{
|
||||||
handlers::compositor::frame_time_filter_fn, protocols::corner_radius::CacheableCorners,
|
background_effect::ComputedBlurRegionCachedState, compositor::frame_time_filter_fn,
|
||||||
|
corner_radius::surface_corners,
|
||||||
},
|
},
|
||||||
};
|
};
|
||||||
use std::{
|
use std::{
|
||||||
|
|
@ -14,11 +19,12 @@ use std::{
|
||||||
};
|
};
|
||||||
|
|
||||||
use smithay::{
|
use smithay::{
|
||||||
backend::renderer::{
|
backend::{
|
||||||
ImportAll, Renderer,
|
drm::DrmNode,
|
||||||
element::{
|
renderer::{
|
||||||
AsRenderElements, Kind, RenderElementStates,
|
ImportAll, Renderer, buffer_has_alpha,
|
||||||
surface::{WaylandSurfaceRenderElement, render_elements_from_surface_tree},
|
element::{Kind, RenderElementStates, surface::KindEvaluation},
|
||||||
|
utils::RendererSurfaceStateUserData,
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
desktop::{
|
desktop::{
|
||||||
|
|
@ -45,10 +51,12 @@ use smithay::{
|
||||||
IsAlive, Logical, Physical, Point, Rectangle, Scale, Serial, Size, user_data::UserDataMap,
|
IsAlive, Logical, Physical, Point, Rectangle, Scale, Serial, Size, user_data::UserDataMap,
|
||||||
},
|
},
|
||||||
wayland::{
|
wayland::{
|
||||||
|
alpha_modifier::AlphaModifierSurfaceCachedState,
|
||||||
compositor::{
|
compositor::{
|
||||||
SubsurfaceCachedState, SurfaceData, TraversalAction, get_parent, with_states,
|
SubsurfaceCachedState, SurfaceData, TraversalAction, get_parent, with_states,
|
||||||
with_surface_tree_downward,
|
with_surface_tree_downward,
|
||||||
},
|
},
|
||||||
|
dmabuf::get_dmabuf,
|
||||||
seat::WaylandFocus,
|
seat::WaylandFocus,
|
||||||
shell::xdg::{
|
shell::xdg::{
|
||||||
SurfaceCachedState, ToplevelCachedState, ToplevelSurface, XdgToplevelSurfaceData,
|
SurfaceCachedState, ToplevelCachedState, ToplevelSurface, XdgToplevelSurfaceData,
|
||||||
|
|
@ -67,6 +75,83 @@ use crate::{
|
||||||
},
|
},
|
||||||
};
|
};
|
||||||
|
|
||||||
|
/// The [`DrmNode`] the surface's currently committed buffer was allocated on, if it is a dmabuf.
|
||||||
|
fn buffer_node(data: &SurfaceData) -> Option<DrmNode> {
|
||||||
|
let surface_state = data.data_map.get::<RendererSurfaceStateUserData>()?;
|
||||||
|
let surface_state = surface_state.lock().unwrap();
|
||||||
|
surface_state
|
||||||
|
.buffer()
|
||||||
|
.and_then(|buffer| get_dmabuf(buffer).ok())
|
||||||
|
.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::<AlphaModifierSurfaceCachedState>();
|
||||||
|
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::<RendererSurfaceStateUserData>() 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::<ComputedBlurRegionCachedState>();
|
||||||
|
blur_state
|
||||||
|
.current()
|
||||||
|
.blur_region
|
||||||
|
.as_ref()
|
||||||
|
.is_some_and(|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
|
||||||
|
/// 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<bool>,
|
||||||
|
scanout_node: Option<DrmNode>,
|
||||||
|
alpha: f32,
|
||||||
|
) -> KindEvaluation {
|
||||||
|
match (scanout_override, scanout_node) {
|
||||||
|
// 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) && !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) && !is_likely_translucent(alpha, data) {
|
||||||
|
frame_time_filter_fn(data)
|
||||||
|
} else {
|
||||||
|
Kind::Unspecified
|
||||||
|
}
|
||||||
|
})),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
#[derive(Debug, Clone, PartialEq, Hash, Eq)]
|
#[derive(Debug, Clone, PartialEq, Hash, Eq)]
|
||||||
pub struct CosmicSurface(pub Window);
|
pub struct CosmicSurface(pub Window);
|
||||||
|
|
||||||
|
|
@ -144,22 +229,7 @@ impl CosmicSurface {
|
||||||
|
|
||||||
pub fn corner_radius(&self, geometry_size: Size<i32, Logical>) -> Option<[u8; 4]> {
|
pub fn corner_radius(&self, geometry_size: Size<i32, Logical>) -> Option<[u8; 4]> {
|
||||||
self.wl_surface().and_then(|surface| {
|
self.wl_surface().and_then(|surface| {
|
||||||
with_states(&surface, |states| {
|
with_states(&surface, |states| surface_corners(states, geometry_size))
|
||||||
let mut guard = states.cached_state.get::<CacheableCorners>();
|
|
||||||
|
|
||||||
// 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.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),
|
|
||||||
])
|
|
||||||
})
|
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -810,97 +880,112 @@ impl CosmicSurface {
|
||||||
self.0.user_data()
|
self.0.user_data()
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn popup_render_elements<R, C>(
|
pub fn push_popup_render_elements<R>(
|
||||||
&self,
|
&self,
|
||||||
renderer: &mut R,
|
renderer: &mut R,
|
||||||
location: Point<i32, Physical>,
|
location: Point<i32, Physical>,
|
||||||
scale: Scale<f64>,
|
scale: Scale<f64>,
|
||||||
alpha: f32,
|
alpha: f32,
|
||||||
) -> Vec<C>
|
scanout_node: Option<DrmNode>,
|
||||||
where
|
blur_strength: usize,
|
||||||
R: Renderer + ImportAll,
|
push: &mut dyn FnMut(SurfaceRenderElement<R>),
|
||||||
|
) where
|
||||||
|
R: Renderer + ImportAll + AsGlowRenderer,
|
||||||
R::TextureId: Clone + 'static,
|
R::TextureId: Clone + 'static,
|
||||||
C: From<WaylandSurfaceRenderElement<R>>,
|
|
||||||
{
|
{
|
||||||
match self.0.underlying_surface() {
|
match self.0.underlying_surface() {
|
||||||
WindowSurface::Wayland(toplevel) => {
|
WindowSurface::Wayland(toplevel) => {
|
||||||
let surface = toplevel.wl_surface();
|
let surface = toplevel.wl_surface();
|
||||||
PopupManager::popups_for_surface(surface)
|
for (popup, popup_offset) in PopupManager::popups_for_surface(surface) {
|
||||||
.flat_map(move |(popup, popup_offset)| {
|
let offset = (self.0.geometry().loc + popup_offset - popup.geometry().loc)
|
||||||
let offset = (self.0.geometry().loc + popup_offset - popup.geometry().loc)
|
.to_physical_precise_round(scale);
|
||||||
.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(
|
let radii = with_states(popup.wl_surface(), |states| {
|
||||||
renderer,
|
surface_corners(states, geometry.size.to_i32_round())
|
||||||
popup.wl_surface(),
|
|
||||||
location + offset,
|
|
||||||
scale,
|
|
||||||
alpha,
|
|
||||||
FRAME_TIME_FILTER,
|
|
||||||
)
|
|
||||||
})
|
})
|
||||||
.collect()
|
.unwrap_or([0; 4]);
|
||||||
|
|
||||||
|
push_render_elements_from_surface_tree(
|
||||||
|
renderer,
|
||||||
|
popup.wl_surface(),
|
||||||
|
location + offset,
|
||||||
|
geometry,
|
||||||
|
scale,
|
||||||
|
alpha,
|
||||||
|
false,
|
||||||
|
radii,
|
||||||
|
None,
|
||||||
|
blur_strength,
|
||||||
|
scanout_kind_eval(None, scanout_node, alpha),
|
||||||
|
push,
|
||||||
|
None,
|
||||||
|
)
|
||||||
|
}
|
||||||
}
|
}
|
||||||
WindowSurface::X11(_) => Vec::new(),
|
WindowSurface::X11(_) => {}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn render_elements<R, C>(
|
pub fn push_render_elements<R>(
|
||||||
&self,
|
&self,
|
||||||
renderer: &mut R,
|
renderer: &mut R,
|
||||||
location: Point<i32, Physical>,
|
location: Point<i32, Physical>,
|
||||||
scale: Scale<f64>,
|
scale: Scale<f64>,
|
||||||
alpha: f32,
|
alpha: f32,
|
||||||
scanout_override: Option<bool>,
|
scanout_override: Option<bool>,
|
||||||
) -> Vec<C>
|
scanout_node: Option<DrmNode>,
|
||||||
where
|
should_clip: bool,
|
||||||
R: Renderer + ImportAll,
|
radii: [u8; 4],
|
||||||
|
blur_strength: usize,
|
||||||
|
push_above: &mut dyn FnMut(SurfaceRenderElement<R>),
|
||||||
|
push_below: Option<&mut dyn FnMut(SurfaceRenderElement<R>)>,
|
||||||
|
) where
|
||||||
|
R: Renderer + ImportAll + AsGlowRenderer,
|
||||||
R::TextureId: Clone + 'static,
|
R::TextureId: Clone + 'static,
|
||||||
C: From<WaylandSurfaceRenderElement<R>>,
|
|
||||||
{
|
{
|
||||||
|
let mut geometry = self.0.geometry().to_f64();
|
||||||
|
geometry.loc += location.to_f64().to_logical(scale);
|
||||||
|
|
||||||
match self.0.underlying_surface() {
|
match self.0.underlying_surface() {
|
||||||
WindowSurface::Wayland(toplevel) => {
|
WindowSurface::Wayland(toplevel) => {
|
||||||
let surface = toplevel.wl_surface();
|
let surface = toplevel.wl_surface();
|
||||||
|
|
||||||
render_elements_from_surface_tree(
|
push_render_elements_from_surface_tree(
|
||||||
renderer,
|
renderer,
|
||||||
surface,
|
surface,
|
||||||
location,
|
location,
|
||||||
|
geometry,
|
||||||
scale,
|
scale,
|
||||||
alpha,
|
alpha,
|
||||||
scanout_override
|
should_clip,
|
||||||
.map(|val| {
|
radii,
|
||||||
if val {
|
None,
|
||||||
Kind::ScanoutCandidate
|
blur_strength,
|
||||||
} else {
|
scanout_kind_eval(scanout_override, scanout_node, alpha),
|
||||||
Kind::Unspecified
|
push_above,
|
||||||
}
|
push_below,
|
||||||
.into()
|
|
||||||
})
|
|
||||||
.unwrap_or(FRAME_TIME_FILTER),
|
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
WindowSurface::X11(surface) => {
|
WindowSurface::X11(surface) => {
|
||||||
let Some(surface) = surface.wl_surface() else {
|
let Some(surface) = surface.wl_surface() else {
|
||||||
return Vec::new();
|
return;
|
||||||
};
|
};
|
||||||
|
|
||||||
render_elements_from_surface_tree(
|
push_render_elements_from_surface_tree(
|
||||||
renderer,
|
renderer,
|
||||||
&surface,
|
&surface,
|
||||||
location,
|
location,
|
||||||
|
geometry,
|
||||||
scale,
|
scale,
|
||||||
alpha,
|
alpha,
|
||||||
scanout_override
|
should_clip,
|
||||||
.map(|val| {
|
radii,
|
||||||
if val {
|
None,
|
||||||
Kind::ScanoutCandidate
|
blur_strength,
|
||||||
} else {
|
scanout_kind_eval(scanout_override, scanout_node, alpha),
|
||||||
Kind::Unspecified
|
push_above,
|
||||||
}
|
push_below,
|
||||||
.into()
|
|
||||||
})
|
|
||||||
.unwrap_or(FRAME_TIME_FILTER),
|
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
@ -1044,24 +1129,6 @@ impl X11Relatable for CosmicSurface {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
impl<R> AsRenderElements<R> for CosmicSurface
|
|
||||||
where
|
|
||||||
R: Renderer + ImportAll,
|
|
||||||
R::TextureId: Clone + 'static,
|
|
||||||
{
|
|
||||||
type RenderElement = WaylandSurfaceRenderElement<R>;
|
|
||||||
|
|
||||||
fn render_elements<C: From<Self::RenderElement>>(
|
|
||||||
&self,
|
|
||||||
renderer: &mut R,
|
|
||||||
location: Point<i32, Physical>,
|
|
||||||
scale: Scale<f64>,
|
|
||||||
alpha: f32,
|
|
||||||
) -> Vec<C> {
|
|
||||||
self.0.render_elements(renderer, location, scale, alpha)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
fn with_toplevel_state<T, F: FnOnce(Option<&smithay::wayland::shell::xdg::ToplevelState>) -> T>(
|
fn with_toplevel_state<T, F: FnOnce(Option<&smithay::wayland::shell::xdg::ToplevelState>) -> T>(
|
||||||
toplevel: &ToplevelSurface,
|
toplevel: &ToplevelSurface,
|
||||||
pending: bool,
|
pending: bool,
|
||||||
|
|
|
||||||
|
|
@ -1,25 +1,88 @@
|
||||||
use crate::{
|
use crate::{
|
||||||
|
backend::render::element::AsGlowRenderer,
|
||||||
fl,
|
fl,
|
||||||
utils::iced::{IcedElement, Program},
|
utils::iced::{IcedElement, IcedRenderElement, Program},
|
||||||
};
|
};
|
||||||
|
|
||||||
use calloop::LoopHandle;
|
use calloop::LoopHandle;
|
||||||
use cosmic::{
|
use cosmic::{
|
||||||
Apply,
|
Apply,
|
||||||
iced::widget::{container, row, space},
|
iced::{
|
||||||
iced_core::{Alignment, Background, Border, Color, Length},
|
core::{Alignment, Background, Border, Color, Length},
|
||||||
|
widget::{container, row, space},
|
||||||
|
},
|
||||||
theme,
|
theme,
|
||||||
widget::{icon::from_name, text},
|
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<SwapIndicatorInternal>;
|
#[derive(Debug, Clone)]
|
||||||
|
pub struct SwapIndicator {
|
||||||
|
location: Point<i32, Logical>,
|
||||||
|
elem: IcedElement<SwapIndicatorInternal>,
|
||||||
|
}
|
||||||
|
|
||||||
pub fn swap_indicator(
|
impl SwapIndicator {
|
||||||
evlh: LoopHandle<'static, crate::state::State>,
|
pub fn new(
|
||||||
theme: cosmic::Theme,
|
evlh: LoopHandle<'static, crate::state::State>,
|
||||||
) -> SwapIndicator {
|
mut theme: cosmic::Theme,
|
||||||
SwapIndicator::new(SwapIndicatorInternal, Size::from((1, 1)), evlh, 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<i32, Logical>) {
|
||||||
|
let minimum = self.elem.minimum_size();
|
||||||
|
let new_size = Size::<i32, Logical>::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<R>(
|
||||||
|
&self,
|
||||||
|
renderer: &mut R,
|
||||||
|
location: Point<i32, Physical>,
|
||||||
|
scale: Scale<f64>,
|
||||||
|
alpha: f32,
|
||||||
|
push_above: &mut dyn FnMut(IcedRenderElement<R>),
|
||||||
|
push_below: Option<&mut dyn FnMut(IcedRenderElement<R>)>,
|
||||||
|
) 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;
|
pub struct SwapIndicatorInternal;
|
||||||
|
|
@ -43,23 +106,30 @@ impl Program for SwapIndicatorInternal {
|
||||||
.align_y(Alignment::Center)
|
.align_y(Alignment::Center)
|
||||||
.padding(16)
|
.padding(16)
|
||||||
.apply(container)
|
.apply(container)
|
||||||
.class(theme::Container::custom(|theme| container::Style {
|
.class(theme::Container::custom(|theme| {
|
||||||
snap: true,
|
let mut background = theme.cosmic().accent_color();
|
||||||
icon_color: Some(Color::from(theme.cosmic().accent.on)),
|
if theme.transparent {
|
||||||
text_color: Some(Color::from(theme.cosmic().accent.on)),
|
background.alpha = theme
|
||||||
background: Some(Background::Color(theme.cosmic().accent_color().into())),
|
.cosmic()
|
||||||
border: Border {
|
.alpha_map
|
||||||
radius: 18.0.into(),
|
.blurred_alpha(theme.cosmic().frosted);
|
||||||
width: 0.0,
|
}
|
||||||
color: Color::TRANSPARENT,
|
|
||||||
},
|
container::Style {
|
||||||
shadow: Default::default(),
|
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)
|
.width(Length::Shrink)
|
||||||
.height(Length::Shrink)
|
.height(Length::Shrink)
|
||||||
.apply(container)
|
|
||||||
.center_x(Length::Fill)
|
|
||||||
.center_y(Length::Fill)
|
|
||||||
.into()
|
.into()
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -1,10 +1,7 @@
|
||||||
use crate::{
|
use crate::{
|
||||||
backend::render::{
|
backend::render::{
|
||||||
IndicatorShader, Key, Usage,
|
IndicatorShader, Key, Usage, cursor::CursorState, element::AsGlowRenderer,
|
||||||
clipped_surface::ClippedSurfaceRenderElement,
|
shadow::ShadowShader, wayland::SurfaceRenderElement,
|
||||||
cursor::CursorState,
|
|
||||||
element::{AsGlowRenderer, FromGlesError},
|
|
||||||
shadow::ShadowShader,
|
|
||||||
},
|
},
|
||||||
hooks::{Decorations, HOOKS},
|
hooks::{Decorations, HOOKS},
|
||||||
shell::{
|
shell::{
|
||||||
|
|
@ -14,7 +11,7 @@ use crate::{
|
||||||
},
|
},
|
||||||
state::State,
|
state::State,
|
||||||
utils::{
|
utils::{
|
||||||
iced::{IcedElement, Program},
|
iced::{IcedElement, IcedRenderElement, Program},
|
||||||
prelude::*,
|
prelude::*,
|
||||||
},
|
},
|
||||||
};
|
};
|
||||||
|
|
@ -23,14 +20,11 @@ use cosmic::iced::{Color, Task};
|
||||||
use cosmic_comp_config::AppearanceConfig;
|
use cosmic_comp_config::AppearanceConfig;
|
||||||
use smithay::{
|
use smithay::{
|
||||||
backend::{
|
backend::{
|
||||||
|
drm::DrmNode,
|
||||||
input::KeyState,
|
input::KeyState,
|
||||||
renderer::{
|
renderer::{
|
||||||
ImportAll, ImportMem, Renderer,
|
ImportAll, ImportMem, Renderer,
|
||||||
element::{
|
element::{Element, Id as RendererId, Kind, RenderElement, UnderlyingStorage},
|
||||||
AsRenderElements, Element, Id as RendererId, Kind, RenderElement,
|
|
||||||
UnderlyingStorage, memory::MemoryRenderBufferRenderElement,
|
|
||||||
surface::WaylandSurfaceRenderElement,
|
|
||||||
},
|
|
||||||
gles::element::PixelShaderElement,
|
gles::element::PixelShaderElement,
|
||||||
glow::GlowRenderer,
|
glow::GlowRenderer,
|
||||||
utils::{CommitCounter, DamageSet, OpaqueRegions},
|
utils::{CommitCounter, DamageSet, OpaqueRegions},
|
||||||
|
|
@ -119,7 +113,7 @@ impl Focus {
|
||||||
location: Point<f64, Logical>,
|
location: Point<f64, Logical>,
|
||||||
) -> Option<Focus> {
|
) -> Option<Focus> {
|
||||||
let geo = surface.geometry();
|
let geo = surface.geometry();
|
||||||
let loc = location.to_i32_round::<i32>() - geo.loc;
|
let loc = location.to_i32_floor::<i32>() - geo.loc;
|
||||||
if loc.y < 0 && loc.x < 0 {
|
if loc.y < 0 && loc.x < 0 {
|
||||||
Some(Focus::ResizeTopLeft)
|
Some(Focus::ResizeTopLeft)
|
||||||
} else if loc.y < 0 && loc.x >= geo.size.w {
|
} else if loc.y < 0 && loc.x >= geo.size.w {
|
||||||
|
|
@ -196,12 +190,13 @@ impl CosmicWindow {
|
||||||
pub fn new(
|
pub fn new(
|
||||||
window: impl Into<CosmicSurface>,
|
window: impl Into<CosmicSurface>,
|
||||||
handle: LoopHandle<'static, crate::state::State>,
|
handle: LoopHandle<'static, crate::state::State>,
|
||||||
theme: cosmic::Theme,
|
mut theme: cosmic::Theme,
|
||||||
appearance: AppearanceConfig,
|
appearance: AppearanceConfig,
|
||||||
) -> CosmicWindow {
|
) -> CosmicWindow {
|
||||||
let window = window.into();
|
let window = window.into();
|
||||||
let width = window.geometry().size.w;
|
let width = window.geometry().size.w;
|
||||||
let last_title = window.title();
|
let last_title = window.title();
|
||||||
|
theme.transparent = theme.cosmic().frosted_windows;
|
||||||
|
|
||||||
if appearance.clip_floating_windows {
|
if appearance.clip_floating_windows {
|
||||||
window.set_tiled(true);
|
window.set_tiled(true);
|
||||||
|
|
@ -297,7 +292,7 @@ impl CosmicWindow {
|
||||||
{
|
{
|
||||||
let geo = p.window.geometry();
|
let geo = p.window.geometry();
|
||||||
|
|
||||||
let point_i32 = relative_pos.to_i32_round::<i32>();
|
let point_i32 = relative_pos.to_i32_floor::<i32>();
|
||||||
let ssd_height = if has_ssd { SSD_HEIGHT } else { 0 };
|
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)
|
if (point_i32.x - geo.loc.x >= -RESIZE_BORDER && point_i32.x - geo.loc.x < 0)
|
||||||
|
|
@ -351,17 +346,17 @@ impl CosmicWindow {
|
||||||
self.0.loop_handle()
|
self.0.loop_handle()
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn popup_render_elements<R, C>(
|
pub fn push_popup_render_elements<R>(
|
||||||
&self,
|
&self,
|
||||||
renderer: &mut R,
|
renderer: &mut R,
|
||||||
location: Point<i32, Physical>,
|
location: Point<i32, Physical>,
|
||||||
scale: Scale<f64>,
|
scale: Scale<f64>,
|
||||||
alpha: f32,
|
alpha: f32,
|
||||||
) -> Vec<C>
|
scanout_node: Option<DrmNode>,
|
||||||
where
|
push: &mut dyn FnMut(CosmicWindowRenderElement<R>),
|
||||||
R: Renderer + ImportAll + ImportMem,
|
) where
|
||||||
|
R: Renderer + AsGlowRenderer + ImportAll + ImportMem,
|
||||||
R::TextureId: Send + Clone + 'static,
|
R::TextureId: Send + Clone + 'static,
|
||||||
C: From<CosmicWindowRenderElement<R>>,
|
|
||||||
{
|
{
|
||||||
let has_ssd = self.0.with_program(|p| p.has_ssd(false));
|
let has_ssd = self.0.with_program(|p| p.has_ssd(false));
|
||||||
|
|
||||||
|
|
@ -372,13 +367,22 @@ impl CosmicWindow {
|
||||||
};
|
};
|
||||||
|
|
||||||
self.0.with_program(|p| {
|
self.0.with_program(|p| {
|
||||||
p.window
|
let theme = p.theme.lock().unwrap();
|
||||||
.popup_render_elements::<R, CosmicWindowRenderElement<R>>(
|
let frosted = if theme.cosmic().frosted_windows {
|
||||||
renderer, window_loc, scale, alpha,
|
(theme.cosmic().frosted as u8 + 1) as usize
|
||||||
)
|
} else {
|
||||||
.into_iter()
|
0
|
||||||
.map(C::from)
|
};
|
||||||
.collect()
|
|
||||||
|
p.window.push_popup_render_elements(
|
||||||
|
renderer,
|
||||||
|
window_loc,
|
||||||
|
scale,
|
||||||
|
alpha,
|
||||||
|
scanout_node,
|
||||||
|
frosted,
|
||||||
|
&mut |elem| push(elem.into()),
|
||||||
|
)
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -462,7 +466,7 @@ impl CosmicWindow {
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn render_elements<R, C>(
|
pub fn push_render_elements<R>(
|
||||||
&self,
|
&self,
|
||||||
renderer: &mut R,
|
renderer: &mut R,
|
||||||
location: Point<i32, Physical>,
|
location: Point<i32, Physical>,
|
||||||
|
|
@ -470,11 +474,12 @@ impl CosmicWindow {
|
||||||
scale: Scale<f64>,
|
scale: Scale<f64>,
|
||||||
alpha: f32,
|
alpha: f32,
|
||||||
scanout_override: Option<bool>,
|
scanout_override: Option<bool>,
|
||||||
) -> Vec<C>
|
scanout_node: Option<DrmNode>,
|
||||||
where
|
push_above: &mut dyn FnMut(CosmicWindowRenderElement<R>),
|
||||||
|
push_below: &mut dyn FnMut(CosmicWindowRenderElement<R>),
|
||||||
|
) where
|
||||||
R: AsGlowRenderer,
|
R: AsGlowRenderer,
|
||||||
R::TextureId: Send + Clone + 'static,
|
R::TextureId: Send + Clone + 'static,
|
||||||
C: From<CosmicWindowRenderElement<R>>,
|
|
||||||
{
|
{
|
||||||
let (has_ssd, is_tiled, is_maximized, mut radii, appearance) = self.0.with_program(|p| {
|
let (has_ssd, is_tiled, is_maximized, mut radii, appearance) = self.0.with_program(|p| {
|
||||||
(
|
(
|
||||||
|
|
@ -511,8 +516,6 @@ impl CosmicWindow {
|
||||||
location
|
location
|
||||||
};
|
};
|
||||||
|
|
||||||
let mut elements = Vec::new();
|
|
||||||
|
|
||||||
let (mut geo, bg_divider) = self.0.with_program(|p| {
|
let (mut geo, bg_divider) = self.0.with_program(|p| {
|
||||||
(
|
(
|
||||||
SpaceElement::geometry(&p.window).to_f64(),
|
SpaceElement::geometry(&p.window).to_f64(),
|
||||||
|
|
@ -542,55 +545,57 @@ impl CosmicWindow {
|
||||||
scale.x,
|
scale.x,
|
||||||
[r, g, b],
|
[r, g, b],
|
||||||
));
|
));
|
||||||
elements.push(elem);
|
push_above(elem);
|
||||||
}
|
}
|
||||||
|
|
||||||
let window_elements = self.0.with_program(|p| {
|
self.0.with_program(|p| {
|
||||||
p.window
|
let mut radii = radii;
|
||||||
.render_elements::<R, WaylandSurfaceRenderElement<R>>(
|
|
||||||
renderer,
|
|
||||||
window_loc,
|
|
||||||
scale,
|
|
||||||
alpha,
|
|
||||||
scanout_override,
|
|
||||||
)
|
|
||||||
});
|
|
||||||
if window_elements.is_empty() {
|
|
||||||
return Vec::new();
|
|
||||||
}
|
|
||||||
|
|
||||||
elements.extend(window_elements.into_iter().map(|elem| {
|
|
||||||
if has_ssd {
|
if has_ssd {
|
||||||
radii[1] = 0;
|
radii[1] = 0;
|
||||||
radii[3] = 0;
|
radii[3] = 0;
|
||||||
}
|
}
|
||||||
if radii.iter().any(|x| *x != 0)
|
let theme = p.theme.lock().unwrap();
|
||||||
&& clip
|
let frosted = if theme.cosmic().frosted_windows {
|
||||||
&& ClippedSurfaceRenderElement::will_clip(&elem, scale, geo, radii)
|
(theme.cosmic().frosted as u8 + 1) as usize
|
||||||
{
|
|
||||||
CosmicWindowRenderElement::Clipped(ClippedSurfaceRenderElement::new(
|
|
||||||
renderer, elem, scale, geo, radii,
|
|
||||||
))
|
|
||||||
} else {
|
} else {
|
||||||
CosmicWindowRenderElement::Window(elem)
|
0
|
||||||
}
|
};
|
||||||
}));
|
p.window.push_render_elements(
|
||||||
|
renderer,
|
||||||
|
window_loc,
|
||||||
|
scale,
|
||||||
|
alpha,
|
||||||
|
scanout_override,
|
||||||
|
scanout_node,
|
||||||
|
clip,
|
||||||
|
radii,
|
||||||
|
frosted,
|
||||||
|
&mut |elem| push_above(elem.into()),
|
||||||
|
Some(&mut |elem| push_below(elem.into())),
|
||||||
|
);
|
||||||
|
});
|
||||||
|
|
||||||
if has_ssd {
|
if has_ssd {
|
||||||
|
radii[0] = 0;
|
||||||
|
radii[2] = 0;
|
||||||
let ssd_loc = location
|
let ssd_loc = location
|
||||||
+ self
|
+ self
|
||||||
.0
|
.0
|
||||||
.with_program(|p| p.window.geometry().loc)
|
.with_program(|p| p.window.geometry().loc.to_physical_precise_round(scale));
|
||||||
.to_physical_precise_round(scale);
|
self.0.push_render_elements(
|
||||||
elements.extend(AsRenderElements::<R>::render_elements::<
|
renderer,
|
||||||
CosmicWindowRenderElement<R>,
|
ssd_loc,
|
||||||
>(&self.0, renderer, ssd_loc, scale, alpha))
|
scale,
|
||||||
|
alpha,
|
||||||
|
radii,
|
||||||
|
&mut |elem| push_above(elem.into()),
|
||||||
|
Some(&mut |elem| push_below(elem.into())),
|
||||||
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
elements.into_iter().map(C::from).collect()
|
|
||||||
}
|
}
|
||||||
|
|
||||||
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| {
|
self.0.with_program(|p| {
|
||||||
*p.theme.lock().unwrap() = theme.clone();
|
*p.theme.lock().unwrap() = theme.clone();
|
||||||
});
|
});
|
||||||
|
|
@ -1243,41 +1248,33 @@ impl WaylandFocus for CosmicWindow {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
pub enum CosmicWindowRenderElement<R: Renderer + ImportAll + ImportMem> {
|
pub enum CosmicWindowRenderElement<R: AsGlowRenderer + ImportAll + ImportMem> {
|
||||||
Header(MemoryRenderBufferRenderElement<R>),
|
Header(IcedRenderElement<R>),
|
||||||
Shadow(PixelShaderElement),
|
Shadow(PixelShaderElement),
|
||||||
Border(PixelShaderElement),
|
Border(PixelShaderElement),
|
||||||
Window(WaylandSurfaceRenderElement<R>),
|
Window(SurfaceRenderElement<R>),
|
||||||
Clipped(ClippedSurfaceRenderElement<R>),
|
|
||||||
}
|
}
|
||||||
|
|
||||||
impl<R: Renderer + ImportAll + ImportMem> From<MemoryRenderBufferRenderElement<R>>
|
impl<R: AsGlowRenderer + ImportAll + ImportMem> From<IcedRenderElement<R>>
|
||||||
for CosmicWindowRenderElement<R>
|
for CosmicWindowRenderElement<R>
|
||||||
{
|
{
|
||||||
fn from(value: MemoryRenderBufferRenderElement<R>) -> Self {
|
fn from(value: IcedRenderElement<R>) -> Self {
|
||||||
Self::Header(value)
|
Self::Header(value)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
impl<R: Renderer + ImportAll + ImportMem> From<WaylandSurfaceRenderElement<R>>
|
impl<R: AsGlowRenderer + ImportAll + ImportMem> From<SurfaceRenderElement<R>>
|
||||||
for CosmicWindowRenderElement<R>
|
for CosmicWindowRenderElement<R>
|
||||||
{
|
{
|
||||||
fn from(value: WaylandSurfaceRenderElement<R>) -> Self {
|
fn from(value: SurfaceRenderElement<R>) -> Self {
|
||||||
Self::Window(value)
|
Self::Window(value)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
impl<R: Renderer + ImportAll + ImportMem> From<ClippedSurfaceRenderElement<R>>
|
|
||||||
for CosmicWindowRenderElement<R>
|
|
||||||
{
|
|
||||||
fn from(value: ClippedSurfaceRenderElement<R>) -> Self {
|
|
||||||
Self::Clipped(value)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
impl<R> Element for CosmicWindowRenderElement<R>
|
impl<R> Element for CosmicWindowRenderElement<R>
|
||||||
where
|
where
|
||||||
R: Renderer + ImportAll + ImportMem,
|
R: Renderer + ImportAll + ImportMem + AsGlowRenderer,
|
||||||
|
R::TextureId: Send + 'static,
|
||||||
{
|
{
|
||||||
fn id(&self) -> &RendererId {
|
fn id(&self) -> &RendererId {
|
||||||
match self {
|
match self {
|
||||||
|
|
@ -1285,7 +1282,6 @@ where
|
||||||
CosmicWindowRenderElement::Shadow(elem) => elem.id(),
|
CosmicWindowRenderElement::Shadow(elem) => elem.id(),
|
||||||
CosmicWindowRenderElement::Border(elem) => elem.id(),
|
CosmicWindowRenderElement::Border(elem) => elem.id(),
|
||||||
CosmicWindowRenderElement::Window(elem) => elem.id(),
|
CosmicWindowRenderElement::Window(elem) => elem.id(),
|
||||||
CosmicWindowRenderElement::Clipped(elem) => elem.id(),
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -1295,7 +1291,6 @@ where
|
||||||
CosmicWindowRenderElement::Shadow(elem) => elem.current_commit(),
|
CosmicWindowRenderElement::Shadow(elem) => elem.current_commit(),
|
||||||
CosmicWindowRenderElement::Border(elem) => elem.current_commit(),
|
CosmicWindowRenderElement::Border(elem) => elem.current_commit(),
|
||||||
CosmicWindowRenderElement::Window(elem) => elem.current_commit(),
|
CosmicWindowRenderElement::Window(elem) => elem.current_commit(),
|
||||||
CosmicWindowRenderElement::Clipped(elem) => elem.current_commit(),
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -1305,7 +1300,6 @@ where
|
||||||
CosmicWindowRenderElement::Shadow(elem) => elem.src(),
|
CosmicWindowRenderElement::Shadow(elem) => elem.src(),
|
||||||
CosmicWindowRenderElement::Border(elem) => elem.src(),
|
CosmicWindowRenderElement::Border(elem) => elem.src(),
|
||||||
CosmicWindowRenderElement::Window(elem) => elem.src(),
|
CosmicWindowRenderElement::Window(elem) => elem.src(),
|
||||||
CosmicWindowRenderElement::Clipped(elem) => elem.src(),
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -1315,7 +1309,6 @@ where
|
||||||
CosmicWindowRenderElement::Shadow(elem) => elem.geometry(scale),
|
CosmicWindowRenderElement::Shadow(elem) => elem.geometry(scale),
|
||||||
CosmicWindowRenderElement::Border(elem) => elem.geometry(scale),
|
CosmicWindowRenderElement::Border(elem) => elem.geometry(scale),
|
||||||
CosmicWindowRenderElement::Window(elem) => elem.geometry(scale),
|
CosmicWindowRenderElement::Window(elem) => elem.geometry(scale),
|
||||||
CosmicWindowRenderElement::Clipped(elem) => elem.geometry(scale),
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -1325,7 +1318,6 @@ where
|
||||||
CosmicWindowRenderElement::Shadow(elem) => elem.location(scale),
|
CosmicWindowRenderElement::Shadow(elem) => elem.location(scale),
|
||||||
CosmicWindowRenderElement::Border(elem) => elem.location(scale),
|
CosmicWindowRenderElement::Border(elem) => elem.location(scale),
|
||||||
CosmicWindowRenderElement::Window(elem) => elem.location(scale),
|
CosmicWindowRenderElement::Window(elem) => elem.location(scale),
|
||||||
CosmicWindowRenderElement::Clipped(elem) => elem.location(scale),
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -1335,7 +1327,6 @@ where
|
||||||
CosmicWindowRenderElement::Shadow(elem) => elem.transform(),
|
CosmicWindowRenderElement::Shadow(elem) => elem.transform(),
|
||||||
CosmicWindowRenderElement::Border(elem) => elem.transform(),
|
CosmicWindowRenderElement::Border(elem) => elem.transform(),
|
||||||
CosmicWindowRenderElement::Window(elem) => elem.transform(),
|
CosmicWindowRenderElement::Window(elem) => elem.transform(),
|
||||||
CosmicWindowRenderElement::Clipped(elem) => elem.transform(),
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -1349,7 +1340,6 @@ where
|
||||||
CosmicWindowRenderElement::Shadow(elem) => elem.damage_since(scale, commit),
|
CosmicWindowRenderElement::Shadow(elem) => elem.damage_since(scale, commit),
|
||||||
CosmicWindowRenderElement::Border(elem) => elem.damage_since(scale, commit),
|
CosmicWindowRenderElement::Border(elem) => elem.damage_since(scale, commit),
|
||||||
CosmicWindowRenderElement::Window(elem) => elem.damage_since(scale, commit),
|
CosmicWindowRenderElement::Window(elem) => elem.damage_since(scale, commit),
|
||||||
CosmicWindowRenderElement::Clipped(elem) => elem.damage_since(scale, commit),
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -1359,7 +1349,6 @@ where
|
||||||
CosmicWindowRenderElement::Shadow(elem) => elem.opaque_regions(scale),
|
CosmicWindowRenderElement::Shadow(elem) => elem.opaque_regions(scale),
|
||||||
CosmicWindowRenderElement::Border(elem) => elem.opaque_regions(scale),
|
CosmicWindowRenderElement::Border(elem) => elem.opaque_regions(scale),
|
||||||
CosmicWindowRenderElement::Window(elem) => elem.opaque_regions(scale),
|
CosmicWindowRenderElement::Window(elem) => elem.opaque_regions(scale),
|
||||||
CosmicWindowRenderElement::Clipped(elem) => elem.opaque_regions(scale),
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -1369,7 +1358,6 @@ where
|
||||||
CosmicWindowRenderElement::Shadow(elem) => elem.alpha(),
|
CosmicWindowRenderElement::Shadow(elem) => elem.alpha(),
|
||||||
CosmicWindowRenderElement::Border(elem) => elem.alpha(),
|
CosmicWindowRenderElement::Border(elem) => elem.alpha(),
|
||||||
CosmicWindowRenderElement::Window(elem) => elem.alpha(),
|
CosmicWindowRenderElement::Window(elem) => elem.alpha(),
|
||||||
CosmicWindowRenderElement::Clipped(elem) => elem.alpha(),
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -1379,7 +1367,6 @@ where
|
||||||
CosmicWindowRenderElement::Shadow(elem) => elem.kind(),
|
CosmicWindowRenderElement::Shadow(elem) => elem.kind(),
|
||||||
CosmicWindowRenderElement::Border(elem) => elem.kind(),
|
CosmicWindowRenderElement::Border(elem) => elem.kind(),
|
||||||
CosmicWindowRenderElement::Window(elem) => elem.kind(),
|
CosmicWindowRenderElement::Window(elem) => elem.kind(),
|
||||||
CosmicWindowRenderElement::Clipped(elem) => elem.kind(),
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -1389,7 +1376,6 @@ where
|
||||||
CosmicWindowRenderElement::Shadow(elem) => elem.is_framebuffer_effect(),
|
CosmicWindowRenderElement::Shadow(elem) => elem.is_framebuffer_effect(),
|
||||||
CosmicWindowRenderElement::Border(elem) => elem.is_framebuffer_effect(),
|
CosmicWindowRenderElement::Border(elem) => elem.is_framebuffer_effect(),
|
||||||
CosmicWindowRenderElement::Window(elem) => elem.is_framebuffer_effect(),
|
CosmicWindowRenderElement::Window(elem) => elem.is_framebuffer_effect(),
|
||||||
CosmicWindowRenderElement::Clipped(elem) => elem.is_framebuffer_effect(),
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
@ -1397,8 +1383,7 @@ where
|
||||||
impl<R> RenderElement<R> for CosmicWindowRenderElement<R>
|
impl<R> RenderElement<R> for CosmicWindowRenderElement<R>
|
||||||
where
|
where
|
||||||
R: AsGlowRenderer,
|
R: AsGlowRenderer,
|
||||||
R::TextureId: 'static,
|
R::TextureId: Send + 'static,
|
||||||
R::Error: FromGlesError,
|
|
||||||
{
|
{
|
||||||
fn draw(
|
fn draw(
|
||||||
&self,
|
&self,
|
||||||
|
|
@ -1423,14 +1408,11 @@ where
|
||||||
opaque_regions,
|
opaque_regions,
|
||||||
cache,
|
cache,
|
||||||
)
|
)
|
||||||
.map_err(FromGlesError::from_gles_error)
|
.map_err(R::from_gles_error)
|
||||||
}
|
}
|
||||||
CosmicWindowRenderElement::Window(elem) => {
|
CosmicWindowRenderElement::Window(elem) => {
|
||||||
elem.draw(frame, src, dst, damage, opaque_regions, cache)
|
elem.draw(frame, src, dst, damage, opaque_regions, cache)
|
||||||
}
|
}
|
||||||
CosmicWindowRenderElement::Clipped(elem) => {
|
|
||||||
elem.draw(frame, src, dst, damage, opaque_regions, cache)
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -1441,7 +1423,6 @@ where
|
||||||
elem.underlying_storage(renderer.glow_renderer_mut())
|
elem.underlying_storage(renderer.glow_renderer_mut())
|
||||||
}
|
}
|
||||||
CosmicWindowRenderElement::Window(elem) => elem.underlying_storage(renderer),
|
CosmicWindowRenderElement::Window(elem) => elem.underlying_storage(renderer),
|
||||||
CosmicWindowRenderElement::Clipped(elem) => elem.underlying_storage(renderer),
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -1464,14 +1445,11 @@ where
|
||||||
dst,
|
dst,
|
||||||
cache,
|
cache,
|
||||||
)
|
)
|
||||||
.map_err(FromGlesError::from_gles_error)
|
.map_err(R::from_gles_error)
|
||||||
}
|
}
|
||||||
CosmicWindowRenderElement::Window(elem) => {
|
CosmicWindowRenderElement::Window(elem) => {
|
||||||
elem.capture_framebuffer(frame, src, dst, cache)
|
elem.capture_framebuffer(frame, src, dst, cache)
|
||||||
}
|
}
|
||||||
CosmicWindowRenderElement::Clipped(elem) => {
|
|
||||||
elem.capture_framebuffer(frame, src, dst, cache)
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -378,6 +378,14 @@ fn update_focus_state(
|
||||||
.cloned()
|
.cloned()
|
||||||
.unwrap_or(seat.active_output());
|
.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)
|
let focus = State::surface_under(new_pos, &output, &shell)
|
||||||
.map(|(focus, loc)| (focus, loc.as_logical()));
|
.map(|(focus, loc)| (focus, loc.as_logical()));
|
||||||
//drop here to avoid multiple borrows
|
//drop here to avoid multiple borrows
|
||||||
|
|
|
||||||
|
|
@ -32,10 +32,12 @@ pub enum Stage<'a> {
|
||||||
layer: LayerSurface,
|
layer: LayerSurface,
|
||||||
popup: &'a PopupKind,
|
popup: &'a PopupKind,
|
||||||
location: Point<i32, Global>,
|
location: Point<i32, Global>,
|
||||||
|
workspace_idx: usize,
|
||||||
},
|
},
|
||||||
LayerSurface {
|
LayerSurface {
|
||||||
layer: LayerSurface,
|
layer: LayerSurface,
|
||||||
location: Point<i32, Global>,
|
location: Point<i32, Global>,
|
||||||
|
workspace_idx: usize,
|
||||||
},
|
},
|
||||||
OverrideRedirect {
|
OverrideRedirect {
|
||||||
surface: &'a X11Surface,
|
surface: &'a X11Surface,
|
||||||
|
|
@ -95,10 +97,15 @@ fn render_input_order_internal<R: 'static>(
|
||||||
layer,
|
layer,
|
||||||
popup: &popup,
|
popup: &popup,
|
||||||
location,
|
location,
|
||||||
|
workspace_idx: current.1,
|
||||||
})?;
|
})?;
|
||||||
}
|
}
|
||||||
for (layer, location) in layer_surfaces(output, Layer::Overlay, element_filter) {
|
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
|
// calculate a bunch of stuff for workspace transitions
|
||||||
|
|
@ -187,7 +194,7 @@ fn render_input_order_internal<R: 'static>(
|
||||||
});
|
});
|
||||||
|
|
||||||
(
|
(
|
||||||
Some((previous, has_fullscreen, offset)),
|
Some((previous, previous_idx, has_fullscreen, offset)),
|
||||||
Point::<i32, Logical>::from(match (layout, forward) {
|
Point::<i32, Logical>::from(match (layout, forward) {
|
||||||
(WorkspaceLayout::Vertical, true) => (0, output_size.h + offset.y),
|
(WorkspaceLayout::Vertical, true) => (0, output_size.h + offset.y),
|
||||||
(WorkspaceLayout::Vertical, false) => (0, -(output_size.h - offset.y)),
|
(WorkspaceLayout::Vertical, false) => (0, -(output_size.h - offset.y)),
|
||||||
|
|
@ -206,6 +213,7 @@ fn render_input_order_internal<R: 'static>(
|
||||||
layer,
|
layer,
|
||||||
popup: &popup,
|
popup: &popup,
|
||||||
location,
|
location,
|
||||||
|
workspace_idx: current.1,
|
||||||
})?;
|
})?;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
@ -236,7 +244,7 @@ fn render_input_order_internal<R: 'static>(
|
||||||
|
|
||||||
if element_filter != ElementFilter::LayerShellOnly {
|
if element_filter != ElementFilter::LayerShellOnly {
|
||||||
// previous workspace popups
|
// 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 {
|
let Some(workspace) = shell.workspaces.space_for_handle(previous_handle) else {
|
||||||
return ControlFlow::Break(Err(OutputNoMode));
|
return ControlFlow::Break(Err(OutputNoMode));
|
||||||
};
|
};
|
||||||
|
|
@ -265,11 +273,12 @@ fn render_input_order_internal<R: 'static>(
|
||||||
layer,
|
layer,
|
||||||
popup: &popup,
|
popup: &popup,
|
||||||
location,
|
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
|
&& !has_fullscreen
|
||||||
{
|
{
|
||||||
// previous bottom layer popups
|
// previous bottom layer popups
|
||||||
|
|
@ -278,6 +287,7 @@ fn render_input_order_internal<R: 'static>(
|
||||||
layer,
|
layer,
|
||||||
popup: &popup,
|
popup: &popup,
|
||||||
location: location + offset.as_global(),
|
location: location + offset.as_global(),
|
||||||
|
workspace_idx: **idx,
|
||||||
})?;
|
})?;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
@ -289,11 +299,12 @@ fn render_input_order_internal<R: 'static>(
|
||||||
layer,
|
layer,
|
||||||
popup: &popup,
|
popup: &popup,
|
||||||
location,
|
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
|
&& !has_fullscreen
|
||||||
{
|
{
|
||||||
// previous background layer popups
|
// previous background layer popups
|
||||||
|
|
@ -302,6 +313,7 @@ fn render_input_order_internal<R: 'static>(
|
||||||
layer,
|
layer,
|
||||||
popup: &popup,
|
popup: &popup,
|
||||||
location: location + offset.as_global(),
|
location: location + offset.as_global(),
|
||||||
|
workspace_idx: **idx,
|
||||||
})?;
|
})?;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
@ -309,7 +321,11 @@ fn render_input_order_internal<R: 'static>(
|
||||||
if !has_focused_fullscreen {
|
if !has_focused_fullscreen {
|
||||||
// top-layer shell
|
// top-layer shell
|
||||||
for (layer, location) in layer_surfaces(output, Layer::Top, element_filter) {
|
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<R: 'static>(
|
||||||
})?;
|
})?;
|
||||||
|
|
||||||
// previous workspace windows
|
// 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 {
|
let Some(workspace) = shell.workspaces.space_for_handle(previous_handle) else {
|
||||||
return ControlFlow::Break(Err(OutputNoMode));
|
return ControlFlow::Break(Err(OutputNoMode));
|
||||||
};
|
};
|
||||||
|
|
@ -341,17 +357,25 @@ fn render_input_order_internal<R: 'static>(
|
||||||
// bottom layer
|
// bottom layer
|
||||||
for (layer, mut location) in layer_surfaces(output, Layer::Bottom, element_filter) {
|
for (layer, mut location) in layer_surfaces(output, Layer::Bottom, element_filter) {
|
||||||
location += current_offset.as_global();
|
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
|
&& !has_fullscreen
|
||||||
{
|
{
|
||||||
// previous bottom layer
|
// previous bottom layer
|
||||||
for (layer, mut location) in layer_surfaces(output, Layer::Bottom, element_filter) {
|
for (layer, mut location) in layer_surfaces(output, Layer::Bottom, element_filter) {
|
||||||
location += offset.as_global();
|
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<R: 'static>(
|
||||||
// background layer
|
// background layer
|
||||||
for (layer, mut location) in layer_surfaces(output, Layer::Background, element_filter) {
|
for (layer, mut location) in layer_surfaces(output, Layer::Background, element_filter) {
|
||||||
location += current_offset.as_global();
|
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
|
&& !has_fullscreen
|
||||||
{
|
{
|
||||||
// previous background layer
|
// previous background layer
|
||||||
for (layer, mut location) in layer_surfaces(output, Layer::Background, element_filter) {
|
for (layer, mut location) in layer_surfaces(output, Layer::Background, element_filter) {
|
||||||
location += offset.as_global();
|
location += offset.as_global();
|
||||||
callback(Stage::LayerSurface { layer, location })?;
|
callback(Stage::LayerSurface {
|
||||||
|
layer,
|
||||||
|
location,
|
||||||
|
workspace_idx: **idx,
|
||||||
|
})?;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -6,7 +6,7 @@ use std::{
|
||||||
|
|
||||||
use crate::{
|
use crate::{
|
||||||
shell::{
|
shell::{
|
||||||
CosmicSurface, SeatExt,
|
CosmicSurface, CursorGeometry, SeatExt,
|
||||||
element::{CosmicMapped, CosmicStack, CosmicWindow},
|
element::{CosmicMapped, CosmicStack, CosmicWindow},
|
||||||
layout::tiling::ResizeForkTarget,
|
layout::tiling::ResizeForkTarget,
|
||||||
zoom::ZoomFocusTarget,
|
zoom::ZoomFocusTarget,
|
||||||
|
|
@ -272,7 +272,7 @@ impl PointerFocusTarget {
|
||||||
None
|
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())
|
seat.cursor_geometry((0.0, 0.0), Duration::from_millis(event.time as u64).into())
|
||||||
{
|
{
|
||||||
hotspot
|
hotspot
|
||||||
|
|
|
||||||
|
|
@ -1,10 +1,12 @@
|
||||||
use cosmic::{
|
use cosmic::{
|
||||||
iced::Element,
|
iced::{
|
||||||
iced_core::{
|
Alignment, Element,
|
||||||
Background, Border, Clipboard, Color, Event, Layout, Length, Rectangle,
|
core::{
|
||||||
Renderer as IcedRenderer, Shell, Size, layout, mouse, overlay,
|
Background, Border, Clipboard, Color, Event, Layout, Length, Rectangle,
|
||||||
renderer::{Quad, Style},
|
Renderer as IcedRenderer, Shell, Size, layout, mouse, overlay,
|
||||||
widget::{Id, Tree, Widget, tree},
|
renderer::{Quad, Style},
|
||||||
|
widget::{Id, Tree, Widget, tree},
|
||||||
|
},
|
||||||
},
|
},
|
||||||
widget::button::Catalog,
|
widget::button::Catalog,
|
||||||
};
|
};
|
||||||
|
|
@ -73,11 +75,26 @@ where
|
||||||
viewport: &Rectangle,
|
viewport: &Rectangle,
|
||||||
) {
|
) {
|
||||||
let widget_state = state.state.downcast_ref::<State>();
|
let widget_state = state.state.downcast_ref::<State>();
|
||||||
let styling = if widget_state.cursor_over {
|
let mut styling = if widget_state.cursor_over {
|
||||||
theme.hovered(true, false, &self.styling)
|
theme.hovered(true, false, &self.styling)
|
||||||
} else {
|
} else {
|
||||||
theme.active(true, false, &self.styling)
|
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(
|
renderer.fill_quad(
|
||||||
Quad {
|
Quad {
|
||||||
|
|
@ -85,7 +102,7 @@ where
|
||||||
bounds: layout.bounds(),
|
bounds: layout.bounds(),
|
||||||
border: Border {
|
border: Border {
|
||||||
radius: styling.border_radius,
|
radius: styling.border_radius,
|
||||||
width: styling.border_width,
|
width: 0.,
|
||||||
color: styling.border_color,
|
color: styling.border_color,
|
||||||
},
|
},
|
||||||
shadow: Default::default(),
|
shadow: Default::default(),
|
||||||
|
|
|
||||||
|
|
@ -9,19 +9,18 @@ use std::{
|
||||||
use calloop::LoopHandle;
|
use calloop::LoopHandle;
|
||||||
use cosmic::{
|
use cosmic::{
|
||||||
Apply as _, Task,
|
Apply as _, Task,
|
||||||
iced::{Alignment, Background},
|
iced::{
|
||||||
iced_core::{Border, Length, Rectangle as IcedRectangle, alignment::Horizontal},
|
Alignment, Background,
|
||||||
iced_widget::{self, Column, Row, text::Style as TextStyle},
|
core::{Border, Length, Rectangle as IcedRectangle, alignment::Horizontal},
|
||||||
|
widget::{self as iced_widget, Row, text::Style as TextStyle},
|
||||||
|
},
|
||||||
theme,
|
theme,
|
||||||
widget::{button, divider, icon::from_name, space, text},
|
widget::{button, divider, icon::from_name, menu::menu_column::MenuColumn, space, text},
|
||||||
};
|
};
|
||||||
use smithay::{
|
use smithay::{
|
||||||
backend::{
|
backend::{
|
||||||
input::{ButtonState, TouchSlot},
|
input::{ButtonState, TouchSlot},
|
||||||
renderer::{
|
renderer::ImportMem,
|
||||||
ImportMem, Renderer,
|
|
||||||
element::{AsRenderElements, memory::MemoryRenderBufferRenderElement},
|
|
||||||
},
|
|
||||||
},
|
},
|
||||||
desktop::space::SpaceElement,
|
desktop::space::SpaceElement,
|
||||||
input::{
|
input::{
|
||||||
|
|
@ -43,10 +42,11 @@ use smithay::{
|
||||||
};
|
};
|
||||||
|
|
||||||
use crate::{
|
use crate::{
|
||||||
|
backend::render::element::AsGlowRenderer,
|
||||||
shell::{SeatExt, focus::target::PointerFocusTarget},
|
shell::{SeatExt, focus::target::PointerFocusTarget},
|
||||||
state::State,
|
state::State,
|
||||||
utils::{
|
utils::{
|
||||||
iced::{IcedElement, Program},
|
iced::{IcedElement, IcedRenderElement, Program},
|
||||||
prelude::*,
|
prelude::*,
|
||||||
},
|
},
|
||||||
};
|
};
|
||||||
|
|
@ -65,29 +65,32 @@ pub struct MenuGrabState {
|
||||||
pub type SeatMenuGrabState = Mutex<Option<MenuGrabState>>;
|
pub type SeatMenuGrabState = Mutex<Option<MenuGrabState>>;
|
||||||
|
|
||||||
impl MenuGrabState {
|
impl MenuGrabState {
|
||||||
pub fn render<I, R>(&self, renderer: &mut R, output: &Output) -> Vec<I>
|
pub fn render<R>(
|
||||||
where
|
&self,
|
||||||
R: Renderer + ImportMem,
|
renderer: &mut R,
|
||||||
|
output: &Output,
|
||||||
|
push: &mut dyn FnMut(IcedRenderElement<R>),
|
||||||
|
) where
|
||||||
|
R: AsGlowRenderer + ImportMem,
|
||||||
R::TextureId: Send + Clone + 'static,
|
R::TextureId: Send + Clone + 'static,
|
||||||
I: From<MemoryRenderBufferRenderElement<R>>,
|
|
||||||
{
|
{
|
||||||
let scale = output.current_scale().fractional_scale();
|
let scale = output.current_scale().fractional_scale();
|
||||||
self.elements
|
for elem in self.elements.lock().unwrap().iter() {
|
||||||
.lock()
|
elem.iced.push_render_elements(
|
||||||
.unwrap()
|
renderer,
|
||||||
.iter()
|
elem.position
|
||||||
.flat_map(|elem| {
|
.to_local(output)
|
||||||
elem.iced.render_elements(
|
.as_logical()
|
||||||
renderer,
|
.to_physical_precise_round(scale),
|
||||||
elem.position
|
scale.into(),
|
||||||
.to_local(output)
|
1.0,
|
||||||
.as_logical()
|
elem.iced
|
||||||
.to_physical_precise_round(scale),
|
.with_theme(|theme| theme.cosmic().radius_s())
|
||||||
scale.into(),
|
.map(|x| x.round() as u8),
|
||||||
1.0,
|
push,
|
||||||
)
|
None,
|
||||||
})
|
)
|
||||||
.collect()
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn is_in_screen_space(&self) -> bool {
|
pub fn is_in_screen_space(&self) -> bool {
|
||||||
|
|
@ -276,11 +279,13 @@ impl Program for ContextMenu {
|
||||||
let mut elements = grab_state.elements.lock().unwrap();
|
let mut elements = grab_state.elements.lock().unwrap();
|
||||||
|
|
||||||
let position = elements.last().unwrap().position;
|
let position = elements.last().unwrap().position;
|
||||||
|
let mut theme = state.common.theme.clone();
|
||||||
|
theme.transparent = theme.cosmic().frosted_system_interface;
|
||||||
let element = IcedElement::new(
|
let element = IcedElement::new(
|
||||||
ContextMenu::new(items),
|
ContextMenu::new(items),
|
||||||
Size::default(),
|
Size::default(),
|
||||||
state.common.event_loop_handle.clone(),
|
state.common.event_loop_handle.clone(),
|
||||||
state.common.theme.clone(),
|
theme,
|
||||||
);
|
);
|
||||||
|
|
||||||
let min_size = element.minimum_size();
|
let min_size = element.minimum_size();
|
||||||
|
|
@ -295,7 +300,7 @@ impl Program for ContextMenu {
|
||||||
Rectangle::new(
|
Rectangle::new(
|
||||||
position
|
position
|
||||||
+ Point::from((
|
+ Point::from((
|
||||||
bounds.width.ceil() as i32,
|
bounds.width.floor() as i32,
|
||||||
bounds.y.ceil() as i32,
|
bounds.y.ceil() as i32,
|
||||||
)),
|
)),
|
||||||
min_size.as_global(),
|
min_size.as_global(),
|
||||||
|
|
@ -304,7 +309,7 @@ impl Program for ContextMenu {
|
||||||
Rectangle::new(
|
Rectangle::new(
|
||||||
position
|
position
|
||||||
+ Point::from((
|
+ Point::from((
|
||||||
bounds.width.ceil() as i32,
|
bounds.width.floor() as i32,
|
||||||
bounds.y.ceil() as i32 + bounds.height.ceil() as i32
|
bounds.y.ceil() as i32 + bounds.height.ceil() as i32
|
||||||
- min_size.h,
|
- min_size.h,
|
||||||
)),
|
)),
|
||||||
|
|
@ -312,14 +317,15 @@ impl Program for ContextMenu {
|
||||||
),
|
),
|
||||||
// to the left -> down
|
// to the left -> down
|
||||||
Rectangle::new(
|
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(),
|
min_size.as_global(),
|
||||||
),
|
),
|
||||||
// to the left -> up
|
// to the left -> up
|
||||||
Rectangle::new(
|
Rectangle::new(
|
||||||
position
|
position
|
||||||
+ Point::from((
|
+ Point::from((
|
||||||
-min_size.w,
|
-min_size.w + 1,
|
||||||
bounds.y.ceil() as i32 + bounds.height.ceil() as i32
|
bounds.y.ceil() as i32 + bounds.height.ceil() as i32
|
||||||
- min_size.h,
|
- min_size.h,
|
||||||
)),
|
)),
|
||||||
|
|
@ -385,9 +391,11 @@ impl Program for ContextMenu {
|
||||||
_ => Length::Fill,
|
_ => Length::Fill,
|
||||||
};
|
};
|
||||||
|
|
||||||
Column::with_children(self.items.iter().enumerate().map(|(idx, item)| {
|
MenuColumn::with_children(self.items.iter().enumerate().map(|(idx, item)| {
|
||||||
match 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![
|
Item::Submenu { title, .. } => Row::with_children(vec![
|
||||||
space::horizontal().width(16).into(),
|
space::horizontal().width(16).into(),
|
||||||
text::body(title).width(mode).into(),
|
text::body(title).width(mode).into(),
|
||||||
|
|
@ -477,6 +485,7 @@ impl Program for ContextMenu {
|
||||||
.class(theme::Container::custom(|theme| {
|
.class(theme::Container::custom(|theme| {
|
||||||
let cosmic = theme.cosmic();
|
let cosmic = theme.cosmic();
|
||||||
let component = &cosmic.background(false).component;
|
let component = &cosmic.background(false).component;
|
||||||
|
let component = &cosmic.background(false).component;
|
||||||
iced_widget::container::Style {
|
iced_widget::container::Style {
|
||||||
snap: true,
|
snap: true,
|
||||||
icon_color: Some(cosmic.accent.base.into()),
|
icon_color: Some(cosmic.accent.base.into()),
|
||||||
|
|
@ -540,7 +549,7 @@ impl PointerGrab<State> for MenuGrab {
|
||||||
let mut bbox = elem.iced.bbox();
|
let mut bbox = elem.iced.bbox();
|
||||||
bbox.loc = elem.position.as_logical();
|
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];
|
let element = &mut elements[i];
|
||||||
|
|
||||||
|
|
@ -556,8 +565,11 @@ impl PointerGrab<State> for MenuGrab {
|
||||||
PointerTarget::motion(&element.iced, &self.seat, state, &new_event);
|
PointerTarget::motion(&element.iced, &self.seat, state, &new_event);
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
elements.iter_mut().for_each(|element| {
|
elements
|
||||||
if element.pointer_entered {
|
.iter_mut()
|
||||||
|
.filter(|element| element.pointer_entered)
|
||||||
|
.skip(1)
|
||||||
|
.for_each(|element| {
|
||||||
PointerTarget::leave(
|
PointerTarget::leave(
|
||||||
&element.iced,
|
&element.iced,
|
||||||
&self.seat,
|
&self.seat,
|
||||||
|
|
@ -566,8 +578,7 @@ impl PointerGrab<State> for MenuGrab {
|
||||||
event.time,
|
event.time,
|
||||||
);
|
);
|
||||||
element.pointer_entered = false;
|
element.pointer_entered = false;
|
||||||
}
|
})
|
||||||
})
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
handle.motion(state, None, event);
|
handle.motion(state, None, event);
|
||||||
|
|
@ -743,7 +754,7 @@ impl TouchGrab<State> for MenuGrab {
|
||||||
let mut bbox = elem.iced.bbox();
|
let mut bbox = elem.iced.bbox();
|
||||||
bbox.loc = elem.position.as_logical();
|
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];
|
let element = &mut elements[i];
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -6,10 +6,7 @@ use crate::{
|
||||||
},
|
},
|
||||||
shell::{
|
shell::{
|
||||||
CosmicMapped, CosmicSurface, Direction, ManagedLayer,
|
CosmicMapped, CosmicSurface, Direction, ManagedLayer,
|
||||||
element::{
|
element::{CosmicMappedRenderElement, stack_hover::StackHover},
|
||||||
CosmicMappedRenderElement,
|
|
||||||
stack_hover::{StackHover, stack_hover},
|
|
||||||
},
|
|
||||||
focus::target::{KeyboardFocusTarget, PointerFocusTarget},
|
focus::target::{KeyboardFocusTarget, PointerFocusTarget},
|
||||||
layout::floating::TiledCorners,
|
layout::floating::TiledCorners,
|
||||||
},
|
},
|
||||||
|
|
@ -19,12 +16,14 @@ use crate::{
|
||||||
|
|
||||||
use calloop::LoopHandle;
|
use calloop::LoopHandle;
|
||||||
use cosmic::theme::CosmicTheme;
|
use cosmic::theme::CosmicTheme;
|
||||||
|
use smallvec::SmallVec;
|
||||||
use smithay::{
|
use smithay::{
|
||||||
backend::{
|
backend::{
|
||||||
|
drm::DrmNode,
|
||||||
input::ButtonState,
|
input::ButtonState,
|
||||||
renderer::{
|
renderer::{
|
||||||
ImportAll, ImportMem, Renderer,
|
ImportAll, ImportMem,
|
||||||
element::{AsRenderElements, RenderElement, utils::RescaleRenderElement},
|
element::{RenderElement, utils::RescaleRenderElement},
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
desktop::{WindowSurfaceType, layer_map_for_output, space::SpaceElement},
|
desktop::{WindowSurfaceType, layer_map_for_output, space::SpaceElement},
|
||||||
|
|
@ -68,12 +67,17 @@ pub struct MoveGrabState {
|
||||||
|
|
||||||
impl MoveGrabState {
|
impl MoveGrabState {
|
||||||
#[profiling::function]
|
#[profiling::function]
|
||||||
pub fn render<I, R>(&self, renderer: &mut R, output: &Output, theme: &CosmicTheme) -> Vec<I>
|
pub fn render<R>(
|
||||||
where
|
&self,
|
||||||
R: Renderer + ImportAll + ImportMem + AsGlowRenderer,
|
renderer: &mut R,
|
||||||
|
output: &Output,
|
||||||
|
theme: &CosmicTheme,
|
||||||
|
scanout_node: Option<DrmNode>,
|
||||||
|
push: &mut dyn FnMut(CosmicMappedRenderElement<R>),
|
||||||
|
) where
|
||||||
|
R: AsGlowRenderer + ImportAll + ImportMem,
|
||||||
R::TextureId: Send + Clone + 'static,
|
R::TextureId: Send + Clone + 'static,
|
||||||
CosmicMappedRenderElement<R>: RenderElement<R>,
|
CosmicMappedRenderElement<R>: RenderElement<R>,
|
||||||
I: From<CosmicMappedRenderElement<R>>,
|
|
||||||
{
|
{
|
||||||
let scale = if self.previous == ManagedLayer::Tiling {
|
let scale = if self.previous == ManagedLayer::Tiling {
|
||||||
0.6 + ((1.0
|
0.6 + ((1.0
|
||||||
|
|
@ -98,7 +102,7 @@ impl MoveGrabState {
|
||||||
.intersection(window_geo)
|
.intersection(window_geo)
|
||||||
.is_none()
|
.is_none()
|
||||||
{
|
{
|
||||||
return Vec::new();
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
let output_scale: Scale<f64> = output.current_scale().fractional_scale().into();
|
let output_scale: Scale<f64> = output.current_scale().fractional_scale().into();
|
||||||
|
|
@ -108,13 +112,33 @@ impl MoveGrabState {
|
||||||
+ self.window_offset
|
+ self.window_offset
|
||||||
- scaling_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()),
|
||||||
|
None,
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
self.window.push_popup_render_elements::<R>(
|
||||||
|
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 active_window_hint = crate::theme::active_window_hint(theme);
|
||||||
let radius = self
|
let radius = self
|
||||||
.element()
|
.element()
|
||||||
.corner_radius(window_geo.size, self.indicator_thickness);
|
.corner_radius(window_geo.size, self.indicator_thickness);
|
||||||
|
|
||||||
let focus_element = if self.indicator_thickness > 0 {
|
if self.indicator_thickness > 0 {
|
||||||
Some(CosmicMappedRenderElement::from(
|
push(
|
||||||
IndicatorShader::focus_element(
|
IndicatorShader::focus_element(
|
||||||
renderer,
|
renderer,
|
||||||
Key::Window(Usage::MoveGrabIndicator, self.window.key()),
|
Key::Window(Usage::MoveGrabIndicator, self.window.key()),
|
||||||
|
|
@ -137,12 +161,57 @@ impl MoveGrabState {
|
||||||
active_window_hint.green,
|
active_window_hint.green,
|
||||||
active_window_hint.blue,
|
active_window_hint.blue,
|
||||||
],
|
],
|
||||||
),
|
)
|
||||||
))
|
.into(),
|
||||||
} else {
|
)
|
||||||
None
|
}
|
||||||
|
|
||||||
|
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<R>; 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 non_exclusive_geometry = {
|
||||||
let layers = layer_map_for_output(output);
|
let layers = layer_map_for_output(output);
|
||||||
layers.non_exclusive_zone()
|
layers.non_exclusive_zone()
|
||||||
|
|
@ -151,117 +220,46 @@ impl MoveGrabState {
|
||||||
let gaps = (theme.gaps.0 as i32, theme.gaps.1 as i32);
|
let gaps = (theme.gaps.0 as i32, theme.gaps.1 as i32);
|
||||||
let thickness = self.indicator_thickness.max(1);
|
let thickness = self.indicator_thickness.max(1);
|
||||||
|
|
||||||
let snapping_indicator = match &self.snapping_zone {
|
if let Some(t) = &self.snapping_zone
|
||||||
Some(t) if &self.cursor_output == output => {
|
&& &self.cursor_output == output
|
||||||
let base_color = theme.palette.neutral_9;
|
{
|
||||||
let overlay_geometry = t.overlay_geometry(non_exclusive_geometry, gaps);
|
let base_color = theme.palette.neutral_9;
|
||||||
vec![
|
let overlay_geometry = t.overlay_geometry(non_exclusive_geometry, gaps);
|
||||||
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![],
|
|
||||||
};
|
|
||||||
|
|
||||||
let w_elements = self
|
push(
|
||||||
.window
|
IndicatorShader::element(
|
||||||
.render_elements::<R, CosmicMappedRenderElement<R>>(
|
|
||||||
renderer,
|
|
||||||
(render_location - self.window.geometry().loc)
|
|
||||||
.to_physical_precise_round(output_scale),
|
|
||||||
None,
|
|
||||||
output_scale,
|
|
||||||
alpha,
|
|
||||||
Some(false),
|
|
||||||
);
|
|
||||||
let p_elements = self
|
|
||||||
.window
|
|
||||||
.popup_render_elements::<R, CosmicMappedRenderElement<R>>(
|
|
||||||
renderer,
|
|
||||||
(render_location - self.window.geometry().loc)
|
|
||||||
.to_physical_precise_round(output_scale),
|
|
||||||
output_scale,
|
|
||||||
alpha,
|
|
||||||
);
|
|
||||||
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(
|
|
||||||
renderer,
|
renderer,
|
||||||
location.to_physical_precise_round(output_scale),
|
Key::Window(Usage::SnappingIndicator, self.window.key()),
|
||||||
output_scale,
|
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,
|
1.0,
|
||||||
|
output_scale.x,
|
||||||
|
[
|
||||||
|
active_window_hint.red,
|
||||||
|
active_window_hint.green,
|
||||||
|
active_window_hint.blue,
|
||||||
|
],
|
||||||
)
|
)
|
||||||
})
|
.into(),
|
||||||
.chain(p_elements)
|
);
|
||||||
.chain(focus_element)
|
push(
|
||||||
.chain(
|
BackdropShader::element(
|
||||||
w_elements
|
renderer,
|
||||||
.into_iter()
|
Key::Window(Usage::SnappingIndicator, self.window.key()),
|
||||||
.chain(shadow_element)
|
t.overlay_geometry(non_exclusive_geometry, gaps),
|
||||||
.map(|elem| match elem {
|
theme.radius_s()[0], // TODO: Fix once shaders support 4 corner radii customization
|
||||||
CosmicMappedRenderElement::Stack(stack) => {
|
0.4,
|
||||||
CosmicMappedRenderElement::GrabbedStack(
|
[base_color.red, base_color.green, base_color.blue],
|
||||||
RescaleRenderElement::from_element(
|
)
|
||||||
stack,
|
.into(),
|
||||||
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,
|
|
||||||
}),
|
|
||||||
)
|
)
|
||||||
.chain(snapping_indicator)
|
}
|
||||||
.map(I::from)
|
|
||||||
.collect()
|
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn element(&self) -> CosmicMapped {
|
pub fn element(&self) -> CosmicMapped {
|
||||||
|
|
@ -441,7 +439,7 @@ impl MoveGrab {
|
||||||
if let Some(indicator) =
|
if let Some(indicator) =
|
||||||
grab_state.stacking_indicator.as_ref().map(|x| &x.0)
|
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) {
|
} else if self.window_outputs.remove(output) {
|
||||||
|
|
@ -455,16 +453,14 @@ impl MoveGrab {
|
||||||
let indicator_location = shell.stacking_indicator(¤t_output, self.previous);
|
let indicator_location = shell.stacking_indicator(¤t_output, self.previous);
|
||||||
if indicator_location.is_some() != grab_state.stacking_indicator.is_some() {
|
if indicator_location.is_some() != grab_state.stacking_indicator.is_some() {
|
||||||
grab_state.stacking_indicator = indicator_location.map(|geo| {
|
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(),
|
state.common.event_loop_handle.clone(),
|
||||||
geo.size.as_logical(),
|
size,
|
||||||
state.common.theme.clone(),
|
state.common.theme.clone(),
|
||||||
);
|
);
|
||||||
for output in &self.window_outputs {
|
for output in &self.window_outputs {
|
||||||
element.output_enter(
|
element.output_enter(output);
|
||||||
output,
|
|
||||||
Rectangle::from_size(output.geometry().size.as_logical()),
|
|
||||||
);
|
|
||||||
}
|
}
|
||||||
(element, geo.loc.as_logical())
|
(element, geo.loc.as_logical())
|
||||||
});
|
});
|
||||||
|
|
|
||||||
|
|
@ -9,10 +9,14 @@ use std::{
|
||||||
use cosmic_comp_config::AppearanceConfig;
|
use cosmic_comp_config::AppearanceConfig;
|
||||||
use cosmic_settings_config::shortcuts::action::ResizeDirection;
|
use cosmic_settings_config::shortcuts::action::ResizeDirection;
|
||||||
use keyframe::{ease, functions::EaseInOutCubic};
|
use keyframe::{ease, functions::EaseInOutCubic};
|
||||||
|
use smallvec::SmallVec;
|
||||||
use smithay::{
|
use smithay::{
|
||||||
backend::renderer::element::{
|
backend::{
|
||||||
AsRenderElements, RenderElement,
|
drm::DrmNode,
|
||||||
utils::{Relocate, RelocateRenderElement, RescaleRenderElement},
|
renderer::element::{
|
||||||
|
RenderElement,
|
||||||
|
utils::{Relocate, RelocateRenderElement, RescaleRenderElement},
|
||||||
|
},
|
||||||
},
|
},
|
||||||
desktop::{PopupKind, Space, WindowSurfaceType, layer_map_for_output, space::SpaceElement},
|
desktop::{PopupKind, Space, WindowSurfaceType, layer_map_for_output, space::SpaceElement},
|
||||||
input::Seat,
|
input::Seat,
|
||||||
|
|
@ -1346,7 +1350,7 @@ impl FloatingLayout {
|
||||||
mapped.set_bounds(geometry.size.as_logical());
|
mapped.set_bounds(geometry.size.as_logical());
|
||||||
let prev = self.space.element_geometry(&mapped).map(RectExt::as_local);
|
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
|
geometry
|
||||||
} else {
|
} else {
|
||||||
prev.map(|mut rect| {
|
prev.map(|mut rect| {
|
||||||
|
|
@ -1439,8 +1443,9 @@ impl FloatingLayout {
|
||||||
&self,
|
&self,
|
||||||
renderer: &mut R,
|
renderer: &mut R,
|
||||||
alpha: f32,
|
alpha: f32,
|
||||||
) -> Vec<CosmicMappedRenderElement<R>>
|
scanout_node: Option<DrmNode>,
|
||||||
where
|
push: &mut dyn FnMut(CosmicMappedRenderElement<R>),
|
||||||
|
) where
|
||||||
R: AsGlowRenderer,
|
R: AsGlowRenderer,
|
||||||
R::TextureId: Send + Clone + 'static,
|
R::TextureId: Send + Clone + 'static,
|
||||||
CosmicMappedRenderElement<R>: RenderElement<R>,
|
CosmicMappedRenderElement<R>: RenderElement<R>,
|
||||||
|
|
@ -1450,8 +1455,6 @@ impl FloatingLayout {
|
||||||
let output = self.space.outputs().next().unwrap();
|
let output = self.space.outputs().next().unwrap();
|
||||||
let output_scale = output.current_scale().fractional_scale();
|
let output_scale = output.current_scale().fractional_scale();
|
||||||
|
|
||||||
let mut elements = Vec::default();
|
|
||||||
|
|
||||||
for elem in self
|
for elem in self
|
||||||
.animations
|
.animations
|
||||||
.iter()
|
.iter()
|
||||||
|
|
@ -1466,19 +1469,17 @@ impl FloatingLayout {
|
||||||
.unwrap_or_else(|| (self.space.element_geometry(elem).unwrap().as_local(), alpha));
|
.unwrap_or_else(|| (self.space.element_geometry(elem).unwrap().as_local(), alpha));
|
||||||
|
|
||||||
let render_location = geometry.loc - elem.geometry().loc.as_local();
|
let render_location = geometry.loc - elem.geometry().loc.as_local();
|
||||||
elements.extend(
|
elem.push_popup_render_elements(
|
||||||
elem.popup_render_elements(
|
renderer,
|
||||||
renderer,
|
render_location
|
||||||
render_location
|
.as_logical()
|
||||||
.as_logical()
|
.to_physical_precise_round(output_scale),
|
||||||
.to_physical_precise_round(output_scale),
|
output_scale.into(),
|
||||||
output_scale.into(),
|
alpha,
|
||||||
alpha,
|
scanout_node,
|
||||||
),
|
push,
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
elements
|
|
||||||
}
|
}
|
||||||
|
|
||||||
#[profiling::function]
|
#[profiling::function]
|
||||||
|
|
@ -1490,8 +1491,9 @@ impl FloatingLayout {
|
||||||
indicator_thickness: u8,
|
indicator_thickness: u8,
|
||||||
alpha: f32,
|
alpha: f32,
|
||||||
theme: &cosmic::theme::CosmicTheme,
|
theme: &cosmic::theme::CosmicTheme,
|
||||||
) -> Vec<CosmicMappedRenderElement<R>>
|
scanout_node: Option<DrmNode>,
|
||||||
where
|
push: &mut dyn FnMut(CosmicMappedRenderElement<R>),
|
||||||
|
) where
|
||||||
R: AsGlowRenderer,
|
R: AsGlowRenderer,
|
||||||
R::TextureId: Send + Clone + 'static,
|
R::TextureId: Send + Clone + 'static,
|
||||||
CosmicMappedRenderElement<R>: RenderElement<R>,
|
CosmicMappedRenderElement<R>: RenderElement<R>,
|
||||||
|
|
@ -1504,8 +1506,7 @@ impl FloatingLayout {
|
||||||
layers.non_exclusive_zone()
|
layers.non_exclusive_zone()
|
||||||
};
|
};
|
||||||
let output_scale = output.current_scale().fractional_scale();
|
let output_scale = output.current_scale().fractional_scale();
|
||||||
|
let mut lower_elements = SmallVec::<[_; 4]>::new_const();
|
||||||
let mut elements = Vec::default();
|
|
||||||
|
|
||||||
for elem in self
|
for elem in self
|
||||||
.animations
|
.animations
|
||||||
|
|
@ -1519,32 +1520,9 @@ impl FloatingLayout {
|
||||||
.get(elem)
|
.get(elem)
|
||||||
.map(|anim| (*anim.previous_geometry(), alpha * anim.alpha()))
|
.map(|anim| (*anim.previous_geometry(), alpha * anim.alpha()))
|
||||||
.unwrap_or_else(|| (self.space.element_geometry(elem).unwrap().as_local(), alpha));
|
.unwrap_or_else(|| (self.space.element_geometry(elem).unwrap().as_local(), alpha));
|
||||||
|
|
||||||
let render_location = geometry.loc - elem.geometry().loc.as_local();
|
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,
|
|
||||||
);
|
|
||||||
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 maybe_map = if let Some(anim) = self.animations.get(elem) {
|
||||||
let original_geo = anim.previous_geometry();
|
let original_geo = anim.previous_geometry();
|
||||||
geometry = anim.geometry(
|
geometry = anim.geometry(
|
||||||
output_geometry,
|
output_geometry,
|
||||||
|
|
@ -1562,80 +1540,77 @@ impl FloatingLayout {
|
||||||
y: geometry.size.h as f64 / buffer_size.h as f64,
|
y: geometry.size.h as f64 / buffer_size.h as f64,
|
||||||
};
|
};
|
||||||
|
|
||||||
window_elements = window_elements
|
Some(move |element| match element {
|
||||||
.into_iter()
|
CosmicMappedRenderElement::Stack(elem) => {
|
||||||
.map(|element| match element {
|
CosmicMappedRenderElement::MovingStack({
|
||||||
CosmicMappedRenderElement::Stack(elem) => {
|
let rescaled = RescaleRenderElement::from_element(
|
||||||
CosmicMappedRenderElement::MovingStack({
|
elem,
|
||||||
let rescaled = RescaleRenderElement::from_element(
|
original_geo
|
||||||
elem,
|
.loc
|
||||||
original_geo
|
.as_logical()
|
||||||
.loc
|
.to_physical_precise_round(output_scale),
|
||||||
.as_logical()
|
scale,
|
||||||
.to_physical_precise_round(output_scale),
|
);
|
||||||
scale,
|
|
||||||
);
|
|
||||||
|
|
||||||
RelocateRenderElement::from_element(
|
RelocateRenderElement::from_element(
|
||||||
rescaled,
|
rescaled,
|
||||||
(geometry.loc - original_geo.loc)
|
(geometry.loc - original_geo.loc)
|
||||||
.as_logical()
|
.as_logical()
|
||||||
.to_physical_precise_round(output_scale),
|
.to_physical_precise_round(output_scale),
|
||||||
Relocate::Relative,
|
Relocate::Relative,
|
||||||
)
|
)
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
CosmicMappedRenderElement::Window(elem) => {
|
CosmicMappedRenderElement::Window(elem) => {
|
||||||
CosmicMappedRenderElement::MovingWindow({
|
CosmicMappedRenderElement::MovingWindow({
|
||||||
let rescaled = RescaleRenderElement::from_element(
|
let rescaled = RescaleRenderElement::from_element(
|
||||||
elem,
|
elem,
|
||||||
original_geo
|
original_geo
|
||||||
.loc
|
.loc
|
||||||
.as_logical()
|
.as_logical()
|
||||||
.to_physical_precise_round(output_scale),
|
.to_physical_precise_round(output_scale),
|
||||||
scale,
|
scale,
|
||||||
);
|
);
|
||||||
|
|
||||||
RelocateRenderElement::from_element(
|
RelocateRenderElement::from_element(
|
||||||
rescaled,
|
rescaled,
|
||||||
(geometry.loc - original_geo.loc)
|
(geometry.loc - original_geo.loc)
|
||||||
.as_logical()
|
.as_logical()
|
||||||
.to_physical_precise_round(output_scale),
|
.to_physical_precise_round(output_scale),
|
||||||
Relocate::Relative,
|
Relocate::Relative,
|
||||||
)
|
)
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
x => x,
|
x => x,
|
||||||
})
|
})
|
||||||
.collect();
|
} else {
|
||||||
}
|
None
|
||||||
|
};
|
||||||
|
|
||||||
if focused == Some(elem) && !elem.is_maximized(false) {
|
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() {
|
if let Some((mode, resize)) = resize_indicator.as_mut() {
|
||||||
let mut resize_geometry = geometry;
|
let mut resize_geometry = geometry;
|
||||||
resize_geometry.loc -= (18, 18).into();
|
resize_geometry.loc -= (18, 18).into();
|
||||||
resize_geometry.size += (36, 36).into();
|
resize_geometry.size += (36, 36).into();
|
||||||
|
|
||||||
resize.resize(resize_geometry.size.as_logical());
|
resize.resize(resize_geometry.size.as_logical());
|
||||||
resize.output_enter(output, Rectangle::default() /* unused */);
|
resize.output_enter(output);
|
||||||
window_elements = resize
|
resize.push_render_elements(
|
||||||
.render_elements::<CosmicWindowRenderElement<R>>(
|
renderer,
|
||||||
renderer,
|
resize_geometry
|
||||||
resize_geometry
|
.loc
|
||||||
.loc
|
.as_logical()
|
||||||
.as_logical()
|
.to_physical_precise_round(output_scale),
|
||||||
.to_physical_precise_round(output_scale),
|
output_scale.into(),
|
||||||
output_scale.into(),
|
alpha * mode.alpha().unwrap_or(1.0),
|
||||||
alpha * mode.alpha().unwrap_or(1.0),
|
&mut |elem| push(CosmicMappedRenderElement::Window(elem.into())),
|
||||||
)
|
None,
|
||||||
.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 {
|
if indicator_thickness > 0 {
|
||||||
let element = IndicatorShader::focus_element(
|
let element = IndicatorShader::focus_element(
|
||||||
renderer,
|
renderer,
|
||||||
|
|
@ -1651,14 +1626,47 @@ impl FloatingLayout {
|
||||||
active_window_hint.blue,
|
active_window_hint.blue,
|
||||||
],
|
],
|
||||||
);
|
);
|
||||||
window_elements.insert(0, element.into());
|
push(element.into());
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
elements.extend(window_elements);
|
let map_anim = |elem| {
|
||||||
}
|
if let Some(map) = maybe_map {
|
||||||
|
map(elem)
|
||||||
|
} else {
|
||||||
|
elem
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
elements
|
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) {
|
pub fn snap_to_corner(&self, mapped: &CosmicMapped, corners: &TiledCorners) {
|
||||||
|
|
|
||||||
File diff suppressed because it is too large
Load diff
204
src/shell/mod.rs
204
src/shell/mod.rs
|
|
@ -12,7 +12,11 @@ use std::{
|
||||||
use wayland_backend::server::ClientId;
|
use wayland_backend::server::ClientId;
|
||||||
|
|
||||||
use crate::{
|
use crate::{
|
||||||
shell::{focus::FocusTarget, grabs::fullscreen_items, layout::tiling::PlaceholderType},
|
shell::{
|
||||||
|
element::CosmicStack, focus::FocusTarget, grabs::fullscreen_items,
|
||||||
|
layout::tiling::PlaceholderType,
|
||||||
|
},
|
||||||
|
utils,
|
||||||
wayland::{
|
wayland::{
|
||||||
handlers::data_device::{self, get_dnd_icon},
|
handlers::data_device::{self, get_dnd_icon},
|
||||||
protocols::workspace::{State as WState, WorkspaceCapabilities},
|
protocols::workspace::{State as WState, WorkspaceCapabilities},
|
||||||
|
|
@ -99,9 +103,8 @@ use self::zoom::{OutputZoomState, ZoomState};
|
||||||
|
|
||||||
use self::{
|
use self::{
|
||||||
element::{
|
element::{
|
||||||
CosmicWindow, MaximizedState,
|
CosmicWindow, MaximizedState, resize_indicator::ResizeIndicator,
|
||||||
resize_indicator::{ResizeIndicator, resize_indicator},
|
swap_indicator::SwapIndicator,
|
||||||
swap_indicator::{SwapIndicator, swap_indicator},
|
|
||||||
},
|
},
|
||||||
focus::target::{KeyboardFocusTarget, PointerFocusTarget},
|
focus::target::{KeyboardFocusTarget, PointerFocusTarget},
|
||||||
grabs::{
|
grabs::{
|
||||||
|
|
@ -1508,6 +1511,21 @@ impl Workspaces {
|
||||||
#[derive(Debug)]
|
#[derive(Debug)]
|
||||||
pub struct InvalidWorkspaceIndex;
|
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 {
|
impl Common {
|
||||||
pub fn add_output(&mut self, output: &Output) {
|
pub fn add_output(&mut self, output: &Output) {
|
||||||
let mut shell = self.shell.write();
|
let mut shell = self.shell.write();
|
||||||
|
|
@ -1515,6 +1533,10 @@ impl Common {
|
||||||
.workspaces
|
.workspaces
|
||||||
.add_output(output, &mut self.workspace_state.update());
|
.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() {
|
if let Some(state) = shell.zoom_state.as_ref() {
|
||||||
output.user_data().insert_if_missing_threadsafe(|| {
|
output.user_data().insert_if_missing_threadsafe(|| {
|
||||||
Mutex::new(OutputZoomState::new(
|
Mutex::new(OutputZoomState::new(
|
||||||
|
|
@ -2288,7 +2310,7 @@ impl Shell {
|
||||||
OverviewMode::Started(_, _) | OverviewMode::Active(_)
|
OverviewMode::Started(_, _) | OverviewMode::Active(_)
|
||||||
) {
|
) {
|
||||||
if matches!(trigger, Trigger::KeyboardSwap(_, _)) {
|
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());
|
self.overview_mode = OverviewMode::Started(trigger, Instant::now());
|
||||||
}
|
}
|
||||||
|
|
@ -2341,7 +2363,7 @@ impl Shell {
|
||||||
} else {
|
} else {
|
||||||
self.resize_mode = ResizeMode::Started(pattern, Instant::now(), direction);
|
self.resize_mode = ResizeMode::Started(pattern, Instant::now(), direction);
|
||||||
}
|
}
|
||||||
self.resize_indicator = Some(resize_indicator(
|
self.resize_indicator = Some(ResizeIndicator::new(
|
||||||
direction,
|
direction,
|
||||||
config,
|
config,
|
||||||
evlh,
|
evlh,
|
||||||
|
|
@ -2468,6 +2490,12 @@ impl Shell {
|
||||||
increment: zoom_config.increment,
|
increment: zoom_config.increment,
|
||||||
movement: zoom_config.view_moves,
|
movement: 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(
|
pub fn update_focal_point(
|
||||||
|
|
@ -2592,15 +2620,36 @@ impl Shell {
|
||||||
pub fn remap_unfullscreened_window(
|
pub fn remap_unfullscreened_window(
|
||||||
&mut self,
|
&mut self,
|
||||||
surface: CosmicSurface,
|
surface: CosmicSurface,
|
||||||
state: Option<FullscreenRestoreState>,
|
mut state: Option<FullscreenRestoreState>,
|
||||||
loop_handle: &LoopHandle<'static, State>,
|
loop_handle: &LoopHandle<'static, State>,
|
||||||
) -> CosmicMapped {
|
) -> CosmicMapped {
|
||||||
let window = CosmicMapped::from(CosmicWindow::new(
|
if let Some(FullscreenRestoreState::Stack { state: stack_state }) = &state {
|
||||||
surface,
|
if let Some(mapped) = self.mapped().find(|m| **m == stack_state.stack)
|
||||||
loop_handle.clone(),
|
&& let Some(stack) = mapped.stack_ref()
|
||||||
self.theme.clone(),
|
{
|
||||||
self.appearance_conf,
|
let idx = stack_state.idx.min(stack.len());
|
||||||
));
|
stack.add_window(surface, Some(idx), None);
|
||||||
|
return mapped.clone();
|
||||||
|
} else {
|
||||||
|
state = None;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
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 {
|
if let Some(FullscreenRestoreState::Sticky { output, state, .. }) = &state {
|
||||||
let output = output
|
let output = output
|
||||||
|
|
@ -2637,7 +2686,9 @@ impl Shell {
|
||||||
workspace
|
workspace
|
||||||
}
|
}
|
||||||
None => self.workspaces.active_mut(&seat.active_output()).unwrap(),
|
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);
|
let fullscreen_geometry = workspace.output.geometry().to_local(&workspace.output);
|
||||||
|
|
||||||
|
|
@ -2752,7 +2803,9 @@ impl Shell {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
Some(FullscreenRestoreState::Sticky { .. }) => unreachable!(),
|
Some(FullscreenRestoreState::Sticky { .. } | FullscreenRestoreState::Stack { .. }) => {
|
||||||
|
unreachable!()
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
window
|
window
|
||||||
|
|
@ -2856,10 +2909,12 @@ impl Shell {
|
||||||
|
|
||||||
let maybe_focused = workspace.focus_stack.get(&seat).iter().next().cloned();
|
let maybe_focused = workspace.focus_stack.get(&seat).iter().next().cloned();
|
||||||
if let Some(FocusTarget::Window(focused)) = maybe_focused
|
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)
|
&& !(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 {
|
if was_activated {
|
||||||
workspace_state.add_workspace_state(&workspace_handle, WState::Urgent);
|
workspace_state.add_workspace_state(&workspace_handle, WState::Urgent);
|
||||||
}
|
}
|
||||||
|
|
@ -2985,8 +3040,8 @@ impl Shell {
|
||||||
.map(|idx| (idx, m.clone()))
|
.map(|idx| (idx, m.clone()))
|
||||||
});
|
});
|
||||||
let surface = if let Some((idx, mapped)) = sticky_res {
|
let surface = if let Some((idx, mapped)) = sticky_res {
|
||||||
if mapped.is_stack() {
|
if let Some(stack) = mapped.stack_ref() {
|
||||||
mapped.stack_ref().unwrap().remove_idx(idx)
|
stack.remove_idx(idx)
|
||||||
} else {
|
} else {
|
||||||
set.sticky_layer.unmap(&mapped, None);
|
set.sticky_layer.unmap(&mapped, None);
|
||||||
Some(mapped.active_window())
|
Some(mapped.active_window())
|
||||||
|
|
@ -2996,20 +3051,13 @@ impl Shell {
|
||||||
.iter()
|
.iter()
|
||||||
.position(|w| w.windows().any(|s| &s == surface))
|
.position(|w| w.windows().any(|s| &s == surface))
|
||||||
{
|
{
|
||||||
if set
|
if let Some(stack) = set
|
||||||
.minimized_windows
|
.minimized_windows
|
||||||
.get(idx)
|
.get_mut(idx)
|
||||||
.unwrap()
|
.unwrap()
|
||||||
.mapped()
|
.mapped_mut()
|
||||||
.is_some_and(CosmicMapped::is_stack)
|
.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);
|
let idx = stack.surfaces().position(|s| &s == surface);
|
||||||
idx.and_then(|idx| stack.remove_idx(idx))
|
idx.and_then(|idx| stack.remove_idx(idx))
|
||||||
} else {
|
} else {
|
||||||
|
|
@ -3240,7 +3288,7 @@ impl Shell {
|
||||||
toplevel_enter_workspace(window, to);
|
toplevel_enter_workspace(window, to);
|
||||||
|
|
||||||
// we can't restore to a given position
|
// 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();
|
state.state.take();
|
||||||
}
|
}
|
||||||
// update fullscreen state to restore to the new workspace
|
// update fullscreen state to restore to the new workspace
|
||||||
|
|
@ -3256,6 +3304,7 @@ impl Shell {
|
||||||
state: None,
|
state: None,
|
||||||
was_maximized: previous.was_maximized(),
|
was_maximized: previous.was_maximized(),
|
||||||
},
|
},
|
||||||
|
was_stack: previous.was_stack(),
|
||||||
};
|
};
|
||||||
} else if let FullscreenRestoreState::Tiling { workspace, .. }
|
} else if let FullscreenRestoreState::Tiling { workspace, .. }
|
||||||
| FullscreenRestoreState::Floating { workspace, .. } = previous
|
| FullscreenRestoreState::Floating { workspace, .. } = previous
|
||||||
|
|
@ -3267,7 +3316,7 @@ impl Shell {
|
||||||
if is_minimized {
|
if is_minimized {
|
||||||
let to_workspace = self.workspaces.space_for_handle_mut(to).unwrap(); // checked above
|
let to_workspace = self.workspaces.space_for_handle_mut(to).unwrap(); // checked above
|
||||||
let minimized_window = match window_state {
|
let minimized_window = match window_state {
|
||||||
WorkspaceRestoreData::Floating(Some(previous)) => {
|
WorkspaceRestoreData::Floating(previous) => {
|
||||||
let window = CosmicMapped::from(CosmicWindow::new(
|
let window = CosmicMapped::from(CosmicWindow::new(
|
||||||
window.clone(),
|
window.clone(),
|
||||||
evlh.clone(),
|
evlh.clone(),
|
||||||
|
|
@ -3277,7 +3326,7 @@ impl Shell {
|
||||||
window.set_minimized(true);
|
window.set_minimized(true);
|
||||||
MinimizedWindow::Floating { window, previous }
|
MinimizedWindow::Floating { window, previous }
|
||||||
}
|
}
|
||||||
WorkspaceRestoreData::Tiling(Some(previous)) => {
|
WorkspaceRestoreData::Tiling(previous) => {
|
||||||
let window = CosmicMapped::from(CosmicWindow::new(
|
let window = CosmicMapped::from(CosmicWindow::new(
|
||||||
window.clone(),
|
window.clone(),
|
||||||
evlh.clone(),
|
evlh.clone(),
|
||||||
|
|
@ -3344,7 +3393,7 @@ impl Shell {
|
||||||
self.appearance_conf,
|
self.appearance_conf,
|
||||||
));
|
));
|
||||||
let position = match window_state {
|
let position = match window_state {
|
||||||
WorkspaceRestoreData::Floating(Some(data)) => Some(
|
WorkspaceRestoreData::Floating(data) => Some(
|
||||||
data.position_relative(to_workspace.output.geometry().size.as_logical()),
|
data.position_relative(to_workspace.output.geometry().size.as_logical()),
|
||||||
),
|
),
|
||||||
_ => None,
|
_ => None,
|
||||||
|
|
@ -3444,7 +3493,7 @@ impl Shell {
|
||||||
let to_workspace = self.workspaces.space_for_handle_mut(to).unwrap(); // checked above
|
let to_workspace = self.workspaces.space_for_handle_mut(to).unwrap(); // checked above
|
||||||
if !to_workspace.tiling_enabled {
|
if !to_workspace.tiling_enabled {
|
||||||
let (position, was_maximized, was_snapped) = match &window_state {
|
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())),
|
Some(data.position_relative(to_workspace.output.geometry().size.as_logical())),
|
||||||
data.was_maximized,
|
data.was_maximized,
|
||||||
data.was_snapped,
|
data.was_snapped,
|
||||||
|
|
@ -3644,6 +3693,8 @@ impl Shell {
|
||||||
return None;
|
return None;
|
||||||
};
|
};
|
||||||
|
|
||||||
|
let mut theme = self.theme.clone();
|
||||||
|
theme.transparent = theme.cosmic().frosted_windows;
|
||||||
let grab = MenuGrab::new(
|
let grab = MenuGrab::new(
|
||||||
GrabStartData::Pointer(start_data),
|
GrabStartData::Pointer(start_data),
|
||||||
seat,
|
seat,
|
||||||
|
|
@ -3652,7 +3703,7 @@ impl Shell {
|
||||||
MenuAlignment::CORNER,
|
MenuAlignment::CORNER,
|
||||||
None,
|
None,
|
||||||
evlh.clone(),
|
evlh.clone(),
|
||||||
self.theme.clone(),
|
theme,
|
||||||
);
|
);
|
||||||
|
|
||||||
Some((grab, Focus::Keep))
|
Some((grab, Focus::Keep))
|
||||||
|
|
@ -3827,13 +3878,13 @@ impl Shell {
|
||||||
}
|
}
|
||||||
|
|
||||||
(initial_window_location, layer, workspace.handle)
|
(initial_window_location, layer, workspace.handle)
|
||||||
} else if let Some(sticky_layer) = self
|
} else {
|
||||||
.workspaces
|
let sticky_layer = self
|
||||||
.sets
|
.workspaces
|
||||||
.get_mut(&cursor_output)
|
.sets
|
||||||
.filter(|set| set.sticky_layer.mapped().any(|m| m == &old_mapped))
|
.get_mut(&cursor_output)
|
||||||
.map(|set| &mut set.sticky_layer)
|
.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 elem_geo = sticky_layer.element_geometry(&old_mapped).unwrap();
|
||||||
let mut initial_window_location = elem_geo.loc.to_global(&cursor_output);
|
let mut initial_window_location = elem_geo.loc.to_global(&cursor_output);
|
||||||
|
|
||||||
|
|
@ -3876,8 +3927,6 @@ impl Shell {
|
||||||
ManagedLayer::Sticky,
|
ManagedLayer::Sticky,
|
||||||
self.active_space(&cursor_output).unwrap().handle,
|
self.active_space(&cursor_output).unwrap().handle,
|
||||||
)
|
)
|
||||||
} else {
|
|
||||||
return None;
|
|
||||||
};
|
};
|
||||||
|
|
||||||
toplevel_leave_workspace(&window, &workspace_handle);
|
toplevel_leave_workspace(&window, &workspace_handle);
|
||||||
|
|
@ -4171,14 +4220,13 @@ impl Shell {
|
||||||
.unwrap()
|
.unwrap()
|
||||||
.to_global(&set.output);
|
.to_global(&set.output);
|
||||||
(&mut set.sticky_layer, geometry)
|
(&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
|
let geometry = workspace
|
||||||
.element_geometry(mapped)
|
.element_geometry(mapped)
|
||||||
.unwrap()
|
.unwrap()
|
||||||
.to_global(workspace.output());
|
.to_global(workspace.output());
|
||||||
(&mut workspace.floating_layer, geometry)
|
(&mut workspace.floating_layer, geometry)
|
||||||
} else {
|
|
||||||
return None;
|
|
||||||
};
|
};
|
||||||
|
|
||||||
let new_loc = if edge.contains(ResizeEdge::LEFT) {
|
let new_loc = if edge.contains(ResizeEdge::LEFT) {
|
||||||
|
|
@ -4215,7 +4263,8 @@ impl Shell {
|
||||||
ReleaseMode::Click,
|
ReleaseMode::Click,
|
||||||
) {
|
) {
|
||||||
grab.into()
|
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_id = mapped.tiling_node_id.lock().unwrap().clone()?;
|
||||||
let (node, left_up_idx, orientation) = ws.tiling_layer.resize_request(node_id, edge)?;
|
let (node, left_up_idx, orientation) = ws.tiling_layer.resize_request(node_id, edge)?;
|
||||||
ResizeForkGrab::new(
|
ResizeForkGrab::new(
|
||||||
|
|
@ -4228,8 +4277,6 @@ impl Shell {
|
||||||
ReleaseMode::Click,
|
ReleaseMode::Click,
|
||||||
)
|
)
|
||||||
.into()
|
.into()
|
||||||
} else {
|
|
||||||
return None;
|
|
||||||
};
|
};
|
||||||
|
|
||||||
Some(((focus, new_loc), (grab, Focus::Keep)))
|
Some(((focus, new_loc), (grab, Focus::Keep)))
|
||||||
|
|
@ -4466,10 +4513,9 @@ impl Shell {
|
||||||
.find(|set| set.sticky_layer.mapped().any(|m| m == &mapped))
|
.find(|set| set.sticky_layer.mapped().any(|m| m == &mapped))
|
||||||
{
|
{
|
||||||
&mut set.sticky_layer
|
&mut set.sticky_layer
|
||||||
} else if let Some(workspace) = self.space_for_mut(&mapped) {
|
|
||||||
&mut workspace.floating_layer
|
|
||||||
} else {
|
} 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(
|
let grab: ResizeGrab = if let Some(grab) = floating_layer.resize_request(
|
||||||
|
|
@ -4481,7 +4527,8 @@ impl Shell {
|
||||||
ReleaseMode::NoMouseButtons,
|
ReleaseMode::NoMouseButtons,
|
||||||
) {
|
) {
|
||||||
grab.into()
|
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_id = mapped.tiling_node_id.lock().unwrap().clone()?;
|
||||||
let (node, left_up_idx, orientation) =
|
let (node, left_up_idx, orientation) =
|
||||||
ws.tiling_layer.resize_request(node_id, edges)?;
|
ws.tiling_layer.resize_request(node_id, edges)?;
|
||||||
|
|
@ -4495,8 +4542,6 @@ impl Shell {
|
||||||
ReleaseMode::NoMouseButtons,
|
ReleaseMode::NoMouseButtons,
|
||||||
)
|
)
|
||||||
.into()
|
.into()
|
||||||
} else {
|
|
||||||
return None;
|
|
||||||
};
|
};
|
||||||
|
|
||||||
Some((grab, Focus::Clear))
|
Some((grab, Focus::Clear))
|
||||||
|
|
@ -4796,14 +4841,19 @@ impl Shell {
|
||||||
{
|
{
|
||||||
let mut from = set.sticky_layer.element_geometry(&mapped).unwrap();
|
let mut from = set.sticky_layer.element_geometry(&mapped).unwrap();
|
||||||
let mut was_maximized = false;
|
let mut was_maximized = false;
|
||||||
window = if mapped
|
let mut restore_state = None;
|
||||||
.stack_ref()
|
let was_stack = mapped.is_stack();
|
||||||
.map(|stack| stack.len() > 1)
|
window = if let Some(stack) = mapped.stack_ref()
|
||||||
.unwrap_or(false)
|
&& stack.len() > 1
|
||||||
{
|
{
|
||||||
let stack = mapped.stack_ref().unwrap();
|
let idx = stack.surfaces().position(|s| &s == surface)?;
|
||||||
let surface = stack.surfaces().find(|s| s == surface).unwrap();
|
let surface = stack.remove_idx(idx)?;
|
||||||
stack.remove_window(&surface);
|
restore_state = Some(FullscreenRestoreState::Stack {
|
||||||
|
state: StackRestoreData {
|
||||||
|
stack: mapped.key(),
|
||||||
|
idx,
|
||||||
|
},
|
||||||
|
});
|
||||||
surface
|
surface
|
||||||
} else {
|
} else {
|
||||||
// Must be set before `map_internal`/`unmap` below, as both may call
|
// Must be set before `map_internal`/`unmap` below, as both may call
|
||||||
|
|
@ -4836,7 +4886,7 @@ impl Shell {
|
||||||
workspace.map_fullscreen(
|
workspace.map_fullscreen(
|
||||||
&window,
|
&window,
|
||||||
&seat,
|
&seat,
|
||||||
Some(FullscreenRestoreState::Sticky {
|
Some(restore_state.unwrap_or(FullscreenRestoreState::Sticky {
|
||||||
output: old_output,
|
output: old_output,
|
||||||
state: FloatingRestoreData {
|
state: FloatingRestoreData {
|
||||||
geometry: from,
|
geometry: from,
|
||||||
|
|
@ -4844,10 +4894,12 @@ impl Shell {
|
||||||
was_maximized,
|
was_maximized,
|
||||||
was_snapped: None,
|
was_snapped: None,
|
||||||
},
|
},
|
||||||
}),
|
was_stack,
|
||||||
|
})),
|
||||||
Some(from),
|
Some(from),
|
||||||
);
|
);
|
||||||
} else if let Some(workspace) = self.space_for_mut(&mapped) {
|
} else {
|
||||||
|
let workspace = self.space_for_mut(&mapped)?;
|
||||||
if mapped.is_minimized() {
|
if mapped.is_minimized() {
|
||||||
// TODO: Rewrite the `MinimizedWindow` to restore to fullscreen
|
// TODO: Rewrite the `MinimizedWindow` to restore to fullscreen
|
||||||
return None;
|
return None;
|
||||||
|
|
@ -4875,23 +4927,26 @@ impl Shell {
|
||||||
&seat,
|
&seat,
|
||||||
match state {
|
match state {
|
||||||
WorkspaceRestoreData::Floating(floating_state) => {
|
WorkspaceRestoreData::Floating(floating_state) => {
|
||||||
floating_state.map(|state| FullscreenRestoreState::Floating {
|
Some(FullscreenRestoreState::Floating {
|
||||||
workspace: handle,
|
workspace: handle,
|
||||||
state,
|
state: floating_state,
|
||||||
|
was_stack: mapped.is_stack(),
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
WorkspaceRestoreData::Tiling(tiling_state) => {
|
WorkspaceRestoreData::Tiling(tiling_state) => {
|
||||||
tiling_state.map(|state| FullscreenRestoreState::Tiling {
|
Some(FullscreenRestoreState::Tiling {
|
||||||
workspace: handle,
|
workspace: handle,
|
||||||
state,
|
state: tiling_state,
|
||||||
|
was_stack: mapped.is_stack(),
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
WorkspaceRestoreData::Stack(stack_state) => {
|
||||||
|
Some(FullscreenRestoreState::Stack { state: stack_state })
|
||||||
|
}
|
||||||
WorkspaceRestoreData::Fullscreen(_) => unreachable!(),
|
WorkspaceRestoreData::Fullscreen(_) => unreachable!(),
|
||||||
},
|
},
|
||||||
Some(from),
|
Some(from),
|
||||||
);
|
);
|
||||||
} else {
|
|
||||||
return None;
|
|
||||||
};
|
};
|
||||||
|
|
||||||
Some(KeyboardFocusTarget::Fullscreen(window))
|
Some(KeyboardFocusTarget::Fullscreen(window))
|
||||||
|
|
@ -5003,13 +5058,14 @@ impl Shell {
|
||||||
|
|
||||||
let map = smithay::desktop::layer_map_for_output(output);
|
let map = smithay::desktop::layer_map_for_output(output);
|
||||||
for layer_surface in map.layers() {
|
for layer_surface in map.layers() {
|
||||||
|
let namespace = self.workspaces.active_num(output).1;
|
||||||
layer_surface.take_presentation_feedback(
|
layer_surface.take_presentation_feedback(
|
||||||
&mut output_presentation_feedback,
|
&mut output_presentation_feedback,
|
||||||
surface_primary_scanout_output,
|
surface_primary_scanout_output,
|
||||||
|surface, _| {
|
|surface, _| {
|
||||||
surface_presentation_feedback_flags_from_states(
|
surface_presentation_feedback_flags_from_states(
|
||||||
surface,
|
surface,
|
||||||
None,
|
Some(namespace),
|
||||||
render_element_states,
|
render_element_states,
|
||||||
)
|
)
|
||||||
},
|
},
|
||||||
|
|
|
||||||
|
|
@ -243,6 +243,12 @@ pub fn create_seat(
|
||||||
seat
|
seat
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[derive(Debug, Copy, Clone)]
|
||||||
|
pub struct CursorGeometry {
|
||||||
|
pub geometry: Rectangle<i32, Buffer>,
|
||||||
|
pub hotspot: Point<i32, Buffer>,
|
||||||
|
}
|
||||||
|
|
||||||
pub trait SeatExt {
|
pub trait SeatExt {
|
||||||
fn id(&self) -> usize;
|
fn id(&self) -> usize;
|
||||||
|
|
||||||
|
|
@ -266,7 +272,7 @@ pub trait SeatExt {
|
||||||
&self,
|
&self,
|
||||||
loc: impl Into<Point<f64, Buffer>>,
|
loc: impl Into<Point<f64, Buffer>>,
|
||||||
time: Time<Monotonic>,
|
time: Time<Monotonic>,
|
||||||
) -> Option<(Rectangle<i32, Buffer>, Point<i32, Buffer>)>;
|
) -> Option<CursorGeometry>;
|
||||||
fn cursor_image_status(&self) -> CursorImageStatus;
|
fn cursor_image_status(&self) -> CursorImageStatus;
|
||||||
fn set_cursor_image_status(&self, status: CursorImageStatus);
|
fn set_cursor_image_status(&self, status: CursorImageStatus);
|
||||||
}
|
}
|
||||||
|
|
@ -367,7 +373,7 @@ impl SeatExt for Seat<State> {
|
||||||
&self,
|
&self,
|
||||||
loc: impl Into<Point<f64, Buffer>>,
|
loc: impl Into<Point<f64, Buffer>>,
|
||||||
time: Time<Monotonic>,
|
time: Time<Monotonic>,
|
||||||
) -> Option<(Rectangle<i32, Buffer>, Point<i32, Buffer>)> {
|
) -> Option<CursorGeometry> {
|
||||||
let location = loc.into().to_i32_round();
|
let location = loc.into().to_i32_round();
|
||||||
|
|
||||||
match self.cursor_image_status() {
|
match self.cursor_image_status() {
|
||||||
|
|
@ -386,7 +392,10 @@ impl SeatExt for Seat<State> {
|
||||||
(geo.loc.x, geo.loc.y).into(),
|
(geo.loc.x, geo.loc.y).into(),
|
||||||
geo.size.to_buffer(1, Transform::Normal),
|
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) => {
|
CursorImageStatus::Named(cursor_icon) => {
|
||||||
let seat_userdata = self.user_data();
|
let seat_userdata = self.user_data();
|
||||||
|
|
@ -398,10 +407,13 @@ impl SeatExt for Seat<State> {
|
||||||
.get_named_cursor(cursor_icon)
|
.get_named_cursor(cursor_icon)
|
||||||
.get_image(1, time.as_millis());
|
.get_image(1, time.as_millis());
|
||||||
|
|
||||||
Some((
|
Some(CursorGeometry {
|
||||||
Rectangle::new(location, (frame.width as i32, frame.height as i32).into()),
|
geometry: Rectangle::new(
|
||||||
(frame.xhot as i32, frame.yhot as i32).into(),
|
location,
|
||||||
))
|
(frame.width as i32, frame.height as i32).into(),
|
||||||
|
),
|
||||||
|
hotspot: (frame.xhot as i32, frame.yhot as i32).into(),
|
||||||
|
})
|
||||||
}
|
}
|
||||||
CursorImageStatus::Hidden => None,
|
CursorImageStatus::Hidden => None,
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -1,11 +1,9 @@
|
||||||
|
use crate::backend::render::wayland::SurfaceRenderElement;
|
||||||
use crate::shell::focus::FocusTarget;
|
use crate::shell::focus::FocusTarget;
|
||||||
use crate::shell::layout::tiling::RestoreTilingState;
|
use crate::shell::layout::tiling::RestoreTilingState;
|
||||||
use crate::wayland::handlers::xdg_activation::ActivationContext;
|
use crate::wayland::handlers::xdg_activation::ActivationContext;
|
||||||
use crate::{
|
use crate::{
|
||||||
backend::render::{
|
backend::render::{BackdropShader, element::AsGlowRenderer},
|
||||||
BackdropShader,
|
|
||||||
element::{AsGlowRenderer, FromGlesError},
|
|
||||||
},
|
|
||||||
shell::{
|
shell::{
|
||||||
ANIMATION_DURATION, OverviewMode, SeatMoveGrabState,
|
ANIMATION_DURATION, OverviewMode, SeatMoveGrabState,
|
||||||
layout::{
|
layout::{
|
||||||
|
|
@ -31,14 +29,15 @@ use cosmic_protocols::workspace::v2::server::zcosmic_workspace_handle_v2::Tiling
|
||||||
use id_tree::Tree;
|
use id_tree::Tree;
|
||||||
use indexmap::IndexSet;
|
use indexmap::IndexSet;
|
||||||
use keyframe::{ease, functions::EaseInOutCubic};
|
use keyframe::{ease, functions::EaseInOutCubic};
|
||||||
use smithay::backend::renderer::element::Kind;
|
use smallvec::SmallVec;
|
||||||
|
use smithay::backend::drm::DrmNode;
|
||||||
|
use smithay::backend::renderer::element::{Kind, NamespacedElement};
|
||||||
use smithay::output::WeakOutput;
|
use smithay::output::WeakOutput;
|
||||||
use smithay::utils::user_data::UserDataMap;
|
use smithay::utils::user_data::UserDataMap;
|
||||||
use smithay::{
|
use smithay::{
|
||||||
backend::renderer::{
|
backend::renderer::{
|
||||||
element::{
|
element::{
|
||||||
Element, Id, RenderElement, surface::WaylandSurfaceRenderElement,
|
Element, Id, RenderElement, texture::TextureRenderElement, utils::RescaleRenderElement,
|
||||||
texture::TextureRenderElement, utils::RescaleRenderElement,
|
|
||||||
},
|
},
|
||||||
gles::GlesTexture,
|
gles::GlesTexture,
|
||||||
glow::GlowRenderer,
|
glow::GlowRenderer,
|
||||||
|
|
@ -61,7 +60,7 @@ use wayland_backend::server::ClientId;
|
||||||
use super::{
|
use super::{
|
||||||
CosmicMappedRenderElement, CosmicSurface, ResizeDirection, ResizeMode,
|
CosmicMappedRenderElement, CosmicSurface, ResizeDirection, ResizeMode,
|
||||||
element::{
|
element::{
|
||||||
CosmicMapped, MaximizedState, resize_indicator::ResizeIndicator,
|
CosmicMapped, CosmicMappedKey, MaximizedState, resize_indicator::ResizeIndicator,
|
||||||
stack::CosmicStackRenderElement, swap_indicator::SwapIndicator,
|
stack::CosmicStackRenderElement, swap_indicator::SwapIndicator,
|
||||||
window::CosmicWindowRenderElement,
|
window::CosmicWindowRenderElement,
|
||||||
},
|
},
|
||||||
|
|
@ -253,14 +252,20 @@ pub enum FullscreenRestoreState {
|
||||||
Tiling {
|
Tiling {
|
||||||
workspace: WorkspaceHandle,
|
workspace: WorkspaceHandle,
|
||||||
state: TilingRestoreData,
|
state: TilingRestoreData,
|
||||||
|
was_stack: bool,
|
||||||
},
|
},
|
||||||
Floating {
|
Floating {
|
||||||
workspace: WorkspaceHandle,
|
workspace: WorkspaceHandle,
|
||||||
state: FloatingRestoreData,
|
state: FloatingRestoreData,
|
||||||
|
was_stack: bool,
|
||||||
},
|
},
|
||||||
Sticky {
|
Sticky {
|
||||||
output: WeakOutput,
|
output: WeakOutput,
|
||||||
state: FloatingRestoreData,
|
state: FloatingRestoreData,
|
||||||
|
was_stack: bool,
|
||||||
|
},
|
||||||
|
Stack {
|
||||||
|
state: StackRestoreData,
|
||||||
},
|
},
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -270,6 +275,18 @@ impl FullscreenRestoreState {
|
||||||
FullscreenRestoreState::Floating { state, .. }
|
FullscreenRestoreState::Floating { state, .. }
|
||||||
| FullscreenRestoreState::Sticky { state, .. } => state.was_maximized,
|
| FullscreenRestoreState::Sticky { state, .. } => state.was_maximized,
|
||||||
FullscreenRestoreState::Tiling { state, .. } => state.was_maximized,
|
FullscreenRestoreState::Tiling { state, .. } => state.was_maximized,
|
||||||
|
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,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
@ -277,18 +294,9 @@ impl FullscreenRestoreState {
|
||||||
#[derive(Debug, Clone)]
|
#[derive(Debug, Clone)]
|
||||||
pub enum WorkspaceRestoreData {
|
pub enum WorkspaceRestoreData {
|
||||||
Fullscreen(Option<FullscreenRestoreData>),
|
Fullscreen(Option<FullscreenRestoreData>),
|
||||||
Tiling(Option<TilingRestoreData>),
|
Tiling(TilingRestoreData),
|
||||||
Floating(Option<FloatingRestoreData>),
|
Floating(FloatingRestoreData),
|
||||||
}
|
Stack(StackRestoreData),
|
||||||
|
|
||||||
impl From<ManagedLayer> 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),
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
#[derive(Debug, Clone)]
|
#[derive(Debug, Clone)]
|
||||||
|
|
@ -320,6 +328,12 @@ pub struct TilingRestoreData {
|
||||||
pub was_maximized: bool,
|
pub was_maximized: bool,
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[derive(Debug, Clone)]
|
||||||
|
pub struct StackRestoreData {
|
||||||
|
pub stack: CosmicMappedKey,
|
||||||
|
pub idx: usize,
|
||||||
|
}
|
||||||
|
|
||||||
#[derive(Debug, Clone)]
|
#[derive(Debug, Clone)]
|
||||||
pub struct FullscreenRestoreData {
|
pub struct FullscreenRestoreData {
|
||||||
pub previous_state: FullscreenRestoreState,
|
pub previous_state: FullscreenRestoreState,
|
||||||
|
|
@ -641,32 +655,30 @@ impl Workspace {
|
||||||
mapped.set_minimized(false);
|
mapped.set_minimized(false);
|
||||||
return Some(match state {
|
return Some(match state {
|
||||||
MinimizedWindow::Floating { previous, .. } => {
|
MinimizedWindow::Floating { previous, .. } => {
|
||||||
WorkspaceRestoreData::Floating(Some(previous))
|
WorkspaceRestoreData::Floating(previous)
|
||||||
}
|
|
||||||
MinimizedWindow::Tiling { previous, .. } => {
|
|
||||||
WorkspaceRestoreData::Tiling(Some(previous))
|
|
||||||
}
|
}
|
||||||
|
MinimizedWindow::Tiling { previous, .. } => WorkspaceRestoreData::Tiling(previous),
|
||||||
MinimizedWindow::Fullscreen { .. } => unreachable!(),
|
MinimizedWindow::Fullscreen { .. } => unreachable!(),
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
if let Ok(state) = self.tiling_layer.unmap(mapped, None) {
|
if let Ok(state) = self.tiling_layer.unmap(mapped, None) {
|
||||||
return Some(WorkspaceRestoreData::Tiling(Some(TilingRestoreData {
|
return Some(WorkspaceRestoreData::Tiling(TilingRestoreData {
|
||||||
state,
|
state,
|
||||||
was_maximized: was_maximized.is_some(),
|
was_maximized: was_maximized.is_some(),
|
||||||
})));
|
}));
|
||||||
}
|
}
|
||||||
|
|
||||||
let was_snapped = *mapped.floating_tiled.lock().unwrap();
|
let was_snapped = *mapped.floating_tiled.lock().unwrap();
|
||||||
// unmaximize_request might have triggered a `floating_layer.refresh()`,
|
// unmaximize_request might have triggered a `floating_layer.refresh()`,
|
||||||
// which may have already removed a non-alive surface.
|
// which may have already removed a non-alive surface.
|
||||||
if let Some(floating_geometry) = self.floating_layer.unmap(mapped, None).or(was_maximized) {
|
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,
|
geometry: floating_geometry,
|
||||||
output_size: self.output.geometry().size.as_logical(),
|
output_size: self.output.geometry().size.as_logical(),
|
||||||
was_maximized: was_maximized.is_some(),
|
was_maximized: was_maximized.is_some(),
|
||||||
was_snapped,
|
was_snapped,
|
||||||
})));
|
}));
|
||||||
};
|
};
|
||||||
|
|
||||||
None
|
None
|
||||||
|
|
@ -712,19 +724,17 @@ impl Workspace {
|
||||||
}
|
}
|
||||||
|
|
||||||
let mapped = self.element_for_surface(surface)?;
|
let mapped = self.element_for_surface(surface)?;
|
||||||
let maybe_stack = mapped.stack_ref().filter(|s| s.len() > 1);
|
if let Some(stack) = mapped.stack_ref()
|
||||||
if let Some(stack) = maybe_stack
|
|
||||||
&& stack.len() > 1
|
&& stack.len() > 1
|
||||||
{
|
{
|
||||||
let idx = stack.surfaces().position(|s| &s == surface);
|
let idx = stack.surfaces().position(|s| &s == surface)?;
|
||||||
let layer = if self.is_tiled(surface) {
|
return Some((
|
||||||
ManagedLayer::Tiling
|
stack.remove_idx(idx)?,
|
||||||
} else {
|
WorkspaceRestoreData::Stack(StackRestoreData {
|
||||||
ManagedLayer::Floating
|
stack: mapped.key(),
|
||||||
};
|
idx,
|
||||||
return idx
|
}),
|
||||||
.and_then(|idx| stack.remove_idx(idx))
|
));
|
||||||
.map(|s| (s, layer.into()));
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// we know mapped is no stack with more than one element now,
|
// we know mapped is no stack with more than one element now,
|
||||||
|
|
@ -775,7 +785,7 @@ impl Workspace {
|
||||||
location: Point<f64, Global>,
|
location: Point<f64, Global>,
|
||||||
seat: &Seat<State>,
|
seat: &Seat<State>,
|
||||||
) -> Option<KeyboardFocusTarget> {
|
) -> Option<KeyboardFocusTarget> {
|
||||||
if !self.output.geometry().contains(location.to_i32_round()) {
|
if !self.output.geometry().contains(location.to_i32_floor()) {
|
||||||
return None;
|
return None;
|
||||||
}
|
}
|
||||||
let location = location.to_local(&self.output);
|
let location = location.to_local(&self.output);
|
||||||
|
|
@ -824,7 +834,7 @@ impl Workspace {
|
||||||
location: Point<f64, Global>,
|
location: Point<f64, Global>,
|
||||||
seat: &Seat<State>,
|
seat: &Seat<State>,
|
||||||
) -> Option<KeyboardFocusTarget> {
|
) -> Option<KeyboardFocusTarget> {
|
||||||
if !self.output.geometry().contains(location.to_i32_round()) {
|
if !self.output.geometry().contains(location.to_i32_floor()) {
|
||||||
return None;
|
return None;
|
||||||
}
|
}
|
||||||
let location = location.to_local(&self.output);
|
let location = location.to_local(&self.output);
|
||||||
|
|
@ -874,7 +884,7 @@ impl Workspace {
|
||||||
overview: OverviewMode,
|
overview: OverviewMode,
|
||||||
seat: &Seat<State>,
|
seat: &Seat<State>,
|
||||||
) -> Option<(PointerFocusTarget, Point<f64, Global>)> {
|
) -> Option<(PointerFocusTarget, Point<f64, Global>)> {
|
||||||
if !self.output.geometry().contains(location.to_i32_round()) {
|
if !self.output.geometry().contains(location.to_i32_floor()) {
|
||||||
return None;
|
return None;
|
||||||
}
|
}
|
||||||
let location = location.to_local(&self.output);
|
let location = location.to_local(&self.output);
|
||||||
|
|
@ -924,7 +934,7 @@ impl Workspace {
|
||||||
overview: OverviewMode,
|
overview: OverviewMode,
|
||||||
seat: &Seat<State>,
|
seat: &Seat<State>,
|
||||||
) -> Option<(PointerFocusTarget, Point<f64, Global>)> {
|
) -> Option<(PointerFocusTarget, Point<f64, Global>)> {
|
||||||
if !self.output.geometry().contains(location.to_i32_round()) {
|
if !self.output.geometry().contains(location.to_i32_floor()) {
|
||||||
return None;
|
return None;
|
||||||
}
|
}
|
||||||
let location = location.to_local(&self.output);
|
let location = location.to_local(&self.output);
|
||||||
|
|
@ -1612,8 +1622,9 @@ impl Workspace {
|
||||||
resize_indicator: Option<(ResizeMode, ResizeIndicator)>,
|
resize_indicator: Option<(ResizeMode, ResizeIndicator)>,
|
||||||
indicator_thickness: u8,
|
indicator_thickness: u8,
|
||||||
theme: &CosmicTheme,
|
theme: &CosmicTheme,
|
||||||
) -> Result<Vec<WorkspaceRenderElement<R>>, OutputNotMapped>
|
scanout_node: Option<DrmNode>,
|
||||||
where
|
push: &mut dyn FnMut(WorkspaceRenderElement<R>),
|
||||||
|
) where
|
||||||
R: AsGlowRenderer,
|
R: AsGlowRenderer,
|
||||||
R::TextureId: Send + Clone + 'static,
|
R::TextureId: Send + Clone + 'static,
|
||||||
CosmicMappedRenderElement<R>: RenderElement<R>,
|
CosmicMappedRenderElement<R>: RenderElement<R>,
|
||||||
|
|
@ -1621,108 +1632,109 @@ impl Workspace {
|
||||||
CosmicStackRenderElement<R>: RenderElement<R>,
|
CosmicStackRenderElement<R>: RenderElement<R>,
|
||||||
WorkspaceRenderElement<R>: RenderElement<R>,
|
WorkspaceRenderElement<R>: RenderElement<R>,
|
||||||
{
|
{
|
||||||
let mut elements = Vec::default();
|
|
||||||
|
|
||||||
let output_scale = self.output.current_scale().fractional_scale();
|
let output_scale = self.output.current_scale().fractional_scale();
|
||||||
let zone = {
|
let zone = {
|
||||||
let layer_map = layer_map_for_output(&self.output);
|
let layer_map = layer_map_for_output(&self.output);
|
||||||
layer_map.non_exclusive_zone().as_local()
|
layer_map.non_exclusive_zone().as_local()
|
||||||
};
|
};
|
||||||
let focused = self.focus_stack.get(last_active_seat).last().cloned();
|
let focused = self.focus_stack.get(last_active_seat).last().cloned();
|
||||||
|
let fullscreen_focused = matches!(focused, Some(FocusTarget::Fullscreen(_)));
|
||||||
|
|
||||||
let render_fullscreen = |fullscreen: &FullscreenSurface,
|
let mut fullscreen_elements = SmallVec::<[WorkspaceRenderElement<R>; 2]>::new_const();
|
||||||
renderer: &mut R,
|
let mut render_fullscreen =
|
||||||
output_scale: f64|
|
|fullscreen: &FullscreenSurface, renderer: &mut R, output_scale: f64| {
|
||||||
-> Vec<WorkspaceRenderElement<R>> {
|
let fullscreen_geo = self.fullscreen_geometry_for(fullscreen);
|
||||||
let fullscreen_geo = self.fullscreen_geometry_for(fullscreen);
|
let previous_geo = fullscreen
|
||||||
let previous_geo = fullscreen
|
.previous_geometry
|
||||||
.previous_geometry
|
.as_ref()
|
||||||
.as_ref()
|
.unwrap_or(&fullscreen_geo);
|
||||||
.unwrap_or(&fullscreen_geo);
|
|
||||||
|
|
||||||
let (target_geo, alpha) = match (fullscreen.start_at, fullscreen.ended_at) {
|
let (target_geo, alpha) = match (fullscreen.start_at, fullscreen.ended_at) {
|
||||||
(Some(started), _) => {
|
(Some(started), _) => {
|
||||||
let duration = Instant::now().duration_since(started).as_secs_f64()
|
let duration = Instant::now().duration_since(started).as_secs_f64()
|
||||||
/ FULLSCREEN_ANIMATION_DURATION.as_secs_f64();
|
/ FULLSCREEN_ANIMATION_DURATION.as_secs_f64();
|
||||||
(
|
(
|
||||||
ease(
|
ease(
|
||||||
EaseInOutCubic,
|
EaseInOutCubic,
|
||||||
EaseRectangle(*previous_geo),
|
EaseRectangle(*previous_geo),
|
||||||
EaseRectangle(fullscreen_geo),
|
EaseRectangle(fullscreen_geo),
|
||||||
duration,
|
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();
|
||||||
(_, Some(ended)) => {
|
(
|
||||||
let duration = Instant::now().duration_since(ended).as_secs_f64()
|
ease(
|
||||||
/ FULLSCREEN_ANIMATION_DURATION.as_secs_f64();
|
EaseInOutCubic,
|
||||||
(
|
EaseRectangle(fullscreen_geo),
|
||||||
ease(
|
EaseRectangle(*previous_geo),
|
||||||
EaseInOutCubic,
|
duration,
|
||||||
EaseRectangle(fullscreen_geo),
|
)
|
||||||
EaseRectangle(*previous_geo),
|
.0,
|
||||||
duration,
|
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
|
let render_loc = target_geo
|
||||||
.loc
|
.loc
|
||||||
.as_logical()
|
.as_logical()
|
||||||
.to_physical_precise_round(output_scale);
|
.to_physical_precise_round(output_scale);
|
||||||
|
|
||||||
// Only rescale geometry when animating
|
let fullscreen_geo = fullscreen.surface.0.geometry();
|
||||||
let animation_rescale = |elem| {
|
let scale = Scale {
|
||||||
if fullscreen.is_animating() {
|
x: target_geo.size.w as f64 / fullscreen_geo.size.w as f64,
|
||||||
let fullscreen_geo = fullscreen.surface.0.geometry();
|
y: target_geo.size.h as f64 / fullscreen_geo.size.h as f64,
|
||||||
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()
|
// Only rescale geometry when animating
|
||||||
} else {
|
let animation_rescale = |elem| {
|
||||||
Into::<WorkspaceRenderElement<_>>::into(elem)
|
if fullscreen.is_animating() {
|
||||||
}
|
RescaleRenderElement::from_element(elem, render_loc, scale).into()
|
||||||
};
|
} else {
|
||||||
|
Into::<WorkspaceRenderElement<_>>::into(elem)
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
fullscreen
|
let mut fullscreen_push = |elem: SurfaceRenderElement<R>| {
|
||||||
.surface
|
if fullscreen_focused {
|
||||||
.render_elements::<R, CosmicWindowRenderElement<R>>(
|
push(animation_rescale(elem.into()))
|
||||||
|
} else {
|
||||||
|
fullscreen_elements.push(animation_rescale(elem.into()))
|
||||||
|
}
|
||||||
|
};
|
||||||
|
fullscreen.surface.push_render_elements(
|
||||||
renderer,
|
renderer,
|
||||||
render_loc,
|
render_loc,
|
||||||
output_scale.into(),
|
output_scale.into(),
|
||||||
alpha,
|
alpha,
|
||||||
Some(true),
|
Some(true),
|
||||||
)
|
scanout_node,
|
||||||
.into_iter()
|
false,
|
||||||
.map(animation_rescale)
|
[0, 0, 0, 0],
|
||||||
.collect::<Vec<_>>()
|
0,
|
||||||
};
|
&mut fullscreen_push,
|
||||||
|
None,
|
||||||
|
);
|
||||||
|
};
|
||||||
|
|
||||||
let top_fullscreen = self.get_fullscreen(last_active_seat);
|
let top_fullscreen = self.get_fullscreen(last_active_seat);
|
||||||
|
|
||||||
let mut fullscreen_elements: Vec<WorkspaceRenderElement<R>> = Vec::new();
|
|
||||||
if let Some(fs) = top_fullscreen {
|
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
|
// Also render any animating (entering/exiting) fullscreens
|
||||||
for fs in self.fullscreen_surfaces.iter().filter(|f| f.is_animating()) {
|
for fs in self.fullscreen_surfaces.iter().filter(|f| f.is_animating()) {
|
||||||
if top_fullscreen.is_none_or(|top| top.surface != fs.surface) {
|
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
|
let any_fullscreen_animating = self
|
||||||
.fullscreen_surfaces
|
.fullscreen_surfaces
|
||||||
.iter()
|
.iter()
|
||||||
|
|
@ -1753,28 +1765,21 @@ impl Workspace {
|
||||||
OverviewMode::None => 1.0,
|
OverviewMode::None => 1.0,
|
||||||
};
|
};
|
||||||
|
|
||||||
elements.extend(
|
self.floating_layer.render(
|
||||||
self.floating_layer
|
renderer,
|
||||||
.render::<R>(
|
focused.as_ref().and_then(|target| {
|
||||||
renderer,
|
if let FocusTarget::Window(mapped) = target {
|
||||||
render_focus
|
Some(mapped)
|
||||||
.then(|| {
|
} else {
|
||||||
focused.as_ref().and_then(|target| {
|
None
|
||||||
if let FocusTarget::Window(mapped) = target {
|
}
|
||||||
Some(mapped)
|
}),
|
||||||
} else {
|
resize_indicator.clone(),
|
||||||
None
|
indicator_thickness,
|
||||||
}
|
alpha,
|
||||||
})
|
theme,
|
||||||
})
|
scanout_node,
|
||||||
.flatten(),
|
&mut |elem| push(elem.into()),
|
||||||
resize_indicator.clone(),
|
|
||||||
indicator_thickness,
|
|
||||||
alpha,
|
|
||||||
theme,
|
|
||||||
)
|
|
||||||
.into_iter()
|
|
||||||
.map(WorkspaceRenderElement::from),
|
|
||||||
);
|
);
|
||||||
|
|
||||||
let alpha = match &overview.0 {
|
let alpha = match &overview.0 {
|
||||||
|
|
@ -1791,23 +1796,31 @@ impl Workspace {
|
||||||
};
|
};
|
||||||
|
|
||||||
//tiling surfaces
|
//tiling surfaces
|
||||||
elements.extend(
|
self.tiling_layer.render(
|
||||||
self.tiling_layer
|
renderer,
|
||||||
.render::<R>(
|
render_focus.then_some(last_active_seat),
|
||||||
renderer,
|
render_focus
|
||||||
render_focus.then_some(last_active_seat),
|
.then(|| {
|
||||||
zone,
|
focused.as_ref().and_then(|target| {
|
||||||
overview,
|
if let FocusTarget::Window(mapped) = target {
|
||||||
resize_indicator,
|
Some(mapped)
|
||||||
indicator_thickness,
|
} else {
|
||||||
theme,
|
None
|
||||||
)?
|
}
|
||||||
.into_iter()
|
})
|
||||||
.map(WorkspaceRenderElement::from),
|
})
|
||||||
|
.flatten(),
|
||||||
|
zone,
|
||||||
|
overview,
|
||||||
|
resize_indicator,
|
||||||
|
indicator_thickness,
|
||||||
|
theme,
|
||||||
|
scanout_node,
|
||||||
|
&mut |elem| push(elem.into()),
|
||||||
);
|
);
|
||||||
|
|
||||||
if let Some(alpha) = alpha {
|
if let Some(alpha) = alpha {
|
||||||
elements.push(
|
push(
|
||||||
Into::<CosmicMappedRenderElement<R>>::into(BackdropShader::element(
|
Into::<CosmicMappedRenderElement<R>>::into(BackdropShader::element(
|
||||||
renderer,
|
renderer,
|
||||||
self.backdrop_id.clone(),
|
self.backdrop_id.clone(),
|
||||||
|
|
@ -1821,11 +1834,9 @@ impl Workspace {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
if !matches!(focused, Some(FocusTarget::Fullscreen(_))) {
|
for elem in fullscreen_elements {
|
||||||
elements.extend(fullscreen_elements.into_iter());
|
push(elem);
|
||||||
}
|
}
|
||||||
|
|
||||||
Ok(elements)
|
|
||||||
}
|
}
|
||||||
|
|
||||||
#[profiling::function]
|
#[profiling::function]
|
||||||
|
|
@ -1836,8 +1847,9 @@ impl Workspace {
|
||||||
render_focus: bool,
|
render_focus: bool,
|
||||||
overview: (OverviewMode, Option<(SwapIndicator, Option<&Tree<Data>>)>),
|
overview: (OverviewMode, Option<(SwapIndicator, Option<&Tree<Data>>)>),
|
||||||
theme: &CosmicTheme,
|
theme: &CosmicTheme,
|
||||||
) -> Result<Vec<WorkspaceRenderElement<R>>, OutputNotMapped>
|
scanout_node: Option<DrmNode>,
|
||||||
where
|
push: &mut dyn FnMut(WorkspaceRenderElement<R>),
|
||||||
|
) where
|
||||||
R: AsGlowRenderer,
|
R: AsGlowRenderer,
|
||||||
R::TextureId: Send + Clone + 'static,
|
R::TextureId: Send + Clone + 'static,
|
||||||
CosmicMappedRenderElement<R>: RenderElement<R>,
|
CosmicMappedRenderElement<R>: RenderElement<R>,
|
||||||
|
|
@ -1845,8 +1857,6 @@ impl Workspace {
|
||||||
CosmicStackRenderElement<R>: RenderElement<R>,
|
CosmicStackRenderElement<R>: RenderElement<R>,
|
||||||
WorkspaceRenderElement<R>: RenderElement<R>,
|
WorkspaceRenderElement<R>: RenderElement<R>,
|
||||||
{
|
{
|
||||||
let mut elements = Vec::default();
|
|
||||||
|
|
||||||
let output_scale = self.output.current_scale().fractional_scale();
|
let output_scale = self.output.current_scale().fractional_scale();
|
||||||
let zone = {
|
let zone = {
|
||||||
let layer_map = layer_map_for_output(&self.output);
|
let layer_map = layer_map_for_output(&self.output);
|
||||||
|
|
@ -1901,17 +1911,14 @@ impl Workspace {
|
||||||
.as_logical()
|
.as_logical()
|
||||||
.to_physical_precise_round(output_scale);
|
.to_physical_precise_round(output_scale);
|
||||||
|
|
||||||
elements.extend(
|
fullscreen.surface.push_popup_render_elements(
|
||||||
fullscreen
|
renderer,
|
||||||
.surface
|
render_loc,
|
||||||
.popup_render_elements::<R, CosmicWindowRenderElement<R>>(
|
output_scale.into(),
|
||||||
renderer,
|
alpha,
|
||||||
render_loc,
|
scanout_node,
|
||||||
output_scale.into(),
|
0,
|
||||||
alpha,
|
&mut |elem| push(WorkspaceRenderElement::FullscreenPopup(elem.into())),
|
||||||
)
|
|
||||||
.into_iter()
|
|
||||||
.map(Into::into),
|
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -1945,29 +1952,20 @@ impl Workspace {
|
||||||
OverviewMode::None => 1.0,
|
OverviewMode::None => 1.0,
|
||||||
};
|
};
|
||||||
|
|
||||||
elements.extend(
|
self.floating_layer
|
||||||
self.floating_layer
|
.render_popups(renderer, alpha, scanout_node, &mut |elem| push(elem.into()));
|
||||||
.render_popups::<R>(renderer, alpha)
|
|
||||||
.into_iter()
|
|
||||||
.map(WorkspaceRenderElement::from),
|
|
||||||
);
|
|
||||||
|
|
||||||
//tiling surfaces
|
//tiling surfaces
|
||||||
elements.extend(
|
self.tiling_layer.render_popups(
|
||||||
self.tiling_layer
|
renderer,
|
||||||
.render_popups::<R>(
|
render_focus.then_some(last_active_seat),
|
||||||
renderer,
|
zone,
|
||||||
render_focus.then_some(last_active_seat),
|
overview,
|
||||||
zone,
|
theme,
|
||||||
overview,
|
scanout_node,
|
||||||
theme,
|
&mut |elem| push(elem.into()),
|
||||||
)?
|
|
||||||
.into_iter()
|
|
||||||
.map(WorkspaceRenderElement::from),
|
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
Ok(elements)
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -1986,9 +1984,10 @@ pub struct OutputNotMapped;
|
||||||
pub enum WorkspaceRenderElement<R>
|
pub enum WorkspaceRenderElement<R>
|
||||||
where
|
where
|
||||||
R: AsGlowRenderer,
|
R: AsGlowRenderer,
|
||||||
R::TextureId: 'static,
|
R::TextureId: Send + 'static,
|
||||||
{
|
{
|
||||||
OverrideRedirect(WaylandSurfaceRenderElement<R>),
|
OverrideRedirect(SurfaceRenderElement<R>),
|
||||||
|
LowerLayerShell(NamespacedElement<SurfaceRenderElement<R>>),
|
||||||
Fullscreen(RescaleRenderElement<CosmicWindowRenderElement<R>>),
|
Fullscreen(RescaleRenderElement<CosmicWindowRenderElement<R>>),
|
||||||
FullscreenPopup(CosmicWindowRenderElement<R>),
|
FullscreenPopup(CosmicWindowRenderElement<R>),
|
||||||
Window(CosmicMappedRenderElement<R>),
|
Window(CosmicMappedRenderElement<R>),
|
||||||
|
|
@ -1998,11 +1997,12 @@ where
|
||||||
impl<R> Element for WorkspaceRenderElement<R>
|
impl<R> Element for WorkspaceRenderElement<R>
|
||||||
where
|
where
|
||||||
R: AsGlowRenderer,
|
R: AsGlowRenderer,
|
||||||
R::TextureId: 'static,
|
R::TextureId: Send + 'static,
|
||||||
{
|
{
|
||||||
fn id(&self) -> &smithay::backend::renderer::element::Id {
|
fn id(&self) -> &smithay::backend::renderer::element::Id {
|
||||||
match self {
|
match self {
|
||||||
WorkspaceRenderElement::OverrideRedirect(elem) => elem.id(),
|
WorkspaceRenderElement::OverrideRedirect(elem) => elem.id(),
|
||||||
|
WorkspaceRenderElement::LowerLayerShell(elem) => elem.id(),
|
||||||
WorkspaceRenderElement::Fullscreen(elem) => elem.id(),
|
WorkspaceRenderElement::Fullscreen(elem) => elem.id(),
|
||||||
WorkspaceRenderElement::FullscreenPopup(elem) => elem.id(),
|
WorkspaceRenderElement::FullscreenPopup(elem) => elem.id(),
|
||||||
WorkspaceRenderElement::Window(elem) => elem.id(),
|
WorkspaceRenderElement::Window(elem) => elem.id(),
|
||||||
|
|
@ -2013,6 +2013,7 @@ where
|
||||||
fn current_commit(&self) -> smithay::backend::renderer::utils::CommitCounter {
|
fn current_commit(&self) -> smithay::backend::renderer::utils::CommitCounter {
|
||||||
match self {
|
match self {
|
||||||
WorkspaceRenderElement::OverrideRedirect(elem) => elem.current_commit(),
|
WorkspaceRenderElement::OverrideRedirect(elem) => elem.current_commit(),
|
||||||
|
WorkspaceRenderElement::LowerLayerShell(elem) => elem.current_commit(),
|
||||||
WorkspaceRenderElement::Fullscreen(elem) => elem.current_commit(),
|
WorkspaceRenderElement::Fullscreen(elem) => elem.current_commit(),
|
||||||
WorkspaceRenderElement::FullscreenPopup(elem) => elem.current_commit(),
|
WorkspaceRenderElement::FullscreenPopup(elem) => elem.current_commit(),
|
||||||
WorkspaceRenderElement::Window(elem) => elem.current_commit(),
|
WorkspaceRenderElement::Window(elem) => elem.current_commit(),
|
||||||
|
|
@ -2023,6 +2024,7 @@ where
|
||||||
fn src(&self) -> Rectangle<f64, smithay::utils::Buffer> {
|
fn src(&self) -> Rectangle<f64, smithay::utils::Buffer> {
|
||||||
match self {
|
match self {
|
||||||
WorkspaceRenderElement::OverrideRedirect(elem) => elem.src(),
|
WorkspaceRenderElement::OverrideRedirect(elem) => elem.src(),
|
||||||
|
WorkspaceRenderElement::LowerLayerShell(elem) => elem.src(),
|
||||||
WorkspaceRenderElement::Fullscreen(elem) => elem.src(),
|
WorkspaceRenderElement::Fullscreen(elem) => elem.src(),
|
||||||
WorkspaceRenderElement::FullscreenPopup(elem) => elem.src(),
|
WorkspaceRenderElement::FullscreenPopup(elem) => elem.src(),
|
||||||
WorkspaceRenderElement::Window(elem) => elem.src(),
|
WorkspaceRenderElement::Window(elem) => elem.src(),
|
||||||
|
|
@ -2033,6 +2035,7 @@ where
|
||||||
fn geometry(&self, scale: Scale<f64>) -> Rectangle<i32, smithay::utils::Physical> {
|
fn geometry(&self, scale: Scale<f64>) -> Rectangle<i32, smithay::utils::Physical> {
|
||||||
match self {
|
match self {
|
||||||
WorkspaceRenderElement::OverrideRedirect(elem) => elem.geometry(scale),
|
WorkspaceRenderElement::OverrideRedirect(elem) => elem.geometry(scale),
|
||||||
|
WorkspaceRenderElement::LowerLayerShell(elem) => elem.geometry(scale),
|
||||||
WorkspaceRenderElement::Fullscreen(elem) => elem.geometry(scale),
|
WorkspaceRenderElement::Fullscreen(elem) => elem.geometry(scale),
|
||||||
WorkspaceRenderElement::FullscreenPopup(elem) => elem.geometry(scale),
|
WorkspaceRenderElement::FullscreenPopup(elem) => elem.geometry(scale),
|
||||||
WorkspaceRenderElement::Window(elem) => elem.geometry(scale),
|
WorkspaceRenderElement::Window(elem) => elem.geometry(scale),
|
||||||
|
|
@ -2043,6 +2046,7 @@ where
|
||||||
fn location(&self, scale: Scale<f64>) -> Point<i32, smithay::utils::Physical> {
|
fn location(&self, scale: Scale<f64>) -> Point<i32, smithay::utils::Physical> {
|
||||||
match self {
|
match self {
|
||||||
WorkspaceRenderElement::OverrideRedirect(elem) => elem.location(scale),
|
WorkspaceRenderElement::OverrideRedirect(elem) => elem.location(scale),
|
||||||
|
WorkspaceRenderElement::LowerLayerShell(elem) => elem.location(scale),
|
||||||
WorkspaceRenderElement::Fullscreen(elem) => elem.location(scale),
|
WorkspaceRenderElement::Fullscreen(elem) => elem.location(scale),
|
||||||
WorkspaceRenderElement::FullscreenPopup(elem) => elem.location(scale),
|
WorkspaceRenderElement::FullscreenPopup(elem) => elem.location(scale),
|
||||||
WorkspaceRenderElement::Window(elem) => elem.location(scale),
|
WorkspaceRenderElement::Window(elem) => elem.location(scale),
|
||||||
|
|
@ -2053,6 +2057,7 @@ where
|
||||||
fn transform(&self) -> smithay::utils::Transform {
|
fn transform(&self) -> smithay::utils::Transform {
|
||||||
match self {
|
match self {
|
||||||
WorkspaceRenderElement::OverrideRedirect(elem) => elem.transform(),
|
WorkspaceRenderElement::OverrideRedirect(elem) => elem.transform(),
|
||||||
|
WorkspaceRenderElement::LowerLayerShell(elem) => elem.transform(),
|
||||||
WorkspaceRenderElement::Fullscreen(elem) => elem.transform(),
|
WorkspaceRenderElement::Fullscreen(elem) => elem.transform(),
|
||||||
WorkspaceRenderElement::FullscreenPopup(elem) => elem.transform(),
|
WorkspaceRenderElement::FullscreenPopup(elem) => elem.transform(),
|
||||||
WorkspaceRenderElement::Window(elem) => elem.transform(),
|
WorkspaceRenderElement::Window(elem) => elem.transform(),
|
||||||
|
|
@ -2067,6 +2072,7 @@ where
|
||||||
) -> DamageSet<i32, smithay::utils::Physical> {
|
) -> DamageSet<i32, smithay::utils::Physical> {
|
||||||
match self {
|
match self {
|
||||||
WorkspaceRenderElement::OverrideRedirect(elem) => elem.damage_since(scale, commit),
|
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::Fullscreen(elem) => elem.damage_since(scale, commit),
|
||||||
WorkspaceRenderElement::FullscreenPopup(elem) => elem.damage_since(scale, commit),
|
WorkspaceRenderElement::FullscreenPopup(elem) => elem.damage_since(scale, commit),
|
||||||
WorkspaceRenderElement::Window(elem) => elem.damage_since(scale, commit),
|
WorkspaceRenderElement::Window(elem) => elem.damage_since(scale, commit),
|
||||||
|
|
@ -2077,6 +2083,7 @@ where
|
||||||
fn opaque_regions(&self, scale: Scale<f64>) -> OpaqueRegions<i32, smithay::utils::Physical> {
|
fn opaque_regions(&self, scale: Scale<f64>) -> OpaqueRegions<i32, smithay::utils::Physical> {
|
||||||
match self {
|
match self {
|
||||||
WorkspaceRenderElement::OverrideRedirect(elem) => elem.opaque_regions(scale),
|
WorkspaceRenderElement::OverrideRedirect(elem) => elem.opaque_regions(scale),
|
||||||
|
WorkspaceRenderElement::LowerLayerShell(elem) => elem.opaque_regions(scale),
|
||||||
WorkspaceRenderElement::Fullscreen(elem) => elem.opaque_regions(scale),
|
WorkspaceRenderElement::Fullscreen(elem) => elem.opaque_regions(scale),
|
||||||
WorkspaceRenderElement::FullscreenPopup(elem) => elem.opaque_regions(scale),
|
WorkspaceRenderElement::FullscreenPopup(elem) => elem.opaque_regions(scale),
|
||||||
WorkspaceRenderElement::Window(elem) => elem.opaque_regions(scale),
|
WorkspaceRenderElement::Window(elem) => elem.opaque_regions(scale),
|
||||||
|
|
@ -2087,6 +2094,7 @@ where
|
||||||
fn alpha(&self) -> f32 {
|
fn alpha(&self) -> f32 {
|
||||||
match self {
|
match self {
|
||||||
WorkspaceRenderElement::OverrideRedirect(elem) => elem.alpha(),
|
WorkspaceRenderElement::OverrideRedirect(elem) => elem.alpha(),
|
||||||
|
WorkspaceRenderElement::LowerLayerShell(elem) => elem.alpha(),
|
||||||
WorkspaceRenderElement::Fullscreen(elem) => elem.alpha(),
|
WorkspaceRenderElement::Fullscreen(elem) => elem.alpha(),
|
||||||
WorkspaceRenderElement::FullscreenPopup(elem) => elem.alpha(),
|
WorkspaceRenderElement::FullscreenPopup(elem) => elem.alpha(),
|
||||||
WorkspaceRenderElement::Window(elem) => elem.alpha(),
|
WorkspaceRenderElement::Window(elem) => elem.alpha(),
|
||||||
|
|
@ -2097,6 +2105,7 @@ where
|
||||||
fn kind(&self) -> Kind {
|
fn kind(&self) -> Kind {
|
||||||
match self {
|
match self {
|
||||||
WorkspaceRenderElement::OverrideRedirect(elem) => elem.kind(),
|
WorkspaceRenderElement::OverrideRedirect(elem) => elem.kind(),
|
||||||
|
WorkspaceRenderElement::LowerLayerShell(elem) => elem.kind(),
|
||||||
WorkspaceRenderElement::Fullscreen(elem) => elem.kind(),
|
WorkspaceRenderElement::Fullscreen(elem) => elem.kind(),
|
||||||
WorkspaceRenderElement::FullscreenPopup(elem) => elem.kind(),
|
WorkspaceRenderElement::FullscreenPopup(elem) => elem.kind(),
|
||||||
WorkspaceRenderElement::Window(elem) => elem.kind(),
|
WorkspaceRenderElement::Window(elem) => elem.kind(),
|
||||||
|
|
@ -2107,6 +2116,7 @@ where
|
||||||
fn is_framebuffer_effect(&self) -> bool {
|
fn is_framebuffer_effect(&self) -> bool {
|
||||||
match self {
|
match self {
|
||||||
WorkspaceRenderElement::OverrideRedirect(elem) => elem.is_framebuffer_effect(),
|
WorkspaceRenderElement::OverrideRedirect(elem) => elem.is_framebuffer_effect(),
|
||||||
|
WorkspaceRenderElement::LowerLayerShell(elem) => elem.is_framebuffer_effect(),
|
||||||
WorkspaceRenderElement::Fullscreen(elem) => elem.is_framebuffer_effect(),
|
WorkspaceRenderElement::Fullscreen(elem) => elem.is_framebuffer_effect(),
|
||||||
WorkspaceRenderElement::FullscreenPopup(elem) => elem.is_framebuffer_effect(),
|
WorkspaceRenderElement::FullscreenPopup(elem) => elem.is_framebuffer_effect(),
|
||||||
WorkspaceRenderElement::Window(elem) => elem.is_framebuffer_effect(),
|
WorkspaceRenderElement::Window(elem) => elem.is_framebuffer_effect(),
|
||||||
|
|
@ -2118,8 +2128,7 @@ where
|
||||||
impl<R> RenderElement<R> for WorkspaceRenderElement<R>
|
impl<R> RenderElement<R> for WorkspaceRenderElement<R>
|
||||||
where
|
where
|
||||||
R: AsGlowRenderer,
|
R: AsGlowRenderer,
|
||||||
R::TextureId: 'static,
|
R::TextureId: Send + 'static,
|
||||||
R::Error: FromGlesError,
|
|
||||||
{
|
{
|
||||||
fn draw(
|
fn draw(
|
||||||
&self,
|
&self,
|
||||||
|
|
@ -2134,6 +2143,9 @@ where
|
||||||
WorkspaceRenderElement::OverrideRedirect(elem) => {
|
WorkspaceRenderElement::OverrideRedirect(elem) => {
|
||||||
elem.draw(frame, src, dst, damage, opaque_regions, cache)
|
elem.draw(frame, src, dst, damage, opaque_regions, cache)
|
||||||
}
|
}
|
||||||
|
WorkspaceRenderElement::LowerLayerShell(elem) => {
|
||||||
|
elem.draw(frame, src, dst, damage, opaque_regions, cache)
|
||||||
|
}
|
||||||
WorkspaceRenderElement::Fullscreen(elem) => {
|
WorkspaceRenderElement::Fullscreen(elem) => {
|
||||||
elem.draw(frame, src, dst, damage, opaque_regions, cache)
|
elem.draw(frame, src, dst, damage, opaque_regions, cache)
|
||||||
}
|
}
|
||||||
|
|
@ -2152,7 +2164,7 @@ where
|
||||||
opaque_regions,
|
opaque_regions,
|
||||||
cache,
|
cache,
|
||||||
)
|
)
|
||||||
.map_err(FromGlesError::from_gles_error),
|
.map_err(R::from_gles_error),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -2162,6 +2174,7 @@ where
|
||||||
) -> Option<smithay::backend::renderer::element::UnderlyingStorage<'_>> {
|
) -> Option<smithay::backend::renderer::element::UnderlyingStorage<'_>> {
|
||||||
match self {
|
match self {
|
||||||
WorkspaceRenderElement::OverrideRedirect(elem) => elem.underlying_storage(renderer),
|
WorkspaceRenderElement::OverrideRedirect(elem) => elem.underlying_storage(renderer),
|
||||||
|
WorkspaceRenderElement::LowerLayerShell(elem) => elem.underlying_storage(renderer),
|
||||||
WorkspaceRenderElement::Fullscreen(elem) => elem.underlying_storage(renderer),
|
WorkspaceRenderElement::Fullscreen(elem) => elem.underlying_storage(renderer),
|
||||||
WorkspaceRenderElement::FullscreenPopup(elem) => elem.underlying_storage(renderer),
|
WorkspaceRenderElement::FullscreenPopup(elem) => elem.underlying_storage(renderer),
|
||||||
WorkspaceRenderElement::Window(elem) => elem.underlying_storage(renderer),
|
WorkspaceRenderElement::Window(elem) => elem.underlying_storage(renderer),
|
||||||
|
|
@ -2182,6 +2195,9 @@ where
|
||||||
WorkspaceRenderElement::OverrideRedirect(elem) => {
|
WorkspaceRenderElement::OverrideRedirect(elem) => {
|
||||||
elem.capture_framebuffer(frame, src, dst, cache)
|
elem.capture_framebuffer(frame, src, dst, cache)
|
||||||
}
|
}
|
||||||
|
WorkspaceRenderElement::LowerLayerShell(elem) => {
|
||||||
|
elem.capture_framebuffer(frame, src, dst, cache)
|
||||||
|
}
|
||||||
WorkspaceRenderElement::Fullscreen(elem) => {
|
WorkspaceRenderElement::Fullscreen(elem) => {
|
||||||
elem.capture_framebuffer(frame, src, dst, cache)
|
elem.capture_framebuffer(frame, src, dst, cache)
|
||||||
}
|
}
|
||||||
|
|
@ -2199,7 +2215,7 @@ where
|
||||||
dst,
|
dst,
|
||||||
cache,
|
cache,
|
||||||
)
|
)
|
||||||
.map_err(FromGlesError::from_gles_error)
|
.map_err(R::from_gles_error)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
@ -2208,7 +2224,7 @@ where
|
||||||
impl<R> From<RescaleRenderElement<CosmicWindowRenderElement<R>>> for WorkspaceRenderElement<R>
|
impl<R> From<RescaleRenderElement<CosmicWindowRenderElement<R>>> for WorkspaceRenderElement<R>
|
||||||
where
|
where
|
||||||
R: AsGlowRenderer,
|
R: AsGlowRenderer,
|
||||||
R::TextureId: 'static,
|
R::TextureId: Send + 'static,
|
||||||
CosmicMappedRenderElement<R>: RenderElement<R>,
|
CosmicMappedRenderElement<R>: RenderElement<R>,
|
||||||
{
|
{
|
||||||
fn from(elem: RescaleRenderElement<CosmicWindowRenderElement<R>>) -> Self {
|
fn from(elem: RescaleRenderElement<CosmicWindowRenderElement<R>>) -> Self {
|
||||||
|
|
@ -2219,7 +2235,7 @@ where
|
||||||
impl<R> From<CosmicWindowRenderElement<R>> for WorkspaceRenderElement<R>
|
impl<R> From<CosmicWindowRenderElement<R>> for WorkspaceRenderElement<R>
|
||||||
where
|
where
|
||||||
R: AsGlowRenderer,
|
R: AsGlowRenderer,
|
||||||
R::TextureId: 'static,
|
R::TextureId: Send + 'static,
|
||||||
CosmicMappedRenderElement<R>: RenderElement<R>,
|
CosmicMappedRenderElement<R>: RenderElement<R>,
|
||||||
{
|
{
|
||||||
fn from(elem: CosmicWindowRenderElement<R>) -> Self {
|
fn from(elem: CosmicWindowRenderElement<R>) -> Self {
|
||||||
|
|
@ -2227,21 +2243,32 @@ where
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
impl<R> From<WaylandSurfaceRenderElement<R>> for WorkspaceRenderElement<R>
|
impl<R> From<SurfaceRenderElement<R>> for WorkspaceRenderElement<R>
|
||||||
where
|
where
|
||||||
R: AsGlowRenderer,
|
R: AsGlowRenderer,
|
||||||
R::TextureId: 'static,
|
R::TextureId: Send + 'static,
|
||||||
CosmicMappedRenderElement<R>: RenderElement<R>,
|
CosmicMappedRenderElement<R>: RenderElement<R>,
|
||||||
{
|
{
|
||||||
fn from(elem: WaylandSurfaceRenderElement<R>) -> Self {
|
fn from(elem: SurfaceRenderElement<R>) -> Self {
|
||||||
WorkspaceRenderElement::OverrideRedirect(elem)
|
WorkspaceRenderElement::OverrideRedirect(elem)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
impl<R> From<NamespacedElement<SurfaceRenderElement<R>>> for WorkspaceRenderElement<R>
|
||||||
|
where
|
||||||
|
R: AsGlowRenderer,
|
||||||
|
R::TextureId: Send + 'static,
|
||||||
|
CosmicMappedRenderElement<R>: RenderElement<R>,
|
||||||
|
{
|
||||||
|
fn from(elem: NamespacedElement<SurfaceRenderElement<R>>) -> Self {
|
||||||
|
WorkspaceRenderElement::LowerLayerShell(elem)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
impl<R> From<CosmicMappedRenderElement<R>> for WorkspaceRenderElement<R>
|
impl<R> From<CosmicMappedRenderElement<R>> for WorkspaceRenderElement<R>
|
||||||
where
|
where
|
||||||
R: AsGlowRenderer,
|
R: AsGlowRenderer,
|
||||||
R::TextureId: 'static,
|
R::TextureId: Send + 'static,
|
||||||
CosmicMappedRenderElement<R>: RenderElement<R>,
|
CosmicMappedRenderElement<R>: RenderElement<R>,
|
||||||
{
|
{
|
||||||
fn from(elem: CosmicMappedRenderElement<R>) -> Self {
|
fn from(elem: CosmicMappedRenderElement<R>) -> Self {
|
||||||
|
|
@ -2252,7 +2279,7 @@ where
|
||||||
impl<R> From<TextureRenderElement<GlesTexture>> for WorkspaceRenderElement<R>
|
impl<R> From<TextureRenderElement<GlesTexture>> for WorkspaceRenderElement<R>
|
||||||
where
|
where
|
||||||
R: AsGlowRenderer,
|
R: AsGlowRenderer,
|
||||||
R::TextureId: 'static,
|
R::TextureId: Send + 'static,
|
||||||
CosmicMappedRenderElement<R>: RenderElement<R>,
|
CosmicMappedRenderElement<R>: RenderElement<R>,
|
||||||
{
|
{
|
||||||
fn from(elem: TextureRenderElement<GlesTexture>) -> Self {
|
fn from(elem: TextureRenderElement<GlesTexture>) -> Self {
|
||||||
|
|
|
||||||
|
|
@ -3,15 +3,15 @@ use std::{sync::Mutex, time::Instant};
|
||||||
use calloop::LoopHandle;
|
use calloop::LoopHandle;
|
||||||
use cosmic::{
|
use cosmic::{
|
||||||
Apply,
|
Apply,
|
||||||
iced::{Alignment, Background, Border, Length, alignment::Vertical},
|
iced::{Alignment, Background, Border, Length, alignment::Vertical, widget as iced_widget},
|
||||||
iced_widget, theme,
|
theme,
|
||||||
widget::{self, icon::Named},
|
widget::{self, icon::Named},
|
||||||
};
|
};
|
||||||
use cosmic_comp_config::ZoomMovement;
|
use cosmic_comp_config::ZoomMovement;
|
||||||
use cosmic_config::ConfigSet;
|
use cosmic_config::ConfigSet;
|
||||||
use keyframe::{ease, functions::Linear};
|
use keyframe::{ease, functions::Linear};
|
||||||
use smithay::{
|
use smithay::{
|
||||||
backend::renderer::{ImportMem, Renderer, element::AsRenderElements},
|
backend::renderer::ImportMem,
|
||||||
desktop::space::SpaceElement,
|
desktop::space::SpaceElement,
|
||||||
input::{
|
input::{
|
||||||
Seat,
|
Seat,
|
||||||
|
|
@ -27,14 +27,15 @@ use smithay::{
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
output::Output,
|
output::Output,
|
||||||
utils::{IsAlive, Point, Rectangle, Serial, Size},
|
utils::{FrameExtents, IsAlive, Point, Rectangle, Serial, Size},
|
||||||
};
|
};
|
||||||
use tracing::error;
|
use tracing::error;
|
||||||
|
|
||||||
use crate::{
|
use crate::{
|
||||||
|
backend::render::element::AsGlowRenderer,
|
||||||
state::State,
|
state::State,
|
||||||
utils::{
|
utils::{
|
||||||
iced::{IcedElement, Program},
|
iced::{IcedElement, IcedRenderElement, Program},
|
||||||
prelude::*,
|
prelude::*,
|
||||||
tween::EasePoint,
|
tween::EasePoint,
|
||||||
},
|
},
|
||||||
|
|
@ -71,45 +72,17 @@ impl OutputZoomState {
|
||||||
increment: u32,
|
increment: u32,
|
||||||
movement: ZoomMovement,
|
movement: ZoomMovement,
|
||||||
loop_handle: LoopHandle<'static, State>,
|
loop_handle: LoopHandle<'static, State>,
|
||||||
theme: cosmic::Theme,
|
mut theme: cosmic::Theme,
|
||||||
) -> OutputZoomState {
|
) -> OutputZoomState {
|
||||||
|
theme.transparent = theme.cosmic().frosted_system_interface;
|
||||||
let cursor_position = seat.get_pointer().unwrap().current_location().as_global();
|
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 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 program = ZoomProgram::new(level, movement, increment);
|
||||||
let element = IcedElement::new(program, Size::default(), loop_handle, theme);
|
let element = IcedElement::new(program, Size::default(), loop_handle, theme);
|
||||||
let mut size = element.minimum_size();
|
let mut size = element.minimum_size();
|
||||||
size.w = (size.w + 32/*TODO: figure out why iced is calculating too little*/)
|
size.w = size.w.min(output_geometry.size.w.round() as i32);
|
||||||
.min(output_geometry.size.w.round() as i32);
|
|
||||||
element.set_activate(true);
|
element.set_activate(true);
|
||||||
element.resize(size);
|
element.resize(size);
|
||||||
element.output_enter(output, Rectangle::new(Point::from((0, 0)), size));
|
element.output_enter(output, Rectangle::new(Point::from((0, 0)), size));
|
||||||
|
|
@ -192,10 +165,13 @@ impl OutputZoomState {
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
fn render<R, C>(&mut self, renderer: &mut R, output: &Output) -> Vec<C>
|
fn render<R>(
|
||||||
where
|
&mut self,
|
||||||
C: From<<IcedElement<ZoomProgram> as AsRenderElements<R>>::RenderElement>,
|
renderer: &mut R,
|
||||||
R: Renderer + ImportMem,
|
output: &Output,
|
||||||
|
push: &mut dyn FnMut(IcedRenderElement<R>),
|
||||||
|
) where
|
||||||
|
R: AsGlowRenderer + ImportMem,
|
||||||
R::TextureId: Send + Clone + 'static,
|
R::TextureId: Send + Clone + 'static,
|
||||||
{
|
{
|
||||||
let size = self.element.current_size().to_f64();
|
let size = self.element.current_size().to_f64();
|
||||||
|
|
@ -208,8 +184,17 @@ impl OutputZoomState {
|
||||||
.to_physical(scale.fractional_scale())
|
.to_physical(scale.fractional_scale())
|
||||||
.to_i32_round();
|
.to_i32_round();
|
||||||
|
|
||||||
self.element
|
self.element.push_render_elements(
|
||||||
.render_elements(renderer, location, scale.fractional_scale().into(), 1.0)
|
renderer,
|
||||||
|
location,
|
||||||
|
scale.fractional_scale().into(),
|
||||||
|
1.0,
|
||||||
|
self.element
|
||||||
|
.with_theme(|theme| theme.cosmic().radius_s())
|
||||||
|
.map(|x| x.round() as u8),
|
||||||
|
push,
|
||||||
|
None,
|
||||||
|
)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -256,86 +241,101 @@ impl ZoomState {
|
||||||
&mut self,
|
&mut self,
|
||||||
output: &Output,
|
output: &Output,
|
||||||
cursor_position: Point<f64, Global>,
|
cursor_position: Point<f64, Global>,
|
||||||
original_position: Point<f64, Global>,
|
_original_position: Point<f64, Global>,
|
||||||
movement: ZoomMovement,
|
movement: ZoomMovement,
|
||||||
) {
|
) {
|
||||||
let output_geometry = output.geometry().to_f64();
|
let output_geometry = output.geometry().to_f64().to_local(output);
|
||||||
let mut zoomed_output_geometry = output.zoomed_geometry().unwrap().to_f64();
|
let zoomed_output_geometry = output.zoomed_geometry().unwrap().to_f64().to_local(output);
|
||||||
|
|
||||||
let output_state = output.user_data().get::<Mutex<OutputZoomState>>().unwrap();
|
let output_state = output.user_data().get::<Mutex<OutputZoomState>>().unwrap();
|
||||||
let mut output_state_ref = output_state.lock().unwrap();
|
let mut output_state_ref = output_state.lock().unwrap();
|
||||||
|
|
||||||
// animate movement type changes
|
let level = output_state_ref.current_level();
|
||||||
if self.movement != movement {
|
|
||||||
|
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()));
|
output_state_ref.previous_point = Some((output_state_ref.focal_point, Instant::now()));
|
||||||
self.movement = movement;
|
self.movement = movement;
|
||||||
}
|
}
|
||||||
|
|
||||||
let cursor_position = cursor_position.to_local(output);
|
let cursor_position = cursor_position.to_local(output);
|
||||||
match movement {
|
match movement {
|
||||||
ZoomMovement::Continuously => output_state_ref.focal_point = cursor_position,
|
ZoomMovement::Continuously => {
|
||||||
ZoomMovement::OnEdge => {
|
// Focal point is the cursor position
|
||||||
if !zoomed_output_geometry
|
output_state_ref.focal_point = cursor_position;
|
||||||
.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::Centered => {
|
ZoomMovement::OnEdge => {
|
||||||
zoomed_output_geometry.loc = cursor_position.to_global(output)
|
if is_level_change {
|
||||||
- zoomed_output_geometry.size.downscale(2.).to_point();
|
// Ensure the cursor doesn't disappear off screen
|
||||||
|
output_state_ref.focal_point = cursor_position;
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
let mut focal_point = zoomed_output_geometry
|
// Compute small margin relative to zoomed output to keep cursor within
|
||||||
.loc
|
// (can be user-configurable in the future)
|
||||||
.to_local(output)
|
let margin_size = zoomed_output_geometry.size.h * 0.02;
|
||||||
.upscale(
|
let margins = FrameExtents::new(margin_size, margin_size, margin_size, margin_size);
|
||||||
(output_geometry.size.w
|
let inner_rect = zoomed_output_geometry - margins;
|
||||||
/ (output_geometry.size.w - zoomed_output_geometry.size.w)
|
|
||||||
.max(f64::EPSILON))
|
if inner_rect.contains(cursor_position) {
|
||||||
.max(1.),
|
// Do not move if cursor within margins
|
||||||
)
|
return;
|
||||||
.to_global(output);
|
}
|
||||||
|
|
||||||
|
// 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(
|
focal_point.x = focal_point.x.clamp(
|
||||||
output_geometry.loc.x,
|
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(
|
focal_point.y = focal_point.y.clamp(
|
||||||
output_geometry.loc.y,
|
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 {
|
||||||
|
// Prevent focal point jumping to top-left corner (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)));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
@ -382,14 +382,13 @@ impl ZoomState {
|
||||||
None
|
None
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn render<R, C>(renderer: &mut R, output: &Output) -> Vec<C>
|
pub fn render<R>(renderer: &mut R, output: &Output, push: &mut dyn FnMut(IcedRenderElement<R>))
|
||||||
where
|
where
|
||||||
C: From<<IcedElement<ZoomProgram> as AsRenderElements<R>>::RenderElement>,
|
R: AsGlowRenderer + ImportMem,
|
||||||
R: Renderer + ImportMem,
|
|
||||||
R::TextureId: Send + Clone + 'static,
|
R::TextureId: Send + Clone + 'static,
|
||||||
{
|
{
|
||||||
let output_state = output.user_data().get::<Mutex<OutputZoomState>>().unwrap();
|
let output_state = output.user_data().get::<Mutex<OutputZoomState>>().unwrap();
|
||||||
output_state.lock().unwrap().render(renderer, output)
|
output_state.lock().unwrap().render(renderer, output, push)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -506,6 +505,7 @@ impl Program for ZoomProgram {
|
||||||
.class(theme::Container::custom(|theme| {
|
.class(theme::Container::custom(|theme| {
|
||||||
let cosmic = theme.cosmic();
|
let cosmic = theme.cosmic();
|
||||||
let component = &cosmic.background(false).component;
|
let component = &cosmic.background(false).component;
|
||||||
|
let component = &cosmic.background(false).component;
|
||||||
iced_widget::container::Style {
|
iced_widget::container::Style {
|
||||||
snap: true,
|
snap: true,
|
||||||
icon_color: Some(component.on.into()),
|
icon_color: Some(component.on.into()),
|
||||||
|
|
@ -581,6 +581,8 @@ impl Program for ZoomProgram {
|
||||||
let level = output_state_ref.level;
|
let level = output_state_ref.level;
|
||||||
std::mem::drop(output_state_ref);
|
std::mem::drop(output_state_ref);
|
||||||
|
|
||||||
|
let mut theme = state.common.theme.clone();
|
||||||
|
theme.transparent = theme.cosmic().frosted_system_interface;
|
||||||
let grab = MenuGrab::new(
|
let grab = MenuGrab::new(
|
||||||
start_data,
|
start_data,
|
||||||
&seat,
|
&seat,
|
||||||
|
|
@ -693,7 +695,7 @@ impl Program for ZoomProgram {
|
||||||
),
|
),
|
||||||
Some(level.min(4.)),
|
Some(level.min(4.)),
|
||||||
state.common.event_loop_handle.clone(),
|
state.common.event_loop_handle.clone(),
|
||||||
state.common.theme.clone(),
|
theme,
|
||||||
);
|
);
|
||||||
|
|
||||||
std::mem::drop(shell);
|
std::mem::drop(shell);
|
||||||
|
|
@ -746,6 +748,8 @@ impl Program for ZoomProgram {
|
||||||
let level = output_state_ref.level;
|
let level = output_state_ref.level;
|
||||||
std::mem::drop(output_state_ref);
|
std::mem::drop(output_state_ref);
|
||||||
|
|
||||||
|
let mut theme = state.common.theme.clone();
|
||||||
|
theme.transparent = theme.cosmic().frosted_system_interface;
|
||||||
let grab = MenuGrab::new(
|
let grab = MenuGrab::new(
|
||||||
start_data,
|
start_data,
|
||||||
&seat,
|
&seat,
|
||||||
|
|
@ -778,7 +782,7 @@ impl Program for ZoomProgram {
|
||||||
MenuAlignment::PREFER_CENTERED,
|
MenuAlignment::PREFER_CENTERED,
|
||||||
Some(level.min(4.)),
|
Some(level.min(4.)),
|
||||||
state.common.event_loop_handle.clone(),
|
state.common.event_loop_handle.clone(),
|
||||||
state.common.theme.clone(),
|
theme,
|
||||||
);
|
);
|
||||||
|
|
||||||
std::mem::drop(shell);
|
std::mem::drop(shell);
|
||||||
|
|
|
||||||
64
src/state.rs
64
src/state.rs
|
|
@ -19,6 +19,7 @@ use crate::{
|
||||||
corner_radius::CornerRadiusState,
|
corner_radius::CornerRadiusState,
|
||||||
drm::WlDrmState,
|
drm::WlDrmState,
|
||||||
image_capture_source::CosmicImageCaptureSourceState,
|
image_capture_source::CosmicImageCaptureSourceState,
|
||||||
|
keyboard_layout::KeyboardLayoutState,
|
||||||
output_configuration::OutputConfigurationState,
|
output_configuration::OutputConfigurationState,
|
||||||
output_power::OutputPowerState,
|
output_power::OutputPowerState,
|
||||||
overlap_notify::OverlapNotifyState,
|
overlap_notify::OverlapNotifyState,
|
||||||
|
|
@ -72,6 +73,7 @@ use smithay::{
|
||||||
utils::{Clock, Monotonic, Point},
|
utils::{Clock, Monotonic, Point},
|
||||||
wayland::{
|
wayland::{
|
||||||
alpha_modifier::AlphaModifierState,
|
alpha_modifier::AlphaModifierState,
|
||||||
|
background_effect::BackgroundEffectState,
|
||||||
compositor::{CompositorClientState, CompositorState, SurfaceData},
|
compositor::{CompositorClientState, CompositorState, SurfaceData},
|
||||||
cursor_shape::CursorShapeManagerState,
|
cursor_shape::CursorShapeManagerState,
|
||||||
dmabuf::{DmabufFeedback, DmabufGlobal, DmabufState},
|
dmabuf::{DmabufFeedback, DmabufGlobal, DmabufState},
|
||||||
|
|
@ -125,7 +127,7 @@ use std::{
|
||||||
cmp::min,
|
cmp::min,
|
||||||
collections::HashSet,
|
collections::HashSet,
|
||||||
ffi::OsString,
|
ffi::OsString,
|
||||||
process::Child,
|
process::{Child, Command},
|
||||||
sync::{Arc, LazyLock, Once, atomic::AtomicBool},
|
sync::{Arc, LazyLock, Once, atomic::AtomicBool},
|
||||||
time::{Duration, Instant},
|
time::{Duration, Instant},
|
||||||
};
|
};
|
||||||
|
|
@ -223,6 +225,7 @@ pub struct State {
|
||||||
pub common: Common,
|
pub common: Common,
|
||||||
pub ready: Once,
|
pub ready: Once,
|
||||||
pub last_refresh: LastRefresh,
|
pub last_refresh: LastRefresh,
|
||||||
|
pub kiosk_command: Option<Command>,
|
||||||
}
|
}
|
||||||
smithay::delegate_dispatch2!(State);
|
smithay::delegate_dispatch2!(State);
|
||||||
|
|
||||||
|
|
@ -279,6 +282,8 @@ pub struct Common {
|
||||||
pub overlap_notify_state: OverlapNotifyState,
|
pub overlap_notify_state: OverlapNotifyState,
|
||||||
pub a11y_state: A11yState,
|
pub a11y_state: A11yState,
|
||||||
pub dbus_state: DBusState,
|
pub dbus_state: DBusState,
|
||||||
|
pub keyboard_layout_state: KeyboardLayoutState,
|
||||||
|
pub background_effect_state: BackgroundEffectState,
|
||||||
|
|
||||||
// shell-related wayland state
|
// shell-related wayland state
|
||||||
pub xdg_shell_state: XdgShellState,
|
pub xdg_shell_state: XdgShellState,
|
||||||
|
|
@ -629,6 +634,7 @@ impl State {
|
||||||
handle: LoopHandle<'static, State>,
|
handle: LoopHandle<'static, State>,
|
||||||
signal: LoopSignal,
|
signal: LoopSignal,
|
||||||
with_xwayland: bool,
|
with_xwayland: bool,
|
||||||
|
kiosk_command: Option<Command>,
|
||||||
) -> State {
|
) -> State {
|
||||||
let requested_languages = DesktopLanguageRequester::requested_languages();
|
let requested_languages = DesktopLanguageRequester::requested_languages();
|
||||||
i18n_embed::select(&*LANG_LOADER, &Localizations, &requested_languages)
|
i18n_embed::select(&*LANG_LOADER, &Localizations, &requested_languages)
|
||||||
|
|
@ -682,6 +688,9 @@ impl State {
|
||||||
AlphaModifierState::new::<Self>(dh);
|
AlphaModifierState::new::<Self>(dh);
|
||||||
SinglePixelBufferState::new::<Self>(dh);
|
SinglePixelBufferState::new::<Self>(dh);
|
||||||
FixesState::new::<Self>(dh);
|
FixesState::new::<Self>(dh);
|
||||||
|
let keyboard_layout_state = KeyboardLayoutState::new::<State, _>(dh, client_not_sandboxed);
|
||||||
|
|
||||||
|
let background_effect_state = BackgroundEffectState::new::<Self>(dh);
|
||||||
|
|
||||||
let idle_notifier_state = IdleNotifierState::<Self>::new(dh, handle.clone());
|
let idle_notifier_state = IdleNotifierState::<Self>::new(dh, handle.clone());
|
||||||
let idle_inhibit_manager_state = IdleInhibitManagerState::new::<State>(dh);
|
let idle_inhibit_manager_state = IdleInhibitManagerState::new::<State>(dh);
|
||||||
|
|
@ -786,12 +795,14 @@ impl State {
|
||||||
xdg_activation_state,
|
xdg_activation_state,
|
||||||
xdg_foreign_state,
|
xdg_foreign_state,
|
||||||
workspace_state,
|
workspace_state,
|
||||||
|
background_effect_state,
|
||||||
a11y_state,
|
a11y_state,
|
||||||
xwayland_scale: None,
|
xwayland_scale: None,
|
||||||
xwayland_state: None,
|
xwayland_state: None,
|
||||||
xwayland_shell_state,
|
xwayland_shell_state,
|
||||||
pointer_focus_state: None,
|
pointer_focus_state: None,
|
||||||
dbus_state,
|
dbus_state,
|
||||||
|
keyboard_layout_state,
|
||||||
|
|
||||||
#[cfg(feature = "logind")]
|
#[cfg(feature = "logind")]
|
||||||
inhibit_lid_fd: None,
|
inhibit_lid_fd: None,
|
||||||
|
|
@ -801,6 +812,7 @@ impl State {
|
||||||
backend: BackendData::Unset,
|
backend: BackendData::Unset,
|
||||||
ready: Once::new(),
|
ready: Once::new(),
|
||||||
last_refresh: LastRefresh::None,
|
last_refresh: LastRefresh::None,
|
||||||
|
kiosk_command,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -930,19 +942,22 @@ impl Common {
|
||||||
render_element_states: &RenderElementStates,
|
render_element_states: &RenderElementStates,
|
||||||
) {
|
) {
|
||||||
let shell = self.shell.read();
|
let shell = self.shell.read();
|
||||||
let processor = |surface: &WlSurface, states: &SurfaceData| {
|
let processor = |namespace: Option<usize>| {
|
||||||
let primary_scanout_output = update_surface_primary_scanout_output(
|
move |surface: &WlSurface, states: &SurfaceData| {
|
||||||
surface,
|
let primary_scanout_output = update_surface_primary_scanout_output(
|
||||||
output,
|
surface,
|
||||||
states,
|
output,
|
||||||
None,
|
states,
|
||||||
render_element_states,
|
namespace,
|
||||||
primary_scanout_output_compare,
|
render_element_states,
|
||||||
);
|
primary_scanout_output_compare,
|
||||||
if let Some(output) = primary_scanout_output {
|
);
|
||||||
with_fractional_scale(states, |fraction_scale| {
|
if let Some(output) = primary_scanout_output {
|
||||||
fraction_scale.set_preferred_scale(output.current_scale().fractional_scale());
|
with_fractional_scale(states, |fraction_scale| {
|
||||||
});
|
fraction_scale
|
||||||
|
.set_preferred_scale(output.current_scale().fractional_scale());
|
||||||
|
});
|
||||||
|
}
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|
@ -950,7 +965,7 @@ impl Common {
|
||||||
if let Some(session_lock) = shell.session_lock.as_ref()
|
if let Some(session_lock) = shell.session_lock.as_ref()
|
||||||
&& let Some(lock_surface) = session_lock.surfaces.get(output)
|
&& 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
|
for seat in shell
|
||||||
|
|
@ -962,7 +977,7 @@ impl Common {
|
||||||
|
|
||||||
// cursor ...
|
// cursor ...
|
||||||
if let CursorImageStatus::Surface(wl_surface) = cursor_status {
|
if let CursorImageStatus::Surface(wl_surface) = cursor_status {
|
||||||
with_surfaces_surface_tree(&wl_surface, processor);
|
with_surfaces_surface_tree(&wl_surface, processor(None));
|
||||||
}
|
}
|
||||||
|
|
||||||
// grabs
|
// grabs
|
||||||
|
|
@ -970,12 +985,12 @@ impl Common {
|
||||||
&& let Some(grab_state) = move_grab.lock().unwrap().as_ref()
|
&& let Some(grab_state) = move_grab.lock().unwrap().as_ref()
|
||||||
{
|
{
|
||||||
for (window, _) in grab_state.element().windows() {
|
for (window, _) in grab_state.element().windows() {
|
||||||
window.with_surfaces(processor);
|
window.with_surfaces(processor(None));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
if let Some(icon) = get_dnd_icon(seat) {
|
if let Some(icon) = get_dnd_icon(seat) {
|
||||||
with_surfaces_surface_tree(&icon.surface, processor);
|
with_surfaces_surface_tree(&icon.surface, processor(None));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -983,7 +998,7 @@ impl Common {
|
||||||
for set in shell.workspaces.sets.values() {
|
for set in shell.workspaces.sets.values() {
|
||||||
set.sticky_layer.mapped().for_each(|mapped| {
|
set.sticky_layer.mapped().for_each(|mapped| {
|
||||||
for (window, _) in mapped.windows() {
|
for (window, _) in mapped.windows() {
|
||||||
window.with_surfaces(processor);
|
window.with_surfaces(processor(None));
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
@ -991,16 +1006,16 @@ impl Common {
|
||||||
// normal windows
|
// normal windows
|
||||||
for space in shell.workspaces.spaces() {
|
for space in shell.workspaces.spaces() {
|
||||||
if let Some(fs) = space.get_fullscreen(shell.seats.last_active()) {
|
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| {
|
space.mapped().for_each(|mapped| {
|
||||||
for (window, _) in mapped.windows() {
|
for (window, _) in mapped.windows() {
|
||||||
window.with_surfaces(processor);
|
window.with_surfaces(processor(None));
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
space.minimized_windows.iter().for_each(|m| {
|
space.minimized_windows.iter().for_each(|m| {
|
||||||
for window in m.windows() {
|
for window in m.windows() {
|
||||||
window.with_surfaces(processor);
|
window.with_surfaces(processor(None));
|
||||||
}
|
}
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
@ -1008,15 +1023,16 @@ impl Common {
|
||||||
// OR windows
|
// OR windows
|
||||||
shell.override_redirect_windows.iter().for_each(|or| {
|
shell.override_redirect_windows.iter().for_each(|or| {
|
||||||
if let Some(wl_surface) = or.wl_surface() {
|
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
|
// layer surfaces
|
||||||
for o in shell.outputs() {
|
for o in shell.outputs() {
|
||||||
|
let namespace = shell.workspaces.active_num(o).1;
|
||||||
let map = smithay::desktop::layer_map_for_output(o);
|
let map = smithay::desktop::layer_map_for_output(o);
|
||||||
for layer_surface in map.layers() {
|
for layer_surface in map.layers() {
|
||||||
layer_surface.with_surfaces(processor);
|
layer_surface.with_surfaces(processor(Some(namespace)));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -27,9 +27,9 @@ pub fn ready(common: &Common) {
|
||||||
),
|
),
|
||||||
Err(err) => error!(?err, "Failed to run systemctl although booted with systemd",),
|
Err(err) => error!(?err, "Failed to run systemctl although booted with systemd",),
|
||||||
};
|
};
|
||||||
|
}
|
||||||
|
|
||||||
if let Err(err) = notify(false, &[NotifyState::Ready]) {
|
if let Err(err) = notify(false, &[NotifyState::Ready]) {
|
||||||
error!(?err, "Failed to notify systemd");
|
error!(?err, "Failed to notify systemd");
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -12,15 +12,15 @@ use cosmic::{
|
||||||
iced::{
|
iced::{
|
||||||
Limits, Point as IcedPoint, Size as IcedSize, Task,
|
Limits, Point as IcedPoint, Size as IcedSize, Task,
|
||||||
advanced::{graphics::text::font_system, widget::Tree},
|
advanced::{graphics::text::font_system, widget::Tree},
|
||||||
|
core::{Color, Length, Pixels, clipboard::Null as NullClipboard, id::Id, renderer::Style},
|
||||||
event::Event,
|
event::Event,
|
||||||
futures::{FutureExt, StreamExt},
|
futures::{FutureExt, StreamExt},
|
||||||
keyboard::{Event as KeyboardEvent, Modifiers as IcedModifiers},
|
keyboard::{Event as KeyboardEvent, Modifiers as IcedModifiers},
|
||||||
mouse::{Button as MouseButton, Cursor, Event as MouseEvent, ScrollDelta},
|
mouse::{Button as MouseButton, Cursor, Event as MouseEvent, ScrollDelta},
|
||||||
|
runtime::{Action, task::into_stream},
|
||||||
touch::{Event as TouchEvent, Finger},
|
touch::{Event as TouchEvent, Finger},
|
||||||
window::Event as WindowEvent,
|
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::{
|
use iced_tiny_skia::{
|
||||||
Layer,
|
Layer,
|
||||||
|
|
@ -33,9 +33,9 @@ use smithay::{
|
||||||
allocator::Fourcc,
|
allocator::Fourcc,
|
||||||
input::{ButtonState, KeyState},
|
input::{ButtonState, KeyState},
|
||||||
renderer::{
|
renderer::{
|
||||||
ImportMem, Renderer,
|
ImportMem,
|
||||||
element::{
|
element::{
|
||||||
AsRenderElements, Kind,
|
Kind,
|
||||||
memory::{MemoryRenderBuffer, MemoryRenderBufferRenderElement},
|
memory::{MemoryRenderBuffer, MemoryRenderBufferRenderElement},
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
|
|
@ -57,13 +57,20 @@ use smithay::{
|
||||||
},
|
},
|
||||||
output::Output,
|
output::Output,
|
||||||
reexports::calloop::{self, LoopHandle, RegistrationToken, futures::Scheduler},
|
reexports::calloop::{self, LoopHandle, RegistrationToken, futures::Scheduler},
|
||||||
|
render_elements,
|
||||||
utils::{
|
utils::{
|
||||||
Buffer as BufferCoords, IsAlive, Logical, Physical, Point, Rectangle, Scale, Serial, Size,
|
Buffer as BufferCoords, IsAlive, Logical, Physical, Point, Rectangle, Scale, Serial, Size,
|
||||||
Transform,
|
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<Id> = LazyLock::new(|| Id::new("Program"));
|
static ID: LazyLock<Id> = LazyLock::new(|| Id::new("Program"));
|
||||||
|
|
||||||
|
|
@ -173,6 +180,7 @@ pub(crate) struct IcedElementInternal<P: Program + Send + 'static> {
|
||||||
outputs: HashSet<Output>,
|
outputs: HashSet<Output>,
|
||||||
buffers: HashMap<OrderedFloat<f64>, (MemoryRenderBuffer, Option<(Vec<Layer>, Color)>)>,
|
buffers: HashMap<OrderedFloat<f64>, (MemoryRenderBuffer, Option<(Vec<Layer>, Color)>)>,
|
||||||
pending_realloc: bool,
|
pending_realloc: bool,
|
||||||
|
blur: BlurState,
|
||||||
|
|
||||||
// state
|
// state
|
||||||
size: Size<i32, Logical>,
|
size: Size<i32, Logical>,
|
||||||
|
|
@ -225,6 +233,7 @@ impl<P: Program + Send + Clone + 'static> Clone for IcedElementInternal<P> {
|
||||||
outputs: self.outputs.clone(),
|
outputs: self.outputs.clone(),
|
||||||
buffers: self.buffers.clone(),
|
buffers: self.buffers.clone(),
|
||||||
pending_realloc: self.pending_realloc,
|
pending_realloc: self.pending_realloc,
|
||||||
|
blur: BlurState::default(),
|
||||||
size: self.size,
|
size: self.size,
|
||||||
last_seat: self.last_seat.clone(),
|
last_seat: self.last_seat.clone(),
|
||||||
cursor_pos: self.cursor_pos,
|
cursor_pos: self.cursor_pos,
|
||||||
|
|
@ -310,6 +319,7 @@ impl<P: Program + Send + 'static> IcedElement<P> {
|
||||||
outputs: HashSet::new(),
|
outputs: HashSet::new(),
|
||||||
buffers: HashMap::new(),
|
buffers: HashMap::new(),
|
||||||
pending_realloc: false,
|
pending_realloc: false,
|
||||||
|
blur: BlurState::default(),
|
||||||
size,
|
size,
|
||||||
cursor_pos: None,
|
cursor_pos: None,
|
||||||
last_seat,
|
last_seat,
|
||||||
|
|
@ -342,7 +352,6 @@ impl<P: Program + Send + 'static> IcedElement<P> {
|
||||||
let node = element
|
let node = element
|
||||||
.as_widget_mut()
|
.as_widget_mut()
|
||||||
.layout(
|
.layout(
|
||||||
// TODO Avoid creating a new tree here?
|
|
||||||
&mut tree,
|
&mut tree,
|
||||||
&internal.renderer,
|
&internal.renderer,
|
||||||
&Limits::new(IcedSize::ZERO, IcedSize::INFINITE)
|
&Limits::new(IcedSize::ZERO, IcedSize::INFINITE)
|
||||||
|
|
@ -386,6 +395,11 @@ impl<P: Program + Send + 'static> IcedElement<P> {
|
||||||
self.0.lock().unwrap().update(true);
|
self.0.lock().unwrap().update(true);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
pub fn with_theme<R: 'static>(&self, f: impl FnOnce(&Theme) -> R) -> R {
|
||||||
|
let guard = self.0.lock().unwrap();
|
||||||
|
f(&guard.theme)
|
||||||
|
}
|
||||||
|
|
||||||
pub fn set_theme(&self, theme: cosmic::Theme) {
|
pub fn set_theme(&self, theme: cosmic::Theme) {
|
||||||
let mut guard = self.0.lock().unwrap();
|
let mut guard = self.0.lock().unwrap();
|
||||||
guard.theme = theme.clone();
|
guard.theme = theme.clone();
|
||||||
|
|
@ -910,21 +924,20 @@ impl<P: Program + Send + 'static> SpaceElement for IcedElement<P> {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
impl<P, R> AsRenderElements<R> for IcedElement<P>
|
impl<P: Program + Send + 'static> IcedElement<P> {
|
||||||
where
|
pub fn push_render_elements<R>(
|
||||||
P: Program + Send + 'static,
|
|
||||||
R: Renderer + ImportMem,
|
|
||||||
R::TextureId: Send + Clone + 'static,
|
|
||||||
{
|
|
||||||
type RenderElement = MemoryRenderBufferRenderElement<R>;
|
|
||||||
|
|
||||||
fn render_elements<C: From<Self::RenderElement>>(
|
|
||||||
&self,
|
&self,
|
||||||
renderer: &mut R,
|
renderer: &mut R,
|
||||||
location: Point<i32, Physical>,
|
location: Point<i32, Physical>,
|
||||||
mut scale: Scale<f64>,
|
mut scale: Scale<f64>,
|
||||||
alpha: f32,
|
alpha: f32,
|
||||||
) -> Vec<C> {
|
mut radii: [u8; 4],
|
||||||
|
push_above: &mut dyn FnMut(IcedRenderElement<R>),
|
||||||
|
push_below: Option<&mut dyn FnMut(IcedRenderElement<R>)>,
|
||||||
|
) where
|
||||||
|
R: AsGlowRenderer + ImportMem,
|
||||||
|
R::TextureId: Send + Clone + 'static,
|
||||||
|
{
|
||||||
let mut internal = self.0.lock().unwrap();
|
let mut internal = self.0.lock().unwrap();
|
||||||
// makes partial borrows easier
|
// makes partial borrows easier
|
||||||
let internal_ref = &mut *internal;
|
let internal_ref = &mut *internal;
|
||||||
|
|
@ -1055,11 +1068,51 @@ where
|
||||||
Kind::Unspecified,
|
Kind::Unspecified,
|
||||||
) {
|
) {
|
||||||
Ok(buffer) => {
|
Ok(buffer) => {
|
||||||
return vec![C::from(buffer)];
|
push_above(buffer.into());
|
||||||
}
|
}
|
||||||
Err(err) => tracing::warn!("What? {:?}", err),
|
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),
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
Vec::new()
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
render_elements! {
|
||||||
|
pub IcedRenderElement<R> where R: ImportMem + AsGlowRenderer, R::TextureId: Send;
|
||||||
|
UI=MemoryRenderBufferRenderElement<R>,
|
||||||
|
Blur=BlurElement,
|
||||||
|
}
|
||||||
|
|
|
||||||
|
|
@ -1,12 +1,16 @@
|
||||||
use super::IcedProgram as Program;
|
use super::IcedProgram as Program;
|
||||||
use cosmic::iced::core::event::{self, Event};
|
use cosmic::iced::{
|
||||||
use cosmic::iced::core::mouse;
|
core::{
|
||||||
use cosmic::iced::core::renderer;
|
self as iced_core, Clipboard, Size,
|
||||||
use cosmic::iced::core::widget::operation::{self, Operation};
|
event::{self, Event},
|
||||||
use cosmic::iced::core::{Clipboard, Size};
|
mouse, renderer,
|
||||||
use cosmic::iced_core;
|
widget::operation::{self, Operation},
|
||||||
use cosmic::iced_runtime::Task;
|
},
|
||||||
use cosmic::iced_runtime::user_interface::{self, UserInterface};
|
runtime::{
|
||||||
|
Task,
|
||||||
|
user_interface::{self, UserInterface},
|
||||||
|
},
|
||||||
|
};
|
||||||
|
|
||||||
/// The execution state of a [`Program`]. It leverages caching, event
|
/// The execution state of a [`Program`]. It leverages caching, event
|
||||||
/// processing, and rendering primitive storage.
|
/// processing, and rendering primitive storage.
|
||||||
|
|
@ -128,7 +132,7 @@ where
|
||||||
messages.append(&mut self.queued_messages);
|
messages.append(&mut self.queued_messages);
|
||||||
|
|
||||||
let task = if messages.is_empty() {
|
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, ..
|
mouse_interaction, ..
|
||||||
} = state
|
} = state
|
||||||
{
|
{
|
||||||
|
|
@ -154,7 +158,7 @@ where
|
||||||
let mut user_interface =
|
let mut user_interface =
|
||||||
build_user_interface(id, &mut self.program, temp_cache, renderer, bounds);
|
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, ..
|
mouse_interaction, ..
|
||||||
} = state
|
} = state
|
||||||
{
|
{
|
||||||
|
|
|
||||||
|
|
@ -3,9 +3,7 @@ use smithay::{
|
||||||
backend::{
|
backend::{
|
||||||
allocator::Fourcc,
|
allocator::Fourcc,
|
||||||
renderer::{
|
renderer::{
|
||||||
ExportMem, ImportAll, Offscreen, Renderer,
|
ExportMem, ImportAll, Offscreen, Renderer, damage::OutputDamageTracker,
|
||||||
damage::OutputDamageTracker,
|
|
||||||
element::{AsRenderElements, surface::WaylandSurfaceRenderElement},
|
|
||||||
gles::GlesRenderbuffer,
|
gles::GlesRenderbuffer,
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
|
|
@ -16,7 +14,7 @@ use smithay::{
|
||||||
use tracing::warn;
|
use tracing::warn;
|
||||||
|
|
||||||
use crate::{
|
use crate::{
|
||||||
backend::render::RendererRef,
|
backend::render::{RendererRef, element::AsGlowRenderer},
|
||||||
shell::element::CosmicSurface,
|
shell::element::CosmicSurface,
|
||||||
state::{State, advertised_node_for_surface},
|
state::{State, advertised_node_for_surface},
|
||||||
};
|
};
|
||||||
|
|
@ -24,17 +22,24 @@ use crate::{
|
||||||
pub fn screenshot_window(state: &mut State, surface: &CosmicSurface) {
|
pub fn screenshot_window(state: &mut State, surface: &CosmicSurface) {
|
||||||
fn render_window<R>(renderer: &mut R, window: &CosmicSurface) -> anyhow::Result<()>
|
fn render_window<R>(renderer: &mut R, window: &CosmicSurface) -> anyhow::Result<()>
|
||||||
where
|
where
|
||||||
R: Renderer + ImportAll + Offscreen<GlesRenderbuffer> + ExportMem,
|
R: Renderer + ImportAll + Offscreen<GlesRenderbuffer> + ExportMem + AsGlowRenderer,
|
||||||
R::TextureId: Clone + 'static,
|
R::TextureId: Send + Clone + 'static,
|
||||||
R::Error: Send + Sync + 'static,
|
R::Error: Send + Sync + 'static,
|
||||||
{
|
{
|
||||||
let bbox = bbox_from_surface_tree(&window.wl_surface().unwrap(), (0, 0));
|
let bbox = bbox_from_surface_tree(&window.wl_surface().unwrap(), (0, 0));
|
||||||
let elements = AsRenderElements::<R>::render_elements::<WaylandSurfaceRenderElement<R>>(
|
let mut elements = Vec::new();
|
||||||
window,
|
window.push_render_elements(
|
||||||
renderer,
|
renderer,
|
||||||
(-bbox.loc.x, -bbox.loc.y).into(),
|
(-bbox.loc.x, -bbox.loc.y).into(),
|
||||||
Scale::from(1.0),
|
Scale::from(1.0),
|
||||||
1.0,
|
1.0,
|
||||||
|
None,
|
||||||
|
None,
|
||||||
|
false,
|
||||||
|
[0; 4],
|
||||||
|
0,
|
||||||
|
&mut |elem| elements.push(elem),
|
||||||
|
None,
|
||||||
);
|
);
|
||||||
|
|
||||||
// TODO: 10-bit
|
// TODO: 10-bit
|
||||||
|
|
|
||||||
59
src/wayland/handlers/background_effect.rs
Normal file
59
src/wayland/handlers/background_effect.rs
Normal file
|
|
@ -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<Vec<Rectangle<i32, Logical>>>,
|
||||||
|
}
|
||||||
|
|
||||||
|
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::<ComputedBlurRegionCachedState>();
|
||||||
|
|
||||||
|
blur_state.pending().blur_region = Some({
|
||||||
|
let (added, subtracted) = region
|
||||||
|
.rects
|
||||||
|
.iter()
|
||||||
|
.cloned()
|
||||||
|
.partition::<Vec<_>, _>(|(op, _)| matches!(op, RectangleKind::Add));
|
||||||
|
let added = added.into_iter().map(|(_, rect)| rect).collect::<Vec<_>>();
|
||||||
|
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::<ComputedBlurRegionCachedState>();
|
||||||
|
|
||||||
|
blur_state.pending().blur_region.take();
|
||||||
|
})
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
@ -314,8 +314,7 @@ impl CompositorHandler for State {
|
||||||
.then(|| state.element())
|
.then(|| state.element())
|
||||||
});
|
});
|
||||||
if let Some(window) = moved_window {
|
if let Some(window) = moved_window {
|
||||||
if window.is_stack() {
|
if let Some(stack) = window.stack_ref() {
|
||||||
let stack = window.stack_ref().unwrap();
|
|
||||||
if let Some(i) = stack.surfaces().position(|s| {
|
if let Some(i) = stack.surfaces().position(|s| {
|
||||||
s.wl_surface()
|
s.wl_surface()
|
||||||
.as_deref()
|
.as_deref()
|
||||||
|
|
|
||||||
|
|
@ -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::{
|
use crate::wayland::protocols::corner_radius::{
|
||||||
CornerRadiusData, CornerRadiusHandler, CornerRadiusState, delegate_corner_radius,
|
CacheableCorners, CacheablePadding, CornerRadiusData, CornerRadiusHandler, CornerRadiusState,
|
||||||
|
delegate_corner_radius,
|
||||||
};
|
};
|
||||||
|
|
||||||
use crate::state::State;
|
use crate::state::State;
|
||||||
|
|
@ -11,39 +13,63 @@ impl CornerRadiusHandler for State {
|
||||||
&mut self.common.corner_radius_state
|
&mut self.common.corner_radius_state
|
||||||
}
|
}
|
||||||
|
|
||||||
fn set_corner_radius(
|
fn set_corner_radius(&mut self, _data: &CornerRadiusData) {}
|
||||||
&mut self,
|
|
||||||
_: &cosmic_corner_radius_toplevel_v1::CosmicCornerRadiusToplevelV1,
|
|
||||||
data: &CornerRadiusData,
|
|
||||||
) {
|
|
||||||
if force_redraw(self, data).is_none() {
|
|
||||||
tracing::warn!("Failed to force redraw for corner radius change.");
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
fn unset_corner_radius(
|
fn unset_corner_radius(&mut self, _data: &CornerRadiusData) {}
|
||||||
&mut self,
|
|
||||||
_: &cosmic_corner_radius_toplevel_v1::CosmicCornerRadiusToplevelV1,
|
fn set_padding(&mut self, _data: &CornerRadiusData) {}
|
||||||
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<()> {
|
pub fn surface_corners(states: &SurfaceData, size: Size<i32, Logical>) -> Option<[u8; 4]> {
|
||||||
let guard = data.lock().unwrap();
|
let mut guard = states.cached_state.get::<CacheableCorners>();
|
||||||
|
|
||||||
let toplevel = guard.toplevel.upgrade().ok()?;
|
// 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?;
|
||||||
|
|
||||||
let surface = state.common.xdg_shell_state.get_toplevel(&toplevel)?;
|
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),
|
||||||
|
])
|
||||||
|
}
|
||||||
|
|
||||||
let guard = state.common.shell.read();
|
pub fn surface_padding(states: &SurfaceData, size: Size<i32, Logical>) -> Option<[i32; 4]> {
|
||||||
let output = guard.visible_output_for_surface(surface.wl_surface())?;
|
let mut guard = states.cached_state.get::<CacheablePadding>();
|
||||||
|
|
||||||
state.backend.schedule_render(output);
|
let padding = guard.current().0?;
|
||||||
Some(())
|
|
||||||
|
// guard against padding being too large
|
||||||
|
Some([
|
||||||
|
padding.top.min(size.h / 2),
|
||||||
|
padding.right.min(size.w / 2),
|
||||||
|
padding.bottom.min(size.h / 2),
|
||||||
|
padding.left.min(size.w / 2),
|
||||||
|
])
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn pad_rect(
|
||||||
|
mut rect: Rectangle<i32, Logical>,
|
||||||
|
padding: &[i32; 4],
|
||||||
|
) -> Option<Rectangle<i32, Logical>> {
|
||||||
|
rect.size.h = rect.size.h.checked_sub(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[0];
|
||||||
|
Some(rect)
|
||||||
}
|
}
|
||||||
|
|
||||||
delegate_corner_radius!(State);
|
delegate_corner_radius!(State);
|
||||||
|
|
|
||||||
|
|
@ -14,6 +14,7 @@ use smithay::{
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
desktop::space::SpaceElement,
|
desktop::space::SpaceElement,
|
||||||
|
input::{Seat, pointer::PointerHandle},
|
||||||
output::Output,
|
output::Output,
|
||||||
reexports::wayland_server::protocol::{wl_pointer::WlPointer, wl_shm::Format as ShmFormat},
|
reexports::wayland_server::protocol::{wl_pointer::WlPointer, wl_shm::Format as ShmFormat},
|
||||||
utils::{Buffer as BufferCoords, Point, Size, Transform},
|
utils::{Buffer as BufferCoords, Point, Size, Transform},
|
||||||
|
|
@ -30,7 +31,7 @@ use smithay::{
|
||||||
};
|
};
|
||||||
|
|
||||||
use crate::{
|
use crate::{
|
||||||
shell::CosmicSurface,
|
shell::{CosmicSurface, CursorGeometry, Shell},
|
||||||
state::{BackendData, State},
|
state::{BackendData, State},
|
||||||
utils::prelude::{
|
utils::prelude::{
|
||||||
OutputExt, PointExt, PointGlobalExt, PointLocalExt, RectExt, RectLocalExt, SeatExt,
|
OutputExt, PointExt, PointGlobalExt, PointLocalExt, RectExt, RectLocalExt, SeatExt,
|
||||||
|
|
@ -44,6 +45,36 @@ pub use self::render::*;
|
||||||
use self::user_data::*;
|
use self::user_data::*;
|
||||||
pub use self::user_data::{FrameHolder, ImageCopySessions, SessionData, SessionHolder};
|
pub use self::user_data::{FrameHolder, ImageCopySessions, SessionData, SessionHolder};
|
||||||
|
|
||||||
|
fn default_cursor_size() -> Size<i32, BufferCoords> {
|
||||||
|
Size::new(64, 64)
|
||||||
|
}
|
||||||
|
|
||||||
|
fn seat_for_wl_pointer<'a>(shell: &'a Shell, pointer: &WlPointer) -> Option<&'a Seat<State>> {
|
||||||
|
let pointer_handle = PointerHandle::<State>::from_resource(pointer)?;
|
||||||
|
shell
|
||||||
|
.seats
|
||||||
|
.iter()
|
||||||
|
.find(|seat| seat.get_pointer().is_some_and(|p| p == pointer_handle))
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn cursor_capture_constraints(cursor_geometry: Option<CursorGeometry>) -> 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 {
|
impl ImageCopyCaptureHandler for State {
|
||||||
fn image_copy_capture_state(&mut self) -> &mut ImageCopyCaptureState {
|
fn image_copy_capture_state(&mut self) -> &mut ImageCopyCaptureState {
|
||||||
&mut self.common.image_copy_capture_state
|
&mut self.common.image_copy_capture_state
|
||||||
|
|
@ -73,26 +104,12 @@ impl ImageCopyCaptureHandler for State {
|
||||||
fn cursor_capture_constraints(
|
fn cursor_capture_constraints(
|
||||||
&mut self,
|
&mut self,
|
||||||
_source: &ImageCaptureSource,
|
_source: &ImageCaptureSource,
|
||||||
_pointer: &WlPointer,
|
pointer: &WlPointer,
|
||||||
) -> Option<BufferConstraints> {
|
) -> Option<BufferConstraints> {
|
||||||
let size = if let Some((geometry, _)) = self
|
let shell = self.common.shell.read();
|
||||||
.common
|
let seat = seat_for_wl_pointer(&shell, pointer)?;
|
||||||
.shell
|
let cursor_geometry = seat.cursor_geometry((0.0, 0.0), self.common.clock.now());
|
||||||
.read()
|
Some(cursor_capture_constraints(cursor_geometry))
|
||||||
.seats
|
|
||||||
.last_active()
|
|
||||||
.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,
|
|
||||||
})
|
|
||||||
}
|
}
|
||||||
|
|
||||||
fn new_session(&mut self, session: Session) {
|
fn new_session(&mut self, session: Session) {
|
||||||
|
|
@ -149,20 +166,23 @@ impl ImageCopyCaptureHandler for State {
|
||||||
|
|
||||||
fn new_cursor_session(&mut self, session: CursorSession) {
|
fn new_cursor_session(&mut self, session: CursorSession) {
|
||||||
let (pointer_loc, pointer_size, hotspot) = {
|
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_size, hotspot) = if let Some(CursorGeometry { geometry, hotspot }) =
|
||||||
let pointer_loc = pointer.current_location().to_i32_round().as_global();
|
seat.cursor_geometry((0.0, 0.0), self.common.clock.now())
|
||||||
|
{
|
||||||
|
(geometry.size, hotspot)
|
||||||
|
} else {
|
||||||
|
(default_cursor_size(), Point::from((0, 0)))
|
||||||
|
};
|
||||||
|
|
||||||
let (pointer_size, hotspot) = if let Some((geometry, hotspot)) =
|
(pointer_loc, pointer_size, hotspot)
|
||||||
seat.cursor_geometry((0.0, 0.0), self.common.clock.now())
|
|
||||||
{
|
|
||||||
(geometry.size, hotspot)
|
|
||||||
} else {
|
} else {
|
||||||
(Size::from((64, 64)), Point::from((0, 0)))
|
(Point::new(0, 0), default_cursor_size(), Point::from((0, 0)))
|
||||||
};
|
}
|
||||||
|
|
||||||
(pointer_loc, pointer_size, hotspot)
|
|
||||||
};
|
};
|
||||||
|
|
||||||
session.user_data().insert_if_missing_threadsafe(|| {
|
session.user_data().insert_if_missing_threadsafe(|| {
|
||||||
|
|
@ -299,8 +319,11 @@ impl ImageCopyCaptureHandler for State {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
let seat = self.common.shell.read().seats.last_active().clone();
|
let shell = self.common.shell.read();
|
||||||
render_cursor_to_buffer(self, session, frame, &seat);
|
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) {
|
fn frame_aborted(&mut self, frame: FrameRef) {
|
||||||
|
|
|
||||||
|
|
@ -9,8 +9,7 @@ use smithay::{
|
||||||
buffer_dimensions, buffer_type,
|
buffer_dimensions, buffer_type,
|
||||||
damage::{Error as DTError, OutputDamageTracker, RenderOutputResult},
|
damage::{Error as DTError, OutputDamageTracker, RenderOutputResult},
|
||||||
element::{
|
element::{
|
||||||
AsRenderElements, RenderElement,
|
RenderElement, UnderlyingStorage,
|
||||||
surface::WaylandSurfaceRenderElement,
|
|
||||||
utils::{Relocate, RelocateRenderElement},
|
utils::{Relocate, RelocateRenderElement},
|
||||||
},
|
},
|
||||||
gles::{GlesError, GlesRenderbuffer},
|
gles::{GlesError, GlesRenderbuffer},
|
||||||
|
|
@ -21,16 +20,14 @@ use smithay::{
|
||||||
desktop::space::SpaceElement,
|
desktop::space::SpaceElement,
|
||||||
input::Seat,
|
input::Seat,
|
||||||
output::{Output, OutputNoMode},
|
output::{Output, OutputNoMode},
|
||||||
reexports::wayland_server::protocol::{wl_buffer::WlBuffer, wl_shm::Format as ShmFormat},
|
reexports::wayland_server::protocol::wl_buffer::WlBuffer,
|
||||||
utils::{
|
utils::{
|
||||||
Buffer as BufferCoords, IsAlive, Logical, Physical, Point, Rectangle, Scale, Size,
|
Buffer as BufferCoords, IsAlive, Logical, Physical, Point, Rectangle, Scale, Size,
|
||||||
Transform,
|
Transform,
|
||||||
},
|
},
|
||||||
wayland::{
|
wayland::{
|
||||||
dmabuf::get_dmabuf,
|
dmabuf::get_dmabuf,
|
||||||
image_copy_capture::{
|
image_copy_capture::{CaptureFailureReason, CursorSessionRef, Frame, SessionRef},
|
||||||
BufferConstraints, CaptureFailureReason, CursorSessionRef, Frame, SessionRef,
|
|
||||||
},
|
|
||||||
seat::WaylandFocus,
|
seat::WaylandFocus,
|
||||||
shm::{shm_format_to_fourcc, with_buffer_contents, with_buffer_contents_mut},
|
shm::{shm_format_to_fourcc, with_buffer_contents, with_buffer_contents_mut},
|
||||||
},
|
},
|
||||||
|
|
@ -40,9 +37,11 @@ use tracing::warn;
|
||||||
|
|
||||||
use crate::{
|
use crate::{
|
||||||
backend::render::{
|
backend::render::{
|
||||||
CursorMode, ElementFilter, RendererRef, cursor,
|
CursorMode, ElementFilter, RendererRef,
|
||||||
element::{AsGlowRenderer, CosmicElement, DamageElement, FromGlesError},
|
cursor::{self, CursorRenderElement},
|
||||||
|
element::{AsGlowRenderer, CosmicElement, DamageElement},
|
||||||
render_workspace,
|
render_workspace,
|
||||||
|
wayland::SurfaceRenderElement,
|
||||||
},
|
},
|
||||||
shell::{CosmicMappedRenderElement, CosmicSurface, WorkspaceRenderElement},
|
shell::{CosmicMappedRenderElement, CosmicSurface, WorkspaceRenderElement},
|
||||||
state::{Common, KmsNodes, State},
|
state::{Common, KmsNodes, State},
|
||||||
|
|
@ -55,12 +54,34 @@ 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<R, E>(
|
||||||
|
renderer: &mut R,
|
||||||
|
elements: &[E],
|
||||||
|
) -> Vec<smithay::backend::renderer::utils::Buffer>
|
||||||
|
where
|
||||||
|
R: AsGlowRenderer,
|
||||||
|
E: RenderElement<R>,
|
||||||
|
{
|
||||||
|
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 struct PendingImageCopyData {
|
||||||
pub frame: Frame,
|
pub frame: Frame,
|
||||||
pub damage: Vec<smithay::utils::Rectangle<i32, BufferCoords>>,
|
pub damage: Vec<smithay::utils::Rectangle<i32, BufferCoords>>,
|
||||||
pub sync: SyncPoint,
|
pub sync: SyncPoint,
|
||||||
|
// Hold reference so `wl_buffer` isn't released and sync point isn't signaled
|
||||||
|
// until image copy completes.
|
||||||
|
_buffers: Vec<smithay::backend::renderer::utils::Buffer>,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl PendingImageCopyData {
|
impl PendingImageCopyData {
|
||||||
|
|
@ -103,10 +124,10 @@ pub fn submit_buffer<R>(
|
||||||
transform: Transform,
|
transform: Transform,
|
||||||
damage: Option<&[Rectangle<i32, Physical>]>,
|
damage: Option<&[Rectangle<i32, Physical>]>,
|
||||||
mut sync: SyncPoint,
|
mut sync: SyncPoint,
|
||||||
|
buffers: Vec<smithay::backend::renderer::utils::Buffer>,
|
||||||
) -> Result<Option<PendingImageCopyData>, R::Error>
|
) -> Result<Option<PendingImageCopyData>, R::Error>
|
||||||
where
|
where
|
||||||
R: ExportMem,
|
R: ExportMem + AsGlowRenderer,
|
||||||
R::Error: FromGlesError,
|
|
||||||
{
|
{
|
||||||
let Some(damage) = damage else {
|
let Some(damage) = damage else {
|
||||||
frame.success(
|
frame.success(
|
||||||
|
|
@ -164,7 +185,7 @@ where
|
||||||
|
|
||||||
Ok(())
|
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)
|
.and_then(|x| x)
|
||||||
{
|
{
|
||||||
frame.fail(CaptureFailureReason::Unknown);
|
frame.fail(CaptureFailureReason::Unknown);
|
||||||
|
|
@ -182,6 +203,7 @@ where
|
||||||
})
|
})
|
||||||
.collect(),
|
.collect(),
|
||||||
sync,
|
sync,
|
||||||
|
_buffers: buffers,
|
||||||
}))
|
}))
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -194,7 +216,6 @@ pub fn render_session<F, R>(
|
||||||
) -> Result<Option<PendingImageCopyData>, DTError<R::Error>>
|
) -> Result<Option<PendingImageCopyData>, DTError<R::Error>>
|
||||||
where
|
where
|
||||||
R: AsGlowRenderer,
|
R: AsGlowRenderer,
|
||||||
R::Error: FromGlesError,
|
|
||||||
F: for<'d> FnOnce(
|
F: for<'d> FnOnce(
|
||||||
&WlBuffer,
|
&WlBuffer,
|
||||||
&mut R,
|
&mut R,
|
||||||
|
|
@ -202,7 +223,13 @@ where
|
||||||
&'d mut OutputDamageTracker,
|
&'d mut OutputDamageTracker,
|
||||||
usize,
|
usize,
|
||||||
Vec<Rectangle<i32, BufferCoords>>,
|
Vec<Rectangle<i32, BufferCoords>>,
|
||||||
) -> Result<RenderOutputResult<'d>, DTError<R::Error>>,
|
) -> Result<
|
||||||
|
(
|
||||||
|
RenderOutputResult<'d>,
|
||||||
|
Vec<smithay::backend::renderer::utils::Buffer>,
|
||||||
|
),
|
||||||
|
DTError<R::Error>,
|
||||||
|
>,
|
||||||
{
|
{
|
||||||
let mut session_user_data = session.lock().unwrap();
|
let mut session_user_data = session.lock().unwrap();
|
||||||
|
|
||||||
|
|
@ -246,30 +273,25 @@ where
|
||||||
.as_mut()
|
.as_mut()
|
||||||
.map(|(_, tex)| renderer.bind(tex).map_err(DTError::Rendering))
|
.map(|(_, tex)| renderer.bind(tex).map_err(DTError::Rendering))
|
||||||
.transpose()?;
|
.transpose()?;
|
||||||
let res = render_fn(
|
let (result, buffers) = render_fn(
|
||||||
&frame.buffer(),
|
&frame.buffer(),
|
||||||
renderer,
|
renderer,
|
||||||
fb.as_mut(),
|
fb.as_mut(),
|
||||||
dt,
|
dt,
|
||||||
age,
|
age,
|
||||||
frame.damage(),
|
frame.damage(),
|
||||||
);
|
)?;
|
||||||
|
|
||||||
match res {
|
submit_buffer(
|
||||||
Ok(result) => submit_buffer(
|
frame,
|
||||||
frame,
|
renderer,
|
||||||
renderer,
|
fb.as_mut(),
|
||||||
fb.as_mut(),
|
transform,
|
||||||
transform,
|
result.damage.map(|x| x.as_slice()),
|
||||||
result.damage.map(|x| x.as_slice()),
|
result.sync,
|
||||||
result.sync,
|
buffers,
|
||||||
)
|
)
|
||||||
.map_err(DTError::Rendering),
|
.map_err(DTError::Rendering)
|
||||||
Err(err) => {
|
|
||||||
frame.fail(CaptureFailureReason::Unknown);
|
|
||||||
Err(err)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn render_workspace_to_buffer(
|
pub fn render_workspace_to_buffer(
|
||||||
|
|
@ -317,11 +339,16 @@ pub fn render_workspace_to_buffer(
|
||||||
common: &mut Common,
|
common: &mut Common,
|
||||||
output: &Output,
|
output: &Output,
|
||||||
handle: (WorkspaceHandle, usize),
|
handle: (WorkspaceHandle, usize),
|
||||||
) -> Result<RenderOutputResult<'d>, DTError<R::Error>>
|
) -> Result<
|
||||||
|
(
|
||||||
|
RenderOutputResult<'d>,
|
||||||
|
Vec<smithay::backend::renderer::utils::Buffer>,
|
||||||
|
),
|
||||||
|
DTError<R::Error>,
|
||||||
|
>
|
||||||
where
|
where
|
||||||
R: AsGlowRenderer,
|
R: AsGlowRenderer,
|
||||||
R::TextureId: Send + Clone + 'static,
|
R::TextureId: Send + Clone + 'static,
|
||||||
R::Error: FromGlesError,
|
|
||||||
CosmicElement<R>: RenderElement<R>,
|
CosmicElement<R>: RenderElement<R>,
|
||||||
CosmicMappedRenderElement<R>: RenderElement<R>,
|
CosmicMappedRenderElement<R>: RenderElement<R>,
|
||||||
WorkspaceRenderElement<R>: RenderElement<R>,
|
WorkspaceRenderElement<R>: RenderElement<R>,
|
||||||
|
|
@ -358,7 +385,7 @@ pub fn render_workspace_to_buffer(
|
||||||
.collect()
|
.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 dmabuf = dmabuf.clone();
|
||||||
let mut fb = renderer.bind(&mut dmabuf).map_err(DTError::Rendering)?;
|
let mut fb = renderer.bind(&mut dmabuf).map_err(DTError::Rendering)?;
|
||||||
render_workspace(
|
render_workspace(
|
||||||
|
|
@ -376,8 +403,7 @@ pub fn render_workspace_to_buffer(
|
||||||
handle,
|
handle,
|
||||||
cursor_mode,
|
cursor_mode,
|
||||||
ElementFilter::ExcludeWorkspaceOverview,
|
ElementFilter::ExcludeWorkspaceOverview,
|
||||||
)
|
)?
|
||||||
.map(|res| res.0)
|
|
||||||
} else {
|
} else {
|
||||||
let target = offscreen.expect("shm buffers should have an offscreen target");
|
let target = offscreen.expect("shm buffers should have an offscreen target");
|
||||||
render_workspace(
|
render_workspace(
|
||||||
|
|
@ -395,9 +421,12 @@ pub fn render_workspace_to_buffer(
|
||||||
handle,
|
handle,
|
||||||
cursor_mode,
|
cursor_mode,
|
||||||
ElementFilter::ExcludeWorkspaceOverview,
|
ElementFilter::ExcludeWorkspaceOverview,
|
||||||
)
|
)?
|
||||||
.map(|res| res.0)
|
};
|
||||||
}
|
|
||||||
|
let buffers = render_element_buffers(renderer, &elements);
|
||||||
|
|
||||||
|
Ok((res, buffers))
|
||||||
}
|
}
|
||||||
|
|
||||||
let draw_cursor = session.draw_cursor();
|
let draw_cursor = session.draw_cursor();
|
||||||
|
|
@ -504,9 +533,10 @@ pub fn render_workspace_to_buffer(
|
||||||
}
|
}
|
||||||
|
|
||||||
smithay::render_elements! {
|
smithay::render_elements! {
|
||||||
pub WindowCaptureElement<R> where R: ImportAll + ImportMem;
|
pub WindowCaptureElement<R> where R: ImportAll + ImportMem + AsGlowRenderer, R::TextureId: Send;
|
||||||
WaylandElement=WaylandSurfaceRenderElement<R>,
|
WaylandElement=SurfaceRenderElement<R>,
|
||||||
CursorElement=RelocateRenderElement<cursor::CursorRenderElement<R>>,
|
CursorElement=RelocateRenderElement<cursor::CursorRenderElement<R>>,
|
||||||
|
DamageElement=DamageElement,
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn render_window_to_buffer(
|
pub fn render_window_to_buffer(
|
||||||
|
|
@ -550,15 +580,20 @@ pub fn render_window_to_buffer(
|
||||||
common: &mut Common,
|
common: &mut Common,
|
||||||
toplevel: &CosmicSurface,
|
toplevel: &CosmicSurface,
|
||||||
geometry: Rectangle<i32, Logical>,
|
geometry: Rectangle<i32, Logical>,
|
||||||
) -> Result<RenderOutputResult<'d>, DTError<R::Error>>
|
) -> Result<
|
||||||
|
(
|
||||||
|
RenderOutputResult<'d>,
|
||||||
|
Vec<smithay::backend::renderer::utils::Buffer>,
|
||||||
|
),
|
||||||
|
DTError<R::Error>,
|
||||||
|
>
|
||||||
where
|
where
|
||||||
R: AsGlowRenderer,
|
R: AsGlowRenderer,
|
||||||
R::TextureId: Send + Clone + 'static,
|
R::TextureId: Send + Clone + 'static,
|
||||||
R::Error: FromGlesError,
|
|
||||||
CosmicElement<R>: RenderElement<R>,
|
CosmicElement<R>: RenderElement<R>,
|
||||||
CosmicMappedRenderElement<R>: RenderElement<R>,
|
CosmicMappedRenderElement<R>: RenderElement<R>,
|
||||||
{
|
{
|
||||||
let additional_damage_elements: Vec<_> = additional_damage
|
let mut elements: Vec<_> = additional_damage
|
||||||
.into_iter()
|
.into_iter()
|
||||||
.filter_map(|rect| {
|
.filter_map(|rect| {
|
||||||
let logical_rect = rect.to_logical(
|
let logical_rect = rect.to_logical(
|
||||||
|
|
@ -569,15 +604,21 @@ pub fn render_window_to_buffer(
|
||||||
logical_rect.intersection(Rectangle::from_size(geometry.size))
|
logical_rect.intersection(Rectangle::from_size(geometry.size))
|
||||||
})
|
})
|
||||||
.map(DamageElement::new)
|
.map(DamageElement::new)
|
||||||
|
.map(WindowCaptureElement::<R>::from)
|
||||||
.collect();
|
.collect();
|
||||||
dt.damage_output(age, &additional_damage_elements)?;
|
|
||||||
|
|
||||||
let shell = common.shell.read();
|
let shell = common.shell.read();
|
||||||
|
let blur_strength = 9; // TODO
|
||||||
let seat = shell.seats.last_active().clone();
|
let seat = shell.seats.last_active().clone();
|
||||||
let pointer = seat.get_pointer().unwrap();
|
let pointer = seat.get_pointer().unwrap();
|
||||||
let pointer_loc = pointer.current_location().to_i32_round().as_global();
|
let pointer_loc = pointer.current_location().to_i32_round().as_global();
|
||||||
let mut location = None;
|
let mut location = None;
|
||||||
if let Some(element) = shell.element_for_surface(toplevel)
|
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)
|
&& element.has_active_window(toplevel)
|
||||||
&& let Some(workspace) = shell.space_for(element)
|
&& let Some(workspace) = shell.space_for(element)
|
||||||
&& let Some(geometry) = workspace.element_geometry(element)
|
&& let Some(geometry) = workspace.element_geometry(element)
|
||||||
|
|
@ -591,67 +632,80 @@ pub fn render_window_to_buffer(
|
||||||
};
|
};
|
||||||
std::mem::drop(shell);
|
std::mem::drop(shell);
|
||||||
|
|
||||||
let mut elements = Vec::new();
|
|
||||||
|
|
||||||
if let Some(location) = location {
|
if let Some(location) = location {
|
||||||
if draw_cursor {
|
if draw_cursor {
|
||||||
elements.extend(
|
cursor::draw_cursor(
|
||||||
cursor::draw_cursor(
|
renderer,
|
||||||
renderer,
|
&seat,
|
||||||
&seat,
|
location,
|
||||||
location,
|
1.0.into(),
|
||||||
1.0.into(),
|
1.0,
|
||||||
1.0,
|
common.clock.now(),
|
||||||
common.clock.now(),
|
blur_strength,
|
||||||
true,
|
true,
|
||||||
)
|
&mut |elem, hotspot| {
|
||||||
.into_iter()
|
elements.push(WindowCaptureElement::CursorElement(
|
||||||
.map(|(elem, hotspot)| {
|
RelocateRenderElement::from_element(
|
||||||
WindowCaptureElement::CursorElement(RelocateRenderElement::from_element(
|
elem,
|
||||||
elem,
|
Point::from((-hotspot.x, -hotspot.y)),
|
||||||
Point::from((-hotspot.x, -hotspot.y)),
|
Relocate::Relative,
|
||||||
Relocate::Relative,
|
),
|
||||||
))
|
));
|
||||||
}),
|
},
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
// TODO cosmic-workspaces wants to omit, but metadata cursor capture in portal should
|
// TODO cosmic-workspaces wants to omit, but metadata cursor capture in portal should
|
||||||
// still include dnd surface in window capture buffer?
|
// still include dnd surface in window capture buffer?
|
||||||
if draw_cursor && let Some(dnd_icon) = get_dnd_icon(&seat) {
|
if draw_cursor && let Some(dnd_icon) = get_dnd_icon(&seat) {
|
||||||
elements.extend(
|
cursor::draw_dnd_icon(
|
||||||
cursor::draw_dnd_icon(
|
renderer,
|
||||||
renderer,
|
&dnd_icon.surface,
|
||||||
&dnd_icon.surface,
|
(location + dnd_icon.offset.to_f64()).to_i32_round(),
|
||||||
(location + dnd_icon.offset.to_f64()).to_i32_round(),
|
1.0,
|
||||||
1.0,
|
blur_strength,
|
||||||
)
|
&mut |elem| {
|
||||||
.into_iter()
|
elements.push(
|
||||||
.map(WindowCaptureElement::from),
|
RelocateRenderElement::from_element(
|
||||||
|
CursorRenderElement::Surface(elem),
|
||||||
|
Point::new(0, 0),
|
||||||
|
Relocate::Relative,
|
||||||
|
)
|
||||||
|
.into(),
|
||||||
|
)
|
||||||
|
},
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
elements.extend(AsRenderElements::<R>::render_elements::<
|
toplevel.push_render_elements(
|
||||||
WindowCaptureElement<R>,
|
|
||||||
>(
|
|
||||||
toplevel,
|
|
||||||
renderer,
|
renderer,
|
||||||
(-geometry.loc.x, -geometry.loc.y).into(),
|
(-geometry.loc.x, -geometry.loc.y).into(),
|
||||||
Scale::from(1.0),
|
Scale::from(1.0),
|
||||||
1.0,
|
1.0,
|
||||||
));
|
None,
|
||||||
|
None,
|
||||||
|
false,
|
||||||
|
[0; 4],
|
||||||
|
blur_strength,
|
||||||
|
&mut |elem| elements.push(elem.into()),
|
||||||
|
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 dmabuf_clone = dmabuf.clone();
|
||||||
let mut fb = renderer
|
let mut fb = renderer
|
||||||
.bind(&mut dmabuf_clone)
|
.bind(&mut dmabuf_clone)
|
||||||
.map_err(DTError::Rendering)?;
|
.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 {
|
} else {
|
||||||
let fb = offscreen.expect("shm buffer should have an offscreen target");
|
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;
|
let common = &mut state.common;
|
||||||
|
|
@ -753,25 +807,17 @@ pub fn render_cursor_to_buffer(
|
||||||
seat: &Seat<State>,
|
seat: &Seat<State>,
|
||||||
) {
|
) {
|
||||||
let buffer = frame.buffer();
|
let buffer = frame.buffer();
|
||||||
let mut cursor_size = seat
|
let cursor_geometry = seat.cursor_geometry((0.0, 0.0), state.common.clock.now());
|
||||||
.cursor_geometry((0.0, 0.0), state.common.clock.now())
|
let constraints = cursor_capture_constraints(cursor_geometry);
|
||||||
.map(|(geo, _hotspot)| geo.size)
|
|
||||||
.unwrap_or_else(|| Size::from((64, 64)));
|
|
||||||
let buffer_size = buffer_dimensions(&buffer).unwrap();
|
let buffer_size = buffer_dimensions(&buffer).unwrap();
|
||||||
// Client shouldn't try to allocate 0x0 buffer
|
if buffer_size != constraints.size {
|
||||||
if cursor_size == Size::new(0, 0) {
|
session.update_constraints(constraints.clone());
|
||||||
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 let Some(data) = session.user_data().get::<SessionData>() {
|
if let Some(data) = session.user_data().get::<SessionData>() {
|
||||||
*data.lock().unwrap() = SessionUserData::new(OutputDamageTracker::new(
|
*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,
|
1.0,
|
||||||
Transform::Normal,
|
Transform::Normal,
|
||||||
));
|
));
|
||||||
|
|
@ -789,48 +835,59 @@ pub fn render_cursor_to_buffer(
|
||||||
additional_damage: Vec<Rectangle<i32, BufferCoords>>,
|
additional_damage: Vec<Rectangle<i32, BufferCoords>>,
|
||||||
common: &mut Common,
|
common: &mut Common,
|
||||||
seat: &Seat<State>,
|
seat: &Seat<State>,
|
||||||
) -> Result<RenderOutputResult<'d>, DTError<R::Error>>
|
) -> Result<
|
||||||
|
(
|
||||||
|
RenderOutputResult<'d>,
|
||||||
|
Vec<smithay::backend::renderer::utils::Buffer>,
|
||||||
|
),
|
||||||
|
DTError<R::Error>,
|
||||||
|
>
|
||||||
where
|
where
|
||||||
R: AsGlowRenderer,
|
R: AsGlowRenderer,
|
||||||
R::TextureId: Send + Clone + 'static,
|
R::TextureId: Send + Clone + 'static,
|
||||||
R::Error: FromGlesError,
|
|
||||||
CosmicElement<R>: RenderElement<R>,
|
CosmicElement<R>: RenderElement<R>,
|
||||||
CosmicMappedRenderElement<R>: RenderElement<R>,
|
CosmicMappedRenderElement<R>: RenderElement<R>,
|
||||||
{
|
{
|
||||||
let additional_damage_elements: Vec<_> = additional_damage
|
let mut elements: Vec<_> = additional_damage
|
||||||
.into_iter()
|
.into_iter()
|
||||||
.filter_map(|rect| {
|
.filter_map(|rect| {
|
||||||
let logical_rect = rect.to_logical(1, Transform::Normal, &Size::from((64, 64)));
|
let logical_rect = rect.to_logical(1, Transform::Normal, &Size::from((64, 64)));
|
||||||
logical_rect.intersection(Rectangle::from_size((64, 64).into()))
|
logical_rect.intersection(Rectangle::from_size((64, 64).into()))
|
||||||
})
|
})
|
||||||
.map(DamageElement::new)
|
.map(DamageElement::new)
|
||||||
|
.map(WindowCaptureElement::from)
|
||||||
.collect();
|
.collect();
|
||||||
dt.damage_output(age, &additional_damage_elements)?;
|
|
||||||
|
|
||||||
let elements = cursor::draw_cursor(
|
cursor::draw_cursor(
|
||||||
renderer,
|
renderer,
|
||||||
seat,
|
seat,
|
||||||
Point::from((0.0, 0.0)),
|
Point::from((0.0, 0.0)),
|
||||||
1.0.into(),
|
1.0.into(),
|
||||||
1.0,
|
1.0,
|
||||||
common.clock.now(),
|
common.clock.now(),
|
||||||
|
0,
|
||||||
true,
|
true,
|
||||||
)
|
&mut |elem, _| {
|
||||||
.into_iter()
|
elements.push(
|
||||||
.map(|(elem, _)| RelocateRenderElement::from_element(elem, (0, 0), Relocate::Relative))
|
RelocateRenderElement::from_element(elem, (0, 0), Relocate::Relative).into(),
|
||||||
.map(WindowCaptureElement::from)
|
)
|
||||||
.collect::<Vec<_>>();
|
},
|
||||||
|
);
|
||||||
|
|
||||||
if let Ok(dmabuf) = get_dmabuf(buffer) {
|
let res = if let Ok(dmabuf) = get_dmabuf(buffer) {
|
||||||
let mut dmabuf_clone = dmabuf.clone();
|
let mut dmabuf_clone = dmabuf.clone();
|
||||||
let mut fb = renderer
|
let mut fb = renderer
|
||||||
.bind(&mut dmabuf_clone)
|
.bind(&mut dmabuf_clone)
|
||||||
.map_err(DTError::Rendering)?;
|
.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 {
|
} else {
|
||||||
let fb = offscreen.expect("shm buffers should have offscreen target");
|
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;
|
let common = &mut state.common;
|
||||||
|
|
|
||||||
10
src/wayland/handlers/keyboard_layout.rs
Normal file
10
src/wayland/handlers/keyboard_layout.rs
Normal file
|
|
@ -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
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
@ -1,6 +1,7 @@
|
||||||
// SPDX-License-Identifier: GPL-3.0-only
|
// SPDX-License-Identifier: GPL-3.0-only
|
||||||
|
|
||||||
pub mod a11y;
|
pub mod a11y;
|
||||||
|
pub mod background_effect;
|
||||||
pub mod buffer;
|
pub mod buffer;
|
||||||
pub mod compositor;
|
pub mod compositor;
|
||||||
pub mod corner_radius;
|
pub mod corner_radius;
|
||||||
|
|
@ -18,6 +19,7 @@ pub mod idle_notify;
|
||||||
pub mod image_capture_source;
|
pub mod image_capture_source;
|
||||||
pub mod image_copy_capture;
|
pub mod image_copy_capture;
|
||||||
pub mod input_method;
|
pub mod input_method;
|
||||||
|
pub mod keyboard_layout;
|
||||||
pub mod keyboard_shortcuts_inhibit;
|
pub mod keyboard_shortcuts_inhibit;
|
||||||
pub mod layer_shell;
|
pub mod layer_shell;
|
||||||
pub mod output;
|
pub mod output;
|
||||||
|
|
|
||||||
|
|
@ -63,7 +63,7 @@ impl PointerConstraintsHandler for State {
|
||||||
if let Some(region) = constraint.region() {
|
if let Some(region) = constraint.region() {
|
||||||
if let Some(surface_location) = surface_location
|
if let Some(surface_location) = surface_location
|
||||||
&& let position = pointer.current_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)
|
&& region.contains(point)
|
||||||
{
|
{
|
||||||
constraint.activate();
|
constraint.activate();
|
||||||
|
|
|
||||||
|
|
@ -317,8 +317,45 @@ impl XdgShellHandler for State {
|
||||||
}
|
}
|
||||||
|
|
||||||
fn toplevel_destroyed(&mut self, surface: ToplevelSurface) {
|
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 (output, clients) = {
|
||||||
let mut shell = self.common.shell.write();
|
let mut shell = self.common.shell.write();
|
||||||
|
|
||||||
|
for seat in shell.seats.iter() {
|
||||||
|
if let Some(data) = seat.user_data().get::<PopupGrabData>() {
|
||||||
|
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();
|
let seat = shell.seats.last_active().clone();
|
||||||
|
|
||||||
// Clean up pending_windows for surfaces that were never mapped.
|
// Clean up pending_windows for surfaces that were never mapped.
|
||||||
|
|
|
||||||
|
|
@ -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_toplevel_v1::CosmicCornerRadiusToplevelV1;
|
||||||
use cosmic_protocols::corner_radius::v1::server::{
|
use cosmic_protocols::corner_radius::v1::server::{
|
||||||
cosmic_corner_radius_manager_v1, cosmic_corner_radius_toplevel_v1,
|
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::Cacheable;
|
||||||
use smithay::wayland::compositor::add_pre_commit_hook;
|
use smithay::wayland::compositor::add_pre_commit_hook;
|
||||||
use smithay::wayland::compositor::with_states;
|
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::{
|
use smithay::{
|
||||||
reexports::{
|
reexports::{
|
||||||
wayland_protocols::xdg::shell::server::xdg_toplevel::XdgToplevel,
|
wayland_protocols::xdg::shell::server::xdg_toplevel::XdgToplevel,
|
||||||
|
|
@ -18,6 +27,7 @@ use std::sync::Mutex;
|
||||||
use wayland_backend::server::GlobalId;
|
use wayland_backend::server::GlobalId;
|
||||||
|
|
||||||
type ToplevelHookId = Mutex<Option<(HookId, Weak<CosmicCornerRadiusToplevelV1>)>>;
|
type ToplevelHookId = Mutex<Option<(HookId, Weak<CosmicCornerRadiusToplevelV1>)>>;
|
||||||
|
type LayerHookId = Mutex<Option<(HookId, Weak<CosmicCornerRadiusLayerV1>)>>;
|
||||||
|
|
||||||
#[derive(Debug)]
|
#[derive(Debug)]
|
||||||
pub struct CornerRadiusState {
|
pub struct CornerRadiusState {
|
||||||
|
|
@ -33,12 +43,13 @@ impl CornerRadiusState {
|
||||||
+ Dispatch<
|
+ Dispatch<
|
||||||
cosmic_corner_radius_toplevel_v1::CosmicCornerRadiusToplevelV1,
|
cosmic_corner_radius_toplevel_v1::CosmicCornerRadiusToplevelV1,
|
||||||
CornerRadiusData,
|
CornerRadiusData,
|
||||||
> + CornerRadiusHandler
|
> + Dispatch<cosmic_corner_radius_layer_v1::CosmicCornerRadiusLayerV1, CornerRadiusData>
|
||||||
|
+ CornerRadiusHandler
|
||||||
+ 'static,
|
+ 'static,
|
||||||
{
|
{
|
||||||
let global = dh
|
let global = dh
|
||||||
.create_global::<D, cosmic_corner_radius_manager_v1::CosmicCornerRadiusManagerV1, _>(
|
.create_global::<D, cosmic_corner_radius_manager_v1::CosmicCornerRadiusManagerV1, _>(
|
||||||
1,
|
2,
|
||||||
(),
|
(),
|
||||||
);
|
);
|
||||||
CornerRadiusState {
|
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 corner_radius_state(&mut self) -> &mut CornerRadiusState;
|
||||||
fn set_corner_radius(
|
fn set_corner_radius(&mut self, data: &CornerRadiusData);
|
||||||
&mut self,
|
fn unset_corner_radius(&mut self, data: &CornerRadiusData);
|
||||||
toplevel: &cosmic_corner_radius_toplevel_v1::CosmicCornerRadiusToplevelV1,
|
fn set_padding(&mut self, data: &CornerRadiusData);
|
||||||
data: &CornerRadiusData,
|
fn unset_padding(&mut self, data: &CornerRadiusData);
|
||||||
);
|
|
||||||
fn unset_corner_radius(
|
|
||||||
&mut self,
|
|
||||||
toplevel: &cosmic_corner_radius_toplevel_v1::CosmicCornerRadiusToplevelV1,
|
|
||||||
data: &CornerRadiusData,
|
|
||||||
);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
impl<D> GlobalDispatch<cosmic_corner_radius_manager_v1::CosmicCornerRadiusManagerV1, (), D>
|
impl<D> GlobalDispatch<cosmic_corner_radius_manager_v1::CosmicCornerRadiusManagerV1, (), D>
|
||||||
|
|
@ -72,6 +77,7 @@ where
|
||||||
D: GlobalDispatch<cosmic_corner_radius_manager_v1::CosmicCornerRadiusManagerV1, ()>
|
D: GlobalDispatch<cosmic_corner_radius_manager_v1::CosmicCornerRadiusManagerV1, ()>
|
||||||
+ Dispatch<cosmic_corner_radius_manager_v1::CosmicCornerRadiusManagerV1, ()>
|
+ Dispatch<cosmic_corner_radius_manager_v1::CosmicCornerRadiusManagerV1, ()>
|
||||||
+ Dispatch<cosmic_corner_radius_toplevel_v1::CosmicCornerRadiusToplevelV1, CornerRadiusData>
|
+ Dispatch<cosmic_corner_radius_toplevel_v1::CosmicCornerRadiusToplevelV1, CornerRadiusData>
|
||||||
|
+ Dispatch<cosmic_corner_radius_layer_v1::CosmicCornerRadiusLayerV1, CornerRadiusData>
|
||||||
+ CornerRadiusHandler
|
+ CornerRadiusHandler
|
||||||
+ 'static,
|
+ 'static,
|
||||||
{
|
{
|
||||||
|
|
@ -96,6 +102,7 @@ where
|
||||||
D: GlobalDispatch<cosmic_corner_radius_manager_v1::CosmicCornerRadiusManagerV1, ()>
|
D: GlobalDispatch<cosmic_corner_radius_manager_v1::CosmicCornerRadiusManagerV1, ()>
|
||||||
+ Dispatch<cosmic_corner_radius_manager_v1::CosmicCornerRadiusManagerV1, ()>
|
+ Dispatch<cosmic_corner_radius_manager_v1::CosmicCornerRadiusManagerV1, ()>
|
||||||
+ Dispatch<cosmic_corner_radius_toplevel_v1::CosmicCornerRadiusToplevelV1, CornerRadiusData>
|
+ Dispatch<cosmic_corner_radius_toplevel_v1::CosmicCornerRadiusToplevelV1, CornerRadiusData>
|
||||||
|
+ Dispatch<cosmic_corner_radius_layer_v1::CosmicCornerRadiusLayerV1, CornerRadiusData>
|
||||||
+ CornerRadiusHandler
|
+ CornerRadiusHandler
|
||||||
+ 'static,
|
+ 'static,
|
||||||
{
|
{
|
||||||
|
|
@ -115,6 +122,71 @@ where
|
||||||
}
|
}
|
||||||
cosmic_corner_radius_manager_v1::Request::GetCornerRadius { id, toplevel } => {
|
cosmic_corner_radius_manager_v1::Request::GetCornerRadius { id, toplevel } => {
|
||||||
if let Some(surface) = state.xdg_shell_state().get_toplevel(&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::<XdgShellSurfaceUserData>()
|
||||||
|
.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::<XdgShellSurfaceUserData>()
|
||||||
|
.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 radius_exists = with_states(surface.wl_surface(), |surface_data| {
|
||||||
let hook_id = surface_data
|
let hook_id = surface_data
|
||||||
.data_map
|
.data_map
|
||||||
|
|
@ -124,63 +196,33 @@ where
|
||||||
});
|
});
|
||||||
if radius_exists.unwrap_or_default() {
|
if radius_exists.unwrap_or_default() {
|
||||||
resource.post_error(
|
resource.post_error(
|
||||||
cosmic_corner_radius_manager_v1::Error::CornerRadiusExists as u32,
|
cosmic_corner_radius_manager_v1::Error::CornerRadiusExists as u32,
|
||||||
format!("{resource:?} CosmicCornerRadiusToplevelV1 object already exists for the surface"),
|
format!(
|
||||||
);
|
"{resource:?} CosmicCornerRadiusToplevelV1 object already exists for the surface"
|
||||||
|
),
|
||||||
|
);
|
||||||
}
|
}
|
||||||
let data = Mutex::new(CornerRadiusInternal {
|
let data = Mutex::new(CornerRadiusInternal {
|
||||||
toplevel: toplevel.downgrade(),
|
surface: CornerRadiusSurface::Layer(layer.downgrade()),
|
||||||
corners: None,
|
corners: None,
|
||||||
|
padding: None,
|
||||||
});
|
});
|
||||||
let obj = data_init.init(id, data);
|
let obj = data_init.init(id, data);
|
||||||
let obj_downgrade = obj.downgrade();
|
let obj_downgrade = obj.downgrade();
|
||||||
|
|
||||||
let needs_hook = radius_exists.is_none();
|
let needs_hook = radius_exists.is_none();
|
||||||
if needs_hook {
|
if needs_hook {
|
||||||
let hook_id = add_pre_commit_hook::<D, _>(
|
let hook_id =
|
||||||
surface.wl_surface(),
|
add_pre_commit_hook::<D, _>(surface.wl_surface(), layer_radius_hook);
|
||||||
move |_, _dh, surface| {
|
|
||||||
let corner_radii_too_big = with_states(surface, |surface_data| {
|
|
||||||
let corners = *surface_data
|
|
||||||
.cached_state
|
|
||||||
.get::<CacheableCorners>()
|
|
||||||
.pending();
|
|
||||||
surface_data
|
|
||||||
.cached_state
|
|
||||||
.get::<SurfaceCachedState>()
|
|
||||||
.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"),
|
|
||||||
);
|
|
||||||
}
|
|
||||||
},
|
|
||||||
);
|
|
||||||
|
|
||||||
with_states(surface.wl_surface(), |surface_data| {
|
with_states(surface.wl_surface(), |surface_data| {
|
||||||
let hook_ids = surface_data
|
let hook_ids = surface_data
|
||||||
.data_map
|
.data_map
|
||||||
.get_or_insert_threadsafe(|| ToplevelHookId::new(None));
|
.get_or_insert_threadsafe(|| LayerHookId::new(None));
|
||||||
let mut guard = hook_ids.lock().unwrap();
|
let mut guard = hook_ids.lock().unwrap();
|
||||||
*guard = Some((hook_id, obj_downgrade));
|
*guard = Some((hook_id, obj_downgrade));
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
}
|
} // TODO: can this fail?
|
||||||
}
|
}
|
||||||
_ => unimplemented!(),
|
_ => unimplemented!(),
|
||||||
}
|
}
|
||||||
|
|
@ -197,6 +239,55 @@ where
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
fn new_xdg<D>(
|
||||||
|
wl_surface: &WlSurface,
|
||||||
|
surface: CornerRadiusSurface,
|
||||||
|
resource: &cosmic_corner_radius_manager_v1::CosmicCornerRadiusManagerV1,
|
||||||
|
data_init: &mut smithay::reexports::wayland_server::DataInit<'_, D>,
|
||||||
|
id: New<cosmic_corner_radius_toplevel_v1::CosmicCornerRadiusToplevelV1>,
|
||||||
|
) where
|
||||||
|
D: GlobalDispatch<cosmic_corner_radius_manager_v1::CosmicCornerRadiusManagerV1, ()>
|
||||||
|
+ Dispatch<cosmic_corner_radius_manager_v1::CosmicCornerRadiusManagerV1, ()>
|
||||||
|
+ Dispatch<cosmic_corner_radius_toplevel_v1::CosmicCornerRadiusToplevelV1, CornerRadiusData>
|
||||||
|
+ 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::<D, _>(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<D>
|
impl<D>
|
||||||
Dispatch<cosmic_corner_radius_toplevel_v1::CosmicCornerRadiusToplevelV1, CornerRadiusData, D>
|
Dispatch<cosmic_corner_radius_toplevel_v1::CosmicCornerRadiusToplevelV1, CornerRadiusData, D>
|
||||||
for CornerRadiusState
|
for CornerRadiusState
|
||||||
|
|
@ -221,30 +312,35 @@ where
|
||||||
let mut guard = data.lock().unwrap();
|
let mut guard = data.lock().unwrap();
|
||||||
guard.corners = None;
|
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;
|
return;
|
||||||
};
|
};
|
||||||
|
|
||||||
if let Some(surface) = state.xdg_shell_state().get_toplevel(&toplevel) {
|
with_states(&wl_surface, |surface_data| {
|
||||||
with_states(surface.wl_surface(), |surface_data| {
|
if let Some(hook_ids_mutex) = surface_data.data_map.get::<ToplevelHookId>() {
|
||||||
if let Some(hook_ids_mutex) = surface_data.data_map.get::<ToplevelHookId>()
|
let mut hook_id = hook_ids_mutex.lock().unwrap();
|
||||||
{
|
*hook_id = None;
|
||||||
let mut hook_id = hook_ids_mutex.lock().unwrap();
|
}
|
||||||
*hook_id = None;
|
|
||||||
}
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
if let Some(surface) = state.xdg_shell_state().get_toplevel(&toplevel) {
|
let mut cached = surface_data.cached_state.get::<CacheableCorners>();
|
||||||
with_states(surface.wl_surface(), |s| {
|
let pending = cached.pending();
|
||||||
let mut cached = s.cached_state.get::<CacheableCorners>();
|
*pending = CacheableCorners(None);
|
||||||
let pending = cached.pending();
|
});
|
||||||
*pending = CacheableCorners(None);
|
|
||||||
});
|
|
||||||
}
|
|
||||||
drop(guard);
|
drop(guard);
|
||||||
|
|
||||||
state.unset_corner_radius(resource, data);
|
state.unset_corner_radius(data);
|
||||||
}
|
}
|
||||||
cosmic_corner_radius_toplevel_v1::Request::SetRadius {
|
cosmic_corner_radius_toplevel_v1::Request::SetRadius {
|
||||||
top_left,
|
top_left,
|
||||||
|
|
@ -254,66 +350,388 @@ where
|
||||||
} => {
|
} => {
|
||||||
let mut guard = data.lock().unwrap();
|
let mut guard = data.lock().unwrap();
|
||||||
guard.set_corner_radius(top_left, top_right, bottom_right, bottom_left);
|
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(
|
resource.post_error(
|
||||||
cosmic_corner_radius_toplevel_v1::Error::ToplevelDestroyed as u32,
|
cosmic_corner_radius_toplevel_v1::Error::ToplevelDestroyed as u32,
|
||||||
format!("{:?} No toplevel found", resource),
|
format!("{:?} No toplevel found", resource),
|
||||||
);
|
);
|
||||||
return;
|
return;
|
||||||
};
|
};
|
||||||
|
with_states(&wl_surface, |s| {
|
||||||
if let Some(surface) = state.xdg_shell_state().get_toplevel(&toplevel) {
|
let mut cached = s.cached_state.get::<CacheableCorners>();
|
||||||
with_states(surface.wl_surface(), |s| {
|
let pending = cached.pending();
|
||||||
let mut cached = s.cached_state.get::<CacheableCorners>();
|
*pending = CacheableCorners(guard.corners);
|
||||||
let pending = cached.pending();
|
});
|
||||||
*pending = CacheableCorners(guard.corners);
|
|
||||||
});
|
|
||||||
}
|
|
||||||
drop(guard);
|
drop(guard);
|
||||||
|
|
||||||
state.set_corner_radius(resource, data);
|
state.set_corner_radius(data);
|
||||||
}
|
}
|
||||||
cosmic_corner_radius_toplevel_v1::Request::UnsetRadius => {
|
cosmic_corner_radius_toplevel_v1::Request::UnsetRadius => {
|
||||||
let mut guard = data.lock().unwrap();
|
let mut guard = data.lock().unwrap();
|
||||||
guard.corners = None;
|
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(
|
resource.post_error(
|
||||||
cosmic_corner_radius_toplevel_v1::Error::ToplevelDestroyed as u32,
|
cosmic_corner_radius_toplevel_v1::Error::ToplevelDestroyed as u32,
|
||||||
format!("{:?} No toplevel found", resource),
|
format!("{:?} No toplevel found", resource),
|
||||||
);
|
);
|
||||||
|
|
||||||
return;
|
return;
|
||||||
};
|
};
|
||||||
|
|
||||||
if let Some(surface) = state.xdg_shell_state().get_toplevel(&toplevel) {
|
with_states(&wl_surface, |s| {
|
||||||
with_states(surface.wl_surface(), |s| {
|
let mut cached = s.cached_state.get::<CacheableCorners>();
|
||||||
let mut cached = s.cached_state.get::<CacheableCorners>();
|
let pending = cached.pending();
|
||||||
let pending = cached.pending();
|
*pending = CacheableCorners(None);
|
||||||
*pending = CacheableCorners(None);
|
});
|
||||||
});
|
|
||||||
}
|
|
||||||
drop(guard);
|
drop(guard);
|
||||||
|
|
||||||
state.unset_corner_radius(resource, data);
|
state.unset_corner_radius(data);
|
||||||
}
|
}
|
||||||
_ => unimplemented!(),
|
_ => unimplemented!(),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
impl<D> Dispatch<cosmic_corner_radius_layer_v1::CosmicCornerRadiusLayerV1, CornerRadiusData, D>
|
||||||
|
for CornerRadiusState
|
||||||
|
where
|
||||||
|
D: GlobalDispatch<cosmic_corner_radius_manager_v1::CosmicCornerRadiusManagerV1, ()>
|
||||||
|
+ Dispatch<cosmic_corner_radius_manager_v1::CosmicCornerRadiusManagerV1, ()>
|
||||||
|
+ Dispatch<cosmic_corner_radius_layer_v1::CosmicCornerRadiusLayerV1, CornerRadiusData>
|
||||||
|
+ CornerRadiusHandler
|
||||||
|
+ 'static,
|
||||||
|
{
|
||||||
|
fn request(
|
||||||
|
state: &mut D,
|
||||||
|
_client: &Client,
|
||||||
|
resource: &cosmic_corner_radius_layer_v1::CosmicCornerRadiusLayerV1,
|
||||||
|
request: <cosmic_corner_radius_layer_v1::CosmicCornerRadiusLayerV1 as Resource>::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::<LayerHookId>() {
|
||||||
|
let mut hook_id = hook_ids_mutex.lock().unwrap();
|
||||||
|
*hook_id = None;
|
||||||
|
}
|
||||||
|
|
||||||
|
let mut cached = surface_data.cached_state.get::<CacheableCorners>();
|
||||||
|
let pending = cached.pending();
|
||||||
|
*pending = CacheableCorners(None);
|
||||||
|
|
||||||
|
let mut cached = surface_data.cached_state.get::<CacheablePadding>();
|
||||||
|
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::<CacheableCorners>();
|
||||||
|
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::<CacheableCorners>();
|
||||||
|
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::<CacheablePadding>();
|
||||||
|
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::<CacheablePadding>();
|
||||||
|
let pending = cached.pending();
|
||||||
|
*pending = CacheablePadding(None);
|
||||||
|
});
|
||||||
|
drop(guard);
|
||||||
|
|
||||||
|
state.unset_padding(data);
|
||||||
|
}
|
||||||
|
_ => unimplemented!(),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fn xdg_radius_hook<D: 'static>(_state: &mut D, _dh: &DisplayHandle, surface: &WlSurface) {
|
||||||
|
with_states(surface, |surface_data| {
|
||||||
|
let corners = *surface_data
|
||||||
|
.cached_state
|
||||||
|
.get::<CacheableCorners>()
|
||||||
|
.pending();
|
||||||
|
if surface_data
|
||||||
|
.cached_state
|
||||||
|
.get::<SurfaceCachedState>()
|
||||||
|
.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::<ToplevelHookId>()
|
||||||
|
{
|
||||||
|
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<i32, Logical>,
|
||||||
|
padding: &Padding,
|
||||||
|
) -> Option<Rectangle<i32, Logical>> {
|
||||||
|
rect.size.h = rect.size.h.checked_sub(padding.top)?;
|
||||||
|
rect.loc.x += padding.left;
|
||||||
|
rect.size.w = rect.size.w.checked_sub(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)
|
||||||
|
}
|
||||||
|
|
||||||
|
fn layer_radius_hook<D: 'static>(_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::<CacheableCorners>()
|
||||||
|
.pending();
|
||||||
|
let padding = *surface_data
|
||||||
|
.cached_state
|
||||||
|
.get::<CacheablePadding>()
|
||||||
|
.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::<LayerHookId>() {
|
||||||
|
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::<LayerHookId>()
|
||||||
|
{
|
||||||
|
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<CornerRadiusInternal>;
|
pub type CornerRadiusData = Mutex<CornerRadiusInternal>;
|
||||||
|
|
||||||
|
#[derive(Debug)]
|
||||||
|
pub enum CornerRadiusSurface {
|
||||||
|
Toplevel(Weak<XdgToplevel>),
|
||||||
|
Popup(Weak<XdgPopup>),
|
||||||
|
Layer(Weak<ZwlrLayerSurfaceV1>),
|
||||||
|
}
|
||||||
|
|
||||||
#[derive(Debug)]
|
#[derive(Debug)]
|
||||||
pub struct CornerRadiusInternal {
|
pub struct CornerRadiusInternal {
|
||||||
pub toplevel: Weak<XdgToplevel>,
|
pub surface: CornerRadiusSurface,
|
||||||
pub corners: Option<Corners>,
|
pub corners: Option<Corners>,
|
||||||
|
pub padding: Option<Padding>,
|
||||||
}
|
}
|
||||||
|
|
||||||
#[derive(Debug, Copy, Clone)]
|
#[derive(Debug, Copy, Clone)]
|
||||||
pub struct Corners {
|
pub struct Corners {
|
||||||
pub top_left: u8,
|
pub top_left: u32,
|
||||||
pub top_right: u8,
|
pub top_right: u32,
|
||||||
pub bottom_right: u8,
|
pub bottom_right: u32,
|
||||||
pub bottom_left: u8,
|
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)]
|
#[derive(Default, Debug, Copy, Clone)]
|
||||||
|
|
@ -328,6 +746,18 @@ impl Cacheable for CacheableCorners {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[derive(Default, Debug, Copy, Clone)]
|
||||||
|
pub struct CacheablePadding(pub Option<Padding>);
|
||||||
|
|
||||||
|
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 {
|
impl CornerRadiusInternal {
|
||||||
fn set_corner_radius(
|
fn set_corner_radius(
|
||||||
&mut self,
|
&mut self,
|
||||||
|
|
@ -337,13 +767,23 @@ impl CornerRadiusInternal {
|
||||||
bottom_left: u32,
|
bottom_left: u32,
|
||||||
) {
|
) {
|
||||||
let corners = Corners {
|
let corners = Corners {
|
||||||
top_left: top_left.clamp(u8::MIN as u32, u8::MAX as u32) as u8,
|
top_left,
|
||||||
top_right: top_right.clamp(u8::MIN as u32, u8::MAX as u32) as u8,
|
top_right,
|
||||||
bottom_right: bottom_right.clamp(u8::MIN as u32, u8::MAX as u32) as u8,
|
bottom_right,
|
||||||
bottom_left: bottom_left.clamp(u8::MIN as u32, u8::MAX as u32) as u8,
|
bottom_left,
|
||||||
};
|
};
|
||||||
self.corners = Some(corners);
|
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 {
|
macro_rules! delegate_corner_radius {
|
||||||
|
|
@ -357,6 +797,9 @@ macro_rules! delegate_corner_radius {
|
||||||
smithay::reexports::wayland_server::delegate_dispatch!($(@< $( $lt $( : $clt $(+ $dlt )* )? ),+ >)? $ty: [
|
smithay::reexports::wayland_server::delegate_dispatch!($(@< $( $lt $( : $clt $(+ $dlt )* )? ),+ >)? $ty: [
|
||||||
cosmic_protocols::corner_radius::v1::server::cosmic_corner_radius_toplevel_v1::CosmicCornerRadiusToplevelV1: CornerRadiusData
|
cosmic_protocols::corner_radius::v1::server::cosmic_corner_radius_toplevel_v1::CosmicCornerRadiusToplevelV1: CornerRadiusData
|
||||||
] => $crate::wayland::protocols::corner_radius::CornerRadiusState);
|
] => $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;
|
pub(crate) use delegate_corner_radius;
|
||||||
|
|
|
||||||
|
|
@ -180,7 +180,7 @@ where
|
||||||
Modifier::Invalid,
|
Modifier::Invalid,
|
||||||
DmabufFlags::empty(),
|
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() {
|
match dma.build() {
|
||||||
Some(dmabuf) => {
|
Some(dmabuf) => {
|
||||||
match state.dmabuf_imported(&data.dmabuf_global, dmabuf.clone()) {
|
match state.dmabuf_imported(&data.dmabuf_global, dmabuf.clone()) {
|
||||||
|
|
|
||||||
185
src/wayland/protocols/keyboard_layout.rs
Normal file
185
src/wayland/protocols/keyboard_layout.rs
Normal file
|
|
@ -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<Layout>)>,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl KeyboardLayoutState {
|
||||||
|
pub fn new<D, F>(dh: &DisplayHandle, client_filter: F) -> Self
|
||||||
|
where
|
||||||
|
D: GlobalDispatch<ZcosmicKeyboardLayoutManagerV1, LayoutGlobalData> + 'static,
|
||||||
|
F: for<'a> Fn(&'a Client) -> bool + Send + Sync + 'static,
|
||||||
|
{
|
||||||
|
let global = dh.create_global::<D, ZcosmicKeyboardLayoutManagerV1, _>(
|
||||||
|
1,
|
||||||
|
LayoutGlobalData {
|
||||||
|
filter: Box::new(client_filter),
|
||||||
|
},
|
||||||
|
);
|
||||||
|
KeyboardLayoutState {
|
||||||
|
global,
|
||||||
|
keyboard_layouts: Vec::new(),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn refresh<D>(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::<LayoutUserData<D>>()
|
||||||
|
&& 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<dyn for<'a> Fn(&'a Client) -> bool + Send + Sync>,
|
||||||
|
}
|
||||||
|
|
||||||
|
struct LayoutManagerData;
|
||||||
|
|
||||||
|
impl<D> GlobalDispatch2<ZcosmicKeyboardLayoutManagerV1, D> for LayoutGlobalData
|
||||||
|
where
|
||||||
|
D: Dispatch<ZcosmicKeyboardLayoutManagerV1, LayoutManagerData> + 'static,
|
||||||
|
{
|
||||||
|
fn bind(
|
||||||
|
&self,
|
||||||
|
_state: &mut D,
|
||||||
|
_handle: &DisplayHandle,
|
||||||
|
_client: &Client,
|
||||||
|
resource: New<ZcosmicKeyboardLayoutManagerV1>,
|
||||||
|
data_init: &mut DataInit<'_, D>,
|
||||||
|
) {
|
||||||
|
data_init.init(resource, LayoutManagerData);
|
||||||
|
}
|
||||||
|
|
||||||
|
fn can_view(&self, client: &Client) -> bool {
|
||||||
|
(self.filter)(client)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl<D> Dispatch2<ZcosmicKeyboardLayoutManagerV1, D> for LayoutManagerData
|
||||||
|
where
|
||||||
|
D: Dispatch<ZcosmicKeyboardLayoutV1, LayoutUserData<D>>,
|
||||||
|
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::<D>::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<D: SeatHandler> {
|
||||||
|
handle: Option<KeyboardHandle<D>>,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl<D> Dispatch2<ZcosmicKeyboardLayoutV1, D> for LayoutUserData<D>
|
||||||
|
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);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
@ -4,6 +4,7 @@ pub mod a11y;
|
||||||
pub mod corner_radius;
|
pub mod corner_radius;
|
||||||
pub mod drm;
|
pub mod drm;
|
||||||
pub mod image_capture_source;
|
pub mod image_capture_source;
|
||||||
|
pub mod keyboard_layout;
|
||||||
pub mod output_configuration;
|
pub mod output_configuration;
|
||||||
pub mod output_power;
|
pub mod output_power;
|
||||||
pub mod overlap_notify;
|
pub mod overlap_notify;
|
||||||
|
|
|
||||||
|
|
@ -807,9 +807,17 @@ impl XwmHandler for State {
|
||||||
*context,
|
*context,
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
if !shell.pending_windows.iter().any(|w| w.surface == window) {
|
let fullscreen = window.is_fullscreen().then(|| seat.active_output());
|
||||||
let fullscreen = window.is_fullscreen().then(|| seat.active_output());
|
let maximized = window.is_maximized();
|
||||||
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);
|
let surface = CosmicSurface::from(window);
|
||||||
shell.pending_windows.push(PendingWindow {
|
shell.pending_windows.push(PendingWindow {
|
||||||
surface,
|
surface,
|
||||||
|
|
|
||||||
Loading…
Add table
Add a link
Reference in a new issue