atagen

monolith

public
Star0
Watch0

Commit 845a622a162f

tarball

atagen <boss@atagen.co>2026-10-17 02:49 UTC

11 files changed+152-81
monolith: make the backend an enum

The DRM and nested backends were two Options on State, plus fields that only one of them filled: the nested output id, the render node and the syncobj global. Each now lives in its backend, and after_dispatch renders the one that runs instead of calling both and letting one return early. The set is closed, and a headless variant for tests is the only addition planned.
Added file: crates/monolith/src/backend.rs+64 additions
@@ -0,0 +1,64 @@
1+//! Where frames go and input comes from. The set is closed: bare metal and
2+//! a window on another compositor, and later a headless one for tests.
3+
4+use crate::drm::Drm;
5+use crate::nested::Host;
6+use monolith_layout::OutputId;
7+use smithay::backend::drm::DrmNode;
8+use smithay::output::Output;
9+use smithay::wayland::drm_syncobj::DrmSyncobjState;
10+
11+pub enum Backend {
12+ /// Before `drm::init` or `nested::init` has run.
13+ Starting,
14+ Drm(Box<Drm>),
15+ Nested(Box<Host>),
16+}
17+
18+impl Backend {
19+ pub fn drm(&self) -> Option<&Drm> {
20+ match self {
21+ Backend::Drm(d) => Some(d),
22+ _ => None,
23+ }
24+ }
25+
26+ pub fn drm_mut(&mut self) -> Option<&mut Drm> {
27+ match self {
28+ Backend::Drm(d) => Some(d),
29+ _ => None,
30+ }
31+ }
32+
33+ pub fn nested(&self) -> Option<&Host> {
34+ match self {
35+ Backend::Nested(h) => Some(h),
36+ _ => None,
37+ }
38+ }
39+
40+ pub fn nested_mut(&mut self) -> Option<&mut Host> {
41+ match self {
42+ Backend::Nested(h) => Some(h),
43+ _ => None,
44+ }
45+ }
46+
47+ /// The smithay output behind a layout output.
48+ pub fn output(&self, o: OutputId) -> Option<Output> {
49+ match self {
50+ Backend::Drm(d) => d.output(o),
51+ Backend::Nested(h) => (h.output_id == o).then(|| h.output.clone()),
52+ Backend::Starting => None,
53+ }
54+ }
55+
56+ /// The node clients allocate dmabufs on, known on bare metal.
57+ pub fn render_node(&self) -> Option<DrmNode> {
58+ self.drm().map(|d| d.render_node)
59+ }
60+
61+ pub fn syncobj_mut(&mut self) -> Option<&mut DrmSyncobjState> {
62+ self.drm_mut()?.syncobj.as_mut()
63+ }
64+}
Modified file: crates/monolith/src/capture.rs+2 additions−2 deletions
@@ -163,7 +163,7 @@ impl State {
163163 }
164164
165165 fn constraints(&self, size: (i32, i32)) -> BufferConstraints {
166- let dma = self.render_node.map(|node| {
166+ let dma = self.backend.render_node().map(|node| {
167167 let mut formats: Vec<(Fourcc, Vec<_>)> = Vec::new();
168168 for f in self.renderer.render_formats().iter() {
169169 match formats.iter_mut().find(|(c, _)| *c == f.code) {
@@ -302,7 +302,7 @@ impl State {
302302 fn output_named(&self, name: &str) -> Option<(OutputId, Output)> {
303303 self.layout
304304 .outputs()
305- .filter_map(|(o, _)| Some((o, self.smithay_output(o)?)))
305+ .filter_map(|(o, _)| Some((o, self.backend.output(o)?)))
306306 .find(|(_, out)| out.name() == name)
307307 }
308308 }
Modified file: crates/monolith/src/commands.rs+1 additions−1 deletions
@@ -176,7 +176,7 @@ impl State {
176176 if let Some(sock) = &self.services.ipc.path {
177177 cmd.env("MONOLITH_SOCKET", sock);
178178 }
179- if self.drm.is_some() {
179+ if self.backend.drm().is_some() {
180180 cmd.env("XDG_CURRENT_DESKTOP", "monolith")
181181 .env("XDG_SESSION_TYPE", "wayland");
182182 }
Modified file: crates/monolith/src/drm.rs+45 additions−27 deletions
@@ -2,6 +2,7 @@
22 //! connectors, libinput for input, and one smithay DRM output per connector,
33 //! each rendered when its own vblank lands.
44
5+use crate::backend::Backend;
56 use crate::scene::Draw;
67 use crate::state::Services;
78 use crate::state::{State, now_ns};
@@ -54,6 +55,8 @@ pub struct Drm {
5455 libinput: Libinput,
5556 handle: LoopHandle<'static, State>,
5657 node: DrmNode,
58+ pub render_node: DrmNode,
59+ pub syncobj: Option<DrmSyncobjState>,
5760 manager: Manager,
5861 scanner: DrmScanner,
5962 nvidia: bool,
@@ -193,20 +196,20 @@ pub fn init(
193196 .get_driver()
194197 .map(|d| d.name().to_string_lossy().to_lowercase().contains("nvidia"))
195198 .unwrap_or(false);
196- if supports_syncobj_eventfd(&fd) {
197- state.syncobj = Some(DrmSyncobjState::new::<State>(
199+ let syncobj = if supports_syncobj_eventfd(&fd) {
200+ Some(DrmSyncobjState::new::<State>(
198201 &state.core.display,
199202 fd.clone(),
200- ));
203+ ))
201204 } else {
202205 tracing::info!("no syncobj eventfd support, clients stay on implicit sync");
203- }
206+ None
207+ };
204208 let gbm = GbmDevice::new(fd)?;
205209 let render_node = node
206210 .node_with_type(NodeType::Render)
207211 .and_then(Result::ok)
208212 .unwrap_or(node);
209- state.render_node = Some(render_node);
210213 let allocator = GbmAllocator::new(
211214 gbm.clone(),
212215 GbmBufferFlags::RENDERING | GbmBufferFlags::SCANOUT,
@@ -236,13 +239,13 @@ pub fn init(
236239 )?;
237240 handle.insert_source(notifier, |event, _, state| match event {
238241 SessionEvent::PauseSession => {
239- if let Some(drm) = state.drm.as_mut() {
242+ if let Some(drm) = state.backend.drm_mut() {
240243 drm.libinput.suspend();
241244 drm.manager.pause();
242245 }
243246 }
244247 SessionEvent::ActivateSession => {
245- if let Some(drm) = state.drm.as_mut() {
248+ if let Some(drm) = state.backend.drm_mut() {
246249 if drm.libinput.resume().is_err() {
247250 tracing::warn!("libinput did not resume");
248251 }
@@ -265,19 +268,21 @@ pub fn init(
265268 handle.insert_source(udev, move |event, _, state| {
266269 if let UdevEvent::Changed { device_id } = event
267270 && state
268- .drm
269- .as_ref()
271+ .backend
272+ .drm()
270273 .is_some_and(|d| d.node.dev_id() == device_id)
271274 {
272275 scan(state);
273276 }
274277 })?;
275278
276- state.drm = Some(Drm {
279+ state.backend = Backend::Drm(Box::new(Drm {
277280 session,
278281 libinput,
279282 handle,
280283 node,
284+ render_node,
285+ syncobj,
281286 manager,
282287 scanner: DrmScanner::new(),
283288 nvidia,
@@ -287,14 +292,14 @@ pub fn init(
287292 FrameFlags::DEFAULT
288293 },
289294 surfaces: HashMap::new(),
290- });
295+ }));
291296 scan(state);
292297 Ok(())
293298 }
294299
295300 /// Brings outputs in line with what is plugged in.
296301 fn scan(state: &mut State) {
297- let Some(drm) = state.drm.as_mut() else {
302+ let Some(drm) = state.backend.drm_mut() else {
298303 return;
299304 };
300305 let events: Vec<DrmScanEvent> = match drm.scanner.scan_connectors(drm.manager.device()) {
@@ -335,7 +340,7 @@ fn connect(state: &mut State, connector: connector::Info, crtc: crtc::Handle) {
335340 return;
336341 };
337342 let State {
338- drm: Some(drm),
343+ backend: Backend::Drm(drm),
339344 renderer,
340345 layout,
341346 space,
@@ -431,7 +436,7 @@ fn connect(state: &mut State, connector: connector::Info, crtc: crtc::Handle) {
431436 }
432437
433438 fn disconnect(state: &mut State, crtc: crtc::Handle) {
434- let Some(drm) = state.drm.as_mut() else {
439+ let Some(drm) = state.backend.drm_mut() else {
435440 return;
436441 };
437442 let Some(surface) = drm.surfaces.remove(&crtc) else {
@@ -445,16 +450,21 @@ fn disconnect(state: &mut State, crtc: crtc::Handle) {
445450 state.layout.remove_output(surface.id);
446451 }
447452
448-/// The smithay output for a layout output, for protocols that need one.
449-pub fn output_for(drm: &Drm, id: OutputId) -> Option<Output> {
450- drm.surfaces
451- .values()
452- .find(|s| s.id == id)
453- .map(|s| s.output.clone())
453+impl Drm {
454+ pub fn output(&self, id: OutputId) -> Option<Output> {
455+ self.surfaces
456+ .values()
457+ .find(|s| s.id == id)
458+ .map(|s| s.output.clone())
459+ }
454460 }
455461
456462 fn on_vblank(state: &mut State, crtc: crtc::Handle, meta: &mut Option<DrmEventMetadata>) {
457- let Some(surface) = state.drm.as_mut().and_then(|d| d.surfaces.get_mut(&crtc)) else {
463+ let Some(surface) = state
464+ .backend
465+ .drm_mut()
466+ .and_then(|d| d.surfaces.get_mut(&crtc))
467+ else {
458468 return;
459469 };
460470 if let Err(e) = surface.drm.frame_submitted() {
@@ -464,7 +474,11 @@ fn on_vblank(state: &mut State, crtc: crtc::Handle, meta: &mut Option<DrmEventMe
464474 if let Err(e) = state.renderer.absorb_submit_stall() {
465475 tracing::warn!("absorbing a submit stall failed: {e}");
466476 }
467- let Some(surface) = state.drm.as_mut().and_then(|d| d.surfaces.get_mut(&crtc)) else {
477+ let Some(surface) = state
478+ .backend
479+ .drm_mut()
480+ .and_then(|d| d.surfaces.get_mut(&crtc))
481+ else {
468482 return;
469483 };
470484 if let Some(DrmEventTime::Monotonic(t)) = meta.as_ref().map(|m| m.time) {
@@ -506,7 +520,7 @@ fn on_vblank(state: &mut State, crtc: crtc::Handle, meta: &mut Option<DrmEventMe
506520 /// Starts a render at the last moment that still makes the next reachable
507521 /// vblank, so commits arriving until then make it into the frame.
508522 fn schedule(state: &mut State, crtc: crtc::Handle) {
509- let Some(drm) = state.drm.as_mut() else {
523+ let Some(drm) = state.backend.drm_mut() else {
510524 return;
511525 };
512526 let Some(surface) = drm.surfaces.get_mut(&crtc) else {
@@ -526,7 +540,11 @@ fn schedule(state: &mut State, crtc: crtc::Handle) {
526540 }
527541 let wait = Timer::from_duration(Duration::from_nanos(CURSOR_FRAME_NS - since_flip));
528542 let token = drm.handle.insert_source(wait, move |_, _, state| {
529- if let Some(s) = state.drm.as_mut().and_then(|d| d.surfaces.get_mut(&crtc)) {
543+ if let Some(s) = state
544+ .backend
545+ .drm_mut()
546+ .and_then(|d| d.surfaces.get_mut(&crtc))
547+ {
530548 s.scheduled = None;
531549 }
532550 draw(state, crtc, now_ns());
@@ -542,7 +560,7 @@ fn schedule(state: &mut State, crtc: crtc::Handle) {
542560 surface.target_ns = Some(vblank);
543561 let wait = Timer::from_duration(Duration::from_nanos(start.saturating_sub(now)));
544562 let token = drm.handle.insert_source(wait, move |_, _, state| {
545- let target = state.drm.as_mut().and_then(|d| {
563+ let target = state.backend.drm_mut().and_then(|d| {
546564 let s = d.surfaces.get_mut(&crtc)?;
547565 s.scheduled = None;
548566 s.target_ns
@@ -563,7 +581,7 @@ fn draw(state: &mut State, crtc: crtc::Handle, target: u64) {
563581
564582 /// Renders every output that has something new and no frame in flight.
565583 pub fn render(state: &mut State) {
566- let Some(drm) = state.drm.as_mut() else {
584+ let Some(drm) = state.backend.drm_mut() else {
567585 return;
568586 };
569587 let cursor = std::mem::take(&mut state.cursor_moved);
@@ -588,7 +606,7 @@ pub fn render(state: &mut State) {
588606 #[profiling::function]
589607 fn render_surface(state: &mut State, crtc: crtc::Handle, target: u64) -> bool {
590608 let State {
591- drm: Some(drm),
609+ backend: Backend::Drm(drm),
592610 renderer,
593611 layout,
594612 windows,
Modified file: crates/monolith/src/handlers.rs+1 additions−1 deletions
@@ -274,7 +274,7 @@ smithay::delegate_dispatch2!(State);
274274
275275 impl DrmSyncobjHandler for State {
276276 fn drm_syncobj_state(&mut self) -> Option<&mut DrmSyncobjState> {
277- self.syncobj.as_mut()
277+ self.backend.syncobj_mut()
278278 }
279279 }
280280
Modified file: crates/monolith/src/input.rs+1 additions−1 deletions
@@ -121,7 +121,7 @@ impl State {
121121 }
122122 }
123123 Some(Some(Action::Vt(vt))) => {
124- if let Some(drm) = self.drm.as_mut()
124+ if let Some(drm) = self.backend.drm_mut()
125125 && let Err(e) = drm.session.change_vt(vt)
126126 {
127127 tracing::warn!("switching to VT {vt} failed: {e}");
Modified file: crates/monolith/src/layers.rs+1 additions−1 deletions
@@ -112,7 +112,7 @@ impl State {
112112 let outputs: Vec<OutputId> = self.layout.outputs().map(|(o, _)| o).collect();
113113 let mut changed = false;
114114 for o in outputs {
115- let Some(output) = self.smithay_output(o) else {
115+ let Some(output) = self.backend.output(o) else {
116116 continue;
117117 };
118118 let Some(area) = self.layout.output(o).map(|out| out.area) else {
Modified file: crates/monolith/src/main.rs+1 additions
@@ -1,5 +1,6 @@
11 mod activation;
22 mod anim;
3+mod backend;
34 mod capture;
45 mod commands;
56 mod cursor;
Modified file: crates/monolith/src/nested/mod.rs+19 additions−15 deletions
@@ -5,10 +5,11 @@
55
66 mod input;
77
8+use crate::backend::Backend;
89 use crate::state::Services;
910 use crate::state::State;
1011 use calloop_wayland_source::WaylandSource;
11-use monolith_layout::Rect;
12+use monolith_layout::{OutputId, Rect};
1213 use monolith_render_vk::VulkanAllocator;
1314 use smithay::backend::allocator::dmabuf::Dmabuf;
1415 use smithay::backend::allocator::{Format, Fourcc, Modifier, Slot, Swapchain};
@@ -35,6 +36,7 @@ const DEFAULT_SIZE: (i32, i32) = (1280, 800);
3536 const CLOCK_MONOTONIC: u32 = 1;
3637
3738 pub struct Host {
39+ pub output_id: OutputId,
3840 pub size: Size<i32, Logical>,
3941 pub output: Output,
4042 conn: Connection,
@@ -137,9 +139,10 @@ pub fn init(
137139 f64::from(DEFAULT_SIZE.0),
138140 f64::from(DEFAULT_SIZE.1),
139141 );
140- state.output_id = Some(state.layout.add_output("nested", area));
142+ let output_id = state.layout.add_output("nested", area);
141143
142- state.host = Some(Host {
144+ state.backend = Backend::Nested(Box::new(Host {
145+ output_id,
143146 size: DEFAULT_SIZE.into(),
144147 damage: OutputDamageTracker::from_output(&output),
145148 output,
@@ -159,7 +162,7 @@ pub fn init(
159162 monotonic: false,
160163 last_present_ns: None,
161164 refresh_ns: 16_666_667,
162- });
165+ }));
163166 queue.roundtrip(state)?;
164167 queue.roundtrip(state)?;
165168 WaylandSource::new(conn, queue).insert(event_loop.handle())?;
@@ -169,7 +172,7 @@ pub fn init(
169172 /// Renders a frame if something changed and the host is ready for one.
170173 pub fn render(state: &mut State) {
171174 let State {
172- host,
175+ backend,
173176 renderer,
174177 space,
175178 needs_redraw,
@@ -179,7 +182,6 @@ pub fn render(state: &mut State) {
179182 anim,
180183 borders,
181184 settings,
182- output_id,
183185 services:
184186 Services {
185187 lock,
@@ -189,7 +191,9 @@ pub fn render(state: &mut State) {
189191 },
190192 ..
191193 } = state;
192- let Some(host) = host.as_mut() else { return };
194+ let Some(host) = backend.nested_mut() else {
195+ return;
196+ };
193197 if !host.configured || host.frame_pending || !*needs_redraw {
194198 return;
195199 }
@@ -230,7 +234,7 @@ pub fn render(state: &mut State) {
230234 return;
231235 }
232236 };
233- let Some(o) = *output_id else { return };
237+ let o = host.output_id;
234238 let elements = crate::lock::elements(lock, renderer, &host.output).unwrap_or_else(|| {
235239 crate::scene::build(
236240 layout,
@@ -407,7 +411,7 @@ impl Dispatch<xdg_surface::XdgSurface, ()> for State {
407411 ) {
408412 if let xdg_surface::Event::Configure { serial } = event {
409413 surface.ack_configure(serial);
410- if let Some(host) = state.host.as_mut() {
414+ if let Some(host) = state.backend.nested_mut() {
411415 host.configured = true;
412416 }
413417 state.needs_redraw = true;
@@ -427,7 +431,7 @@ impl Dispatch<xdg_toplevel::XdgToplevel, ()> for State {
427431 match event {
428432 xdg_toplevel::Event::Configure { width, height, .. } if width > 0 && height > 0 => {
429433 let size = Size::from((width, height));
430- if let Some(host) = state.host.as_mut()
434+ if let Some(host) = state.backend.nested_mut()
431435 && host.size != size
432436 {
433437 host.pending_size = Some(size);
@@ -455,7 +459,7 @@ impl Dispatch<zwp_linux_dmabuf_v1::ZwpLinuxDmabufV1, ()> for State {
455459 modifier_hi,
456460 modifier_lo,
457461 } = event
458- && let (Ok(code), Some(host)) = (Fourcc::try_from(format), state.host.as_mut())
462+ && let (Ok(code), Some(host)) = (Fourcc::try_from(format), state.backend.nested_mut())
459463 {
460464 let modifier = Modifier::from((u64::from(modifier_hi) << 32) | u64::from(modifier_lo));
461465 host.formats.push(Format { code, modifier });
@@ -473,7 +477,7 @@ impl Dispatch<wl_buffer::WlBuffer, ()> for State {
473477 _: &QueueHandle<Self>,
474478 ) {
475479 if let wl_buffer::Event::Release = event
476- && let Some(host) = state.host.as_mut()
480+ && let Some(host) = state.backend.nested_mut()
477481 {
478482 host.in_flight.retain(|(b, _)| b != buffer);
479483 }
@@ -490,7 +494,7 @@ impl Dispatch<wl_callback::WlCallback, ()> for State {
490494 _: &QueueHandle<Self>,
491495 ) {
492496 if let wl_callback::Event::Done { .. } = event
493- && let Some(host) = state.host.as_mut()
497+ && let Some(host) = state.backend.nested_mut()
494498 {
495499 host.frame_pending = false;
496500 }
@@ -513,7 +517,7 @@ impl Dispatch<wp_presentation::WpPresentation, ()> for State {
513517 _: &QueueHandle<Self>,
514518 ) {
515519 if let wp_presentation::Event::ClockId { clk_id } = event
516- && let Some(host) = state.host.as_mut()
520+ && let Some(host) = state.backend.nested_mut()
517521 {
518522 host.monotonic = clk_id == CLOCK_MONOTONIC;
519523 }
@@ -539,7 +543,7 @@ impl Dispatch<wp_presentation_feedback::WpPresentationFeedback, ()> for State {
539543 else {
540544 return;
541545 };
542- let Some(host) = state.host.as_mut() else {
546+ let Some(host) = state.backend.nested_mut() else {
543547 return;
544548 };
545549 let secs = (u64::from(tv_sec_hi) << 32) | u64::from(tv_sec_lo);
Modified file: crates/monolith/src/state.rs+16 additions−32 deletions
@@ -2,7 +2,6 @@ use crate::anim::{Animator, Ghost};
22 use crate::commands::Binds;
33 use crate::focus::Focus;
44 use crate::globals::Protocols;
5-use crate::nested::Host;
65 use monolith_config::Config;
76 use monolith_layout::{Layout, OutputId, Rect, WindowId};
87 use monolith_protocols::workspace::{Group, Snapshot, Workspace};
@@ -42,9 +41,7 @@ pub struct State {
4241 pub renderer: VulkanRenderer,
4342 pub space: Space<Window>,
4443 pub popups: PopupManager,
45- pub host: Option<Host>,
46- pub drm: Option<crate::drm::Drm>,
47- pub syncobj: Option<smithay::wayland::drm_syncobj::DrmSyncobjState>,
44+ pub backend: crate::backend::Backend,
4845 pub cursor: crate::cursor::Cursor,
4946 pub cursor_status: CursorImageStatus,
5047 pub needs_redraw: bool,
@@ -56,7 +53,6 @@ pub struct State {
5653 /// Toplevels that have not committed yet. Rules need their app_id and
5754 /// title, which clients set after creating the toplevel.
5855 pub unmapped: Vec<Window>,
59- pub output_id: Option<OutputId>,
6056 pub suppressed: Vec<Keycode>,
6157 pub move_inverted: bool,
6258 pub drag: Option<crate::input::Drag>,
@@ -74,8 +70,6 @@ pub struct State {
7470 pub placed_by_rule: HashSet<WindowId>,
7571 /// A layer surface given the keyboard by a click, like a launcher.
7672 pub layer_focus: Option<WlSurface>,
77- /// The render node clients get dmabufs for, known on bare metal.
78- pub render_node: Option<smithay::backend::drm::DrmNode>,
7973 pub seat_state: SeatState<State>,
8074 pub seat: Seat<State>,
8175 }
@@ -173,9 +167,7 @@ impl State {
173167 renderer,
174168 space: Space::default(),
175169 popups: PopupManager::default(),
176- host: None,
177- drm: None,
178- syncobj: None,
170+ backend: crate::backend::Backend::Starting,
179171 cursor: crate::cursor::Cursor::load(),
180172 cursor_status: CursorImageStatus::default_named(),
181173 needs_redraw: true,
@@ -183,7 +175,6 @@ impl State {
183175 layout,
184176 windows: HashMap::new(),
185177 unmapped: Vec::new(),
186- output_id: None,
187178 suppressed: Vec::new(),
188179 move_inverted: false,
189180 drag: None,
@@ -204,7 +195,6 @@ impl State {
204195 awaiting: HashSet::new(),
205196 placed_by_rule: HashSet::new(),
206197 layer_focus: None,
207- render_node: None,
208198 seat_state,
209199 seat,
210200 })
@@ -214,8 +204,11 @@ impl State {
214204 pub fn after_dispatch(&mut self) {
215205 self.space.refresh();
216206 self.popups.cleanup();
217- crate::nested::render(self);
218- crate::drm::render(self);
207+ match self.backend {
208+ crate::backend::Backend::Drm(_) => crate::drm::render(self),
209+ crate::backend::Backend::Nested(_) => crate::nested::render(self),
210+ crate::backend::Backend::Starting => {}
211+ }
219212 self.confirm_lock();
220213 self.complete_captures();
221214 crate::ipc::broadcast(self);
@@ -225,17 +218,16 @@ impl State {
225218 }
226219
227220 pub fn resize_output(&mut self, size: Size<i32, Logical>) {
228- if let Some(o) = self.output_id {
221+ if let Some(host) = self.backend.nested() {
222+ let o = host.output_id;
229223 let area = Rect::new(0.0, 0.0, f64::from(size.w), f64::from(size.h));
224+ let mode = smithay::output::Mode {
225+ size: (size.w, size.h).into(),
226+ refresh: 60_000,
227+ };
228+ host.output
229+ .change_current_state(Some(mode), None, None, None);
230230 self.layout.set_output_area(o, area);
231- if let Some(host) = &self.host {
232- let mode = smithay::output::Mode {
233- size: (size.w, size.h).into(),
234- refresh: 60_000,
235- };
236- host.output
237- .change_current_state(Some(mode), None, None, None);
238- }
239231 self.reserve_layers();
240232 self.reconfigure_lock();
241233 self.apply_layout();
@@ -541,14 +533,6 @@ impl State {
541533 }
542534 }
543535
544- /// The smithay output behind a layout output, on whichever backend runs.
545- pub fn smithay_output(&self, o: OutputId) -> Option<smithay::output::Output> {
546- if let Some(host) = self.host.as_ref().filter(|_| Some(o) == self.output_id) {
547- return Some(host.output.clone());
548- }
549- self.drm.as_ref().and_then(|d| crate::drm::output_for(d, o))
550- }
551-
552536 fn publish_workspaces(&mut self) {
553537 if !self.protocols.workspaces.has_clients() {
554538 return;
@@ -562,7 +546,7 @@ impl State {
562546 let groups = l
563547 .outputs()
564548 .filter_map(|(o, _)| {
565- let output = self.smithay_output(o)?;
549+ let output = self.backend.output(o)?;
566550 // Bars read a tag's existence as having windows, as with
567551 // workspaces that come and go, so empty unviewed tags are left out.
568552 let workspaces = l
Modified file: crates/monolith/src/toplevels.rs+1 additions−1 deletions
@@ -132,7 +132,7 @@ impl State {
132132 app_id,
133133 outputs: w
134134 .and_then(|w| w.output)
135- .and_then(|o| self.smithay_output(o))
135+ .and_then(|o| self.backend.output(o))
136136 .into_iter()
137137 .collect(),
138138 activated: focused == Some(id),