From 4df6b559b7de009b8fd83143cee0a53a560a8381 Mon Sep 17 00:00:00 2001 From: Julian Orth Date: Wed, 9 Mar 2022 14:01:21 +0100 Subject: [PATCH] autocommit 2022-03-09 14:01:21 CET --- build/enums.rs | 64 ++++++++++++ src/acceptor.rs | 14 +-- src/async_engine.rs | 7 +- src/backends/metal.rs | 150 ++++++++++++++------------- src/backends/metal/input.rs | 55 ++++++++++ src/backends/metal/monitor.rs | 131 ++++++++++++++++++++++++ src/client/mod.rs | 2 +- src/clientmem.rs | 4 +- src/dbus.rs | 8 +- src/dbus/outgoing.rs | 6 +- src/drm/drm.rs | 4 +- src/event_loop.rs | 10 +- src/forker.rs | 18 ++-- src/ifs/wl_seat/wl_keyboard.rs | 4 +- src/libinput.rs | 157 ++++++++++++++++++++++------ src/libinput/consts.rs | 152 +++++++++++++++++++++++++++ src/libinput/device.rs | 51 ++++++++++ src/libinput/event.rs | 66 ++++++++++++ src/libinput/sys.rs | 64 ++++++++++++ src/logind.rs | 21 +++- src/macros.rs | 1 + src/main.rs | 6 +- src/servermem.rs | 8 +- src/sighand.rs | 6 +- src/time.rs | 2 +- src/udev.rs | 34 ++++--- src/utils/buffd/mod.rs | 2 +- src/utils/mod.rs | 4 +- src/utils/oserror.rs | 181 +++++++++++++++++++++++++++++++++ src/utils/vasprintf.rs | 38 +++++++ src/wheel.rs | 4 +- src/xwayland.rs | 19 ++-- 32 files changed, 1121 insertions(+), 172 deletions(-) create mode 100644 src/backends/metal/input.rs create mode 100644 src/backends/metal/monitor.rs create mode 100644 src/libinput/consts.rs create mode 100644 src/libinput/device.rs create mode 100644 src/libinput/event.rs create mode 100644 src/libinput/sys.rs create mode 100644 src/utils/oserror.rs create mode 100644 src/utils/vasprintf.rs diff --git a/build/enums.rs b/build/enums.rs index abe0b575..fcbbb5b3 100644 --- a/build/enums.rs +++ b/build/enums.rs @@ -15,6 +15,9 @@ mod pixman; #[path = "../src/xkbcommon/consts.rs"] mod xkbcommon; +#[path = "../src/libinput/consts.rs"] +mod libinput; + fn get_target() -> repc::Target { let rustc_target = env::var("TARGET").unwrap(); repc::TARGET_MAP @@ -193,6 +196,67 @@ fn write_egl_procs(f: &mut W) -> anyhow::Result<()> { } pub fn main() -> anyhow::Result<()> { + let mut f = open("libinput_tys.rs")?; + write_ty( + &mut f, + libinput::LIBINPUT_LOG_PRIORITY, + "libinput_log_priority", + )?; + write_ty( + &mut f, + libinput::LIBINPUT_DEVICE_CAPABILITY, + "libinput_device_capability", + )?; + write_ty(&mut f, libinput::LIBINPUT_KEY_STATE, "libinput_key_state")?; + write_ty(&mut f, libinput::LIBINPUT_LED, "libinput_led")?; + write_ty( + &mut f, + libinput::LIBINPUT_BUTTON_STATE, + "libinput_button_state", + )?; + write_ty( + &mut f, + libinput::LIBINPUT_POINTER_AXIS, + "libinput_pointer_axis", + )?; + write_ty( + &mut f, + libinput::LIBINPUT_POINTER_AXIS_SOURCE, + "libinput_pointer_axis_source", + )?; + write_ty( + &mut f, + libinput::LIBINPUT_TABLET_PAD_RING_AXIS_SOURCE, + "libinput_tablet_pad_ring_axis_source", + )?; + write_ty( + &mut f, + libinput::LIBINPUT_TABLET_PAD_STRIP_AXIS_SOURCE, + "libinput_tablet_pad_strip_axis_source", + )?; + write_ty( + &mut f, + libinput::LIBINPUT_TABLET_TOOL_TYPE, + "libinput_tablet_tool_type", + )?; + write_ty( + &mut f, + libinput::LIBINPUT_TABLET_TOOL_PROXIMITY_STATE, + "libinput_tablet_tool_proximity_state", + )?; + write_ty( + &mut f, + libinput::LIBINPUT_TABLET_TOOL_TIP_STATE, + "libinput_tablet_tool_tip_state", + )?; + write_ty( + &mut f, + libinput::LIBINPUT_SWITCH_STATE, + "libinput_switch_state", + )?; + write_ty(&mut f, libinput::LIBINPUT_SWITCH, "libinput_switch")?; + write_ty(&mut f, libinput::LIBINPUT_EVENT_TYPE, "libinput_event_type")?; + let mut f = open("pixman_tys.rs")?; write_ty(&mut f, pixman::FORMATS, "PixmanFormat")?; write_ty(&mut f, pixman::OPS, "PixmanOp")?; diff --git a/src/acceptor.rs b/src/acceptor.rs index a5908636..89f04a9a 100644 --- a/src/acceptor.rs +++ b/src/acceptor.rs @@ -13,23 +13,23 @@ pub enum AcceptorError { #[error("XDG_RUNTIME_DIR ({0:?}) is too long to form a unix socket address")] XrdTooLong(String), #[error("Could not create a wayland socket")] - SocketFailed(#[source] std::io::Error), + SocketFailed(#[source] crate::utils::oserror::OsError), #[error("Could not stat the existing socket")] - SocketStat(#[source] std::io::Error), + SocketStat(#[source] crate::utils::oserror::OsError), #[error("Could not start listening for incoming connections")] - ListenFailed(#[source] std::io::Error), + ListenFailed(#[source] crate::utils::oserror::OsError), #[error("Could not open the lock file")] - OpenLockFile(#[source] std::io::Error), + OpenLockFile(#[source] crate::utils::oserror::OsError), #[error("Could not lock the lock file")] - LockLockFile(#[source] std::io::Error), + LockLockFile(#[source] crate::utils::oserror::OsError), #[error("The wayland socket is in an error state")] ErrorEvent, #[error("Could not accept new connections")] - AcceptFailed(#[source] std::io::Error), + AcceptFailed(#[source] crate::utils::oserror::OsError), #[error("Could not spawn an event handler for a new connection")] SpawnFailed(#[source] ClientError), #[error("Could not bind the socket to an address")] - BindFailed(#[source] std::io::Error), + BindFailed(#[source] crate::utils::oserror::OsError), #[error("All wayland addresses in the range 0..1000 are already in use")] AddressesInUse, #[error("The event loop caused an error")] diff --git a/src/async_engine.rs b/src/async_engine.rs index e31fd24e..5a8bc4d5 100644 --- a/src/async_engine.rs +++ b/src/async_engine.rs @@ -3,8 +3,8 @@ use crate::event_loop::{EventLoop, EventLoopError}; use crate::utils::copyhashmap::CopyHashMap; use crate::utils::numcell::NumCell; use crate::wheel::{Wheel, WheelError}; -pub use fd::{AsyncFd, FdStatus}; use fd::AsyncFdData; +pub use fd::{AsyncFd, FdStatus}; use queue::{DispatchQueue, Dispatcher}; use std::cell::{Cell, RefCell}; use std::future::Future; @@ -562,7 +562,10 @@ mod fd { let res = self.el.modify(self.id, events); if res.is_err() { if let Err(e) = self.el.remove(self.id) { - log::error!("Fatal error: Cannot remove file descriptor from event loop: {:?}", e); + log::error!( + "Fatal error: Cannot remove file descriptor from event loop: {:?}", + e + ); self.el.stop(); } } diff --git a/src/backends/metal.rs b/src/backends/metal.rs index e2721ad9..8db4b3af 100644 --- a/src/backends/metal.rs +++ b/src/backends/metal.rs @@ -1,12 +1,19 @@ +mod input; +mod monitor; + +use crate::async_engine::AsyncFd; use crate::dbus::DbusError; +use crate::libinput::device::RegisteredDevice; +use crate::libinput::{LibInput, LibInputAdapter, LibInputError}; use crate::logind::{LogindError, Session}; -use crate::{AsyncQueue, ErrorFmt, State, Udev}; +use crate::udev::{UdevError, UdevMonitor}; +use crate::utils::copyhashmap::CopyHashMap; +use crate::{AsyncQueue, CloneCell, ErrorFmt, NumCell, State, Udev}; +use std::cell::{Cell, RefCell}; +use std::ffi::{CStr, CString}; use std::rc::Rc; use thiserror::Error; -use uapi::OwnedFd; -use crate::async_engine::{AsyncFd, FdStatus}; -use crate::libinput::{LibInput, LibInputError}; -use crate::udev::{UdevError, UdevMonitor}; +use uapi::{c, OwnedFd}; #[derive(Debug, Error)] pub enum MetalError { @@ -16,12 +23,14 @@ pub enum MetalError { LogindSession(#[source] LogindError), #[error("Could not take control of the logind session")] TakeControl(#[source] LogindError), + #[error("Could not enumerate devices")] + Enumerate(#[source] Box), #[error(transparent)] Udev(#[from] UdevError), #[error(transparent)] LibInput(#[from] LibInputError), #[error("Dupfd failed")] - Dup(#[source] std::io::Error), + Dup(#[source] crate::utils::oserror::OsError), } pub async fn run(state: Rc) { @@ -30,6 +39,18 @@ pub async fn run(state: Rc) { } } +struct MetalBackend { + state: Rc, + udev: Rc, + monitor: Rc, + monitor_fd: AsyncFd, + libinput: Rc, + libinput_fd: AsyncFd, + device_holder: Rc, + session: Session, + ids: NumCell, +} + async fn run_(state: Rc) -> Result<(), MetalError> { let socket = match state.dbus.system() { Ok(s) => s, @@ -39,90 +60,83 @@ async fn run_(state: Rc) -> Result<(), MetalError> { Ok(s) => s, Err(e) => return Err(MetalError::LogindSession(e)), }; - // if let Err(e) = session.take_control().await { - // return Err(MetalError::TakeControl(e)); - // } + if let Err(e) = session.take_control().await { + return Err(MetalError::TakeControl(e)); + } + let device_holder = Rc::new(DeviceHolder { + input_devices: Default::default(), + input_devices_: Default::default(), + }); let udev = Rc::new(Udev::new()?); let monitor = Rc::new(udev.create_monitor()?); monitor.add_match_subsystem_devtype(Some("input"), None)?; monitor.enable_receiving()?; - let libinput = Rc::new(LibInput::new()?); + let libinput = Rc::new(LibInput::new(device_holder.clone())?); let monitor_fd = match uapi::fcntl_dupfd_cloexec(monitor.fd(), 0) { Ok(m) => state.eng.fd(&Rc::new(m)).unwrap(), Err(e) => return Err(MetalError::Dup(e.into())), }; + let libinput_fd = match uapi::fcntl_dupfd_cloexec(libinput.fd(), 0) { + Ok(m) => state.eng.fd(&Rc::new(m)).unwrap(), + Err(e) => return Err(MetalError::Dup(e.into())), + }; let metal = Rc::new(MetalBackend { state: state.clone(), udev, monitor, monitor_fd, libinput, + libinput_fd, + device_holder, + session, + ids: Default::default(), }); let _monitor = state.eng.spawn(metal.clone().monitor_devices()); - let mut queue = AsyncQueue::::new(); + let _events = state.eng.spawn(metal.clone().handle_libinput_events()); + if let Err(e) = metal.enumerate_devices() { + return Err(MetalError::Enumerate(Box::new(e))); + } + let queue = AsyncQueue::::new(); queue.pop().await; Ok(()) - // let libinput_fd = match uapi::fcntl_dupfd_cloexec(monitor.fd(), 0) { - // Ok(m) => m, - // Err(e) => Err(MetalError::Dup(e.into())), - // }; - // let mut enumerate = udev.create_enumerate()?; - // enumerate.add_match_subsystem("input")?; - // enumerate.scan_devices()?; - // let mut entry_opt = enumerate.get_list_entry()?; - // while let Some(entry) = entry_opt { - // let device = udev.create_device_from_syspath(entry.name()?)?; - // if device.sysname()?.to_bytes().starts_with(b"event") { - // let devnode = device.devnode()?; - // } - // } } -struct MetalBackend { - state: Rc, - udev: Rc, - monitor: Rc, - monitor_fd: AsyncFd, - libinput: Rc, - libinput_fd: AsyncFd, +struct MetalDevice { + slot: usize, + device_id: u64, + devnum: c::dev_t, + fd: CloneCell>>, + inputdev: Cell>, + devnode: CString, + sysname: CString, +} + +struct DeviceHolder { + input_devices: CopyHashMap>, + input_devices_: RefCell>>>, +} + +impl LibInputAdapter for DeviceHolder { + fn open(&self, path: &CStr) -> Result { + let stat = match uapi::stat(path) { + Ok(s) => s, + Err(e) => return Err(LibInputError::Stat(e.into())), + }; + match self.input_devices.get(&stat.st_rdev) { + Some(d) => match d.fd.get() { + Some(fd) => match uapi::fcntl_dupfd_cloexec(fd.raw(), 0) { + Ok(fd) => Ok(fd), + Err(e) => Err(LibInputError::DupFd(e.into())), + }, + _ => Err(LibInputError::DeviceUnavailable), + }, + _ => Err(LibInputError::DeviceUnavailable), + } + } } impl MetalBackend { - async fn monitor_devices(self: Rc) { - loop { - match self.monitor_fd.readable().await { - Err(e) => { - log::error!("Cannot wait for udev_monitor to become readable: {}", ErrorFmt(e)); - break; - } - Ok(FdStatus::Err) => { - log::error!("udev_monitor fd is in an error state"); - break; - } - _ => { }, - } - while let Some(dev) = self.monitor.receive_device() { - log::info!("x {:?}", dev.devnode()); - } - } - log::error!("Monitor task exited. Future hotplug events will be ignored."); - } - - async fn handle_libinput_events(self: Rc) { - loop { - match self.libinput_fd.readable().await { - Err(e) => { - log::error!("Cannot wait for udev_monitor to become readable: {}", ErrorFmt(e)); - break; - } - Ok(FdStatus::Err) => { - log::error!("udev_monitor fd is in an error state"); - break; - } - _ => { }, - } - self.libinput.fd() - } - log::error!("Monitor task exited. Future hotplug events will be ignored."); + fn id(&self) -> u64 { + self.ids.fetch_add(1) } } diff --git a/src/backends/metal/input.rs b/src/backends/metal/input.rs new file mode 100644 index 00000000..ed3320e4 --- /dev/null +++ b/src/backends/metal/input.rs @@ -0,0 +1,55 @@ +use crate::async_engine::FdStatus; +use crate::libinput::consts::LIBINPUT_EVENT_KEYBOARD_KEY; +use crate::libinput::event::LibInputEvent; +use crate::metal::MetalBackend; +use crate::ErrorFmt; +use std::rc::Rc; + +impl MetalBackend { + pub async fn handle_libinput_events(self: Rc) { + loop { + match self.libinput_fd.readable().await { + Err(e) => { + log::error!( + "Cannot wait for libinput fd to become readable: {}", + ErrorFmt(e) + ); + break; + } + Ok(FdStatus::Err) => { + log::error!("libinput fd fd is in an error state"); + break; + } + _ => {} + } + if let Err(e) = self.libinput.dispatch() { + log::error!("Could not dispatch libinput events: {}", ErrorFmt(e)); + break; + } + while let Some(event) = self.libinput.event() { + self.handle_event(event); + } + } + log::error!("Libinput task exited. Future input events will be ignored."); + } + + fn handle_event(self: &Rc, event: LibInputEvent) { + match event.ty() { + LIBINPUT_EVENT_KEYBOARD_KEY => self.handle_keyboard_event(event), + _ => {} + } + } + + fn handle_keyboard_event(self: &Rc, event: LibInputEvent) { + let event = match event.keyboard_event() { + Some(event) => event, + _ => return, + }; + log::info!( + "key: {}, state: {:?}, time: {}", + event.key(), + event.key_state(), + event.time_usec() + ); + } +} diff --git a/src/backends/metal/monitor.rs b/src/backends/metal/monitor.rs new file mode 100644 index 00000000..5f7425f0 --- /dev/null +++ b/src/backends/metal/monitor.rs @@ -0,0 +1,131 @@ +use crate::async_engine::FdStatus; +use crate::dbus::TRUE; +use crate::metal::{MetalBackend, MetalDevice, MetalError}; +use crate::udev::UdevDevice; +use crate::ErrorFmt; +use std::rc::Rc; + +impl MetalBackend { + pub async fn monitor_devices(self: Rc) { + loop { + match self.monitor_fd.readable().await { + Err(e) => { + log::error!( + "Cannot wait for udev_monitor to become readable: {}", + ErrorFmt(e) + ); + break; + } + Ok(FdStatus::Err) => { + log::error!("udev_monitor fd is in an error state"); + break; + } + _ => {} + } + while let Some(dev) = self.monitor.receive_device() { + log::info!("x {:?}", dev.devnode()); + } + } + log::error!("Monitor task exited. Future hotplug events will be ignored."); + } + + pub fn enumerate_devices(self: &Rc) -> Result<(), MetalError> { + let mut enumerate = self.udev.create_enumerate()?; + enumerate.add_match_subsystem("input")?; + enumerate.scan_devices()?; + let mut entry_opt = enumerate.get_list_entry()?; + while let Some(entry) = entry_opt.take() { + 'inner: { + let device = match self.udev.create_device_from_syspath(entry.name()) { + Ok(d) => d, + _ => break 'inner, + }; + let sysname = match device.sysname() { + Ok(s) => s, + _ => break 'inner, + }; + if sysname.to_bytes().starts_with(b"event") { + self.add_input_device(&device); + } + } + entry_opt = entry.next(); + } + Ok(()) + } + + fn add_input_device(self: &Rc, dev: &UdevDevice) { + let slf = self.clone(); + let device_id = self.id(); + let devnum = dev.devnum(); + let devnode = match dev.devnode() { + Ok(n) => n, + Err(e) => { + log::error!("Could not retrieve devnode of udev device: {}", ErrorFmt(e)); + return; + } + }; + let sysname = match dev.sysname() { + Ok(n) => n, + Err(e) => { + log::error!("Could not retrieve sysname of udev device: {}", ErrorFmt(e)); + return; + } + }; + let mut slots = self.device_holder.input_devices_.borrow_mut(); + let slot = 'slot: { + for (i, s) in slots.iter().enumerate() { + if s.is_none() { + break 'slot i; + } + } + slots.push(None); + slots.len() - 1 + }; + let dev = Rc::new(MetalDevice { + slot, + device_id, + devnum, + fd: Default::default(), + inputdev: Default::default(), + devnode: devnode.to_owned(), + sysname: sysname.to_owned(), + }); + slots[slot] = Some(dev.clone()); + self.device_holder.input_devices.set(devnum, dev); + self.session.get_device(devnum, move |res| { + let id = &slf.device_holder.input_devices; + let mut slots = slf.device_holder.input_devices_.borrow_mut(); + let dev = 'dev: { + if let Some(dev) = id.get(&devnum) { + if dev.device_id == device_id { + break 'dev dev; + } + } + return; + }; + let res = match res { + Ok(r) => r, + Err(e) => { + log::error!("Could not take control of input device: {}", ErrorFmt(e)); + slots[dev.slot] = None; + id.remove(&devnum); + return; + } + }; + if res.inactive == TRUE { + return; + } + dev.fd.set(Some(res.fd.clone())); + let inputdev = match slf.libinput.open(dev.devnode.as_c_str()) { + Ok(d) => d, + Err(_) => { + slots[dev.slot] = None; + id.remove(&devnum); + return; + } + }; + inputdev.device().set_slot(slot); + dev.inputdev.set(Some(inputdev)); + }); + } +} diff --git a/src/client/mod.rs b/src/client/mod.rs index 3ccb06e2..55301c0b 100644 --- a/src/client/mod.rs +++ b/src/client/mod.rs @@ -87,7 +87,7 @@ impl Clients { Err(e) => { log::error!( "Cannot determine peer credentials of new connection: {:?}", - std::io::Error::from(e) + crate::utils::oserror::OsError::from(e) ); return Ok(()); } diff --git a/src/clientmem.rs b/src/clientmem.rs index ffa8f415..b75aad4c 100644 --- a/src/clientmem.rs +++ b/src/clientmem.rs @@ -10,11 +10,11 @@ use uapi::c::raise; #[derive(Debug, Error)] pub enum ClientMemError { #[error("Could not install the sigbus handler")] - SigactionFailed(#[source] std::io::Error), + SigactionFailed(#[source] crate::utils::oserror::OsError), #[error("A SIGBUS occurred while accessing mapped memory")] Sigbus, #[error("mmap failed")] - MmapFailed(#[source] std::io::Error), + MmapFailed(#[source] crate::utils::oserror::OsError), } pub struct ClientMem { diff --git a/src/dbus.rs b/src/dbus.rs index db69bc01..f65eb522 100644 --- a/src/dbus.rs +++ b/src/dbus.rs @@ -67,13 +67,13 @@ pub enum DbusError { #[error("Variant has an invalid type")] InvalidVariantType, #[error("Could not create a socket")] - Socket(#[source] std::io::Error), + Socket(#[source] crate::utils::oserror::OsError), #[error("Could not connect")] - Connect(#[source] std::io::Error), + Connect(#[source] crate::utils::oserror::OsError), #[error("Could not write to the dbus socket")] - WriteError(#[source] std::io::Error), + WriteError(#[source] crate::utils::oserror::OsError), #[error("Could not read from the dbus socket")] - ReadError(#[source] std::io::Error), + ReadError(#[source] crate::utils::oserror::OsError), #[error("timeout")] AsyncError(#[source] Box), #[error("Server did not accept our authentication")] diff --git a/src/dbus/outgoing.rs b/src/dbus/outgoing.rs index d7fac1f5..17e2dcb3 100644 --- a/src/dbus/outgoing.rs +++ b/src/dbus/outgoing.rs @@ -49,7 +49,11 @@ impl Outgoing { return; } if let Err(e) = self.socket.fd.writable().await { - log::error!("{}: Cannot wait for fd to become readable: {}", self.socket.bus_name, ErrorFmt(e)); + log::error!( + "{}: Cannot wait for fd to become readable: {}", + self.socket.bus_name, + ErrorFmt(e) + ); self.socket.kill(); return; } diff --git a/src/drm/drm.rs b/src/drm/drm.rs index eb8d7c50..c8851fb9 100644 --- a/src/drm/drm.rs +++ b/src/drm/drm.rs @@ -12,13 +12,13 @@ use uapi::{c, Errno, OwnedFd, Ustring}; #[derive(Debug, Error)] pub enum DrmError { #[error("Could not reopen a node")] - ReopenNode(#[source] std::io::Error), + ReopenNode(#[source] crate::utils::oserror::OsError), #[error("Could not retrieve the render node name")] RenderNodeName, #[error("Could not retrieve the device node name")] DeviceNodeName, #[error("Could not retrieve device")] - GetDevice(#[source] std::io::Error), + GetDevice(#[source] crate::utils::oserror::OsError), } #[allow(dead_code)] diff --git a/src/event_loop.rs b/src/event_loop.rs index 1b62055c..bc113ca7 100644 --- a/src/event_loop.rs +++ b/src/event_loop.rs @@ -9,17 +9,17 @@ use uapi::{c, Errno, OwnedFd}; #[derive(Debug, Error)] pub enum EventLoopError { #[error("Could not create an epoll fd: {0}")] - CreateFailed(std::io::Error), + CreateFailed(crate::utils::oserror::OsError), #[error("epoll_wait failed: {0}")] - WaitFailed(std::io::Error), + WaitFailed(crate::utils::oserror::OsError), #[error("A dispatcher returned a fatal error: {0}")] DispatcherError(Box), #[error("Could not insert an fd to wait on: {0}")] - InsertFailed(std::io::Error), + InsertFailed(crate::utils::oserror::OsError), #[error("Could not modify an fd to wait on: {0}")] - ModifyFailed(std::io::Error), + ModifyFailed(crate::utils::oserror::OsError), #[error("Could not remove an fd to wait on: {0}")] - RemoveFailed(std::io::Error), + RemoveFailed(crate::utils::oserror::OsError), #[error("Entry is not registered")] NoEntry, #[error("Event loop is already destroyed")] diff --git a/src/forker.rs b/src/forker.rs index bc4a6e5f..545c4270 100644 --- a/src/forker.rs +++ b/src/forker.rs @@ -43,9 +43,9 @@ struct PidfdHandoff { #[derive(Debug, Error)] pub enum ForkerError { #[error("Could not create a socketpair")] - Socketpair(#[source] std::io::Error), + Socketpair(#[source] crate::utils::oserror::OsError), #[error("Could not fork")] - Fork(#[source] std::io::Error), + Fork(#[source] crate::utils::oserror::OsError), #[error("Could not read the next message")] ReadFailed(#[source] BufFdError), #[error("Could not write the next message")] @@ -235,7 +235,10 @@ impl ForkerProxy { async fn check_process(self: Rc, state: Rc) { let pidfd = state.eng.fd(&self.pidfd).unwrap(); if let Err(e) = pidfd.readable().await { - log::error!("Cannot wait for the forker pidfd to become readable: {}", ErrorFmt(e)); + log::error!( + "Cannot wait for the forker pidfd to become readable: {}", + ErrorFmt(e) + ); } else { let _ = uapi::waitpid(self.pid, 0); } @@ -417,7 +420,10 @@ impl Forker { let spawn = self.ae.spawn(async move { let read = slf.ae.fd(&Rc::new(read)).unwrap(); if let Err(e) = read.readable().await { - log::error!("Cannot wait for the child fd to become readable: {}", ErrorFmt(e)); + log::error!( + "Cannot wait for the child fd to become readable: {}", + ErrorFmt(e) + ); } else { let mut s = String::new(); let _ = Fd::new(read.raw()).read_to_string(&mut s); @@ -476,9 +482,9 @@ impl Forker { #[derive(Debug, Error)] enum SpawnError { #[error("exec failed")] - Exec(#[source] std::io::Error), + Exec(#[source] crate::utils::oserror::OsError), #[error("Could not unset cloexec flag")] - Cloexec(#[source] std::io::Error), + Cloexec(#[source] crate::utils::oserror::OsError), } fn setup_fds(mut socket: OwnedFd) -> OwnedFd { diff --git a/src/ifs/wl_seat/wl_keyboard.rs b/src/ifs/wl_seat/wl_keyboard.rs index d1e97514..55af17bd 100644 --- a/src/ifs/wl_seat/wl_keyboard.rs +++ b/src/ifs/wl_seat/wl_keyboard.rs @@ -125,9 +125,9 @@ pub enum WlKeyboardError { #[error("Could not process a `release` request")] ReleaseError(#[from] ReleaseError), #[error("Could not create a keymap memfd")] - KeymapMemfd(#[source] std::io::Error), + KeymapMemfd(#[source] crate::utils::oserror::OsError), #[error("Could not copy the keymap")] - KeymapCopy(#[source] std::io::Error), + KeymapCopy(#[source] crate::utils::oserror::OsError), } efrom!(WlKeyboardError, ClientError, ClientError); diff --git a/src/libinput.rs b/src/libinput.rs index c5bb54dc..485200a0 100644 --- a/src/libinput.rs +++ b/src/libinput.rs @@ -1,31 +1,31 @@ -use crate::udev::Udev; +#![allow(non_camel_case_types)] + +pub mod consts; +pub mod device; +pub mod event; +mod sys; + +use crate::libinput::consts::{ + LogPriority, LIBINPUT_LOG_PRIORITY_DEBUG, LIBINPUT_LOG_PRIORITY_ERROR, + LIBINPUT_LOG_PRIORITY_INFO, +}; +use crate::libinput::device::RegisteredDevice; +use crate::libinput::event::LibInputEvent; +use crate::libinput::sys::{ + libinput, libinput_device_ref, libinput_dispatch, libinput_get_event, libinput_get_fd, + libinput_interface, libinput_log_priority, libinput_log_set_handler, libinput_log_set_priority, + libinput_path_add_device, libinput_path_create_context, libinput_unref, +}; +use crate::udev::UdevError; +use crate::utils::oserror::OsError; use crate::utils::ptr_ext::PtrExt; -use std::ops::DerefMut; +use crate::utils::vasprintf::vasprintf_; +use crate::ErrorFmt; +use bstr::ByteSlice; +use std::ffi::{CStr, VaList}; use std::rc::Rc; use thiserror::Error; -use uapi::{c, OwnedFd}; - -#[link(name = "input")] -extern "C" { - type libinput; - - fn libinput_path_create_context( - interface: *const libinput_interface, - user_data: *mut c::c_void, - ) -> *mut libinput; - fn libinput_unref(libinput: *mut libinput) -> *mut libinput; - fn libinput_get_fd(libinput: *mut libinput) -> c::c_int; -} - -#[repr(C)] -struct libinput_interface { - open_restricted: unsafe extern "C" fn( - path: *const c::c_char, - flags: c::c_int, - user_data: *mut c::c_void, - ) -> c::c_int, - close_restricted: unsafe extern "C" fn(fd: c::c_int, user_data: *mut c::c_void), -} +use uapi::{c, Errno, IntoUstr, OwnedFd}; static INTERFACE: libinput_interface = libinput_interface { open_restricted, @@ -34,23 +34,47 @@ static INTERFACE: libinput_interface = libinput_interface { unsafe extern "C" fn open_restricted( path: *const c::c_char, - flags: c::c_int, + _flags: c::c_int, user_data: *mut c::c_void, ) -> c::c_int { let ud = (user_data as *const UserData).deref(); - -1 + match ud.adapter.open(CStr::from_ptr(path)) { + Ok(f) => f.unwrap(), + Err(e) => { + log::error!("Could not open device for libinput: {}", ErrorFmt(e)); + -1 + } + } } unsafe extern "C" fn close_restricted(fd: c::c_int, _user_data: *mut c::c_void) { drop(OwnedFd::new(fd)); } -struct UserData {} +struct UserData { + adapter: Rc, +} + +pub trait LibInputAdapter { + fn open(&self, path: &CStr) -> Result; +} #[derive(Debug, Error)] pub enum LibInputError { #[error("Could not create a libinput instance")] New, + #[error("Could not open a libinput device")] + Open, + #[error("Could not dispatch libinput events")] + Dispatch(#[source] OsError), + #[error("The requested device is not available")] + DeviceUnavailable, + #[error("Dupfd failed")] + DupFd(#[source] OsError), + #[error("The udev subsystem produced an error")] + Udev(#[from] UdevError), + #[error("Stat failed")] + Stat(#[source] OsError), } pub struct LibInput { @@ -59,20 +83,71 @@ pub struct LibInput { } impl LibInput { - pub fn new() -> Result { - let mut ud = Box::new(UserData {}); + pub fn new(adapter: Rc) -> Result { + let mut ud = Box::new(UserData { adapter }); let li = unsafe { libinput_path_create_context(&INTERFACE, &mut *ud as *mut _ as *mut c::c_void) }; if li.is_null() { return Err(LibInputError::New); } + unsafe { + libinput_log_set_handler(li, log_handler); + let priority = if log::log_enabled!(log::Level::Debug) { + LIBINPUT_LOG_PRIORITY_DEBUG + } else if log::log_enabled!(log::Level::Info) { + LIBINPUT_LOG_PRIORITY_INFO + } else { + LIBINPUT_LOG_PRIORITY_ERROR + }; + libinput_log_set_priority(li, priority.raw() as _); + } Ok(Self { data: ud, li }) } pub fn fd(&self) -> c::c_int { unsafe { libinput_get_fd(self.li) } } + + pub fn open<'a>( + self: &Rc, + path: impl IntoUstr<'a>, + ) -> Result { + let path = path.into_ustr(); + let res = unsafe { libinput_path_add_device(self.li, path.as_ptr()) }; + if res.is_null() { + Err(LibInputError::Open) + } else { + unsafe { + libinput_device_ref(res); + } + Ok(RegisteredDevice { + li: self.clone(), + dev: res, + }) + } + } + + pub fn dispatch(&self) -> Result<(), LibInputError> { + let res = unsafe { libinput_dispatch(self.li) }; + if res < 0 { + Err(LibInputError::Dispatch(Errno(-res).into())) + } else { + Ok(()) + } + } + + pub fn event(&self) -> Option { + let res = unsafe { libinput_get_event(self.li) }; + if res.is_null() { + None + } else { + Some(LibInputEvent { + event: res, + _phantom: Default::default(), + }) + } + } } impl Drop for LibInput { @@ -82,3 +157,25 @@ impl Drop for LibInput { } } } + +unsafe extern "C" fn log_handler( + _libinput: *mut libinput, + priority: libinput_log_priority, + format: *const c::c_char, + args: VaList, +) { + let str = match vasprintf_(format, args) { + Some(s) => s, + _ => { + log::error!("Could not format log message"); + return; + } + }; + let priority = match LogPriority(priority as _) { + LIBINPUT_LOG_PRIORITY_DEBUG => log::Level::Debug, + LIBINPUT_LOG_PRIORITY_INFO => log::Level::Info, + LIBINPUT_LOG_PRIORITY_ERROR => log::Level::Error, + _ => log::Level::Error, + }; + log::log!(priority, "libinput: {}", str.to_bytes().trim().as_bstr()); +} diff --git a/src/libinput/consts.rs b/src/libinput/consts.rs new file mode 100644 index 00000000..716f35d3 --- /dev/null +++ b/src/libinput/consts.rs @@ -0,0 +1,152 @@ +#![allow(dead_code)] + +cenum! { + LogPriority, LIBINPUT_LOG_PRIORITY; + + LIBINPUT_LOG_PRIORITY_DEBUG = 10, + LIBINPUT_LOG_PRIORITY_INFO = 20, + LIBINPUT_LOG_PRIORITY_ERROR = 30, +} + +cenum! { + DeviceCapability, LIBINPUT_DEVICE_CAPABILITY; + + LIBINPUT_DEVICE_CAP_KEYBOARD = 0, + LIBINPUT_DEVICE_CAP_POINTER = 1, + LIBINPUT_DEVICE_CAP_TOUCH = 2, + LIBINPUT_DEVICE_CAP_TABLET_TOOL = 3, + LIBINPUT_DEVICE_CAP_TABLET_PAD = 4, + LIBINPUT_DEVICE_CAP_GESTURE = 5, + LIBINPUT_DEVICE_CAP_SWITCH = 6, +} + +cenum! { + KeyState, LIBINPUT_KEY_STATE; + + LIBINPUT_KEY_STATE_RELEASED = 0, + LIBINPUT_KEY_STATE_PRESSED = 1, +} + +cenum! { + Led, LIBINPUT_LED; + + LIBINPUT_LED_NUM_LOCK = 1 << 0, + LIBINPUT_LED_CAPS_LOCK = 1 << 1, + LIBINPUT_LED_SCROLL_LOCK = 1 << 2, +} + +cenum! { + ButtonState, LIBINPUT_BUTTON_STATE; + + LIBINPUT_BUTTON_STATE_RELEASED = 0, + LIBINPUT_BUTTON_STATE_PRESSED = 1, +} + +cenum! { + PointerAxis, LIBINPUT_POINTER_AXIS; + + LIBINPUT_POINTER_AXIS_SCROLL_VERTICAL = 0, + LIBINPUT_POINTER_AXIS_SCROLL_HORIZONTAL = 1, +} + +cenum! { + PointerAxisSource, LIBINPUT_POINTER_AXIS_SOURCE; + + LIBINPUT_POINTER_AXIS_SOURCE_WHEEL = 1, + LIBINPUT_POINTER_AXIS_SOURCE_FINGER = 2, + LIBINPUT_POINTER_AXIS_SOURCE_CONTINUOUS = 3, + LIBINPUT_POINTER_AXIS_SOURCE_WHEEL_TILT = 4, +} + +cenum! { + TabletPadRingAxisSource, LIBINPUT_TABLET_PAD_RING_AXIS_SOURCE; + + LIBINPUT_TABLET_PAD_RING_SOURCE_UNKNOWN = 1, + LIBINPUT_TABLET_PAD_RING_SOURCE_FINGER = 2, +} + +cenum! { + TabletPadStripAxisSource, LIBINPUT_TABLET_PAD_STRIP_AXIS_SOURCE; + + LIBINPUT_TABLET_PAD_STRIP_SOURCE_UNKNOWN = 1, + LIBINPUT_TABLET_PAD_STRIP_SOURCE_FINGER = 2, +} + +cenum! { + TabletToolType, LIBINPUT_TABLET_TOOL_TYPE; + + LIBINPUT_TABLET_TOOL_TYPE_PEN = 1, + LIBINPUT_TABLET_TOOL_TYPE_ERASER = 2, + LIBINPUT_TABLET_TOOL_TYPE_BRUSH = 3, + LIBINPUT_TABLET_TOOL_TYPE_PENCIL = 4, + LIBINPUT_TABLET_TOOL_TYPE_AIRBRUSH = 5, + LIBINPUT_TABLET_TOOL_TYPE_MOUSE = 6, + LIBINPUT_TABLET_TOOL_TYPE_LENS = 7, + LIBINPUT_TABLET_TOOL_TYPE_TOTEM = 8, +} + +cenum! { + TabletToolProximityState, LIBINPUT_TABLET_TOOL_PROXIMITY_STATE; + + LIBINPUT_TABLET_TOOL_PROXIMITY_STATE_OUT = 0, + LIBINPUT_TABLET_TOOL_PROXIMITY_STATE_IN = 1, +} + +cenum! { + TabletToolTipState, LIBINPUT_TABLET_TOOL_TIP_STATE; + + LIBINPUT_TABLET_TOOL_TIP_UP = 0, + LIBINPUT_TABLET_TOOL_TIP_DOWN = 1, +} + +cenum! { + SwitchState, LIBINPUT_SWITCH_STATE; + + LIBINPUT_SWITCH_STATE_OFF = 0, + LIBINPUT_SWITCH_STATE_ON = 1, +} + +cenum! { + Switch, LIBINPUT_SWITCH; + + LIBINPUT_SWITCH_LID = 1, + LIBINPUT_SWITCH_TABLET_MODE = 2, +} + +cenum! { + EventType, LIBINPUT_EVENT_TYPE; + + LIBINPUT_EVENT_NONE = 0, + LIBINPUT_EVENT_DEVICE_ADDED = 1, + LIBINPUT_EVENT_DEVICE_REMOVED = 2, + LIBINPUT_EVENT_KEYBOARD_KEY = 300, + LIBINPUT_EVENT_POINTER_MOTION = 400, + LIBINPUT_EVENT_POINTER_MOTION_ABSOLUTE = 401, + LIBINPUT_EVENT_POINTER_BUTTON = 402, + LIBINPUT_EVENT_POINTER_AXIS = 403, + LIBINPUT_EVENT_POINTER_SCROLL_WHEEL = 404, + LIBINPUT_EVENT_POINTER_SCROLL_FINGER = 405, + LIBINPUT_EVENT_POINTER_SCROLL_CONTINUOUS = 406, + LIBINPUT_EVENT_TOUCH_DOWN = 500, + LIBINPUT_EVENT_TOUCH_UP = 501, + LIBINPUT_EVENT_TOUCH_MOTION = 502, + LIBINPUT_EVENT_TOUCH_CANCEL = 503, + LIBINPUT_EVENT_TOUCH_FRAME = 504, + LIBINPUT_EVENT_TABLET_TOOL_AXIS = 600, + LIBINPUT_EVENT_TABLET_TOOL_PROXIMITY = 601, + LIBINPUT_EVENT_TABLET_TOOL_TIP = 602, + LIBINPUT_EVENT_TABLET_TOOL_BUTTON = 603, + LIBINPUT_EVENT_TABLET_PAD_BUTTON = 700, + LIBINPUT_EVENT_TABLET_PAD_RING = 701, + LIBINPUT_EVENT_TABLET_PAD_STRIP = 702, + LIBINPUT_EVENT_TABLET_PAD_KEY = 703, + LIBINPUT_EVENT_GESTURE_SWIPE_BEGIN = 800, + LIBINPUT_EVENT_GESTURE_SWIPE_UPDATE = 801, + LIBINPUT_EVENT_GESTURE_SWIPE_END = 802, + LIBINPUT_EVENT_GESTURE_PINCH_BEGIN = 803, + LIBINPUT_EVENT_GESTURE_PINCH_UPDATE = 804, + LIBINPUT_EVENT_GESTURE_PINCH_END = 805, + LIBINPUT_EVENT_GESTURE_HOLD_BEGIN = 806, + LIBINPUT_EVENT_GESTURE_HOLD_END = 807, + LIBINPUT_EVENT_SWITCH_TOGGLE = 900, +} diff --git a/src/libinput/device.rs b/src/libinput/device.rs new file mode 100644 index 00000000..bed4ca6d --- /dev/null +++ b/src/libinput/device.rs @@ -0,0 +1,51 @@ +use crate::libinput::sys::{ + libinput_device, libinput_device_set_user_data, libinput_device_unref, + libinput_path_remove_device, +}; +use crate::libinput::LibInput; +use std::marker::PhantomData; +use std::rc::Rc; + +pub struct LibInputDevice<'a> { + pub(super) dev: *mut libinput_device, + pub(super) _phantom: PhantomData<&'a ()>, +} + +pub struct RegisteredDevice { + pub(super) li: Rc, + pub(super) dev: *mut libinput_device, +} + +impl<'a> LibInputDevice<'a> { + pub fn set_slot(&self, slot: usize) { + self.set_slot_(slot + 1) + } + + pub fn unset_slot(&self) { + self.set_slot_(0) + } + + fn set_slot_(&self, slot: usize) { + unsafe { + libinput_device_set_user_data(self.dev, slot as _); + } + } +} + +impl RegisteredDevice { + pub fn device(&self) -> LibInputDevice { + LibInputDevice { + dev: self.dev, + _phantom: Default::default(), + } + } +} + +impl Drop for RegisteredDevice { + fn drop(&mut self) { + unsafe { + libinput_path_remove_device(self.dev); + libinput_device_unref(self.dev); + } + } +} diff --git a/src/libinput/event.rs b/src/libinput/event.rs new file mode 100644 index 00000000..3df51e69 --- /dev/null +++ b/src/libinput/event.rs @@ -0,0 +1,66 @@ +use crate::libinput::consts::{EventType, KeyState}; +use crate::libinput::device::LibInputDevice; +use crate::libinput::sys::{ + libinput_event, libinput_event_destroy, libinput_event_get_device, + libinput_event_get_keyboard_event, libinput_event_get_type, libinput_event_keyboard, + libinput_event_keyboard_get_key, libinput_event_keyboard_get_key_state, + libinput_event_keyboard_get_time_usec, +}; +use std::marker::PhantomData; + +pub struct LibInputEvent<'a> { + pub(super) event: *mut libinput_event, + pub(super) _phantom: PhantomData<&'a ()>, +} + +pub struct LibInputEventKeyboard<'a> { + pub(super) event: *mut libinput_event_keyboard, + pub(super) _phantom: PhantomData<&'a ()>, +} + +impl<'a> Drop for LibInputEvent<'a> { + fn drop(&mut self) { + unsafe { + libinput_event_destroy(self.event); + } + } +} + +impl<'a> LibInputEvent<'a> { + pub fn ty(&self) -> EventType { + unsafe { EventType(libinput_event_get_type(self.event)) } + } + + pub fn device(&self) -> LibInputDevice { + LibInputDevice { + dev: unsafe { libinput_event_get_device(self.event) }, + _phantom: Default::default(), + } + } + + pub fn keyboard_event(&self) -> Option { + let res = unsafe { libinput_event_get_keyboard_event(self.event) }; + if res.is_null() { + None + } else { + Some(LibInputEventKeyboard { + event: res, + _phantom: Default::default(), + }) + } + } +} + +impl<'a> LibInputEventKeyboard<'a> { + pub fn key(&self) -> u32 { + unsafe { libinput_event_keyboard_get_key(self.event) } + } + + pub fn key_state(&self) -> KeyState { + unsafe { KeyState(libinput_event_keyboard_get_key_state(self.event)) } + } + + pub fn time_usec(&self) -> u64 { + unsafe { libinput_event_keyboard_get_time_usec(self.event) } + } +} diff --git a/src/libinput/sys.rs b/src/libinput/sys.rs new file mode 100644 index 00000000..f5ccd8b5 --- /dev/null +++ b/src/libinput/sys.rs @@ -0,0 +1,64 @@ +use std::ffi::VaList; +use uapi::c; + +include!(concat!(env!("OUT_DIR"), "/libinput_tys.rs")); + +pub type libinput_log_handler = unsafe extern "C" fn( + libinput: *mut libinput, + priority: libinput_log_priority, + format: *const c::c_char, + args: VaList, +); + +#[link(name = "input")] +extern "C" { + pub type libinput; + pub type libinput_device; + pub type libinput_event; + pub type libinput_event_keyboard; + + pub fn libinput_log_set_handler(libinput: *mut libinput, log_handler: libinput_log_handler); + pub fn libinput_log_set_priority(libinput: *mut libinput, priority: libinput_log_priority); + pub fn libinput_path_create_context( + interface: *const libinput_interface, + user_data: *mut c::c_void, + ) -> *mut libinput; + pub fn libinput_device_set_user_data(device: *mut libinput_device, user_data: *mut c::c_void); + pub fn libinput_device_get_user_data(device: *mut libinput_device) -> *mut c::c_void; + pub fn libinput_device_ref(device: *mut libinput_device) -> *mut libinput_device; + pub fn libinput_device_unref(device: *mut libinput_device) -> *mut libinput_device; + pub fn libinput_path_add_device( + libinput: *mut libinput, + path: *const c::c_char, + ) -> *mut libinput_device; + pub fn libinput_path_remove_device(device: *mut libinput_device); + pub fn libinput_unref(libinput: *mut libinput) -> *mut libinput; + pub fn libinput_get_fd(libinput: *mut libinput) -> c::c_int; + + pub fn libinput_dispatch(libinput: *mut libinput) -> c::c_int; + + pub fn libinput_get_event(libinput: *mut libinput) -> *mut libinput_event; + + pub fn libinput_event_destroy(event: *mut libinput_event); + pub fn libinput_event_get_type(event: *mut libinput_event) -> libinput_event_type; + pub fn libinput_event_get_device(event: *mut libinput_event) -> *mut libinput_device; + pub fn libinput_event_get_keyboard_event( + event: *mut libinput_event, + ) -> *mut libinput_event_keyboard; + + pub fn libinput_event_keyboard_get_key(event: *mut libinput_event_keyboard) -> u32; + pub fn libinput_event_keyboard_get_key_state( + event: *mut libinput_event_keyboard, + ) -> libinput_key_state; + pub fn libinput_event_keyboard_get_time_usec(event: *mut libinput_event_keyboard) -> u64; +} + +#[repr(C)] +pub struct libinput_interface { + pub open_restricted: unsafe extern "C" fn( + path: *const c::c_char, + flags: c::c_int, + user_data: *mut c::c_void, + ) -> c::c_int, + pub close_restricted: unsafe extern "C" fn(fd: c::c_int, user_data: *mut c::c_void), +} diff --git a/src/logind.rs b/src/logind.rs index ae81a64b..5ff34031 100644 --- a/src/logind.rs +++ b/src/logind.rs @@ -1,8 +1,9 @@ -use crate::dbus::{DbusError, DbusSocket, Reply}; -use crate::org::freedesktop::login1::session::TakeControlReply; +use crate::dbus::{DbusError, DbusSocket}; +use crate::org::freedesktop::login1::session::TakeDeviceReply; use crate::{org, FALSE}; use std::rc::Rc; use thiserror::Error; +use uapi::c; const LOGIND_NAME: &str = "org.freedesktop.login1"; const MANAGER_PATH: &str = "/org/freedesktop/login1"; @@ -72,8 +73,22 @@ impl Session { ) .await; match res { - Ok(r) => Ok(()), + Ok(_) => Ok(()), Err(e) => Err(LogindError::TakeControl(e)), } } + + pub fn get_device(&self, dev: c::dev_t, f: F) + where + F: FnOnce(Result<&TakeDeviceReply, DbusError>) + 'static, + { + let major = uapi::major(dev) as _; + let minor = uapi::minor(dev) as _; + self.socket.call( + LOGIND_NAME, + &self.session_path, + org::freedesktop::login1::session::TakeDevice { major, minor }, + move |r| f(r), + ); + } } diff --git a/src/macros.rs b/src/macros.rs index 7f8671e2..ca6c300c 100644 --- a/src/macros.rs +++ b/src/macros.rs @@ -330,6 +330,7 @@ macro_rules! assert_size_eq { }}; } +#[allow(unused_macros)] macro_rules! assert_size_le { ($t:ty, $u:ty) => {{ struct AssertLeSize(std::marker::PhantomData, std::marker::PhantomData); diff --git a/src/main.rs b/src/main.rs index df4053c8..ad7e7c34 100644 --- a/src/main.rs +++ b/src/main.rs @@ -17,10 +17,11 @@ use crate::acceptor::AcceptorError; use crate::async_engine::{AsyncError, Phase}; use crate::backends::dummy::DummyBackend; -use crate::backends::xorg::{XorgBackend, XorgBackendError}; +use crate::backends::metal; +use crate::backends::xorg::XorgBackendError; use crate::client::Clients; use crate::clientmem::ClientMemError; -use crate::dbus::{Dbus, FALSE, TRUE}; +use crate::dbus::{Dbus, FALSE}; use crate::event_loop::EventLoopError; use crate::forker::ForkerError; use crate::globals::Globals; @@ -55,7 +56,6 @@ use std::ops::Deref; use std::rc::Rc; use thiserror::Error; use wheel::Wheel; -use crate::backends::metal; #[macro_use] mod macros; diff --git a/src/servermem.rs b/src/servermem.rs index a7b50150..75631dc6 100644 --- a/src/servermem.rs +++ b/src/servermem.rs @@ -10,15 +10,15 @@ use uapi::{c, Errno, OwnedFd}; #[derive(Debug, Error)] pub enum ServerMemError { #[error("memfd_create failed")] - MemfdCreate(#[source] std::io::Error), + MemfdCreate(#[source] crate::utils::oserror::OsError), #[error("The provided size does not fit into off_t")] SizeOverflow, #[error("ftruncate failed")] - Ftruncate(#[source] std::io::Error), + Ftruncate(#[source] crate::utils::oserror::OsError), #[error("mmap failed")] - MmapFailed(#[source] std::io::Error), + MmapFailed(#[source] crate::utils::oserror::OsError), #[error("sealing failed")] - Seal(#[source] std::io::Error), + Seal(#[source] crate::utils::oserror::OsError), } pub struct ServerMem { diff --git a/src/sighand.rs b/src/sighand.rs index fd763d3f..282f2e50 100644 --- a/src/sighand.rs +++ b/src/sighand.rs @@ -10,11 +10,11 @@ pub enum SighandError { #[error("The signal fd is in an error state")] ErrorEvent, #[error("Could not read from the signal fd")] - ReadFailed(#[source] std::io::Error), + ReadFailed(#[source] crate::utils::oserror::OsError), #[error("Could not block the signalfd signals")] - BlockFailed(#[source] std::io::Error), + BlockFailed(#[source] crate::utils::oserror::OsError), #[error("Could not create a signalfd")] - CreateFailed(#[source] std::io::Error), + CreateFailed(#[source] crate::utils::oserror::OsError), #[error("The event loop caused an error")] EventLoopError(#[from] EventLoopError), } diff --git a/src/time.rs b/src/time.rs index cbdc56d4..ad95015a 100644 --- a/src/time.rs +++ b/src/time.rs @@ -8,7 +8,7 @@ use uapi::c; #[derive(Debug, Error)] pub enum TimeError { #[error("clock_gettime failed: {0}")] - ClockGettime(std::io::Error), + ClockGettime(crate::utils::oserror::OsError), } #[derive(Copy, Clone)] diff --git a/src/udev.rs b/src/udev.rs index cf17d410..7808a795 100644 --- a/src/udev.rs +++ b/src/udev.rs @@ -1,10 +1,9 @@ -use crate::dbus::DbusError; use std::ffi::CStr; use std::marker::PhantomData; use std::ptr; use std::rc::Rc; use thiserror::Error; -use uapi::{c, Errno, IntoUstr, Ustr}; +use uapi::{c, Errno, IntoUstr}; #[link(name = "udev")] extern "C" { @@ -47,32 +46,33 @@ extern "C" { fn udev_device_get_sysname(udev_device: *mut udev_device) -> *const c::c_char; fn udev_device_get_is_initialized(udev_device: *mut udev_device) -> c::c_int; fn udev_device_get_devnode(udev_device: *mut udev_device) -> *const c::c_char; + fn udev_device_get_devnum(udev_device: *mut udev_device) -> c::dev_t; } #[derive(Debug, Error)] pub enum UdevError { #[error("Could not create a new udev instance")] - New(#[source] std::io::Error), + New(#[source] crate::utils::oserror::OsError), #[error("Could not create a new udev_monitor instance")] - NewMonitor(#[source] std::io::Error), + NewMonitor(#[source] crate::utils::oserror::OsError), #[error("Could not create a new udev_enumerate instance")] - NewEnumerate(#[source] std::io::Error), + NewEnumerate(#[source] crate::utils::oserror::OsError), #[error("Could not enable receiving on a udev_monitor")] - EnableReceiving(#[source] std::io::Error), + EnableReceiving(#[source] crate::utils::oserror::OsError), #[error("Could not add a match rule to a udev_monitor")] - MonitorAddMatch(#[source] std::io::Error), + MonitorAddMatch(#[source] crate::utils::oserror::OsError), #[error("Could not add a match rule to a udev_enumerate")] - EnumerateAddMatch(#[source] std::io::Error), + EnumerateAddMatch(#[source] crate::utils::oserror::OsError), #[error("Could not list devices of a udev_enumerate")] - EnumerateGetListEntry(#[source] std::io::Error), + EnumerateGetListEntry(#[source] crate::utils::oserror::OsError), #[error("Could not scan devices of a udev_enumerate")] - ScanDevices(#[source] std::io::Error), + ScanDevices(#[source] crate::utils::oserror::OsError), #[error("Could not create a udev_device from a syspath")] - DeviceFromSyspath(#[source] std::io::Error), + DeviceFromSyspath(#[source] crate::utils::oserror::OsError), #[error("Could not retrieve the sysname of a udev_device")] - GetSysname(#[source] std::io::Error), + GetSysname(#[source] crate::utils::oserror::OsError), #[error("Could not retrieve the devnode of a udev_device")] - GetDevnode(#[source] std::io::Error), + GetDevnode(#[source] crate::utils::oserror::OsError), } pub struct Udev { @@ -193,9 +193,7 @@ impl UdevMonitor { } pub fn receive_device(&self) -> Option { - let res = unsafe { - udev_monitor_receive_device(self.monitor) - }; + let res = unsafe { udev_monitor_receive_device(self.monitor) }; if res.is_null() { None } else { @@ -303,6 +301,10 @@ impl UdevDevice { } } + pub fn devnum(&self) -> c::dev_t { + unsafe { udev_device_get_devnum(self.device) } + } + pub fn is_initialized(&self) -> bool { unsafe { udev_device_get_is_initialized(self.device) != 0 } } diff --git a/src/utils/buffd/mod.rs b/src/utils/buffd/mod.rs index 2d7df5c7..ad796d9b 100644 --- a/src/utils/buffd/mod.rs +++ b/src/utils/buffd/mod.rs @@ -13,7 +13,7 @@ mod parser; #[derive(Debug, Error)] pub enum BufFdError { #[error("An IO error occurred")] - Io(#[source] std::io::Error), + Io(#[source] crate::utils::oserror::OsError), #[error("An async error occurred")] Async(#[from] AsyncError), #[error("The peer did not send a file descriptor")] diff --git a/src/utils/mod.rs b/src/utils/mod.rs index 1a8e3b5a..1461caf0 100644 --- a/src/utils/mod.rs +++ b/src/utils/mod.rs @@ -1,5 +1,6 @@ pub mod array; pub mod asyncevent; +pub mod bitfield; pub mod bitflags; pub mod buffd; pub mod clonecell; @@ -9,12 +10,13 @@ pub mod errorfmt; pub mod hex; pub mod linkedlist; pub mod numcell; +pub mod oserror; pub mod ptr_ext; pub mod queue; pub mod run_toplevel; pub mod smallmap; pub mod stack; pub mod tri; +pub mod vasprintf; pub mod vec_ext; pub mod vecstorage; -pub mod bitfield; diff --git a/src/utils/oserror.rs b/src/utils/oserror.rs new file mode 100644 index 00000000..d1205523 --- /dev/null +++ b/src/utils/oserror.rs @@ -0,0 +1,181 @@ +use once_cell::sync::Lazy; +use std::error::Error; +use std::fmt::{Display, Formatter}; +use uapi::{c, Errno}; + +static ERRORS: Lazy<&'static [Option<&'static str>]> = Lazy::new(|| { + static MSGS: &'static [(c::c_int, &'static str)] = &[ + (c::EWOULDBLOCK, "Operation would block"), + (c::ENOTSUP, "Not supported"), + (c::EHWPOISON, "Memory page has hardware error"), + (c::ERFKILL, "Operation not possible due to RF-kill"), + (c::EKEYREJECTED, "Key was rejected by service"), + (c::EKEYREVOKED, "Key has been revoked"), + (c::EKEYEXPIRED, "Key has expired"), + (c::ENOKEY, "Required key not available"), + (c::EMEDIUMTYPE, "Wrong medium type"), + (c::ENOMEDIUM, "No medium found"), + (c::EREMOTEIO, "Remote I/O error"), + (c::EISNAM, "Is a named type file"), + (c::ENAVAIL, "No XENIX semaphores available"), + (c::ENOTNAM, "Not a XENIX named type file"), + (c::EUCLEAN, "Structure needs cleaning"), + (c::ESTRPIPE, "Streams pipe error"), + (c::ELIBEXEC, "Cannot exec a shared library directly"), + ( + c::ELIBMAX, + "Attempting to link in too many shared libraries", + ), + (c::ELIBSCN, ".lib section in a.out corrupted"), + (c::ELIBBAD, "Accessing a corrupted shared library"), + (c::ELIBACC, "Can not access a needed shared library"), + (c::EREMCHG, "Remote address changed"), + (c::EBADFD, "File descriptor in bad state"), + (c::ENOTUNIQ, "Name not unique on network"), + (c::EDOTDOT, "RFS specific error"), + (c::ECOMM, "Communication error on send"), + (c::ESRMNT, "Srmount error"), + (c::EADV, "Advertise error"), + (c::ENOPKG, "Package not installed"), + (c::ENONET, "Machine is not on the network"), + (c::EBFONT, "Bad font file format"), + (c::EBADSLT, "Invalid slot"), + (c::EBADRQC, "Invalid request code"), + (c::ENOANO, "No anode"), + (c::EXFULL, "Exchange full"), + (c::EBADR, "Invalid request descriptor"), + (c::EBADE, "Invalid exchange"), + (c::EL2HLT, "Level 2 halted"), + (c::ENOCSI, "No CSI structure available"), + (c::EUNATCH, "Protocol driver not attached"), + (c::ELNRNG, "Link number out of range"), + (c::EL3RST, "Level 3 reset"), + (c::EL3HLT, "Level 3 halted"), + (c::EL2NSYNC, "Level 2 not synchronized"), + (c::ECHRNG, "Channel number out of range"), + (c::ERESTART, "Interrupted system call should be restarted"), + (c::ENOTRECOVERABLE, "State not recoverable"), + (c::EOWNERDEAD, "Owner died"), + (c::ECANCELED, "Operation canceled"), + (c::ETIME, "Timer expired"), + (c::EPROTO, "Protocol error"), + (c::EOVERFLOW, "Value too large for defined data type"), + (c::ENOSTR, "Device not a stream"), + (c::ENOSR, "Out of streams resources"), + (c::ENOMSG, "No message of desired type"), + (c::ENOLINK, "Link has been severed"), + (c::ENODATA, "No data available"), + (c::EMULTIHOP, "Multihop attempted"), + (c::EIDRM, "Identifier removed"), + (c::EBADMSG, "Bad message"), + ( + c::EILSEQ, + "Invalid or incomplete multibyte or wide character", + ), + (c::ENOSYS, "Function not implemented"), + (c::ENOLCK, "No locks available"), + (c::EREMOTE, "Object is remote"), + (c::ESTALE, "Stale file handle"), + (c::EDQUOT, "Disk quota exceeded"), + (c::EUSERS, "Too many users"), + (c::ENOTEMPTY, "Directory not empty"), + (c::EHOSTUNREACH, "No route to host"), + (c::EHOSTDOWN, "Host is down"), + (c::ENAMETOOLONG, "File name too long"), + (c::ELOOP, "Too many levels of symbolic links"), + (c::ECONNREFUSED, "Connection refused"), + (c::ETIMEDOUT, "Connection timed out"), + (c::ETOOMANYREFS, "Too many references: cannot splice"), + ( + c::ESHUTDOWN, + "Cannot send after transport endpoint shutdown", + ), + (c::EDESTADDRREQ, "Destination address required"), + (c::ENOTCONN, "Transport endpoint is not connected"), + (c::EISCONN, "Transport endpoint is already connected"), + (c::ENOBUFS, "No buffer space available"), + (c::ECONNRESET, "Connection reset by peer"), + (c::ECONNABORTED, "Software caused connection abort"), + (c::ENETRESET, "Network dropped connection on reset"), + (c::ENETUNREACH, "Network is unreachable"), + (c::ENETDOWN, "Network is down"), + (c::EADDRNOTAVAIL, "Cannot assign requested address"), + (c::EADDRINUSE, "Address already in use"), + (c::EAFNOSUPPORT, "Address family not supported by protocol"), + (c::EPFNOSUPPORT, "Protocol family not supported"), + (c::EOPNOTSUPP, "Operation not supported"), + (c::ESOCKTNOSUPPORT, "Socket type not supported"), + (c::EPROTONOSUPPORT, "Protocol not supported"), + (c::ENOPROTOOPT, "Protocol not available"), + (c::EPROTOTYPE, "Protocol wrong type for socket"), + (c::EMSGSIZE, "Message too long"), + (c::ENOTSOCK, "Socket operation on non-socket"), + (c::EALREADY, "Operation already in progress"), + (c::EINPROGRESS, "Operation now in progress"), + (c::EAGAIN, "Resource temporarily unavailable"), + (c::ERANGE, "Numerical result out of range"), + (c::EDOM, "Numerical argument out of domain"), + (c::EPIPE, "Broken pipe"), + (c::EMLINK, "Too many links"), + (c::EROFS, "Read-only file system"), + (c::ESPIPE, "Illegal seek"), + (c::ENOSPC, "No space left on device"), + (c::EFBIG, "File too large"), + (c::ETXTBSY, "Text file busy"), + (c::ENOTTY, "Inappropriate ioctl for device"), + (c::ENFILE, "Too many open files in system"), + (c::EMFILE, "Too many open files"), + (c::EINVAL, "Invalid argument"), + (c::EISDIR, "Is a directory"), + (c::ENOTDIR, "Not a directory"), + (c::ENODEV, "No such device"), + (c::EXDEV, "Invalid cross-device link"), + (c::EEXIST, "File exists"), + (c::EBUSY, "Device or resource busy"), + (c::ENOTBLK, "Block device required"), + (c::EFAULT, "Bad address"), + (c::EACCES, "Permission denied"), + (c::ENOMEM, "Cannot allocate memory"), + (c::EDEADLK, "Resource deadlock avoided"), + (c::ECHILD, "No child processes"), + (c::EBADF, "Bad file descriptor"), + (c::ENOEXEC, "Exec format error"), + (c::E2BIG, "Argument list too long"), + (c::ENXIO, "No such device or address"), + (c::EIO, "Input/output error"), + (c::EINTR, "Interrupted system call"), + (c::ESRCH, "No such process"), + (c::ENOENT, "No such file or directory"), + (c::EPERM, "Operation not permitted"), + ]; + let mut res = vec![]; + for &(idx, msg) in MSGS { + let idx = idx as usize; + while res.len() <= idx { + res.push(None); + } + res[idx] = Some(msg); + } + res.leak() +}); + +#[derive(Debug)] +pub struct OsError(pub c::c_int); + +impl From for OsError { + fn from(e: Errno) -> Self { + Self(e.0) + } +} + +impl Error for OsError {} + +impl Display for OsError { + fn fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result { + let msg = ERRORS + .get(self.0 as usize) + .and_then(|v| *v) + .unwrap_or("unknown error"); + write!(f, "{} (os error {})", msg, self.0) + } +} diff --git a/src/utils/vasprintf.rs b/src/utils/vasprintf.rs new file mode 100644 index 00000000..f4411812 --- /dev/null +++ b/src/utils/vasprintf.rs @@ -0,0 +1,38 @@ +use std::ffi::{CStr, VaList}; +use std::ops::Deref; +use std::ptr; +use uapi::c; + +extern "C" { + fn vasprintf(strp: *mut *mut c::c_char, fmt: *const c::c_char, ap: VaList) -> c::c_int; +} + +pub struct OwnedCStr { + val: &'static CStr, +} + +impl Deref for OwnedCStr { + type Target = CStr; + + fn deref(&self) -> &Self::Target { + self.val + } +} + +impl Drop for OwnedCStr { + fn drop(&mut self) { + unsafe { + c::free(self.val.as_ptr() as _); + } + } +} + +pub unsafe fn vasprintf_(fmt: *const c::c_char, ap: VaList) -> Option { + let mut res = ptr::null_mut(); + if vasprintf(&mut res, fmt, ap) == -1 { + return None; + } + Some(OwnedCStr { + val: CStr::from_ptr(res), + }) +} diff --git a/src/wheel.rs b/src/wheel.rs index 9fe6cab4..86056bac 100644 --- a/src/wheel.rs +++ b/src/wheel.rs @@ -14,9 +14,9 @@ use uapi::{c, OwnedFd}; #[derive(Debug, Error)] pub enum WheelError { #[error("Could not create the timerfd: {0}")] - CreateFailed(std::io::Error), + CreateFailed(crate::utils::oserror::OsError), #[error("Could not set the timerfd: {0}")] - SetFailed(std::io::Error), + SetFailed(crate::utils::oserror::OsError), #[error("The timerfd is in an error state")] ErrorEvent, #[error("An event loop error occurred: {0}")] diff --git a/src/xwayland.rs b/src/xwayland.rs index 7b68ce42..96a36705 100644 --- a/src/xwayland.rs +++ b/src/xwayland.rs @@ -20,11 +20,11 @@ use uapi::{c, pipe2, Errno, OwnedFd}; #[derive(Debug, Error)] enum XWaylandError { #[error("Could not create a wayland socket")] - SocketFailed(#[source] std::io::Error), + SocketFailed(#[source] crate::utils::oserror::OsError), #[error("/tmp/.X11-unix does not exist")] MissingSocketDir, #[error("Could not stat /tmp/.X11-unix")] - StatSocketDir(#[source] std::io::Error), + StatSocketDir(#[source] crate::utils::oserror::OsError), #[error("/tmp/.X11-unix is not a directory")] NotASocketDir, #[error("/tmp/.X11-unix is writable")] @@ -38,17 +38,17 @@ enum XWaylandError { #[error("The socket is already in use")] AlreadyInUse, #[error("Could not bind the socket to an address")] - BindFailed(#[source] std::io::Error), + BindFailed(#[source] crate::utils::oserror::OsError), #[error("All X displays in the range 0..1000 are already in use")] AddressesInUse, #[error("The async engine returned an error")] AsyncError(#[from] AsyncError), #[error("pipe(2) failed")] - Pipe(#[source] std::io::Error), + Pipe(#[source] crate::utils::oserror::OsError), #[error("dupfd(2) failed")] - Dupfd(#[source] std::io::Error), + Dupfd(#[source] crate::utils::oserror::OsError), #[error("socketpair(2) failed")] - Socketpair(#[source] std::io::Error), + Socketpair(#[source] crate::utils::oserror::OsError), #[error("Could not start Xwayland")] ExecFailed(#[source] ForkerError), #[error("Could not load the atoms")] @@ -224,7 +224,10 @@ async fn log_xwayland(state: Rc, stderr: OwnedFd) { let mut done = false; while !done { if let Err(e) = afd.readable().await { - log::error!("Cannot wait for the xwayland stderr to become readable: {}", ErrorFmt(e)); + log::error!( + "Cannot wait for the xwayland stderr to become readable: {}", + ErrorFmt(e) + ); return; } loop { @@ -242,7 +245,7 @@ async fn log_xwayland(state: Rc, stderr: OwnedFd) { Err(e) => { log::error!( "Could not read from stderr fd: {}", - ErrorFmt(std::io::Error::from(e)) + ErrorFmt(crate::utils::oserror::OsError::from(e)) ); return; }