1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
// SPDX-FileCopyrightText: (c) 2026 Joel Winarske
// SPDX-License-Identifier: MIT
//! Holding devices across a VT switch.
//!
//! A compositor that owns the display cannot simply open `/dev/dri/card0` and
//! keep it: switching to another virtual terminal has to take the display away
//! and give it back. A seat -- logind, seatd, or a bare-root fallback -- is
//! what arbitrates that, handing out descriptors it can revoke.
//!
//! What arrives from the seat is two events. *Paused*: stop touching every
//! descriptor immediately, because the ioctls will start failing. *Resumed*:
//! here are fresh descriptors, and every per-descriptor kernel object you had
//! -- framebuffers, property blobs, client caps, GEM handles -- is gone.
//! `drmkit_cursor::Paused` is the shape that pairs with this on the other
//! side.
//!
//! # Why the backend is a trait
//!
//! Because a seat cannot be opened on a machine that has no session. Over SSH,
//! in a container, on a CI runner, logind says the session is busy, seatd is
//! not running, and the builtin backend wants root and a TTY. That is most
//! places this code will be built.
//!
//! So the sequencing -- which devices are held, what happens to them across a
//! pause, when the pause is acknowledged, what a caller is allowed to do while
//! inactive -- lives in [`Session`] over a [`Backend`], and is tested against
//! a fake. The libseat binding is a thin adapter behind the `libseat` feature,
//! and libseat's own diagnostics arrive through `drmkit-log`, tagged
//! `[libseat]`, at error level.
//!
//! # Not here yet
//!
//! Two things the reference has and this does not.
//!
//! `TakeDeviceOpts::preserve_fd_across_resume` asks the seat to hand back the
//! same descriptor rather than a fresh one, on the grounds that the
//! capability-revoking backends leave it valid. Plausible, and it saves a
//! reopen per device per switch, but it is an optimization over a path that is
//! already correct -- and getting it wrong means scanning out through a
//! descriptor the kernel has stopped honoring, on a machine none of the CI
//! lanes resemble. It waits for a device to be proved on.
//!
//! `Seat::input_opener` routes libinput's privileged device opens through the
//! same seat, so input descriptors get the revocable lifetime the DRM one has.
//!
//! Both halves it needs now exist: this crate opens devices through a seat,
//! and `drmkit_input::Seat::open_with` takes any `LibinputInterface` the caller
//! supplies. What is missing is the piece between them — an implementation of
//! that trait backed by a [`Session`], so an input descriptor is revoked and
//! reissued on a VT switch the way the DRM one is. Until it exists a caller
//! passing their own opener gets input descriptors with an ordinary lifetime,
//! which works and is not what the seat is for.
pub use ;
pub use ;
/// What can go wrong holding a session.