wip: corner radius v2

This commit is contained in:
Ashley Wulber 2026-04-16 23:01:58 -04:00 committed by Ashley Wulber
parent 9f41055861
commit 1bcba7e30c
7 changed files with 137 additions and 65 deletions

4
Cargo.lock generated
View file

@ -1294,7 +1294,7 @@ dependencies = [
[[package]] [[package]]
name = "cosmic-client-toolkit" name = "cosmic-client-toolkit"
version = "0.2.0" 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 = [ dependencies = [
"bitflags 2.11.0", "bitflags 2.11.0",
"cosmic-protocols", "cosmic-protocols",
@ -1307,7 +1307,7 @@ dependencies = [
[[package]] [[package]]
name = "cosmic-protocols" name = "cosmic-protocols"
version = "0.2.0" 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 = [ dependencies = [
"bitflags 2.11.0", "bitflags 2.11.0",
"wayland-backend", "wayland-backend",

View file

@ -274,7 +274,7 @@ tiny-skia = { version = "0.11", default-features = false, features = [
"std", "std",
"simd", "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" } softbuffer = { git = "https://github.com/pop-os/softbuffer", tag = "cosmic-4.0" }
syntect = "5.2" syntect = "5.2"
tokio = "1.0" tokio = "1.0"

View file

@ -682,7 +682,9 @@ pub use error::Error;
pub use event::Event; pub use event::Event;
pub use executor::Executor; pub use executor::Executor;
pub use font::Font; 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 task::Task;
pub use window::Window; pub use window::Window;

View file

@ -33,7 +33,7 @@ use cctk::{
reexports::{ reexports::{
calloop::{self, EventLoop}, calloop::{self, EventLoop},
client::{ client::{
ConnectError, Connection, Proxy, globals::registry_queue_init, Connection, Proxy, globals::registry_queue_init,
}, },
}, },
registry::RegistryState, registry::RegistryState,
@ -46,8 +46,6 @@ use cctk::{
}; };
use raw_window_handle::HasDisplayHandle; use raw_window_handle::HasDisplayHandle;
use state::{FrameStatus, SctkWindow, send_event}; use state::{FrameStatus, SctkWindow, send_event};
#[cfg(feature = "a11y")]
use std::sync::{Arc, Mutex};
use std::{ use std::{
collections::{HashMap, HashSet}, collections::{HashMap, HashSet},
fmt::Debug, fmt::Debug,
@ -71,8 +69,8 @@ pub struct SctkEventLoop {
pub(crate) state: SctkState, pub(crate) state: SctkState,
} }
#[derive(Debug)]
pub enum Error { pub enum Error {
Connect(ConnectError),
Calloop(calloop::Error), Calloop(calloop::Error),
Global(GlobalError), Global(GlobalError),
NoDisplayHandle, NoDisplayHandle,
@ -129,7 +127,7 @@ impl SctkEventLoop {
crate::platform_specific::Action::ResizeWindow(id) => { crate::platform_specific::Action::ResizeWindow(id) => {
if let Some((_, v)) = state.windows.iter() if let Some((_, v)) = state.windows.iter()
.find(|w| w.id == id) .find(|w| w.id == id)
.map(|w| w.corner_radius.as_ref()) .map(|w| state.corner_radii.get(&id))
.unwrap_or_default() { .unwrap_or_default() {
_ = state.handle_action(iced_runtime::platform_specific::wayland::Action::RoundedCorners(id, *v)); _ = state.handle_action(iced_runtime::platform_specific::wayland::Action::RoundedCorners(id, *v));
} }
@ -138,7 +136,7 @@ impl SctkEventLoop {
window, window,
id, 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) { if let Some(v) = state.pending_corner_radius.remove(&id) {
_ = state.handle_action(iced_runtime::platform_specific::wayland::Action::RoundedCorners(id, Some(v))); _ = 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::<CosmicCornerRadiusManagerV1, _, _>( corner_radius_manager: registry_state.bind_one::<CosmicCornerRadiusManagerV1, _, _>(
&qh, &qh,
1..=1, 1..=2,
(), (),
).ok(), ).ok(),
toplevel_manager: ToplevelManagerState::try_new( toplevel_manager: ToplevelManagerState::try_new(
@ -377,6 +375,7 @@ impl SctkEventLoop {
seats: Vec::new(), seats: Vec::new(),
windows: Vec::new(), windows: Vec::new(),
blur_surfaces: HashMap::new(), blur_surfaces: HashMap::new(),
corner_radii: HashMap::new(),
layer_surfaces: Vec::new(), layer_surfaces: Vec::new(),
popups: Vec::new(), popups: Vec::new(),
lock_surfaces: Vec::new(), lock_surfaces: Vec::new(),

View file

@ -1,10 +1,7 @@
use crate::{ use crate::{
Control, Control,
handlers::{ handlers::{
activation::IcedRequestData, activation::IcedRequestData, ext_background_effect, overlap::{OverlapNotificationV1, OverlapNotifyV1}, shell::corner_radius::CornerRadiusWrapper, text_input::{Preedit, TextInputManager}
ext_background_effect,
overlap::{OverlapNotificationV1, OverlapNotifyV1},
text_input::{Preedit, TextInputManager},
}, },
platform_specific::{ platform_specific::{
Event, Event,
@ -65,7 +62,7 @@ use cctk::{
wl_surface::{self, WlSurface}, wl_surface::{self, WlSurface},
wl_touch::WlTouch, wl_touch::WlTouch,
}, },
}, }, protocols_wlr::layer_shell::v1::client::zwlr_layer_surface_v1::ZwlrLayerSurfaceV1,
}, },
registry::RegistryState, registry::RegistryState,
seat::{ seat::{
@ -341,22 +338,13 @@ pub struct SctkPopupData {
pub(crate) grab: bool, pub(crate) grab: bool,
} }
#[derive(Debug)]
pub struct MyCosmicCornerRadiusToplevelV1(CosmicCornerRadiusToplevelV1);
impl Drop for MyCosmicCornerRadiusToplevelV1 {
fn drop(&mut self) {
self.0.destroy();
}
}
#[derive(Debug, Clone)] #[derive(Debug, Clone)]
pub struct SctkCornerRadius(Arc<MyCosmicCornerRadiusToplevelV1>); pub struct SctkCornerRadius(Arc<CornerRadiusWrapper>);
pub struct SctkWindow { pub struct SctkWindow {
pub(crate) window: Arc<dyn winit::window::Window>, pub(crate) window: Arc<dyn winit::window::Window>,
pub(crate) id: core::window::Id, pub(crate) id: core::window::Id,
pub(crate) corner_radius: Option<(SctkCornerRadius, Option<CornerRadius>)>,
} }
impl SctkWindow { impl SctkWindow {
@ -444,6 +432,7 @@ pub struct SctkState {
pub(crate) subsurfaces: Vec<SctkSubsurface>, pub(crate) subsurfaces: Vec<SctkSubsurface>,
pub(crate) lock_surfaces: Vec<SctkLockSurface>, pub(crate) lock_surfaces: Vec<SctkLockSurface>,
pub(crate) blur_surfaces: HashMap<core::window::Id, Vec<ExtBackgroundEffectSurfaceV1>>, pub(crate) blur_surfaces: HashMap<core::window::Id, Vec<ExtBackgroundEffectSurfaceV1>>,
pub(crate) corner_radii: HashMap<core::window::Id, (SctkCornerRadius, Option<CornerRadius>)>,
pub(crate) touch_points: HashMap<touch::Finger, (WlSurface, Point)>, pub(crate) touch_points: HashMap<touch::Finger, (WlSurface, Point)>,
/// Window updates, which are coming from SCTK or the compositor, which require /// Window updates, which are coming from SCTK or the compositor, which require
@ -1137,6 +1126,7 @@ impl SctkState {
s.destroy(); s.destroy();
} }
} }
_ = self.corner_radii.remove(&id);
let (removed, remaining): (Vec<_>, Vec<_>) = self let (removed, remaining): (Vec<_>, Vec<_>) = self
.subsurfaces .subsurfaces
@ -1396,6 +1386,8 @@ impl SctkState {
s.destroy(); s.destroy();
} }
} }
_ = self.corner_radii.remove(&id);
let (removed, remaining): (Vec<_>, Vec<_>) = self let (removed, remaining): (Vec<_>, Vec<_>) = self
.subsurfaces .subsurfaces
@ -1524,6 +1516,8 @@ impl SctkState {
s.destroy(); s.destroy();
} }
} }
_ = self.corner_radii.remove(&id);
let (removed, remaining): (Vec<_>, Vec<_>) = self let (removed, remaining): (Vec<_>, Vec<_>) = self
.subsurfaces .subsurfaces
.drain(..) .drain(..)
@ -1609,6 +1603,8 @@ impl SctkState {
s.destroy(); s.destroy();
} }
} }
_ = self.corner_radii.remove(&id);
if let Some((wl_surface, f)) = self.seats.iter_mut().find(|f| { if let Some((wl_surface, f)) = self.seats.iter_mut().find(|f| {
f.kbd_focus.as_ref().is_some_and(|f| *f == destroyed) f.kbd_focus.as_ref().is_some_and(|f| *f == destroyed)
}).and_then(|f| Some((parent, &mut f.kbd_focus))) { }).and_then(|f| Some((parent, &mut f.kbd_focus))) {
@ -1636,8 +1632,25 @@ impl SctkState {
} }
Action::RoundedCorners(id, v) => { Action::RoundedCorners(id, v) => {
if let Some(manager) = self.corner_radius_manager.as_ref() { if let Some(manager) = self.corner_radius_manager.as_ref() {
if let Some(w) = self.windows.iter_mut().find(|w| w.id == id) { enum Surface {
let geo_size: LogicalSize<f64> = w.window.surface_size().cast::<f64>().to_logical(w.window.scale_factor()); Xdg(XdgSurface, Option<XdgToplevel>),
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::<f64>().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::<f64>()))
} 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::<f64>())),
_ => 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; let half_min_dim = (geo_size.width as u32).min(geo_size.height as u32) / 2;
if let Some(radii) = v { if let Some(radii) = v {
@ -1647,37 +1660,73 @@ impl SctkState {
bottom_right: radii.bottom_right.min(half_min_dim), bottom_right: radii.bottom_right.min(half_min_dim),
bottom_left: radii.bottom_left.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) { 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_left,
adjusted_radii.top_right, adjusted_radii.top_right,
adjusted_radii.bottom_right, adjusted_radii.bottom_right,
adjusted_radii.bottom_left, adjusted_radii.bottom_left,
); ),
*corner_radii = Some(adjusted_radii.clone()); CornerRadiusWrapper::Wlr(protocol_object) => protocol_object.set_radius(
} adjusted_radii.top_left,
} else { adjusted_radii.top_right,
let toplevel = w.xdg_toplevel(&self.connection); adjusted_radii.bottom_right,
adjusted_radii.bottom_left,
let protocol_object = manager.get_corner_radius(&toplevel, &self.queue_handle, ()); )
};
protocol_object.set_radius( _ = self.corner_radii.insert(id, (SctkCornerRadius(Arc::new(protocol_object)), Some(adjusted_radii.clone())));
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())));
} }
} else { } else {
if let Some(old) = w.corner_radius.as_mut() { if let Some(old) = self.corner_radii.get_mut(&id) {
old.0.0.as_ref().0.unset_radius(); 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; old.1 = None;
} }
} }
} else { } else {
if let Some(v) = v{ if let Some(v) = v {
_ = self.pending_corner_radius.insert(id, v); _ = self.pending_corner_radius.insert(id, v);
} else { } else {
_ = self.pending_corner_radius.remove(&id); _ = self.pending_corner_radius.remove(&id);

View file

@ -1,17 +1,29 @@
use cctk::{sctk, cosmic_protocols::{ use cctk::{
corner_radius::v1::client::{ cosmic_protocols::corner_radius::v1::client::{
cosmic_corner_radius_layer_v1::CosmicCornerRadiusLayerV1,
cosmic_corner_radius_manager_v1::CosmicCornerRadiusManagerV1, cosmic_corner_radius_manager_v1::CosmicCornerRadiusManagerV1,
cosmic_corner_radius_toplevel_v1::CosmicCornerRadiusToplevelV1, cosmic_corner_radius_toplevel_v1::CosmicCornerRadiusToplevelV1,
}, },
overlap_notify::v1::client::zcosmic_overlap_notification_v1::ZcosmicOverlapNotificationV1, sctk,
}};
use sctk::reexports::{
client::{Connection, Dispatch, Proxy},
}; };
use sctk::reexports::client::{Connection, Dispatch, Proxy};
use crate::event_loop::state::SctkState; 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<CosmicCornerRadiusManagerV1, ()> for SctkState { impl Dispatch<CosmicCornerRadiusManagerV1, ()> for SctkState {
fn event( fn event(
@ -21,15 +33,11 @@ impl Dispatch<CosmicCornerRadiusManagerV1, ()> for SctkState {
_data: &(), _data: &(),
_conn: &Connection, _conn: &Connection,
_qhandle: &sctk::reexports::client::QueueHandle<Self>, _qhandle: &sctk::reexports::client::QueueHandle<Self>,
) {} ) {
}
} }
impl impl Dispatch<CosmicCornerRadiusToplevelV1, ()> for SctkState {
Dispatch<
CosmicCornerRadiusToplevelV1,
(),
> for SctkState
{
fn event( fn event(
state: &mut Self, state: &mut Self,
_proxy: &CosmicCornerRadiusToplevelV1, _proxy: &CosmicCornerRadiusToplevelV1,
@ -39,7 +47,22 @@ impl
_qhandle: &sctk::reexports::client::QueueHandle<Self>, _qhandle: &sctk::reexports::client::QueueHandle<Self>,
) { ) {
match event { match event {
_ => unimplemented!() _ => unimplemented!(),
}
}
}
impl Dispatch<CosmicCornerRadiusLayerV1, ()> for SctkState {
fn event(
state: &mut Self,
_proxy: &CosmicCornerRadiusLayerV1,
event: <CosmicCornerRadiusLayerV1 as Proxy>::Event,
_data: &(),
_conn: &Connection,
_qhandle: &sctk::reexports::client::QueueHandle<Self>,
) {
match event {
_ => unimplemented!(),
} }
} }
} }

View file

@ -128,7 +128,6 @@ impl PlatformSpecific {
}; };
self.wayland.display_handle = Some(display); self.wayland.display_handle = Some(display);
self.wayland.proxy = Some(raw); self.wayland.proxy = Some(raw);
// TODO remove this
self.wayland.sender = self.wayland.sender =
crate::platform_specific::event_loop::SctkEventLoop::new( crate::platform_specific::event_loop::SctkEventLoop::new(
self.wayland.winit_event_sender.clone().unwrap(), self.wayland.winit_event_sender.clone().unwrap(),