diff --git a/build/build.rs b/build/build.rs index b5f8bf0a..3814632c 100644 --- a/build/build.rs +++ b/build/build.rs @@ -1,3 +1,5 @@ +extern crate core; + use std::fs::{File, OpenOptions}; use std::io::BufWriter; use std::path::PathBuf; diff --git a/build/wire_dbus.rs b/build/wire_dbus.rs index 74501c0a..3cbd051e 100644 --- a/build/wire_dbus.rs +++ b/build/wire_dbus.rs @@ -47,6 +47,12 @@ struct Property { ty: Type, } +#[derive(Debug)] +struct Signal { + name: BString, + fields: Vec, +} + struct Parser<'a> { pos: usize, tokens: &'a [Token<'a>], @@ -57,12 +63,14 @@ impl<'a> Parser<'a> { let mut res = Component { functions: vec![], properties: vec![], + signals: vec![], }; while !self.eof() { let (line, ty) = self.expect_ident()?; match ty.as_bytes() { b"fn" => res.functions.push(self.parse_fn()?.val), b"prop" => res.properties.push(self.parse_prop()?.val), + b"sig" => res.signals.push(self.parse_signal()?.val), _ => bail!("In line {}: Unexpected entry {:?}", line, ty), } } @@ -96,6 +104,29 @@ impl<'a> Parser<'a> { res.with_context(|| format!("While parsing property starting at line {}", line)) } + fn parse_signal(&mut self) -> Result> { + let (line, name) = self.expect_ident()?; + let res: Result<_> = (|| { + let (_, body) = self.expect_tree(TreeDelim::Brace)?; + let mut parser = Parser { + pos: 0, + tokens: body, + }; + let mut fields = vec![]; + while !parser.eof() { + fields.push(parser.parse_field()?); + } + Ok(Lined { + line, + val: Signal { + name: name.to_owned(), + fields, + }, + }) + })(); + res.with_context(|| format!("While parsing signal starting at line {}", line)) + } + fn parse_fn(&mut self) -> Result> { let (line, name) = self.expect_ident()?; let res: Result<_> = (|| { @@ -379,9 +410,9 @@ fn write_type2(f: &mut W, lt: &str, ty: &Type) -> Result<()> { Type::U16 => "u16", Type::I32 => "i32", Type::U32 => "u32", - Type::I64 => "AlignedI64", - Type::U64 => "AlignedU64", - Type::F64 => "AlignedF64", + Type::I64 => "i64", + Type::U64 => "u64", + Type::F64 => "f64", Type::String => { write!(f, "Cow<{}, str>", lt)?; return Ok(()); @@ -432,7 +463,7 @@ fn write_type2(f: &mut W, lt: &str, ty: &Type) -> Result<()> { fn write_message( f: &mut W, el: &Element, - fun: &Function, + msg_name: &BStr, name: &str, indent: &str, fields: &[Field], @@ -474,7 +505,7 @@ fn write_message( writeln!( f, "{} const MEMBER: &'static str = \"{}\";", - indent, fun.name + indent, msg_name, )?; writeln!(f, "{} type Generic<'b> = {}{};", indent, name, ltb,)?; writeln!(f)?; @@ -540,6 +571,9 @@ fn write_component( for prop in &component.properties { write_property(f, element, prop, indent)?; } + for sig in &component.signals { + write_signal(f, element, sig, indent)?; + } Ok(()) } @@ -570,6 +604,25 @@ fn write_property( Ok(()) } +fn write_signal(f: &mut W, element: &Element, sig: &Signal, indent: &str) -> Result<()> { + let name = format!("{}", sig.name); + write_message( + f, + element, + sig.name.as_bstr(), + &name, + indent, + &sig.fields, + None, + false, + )?; + let has_lt = sig.fields.iter().any(|f| needs_lifetime(&f.ty)); + let lt = if has_lt { "<'a>" } else { "" }; + writeln!(f)?; + writeln!(f, "{}impl<'a> Signal<'a> for {}{} {{ }}", indent, name, lt)?; + Ok(()) +} + fn write_function( f: &mut W, element: &Element, @@ -582,7 +635,7 @@ fn write_function( write_message( f, element, - fun, + fun.name.as_bstr(), &in_name, indent, &fun.in_fields, @@ -592,7 +645,7 @@ fn write_function( write_message( f, element, - fun, + fun.name.as_bstr(), &out_name, indent, &fun.out_fields, @@ -630,6 +683,7 @@ fn write_element(f: &mut W, element: Element, indent: &str) -> Result< struct Component { functions: Vec, properties: Vec, + signals: Vec, } #[derive(Debug)] diff --git a/src/async_engine.rs b/src/async_engine.rs index b2d3a808..e31fd24e 100644 --- a/src/async_engine.rs +++ b/src/async_engine.rs @@ -3,7 +3,7 @@ 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; +pub use fd::{AsyncFd, FdStatus}; use fd::AsyncFdData; use queue::{DispatchQueue, Dispatcher}; use std::cell::{Cell, RefCell}; @@ -21,6 +21,8 @@ pub enum AsyncError { WheelError(#[from] WheelError), #[error("The event loop caused an error: {0}")] EventLoopError(#[from] EventLoopError), + #[error("The file descriptor is in an error state")] + FdError, } #[derive(Copy, Clone, Eq, PartialEq)] @@ -90,7 +92,6 @@ impl AsyncEngine { read_registered: Cell::new(false), readers: RefCell::new(vec![]), writers: RefCell::new(vec![]), - erroneous: Cell::new(false), }); self.el.insert(id, Some(fd.raw()), 0, afd.clone())?; afd @@ -198,6 +199,7 @@ mod task { use std::rc::Rc; use std::task::{Context, Poll, RawWaker, RawWakerVTable, Waker}; + #[must_use] pub struct SpawnedFuture { vtable: &'static SpawnedFutureVtable, data: *mut u8, @@ -535,7 +537,7 @@ mod fd { use std::task::{Context, Poll, Waker}; use uapi::{c, OwnedFd}; - type Queue = RefCell>)>>; + type Queue = RefCell>>)>>; pub(super) struct AsyncFdData { pub(super) ref_count: NumCell, @@ -546,7 +548,6 @@ mod fd { pub(super) read_registered: Cell, pub(super) readers: Queue, pub(super) writers: Queue, - pub(super) erroneous: Cell, } impl AsyncFdData { @@ -560,21 +561,23 @@ mod fd { } let res = self.el.modify(self.id, events); if res.is_err() { - self.erroneous.set(true); - let _ = self.el.remove(self.id); + if let Err(e) = self.el.remove(self.id) { + log::error!("Fatal error: Cannot remove file descriptor from event loop: {:?}", e); + self.el.stop(); + } } res } fn poll( &self, - woken: &Rc>, + woken: &Rc>>, cx: &mut Context<'_>, registered: impl Fn(&AsyncFdData) -> &Cell, queue: impl Fn(&AsyncFdData) -> &Queue, - ) -> Poll> { - if woken.get() || self.erroneous.get() { - return Poll::Ready(Ok(())); + ) -> Poll> { + if let Some(status) = woken.get() { + return Poll::Ready(Ok(status)); } if !registered(self).get() { registered(self).set(true); @@ -591,30 +594,31 @@ mod fd { impl EventLoopDispatcher for AsyncFdData { fn dispatch(self: Rc, events: i32) -> Result<(), Box> { + let mut status = FdStatus::Ok; if events & (c::EPOLLERR | c::EPOLLHUP) != 0 { - self.erroneous.set(true); + status = FdStatus::Err; if let Err(e) = self.el.remove(self.id) { return Err(Box::new(e)); } } let mut woke_any = false; - if events & c::EPOLLIN != 0 || self.erroneous.get() { + if events & c::EPOLLIN != 0 || status == FdStatus::Err { let mut readers = self.readers.borrow_mut(); woke_any |= !readers.is_empty(); for (waker, woken) in readers.drain(..) { - woken.set(true); + woken.set(Some(status)); waker.wake(); } } - if events & c::EPOLLOUT != 0 || self.erroneous.get() { + if events & c::EPOLLOUT != 0 || status == FdStatus::Err { let mut writers = self.writers.borrow_mut(); woke_any |= !writers.is_empty(); for (waker, woken) in writers.drain(..) { - woken.set(true); + woken.set(Some(status)); waker.wake(); } } - if !woke_any && !self.erroneous.get() { + if !woke_any && status == FdStatus::Ok { self.read_registered.set(false); self.write_registered.set(false); if let Err(e) = self.update_interests() { @@ -666,25 +670,31 @@ mod fd { pub fn readable(&self) -> AsyncFdReadable { AsyncFdReadable { fd: self, - woken: Rc::new(Cell::new(false)), + woken: Rc::new(Cell::new(None)), } } pub fn writable(&self) -> AsyncFdWritable { AsyncFdWritable { fd: self, - woken: Rc::new(Cell::new(false)), + woken: Rc::new(Cell::new(None)), } } } + #[derive(Copy, Clone, Debug, Eq, PartialEq)] + pub enum FdStatus { + Ok, + Err, + } + pub struct AsyncFdReadable<'a> { fd: &'a AsyncFd, - woken: Rc>, + woken: Rc>>, } impl<'a> Future for AsyncFdReadable<'a> { - type Output = Result<(), AsyncError>; + type Output = Result; fn poll(self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll { let data = &self.fd.data; @@ -694,11 +704,11 @@ mod fd { pub struct AsyncFdWritable<'a> { fd: &'a AsyncFd, - woken: Rc>, + woken: Rc>>, } impl<'a> Future for AsyncFdWritable<'a> { - type Output = Result<(), AsyncError>; + type Output = Result; fn poll(self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll { let data = &self.fd.data; diff --git a/src/backends/metal.rs b/src/backends/metal.rs new file mode 100644 index 00000000..e2721ad9 --- /dev/null +++ b/src/backends/metal.rs @@ -0,0 +1,128 @@ +use crate::dbus::DbusError; +use crate::logind::{LogindError, Session}; +use crate::{AsyncQueue, ErrorFmt, State, Udev}; +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}; + +#[derive(Debug, Error)] +pub enum MetalError { + #[error("Could not connect to the dbus system socket")] + DbusSystemSocket(#[source] DbusError), + #[error("Could not retrieve the logind session")] + LogindSession(#[source] LogindError), + #[error("Could not take control of the logind session")] + TakeControl(#[source] LogindError), + #[error(transparent)] + Udev(#[from] UdevError), + #[error(transparent)] + LibInput(#[from] LibInputError), + #[error("Dupfd failed")] + Dup(#[source] std::io::Error), +} + +pub async fn run(state: Rc) { + if let Err(e) = run_(state).await { + log::error!("{}", ErrorFmt(e)); + } +} + +async fn run_(state: Rc) -> Result<(), MetalError> { + let socket = match state.dbus.system() { + Ok(s) => s, + Err(e) => return Err(MetalError::DbusSystemSocket(e)), + }; + let session = match Session::get(&socket).await { + Ok(s) => s, + Err(e) => return Err(MetalError::LogindSession(e)), + }; + // if let Err(e) = session.take_control().await { + // return Err(MetalError::TakeControl(e)); + // } + 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 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 metal = Rc::new(MetalBackend { + state: state.clone(), + udev, + monitor, + monitor_fd, + libinput, + }); + let _monitor = state.eng.spawn(metal.clone().monitor_devices()); + let mut 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, +} + +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."); + } +} diff --git a/src/backends/mod.rs b/src/backends/mod.rs index 03fd5cba..4888f27d 100644 --- a/src/backends/mod.rs +++ b/src/backends/mod.rs @@ -1,2 +1,3 @@ pub mod dummy; +pub mod metal; pub mod xorg; diff --git a/src/dbus.rs b/src/dbus.rs index 6a5ed5a4..db69bc01 100644 --- a/src/dbus.rs +++ b/src/dbus.rs @@ -5,10 +5,12 @@ use crate::utils::copyhashmap::CopyHashMap; use crate::utils::stack::Stack; use crate::utils::vecstorage::VecStorage; use crate::{AsyncEngine, AsyncError, AsyncQueue, CloneCell, NumCell, RunToplevel}; +use ahash::AHashMap; use std::borrow::Cow; use std::cell::{Cell, RefCell}; use std::fmt::{Debug, Display}; use std::future::Future; +use std::marker::PhantomData; use std::mem; use std::pin::Pin; use std::rc::Rc; @@ -52,6 +54,8 @@ pub enum DbusError { Closed, #[error("Function call reply does not contain a reply serial")] NoReplySerial, + #[error("Signal message contains no interface or member or path")] + MissingSignalHeaders, #[error("Error has no error name")] NoErrorName, #[error("The socket was killed")] @@ -102,6 +106,8 @@ pub enum DbusError { InvalidProtocol, #[error("Signature contains an invalid type")] InvalidSignatureType, + #[error("The signal already has a handler")] + AlreadyHandled, } efrom!(DbusError, AsyncError); @@ -151,6 +157,7 @@ pub struct DbusSocket { dead: Cell, headers: RefCell)>>, run_toplevel: Rc, + signal_handlers: RefCell>, } const TY_BYTE: u8 = b'y'; @@ -188,6 +195,9 @@ const NO_REPLY_EXPECTED: u8 = 0x1; const NO_AUTO_START: u8 = 0x2; const ALLOW_INTERACTIVE_AUTHORIZATION: u8 = 0x4; +pub const BUS_DEST: &'static str = "org.freedesktop.DBus"; +pub const BUS_PATH: &'static str = "/org/freedesktop/dbus"; + #[derive(Default, Debug)] struct Headers<'a> { path: Option>, @@ -279,6 +289,8 @@ pub trait Property { type Type: DbusType<'static>; } +pub trait Signal<'a>: Message<'a> {} + pub trait MethodCall<'a>: Message<'a> { type Reply: Message<'static>; } @@ -391,10 +403,74 @@ impl Future for AsyncProperty { } } +struct SignalHandlerData { + path: Option, + rule: String, + handler: F, + _phantom: PhantomData, +} + +trait SignalHandlerApi { + fn interface(&self) -> &'static str; + fn member(&self) -> &'static str; + fn signature(&self) -> &'static str; + fn path(&self) -> Option<&str>; + fn rule(&self) -> &str; + fn handle(&self, parser: &mut Parser) -> Result<(), DbusError>; +} + +impl SignalHandlerApi for SignalHandlerData +where + T: Signal<'static>, + F: for<'a> Fn(T::Generic<'a>), +{ + fn interface(&self) -> &'static str { + T::INTERFACE + } + + fn member(&self) -> &'static str { + T::MEMBER + } + + fn signature(&self) -> &'static str { + T::SIGNATURE + } + + fn path(&self) -> Option<&str> { + self.path.as_deref() + } + + fn rule(&self) -> &str { + &self.rule + } + + fn handle<'a>(&self, parser: &mut Parser<'a>) -> Result<(), DbusError> { + (self.handler)(T::Generic::<'a>::unmarshal(parser)?); + Ok(()) + } +} + +#[must_use] +pub struct SignalHandler { + socket: Rc, + data: Rc, +} + +impl Drop for SignalHandler { + fn drop(&mut self) { + self.socket.remove_signal_handler(&*self.data); + } +} + +struct InterfaceSignalHandlers { + unconditional: Option>, + conditional: AHashMap>, +} + pub mod prelude { pub use super::{ types::{Bool, DictEntry, ObjectPath, Signature, Variant}, - DbusError, DbusType, Formatter, Message, MethodCall, Parser, Property, + DbusError, DbusType, Formatter, Message, MethodCall, Parser, Property, Signal, }; pub use std::borrow::Cow; pub use std::rc::Rc; diff --git a/src/dbus/auth.rs b/src/dbus/auth.rs index 872c3984..dd0fb4a5 100644 --- a/src/dbus/auth.rs +++ b/src/dbus/auth.rs @@ -86,7 +86,7 @@ impl Auth { match uapi::read(self.socket.fd.raw(), &mut self.buf[..]) { Ok(n) => self.buf_stop = n.len(), Err(Errno(c::EAGAIN)) => { - let _ = self.socket.fd.readable().await; + self.socket.fd.readable().await?; } Err(e) => return Err(DbusError::ReadError(e.into())), } @@ -99,7 +99,7 @@ impl Auth { match uapi::write(self.socket.fd.raw(), &buf[start..]) { Ok(n) => start += n, Err(Errno(c::EAGAIN)) => { - let _ = self.socket.fd.writable().await; + self.socket.fd.writable().await?; } Err(e) => return Err(DbusError::WriteError(e.into())), } diff --git a/src/dbus/dynamic_type.rs b/src/dbus/dynamic_type.rs index 5d270d6e..ca4654d3 100644 --- a/src/dbus/dynamic_type.rs +++ b/src/dbus/dynamic_type.rs @@ -137,7 +137,7 @@ impl DynamicType { DynamicType::U32 => Variant::U32(parser.read_pod()?), DynamicType::I64 => Variant::I64(parser.read_pod()?), DynamicType::U64 => Variant::U64(parser.read_pod()?), - DynamicType::F64 => Variant::F64(parser.read_pod()?), + DynamicType::F64 => Variant::F64(f64::from_bits(parser.read_pod()?)), DynamicType::String => Variant::String(parser.read_string()?), DynamicType::ObjectPath => Variant::ObjectPath(parser.read_object_path()?), DynamicType::Signature => Variant::Signature(parser.read_signature()?), diff --git a/src/dbus/holder.rs b/src/dbus/holder.rs index 0747a75d..d04e9bcf 100644 --- a/src/dbus/holder.rs +++ b/src/dbus/holder.rs @@ -61,6 +61,7 @@ fn connect( dead: Cell::new(false), headers: Default::default(), run_toplevel: run_toplevel.clone(), + signal_handlers: Default::default(), }); let skt = socket.clone(); socket.call( diff --git a/src/dbus/incoming.rs b/src/dbus/incoming.rs index e49c337b..1019aa62 100644 --- a/src/dbus/incoming.rs +++ b/src/dbus/incoming.rs @@ -3,7 +3,7 @@ use super::{ HDR_SENDER, HDR_SIGNATURE, HDR_UNIX_FDS, }; use crate::dbus::{ - CallError, DbusError, DbusSocket, Headers, Parser, MSG_ERROR, MSG_METHOD_RETURN, + CallError, DbusError, DbusSocket, Headers, Parser, MSG_ERROR, MSG_METHOD_RETURN, MSG_SIGNAL, }; use crate::utils::ptr_ext::{MutPtrExt, PtrExt}; use crate::ErrorFmt; @@ -119,8 +119,8 @@ impl Incoming { log::error!( "{}: Message reply has an invalid signature: expected: {}, actual: {}", self.socket.bus_name, + reply.signature(), sig, - reply.signature() ); } else { let buf = unsafe { std::mem::take(msg_buf_data.get().deref_mut()) }; @@ -135,6 +135,39 @@ impl Incoming { } } } + MSG_SIGNAL => { + let (interface, member, path) = + match (&headers.interface, &headers.member, &headers.path) { + (Some(i), Some(m), Some(p)) => (i, m, p), + _ => return Err(DbusError::MissingSignalHeaders), + }; + let handlers = self.socket.signal_handlers.borrow_mut(); + if let Some(handler) = handlers.get(&(interface.deref(), member.deref())) { + let handler = handler + .conditional + .get(path.deref()) + .or(handler.unconditional.as_ref()); + if let Some(handler) = handler { + let sig = headers.signature.as_deref().unwrap_or(""); + if sig != handler.signature() { + log::error!( + "{}: Signal has an invalid signature: expected: {}, actual: {}", + self.socket.bus_name, + handler.signature(), + sig, + ); + } else { + if let Err(e) = handler.handle(&mut parser) { + log::error!( + "{}: Could not handle signal: {}", + self.socket.bus_name, + ErrorFmt(e) + ); + } + } + } + } + } _ => {} } let msg_buf = msg_buf_data.into_inner(); @@ -174,7 +207,7 @@ impl Incoming { if e.0 != c::EAGAIN { return Err(DbusError::ReadError(e.into())); } - let _ = self.socket.fd.readable().await; + self.socket.fd.readable().await?; } if self.buf_start == self.buf_end { return Err(DbusError::Closed); diff --git a/src/dbus/outgoing.rs b/src/dbus/outgoing.rs index 0e431a7c..d7fac1f5 100644 --- a/src/dbus/outgoing.rs +++ b/src/dbus/outgoing.rs @@ -48,7 +48,11 @@ impl Outgoing { self.socket.kill(); return; } - let _ = self.socket.fd.writable().await; + if let Err(e) = self.socket.fd.writable().await { + log::error!("{}: Cannot wait for fd to become readable: {}", self.socket.bus_name, ErrorFmt(e)); + self.socket.kill(); + return; + } } } } diff --git a/src/dbus/socket.rs b/src/dbus/socket.rs index 2a4a11dc..ed9d8a41 100644 --- a/src/dbus/socket.rs +++ b/src/dbus/socket.rs @@ -2,11 +2,15 @@ use crate::dbus::property::Get; use crate::dbus::types::{ObjectPath, Signature, Variant}; use crate::dbus::{ AsyncProperty, AsyncReply, AsyncReplySlot, DbusError, DbusMessage, DbusSocket, DbusType, - Formatter, Headers, Message, MethodCall, Parser, Property, Reply, ReplyHandler, + Formatter, Headers, InterfaceSignalHandlers, Message, MethodCall, Parser, Property, Reply, + ReplyHandler, Signal, SignalHandler, SignalHandlerApi, SignalHandlerData, BUS_DEST, BUS_PATH, HDR_DESTINATION, HDR_INTERFACE, HDR_MEMBER, HDR_PATH, HDR_SIGNATURE, HDR_UNIX_FDS, MSG_METHOD_CALL, NO_REPLY_EXPECTED, }; +use crate::{org, ErrorFmt}; use std::cell::Cell; +use std::collections::hash_map::Entry; +use std::fmt::Write; use std::marker::PhantomData; use std::mem; use std::ops::DerefMut; @@ -116,6 +120,118 @@ impl DbusSocket { } } + pub fn handle_signal( + self: &Rc, + sender: Option<&str>, + path: Option<&str>, + handler: F, + ) -> Result + where + T: Signal<'static>, + F: for<'a> Fn(T::Generic<'a>) + 'static, + { + let mut rule = format!( + "type='signal',interface='{}',member='{}'", + T::INTERFACE, + T::MEMBER + ); + if let Some(sender) = sender { + let _ = write!(rule, ",sender='{}'", sender); + } + if let Some(path) = path { + let _ = write!(rule, ",path='{}'", path); + } + let shd: SignalHandlerData = SignalHandlerData { + path: path.map(|s| s.to_owned()), + rule, + handler, + _phantom: Default::default(), + }; + self.handle_signal_dyn(Rc::new(shd)) + } + + fn handle_signal_dyn( + self: &Rc, + handler: Rc, + ) -> Result { + let mut sh = self.signal_handlers.borrow_mut(); + let entry = sh + .entry((handler.interface(), handler.member())) + .or_insert_with(|| InterfaceSignalHandlers { + unconditional: Default::default(), + conditional: Default::default(), + }); + match handler.path() { + Some(p) => match entry.conditional.entry(p.to_owned()) { + Entry::Occupied(_) => return Err(DbusError::AlreadyHandled), + Entry::Vacant(v) => { + v.insert(handler.clone()); + } + }, + _ if entry.unconditional.is_some() => return Err(DbusError::AlreadyHandled), + _ => entry.unconditional = Some(handler.clone()), + } + self.call( + BUS_DEST, + BUS_PATH, + org::freedesktop::dbus::AddMatch { + rule: handler.rule().into(), + }, + { + let slf = self.clone(); + move |res| { + if let Err(e) = res { + log::error!( + "{}: Could not register a signal handler: {}", + slf.bus_name, + ErrorFmt(e) + ); + } + } + }, + ); + Ok(SignalHandler { + socket: self.clone(), + data: handler, + }) + } + + pub(super) fn remove_signal_handler(self: &Rc, handler: &dyn SignalHandlerApi) { + let mut sh = self.signal_handlers.borrow_mut(); + let mut entry = match sh.entry((handler.interface(), handler.member())) { + Entry::Occupied(o) => o, + Entry::Vacant(_) => return, + }; + match handler.path() { + Some(p) => { + entry.get_mut().conditional.remove(p); + } + _ => entry.get_mut().unconditional = None, + } + if entry.get().unconditional.is_none() && entry.get().conditional.is_empty() { + entry.remove(); + } + self.call( + BUS_DEST, + BUS_PATH, + org::freedesktop::dbus::RemoveMatch { + rule: handler.rule().into(), + }, + { + let slf = self.clone(); + move |res| { + if let Err(e) = res { + log::error!( + "{}: Could not unregister a signal handler: {}", + slf.bus_name, + ErrorFmt(e) + ); + } + } + }, + ); + } + fn send_call<'a, T: Message<'a>>( &self, path: &str, diff --git a/src/dbus/types.rs b/src/dbus/types.rs index 3d2536bd..ed18e672 100644 --- a/src/dbus/types.rs +++ b/src/dbus/types.rs @@ -3,7 +3,6 @@ use crate::dbus::{ TY_INT16, TY_INT32, TY_INT64, TY_OBJECT_PATH, TY_SIGNATURE, TY_STRING, TY_UINT16, TY_UINT32, TY_UINT64, TY_UNIX_FD, TY_VARIANT, }; -use crate::utils::aligned::{AlignedF64, AlignedI64, AlignedU64}; use std::borrow::Cow; use std::ops::Deref; use std::rc::Rc; @@ -139,7 +138,7 @@ unsafe impl<'a> DbusType<'a> for u32 { } } -unsafe impl<'a> DbusType<'a> for AlignedI64 { +unsafe impl<'a> DbusType<'a> for i64 { const ALIGNMENT: usize = 8; const IS_POD: bool = true; type Generic<'b> = Self; @@ -155,7 +154,7 @@ unsafe impl<'a> DbusType<'a> for AlignedI64 { } } -unsafe impl<'a> DbusType<'a> for AlignedU64 { +unsafe impl<'a> DbusType<'a> for u64 { const ALIGNMENT: usize = 8; const IS_POD: bool = true; type Generic<'b> = Self; @@ -171,7 +170,7 @@ unsafe impl<'a> DbusType<'a> for AlignedU64 { } } -unsafe impl<'a> DbusType<'a> for AlignedF64 { +unsafe impl<'a> DbusType<'a> for f64 { const ALIGNMENT: usize = 8; const IS_POD: bool = true; type Generic<'b> = Self; @@ -179,11 +178,11 @@ unsafe impl<'a> DbusType<'a> for AlignedF64 { signature!(TY_DOUBLE); fn marshal(&self, fmt: &mut Formatter) { - fmt.write_packed(self); + fmt.write_packed(&self.to_bits()); } fn unmarshal(parser: &mut Parser<'a>) -> Result { - parser.read_pod() + Ok(f64::from_bits(parser.read_pod()?)) } } @@ -417,9 +416,9 @@ pub enum Variant<'a> { U16(u16), I32(i32), U32(u32), - I64(AlignedI64), - U64(AlignedU64), - F64(AlignedF64), + I64(i64), + U64(u64), + F64(f64), String(Cow<'a, str>), ObjectPath(ObjectPath<'a>), Signature(Signature<'a>), diff --git a/src/event_loop.rs b/src/event_loop.rs index 1914af94..1b62055c 100644 --- a/src/event_loop.rs +++ b/src/event_loop.rs @@ -128,7 +128,9 @@ impl EventLoop { }; if let Some(fd) = entry.fd { if let Err(e) = uapi::epoll_ctl(self.epoll.raw(), c::EPOLL_CTL_DEL, fd, None) { - return Err(EventLoopError::RemoveFailed(e.into())); + if e.0 != c::ENOENT { + return Err(EventLoopError::RemoveFailed(e.into())); + } } } Ok(()) diff --git a/src/forker.rs b/src/forker.rs index 6da587df..bc4a6e5f 100644 --- a/src/forker.rs +++ b/src/forker.rs @@ -234,8 +234,11 @@ impl ForkerProxy { async fn check_process(self: Rc, state: Rc) { let pidfd = state.eng.fd(&self.pidfd).unwrap(); - let _ = pidfd.readable().await; - let _ = uapi::waitpid(self.pid, 0); + if let Err(e) = pidfd.readable().await { + log::error!("Cannot wait for the forker pidfd to become readable: {}", ErrorFmt(e)); + } else { + let _ = uapi::waitpid(self.pid, 0); + } log::error!("The ol' forker died. Cannot spawn further processes."); state.forker.set(None); self.task_out.take(); @@ -413,14 +416,17 @@ impl Forker { let slf = self.clone(); let spawn = self.ae.spawn(async move { let read = slf.ae.fd(&Rc::new(read)).unwrap(); - let _ = read.readable().await; - let mut s = String::new(); - let _ = Fd::new(read.raw()).read_to_string(&mut s); - if s.len() > 0 { - slf.outgoing.push(ForkerMessage::Log { - level: log::Level::Error as _, - msg: format!("Could not spawn `{}`: {}", prog, s), - }); + if let Err(e) = read.readable().await { + 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); + if s.len() > 0 { + slf.outgoing.push(ForkerMessage::Log { + level: log::Level::Error as _, + msg: format!("Could not spawn `{}`: {}", prog, s), + }); + } } slf.pending_spawns.remove(&pid); }); diff --git a/src/ifs/wl_seat.rs b/src/ifs/wl_seat.rs index d6ae4afa..f9c9e8d6 100644 --- a/src/ifs/wl_seat.rs +++ b/src/ifs/wl_seat.rs @@ -157,13 +157,14 @@ impl WlSeatGlobal { tree_changed_handler: Cell::new(None), }); let seat = slf.clone(); - state.eng.spawn(async move { + let future = state.eng.spawn(async move { loop { seat.tree_changed.triggered().await; seat.state.tree_changed_sent.set(false); seat.tree_changed(); } }); + slf.tree_changed_handler.set(Some(future)); slf } diff --git a/src/libinput.rs b/src/libinput.rs new file mode 100644 index 00000000..c5bb54dc --- /dev/null +++ b/src/libinput.rs @@ -0,0 +1,84 @@ +use crate::udev::Udev; +use crate::utils::ptr_ext::PtrExt; +use std::ops::DerefMut; +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), +} + +static INTERFACE: libinput_interface = libinput_interface { + open_restricted, + close_restricted, +}; + +unsafe extern "C" fn open_restricted( + path: *const c::c_char, + flags: c::c_int, + user_data: *mut c::c_void, +) -> c::c_int { + let ud = (user_data as *const UserData).deref(); + -1 +} + +unsafe extern "C" fn close_restricted(fd: c::c_int, _user_data: *mut c::c_void) { + drop(OwnedFd::new(fd)); +} + +struct UserData {} + +#[derive(Debug, Error)] +pub enum LibInputError { + #[error("Could not create a libinput instance")] + New, +} + +pub struct LibInput { + data: Box, + li: *mut libinput, +} + +impl LibInput { + pub fn new() -> Result { + let mut ud = Box::new(UserData {}); + 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); + } + Ok(Self { data: ud, li }) + } + + pub fn fd(&self) -> c::c_int { + unsafe { libinput_get_fd(self.li) } + } +} + +impl Drop for LibInput { + fn drop(&mut self) { + unsafe { + libinput_unref(self.li); + } + } +} diff --git a/src/logind.rs b/src/logind.rs index 8b137891..ae81a64b 100644 --- a/src/logind.rs +++ b/src/logind.rs @@ -1 +1,79 @@ +use crate::dbus::{DbusError, DbusSocket, Reply}; +use crate::org::freedesktop::login1::session::TakeControlReply; +use crate::{org, FALSE}; +use std::rc::Rc; +use thiserror::Error; +const LOGIND_NAME: &str = "org.freedesktop.login1"; +const MANAGER_PATH: &str = "/org/freedesktop/login1"; + +#[derive(Debug, Error)] +pub enum LogindError { + #[error("XDG_SESSION_ID is not set")] + XdgSessionId, + #[error("Could not retrieve the session dbus path")] + GetSession(DbusError), + #[error("Could not retrieve the session's seat name")] + GetSeatName(DbusError), + #[error(transparent)] + TakeControl(DbusError), +} + +pub struct Session { + socket: Rc, + seat: String, + session_path: String, +} + +impl Session { + pub async fn get(socket: &Rc) -> Result { + let session_id = match std::env::var("XDG_SESSION_ID") { + Ok(id) => id, + _ => return Err(LogindError::XdgSessionId), + }; + let session_path = { + let session = socket + .call_async( + LOGIND_NAME, + MANAGER_PATH, + org::freedesktop::login1::manager::GetSession { + session_id: session_id.as_str().into(), + }, + ) + .await; + match session { + Ok(s) => s.get().object_path.to_string(), + Err(e) => return Err(LogindError::GetSession(e)), + } + }; + let seat = { + let seat = socket + .get_async::(LOGIND_NAME, &session_path) + .await; + match seat { + Ok(s) => s.get().0.to_string(), + Err(e) => return Err(LogindError::GetSeatName(e)), + } + }; + Ok(Self { + socket: socket.clone(), + seat, + session_path, + }) + } + + pub async fn take_control(&self) -> Result<(), LogindError> { + let res = self + .socket + .call_async( + LOGIND_NAME, + &self.session_path, + org::freedesktop::login1::session::TakeControl { force: FALSE }, + ) + .await; + match res { + Ok(r) => Ok(()), + Err(e) => Err(LogindError::TakeControl(e)), + } + } +} diff --git a/src/macros.rs b/src/macros.rs index 8dd34011..7f8671e2 100644 --- a/src/macros.rs +++ b/src/macros.rs @@ -330,6 +330,21 @@ macro_rules! assert_size_eq { }}; } +macro_rules! assert_size_le { + ($t:ty, $u:ty) => {{ + struct AssertLeSize(std::marker::PhantomData, std::marker::PhantomData); + impl AssertLeSize { + const VAL: usize = { + if std::mem::size_of::() > std::mem::size_of::() { + panic!("Left type has size larger than right type"); + } + 1 + }; + } + let _ = AssertLeSize::<$t, $u>::VAL; + }}; +} + macro_rules! assert_align_eq { ($t:ty, $u:ty) => {{ struct AssertEqAlign(std::marker::PhantomData, std::marker::PhantomData); diff --git a/src/main.rs b/src/main.rs index 77242aad..df4053c8 100644 --- a/src/main.rs +++ b/src/main.rs @@ -3,7 +3,8 @@ thread_local, label_break_value, try_blocks, - generic_associated_types + generic_associated_types, + extern_types )] #![allow( clippy::len_zero, @@ -19,7 +20,7 @@ use crate::backends::dummy::DummyBackend; use crate::backends::xorg::{XorgBackend, XorgBackendError}; use crate::client::Clients; use crate::clientmem::ClientMemError; -use crate::dbus::{Dbus, FALSE}; +use crate::dbus::{Dbus, FALSE, TRUE}; use crate::event_loop::EventLoopError; use crate::forker::ForkerError; use crate::globals::Globals; @@ -36,6 +37,7 @@ use crate::state::State; use crate::tree::{ container_layout, container_titles, float_layout, float_titles, DisplayNode, NodeIds, }; +use crate::udev::Udev; use crate::utils::clonecell::CloneCell; use crate::utils::errorfmt::ErrorFmt; use crate::utils::numcell::NumCell; @@ -53,6 +55,7 @@ use std::ops::Deref; use std::rc::Rc; use thiserror::Error; use wheel::Wheel; +use crate::backends::metal; #[macro_use] mod macros; @@ -75,6 +78,7 @@ mod forker; mod format; mod globals; mod ifs; +mod libinput; mod logind; mod object; mod pixman; @@ -88,6 +92,7 @@ mod text; mod theme; mod time; mod tree; +mod udev; mod utils; mod wheel; mod wire; @@ -178,40 +183,10 @@ fn main_() -> Result<(), MainError> { pending_float_titles: Default::default(), dbus: Dbus::new(&engine, &run_toplevel), }); - let _future = state.eng.spawn({ - let dbus = state.dbus.system().unwrap(); - async move { - const LOGIND: &str = "org.freedesktop.login1"; - let reply = dbus - .call_async( - LOGIND, - "/org/freedesktop/login1", - org::freedesktop::login1::manager::GetSession { - session_id: std::env::var("XDG_SESSION_ID").unwrap().into(), - }, - ) - .await - .unwrap(); - let reply = dbus - .call_async( - LOGIND, - &reply.get().object_path, - org::freedesktop::login1::session::TakeControl { force: FALSE }, - ) - .await; - log::info!("{:?}", reply); - let reply = dbus - .get_async::( - LOGIND, - "/org/freedesktop/login1", - ) - .await; - log::info!("{:?}", reply); - } - }); + let _future = state.eng.spawn(metal::run(state.clone())); forker.install(&state); - let backend = XorgBackend::new(&state)?; - state.backend.set(backend); + // let backend = XorgBackend::new(&state)?; + // state.backend.set(backend); let config = config::ConfigProxy::default(&state); state.config.set(Some(Rc::new(config))); let _global_event_handler = engine.spawn(tasks::handle_backend_events(state.clone())); diff --git a/src/udev.rs b/src/udev.rs new file mode 100644 index 00000000..cf17d410 --- /dev/null +++ b/src/udev.rs @@ -0,0 +1,317 @@ +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}; + +#[link(name = "udev")] +extern "C" { + type udev; + type udev_monitor; + type udev_enumerate; + type udev_list_entry; + type udev_device; + + fn udev_new() -> *mut udev; + fn udev_unref(udev: *mut udev) -> *mut udev; + + fn udev_monitor_new_from_netlink(udev: *mut udev, name: *const c::c_char) -> *mut udev_monitor; + fn udev_monitor_get_fd(udev_monitor: *mut udev_monitor) -> c::c_int; + fn udev_monitor_unref(udev_monitor: *mut udev_monitor) -> *mut udev_monitor; + fn udev_monitor_enable_receiving(udev_monitor: *mut udev_monitor) -> c::c_int; + fn udev_monitor_filter_add_match_subsystem_devtype( + udev_monitor: *mut udev_monitor, + subsystem: *const c::c_char, + devtype: *const c::c_char, + ) -> c::c_int; + fn udev_monitor_receive_device(udev_monitor: *mut udev_monitor) -> *mut udev_device; + + fn udev_enumerate_new(udev: *mut udev) -> *mut udev_enumerate; + fn udev_enumerate_unref(udev_enumerate: *mut udev_enumerate) -> *mut udev_enumerate; + fn udev_enumerate_add_match_subsystem( + udev_enumerate: *mut udev_enumerate, + subsystem: *const c::c_char, + ) -> c::c_int; + fn udev_enumerate_scan_devices(udev_enumerate: *mut udev_enumerate) -> c::c_int; + fn udev_enumerate_get_list_entry(udev_enumerate: *mut udev_enumerate) -> *mut udev_list_entry; + + fn udev_list_entry_get_next(list_entry: *mut udev_list_entry) -> *mut udev_list_entry; + fn udev_list_entry_get_name(list_entry: *mut udev_list_entry) -> *const c::c_char; + fn udev_list_entry_get_value(list_entry: *mut udev_list_entry) -> *const c::c_char; + + fn udev_device_new_from_syspath(udev: *mut udev, syspath: *const c::c_char) + -> *mut udev_device; + fn udev_device_unref(udev_device: *mut udev_device) -> *mut udev_device; + 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; +} + +#[derive(Debug, Error)] +pub enum UdevError { + #[error("Could not create a new udev instance")] + New(#[source] std::io::Error), + #[error("Could not create a new udev_monitor instance")] + NewMonitor(#[source] std::io::Error), + #[error("Could not create a new udev_enumerate instance")] + NewEnumerate(#[source] std::io::Error), + #[error("Could not enable receiving on a udev_monitor")] + EnableReceiving(#[source] std::io::Error), + #[error("Could not add a match rule to a udev_monitor")] + MonitorAddMatch(#[source] std::io::Error), + #[error("Could not add a match rule to a udev_enumerate")] + EnumerateAddMatch(#[source] std::io::Error), + #[error("Could not list devices of a udev_enumerate")] + EnumerateGetListEntry(#[source] std::io::Error), + #[error("Could not scan devices of a udev_enumerate")] + ScanDevices(#[source] std::io::Error), + #[error("Could not create a udev_device from a syspath")] + DeviceFromSyspath(#[source] std::io::Error), + #[error("Could not retrieve the sysname of a udev_device")] + GetSysname(#[source] std::io::Error), + #[error("Could not retrieve the devnode of a udev_device")] + GetDevnode(#[source] std::io::Error), +} + +pub struct Udev { + udev: *mut udev, +} + +pub struct UdevMonitor { + udev: Rc, + monitor: *mut udev_monitor, +} + +pub struct UdevEnumerate { + udev: Rc, + enumerate: *mut udev_enumerate, +} + +pub struct UdevListEntry<'a> { + list_entry: *mut udev_list_entry, + _phantom: PhantomData<&'a mut ()>, +} + +pub struct UdevDevice { + udev: Rc, + device: *mut udev_device, +} + +impl Udev { + pub fn new() -> Result { + let res = unsafe { udev_new() }; + if res.is_null() { + return Err(UdevError::New(Errno::default().into())); + } + Ok(Self { udev: res }) + } + + pub fn create_monitor(self: &Rc) -> Result { + let res = unsafe { udev_monitor_new_from_netlink(self.udev, "udev\0".as_ptr() as _) }; + if res.is_null() { + return Err(UdevError::NewMonitor(Errno::default().into())); + } + Ok(UdevMonitor { + udev: self.clone(), + monitor: res, + }) + } + + pub fn create_enumerate(self: &Rc) -> Result { + let res = unsafe { udev_enumerate_new(self.udev) }; + if res.is_null() { + return Err(UdevError::NewEnumerate(Errno::default().into())); + } + Ok(UdevEnumerate { + udev: self.clone(), + enumerate: res, + }) + } + + pub fn create_device_from_syspath<'a>( + self: &Rc, + syspath: impl IntoUstr<'a>, + ) -> Result { + let syspath = syspath.into_ustr(); + let res = unsafe { udev_device_new_from_syspath(self.udev, syspath.as_ptr()) }; + if res.is_null() { + return Err(UdevError::DeviceFromSyspath(Errno::default().into())); + } + Ok(UdevDevice { + udev: self.clone(), + device: res, + }) + } +} + +impl Drop for Udev { + fn drop(&mut self) { + unsafe { + udev_unref(self.udev); + } + } +} + +impl UdevMonitor { + pub fn fd(&self) -> c::c_int { + unsafe { udev_monitor_get_fd(self.monitor) } + } + + pub fn enable_receiving(&self) -> Result<(), UdevError> { + let res = unsafe { udev_monitor_enable_receiving(self.monitor) }; + if res < 0 { + Err(UdevError::EnableReceiving(Errno(-res).into())) + } else { + Ok(()) + } + } + + pub fn add_match_subsystem_devtype( + &self, + subsystem: Option<&str>, + devtype: Option<&str>, + ) -> Result<(), UdevError> { + let subsystem = subsystem.map(|s| s.into_ustr()); + let devtype = devtype.map(|s| s.into_ustr()); + let res = unsafe { + udev_monitor_filter_add_match_subsystem_devtype( + self.monitor, + subsystem + .as_ref() + .map(|s| s.as_ptr()) + .unwrap_or(ptr::null()), + devtype.as_ref().map(|s| s.as_ptr()).unwrap_or(ptr::null()), + ) + }; + if res < 0 { + Err(UdevError::MonitorAddMatch(Errno(-res).into())) + } else { + Ok(()) + } + } + + pub fn receive_device(&self) -> Option { + let res = unsafe { + udev_monitor_receive_device(self.monitor) + }; + if res.is_null() { + None + } else { + Some(UdevDevice { + udev: self.udev.clone(), + device: res, + }) + } + } +} + +impl Drop for UdevMonitor { + fn drop(&mut self) { + unsafe { + udev_monitor_unref(self.monitor); + } + } +} + +impl UdevEnumerate { + pub fn add_match_subsystem(&self, subsystem: &str) -> Result<(), UdevError> { + let subsystem = subsystem.into_ustr(); + let res = unsafe { udev_enumerate_add_match_subsystem(self.enumerate, subsystem.as_ptr()) }; + if res < 0 { + Err(UdevError::EnumerateAddMatch(Errno(-res).into())) + } else { + Ok(()) + } + } + + pub fn scan_devices(&self) -> Result<(), UdevError> { + let res = unsafe { udev_enumerate_scan_devices(self.enumerate) }; + if res < 0 { + Err(UdevError::ScanDevices(Errno(-res).into())) + } else { + Ok(()) + } + } + + pub fn get_list_entry(&mut self) -> Result, UdevError> { + let res = unsafe { udev_enumerate_get_list_entry(self.enumerate) }; + if res.is_null() { + let err = Errno::default(); + if err.0 == c::ENODATA { + Ok(None) + } else { + Err(UdevError::EnumerateGetListEntry(err.into())) + } + } else { + Ok(Some(UdevListEntry { + list_entry: res, + _phantom: Default::default(), + })) + } + } +} + +impl Drop for UdevEnumerate { + fn drop(&mut self) { + unsafe { + udev_enumerate_unref(self.enumerate); + } + } +} + +impl<'a> UdevListEntry<'a> { + pub fn next(self) -> Option { + unsafe { + let res = udev_list_entry_get_next(self.list_entry); + if res.is_null() { + None + } else { + Some(Self { + list_entry: res, + _phantom: Default::default(), + }) + } + } + } + + pub fn name(&self) -> &CStr { + unsafe { + let s = udev_list_entry_get_name(self.list_entry); + CStr::from_ptr(s) + } + } +} + +impl UdevDevice { + pub fn sysname(&self) -> Result<&CStr, UdevError> { + let res = unsafe { udev_device_get_sysname(self.device) }; + if res.is_null() { + Err(UdevError::GetSysname(Errno::default().into())) + } else { + unsafe { Ok(CStr::from_ptr(res)) } + } + } + + pub fn devnode(&self) -> Result<&CStr, UdevError> { + let res = unsafe { udev_device_get_devnode(self.device) }; + if res.is_null() { + Err(UdevError::GetDevnode(Errno::default().into())) + } else { + unsafe { Ok(CStr::from_ptr(res)) } + } + } + + pub fn is_initialized(&self) -> bool { + unsafe { udev_device_get_is_initialized(self.device) != 0 } + } +} + +impl Drop for UdevDevice { + fn drop(&mut self) { + unsafe { + udev_device_unref(self.device); + } + } +} diff --git a/src/utils/aligned.rs b/src/utils/aligned.rs deleted file mode 100644 index 4638fbd4..00000000 --- a/src/utils/aligned.rs +++ /dev/null @@ -1,22 +0,0 @@ -use uapi::{Packed, Pod}; - -#[repr(C, align(8))] -#[derive(Copy, Clone, Debug, Eq, PartialEq)] -pub struct AlignedI64(pub i64); - -unsafe impl Pod for AlignedI64 {} -unsafe impl Packed for AlignedI64 {} - -#[repr(C, align(8))] -#[derive(Copy, Clone, Debug, Eq, PartialEq)] -pub struct AlignedU64(pub u64); - -unsafe impl Pod for AlignedU64 {} -unsafe impl Packed for AlignedU64 {} - -#[repr(C, align(8))] -#[derive(Copy, Clone, Debug, PartialEq)] -pub struct AlignedF64(pub f64); - -unsafe impl Pod for AlignedF64 {} -unsafe impl Packed for AlignedF64 {} diff --git a/src/utils/bitfield.rs b/src/utils/bitfield.rs new file mode 100644 index 00000000..d5ad22e1 --- /dev/null +++ b/src/utils/bitfield.rs @@ -0,0 +1,28 @@ +use std::mem; + +const SEG_SIZE: usize = 8 * mem::size_of::(); + +#[derive(Default)] +pub struct Bitfield { + vals: Vec, +} + +impl Bitfield { + pub fn acquire(&mut self) -> u32 { + for (idx, n) in self.vals.iter_mut().enumerate() { + if *n != 0 { + let pos = n.trailing_zeros(); + *n &= !(1 << pos); + return (idx * SEG_SIZE) as u32 + pos; + } + } + self.vals.push(!1); + ((self.vals.len() - 1) * SEG_SIZE) as u32 + } + + pub fn release(&mut self, val: u32) { + let idx = val as usize / SEG_SIZE; + let pos = val as usize % SEG_SIZE; + self.vals[idx] |= 1 << pos; + } +} diff --git a/src/utils/buffd/buf_out.rs b/src/utils/buffd/buf_out.rs index 3d3f7b1a..b063bc74 100644 --- a/src/utils/buffd/buf_out.rs +++ b/src/utils/buffd/buf_out.rs @@ -101,7 +101,9 @@ impl BufFdOut { _ = timeout.as_mut().unwrap() => { return Err(BufFdError::Timeout); }, - res = self.fd.writable().fuse() => res?, + res = self.fd.writable().fuse() => { + res?; + }, } } } diff --git a/src/utils/mod.rs b/src/utils/mod.rs index f01ef01a..1a8e3b5a 100644 --- a/src/utils/mod.rs +++ b/src/utils/mod.rs @@ -1,4 +1,3 @@ -pub mod aligned; pub mod array; pub mod asyncevent; pub mod bitflags; @@ -18,3 +17,4 @@ pub mod stack; pub mod tri; pub mod vec_ext; pub mod vecstorage; +pub mod bitfield; diff --git a/src/xwayland.rs b/src/xwayland.rs index 5e33752f..7b68ce42 100644 --- a/src/xwayland.rs +++ b/src/xwayland.rs @@ -100,7 +100,7 @@ pub async fn manage(state: Rc) { log::info!("Allocated display :{} for Xwayland", xsocket.id); log::info!("Waiting for connection attempt"); let res = XWaylandError::tria(async { - let _ = state.eng.fd(&socket)?.readable().await; + state.eng.fd(&socket)?.readable().await?; Ok(()) }) .await; @@ -168,13 +168,13 @@ async fn run( Ok(c) => c, Err(e) => return Err(XWaylandError::SpawnClient(e)), }; - let _ = state.eng.fd(&Rc::new(dfdread))?.readable().await; + state.eng.fd(&Rc::new(dfdread))?.readable().await?; let wm = match Wm::get(state, client, wm1, queue.clone()) { Ok(w) => w, Err(e) => return Err(XWaylandError::CreateWm(Box::new(e))), }; let wm = state.eng.spawn(wm.run()); - let _ = state.eng.fd(&Rc::new(pidfd))?.readable().await; + state.eng.fd(&Rc::new(pidfd))?.readable().await?; drop(wm); queue.clear(); stderr_read.await; @@ -223,7 +223,10 @@ async fn log_xwayland(state: Rc, stderr: OwnedFd) { let mut buf2 = [0; 128]; let mut done = false; while !done { - let _ = afd.readable().await; + if let Err(e) = afd.readable().await { + log::error!("Cannot wait for the xwayland stderr to become readable: {}", ErrorFmt(e)); + return; + } loop { match uapi::read(afd.raw(), &mut buf2[..]) { Ok(buf2) if buf2.len() > 0 => { diff --git a/src/xwayland/xwm.rs b/src/xwayland/xwm.rs index 0937664d..a7b0d047 100644 --- a/src/xwayland/xwm.rs +++ b/src/xwayland/xwm.rs @@ -247,7 +247,12 @@ impl Wm { return; } futures::select! { - _ = self.socket.readable().fuse() => { }, + res = self.socket.readable().fuse() => { + if let Err(e) = res { + log::error!("Cannot wait for xwm fd to become readable: {}", ErrorFmt(e)); + return; + } + } _ = self.queue.non_empty().fuse() => { }, } } diff --git a/wire-dbus/org.freedesktop.DBus.txt b/wire-dbus/org.freedesktop.DBus.txt index 35f32840..5fee421e 100644 --- a/wire-dbus/org.freedesktop.DBus.txt +++ b/wire-dbus/org.freedesktop.DBus.txt @@ -1,3 +1,7 @@ fn Hello() { name: string, } + +fn AddMatch(rule: string) { } + +fn RemoveMatch(rule: string) { } diff --git a/wire-dbus/org.freedesktop.login1.Manager.txt b/wire-dbus/org.freedesktop.login1.Manager.txt index 52dab994..aa70fd14 100644 --- a/wire-dbus/org.freedesktop.login1.Manager.txt +++ b/wire-dbus/org.freedesktop.login1.Manager.txt @@ -5,3 +5,4 @@ fn GetSession( } prop BootLoaderEntries = array(string) +prop ScheduledShutdown = struct(string, u64) diff --git a/wire-dbus/org.freedesktop.login1.Session.txt b/wire-dbus/org.freedesktop.login1.Session.txt index d24d94db..aa519210 100644 --- a/wire-dbus/org.freedesktop.login1.Session.txt +++ b/wire-dbus/org.freedesktop.login1.Session.txt @@ -8,3 +8,16 @@ fn TakeDevice(major: u32, minor: u32) { fn PauseDeviceComplete(major: u32, minor: u32) { } +prop Seat = struct(string, object_path) + +sig PauseDevice { + major: u32, + minor: u32, + ty: string, +} + +sig ResumeDevice { + major: u32, + minor: u32, + fd: fd, +}