1use libseat::{Seat, SeatEvent};
7use std::{
8 cell::RefCell,
9 collections::HashMap,
10 os::unix::io::{AsFd, AsRawFd, FromRawFd, OwnedFd, RawFd},
11 path::Path,
12 rc::{Rc, Weak},
13 sync::{
14 atomic::{AtomicBool, Ordering},
15 Arc,
16 },
17};
18
19use rustix::{fs::OFlags, io::Errno};
20
21use calloop::{
22 channel::{self, Channel},
23 EventSource, Poll, PostAction, Readiness, Token, TokenFactory,
24};
25
26use crate::backend::session::{AsErrno, Event as SessionEvent, Session};
27
28use tracing::{debug, error, info_span, instrument};
29
30#[derive(Debug)]
31struct LibSeatSessionImpl {
32 seat: RefCell<Seat>,
33 active: Arc<AtomicBool>,
34 devices: RefCell<HashMap<RawFd, libseat::Device>>,
35}
36
37impl Drop for LibSeatSessionImpl {
38 fn drop(&mut self) {
39 debug!("Closing seat")
40 }
41}
42
43#[derive(Debug, Clone)]
45pub struct LibSeatSession {
46 internal: Weak<LibSeatSessionImpl>,
47 seat_name: String,
48 span: tracing::Span,
49}
50
51#[derive(Debug)]
53pub struct LibSeatSessionNotifier {
54 internal: Rc<LibSeatSessionImpl>,
55 rx: Channel<SeatEvent>,
56 token: Option<Token>,
57 span: tracing::Span,
58}
59
60impl LibSeatSession {
61 pub fn new() -> Result<(LibSeatSession, LibSeatSessionNotifier), Error> {
63 let span = info_span!("backend_session", "type" = "libseat");
64 let _guard = span.enter();
65 let (tx, rx) = calloop::channel::channel();
66
67 let seat = {
68 Seat::open(move |_seat, event| match event {
69 SeatEvent::Enable => {
70 debug!("Enable callback called");
71 tx.send(event).unwrap();
72 }
73 SeatEvent::Disable => {
74 debug!("Disable callback called");
75 tx.send(event).unwrap();
76 }
77 })
78 };
79
80 drop(_guard);
81 seat.map(|mut seat| {
82 let seat_name = seat.name().to_owned();
83
84 seat.dispatch(0).unwrap();
87 let active = matches!(rx.try_recv(), Ok(SeatEvent::Enable));
88
89 let internal = Rc::new(LibSeatSessionImpl {
90 seat: RefCell::new(seat),
91 active: Arc::new(AtomicBool::new(active)),
92 devices: RefCell::new(HashMap::new()),
93 });
94
95 let session = LibSeatSession {
96 internal: Rc::downgrade(&internal),
97 seat_name,
98 span: span.clone(),
99 };
100
101 let notifier = LibSeatSessionNotifier {
102 internal,
103 rx,
104 token: None,
105 span,
106 };
107
108 (session, notifier)
109 })
110 .map_err(|err| Error::FailedToOpenSession(Errno::from_raw_os_error(err.into())))
111 }
112}
113
114impl Session for LibSeatSession {
115 type Error = Error;
116
117 #[instrument(parent = &self.span, skip(self))]
118 fn open(&mut self, path: &Path, _flags: OFlags) -> Result<OwnedFd, Self::Error> {
119 if let Some(session) = self.internal.upgrade() {
120 debug!("Opening device: {:?}", path);
121
122 session
123 .seat
124 .borrow_mut()
125 .open_device(&path)
126 .map(|device| {
127 let raw_fd = device.as_fd().as_raw_fd();
128
129 session.devices.borrow_mut().insert(raw_fd, device);
130
131 unsafe { OwnedFd::from_raw_fd(raw_fd) }
133 })
134 .map_err(|err| Error::FailedToOpenDevice(Errno::from_raw_os_error(err.into())))
135 } else {
136 Err(Error::SessionLost)
137 }
138 }
139
140 #[instrument(parent = &self.span, skip(self))]
141 fn close(&mut self, fd: OwnedFd) -> Result<(), Self::Error> {
142 if let Some(session) = self.internal.upgrade() {
143 debug!("Closing device: {:?}", fd);
144
145 let out = if let Some(dev) = session.devices.borrow_mut().remove(&fd.as_fd().as_raw_fd()) {
146 session
147 .seat
148 .borrow_mut()
149 .close_device(dev)
150 .map_err(|err| Error::FailedToCloseDevice(Errno::from_raw_os_error(err.into())))
151 } else {
152 Ok(())
153 };
154
155 out
158 } else {
159 Err(Error::SessionLost)
160 }
161 }
162
163 #[instrument(parent = &self.span, skip(self))]
164 fn change_vt(&mut self, vt: i32) -> Result<(), Self::Error> {
165 if let Some(session) = self.internal.upgrade() {
166 debug!("Session switch: {:?}", vt);
167 session
168 .seat
169 .borrow_mut()
170 .switch_session(vt)
171 .map_err(|err| Error::FailedToChangeVt(Errno::from_raw_os_error(err.into())))
172 } else {
173 Err(Error::SessionLost)
174 }
175 }
176
177 fn is_active(&self) -> bool {
178 if let Some(internal) = self.internal.upgrade() {
179 internal.active.load(Ordering::SeqCst)
180 } else {
181 false
182 }
183 }
184
185 fn seat(&self) -> String {
186 self.seat_name.clone()
187 }
188}
189
190impl LibSeatSessionNotifier {
191 pub fn session(&self) -> LibSeatSession {
193 LibSeatSession {
194 internal: Rc::downgrade(&self.internal),
195 seat_name: self.internal.seat.borrow_mut().name().to_owned(),
196 span: self.span.clone(),
197 }
198 }
199}
200
201impl EventSource for LibSeatSessionNotifier {
202 type Event = SessionEvent;
203 type Metadata = ();
204 type Ret = ();
205 type Error = Error;
206
207 #[profiling::function]
208 fn process_events<F>(
209 &mut self,
210 readiness: Readiness,
211 token: Token,
212 mut callback: F,
213 ) -> Result<PostAction, Error>
214 where
215 F: FnMut(SessionEvent, &mut ()),
216 {
217 if Some(token) == self.token {
218 self.internal.seat.borrow_mut().dispatch(0).unwrap();
219 }
220
221 let internal = &self.internal;
222 self.rx
223 .process_events(readiness, token, |event, _| match event {
224 channel::Event::Msg(event) => match event {
225 SeatEvent::Enable => {
226 internal.active.store(true, Ordering::SeqCst);
227 callback(SessionEvent::ActivateSession, &mut ());
228 }
229 SeatEvent::Disable => {
230 internal.active.store(false, Ordering::SeqCst);
231 internal.seat.borrow_mut().disable().unwrap();
232 callback(SessionEvent::PauseSession, &mut ());
233 }
234 },
235 channel::Event::Closed => {
236 }
238 })
239 .map_err(|_| Error::SessionLost)
240 }
241
242 fn register(&mut self, poll: &mut Poll, factory: &mut TokenFactory) -> calloop::Result<()> {
243 self.rx.register(poll, factory)?;
244
245 self.token = Some(factory.token());
246 let mut seat = self.internal.seat.borrow_mut();
247 unsafe {
249 poll.register(
250 seat.get_fd().unwrap(),
251 calloop::Interest::READ,
252 calloop::Mode::Level,
253 self.token.unwrap(),
254 )
255 }
256 }
257
258 fn reregister(&mut self, poll: &mut Poll, factory: &mut TokenFactory) -> calloop::Result<()> {
259 self.rx.reregister(poll, factory)?;
260
261 self.token = Some(factory.token());
262 let mut seat = self.internal.seat.borrow_mut();
263 poll.reregister(
264 seat.get_fd().unwrap(),
265 calloop::Interest::READ,
266 calloop::Mode::Level,
267 self.token.unwrap(),
268 )
269 }
270
271 fn unregister(&mut self, poll: &mut Poll) -> calloop::Result<()> {
272 self.rx.unregister(poll)?;
273
274 self.token = None;
275 let mut seat = self.internal.seat.borrow_mut();
276 poll.unregister(seat.get_fd().unwrap())
277 }
278}
279
280#[derive(thiserror::Error, Debug)]
282pub enum Error {
283 #[error("Failed to open session: {0}")]
285 FailedToOpenSession(Errno),
286
287 #[error("Failed to open device: {0}")]
289 FailedToOpenDevice(Errno),
290
291 #[error("Failed to close device: {0}")]
293 FailedToCloseDevice(Errno),
294
295 #[error("Failed to change vt: {0}")]
297 FailedToChangeVt(Errno),
298
299 #[error("Session is already closed")]
301 SessionLost,
302}
303
304impl AsErrno for Error {
305 fn as_errno(&self) -> Option<i32> {
306 match self {
307 &Self::FailedToOpenSession(errno)
308 | &Self::FailedToOpenDevice(errno)
309 | &Self::FailedToCloseDevice(errno)
310 | &Self::FailedToChangeVt(errno) => Some(errno.raw_os_error()),
311 _ => None,
312 }
313 }
314}