From 1d2857408877c55b0fa0efc151bd5b04985b066d Mon Sep 17 00:00:00 2001 From: Victoria Brekenfeld Date: Wed, 18 Jan 2023 20:23:41 +0100 Subject: [PATCH] xwayland: Add initial support --- Cargo.lock | 24 +- Cargo.toml | 6 +- src/backend/kms/mod.rs | 3 + src/backend/kms/socket.rs | 12 +- src/backend/winit.rs | 3 + src/backend/x11.rs | 2 + src/main.rs | 6 +- src/shell/element/mod.rs | 10 + src/shell/element/surface.rs | 31 +- src/shell/element/window.rs | 102 +++++-- src/shell/grabs.rs | 17 ++ src/shell/layout/floating/mod.rs | 3 +- src/shell/layout/tiling/mod.rs | 3 +- src/shell/mod.rs | 170 ++++++++++- src/shell/workspace.rs | 35 ++- src/state.rs | 8 +- src/utils/iced.rs | 14 +- src/wayland/handlers/compositor.rs | 9 +- src/wayland/handlers/decoration.rs | 24 +- src/wayland/handlers/screencopy.rs | 10 +- src/wayland/handlers/xdg_shell/mod.rs | 101 +------ src/wayland/handlers/xdg_shell/popup.rs | 2 +- src/xwayland.rs | 371 ++++++++++++++++++++++++ 23 files changed, 781 insertions(+), 185 deletions(-) create mode 100644 src/xwayland.rs diff --git a/Cargo.lock b/Cargo.lock index 1d601894..07819f3b 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -1561,7 +1561,7 @@ dependencies = [ [[package]] name = "iced" version = "0.6.0" -source = "git+https://github.com/pop-os/libcosmic?branch=ci_add_without_shell#99842258b4d77dcb0657b7c299bae45579e139a1" +source = "git+https://github.com/pop-os/libcosmic#abf8fc96c2eb8c3ed25d90c4538e29c3a12029fb" dependencies = [ "iced_core", "iced_futures", @@ -1577,7 +1577,7 @@ dependencies = [ [[package]] name = "iced_core" version = "0.6.2" -source = "git+https://github.com/pop-os/libcosmic?branch=ci_add_without_shell#99842258b4d77dcb0657b7c299bae45579e139a1" +source = "git+https://github.com/pop-os/libcosmic#abf8fc96c2eb8c3ed25d90c4538e29c3a12029fb" dependencies = [ "bitflags", "palette", @@ -1587,7 +1587,7 @@ dependencies = [ [[package]] name = "iced_futures" version = "0.5.1" -source = "git+https://github.com/pop-os/libcosmic?branch=ci_add_without_shell#99842258b4d77dcb0657b7c299bae45579e139a1" +source = "git+https://github.com/pop-os/libcosmic#abf8fc96c2eb8c3ed25d90c4538e29c3a12029fb" dependencies = [ "futures", "log", @@ -1598,7 +1598,7 @@ dependencies = [ [[package]] name = "iced_glow" version = "0.5.1" -source = "git+https://github.com/pop-os/libcosmic?branch=ci_add_without_shell#99842258b4d77dcb0657b7c299bae45579e139a1" +source = "git+https://github.com/pop-os/libcosmic#abf8fc96c2eb8c3ed25d90c4538e29c3a12029fb" dependencies = [ "bytemuck", "euclid", @@ -1613,7 +1613,7 @@ dependencies = [ [[package]] name = "iced_graphics" version = "0.5.0" -source = "git+https://github.com/pop-os/libcosmic?branch=ci_add_without_shell#99842258b4d77dcb0657b7c299bae45579e139a1" +source = "git+https://github.com/pop-os/libcosmic#abf8fc96c2eb8c3ed25d90c4538e29c3a12029fb" dependencies = [ "bitflags", "bytemuck", @@ -1633,7 +1633,7 @@ dependencies = [ [[package]] name = "iced_lazy" version = "0.3.0" -source = "git+https://github.com/pop-os/libcosmic?branch=ci_add_without_shell#99842258b4d77dcb0657b7c299bae45579e139a1" +source = "git+https://github.com/pop-os/libcosmic#abf8fc96c2eb8c3ed25d90c4538e29c3a12029fb" dependencies = [ "iced_native", "ouroboros 0.13.0", @@ -1642,7 +1642,7 @@ dependencies = [ [[package]] name = "iced_native" version = "0.7.0" -source = "git+https://github.com/pop-os/libcosmic?branch=ci_add_without_shell#99842258b4d77dcb0657b7c299bae45579e139a1" +source = "git+https://github.com/pop-os/libcosmic#abf8fc96c2eb8c3ed25d90c4538e29c3a12029fb" dependencies = [ "iced_core", "iced_futures", @@ -1655,7 +1655,7 @@ dependencies = [ [[package]] name = "iced_softbuffer" version = "0.1.0" -source = "git+https://github.com/pop-os/libcosmic?branch=ci_add_without_shell#99842258b4d77dcb0657b7c299bae45579e139a1" +source = "git+https://github.com/pop-os/libcosmic#abf8fc96c2eb8c3ed25d90c4538e29c3a12029fb" dependencies = [ "cosmic-text", "iced_graphics", @@ -1670,7 +1670,7 @@ dependencies = [ [[package]] name = "iced_style" version = "0.5.1" -source = "git+https://github.com/pop-os/libcosmic?branch=ci_add_without_shell#99842258b4d77dcb0657b7c299bae45579e139a1" +source = "git+https://github.com/pop-os/libcosmic#abf8fc96c2eb8c3ed25d90c4538e29c3a12029fb" dependencies = [ "iced_core", "once_cell", @@ -1680,7 +1680,7 @@ dependencies = [ [[package]] name = "iced_wgpu" version = "0.7.0" -source = "git+https://github.com/pop-os/libcosmic?branch=ci_add_without_shell#99842258b4d77dcb0657b7c299bae45579e139a1" +source = "git+https://github.com/pop-os/libcosmic#abf8fc96c2eb8c3ed25d90c4538e29c3a12029fb" dependencies = [ "bitflags", "bytemuck", @@ -1882,7 +1882,7 @@ checksum = "201de327520df007757c1f0adce6e827fe8562fbc28bfd9c15571c66ca1f5f79" [[package]] name = "libcosmic" version = "0.1.0" -source = "git+https://github.com/pop-os/libcosmic?branch=ci_add_without_shell#99842258b4d77dcb0657b7c299bae45579e139a1" +source = "git+https://github.com/pop-os/libcosmic#abf8fc96c2eb8c3ed25d90c4538e29c3a12029fb" dependencies = [ "apply", "cosmic-theme", @@ -3273,7 +3273,7 @@ checksum = "a507befe795404456341dfab10cef66ead4c041f62b8b11bbb92bffe5d0953e0" [[package]] name = "smithay" version = "0.3.0" -source = "git+https://github.com/pop-os/smithay?rev=2a05d8cd64#2a05d8cd648bc9689491ff5a996fd7713f23aa77" +source = "git+https://github.com/pop-os/smithay?rev=b6742bfc5d#b6742bfc5d164826d9a6584a62e6012b778081ec" dependencies = [ "appendlist", "bitflags", diff --git a/Cargo.toml b/Cargo.toml index d9cec7cd..70c2edc3 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -38,8 +38,8 @@ libsystemd = "0.5" wayland-backend = "0.1.0" wayland-scanner = "0.30.0" cosmic-protocols = { git = "https://github.com/pop-os/cosmic-protocols", branch = "main", default-features = false, features = ["server"] } -libcosmic = { git = "https://github.com/pop-os/libcosmic", branch = "ci_add_without_shell", default-features = false, features = ["softbuffer"] } -iced_softbuffer = { git = "https://github.com/pop-os/libcosmic", branch = "ci_add_without_shell" } +libcosmic = { git = "https://github.com/pop-os/libcosmic", ref = "abf8fc96c", default-features = false, features = ["softbuffer"] } +iced_softbuffer = { git = "https://github.com/pop-os/libcosmic", ref = "abf8fc96c" } ordered-float = "3.0" [dependencies.smithay] @@ -70,4 +70,4 @@ debug = true lto = "fat" [patch."https://github.com/Smithay/smithay.git"] -smithay = { git = "https://github.com/pop-os/smithay", rev = "2a05d8cd64" } +smithay = { git = "https://github.com/pop-os/smithay", rev = "b6742bfc5d" } diff --git a/src/backend/kms/mod.rs b/src/backend/kms/mod.rs index bd8680f7..512d2347 100644 --- a/src/backend/kms/mod.rs +++ b/src/backend/kms/mod.rs @@ -301,6 +301,9 @@ pub fn init_backend( devices: HashMap::new(), }); + // TODO: Do multiple Xwaylands for better multigpu + state.launch_xwayland(Some(primary)); + for (dev, path) in udev_dispatcher.as_source_ref().device_list() { state .device_added(dev, path.into(), dh) diff --git a/src/backend/kms/socket.rs b/src/backend/kms/socket.rs index 158d477c..f7143899 100644 --- a/src/backend/kms/socket.rs +++ b/src/backend/kms/socket.rs @@ -11,6 +11,7 @@ use smithay::{ wayland_server::{backend::GlobalId, Client, DisplayHandle}, }, wayland::{dmabuf::DmabufGlobal, socket::ListeningSocketSource}, + xwayland::XWaylandClientData, }; use std::sync::Arc; @@ -46,8 +47,15 @@ impl State { // initialize globals let filter = move |client: &Client| { - let dev_id = client.get_data::().unwrap().drm_node.unwrap(); - dev_id == render_node + if let Some(normal_client) = client.get_data::() { + let dev_id = normal_client.drm_node.unwrap(); + return dev_id == render_node; + } + if let Some(xwayland_client) = client.get_data::() { + let dev_id = xwayland_client.user_data().get::().unwrap(); + return *dev_id == render_node; + } + false }; let dmabuf_global = self diff --git a/src/backend/winit.rs b/src/backend/winit.rs index 0a211a42..92a000d9 100644 --- a/src/backend/winit.rs +++ b/src/backend/winit.rs @@ -241,6 +241,7 @@ pub fn init_backend( #[cfg(feature = "debug")] fps, }); + state .common .output_configuration_state @@ -255,6 +256,8 @@ pub fn init_backend( &state.common.event_loop_handle, ); + state.launch_xwayland(None); + Ok(()) } diff --git a/src/backend/x11.rs b/src/backend/x11.rs index 5d08be56..3303ef82 100644 --- a/src/backend/x11.rs +++ b/src/backend/x11.rs @@ -305,6 +305,8 @@ pub fn init_backend( &state.common.event_loop_handle, ); + state.launch_xwayland(None); + event_loop .handle() .insert_source(backend, move |event, _, data| match event { diff --git a/src/main.rs b/src/main.rs index 44389877..ecc7f2b4 100644 --- a/src/main.rs +++ b/src/main.rs @@ -13,6 +13,8 @@ use std::{ffi::OsString, os::unix::prelude::AsRawFd, sync::Arc}; pub mod backend; pub mod config; +#[cfg(feature = "debug")] +pub mod debug; pub mod input; mod logger; pub mod session; @@ -21,9 +23,7 @@ pub mod state; pub mod systemd; pub mod utils; pub mod wayland; - -#[cfg(feature = "debug")] -pub mod debug; +pub mod xwayland; fn main() -> Result<()> { // setup logger diff --git a/src/shell/element/mod.rs b/src/shell/element/mod.rs index 1b87dee5..648c044b 100644 --- a/src/shell/element/mod.rs +++ b/src/shell/element/mod.rs @@ -29,6 +29,7 @@ use smithay::{ compositor::{with_surface_tree_downward, TraversalAction}, seat::WaylandFocus, }, + xwayland::{xwm::X11Relatable, X11Surface}, }; use std::{ collections::HashMap, @@ -483,6 +484,15 @@ impl SpaceElement for CosmicMapped { } } +impl X11Relatable for CosmicMapped { + fn is_window(&self, window: &X11Surface) -> bool { + match self.active_window() { + CosmicSurface::X11(surface) => &surface == window, + _ => false, + } + } +} + impl KeyboardTarget for CosmicMapped { fn enter( &self, diff --git a/src/shell/element/surface.rs b/src/shell/element/surface.rs index 2f919124..760baebc 100644 --- a/src/shell/element/surface.rs +++ b/src/shell/element/surface.rs @@ -29,9 +29,9 @@ use smithay::{ wayland::{ compositor::{with_states, SurfaceData}, seat::WaylandFocus, - shell::xdg::XdgToplevelSurfaceData, + shell::xdg::{ToplevelSurface, XdgToplevelSurfaceData}, }, - xwayland::X11Surface, + xwayland::{xwm::X11Relatable, X11Surface}, }; space_elements! { @@ -41,6 +41,24 @@ space_elements! { X11=X11Surface, } +impl From for CosmicSurface { + fn from(s: ToplevelSurface) -> Self { + CosmicSurface::Wayland(Window::new(s)) + } +} + +impl From for CosmicSurface { + fn from(w: Window) -> Self { + CosmicSurface::Wayland(w) + } +} + +impl From for CosmicSurface { + fn from(s: X11Surface) -> Self { + CosmicSurface::X11(s) + } +} + pub const SSD_HEIGHT: i32 = 48; impl CosmicSurface { @@ -577,3 +595,12 @@ where } } } + +impl X11Relatable for CosmicSurface { + fn is_window(&self, window: &X11Surface) -> bool { + match self { + CosmicSurface::X11(surface) => surface == window, + _ => false, + } + } +} diff --git a/src/shell/element/window.rs b/src/shell/element/window.rs index 72e15a08..c382046e 100644 --- a/src/shell/element/window.rs +++ b/src/shell/element/window.rs @@ -1,4 +1,5 @@ use crate::{ + shell::Shell, state::State, utils::{ iced::{IcedElement, Program}, @@ -30,13 +31,14 @@ use smithay::{ output::Output, render_elements, utils::{IsAlive, Logical, Physical, Point, Rectangle, Scale, Serial, Size}, + wayland::seat::WaylandFocus, }; use std::{ fmt, hash::Hash, sync::{ atomic::{AtomicU8, Ordering}, - Arc, + Arc, Mutex, }, }; @@ -55,10 +57,23 @@ impl fmt::Debug for CosmicWindow { } } -#[derive(Debug, Clone)] +#[derive(Clone)] pub struct CosmicWindowInternal { pub(super) window: CosmicSurface, + /// TODO: This needs to be per seat pointer_entered: Arc, + last_seat: Arc, Serial)>>>, +} + +impl fmt::Debug for CosmicWindowInternal { + fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { + f.debug_struct("CosmicWindowInternal") + .field("window", &self.window) + .field("pointer_entered", &self.pointer_entered) + // skip seat to avoid loop + .field("last_seat", &"...") + .finish() + } } #[repr(u8)] @@ -98,6 +113,7 @@ impl CosmicWindow { CosmicWindowInternal { window, pointer_entered: Arc::new(AtomicU8::new(Focus::None as u8)), + last_seat: Arc::new(Mutex::new(None)), }, (width, SSD_HEIGHT), handle, @@ -136,18 +152,49 @@ pub enum Message { impl Program for CosmicWindowInternal { type Message = Message; - fn update(&mut self, message: Self::Message) -> Command { - /* + fn update( + &mut self, + message: Self::Message, + loop_handle: &LoopHandle<'static, crate::state::Data>, + ) -> Command { match message { - Message::DragStart => match &self.window { - CosmicWindowSurface::Wayland(window) => self - .with_program(|internal| internal.loop_handle()) - .insert_idle(|data| {}), - CosmicWindowSurface::X11(surface) => {} - _ => unreachable!(), - }, + Message::DragStart => { + if let Some((seat, serial)) = self.last_seat.lock().unwrap().clone() { + if let Some(surface) = self.window.wl_surface() { + loop_handle.insert_idle(move |data| { + Shell::move_request(&mut data.state, &surface, &seat, serial); + }); + } + } + } + Message::Maximize => { + if let Some((seat, _serial)) = self.last_seat.lock().unwrap().clone() { + if let Some(surface) = self.window.wl_surface() { + loop_handle.insert_idle(move |data| { + if let Some(mapped) = data + .state + .common + .shell + .element_for_wl_surface(&surface) + .cloned() + { + if let Some(workspace) = + data.state.common.shell.space_for_mut(&mapped) + { + let output = seat.active_output(); + let (window, _) = mapped + .windows() + .find(|(w, _)| w.wl_surface().as_ref() == Some(&surface)) + .unwrap(); + workspace.maximize_request(&window, &output) + } + } + }); + } + } + } + Message::Close => self.window.close(), } - */ Command::none() } @@ -155,21 +202,23 @@ impl Program for CosmicWindowInternal { let radius = 8.; let (w, h) = (target.width() as f32, target.height() as f32); - let mut pb = PathBuilder::new(); - pb.move_to(0., h); // lower-left + if !(self.window.is_maximized() || self.window.is_fullscreen()) { + let mut pb = PathBuilder::new(); + pb.move_to(0., h); // lower-left - // upper-left rounded corner - pb.line_to(0., radius); - pb.quad_to(0., 0., radius, 0.); + // upper-left rounded corner + pb.line_to(0., radius); + pb.quad_to(0., 0., radius, 0.); - // upper-right rounded corner - pb.line_to(w - radius, 0.); - pb.quad_to(w, 0., w, radius); + // upper-right rounded corner + pb.line_to(w - radius, 0.); + pb.quad_to(w, 0., w, radius); - pb.line_to(w, h); // lower-right + pb.line_to(w, h); // lower-right - let path = pb.finish(); - target.push_clip(&path); + let path = pb.finish(); + target.push_clip(&path); + } if self.window.is_activated() { target.clear(SolidSource::from_unpremultiplied_argb(u8::MAX, 30, 30, 30)); @@ -387,7 +436,12 @@ impl PointerTarget for CosmicWindow { fn button(&self, seat: &Seat, data: &mut State, event: &ButtonEvent) { match self.0.with_program(|p| p.current_focus()) { - Focus::Header => PointerTarget::button(&self.0, seat, data, event), + Focus::Header => { + self.0.with_program(|p| { + *p.last_seat.lock().unwrap() = Some((seat.clone(), event.serial)); + }); + PointerTarget::button(&self.0, seat, data, event) + } Focus::Window => self .0 .with_program(|p| PointerTarget::button(&p.window, seat, data, event)), diff --git a/src/shell/grabs.rs b/src/shell/grabs.rs index ac5f6877..6531bc0c 100644 --- a/src/shell/grabs.rs +++ b/src/shell/grabs.rs @@ -5,6 +5,7 @@ use smithay::{ }, reexports::wayland_protocols::xdg::shell::server::xdg_toplevel, utils::{Logical, Point}, + xwayland::xwm, }; use crate::state::State; @@ -43,6 +44,22 @@ impl From for xdg_toplevel::ResizeEdge { } } +impl From for ResizeEdge { + #[inline] + fn from(x: xwm::ResizeEdge) -> Self { + match x { + xwm::ResizeEdge::Top => ResizeEdge::TOP, + xwm::ResizeEdge::Bottom => ResizeEdge::BOTTOM, + xwm::ResizeEdge::Left => ResizeEdge::LEFT, + xwm::ResizeEdge::Right => ResizeEdge::RIGHT, + xwm::ResizeEdge::TopLeft => ResizeEdge::TOP_LEFT, + xwm::ResizeEdge::BottomLeft => ResizeEdge::BOTTOM_LEFT, + xwm::ResizeEdge::TopRight => ResizeEdge::TOP_RIGHT, + xwm::ResizeEdge::BottomRight => ResizeEdge::BOTTOM_RIGHT, + } + } +} + pub enum ResizeGrab { Floating(ResizeSurfaceGrab), Tiling(ResizeForkGrab), diff --git a/src/shell/layout/floating/mod.rs b/src/shell/layout/floating/mod.rs index c09f4ac9..e197de9a 100644 --- a/src/shell/layout/floating/mod.rs +++ b/src/shell/layout/floating/mod.rs @@ -5,7 +5,7 @@ use smithay::{ desktop::{layer_map_for_output, space::SpaceElement, Space}, input::{pointer::GrabStartData as PointerGrabStartData, Seat}, output::Output, - utils::{Logical, Point, Rectangle, Serial, Size}, + utils::{Logical, Point, Rectangle, Size}, }; use std::collections::HashMap; @@ -196,7 +196,6 @@ impl FloatingLayout { &mut self, mapped: &CosmicMapped, seat: &Seat, - _serial: Serial, start_data: PointerGrabStartData, edges: ResizeEdge, ) -> Option { diff --git a/src/shell/layout/tiling/mod.rs b/src/shell/layout/tiling/mod.rs index 99dde897..d35a7908 100644 --- a/src/shell/layout/tiling/mod.rs +++ b/src/shell/layout/tiling/mod.rs @@ -25,7 +25,7 @@ use smithay::{ desktop::{layer_map_for_output, space::SpaceElement, PopupKind}, input::{pointer::GrabStartData as PointerGrabStartData, Seat}, output::Output, - utils::{IsAlive, Logical, Point, Rectangle, Scale, Serial}, + utils::{IsAlive, Logical, Point, Rectangle, Scale}, wayland::seat::WaylandFocus, }; use std::{borrow::Borrow, collections::HashMap, hash::Hash, sync::Arc}; @@ -928,7 +928,6 @@ impl TilingLayout { &self, mapped: &CosmicMapped, _seat: &Seat, - _serial: Serial, start_data: PointerGrabStartData, edges: ResizeEdge, ) -> Option { diff --git a/src/shell/mod.rs b/src/shell/mod.rs index d734a903..47e7b7e0 100644 --- a/src/shell/mod.rs +++ b/src/shell/mod.rs @@ -4,12 +4,19 @@ use std::{cell::RefCell, collections::HashMap}; use cosmic_protocols::workspace::v1::server::zcosmic_workspace_handle_v1::State as WState; use smithay::{ desktop::{layer_map_for_output, LayerSurface, PopupManager, WindowSurfaceType}, - input::Seat, + input::{ + pointer::{Focus, GrabStartData as PointerGrabStartData}, + Seat, + }, output::Output, - reexports::wayland_server::{protocol::wl_surface::WlSurface, DisplayHandle}, - utils::{Logical, Point, Rectangle}, + reexports::{ + wayland_server::{protocol::wl_surface::WlSurface, DisplayHandle}, + x11rb::protocol::xproto::Window as X11Window, + }, + utils::{Logical, Point, Rectangle, Serial, SERIAL_COUNTER}, wayland::{ compositor::with_states, + seat::WaylandFocus, shell::{ wlr_layer::{ KeyboardInteractivity, Layer, LayerSurfaceCachedState, WlrLayerShellState, @@ -17,6 +24,7 @@ use smithay::{ xdg::XdgShellState, }, }, + xwayland::X11Surface, }; use crate::{ @@ -42,6 +50,7 @@ pub use self::workspace::*; use self::{ element::CosmicWindow, focus::target::KeyboardFocusTarget, + grabs::ResizeEdge, layout::{floating::FloatingLayout, tiling::TilingLayout}, }; @@ -52,6 +61,7 @@ pub struct Shell { pub floating_default: bool, pub pending_windows: Vec<(CosmicSurface, Seat)>, pub pending_layers: Vec<(LayerSurface, Output, Seat)>, + pub override_redirect_windows: Vec, // wayland_state pub layer_shell_state: WlrLayerShellState, @@ -61,6 +71,11 @@ pub struct Shell { pub workspace_state: WorkspaceState, } +pub struct OverrideRedirectWindow { + pub surface: X11Surface, + pub above: Option, +} + #[derive(Debug)] pub struct WorkspaceSet { active: usize, @@ -431,6 +446,7 @@ impl Shell { pending_windows: Vec::new(), pending_layers: Vec::new(), + override_redirect_windows: Vec::new(), layer_shell_state, toplevel_info_state, @@ -871,12 +887,18 @@ impl Shell { .into_iter() } - pub fn element_for_surface(&self, surface: &WlSurface) -> Option<&CosmicMapped> { + pub fn element_for_surface(&self, surface: &CosmicSurface) -> Option<&CosmicMapped> { self.workspaces .spaces() .find_map(|w| w.element_for_surface(surface)) } + pub fn element_for_wl_surface(&self, surface: &WlSurface) -> Option<&CosmicMapped> { + self.workspaces + .spaces() + .find_map(|w| w.element_for_wl_surface(surface)) + } + pub fn space_for(&self, mapped: &CosmicMapped) -> Option<&Workspace> { self.workspaces .spaces() @@ -996,6 +1018,24 @@ impl Shell { .map(mapped.clone(), &seat, focus_stack.iter()); } + if let CosmicSurface::X11(surface) = window { + let geometry = workspace.element_geometry(&mapped); + if let Err(err) = surface.configure(geometry) { + slog_scope::warn!("Failed to configure X11 surface ({:?}): {}", surface, err); + }; + if let Some(xwm) = state + .common + .xwayland_state + .values_mut() + .flat_map(|state| state.xwm.as_mut()) + .find(|xwm| Some(xwm.id()) == surface.xwm_id()) + { + if let Err(err) = xwm.update_stacking_order_downwards(workspace.mapped()) { + slog_scope::warn!("Failed to update Xwayland stacking order: {}", err); + } + } + } + Shell::set_focus(state, Some(&KeyboardFocusTarget::from(mapped)), &seat, None); let active_space = state.common.shell.active_space(output); @@ -1004,6 +1044,28 @@ impl Shell { } } + pub fn map_override_redirect(state: &mut State, window: X11Surface) { + let seat = state.common.last_active_seat().clone(); + let mut pos = window.geometry().loc; + let output = state + .common + .shell + .outputs() + .find(|o| o.geometry().contains(pos)) + .cloned() + .unwrap_or_else(|| seat.active_output()); + pos -= output.geometry().loc; + + state + .common + .shell + .override_redirect_windows + .push(OverrideRedirectWindow { + surface: window, + above: None, + }); + } + pub fn map_layer(state: &mut State, layer_surface: &LayerSurface) { let pos = state .common @@ -1125,6 +1187,71 @@ impl Shell { } } } + + pub fn move_request( + state: &mut State, + surface: &WlSurface, + seat: &Seat, + serial: impl Into>, + ) { + let serial = serial.into(); + if let Some(start_data) = check_grab_preconditions(&seat, surface, serial) { + if let Some(mapped) = state.common.shell.element_for_wl_surface(surface).cloned() { + if let Some(workspace) = state.common.shell.space_for_mut(&mapped) { + let output = seat.active_output(); + let (window, _) = mapped + .windows() + .find(|(w, _)| w.wl_surface().as_ref() == Some(surface)) + .unwrap(); + if let Some(grab) = workspace.move_request(&window, &seat, &output, start_data) + { + let handle = workspace.handle; + state + .common + .shell + .toplevel_info_state + .toplevel_leave_workspace(&window, &handle); + state + .common + .shell + .toplevel_info_state + .toplevel_leave_output(&window, &output); + seat.get_pointer().unwrap().set_grab( + state, + grab, + serial.unwrap_or_else(|| SERIAL_COUNTER.next_serial()), + Focus::Clear, + ); + } + } + } + } + } + + pub fn resize_request( + state: &mut State, + surface: &WlSurface, + seat: &Seat, + serial: impl Into>, + edges: ResizeEdge, + ) { + let serial = serial.into(); + if let Some(start_data) = check_grab_preconditions(&seat, surface, serial) { + if let Some(mapped) = state.common.shell.element_for_wl_surface(surface).cloned() { + if let Some(workspace) = state.common.shell.space_for_mut(&mapped) { + if let Some(grab) = workspace.resize_request(&mapped, &seat, start_data, edges) + { + seat.get_pointer().unwrap().set_grab( + state, + grab, + serial.unwrap_or_else(|| SERIAL_COUNTER.next_serial()), + Focus::Clear, + ); + } + } + } + } + } } fn workspace_set_idx<'a>( @@ -1136,3 +1263,38 @@ fn workspace_set_idx<'a>( state.set_workspace_name(&handle, format!("{}", idx)); state.set_workspace_coordinates(&handle, [Some(idx as u32), Some(output_pos as u32), None]); } + +pub fn check_grab_preconditions( + seat: &Seat, + surface: &WlSurface, + serial: Option, +) -> Option> { + use smithay::reexports::wayland_server::Resource; + + // TODO: touch resize. + let pointer = seat.get_pointer().unwrap(); + + // Check that this surface has a click grab. + if !match serial { + Some(serial) => pointer.has_grab(serial), + None => pointer.is_grabbed(), + } { + return None; + } + + let start_data = pointer.grab_start_data().unwrap(); + + // If the focus was for a different surface, ignore the request. + if start_data.focus.is_none() + || !start_data + .focus + .as_ref() + .unwrap() + .0 + .same_client_as(&surface.id()) + { + return None; + } + + Some(start_data) +} diff --git a/src/shell/workspace.rs b/src/shell/workspace.rs index 3a010cbe..6a36a9fe 100644 --- a/src/shell/workspace.rs +++ b/src/shell/workspace.rs @@ -24,13 +24,8 @@ use smithay::{ desktop::{layer_map_for_output, space::SpaceElement, LayerSurface}, input::{pointer::GrabStartData as PointerGrabStartData, Seat}, output::Output, - reexports::{ - wayland_protocols::xdg::shell::server::xdg_toplevel::ResizeEdge, - wayland_server::protocol::wl_surface::WlSurface, - }, - utils::{ - Buffer as BufferCoords, IsAlive, Logical, Physical, Point, Rectangle, Scale, Serial, Size, - }, + reexports::wayland_server::protocol::wl_surface::WlSurface, + utils::{Buffer as BufferCoords, IsAlive, Logical, Physical, Point, Rectangle, Scale, Size}, wayland::{seat::WaylandFocus, shell::wlr_layer::Layer}, }; use std::collections::HashMap; @@ -38,7 +33,7 @@ use std::collections::HashMap; use super::{ element::CosmicMapped, focus::{FocusStack, FocusStackMut}, - grabs::ResizeGrab, + grabs::{ResizeEdge, ResizeGrab}, CosmicMappedRenderElement, CosmicSurface, }; @@ -84,7 +79,7 @@ impl Workspace { } pub fn commit(&mut self, surface: &WlSurface) { - if let Some(mapped) = self.element_for_surface(surface) { + if let Some(mapped) = self.element_for_wl_surface(surface) { mapped .windows() .find(|(w, _)| w.wl_surface().as_ref() == Some(surface)) @@ -127,7 +122,14 @@ impl Workspace { } } - pub fn element_for_surface(&self, surface: &WlSurface) -> Option<&CosmicMapped> { + pub fn element_for_surface(&self, surface: &CosmicSurface) -> Option<&CosmicMapped> { + self.floating_layer + .mapped() + .chain(self.tiling_layer.mapped().map(|(_, w, _)| w)) + .find(|e| e.windows().any(|(w, _)| &w == surface)) + } + + pub fn element_for_wl_surface(&self, surface: &WlSurface) -> Option<&CosmicMapped> { self.floating_layer .mapped() .chain(self.tiling_layer.mapped().map(|(_, w, _)| w)) @@ -270,7 +272,6 @@ impl Workspace { &mut self, mapped: &CosmicMapped, seat: &Seat, - serial: Serial, start_data: PointerGrabStartData, edges: ResizeEdge, ) -> Option { @@ -278,14 +279,13 @@ impl Workspace { return None; } - let edges = edges.into(); if self.floating_layer.mapped().any(|m| m == mapped) { self.floating_layer - .resize_request(mapped, seat, serial, start_data.clone(), edges) + .resize_request(mapped, seat, start_data.clone(), edges) .map(Into::into) } else if self.tiling_layer.mapped().any(|(_, m, _)| m == mapped) { self.tiling_layer - .resize_request(mapped, seat, serial, start_data, edges) + .resize_request(mapped, seat, start_data, edges) .map(Into::into) } else { None @@ -297,13 +297,12 @@ impl Workspace { window: &CosmicSurface, seat: &Seat, output: &Output, - _serial: Serial, start_data: PointerGrabStartData, ) -> Option { let pointer = seat.get_pointer().unwrap(); let pos = pointer.current_location(); - let mapped = self.element_for_surface(&window.wl_surface()?)?.clone(); + let mapped = self.element_for_surface(&window)?.clone(); let mut initial_window_location = self.element_geometry(&mapped).unwrap().loc; if mapped.is_fullscreen() || mapped.is_maximized() { @@ -390,6 +389,10 @@ impl Workspace { .chain(self.tiling_layer.windows().map(|(_, w, _)| w)) } + pub fn is_floating(&self, mapped: &CosmicMapped) -> bool { + self.floating_layer.mapped().any(|m| m == mapped) + } + pub fn render_output( &self, renderer: &mut R, diff --git a/src/state.rs b/src/state.rs index e4cab952..2d9f32ff 100644 --- a/src/state.rs +++ b/src/state.rs @@ -11,6 +11,7 @@ use crate::{ screencopy::{BufferParams, ScreencopyState, Session as ScreencopySession}, workspace::WorkspaceClientState, }, + xwayland::XWaylandState, }; use cosmic_protocols::screencopy::v1::server::zcosmic_screencopy_manager_v1::CursorMode; use smithay::{ @@ -51,7 +52,7 @@ use smithay::{ }, }; -use std::{cell::RefCell, ffi::OsString, time::Duration}; +use std::{cell::RefCell, collections::HashMap, ffi::OsString, time::Duration}; use std::{collections::VecDeque, time::Instant}; pub struct ClientState { @@ -110,6 +111,9 @@ pub struct Common { pub viewporter_state: ViewporterState, pub kde_decoration_state: KdeDecorationState, pub xdg_decoration_state: XdgDecorationState, + + // xwayland state + pub xwayland_state: HashMap, XWaylandState>, } pub enum BackendData { @@ -277,6 +281,8 @@ impl State { wl_drm_state, kde_decoration_state, xdg_decoration_state, + + xwayland_state: HashMap::new(), }, backend: BackendData::Unset, } diff --git a/src/utils/iced.rs b/src/utils/iced.rs index 7da9132f..ac212ee3 100644 --- a/src/utils/iced.rs +++ b/src/utils/iced.rs @@ -77,8 +77,12 @@ impl Hash for IcedElement

{ pub trait Program { type Message: std::fmt::Debug + Send; - fn update(&mut self, message: Self::Message) -> Command { - let _ = message; + fn update( + &mut self, + message: Self::Message, + loop_handle: &LoopHandle<'static, crate::state::Data>, + ) -> Command { + let _ = (message, loop_handle); Command::none() } fn view(&self) -> Element<'_, Self::Message>; @@ -91,13 +95,13 @@ pub trait Program { } } -struct ProgramWrapper(P); +struct ProgramWrapper(P, LoopHandle<'static, crate::state::Data>); impl IcedProgram for ProgramWrapper

{ type Message =

::Message; type Renderer = IcedRenderer; fn update(&mut self, message: Self::Message) -> Command { - self.0.update(message) + self.0.update(message, &self.1) } fn view(&self) -> Element<'_, Self::Message> { @@ -161,7 +165,7 @@ impl IcedElement

{ let mut debug = Debug::new(); let state = State::new( - ProgramWrapper(program), + ProgramWrapper(program, handle.clone()), IcedSize::new(size.w as f32, size.h as f32), &mut renderer, &mut debug, diff --git a/src/wayland/handlers/compositor.rs b/src/wayland/handlers/compositor.rs index b8ede805..a188a55d 100644 --- a/src/wayland/handlers/compositor.rs +++ b/src/wayland/handlers/compositor.rs @@ -19,6 +19,7 @@ use smithay::{ }, }, }, + xwayland::X11Wm, }; use std::sync::Mutex; @@ -108,6 +109,7 @@ impl CompositorHandler for State { } fn commit(&mut self, surface: &WlSurface) { + X11Wm::commit_hook(surface); // first load the buffer for various smithay helper functions on_commit_buffer_handler(surface); @@ -134,10 +136,7 @@ impl CompositorHandler for State { return; } } - CosmicSurface::X11(_) => { - let output = seat.active_output(); - Shell::map_window(self, &window, &output); - } + CosmicSurface::X11(_) => {} _ => unreachable!(), } } @@ -164,7 +163,7 @@ impl CompositorHandler for State { // If we would re-position the window inside the grab we would get a weird jittery animation. // We only want to resize once the client has acknoledged & commited the new size, // so we need to carefully track the state through different handlers. - if let Some(element) = self.common.shell.element_for_surface(surface).cloned() { + if let Some(element) = self.common.shell.element_for_wl_surface(surface).cloned() { if let Some(workspace) = self.common.shell.space_for_mut(&element) { crate::shell::layout::floating::ResizeSurfaceGrab::apply_resize_to_location( element.clone(), diff --git a/src/wayland/handlers/decoration.rs b/src/wayland/handlers/decoration.rs index 4dff1650..94c8f8be 100644 --- a/src/wayland/handlers/decoration.rs +++ b/src/wayland/handlers/decoration.rs @@ -76,13 +76,21 @@ impl State { impl XdgDecorationHandler for State { fn new_decoration(&mut self, toplevel: ToplevelSurface) { - if let Some(mapped) = self.common.shell.element_for_surface(toplevel.wl_surface()) { + if let Some(mapped) = self + .common + .shell + .element_for_wl_surface(toplevel.wl_surface()) + { State::new_decoration(mapped, toplevel.wl_surface()); } } fn request_mode(&mut self, toplevel: ToplevelSurface, mode: XdgMode) { - if let Some(mapped) = self.common.shell.element_for_surface(toplevel.wl_surface()) { + if let Some(mapped) = self + .common + .shell + .element_for_wl_surface(toplevel.wl_surface()) + { State::request_mode(mapped, toplevel.wl_surface(), mode); } else { toplevel.with_pending_state(|state| state.decoration_mode = Some(mode)); @@ -90,7 +98,11 @@ impl XdgDecorationHandler for State { } fn unset_mode(&mut self, toplevel: ToplevelSurface) { - if let Some(mapped) = self.common.shell.element_for_surface(toplevel.wl_surface()) { + if let Some(mapped) = self + .common + .shell + .element_for_wl_surface(toplevel.wl_surface()) + { State::unset_mode(mapped, toplevel.wl_surface()) } } @@ -102,7 +114,7 @@ impl KdeDecorationHandler for State { } fn new_decoration(&mut self, surface: &WlSurface, decoration: &OrgKdeKwinServerDecoration) { - if let Some(mapped) = self.common.shell.element_for_surface(surface) { + if let Some(mapped) = self.common.shell.element_for_wl_surface(surface) { let mode = State::new_decoration(mapped, surface); decoration.mode(mode); } @@ -116,7 +128,7 @@ impl KdeDecorationHandler for State { ) { if let WEnum::Value(mode) = mode { // TODO: We need to store this value until it gets mapped and apply it then, if it is not mapped yet. - if let Some(mapped) = self.common.shell.element_for_surface(surface) { + if let Some(mapped) = self.common.shell.element_for_wl_surface(surface) { State::request_mode( mapped, surface, @@ -131,7 +143,7 @@ impl KdeDecorationHandler for State { } fn release(&mut self, _decoration: &OrgKdeKwinServerDecoration, surface: &WlSurface) { - if let Some(mapped) = self.common.shell.element_for_surface(surface) { + if let Some(mapped) = self.common.shell.element_for_wl_surface(surface) { State::unset_mode(mapped, surface) } } diff --git a/src/wayland/handlers/screencopy.rs b/src/wayland/handlers/screencopy.rs index 7ef0dd04..92f85f4a 100644 --- a/src/wayland/handlers/screencopy.rs +++ b/src/wayland/handlers/screencopy.rs @@ -890,10 +890,7 @@ pub fn render_window_to_buffer( for seat in common.seats() { if let Some(location) = { // we need to find the mapped element in that case - if let Some(mapped) = window - .wl_surface() - .and_then(|surf| common.shell.element_for_surface(&surf)) - { + if let Some(mapped) = common.shell.element_for_surface(&window) { mapped.cursor_position(seat).and_then(|mut p| { p -= mapped.active_window_offset().to_f64(); if p.x < 0. || p.y < 0. { @@ -1100,7 +1097,10 @@ impl UserdataExt for CosmicSurface { impl State { pub fn schedule_window_session(&mut self, surface: &WlSurface) { - if let Some(element) = self.common.shell.element_for_surface(surface).cloned() { + if let Some(element) = surface + .wl_surface() + .and_then(|surface| self.common.shell.element_for_wl_surface(&surface).cloned()) + { let active = element.active_window(); if active.wl_surface().as_ref() == Some(surface) { for (session, params) in active.pending_buffers() { diff --git a/src/wayland/handlers/xdg_shell/mod.rs b/src/wayland/handlers/xdg_shell/mod.rs index c0ca0851..b8ddf58e 100644 --- a/src/wayland/handlers/xdg_shell/mod.rs +++ b/src/wayland/handlers/xdg_shell/mod.rs @@ -7,14 +7,11 @@ use smithay::{ find_popup_root_surface, PopupGrab, PopupKeyboardGrab, PopupKind, PopupPointerGrab, PopupUngrabStrategy, Window, }, - input::{ - pointer::{Focus, GrabStartData as PointerGrabStartData}, - Seat, - }, + input::{pointer::Focus, Seat}, output::Output, reexports::{ wayland_protocols::xdg::shell::server::xdg_toplevel, - wayland_server::protocol::{wl_output::WlOutput, wl_seat::WlSeat, wl_surface::WlSurface}, + wayland_server::protocol::{wl_output::WlOutput, wl_seat::WlSeat}, }, utils::Serial, wayland::{ @@ -70,7 +67,7 @@ impl XdgShellHandler for State { let kind = PopupKind::Xdg(surface); if let Some(root) = find_popup_root_surface(&kind) .ok() - .and_then(|root| self.common.shell.element_for_surface(&root)) + .and_then(|root| self.common.shell.element_for_wl_surface(&root)) { let target = root.clone().into(); let ret = self @@ -138,38 +135,7 @@ impl XdgShellHandler for State { fn move_request(&mut self, surface: ToplevelSurface, seat: WlSeat, serial: Serial) { let seat = Seat::from_resource(&seat).unwrap(); - if let Some(start_data) = check_grab_preconditions(&seat, surface.wl_surface(), serial) { - if let Some(mapped) = self - .common - .shell - .element_for_surface(surface.wl_surface()) - .cloned() - { - if let Some(workspace) = self.common.shell.space_for_mut(&mapped) { - let output = seat.active_output(); - let (window, _) = mapped - .windows() - .find(|(w, _)| w.wl_surface().as_ref() == Some(surface.wl_surface())) - .unwrap(); - if let Some(grab) = - workspace.move_request(&window, &seat, &output, serial, start_data) - { - let handle = workspace.handle; - self.common - .shell - .toplevel_info_state - .toplevel_leave_workspace(&window, &handle); - self.common - .shell - .toplevel_info_state - .toplevel_leave_output(&window, &output); - seat.get_pointer() - .unwrap() - .set_grab(self, grab, serial, Focus::Clear); - } - } - } - } + Shell::move_request(self, surface.wl_surface(), &seat, serial) } fn resize_request( @@ -180,24 +146,7 @@ impl XdgShellHandler for State { edges: xdg_toplevel::ResizeEdge, ) { let seat = Seat::from_resource(&seat).unwrap(); - if let Some(start_data) = check_grab_preconditions(&seat, surface.wl_surface(), serial) { - if let Some(mapped) = self - .common - .shell - .element_for_surface(surface.wl_surface()) - .cloned() - { - if let Some(workspace) = self.common.shell.space_for_mut(&mapped) { - if let Some(grab) = - workspace.resize_request(&mapped, &seat, serial, start_data, edges) - { - seat.get_pointer() - .unwrap() - .set_grab(self, grab, serial, Focus::Clear); - } - } - } - } + Shell::resize_request(self, surface.wl_surface(), &seat, serial, edges.into()) } fn maximize_request(&mut self, surface: ToplevelSurface) { @@ -207,7 +156,7 @@ impl XdgShellHandler for State { if let Some(mapped) = self .common .shell - .element_for_surface(surface.wl_surface()) + .element_for_wl_surface(surface.wl_surface()) .cloned() { if let Some(workspace) = self.common.shell.space_for_mut(&mapped) { @@ -224,7 +173,7 @@ impl XdgShellHandler for State { if let Some(mapped) = self .common .shell - .element_for_surface(surface.wl_surface()) + .element_for_wl_surface(surface.wl_surface()) .cloned() { if let Some(workspace) = self.common.shell.space_for_mut(&mapped) { @@ -249,7 +198,7 @@ impl XdgShellHandler for State { if let Some(mapped) = self .common .shell - .element_for_surface(surface.wl_surface()) + .element_for_wl_surface(surface.wl_surface()) .cloned() { if let Some(workspace) = self.common.shell.space_for_mut(&mapped) { @@ -266,7 +215,7 @@ impl XdgShellHandler for State { if let Some(mapped) = self .common .shell - .element_for_surface(surface.wl_surface()) + .element_for_wl_surface(surface.wl_surface()) .cloned() { if let Some(workspace) = self.common.shell.space_for_mut(&mapped) { @@ -319,36 +268,4 @@ impl XdgShellHandler for State { } } -fn check_grab_preconditions( - seat: &Seat, - surface: &WlSurface, - serial: Serial, -) -> Option> { - use smithay::reexports::wayland_server::Resource; - - // TODO: touch resize. - let pointer = seat.get_pointer().unwrap(); - - // Check that this surface has a click grab. - if !pointer.has_grab(serial) { - return None; - } - - let start_data = pointer.grab_start_data().unwrap(); - - // If the focus was for a different surface, ignore the request. - if start_data.focus.is_none() - || !start_data - .focus - .as_ref() - .unwrap() - .0 - .same_client_as(&surface.id()) - { - return None; - } - - Some(start_data) -} - delegate_xdg_shell!(State); diff --git a/src/wayland/handlers/xdg_shell/popup.rs b/src/wayland/handlers/xdg_shell/popup.rs index 641d191e..3dfa770c 100644 --- a/src/wayland/handlers/xdg_shell/popup.rs +++ b/src/wayland/handlers/xdg_shell/popup.rs @@ -28,7 +28,7 @@ use std::sync::Mutex; impl Shell { pub fn unconstrain_popup(&self, surface: &PopupSurface, positioner: &PositionerState) { if let Some(parent) = get_popup_toplevel(&surface) { - if let Some(elem) = self.element_for_surface(&parent) { + if let Some(elem) = self.element_for_wl_surface(&parent) { let workspace = self.space_for(elem).unwrap(); let element_geo = workspace.element_geometry(elem).unwrap(); let (window, offset) = elem diff --git a/src/xwayland.rs b/src/xwayland.rs new file mode 100644 index 00000000..32b186ea --- /dev/null +++ b/src/xwayland.rs @@ -0,0 +1,371 @@ +use crate::{ + backend::render::cursor::Cursor, + shell::{CosmicSurface, Shell}, + state::{Data, State}, + utils::prelude::SeatExt, + wayland::{handlers::screencopy::PendingScreencopyBuffers, protocols::screencopy::SessionType}, +}; +use smithay::{ + backend::drm::DrmNode, + reexports::x11rb::protocol::xproto::Window as X11Window, + utils::{Logical, Point, Rectangle, Size}, + xwayland::{ + xwm::{Reorder, XwmId}, + X11Surface, X11Wm, XWayland, XWaylandEvent, XwmHandler, + }, +}; + +pub struct XWaylandState { + pub xwm: Option, + pub display: Option, + #[allow(unused)] + xwayland: XWayland, +} + +impl State { + pub fn launch_xwayland(&mut self, drm_node: Option) { + if self.common.xwayland_state.contains_key(&drm_node) { + return; + } + + let (xwayland, source) = XWayland::new(None, &self.common.display_handle); + let token = + match self + .common + .event_loop_handle + .insert_source(source, move |event, _, data| match event { + XWaylandEvent::Ready { + connection, + client, + client_fd: _, + display, + } => { + let mut wm = match X11Wm::start_wm( + data.state.common.event_loop_handle.clone(), + data.state.common.display_handle.clone(), + connection, + client, + None, + ) { + Ok(wm) => wm, + Err(err) => { + slog_scope::error!("Failed to start Xwayland WM: {}", err); + return; + } + }; + + let cursor = Cursor::load(); + let image = cursor.get_image(1, 0); + if let Err(err) = 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)), + ) { + slog_scope::warn!( + "Failed to set default cursor for Xwayland WM ({:?}): {}", + wm.id(), + err + ); + } + + let mut xwayland_state = + data.state.common.xwayland_state.get_mut(&drm_node).unwrap(); + xwayland_state.xwm = Some(wm); + xwayland_state.display = Some(display); + } + XWaylandEvent::Exited => { + if let Some(mut xwayland_state) = + data.state.common.xwayland_state.remove(&drm_node) + { + xwayland_state.xwm = None; + xwayland_state.display = None; + } + } + }) { + Ok(token) => token, + Err(err) => { + slog_scope::error!("Failed to listen for Xwayland: {}", err); + return; + } + }; + + match xwayland.start(self.common.event_loop_handle.clone()) { + Ok(_) => { + self.common.xwayland_state.insert( + drm_node, + XWaylandState { + xwayland, + xwm: None, + display: None, + }, + ); + } + Err(err) => { + slog_scope::error!("Failed to start Xwayland: {}", err); + self.common.event_loop_handle.remove(token); + } + } + } +} + +impl XwmHandler for Data { + fn xwm_state(&mut self, xwm: XwmId) -> &mut X11Wm { + self.state + .common + .xwayland_state + .values_mut() + .flat_map(|state| &mut state.xwm) + .find(|wm| wm.id() == xwm) + .unwrap() + } + + fn new_window(&mut self, _xwm: XwmId, _window: X11Surface) {} + fn new_override_redirect_window(&mut self, _xwm: XwmId, _window: X11Surface) {} + fn destroyed_window(&mut self, _xwm: XwmId, _window: X11Surface) {} + + fn map_window_request(&mut self, _xwm: XwmId, window: X11Surface) { + if let Err(err) = window.set_mapped(true) { + slog_scope::warn!( + "Failed to send Xwayland Mapped-Event (for window {:?}): {}", + window, + err + ); + } + let window = CosmicSurface::X11(window); + self.state + .common + .shell + .toplevel_info_state + .new_toplevel(&window); + + let seat = self.state.common.last_active_seat().clone(); + let output = seat.active_output(); + self.state + .common + .shell + .pending_windows + .push((window.clone(), seat)); + Shell::map_window(&mut self.state, &window, &output); + } + + fn mapped_override_redirect_window(&mut self, _xwm: XwmId, window: X11Surface) { + Shell::map_override_redirect(&mut self.state, window) + } + + fn unmapped_window(&mut self, _xwm: XwmId, window: X11Surface) { + if let Some((element, space)) = self + .state + .common + .shell + .element_for_surface(&CosmicSurface::X11(window.clone())) + .cloned() + .and_then(|element| { + self.state + .common + .shell + .space_for_mut(&element) + .map(|space| (element, space)) + }) + { + space.unmap(&element); + } + + let outputs = if let Some(wl_surface) = window.wl_surface() { + self.state + .common + .shell + .visible_outputs_for_surface(&wl_surface) + .collect::>() + } else { + self.state + .common + .shell + .outputs() + .cloned() + .collect::>() + }; + for output in outputs.iter() { + self.state.common.shell.active_space_mut(output).refresh(); + } + + // screencopy + let mut scheduled_sessions = window + .wl_surface() + .map(|wl_surface| self.state.schedule_workspace_sessions(&wl_surface)) + .unwrap_or_default(); + + for output in outputs.into_iter() { + if let Some(sessions) = output.user_data().get::() { + scheduled_sessions + .get_or_insert_with(Vec::new) + .extend(sessions.borrow_mut().drain(..)); + } + self.state.backend.schedule_render( + &self.state.common.event_loop_handle, + &output, + scheduled_sessions.as_ref().map(|sessions| { + sessions + .iter() + .filter(|(s, _)| match s.session_type() { + SessionType::Output(o) | SessionType::Workspace(o, _) + if o == output => + { + true + } + _ => false, + }) + .cloned() + .collect::>() + }), + ); + } + } + + fn configure_request( + &mut self, + _xwm: XwmId, + window: X11Surface, + _x: Option, + _y: Option, + w: Option, + h: Option, + _reorder: Option, + ) { + // We only allow floating X11 windows to resize themselves. Nothing else + let current_size = window.geometry().size; + if let Some(mapped) = self + .state + .common + .shell + .element_for_surface(&CosmicSurface::X11(window)) + { + let space = self.state.common.shell.space_for(mapped).unwrap(); + if space.is_floating(mapped) { + mapped.set_size( + ( + w.map(|w| w as i32).unwrap_or(current_size.w), + h.map(|h| h as i32).unwrap_or(current_size.h), + ) + .into(), + ) + } + } + } + + fn configure_notify( + &mut self, + _xwm: XwmId, + window: X11Surface, + _geometry: Rectangle, + above: Option, + ) { + if window.is_override_redirect() { + if let Some(or) = self + .state + .common + .shell + .override_redirect_windows + .iter_mut() + .find(|or| or.surface == window) + { + or.above = above; + } + } + } + + fn resize_request( + &mut self, + _xwm: XwmId, + window: X11Surface, + _button: u32, + resize_edge: smithay::xwayland::xwm::ResizeEdge, + ) { + if let Some(wl_surface) = window.wl_surface() { + let seat = self.state.common.last_active_seat().clone(); + Shell::resize_request( + &mut self.state, + &wl_surface, + &seat, + None, + resize_edge.into(), + ) + } + } + + fn move_request(&mut self, _xwm: XwmId, window: X11Surface, _button: u32) { + if let Some(wl_surface) = window.wl_surface() { + let seat = self.state.common.last_active_seat().clone(); + Shell::move_request(&mut self.state, &wl_surface, &seat, None) + } + } + + fn maximize_request(&mut self, _xwm: XwmId, window: X11Surface) { + let seat = self.state.common.last_active_seat(); + let output = seat.active_output(); + let surface = CosmicSurface::X11(window); + + if let Some(mapped) = self + .state + .common + .shell + .element_for_surface(&surface) + .cloned() + { + if let Some(workspace) = self.state.common.shell.space_for_mut(&mapped) { + let (window, _) = mapped.windows().find(|(w, _)| w == &surface).unwrap(); + workspace.maximize_request(&window, &output) + } + } + } + + fn unmaximize_request(&mut self, _xwm: XwmId, window: X11Surface) { + let surface = CosmicSurface::X11(window); + if let Some(mapped) = self + .state + .common + .shell + .element_for_surface(&surface) + .cloned() + { + if let Some(workspace) = self.state.common.shell.space_for_mut(&mapped) { + let (window, _) = mapped.windows().find(|(w, _)| w == &surface).unwrap(); + workspace.unmaximize_request(&window); + } + } + } + + fn fullscreen_request(&mut self, _xwm: XwmId, window: X11Surface) { + let seat = self.state.common.last_active_seat(); + let output = seat.active_output(); + let surface = CosmicSurface::X11(window); + + if let Some(mapped) = self + .state + .common + .shell + .element_for_surface(&surface) + .cloned() + { + if let Some(workspace) = self.state.common.shell.space_for_mut(&mapped) { + let (window, _) = mapped.windows().find(|(w, _)| w == &surface).unwrap(); + workspace.fullscreen_request(&window, &output) + } + } + } + + fn unfullscreen_request(&mut self, _xwm: XwmId, window: X11Surface) { + let surface = CosmicSurface::X11(window); + if let Some(mapped) = self + .state + .common + .shell + .element_for_surface(&surface) + .cloned() + { + if let Some(workspace) = self.state.common.shell.space_for_mut(&mapped) { + let (window, _) = mapped.windows().find(|(w, _)| w == &surface).unwrap(); + workspace.unfullscreen_request(&window) + } + } + } +}