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
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
use libseat_sys as sys;

use errno::{errno, Errno};

use std::{
    ffi::CString,
    mem::MaybeUninit,
    ops::{Deref, DerefMut},
    os::unix::io::RawFd,
    path::Path,
    ptr::NonNull,
};

mod ffi_seat_listener;
use ffi_seat_listener::FFI_SEAT_LISTENER;

mod log;
pub use self::log::*;

#[cfg(feature = "custom_logger")]
mod log_handler;
#[cfg(feature = "custom_logger")]
use log_handler::*;

#[derive(Debug, Clone, Copy)]
pub enum SeatEvent {
    Enable,
    Disable,
}

struct SeatListener {
    callback: Box<dyn FnMut(&mut SeatRef, SeatEvent)>,
}

impl std::fmt::Debug for SeatListener {
    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
        f.debug_struct("UserSeatListener").finish()
    }
}

#[derive(Debug)]
pub struct Seat {
    inner: SeatRef,
    _seat_listener: Box<SeatListener>,
    #[cfg(feature = "custom_logger")]
    _logger: Option<LogHandler>,
}

impl Seat {
    /// Opens a seat, taking control of it if possible and returning a pointer to
    /// the libseat instance. If LIBSEAT_BACKEND is set, the specified backend is
    /// used. Otherwise, the first successful backend will be used.
    pub fn open<C, L>(callback: C, _logger: L) -> Result<Self, Errno>
    where
        C: FnMut(&mut SeatRef, SeatEvent) + 'static,
        L: Into<Option<slog::Logger>>,
    {
        #[cfg(feature = "custom_logger")]
        let _logger = _logger.into().map(LogHandler::new);

        let user_listener = SeatListener {
            callback: Box::new(callback),
        };

        let mut user_data = Box::new(user_listener);

        let seat = unsafe {
            sys::libseat_open_seat(&mut FFI_SEAT_LISTENER, user_data.as_mut() as *mut _ as _)
        };

        NonNull::new(seat)
            .map(|nn| Self {
                inner: SeatRef(nn),
                _seat_listener: user_data,
                #[cfg(feature = "custom_logger")]
                _logger,
            })
            .ok_or_else(errno)
    }
}

impl Drop for Seat {
    fn drop(&mut self) {
        // Closes the seat. This frees the libseat structure.
        unsafe { sys::libseat_close_seat(self.0.as_mut()) };
    }
}

impl Deref for Seat {
    type Target = SeatRef;

    fn deref(&self) -> &Self::Target {
        &self.inner
    }
}

impl DerefMut for Seat {
    fn deref_mut(&mut self) -> &mut Self::Target {
        &mut self.inner
    }
}

#[derive(Debug)]
pub struct SeatRef(NonNull<sys::libseat>);

impl SeatRef {
    /// Disables a seat, used in response to a disable_seat event. After disabling
    /// the seat, the seat devices must not be used until enable_seat is received,
    /// and all requests on the seat will fail during this period.
    pub fn disable(&mut self) -> Result<(), Errno> {
        if unsafe { sys::libseat_disable_seat(self.0.as_mut()) } == 0 {
            Ok(())
        } else {
            Err(errno())
        }
    }

    /// Opens a device on the seat, returning its device ID and fd
    ///
    /// This will only succeed if the seat is active and the device is of a type
    /// permitted for opening on the backend, such as drm and evdev.
    ///
    /// The device may be revoked in some situations, such as in situations where a
    /// seat session switch is being forced.
    pub fn open_device<P: AsRef<Path>>(&mut self, path: &P) -> Result<(i32, RawFd), Errno> {
        let path = path.as_ref();
        let string = path.as_os_str().to_str().unwrap();
        let cstring = CString::new(string).unwrap();

        let mut fd = MaybeUninit::uninit();
        let dev_id =
            unsafe { sys::libseat_open_device(self.0.as_mut(), cstring.as_ptr(), fd.as_mut_ptr()) };

        if dev_id != -1 {
            let fd = unsafe { fd.assume_init() };
            Ok((dev_id, fd))
        } else {
            Err(errno())
        }
    }

    /// Closes a device that has been opened on the seat using the device_id from
    /// libseat_open_device.
    pub fn close_device(&mut self, device_id: i32) -> Result<(), Errno> {
        if unsafe { sys::libseat_close_device(self.0.as_mut(), device_id) } == 0 {
            Ok(())
        } else {
            Err(errno())
        }
    }

    /// Retrieves the name of the seat that is currently made available through the
    /// provided libseat instance.
    pub fn name(&mut self) -> &str {
        unsafe {
            let cstr = sys::libseat_seat_name(self.0.as_mut());
            let cstr = std::ffi::CStr::from_ptr(cstr as *const _);
            cstr.to_str().unwrap()
        }
    }

    /// Requests that the seat switches session to the specified session number.
    /// For seats that are VT-bound, the session number matches the VT number, and
    /// switching session results in a VT switch.
    ///
    /// A call to libseat_switch_session does not imply that a switch will occur,
    /// and the caller should assume that the session continues unaffected.
    pub fn switch_session(&mut self, session: i32) -> Result<(), Errno> {
        if unsafe { sys::libseat_switch_session(self.0.as_mut(), session) } == 0 {
            Ok(())
        } else {
            Err(errno())
        }
    }

    /// Retrieve the pollable connection fd for a given libseat instance. Used to
    /// poll the libseat connection for events that need to be dispatched.
    ///
    /// Returns a pollable fd on success.
    pub fn get_fd(&mut self) -> Result<RawFd, Errno> {
        let fd = unsafe { sys::libseat_get_fd(self.0.as_mut()) };
        if fd == -1 {
            Err(errno())
        } else {
            Ok(fd)
        }
    }

    /// Reads and dispatches events on the libseat connection fd.
    ///
    /// The specified timeout dictates how long libseat might wait for data if none
    /// is available: 0 means that no wait will occur, -1 means that libseat might
    /// wait indefinitely for data to arrive, while > 0 is the maximum wait in
    /// milliseconds that might occur.
    ///
    /// Returns a positive number signifying processed internal messages on success.
    /// Returns 0 if no messages were processed. Returns -1 and sets errno on error.
    pub fn dispatch(&mut self, timeout: i32) -> Result<i32, Errno> {
        let v = unsafe { sys::libseat_dispatch(self.0.as_mut(), timeout) };
        if v == -1 {
            Err(errno())
        } else {
            Ok(v)
        }
    }
}