Something went wrong. Try again.
This repository has no description
Something went wrong. Try again.
14 kB · 513 lines
Rust
at master
123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514mod seat_v1 { hyprwire::include_protocol!("seat_v1"); pub use client::*;}
use seat_v1::{device, seat, seat_manager};use std::collections::{HashMap, VecDeque};use std::os::fd::{AsRawFd, IntoRawFd, OwnedFd};use std::{ffi, ptr};
const SESSIOND_SEAT_SOCKET: &str = "/run/sessiond-seat.sock";
#[repr(C)]pub struct LibseatSeatListener { pub enable_seat: Option<unsafe extern "C" fn(*mut Libseat, *mut libc::c_void)>, pub disable_seat: Option<unsafe extern "C" fn(*mut Libseat, *mut libc::c_void)>,}
#[repr(C)]pub struct Libseat { listener: LibseatSeatListener, userdata: *mut libc::c_void, seat_name: ffi::CString, client: hyprwire::client::Client, queue: hyprwire::EventQueue, seat: seat::Seat, initialized: bool, enabled: bool, pending_seat_events: VecDeque<SeatEvent>, devices: HashMap<libc::c_int, DeviceHandle>, pending_open: Option<PendingOpen>,}
type LibseatLogFunc = Option<unsafe extern "C" fn(libc::c_int, *const libc::c_char, *mut libc::c_void)>;
struct DeviceHandle { _object: device::Device,}
enum SeatEvent { Enabled, Disabled,}
enum PendingOpen { Waiting { _object: device::Device, }, Opened { object: device::Device, device_id: libc::c_int, fd: OwnedFd, }, Failed { errno: libc::c_int, },}
fn set_errno(errno: libc::c_int) { // SAFETY: libc exposes the thread-local errno location for the current // platform. Writing an integer errno value matches the C ABI contract. unsafe { *libc::__errno_location() = errno; }}
fn errno_from_u32(errno: u32) -> libc::c_int { match libc::c_int::try_from(errno) { Ok(errno) if errno > 0 => errno, _ => libc::EIO, }}
fn errno_from_io(error: &hyprwire::Error) -> libc::c_int { match error { hyprwire::Error::Io(error) => error.raw_os_error().unwrap_or(libc::EIO), hyprwire::Error::ProtocolNotFound => libc::ENOSYS, hyprwire::Error::VersionOutOfRange { .. } | hyprwire::Error::VersionNegotiationFailed | hyprwire::Error::ProtocolViolation(_) | hyprwire::Error::HandshakeTimeout | hyprwire::Error::ConnectionClosed => libc::EIO, }}
fn dispatch_once(seat: &mut Libseat, block: bool) -> libc::c_int { let queue = seat.queue.clone(); match queue.dispatch_events(seat, block) { Ok(()) => { dispatch_pending_seat_events(seat); 1 } Err(hyprwire::Error::ConnectionClosed) if !block => 0, Err(err) => { set_errno(errno_from_io(&err)); -1 } }}
fn dispatch_pending_seat_events(seat: &mut Libseat) { while let Some(event) = seat.pending_seat_events.pop_front() { let callback = match event { SeatEvent::Enabled => seat.listener.enable_seat, SeatEvent::Disabled => seat.listener.disable_seat, }; if let Some(callback) = callback { let seat_ptr = ptr::from_mut::<Libseat>(seat); unsafe { callback(seat_ptr, seat.userdata); } } }}
fn dispatch_until<F>(seat: &mut Libseat, mut done: F) -> libc::c_intwhere F: FnMut(&Libseat) -> bool,{ while !done(seat) { if dispatch_once(seat, true) < 0 { return -1; } } 0}
fn poll_loop_fd(seat: &Libseat, timeout: libc::c_int) -> libc::c_int { let mut pollfd = libc::pollfd { fd: seat.client.extract_loop_fd().as_raw_fd(), events: libc::POLLIN, revents: 0, };
// SAFETY: pollfd points to one valid pollfd entry for the duration of the // call. The fd is borrowed from the live Hyprwire client. let ready = unsafe { libc::poll(&raw mut pollfd, 1, timeout) }; if ready < 0 { return -1; } if ready == 0 { return 0; } if pollfd.revents & (libc::POLLERR | libc::POLLHUP | libc::POLLNVAL) != 0 { set_errno(libc::EIO); return -1; } 1}
impl hyprwire::Dispatch<seat_manager::SeatManager> for Libseat { fn event( &mut self, _object: &seat_manager::SeatManager, event: <seat_manager::SeatManager as hyprwire::Object>::Event<'_>, ) { match event {} }}
impl hyprwire::Dispatch<seat::Seat> for Libseat { fn event(&mut self, _object: &seat::Seat, event: <seat::Seat as hyprwire::Object>::Event<'_>) { match event { seat::Event::Enabled => { self.initialized = true; self.enabled = true; self.pending_seat_events.push_back(SeatEvent::Enabled); } seat::Event::Disabled => { self.initialized = true; self.enabled = false; self.pending_seat_events.push_back(SeatEvent::Disabled); } _ => {} } }}
impl hyprwire::Dispatch<device::Device> for Libseat { fn event( &mut self, object: &device::Device, event: <device::Device as hyprwire::Object>::Event<'_>, ) { match event { device::Event::Opened { device_id, fd, .. } => { let Ok(device_id) = libc::c_int::try_from(device_id) else { self.pending_open = Some(PendingOpen::Failed { errno: libc::EOVERFLOW, }); return; }; self.pending_open = Some(PendingOpen::Opened { object: object.clone(), device_id, fd, }); } device::Event::Failed { errno, .. } => { self.pending_open = Some(PendingOpen::Failed { errno: errno_from_u32(errno), }); } } }}
unsafe fn seat_from_ptr<'a>(seat: *mut Libseat) -> Option<&'a mut Libseat> { if seat.is_null() { set_errno(libc::EINVAL); None } else { // SAFETY: callers pass back the opaque pointer returned by // libseat_open_seat. Null is handled above. Some(unsafe { &mut *seat }) }}
#[unsafe(no_mangle)]/// # Safety////// `listener` must either be null or point to a valid/// `LibseatSeatListener` for the duration of this call.pub unsafe extern "C" fn libseat_open_seat( listener: *const LibseatSeatListener, userdata: *mut libc::c_void,) -> *mut Libseat { if listener.is_null() { set_errno(libc::EINVAL); return ptr::null_mut(); }
// SAFETY: listener was checked for null and is only copied by value. let listener = unsafe { ptr::read(listener) }; if listener.enable_seat.is_none() || listener.disable_seat.is_none() { set_errno(libc::EINVAL); return ptr::null_mut(); }
let mut client = match hyprwire::client::Client::connect(SESSIOND_SEAT_SOCKET) { Ok(client) => client, Err(err) => { set_errno(errno_from_io(&err)); return ptr::null_mut(); } }; client.add_implementation::<seat_v1::SeatV1>(); let queue = client.new_event_queue();
// SAFETY: static string has no nul byte let seat_name = unsafe { ffi::CString::new("seat0").unwrap_unchecked() }; let mut state = OpenState; if let Err(err) = queue.wait_for_handshake(&mut state) { set_errno(errno_from_io(&err)); return ptr::null_mut(); } let manager = match client.bind::<seat_manager::SeatManager, OpenState>(&queue, &mut state, 1) { Ok(manager) => manager, Err(err) => { set_errno(errno_from_io(&err)); return ptr::null_mut(); } }; let Some(seat_object) = manager.send_open_seat::<Libseat>() else { set_errno(libc::EIO); return ptr::null_mut(); };
let mut seat = Box::new(Libseat { listener, userdata, seat_name, client, queue, seat: seat_object, initialized: false, enabled: false, pending_seat_events: VecDeque::new(), devices: HashMap::new(), pending_open: None, });
if dispatch_until(&mut seat, |seat| seat.initialized) < 0 { return ptr::null_mut(); }
Box::into_raw(seat)}
struct OpenState;
impl hyprwire::Dispatch<seat_manager::SeatManager> for OpenState { fn event( &mut self, _object: &seat_manager::SeatManager, event: <seat_manager::SeatManager as hyprwire::Object>::Event<'_>, ) { match event {} }}
#[unsafe(no_mangle)]/// # Safety////// `seat` must be null or a pointer returned by `libseat_open_seat` that has/// not been closed.pub unsafe extern "C" fn libseat_disable_seat(seat: *mut Libseat) -> libc::c_int { let Some(seat) = (unsafe { seat_from_ptr(seat) }) else { return -1; }; seat.seat.send_ack_disabled(); seat.enabled = false; 0}
#[unsafe(no_mangle)]/// # Safety////// `seat` must be null or a pointer returned by `libseat_open_seat`. A/// non-null pointer is consumed and must not be used again.pub unsafe extern "C" fn libseat_close_seat(seat: *mut Libseat) -> libc::c_int { if seat.is_null() { set_errno(libc::EINVAL); return -1; }
// SAFETY: the pointer must have been returned by libseat_open_seat and is // consumed exactly once by libseat_close_seat. drop(unsafe { Box::from_raw(seat) }); 0}
#[unsafe(no_mangle)]/// # Safety////// `seat` must be null or a live libseat pointer. `path` must be a valid C/// string and `fd` must be writable when non-null.pub unsafe extern "C" fn libseat_open_device( seat: *mut Libseat, path: *const libc::c_char, fd: *mut libc::c_int,) -> libc::c_int { let Some(seat) = (unsafe { seat_from_ptr(seat) }) else { return -1; }; if path.is_null() || fd.is_null() { set_errno(libc::EINVAL); return -1; } if !seat.enabled { set_errno(libc::EACCES); return -1; }
// SAFETY: path is checked for null above and must be a valid C string per // the function contract. let Ok(path) = unsafe { ffi::CStr::from_ptr(path) }.to_str() else { set_errno(libc::EINVAL); return -1; };
let Some(object) = seat.seat.send_open_device::<Libseat>(path) else { set_errno(libc::EIO); return -1; }; seat.pending_open = Some(PendingOpen::Waiting { _object: object });
if dispatch_until(seat, |seat| { !matches!(seat.pending_open, Some(PendingOpen::Waiting { .. })) }) < 0 { seat.pending_open = None; return -1; }
match seat.pending_open.take() { Some(PendingOpen::Opened { object, device_id, fd: device_fd, }) => { // SAFETY: fd was checked for null above. unsafe { *fd = device_fd.into_raw_fd(); } seat.devices .insert(device_id, DeviceHandle { _object: object }); device_id } Some(PendingOpen::Failed { errno }) => { set_errno(errno); -1 } Some(PendingOpen::Waiting { .. }) | None => { set_errno(libc::EIO); -1 } }}
#[unsafe(no_mangle)]/// # Safety////// `seat` must be null or a pointer returned by `libseat_open_seat` that has/// not been closed.pub unsafe extern "C" fn libseat_close_device( seat: *mut Libseat, device_id: libc::c_int,) -> libc::c_int { let Some(seat) = (unsafe { seat_from_ptr(seat) }) else { return -1; }; if device_id < 0 { set_errno(libc::EINVAL); return -1; }
if seat.devices.remove(&device_id).is_some() { 0 } else { set_errno(libc::ENOENT); -1 }}
#[unsafe(no_mangle)]/// # Safety////// `seat` must be null or a pointer returned by `libseat_open_seat` that has/// not been closed.pub unsafe extern "C" fn libseat_seat_name(seat: *mut Libseat) -> *const libc::c_char { match unsafe { seat_from_ptr(seat) } { Some(seat) => seat.seat_name.as_ptr(), None => ptr::null(), }}
#[unsafe(no_mangle)]/// # Safety////// `seat` must be null or a pointer returned by `libseat_open_seat` that has/// not been closed.pub unsafe extern "C" fn libseat_switch_session( seat: *mut Libseat, session: libc::c_int,) -> libc::c_int { let Some(seat) = (unsafe { seat_from_ptr(seat) }) else { return -1; }; if session <= 0 { set_errno(libc::EINVAL); return -1; }
let Ok(session) = u32::try_from(session) else { set_errno(libc::EINVAL); return -1; }; seat.seat.send_switch_session(session); 0}
#[unsafe(no_mangle)]/// # Safety////// `seat` must be null or a pointer returned by `libseat_open_seat` that has/// not been closed.pub unsafe extern "C" fn libseat_get_fd(seat: *mut Libseat) -> libc::c_int { let Some(seat) = (unsafe { seat_from_ptr(seat) }) else { return -1; }; seat.client.extract_loop_fd().as_raw_fd()}
#[unsafe(no_mangle)]/// # Safety////// `seat` must be null or a pointer returned by `libseat_open_seat` that has/// not been closed.pub unsafe extern "C" fn libseat_dispatch(seat: *mut Libseat, timeout: libc::c_int) -> libc::c_int { let Some(seat) = (unsafe { seat_from_ptr(seat) }) else { return -1; }; if timeout < -1 { set_errno(libc::EINVAL); return -1; }
if timeout == 0 { let ready = poll_loop_fd(seat, 0); if ready <= 0 { return ready; } return dispatch_once(seat, false); }
if timeout > 0 { let ready = poll_loop_fd(seat, timeout); if ready <= 0 { return ready; } return dispatch_once(seat, false); }
dispatch_once(seat, true)}
#[unsafe(no_mangle)]pub extern "C" fn libseat_set_log_handler(_handler: LibseatLogFunc) {}
#[unsafe(no_mangle)]pub const extern "C" fn libseat_set_log_level(_level: libc::c_int) {}