support resizing, start on layer shell support

This commit is contained in:
IoIxD 2026-07-09 17:26:45 -07:00
commit eea1406bd4
5 changed files with 257 additions and 19 deletions

View file

@ -1,4 +1,5 @@
mod compositor;
mod wlr_layer_shell;
mod xdg_activation;
mod xdg_decoration;
mod xdg_icon;

View file

@ -0,0 +1,30 @@
use smithay::{
desktop::{layer_map_for_output, LayerSurface},
output::Output,
wayland::shell::wlr_layer::WlrLayerShellHandler,
};
use wayland_server::protocol::wl_output::WlOutput;
use crate::State;
impl WlrLayerShellHandler for State {
fn shell_state(&mut self) -> &mut smithay::wayland::shell::wlr_layer::WlrLayerShellState {
&mut self.wlr_layer_shell_state
}
fn new_layer_surface(
&mut self,
surface: smithay::wayland::shell::wlr_layer::LayerSurface,
output: Option<WlOutput>,
layer: smithay::wayland::shell::wlr_layer::Layer,
namespace: String,
) {
let output = output
.as_ref()
.and_then(Output::from_resource)
.unwrap_or_else(|| self.space.outputs().next().unwrap().clone());
let mut map = layer_map_for_output(&output);
map.map_layer(&LayerSurface::new(surface, namespace))
.unwrap();
}
}

View file

@ -1,3 +1,5 @@
use smithay::utils::Point;
use smithay::wayland::seat::WaylandFocus;
use smithay::{
desktop::{
find_popup_root_surface, get_popup_toplevel_coords, layer_map_for_output,
@ -8,6 +10,7 @@ use smithay::{
pointer::{Focus, GrabStartData as PointerGrabStartData},
Seat,
},
output::Output,
reexports::{
wayland_protocols::xdg::shell::server::xdg_toplevel,
wayland_server::{
@ -24,6 +27,7 @@ use smithay::{
},
},
};
use wayland_server::protocol::wl_output;
use crate::{
grabs::{MoveSurfaceGrab, ResizeSurfaceGrab},
@ -82,6 +86,11 @@ impl XdgShellHandler for State {
.find(|w| w.window.toplevel().unwrap().wl_surface() == wl_surface)
.unwrap()
.clone();
if window.is_fullscreen() {
return;
}
let initial_window_location = self.space.element_location(&window).unwrap();
let grab = MoveSurfaceGrab {
@ -115,6 +124,10 @@ impl XdgShellHandler for State {
.find(|w| w.window.toplevel().unwrap().wl_surface() == wl_surface)
.unwrap()
.clone();
if window.is_fullscreen() {
return;
}
let initial_window_location = self.space.element_location(&window).unwrap();
let initial_window_size = window.window.geometry().size;
@ -169,12 +182,7 @@ impl XdgShellHandler for State {
}
fn title_changed(&mut self, surface: ToplevelSurface) {
let window = self
.space
.elements()
.filter(|w| w.window.toplevel().is_some())
.find(|w| *w.window.toplevel().unwrap() == surface)
.unwrap();
let window = self.get_window_by_surface(&surface);
smithay::wayland::compositor::with_states(surface.wl_surface(), |states| {
if let Some(data) = states.data_map.get::<XdgToplevelSurfaceData>() {
@ -184,6 +192,154 @@ impl XdgShellHandler for State {
}
});
}
// fn new_client(&mut self, client: smithay::wayland::shell::xdg::ShellClient) {}
// fn client_pong(&mut self, client: smithay::wayland::shell::xdg::ShellClient) {}
fn maximize_request(&mut self, surface: ToplevelSurface) {
{
let window = self.get_window_by_surface(&surface);
if let Some(loc) = self.space.element_location(&window) {
window.set_saved_location(loc);
}
}
let window = self.get_window_by_surface(&surface).clone();
if let Some(loc) = self.space.element_location(&window) {
window.set_saved_location(loc);
}
self.space.relocate_element(&window, Point::new(0, 0));
if let Some(output) = window.output.lock().clone() {
let geom = self.space.output_geometry(&output).unwrap();
let xdg = window.window.toplevel().unwrap();
xdg.with_pending_state(|state| {
state.states.set(xdg_toplevel::State::Maximized);
state.size = Some(geom.size);
});
}
surface.send_configure();
}
fn unmaximize_request(&mut self, surface: ToplevelSurface) {
let window = self.get_window_by_surface(&surface).clone();
self.space
.relocate_element(&window, window.saved_location());
if let Some(output) = window.output.lock().clone() {
let xdg = window.window.toplevel().unwrap();
xdg.with_pending_state(|state| {
state.states.unset(xdg_toplevel::State::Maximized);
state.size = None;
});
}
surface.send_configure();
}
fn fullscreen_request(
&mut self,
surface: ToplevelSurface,
mut wl_output: Option<wl_output::WlOutput>,
) {
// NOTE: This is only one part of the solution. We can set the
// location and configure size here, but the surface should be rendered fullscreen
// independently from its buffer size
let wl_surface = surface.wl_surface();
let window = self.get_window_by_surface(&surface);
window.set_fullscreen(true);
let output_geometry = wl_output
.as_ref()
.and_then(Output::from_resource)
.or_else(|| {
let w = self.space.elements().find(|window| {
window
.window
.wl_surface()
.map(|s| &*s == wl_surface)
.unwrap_or(false)
});
w.and_then(|w| self.space.outputs_for_element(w).first().cloned())
})
.as_ref()
.and_then(|o| self.space.output_geometry(o));
if let Some(geometry) = output_geometry {
let output = wl_output
.as_ref()
.and_then(Output::from_resource)
.unwrap_or_else(|| self.space.outputs().next().unwrap().clone());
let client = match self.display_handle.get_client(wl_surface.id()) {
Ok(client) => client,
Err(_) => return,
};
for output in output.client_outputs(&client) {
wl_output = Some(output);
}
surface.with_pending_state(|state| {
state.states.set(xdg_toplevel::State::Fullscreen);
state.size = Some(geometry.size);
state.fullscreen_output = wl_output;
});
}
// The protocol demands us to always reply with a configure,
// regardless of we fulfilled the request or not
if surface.is_initial_configure_sent() {
surface.send_configure();
} else {
// Will be sent during initial configure
}
}
fn unfullscreen_request(&mut self, surface: ToplevelSurface) {
let window = self.get_window_by_surface(&surface);
window.set_fullscreen(false);
let xdg = window.window.toplevel().unwrap();
xdg.with_pending_state(|state| {
state.states.unset(xdg_toplevel::State::Fullscreen);
state.size = None;
state.fullscreen_output = None;
});
surface.send_configure();
}
// fn minimize_request(&mut self, surface: ToplevelSurface) {}
// fn show_window_menu(
// &mut self,
// surface: ToplevelSurface,
// seat: wl_seat::WlSeat,
// serial: Serial,
// location: smithay::utils::Point<i32, smithay::utils::Logical>,
// ) {
// }
// fn ack_configure(
// &mut self,
// surface: wl_surface::WlSurface,
// configure: smithay::wayland::shell::xdg::Configure,
// ) {
// }
// fn client_destroyed(&mut self, client: smithay::wayland::shell::xdg::ShellClient) {}
// fn toplevel_destroyed(&mut self, surface: ToplevelSurface) {}
// fn popup_destroyed(&mut self, surface: PopupSurface) {}
// fn app_id_changed(&mut self, surface: ToplevelSurface) {}
// fn parent_changed(&mut self, surface: ToplevelSurface) {}
}
fn check_grab(
@ -277,4 +433,12 @@ impl State {
state.geometry = state.positioner.get_unconstrained_geometry(target);
});
}
fn get_window_by_surface(&self, surface: &ToplevelSurface) -> &DecoratedWindow {
self.space
.elements()
.filter(|w| w.window.toplevel().is_some())
.find(|w| *w.window.toplevel().unwrap() == *surface)
.unwrap()
}
}

View file

@ -4,7 +4,9 @@ use cairo::{Context, Format, ImageSurface};
use parking_lot::Mutex;
use smithay::backend::allocator::Fourcc;
use smithay::backend::renderer::element::memory::MemoryRenderBuffer;
use smithay::utils::Transform;
use smithay::desktop::layer_map_for_output;
use smithay::utils::{Logical, Transform};
use smithay::wayland::commit_timing::CommitTimerBarrierStateUserData;
use smithay::{
backend::renderer::{
damage::OutputDamageTracker,
@ -36,7 +38,10 @@ const CSD_BORDER_FRAME_BOTTOM: f64 = 4.0;
pub struct DecoratedWindow {
pub window: Window,
title: Mutex<String>,
decorations: bool,
pub output: Mutex<Option<Output>>,
decorations: Mutex<bool>,
fullscreen: Mutex<bool>,
saved_location: Mutex<Point<i32, Logical>>,
}
impl PartialEq for DecoratedWindow {
@ -49,10 +54,17 @@ impl Eq for DecoratedWindow {}
impl Clone for DecoratedWindow {
fn clone(&self) -> Self {
let title = self.title.lock().clone();
let output = self.output.lock().clone();
let decorations = self.decorations.lock().clone();
let fullscreen = self.fullscreen.lock().clone();
let saved_location = self.saved_location.lock().clone();
Self {
window: self.window.clone(),
title: Mutex::new(title),
decorations: self.decorations,
output: Mutex::new(output),
decorations: Mutex::new(decorations),
fullscreen: Mutex::new(fullscreen),
saved_location: Mutex::new(saved_location),
}
}
}
@ -63,12 +75,35 @@ impl DecoratedWindow {
Self {
window,
title: Mutex::new(String::new()),
decorations,
output: Mutex::new(None),
decorations: Mutex::new(decorations),
fullscreen: Mutex::new(false),
saved_location: Mutex::new(Point::new(0, 0)),
}
}
pub fn saved_location(&self) -> Point<i32, Logical> {
self.saved_location.lock().clone()
}
pub fn set_saved_location(&self, point: Point<i32, Logical>) {
*self.saved_location.lock() = point;
}
pub fn set_title(&self, title: String) {
*self.title.lock() = title;
}
pub fn is_fullscreen(&self) -> bool {
return *self.fullscreen.lock();
}
pub fn set_fullscreen(&self, fs: bool) {
// *self.decorations.lock() = !fs;
*self.fullscreen.lock() = fs;
}
fn decorated(&self) -> bool {
let mut b = self.decorations;
let mut b = *self.decorations.lock();
if let Some(toplevel) = self.window.toplevel() {
toplevel.with_pending_state(|f| {
if let Some(decor) = f.decoration_mode {
@ -79,10 +114,6 @@ impl DecoratedWindow {
b
}
pub fn set_title(&self, title: String) {
*self.title.lock() = title;
}
fn render_border(&self, width: i32, height: i32) -> (Vec<u8>, i32) {
let mut surface = ImageSurface::create(Format::ARgb32, width, height).unwrap();
let stride = surface.stride();
@ -285,10 +316,12 @@ impl SpaceElement for DecoratedWindow {
output: &Output,
overlap: smithay::utils::Rectangle<i32, smithay::utils::Logical>,
) {
*self.output.lock() = Some(output.clone());
self.window.output_enter(output, overlap)
}
fn output_leave(&self, output: &Output) {
*self.output.lock() = None;
self.window.output_leave(output)
}
}
@ -309,12 +342,17 @@ where
fn render_elements<C: From<Self::RenderElement>>(
&self,
renderer: &mut R,
location: Point<i32, Physical>,
mut location: Point<i32, Physical>,
scale: Scale<f64>,
alpha: f32,
) -> Vec<C> {
let mut elements: Vec<C> = Vec::new();
if self.is_fullscreen() {
location.x = 0;
location.y = 0;
}
/* Start with actual window elements */
elements.extend(
self.window
@ -381,8 +419,6 @@ impl State {
framebuffer: &mut GlesTarget,
damage_tracker: &mut OutputDamageTracker,
) -> Result<(), Box<dyn std::error::Error>> {
// let elem = DecorationElement::
smithay::desktop::space::render_output::<_, OutputElement<GlesRenderer>, _, _>(
output,
renderer,
@ -394,6 +430,7 @@ impl State {
damage_tracker,
[0.07, 0.16, 0.286, 1.0],
)?;
Ok(())
}
}

View file

@ -18,7 +18,7 @@ use smithay::{
fixes::FixesState,
output::OutputManagerState,
selection::data_device::DataDeviceState,
shell::xdg::{decoration::XdgDecorationState, XdgShellState},
shell::{wlr_layer::WlrLayerShellState, xdg::XdgShellState},
shm::ShmState,
socket::ListeningSocketSource,
xdg_activation::XdgActivationState,
@ -59,6 +59,9 @@ pub struct State {
pub xdg_toplevel_tag_manager_state: XdgToplevelTagManager,
pub xdg_system_bell_state: XdgSystemBellState,
// the one wlroots protocol that everyone adopted
pub wlr_layer_shell_state: WlrLayerShellState,
pub output_manager_state: OutputManagerState,
pub xwayland_state: XWaylandState,
@ -88,6 +91,8 @@ impl State {
let xdg_toplevel_tag_manager_state = XdgToplevelTagManager::new::<Self>(&dh);
let xdg_system_bell_state = XdgSystemBellState::new::<Self>(&dh);
let wlr_layer_shell_state = WlrLayerShellState::new::<Self>(&dh);
let shm_state = ShmState::new::<Self>(&dh, vec![]);
let popups = PopupManager::default();
@ -149,6 +154,7 @@ impl State {
data_device_state,
wl_fixes_state,
output_manager_state,
wlr_layer_shell_state,
// xdg_decoration_state,
xdg_activation_state,