Skip to main content

smithay_client_toolkit/
foreign_toplevel_list.rs

1use crate::{dispatch2::Dispatch2, globals::GlobalData, registry::GlobalProxy};
2use std::sync::{Arc, Mutex};
3use wayland_client::{globals::GlobalList, Connection, Dispatch, Proxy, QueueHandle};
4use wayland_protocols::ext::foreign_toplevel_list::v1::client::{
5    ext_foreign_toplevel_handle_v1, ext_foreign_toplevel_list_v1,
6};
7
8/// Information about a toplevel.
9#[derive(Clone, Debug, Default)]
10#[non_exhaustive]
11pub struct ForeignToplevelInfo {
12    /// Title
13    pub title: String,
14    /// App id
15    pub app_id: String,
16    /// Identifier to check if two toplevel handles refer to same toplevel
17    pub identifier: String,
18}
19
20#[derive(Debug, Default)]
21struct ForeignToplevelInner {
22    current_info: Option<ForeignToplevelInfo>,
23    pending_info: ForeignToplevelInfo,
24}
25
26#[doc(hidden)]
27#[derive(Debug, Default, Clone)]
28pub struct ForeignToplevelData(Arc<Mutex<ForeignToplevelInner>>);
29
30#[derive(Debug)]
31pub struct ForeignToplevelList {
32    foreign_toplevel_list: GlobalProxy<ext_foreign_toplevel_list_v1::ExtForeignToplevelListV1>,
33    toplevels: Vec<ext_foreign_toplevel_handle_v1::ExtForeignToplevelHandleV1>,
34}
35
36impl ForeignToplevelList {
37    pub fn new<D>(globals: &GlobalList, qh: &QueueHandle<D>) -> Self
38    where
39        D: Dispatch<ext_foreign_toplevel_list_v1::ExtForeignToplevelListV1, GlobalData> + 'static,
40    {
41        let foreign_toplevel_list = GlobalProxy::from(globals.bind(qh, 1..=1, GlobalData));
42        Self { foreign_toplevel_list, toplevels: Vec::new() }
43    }
44
45    /// Returns list of toplevels.
46    pub fn toplevels(&self) -> &[ext_foreign_toplevel_handle_v1::ExtForeignToplevelHandleV1] {
47        &self.toplevels
48    }
49
50    /// Returns information about a toplevel.
51    ///
52    /// This may be none if the toplevel has been destroyed or the compositor has not sent
53    /// information about the toplevel yet.
54    pub fn info(
55        &self,
56        toplevel: &ext_foreign_toplevel_handle_v1::ExtForeignToplevelHandleV1,
57    ) -> Option<ForeignToplevelInfo> {
58        toplevel.data::<ForeignToplevelData>()?.0.lock().unwrap().current_info.clone()
59    }
60
61    pub fn stop(&self) {
62        if let Ok(toplevel_list) = self.foreign_toplevel_list.get() {
63            toplevel_list.stop();
64        }
65    }
66}
67
68/// Handler trait for foreign toplevel list protocol.
69pub trait ForeignToplevelListHandler: Sized {
70    fn foreign_toplevel_list_state(&mut self) -> &mut ForeignToplevelList;
71
72    /// A new toplevel has been opened.
73    fn new_toplevel(
74        &mut self,
75        conn: &Connection,
76        qh: &QueueHandle<Self>,
77        toplevel_handle: ext_foreign_toplevel_handle_v1::ExtForeignToplevelHandleV1,
78    );
79
80    /// An existing toplevel has changed.
81    fn update_toplevel(
82        &mut self,
83        conn: &Connection,
84        qh: &QueueHandle<Self>,
85        toplevel_handle: ext_foreign_toplevel_handle_v1::ExtForeignToplevelHandleV1,
86    );
87
88    /// A toplevel has closed.
89    fn toplevel_closed(
90        &mut self,
91        conn: &Connection,
92        qh: &QueueHandle<Self>,
93        toplevel_handle: ext_foreign_toplevel_handle_v1::ExtForeignToplevelHandleV1,
94    );
95
96    fn finished(&mut self, _conn: &Connection, _qh: &QueueHandle<Self>) {}
97}
98
99impl<D> Dispatch2<ext_foreign_toplevel_list_v1::ExtForeignToplevelListV1, D> for GlobalData
100where
101    D: Dispatch<ext_foreign_toplevel_handle_v1::ExtForeignToplevelHandleV1, ForeignToplevelData>
102        + ForeignToplevelListHandler
103        + 'static,
104{
105    fn event(
106        &self,
107        state: &mut D,
108        proxy: &ext_foreign_toplevel_list_v1::ExtForeignToplevelListV1,
109        event: ext_foreign_toplevel_list_v1::Event,
110        conn: &Connection,
111        qh: &QueueHandle<D>,
112    ) {
113        match event {
114            ext_foreign_toplevel_list_v1::Event::Toplevel { toplevel: _ } => {}
115            ext_foreign_toplevel_list_v1::Event::Finished => {
116                state.finished(conn, qh);
117                proxy.destroy();
118            }
119            _ => unreachable!(),
120        }
121    }
122
123    wayland_client::event_created_child!(D, ext_foreign_toplevel_list_v1::ExtForeignToplevelListV1, [
124        ext_foreign_toplevel_list_v1::EVT_TOPLEVEL_OPCODE => (ext_foreign_toplevel_handle_v1::ExtForeignToplevelHandleV1, Default::default())
125    ]);
126}
127
128impl<D> Dispatch2<ext_foreign_toplevel_handle_v1::ExtForeignToplevelHandleV1, D>
129    for ForeignToplevelData
130where
131    D: ForeignToplevelListHandler,
132{
133    fn event(
134        &self,
135        state: &mut D,
136        handle: &ext_foreign_toplevel_handle_v1::ExtForeignToplevelHandleV1,
137        event: ext_foreign_toplevel_handle_v1::Event,
138        conn: &Connection,
139        qh: &QueueHandle<D>,
140    ) {
141        match event {
142            ext_foreign_toplevel_handle_v1::Event::Closed => {
143                state.toplevel_closed(conn, qh, handle.clone());
144                let toplevels = &mut state.foreign_toplevel_list_state().toplevels;
145                if let Some(idx) = toplevels.iter().position(|x| x == handle) {
146                    toplevels.remove(idx);
147                }
148                handle.destroy();
149            }
150            ext_foreign_toplevel_handle_v1::Event::Done => {
151                let mut inner = self.0.lock().unwrap();
152                let just_created = inner.current_info.is_none();
153                inner.current_info = Some(inner.pending_info.clone());
154                drop(inner);
155                if just_created {
156                    state.foreign_toplevel_list_state().toplevels.push(handle.clone());
157                    state.new_toplevel(conn, qh, handle.clone());
158                } else {
159                    state.update_toplevel(conn, qh, handle.clone());
160                }
161            }
162            ext_foreign_toplevel_handle_v1::Event::Title { title } => {
163                self.0.lock().unwrap().pending_info.title = title;
164            }
165            ext_foreign_toplevel_handle_v1::Event::AppId { app_id } => {
166                self.0.lock().unwrap().pending_info.app_id = app_id;
167            }
168            ext_foreign_toplevel_handle_v1::Event::Identifier { identifier } => {
169                self.0.lock().unwrap().pending_info.identifier = identifier;
170            }
171            _ => unreachable!(),
172        }
173    }
174}