<NME> toplevel.rs
<BEF> use std::sync::atomic::Ordering;

use crate::{
    utils::Serial,
    wayland::{compositor, shell::xdg::XdgToplevelSurfaceData},
};

use wayland_protocols::xdg::shell::server::xdg_toplevel::{self, XdgToplevel};

use wayland_server::{
};

use super::{
    SurfaceCachedState, SurfaceKind, ToplevelConfigure, XdgShellHandler, XdgShellState,
    XdgShellSurfaceUserData, XdgSurfaceUserData, XdgToplevelSurfaceRoleAttributes,
};

impl<D> DelegateDispatch<XdgToplevel, XdgShellSurfaceUserData, D> for XdgShellState
};

impl<D> Dispatch<XdgToplevel, XdgShellSurfaceUserData, D> for XdgShellState
where
    D: Dispatch<XdgToplevel, XdgShellSurfaceUserData>,
    D: XdgShellHandler,
    D: 'static,
{
    fn request(
        state: &mut D,
        _client: &wayland_server::Client,
        toplevel: &XdgToplevel,
        request: xdg_toplevel::Request,
    ) {
        match request {
            xdg_toplevel::Request::Destroy => {
                // all it done by the destructor
            }
            xdg_toplevel::Request::SetParent { parent } => {
                let parent_surface = parent.map(|toplevel_surface_parent| {
            }
            xdg_toplevel::Request::SetParent { parent } => {
                let parent_surface = parent.map(|toplevel_surface_parent| {
                    toplevel_surface_parent
                        .data::<XdgShellSurfaceUserData>()
                        .unwrap()
                        .wl_surface
                        .clone()
                });

                // Parent is not double buffered, we can set it directly
                set_parent(toplevel, parent_surface);
            }
            xdg_toplevel::Request::SetTitle { title } => {
                // Title is not double buffered, we can set it directly
                with_surface_toplevel_role_data(toplevel, |data| {
                    data.title = Some(title);
                });
            }
            xdg_toplevel::Request::SetAppId { app_id } => {
                // AppId is not double buffered, we can set it directly
                with_surface_toplevel_role_data(toplevel, |role| {
                    role.app_id = Some(app_id);
                });
            }
            xdg_toplevel::Request::ShowWindowMenu { seat, serial, x, y } => {
                // This has to be handled by the compositor
                let handle = make_toplevel_handle(toplevel);
                let serial = Serial::from(serial);

                XdgShellHandler::show_window_menu(state, handle, seat, serial, (x, y).into());
            }
            xdg_toplevel::Request::Move { seat, serial } => {
                // This has to be handled by the compositor
                let handle = make_toplevel_handle(toplevel);
                let serial = Serial::from(serial);

                XdgShellHandler::move_request(state, handle, seat, serial);
            }
            xdg_toplevel::Request::Resize { seat, serial, edges } => {
                if let WEnum::Value(edges) = edges {
                    // This has to be handled by the compositor
                    let handle = make_toplevel_handle(toplevel);
                    let serial = Serial::from(serial);

                    XdgShellHandler::resize_request(state, handle, seat, serial, edges);
                }
            }
            xdg_toplevel::Request::SetMaxSize { width, height } => {
                with_toplevel_pending_state(data, |toplevel_data| {
                    toplevel_data.max_size = (width, height).into();
                });
            }
            xdg_toplevel::Request::SetMinSize { width, height } => {
                with_toplevel_pending_state(data, |toplevel_data| {
                    toplevel_data.min_size = (width, height).into();
                });
            }
            xdg_toplevel::Request::SetMaximized => {
                let handle = make_toplevel_handle(toplevel);
                XdgShellHandler::maximize_request(state, handle);
            }
            xdg_toplevel::Request::UnsetMaximized => {
                let handle = make_toplevel_handle(toplevel);
                XdgShellHandler::unmaximize_request(state, handle);
            }
            xdg_toplevel::Request::SetFullscreen { output } => {
                let handle = make_toplevel_handle(toplevel);
                XdgShellHandler::fullscreen_request(state, handle, output);
            }
            xdg_toplevel::Request::UnsetFullscreen => {
                let handle = make_toplevel_handle(toplevel);
                XdgShellHandler::unfullscreen_request(state, handle);
            }
            xdg_toplevel::Request::SetMinimized => {
                // This has to be handled by the compositor, may not be
                // supported and just ignored
                let handle = make_toplevel_handle(toplevel);
                XdgShellHandler::minimize_request(state, handle);
            }
            _ => unreachable!(),
    fn destroyed(_state: &mut D, _client_id: ClientId, object_id: ObjectId, data: &XdgShellSurfaceUserData) {
        data.alive_tracker.destroy_notify();

        if let Some(surface_data) = data.xdg_surface.data::<XdgSurfaceUserData>() {
            surface_data.has_active_role.store(false, Ordering::Release);
        }

        match &data.kind {
            SurfaceKind::Toplevel => {
                // remove this surface from the known ones (as well as any leftover dead surface)
                data.shell_data
                    .lock()
                    .unwrap()
                    .known_toplevels
                    .retain(|other| other.shell_surface.id() != object_id);
            }
            SurfaceKind::Popup => {
                // remove this surface from the known ones (as well as any leftover dead surface)
                data.shell_data
                    .lock()
                    .unwrap()
                    .known_popups
                    .retain(|other| other.shell_surface.id() != object_id);
            }
        }
    }
}

            .retain(|other| other.shell_surface.id() != object_id);

        let mut shell_data = data.shell_data.lock().unwrap();
        if let Some(index) = shell_data
            .known_toplevels
            .iter()
            .position(|top| top.shell_surface.id() == object_id)
        {
            let toplevel = shell_data.known_toplevels.remove(index);
            drop(shell_data);
            XdgShellHandler::toplevel_destroyed(state, toplevel);
        }
    }
}

// Utility functions allowing to factor out a lot of the upcoming logic
fn with_surface_toplevel_role_data<F, T>(toplevel: &xdg_toplevel::XdgToplevel, f: F) -> T
where
    F: FnOnce(&mut XdgToplevelSurfaceRoleAttributes) -> T,
{
    let data = toplevel.data::<XdgShellSurfaceUserData>().unwrap();
    compositor::with_states(&data.wl_surface, |states| {
        f(&mut *states
            .data_map
            .get::<XdgToplevelSurfaceData>()
            .unwrap()
            .lock()
            .unwrap())
    })
}

pub(super) fn make_toplevel_handle(
    resource: &xdg_toplevel::XdgToplevel,
) -> crate::wayland::shell::xdg::ToplevelSurface {
    let data = resource.data::<XdgShellSurfaceUserData>().unwrap();
    crate::wayland::shell::xdg::ToplevelSurface {
        wl_surface: data.wl_surface.clone(),
        shell_surface: resource.clone(),
    }
}

pub fn get_parent(toplevel: &xdg_toplevel::XdgToplevel) -> Option<wl_surface::WlSurface> {
    with_surface_toplevel_role_data(toplevel, |data| data.parent.clone())
}

/// Sets the parent of the specified toplevel surface.
///
/// The parent must be a toplevel surface.
///
/// The parent of a surface is not double buffered and therefore may be set directly.
///
/// If the parent is `None`, the parent-child relationship is removed.
pub fn set_parent(toplevel: &xdg_toplevel::XdgToplevel, parent: Option<wl_surface::WlSurface>) {
    with_surface_toplevel_role_data(toplevel, |data| {
        data.parent = parent;
    });
}

fn with_toplevel_pending_state<F, T>(data: &XdgShellSurfaceUserData, f: F) -> T
where
    F: FnOnce(&mut SurfaceCachedState) -> T,
{
    compositor::with_states(&data.wl_surface, |states| {
        f(&mut *states.cached_state.pending::<SurfaceCachedState>())
    })
}

pub fn send_toplevel_configure(resource: &xdg_toplevel::XdgToplevel, configure: ToplevelConfigure) {
    let data = resource.data::<XdgShellSurfaceUserData>().unwrap();
    let (width, height) = configure.state.size.unwrap_or_default().into();
    // convert the Vec<State> (which is really a Vec<u32>) into Vec<u8>
    let states = {
        let mut states: Vec<xdg_toplevel::State> =
            configure.state.states.into_filtered_states(resource.version());
        let ptr = states.as_mut_ptr();
        let len = states.len();
        let cap = states.capacity();
        ::std::mem::forget(states);
        unsafe { Vec::from_raw_parts(ptr as *mut u8, len * 4, cap * 4) }
    };
    let serial = configure.serial;

    // Send the toplevel configure
    resource.configure(width, height, states);

    // Send the base xdg_surface configure event to mark
    // The configure as finished
    data.xdg_surface.configure(serial.into());
}

<MSG> fix xdg destroy protocol checks

moves the xdg destroy checks from the
destoryed handler to the destroy request
handler. this is done to ensure sequential processing
of the destroy events as the destroyed handler
are executed delayed and do not guarantee
ordering. ordering is important as the protocol
specifies that a xdg role object has to be destroyed
before destroying the xdg surface

<DFF> @@ -11,8 +11,8 @@ use wayland_server::{
 };
 
 use super::{
-    SurfaceCachedState, SurfaceKind, ToplevelConfigure, XdgShellHandler, XdgShellState,
-    XdgShellSurfaceUserData, XdgSurfaceUserData, XdgToplevelSurfaceRoleAttributes,
+    SurfaceCachedState, ToplevelConfigure, XdgShellHandler, XdgShellState, XdgShellSurfaceUserData,
+    XdgSurfaceUserData, XdgToplevelSurfaceRoleAttributes,
 };
 
 impl<D> DelegateDispatch<XdgToplevel, XdgShellSurfaceUserData, D> for XdgShellState
@@ -32,7 +32,9 @@ where
     ) {
         match request {
             xdg_toplevel::Request::Destroy => {
-                // all it done by the destructor
+                if let Some(surface_data) = data.xdg_surface.data::<XdgSurfaceUserData>() {
+                    surface_data.has_active_role.store(false, Ordering::Release);
+                }
             }
             xdg_toplevel::Request::SetParent { parent } => {
                 let parent_surface = parent.map(|toplevel_surface_parent| {
@@ -120,28 +122,12 @@ where
     fn destroyed(_state: &mut D, _client_id: ClientId, object_id: ObjectId, data: &XdgShellSurfaceUserData) {
         data.alive_tracker.destroy_notify();
 
-        if let Some(surface_data) = data.xdg_surface.data::<XdgSurfaceUserData>() {
-            surface_data.has_active_role.store(false, Ordering::Release);
-        }
-
-        match &data.kind {
-            SurfaceKind::Toplevel => {
-                // remove this surface from the known ones (as well as any leftover dead surface)
-                data.shell_data
-                    .lock()
-                    .unwrap()
-                    .known_toplevels
-                    .retain(|other| other.shell_surface.id() != object_id);
-            }
-            SurfaceKind::Popup => {
-                // remove this surface from the known ones (as well as any leftover dead surface)
-                data.shell_data
-                    .lock()
-                    .unwrap()
-                    .known_popups
-                    .retain(|other| other.shell_surface.id() != object_id);
-            }
-        }
+        // remove this surface from the known ones (as well as any leftover dead surface)
+        data.shell_data
+            .lock()
+            .unwrap()
+            .known_toplevels
+            .retain(|other| other.shell_surface.id() != object_id);
     }
 }
 

fix xdg destroy protocol checks