[go: up one dir, main page]

libseat/
lib.rs

1use libseat_sys as sys;
2
3use errno::{errno, Errno};
4
5use std::{
6    ffi::CString,
7    mem::MaybeUninit,
8    ops::{Deref, DerefMut},
9    os::unix::io::{AsFd, BorrowedFd, RawFd},
10    path::Path,
11    ptr::NonNull,
12};
13
14mod ffi_seat_listener;
15use ffi_seat_listener::FFI_SEAT_LISTENER;
16
17mod log;
18pub use self::log::*;
19
20#[cfg(feature = "custom_logger")]
21mod log_handler;
22
23#[derive(Debug, Clone, Copy)]
24pub enum SeatEvent {
25    Enable,
26    Disable,
27}
28
29type SeatListenerCallback = dyn FnMut(&mut SeatRef, SeatEvent);
30
31struct SeatListener {
32    callback: Box<SeatListenerCallback>,
33}
34
35impl std::fmt::Debug for SeatListener {
36    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
37        f.debug_struct("UserSeatListener").finish()
38    }
39}
40
41#[derive(Debug)]
42pub struct Seat {
43    inner: SeatRef,
44    _seat_listener: Box<SeatListener>,
45}
46
47impl Seat {
48    /// Opens a seat, taking control of it if possible and returning a pointer to
49    /// the libseat instance. If LIBSEAT_BACKEND is set, the specified backend is
50    /// used. Otherwise, the first successful backend will be used.
51    pub fn open<C>(callback: C) -> Result<Self, Errno>
52    where
53        C: FnMut(&mut SeatRef, SeatEvent) + 'static,
54    {
55        #[cfg(feature = "custom_logger")]
56        log_handler::init();
57
58        let user_listener = SeatListener {
59            callback: Box::new(callback),
60        };
61
62        let mut user_data = Box::new(user_listener);
63
64        let seat = unsafe {
65            sys::libseat_open_seat(&FFI_SEAT_LISTENER, user_data.as_mut() as *mut _ as _)
66        };
67
68        NonNull::new(seat)
69            .map(|nn| Self {
70                inner: SeatRef(nn),
71                _seat_listener: user_data,
72            })
73            .ok_or_else(errno)
74    }
75}
76
77impl Drop for Seat {
78    fn drop(&mut self) {
79        // Closes the seat. This frees the libseat structure.
80        unsafe { sys::libseat_close_seat(self.0.as_mut()) };
81    }
82}
83
84impl Deref for Seat {
85    type Target = SeatRef;
86
87    fn deref(&self) -> &Self::Target {
88        &self.inner
89    }
90}
91
92impl DerefMut for Seat {
93    fn deref_mut(&mut self) -> &mut Self::Target {
94        &mut self.inner
95    }
96}
97
98#[derive(Debug)]
99pub struct SeatRef(NonNull<sys::libseat>);
100
101impl SeatRef {
102    /// Disables a seat, used in response to a disable_seat event. After disabling
103    /// the seat, the seat devices must not be used until enable_seat is received,
104    /// and all requests on the seat will fail during this period.
105    pub fn disable(&mut self) -> Result<(), Errno> {
106        if unsafe { sys::libseat_disable_seat(self.0.as_mut()) } == 0 {
107            Ok(())
108        } else {
109            Err(errno())
110        }
111    }
112
113    /// Opens a device on the seat, returning its device ID and fd
114    ///
115    /// This will only succeed if the seat is active and the device is of a type
116    /// permitted for opening on the backend, such as drm and evdev.
117    ///
118    /// The device may be revoked in some situations, such as in situations where a
119    /// seat session switch is being forced.
120    pub fn open_device<P: AsRef<Path>>(&mut self, path: &P) -> Result<Device, Errno> {
121        let path = path.as_ref();
122        let string = path.as_os_str().to_str().unwrap();
123        let cstring = CString::new(string).unwrap();
124
125        let mut fd = MaybeUninit::uninit();
126        let id =
127            unsafe { sys::libseat_open_device(self.0.as_mut(), cstring.as_ptr(), fd.as_mut_ptr()) };
128
129        if id != -1 {
130            Ok(Device {
131                id,
132                fd: unsafe { fd.assume_init() },
133            })
134        } else {
135            Err(errno())
136        }
137    }
138
139    /// Closes a device that has been opened on the seat using the device_id from
140    /// libseat_open_device.
141    pub fn close_device(&mut self, device: Device) -> Result<(), Errno> {
142        if unsafe { sys::libseat_close_device(self.0.as_mut(), device.id) } == 0 {
143            Ok(())
144        } else {
145            Err(errno())
146        }
147    }
148
149    /// Retrieves the name of the seat that is currently made available through the
150    /// provided libseat instance.
151    pub fn name(&mut self) -> &str {
152        unsafe {
153            let cstr = sys::libseat_seat_name(self.0.as_mut());
154            let cstr = std::ffi::CStr::from_ptr(cstr as *const _);
155            cstr.to_str().unwrap()
156        }
157    }
158
159    /// Requests that the seat switches session to the specified session number.
160    /// For seats that are VT-bound, the session number matches the VT number, and
161    /// switching session results in a VT switch.
162    ///
163    /// A call to libseat_switch_session does not imply that a switch will occur,
164    /// and the caller should assume that the session continues unaffected.
165    pub fn switch_session(&mut self, session: i32) -> Result<(), Errno> {
166        if unsafe { sys::libseat_switch_session(self.0.as_mut(), session) } == 0 {
167            Ok(())
168        } else {
169            Err(errno())
170        }
171    }
172
173    /// Retrieve the pollable connection fd for a given libseat instance. Used to
174    /// poll the libseat connection for events that need to be dispatched.
175    ///
176    /// Returns a pollable fd on success.
177    pub fn get_fd(&mut self) -> Result<BorrowedFd<'_>, Errno> {
178        let fd = unsafe { sys::libseat_get_fd(self.0.as_mut()) };
179        if fd == -1 {
180            Err(errno())
181        } else {
182            Ok(unsafe { BorrowedFd::borrow_raw(fd) })
183        }
184    }
185
186    /// Reads and dispatches events on the libseat connection fd.
187    ///
188    /// The specified timeout dictates how long libseat might wait for data if none
189    /// is available: 0 means that no wait will occur, -1 means that libseat might
190    /// wait indefinitely for data to arrive, while > 0 is the maximum wait in
191    /// milliseconds that might occur.
192    ///
193    /// Returns a positive number signifying processed internal messages on success.
194    /// Returns 0 if no messages were processed. Returns -1 and sets errno on error.
195    pub fn dispatch(&mut self, timeout: i32) -> Result<i32, Errno> {
196        let v = unsafe { sys::libseat_dispatch(self.0.as_mut(), timeout) };
197        if v == -1 {
198            Err(errno())
199        } else {
200            Ok(v)
201        }
202    }
203}
204
205/// Wrapper around the device id and fd of a libseat device. Opened with
206/// [SeatRef::open_device].
207///
208/// Use `AsFd` to access the file descriptor. The device should be closed
209/// with [SeatRef::close_device].
210#[derive(Debug)]
211#[must_use]
212pub struct Device {
213    id: i32,
214    fd: RawFd,
215}
216
217impl AsFd for Device {
218    fn as_fd(&self) -> BorrowedFd<'_> {
219        unsafe { BorrowedFd::borrow_raw(self.fd) }
220    }
221}