Add kwin full source tree, greeter login, zsh, pcid service, and build system improvements

This commit is contained in:
2026-04-26 22:31:07 +01:00
parent d4a6b356eb
commit 70a84cefee
3416 changed files with 1360518 additions and 10522 deletions
-896
View File
@@ -1,896 +0,0 @@
# P2-inputd.patch
# Extract inputd improvements: named producers, device consumers, hotplug events,
# error handling, and input scheme extensions.
#
# Files: drivers/inputd/src/lib.rs, drivers/inputd/src/main.rs
diff --git a/drivers/inputd/src/lib.rs b/drivers/inputd/src/lib.rs
index b68e8211..f07a411d 100644
--- a/drivers/inputd/src/lib.rs
+++ b/drivers/inputd/src/lib.rs
@@ -64,25 +64,53 @@ impl ConsumerHandle {
let fd = self.0.as_raw_fd();
let written = libredox::call::fpath(fd as usize, &mut buffer)?;
- assert!(written <= buffer.len());
-
- let mut display_path = PathBuf::from(
- std::str::from_utf8(&buffer[..written])
- .expect("init: display path UTF-8 check failed")
- .to_owned(),
- );
- display_path.set_file_name(format!(
- "v2/{}",
- display_path.file_name().unwrap().to_str().unwrap()
- ));
- let display_path = display_path.to_str().unwrap();
+ if written > buffer.len() {
+ return Err(io::Error::new(
+ io::ErrorKind::InvalidData,
+ "inputd: display path exceeded buffer size",
+ ));
+ }
+
+ let path_str = std::str::from_utf8(&buffer[..written]).map_err(|e| {
+ io::Error::new(
+ io::ErrorKind::InvalidData,
+ format!("inputd: display path is not valid UTF-8: {e}"),
+ )
+ })?;
+ let mut display_path = PathBuf::from(path_str.to_owned());
+
+ let file_name = display_path
+ .file_name()
+ .and_then(|n| n.to_str())
+ .ok_or_else(|| {
+ io::Error::new(
+ io::ErrorKind::InvalidData,
+ format!(
+ "inputd: display path has no valid file name: {}",
+ display_path.display()
+ ),
+ )
+ })?;
+ display_path.set_file_name(format!("v2/{file_name}"));
+ let display_path_str = display_path.to_str().ok_or_else(|| {
+ io::Error::new(
+ io::ErrorKind::InvalidData,
+ format!(
+ "inputd: constructed display path is not valid UTF-8: {}",
+ display_path.display()
+ ),
+ )
+ })?;
let display_file =
- libredox::call::open(display_path, (O_CLOEXEC | O_NONBLOCK | O_RDWR) as _, 0)
+ libredox::call::open(display_path_str, (O_CLOEXEC | O_NONBLOCK | O_RDWR) as _, 0)
.map(|socket| unsafe { File::from_raw_fd(socket as RawFd) })
- .unwrap_or_else(|err| {
- panic!("failed to open display {}: {}", display_path, err);
- });
+ .map_err(|err| {
+ io::Error::new(
+ io::ErrorKind::Other,
+ format!("inputd: failed to open display {display_path_str}: {err}"),
+ )
+ })?;
Ok(display_file)
}
@@ -152,8 +180,12 @@ impl DisplayHandle {
if nread == 0 {
Ok(None)
+ } else if nread != size_of::<VtEvent>() {
+ Err(io::Error::new(
+ io::ErrorKind::InvalidData,
+ format!("inputd: partial vt event read: got {nread}, expected {}", size_of::<VtEvent>()),
+ ))
} else {
- assert_eq!(nread, size_of::<VtEvent>());
Ok(Some(event))
}
}
@@ -171,13 +203,11 @@ impl ControlHandle {
Ok(Self(File::open(path)?))
}
- /// Sent to Handle::Display
pub fn activate_vt(&mut self, vt: usize) -> io::Result<usize> {
let cmd = ControlEvent::from(VtActivate { vt });
self.0.write(unsafe { any_as_u8_slice(&cmd) })
}
- /// Sent to Handle::Producer
pub fn activate_keymap(&mut self, keymap: usize) -> io::Result<usize> {
let cmd = ControlEvent::from(KeymapActivate { keymap });
self.0.write(unsafe { any_as_u8_slice(&cmd) })
@@ -209,3 +239,195 @@ impl ProducerHandle {
Ok(())
}
}
+
+pub struct NamedProducerHandle(File);
+
+impl NamedProducerHandle {
+ pub fn new(name: &str) -> io::Result<Self> {
+ let path = format!("/scheme/input/producer/{name}");
+ Ok(Self(File::open(path)?))
+ }
+
+ pub fn write_event(&mut self, event: orbclient::Event) -> io::Result<()> {
+ self.0.write(&event)?;
+ Ok(())
+ }
+}
+
+/// Convenience wrapper that tries a named producer first,
+/// falling back to the legacy anonymous producer on failure.
+pub enum InputProducer {
+ Named(NamedProducerHandle),
+ Legacy(ProducerHandle),
+}
+
+impl InputProducer {
+ /// Open a named producer (`/scheme/input/producer/{name}`).
+ /// If the named path is unavailable, fall back to the legacy
+ /// `/scheme/input/producer` path so the driver keeps working on
+ /// older inputd builds or degraded schemes.
+ pub fn new_named_or_fallback(name: &str) -> io::Result<Self> {
+ match NamedProducerHandle::new(name) {
+ Ok(named) => Ok(InputProducer::Named(named)),
+ Err(named_err) => {
+ log::debug!(
+ "inputd: named producer '{}' unavailable ({}), falling back to legacy",
+ name,
+ named_err
+ );
+ ProducerHandle::new().map(InputProducer::Legacy)
+ }
+ }
+ }
+
+ /// Open the legacy anonymous producer directly.
+ pub fn new_legacy() -> io::Result<Self> {
+ ProducerHandle::new().map(InputProducer::Legacy)
+ }
+
+ pub fn write_event(&mut self, event: orbclient::Event) -> io::Result<()> {
+ match self {
+ InputProducer::Named(h) => h.write_event(event),
+ InputProducer::Legacy(h) => h.write_event(event),
+ }
+ }
+}
+
+pub struct DeviceConsumerHandle(File);
+
+pub enum DeviceConsumerHandleEvent<'a> {
+ Events(&'a [Event]),
+}
+
+impl DeviceConsumerHandle {
+ pub fn new(device_name: &str) -> io::Result<Self> {
+ let path = format!("/scheme/input/{device_name}");
+ Ok(Self(File::open(path)?))
+ }
+
+ pub fn event_handle(&self) -> BorrowedFd<'_> {
+ self.0.as_fd()
+ }
+
+ pub fn read_events<'a>(
+ &self,
+ events: &'a mut [Event],
+ ) -> io::Result<DeviceConsumerHandleEvent<'a>> {
+ match read_to_slice(self.0.as_fd(), events) {
+ Ok(count) => Ok(DeviceConsumerHandleEvent::Events(&events[..count])),
+ Err(err) => Err(err.into()),
+ }
+ }
+}
+
+#[derive(Debug, Clone)]
+#[repr(C)]
+pub struct HotplugEventHeader {
+ pub kind: u32,
+ pub device_id: u32,
+ pub name_len: u32,
+ pub reserved: u32,
+}
+
+#[derive(Debug, Clone)]
+pub struct HotplugEvent {
+ pub kind: u32,
+ pub device_id: u32,
+ pub name: String,
+}
+
+pub struct HotplugHandle {
+ file: File,
+ partial: Vec<u8>,
+}
+
+impl HotplugHandle {
+ pub fn new() -> io::Result<Self> {
+ let file = File::open("/scheme/input/events")?;
+ Ok(Self {
+ file,
+ partial: Vec::new(),
+ })
+ }
+
+ pub fn event_handle(&self) -> BorrowedFd<'_> {
+ self.file.as_fd()
+ }
+
+ pub fn read_event(&mut self) -> io::Result<Option<HotplugEvent>> {
+ let mut tmp = [0u8; 256];
+ match self.file.read(&mut tmp) {
+ Ok(0) => {}
+ Ok(n) => self.partial.extend_from_slice(&tmp[..n]),
+ Err(ref e) if e.kind() == io::ErrorKind::WouldBlock => {}
+ Err(e) => return Err(e),
+ }
+
+ if self.partial.len() < 16 {
+ return Ok(None);
+ }
+
+ let header = HotplugEventHeader {
+ kind: u32::from_ne_bytes(self.partial[0..4].try_into().map_err(|_| {
+ io::Error::new(io::ErrorKind::InvalidData, "header parse failed")
+ })?),
+ device_id: u32::from_ne_bytes(self.partial[4..8].try_into().map_err(|_| {
+ io::Error::new(io::ErrorKind::InvalidData, "header parse failed")
+ })?),
+ name_len: u32::from_ne_bytes(self.partial[8..12].try_into().map_err(|_| {
+ io::Error::new(io::ErrorKind::InvalidData, "header parse failed")
+ })?),
+ reserved: 0,
+ };
+
+ let total_len = 16 + header.name_len as usize;
+ if self.partial.len() < total_len {
+ return Ok(None);
+ }
+
+ let name = String::from_utf8(self.partial[16..total_len].to_vec()).map_err(|e| {
+ io::Error::new(io::ErrorKind::InvalidData, format!("invalid UTF-8: {e}"))
+ })?;
+
+ self.partial.drain(..total_len);
+
+ Ok(Some(HotplugEvent {
+ kind: header.kind,
+ device_id: header.device_id,
+ name,
+ }))
+ }
+}
+
+pub const RESERVED_DEVICE_NAMES: &[&str] = &[
+ "producer",
+ "consumer",
+ "consumer_bootlog",
+ "events",
+ "handle",
+ "handle_early",
+ "control",
+];
+
+pub struct InputDeviceLister;
+
+impl InputDeviceLister {
+ pub fn list() -> io::Result<Vec<String>> {
+ let mut dir = std::fs::read_dir("/scheme/input/")?;
+ let mut devices = Vec::new();
+ loop {
+ match dir.next() {
+ Some(Ok(entry)) => {
+ if let Some(name) = entry.file_name().to_str() {
+ if !RESERVED_DEVICE_NAMES.contains(&name) {
+ devices.push(name.to_owned());
+ }
+ }
+ }
+ Some(Err(e)) => return Err(e),
+ None => break,
+ }
+ }
+ Ok(devices)
+ }
+}
diff --git a/drivers/inputd/src/main.rs b/drivers/inputd/src/main.rs
index 07aa943e..89018568 100644
--- a/drivers/inputd/src/main.rs
+++ b/drivers/inputd/src/main.rs
@@ -13,7 +13,7 @@
use core::mem::size_of;
use std::borrow::Cow;
-use std::collections::BTreeSet;
+use std::collections::{BTreeMap, BTreeSet};
use std::mem::transmute;
use std::ops::ControlFlow;
use std::sync::atomic::{AtomicUsize, Ordering};
@@ -26,8 +26,9 @@ use redox_scheme::{CallerCtx, OpenResult, Response, SignalBehavior, Socket};
use orbclient::{Event, EventOption};
use scheme_utils::{Blocking, FpathWriter, HandleMap};
+use syscall::dirent::{DirEntry, DirentBuf, DirentKind};
use syscall::schemev2::NewFdFlags;
-use syscall::{Error as SysError, EventFlags, EACCES, EBADF, EEXIST, EINVAL};
+use syscall::{Error as SysError, EventFlags, EACCES, EBADF, EEXIST, EINVAL, ENOENT, ENOTDIR};
pub mod keymap;
@@ -35,8 +36,57 @@ use keymap::KeymapKind;
use crate::keymap::KeymapData;
+const DEVICE_ADD: u32 = 1;
+const DEVICE_REMOVE: u32 = 2;
+
+fn validate_producer_name(name: &str) -> Result<(), SysError> {
+ if name.is_empty() || name.contains('/') {
+ return Err(SysError::new(EINVAL));
+ }
+ if inputd::RESERVED_DEVICE_NAMES.contains(&name) {
+ return Err(SysError::new(EINVAL));
+ }
+ Ok(())
+}
+
+fn serialize_hotplug(kind: u32, device_id: u32, name: &str) -> Vec<u8> {
+ let name_bytes = name.as_bytes();
+ let header = HotplugHeader {
+ kind,
+ device_id,
+ name_len: name_bytes.len() as u32,
+ _reserved: 0,
+ };
+ let mut out = Vec::with_capacity(16 + name_bytes.len());
+ out.extend_from_slice(&header.to_bytes());
+ out.extend_from_slice(name_bytes);
+ out
+}
+
+#[repr(C)]
+struct HotplugHeader {
+ kind: u32,
+ device_id: u32,
+ name_len: u32,
+ _reserved: u32,
+}
+
+impl HotplugHeader {
+ fn to_bytes(&self) -> [u8; 16] {
+ let mut buf = [0u8; 16];
+ buf[0..4].copy_from_slice(&self.kind.to_ne_bytes());
+ buf[4..8].copy_from_slice(&self.device_id.to_ne_bytes());
+ buf[8..12].copy_from_slice(&self.name_len.to_ne_bytes());
+ buf[12..16].copy_from_slice(&self._reserved.to_ne_bytes());
+ buf
+ }
+}
+
enum Handle {
Producer,
+ NamedProducer {
+ name: String,
+ },
Consumer {
events: EventFlags,
pending: Vec<u8>,
@@ -46,6 +96,17 @@ enum Handle {
notified: bool,
vt: usize,
},
+ DeviceConsumer {
+ device_name: String,
+ events: EventFlags,
+ pending: Vec<u8>,
+ notified: bool,
+ },
+ HotplugEvents {
+ events: EventFlags,
+ pending: Vec<u8>,
+ notified: bool,
+ },
Display {
events: EventFlags,
pending: Vec<VtEvent>,
@@ -72,6 +133,9 @@ struct InputScheme {
rshift: bool,
has_new_events: bool,
+
+ devices: BTreeMap<String, u32>,
+ next_device_id: AtomicUsize,
}
impl InputScheme {
@@ -90,9 +154,28 @@ impl InputScheme {
lshift: false,
rshift: false,
has_new_events: false,
+
+ devices: BTreeMap::new(),
+ next_device_id: AtomicUsize::new(1),
}
}
+ fn emit_hotplug(&mut self, kind: u32, device_id: u32, name: &str) {
+ let record = serialize_hotplug(kind, device_id, name);
+ for handle in self.handles.values_mut() {
+ if let Handle::HotplugEvents {
+ pending,
+ notified,
+ ..
+ } = handle
+ {
+ pending.extend_from_slice(&record);
+ *notified = false;
+ }
+ }
+ self.has_new_events = true;
+ }
+
fn switch_vt(&mut self, new_active: usize) {
if let Some(active_vt) = self.active_vt {
if new_active == active_vt {
@@ -146,6 +229,43 @@ impl InputScheme {
self.active_keymap = KeymapData::new(new_active.into());
}
+
+ fn deliver_to_legacy_consumers(&mut self, buf: &[u8]) {
+ if let Some(active_vt) = self.active_vt {
+ for handle in self.handles.values_mut() {
+ if let Handle::Consumer {
+ pending,
+ notified,
+ vt,
+ ..
+ } = handle
+ {
+ if *vt != active_vt {
+ continue;
+ }
+ pending.extend_from_slice(buf);
+ *notified = false;
+ }
+ }
+ }
+ }
+
+ fn deliver_to_device_consumers(&mut self, name: &str, buf: &[u8]) {
+ for handle in self.handles.values_mut() {
+ if let Handle::DeviceConsumer {
+ device_name,
+ pending,
+ notified,
+ ..
+ } = handle
+ {
+ if device_name == name {
+ pending.extend_from_slice(buf);
+ *notified = false;
+ }
+ }
+ }
+ }
}
impl SchemeSync for InputScheme {
@@ -170,7 +290,23 @@ impl SchemeSync for InputScheme {
let command = path_parts.next().ok_or(SysError::new(EINVAL))?;
let handle_ty = match command {
- "producer" => Handle::Producer,
+ "producer" => {
+ if let Some(name) = path_parts.next() {
+ validate_producer_name(name)?;
+ if self.devices.contains_key(name) {
+ return Err(SysError::new(EEXIST));
+ }
+ let device_id = self.next_device_id.fetch_add(1, Ordering::SeqCst) as u32;
+ self.devices.insert(name.to_owned(), device_id);
+ let handle = Handle::NamedProducer {
+ name: name.to_owned(),
+ };
+ self.emit_hotplug(DEVICE_ADD, device_id, name);
+ handle
+ } else {
+ Handle::Producer
+ }
+ }
"consumer" => {
let vt = self.next_vt_id.fetch_add(1, Ordering::Relaxed);
self.vts.insert(vt);
@@ -253,9 +389,23 @@ impl SchemeSync for InputScheme {
}
"control" => Handle::Control,
- _ => {
- log::error!("invalid path '{path}'");
- return Err(SysError::new(EINVAL));
+ "events" => Handle::HotplugEvents {
+ events: EventFlags::empty(),
+ pending: Vec::new(),
+ notified: false,
+ },
+
+ // dynamic device consumer: must be a currently registered device
+ name => {
+ if !self.devices.contains_key(name) {
+ return Err(SysError::new(ENOENT));
+ }
+ Handle::DeviceConsumer {
+ device_name: name.to_owned(),
+ events: EventFlags::empty(),
+ pending: Vec::new(),
+ notified: false,
+ }
}
};
@@ -274,7 +424,7 @@ impl SchemeSync for InputScheme {
let handle = self.handles.get(id)?;
if let Handle::Consumer { vt, .. } = handle {
- write!(w, "{vt}").unwrap();
+ write!(w, "{vt}").map_err(|_| SysError::new(EINVAL))?;
Ok(())
} else {
Err(SysError::new(EINVAL))
@@ -282,6 +432,50 @@ impl SchemeSync for InputScheme {
})
}
+ fn getdents<'buf>(
+ &mut self,
+ id: usize,
+ mut buf: DirentBuf<&'buf mut [u8]>,
+ opaque_offset: u64,
+ ) -> syscall::Result<DirentBuf<&'buf mut [u8]>> {
+ let handle = self.handles.get(id)?;
+ if !matches!(handle, Handle::SchemeRoot) {
+ return Err(SysError::new(ENOTDIR));
+ }
+
+ let static_entries: &[&str] = &[
+ "producer",
+ "consumer",
+ "consumer_bootlog",
+ "events",
+ "handle",
+ "handle_early",
+ "control",
+ ];
+
+ let device_names: Vec<&str> = self.devices.keys().map(|s| s.as_str()).collect();
+
+ let all_entries: Vec<(&str, DirentKind)> = static_entries
+ .iter()
+ .map(|&name| (name, DirentKind::Directory))
+ .chain(device_names.iter().map(|&name| (name, DirentKind::Unspecified)))
+ .collect();
+
+ for (idx, (name, kind)) in all_entries
+ .iter()
+ .enumerate()
+ .skip(opaque_offset as usize)
+ {
+ buf.entry(DirEntry {
+ inode: 0,
+ next_opaque_id: idx as u64 + 1,
+ name,
+ kind: *kind,
+ })?;
+ }
+ Ok(buf)
+ }
+
fn read(
&mut self,
id: usize,
@@ -313,6 +507,22 @@ impl SchemeSync for InputScheme {
Ok(copy)
}
+ Handle::DeviceConsumer { pending, .. } => {
+ let copy = core::cmp::min(pending.len(), buf.len());
+ for (i, byte) in pending.drain(..copy).enumerate() {
+ buf[i] = byte;
+ }
+ Ok(copy)
+ }
+
+ Handle::HotplugEvents { pending, .. } => {
+ let copy = core::cmp::min(pending.len(), buf.len());
+ for (i, byte) in pending.drain(..copy).enumerate() {
+ buf[i] = byte;
+ }
+ Ok(copy)
+ }
+
Handle::Display { pending, .. } => {
if buf.len() % size_of::<VtEvent>() == 0 {
let copy = core::cmp::min(pending.len(), buf.len() / size_of::<VtEvent>());
@@ -334,6 +544,10 @@ impl SchemeSync for InputScheme {
log::error!("producer tried to read");
return Err(SysError::new(EINVAL));
}
+ Handle::NamedProducer { .. } => {
+ log::error!("named producer tried to read");
+ return Err(SysError::new(EINVAL));
+ }
Handle::Control => {
log::error!("control tried to read");
return Err(SysError::new(EINVAL));
@@ -379,11 +593,20 @@ impl SchemeSync for InputScheme {
log::error!("consumer tried to write");
return Err(SysError::new(EINVAL));
}
+ Handle::DeviceConsumer { .. } => {
+ log::error!("device consumer tried to write");
+ return Err(SysError::new(EINVAL));
+ }
+ Handle::HotplugEvents { .. } => {
+ log::error!("hotplug events tried to write");
+ return Err(SysError::new(EINVAL));
+ }
Handle::Display { .. } => {
log::error!("display tried to write");
return Err(SysError::new(EINVAL));
}
Handle::Producer => {}
+ Handle::NamedProducer { .. } => {}
Handle::SchemeRoot => return Err(SysError::new(EBADF)),
}
@@ -397,6 +620,11 @@ impl SchemeSync for InputScheme {
buf.len() / size_of::<Event>(),
)
});
+ let producer_name = match self.handles.get(id)? {
+ Handle::NamedProducer { ref name } => Some(name.clone()),
+ Handle::Producer => None,
+ _ => return Err(SysError::new(EBADF)),
+ };
for i in 0..events.len() {
let mut new_active_opt = None;
@@ -437,38 +665,21 @@ impl SchemeSync for InputScheme {
}
}
- let handle = self.handles.get_mut(id)?;
- assert!(matches!(handle, Handle::Producer));
-
- let buf = unsafe {
+ let serialized = unsafe {
core::slice::from_raw_parts(
(events.as_ptr()) as *const u8,
events.len() * size_of::<Event>(),
)
};
- if let Some(active_vt) = self.active_vt {
- for handle in self.handles.values_mut() {
- match handle {
- Handle::Consumer {
- pending,
- notified,
- vt,
- ..
- } => {
- if *vt != active_vt {
- continue;
- }
-
- pending.extend_from_slice(buf);
- *notified = false;
- }
- _ => continue,
- }
- }
+ if let Some(ref name) = producer_name {
+ self.deliver_to_device_consumers(name, serialized);
}
- Ok(buf.len())
+ // named producers also feed the legacy path; legacy producers only feed legacy
+ self.deliver_to_legacy_consumers(serialized);
+
+ Ok(serialized.len())
}
fn fevent(
@@ -487,6 +698,24 @@ impl SchemeSync for InputScheme {
*notified = false;
Ok(EventFlags::empty())
}
+ Handle::DeviceConsumer {
+ ref mut events,
+ ref mut notified,
+ ..
+ } => {
+ *events = flags;
+ *notified = false;
+ Ok(EventFlags::empty())
+ }
+ Handle::HotplugEvents {
+ ref mut events,
+ ref mut notified,
+ ..
+ } => {
+ *events = flags;
+ *notified = false;
+ Ok(EventFlags::empty())
+ }
Handle::Display {
ref mut events,
ref mut notified,
@@ -496,7 +725,7 @@ impl SchemeSync for InputScheme {
*notified = false;
Ok(EventFlags::empty())
}
- Handle::Producer | Handle::Control => {
+ Handle::Producer | Handle::NamedProducer { .. } | Handle::Control => {
log::error!("producer or control tried to use an event queue");
Err(SysError::new(EINVAL))
}
@@ -505,8 +734,8 @@ impl SchemeSync for InputScheme {
}
fn on_close(&mut self, id: usize) {
- match self.handles.remove(id).unwrap() {
- Handle::Consumer { vt, .. } => {
+ match self.handles.remove(id) {
+ Some(Handle::Consumer { vt, .. }) => {
self.vts.remove(&vt);
if self.active_vt == Some(vt) {
if let Some(&new_vt) = self.vts.last() {
@@ -516,7 +745,15 @@ impl SchemeSync for InputScheme {
}
}
}
- _ => {}
+ Some(Handle::NamedProducer { name, .. }) => {
+ if let Some(device_id) = self.devices.remove(&name) {
+ self.emit_hotplug(DEVICE_REMOVE, device_id, &name);
+ }
+ }
+ Some(_) => {}
+ None => {
+ log::warn!("inputd: on_close called with unknown handle id {id}");
+ }
}
}
}
@@ -564,6 +801,39 @@ fn deamon(daemon: daemon::SchemeDaemon) -> anyhow::Result<()> {
*notified = true;
}
+ Handle::DeviceConsumer {
+ events,
+ pending,
+ ref mut notified,
+ ..
+ } => {
+ if pending.is_empty() || *notified || !events.contains(EventFlags::EVENT_READ) {
+ continue;
+ }
+
+ socket_file.write_response(
+ Response::post_fevent(*id, EventFlags::EVENT_READ.bits()),
+ SignalBehavior::Restart,
+ )?;
+
+ *notified = true;
+ }
+ Handle::HotplugEvents {
+ events,
+ pending,
+ ref mut notified,
+ } => {
+ if pending.is_empty() || *notified || !events.contains(EventFlags::EVENT_READ) {
+ continue;
+ }
+
+ socket_file.write_response(
+ Response::post_fevent(*id, EventFlags::EVENT_READ.bits()),
+ SignalBehavior::Restart,
+ )?;
+
+ *notified = true;
+ }
Handle::Display {
events,
pending,
@@ -589,8 +859,11 @@ fn deamon(daemon: daemon::SchemeDaemon) -> anyhow::Result<()> {
}
fn daemon_runner(daemon: daemon::SchemeDaemon) -> ! {
- deamon(daemon).unwrap();
- unreachable!();
+ if let Err(err) = deamon(daemon) {
+ log::error!("inputd: scheme daemon failed: {err}");
+ std::process::exit(1);
+ }
+ unreachable!()
}
const HELP: &str = r#"
@@ -608,13 +881,26 @@ fn main() {
match val.as_ref() {
// Activates a VT.
"-A" => {
- let vt = args.next().unwrap().parse::<usize>().unwrap();
+ let vt_str = args.next().unwrap_or_else(|| {
+ eprintln!("inputd: -A requires a VT number argument");
+ std::process::exit(1);
+ });
+ let vt = vt_str.parse::<usize>().unwrap_or_else(|_| {
+ eprintln!("inputd: invalid VT number: {vt_str}");
+ std::process::exit(1);
+ });
- let mut handle =
- inputd::ControlHandle::new().expect("inputd: failed to open control handle");
- handle
- .activate_vt(vt)
- .expect("inputd: failed to activate VT");
+ let mut handle = match inputd::ControlHandle::new() {
+ Ok(h) => h,
+ Err(e) => {
+ eprintln!("inputd: failed to open control handle: {e}");
+ std::process::exit(1);
+ }
+ };
+ if let Err(e) = handle.activate_vt(vt) {
+ eprintln!("inputd: failed to activate VT {vt}: {e}");
+ std::process::exit(1);
+ }
}
// Activates a keymap.
"-K" => {
@@ -630,11 +916,17 @@ fn main() {
std::process::exit(1);
});
- let mut handle =
- inputd::ControlHandle::new().expect("inputd: failed to open control handle");
- handle
- .activate_keymap(vt as usize)
- .expect("inputd: failed to activate keymap");
+ let mut handle = match inputd::ControlHandle::new() {
+ Ok(h) => h,
+ Err(e) => {
+ eprintln!("inputd: failed to open control handle: {e}");
+ std::process::exit(1);
+ }
+ };
+ if let Err(e) = handle.activate_keymap(vt as usize) {
+ eprintln!("inputd: failed to activate keymap: {e}");
+ std::process::exit(1);
+ }
}
// List available keymaps
"--keymaps" => {
@@ -647,7 +939,10 @@ fn main() {
println!("{}", HELP);
}
- _ => panic!("inputd: invalid argument: {}", val),
+ _ => {
+ eprintln!("inputd: invalid argument: {val}");
+ std::process::exit(1);
+ }
}
} else {
common::setup_logging(