smithay/wayland/foreign_toplevel_list/
mod.rs
1use std::sync::{Arc, Mutex};
38
39use rand::distributions::{Alphanumeric, DistString};
40use wayland_protocols::ext::foreign_toplevel_list::v1::server::{
41 ext_foreign_toplevel_handle_v1::{self, ExtForeignToplevelHandleV1},
42 ext_foreign_toplevel_list_v1::{self, ExtForeignToplevelListV1},
43};
44use wayland_server::{
45 backend::{ClientId, GlobalId},
46 Client, DataInit, Dispatch, DisplayHandle, GlobalDispatch, New, Resource, Weak,
47};
48
49use crate::utils::user_data::UserDataMap;
50
51pub trait ForeignToplevelListHandler:
53 GlobalDispatch<ExtForeignToplevelListV1, ForeignToplevelListGlobalData>
54 + Dispatch<ExtForeignToplevelListV1, ()>
55 + Dispatch<ExtForeignToplevelHandleV1, ForeignToplevelHandle>
56 + 'static
57{
58 fn foreign_toplevel_list_state(&mut self) -> &mut ForeignToplevelListState;
60}
61
62#[derive(Debug)]
63struct ForeignToplevelHandleInner {
64 identifier: String,
65 title: String,
66 app_id: String,
67 instances: Vec<Weak<ExtForeignToplevelHandleV1>>,
70 closed: bool,
71}
72
73impl ForeignToplevelHandleInner {
74 fn send_closed(&mut self) {
76 if self.closed {
77 return;
78 }
79
80 self.closed = true;
81 for toplevel in self.instances.drain(..) {
83 if let Ok(toplevel) = toplevel.upgrade() {
84 toplevel.closed();
85 }
86 }
87 }
88}
89
90impl Drop for ForeignToplevelHandleInner {
91 fn drop(&mut self) {
92 self.send_closed()
93 }
94}
95
96#[derive(Debug, Clone)]
98pub struct ForeignToplevelWeakHandle {
99 inner: std::sync::Weak<(Mutex<ForeignToplevelHandleInner>, UserDataMap)>,
100}
101
102impl ForeignToplevelWeakHandle {
103 pub fn upgrade(&self) -> Option<ForeignToplevelHandle> {
105 Some(ForeignToplevelHandle {
106 inner: self.inner.upgrade()?,
107 })
108 }
109}
110
111#[derive(Debug, Clone)]
128pub struct ForeignToplevelHandle {
129 inner: Arc<(Mutex<ForeignToplevelHandleInner>, UserDataMap)>,
130}
131
132impl ForeignToplevelHandle {
133 fn new(
134 identifier: String,
135 title: String,
136 app_id: String,
137 instances: Vec<Weak<ExtForeignToplevelHandleV1>>,
138 ) -> Self {
139 Self {
140 inner: Arc::new((
141 Mutex::new(ForeignToplevelHandleInner {
142 identifier,
143 title,
144 app_id,
145 instances,
146 closed: false,
147 }),
148 UserDataMap::new(),
149 )),
150 }
151 }
152
153 pub fn downgrade(&self) -> ForeignToplevelWeakHandle {
155 ForeignToplevelWeakHandle {
156 inner: Arc::downgrade(&self.inner),
157 }
158 }
159
160 pub fn from_resource(resource: &ExtForeignToplevelHandleV1) -> Option<Self> {
162 resource.data::<Self>().cloned()
163 }
164
165 pub fn resources(&self) -> Vec<ExtForeignToplevelHandleV1> {
168 let inner = self.inner.0.lock().unwrap();
169 inner
170 .instances
171 .iter()
172 .filter_map(|weak| weak.upgrade().ok())
173 .collect()
174 }
175
176 pub fn resources_for_client(&self, client: &Client) -> Vec<ExtForeignToplevelHandleV1> {
179 self.resources()
180 .into_iter()
181 .filter(|handle| handle.client().as_ref().is_some_and(|c| c == client))
182 .collect()
183 }
184
185 pub fn user_data(&self) -> &UserDataMap {
187 &self.inner.1
188 }
189
190 pub fn send_title(&self, title: &str) {
194 let mut inner = self.inner.0.lock().unwrap();
195 if inner.title == title {
196 return;
197 }
198
199 inner.title = title.to_string();
200
201 for toplevel in inner.instances.iter() {
202 if let Ok(toplevel) = toplevel.upgrade() {
203 toplevel.title(title.to_string());
204 }
205 }
206 }
207
208 pub fn send_app_id(&self, app_id: &str) {
212 let mut inner = self.inner.0.lock().unwrap();
213 if inner.app_id == app_id {
214 return;
215 }
216
217 inner.app_id = app_id.to_string();
218
219 for toplevel in inner.instances.iter() {
220 if let Ok(toplevel) = toplevel.upgrade() {
221 toplevel.app_id(app_id.to_string());
222 }
223 }
224 }
225
226 pub fn send_done(&self) {
228 let inner = self.inner.0.lock().unwrap();
229 for toplevel in inner.instances.iter() {
230 if let Ok(toplevel) = toplevel.upgrade() {
231 toplevel.done();
232 }
233 }
234 }
235
236 pub fn send_closed(&self) {
238 self.inner.0.lock().unwrap().send_closed();
239 }
240
241 pub fn identifier(&self) -> String {
243 self.inner.0.lock().unwrap().identifier.clone()
244 }
245
246 pub fn title(&self) -> String {
248 self.inner.0.lock().unwrap().title.clone()
249 }
250
251 pub fn app_id(&self) -> String {
253 self.inner.0.lock().unwrap().app_id.clone()
254 }
255
256 pub fn is_closed(&self) -> bool {
258 self.inner.0.lock().unwrap().closed
259 }
260
261 fn init_new_instance(&self, toplevel: ExtForeignToplevelHandleV1) {
262 debug_assert!(
263 !self.is_closed(),
264 "No handles should ever be created for closed toplevel"
265 );
266
267 toplevel.identifier(self.identifier());
268 toplevel.title(self.title());
269 toplevel.app_id(self.app_id());
270 toplevel.done();
271
272 self.inner.0.lock().unwrap().instances.push(toplevel.downgrade());
273 }
274
275 fn remove_instance(&self, instance: &ExtForeignToplevelHandleV1) {
276 let mut inner = self.inner.0.lock().unwrap();
277 if let Some(pos) = inner.instances.iter().position(|i| i == instance) {
278 inner.instances.remove(pos);
279 }
280 }
281}
282
283#[derive(Debug)]
285pub struct ForeignToplevelListState {
286 global: GlobalId,
287 toplevels: Vec<ForeignToplevelWeakHandle>,
288 list_instances: Vec<ExtForeignToplevelListV1>,
289 dh: DisplayHandle,
290}
291
292impl ForeignToplevelListState {
293 pub fn new<D: ForeignToplevelListHandler>(dh: &DisplayHandle) -> Self {
295 Self::new_with_filter::<D>(dh, |_| true)
296 }
297
298 pub fn new_with_filter<D: ForeignToplevelListHandler>(
300 dh: &DisplayHandle,
301 can_view: impl Fn(&Client) -> bool + Send + Sync + 'static,
302 ) -> Self {
303 let global = dh.create_global::<D, ExtForeignToplevelListV1, _>(
304 1,
305 ForeignToplevelListGlobalData {
306 filter: Box::new(can_view),
307 },
308 );
309
310 Self {
311 global,
312 toplevels: Vec::new(),
313 list_instances: Vec::new(),
314 dh: dh.clone(),
315 }
316 }
317
318 pub fn global(&self) -> GlobalId {
320 self.global.clone()
321 }
322
323 pub fn new_toplevel<D: ForeignToplevelListHandler>(
326 &mut self,
327 title: impl Into<String>,
328 app_id: impl Into<String>,
329 ) -> ForeignToplevelHandle {
330 let handle = ForeignToplevelHandle::new(
331 Alphanumeric.sample_string(&mut rand::thread_rng(), 32),
332 title.into(),
333 app_id.into(),
334 Vec::with_capacity(self.list_instances.len()),
335 );
336
337 for instance in &self.list_instances {
338 let Ok(client) = self.dh.get_client(instance.id()) else {
339 continue;
340 };
341
342 let Ok(toplevel) = client.create_resource::<ExtForeignToplevelHandleV1, _, D>(
343 &self.dh,
344 instance.version(),
345 handle.clone(),
346 ) else {
347 continue;
348 };
349
350 instance.toplevel(&toplevel);
351 handle.init_new_instance(toplevel);
352 }
353
354 self.toplevels.push(handle.downgrade());
355
356 handle
357 }
358
359 pub fn remove_toplevel(&mut self, handle: &ForeignToplevelHandle) {
364 handle.send_closed();
365
366 if let Some(pos) = self
367 .toplevels
368 .iter()
369 .filter_map(|h| h.upgrade())
370 .position(|h| Arc::ptr_eq(&h.inner, &handle.inner))
371 {
372 self.toplevels.remove(pos);
373 }
374 }
375
376 pub fn cleanup_closed_handles(&mut self) {
380 self.toplevels.retain(|handle| {
381 let Some(handle) = handle.upgrade() else {
382 return false;
383 };
384 !handle.is_closed()
385 });
386 }
387}
388
389pub struct ForeignToplevelListGlobalData {
391 filter: Box<dyn Fn(&Client) -> bool + Send + Sync>,
392}
393
394impl std::fmt::Debug for ForeignToplevelListGlobalData {
395 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
396 f.debug_struct("ForeignToplevelListGlobalData")
397 .finish_non_exhaustive()
398 }
399}
400
401impl<D: ForeignToplevelListHandler> GlobalDispatch<ExtForeignToplevelListV1, ForeignToplevelListGlobalData, D>
402 for ForeignToplevelListState
403{
404 fn bind(
405 state: &mut D,
406 dh: &DisplayHandle,
407 client: &Client,
408 resource: New<ExtForeignToplevelListV1>,
409 _global_data: &ForeignToplevelListGlobalData,
410 data_init: &mut DataInit<'_, D>,
411 ) {
412 let instance = data_init.init(resource, ());
413
414 let state = state.foreign_toplevel_list_state();
415
416 state.toplevels.retain(|handle| {
417 let Some(handle) = handle.upgrade() else {
418 return false;
420 };
421
422 if handle.is_closed() {
423 return false;
425 }
426
427 if let Ok(toplevel) = client.create_resource::<ExtForeignToplevelHandleV1, _, D>(
428 dh,
429 instance.version(),
430 handle.clone(),
431 ) {
432 instance.toplevel(&toplevel);
433 handle.init_new_instance(toplevel);
434 }
435
436 true
437 });
438
439 state.list_instances.push(instance);
440 }
441
442 fn can_view(client: Client, global_data: &ForeignToplevelListGlobalData) -> bool {
443 (global_data.filter)(&client)
444 }
445}
446
447impl<D: ForeignToplevelListHandler> Dispatch<ExtForeignToplevelListV1, (), D> for ForeignToplevelListState {
448 fn request(
449 state: &mut D,
450 client: &wayland_server::Client,
451 manager: &ExtForeignToplevelListV1,
452 request: ext_foreign_toplevel_list_v1::Request,
453 data: &(),
454 _dh: &DisplayHandle,
455 _data_init: &mut wayland_server::DataInit<'_, D>,
456 ) {
457 match request {
458 ext_foreign_toplevel_list_v1::Request::Stop => {
459 Self::destroyed(state, client.id(), manager, data);
460 manager.finished();
461 }
462 ext_foreign_toplevel_list_v1::Request::Destroy => {}
463 _ => unreachable!(),
464 }
465 }
466
467 fn destroyed(state: &mut D, _client: ClientId, resource: &ExtForeignToplevelListV1, _data: &()) {
468 state
469 .foreign_toplevel_list_state()
470 .list_instances
471 .retain(|i| i != resource);
472 }
473}
474
475impl<D: ForeignToplevelListHandler> Dispatch<ExtForeignToplevelHandleV1, ForeignToplevelHandle, D>
476 for ForeignToplevelListState
477{
478 fn request(
479 _state: &mut D,
480 _client: &wayland_server::Client,
481 _context: &ExtForeignToplevelHandleV1,
482 request: ext_foreign_toplevel_handle_v1::Request,
483 _data: &ForeignToplevelHandle,
484 _dh: &DisplayHandle,
485 _data_init: &mut wayland_server::DataInit<'_, D>,
486 ) {
487 match request {
488 ext_foreign_toplevel_handle_v1::Request::Destroy => {}
489 _ => unreachable!(),
490 }
491 }
492
493 fn destroyed(
494 _state: &mut D,
495 _client: ClientId,
496 resource: &ExtForeignToplevelHandleV1,
497 handle: &ForeignToplevelHandle,
498 ) {
499 handle.remove_instance(resource);
500 }
501}
502
503#[macro_export]
507macro_rules! delegate_foreign_toplevel_list {
508 ($(@<$( $lt:tt $( : $clt:tt $(+ $dlt:tt )* )? ),+>)? $ty: ty) => {
509 $crate::reexports::wayland_server::delegate_global_dispatch!($(@< $( $lt $( : $clt $(+ $dlt )* )? ),+ >)? $ty: [
510 $crate::reexports::wayland_protocols::ext::foreign_toplevel_list::v1::server::ext_foreign_toplevel_list_v1::ExtForeignToplevelListV1: $crate::wayland::foreign_toplevel_list::ForeignToplevelListGlobalData
511 ] => $crate::wayland::foreign_toplevel_list::ForeignToplevelListState);
512 $crate::reexports::wayland_server::delegate_dispatch!($(@< $( $lt $( : $clt $(+ $dlt )* )? ),+ >)? $ty: [
513 $crate::reexports::wayland_protocols::ext::foreign_toplevel_list::v1::server::ext_foreign_toplevel_list_v1::ExtForeignToplevelListV1: ()
514 ] => $crate::wayland::foreign_toplevel_list::ForeignToplevelListState);
515 $crate::reexports::wayland_server::delegate_dispatch!($(@< $( $lt $( : $clt $(+ $dlt )* )? ),+ >)? $ty: [
516 $crate::reexports::wayland_protocols::ext::foreign_toplevel_list::v1::server::ext_foreign_toplevel_handle_v1::ExtForeignToplevelHandleV1: $crate::wayland::foreign_toplevel_list::ForeignToplevelHandle
517 ] => $crate::wayland::foreign_toplevel_list::ForeignToplevelListState);
518 };
519}