|
| 1 | +use calloop::LoopHandle; |
| 2 | +use smithay::{ |
| 3 | + desktop::Window, |
| 4 | + utils::{Logical, Point, Rectangle}, |
| 5 | + wayland::seat::WaylandFocus, |
| 6 | + wayland::selection::SelectionTarget, |
| 7 | + xwayland::{ |
| 8 | + xwm::{Reorder, ResizeEdge as X11ResizeEdge, XwmId}, |
| 9 | + X11Surface, X11Wm, XWayland, XWaylandClientData, XWaylandEvent, |
| 10 | + }, |
| 11 | +}; |
| 12 | +use tracing::{info, warn}; |
| 13 | + |
| 14 | +use crate::state::BlueState; |
| 15 | + |
| 16 | +pub fn init_xwayland(state: &mut BlueState, loop_handle: &LoopHandle<'static, BlueState>) { |
| 17 | + let xwayland = XWayland::new(loop_handle, &state.display_handle); |
| 18 | + |
| 19 | + match xwayland { |
| 20 | + Ok((xwl, source)) => { |
| 21 | + loop_handle |
| 22 | + .insert_source(source, |event, _, state: &mut BlueState| { |
| 23 | + handle_xwayland_event(state, event); |
| 24 | + }) |
| 25 | + .expect("Failed to insert XWayland source"); |
| 26 | + info!("XWayland initialized"); |
| 27 | + } |
| 28 | + Err(e) => { |
| 29 | + warn!("Failed to init XWayland: {} (X11 apps won't work)", e); |
| 30 | + } |
| 31 | + } |
| 32 | +} |
| 33 | + |
| 34 | +fn handle_xwayland_event(state: &mut BlueState, event: XWaylandEvent) { |
| 35 | + match event { |
| 36 | + XWaylandEvent::Ready { x11_socket, display_number } => { |
| 37 | + info!("XWayland ready on DISPLAY :{}", display_number); |
| 38 | + std::env::set_var("DISPLAY", format!(":{}", display_number)); |
| 39 | + state.x11_display = Some(display_number); |
| 40 | + |
| 41 | + match X11Wm::start_wm(state.loop_handle.clone(), x11_socket, state.display_handle.clone()) { |
| 42 | + Ok(wm) => { state.xwm = Some(wm); } |
| 43 | + Err(e) => warn!("Failed to start X11 WM: {}", e), |
| 44 | + } |
| 45 | + } |
| 46 | + XWaylandEvent::Error => { |
| 47 | + warn!("XWayland encountered an error"); |
| 48 | + state.xwm = None; |
| 49 | + } |
| 50 | + } |
| 51 | +} |
| 52 | + |
| 53 | +// ── XWayland shell handler ───────────────────────────────────────────────── |
| 54 | + |
| 55 | +impl smithay::xwayland::xwayland_shell::XWaylandShellHandler for BlueState { |
| 56 | + fn xwayland_shell_state(&mut self) -> &mut smithay::xwayland::xwayland_shell::XWaylandShellState { |
| 57 | + panic!("XWaylandShellState not initialized — needed by XWaylandShellHandler"); |
| 58 | + } |
| 59 | +} |
| 60 | +smithay::delegate_xwayland_shell!(BlueState); |
| 61 | + |
| 62 | +// ── X11 WM implementation ────────────────────────────────────────────────── |
| 63 | + |
| 64 | +impl smithay::xwayland::xwm::XwmHandler for BlueState { |
| 65 | + fn xwm_state(&mut self, _xwm: XwmId) -> &mut X11Wm { |
| 66 | + self.xwm.as_mut().unwrap() |
| 67 | + } |
| 68 | + |
| 69 | + fn new_window(&mut self, _xwm: XwmId, _window: X11Surface) {} |
| 70 | + fn new_override_redirect_window(&mut self, _xwm: XwmId, _window: X11Surface) {} |
| 71 | + |
| 72 | + fn map_window_request(&mut self, _xwm: XwmId, window: X11Surface) { |
| 73 | + window.set_mapped(true).ok(); |
| 74 | + let offset = self.space.elements().count() * 30; |
| 75 | + let loc = Point::from(((offset + 150) as i32, (offset + 80) as i32)); |
| 76 | + self.space.map_element(Window::new_x11_window(window), loc, true); |
| 77 | + } |
| 78 | + |
| 79 | + fn map_window_notify(&mut self, _xwm: XwmId, _window: X11Surface) {} |
| 80 | + |
| 81 | + fn mapped_override_redirect_window(&mut self, _xwm: XwmId, window: X11Surface) { |
| 82 | + self.space.map_element( |
| 83 | + Window::new_x11_window(window), |
| 84 | + Point::from((100_i32, 100_i32)), |
| 85 | + true, |
| 86 | + ); |
| 87 | + } |
| 88 | + |
| 89 | + fn unmapped_window(&mut self, _xwm: XwmId, window: X11Surface) { |
| 90 | + let found = self |
| 91 | + .space |
| 92 | + .elements() |
| 93 | + .find(|w| w.x11_surface().map(|x| x == &window).unwrap_or(false)) |
| 94 | + .cloned(); |
| 95 | + if let Some(w) = found { |
| 96 | + self.space.unmap_elem(&w); |
| 97 | + } |
| 98 | + } |
| 99 | + |
| 100 | + fn destroyed_window(&mut self, _xwm: XwmId, _window: X11Surface) {} |
| 101 | + |
| 102 | + fn configure_request( |
| 103 | + &mut self, |
| 104 | + _xwm: XwmId, |
| 105 | + window: X11Surface, |
| 106 | + _x: Option<i32>, |
| 107 | + _y: Option<i32>, |
| 108 | + w: Option<u32>, |
| 109 | + h: Option<u32>, |
| 110 | + _reorder: Option<Reorder>, |
| 111 | + ) { |
| 112 | + let mut geo = window.geometry(); |
| 113 | + if let Some(w) = w { geo.size.w = w as i32; } |
| 114 | + if let Some(h) = h { geo.size.h = h as i32; } |
| 115 | + let _ = window.configure(geo); |
| 116 | + } |
| 117 | + |
| 118 | + fn configure_notify( |
| 119 | + &mut self, |
| 120 | + _xwm: XwmId, |
| 121 | + _window: X11Surface, |
| 122 | + _geometry: Rectangle<i32, Logical>, |
| 123 | + _above: Option<u32>, |
| 124 | + ) {} |
| 125 | + |
| 126 | + fn resize_request( |
| 127 | + &mut self, |
| 128 | + _xwm: XwmId, |
| 129 | + _window: X11Surface, |
| 130 | + _button: u32, |
| 131 | + _resize_edge: X11ResizeEdge, |
| 132 | + ) {} |
| 133 | + |
| 134 | + fn move_request(&mut self, _xwm: XwmId, _window: X11Surface, _button: u32) {} |
| 135 | + |
| 136 | + fn send_selection( |
| 137 | + &mut self, |
| 138 | + _xwm: XwmId, |
| 139 | + _selection: SelectionTarget, |
| 140 | + _mime_type: String, |
| 141 | + _fd: std::os::fd::OwnedFd, |
| 142 | + ) {} |
| 143 | + |
| 144 | + fn allow_selection_access(&mut self, _xwm: XwmId, _selection: SelectionTarget) -> bool { |
| 145 | + true |
| 146 | + } |
| 147 | + |
| 148 | + fn new_selection( |
| 149 | + &mut self, |
| 150 | + _xwm: XwmId, |
| 151 | + _selection: SelectionTarget, |
| 152 | + _mime_types: Vec<String>, |
| 153 | + ) {} |
| 154 | + |
| 155 | + fn cleared_selection(&mut self, _xwm: XwmId, _selection: SelectionTarget) {} |
| 156 | +} |
| 157 | + |
| 158 | +smithay::delegate_xwm!(BlueState); |
0 commit comments