Compare commits

..

7 commits

62 changed files with 2441 additions and 5098 deletions

28
Cargo.lock generated
View file

@ -866,7 +866,7 @@ checksum = "fe6d2e5af09e8c8ad56c969f2157a3d4238cebc7c55f0a517728c38f7b200f81"
dependencies = [ dependencies = [
"serde", "serde",
"termcolor", "termcolor",
"unicode-width 0.2.2", "unicode-width 0.1.14",
] ]
[[package]] [[package]]
@ -989,6 +989,7 @@ 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",
@ -1000,16 +1001,14 @@ 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", "libcosmic-yoda",
"libdisplay-info", "libdisplay-info",
"libsystemd", "libsystemd",
"log-panics", "log-panics",
@ -1026,7 +1025,6 @@ 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",
@ -2162,12 +2160,6 @@ 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"
@ -2482,7 +2474,7 @@ dependencies = [
"bytes", "bytes",
"cosmic-client-toolkit", "cosmic-client-toolkit",
"dnd", "dnd",
"glam 0.25.0", "glam",
"lilt", "lilt",
"log", "log",
"mime 0.1.0", "mime 0.1.0",
@ -2598,7 +2590,7 @@ dependencies = [
"cosmic-client-toolkit", "cosmic-client-toolkit",
"cryoglyph", "cryoglyph",
"futures", "futures",
"glam 0.25.0", "glam",
"guillotiere", "guillotiere",
"iced_debug", "iced_debug",
"iced_graphics", "iced_graphics",
@ -3139,7 +3131,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "68ab91017fe16c622486840e4c83c9a37afeff978bd239b5293d61ece587de66" checksum = "68ab91017fe16c622486840e4c83c9a37afeff978bd239b5293d61ece587de66"
[[package]] [[package]]
name = "libcosmic" name = "libcosmic-yoda"
version = "0.1.0-yoda.2" version = "0.1.0-yoda.2"
dependencies = [ dependencies = [
"apply", "apply",
@ -5257,14 +5249,14 @@ dependencies = [
[[package]] [[package]]
name = "smallvec" name = "smallvec"
version = "1.15.2" version = "1.15.1"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "8ed6a63f02c8539c91a8685a86f4099661ba3da017932f6ebbea6de3f0fa7c90" checksum = "67b1b7a3b5fe4f1376887184045fcf45c69e92af734b7aaddc05fb777b6fbd03"
[[package]] [[package]]
name = "smithay" name = "smithay"
version = "0.7.0" version = "0.7.0"
source = "git+https://github.com/smithay/smithay.git?rev=1ed69cb#1ed69cb55e9d0e9f38887c3b7d53d24365c4b14f" source = "git+https://github.com/smithay/smithay.git?rev=8eb4076#8eb4076cad8705c56f5e3bac8577b83576b6ce76"
dependencies = [ dependencies = [
"aliasable", "aliasable",
"appendlist", "appendlist",
@ -5273,6 +5265,7 @@ dependencies = [
"bitflags 2.11.0", "bitflags 2.11.0",
"calloop", "calloop",
"cc", "cc",
"cgmath",
"cursor-icon", "cursor-icon",
"downcast-rs", "downcast-rs",
"drm", "drm",
@ -5282,7 +5275,6 @@ 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",

View file

@ -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.93" rust-version = "1.90"
[workspace] [workspace]
members = ["cosmic-comp-config"] members = ["cosmic-comp-config"]
@ -40,10 +40,9 @@ 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", path = "../libcosmic", default-features = false, features = [ cosmic = { package = "libcosmic-yoda", path = "../libcosmic", default-features = false, features = [
"tokio", "tokio",
"wayland", "wayland",
"multi-window", "multi-window",
@ -88,8 +87,7 @@ 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"
glam = { version = "0.33", default-features = false, features = ["std"] } cgmath = "0.18.0"
smallvec = "1.15.2"
[dependencies.id_tree] [dependencies.id_tree]
branch = "feature/copy_clone" branch = "feature/copy_clone"
@ -145,7 +143,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" }
@ -160,21 +158,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 = "1ed69cb" } smithay = { git = "https://github.com/smithay/smithay.git", rev = "8eb4076" }

View file

@ -23,7 +23,7 @@ struct Circle {
color: Color, color: Color,
} }
impl<Message, Theme, Renderer: iced_graphics::geometry::Renderer> impl<Message, Theme, Renderer: cosmic::iced_renderer::geometry::Renderer>
canvas::Program<Message, Theme, Renderer> for Circle canvas::Program<Message, Theme, Renderer> for Circle
{ {
type State = (); type State = ();
@ -50,16 +50,16 @@ 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()
widget::column([canvas(Circle { .push(
widget::column()
.push(canvas(Circle {
radius: (self.height as f32 / 2.) * 0.8, radius: (self.height as f32 / 2.) * 0.8,
color: Color::from_rgba(1.0, 0.0, 0.0, 1.0), color: Color::from_rgba(1.0, 0.0, 0.0, 1.0),
}) }))
.into()]) .width(self.height as f32),
.width(self.height as f32) )
.into(), .push(orig)
orig,
])
.into() .into()
} }
} }

View file

@ -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

View file

@ -1,28 +0,0 @@
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

View file

@ -943,7 +943,6 @@ 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(),

View file

@ -978,7 +978,6 @@ impl KmsGuard<'_> {
output, output,
CursorMode::All, CursorMode::All,
None, None,
None,
) )
.with_context(|| "Failed to render outputs")?; .with_context(|| "Failed to render outputs")?;
@ -1089,7 +1088,6 @@ impl KmsGuard<'_> {
output, output,
CursorMode::All, CursorMode::All,
None, None,
None,
) )
.with_context(|| "Failed to render outputs")?; .with_context(|| "Failed to render outputs")?;
@ -1134,7 +1132,6 @@ impl KmsGuard<'_> {
output, output,
CursorMode::All, CursorMode::All,
None, None,
None,
) )
.with_context(|| "Failed to render outputs")?; .with_context(|| "Failed to render outputs")?;

View file

@ -12,9 +12,10 @@ 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, GraphicsApi}, multigpu::{ApiDevice, Error as MultiError, GraphicsApi},
}, },
}, },
reexports::drm::control::Device, reexports::drm::control::Device,
@ -31,6 +32,8 @@ 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 {
@ -210,3 +213,15 @@ 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)
}
}

View file

@ -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()

View file

@ -1068,7 +1068,6 @@ 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)
@ -1674,7 +1673,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<'a>>], elements: &[CosmicElement<GlMultiRenderer>],
(session, frame, res): ( (session, frame, res): (
&ScreencopySessionRef, &ScreencopySessionRef,
ScreencopyFrame, ScreencopyFrame,
@ -1855,8 +1854,6 @@ 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

View file

@ -2,11 +2,11 @@
use std::borrow::{Borrow, BorrowMut}; use std::borrow::{Borrow, BorrowMut};
use glam::{Affine2, Mat3, Vec2}; use cgmath::{Matrix3, Vector2};
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, Renderer, ImportAll, ImportMem, 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,7 +35,10 @@ impl ClippingShader {
} }
#[derive(Debug)] #[derive(Debug)]
pub struct ClippedSurfaceRenderElement<R: Renderer> { pub struct ClippedSurfaceRenderElement<R>
where
R: Renderer + ImportAll + ImportMem,
{
inner: WaylandSurfaceRenderElement<R>, inner: WaylandSurfaceRenderElement<R>,
program: GlesTexProgram, program: GlesTexProgram,
radius: [u8; 4], radius: [u8; 4],
@ -45,7 +48,7 @@ pub struct ClippedSurfaceRenderElement<R: Renderer> {
impl<R> ClippedSurfaceRenderElement<R> impl<R> ClippedSurfaceRenderElement<R>
where where
R: Renderer + ImportAll, R: Renderer + ImportAll + ImportMem,
{ {
pub fn new( pub fn new(
renderer: &mut R, renderer: &mut R,
@ -63,18 +66,18 @@ where
let view = elem.view(); let view = elem.view();
let transform = elem.transform(); let transform = elem.transform();
let transform_matrix = Affine2::from_translation(Vec2::new(0.5, 0.5)) let transform_matrix = Matrix3::<f32>::from_translation(Vector2::new(0.5, 0.5))
* transform.matrix() * transform.matrix()
* Affine2::from_translation(-Vec2::new(0.5, 0.5)); * Matrix3::<f32>::from_translation(-Vector2::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();
Affine2::from_scale(Vec2::new(x as f32, y as f32)) Matrix3::from_nonuniform_scale(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();
Affine2::from_translation(Vec2::new( Matrix3::from_translation(Vector2::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,
)) ))
@ -82,17 +85,16 @@ 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();
Affine2::from_scale(Vec2::new(x as f32, y as f32)) Matrix3::from_nonuniform_scale(x as f32, y as f32)
}; };
let buf_translation = Affine2::from_translation(Vec2::new( let buf_translation = Matrix3::from_translation(Vector2::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 = Mat3::from( let input_to_geo =
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)),
@ -112,7 +114,6 @@ where
transpose: false, transpose: false,
}, },
), ),
Uniform::new("noise", UniformValue::_1f(0.0)),
]; ];
Self { Self {
@ -173,8 +174,7 @@ where
impl<R> Element for ClippedSurfaceRenderElement<R> impl<R> Element for ClippedSurfaceRenderElement<R>
where where
R: Renderer + ImportAll + AsGlowRenderer, R: Renderer + ImportAll + ImportMem,
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, R: AsGlowRenderer + Renderer + ImportAll + ImportMem,
R::TextureId: 'static, R::TextureId: 'static,
{ {
fn draw( fn draw(

View file

@ -1,13 +1,6 @@
// SPDX-License-Identifier: GPL-3.0-only // SPDX-License-Identifier: GPL-3.0-only
use crate::{ use crate::{utils::prelude::*, wayland::handlers::compositor::FRAME_TIME_FILTER};
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,
@ -16,10 +9,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},
@ -121,63 +114,24 @@ 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 shape_name = shape.to_string(); let icon_path = theme
.load_icon(&shape.to_string())
for name in cursor_aliases(&shape_name) .ok_or(Error::NoDefaultCursor)?;
.iter()
.copied()
.chain(std::iter::once(shape_name.as_str()))
{
if let Some(icon_path) = theme.load_icon(name) {
let mut cursor_file = std::fs::File::open(&icon_path)?; let mut cursor_file = std::fs::File::open(&icon_path)?;
let mut cursor_data = Vec::new(); let mut cursor_data = Vec::new();
cursor_file.read_to_end(&mut cursor_data)?; cursor_file.read_to_end(&mut cursor_data)?;
parse_xcursor(&cursor_data).ok_or(Error::Parse)
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 + AsGlowRenderer, R::TextureId: Send; pub CursorRenderElement<R> where R: ImportAll + ImportMem;
Static=MemoryRenderBufferRenderElement<R>, Static=MemoryRenderBufferRenderElement<R>,
Surface=SurfaceRenderElement<R>, Surface=WaylandSurfaceRenderElement<R>,
} }
pub fn draw_surface_cursor<R>( pub fn draw_surface_cursor<R>(
@ -185,10 +139,9 @@ 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>>,
blur_strength: usize, ) -> Vec<(CursorRenderElement<R>, Point<i32, Physical>)>
push: &mut dyn FnMut(CursorRenderElement<R>, Point<i32, Physical>), where
) where R: Renderer + ImportAll,
R: Renderer + ImportAll + AsGlowRenderer,
R::TextureId: Clone + 'static, R::TextureId: Clone + 'static,
{ {
let scale = scale.into(); let scale = scale.into();
@ -203,21 +156,17 @@ pub fn draw_surface_cursor<R>(
.to_physical_precise_round(scale) .to_physical_precise_round(scale)
}); });
push_render_elements_from_surface_tree( 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), )
None, .into_iter()
); .map(|elem| (elem, h))
.collect()
} }
#[profiling::function] #[profiling::function]
@ -226,10 +175,9 @@ 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>>,
blur_strength: usize, ) -> Vec<WaylandSurfaceRenderElement<R>>
push: &mut dyn FnMut(SurfaceRenderElement<R>), where
) where R: Renderer + ImportAll,
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") {
@ -239,21 +187,14 @@ pub fn draw_dnd_icon<R>(
); );
} }
let scale = scale.into(); let scale = scale.into();
push_render_elements_from_surface_tree( 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>;
@ -336,11 +277,10 @@ 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,
push: &mut dyn FnMut(CursorRenderElement<R>, Point<i32, Physical>), ) -> Vec<(CursorRenderElement<R>, Point<i32, Physical>)>
) where where
R: Renderer + ImportMem + ImportAll + AsGlowRenderer, R: Renderer + ImportMem + ImportAll,
R::TextureId: Send + Clone + 'static, R::TextureId: Send + Clone + 'static,
{ {
// draw the cursor as relevant // draw the cursor as relevant
@ -351,7 +291,7 @@ pub fn draw_cursor<R>(
let state = &mut *state_ref; let state = &mut *state_ref;
if state.hidden { if state.hidden {
return; return Vec::new();
} }
let named_cursor = state.current_cursor.or(match cursor_status { let named_cursor = state.current_cursor.or(match cursor_status {
@ -360,7 +300,7 @@ pub fn draw_cursor<R>(
}); });
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; return Vec::new();
} }
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;
@ -397,7 +337,7 @@ pub fn draw_cursor<R>(
); );
state.current_image = Some(frame); state.current_image = Some(frame);
push( return vec![(
CursorRenderElement::Static( CursorRenderElement::Static(
MemoryRenderBufferRenderElement::from_buffer( MemoryRenderBufferRenderElement::from_buffer(
renderer, renderer,
@ -411,9 +351,11 @@ pub fn draw_cursor<R>(
.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 {
draw_surface_cursor(renderer, wl_surface, location, scale, blur_strength, push); return draw_surface_cursor(renderer, wl_surface, location, scale);
} else {
Vec::new()
} }
} }

View file

@ -1,27 +1,20 @@
use crate::{ use crate::shell::{CosmicMappedRenderElement, WorkspaceRenderElement};
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, ContextId, ExportMem, ImportAll, ImportMem, Offscreen, Renderer, Bind, Blit, 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},
}, },
}, },
@ -35,20 +28,19 @@ use super::{GlMultiRenderer, cursor::CursorRenderElement};
pub enum CosmicElement<R> pub enum CosmicElement<R>
where where
R: AsGlowRenderer, R: AsGlowRenderer,
R::TextureId: Send + 'static, R::TextureId: '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(SurfaceRenderElement<R>), Dnd(WaylandSurfaceRenderElement<R>),
MoveGrab(RescaleRenderElement<CosmicMappedRenderElement<R>>), MoveGrab(RescaleRenderElement<CosmicMappedRenderElement<R>>),
Postprocess( Postprocess(
CropRenderElement<RelocateRenderElement<RescaleRenderElement<TextureShaderElement>>>, CropRenderElement<RelocateRenderElement<RescaleRenderElement<TextureShaderElement>>>,
), ),
Zoom(IcedRenderElement<R>), Zoom(MemoryRenderBufferRenderElement<R>),
Damage(DamageElement),
#[cfg(feature = "debug")] #[cfg(feature = "debug")]
Egui(TextureRenderElement<GlesTexture>), Egui(TextureRenderElement<GlesTexture>),
} }
@ -56,7 +48,7 @@ where
impl<R> Element for CosmicElement<R> impl<R> Element for CosmicElement<R>
where where
R: AsGlowRenderer, R: AsGlowRenderer,
R::TextureId: Send + 'static, R::TextureId: 'static,
CosmicMappedRenderElement<R>: RenderElement<R>, CosmicMappedRenderElement<R>: RenderElement<R>,
{ {
fn id(&self) -> &Id { fn id(&self) -> &Id {
@ -67,7 +59,6 @@ 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(),
} }
@ -81,7 +72,6 @@ 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(),
} }
@ -95,7 +85,6 @@ 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(),
} }
@ -109,7 +98,6 @@ 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),
} }
@ -123,7 +111,6 @@ 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),
} }
@ -137,7 +124,6 @@ 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(),
} }
@ -155,7 +141,6 @@ 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),
} }
@ -169,7 +154,6 @@ 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),
} }
@ -183,7 +167,6 @@ 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(),
} }
@ -197,7 +180,6 @@ 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(),
} }
@ -211,7 +193,6 @@ 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(),
} }
@ -221,7 +202,8 @@ where
impl<R> RenderElement<R> for CosmicElement<R> impl<R> RenderElement<R> for CosmicElement<R>
where where
R: AsGlowRenderer, R: AsGlowRenderer,
R::TextureId: Send + 'static, R::TextureId: 'static,
R::Error: FromGlesError,
CosmicMappedRenderElement<R>: RenderElement<R>, CosmicMappedRenderElement<R>: RenderElement<R>,
{ {
fn draw( fn draw(
@ -255,12 +237,9 @@ where
opaque_regions, opaque_regions,
cache, cache,
) )
.map_err(R::from_gles_error) .map_err(FromGlesError::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);
@ -273,7 +252,7 @@ where
opaque_regions, opaque_regions,
cache, cache,
) )
.map_err(R::from_gles_error) .map_err(FromGlesError::from_gles_error)
} }
} }
} }
@ -289,7 +268,6 @@ 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();
@ -315,19 +293,16 @@ where
RenderElement::<GlowRenderer>::capture_framebuffer( RenderElement::<GlowRenderer>::capture_framebuffer(
elem, glow_frame, src, dst, cache, elem, glow_frame, src, dst, cache,
) )
.map_err(R::from_gles_error) .map_err(FromGlesError::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(R::from_gles_error) .map_err(FromGlesError::from_gles_error)
} }
} }
} }
@ -337,7 +312,7 @@ impl<R> From<CropRenderElement<RescaleRenderElement<WorkspaceRenderElement<R>>>>
for CosmicElement<R> for CosmicElement<R>
where where
R: AsGlowRenderer, R: AsGlowRenderer,
R::TextureId: Send + 'static, R::TextureId: '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 {
@ -349,33 +324,22 @@ where
} }
} }
impl<R> From<IcedRenderElement<R>> for CosmicElement<R> impl<R> From<MemoryRenderBufferRenderElement<R>> for CosmicElement<R>
where where
R: AsGlowRenderer, R: AsGlowRenderer,
R::TextureId: Send + 'static, R::TextureId: 'static,
CosmicMappedRenderElement<R>: RenderElement<R>, CosmicMappedRenderElement<R>: RenderElement<R>,
{ {
fn from(value: IcedRenderElement<R>) -> Self { fn from(value: MemoryRenderBufferRenderElement<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: Send + 'static, R::TextureId: 'static,
CosmicMappedRenderElement<R>: RenderElement<R>, CosmicMappedRenderElement<R>: RenderElement<R>,
{ {
fn from(elem: TextureRenderElement<GlesTexture>) -> Self { fn from(elem: TextureRenderElement<GlesTexture>) -> Self {
@ -401,12 +365,6 @@ 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 {
@ -426,18 +384,6 @@ 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<'_> {
@ -457,18 +403,6 @@ 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 {
@ -524,3 +458,13 @@ 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
}
}

View file

@ -15,19 +15,14 @@ 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, OutputId, OverviewMode, SeatExt, Trigger, WorkspaceDelta, CosmicMappedRenderElement, 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},
@ -39,26 +34,25 @@ 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::{ image_copy_capture::{FrameHolder, SessionData, render_session},
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, Offscreen, Texture, TextureFilter, Color32F, ImportAll, Offscreen, Renderer, Texture, TextureFilter,
damage::{Error as RenderError, OutputDamageTracker, RenderOutputResult}, damage::{Error as RenderError, OutputDamageTracker, RenderOutputResult},
element::{ element::{
Element, Id, Kind, NamespacedElement, RenderElement, WeakId, Element, Id, Kind, 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,
@ -75,23 +69,22 @@ 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::{compositor::with_states, dmabuf::get_dmabuf, session_lock::LockSurface}, wayland::{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;
@ -269,7 +262,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) / scale) as f32; let thickness: f32 = ((thickness as f64 * scale).ceil() / scale) as f32;
let shader = Self::get(renderer); let shader = Self::get(renderer);
let elem = PixelShaderElement::new( let elem = PixelShaderElement::new(
@ -429,7 +422,6 @@ 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(
@ -445,7 +437,6 @@ 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
@ -463,7 +454,6 @@ 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(())
} }
@ -481,14 +471,12 @@ 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,
scanout_node: Option<DrmNode>, ) -> Vec<CosmicElement<R>>
push: &mut dyn FnMut(CosmicElement<R>), where
) where
R: AsGlowRenderer, R: AsGlowRenderer,
R::TextureId: Send + Clone + 'static, R::TextureId: Send + Clone + 'static,
CosmicMappedRenderElement<R>: RenderElement<R>, CosmicMappedRenderElement<R>: RenderElement<R>,
@ -502,6 +490,7 @@ pub fn cursor_elements<'a, 'frame, R>(
) )
}) })
.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() {
@ -511,6 +500,7 @@ pub fn cursor_elements<'a, 'frame, R>(
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,
@ -518,10 +508,11 @@ pub fn cursor_elements<'a, 'frame, R>(
scale.into(), scale.into(),
zoom_scale, zoom_scale,
now, now,
blur_strength,
mode != CursorMode::NotDefault, mode != CursorMode::NotDefault,
&mut |elem, hotspot| { )
push(CosmicElement::Cursor(RescaleRenderElement::from_element( .into_iter()
.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)),
@ -532,55 +523,63 @@ pub fn cursor_elements<'a, 'frame, R>(
.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_state) = seat if let Some(grab_elements) = 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))
{ {
grab_state.render(renderer, output, theme, scanout_node, &mut |elem| { elements.extend(grab_elements.into_iter().map(|elem| {
push(CosmicElement::MoveGrab(RescaleRenderElement::from_element( 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_state) = seat if let Some((grab_elements, should_scale)) = 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(),
)
})
{ {
let should_scale = !grab_state.is_in_screen_space(); elements.extend(grab_elements.into_iter().map(|elem| {
grab_state.render(renderer, output, &mut |elem| { CosmicElement::MoveGrab(RescaleRenderElement::from_element(
push(CosmicElement::MoveGrab(RescaleRenderElement::from_element( elem,
elem.into(),
if should_scale { if should_scale {
focal_point focal_point
.as_logical() .as_logical()
@ -590,10 +589,12 @@ pub fn cursor_elements<'a, 'frame, R>(
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"))]
@ -614,11 +615,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>,
{ {
@ -645,7 +646,7 @@ where
), ),
scale, scale,
) )
.map_err(R::from_gles_error) .map_err(FromGlesError::from_gles_error)
.map_err(RenderError::Rendering)? .map_err(RenderError::Rendering)?
.into(), .into(),
] ]
@ -689,7 +690,6 @@ 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::<CosmicElement<R>>::new(); let mut elements = Vec::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,25 +729,21 @@ 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);
cursor_elements( elements.extend(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();
@ -823,132 +819,72 @@ 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 => {
ZoomState::render(renderer, output, &mut |elem| { elements.extend(ZoomState::render(renderer, output));
elements.push(CosmicElement::Zoom(elem))
});
} }
Stage::SessionLock(lock_surface) => { Stage::SessionLock(lock_surface) => {
session_lock_elements(renderer, output, lock_surface, blur_strength, &mut |elem| { elements.extend(
elements.extend(crop_to_output(elem.into()).map(Into::into)) session_lock_elements(renderer, output, lock_surface)
}) .into_iter()
.map(Into::into)
.flat_map(crop_to_output)
.map(Into::into),
);
} }
Stage::LayerPopup { Stage::LayerPopup {
popup, popup, location, ..
location,
workspace_idx,
..
} => { } => {
let mut geometry = popup.geometry().as_global(); elements.extend(
geometry.loc += location; render_elements_from_surface_tree::<_, WorkspaceRenderElement<_>>(
let radii = with_states(popup.wl_surface(), |states| {
surface_corners(states, geometry.size.as_logical())
})
.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, renderer,
popup.wl_surface(), popup.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,
radii,
None,
blur_strength,
FRAME_TIME_FILTER, FRAME_TIME_FILTER,
&mut |elem| { )
elements.extend( .into_iter()
crop_to_output(NamespacedElement::new(elem, namespace).into()) .flat_map(crop_to_output)
.map(Into::into), .map(Into::into),
) );
},
None,
)
} }
Stage::LayerSurface { Stage::LayerSurface { layer, location } => {
layer, elements.extend(
location, render_elements_from_surface_tree::<_, WorkspaceRenderElement<_>>(
workspace_idx,
} => {
let mut geometry = layer.geometry().as_global();
geometry.loc += location;
let geometry = geometry.to_local(output).as_logical();
let padded = with_states(layer.wl_surface(), |states| {
surface_padding(states, geometry.size)
.and_then(|padding| pad_rect(geometry, &padding))
})
.unwrap_or(geometry);
let radii = with_states(layer.wl_surface(), |states| {
surface_corners(states, padded.size)
})
.unwrap_or([0; 4]);
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, renderer,
layer.wl_surface(), layer.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_f64(),
Scale::from(scale), Scale::from(scale),
1.0, 1.0,
false,
radii,
padded.to_f64(),
blur_strength,
FRAME_TIME_FILTER, FRAME_TIME_FILTER,
&mut |elem| {
elements.extend(
crop_to_output(NamespacedElement::new(elem, namespace).into())
.map(Into::into),
) )
}, .into_iter()
None, .flat_map(crop_to_output)
.map(Into::into),
); );
} }
Stage::OverrideRedirect { surface, location } => { Stage::OverrideRedirect { surface, location } => {
if let Some(wl_surface) = surface.wl_surface() { elements.extend(surface.wl_surface().into_iter().flat_map(|surface| {
let mut geometry = surface.geometry().as_global(); render_elements_from_surface_tree::<_, WorkspaceRenderElement<_>>(
geometry.loc += location;
push_render_elements_from_surface_tree(
renderer, renderer,
&wl_surface, &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)), )
None, .into_iter()
); .flat_map(crop_to_output)
} .map(Into::into)
}));
} }
Stage::StickyPopups(layout) => { Stage::StickyPopups(layout) => {
let alpha = match &overview.0 { let alpha = match &overview.0 {
@ -969,11 +905,14 @@ where
OverviewMode::None => 1.0, OverviewMode::None => 1.0,
}; };
layout.render_popups(renderer, alpha, scanout_node, &mut |elem| { elements.extend(
if let Some(elem) = crop_to_output(elem.into()) { layout
elements.push(elem.into()) .render_popups(renderer, alpha)
} .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 {
@ -998,7 +937,9 @@ 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));
layout.render( elements.extend(
layout
.render(
renderer, renderer,
current_focus.as_ref().and_then(|stack| { current_focus.as_ref().and_then(|stack| {
stack.last().and_then(|t| match t { stack.last().and_then(|t| match t {
@ -1010,37 +951,43 @@ where
active_hint, active_hint,
alpha, alpha,
theme.cosmic(), theme.cosmic(),
scanout_node, )
&mut |elem| { .into_iter()
if let Some(elem) = crop_to_output(elem.into()) { .map(Into::into)
elements.push(elem.into()) .flat_map(crop_to_output)
} .map(Into::into),
}, )
);
} }
Stage::WorkspacePopups { workspace, offset } => { Stage::WorkspacePopups { workspace, offset } => {
workspace.render_popups( elements.extend(
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| {
CosmicElement::Workspace(RelocateRenderElement::from_element(
element,
offset.to_physical_precise_round(scale), offset.to_physical_precise_round(scale),
Relocate::Relative, Relocate::Relative,
), ))
)); })
}
Err(_) => {
return ControlFlow::Break(Err(OutputNoMode));
} }
}, },
); );
} }
Stage::Workspace { workspace, offset } => { Stage::Workspace { workspace, offset } => {
workspace.render( elements.extend(
match workspace.render(
renderer, renderer,
last_active_seat, last_active_seat,
!move_active && is_active_space, !move_active && is_active_space,
@ -1048,16 +995,21 @@ where
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| {
CosmicElement::Workspace(RelocateRenderElement::from_element(
element,
offset.to_physical_precise_round(scale), offset.to_physical_precise_round(scale),
Relocate::Relative, Relocate::Relative,
), ))
)); })
}
Err(_) => {
return ControlFlow::Break(Err(OutputNoMode));
} }
}, },
); );
@ -1074,29 +1026,23 @@ fn session_lock_elements<R>(
renderer: &mut R, renderer: &mut R,
output: &Output, output: &Output,
lock_surface: Option<&LockSurface>, lock_surface: Option<&LockSurface>,
blur_strength: usize, ) -> Vec<WaylandSurfaceRenderElement<R>>
push: &mut dyn FnMut(SurfaceRenderElement<R>), where
) where R: Renderer + ImportAll,
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());
push_render_elements_from_surface_tree( 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()
} }
} }
@ -1244,6 +1190,7 @@ 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>,
@ -1507,16 +1454,11 @@ where
} }
} }
let buffers = render_element_buffers(renderer, &elements); Ok(RenderOutputResult {
Ok((
RenderOutputResult {
damage: res.0, damage: res.0,
sync, sync,
states: res.1, states: res.1,
}, })
buffers,
))
}, },
)? { )? {
pending_image_copy_data.send_success_when_ready( pending_image_copy_data.send_success_when_ready(
@ -1553,23 +1495,12 @@ 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 mut elements: Vec<CosmicElement<R>> = if let Some(additional_damage) = additional_damage { let elements: Vec<CosmicElement<R>> = workspace_elements(
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,
@ -1580,8 +1511,17 @@ 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,

View file

@ -1,30 +0,0 @@
#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;
}
}

View file

@ -1,31 +0,0 @@
#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;
}
}

View file

@ -25,7 +25,6 @@ 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;
@ -52,26 +51,15 @@ 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);
} }
float hash(vec2 p) {
vec3 p3 = fract(vec3(p.xyx) * 727.727);
p3 += dot(p3, p3.xyz + 33.33);
return fract((p3.x + p3.y) * p3.z);
}
void main() { void main() {
if (alpha <= 0.0) { vec3 coords_geo = input_to_geo * vec3(v_coords, 1.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);
@ -80,20 +68,8 @@ 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 *= alpha; color = color * alpha;
#if defined(DEBUG_FLAGS) #if defined(DEBUG_FLAGS)
if (tint == 1.0) if (tint == 1.0)

View file

@ -1,6 +1,6 @@
use std::{borrow::Borrow, cell::RefCell, collections::HashMap}; use std::{borrow::Borrow, cell::RefCell, collections::HashMap};
use glam::{Affine2, Mat3, Vec2}; use cgmath::{Matrix3, Vector2};
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 = Vec2::new(shader_geo.size.w as f32, shader_geo.size.h as f32); let area_size = Vector2::new(shader_geo.size.w, shader_geo.size.h);
let geo_loc = Vec2::new(-shader_geo.loc.x as f32, -shader_geo.loc.y as f32); let geo_loc = Vector2::new(-shader_geo.loc.x, -shader_geo.loc.y);
shader_geo.loc += offset + geo.loc; shader_geo.loc += offset + geo.loc;
let input_to_geo = Mat3::from( let input_to_geo = (Matrix3::from_nonuniform_scale(area_size.x, area_size.y)
Affine2::from_scale(area_size) * Matrix3::from_translation(Vector2::new(
* Affine2::from_translation(Vec2::new(
-geo_loc.x / area_size.x, -geo_loc.x / area_size.x,
-geo_loc.y / area_size.y, -geo_loc.y / area_size.y,
)), )))
); .cast::<f32>()
.unwrap();
let window_geo_loc = Vec2::new(window_geo.loc.x as f32, window_geo.loc.y as f32); let window_geo_loc = Vector2::new(window_geo.loc.x, window_geo.loc.y);
let window_input_to_geo = Mat3::from( let window_input_to_geo = (Matrix3::from_nonuniform_scale(area_size.x, area_size.y)
Affine2::from_scale(area_size) * Matrix3::from_translation(Vector2::new(
* Affine2::from_translation(Vec2::new(
-window_geo_loc.x / area_size.x, -window_geo_loc.x / area_size.x,
-window_geo_loc.y / area_size.y, -window_geo_loc.y / area_size.y,
)), )))
); .cast::<f32>()
.unwrap();
let element = PixelShaderElement::new( let element = PixelShaderElement::new(
shader, shader,

View file

@ -1,705 +0,0 @@
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: {:#?}", &params);
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,
&region,
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(())
}

View file

@ -1,145 +0,0 @@
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,
);
}

View file

@ -25,8 +25,7 @@ use crate::{
}, },
utils::{prelude::*, quirks::workspace_overview_is_open}, utils::{prelude::*, quirks::workspace_overview_is_open},
wayland::handlers::{ wayland::handlers::{
image_copy_capture::{SessionHolder, cursor_capture_constraints}, image_copy_capture::SessionHolder, xwayland_keyboard_grab::XWaylandGrabSeat,
xwayland_keyboard_grab::XWaylandGrabSeat,
}, },
}; };
use calloop::{ use calloop::{
@ -36,15 +35,14 @@ 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, TabletToolButtonEvent, TabletToolEvent, PointerAxisEvent, ProximityState, Switch, SwitchState, SwitchToggleEvent,
TabletToolProximityEvent, TabletToolTipEvent, TabletToolTipState, TouchEvent, TabletToolButtonEvent, TabletToolEvent, TabletToolProximityEvent, TabletToolTipEvent,
TabletToolTipState, TouchEvent,
}, },
desktop::{PopupKeyboardGrab, WindowSurfaceType, utils::under_from_surface_tree}, desktop::{PopupKeyboardGrab, WindowSurfaceType, utils::under_from_surface_tree},
input::{ input::{
@ -61,19 +59,22 @@ use smithay::{
output::Output, output::Output,
reexports::{ reexports::{
input::Device as InputDevice, input::Device as InputDevice,
wayland_server::{Resource as _, protocol::wl_surface::WlSurface}, wayland_server::{
Resource as _,
protocol::{wl_shm::Format as ShmFormat, wl_surface::WlSurface},
}, },
utils::{Clock, Logical, Monotonic, Point, Rectangle, SERIAL_COUNTER, Serial}, },
utils::{Logical, Point, Rectangle, SERIAL_COUNTER, Serial, Size},
wayland::{ wayland::{
compositor::CompositorHandler, compositor::CompositorHandler,
image_copy_capture::CursorSessionRef, image_copy_capture::{BufferConstraints, 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}; use tracing::{error, trace, warn};
use xkbcommon::xkb::{Keycode, Keysym}; use xkbcommon::xkb::{Keycode, Keysym};
use std::{ use std::{
@ -335,7 +336,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_floor(), (ptr.current_location() - *surface_loc).to_i32_round(),
) )
}) { }) {
return; return;
@ -380,25 +381,14 @@ 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(
let scale = output.current_scale().fractional_scale(); output_geometry.loc.x as f64,
let physical = output (output_geometry.loc.x + output_geometry.size.w - 1) as f64,
.current_mode() );
.map(|mode| output.current_transform().transform_size(mode.size)) position.y = position.y.clamp(
.unwrap_or_default(); output_geometry.loc.y as f64,
let logical = physical.to_f64().to_logical(scale); (output_geometry.loc.y + output_geometry.size.h - 1) as f64,
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
@ -552,7 +542,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_floor()) .contains((pos.as_logical() - *surface_loc).to_i32_round())
{ {
return (false, None); return (false, None);
} }
@ -623,7 +613,7 @@ impl State {
PointerConstraint::Confined(confined) => confined.region(), PointerConstraint::Confined(confined) => confined.region(),
}; };
let point = let point =
(ptr.current_location() - surface_location).to_i32_floor(); (ptr.current_location() - surface_location).to_i32_round();
if region.is_none_or(|region| region.contains(point)) { if region.is_none_or(|region| region.contains(point)) {
constraint.activate(); constraint.activate();
} }
@ -648,13 +638,37 @@ impl State {
seat.set_active_output(&output); seat.set_active_output(&output);
} }
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, (position - output_geometry.loc.to_f64())
&output, .as_logical()
&seat, .to_buffer(
position, output.current_scale().fractional_scale(),
); 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, .. } => {
@ -693,13 +707,37 @@ impl State {
ptr.frame(self); ptr.frame(self);
let shell = self.common.shell.read(); let shell = self.common.shell.read();
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, (position - output_geometry.loc.to_f64())
&output, .as_logical()
&seat, .to_buffer(
position, output.current_scale().fractional_scale(),
); 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, .. } => {
@ -749,12 +787,7 @@ 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)
}; };
// Grabbing a tiling resize handle (the gap between tiles) must not change keyboard focus if let Some(target) = under {
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
@ -969,10 +1002,6 @@ 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(
@ -986,12 +1015,8 @@ 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 =
.relative_direction( frame.value(Axis::Vertical, scroll_factor * vertical_amount);
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,
@ -1574,7 +1599,6 @@ 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() {
@ -1596,7 +1620,7 @@ impl State {
if let Err(err) = self.refresh_output_config() { if let Err(err) = self.refresh_output_config() {
if !closed { if !closed {
tracing::warn!(?err, "Failed to re-enable internal connector"); 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;
@ -2108,7 +2132,6 @@ 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();
@ -2126,9 +2149,7 @@ impl State {
} }
} }
} }
Stage::LayerSurface { Stage::LayerSurface { layer, location } => {
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(),
@ -2171,7 +2192,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_floor()) geometry.contains(global_pos.to_local(output).to_i32_round())
}) })
&& let Some(element) = workspace.popup_element_under(location, seat) && let Some(element) = workspace.popup_element_under(location, seat)
{ {
@ -2185,7 +2206,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_floor()) geometry.contains(global_pos.to_local(output).to_i32_round())
}) })
&& let Some(element) = workspace.toplevel_element_under(location, seat) && let Some(element) = workspace.toplevel_element_under(location, seat)
{ {
@ -2280,9 +2301,7 @@ impl State {
)))); ))));
} }
} }
Stage::LayerSurface { Stage::LayerSurface { layer, location } => {
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,
@ -2388,7 +2407,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_floor(); let point_in_surface = (p - surface_offset.to_f64()).to_i32_round();
return region.contains(point_in_surface); return region.contains(point_in_surface);
} }
true true
@ -2401,7 +2420,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::<_, Global>::new(x, y), output.clone())) Some((Point::new(x, y), output.clone()))
} else { } else {
None None
} }
@ -2411,34 +2430,11 @@ 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();
let serial = SERIAL_COUNTER.next_serial(); pointer.set_location(point);
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.to_local(&output), &output); shell.update_pointer_position(point.as_global().to_local(&output), &output);
let seat = shell let seat = shell
.seats .seats
@ -2453,13 +2449,31 @@ impl State {
self.common.config.cosmic_conf.accessibility_zoom.view_moves, self.common.config.cosmic_conf.accessibility_zoom.view_moves,
); );
update_output_image_copy_cursor_position( let output_geometry = output.geometry();
&shell, for session in cursor_sessions_for_output(&shell, &output) {
&self.common.clock, if let Some((geometry, offset)) = seat.cursor_geometry(
&output, point.to_buffer(
&seat, output.current_scale().fractional_scale(),
point, 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)
{
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));
}
}
} }
} }
} }
@ -2520,36 +2534,3 @@ 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));
}
}
}

View file

@ -26,9 +26,7 @@ use std::{
time::{Duration, Instant}, time::{Duration, Instant},
}; };
use tracing::{error, info, warn}; use tracing::{error, info, warn};
use wayland::protocols::{ use wayland::protocols::overlap_notify::OverlapNotifyState;
keyboard_layout::KeyboardLayoutState, overlap_notify::OverlapNotifyState,
};
use crate::wayland::handlers::compositor::client_compositor_state; use crate::wayland::handlers::compositor::client_compositor_state;
@ -81,8 +79,11 @@ impl State {
warn!(?err, "Failed to setup cosmic-session communication"); warn!(?err, "Failed to setup cosmic-session communication");
} }
self.common.kiosk_child = if let Some(mut command) = self.kiosk_command.take() { let mut args = env::args().skip(1);
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"),
); );
@ -93,7 +94,7 @@ impl State {
}) })
}; };
info!("Running {:?}", command.get_program()); info!("Running {:?}", exec);
command command
.spawn() .spawn()
.map_err(|err| { .map_err(|err| {
@ -112,10 +113,8 @@ 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) {
@ -136,11 +135,7 @@ 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);
}
} }
} }
@ -174,7 +169,6 @@ 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)?;
@ -337,6 +331,5 @@ 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());
} }

View file

@ -1,21 +1,18 @@
use crate::{ use crate::{
backend::render::element::AsGlowRenderer, backend::render::element::{AsGlowRenderer, FromGlesError},
state::State, state::State,
utils::{ utils::{iced::IcedElementInternal, prelude::*},
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,
@ -127,9 +124,9 @@ impl fmt::Debug for CosmicMapped {
} }
} }
#[derive(Clone, Debug)] #[derive(Clone)]
pub struct CosmicMappedKey(CosmicMappedKeyInner); pub struct CosmicMappedKey(CosmicMappedKeyInner);
#[derive(Clone, Debug)] #[derive(Clone)]
enum CosmicMappedKeyInner { enum CosmicMappedKeyInner {
Window(Weak<Mutex<IcedElementInternal<CosmicWindowInternal>>>), Window(Weak<Mutex<IcedElementInternal<CosmicWindowInternal>>>),
Stack(Weak<Mutex<IcedElementInternal<CosmicStackInternal>>>), Stack(Weak<Mutex<IcedElementInternal<CosmicStackInternal>>>),
@ -168,20 +165,6 @@ 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
@ -600,38 +583,33 @@ impl CosmicMapped {
} }
} }
pub fn push_popup_render_elements<R>( pub fn popup_render_elements<R, C>(
&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,
scanout_node: Option<DrmNode>, ) -> Vec<C>
push: &mut dyn FnMut(CosmicMappedRenderElement<R>), where
) 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.push_popup_render_elements( CosmicMappedInternal::Stack(s) => s
renderer, .popup_render_elements::<R, CosmicMappedRenderElement<R>>(
location, renderer, location, scale, alpha,
scale,
alpha,
scanout_node,
&mut |elem| push(elem.into()),
), ),
CosmicMappedInternal::Window(w) => w.push_popup_render_elements( CosmicMappedInternal::Window(w) => w
renderer, .popup_render_elements::<R, CosmicMappedRenderElement<R>>(
location, renderer, location, scale, alpha,
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>(
@ -678,7 +656,7 @@ impl CosmicMapped {
} }
} }
pub fn push_render_elements<R>( pub fn render_elements<R, C>(
&self, &self,
renderer: &mut R, renderer: &mut R,
location: smithay::utils::Point<i32, smithay::utils::Physical>, location: smithay::utils::Point<i32, smithay::utils::Physical>,
@ -686,16 +664,15 @@ impl CosmicMapped {
scale: smithay::utils::Scale<f64>, scale: smithay::utils::Scale<f64>,
alpha: f32, alpha: f32,
scanout_override: Option<bool>, scanout_override: Option<bool>,
scanout_node: Option<DrmNode>, ) -> Vec<C>
push_above: &mut dyn FnMut(CosmicMappedRenderElement<R>), where
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")]
if let Some(debug) = self.debug.lock().unwrap().as_mut() { let mut elements = 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) = (
@ -856,38 +833,41 @@ impl CosmicMapped {
scale.x, scale.x,
0.8, 0.8,
) { ) {
Ok(element) => push_above(element.into()), Ok(element) => vec![CosmicMappedRenderElement::from(element)],
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();
match &self.element { #[cfg_attr(not(feature = "debug"), allow(unused_mut))]
CosmicMappedInternal::Stack(s) => s.push_render_elements( elements.extend(match &self.element {
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( CosmicMappedInternal::Window(w) => w
.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()),
), ),
_ => 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) {
@ -1129,7 +1109,7 @@ where
GrabbedWindow(RescaleRenderElement<self::window::CosmicWindowRenderElement<R>>), GrabbedWindow(RescaleRenderElement<self::window::CosmicWindowRenderElement<R>>),
FocusIndicator(PixelShaderElement), FocusIndicator(PixelShaderElement),
Overlay(PixelShaderElement), Overlay(PixelShaderElement),
StackHoverIndicator(IcedRenderElement<R>), StackHoverIndicator(MemoryRenderBufferRenderElement<R>),
#[cfg(feature = "debug")] #[cfg(feature = "debug")]
Egui(TextureRenderElement<GlesTexture>), Egui(TextureRenderElement<GlesTexture>),
} }
@ -1137,7 +1117,7 @@ where
impl<R> Element for CosmicMappedRenderElement<R> impl<R> Element for CosmicMappedRenderElement<R>
where where
R: AsGlowRenderer, R: AsGlowRenderer,
R::TextureId: Send + 'static, R::TextureId: 'static,
{ {
fn id(&self) -> &smithay::backend::renderer::element::Id { fn id(&self) -> &smithay::backend::renderer::element::Id {
match self { match self {
@ -1358,7 +1338,8 @@ where
impl<R> RenderElement<R> for CosmicMappedRenderElement<R> impl<R> RenderElement<R> for CosmicMappedRenderElement<R>
where where
R: AsGlowRenderer, R: AsGlowRenderer,
R::TextureId: Send + 'static, R::TextureId: 'static,
R::Error: FromGlesError,
{ {
fn draw( fn draw(
&self, &self,
@ -1391,7 +1372,7 @@ where
opaque_regions, opaque_regions,
cache, cache,
) )
.map_err(R::from_gles_error), .map_err(FromGlesError::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)
} }
@ -1413,7 +1394,7 @@ where
opaque_regions, opaque_regions,
cache, cache,
) )
.map_err(R::from_gles_error), .map_err(FromGlesError::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),
@ -1423,7 +1404,7 @@ where
opaque_regions, opaque_regions,
cache, cache,
) )
.map_err(R::from_gles_error), .map_err(FromGlesError::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)
} }
@ -1439,7 +1420,7 @@ where
opaque_regions, opaque_regions,
cache, cache,
) )
.map_err(R::from_gles_error) .map_err(FromGlesError::from_gles_error)
} }
} }
} }
@ -1502,7 +1483,7 @@ where
dst, dst,
cache, cache,
) )
.map_err(R::from_gles_error) .map_err(FromGlesError::from_gles_error)
} }
CosmicMappedRenderElement::MovingStack(elem) => { CosmicMappedRenderElement::MovingStack(elem) => {
elem.capture_framebuffer(frame, src, dst, cache) elem.capture_framebuffer(frame, src, dst, cache)
@ -1524,7 +1505,7 @@ where
dst, dst,
cache, cache,
) )
.map_err(R::from_gles_error) .map_err(FromGlesError::from_gles_error)
} }
CosmicMappedRenderElement::Overlay(elem) => { CosmicMappedRenderElement::Overlay(elem) => {
RenderElement::<GlowRenderer>::capture_framebuffer( RenderElement::<GlowRenderer>::capture_framebuffer(
@ -1534,7 +1515,7 @@ where
dst, dst,
cache, cache,
) )
.map_err(R::from_gles_error) .map_err(FromGlesError::from_gles_error)
} }
CosmicMappedRenderElement::StackHoverIndicator(elem) => { CosmicMappedRenderElement::StackHoverIndicator(elem) => {
elem.capture_framebuffer(frame, src, dst, cache) elem.capture_framebuffer(frame, src, dst, cache)
@ -1545,7 +1526,7 @@ where
RenderElement::<GlowRenderer>::capture_framebuffer( RenderElement::<GlowRenderer>::capture_framebuffer(
elem, glow_frame, src, dst, cache, elem, glow_frame, src, dst, cache,
) )
.map_err(R::from_gles_error) .map_err(FromGlesError::from_gles_error)
} }
} }
} }
@ -1583,13 +1564,13 @@ where
} }
} }
impl<R> From<IcedRenderElement<R>> for CosmicMappedRenderElement<R> impl<R> From<MemoryRenderBufferRenderElement<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: IcedRenderElement<R>) -> Self { fn from(elem: MemoryRenderBufferRenderElement<R>) -> Self {
CosmicMappedRenderElement::StackHoverIndicator(elem) CosmicMappedRenderElement::StackHoverIndicator(elem)
} }
} }

View file

@ -1,11 +1,10 @@
use std::sync::{Arc, Mutex}; use std::sync::Mutex;
use crate::{ use crate::{
backend::render::element::AsGlowRenderer,
config::Config, config::Config,
fl, fl,
shell::grabs::ResizeEdge, shell::grabs::ResizeEdge,
utils::iced::{IcedElement, IcedRenderElement, Program}, utils::iced::{IcedElement, Program},
}; };
use calloop::LoopHandle; use calloop::LoopHandle;
@ -13,57 +12,32 @@ use cosmic::{
Apply, Apply,
iced::{ iced::{
Alignment, Alignment,
core::{Background, Border, Color, Length}, widget::{column, container, row, space},
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::{ use smithay::utils::Size;
backend::renderer::ImportMem,
desktop::space::SpaceElement,
output::Output,
utils::{Logical, Physical, Point, Rectangle, Scale, Size},
};
#[derive(Debug, Clone)] pub type ResizeIndicator = IcedElement<ResizeIndicatorInternal>;
pub struct ResizeIndicator {
edges: ResizeEdge,
direction: Arc<Mutex<ResizeDirection>>,
size: Size<i32, Logical>,
center_elem: IcedElement<ResizeIndicatorInternal>, pub fn resize_indicator(
left_elem: IcedElement<ResizeIndicatorArrow>,
top_elem: IcedElement<ResizeIndicatorArrow>,
right_elem: IcedElement<ResizeIndicatorArrow>,
down_elem: IcedElement<ResizeIndicatorArrow>,
}
const ARROW_SIZE: i32 = 36;
impl ResizeIndicator {
pub fn new(
direction: ResizeDirection, direction: ResizeDirection,
config: &Config, config: &Config,
evlh: LoopHandle<'static, crate::state::State>, evlh: LoopHandle<'static, crate::state::State>,
mut theme: cosmic::Theme, theme: cosmic::Theme,
) -> ResizeIndicator { ) -> ResizeIndicator {
theme.transparent = theme.cosmic().frosted_system_interface; ResizeIndicator::new(
let direction = Arc::new(Mutex::new(direction));
ResizeIndicator {
edges: ResizeEdge::all(),
direction: direction.clone(),
size: Size::default(),
center_elem: IcedElement::new(
ResizeIndicatorInternal { ResizeIndicatorInternal {
edges: Mutex::new(ResizeEdge::all()),
direction,
shortcut1: config shortcut1: config
.shortcuts .shortcuts
.iter() .iter()
.find_map(|(pattern, action)| { .find_map(|(pattern, action)| {
(*action == Action::Resizing(ResizeDirection::Outwards)) (*action == Action::Resizing(ResizeDirection::Outwards)).then_some(pattern)
.then_some(pattern)
}) })
.map(|pattern| format!("{}: ", pattern.to_string())) .map(|pattern| format!("{}: ", pattern.to_string()))
.unwrap_or_else(|| crate::fl!("unknown-keybinding")), .unwrap_or_else(|| crate::fl!("unknown-keybinding")),
@ -71,188 +45,82 @@ impl ResizeIndicator {
.shortcuts .shortcuts
.iter() .iter()
.find_map(|(pattern, action)| { .find_map(|(pattern, action)| {
(*action == Action::Resizing(ResizeDirection::Inwards)) (*action == Action::Resizing(ResizeDirection::Inwards)).then_some(pattern)
.then_some(pattern)
}) })
.map(|pattern| format!("{}: ", pattern.to_string())) .map(|pattern| format!("{}: ", pattern.to_string()))
.unwrap_or_else(|| crate::fl!("unknown-keybinding")), .unwrap_or_else(|| crate::fl!("unknown-keybinding")),
}, },
Size::from((1, 1)), Size::from((1, 1)),
evlh.clone(), evlh,
theme.clone(), theme,
), )
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 {
shortcut1: String, pub edges: Mutex<ResizeEdge>,
shortcut2: String, pub direction: ResizeDirection,
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();
let icon_container_style = || {
theme::Container::custom(|theme| container::Style {
snap: true,
icon_color: Some(Color::from(theme.cosmic().accent.on)),
text_color: Some(Color::from(theme.cosmic().accent.on)),
background: Some(Background::Color(theme.cosmic().accent_color().into())),
border: Border {
radius: 18.0.into(),
width: 0.0,
color: Color::TRANSPARENT,
},
shadow: Default::default(),
})
};
column(vec![
if edges.contains(ResizeEdge::TOP) {
from_name(if self.direction == ResizeDirection::Outwards {
"go-up-symbolic"
} else {
"go-down-symbolic"
})
.size(20)
.prefer_svg(true)
.apply(container)
.padding(8)
.class(icon_container_style())
.width(Length::Shrink)
.apply(container)
.center_x(Length::Fill)
.into()
} else {
space::vertical().height(36).into()
},
row(vec![
if edges.contains(ResizeEdge::LEFT) {
from_name(if self.direction == ResizeDirection::Outwards {
"go-previous-symbolic"
} else {
"go-next-symbolic"
})
.size(20)
.prefer_svg(true)
.apply(container)
.padding(8)
.class(icon_container_style())
.width(Length::Shrink)
.apply(container)
.center_y(Length::Fill)
.into()
} else {
space::horizontal().width(36).into()
},
row(vec![ row(vec![
text::heading(&self.shortcut1).into(), text::heading(&self.shortcut1).into(),
text::body(fl!("grow-window")).into(), text::body(fl!("grow-window")).into(),
@ -265,78 +133,54 @@ impl Program for ResizeIndicatorInternal {
.align_y(Alignment::Center) .align_y(Alignment::Center)
.padding(16) .padding(16)
.apply(container) .apply(container)
.class(theme::Container::custom(|theme| { .class(icon_container_style())
let mut background = theme.cosmic().accent_color();
if theme.transparent {
background.alpha = theme
.cosmic()
.alpha_map
.blurred_alpha(theme.cosmic().frosted);
}
container::Style {
snap: true,
icon_color: Some(Color::from(theme.cosmic().accent.on)),
text_color: Some(Color::from(theme.cosmic().accent.on)),
background: Some(Background::Color(background.into())),
border: Border {
radius: theme.cosmic().radius_s().into(),
width: 0.0,
color: Color::TRANSPARENT,
},
shadow: Default::default(),
}
}))
.width(Length::Shrink) .width(Length::Shrink)
.height(Length::Shrink) .height(Length::Shrink)
.into() .apply(container)
} .center_x(Length::Fill)
} .center_y(Length::Fill)
.into(),
pub struct ResizeIndicatorArrow { if edges.contains(ResizeEdge::RIGHT) {
direction: Arc<Mutex<ResizeDirection>>, from_name(if self.direction == ResizeDirection::Outwards {
icon_outwards: &'static str, "go-next-symbolic"
icon_inwards: &'static str,
}
impl Program for ResizeIndicatorArrow {
type Message = ();
fn view(&self) -> cosmic::Element<'_, Self::Message> {
from_name(
if *self.direction.lock().unwrap() == ResizeDirection::Outwards {
self.icon_outwards
} else { } else {
self.icon_inwards "go-previous-symbolic"
}, })
)
.size(20) .size(20)
.prefer_svg(true) .prefer_svg(true)
.apply(container) .apply(container)
.padding(8) .padding(8)
.class(theme::Container::custom(|theme| { .class(icon_container_style())
let mut background = theme.cosmic().accent_color(); .height(Length::Shrink)
if theme.transparent { .apply(container)
background.alpha = theme .center_y(Length::Fill)
.cosmic() .into()
.alpha_map } else {
.blurred_alpha(theme.cosmic().frosted); space::horizontal().width(36).into()
}
container::Style {
snap: true,
icon_color: Some(Color::from(theme.cosmic().accent.on)),
text_color: Some(Color::from(theme.cosmic().accent.on)),
background: Some(Background::Color(background.into())),
border: Border {
radius: theme.cosmic().radius_s().into(),
width: 0.0,
color: Color::TRANSPARENT,
}, },
shadow: Default::default(), ])
} .width(Length::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) .width(Length::Shrink)
.apply(container)
.center_x(Length::Fill)
.into()
} else {
space::vertical().height(36).into()
},
])
.into() .into()
} }
} }

View file

@ -4,8 +4,11 @@ use super::{
}; };
use crate::{ use crate::{
backend::render::{ backend::render::{
IndicatorShader, Key, Usage, cursor::CursorState, element::AsGlowRenderer, IndicatorShader, Key, Usage,
shadow::ShadowShader, wayland::SurfaceRenderElement, clipped_surface::ClippedSurfaceRenderElement,
cursor::CursorState,
element::{AsGlowRenderer, FromGlesError},
shadow::ShadowShader,
}, },
hooks::{Decorations, HOOKS}, hooks::{Decorations, HOOKS},
shell::{ shell::{
@ -16,20 +19,17 @@ use crate::{
}, },
state::State, state::State,
utils::{ utils::{
iced::{IcedElement, IcedRenderElement, Program}, iced::{IcedElement, Program},
prelude::*, prelude::*,
}, },
}; };
use calloop::LoopHandle; use calloop::LoopHandle;
use cosmic::{ use cosmic::{
Apply, Element as CosmicElement, Theme, Apply, Element as CosmicElement, Theme,
iced::{ iced::{Alignment, id::Id, widget as iced_widget},
Alignment, iced_core::{Background, Border, Color, Length, border::Radius},
core::{Background, Border, Color, Length, border::Radius}, iced_runtime::Task,
id::Id, iced_widget::scrollable::AbsoluteOffset,
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,11 +37,14 @@ 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, Id as RendererId, Kind, RenderElement, UnderlyingStorage}, element::{
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},
@ -146,10 +149,9 @@ 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>,
mut theme: cosmic::Theme, 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());
@ -530,7 +532,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_floor::<i32>(); let point_i32 = relative_pos.to_i32_round::<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
@ -646,38 +648,30 @@ impl CosmicStack {
self.0.loop_handle() self.0.loop_handle()
} }
pub fn push_popup_render_elements<R>( pub fn popup_render_elements<R, C>(
&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_node: Option<DrmNode>, ) -> Vec<C>
push: &mut dyn FnMut(CosmicStackRenderElement<R>), where
) 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].push_popup_render_elements( windows[active]
renderer, .popup_render_elements::<R, CosmicStackRenderElement<R>>(
window_loc, renderer, window_loc, scale, alpha,
scale,
alpha,
scanout_node,
frosted,
&mut |elem| push(elem.into()),
) )
.into_iter()
.map(C::from)
.collect()
}) })
} }
@ -748,7 +742,7 @@ impl CosmicStack {
}) })
} }
pub fn push_render_elements<R>( pub fn render_elements<R, C>(
&self, &self,
renderer: &mut R, renderer: &mut R,
location: Point<i32, Physical>, location: Point<i32, Physical>,
@ -756,18 +750,17 @@ impl CosmicStack {
scale: Scale<f64>, scale: Scale<f64>,
alpha: f32, alpha: f32,
scanout_override: Option<bool>, scanout_override: Option<bool>,
scanout_node: Option<DrmNode>, ) -> Vec<C>
push_above: &mut dyn FnMut(CosmicStackRenderElement<R>), where
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; return Vec::new();
} }
let geometry = self let geometry = self
@ -776,37 +769,28 @@ 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 theme = p.theme.lock().unwrap(); let mut elements = AsRenderElements::<R>::render_elements::<CosmicStackRenderElement<R>>(
if theme.cosmic().frosted_windows { &self.0, renderer, stack_loc, scale, alpha,
(theme.cosmic().frosted as u8 + 1) as usize );
} else {
0 elements.extend(self.0.with_program(|p| {
}
});
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 appearance = p.appearance_conf.lock().unwrap();
let theme = p.theme.lock().unwrap(); let theme = p.theme.lock().unwrap();
let appearance = p.appearance_conf.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;
round.then(|| { let radii = 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);
geo.size.h += TAB_HEIGHT as f64; geo.size.h += TAB_HEIGHT as f64;
@ -817,49 +801,53 @@ impl CosmicStack {
let window_key = let window_key =
CosmicMappedKey(CosmicMappedKeyInner::Stack(Arc::downgrade(&self.0.0))); CosmicMappedKey(CosmicMappedKeyInner::Stack(Arc::downgrade(&self.0.0)));
if !maximized { let border = (!maximized).then(|| {
let (r, g, b, a) = theme.cosmic().bg_divider().into_components(); let (r, g, b, a) = theme.cosmic().bg_divider().into_components();
push_above(CosmicStackRenderElement::Border(IndicatorShader::element( 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; 4]), radii.unwrap_or([0, 0, 0, 0]),
a * alpha, a * alpha,
scale.x, scale.x,
[r, g, b], [r, g, b],
))); ))
}; });
let radii = radii.map(|[a, _, c, _]| [a, 0, c, 0]); border.into_iter().chain(
windows[active].push_render_elements( windows[active]
.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(
self.0.push_render_elements(
renderer, renderer,
stack_loc, elem,
scale, scale,
alpha, geo,
radii.map(|[_, b, _, d]| [0, b, 0, d]).unwrap_or([0; 4]), radii.unwrap(),
&mut |elem| push_above(elem.into()), ))
Some(&mut |elem| push_below(elem.into())), } else {
); CosmicStackRenderElement::Window(elem)
}
}),
)
}));
elements.into_iter().map(C::from).collect()
} }
pub(crate) fn set_theme(&self, mut theme: cosmic::Theme) { pub(crate) fn set_theme(&self, 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();
}); });
@ -1368,14 +1356,11 @@ 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 mut background = if group_focused { let 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,
@ -1892,32 +1877,40 @@ impl TouchTarget<State> for CosmicStack {
} }
pub enum CosmicStackRenderElement<R: Renderer + ImportAll + ImportMem> { pub enum CosmicStackRenderElement<R: Renderer + ImportAll + ImportMem> {
Header(IcedRenderElement<R>), Header(MemoryRenderBufferRenderElement<R>),
Shadow(PixelShaderElement), Shadow(PixelShaderElement),
Border(PixelShaderElement), Border(PixelShaderElement),
Window(SurfaceRenderElement<R>), Window(WaylandSurfaceRenderElement<R>),
Clipped(ClippedSurfaceRenderElement<R>),
} }
impl<R: Renderer + ImportAll + ImportMem> From<IcedRenderElement<R>> impl<R: Renderer + ImportAll + ImportMem> From<MemoryRenderBufferRenderElement<R>>
for CosmicStackRenderElement<R> for CosmicStackRenderElement<R>
{ {
fn from(value: IcedRenderElement<R>) -> Self { fn from(value: MemoryRenderBufferRenderElement<R>) -> Self {
Self::Header(value) Self::Header(value)
} }
} }
impl<R: Renderer + ImportAll + ImportMem> From<SurfaceRenderElement<R>> impl<R: Renderer + ImportAll + ImportMem> From<WaylandSurfaceRenderElement<R>>
for CosmicStackRenderElement<R> for CosmicStackRenderElement<R>
{ {
fn from(value: SurfaceRenderElement<R>) -> Self { fn from(value: WaylandSurfaceRenderElement<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 + AsGlowRenderer, R: Renderer + ImportAll + ImportMem,
R::TextureId: Send + 'static,
{ {
fn id(&self) -> &RendererId { fn id(&self) -> &RendererId {
match self { match self {
@ -1925,6 +1918,7 @@ 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(),
} }
} }
@ -1934,6 +1928,7 @@ 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(),
} }
} }
@ -1943,6 +1938,7 @@ 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(),
} }
} }
@ -1952,6 +1948,7 @@ 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),
} }
} }
@ -1961,6 +1958,7 @@ 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),
} }
} }
@ -1970,6 +1968,7 @@ 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(),
} }
} }
@ -1983,6 +1982,7 @@ 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,6 +1992,7 @@ 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),
} }
} }
@ -2001,6 +2002,7 @@ 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(),
} }
} }
@ -2010,6 +2012,7 @@ 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(),
} }
} }
@ -2019,6 +2022,7 @@ 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(),
} }
} }
} }
@ -2026,11 +2030,12 @@ where
impl<R> RenderElement<R> for CosmicStackRenderElement<R> impl<R> RenderElement<R> for CosmicStackRenderElement<R>
where where
R: AsGlowRenderer, R: AsGlowRenderer,
R::TextureId: Send + 'static, R::TextureId: '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>],
@ -2051,11 +2056,14 @@ where
opaque_regions, opaque_regions,
cache, cache,
) )
.map_err(R::from_gles_error) .map_err(FromGlesError::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)
}
} }
} }
@ -2066,6 +2074,7 @@ 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),
} }
} }
@ -2088,11 +2097,14 @@ where
dst, dst,
cache, cache,
) )
.map_err(R::from_gles_error) .map_err(FromGlesError::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)
}
} }
} }
} }

View file

@ -3,15 +3,16 @@ use cosmic::{
font::Font, font::Font,
iced::{ iced::{
Background, Background,
core::{ widget::{self, container::draw_background, rule::FillMode},
},
iced_core::{
Border, Clipboard, Color, Length, Rectangle, Shell, Size, alignment, event, Border, Clipboard, Color, Length, Rectangle, Shell, Size, alignment, event,
layout::{Layout, Limits, Node}, layout::{Layout, Limits, Node},
mouse, overlay, renderer, mouse, overlay, renderer,
text::{Ellipsize, EllipsizeHeightLimit, Shaping, Wrapping}, text::{Ellipsize, EllipsizeHeightLimit, Shaping, Wrapping},
widget::{Id, Widget, operation::Operation, tree::Tree}, widget::{Id, Widget, operation::Operation, tree::Tree},
}, },
widget::{self, container::draw_background, rule::FillMode, scrollable::AbsoluteOffset}, iced_widget::scrollable::AbsoluteOffset,
},
theme, theme,
widget::{Icon, icon::from_name, text}, widget::{Icon, icon::from_name, text},
}; };
@ -288,8 +289,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,

View file

@ -1,9 +1,8 @@
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::{ iced::{Element, id::Id, widget},
Element, iced_core::{
core::{
Background, Border, Clipboard, Color, Length, Point, Rectangle, Renderer, Shell, Size, Background, Border, Clipboard, Color, Length, Point, Rectangle, Renderer, Shell, Size,
Vector, event, Vector, event,
layout::{Layout, Limits, Node}, layout::{Layout, Limits, Node},
@ -17,14 +16,9 @@ use cosmic::{
tree::{self, Tree}, tree::{self, Tree},
}, },
}, },
id::Id, iced_widget::container::draw_background,
widget::{
self,
container::{Catalog, draw_background},
},
},
theme, theme,
widget::icon::from_name, widget::{container::Catalog, icon::from_name},
}; };
use keyframe::{ use keyframe::{
ease, ease,
@ -385,8 +379,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,
@ -519,11 +513,13 @@ 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(Color::TRANSPARENT)), background: Some(Background::Color(
theme.cosmic().primary_container_color().into(),
)),
border: Border { border: Border {
radius: 0.0.into(), radius: 0.0.into(),
width: 0.0, width: 0.0,

View file

@ -1,7 +1,6 @@
use crate::{ use crate::{
backend::render::element::AsGlowRenderer,
fl, fl,
utils::iced::{IcedElement, IcedRenderElement, Program}, utils::iced::{IcedElement, Program},
}; };
use calloop::LoopHandle; use calloop::LoopHandle;
@ -9,78 +8,22 @@ 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::{ use smithay::utils::{Logical, Size};
backend::renderer::ImportMem,
desktop::space::SpaceElement,
output::Output,
utils::{Logical, Physical, Point, Rectangle, Scale, Size},
};
#[derive(Debug)] pub type StackHover = IcedElement<StackHoverInternal>;
pub struct StackHover {
location: Point<i32, Logical>,
elem: IcedElement<StackHoverInternal>,
}
impl StackHover { pub fn stack_hover(
pub fn new(
evlh: LoopHandle<'static, crate::state::State>, evlh: LoopHandle<'static, crate::state::State>,
size: Size<i32, Logical>, size: Size<i32, Logical>,
mut theme: cosmic::Theme, theme: cosmic::Theme,
) -> StackHover { ) -> StackHover {
theme.transparent = theme.cosmic().frosted_system_interface; StackHover::new(StackHoverInternal, size, evlh, theme)
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;
@ -104,30 +47,23 @@ 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| { .class(theme::Container::custom(|theme| container::Style {
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(background.into())), background: Some(Background::Color(theme.cosmic().accent_color().into())),
border: Border { border: Border {
radius: theme.cosmic().radius_s().into(), radius: 18.0.into(),
width: 0.0, width: 0.0,
color: Color::TRANSPARENT, color: Color::TRANSPARENT,
}, },
shadow: Default::default(), 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()
} }
} }

View file

@ -1,12 +1,7 @@
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::handlers::{ wayland::{
background_effect::ComputedBlurRegionCachedState, compositor::frame_time_filter_fn, handlers::compositor::frame_time_filter_fn, protocols::corner_radius::CacheableCorners,
corner_radius::surface_corners,
}, },
}; };
use std::{ use std::{
@ -19,12 +14,11 @@ use std::{
}; };
use smithay::{ use smithay::{
backend::{ backend::renderer::{
drm::DrmNode, ImportAll, Renderer,
renderer::{ element::{
ImportAll, Renderer, buffer_has_alpha, AsRenderElements, Kind, RenderElementStates,
element::{Kind, RenderElementStates, surface::KindEvaluation}, surface::{WaylandSurfaceRenderElement, render_elements_from_surface_tree},
utils::RendererSurfaceStateUserData,
}, },
}, },
desktop::{ desktop::{
@ -51,12 +45,10 @@ 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,
@ -75,83 +67,6 @@ 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);
@ -229,7 +144,22 @@ 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| surface_corners(states, geometry_size)) with_states(&surface, |states| {
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),
])
})
}) })
} }
@ -880,112 +810,97 @@ impl CosmicSurface {
self.0.user_data() self.0.user_data()
} }
pub fn push_popup_render_elements<R>( pub fn popup_render_elements<R, C>(
&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_node: Option<DrmNode>, ) -> Vec<C>
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,
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();
for (popup, popup_offset) in PopupManager::popups_for_surface(surface) { 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();
let radii = with_states(popup.wl_surface(), |states| {
surface_corners(states, geometry.size.to_i32_round())
})
.unwrap_or([0; 4]);
push_render_elements_from_surface_tree( render_elements_from_surface_tree(
renderer, renderer,
popup.wl_surface(), popup.wl_surface(),
location + offset, location + offset,
geometry,
scale, scale,
alpha, alpha,
false, FRAME_TIME_FILTER,
radii,
None,
blur_strength,
scanout_kind_eval(None, scanout_node, alpha),
push,
None,
) )
})
.collect()
} }
} WindowSurface::X11(_) => Vec::new(),
WindowSurface::X11(_) => {}
} }
} }
pub fn push_render_elements<R>( pub fn render_elements<R, C>(
&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>,
scanout_node: Option<DrmNode>, ) -> Vec<C>
should_clip: bool, where
radii: [u8; 4], R: Renderer + ImportAll,
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();
push_render_elements_from_surface_tree( render_elements_from_surface_tree(
renderer, renderer,
surface, surface,
location, location,
geometry,
scale, scale,
alpha, alpha,
should_clip, scanout_override
radii, .map(|val| {
None, if val {
blur_strength, Kind::ScanoutCandidate
scanout_kind_eval(scanout_override, scanout_node, alpha), } else {
push_above, Kind::Unspecified
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; return Vec::new();
}; };
push_render_elements_from_surface_tree( render_elements_from_surface_tree(
renderer, renderer,
&surface, &surface,
location, location,
geometry,
scale, scale,
alpha, alpha,
should_clip, scanout_override
radii, .map(|val| {
None, if val {
blur_strength, Kind::ScanoutCandidate
scanout_kind_eval(scanout_override, scanout_node, alpha), } else {
push_above, Kind::Unspecified
push_below, }
.into()
})
.unwrap_or(FRAME_TIME_FILTER),
) )
} }
} }
@ -1129,6 +1044,24 @@ 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,

View file

@ -1,88 +1,25 @@
use crate::{ use crate::{
backend::render::element::AsGlowRenderer,
fl, fl,
utils::iced::{IcedElement, IcedRenderElement, Program}, utils::iced::{IcedElement, Program},
}; };
use calloop::LoopHandle; use calloop::LoopHandle;
use cosmic::{ use cosmic::{
Apply, Apply,
iced::{ iced::widget::{container, row, space},
core::{Alignment, Background, Border, Color, Length}, iced_core::{Alignment, Background, Border, Color, Length},
widget::{container, row, space},
},
theme, theme,
widget::{icon::from_name, text}, widget::{icon::from_name, text},
}; };
use smithay::{ use smithay::utils::Size;
backend::renderer::ImportMem,
desktop::space::SpaceElement,
output::Output,
utils::{Logical, Physical, Point, Rectangle, Scale, Size},
};
#[derive(Debug, Clone)] pub type SwapIndicator = IcedElement<SwapIndicatorInternal>;
pub struct SwapIndicator {
location: Point<i32, Logical>,
elem: IcedElement<SwapIndicatorInternal>,
}
impl SwapIndicator { pub fn swap_indicator(
pub fn new(
evlh: LoopHandle<'static, crate::state::State>, evlh: LoopHandle<'static, crate::state::State>,
mut theme: cosmic::Theme, theme: cosmic::Theme,
) -> SwapIndicator { ) -> SwapIndicator {
theme.transparent = theme.cosmic().frosted_system_interface; SwapIndicator::new(SwapIndicatorInternal, Size::from((1, 1)), evlh, theme)
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;
@ -106,30 +43,23 @@ 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| { .class(theme::Container::custom(|theme| container::Style {
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(background.into())), background: Some(Background::Color(theme.cosmic().accent_color().into())),
border: Border { border: Border {
radius: theme.cosmic().radius_s().into(), radius: 18.0.into(),
width: 0.0, width: 0.0,
color: Color::TRANSPARENT, color: Color::TRANSPARENT,
}, },
shadow: Default::default(), 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()
} }
} }

View file

@ -1,7 +1,10 @@
use crate::{ use crate::{
backend::render::{ backend::render::{
IndicatorShader, Key, Usage, cursor::CursorState, element::AsGlowRenderer, IndicatorShader, Key, Usage,
shadow::ShadowShader, wayland::SurfaceRenderElement, clipped_surface::ClippedSurfaceRenderElement,
cursor::CursorState,
element::{AsGlowRenderer, FromGlesError},
shadow::ShadowShader,
}, },
hooks::{Decorations, HOOKS}, hooks::{Decorations, HOOKS},
shell::{ shell::{
@ -11,7 +14,7 @@ use crate::{
}, },
state::State, state::State,
utils::{ utils::{
iced::{IcedElement, IcedRenderElement, Program}, iced::{IcedElement, Program},
prelude::*, prelude::*,
}, },
}; };
@ -20,11 +23,14 @@ 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, Id as RendererId, Kind, RenderElement, UnderlyingStorage}, element::{
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},
@ -113,7 +119,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_floor::<i32>() - geo.loc; let loc = location.to_i32_round::<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 {
@ -190,13 +196,12 @@ 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>,
mut theme: cosmic::Theme, 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);
@ -292,7 +297,7 @@ impl CosmicWindow {
{ {
let geo = p.window.geometry(); let geo = p.window.geometry();
let point_i32 = relative_pos.to_i32_floor::<i32>(); let point_i32 = relative_pos.to_i32_round::<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)
@ -346,17 +351,17 @@ impl CosmicWindow {
self.0.loop_handle() self.0.loop_handle()
} }
pub fn push_popup_render_elements<R>( pub fn popup_render_elements<R, C>(
&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_node: Option<DrmNode>, ) -> Vec<C>
push: &mut dyn FnMut(CosmicWindowRenderElement<R>), where
) where R: Renderer + ImportAll + ImportMem,
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));
@ -367,22 +372,13 @@ impl CosmicWindow {
}; };
self.0.with_program(|p| { self.0.with_program(|p| {
let theme = p.theme.lock().unwrap(); p.window
let frosted = if theme.cosmic().frosted_windows { .popup_render_elements::<R, CosmicWindowRenderElement<R>>(
(theme.cosmic().frosted as u8 + 1) as usize renderer, window_loc, scale, alpha,
} else {
0
};
p.window.push_popup_render_elements(
renderer,
window_loc,
scale,
alpha,
scanout_node,
frosted,
&mut |elem| push(elem.into()),
) )
.into_iter()
.map(C::from)
.collect()
}) })
} }
@ -466,7 +462,7 @@ impl CosmicWindow {
}) })
} }
pub fn push_render_elements<R>( pub fn render_elements<R, C>(
&self, &self,
renderer: &mut R, renderer: &mut R,
location: Point<i32, Physical>, location: Point<i32, Physical>,
@ -474,12 +470,11 @@ impl CosmicWindow {
scale: Scale<f64>, scale: Scale<f64>,
alpha: f32, alpha: f32,
scanout_override: Option<bool>, scanout_override: Option<bool>,
scanout_node: Option<DrmNode>, ) -> Vec<C>
push_above: &mut dyn FnMut(CosmicWindowRenderElement<R>), where
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| {
( (
@ -516,6 +511,8 @@ 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(),
@ -545,57 +542,55 @@ impl CosmicWindow {
scale.x, scale.x,
[r, g, b], [r, g, b],
)); ));
push_above(elem); elements.push(elem);
} }
self.0.with_program(|p| { let window_elements = self.0.with_program(|p| {
let mut radii = radii; p.window
if has_ssd { .render_elements::<R, WaylandSurfaceRenderElement<R>>(
radii[1] = 0;
radii[3] = 0;
}
let theme = p.theme.lock().unwrap();
let frosted = if theme.cosmic().frosted_windows {
(theme.cosmic().frosted as u8 + 1) as usize
} else {
0
};
p.window.push_render_elements(
renderer, renderer,
window_loc, window_loc,
scale, scale,
alpha, alpha,
scanout_override, scanout_override,
scanout_node, )
clip,
radii,
frosted,
&mut |elem| push_above(elem.into()),
Some(&mut |elem| push_below(elem.into())),
);
}); });
if window_elements.is_empty() {
return Vec::new();
}
elements.extend(window_elements.into_iter().map(|elem| {
if has_ssd {
radii[1] = 0;
radii[3] = 0;
}
if radii.iter().any(|x| *x != 0)
&& clip
&& ClippedSurfaceRenderElement::will_clip(&elem, scale, geo, radii)
{
CosmicWindowRenderElement::Clipped(ClippedSurfaceRenderElement::new(
renderer, elem, scale, geo, radii,
))
} else {
CosmicWindowRenderElement::Window(elem)
}
}));
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.to_physical_precise_round(scale)); .with_program(|p| p.window.geometry().loc)
self.0.push_render_elements( .to_physical_precise_round(scale);
renderer, elements.extend(AsRenderElements::<R>::render_elements::<
ssd_loc, CosmicWindowRenderElement<R>,
scale, >(&self.0, renderer, ssd_loc, scale, alpha))
alpha,
radii,
&mut |elem| push_above(elem.into()),
Some(&mut |elem| push_below(elem.into())),
);
}
} }
pub(crate) fn set_theme(&self, mut theme: cosmic::Theme) { elements.into_iter().map(C::from).collect()
theme.transparent = theme.cosmic().frosted_windows; }
pub(crate) fn set_theme(&self, theme: cosmic::Theme) {
self.0.with_program(|p| { self.0.with_program(|p| {
*p.theme.lock().unwrap() = theme.clone(); *p.theme.lock().unwrap() = theme.clone();
}); });
@ -1248,33 +1243,41 @@ impl WaylandFocus for CosmicWindow {
} }
} }
pub enum CosmicWindowRenderElement<R: AsGlowRenderer + ImportAll + ImportMem> { pub enum CosmicWindowRenderElement<R: Renderer + ImportAll + ImportMem> {
Header(IcedRenderElement<R>), Header(MemoryRenderBufferRenderElement<R>),
Shadow(PixelShaderElement), Shadow(PixelShaderElement),
Border(PixelShaderElement), Border(PixelShaderElement),
Window(SurfaceRenderElement<R>), Window(WaylandSurfaceRenderElement<R>),
Clipped(ClippedSurfaceRenderElement<R>),
} }
impl<R: AsGlowRenderer + ImportAll + ImportMem> From<IcedRenderElement<R>> impl<R: Renderer + ImportAll + ImportMem> From<MemoryRenderBufferRenderElement<R>>
for CosmicWindowRenderElement<R> for CosmicWindowRenderElement<R>
{ {
fn from(value: IcedRenderElement<R>) -> Self { fn from(value: MemoryRenderBufferRenderElement<R>) -> Self {
Self::Header(value) Self::Header(value)
} }
} }
impl<R: AsGlowRenderer + ImportAll + ImportMem> From<SurfaceRenderElement<R>> impl<R: Renderer + ImportAll + ImportMem> From<WaylandSurfaceRenderElement<R>>
for CosmicWindowRenderElement<R> for CosmicWindowRenderElement<R>
{ {
fn from(value: SurfaceRenderElement<R>) -> Self { fn from(value: WaylandSurfaceRenderElement<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 + AsGlowRenderer, R: Renderer + ImportAll + ImportMem,
R::TextureId: Send + 'static,
{ {
fn id(&self) -> &RendererId { fn id(&self) -> &RendererId {
match self { match self {
@ -1282,6 +1285,7 @@ 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(),
} }
} }
@ -1291,6 +1295,7 @@ 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(),
} }
} }
@ -1300,6 +1305,7 @@ 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(),
} }
} }
@ -1309,6 +1315,7 @@ 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),
} }
} }
@ -1318,6 +1325,7 @@ 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),
} }
} }
@ -1327,6 +1335,7 @@ 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(),
} }
} }
@ -1340,6 +1349,7 @@ 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),
} }
} }
@ -1349,6 +1359,7 @@ 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),
} }
} }
@ -1358,6 +1369,7 @@ 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(),
} }
} }
@ -1367,6 +1379,7 @@ 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(),
} }
} }
@ -1376,6 +1389,7 @@ 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(),
} }
} }
} }
@ -1383,7 +1397,8 @@ where
impl<R> RenderElement<R> for CosmicWindowRenderElement<R> impl<R> RenderElement<R> for CosmicWindowRenderElement<R>
where where
R: AsGlowRenderer, R: AsGlowRenderer,
R::TextureId: Send + 'static, R::TextureId: 'static,
R::Error: FromGlesError,
{ {
fn draw( fn draw(
&self, &self,
@ -1408,11 +1423,14 @@ where
opaque_regions, opaque_regions,
cache, cache,
) )
.map_err(R::from_gles_error) .map_err(FromGlesError::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)
}
} }
} }
@ -1423,6 +1441,7 @@ 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),
} }
} }
@ -1445,11 +1464,14 @@ where
dst, dst,
cache, cache,
) )
.map_err(R::from_gles_error) .map_err(FromGlesError::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)
}
} }
} }
} }

View file

@ -378,14 +378,6 @@ 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

View file

@ -32,12 +32,10 @@ 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,
@ -97,15 +95,10 @@ 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 { callback(Stage::LayerSurface { layer, location })?;
layer,
location,
workspace_idx: current.1,
})?;
} }
// calculate a bunch of stuff for workspace transitions // calculate a bunch of stuff for workspace transitions
@ -194,7 +187,7 @@ fn render_input_order_internal<R: 'static>(
}); });
( (
Some((previous, previous_idx, has_fullscreen, offset)), Some((previous, 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)),
@ -213,7 +206,6 @@ fn render_input_order_internal<R: 'static>(
layer, layer,
popup: &popup, popup: &popup,
location, location,
workspace_idx: current.1,
})?; })?;
} }
} }
@ -244,7 +236,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));
}; };
@ -273,12 +265,11 @@ fn render_input_order_internal<R: 'static>(
layer, layer,
popup: &popup, popup: &popup,
location, location,
workspace_idx: current.1,
})?; })?;
} }
} }
if let Some((_, idx, has_fullscreen, offset)) = previous.as_ref() if let Some((_, has_fullscreen, offset)) = previous.as_ref()
&& !has_fullscreen && !has_fullscreen
{ {
// previous bottom layer popups // previous bottom layer popups
@ -287,7 +278,6 @@ 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,
})?; })?;
} }
} }
@ -299,12 +289,11 @@ fn render_input_order_internal<R: 'static>(
layer, layer,
popup: &popup, popup: &popup,
location, location,
workspace_idx: current.1,
})?; })?;
} }
} }
if let Some((_, idx, has_fullscreen, offset)) = previous.as_ref() if let Some((_, has_fullscreen, offset)) = previous.as_ref()
&& !has_fullscreen && !has_fullscreen
{ {
// previous background layer popups // previous background layer popups
@ -313,7 +302,6 @@ 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,
})?; })?;
} }
} }
@ -321,11 +309,7 @@ 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 { callback(Stage::LayerSurface { layer, location })?;
layer,
location,
workspace_idx: current.1,
})?;
} }
} }
@ -342,7 +326,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));
}; };
@ -357,25 +341,17 @@ 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 { callback(Stage::LayerSurface { layer, location })?;
layer,
location,
workspace_idx: current.1,
})?;
} }
} }
if let Some((_, idx, has_fullscreen, offset)) = previous.as_ref() if let Some((_, 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 { callback(Stage::LayerSurface { layer, location })?;
layer,
location,
workspace_idx: **idx,
})?;
} }
} }
@ -383,25 +359,17 @@ 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 { callback(Stage::LayerSurface { layer, location })?;
layer,
location,
workspace_idx: current.1,
})?;
} }
} }
if let Some((_, idx, has_fullscreen, offset)) = previous.as_ref() if let Some((_, 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 { callback(Stage::LayerSurface { layer, location })?;
layer,
location,
workspace_idx: **idx,
})?;
} }
} }

View file

@ -6,7 +6,7 @@ use std::{
use crate::{ use crate::{
shell::{ shell::{
CosmicSurface, CursorGeometry, SeatExt, CosmicSurface, 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(CursorGeometry { hotspot, .. }) = let cursor_hotspot = if let Some((_, 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

View file

@ -1,13 +1,11 @@
use cosmic::{ use cosmic::{
iced::{ iced::Element,
Alignment, Element, iced_core::{
core::{
Background, Border, Clipboard, Color, Event, Layout, Length, Rectangle, Background, Border, Clipboard, Color, Event, Layout, Length, Rectangle,
Renderer as IcedRenderer, Shell, Size, layout, mouse, overlay, Renderer as IcedRenderer, Shell, Size, layout, mouse, overlay,
renderer::{Quad, Style}, renderer::{Quad, Style},
widget::{Id, Tree, Widget, tree}, widget::{Id, Tree, Widget, tree},
}, },
},
widget::button::Catalog, widget::button::Catalog,
}; };
@ -75,26 +73,11 @@ where
viewport: &Rectangle, viewport: &Rectangle,
) { ) {
let widget_state = state.state.downcast_ref::<State>(); let widget_state = state.state.downcast_ref::<State>();
let mut styling = if widget_state.cursor_over { let 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 {
@ -102,7 +85,7 @@ where
bounds: layout.bounds(), bounds: layout.bounds(),
border: Border { border: Border {
radius: styling.border_radius, radius: styling.border_radius,
width: 0., width: styling.border_width,
color: styling.border_color, color: styling.border_color,
}, },
shadow: Default::default(), shadow: Default::default(),

View file

@ -9,18 +9,19 @@ use std::{
use calloop::LoopHandle; use calloop::LoopHandle;
use cosmic::{ use cosmic::{
Apply as _, Task, Apply as _, Task,
iced::{ iced::{Alignment, Background},
Alignment, Background, iced_core::{Border, Length, Rectangle as IcedRectangle, alignment::Horizontal},
core::{Border, Length, Rectangle as IcedRectangle, alignment::Horizontal}, iced_widget::{self, Column, Row, text::Style as TextStyle},
widget::{self as iced_widget, Row, text::Style as TextStyle},
},
theme, theme,
widget::{button, divider, icon::from_name, menu::menu_column::MenuColumn, space, text}, widget::{button, divider, icon::from_name, space, text},
}; };
use smithay::{ use smithay::{
backend::{ backend::{
input::{ButtonState, TouchSlot}, input::{ButtonState, TouchSlot},
renderer::ImportMem, renderer::{
ImportMem, Renderer,
element::{AsRenderElements, memory::MemoryRenderBufferRenderElement},
},
}, },
desktop::space::SpaceElement, desktop::space::SpaceElement,
input::{ input::{
@ -42,11 +43,10 @@ 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, IcedRenderElement, Program}, iced::{IcedElement, Program},
prelude::*, prelude::*,
}, },
}; };
@ -65,18 +65,19 @@ pub struct MenuGrabState {
pub type SeatMenuGrabState = Mutex<Option<MenuGrabState>>; pub type SeatMenuGrabState = Mutex<Option<MenuGrabState>>;
impl MenuGrabState { impl MenuGrabState {
pub fn render<R>( pub fn render<I, R>(&self, renderer: &mut R, output: &Output) -> Vec<I>
&self, where
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,
I: From<MemoryRenderBufferRenderElement<R>>,
{ {
let scale = output.current_scale().fractional_scale(); let scale = output.current_scale().fractional_scale();
for elem in self.elements.lock().unwrap().iter() { self.elements
elem.iced.push_render_elements( .lock()
.unwrap()
.iter()
.flat_map(|elem| {
elem.iced.render_elements(
renderer, renderer,
elem.position elem.position
.to_local(output) .to_local(output)
@ -84,13 +85,9 @@ impl MenuGrabState {
.to_physical_precise_round(scale), .to_physical_precise_round(scale),
scale.into(), scale.into(),
1.0, 1.0,
elem.iced
.with_theme(|theme| theme.cosmic().radius_s())
.map(|x| x.round() as u8),
push,
None,
) )
} })
.collect()
} }
pub fn is_in_screen_space(&self) -> bool { pub fn is_in_screen_space(&self) -> bool {
@ -279,13 +276,11 @@ 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(),
theme, state.common.theme.clone(),
); );
let min_size = element.minimum_size(); let min_size = element.minimum_size();
@ -300,7 +295,7 @@ impl Program for ContextMenu {
Rectangle::new( Rectangle::new(
position position
+ Point::from(( + Point::from((
bounds.width.floor() as i32, bounds.width.ceil() as i32,
bounds.y.ceil() as i32, bounds.y.ceil() as i32,
)), )),
min_size.as_global(), min_size.as_global(),
@ -309,7 +304,7 @@ impl Program for ContextMenu {
Rectangle::new( Rectangle::new(
position position
+ Point::from(( + Point::from((
bounds.width.floor() as i32, bounds.width.ceil() 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,
)), )),
@ -317,15 +312,14 @@ impl Program for ContextMenu {
), ),
// to the left -> down // to the left -> down
Rectangle::new( Rectangle::new(
position position + Point::from((-min_size.w, bounds.y.ceil() as i32)),
+ 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 + 1, -min_size.w,
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,
)), )),
@ -391,11 +385,9 @@ impl Program for ContextMenu {
_ => Length::Fill, _ => Length::Fill,
}; };
MenuColumn::with_children(self.items.iter().enumerate().map(|(idx, item)| { Column::with_children(self.items.iter().enumerate().map(|(idx, item)| {
match item { match item {
Item::Separator => divider::horizontal::light() Item::Separator => divider::horizontal::light().into(),
.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(),
@ -485,7 +477,6 @@ 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()),
@ -549,7 +540,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_floor()) bbox.contains(event_location.to_i32_round())
}) { }) {
let element = &mut elements[i]; let element = &mut elements[i];
@ -565,11 +556,8 @@ 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 elements.iter_mut().for_each(|element| {
.iter_mut() if element.pointer_entered {
.filter(|element| element.pointer_entered)
.skip(1)
.for_each(|element| {
PointerTarget::leave( PointerTarget::leave(
&element.iced, &element.iced,
&self.seat, &self.seat,
@ -578,6 +566,7 @@ impl PointerGrab<State> for MenuGrab {
event.time, event.time,
); );
element.pointer_entered = false; element.pointer_entered = false;
}
}) })
} }
} }
@ -754,7 +743,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_floor()) bbox.contains(event_location.to_i32_round())
}) { }) {
let element = &mut elements[i]; let element = &mut elements[i];

View file

@ -6,7 +6,10 @@ use crate::{
}, },
shell::{ shell::{
CosmicMapped, CosmicSurface, Direction, ManagedLayer, CosmicMapped, CosmicSurface, Direction, ManagedLayer,
element::{CosmicMappedRenderElement, stack_hover::StackHover}, element::{
CosmicMappedRenderElement,
stack_hover::{StackHover, stack_hover},
},
focus::target::{KeyboardFocusTarget, PointerFocusTarget}, focus::target::{KeyboardFocusTarget, PointerFocusTarget},
layout::floating::TiledCorners, layout::floating::TiledCorners,
}, },
@ -16,14 +19,12 @@ 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, ImportAll, ImportMem, Renderer,
element::{RenderElement, utils::RescaleRenderElement}, element::{AsRenderElements, RenderElement, utils::RescaleRenderElement},
}, },
}, },
desktop::{WindowSurfaceType, layer_map_for_output, space::SpaceElement}, desktop::{WindowSurfaceType, layer_map_for_output, space::SpaceElement},
@ -67,17 +68,12 @@ pub struct MoveGrabState {
impl MoveGrabState { impl MoveGrabState {
#[profiling::function] #[profiling::function]
pub fn render<R>( pub fn render<I, R>(&self, renderer: &mut R, output: &Output, theme: &CosmicTheme) -> Vec<I>
&self, where
renderer: &mut R, R: Renderer + ImportAll + ImportMem + AsGlowRenderer,
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
@ -102,7 +98,7 @@ impl MoveGrabState {
.intersection(window_geo) .intersection(window_geo)
.is_none() .is_none()
{ {
return; return Vec::new();
} }
let output_scale: Scale<f64> = output.current_scale().fractional_scale().into(); let output_scale: Scale<f64> = output.current_scale().fractional_scale().into();
@ -112,33 +108,13 @@ 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);
if self.indicator_thickness > 0 { let focus_element = if self.indicator_thickness > 0 {
push( Some(CosmicMappedRenderElement::from(
IndicatorShader::focus_element( IndicatorShader::focus_element(
renderer, renderer,
Key::Window(Usage::MoveGrabIndicator, self.window.key()), Key::Window(Usage::MoveGrabIndicator, self.window.key()),
@ -161,57 +137,12 @@ impl MoveGrabState {
active_window_hint.green, active_window_hint.green,
active_window_hint.blue, active_window_hint.blue,
], ],
) ),
.into(),
)
}
let map_window_element = |elem| match elem {
CosmicMappedRenderElement::Stack(stack) => {
CosmicMappedRenderElement::GrabbedStack(RescaleRenderElement::from_element(
stack,
render_location
.to_physical_precise_round(output.current_scale().fractional_scale()),
scale,
)) ))
} } else {
CosmicMappedRenderElement::Window(window) => { None
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()
@ -220,14 +151,12 @@ 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);
if let Some(t) = &self.snapping_zone let snapping_indicator = match &self.snapping_zone {
&& &self.cursor_output == output Some(t) if &self.cursor_output == output => {
{
let base_color = theme.palette.neutral_9; let base_color = theme.palette.neutral_9;
let overlay_geometry = t.overlay_geometry(non_exclusive_geometry, gaps); let overlay_geometry = t.overlay_geometry(non_exclusive_geometry, gaps);
vec![
push( CosmicMappedRenderElement::from(IndicatorShader::element(
IndicatorShader::element(
renderer, renderer,
Key::Window(Usage::SnappingIndicator, self.window.key()), Key::Window(Usage::SnappingIndicator, self.window.key()),
overlay_geometry, overlay_geometry,
@ -245,21 +174,94 @@ impl MoveGrabState {
active_window_hint.green, active_window_hint.green,
active_window_hint.blue, active_window_hint.blue,
], ],
) )),
.into(), CosmicMappedRenderElement::from(BackdropShader::element(
);
push(
BackdropShader::element(
renderer, renderer,
Key::Window(Usage::SnappingIndicator, self.window.key()), Key::Window(Usage::SnappingIndicator, self.window.key()),
t.overlay_geometry(non_exclusive_geometry, gaps), t.overlay_geometry(non_exclusive_geometry, gaps),
theme.radius_s()[0], // TODO: Fix once shaders support 4 corner radii customization theme.radius_s()[0], // TODO: Fix once shaders support 4 corner radii customization
0.4, 0.4,
[base_color.red, base_color.green, base_color.blue], [base_color.red, base_color.green, base_color.blue],
)),
]
}
_ => vec![],
};
let w_elements = self
.window
.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,
location.to_physical_precise_round(output_scale),
output_scale,
1.0,
) )
.into(), })
.chain(p_elements)
.chain(focus_element)
.chain(
w_elements
.into_iter()
.chain(shadow_element)
.map(|elem| match elem {
CosmicMappedRenderElement::Stack(stack) => {
CosmicMappedRenderElement::GrabbedStack(
RescaleRenderElement::from_element(
stack,
render_location.to_physical_precise_round(
output.current_scale().fractional_scale(),
),
scale,
),
) )
} }
CosmicMappedRenderElement::Window(window) => {
CosmicMappedRenderElement::GrabbedWindow(
RescaleRenderElement::from_element(
window,
render_location.to_physical_precise_round(
output.current_scale().fractional_scale(),
),
scale,
),
)
}
x => x,
}),
)
.chain(snapping_indicator)
.map(I::from)
.collect()
} }
pub fn element(&self) -> CosmicMapped { pub fn element(&self) -> CosmicMapped {
@ -439,7 +441,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); indicator.output_enter(output, overlap);
} }
} }
} else if self.window_outputs.remove(output) { } else if self.window_outputs.remove(output) {
@ -453,14 +455,16 @@ impl MoveGrab {
let indicator_location = shell.stacking_indicator(&current_output, self.previous); let indicator_location = shell.stacking_indicator(&current_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 size = geo.size.as_logical(); let element = stack_hover(
let element = StackHover::new(
state.common.event_loop_handle.clone(), state.common.event_loop_handle.clone(),
size, geo.size.as_logical(),
state.common.theme.clone(), state.common.theme.clone(),
); );
for output in &self.window_outputs { for output in &self.window_outputs {
element.output_enter(output); element.output_enter(
output,
Rectangle::from_size(output.geometry().size.as_logical()),
);
} }
(element, geo.loc.as_logical()) (element, geo.loc.as_logical())
}); });

View file

@ -9,15 +9,11 @@ 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::{ backend::renderer::element::{
drm::DrmNode, AsRenderElements, RenderElement,
renderer::element::{
RenderElement,
utils::{Relocate, RelocateRenderElement, RescaleRenderElement}, 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,
output::Output, output::Output,
@ -1350,7 +1346,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(true) { let window_geometry = if mapped.is_maximized(false) {
geometry geometry
} else { } else {
prev.map(|mut rect| { prev.map(|mut rect| {
@ -1443,9 +1439,8 @@ impl FloatingLayout {
&self, &self,
renderer: &mut R, renderer: &mut R,
alpha: f32, alpha: f32,
scanout_node: Option<DrmNode>, ) -> Vec<CosmicMappedRenderElement<R>>
push: &mut dyn FnMut(CosmicMappedRenderElement<R>), where
) where
R: AsGlowRenderer, R: AsGlowRenderer,
R::TextureId: Send + Clone + 'static, R::TextureId: Send + Clone + 'static,
CosmicMappedRenderElement<R>: RenderElement<R>, CosmicMappedRenderElement<R>: RenderElement<R>,
@ -1455,6 +1450,8 @@ 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()
@ -1469,17 +1466,19 @@ 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();
elem.push_popup_render_elements( elements.extend(
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]
@ -1491,9 +1490,8 @@ impl FloatingLayout {
indicator_thickness: u8, indicator_thickness: u8,
alpha: f32, alpha: f32,
theme: &cosmic::theme::CosmicTheme, theme: &cosmic::theme::CosmicTheme,
scanout_node: Option<DrmNode>, ) -> Vec<CosmicMappedRenderElement<R>>
push: &mut dyn FnMut(CosmicMappedRenderElement<R>), where
) where
R: AsGlowRenderer, R: AsGlowRenderer,
R::TextureId: Send + Clone + 'static, R::TextureId: Send + Clone + 'static,
CosmicMappedRenderElement<R>: RenderElement<R>, CosmicMappedRenderElement<R>: RenderElement<R>,
@ -1506,7 +1504,8 @@ 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
@ -1520,9 +1519,32 @@ 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 maybe_map = if let Some(anim) = self.animations.get(elem) { 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 original_geo = anim.previous_geometry(); let original_geo = anim.previous_geometry();
geometry = anim.geometry( geometry = anim.geometry(
output_geometry, output_geometry,
@ -1540,7 +1562,9 @@ impl FloatingLayout {
y: geometry.size.h as f64 / buffer_size.h as f64, y: geometry.size.h as f64 / buffer_size.h as f64,
}; };
Some(move |element| match element { window_elements = window_elements
.into_iter()
.map(|element| match element {
CosmicMappedRenderElement::Stack(elem) => { CosmicMappedRenderElement::Stack(elem) => {
CosmicMappedRenderElement::MovingStack({ CosmicMappedRenderElement::MovingStack({
let rescaled = RescaleRenderElement::from_element( let rescaled = RescaleRenderElement::from_element(
@ -1583,22 +1607,19 @@ impl FloatingLayout {
} }
x => x, x => x,
}) })
} else { .collect();
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); resize.output_enter(output, Rectangle::default() /* unused */);
resize.push_render_elements( window_elements = resize
.render_elements::<CosmicWindowRenderElement<R>>(
renderer, renderer,
resize_geometry resize_geometry
.loc .loc
@ -1606,11 +1627,15 @@ impl FloatingLayout {
.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,
@ -1626,47 +1651,14 @@ impl FloatingLayout {
active_window_hint.blue, active_window_hint.blue,
], ],
); );
push(element.into()); window_elements.insert(0, element.into());
} }
} }
let map_anim = |elem| { elements.extend(window_elements);
if let Some(map) = maybe_map {
map(elem)
} else {
elem
} }
};
elem.push_render_elements( 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

View file

@ -12,11 +12,7 @@ use std::{
use wayland_backend::server::ClientId; use wayland_backend::server::ClientId;
use crate::{ use crate::{
shell::{ shell::{focus::FocusTarget, grabs::fullscreen_items, layout::tiling::PlaceholderType},
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},
@ -103,8 +99,9 @@ use self::zoom::{OutputZoomState, ZoomState};
use self::{ use self::{
element::{ element::{
CosmicWindow, MaximizedState, resize_indicator::ResizeIndicator, CosmicWindow, MaximizedState,
swap_indicator::SwapIndicator, resize_indicator::{ResizeIndicator, resize_indicator},
swap_indicator::{SwapIndicator, swap_indicator},
}, },
focus::target::{KeyboardFocusTarget, PointerFocusTarget}, focus::target::{KeyboardFocusTarget, PointerFocusTarget},
grabs::{ grabs::{
@ -1511,21 +1508,6 @@ 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();
@ -1533,10 +1515,6 @@ 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(
@ -2310,7 +2288,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(SwapIndicator::new(evlh, self.theme.clone())); self.swap_indicator = Some(swap_indicator(evlh, self.theme.clone()));
} }
self.overview_mode = OverviewMode::Started(trigger, Instant::now()); self.overview_mode = OverviewMode::Started(trigger, Instant::now());
} }
@ -2363,7 +2341,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(ResizeIndicator::new( self.resize_indicator = Some(resize_indicator(
direction, direction,
config, config,
evlh, evlh,
@ -2490,12 +2468,6 @@ 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(
@ -2620,36 +2592,15 @@ impl Shell {
pub fn remap_unfullscreened_window( pub fn remap_unfullscreened_window(
&mut self, &mut self,
surface: CosmicSurface, surface: CosmicSurface,
mut state: Option<FullscreenRestoreState>, state: Option<FullscreenRestoreState>,
loop_handle: &LoopHandle<'static, State>, loop_handle: &LoopHandle<'static, State>,
) -> CosmicMapped { ) -> CosmicMapped {
if let Some(FullscreenRestoreState::Stack { state: stack_state }) = &state { let window = CosmicMapped::from(CosmicWindow::new(
if let Some(mapped) = self.mapped().find(|m| **m == stack_state.stack)
&& let Some(stack) = mapped.stack_ref()
{
let idx = stack_state.idx.min(stack.len());
stack.add_window(surface, Some(idx), None);
return mapped.clone();
} else {
state = None;
}
}
let window = 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, surface,
loop_handle.clone(), loop_handle.clone(),
self.theme.clone(), self.theme.clone(),
self.appearance_conf, 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
@ -2686,9 +2637,7 @@ 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 { .. } | FullscreenRestoreState::Stack { .. }) => { Some(FullscreenRestoreState::Sticky { .. }) => unreachable!(),
unreachable!()
}
}; };
let fullscreen_geometry = workspace.output.geometry().to_local(&workspace.output); let fullscreen_geometry = workspace.output.geometry().to_local(&workspace.output);
@ -2803,9 +2752,7 @@ impl Shell {
} }
} }
} }
Some(FullscreenRestoreState::Sticky { .. } | FullscreenRestoreState::Stack { .. }) => { Some(FullscreenRestoreState::Sticky { .. }) => unreachable!(),
unreachable!()
}
} }
window window
@ -2909,12 +2856,10 @@ 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
&& let Some(stack) = focused.stack_ref() && (focused.is_stack() && !is_dialog && !should_be_maximized)
&& !is_dialog
&& !should_be_maximized
&& !(workspace.is_tiled(&focused.active_window()) && floating_exception) && !(workspace.is_tiled(&focused.active_window()) && floating_exception)
{ {
stack.add_window(window, None, None); focused.stack_ref().unwrap().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);
} }
@ -3040,8 +2985,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 let Some(stack) = mapped.stack_ref() { if mapped.is_stack() {
stack.remove_idx(idx) mapped.stack_ref().unwrap().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())
@ -3051,13 +2996,20 @@ impl Shell {
.iter() .iter()
.position(|w| w.windows().any(|s| &s == surface)) .position(|w| w.windows().any(|s| &s == surface))
{ {
if let Some(stack) = set if set
.minimized_windows
.get(idx)
.unwrap()
.mapped()
.is_some_and(CosmicMapped::is_stack)
{
let window = set
.minimized_windows .minimized_windows
.get_mut(idx) .get_mut(idx)
.unwrap() .unwrap()
.mapped_mut() .mapped_mut()
.and_then(|m| m.stack_ref()) .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 {
@ -3288,7 +3240,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(state) = &mut window_state { if let WorkspaceRestoreData::Tiling(Some(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
@ -3304,7 +3256,6 @@ 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
@ -3316,7 +3267,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(previous) => { WorkspaceRestoreData::Floating(Some(previous)) => {
let window = CosmicMapped::from(CosmicWindow::new( let window = CosmicMapped::from(CosmicWindow::new(
window.clone(), window.clone(),
evlh.clone(), evlh.clone(),
@ -3326,7 +3277,7 @@ impl Shell {
window.set_minimized(true); window.set_minimized(true);
MinimizedWindow::Floating { window, previous } MinimizedWindow::Floating { window, previous }
} }
WorkspaceRestoreData::Tiling(previous) => { WorkspaceRestoreData::Tiling(Some(previous)) => {
let window = CosmicMapped::from(CosmicWindow::new( let window = CosmicMapped::from(CosmicWindow::new(
window.clone(), window.clone(),
evlh.clone(), evlh.clone(),
@ -3393,7 +3344,7 @@ impl Shell {
self.appearance_conf, self.appearance_conf,
)); ));
let position = match window_state { let position = match window_state {
WorkspaceRestoreData::Floating(data) => Some( WorkspaceRestoreData::Floating(Some(data)) => Some(
data.position_relative(to_workspace.output.geometry().size.as_logical()), data.position_relative(to_workspace.output.geometry().size.as_logical()),
), ),
_ => None, _ => None,
@ -3493,7 +3444,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(data) => ( WorkspaceRestoreData::Floating(Some(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,
@ -3693,8 +3644,6 @@ 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,
@ -3703,7 +3652,7 @@ impl Shell {
MenuAlignment::CORNER, MenuAlignment::CORNER,
None, None,
evlh.clone(), evlh.clone(),
theme, self.theme.clone(),
); );
Some((grab, Focus::Keep)) Some((grab, Focus::Keep))
@ -3878,13 +3827,13 @@ impl Shell {
} }
(initial_window_location, layer, workspace.handle) (initial_window_location, layer, workspace.handle)
} else { } else if let Some(sticky_layer) = self
let sticky_layer = self
.workspaces .workspaces
.sets .sets
.get_mut(&cursor_output) .get_mut(&cursor_output)
.filter(|set| set.sticky_layer.mapped().any(|m| m == &old_mapped)) .filter(|set| set.sticky_layer.mapped().any(|m| m == &old_mapped))
.map(|set| &mut set.sticky_layer)?; .map(|set| &mut set.sticky_layer)
{
let elem_geo = sticky_layer.element_geometry(&old_mapped).unwrap(); let 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);
@ -3927,6 +3876,8 @@ 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);
@ -4220,13 +4171,14 @@ impl Shell {
.unwrap() .unwrap()
.to_global(&set.output); .to_global(&set.output);
(&mut set.sticky_layer, geometry) (&mut set.sticky_layer, geometry)
} else { } else if let Some(workspace) = self.space_for_mut(mapped) {
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) {
@ -4263,8 +4215,7 @@ impl Shell {
ReleaseMode::Click, ReleaseMode::Click,
) { ) {
grab.into() grab.into()
} else { } else if let Some(ws) = self.space_for_mut(mapped) {
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(
@ -4277,6 +4228,8 @@ 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)))
@ -4513,9 +4466,10 @@ 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 { } else if let Some(workspace) = self.space_for_mut(&mapped) {
let workspace = self.space_for_mut(&mapped)?;
&mut workspace.floating_layer &mut workspace.floating_layer
} else {
return None;
}; };
let grab: ResizeGrab = if let Some(grab) = floating_layer.resize_request( let grab: ResizeGrab = if let Some(grab) = floating_layer.resize_request(
@ -4527,8 +4481,7 @@ impl Shell {
ReleaseMode::NoMouseButtons, ReleaseMode::NoMouseButtons,
) { ) {
grab.into() grab.into()
} else { } else if let Some(ws) = self.space_for_mut(&mapped) {
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)?;
@ -4542,6 +4495,8 @@ impl Shell {
ReleaseMode::NoMouseButtons, ReleaseMode::NoMouseButtons,
) )
.into() .into()
} else {
return None;
}; };
Some((grab, Focus::Clear)) Some((grab, Focus::Clear))
@ -4841,19 +4796,14 @@ 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;
let mut restore_state = None; window = if mapped
let was_stack = mapped.is_stack(); .stack_ref()
window = if let Some(stack) = mapped.stack_ref() .map(|stack| stack.len() > 1)
&& stack.len() > 1 .unwrap_or(false)
{ {
let idx = stack.surfaces().position(|s| &s == surface)?; let stack = mapped.stack_ref().unwrap();
let surface = stack.remove_idx(idx)?; let surface = stack.surfaces().find(|s| s == surface).unwrap();
restore_state = Some(FullscreenRestoreState::Stack { stack.remove_window(&surface);
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
@ -4886,7 +4836,7 @@ impl Shell {
workspace.map_fullscreen( workspace.map_fullscreen(
&window, &window,
&seat, &seat,
Some(restore_state.unwrap_or(FullscreenRestoreState::Sticky { Some(FullscreenRestoreState::Sticky {
output: old_output, output: old_output,
state: FloatingRestoreData { state: FloatingRestoreData {
geometry: from, geometry: from,
@ -4894,12 +4844,10 @@ impl Shell {
was_maximized, was_maximized,
was_snapped: None, was_snapped: None,
}, },
was_stack, }),
})),
Some(from), Some(from),
); );
} else { } else if let Some(workspace) = self.space_for_mut(&mapped) {
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;
@ -4927,26 +4875,23 @@ impl Shell {
&seat, &seat,
match state { match state {
WorkspaceRestoreData::Floating(floating_state) => { WorkspaceRestoreData::Floating(floating_state) => {
Some(FullscreenRestoreState::Floating { floating_state.map(|state| FullscreenRestoreState::Floating {
workspace: handle, workspace: handle,
state: floating_state, state,
was_stack: mapped.is_stack(),
}) })
} }
WorkspaceRestoreData::Tiling(tiling_state) => { WorkspaceRestoreData::Tiling(tiling_state) => {
Some(FullscreenRestoreState::Tiling { tiling_state.map(|state| FullscreenRestoreState::Tiling {
workspace: handle, workspace: handle,
state: tiling_state, 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))
@ -5058,14 +5003,13 @@ 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,
Some(namespace), None,
render_element_states, render_element_states,
) )
}, },

View file

@ -243,12 +243,6 @@ 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;
@ -272,7 +266,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<CursorGeometry>; ) -> Option<(Rectangle<i32, Buffer>, Point<i32, Buffer>)>;
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);
} }
@ -373,7 +367,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<CursorGeometry> { ) -> Option<(Rectangle<i32, Buffer>, Point<i32, Buffer>)> {
let location = loc.into().to_i32_round(); let location = loc.into().to_i32_round();
match self.cursor_image_status() { match self.cursor_image_status() {
@ -392,10 +386,7 @@ 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(CursorGeometry { Some((buffer_geo, (hotspot.x, hotspot.y).into()))
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();
@ -407,13 +398,10 @@ 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(CursorGeometry { Some((
geometry: Rectangle::new( Rectangle::new(location, (frame.width as i32, frame.height as i32).into()),
location, (frame.xhot as i32, frame.yhot as i32).into(),
(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,
} }

View file

@ -1,9 +1,11 @@
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::{BackdropShader, element::AsGlowRenderer}, backend::render::{
BackdropShader,
element::{AsGlowRenderer, FromGlesError},
},
shell::{ shell::{
ANIMATION_DURATION, OverviewMode, SeatMoveGrabState, ANIMATION_DURATION, OverviewMode, SeatMoveGrabState,
layout::{ layout::{
@ -29,15 +31,14 @@ 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 smallvec::SmallVec; use smithay::backend::renderer::element::Kind;
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, texture::TextureRenderElement, utils::RescaleRenderElement, Element, Id, RenderElement, surface::WaylandSurfaceRenderElement,
texture::TextureRenderElement, utils::RescaleRenderElement,
}, },
gles::GlesTexture, gles::GlesTexture,
glow::GlowRenderer, glow::GlowRenderer,
@ -60,7 +61,7 @@ use wayland_backend::server::ClientId;
use super::{ use super::{
CosmicMappedRenderElement, CosmicSurface, ResizeDirection, ResizeMode, CosmicMappedRenderElement, CosmicSurface, ResizeDirection, ResizeMode,
element::{ element::{
CosmicMapped, CosmicMappedKey, MaximizedState, resize_indicator::ResizeIndicator, CosmicMapped, MaximizedState, resize_indicator::ResizeIndicator,
stack::CosmicStackRenderElement, swap_indicator::SwapIndicator, stack::CosmicStackRenderElement, swap_indicator::SwapIndicator,
window::CosmicWindowRenderElement, window::CosmicWindowRenderElement,
}, },
@ -252,20 +253,14 @@ 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,
}, },
} }
@ -275,18 +270,6 @@ 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,
} }
} }
} }
@ -294,9 +277,18 @@ impl FullscreenRestoreState {
#[derive(Debug, Clone)] #[derive(Debug, Clone)]
pub enum WorkspaceRestoreData { pub enum WorkspaceRestoreData {
Fullscreen(Option<FullscreenRestoreData>), Fullscreen(Option<FullscreenRestoreData>),
Tiling(TilingRestoreData), Tiling(Option<TilingRestoreData>),
Floating(FloatingRestoreData), Floating(Option<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)]
@ -328,12 +320,6 @@ 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,
@ -655,30 +641,32 @@ 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(previous) WorkspaceRestoreData::Floating(Some(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(TilingRestoreData { return Some(WorkspaceRestoreData::Tiling(Some(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(FloatingRestoreData { return Some(WorkspaceRestoreData::Floating(Some(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
@ -724,17 +712,19 @@ impl Workspace {
} }
let mapped = self.element_for_surface(surface)?; let mapped = self.element_for_surface(surface)?;
if let Some(stack) = mapped.stack_ref() let maybe_stack = mapped.stack_ref().filter(|s| s.len() > 1);
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);
return Some(( let layer = if self.is_tiled(surface) {
stack.remove_idx(idx)?, ManagedLayer::Tiling
WorkspaceRestoreData::Stack(StackRestoreData { } else {
stack: mapped.key(), ManagedLayer::Floating
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,
@ -785,7 +775,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_floor()) { if !self.output.geometry().contains(location.to_i32_round()) {
return None; return None;
} }
let location = location.to_local(&self.output); let location = location.to_local(&self.output);
@ -834,7 +824,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_floor()) { if !self.output.geometry().contains(location.to_i32_round()) {
return None; return None;
} }
let location = location.to_local(&self.output); let location = location.to_local(&self.output);
@ -884,7 +874,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_floor()) { if !self.output.geometry().contains(location.to_i32_round()) {
return None; return None;
} }
let location = location.to_local(&self.output); let location = location.to_local(&self.output);
@ -934,7 +924,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_floor()) { if !self.output.geometry().contains(location.to_i32_round()) {
return None; return None;
} }
let location = location.to_local(&self.output); let location = location.to_local(&self.output);
@ -1622,9 +1612,8 @@ impl Workspace {
resize_indicator: Option<(ResizeMode, ResizeIndicator)>, resize_indicator: Option<(ResizeMode, ResizeIndicator)>,
indicator_thickness: u8, indicator_thickness: u8,
theme: &CosmicTheme, theme: &CosmicTheme,
scanout_node: Option<DrmNode>, ) -> Result<Vec<WorkspaceRenderElement<R>>, OutputNotMapped>
push: &mut dyn FnMut(WorkspaceRenderElement<R>), where
) where
R: AsGlowRenderer, R: AsGlowRenderer,
R::TextureId: Send + Clone + 'static, R::TextureId: Send + Clone + 'static,
CosmicMappedRenderElement<R>: RenderElement<R>, CosmicMappedRenderElement<R>: RenderElement<R>,
@ -1632,17 +1621,19 @@ 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 mut fullscreen_elements = SmallVec::<[WorkspaceRenderElement<R>; 2]>::new_const(); let render_fullscreen = |fullscreen: &FullscreenSurface,
let mut render_fullscreen = renderer: &mut R,
|fullscreen: &FullscreenSurface, renderer: &mut R, output_scale: f64| { 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
@ -1686,55 +1677,52 @@ impl Workspace {
.as_logical() .as_logical()
.to_physical_precise_round(output_scale); .to_physical_precise_round(output_scale);
// Only rescale geometry when animating
let animation_rescale = |elem| {
if fullscreen.is_animating() {
let fullscreen_geo = fullscreen.surface.0.geometry(); let fullscreen_geo = fullscreen.surface.0.geometry();
let scale = Scale { let scale = Scale {
x: target_geo.size.w as f64 / fullscreen_geo.size.w as f64, 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, y: target_geo.size.h as f64 / fullscreen_geo.size.h as f64,
}; };
// Only rescale geometry when animating
let animation_rescale = |elem| {
if fullscreen.is_animating() {
RescaleRenderElement::from_element(elem, render_loc, scale).into() RescaleRenderElement::from_element(elem, render_loc, scale).into()
} else { } else {
Into::<WorkspaceRenderElement<_>>::into(elem) Into::<WorkspaceRenderElement<_>>::into(elem)
} }
}; };
let mut fullscreen_push = |elem: SurfaceRenderElement<R>| { fullscreen
if fullscreen_focused { .surface
push(animation_rescale(elem.into())) .render_elements::<R, CosmicWindowRenderElement<R>>(
} 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, )
false, .into_iter()
[0, 0, 0, 0], .map(animation_rescale)
0, .collect::<Vec<_>>()
&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 {
render_fullscreen(fs, renderer, output_scale) fullscreen_elements.extend(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) {
render_fullscreen(fs, renderer, output_scale); fullscreen_elements.extend(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()
@ -1765,21 +1753,28 @@ impl Workspace {
OverviewMode::None => 1.0, OverviewMode::None => 1.0,
}; };
self.floating_layer.render( elements.extend(
self.floating_layer
.render::<R>(
renderer, renderer,
render_focus
.then(|| {
focused.as_ref().and_then(|target| { focused.as_ref().and_then(|target| {
if let FocusTarget::Window(mapped) = target { if let FocusTarget::Window(mapped) = target {
Some(mapped) Some(mapped)
} else { } else {
None None
} }
}), })
})
.flatten(),
resize_indicator.clone(), resize_indicator.clone(),
indicator_thickness, indicator_thickness,
alpha, alpha,
theme, theme,
scanout_node, )
&mut |elem| push(elem.into()), .into_iter()
.map(WorkspaceRenderElement::from),
); );
let alpha = match &overview.0 { let alpha = match &overview.0 {
@ -1796,31 +1791,23 @@ impl Workspace {
}; };
//tiling surfaces //tiling surfaces
self.tiling_layer.render( elements.extend(
self.tiling_layer
.render::<R>(
renderer, renderer,
render_focus.then_some(last_active_seat), render_focus.then_some(last_active_seat),
render_focus
.then(|| {
focused.as_ref().and_then(|target| {
if let FocusTarget::Window(mapped) = target {
Some(mapped)
} else {
None
}
})
})
.flatten(),
zone, zone,
overview, overview,
resize_indicator, resize_indicator,
indicator_thickness, indicator_thickness,
theme, theme,
scanout_node, )?
&mut |elem| push(elem.into()), .into_iter()
.map(WorkspaceRenderElement::from),
); );
if let Some(alpha) = alpha { if let Some(alpha) = alpha {
push( elements.push(
Into::<CosmicMappedRenderElement<R>>::into(BackdropShader::element( Into::<CosmicMappedRenderElement<R>>::into(BackdropShader::element(
renderer, renderer,
self.backdrop_id.clone(), self.backdrop_id.clone(),
@ -1834,9 +1821,11 @@ impl Workspace {
} }
} }
for elem in fullscreen_elements { if !matches!(focused, Some(FocusTarget::Fullscreen(_))) {
push(elem); elements.extend(fullscreen_elements.into_iter());
} }
Ok(elements)
} }
#[profiling::function] #[profiling::function]
@ -1847,9 +1836,8 @@ 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,
scanout_node: Option<DrmNode>, ) -> Result<Vec<WorkspaceRenderElement<R>>, OutputNotMapped>
push: &mut dyn FnMut(WorkspaceRenderElement<R>), where
) where
R: AsGlowRenderer, R: AsGlowRenderer,
R::TextureId: Send + Clone + 'static, R::TextureId: Send + Clone + 'static,
CosmicMappedRenderElement<R>: RenderElement<R>, CosmicMappedRenderElement<R>: RenderElement<R>,
@ -1857,6 +1845,8 @@ 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);
@ -1911,14 +1901,17 @@ impl Workspace {
.as_logical() .as_logical()
.to_physical_precise_round(output_scale); .to_physical_precise_round(output_scale);
fullscreen.surface.push_popup_render_elements( elements.extend(
fullscreen
.surface
.popup_render_elements::<R, CosmicWindowRenderElement<R>>(
renderer, renderer,
render_loc, render_loc,
output_scale.into(), output_scale.into(),
alpha, alpha,
scanout_node, )
0, .into_iter()
&mut |elem| push(WorkspaceRenderElement::FullscreenPopup(elem.into())), .map(Into::into),
); );
} }
@ -1952,20 +1945,29 @@ 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
self.tiling_layer.render_popups( elements.extend(
self.tiling_layer
.render_popups::<R>(
renderer, renderer,
render_focus.then_some(last_active_seat), render_focus.then_some(last_active_seat),
zone, zone,
overview, overview,
theme, theme,
scanout_node, )?
&mut |elem| push(elem.into()), .into_iter()
.map(WorkspaceRenderElement::from),
); );
} }
Ok(elements)
} }
} }
@ -1984,10 +1986,9 @@ pub struct OutputNotMapped;
pub enum WorkspaceRenderElement<R> pub enum WorkspaceRenderElement<R>
where where
R: AsGlowRenderer, R: AsGlowRenderer,
R::TextureId: Send + 'static, R::TextureId: 'static,
{ {
OverrideRedirect(SurfaceRenderElement<R>), OverrideRedirect(WaylandSurfaceRenderElement<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>),
@ -1997,12 +1998,11 @@ where
impl<R> Element for WorkspaceRenderElement<R> impl<R> Element for WorkspaceRenderElement<R>
where where
R: AsGlowRenderer, R: AsGlowRenderer,
R::TextureId: Send + 'static, R::TextureId: '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,7 +2013,6 @@ 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(),
@ -2024,7 +2023,6 @@ 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(),
@ -2035,7 +2033,6 @@ 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),
@ -2046,7 +2043,6 @@ 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),
@ -2057,7 +2053,6 @@ 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(),
@ -2072,7 +2067,6 @@ 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),
@ -2083,7 +2077,6 @@ 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),
@ -2094,7 +2087,6 @@ 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(),
@ -2105,7 +2097,6 @@ 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(),
@ -2116,7 +2107,6 @@ 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(),
@ -2128,7 +2118,8 @@ where
impl<R> RenderElement<R> for WorkspaceRenderElement<R> impl<R> RenderElement<R> for WorkspaceRenderElement<R>
where where
R: AsGlowRenderer, R: AsGlowRenderer,
R::TextureId: Send + 'static, R::TextureId: 'static,
R::Error: FromGlesError,
{ {
fn draw( fn draw(
&self, &self,
@ -2143,9 +2134,6 @@ 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)
} }
@ -2164,7 +2152,7 @@ where
opaque_regions, opaque_regions,
cache, cache,
) )
.map_err(R::from_gles_error), .map_err(FromGlesError::from_gles_error),
} }
} }
@ -2174,7 +2162,6 @@ 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),
@ -2195,9 +2182,6 @@ 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)
} }
@ -2215,7 +2199,7 @@ where
dst, dst,
cache, cache,
) )
.map_err(R::from_gles_error) .map_err(FromGlesError::from_gles_error)
} }
} }
} }
@ -2224,7 +2208,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: Send + 'static, R::TextureId: 'static,
CosmicMappedRenderElement<R>: RenderElement<R>, CosmicMappedRenderElement<R>: RenderElement<R>,
{ {
fn from(elem: RescaleRenderElement<CosmicWindowRenderElement<R>>) -> Self { fn from(elem: RescaleRenderElement<CosmicWindowRenderElement<R>>) -> Self {
@ -2235,7 +2219,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: Send + 'static, R::TextureId: 'static,
CosmicMappedRenderElement<R>: RenderElement<R>, CosmicMappedRenderElement<R>: RenderElement<R>,
{ {
fn from(elem: CosmicWindowRenderElement<R>) -> Self { fn from(elem: CosmicWindowRenderElement<R>) -> Self {
@ -2243,32 +2227,21 @@ where
} }
} }
impl<R> From<SurfaceRenderElement<R>> for WorkspaceRenderElement<R> impl<R> From<WaylandSurfaceRenderElement<R>> for WorkspaceRenderElement<R>
where where
R: AsGlowRenderer, R: AsGlowRenderer,
R::TextureId: Send + 'static, R::TextureId: 'static,
CosmicMappedRenderElement<R>: RenderElement<R>, CosmicMappedRenderElement<R>: RenderElement<R>,
{ {
fn from(elem: SurfaceRenderElement<R>) -> Self { fn from(elem: WaylandSurfaceRenderElement<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: Send + 'static, R::TextureId: 'static,
CosmicMappedRenderElement<R>: RenderElement<R>, CosmicMappedRenderElement<R>: RenderElement<R>,
{ {
fn from(elem: CosmicMappedRenderElement<R>) -> Self { fn from(elem: CosmicMappedRenderElement<R>) -> Self {
@ -2279,7 +2252,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: Send + 'static, R::TextureId: 'static,
CosmicMappedRenderElement<R>: RenderElement<R>, CosmicMappedRenderElement<R>: RenderElement<R>,
{ {
fn from(elem: TextureRenderElement<GlesTexture>) -> Self { fn from(elem: TextureRenderElement<GlesTexture>) -> Self {

View file

@ -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, widget as iced_widget}, iced::{Alignment, Background, Border, Length, alignment::Vertical},
theme, iced_widget, 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, backend::renderer::{ImportMem, Renderer, element::AsRenderElements},
desktop::space::SpaceElement, desktop::space::SpaceElement,
input::{ input::{
Seat, Seat,
@ -27,15 +27,14 @@ use smithay::{
}, },
}, },
output::Output, output::Output,
utils::{FrameExtents, IsAlive, Point, Rectangle, Serial, Size}, utils::{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, IcedRenderElement, Program}, iced::{IcedElement, Program},
prelude::*, prelude::*,
tween::EasePoint, tween::EasePoint,
}, },
@ -72,17 +71,45 @@ impl OutputZoomState {
increment: u32, increment: u32,
movement: ZoomMovement, movement: ZoomMovement,
loop_handle: LoopHandle<'static, State>, loop_handle: LoopHandle<'static, State>,
mut theme: cosmic::Theme, 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.min(output_geometry.size.w.round() as i32); size.w = (size.w + 32/*TODO: figure out why iced is calculating too little*/)
.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));
@ -165,13 +192,10 @@ impl OutputZoomState {
}); });
} }
fn render<R>( fn render<R, C>(&mut self, renderer: &mut R, output: &Output) -> Vec<C>
&mut self, where
renderer: &mut R, C: From<<IcedElement<ZoomProgram> as AsRenderElements<R>>::RenderElement>,
output: &Output, R: Renderer + ImportMem,
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();
@ -184,17 +208,8 @@ impl OutputZoomState {
.to_physical(scale.fractional_scale()) .to_physical(scale.fractional_scale())
.to_i32_round(); .to_i32_round();
self.element.push_render_elements(
renderer,
location,
scale.fractional_scale().into(),
1.0,
self.element self.element
.with_theme(|theme| theme.cosmic().radius_s()) .render_elements(renderer, location, scale.fractional_scale().into(), 1.0)
.map(|x| x.round() as u8),
push,
None,
)
} }
} }
@ -241,101 +256,86 @@ 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().to_local(output); let output_geometry = output.geometry().to_f64();
let zoomed_output_geometry = output.zoomed_geometry().unwrap().to_f64().to_local(output); let mut zoomed_output_geometry = output.zoomed_geometry().unwrap().to_f64();
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();
let level = output_state_ref.current_level(); // animate movement type changes
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 => { ZoomMovement::Continuously => output_state_ref.focal_point = cursor_position,
// Focal point is the cursor position
output_state_ref.focal_point = cursor_position;
}
ZoomMovement::OnEdge => { ZoomMovement::OnEdge => {
if is_level_change { if !zoomed_output_geometry
// Ensure the cursor doesn't disappear off screen .overlaps_or_touches(Rectangle::new(original_position, Size::from((16., 16.))))
output_state_ref.focal_point = cursor_position; {
return; zoomed_output_geometry.loc = cursor_position.to_global(output)
} - zoomed_output_geometry.size.downscale(2.).to_point();
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(output_state_ref.level)
let margins = FrameExtents::new(margin_size, margin_size, margin_size, margin_size); .to_global(output);
let inner_rect = zoomed_output_geometry - margins;
if inner_rect.contains(cursor_position) {
// Do not move if cursor within margins
return;
}
// Compute dx and dy to move the zoomed output based on cursor distance outside margin(s)
let dx = if cursor_position.x < inner_rect.loc.x {
cursor_position.x - inner_rect.loc.x
} else if cursor_position.x > inner_rect.loc.x + inner_rect.size.w {
cursor_position.x - (inner_rect.loc.x + inner_rect.size.w)
} else {
0.0
};
let dy = if cursor_position.y < inner_rect.loc.y {
cursor_position.y - inner_rect.loc.y
} else if cursor_position.y > inner_rect.loc.y + inner_rect.size.h {
cursor_position.y - (inner_rect.loc.y + inner_rect.size.h)
} else {
0.0
};
let mut focal_point = output_state_ref.focal_point + Point::new(dx, dy);
// Clamp the final focal point to output geometry
focal_point.x = focal_point.x.clamp( focal_point.x = focal_point.x.clamp(
output_geometry.loc.x, output_geometry.loc.x,
output_geometry.loc.x + output_geometry.size.w - 1.0, output_geometry.loc.x + output_geometry.size.w - 1.,
); );
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.0, 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 = focal_point; output_state_ref.focal_point += diff.as_logical().as_local();
}
} }
ZoomMovement::Centered => { ZoomMovement::Centered => {
let center = (output_geometry.size / 2.).to_point(); zoomed_output_geometry.loc = cursor_position.to_global(output)
- zoomed_output_geometry.size.downscale(2.).to_point();
if level == 1.0 { let mut focal_point = zoomed_output_geometry
// Prevent focal point jumping to top-left corner (0, 0) on zoom out .loc
output_state_ref.focal_point = center; .to_local(output)
return; .upscale(
} (output_geometry.size.w
/ (output_geometry.size.w - zoomed_output_geometry.size.w)
// Compute translation to keep cursor at center of screen .max(f64::EPSILON))
let mut tx = center.x - cursor_position.x * level; .max(1.),
let mut ty = center.y - cursor_position.y * level; )
.to_global(output);
// Clamp translation to keep viewport within screen bounds focal_point.x = focal_point.x.clamp(
tx = tx.clamp(output_geometry.size.w * (1.0 - level), 0.0); output_geometry.loc.x,
ty = ty.clamp(output_geometry.size.h * (1.0 - level), 0.0); output_geometry.loc.x + output_geometry.size.w - 1.,
);
// Convert translation back to focal point: T = F * (1 - level) focal_point.y = focal_point.y.clamp(
output_state_ref.focal_point = output_geometry.loc.y,
Point::from((tx / (1.0 - level), ty / (1.0 - level))); output_geometry.loc.y + output_geometry.size.h - 1.,
);
output_state_ref.focal_point = focal_point.to_local(output);
} }
} }
} }
@ -382,13 +382,14 @@ impl ZoomState {
None None
} }
pub fn render<R>(renderer: &mut R, output: &Output, push: &mut dyn FnMut(IcedRenderElement<R>)) pub fn render<R, C>(renderer: &mut R, output: &Output) -> Vec<C>
where where
R: AsGlowRenderer + ImportMem, C: From<<IcedElement<ZoomProgram> as AsRenderElements<R>>::RenderElement>,
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, push) output_state.lock().unwrap().render(renderer, output)
} }
} }
@ -505,7 +506,6 @@ 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,8 +581,6 @@ 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,
@ -695,7 +693,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(),
theme, state.common.theme.clone(),
); );
std::mem::drop(shell); std::mem::drop(shell);
@ -748,8 +746,6 @@ 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,
@ -782,7 +778,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(),
theme, state.common.theme.clone(),
); );
std::mem::drop(shell); std::mem::drop(shell);

View file

@ -19,7 +19,6 @@ 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,
@ -73,7 +72,6 @@ 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},
@ -127,7 +125,7 @@ use std::{
cmp::min, cmp::min,
collections::HashSet, collections::HashSet,
ffi::OsString, ffi::OsString,
process::{Child, Command}, process::Child,
sync::{Arc, LazyLock, Once, atomic::AtomicBool}, sync::{Arc, LazyLock, Once, atomic::AtomicBool},
time::{Duration, Instant}, time::{Duration, Instant},
}; };
@ -225,7 +223,6 @@ 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);
@ -282,8 +279,6 @@ 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,
@ -634,7 +629,6 @@ 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)
@ -688,9 +682,6 @@ 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);
@ -795,14 +786,12 @@ 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,
@ -812,7 +801,6 @@ impl State {
backend: BackendData::Unset, backend: BackendData::Unset,
ready: Once::new(), ready: Once::new(),
last_refresh: LastRefresh::None, last_refresh: LastRefresh::None,
kiosk_command,
} }
} }
@ -942,30 +930,27 @@ impl Common {
render_element_states: &RenderElementStates, render_element_states: &RenderElementStates,
) { ) {
let shell = self.shell.read(); let shell = self.shell.read();
let processor = |namespace: Option<usize>| { let processor = |surface: &WlSurface, states: &SurfaceData| {
move |surface: &WlSurface, states: &SurfaceData| {
let primary_scanout_output = update_surface_primary_scanout_output( let primary_scanout_output = update_surface_primary_scanout_output(
surface, surface,
output, output,
states, states,
namespace, None,
render_element_states, render_element_states,
primary_scanout_output_compare, primary_scanout_output_compare,
); );
if let Some(output) = primary_scanout_output { if let Some(output) = primary_scanout_output {
with_fractional_scale(states, |fraction_scale| { with_fractional_scale(states, |fraction_scale| {
fraction_scale fraction_scale.set_preferred_scale(output.current_scale().fractional_scale());
.set_preferred_scale(output.current_scale().fractional_scale());
}); });
} }
}
}; };
// lock surface // lock surface
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(None)) with_surfaces_surface_tree(lock_surface.wl_surface(), processor)
} }
for seat in shell for seat in shell
@ -977,7 +962,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(None)); with_surfaces_surface_tree(&wl_surface, processor);
} }
// grabs // grabs
@ -985,12 +970,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(None)); window.with_surfaces(processor);
} }
} }
if let Some(icon) = get_dnd_icon(seat) { if let Some(icon) = get_dnd_icon(seat) {
with_surfaces_surface_tree(&icon.surface, processor(None)); with_surfaces_surface_tree(&icon.surface, processor);
} }
} }
@ -998,7 +983,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(None)); window.with_surfaces(processor);
} }
}); });
} }
@ -1006,16 +991,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(None)); fs.surface.with_surfaces(processor);
} }
space.mapped().for_each(|mapped| { space.mapped().for_each(|mapped| {
for (window, _) in mapped.windows() { for (window, _) in mapped.windows() {
window.with_surfaces(processor(None)); window.with_surfaces(processor);
} }
}); });
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(None)); window.with_surfaces(processor);
} }
}) })
} }
@ -1023,16 +1008,15 @@ 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(None)); with_surfaces_surface_tree(&wl_surface, processor);
} }
}); });
// 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(Some(namespace))); layer_surface.with_surfaces(processor);
} }
} }
} }

View file

@ -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");
} }
} }
}

View file

@ -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, ImportMem, Renderer,
element::{ element::{
Kind, AsRenderElements, Kind,
memory::{MemoryRenderBuffer, MemoryRenderBufferRenderElement}, memory::{MemoryRenderBuffer, MemoryRenderBufferRenderElement},
}, },
}, },
@ -57,20 +57,13 @@ 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::{ use crate::utils::iced::state::State;
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"));
@ -180,7 +173,6 @@ 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>,
@ -233,7 +225,6 @@ 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,
@ -319,7 +310,6 @@ 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,
@ -352,6 +342,7 @@ 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)
@ -395,11 +386,6 @@ 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();
@ -924,20 +910,21 @@ impl<P: Program + Send + 'static> SpaceElement for IcedElement<P> {
} }
} }
impl<P: Program + Send + 'static> IcedElement<P> { impl<P, R> AsRenderElements<R> for IcedElement<P>
pub fn push_render_elements<R>( where
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,
mut radii: [u8; 4], ) -> Vec<C> {
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;
@ -1068,51 +1055,11 @@ impl<P: Program + Send + 'static> IcedElement<P> {
Kind::Unspecified, Kind::Unspecified,
) { ) {
Ok(buffer) => { Ok(buffer) => {
push_above(buffer.into()); return vec![C::from(buffer)];
} }
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;
} }
Vec::new()
match BlurElement::from_state(
renderer,
&mut internal_ref.blur,
Rectangle::new(
location
.to_f64()
.to_logical(scale)
.upscale(internal_ref.additional_scale),
internal_ref
.size
.to_f64()
.upscale(internal_ref.additional_scale)
.to_i32_round(),
),
scale.x,
radii,
(internal_ref.theme.cosmic().frosted as u8 + 1) as usize,
) {
Ok(Some(elem)) => {
if let Some(push_below) = push_below {
push_below(elem.into())
} else {
push_above(elem.into())
} }
} }
Ok(None) => {}
Err(err) => tracing::warn!("Blur elem error: {:?}", err),
}
}
}
}
}
render_elements! {
pub IcedRenderElement<R> where R: ImportMem + AsGlowRenderer, R::TextureId: Send;
UI=MemoryRenderBufferRenderElement<R>,
Blur=BlurElement,
}

View file

@ -1,16 +1,12 @@
use super::IcedProgram as Program; use super::IcedProgram as Program;
use cosmic::iced::{ use cosmic::iced::core::event::{self, Event};
core::{ use cosmic::iced::core::mouse;
self as iced_core, Clipboard, Size, use cosmic::iced::core::renderer;
event::{self, Event}, use cosmic::iced::core::widget::operation::{self, Operation};
mouse, renderer, use cosmic::iced::core::{Clipboard, Size};
widget::operation::{self, Operation}, use cosmic::iced_core;
}, use cosmic::iced_runtime::Task;
runtime::{ use cosmic::iced_runtime::user_interface::{self, UserInterface};
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.
@ -132,7 +128,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
{ {
@ -158,7 +154,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
{ {

View file

@ -3,7 +3,9 @@ use smithay::{
backend::{ backend::{
allocator::Fourcc, allocator::Fourcc,
renderer::{ renderer::{
ExportMem, ImportAll, Offscreen, Renderer, damage::OutputDamageTracker, ExportMem, ImportAll, Offscreen, Renderer,
damage::OutputDamageTracker,
element::{AsRenderElements, surface::WaylandSurfaceRenderElement},
gles::GlesRenderbuffer, gles::GlesRenderbuffer,
}, },
}, },
@ -14,7 +16,7 @@ use smithay::{
use tracing::warn; use tracing::warn;
use crate::{ use crate::{
backend::render::{RendererRef, element::AsGlowRenderer}, backend::render::RendererRef,
shell::element::CosmicSurface, shell::element::CosmicSurface,
state::{State, advertised_node_for_surface}, state::{State, advertised_node_for_surface},
}; };
@ -22,24 +24,17 @@ 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 + AsGlowRenderer, R: Renderer + ImportAll + Offscreen<GlesRenderbuffer> + ExportMem,
R::TextureId: Send + Clone + 'static, R::TextureId: 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 mut elements = Vec::new(); let elements = AsRenderElements::<R>::render_elements::<WaylandSurfaceRenderElement<R>>(
window.push_render_elements( window,
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

View file

@ -1,59 +0,0 @@
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();
})
}
}

View file

@ -314,7 +314,8 @@ impl CompositorHandler for State {
.then(|| state.element()) .then(|| state.element())
}); });
if let Some(window) = moved_window { if let Some(window) = moved_window {
if let Some(stack) = window.stack_ref() { if window.is_stack() {
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()

View file

@ -1,9 +1,7 @@
use smithay::utils::{Logical, Rectangle, Size}; use cosmic_protocols::corner_radius::v1::server::cosmic_corner_radius_toplevel_v1;
use smithay::wayland::compositor::SurfaceData;
use crate::wayland::protocols::corner_radius::{ use crate::wayland::protocols::corner_radius::{
CacheableCorners, CacheablePadding, CornerRadiusData, CornerRadiusHandler, CornerRadiusState, CornerRadiusData, CornerRadiusHandler, CornerRadiusState, delegate_corner_radius,
delegate_corner_radius,
}; };
use crate::state::State; use crate::state::State;
@ -13,63 +11,39 @@ impl CornerRadiusHandler for State {
&mut self.common.corner_radius_state &mut self.common.corner_radius_state
} }
fn set_corner_radius(&mut self, _data: &CornerRadiusData) {} fn set_corner_radius(
&mut self,
fn unset_corner_radius(&mut self, _data: &CornerRadiusData) {} _: &cosmic_corner_radius_toplevel_v1::CosmicCornerRadiusToplevelV1,
data: &CornerRadiusData,
fn set_padding(&mut self, _data: &CornerRadiusData) {} ) {
if force_redraw(self, data).is_none() {
fn unset_padding(&mut self, _data: &CornerRadiusData) {} tracing::warn!("Failed to force redraw for corner radius change.");
}
} }
pub fn surface_corners(states: &SurfaceData, size: Size<i32, Logical>) -> Option<[u8; 4]> { fn unset_corner_radius(
let mut guard = states.cached_state.get::<CacheableCorners>(); &mut self,
_: &cosmic_corner_radius_toplevel_v1::CosmicCornerRadiusToplevelV1,
// guard against corner radius being too large, potentially disconnecting the outline data: &CornerRadiusData,
let half_min_dim = u8::try_from(size.w.min(size.h) / 2).unwrap_or(u8::MAX); ) {
let corners = guard.current().0?; if force_redraw(self, data).is_none() {
tracing::warn!("Failed to force redraw for corner radius reset.");
Some([ }
u8::try_from(corners.top_left) }
.unwrap_or(u8::MAX)
.min(half_min_dim),
u8::try_from(corners.top_right)
.unwrap_or(u8::MAX)
.min(half_min_dim),
u8::try_from(corners.bottom_right)
.unwrap_or(u8::MAX)
.min(half_min_dim),
u8::try_from(corners.bottom_left)
.unwrap_or(u8::MAX)
.min(half_min_dim),
])
} }
pub fn surface_padding(states: &SurfaceData, size: Size<i32, Logical>) -> Option<[i32; 4]> { fn force_redraw(state: &mut State, data: &CornerRadiusData) -> Option<()> {
let mut guard = states.cached_state.get::<CacheablePadding>(); let guard = data.lock().unwrap();
let padding = guard.current().0?; let toplevel = guard.toplevel.upgrade().ok()?;
// guard against padding being too large let surface = state.common.xdg_shell_state.get_toplevel(&toplevel)?;
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( let guard = state.common.shell.read();
mut rect: Rectangle<i32, Logical>, let output = guard.visible_output_for_surface(surface.wl_surface())?;
padding: &[i32; 4],
) -> Option<Rectangle<i32, Logical>> { state.backend.schedule_render(output);
rect.size.h = rect.size.h.checked_sub(padding[0])?; Some(())
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);

View file

@ -14,7 +14,6 @@ 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},
@ -31,7 +30,7 @@ use smithay::{
}; };
use crate::{ use crate::{
shell::{CosmicSurface, CursorGeometry, Shell}, shell::CosmicSurface,
state::{BackendData, State}, state::{BackendData, State},
utils::prelude::{ utils::prelude::{
OutputExt, PointExt, PointGlobalExt, PointLocalExt, RectExt, RectLocalExt, SeatExt, OutputExt, PointExt, PointGlobalExt, PointLocalExt, RectExt, RectLocalExt, SeatExt,
@ -45,36 +44,6 @@ 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
@ -104,12 +73,26 @@ 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 shell = self.common.shell.read(); let size = if let Some((geometry, _)) = self
let seat = seat_for_wl_pointer(&shell, pointer)?; .common
let cursor_geometry = seat.cursor_geometry((0.0, 0.0), self.common.clock.now()); .shell
Some(cursor_capture_constraints(cursor_geometry)) .read()
.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) {
@ -166,23 +149,20 @@ 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 shell = self.common.shell.read(); let seat = self.common.shell.read().seats.last_active().clone();
if let Some(seat) = seat_for_wl_pointer(&shell, &session.pointer()) {
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 (pointer_size, hotspot) = if let Some(CursorGeometry { geometry, hotspot }) = let (pointer_size, hotspot) = if let Some((geometry, hotspot)) =
seat.cursor_geometry((0.0, 0.0), self.common.clock.now()) seat.cursor_geometry((0.0, 0.0), self.common.clock.now())
{ {
(geometry.size, hotspot) (geometry.size, hotspot)
} else { } else {
(default_cursor_size(), Point::from((0, 0))) (Size::from((64, 64)), Point::from((0, 0)))
}; };
(pointer_loc, pointer_size, hotspot) (pointer_loc, pointer_size, hotspot)
} else {
(Point::new(0, 0), default_cursor_size(), Point::from((0, 0)))
}
}; };
session.user_data().insert_if_missing_threadsafe(|| { session.user_data().insert_if_missing_threadsafe(|| {
@ -319,12 +299,9 @@ impl ImageCopyCaptureHandler for State {
return; return;
} }
let shell = self.common.shell.read(); let seat = self.common.shell.read().seats.last_active().clone();
if let Some(seat) = seat_for_wl_pointer(&shell, &session.pointer()).cloned() {
drop(shell);
render_cursor_to_buffer(self, session, frame, &seat); render_cursor_to_buffer(self, session, frame, &seat);
} }
}
fn frame_aborted(&mut self, frame: FrameRef) { fn frame_aborted(&mut self, frame: FrameRef) {
let shell = self.common.shell.read(); let shell = self.common.shell.read();

View file

@ -9,7 +9,8 @@ use smithay::{
buffer_dimensions, buffer_type, buffer_dimensions, buffer_type,
damage::{Error as DTError, OutputDamageTracker, RenderOutputResult}, damage::{Error as DTError, OutputDamageTracker, RenderOutputResult},
element::{ element::{
RenderElement, UnderlyingStorage, AsRenderElements, RenderElement,
surface::WaylandSurfaceRenderElement,
utils::{Relocate, RelocateRenderElement}, utils::{Relocate, RelocateRenderElement},
}, },
gles::{GlesError, GlesRenderbuffer}, gles::{GlesError, GlesRenderbuffer},
@ -20,14 +21,16 @@ 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, reexports::wayland_server::protocol::{wl_buffer::WlBuffer, wl_shm::Format as ShmFormat},
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::{CaptureFailureReason, CursorSessionRef, Frame, SessionRef}, image_copy_capture::{
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},
}, },
@ -37,11 +40,9 @@ use tracing::warn;
use crate::{ use crate::{
backend::render::{ backend::render::{
CursorMode, ElementFilter, RendererRef, CursorMode, ElementFilter, RendererRef, cursor,
cursor::{self, CursorRenderElement}, element::{AsGlowRenderer, CosmicElement, DamageElement, FromGlesError},
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},
@ -54,34 +55,12 @@ use crate::{
}, },
}; };
use super::{ use super::{super::data_device::get_dnd_icon, user_data::SessionHolder};
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 {
@ -124,10 +103,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 + AsGlowRenderer, R: ExportMem,
R::Error: FromGlesError,
{ {
let Some(damage) = damage else { let Some(damage) = damage else {
frame.success( frame.success(
@ -185,7 +164,7 @@ where
Ok(()) Ok(())
}) })
.map_err(|err| R::from_gles_error(GlesError::BufferAccessError(err))) .map_err(|err| R::Error::from_gles_error(GlesError::BufferAccessError(err)))
.and_then(|x| x) .and_then(|x| x)
{ {
frame.fail(CaptureFailureReason::Unknown); frame.fail(CaptureFailureReason::Unknown);
@ -203,7 +182,6 @@ where
}) })
.collect(), .collect(),
sync, sync,
_buffers: buffers,
})) }))
} }
@ -216,6 +194,7 @@ 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,
@ -223,13 +202,7 @@ where
&'d mut OutputDamageTracker, &'d mut OutputDamageTracker,
usize, usize,
Vec<Rectangle<i32, BufferCoords>>, Vec<Rectangle<i32, BufferCoords>>,
) -> Result< ) -> Result<RenderOutputResult<'d>, DTError<R::Error>>,
(
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();
@ -273,25 +246,30 @@ 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 (result, buffers) = render_fn( let res = render_fn(
&frame.buffer(), &frame.buffer(),
renderer, renderer,
fb.as_mut(), fb.as_mut(),
dt, dt,
age, age,
frame.damage(), frame.damage(),
)?; );
submit_buffer( match res {
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(
@ -339,16 +317,11 @@ pub fn render_workspace_to_buffer(
common: &mut Common, common: &mut Common,
output: &Output, output: &Output,
handle: (WorkspaceHandle, usize), handle: (WorkspaceHandle, usize),
) -> Result< ) -> Result<RenderOutputResult<'d>, DTError<R::Error>>
(
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>,
@ -385,7 +358,7 @@ pub fn render_workspace_to_buffer(
.collect() .collect()
}); });
let (res, elements) = if let Ok(dmabuf) = get_dmabuf(buffer) { 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(
@ -403,7 +376,8 @@ 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(
@ -421,12 +395,9 @@ 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();
@ -533,10 +504,9 @@ pub fn render_workspace_to_buffer(
} }
smithay::render_elements! { smithay::render_elements! {
pub WindowCaptureElement<R> where R: ImportAll + ImportMem + AsGlowRenderer, R::TextureId: Send; pub WindowCaptureElement<R> where R: ImportAll + ImportMem;
WaylandElement=SurfaceRenderElement<R>, WaylandElement=WaylandSurfaceRenderElement<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(
@ -580,20 +550,15 @@ 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< ) -> Result<RenderOutputResult<'d>, DTError<R::Error>>
(
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 mut elements: Vec<_> = additional_damage let additional_damage_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(
@ -604,21 +569,15 @@ 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)
@ -632,8 +591,11 @@ 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,
@ -641,71 +603,55 @@ pub fn render_window_to_buffer(
1.0.into(), 1.0.into(),
1.0, 1.0,
common.clock.now(), common.clock.now(),
blur_strength,
true, true,
&mut |elem, hotspot| { )
elements.push(WindowCaptureElement::CursorElement( .into_iter()
RelocateRenderElement::from_element( .map(|(elem, hotspot)| {
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| {
elements.push(
RelocateRenderElement::from_element(
CursorRenderElement::Surface(elem),
Point::new(0, 0),
Relocate::Relative,
) )
.into(), .into_iter()
) .map(WindowCaptureElement::from),
},
); );
} }
} }
toplevel.push_render_elements( elements.extend(AsRenderElements::<R>::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,
);
let res = if let Ok(dmabuf) = get_dmabuf(buffer) { 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;
@ -807,17 +753,25 @@ pub fn render_cursor_to_buffer(
seat: &Seat<State>, seat: &Seat<State>,
) { ) {
let buffer = frame.buffer(); let buffer = frame.buffer();
let cursor_geometry = seat.cursor_geometry((0.0, 0.0), state.common.clock.now()); let mut cursor_size = seat
let constraints = cursor_capture_constraints(cursor_geometry); .cursor_geometry((0.0, 0.0), state.common.clock.now())
.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();
if buffer_size != constraints.size { // Client shouldn't try to allocate 0x0 buffer
session.update_constraints(constraints.clone()); if cursor_size == Size::new(0, 0) {
cursor_size = Size::new(1, 1);
}
if buffer_size != cursor_size {
let constraints = BufferConstraints {
size: cursor_size,
shm: vec![ShmFormat::Argb8888],
dma: None,
};
session.update_constraints(constraints);
if 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(
constraints cursor_size.to_logical(1, Transform::Normal).to_physical(1),
.size
.to_logical(1, Transform::Normal)
.to_physical(1),
1.0, 1.0,
Transform::Normal, Transform::Normal,
)); ));
@ -835,59 +789,48 @@ 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< ) -> Result<RenderOutputResult<'d>, DTError<R::Error>>
(
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 mut elements: Vec<_> = additional_damage let additional_damage_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)?;
cursor::draw_cursor( let elements = 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, _| {
elements.push(
RelocateRenderElement::from_element(elem, (0, 0), Relocate::Relative).into(),
) )
}, .into_iter()
); .map(|(elem, _)| RelocateRenderElement::from_element(elem, (0, 0), Relocate::Relative))
.map(WindowCaptureElement::from)
.collect::<Vec<_>>();
let res = if let Ok(dmabuf) = get_dmabuf(buffer) { 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;

View file

@ -1,10 +0,0 @@
// 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
}
}

View file

@ -1,7 +1,6 @@
// 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;
@ -19,7 +18,6 @@ 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;

View file

@ -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_floor() && let point = (position - surface_location.as_logical()).to_i32_round()
&& region.contains(point) && region.contains(point)
{ {
constraint.activate(); constraint.activate();

View file

@ -317,45 +317,8 @@ 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.

View file

@ -1,21 +1,12 @@
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::desktop::utils::bbox_from_surface_tree; use smithay::utils::HookId;
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::wlr_layer::WlrLayerShellHandler; use smithay::wayland::shell::xdg::SurfaceCachedState;
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,
@ -27,7 +18,6 @@ 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 {
@ -43,13 +33,12 @@ impl CornerRadiusState {
+ Dispatch< + Dispatch<
cosmic_corner_radius_toplevel_v1::CosmicCornerRadiusToplevelV1, cosmic_corner_radius_toplevel_v1::CosmicCornerRadiusToplevelV1,
CornerRadiusData, CornerRadiusData,
> + Dispatch<cosmic_corner_radius_layer_v1::CosmicCornerRadiusLayerV1, CornerRadiusData> > + CornerRadiusHandler
+ 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, _>(
2, 1,
(), (),
); );
CornerRadiusState { CornerRadiusState {
@ -63,12 +52,18 @@ impl CornerRadiusState {
} }
} }
pub trait CornerRadiusHandler: XdgShellHandler + WlrLayerShellHandler { pub trait CornerRadiusHandler: XdgShellHandler {
fn corner_radius_state(&mut self) -> &mut CornerRadiusState; fn corner_radius_state(&mut self) -> &mut CornerRadiusState;
fn set_corner_radius(&mut self, data: &CornerRadiusData); fn set_corner_radius(
fn unset_corner_radius(&mut self, data: &CornerRadiusData); &mut self,
fn set_padding(&mut self, data: &CornerRadiusData); toplevel: &cosmic_corner_radius_toplevel_v1::CosmicCornerRadiusToplevelV1,
fn unset_padding(&mut self, data: &CornerRadiusData); 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>
@ -77,7 +72,6 @@ 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,
{ {
@ -102,7 +96,6 @@ 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,
{ {
@ -122,71 +115,6 @@ 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
@ -197,32 +125,62 @@ 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!( format!("{resource:?} CosmicCornerRadiusToplevelV1 object already exists for the surface"),
"{resource:?} CosmicCornerRadiusToplevelV1 object already exists for the surface"
),
); );
} }
let data = Mutex::new(CornerRadiusInternal { let data = Mutex::new(CornerRadiusInternal {
surface: CornerRadiusSurface::Layer(layer.downgrade()), toplevel: toplevel.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 = let hook_id = add_pre_commit_hook::<D, _>(
add_pre_commit_hook::<D, _>(surface.wl_surface(), layer_radius_hook); surface.wl_surface(),
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(|| LayerHookId::new(None)); .get_or_insert_threadsafe(|| ToplevelHookId::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!(),
} }
@ -239,55 +197,6 @@ 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
@ -312,35 +221,30 @@ where
let mut guard = data.lock().unwrap(); let mut guard = data.lock().unwrap();
guard.corners = None; guard.corners = None;
let Some(wl_surface) = (match &guard.surface { let Ok(toplevel) = guard.toplevel.upgrade() else {
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;
}; };
with_states(&wl_surface, |surface_data| { if let Some(surface) = state.xdg_shell_state().get_toplevel(&toplevel) {
if let Some(hook_ids_mutex) = surface_data.data_map.get::<ToplevelHookId>() { with_states(surface.wl_surface(), |surface_data| {
if let Some(hook_ids_mutex) = surface_data.data_map.get::<ToplevelHookId>()
{
let mut hook_id = hook_ids_mutex.lock().unwrap(); let mut hook_id = hook_ids_mutex.lock().unwrap();
*hook_id = None; *hook_id = None;
} }
});
}
let mut cached = surface_data.cached_state.get::<CacheableCorners>(); if let Some(surface) = state.xdg_shell_state().get_toplevel(&toplevel) {
with_states(surface.wl_surface(), |s| {
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(data); state.unset_corner_radius(resource, data);
} }
cosmic_corner_radius_toplevel_v1::Request::SetRadius { cosmic_corner_radius_toplevel_v1::Request::SetRadius {
top_left, top_left,
@ -350,388 +254,66 @@ 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) {
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(guard.corners); *pending = CacheableCorners(guard.corners);
}); });
}
drop(guard); drop(guard);
state.set_corner_radius(data); state.set_corner_radius(resource, 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;
}; };
with_states(&wl_surface, |s| { if let Some(surface) = state.xdg_shell_state().get_toplevel(&toplevel) {
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(data); state.unset_corner_radius(resource, 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 surface: CornerRadiusSurface, pub toplevel: Weak<XdgToplevel>,
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: u32, pub top_left: u8,
pub top_right: u32, pub top_right: u8,
pub bottom_right: u32, pub bottom_right: u8,
pub bottom_left: u32, pub bottom_left: u8,
}
#[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)]
@ -746,18 +328,6 @@ 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,
@ -767,23 +337,13 @@ impl CornerRadiusInternal {
bottom_left: u32, bottom_left: u32,
) { ) {
let corners = Corners { let corners = Corners {
top_left, top_left: top_left.clamp(u8::MIN as u32, u8::MAX as u32) as u8,
top_right, top_right: top_right.clamp(u8::MIN as u32, u8::MAX as u32) as u8,
bottom_right, bottom_right: bottom_right.clamp(u8::MIN as u32, u8::MAX as u32) as u8,
bottom_left, bottom_left: bottom_left.clamp(u8::MIN as u32, u8::MAX as u32) as u8,
}; };
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 {
@ -797,9 +357,6 @@ 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;

View file

@ -180,7 +180,7 @@ where
Modifier::Invalid, Modifier::Invalid,
DmabufFlags::empty(), DmabufFlags::empty(),
); );
dma.add_plane(name, offset0 as u32, stride0 as u32); dma.add_plane(name, 0, 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()) {

View file

@ -1,185 +0,0 @@
// 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);
}
}
}

View file

@ -4,7 +4,6 @@ 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;

View file

@ -807,17 +807,9 @@ 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,