atagen

monolith

public
Star0
Watch0
8e6f96f0a32d329d75ee602576de162d212a6169
1
ssh://brewery@pls.lobotomise.me:33/atagen/monolith.git
crates/monolith/src/handlers.rsRust365 lines11.6 KB
1
use crate::state::{ClientState, State};
2
use smithay::backend::allocator::dmabuf::Dmabuf;
3
use smithay::backend::renderer::ImportDma;
4
use smithay::backend::renderer::utils::{import_surface_tree, on_commit_buffer_handler};
5
use smithay::desktop::{PopupKind, Window};
6
use smithay::input::dnd::{DndGrabHandler, GrabType, Source};
7
use smithay::input::pointer::CursorImageStatus;
8
use smithay::input::{Seat, SeatHandler, SeatState};
9
use smithay::reexports::calloop::Interest;
10
use smithay::reexports::wayland_server::protocol::wl_buffer::WlBuffer;
11
use smithay::reexports::wayland_protocols::xdg::decoration::zv1::server::zxdg_toplevel_decoration_v1::Mode as DecorationMode;
12
use smithay::reexports::wayland_server::protocol::wl_output::WlOutput;
13
use smithay::reexports::wayland_server::protocol::wl_seat::WlSeat;
14
use smithay::reexports::wayland_server::protocol::wl_surface::WlSurface;
15
use smithay::reexports::wayland_server::{Client, Resource};
16
use smithay::utils::Serial;
17
use smithay::wayland::buffer::BufferHandler;
18
use smithay::wayland::compositor::{
19
    BufferAssignment, CompositorClientState, CompositorHandler, CompositorState,
20
    SurfaceAttributes, add_blocker, add_pre_commit_hook, get_parent, is_sync_subsurface,
21
    with_states,
22
};
23
use smithay::wayland::drm_syncobj::{DrmSyncobjCachedState, DrmSyncobjHandler, DrmSyncobjState};
24
use smithay::wayland::dmabuf::{
25
    DmabufGlobal, DmabufHandler, DmabufState, ImportNotifier, get_dmabuf,
26
};
27
use smithay::wayland::output::OutputHandler;
28
use smithay::wayland::selection::SelectionHandler;
29
use smithay::wayland::selection::data_device::{
30
    DataDeviceHandler, DataDeviceState, WaylandDndGrabHandler, set_data_device_focus,
31
};
32
use smithay::wayland::shell::xdg::decoration::XdgDecorationHandler;
33
use smithay::wayland::shell::xdg::{
34
    PopupSurface, PositionerState, ToplevelSurface, XdgShellHandler, XdgShellState,
35
};
36
use monolith_protocols::workspace::{Action, WorkspaceHandler, WorkspaceState};
37
use smithay::wayland::shm::{ShmHandler, ShmState};
38
39
impl CompositorHandler for State {
40
    fn compositor_state(&mut self) -> &mut CompositorState {
41
        &mut self.compositor_state
42
    }
43
44
    fn new_surface(&mut self, surface: &WlSurface) {
45
        add_pre_commit_hook::<Self, _>(surface, |state, _dh, surface| {
46
            hold_until_readable(state, surface);
47
        });
48
    }
49
50
    fn client_compositor_state<'a>(&self, client: &'a Client) -> &'a CompositorClientState {
51
        &client
52
            .get_data::<ClientState>()
53
            .expect("every client has ClientState")
54
            .compositor_state
55
    }
56
57
    fn commit(&mut self, surface: &WlSurface) {
58
        on_commit_buffer_handler::<Self>(surface);
59
        // Importing at commit keeps first-time dmabuf imports, which take
60
        // milliseconds on NVIDIA, out of the render racing the next vblank.
61
        if let Err(e) = import_surface_tree(&mut self.renderer, surface) {
62
            tracing::debug!("early import failed: {e}");
63
        }
64
        if !is_sync_subsurface(surface) {
65
            let mut root = surface.clone();
66
            while let Some(parent) = get_parent(&root) {
67
                root = parent;
68
            }
69
            if let Some(window) = self
70
                .windows
71
                .values()
72
                .chain(&self.unmapped)
73
                .find(|w| w.toplevel().is_some_and(|t| t.wl_surface() == &root))
74
            {
75
                window.on_commit();
76
            }
77
        }
78
        if let Some(i) = self
79
            .unmapped
80
            .iter()
81
            .position(|w| w.toplevel().is_some_and(|t| t.wl_surface() == surface))
82
        {
83
            let window = self.unmapped.remove(i);
84
            let toplevel = window.toplevel().cloned();
85
            self.map_window(window);
86
            if let Some(t) = toplevel {
87
                t.send_configure();
88
            }
89
        }
90
        self.on_buffer(surface);
91
        self.popups.commit(surface);
92
        if let Some(PopupKind::Xdg(popup)) = self.popups.find_popup(surface)
93
            && !popup.is_initial_configure_sent()
94
        {
95
            let _ = popup.send_configure();
96
        }
97
        self.needs_redraw = true;
98
    }
99
}
100
101
impl BufferHandler for State {
102
    fn buffer_destroyed(&mut self, _buffer: &WlBuffer) {}
103
}
104
105
impl ShmHandler for State {
106
    fn shm_state(&self) -> &ShmState {
107
        &self.shm_state
108
    }
109
}
110
111
impl DmabufHandler for State {
112
    fn dmabuf_state(&mut self) -> &mut DmabufState {
113
        &mut self.dmabuf_state
114
    }
115
116
    fn dmabuf_imported(
117
        &mut self,
118
        _global: &DmabufGlobal,
119
        dmabuf: Dmabuf,
120
        notifier: ImportNotifier,
121
    ) {
122
        if self.renderer.import_dmabuf(&dmabuf, None).is_ok() {
123
            let _ = notifier.successful::<State>();
124
        } else {
125
            notifier.failed();
126
        }
127
    }
128
}
129
130
impl XdgShellHandler for State {
131
    fn xdg_shell_state(&mut self) -> &mut XdgShellState {
132
        &mut self.xdg_shell_state
133
    }
134
135
    fn new_toplevel(&mut self, surface: ToplevelSurface) {
136
        self.unmapped.push(Window::new_wayland_window(surface));
137
    }
138
139
    fn toplevel_destroyed(&mut self, surface: ToplevelSurface) {
140
        self.unmap_window(surface.wl_surface());
141
    }
142
143
    fn fullscreen_request(&mut self, surface: ToplevelSurface, _output: Option<WlOutput>) {
144
        self.set_fullscreen(surface.wl_surface(), true);
145
    }
146
147
    fn unfullscreen_request(&mut self, surface: ToplevelSurface) {
148
        self.set_fullscreen(surface.wl_surface(), false);
149
    }
150
151
    fn new_popup(&mut self, surface: PopupSurface, positioner: PositionerState) {
152
        surface.with_pending_state(|s| s.geometry = positioner.get_geometry());
153
        let _ = self.popups.track_popup(PopupKind::Xdg(surface));
154
    }
155
156
    fn reposition_request(
157
        &mut self,
158
        surface: PopupSurface,
159
        positioner: PositionerState,
160
        token: u32,
161
    ) {
162
        surface.with_pending_state(|s| {
163
            s.geometry = positioner.get_geometry();
164
            s.positioner = positioner;
165
        });
166
        surface.send_repositioned(token);
167
    }
168
169
    fn grab(&mut self, _surface: PopupSurface, _seat: WlSeat, _serial: Serial) {}
170
}
171
172
impl SeatHandler for State {
173
    type KeyboardFocus = WlSurface;
174
    type PointerFocus = WlSurface;
175
    type TouchFocus = WlSurface;
176
177
    fn seat_state(&mut self) -> &mut SeatState<State> {
178
        &mut self.seat_state
179
    }
180
181
    fn cursor_image(&mut self, _seat: &Seat<Self>, image: CursorImageStatus) {
182
        self.cursor_status = image;
183
        self.needs_redraw = true;
184
    }
185
186
    fn focus_changed(&mut self, seat: &Seat<Self>, focused: Option<&WlSurface>) {
187
        let client = focused.and_then(|s| self.display.get_client(s.id()).ok());
188
        set_data_device_focus(&self.display, seat, client);
189
    }
190
}
191
192
impl SelectionHandler for State {
193
    type SelectionUserData = ();
194
}
195
196
impl DataDeviceHandler for State {
197
    fn data_device_state(&mut self) -> &mut DataDeviceState {
198
        &mut self.data_device_state
199
    }
200
}
201
202
impl DndGrabHandler for State {}
203
204
impl WaylandDndGrabHandler for State {
205
    fn dnd_requested<S: Source>(
206
        &mut self,
207
        source: S,
208
        _icon: Option<WlSurface>,
209
        _seat: Seat<Self>,
210
        _serial: Serial,
211
        _type: GrabType,
212
    ) {
213
        source.cancel();
214
    }
215
}
216
217
impl OutputHandler for State {}
218
219
impl smithay::wayland::pointer_constraints::PointerConstraintsHandler for State {
220
    fn new_constraint(
221
        &mut self,
222
        _surface: &WlSurface,
223
        _pointer: &smithay::input::pointer::PointerHandle<Self>,
224
    ) {
225
    }
226
227
    fn cursor_position_hint(
228
        &mut self,
229
        _surface: &WlSurface,
230
        _pointer: &smithay::input::pointer::PointerHandle<Self>,
231
        _location: smithay::utils::Point<f64, smithay::utils::Logical>,
232
    ) {
233
    }
234
}
235
236
smithay::delegate_dispatch2!(State);
237
238
impl DrmSyncobjHandler for State {
239
    fn drm_syncobj_state(&mut self) -> Option<&mut DrmSyncobjState> {
240
        self.syncobj.as_mut()
241
    }
242
}
243
244
/// Holds a dmabuf commit until the buffer can be read without the renderer
245
/// stalling on the client's GPU work: until its explicit acquire point
246
/// signals, or for implicitly synced buffers until their writes finish.
247
fn hold_until_readable(state: &mut State, surface: &WlSurface) {
248
    let (acquire, dmabuf) = with_states(surface, |s| {
249
        let acquire = s
250
            .cached_state
251
            .get::<DrmSyncobjCachedState>()
252
            .pending()
253
            .acquire_point
254
            .clone();
255
        let dmabuf = match s
256
            .cached_state
257
            .get::<SurfaceAttributes>()
258
            .pending()
259
            .buffer
260
            .as_ref()
261
        {
262
            Some(BufferAssignment::NewBuffer(buffer)) => get_dmabuf(buffer).cloned().ok(),
263
            _ => None,
264
        };
265
        (acquire, dmabuf)
266
    });
267
    let (Some(dmabuf), Some(client)) = (dmabuf, surface.client()) else {
268
        return;
269
    };
270
    if let Some(point) = acquire
271
        && let Ok((blocker, source)) = point.generate_blocker()
272
    {
273
        let inserted = state.handle.insert_source(source, move |_, _, state| {
274
            let dh = state.display.clone();
275
            state
276
                .client_compositor_state(&client)
277
                .blocker_cleared(state, &dh);
278
            Ok(())
279
        });
280
        if inserted.is_ok() {
281
            add_blocker(surface, blocker);
282
        }
283
        return;
284
    }
285
    if let Ok((blocker, source)) = dmabuf.generate_blocker(Interest::READ) {
286
        let inserted = state.handle.insert_source(source, move |_, _, state| {
287
            let dh = state.display.clone();
288
            state
289
                .client_compositor_state(&client)
290
                .blocker_cleared(state, &dh);
291
            Ok(())
292
        });
293
        if inserted.is_ok() {
294
            add_blocker(surface, blocker);
295
        }
296
    }
297
}
298
299
impl XdgDecorationHandler for State {
300
    fn new_decoration(&mut self, toplevel: ToplevelSurface) {
301
        server_side(&toplevel);
302
    }
303
304
    fn request_mode(&mut self, toplevel: ToplevelSurface, _mode: DecorationMode) {
305
        server_side(&toplevel);
306
    }
307
308
    fn unset_mode(&mut self, toplevel: ToplevelSurface) {
309
        server_side(&toplevel);
310
    }
311
}
312
313
/// monolith draws no titlebars, and clients are told so whatever they ask for.
314
fn server_side(toplevel: &ToplevelSurface) {
315
    toplevel.with_pending_state(|s| s.decoration_mode = Some(DecorationMode::ServerSide));
316
    if toplevel.is_initial_configure_sent() {
317
        toplevel.send_pending_configure();
318
    }
319
}
320
321
impl State {
322
    fn set_fullscreen(&mut self, surface: &WlSurface, on: bool) {
323
        if let Some(id) = self.window_id(surface)
324
            && self.layout.window(id).is_some_and(|w| w.fullscreen != on)
325
        {
326
            self.layout.toggle_fullscreen(id);
327
            self.apply_layout();
328
        }
329
    }
330
}
331
332
impl WorkspaceHandler for State {
333
    fn workspace_state(&mut self) -> &mut WorkspaceState {
334
        &mut self.workspaces
335
    }
336
337
    fn workspace_actions(&mut self, actions: Vec<Action>) {
338
        for action in actions {
339
            let (group, tag) = match &action {
340
                Action::Activate { group, workspace } | Action::Deactivate { group, workspace } => {
341
                    (group, workspace)
342
                }
343
                Action::Create { group, name } => (group, name),
344
            };
345
            let Some(o) = self
346
                .layout
347
                .outputs()
348
                .find(|(_, out)| out.name == *group)
349
                .map(|(o, _)| o)
350
            else {
351
                continue;
352
            };
353
            let t = self.layout.tag(tag);
354
            match action {
355
                Action::Activate { .. } | Action::Create { .. } => self.layout.view(o, t),
356
                Action::Deactivate { .. } => {
357
                    if self.layout.is_viewed(o, t) {
358
                        self.layout.toggle_view(o, t);
359
                    }
360
                }
361
            }
362
        }
363
        self.apply_layout();
364
    }
365
}