feat(pointer): virtio tablet as 2nd input dev (driver per-instance VAs, abs/btn decode), input server serves 2 devices on one notif; display software cursor, bar click switches window, presses/releases to front window in its px; k-ui hit-tests (press focuses, release on same presses, field only focuses)

This commit is contained in:
2026-10-28 16:50:20 +01:00
parent 0de13f124d
commit 1401af3d82
22 changed files with 545 additions and 46 deletions

View File

@@ -34,9 +34,9 @@ impl Decode for InputRequest {
const EV_KEY_DOWN: usize = 1;
const EV_KEY_UP: usize = 2;
const EV_PTR_DOWN: usize = 3;
const EV_PTR_MOVE: usize = 4;
const EV_PTR_UP: usize = 5;
const EV_PTR_DOWN: usize = types::input::event::POINTER_DOWN;
const EV_PTR_MOVE: usize = types::input::event::POINTER_MOVE;
const EV_PTR_UP: usize = types::input::event::POINTER_UP;
const EV_SCROLL: usize = 6;
const EV_GAMEPAD_BTN: usize = 7;
const EV_GAMEPAD_AXIS: usize = 8;

View File

@@ -4,13 +4,39 @@ use port_input_device::{InputDevice, InputEvent};
use types::bounded::CapSlot;
use crate::protocol::{InputRequest, InputResponse};
/// Events captured on interrupts and not yet asked for.
const BUFFERED: usize = 32;
/// Events captured on interrupts and not yet asked for (a tablet sends
/// many).
pub const BUFFERED: usize = 64;
/// Devices served at once (a keyboard and a tablet).
pub const MAX_DEVICES: usize = 2;
/// Two devices as one: events from either, the first's first.
pub struct Pair<A, B> {
pub first: A,
pub second: Option<B>,
}
impl<A: InputDevice, B: InputDevice> InputDevice for Pair<A, B> {
fn poll(&mut self) -> Option<InputEvent> {
self.first.poll().or_else(|| self.second.as_mut()?.poll())
}
fn has_event(&self) -> bool {
self.first.has_event() || self.second.as_ref().is_some_and(|d| d.has_event())
}
fn acknowledge(&mut self) {
self.first.acknowledge();
if let Some(d) = self.second.as_mut() {
d.acknowledge();
}
}
}
pub struct InputServer<D: InputDevice> {
device: D,
/// The IRQ to re-arm after each interrupt, if the device has one.
irq: Option<CapSlot>,
/// The IRQs to re-arm after each interrupt, those the devices have.
irqs: [Option<CapSlot>; MAX_DEVICES],
events: [Option<InputEvent>; BUFFERED],
head: usize,
len: usize,
@@ -21,14 +47,14 @@ pub struct InputServer<D: InputDevice> {
impl<D: InputDevice> InputServer<D> {
/// A server that reads the device only when a client asks.
pub fn new(device: D) -> Self {
Self { device, irq: None, events: [None; BUFFERED], head: 0, len: 0, subscriber: None }
Self { device, irqs: [None; MAX_DEVICES], events: [None; BUFFERED], head: 0, len: 0, subscriber: None }
}
/// A server woken by `irq` (routed and bound by the caller): it drains
/// the device into a buffer on every interrupt, so events that arrive
/// between requests are kept.
pub fn with_irq(device: D, irq: CapSlot) -> Self {
Self { irq: Some(irq), ..Self::new(device) }
/// A server woken by `irqs` (routed and bound by the caller): it
/// drains the devices into a buffer on every interrupt, so events that
/// arrive between requests are kept.
pub fn with_irqs(device: D, irqs: [Option<CapSlot>; MAX_DEVICES]) -> Self {
Self { irqs, ..Self::new(device) }
}
fn push(&mut self, event: InputEvent) {
@@ -109,7 +135,7 @@ impl<D: InputDevice> Server for InputServer<D> {
let _ = libkos::syscall::signal(subscriber);
}
self.device.acknowledge();
if let Some(irq) = self.irq {
for irq in self.irqs.into_iter().flatten() {
let _ = libkos::syscall::ack_interrupt(irq);
}
}

View File

@@ -55,7 +55,7 @@ fn an_interrupt_buffers_events_until_asked() {
let mut device = MockInputDevice::new();
device.push_event(InputEvent::KeyDown(Key::Up));
device.push_event(InputEvent::KeyDown(Key::Down));
let mut server = InputServer::with_irq(device, types::bounded::CapSlot::new(1).unwrap());
let mut server = InputServer::with_irqs(device, [types::bounded::CapSlot::new(1), None]);
server.signal(1);
// The device itself is now drained; the events wait in the server.
assert!(!port_input_device::InputDevice::has_event(server.device()));
@@ -72,9 +72,10 @@ fn an_interrupt_buffers_events_until_asked() {
#[test]
fn a_full_buffer_drops_the_oldest_event() {
let mut server = InputServer::with_irq(MockInputDevice::new(), types::bounded::CapSlot::new(1).unwrap());
// 40 interrupts, one key each; the buffer keeps the last 32.
for i in 0..40u8 {
let mut server = InputServer::with_irqs(MockInputDevice::new(), [types::bounded::CapSlot::new(1), None]);
// More interrupts than the buffer holds, one key each: it keeps the last.
let extra = 8;
for i in 0..(crate::server::BUFFERED + extra) as u8 {
let mut device = MockInputDevice::new();
device.push_event(InputEvent::KeyDown(Key::Char(i)));
server.replace_device_for_test(device);
@@ -82,7 +83,7 @@ fn a_full_buffer_drops_the_oldest_event() {
}
assert_eq!(
server.handle(InputRequest::PollEvent),
InputResponse::Event(InputEvent::KeyDown(Key::Char(8)))
InputResponse::Event(InputEvent::KeyDown(Key::Char(extra as u8)))
);
}