diff --git a/Cargo.lock b/Cargo.lock index 0d489aa2..5edc17e0 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -866,7 +866,7 @@ checksum = "fe6d2e5af09e8c8ad56c969f2157a3d4238cebc7c55f0a517728c38f7b200f81" dependencies = [ "serde", "termcolor", - "unicode-width 0.2.2", + "unicode-width 0.1.14", ] [[package]] @@ -989,6 +989,7 @@ dependencies = [ "anyhow", "bitflags 2.11.0", "calloop", + "cgmath", "clap_lex", "cosmic-comp-config", "cosmic-config", @@ -1000,16 +1001,14 @@ dependencies = [ "egui_plot", "futures-executor", "futures-util", - "glam 0.33.2", "i18n-embed", "i18n-embed-fl", - "iced_graphics", "iced_tiny_skia", "id_tree", "indexmap 2.13.0", "jiff", "keyframe", - "libcosmic", + "libcosmic-yoda", "libdisplay-info", "libsystemd", "log-panics", @@ -1026,7 +1025,6 @@ dependencies = [ "sanitize-filename", "serde", "serde_json", - "smallvec", "smithay", "smithay-egui", "thiserror 2.0.18", @@ -2162,12 +2160,6 @@ version = "0.25.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "151665d9be52f9bb40fc7966565d39666f2d1e69233571b71b87791c7e0528b3" -[[package]] -name = "glam" -version = "0.33.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7f22fb22f065b308be0d8724e3706c7fa3fc2a6c7d6899df4cad7860e7a75436" - [[package]] name = "glow" version = "0.16.0" @@ -2482,7 +2474,7 @@ dependencies = [ "bytes", "cosmic-client-toolkit", "dnd", - "glam 0.25.0", + "glam", "lilt", "log", "mime 0.1.0", @@ -2598,7 +2590,7 @@ dependencies = [ "cosmic-client-toolkit", "cryoglyph", "futures", - "glam 0.25.0", + "glam", "guillotiere", "iced_debug", "iced_graphics", @@ -3139,7 +3131,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "68ab91017fe16c622486840e4c83c9a37afeff978bd239b5293d61ece587de66" [[package]] -name = "libcosmic" +name = "libcosmic-yoda" version = "0.1.0-yoda.2" dependencies = [ "apply", @@ -5257,14 +5249,14 @@ dependencies = [ [[package]] name = "smallvec" -version = "1.15.2" +version = "1.15.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "8ed6a63f02c8539c91a8685a86f4099661ba3da017932f6ebbea6de3f0fa7c90" +checksum = "67b1b7a3b5fe4f1376887184045fcf45c69e92af734b7aaddc05fb777b6fbd03" [[package]] name = "smithay" version = "0.7.0" -source = "git+https://github.com/smithay/smithay.git?rev=1ed69cb#1ed69cb55e9d0e9f38887c3b7d53d24365c4b14f" +source = "git+https://github.com/smithay/smithay.git?rev=8eb4076#8eb4076cad8705c56f5e3bac8577b83576b6ce76" dependencies = [ "aliasable", "appendlist", @@ -5273,6 +5265,7 @@ dependencies = [ "bitflags 2.11.0", "calloop", "cc", + "cgmath", "cursor-icon", "downcast-rs", "drm", @@ -5282,7 +5275,6 @@ dependencies = [ "errno", "gbm", "gl_generator", - "glam 0.33.2", "glow", "indexmap 2.13.0", "input", diff --git a/Cargo.toml b/Cargo.toml index fa3747f6..277655e7 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -4,7 +4,7 @@ edition = "2024" license = "GPL-3.0-only" name = "cosmic-comp" version = "1.0.0" -rust-version = "1.93" +rust-version = "1.90" [workspace] members = ["cosmic-comp-config"] @@ -40,10 +40,9 @@ i18n-embed = { version = "0.16", features = [ ] } i18n-embed-fl = "0.10" iced_tiny_skia = { path = "../libcosmic/iced/tiny_skia" } -iced_graphics = { path = "../libcosmic/iced/graphics", features = ["geometry"] } indexmap = "2.13" keyframe = "1.1.1" -cosmic = { package = "libcosmic", path = "../libcosmic", default-features = false, features = [ +cosmic = { package = "libcosmic-yoda", path = "../libcosmic", default-features = false, features = [ "tokio", "wayland", "multi-window", @@ -88,8 +87,7 @@ parking_lot = "0.12.5" logind-zbus = { version = "5.3.2", optional = true } futures-executor = { version = "0.3.32" } futures-util = "0.3.32" -glam = { version = "0.33", default-features = false, features = ["std"] } -smallvec = "1.15.2" +cgmath = "0.18.0" [dependencies.id_tree] branch = "feature/copy_clone" @@ -145,7 +143,7 @@ inherits = "release" [profile.release] lto = "fat" -[patch."https://github.com/pop-os/libcosmic"] +[patch.'https://github.com/pop-os/libcosmic'] cosmic-config = { path = "../libcosmic/cosmic-config" } cosmic-theme = { path = "../libcosmic/cosmic-theme" } iced = { path = "../libcosmic/iced" } @@ -160,21 +158,21 @@ iced_wgpu = { path = "../libcosmic/iced/wgpu" } iced_widget = { path = "../libcosmic/iced/widget" } iced_winit = { path = "../libcosmic/iced/winit" } -[patch."https://github.com/pop-os/cosmic-protocols"] +[patch.'https://github.com/pop-os/cosmic-protocols'] cosmic-protocols = { path = "../cosmic-protocols" } cosmic-client-toolkit = { path = "../cosmic-protocols/client-toolkit" } -[patch."https://github.com/pop-os/cosmic-settings-daemon"] +[patch.'https://github.com/pop-os/cosmic-settings-daemon'] cosmic-settings-config = { path = "../cosmic-settings-daemon/config" } cosmic-settings-daemon-config = { path = "../cosmic-settings-daemon/cosmic-settings-daemon-config" } -[patch."https://github.com/pop-os/cosmic-text.git"] +[patch.'https://github.com/pop-os/cosmic-text.git'] cosmic-text = { path = "../cosmic-text" } -[patch."https://forge.aditua.com/leyoda/window_clipboard.git"] +[patch.'https://forge.aditua.com/leyoda/window_clipboard.git'] window_clipboard = { path = "../window_clipboard" } dnd = { path = "../window_clipboard/dnd" } mime = { path = "../window_clipboard/mime" } [patch.crates-io] -smithay = { git = "https://github.com/smithay/smithay.git", rev = "1ed69cb" } +smithay = { git = "https://github.com/smithay/smithay.git", rev = "8eb4076" } diff --git a/examples/customized-window-decorations.rs b/examples/customized-window-decorations.rs index 6b3baf8f..ffe677f1 100644 --- a/examples/customized-window-decorations.rs +++ b/examples/customized-window-decorations.rs @@ -23,7 +23,7 @@ struct Circle { color: Color, } -impl +impl canvas::Program for Circle { type State = (); @@ -50,17 +50,17 @@ impl cosmic::Element<'_, Message> { let orig = self.lower.view(window); - widget::row([ - widget::column([canvas(Circle { - radius: (self.height as f32 / 2.) * 0.8, - color: Color::from_rgba(1.0, 0.0, 0.0, 1.0), - }) - .into()]) - .width(self.height as f32) - .into(), - orig, - ]) - .into() + widget::row() + .push( + widget::column() + .push(canvas(Circle { + radius: (self.height as f32 / 2.) * 0.8, + color: Color::from_rgba(1.0, 0.0, 0.0, 1.0), + })) + .width(self.height as f32), + ) + .push(orig) + .into() } } diff --git a/resources/i18n/de/cosmic_comp.ftl b/resources/i18n/de/cosmic_comp.ftl index 4ca5d5ea..f1644936 100644 --- a/resources/i18n/de/cosmic_comp.ftl +++ b/resources/i18n/de/cosmic_comp.ftl @@ -1,7 +1,7 @@ a11y-zoom-move-continuously = Ansicht bewegt sich kontinuierlich mit dem Cursor a11y-zoom-move-onedge = Ansicht bewegt sich, sobald der Cursor den Rand erreicht a11y-zoom-move-centered = Ansicht bewegt sich, um den Cursor zentiert zu halten -a11y-zoom-settings = Vergrößerungsglas-Einstellungen ... +a11y-zoom-settings = Vergrößerungsglas-Einstellungen… grow-window = Vergrößern shrink-window = Verkleinern swap-windows = Fenster tauschen diff --git a/resources/i18n/kmr/cosmic_comp.ftl b/resources/i18n/kmr/cosmic_comp.ftl index 8f207a8e..e69de29b 100644 --- a/resources/i18n/kmr/cosmic_comp.ftl +++ b/resources/i18n/kmr/cosmic_comp.ftl @@ -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 = -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 diff --git a/src/backend/kms/device.rs b/src/backend/kms/device.rs index 44682dc5..2b0e8f9d 100644 --- a/src/backend/kms/device.rs +++ b/src/backend/kms/device.rs @@ -943,7 +943,6 @@ impl LockedDevice<'_> { output, CursorMode::All, None, - None, ) .with_context(|| "Failed to render outputs")?, None => Vec::new(), diff --git a/src/backend/kms/mod.rs b/src/backend/kms/mod.rs index 16559e4e..13832da8 100644 --- a/src/backend/kms/mod.rs +++ b/src/backend/kms/mod.rs @@ -978,7 +978,6 @@ impl KmsGuard<'_> { output, CursorMode::All, None, - None, ) .with_context(|| "Failed to render outputs")?; @@ -1089,7 +1088,6 @@ impl KmsGuard<'_> { output, CursorMode::All, None, - None, ) .with_context(|| "Failed to render outputs")?; @@ -1134,7 +1132,6 @@ impl KmsGuard<'_> { output, CursorMode::All, None, - None, ) .with_context(|| "Failed to render outputs")?; diff --git a/src/backend/kms/render/gles.rs b/src/backend/kms/render/gles.rs index f6e44861..4a75c0ab 100644 --- a/src/backend/kms/render/gles.rs +++ b/src/backend/kms/render/gles.rs @@ -12,9 +12,10 @@ use smithay::{ }, drm::{CreateDrmNodeError, DrmNode}, renderer::{ + RendererSuper, gles::{GlesError, GlesRenderer, ffi}, glow::GlowRenderer, - multigpu::{ApiDevice, GraphicsApi}, + multigpu::{ApiDevice, Error as MultiError, GraphicsApi}, }, }, reexports::drm::control::Device, @@ -31,6 +32,8 @@ use std::{ sync::atomic::{AtomicBool, Ordering}, }; +use crate::backend::render::element::FromGlesError; + /// Errors raised by the [`GbmGlesBackend`] #[derive(Debug, thiserror::Error)] pub enum Error { @@ -210,3 +213,15 @@ impl ApiDevice for GbmGlowDevice { !self.intel_export_quirk } } + +impl FromGlesError + for MultiError, T> +where + T::Error: 'static, + <::Renderer as RendererSuper>::Error: 'static, +{ + #[inline] + fn from_gles_error(err: GlesError) -> MultiError, T> { + MultiError::Render(err) + } +} diff --git a/src/backend/kms/socket.rs b/src/backend/kms/socket.rs index f1c2b0eb..3babd35a 100644 --- a/src/backend/kms/socket.rs +++ b/src/backend/kms/socket.rs @@ -36,7 +36,7 @@ impl Common { ) -> Result { let socket_name = format!( "{}-{}", - self.socket.to_string_lossy(), + &self.socket.to_string_lossy(), render_node .dev_path() .unwrap() diff --git a/src/backend/kms/surface/mod.rs b/src/backend/kms/surface/mod.rs index bb28bc3c..62ba7c1f 100644 --- a/src/backend/kms/surface/mod.rs +++ b/src/backend/kms/surface/mod.rs @@ -1068,7 +1068,6 @@ impl SurfaceThreadState { None, #[cfg(feature = "debug")] Some((&self.egui, &self.timings)), - Some(self.target_node), ) .map_err(|err| { anyhow::format_err!("Failed to accumulate elements for rendering: {:?}", err) @@ -1674,7 +1673,7 @@ fn send_screencopy_result<'a>( pre_postprocess_data: &mut PrePostprocessData, tx: &std::sync::mpsc::Sender, frame_result: &RenderFrameResult>>, - elements: &[CosmicElement>], + elements: &[CosmicElement], (session, frame, res): ( &ScreencopySessionRef, ScreencopyFrame, @@ -1855,8 +1854,6 @@ fn send_screencopy_result<'a>( transform, damage.as_deref(), sync, - // Don't reference `Buffer`s since we blit from framebuffer/postprocess buffer - vec![], )? { if frame_result.is_empty { data.frame diff --git a/src/backend/render/wayland/clipped_surface.rs b/src/backend/render/clipped_surface.rs similarity index 90% rename from src/backend/render/wayland/clipped_surface.rs rename to src/backend/render/clipped_surface.rs index 8322079e..8705051e 100644 --- a/src/backend/render/wayland/clipped_surface.rs +++ b/src/backend/render/clipped_surface.rs @@ -2,11 +2,11 @@ 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::{ backend::renderer::{ - ImportAll, Renderer, + ImportAll, ImportMem, Renderer, element::{ Element, Id, Kind, RenderElement, UnderlyingStorage, surface::WaylandSurfaceRenderElement, @@ -19,7 +19,7 @@ use smithay::{ use crate::backend::render::element::AsGlowRenderer; -pub static CLIPPING_SHADER: &str = include_str!("../shaders/clipped_surface.frag"); +pub static CLIPPING_SHADER: &str = include_str!("./shaders/clipped_surface.frag"); pub struct ClippingShader(pub GlesTexProgram); impl ClippingShader { @@ -35,7 +35,10 @@ impl ClippingShader { } #[derive(Debug)] -pub struct ClippedSurfaceRenderElement { +pub struct ClippedSurfaceRenderElement +where + R: Renderer + ImportAll + ImportMem, +{ inner: WaylandSurfaceRenderElement, program: GlesTexProgram, radius: [u8; 4], @@ -45,7 +48,7 @@ pub struct ClippedSurfaceRenderElement { impl ClippedSurfaceRenderElement where - R: Renderer + ImportAll, + R: Renderer + ImportAll + ImportMem, { pub fn new( renderer: &mut R, @@ -63,18 +66,18 @@ where let view = elem.view(); let transform = elem.transform(); - let transform_matrix = Affine2::from_translation(Vec2::new(0.5, 0.5)) + let transform_matrix = Matrix3::::from_translation(Vector2::new(0.5, 0.5)) * transform.matrix() - * Affine2::from_translation(-Vec2::new(0.5, 0.5)); + * Matrix3::::from_translation(-Vector2::new(0.5, 0.5)); let geo_scale = { 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 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.y / elem_geo.size.h as f64) as f32, )) @@ -82,17 +85,16 @@ where let buf_scale = { 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.y / buf_size.h as f64) as f32, )); - let input_to_geo = Mat3::from( - transform_matrix * geo_scale * geo_translation * buf_scale * buf_translation, - ); + let input_to_geo = + transform_matrix * geo_scale * geo_translation * buf_scale * buf_translation; let uniforms = vec![ Uniform::new("geo_size", (geometry.size.w as f32, geometry.size.h as f32)), @@ -112,7 +114,6 @@ where transpose: false, }, ), - Uniform::new("noise", UniformValue::_1f(0.0)), ]; Self { @@ -173,8 +174,7 @@ where impl Element for ClippedSurfaceRenderElement where - R: Renderer + ImportAll + AsGlowRenderer, - R::TextureId: 'static, + R: Renderer + ImportAll + ImportMem, { fn id(&self) -> &Id { self.inner.id() @@ -247,7 +247,7 @@ where impl RenderElement for ClippedSurfaceRenderElement where - R: AsGlowRenderer + Renderer + ImportAll, + R: AsGlowRenderer + Renderer + ImportAll + ImportMem, R::TextureId: 'static, { fn draw( diff --git a/src/backend/render/cursor.rs b/src/backend/render/cursor.rs index da01b270..3c75996d 100644 --- a/src/backend/render/cursor.rs +++ b/src/backend/render/cursor.rs @@ -1,13 +1,6 @@ // SPDX-License-Identifier: GPL-3.0-only -use crate::{ - backend::render::{ - element::AsGlowRenderer, - wayland::{SurfaceRenderElement, push_render_elements_from_surface_tree}, - }, - utils::prelude::*, - wayland::handlers::compositor::FRAME_TIME_FILTER, -}; +use crate::{utils::prelude::*, wayland::handlers::compositor::FRAME_TIME_FILTER}; use smithay::{ backend::{ allocator::Fourcc, @@ -16,10 +9,10 @@ use smithay::{ element::{ Kind, memory::{MemoryRenderBuffer, MemoryRenderBufferRenderElement}, + surface::{WaylandSurfaceRenderElement, render_elements_from_surface_tree}, }, }, }, - desktop::utils::bbox_from_surface_tree, input::{ Seat, pointer::{CursorIcon, CursorImageAttributes, CursorImageStatus}, @@ -121,63 +114,24 @@ enum Error { NoDefaultCursor, #[error("Error opening xcursor file: {0}")] File(#[from] std::io::Error), -} - -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"], - - _ => &[], - } + #[error("Failed to parse XCursor file")] + Parse, } fn load_icon(theme: &CursorTheme, shape: CursorIcon) -> Result, Error> { - let shape_name = shape.to_string(); - - for name in cursor_aliases(&shape_name) - .iter() - .copied() - .chain(std::iter::once(shape_name.as_str())) - { - if let Some(icon_path) = theme.load_icon(name) { - let mut cursor_file = std::fs::File::open(&icon_path)?; - let mut cursor_data = Vec::new(); - cursor_file.read_to_end(&mut cursor_data)?; - - if let Some(images) = parse_xcursor(&cursor_data) { - return Ok(images); - } - } - } - - Err(Error::NoDefaultCursor) + let icon_path = theme + .load_icon(&shape.to_string()) + .ok_or(Error::NoDefaultCursor)?; + let mut cursor_file = std::fs::File::open(&icon_path)?; + let mut cursor_data = Vec::new(); + cursor_file.read_to_end(&mut cursor_data)?; + parse_xcursor(&cursor_data).ok_or(Error::Parse) } render_elements! { - pub CursorRenderElement where R: ImportAll + ImportMem + AsGlowRenderer, R::TextureId: Send; + pub CursorRenderElement where R: ImportAll + ImportMem; Static=MemoryRenderBufferRenderElement, - Surface=SurfaceRenderElement, + Surface=WaylandSurfaceRenderElement, } pub fn draw_surface_cursor( @@ -185,10 +139,9 @@ pub fn draw_surface_cursor( surface: &wl_surface::WlSurface, location: Point, scale: impl Into>, - blur_strength: usize, - push: &mut dyn FnMut(CursorRenderElement, Point), -) where - R: Renderer + ImportAll + AsGlowRenderer, +) -> Vec<(CursorRenderElement, Point)> +where + R: Renderer + ImportAll, R::TextureId: Clone + 'static, { let scale = scale.into(); @@ -203,21 +156,17 @@ pub fn draw_surface_cursor( .to_physical_precise_round(scale) }); - push_render_elements_from_surface_tree( + render_elements_from_surface_tree( renderer, surface, location.to_physical(scale).to_i32_round(), - bbox_from_surface_tree(surface, location.to_i32_round()).to_f64(), scale, 1.0, - false, - [0; 4], - None, - blur_strength, Kind::Cursor, - &mut |elem| push(elem.into(), h), - None, - ); + ) + .into_iter() + .map(|elem| (elem, h)) + .collect() } #[profiling::function] @@ -226,10 +175,9 @@ pub fn draw_dnd_icon( surface: &wl_surface::WlSurface, location: Point, scale: impl Into>, - blur_strength: usize, - push: &mut dyn FnMut(SurfaceRenderElement), -) where - R: Renderer + ImportAll + AsGlowRenderer, +) -> Vec> +where + R: Renderer + ImportAll, R::TextureId: Clone + 'static, { if get_role(surface) != Some("dnd_icon") { @@ -239,21 +187,14 @@ pub fn draw_dnd_icon( ); } let scale = scale.into(); - push_render_elements_from_surface_tree( + render_elements_from_surface_tree( renderer, surface, location.to_physical(scale).to_i32_round(), - bbox_from_surface_tree(surface, location.to_i32_round()).to_f64(), scale, 1.0, - false, - [0; 4], - None, - blur_strength, FRAME_TIME_FILTER, - push, - None, - ); + ) } pub type CursorState = Mutex; @@ -336,11 +277,10 @@ pub fn draw_cursor( scale: Scale, buffer_scale: f64, time: Time, - blur_strength: usize, draw_default: bool, - push: &mut dyn FnMut(CursorRenderElement, Point), -) where - R: Renderer + ImportMem + ImportAll + AsGlowRenderer, +) -> Vec<(CursorRenderElement, Point)> +where + R: Renderer + ImportMem + ImportAll, R::TextureId: Send + Clone + 'static, { // draw the cursor as relevant @@ -351,7 +291,7 @@ pub fn draw_cursor( let state = &mut *state_ref; if state.hidden { - return; + return Vec::new(); } let named_cursor = state.current_cursor.or(match cursor_status { @@ -360,7 +300,7 @@ pub fn draw_cursor( }); if let Some(current_cursor) = named_cursor { if !draw_default && current_cursor == CursorIcon::Default { - return; + return Vec::new(); } let integer_scale = (scale.x.max(scale.y) * buffer_scale).ceil() as u32; @@ -397,7 +337,7 @@ pub fn draw_cursor( ); state.current_image = Some(frame); - push( + return vec![( CursorRenderElement::Static( MemoryRenderBufferRenderElement::from_buffer( renderer, @@ -411,9 +351,11 @@ pub fn draw_cursor( .expect("Failed to import cursor bitmap"), ), hotspot.to_physical_precise_round(scale), - ); + )]; } 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() } } diff --git a/src/backend/render/element.rs b/src/backend/render/element.rs index e1259dc1..496702d0 100644 --- a/src/backend/render/element.rs +++ b/src/backend/render/element.rs @@ -1,27 +1,20 @@ -use crate::{ - backend::{ - kms::render::gles::GbmGlowBackend, - render::{GlMultiError, wayland::SurfaceRenderElement}, - }, - shell::{CosmicMappedRenderElement, WorkspaceRenderElement}, - utils::iced::IcedRenderElement, -}; +use crate::shell::{CosmicMappedRenderElement, WorkspaceRenderElement}; #[cfg(feature = "debug")] use smithay::backend::renderer::element::texture::TextureRenderElement; use smithay::{ backend::{ allocator::dmabuf::Dmabuf, - drm::DrmDeviceFd, renderer::{ - Bind, Blit, ContextId, ExportMem, ImportAll, ImportMem, Offscreen, Renderer, + Bind, Blit, ExportMem, ImportAll, ImportMem, Offscreen, Renderer, element::{ Element, Id, Kind, RenderElement, UnderlyingStorage, + memory::MemoryRenderBufferRenderElement, + surface::WaylandSurfaceRenderElement, utils::{CropRenderElement, Relocate, RelocateRenderElement, RescaleRenderElement}, }, gles::{GlesError, GlesRenderbuffer, GlesTexture, element::TextureShaderElement}, glow::{GlowFrame, GlowRenderer}, - multigpu::MultiTexture, utils::{CommitCounter, DamageSet, OpaqueRegions}, }, }, @@ -35,20 +28,19 @@ use super::{GlMultiRenderer, cursor::CursorRenderElement}; pub enum CosmicElement where R: AsGlowRenderer, - R::TextureId: Send + 'static, + R::TextureId: 'static, CosmicMappedRenderElement: RenderElement, { Workspace( RelocateRenderElement>>>, ), Cursor(RescaleRenderElement>>), - Dnd(SurfaceRenderElement), + Dnd(WaylandSurfaceRenderElement), MoveGrab(RescaleRenderElement>), Postprocess( CropRenderElement>>, ), - Zoom(IcedRenderElement), - Damage(DamageElement), + Zoom(MemoryRenderBufferRenderElement), #[cfg(feature = "debug")] Egui(TextureRenderElement), } @@ -56,7 +48,7 @@ where impl Element for CosmicElement where R: AsGlowRenderer, - R::TextureId: Send + 'static, + R::TextureId: 'static, CosmicMappedRenderElement: RenderElement, { fn id(&self) -> &Id { @@ -67,7 +59,6 @@ where CosmicElement::MoveGrab(elem) => elem.id(), CosmicElement::Postprocess(elem) => elem.id(), CosmicElement::Zoom(elem) => elem.id(), - CosmicElement::Damage(elem) => elem.id(), #[cfg(feature = "debug")] CosmicElement::Egui(elem) => elem.id(), } @@ -81,7 +72,6 @@ where CosmicElement::MoveGrab(elem) => elem.current_commit(), CosmicElement::Postprocess(elem) => elem.current_commit(), CosmicElement::Zoom(elem) => elem.current_commit(), - CosmicElement::Damage(elem) => elem.current_commit(), #[cfg(feature = "debug")] CosmicElement::Egui(elem) => elem.current_commit(), } @@ -95,7 +85,6 @@ where CosmicElement::MoveGrab(elem) => elem.src(), CosmicElement::Postprocess(elem) => elem.src(), CosmicElement::Zoom(elem) => elem.src(), - CosmicElement::Damage(elem) => elem.src(), #[cfg(feature = "debug")] CosmicElement::Egui(elem) => elem.src(), } @@ -109,7 +98,6 @@ where CosmicElement::MoveGrab(elem) => elem.geometry(scale), CosmicElement::Postprocess(elem) => elem.geometry(scale), CosmicElement::Zoom(elem) => elem.geometry(scale), - CosmicElement::Damage(elem) => elem.geometry(scale), #[cfg(feature = "debug")] CosmicElement::Egui(elem) => elem.geometry(scale), } @@ -123,7 +111,6 @@ where CosmicElement::MoveGrab(elem) => elem.location(scale), CosmicElement::Postprocess(elem) => elem.location(scale), CosmicElement::Zoom(elem) => elem.location(scale), - CosmicElement::Damage(elem) => elem.location(scale), #[cfg(feature = "debug")] CosmicElement::Egui(elem) => elem.location(scale), } @@ -137,7 +124,6 @@ where CosmicElement::MoveGrab(elem) => elem.transform(), CosmicElement::Postprocess(elem) => elem.transform(), CosmicElement::Zoom(elem) => elem.transform(), - CosmicElement::Damage(elem) => elem.transform(), #[cfg(feature = "debug")] CosmicElement::Egui(elem) => elem.transform(), } @@ -155,7 +141,6 @@ where CosmicElement::MoveGrab(elem) => elem.damage_since(scale, commit), CosmicElement::Postprocess(elem) => elem.damage_since(scale, commit), CosmicElement::Zoom(elem) => elem.damage_since(scale, commit), - CosmicElement::Damage(elem) => elem.damage_since(scale, commit), #[cfg(feature = "debug")] CosmicElement::Egui(elem) => elem.damage_since(scale, commit), } @@ -169,7 +154,6 @@ where CosmicElement::MoveGrab(elem) => elem.opaque_regions(scale), CosmicElement::Postprocess(elem) => elem.opaque_regions(scale), CosmicElement::Zoom(elem) => elem.opaque_regions(scale), - CosmicElement::Damage(elem) => elem.opaque_regions(scale), #[cfg(feature = "debug")] CosmicElement::Egui(elem) => elem.opaque_regions(scale), } @@ -183,7 +167,6 @@ where CosmicElement::MoveGrab(elem) => elem.alpha(), CosmicElement::Postprocess(elem) => elem.alpha(), CosmicElement::Zoom(elem) => elem.alpha(), - CosmicElement::Damage(elem) => elem.alpha(), #[cfg(feature = "debug")] CosmicElement::Egui(elem) => elem.alpha(), } @@ -197,7 +180,6 @@ where CosmicElement::MoveGrab(elem) => elem.kind(), CosmicElement::Postprocess(elem) => elem.kind(), CosmicElement::Zoom(elem) => elem.kind(), - CosmicElement::Damage(elem) => elem.kind(), #[cfg(feature = "debug")] CosmicElement::Egui(elem) => elem.kind(), } @@ -211,7 +193,6 @@ where CosmicElement::MoveGrab(elem) => elem.is_framebuffer_effect(), CosmicElement::Postprocess(elem) => elem.is_framebuffer_effect(), CosmicElement::Zoom(elem) => elem.is_framebuffer_effect(), - CosmicElement::Damage(elem) => elem.is_framebuffer_effect(), #[cfg(feature = "debug")] CosmicElement::Egui(elem) => elem.is_framebuffer_effect(), } @@ -221,7 +202,8 @@ where impl RenderElement for CosmicElement where R: AsGlowRenderer, - R::TextureId: Send + 'static, + R::TextureId: 'static, + R::Error: FromGlesError, CosmicMappedRenderElement: RenderElement, { fn draw( @@ -255,12 +237,9 @@ where opaque_regions, 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::Damage(elem) => { - RenderElement::::draw(elem, frame, src, dst, damage, opaque_regions, cache) - } #[cfg(feature = "debug")] CosmicElement::Egui(elem) => { let glow_frame = R::glow_frame_mut(frame); @@ -273,7 +252,7 @@ where opaque_regions, cache, ) - .map_err(R::from_gles_error) + .map_err(FromGlesError::from_gles_error) } } } @@ -289,7 +268,6 @@ where elem.underlying_storage(glow_renderer) } CosmicElement::Zoom(elem) => elem.underlying_storage(renderer), - CosmicElement::Damage(elem) => elem.underlying_storage(renderer), #[cfg(feature = "debug")] CosmicElement::Egui(elem) => { let glow_renderer = renderer.glow_renderer_mut(); @@ -315,19 +293,16 @@ where RenderElement::::capture_framebuffer( 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::Damage(elem) => { - RenderElement::::capture_framebuffer(elem, frame, src, dst, cache) - } #[cfg(feature = "debug")] CosmicElement::Egui(elem) => { let glow_frame = R::glow_frame_mut(frame); RenderElement::::capture_framebuffer( elem, glow_frame, src, dst, cache, ) - .map_err(R::from_gles_error) + .map_err(FromGlesError::from_gles_error) } } } @@ -337,7 +312,7 @@ impl From>>> for CosmicElement where R: AsGlowRenderer, - R::TextureId: Send + 'static, + R::TextureId: 'static, CosmicMappedRenderElement: RenderElement, { fn from(elem: CropRenderElement>>) -> Self { @@ -349,33 +324,22 @@ where } } -impl From> for CosmicElement +impl From> for CosmicElement where R: AsGlowRenderer, - R::TextureId: Send + 'static, + R::TextureId: 'static, CosmicMappedRenderElement: RenderElement, { - fn from(value: IcedRenderElement) -> Self { + fn from(value: MemoryRenderBufferRenderElement) -> Self { Self::Zoom(value) } } -impl From for CosmicElement -where - R: Renderer + ImportAll + ImportMem + AsGlowRenderer, - R::TextureId: Send + 'static, - CosmicMappedRenderElement: RenderElement, -{ - fn from(value: DamageElement) -> Self { - Self::Damage(value) - } -} - #[cfg(feature = "debug")] impl From> for CosmicElement where R: AsGlowRenderer, - R::TextureId: Send + 'static, + R::TextureId: 'static, CosmicMappedRenderElement: RenderElement, { fn from(elem: TextureRenderElement) -> Self { @@ -401,12 +365,6 @@ pub trait AsGlowRenderer: fn glow_frame_mut<'a, 'frame, 'buffer>( frame: &'a mut Self::Frame<'frame, 'buffer>, ) -> &'a mut GlowFrame<'frame, 'buffer>; - fn tex_from_gl(context: &ContextId, texture: GlesTexture) -> Self::TextureId; - fn tex_to_gl( - context: &ContextId, - texture: &Self::TextureId, - ) -> Option; - fn from_gles_error(err: GlesError) -> Self::Error; } impl AsGlowRenderer for GlowRenderer { @@ -426,18 +384,6 @@ impl AsGlowRenderer for GlowRenderer { ) -> &'a mut GlowFrame<'frame, 'buffer> { frame } - fn tex_from_gl(_context: &ContextId, texture: GlesTexture) -> Self::TextureId { - texture - } - fn tex_to_gl( - _context: &ContextId, - texture: &Self::TextureId, - ) -> Option { - Some(texture.clone()) - } - fn from_gles_error(err: GlesError) -> Self::Error { - err - } } impl AsGlowRenderer for GlMultiRenderer<'_> { @@ -457,18 +403,6 @@ impl AsGlowRenderer for GlMultiRenderer<'_> { ) -> &'b mut GlowFrame<'frame, 'buffer> { frame.as_mut() } - fn tex_from_gl(context: &ContextId, texture: GlesTexture) -> Self::TextureId { - MultiTexture::from_native_texture::>(context, texture).unwrap() - } - fn tex_to_gl( - context: &ContextId, - texture: &Self::TextureId, - ) -> Option { - texture.get::>(context) - } - fn from_gles_error(err: GlesError) -> Self::Error { - GlMultiError::Render(err) - } } pub struct DamageElement { @@ -524,3 +458,13 @@ impl RenderElement for DamageElement { Ok(()) } } + +pub trait FromGlesError { + fn from_gles_error(err: GlesError) -> Self; +} + +impl FromGlesError for GlesError { + fn from_gles_error(err: GlesError) -> Self { + err + } +} diff --git a/src/backend/render/mod.rs b/src/backend/render/mod.rs index e9cf651f..bfbb21a3 100644 --- a/src/backend/render/mod.rs +++ b/src/backend/render/mod.rs @@ -15,19 +15,14 @@ use crate::{ backend::{ kms::render::gles::GbmGlowBackend, render::{ + clipped_surface::{CLIPPING_SHADER, ClippingShader}, element::DamageElement, shadow::{SHADOW_SHADER, ShadowShader}, - wayland::{ - SurfaceRenderElement, - blur_effect::BlurShaders, - clipped_surface::{CLIPPING_SHADER, ClippingShader}, - push_render_elements_from_surface_tree, - }, }, }, config::ScreenFilter, shell::{ - CosmicMappedRenderElement, OutputId, OverviewMode, SeatExt, Trigger, WorkspaceDelta, + CosmicMappedRenderElement, OverviewMode, SeatExt, Trigger, WorkspaceDelta, WorkspaceRenderElement, element::CosmicMappedKey, focus::{FocusTarget, Stage, render_input_order, target::WindowGroup}, @@ -39,26 +34,25 @@ use crate::{ wayland::{ handlers::{ compositor::FRAME_TIME_FILTER, - corner_radius::{pad_rect, surface_corners, surface_padding}, data_device::get_dnd_icon, - image_copy_capture::{ - FrameHolder, SessionData, render_element_buffers, render_session, - }, + image_copy_capture::{FrameHolder, SessionData, render_session}, }, protocols::workspace::WorkspaceHandle, }, }; use cosmic::Theme; +use element::FromGlesError; use smithay::{ backend::{ allocator::Fourcc, drm::{DrmDeviceFd, DrmNode}, renderer::{ - Color32F, Offscreen, Texture, TextureFilter, + Color32F, ImportAll, Offscreen, Renderer, Texture, TextureFilter, damage::{Error as RenderError, OutputDamageTracker, RenderOutputResult}, element::{ - Element, Id, Kind, NamespacedElement, RenderElement, WeakId, + Element, Id, Kind, RenderElement, WeakId, + surface::{WaylandSurfaceRenderElement, render_elements_from_surface_tree}, texture::{TextureRenderBuffer, TextureRenderElement}, utils::{ ConstrainAlign, ConstrainScaleBehavior, CropRenderElement, Relocate, @@ -75,23 +69,22 @@ use smithay::{ sync::SyncPoint, }, }, - desktop::utils::bbox_from_surface_tree, input::Seat, output::{Output, OutputModeSource, OutputNoMode}, utils::{ 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")] use smithay_egui::EguiState; pub mod animations; +pub mod clipped_surface; pub mod cursor; pub mod element; pub mod shadow; -pub mod wayland; use self::element::{AsGlowRenderer, CosmicElement}; use super::kms::Timings; @@ -269,7 +262,7 @@ impl IndicatorShader { .filter(|(old_settings, _)| &settings == old_settings) .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 elem = PixelShaderElement::new( @@ -429,7 +422,6 @@ pub fn init_shaders(renderer: &mut GlesRenderer) -> Result<(), GlesError> { UniformName::new("geo_size", UniformType::_2f), UniformName::new("corner_radius", UniformType::_4f), UniformName::new("input_to_geo", UniformType::Matrix3x3), - UniformName::new("noise", UniformType::_1f), ], )?; let shadow_shader = renderer.compile_custom_pixel_shader( @@ -445,7 +437,6 @@ pub fn init_shaders(renderer: &mut GlesRenderer) -> Result<(), GlesError> { UniformName::new("window_corner_radius", UniformType::_4f), ], )?; - let blur_shaders = BlurShaders::compile(renderer)?; let egl_context = renderer.egl_context(); egl_context @@ -463,7 +454,6 @@ pub fn init_shaders(renderer: &mut GlesRenderer) -> Result<(), GlesError> { egl_context .user_data() .insert_if_missing(|| ShadowShader(shadow_shader)); - egl_context.user_data().insert_if_missing(|| blur_shaders); Ok(()) } @@ -481,14 +471,12 @@ pub fn cursor_elements<'a, 'frame, R>( seats: impl Iterator>, zoom_state: Option<&ZoomState>, theme: &Theme, - blur_strength: usize, now: Time, output: &Output, mode: CursorMode, exclude_dnd_icon: bool, - scanout_node: Option, - push: &mut dyn FnMut(CosmicElement), -) where +) -> Vec> +where R: AsGlowRenderer, R::TextureId: Send + Clone + 'static, CosmicMappedRenderElement: RenderElement, @@ -502,6 +490,7 @@ pub fn cursor_elements<'a, 'frame, R>( ) }) .unwrap_or_else(|| ((0., 0.).into(), 1.)); + let mut elements = Vec::new(); for seat in seats { let pointer = match seat.get_pointer() { @@ -511,17 +500,19 @@ pub fn cursor_elements<'a, 'frame, R>( let location = pointer.current_location() - output.current_location().to_f64(); if mode != CursorMode::None { - cursor::draw_cursor( - renderer, - seat, - location, - scale.into(), - zoom_scale, - now, - blur_strength, - mode != CursorMode::NotDefault, - &mut |elem, hotspot| { - push(CosmicElement::Cursor(RescaleRenderElement::from_element( + elements.extend( + cursor::draw_cursor( + renderer, + seat, + location, + scale.into(), + zoom_scale, + now, + mode != CursorMode::NotDefault, + ) + .into_iter() + .map(|(elem, hotspot)| { + CosmicElement::Cursor(RescaleRenderElement::from_element( RelocateRenderElement::from_element( elem, 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_i32_round(), zoom_scale, - ))) - }, + )) + }), ); } if !exclude_dnd_icon && let Some(dnd_icon) = get_dnd_icon(seat) { - cursor::draw_dnd_icon( - renderer, - &dnd_icon.surface, - (location + dnd_icon.offset.to_f64()).to_i32_round(), - scale, - blur_strength, - &mut |elem| push(CosmicElement::Dnd(elem)), + elements.extend( + cursor::draw_dnd_icon( + renderer, + &dnd_icon.surface, + (location + dnd_icon.offset.to_f64()).to_i32_round(), + scale, + ) + .into_iter() + .map(CosmicElement::Dnd), ); } let theme = theme.cosmic(); - if let Some(grab_state) = seat + if let Some(grab_elements) = seat .user_data() .get::() .unwrap() .lock() .unwrap() .as_ref() + .map(|state| state.render::, R>(renderer, output, theme)) { - grab_state.render(renderer, output, theme, scanout_node, &mut |elem| { - push(CosmicElement::MoveGrab(RescaleRenderElement::from_element( + elements.extend(grab_elements.into_iter().map(|elem| { + CosmicElement::MoveGrab(RescaleRenderElement::from_element( elem, focal_point .as_logical() .to_physical(output.current_scale().fractional_scale()) .to_i32_round(), zoom_scale, - ))); - }) + )) + })); } - if let Some(grab_state) = seat + if let Some((grab_elements, should_scale)) = seat .user_data() .get::() .unwrap() .lock() .unwrap() .as_ref() + .map(|state| { + ( + state.render::, R>(renderer, output), + !state.is_in_screen_space(), + ) + }) { - let should_scale = !grab_state.is_in_screen_space(); - grab_state.render(renderer, output, &mut |elem| { - push(CosmicElement::MoveGrab(RescaleRenderElement::from_element( - elem.into(), + elements.extend(grab_elements.into_iter().map(|elem| { + CosmicElement::MoveGrab(RescaleRenderElement::from_element( + elem, if should_scale { focal_point .as_logical() @@ -590,10 +589,12 @@ pub fn cursor_elements<'a, 'frame, R>( Point::from((0, 0)) }, if should_scale { zoom_scale } else { 1.0 }, - ))); - }) + )) + })); } } + + elements } #[cfg(not(feature = "debug"))] @@ -614,11 +615,11 @@ pub fn output_elements( output: &Output, cursor_mode: CursorMode, _fps: Option<(&EguiState, &Timings)>, - scanout_node: Option, ) -> Result>, RenderError> where R: AsGlowRenderer, R::TextureId: Send + Clone + 'static, + R::Error: FromGlesError, CosmicMappedRenderElement: RenderElement, WorkspaceRenderElement: RenderElement, { @@ -645,7 +646,7 @@ where ), scale, ) - .map_err(R::from_gles_error) + .map_err(FromGlesError::from_gles_error) .map_err(RenderError::Rendering)? .into(), ] @@ -689,7 +690,6 @@ where workspace, cursor_mode, element_filter, - scanout_node, )?; #[cfg(feature = "debug")] @@ -713,15 +713,15 @@ pub fn workspace_elements( current: (WorkspaceHandle, usize), cursor_mode: CursorMode, element_filter: ElementFilter, - scanout_node: Option, ) -> Result>, RenderError> where R: AsGlowRenderer, R::TextureId: Send + Clone + 'static, + R::Error: FromGlesError, CosmicMappedRenderElement: RenderElement, WorkspaceRenderElement: RenderElement, { - let mut elements = Vec::>::new(); + let mut elements = Vec::new(); let shell_ref = shell.read(); let seats = shell_ref.seats.iter().cloned().collect::>(); @@ -729,25 +729,21 @@ where return Ok(Vec::new()); } let theme = shell_ref.theme().clone(); - let blur_strength = (theme.cosmic().frosted as u8 + 1) as usize; let scale = output.current_scale().fractional_scale(); // we don't want to hold a shell lock across `cursor_elements`, // that is prone to deadlock with the main-thread on some grabs. std::mem::drop(shell_ref); - cursor_elements( + elements.extend(cursor_elements( renderer, seats.iter(), zoom_level, &theme, - blur_strength, now, output, cursor_mode, element_filter == ElementFilter::ExcludeWorkspaceOverview, - scanout_node, - &mut |elem| elements.push(elem), - ); + )); let shell = shell.read(); let overview = shell.overview_mode(); @@ -823,132 +819,72 @@ where render_input_order::<()>(&shell, output, previous, current, element_filter, |stage| { match stage { Stage::ZoomUI => { - ZoomState::render(renderer, output, &mut |elem| { - elements.push(CosmicElement::Zoom(elem)) - }); + elements.extend(ZoomState::render(renderer, output)); } Stage::SessionLock(lock_surface) => { - session_lock_elements(renderer, output, lock_surface, blur_strength, &mut |elem| { - elements.extend(crop_to_output(elem.into()).map(Into::into)) - }) - } - Stage::LayerPopup { - popup, - location, - workspace_idx, - .. - } => { - let mut geometry = popup.geometry().as_global(); - geometry.loc += location; - - let radii = with_states(popup.wl_surface(), |states| { - surface_corners(states, geometry.size.as_logical()) - }) - .unwrap_or([0; 4]); - - let namespace = output - .user_data() - .get::() - .map(|id| id.namespace_for_workspace(workspace_idx)) - .unwrap_or(workspace_idx); - - push_render_elements_from_surface_tree( - renderer, - popup.wl_surface(), - location - .to_local(output) - .as_logical() - .to_physical_precise_round(scale), - geometry.to_local(output).as_logical().to_f64(), - Scale::from(scale), - 1.0, - false, - radii, - None, - blur_strength, - FRAME_TIME_FILTER, - &mut |elem| { - elements.extend( - crop_to_output(NamespacedElement::new(elem, namespace).into()) - .map(Into::into), - ) - }, - None, - ) - } - Stage::LayerSurface { - layer, - location, - 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::() - .map(|id| id.namespace_for_workspace(workspace_idx)) - .unwrap_or(workspace_idx); - - push_render_elements_from_surface_tree( - renderer, - layer.wl_surface(), - location - .to_local(output) - .as_logical() - .to_physical_precise_round(scale), - geometry.to_f64(), - Scale::from(scale), - 1.0, - false, - radii, - padded.to_f64(), - blur_strength, - FRAME_TIME_FILTER, - &mut |elem| { - elements.extend( - crop_to_output(NamespacedElement::new(elem, namespace).into()) - .map(Into::into), - ) - }, - None, + elements.extend( + session_lock_elements(renderer, output, lock_surface) + .into_iter() + .map(Into::into) + .flat_map(crop_to_output) + .map(Into::into), ); } - Stage::OverrideRedirect { surface, location } => { - if let Some(wl_surface) = surface.wl_surface() { - let mut geometry = surface.geometry().as_global(); - geometry.loc += location; - - push_render_elements_from_surface_tree( + Stage::LayerPopup { + popup, location, .. + } => { + elements.extend( + render_elements_from_surface_tree::<_, WorkspaceRenderElement<_>>( renderer, - &wl_surface, + popup.wl_surface(), location .to_local(output) .as_logical() .to_physical_precise_round(scale), - geometry.to_local(output).as_logical().to_f64(), Scale::from(scale), 1.0, - false, - [0; 4], - None, - blur_strength, 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::LayerSurface { layer, location } => { + elements.extend( + render_elements_from_surface_tree::<_, WorkspaceRenderElement<_>>( + renderer, + layer.wl_surface(), + location + .to_local(output) + .as_logical() + .to_physical_precise_round(scale), + Scale::from(scale), + 1.0, + FRAME_TIME_FILTER, + ) + .into_iter() + .flat_map(crop_to_output) + .map(Into::into), + ); + } + Stage::OverrideRedirect { surface, location } => { + elements.extend(surface.wl_surface().into_iter().flat_map(|surface| { + render_elements_from_surface_tree::<_, WorkspaceRenderElement<_>>( + renderer, + &surface, + location + .to_local(output) + .as_logical() + .to_physical_precise_round(scale), + Scale::from(scale), + 1.0, + FRAME_TIME_FILTER, + ) + .into_iter() + .flat_map(crop_to_output) + .map(Into::into) + })); } Stage::StickyPopups(layout) => { let alpha = match &overview.0 { @@ -969,11 +905,14 @@ where OverviewMode::None => 1.0, }; - layout.render_popups(renderer, alpha, scanout_node, &mut |elem| { - if let Some(elem) = crop_to_output(elem.into()) { - elements.push(elem.into()) - } - }); + elements.extend( + layout + .render_popups(renderer, alpha) + .into_iter() + .map(Into::into) + .flat_map(crop_to_output) + .map(Into::into), + ); } Stage::Sticky(layout) => { let alpha = match &overview.0 { @@ -998,66 +937,79 @@ where .then_some(last_active_seat) .map(|seat| workspace.focus_stack.get(seat)); - layout.render( - renderer, - current_focus.as_ref().and_then(|stack| { - stack.last().and_then(|t| match t { - FocusTarget::Window(w) => Some(w), - _ => None, - }) - }), - resize_indicator.clone(), - active_hint, - alpha, - theme.cosmic(), - scanout_node, - &mut |elem| { - if let Some(elem) = crop_to_output(elem.into()) { - elements.push(elem.into()) - } - }, - ); + elements.extend( + layout + .render( + renderer, + current_focus.as_ref().and_then(|stack| { + stack.last().and_then(|t| match t { + FocusTarget::Window(w) => Some(w), + _ => None, + }) + }), + resize_indicator.clone(), + active_hint, + alpha, + theme.cosmic(), + ) + .into_iter() + .map(Into::into) + .flat_map(crop_to_output) + .map(Into::into), + ) } Stage::WorkspacePopups { workspace, offset } => { - workspace.render_popups( - renderer, - last_active_seat, - !move_active && is_active_space, - overview.clone(), - theme.cosmic(), - scanout_node, - &mut |elem| { - if let Some(elem) = crop_to_output(elem) { - elements.push(CosmicElement::Workspace( - RelocateRenderElement::from_element( - elem, - offset.to_physical_precise_round(scale), - Relocate::Relative, - ), - )); + elements.extend( + match workspace.render_popups( + renderer, + last_active_seat, + !move_active && is_active_space, + overview.clone(), + theme.cosmic(), + ) { + Ok(elements) => { + elements + .into_iter() + .flat_map(crop_to_output) + .map(|element| { + CosmicElement::Workspace(RelocateRenderElement::from_element( + element, + offset.to_physical_precise_round(scale), + Relocate::Relative, + )) + }) + } + Err(_) => { + return ControlFlow::Break(Err(OutputNoMode)); } }, ); } Stage::Workspace { workspace, offset } => { - workspace.render( - renderer, - last_active_seat, - !move_active && is_active_space, - overview.clone(), - resize_indicator.clone(), - active_hint, - theme.cosmic(), - scanout_node, - &mut |elem| { - if let Some(elem) = crop_to_output(elem) { - elements.push(CosmicElement::Workspace( - RelocateRenderElement::from_element( - elem, - offset.to_physical_precise_round(scale), - Relocate::Relative, - ), - )); + elements.extend( + match workspace.render( + renderer, + last_active_seat, + !move_active && is_active_space, + overview.clone(), + resize_indicator.clone(), + active_hint, + theme.cosmic(), + ) { + Ok(elements) => { + elements + .into_iter() + .flat_map(crop_to_output) + .map(|element| { + CosmicElement::Workspace(RelocateRenderElement::from_element( + element, + offset.to_physical_precise_round(scale), + Relocate::Relative, + )) + }) + } + Err(_) => { + return ControlFlow::Break(Err(OutputNoMode)); } }, ); @@ -1074,29 +1026,23 @@ fn session_lock_elements( renderer: &mut R, output: &Output, lock_surface: Option<&LockSurface>, - blur_strength: usize, - push: &mut dyn FnMut(SurfaceRenderElement), -) where - R: AsGlowRenderer, +) -> Vec> +where + R: Renderer + ImportAll, R::TextureId: Clone + 'static, { if let Some(surface) = lock_surface { let scale = Scale::from(output.current_scale().fractional_scale()); - push_render_elements_from_surface_tree( + render_elements_from_surface_tree( renderer, surface.wl_surface(), (0, 0), - bbox_from_surface_tree(surface.wl_surface(), (0, 0)).to_f64(), scale, 1.0, - false, - [0; 4], - None, - blur_strength, FRAME_TIME_FILTER, - push, - None, ) + } else { + Vec::new() } } @@ -1244,6 +1190,7 @@ pub fn render_output<'d, R>( where R: AsGlowRenderer, R::TextureId: Send + Clone + 'static, + R::Error: FromGlesError, CosmicElement: RenderElement, CosmicMappedRenderElement: RenderElement, WorkspaceRenderElement: RenderElement, @@ -1507,16 +1454,11 @@ where } } - let buffers = render_element_buffers(renderer, &elements); - - Ok(( - RenderOutputResult { - damage: res.0, - sync, - states: res.1, - }, - buffers, - )) + Ok(RenderOutputResult { + damage: res.0, + sync, + states: res.1, + }) }, )? { pending_image_copy_data.send_success_when_ready( @@ -1553,23 +1495,12 @@ pub fn render_workspace<'d, R>( where R: AsGlowRenderer, R::TextureId: Send + Clone + 'static, + R::Error: FromGlesError, CosmicElement: RenderElement, CosmicMappedRenderElement: RenderElement, WorkspaceRenderElement: RenderElement, { - let mut elements: Vec> = if let Some(additional_damage) = additional_damage { - let output_geo = output.geometry().to_local(output).as_logical(); - additional_damage - .into_iter() - .filter_map(|rect| rect.intersection(output_geo)) - .map(DamageElement::new) - .map(CosmicElement::from) - .collect() - } else { - Vec::new() - }; - - elements.extend(workspace_elements( + let elements: Vec> = workspace_elements( gpu, renderer, shell, @@ -1580,8 +1511,17 @@ where current, cursor_mode, element_filter, - None, - )?); + )?; + + if let Some(additional_damage) = additional_damage { + let output_geo = output.geometry().to_local(output).as_logical(); + let additional_damage_elements: Vec<_> = additional_damage + .into_iter() + .filter_map(|rect| rect.intersection(output_geo)) + .map(DamageElement::new) + .collect(); + damage_tracker.damage_output(age, &additional_damage_elements)?; + } let res = damage_tracker.render_output( renderer, diff --git a/src/backend/render/shaders/blur_downsample.frag b/src/backend/render/shaders/blur_downsample.frag deleted file mode 100644 index 1fff7522..00000000 --- a/src/backend/render/shaders/blur_downsample.frag +++ /dev/null @@ -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; - } -} diff --git a/src/backend/render/shaders/blur_upsample.frag b/src/backend/render/shaders/blur_upsample.frag deleted file mode 100644 index 588fcb65..00000000 --- a/src/backend/render/shaders/blur_upsample.frag +++ /dev/null @@ -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; - } -} diff --git a/src/backend/render/shaders/clipped_surface.frag b/src/backend/render/shaders/clipped_surface.frag index d99d1a56..dd863d23 100644 --- a/src/backend/render/shaders/clipped_surface.frag +++ b/src/backend/render/shaders/clipped_surface.frag @@ -25,7 +25,6 @@ uniform float tint; uniform vec2 geo_size; uniform vec4 corner_radius; uniform mat3 input_to_geo; -uniform float noise; float rounding_alpha(vec2 coords, vec2 size) { vec2 center; @@ -52,26 +51,15 @@ float rounding_alpha(vec2 coords, vec2 size) { 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() { - if (alpha <= 0.0) { - discard; - } + vec3 coords_geo = input_to_geo * vec3(v_coords, 1.0); + // Sample the texture. vec4 color = texture2D(tex, v_coords); #if defined(NO_ALPHA) color = vec4(color.rgb, 1.0); #endif - if (color.a <= 0.0) { - discard; - } - vec3 coords_geo = input_to_geo * vec3(v_coords, 1.0); if (coords_geo.x < 0.0 || 1.0 < coords_geo.x || coords_geo.y < 0.0 || 1.0 < coords_geo.y) { // Clip outside geometry. color = vec4(0.0); @@ -80,20 +68,8 @@ void main() { color = color * rounding_alpha(coords_geo.xy * geo_size, geo_size); } - if (color.a <= 0.0) { - discard; - } - - if (noise > 0.0) { - // Add noise fx - // This can be used to achieve a glass look - float noiseHash = hash(v_coords); - float noiseAmount = (mod(noiseHash, 1.0) - 0.5); - color.rgb += noiseAmount * noise; - } - // Apply final alpha and tint. - color *= alpha; + color = color * alpha; #if defined(DEBUG_FLAGS) if (tint == 1.0) diff --git a/src/backend/render/shadow.rs b/src/backend/render/shadow.rs index 37b1fdec..edb80634 100644 --- a/src/backend/render/shadow.rs +++ b/src/backend/render/shadow.rs @@ -1,6 +1,6 @@ use std::{borrow::Borrow, cell::RefCell, collections::HashMap}; -use glam::{Affine2, Mat3, Vec2}; +use cgmath::{Matrix3, Vector2}; use smithay::{ backend::renderer::{ element::Kind, @@ -113,26 +113,26 @@ impl ShadowShader { let mut shader_geo = Rectangle::new(Point::from((-width, -width)), shader_size); let window_geo = Rectangle::new(Point::new(0., 0.) - offset - shader_geo.loc, geo.size); - let area_size = Vec2::new(shader_geo.size.w as f32, shader_geo.size.h as f32); - let geo_loc = Vec2::new(-shader_geo.loc.x as f32, -shader_geo.loc.y as f32); + let area_size = Vector2::new(shader_geo.size.w, shader_geo.size.h); + let geo_loc = Vector2::new(-shader_geo.loc.x, -shader_geo.loc.y); shader_geo.loc += offset + geo.loc; - let input_to_geo = Mat3::from( - Affine2::from_scale(area_size) - * Affine2::from_translation(Vec2::new( - -geo_loc.x / area_size.x, - -geo_loc.y / area_size.y, - )), - ); + let input_to_geo = (Matrix3::from_nonuniform_scale(area_size.x, area_size.y) + * Matrix3::from_translation(Vector2::new( + -geo_loc.x / area_size.x, + -geo_loc.y / area_size.y, + ))) + .cast::() + .unwrap(); - let window_geo_loc = Vec2::new(window_geo.loc.x as f32, window_geo.loc.y as f32); - let window_input_to_geo = Mat3::from( - Affine2::from_scale(area_size) - * Affine2::from_translation(Vec2::new( - -window_geo_loc.x / area_size.x, - -window_geo_loc.y / area_size.y, - )), - ); + let window_geo_loc = Vector2::new(window_geo.loc.x, window_geo.loc.y); + let window_input_to_geo = (Matrix3::from_nonuniform_scale(area_size.x, area_size.y) + * Matrix3::from_translation(Vector2::new( + -window_geo_loc.x / area_size.x, + -window_geo_loc.y / area_size.y, + ))) + .cast::() + .unwrap(); let element = PixelShaderElement::new( shader, diff --git a/src/backend/render/wayland/blur_effect.rs b/src/backend/render/wayland/blur_effect.rs deleted file mode 100644 index c4d27fac..00000000 --- a/src/backend/render/wayland/blur_effect.rs +++ /dev/null @@ -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> = LazyLock::new(|| { - let mut params = Vec::new(); - - let mut remaining_steps = MAX_STEPS as isize; - let offsets = [ - // min offset, max offset, extended radius to avoid artifacts - (1.0, 2.0, 10), - (2.0, 3.0, 20), - (2.0, 5.0, 50), - (3.0, 8.0, 150), - ]; - - let sum = offsets.iter().map(|(min, max, _)| *max - *min).sum::(); - for (i, (min, max, extended_radius)) in offsets.into_iter().enumerate() { - let mut iter_num = f64::ceil((max - min) / sum * (MAX_STEPS as f64)) as usize; - remaining_steps -= iter_num as isize; - - if remaining_steps < 0 { - iter_num = iter_num.saturating_add_signed(remaining_steps); - } - - let diff = max - min; - for j in 1..=iter_num { - params.push(BlurParameters { - passes: i + 1, - offset: min + (diff / iter_num as f64) * j as f64, - extended_radius, - }); - } - } - - trace!("Computed blur values: {:#?}", ¶ms); - params -}); - -#[derive(Debug, Clone)] -pub struct BlurShaders { - down: GlesTexProgram, - up: GlesTexProgram, -} - -impl BlurShaders { - pub fn compile(renderer: &mut GlesRenderer) -> Result { - let up = renderer.compile_custom_texture_shader( - BLUR_UPSAMPLE_SHADER, - &[ - UniformName::new("half_pixel", UniformType::_2f), - UniformName::new("offset", UniformType::_1f), - ], - )?; - let down = renderer.compile_custom_texture_shader( - BLUR_DOWNSAMPLE_SHADER, - &[ - UniformName::new("half_pixel", UniformType::_2f), - UniformName::new("offset", UniformType::_1f), - ], - )?; - - Ok(BlurShaders { up, down }) - } - - pub fn get(renderer: &R) -> Self { - Borrow::::borrow(renderer.glow_renderer()) - .egl_context() - .user_data() - .get::() - .expect("Custom Shaders not initialized") - .clone() - } -} - -type BlurTexture = Mutex>; - -#[derive(Debug)] -pub struct BlurState { - pub id: Id, - pub renderer_id: Option>, - pub src: Size, - pub offset: f64, - pub passes: usize, - pub region: Vec>, - pub commit: CommitCounter, -} - -unsafe impl Send for BlurState {} -unsafe impl Sync for BlurState {} - -impl 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, - extended_offset: Point, - geometry: Rectangle, - scaling_shaders: BlurShaders, - render_shader: GlesTexProgram, - region: Vec>, - offset: f64, - passes: usize, - uniforms: Vec>, -} - -impl BlurElement { - pub fn from_state( - renderer: &mut R, - state: &mut BlurState, - geometry: Rectangle, - output_scale: f64, - radii: [u8; 4], - strength: usize, - ) -> Result, R::Error> { - let region = vec![Rectangle::from_size(geometry.size.to_i32_round())]; - - Self::internal( - renderer, - state, - geometry, - ®ion, - output_scale, - radii, - strength, - ) - } - - pub fn from_surface( - renderer: &mut R, - states: &SurfaceData, - geometry: Rectangle, - output_scale: f64, - radii: [u8; 4], - strength: usize, - ) -> Result, R::Error> { - let mut blur_region_state = states.cached_state.get::(); - let Some(region) = blur_region_state.current().blur_region.as_ref() else { - return Ok(None); - }; - - let state = states - .data_map - .get_or_insert_threadsafe::, _>(Default::default); - - Self::internal( - renderer, - &mut state.lock().unwrap(), - geometry, - region, - output_scale, - radii, - strength, - ) - } - - pub fn internal( - renderer: &mut R, - state: &mut BlurState, - geometry: Rectangle, - region: &Vec>, - output_scale: f64, - radii: [u8; 4], - strength: usize, - ) -> Result, R::Error> { - if strength == 0 || 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::::new(radius, radius); - extended_geo.size += Size::::new(radius, radius).upscale(2.); - - // compute input_to_geo so that it crops the extended capture radius - let geo_scale = { - let Scale { x, y } = geo.size / extended_geo.size; - 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::::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 { - Rectangle::from_size(self.src) - } - - fn geometry(&self, scale: Scale) -> Rectangle { - self.geometry.to_physical_precise_round(scale) - } - - fn transform(&self) -> Transform { - Transform::Normal - } - - fn damage_since( - &self, - scale: Scale, - commit: Option, - ) -> DamageSet { - if 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 RenderElement for BlurElement -where - R::TextureId: Send + 'static, -{ - fn capture_framebuffer( - &self, - frame: &mut ::Frame<'_, '_>, - src: Rectangle, - dst: Rectangle, - cache: &UserDataMap, - ) -> Result<(), ::Error> { - let transform = frame.transformation(); - let tex_size = self.src.to_i32_round(); - let glow_frame = ::glow_frame_mut(frame); - let gles_frame = BorrowMut::>::borrow_mut(glow_frame); - let mut renderer = gles_frame.renderer(); - - let texture_ref = cache.get_or_insert_threadsafe(BlurTexture::::default); - let mut texture_entry = texture_ref.lock().unwrap(); - if texture_entry.as_ref().is_some_and(|tex| { - tex.size() != tex_size - || 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, - dst: Rectangle, - damage: &[Rectangle], - opaque_regions: &[Rectangle], - 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::>(); - let cache = cache.expect("Framebuffer element without cache?"); - let Some(texture) = cache.get::>() else { - return Err(R::from_gles_error(GlesError::BlitError)); - }; - let texture_ref = texture.lock().unwrap(); - - if let Some(tex) = texture_ref.as_ref() { - BorrowMut::::borrow_mut(::glow_frame_mut(frame)) - .override_default_tex_program(self.render_shader.clone(), self.uniforms.clone()); - frame.render_texture_from_to( - tex, - src, - dst, - &damage, - opaque_regions, - Transform::Normal, - 1.0, - )?; - BorrowMut::::borrow_mut(::glow_frame_mut(frame)) - .clear_tex_program_override(); - } - Ok(()) - } -} - -fn blit_from_active_fb( - frame: &mut GlesFrame<'_, '_>, - src: Rectangle, - dst: Rectangle, - transform: Transform, - to_texture: &mut GlesTexture, -) -> Result { - 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(()) -} diff --git a/src/backend/render/wayland/mod.rs b/src/backend/render/wayland/mod.rs deleted file mode 100644 index c2b7d4ec..00000000 --- a/src/backend/render/wayland/mod.rs +++ /dev/null @@ -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 where R: AsGlowRenderer + ImportAll, R::TextureId: Send; - Blur=BlurElement, - Clipped=ClippedSurfaceRenderElement, - Wayland=WaylandSurfaceRenderElement, -} - -pub fn push_render_elements_from_surface_tree( - renderer: &mut R, - main_surface: &wl_surface::WlSurface, - location: impl Into>, - geometry: impl Into>, - scale: impl Into>, - alpha: f32, - should_clip: bool, - radii: [u8; 4], - blur_geometry: impl Into>>, - blur_strength: usize, - kind: impl Into, - push_above: &mut dyn FnMut(SurfaceRenderElement), - mut push_below: Option<&mut dyn FnMut(SurfaceRenderElement)>, -) 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::(); - - 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::(); - 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 = 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, - ); -} diff --git a/src/input/mod.rs b/src/input/mod.rs index 0cb26a9d..da3014ae 100644 --- a/src/input/mod.rs +++ b/src/input/mod.rs @@ -25,8 +25,7 @@ use crate::{ }, utils::{prelude::*, quirks::workspace_overview_is_open}, wayland::handlers::{ - image_copy_capture::{SessionHolder, cursor_capture_constraints}, - xwayland_keyboard_grab::XWaylandGrabSeat, + image_copy_capture::SessionHolder, xwayland_keyboard_grab::XWaylandGrabSeat, }, }; use calloop::{ @@ -36,15 +35,14 @@ use calloop::{ use cosmic_comp_config::{NumlockState, workspace::WorkspaceLayout}; use cosmic_settings_config::shortcuts; use cosmic_settings_config::shortcuts::action::{Direction, ResizeDirection}; -#[cfg(feature = "logind")] -use smithay::backend::input::{Switch, SwitchState, SwitchToggleEvent}; use smithay::{ backend::input::{ AbsolutePositionEvent, Axis, AxisRelativeDirection, AxisSource, Device, DeviceCapability, GestureBeginEvent, GestureEndEvent, GesturePinchUpdateEvent as _, GestureSwipeUpdateEvent as _, InputBackend, InputEvent, KeyState, KeyboardKeyEvent, - PointerAxisEvent, ProximityState, TabletToolButtonEvent, TabletToolEvent, - TabletToolProximityEvent, TabletToolTipEvent, TabletToolTipState, TouchEvent, + PointerAxisEvent, ProximityState, Switch, SwitchState, SwitchToggleEvent, + TabletToolButtonEvent, TabletToolEvent, TabletToolProximityEvent, TabletToolTipEvent, + TabletToolTipState, TouchEvent, }, desktop::{PopupKeyboardGrab, WindowSurfaceType, utils::under_from_surface_tree}, input::{ @@ -61,19 +59,22 @@ use smithay::{ output::Output, reexports::{ 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::{ compositor::CompositorHandler, - image_copy_capture::CursorSessionRef, + image_copy_capture::{BufferConstraints, CursorSessionRef}, keyboard_shortcuts_inhibit::KeyboardShortcutsInhibitorSeat, pointer_constraints::{PointerConstraint, with_pointer_constraint}, seat::WaylandFocus, tablet_manager::{TabletDescriptor, TabletSeatTrait}, }, }; -use tracing::{error, trace}; +use tracing::{error, trace, warn}; use xkbcommon::xkb::{Keycode, Keysym}; use std::{ @@ -335,7 +336,7 @@ impl State { // Constraint does not apply if not within region if !constraint.region().is_none_or(|x| { x.contains( - (ptr.current_location() - *surface_loc).to_i32_floor(), + (ptr.current_location() - *surface_loc).to_i32_round(), ) }) { return; @@ -380,25 +381,14 @@ impl State { .unwrap_or(current_output.clone()); drop(shell); let output_geometry = output.geometry(); - - let scale = output.current_scale().fractional_scale(); - let physical = output - .current_mode() - .map(|mode| output.current_transform().transform_size(mode.size)) - .unwrap_or_default(); - let logical = physical.to_f64().to_logical(scale); - let output_geometry_loc = output_geometry.loc.to_f64(); - // output_geometry.size is a rounded value and may undershoot/overshoot the accurate logical size - // We constrain the position with: - // - output_geometry.size so that we don't send leave events to a fullscreen app - // - logical size so that the position doesn't end up outside the actual size of the output - // See https://github.com/pop-os/cosmic-comp/pull/2568 - let max_x = output_geometry_loc.x - + logical.w.min(output_geometry.size.w as f64).next_down(); - let max_y = output_geometry_loc.y - + logical.h.min(output_geometry.size.h as f64).next_down(); - position.x = position.x.clamp(output_geometry_loc.x, max_x); - position.y = position.y.clamp(output_geometry_loc.y, max_y); + position.x = position.x.clamp( + output_geometry.loc.x as f64, + (output_geometry.loc.x + output_geometry.size.w - 1) as f64, + ); + position.y = position.y.clamp( + output_geometry.loc.y as f64, + (output_geometry.loc.y + output_geometry.size.h - 1) as f64, + ); if ptr.is_grabbed() { if seat @@ -552,7 +542,7 @@ impl State { if let Some(region) = &confine_region && !region - .contains((pos.as_logical() - *surface_loc).to_i32_floor()) + .contains((pos.as_logical() - *surface_loc).to_i32_round()) { return (false, None); } @@ -623,7 +613,7 @@ impl State { PointerConstraint::Confined(confined) => confined.region(), }; 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)) { constraint.activate(); } @@ -648,13 +638,37 @@ impl State { seat.set_active_output(&output); } - update_output_image_copy_cursor_position( - &shell, - &self.common.clock, - &output, - &seat, - position, - ); + for session in cursor_sessions_for_output(&shell, &output) { + if let Some((geometry, offset)) = seat.cursor_geometry( + (position - output_geometry.loc.to_f64()) + .as_logical() + .to_buffer( + output.current_scale().fractional_scale(), + output.current_transform(), + &output_geometry.size.to_f64().as_logical(), + ), + self.common.clock.now(), + ) { + 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, .. } => { @@ -693,13 +707,37 @@ impl State { ptr.frame(self); let shell = self.common.shell.read(); - update_output_image_copy_cursor_position( - &shell, - &self.common.clock, - &output, - &seat, - position, - ); + for session in cursor_sessions_for_output(&shell, &output) { + if let Some((geometry, offset)) = seat.cursor_geometry( + (position - output_geometry.loc.to_f64()) + .as_logical() + .to_buffer( + output.current_scale().fractional_scale(), + output.current_transform(), + &output_geometry.size.to_f64().as_logical(), + ), + self.common.clock.now(), + ) { + 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, .. } => { @@ -749,12 +787,7 @@ impl State { let shell = self.common.shell.read(); State::element_under(global_position, &output, &shell, &seat) }; - // Grabbing a tiling resize handle (the gap between tiles) must not change keyboard focus - let on_resize_fork = matches!( - seat.get_pointer().unwrap().current_focus(), - Some(PointerFocusTarget::ResizeFork(_)) - ); - if let Some(target) = under.filter(|_| !on_resize_fork) { + if let Some(target) = under { if let Some(surface) = target.toplevel().map(Cow::into_owned) && seat.get_keyboard().unwrap().modifier_state().logo && !shortcuts_inhibited @@ -969,10 +1002,6 @@ impl State { if let Some(horizontal_amount) = event.amount(Axis::Horizontal) { if horizontal_amount != 0.0 { frame = frame - .relative_direction( - Axis::Horizontal, - event.relative_direction(Axis::Horizontal), - ) .value(Axis::Horizontal, scroll_factor * horizontal_amount); if let Some(discrete) = event.amount_v120(Axis::Horizontal) { frame = frame.v120( @@ -986,12 +1015,8 @@ impl State { } if let Some(vertical_amount) = event.amount(Axis::Vertical) { if vertical_amount != 0.0 { - frame = frame - .relative_direction( - Axis::Vertical, - event.relative_direction(Axis::Vertical), - ) - .value(Axis::Vertical, scroll_factor * vertical_amount); + frame = + frame.value(Axis::Vertical, scroll_factor * vertical_amount); if let Some(discrete) = event.amount_v120(Axis::Vertical) { frame = frame.v120( Axis::Vertical, @@ -1574,7 +1599,6 @@ impl State { } } InputEvent::Special(_) => {} - #[allow(unused_variables)] InputEvent::SwitchToggle { event } => { #[cfg(feature = "logind")] if event.switch() == Some(Switch::Lid) && self.common.inhibit_lid_fd.is_some() { @@ -1596,7 +1620,7 @@ impl State { if let Err(err) = self.refresh_output_config() { if !closed { - tracing::warn!(?err, "Failed to re-enable internal connector"); + warn!(?err, "Failed to re-enable internal connector"); if let Some(output) = output { use cosmic_comp_config::output::comp::OutputState; @@ -2108,7 +2132,6 @@ impl State { layer, popup, location, - .. } => { if layer.can_receive_keyboard_focus() { let surface = popup.wl_surface(); @@ -2126,9 +2149,7 @@ impl State { } } } - Stage::LayerSurface { - layer, location, .. - } => { + Stage::LayerSurface { layer, location } => { if under_from_surface_tree( layer.wl_surface(), global_pos.as_logical(), @@ -2171,7 +2192,7 @@ impl State { if Rectangle::new(offset.as_local(), output_geo.size) .intersection(output_geo) .is_some_and(|geometry| { - geometry.contains(global_pos.to_local(output).to_i32_floor()) + geometry.contains(global_pos.to_local(output).to_i32_round()) }) && let Some(element) = workspace.popup_element_under(location, seat) { @@ -2185,7 +2206,7 @@ impl State { if Rectangle::new(offset.as_local(), output_geo.size) .intersection(output_geo) .is_some_and(|geometry| { - geometry.contains(global_pos.to_local(output).to_i32_floor()) + geometry.contains(global_pos.to_local(output).to_i32_round()) }) && let Some(element) = workspace.toplevel_element_under(location, seat) { @@ -2280,9 +2301,7 @@ impl State { )))); } } - Stage::LayerSurface { - layer, location, .. - } => { + Stage::LayerSurface { layer, location } => { let surface = layer.wl_surface(); if let Some((surface, surface_loc)) = under_from_surface_tree( surface, @@ -2388,7 +2407,7 @@ impl State { if let Some(constraint) = constraint && 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); } true @@ -2401,7 +2420,7 @@ impl State { if is_legal(pos_in_element) { let x = workspace_origin.x + origin.x + pos_in_element.x; let y = workspace_origin.y + origin.y + pos_in_element.y; - Some((Point::<_, Global>::new(x, y), output.clone())) + Some((Point::new(x, y), output.clone())) } else { None } @@ -2411,34 +2430,11 @@ impl State { }; if let Some((point, output)) = point_and_output { - // TODO: Replace with `wl_pointer.warp` let original_position = pointer.current_location(); - let serial = SERIAL_COUNTER.next_serial(); - let under = State::surface_under(point, &output, &self.common.shell.write()) - .map(|(target, pos)| (target, pos.as_logical())); - let time = self.common.clock.now(); - pointer.relative_motion( - self, - under.clone(), - &RelativeMotionEvent { - delta: (0., 0.).into(), - delta_unaccel: (0., 0.).into(), - utime: time.as_micros(), - }, - ); - pointer.motion( - self, - under, - &MotionEvent { - location: point.as_logical(), - serial, - time: time.as_millis(), - }, - ); - pointer.frame(self); + pointer.set_location(point); 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 .seats @@ -2453,13 +2449,31 @@ impl State { self.common.config.cosmic_conf.accessibility_zoom.view_moves, ); - update_output_image_copy_cursor_position( - &shell, - &self.common.clock, - &output, - &seat, - point, - ); + let output_geometry = output.geometry(); + for session in cursor_sessions_for_output(&shell, &output) { + if let Some((geometry, offset)) = seat.cursor_geometry( + point.to_buffer( + 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) + { + 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()) } - -pub fn update_output_image_copy_cursor_position( - shell: &Shell, - clock: &Clock, - output: &Output, - seat: &Seat, - position: Point, -) { - let output_geometry = output.geometry(); - for session in cursor_sessions_for_output(&shell, &output) { - if let Some(cursor_geometry) = seat.cursor_geometry( - (position - output_geometry.loc.to_f64()) - .as_logical() - .to_buffer( - output.current_scale().fractional_scale(), - output.current_transform(), - &output_geometry.size.to_f64().as_logical(), - ), - clock.now(), - ) { - let constraints = cursor_capture_constraints(Some(cursor_geometry)); - if session - .current_constraints() - .map(|current_constraints| current_constraints.size != constraints.size) - .unwrap_or(true) - { - session.update_constraints(constraints); - } - session.set_cursor_hotspot(cursor_geometry.hotspot); - session.set_cursor_pos(Some(cursor_geometry.geometry.loc)); - } - } -} diff --git a/src/lib.rs b/src/lib.rs index 542162eb..09d1b961 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -26,9 +26,7 @@ use std::{ time::{Duration, Instant}, }; use tracing::{error, info, warn}; -use wayland::protocols::{ - keyboard_layout::KeyboardLayoutState, overlap_notify::OverlapNotifyState, -}; +use wayland::protocols::overlap_notify::OverlapNotifyState; use crate::wayland::handlers::compositor::client_compositor_state; @@ -81,8 +79,11 @@ impl State { 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 + let mut command = process::Command::new(&exec); + command.args(args); command.envs( session::get_env(&self.common).expect("WAYLAND_DISPLAY should be valid UTF-8"), ); @@ -93,7 +94,7 @@ impl State { }) }; - info!("Running {:?}", command.get_program()); + info!("Running {:?}", exec); command .spawn() .map_err(|err| { @@ -112,10 +113,8 @@ impl State { pub fn run(hooks: crate::hooks::Hooks) -> Result<(), Box> { let raw_args = RawArgs::from_args(); let mut cursor = raw_args.cursor(); - raw_args.next_os(&mut cursor); let git_hash = option_env!("GIT_HASH").unwrap_or("unknown"); - let mut kiosk_command = None; let mut with_xwayland = true; // Parse the arguments while let Some(arg) = raw_args.next_os(&mut cursor) { @@ -136,11 +135,7 @@ pub fn run(hooks: crate::hooks::Hooks) -> Result<(), Box> { ); return Ok(()); } - _ => { - let mut cmd = process::Command::new(arg); - cmd.args(raw_args.remaining(&mut cursor)); - kiosk_command = Some(cmd); - } + _ => {} } } @@ -174,7 +169,6 @@ pub fn run(hooks: crate::hooks::Hooks) -> Result<(), Box> { event_loop.handle(), event_loop.get_signal(), with_xwayland, - kiosk_command, ); // init backend backend::init_backend_auto(&display, &mut event_loop, &mut state)?; @@ -337,6 +331,5 @@ fn refresh(state: &mut State) { state::Common::refresh_focus(state); OverlapNotifyState::refresh(state); state.common.update_x11_stacking_order(); - KeyboardLayoutState::refresh(state); state.last_refresh = LastRefresh::At(Instant::now()); } diff --git a/src/shell/element/mod.rs b/src/shell/element/mod.rs index 14736719..f58259bf 100644 --- a/src/shell/element/mod.rs +++ b/src/shell/element/mod.rs @@ -1,21 +1,18 @@ use crate::{ - backend::render::element::AsGlowRenderer, + backend::render::element::{AsGlowRenderer, FromGlesError}, state::State, - utils::{ - iced::{IcedElementInternal, IcedRenderElement}, - prelude::*, - }, + utils::{iced::IcedElementInternal, prelude::*}, }; use calloop::LoopHandle; use cosmic_comp_config::AppearanceConfig; use id_tree::NodeId; use smithay::{ backend::{ - drm::DrmNode, input::KeyState, renderer::{ element::{ Element, Kind, RenderElement, UnderlyingStorage, + memory::MemoryRenderBufferRenderElement, utils::{CropRenderElement, RelocateRenderElement, RescaleRenderElement}, }, gles::element::PixelShaderElement, @@ -127,9 +124,9 @@ impl fmt::Debug for CosmicMapped { } } -#[derive(Clone, Debug)] +#[derive(Clone)] pub struct CosmicMappedKey(CosmicMappedKeyInner); -#[derive(Clone, Debug)] +#[derive(Clone)] enum CosmicMappedKeyInner { Window(Weak>>), Stack(Weak>>), @@ -168,20 +165,6 @@ impl PartialEq for CosmicMappedKey { } impl Eq for CosmicMappedKey {} -impl PartialEq for CosmicMapped { - fn eq(&self, other: &CosmicMappedKey) -> bool { - match (&self.element, &other.0) { - (CosmicMappedInternal::Window(window), CosmicMappedKeyInner::Window(weak)) => { - Arc::as_ptr(&window.0.0) == weak.as_ptr() - } - (CosmicMappedInternal::Stack(stack), CosmicMappedKeyInner::Stack(weak)) => { - Arc::as_ptr(&stack.0.0) == weak.as_ptr() - } - _ => false, - } - } -} - impl PartialEq for CosmicMapped { fn eq(&self, other: &Self) -> bool { self.element == other.element @@ -600,38 +583,33 @@ impl CosmicMapped { } } - pub fn push_popup_render_elements( + pub fn popup_render_elements( &self, renderer: &mut R, location: smithay::utils::Point, scale: smithay::utils::Scale, alpha: f32, - scanout_node: Option, - push: &mut dyn FnMut(CosmicMappedRenderElement), - ) where + ) -> Vec + where R: AsGlowRenderer, R::TextureId: Send + Clone + 'static, CosmicMappedRenderElement: RenderElement, + C: From>, { match &self.element { - CosmicMappedInternal::Stack(s) => s.push_popup_render_elements( - renderer, - location, - scale, - alpha, - scanout_node, - &mut |elem| push(elem.into()), - ), - CosmicMappedInternal::Window(w) => w.push_popup_render_elements( - renderer, - location, - scale, - alpha, - scanout_node, - &mut |elem| push(elem.into()), - ), + CosmicMappedInternal::Stack(s) => s + .popup_render_elements::>( + renderer, location, scale, alpha, + ), + CosmicMappedInternal::Window(w) => w + .popup_render_elements::>( + renderer, location, scale, alpha, + ), _ => unreachable!(), } + .into_iter() + .map(C::from) + .collect() } pub fn shadow_render_element( @@ -678,7 +656,7 @@ impl CosmicMapped { } } - pub fn push_render_elements( + pub fn render_elements( &self, renderer: &mut R, location: smithay::utils::Point, @@ -686,16 +664,15 @@ impl CosmicMapped { scale: smithay::utils::Scale, alpha: f32, scanout_override: Option, - scanout_node: Option, - push_above: &mut dyn FnMut(CosmicMappedRenderElement), - push_below: &mut dyn FnMut(CosmicMappedRenderElement), - ) where + ) -> Vec + where R: AsGlowRenderer, R::TextureId: Send + Clone + 'static, CosmicMappedRenderElement: RenderElement, + C: From>, { #[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_geo = window.geometry(); let (min_size, max_size, size) = ( @@ -856,38 +833,41 @@ impl CosmicMapped { scale.x, 0.8, ) { - Ok(element) => push_above(element.into()), + Ok(element) => vec![CosmicMappedRenderElement::from(element)], Err(err) => { debug!(?err, "Error rendering debug overlay."); + Vec::new() } } + } else { + Vec::new() }; + #[cfg(not(feature = "debug"))] + let mut elements = Vec::new(); - match &self.element { - CosmicMappedInternal::Stack(s) => s.push_render_elements( + #[cfg_attr(not(feature = "debug"), allow(unused_mut))] + elements.extend(match &self.element { + CosmicMappedInternal::Stack(s) => s.render_elements::>( renderer, location, max_size, scale, alpha, scanout_override, - scanout_node, - &mut |elem| push_above(elem.into()), - &mut |elem| push_below(elem.into()), - ), - CosmicMappedInternal::Window(w) => w.push_render_elements( - renderer, - location, - max_size, - scale, - alpha, - scanout_override, - scanout_node, - &mut |elem| push_above(elem.into()), - &mut |elem| push_below(elem.into()), ), + CosmicMappedInternal::Window(w) => w + .render_elements::>( + renderer, + location, + max_size, + scale, + alpha, + scanout_override, + ), _ => unreachable!(), - } + }); + + elements.into_iter().map(C::from).collect() } pub(crate) fn update_theme(&self, theme: cosmic::Theme) { @@ -1129,7 +1109,7 @@ where GrabbedWindow(RescaleRenderElement>), FocusIndicator(PixelShaderElement), Overlay(PixelShaderElement), - StackHoverIndicator(IcedRenderElement), + StackHoverIndicator(MemoryRenderBufferRenderElement), #[cfg(feature = "debug")] Egui(TextureRenderElement), } @@ -1137,7 +1117,7 @@ where impl Element for CosmicMappedRenderElement where R: AsGlowRenderer, - R::TextureId: Send + 'static, + R::TextureId: 'static, { fn id(&self) -> &smithay::backend::renderer::element::Id { match self { @@ -1358,7 +1338,8 @@ where impl RenderElement for CosmicMappedRenderElement where R: AsGlowRenderer, - R::TextureId: Send + 'static, + R::TextureId: 'static, + R::Error: FromGlesError, { fn draw( &self, @@ -1391,7 +1372,7 @@ where opaque_regions, cache, ) - .map_err(R::from_gles_error), + .map_err(FromGlesError::from_gles_error), CosmicMappedRenderElement::MovingStack(elem) => { elem.draw(frame, src, dst, damage, opaque_regions, cache) } @@ -1413,7 +1394,7 @@ where opaque_regions, cache, ) - .map_err(R::from_gles_error), + .map_err(FromGlesError::from_gles_error), CosmicMappedRenderElement::Overlay(elem) => RenderElement::::draw( elem, R::glow_frame_mut(frame), @@ -1423,7 +1404,7 @@ where opaque_regions, cache, ) - .map_err(R::from_gles_error), + .map_err(FromGlesError::from_gles_error), CosmicMappedRenderElement::StackHoverIndicator(elem) => { elem.draw(frame, src, dst, damage, opaque_regions, cache) } @@ -1439,7 +1420,7 @@ where opaque_regions, cache, ) - .map_err(R::from_gles_error) + .map_err(FromGlesError::from_gles_error) } } } @@ -1502,7 +1483,7 @@ where dst, cache, ) - .map_err(R::from_gles_error) + .map_err(FromGlesError::from_gles_error) } CosmicMappedRenderElement::MovingStack(elem) => { elem.capture_framebuffer(frame, src, dst, cache) @@ -1524,7 +1505,7 @@ where dst, cache, ) - .map_err(R::from_gles_error) + .map_err(FromGlesError::from_gles_error) } CosmicMappedRenderElement::Overlay(elem) => { RenderElement::::capture_framebuffer( @@ -1534,7 +1515,7 @@ where dst, cache, ) - .map_err(R::from_gles_error) + .map_err(FromGlesError::from_gles_error) } CosmicMappedRenderElement::StackHoverIndicator(elem) => { elem.capture_framebuffer(frame, src, dst, cache) @@ -1545,7 +1526,7 @@ where RenderElement::::capture_framebuffer( elem, glow_frame, src, dst, cache, ) - .map_err(R::from_gles_error) + .map_err(FromGlesError::from_gles_error) } } } @@ -1583,13 +1564,13 @@ where } } -impl From> for CosmicMappedRenderElement +impl From> for CosmicMappedRenderElement where R: AsGlowRenderer, R::TextureId: 'static, CosmicMappedRenderElement: RenderElement, { - fn from(elem: IcedRenderElement) -> Self { + fn from(elem: MemoryRenderBufferRenderElement) -> Self { CosmicMappedRenderElement::StackHoverIndicator(elem) } } diff --git a/src/shell/element/resize_indicator.rs b/src/shell/element/resize_indicator.rs index fb5441c3..49dc4781 100644 --- a/src/shell/element/resize_indicator.rs +++ b/src/shell/element/resize_indicator.rs @@ -1,11 +1,10 @@ -use std::sync::{Arc, Mutex}; +use std::sync::Mutex; use crate::{ - backend::render::element::AsGlowRenderer, config::Config, fl, shell::grabs::ResizeEdge, - utils::iced::{IcedElement, IcedRenderElement, Program}, + utils::iced::{IcedElement, Program}, }; use calloop::LoopHandle; @@ -13,330 +12,175 @@ use cosmic::{ Apply, iced::{ Alignment, - core::{Background, Border, Color, Length}, - widget::{container, row, space}, + widget::{column, container, row, space}, }, + iced_core::{Background, Border, Color, Length}, theme, widget::{icon::from_name, text}, }; use cosmic_settings_config::shortcuts::action::{Action, ResizeDirection}; -use smithay::{ - backend::renderer::ImportMem, - desktop::space::SpaceElement, - output::Output, - utils::{Logical, Physical, Point, Rectangle, Scale, Size}, -}; +use smithay::utils::Size; -#[derive(Debug, Clone)] -pub struct ResizeIndicator { - edges: ResizeEdge, - direction: Arc>, - size: Size, +pub type ResizeIndicator = IcedElement; - center_elem: IcedElement, - left_elem: IcedElement, - top_elem: IcedElement, - right_elem: IcedElement, - down_elem: IcedElement, -} - -const ARROW_SIZE: i32 = 36; - -impl ResizeIndicator { - pub fn new( - direction: ResizeDirection, - config: &Config, - evlh: LoopHandle<'static, crate::state::State>, - mut theme: cosmic::Theme, - ) -> ResizeIndicator { - theme.transparent = theme.cosmic().frosted_system_interface; - let direction = Arc::new(Mutex::new(direction)); - - ResizeIndicator { - edges: ResizeEdge::all(), - direction: direction.clone(), - size: Size::default(), - center_elem: IcedElement::new( - ResizeIndicatorInternal { - shortcut1: config - .shortcuts - .iter() - .find_map(|(pattern, action)| { - (*action == Action::Resizing(ResizeDirection::Outwards)) - .then_some(pattern) - }) - .map(|pattern| format!("{}: ", pattern.to_string())) - .unwrap_or_else(|| crate::fl!("unknown-keybinding")), - shortcut2: config - .shortcuts - .iter() - .find_map(|(pattern, action)| { - (*action == Action::Resizing(ResizeDirection::Inwards)) - .then_some(pattern) - }) - .map(|pattern| format!("{}: ", pattern.to_string())) - .unwrap_or_else(|| crate::fl!("unknown-keybinding")), - }, - Size::from((1, 1)), - evlh.clone(), - theme.clone(), - ), - left_elem: IcedElement::new( - ResizeIndicatorArrow { - direction: direction.clone(), - icon_outwards: "go-previous-symbolic", - icon_inwards: "go-next-symbolic", - }, - Size::from((ARROW_SIZE, ARROW_SIZE)), - evlh.clone(), - theme.clone(), - ), - top_elem: IcedElement::new( - ResizeIndicatorArrow { - direction: direction.clone(), - icon_outwards: "go-up-symbolic", - icon_inwards: "go-down-symbolic", - }, - Size::from((ARROW_SIZE, ARROW_SIZE)), - evlh.clone(), - theme.clone(), - ), - right_elem: IcedElement::new( - ResizeIndicatorArrow { - direction: direction.clone(), - icon_outwards: "go-next-symbolic", - icon_inwards: "go-previous-symbolic", - }, - Size::from((ARROW_SIZE, ARROW_SIZE)), - evlh.clone(), - theme.clone(), - ), - down_elem: IcedElement::new( - ResizeIndicatorArrow { - direction, - icon_outwards: "go-down-symbolic", - icon_inwards: "go-up-symbolic", - }, - Size::from((ARROW_SIZE, ARROW_SIZE)), - evlh.clone(), - theme.clone(), - ), - } - } - - pub fn resize(&mut self, size: Size) { - let minimum = self.center_elem.minimum_size(); - let new_size = Size::::new(size.w.min(minimum.w), size.h.min(minimum.h)); - self.center_elem.resize(new_size); - self.size = size; - } - - pub fn push_render_elements( - &self, - renderer: &mut R, - location: Point, - scale: Scale, - alpha: f32, - push_above: &mut dyn FnMut(IcedRenderElement), - mut push_below: Option<&mut dyn FnMut(IcedRenderElement)>, - ) where - R: AsGlowRenderer + ImportMem, - R::TextureId: Send + Clone + 'static, - { - let elem_size = self.center_elem.current_size(); - let center_location = Point::new( - self.size.w.saturating_sub(elem_size.w) / 2, - self.size.h.saturating_sub(elem_size.h) / 2, - ); - let left_location = Point::new(0, self.size.h.saturating_sub(ARROW_SIZE) / 2); - let top_location = Point::new(self.size.w.saturating_sub(ARROW_SIZE) / 2, 0); - let right_location = Point::new( - self.size.w.saturating_sub(ARROW_SIZE), - self.size.h.saturating_sub(ARROW_SIZE) / 2, - ); - let down_location = Point::new( - self.size.w.saturating_sub(ARROW_SIZE) / 2, - self.size.h.saturating_sub(ARROW_SIZE), - ); - let radii = self - .center_elem - .with_theme(|theme| theme.cosmic().radius_s()) - .map(|x| x.round() as u8); - - self.center_elem.push_render_elements( - renderer, - location + center_location.to_physical_precise_round(scale), - scale, - alpha, - radii, - push_above, - if let Some(push_below) = push_below.as_mut() { - Some(&mut **push_below) - } else { - None - }, - ); - let mut render = move |elem: &IcedElement, - loc: Point| { - elem.push_render_elements( - renderer, - location + loc.to_physical_precise_round(scale), - scale, - alpha, - radii, - push_above, - if let Some(push_below) = push_below.as_mut() { - Some(&mut **push_below) - } else { - None - }, - ); - }; - - if self.edges.contains(ResizeEdge::LEFT) { - render(&self.left_elem, left_location); - } - if self.edges.contains(ResizeEdge::TOP) { - render(&self.top_elem, top_location); - } - if self.edges.contains(ResizeEdge::RIGHT) { - render(&self.right_elem, right_location); - } - if self.edges.contains(ResizeEdge::BOTTOM) { - render(&self.down_elem, down_location); - } - } - - pub fn set_edges(&mut self, edges: ResizeEdge) { - self.edges = edges; - } - - pub fn set_direction(&self, direction: ResizeDirection) { - let mut dir_ref = self.direction.lock().unwrap(); - if *dir_ref != direction { - *dir_ref = direction; - self.left_elem.force_redraw(); - self.top_elem.force_redraw(); - self.right_elem.force_redraw(); - self.down_elem.force_redraw(); - } - } - - pub fn output_enter(&self, output: &Output) { - self.center_elem - .output_enter(output, Rectangle::default() /*unused*/); - self.left_elem - .output_enter(output, Rectangle::default() /*unused*/); - self.top_elem - .output_enter(output, Rectangle::default() /*unused*/); - self.right_elem - .output_enter(output, Rectangle::default() /*unused*/); - self.down_elem - .output_enter(output, Rectangle::default() /*unused*/); - } - - pub fn output_leave(&self, output: &Output) { - self.center_elem.output_leave(output); - self.left_elem.output_leave(output); - self.top_elem.output_leave(output); - self.right_elem.output_leave(output); - self.down_elem.output_leave(output); - } +pub fn resize_indicator( + direction: ResizeDirection, + config: &Config, + evlh: LoopHandle<'static, crate::state::State>, + theme: cosmic::Theme, +) -> ResizeIndicator { + ResizeIndicator::new( + ResizeIndicatorInternal { + edges: Mutex::new(ResizeEdge::all()), + direction, + shortcut1: config + .shortcuts + .iter() + .find_map(|(pattern, action)| { + (*action == Action::Resizing(ResizeDirection::Outwards)).then_some(pattern) + }) + .map(|pattern| format!("{}: ", pattern.to_string())) + .unwrap_or_else(|| crate::fl!("unknown-keybinding")), + shortcut2: config + .shortcuts + .iter() + .find_map(|(pattern, action)| { + (*action == Action::Resizing(ResizeDirection::Inwards)).then_some(pattern) + }) + .map(|pattern| format!("{}: ", pattern.to_string())) + .unwrap_or_else(|| crate::fl!("unknown-keybinding")), + }, + Size::from((1, 1)), + evlh, + theme, + ) } pub struct ResizeIndicatorInternal { - shortcut1: String, - shortcut2: String, + pub edges: Mutex, + pub direction: ResizeDirection, + pub shortcut1: String, + pub shortcut2: String, } impl Program for ResizeIndicatorInternal { type Message = (); fn view(&self) -> cosmic::Element<'_, Self::Message> { - row(vec![ - text::heading(&self.shortcut1).into(), - text::body(fl!("grow-window")).into(), - space::horizontal().width(40).into(), - text::heading(&self.shortcut2).into(), - text::body(fl!("shrink-window")).into(), - ]) - .apply(container) - .align_x(Alignment::Center) - .align_y(Alignment::Center) - .padding(16) - .apply(container) - .class(theme::Container::custom(|theme| { - let mut background = theme.cosmic().accent_color(); - if theme.transparent { - background.alpha = theme - .cosmic() - .alpha_map - .blurred_alpha(theme.cosmic().frosted); - } - - container::Style { + 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(background.into())), + background: Some(Background::Color(theme.cosmic().accent_color().into())), border: Border { - radius: theme.cosmic().radius_s().into(), + radius: 18.0.into(), width: 0.0, color: Color::TRANSPARENT, }, shadow: Default::default(), - } - })) - .width(Length::Shrink) - .height(Length::Shrink) - .into() - } -} + }) + }; -pub struct ResizeIndicatorArrow { - direction: Arc>, - icon_outwards: &'static str, - icon_inwards: &'static str, -} - -impl Program for ResizeIndicatorArrow { - type Message = (); - - fn view(&self) -> cosmic::Element<'_, Self::Message> { - from_name( - if *self.direction.lock().unwrap() == ResizeDirection::Outwards { - self.icon_outwards + 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 { - self.icon_inwards + space::vertical().height(36).into() }, - ) - .size(20) - .prefer_svg(true) - .apply(container) - .padding(8) - .class(theme::Container::custom(|theme| { - let mut background = theme.cosmic().accent_color(); - if theme.transparent { - background.alpha = theme - .cosmic() - .alpha_map - .blurred_alpha(theme.cosmic().frosted); - } - - container::Style { - snap: true, - icon_color: Some(Color::from(theme.cosmic().accent.on)), - text_color: Some(Color::from(theme.cosmic().accent.on)), - background: Some(Background::Color(background.into())), - border: Border { - radius: theme.cosmic().radius_s().into(), - width: 0.0, - color: Color::TRANSPARENT, + 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() }, - shadow: Default::default(), - } - })) - .width(Length::Shrink) + row(vec![ + text::heading(&self.shortcut1).into(), + text::body(fl!("grow-window")).into(), + space::horizontal().width(40).into(), + text::heading(&self.shortcut2).into(), + text::body(fl!("shrink-window")).into(), + ]) + .apply(container) + .align_x(Alignment::Center) + .align_y(Alignment::Center) + .padding(16) + .apply(container) + .class(icon_container_style()) + .width(Length::Shrink) + .height(Length::Shrink) + .apply(container) + .center_x(Length::Fill) + .center_y(Length::Fill) + .into(), + if edges.contains(ResizeEdge::RIGHT) { + from_name(if self.direction == ResizeDirection::Outwards { + "go-next-symbolic" + } else { + "go-previous-symbolic" + }) + .size(20) + .prefer_svg(true) + .apply(container) + .padding(8) + .class(icon_container_style()) + .height(Length::Shrink) + .apply(container) + .center_y(Length::Fill) + .into() + } else { + space::horizontal().width(36).into() + }, + ]) + .width(Length::Fill) + .height(Length::Fill) + .into(), + if edges.contains(ResizeEdge::BOTTOM) { + from_name(if self.direction == ResizeDirection::Outwards { + "go-down-symbolic" + } else { + "go-up-symbolic" + }) + .size(20) + .prefer_svg(true) + .apply(container) + .padding(8) + .class(icon_container_style()) + .width(Length::Shrink) + .apply(container) + .center_x(Length::Fill) + .into() + } else { + space::vertical().height(36).into() + }, + ]) .into() } } diff --git a/src/shell/element/stack.rs b/src/shell/element/stack.rs index 7ce26204..058719e3 100644 --- a/src/shell/element/stack.rs +++ b/src/shell/element/stack.rs @@ -4,8 +4,11 @@ use super::{ }; use crate::{ backend::render::{ - IndicatorShader, Key, Usage, cursor::CursorState, element::AsGlowRenderer, - shadow::ShadowShader, wayland::SurfaceRenderElement, + IndicatorShader, Key, Usage, + clipped_surface::ClippedSurfaceRenderElement, + cursor::CursorState, + element::{AsGlowRenderer, FromGlesError}, + shadow::ShadowShader, }, hooks::{Decorations, HOOKS}, shell::{ @@ -16,20 +19,17 @@ use crate::{ }, state::State, utils::{ - iced::{IcedElement, IcedRenderElement, Program}, + iced::{IcedElement, Program}, prelude::*, }, }; use calloop::LoopHandle; use cosmic::{ Apply, Element as CosmicElement, Theme, - iced::{ - Alignment, - core::{Background, Border, Color, Length, border::Radius}, - id::Id, - runtime::Task, - widget::{self as iced_widget, scrollable::AbsoluteOffset}, - }, + iced::{Alignment, id::Id, widget as iced_widget}, + iced_core::{Background, Border, Color, Length, border::Radius}, + iced_runtime::Task, + iced_widget::scrollable::AbsoluteOffset, theme, widget as cosmic_widget, }; use cosmic_comp_config::AppearanceConfig; @@ -37,11 +37,14 @@ use cosmic_settings_config::shortcuts; use shortcuts::action::{Direction, FocusDirection}; use smithay::{ backend::{ - drm::DrmNode, input::KeyState, 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, glow::GlowRenderer, utils::{CommitCounter, DamageSet, OpaqueRegions}, @@ -146,10 +149,9 @@ impl CosmicStack { pub fn new>( windows: impl Iterator, handle: LoopHandle<'static, crate::state::State>, - mut theme: cosmic::Theme, + theme: cosmic::Theme, appearance: AppearanceConfig, ) -> CosmicStack { - theme.transparent = theme.cosmic().frosted_windows; let windows = windows.map(Into::into).collect::>(); assert!(!windows.is_empty()); @@ -530,7 +532,7 @@ impl CosmicStack { let geo = p.windows.lock().unwrap()[p.active.load(Ordering::SeqCst)].geometry(); if surface_type.contains(WindowSurfaceType::TOPLEVEL) { - let point_i32 = relative_pos.to_i32_floor::(); + let point_i32 = relative_pos.to_i32_round::(); if (point_i32.x - geo.loc.x >= -RESIZE_BORDER && point_i32.x - geo.loc.x < 0) || (point_i32.y - geo.loc.y >= -RESIZE_BORDER && point_i32.y - geo.loc.y < 0) || (point_i32.x - geo.loc.x >= geo.size.w @@ -646,38 +648,30 @@ impl CosmicStack { self.0.loop_handle() } - pub fn push_popup_render_elements( + pub fn popup_render_elements( &self, renderer: &mut R, location: Point, scale: Scale, alpha: f32, - scanout_node: Option, - push: &mut dyn FnMut(CosmicStackRenderElement), - ) where + ) -> Vec + where R: AsGlowRenderer, R::TextureId: Send + Clone + 'static, + C: From>, { let window_loc = location + Point::from((0, (TAB_HEIGHT as f64 * scale.y) as i32)); self.0.with_program(|p| { let windows = p.windows.lock().unwrap(); let active = p.active.load(Ordering::SeqCst); - let theme = p.theme.lock().unwrap(); - let frosted = if theme.cosmic().frosted_windows { - (theme.cosmic().frosted as u8 + 1) as usize - } else { - 0 - }; - windows[active].push_popup_render_elements( - renderer, - window_loc, - scale, - alpha, - scanout_node, - frosted, - &mut |elem| push(elem.into()), - ) + windows[active] + .popup_render_elements::>( + renderer, window_loc, scale, alpha, + ) + .into_iter() + .map(C::from) + .collect() }) } @@ -748,7 +742,7 @@ impl CosmicStack { }) } - pub fn push_render_elements( + pub fn render_elements( &self, renderer: &mut R, location: Point, @@ -756,18 +750,17 @@ impl CosmicStack { scale: Scale, alpha: f32, scanout_override: Option, - scanout_node: Option, - push_above: &mut dyn FnMut(CosmicStackRenderElement), - push_below: &mut dyn FnMut(CosmicStackRenderElement), - ) where + ) -> Vec + where R: AsGlowRenderer, R::TextureId: Send + Clone + 'static, + C: From>, { if !self .0 .with_program(|p| p.override_alive.load(Ordering::Acquire)) { - return; + return Vec::new(); } let geometry = self @@ -776,36 +769,27 @@ impl CosmicStack { .to_physical_precise_round(scale); let stack_loc = location + geometry.loc; let window_loc = location + Point::from((0, (TAB_HEIGHT as f64 * scale.y) as i32)); - let frosted = self.0.with_program(|p| { - let theme = p.theme.lock().unwrap(); - if theme.cosmic().frosted_windows { - (theme.cosmic().frosted as u8 + 1) as usize - } else { - 0 - } - }); - let radii = self.0.with_program(|p| { + + let mut elements = AsRenderElements::::render_elements::>( + &self.0, renderer, stack_loc, scale, alpha, + ); + + elements.extend(self.0.with_program(|p| { let windows = p.windows.lock().unwrap(); let active = p.active.load(Ordering::SeqCst); - let appearance = p.appearance_conf.lock().unwrap(); let theme = p.theme.lock().unwrap(); + let appearance = p.appearance_conf.lock().unwrap(); let tiled = p.tiled.load(Ordering::Acquire); let maximized = windows[active].is_maximized(false); + let round = (appearance.clip_tiled_windows || !tiled) && !maximized; - round.then(|| { + let radii = round.then(|| { theme .cosmic() .radius_s() .map(|x| if x < 4.0 { x } else { x + 4.0 }) .map(|x| x.round() as u8) - }) - }); - - 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(); geo.loc += location.to_f64().to_logical(scale); @@ -817,49 +801,53 @@ impl CosmicStack { let window_key = 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(); - push_above(CosmicStackRenderElement::Border(IndicatorShader::element( + CosmicStackRenderElement::Border(IndicatorShader::element( renderer, Key::Window(Usage::Border, window_key.clone()), geo.to_i32_round().as_local(), 1, - radii.unwrap_or([0; 4]), + radii.unwrap_or([0, 0, 0, 0]), a * alpha, scale.x, [r, g, b], - ))); - }; + )) + }); - let radii = radii.map(|[a, _, c, _]| [a, 0, c, 0]); - windows[active].push_render_elements( - renderer, - window_loc, - scale, - alpha, - scanout_override, - scanout_node, - radii.is_some(), - radii.unwrap_or([0; 4]), - frosted, - &mut |elem| push_above(elem.into()), - Some(&mut |elem| push_below(elem.into())), - ); - }); + border.into_iter().chain( + windows[active] + .render_elements::>( + renderer, + window_loc, + scale, + alpha, + scanout_override, + ) + .into_iter() + .map(move |elem| { + let radii = radii.map(|[a, _, c, _]| [a, 0, c, 0]); + if radii.is_some_and(|radii| { + ClippedSurfaceRenderElement::will_clip(&elem, scale, geo, radii) + }) { + CosmicStackRenderElement::Clipped(ClippedSurfaceRenderElement::new( + renderer, + elem, + scale, + geo, + radii.unwrap(), + )) + } else { + CosmicStackRenderElement::Window(elem) + } + }), + ) + })); - self.0.push_render_elements( - renderer, - stack_loc, - scale, - alpha, - radii.map(|[_, b, _, d]| [0, b, 0, d]).unwrap_or([0; 4]), - &mut |elem| push_above(elem.into()), - Some(&mut |elem| push_below(elem.into())), - ); + elements.into_iter().map(C::from).collect() } - pub(crate) fn set_theme(&self, mut theme: cosmic::Theme) { - theme.transparent = theme.cosmic().frosted_windows; + pub(crate) fn set_theme(&self, theme: cosmic::Theme) { self.0.with_program(|p| { *p.theme.lock().unwrap() = theme.clone(); }); @@ -1368,14 +1356,11 @@ impl Decorations for DefaultDecorations { .class(theme::Container::custom(move |theme| { let cosmic_theme = theme.cosmic(); - let mut background = if group_focused { + let background = if group_focused { cosmic_theme.accent_color() } else { cosmic_theme.primary_container_color() }; - if cosmic_theme.frosted_windows { - background.alpha = cosmic_theme.alpha_map.blurred_alpha(cosmic_theme.frosted); - } iced_widget::container::Style { snap: true, @@ -1892,32 +1877,40 @@ impl TouchTarget for CosmicStack { } pub enum CosmicStackRenderElement { - Header(IcedRenderElement), + Header(MemoryRenderBufferRenderElement), Shadow(PixelShaderElement), Border(PixelShaderElement), - Window(SurfaceRenderElement), + Window(WaylandSurfaceRenderElement), + Clipped(ClippedSurfaceRenderElement), } -impl From> +impl From> for CosmicStackRenderElement { - fn from(value: IcedRenderElement) -> Self { + fn from(value: MemoryRenderBufferRenderElement) -> Self { Self::Header(value) } } -impl From> +impl From> for CosmicStackRenderElement { - fn from(value: SurfaceRenderElement) -> Self { + fn from(value: WaylandSurfaceRenderElement) -> Self { Self::Window(value) } } +impl From> + for CosmicStackRenderElement +{ + fn from(value: ClippedSurfaceRenderElement) -> Self { + Self::Clipped(value) + } +} + impl Element for CosmicStackRenderElement where - R: Renderer + ImportAll + ImportMem + AsGlowRenderer, - R::TextureId: Send + 'static, + R: Renderer + ImportAll + ImportMem, { fn id(&self) -> &RendererId { match self { @@ -1925,6 +1918,7 @@ where CosmicStackRenderElement::Shadow(elem) => elem.id(), CosmicStackRenderElement::Border(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::Border(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::Border(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::Border(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::Border(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::Border(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::Border(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::Border(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::Border(elem) => elem.alpha(), CosmicStackRenderElement::Window(elem) => elem.alpha(), + CosmicStackRenderElement::Clipped(elem) => elem.alpha(), } } @@ -2010,6 +2012,7 @@ where CosmicStackRenderElement::Shadow(elem) => elem.kind(), CosmicStackRenderElement::Border(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::Border(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 RenderElement for CosmicStackRenderElement where R: AsGlowRenderer, - R::TextureId: Send + 'static, + R::TextureId: 'static, + R::Error: FromGlesError, { fn draw( &self, - frame: &mut R::Frame<'_, '_>, + frame: &mut ::Frame<'_, '_>, src: Rectangle, dst: Rectangle, damage: &[Rectangle], @@ -2051,11 +2056,14 @@ where opaque_regions, cache, ) - .map_err(R::from_gles_error) + .map_err(FromGlesError::from_gles_error) } CosmicStackRenderElement::Window(elem) => { 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()) } CosmicStackRenderElement::Window(elem) => elem.underlying_storage(renderer), + CosmicStackRenderElement::Clipped(elem) => elem.underlying_storage(renderer), } } @@ -2088,11 +2097,14 @@ where dst, cache, ) - .map_err(R::from_gles_error) + .map_err(FromGlesError::from_gles_error) } CosmicStackRenderElement::Window(elem) => { elem.capture_framebuffer(frame, src, dst, cache) } + CosmicStackRenderElement::Clipped(elem) => { + elem.capture_framebuffer(frame, src, dst, cache) + } } } } diff --git a/src/shell/element/stack/tab.rs b/src/shell/element/stack/tab.rs index a92c13c5..ea48a585 100644 --- a/src/shell/element/stack/tab.rs +++ b/src/shell/element/stack/tab.rs @@ -3,15 +3,16 @@ use cosmic::{ font::Font, iced::{ Background, - core::{ - Border, Clipboard, Color, Length, Rectangle, Shell, Size, alignment, event, - layout::{Layout, Limits, Node}, - mouse, overlay, renderer, - text::{Ellipsize, EllipsizeHeightLimit, Shaping, Wrapping}, - widget::{Id, Widget, operation::Operation, tree::Tree}, - }, - widget::{self, container::draw_background, rule::FillMode, scrollable::AbsoluteOffset}, + widget::{self, container::draw_background, rule::FillMode}, }, + iced_core::{ + Border, Clipboard, Color, Length, Rectangle, Shell, Size, alignment, event, + layout::{Layout, Limits, Node}, + mouse, overlay, renderer, + text::{Ellipsize, EllipsizeHeightLimit, Shaping, Wrapping}, + widget::{Id, Widget, operation::Operation, tree::Tree}, + }, + iced_widget::scrollable::AbsoluteOffset, theme, widget::{Icon, icon::from_name, text}, }; @@ -288,8 +289,8 @@ where .min_height(size.height) .width(size.width) .height(size.height); - cosmic::iced::core::layout::flex::resolve( - cosmic::iced::core::layout::flex::Axis::Horizontal, + cosmic::iced_core::layout::flex::resolve( + cosmic::iced_core::layout::flex::Axis::Horizontal, renderer, &limits, Length::Fill, diff --git a/src/shell/element/stack/tabs.rs b/src/shell/element/stack/tabs.rs index 86218db6..f411fe26 100644 --- a/src/shell/element/stack/tabs.rs +++ b/src/shell/element/stack/tabs.rs @@ -1,30 +1,24 @@ use super::tab::{MIN_ACTIVE_TAB_WIDTH, Tab, TabBackgroundTheme, TabMessage, TabRuleTheme}; use cosmic::{ Apply, - iced::{ - Element, - core::{ - Background, Border, Clipboard, Color, Length, Point, Rectangle, Renderer, Shell, Size, - Vector, event, - layout::{Layout, Limits, Node}, - mouse, overlay, renderer, - widget::{ - Widget, - operation::{ - Operation, Scrollable, - scrollable::{AbsoluteOffset, RelativeOffset}, - }, - tree::{self, Tree}, - }, - }, - id::Id, + iced::{Element, id::Id, widget}, + iced_core::{ + Background, Border, Clipboard, Color, Length, Point, Rectangle, Renderer, Shell, Size, + Vector, event, + layout::{Layout, Limits, Node}, + mouse, overlay, renderer, widget::{ - self, - container::{Catalog, draw_background}, + Widget, + operation::{ + Operation, Scrollable, + scrollable::{AbsoluteOffset, RelativeOffset}, + }, + tree::{self, Tree}, }, }, + iced_widget::container::draw_background, theme, - widget::icon::from_name, + widget::{container::Catalog, icon::from_name}, }; use keyframe::{ ease, @@ -385,8 +379,8 @@ where >= MIN_ACTIVE_TAB_WIDTH { // just use a flex layout - cosmic::iced::core::layout::flex::resolve( - cosmic::iced::core::layout::flex::Axis::Horizontal, + cosmic::iced_core::layout::flex::resolve( + cosmic::iced_core::layout::flex::Axis::Horizontal, renderer, &limits, self.width, @@ -519,11 +513,13 @@ where let background_style = Catalog::style( theme, - &theme::Container::custom(|_theme| widget::container::Style { + &theme::Container::custom(|theme| widget::container::Style { snap: true, icon_color: None, text_color: None, - background: Some(Background::Color(Color::TRANSPARENT)), + background: Some(Background::Color( + theme.cosmic().primary_container_color().into(), + )), border: Border { radius: 0.0.into(), width: 0.0, diff --git a/src/shell/element/stack_hover.rs b/src/shell/element/stack_hover.rs index f1238404..9a86709c 100644 --- a/src/shell/element/stack_hover.rs +++ b/src/shell/element/stack_hover.rs @@ -1,7 +1,6 @@ use crate::{ - backend::render::element::AsGlowRenderer, fl, - utils::iced::{IcedElement, IcedRenderElement, Program}, + utils::iced::{IcedElement, Program}, }; use calloop::LoopHandle; @@ -9,78 +8,22 @@ use cosmic::{ Apply, iced::{ Alignment, - core::{Background, Border, Color, Length}, widget::{container, row}, }, + iced_core::{Background, Border, Color, Length}, theme, widget::{icon::from_name, space, text}, }; -use smithay::{ - backend::renderer::ImportMem, - desktop::space::SpaceElement, - output::Output, - utils::{Logical, Physical, Point, Rectangle, Scale, Size}, -}; +use smithay::utils::{Logical, Size}; -#[derive(Debug)] -pub struct StackHover { - location: Point, - elem: IcedElement, -} +pub type StackHover = IcedElement; -impl StackHover { - pub fn new( - evlh: LoopHandle<'static, crate::state::State>, - size: Size, - mut theme: cosmic::Theme, - ) -> StackHover { - theme.transparent = theme.cosmic().frosted_system_interface; - - let elem = IcedElement::new(StackHoverInternal, size, evlh, theme); - let minimum = elem.minimum_size(); - let new_size = Size::::new(size.w.min(minimum.w), size.h.min(minimum.h)); - let location = Point::new( - size.w.saturating_sub(new_size.w) / 2, - size.h.saturating_sub(new_size.h) / 2, - ); - elem.resize(new_size); - - StackHover { location, elem } - } - - pub fn push_render_elements( - &self, - renderer: &mut R, - location: Point, - scale: Scale, - alpha: f32, - push_above: &mut dyn FnMut(IcedRenderElement), - push_below: Option<&mut dyn FnMut(IcedRenderElement)>, - ) where - R: AsGlowRenderer + ImportMem, - R::TextureId: Send + Clone + 'static, - { - self.elem.push_render_elements( - renderer, - location + self.location.to_physical_precise_round(scale), - scale, - alpha, - self.elem - .with_theme(|theme| theme.cosmic().radius_s()) - .map(|x| x.round() as u8), - push_above, - push_below, - ); - } - - pub fn output_enter(&self, output: &Output) { - self.elem - .output_enter(output, Rectangle::default() /*unused*/); - } - - pub fn output_leave(&self, output: &Output) { - self.elem.output_leave(output); - } +pub fn stack_hover( + evlh: LoopHandle<'static, crate::state::State>, + size: Size, + theme: cosmic::Theme, +) -> StackHover { + StackHover::new(StackHoverInternal, size, evlh, theme) } pub struct StackHoverInternal; @@ -104,30 +47,23 @@ impl Program for StackHoverInternal { .align_y(Alignment::Center) .padding(16) .apply(container) - .class(theme::Container::custom(|theme| { - let mut background = theme.cosmic().accent_color(); - if theme.transparent { - background.alpha = theme - .cosmic() - .alpha_map - .blurred_alpha(theme.cosmic().frosted); - } - - container::Style { - snap: true, - icon_color: Some(Color::from(theme.cosmic().accent.on)), - text_color: Some(Color::from(theme.cosmic().accent.on)), - background: Some(Background::Color(background.into())), - border: Border { - radius: theme.cosmic().radius_s().into(), - width: 0.0, - color: Color::TRANSPARENT, - }, - shadow: Default::default(), - } + .class(theme::Container::custom(|theme| container::Style { + snap: true, + icon_color: Some(Color::from(theme.cosmic().accent.on)), + text_color: Some(Color::from(theme.cosmic().accent.on)), + background: Some(Background::Color(theme.cosmic().accent_color().into())), + border: Border { + radius: 18.0.into(), + width: 0.0, + color: Color::TRANSPARENT, + }, + shadow: Default::default(), })) .width(Length::Shrink) .height(Length::Shrink) + .apply(container) + .center_x(Length::Fill) + .center_y(Length::Fill) .into() } } diff --git a/src/shell/element/surface.rs b/src/shell/element/surface.rs index e146efeb..8495fe30 100644 --- a/src/shell/element/surface.rs +++ b/src/shell/element/surface.rs @@ -1,12 +1,7 @@ use crate::{ - backend::render::{ - element::AsGlowRenderer, - wayland::{SurfaceRenderElement, push_render_elements_from_surface_tree}, - }, shell::focus::target::PointerFocusTarget, - wayland::handlers::{ - background_effect::ComputedBlurRegionCachedState, compositor::frame_time_filter_fn, - corner_radius::surface_corners, + wayland::{ + handlers::compositor::frame_time_filter_fn, protocols::corner_radius::CacheableCorners, }, }; use std::{ @@ -19,12 +14,11 @@ use std::{ }; use smithay::{ - backend::{ - drm::DrmNode, - renderer::{ - ImportAll, Renderer, buffer_has_alpha, - element::{Kind, RenderElementStates, surface::KindEvaluation}, - utils::RendererSurfaceStateUserData, + backend::renderer::{ + ImportAll, Renderer, + element::{ + AsRenderElements, Kind, RenderElementStates, + surface::{WaylandSurfaceRenderElement, render_elements_from_surface_tree}, }, }, desktop::{ @@ -51,12 +45,10 @@ use smithay::{ IsAlive, Logical, Physical, Point, Rectangle, Scale, Serial, Size, user_data::UserDataMap, }, wayland::{ - alpha_modifier::AlphaModifierSurfaceCachedState, compositor::{ SubsurfaceCachedState, SurfaceData, TraversalAction, get_parent, with_states, with_surface_tree_downward, }, - dmabuf::get_dmabuf, seat::WaylandFocus, shell::xdg::{ 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 { - let surface_state = data.data_map.get::()?; - let surface_state = surface_state.lock().unwrap(); - surface_state - .buffer() - .and_then(|buffer| get_dmabuf(buffer).ok()) - .and_then(|dmabuf| dmabuf.node()) -} - -fn is_likely_translucent(alpha: f32, data: &SurfaceData) -> bool { - if alpha < 1.0 { - return true; - } - - let mut alpha_modifier_state = data.cached_state.get::(); - let alpha_multiplier = alpha_modifier_state - .current() - .multiplier_f32() - .unwrap_or(1.0); - if alpha_multiplier < 1.0 { - return true; - } - - let Some(surface_state) = data.data_map.get::() else { - return false; - }; - let surface_state = surface_state.lock().unwrap(); - if surface_state - .buffer() - .is_none_or(|buffer| !buffer_has_alpha(buffer).unwrap_or(true)) - { - return false; - } - - let mut blur_state = data.cached_state.get::(); - blur_state - .current() - .blur_region - .as_ref() - .is_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, - scanout_node: Option, - 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)] pub struct CosmicSurface(pub Window); @@ -229,7 +144,22 @@ impl CosmicSurface { pub fn corner_radius(&self, geometry_size: Size) -> Option<[u8; 4]> { 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::(); + + // 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() } - pub fn push_popup_render_elements( + pub fn popup_render_elements( &self, renderer: &mut R, location: Point, scale: Scale, alpha: f32, - scanout_node: Option, - blur_strength: usize, - push: &mut dyn FnMut(SurfaceRenderElement), - ) where - R: Renderer + ImportAll + AsGlowRenderer, + ) -> Vec + where + R: Renderer + ImportAll, R::TextureId: Clone + 'static, + C: From>, { match self.0.underlying_surface() { WindowSurface::Wayland(toplevel) => { let surface = toplevel.wl_surface(); - for (popup, popup_offset) in PopupManager::popups_for_surface(surface) { - let offset = (self.0.geometry().loc + popup_offset - popup.geometry().loc) - .to_physical_precise_round(scale); - let mut geometry = popup.geometry().to_f64(); - geometry.loc += location.to_f64().to_logical(scale) + popup_offset.to_f64(); - let radii = with_states(popup.wl_surface(), |states| { - surface_corners(states, geometry.size.to_i32_round()) - }) - .unwrap_or([0; 4]); + PopupManager::popups_for_surface(surface) + .flat_map(move |(popup, popup_offset)| { + let offset = (self.0.geometry().loc + popup_offset - popup.geometry().loc) + .to_physical_precise_round(scale); - push_render_elements_from_surface_tree( - renderer, - popup.wl_surface(), - location + offset, - geometry, - scale, - alpha, - false, - radii, - None, - blur_strength, - scanout_kind_eval(None, scanout_node, alpha), - push, - None, - ) - } + render_elements_from_surface_tree( + renderer, + popup.wl_surface(), + location + offset, + scale, + alpha, + FRAME_TIME_FILTER, + ) + }) + .collect() } - WindowSurface::X11(_) => {} + WindowSurface::X11(_) => Vec::new(), } } - pub fn push_render_elements( + pub fn render_elements( &self, renderer: &mut R, location: Point, scale: Scale, alpha: f32, scanout_override: Option, - scanout_node: Option, - should_clip: bool, - radii: [u8; 4], - blur_strength: usize, - push_above: &mut dyn FnMut(SurfaceRenderElement), - push_below: Option<&mut dyn FnMut(SurfaceRenderElement)>, - ) where - R: Renderer + ImportAll + AsGlowRenderer, + ) -> Vec + where + R: Renderer + ImportAll, R::TextureId: Clone + 'static, + C: From>, { - let mut geometry = self.0.geometry().to_f64(); - geometry.loc += location.to_f64().to_logical(scale); - match self.0.underlying_surface() { WindowSurface::Wayland(toplevel) => { let surface = toplevel.wl_surface(); - push_render_elements_from_surface_tree( + render_elements_from_surface_tree( renderer, surface, location, - geometry, scale, alpha, - should_clip, - radii, - None, - blur_strength, - scanout_kind_eval(scanout_override, scanout_node, alpha), - push_above, - push_below, + scanout_override + .map(|val| { + if val { + Kind::ScanoutCandidate + } else { + Kind::Unspecified + } + .into() + }) + .unwrap_or(FRAME_TIME_FILTER), ) } WindowSurface::X11(surface) => { let Some(surface) = surface.wl_surface() else { - return; + return Vec::new(); }; - push_render_elements_from_surface_tree( + render_elements_from_surface_tree( renderer, &surface, location, - geometry, scale, alpha, - should_clip, - radii, - None, - blur_strength, - scanout_kind_eval(scanout_override, scanout_node, alpha), - push_above, - push_below, + scanout_override + .map(|val| { + if val { + Kind::ScanoutCandidate + } else { + Kind::Unspecified + } + .into() + }) + .unwrap_or(FRAME_TIME_FILTER), ) } } @@ -1129,6 +1044,24 @@ impl X11Relatable for CosmicSurface { } } +impl AsRenderElements for CosmicSurface +where + R: Renderer + ImportAll, + R::TextureId: Clone + 'static, +{ + type RenderElement = WaylandSurfaceRenderElement; + + fn render_elements>( + &self, + renderer: &mut R, + location: Point, + scale: Scale, + alpha: f32, + ) -> Vec { + self.0.render_elements(renderer, location, scale, alpha) + } +} + fn with_toplevel_state) -> T>( toplevel: &ToplevelSurface, pending: bool, diff --git a/src/shell/element/swap_indicator.rs b/src/shell/element/swap_indicator.rs index 6f9498da..0db66cda 100644 --- a/src/shell/element/swap_indicator.rs +++ b/src/shell/element/swap_indicator.rs @@ -1,88 +1,25 @@ use crate::{ - backend::render::element::AsGlowRenderer, fl, - utils::iced::{IcedElement, IcedRenderElement, Program}, + utils::iced::{IcedElement, Program}, }; use calloop::LoopHandle; use cosmic::{ Apply, - iced::{ - core::{Alignment, Background, Border, Color, Length}, - widget::{container, row, space}, - }, + iced::widget::{container, row, space}, + iced_core::{Alignment, Background, Border, Color, Length}, theme, widget::{icon::from_name, text}, }; -use smithay::{ - backend::renderer::ImportMem, - desktop::space::SpaceElement, - output::Output, - utils::{Logical, Physical, Point, Rectangle, Scale, Size}, -}; +use smithay::utils::Size; -#[derive(Debug, Clone)] -pub struct SwapIndicator { - location: Point, - elem: IcedElement, -} +pub type SwapIndicator = IcedElement; -impl SwapIndicator { - pub fn new( - evlh: LoopHandle<'static, crate::state::State>, - mut theme: cosmic::Theme, - ) -> SwapIndicator { - theme.transparent = theme.cosmic().frosted_system_interface; - SwapIndicator { - location: Point::default(), - elem: IcedElement::new(SwapIndicatorInternal, Size::from((1, 1)), evlh, theme), - } - } - - pub fn resize(&mut self, size: Size) { - let minimum = self.elem.minimum_size(); - let new_size = Size::::new(size.w.min(minimum.w), size.h.min(minimum.h)); - let location = Point::new( - size.w.saturating_sub(new_size.w) / 2, - size.h.saturating_sub(new_size.h) / 2, - ); - self.elem.resize(new_size); - self.location = location; - } - - pub fn push_render_elements( - &self, - renderer: &mut R, - location: Point, - scale: Scale, - alpha: f32, - push_above: &mut dyn FnMut(IcedRenderElement), - push_below: Option<&mut dyn FnMut(IcedRenderElement)>, - ) where - R: AsGlowRenderer + ImportMem, - R::TextureId: Send + Clone + 'static, - { - self.elem.push_render_elements( - renderer, - location + self.location.to_physical_precise_round(scale), - scale, - alpha, - self.elem - .with_theme(|theme| theme.cosmic().radius_s()) - .map(|x| x.round() as u8), - push_above, - push_below, - ); - } - - pub fn output_enter(&self, output: &Output) { - self.elem - .output_enter(output, Rectangle::default() /*unused*/); - } - - pub fn output_leave(&self, output: &Output) { - self.elem.output_leave(output); - } +pub fn swap_indicator( + evlh: LoopHandle<'static, crate::state::State>, + theme: cosmic::Theme, +) -> SwapIndicator { + SwapIndicator::new(SwapIndicatorInternal, Size::from((1, 1)), evlh, theme) } pub struct SwapIndicatorInternal; @@ -106,30 +43,23 @@ impl Program for SwapIndicatorInternal { .align_y(Alignment::Center) .padding(16) .apply(container) - .class(theme::Container::custom(|theme| { - let mut background = theme.cosmic().accent_color(); - if theme.transparent { - background.alpha = theme - .cosmic() - .alpha_map - .blurred_alpha(theme.cosmic().frosted); - } - - container::Style { - snap: true, - icon_color: Some(Color::from(theme.cosmic().accent.on)), - text_color: Some(Color::from(theme.cosmic().accent.on)), - background: Some(Background::Color(background.into())), - border: Border { - radius: theme.cosmic().radius_s().into(), - width: 0.0, - color: Color::TRANSPARENT, - }, - shadow: Default::default(), - } + .class(theme::Container::custom(|theme| container::Style { + snap: true, + icon_color: Some(Color::from(theme.cosmic().accent.on)), + text_color: Some(Color::from(theme.cosmic().accent.on)), + background: Some(Background::Color(theme.cosmic().accent_color().into())), + border: Border { + radius: 18.0.into(), + width: 0.0, + color: Color::TRANSPARENT, + }, + shadow: Default::default(), })) .width(Length::Shrink) .height(Length::Shrink) + .apply(container) + .center_x(Length::Fill) + .center_y(Length::Fill) .into() } } diff --git a/src/shell/element/window.rs b/src/shell/element/window.rs index 838efd24..ea20bb6a 100644 --- a/src/shell/element/window.rs +++ b/src/shell/element/window.rs @@ -1,7 +1,10 @@ use crate::{ backend::render::{ - IndicatorShader, Key, Usage, cursor::CursorState, element::AsGlowRenderer, - shadow::ShadowShader, wayland::SurfaceRenderElement, + IndicatorShader, Key, Usage, + clipped_surface::ClippedSurfaceRenderElement, + cursor::CursorState, + element::{AsGlowRenderer, FromGlesError}, + shadow::ShadowShader, }, hooks::{Decorations, HOOKS}, shell::{ @@ -11,7 +14,7 @@ use crate::{ }, state::State, utils::{ - iced::{IcedElement, IcedRenderElement, Program}, + iced::{IcedElement, Program}, prelude::*, }, }; @@ -20,11 +23,14 @@ use cosmic::iced::{Color, Task}; use cosmic_comp_config::AppearanceConfig; use smithay::{ backend::{ - drm::DrmNode, input::KeyState, 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, glow::GlowRenderer, utils::{CommitCounter, DamageSet, OpaqueRegions}, @@ -113,7 +119,7 @@ impl Focus { location: Point, ) -> Option { let geo = surface.geometry(); - let loc = location.to_i32_floor::() - geo.loc; + let loc = location.to_i32_round::() - geo.loc; if loc.y < 0 && loc.x < 0 { Some(Focus::ResizeTopLeft) } else if loc.y < 0 && loc.x >= geo.size.w { @@ -190,13 +196,12 @@ impl CosmicWindow { pub fn new( window: impl Into, handle: LoopHandle<'static, crate::state::State>, - mut theme: cosmic::Theme, + theme: cosmic::Theme, appearance: AppearanceConfig, ) -> CosmicWindow { let window = window.into(); let width = window.geometry().size.w; let last_title = window.title(); - theme.transparent = theme.cosmic().frosted_windows; if appearance.clip_floating_windows { window.set_tiled(true); @@ -292,7 +297,7 @@ impl CosmicWindow { { let geo = p.window.geometry(); - let point_i32 = relative_pos.to_i32_floor::(); + let point_i32 = relative_pos.to_i32_round::(); 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) @@ -346,17 +351,17 @@ impl CosmicWindow { self.0.loop_handle() } - pub fn push_popup_render_elements( + pub fn popup_render_elements( &self, renderer: &mut R, location: Point, scale: Scale, alpha: f32, - scanout_node: Option, - push: &mut dyn FnMut(CosmicWindowRenderElement), - ) where - R: Renderer + AsGlowRenderer + ImportAll + ImportMem, + ) -> Vec + where + R: Renderer + ImportAll + ImportMem, R::TextureId: Send + Clone + 'static, + C: From>, { let has_ssd = self.0.with_program(|p| p.has_ssd(false)); @@ -367,22 +372,13 @@ impl CosmicWindow { }; self.0.with_program(|p| { - let theme = p.theme.lock().unwrap(); - let frosted = if theme.cosmic().frosted_windows { - (theme.cosmic().frosted as u8 + 1) as usize - } else { - 0 - }; - - p.window.push_popup_render_elements( - renderer, - window_loc, - scale, - alpha, - scanout_node, - frosted, - &mut |elem| push(elem.into()), - ) + p.window + .popup_render_elements::>( + renderer, window_loc, scale, alpha, + ) + .into_iter() + .map(C::from) + .collect() }) } @@ -466,7 +462,7 @@ impl CosmicWindow { }) } - pub fn push_render_elements( + pub fn render_elements( &self, renderer: &mut R, location: Point, @@ -474,12 +470,11 @@ impl CosmicWindow { scale: Scale, alpha: f32, scanout_override: Option, - scanout_node: Option, - push_above: &mut dyn FnMut(CosmicWindowRenderElement), - push_below: &mut dyn FnMut(CosmicWindowRenderElement), - ) where + ) -> Vec + where R: AsGlowRenderer, R::TextureId: Send + Clone + 'static, + C: From>, { let (has_ssd, is_tiled, is_maximized, mut radii, appearance) = self.0.with_program(|p| { ( @@ -516,6 +511,8 @@ impl CosmicWindow { location }; + let mut elements = Vec::new(); + let (mut geo, bg_divider) = self.0.with_program(|p| { ( SpaceElement::geometry(&p.window).to_f64(), @@ -545,57 +542,55 @@ impl CosmicWindow { scale.x, [r, g, b], )); - push_above(elem); + elements.push(elem); } - self.0.with_program(|p| { - let mut radii = radii; + let window_elements = self.0.with_program(|p| { + p.window + .render_elements::>( + renderer, + window_loc, + scale, + alpha, + scanout_override, + ) + }); + if window_elements.is_empty() { + return Vec::new(); + } + + elements.extend(window_elements.into_iter().map(|elem| { if has_ssd { 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 + 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 { - 0 - }; - p.window.push_render_elements( - renderer, - window_loc, - scale, - alpha, - scanout_override, - scanout_node, - clip, - radii, - frosted, - &mut |elem| push_above(elem.into()), - Some(&mut |elem| push_below(elem.into())), - ); - }); + CosmicWindowRenderElement::Window(elem) + } + })); if has_ssd { - radii[0] = 0; - radii[2] = 0; let ssd_loc = location + self .0 - .with_program(|p| p.window.geometry().loc.to_physical_precise_round(scale)); - self.0.push_render_elements( - renderer, - ssd_loc, - scale, - alpha, - radii, - &mut |elem| push_above(elem.into()), - Some(&mut |elem| push_below(elem.into())), - ); + .with_program(|p| p.window.geometry().loc) + .to_physical_precise_round(scale); + elements.extend(AsRenderElements::::render_elements::< + CosmicWindowRenderElement, + >(&self.0, renderer, ssd_loc, scale, alpha)) } + + elements.into_iter().map(C::from).collect() } - pub(crate) fn set_theme(&self, mut theme: cosmic::Theme) { - theme.transparent = theme.cosmic().frosted_windows; + pub(crate) fn set_theme(&self, theme: cosmic::Theme) { self.0.with_program(|p| { *p.theme.lock().unwrap() = theme.clone(); }); @@ -1248,33 +1243,41 @@ impl WaylandFocus for CosmicWindow { } } -pub enum CosmicWindowRenderElement { - Header(IcedRenderElement), +pub enum CosmicWindowRenderElement { + Header(MemoryRenderBufferRenderElement), Shadow(PixelShaderElement), Border(PixelShaderElement), - Window(SurfaceRenderElement), + Window(WaylandSurfaceRenderElement), + Clipped(ClippedSurfaceRenderElement), } -impl From> +impl From> for CosmicWindowRenderElement { - fn from(value: IcedRenderElement) -> Self { + fn from(value: MemoryRenderBufferRenderElement) -> Self { Self::Header(value) } } -impl From> +impl From> for CosmicWindowRenderElement { - fn from(value: SurfaceRenderElement) -> Self { + fn from(value: WaylandSurfaceRenderElement) -> Self { Self::Window(value) } } +impl From> + for CosmicWindowRenderElement +{ + fn from(value: ClippedSurfaceRenderElement) -> Self { + Self::Clipped(value) + } +} + impl Element for CosmicWindowRenderElement where - R: Renderer + ImportAll + ImportMem + AsGlowRenderer, - R::TextureId: Send + 'static, + R: Renderer + ImportAll + ImportMem, { fn id(&self) -> &RendererId { match self { @@ -1282,6 +1285,7 @@ where CosmicWindowRenderElement::Shadow(elem) => elem.id(), CosmicWindowRenderElement::Border(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::Border(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::Border(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::Border(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::Border(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::Border(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::Border(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::Border(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::Border(elem) => elem.alpha(), CosmicWindowRenderElement::Window(elem) => elem.alpha(), + CosmicWindowRenderElement::Clipped(elem) => elem.alpha(), } } @@ -1367,6 +1379,7 @@ where CosmicWindowRenderElement::Shadow(elem) => elem.kind(), CosmicWindowRenderElement::Border(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::Border(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 RenderElement for CosmicWindowRenderElement where R: AsGlowRenderer, - R::TextureId: Send + 'static, + R::TextureId: 'static, + R::Error: FromGlesError, { fn draw( &self, @@ -1408,11 +1423,14 @@ where opaque_regions, cache, ) - .map_err(R::from_gles_error) + .map_err(FromGlesError::from_gles_error) } CosmicWindowRenderElement::Window(elem) => { 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()) } CosmicWindowRenderElement::Window(elem) => elem.underlying_storage(renderer), + CosmicWindowRenderElement::Clipped(elem) => elem.underlying_storage(renderer), } } @@ -1445,11 +1464,14 @@ where dst, cache, ) - .map_err(R::from_gles_error) + .map_err(FromGlesError::from_gles_error) } CosmicWindowRenderElement::Window(elem) => { elem.capture_framebuffer(frame, src, dst, cache) } + CosmicWindowRenderElement::Clipped(elem) => { + elem.capture_framebuffer(frame, src, dst, cache) + } } } } diff --git a/src/shell/focus/mod.rs b/src/shell/focus/mod.rs index 4e88c499..9374230a 100644 --- a/src/shell/focus/mod.rs +++ b/src/shell/focus/mod.rs @@ -378,14 +378,6 @@ fn update_focus_state( .cloned() .unwrap_or(seat.active_output()); - crate::input::update_output_image_copy_cursor_position( - &shell, - &state.common.clock, - &output, - seat, - new_pos, - ); - let focus = State::surface_under(new_pos, &output, &shell) .map(|(focus, loc)| (focus, loc.as_logical())); //drop here to avoid multiple borrows diff --git a/src/shell/focus/order.rs b/src/shell/focus/order.rs index fb904c01..d02b310e 100644 --- a/src/shell/focus/order.rs +++ b/src/shell/focus/order.rs @@ -32,12 +32,10 @@ pub enum Stage<'a> { layer: LayerSurface, popup: &'a PopupKind, location: Point, - workspace_idx: usize, }, LayerSurface { layer: LayerSurface, location: Point, - workspace_idx: usize, }, OverrideRedirect { surface: &'a X11Surface, @@ -97,15 +95,10 @@ fn render_input_order_internal( layer, popup: &popup, location, - workspace_idx: current.1, })?; } for (layer, location) in layer_surfaces(output, Layer::Overlay, element_filter) { - callback(Stage::LayerSurface { - layer, - location, - workspace_idx: current.1, - })?; + callback(Stage::LayerSurface { layer, location })?; } // calculate a bunch of stuff for workspace transitions @@ -194,7 +187,7 @@ fn render_input_order_internal( }); ( - Some((previous, previous_idx, has_fullscreen, offset)), + Some((previous, has_fullscreen, offset)), Point::::from(match (layout, forward) { (WorkspaceLayout::Vertical, true) => (0, output_size.h + offset.y), (WorkspaceLayout::Vertical, false) => (0, -(output_size.h - offset.y)), @@ -213,7 +206,6 @@ fn render_input_order_internal( layer, popup: &popup, location, - workspace_idx: current.1, })?; } } @@ -244,7 +236,7 @@ fn render_input_order_internal( if element_filter != ElementFilter::LayerShellOnly { // previous workspace popups - if let Some((previous_handle, _, _, offset)) = previous.as_ref() { + if let Some((previous_handle, _, offset)) = previous.as_ref() { let Some(workspace) = shell.workspaces.space_for_handle(previous_handle) else { return ControlFlow::Break(Err(OutputNoMode)); }; @@ -273,12 +265,11 @@ fn render_input_order_internal( layer, popup: &popup, 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 { // previous bottom layer popups @@ -287,7 +278,6 @@ fn render_input_order_internal( layer, popup: &popup, location: location + offset.as_global(), - workspace_idx: **idx, })?; } } @@ -299,12 +289,11 @@ fn render_input_order_internal( layer, popup: &popup, 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 { // previous background layer popups @@ -313,7 +302,6 @@ fn render_input_order_internal( layer, popup: &popup, location: location + offset.as_global(), - workspace_idx: **idx, })?; } } @@ -321,11 +309,7 @@ fn render_input_order_internal( if !has_focused_fullscreen { // top-layer shell for (layer, location) in layer_surfaces(output, Layer::Top, element_filter) { - callback(Stage::LayerSurface { - layer, - location, - workspace_idx: current.1, - })?; + callback(Stage::LayerSurface { layer, location })?; } } @@ -342,7 +326,7 @@ fn render_input_order_internal( })?; // previous workspace windows - if let Some((previous_handle, _, _, offset)) = previous.as_ref() { + if let Some((previous_handle, _, offset)) = previous.as_ref() { let Some(workspace) = shell.workspaces.space_for_handle(previous_handle) else { return ControlFlow::Break(Err(OutputNoMode)); }; @@ -357,25 +341,17 @@ fn render_input_order_internal( // bottom layer for (layer, mut location) in layer_surfaces(output, Layer::Bottom, element_filter) { location += current_offset.as_global(); - callback(Stage::LayerSurface { - layer, - location, - workspace_idx: current.1, - })?; + callback(Stage::LayerSurface { layer, location })?; } } - if let Some((_, idx, has_fullscreen, offset)) = previous.as_ref() + if let Some((_, has_fullscreen, offset)) = previous.as_ref() && !has_fullscreen { // previous bottom layer for (layer, mut location) in layer_surfaces(output, Layer::Bottom, element_filter) { location += offset.as_global(); - callback(Stage::LayerSurface { - layer, - location, - workspace_idx: **idx, - })?; + callback(Stage::LayerSurface { layer, location })?; } } @@ -383,25 +359,17 @@ fn render_input_order_internal( // background layer for (layer, mut location) in layer_surfaces(output, Layer::Background, element_filter) { location += current_offset.as_global(); - callback(Stage::LayerSurface { - layer, - location, - workspace_idx: current.1, - })?; + callback(Stage::LayerSurface { layer, location })?; } } - if let Some((_, idx, has_fullscreen, offset)) = previous.as_ref() + if let Some((_, has_fullscreen, offset)) = previous.as_ref() && !has_fullscreen { // previous background layer for (layer, mut location) in layer_surfaces(output, Layer::Background, element_filter) { location += offset.as_global(); - callback(Stage::LayerSurface { - layer, - location, - workspace_idx: **idx, - })?; + callback(Stage::LayerSurface { layer, location })?; } } diff --git a/src/shell/focus/target.rs b/src/shell/focus/target.rs index 929cd92e..fff77282 100644 --- a/src/shell/focus/target.rs +++ b/src/shell/focus/target.rs @@ -6,7 +6,7 @@ use std::{ use crate::{ shell::{ - CosmicSurface, CursorGeometry, SeatExt, + CosmicSurface, SeatExt, element::{CosmicMapped, CosmicStack, CosmicWindow}, layout::tiling::ResizeForkTarget, zoom::ZoomFocusTarget, @@ -272,7 +272,7 @@ impl PointerFocusTarget { 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()) { hotspot diff --git a/src/shell/grabs/menu/item.rs b/src/shell/grabs/menu/item.rs index f254df72..baa61fde 100644 --- a/src/shell/grabs/menu/item.rs +++ b/src/shell/grabs/menu/item.rs @@ -1,12 +1,10 @@ use cosmic::{ - iced::{ - Alignment, Element, - core::{ - Background, Border, Clipboard, Color, Event, Layout, Length, Rectangle, - Renderer as IcedRenderer, Shell, Size, layout, mouse, overlay, - renderer::{Quad, Style}, - widget::{Id, Tree, Widget, tree}, - }, + iced::Element, + iced_core::{ + Background, Border, Clipboard, Color, Event, Layout, Length, Rectangle, + Renderer as IcedRenderer, Shell, Size, layout, mouse, overlay, + renderer::{Quad, Style}, + widget::{Id, Tree, Widget, tree}, }, widget::button::Catalog, }; @@ -75,26 +73,11 @@ where viewport: &Rectangle, ) { let widget_state = state.state.downcast_ref::(); - let mut styling = if widget_state.cursor_over { + let styling = if widget_state.cursor_over { theme.hovered(true, false, &self.styling) } else { theme.active(true, false, &self.styling) }; - if matches!(self.styling, cosmic::theme::Button::MenuItem) { - match theme.list_item_position { - Some((Alignment::Start, _)) => { - styling.border_radius = - styling.border_radius.bottom(theme.cosmic().radius_0()[3]); - } - Some((Alignment::End, _)) => { - styling.border_radius = styling.border_radius.top(theme.cosmic().radius_0()[0]); - } - Some((Alignment::Center, _)) => {} - None => { - styling.border_radius = theme.cosmic().radius_0().into(); - } - }; - } renderer.fill_quad( Quad { @@ -102,7 +85,7 @@ where bounds: layout.bounds(), border: Border { radius: styling.border_radius, - width: 0., + width: styling.border_width, color: styling.border_color, }, shadow: Default::default(), diff --git a/src/shell/grabs/menu/mod.rs b/src/shell/grabs/menu/mod.rs index 1eb571f1..671e004a 100644 --- a/src/shell/grabs/menu/mod.rs +++ b/src/shell/grabs/menu/mod.rs @@ -9,18 +9,19 @@ use std::{ use calloop::LoopHandle; use cosmic::{ Apply as _, Task, - iced::{ - Alignment, Background, - core::{Border, Length, Rectangle as IcedRectangle, alignment::Horizontal}, - widget::{self as iced_widget, Row, text::Style as TextStyle}, - }, + iced::{Alignment, Background}, + iced_core::{Border, Length, Rectangle as IcedRectangle, alignment::Horizontal}, + iced_widget::{self, Column, Row, text::Style as TextStyle}, theme, - widget::{button, divider, icon::from_name, menu::menu_column::MenuColumn, space, text}, + widget::{button, divider, icon::from_name, space, text}, }; use smithay::{ backend::{ input::{ButtonState, TouchSlot}, - renderer::ImportMem, + renderer::{ + ImportMem, Renderer, + element::{AsRenderElements, memory::MemoryRenderBufferRenderElement}, + }, }, desktop::space::SpaceElement, input::{ @@ -42,11 +43,10 @@ use smithay::{ }; use crate::{ - backend::render::element::AsGlowRenderer, shell::{SeatExt, focus::target::PointerFocusTarget}, state::State, utils::{ - iced::{IcedElement, IcedRenderElement, Program}, + iced::{IcedElement, Program}, prelude::*, }, }; @@ -65,32 +65,29 @@ pub struct MenuGrabState { pub type SeatMenuGrabState = Mutex>; impl MenuGrabState { - pub fn render( - &self, - renderer: &mut R, - output: &Output, - push: &mut dyn FnMut(IcedRenderElement), - ) where - R: AsGlowRenderer + ImportMem, + pub fn render(&self, renderer: &mut R, output: &Output) -> Vec + where + R: Renderer + ImportMem, R::TextureId: Send + Clone + 'static, + I: From>, { let scale = output.current_scale().fractional_scale(); - for elem in self.elements.lock().unwrap().iter() { - elem.iced.push_render_elements( - renderer, - elem.position - .to_local(output) - .as_logical() - .to_physical_precise_round(scale), - scale.into(), - 1.0, - elem.iced - .with_theme(|theme| theme.cosmic().radius_s()) - .map(|x| x.round() as u8), - push, - None, - ) - } + self.elements + .lock() + .unwrap() + .iter() + .flat_map(|elem| { + elem.iced.render_elements( + renderer, + elem.position + .to_local(output) + .as_logical() + .to_physical_precise_round(scale), + scale.into(), + 1.0, + ) + }) + .collect() } 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 position = elements.last().unwrap().position; - let mut theme = state.common.theme.clone(); - theme.transparent = theme.cosmic().frosted_system_interface; let element = IcedElement::new( ContextMenu::new(items), Size::default(), state.common.event_loop_handle.clone(), - theme, + state.common.theme.clone(), ); let min_size = element.minimum_size(); @@ -300,7 +295,7 @@ impl Program for ContextMenu { Rectangle::new( position + Point::from(( - bounds.width.floor() as i32, + bounds.width.ceil() as i32, bounds.y.ceil() as i32, )), min_size.as_global(), @@ -309,7 +304,7 @@ impl Program for ContextMenu { Rectangle::new( position + Point::from(( - bounds.width.floor() as i32, + bounds.width.ceil() as i32, bounds.y.ceil() as i32 + bounds.height.ceil() as i32 - min_size.h, )), @@ -317,15 +312,14 @@ impl Program for ContextMenu { ), // to the left -> down Rectangle::new( - position - + Point::from((-min_size.w + 1, bounds.y.ceil() as i32)), + position + Point::from((-min_size.w, bounds.y.ceil() as i32)), min_size.as_global(), ), // to the left -> up Rectangle::new( position + Point::from(( - -min_size.w + 1, + -min_size.w, bounds.y.ceil() as i32 + bounds.height.ceil() as i32 - min_size.h, )), @@ -391,11 +385,9 @@ impl Program for ContextMenu { _ => Length::Fill, }; - MenuColumn::with_children(self.items.iter().enumerate().map(|(idx, item)| { + Column::with_children(self.items.iter().enumerate().map(|(idx, item)| { match item { - Item::Separator => divider::horizontal::light() - .class(theme::Rule::Default) - .into(), + Item::Separator => divider::horizontal::light().into(), Item::Submenu { title, .. } => Row::with_children(vec![ space::horizontal().width(16).into(), text::body(title).width(mode).into(), @@ -485,7 +477,6 @@ impl Program for ContextMenu { .class(theme::Container::custom(|theme| { let cosmic = theme.cosmic(); let component = &cosmic.background(false).component; - let component = &cosmic.background(false).component; iced_widget::container::Style { snap: true, icon_color: Some(cosmic.accent.base.into()), @@ -549,7 +540,7 @@ impl PointerGrab for MenuGrab { let mut bbox = elem.iced.bbox(); 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]; @@ -565,11 +556,8 @@ impl PointerGrab for MenuGrab { PointerTarget::motion(&element.iced, &self.seat, state, &new_event); } } else { - elements - .iter_mut() - .filter(|element| element.pointer_entered) - .skip(1) - .for_each(|element| { + elements.iter_mut().for_each(|element| { + if element.pointer_entered { PointerTarget::leave( &element.iced, &self.seat, @@ -578,7 +566,8 @@ impl PointerGrab for MenuGrab { event.time, ); element.pointer_entered = false; - }) + } + }) } } handle.motion(state, None, event); @@ -754,7 +743,7 @@ impl TouchGrab for MenuGrab { let mut bbox = elem.iced.bbox(); 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]; diff --git a/src/shell/grabs/moving.rs b/src/shell/grabs/moving.rs index d8714830..e73b108d 100644 --- a/src/shell/grabs/moving.rs +++ b/src/shell/grabs/moving.rs @@ -6,7 +6,10 @@ use crate::{ }, shell::{ CosmicMapped, CosmicSurface, Direction, ManagedLayer, - element::{CosmicMappedRenderElement, stack_hover::StackHover}, + element::{ + CosmicMappedRenderElement, + stack_hover::{StackHover, stack_hover}, + }, focus::target::{KeyboardFocusTarget, PointerFocusTarget}, layout::floating::TiledCorners, }, @@ -16,14 +19,12 @@ use crate::{ use calloop::LoopHandle; use cosmic::theme::CosmicTheme; -use smallvec::SmallVec; use smithay::{ backend::{ - drm::DrmNode, input::ButtonState, renderer::{ - ImportAll, ImportMem, - element::{RenderElement, utils::RescaleRenderElement}, + ImportAll, ImportMem, Renderer, + element::{AsRenderElements, RenderElement, utils::RescaleRenderElement}, }, }, desktop::{WindowSurfaceType, layer_map_for_output, space::SpaceElement}, @@ -67,17 +68,12 @@ pub struct MoveGrabState { impl MoveGrabState { #[profiling::function] - pub fn render( - &self, - renderer: &mut R, - output: &Output, - theme: &CosmicTheme, - scanout_node: Option, - push: &mut dyn FnMut(CosmicMappedRenderElement), - ) where - R: AsGlowRenderer + ImportAll + ImportMem, + pub fn render(&self, renderer: &mut R, output: &Output, theme: &CosmicTheme) -> Vec + where + R: Renderer + ImportAll + ImportMem + AsGlowRenderer, R::TextureId: Send + Clone + 'static, CosmicMappedRenderElement: RenderElement, + I: From>, { let scale = if self.previous == ManagedLayer::Tiling { 0.6 + ((1.0 @@ -102,7 +98,7 @@ impl MoveGrabState { .intersection(window_geo) .is_none() { - return; + return Vec::new(); } let output_scale: Scale = output.current_scale().fractional_scale().into(); @@ -112,33 +108,13 @@ impl MoveGrabState { + self.window_offset - scaling_offset; - for (indicator, location) in self.stacking_indicator.iter() { - indicator.push_render_elements( - renderer, - location.to_physical_precise_round(output_scale), - output_scale, - 1.0, - &mut |elem| push(elem.into()), - None, - ); - } - - self.window.push_popup_render_elements::( - renderer, - (render_location - self.window.geometry().loc).to_physical_precise_round(output_scale), - output_scale, - alpha, - scanout_node, - push, - ); - let active_window_hint = crate::theme::active_window_hint(theme); let radius = self .element() .corner_radius(window_geo.size, self.indicator_thickness); - if self.indicator_thickness > 0 { - push( + let focus_element = if self.indicator_thickness > 0 { + Some(CosmicMappedRenderElement::from( IndicatorShader::focus_element( renderer, Key::Window(Usage::MoveGrabIndicator, self.window.key()), @@ -161,57 +137,12 @@ impl MoveGrabState { active_window_hint.green, 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, - )) - } - CosmicMappedRenderElement::Window(window) => { - CosmicMappedRenderElement::GrabbedWindow(RescaleRenderElement::from_element( - window, - render_location - .to_physical_precise_round(output.current_scale().fractional_scale()), - scale, - )) - } - x => x, + ), + )) + } else { + None }; - let mut lower_elements = SmallVec::<[CosmicMappedRenderElement; 4]>::new_const(); - self.window.push_render_elements( - renderer, - (render_location - self.window.geometry().loc).to_physical_precise_round(output_scale), - None, - output_scale, - alpha, - Some(false), - scanout_node, - &mut |elem| push(map_window_element(elem)), - &mut |elem| lower_elements.push(map_window_element(elem)), - ); - if let Some(shadow_element) = self.window.shadow_render_element( - renderer, - (render_location - self.window.geometry().loc).to_physical_precise_round(output_scale), - None, - output_scale, - scale, - alpha, - ) { - push(shadow_element); - } - for elem in lower_elements.into_iter() { - push(elem); - } - let non_exclusive_geometry = { let layers = layer_map_for_output(output); layers.non_exclusive_zone() @@ -220,46 +151,117 @@ impl MoveGrabState { let gaps = (theme.gaps.0 as i32, theme.gaps.1 as i32); let thickness = self.indicator_thickness.max(1); - if let Some(t) = &self.snapping_zone - && &self.cursor_output == output - { - let base_color = theme.palette.neutral_9; - let overlay_geometry = t.overlay_geometry(non_exclusive_geometry, gaps); + let snapping_indicator = match &self.snapping_zone { + Some(t) if &self.cursor_output == output => { + let base_color = theme.palette.neutral_9; + let overlay_geometry = t.overlay_geometry(non_exclusive_geometry, gaps); + vec![ + CosmicMappedRenderElement::from(IndicatorShader::element( + renderer, + Key::Window(Usage::SnappingIndicator, self.window.key()), + overlay_geometry, + thickness, + [ + theme.radius_s()[0] as u8, + theme.radius_s()[1] as u8, + theme.radius_s()[2] as u8, + theme.radius_s()[3] as u8, + ], + 1.0, + output_scale.x, + [ + active_window_hint.red, + active_window_hint.green, + active_window_hint.blue, + ], + )), + CosmicMappedRenderElement::from(BackdropShader::element( + renderer, + Key::Window(Usage::SnappingIndicator, self.window.key()), + t.overlay_geometry(non_exclusive_geometry, gaps), + theme.radius_s()[0], // TODO: Fix once shaders support 4 corner radii customization + 0.4, + [base_color.red, base_color.green, base_color.blue], + )), + ] + } + _ => vec![], + }; - push( - IndicatorShader::element( - renderer, - Key::Window(Usage::SnappingIndicator, self.window.key()), - overlay_geometry, - thickness, - [ - theme.radius_s()[0] as u8, - theme.radius_s()[1] as u8, - theme.radius_s()[2] as u8, - theme.radius_s()[3] as u8, - ], - 1.0, - output_scale.x, - [ - active_window_hint.red, - active_window_hint.green, - active_window_hint.blue, - ], - ) - .into(), + let w_elements = self + .window + .render_elements::>( + renderer, + (render_location - self.window.geometry().loc) + .to_physical_precise_round(output_scale), + None, + output_scale, + alpha, + Some(false), ); - push( - BackdropShader::element( + let p_elements = self + .window + .popup_render_elements::>( + 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, - Key::Window(Usage::SnappingIndicator, self.window.key()), - t.overlay_geometry(non_exclusive_geometry, gaps), - theme.radius_s()[0], // TODO: Fix once shaders support 4 corner radii customization - 0.4, - [base_color.red, base_color.green, base_color.blue], + 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 { @@ -439,7 +441,7 @@ impl MoveGrab { if let Some(indicator) = 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) { @@ -453,14 +455,16 @@ impl MoveGrab { let indicator_location = shell.stacking_indicator(¤t_output, self.previous); if indicator_location.is_some() != grab_state.stacking_indicator.is_some() { grab_state.stacking_indicator = indicator_location.map(|geo| { - let size = geo.size.as_logical(); - let element = StackHover::new( + let element = stack_hover( state.common.event_loop_handle.clone(), - size, + geo.size.as_logical(), state.common.theme.clone(), ); 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()) }); diff --git a/src/shell/layout/floating/mod.rs b/src/shell/layout/floating/mod.rs index 95d7264d..e3829e58 100644 --- a/src/shell/layout/floating/mod.rs +++ b/src/shell/layout/floating/mod.rs @@ -9,14 +9,10 @@ use std::{ use cosmic_comp_config::AppearanceConfig; use cosmic_settings_config::shortcuts::action::ResizeDirection; use keyframe::{ease, functions::EaseInOutCubic}; -use smallvec::SmallVec; use smithay::{ - backend::{ - drm::DrmNode, - renderer::element::{ - RenderElement, - utils::{Relocate, RelocateRenderElement, RescaleRenderElement}, - }, + backend::renderer::element::{ + AsRenderElements, RenderElement, + utils::{Relocate, RelocateRenderElement, RescaleRenderElement}, }, desktop::{PopupKind, Space, WindowSurfaceType, layer_map_for_output, space::SpaceElement}, input::Seat, @@ -1350,7 +1346,7 @@ impl FloatingLayout { mapped.set_bounds(geometry.size.as_logical()); let prev = self.space.element_geometry(&mapped).map(RectExt::as_local); - let window_geometry = if mapped.is_maximized(true) { + let window_geometry = if mapped.is_maximized(false) { geometry } else { prev.map(|mut rect| { @@ -1443,9 +1439,8 @@ impl FloatingLayout { &self, renderer: &mut R, alpha: f32, - scanout_node: Option, - push: &mut dyn FnMut(CosmicMappedRenderElement), - ) where + ) -> Vec> + where R: AsGlowRenderer, R::TextureId: Send + Clone + 'static, CosmicMappedRenderElement: RenderElement, @@ -1455,6 +1450,8 @@ impl FloatingLayout { let output = self.space.outputs().next().unwrap(); let output_scale = output.current_scale().fractional_scale(); + let mut elements = Vec::default(); + for elem in self .animations .iter() @@ -1469,17 +1466,19 @@ impl FloatingLayout { .unwrap_or_else(|| (self.space.element_geometry(elem).unwrap().as_local(), alpha)); let render_location = geometry.loc - elem.geometry().loc.as_local(); - elem.push_popup_render_elements( - renderer, - render_location - .as_logical() - .to_physical_precise_round(output_scale), - output_scale.into(), - alpha, - scanout_node, - push, + elements.extend( + elem.popup_render_elements( + renderer, + render_location + .as_logical() + .to_physical_precise_round(output_scale), + output_scale.into(), + alpha, + ), ); } + + elements } #[profiling::function] @@ -1491,9 +1490,8 @@ impl FloatingLayout { indicator_thickness: u8, alpha: f32, theme: &cosmic::theme::CosmicTheme, - scanout_node: Option, - push: &mut dyn FnMut(CosmicMappedRenderElement), - ) where + ) -> Vec> + where R: AsGlowRenderer, R::TextureId: Send + Clone + 'static, CosmicMappedRenderElement: RenderElement, @@ -1506,7 +1504,8 @@ impl FloatingLayout { layers.non_exclusive_zone() }; 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 .animations @@ -1520,9 +1519,32 @@ impl FloatingLayout { .get(elem) .map(|anim| (*anim.previous_geometry(), alpha * anim.alpha())) .unwrap_or_else(|| (self.space.element_geometry(elem).unwrap().as_local(), alpha)); - let render_location = geometry.loc - elem.geometry().loc.as_local(); - let 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(); geometry = anim.geometry( output_geometry, @@ -1540,77 +1562,80 @@ impl FloatingLayout { y: geometry.size.h as f64 / buffer_size.h as f64, }; - Some(move |element| match element { - CosmicMappedRenderElement::Stack(elem) => { - CosmicMappedRenderElement::MovingStack({ - let rescaled = RescaleRenderElement::from_element( - elem, - original_geo - .loc - .as_logical() - .to_physical_precise_round(output_scale), - scale, - ); + window_elements = window_elements + .into_iter() + .map(|element| match element { + CosmicMappedRenderElement::Stack(elem) => { + CosmicMappedRenderElement::MovingStack({ + let rescaled = RescaleRenderElement::from_element( + elem, + original_geo + .loc + .as_logical() + .to_physical_precise_round(output_scale), + scale, + ); - RelocateRenderElement::from_element( - rescaled, - (geometry.loc - original_geo.loc) - .as_logical() - .to_physical_precise_round(output_scale), - Relocate::Relative, - ) - }) - } - CosmicMappedRenderElement::Window(elem) => { - CosmicMappedRenderElement::MovingWindow({ - let rescaled = RescaleRenderElement::from_element( - elem, - original_geo - .loc - .as_logical() - .to_physical_precise_round(output_scale), - scale, - ); + RelocateRenderElement::from_element( + rescaled, + (geometry.loc - original_geo.loc) + .as_logical() + .to_physical_precise_round(output_scale), + Relocate::Relative, + ) + }) + } + CosmicMappedRenderElement::Window(elem) => { + CosmicMappedRenderElement::MovingWindow({ + let rescaled = RescaleRenderElement::from_element( + elem, + original_geo + .loc + .as_logical() + .to_physical_precise_round(output_scale), + scale, + ); - RelocateRenderElement::from_element( - rescaled, - (geometry.loc - original_geo.loc) - .as_logical() - .to_physical_precise_round(output_scale), - Relocate::Relative, - ) - }) - } - x => x, - }) - } else { - None - }; + RelocateRenderElement::from_element( + rescaled, + (geometry.loc - original_geo.loc) + .as_logical() + .to_physical_precise_round(output_scale), + Relocate::Relative, + ) + }) + } + x => x, + }) + .collect(); + } if focused == Some(elem) && !elem.is_maximized(false) { - let active_window_hint = crate::theme::active_window_hint(theme); - let radius = elem.corner_radius(geometry.size.as_logical(), indicator_thickness); - if let Some((mode, resize)) = resize_indicator.as_mut() { let mut resize_geometry = geometry; resize_geometry.loc -= (18, 18).into(); resize_geometry.size += (36, 36).into(); resize.resize(resize_geometry.size.as_logical()); - resize.output_enter(output); - resize.push_render_elements( - renderer, - resize_geometry - .loc - .as_logical() - .to_physical_precise_round(output_scale), - output_scale.into(), - alpha * mode.alpha().unwrap_or(1.0), - &mut |elem| push(CosmicMappedRenderElement::Window(elem.into())), - None, - ); + resize.output_enter(output, Rectangle::default() /* unused */); + window_elements = resize + .render_elements::>( + renderer, + resize_geometry + .loc + .as_logical() + .to_physical_precise_round(output_scale), + output_scale.into(), + alpha * mode.alpha().unwrap_or(1.0), + ) + .into_iter() + .map(CosmicMappedRenderElement::Window) + .chain(window_elements.into_iter()) + .collect(); } + let active_window_hint = crate::theme::active_window_hint(theme); + let radius = elem.corner_radius(geometry.size.as_logical(), indicator_thickness); if indicator_thickness > 0 { let element = IndicatorShader::focus_element( renderer, @@ -1626,47 +1651,14 @@ impl FloatingLayout { active_window_hint.blue, ], ); - push(element.into()); + window_elements.insert(0, element.into()); } } - let map_anim = |elem| { - if let Some(map) = maybe_map { - map(elem) - } else { - elem - } - }; - - elem.push_render_elements( - renderer, - render_location - .as_logical() - .to_physical_precise_round(output_scale), - None, - output_scale.into(), - alpha, - None, - scanout_node, - &mut |elem| push(map_anim(elem)), - &mut |elem| lower_elements.push(map_anim(elem)), - ); - if let Some(shadow_element) = elem.shadow_render_element( - renderer, - render_location - .as_logical() - .to_physical_precise_round(output_scale), - None, - output_scale.into(), - 1., - alpha, - ) { - push(map_anim(shadow_element)); - } - for elem in lower_elements.drain(..) { - push(elem); - } + elements.extend(window_elements); } + + elements } pub fn snap_to_corner(&self, mapped: &CosmicMapped, corners: &TiledCorners) { diff --git a/src/shell/layout/tiling/mod.rs b/src/shell/layout/tiling/mod.rs index 2d95cd27..e7a7f0f1 100644 --- a/src/shell/layout/tiling/mod.rs +++ b/src/shell/layout/tiling/mod.rs @@ -6,7 +6,8 @@ use crate::{ element::AsGlowRenderer, }, shell::{ - CosmicSurface, Direction, FocusResult, MoveResult, OverviewMode, ResizeMode, Trigger, + CosmicSurface, Direction, FocusResult, MoveResult, OutputNotMapped, OverviewMode, + ResizeMode, Trigger, element::{ CosmicMapped, CosmicMappedRenderElement, CosmicStack, CosmicWindow, resize_indicator::ResizeIndicator, @@ -44,20 +45,16 @@ use keyframe::{ ease, functions::{EaseInOutCubic, Linear}, }; -use smallvec::SmallVec; use smithay::{ - backend::{ - drm::DrmNode, - renderer::{ - element::{ - Id, RenderElement, - utils::{ - ConstrainAlign, ConstrainScaleBehavior, RescaleRenderElement, - constrain_render_elements, - }, + backend::renderer::{ + element::{ + AsRenderElements, Id, RenderElement, + utils::{ + ConstrainAlign, ConstrainScaleBehavior, RescaleRenderElement, + constrain_render_elements, }, - glow::GlowRenderer, }, + glow::GlowRenderer, }, desktop::{PopupKind, WindowSurfaceType, layer_map_for_output, space::SpaceElement}, input::Seat, @@ -1877,8 +1874,8 @@ impl TilingLayout { if let Some(id) = id { return match tree.get(&id).unwrap().data() { Data::Mapped { mapped, .. } => { - if let Some(stack) = mapped.stack_ref() { - stack.focus_stack(); + if mapped.is_stack() { + mapped.stack_ref().unwrap().focus_stack(); } FocusResult::Some(mapped.clone().into()) } @@ -1986,11 +1983,11 @@ impl TilingLayout { })) } Data::Mapped { mapped, .. } => { - if let Some(stack) = mapped.stack_ref() + if mapped.is_stack() && desc.stack_window.is_none() && replacement_id == &desc.node { - stack.focus_stack(); + mapped.stack_ref().unwrap().focus_stack(); } FocusResult::Some(KeyboardFocusTarget::Element(mapped.clone())) } @@ -2062,7 +2059,7 @@ impl TilingLayout { }); } Data::Mapped { mapped, .. } => { - if let Some(stack) = mapped.stack_ref() + if mapped.is_stack() && swap_desc .as_ref() .map(|desc| { @@ -2071,7 +2068,7 @@ impl TilingLayout { }) .unwrap_or(false) { - stack.focus_stack(); + mapped.stack_ref().unwrap().focus_stack(); } return FocusResult::Some(mapped.clone().into()); } @@ -3150,7 +3147,7 @@ impl TilingLayout { location_f64: Point, seat: &Seat, ) -> Option { - let location = location_f64.to_i32_floor(); + let location = location_f64.to_i32_round(); for (mapped, geo) in self.mapped() { if !mapped.bbox().contains((location - geo.loc).as_logical()) { @@ -3177,13 +3174,9 @@ impl TilingLayout { location_f64: Point, seat: &Seat, ) -> Option { - let location = location_f64.to_i32_floor(); + let location = location_f64.to_i32_round(); for (mapped, geo) in self.mapped() { - // Tiled windows are rendered cropped to their tile (`geo`), so input must be bound to the tile as well - if !geo.contains(location) { - continue; - } if !mapped.bbox().contains((location - geo.loc).as_logical()) { continue; } @@ -3209,7 +3202,7 @@ impl TilingLayout { overview: OverviewMode, seat: &Seat, ) -> Option<(PointerFocusTarget, Point)> { - let location = location_f64.to_i32_floor(); + let location = location_f64.to_i32_round(); if matches!(overview, OverviewMode::None) { for (mapped, geo) in self.mapped() { @@ -3244,14 +3237,10 @@ impl TilingLayout { ) -> Option<(PointerFocusTarget, Point)> { let tree = &self.queue.trees.back().unwrap().0; let root = tree.root_node_id()?; - let location = location_f64.to_i32_floor(); + let location = location_f64.to_i32_round(); if matches!(overview, OverviewMode::None) { for (mapped, geo) in self.mapped() { - // Tiled windows are rendered cropped to their tile (`geo`), so input must be bound to the tile as well - if !geo.contains(location) { - continue; - } if !mapped.bbox().contains((location - geo.loc).as_logical()) { continue; } @@ -3305,7 +3294,7 @@ impl TilingLayout { .. }, )) => { - let test_point = (location_f64 - last_geometry.loc.to_f64() + let test_point = (location.to_f64() - last_geometry.loc.to_f64() + mapped.geometry().loc.to_f64().as_local()) .as_logical(); mapped @@ -3410,7 +3399,7 @@ impl TilingLayout { } let location_f64 = location_f64.unwrap(); - let location = location_f64.to_i32_floor(); + let location = location_f64.to_i32_round(); if matches!( overview.active_trigger(), @@ -3432,8 +3421,8 @@ impl TilingLayout { None, None, self.theme.cosmic(), - &mut |_| {}, - ); + ) + .0; let mut result = None; let mut lookup = Some(root.clone()); @@ -3660,7 +3649,7 @@ impl TilingLayout { TargetZone::WindowStack(id, last_geometry) } else { let left_right = { - let relative_loc = location_f64.x - last_geometry.loc.x as f64; + let relative_loc = (location.x - last_geometry.loc.x) as f64; if relative_loc < last_geometry.size.w as f64 / 2.0 { (Direction::Left, relative_loc / last_geometry.size.w as f64) } else { @@ -3671,7 +3660,7 @@ impl TilingLayout { } }; let up_down = { - let relative_loc = location_f64.y - last_geometry.loc.y as f64; + let relative_loc = (location.y - last_geometry.loc.y) as f64; if relative_loc < last_geometry.size.h as f64 / 2.0 { (Direction::Up, relative_loc / last_geometry.size.h as f64) } else { @@ -4013,15 +4002,13 @@ impl TilingLayout { &self, renderer: &mut R, seat: Option<&Seat>, - focused: Option<&CosmicMapped>, non_exclusive_zone: Rectangle, overview: (OverviewMode, Option<(SwapIndicator, Option<&Tree>)>), resize_indicator: Option<(ResizeMode, ResizeIndicator)>, indicator_thickness: u8, theme: &cosmic::theme::CosmicTheme, - scanout_node: Option, - push: &mut dyn FnMut(CosmicMappedRenderElement), - ) where + ) -> Result>, OutputNotMapped> + where R: AsGlowRenderer, R::TextureId: Send + Clone + 'static, CosmicMappedRenderElement: RenderElement, @@ -4061,6 +4048,8 @@ impl TilingLayout { }; let draw_groups = overview.0.alpha(); + let mut elements = Vec::default(); + let is_overview = !matches!(overview.0, OverviewMode::None); let is_mouse_tiling = (matches!(overview.0.trigger(), Some(Trigger::Pointer(_)))) .then(|| self.last_overview_hover.as_ref().map(|(_, zone)| zone)); @@ -4072,7 +4061,7 @@ impl TilingLayout { // all gone windows and fade them out let old_geometries = if let Some(reference_tree) = reference_tree.as_ref() { - let geometries = if let Some(transition) = draw_groups { + let (geometries, _) = if let Some(transition) = draw_groups { Some(geometries_for_groupview( reference_tree, &mut *renderer, @@ -4087,14 +4076,14 @@ impl TilingLayout { swap_desc.clone(), overview.1.as_ref().and_then(|(_, tree)| *tree), theme, - &mut |_elem| {}, )) } else { None - }; + } + .unzip(); // all old windows we want to fade out - render_old_tree_windows( + elements.extend(render_old_tree_windows( reference_tree, target_tree, renderer, @@ -4104,17 +4093,14 @@ impl TilingLayout { indicator_thickness, swap_desc.is_some(), theme, - scanout_node, - push, - ); + )); geometries } else { None }; - let mut group_elements = SmallVec::<[_; 4]>::new_const(); - let geometries = if let Some(transition) = draw_groups { + let (geometries, group_elements) = if let Some(transition) = draw_groups { Some(geometries_for_groupview( target_tree, &mut *renderer, @@ -4128,14 +4114,14 @@ impl TilingLayout { swap_desc.clone(), overview.1.as_ref().and_then(|(_, tree)| *tree), theme, - &mut |elem| group_elements.push(elem), )) } else { None - }; + } + .unzip(); // all alive windows - render_new_tree_windows( + elements.extend(render_new_tree_windows( target_tree, reference_tree, renderer, @@ -4144,7 +4130,6 @@ impl TilingLayout { old_geometries, is_overview, seat, - focused, &self.output, percentage, draw_groups, @@ -4164,14 +4149,14 @@ impl TilingLayout { &self.swapping_stack_surface_id, &self.backdrop_id, theme, - scanout_node, - push, - ); + )); // tiling hints - for elem in group_elements.into_iter() { - push(elem); + if let Some(group_elements) = group_elements { + elements.extend(group_elements); } + + Ok(elements) } #[profiling::function] @@ -4182,9 +4167,8 @@ impl TilingLayout { non_exclusive_zone: Rectangle, overview: (OverviewMode, Option<(SwapIndicator, Option<&Tree>)>), theme: &cosmic::theme::CosmicTheme, - scanout_node: Option, - push: &mut dyn FnMut(CosmicMappedRenderElement), - ) where + ) -> Result>, OutputNotMapped> + where R: AsGlowRenderer, R::TextureId: Send + Clone + 'static, CosmicMappedRenderElement: RenderElement, @@ -4216,6 +4200,8 @@ impl TilingLayout { }; let draw_groups = overview.0.alpha(); + let mut elements = Vec::default(); + let is_mouse_tiling = (matches!(overview.0.trigger(), Some(Trigger::Pointer(_)))) .then(|| self.last_overview_hover.as_ref().map(|(_, zone)| zone)); let swap_desc = if let Some(Trigger::KeyboardSwap(_, desc)) = overview.0.trigger() { @@ -4226,7 +4212,7 @@ impl TilingLayout { // all gone windows and fade them out let old_geometries = if let Some(reference_tree) = reference_tree.as_ref() { - let geometries = if let Some(transition) = draw_groups { + let (geometries, _) = if let Some(transition) = draw_groups { Some(geometries_for_groupview( reference_tree, &mut *renderer, @@ -4241,14 +4227,14 @@ impl TilingLayout { swap_desc.clone(), overview.1.as_ref().and_then(|(_, tree)| *tree), theme, - &mut |_| {}, )) } else { None - }; + } + .unzip(); // all old windows we want to fade out - render_old_tree_popups( + elements.extend(render_old_tree_popups( reference_tree, target_tree, renderer, @@ -4256,16 +4242,14 @@ impl TilingLayout { output_scale, percentage, swap_desc.is_some(), - scanout_node, - push, - ); + )); geometries } else { None }; - let geometries = if let Some(transition) = draw_groups { + let (geometries, _) = if let Some(transition) = draw_groups { Some(geometries_for_groupview( target_tree, &mut *renderer, @@ -4279,14 +4263,14 @@ impl TilingLayout { swap_desc.clone(), overview.1.as_ref().and_then(|(_, tree)| *tree), theme, - &mut |_| {}, )) } else { None - }; + } + .unzip(); // all alive windows - render_new_tree_popups( + elements.extend(render_new_tree_popups( target_tree, reference_tree, renderer, @@ -4297,9 +4281,9 @@ impl TilingLayout { percentage, overview, swap_desc.clone(), - scanout_node, - push, - ); + )); + + Ok(elements) } fn gaps(&self) -> (i32, i32) { @@ -4354,8 +4338,10 @@ fn geometries_for_groupview<'a, R>( swap_desc: Option, swap_tree: Option<&Tree>, _theme: &cosmic::theme::CosmicTheme, - push: &mut dyn FnMut(CosmicMappedRenderElement), -) -> HashMap> +) -> ( + HashMap>, + Vec>, +) where R: AsGlowRenderer + 'a, R::TextureId: 'static, @@ -4378,18 +4364,11 @@ where // push bogos value, that will get ignored anyway stack.push((Rectangle::from_size((320, 240).into()), 0)); } - - let has_root = root.is_some(); - if has_root { + if root.is_some() { stack.push((non_exclusive_zone, 0)); } - let mut push = |elem| { - if has_root { - push(elem) - } - }; - + let mut elements = Vec::new(); let mut geometries: HashMap> = HashMap::new(); let alpha = alpha * transition; @@ -4544,7 +4523,7 @@ where if let Some(renderer) = renderer.as_mut() { if (render_potential_group || render_active_child) && Some(&node_id) != root { - push( + elements.push( IndicatorShader::element( *renderer, Key::Group(Arc::downgrade(alive)), @@ -4562,7 +4541,7 @@ where && pill_indicator.is_some() && Some(&node_id) != root { - push( + elements.push( IndicatorShader::element( *renderer, Key::Group(Arc::downgrade(alive)), @@ -4626,7 +4605,7 @@ where }; if draw_outline { - push( + elements.push( IndicatorShader::element( *renderer, Key::Group(Arc::downgrade(alive)), @@ -4687,7 +4666,7 @@ where }; if let Some(renderer) = renderer.as_mut() { - push( + elements.push( BackdropShader::element( *renderer, backdrop_id.clone(), @@ -4705,7 +4684,7 @@ where if matches!(swap_desc, Some(ref desc) if desc.node == node_id) { if let Some(renderer) = renderer.as_mut() { - push( + elements.push( BackdropShader::element( *renderer, Key::Group(Arc::downgrade(alive)), @@ -4790,7 +4769,7 @@ where .unwrap_or(false) { if let Some(renderer) = renderer.as_mut() { - push( + elements.push( BackdropShader::element( *renderer, backdrop_id.clone(), @@ -4832,7 +4811,7 @@ where .unwrap_or(false) { if let Some(renderer) = renderer.as_mut() { - push( + elements.push( BackdropShader::element( *renderer, backdrop_id.clone(), @@ -4863,7 +4842,7 @@ where if let Some(renderer) = renderer.as_mut() { if render_potential_group { - push( + elements.push( IndicatorShader::element( *renderer, Key::Window(Usage::PotentialGroupIndicator, mapped.key()), @@ -4903,7 +4882,7 @@ where geo.loc += (WINDOW_BACKDROP_BORDER, WINDOW_BACKDROP_BORDER).into(); geo.size -= (WINDOW_BACKDROP_BORDER * 2, WINDOW_BACKDROP_BORDER * 2).into(); - push( + elements.push( BackdropShader::element( *renderer, Key::Window(Usage::OverviewBackdrop, mapped.key()), @@ -4960,7 +4939,7 @@ where if let Some(renderer) = renderer.as_mut() { geo.loc += (WINDOW_BACKDROP_BORDER, WINDOW_BACKDROP_BORDER).into(); geo.size -= (WINDOW_BACKDROP_BORDER * 2, WINDOW_BACKDROP_BORDER * 2).into(); - push( + elements.push( BackdropShader::element( *renderer, id.clone(), @@ -4979,7 +4958,11 @@ where } } - geometries + if root.is_none() { + elements.clear(); + } + + (geometries, elements) } fn render_old_tree_popups( @@ -4990,15 +4973,16 @@ fn render_old_tree_popups( output_scale: f64, percentage: f32, is_swap_mode: bool, - scanout_node: Option, - push: &mut dyn FnMut(CosmicMappedRenderElement), -) where +) -> Vec> +where R: AsGlowRenderer, R::TextureId: Send + Clone + 'static, CosmicMappedRenderElement: RenderElement, CosmicWindowRenderElement: RenderElement, CosmicStackRenderElement: RenderElement, { + let mut elements = Vec::default(); + render_old_tree( reference_tree, target_tree, @@ -5007,16 +4991,19 @@ fn render_old_tree_popups( percentage, is_swap_mode, |mapped, elem_geometry, geo, alpha, _| { - mapped.push_popup_render_elements( - renderer, - geo.loc.as_logical().to_physical_precise_round(output_scale) - elem_geometry.loc, - Scale::from(output_scale), - alpha, - scanout_node, - push, - ) + elements.extend( + mapped.popup_render_elements::>( + renderer, + geo.loc.as_logical().to_physical_precise_round(output_scale) + - elem_geometry.loc, + Scale::from(output_scale), + alpha, + ), + ); }, - ) + ); + + elements } fn render_old_tree_windows( @@ -5029,9 +5016,8 @@ fn render_old_tree_windows( indicator_thickness: u8, is_swap_mode: bool, theme: &cosmic::theme::CosmicTheme, - scanout_node: Option, - push: &mut dyn FnMut(CosmicMappedRenderElement), -) where +) -> Vec> +where R: AsGlowRenderer, R::TextureId: Send + Clone + 'static, CosmicMappedRenderElement: RenderElement, @@ -5039,35 +5025,8 @@ fn render_old_tree_windows( CosmicStackRenderElement: RenderElement, { let window_hint = crate::theme::active_window_hint(theme); - let mut lower_elements = Vec::default(); - let mut shadow_elements = SmallVec::<[_; 4]>::new_const(); - - let window_map = - |elem, geo: Rectangle, elem_geometry: Rectangle| match elem { - CosmicMappedRenderElement::Stack(elem) => constrain_render_elements( - std::iter::once(elem), - geo.loc.as_logical().to_physical_precise_round(output_scale) - elem_geometry.loc, - geo.as_logical().to_physical_precise_round(output_scale), - elem_geometry, - ConstrainScaleBehavior::Stretch, - ConstrainAlign::CENTER, - output_scale, - ) - .next() - .map(CosmicMappedRenderElement::TiledStack), - CosmicMappedRenderElement::Window(elem) => constrain_render_elements( - std::iter::once(elem), - geo.loc.as_logical().to_physical_precise_round(output_scale) - elem_geometry.loc, - geo.as_logical().to_physical_precise_round(output_scale), - elem_geometry, - ConstrainScaleBehavior::Stretch, - ConstrainAlign::CENTER, - output_scale, - ) - .next() - .map(CosmicMappedRenderElement::TiledWindow), - x => Some(x), - }; + let mut elements = Vec::default(); + let mut shadow_elements = Vec::default(); render_old_tree( reference_tree, @@ -5077,9 +5036,56 @@ fn render_old_tree_windows( percentage, is_swap_mode, |mapped, elem_geometry, geo, alpha, is_minimizing| { + shadow_elements.extend(mapped.shadow_render_element( + renderer, + geo.loc.as_logical().to_physical_precise_round(output_scale) - elem_geometry.loc, + Some(geo.size.as_logical()), + Scale::from(output_scale), + 1., + alpha, + )); + + let window_elements = mapped.render_elements::>( + renderer, + geo.loc.as_logical().to_physical_precise_round(output_scale) - elem_geometry.loc, + Some(geo.size.as_logical()), + Scale::from(output_scale), + alpha, + None, + ); + + elements.extend(window_elements.into_iter().flat_map(|element| { + match element { + CosmicMappedRenderElement::Stack(elem) => constrain_render_elements( + std::iter::once(elem), + geo.loc.as_logical().to_physical_precise_round(output_scale) + - elem_geometry.loc, + geo.as_logical().to_physical_precise_round(output_scale), + elem_geometry, + ConstrainScaleBehavior::Stretch, + ConstrainAlign::CENTER, + output_scale, + ) + .next() + .map(CosmicMappedRenderElement::TiledStack), + CosmicMappedRenderElement::Window(elem) => constrain_render_elements( + std::iter::once(elem), + geo.loc.as_logical().to_physical_precise_round(output_scale) + - elem_geometry.loc, + geo.as_logical().to_physical_precise_round(output_scale), + elem_geometry, + ConstrainScaleBehavior::Stretch, + ConstrainAlign::CENTER, + output_scale, + ) + .next() + .map(CosmicMappedRenderElement::TiledWindow), + x => Some(x), + } + })); let radius = mapped.corner_radius(geo.size.as_logical(), indicator_thickness); if is_minimizing && indicator_thickness > 0 { - push(CosmicMappedRenderElement::FocusIndicator( + elements.push(CosmicMappedRenderElement::FocusIndicator( IndicatorShader::focus_element( renderer, Key::Window(Usage::FocusIndicator, mapped.clone().key()), @@ -5092,44 +5098,10 @@ fn render_old_tree_windows( ), )); } - - mapped.push_render_elements( - renderer, - geo.loc.as_logical().to_physical_precise_round(output_scale) - elem_geometry.loc, - Some(geo.size.as_logical()), - Scale::from(output_scale), - alpha, - None, - scanout_node, - &mut |elem| { - if let Some(elem) = window_map(elem, geo, elem_geometry) { - push(elem); - } - }, - &mut |elem| { - if let Some(elem) = window_map(elem, geo, elem_geometry) { - lower_elements.push(elem); - } - }, - ); - - shadow_elements.extend(mapped.shadow_render_element( - renderer, - geo.loc.as_logical().to_physical_precise_round(output_scale) - elem_geometry.loc, - Some(geo.size.as_logical()), - Scale::from(output_scale), - 1., - alpha, - )); }, ); - for elem in shadow_elements { - push(elem); - } - for elem in lower_elements { - push(elem); - } + shadow_elements.into_iter().chain(elements).collect() } fn render_old_tree( @@ -5224,15 +5196,15 @@ fn render_new_tree_popups( percentage: f32, overview: (OverviewMode, Option<(SwapIndicator, Option<&Tree>)>), swap_desc: Option, - scanout_node: Option, - push: &mut dyn FnMut(CosmicMappedRenderElement), -) where +) -> Vec> +where R: AsGlowRenderer, R::TextureId: Send + Clone + 'static, CosmicMappedRenderElement: RenderElement, CosmicWindowRenderElement: RenderElement, CosmicStackRenderElement: RenderElement, { + let mut popup_elements = Vec::new(); let output_scale = output.current_scale().fractional_scale(); let is_active_output = seat @@ -5255,18 +5227,20 @@ fn render_new_tree_popups( if let Data::Mapped { mapped, .. } = data { let elem_geometry = mapped.geometry().to_physical_precise_round(output_scale); - mapped.push_popup_render_elements( - renderer, - geo.loc.as_logical().to_physical_precise_round(output_scale) - - elem_geometry.loc, - Scale::from(output_scale), - alpha, - scanout_node, - push, + popup_elements.extend( + mapped.popup_render_elements::>( + renderer, + geo.loc.as_logical().to_physical_precise_round(output_scale) + - elem_geometry.loc, + Scale::from(output_scale), + alpha, + ), ); } }, ); + + popup_elements } fn render_new_tree_windows( @@ -5278,7 +5252,6 @@ fn render_new_tree_windows( old_geometries: Option>>, is_overview: bool, seat: Option<&Seat>, - focused: Option<&CosmicMapped>, output: &Output, percentage: f32, transition: Option, @@ -5289,28 +5262,22 @@ fn render_new_tree_windows( swapping_stack_surface_id: &Id, backdrop_id: &Id, theme: &cosmic::theme::CosmicTheme, - scanout_node: Option, - push: &mut dyn FnMut(CosmicMappedRenderElement), -) where +) -> Vec> +where R: AsGlowRenderer, R::TextureId: Send + Clone + 'static, CosmicMappedRenderElement: RenderElement, CosmicWindowRenderElement: RenderElement, CosmicStackRenderElement: RenderElement, { - let focused = match seat.and_then(|seat| seat.get_keyboard().unwrap().current_focus()) { - Some(target @ KeyboardFocusTarget::Group(_)) => { - TilingLayout::currently_focused_node(target_tree, target).map(|(id, _)| id) - } - _ => focused.and_then(|mapped| { - let node_id = mapped.tiling_node_id.lock().unwrap().clone()?; - target_tree - .get(&node_id) - .ok() - .filter(|node| node.data().is_mapped(Some(mapped))) - .map(|_| node_id) - }), - }; + let focused = seat + .and_then(|seat| { + seat.get_keyboard() + .unwrap() + .current_focus() + .and_then(|target| TilingLayout::currently_focused_node(target_tree, target)) + }) + .map(|(id, _)| id); let focused_geo = if let Some(focused) = focused.as_ref() { geometries .as_ref() @@ -5336,22 +5303,19 @@ fn render_new_tree_windows( .map(|seat| &seat.active_output() == output) .unwrap_or(false); - let mut animating_window_upper_elements = Vec::new(); - let mut animating_window_lower_elements = Vec::new(); - let mut animating_shadow_elements = SmallVec::<[CosmicMappedRenderElement; 4]>::new_const(); - - let mut window_upper_elements = Vec::new(); - let mut window_lower_elements = Vec::new(); - let mut shadow_elements = SmallVec::<[CosmicMappedRenderElement; 4]>::new_const(); + let mut animating_window_elements = Vec::new(); + let mut window_elements = Vec::new(); let mut group_backdrop = None; - let mut indicators = SmallVec::<[CosmicMappedRenderElement; 2]>::new_const(); - let mut resize_elements = SmallVec::<[CosmicMappedRenderElement; 10]>::new_const(); - let mut swap_elements = SmallVec::<[CosmicMappedRenderElement; 4]>::new_const(); + let mut indicators = Vec::new(); + let mut resize_elements = None; + let mut swap_elements = Vec::new(); + let mut shadow_elements = Vec::new(); + let output_geo = output.geometry(); let output_scale = output.current_scale().fractional_scale(); - let (mut swap_indicator, swap_tree) = overview.1.unzip(); + let (swap_indicator, swap_tree) = overview.1.unzip(); let swap_desc = swap_desc.filter(|_| is_active_output); let swap_tree = swap_tree.flatten().filter(|_| is_active_output); let window_hint = crate::theme::active_window_hint(theme); @@ -5359,7 +5323,7 @@ fn render_new_tree_windows( // render placeholder, if we are swapping to an empty workspace if target_tree.root_node_id().is_none() && swap_desc.is_some() { - window_upper_elements.push( + window_elements.push( BackdropShader::element( renderer, backdrop_id.clone(), @@ -5414,36 +5378,31 @@ fn render_new_tree_windows( let render_loc = (swap_geo.loc.as_logical() - window_geo.loc).to_physical_precise_round(output_scale); - window.push_render_elements( - renderer, - render_loc, - output_scale.into(), - 1.0, - None, - scanout_node, - false, - [0, 0, 0, 0], - // TODO - 0, - &mut |elem| { - swap_elements.push(CosmicMappedRenderElement::GrabbedWindow( - RescaleRenderElement::from_element( - elem.into(), - swap_geo - .loc - .as_logical() - .to_physical_precise_round(output_scale), - ease( - Linear, - 1.0, - swap_factor(window_geo.size), - transition.unwrap_or(1.0), - ), + swap_elements.extend( + AsRenderElements::render_elements::>( + &window, + renderer, + render_loc, + output_scale.into(), + 1.0, + ) + .into_iter() + .map(|window| { + CosmicMappedRenderElement::GrabbedWindow(RescaleRenderElement::from_element( + window, + swap_geo + .loc + .as_logical() + .to_physical_precise_round(output_scale), + ease( + Linear, + 1.0, + swap_factor(window_geo.size), + transition.unwrap_or(1.0), ), - )); - }, - None, - ); + )) + }), + ) } // render actual tree nodes @@ -5555,18 +5514,19 @@ fn render_new_tree_windows( .unwrap_or(false) }) .unwrap_or(false)) - && let Some(swap) = swap_indicator.as_mut() + && let Some(swap) = swap_indicator.as_ref() { - let size = geo.size.as_logical(); - swap.resize(size); - swap.output_enter(output); - swap.push_render_elements( - renderer, - geo.loc.as_logical().to_physical_precise_round(output_scale), - output_scale.into(), - alpha * overview.0.alpha().unwrap_or(1.0), - &mut |elem| swap_elements.push(elem.into()), - None, + swap.resize(geo.size.as_logical()); + swap.output_enter(output, output_geo.as_logical()); + swap_elements.extend( + swap.render_elements::>( + renderer, + geo.loc.as_logical().to_physical_precise_round(output_scale), + output_scale.into(), + alpha * overview.0.alpha().unwrap_or(1.0), + ) + .into_iter() + .map(CosmicMappedRenderElement::from), ); } } @@ -5577,20 +5537,32 @@ fn render_new_tree_windows( geo.size += (36, 36).into(); resize.resize(geo.size.as_logical()); - resize.output_enter(output); + resize.output_enter(output, output_geo.as_logical()); let possible_edges = TilingLayout::possible_resizes(target_tree, node_id.clone()); if !possible_edges.is_empty() { - resize.set_edges(possible_edges); - resize.push_render_elements( - renderer, - geo.loc.as_logical().to_physical_precise_round(output_scale), - output_scale.into(), - alpha * mode.alpha().unwrap_or(1.0), - &mut |elem| { - resize_elements.push(elem.into()); - }, - None, + if resize.with_program(|internal| { + let mut edges = internal.edges.lock().unwrap(); + if *edges != possible_edges { + *edges = possible_edges; + true + } else { + false + } + }) { + resize.force_update(); + } + resize_elements = Some( + resize + .render_elements::>( + renderer, + geo.loc.as_logical().to_physical_precise_round(output_scale), + output_scale.into(), + alpha * mode.alpha().unwrap_or(1.0), + ) + .into_iter() + .map(CosmicMappedRenderElement::from) + .collect::>(), ); } } @@ -5616,6 +5588,51 @@ fn render_new_tree_windows( scale.x.min(scale.y), alpha, ); + let mut elements = mapped.render_elements::>( + renderer, + //original_location, + geo.loc.as_logical().to_physical_precise_round(output_scale) + - elem_geometry.loc, + max_size, + Scale::from(output_scale), + alpha, + None, + ); + + if swap_desc + .as_ref() + .filter(|swap_desc| swap_desc.node == node_id) + .and_then(|swap_desc| swap_desc.stack_window.as_ref()) + .zip(focused.as_ref()) + .map(|(stack_window, focused_id)| { + target_tree + .get(focused_id) + .ok() + .map(|focused| match focused.data() { + Data::Mapped { mapped, .. } => mapped + .stack_ref() + .map(|stack| &stack.active() == stack_window) + .unwrap_or(false), + _ => false, + }) + .unwrap_or(false) + }) + .unwrap_or(false) + { + let mut active_geo = mapped.active_window_geometry().as_local(); + active_geo.loc += geo.loc - mapped.geometry().loc.as_local(); + elements.insert( + 0, + CosmicMappedRenderElement::Overlay(BackdropShader::element( + renderer, + Key::Window(Usage::Overlay, mapped.key()), + active_geo, + 0.0, + 0.3, + group_color, + )), + ) + } let (behavior, align) = if is_overview { (ConstrainScaleBehavior::Fit, ConstrainAlign::CENTER) @@ -5625,7 +5642,7 @@ fn render_new_tree_windows( (ConstrainScaleBehavior::CutOff, ConstrainAlign::TOP_LEFT) }; - let map_elem = |element| match element { + let elements = elements.into_iter().flat_map(|element| match element { CosmicMappedRenderElement::Stack(elem) => constrain_render_elements( std::iter::once(elem), geo.loc.as_logical().to_physical_precise_round(output_scale) @@ -5663,66 +5680,7 @@ fn render_new_tree_windows( .next() .map(CosmicMappedRenderElement::TiledOverlay), x => Some(x), - }; - - let mut upper_elements = SmallVec::<[CosmicMappedRenderElement; 4]>::new_const(); - let mut lower_elements = SmallVec::<[CosmicMappedRenderElement; 4]>::new_const(); - mapped.push_render_elements( - renderer, - //original_location, - geo.loc.as_logical().to_physical_precise_round(output_scale) - - elem_geometry.loc, - max_size, - Scale::from(output_scale), - alpha, - None, - scanout_node, - &mut |elem| { - if let Some(elem) = map_elem(elem) { - upper_elements.push(elem) - } - }, - &mut |elem| { - if let Some(elem) = map_elem(elem) { - lower_elements.push(elem) - } - }, - ); - - if swap_desc - .as_ref() - .filter(|swap_desc| swap_desc.node == node_id) - .and_then(|swap_desc| swap_desc.stack_window.as_ref()) - .zip(focused.as_ref()) - .map(|(stack_window, focused_id)| { - target_tree - .get(focused_id) - .ok() - .map(|focused| match focused.data() { - Data::Mapped { mapped, .. } => mapped - .stack_ref() - .map(|stack| &stack.active() == stack_window) - .unwrap_or(false), - _ => false, - }) - .unwrap_or(false) - }) - .unwrap_or(false) - { - let mut active_geo = mapped.active_window_geometry().as_local(); - active_geo.loc += geo.loc - mapped.geometry().loc.as_local(); - upper_elements.insert( - 0, - CosmicMappedRenderElement::Overlay(BackdropShader::element( - renderer, - Key::Window(Usage::Overlay, mapped.key()), - active_geo, - 0.0, - 0.3, - group_color, - )), - ) - } + }); if swap_desc .as_ref() @@ -5735,36 +5693,30 @@ fn render_new_tree_windows( }) .unwrap_or(false) { - swap_elements.extend(upper_elements); swap_elements.extend(shadow_element); - swap_elements.extend(lower_elements); - } else if animating { - animating_window_upper_elements.extend(upper_elements); - animating_shadow_elements.extend(shadow_element); - animating_window_lower_elements.extend(lower_elements); + swap_elements.extend(elements); } else { - window_upper_elements.extend(upper_elements); shadow_elements.extend(shadow_element); - window_lower_elements.extend(lower_elements); + if animating { + animating_window_elements.extend(elements); + } else { + window_elements.extend(elements); + } } } }, ); - for elem in resize_elements + resize_elements .into_iter() + .flatten() .chain(swap_elements) .chain(indicators) - .chain(window_upper_elements) + .chain(window_elements) + .chain(animating_window_elements) .chain(shadow_elements) - .chain(window_lower_elements) - .chain(animating_window_upper_elements) - .chain(animating_shadow_elements) - .chain(animating_window_lower_elements) .chain(group_backdrop.into_iter().map(Into::into)) - { - push(elem); - } + .collect() } fn render_new_tree( diff --git a/src/shell/mod.rs b/src/shell/mod.rs index 39d692e2..89309e88 100644 --- a/src/shell/mod.rs +++ b/src/shell/mod.rs @@ -12,11 +12,7 @@ use std::{ use wayland_backend::server::ClientId; use crate::{ - shell::{ - element::CosmicStack, focus::FocusTarget, grabs::fullscreen_items, - layout::tiling::PlaceholderType, - }, - utils, + shell::{focus::FocusTarget, grabs::fullscreen_items, layout::tiling::PlaceholderType}, wayland::{ handlers::data_device::{self, get_dnd_icon}, protocols::workspace::{State as WState, WorkspaceCapabilities}, @@ -103,8 +99,9 @@ use self::zoom::{OutputZoomState, ZoomState}; use self::{ element::{ - CosmicWindow, MaximizedState, resize_indicator::ResizeIndicator, - swap_indicator::SwapIndicator, + CosmicWindow, MaximizedState, + resize_indicator::{ResizeIndicator, resize_indicator}, + swap_indicator::{SwapIndicator, swap_indicator}, }, focus::target::{KeyboardFocusTarget, PointerFocusTarget}, grabs::{ @@ -1511,21 +1508,6 @@ impl Workspaces { #[derive(Debug)] pub struct InvalidWorkspaceIndex; -utils::id_gen!(next_output_id, OUTPUT_ID, OUTPUT_IDS); -pub struct OutputId(usize); - -impl OutputId { - pub fn namespace_for_workspace(&self, idx: usize) -> usize { - self.0 | (idx << 32) - } -} - -impl Drop for OutputId { - fn drop(&mut self) { - OUTPUT_IDS.lock().unwrap().remove(&self.0); - } -} - impl Common { pub fn add_output(&mut self, output: &Output) { let mut shell = self.shell.write(); @@ -1533,10 +1515,6 @@ impl Common { .workspaces .add_output(output, &mut self.workspace_state.update()); - output - .user_data() - .insert_if_missing_threadsafe(|| OutputId(next_output_id())); - if let Some(state) = shell.zoom_state.as_ref() { output.user_data().insert_if_missing_threadsafe(|| { Mutex::new(OutputZoomState::new( @@ -2310,7 +2288,7 @@ impl Shell { OverviewMode::Started(_, _) | OverviewMode::Active(_) ) { 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()); } @@ -2363,7 +2341,7 @@ impl Shell { } else { self.resize_mode = ResizeMode::Started(pattern, Instant::now(), direction); } - self.resize_indicator = Some(ResizeIndicator::new( + self.resize_indicator = Some(resize_indicator( direction, config, evlh, @@ -2490,12 +2468,6 @@ impl Shell { increment: zoom_config.increment, 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( @@ -2620,36 +2592,15 @@ impl Shell { pub fn remap_unfullscreened_window( &mut self, surface: CosmicSurface, - mut state: Option, + state: Option, loop_handle: &LoopHandle<'static, State>, ) -> CosmicMapped { - if let Some(FullscreenRestoreState::Stack { state: stack_state }) = &state { - if let Some(mapped) = self.mapped().find(|m| **m == stack_state.stack) - && let Some(stack) = mapped.stack_ref() - { - let idx = stack_state.idx.min(stack.len()); - stack.add_window(surface, Some(idx), None); - return mapped.clone(); - } else { - state = None; - } - } - - let window = if state.as_ref().is_some_and(|s| s.was_stack()) { - CosmicMapped::from(CosmicStack::new( - std::iter::once(surface), - loop_handle.clone(), - self.theme.clone(), - self.appearance_conf, - )) - } else { - CosmicMapped::from(CosmicWindow::new( - surface, - loop_handle.clone(), - self.theme.clone(), - self.appearance_conf, - )) - }; + let window = CosmicMapped::from(CosmicWindow::new( + surface, + loop_handle.clone(), + self.theme.clone(), + self.appearance_conf, + )); if let Some(FullscreenRestoreState::Sticky { output, state, .. }) = &state { let output = output @@ -2686,9 +2637,7 @@ impl Shell { workspace } None => self.workspaces.active_mut(&seat.active_output()).unwrap(), - Some(FullscreenRestoreState::Sticky { .. } | FullscreenRestoreState::Stack { .. }) => { - unreachable!() - } + Some(FullscreenRestoreState::Sticky { .. }) => unreachable!(), }; let fullscreen_geometry = workspace.output.geometry().to_local(&workspace.output); @@ -2803,9 +2752,7 @@ impl Shell { } } } - Some(FullscreenRestoreState::Sticky { .. } | FullscreenRestoreState::Stack { .. }) => { - unreachable!() - } + Some(FullscreenRestoreState::Sticky { .. }) => unreachable!(), } window @@ -2909,12 +2856,10 @@ impl Shell { let maybe_focused = workspace.focus_stack.get(&seat).iter().next().cloned(); if let Some(FocusTarget::Window(focused)) = maybe_focused - && let Some(stack) = focused.stack_ref() - && !is_dialog - && !should_be_maximized + && (focused.is_stack() && !is_dialog && !should_be_maximized) && !(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 { workspace_state.add_workspace_state(&workspace_handle, WState::Urgent); } @@ -3040,8 +2985,8 @@ impl Shell { .map(|idx| (idx, m.clone())) }); let surface = if let Some((idx, mapped)) = sticky_res { - if let Some(stack) = mapped.stack_ref() { - stack.remove_idx(idx) + if mapped.is_stack() { + mapped.stack_ref().unwrap().remove_idx(idx) } else { set.sticky_layer.unmap(&mapped, None); Some(mapped.active_window()) @@ -3051,13 +2996,20 @@ impl Shell { .iter() .position(|w| w.windows().any(|s| &s == surface)) { - if let Some(stack) = set + if set .minimized_windows - .get_mut(idx) + .get(idx) .unwrap() - .mapped_mut() - .and_then(|m| m.stack_ref()) + .mapped() + .is_some_and(CosmicMapped::is_stack) { + let window = set + .minimized_windows + .get_mut(idx) + .unwrap() + .mapped_mut() + .unwrap(); + let stack = window.stack_ref().unwrap(); let idx = stack.surfaces().position(|s| &s == surface); idx.and_then(|idx| stack.remove_idx(idx)) } else { @@ -3288,7 +3240,7 @@ impl Shell { toplevel_enter_workspace(window, to); // 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(); } // update fullscreen state to restore to the new workspace @@ -3304,7 +3256,6 @@ impl Shell { state: None, was_maximized: previous.was_maximized(), }, - was_stack: previous.was_stack(), }; } else if let FullscreenRestoreState::Tiling { workspace, .. } | FullscreenRestoreState::Floating { workspace, .. } = previous @@ -3316,7 +3267,7 @@ impl Shell { if is_minimized { let to_workspace = self.workspaces.space_for_handle_mut(to).unwrap(); // checked above let minimized_window = match window_state { - WorkspaceRestoreData::Floating(previous) => { + WorkspaceRestoreData::Floating(Some(previous)) => { let window = CosmicMapped::from(CosmicWindow::new( window.clone(), evlh.clone(), @@ -3326,7 +3277,7 @@ impl Shell { window.set_minimized(true); MinimizedWindow::Floating { window, previous } } - WorkspaceRestoreData::Tiling(previous) => { + WorkspaceRestoreData::Tiling(Some(previous)) => { let window = CosmicMapped::from(CosmicWindow::new( window.clone(), evlh.clone(), @@ -3393,7 +3344,7 @@ impl Shell { self.appearance_conf, )); let position = match window_state { - WorkspaceRestoreData::Floating(data) => Some( + WorkspaceRestoreData::Floating(Some(data)) => Some( data.position_relative(to_workspace.output.geometry().size.as_logical()), ), _ => None, @@ -3493,7 +3444,7 @@ impl Shell { let to_workspace = self.workspaces.space_for_handle_mut(to).unwrap(); // checked above if !to_workspace.tiling_enabled { let (position, was_maximized, was_snapped) = match &window_state { - WorkspaceRestoreData::Floating(data) => ( + WorkspaceRestoreData::Floating(Some(data)) => ( Some(data.position_relative(to_workspace.output.geometry().size.as_logical())), data.was_maximized, data.was_snapped, @@ -3693,8 +3644,6 @@ impl Shell { return None; }; - let mut theme = self.theme.clone(); - theme.transparent = theme.cosmic().frosted_windows; let grab = MenuGrab::new( GrabStartData::Pointer(start_data), seat, @@ -3703,7 +3652,7 @@ impl Shell { MenuAlignment::CORNER, None, evlh.clone(), - theme, + self.theme.clone(), ); Some((grab, Focus::Keep)) @@ -3878,13 +3827,13 @@ impl Shell { } (initial_window_location, layer, workspace.handle) - } else { - let sticky_layer = self - .workspaces - .sets - .get_mut(&cursor_output) - .filter(|set| set.sticky_layer.mapped().any(|m| m == &old_mapped)) - .map(|set| &mut set.sticky_layer)?; + } else if let Some(sticky_layer) = self + .workspaces + .sets + .get_mut(&cursor_output) + .filter(|set| set.sticky_layer.mapped().any(|m| m == &old_mapped)) + .map(|set| &mut set.sticky_layer) + { let elem_geo = sticky_layer.element_geometry(&old_mapped).unwrap(); let mut initial_window_location = elem_geo.loc.to_global(&cursor_output); @@ -3927,6 +3876,8 @@ impl Shell { ManagedLayer::Sticky, self.active_space(&cursor_output).unwrap().handle, ) + } else { + return None; }; toplevel_leave_workspace(&window, &workspace_handle); @@ -4220,13 +4171,14 @@ impl Shell { .unwrap() .to_global(&set.output); (&mut set.sticky_layer, geometry) - } else { - let workspace = self.space_for_mut(mapped)?; + } else if let Some(workspace) = self.space_for_mut(mapped) { let geometry = workspace .element_geometry(mapped) .unwrap() .to_global(workspace.output()); (&mut workspace.floating_layer, geometry) + } else { + return None; }; let new_loc = if edge.contains(ResizeEdge::LEFT) { @@ -4263,8 +4215,7 @@ impl Shell { ReleaseMode::Click, ) { grab.into() - } else { - let ws = self.space_for_mut(mapped)?; + } else if let Some(ws) = self.space_for_mut(mapped) { let node_id = mapped.tiling_node_id.lock().unwrap().clone()?; let (node, left_up_idx, orientation) = ws.tiling_layer.resize_request(node_id, edge)?; ResizeForkGrab::new( @@ -4277,6 +4228,8 @@ impl Shell { ReleaseMode::Click, ) .into() + } else { + return None; }; Some(((focus, new_loc), (grab, Focus::Keep))) @@ -4513,9 +4466,10 @@ impl Shell { .find(|set| set.sticky_layer.mapped().any(|m| m == &mapped)) { &mut set.sticky_layer - } else { - let workspace = self.space_for_mut(&mapped)?; + } else if let Some(workspace) = self.space_for_mut(&mapped) { &mut workspace.floating_layer + } else { + return None; }; let grab: ResizeGrab = if let Some(grab) = floating_layer.resize_request( @@ -4527,8 +4481,7 @@ impl Shell { ReleaseMode::NoMouseButtons, ) { grab.into() - } else { - let ws = self.space_for_mut(&mapped)?; + } else if let Some(ws) = self.space_for_mut(&mapped) { let node_id = mapped.tiling_node_id.lock().unwrap().clone()?; let (node, left_up_idx, orientation) = ws.tiling_layer.resize_request(node_id, edges)?; @@ -4542,6 +4495,8 @@ impl Shell { ReleaseMode::NoMouseButtons, ) .into() + } else { + return None; }; Some((grab, Focus::Clear)) @@ -4841,19 +4796,14 @@ impl Shell { { let mut from = set.sticky_layer.element_geometry(&mapped).unwrap(); let mut was_maximized = false; - let mut restore_state = None; - let was_stack = mapped.is_stack(); - window = if let Some(stack) = mapped.stack_ref() - && stack.len() > 1 + window = if mapped + .stack_ref() + .map(|stack| stack.len() > 1) + .unwrap_or(false) { - let idx = stack.surfaces().position(|s| &s == surface)?; - let surface = stack.remove_idx(idx)?; - restore_state = Some(FullscreenRestoreState::Stack { - state: StackRestoreData { - stack: mapped.key(), - idx, - }, - }); + let stack = mapped.stack_ref().unwrap(); + let surface = stack.surfaces().find(|s| s == surface).unwrap(); + stack.remove_window(&surface); surface } else { // Must be set before `map_internal`/`unmap` below, as both may call @@ -4886,7 +4836,7 @@ impl Shell { workspace.map_fullscreen( &window, &seat, - Some(restore_state.unwrap_or(FullscreenRestoreState::Sticky { + Some(FullscreenRestoreState::Sticky { output: old_output, state: FloatingRestoreData { geometry: from, @@ -4894,12 +4844,10 @@ impl Shell { was_maximized, was_snapped: None, }, - was_stack, - })), + }), Some(from), ); - } else { - let workspace = self.space_for_mut(&mapped)?; + } else if let Some(workspace) = self.space_for_mut(&mapped) { if mapped.is_minimized() { // TODO: Rewrite the `MinimizedWindow` to restore to fullscreen return None; @@ -4927,26 +4875,23 @@ impl Shell { &seat, match state { WorkspaceRestoreData::Floating(floating_state) => { - Some(FullscreenRestoreState::Floating { + floating_state.map(|state| FullscreenRestoreState::Floating { workspace: handle, - state: floating_state, - was_stack: mapped.is_stack(), + state, }) } WorkspaceRestoreData::Tiling(tiling_state) => { - Some(FullscreenRestoreState::Tiling { + tiling_state.map(|state| FullscreenRestoreState::Tiling { workspace: handle, - state: tiling_state, - was_stack: mapped.is_stack(), + state, }) } - WorkspaceRestoreData::Stack(stack_state) => { - Some(FullscreenRestoreState::Stack { state: stack_state }) - } WorkspaceRestoreData::Fullscreen(_) => unreachable!(), }, Some(from), ); + } else { + return None; }; Some(KeyboardFocusTarget::Fullscreen(window)) @@ -5058,14 +5003,13 @@ impl Shell { let map = smithay::desktop::layer_map_for_output(output); for layer_surface in map.layers() { - let namespace = self.workspaces.active_num(output).1; layer_surface.take_presentation_feedback( &mut output_presentation_feedback, surface_primary_scanout_output, |surface, _| { surface_presentation_feedback_flags_from_states( surface, - Some(namespace), + None, render_element_states, ) }, diff --git a/src/shell/seats.rs b/src/shell/seats.rs index 11211e90..5273e97f 100644 --- a/src/shell/seats.rs +++ b/src/shell/seats.rs @@ -243,12 +243,6 @@ pub fn create_seat( seat } -#[derive(Debug, Copy, Clone)] -pub struct CursorGeometry { - pub geometry: Rectangle, - pub hotspot: Point, -} - pub trait SeatExt { fn id(&self) -> usize; @@ -272,7 +266,7 @@ pub trait SeatExt { &self, loc: impl Into>, time: Time, - ) -> Option; + ) -> Option<(Rectangle, Point)>; fn cursor_image_status(&self) -> CursorImageStatus; fn set_cursor_image_status(&self, status: CursorImageStatus); } @@ -373,7 +367,7 @@ impl SeatExt for Seat { &self, loc: impl Into>, time: Time, - ) -> Option { + ) -> Option<(Rectangle, Point)> { let location = loc.into().to_i32_round(); match self.cursor_image_status() { @@ -392,10 +386,7 @@ impl SeatExt for Seat { (geo.loc.x, geo.loc.y).into(), geo.size.to_buffer(1, Transform::Normal), ); - Some(CursorGeometry { - geometry: buffer_geo, - hotspot: (hotspot.x, hotspot.y).into(), - }) + Some((buffer_geo, (hotspot.x, hotspot.y).into())) } CursorImageStatus::Named(cursor_icon) => { let seat_userdata = self.user_data(); @@ -407,13 +398,10 @@ impl SeatExt for Seat { .get_named_cursor(cursor_icon) .get_image(1, time.as_millis()); - Some(CursorGeometry { - geometry: Rectangle::new( - location, - (frame.width as i32, frame.height as i32).into(), - ), - hotspot: (frame.xhot as i32, frame.yhot as i32).into(), - }) + Some(( + Rectangle::new(location, (frame.width as i32, frame.height as i32).into()), + (frame.xhot as i32, frame.yhot as i32).into(), + )) } CursorImageStatus::Hidden => None, } diff --git a/src/shell/workspace.rs b/src/shell/workspace.rs index 84a020ff..2b4db98c 100644 --- a/src/shell/workspace.rs +++ b/src/shell/workspace.rs @@ -1,9 +1,11 @@ -use crate::backend::render::wayland::SurfaceRenderElement; use crate::shell::focus::FocusTarget; use crate::shell::layout::tiling::RestoreTilingState; use crate::wayland::handlers::xdg_activation::ActivationContext; use crate::{ - backend::render::{BackdropShader, element::AsGlowRenderer}, + backend::render::{ + BackdropShader, + element::{AsGlowRenderer, FromGlesError}, + }, shell::{ ANIMATION_DURATION, OverviewMode, SeatMoveGrabState, layout::{ @@ -29,15 +31,14 @@ use cosmic_protocols::workspace::v2::server::zcosmic_workspace_handle_v2::Tiling use id_tree::Tree; use indexmap::IndexSet; use keyframe::{ease, functions::EaseInOutCubic}; -use smallvec::SmallVec; -use smithay::backend::drm::DrmNode; -use smithay::backend::renderer::element::{Kind, NamespacedElement}; +use smithay::backend::renderer::element::Kind; use smithay::output::WeakOutput; use smithay::utils::user_data::UserDataMap; use smithay::{ backend::renderer::{ element::{ - Element, Id, RenderElement, texture::TextureRenderElement, utils::RescaleRenderElement, + Element, Id, RenderElement, surface::WaylandSurfaceRenderElement, + texture::TextureRenderElement, utils::RescaleRenderElement, }, gles::GlesTexture, glow::GlowRenderer, @@ -60,7 +61,7 @@ use wayland_backend::server::ClientId; use super::{ CosmicMappedRenderElement, CosmicSurface, ResizeDirection, ResizeMode, element::{ - CosmicMapped, CosmicMappedKey, MaximizedState, resize_indicator::ResizeIndicator, + CosmicMapped, MaximizedState, resize_indicator::ResizeIndicator, stack::CosmicStackRenderElement, swap_indicator::SwapIndicator, window::CosmicWindowRenderElement, }, @@ -252,20 +253,14 @@ pub enum FullscreenRestoreState { Tiling { workspace: WorkspaceHandle, state: TilingRestoreData, - was_stack: bool, }, Floating { workspace: WorkspaceHandle, state: FloatingRestoreData, - was_stack: bool, }, Sticky { output: WeakOutput, state: FloatingRestoreData, - was_stack: bool, - }, - Stack { - state: StackRestoreData, }, } @@ -275,18 +270,6 @@ impl FullscreenRestoreState { FullscreenRestoreState::Floating { state, .. } | FullscreenRestoreState::Sticky { 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)] pub enum WorkspaceRestoreData { Fullscreen(Option), - Tiling(TilingRestoreData), - Floating(FloatingRestoreData), - Stack(StackRestoreData), + Tiling(Option), + Floating(Option), +} + +impl From for WorkspaceRestoreData { + fn from(value: ManagedLayer) -> Self { + match value { + ManagedLayer::Floating | ManagedLayer::Sticky => WorkspaceRestoreData::Floating(None), + ManagedLayer::Tiling => WorkspaceRestoreData::Tiling(None), + ManagedLayer::Fullscreen => WorkspaceRestoreData::Fullscreen(None), + } + } } #[derive(Debug, Clone)] @@ -328,12 +320,6 @@ pub struct TilingRestoreData { pub was_maximized: bool, } -#[derive(Debug, Clone)] -pub struct StackRestoreData { - pub stack: CosmicMappedKey, - pub idx: usize, -} - #[derive(Debug, Clone)] pub struct FullscreenRestoreData { pub previous_state: FullscreenRestoreState, @@ -655,30 +641,32 @@ impl Workspace { mapped.set_minimized(false); return Some(match state { 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!(), }); } if let Ok(state) = self.tiling_layer.unmap(mapped, None) { - return Some(WorkspaceRestoreData::Tiling(TilingRestoreData { + return Some(WorkspaceRestoreData::Tiling(Some(TilingRestoreData { state, was_maximized: was_maximized.is_some(), - })); + }))); } let was_snapped = *mapped.floating_tiled.lock().unwrap(); // unmaximize_request might have triggered a `floating_layer.refresh()`, // which may have already removed a non-alive surface. if let Some(floating_geometry) = self.floating_layer.unmap(mapped, None).or(was_maximized) { - return Some(WorkspaceRestoreData::Floating(FloatingRestoreData { + return Some(WorkspaceRestoreData::Floating(Some(FloatingRestoreData { geometry: floating_geometry, output_size: self.output.geometry().size.as_logical(), was_maximized: was_maximized.is_some(), was_snapped, - })); + }))); }; None @@ -724,17 +712,19 @@ impl Workspace { } 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 { - let idx = stack.surfaces().position(|s| &s == surface)?; - return Some(( - stack.remove_idx(idx)?, - WorkspaceRestoreData::Stack(StackRestoreData { - stack: mapped.key(), - idx, - }), - )); + let idx = stack.surfaces().position(|s| &s == surface); + let layer = if self.is_tiled(surface) { + ManagedLayer::Tiling + } else { + ManagedLayer::Floating + }; + return idx + .and_then(|idx| stack.remove_idx(idx)) + .map(|s| (s, layer.into())); } // we know mapped is no stack with more than one element now, @@ -785,7 +775,7 @@ impl Workspace { location: Point, seat: &Seat, ) -> Option { - if !self.output.geometry().contains(location.to_i32_floor()) { + if !self.output.geometry().contains(location.to_i32_round()) { return None; } let location = location.to_local(&self.output); @@ -834,7 +824,7 @@ impl Workspace { location: Point, seat: &Seat, ) -> Option { - if !self.output.geometry().contains(location.to_i32_floor()) { + if !self.output.geometry().contains(location.to_i32_round()) { return None; } let location = location.to_local(&self.output); @@ -884,7 +874,7 @@ impl Workspace { overview: OverviewMode, seat: &Seat, ) -> Option<(PointerFocusTarget, Point)> { - if !self.output.geometry().contains(location.to_i32_floor()) { + if !self.output.geometry().contains(location.to_i32_round()) { return None; } let location = location.to_local(&self.output); @@ -934,7 +924,7 @@ impl Workspace { overview: OverviewMode, seat: &Seat, ) -> Option<(PointerFocusTarget, Point)> { - if !self.output.geometry().contains(location.to_i32_floor()) { + if !self.output.geometry().contains(location.to_i32_round()) { return None; } let location = location.to_local(&self.output); @@ -1622,9 +1612,8 @@ impl Workspace { resize_indicator: Option<(ResizeMode, ResizeIndicator)>, indicator_thickness: u8, theme: &CosmicTheme, - scanout_node: Option, - push: &mut dyn FnMut(WorkspaceRenderElement), - ) where + ) -> Result>, OutputNotMapped> + where R: AsGlowRenderer, R::TextureId: Send + Clone + 'static, CosmicMappedRenderElement: RenderElement, @@ -1632,109 +1621,108 @@ impl Workspace { CosmicStackRenderElement: RenderElement, WorkspaceRenderElement: RenderElement, { + let mut elements = Vec::default(); + let output_scale = self.output.current_scale().fractional_scale(); let zone = { let layer_map = layer_map_for_output(&self.output); layer_map.non_exclusive_zone().as_local() }; let focused = self.focus_stack.get(last_active_seat).last().cloned(); - let fullscreen_focused = matches!(focused, Some(FocusTarget::Fullscreen(_))); - let mut fullscreen_elements = SmallVec::<[WorkspaceRenderElement; 2]>::new_const(); - let mut render_fullscreen = - |fullscreen: &FullscreenSurface, renderer: &mut R, output_scale: f64| { - let fullscreen_geo = self.fullscreen_geometry_for(fullscreen); - let previous_geo = fullscreen - .previous_geometry - .as_ref() - .unwrap_or(&fullscreen_geo); + let render_fullscreen = |fullscreen: &FullscreenSurface, + renderer: &mut R, + output_scale: f64| + -> Vec> { + let fullscreen_geo = self.fullscreen_geometry_for(fullscreen); + let previous_geo = fullscreen + .previous_geometry + .as_ref() + .unwrap_or(&fullscreen_geo); - let (target_geo, alpha) = match (fullscreen.start_at, fullscreen.ended_at) { - (Some(started), _) => { - let duration = Instant::now().duration_since(started).as_secs_f64() - / FULLSCREEN_ANIMATION_DURATION.as_secs_f64(); - ( - ease( - EaseInOutCubic, - EaseRectangle(*previous_geo), - EaseRectangle(fullscreen_geo), - duration, - ) - .0, - ease(EaseInOutCubic, 0.0, 1.0, duration), + let (target_geo, alpha) = match (fullscreen.start_at, fullscreen.ended_at) { + (Some(started), _) => { + let duration = Instant::now().duration_since(started).as_secs_f64() + / FULLSCREEN_ANIMATION_DURATION.as_secs_f64(); + ( + ease( + EaseInOutCubic, + EaseRectangle(*previous_geo), + EaseRectangle(fullscreen_geo), + duration, ) - } - (_, Some(ended)) => { - let duration = Instant::now().duration_since(ended).as_secs_f64() - / FULLSCREEN_ANIMATION_DURATION.as_secs_f64(); - ( - ease( - EaseInOutCubic, - EaseRectangle(fullscreen_geo), - EaseRectangle(*previous_geo), - duration, - ) - .0, - ease(EaseInOutCubic, 1.0, 0.0, duration), + .0, + ease(EaseInOutCubic, 0.0, 1.0, duration), + ) + } + (_, Some(ended)) => { + let duration = Instant::now().duration_since(ended).as_secs_f64() + / FULLSCREEN_ANIMATION_DURATION.as_secs_f64(); + ( + ease( + EaseInOutCubic, + EaseRectangle(fullscreen_geo), + EaseRectangle(*previous_geo), + duration, ) - } - (None, None) => (fullscreen_geo, 1.0), - }; + .0, + ease(EaseInOutCubic, 1.0, 0.0, duration), + ) + } + (None, None) => (fullscreen_geo, 1.0), + }; - let render_loc = target_geo - .loc - .as_logical() - .to_physical_precise_round(output_scale); + let render_loc = target_geo + .loc + .as_logical() + .to_physical_precise_round(output_scale); - let fullscreen_geo = fullscreen.surface.0.geometry(); - let scale = Scale { - x: target_geo.size.w as f64 / fullscreen_geo.size.w as f64, - y: target_geo.size.h as f64 / fullscreen_geo.size.h as f64, - }; + // Only rescale geometry when animating + let animation_rescale = |elem| { + if fullscreen.is_animating() { + let fullscreen_geo = fullscreen.surface.0.geometry(); + let scale = Scale { + x: target_geo.size.w as f64 / fullscreen_geo.size.w as f64, + y: target_geo.size.h as f64 / fullscreen_geo.size.h as f64, + }; - // Only rescale geometry when animating - let animation_rescale = |elem| { - if fullscreen.is_animating() { - RescaleRenderElement::from_element(elem, render_loc, scale).into() - } else { - Into::>::into(elem) - } - }; + RescaleRenderElement::from_element(elem, render_loc, scale).into() + } else { + Into::>::into(elem) + } + }; - let mut fullscreen_push = |elem: SurfaceRenderElement| { - if fullscreen_focused { - push(animation_rescale(elem.into())) - } else { - fullscreen_elements.push(animation_rescale(elem.into())) - } - }; - fullscreen.surface.push_render_elements( + fullscreen + .surface + .render_elements::>( renderer, render_loc, output_scale.into(), alpha, Some(true), - scanout_node, - false, - [0, 0, 0, 0], - 0, - &mut fullscreen_push, - None, - ); - }; + ) + .into_iter() + .map(animation_rescale) + .collect::>() + }; let top_fullscreen = self.get_fullscreen(last_active_seat); + let mut fullscreen_elements: Vec> = Vec::new(); 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 for fs in self.fullscreen_surfaces.iter().filter(|f| f.is_animating()) { 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 .fullscreen_surfaces .iter() @@ -1765,21 +1753,28 @@ impl Workspace { OverviewMode::None => 1.0, }; - self.floating_layer.render( - renderer, - focused.as_ref().and_then(|target| { - if let FocusTarget::Window(mapped) = target { - Some(mapped) - } else { - None - } - }), - resize_indicator.clone(), - indicator_thickness, - alpha, - theme, - scanout_node, - &mut |elem| push(elem.into()), + elements.extend( + self.floating_layer + .render::( + renderer, + render_focus + .then(|| { + focused.as_ref().and_then(|target| { + if let FocusTarget::Window(mapped) = target { + Some(mapped) + } else { + None + } + }) + }) + .flatten(), + resize_indicator.clone(), + indicator_thickness, + alpha, + theme, + ) + .into_iter() + .map(WorkspaceRenderElement::from), ); let alpha = match &overview.0 { @@ -1796,31 +1791,23 @@ impl Workspace { }; //tiling surfaces - self.tiling_layer.render( - renderer, - render_focus.then_some(last_active_seat), - render_focus - .then(|| { - focused.as_ref().and_then(|target| { - if let FocusTarget::Window(mapped) = target { - Some(mapped) - } else { - None - } - }) - }) - .flatten(), - zone, - overview, - resize_indicator, - indicator_thickness, - theme, - scanout_node, - &mut |elem| push(elem.into()), + elements.extend( + self.tiling_layer + .render::( + renderer, + render_focus.then_some(last_active_seat), + zone, + overview, + resize_indicator, + indicator_thickness, + theme, + )? + .into_iter() + .map(WorkspaceRenderElement::from), ); if let Some(alpha) = alpha { - push( + elements.push( Into::>::into(BackdropShader::element( renderer, self.backdrop_id.clone(), @@ -1834,9 +1821,11 @@ impl Workspace { } } - for elem in fullscreen_elements { - push(elem); + if !matches!(focused, Some(FocusTarget::Fullscreen(_))) { + elements.extend(fullscreen_elements.into_iter()); } + + Ok(elements) } #[profiling::function] @@ -1847,9 +1836,8 @@ impl Workspace { render_focus: bool, overview: (OverviewMode, Option<(SwapIndicator, Option<&Tree>)>), theme: &CosmicTheme, - scanout_node: Option, - push: &mut dyn FnMut(WorkspaceRenderElement), - ) where + ) -> Result>, OutputNotMapped> + where R: AsGlowRenderer, R::TextureId: Send + Clone + 'static, CosmicMappedRenderElement: RenderElement, @@ -1857,6 +1845,8 @@ impl Workspace { CosmicStackRenderElement: RenderElement, WorkspaceRenderElement: RenderElement, { + let mut elements = Vec::default(); + let output_scale = self.output.current_scale().fractional_scale(); let zone = { let layer_map = layer_map_for_output(&self.output); @@ -1911,14 +1901,17 @@ impl Workspace { .as_logical() .to_physical_precise_round(output_scale); - fullscreen.surface.push_popup_render_elements( - renderer, - render_loc, - output_scale.into(), - alpha, - scanout_node, - 0, - &mut |elem| push(WorkspaceRenderElement::FullscreenPopup(elem.into())), + elements.extend( + fullscreen + .surface + .popup_render_elements::>( + renderer, + render_loc, + output_scale.into(), + alpha, + ) + .into_iter() + .map(Into::into), ); } @@ -1952,20 +1945,29 @@ impl Workspace { OverviewMode::None => 1.0, }; - self.floating_layer - .render_popups(renderer, alpha, scanout_node, &mut |elem| push(elem.into())); + elements.extend( + self.floating_layer + .render_popups::(renderer, alpha) + .into_iter() + .map(WorkspaceRenderElement::from), + ); //tiling surfaces - self.tiling_layer.render_popups( - renderer, - render_focus.then_some(last_active_seat), - zone, - overview, - theme, - scanout_node, - &mut |elem| push(elem.into()), + elements.extend( + self.tiling_layer + .render_popups::( + renderer, + render_focus.then_some(last_active_seat), + zone, + overview, + theme, + )? + .into_iter() + .map(WorkspaceRenderElement::from), ); } + + Ok(elements) } } @@ -1984,10 +1986,9 @@ pub struct OutputNotMapped; pub enum WorkspaceRenderElement where R: AsGlowRenderer, - R::TextureId: Send + 'static, + R::TextureId: 'static, { - OverrideRedirect(SurfaceRenderElement), - LowerLayerShell(NamespacedElement>), + OverrideRedirect(WaylandSurfaceRenderElement), Fullscreen(RescaleRenderElement>), FullscreenPopup(CosmicWindowRenderElement), Window(CosmicMappedRenderElement), @@ -1997,12 +1998,11 @@ where impl Element for WorkspaceRenderElement where R: AsGlowRenderer, - R::TextureId: Send + 'static, + R::TextureId: 'static, { fn id(&self) -> &smithay::backend::renderer::element::Id { match self { WorkspaceRenderElement::OverrideRedirect(elem) => elem.id(), - WorkspaceRenderElement::LowerLayerShell(elem) => elem.id(), WorkspaceRenderElement::Fullscreen(elem) => elem.id(), WorkspaceRenderElement::FullscreenPopup(elem) => elem.id(), WorkspaceRenderElement::Window(elem) => elem.id(), @@ -2013,7 +2013,6 @@ where fn current_commit(&self) -> smithay::backend::renderer::utils::CommitCounter { match self { WorkspaceRenderElement::OverrideRedirect(elem) => elem.current_commit(), - WorkspaceRenderElement::LowerLayerShell(elem) => elem.current_commit(), WorkspaceRenderElement::Fullscreen(elem) => elem.current_commit(), WorkspaceRenderElement::FullscreenPopup(elem) => elem.current_commit(), WorkspaceRenderElement::Window(elem) => elem.current_commit(), @@ -2024,7 +2023,6 @@ where fn src(&self) -> Rectangle { match self { WorkspaceRenderElement::OverrideRedirect(elem) => elem.src(), - WorkspaceRenderElement::LowerLayerShell(elem) => elem.src(), WorkspaceRenderElement::Fullscreen(elem) => elem.src(), WorkspaceRenderElement::FullscreenPopup(elem) => elem.src(), WorkspaceRenderElement::Window(elem) => elem.src(), @@ -2035,7 +2033,6 @@ where fn geometry(&self, scale: Scale) -> Rectangle { match self { WorkspaceRenderElement::OverrideRedirect(elem) => elem.geometry(scale), - WorkspaceRenderElement::LowerLayerShell(elem) => elem.geometry(scale), WorkspaceRenderElement::Fullscreen(elem) => elem.geometry(scale), WorkspaceRenderElement::FullscreenPopup(elem) => elem.geometry(scale), WorkspaceRenderElement::Window(elem) => elem.geometry(scale), @@ -2046,7 +2043,6 @@ where fn location(&self, scale: Scale) -> Point { match self { WorkspaceRenderElement::OverrideRedirect(elem) => elem.location(scale), - WorkspaceRenderElement::LowerLayerShell(elem) => elem.location(scale), WorkspaceRenderElement::Fullscreen(elem) => elem.location(scale), WorkspaceRenderElement::FullscreenPopup(elem) => elem.location(scale), WorkspaceRenderElement::Window(elem) => elem.location(scale), @@ -2057,7 +2053,6 @@ where fn transform(&self) -> smithay::utils::Transform { match self { WorkspaceRenderElement::OverrideRedirect(elem) => elem.transform(), - WorkspaceRenderElement::LowerLayerShell(elem) => elem.transform(), WorkspaceRenderElement::Fullscreen(elem) => elem.transform(), WorkspaceRenderElement::FullscreenPopup(elem) => elem.transform(), WorkspaceRenderElement::Window(elem) => elem.transform(), @@ -2072,7 +2067,6 @@ where ) -> DamageSet { match self { WorkspaceRenderElement::OverrideRedirect(elem) => elem.damage_since(scale, commit), - WorkspaceRenderElement::LowerLayerShell(elem) => elem.damage_since(scale, commit), WorkspaceRenderElement::Fullscreen(elem) => elem.damage_since(scale, commit), WorkspaceRenderElement::FullscreenPopup(elem) => elem.damage_since(scale, commit), WorkspaceRenderElement::Window(elem) => elem.damage_since(scale, commit), @@ -2083,7 +2077,6 @@ where fn opaque_regions(&self, scale: Scale) -> OpaqueRegions { match self { WorkspaceRenderElement::OverrideRedirect(elem) => elem.opaque_regions(scale), - WorkspaceRenderElement::LowerLayerShell(elem) => elem.opaque_regions(scale), WorkspaceRenderElement::Fullscreen(elem) => elem.opaque_regions(scale), WorkspaceRenderElement::FullscreenPopup(elem) => elem.opaque_regions(scale), WorkspaceRenderElement::Window(elem) => elem.opaque_regions(scale), @@ -2094,7 +2087,6 @@ where fn alpha(&self) -> f32 { match self { WorkspaceRenderElement::OverrideRedirect(elem) => elem.alpha(), - WorkspaceRenderElement::LowerLayerShell(elem) => elem.alpha(), WorkspaceRenderElement::Fullscreen(elem) => elem.alpha(), WorkspaceRenderElement::FullscreenPopup(elem) => elem.alpha(), WorkspaceRenderElement::Window(elem) => elem.alpha(), @@ -2105,7 +2097,6 @@ where fn kind(&self) -> Kind { match self { WorkspaceRenderElement::OverrideRedirect(elem) => elem.kind(), - WorkspaceRenderElement::LowerLayerShell(elem) => elem.kind(), WorkspaceRenderElement::Fullscreen(elem) => elem.kind(), WorkspaceRenderElement::FullscreenPopup(elem) => elem.kind(), WorkspaceRenderElement::Window(elem) => elem.kind(), @@ -2116,7 +2107,6 @@ where fn is_framebuffer_effect(&self) -> bool { match self { WorkspaceRenderElement::OverrideRedirect(elem) => elem.is_framebuffer_effect(), - WorkspaceRenderElement::LowerLayerShell(elem) => elem.is_framebuffer_effect(), WorkspaceRenderElement::Fullscreen(elem) => elem.is_framebuffer_effect(), WorkspaceRenderElement::FullscreenPopup(elem) => elem.is_framebuffer_effect(), WorkspaceRenderElement::Window(elem) => elem.is_framebuffer_effect(), @@ -2128,7 +2118,8 @@ where impl RenderElement for WorkspaceRenderElement where R: AsGlowRenderer, - R::TextureId: Send + 'static, + R::TextureId: 'static, + R::Error: FromGlesError, { fn draw( &self, @@ -2143,9 +2134,6 @@ where WorkspaceRenderElement::OverrideRedirect(elem) => { elem.draw(frame, src, dst, damage, opaque_regions, cache) } - WorkspaceRenderElement::LowerLayerShell(elem) => { - elem.draw(frame, src, dst, damage, opaque_regions, cache) - } WorkspaceRenderElement::Fullscreen(elem) => { elem.draw(frame, src, dst, damage, opaque_regions, cache) } @@ -2164,7 +2152,7 @@ where opaque_regions, cache, ) - .map_err(R::from_gles_error), + .map_err(FromGlesError::from_gles_error), } } @@ -2174,7 +2162,6 @@ where ) -> Option> { match self { WorkspaceRenderElement::OverrideRedirect(elem) => elem.underlying_storage(renderer), - WorkspaceRenderElement::LowerLayerShell(elem) => elem.underlying_storage(renderer), WorkspaceRenderElement::Fullscreen(elem) => elem.underlying_storage(renderer), WorkspaceRenderElement::FullscreenPopup(elem) => elem.underlying_storage(renderer), WorkspaceRenderElement::Window(elem) => elem.underlying_storage(renderer), @@ -2195,9 +2182,6 @@ where WorkspaceRenderElement::OverrideRedirect(elem) => { elem.capture_framebuffer(frame, src, dst, cache) } - WorkspaceRenderElement::LowerLayerShell(elem) => { - elem.capture_framebuffer(frame, src, dst, cache) - } WorkspaceRenderElement::Fullscreen(elem) => { elem.capture_framebuffer(frame, src, dst, cache) } @@ -2215,7 +2199,7 @@ where dst, cache, ) - .map_err(R::from_gles_error) + .map_err(FromGlesError::from_gles_error) } } } @@ -2224,7 +2208,7 @@ where impl From>> for WorkspaceRenderElement where R: AsGlowRenderer, - R::TextureId: Send + 'static, + R::TextureId: 'static, CosmicMappedRenderElement: RenderElement, { fn from(elem: RescaleRenderElement>) -> Self { @@ -2235,7 +2219,7 @@ where impl From> for WorkspaceRenderElement where R: AsGlowRenderer, - R::TextureId: Send + 'static, + R::TextureId: 'static, CosmicMappedRenderElement: RenderElement, { fn from(elem: CosmicWindowRenderElement) -> Self { @@ -2243,32 +2227,21 @@ where } } -impl From> for WorkspaceRenderElement +impl From> for WorkspaceRenderElement where R: AsGlowRenderer, - R::TextureId: Send + 'static, + R::TextureId: 'static, CosmicMappedRenderElement: RenderElement, { - fn from(elem: SurfaceRenderElement) -> Self { + fn from(elem: WaylandSurfaceRenderElement) -> Self { WorkspaceRenderElement::OverrideRedirect(elem) } } -impl From>> for WorkspaceRenderElement -where - R: AsGlowRenderer, - R::TextureId: Send + 'static, - CosmicMappedRenderElement: RenderElement, -{ - fn from(elem: NamespacedElement>) -> Self { - WorkspaceRenderElement::LowerLayerShell(elem) - } -} - impl From> for WorkspaceRenderElement where R: AsGlowRenderer, - R::TextureId: Send + 'static, + R::TextureId: 'static, CosmicMappedRenderElement: RenderElement, { fn from(elem: CosmicMappedRenderElement) -> Self { @@ -2279,7 +2252,7 @@ where impl From> for WorkspaceRenderElement where R: AsGlowRenderer, - R::TextureId: Send + 'static, + R::TextureId: 'static, CosmicMappedRenderElement: RenderElement, { fn from(elem: TextureRenderElement) -> Self { diff --git a/src/shell/zoom.rs b/src/shell/zoom.rs index 8363510a..f72ad8e8 100644 --- a/src/shell/zoom.rs +++ b/src/shell/zoom.rs @@ -3,15 +3,15 @@ use std::{sync::Mutex, time::Instant}; use calloop::LoopHandle; use cosmic::{ Apply, - iced::{Alignment, Background, Border, Length, alignment::Vertical, widget as iced_widget}, - theme, + iced::{Alignment, Background, Border, Length, alignment::Vertical}, + iced_widget, theme, widget::{self, icon::Named}, }; use cosmic_comp_config::ZoomMovement; use cosmic_config::ConfigSet; use keyframe::{ease, functions::Linear}; use smithay::{ - backend::renderer::ImportMem, + backend::renderer::{ImportMem, Renderer, element::AsRenderElements}, desktop::space::SpaceElement, input::{ Seat, @@ -27,15 +27,14 @@ use smithay::{ }, }, output::Output, - utils::{FrameExtents, IsAlive, Point, Rectangle, Serial, Size}, + utils::{IsAlive, Point, Rectangle, Serial, Size}, }; use tracing::error; use crate::{ - backend::render::element::AsGlowRenderer, state::State, utils::{ - iced::{IcedElement, IcedRenderElement, Program}, + iced::{IcedElement, Program}, prelude::*, tween::EasePoint, }, @@ -72,17 +71,45 @@ impl OutputZoomState { increment: u32, movement: ZoomMovement, loop_handle: LoopHandle<'static, State>, - mut theme: cosmic::Theme, + theme: cosmic::Theme, ) -> OutputZoomState { - theme.transparent = theme.cosmic().frosted_system_interface; let cursor_position = seat.get_pointer().unwrap().current_location().as_global(); - let focal_point = cursor_position.to_local(output); let output_geometry = output.geometry().to_f64(); + let focal_point = if output_geometry.contains(cursor_position) { + match movement { + ZoomMovement::Continuously | ZoomMovement::OnEdge => { + cursor_position.to_local(output) + } + ZoomMovement::Centered => { + let mut zoomed_output_geometry = output.geometry().to_f64().downscale(level); + zoomed_output_geometry.loc = + cursor_position - zoomed_output_geometry.size.downscale(2.).to_point(); + + let mut focal_point = zoomed_output_geometry + .loc + .to_local(output) + .upscale(level) + .to_global(output); + focal_point.x = focal_point.x.clamp( + output_geometry.loc.x, + (output_geometry.loc.x + output_geometry.size.w).next_down(), + ); + focal_point.y = focal_point.y.clamp( + output_geometry.loc.y, + (output_geometry.loc.y + output_geometry.size.h).next_down(), + ); + focal_point.to_local(output) + } + } + } else { + (output_geometry.size.w / 2., output_geometry.size.h / 2.).into() + }; let program = ZoomProgram::new(level, movement, increment); let element = IcedElement::new(program, Size::default(), loop_handle, theme); 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.resize(size); element.output_enter(output, Rectangle::new(Point::from((0, 0)), size)); @@ -165,13 +192,10 @@ impl OutputZoomState { }); } - fn render( - &mut self, - renderer: &mut R, - output: &Output, - push: &mut dyn FnMut(IcedRenderElement), - ) where - R: AsGlowRenderer + ImportMem, + fn render(&mut self, renderer: &mut R, output: &Output) -> Vec + where + C: From< as AsRenderElements>::RenderElement>, + R: Renderer + ImportMem, R::TextureId: Send + Clone + 'static, { let size = self.element.current_size().to_f64(); @@ -184,17 +208,8 @@ impl OutputZoomState { .to_physical(scale.fractional_scale()) .to_i32_round(); - self.element.push_render_elements( - renderer, - location, - scale.fractional_scale().into(), - 1.0, - self.element - .with_theme(|theme| theme.cosmic().radius_s()) - .map(|x| x.round() as u8), - push, - None, - ) + self.element + .render_elements(renderer, location, scale.fractional_scale().into(), 1.0) } } @@ -241,101 +256,86 @@ impl ZoomState { &mut self, output: &Output, cursor_position: Point, - _original_position: Point, + original_position: Point, movement: ZoomMovement, ) { - let output_geometry = output.geometry().to_f64().to_local(output); - let zoomed_output_geometry = output.zoomed_geometry().unwrap().to_f64().to_local(output); + let output_geometry = output.geometry().to_f64(); + let mut zoomed_output_geometry = output.zoomed_geometry().unwrap().to_f64(); let output_state = output.user_data().get::>().unwrap(); let mut output_state_ref = output_state.lock().unwrap(); - let level = output_state_ref.current_level(); - - let is_level_change = output_state_ref - .previous_level - .is_some_and(|prev| prev.0 != level); - - // animate level and movement type changes - if is_level_change || self.movement != movement { + // animate movement type changes + if self.movement != movement { output_state_ref.previous_point = Some((output_state_ref.focal_point, Instant::now())); self.movement = movement; } let cursor_position = cursor_position.to_local(output); match movement { - ZoomMovement::Continuously => { - // Focal point is the cursor position - output_state_ref.focal_point = cursor_position; - } + ZoomMovement::Continuously => output_state_ref.focal_point = cursor_position, ZoomMovement::OnEdge => { - if is_level_change { - // Ensure the cursor doesn't disappear off screen - output_state_ref.focal_point = cursor_position; - return; + if !zoomed_output_geometry + .overlaps_or_touches(Rectangle::new(original_position, Size::from((16., 16.)))) + { + zoomed_output_geometry.loc = cursor_position.to_global(output) + - zoomed_output_geometry.size.downscale(2.).to_point(); + let mut focal_point = zoomed_output_geometry + .loc + .to_local(output) + .upscale(output_state_ref.level) + .to_global(output); + focal_point.x = focal_point.x.clamp( + output_geometry.loc.x, + output_geometry.loc.x + output_geometry.size.w - 1., + ); + focal_point.y = focal_point.y.clamp( + output_geometry.loc.y, + output_geometry.loc.y + output_geometry.size.h - 1., + ); + output_state_ref.previous_point = + Some((output_state_ref.focal_point, Instant::now())); + output_state_ref.focal_point = focal_point.to_local(output); + } else if !zoomed_output_geometry.contains(cursor_position.to_global(output)) { + let mut diff = output_state_ref.focal_point.to_global(output) + + (cursor_position.to_global(output) - original_position) + .upscale(output_state_ref.level); + diff.x = diff.x.clamp( + output_geometry.loc.x, + (output_geometry.loc.x + output_geometry.size.w).next_down(), + ); + diff.y = diff.y.clamp( + output_geometry.loc.y, + (output_geometry.loc.y + output_geometry.size.h).next_down(), + ); + diff -= output_state_ref.focal_point.to_global(output); + + output_state_ref.focal_point += diff.as_logical().as_local(); } + } + ZoomMovement::Centered => { + zoomed_output_geometry.loc = cursor_position.to_global(output) + - zoomed_output_geometry.size.downscale(2.).to_point(); - // Compute small margin relative to zoomed output to keep cursor within - // (can be user-configurable in the future) - let margin_size = zoomed_output_geometry.size.h * 0.02; - let margins = FrameExtents::new(margin_size, margin_size, margin_size, margin_size); - let inner_rect = zoomed_output_geometry - margins; - - 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 + let mut focal_point = zoomed_output_geometry + .loc + .to_local(output) + .upscale( + (output_geometry.size.w + / (output_geometry.size.w - zoomed_output_geometry.size.w) + .max(f64::EPSILON)) + .max(1.), + ) + .to_global(output); focal_point.x = focal_point.x.clamp( 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( 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.focal_point = focal_point; - } - ZoomMovement::Centered => { - let center = (output_geometry.size / 2.).to_point(); - - if level == 1.0 { - // Prevent focal point jumping to top-left corner (0, 0) on zoom out - output_state_ref.focal_point = center; - return; - } - - // Compute translation to keep cursor at center of screen - let mut tx = center.x - cursor_position.x * level; - let mut ty = center.y - cursor_position.y * level; - - // Clamp translation to keep viewport within screen bounds - tx = tx.clamp(output_geometry.size.w * (1.0 - level), 0.0); - ty = ty.clamp(output_geometry.size.h * (1.0 - level), 0.0); - - // Convert translation back to focal point: T = F * (1 - level) - output_state_ref.focal_point = - Point::from((tx / (1.0 - level), ty / (1.0 - level))); + output_state_ref.focal_point = focal_point.to_local(output); } } } @@ -382,13 +382,14 @@ impl ZoomState { None } - pub fn render(renderer: &mut R, output: &Output, push: &mut dyn FnMut(IcedRenderElement)) + pub fn render(renderer: &mut R, output: &Output) -> Vec where - R: AsGlowRenderer + ImportMem, + C: From< as AsRenderElements>::RenderElement>, + R: Renderer + ImportMem, R::TextureId: Send + Clone + 'static, { let output_state = output.user_data().get::>().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| { let cosmic = theme.cosmic(); let component = &cosmic.background(false).component; - let component = &cosmic.background(false).component; iced_widget::container::Style { snap: true, icon_color: Some(component.on.into()), @@ -581,8 +581,6 @@ impl Program for ZoomProgram { let level = output_state_ref.level; std::mem::drop(output_state_ref); - let mut theme = state.common.theme.clone(); - theme.transparent = theme.cosmic().frosted_system_interface; let grab = MenuGrab::new( start_data, &seat, @@ -695,7 +693,7 @@ impl Program for ZoomProgram { ), Some(level.min(4.)), state.common.event_loop_handle.clone(), - theme, + state.common.theme.clone(), ); std::mem::drop(shell); @@ -748,8 +746,6 @@ impl Program for ZoomProgram { let level = output_state_ref.level; std::mem::drop(output_state_ref); - let mut theme = state.common.theme.clone(); - theme.transparent = theme.cosmic().frosted_system_interface; let grab = MenuGrab::new( start_data, &seat, @@ -782,7 +778,7 @@ impl Program for ZoomProgram { MenuAlignment::PREFER_CENTERED, Some(level.min(4.)), state.common.event_loop_handle.clone(), - theme, + state.common.theme.clone(), ); std::mem::drop(shell); diff --git a/src/state.rs b/src/state.rs index 21cbb400..18de36ba 100644 --- a/src/state.rs +++ b/src/state.rs @@ -19,7 +19,6 @@ use crate::{ corner_radius::CornerRadiusState, drm::WlDrmState, image_capture_source::CosmicImageCaptureSourceState, - keyboard_layout::KeyboardLayoutState, output_configuration::OutputConfigurationState, output_power::OutputPowerState, overlap_notify::OverlapNotifyState, @@ -73,7 +72,6 @@ use smithay::{ utils::{Clock, Monotonic, Point}, wayland::{ alpha_modifier::AlphaModifierState, - background_effect::BackgroundEffectState, compositor::{CompositorClientState, CompositorState, SurfaceData}, cursor_shape::CursorShapeManagerState, dmabuf::{DmabufFeedback, DmabufGlobal, DmabufState}, @@ -127,7 +125,7 @@ use std::{ cmp::min, collections::HashSet, ffi::OsString, - process::{Child, Command}, + process::Child, sync::{Arc, LazyLock, Once, atomic::AtomicBool}, time::{Duration, Instant}, }; @@ -225,7 +223,6 @@ pub struct State { pub common: Common, pub ready: Once, pub last_refresh: LastRefresh, - pub kiosk_command: Option, } smithay::delegate_dispatch2!(State); @@ -282,8 +279,6 @@ pub struct Common { pub overlap_notify_state: OverlapNotifyState, pub a11y_state: A11yState, pub dbus_state: DBusState, - pub keyboard_layout_state: KeyboardLayoutState, - pub background_effect_state: BackgroundEffectState, // shell-related wayland state pub xdg_shell_state: XdgShellState, @@ -634,7 +629,6 @@ impl State { handle: LoopHandle<'static, State>, signal: LoopSignal, with_xwayland: bool, - kiosk_command: Option, ) -> State { let requested_languages = DesktopLanguageRequester::requested_languages(); i18n_embed::select(&*LANG_LOADER, &Localizations, &requested_languages) @@ -688,9 +682,6 @@ impl State { AlphaModifierState::new::(dh); SinglePixelBufferState::new::(dh); FixesState::new::(dh); - let keyboard_layout_state = KeyboardLayoutState::new::(dh, client_not_sandboxed); - - let background_effect_state = BackgroundEffectState::new::(dh); let idle_notifier_state = IdleNotifierState::::new(dh, handle.clone()); let idle_inhibit_manager_state = IdleInhibitManagerState::new::(dh); @@ -795,14 +786,12 @@ impl State { xdg_activation_state, xdg_foreign_state, workspace_state, - background_effect_state, a11y_state, xwayland_scale: None, xwayland_state: None, xwayland_shell_state, pointer_focus_state: None, dbus_state, - keyboard_layout_state, #[cfg(feature = "logind")] inhibit_lid_fd: None, @@ -812,7 +801,6 @@ impl State { backend: BackendData::Unset, ready: Once::new(), last_refresh: LastRefresh::None, - kiosk_command, } } @@ -942,22 +930,19 @@ impl Common { render_element_states: &RenderElementStates, ) { let shell = self.shell.read(); - let processor = |namespace: Option| { - move |surface: &WlSurface, states: &SurfaceData| { - let primary_scanout_output = update_surface_primary_scanout_output( - surface, - output, - states, - namespace, - render_element_states, - primary_scanout_output_compare, - ); - if let Some(output) = primary_scanout_output { - with_fractional_scale(states, |fraction_scale| { - fraction_scale - .set_preferred_scale(output.current_scale().fractional_scale()); - }); - } + let processor = |surface: &WlSurface, states: &SurfaceData| { + let primary_scanout_output = update_surface_primary_scanout_output( + surface, + output, + states, + None, + render_element_states, + primary_scanout_output_compare, + ); + if let Some(output) = primary_scanout_output { + with_fractional_scale(states, |fraction_scale| { + fraction_scale.set_preferred_scale(output.current_scale().fractional_scale()); + }); } }; @@ -965,7 +950,7 @@ impl Common { if let Some(session_lock) = shell.session_lock.as_ref() && let Some(lock_surface) = session_lock.surfaces.get(output) { - with_surfaces_surface_tree(lock_surface.wl_surface(), processor(None)) + with_surfaces_surface_tree(lock_surface.wl_surface(), processor) } for seat in shell @@ -977,7 +962,7 @@ impl Common { // cursor ... if let CursorImageStatus::Surface(wl_surface) = cursor_status { - with_surfaces_surface_tree(&wl_surface, processor(None)); + with_surfaces_surface_tree(&wl_surface, processor); } // grabs @@ -985,12 +970,12 @@ impl Common { && let Some(grab_state) = move_grab.lock().unwrap().as_ref() { for (window, _) in grab_state.element().windows() { - window.with_surfaces(processor(None)); + window.with_surfaces(processor); } } 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() { set.sticky_layer.mapped().for_each(|mapped| { for (window, _) in mapped.windows() { - window.with_surfaces(processor(None)); + window.with_surfaces(processor); } }); } @@ -1006,16 +991,16 @@ impl Common { // normal windows for space in shell.workspaces.spaces() { if let Some(fs) = space.get_fullscreen(shell.seats.last_active()) { - fs.surface.with_surfaces(processor(None)); + fs.surface.with_surfaces(processor); } space.mapped().for_each(|mapped| { for (window, _) in mapped.windows() { - window.with_surfaces(processor(None)); + window.with_surfaces(processor); } }); space.minimized_windows.iter().for_each(|m| { for window in m.windows() { - window.with_surfaces(processor(None)); + window.with_surfaces(processor); } }) } @@ -1023,16 +1008,15 @@ impl Common { // OR windows shell.override_redirect_windows.iter().for_each(|or| { if let Some(wl_surface) = or.wl_surface() { - with_surfaces_surface_tree(&wl_surface, processor(None)); + with_surfaces_surface_tree(&wl_surface, processor); } }); // layer surfaces for o in shell.outputs() { - let namespace = shell.workspaces.active_num(o).1; let map = smithay::desktop::layer_map_for_output(o); for layer_surface in map.layers() { - layer_surface.with_surfaces(processor(Some(namespace))); + layer_surface.with_surfaces(processor); } } } diff --git a/src/systemd.rs b/src/systemd.rs index 36720d9c..422d91c9 100644 --- a/src/systemd.rs +++ b/src/systemd.rs @@ -27,9 +27,9 @@ pub fn ready(common: &Common) { ), Err(err) => error!(?err, "Failed to run systemctl although booted with systemd",), }; - } - if let Err(err) = notify(false, &[NotifyState::Ready]) { - error!(?err, "Failed to notify systemd"); + if let Err(err) = notify(false, &[NotifyState::Ready]) { + error!(?err, "Failed to notify systemd"); + } } } diff --git a/src/utils/iced/mod.rs b/src/utils/iced/mod.rs index 2bfe888d..44a722db 100644 --- a/src/utils/iced/mod.rs +++ b/src/utils/iced/mod.rs @@ -12,15 +12,15 @@ use cosmic::{ iced::{ Limits, Point as IcedPoint, Size as IcedSize, Task, advanced::{graphics::text::font_system, widget::Tree}, - core::{Color, Length, Pixels, clipboard::Null as NullClipboard, id::Id, renderer::Style}, event::Event, futures::{FutureExt, StreamExt}, keyboard::{Event as KeyboardEvent, Modifiers as IcedModifiers}, mouse::{Button as MouseButton, Cursor, Event as MouseEvent, ScrollDelta}, - runtime::{Action, task::into_stream}, touch::{Event as TouchEvent, Finger}, window::Event as WindowEvent, }, + iced_core::{Color, Length, Pixels, clipboard::Null as NullClipboard, id::Id, renderer::Style}, + iced_runtime::{Action, task::into_stream}, }; use iced_tiny_skia::{ Layer, @@ -33,9 +33,9 @@ use smithay::{ allocator::Fourcc, input::{ButtonState, KeyState}, renderer::{ - ImportMem, + ImportMem, Renderer, element::{ - Kind, + AsRenderElements, Kind, memory::{MemoryRenderBuffer, MemoryRenderBufferRenderElement}, }, }, @@ -57,20 +57,13 @@ use smithay::{ }, output::Output, reexports::calloop::{self, LoopHandle, RegistrationToken, futures::Scheduler}, - render_elements, utils::{ Buffer as BufferCoords, IsAlive, Logical, Physical, Point, Rectangle, Scale, Serial, Size, Transform, }, }; -use crate::{ - backend::render::{ - element::AsGlowRenderer, - wayland::blur_effect::{BlurElement, BlurState}, - }, - utils::iced::state::State, -}; +use crate::utils::iced::state::State; static ID: LazyLock = LazyLock::new(|| Id::new("Program")); @@ -180,7 +173,6 @@ pub(crate) struct IcedElementInternal { outputs: HashSet, buffers: HashMap, (MemoryRenderBuffer, Option<(Vec, Color)>)>, pending_realloc: bool, - blur: BlurState, // state size: Size, @@ -233,7 +225,6 @@ impl Clone for IcedElementInternal

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

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

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

{ self.0.lock().unwrap().update(true); } - pub fn with_theme(&self, f: impl FnOnce(&Theme) -> R) -> R { - let guard = self.0.lock().unwrap(); - f(&guard.theme) - } - pub fn set_theme(&self, theme: cosmic::Theme) { let mut guard = self.0.lock().unwrap(); guard.theme = theme.clone(); @@ -924,20 +910,21 @@ impl SpaceElement for IcedElement

{ } } -impl IcedElement

{ - pub fn push_render_elements( +impl AsRenderElements for IcedElement

+where + P: Program + Send + 'static, + R: Renderer + ImportMem, + R::TextureId: Send + Clone + 'static, +{ + type RenderElement = MemoryRenderBufferRenderElement; + + fn render_elements>( &self, renderer: &mut R, location: Point, mut scale: Scale, alpha: f32, - mut radii: [u8; 4], - push_above: &mut dyn FnMut(IcedRenderElement), - push_below: Option<&mut dyn FnMut(IcedRenderElement)>, - ) where - R: AsGlowRenderer + ImportMem, - R::TextureId: Send + Clone + 'static, - { + ) -> Vec { let mut internal = self.0.lock().unwrap(); // makes partial borrows easier let internal_ref = &mut *internal; @@ -1068,51 +1055,11 @@ impl IcedElement

{ Kind::Unspecified, ) { Ok(buffer) => { - push_above(buffer.into()); + return vec![C::from(buffer)]; } Err(err) => tracing::warn!("What? {:?}", err), } - - if internal_ref.theme.transparent { - for radius in radii.iter_mut() { - *radius = ((*radius as f64) * internal_ref.additional_scale).round() as u8; - } - - match BlurElement::from_state( - renderer, - &mut internal_ref.blur, - Rectangle::new( - location - .to_f64() - .to_logical(scale) - .upscale(internal_ref.additional_scale), - internal_ref - .size - .to_f64() - .upscale(internal_ref.additional_scale) - .to_i32_round(), - ), - scale.x, - radii, - (internal_ref.theme.cosmic().frosted as u8 + 1) as usize, - ) { - Ok(Some(elem)) => { - if let Some(push_below) = push_below { - push_below(elem.into()) - } else { - push_above(elem.into()) - } - } - Ok(None) => {} - Err(err) => tracing::warn!("Blur elem error: {:?}", err), - } - } } + Vec::new() } } - -render_elements! { - pub IcedRenderElement where R: ImportMem + AsGlowRenderer, R::TextureId: Send; - UI=MemoryRenderBufferRenderElement, - Blur=BlurElement, -} diff --git a/src/utils/iced/state.rs b/src/utils/iced/state.rs index 2af3a993..db67d8f3 100644 --- a/src/utils/iced/state.rs +++ b/src/utils/iced/state.rs @@ -1,16 +1,12 @@ use super::IcedProgram as Program; -use cosmic::iced::{ - core::{ - self as iced_core, Clipboard, Size, - event::{self, Event}, - mouse, renderer, - widget::operation::{self, Operation}, - }, - runtime::{ - Task, - user_interface::{self, UserInterface}, - }, -}; +use cosmic::iced::core::event::{self, Event}; +use cosmic::iced::core::mouse; +use cosmic::iced::core::renderer; +use cosmic::iced::core::widget::operation::{self, Operation}; +use cosmic::iced::core::{Clipboard, Size}; +use cosmic::iced_core; +use cosmic::iced_runtime::Task; +use cosmic::iced_runtime::user_interface::{self, UserInterface}; /// The execution state of a [`Program`]. It leverages caching, event /// processing, and rendering primitive storage. @@ -132,7 +128,7 @@ where messages.append(&mut self.queued_messages); let task = if messages.is_empty() { - if let cosmic::iced::runtime::user_interface::State::Updated { + if let cosmic::iced_runtime::user_interface::State::Updated { mouse_interaction, .. } = state { @@ -158,7 +154,7 @@ where let mut user_interface = build_user_interface(id, &mut self.program, temp_cache, renderer, bounds); - if let cosmic::iced::runtime::user_interface::State::Updated { + if let cosmic::iced_runtime::user_interface::State::Updated { mouse_interaction, .. } = state { diff --git a/src/utils/screenshot.rs b/src/utils/screenshot.rs index f1f1f5f4..e90fb532 100644 --- a/src/utils/screenshot.rs +++ b/src/utils/screenshot.rs @@ -3,7 +3,9 @@ use smithay::{ backend::{ allocator::Fourcc, renderer::{ - ExportMem, ImportAll, Offscreen, Renderer, damage::OutputDamageTracker, + ExportMem, ImportAll, Offscreen, Renderer, + damage::OutputDamageTracker, + element::{AsRenderElements, surface::WaylandSurfaceRenderElement}, gles::GlesRenderbuffer, }, }, @@ -14,7 +16,7 @@ use smithay::{ use tracing::warn; use crate::{ - backend::render::{RendererRef, element::AsGlowRenderer}, + backend::render::RendererRef, shell::element::CosmicSurface, state::{State, advertised_node_for_surface}, }; @@ -22,24 +24,17 @@ use crate::{ pub fn screenshot_window(state: &mut State, surface: &CosmicSurface) { fn render_window(renderer: &mut R, window: &CosmicSurface) -> anyhow::Result<()> where - R: Renderer + ImportAll + Offscreen + ExportMem + AsGlowRenderer, - R::TextureId: Send + Clone + 'static, + R: Renderer + ImportAll + Offscreen + ExportMem, + R::TextureId: Clone + 'static, R::Error: Send + Sync + 'static, { let bbox = bbox_from_surface_tree(&window.wl_surface().unwrap(), (0, 0)); - let mut elements = Vec::new(); - window.push_render_elements( + let elements = AsRenderElements::::render_elements::>( + window, renderer, (-bbox.loc.x, -bbox.loc.y).into(), Scale::from(1.0), 1.0, - None, - None, - false, - [0; 4], - 0, - &mut |elem| elements.push(elem), - None, ); // TODO: 10-bit diff --git a/src/wayland/handlers/background_effect.rs b/src/wayland/handlers/background_effect.rs deleted file mode 100644 index 28682e03..00000000 --- a/src/wayland/handlers/background_effect.rs +++ /dev/null @@ -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>>, -} - -impl Cacheable for ComputedBlurRegionCachedState { - fn commit(&mut self, _dh: &DisplayHandle) -> Self { - self.clone() - } - - fn merge_into(self, into: &mut Self, _dh: &DisplayHandle) { - *into = self; - } -} - -impl ExtBackgroundEffectHandler for State { - fn capabilities(&self) -> Capability { - Capability::Blur - } - - fn set_blur_region(&mut self, surface: WlSurface, region: RegionAttributes) { - with_states(&surface, |states| { - let mut blur_state = states.cached_state.get::(); - - blur_state.pending().blur_region = Some({ - let (added, subtracted) = region - .rects - .iter() - .cloned() - .partition::, _>(|(op, _)| matches!(op, RectangleKind::Add)); - let added = added.into_iter().map(|(_, rect)| rect).collect::>(); - Rectangle::subtract_rects_many_in_place( - added, - subtracted.into_iter().map(|(_, rect)| rect), - ) - }) - }) - } - - fn unset_blur_region(&mut self, surface: WlSurface) { - with_states(&surface, |states| { - let mut blur_state = states.cached_state.get::(); - - blur_state.pending().blur_region.take(); - }) - } -} diff --git a/src/wayland/handlers/compositor.rs b/src/wayland/handlers/compositor.rs index 12bc2e56..92b51e58 100644 --- a/src/wayland/handlers/compositor.rs +++ b/src/wayland/handlers/compositor.rs @@ -314,7 +314,8 @@ impl CompositorHandler for State { .then(|| state.element()) }); 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| { s.wl_surface() .as_deref() diff --git a/src/wayland/handlers/corner_radius.rs b/src/wayland/handlers/corner_radius.rs index ded8d923..dbcae2dc 100644 --- a/src/wayland/handlers/corner_radius.rs +++ b/src/wayland/handlers/corner_radius.rs @@ -1,9 +1,7 @@ -use smithay::utils::{Logical, Rectangle, Size}; -use smithay::wayland::compositor::SurfaceData; +use cosmic_protocols::corner_radius::v1::server::cosmic_corner_radius_toplevel_v1; use crate::wayland::protocols::corner_radius::{ - CacheableCorners, CacheablePadding, CornerRadiusData, CornerRadiusHandler, CornerRadiusState, - delegate_corner_radius, + CornerRadiusData, CornerRadiusHandler, CornerRadiusState, delegate_corner_radius, }; use crate::state::State; @@ -13,63 +11,39 @@ impl CornerRadiusHandler for State { &mut self.common.corner_radius_state } - fn set_corner_radius(&mut self, _data: &CornerRadiusData) {} + fn set_corner_radius( + &mut self, + _: &cosmic_corner_radius_toplevel_v1::CosmicCornerRadiusToplevelV1, + data: &CornerRadiusData, + ) { + if force_redraw(self, data).is_none() { + tracing::warn!("Failed to force redraw for corner radius change."); + } + } - fn unset_corner_radius(&mut self, _data: &CornerRadiusData) {} - - fn set_padding(&mut self, _data: &CornerRadiusData) {} - - fn unset_padding(&mut self, _data: &CornerRadiusData) {} + fn unset_corner_radius( + &mut self, + _: &cosmic_corner_radius_toplevel_v1::CosmicCornerRadiusToplevelV1, + data: &CornerRadiusData, + ) { + if force_redraw(self, data).is_none() { + tracing::warn!("Failed to force redraw for corner radius reset."); + } + } } -pub fn surface_corners(states: &SurfaceData, size: Size) -> Option<[u8; 4]> { - let mut guard = states.cached_state.get::(); +fn force_redraw(state: &mut State, data: &CornerRadiusData) -> Option<()> { + let guard = data.lock().unwrap(); - // guard against corner radius being too large, potentially disconnecting the outline - let half_min_dim = u8::try_from(size.w.min(size.h) / 2).unwrap_or(u8::MAX); - let corners = guard.current().0?; + let toplevel = guard.toplevel.upgrade().ok()?; - Some([ - u8::try_from(corners.top_left) - .unwrap_or(u8::MAX) - .min(half_min_dim), - u8::try_from(corners.top_right) - .unwrap_or(u8::MAX) - .min(half_min_dim), - u8::try_from(corners.bottom_right) - .unwrap_or(u8::MAX) - .min(half_min_dim), - u8::try_from(corners.bottom_left) - .unwrap_or(u8::MAX) - .min(half_min_dim), - ]) -} + let surface = state.common.xdg_shell_state.get_toplevel(&toplevel)?; -pub fn surface_padding(states: &SurfaceData, size: Size) -> Option<[i32; 4]> { - let mut guard = states.cached_state.get::(); + let guard = state.common.shell.read(); + let output = guard.visible_output_for_surface(surface.wl_surface())?; - let padding = guard.current().0?; - - // guard against padding being too large - Some([ - padding.top.min(size.h / 2), - padding.right.min(size.w / 2), - padding.bottom.min(size.h / 2), - padding.left.min(size.w / 2), - ]) -} - -pub fn pad_rect( - mut rect: Rectangle, - padding: &[i32; 4], -) -> Option> { - rect.size.h = rect.size.h.checked_sub(padding[0])?; - rect.loc.x += padding[3]; - rect.size.w = rect.size.w.checked_sub(padding[1])?; - rect.size.h = rect.size.h.checked_sub(padding[2])?; - rect.size.w = rect.size.w.checked_sub(padding[3])?; - rect.loc.y += padding[0]; - Some(rect) + state.backend.schedule_render(output); + Some(()) } delegate_corner_radius!(State); diff --git a/src/wayland/handlers/image_copy_capture/mod.rs b/src/wayland/handlers/image_copy_capture/mod.rs index 85a01c9a..733e05ef 100644 --- a/src/wayland/handlers/image_copy_capture/mod.rs +++ b/src/wayland/handlers/image_copy_capture/mod.rs @@ -14,7 +14,6 @@ use smithay::{ }, }, desktop::space::SpaceElement, - input::{Seat, pointer::PointerHandle}, output::Output, reexports::wayland_server::protocol::{wl_pointer::WlPointer, wl_shm::Format as ShmFormat}, utils::{Buffer as BufferCoords, Point, Size, Transform}, @@ -31,7 +30,7 @@ use smithay::{ }; use crate::{ - shell::{CosmicSurface, CursorGeometry, Shell}, + shell::CosmicSurface, state::{BackendData, State}, utils::prelude::{ OutputExt, PointExt, PointGlobalExt, PointLocalExt, RectExt, RectLocalExt, SeatExt, @@ -45,36 +44,6 @@ pub use self::render::*; use self::user_data::*; pub use self::user_data::{FrameHolder, ImageCopySessions, SessionData, SessionHolder}; -fn default_cursor_size() -> Size { - Size::new(64, 64) -} - -fn seat_for_wl_pointer<'a>(shell: &'a Shell, pointer: &WlPointer) -> Option<&'a Seat> { - let pointer_handle = PointerHandle::::from_resource(pointer)?; - shell - .seats - .iter() - .find(|seat| seat.get_pointer().is_some_and(|p| p == pointer_handle)) -} - -pub fn cursor_capture_constraints(cursor_geometry: Option) -> BufferConstraints { - let size = if let Some(cursor_geometry) = cursor_geometry { - let mut size = cursor_geometry.geometry.size; - // Client shouldn't try to allocate 0x0 buffer - if size == Size::new(0, 0) { - size = Size::new(1, 1); - } - size - } else { - default_cursor_size() - }; - BufferConstraints { - size, - shm: vec![ShmFormat::Argb8888], - dma: None, - } -} - impl ImageCopyCaptureHandler for State { fn image_copy_capture_state(&mut self) -> &mut ImageCopyCaptureState { &mut self.common.image_copy_capture_state @@ -104,12 +73,26 @@ impl ImageCopyCaptureHandler for State { fn cursor_capture_constraints( &mut self, _source: &ImageCaptureSource, - pointer: &WlPointer, + _pointer: &WlPointer, ) -> Option { - let shell = self.common.shell.read(); - let seat = seat_for_wl_pointer(&shell, pointer)?; - let cursor_geometry = seat.cursor_geometry((0.0, 0.0), self.common.clock.now()); - Some(cursor_capture_constraints(cursor_geometry)) + let size = if let Some((geometry, _)) = self + .common + .shell + .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) { @@ -166,23 +149,20 @@ impl ImageCopyCaptureHandler for State { fn new_cursor_session(&mut self, session: CursorSession) { let (pointer_loc, pointer_size, hotspot) = { - let shell = self.common.shell.read(); - if let Some(seat) = seat_for_wl_pointer(&shell, &session.pointer()) { - let pointer = seat.get_pointer().unwrap(); - let pointer_loc = pointer.current_location().to_i32_round().as_global(); + let seat = self.common.shell.read().seats.last_active().clone(); - let (pointer_size, hotspot) = if let Some(CursorGeometry { geometry, hotspot }) = - seat.cursor_geometry((0.0, 0.0), self.common.clock.now()) - { - (geometry.size, hotspot) - } else { - (default_cursor_size(), Point::from((0, 0))) - }; + let pointer = seat.get_pointer().unwrap(); + let pointer_loc = pointer.current_location().to_i32_round().as_global(); - (pointer_loc, pointer_size, hotspot) + let (pointer_size, hotspot) = if let Some((geometry, hotspot)) = + seat.cursor_geometry((0.0, 0.0), self.common.clock.now()) + { + (geometry.size, hotspot) } else { - (Point::new(0, 0), default_cursor_size(), Point::from((0, 0))) - } + (Size::from((64, 64)), Point::from((0, 0))) + }; + + (pointer_loc, pointer_size, hotspot) }; session.user_data().insert_if_missing_threadsafe(|| { @@ -319,11 +299,8 @@ impl ImageCopyCaptureHandler for State { return; } - let shell = self.common.shell.read(); - if let Some(seat) = seat_for_wl_pointer(&shell, &session.pointer()).cloned() { - drop(shell); - render_cursor_to_buffer(self, session, frame, &seat); - } + let seat = self.common.shell.read().seats.last_active().clone(); + render_cursor_to_buffer(self, session, frame, &seat); } fn frame_aborted(&mut self, frame: FrameRef) { diff --git a/src/wayland/handlers/image_copy_capture/render.rs b/src/wayland/handlers/image_copy_capture/render.rs index f8b308e2..21370f31 100644 --- a/src/wayland/handlers/image_copy_capture/render.rs +++ b/src/wayland/handlers/image_copy_capture/render.rs @@ -9,7 +9,8 @@ use smithay::{ buffer_dimensions, buffer_type, damage::{Error as DTError, OutputDamageTracker, RenderOutputResult}, element::{ - RenderElement, UnderlyingStorage, + AsRenderElements, RenderElement, + surface::WaylandSurfaceRenderElement, utils::{Relocate, RelocateRenderElement}, }, gles::{GlesError, GlesRenderbuffer}, @@ -20,14 +21,16 @@ use smithay::{ desktop::space::SpaceElement, input::Seat, output::{Output, OutputNoMode}, - reexports::wayland_server::protocol::wl_buffer::WlBuffer, + reexports::wayland_server::protocol::{wl_buffer::WlBuffer, wl_shm::Format as ShmFormat}, utils::{ Buffer as BufferCoords, IsAlive, Logical, Physical, Point, Rectangle, Scale, Size, Transform, }, wayland::{ dmabuf::get_dmabuf, - image_copy_capture::{CaptureFailureReason, CursorSessionRef, Frame, SessionRef}, + image_copy_capture::{ + BufferConstraints, CaptureFailureReason, CursorSessionRef, Frame, SessionRef, + }, seat::WaylandFocus, shm::{shm_format_to_fourcc, with_buffer_contents, with_buffer_contents_mut}, }, @@ -37,11 +40,9 @@ use tracing::warn; use crate::{ backend::render::{ - CursorMode, ElementFilter, RendererRef, - cursor::{self, CursorRenderElement}, - element::{AsGlowRenderer, CosmicElement, DamageElement}, + CursorMode, ElementFilter, RendererRef, cursor, + element::{AsGlowRenderer, CosmicElement, DamageElement, FromGlesError}, render_workspace, - wayland::SurfaceRenderElement, }, shell::{CosmicMappedRenderElement, CosmicSurface, WorkspaceRenderElement}, state::{Common, KmsNodes, State}, @@ -54,34 +55,12 @@ use crate::{ }, }; -use super::{ - super::data_device::get_dnd_icon, cursor_capture_constraints, user_data::SessionHolder, -}; - -pub fn render_element_buffers( - renderer: &mut R, - elements: &[E], -) -> Vec -where - R: AsGlowRenderer, - E: RenderElement, -{ - elements - .iter() - .filter_map(|elem| match elem.underlying_storage(renderer) { - Some(UnderlyingStorage::Wayland(buffer)) => Some(buffer.clone()), - Some(UnderlyingStorage::Memory(_)) | None => None, - }) - .collect() -} +use super::{super::data_device::get_dnd_icon, user_data::SessionHolder}; pub struct PendingImageCopyData { pub frame: Frame, pub damage: Vec>, pub sync: SyncPoint, - // Hold reference so `wl_buffer` isn't released and sync point isn't signaled - // until image copy completes. - _buffers: Vec, } impl PendingImageCopyData { @@ -124,10 +103,10 @@ pub fn submit_buffer( transform: Transform, damage: Option<&[Rectangle]>, mut sync: SyncPoint, - buffers: Vec, ) -> Result, R::Error> where - R: ExportMem + AsGlowRenderer, + R: ExportMem, + R::Error: FromGlesError, { let Some(damage) = damage else { frame.success( @@ -185,7 +164,7 @@ where 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) { frame.fail(CaptureFailureReason::Unknown); @@ -203,7 +182,6 @@ where }) .collect(), sync, - _buffers: buffers, })) } @@ -216,6 +194,7 @@ pub fn render_session( ) -> Result, DTError> where R: AsGlowRenderer, + R::Error: FromGlesError, F: for<'d> FnOnce( &WlBuffer, &mut R, @@ -223,13 +202,7 @@ where &'d mut OutputDamageTracker, usize, Vec>, - ) -> Result< - ( - RenderOutputResult<'d>, - Vec, - ), - DTError, - >, + ) -> Result, DTError>, { let mut session_user_data = session.lock().unwrap(); @@ -273,25 +246,30 @@ where .as_mut() .map(|(_, tex)| renderer.bind(tex).map_err(DTError::Rendering)) .transpose()?; - let (result, buffers) = render_fn( + let res = render_fn( &frame.buffer(), renderer, fb.as_mut(), dt, age, frame.damage(), - )?; + ); - submit_buffer( - frame, - renderer, - fb.as_mut(), - transform, - result.damage.map(|x| x.as_slice()), - result.sync, - buffers, - ) - .map_err(DTError::Rendering) + match res { + Ok(result) => submit_buffer( + frame, + renderer, + fb.as_mut(), + transform, + result.damage.map(|x| x.as_slice()), + result.sync, + ) + .map_err(DTError::Rendering), + Err(err) => { + frame.fail(CaptureFailureReason::Unknown); + Err(err) + } + } } pub fn render_workspace_to_buffer( @@ -339,16 +317,11 @@ pub fn render_workspace_to_buffer( common: &mut Common, output: &Output, handle: (WorkspaceHandle, usize), - ) -> Result< - ( - RenderOutputResult<'d>, - Vec, - ), - DTError, - > + ) -> Result, DTError> where R: AsGlowRenderer, R::TextureId: Send + Clone + 'static, + R::Error: FromGlesError, CosmicElement: RenderElement, CosmicMappedRenderElement: RenderElement, WorkspaceRenderElement: RenderElement, @@ -385,7 +358,7 @@ pub fn render_workspace_to_buffer( .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 fb = renderer.bind(&mut dmabuf).map_err(DTError::Rendering)?; render_workspace( @@ -403,7 +376,8 @@ pub fn render_workspace_to_buffer( handle, cursor_mode, ElementFilter::ExcludeWorkspaceOverview, - )? + ) + .map(|res| res.0) } else { let target = offscreen.expect("shm buffers should have an offscreen target"); render_workspace( @@ -421,12 +395,9 @@ pub fn render_workspace_to_buffer( handle, cursor_mode, ElementFilter::ExcludeWorkspaceOverview, - )? - }; - - let buffers = render_element_buffers(renderer, &elements); - - Ok((res, buffers)) + ) + .map(|res| res.0) + } } let draw_cursor = session.draw_cursor(); @@ -533,10 +504,9 @@ pub fn render_workspace_to_buffer( } smithay::render_elements! { - pub WindowCaptureElement where R: ImportAll + ImportMem + AsGlowRenderer, R::TextureId: Send; - WaylandElement=SurfaceRenderElement, + pub WindowCaptureElement where R: ImportAll + ImportMem; + WaylandElement=WaylandSurfaceRenderElement, CursorElement=RelocateRenderElement>, - DamageElement=DamageElement, } pub fn render_window_to_buffer( @@ -580,20 +550,15 @@ pub fn render_window_to_buffer( common: &mut Common, toplevel: &CosmicSurface, geometry: Rectangle, - ) -> Result< - ( - RenderOutputResult<'d>, - Vec, - ), - DTError, - > + ) -> Result, DTError> where R: AsGlowRenderer, R::TextureId: Send + Clone + 'static, + R::Error: FromGlesError, CosmicElement: RenderElement, CosmicMappedRenderElement: RenderElement, { - let mut elements: Vec<_> = additional_damage + let additional_damage_elements: Vec<_> = additional_damage .into_iter() .filter_map(|rect| { let logical_rect = rect.to_logical( @@ -604,21 +569,15 @@ pub fn render_window_to_buffer( logical_rect.intersection(Rectangle::from_size(geometry.size)) }) .map(DamageElement::new) - .map(WindowCaptureElement::::from) .collect(); + dt.damage_output(age, &additional_damage_elements)?; let shell = common.shell.read(); - let blur_strength = 9; // TODO let seat = shell.seats.last_active().clone(); let pointer = seat.get_pointer().unwrap(); let pointer_loc = pointer.current_location().to_i32_round().as_global(); let mut location = None; if let Some(element) = shell.element_for_surface(toplevel) - && pointer - .current_focus() - .and_then(|f| f.toplevel(&shell)) - .as_ref() - == Some(toplevel) && element.has_active_window(toplevel) && let Some(workspace) = shell.space_for(element) && let Some(geometry) = workspace.element_geometry(element) @@ -632,80 +591,67 @@ pub fn render_window_to_buffer( }; std::mem::drop(shell); + let mut elements = Vec::new(); + if let Some(location) = location { if draw_cursor { - cursor::draw_cursor( - renderer, - &seat, - location, - 1.0.into(), - 1.0, - common.clock.now(), - blur_strength, - true, - &mut |elem, hotspot| { - elements.push(WindowCaptureElement::CursorElement( - RelocateRenderElement::from_element( - elem, - Point::from((-hotspot.x, -hotspot.y)), - Relocate::Relative, - ), - )); - }, + elements.extend( + cursor::draw_cursor( + renderer, + &seat, + location, + 1.0.into(), + 1.0, + common.clock.now(), + true, + ) + .into_iter() + .map(|(elem, hotspot)| { + WindowCaptureElement::CursorElement(RelocateRenderElement::from_element( + elem, + Point::from((-hotspot.x, -hotspot.y)), + Relocate::Relative, + )) + }), ); } // TODO cosmic-workspaces wants to omit, but metadata cursor capture in portal should // still include dnd surface in window capture buffer? if draw_cursor && let Some(dnd_icon) = get_dnd_icon(&seat) { - cursor::draw_dnd_icon( - renderer, - &dnd_icon.surface, - (location + dnd_icon.offset.to_f64()).to_i32_round(), - 1.0, - blur_strength, - &mut |elem| { - elements.push( - RelocateRenderElement::from_element( - CursorRenderElement::Surface(elem), - Point::new(0, 0), - Relocate::Relative, - ) - .into(), - ) - }, + elements.extend( + cursor::draw_dnd_icon( + renderer, + &dnd_icon.surface, + (location + dnd_icon.offset.to_f64()).to_i32_round(), + 1.0, + ) + .into_iter() + .map(WindowCaptureElement::from), ); } } - toplevel.push_render_elements( + elements.extend(AsRenderElements::::render_elements::< + WindowCaptureElement, + >( + toplevel, renderer, (-geometry.loc.x, -geometry.loc.y).into(), Scale::from(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 fb = renderer .bind(&mut dmabuf_clone) .map_err(DTError::Rendering)?; - dt.render_output(renderer, &mut fb, age, &elements, Color32F::TRANSPARENT)? + dt.render_output(renderer, &mut fb, age, &elements, Color32F::TRANSPARENT) } else { let fb = offscreen.expect("shm buffer should have an offscreen target"); - dt.render_output(renderer, fb, age, &elements, Color32F::TRANSPARENT)? - }; - - let buffers = render_element_buffers(renderer, &elements); - - Ok((res, buffers)) + dt.render_output(renderer, fb, age, &elements, Color32F::TRANSPARENT) + } } let common = &mut state.common; @@ -807,17 +753,25 @@ pub fn render_cursor_to_buffer( seat: &Seat, ) { let buffer = frame.buffer(); - let cursor_geometry = seat.cursor_geometry((0.0, 0.0), state.common.clock.now()); - let constraints = cursor_capture_constraints(cursor_geometry); + let mut cursor_size = seat + .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(); - if buffer_size != constraints.size { - session.update_constraints(constraints.clone()); + // Client shouldn't try to allocate 0x0 buffer + if cursor_size == Size::new(0, 0) { + cursor_size = Size::new(1, 1); + } + if buffer_size != cursor_size { + let constraints = BufferConstraints { + size: cursor_size, + shm: vec![ShmFormat::Argb8888], + dma: None, + }; + session.update_constraints(constraints); if let Some(data) = session.user_data().get::() { *data.lock().unwrap() = SessionUserData::new(OutputDamageTracker::new( - constraints - .size - .to_logical(1, Transform::Normal) - .to_physical(1), + cursor_size.to_logical(1, Transform::Normal).to_physical(1), 1.0, Transform::Normal, )); @@ -835,59 +789,48 @@ pub fn render_cursor_to_buffer( additional_damage: Vec>, common: &mut Common, seat: &Seat, - ) -> Result< - ( - RenderOutputResult<'d>, - Vec, - ), - DTError, - > + ) -> Result, DTError> where R: AsGlowRenderer, R::TextureId: Send + Clone + 'static, + R::Error: FromGlesError, CosmicElement: RenderElement, CosmicMappedRenderElement: RenderElement, { - let mut elements: Vec<_> = additional_damage + let additional_damage_elements: Vec<_> = additional_damage .into_iter() .filter_map(|rect| { let logical_rect = rect.to_logical(1, Transform::Normal, &Size::from((64, 64))); logical_rect.intersection(Rectangle::from_size((64, 64).into())) }) .map(DamageElement::new) - .map(WindowCaptureElement::from) .collect(); + dt.damage_output(age, &additional_damage_elements)?; - cursor::draw_cursor( + let elements = cursor::draw_cursor( renderer, seat, Point::from((0.0, 0.0)), 1.0.into(), 1.0, common.clock.now(), - 0, 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::>(); - let res = if let Ok(dmabuf) = get_dmabuf(buffer) { + if let Ok(dmabuf) = get_dmabuf(buffer) { let mut dmabuf_clone = dmabuf.clone(); let mut fb = renderer .bind(&mut dmabuf_clone) .map_err(DTError::Rendering)?; - dt.render_output(renderer, &mut fb, age, &elements, [0.0, 0.0, 0.0, 0.0])? + dt.render_output(renderer, &mut fb, age, &elements, [0.0, 0.0, 0.0, 0.0]) } else { let fb = offscreen.expect("shm buffers should have offscreen target"); - dt.render_output(renderer, fb, age, &elements, [0.0, 0.0, 0.0, 0.0])? - }; - - let buffers = render_element_buffers(renderer, &elements); - - Ok((res, buffers)) + dt.render_output(renderer, fb, age, &elements, [0.0, 0.0, 0.0, 0.0]) + } } let common = &mut state.common; diff --git a/src/wayland/handlers/keyboard_layout.rs b/src/wayland/handlers/keyboard_layout.rs deleted file mode 100644 index ac1fda87..00000000 --- a/src/wayland/handlers/keyboard_layout.rs +++ /dev/null @@ -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 - } -} diff --git a/src/wayland/handlers/mod.rs b/src/wayland/handlers/mod.rs index 651eadc9..34d063d0 100644 --- a/src/wayland/handlers/mod.rs +++ b/src/wayland/handlers/mod.rs @@ -1,7 +1,6 @@ // SPDX-License-Identifier: GPL-3.0-only pub mod a11y; -pub mod background_effect; pub mod buffer; pub mod compositor; pub mod corner_radius; @@ -19,7 +18,6 @@ pub mod idle_notify; pub mod image_capture_source; pub mod image_copy_capture; pub mod input_method; -pub mod keyboard_layout; pub mod keyboard_shortcuts_inhibit; pub mod layer_shell; pub mod output; diff --git a/src/wayland/handlers/pointer_constraints.rs b/src/wayland/handlers/pointer_constraints.rs index 9ce5f63a..e17c1961 100644 --- a/src/wayland/handlers/pointer_constraints.rs +++ b/src/wayland/handlers/pointer_constraints.rs @@ -63,7 +63,7 @@ impl PointerConstraintsHandler for State { if let Some(region) = constraint.region() { if let Some(surface_location) = surface_location && let position = pointer.current_location() - && let point = (position - surface_location.as_logical()).to_i32_floor() + && let point = (position - surface_location.as_logical()).to_i32_round() && region.contains(point) { constraint.activate(); diff --git a/src/wayland/handlers/xdg_shell/mod.rs b/src/wayland/handlers/xdg_shell/mod.rs index b8288019..98b8acf6 100644 --- a/src/wayland/handlers/xdg_shell/mod.rs +++ b/src/wayland/handlers/xdg_shell/mod.rs @@ -317,45 +317,8 @@ impl XdgShellHandler for State { } fn toplevel_destroyed(&mut self, surface: ToplevelSurface) { - for (popup, _) in smithay::desktop::PopupManager::popups_for_surface(surface.wl_surface()) { - if let smithay::desktop::PopupKind::Xdg(ref xdg_popup) = popup { - xdg_popup.send_popup_done(); - } - } - let (output, clients) = { let mut shell = self.common.shell.write(); - - for seat in shell.seats.iter() { - if let Some(data) = seat.user_data().get::() { - let mut should_ungrab = false; - let grab = data.take(); - if let Some(ref grab) = grab { - if grab.has_ended() { - should_ungrab = true; - } else if let Some(target) = grab.current_grab() { - if let Some(wl_surface) = target.wl_surface() { - if wl_surface.as_ref() == surface.wl_surface() - || smithay::desktop::PopupManager::popups_for_surface( - surface.wl_surface(), - ) - .any(|(p, _)| p.wl_surface() == wl_surface.as_ref()) - { - should_ungrab = true; - } - } - } - } - if should_ungrab { - if let Some(mut grab) = grab { - grab.ungrab(PopupUngrabStrategy::All); - } - } else { - data.set(grab); - } - } - } - let seat = shell.seats.last_active().clone(); // Clean up pending_windows for surfaces that were never mapped. diff --git a/src/wayland/protocols/corner_radius.rs b/src/wayland/protocols/corner_radius.rs index be6f9ca0..5baafa23 100644 --- a/src/wayland/protocols/corner_radius.rs +++ b/src/wayland/protocols/corner_radius.rs @@ -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_manager_v1, cosmic_corner_radius_toplevel_v1, }; -use smithay::desktop::utils::bbox_from_surface_tree; -use smithay::reexports::wayland_protocols::xdg::shell::server::xdg_popup::XdgPopup; -use smithay::reexports::wayland_protocols_wlr::layer_shell::v1::server::zwlr_layer_surface_v1::ZwlrLayerSurfaceV1; -use smithay::reexports::wayland_server::New; -use smithay::reexports::wayland_server::protocol::wl_surface::WlSurface; -use smithay::utils::{HookId, Logical, Point, Rectangle}; +use smithay::utils::HookId; use smithay::wayland::compositor::Cacheable; use smithay::wayland::compositor::add_pre_commit_hook; use smithay::wayland::compositor::with_states; -use smithay::wayland::shell::wlr_layer::WlrLayerShellHandler; -use smithay::wayland::shell::xdg::{SurfaceCachedState, XdgShellSurfaceUserData}; +use smithay::wayland::shell::xdg::SurfaceCachedState; use smithay::{ reexports::{ wayland_protocols::xdg::shell::server::xdg_toplevel::XdgToplevel, @@ -27,7 +18,6 @@ use std::sync::Mutex; use wayland_backend::server::GlobalId; type ToplevelHookId = Mutex)>>; -type LayerHookId = Mutex)>>; #[derive(Debug)] pub struct CornerRadiusState { @@ -43,13 +33,12 @@ impl CornerRadiusState { + Dispatch< cosmic_corner_radius_toplevel_v1::CosmicCornerRadiusToplevelV1, CornerRadiusData, - > + Dispatch - + CornerRadiusHandler + > + CornerRadiusHandler + 'static, { let global = dh .create_global::( - 2, + 1, (), ); 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 set_corner_radius(&mut self, data: &CornerRadiusData); - fn unset_corner_radius(&mut self, data: &CornerRadiusData); - fn set_padding(&mut self, data: &CornerRadiusData); - fn unset_padding(&mut self, data: &CornerRadiusData); + fn set_corner_radius( + &mut self, + toplevel: &cosmic_corner_radius_toplevel_v1::CosmicCornerRadiusToplevelV1, + data: &CornerRadiusData, + ); + fn unset_corner_radius( + &mut self, + toplevel: &cosmic_corner_radius_toplevel_v1::CosmicCornerRadiusToplevelV1, + data: &CornerRadiusData, + ); } impl GlobalDispatch @@ -77,7 +72,6 @@ where D: GlobalDispatch + Dispatch + Dispatch - + Dispatch + CornerRadiusHandler + 'static, { @@ -102,7 +96,6 @@ where D: GlobalDispatch + Dispatch + Dispatch - + Dispatch + CornerRadiusHandler + 'static, { @@ -122,71 +115,6 @@ where } cosmic_corner_radius_manager_v1::Request::GetCornerRadius { id, toplevel } => { if let Some(surface) = state.xdg_shell_state().get_toplevel(&toplevel) { - new_xdg( - surface.wl_surface(), - CornerRadiusSurface::Toplevel(surface.xdg_toplevel().downgrade()), - resource, - data_init, - id, - ) - } // TODO: can this fail? - } - cosmic_corner_radius_manager_v1::Request::GetCornerRadiusSurface { id, surface } => { - if let Some(toplevel) = - state - .xdg_shell_state() - .toplevel_surfaces() - .iter() - .find(|toplevel| { - toplevel - .xdg_toplevel() - .data::() - .unwrap() - .xdg_surface() - == &surface - }) - { - new_xdg( - toplevel.wl_surface(), - CornerRadiusSurface::Toplevel(toplevel.xdg_toplevel().downgrade()), - resource, - data_init, - id, - ) - } else if let Some(popup) = - state - .xdg_shell_state() - .popup_surfaces() - .iter() - .find(|popup| { - popup - .xdg_popup() - .data::() - .unwrap() - .xdg_surface() - == &surface - }) - { - new_xdg( - popup.wl_surface(), - CornerRadiusSurface::Popup(popup.xdg_popup().downgrade()), - resource, - data_init, - id, - ) - } else { - resource.post_error( - cosmic_corner_radius_manager_v1::Error::NoRole as u32, - "xdg_surface has no known role object".to_string(), - ); - } - } - cosmic_corner_radius_manager_v1::Request::GetCornerRadiusLayer { id, layer } => { - if let Some(surface) = state - .shell_state() - .layer_surfaces() - .find(|surface| surface.shell_surface() == &layer) - { let radius_exists = with_states(surface.wl_surface(), |surface_data| { let hook_id = surface_data .data_map @@ -196,33 +124,63 @@ where }); if radius_exists.unwrap_or_default() { resource.post_error( - cosmic_corner_radius_manager_v1::Error::CornerRadiusExists as u32, - format!( - "{resource:?} CosmicCornerRadiusToplevelV1 object already exists for the surface" - ), - ); + cosmic_corner_radius_manager_v1::Error::CornerRadiusExists as u32, + format!("{resource:?} CosmicCornerRadiusToplevelV1 object already exists for the surface"), + ); } let data = Mutex::new(CornerRadiusInternal { - surface: CornerRadiusSurface::Layer(layer.downgrade()), + toplevel: toplevel.downgrade(), corners: None, - padding: None, }); let obj = data_init.init(id, data); let obj_downgrade = obj.downgrade(); let needs_hook = radius_exists.is_none(); if needs_hook { - let hook_id = - add_pre_commit_hook::(surface.wl_surface(), layer_radius_hook); + let hook_id = add_pre_commit_hook::( + surface.wl_surface(), + move |_, _dh, surface| { + let corner_radii_too_big = with_states(surface, |surface_data| { + let corners = *surface_data + .cached_state + .get::() + .pending(); + surface_data + .cached_state + .get::() + .pending() + .geometry + .zip(corners.0.as_ref()) + .is_some_and(|(geo, corners)| { + let half_min_dim = + u8::try_from(geo.size.w.min(geo.size.h) / 2) + .unwrap_or(u8::MAX); + corners.top_right > half_min_dim + || corners.top_left > half_min_dim + || corners.bottom_right > half_min_dim + || corners.bottom_left > half_min_dim + }) + }); + + if corner_radii_too_big { + obj.post_error( + cosmic_corner_radius_toplevel_v1::Error::RadiusTooLarge + as u32, + format!("{obj:?} corner radius too large"), + ); + } + }, + ); + with_states(surface.wl_surface(), |surface_data| { let hook_ids = surface_data .data_map - .get_or_insert_threadsafe(|| LayerHookId::new(None)); + .get_or_insert_threadsafe(|| ToplevelHookId::new(None)); let mut guard = hook_ids.lock().unwrap(); *guard = Some((hook_id, obj_downgrade)); }); } - } // TODO: can this fail? + } } _ => unimplemented!(), } @@ -239,55 +197,6 @@ where } } -fn new_xdg( - wl_surface: &WlSurface, - surface: CornerRadiusSurface, - resource: &cosmic_corner_radius_manager_v1::CosmicCornerRadiusManagerV1, - data_init: &mut smithay::reexports::wayland_server::DataInit<'_, D>, - id: New, -) where - D: GlobalDispatch - + Dispatch - + Dispatch - + CornerRadiusHandler - + 'static, -{ - let radius_exists = with_states(wl_surface, |surface_data| { - let hook_id = surface_data - .data_map - .get_or_insert_threadsafe(|| ToplevelHookId::new(None)); - let guard = hook_id.lock().unwrap(); - guard.as_ref().map(|(_, t)| t.upgrade().is_ok()) - }); - if radius_exists.unwrap_or_default() { - resource.post_error( - cosmic_corner_radius_manager_v1::Error::CornerRadiusExists as u32, - format!( - "{resource:?} CosmicCornerRadiusToplevelV1 object already exists for the surface" - ), - ); - } - let data = Mutex::new(CornerRadiusInternal { - surface, - corners: None, - padding: None, - }); - let obj = data_init.init(id, data); - let obj_downgrade = obj.downgrade(); - - let needs_hook = radius_exists.is_none(); - if needs_hook { - let hook_id = add_pre_commit_hook::(wl_surface, xdg_radius_hook); - with_states(wl_surface, |surface_data| { - let hook_ids = surface_data - .data_map - .get_or_insert_threadsafe(|| ToplevelHookId::new(None)); - let mut guard = hook_ids.lock().unwrap(); - *guard = Some((hook_id, obj_downgrade)); - }); - } -} - impl Dispatch for CornerRadiusState @@ -312,35 +221,30 @@ where let mut guard = data.lock().unwrap(); guard.corners = None; - 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 { + let Ok(toplevel) = guard.toplevel.upgrade() else { return; }; - with_states(&wl_surface, |surface_data| { - if let Some(hook_ids_mutex) = surface_data.data_map.get::() { - let mut hook_id = hook_ids_mutex.lock().unwrap(); - *hook_id = None; - } + if let Some(surface) = state.xdg_shell_state().get_toplevel(&toplevel) { + with_states(surface.wl_surface(), |surface_data| { + if let Some(hook_ids_mutex) = surface_data.data_map.get::() + { + let mut hook_id = hook_ids_mutex.lock().unwrap(); + *hook_id = None; + } + }); + } - let mut cached = surface_data.cached_state.get::(); - let pending = cached.pending(); - *pending = CacheableCorners(None); - }); + if let Some(surface) = state.xdg_shell_state().get_toplevel(&toplevel) { + with_states(surface.wl_surface(), |s| { + let mut cached = s.cached_state.get::(); + let pending = cached.pending(); + *pending = CacheableCorners(None); + }); + } drop(guard); - state.unset_corner_radius(data); + state.unset_corner_radius(resource, data); } cosmic_corner_radius_toplevel_v1::Request::SetRadius { top_left, @@ -350,388 +254,66 @@ where } => { let mut guard = data.lock().unwrap(); guard.set_corner_radius(top_left, top_right, bottom_right, bottom_left); - - 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 { + let Ok(toplevel) = guard.toplevel.upgrade() else { resource.post_error( cosmic_corner_radius_toplevel_v1::Error::ToplevelDestroyed as u32, format!("{:?} No toplevel found", resource), ); return; }; - with_states(&wl_surface, |s| { - let mut cached = s.cached_state.get::(); - let pending = cached.pending(); - *pending = CacheableCorners(guard.corners); - }); + + if let Some(surface) = state.xdg_shell_state().get_toplevel(&toplevel) { + with_states(surface.wl_surface(), |s| { + let mut cached = s.cached_state.get::(); + let pending = cached.pending(); + *pending = CacheableCorners(guard.corners); + }); + } drop(guard); - state.set_corner_radius(data); + state.set_corner_radius(resource, data); } cosmic_corner_radius_toplevel_v1::Request::UnsetRadius => { let mut guard = data.lock().unwrap(); guard.corners = None; - - 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 { + let Ok(toplevel) = guard.toplevel.upgrade() else { resource.post_error( cosmic_corner_radius_toplevel_v1::Error::ToplevelDestroyed as u32, format!("{:?} No toplevel found", resource), ); - return; }; - with_states(&wl_surface, |s| { - let mut cached = s.cached_state.get::(); - let pending = cached.pending(); - *pending = CacheableCorners(None); - }); - drop(guard); - - state.unset_corner_radius(data); - } - _ => unimplemented!(), - } - } -} - -impl Dispatch - for CornerRadiusState -where - D: GlobalDispatch - + Dispatch - + Dispatch - + CornerRadiusHandler - + 'static, -{ - fn request( - state: &mut D, - _client: &Client, - resource: &cosmic_corner_radius_layer_v1::CosmicCornerRadiusLayerV1, - request: ::Request, - data: &CornerRadiusData, - _dhandle: &DisplayHandle, - _data_init: &mut smithay::reexports::wayland_server::DataInit<'_, D>, - ) { - match request { - cosmic_corner_radius_layer_v1::Request::Destroy => { - let mut guard = data.lock().unwrap(); - guard.corners = None; - - let CornerRadiusSurface::Layer(layer_surface) = &guard.surface else { - unreachable!("corner_radius_layer without layer shell?"); - }; - let Some(layer_surface) = layer_surface.upgrade().ok().and_then(|layer| { - state - .shell_state() - .layer_surfaces() - .find(|s| s.shell_surface() == &layer) - }) else { - resource.post_error( - cosmic_corner_radius_layer_v1::Error::LayerDestroyed as u32, - format!("{:?} No layer found", resource), - ); - return; - }; - - with_states(layer_surface.wl_surface(), |surface_data| { - if let Some(hook_ids_mutex) = surface_data.data_map.get::() { - let mut hook_id = hook_ids_mutex.lock().unwrap(); - *hook_id = None; - } - - let mut cached = surface_data.cached_state.get::(); - let pending = cached.pending(); - *pending = CacheableCorners(None); - - let mut cached = surface_data.cached_state.get::(); - let pending = cached.pending(); - *pending = CacheablePadding(None); - }); - drop(guard); - - state.unset_corner_radius(data); - state.unset_padding(data); - } - cosmic_corner_radius_layer_v1::Request::SetRadius { - top_left, - top_right, - bottom_right, - bottom_left, - } => { - let mut guard = data.lock().unwrap(); - guard.set_corner_radius(top_left, top_right, bottom_right, bottom_left); - - let CornerRadiusSurface::Layer(layer_surface) = &guard.surface else { - unreachable!("corner_radius_layer without layer shell?"); - }; - let Some(layer_surface) = layer_surface.upgrade().ok().and_then(|layer| { - state - .shell_state() - .layer_surfaces() - .find(|s| s.shell_surface() == &layer) - }) else { - resource.post_error( - cosmic_corner_radius_layer_v1::Error::LayerDestroyed as u32, - format!("{:?} No layer found", resource), - ); - return; - }; - - with_states(layer_surface.wl_surface(), |s| { - let mut cached = s.cached_state.get::(); - let pending = cached.pending(); - *pending = CacheableCorners(guard.corners); - }); - drop(guard); - - state.set_corner_radius(data); - } - cosmic_corner_radius_layer_v1::Request::UnsetRadius => { - let mut guard = data.lock().unwrap(); - guard.corners = None; - - let CornerRadiusSurface::Layer(layer_surface) = &guard.surface else { - unreachable!("corner_radius_layer without layer shell?"); - }; - let Some(layer_surface) = layer_surface.upgrade().ok().and_then(|layer| { - state - .shell_state() - .layer_surfaces() - .find(|s| s.shell_surface() == &layer) - }) else { - resource.post_error( - cosmic_corner_radius_layer_v1::Error::LayerDestroyed as u32, - format!("{:?} No layer found", resource), - ); - return; - }; - - with_states(layer_surface.wl_surface(), |s| { - let mut cached = s.cached_state.get::(); - let pending = cached.pending(); - *pending = CacheableCorners(None); - }); - drop(guard); - - state.unset_corner_radius(data); - } - cosmic_corner_radius_layer_v1::Request::SetPadding { - top, - right, - bottom, - left, - } => { - let mut guard = data.lock().unwrap(); - guard.set_padding(top, right, bottom, left); - - let CornerRadiusSurface::Layer(layer_surface) = &guard.surface else { - unreachable!("corner_radius_layer without layer shell?"); - }; - let Some(layer_surface) = layer_surface.upgrade().ok().and_then(|layer| { - state - .shell_state() - .layer_surfaces() - .find(|s| s.shell_surface() == &layer) - }) else { - resource.post_error( - cosmic_corner_radius_layer_v1::Error::LayerDestroyed as u32, - format!("{:?} No layer found", resource), - ); - return; - }; - - with_states(layer_surface.wl_surface(), |s| { - let mut cached = s.cached_state.get::(); - let pending = cached.pending(); - *pending = CacheablePadding(guard.padding); - }); - drop(guard); - - state.set_padding(data); - } - cosmic_corner_radius_layer_v1::Request::UnsetPadding => { - let mut guard = data.lock().unwrap(); - guard.corners = None; - - let CornerRadiusSurface::Layer(layer_surface) = &guard.surface else { - unreachable!("corner_radius_layer without layer shell?"); - }; - let Some(layer_surface) = layer_surface.upgrade().ok().and_then(|layer| { - state - .shell_state() - .layer_surfaces() - .find(|s| s.shell_surface() == &layer) - }) else { - resource.post_error( - cosmic_corner_radius_layer_v1::Error::LayerDestroyed as u32, - format!("{:?} No layer found", resource), - ); - return; - }; - - with_states(layer_surface.wl_surface(), |s| { - let mut cached = s.cached_state.get::(); - let pending = cached.pending(); - *pending = CacheablePadding(None); - }); - drop(guard); - - state.unset_padding(data); - } - _ => unimplemented!(), - } - } -} - -fn xdg_radius_hook(_state: &mut D, _dh: &DisplayHandle, surface: &WlSurface) { - with_states(surface, |surface_data| { - let corners = *surface_data - .cached_state - .get::() - .pending(); - if surface_data - .cached_state - .get::() - .pending() - .geometry - .zip(corners.0.as_ref()) - .is_some_and(|(geo, corners)| { - let half_min_dim = (geo.size.w.min(geo.size.h) / 2) as u32; - corners.top_right > half_min_dim - || corners.top_left > half_min_dim - || corners.bottom_right > half_min_dim - || corners.bottom_left > half_min_dim - }) - && let Some(hook) = surface_data.data_map.get::() - { - let hook_ref = hook.lock().unwrap(); - if let Some((_, obj)) = hook_ref.as_ref() - && let Ok(obj) = obj.upgrade() - { - obj.post_error( - cosmic_corner_radius_toplevel_v1::Error::RadiusTooLarge as u32, - format!("{obj:?} corner radius too large"), - ); - } - } - }); -} - -fn pad_rect( - mut rect: Rectangle, - padding: &Padding, -) -> Option> { - rect.size.h = rect.size.h.checked_sub(padding.top)?; - rect.loc.x += padding.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(_state: &mut D, _dh: &DisplayHandle, surface: &WlSurface) { - let bbox = bbox_from_surface_tree(surface, Point::default()); - with_states(surface, |surface_data| { - let corners = *surface_data - .cached_state - .get::() - .pending(); - let padding = *surface_data - .cached_state - .get::() - .pending(); - let empty = Padding::default(); - let Some(padded_box) = pad_rect(bbox, padding.0.as_ref().unwrap_or(&empty)) else { - if let Some(hook) = surface_data.data_map.get::() { - let hook_ref = hook.lock().unwrap(); - if let Some((_, obj)) = hook_ref.as_ref() - && let Ok(obj) = obj.upgrade() - { - obj.post_error( - cosmic_corner_radius_layer_v1::Error::PaddingTooLarge as u32, - format!("{obj:?} padding too large"), - ); + if let Some(surface) = state.xdg_shell_state().get_toplevel(&toplevel) { + with_states(surface.wl_surface(), |s| { + let mut cached = s.cached_state.get::(); + let pending = cached.pending(); + *pending = CacheableCorners(None); + }); } - } - return; - }; + drop(guard); - if corners.0.as_ref().is_some_and(|corners| { - let half_min_dim = (padded_box.size.w.min(padded_box.size.h) / 2) as u32; - corners.top_right > half_min_dim - || corners.top_left > half_min_dim - || corners.bottom_right > half_min_dim - || corners.bottom_left > half_min_dim - }) && let Some(hook) = surface_data.data_map.get::() - { - let hook_ref = hook.lock().unwrap(); - if let Some((_, obj)) = hook_ref.as_ref() - && let Ok(obj) = obj.upgrade() - { - obj.post_error( - cosmic_corner_radius_layer_v1::Error::RadiusTooLarge as u32, - format!("{obj:?} corner radius too large"), - ); + state.unset_corner_radius(resource, data); } + _ => unimplemented!(), } - }); + } } pub type CornerRadiusData = Mutex; -#[derive(Debug)] -pub enum CornerRadiusSurface { - Toplevel(Weak), - Popup(Weak), - Layer(Weak), -} - #[derive(Debug)] pub struct CornerRadiusInternal { - pub surface: CornerRadiusSurface, + pub toplevel: Weak, pub corners: Option, - pub padding: Option, } #[derive(Debug, Copy, Clone)] pub struct Corners { - pub top_left: u32, - pub top_right: u32, - pub bottom_right: u32, - pub bottom_left: u32, -} - -#[derive(Debug, Copy, Clone, Default)] -pub struct Padding { - pub top: i32, - pub right: i32, - pub bottom: i32, - pub left: i32, + pub top_left: u8, + pub top_right: u8, + pub bottom_right: u8, + pub bottom_left: u8, } #[derive(Default, Debug, Copy, Clone)] @@ -746,18 +328,6 @@ impl Cacheable for CacheableCorners { } } -#[derive(Default, Debug, Copy, Clone)] -pub struct CacheablePadding(pub Option); - -impl Cacheable for CacheablePadding { - fn commit(&mut self, _dh: &DisplayHandle) -> Self { - *self - } - fn merge_into(self, into: &mut Self, _dh: &DisplayHandle) { - *into = self; - } -} - impl CornerRadiusInternal { fn set_corner_radius( &mut self, @@ -767,23 +337,13 @@ impl CornerRadiusInternal { bottom_left: u32, ) { let corners = Corners { - top_left, - top_right, - bottom_right, - bottom_left, + top_left: top_left.clamp(u8::MIN as u32, u8::MAX as u32) as u8, + top_right: top_right.clamp(u8::MIN as u32, u8::MAX as u32) as u8, + bottom_right: bottom_right.clamp(u8::MIN as u32, u8::MAX as u32) as u8, + bottom_left: bottom_left.clamp(u8::MIN as u32, u8::MAX as u32) as u8, }; 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 { @@ -797,9 +357,6 @@ macro_rules! delegate_corner_radius { smithay::reexports::wayland_server::delegate_dispatch!($(@< $( $lt $( : $clt $(+ $dlt )* )? ),+ >)? $ty: [ cosmic_protocols::corner_radius::v1::server::cosmic_corner_radius_toplevel_v1::CosmicCornerRadiusToplevelV1: CornerRadiusData ] => $crate::wayland::protocols::corner_radius::CornerRadiusState); - smithay::reexports::wayland_server::delegate_dispatch!($(@< $( $lt $( : $clt $(+ $dlt )* )? ),+ >)? $ty: [ - cosmic_protocols::corner_radius::v1::server::cosmic_corner_radius_layer_v1::CosmicCornerRadiusLayerV1: CornerRadiusData - ] => $crate::wayland::protocols::corner_radius::CornerRadiusState); }; } pub(crate) use delegate_corner_radius; diff --git a/src/wayland/protocols/drm.rs b/src/wayland/protocols/drm.rs index c86e5ff0..87eeb0db 100644 --- a/src/wayland/protocols/drm.rs +++ b/src/wayland/protocols/drm.rs @@ -180,7 +180,7 @@ where Modifier::Invalid, DmabufFlags::empty(), ); - dma.add_plane(name, offset0 as u32, stride0 as u32); + dma.add_plane(name, 0, offset0 as u32, stride0 as u32); match dma.build() { Some(dmabuf) => { match state.dmabuf_imported(&data.dmabuf_global, dmabuf.clone()) { diff --git a/src/wayland/protocols/keyboard_layout.rs b/src/wayland/protocols/keyboard_layout.rs deleted file mode 100644 index f3a44f0a..00000000 --- a/src/wayland/protocols/keyboard_layout.rs +++ /dev/null @@ -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)>, -} - -impl KeyboardLayoutState { - pub fn new(dh: &DisplayHandle, client_filter: F) -> Self - where - D: GlobalDispatch + 'static, - F: for<'a> Fn(&'a Client) -> bool + Send + Sync + 'static, - { - let global = dh.create_global::( - 1, - LayoutGlobalData { - filter: Box::new(client_filter), - }, - ); - KeyboardLayoutState { - global, - keyboard_layouts: Vec::new(), - } - } - - pub fn refresh(state: &mut D) - where - D: SeatHandler + KeyboardLayoutHandler + 'static, - { - let mut keyboard_layouts = mem::take(&mut state.keyboard_layout_state().keyboard_layouts); - for (keyboard_layout, last_layout) in &mut keyboard_layouts { - if let Some(data) = keyboard_layout.data::>() - && let Some(handle) = &data.handle - { - let active_layout = handle.with_xkb_state(state, |context| { - context.xkb().lock().unwrap().active_layout() - }); - if *last_layout != Some(active_layout) { - keyboard_layout.group(active_layout.0); - *last_layout = Some(active_layout); - } - } - } - state.keyboard_layout_state().keyboard_layouts = keyboard_layouts; - } -} - -pub struct LayoutGlobalData { - filter: Box Fn(&'a Client) -> bool + Send + Sync>, -} - -struct LayoutManagerData; - -impl GlobalDispatch2 for LayoutGlobalData -where - D: Dispatch + 'static, -{ - fn bind( - &self, - _state: &mut D, - _handle: &DisplayHandle, - _client: &Client, - resource: New, - data_init: &mut DataInit<'_, D>, - ) { - data_init.init(resource, LayoutManagerData); - } - - fn can_view(&self, client: &Client) -> bool { - (self.filter)(client) - } -} - -impl Dispatch2 for LayoutManagerData -where - D: Dispatch>, - D: 'static, - D: SeatHandler, - D: KeyboardLayoutHandler, -{ - fn request( - &self, - state: &mut D, - _client: &Client, - _resource: &ZcosmicKeyboardLayoutManagerV1, - request: zcosmic_keyboard_layout_manager_v1::Request, - _dhandle: &DisplayHandle, - data_init: &mut DataInit<'_, D>, - ) { - match request { - zcosmic_keyboard_layout_manager_v1::Request::GetKeyboardLayout { - keyboard_layout, - keyboard, - } => { - let handle = KeyboardHandle::::from_resource(&keyboard); - let active_layout = handle.as_ref().map(|handle| { - handle.with_xkb_state(state, |context| { - context.xkb().lock().unwrap().active_layout() - }) - }); - let keyboard_layout = data_init.init(keyboard_layout, LayoutUserData { handle }); - if let Some(layout) = active_layout { - keyboard_layout.group(layout.0); - } - state - .keyboard_layout_state() - .keyboard_layouts - .push((keyboard_layout, active_layout)); - } - zcosmic_keyboard_layout_manager_v1::Request::Destroy => {} - _ => unreachable!(), - } - } -} - -#[doc(hidden)] -struct LayoutUserData { - handle: Option>, -} - -impl Dispatch2 for LayoutUserData -where - D: 'static, - D: SeatHandler, - D: KeyboardLayoutHandler, -{ - fn request( - &self, - state: &mut D, - _client: &Client, - _resource: &ZcosmicKeyboardLayoutV1, - request: zcosmic_keyboard_layout_v1::Request, - _dhandle: &DisplayHandle, - _data_init: &mut DataInit<'_, D>, - ) { - match request { - zcosmic_keyboard_layout_v1::Request::SetGroup { group } => { - if let Some(handle) = self.handle.as_ref() { - handle.with_xkb_state(state, |mut context| { - context.set_layout(Layout(group)); - }); - } - } - zcosmic_keyboard_layout_v1::Request::Destroy => {} - _ => unreachable!(), - } - } - - fn destroyed( - &self, - state: &mut D, - _client: ClientId, - keyboard_layout: &ZcosmicKeyboardLayoutV1, - ) { - let keyboard_layouts = &mut state.keyboard_layout_state().keyboard_layouts; - if let Some(idx) = keyboard_layouts - .iter() - .position(|(x, _)| x == keyboard_layout) - { - keyboard_layouts.remove(idx); - } - } -} diff --git a/src/wayland/protocols/mod.rs b/src/wayland/protocols/mod.rs index c6c0234a..57e05b8a 100644 --- a/src/wayland/protocols/mod.rs +++ b/src/wayland/protocols/mod.rs @@ -4,7 +4,6 @@ pub mod a11y; pub mod corner_radius; pub mod drm; pub mod image_capture_source; -pub mod keyboard_layout; pub mod output_configuration; pub mod output_power; pub mod overlap_notify; diff --git a/src/xwayland.rs b/src/xwayland.rs index 25b8b0e0..aba65ef5 100644 --- a/src/xwayland.rs +++ b/src/xwayland.rs @@ -807,17 +807,9 @@ impl XwmHandler for State { *context, ); } - let fullscreen = window.is_fullscreen().then(|| seat.active_output()); - let maximized = window.is_maximized(); - if let Some(pending) = shell - .pending_windows - .iter_mut() - .find(|w| w.surface == window) - { - pending.seat = seat; - pending.fullscreen = fullscreen; - pending.maximized = maximized; - } else { + if !shell.pending_windows.iter().any(|w| w.surface == window) { + let fullscreen = window.is_fullscreen().then(|| seat.active_output()); + let maximized = window.is_maximized(); let surface = CosmicSurface::from(window); shell.pending_windows.push(PendingWindow { surface,