Skip to main content

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    dispatch2::Dispatch2,
10    error::GlobalError,
11    globals::{GlobalData, ProvidesBoundGlobal},
12};
13
14/// Minimal implementation of [`RequestDataExt`].
15///
16/// Use a custom type implementing [`RequestDataExt`] to store more data with a token request
17/// e.g. to identify which request produced which token.
18#[derive(Debug, Clone)]
19pub struct RequestData<U> {
20    /// App_id of the application requesting the token, if applicable
21    pub app_id: Option<String>,
22    /// Seat and serial of the window requesting the token, if applicable.
23    ///
24    /// *Warning*: Many compositors will issue invalid tokens for requests without
25    /// recent serials. There is no way to detect this from the client-side.
26    pub seat_and_serial: Option<(wl_seat::WlSeat, u32)>,
27    /// Surface of the window requesting the token, if applicable.
28    ///
29    /// *Warning*: Many compositors will issue invalid tokens for requests from
30    /// unfocused surfaces. There is no way to detect this from the client-side.
31    pub surface: Option<wl_surface::WlSurface>,
32
33    pub udata: U,
34}
35
36/// Handler for xdg-activation
37pub trait ActivationHandler: Sized {
38    /// Data type used for requesting activation tokens
39    // TODO: Default to `()` if default associated types are ever supported
40    type RequestUdata;
41    /// A token was issued for a previous request with `data`.
42    fn new_token(&mut self, token: String, data: &RequestData<Self::RequestUdata>);
43}
44
45/// State for xdg-activation
46#[derive(Debug)]
47pub struct ActivationState {
48    xdg_activation: xdg_activation_v1::XdgActivationV1,
49}
50
51impl ActivationState {
52    /// Bind the `xdg-activation` global
53    pub fn bind<State>(
54        globals: &GlobalList,
55        qh: &QueueHandle<State>,
56    ) -> Result<ActivationState, BindError>
57    where
58        State: Dispatch<xdg_activation_v1::XdgActivationV1, GlobalData, State> + 'static,
59    {
60        let xdg_activation = globals.bind(qh, 1..=1, GlobalData)?;
61        Ok(ActivationState { xdg_activation })
62    }
63
64    /// Activate a surface with the provided token.
65    pub fn activate<D>(&self, surface: &wl_surface::WlSurface, token: String) {
66        self.xdg_activation.activate(token, surface)
67    }
68
69    /// Request a token for surface activation.
70    ///
71    /// To attach custom data to the request implement [`RequestDataExt`] on a custom type
72    /// and use [`Self::request_token_with_data`] instead.
73    pub fn request_token<D, U>(&self, qh: &QueueHandle<D>, request_data: RequestData<U>)
74    where
75        D: ActivationHandler<RequestUdata = U>,
76        D: Dispatch<xdg_activation_token_v1::XdgActivationTokenV1, RequestData<U>> + 'static,
77        U: Send + Sync + 'static,
78    {
79        let token = self.xdg_activation.get_activation_token(qh, request_data);
80        let data = token.data::<RequestData<U>>().unwrap();
81        if let Some(app_id) = &data.app_id {
82            token.set_app_id(String::from(app_id));
83        }
84        if let Some((seat, serial)) = &data.seat_and_serial {
85            token.set_serial(*serial, seat);
86        }
87        if let Some(surface) = &data.surface {
88            token.set_surface(surface);
89        }
90        token.commit();
91    }
92}
93
94impl<D> Dispatch2<xdg_activation_v1::XdgActivationV1, D> for GlobalData
95where
96    D: ActivationHandler,
97{
98    fn event(
99        &self,
100        _: &mut D,
101        _: &xdg_activation_v1::XdgActivationV1,
102        _: <xdg_activation_v1::XdgActivationV1 as Proxy>::Event,
103        _: &wayland_client::Connection,
104        _: &QueueHandle<D>,
105    ) {
106        unreachable!("xdg_activation_v1 has no events");
107    }
108}
109
110impl ProvidesBoundGlobal<xdg_activation_v1::XdgActivationV1, 1> for ActivationState {
111    fn bound_global(&self) -> Result<xdg_activation_v1::XdgActivationV1, GlobalError> {
112        Ok(self.xdg_activation.clone())
113    }
114}
115
116impl<D, U> Dispatch2<xdg_activation_token_v1::XdgActivationTokenV1, D> for RequestData<U>
117where
118    D: ActivationHandler<RequestUdata = U>,
119{
120    fn event(
121        &self,
122        state: &mut D,
123        _proxy: &xdg_activation_token_v1::XdgActivationTokenV1,
124        event: <xdg_activation_token_v1::XdgActivationTokenV1 as Proxy>::Event,
125        _conn: &wayland_client::Connection,
126        _qhandle: &QueueHandle<D>,
127    ) {
128        if let xdg_activation_token_v1::Event::Done { token } = event {
129            state.new_token(token, self);
130        }
131    }
132}