Add synthetic xdg-decoration global for the client

For now, handle SSD preference by drawing a blank title bar (actual
impl coming later)
This commit is contained in:
Val Packett 2026-03-20 01:46:57 -03:00
parent 3da5fac131
commit a5dbec759f
3 changed files with 119 additions and 11 deletions

View file

@ -26,6 +26,7 @@ use crate::{
#[derive(Default, Debug, Clone, PartialEq)]
struct DecoParams {
has_geometry: bool,
prefers_ssd: bool,
is_active: bool,
bounds: Bounds,
restricted_edges: Edges<bool>,
@ -34,7 +35,6 @@ struct DecoParams {
pub struct Deco {
last_params: RefCell<DecoParams>,
next_params: RefCell<DecoParams>,
top_size: i32,
border_size: i32,
wl_surface: Rc<WlSurface>,
@ -133,6 +133,15 @@ impl DecoSurface {
}
impl Deco {
fn top_size(&self) -> i32 {
let params = self.last_params.borrow();
if params.prefers_ssd {
self.border_size + 24
} else {
self.border_size
}
}
pub fn new(
host_surface: &Rc<WlSurface>,
toplevel: &Rc<XdgToplevel>,
@ -151,7 +160,6 @@ impl Deco {
let deco = Rc::new(Deco {
last_params: RefCell::new(Default::default()),
next_params: RefCell::new(Default::default()),
top_size: args.thickness as i32, // TODO: title
border_size: args.thickness as i32,
wl_surface: wl_surface.clone(),
subsurface,
@ -200,8 +208,8 @@ impl Deco {
height,
};
let width = width + self.border_size * 2;
let height = height + self.top_size + self.border_size;
(x - self.border_size, y - self.top_size, width, height)
let height = height + self.top_size() + self.border_size;
(x - self.border_size, y - self.top_size(), width, height)
}
pub fn set_restricted_edges(&self, edges: Edges<bool>) {
@ -214,6 +222,11 @@ impl Deco {
params.is_active = is_active;
}
pub fn set_prefers_ssd(&self, prefers_ssd: bool) {
let mut params = self.next_params.borrow_mut();
params.prefers_ssd = prefers_ssd;
}
pub fn handle_commit(&self) -> eyre::Result<()> {
self.draw()
}
@ -221,19 +234,19 @@ impl Deco {
pub fn transform_configure(&self, width: i32, height: i32) -> (i32, i32) {
(
width - self.border_size * 2,
height - self.top_size - self.border_size,
height - self.top_size() - self.border_size,
)
}
pub fn transform_size(&self, width: i32, height: i32) -> (i32, i32) {
(
width + self.border_size * 2,
height + self.top_size + self.border_size,
height + self.top_size() + self.border_size,
)
}
pub fn transform_anchor(&self, x: i32, y: i32) -> (i32, i32) {
(x + self.border_size, y + self.top_size)
(x + self.border_size, y + self.top_size())
}
fn draw(&self) -> eyre::Result<()> {
@ -273,13 +286,13 @@ impl Deco {
self.wl_surface.send_damage_buffer(0, 0, width, height);
let input_region = self.globals.wl_compositor.new_send_create_region();
input_region.set_forward_to_client(false);
input_region.send_add(0, 0, width, self.top_size);
input_region.send_add(0, 0, width, self.top_size());
input_region.send_add(0, 0, self.border_size, height);
input_region.send_add(width - self.border_size, 0, self.border_size, height);
input_region.send_add(0, height - self.border_size, width, self.border_size);
self.wl_surface.send_set_input_region(Some(&input_region));
self.subsurface
.send_set_position(bounds.x - self.border_size, bounds.y - self.top_size);
.send_set_position(bounds.x - self.border_size, bounds.y - self.top_size());
self.wl_surface.send_commit();
// let _ = self.globals.wl_display.new_send_sync();
Ok(())

View file

@ -3,13 +3,16 @@ use std::{process::Command, rc::Rc, sync::OnceLock};
use clap::{CommandFactory, Parser};
use wl_proxy::{
baseline::Baseline,
global_mapper::GlobalMapper,
object::{ConcreteObject, Object, ObjectCoreApi, ObjectRcUtils},
protocols::{
ObjectInterface,
linux_dmabuf_v1::zwp_linux_dmabuf_v1::ZwpLinuxDmabufV1,
wayland::{
wl_registry::{WlRegistry, WlRegistryHandler},
wl_shm::WlShm,
},
xdg_decoration_unstable_v1::zxdg_decoration_manager_v1::ZxdgDecorationManagerV1,
xdg_shell::xdg_wm_base::XdgWmBase,
},
simple::{SimpleCommandExt, SimpleProxy},
@ -27,12 +30,16 @@ mod xdg;
struct ClientWlRegistry {
globals: Rc<Globals>,
globals_requested: bool,
mapper: GlobalMapper,
}
impl ClientWlRegistry {
fn new(globals: &Rc<Globals>) -> Self {
Self {
globals: globals.clone(),
globals_requested: false,
mapper: GlobalMapper::default(),
}
}
}
@ -45,6 +52,12 @@ impl WlRegistryHandler for ClientWlRegistry {
.set_handler(xdg::ClientXdgWmBase {
globals: self.globals.clone(),
}),
ZxdgDecorationManagerV1::INTERFACE => {
id.set_forward_to_server(false);
id.downcast::<ZxdgDecorationManagerV1>()
.set_handler(xdg::ClientZxdgDecorationManagerV1);
return;
}
WlShm::INTERFACE => id
.downcast::<WlShm>()
.set_handler(client_buffer::ClientWlShm),
@ -53,7 +66,34 @@ impl WlRegistryHandler for ClientWlRegistry {
.set_handler(client_buffer::ClientZwpLinuxDmabufV1),
_ => {}
}
slf.send_bind(name, id);
self.mapper.forward_bind(slf, name, &id);
}
fn handle_global(
&mut self,
slf: &Rc<WlRegistry>,
name: u32,
interface: ObjectInterface,
version: u32,
) {
use ObjectInterface::*;
if !self.globals_requested {
self.globals_requested = true;
self.mapper
.add_synthetic_global(slf, ZxdgDecorationManagerV1, 1);
}
match interface {
ZxdgDecorationManagerV1 | WpSecurityContextManagerV1 => {
self.mapper.ignore_global(name);
}
_ => {
self.mapper.forward_global(slf, name, interface, version);
}
}
}
fn handle_global_remove(&mut self, slf: &Rc<WlRegistry>, name: u32) {
self.mapper.forward_global_remove(slf, name);
}
}

View file

@ -1,13 +1,20 @@
use std::rc::Rc;
use wl_proxy::{
object::ObjectUtils,
object::{ObjectCoreApi, ObjectUtils},
protocols::{
wayland::{
wl_buffer::WlBuffer,
wl_output::{WlOutput, WlOutputTransform},
wl_surface::{WlSurface, WlSurfaceHandler},
},
xdg_decoration_unstable_v1::{
zxdg_decoration_manager_v1::{ZxdgDecorationManagerV1, ZxdgDecorationManagerV1Handler},
zxdg_toplevel_decoration_v1::{
ZxdgToplevelDecorationV1, ZxdgToplevelDecorationV1Handler,
ZxdgToplevelDecorationV1Mode,
},
},
xdg_shell::{
xdg_popup::XdgPopup,
xdg_positioner::{XdgPositioner, XdgPositionerHandler},
@ -90,6 +97,54 @@ impl WlSurfaceHandler for ClientWlSurface {
}
}
pub struct ClientZxdgDecorationManagerV1;
impl ZxdgDecorationManagerV1Handler for ClientZxdgDecorationManagerV1 {
fn handle_destroy(&mut self, slf: &Rc<ZxdgDecorationManagerV1>) {
slf.delete_id();
}
fn handle_get_toplevel_decoration(
&mut self,
_slf: &Rc<ZxdgDecorationManagerV1>,
id: &Rc<ZxdgToplevelDecorationV1>,
toplevel: &Rc<XdgToplevel>,
) {
id.set_handler(ClientZxdgToplevelDecorationV1 {
toplevel: toplevel.clone(),
});
// TODO: send_configure here if a force mode is picked
}
}
pub struct ClientZxdgToplevelDecorationV1 {
toplevel: Rc<XdgToplevel>,
}
impl ZxdgToplevelDecorationV1Handler for ClientZxdgToplevelDecorationV1 {
fn handle_destroy(&mut self, slf: &Rc<ZxdgToplevelDecorationV1>) {
slf.delete_id();
}
fn handle_set_mode(
&mut self,
slf: &Rc<ZxdgToplevelDecorationV1>,
mode: ZxdgToplevelDecorationV1Mode,
) {
let toplevel_handler = self.toplevel.get_handler_ref::<ClientXdgToplevel>();
toplevel_handler
.deco
.set_prefers_ssd(mode == ZxdgToplevelDecorationV1Mode::SERVER_SIDE);
slf.send_configure(mode);
}
fn handle_unset_mode(&mut self, slf: &Rc<ZxdgToplevelDecorationV1>) {
let toplevel_handler = self.toplevel.get_handler_ref::<ClientXdgToplevel>();
toplevel_handler.deco.set_prefers_ssd(false);
slf.send_configure(ZxdgToplevelDecorationV1Mode::CLIENT_SIDE);
}
}
pub struct ClientXdgWmBase {
pub globals: Rc<Globals>,
}