git.lucas.co / cce-compositor
Wayland compositor (wlroots)
git clone https://git.lucas.co/cce-compositor.git

src/server/server.rs (44.8K)

   1 // SPDX-FileCopyrightText: © 2020 The River Developers
   2 // SPDX-License-Identifier: GPL-3.0-only
   3 
   4 use crate::ffi;
   5 use std::ptr;
   6 
   7 use crate::window_manager::WindowManager;
   8 use crate::xkb_bindings::XkbBindings;
   9 use crate::layer_shell::LayerShell;
  10 use crate::scene::Scene;
  11 use crate::output_manager::OutputManager;
  12 use crate::input_manager::InputManager;
  13 use crate::libinput_config::LibinputConfig;
  14 use crate::xkb_config::XkbConfig;
  15 use crate::idle_inhibit_manager::IdleInhibitManager;
  16 use crate::idle::IdleManager;
  17 use crate::lock_manager::LockManager;
  18 
  19 // Activation-attention notifications ("<app> needs attention / has requested
  20 // activation") are suppressed during the compositor's startup sequence: session
  21 // restore respawns every previously-open window, and each one issues an
  22 // xdg-activation request as it maps, which would otherwise spray a burst of
  23 // "needs attention" popups the moment you log in. `begin_startup_activation_grace`
  24 // is called once when startup begins; until the deadline passes, the handler
  25 // swallows the notification. Outside the startup path the cell is never set, so
  26 // activation notifications behave normally.
  27 static STARTUP_ACTIVATION_GRACE_UNTIL: std::sync::OnceLock<std::time::Instant> =
  28     std::sync::OnceLock::new();
  29 
  30 /// How long after startup begins to keep suppressing activation notifications.
  31 /// Generous enough to cover restored heavyweight apps (e.g. a browser) that take
  32 /// several seconds to launch and request focus.
  33 const STARTUP_ACTIVATION_GRACE: std::time::Duration = std::time::Duration::from_secs(8);
  34 
  35 /// Begin the startup grace window during which activation-attention
  36 /// notifications are suppressed. Idempotent — only the first call takes effect.
  37 pub fn begin_startup_activation_grace() {
  38     let _ = STARTUP_ACTIVATION_GRACE_UNTIL.set(std::time::Instant::now() + STARTUP_ACTIVATION_GRACE);
  39 }
  40 
  41 /// True while we're still inside the post-startup grace window.
  42 fn in_startup_activation_grace() -> bool {
  43     STARTUP_ACTIVATION_GRACE_UNTIL
  44         .get()
  45         .is_some_and(|deadline| std::time::Instant::now() < *deadline)
  46 }
  47 
  48 // Helper macro equivalent to @fieldParentPtr in Zig
  49 #[macro_export]
  50 macro_rules! container_of {
  51     ($ptr:expr, $container:path, $field:ident) => {{
  52         let offset = {
  53             let dummy = std::mem::MaybeUninit::<$container>::uninit();
  54             let dummy_ptr = dummy.as_ptr();
  55             let field_ptr = std::ptr::addr_of!((*dummy_ptr).$field);
  56             (field_ptr as usize).wrapping_sub(dummy_ptr as usize)
  57         };
  58         ((($ptr as *const _) as usize).wrapping_sub(offset)) as *mut $container
  59     }};
  60 }
  61 
  62 // Custom non-opaque layouts for FFI casting
  63 #[repr(C)]
  64 pub struct WlList {
  65     pub prev: *mut WlList,
  66     pub next: *mut WlList,
  67 }
  68 
  69 #[repr(C)]
  70 pub struct WlListener {
  71     pub link: WlList,
  72     pub notify: Option<unsafe extern "C" fn(listener: *mut ffi::wl_listener, data: *mut std::ffi::c_void)>,
  73 }
  74 
  75 #[repr(C)]
  76 pub struct WlrRendererEvents {
  77     pub destroy: ffi::wl_signal,
  78     pub lost: ffi::wl_signal,
  79 }
  80 
  81 #[repr(C)]
  82 pub struct WlrRendererFeatures {
  83     pub output_color_transform: bool,
  84     pub timeline: bool,
  85 }
  86 
  87 #[repr(C)]
  88 pub struct WlrRenderer {
  89     pub render_buffer_caps: u32,
  90     pub events: WlrRendererEvents,
  91     pub features: WlrRendererFeatures,
  92 }
  93 
  94 #[repr(C)]
  95 pub struct WlrBackendFeatures {
  96     pub timeline: bool,
  97 }
  98 
  99 #[repr(C)]
 100 pub struct WlrBackendEvents {
 101     pub destroy: ffi::wl_signal,
 102     pub new_input: ffi::wl_signal,
 103     pub new_output: ffi::wl_signal,
 104 }
 105 
 106 #[repr(C)]
 107 pub struct WlrBackend {
 108     pub impl_: *const std::ffi::c_void,
 109     pub buffer_caps: u32,
 110     pub features: WlrBackendFeatures,
 111     pub events: WlrBackendEvents,
 112 }
 113 
 114 #[repr(C)]
 115 pub struct WlrXdgShellEvents {
 116     pub new_surface: ffi::wl_signal,
 117     pub new_toplevel: ffi::wl_signal,
 118     pub new_popup: ffi::wl_signal,
 119     pub destroy: ffi::wl_signal,
 120 }
 121 
 122 #[repr(C)]
 123 pub struct WlrXdgShell {
 124     pub global: *mut ffi::wl_global,
 125     pub version: u32,
 126     pub clients: ffi::wl_list,
 127     pub popup_grabs: ffi::wl_list,
 128     pub ping_timeout: u32,
 129     pub events: WlrXdgShellEvents,
 130 }
 131 
 132 #[repr(C)]
 133 pub struct WlrXdgDecorationManagerV1Events {
 134     pub new_toplevel_decoration: ffi::wl_signal,
 135     pub destroy: ffi::wl_signal,
 136 }
 137 
 138 #[repr(C)]
 139 pub struct WlrXdgDecorationManagerV1 {
 140     pub global: *mut ffi::wl_global,
 141     pub decorations: ffi::wl_list,
 142     pub events: WlrXdgDecorationManagerV1Events,
 143 }
 144 
 145 #[repr(C)]
 146 pub struct WlrXdgActivationV1Events {
 147     pub destroy: ffi::wl_signal,
 148     pub request_activate: ffi::wl_signal,
 149     pub new_token: ffi::wl_signal,
 150 }
 151 
 152 #[repr(C)]
 153 pub struct WlrXdgActivationV1 {
 154     pub global: *mut ffi::wl_global,
 155     pub token_timeout_msec: u32,
 156     pub tokens: ffi::wl_list,
 157     pub events: WlrXdgActivationV1Events,
 158 }
 159 
 160 #[repr(C)]
 161 pub struct WlrCursorShapeManagerV1Events {
 162     pub request_set_shape: ffi::wl_signal,
 163     pub destroy: ffi::wl_signal,
 164 }
 165 
 166 #[repr(C)]
 167 pub struct WlrCursorShapeManagerV1 {
 168     pub global: *mut ffi::wl_global,
 169     pub events: WlrCursorShapeManagerV1Events,
 170 }
 171 
 172 #[repr(C)]
 173 pub struct WlrExtForeignToplevelImageCaptureSourceManagerV1Events {
 174     pub destroy: ffi::wl_signal,
 175     pub new_request: ffi::wl_signal,
 176 }
 177 
 178 #[repr(C)]
 179 pub struct WlrExtForeignToplevelImageCaptureSourceManagerV1 {
 180     pub global: *mut ffi::wl_global,
 181     pub events: WlrExtForeignToplevelImageCaptureSourceManagerV1Events,
 182 }
 183 
 184 #[repr(C)]
 185 pub struct WlrXwaylandEvents {
 186     pub destroy: ffi::wl_signal,
 187     pub ready: ffi::wl_signal,
 188     pub new_surface: ffi::wl_signal,
 189     pub remove_startup_info: ffi::wl_signal,
 190 }
 191 
 192 #[repr(C)]
 193 pub struct WlrXwayland {
 194     pub server: *mut std::ffi::c_void,
 195     pub own_server: bool,
 196     pub xwm: *mut std::ffi::c_void,
 197     pub shell_v1: *mut std::ffi::c_void,
 198     pub display_name: *const std::os::raw::c_char,
 199     pub wl_display: *mut ffi::wl_display,
 200     pub compositor: *mut ffi::wlr_compositor,
 201     pub seat: *mut std::ffi::c_void,
 202     pub events: WlrXwaylandEvents,
 203 }
 204 
 205 // Wayland list manipulation utilities
 206 pub unsafe fn wl_list_insert(list: *mut WlList, elm: *mut WlList) {
 207     if list.is_null() {
 208         log::error!("wl_list_insert: list is null!");
 209         return;
 210     }
 211     if elm.is_null() {
 212         log::error!("wl_list_insert: elm is null!");
 213         return;
 214     }
 215     if list == elm {
 216         // Inserting a node after itself severs it into a self-loop while
 217         // outside pointers may still reference it — always a caller bug
 218         // (reachable when a stale head.prev names the node being inserted).
 219         log::error!("wl_list_insert: elm == list, refusing self-insert");
 220         return;
 221     }
 222     (*elm).prev = list;
 223     (*elm).next = (*list).next;
 224     (*(*list).next).prev = elm;
 225     (*list).next = elm;
 226 }
 227 
 228 pub unsafe fn wl_list_remove(elm: *mut WlList) {
 229     // Upstream libwayland nulls the removed element's pointers; this port
 230     // originally left them stale, so a second remove — or any later
 231     // tail/linked check against them — wrote through pointers into whatever
 232     // list the node USED to be in, silently corrupting live members. That
 233     // corruption class is how a status segment ended up self-looped and
 234     // invisible to every re-link heal (the tray parked outside the right
 235     // group after bar restarts). Match C semantics: null after unlinking,
 236     // and no-op an already-removed node instead of dereferencing null.
 237     if (*elm).prev.is_null() || (*elm).next.is_null() {
 238         return;
 239     }
 240     (*(*elm).next).prev = (*elm).prev;
 241     (*(*elm).prev).next = (*elm).next;
 242     (*elm).prev = std::ptr::null_mut();
 243     (*elm).next = std::ptr::null_mut();
 244 }
 245 
 246 pub unsafe fn wl_list_remove_and_reinit(elm: *mut WlList) {
 247     wl_list_remove(elm);
 248     (*elm).prev = elm;
 249     (*elm).next = elm;
 250 }
 251 
 252 pub unsafe fn wl_signal_add(signal: *mut ffi::wl_signal, listener: *mut ffi::wl_listener) {
 253     log::info!("wl_signal_add: signal={:?}, listener={:?}", signal, listener);
 254     if signal.is_null() {
 255         log::error!("wl_signal_add: signal is null!");
 256         return;
 257     }
 258     let sig_list = &mut (*signal).listener_list as *mut ffi::wl_list as *mut WlList;
 259     log::info!("wl_signal_add: sig_list={:?}, prev={:?}, next={:?}", sig_list, (*sig_list).prev, (*sig_list).next);
 260     let listener_custom = listener as *mut WlListener;
 261     wl_list_insert((*sig_list).prev, &mut (*listener_custom).link);
 262 }
 263 
 264 pub unsafe fn wl_listener_remove(listener: *mut ffi::wl_listener) {
 265     let listener_custom = listener as *mut WlListener;
 266     wl_list_remove(&mut (*listener_custom).link);
 267 }
 268 
 269 
 270 pub struct Server {
 271     pub wl_server: *mut ffi::wl_display,
 272     pub sigint_source: *mut ffi::wl_event_source,
 273     pub sigterm_source: *mut ffi::wl_event_source,
 274     pub sigchld_source: *mut ffi::wl_event_source,
 275     // pub fixes: *mut ffi::wlr_fixes,
 276     pub backend: *mut ffi::wlr_backend,
 277     pub session: *mut ffi::wlr_session,
 278     pub renderer: *mut ffi::wlr_renderer,
 279     pub allocator: *mut ffi::wlr_allocator,
 280     pub gpu_reset_recover: *mut ffi::wl_event_source,
 281     pub security_context_manager: *mut ffi::wlr_security_context_manager_v1,
 282     pub shm: *mut ffi::wlr_shm,
 283     pub linux_dmabuf: *mut ffi::wlr_linux_dmabuf_v1,
 284     pub linux_drm_syncobj_manager: *mut ffi::wlr_linux_drm_syncobj_manager_v1,
 285     pub single_pixel_buffer_manager: *mut ffi::wlr_single_pixel_buffer_manager_v1,
 286     pub alpha_modifier: *mut ffi::wlr_alpha_modifier_v1,
 287     pub color_manager: *mut ffi::wlr_color_manager_v1,
 288     // pub color_representation_manager: *mut ffi::wlr_color_representation_manager_v1,
 289     pub viewporter: *mut ffi::wlr_viewporter,
 290     pub fractional_scale_manager: *mut ffi::wlr_fractional_scale_manager_v1,
 291     pub compositor: *mut ffi::wlr_compositor,
 292     pub subcompositor: *mut ffi::wlr_subcompositor,
 293     pub cursor_shape_manager: *mut ffi::wlr_cursor_shape_manager_v1,
 294     pub xdg_shell: *mut ffi::wlr_xdg_shell,
 295     pub xdg_decoration_manager: *mut ffi::wlr_xdg_decoration_manager_v1,
 296     pub xdg_activation: *mut ffi::wlr_xdg_activation_v1,
 297     pub xdg_foreign_registry: *mut ffi::wlr_xdg_foreign_registry,
 298     pub xdg_foreign_v2: *mut ffi::wlr_xdg_foreign_v2,
 299     pub data_device_manager: *mut ffi::wlr_data_device_manager,
 300     pub primary_selection_manager: *mut ffi::wlr_primary_selection_v1_device_manager,
 301     pub data_control_manager: *mut ffi::wlr_ext_data_control_manager_v1,
 302     pub wlr_data_control_manager: *mut ffi::wlr_data_control_manager_v1,
 303     pub export_dmabuf_manager: *mut ffi::wlr_export_dmabuf_manager_v1,
 304     pub screencopy_manager: *mut ffi::wlr_screencopy_manager_v1,
 305     pub image_copy_capture_manager: *mut ffi::wlr_ext_image_copy_capture_manager_v1,
 306     pub output_image_capture_source_manager: *mut ffi::wlr_ext_output_image_capture_source_manager_v1,
 307     pub wlr_foreign_toplevel_manager: *mut ffi::wlr_foreign_toplevel_manager_v1,
 308     pub foreign_toplevel_list: *mut ffi::wlr_ext_foreign_toplevel_list_v1,
 309     // pub toplevel_capture_source_manager: *mut ffi::wlr_ext_foreign_toplevel_image_capture_source_manager_v1,
 310     pub tearing_control_manager: *mut ffi::wlr_tearing_control_manager_v1,
 311 
 312     pub xwayland: *mut ffi::wlr_xwayland,
 313 
 314     // Subcomponents
 315     pub wm: WindowManager,
 316     pub xkb_bindings: XkbBindings,
 317     pub layer_shell: LayerShell,
 318     pub scene: Scene,
 319     pub om: OutputManager,
 320     pub input_manager: InputManager,
 321     pub libinput_config: LibinputConfig,
 322     pub xkb_config: XkbConfig,
 323     pub idle_inhibit_manager: IdleInhibitManager,
 324     pub idle: IdleManager,
 325     pub lock_manager: LockManager,
 326     pub inspector: crate::inspector::Inspector,
 327     pub cce_window_management: crate::cce_window_management::CceWindowManagement,
 328 
 329     // Event listeners
 330     pub renderer_lost: ffi::wl_listener,
 331     pub new_xdg_toplevel: ffi::wl_listener,
 332     pub new_toplevel_decoration: ffi::wl_listener,
 333     pub request_activate: ffi::wl_listener,
 334     pub request_set_cursor_shape: ffi::wl_listener,
 335     // pub toplevel_capture_request: ffi::wl_listener,
 336     pub new_xsurface: ffi::wl_listener,
 337     pub xwayland_ready: ffi::wl_listener,
 338 }
 339 
 340 unsafe extern "C" fn terminate(_signum: std::os::raw::c_int, data: *mut std::ffi::c_void) -> std::os::raw::c_int {
 341     let wl_server = data as *mut ffi::wl_display;
 342     ffi::wl_display_terminate(wl_server);
 343     0
 344 }
 345 
 346 unsafe extern "C" fn handle_sigchld(_signum: std::os::raw::c_int, _data: *mut std::ffi::c_void) -> std::os::raw::c_int {
 347     let mut status = 0;
 348     while libc::waitpid(-1, &mut status, libc::WNOHANG) > 0 {}
 349     0
 350 }
 351 
 352 unsafe extern "C" fn handle_renderer_lost(listener: *mut ffi::wl_listener, _data: *mut std::ffi::c_void) {
 353     let _server = container_of!(listener, Server, renderer_lost);
 354     log::info!("received GPU reset event");
 355 }
 356 
 357 unsafe extern "C" fn handle_new_xdg_toplevel(listener: *mut ffi::wl_listener, data: *mut std::ffi::c_void) {
 358     let server = container_of!(listener, Server, new_xdg_toplevel);
 359     let xdg_toplevel = data as *mut ffi::wlr_xdg_toplevel;
 360     log::info!("new xdg toplevel surface");
 361     if let Err(e) = crate::xdg_toplevel::XdgToplevel::create(xdg_toplevel, server) {
 362         log::error!("Failed to create xdg toplevel: {}", e);
 363         let client = ffi::wl_resource_get_client((*xdg_toplevel).resource);
 364         ffi::wl_client_post_no_memory(client);
 365     }
 366 }
 367 
 368 unsafe extern "C" fn handle_new_toplevel_decoration(listener: *mut ffi::wl_listener, data: *mut std::ffi::c_void) {
 369     let _server = container_of!(listener, Server, new_toplevel_decoration);
 370     let decoration = data as *mut ffi::wlr_xdg_toplevel_decoration_v1;
 371     log::info!("new toplevel decoration");
 372     crate::xdg_toplevel::XdgDecoration::init(decoration);
 373 }
 374 
 375 unsafe extern "C" fn handle_request_activate(listener: *mut ffi::wl_listener, data: *mut std::ffi::c_void) {
 376     let server = container_of!(listener, Server, request_activate);
 377     log::info!("xdg activation request received");
 378     if data.is_null() {
 379         return;
 380     }
 381     let event = data as *mut ffi::wlr_xdg_activation_v1_request_activate_event;
 382     let surface = (*event).surface;
 383     if surface.is_null() {
 384         return;
 385     }
 386 
 387     let default_seat = (*server).input_manager.default_seat;
 388     if !default_seat.is_null() {
 389         let focused_surf = ffi::river_wlr_seat_get_keyboard_focused_surface((*default_seat).wlr_seat);
 390         if focused_surf == surface {
 391             log::info!("xdg activation request ignored: window is already focused");
 392             return;
 393         }
 394     }
 395 
 396     for &win_ptr in (*server).wm.windows.iter() {
 397         if !win_ptr.is_null() && (*win_ptr).root_surface() == surface {
 398             let title_ptr = (*win_ptr).get_title();
 399             let title = if title_ptr.is_null() {
 400                 "Window".to_string()
 401             } else {
 402                 std::ffi::CStr::from_ptr(title_ptr).to_string_lossy().into_owned()
 403             };
 404             
 405             let app_id_ptr = (*win_ptr).get_app_id();
 406             let app_id = if app_id_ptr.is_null() {
 407                 "unknown".to_string()
 408             } else {
 409                 std::ffi::CStr::from_ptr(app_id_ptr).to_string_lossy().into_owned()
 410             };
 411             
 412             log::info!("xdg activation request for window '{}' ({})", title, app_id);
 413 
 414             if in_startup_activation_grace() {
 415                 log::info!(
 416                     "activation notification suppressed during startup grace for '{}' ({})",
 417                     title,
 418                     app_id
 419                 );
 420                 break;
 421             }
 422 
 423             // A valid token (wlroots has already checked it against a recent
 424             // input serial or the requesting surface's focus) is the user's
 425             // intent to see this window, so honour it like `ccectl
 426             // focus-window`: un-minimize, focus and raise — `Seat::focus`
 427             // raises a Floating window itself, and `raise_window` covers
 428             // the rest. Until now this handler only fired the "needs
 429             // attention" notification, so an activation for an already-mapped
 430             // window changed nothing: the window stayed beneath whatever
 431             // covered it. A window that has not mapped yet (the request
 432             // often lands between app_id and map) is left to the map path,
 433             // which focuses new windows under its own settle rules.
 434             if matches!((*win_ptr).state, crate::window::WindowState::Mapped) && !(*win_ptr).is_shy() {
 435                 if let Some(seat) = (*server).wm.first_seat() {
 436                     if (*win_ptr).minimized {
 437                         (*win_ptr).minimized = false;
 438                     }
 439                     (*seat).focus(crate::seat::Focus::Window(win_ptr));
 440                     (*server).wm.raise_window(win_ptr);
 441                     (*server).wm.dirty_windowing();
 442                     log::info!("xdg activation focused and raised '{}' ({})", title, app_id);
 443                 }
 444             } else {
 445                 log::info!("xdg activation for unmapped window '{}' ({}): left to the map path", title, app_id);
 446             }
 447 
 448             let uid = unsafe { libc::getuid() };
 449             let bus_address = format!("unix:path=/run/user/{}/bus", uid);
 450             
 451             std::process::Command::new("gdbus")
 452                 .env("DBUS_SESSION_BUS_ADDRESS", &bus_address)
 453                 .args([
 454                     "call",
 455                     "--session",
 456                     "--dest",
 457                     "org.kde.StatusNotifierWatcher",
 458                     "--object-path",
 459                     "/StatusInterface",
 460                     "--method",
 461                     "org.clear.StatusInterface.NotifyAttention",
 462                     &app_id,
 463                     &title,
 464                 ])
 465                 .spawn()
 466                 .ok();
 467             break;
 468         }
 469     }
 470 }
 471 
 472 unsafe extern "C" fn handle_request_set_cursor_shape(listener: *mut ffi::wl_listener, data: *mut std::ffi::c_void) {
 473     let server = container_of!(listener, Server, request_set_cursor_shape);
 474     let event = data as *mut ffi::wlr_cursor_shape_manager_v1_request_set_shape_event;
 475     
 476     let wlr_seat = (*(*event).seat_client).seat;
 477     let focused_client = ffi::river_wlr_seat_get_pointer_focused_client(wlr_seat);
 478     
 479     let event_client = ffi::river_wlr_seat_client_get_client((*event).seat_client);
 480     let wm_client = if !(*server).wm.object.is_null() {
 481         ffi::wl_resource_get_client((*server).wm.object)
 482     } else {
 483         std::ptr::null_mut()
 484     };
 485     let is_wm = !wm_client.is_null() && event_client == wm_client;
 486 
 487     let shape_name = ffi::wlr_cursor_shape_v1_name((*event).shape);
 488     let shape_str = if shape_name.is_null() {
 489         "unknown".to_string()
 490     } else {
 491         std::ffi::CStr::from_ptr(shape_name).to_string_lossy().into_owned()
 492     };
 493 
 494     log::debug!(
 495         "set_cursor_shape: event_client={:?}, wm_client={:?}, is_wm={}, focused={:?}, shape={}",
 496         event_client,
 497         wm_client,
 498         is_wm,
 499         focused_client,
 500         shape_str
 501     );
 502 
 503     if focused_client == (*event).seat_client || is_wm {
 504         let seat = ffi::river_wlr_seat_get_data(wlr_seat) as *mut crate::seat::Seat;
 505         if !seat.is_null() {
 506             (*seat).cursor.set_xcursor(shape_name);
 507         }
 508     }
 509 }
 510 
 511 // unsafe extern "C" fn handle_toplevel_capture_request(listener: *mut ffi::wl_listener, _data: *mut std::ffi::c_void) {
 512 //     let _server = container_of!(listener, Server, toplevel_capture_request);
 513 //     log::info!("toplevel capture request");
 514 // }
 515 
 516 unsafe extern "C" fn handle_new_xwayland_surface(listener: *mut ffi::wl_listener, data: *mut std::ffi::c_void) {
 517     let server = container_of!(listener, Server, new_xsurface);
 518     let xsurface = data as *mut ffi::wlr_xwayland_surface;
 519     log::info!("new xwayland surface");
 520 
 521     if (*xsurface).override_redirect {
 522         if let Err(e) = crate::xwayland_override_redirect::XwaylandOverrideRedirect::create(xsurface, server) {
 523             log::error!("Failed to create xwayland override redirect surface: {}", e);
 524         }
 525     } else {
 526         if let Err(e) = crate::xwayland_window::XwaylandWindow::create(xsurface, server) {
 527             log::error!("Failed to create xwayland window surface: {}", e);
 528         }
 529     }
 530 }
 531 
 532 /// The first field of `struct wl_interface`; bindgen leaves the type opaque.
 533 #[repr(C)]
 534 struct WlInterfaceHead {
 535     name: *const std::os::raw::c_char,
 536 }
 537 
 538 /// Hide the xdg-output global from the Xwayland client while
 539 /// `xwayland_hidpi` is on. Xwayland sizes its root window from xdg-output's
 540 /// LOGICAL size when it can see one (1920x1200 on the scale-2 panel), and
 541 /// then every X11 app draws at logical resolution and is upscaled — blurry.
 542 /// Without xdg-output it falls back to the wl_output mode, the physical
 543 /// pixel grid, and a DPI-aware X11 app renders sharp; the compositor draws
 544 /// X11 surfaces at 1/scale and converts X11 geometry to match
 545 /// (`xwayland_window.rs`). Every other client keeps seeing xdg-output.
 546 unsafe extern "C" fn xwayland_global_filter(
 547     client: *const ffi::wl_client,
 548     global: *const ffi::wl_global,
 549     data: *mut std::ffi::c_void,
 550 ) -> bool {
 551     let server = data as *mut Server;
 552     if server.is_null() || (*server).xwayland.is_null() || !(*server).wm.xwayland_hidpi {
 553         return true;
 554     }
 555     let xserver = (*((*server).xwayland as *mut WlrXwayland)).server as *mut ffi::wlr_xwayland_server;
 556     if xserver.is_null() || (*xserver).client.is_null() || (*xserver).client as *const ffi::wl_client != client {
 557         return true;
 558     }
 559     let iface = ffi::wl_global_get_interface(global) as *const WlInterfaceHead;
 560     if iface.is_null() || (*iface).name.is_null() {
 561         return true;
 562     }
 563     std::ffi::CStr::from_ptr((*iface).name).to_bytes() != b"zxdg_output_manager_v1"
 564 }
 565 
 566 unsafe extern "C" fn handle_xwayland_ready(listener: *mut ffi::wl_listener, _data: *mut std::ffi::c_void) {
 567     let server = container_of!(listener, Server, xwayland_ready);
 568     let xwayland_cast = (*server).xwayland as *mut WlrXwayland;
 569     if !xwayland_cast.is_null() && !(*xwayland_cast).display_name.is_null() {
 570         let display_name = std::ffi::CStr::from_ptr((*xwayland_cast).display_name)
 571             .to_string_lossy()
 572             .into_owned();
 573         log::info!("Xwayland is ready on display {}", display_name);
 574         std::env::set_var("DISPLAY", &display_name);
 575 
 576         // Under `xwayland_hidpi` X11 is a physical-pixel world (see
 577         // `xwayland_window::x11_scale`), so tell X11 clients the DPI that goes
 578         // with it: Qt 6 (Houdini) and Xft-based toolkits read Xft.dpi and scale
 579         // themselves to match. GTK on X11 wants GDK_SCALE in its own environment
 580         // on top of this; that is the app launcher's to provide.
 581         //
 582         // `Xcursor.size` goes with them, and is where X11 clients get their
 583         // cursor size from — the session deliberately exports no XCURSOR_SIZE,
 584         // which would beat this resource (see `scripts/startcce`). It is the
 585         // physical size for the same reason: an X11 cursor bitmap is drawn at
 586         // 1/scale like the rest of the client's drawing (`seat.rs`,
 587         // `handle_request_set_cursor`), so 24 logical pixels is 24*scale of
 588         // them. Sent even at scale 1, because otherwise libXcursor guesses
 589         // from the screen height and lands somewhere else entirely.
 590         let s = crate::xwayland_window::x11_scale(server);
 591         let dpi = (96.0 * s).round() as i32;
 592         let cursor = (24.0 * s).round() as i32;
 593         let mut resources = format!("Xcursor.size: {}\n", cursor);
 594         if s != 1.0 {
 595             resources.push_str(&format!("Xft.dpi: {}\n", dpi));
 596         }
 597         match std::process::Command::new("xrdb")
 598             .args(["-merge", "-"])
 599             .env("DISPLAY", &display_name)
 600             .stdin(std::process::Stdio::piped())
 601             .stdout(std::process::Stdio::null())
 602             .stderr(std::process::Stdio::null())
 603             .spawn()
 604         {
 605             Ok(mut child) => {
 606                 use std::io::Write;
 607                 if let Some(mut stdin) = child.stdin.take() {
 608                     let _ = write!(stdin, "{}", resources);
 609                 }
 610                 std::thread::spawn(move || { let _ = child.wait(); });
 611                 log::info!("Xwayland: X11 scale {} — set Xcursor.size {}{} via xrdb", s, cursor,
 612                     if s != 1.0 { format!(", Xft.dpi {}", dpi) } else { String::new() });
 613             }
 614             Err(e) => log::warn!("Xwayland: could not run xrdb to set X resources: {}", e),
 615         }
 616     }
 617 }
 618 
 619 impl Server {
 620     pub fn init(&mut self, runtime_xwayland: bool) -> Result<(), &'static str> {
 621         unsafe {
 622             let wl_server = ffi::wl_display_create();
 623             if wl_server.is_null() {
 624                 return Err("Failed to create wayland server");
 625             }
 626             self.wl_server = wl_server;
 627 
 628             let loop_ = ffi::wl_display_get_event_loop(wl_server);
 629             if loop_.is_null() {
 630                 return Err("Failed to get event loop");
 631             }
 632 
 633             self.sigint_source = ffi::wl_event_loop_add_signal(loop_, libc::SIGINT, Some(terminate), wl_server as *mut _);
 634             self.sigterm_source = ffi::wl_event_loop_add_signal(loop_, libc::SIGTERM, Some(terminate), wl_server as *mut _);
 635             self.sigchld_source = ffi::wl_event_loop_add_signal(loop_, libc::SIGCHLD, Some(handle_sigchld), wl_server as *mut _);
 636 
 637             let mut session: *mut ffi::wlr_session = ptr::null_mut();
 638             let backend = ffi::wlr_backend_autocreate(loop_, &mut session);
 639             if backend.is_null() {
 640                 return Err("Failed to autocreate wlr_backend");
 641             }
 642             self.backend = backend;
 643             self.session = session;
 644 
 645             let renderer = ffi::fx_renderer_create(backend);
 646             if renderer.is_null() {
 647                 return Err("Failed to create fx_renderer");
 648             }
 649             self.renderer = renderer;
 650 
 651             let compositor = ffi::wlr_compositor_create(wl_server, 6, renderer);
 652             if compositor.is_null() {
 653                 return Err("Failed to create wlr_compositor");
 654             }
 655             self.compositor = compositor;
 656 
 657             let xdg_foreign_registry = ffi::wlr_xdg_foreign_registry_create(wl_server);
 658             if xdg_foreign_registry.is_null() {
 659                 return Err("Failed to create xdg_foreign_registry");
 660             }
 661             self.xdg_foreign_registry = xdg_foreign_registry;
 662 
 663             // let fixes = ffi::wlr_fixes_create(wl_server, 1);
 664             // if fixes.is_null() {
 665             //     return Err("Failed to create wlr_fixes");
 666             // }
 667             // self.fixes = fixes;
 668 
 669             let allocator = ffi::wlr_allocator_autocreate(backend, renderer);
 670             if allocator.is_null() {
 671                 return Err("Failed to autocreate allocator");
 672             }
 673             self.allocator = allocator;
 674 
 675             let security_context_manager = ffi::wlr_security_context_manager_v1_create(wl_server);
 676             if security_context_manager.is_null() {
 677                 return Err("Failed to create security context manager");
 678             }
 679             self.security_context_manager = security_context_manager;
 680 
 681             let shm = ffi::wlr_shm_create_with_renderer(wl_server, 2, renderer);
 682             if shm.is_null() {
 683                 return Err("Failed to create shm");
 684             }
 685             self.shm = shm;
 686 
 687             let single_pixel_buffer_manager = ffi::wlr_single_pixel_buffer_manager_v1_create(wl_server);
 688             if single_pixel_buffer_manager.is_null() {
 689                 return Err("Failed to create single pixel buffer manager");
 690             }
 691             self.single_pixel_buffer_manager = single_pixel_buffer_manager;
 692 
 693             let alpha_modifier = ffi::wlr_alpha_modifier_v1_create(wl_server);
 694             if alpha_modifier.is_null() {
 695                 return Err("Failed to create alpha modifier");
 696             }
 697             self.alpha_modifier = alpha_modifier;
 698 
 699             // let color_representation_manager = ffi::wlr_color_representation_manager_v1_create_with_renderer(wl_server, 1, renderer);
 700             // if color_representation_manager.is_null() {
 701             //     return Err("Failed to create color representation manager");
 702             // }
 703             // self.color_representation_manager = color_representation_manager;
 704 
 705             let viewporter = ffi::wlr_viewporter_create(wl_server);
 706             if viewporter.is_null() {
 707                 return Err("Failed to create viewporter");
 708             }
 709             self.viewporter = viewporter;
 710 
 711             let fractional_scale_manager = ffi::wlr_fractional_scale_manager_v1_create(wl_server, 1);
 712             if fractional_scale_manager.is_null() {
 713                 return Err("Failed to create fractional scale manager");
 714             }
 715             self.fractional_scale_manager = fractional_scale_manager;
 716 
 717             let subcompositor = ffi::wlr_subcompositor_create(wl_server);
 718             if subcompositor.is_null() {
 719                 return Err("Failed to create subcompositor");
 720             }
 721             self.subcompositor = subcompositor;
 722 
 723             let cursor_shape_manager = ffi::wlr_cursor_shape_manager_v1_create(wl_server, 1);
 724             if cursor_shape_manager.is_null() {
 725                 return Err("Failed to create cursor shape manager");
 726             }
 727             self.cursor_shape_manager = cursor_shape_manager;
 728 
 729             let xdg_shell = ffi::wlr_xdg_shell_create(wl_server, 5);
 730             if xdg_shell.is_null() {
 731                 return Err("Failed to create xdg shell");
 732             }
 733             self.xdg_shell = xdg_shell;
 734 
 735             let xdg_decoration_manager = ffi::wlr_xdg_decoration_manager_v1_create(wl_server);
 736             if xdg_decoration_manager.is_null() {
 737                 return Err("Failed to create xdg decoration manager");
 738             }
 739             self.xdg_decoration_manager = xdg_decoration_manager;
 740 
 741             let xdg_activation = ffi::wlr_xdg_activation_v1_create(wl_server);
 742             if xdg_activation.is_null() {
 743                 return Err("Failed to create xdg activation");
 744             }
 745             self.xdg_activation = xdg_activation;
 746 
 747             let xdg_foreign_v2 = ffi::wlr_xdg_foreign_v2_create(wl_server, xdg_foreign_registry);
 748             if xdg_foreign_v2.is_null() {
 749                 return Err("Failed to create xdg foreign v2");
 750             }
 751             self.xdg_foreign_v2 = xdg_foreign_v2;
 752 
 753             let data_device_manager = ffi::wlr_data_device_manager_create(wl_server);
 754             if data_device_manager.is_null() {
 755                 return Err("Failed to create data device manager");
 756             }
 757             self.data_device_manager = data_device_manager;
 758 
 759             let primary_selection_manager = ffi::wlr_primary_selection_v1_device_manager_create(wl_server);
 760             if primary_selection_manager.is_null() {
 761                 return Err("Failed to create primary selection manager");
 762             }
 763             self.primary_selection_manager = primary_selection_manager;
 764 
 765             let data_control_manager = ffi::wlr_ext_data_control_manager_v1_create(wl_server, 1);
 766             if data_control_manager.is_null() {
 767                 return Err("Failed to create ext data control manager");
 768             }
 769             self.data_control_manager = data_control_manager;
 770 
 771             let wlr_data_control_manager = ffi::wlr_data_control_manager_v1_create(wl_server);
 772             if wlr_data_control_manager.is_null() {
 773                 return Err("Failed to create data control manager");
 774             }
 775             self.wlr_data_control_manager = wlr_data_control_manager;
 776 
 777             let export_dmabuf_manager = ffi::wlr_export_dmabuf_manager_v1_create(wl_server);
 778             if export_dmabuf_manager.is_null() {
 779                 return Err("Failed to create export dmabuf manager");
 780             }
 781             self.export_dmabuf_manager = export_dmabuf_manager;
 782 
 783             let screencopy_manager = ffi::wlr_screencopy_manager_v1_create(wl_server);
 784             if screencopy_manager.is_null() {
 785                 return Err("Failed to create screencopy manager");
 786             }
 787             self.screencopy_manager = screencopy_manager;
 788 
 789             let image_copy_capture_manager = ffi::wlr_ext_image_copy_capture_manager_v1_create(wl_server, 1);
 790             if image_copy_capture_manager.is_null() {
 791                 return Err("Failed to create image copy capture manager");
 792             }
 793             self.image_copy_capture_manager = image_copy_capture_manager;
 794 
 795             let output_image_capture_source_manager = ffi::wlr_ext_output_image_capture_source_manager_v1_create(wl_server, 1);
 796             if output_image_capture_source_manager.is_null() {
 797                 return Err("Failed to create output image capture source manager");
 798             }
 799             self.output_image_capture_source_manager = output_image_capture_source_manager;
 800 
 801             let wlr_foreign_toplevel_manager = ffi::wlr_foreign_toplevel_manager_v1_create(wl_server);
 802             if wlr_foreign_toplevel_manager.is_null() {
 803                 return Err("Failed to create foreign toplevel manager");
 804             }
 805             self.wlr_foreign_toplevel_manager = wlr_foreign_toplevel_manager;
 806 
 807             let foreign_toplevel_list = ffi::wlr_ext_foreign_toplevel_list_v1_create(wl_server, 1);
 808             if foreign_toplevel_list.is_null() {
 809                 return Err("Failed to create foreign toplevel list");
 810             }
 811             self.foreign_toplevel_list = foreign_toplevel_list;
 812 
 813             // let toplevel_capture_source_manager = ffi::wlr_ext_foreign_toplevel_image_capture_source_manager_v1_create(wl_server, 1);
 814             // if toplevel_capture_source_manager.is_null() {
 815             //     return Err("Failed to create toplevel capture source manager");
 816             // }
 817             // self.toplevel_capture_source_manager = toplevel_capture_source_manager;
 818 
 819             let tearing_control_manager = ffi::wlr_tearing_control_manager_v1_create(wl_server, 1);
 820             if tearing_control_manager.is_null() {
 821                 return Err("Failed to create tearing control manager");
 822             }
 823             self.tearing_control_manager = tearing_control_manager;
 824 
 825             // Setup Xwayland if runtime requested
 826             if runtime_xwayland {
 827                 let xwayland = ffi::wlr_xwayland_create(wl_server, compositor, false);
 828                 if xwayland.is_null() {
 829                     return Err("Failed to create xwayland server");
 830                 }
 831                 self.xwayland = xwayland;
 832                 // See `xwayland_global_filter`.
 833                 ffi::wl_display_set_global_filter(
 834                     wl_server,
 835                     Some(xwayland_global_filter),
 836                     self as *mut Server as *mut std::ffi::c_void,
 837                 );
 838             }
 839 
 840             // Setup linux dmabuf if supported
 841             if !ffi::wlr_renderer_get_texture_formats(renderer, ffi::wlr_buffer_cap_WLR_BUFFER_CAP_DMABUF).is_null() {
 842                 self.linux_dmabuf = ffi::wlr_linux_dmabuf_v1_create_with_renderer(wl_server, 5, renderer);
 843             }
 844 
 845             // Setup linux drm syncobj if supported
 846             let renderer_cast = renderer as *mut WlrRenderer;
 847             let backend_cast = backend as *mut WlrBackend;
 848             if (*renderer_cast).features.timeline && (*backend_cast).features.timeline {
 849                 let drm_fd = ffi::wlr_renderer_get_drm_fd(renderer);
 850                 if drm_fd >= 0 {
 851                     self.linux_drm_syncobj_manager = ffi::wlr_linux_drm_syncobj_manager_v1_create(wl_server, 1, drm_fd);
 852                 }
 853             }
 854 
 855             // Setup color manager if supported
 856             // (Commented out for wlroots 0.19 compatibility)
 857             self.color_manager = std::ptr::null_mut();
 858 
 859             // Setup subcomponents stubs
 860             let server_ptr = self as *mut Server;
 861             self.wm.init_with_server(server_ptr).map_err(|_| "Failed to init wm")?;
 862             self.xkb_bindings.init(server_ptr, self.wl_server).map_err(|_| "Failed to init xkb_bindings")?;
 863             self.layer_shell.init(server_ptr, self.wl_server).map_err(|_| "Failed to init layer_shell")?;
 864             self.scene.init(self.linux_dmabuf, self.color_manager).map_err(|_| "Failed to init scene")?;
 865             self.om.init(server_ptr).map_err(|_| "Failed to init om")?;
 866             self.input_manager.init(server_ptr).map_err(|_| "Failed to init input_manager")?;
 867             self.libinput_config.init(server_ptr).map_err(|_| "Failed to init libinput_config")?;
 868             self.xkb_config.init(server_ptr).map_err(|_| "Failed to init xkb_config")?;
 869             self.idle_inhibit_manager.init(server_ptr).map_err(|_| "Failed to init idle_inhibit_manager")?;
 870             self.idle.init(server_ptr).map_err(|_| "Failed to init idle")?;
 871             self.lock_manager.init(server_ptr).map_err(|_| "Failed to init lock_manager")?;
 872             self.inspector.init(server_ptr).map_err(|_| "Failed to init inspector")?;
 873             self.cce_window_management.init(server_ptr).map_err(|_| "Failed to init cce_window_management")?;
 874 
 875             // Setup listeners
 876             let r_lost = &mut self.renderer_lost as *mut ffi::wl_listener as *mut WlListener;
 877             (*r_lost).notify = Some(handle_renderer_lost);
 878 
 879             let new_xdg = &mut self.new_xdg_toplevel as *mut ffi::wl_listener as *mut WlListener;
 880             (*new_xdg).notify = Some(handle_new_xdg_toplevel);
 881 
 882             let new_dec = &mut self.new_toplevel_decoration as *mut ffi::wl_listener as *mut WlListener;
 883             (*new_dec).notify = Some(handle_new_toplevel_decoration);
 884 
 885             let req_act = &mut self.request_activate as *mut ffi::wl_listener as *mut WlListener;
 886             (*req_act).notify = Some(handle_request_activate);
 887 
 888             let req_cursor = &mut self.request_set_cursor_shape as *mut ffi::wl_listener as *mut WlListener;
 889             (*req_cursor).notify = Some(handle_request_set_cursor_shape);
 890 
 891             // let cap_req = &mut self.toplevel_capture_request as *mut ffi::wl_listener as *mut WlListener;
 892             // (*cap_req).notify = Some(handle_toplevel_capture_request);
 893 
 894             let xdg_shell_cast = self.xdg_shell as *mut WlrXdgShell;
 895             let xdg_decoration_manager_cast = self.xdg_decoration_manager as *mut WlrXdgDecorationManagerV1;
 896             let xdg_activation_cast = self.xdg_activation as *mut WlrXdgActivationV1;
 897             let cursor_shape_manager_cast = self.cursor_shape_manager as *mut WlrCursorShapeManagerV1;
 898             // let toplevel_capture_source_manager_cast = self.toplevel_capture_source_manager as *mut WlrExtForeignToplevelImageCaptureSourceManagerV1;
 899 
 900             wl_signal_add(&mut (*renderer_cast).events.lost, &mut self.renderer_lost);
 901             wl_signal_add(&mut (*xdg_shell_cast).events.new_toplevel, &mut self.new_xdg_toplevel);
 902             wl_signal_add(&mut (*xdg_decoration_manager_cast).events.new_toplevel_decoration, &mut self.new_toplevel_decoration);
 903             wl_signal_add(&mut (*xdg_activation_cast).events.request_activate, &mut self.request_activate);
 904             wl_signal_add(&mut (*cursor_shape_manager_cast).events.request_set_shape, &mut self.request_set_cursor_shape);
 905             // wl_signal_add(&mut (*toplevel_capture_source_manager_cast).events.new_request, &mut self.toplevel_capture_request);
 906 
 907             // Register Xwayland surface listener if active
 908             if !self.xwayland.is_null() {
 909                 let new_x = &mut self.new_xsurface as *mut ffi::wl_listener as *mut WlListener;
 910                 (*new_x).notify = Some(handle_new_xwayland_surface);
 911 
 912                 let xwayland_cast = self.xwayland as *mut WlrXwayland;
 913                 wl_signal_add(&mut (*xwayland_cast).events.new_surface, &mut self.new_xsurface);
 914 
 915                 let ready_x = &mut self.xwayland_ready as *mut ffi::wl_listener as *mut WlListener;
 916                 (*ready_x).notify = Some(handle_xwayland_ready);
 917                 wl_signal_add(&mut (*xwayland_cast).events.ready, &mut self.xwayland_ready);
 918             }
 919         }
 920 
 921         Ok(())
 922     }
 923 
 924     pub fn deinit(&mut self) {
 925         unsafe {
 926             log::info!("[deinit] Server::deinit started");
 927             // 1. Terminate all client connections first
 928             log::info!("[deinit] wl_display_destroy_clients started");
 929             ffi::wl_display_destroy_clients(self.wl_server);
 930             log::info!("[deinit] wl_display_destroy_clients finished");
 931 
 932             // 2. Deinitialize subcomponents while backend, renderer, allocator, and display are valid
 933             log::info!("[deinit] self.om.deinit started");
 934             self.om.deinit();
 935             log::info!("[deinit] self.om.deinit finished");
 936 
 937             log::info!("[deinit] self.input_manager.deinit started");
 938             self.input_manager.deinit();
 939             log::info!("[deinit] self.input_manager.deinit finished");
 940 
 941             log::info!("[deinit] deinitializing other subcomponents");
 942             self.idle.deinit();
 943             self.idle_inhibit_manager.deinit();
 944             self.lock_manager.deinit();
 945             self.layer_shell.deinit();
 946             self.inspector.deinit();
 947             self.xkb_bindings.deinit();
 948             self.libinput_config.deinit();
 949             self.xkb_config.deinit();
 950             log::info!("[deinit] other subcomponents deinitialized");
 951 
 952             // 3. Remove signal listeners registered directly by the server
 953             log::info!("[deinit] removing server listeners");
 954             ffi::wl_event_source_remove(self.sigint_source);
 955             ffi::wl_event_source_remove(self.sigterm_source);
 956             if !self.sigchld_source.is_null() {
 957                 ffi::wl_event_source_remove(self.sigchld_source);
 958             }
 959 
 960             wl_listener_remove(&mut self.renderer_lost);
 961             wl_listener_remove(&mut self.new_xdg_toplevel);
 962             wl_listener_remove(&mut self.new_toplevel_decoration);
 963             wl_listener_remove(&mut self.request_activate);
 964             wl_listener_remove(&mut self.request_set_cursor_shape);
 965 
 966             // 4. Destroy Xwayland if active
 967             if !self.xwayland.is_null() {
 968                 wl_listener_remove(&mut self.new_xsurface);
 969                 wl_listener_remove(&mut self.xwayland_ready);
 970                 ffi::wlr_xwayland_destroy(self.xwayland);
 971             }
 972             log::info!("[deinit] server listeners removed");
 973 
 974             // 5. Destroy wlroots core hardware interfaces
 975             log::info!("[deinit] destroying backend");
 976             ffi::wlr_backend_destroy(self.backend);
 977             if !self.session.is_null() {
 978                 log::info!("[deinit] destroying session");
 979                 ffi::wlr_session_destroy(self.session);
 980             }
 981             log::info!("[deinit] destroying renderer");
 982             ffi::wlr_renderer_destroy(self.renderer);
 983             log::info!("[deinit] destroying allocator");
 984             ffi::wlr_allocator_destroy(self.allocator);
 985 
 986             // 6. Finally, destroy the display
 987             log::info!("[deinit] destroying display");
 988             ffi::wl_display_destroy(self.wl_server);
 989             log::info!("[deinit] Server::deinit finished successfully");
 990         }
 991     }
 992 }
 993 
 994 impl Default for Server {
 995     fn default() -> Self {
 996         let mut server = std::mem::MaybeUninit::<Server>::uninit();
 997         unsafe {
 998             // Zero-initialize the memory (C structures and primitive fields)
 999             std::ptr::write_bytes(server.as_mut_ptr(), 0, 1);
1000             // Overwrite collections and SlotMap with valid instances to avoid UB/segfaults from null pointers
1001             std::ptr::write(&mut (*server.as_mut_ptr()).wm.windows, crate::slotmap::SlotMap::new());
1002             std::ptr::write(&mut (*server.as_mut_ptr()).wm.focus_history, Vec::new());
1003             std::ptr::write(&mut (*server.as_mut_ptr()).wm.mode_rules, Vec::new());
1004             std::ptr::write(&mut (*server.as_mut_ptr()).wm.keybinds, Vec::new());
1005             std::ptr::write(&mut (*server.as_mut_ptr()).wm.pointer_binds, Vec::new());
1006             std::ptr::write(&mut (*server.as_mut_ptr()).wm.gesture_binds, Vec::new());
1007             std::ptr::write(&mut (*server.as_mut_ptr()).wm.ipc_rx, None);
1008             // Same reason as ipc_rx above, and not optional: an mpsc endpoint
1009             // has no null niche, so `Option` tags it out of band and zeroed
1010             // bytes decode as `Some(<null channel>)` — dropping that segfaults.
1011             // pending_screenshot holds one too (its deferred IPC reply), which
1012             // is what makes zeroed bytes decode as a live `Some` there as well.
1013             std::ptr::write(&mut (*server.as_mut_ptr()).wm.pending_ipc_reply, None);
1014             std::ptr::write(&mut (*server.as_mut_ptr()).wm.pending_screenshot, None);
1015             std::ptr::write(&mut (*server.as_mut_ptr()).wm.startup, Vec::new());
1016             std::ptr::write(&mut (*server.as_mut_ptr()).wm.startup_pids, Vec::new());
1017             std::ptr::write(&mut (*server.as_mut_ptr()).wm.status_sender, None);
1018             // A zeroed Vec is a null data pointer, which Vec's NonNull
1019             // invariant forbids — the same reason `startup` above is written
1020             // explicitly rather than left to the zeroed MaybeUninit.
1021             std::ptr::write(&mut (*server.as_mut_ptr()).wm.status_backdrops, std::cell::RefCell::new(Vec::new()));
1022             std::ptr::write(&mut (*server.as_mut_ptr()).wm.last_saved_state_json, None);
1023             std::ptr::write(&mut (*server.as_mut_ptr()).layer_shell.surfaces, crate::slotmap::SlotMap::new());
1024             std::ptr::write(&mut (*server.as_mut_ptr()).inspector, crate::inspector::Inspector::new());
1025             std::ptr::write(&mut (*server.as_mut_ptr()).cce_window_management, crate::cce_window_management::CceWindowManagement::new());
1026             server.assume_init()
1027         }
1028     }
1029 }