window decorations

This commit is contained in:
IoIxD 2026-07-06 01:26:00 -07:00
commit d78c792f05
10 changed files with 174 additions and 54 deletions

View file

@ -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<State>,
pub window: Window,
pub window: DecoratedWindow,
pub initial_window_location: Point<i32, Logical>,
}

View file

@ -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);
}
}

View file

@ -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);

View file

@ -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();
}

View file

@ -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;
};

View file

@ -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::<WlSurface>::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(

View file

@ -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<R> where R: ImportMem;
Chrome=SolidColorRenderElement,
Label=MemoryRenderBufferRenderElement<R>,
pub OutputElement<R> where R: ImportMem + ImportDmaWl + ImportMemWl;
Window=WaylandSurfaceRenderElement<R>,
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<i32, smithay::utils::Logical> {
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<f64, smithay::utils::Logical>,
) -> 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<i32, smithay::utils::Logical>,
) {
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<R> AsRenderElements<R> for DecoratedWindow
where
R: Renderer + ImportDmaWl + ImportMemWl + ImportMem + ImportAll,
<R as RendererSuper>::TextureId: Clone + Send + 'static,
{
type RenderElement = OutputElement<R>; // one enum, both variants
fn render_elements<C: From<Self::RenderElement>>(
&self,
renderer: &mut R,
location: Point<i32, Physical>,
scale: Scale<f64>,
alpha: f32,
) -> Vec<C> {
let mut elements: Vec<C> = Vec::new();
/* Start with actual window elements */
elements.extend(
self.window
.render_elements::<OutputElement<R>>(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<dyn std::error::Error>> {
// let elem = DecorationElement::
smithay::desktop::space::render_output::<_, WaylandSurfaceRenderElement<GlesRenderer>, _, _>(
smithay::desktop::space::render_output::<_, OutputElement<GlesRenderer>, _, _>(
output,
renderer,
framebuffer,

View file

@ -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<Window>,
pub space: Space<DecoratedWindow>,
pub handle: LoopHandle<'static, Self>,
pub popups: PopupManager,
pub seat: Seat<Self>,
@ -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()))
})

View file

@ -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<dyn std::error::Error>> {
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),

View file

@ -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<X11Wm>,
@ -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) {}