use std::{cell::RefCell, rc::Rc}; use wl_proxy::{ fixed::Fixed, object::ObjectCoreApi, protocols::{ cursor_shape_v1::wp_cursor_shape_device_v1::{ WpCursorShapeDeviceV1, WpCursorShapeDeviceV1Shape, }, wayland::{ wl_pointer::WlPointerButtonState, wl_seat::WlSeat, wl_subsurface::WlSubsurface, wl_surface::{WlSurface, WlSurfaceHandler}, }, xdg_shell::xdg_toplevel::{XdgToplevel, XdgToplevelResizeEdge}, }, }; use crate::{buffer::SimpleBufferPool, globals::Globals, util::Bounds}; #[derive(Default, Debug, Clone, PartialEq)] struct DecoParams { has_geometry: bool, bounds: Bounds, } pub struct Deco { last_params: RefCell, next_params: RefCell, top_size: i32, border_size: i32, wl_surface: Rc, subsurface: Rc, toplevel: Rc, pool: RefCell, globals: Rc, } impl Deco { fn resize_anchor(&self, x: i32, y: i32) -> XdgToplevelResizeEdge { let bounds = &self.last_params.borrow().bounds; let n = y < self.border_size; let e = x >= bounds.width + self.border_size; let w = x < self.border_size; let s = y >= bounds.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); impl WlSurfaceHandler for DecoSurface {} impl DecoSurface { pub fn pointer_motion( &self, serial: u32, surface_x: Fixed, surface_y: Fixed, cursor_shape: Option<&Rc>, ) { 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, 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, toplevel: &Rc, globals: &Rc, ) -> Rc { let wl_surface = globals.wl_compositor.new_send_create_surface(); log::debug!("creating deco for surface {host_surface:?} -> {wl_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(); 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, toplevel: toplevel.clone(), pool: RefCell::new(pool), globals: globals.clone(), }); wl_surface.set_handler(DecoSurface(deco.clone())); deco } pub fn handle_intrinsic_size(&self, width: i32, height: i32) { let mut params = self.next_params.borrow_mut(); if params.has_geometry { return; } log::debug!( "decoration {:?} handling buffer size {width}x{height}", self.wl_surface ); params.bounds = Bounds { x: 0, y: 0, width, height, }; } pub fn handle_window_geometry( &self, x: i32, y: i32, width: i32, height: i32, ) -> (i32, i32, i32, i32) { log::debug!( "decoration {:?} handling geometry {width}x{height} @ {x},{y}", self.wl_surface ); let mut params = self.next_params.borrow_mut(); params.has_geometry = true; params.bounds = Bounds { x, y, width, 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) } pub fn handle_commit(&self) -> eyre::Result<()> { self.draw() } pub fn transform_configure(&self, width: i32, height: i32) -> (i32, i32) { ( width - self.border_size * 2, 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, ) } pub fn transform_anchor(&self, x: i32, y: i32) -> (i32, i32) { (x + self.border_size, y + self.top_size) } fn draw(&self) -> eyre::Result<()> { let params = self.next_params.borrow(); if *params == *self.last_params.borrow() { log::debug!("decoration {:?} unchanged", self.wl_surface); return Ok(()); } log::debug!( "decoration {:?} changed to {params:?}, drawing", self.wl_surface ); self.last_params.replace(params.clone()); let bounds = ¶ms.bounds; let (width, height) = self.transform_size(bounds.width, bounds.height); let mut pool = self.pool.borrow_mut(); let (buf, map) = pool.buffer(width, height)?; let (pre, pix, post) = unsafe { map.align_to_mut::() }; 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]); for y in 0..height { for x in 0..width { pix[(y * width + x) as usize] = bg_argb; } } 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); 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); self.wl_surface.send_commit(); // let _ = self.globals.wl_display.new_send_sync(); Ok(()) } }