diff --git a/src/wayland/compositor.rs b/src/wayland/compositor.rs index 6997a40..232d617 100644 --- a/src/wayland/compositor.rs +++ b/src/wayland/compositor.rs @@ -6,6 +6,7 @@ use smithay::{ delegate_compositor, reexports::wayland_server::{protocol::wl_surface::WlSurface, Client}, wayland::compositor::{self, CompositorClientState, CompositorHandler, CompositorState}, + xwayland::XWaylandClientData, }; use std::sync::Arc; use tracing::debug; @@ -38,7 +39,13 @@ impl CompositorHandler for WaylandState { } fn client_compositor_state<'a>(&self, client: &'a Client) -> &'a CompositorClientState { - &client.get_data::().unwrap().compositor_state + if let Some(client_state) = client.get_data::() { + &client_state.compositor_state + } else if let Some(xwayland_client_data) = client.get_data::() { + &xwayland_client_data.compositor_state + } else { + unimplemented!() + } } } diff --git a/src/wayland/mod.rs b/src/wayland/mod.rs index c594d5e..827608d 100644 --- a/src/wayland/mod.rs +++ b/src/wayland/mod.rs @@ -88,7 +88,7 @@ impl Wayland { let (dmabuf_tx, dmabuf_rx) = mpsc::unbounded_channel(); let display = Arc::new(Mutex::new(display)); - let xwayland_state = XWaylandState::create(&display_handle).unwrap(); + let xwayland_state = XWaylandState::create(display.clone(), &display_handle).unwrap(); let wayland_state = WaylandState::new(display.clone(), display_handle, &renderer, dmabuf_tx); let (global_destroy_queue_in, global_destroy_queue) = mpsc::channel(8); diff --git a/src/wayland/panel_item.rs b/src/wayland/panel_item.rs index 3770c50..75ccfc3 100644 --- a/src/wayland/panel_item.rs +++ b/src/wayland/panel_item.rs @@ -41,6 +41,7 @@ use smithay::{ }, }, wayland::compositor, + xwayland::X11Surface, }; use stardust_xr::schemas::flex::{deserialize, serialize}; use std::sync::{Arc, Weak}; @@ -316,7 +317,10 @@ impl WaylandBackend { } } #[derive(Debug)] -pub struct X11Backend; +pub struct X11Backend { + pub toplevel_parent: Option, + pub toplevel: X11Surface, +} #[derive(Debug)] pub enum Backend { @@ -719,7 +723,24 @@ impl ItemSpecialization for PanelItem { )) .unwrap() } - Backend::X11(_) => todo!(), + Backend::X11(x) => { + let toplevel_state = ( + None::, + x.toplevel.title(), + None::, + (x.toplevel.geometry().size.w, x.toplevel.geometry().size.h), + x.toplevel.min_size().map(|s| (s.w, s.h)), + x.toplevel.max_size().map(|s| (s.w, s.w)), + ); + let info = ( + None::, + toplevel_state, + Vec::::new(), + None::, + None::, + ); + serialize((id, info)).unwrap() + } } } } diff --git a/src/wayland/xwayland.rs b/src/wayland/xwayland.rs index 3fd2730..8c5bb1e 100644 --- a/src/wayland/xwayland.rs +++ b/src/wayland/xwayland.rs @@ -1,13 +1,20 @@ -use super::seat::SeatData; +use super::{panel_item::RecommendedState, seat::SeatData, state::WaylandState}; +use crate::wayland::{ + panel_item::{Backend, PanelItem, X11Backend}, + surface::CoreSurface, +}; use color_eyre::eyre::Result; use once_cell::sync::OnceCell; +use parking_lot::Mutex; use smithay::{ reexports::{ calloop::{self, EventLoop, LoopSignal}, - wayland_server::DisplayHandle, + wayland_protocols::xdg::shell::server::xdg_toplevel, + wayland_server::{Display, DisplayHandle, Resource, WEnum}, x11rb::protocol::xproto::Window, }, utils::{Logical, Rectangle}, + wayland::compositor, xwayland::{ xwm::{Reorder, ResizeEdge, XwmId}, X11Surface, X11Wm, XWayland, XWaylandEvent, XwmHandler, @@ -26,7 +33,10 @@ pub struct XWaylandState { event_loop_join: Option>>, } impl XWaylandState { - pub fn create(dh: &DisplayHandle) -> Result { + pub fn create( + wayland_display: Arc>>, + dh: &DisplayHandle, + ) -> Result { let dh = dh.clone(); let (tx, rx) = oneshot::channel(); @@ -57,7 +67,7 @@ impl XWaylandState { None, empty::<(&OsStr, &OsStr)>(), true, - move |_| (), + |_| (), )?; let _ = tx.send(XWaylandState { display, @@ -65,7 +75,13 @@ impl XWaylandState { event_loop_signal: event_loop.get_signal(), event_loop_join: None, }); - let mut handler = XWaylandHandler::default(); + let wayland_display_handle = wayland_display.lock().handle(); + let mut handler = XWaylandHandler { + wayland_display, + wayland_display_handle, + wm: None, + seat: None, + }; event_loop.run(Duration::from_secs(60 * 60), &mut handler, |_| ()) }); @@ -83,11 +99,19 @@ impl Drop for XWaylandState { } } -#[derive(Default)] struct XWaylandHandler { + wayland_display: Arc>>, + wayland_display_handle: DisplayHandle, wm: Option, seat: Option>, } +impl XWaylandHandler { + fn panel_item(&self, window: &X11Surface) -> Option> { + compositor::with_states(&window.wl_surface()?, |s| { + s.data_map.get::>().cloned() + }) + } +} impl XwmHandler for XWaylandHandler { fn xwm_state(&mut self, _xwm: XwmId) -> &mut X11Wm { @@ -104,6 +128,44 @@ impl XwmHandler for XWaylandHandler { fn map_window_request(&mut self, _xwm: XwmId, window: X11Surface) { debug!(?window, "X map window request"); + window.set_mapped(true).unwrap(); + } + fn map_window_notify(&mut self, _xwm: XwmId, window: X11Surface) { + debug!(?window, "X map window notify"); + + let dh = self.wayland_display_handle.clone(); + let seat = self.seat.clone().unwrap(); + CoreSurface::add_to( + &self.wayland_display, + self.wayland_display.lock().handle(), + &window.wl_surface().unwrap(), + move |c| { + let Some(wl_surface) = window.wl_surface() else {return}; + match c { + 0 => { + let seat = seat.clone(); + window.user_data().insert_if_missing_threadsafe(|| { + let (_node, panel_item) = PanelItem::create( + wl_surface.clone(), + Backend::X11(X11Backend { + toplevel_parent: None, + toplevel: window.clone(), + }), + wl_surface + .client() + .and_then(|c| c.get_credentials(&dh).ok()), + seat, + ); + panel_item + }); + } + _ => { + let Some(panel_item) = window.user_data().get::>() else {return}; + panel_item.commit_toplevel(); + } + } + }, + ); } fn mapped_override_redirect_window(&mut self, _xwm: XwmId, window: X11Surface) { @@ -141,6 +203,11 @@ impl XwmHandler for XWaylandHandler { debug!(?window, ?geometry, above, "Configure X window"); } + fn move_request(&mut self, _xwm: XwmId, window: X11Surface, button: u32) { + let Some(panel_item) = self.panel_item(&window) else {return}; + debug!(?window, button, "X window requests move"); + panel_item.recommend_toplevel_state(RecommendedState::Move); + } fn resize_request( &mut self, _xwm: XwmId, @@ -148,10 +215,41 @@ impl XwmHandler for XWaylandHandler { button: u32, resize_edge: ResizeEdge, ) { + let Some(panel_item) = self.panel_item(&window) else {return}; debug!(?window, button, ?resize_edge, "X window requests resize"); + panel_item.recommend_toplevel_state(RecommendedState::Resize( + WEnum::Value(match resize_edge { + ResizeEdge::Top => xdg_toplevel::ResizeEdge::Top, + ResizeEdge::Bottom => xdg_toplevel::ResizeEdge::Bottom, + ResizeEdge::Left => xdg_toplevel::ResizeEdge::Left, + ResizeEdge::TopLeft => xdg_toplevel::ResizeEdge::TopLeft, + ResizeEdge::BottomLeft => xdg_toplevel::ResizeEdge::BottomLeft, + ResizeEdge::Right => xdg_toplevel::ResizeEdge::Right, + ResizeEdge::TopRight => xdg_toplevel::ResizeEdge::TopRight, + ResizeEdge::BottomRight => xdg_toplevel::ResizeEdge::BottomRight, + }) + .into(), + )); } - fn move_request(&mut self, _xwm: XwmId, window: X11Surface, button: u32) { - debug!(?window, button, "X window requests move"); + fn maximize_request(&mut self, _xwm: XwmId, window: X11Surface) { + let Some(panel_item) = self.panel_item(&window) else {return}; + panel_item.recommend_toplevel_state(RecommendedState::Maximize(true)); + } + fn unmaximize_request(&mut self, _xwm: XwmId, window: X11Surface) { + let Some(panel_item) = self.panel_item(&window) else {return}; + panel_item.recommend_toplevel_state(RecommendedState::Maximize(false)); + } + fn fullscreen_request(&mut self, _xwm: XwmId, window: X11Surface) { + let Some(panel_item) = self.panel_item(&window) else {return}; + panel_item.recommend_toplevel_state(RecommendedState::Fullscreen(true)); + } + fn unfullscreen_request(&mut self, _xwm: XwmId, window: X11Surface) { + let Some(panel_item) = self.panel_item(&window) else {return}; + panel_item.recommend_toplevel_state(RecommendedState::Fullscreen(true)); + } + fn minimize_request(&mut self, _xwm: XwmId, window: X11Surface) { + let Some(panel_item) = self.panel_item(&window) else {return}; + panel_item.recommend_toplevel_state(RecommendedState::Minimize); } }