Add kwin full source tree, greeter login, zsh, pcid service, and build system improvements
This commit is contained in:
@@ -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(
|
||||
|
||||
Reference in New Issue
Block a user