cosmic-comp/src/wayland/handlers/corner_radius.rs
2026-07-09 18:08:01 +02:00

121 lines
4 KiB
Rust

use smithay::utils::{Logical, Rectangle, Size};
use smithay::wayland::compositor::SurfaceData;
use crate::wayland::protocols::corner_radius::{
CacheableCorners, CacheablePadding, CornerRadiusData, CornerRadiusHandler, CornerRadiusState,
CornerRadiusSurface, delegate_corner_radius,
};
use crate::state::State;
impl CornerRadiusHandler for State {
fn corner_radius_state(&mut self) -> &mut CornerRadiusState {
&mut self.common.corner_radius_state
}
fn set_corner_radius(&mut self, 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) {
if force_redraw(self, data).is_none() {
tracing::warn!("Failed to force redraw for corner radius reset.");
}
}
fn set_padding(&mut self, data: &CornerRadiusData) {
if force_redraw(self, data).is_none() {
tracing::warn!("Failed to force redraw for corner radius change.");
}
}
fn unset_padding(&mut self, data: &CornerRadiusData) {
if force_redraw(self, data).is_none() {
tracing::warn!("Failed to force redraw for corner radius reset.");
}
}
}
fn force_redraw(state: &mut State, data: &CornerRadiusData) -> Option<()> {
let guard = data.lock().unwrap();
let shell = state.common.shell.read();
let output = match &guard.surface {
CornerRadiusSurface::Toplevel(toplevel) => {
let toplevel = toplevel.upgrade().ok()?;
let surface = state.common.xdg_shell_state.get_toplevel(&toplevel)?;
shell.visible_output_for_surface(surface.wl_surface())?
}
CornerRadiusSurface::Popup(popup) => {
let popup = popup.upgrade().ok()?;
let surface = state.common.xdg_shell_state.get_popup(&popup)?;
shell.visible_output_for_surface(surface.wl_surface())?
}
CornerRadiusSurface::Layer(layer) => {
let layer = layer.upgrade().ok()?;
let surface = state
.common
.layer_shell_state
.layer_surfaces()
.find(|l| l.shell_surface() == &layer)?;
shell.visible_output_for_surface(surface.wl_surface())?
}
};
state.backend.schedule_render(output);
Some(())
}
pub fn surface_corners(states: &SurfaceData, size: Size<i32, Logical>) -> Option<[u8; 4]> {
let mut guard = states.cached_state.get::<CacheableCorners>();
// guard against corner radius being too large, potentially disconnecting the outline
let half_min_dim = u8::try_from(size.w.min(size.h) / 2).unwrap_or(u8::MAX);
let corners = guard.current().0?;
Some([
u8::try_from(corners.top_left)
.unwrap_or(u8::MAX)
.min(half_min_dim),
u8::try_from(corners.top_right)
.unwrap_or(u8::MAX)
.min(half_min_dim),
u8::try_from(corners.bottom_right)
.unwrap_or(u8::MAX)
.min(half_min_dim),
u8::try_from(corners.bottom_left)
.unwrap_or(u8::MAX)
.min(half_min_dim),
])
}
pub fn surface_padding(states: &SurfaceData, size: Size<i32, Logical>) -> Option<[i32; 4]> {
let mut guard = states.cached_state.get::<CacheablePadding>();
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<i32, Logical>,
padding: &[i32; 4],
) -> Option<Rectangle<i32, Logical>> {
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)
}
delegate_corner_radius!(State);