atagen

monolith

public
Star0
Watch0
9c7ef7f4bc24e8c23d00ac0a80fa55eda797fdab
1
ssh://brewery@pls.lobotomise.me:33/atagen/monolith.git
crates/protocols/src/toplevel.rsRust288 lines8.7 KB
1
//! wlr-foreign-toplevel-management-v1, for taskbars and window titles in
2
//! bars. Like ext-workspace, the compositor publishes a snapshot of its
3
//! windows and each client is sent the difference from what it has seen.
4
5
use smithay::output::Output;
6
use smithay::reexports::wayland_protocols_wlr::foreign_toplevel::v1::server::{
7
    zwlr_foreign_toplevel_handle_v1::{self, ZwlrForeignToplevelHandleV1},
8
    zwlr_foreign_toplevel_manager_v1::{self, ZwlrForeignToplevelManagerV1},
9
};
10
use smithay::reexports::wayland_server::backend::{ClientId, GlobalId};
11
use smithay::reexports::wayland_server::protocol::wl_output::WlOutput;
12
use smithay::reexports::wayland_server::{
13
    Client, DataInit, Dispatch, DisplayHandle, GlobalDispatch, New, Resource,
14
};
15
use smithay::wayland::{Dispatch2, GlobalDispatch2};
16
17
#[derive(Clone, Debug, Default, PartialEq)]
18
pub struct Snapshot {
19
    pub toplevels: Vec<Toplevel>,
20
}
21
22
#[derive(Clone, Debug, PartialEq)]
23
pub struct Toplevel {
24
    /// Stable for the window's lifetime.
25
    pub key: u64,
26
    pub title: String,
27
    pub app_id: String,
28
    pub outputs: Vec<Output>,
29
    pub activated: bool,
30
    pub maximized: bool,
31
    pub fullscreen: bool,
32
}
33
34
#[derive(Clone, Copy, Debug, PartialEq, Eq)]
35
pub enum Action {
36
    Activate(u64),
37
    Close(u64),
38
    Maximize(u64, bool),
39
    Fullscreen(u64, bool),
40
}
41
42
pub trait ForeignToplevelHandler {
43
    fn foreign_toplevel_state(&mut self) -> &mut ForeignToplevelState;
44
    fn foreign_toplevel_action(&mut self, action: Action);
45
}
46
47
struct Sent {
48
    handle: ZwlrForeignToplevelHandleV1,
49
    last: Toplevel,
50
    outputs: Vec<WlOutput>,
51
}
52
53
struct Manager {
54
    handle: ZwlrForeignToplevelManagerV1,
55
    toplevels: Vec<Sent>,
56
}
57
58
pub struct ForeignToplevelState {
59
    global: GlobalId,
60
    managers: Vec<Manager>,
61
    snapshot: Snapshot,
62
}
63
64
pub struct ManagerGlobal;
65
66
pub struct ManagerData;
67
68
pub struct HandleData {
69
    key: u64,
70
}
71
72
impl ForeignToplevelState {
73
    pub fn new<D>(dh: &DisplayHandle) -> Self
74
    where
75
        D: GlobalDispatch<ZwlrForeignToplevelManagerV1, ManagerGlobal> + 'static,
76
    {
77
        Self {
78
            global: dh.create_global::<D, ZwlrForeignToplevelManagerV1, _>(3, ManagerGlobal),
79
            managers: Vec::new(),
80
            snapshot: Snapshot::default(),
81
        }
82
    }
83
84
    pub fn global(&self) -> GlobalId {
85
        self.global.clone()
86
    }
87
88
    pub fn has_clients(&self) -> bool {
89
        !self.managers.is_empty()
90
    }
91
92
    pub fn update<D>(&mut self, dh: &DisplayHandle, snapshot: Snapshot)
93
    where
94
        D: Dispatch<ZwlrForeignToplevelHandleV1, HandleData> + 'static,
95
    {
96
        self.snapshot = snapshot;
97
        self.managers.retain(|m| m.handle.is_alive());
98
        for m in &mut self.managers {
99
            sync::<D>(dh, m, &self.snapshot);
100
        }
101
    }
102
}
103
104
fn sync<D>(dh: &DisplayHandle, m: &mut Manager, snap: &Snapshot)
105
where
106
    D: Dispatch<ZwlrForeignToplevelHandleV1, HandleData> + 'static,
107
{
108
    let Ok(client) = dh.get_client(m.handle.id()) else {
109
        return;
110
    };
111
    m.toplevels.retain(|t| {
112
        let keep = snap.toplevels.iter().any(|s| s.key == t.last.key);
113
        if !keep {
114
            t.handle.closed();
115
        }
116
        keep
117
    });
118
    for top in &snap.toplevels {
119
        let i = match m.toplevels.iter().position(|t| t.last.key == top.key) {
120
            Some(i) => i,
121
            None => {
122
                let Ok(handle) = client.create_resource::<ZwlrForeignToplevelHandleV1, _, D>(
123
                    dh,
124
                    m.handle.version(),
125
                    HandleData { key: top.key },
126
                ) else {
127
                    continue;
128
                };
129
                m.handle.toplevel(&handle);
130
                send_props(&handle, None, top);
131
                m.toplevels.push(Sent {
132
                    handle,
133
                    last: top.clone(),
134
                    outputs: Vec::new(),
135
                });
136
                let sent = m.toplevels.last_mut().expect("just pushed");
137
                sync_outputs(&client, sent, top);
138
                sent.handle.done();
139
                continue;
140
            }
141
        };
142
        let sent = &mut m.toplevels[i];
143
        let mut changed = sync_outputs(&client, sent, top);
144
        if sent.last != *top {
145
            send_props(&sent.handle, Some(&sent.last), top);
146
            sent.last = top.clone();
147
            changed = true;
148
        }
149
        if changed {
150
            sent.handle.done();
151
        }
152
    }
153
}
154
155
/// Sends output enter and leave for the client's wl_outputs. Returns
156
/// whether anything was sent.
157
fn sync_outputs(client: &Client, sent: &mut Sent, top: &Toplevel) -> bool {
158
    let want: Vec<WlOutput> = top
159
        .outputs
160
        .iter()
161
        .flat_map(|o| o.client_outputs(client))
162
        .collect();
163
    let mut changed = false;
164
    sent.outputs.retain(|o| {
165
        let keep = o.is_alive() && want.contains(o);
166
        if !keep && o.is_alive() {
167
            sent.handle.output_leave(o);
168
            changed = true;
169
        }
170
        keep
171
    });
172
    for wl in want {
173
        if !sent.outputs.contains(&wl) {
174
            sent.handle.output_enter(&wl);
175
            sent.outputs.push(wl);
176
            changed = true;
177
        }
178
    }
179
    changed
180
}
181
182
fn send_props(h: &ZwlrForeignToplevelHandleV1, old: Option<&Toplevel>, new: &Toplevel) {
183
    if old.is_none_or(|o| o.title != new.title) {
184
        h.title(new.title.clone());
185
    }
186
    if old.is_none_or(|o| o.app_id != new.app_id) {
187
        h.app_id(new.app_id.clone());
188
    }
189
    let states = |t: &Toplevel| {
190
        let mut s: Vec<u32> = Vec::new();
191
        if t.maximized {
192
            s.push(zwlr_foreign_toplevel_handle_v1::State::Maximized as u32);
193
        }
194
        if t.activated {
195
            s.push(zwlr_foreign_toplevel_handle_v1::State::Activated as u32);
196
        }
197
        if t.fullscreen && h.version() >= 2 {
198
            s.push(zwlr_foreign_toplevel_handle_v1::State::Fullscreen as u32);
199
        }
200
        s
201
    };
202
    if old.is_none_or(|o| states(o) != states(new)) {
203
        h.state(states(new).iter().flat_map(|s| s.to_ne_bytes()).collect());
204
    }
205
}
206
207
impl<D> GlobalDispatch2<ZwlrForeignToplevelManagerV1, D> for ManagerGlobal
208
where
209
    D: ForeignToplevelHandler
210
        + Dispatch<ZwlrForeignToplevelManagerV1, ManagerData>
211
        + Dispatch<ZwlrForeignToplevelHandleV1, HandleData>
212
        + 'static,
213
{
214
    fn bind(
215
        &self,
216
        state: &mut D,
217
        dh: &DisplayHandle,
218
        _client: &Client,
219
        resource: New<ZwlrForeignToplevelManagerV1>,
220
        data_init: &mut DataInit<'_, D>,
221
    ) {
222
        let handle = data_init.init(resource, ManagerData);
223
        let s = state.foreign_toplevel_state();
224
        let mut m = Manager {
225
            handle,
226
            toplevels: Vec::new(),
227
        };
228
        sync::<D>(dh, &mut m, &s.snapshot);
229
        s.managers.push(m);
230
    }
231
}
232
233
impl<D: ForeignToplevelHandler> Dispatch2<ZwlrForeignToplevelManagerV1, D> for ManagerData {
234
    fn request(
235
        &self,
236
        state: &mut D,
237
        _client: &Client,
238
        resource: &ZwlrForeignToplevelManagerV1,
239
        request: zwlr_foreign_toplevel_manager_v1::Request,
240
        _dh: &DisplayHandle,
241
        _data_init: &mut DataInit<'_, D>,
242
    ) {
243
        if let zwlr_foreign_toplevel_manager_v1::Request::Stop = request {
244
            resource.finished();
245
            state
246
                .foreign_toplevel_state()
247
                .managers
248
                .retain(|m| m.handle != *resource);
249
        }
250
    }
251
252
    fn destroyed(&self, state: &mut D, _client: ClientId, resource: &ZwlrForeignToplevelManagerV1) {
253
        state
254
            .foreign_toplevel_state()
255
            .managers
256
            .retain(|m| m.handle != *resource);
257
    }
258
}
259
260
impl<D: ForeignToplevelHandler> Dispatch2<ZwlrForeignToplevelHandleV1, D> for HandleData {
261
    fn request(
262
        &self,
263
        state: &mut D,
264
        _client: &Client,
265
        _resource: &ZwlrForeignToplevelHandleV1,
266
        request: zwlr_foreign_toplevel_handle_v1::Request,
267
        _dh: &DisplayHandle,
268
        _data_init: &mut DataInit<'_, D>,
269
    ) {
270
        use zwlr_foreign_toplevel_handle_v1::Request;
271
        let action = match request {
272
            Request::Activate { .. } => Action::Activate(self.key),
273
            Request::Close => Action::Close(self.key),
274
            Request::SetMaximized => Action::Maximize(self.key, true),
275
            Request::UnsetMaximized => Action::Maximize(self.key, false),
276
            Request::SetFullscreen { .. } => Action::Fullscreen(self.key, true),
277
            Request::UnsetFullscreen => Action::Fullscreen(self.key, false),
278
            _ => return,
279
        };
280
        state.foreign_toplevel_action(action);
281
    }
282
283
    fn destroyed(&self, state: &mut D, _client: ClientId, resource: &ZwlrForeignToplevelHandleV1) {
284
        for m in &mut state.foreign_toplevel_state().managers {
285
            m.toplevels.retain(|t| t.handle != *resource);
286
        }
287
    }
288
}