From e879b724ecd75b7b3e29fc663c73638667892058 Mon Sep 17 00:00:00 2001 From: Nova Date: Thu, 11 May 2023 04:58:49 -0400 Subject: [PATCH] feat(wayland): initial xwayland support --- Cargo.lock | 108 +++++++++++++++++++++++++++ Cargo.toml | 8 +- src/main.rs | 1 + src/nodes/startup.rs | 7 +- src/wayland/mod.rs | 21 ++++-- src/wayland/xwayland.rs | 157 ++++++++++++++++++++++++++++++++++++++++ 6 files changed, 294 insertions(+), 8 deletions(-) create mode 100644 src/wayland/xwayland.rs diff --git a/Cargo.lock b/Cargo.lock index 0f59b0d..971019d 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -670,6 +670,70 @@ version = "1.9.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "a26ae43d7bcc3b814de94796a5e736d4029efb0ee900c12e2d54c993ad1a1e07" +[[package]] +name = "encoding" +version = "0.2.33" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6b0d943856b990d12d3b55b359144ff341533e516d94098b1d3fc1ac666d36ec" +dependencies = [ + "encoding-index-japanese", + "encoding-index-korean", + "encoding-index-simpchinese", + "encoding-index-singlebyte", + "encoding-index-tradchinese", +] + +[[package]] +name = "encoding-index-japanese" +version = "1.20141219.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "04e8b2ff42e9a05335dbf8b5c6f7567e5591d0d916ccef4e0b1710d32a0d0c91" +dependencies = [ + "encoding_index_tests", +] + +[[package]] +name = "encoding-index-korean" +version = "1.20141219.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "4dc33fb8e6bcba213fe2f14275f0963fd16f0a02c878e3095ecfdf5bee529d81" +dependencies = [ + "encoding_index_tests", +] + +[[package]] +name = "encoding-index-simpchinese" +version = "1.20141219.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d87a7194909b9118fc707194baa434a4e3b0fb6a5a757c73c3adb07aa25031f7" +dependencies = [ + "encoding_index_tests", +] + +[[package]] +name = "encoding-index-singlebyte" +version = "1.20141219.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3351d5acffb224af9ca265f435b859c7c01537c0849754d3db3fdf2bfe2ae84a" +dependencies = [ + "encoding_index_tests", +] + +[[package]] +name = "encoding-index-tradchinese" +version = "1.20141219.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "fd0e20d5688ce3cab59eb3ef3a2083a5c77bf496cb798dc6fcdb75f323890c18" +dependencies = [ + "encoding_index_tests", +] + +[[package]] +name = "encoding_index_tests" +version = "0.1.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a246d82be1c9d791c5dfde9a2bd045fc3cbba3fa2b11ad558f27d01712f00569" + [[package]] name = "equivalent" version = "1.0.1" @@ -813,6 +877,16 @@ dependencies = [ "pin-utils", ] +[[package]] +name = "gethostname" +version = "0.2.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c1ebd34e35c46e00bb73e81363248d627782724609fe1b6396f553f68fe3862e" +dependencies = [ + "libc", + "winapi", +] + [[package]] name = "getrandom" version = "0.2.10" @@ -1988,6 +2062,7 @@ dependencies = [ "drm", "drm-ffi", "drm-fourcc", + "encoding", "gl_generator", "indexmap 1.9.3", "lazy_static", @@ -1998,6 +2073,7 @@ dependencies = [ "profiling", "rand", "scan_fmt", + "scopeguard", "tempfile", "thiserror", "tracing", @@ -2005,6 +2081,7 @@ dependencies = [ "wayland-protocols-misc", "wayland-protocols-wlr", "wayland-server", + "x11rb", "xkbcommon", ] @@ -2654,6 +2731,15 @@ version = "0.4.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "ac3b87c63620426dd9b991e5ce0329eff545bccbbb34f3be09ff6fb6ab51b7b6" +[[package]] +name = "winapi-wsapoll" +version = "0.1.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "44c17110f57155602a80dca10be03852116403c9ff3cd25b079d666f2aa3df6e" +dependencies = [ + "winapi", +] + [[package]] name = "winapi-x86_64-pc-windows-gnu" version = "0.4.0" @@ -2744,6 +2830,28 @@ dependencies = [ "memchr", ] +[[package]] +name = "x11rb" +version = "0.11.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "cdf3c79412dd91bae7a7366b8ad1565a85e35dd049affc3a6a2c549e97419617" +dependencies = [ + "gethostname", + "nix 0.25.1", + "winapi", + "winapi-wsapoll", + "x11rb-protocol", +] + +[[package]] +name = "x11rb-protocol" +version = "0.11.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e0b1513b141123073ce54d5bb1d33f801f17508fbd61e02060b1214e96d39c56" +dependencies = [ + "nix 0.25.1", +] + [[package]] name = "xkbcommon" version = "0.5.1" diff --git a/Cargo.toml b/Cargo.toml index e14e6f5..2b590be 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -70,7 +70,13 @@ version = "0.16.7" git = "https://github.com/smithay/smithay.git" # Until we get stereokit to understand OES samplers and external textures # path = "../smithay" default-features = false -features = ["desktop", "backend_drm", "renderer_gl", "wayland_frontend"] +features = [ + "desktop", + "backend_drm", + "renderer_gl", + "wayland_frontend", + "xwayland", +] version = "*" optional = true diff --git a/src/main.rs b/src/main.rs index bd3067d..7964ea6 100644 --- a/src/main.rs +++ b/src/main.rs @@ -205,6 +205,7 @@ fn main() { std::env::var_os("WAYLAND_DISPLAY").unwrap_or_default(), ) .env("WAYLAND_DISPLAY", &wayland.socket_name) + .env("DISPLAY", format!(":{}", wayland.xwayland_state.display)) .env( "STARDUST_INSTANCE", event_loop_info diff --git a/src/nodes/startup.rs b/src/nodes/startup.rs index 7666f08..111b756 100644 --- a/src/nodes/startup.rs +++ b/src/nodes/startup.rs @@ -1,4 +1,8 @@ -use crate::{core::client::Client, wayland::WAYLAND_DISPLAY, STARDUST_INSTANCE}; +use crate::{ + core::client::Client, + wayland::{xwayland::DISPLAY, WAYLAND_DISPLAY}, + STARDUST_INSTANCE, +}; use super::{ items::{ItemAcceptor, TypeInfo}, @@ -135,6 +139,7 @@ pub fn get_connection_environment_flex( #[cfg(feature = "wayland")] { var_env_insert!(env, WAYLAND_DISPLAY); + var_env_insert!(env, DISPLAY); env.insert("GDK_BACKEND".to_string(), "wayland".to_string()); env.insert("QT_QPA_PLATFORM".to_string(), "wayland".to_string()); env.insert("MOZ_ENABLE_WAYLAND".to_string(), "1".to_string()); diff --git a/src/wayland/mod.rs b/src/wayland/mod.rs index 892322f..c594d5e 100644 --- a/src/wayland/mod.rs +++ b/src/wayland/mod.rs @@ -8,7 +8,9 @@ mod state; mod surface; // mod xdg_activation; mod xdg_shell; +pub mod xwayland; +use self::xwayland::XWaylandState; use self::{state::WaylandState, surface::CORE_SURFACES}; use crate::{core::task, wayland::state::ClientState}; use color_eyre::eyre::{ensure, Result}; @@ -67,7 +69,8 @@ pub struct Wayland { join_handle: JoinHandle>, renderer: GlesRenderer, dmabuf_rx: UnboundedReceiver, - state: Arc>, + wayland_state: Arc>, + pub xwayland_state: XWaylandState, } impl Wayland { pub fn new() -> Result { @@ -85,7 +88,8 @@ impl Wayland { let (dmabuf_tx, dmabuf_rx) = mpsc::unbounded_channel(); let display = Arc::new(Mutex::new(display)); - let state = WaylandState::new(display.clone(), display_handle, &renderer, dmabuf_tx); + let xwayland_state = XWaylandState::create(&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); GLOBAL_DESTROY_QUEUE.set(global_destroy_queue_in).unwrap(); @@ -97,8 +101,12 @@ impl Wayland { .expect("seriously message nova this time they screwed up big time"); info!(socket_name, "Wayland active"); - let join_handle = - Wayland::start_loop(display.clone(), socket, state.clone(), global_destroy_queue)?; + let join_handle = Wayland::start_loop( + display.clone(), + socket, + wayland_state.clone(), + global_destroy_queue, + )?; Ok(Wayland { display, @@ -106,7 +114,8 @@ impl Wayland { join_handle, renderer, dmabuf_rx, - state, + wayland_state, + xwayland_state, }) } @@ -167,7 +176,7 @@ impl Wayland { } pub fn frame_event(&self, sk: &impl StereoKitDraw) { - let state = self.state.lock(); + let state = self.wayland_state.lock(); for core_surface in CORE_SURFACES.get_valid_contents() { core_surface.frame(sk, state.output.clone()); diff --git a/src/wayland/xwayland.rs b/src/wayland/xwayland.rs new file mode 100644 index 0000000..3fd2730 --- /dev/null +++ b/src/wayland/xwayland.rs @@ -0,0 +1,157 @@ +use super::seat::SeatData; +use color_eyre::eyre::Result; +use once_cell::sync::OnceCell; +use smithay::{ + reexports::{ + calloop::{self, EventLoop, LoopSignal}, + wayland_server::DisplayHandle, + x11rb::protocol::xproto::Window, + }, + utils::{Logical, Rectangle}, + xwayland::{ + xwm::{Reorder, ResizeEdge, XwmId}, + X11Surface, X11Wm, XWayland, XWaylandEvent, XwmHandler, + }, +}; +use std::{ffi::OsStr, iter::empty, sync::Arc, time::Duration}; +use tokio::{sync::oneshot, task::JoinHandle}; +use tracing::debug; + +pub static DISPLAY: OnceCell = OnceCell::new(); + +pub struct XWaylandState { + pub display: u32, + xwayland: XWayland, + event_loop_signal: LoopSignal, + event_loop_join: Option>>, +} +impl XWaylandState { + pub fn create(dh: &DisplayHandle) -> Result { + let dh = dh.clone(); + + let (tx, rx) = oneshot::channel(); + + let event_loop_join = tokio::task::spawn_blocking(move || { + let mut event_loop: EventLoop = EventLoop::try_new()?; + let (xwayland, connection) = XWayland::new(&dh); + let handle = event_loop.handle(); + event_loop + .handle() + .insert_source(connection, move |event, _, handler| match event { + XWaylandEvent::Ready { + connection, + client, + client_fd: _, + display: _, + } => { + handler.seat = Some(SeatData::new(&dh, client.id())); + handler.wm = + X11Wm::start_wm(handle.clone(), dh.clone(), connection, client).ok(); + } + XWaylandEvent::Exited => (), + }) + .map_err(|e| e.error)?; + + let display = xwayland.start( + event_loop.handle(), + None, + empty::<(&OsStr, &OsStr)>(), + true, + move |_| (), + )?; + let _ = tx.send(XWaylandState { + display, + xwayland, + event_loop_signal: event_loop.get_signal(), + event_loop_join: None, + }); + let mut handler = XWaylandHandler::default(); + event_loop.run(Duration::from_secs(60 * 60), &mut handler, |_| ()) + }); + + let mut state = rx.blocking_recv()?; + state.event_loop_join.replace(event_loop_join); + let _ = DISPLAY.set(format!(":{}", state.display)); + + Ok(state) + } +} +impl Drop for XWaylandState { + fn drop(&mut self) { + self.xwayland.shutdown(); + self.event_loop_signal.stop(); + } +} + +#[derive(Default)] +struct XWaylandHandler { + wm: Option, + seat: Option>, +} + +impl XwmHandler for XWaylandHandler { + fn xwm_state(&mut self, _xwm: XwmId) -> &mut X11Wm { + self.wm.as_mut().unwrap() + } + + fn new_window(&mut self, _xwm: XwmId, window: X11Surface) { + debug!(?window, "New X window"); + } + + fn new_override_redirect_window(&mut self, _xwm: XwmId, window: X11Surface) { + debug!(?window, "New X override redirect window"); + } + + fn map_window_request(&mut self, _xwm: XwmId, window: X11Surface) { + debug!(?window, "X map window request"); + } + + fn mapped_override_redirect_window(&mut self, _xwm: XwmId, window: X11Surface) { + debug!(?window, "X map override redirect window"); + } + + fn unmapped_window(&mut self, _xwm: XwmId, window: X11Surface) { + debug!(?window, "Unmap X window"); + } + + fn destroyed_window(&mut self, _xwm: XwmId, window: X11Surface) { + debug!(?window, "Destroy X window"); + } + + fn configure_request( + &mut self, + _xwm: XwmId, + window: X11Surface, + x: Option, + y: Option, + w: Option, + h: Option, + reorder: Option, + ) { + debug!(?window, x, y, w, h, ?reorder, "Configure X window"); + } + + fn configure_notify( + &mut self, + _xwm: XwmId, + window: X11Surface, + geometry: Rectangle, + above: Option, + ) { + debug!(?window, ?geometry, above, "Configure X window"); + } + + fn resize_request( + &mut self, + _xwm: XwmId, + window: X11Surface, + button: u32, + resize_edge: ResizeEdge, + ) { + debug!(?window, button, ?resize_edge, "X window requests resize"); + } + + fn move_request(&mut self, _xwm: XwmId, window: X11Surface, button: u32) { + debug!(?window, button, "X window requests move"); + } +}