clan-wl-backdrop/src/deco.rs

230 lines
7.5 KiB
Rust
Raw Normal View History

2026-03-04 01:53:44 -03:00
use std::{cell::RefCell, rc::Rc};
use wl_proxy::{
2026-03-04 22:07:53 -03:00
fixed::Fixed,
2026-03-04 01:53:44 -03:00
object::ObjectCoreApi,
2026-03-04 22:07:53 -03:00
protocols::{
cursor_shape_v1::wp_cursor_shape_device_v1::{
WpCursorShapeDeviceV1, WpCursorShapeDeviceV1Shape,
},
wayland::{
wl_buffer::WlBuffer,
wl_pointer::WlPointerButtonState,
wl_seat::WlSeat,
wl_subsurface::WlSubsurface,
wl_surface::{WlSurface, WlSurfaceHandler},
},
xdg_shell::xdg_toplevel::{XdgToplevel, XdgToplevelResizeEdge},
},
2026-03-04 01:53:44 -03:00
};
use crate::{buffer::SimpleBufferPool, globals::Globals, util::Bounds};
pub struct Deco {
bounds: RefCell<Bounds>,
top_size: i32,
border_size: i32,
wl_surface: Rc<WlSurface>,
subsurface: Rc<WlSubsurface>,
2026-03-04 22:07:53 -03:00
toplevel: Rc<XdgToplevel>,
2026-03-04 01:53:44 -03:00
pool: RefCell<SimpleBufferPool>,
globals: Rc<Globals>,
}
impl Deco {
2026-03-04 22:07:53 -03:00
fn resize_anchor(&self, x: i32, y: i32) -> XdgToplevelResizeEdge {
let n = y < self.border_size;
let e = x >= self.bounds.borrow().width + self.border_size;
let w = x < self.border_size;
let s = y >= self.bounds.borrow().height + self.border_size;
if n && e {
XdgToplevelResizeEdge::TOP_RIGHT
} else if n && w {
XdgToplevelResizeEdge::TOP_LEFT
} else if s && e {
XdgToplevelResizeEdge::BOTTOM_RIGHT
} else if s && w {
XdgToplevelResizeEdge::BOTTOM_LEFT
} else if n {
XdgToplevelResizeEdge::TOP
} else if e {
XdgToplevelResizeEdge::RIGHT
} else if w {
XdgToplevelResizeEdge::LEFT
} else if s {
XdgToplevelResizeEdge::BOTTOM
} else {
XdgToplevelResizeEdge::NONE
}
}
}
fn resize_cursor(anchor: XdgToplevelResizeEdge) -> WpCursorShapeDeviceV1Shape {
match anchor {
XdgToplevelResizeEdge::TOP_LEFT => WpCursorShapeDeviceV1Shape::NW_RESIZE,
XdgToplevelResizeEdge::TOP_RIGHT => WpCursorShapeDeviceV1Shape::NE_RESIZE,
XdgToplevelResizeEdge::BOTTOM_LEFT => WpCursorShapeDeviceV1Shape::SW_RESIZE,
XdgToplevelResizeEdge::BOTTOM_RIGHT => WpCursorShapeDeviceV1Shape::SE_RESIZE,
XdgToplevelResizeEdge::TOP => WpCursorShapeDeviceV1Shape::N_RESIZE,
XdgToplevelResizeEdge::LEFT => WpCursorShapeDeviceV1Shape::W_RESIZE,
XdgToplevelResizeEdge::RIGHT => WpCursorShapeDeviceV1Shape::E_RESIZE,
XdgToplevelResizeEdge::BOTTOM => WpCursorShapeDeviceV1Shape::S_RESIZE,
_ => WpCursorShapeDeviceV1Shape::DEFAULT,
}
}
pub struct DecoSurface(Rc<Deco>);
impl WlSurfaceHandler for DecoSurface {}
impl DecoSurface {
pub fn pointer_motion(
&self,
serial: u32,
surface_x: Fixed,
surface_y: Fixed,
cursor_shape: Option<&Rc<WpCursorShapeDeviceV1>>,
) {
let x = surface_x.to_i32_round_towards_nearest();
let y = surface_y.to_i32_round_towards_nearest();
cursor_shape
.unwrap()
.send_set_shape(serial, resize_cursor(self.0.resize_anchor(x, y)));
}
pub fn pointer_button(
&self,
seat: &Rc<WlSeat>,
serial: u32,
surface_x: Fixed,
surface_y: Fixed,
button: u32,
state: WlPointerButtonState,
) {
let x = surface_x.to_i32_round_towards_nearest();
let y = surface_y.to_i32_round_towards_nearest();
let anchor = self.0.resize_anchor(x, y);
const BTN_LEFT: u32 = 0x110;
if button != BTN_LEFT {
return;
}
if state == WlPointerButtonState::PRESSED {
if anchor == XdgToplevelResizeEdge::NONE {
self.0.toplevel.send_move(seat, serial);
} else {
self.0.toplevel.send_resize(seat, serial, anchor);
}
}
}
}
impl Deco {
pub fn new(
host_surface: &Rc<WlSurface>,
toplevel: &Rc<XdgToplevel>,
globals: &Rc<Globals>,
) -> Rc<Deco> {
2026-03-04 01:53:44 -03:00
let wl_surface = globals.wl_compositor.new_send_create_surface();
wl_surface.set_forward_to_client(false);
let subsurface = globals
.wl_subcompositor
.new_send_get_subsurface(&wl_surface, host_surface);
subsurface.set_forward_to_client(false);
subsurface.send_place_below(&host_surface);
let pool = SimpleBufferPool::new(globals, 1, 1).unwrap();
let args = crate::ARGS.get().unwrap();
2026-03-04 22:07:53 -03:00
let deco = Rc::new(Deco {
2026-03-04 01:53:44 -03:00
bounds: RefCell::new(Bounds {
x: 0,
y: 0,
width: 10,
height: 10,
}),
top_size: args.thickness as i32, // TODO: title
border_size: args.thickness as i32,
2026-03-04 22:07:53 -03:00
wl_surface: wl_surface.clone(),
2026-03-04 01:53:44 -03:00
subsurface,
2026-03-04 22:07:53 -03:00
toplevel: toplevel.clone(),
2026-03-04 01:53:44 -03:00
pool: RefCell::new(pool),
globals: globals.clone(),
2026-03-04 22:07:53 -03:00
});
wl_surface.set_handler(DecoSurface(deco.clone()));
deco
2026-03-04 01:53:44 -03:00
}
pub fn handle_window_geometry(
&self,
x: i32,
y: i32,
width: i32,
height: i32,
) -> eyre::Result<(i32, i32, i32, i32)> {
self.bounds.replace(Bounds {
x,
y,
width,
height,
});
self.draw()?;
let width = width + self.border_size * 2;
let height = height + self.top_size + self.border_size;
Ok((x - self.border_size, y - self.top_size, width, height))
}
pub fn transform_configure(&self, width: i32, height: i32) -> (i32, i32) {
(
width - self.border_size * 2,
height - self.top_size - self.border_size,
)
}
2026-03-06 02:53:15 -03:00
pub fn transform_size(&self, width: i32, height: i32) -> (i32, i32) {
(
width + self.border_size * 2,
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)
}
2026-03-04 01:53:44 -03:00
fn draw(&self) -> eyre::Result<Rc<WlBuffer>> {
let bounds = self.bounds.borrow();
2026-03-06 02:53:15 -03:00
let (width, height) = self.transform_size(bounds.width, bounds.height);
2026-03-04 01:53:44 -03:00
let mut pool = self.pool.borrow_mut();
let (buf, map) = pool.buffer(width, height)?;
let (pre, pix, post) = unsafe { map.align_to_mut::<u32>() };
assert!(pre.len() == 0 && post.len() == 0);
let bg = crate::ARGS
.get()
.unwrap()
.background
.premultiply()
.to_rgba8();
let bg_argb = u32::from_be_bytes([bg.a, bg.r, bg.g, bg.b]);
2026-03-04 01:53:44 -03:00
for y in 0..height {
for x in 0..width {
pix[(y * width + x) as usize] = bg_argb;
2026-03-04 01:53:44 -03:00
}
}
self.wl_surface.send_attach(Some(&buf), 0, 0);
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);
2026-03-04 22:07:53 -03:00
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);
2026-03-04 01:53:44 -03:00
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);
self.wl_surface.send_commit();
// let _ = self.globals.wl_display.new_send_sync();
Ok(buf)
}
}