1 | use wayland_client::{ |
2 | globals::{BindError, GlobalList}, |
3 | protocol::{wl_seat, wl_surface}, |
4 | Dispatch, Proxy, QueueHandle, |
5 | }; |
6 | use wayland_protocols::xdg::activation::v1::client::{xdg_activation_token_v1, xdg_activation_v1}; |
7 | |
8 | use 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)] |
18 | pub 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 |
34 | pub 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 | |
49 | impl 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: &{unknown}, serial: &u32)| (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 |
64 | pub 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)] |
73 | pub struct ActivationState { |
74 | xdg_activation: xdg_activation_v1::XdgActivationV1, |
75 | } |
76 | |
77 | impl 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 | |
132 | impl<D> Dispatch<xdg_activation_v1::XdgActivationV1, GlobalData, D> for ActivationState |
133 | where |
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 | |
148 | impl 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 | |
154 | impl<D, R> Dispatch<xdg_activation_token_v1::XdgActivationTokenV1, R, D> for ActivationState |
155 | where |
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: String } = event { |
169 | state.new_token(token, data); |
170 | } |
171 | } |
172 | } |
173 | |
174 | #[macro_export ] |
175 | macro_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 | } |
201 | |