smithay/wayland/shell/xdg/handlers/surface/
popup.rs

1use std::sync::atomic::Ordering;
2
3use crate::{
4    input::SeatHandler,
5    utils::Serial,
6    wayland::{
7        compositor,
8        shell::xdg::{SurfaceCachedState, XdgPopupSurfaceData, XdgPositionerUserData},
9    },
10};
11
12use wayland_protocols::xdg::shell::server::xdg_popup::{self, XdgPopup};
13
14use wayland_server::{backend::ClientId, DataInit, Dispatch, DisplayHandle, Resource};
15
16use super::{PopupConfigure, XdgShellHandler, XdgShellState, XdgShellSurfaceUserData, XdgSurfaceUserData};
17
18impl<D> Dispatch<XdgPopup, XdgShellSurfaceUserData, D> for XdgShellState
19where
20    D: Dispatch<XdgPopup, XdgShellSurfaceUserData>,
21    D: XdgShellHandler,
22    D: SeatHandler,
23    D: 'static,
24{
25    fn request(
26        state: &mut D,
27        _client: &wayland_server::Client,
28        popup: &XdgPopup,
29        request: xdg_popup::Request,
30        data: &XdgShellSurfaceUserData,
31        _dh: &DisplayHandle,
32        _data_init: &mut DataInit<'_, D>,
33    ) {
34        match request {
35            xdg_popup::Request::Destroy => {
36                if let Some(surface_data) = data.xdg_surface.data::<XdgSurfaceUserData>() {
37                    surface_data.has_active_role.store(false, Ordering::Release);
38                }
39            }
40            xdg_popup::Request::Grab { seat, serial } => {
41                let handle = crate::wayland::shell::xdg::PopupSurface {
42                    wl_surface: data.wl_surface.clone(),
43                    shell_surface: popup.clone(),
44                };
45
46                let serial = Serial::from(serial);
47
48                XdgShellHandler::grab(state, handle, seat, serial);
49            }
50            xdg_popup::Request::Reposition { positioner, token } => {
51                let handle = crate::wayland::shell::xdg::PopupSurface {
52                    wl_surface: data.wl_surface.clone(),
53                    shell_surface: popup.clone(),
54                };
55
56                let positioner_data = *positioner
57                    .data::<XdgPositionerUserData>()
58                    .unwrap()
59                    .inner
60                    .lock()
61                    .unwrap();
62
63                XdgShellHandler::reposition_request(state, handle, positioner_data, token);
64            }
65            _ => unreachable!(),
66        }
67    }
68
69    fn destroyed(state: &mut D, _client_id: ClientId, xdg_popup: &XdgPopup, data: &XdgShellSurfaceUserData) {
70        data.alive_tracker.destroy_notify();
71
72        // remove this surface from the known ones (as well as any leftover dead surface)
73        if let Some(index) = state
74            .xdg_shell_state()
75            .known_popups
76            .iter()
77            .position(|pop| pop.shell_surface.id() == xdg_popup.id())
78        {
79            let popup = state.xdg_shell_state().known_popups.remove(index);
80            let surface = popup.wl_surface().clone();
81            XdgShellHandler::popup_destroyed(state, popup);
82            compositor::with_states(&surface, |states| {
83                *states
84                    .data_map
85                    .get::<XdgPopupSurfaceData>()
86                    .unwrap()
87                    .lock()
88                    .unwrap() = Default::default();
89
90                let mut guard = states.cached_state.get::<SurfaceCachedState>();
91                *guard.pending() = Default::default();
92                *guard.current() = Default::default();
93            })
94        }
95    }
96}
97
98pub fn send_popup_configure(resource: &XdgPopup, configure: PopupConfigure) {
99    let data = resource.data::<XdgShellSurfaceUserData>().unwrap();
100
101    let serial = configure.serial;
102    let geometry = configure.state.geometry;
103
104    // Send repositioned if token is set
105    if let Some(token) = configure.reposition_token {
106        resource.repositioned(token);
107    }
108
109    // Send the popup configure
110    resource.configure(geometry.loc.x, geometry.loc.y, geometry.size.w, geometry.size.h);
111
112    // Send the base xdg_surface configure event to mark
113    // the configure as finished
114    data.xdg_surface.configure(serial.into());
115}
116
117pub fn make_popup_handle(resource: &XdgPopup) -> crate::wayland::shell::xdg::PopupSurface {
118    let data = resource.data::<XdgShellSurfaceUserData>().unwrap();
119    crate::wayland::shell::xdg::PopupSurface {
120        wl_surface: data.wl_surface.clone(),
121        shell_surface: resource.clone(),
122    }
123}