From d78c792f05eccea54b898d77ef43be66bc265804 Mon Sep 17 00:00:00 2001 From: IoIxD Date: Mon, 6 Jul 2026 01:26:00 -0700 Subject: [PATCH] window decorations --- src/grabs/move_grab.rs | 4 +- src/handlers/compositor.rs | 10 +-- src/handlers/xdg_activation.rs | 2 +- src/handlers/xdg_decoration.rs | 12 ++-- src/handlers/xdg_shell.rs | 22 +++--- src/input.rs | 27 +++---- src/render.rs | 128 +++++++++++++++++++++++++++++++-- src/state.rs | 5 +- src/winit.rs | 7 +- src/xwayland.rs | 11 +-- 10 files changed, 174 insertions(+), 54 deletions(-) diff --git a/src/grabs/move_grab.rs b/src/grabs/move_grab.rs index 462eb80..ea524a8 100644 --- a/src/grabs/move_grab.rs +++ b/src/grabs/move_grab.rs @@ -3,7 +3,7 @@ //! eg. Usually whenever a user clicks on the app's titlebar and starts dragging, the compositors //! enters a MoveSurfaceGrab state. -use crate::State; +use crate::{render::DecoratedWindow, State}; use smithay::{ desktop::Window, input::pointer::{ @@ -18,7 +18,7 @@ use smithay::{ pub struct MoveSurfaceGrab { pub start_data: PointerGrabStartData, - pub window: Window, + pub window: DecoratedWindow, pub initial_window_location: Point, } diff --git a/src/handlers/compositor.rs b/src/handlers/compositor.rs index 383132f..fdce340 100644 --- a/src/handlers/compositor.rs +++ b/src/handlers/compositor.rs @@ -41,15 +41,15 @@ impl CompositorHandler for State { if let Some(window) = self .space .elements() - .filter(|w| w.toplevel().is_some()) - .find(|w| w.toplevel().unwrap().wl_surface() == &root) + .filter(|w| w.window.toplevel().is_some()) + .find(|w| w.window.toplevel().unwrap().wl_surface() == &root) { - window.on_commit(); + window.window.on_commit(); } }; - xdg_shell::handle_commit(&mut self.popups, &self.space, surface); - resize_grab::handle_commit(&mut self.space, surface); + // xdg_shell::handle_commit(&mut self.popups, &self.space, surface); + // resize_grab::handle_commit(&mut self.space, surface); } } diff --git a/src/handlers/xdg_activation.rs b/src/handlers/xdg_activation.rs index 58d1d56..391bb7d 100644 --- a/src/handlers/xdg_activation.rs +++ b/src/handlers/xdg_activation.rs @@ -19,7 +19,7 @@ impl XdgActivationHandler for State { { for element in self.space.elements() { - match element.underlying_surface() { + match element.window.underlying_surface() { WindowSurface::Wayland(wl) => { if *wl.wl_surface() == surface { raised_element = Some(element); diff --git a/src/handlers/xdg_decoration.rs b/src/handlers/xdg_decoration.rs index c56cae3..e977404 100644 --- a/src/handlers/xdg_decoration.rs +++ b/src/handlers/xdg_decoration.rs @@ -5,8 +5,8 @@ use wayland_protocols::xdg::decoration::zv1::server::zxdg_toplevel_decoration_v1 impl XdgDecorationHandler for State { fn new_decoration(&mut self, toplevel: smithay::wayland::shell::xdg::ToplevelSurface) { toplevel.with_pending_state(|state| { - state.decoration_mode = Some(Mode::ClientSide); - // state.decoration_mode = Some(Mode::ServerSide); + // state.decoration_mode = Some(Mode::ClientSide); + state.decoration_mode = Some(Mode::ServerSide); }); toplevel.send_configure(); } @@ -17,16 +17,16 @@ impl XdgDecorationHandler for State { mode: Mode, ) { toplevel.with_pending_state(|state| { - state.decoration_mode = Some(Mode::ClientSide); - // state.decoration_mode = Some(mode); + // state.decoration_mode = Some(Mode::ClientSide); + state.decoration_mode = Some(mode); }); toplevel.send_configure(); } fn unset_mode(&mut self, toplevel: smithay::wayland::shell::xdg::ToplevelSurface) { toplevel.with_pending_state(|state| { - state.decoration_mode = Some(Mode::ClientSide); - // state.decoration_mode = Some(Mode::ServerSide); + // state.decoration_mode = Some(Mode::ClientSide); + state.decoration_mode = Some(Mode::ServerSide); }); toplevel.send_configure(); } diff --git a/src/handlers/xdg_shell.rs b/src/handlers/xdg_shell.rs index 764eae4..0fc3343 100644 --- a/src/handlers/xdg_shell.rs +++ b/src/handlers/xdg_shell.rs @@ -25,6 +25,7 @@ use smithay::{ use crate::{ grabs::{MoveSurfaceGrab, ResizeSurfaceGrab}, + render::DecoratedWindow, state::State, }; @@ -35,7 +36,8 @@ impl XdgShellHandler for State { fn new_toplevel(&mut self, surface: ToplevelSurface) { let window = Window::new_wayland_window(surface); - self.space.map_element(window, (0, 0), false); + self.space + .map_element(DecoratedWindow::new(window), (0, 0), false); } fn new_popup(&mut self, surface: PopupSurface, _positioner: PositionerState) { @@ -69,15 +71,15 @@ impl XdgShellHandler for State { let window = self .space .elements() - .filter(|w| w.toplevel().is_some()) - .find(|w| w.toplevel().unwrap().wl_surface() == wl_surface) + .filter(|w| w.window.toplevel().is_some()) + .find(|w| w.window.toplevel().unwrap().wl_surface() == wl_surface) .unwrap() .clone(); let initial_window_location = self.space.element_location(&window).unwrap(); let grab = MoveSurfaceGrab { start_data, - window, + window: window.clone(), initial_window_location, }; @@ -102,12 +104,12 @@ impl XdgShellHandler for State { let window = self .space .elements() - .filter(|w| w.toplevel().is_some()) - .find(|w| w.toplevel().unwrap().wl_surface() == wl_surface) + .filter(|w| w.window.toplevel().is_some()) + .find(|w| w.window.toplevel().unwrap().wl_surface() == wl_surface) .unwrap() .clone(); let initial_window_location = self.space.element_location(&window).unwrap(); - let initial_window_size = window.geometry().size; + let initial_window_size = window.window.geometry().size; surface.with_pending_state(|state| { state.states.set(xdg_toplevel::State::Resizing); @@ -117,7 +119,7 @@ impl XdgShellHandler for State { let grab = ResizeSurfaceGrab::start( start_data, - window, + window.window.clone(), edges.into(), Rectangle::new(initial_window_location, initial_window_size), ); @@ -202,8 +204,8 @@ impl State { let Some(window) = self .space .elements() - .filter(|w| w.toplevel().is_some()) - .find(|w| w.toplevel().unwrap().wl_surface() == &root) + .filter(|w| w.window.toplevel().is_some()) + .find(|w| w.window.toplevel().unwrap().wl_surface() == &root) else { return; }; diff --git a/src/input.rs b/src/input.rs index e0ee859..2968780 100644 --- a/src/input.rs +++ b/src/input.rs @@ -65,29 +65,32 @@ impl State { let button_state = event.state(); if ButtonState::Pressed == button_state && !pointer.is_grabbed() { + let mut actv_window = None; if let Some((window, _loc)) = self .space .element_under(pointer.current_location()) .map(|(w, l)| (w.clone(), l)) { self.space.raise_element(&window, true); - if let Some(toplevel) = window.toplevel() { + if let Some(toplevel) = window.window.toplevel() { keyboard.set_focus(self, Some(toplevel.wl_surface().clone()), serial); } - self.space.elements().for_each(|window| { - if let Some(toplevel) = window.toplevel() { - toplevel.send_pending_configure(); - } - }); + actv_window = Some(window.clone()); } else { - self.space.elements().for_each(|window| { - window.set_activated(false); - if let Some(toplevel) = window.toplevel() { - toplevel.send_pending_configure(); - } - }); keyboard.set_focus(self, Option::::None, serial); } + + self.space.elements().for_each(|window| { + window.window.set_activated(false); + if let Some(actv_window) = actv_window.as_ref() { + if actv_window.window == window.window { + window.window.set_activated(true); + } + } + if let Some(toplevel) = window.window.toplevel() { + toplevel.send_pending_configure(); + } + }); }; pointer.button( diff --git a/src/render.rs b/src/render.rs index 5798f83..b3242a2 100644 --- a/src/render.rs +++ b/src/render.rs @@ -3,21 +3,137 @@ use smithay::{ damage::OutputDamageTracker, element::{ memory::MemoryRenderBufferRenderElement, solid::SolidColorRenderElement, - surface::WaylandSurfaceRenderElement, + surface::WaylandSurfaceRenderElement, AsRenderElements, Id, Kind, }, gles::{GlesRenderer, GlesTarget}, - ImportMem, + utils::CommitCounter, + Color32F, ImportAll, ImportDmaWl, ImportMem, ImportMemWl, Renderer, RendererSuper, }, + desktop::{space::SpaceElement, Window}, output::Output, render_elements, + utils::{IsAlive, Physical, Point, Scale}, }; +use wayland_protocols::xdg::decoration::zv1::server::zxdg_toplevel_decoration_v1::Mode; use crate::State; render_elements! { - DecorationElement where R: ImportMem; - Chrome=SolidColorRenderElement, - Label=MemoryRenderBufferRenderElement, + pub OutputElement where R: ImportMem + ImportDmaWl + ImportMemWl; + Window=WaylandSurfaceRenderElement, + Background=SolidColorRenderElement, +} + +#[derive(PartialEq, Eq, Clone)] +pub struct DecoratedWindow { + pub window: Window, +} + +impl DecoratedWindow { + pub fn new(window: Window) -> Self { + Self { window } + } + + fn decorated(&self) -> bool { + let mut b = false; + if let Some(toplevel) = self.window.toplevel() { + toplevel.with_pending_state(|f| { + if let Some(decor) = f.decoration_mode { + b = decor == Mode::ServerSide; + } + }); + } + b + } +} + +impl SpaceElement for DecoratedWindow { + fn bbox(&self) -> smithay::utils::Rectangle { + let mut bbox = self.window.bbox(); + + if self.decorated() { + bbox.loc.x -= 5; + bbox.loc.y -= 22; + } + + bbox + } + + fn is_in_input_region( + &self, + point: &smithay::utils::Point, + ) -> bool { + self.window.is_in_input_region(point) + } + + fn set_activate(&self, activated: bool) { + self.window.set_activate(activated) + } + + fn output_enter( + &self, + output: &Output, + overlap: smithay::utils::Rectangle, + ) { + self.window.output_enter(output, overlap) + } + + fn output_leave(&self, output: &Output) { + self.window.output_leave(output) + } +} + +impl IsAlive for DecoratedWindow { + fn alive(&self) -> bool { + self.window.alive() + } +} + +impl AsRenderElements for DecoratedWindow +where + R: Renderer + ImportDmaWl + ImportMemWl + ImportMem + ImportAll, + ::TextureId: Clone + Send + 'static, +{ + type RenderElement = OutputElement; // one enum, both variants + + fn render_elements>( + &self, + renderer: &mut R, + location: Point, + scale: Scale, + alpha: f32, + ) -> Vec { + let mut elements: Vec = Vec::new(); + + /* Start with actual window elements */ + elements.extend( + self.window + .render_elements::>(renderer, location, scale, alpha) + .into_iter() + .map(C::from), + ); + + /* If the window is decorated, push a solid color */ + let decorated = self.decorated(); + if decorated { + let mut bg_rect = self.window.geometry().to_physical_precise_round(scale); + + bg_rect.size.w += 10; + bg_rect.size.h += 27; + + let background = SolidColorRenderElement::new( + Id::new(), + bg_rect, + CommitCounter::default(), + Color32F::new(1.0, 0.75, 0.26, 1.0), + Kind::Unspecified, + ); + + elements.push(C::from(OutputElement::Background(background))); + } + + elements + } } impl State { @@ -30,7 +146,7 @@ impl State { ) -> Result<(), Box> { // let elem = DecorationElement:: - smithay::desktop::space::render_output::<_, WaylandSurfaceRenderElement, _, _>( + smithay::desktop::space::render_output::<_, OutputElement, _, _>( output, renderer, framebuffer, diff --git a/src/state.rs b/src/state.rs index 7c25b5d..81d8984 100644 --- a/src/state.rs +++ b/src/state.rs @@ -1,4 +1,4 @@ -use crate::audio::SystemAudioPlayer; +use crate::{audio::SystemAudioPlayer, render::DecoratedWindow}; use std::{ffi::OsString, sync::Arc}; use smithay::{ @@ -35,7 +35,7 @@ pub struct State { pub socket_name: OsString, pub display_handle: DisplayHandle, - pub space: Space, + pub space: Space, pub handle: LoopHandle<'static, Self>, pub popups: PopupManager, pub seat: Seat, @@ -213,6 +213,7 @@ impl State { .element_under(pos) .and_then(|(window, location)| { window + .window .surface_under(pos - location.to_f64(), WindowSurfaceType::ALL) .map(|(s, p)| (s, (p + location).to_f64())) }) diff --git a/src/winit.rs b/src/winit.rs index 986f644..c1a8dc7 100644 --- a/src/winit.rs +++ b/src/winit.rs @@ -2,10 +2,7 @@ use std::time::Duration; use smithay::{ backend::{ - renderer::{ - damage::OutputDamageTracker, element::surface::WaylandSurfaceRenderElement, - gles::GlesRenderer, - }, + renderer::damage::OutputDamageTracker, winit::{self, WinitEvent}, }, output::{Mode, Output, PhysicalProperties, Subpixel}, @@ -72,7 +69,7 @@ pub fn init_winit(state: &mut State) -> Result<(), Box> { backend.submit(Some(&[damage])).unwrap(); state.space.elements().for_each(|window| { - window.send_frame( + window.window.send_frame( &output, state.start_time.elapsed(), Some(Duration::ZERO), diff --git a/src/xwayland.rs b/src/xwayland.rs index 34b0c5b..177eec6 100644 --- a/src/xwayland.rs +++ b/src/xwayland.rs @@ -12,7 +12,7 @@ use smithay::{ }; use wayland_server::{Display, DisplayHandle}; -use crate::state::State; +use crate::{state::State, render::DecoratedWindow}; pub struct XWaylandState { pub xwm: Option, @@ -31,10 +31,11 @@ impl XwmHandler for State { // grant it — the window will be mapped once you set this window.set_mapped(true).unwrap(); - self.space - .map_element(Window::new_x11_window(window.clone()), (0, 0), true); - - // insert it into your Space/window management here + self.space.map_element( + DecoratedWindow::new(Window::new_x11_window(window.clone())), + (0, 0), + true, + ); } fn mapped_override_redirect_window(&mut self, _xwm: XwmId, _window: X11Surface) {}