From 1bcba7e30cb082df1629f8ecdb02123f1403439b Mon Sep 17 00:00:00 2001 From: Ashley Wulber Date: Thu, 16 Apr 2026 23:01:58 -0400 Subject: [PATCH] wip: corner radius v2 --- Cargo.lock | 4 +- Cargo.toml | 2 +- src/lib.rs | 4 +- .../wayland/event_loop/mod.rs | 13 +- .../wayland/event_loop/state.rs | 123 ++++++++++++------ .../wayland/handlers/shell/corner_radius.rs | 55 +++++--- winit/src/platform_specific/wayland/mod.rs | 1 - 7 files changed, 137 insertions(+), 65 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index 38f19187..11a85d77 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -1294,7 +1294,7 @@ dependencies = [ [[package]] name = "cosmic-client-toolkit" version = "0.2.0" -source = "git+https://github.com/pop-os/cosmic-protocols?rev=160b086#160b086abe03cd34a8a375d7fbe47b24308d1f38" +source = "git+https://github.com/pop-os/cosmic-protocols?rev=a7d2d7a#a7d2d7a6188425f699eaddf7a025617ce56068ae" dependencies = [ "bitflags 2.11.0", "cosmic-protocols", @@ -1307,7 +1307,7 @@ dependencies = [ [[package]] name = "cosmic-protocols" version = "0.2.0" -source = "git+https://github.com/pop-os/cosmic-protocols?rev=160b086#160b086abe03cd34a8a375d7fbe47b24308d1f38" +source = "git+https://github.com/pop-os/cosmic-protocols?rev=a7d2d7a#a7d2d7a6188425f699eaddf7a025617ce56068ae" dependencies = [ "bitflags 2.11.0", "wayland-backend", diff --git a/Cargo.toml b/Cargo.toml index e9f43f7f..b5e1b8d7 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -274,7 +274,7 @@ tiny-skia = { version = "0.11", default-features = false, features = [ "std", "simd", ] } -cctk = { git = "https://github.com/pop-os/cosmic-protocols", package = "cosmic-client-toolkit", rev = "c253ec1" } +cctk = { git = "https://github.com/pop-os/cosmic-protocols", package = "cosmic-client-toolkit", rev = "a7d2d7a" } softbuffer = { git = "https://github.com/pop-os/softbuffer", tag = "cosmic-4.0" } syntect = "5.2" tokio = "1.0" diff --git a/src/lib.rs b/src/lib.rs index 46685d76..6e57128b 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -682,7 +682,9 @@ pub use error::Error; pub use event::Event; pub use executor::Executor; pub use font::Font; -pub use renderer::{Renderer, graphics::geometry}; +pub use renderer::Renderer; +#[cfg(feature = "canvas")] +pub use renderer::graphics::geometry; pub use task::Task; pub use window::Window; diff --git a/winit/src/platform_specific/wayland/event_loop/mod.rs b/winit/src/platform_specific/wayland/event_loop/mod.rs index 61bf12ea..fea1c28c 100644 --- a/winit/src/platform_specific/wayland/event_loop/mod.rs +++ b/winit/src/platform_specific/wayland/event_loop/mod.rs @@ -33,7 +33,7 @@ use cctk::{ reexports::{ calloop::{self, EventLoop}, client::{ - ConnectError, Connection, Proxy, globals::registry_queue_init, + Connection, Proxy, globals::registry_queue_init, }, }, registry::RegistryState, @@ -46,8 +46,6 @@ use cctk::{ }; use raw_window_handle::HasDisplayHandle; use state::{FrameStatus, SctkWindow, send_event}; -#[cfg(feature = "a11y")] -use std::sync::{Arc, Mutex}; use std::{ collections::{HashMap, HashSet}, fmt::Debug, @@ -71,8 +69,8 @@ pub struct SctkEventLoop { pub(crate) state: SctkState, } +#[derive(Debug)] pub enum Error { - Connect(ConnectError), Calloop(calloop::Error), Global(GlobalError), NoDisplayHandle, @@ -129,7 +127,7 @@ impl SctkEventLoop { crate::platform_specific::Action::ResizeWindow(id) => { if let Some((_, v)) = state.windows.iter() .find(|w| w.id == id) - .map(|w| w.corner_radius.as_ref()) + .map(|w| state.corner_radii.get(&id)) .unwrap_or_default() { _ = state.handle_action(iced_runtime::platform_specific::wayland::Action::RoundedCorners(id, *v)); } @@ -138,7 +136,7 @@ impl SctkEventLoop { window, id, ) => { - state.windows.push(SctkWindow { window, id, corner_radius: Default::default() }); + state.windows.push(SctkWindow { window, id }); if let Some(v) = state.pending_corner_radius.remove(&id) { _ = state.handle_action(iced_runtime::platform_specific::wayland::Action::RoundedCorners(id, Some(v))); } @@ -350,7 +348,7 @@ impl SctkEventLoop { ), corner_radius_manager: registry_state.bind_one::( &qh, - 1..=1, + 1..=2, (), ).ok(), toplevel_manager: ToplevelManagerState::try_new( @@ -377,6 +375,7 @@ impl SctkEventLoop { seats: Vec::new(), windows: Vec::new(), blur_surfaces: HashMap::new(), + corner_radii: HashMap::new(), layer_surfaces: Vec::new(), popups: Vec::new(), lock_surfaces: Vec::new(), diff --git a/winit/src/platform_specific/wayland/event_loop/state.rs b/winit/src/platform_specific/wayland/event_loop/state.rs index 6e39fdc2..11f987f9 100644 --- a/winit/src/platform_specific/wayland/event_loop/state.rs +++ b/winit/src/platform_specific/wayland/event_loop/state.rs @@ -1,10 +1,7 @@ use crate::{ Control, handlers::{ - activation::IcedRequestData, - ext_background_effect, - overlap::{OverlapNotificationV1, OverlapNotifyV1}, - text_input::{Preedit, TextInputManager}, + activation::IcedRequestData, ext_background_effect, overlap::{OverlapNotificationV1, OverlapNotifyV1}, shell::corner_radius::CornerRadiusWrapper, text_input::{Preedit, TextInputManager} }, platform_specific::{ Event, @@ -65,7 +62,7 @@ use cctk::{ wl_surface::{self, WlSurface}, wl_touch::WlTouch, }, - }, + }, protocols_wlr::layer_shell::v1::client::zwlr_layer_surface_v1::ZwlrLayerSurfaceV1, }, registry::RegistryState, seat::{ @@ -341,22 +338,13 @@ pub struct SctkPopupData { pub(crate) grab: bool, } -#[derive(Debug)] -pub struct MyCosmicCornerRadiusToplevelV1(CosmicCornerRadiusToplevelV1); - -impl Drop for MyCosmicCornerRadiusToplevelV1 { - fn drop(&mut self) { - self.0.destroy(); - } -} #[derive(Debug, Clone)] -pub struct SctkCornerRadius(Arc); +pub struct SctkCornerRadius(Arc); pub struct SctkWindow { pub(crate) window: Arc, pub(crate) id: core::window::Id, - pub(crate) corner_radius: Option<(SctkCornerRadius, Option)>, } impl SctkWindow { @@ -444,6 +432,7 @@ pub struct SctkState { pub(crate) subsurfaces: Vec, pub(crate) lock_surfaces: Vec, pub(crate) blur_surfaces: HashMap>, + pub(crate) corner_radii: HashMap)>, pub(crate) touch_points: HashMap, /// Window updates, which are coming from SCTK or the compositor, which require @@ -1137,6 +1126,7 @@ impl SctkState { s.destroy(); } } + _ = self.corner_radii.remove(&id); let (removed, remaining): (Vec<_>, Vec<_>) = self .subsurfaces @@ -1396,6 +1386,8 @@ impl SctkState { s.destroy(); } } + _ = self.corner_radii.remove(&id); + let (removed, remaining): (Vec<_>, Vec<_>) = self .subsurfaces @@ -1524,6 +1516,8 @@ impl SctkState { s.destroy(); } } + _ = self.corner_radii.remove(&id); + let (removed, remaining): (Vec<_>, Vec<_>) = self .subsurfaces .drain(..) @@ -1609,6 +1603,8 @@ impl SctkState { s.destroy(); } } + _ = self.corner_radii.remove(&id); + if let Some((wl_surface, f)) = self.seats.iter_mut().find(|f| { f.kbd_focus.as_ref().is_some_and(|f| *f == destroyed) }).and_then(|f| Some((parent, &mut f.kbd_focus))) { @@ -1636,8 +1632,25 @@ impl SctkState { } Action::RoundedCorners(id, v) => { if let Some(manager) = self.corner_radius_manager.as_ref() { - if let Some(w) = self.windows.iter_mut().find(|w| w.id == id) { - let geo_size: LogicalSize = w.window.surface_size().cast::().to_logical(w.window.scale_factor()); + enum Surface { + Xdg(XdgSurface, Option), + Wlr(ZwlrLayerSurfaceV1), + } + let s = if let Some(w) = self.windows.iter_mut().find(|w| w.id == id) { + Some((Surface::Xdg(w.xdg_surface(&self.connection), Some(w.xdg_toplevel(&self.connection))), w.window.surface_size().cast::().to_logical(w.window.scale_factor()))) + } else if let Some(p) = self.popups.iter_mut().find(|w| w.data.id == id) { + let guard = p.common.lock().unwrap(); + Some((Surface::Xdg(p.popup.xdg_surface().clone(), None), guard.size.cast::())) + } else if let Some(l) = self.layer_surfaces.iter_mut().find(|l| l.id == id) { + let guard = l.common.lock().unwrap(); + match l.surface.kind() { + SurfaceKind::Wlr(l) => Some((Surface::Wlr(l.clone()), guard.size.cast::())), + _ => None + } + } else { + None + }; + if let Some((s, geo_size)) = s { let half_min_dim = (geo_size.width as u32).min(geo_size.height as u32) / 2; if let Some(radii) = v { @@ -1647,37 +1660,73 @@ impl SctkState { bottom_right: radii.bottom_right.min(half_min_dim), bottom_left: radii.bottom_left.min(half_min_dim), }; - if let Some((protocol_object, corner_radii)) = w.corner_radius.as_mut() { + if let Some((protocol_object, corner_radii)) = self.corner_radii.get_mut(&id) { if *corner_radii != Some(adjusted_radii) { - protocol_object.0.0.set_radius( + match protocol_object.0.as_ref() { + CornerRadiusWrapper::Xdg(protocol_object) => protocol_object.set_radius( + adjusted_radii.top_left, + adjusted_radii.top_right, + adjusted_radii.bottom_right, + adjusted_radii.bottom_left, + ), + CornerRadiusWrapper::Wlr(protocol_object) => protocol_object.set_radius( + adjusted_radii.top_left, + adjusted_radii.top_right, + adjusted_radii.bottom_right, + adjusted_radii.bottom_left, + ) + }; + *corner_radii = Some(adjusted_radii.clone()); + } + } else { + + let protocol_object = match s { + Surface::Xdg(s, w) => { + if manager.version() == 1 { + if let Some(w) = w { + CornerRadiusWrapper::Xdg(manager.get_corner_radius(&w, &self.queue_handle, ())) + } else { + log::error!("Corner radius is not supported for popups on xdg shell v1"); + return Ok(()); + } + } else { + CornerRadiusWrapper::Xdg(manager.get_corner_radius_surface(&s, &self.queue_handle, ()))} + } + Surface::Wlr(l) => { + if manager.version() == 1 { + return Ok(()); + } + CornerRadiusWrapper::Wlr(manager.get_corner_radius_layer(&l, &self.queue_handle, ()))} + }; + match &protocol_object { + CornerRadiusWrapper::Xdg(protocol_object) => protocol_object.set_radius( adjusted_radii.top_left, adjusted_radii.top_right, adjusted_radii.bottom_right, adjusted_radii.bottom_left, - ); - *corner_radii = Some(adjusted_radii.clone()); - } - } else { - let toplevel = w.xdg_toplevel(&self.connection); - - let protocol_object = manager.get_corner_radius(&toplevel, &self.queue_handle, ()); - - protocol_object.set_radius( - adjusted_radii.top_left, - adjusted_radii.top_right, - adjusted_radii.bottom_right, - adjusted_radii.bottom_left, - ); - w.corner_radius = Some((SctkCornerRadius(Arc::new(MyCosmicCornerRadiusToplevelV1( protocol_object))), Some(adjusted_radii.clone()))); + ), + CornerRadiusWrapper::Wlr(protocol_object) => protocol_object.set_radius( + adjusted_radii.top_left, + adjusted_radii.top_right, + adjusted_radii.bottom_right, + adjusted_radii.bottom_left, + ) + }; + _ = self.corner_radii.insert(id, (SctkCornerRadius(Arc::new(protocol_object)), Some(adjusted_radii.clone()))); } } else { - if let Some(old) = w.corner_radius.as_mut() { - old.0.0.as_ref().0.unset_radius(); + if let Some(old) = self.corner_radii.get_mut(&id) { + match old.0.0.as_ref() { + CornerRadiusWrapper::Xdg(protocol_object) => protocol_object.unset_radius(), + CornerRadiusWrapper::Wlr(protocol_object) => { + protocol_object.unset_radius() + } + }; old.1 = None; } } } else { - if let Some(v) = v{ + if let Some(v) = v { _ = self.pending_corner_radius.insert(id, v); } else { _ = self.pending_corner_radius.remove(&id); diff --git a/winit/src/platform_specific/wayland/handlers/shell/corner_radius.rs b/winit/src/platform_specific/wayland/handlers/shell/corner_radius.rs index bd7d2409..c4bdda88 100644 --- a/winit/src/platform_specific/wayland/handlers/shell/corner_radius.rs +++ b/winit/src/platform_specific/wayland/handlers/shell/corner_radius.rs @@ -1,17 +1,29 @@ -use cctk::{sctk, cosmic_protocols::{ - corner_radius::v1::client::{ +use cctk::{ + cosmic_protocols::corner_radius::v1::client::{ + cosmic_corner_radius_layer_v1::CosmicCornerRadiusLayerV1, cosmic_corner_radius_manager_v1::CosmicCornerRadiusManagerV1, cosmic_corner_radius_toplevel_v1::CosmicCornerRadiusToplevelV1, }, - overlap_notify::v1::client::zcosmic_overlap_notification_v1::ZcosmicOverlapNotificationV1, -}}; -use sctk::reexports::{ - client::{Connection, Dispatch, Proxy}, - + sctk, }; +use sctk::reexports::client::{Connection, Dispatch, Proxy}; use crate::event_loop::state::SctkState; -use crate::platform_specific::wayland::SctkEvent; + +#[derive(Debug)] +pub enum CornerRadiusWrapper { + Xdg(CosmicCornerRadiusToplevelV1), + Wlr(CosmicCornerRadiusLayerV1), +} + +impl Drop for CornerRadiusWrapper { + fn drop(&mut self) { + match self { + Self::Xdg(c) => c.destroy(), + Self::Wlr(c) => c.destroy(), + }; + } +} impl Dispatch for SctkState { fn event( @@ -21,15 +33,11 @@ impl Dispatch for SctkState { _data: &(), _conn: &Connection, _qhandle: &sctk::reexports::client::QueueHandle, - ) {} + ) { + } } -impl - Dispatch< - CosmicCornerRadiusToplevelV1, - (), - > for SctkState -{ +impl Dispatch for SctkState { fn event( state: &mut Self, _proxy: &CosmicCornerRadiusToplevelV1, @@ -39,7 +47,22 @@ impl _qhandle: &sctk::reexports::client::QueueHandle, ) { match event { - _ => unimplemented!() + _ => unimplemented!(), + } + } +} + +impl Dispatch for SctkState { + fn event( + state: &mut Self, + _proxy: &CosmicCornerRadiusLayerV1, + event: ::Event, + _data: &(), + _conn: &Connection, + _qhandle: &sctk::reexports::client::QueueHandle, + ) { + match event { + _ => unimplemented!(), } } } diff --git a/winit/src/platform_specific/wayland/mod.rs b/winit/src/platform_specific/wayland/mod.rs index 9b3a7ec0..bbbce8f1 100644 --- a/winit/src/platform_specific/wayland/mod.rs +++ b/winit/src/platform_specific/wayland/mod.rs @@ -128,7 +128,6 @@ impl PlatformSpecific { }; self.wayland.display_handle = Some(display); self.wayland.proxy = Some(raw); - // TODO remove this self.wayland.sender = crate::platform_specific::event_loop::SctkEventLoop::new( self.wayland.winit_event_sender.clone().unwrap(),