diff --git a/drivers/inputd/src/lib.rs b/drivers/inputd/src/lib.rs index a3026650bc..efe4a9cbc1 100644 --- a/drivers/inputd/src/lib.rs +++ b/drivers/inputd/src/lib.rs @@ -73,6 +73,22 @@ impl ConsumerHandle { Ok(Self(file)) } + /// Open a *raw* device tap (the Linux `evdev` role): the pre-keymap event + /// stream, delivered for every producer event regardless of the active VT. + /// + /// This is what a libinput/xkbcommon consumer (evdevd, and in turn a Wayland + /// compositor) must read — the console keymap is applied only on the plain + /// [`Self::new_vt`] console path, never here. The display/handoff helpers on + /// `ConsumerHandle` do not apply to a raw tap (it owns no VT); only + /// [`Self::read_events`] / [`Self::event_handle`] are meaningful. + pub fn new_raw() -> io::Result { + let file = OpenOptions::new() + .read(true) + .custom_flags(O_NONBLOCK as i32) + .open(format!("/scheme/input/consumer_raw"))?; + Ok(Self(file)) + } + pub fn event_handle(&self) -> BorrowedFd<'_> { self.0.as_fd() } diff --git a/drivers/inputd/src/main.rs b/drivers/inputd/src/main.rs index b0cb882f99..05a6d8b291 100644 --- a/drivers/inputd/src/main.rs +++ b/drivers/inputd/src/main.rs @@ -46,6 +46,18 @@ enum Handle { notified: bool, vt: usize, }, + /// A raw, always-on device tap (the Linux `evdev` role). Unlike `Consumer`, + /// it is NOT bound to a VT and receives the *pre-keymap* event stream for + /// every producer event regardless of which VT is active. This is what a + /// libinput/xkbcommon (Wayland) consumer such as evdevd must read: the + /// console keymap is applied only on the `Consumer` (console) path, so a raw + /// tap here never sees a keymapped `character`, and a background compositor + /// still receives input while a text VT is foregrounded. + ConsumerRaw { + events: EventFlags, + pending: Vec, + notified: bool, + }, Display { events: EventFlags, pending: Vec, @@ -186,6 +198,16 @@ impl SchemeSync for InputScheme { vt, } } + "consumer_raw" => { + // A raw device tap (evdev role). No VT is allocated: it observes + // the whole pre-keymap stream, always, independent of the active + // VT — exactly what libinput/xkbcommon needs. + Handle::ConsumerRaw { + events: EventFlags::empty(), + pending: Vec::new(), + notified: false, + } + } "consumer_bootlog" => { if !self.vts.insert(1) { return Err(SysError::new(EEXIST)); @@ -313,6 +335,16 @@ impl SchemeSync for InputScheme { Ok(copy) } + Handle::ConsumerRaw { pending, .. } => { + // Raw taps never trigger a display handoff (they own no VT); just + // drain whatever pre-keymap event bytes are queued. + 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::() == 0 { let copy = core::cmp::min(pending.len(), buf.len() / size_of::()); @@ -379,6 +411,10 @@ impl SchemeSync for InputScheme { log::error!("consumer tried to write"); return Err(SysError::new(EINVAL)); } + Handle::ConsumerRaw { .. } => { + log::error!("raw consumer tried to write"); + return Err(SysError::new(EINVAL)); + } Handle::Display { .. } => { log::error!("display tried to write"); return Err(SysError::new(EINVAL)); @@ -424,6 +460,19 @@ impl SchemeSync for InputScheme { v }); + // Snapshot the RAW event bytes *before* the console keymap is applied + // below. Raw taps (evdevd → libinput → compositor) must never see a + // keymapped `character`; they consume scancodes directly. Taking the + // copy here — after the aligned `Vec` is built but before the + // stamping loop — gives every raw consumer the exact producer stream. + let raw_buf: Vec = unsafe { + core::slice::from_raw_parts( + events.as_ptr() as *const u8, + events.len() * size_of::(), + ) + } + .to_vec(); + for i in 0..events.len() { let mut new_active_opt = None; match events[i].to_option() { @@ -494,6 +543,19 @@ impl SchemeSync for InputScheme { } } + // Raw taps get the pre-keymap stream unconditionally — no VT gate, and + // even when no VT is active — so a Wayland compositor keeps receiving + // input regardless of which text VT (if any) is foregrounded. + for handle in self.handles.values_mut() { + if let Handle::ConsumerRaw { + pending, notified, .. + } = handle + { + pending.extend_from_slice(&raw_buf); + *notified = false; + } + } + Ok(buf.len()) } @@ -513,6 +575,15 @@ impl SchemeSync for InputScheme { *notified = false; Ok(EventFlags::empty()) } + Handle::ConsumerRaw { + ref mut events, + ref mut notified, + .. + } => { + *events = flags; + *notified = false; + Ok(EventFlags::empty()) + } Handle::Display { ref mut events, ref mut notified, @@ -592,6 +663,22 @@ fn deamon(daemon: daemon::SchemeDaemon) -> anyhow::Result<()> { *notified = true; } + Handle::ConsumerRaw { + 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,