diff --git a/src/handlers.rs b/src/handlers.rs index ae59958..de30c18 100644 --- a/src/handlers.rs +++ b/src/handlers.rs @@ -1,5 +1,7 @@ // SPDX-License-Identifier: GPL-3.0-or-later +pub mod xwayland; + use std::time::Duration; use smithay::{ diff --git a/src/handlers/xwayland.rs b/src/handlers/xwayland.rs new file mode 100644 index 0000000..5be0b9f --- /dev/null +++ b/src/handlers/xwayland.rs @@ -0,0 +1,99 @@ +use smithay::xwayland::XwmHandler; + +use crate::{backend::Backend, state::CalloopData}; + +impl XwmHandler for CalloopData { + fn xwm_state(&mut self, xwm: smithay::xwayland::xwm::XwmId) -> &mut smithay::xwayland::X11Wm { + todo!() + } + + fn new_window( + &mut self, + xwm: smithay::xwayland::xwm::XwmId, + window: smithay::xwayland::X11Surface, + ) { + todo!() + } + + fn new_override_redirect_window( + &mut self, + xwm: smithay::xwayland::xwm::XwmId, + window: smithay::xwayland::X11Surface, + ) { + todo!() + } + + fn map_window_request( + &mut self, + xwm: smithay::xwayland::xwm::XwmId, + window: smithay::xwayland::X11Surface, + ) { + todo!() + } + + fn mapped_override_redirect_window( + &mut self, + xwm: smithay::xwayland::xwm::XwmId, + window: smithay::xwayland::X11Surface, + ) { + todo!() + } + + fn unmapped_window( + &mut self, + xwm: smithay::xwayland::xwm::XwmId, + window: smithay::xwayland::X11Surface, + ) { + todo!() + } + + fn destroyed_window( + &mut self, + xwm: smithay::xwayland::xwm::XwmId, + window: smithay::xwayland::X11Surface, + ) { + todo!() + } + + fn configure_request( + &mut self, + xwm: smithay::xwayland::xwm::XwmId, + window: smithay::xwayland::X11Surface, + x: Option, + y: Option, + w: Option, + h: Option, + reorder: Option, + ) { + todo!() + } + + fn configure_notify( + &mut self, + xwm: smithay::xwayland::xwm::XwmId, + window: smithay::xwayland::X11Surface, + geometry: smithay::utils::Rectangle, + above: Option, + ) { + todo!() + } + + fn resize_request( + &mut self, + xwm: smithay::xwayland::xwm::XwmId, + window: smithay::xwayland::X11Surface, + button: u32, + resize_edge: smithay::xwayland::xwm::ResizeEdge, + ) { + todo!() + } + + fn move_request( + &mut self, + xwm: smithay::xwayland::xwm::XwmId, + window: smithay::xwayland::X11Surface, + button: u32, + ) { + todo!() + } +} diff --git a/src/state.rs b/src/state.rs index ad31b41..4d945a8 100644 --- a/src/state.rs +++ b/src/state.rs @@ -8,6 +8,7 @@ use std::{ path::Path, process::Stdio, sync::{Arc, Mutex}, + time::Duration, }; use crate::{ @@ -15,6 +16,7 @@ use crate::{ msg::{Args, CallbackId, Msg, OutgoingMsg, Request, RequestId, RequestResponse}, PinnacleSocketSource, }, + cursor::Cursor, focus::FocusState, grab::resize_grab::ResizeSurfaceState, tag::Tag, @@ -44,7 +46,7 @@ use smithay::{ Display, }, }, - utils::{Clock, Logical, Monotonic, Point}, + utils::{Clock, Logical, Monotonic, Point, Size}, wayland::{ compositor::{self, CompositorClientState, CompositorState}, data_device::DataDeviceState, @@ -56,6 +58,7 @@ use smithay::{ socket::ListeningSocketSource, viewporter::ViewporterState, }, + xwayland::{X11Wm, XWayland, XWaylandEvent}, }; use crate::{backend::Backend, input::InputState}; @@ -97,6 +100,10 @@ pub struct State { // TODO: move into own struct // | basically just clean this mess up pub output_callback_ids: Vec, + + pub xwayland: XWayland, + pub xwm: Option, + pub xdisplay: Option, } impl State { @@ -794,9 +801,48 @@ impl State { Event::Msg(msg) => data.state.handle_msg(msg), Event::Closed => todo!(), }) - .unwrap(); // TODO: unwrap + .expect("failed to insert rx_channel into loop"); }); + 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 { + 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(); + } + }); + if let Err(err) = res { + tracing::error!("Failed to insert XWayland source into loop: {err}"); + } + xwayland + }; + Ok(Self { backend_data, loop_signal, @@ -830,6 +876,10 @@ impl State { windows: vec![], output_callback_ids: vec![], + + xwayland, + xwm: None, + xdisplay: None, }) } } diff --git a/src/window.rs b/src/window.rs index bbb5c84..9b70d37 100644 --- a/src/window.rs +++ b/src/window.rs @@ -1,17 +1,31 @@ // SPDX-License-Identifier: GPL-3.0-or-later -use std::sync::atomic::AtomicU32; +use std::{cell::RefCell, sync::atomic::AtomicU32, time::Duration}; use smithay::{ - desktop::Window, + desktop::{ + utils::{ + send_dmabuf_feedback_surface_tree, send_frames_surface_tree, + take_presentation_feedback_surface_tree, with_surfaces_surface_tree, + OutputPresentationFeedback, + }, + Window, WindowSurfaceType, + }, + output::Output, reexports::{ - wayland_protocols::xdg::shell::server::xdg_toplevel, + wayland_protocols::{ + wp::presentation_time::server::wp_presentation_feedback, + xdg::shell::server::xdg_toplevel, + }, wayland_server::protocol::wl_surface::WlSurface, }, + utils::{user_data::UserDataMap, IsAlive, Logical, Point}, wayland::{ - compositor::{Blocker, BlockerState}, + compositor::{Blocker, BlockerState, SurfaceData}, + dmabuf::DmabufFeedback, seat::WaylandFocus, }, + xwayland::X11Surface, }; use crate::{ @@ -19,10 +33,170 @@ use crate::{ state::{State, WithState}, }; -use self::window_state::Float; +use self::window_state::{Float, WindowState}; pub mod window_state; +#[derive(Debug, Clone, PartialEq)] +pub enum WindowElement { + Wayland(Window), + X11(X11Surface), +} + +impl WindowElement { + pub fn surface_under( + &self, + location: Point, + window_type: WindowSurfaceType, + ) -> Option<(WlSurface, Point)> { + todo!() + } + + pub fn with_surfaces(&self, processor: F) + where + F: FnMut(&WlSurface, &SurfaceData) + Copy, + { + match self { + WindowElement::Wayland(window) => window.with_surfaces(processor), + WindowElement::X11(surface) => { + if let Some(surface) = surface.wl_surface() { + with_surfaces_surface_tree(&surface, processor); + } + } + } + } + + pub fn send_frame( + &self, + output: &Output, + time: T, + throttle: Option, + primary_scan_out_output: F, + ) where + T: Into, + F: FnMut(&WlSurface, &SurfaceData) -> Option + Copy, + { + match self { + WindowElement::Wayland(window) => { + window.send_frame(output, time, throttle, primary_scan_out_output) + } + WindowElement::X11(surface) => { + if let Some(surface) = surface.wl_surface() { + send_frames_surface_tree( + &surface, + output, + time, + throttle, + primary_scan_out_output, + ); + } + } + } + } + + pub fn send_dmabuf_feedback<'a, P, F>( + &self, + output: &Output, + primary_scan_out_output: P, + select_dmabuf_feedback: F, + ) where + P: FnMut(&WlSurface, &SurfaceData) -> Option + Copy, + F: Fn(&WlSurface, &SurfaceData) -> &'a DmabufFeedback + Copy, + { + match self { + WindowElement::Wayland(window) => { + window.send_dmabuf_feedback( + output, + primary_scan_out_output, + select_dmabuf_feedback, + ); + } + WindowElement::X11(surface) => { + if let Some(surface) = surface.wl_surface() { + send_dmabuf_feedback_surface_tree( + &surface, + output, + primary_scan_out_output, + select_dmabuf_feedback, + ); + } + } + } + } + + pub fn take_presentation_feedback( + &self, + output_feedback: &mut OutputPresentationFeedback, + primary_scan_out_output: F1, + presentation_feedback_flags: F2, + ) where + F1: FnMut(&WlSurface, &SurfaceData) -> Option + Copy, + F2: FnMut(&WlSurface, &SurfaceData) -> wp_presentation_feedback::Kind + Copy, + { + match self { + WindowElement::Wayland(window) => { + window.take_presentation_feedback( + output_feedback, + primary_scan_out_output, + presentation_feedback_flags, + ); + } + WindowElement::X11(surface) => { + if let Some(surface) = surface.wl_surface() { + take_presentation_feedback_surface_tree( + &surface, + output_feedback, + primary_scan_out_output, + presentation_feedback_flags, + ); + } + } + } + } + + pub fn wl_surface(&self) -> Option { + match self { + WindowElement::Wayland(window) => window.wl_surface(), + WindowElement::X11(surface) => surface.wl_surface(), + } + } + + pub fn user_data(&self) -> &UserDataMap { + match self { + WindowElement::Wayland(window) => window.user_data(), + WindowElement::X11(surface) => surface.user_data(), + } + } +} + +impl IsAlive for WindowElement { + fn alive(&self) -> bool { + match self { + WindowElement::Wayland(window) => window.alive(), + WindowElement::X11(surface) => surface.alive(), + } + } +} + +impl WithState for WindowElement { + 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 State { /// Returns the [Window] associated with a given [WlSurface]. pub fn window_for_surface(&self, surface: &WlSurface) -> Option {