269 lines
8.7 KiB
Rust
269 lines
8.7 KiB
Rust
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<DecoParams>,
|
|
next_params: RefCell<DecoParams>,
|
|
top_size: i32,
|
|
border_size: i32,
|
|
|
|
wl_surface: Rc<WlSurface>,
|
|
subsurface: Rc<WlSubsurface>,
|
|
toplevel: Rc<XdgToplevel>,
|
|
pool: RefCell<SimpleBufferPool>,
|
|
globals: Rc<Globals>,
|
|
}
|
|
|
|
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<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> {
|
|
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::<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]);
|
|
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(())
|
|
}
|
|
}
|