diff --git a/Cargo.toml b/Cargo.toml index 741e9dc..071edc9 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -21,9 +21,10 @@ calloop = { version = "0.10.1", features = ["executor", "futures-io"] } futures-lite = { version = "1.13.0" } async-process = { version = "1.7.0" } itertools = { version = "0.11.0" } +x11rb = { version = "0.11.1", default-features = false, features = ["composite"], optional = true } [features] -default = ["egl", "winit", "udev"] +default = ["egl", "winit", "udev", "xwayland"] egl = ["smithay/use_system_lib", "smithay/backend_egl"] udev = [ "smithay-drm-extras", @@ -40,3 +41,4 @@ udev = [ "xcursor", ] winit = ["smithay/backend_winit", "smithay/backend_drm"] +xwayland = ["smithay/backend_x11", "x11rb", "smithay/x11rb_event_source", "xcursor"] diff --git a/src/backend/udev.rs b/src/backend/udev.rs index b44903a..fdfdc21 100644 --- a/src/backend/udev.rs +++ b/src/backend/udev.rs @@ -7,6 +7,7 @@ use std::{ collections::{HashMap, HashSet}, error::Error, + ffi::OsString, os::fd::FromRawFd, path::Path, sync::Mutex, @@ -52,7 +53,7 @@ use smithay::{ desktop::{ space::{self, SurfaceTree}, utils::{self, surface_primary_scanout_output, OutputPresentationFeedback}, - Space, Window, + Space, }, input::pointer::{CursorImageAttributes, CursorImageStatus}, output::{Output, PhysicalProperties, Subpixel}, @@ -100,6 +101,7 @@ use crate::{ api::msg::{Args, OutgoingMsg}, render::{pointer::PointerElement, CustomRenderElements, OutputRenderElements}, state::{take_presentation_feedback, CalloopData, State, SurfaceDmabufFeedback}, + window::WindowElement, }; use super::Backend; @@ -449,6 +451,16 @@ pub fn run_udev() -> Result<(), Box> { }); }); + if let Err(err) = state.xwayland.start( + state.loop_handle.clone(), + None, + std::iter::empty::<(OsString, OsString)>(), + true, + |_| {}, + ) { + tracing::error!("Failed to start XWayland: {err}"); + } + event_loop.run( Some(Duration::from_millis(6)), &mut CalloopData { state, display }, @@ -1457,7 +1469,7 @@ impl State { fn render_surface<'a>( surface: &'a mut SurfaceData, renderer: &mut UdevRenderer<'a, '_>, - space: &Space, + space: &Space, output: &Output, input_method: &InputMethodHandle, pointer_location: Point, diff --git a/src/backend/winit.rs b/src/backend/winit.rs index 1eb9c39..03fc4fc 100644 --- a/src/backend/winit.rs +++ b/src/backend/winit.rs @@ -1,6 +1,6 @@ // SPDX-License-Identifier: GPL-3.0-or-later -use std::{error::Error, sync::Mutex, time::Duration}; +use std::{error::Error, ffi::OsString, sync::Mutex, time::Duration}; use smithay::{ backend::{ @@ -192,6 +192,16 @@ pub fn run_winit() -> Result<(), Box> { state.space.map_output(&output, (0, 0)); + if let Err(err) = state.xwayland.start( + state.loop_handle.clone(), + None, + std::iter::empty::<(OsString, OsString)>(), + true, + |_| {}, + ) { + tracing::error!("Failed to start XWayland: {err}"); + } + let mut pointer_element = PointerElement::::new(); // TODO: pointer diff --git a/src/focus.rs b/src/focus.rs index 8867bd2..02c2c89 100644 --- a/src/focus.rs +++ b/src/focus.rs @@ -1,10 +1,12 @@ // SPDX-License-Identifier: GPL-3.0-or-later -use smithay::{desktop::Window, output::Output, utils::IsAlive}; +use smithay::{output::Output, utils::IsAlive}; + +use crate::window::WindowElement; #[derive(Default)] pub struct FocusState { - focus_stack: Vec, + focus_stack: Vec, pub focused_output: Option, } @@ -15,7 +17,7 @@ impl FocusState { // TODO: how does this work with unmapped windows? /// Get the currently focused window. If there is none, the previous focus is returned. - pub fn current_focus(&mut self) -> Option { + pub fn current_focus(&mut self) -> Option { while let Some(window) = self.focus_stack.last() { if window.alive() { return Some(window.clone()); @@ -26,7 +28,7 @@ impl FocusState { } /// Set the currently focused window. - pub fn set_focus(&mut self, window: Window) { + pub fn set_focus(&mut self, window: WindowElement) { self.focus_stack.retain(|win| win != &window); self.focus_stack.push(window); } diff --git a/src/grab/move_grab.rs b/src/grab/move_grab.rs index c81a155..beb4325 100644 --- a/src/grab/move_grab.rs +++ b/src/grab/move_grab.rs @@ -1,7 +1,7 @@ // SPDX-License-Identifier: GPL-3.0-or-later use smithay::{ - desktop::Window, + desktop::space::SpaceElement, // NOTE: maybe alias this to PointerGrabStartData because there's another GrabStartData in // | input::keyboard input::{ @@ -18,12 +18,12 @@ use smithay::{ use crate::{ backend::Backend, state::{State, WithState}, - window::window_state::WindowResizeState, + window::{window_state::WindowResizeState, WindowElement}, }; pub struct MoveSurfaceGrab { pub start_data: GrabStartData, - pub window: Window, + pub window: WindowElement, pub initial_window_loc: Point, } @@ -43,6 +43,13 @@ impl PointerGrab> for MoveSurfaceGrab> { } data.space.raise_element(&self.window, false); + if let WindowElement::X11(surface) = &self.window { + data.xwm + .as_mut() + .expect("no xwm") + .raise_window(surface) + .expect("failed to raise x11 win"); + } // tracing::info!("window geo is: {:?}", self.window.geometry()); // tracing::info!("loc is: {:?}", data.space.element_location(&self.window)); diff --git a/src/grab/resize_grab.rs b/src/grab/resize_grab.rs index bed0b38..aaf8e1f 100644 --- a/src/grab/resize_grab.rs +++ b/src/grab/resize_grab.rs @@ -1,27 +1,47 @@ // SPDX-License-Identifier: GPL-3.0-or-later use smithay::{ - desktop::Window, + desktop::space::SpaceElement, input::{ pointer::{AxisFrame, ButtonEvent, GrabStartData, PointerGrab, PointerInnerHandle}, SeatHandler, }, reexports::{ - wayland_protocols::xdg::shell::server::xdg_toplevel::{self, ResizeEdge}, + wayland_protocols::xdg::shell::server::xdg_toplevel::{self}, wayland_server::protocol::wl_surface::WlSurface, }, utils::{IsAlive, Logical, Point, Rectangle, Size}, - wayland::{compositor, seat::WaylandFocus, shell::xdg::SurfaceCachedState}, + wayland::{compositor, shell::xdg::SurfaceCachedState}, + xwayland, }; use crate::{ backend::Backend, state::{State, WithState}, + window::WindowElement, }; +#[derive(Debug, Clone, Copy, PartialEq, Eq)] +pub struct ResizeEdge(pub xdg_toplevel::ResizeEdge); + +impl From for ResizeEdge { + fn from(value: xwayland::xwm::ResizeEdge) -> Self { + match value { + xwayland::xwm::ResizeEdge::Bottom => Self(xdg_toplevel::ResizeEdge::Bottom), + xwayland::xwm::ResizeEdge::BottomLeft => Self(xdg_toplevel::ResizeEdge::BottomLeft), + xwayland::xwm::ResizeEdge::BottomRight => Self(xdg_toplevel::ResizeEdge::BottomRight), + xwayland::xwm::ResizeEdge::Left => Self(xdg_toplevel::ResizeEdge::Left), + xwayland::xwm::ResizeEdge::Right => Self(xdg_toplevel::ResizeEdge::Right), + xwayland::xwm::ResizeEdge::Top => Self(xdg_toplevel::ResizeEdge::Top), + xwayland::xwm::ResizeEdge::TopLeft => Self(xdg_toplevel::ResizeEdge::TopLeft), + xwayland::xwm::ResizeEdge::TopRight => Self(xdg_toplevel::ResizeEdge::TopRight), + } + } +} + pub struct ResizeSurfaceGrab { start_data: GrabStartData, - window: Window, + window: WindowElement, edges: ResizeEdge, @@ -34,26 +54,26 @@ pub struct ResizeSurfaceGrab { impl ResizeSurfaceGrab { pub fn start( start_data: GrabStartData, - window: Window, + window: WindowElement, edges: ResizeEdge, initial_window_rect: Rectangle, button_used: u32, - ) -> Self { - window.toplevel().wl_surface().with_state(|state| { + ) -> Option { + window.wl_surface()?.with_state(|state| { state.resize_state = ResizeSurfaceState::Resizing { edges, initial_window_rect, }; }); - Self { + Some(Self { start_data, window, edges, initial_window_rect, last_window_size: initial_window_rect.size, button_used, - } + }) } } @@ -77,16 +97,28 @@ impl PointerGrab> for ResizeSurfaceGrab> { let mut new_window_width = self.initial_window_rect.size.w; let mut new_window_height = self.initial_window_rect.size.h; - if let ResizeEdge::Left | ResizeEdge::TopLeft | ResizeEdge::BottomLeft = self.edges { + if let xdg_toplevel::ResizeEdge::Left + | xdg_toplevel::ResizeEdge::TopLeft + | xdg_toplevel::ResizeEdge::BottomLeft = self.edges.0 + { new_window_width = self.initial_window_rect.size.w - delta.x; } - if let ResizeEdge::Right | ResizeEdge::TopRight | ResizeEdge::BottomRight = self.edges { + if let xdg_toplevel::ResizeEdge::Right + | xdg_toplevel::ResizeEdge::TopRight + | xdg_toplevel::ResizeEdge::BottomRight = self.edges.0 + { new_window_width = self.initial_window_rect.size.w + delta.x; } - if let ResizeEdge::Top | ResizeEdge::TopRight | ResizeEdge::TopLeft = self.edges { + if let xdg_toplevel::ResizeEdge::Top + | xdg_toplevel::ResizeEdge::TopRight + | xdg_toplevel::ResizeEdge::TopLeft = self.edges.0 + { new_window_height = self.initial_window_rect.size.h - delta.y; } - if let ResizeEdge::Bottom | ResizeEdge::BottomRight | ResizeEdge::BottomLeft = self.edges { + if let xdg_toplevel::ResizeEdge::Bottom + | xdg_toplevel::ResizeEdge::BottomRight + | xdg_toplevel::ResizeEdge::BottomLeft = self.edges.0 + { new_window_height = self.initial_window_rect.size.h + delta.y; } @@ -129,14 +161,27 @@ impl PointerGrab> for ResizeSurfaceGrab> { new_window_height.clamp(min_height, max_height), )); - let toplevel_surface = self.window.toplevel(); + match &self.window { + WindowElement::Wayland(window) => { + let toplevel_surface = window.toplevel(); - toplevel_surface.with_pending_state(|state| { - state.states.set(xdg_toplevel::State::Resizing); - state.size = Some(self.last_window_size); - }); + toplevel_surface.with_pending_state(|state| { + state.states.set(xdg_toplevel::State::Resizing); + state.size = Some(self.last_window_size); + }); - toplevel_surface.send_pending_configure(); + toplevel_surface.send_pending_configure(); + } + WindowElement::X11(surface) => { + let loc = data + .space + .element_location(&self.window) + .expect("failed to get x11 win loc"); + surface + .configure(Rectangle::from_loc_and_size(loc, self.last_window_size)) + .expect("failed to configure x11 win"); + } + } } fn relative_motion( @@ -160,20 +205,38 @@ impl PointerGrab> for ResizeSurfaceGrab> { if !handle.current_pressed().contains(&self.button_used) { handle.unset_grab(data, event.serial, event.time); - let toplevel_surface = self.window.toplevel(); - toplevel_surface.with_pending_state(|state| { - state.states.unset(xdg_toplevel::State::Resizing); - state.size = Some(self.last_window_size); - }); + if !self.window.alive() { + return; + } - toplevel_surface.send_pending_configure(); + match &self.window { + WindowElement::Wayland(window) => { + let toplevel_surface = window.toplevel(); + toplevel_surface.with_pending_state(|state| { + state.states.unset(xdg_toplevel::State::Resizing); + state.size = Some(self.last_window_size); + }); - toplevel_surface.wl_surface().with_state(|state| { - state.resize_state = ResizeSurfaceState::WaitingForLastCommit { - edges: self.edges, - initial_window_rect: self.initial_window_rect, - }; - }); + toplevel_surface.send_pending_configure(); + + toplevel_surface.wl_surface().with_state(|state| { + // TODO: validate resize state + state.resize_state = ResizeSurfaceState::WaitingForLastCommit { + edges: self.edges, + initial_window_rect: self.initial_window_rect, + }; + }); + } + WindowElement::X11(surface) => { + let Some(surface) = surface.wl_surface() else { return }; + surface.with_state(|state| { + state.resize_state = ResizeSurfaceState::WaitingForLastCommit { + edges: self.edges, + initial_window_rect: self.initial_window_rect, + }; + }); + } + } } } @@ -236,12 +299,18 @@ pub fn handle_commit(state: &mut State, surface: &WlSurface) -> O .map(|(edges, initial_window_rect)| { let mut new_x: Option = None; let mut new_y: Option = None; - if let ResizeEdge::Left | ResizeEdge::TopLeft | ResizeEdge::BottomLeft = edges { + if let xdg_toplevel::ResizeEdge::Left + | xdg_toplevel::ResizeEdge::TopLeft + | xdg_toplevel::ResizeEdge::BottomLeft = edges.0 + { new_x = Some( initial_window_rect.loc.x + (initial_window_rect.size.w - geometry.size.w), ); } - if let ResizeEdge::Top | ResizeEdge::TopLeft | ResizeEdge::TopRight = edges { + if let xdg_toplevel::ResizeEdge::Top + | xdg_toplevel::ResizeEdge::TopLeft + | xdg_toplevel::ResizeEdge::TopRight = edges.0 + { new_y = Some( initial_window_rect.loc.y + (initial_window_rect.size.h - geometry.size.h), ); diff --git a/src/handlers.rs b/src/handlers.rs index de30c18..9c90259 100644 --- a/src/handlers.rs +++ b/src/handlers.rs @@ -43,12 +43,13 @@ use smithay::{ }, shm::{ShmHandler, ShmState}, }, + xwayland::{X11Wm, XWaylandClientData}, }; use crate::{ backend::Backend, - state::{ClientState, State, WithState}, - window::{window_state::WindowResizeState, WindowBlocker, BLOCKER_COUNTER}, + state::{CalloopData, ClientState, State, WithState}, + window::{window_state::WindowResizeState, WindowBlocker, WindowElement, BLOCKER_COUNTER}, }; impl BufferHandler for State { @@ -96,6 +97,8 @@ impl CompositorHandler for State { fn commit(&mut self, surface: &WlSurface) { // tracing::debug!("commit"); + X11Wm::commit_hook::>(surface); + utils::on_commit_buffer_handler::(surface); if !compositor::is_sync_subsurface(surface) { @@ -103,7 +106,7 @@ impl CompositorHandler for State { while let Some(parent) = compositor::get_parent(&root) { root = parent; } - if let Some(window) = self.window_for_surface(surface) { + if let Some(WindowElement::Wayland(window)) = self.window_for_surface(surface) { window.on_commit(); } }; @@ -131,30 +134,34 @@ impl CompositorHandler for State { } fn client_compositor_state<'a>(&self, client: &'a Client) -> &'a CompositorClientState { - &client - .get_data::() - .expect("ClientState wasn't in client's data map") - .compositor_state + if let Some(state) = client.get_data::() { + return &state.compositor_state; + } + if let Some(state) = client.get_data::() { + return &state.compositor_state; + } + panic!("Unknown client data type"); } } delegate_compositor!(@ State); fn ensure_initial_configure(surface: &WlSurface, state: &mut State) { if let Some(window) = state.window_for_surface(surface) { - let initial_configure_sent = compositor::with_states(surface, |states| { - states - .data_map - .get::() - .expect("XdgToplevelSurfaceData wasn't in surface's data map") - .lock() - .expect("Failed to lock Mutex") - .initial_configure_sent - }); - // println!("initial_configure_sent is {}", initial_configure_sent); + if let WindowElement::Wayland(window) = &window { + let initial_configure_sent = compositor::with_states(surface, |states| { + states + .data_map + .get::() + .expect("XdgToplevelSurfaceData wasn't in surface's data map") + .lock() + .expect("Failed to lock Mutex") + .initial_configure_sent + }); - if !initial_configure_sent { - tracing::debug!("Initial configure"); - window.toplevel().send_configure(); + if !initial_configure_sent { + tracing::debug!("Initial configure"); + window.toplevel().send_configure(); + } } return; } @@ -227,14 +234,18 @@ impl XdgShellHandler for State { } fn new_toplevel(&mut self, surface: ToplevelSurface) { - let window = Window::new(surface); + let window = WindowElement::Wayland(Window::new(surface)); + + { + let WindowElement::Wayland(window) = &window else { unreachable!() }; + window.toplevel().with_pending_state(|tl_state| { + tl_state.states.set(xdg_toplevel::State::TiledTop); + tl_state.states.set(xdg_toplevel::State::TiledBottom); + tl_state.states.set(xdg_toplevel::State::TiledLeft); + tl_state.states.set(xdg_toplevel::State::TiledRight); + }); + } - window.toplevel().with_pending_state(|tl_state| { - tl_state.states.set(xdg_toplevel::State::TiledTop); - tl_state.states.set(xdg_toplevel::State::TiledBottom); - tl_state.states.set(xdg_toplevel::State::TiledLeft); - tl_state.states.set(xdg_toplevel::State::TiledRight); - }); window.with_state(|state| { state.tags = match ( &self.focus_state.focused_output, @@ -296,7 +307,9 @@ impl XdgShellHandler for State { BLOCKER_COUNTER.load(std::sync::atomic::Ordering::SeqCst) ); for win in windows_on_output.iter() { - compositor::add_blocker(win.toplevel().wl_surface(), WindowBlocker); + if let Some(surf) = win.wl_surface() { + compositor::add_blocker(&surf, WindowBlocker); + } } let clone = window.clone(); self.loop_handle.insert_idle(|data| { @@ -308,7 +321,7 @@ impl XdgShellHandler for State { ); for client in windows_on_output .iter() - .filter_map(|win| win.toplevel().wl_surface().client()) + .filter_map(|win| win.wl_surface()?.client()) { data.state .client_compositor_state(&client) @@ -324,7 +337,7 @@ impl XdgShellHandler for State { .expect("Seat had no keyboard") // FIXME: actually handle error .set_focus( &mut data.state, - Some(window.toplevel().wl_surface().clone()), + window.wl_surface(), SERIAL_COUNTER.next_serial(), ); }); @@ -332,7 +345,11 @@ impl XdgShellHandler for State { fn toplevel_destroyed(&mut self, surface: ToplevelSurface) { tracing::debug!("toplevel destroyed"); - self.windows.retain(|window| window.toplevel() != &surface); + self.windows.retain(|window| { + window + .wl_surface() + .is_some_and(|surf| &surf != surface.wl_surface()) + }); if let Some(focused_output) = self.focus_state.focused_output.as_ref() { focused_output.with_state(|state| { let first_tag = state.focused_tags().next(); @@ -353,7 +370,7 @@ impl XdgShellHandler for State { let focus = self .focus_state .current_focus() - .map(|win| win.toplevel().wl_surface().clone()); + .and_then(|win| win.wl_surface()); self.seat .get_keyboard() .expect("Seat had no keyboard") @@ -385,7 +402,7 @@ impl XdgShellHandler for State { const BUTTON_LEFT: u32 = 0x110; crate::xdg::request::resize_request( self, - &surface, + surface.wl_surface(), &Seat::from_resource(&seat).expect("Couldn't get seat from WlSeat"), serial, edges, @@ -413,12 +430,11 @@ impl XdgShellHandler for State { .ok() .and_then(|root| self.window_for_surface(&root)) { - if let Ok(mut grab) = self.popup_manager.grab_popup( - root.toplevel().wl_surface().clone(), - popup_kind, - &seat, - serial, - ) { + let Some(wl_surface) = root.wl_surface() else { return }; + if let Ok(mut grab) = self + .popup_manager + .grab_popup(wl_surface, popup_kind, &seat, serial) + { if let Some(keyboard) = seat.get_keyboard() { if keyboard.is_grabbed() && !(keyboard.has_grab(serial) diff --git a/src/handlers/xwayland.rs b/src/handlers/xwayland.rs index 0c83b63..3704ba7 100644 --- a/src/handlers/xwayland.rs +++ b/src/handlers/xwayland.rs @@ -4,66 +4,181 @@ // // SPDX-License-Identifier: MPL-2.0 -use smithay::xwayland::XwmHandler; +use smithay::{ + desktop::space::SpaceElement, + input::pointer::Focus, + reexports::wayland_server::Resource, + utils::{Rectangle, SERIAL_COUNTER}, + wayland::compositor::{self, CompositorHandler}, + xwayland::{xwm::XwmId, X11Wm, XwmHandler}, +}; -use crate::{backend::Backend, state::CalloopData}; +use crate::{ + backend::Backend, + grab::resize_grab::{ResizeSurfaceGrab, ResizeSurfaceState}, + state::{CalloopData, WithState}, + window::{WindowBlocker, WindowElement, BLOCKER_COUNTER}, +}; impl XwmHandler for CalloopData { - fn xwm_state(&mut self, xwm: smithay::xwayland::xwm::XwmId) -> &mut smithay::xwayland::X11Wm { - todo!() + fn xwm_state(&mut self, xwm: XwmId) -> &mut X11Wm { + self.state.xwm.as_mut().expect("xwm not in state") } - fn new_window( - &mut self, - xwm: smithay::xwayland::xwm::XwmId, - window: smithay::xwayland::X11Surface, - ) { - todo!() - } + fn new_window(&mut self, xwm: XwmId, window: smithay::xwayland::X11Surface) {} - fn new_override_redirect_window( - &mut self, - xwm: smithay::xwayland::xwm::XwmId, - window: smithay::xwayland::X11Surface, - ) { - todo!() - } + fn new_override_redirect_window(&mut self, xwm: XwmId, window: smithay::xwayland::X11Surface) {} - fn map_window_request( - &mut self, - xwm: smithay::xwayland::xwm::XwmId, - window: smithay::xwayland::X11Surface, - ) { - todo!() + fn map_window_request(&mut self, xwm: XwmId, window: smithay::xwayland::X11Surface) { + tracing::debug!("new x11 window from map_window_request"); + window.set_mapped(true).expect("failed to map x11 window"); + let window = WindowElement::X11(window); + // TODO: place the window in the space + self.state.space.map_element(window.clone(), (0, 0), true); + let bbox = self + .state + .space + .element_bbox(&window) + .expect("failed to get x11 bbox"); + let WindowElement::X11(surface) = &window else { unreachable!() }; + surface + .configure(Some(bbox)) + .expect("failed to configure x11 window"); + // TODO: ssd + + // TODO: this is a duplicate of the code in new_toplevel, + // | move into its own function + { + window.with_state(|state| { + state.tags = match ( + &self.state.focus_state.focused_output, + self.state.space.outputs().next(), + ) { + (Some(output), _) | (None, Some(output)) => output.with_state(|state| { + let output_tags = state.focused_tags().cloned().collect::>(); + if !output_tags.is_empty() { + output_tags + } else if let Some(first_tag) = state.tags.first() { + vec![first_tag.clone()] + } else { + vec![] + } + }), + (None, None) => vec![], + }; + + tracing::debug!("new window, tags are {:?}", state.tags); + }); + + let windows_on_output = self + .state + .windows + .iter() + .filter(|win| { + win.with_state(|state| { + self.state + .focus_state + .focused_output + .as_ref() + .unwrap() + .with_state(|op_state| { + op_state + .tags + .iter() + .any(|tag| state.tags.iter().any(|tg| tg == tag)) + }) + }) + }) + .cloned() + .collect::>(); + + self.state.windows.push(window.clone()); + // self.space.map_element(window.clone(), (0, 0), true); + if let Some(focused_output) = self.state.focus_state.focused_output.clone() { + focused_output.with_state(|state| { + let first_tag = state.focused_tags().next(); + if let Some(first_tag) = first_tag { + first_tag.layout().layout( + self.state.windows.clone(), + state.focused_tags().cloned().collect(), + &self.state.space, + &focused_output, + ); + } + }); + BLOCKER_COUNTER.store(1, std::sync::atomic::Ordering::SeqCst); + tracing::debug!( + "blocker {}", + BLOCKER_COUNTER.load(std::sync::atomic::Ordering::SeqCst) + ); + for win in windows_on_output.iter() { + if let Some(surf) = win.wl_surface() { + compositor::add_blocker(&surf, WindowBlocker); + } + } + let clone = window.clone(); + self.state.loop_handle.insert_idle(|data| { + crate::state::schedule_on_commit(data, vec![clone], move |data| { + BLOCKER_COUNTER.store(0, std::sync::atomic::Ordering::SeqCst); + tracing::debug!( + "blocker {}", + BLOCKER_COUNTER.load(std::sync::atomic::Ordering::SeqCst) + ); + for client in windows_on_output + .iter() + .filter_map(|win| win.wl_surface()?.client()) + { + data.state + .client_compositor_state(&client) + .blocker_cleared(&mut data.state, &data.display.handle()) + } + }) + }); + } + self.state.loop_handle.insert_idle(move |data| { + data.state + .seat + .get_keyboard() + .expect("Seat had no keyboard") // FIXME: actually handle error + .set_focus( + &mut data.state, + window.wl_surface(), + SERIAL_COUNTER.next_serial(), + ); + }); + } } fn mapped_override_redirect_window( &mut self, - xwm: smithay::xwayland::xwm::XwmId, + xwm: XwmId, window: smithay::xwayland::X11Surface, ) { - todo!() + let loc = window.geometry().loc; + let window = WindowElement::X11(window); + self.state.space.map_element(window, loc, true); } - fn unmapped_window( - &mut self, - xwm: smithay::xwayland::xwm::XwmId, - window: smithay::xwayland::X11Surface, - ) { - todo!() + fn unmapped_window(&mut self, xwm: XwmId, window: smithay::xwayland::X11Surface) { + let win = self + .state + .space + .elements() + .find(|elem| matches!(elem, WindowElement::X11(surface) if surface == &window)) + .cloned(); + if let Some(win) = win { + self.state.space.unmap_elem(&win); + } + if !window.is_override_redirect() { + window.set_mapped(false).expect("failed to unmap x11 win"); + } } - fn destroyed_window( - &mut self, - xwm: smithay::xwayland::xwm::XwmId, - window: smithay::xwayland::X11Surface, - ) { - todo!() - } + fn destroyed_window(&mut self, xwm: XwmId, window: smithay::xwayland::X11Surface) {} fn configure_request( &mut self, - xwm: smithay::xwayland::xwm::XwmId, + xwm: XwmId, window: smithay::xwayland::X11Surface, x: Option, y: Option, @@ -71,35 +186,112 @@ impl XwmHandler for CalloopData { h: Option, reorder: Option, ) { - todo!() + let mut geo = window.geometry(); + if let Some(w) = w { + geo.size.w = w as i32; + } + if let Some(h) = h { + geo.size.h = h as i32; + } + if let Err(err) = window.configure(geo) { + tracing::error!("Failed to configure x11 win: {err}"); + } } fn configure_notify( &mut self, - xwm: smithay::xwayland::xwm::XwmId, + xwm: XwmId, window: smithay::xwayland::X11Surface, geometry: smithay::utils::Rectangle, above: Option, ) { - todo!() + let Some(win) = self + .state + .space + .elements() + .find(|elem| matches!(elem, WindowElement::X11(surface) if surface == &window)) + .cloned() + else { + return; + }; + self.state.space.map_element(win, geometry.loc, false); + // TODO: anvil has a TODO here } + // TODO: maximize request + + // TODO: unmaximize request + + // TODO: fullscreen request + + // TODO: unfullscreen request + fn resize_request( &mut self, - xwm: smithay::xwayland::xwm::XwmId, + xwm: XwmId, window: smithay::xwayland::X11Surface, button: u32, resize_edge: smithay::xwayland::xwm::ResizeEdge, ) { + let seat = &self.state.seat; + let pointer = seat.get_pointer().expect("failed to get pointer"); + let start_data = pointer.grab_start_data().expect("no grab start data"); + + let Some(win) = self + .state + .space + .elements() + .find(|elem| matches!(elem, WindowElement::X11(surface) if surface == &window)) + else { + return; + }; + + let initial_window_location = self + .state + .space + .element_location(win) + .expect("failed to get x11 loc"); + let initial_window_size = win.geometry().size; + + if let Some(wl_surface) = win.wl_surface() { + wl_surface.with_state(|state| { + state.resize_state = ResizeSurfaceState::Resizing { + edges: resize_edge.into(), + initial_window_rect: Rectangle::from_loc_and_size( + initial_window_location, + initial_window_size, + ), + }; + }); + + let grab = ResizeSurfaceGrab::start( + start_data, + win.clone(), + resize_edge.into(), + Rectangle::from_loc_and_size(initial_window_location, initial_window_size), + 0x110, // BUTTON_LEFT + ); + + if let Some(grab) = grab { + pointer.set_grab( + &mut self.state, + grab, + SERIAL_COUNTER.next_serial(), + Focus::Clear, + ); + } + } + } + + fn move_request(&mut self, xwm: XwmId, window: smithay::xwayland::X11Surface, button: u32) { todo!() } - fn move_request( - &mut self, - xwm: smithay::xwayland::xwm::XwmId, - window: smithay::xwayland::X11Surface, - button: u32, - ) { - todo!() - } + // TODO: allow_selection_access + + // TODO: send_selection + + // TODO: new_selection + + // TODO: cleared_selection } diff --git a/src/input.rs b/src/input.rs index d49354f..82ae09c 100644 --- a/src/input.rs +++ b/src/input.rs @@ -2,20 +2,22 @@ use std::collections::HashMap; -use crate::api::msg::{CallbackId, Modifier, ModifierMask, OutgoingMsg}; +use crate::{ + api::msg::{CallbackId, Modifier, ModifierMask, OutgoingMsg}, + window::WindowElement, +}; use smithay::{ backend::input::{ AbsolutePositionEvent, Axis, AxisSource, ButtonState, Event, InputBackend, InputEvent, KeyState, KeyboardKeyEvent, PointerAxisEvent, PointerButtonEvent, PointerMotionEvent, }, - desktop::{Window, WindowSurfaceType}, + desktop::{space::SpaceElement, WindowSurfaceType}, input::{ keyboard::{keysyms, FilterResult}, pointer::{AxisFrame, ButtonEvent, MotionEvent}, }, reexports::wayland_protocols::xdg::shell::server::xdg_toplevel::ResizeEdge, utils::{Logical, Point, SERIAL_COUNTER}, - wayland::seat::WaylandFocus, }; use crate::{ @@ -38,7 +40,7 @@ impl InputState { } impl State { - pub fn surface_under

(&self, point: P) -> Option<(Window, Point)> + pub fn surface_under

(&self, point: P) -> Option<(WindowElement, Point)> where P: Into>, { @@ -74,12 +76,14 @@ impl State { const BUTTON_RIGHT: u32 = 0x111; if self.move_mode { if event.button_code() == BUTTON_LEFT { - crate::xdg::request::move_request_force( - self, - window.toplevel(), - &self.seat.clone(), - serial, - ); + if let Some(wl_surf) = window.wl_surface() { + crate::xdg::request::move_request_force( + self, + &wl_surf, + &self.seat.clone(), + serial, + ); + } return; // TODO: kinda ugly return here } else if event.button_code() == BUTTON_RIGHT { let window_geometry = window.geometry(); @@ -120,29 +124,48 @@ impl State { _ => ResizeEdge::None, }; - crate::xdg::request::resize_request_force( - self, - window.toplevel(), - &self.seat.clone(), - serial, - edges, - BUTTON_RIGHT, - ); + if let Some(wl_surf) = window.wl_surface() { + crate::xdg::request::resize_request_force( + self, + &wl_surf, + &self.seat.clone(), + serial, + edges, + BUTTON_RIGHT, + ); + } } } else { // Move window to top of stack. self.space.raise_element(&window, true); + if let WindowElement::X11(surface) = &window { + self.xwm + .as_mut() + .expect("no xwm") + .raise_window(surface) + .expect("failed to raise x11 win"); + } - keyboard.set_focus(self, Some(window.toplevel().wl_surface().clone()), serial); + keyboard.set_focus(self, window.wl_surface(), serial); self.space.elements().for_each(|window| { - window.toplevel().send_configure(); + if let WindowElement::Wayland(window) = window { + window.toplevel().send_configure(); + } }); } } else { - self.space.elements().for_each(|window| { - window.set_activated(false); - window.toplevel().send_configure(); + self.space.elements().for_each(|window| match window { + WindowElement::Wayland(window) => { + window.set_activated(false); + window.toplevel().send_configure(); + } + WindowElement::X11(surface) => { + surface + .set_activated(false) + .expect("failed to deactivate x11 win"); + // INFO: do i need to configure this? + } }); keyboard.set_focus(self, None, serial); } diff --git a/src/layout.rs b/src/layout.rs index ec6756c..3221053 100644 --- a/src/layout.rs +++ b/src/layout.rs @@ -2,7 +2,7 @@ use itertools::{Either, Itertools}; use smithay::{ - desktop::{Space, Window}, + desktop::Space, output::Output, utils::{Logical, Size}, }; @@ -11,7 +11,7 @@ use crate::{ backend::Backend, state::{State, WithState}, tag::Tag, - window::window_state::WindowResizeState, + window::WindowElement, }; // TODO: couple this with the layouts @@ -29,9 +29,9 @@ pub enum Layout { impl Layout { pub fn layout( &self, - windows: Vec, + windows: Vec, tags: Vec, - space: &Space, + space: &Space, output: &Output, ) { let windows = filter_windows(&windows, tags); @@ -44,525 +44,352 @@ impl Layout { let output_loc = output.current_location(); match self { - Layout::MasterStack => { - let master = windows.first(); - let stack = windows.iter().skip(1); - - let Some(master) = master else { return }; - - let stack_count = stack.clone().count(); - - if stack_count == 0 { - // one window - master.toplevel().with_pending_state(|state| { - state.size = Some(output_geo.size); - }); - - master.with_state(|state| { - state.resize_state = WindowResizeState::Requested( - master.toplevel().send_configure(), - (output_loc.x, output_loc.y).into(), - ); - }); - } else { - let new_master_size: Size = - (output_geo.size.w / 2, output_geo.size.h).into(); - master.toplevel().with_pending_state(|state| { - state.size = Some(new_master_size); - }); - master.with_state(|state| { - state.resize_state = WindowResizeState::Requested( - master.toplevel().send_configure(), - (output_loc.x, output_loc.y).into(), - ); - }); - - let stack_count = stack_count; - - let height = output_geo.size.h as f32 / stack_count as f32; - let mut y_s = vec![]; - for i in 0..stack_count { - y_s.push((i as f32 * height).round() as i32); - } - let heights = y_s - .windows(2) - .map(|pair| pair[1] - pair[0]) - .chain(vec![output_geo.size.h - y_s.last().expect("vec was empty")]) - .collect::>(); - - for (i, win) in stack.enumerate() { - win.toplevel().with_pending_state(|state| { - // INFO: Some windows crash the compositor if they become too short in height, - // | so they're limited to a minimum of 40 pixels as a workaround. - state.size = - Some((output_geo.size.w / 2, i32::max(heights[i], 40)).into()); - }); - - win.with_state(|state| { - state.resize_state = WindowResizeState::Requested( - win.toplevel().send_configure(), - (output_geo.size.w / 2 + output_loc.x, y_s[i] + output_loc.y) - .into(), - ); - }); - } - } - } - Layout::Dwindle => { - let mut iter = windows.windows(2).peekable(); - - if iter.peek().is_none() { - if let Some(window) = windows.first() { - window.toplevel().with_pending_state(|state| { - state.size = Some(output_geo.size); - }); - - window.with_state(|state| { - state.resize_state = WindowResizeState::Requested( - window.toplevel().send_configure(), - (output_loc.x, output_loc.y).into(), - ); - }); - } - } else { - for (i, wins) in iter.enumerate() { - let win1 = &wins[0]; - let win2 = &wins[1]; - - enum Slice { - Right, - Below, - } - - let slice = if i % 2 == 0 { - Slice::Right - } else { - Slice::Below - }; - - if i == 0 { - win1.toplevel() - .with_pending_state(|state| state.size = Some(output_geo.size)); - win1.with_state(|state| { - state.resize_state = WindowResizeState::Requested( - win1.toplevel().send_configure(), - output_loc, - ) - }); - } - - let win1_size = win1.toplevel().with_pending_state(|state| { - state.size.expect("size should have been set") - }); - let win1_loc = win1.with_state(|state| { - let WindowResizeState::Requested(_, loc) = - state.resize_state else { unreachable!() }; - loc - }); - - match slice { - Slice::Right => { - let width_partition = win1_size.w / 2; - win1.toplevel().with_pending_state(|state| { - state.size = Some( - (win1_size.w - width_partition, i32::max(win1_size.h, 40)) - .into(), - ); - }); - win1.with_state(|state| { - state.resize_state = WindowResizeState::Requested( - win1.toplevel().send_configure(), - win1_loc, - ); - }); - win2.toplevel().with_pending_state(|state| { - state.size = - Some((width_partition, i32::max(win1_size.h, 40)).into()); - }); - win2.with_state(|state| { - state.resize_state = WindowResizeState::Requested( - win2.toplevel().send_configure(), - (win1_loc.x + (win1_size.w - width_partition), win1_loc.y) - .into(), - ); - }); - } - Slice::Below => { - let height_partition = win1_size.h / 2; - win1.toplevel().with_pending_state(|state| { - state.size = Some( - (win1_size.w, i32::max(win1_size.h - height_partition, 40)) - .into(), - ); - }); - win1.with_state(|state| { - state.resize_state = WindowResizeState::Requested( - win1.toplevel().send_configure(), - win1_loc, - ); - }); - win2.toplevel().with_pending_state(|state| { - state.size = - Some((win1_size.w, i32::max(height_partition, 40)).into()); - }); - win2.with_state(|state| { - state.resize_state = WindowResizeState::Requested( - win2.toplevel().send_configure(), - (win1_loc.x, win1_loc.y + (win1_size.h - height_partition)) - .into(), - ); - }); - } - } - } - } - } - Layout::Spiral => { - let mut iter = windows.windows(2).peekable(); - - if iter.peek().is_none() { - if let Some(window) = windows.first() { - window.toplevel().with_pending_state(|state| { - state.size = Some(output_geo.size); - }); - - window.with_state(|state| { - state.resize_state = WindowResizeState::Requested( - window.toplevel().send_configure(), - (output_loc.x, output_loc.y).into(), - ); - }); - } - } else { - for (i, wins) in iter.enumerate() { - let win1 = &wins[0]; - let win2 = &wins[1]; - - enum Slice { - Above, - Below, - Left, - Right, - } - - let slice = match i % 4 { - 0 => Slice::Right, - 1 => Slice::Below, - 2 => Slice::Left, - 3 => Slice::Above, - _ => unreachable!(), - }; - - if i == 0 { - win1.toplevel() - .with_pending_state(|state| state.size = Some(output_geo.size)); - win1.with_state(|state| { - state.resize_state = WindowResizeState::Requested( - win1.toplevel().send_configure(), - output_loc, - ) - }); - } - - let win1_size = win1.toplevel().with_pending_state(|state| { - state.size.expect("size should have been set") - }); - let win1_loc = win1.with_state(|state| { - let WindowResizeState::Requested(_, loc) = - state.resize_state else { unreachable!() }; - loc - }); - - match slice { - Slice::Above => { - let height_partition = win1_size.h / 2; - win1.toplevel().with_pending_state(|state| { - state.size = Some( - (win1_size.w, i32::max(win1_size.h - height_partition, 40)) - .into(), - ); - }); - win1.with_state(|state| { - state.resize_state = WindowResizeState::Requested( - win1.toplevel().send_configure(), - (win1_loc.x, win1_loc.y + height_partition).into(), - ); - }); - win2.toplevel().with_pending_state(|state| { - state.size = - Some((win1_size.w, i32::max(height_partition, 40)).into()); - }); - win2.with_state(|state| { - state.resize_state = WindowResizeState::Requested( - win2.toplevel().send_configure(), - win1_loc, - ); - }); - } - Slice::Below => { - let height_partition = win1_size.h / 2; - win1.toplevel().with_pending_state(|state| { - state.size = Some( - (win1_size.w, win1_size.h - i32::max(height_partition, 40)) - .into(), - ); - }); - win1.with_state(|state| { - state.resize_state = WindowResizeState::Requested( - win1.toplevel().send_configure(), - win1_loc, - ); - }); - win2.toplevel().with_pending_state(|state| { - state.size = - Some((win1_size.w, i32::max(height_partition, 40)).into()); - }); - win2.with_state(|state| { - state.resize_state = WindowResizeState::Requested( - win2.toplevel().send_configure(), - (win1_loc.x, win1_loc.y + (win1_size.h - height_partition)) - .into(), - ); - }); - } - Slice::Left => { - let width_partition = win1_size.w / 2; - win1.toplevel().with_pending_state(|state| { - state.size = Some( - (win1_size.w - width_partition, i32::max(win1_size.h, 40)) - .into(), - ); - }); - win1.with_state(|state| { - state.resize_state = WindowResizeState::Requested( - win1.toplevel().send_configure(), - (win1_loc.x + width_partition, win1_loc.y).into(), - ); - }); - win2.toplevel().with_pending_state(|state| { - state.size = - Some((width_partition, i32::max(win1_size.h, 40)).into()); - }); - win2.with_state(|state| { - state.resize_state = WindowResizeState::Requested( - win2.toplevel().send_configure(), - win1_loc, - ); - }); - } - Slice::Right => { - let width_partition = win1_size.w / 2; - win1.toplevel().with_pending_state(|state| { - state.size = Some( - (win1_size.w - width_partition, i32::max(win1_size.h, 40)) - .into(), - ); - }); - win1.with_state(|state| { - state.resize_state = WindowResizeState::Requested( - win1.toplevel().send_configure(), - win1_loc, - ); - }); - win2.toplevel().with_pending_state(|state| { - state.size = - Some((width_partition, i32::max(win1_size.h, 40)).into()); - }); - win2.with_state(|state| { - state.resize_state = WindowResizeState::Requested( - win2.toplevel().send_configure(), - (win1_loc.x + (win1_size.w - width_partition), win1_loc.y) - .into(), - ); - }); - } - } - } - } - } + Layout::MasterStack => master_stack(windows, space, output), + Layout::Dwindle => dwindle(windows, space, output), + Layout::Spiral => spiral(windows, space, output), layout @ (Layout::CornerTopLeft | Layout::CornerTopRight | Layout::CornerBottomLeft - | Layout::CornerBottomRight) => match windows.len() { - 0 => (), - 1 => { - windows[0].toplevel().with_pending_state(|state| { - state.size = Some(output_geo.size); - }); - - windows[0].with_state(|state| { - state.resize_state = WindowResizeState::Requested( - windows[0].toplevel().send_configure(), - (output_loc.x, output_loc.y).into(), - ); - }); - } - 2 => { - windows[0].toplevel().with_pending_state(|state| { - state.size = Some((output_geo.size.w / 2, output_geo.size.h).into()); - }); - windows[0].with_state(|state| { - state.resize_state = WindowResizeState::Requested( - windows[0].toplevel().send_configure(), - (output_loc.x, output_loc.y).into(), - ); - }); - windows[1].toplevel().with_pending_state(|state| { - state.size = Some((output_geo.size.w / 2, output_geo.size.h).into()); - }); - windows[1].with_state(|state| { - state.resize_state = WindowResizeState::Requested( - windows[1].toplevel().send_configure(), - (output_loc.x + output_geo.size.w / 2, output_loc.y).into(), - ); - }); - } - _ => { - let mut windows = windows.into_iter(); - let Some(corner) = windows.next() else { unreachable!() }; - let (horiz_stack, vert_stack): (Vec, Vec) = - windows.enumerate().partition_map(|(i, win)| { - if i % 2 == 0 { - Either::Left(win) - } else { - Either::Right(win) - } - }); - - let div_factor = 2; - - corner.toplevel().with_pending_state(|state| { - state.size = Some( - ( - output_geo.size.w / div_factor, - output_geo.size.h / div_factor, - ) - .into(), - ); - }); - corner.with_state(|state| { - state.resize_state = WindowResizeState::Requested( - corner.toplevel().send_configure(), - match layout { - Layout::CornerTopLeft => (output_loc.x, output_loc.y), - Layout::CornerTopRight => ( - output_loc.x + output_geo.size.w - - output_geo.size.w / div_factor, - output_loc.y, - ), - Layout::CornerBottomLeft => ( - output_loc.x, - output_loc.y + output_geo.size.h - - output_geo.size.h / div_factor, - ), - Layout::CornerBottomRight => ( - output_loc.x + output_geo.size.w - - output_geo.size.w / div_factor, - output_loc.y + output_geo.size.h - - output_geo.size.h / div_factor, - ), - _ => unreachable!(), - } - .into(), - ); - }); - - let vert_stack_count = vert_stack.len(); - - let height = output_geo.size.h as f32 / vert_stack_count as f32; - let mut y_s = vec![]; - for i in 0..vert_stack_count { - y_s.push((i as f32 * height).round() as i32); - } - let heights = y_s - .windows(2) - .map(|pair| pair[1] - pair[0]) - .chain(vec![output_geo.size.h - y_s.last().expect("vec was empty")]) - .collect::>(); - - for (i, win) in vert_stack.iter().enumerate() { - win.toplevel().with_pending_state(|state| { - // INFO: Some windows crash the compositor if they become too short in height, - // | so they're limited to a minimum of 40 pixels as a workaround. - state.size = - Some((output_geo.size.w / 2, i32::max(heights[i], 40)).into()); - }); - - win.with_state(|state| { - state.resize_state = WindowResizeState::Requested( - win.toplevel().send_configure(), - ( - match layout { - Layout::CornerTopLeft | Layout::CornerBottomLeft => { - output_geo.size.w / 2 + output_loc.x - } - Layout::CornerTopRight | Layout::CornerBottomRight => { - output_loc.x - } - _ => unreachable!(), - }, - y_s[i] + output_loc.y, - ) - .into(), - ); - }); - } - - let horiz_stack_count = horiz_stack.len(); - - let width = output_geo.size.w as f32 / 2.0 / horiz_stack_count as f32; - let mut x_s = vec![]; - for i in 0..horiz_stack_count { - x_s.push((i as f32 * width).round() as i32); - } - let widths = x_s - .windows(2) - .map(|pair| pair[1] - pair[0]) - .chain(vec![ - output_geo.size.w / 2 - x_s.last().expect("vec was empty"), - ]) - .collect::>(); - - for (i, win) in horiz_stack.iter().enumerate() { - win.toplevel().with_pending_state(|state| { - // INFO: Some windows crash the compositor if they become too short in height, - // | so they're limited to a minimum of 40 pixels as a workaround. - state.size = - Some((i32::max(widths[i], 1), output_geo.size.h / 2).into()); - }); - - win.with_state(|state| { - state.resize_state = WindowResizeState::Requested( - win.toplevel().send_configure(), - match layout { - Layout::CornerTopLeft => ( - x_s[i] + output_loc.x, - output_loc.y + output_geo.size.h / 2, - ), - Layout::CornerTopRight => ( - x_s[i] + output_loc.x + output_geo.size.w / 2, - output_loc.y + output_geo.size.h / 2, - ), - Layout::CornerBottomLeft => { - (x_s[i] + output_loc.x, output_loc.y) - } - Layout::CornerBottomRight => ( - x_s[i] + output_loc.x + output_geo.size.w / 2, - output_loc.y, - ), - _ => unreachable!(), - } - .into(), - ); - }); - } - } - }, + | Layout::CornerBottomRight) => corner(layout, windows, space, output), } } } -fn filter_windows(windows: &[Window], tags: Vec) -> Vec { +fn master_stack(windows: Vec, space: &Space, output: &Output) { + let Some(output_geo) = space.output_geometry(output) else { + tracing::error!("could not get output geometry"); + return; + }; + + let output_loc = output.current_location(); + + let master = windows.first(); + let stack = windows.iter().skip(1); + + let Some(master) = master else { return }; + + let stack_count = stack.clone().count(); + + if stack_count == 0 { + // one window + master.request_size_change(output_loc, output_geo.size); + } else { + let loc = (output_loc.x, output_loc.y).into(); + let new_master_size: Size = (output_geo.size.w / 2, output_geo.size.h).into(); + master.request_size_change(loc, new_master_size); + + let stack_count = stack_count; + + let height = output_geo.size.h as f32 / stack_count as f32; + let mut y_s = vec![]; + for i in 0..stack_count { + y_s.push((i as f32 * height).round() as i32); + } + let heights = y_s + .windows(2) + .map(|pair| pair[1] - pair[0]) + .chain(vec![output_geo.size.h - y_s.last().expect("vec was empty")]) + .collect::>(); + + for (i, win) in stack.enumerate() { + win.request_size_change( + (output_geo.size.w / 2 + output_loc.x, y_s[i] + output_loc.y).into(), + (output_geo.size.w / 2, i32::max(heights[i], 40)).into(), + ); + } + } +} + +fn dwindle(windows: Vec, space: &Space, output: &Output) { + let Some(output_geo) = space.output_geometry(output) else { + tracing::error!("could not get output geometry"); + return; + }; + + let output_loc = output.current_location(); + + let mut iter = windows.windows(2).peekable(); + + if iter.peek().is_none() { + if let Some(window) = windows.first() { + window.request_size_change(output_loc, output_geo.size); + } + } else { + let mut win1_size = output_geo.size; + let mut win1_loc = output_loc; + for (i, wins) in iter.enumerate() { + let win1 = &wins[0]; + let win2 = &wins[1]; + + enum Slice { + Right, + Below, + } + + let slice = if i % 2 == 0 { + Slice::Right + } else { + Slice::Below + }; + + match slice { + Slice::Right => { + let width_partition = win1_size.w / 2; + + win1.request_size_change( + win1_loc, + (win1_size.w - width_partition, i32::max(win1_size.h, 40)).into(), + ); + + win1_loc = (win1_loc.x + (win1_size.w - width_partition), win1_loc.y).into(); + win1_size = (width_partition, i32::max(win1_size.h, 40)).into(); + + win2.request_size_change(win1_loc, win1_size); + } + Slice::Below => { + let height_partition = win1_size.h / 2; + + win1.request_size_change( + win1_loc, + (win1_size.w, i32::max(win1_size.h - height_partition, 40)).into(), + ); + + win1_loc = (win1_loc.x, win1_loc.y + (win1_size.h - height_partition)).into(); + win1_size = (win1_size.w, i32::max(height_partition, 40)).into(); + + win2.request_size_change(win1_loc, win1_size); + } + } + } + } +} + +fn spiral(windows: Vec, space: &Space, output: &Output) { + let Some(output_geo) = space.output_geometry(output) else { + tracing::error!("could not get output geometry"); + return; + }; + + let output_loc = output.current_location(); + + let mut iter = windows.windows(2).peekable(); + + if iter.peek().is_none() { + if let Some(window) = windows.first() { + window.request_size_change(output_loc, output_geo.size); + } + } else { + let mut win1_loc = output_loc; + let mut win1_size = output_geo.size; + + for (i, wins) in iter.enumerate() { + let win1 = &wins[0]; + let win2 = &wins[1]; + + enum Slice { + Above, + Below, + Left, + Right, + } + + let slice = match i % 4 { + 0 => Slice::Right, + 1 => Slice::Below, + 2 => Slice::Left, + 3 => Slice::Above, + _ => unreachable!(), + }; + + match slice { + Slice::Above => { + let height_partition = win1_size.h / 2; + + win1.request_size_change( + (win1_loc.x, win1_loc.y + height_partition).into(), + (win1_size.w, i32::max(win1_size.h - height_partition, 40)).into(), + ); + + win1_size = (win1_size.w, i32::max(height_partition, 40)).into(); + win2.request_size_change(win1_loc, win1_size); + } + Slice::Below => { + let height_partition = win1_size.h / 2; + + win1.request_size_change( + win1_loc, + (win1_size.w, win1_size.h - i32::max(height_partition, 40)).into(), + ); + + win1_loc = (win1_loc.x, win1_loc.y + (win1_size.h - height_partition)).into(); + win1_size = (win1_size.w, i32::max(height_partition, 40)).into(); + win2.request_size_change(win1_loc, win1_size); + } + Slice::Left => { + let width_partition = win1_size.w / 2; + + win1.request_size_change( + (win1_loc.x + width_partition, win1_loc.y).into(), + (win1_size.w - width_partition, i32::max(win1_size.h, 40)).into(), + ); + + win1_size = (width_partition, i32::max(win1_size.h, 40)).into(); + win2.request_size_change(win1_loc, win1_size); + } + Slice::Right => { + let width_partition = win1_size.w / 2; + + win1.request_size_change( + win1_loc, + (win1_size.w - width_partition, i32::max(win1_size.h, 40)).into(), + ); + + win1_loc = (win1_loc.x + (win1_size.w - width_partition), win1_loc.y).into(); + win1_size = (width_partition, i32::max(win1_size.h, 40)).into(); + win2.request_size_change(win1_loc, win1_size); + } + } + } + } +} + +fn corner( + layout: &Layout, + windows: Vec, + space: &Space, + output: &Output, +) { + let Some(output_geo) = space.output_geometry(output) else { + tracing::error!("could not get output geometry"); + return; + }; + + let output_loc = output.current_location(); + match windows.len() { + 0 => (), + 1 => { + windows[0].request_size_change(output_loc, output_geo.size); + } + 2 => { + windows[0].request_size_change( + output_loc, + (output_geo.size.w / 2, output_geo.size.h).into(), + ); + + windows[1].request_size_change( + (output_loc.x + output_geo.size.w / 2, output_loc.y).into(), + (output_geo.size.w / 2, output_geo.size.h).into(), + ); + } + _ => { + let mut windows = windows.into_iter(); + let Some(corner) = windows.next() else { unreachable!() }; + let (horiz_stack, vert_stack): (Vec, Vec) = + windows.enumerate().partition_map(|(i, win)| { + if i % 2 == 0 { + Either::Left(win) + } else { + Either::Right(win) + } + }); + + let div_factor = 2; + + corner.request_size_change( + match layout { + Layout::CornerTopLeft => (output_loc.x, output_loc.y), + Layout::CornerTopRight => ( + output_loc.x + output_geo.size.w - output_geo.size.w / div_factor, + output_loc.y, + ), + Layout::CornerBottomLeft => ( + output_loc.x, + output_loc.y + output_geo.size.h - output_geo.size.h / div_factor, + ), + Layout::CornerBottomRight => ( + output_loc.x + output_geo.size.w - output_geo.size.w / div_factor, + output_loc.y + output_geo.size.h - output_geo.size.h / div_factor, + ), + _ => unreachable!(), + } + .into(), + ( + output_geo.size.w / div_factor, + output_geo.size.h / div_factor, + ) + .into(), + ); + + let vert_stack_count = vert_stack.len(); + + let height = output_geo.size.h as f32 / vert_stack_count as f32; + let mut y_s = vec![]; + for i in 0..vert_stack_count { + y_s.push((i as f32 * height).round() as i32); + } + let heights = y_s + .windows(2) + .map(|pair| pair[1] - pair[0]) + .chain(vec![output_geo.size.h - y_s.last().expect("vec was empty")]) + .collect::>(); + + for (i, win) in vert_stack.iter().enumerate() { + win.request_size_change( + ( + match layout { + Layout::CornerTopLeft | Layout::CornerBottomLeft => { + output_geo.size.w / 2 + output_loc.x + } + Layout::CornerTopRight | Layout::CornerBottomRight => output_loc.x, + _ => unreachable!(), + }, + y_s[i] + output_loc.y, + ) + .into(), + (output_geo.size.w / 2, i32::max(heights[i], 40)).into(), + ); + } + + let horiz_stack_count = horiz_stack.len(); + + let width = output_geo.size.w as f32 / 2.0 / horiz_stack_count as f32; + let mut x_s = vec![]; + for i in 0..horiz_stack_count { + x_s.push((i as f32 * width).round() as i32); + } + let widths = x_s + .windows(2) + .map(|pair| pair[1] - pair[0]) + .chain(vec![ + output_geo.size.w / 2 - x_s.last().expect("vec was empty"), + ]) + .collect::>(); + + for (i, win) in horiz_stack.iter().enumerate() { + win.request_size_change( + match layout { + Layout::CornerTopLeft => { + (x_s[i] + output_loc.x, output_loc.y + output_geo.size.h / 2) + } + Layout::CornerTopRight => ( + x_s[i] + output_loc.x + output_geo.size.w / 2, + output_loc.y + output_geo.size.h / 2, + ), + Layout::CornerBottomLeft => (x_s[i] + output_loc.x, output_loc.y), + Layout::CornerBottomRight => { + (x_s[i] + output_loc.x + output_geo.size.w / 2, output_loc.y) + } + _ => unreachable!(), + } + .into(), + (i32::max(widths[i], 1), output_geo.size.h / 2).into(), + ); + } + } + } +} + +fn filter_windows(windows: &[WindowElement], tags: Vec) -> Vec { windows .iter() .filter(|window| { @@ -582,7 +409,7 @@ fn filter_windows(windows: &[Window], tags: Vec) -> Vec { } impl State { - pub fn swap_window_positions(&mut self, win1: &Window, win2: &Window) { + pub fn swap_window_positions(&mut self, win1: &WindowElement, win2: &WindowElement) { let mut elems = self .windows .iter_mut() diff --git a/src/render.rs b/src/render.rs index 2f919dd..6f3dcbc 100644 --- a/src/render.rs +++ b/src/render.rs @@ -2,13 +2,16 @@ use smithay::{ backend::renderer::{ - element::{surface::WaylandSurfaceRenderElement, Wrap}, - ImportAll, ImportMem, + element::{surface::WaylandSurfaceRenderElement, AsRenderElements, Wrap}, + ImportAll, ImportMem, Renderer, Texture, }, - desktop::space::SpaceRenderElements, + desktop::space::{SpaceElement, SpaceRenderElements}, render_elements, + utils::{Physical, Point, Scale}, }; +use crate::window::WindowElement; + use self::pointer::PointerRenderElement; pub mod pointer; @@ -26,3 +29,34 @@ render_elements! { Custom=CustomRenderElements, // TODO: preview } + +impl AsRenderElements for WindowElement +where + R: Renderer + ImportAll + ImportMem, + ::TextureId: Texture + 'static, +{ + type RenderElement = WaylandSurfaceRenderElement; + + fn render_elements>( + &self, + renderer: &mut R, + location: Point, + scale: Scale, + alpha: f32, + ) -> Vec { + let window_bbox = self.bbox(); + match self { + WindowElement::Wayland(window) => { + AsRenderElements::::render_elements::>( + window, renderer, location, scale, alpha, + ) + } + WindowElement::X11(surface) => AsRenderElements::::render_elements::< + WaylandSurfaceRenderElement, + >(surface, renderer, location, scale, alpha), + } + .into_iter() + .map(C::from) + .collect() + } +} diff --git a/src/state.rs b/src/state.rs index 4d945a8..39c3eaf 100644 --- a/src/state.rs +++ b/src/state.rs @@ -20,18 +20,19 @@ use crate::{ focus::FocusState, grab::resize_grab::ResizeSurfaceState, tag::Tag, - window::window_state::WindowResizeState, + window::{window_state::WindowResizeState, WindowElement}, }; use calloop::futures::Scheduler; use futures_lite::AsyncBufReadExt; use smithay::{ backend::renderer::element::RenderElementStates, desktop::{ + space::SpaceElement, utils::{ surface_presentation_feedback_flags_from_states, surface_primary_scanout_output, OutputPresentationFeedback, }, - PopupManager, Space, Window, + PopupManager, Space, }, input::{keyboard::XkbConfig, pointer::CursorImageStatus, Seat, SeatState}, output::Output, @@ -71,7 +72,7 @@ pub struct State { pub loop_handle: LoopHandle<'static, CalloopData>, pub clock: Clock, - pub space: Space, + pub space: Space, pub move_mode: bool, pub socket_name: String, @@ -93,7 +94,7 @@ pub struct State { pub cursor_status: CursorImageStatus, pub pointer_location: Point, - pub windows: Vec, + pub windows: Vec, pub async_scheduler: Scheduler<()>, @@ -123,7 +124,12 @@ impl State { Msg::SetMousebind { button: _ } => todo!(), Msg::CloseWindow { window_id } => { if let Some(window) = window_id.window(self) { - window.toplevel().send_close(); + match window { + WindowElement::Wayland(window) => window.toplevel().send_close(), + WindowElement::X11(surface) => { + surface.close().expect("failed to close x11 win"); + } + } } } Msg::ToggleFloating { window_id } => { @@ -147,19 +153,13 @@ impl State { let Some(window) = window_id.window(self) else { return }; // TODO: tiled vs floating + // FIXME: this will map unmapped windows at 0,0 + let window_loc = self + .space + .element_location(&window) + .unwrap_or((0, 0).into()); let window_size = window.geometry().size; - window.toplevel().with_pending_state(|state| { - // INFO: calling window.geometry() in with_pending_state - // | will hang the compositor - state.size = Some( - ( - width.unwrap_or(window_size.w), - height.unwrap_or(window_size.h), - ) - .into(), - ); - }); - window.toplevel().send_pending_configure(); + window.request_size_change(window_loc, window_size); } Msg::MoveWindowToTag { window_id, tag_id } => { let Some(window) = window_id.window(self) else { return }; @@ -305,17 +305,20 @@ impl State { .as_ref() .and_then(|win| self.space.element_location(win)) .map(|loc| (loc.x, loc.y)); - let (class, title) = window.as_ref().map_or((None, None), |win| { - compositor::with_states(win.toplevel().wl_surface(), |states| { - let lock = states - .data_map - .get::() - .expect("XdgToplevelSurfaceData wasn't in surface's data map") - .lock() - .expect("failed to acquire lock"); - (lock.app_id.clone(), lock.title.clone()) - }) - }); + let (class, title) = window.as_ref().and_then(|win| win.wl_surface()).map_or( + (None, None), + |wl_surf| { + compositor::with_states(&wl_surf, |states| { + let lock = states + .data_map + .get::() + .expect("XdgToplevelSurfaceData wasn't in surface's data map") + .lock() + .expect("failed to acquire lock"); + (lock.app_id.clone(), lock.title.clone()) + }) + }, + ); let floating = window .as_ref() .map(|win| win.with_state(|state| state.floating.is_floating())); @@ -459,7 +462,13 @@ impl State { let program = OsString::from(program); let Ok(mut child) = async_process::Command::new(&program) - .env("WAYLAND_DISPLAY", self.socket_name.clone()) + .envs( + [("WAYLAND_DISPLAY", self.socket_name.clone())] + .into_iter() + .chain( + self.xdisplay.map(|xdisp| ("DISPLAY", format!(":{xdisp}"))) + ) + ) .stdin(if callback_id.is_some() { Stdio::piped() } else { @@ -670,7 +679,7 @@ impl State { /// idle. pub fn schedule_on_commit( data: &mut CalloopData, - windows: Vec, + windows: Vec, on_commit: F, ) where F: FnOnce(&mut CalloopData) + 'static, @@ -804,44 +813,48 @@ impl State { .expect("failed to insert rx_channel into loop"); }); + tracing::debug!("before xwayland"); let xwayland = { let (xwayland, channel) = XWayland::new(&display_handle); let clone = display_handle.clone(); - let res = - loop_handle.insert_source(channel, move |event, _metadata, data| match event { - XWaylandEvent::Ready { + tracing::debug!("inserting into loop"); + let res = loop_handle.insert_source(channel, move |event, _, data| match event { + XWaylandEvent::Ready { + connection, + client, + client_fd: _, + display, + } => { + tracing::debug!("XWaylandEvent ready"); + let mut wm = X11Wm::start_wm( + data.state.loop_handle.clone(), + clone.clone(), connection, client, - client_fd: _, - display, - } => { - let mut wm = X11Wm::start_wm( - data.state.loop_handle.clone(), - clone.clone(), - connection, - client, - ) - .expect("failed to attach x11wm"); - let cursor = Cursor::load(); - let image = cursor.get_image(1, Duration::ZERO); - wm.set_cursor( - &image.pixels_rgba, - Size::from((image.width as u16, image.height as u16)), - Point::from((image.xhot as u16, image.yhot as u16)), - ) - .expect("failed to set xwayland default cursor"); - data.state.xwm = Some(wm); - data.state.xdisplay = Some(display); - } - XWaylandEvent::Exited => { - data.state.xwm.take(); - } - }); + ) + .expect("failed to attach x11wm"); + let cursor = Cursor::load(); + let image = cursor.get_image(1, Duration::ZERO); + wm.set_cursor( + &image.pixels_rgba, + Size::from((image.width as u16, image.height as u16)), + Point::from((image.xhot as u16, image.yhot as u16)), + ) + .expect("failed to set xwayland default cursor"); + tracing::debug!("setting xwm and xdisplay"); + data.state.xwm = Some(wm); + data.state.xdisplay = Some(display); + } + XWaylandEvent::Exited => { + data.state.xwm.take(); + } + }); if let Err(err) = res { tracing::error!("Failed to insert XWayland source into loop: {err}"); } xwayland }; + tracing::debug!("after xwayland"); Ok(Self { backend_data, @@ -853,7 +866,7 @@ impl State { seat_state, pointer_location: (0.0, 0.0).into(), shm_state: ShmState::new::(&display_handle, vec![]), - space: Space::::default(), + space: Space::::default(), cursor_status: CursorImageStatus::Default, output_manager_state: OutputManagerState::new_with_xdg_output::(&display_handle), xdg_shell_state: XdgShellState::new::(&display_handle), @@ -908,7 +921,7 @@ pub struct SurfaceDmabufFeedback<'a> { // TODO: docs pub fn take_presentation_feedback( output: &Output, - space: &Space, + space: &Space, render_element_states: &RenderElementStates, ) -> OutputPresentationFeedback { let mut output_presentation_feedback = OutputPresentationFeedback::new(output); diff --git a/src/window.rs b/src/window.rs index aacab4d..a3a6335 100644 --- a/src/window.rs +++ b/src/window.rs @@ -8,8 +8,8 @@ use smithay::{ space::SpaceElement, utils::{ send_dmabuf_feedback_surface_tree, send_frames_surface_tree, - take_presentation_feedback_surface_tree, with_surfaces_surface_tree, - OutputPresentationFeedback, + take_presentation_feedback_surface_tree, under_from_surface_tree, + with_surfaces_surface_tree, OutputPresentationFeedback, }, Window, WindowSurfaceType, }, @@ -26,7 +26,7 @@ use smithay::{ }, wayland_server::protocol::wl_surface::WlSurface, }, - utils::{user_data::UserDataMap, IsAlive, Logical, Point, Rectangle, Serial}, + utils::{user_data::UserDataMap, IsAlive, Logical, Point, Rectangle, Serial, Size}, wayland::{ compositor::{Blocker, BlockerState, SurfaceData}, dmabuf::DmabufFeedback, @@ -40,7 +40,7 @@ use crate::{ state::{State, WithState}, }; -use self::window_state::{Float, WindowState}; +use self::window_state::{Float, WindowResizeState, WindowState}; pub mod window_state; @@ -53,10 +53,15 @@ pub enum WindowElement { impl WindowElement { pub fn surface_under( &self, - location: Point, + location: Point, window_type: WindowSurfaceType, ) -> Option<(WlSurface, Point)> { - todo!() + match self { + WindowElement::Wayland(window) => window.surface_under(location, window_type), + WindowElement::X11(surface) => surface.wl_surface().and_then(|wl_surf| { + under_from_surface_tree(&wl_surf, location, (0, 0), window_type) + }), + } } pub fn with_surfaces(&self, processor: F) @@ -174,6 +179,29 @@ impl WindowElement { WindowElement::X11(surface) => surface.user_data(), } } + + /// Request a size and loc change. + pub fn request_size_change(&self, new_loc: Point, new_size: Size) { + match self { + WindowElement::Wayland(window) => { + window.toplevel().with_pending_state(|state| { + state.size = Some(new_size); + }); + self.with_state(|state| { + state.resize_state = + WindowResizeState::Requested(window.toplevel().send_configure(), new_loc) + }); + } + WindowElement::X11(surface) => { + surface + .configure(Rectangle::from_loc_and_size(new_loc, new_size)) + .expect("failed to configure x11 win"); + self.with_state(|state| { + state.resize_state = WindowResizeState::Acknowledged(new_loc); + }); + } + } + } } impl IsAlive for WindowElement { @@ -393,7 +421,7 @@ impl WithState for WindowElement { impl State { /// Returns the [Window] associated with a given [WlSurface]. - pub fn window_for_surface(&self, surface: &WlSurface) -> Option { + pub fn window_for_surface(&self, surface: &WlSurface) -> Option { self.space .elements() .find(|window| window.wl_surface().map(|s| s == *surface).unwrap_or(false)) @@ -401,35 +429,31 @@ impl State { .or_else(|| { self.windows .iter() - .find(|&win| win.toplevel().wl_surface() == surface) + .find(|&win| win.wl_surface().is_some_and(|surf| &surf == surface)) .cloned() }) } } /// Toggle a window's floating status. -pub fn toggle_floating(state: &mut State, window: &Window) { +pub fn toggle_floating(state: &mut State, window: &WindowElement) { + let mut resize: Option<_> = None; window.with_state(|window_state| { match window_state.floating { Float::Tiled(prev_loc_and_size) => { if let Some((prev_loc, prev_size)) = prev_loc_and_size { - window.toplevel().with_pending_state(|state| { - state.size = Some(prev_size); - }); - - window.toplevel().send_pending_configure(); - - state.space.map_element(window.clone(), prev_loc, false); - // TODO: should it activate? + resize = Some((prev_loc, prev_size)); } window_state.floating = Float::Floating; - window.toplevel().with_pending_state(|tl_state| { - tl_state.states.unset(xdg_toplevel::State::TiledTop); - tl_state.states.unset(xdg_toplevel::State::TiledBottom); - tl_state.states.unset(xdg_toplevel::State::TiledLeft); - tl_state.states.unset(xdg_toplevel::State::TiledRight); - }); + if let WindowElement::Wayland(window) = window { + window.toplevel().with_pending_state(|tl_state| { + tl_state.states.unset(xdg_toplevel::State::TiledTop); + tl_state.states.unset(xdg_toplevel::State::TiledBottom); + tl_state.states.unset(xdg_toplevel::State::TiledLeft); + tl_state.states.unset(xdg_toplevel::State::TiledRight); + }); + } // TODO: tiled states for x11 } Float::Floating => { window_state.floating = Float::Tiled(Some(( @@ -438,16 +462,23 @@ pub fn toggle_floating(state: &mut State, window: &Window) { state.space.element_location(window).unwrap(), window.geometry().size, ))); - window.toplevel().with_pending_state(|tl_state| { - tl_state.states.set(xdg_toplevel::State::TiledTop); - tl_state.states.set(xdg_toplevel::State::TiledBottom); - tl_state.states.set(xdg_toplevel::State::TiledLeft); - tl_state.states.set(xdg_toplevel::State::TiledRight); - }); + + if let WindowElement::Wayland(window) = window { + window.toplevel().with_pending_state(|tl_state| { + tl_state.states.set(xdg_toplevel::State::TiledTop); + tl_state.states.set(xdg_toplevel::State::TiledBottom); + tl_state.states.set(xdg_toplevel::State::TiledLeft); + tl_state.states.set(xdg_toplevel::State::TiledRight); + }); + } } } }); + if let Some((prev_loc, prev_size)) = resize { + window.request_size_change(prev_loc, prev_size); + } + let output = state.focus_state.focused_output.clone().unwrap(); state.re_layout(&output); @@ -476,6 +507,14 @@ pub fn toggle_floating(state: &mut State, window: &Window) { state.loop_handle.insert_idle(move |data| { crate::state::schedule_on_commit(data, render, move |dt| { dt.state.space.raise_element(&clone, true); + if let WindowElement::X11(surface) = clone { + dt.state + .xwm + .as_mut() + .expect("no xwm") + .raise_window(&surface) + .expect("failed to raise x11 win"); + } }); }); } diff --git a/src/window/window_state.rs b/src/window/window_state.rs index 53183b4..e9b733b 100644 --- a/src/window/window_state.rs +++ b/src/window/window_state.rs @@ -1,15 +1,11 @@ // SPDX-License-Identifier: GPL-3.0-or-later use std::{ - cell::RefCell, fmt, sync::atomic::{AtomicU32, Ordering}, }; -use smithay::{ - desktop::Window, - utils::{Logical, Point, Serial, Size}, -}; +use smithay::utils::{Logical, Point, Serial, Size}; use crate::{ backend::Backend, @@ -17,6 +13,8 @@ use crate::{ tag::Tag, }; +use super::WindowElement; + #[derive(Debug, Hash, Clone, Copy, PartialEq, Eq, serde::Serialize, serde::Deserialize)] pub struct WindowId(u32); @@ -28,7 +26,7 @@ impl WindowId { } /// Get the window that has this WindowId. - pub fn window(&self, state: &State) -> Option { + pub fn window(&self, state: &State) -> Option { state .windows .iter() @@ -128,25 +126,6 @@ impl WindowState { } } -impl WithState for Window { - type State = WindowState; - - fn with_state(&self, mut func: F) -> T - where - F: FnMut(&mut Self::State) -> T, - { - self.user_data() - .insert_if_missing(RefCell::::default); - - let state = self - .user_data() - .get::>() - .expect("RefCell not in data map"); - - func(&mut state.borrow_mut()) - } -} - impl Default for WindowState { fn default() -> Self { Self { diff --git a/src/xdg/request.rs b/src/xdg/request.rs index db5dcd0..339bc2f 100644 --- a/src/xdg/request.rs +++ b/src/xdg/request.rs @@ -1,16 +1,24 @@ // SPDX-License-Identifier: GPL-3.0-or-later use smithay::{ + desktop::space::SpaceElement, input::{pointer::Focus, Seat}, - reexports::wayland_protocols::xdg::shell::server::xdg_toplevel, + reexports::{ + wayland_protocols::xdg::shell::server::xdg_toplevel, + wayland_server::protocol::wl_surface::WlSurface, + }, utils::Rectangle, wayland::shell::xdg::ToplevelSurface, }; use crate::{ backend::Backend, - grab::{move_grab::MoveSurfaceGrab, resize_grab::ResizeSurfaceGrab}, + grab::{ + move_grab::MoveSurfaceGrab, + resize_grab::{ResizeEdge, ResizeSurfaceGrab}, + }, state::{State, WithState}, + window::WindowElement, }; pub fn move_request( @@ -19,8 +27,6 @@ pub fn move_request( seat: &Seat>, serial: smithay::utils::Serial, ) { - println!("move_request started"); - let wl_surface = surface.wl_surface(); let pointer = seat.get_pointer().unwrap(); @@ -38,23 +44,19 @@ pub fn move_request( pointer.set_grab(state, grab, serial, Focus::Clear); } else { - println!("no grab start data"); + tracing::warn!("no grab start data"); } } // TODO: see how this interacts with drag and drop and other grabs pub fn move_request_force( state: &mut State, - surface: &ToplevelSurface, + surface: &WlSurface, seat: &Seat>, serial: smithay::utils::Serial, ) { - println!("move_request_force started"); - - let wl_surface = surface.wl_surface(); - let pointer = seat.get_pointer().unwrap(); - let window = state.window_for_surface(wl_surface).unwrap(); + let window = state.window_for_surface(surface).unwrap(); let initial_window_loc = state.space.element_location(&window).unwrap(); @@ -77,19 +79,16 @@ pub fn move_request_force( pub fn resize_request( state: &mut State, - surface: &ToplevelSurface, + surface: &WlSurface, seat: &Seat>, serial: smithay::utils::Serial, edges: xdg_toplevel::ResizeEdge, button_used: u32, ) { - let wl_surface = surface.wl_surface(); - let pointer = seat.get_pointer().unwrap(); - if let Some(start_data) = crate::pointer::pointer_grab_start_data(&pointer, wl_surface, serial) - { - let window = state.window_for_surface(wl_surface).unwrap(); + if let Some(start_data) = crate::pointer::pointer_grab_start_data(&pointer, surface, serial) { + let window = state.window_for_surface(surface).unwrap(); if window.with_state(|state| state.floating.is_tiled()) { return; } @@ -97,37 +96,39 @@ pub fn resize_request( let initial_window_loc = state.space.element_location(&window).unwrap(); let initial_window_size = window.geometry().size; - surface.with_pending_state(|state| { - state.states.set(xdg_toplevel::State::Resizing); - }); + if let Some(WindowElement::Wayland(window)) = state.window_for_surface(surface) { + window.toplevel().with_pending_state(|state| { + state.states.set(xdg_toplevel::State::Resizing); + }); - surface.send_pending_configure(); + window.toplevel().send_pending_configure(); + } let grab = ResizeSurfaceGrab::start( start_data, window, - edges, + ResizeEdge(edges), Rectangle::from_loc_and_size(initial_window_loc, initial_window_size), button_used, ); - pointer.set_grab(state, grab, serial, Focus::Clear); + if let Some(grab) = grab { + pointer.set_grab(state, grab, serial, Focus::Clear); + } } } pub fn resize_request_force( state: &mut State, - surface: &ToplevelSurface, + surface: &WlSurface, seat: &Seat>, serial: smithay::utils::Serial, edges: xdg_toplevel::ResizeEdge, button_used: u32, ) { - let wl_surface = surface.wl_surface(); - let pointer = seat.get_pointer().unwrap(); - let window = state.window_for_surface(wl_surface).unwrap(); + let window = state.window_for_surface(surface).unwrap(); if window.with_state(|state| state.floating.is_tiled()) { return; @@ -136,12 +137,13 @@ pub fn resize_request_force( let initial_window_loc = state.space.element_location(&window).unwrap(); let initial_window_size = window.geometry().size; - surface.with_pending_state(|state| { - println!("setting xdg state to Resizing"); - state.states.set(xdg_toplevel::State::Resizing); - }); + if let Some(WindowElement::Wayland(window)) = state.window_for_surface(surface) { + window.toplevel().with_pending_state(|state| { + state.states.set(xdg_toplevel::State::Resizing); + }); - surface.send_pending_configure(); + window.toplevel().send_pending_configure(); + } let start_data = smithay::input::pointer::GrabStartData { focus: pointer @@ -154,10 +156,12 @@ pub fn resize_request_force( let grab = ResizeSurfaceGrab::start( start_data, window, - edges, + ResizeEdge(edges), Rectangle::from_loc_and_size(initial_window_loc, initial_window_size), button_used, ); - pointer.set_grab(state, grab, serial, Focus::Clear); + if let Some(grab) = grab { + pointer.set_grab(state, grab, serial, Focus::Clear); + } }