smithay_client_toolkit/
activation.rs

1use wayland_client::{
2    globals::{BindError, GlobalList},
3    protocol::{wl_seat, wl_surface},
4    Dispatch, Proxy, QueueHandle,
5};
6use wayland_protocols::xdg::activation::v1::client::{xdg_activation_token_v1, xdg_activation_v1};
7
8use crate::{
9    error::GlobalError,
10    globals::{GlobalData, ProvidesBoundGlobal},
11};
12
13/// Minimal implementation of [`RequestDataExt`].
14///
15/// Use a custom type implementing [`RequestDataExt`] to store more data with a token request
16/// e.g. to identify which request produced which token.
17#[derive(Debug, Clone)]
18pub struct RequestData {
19    /// App_id of the application requesting the token, if applicable
20    pub app_id: Option<String>,
21    /// Seat and serial of the window requesting the token, if applicable.
22    ///
23    /// *Warning*: Many compositors will issue invalid tokens for requests without
24    /// recent serials. There is no way to detect this from the client-side.
25    pub seat_and_serial: Option<(wl_seat::WlSeat, u32)>,
26    /// Surface of the window requesting the token, if applicable.
27    ///
28    /// *Warning*: Many compositors will issue invalid tokens for requests from
29    /// unfocused surfaces. There is no way to detect this from the client-side.
30    pub surface: Option<wl_surface::WlSurface>,
31}
32
33/// Data attached to a token request
34pub trait RequestDataExt: Send + Sync {
35    /// App_id of the application requesting the token, if applicable
36    fn app_id(&self) -> Option<&str>;
37    /// Seat and serial of the window requesting the token, if applicable.
38    ///
39    /// *Warning*: Many compositors will issue invalid tokens for requests without
40    /// recent serials. There is no way to detect this from the client-side.
41    fn seat_and_serial(&self) -> Option<(&wl_seat::WlSeat, u32)>;
42    /// Surface of the window requesting the token, if applicable.
43    ///
44    /// *Warning*: Many compositors will issue invalid tokens for requests from
45    /// unfocused surfaces. There is no way to detect this from the client-side.
46    fn surface(&self) -> Option<&wl_surface::WlSurface>;
47}
48
49impl RequestDataExt for RequestData {
50    fn app_id(&self) -> Option<&str> {
51        self.app_id.as_deref()
52    }
53
54    fn seat_and_serial(&self) -> Option<(&wl_seat::WlSeat, u32)> {
55        self.seat_and_serial.as_ref().map(|(seat, serial)| (seat, *serial))
56    }
57
58    fn surface(&self) -> Option<&wl_surface::WlSurface> {
59        self.surface.as_ref()
60    }
61}
62
63/// Handler for xdg-activation
64pub trait ActivationHandler: Sized {
65    /// Data type used for requesting activation tokens
66    type RequestData: RequestDataExt;
67    /// A token was issued for a previous request with `data`.
68    fn new_token(&mut self, token: String, data: &Self::RequestData);
69}
70
71/// State for xdg-activation
72#[derive(Debug)]
73pub struct ActivationState {
74    xdg_activation: xdg_activation_v1::XdgActivationV1,
75}
76
77impl ActivationState {
78    /// Bind the `xdg-activation` global
79    pub fn bind<State>(
80        globals: &GlobalList,
81        qh: &QueueHandle<State>,
82    ) -> Result<ActivationState, BindError>
83    where
84        State: Dispatch<xdg_activation_v1::XdgActivationV1, GlobalData, State> + 'static,
85    {
86        let xdg_activation = globals.bind(qh, 1..=1, GlobalData)?;
87        Ok(ActivationState { xdg_activation })
88    }
89
90    /// Activate a surface with the provided token.
91    pub fn activate<D>(&self, surface: &wl_surface::WlSurface, token: String) {
92        self.xdg_activation.activate(token, surface)
93    }
94
95    /// Request a token for surface activation.
96    ///
97    /// To attach custom data to the request implement [`RequestDataExt`] on a custom type
98    /// and use [`Self::request_token_with_data`] instead.
99    pub fn request_token<D>(&self, qh: &QueueHandle<D>, request_data: RequestData)
100    where
101        D: ActivationHandler<RequestData = RequestData>,
102        D: Dispatch<xdg_activation_token_v1::XdgActivationTokenV1, RequestData> + 'static,
103    {
104        Self::request_token_with_data::<D, RequestData>(self, qh, request_data)
105    }
106
107    /// Request a token for surface activation with user data.
108    ///
109    /// To use this method you need to provide [`delegate_activation`] with your custom type.
110    /// E.g. `delegate_activation!(SimpleWindow, MyRequestData);`
111    pub fn request_token_with_data<D, R>(&self, qh: &QueueHandle<D>, request_data: R)
112    where
113        D: ActivationHandler<RequestData = R>,
114        D: Dispatch<xdg_activation_token_v1::XdgActivationTokenV1, R> + 'static,
115        R: RequestDataExt + 'static,
116    {
117        let token = self.xdg_activation.get_activation_token(qh, request_data);
118        let data = token.data::<R>().unwrap();
119        if let Some(app_id) = data.app_id() {
120            token.set_app_id(String::from(app_id));
121        }
122        if let Some((seat, serial)) = data.seat_and_serial() {
123            token.set_serial(serial, seat);
124        }
125        if let Some(surface) = data.surface() {
126            token.set_surface(surface);
127        }
128        token.commit();
129    }
130}
131
132impl<D> Dispatch<xdg_activation_v1::XdgActivationV1, GlobalData, D> for ActivationState
133where
134    D: Dispatch<xdg_activation_v1::XdgActivationV1, GlobalData> + ActivationHandler,
135{
136    fn event(
137        _: &mut D,
138        _: &xdg_activation_v1::XdgActivationV1,
139        _: <xdg_activation_v1::XdgActivationV1 as Proxy>::Event,
140        _: &GlobalData,
141        _: &wayland_client::Connection,
142        _: &QueueHandle<D>,
143    ) {
144        unreachable!("xdg_activation_v1 has no events");
145    }
146}
147
148impl ProvidesBoundGlobal<xdg_activation_v1::XdgActivationV1, 1> for ActivationState {
149    fn bound_global(&self) -> Result<xdg_activation_v1::XdgActivationV1, GlobalError> {
150        Ok(self.xdg_activation.clone())
151    }
152}
153
154impl<D, R> Dispatch<xdg_activation_token_v1::XdgActivationTokenV1, R, D> for ActivationState
155where
156    D: Dispatch<xdg_activation_token_v1::XdgActivationTokenV1, R>
157        + ActivationHandler<RequestData = R>,
158    R: RequestDataExt,
159{
160    fn event(
161        state: &mut D,
162        _proxy: &xdg_activation_token_v1::XdgActivationTokenV1,
163        event: <xdg_activation_token_v1::XdgActivationTokenV1 as Proxy>::Event,
164        data: &R,
165        _conn: &wayland_client::Connection,
166        _qhandle: &QueueHandle<D>,
167    ) {
168        if let xdg_activation_token_v1::Event::Done { token } = event {
169            state.new_token(token, data);
170        }
171    }
172}
173
174#[macro_export]
175macro_rules! delegate_activation {
176   ($(@<$( $lt:tt $( : $clt:tt $(+ $dlt:tt )* )? ),+>)? $ty: ty) => {
177        $crate::reexports::client::delegate_dispatch!($(@< $( $lt $( : $clt $(+ $dlt )* )? ),+ >)? $ty:
178            [
179                $crate::reexports::protocols::xdg::activation::v1::client::xdg_activation_v1::XdgActivationV1: $crate::globals::GlobalData
180            ] => $crate::activation::ActivationState
181        );
182        $crate::reexports::client::delegate_dispatch!($(@< $( $lt $( : $clt $(+ $dlt )* )? ),+ >)? $ty:
183            [
184                $crate::reexports::protocols::xdg::activation::v1::client::xdg_activation_token_v1::XdgActivationTokenV1: $crate::activation::RequestData
185            ] => $crate::activation::ActivationState
186        );
187    };
188   ($(@<$( $lt:tt $( : $clt:tt $(+ $dlt:tt )* )? ),+>)? $ty: ty, $data: ty) => {
189        $crate::reexports::client::delegate_dispatch!($(@< $( $lt $( : $clt $(+ $dlt )* )? ),+ >)? $ty:
190            [
191                $crate::reexports::protocols::xdg::activation::v1::client::xdg_activation_v1::XdgActivationV1: $crate::globals::GlobalData
192            ] => $crate::activation::ActivationState
193        );
194        $crate::reexports::client::delegate_dispatch!($(@< $( $lt $( : $clt $(+ $dlt )* )? ),+ >)? $ty:
195            [
196                $crate::reexports::protocols::xdg::activation::v1::client::xdg_activation_token_v1::XdgActivationTokenV1: $data
197            ] => $crate::activation::ActivationState
198        );
199    };
200}