atagen

monolith

public
Star0
Watch0
8e6f96f0a32d329d75ee602576de162d212a6169
1
ssh://brewery@pls.lobotomise.me:33/atagen/monolith.git

Blame: crates/monolith/src/drm.rs

0c1685f1d412atagen2026-10-16 12:18 UTC1
//! Bare metal. A libseat session, udev for the primary GPU and its
0c1685f1d412atagen2026-10-16 12:18 UTC2
//! connectors, libinput for input, and one smithay DRM output per connector,
0c1685f1d412atagen2026-10-16 12:18 UTC3
//! each rendered when its own vblank lands.
0c1685f1d412atagen2026-10-16 12:18 UTC4
0c1685f1d412atagen2026-10-16 12:18 UTC5
use crate::scene::Draw;
0c1685f1d412atagen2026-10-16 12:18 UTC6
use crate::state::{State, now_ns};
8e6f96f0a32datagen2026-10-16 22:17 UTC7
use monolith_config::Vrr;
0c1685f1d412atagen2026-10-16 12:18 UTC8
use monolith_layout::{OutputId, Rect};
0c1685f1d412atagen2026-10-16 12:18 UTC9
use smithay::backend::allocator::Fourcc;
0c1685f1d412atagen2026-10-16 12:18 UTC10
use smithay::backend::allocator::gbm::{GbmAllocator, GbmBufferFlags, GbmDevice};
8e6f96f0a32datagen2026-10-16 22:17 UTC11
use smithay::backend::drm::VrrSupport;
0c1685f1d412atagen2026-10-16 12:18 UTC12
use smithay::backend::drm::compositor::FrameFlags;
0c1685f1d412atagen2026-10-16 12:18 UTC13
use smithay::backend::drm::exporter::gbm::GbmFramebufferExporter;
0c1685f1d412atagen2026-10-16 12:18 UTC14
use smithay::backend::drm::output::{DrmOutput, DrmOutputManager, DrmOutputRenderElements};
0c1685f1d412atagen2026-10-16 12:18 UTC15
use smithay::backend::drm::{
0c1685f1d412atagen2026-10-16 12:18 UTC16
    DrmDevice, DrmDeviceFd, DrmEvent, DrmEventMetadata, DrmEventTime, DrmNode, NodeType,
0c1685f1d412atagen2026-10-16 12:18 UTC17
};
0c1685f1d412atagen2026-10-16 12:18 UTC18
use smithay::backend::input::{
0c1685f1d412atagen2026-10-16 12:18 UTC19
    AbsolutePositionEvent, Axis, AxisSource, Event, InputEvent, KeyboardKeyEvent, PointerAxisEvent,
0c1685f1d412atagen2026-10-16 12:18 UTC20
    PointerButtonEvent, PointerMotionEvent,
0c1685f1d412atagen2026-10-16 12:18 UTC21
};
0c1685f1d412atagen2026-10-16 12:18 UTC22
use smithay::backend::libinput::{LibinputInputBackend, LibinputSessionInterface};
0c1685f1d412atagen2026-10-16 12:18 UTC23
use smithay::backend::session::libseat::LibSeatSession;
0c1685f1d412atagen2026-10-16 12:18 UTC24
use smithay::backend::session::{Event as SessionEvent, Session};
0c1685f1d412atagen2026-10-16 12:18 UTC25
use smithay::backend::udev::{UdevBackend, UdevEvent, all_gpus, primary_gpu};
0c1685f1d412atagen2026-10-16 12:18 UTC26
use smithay::input::pointer::AxisFrame;
0c1685f1d412atagen2026-10-16 12:18 UTC27
use smithay::output::{Mode, Output, PhysicalProperties, Scale, Subpixel};
0c1685f1d412atagen2026-10-16 12:18 UTC28
use smithay::reexports::calloop::timer::{TimeoutAction, Timer};
6482fe98839datagen2026-10-16 13:01 UTC29
use smithay::reexports::calloop::{EventLoop, LoopHandle, RegistrationToken};
0c1685f1d412atagen2026-10-16 12:18 UTC30
use smithay::reexports::drm::Device as _;
0c1685f1d412atagen2026-10-16 12:18 UTC31
use smithay::reexports::drm::control::{ModeTypeFlags, connector, crtc};
0c1685f1d412atagen2026-10-16 12:18 UTC32
use smithay::reexports::input::Libinput;
0c1685f1d412atagen2026-10-16 12:18 UTC33
use smithay::reexports::rustix::fs::OFlags;
0c1685f1d412atagen2026-10-16 12:18 UTC34
use smithay::utils::{DeviceFd, Point, Transform};
e4b186dffb77atagen2026-10-16 13:07 UTC35
use smithay::wayland::drm_syncobj::{DrmSyncobjState, supports_syncobj_eventfd};
0c1685f1d412atagen2026-10-16 12:18 UTC36
use smithay_drm_extras::drm_scanner::{DrmScanEvent, DrmScanner};
6482fe98839datagen2026-10-16 13:01 UTC37
use std::collections::{HashMap, VecDeque};
f27fa4c2ade9atagen2026-10-16 12:59 UTC38
use std::path::{Path, PathBuf};
0c1685f1d412atagen2026-10-16 12:18 UTC39
use std::time::Duration;
0c1685f1d412atagen2026-10-16 12:18 UTC40
0c1685f1d412atagen2026-10-16 12:18 UTC41
const CLEAR: [f32; 4] = [0.07, 0.075, 0.09, 1.0];
8e6f96f0a32datagen2026-10-16 22:17 UTC42
/// Frames that only move the cursor stay at or below 60 Hz under VRR, so the
8e6f96f0a32datagen2026-10-16 22:17 UTC43
/// mouse does not override the refresh rate a game is pacing.
8e6f96f0a32datagen2026-10-16 22:17 UTC44
const CURSOR_FRAME_NS: u64 = 16_666_667;
0c1685f1d412atagen2026-10-16 12:18 UTC45
0c1685f1d412atagen2026-10-16 12:18 UTC46
type Allocator = GbmAllocator<DrmDeviceFd>;
0c1685f1d412atagen2026-10-16 12:18 UTC47
type Exporter = GbmFramebufferExporter<DrmDeviceFd>;
0c1685f1d412atagen2026-10-16 12:18 UTC48
type Manager = DrmOutputManager<Allocator, Exporter, (), DrmDeviceFd>;
0c1685f1d412atagen2026-10-16 12:18 UTC49
type Out = DrmOutput<Allocator, Exporter, (), DrmDeviceFd>;
0c1685f1d412atagen2026-10-16 12:18 UTC50
0c1685f1d412atagen2026-10-16 12:18 UTC51
pub struct Drm {
0c1685f1d412atagen2026-10-16 12:18 UTC52
    pub session: LibSeatSession,
0c1685f1d412atagen2026-10-16 12:18 UTC53
    libinput: Libinput,
0c1685f1d412atagen2026-10-16 12:18 UTC54
    handle: LoopHandle<'static, State>,
0c1685f1d412atagen2026-10-16 12:18 UTC55
    node: DrmNode,
0c1685f1d412atagen2026-10-16 12:18 UTC56
    manager: Manager,
0c1685f1d412atagen2026-10-16 12:18 UTC57
    scanner: DrmScanner,
0c1685f1d412atagen2026-10-16 12:18 UTC58
    nvidia: bool,
fdb16f42492eatagen2026-10-16 13:40 UTC59
    frame_flags: FrameFlags,
0c1685f1d412atagen2026-10-16 12:18 UTC60
    surfaces: HashMap<crtc::Handle, Surface>,
0c1685f1d412atagen2026-10-16 12:18 UTC61
}
0c1685f1d412atagen2026-10-16 12:18 UTC62
0c1685f1d412atagen2026-10-16 12:18 UTC63
struct Surface {
0c1685f1d412atagen2026-10-16 12:18 UTC64
    output: Output,
0c1685f1d412atagen2026-10-16 12:18 UTC65
    id: OutputId,
0c1685f1d412atagen2026-10-16 12:18 UTC66
    drm: Out,
0c1685f1d412atagen2026-10-16 12:18 UTC67
    /// A frame is on its way to the screen and its vblank has not landed.
0c1685f1d412atagen2026-10-16 12:18 UTC68
    queued: bool,
0c1685f1d412atagen2026-10-16 12:18 UTC69
    redraw: bool,
0c1685f1d412atagen2026-10-16 12:18 UTC70
    last_vblank_ns: Option<u64>,
0c1685f1d412atagen2026-10-16 12:18 UTC71
    refresh_ns: u64,
6482fe98839datagen2026-10-16 13:01 UTC72
    /// The pending render, timed to start just in time for `target_ns`.
6482fe98839datagen2026-10-16 13:01 UTC73
    scheduled: Option<RegistrationToken>,
6482fe98839datagen2026-10-16 13:01 UTC74
    /// The vblank the frame being drawn or queued is meant for.
6482fe98839datagen2026-10-16 13:01 UTC75
    target_ns: Option<u64>,
6482fe98839datagen2026-10-16 13:01 UTC76
    budget: Budget,
6c3361e382e4atagen2026-10-16 13:12 UTC77
    /// Frames presented since `stats_since`, for the once-a-second debug line.
6c3361e382e4atagen2026-10-16 13:12 UTC78
    presented: u32,
6c3361e382e4atagen2026-10-16 13:12 UTC79
    stats_since: u64,
8e6f96f0a32datagen2026-10-16 22:17 UTC80
    vrr_capable: bool,
8e6f96f0a32datagen2026-10-16 22:17 UTC81
    vrr_on: bool,
8e6f96f0a32datagen2026-10-16 22:17 UTC82
    /// Only the cursor moved, which under VRR is drawn at a capped rate.
8e6f96f0a32datagen2026-10-16 22:17 UTC83
    cursor_redraw: bool,
0c1685f1d412atagen2026-10-16 12:18 UTC84
}
0c1685f1d412atagen2026-10-16 12:18 UTC85
0c1685f1d412atagen2026-10-16 12:18 UTC86
impl Surface {
6482fe98839datagen2026-10-16 13:01 UTC87
    /// The first vblank a render starting at `now` can still make, and the
6482fe98839datagen2026-10-16 13:01 UTC88
    /// latest moment to start it.
6482fe98839datagen2026-10-16 13:01 UTC89
    fn next_deadline(&self, now: u64) -> Option<(u64, u64)> {
6482fe98839datagen2026-10-16 13:01 UTC90
        let last = self.last_vblank_ns?;
6482fe98839datagen2026-10-16 13:01 UTC91
        let refresh = self.refresh_ns;
6482fe98839datagen2026-10-16 13:01 UTC92
        if refresh == 0 {
6482fe98839datagen2026-10-16 13:01 UTC93
            return None;
6482fe98839datagen2026-10-16 13:01 UTC94
        }
32fbd7e2725catagen2026-10-16 13:19 UTC95
        let budget = self.budget.ns().min(refresh);
32fbd7e2725catagen2026-10-16 13:19 UTC96
        let periods = (now + budget).saturating_sub(last).div_ceil(refresh).max(1);
6482fe98839datagen2026-10-16 13:01 UTC97
        let vblank = last + periods * refresh;
32fbd7e2725catagen2026-10-16 13:19 UTC98
        Some((vblank, vblank - budget))
6482fe98839datagen2026-10-16 13:01 UTC99
    }
6482fe98839datagen2026-10-16 13:01 UTC100
}
6482fe98839datagen2026-10-16 13:01 UTC101
32fbd7e2725catagen2026-10-16 13:19 UTC102
/// How long before a vblank rendering must start. A high percentile of
32fbd7e2725catagen2026-10-16 13:19 UTC103
/// recent CPU render times covers the CPU side without one spike inflating
32fbd7e2725catagen2026-10-16 13:19 UTC104
/// it, and a slack that grows on every missed vblank and shrinks over seconds
32fbd7e2725catagen2026-10-16 13:19 UTC105
/// while frames land covers GPU time and the commit.
6482fe98839datagen2026-10-16 13:01 UTC106
struct Budget {
6482fe98839datagen2026-10-16 13:01 UTC107
    renders: VecDeque<u64>,
6482fe98839datagen2026-10-16 13:01 UTC108
    slack_ns: u64,
9c83f07146b1atagen2026-10-16 13:36 UTC109
    /// The slack that last proved enough after a miss. Decay stops there
9c83f07146b1atagen2026-10-16 13:36 UTC110
    /// for `floor_frames` frames, so it does not keep rediscovering the same
9c83f07146b1atagen2026-10-16 13:36 UTC111
    /// limit one dropped frame at a time.
9c83f07146b1atagen2026-10-16 13:36 UTC112
    floor_ns: u64,
9c83f07146b1atagen2026-10-16 13:36 UTC113
    floor_frames: u64,
6482fe98839datagen2026-10-16 13:01 UTC114
}
6482fe98839datagen2026-10-16 13:01 UTC115
6482fe98839datagen2026-10-16 13:01 UTC116
impl Budget {
6482fe98839datagen2026-10-16 13:01 UTC117
    const KEEP: usize = 32;
6482fe98839datagen2026-10-16 13:01 UTC118
    const MIN_SLACK_NS: u64 = 1_000_000;
6482fe98839datagen2026-10-16 13:01 UTC119
    const STEP_NS: u64 = 500_000;
32fbd7e2725catagen2026-10-16 13:19 UTC120
    const DECAY_NS: u64 = 1_000;
9c83f07146b1atagen2026-10-16 13:36 UTC121
    const FLOOR_NS: u64 = 30_000_000_000;
6482fe98839datagen2026-10-16 13:01 UTC122
6482fe98839datagen2026-10-16 13:01 UTC123
    fn new() -> Self {
6482fe98839datagen2026-10-16 13:01 UTC124
        Self {
6482fe98839datagen2026-10-16 13:01 UTC125
            renders: VecDeque::with_capacity(Self::KEEP),
6482fe98839datagen2026-10-16 13:01 UTC126
            slack_ns: 2_000_000,
9c83f07146b1atagen2026-10-16 13:36 UTC127
            floor_ns: 0,
9c83f07146b1atagen2026-10-16 13:36 UTC128
            floor_frames: 0,
6482fe98839datagen2026-10-16 13:01 UTC129
        }
6482fe98839datagen2026-10-16 13:01 UTC130
    }
6482fe98839datagen2026-10-16 13:01 UTC131
6482fe98839datagen2026-10-16 13:01 UTC132
    fn ns(&self) -> u64 {
32fbd7e2725catagen2026-10-16 13:19 UTC133
        let mut renders: Vec<u64> = self.renders.iter().copied().collect();
32fbd7e2725catagen2026-10-16 13:19 UTC134
        renders.sort_unstable();
32fbd7e2725catagen2026-10-16 13:19 UTC135
        let p90 = renders
32fbd7e2725catagen2026-10-16 13:19 UTC136
            .get(renders.len().saturating_sub(1) * 9 / 10)
32fbd7e2725catagen2026-10-16 13:19 UTC137
            .copied()
32fbd7e2725catagen2026-10-16 13:19 UTC138
            .unwrap_or(0);
32fbd7e2725catagen2026-10-16 13:19 UTC139
        p90 + self.slack_ns
6482fe98839datagen2026-10-16 13:01 UTC140
    }
6482fe98839datagen2026-10-16 13:01 UTC141
6482fe98839datagen2026-10-16 13:01 UTC142
    fn record(&mut self, render_ns: u64) {
6482fe98839datagen2026-10-16 13:01 UTC143
        if self.renders.len() == Self::KEEP {
6482fe98839datagen2026-10-16 13:01 UTC144
            self.renders.pop_front();
0c1685f1d412atagen2026-10-16 12:18 UTC145
        }
6482fe98839datagen2026-10-16 13:01 UTC146
        self.renders.push_back(render_ns);
6482fe98839datagen2026-10-16 13:01 UTC147
    }
6482fe98839datagen2026-10-16 13:01 UTC148
6482fe98839datagen2026-10-16 13:01 UTC149
    fn missed(&mut self, refresh_ns: u64) {
6482fe98839datagen2026-10-16 13:01 UTC150
        self.slack_ns = (self.slack_ns + Self::STEP_NS).min(refresh_ns / 2);
9c83f07146b1atagen2026-10-16 13:36 UTC151
        self.floor_ns = self.slack_ns;
9c83f07146b1atagen2026-10-16 13:36 UTC152
        self.floor_frames = Self::FLOOR_NS / refresh_ns.max(1);
6482fe98839datagen2026-10-16 13:01 UTC153
    }
6482fe98839datagen2026-10-16 13:01 UTC154
6482fe98839datagen2026-10-16 13:01 UTC155
    fn landed(&mut self) {
9c83f07146b1atagen2026-10-16 13:36 UTC156
        self.floor_frames = self.floor_frames.saturating_sub(1);
9c83f07146b1atagen2026-10-16 13:36 UTC157
        let floor = if self.floor_frames > 0 {
9c83f07146b1atagen2026-10-16 13:36 UTC158
            self.floor_ns
9c83f07146b1atagen2026-10-16 13:36 UTC159
        } else {
9c83f07146b1atagen2026-10-16 13:36 UTC160
            Self::MIN_SLACK_NS
9c83f07146b1atagen2026-10-16 13:36 UTC161
        };
9c83f07146b1atagen2026-10-16 13:36 UTC162
        self.slack_ns = self.slack_ns.saturating_sub(Self::DECAY_NS).max(floor);
0c1685f1d412atagen2026-10-16 12:18 UTC163
    }
0c1685f1d412atagen2026-10-16 12:18 UTC164
}
0c1685f1d412atagen2026-10-16 12:18 UTC165
f27fa4c2ade9atagen2026-10-16 12:59 UTC166
/// The GPU monolith drives on bare metal, the seat's primary or else its
f27fa4c2ade9atagen2026-10-16 12:59 UTC167
/// first.
f27fa4c2ade9atagen2026-10-16 12:59 UTC168
pub fn primary() -> Result<PathBuf, Box<dyn std::error::Error>> {
f27fa4c2ade9atagen2026-10-16 12:59 UTC169
    let seat = std::env::var("XDG_SEAT").unwrap_or_else(|_| "seat0".into());
f27fa4c2ade9atagen2026-10-16 12:59 UTC170
    Ok(match primary_gpu(&seat)? {
f27fa4c2ade9atagen2026-10-16 12:59 UTC171
        Some(p) => p,
f27fa4c2ade9atagen2026-10-16 12:59 UTC172
        None => all_gpus(&seat)?.into_iter().next().ok_or("no GPU found")?,
f27fa4c2ade9atagen2026-10-16 12:59 UTC173
    })
f27fa4c2ade9atagen2026-10-16 12:59 UTC174
}
f27fa4c2ade9atagen2026-10-16 12:59 UTC175
0c1685f1d412atagen2026-10-16 12:18 UTC176
pub fn init(
0c1685f1d412atagen2026-10-16 12:18 UTC177
    event_loop: &mut EventLoop<'static, State>,
0c1685f1d412atagen2026-10-16 12:18 UTC178
    state: &mut State,
f27fa4c2ade9atagen2026-10-16 12:59 UTC179
    path: &Path,
0c1685f1d412atagen2026-10-16 12:18 UTC180
) -> Result<(), Box<dyn std::error::Error>> {
0c1685f1d412atagen2026-10-16 12:18 UTC181
    let (mut session, notifier) = LibSeatSession::new()?;
0c1685f1d412atagen2026-10-16 12:18 UTC182
    let seat = session.seat();
f27fa4c2ade9atagen2026-10-16 12:59 UTC183
    let node = DrmNode::from_path(path)?;
0c1685f1d412atagen2026-10-16 12:18 UTC184
    tracing::info!(path = %path.display(), "primary GPU");
0c1685f1d412atagen2026-10-16 12:18 UTC185
    let fd = session.open(
f27fa4c2ade9atagen2026-10-16 12:59 UTC186
        path,
0c1685f1d412atagen2026-10-16 12:18 UTC187
        OFlags::RDWR | OFlags::CLOEXEC | OFlags::NOCTTY | OFlags::NONBLOCK,
0c1685f1d412atagen2026-10-16 12:18 UTC188
    )?;
0c1685f1d412atagen2026-10-16 12:18 UTC189
    let fd = DrmDeviceFd::new(DeviceFd::from(fd));
0c1685f1d412atagen2026-10-16 12:18 UTC190
    let (device, drm_events) = DrmDevice::new(fd.clone(), true)?;
0c1685f1d412atagen2026-10-16 12:18 UTC191
    let nvidia = device
0c1685f1d412atagen2026-10-16 12:18 UTC192
        .get_driver()
0c1685f1d412atagen2026-10-16 12:18 UTC193
        .map(|d| d.name().to_string_lossy().to_lowercase().contains("nvidia"))
0c1685f1d412atagen2026-10-16 12:18 UTC194
        .unwrap_or(false);
e4b186dffb77atagen2026-10-16 13:07 UTC195
    if supports_syncobj_eventfd(&fd) {
e4b186dffb77atagen2026-10-16 13:07 UTC196
        state.syncobj = Some(DrmSyncobjState::new::<State>(&state.display, fd.clone()));
e4b186dffb77atagen2026-10-16 13:07 UTC197
    } else {
e4b186dffb77atagen2026-10-16 13:07 UTC198
        tracing::info!("no syncobj eventfd support, clients stay on implicit sync");
e4b186dffb77atagen2026-10-16 13:07 UTC199
    }
0c1685f1d412atagen2026-10-16 12:18 UTC200
    let gbm = GbmDevice::new(fd)?;
0c1685f1d412atagen2026-10-16 12:18 UTC201
    let render_node = node
0c1685f1d412atagen2026-10-16 12:18 UTC202
        .node_with_type(NodeType::Render)
0c1685f1d412atagen2026-10-16 12:18 UTC203
        .and_then(Result::ok)
0c1685f1d412atagen2026-10-16 12:18 UTC204
        .unwrap_or(node);
0c1685f1d412atagen2026-10-16 12:18 UTC205
    let allocator = GbmAllocator::new(
0c1685f1d412atagen2026-10-16 12:18 UTC206
        gbm.clone(),
0c1685f1d412atagen2026-10-16 12:18 UTC207
        GbmBufferFlags::RENDERING | GbmBufferFlags::SCANOUT,
0c1685f1d412atagen2026-10-16 12:18 UTC208
    );
0c1685f1d412atagen2026-10-16 12:18 UTC209
    let exporter = GbmFramebufferExporter::new(gbm.clone(), render_node.into());
0c1685f1d412atagen2026-10-16 12:18 UTC210
    let render_formats: Vec<_> = state.renderer.render_formats().iter().copied().collect();
0c1685f1d412atagen2026-10-16 12:18 UTC211
    let manager = DrmOutputManager::new(
0c1685f1d412atagen2026-10-16 12:18 UTC212
        device,
0c1685f1d412atagen2026-10-16 12:18 UTC213
        allocator,
0c1685f1d412atagen2026-10-16 12:18 UTC214
        exporter,
0c1685f1d412atagen2026-10-16 12:18 UTC215
        Some(gbm),
0c1685f1d412atagen2026-10-16 12:18 UTC216
        [Fourcc::Xrgb8888, Fourcc::Argb8888],
0c1685f1d412atagen2026-10-16 12:18 UTC217
        render_formats,
0c1685f1d412atagen2026-10-16 12:18 UTC218
    );
0c1685f1d412atagen2026-10-16 12:18 UTC219
0c1685f1d412atagen2026-10-16 12:18 UTC220
    let mut libinput =
0c1685f1d412atagen2026-10-16 12:18 UTC221
        Libinput::new_with_udev::<LibinputSessionInterface<LibSeatSession>>(session.clone().into());
0c1685f1d412atagen2026-10-16 12:18 UTC222
    libinput
0c1685f1d412atagen2026-10-16 12:18 UTC223
        .udev_assign_seat(&seat)
0c1685f1d412atagen2026-10-16 12:18 UTC224
        .map_err(|()| "libinput could not take the seat")?;
0c1685f1d412atagen2026-10-16 12:18 UTC225
    let handle = event_loop.handle();
0c1685f1d412atagen2026-10-16 12:18 UTC226
    handle.insert_source(
0c1685f1d412atagen2026-10-16 12:18 UTC227
        LibinputInputBackend::new(libinput.clone()),
0c1685f1d412atagen2026-10-16 12:18 UTC228
        |event, _, state| {
0c1685f1d412atagen2026-10-16 12:18 UTC229
            on_input(state, event);
0c1685f1d412atagen2026-10-16 12:18 UTC230
        },
0c1685f1d412atagen2026-10-16 12:18 UTC231
    )?;
0c1685f1d412atagen2026-10-16 12:18 UTC232
    handle.insert_source(notifier, |event, _, state| match event {
0c1685f1d412atagen2026-10-16 12:18 UTC233
        SessionEvent::PauseSession => {
0c1685f1d412atagen2026-10-16 12:18 UTC234
            if let Some(drm) = state.drm.as_mut() {
0c1685f1d412atagen2026-10-16 12:18 UTC235
                drm.libinput.suspend();
0c1685f1d412atagen2026-10-16 12:18 UTC236
                drm.manager.pause();
0c1685f1d412atagen2026-10-16 12:18 UTC237
            }
0c1685f1d412atagen2026-10-16 12:18 UTC238
        }
0c1685f1d412atagen2026-10-16 12:18 UTC239
        SessionEvent::ActivateSession => {
0c1685f1d412atagen2026-10-16 12:18 UTC240
            if let Some(drm) = state.drm.as_mut() {
0c1685f1d412atagen2026-10-16 12:18 UTC241
                if drm.libinput.resume().is_err() {
0c1685f1d412atagen2026-10-16 12:18 UTC242
                    tracing::warn!("libinput did not resume");
0c1685f1d412atagen2026-10-16 12:18 UTC243
                }
0c1685f1d412atagen2026-10-16 12:18 UTC244
                if let Err(e) = drm.manager.lock().activate(false) {
0c1685f1d412atagen2026-10-16 12:18 UTC245
                    tracing::warn!("could not reactivate the GPU: {e}");
0c1685f1d412atagen2026-10-16 12:18 UTC246
                }
0c1685f1d412atagen2026-10-16 12:18 UTC247
                for s in drm.surfaces.values_mut() {
0c1685f1d412atagen2026-10-16 12:18 UTC248
                    s.queued = false;
0c1685f1d412atagen2026-10-16 12:18 UTC249
                    s.redraw = true;
0c1685f1d412atagen2026-10-16 12:18 UTC250
                }
0c1685f1d412atagen2026-10-16 12:18 UTC251
            }
0c1685f1d412atagen2026-10-16 12:18 UTC252
            state.needs_redraw = true;
0c1685f1d412atagen2026-10-16 12:18 UTC253
        }
0c1685f1d412atagen2026-10-16 12:18 UTC254
    })?;
0c1685f1d412atagen2026-10-16 12:18 UTC255
    handle.insert_source(drm_events, |event, meta, state| match event {
0c1685f1d412atagen2026-10-16 12:18 UTC256
        DrmEvent::VBlank(crtc) => on_vblank(state, crtc, meta),
0c1685f1d412atagen2026-10-16 12:18 UTC257
        DrmEvent::Error(e) => tracing::warn!("drm: {e}"),
0c1685f1d412atagen2026-10-16 12:18 UTC258
    })?;
0c1685f1d412atagen2026-10-16 12:18 UTC259
    let udev = UdevBackend::new(&seat)?;
0c1685f1d412atagen2026-10-16 12:18 UTC260
    handle.insert_source(udev, move |event, _, state| {
0c1685f1d412atagen2026-10-16 12:18 UTC261
        if let UdevEvent::Changed { device_id } = event
0c1685f1d412atagen2026-10-16 12:18 UTC262
            && state
0c1685f1d412atagen2026-10-16 12:18 UTC263
                .drm
0c1685f1d412atagen2026-10-16 12:18 UTC264
                .as_ref()
0c1685f1d412atagen2026-10-16 12:18 UTC265
                .is_some_and(|d| d.node.dev_id() == device_id)
0c1685f1d412atagen2026-10-16 12:18 UTC266
        {
0c1685f1d412atagen2026-10-16 12:18 UTC267
            scan(state);
0c1685f1d412atagen2026-10-16 12:18 UTC268
        }
0c1685f1d412atagen2026-10-16 12:18 UTC269
    })?;
0c1685f1d412atagen2026-10-16 12:18 UTC270
0c1685f1d412atagen2026-10-16 12:18 UTC271
    state.drm = Some(Drm {
0c1685f1d412atagen2026-10-16 12:18 UTC272
        session,
0c1685f1d412atagen2026-10-16 12:18 UTC273
        libinput,
0c1685f1d412atagen2026-10-16 12:18 UTC274
        handle,
0c1685f1d412atagen2026-10-16 12:18 UTC275
        node,
0c1685f1d412atagen2026-10-16 12:18 UTC276
        manager,
0c1685f1d412atagen2026-10-16 12:18 UTC277
        scanner: DrmScanner::new(),
0c1685f1d412atagen2026-10-16 12:18 UTC278
        nvidia,
fdb16f42492eatagen2026-10-16 13:40 UTC279
        frame_flags: if std::env::var_os("MONOLITH_NO_SCANOUT").is_some() {
fdb16f42492eatagen2026-10-16 13:40 UTC280
            FrameFlags::empty()
fdb16f42492eatagen2026-10-16 13:40 UTC281
        } else {
fdb16f42492eatagen2026-10-16 13:40 UTC282
            FrameFlags::DEFAULT
fdb16f42492eatagen2026-10-16 13:40 UTC283
        },
0c1685f1d412atagen2026-10-16 12:18 UTC284
        surfaces: HashMap::new(),
0c1685f1d412atagen2026-10-16 12:18 UTC285
    });
0c1685f1d412atagen2026-10-16 12:18 UTC286
    scan(state);
0c1685f1d412atagen2026-10-16 12:18 UTC287
    Ok(())
0c1685f1d412atagen2026-10-16 12:18 UTC288
}
0c1685f1d412atagen2026-10-16 12:18 UTC289
0c1685f1d412atagen2026-10-16 12:18 UTC290
/// Brings outputs in line with what is plugged in.
0c1685f1d412atagen2026-10-16 12:18 UTC291
fn scan(state: &mut State) {
0c1685f1d412atagen2026-10-16 12:18 UTC292
    let Some(drm) = state.drm.as_mut() else {
0c1685f1d412atagen2026-10-16 12:18 UTC293
        return;
0c1685f1d412atagen2026-10-16 12:18 UTC294
    };
0c1685f1d412atagen2026-10-16 12:18 UTC295
    let events: Vec<DrmScanEvent> = match drm.scanner.scan_connectors(drm.manager.device()) {
0c1685f1d412atagen2026-10-16 12:18 UTC296
        Ok(result) => result.into_iter().collect(),
0c1685f1d412atagen2026-10-16 12:18 UTC297
        Err(e) => {
0c1685f1d412atagen2026-10-16 12:18 UTC298
            tracing::warn!("scanning connectors failed: {e}");
0c1685f1d412atagen2026-10-16 12:18 UTC299
            return;
0c1685f1d412atagen2026-10-16 12:18 UTC300
        }
0c1685f1d412atagen2026-10-16 12:18 UTC301
    };
0c1685f1d412atagen2026-10-16 12:18 UTC302
    for event in events {
0c1685f1d412atagen2026-10-16 12:18 UTC303
        match event {
0c1685f1d412atagen2026-10-16 12:18 UTC304
            DrmScanEvent::Connected {
0c1685f1d412atagen2026-10-16 12:18 UTC305
                connector,
0c1685f1d412atagen2026-10-16 12:18 UTC306
                crtc: Some(crtc),
0c1685f1d412atagen2026-10-16 12:18 UTC307
            } => connect(state, connector, crtc),
0c1685f1d412atagen2026-10-16 12:18 UTC308
            DrmScanEvent::Disconnected {
0c1685f1d412atagen2026-10-16 12:18 UTC309
                crtc: Some(crtc), ..
0c1685f1d412atagen2026-10-16 12:18 UTC310
            } => disconnect(state, crtc),
0c1685f1d412atagen2026-10-16 12:18 UTC311
            _ => {}
0c1685f1d412atagen2026-10-16 12:18 UTC312
        }
0c1685f1d412atagen2026-10-16 12:18 UTC313
    }
0c1685f1d412atagen2026-10-16 12:18 UTC314
    state.apply_layout();
0c1685f1d412atagen2026-10-16 12:18 UTC315
}
0c1685f1d412atagen2026-10-16 12:18 UTC316
0c1685f1d412atagen2026-10-16 12:18 UTC317
fn connect(state: &mut State, connector: connector::Info, crtc: crtc::Handle) {
0c1685f1d412atagen2026-10-16 12:18 UTC318
    let name = format!(
0c1685f1d412atagen2026-10-16 12:18 UTC319
        "{}-{}",
0c1685f1d412atagen2026-10-16 12:18 UTC320
        connector.interface().as_str(),
0c1685f1d412atagen2026-10-16 12:18 UTC321
        connector.interface_id()
0c1685f1d412atagen2026-10-16 12:18 UTC322
    );
0c1685f1d412atagen2026-10-16 12:18 UTC323
    let Some(&mode) = connector
0c1685f1d412atagen2026-10-16 12:18 UTC324
        .modes()
0c1685f1d412atagen2026-10-16 12:18 UTC325
        .iter()
0c1685f1d412atagen2026-10-16 12:18 UTC326
        .find(|m| m.mode_type().contains(ModeTypeFlags::PREFERRED))
0c1685f1d412atagen2026-10-16 12:18 UTC327
        .or_else(|| connector.modes().first())
0c1685f1d412atagen2026-10-16 12:18 UTC328
    else {
0c1685f1d412atagen2026-10-16 12:18 UTC329
        tracing::warn!(name, "connector has no modes");
0c1685f1d412atagen2026-10-16 12:18 UTC330
        return;
0c1685f1d412atagen2026-10-16 12:18 UTC331
    };
0c1685f1d412atagen2026-10-16 12:18 UTC332
    let State {
0c1685f1d412atagen2026-10-16 12:18 UTC333
        drm: Some(drm),
0c1685f1d412atagen2026-10-16 12:18 UTC334
        renderer,
0c1685f1d412atagen2026-10-16 12:18 UTC335
        layout,
0c1685f1d412atagen2026-10-16 12:18 UTC336
        space,
0c1685f1d412atagen2026-10-16 12:18 UTC337
        display,
0c1685f1d412atagen2026-10-16 12:18 UTC338
        ..
0c1685f1d412atagen2026-10-16 12:18 UTC339
    } = state
0c1685f1d412atagen2026-10-16 12:18 UTC340
    else {
0c1685f1d412atagen2026-10-16 12:18 UTC341
        return;
0c1685f1d412atagen2026-10-16 12:18 UTC342
    };
0c1685f1d412atagen2026-10-16 12:18 UTC343
    let (mm_w, mm_h) = connector.size().unwrap_or((0, 0));
0c1685f1d412atagen2026-10-16 12:18 UTC344
    let output = Output::new(
0c1685f1d412atagen2026-10-16 12:18 UTC345
        name.clone(),
0c1685f1d412atagen2026-10-16 12:18 UTC346
        PhysicalProperties {
0c1685f1d412atagen2026-10-16 12:18 UTC347
            size: (mm_w as i32, mm_h as i32).into(),
0c1685f1d412atagen2026-10-16 12:18 UTC348
            subpixel: Subpixel::Unknown,
0c1685f1d412atagen2026-10-16 12:18 UTC349
            make: "Unknown".into(),
0c1685f1d412atagen2026-10-16 12:18 UTC350
            model: name.clone(),
0c1685f1d412atagen2026-10-16 12:18 UTC351
            serial_number: String::new(),
0c1685f1d412atagen2026-10-16 12:18 UTC352
        },
0c1685f1d412atagen2026-10-16 12:18 UTC353
    );
0c1685f1d412atagen2026-10-16 12:18 UTC354
    let wl_mode = Mode::from(mode);
0c1685f1d412atagen2026-10-16 12:18 UTC355
    let x = layout
0c1685f1d412atagen2026-10-16 12:18 UTC356
        .outputs()
0c1685f1d412atagen2026-10-16 12:18 UTC357
        .map(|(_, o)| o.area.right())
0c1685f1d412atagen2026-10-16 12:18 UTC358
        .fold(0.0, f64::max);
0c1685f1d412atagen2026-10-16 12:18 UTC359
    let position = Point::from((x as i32, 0));
0c1685f1d412atagen2026-10-16 12:18 UTC360
    output.change_current_state(
0c1685f1d412atagen2026-10-16 12:18 UTC361
        Some(wl_mode),
0c1685f1d412atagen2026-10-16 12:18 UTC362
        Some(Transform::Normal),
0c1685f1d412atagen2026-10-16 12:18 UTC363
        Some(Scale::Integer(1)),
0c1685f1d412atagen2026-10-16 12:18 UTC364
        Some(position),
0c1685f1d412atagen2026-10-16 12:18 UTC365
    );
0c1685f1d412atagen2026-10-16 12:18 UTC366
    output.set_preferred(wl_mode);
0c1685f1d412atagen2026-10-16 12:18 UTC367
    output.create_global::<State>(display);
0c1685f1d412atagen2026-10-16 12:18 UTC368
    let mut planes = drm.manager.device().planes(&crtc).ok();
0c1685f1d412atagen2026-10-16 12:18 UTC369
    if drm.nvidia
0c1685f1d412atagen2026-10-16 12:18 UTC370
        && let Some(p) = planes.as_mut()
0c1685f1d412atagen2026-10-16 12:18 UTC371
    {
0c1685f1d412atagen2026-10-16 12:18 UTC372
        // NVIDIA's overlay planes break composition, as anvil also found.
0c1685f1d412atagen2026-10-16 12:18 UTC373
        p.overlay.clear();
0c1685f1d412atagen2026-10-16 12:18 UTC374
    }
0c1685f1d412atagen2026-10-16 12:18 UTC375
    let out = match drm.manager.lock().initialize_output::<_, Draw>(
0c1685f1d412atagen2026-10-16 12:18 UTC376
        crtc,
0c1685f1d412atagen2026-10-16 12:18 UTC377
        mode,
0c1685f1d412atagen2026-10-16 12:18 UTC378
        &[connector.handle()],
0c1685f1d412atagen2026-10-16 12:18 UTC379
        &output,
0c1685f1d412atagen2026-10-16 12:18 UTC380
        planes,
0c1685f1d412atagen2026-10-16 12:18 UTC381
        renderer,
0c1685f1d412atagen2026-10-16 12:18 UTC382
        &DrmOutputRenderElements::default(),
0c1685f1d412atagen2026-10-16 12:18 UTC383
    ) {
0c1685f1d412atagen2026-10-16 12:18 UTC384
        Ok(out) => out,
0c1685f1d412atagen2026-10-16 12:18 UTC385
        Err(e) => {
0c1685f1d412atagen2026-10-16 12:18 UTC386
            tracing::warn!(name, "could not light up the output: {e}");
0c1685f1d412atagen2026-10-16 12:18 UTC387
            return;
0c1685f1d412atagen2026-10-16 12:18 UTC388
        }
0c1685f1d412atagen2026-10-16 12:18 UTC389
    };
8e6f96f0a32datagen2026-10-16 22:17 UTC390
    let vrr_capable = matches!(
8e6f96f0a32datagen2026-10-16 22:17 UTC391
        out.with_compositor(|c| c.vrr_supported(connector.handle())),
8e6f96f0a32datagen2026-10-16 22:17 UTC392
        Ok(VrrSupport::Supported)
8e6f96f0a32datagen2026-10-16 22:17 UTC393
    );
0c1685f1d412atagen2026-10-16 12:18 UTC394
    space.map_output(&output, position);
0c1685f1d412atagen2026-10-16 12:18 UTC395
    let (w, h) = mode.size();
0c1685f1d412atagen2026-10-16 12:18 UTC396
    let area = Rect::new(x, 0.0, f64::from(w), f64::from(h));
0c1685f1d412atagen2026-10-16 12:18 UTC397
    let id = layout.add_output(&name, area);
8e6f96f0a32datagen2026-10-16 22:17 UTC398
    tracing::info!(
8e6f96f0a32datagen2026-10-16 22:17 UTC399
        name,
8e6f96f0a32datagen2026-10-16 22:17 UTC400
        w,
8e6f96f0a32datagen2026-10-16 22:17 UTC401
        h,
8e6f96f0a32datagen2026-10-16 22:17 UTC402
        refresh = mode.vrefresh(),
8e6f96f0a32datagen2026-10-16 22:17 UTC403
        vrr_capable,
8e6f96f0a32datagen2026-10-16 22:17 UTC404
        "output connected"
8e6f96f0a32datagen2026-10-16 22:17 UTC405
    );
0c1685f1d412atagen2026-10-16 12:18 UTC406
    drm.surfaces.insert(
0c1685f1d412atagen2026-10-16 12:18 UTC407
        crtc,
0c1685f1d412atagen2026-10-16 12:18 UTC408
        Surface {
0c1685f1d412atagen2026-10-16 12:18 UTC409
            output,
0c1685f1d412atagen2026-10-16 12:18 UTC410
            id,
0c1685f1d412atagen2026-10-16 12:18 UTC411
            drm: out,
0c1685f1d412atagen2026-10-16 12:18 UTC412
            queued: false,
0c1685f1d412atagen2026-10-16 12:18 UTC413
            redraw: true,
0c1685f1d412atagen2026-10-16 12:18 UTC414
            last_vblank_ns: None,
6482fe98839datagen2026-10-16 13:01 UTC415
            scheduled: None,
6482fe98839datagen2026-10-16 13:01 UTC416
            target_ns: None,
6482fe98839datagen2026-10-16 13:01 UTC417
            budget: Budget::new(),
6c3361e382e4atagen2026-10-16 13:12 UTC418
            presented: 0,
6c3361e382e4atagen2026-10-16 13:12 UTC419
            stats_since: 0,
8e6f96f0a32datagen2026-10-16 22:17 UTC420
            vrr_capable,
8e6f96f0a32datagen2026-10-16 22:17 UTC421
            vrr_on: false,
8e6f96f0a32datagen2026-10-16 22:17 UTC422
            cursor_redraw: false,
0c1685f1d412atagen2026-10-16 12:18 UTC423
            refresh_ns: 1_000_000_000_000 / u64::from(wl_mode.refresh.max(1) as u32),
0c1685f1d412atagen2026-10-16 12:18 UTC424
        },
0c1685f1d412atagen2026-10-16 12:18 UTC425
    );
0c1685f1d412atagen2026-10-16 12:18 UTC426
}
0c1685f1d412atagen2026-10-16 12:18 UTC427
0c1685f1d412atagen2026-10-16 12:18 UTC428
fn disconnect(state: &mut State, crtc: crtc::Handle) {
6482fe98839datagen2026-10-16 13:01 UTC429
    let Some(drm) = state.drm.as_mut() else {
6482fe98839datagen2026-10-16 13:01 UTC430
        return;
6482fe98839datagen2026-10-16 13:01 UTC431
    };
6482fe98839datagen2026-10-16 13:01 UTC432
    let Some(surface) = drm.surfaces.remove(&crtc) else {
0c1685f1d412atagen2026-10-16 12:18 UTC433
        return;
0c1685f1d412atagen2026-10-16 12:18 UTC434
    };
6482fe98839datagen2026-10-16 13:01 UTC435
    if let Some(token) = surface.scheduled {
6482fe98839datagen2026-10-16 13:01 UTC436
        drm.handle.remove(token);
6482fe98839datagen2026-10-16 13:01 UTC437
    }
0c1685f1d412atagen2026-10-16 12:18 UTC438
    tracing::info!(name = surface.output.name(), "output disconnected");
0c1685f1d412atagen2026-10-16 12:18 UTC439
    state.space.unmap_output(&surface.output);
0c1685f1d412atagen2026-10-16 12:18 UTC440
    state.layout.remove_output(surface.id);
0c1685f1d412atagen2026-10-16 12:18 UTC441
}
0c1685f1d412atagen2026-10-16 12:18 UTC442
0c1685f1d412atagen2026-10-16 12:18 UTC443
/// The smithay output for a layout output, for protocols that need one.
0c1685f1d412atagen2026-10-16 12:18 UTC444
pub fn output_for(drm: &Drm, id: OutputId) -> Option<Output> {
0c1685f1d412atagen2026-10-16 12:18 UTC445
    drm.surfaces
0c1685f1d412atagen2026-10-16 12:18 UTC446
        .values()
0c1685f1d412atagen2026-10-16 12:18 UTC447
        .find(|s| s.id == id)
0c1685f1d412atagen2026-10-16 12:18 UTC448
        .map(|s| s.output.clone())
0c1685f1d412atagen2026-10-16 12:18 UTC449
}
0c1685f1d412atagen2026-10-16 12:18 UTC450
0c1685f1d412atagen2026-10-16 12:18 UTC451
fn on_vblank(state: &mut State, crtc: crtc::Handle, meta: &mut Option<DrmEventMetadata>) {
0c1685f1d412atagen2026-10-16 12:18 UTC452
    let Some(surface) = state.drm.as_mut().and_then(|d| d.surfaces.get_mut(&crtc)) else {
0c1685f1d412atagen2026-10-16 12:18 UTC453
        return;
0c1685f1d412atagen2026-10-16 12:18 UTC454
    };
0c1685f1d412atagen2026-10-16 12:18 UTC455
    if let Err(e) = surface.drm.frame_submitted() {
0c1685f1d412atagen2026-10-16 12:18 UTC456
        tracing::warn!("frame was not submitted: {e}");
0c1685f1d412atagen2026-10-16 12:18 UTC457
    }
0c1685f1d412atagen2026-10-16 12:18 UTC458
    surface.queued = false;
0c1685f1d412atagen2026-10-16 12:18 UTC459
    if let Some(DrmEventTime::Monotonic(t)) = meta.as_ref().map(|m| m.time) {
48af562d968batagen2026-10-16 12:39 UTC460
        let t = t.as_nanos() as u64;
6482fe98839datagen2026-10-16 13:01 UTC461
        if let Some(target) = surface.target_ns.take() {
6482fe98839datagen2026-10-16 13:01 UTC462
            if t > target + surface.refresh_ns / 2 {
6482fe98839datagen2026-10-16 13:01 UTC463
                surface.budget.missed(surface.refresh_ns);
6482fe98839datagen2026-10-16 13:01 UTC464
                tracing::debug!(
6482fe98839datagen2026-10-16 13:01 UTC465
                    output = surface.output.name(),
6482fe98839datagen2026-10-16 13:01 UTC466
                    late_ns = t - target,
6482fe98839datagen2026-10-16 13:01 UTC467
                    budget_ns = surface.budget.ns(),
6482fe98839datagen2026-10-16 13:01 UTC468
                    "frame missed its vblank"
6482fe98839datagen2026-10-16 13:01 UTC469
                );
6482fe98839datagen2026-10-16 13:01 UTC470
            } else {
6482fe98839datagen2026-10-16 13:01 UTC471
                surface.budget.landed();
6482fe98839datagen2026-10-16 13:01 UTC472
            }
48af562d968batagen2026-10-16 12:39 UTC473
        }
48af562d968batagen2026-10-16 12:39 UTC474
        surface.last_vblank_ns = Some(t);
6c3361e382e4atagen2026-10-16 13:12 UTC475
        surface.presented += 1;
6c3361e382e4atagen2026-10-16 13:12 UTC476
        let window = t.saturating_sub(surface.stats_since);
6c3361e382e4atagen2026-10-16 13:12 UTC477
        if window >= 1_000_000_000 {
6c3361e382e4atagen2026-10-16 13:12 UTC478
            let vblanks = window / surface.refresh_ns.max(1);
6c3361e382e4atagen2026-10-16 13:12 UTC479
            tracing::debug!(
6c3361e382e4atagen2026-10-16 13:12 UTC480
                output = surface.output.name(),
6c3361e382e4atagen2026-10-16 13:12 UTC481
                presented = surface.presented,
6c3361e382e4atagen2026-10-16 13:12 UTC482
                vblanks,
6c3361e382e4atagen2026-10-16 13:12 UTC483
                budget_us = surface.budget.ns() / 1000,
6c3361e382e4atagen2026-10-16 13:12 UTC484
                "frame rate"
6c3361e382e4atagen2026-10-16 13:12 UTC485
            );
6c3361e382e4atagen2026-10-16 13:12 UTC486
            surface.presented = 0;
6c3361e382e4atagen2026-10-16 13:12 UTC487
            surface.stats_since = t;
6c3361e382e4atagen2026-10-16 13:12 UTC488
        }
0c1685f1d412atagen2026-10-16 12:18 UTC489
    }
8e6f96f0a32datagen2026-10-16 22:17 UTC490
    if surface.redraw || surface.cursor_redraw {
6482fe98839datagen2026-10-16 13:01 UTC491
        schedule(state, crtc);
6482fe98839datagen2026-10-16 13:01 UTC492
    }
6482fe98839datagen2026-10-16 13:01 UTC493
}
6482fe98839datagen2026-10-16 13:01 UTC494
6482fe98839datagen2026-10-16 13:01 UTC495
/// Starts a render at the last moment that still makes the next reachable
6482fe98839datagen2026-10-16 13:01 UTC496
/// vblank, so commits arriving until then make it into the frame.
6482fe98839datagen2026-10-16 13:01 UTC497
fn schedule(state: &mut State, crtc: crtc::Handle) {
6482fe98839datagen2026-10-16 13:01 UTC498
    let Some(drm) = state.drm.as_mut() else {
6482fe98839datagen2026-10-16 13:01 UTC499
        return;
6482fe98839datagen2026-10-16 13:01 UTC500
    };
6482fe98839datagen2026-10-16 13:01 UTC501
    let Some(surface) = drm.surfaces.get_mut(&crtc) else {
6482fe98839datagen2026-10-16 13:01 UTC502
        return;
6482fe98839datagen2026-10-16 13:01 UTC503
    };
6482fe98839datagen2026-10-16 13:01 UTC504
    if surface.queued || surface.scheduled.is_some() {
6482fe98839datagen2026-10-16 13:01 UTC505
        return;
6482fe98839datagen2026-10-16 13:01 UTC506
    }
6482fe98839datagen2026-10-16 13:01 UTC507
    let now = now_ns();
8e6f96f0a32datagen2026-10-16 22:17 UTC508
    if surface.vrr_on {
8e6f96f0a32datagen2026-10-16 22:17 UTC509
        let since_flip = surface
8e6f96f0a32datagen2026-10-16 22:17 UTC510
            .last_vblank_ns
8e6f96f0a32datagen2026-10-16 22:17 UTC511
            .map_or(u64::MAX, |l| now.saturating_sub(l));
8e6f96f0a32datagen2026-10-16 22:17 UTC512
        if surface.redraw || since_flip >= CURSOR_FRAME_NS {
8e6f96f0a32datagen2026-10-16 22:17 UTC513
            draw(state, crtc, now);
8e6f96f0a32datagen2026-10-16 22:17 UTC514
            return;
8e6f96f0a32datagen2026-10-16 22:17 UTC515
        }
8e6f96f0a32datagen2026-10-16 22:17 UTC516
        let wait = Timer::from_duration(Duration::from_nanos(CURSOR_FRAME_NS - since_flip));
8e6f96f0a32datagen2026-10-16 22:17 UTC517
        let token = drm.handle.insert_source(wait, move |_, _, state| {
8e6f96f0a32datagen2026-10-16 22:17 UTC518
            if let Some(s) = state.drm.as_mut().and_then(|d| d.surfaces.get_mut(&crtc)) {
8e6f96f0a32datagen2026-10-16 22:17 UTC519
                s.scheduled = None;
8e6f96f0a32datagen2026-10-16 22:17 UTC520
            }
8e6f96f0a32datagen2026-10-16 22:17 UTC521
            draw(state, crtc, now_ns());
8e6f96f0a32datagen2026-10-16 22:17 UTC522
            TimeoutAction::Drop
8e6f96f0a32datagen2026-10-16 22:17 UTC523
        });
8e6f96f0a32datagen2026-10-16 22:17 UTC524
        surface.scheduled = token.ok();
8e6f96f0a32datagen2026-10-16 22:17 UTC525
        return;
8e6f96f0a32datagen2026-10-16 22:17 UTC526
    }
6482fe98839datagen2026-10-16 13:01 UTC527
    let Some((vblank, start)) = surface.next_deadline(now) else {
6482fe98839datagen2026-10-16 13:01 UTC528
        draw(state, crtc, now);
6482fe98839datagen2026-10-16 13:01 UTC529
        return;
6482fe98839datagen2026-10-16 13:01 UTC530
    };
6482fe98839datagen2026-10-16 13:01 UTC531
    surface.target_ns = Some(vblank);
6482fe98839datagen2026-10-16 13:01 UTC532
    let wait = Timer::from_duration(Duration::from_nanos(start.saturating_sub(now)));
6482fe98839datagen2026-10-16 13:01 UTC533
    let token = drm.handle.insert_source(wait, move |_, _, state| {
6482fe98839datagen2026-10-16 13:01 UTC534
        let target = state.drm.as_mut().and_then(|d| {
6482fe98839datagen2026-10-16 13:01 UTC535
            let s = d.surfaces.get_mut(&crtc)?;
6482fe98839datagen2026-10-16 13:01 UTC536
            s.scheduled = None;
6482fe98839datagen2026-10-16 13:01 UTC537
            s.target_ns
6482fe98839datagen2026-10-16 13:01 UTC538
        });
6482fe98839datagen2026-10-16 13:01 UTC539
        if let Some(target) = target {
6482fe98839datagen2026-10-16 13:01 UTC540
            draw(state, crtc, target);
6482fe98839datagen2026-10-16 13:01 UTC541
        }
6482fe98839datagen2026-10-16 13:01 UTC542
        TimeoutAction::Drop
6482fe98839datagen2026-10-16 13:01 UTC543
    });
6482fe98839datagen2026-10-16 13:01 UTC544
    surface.scheduled = token.ok();
6482fe98839datagen2026-10-16 13:01 UTC545
}
6482fe98839datagen2026-10-16 13:01 UTC546
6482fe98839datagen2026-10-16 13:01 UTC547
fn draw(state: &mut State, crtc: crtc::Handle, target: u64) {
6482fe98839datagen2026-10-16 13:01 UTC548
    if render_surface(state, crtc, target) {
6482fe98839datagen2026-10-16 13:01 UTC549
        schedule(state, crtc);
6482fe98839datagen2026-10-16 13:01 UTC550
    }
0c1685f1d412atagen2026-10-16 12:18 UTC551
}
0c1685f1d412atagen2026-10-16 12:18 UTC552
0c1685f1d412atagen2026-10-16 12:18 UTC553
/// Renders every output that has something new and no frame in flight.
0c1685f1d412atagen2026-10-16 12:18 UTC554
pub fn render(state: &mut State) {
0c1685f1d412atagen2026-10-16 12:18 UTC555
    let Some(drm) = state.drm.as_mut() else {
0c1685f1d412atagen2026-10-16 12:18 UTC556
        return;
0c1685f1d412atagen2026-10-16 12:18 UTC557
    };
8e6f96f0a32datagen2026-10-16 22:17 UTC558
    let cursor = std::mem::take(&mut state.cursor_moved);
8e6f96f0a32datagen2026-10-16 22:17 UTC559
    let all = std::mem::take(&mut state.needs_redraw);
8e6f96f0a32datagen2026-10-16 22:17 UTC560
    for s in drm.surfaces.values_mut() {
8e6f96f0a32datagen2026-10-16 22:17 UTC561
        s.redraw |= all;
8e6f96f0a32datagen2026-10-16 22:17 UTC562
        s.cursor_redraw |= cursor;
0c1685f1d412atagen2026-10-16 12:18 UTC563
    }
0c1685f1d412atagen2026-10-16 12:18 UTC564
    let ready: Vec<crtc::Handle> = drm
0c1685f1d412atagen2026-10-16 12:18 UTC565
        .surfaces
0c1685f1d412atagen2026-10-16 12:18 UTC566
        .iter()
8e6f96f0a32datagen2026-10-16 22:17 UTC567
        .filter(|(_, s)| (s.redraw || s.cursor_redraw) && !s.queued)
0c1685f1d412atagen2026-10-16 12:18 UTC568
        .map(|(c, _)| *c)
0c1685f1d412atagen2026-10-16 12:18 UTC569
        .collect();
0c1685f1d412atagen2026-10-16 12:18 UTC570
    for crtc in ready {
6482fe98839datagen2026-10-16 13:01 UTC571
        schedule(state, crtc);
0c1685f1d412atagen2026-10-16 12:18 UTC572
    }
0c1685f1d412atagen2026-10-16 12:18 UTC573
}
0c1685f1d412atagen2026-10-16 12:18 UTC574
6482fe98839datagen2026-10-16 13:01 UTC575
/// Draws one output for the vblank at `target`. Returns whether it still
6482fe98839datagen2026-10-16 13:01 UTC576
/// animates without a frame in flight, and so needs another render.
30aece3a63ceatagen2026-10-16 13:48 UTC577
#[profiling::function]
6482fe98839datagen2026-10-16 13:01 UTC578
fn render_surface(state: &mut State, crtc: crtc::Handle, target: u64) -> bool {
0c1685f1d412atagen2026-10-16 12:18 UTC579
    let State {
0c1685f1d412atagen2026-10-16 12:18 UTC580
        drm: Some(drm),
0c1685f1d412atagen2026-10-16 12:18 UTC581
        renderer,
0c1685f1d412atagen2026-10-16 12:18 UTC582
        layout,
0c1685f1d412atagen2026-10-16 12:18 UTC583
        windows,
0c1685f1d412atagen2026-10-16 12:18 UTC584
        anim,
0c1685f1d412atagen2026-10-16 12:18 UTC585
        borders,
0c1685f1d412atagen2026-10-16 12:18 UTC586
        config,
0c1685f1d412atagen2026-10-16 12:18 UTC587
        space,
0c1685f1d412atagen2026-10-16 12:18 UTC588
        start,
0c1685f1d412atagen2026-10-16 12:18 UTC589
        cursor,
0c1685f1d412atagen2026-10-16 12:18 UTC590
        cursor_status,
0c1685f1d412atagen2026-10-16 12:18 UTC591
        pointer_pos,
0c1685f1d412atagen2026-10-16 12:18 UTC592
        ..
0c1685f1d412atagen2026-10-16 12:18 UTC593
    } = state
0c1685f1d412atagen2026-10-16 12:18 UTC594
    else {
6482fe98839datagen2026-10-16 13:01 UTC595
        return false;
0c1685f1d412atagen2026-10-16 12:18 UTC596
    };
0c1685f1d412atagen2026-10-16 12:18 UTC597
    if !drm.session.is_active() {
6482fe98839datagen2026-10-16 13:01 UTC598
        return false;
0c1685f1d412atagen2026-10-16 12:18 UTC599
    }
0c1685f1d412atagen2026-10-16 12:18 UTC600
    let Some(surface) = drm.surfaces.get_mut(&crtc) else {
6482fe98839datagen2026-10-16 13:01 UTC601
        return false;
0c1685f1d412atagen2026-10-16 12:18 UTC602
    };
0c1685f1d412atagen2026-10-16 12:18 UTC603
    if surface.queued {
6482fe98839datagen2026-10-16 13:01 UTC604
        return false;
0c1685f1d412atagen2026-10-16 12:18 UTC605
    }
8e6f96f0a32datagen2026-10-16 22:17 UTC606
    surface.cursor_redraw = false;
8e6f96f0a32datagen2026-10-16 22:17 UTC607
    let want_vrr = surface.vrr_capable
8e6f96f0a32datagen2026-10-16 22:17 UTC608
        && match config.output.vrr {
8e6f96f0a32datagen2026-10-16 22:17 UTC609
            Vrr::Off => false,
8e6f96f0a32datagen2026-10-16 22:17 UTC610
            Vrr::On => true,
8e6f96f0a32datagen2026-10-16 22:17 UTC611
            Vrr::Fullscreen => layout.arrange(surface.id).fullscreen.is_some(),
8e6f96f0a32datagen2026-10-16 22:17 UTC612
        };
8e6f96f0a32datagen2026-10-16 22:17 UTC613
    if want_vrr != surface.vrr_on {
8e6f96f0a32datagen2026-10-16 22:17 UTC614
        match surface.drm.with_compositor(|c| c.use_vrr(want_vrr)) {
8e6f96f0a32datagen2026-10-16 22:17 UTC615
            Ok(()) => {
8e6f96f0a32datagen2026-10-16 22:17 UTC616
                surface.vrr_on = want_vrr;
8e6f96f0a32datagen2026-10-16 22:17 UTC617
                tracing::info!(
8e6f96f0a32datagen2026-10-16 22:17 UTC618
                    output = surface.output.name(),
8e6f96f0a32datagen2026-10-16 22:17 UTC619
                    vrr = want_vrr,
8e6f96f0a32datagen2026-10-16 22:17 UTC620
                    "adaptive sync"
8e6f96f0a32datagen2026-10-16 22:17 UTC621
                );
8e6f96f0a32datagen2026-10-16 22:17 UTC622
            }
8e6f96f0a32datagen2026-10-16 22:17 UTC623
            Err(e) => {
8e6f96f0a32datagen2026-10-16 22:17 UTC624
                tracing::warn!("could not switch adaptive sync: {e}");
8e6f96f0a32datagen2026-10-16 22:17 UTC625
                surface.vrr_capable = false;
8e6f96f0a32datagen2026-10-16 22:17 UTC626
            }
8e6f96f0a32datagen2026-10-16 22:17 UTC627
        }
8e6f96f0a32datagen2026-10-16 22:17 UTC628
    }
48af562d968batagen2026-10-16 12:39 UTC629
    let began = std::time::Instant::now();
6482fe98839datagen2026-10-16 13:01 UTC630
    let now = target;
0c1685f1d412atagen2026-10-16 12:18 UTC631
    let Some(area) = layout.output(surface.id).map(|o| o.area) else {
6482fe98839datagen2026-10-16 13:01 UTC632
        return false;
0c1685f1d412atagen2026-10-16 12:18 UTC633
    };
0c1685f1d412atagen2026-10-16 12:18 UTC634
    let mut elements = Vec::new();
0c1685f1d412atagen2026-10-16 12:18 UTC635
    let local = *pointer_pos - Point::from((area.x, area.y));
0c1685f1d412atagen2026-10-16 12:18 UTC636
    if local.x >= 0.0 && local.y >= 0.0 && local.x < area.w && local.y < area.h {
0c1685f1d412atagen2026-10-16 12:18 UTC637
        elements.extend(cursor.elements(renderer, cursor_status, local));
0c1685f1d412atagen2026-10-16 12:18 UTC638
    }
32fbd7e2725catagen2026-10-16 13:19 UTC639
    let scene_began = std::time::Instant::now();
0c1685f1d412atagen2026-10-16 12:18 UTC640
    elements.extend(crate::scene::build(
0c1685f1d412atagen2026-10-16 12:18 UTC641
        layout,
0c1685f1d412atagen2026-10-16 12:18 UTC642
        surface.id,
0c1685f1d412atagen2026-10-16 12:18 UTC643
        windows,
0c1685f1d412atagen2026-10-16 12:18 UTC644
        anim,
0c1685f1d412atagen2026-10-16 12:18 UTC645
        renderer,
0c1685f1d412atagen2026-10-16 12:18 UTC646
        now,
0c1685f1d412atagen2026-10-16 12:18 UTC647
        &config.decoration,
0c1685f1d412atagen2026-10-16 12:18 UTC648
        borders,
0c1685f1d412atagen2026-10-16 12:18 UTC649
    ));
32fbd7e2725catagen2026-10-16 13:19 UTC650
    let scene_ns = scene_began.elapsed().as_nanos() as u64;
0c1685f1d412atagen2026-10-16 12:18 UTC651
    let animating = anim.active(now);
0c1685f1d412atagen2026-10-16 12:18 UTC652
    anim.prune(now);
32fbd7e2725catagen2026-10-16 13:19 UTC653
    let render_began = std::time::Instant::now();
d96bfa1d0892atagen2026-10-16 13:38 UTC654
    let cpu_began = thread_cpu_ns();
32fbd7e2725catagen2026-10-16 13:19 UTC655
    let mut queue_ns = 0;
0c1685f1d412atagen2026-10-16 12:18 UTC656
    match surface
0c1685f1d412atagen2026-10-16 12:18 UTC657
        .drm
fdb16f42492eatagen2026-10-16 13:40 UTC658
        .render_frame(renderer, &elements, CLEAR, drm.frame_flags)
0c1685f1d412atagen2026-10-16 12:18 UTC659
    {
32fbd7e2725catagen2026-10-16 13:19 UTC660
        Ok(result) if !result.is_empty => {
32fbd7e2725catagen2026-10-16 13:19 UTC661
            let queue_began = std::time::Instant::now();
32fbd7e2725catagen2026-10-16 13:19 UTC662
            match surface.drm.queue_frame(()) {
32fbd7e2725catagen2026-10-16 13:19 UTC663
                Ok(()) => surface.queued = true,
32fbd7e2725catagen2026-10-16 13:19 UTC664
                Err(e) => tracing::warn!("queueing a frame failed: {e}"),
32fbd7e2725catagen2026-10-16 13:19 UTC665
            }
32fbd7e2725catagen2026-10-16 13:19 UTC666
            queue_ns = queue_began.elapsed().as_nanos() as u64;
32fbd7e2725catagen2026-10-16 13:19 UTC667
        }
0c1685f1d412atagen2026-10-16 12:18 UTC668
        Ok(_) => {}
0c1685f1d412atagen2026-10-16 12:18 UTC669
        Err(e) => tracing::warn!("rendering failed: {e}"),
0c1685f1d412atagen2026-10-16 12:18 UTC670
    }
d96bfa1d0892atagen2026-10-16 13:38 UTC671
    let render_cpu_ns = thread_cpu_ns() - cpu_began;
32fbd7e2725catagen2026-10-16 13:19 UTC672
    let render_ns = render_began.elapsed().as_nanos() as u64 - queue_ns;
6482fe98839datagen2026-10-16 13:01 UTC673
    let took = began.elapsed().as_nanos() as u64;
6482fe98839datagen2026-10-16 13:01 UTC674
    surface.budget.record(took);
6482fe98839datagen2026-10-16 13:01 UTC675
    if took > surface.refresh_ns / 2 {
32fbd7e2725catagen2026-10-16 13:19 UTC676
        tracing::debug!(
32fbd7e2725catagen2026-10-16 13:19 UTC677
            output = surface.output.name(),
32fbd7e2725catagen2026-10-16 13:19 UTC678
            took_ns = took,
32fbd7e2725catagen2026-10-16 13:19 UTC679
            scene_ns,
32fbd7e2725catagen2026-10-16 13:19 UTC680
            render_ns,
d96bfa1d0892atagen2026-10-16 13:38 UTC681
            render_cpu_ns,
32fbd7e2725catagen2026-10-16 13:19 UTC682
            queue_ns,
32fbd7e2725catagen2026-10-16 13:19 UTC683
            "slow frame"
32fbd7e2725catagen2026-10-16 13:19 UTC684
        );
48af562d968batagen2026-10-16 12:39 UTC685
    }
6482fe98839datagen2026-10-16 13:01 UTC686
    if !surface.queued {
6482fe98839datagen2026-10-16 13:01 UTC687
        surface.target_ns = None;
0c1685f1d412atagen2026-10-16 12:18 UTC688
    }
6482fe98839datagen2026-10-16 13:01 UTC689
    surface.redraw = animating;
0c1685f1d412atagen2026-10-16 12:18 UTC690
    let elapsed = start.elapsed();
0c1685f1d412atagen2026-10-16 12:18 UTC691
    let output = surface.output.clone();
0c1685f1d412atagen2026-10-16 12:18 UTC692
    for window in space.elements_for_output(&output) {
0c1685f1d412atagen2026-10-16 12:18 UTC693
        window.send_frame(&output, elapsed, Some(Duration::ZERO), |_, _| {
0c1685f1d412atagen2026-10-16 12:18 UTC694
            Some(output.clone())
0c1685f1d412atagen2026-10-16 12:18 UTC695
        });
0c1685f1d412atagen2026-10-16 12:18 UTC696
    }
6482fe98839datagen2026-10-16 13:01 UTC697
    animating && !surface.queued
0c1685f1d412atagen2026-10-16 12:18 UTC698
}
0c1685f1d412atagen2026-10-16 12:18 UTC699
d96bfa1d0892atagen2026-10-16 13:38 UTC700
/// CPU time this thread has used, to tell a render that works from one that
d96bfa1d0892atagen2026-10-16 13:38 UTC701
/// waits or is descheduled.
d96bfa1d0892atagen2026-10-16 13:38 UTC702
fn thread_cpu_ns() -> u64 {
d96bfa1d0892atagen2026-10-16 13:38 UTC703
    let t = smithay::reexports::rustix::time::clock_gettime(
d96bfa1d0892atagen2026-10-16 13:38 UTC704
        smithay::reexports::rustix::time::ClockId::ThreadCPUTime,
d96bfa1d0892atagen2026-10-16 13:38 UTC705
    );
d96bfa1d0892atagen2026-10-16 13:38 UTC706
    t.tv_sec as u64 * 1_000_000_000 + t.tv_nsec as u64
d96bfa1d0892atagen2026-10-16 13:38 UTC707
}
d96bfa1d0892atagen2026-10-16 13:38 UTC708
0c1685f1d412atagen2026-10-16 12:18 UTC709
fn on_input(state: &mut State, event: InputEvent<LibinputInputBackend>) {
0c1685f1d412atagen2026-10-16 12:18 UTC710
    match event {
0c1685f1d412atagen2026-10-16 12:18 UTC711
        InputEvent::Keyboard { event } => {
0c1685f1d412atagen2026-10-16 12:18 UTC712
            let time = event.time();
0c1685f1d412atagen2026-10-16 12:18 UTC713
            state.key(event.key_code(), event.state(), time);
0c1685f1d412atagen2026-10-16 12:18 UTC714
        }
0c1685f1d412atagen2026-10-16 12:18 UTC715
        InputEvent::PointerMotion { event } => {
0c1685f1d412atagen2026-10-16 12:18 UTC716
            let pos = clamp(state, state.pointer_pos + event.delta());
0c1685f1d412atagen2026-10-16 12:18 UTC717
            state.pointer_to(pos, event.time());
0c1685f1d412atagen2026-10-16 12:18 UTC718
            state.pointer_frame();
8e6f96f0a32datagen2026-10-16 22:17 UTC719
            state.cursor_moved = true;
0c1685f1d412atagen2026-10-16 12:18 UTC720
        }
0c1685f1d412atagen2026-10-16 12:18 UTC721
        InputEvent::PointerMotionAbsolute { event } => {
0c1685f1d412atagen2026-10-16 12:18 UTC722
            let Some(area) = state.layout.outputs().next().map(|(_, o)| o.area) else {
0c1685f1d412atagen2026-10-16 12:18 UTC723
                return;
0c1685f1d412atagen2026-10-16 12:18 UTC724
            };
0c1685f1d412atagen2026-10-16 12:18 UTC725
            let p = event.position_transformed((area.w as i32, area.h as i32).into());
0c1685f1d412atagen2026-10-16 12:18 UTC726
            let pos = Point::from((area.x + p.x, area.y + p.y));
0c1685f1d412atagen2026-10-16 12:18 UTC727
            state.pointer_to(pos, event.time());
0c1685f1d412atagen2026-10-16 12:18 UTC728
            state.pointer_frame();
8e6f96f0a32datagen2026-10-16 22:17 UTC729
            state.cursor_moved = true;
0c1685f1d412atagen2026-10-16 12:18 UTC730
        }
0c1685f1d412atagen2026-10-16 12:18 UTC731
        InputEvent::PointerButton { event } => {
0c1685f1d412atagen2026-10-16 12:18 UTC732
            let time = event.time();
0c1685f1d412atagen2026-10-16 12:18 UTC733
            state.pointer_button(event.button_code(), event.state(), time);
0c1685f1d412atagen2026-10-16 12:18 UTC734
            state.pointer_frame();
0c1685f1d412atagen2026-10-16 12:18 UTC735
        }
0c1685f1d412atagen2026-10-16 12:18 UTC736
        InputEvent::PointerAxis { event } => {
0c1685f1d412atagen2026-10-16 12:18 UTC737
            let source = event.source();
0c1685f1d412atagen2026-10-16 12:18 UTC738
            let mut frame = AxisFrame::new(event.time()).source(source);
0c1685f1d412atagen2026-10-16 12:18 UTC739
            for axis in [Axis::Horizontal, Axis::Vertical] {
0c1685f1d412atagen2026-10-16 12:18 UTC740
                if let Some(v120) = event.amount_v120(axis) {
0c1685f1d412atagen2026-10-16 12:18 UTC741
                    frame = frame.v120(axis, v120 as i32);
0c1685f1d412atagen2026-10-16 12:18 UTC742
                }
0c1685f1d412atagen2026-10-16 12:18 UTC743
                let amount = event
0c1685f1d412atagen2026-10-16 12:18 UTC744
                    .amount(axis)
0c1685f1d412atagen2026-10-16 12:18 UTC745
                    .or_else(|| event.amount_v120(axis).map(|v| v * 15.0 / 120.0));
0c1685f1d412atagen2026-10-16 12:18 UTC746
                match amount {
0c1685f1d412atagen2026-10-16 12:18 UTC747
                    Some(a) if a != 0.0 => frame = frame.value(axis, a),
0c1685f1d412atagen2026-10-16 12:18 UTC748
                    Some(_) if source == AxisSource::Finger => frame = frame.stop(axis),
0c1685f1d412atagen2026-10-16 12:18 UTC749
                    _ => {}
0c1685f1d412atagen2026-10-16 12:18 UTC750
                }
0c1685f1d412atagen2026-10-16 12:18 UTC751
            }
0c1685f1d412atagen2026-10-16 12:18 UTC752
            state.pointer_axis(frame);
0c1685f1d412atagen2026-10-16 12:18 UTC753
            state.pointer_frame();
0c1685f1d412atagen2026-10-16 12:18 UTC754
        }
0c1685f1d412atagen2026-10-16 12:18 UTC755
        _ => {}
0c1685f1d412atagen2026-10-16 12:18 UTC756
    }
0c1685f1d412atagen2026-10-16 12:18 UTC757
}
0c1685f1d412atagen2026-10-16 12:18 UTC758
0c1685f1d412atagen2026-10-16 12:18 UTC759
/// Keeps the pointer on some output, sliding along the edge it hit.
0c1685f1d412atagen2026-10-16 12:18 UTC760
fn clamp(
0c1685f1d412atagen2026-10-16 12:18 UTC761
    state: &State,
0c1685f1d412atagen2026-10-16 12:18 UTC762
    pos: Point<f64, smithay::utils::Logical>,
0c1685f1d412atagen2026-10-16 12:18 UTC763
) -> Point<f64, smithay::utils::Logical> {
0c1685f1d412atagen2026-10-16 12:18 UTC764
    let inside = |a: &Rect, p: Point<f64, smithay::utils::Logical>| {
0c1685f1d412atagen2026-10-16 12:18 UTC765
        p.x >= a.x && p.y >= a.y && p.x < a.right() && p.y < a.bottom()
0c1685f1d412atagen2026-10-16 12:18 UTC766
    };
0c1685f1d412atagen2026-10-16 12:18 UTC767
    let areas: Vec<Rect> = state.layout.outputs().map(|(_, o)| o.area).collect();
0c1685f1d412atagen2026-10-16 12:18 UTC768
    if areas.iter().any(|a| inside(a, pos)) {
0c1685f1d412atagen2026-10-16 12:18 UTC769
        return pos;
0c1685f1d412atagen2026-10-16 12:18 UTC770
    }
0c1685f1d412atagen2026-10-16 12:18 UTC771
    let Some(a) = areas
0c1685f1d412atagen2026-10-16 12:18 UTC772
        .iter()
0c1685f1d412atagen2026-10-16 12:18 UTC773
        .find(|a| inside(a, state.pointer_pos))
0c1685f1d412atagen2026-10-16 12:18 UTC774
        .or(areas.first())
0c1685f1d412atagen2026-10-16 12:18 UTC775
    else {
0c1685f1d412atagen2026-10-16 12:18 UTC776
        return pos;
0c1685f1d412atagen2026-10-16 12:18 UTC777
    };
0c1685f1d412atagen2026-10-16 12:18 UTC778
    Point::from((
0c1685f1d412atagen2026-10-16 12:18 UTC779
        pos.x.clamp(a.x, a.right() - 1.0),
0c1685f1d412atagen2026-10-16 12:18 UTC780
        pos.y.clamp(a.y, a.bottom() - 1.0),
0c1685f1d412atagen2026-10-16 12:18 UTC781
    ))
0c1685f1d412atagen2026-10-16 12:18 UTC782
}