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]]
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",

View file

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

View file

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

View file

@ -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::<CosmicCornerRadiusManagerV1, _, _>(
&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(),

View file

@ -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<MyCosmicCornerRadiusToplevelV1>);
pub struct SctkCornerRadius(Arc<CornerRadiusWrapper>);
pub struct SctkWindow {
pub(crate) window: Arc<dyn winit::window::Window>,
pub(crate) id: core::window::Id,
pub(crate) corner_radius: Option<(SctkCornerRadius, Option<CornerRadius>)>,
}
impl SctkWindow {
@ -444,6 +432,7 @@ pub struct SctkState {
pub(crate) subsurfaces: Vec<SctkSubsurface>,
pub(crate) lock_surfaces: Vec<SctkLockSurface>,
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)>,
/// 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<f64> = w.window.surface_size().cast::<f64>().to_logical(w.window.scale_factor());
enum Surface {
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;
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);

View file

@ -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<CosmicCornerRadiusManagerV1, ()> for SctkState {
fn event(
@ -21,15 +33,11 @@ impl Dispatch<CosmicCornerRadiusManagerV1, ()> for SctkState {
_data: &(),
_conn: &Connection,
_qhandle: &sctk::reexports::client::QueueHandle<Self>,
) {}
) {
}
}
impl
Dispatch<
CosmicCornerRadiusToplevelV1,
(),
> for SctkState
{
impl Dispatch<CosmicCornerRadiusToplevelV1, ()> for SctkState {
fn event(
state: &mut Self,
_proxy: &CosmicCornerRadiusToplevelV1,
@ -39,7 +47,22 @@ impl
_qhandle: &sctk::reexports::client::QueueHandle<Self>,
) {
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.proxy = Some(raw);
// TODO remove this
self.wayland.sender =
crate::platform_specific::event_loop::SctkEventLoop::new(
self.wayland.winit_event_sender.clone().unwrap(),