From 0667b031cc230c0ed50c65b464d3a29b589b6ed8 Mon Sep 17 00:00:00 2001 From: IoIxD Date: Thu, 30 Jul 2026 14:01:16 -0700 Subject: [PATCH] csd --- src/grabs/mod.rs | 5 - src/grabs/move_grab.rs | 160 ---------- src/handlers/wlr_layer_shell.rs | 18 +- src/handlers/xdg_shell.rs | 114 ++----- src/input.rs | 51 ++- src/main.rs | 3 +- src/state.rs | 60 +++- src/{grabs/resize_grab.rs => window/grab.rs} | 270 +++++++++++++++- src/{render.rs => window/mod.rs} | 246 +++------------ src/window/ssd.rs | 314 +++++++++++++++++++ src/winit.rs | 2 +- src/xwayland.rs | 2 +- 12 files changed, 723 insertions(+), 522 deletions(-) delete mode 100644 src/grabs/mod.rs delete mode 100644 src/grabs/move_grab.rs rename src/{grabs/resize_grab.rs => window/grab.rs} (60%) rename src/{render.rs => window/mod.rs} (51%) create mode 100644 src/window/ssd.rs diff --git a/src/grabs/mod.rs b/src/grabs/mod.rs deleted file mode 100644 index 5756e1b..0000000 --- a/src/grabs/mod.rs +++ /dev/null @@ -1,5 +0,0 @@ -pub mod move_grab; -pub use move_grab::MoveSurfaceGrab; - -pub mod resize_grab; -pub use resize_grab::ResizeSurfaceGrab; diff --git a/src/grabs/move_grab.rs b/src/grabs/move_grab.rs deleted file mode 100644 index ea524a8..0000000 --- a/src/grabs/move_grab.rs +++ /dev/null @@ -1,160 +0,0 @@ -//! Move grab is the state of a composer during which the client window is being dragged around. -//! -//! eg. Usually whenever a user clicks on the app's titlebar and starts dragging, the compositors -//! enters a MoveSurfaceGrab state. - -use crate::{render::DecoratedWindow, State}; -use smithay::{ - desktop::Window, - input::pointer::{ - AxisFrame, ButtonEvent, GestureHoldBeginEvent, GestureHoldEndEvent, GesturePinchBeginEvent, - GesturePinchEndEvent, GesturePinchUpdateEvent, GestureSwipeBeginEvent, - GestureSwipeEndEvent, GestureSwipeUpdateEvent, GrabStartData as PointerGrabStartData, - MotionEvent, PointerGrab, PointerInnerHandle, RelativeMotionEvent, - }, - reexports::wayland_server::protocol::wl_surface::WlSurface, - utils::{Logical, Point}, -}; - -pub struct MoveSurfaceGrab { - pub start_data: PointerGrabStartData, - pub window: DecoratedWindow, - pub initial_window_location: Point, -} - -impl PointerGrab for MoveSurfaceGrab { - fn motion( - &mut self, - data: &mut State, - handle: &mut PointerInnerHandle<'_, State>, - _focus: Option<(WlSurface, Point)>, - event: &MotionEvent, - ) { - // While the grab is active, no client has pointer focus - handle.motion(data, None, event); - - let delta = event.location - self.start_data.location; - let new_location = self.initial_window_location.to_f64() + delta; - data.space - .map_element(self.window.clone(), new_location.to_i32_round(), true); - } - - fn relative_motion( - &mut self, - data: &mut State, - handle: &mut PointerInnerHandle<'_, State>, - focus: Option<(WlSurface, Point)>, - event: &RelativeMotionEvent, - ) { - handle.relative_motion(data, focus, event); - } - - fn button( - &mut self, - data: &mut State, - handle: &mut PointerInnerHandle<'_, State>, - event: &ButtonEvent, - ) { - handle.button(data, event); - - // The button is a button code as defined in the - // Linux kernel's linux/input-event-codes.h header file, e.g. BTN_LEFT. - const BTN_LEFT: u32 = 0x110; - - if !handle.current_pressed().contains(&BTN_LEFT) { - // No more buttons are pressed, release the grab. - handle.unset_grab(self, data, event.serial, event.time, true); - } - } - - fn axis( - &mut self, - data: &mut State, - handle: &mut PointerInnerHandle<'_, State>, - details: AxisFrame, - ) { - handle.axis(data, details) - } - - fn frame(&mut self, data: &mut State, handle: &mut PointerInnerHandle<'_, State>) { - handle.frame(data); - } - - fn gesture_swipe_begin( - &mut self, - data: &mut State, - handle: &mut PointerInnerHandle<'_, State>, - event: &GestureSwipeBeginEvent, - ) { - handle.gesture_swipe_begin(data, event) - } - - fn gesture_swipe_update( - &mut self, - data: &mut State, - handle: &mut PointerInnerHandle<'_, State>, - event: &GestureSwipeUpdateEvent, - ) { - handle.gesture_swipe_update(data, event) - } - - fn gesture_swipe_end( - &mut self, - data: &mut State, - handle: &mut PointerInnerHandle<'_, State>, - event: &GestureSwipeEndEvent, - ) { - handle.gesture_swipe_end(data, event) - } - - fn gesture_pinch_begin( - &mut self, - data: &mut State, - handle: &mut PointerInnerHandle<'_, State>, - event: &GesturePinchBeginEvent, - ) { - handle.gesture_pinch_begin(data, event) - } - - fn gesture_pinch_update( - &mut self, - data: &mut State, - handle: &mut PointerInnerHandle<'_, State>, - event: &GesturePinchUpdateEvent, - ) { - handle.gesture_pinch_update(data, event) - } - - fn gesture_pinch_end( - &mut self, - data: &mut State, - handle: &mut PointerInnerHandle<'_, State>, - event: &GesturePinchEndEvent, - ) { - handle.gesture_pinch_end(data, event) - } - - fn gesture_hold_begin( - &mut self, - data: &mut State, - handle: &mut PointerInnerHandle<'_, State>, - event: &GestureHoldBeginEvent, - ) { - handle.gesture_hold_begin(data, event) - } - - fn gesture_hold_end( - &mut self, - data: &mut State, - handle: &mut PointerInnerHandle<'_, State>, - event: &GestureHoldEndEvent, - ) { - handle.gesture_hold_end(data, event) - } - - fn start_data(&self) -> &PointerGrabStartData { - &self.start_data - } - - fn unset(&mut self, _data: &mut State) {} -} diff --git a/src/handlers/wlr_layer_shell.rs b/src/handlers/wlr_layer_shell.rs index 9a5ac87..f6bd79f 100644 --- a/src/handlers/wlr_layer_shell.rs +++ b/src/handlers/wlr_layer_shell.rs @@ -1,7 +1,8 @@ use smithay::{ desktop::{layer_map_for_output, LayerSurface}, output::Output, - wayland::shell::wlr_layer::WlrLayerShellHandler, + utils::SERIAL_COUNTER, + wayland::shell::wlr_layer::{KeyboardInteractivity, Layer, WlrLayerShellHandler}, }; use wayland_server::protocol::wl_output::WlOutput; @@ -24,9 +25,20 @@ impl WlrLayerShellHandler for State { .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.clone(), namespace)) - .unwrap(); + let layer_surface = LayerSurface::new(surface.clone(), namespace); + map.map_layer(&layer_surface).unwrap(); surface.send_configure(); + + let state = layer_surface.cached_state(); + + if layer_surface.can_receive_keyboard_focus() { + let keyboard = self.seat.get_keyboard().unwrap(); + keyboard.set_focus( + self, + Some(surface.wl_surface().clone()), + SERIAL_COUNTER.next_serial(), + ); + } } fn layer_destroyed(&mut self, surface: smithay::wayland::shell::wlr_layer::LayerSurface) { diff --git a/src/handlers/xdg_shell.rs b/src/handlers/xdg_shell.rs index 6aeeb18..fa62038 100644 --- a/src/handlers/xdg_shell.rs +++ b/src/handlers/xdg_shell.rs @@ -2,14 +2,10 @@ use smithay::utils::Point; use smithay::wayland::seat::WaylandFocus; use smithay::{ desktop::{ - find_popup_root_surface, get_popup_toplevel_coords, layer_map_for_output, - PopupKeyboardGrab, PopupKind, PopupManager, PopupPointerGrab, PopupUngrabStrategy, Space, - Window, WindowSurfaceType, - }, - input::{ - pointer::{Focus, GrabStartData as PointerGrabStartData}, - Seat, + find_popup_root_surface, get_popup_toplevel_coords, PopupKeyboardGrab, PopupKind, + PopupManager, PopupPointerGrab, PopupUngrabStrategy, Space, Window, }, + input::{pointer::Focus, Seat}, output::Output, reexports::{ wayland_protocols::xdg::shell::server::xdg_toplevel, @@ -29,11 +25,7 @@ use smithay::{ }; use wayland_server::protocol::wl_output; -use crate::{ - grabs::{MoveSurfaceGrab, ResizeSurfaceGrab}, - render::DecoratedWindow, - state::State, -}; +use crate::{state::State, window::DecoratedWindow}; impl XdgShellHandler for State { fn xdg_shell_state(&mut self) -> &mut XdgShellState { @@ -72,35 +64,19 @@ impl XdgShellHandler for State { } fn move_request(&mut self, surface: ToplevelSurface, seat: wl_seat::WlSeat, serial: Serial) { - let seat = Seat::from_resource(&seat).unwrap(); + let seat: Seat = Seat::from_resource(&seat).unwrap(); let wl_surface = surface.wl_surface(); - if let Some(start_data) = check_grab(&seat, wl_surface, serial) { - let pointer = seat.get_pointer().unwrap(); + let window = self + .space + .elements() + .filter(|w| w.window.toplevel().is_some()) + .find(|w| w.window.toplevel().unwrap().wl_surface() == wl_surface) + .unwrap() + .clone(); - let window = self - .space - .elements() - .filter(|w| w.window.toplevel().is_some()) - .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 { - start_data, - window: window.clone(), - initial_window_location, - }; - - pointer.set_grab(self, grab, serial, Focus::Clear); - } + window.do_move_grab(self, &seat, &surface, serial, false); } fn resize_request( @@ -110,42 +86,19 @@ impl XdgShellHandler for State { serial: Serial, edges: xdg_toplevel::ResizeEdge, ) { - let seat = Seat::from_resource(&seat).unwrap(); + let seat: Seat = Seat::from_resource(&seat).unwrap(); let wl_surface = surface.wl_surface(); - if let Some(start_data) = check_grab(&seat, wl_surface, serial) { - let pointer = seat.get_pointer().unwrap(); + let window = self + .space + .elements() + .filter(|w| w.window.toplevel().is_some()) + .find(|w| w.window.toplevel().unwrap().wl_surface() == wl_surface) + .unwrap() + .clone(); - let window = self - .space - .elements() - .filter(|w| w.window.toplevel().is_some()) - .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; - - surface.with_pending_state(|state| { - state.states.set(xdg_toplevel::State::Resizing); - }); - - surface.send_pending_configure(); - - let grab = ResizeSurfaceGrab::start( - start_data, - window.window.clone(), - edges.into(), - Rectangle::new(initial_window_location, initial_window_size), - ); - - pointer.set_grab(self, grab, serial, Focus::Clear); - } + window.do_resize_grab(self, &seat, &surface, serial, edges, false); } fn grab(&mut self, surface: PopupSurface, seat: wl_seat::WlSeat, serial: Serial) { @@ -342,29 +295,6 @@ impl XdgShellHandler for State { // fn parent_changed(&mut self, surface: ToplevelSurface) {} } -fn check_grab( - seat: &Seat, - surface: &WlSurface, - serial: Serial, -) -> Option> { - let pointer = seat.get_pointer()?; - - // Check that this surface has a click grab. - if !pointer.has_grab(serial) { - return None; - } - - let start_data = pointer.grab_start_data()?; - - let (focus, _) = start_data.focus.as_ref()?; - // If the focus was for a different surface, ignore the request. - if !focus.id().same_client_as(&surface.id()) { - return None; - } - - Some(start_data) -} - /// Should be called on `WlSurface::commit` pub fn handle_commit(popups: &mut PopupManager, space: &Space, surface: &WlSurface) { // Handle toplevel commits. diff --git a/src/input.rs b/src/input.rs index 3397cdf..adfab2d 100644 --- a/src/input.rs +++ b/src/input.rs @@ -1,14 +1,14 @@ +use smithay::desktop::space::SpaceElement; +use smithay::reexports::x11rb::protocol::xkb::SALockControls; use smithay::{ backend::input::{ AbsolutePositionEvent, Axis, AxisSource, ButtonState, Event, InputBackend, InputEvent, KeyboardKeyEvent, PointerAxisEvent, PointerButtonEvent, }, - desktop::layer_map_for_output, input::{ keyboard::FilterResult, pointer::{AxisFrame, ButtonEvent, MotionEvent}, }, - reexports::wayland_server::protocol::wl_surface::WlSurface, utils::SERIAL_COUNTER, }; @@ -57,29 +57,20 @@ impl State { } InputEvent::PointerButton { event, .. } => { let pointer = self.seat.get_pointer().unwrap(); - let keyboard = self.seat.get_keyboard().unwrap(); let serial = SERIAL_COUNTER.next_serial(); let button = event.button_code(); let button_state = event.state(); + let seat = self.seat.clone(); + + let mut dragged_windows = vec![]; 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.window.toplevel() { - keyboard.set_focus(self, Some(toplevel.wl_surface().clone()), serial); - } - actv_window = Some(window.clone()); - } else { - keyboard.set_focus(self, Option::::None, serial); - } + + self.update_focus(serial, &mut actv_window); self.space.elements().for_each(|window| { window.window.set_activated(false); @@ -87,21 +78,17 @@ impl State { if actv_window.window == window.window { window.window.set_activated(true); } - } - if let Some(toplevel) = window.window.toplevel() { - toplevel.send_pending_configure(); - } - }); - 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); + /* window ssd actions */ + if window.decorated() { + let mut loc = pointer.current_location(); + let window_loc = self.space.element_geometry(window).unwrap(); + loc.x -= (window_loc.loc.x - window.bbox().loc.x) as f64; + loc.y -= (window_loc.loc.y - window.bbox().loc.y) as f64; + if window.in_ssd(&loc) { + dragged_windows.push((window.clone(), loc)); + } } } - if let Some(toplevel) = window.window.toplevel() { - toplevel.send_pending_configure(); - } }); }; @@ -115,6 +102,12 @@ impl State { }, ); pointer.frame(self); + + for (window, loc) in &dragged_windows.clone() { + if let Some(toplevel) = window.window.toplevel() { + window.do_ssd_action(self, &seat, toplevel, serial, &loc); + } + } } InputEvent::PointerAxis { event, .. } => { let source = event.source(); diff --git a/src/main.rs b/src/main.rs index 0cdae96..69f3a9f 100644 --- a/src/main.rs +++ b/src/main.rs @@ -3,10 +3,9 @@ mod handlers; mod audio; -mod grabs; mod input; -pub mod render; mod state; +pub mod window; mod winit; mod xwayland; diff --git a/src/state.rs b/src/state.rs index c1e1792..acb1f08 100644 --- a/src/state.rs +++ b/src/state.rs @@ -1,8 +1,8 @@ -use crate::{audio::SystemAudioPlayer, render::DecoratedWindow}; +use crate::{audio::SystemAudioPlayer, window::DecoratedWindow}; use std::{ffi::OsString, sync::Arc}; use smithay::{ - desktop::{PopupManager, Space, Window, WindowSurfaceType}, + desktop::{layer_map_for_output, PopupManager, Space, Window, WindowSurfaceType}, input::{Seat, SeatState}, reexports::{ calloop::{generic::Generic, Interest, LoopHandle, Mode, PostAction}, @@ -12,13 +12,16 @@ use smithay::{ Display, DisplayHandle, }, }, - utils::{Logical, Point}, + utils::{Logical, Point, Serial}, wayland::{ compositor::{CompositorClientState, CompositorState}, fixes::FixesState, output::OutputManagerState, selection::data_device::DataDeviceState, - shell::{wlr_layer::WlrLayerShellState, xdg::XdgShellState}, + shell::{ + wlr_layer::{Layer, WlrLayerShellState}, + xdg::{decoration::XdgDecorationState, XdgShellState}, + }, shm::ShmState, socket::ListeningSocketSource, xdg_activation::XdgActivationState, @@ -53,7 +56,7 @@ pub struct State { // xdg pub xdg_shell_state: XdgShellState, - // pub xdg_decoration_state: XdgDecorationState, + pub xdg_decoration_state: XdgDecorationState, pub xdg_activation_state: XdgActivationState, pub xdg_toplevel_icon_manager_state: XdgToplevelIconManager, pub xdg_toplevel_tag_manager_state: XdgToplevelTagManager, @@ -85,7 +88,7 @@ impl State { let xdg_shell_state = XdgShellState::new::(&dh); let xdg_shell_state = XdgShellState::new::(&dh); - // let xdg_decoration_state = XdgDecorationState::new::(&dh); + let xdg_decoration_state = XdgDecorationState::new::(&dh); let xdg_activation_state = XdgActivationState::new::(&dh); let xdg_toplevel_icon_manager_state = XdgToplevelIconManager::new::(&dh); let xdg_toplevel_tag_manager_state = XdgToplevelTagManager::new::(&dh); @@ -156,7 +159,7 @@ impl State { output_manager_state, wlr_layer_shell_state, - // xdg_decoration_state, + xdg_decoration_state, xdg_activation_state, xdg_toplevel_icon_manager_state, xdg_toplevel_tag_manager_state, @@ -223,6 +226,49 @@ impl State { }) } + pub fn update_focus(&mut self, serial: Serial, actv_window: &mut Option) { + /* layer map under? */ + let pointer = self.seat.get_pointer().unwrap(); + let keyboard = self.seat.get_keyboard().unwrap(); + let output = self.space.outputs().next().unwrap().clone(); + let map = layer_map_for_output(&output); + + let layers_p1 = vec![Layer::Overlay, Layer::Top]; + let layers_p2 = vec![Layer::Bottom, Layer::Background]; + + keyboard.set_focus(self, Option::::None, serial); + + for l in layers_p1 { + if let Some(layer) = map.layer_under(l, pointer.current_location()) { + if layer.can_receive_keyboard_focus() { + keyboard.set_focus(self, Some(layer.wl_surface().clone()), serial); + } + return; + } + } + + 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.window.toplevel() { + keyboard.set_focus(self, Some(toplevel.wl_surface().clone()), serial); + } + *actv_window = Some(window.clone()); + return; + } + + for l in layers_p2 { + if let Some(layer) = map.layer_under(l, pointer.current_location()) { + if layer.can_receive_keyboard_focus() { + keyboard.set_focus(self, Some(layer.wl_surface().clone()), serial); + } + } + } + } + pub fn spawn_clients() { /* todo: read from shit */ std::process::Command::new("startmde").spawn().ok(); diff --git a/src/grabs/resize_grab.rs b/src/window/grab.rs similarity index 60% rename from src/grabs/resize_grab.rs rename to src/window/grab.rs index 209cfbb..50ed990 100644 --- a/src/grabs/resize_grab.rs +++ b/src/window/grab.rs @@ -1,26 +1,268 @@ -//! Resize grab is the state of a composer during which the client window is being resized. -//! -//! eg. Usually whenever a user clicks on the app's border and starts dragging, the compositors -//! enters a ResizeSurfaceGrab state. - -use crate::State; +use smithay::input::pointer::GrabStartData as PointerGrabStartData; +use smithay::utils::Rectangle; +use smithay::wayland::shell::xdg::ToplevelSurface; use smithay::{ desktop::{Space, Window}, + utils::Size, + wayland::{compositor, shell::xdg::SurfaceCachedState}, +}; +use smithay::{ input::pointer::{ AxisFrame, ButtonEvent, GestureHoldBeginEvent, GestureHoldEndEvent, GesturePinchBeginEvent, GesturePinchEndEvent, GesturePinchUpdateEvent, GestureSwipeBeginEvent, - GestureSwipeEndEvent, GestureSwipeUpdateEvent, GrabStartData as PointerGrabStartData, - MotionEvent, PointerGrab, PointerInnerHandle, RelativeMotionEvent, + GestureSwipeEndEvent, GestureSwipeUpdateEvent, MotionEvent, PointerGrab, + PointerInnerHandle, RelativeMotionEvent, }, - reexports::{ - wayland_protocols::xdg::shell::server::xdg_toplevel, - wayland_server::protocol::wl_surface::WlSurface, - }, - utils::{Logical, Point, Rectangle, Size}, - wayland::{compositor, shell::xdg::SurfaceCachedState}, + utils::{Logical, Point}, +}; +use smithay::{ + input::{pointer::Focus, Seat}, + utils::Serial, }; use std::cell::RefCell; +use crate::State; +use wayland_protocols::xdg::shell::server::xdg_toplevel; +use wayland_server::{protocol::wl_surface::WlSurface, Resource}; + +use super::DecoratedWindow; + +impl DecoratedWindow { + pub fn do_move_grab( + &self, + state: &mut State, + seat: &Seat, + surface: &ToplevelSurface, + serial: Serial, + do_override: bool, + ) { + let wl_surface = surface.wl_surface(); + if let Some(start_data) = check_grab(&seat, wl_surface, serial, do_override) { + let pointer = seat.get_pointer().unwrap(); + if self.is_fullscreen() { + return; + } + + let initial_window_location = state.space.element_location(&self).unwrap(); + + let grab = MoveSurfaceGrab { + start_data, + window: self.clone(), + initial_window_location, + }; + + pointer.set_grab(state, grab, serial, Focus::Clear); + } + } + + pub fn do_resize_grab( + &self, + state: &mut State, + seat: &Seat, + surface: &ToplevelSurface, + serial: Serial, + edges: xdg_toplevel::ResizeEdge, + do_override: bool, + ) { + let wl_surface = surface.wl_surface(); + if let Some(start_data) = check_grab(&seat, wl_surface, serial, do_override) { + let pointer = seat.get_pointer().unwrap(); + + if self.is_fullscreen() { + return; + } + let initial_window_location = state.space.element_location(&self).unwrap(); + let initial_window_size = self.window.geometry().size; + + surface.with_pending_state(|state| { + state.states.set(xdg_toplevel::State::Resizing); + }); + + surface.send_pending_configure(); + + let grab = ResizeSurfaceGrab::start( + start_data, + self.window.clone(), + edges.into(), + Rectangle::new(initial_window_location, initial_window_size), + ); + + pointer.set_grab(state, grab, serial, Focus::Clear); + } + } +} + +fn check_grab( + seat: &Seat, + surface: &WlSurface, + serial: Serial, + do_override: bool, +) -> Option> { + let pointer = seat.get_pointer()?; + + if !do_override { + // Check that this surface has a click grab. + if !pointer.has_grab(serial) { + return None; + } + } + + let start_data = pointer.grab_start_data()?; + + if !do_override { + let (focus, _) = start_data.focus.as_ref()?; + // If the focus was for a different surface, ignore the request. + if !focus.id().same_client_as(&surface.id()) && !do_override { + return None; + } + } + + Some(start_data) +} + +pub struct MoveSurfaceGrab { + pub start_data: PointerGrabStartData, + pub window: DecoratedWindow, + pub initial_window_location: Point, +} + +impl PointerGrab for MoveSurfaceGrab { + fn motion( + &mut self, + data: &mut State, + handle: &mut PointerInnerHandle<'_, State>, + _focus: Option<(WlSurface, Point)>, + event: &MotionEvent, + ) { + // While the grab is active, no client has pointer focus + handle.motion(data, None, event); + + let delta = event.location - self.start_data.location; + let new_location = self.initial_window_location.to_f64() + delta; + data.space + .map_element(self.window.clone(), new_location.to_i32_round(), true); + } + + fn relative_motion( + &mut self, + data: &mut State, + handle: &mut PointerInnerHandle<'_, State>, + focus: Option<(WlSurface, Point)>, + event: &RelativeMotionEvent, + ) { + handle.relative_motion(data, focus, event); + } + + fn button( + &mut self, + data: &mut State, + handle: &mut PointerInnerHandle<'_, State>, + event: &ButtonEvent, + ) { + handle.button(data, event); + + // The button is a button code as defined in the + // Linux kernel's linux/input-event-codes.h header file, e.g. BTN_LEFT. + const BTN_LEFT: u32 = 0x110; + + if !handle.current_pressed().contains(&BTN_LEFT) { + // No more buttons are pressed, release the grab. + handle.unset_grab(self, data, event.serial, event.time, true); + } + } + + fn axis( + &mut self, + data: &mut State, + handle: &mut PointerInnerHandle<'_, State>, + details: AxisFrame, + ) { + handle.axis(data, details) + } + + fn frame(&mut self, data: &mut State, handle: &mut PointerInnerHandle<'_, State>) { + handle.frame(data); + } + + fn gesture_swipe_begin( + &mut self, + data: &mut State, + handle: &mut PointerInnerHandle<'_, State>, + event: &GestureSwipeBeginEvent, + ) { + handle.gesture_swipe_begin(data, event) + } + + fn gesture_swipe_update( + &mut self, + data: &mut State, + handle: &mut PointerInnerHandle<'_, State>, + event: &GestureSwipeUpdateEvent, + ) { + handle.gesture_swipe_update(data, event) + } + + fn gesture_swipe_end( + &mut self, + data: &mut State, + handle: &mut PointerInnerHandle<'_, State>, + event: &GestureSwipeEndEvent, + ) { + handle.gesture_swipe_end(data, event) + } + + fn gesture_pinch_begin( + &mut self, + data: &mut State, + handle: &mut PointerInnerHandle<'_, State>, + event: &GesturePinchBeginEvent, + ) { + handle.gesture_pinch_begin(data, event) + } + + fn gesture_pinch_update( + &mut self, + data: &mut State, + handle: &mut PointerInnerHandle<'_, State>, + event: &GesturePinchUpdateEvent, + ) { + handle.gesture_pinch_update(data, event) + } + + fn gesture_pinch_end( + &mut self, + data: &mut State, + handle: &mut PointerInnerHandle<'_, State>, + event: &GesturePinchEndEvent, + ) { + handle.gesture_pinch_end(data, event) + } + + fn gesture_hold_begin( + &mut self, + data: &mut State, + handle: &mut PointerInnerHandle<'_, State>, + event: &GestureHoldBeginEvent, + ) { + handle.gesture_hold_begin(data, event) + } + + fn gesture_hold_end( + &mut self, + data: &mut State, + handle: &mut PointerInnerHandle<'_, State>, + event: &GestureHoldEndEvent, + ) { + handle.gesture_hold_end(data, event) + } + + fn start_data(&self) -> &PointerGrabStartData { + &self.start_data + } + + fn unset(&mut self, _data: &mut State) {} +} + bitflags::bitflags! { #[derive(Clone, Copy, Debug, PartialEq, Eq, Hash)] pub struct ResizeEdge: u32 { diff --git a/src/render.rs b/src/window/mod.rs similarity index 51% rename from src/render.rs rename to src/window/mod.rs index 40ba75c..5b1c49d 100644 --- a/src/render.rs +++ b/src/window/mod.rs @@ -2,13 +2,13 @@ use std::time::Duration; use crate::State; -use cairo::{Context, Format, ImageSurface}; use parking_lot::Mutex; use smithay::backend::allocator::Fourcc; use smithay::backend::renderer::element::memory::MemoryRenderBuffer; use smithay::desktop::layer_map_for_output; +use smithay::desktop::utils::send_frames_surface_tree; use smithay::utils::{Logical, Transform}; -use smithay::wayland::commit_timing::CommitTimerBarrierStateUserData; +use smithay::wayland::seat::WaylandFocus; use smithay::{ backend::renderer::{ damage::OutputDamageTracker, @@ -26,17 +26,19 @@ use smithay::{ }; use wayland_protocols::xdg::decoration::zv1::server::zxdg_toplevel_decoration_v1::Mode; +use self::ssd::{ + SSD_BORDER_FRAME_BOTTOM, SSD_BORDER_FRAME_LEFT, SSD_BORDER_FRAME_RIGHT, SSD_BORDER_FRAME_TOP, +}; + +pub mod grab; +pub mod ssd; + render_elements! { pub OutputElement where R: ImportMem + ImportDmaWl + ImportMemWl; Window=WaylandSurfaceRenderElement, Border=MemoryRenderBufferRenderElement, } -const CSD_BORDER_FRAME_LEFT: f64 = 5.0; -const CSD_BORDER_FRAME_RIGHT: f64 = 5.0; -const CSD_BORDER_FRAME_TOP: f64 = 23.0; -const CSD_BORDER_FRAME_BOTTOM: f64 = 4.0; - pub struct DecoratedWindow { pub window: Window, title: Mutex, @@ -73,7 +75,6 @@ impl Clone for DecoratedWindow { impl DecoratedWindow { pub fn new(window: Window, decorations: bool) -> Self { - // let surface = ImageSurface::create(Format::ARgb32, width, height).unwrap(); Self { window, title: Mutex::new(String::new()), @@ -104,7 +105,7 @@ impl DecoratedWindow { *self.fullscreen.lock() = fs; } - fn decorated(&self) -> bool { + pub fn decorated(&self) -> bool { let mut b = *self.decorations.lock(); if let Some(toplevel) = self.window.toplevel() { toplevel.with_pending_state(|f| { @@ -115,179 +116,6 @@ impl DecoratedWindow { } b } - - fn render_border(&self, width: i32, height: i32) -> (Vec, i32) { - let mut surface = ImageSurface::create(Format::ARgb32, width, height).unwrap(); - let stride = surface.stride(); - - { - let ctx = Context::new(&surface).unwrap(); // scoped — see note below - ctx.set_source_rgba(0.2, 0.2, 0.2, 1.0); - ctx.set_line_width(1.0); - - let (w, h) = (width as f64, height as f64); - - macro_rules! do_box { - ($x:expr, $y:expr, $col:expr, $w:expr, $h:expr) => { - ctx.rectangle(($x) as f64, ($y) as f64, ($w) as f64, ($h) as f64); - ctx.set_source_rgb(($col) as f64, ($col) as f64, ($col) as f64); - ctx.fill().unwrap(); - }; - } - - macro_rules! do_line { - ($off:expr, $col:expr) => { - ctx.move_to($off, $off); - ctx.line_to($off, h - $off); - ctx.set_source_rgb($col, $col, $col); - ctx.stroke().unwrap(); - ctx.move_to($off, $off); - ctx.line_to(w - $off, $off); - ctx.set_source_rgb($col, $col, $col); - ctx.stroke().unwrap(); - }; - } - - ctx.set_antialias(cairo::Antialias::None); - - /* border */ - do_box!(0.0, 0.0, 0.14901961, w, h); - do_box!(1.0, 1.0, 0.55686275, w - 2.0, h - 2.0); - do_box!(2.0, 2.0, 0.82352941, w - 4.0, h - 4.0); - do_box!(3.0, 3.0, 0.93333333, w - 6.0, h - 6.0); - do_box!(4.0, 4.0, 1.0, w - 8.0, h - 8.0); - do_box!(4.0, 4.0, 0.14901961, w - 9.0, h - 9.0); - do_line!(1.0, 1.0); - do_line!(2.0, 0.93333333); - do_line!(3.0, 0.82352941); - do_line!(4.0, 0.55686275); - do_line!(5.0, 0.14901961); - for y in 0..18 { - let y = y as f64; - let a = FloatColor { - r: 0.2627451, - g: 0.37254902, - b: 0.49411765, - }; - let b = FloatColor { - r: 0.05490196, - g: 0.17254902, - b: 0.30588235, - }; - let c = lerp_color(a, b, y / 18.0); - - ctx.set_source_rgb(c.r, c.g, c.b); - - ctx.move_to(5.0, y + 6.0); - ctx.line_to(w as f64 - 5.0, y + 6.0); - ctx.stroke().unwrap(); - } - - /* icon */ - do_box!(5, 5, 1, 18, 18); - do_box!(6, 6, 0.35294118, 17, 17); - do_box!(6, 6, 0.82352941, 16, 16); - - /* close */ - do_box!( - w - 18.0 - CSD_BORDER_FRAME_LEFT, - CSD_BORDER_FRAME_BOTTOM + 1.0, - 1, - 18, - 18 - ); - do_box!( - w - 17.0 - CSD_BORDER_FRAME_LEFT, - CSD_BORDER_FRAME_BOTTOM + 2.0, - 0.35294118, - 17, - 17 - ); - do_box!( - w - 17.0 - CSD_BORDER_FRAME_LEFT, - CSD_BORDER_FRAME_BOTTOM + 2.0, - 0.82352941, - 16, - 16 - ); - do_box!( - w - 17.0 - CSD_BORDER_FRAME_LEFT + 5.0, - CSD_BORDER_FRAME_BOTTOM + 7.0, - 0, - 6, - 6 - ); - do_box!( - w - 17.0 - CSD_BORDER_FRAME_LEFT + 6.0, - CSD_BORDER_FRAME_BOTTOM + 8.0, - 0.82352941, - 4, - 4 - ); - - /* maxim */ - do_box!( - w - 36.0 - CSD_BORDER_FRAME_LEFT, - CSD_BORDER_FRAME_BOTTOM + 1.0, - 1, - 18, - 18 - ); - do_box!( - w - 35.0 - CSD_BORDER_FRAME_LEFT, - CSD_BORDER_FRAME_BOTTOM + 2.0, - 0.35294118, - 17, - 17 - ); - do_box!( - w - 35.0 - CSD_BORDER_FRAME_LEFT, - CSD_BORDER_FRAME_BOTTOM + 2.0, - 0.82352941, - 16, - 16 - ); - do_box!( - w - 35.0 - CSD_BORDER_FRAME_LEFT + 5.0, - CSD_BORDER_FRAME_BOTTOM + 7.0, - 0, - 6, - 6 - ); - do_box!( - w - 35.0 - CSD_BORDER_FRAME_LEFT, - CSD_BORDER_FRAME_BOTTOM + 9.0, - 0.82352941, - 16, - 2 - ); - do_box!( - w - 35.0 - CSD_BORDER_FRAME_LEFT + 7.0, - CSD_BORDER_FRAME_BOTTOM + 4.0, - 0.82352941, - 2, - 14 - ); - do_box!( - w - 35.0 - CSD_BORDER_FRAME_LEFT + 6.0, - CSD_BORDER_FRAME_BOTTOM + 8.0, - 0.82352941, - 4, - 4 - ); - - /* title */ - ctx.set_antialias(cairo::Antialias::Best); - ctx.move_to(28.0, 19.0); - ctx.set_font_size(12.0); - ctx.select_font_face("", cairo::FontSlant::Normal, cairo::FontWeight::Normal); - ctx.text_path(&self.title.lock().clone()); - ctx.fill().unwrap(); - } // ctx dropped here - - surface.flush(); - (surface.data().unwrap().to_vec(), stride) - } } impl SpaceElement for DecoratedWindow { @@ -295,8 +123,10 @@ impl SpaceElement for DecoratedWindow { let mut bbox = self.window.bbox_with_popups(); if self.decorated() { - bbox.loc.x -= 5; - bbox.loc.y -= 24; + bbox.loc.x -= SSD_BORDER_FRAME_LEFT as i32; + bbox.loc.y -= SSD_BORDER_FRAME_TOP as i32; + bbox.size.w += (SSD_BORDER_FRAME_LEFT + SSD_BORDER_FRAME_RIGHT) as i32; + bbox.size.h += (SSD_BORDER_FRAME_TOP + SSD_BORDER_FRAME_BOTTOM) as i32; } bbox @@ -306,7 +136,11 @@ impl SpaceElement for DecoratedWindow { &self, point: &smithay::utils::Point, ) -> bool { - self.window.is_in_input_region(point) + if !self.window.is_in_input_region(point) { + return self.in_ssd(point); + } else { + return true; + } } fn set_activate(&self, activated: bool) { @@ -326,6 +160,12 @@ impl SpaceElement for DecoratedWindow { *self.output.lock() = None; self.window.output_leave(output) } + + fn z_index(&self) -> u8 { + smithay::desktop::space::RenderZindex::Overlay as u8 + } + + fn refresh(&self) {} } impl IsAlive for DecoratedWindow { @@ -373,7 +213,7 @@ where let width = bg_rect.size.w; let height = bg_rect.size.h; - let data = self.render_border(width, height); + let data = self.render_csd(width, height); let buffer = MemoryRenderBuffer::from_slice( &data.0, Fourcc::Argb8888, @@ -400,19 +240,6 @@ where } } -struct FloatColor { - r: f64, - g: f64, - b: f64, -} -fn lerp_color(a: FloatColor, b: FloatColor, t: f64) -> FloatColor { - return FloatColor { - r: a.r + (b.r - a.r) * t, - g: a.g + (b.g - a.g) * t, - b: a.b + (b.b - a.b) * t, - }; -} - impl State { pub fn render( &self, @@ -422,12 +249,15 @@ impl State { damage_tracker: &mut OutputDamageTracker, ) -> Result<(), Box> { self.space.elements().for_each(|window| { - window.window.send_frame( - &output, - self.start_time.elapsed(), - Some(Duration::ZERO), - |_, _| Some(output.clone()), - ) + if let Some(surface) = window.window.wl_surface() { + send_frames_surface_tree( + &surface, + &output, + self.start_time.elapsed(), + Some(Duration::ZERO), + |_, _| Some(output.clone()), + ); + } }); self.space.outputs().for_each(|output| { @@ -435,13 +265,13 @@ impl State { map.arrange(); map.layers().for_each(|layer| { - layer.layer_surface().send_pending_configure(); - layer.send_frame( + send_frames_surface_tree( + layer.wl_surface(), &output, self.start_time.elapsed(), Some(Duration::ZERO), |_, _| Some(output.clone()), - ) + ); }) }); diff --git a/src/window/ssd.rs b/src/window/ssd.rs new file mode 100644 index 0000000..e43eaa4 --- /dev/null +++ b/src/window/ssd.rs @@ -0,0 +1,314 @@ +use cairo::{Context, Format, ImageSurface}; +use smithay::{ + input::Seat, + output::Scale, + utils::{Logical, Point, Serial}, + wayland::shell::xdg::ToplevelSurface, +}; +use wayland_protocols::xdg::shell::server::xdg_toplevel; + +use crate::State; + +use super::DecoratedWindow; + +pub const SSD_BORDER_FRAME_LEFT: f64 = 5.0; +pub const SSD_BORDER_FRAME_RIGHT: f64 = 5.0; +pub const SSD_BORDER_FRAME_TOP: f64 = 23.0; +pub const SSD_BORDER_FRAME_BOTTOM: f64 = 4.0; + +struct FloatColor { + r: f64, + g: f64, + b: f64, +} +fn lerp_color(a: FloatColor, b: FloatColor, t: f64) -> FloatColor { + return FloatColor { + r: a.r + (b.r - a.r) * t, + g: a.g + (b.g - a.g) * t, + b: a.b + (b.b - a.b) * t, + }; +} + +impl DecoratedWindow { + pub fn in_ssd(&self, point: &Point) -> bool { + let loc = self.window.bbox(); + return (point.x < loc.loc.x as f64 && point.x > loc.loc.x as f64 - SSD_BORDER_FRAME_LEFT) + || (point.x > loc.size.w as f64 + && point.x + < (loc.size.w as f64 + (SSD_BORDER_FRAME_LEFT + SSD_BORDER_FRAME_RIGHT))) + || point.y < loc.loc.y as f64 && point.y > loc.loc.y as f64 - SSD_BORDER_FRAME_TOP + || (point.y > loc.size.h as f64 + && point.y + < (loc.size.h as f64 + (SSD_BORDER_FRAME_TOP + SSD_BORDER_FRAME_BOTTOM))); + } + + pub fn do_ssd_action( + &self, + state: &mut State, + seat: &Seat, + surface: &ToplevelSurface, + serial: Serial, + point: &Point, + ) { + let loc = self.window.bbox(); + /* leftmost */ + let leftmost = + point.x < loc.loc.x as f64 && point.x > loc.loc.x as f64 - SSD_BORDER_FRAME_LEFT; + let rightmost = point.x > loc.size.w as f64 + && point.x < (loc.size.w as f64 + (SSD_BORDER_FRAME_LEFT + SSD_BORDER_FRAME_RIGHT)); + let topmost = + point.y < loc.loc.y as f64 && point.y > loc.loc.y as f64 - SSD_BORDER_FRAME_TOP; + let bottommost = point.y > loc.size.h as f64 + && point.y < (loc.size.h as f64 + (SSD_BORDER_FRAME_TOP + SSD_BORDER_FRAME_BOTTOM)); + if leftmost { + self.do_resize_grab( + state, + seat, + surface, + serial, + { + if topmost { + xdg_toplevel::ResizeEdge::TopLeft + } else if bottommost { + xdg_toplevel::ResizeEdge::BottomLeft + } else { + xdg_toplevel::ResizeEdge::Left + } + }, + true, + ) + } + /* rightmost */ + else if rightmost { + self.do_resize_grab( + state, + seat, + surface, + serial, + if topmost { + xdg_toplevel::ResizeEdge::TopRight + } else if bottommost { + xdg_toplevel::ResizeEdge::BottomRight + } else { + xdg_toplevel::ResizeEdge::Right + }, + true, + ) + } + /* topmost */ + else if topmost { + /* title bar */ + if point.y < loc.loc.y as f64 + && point.y > loc.loc.y as f64 - (SSD_BORDER_FRAME_TOP - 5.0) + { + self.do_move_grab(state, seat, surface, serial, true) + } else { + /* topmost edge */ + self.do_resize_grab( + state, + seat, + surface, + serial, + if leftmost { + xdg_toplevel::ResizeEdge::TopLeft + } else if rightmost { + xdg_toplevel::ResizeEdge::TopRight + } else { + xdg_toplevel::ResizeEdge::Top + }, + true, + ); + } + } + /* bottommost */ + else if bottommost { + self.do_resize_grab( + state, + seat, + surface, + serial, + if leftmost { + xdg_toplevel::ResizeEdge::BottomLeft + } else if rightmost { + xdg_toplevel::ResizeEdge::BottomRight + } else { + xdg_toplevel::ResizeEdge::Bottom + }, + true, + ) + } + } + + pub fn render_csd(&self, width: i32, height: i32) -> (Vec, i32) { + let mut surface = ImageSurface::create(Format::ARgb32, width, height).unwrap(); + let stride = surface.stride(); + + { + let ctx = Context::new(&surface).unwrap(); // scoped — see note below + ctx.set_source_rgba(0.2, 0.2, 0.2, 1.0); + ctx.set_line_width(1.0); + + let (w, h) = (width as f64, height as f64); + + macro_rules! do_box { + ($x:expr, $y:expr, $col:expr, $w:expr, $h:expr) => { + ctx.rectangle(($x) as f64, ($y) as f64, ($w) as f64, ($h) as f64); + ctx.set_source_rgb(($col) as f64, ($col) as f64, ($col) as f64); + ctx.fill().unwrap(); + }; + } + + macro_rules! do_line { + ($off:expr, $col:expr) => { + ctx.move_to($off, $off); + ctx.line_to($off, h - $off); + ctx.set_source_rgb($col, $col, $col); + ctx.stroke().unwrap(); + ctx.move_to($off, $off); + ctx.line_to(w - $off, $off); + ctx.set_source_rgb($col, $col, $col); + ctx.stroke().unwrap(); + }; + } + + ctx.set_antialias(cairo::Antialias::None); + + /* border */ + do_box!(0.0, 0.0, 0.14901961, w, h); + do_box!(1.0, 1.0, 0.55686275, w - 2.0, h - 2.0); + do_box!(2.0, 2.0, 0.82352941, w - 4.0, h - 4.0); + do_box!(3.0, 3.0, 0.93333333, w - 6.0, h - 6.0); + do_box!(4.0, 4.0, 1.0, w - 8.0, h - 8.0); + do_box!(4.0, 4.0, 0.14901961, w - 9.0, h - 9.0); + do_line!(1.0, 1.0); + do_line!(2.0, 0.93333333); + do_line!(3.0, 0.82352941); + do_line!(4.0, 0.55686275); + do_line!(5.0, 0.14901961); + for y in 0..18 { + let y = y as f64; + let a = FloatColor { + r: 0.2627451, + g: 0.37254902, + b: 0.49411765, + }; + let b = FloatColor { + r: 0.05490196, + g: 0.17254902, + b: 0.30588235, + }; + let c = lerp_color(a, b, y / 18.0); + + ctx.set_source_rgb(c.r, c.g, c.b); + + ctx.move_to(5.0, y + 6.0); + ctx.line_to(w as f64 - 5.0, y + 6.0); + ctx.stroke().unwrap(); + } + + /* icon */ + do_box!(5, 5, 1, 18, 18); + do_box!(6, 6, 0.35294118, 17, 17); + do_box!(6, 6, 0.82352941, 16, 16); + + /* close */ + do_box!( + w - 18.0 - SSD_BORDER_FRAME_LEFT, + SSD_BORDER_FRAME_BOTTOM + 1.0, + 1, + 18, + 18 + ); + do_box!( + w - 17.0 - SSD_BORDER_FRAME_LEFT, + SSD_BORDER_FRAME_BOTTOM + 2.0, + 0.35294118, + 17, + 17 + ); + do_box!( + w - 17.0 - SSD_BORDER_FRAME_LEFT, + SSD_BORDER_FRAME_BOTTOM + 2.0, + 0.82352941, + 16, + 16 + ); + do_box!( + w - 17.0 - SSD_BORDER_FRAME_LEFT + 5.0, + SSD_BORDER_FRAME_BOTTOM + 7.0, + 0, + 6, + 6 + ); + do_box!( + w - 17.0 - SSD_BORDER_FRAME_LEFT + 6.0, + SSD_BORDER_FRAME_BOTTOM + 8.0, + 0.82352941, + 4, + 4 + ); + + /* maxim */ + do_box!( + w - 36.0 - SSD_BORDER_FRAME_LEFT, + SSD_BORDER_FRAME_BOTTOM + 1.0, + 1, + 18, + 18 + ); + do_box!( + w - 35.0 - SSD_BORDER_FRAME_LEFT, + SSD_BORDER_FRAME_BOTTOM + 2.0, + 0.35294118, + 17, + 17 + ); + do_box!( + w - 35.0 - SSD_BORDER_FRAME_LEFT, + SSD_BORDER_FRAME_BOTTOM + 2.0, + 0.82352941, + 16, + 16 + ); + do_box!( + w - 35.0 - SSD_BORDER_FRAME_LEFT + 5.0, + SSD_BORDER_FRAME_BOTTOM + 7.0, + 0, + 6, + 6 + ); + do_box!( + w - 35.0 - SSD_BORDER_FRAME_LEFT, + SSD_BORDER_FRAME_BOTTOM + 9.0, + 0.82352941, + 16, + 2 + ); + do_box!( + w - 35.0 - SSD_BORDER_FRAME_LEFT + 7.0, + SSD_BORDER_FRAME_BOTTOM + 4.0, + 0.82352941, + 2, + 14 + ); + do_box!( + w - 35.0 - SSD_BORDER_FRAME_LEFT + 6.0, + SSD_BORDER_FRAME_BOTTOM + 8.0, + 0.82352941, + 4, + 4 + ); + + /* title */ + ctx.set_antialias(cairo::Antialias::Best); + ctx.move_to(28.0, 19.0); + ctx.set_font_size(12.0); + ctx.select_font_face("", cairo::FontSlant::Normal, cairo::FontWeight::Normal); + ctx.text_path(&self.title.lock().clone()); + ctx.fill().unwrap(); + } // ctx dropped here + + surface.flush(); + (surface.data().unwrap().to_vec(), stride) + } +} diff --git a/src/winit.rs b/src/winit.rs index 2c4926f..55196c0 100644 --- a/src/winit.rs +++ b/src/winit.rs @@ -6,7 +6,7 @@ use smithay::{ winit::{self, WinitEvent}, }, output::{Mode, Output, PhysicalProperties, Subpixel}, - utils::{Rectangle, Transform}, + utils::{Rectangle, Transform, SERIAL_COUNTER}, }; use crate::State; diff --git a/src/xwayland.rs b/src/xwayland.rs index 6fdf3ec..fabed4e 100644 --- a/src/xwayland.rs +++ b/src/xwayland.rs @@ -12,7 +12,7 @@ use smithay::{ }; use wayland_server::DisplayHandle; -use crate::{render::DecoratedWindow, state::State}; +use crate::{state::State, window::DecoratedWindow}; pub struct XWaylandState { pub xwm: Option,