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:
@@ -2,6 +2,7 @@
|
||||
#![forbid(unsafe_code)]
|
||||
|
||||
mod keymap;
|
||||
mod pointer;
|
||||
|
||||
use core::sync::atomic::{Ordering, fence};
|
||||
use hw_virtio::mmio::{MmioAccess, Registers};
|
||||
@@ -11,10 +12,16 @@ use types::address::{ByteOffset, PhysicalAddress, VirtualAddress};
|
||||
use types::bounded::PageCount;
|
||||
|
||||
/// Drivers map their devices in the device window (0x01D0_0000 up, 64 KiB
|
||||
/// each), clear of program images and heaps.
|
||||
const INPUT_MMIO_VA: VirtualAddress = VirtualAddress::from_raw(0x01D1_0000);
|
||||
const VQUEUE_VA: VirtualAddress = VirtualAddress::from_raw(0x01D1_1000);
|
||||
const EVBUF_VA: VirtualAddress = VirtualAddress::from_raw(0x01D1_3000);
|
||||
/// each), clear of program images and heaps; an input device takes 16 KiB
|
||||
/// of it, the `n`th from here (a keyboard, a tablet).
|
||||
const INPUT_VA: usize = 0x01D1_0000;
|
||||
const INPUT_SPAN: usize = 0x4000;
|
||||
/// Its registers, its queue's rings (two pages), its event buffers.
|
||||
const MMIO_AT: usize = 0;
|
||||
const VQUEUE_AT: usize = 0x1000;
|
||||
const EVBUF_AT: usize = 0x3000;
|
||||
/// How many may be driven at once in one program.
|
||||
pub const MAX_INPUTS: usize = 4;
|
||||
|
||||
const QUEUE_SIZE: usize = 32;
|
||||
const EVENT_SIZE: usize = 8;
|
||||
@@ -37,20 +44,27 @@ pub struct VirtioInputDevice {
|
||||
shift: bool,
|
||||
ctrl: bool,
|
||||
alt: bool,
|
||||
/// Where a tablet's pointer is, between its reports.
|
||||
pointer: pointer::Pointer,
|
||||
}
|
||||
|
||||
impl VirtioInputDevice {
|
||||
/// Takes over the virtio input device behind `mmio`. `None` if it is
|
||||
/// not one or any step fails.
|
||||
pub fn try_new(mmio: types::bounded::CapSlot) -> Option<Self> {
|
||||
let regs = Registers::new(libkos::device::map_mmio(mmio, INPUT_MMIO_VA).ok()?);
|
||||
/// Takes over the virtio input device behind `mmio`, as this program's
|
||||
/// `n`th (each has addresses of its own). `None` if it is not one or
|
||||
/// any step fails.
|
||||
pub fn try_new(mmio: types::bounded::CapSlot, n: usize) -> Option<Self> {
|
||||
if n >= MAX_INPUTS {
|
||||
return None;
|
||||
}
|
||||
let at = |offset: usize| VirtualAddress::from_raw(INPUT_VA + n * INPUT_SPAN + offset);
|
||||
let regs = Registers::new(libkos::device::map_mmio(mmio, at(MMIO_AT)).ok()?);
|
||||
if regs.read(hw_virtio::mmio::REG_MAGIC) != hw_virtio::mmio::MAGIC
|
||||
|| regs.read(hw_virtio::mmio::REG_DEVICE_ID) != hw_virtio::mmio::DEVICE_ID_INPUT
|
||||
{
|
||||
return None;
|
||||
}
|
||||
let rings = libkos::device::dma(PageCount::new(2)?, VQUEUE_VA).ok()?;
|
||||
let events = libkos::device::dma(PageCount::new(1)?, EVBUF_VA).ok()?;
|
||||
let rings = libkos::device::dma(PageCount::new(2)?, at(VQUEUE_AT)).ok()?;
|
||||
let events = libkos::device::dma(PageCount::new(1)?, at(EVBUF_AT)).ok()?;
|
||||
hw_virtio::mmio::init_legacy_device(®s, rings.physical, QUEUE_SIZE as u32);
|
||||
|
||||
let mut dev = Self {
|
||||
@@ -63,6 +77,7 @@ impl VirtioInputDevice {
|
||||
shift: false,
|
||||
ctrl: false,
|
||||
alt: false,
|
||||
pointer: pointer::Pointer::default(),
|
||||
};
|
||||
dev.fill_queue();
|
||||
Some(dev)
|
||||
@@ -123,6 +138,9 @@ impl InputDevice for VirtioInputDevice {
|
||||
let (ev_type, ev_code, ev_value) = self.read_event(desc_idx);
|
||||
self.resubmit(desc_idx);
|
||||
|
||||
if let Some(event) = self.pointer.event(ev_type, ev_code, ev_value) {
|
||||
return Some(event);
|
||||
}
|
||||
if ev_type == EV_KEY && matches!(ev_code, keymap::LEFT_SHIFT | keymap::RIGHT_SHIFT) {
|
||||
self.shift = ev_value != 0;
|
||||
continue;
|
||||
|
||||
79
crates/drivers/input/virtio/src/pointer.rs
Normal file
79
crates/drivers/input/virtio/src/pointer.rs
Normal file
@@ -0,0 +1,79 @@
|
||||
//! A tablet's events (Linux codes, as virtio-input reports them) to
|
||||
//! pointer events: absolute axes collect until the report's sync, then a
|
||||
//! move; buttons press and let go where the pointer is. Positions are
|
||||
//! fractions of the screen, 0 to 65535 each way.
|
||||
|
||||
use port_input_device::InputEvent;
|
||||
|
||||
pub const EV_SYN: u16 = 0;
|
||||
pub const EV_ABS: u16 = 3;
|
||||
const ABS_X: u16 = 0;
|
||||
const ABS_Y: u16 = 1;
|
||||
const BTN_LEFT: u16 = 0x110;
|
||||
const BTN_RIGHT: u16 = 0x111;
|
||||
/// QEMU's tablet reports 0 to this on each axis.
|
||||
const ABS_MAX: u32 = 0x7fff;
|
||||
|
||||
/// Button ids in pointer events.
|
||||
pub const LEFT: u8 = 0;
|
||||
pub const RIGHT: u8 = 1;
|
||||
|
||||
#[derive(Default)]
|
||||
pub struct Pointer {
|
||||
x: u16,
|
||||
y: u16,
|
||||
/// An axis changed since the last sync.
|
||||
moved: bool,
|
||||
}
|
||||
|
||||
fn scaled(v: u32) -> u16 {
|
||||
(v.min(ABS_MAX) * 0xffff / ABS_MAX) as u16
|
||||
}
|
||||
|
||||
impl Pointer {
|
||||
/// What one event of `kind`, `code`, `value` means, if anything yet.
|
||||
pub fn event(&mut self, kind: u16, code: u16, value: u32) -> Option<InputEvent> {
|
||||
let (x, y) = (self.x, self.y);
|
||||
match (kind, code) {
|
||||
(EV_ABS, ABS_X) => {
|
||||
self.x = scaled(value);
|
||||
self.moved = true;
|
||||
None
|
||||
}
|
||||
(EV_ABS, ABS_Y) => {
|
||||
self.y = scaled(value);
|
||||
self.moved = true;
|
||||
None
|
||||
}
|
||||
(EV_SYN, _) if self.moved => {
|
||||
self.moved = false;
|
||||
Some(InputEvent::PointerMove { id: LEFT, x, y })
|
||||
}
|
||||
(super::EV_KEY, BTN_LEFT | BTN_RIGHT) => {
|
||||
let id = if code == BTN_LEFT { LEFT } else { RIGHT };
|
||||
match value {
|
||||
0 => Some(InputEvent::PointerUp { id, x, y }),
|
||||
1 => Some(InputEvent::PointerDown { id, x, y }),
|
||||
_ => None,
|
||||
}
|
||||
}
|
||||
_ => None,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(test)]
|
||||
mod tests {
|
||||
use super::*;
|
||||
|
||||
#[test]
|
||||
fn axes_move_on_sync_and_buttons_press_where_it_is() {
|
||||
let mut p = Pointer::default();
|
||||
assert_eq!(p.event(EV_ABS, ABS_X, 0x7fff), None);
|
||||
assert_eq!(p.event(EV_ABS, ABS_Y, 0x4000), None);
|
||||
assert_eq!(p.event(EV_SYN, 0, 0), Some(InputEvent::PointerMove { id: LEFT, x: 0xffff, y: 0x8000 }));
|
||||
assert_eq!(p.event(EV_SYN, 0, 0), None, "nothing moved since");
|
||||
assert_eq!(p.event(super::super::EV_KEY, BTN_LEFT, 1), Some(InputEvent::PointerDown { id: LEFT, x: 0xffff, y: 0x8000 }));
|
||||
assert_eq!(p.event(super::super::EV_KEY, BTN_RIGHT, 0), Some(InputEvent::PointerUp { id: RIGHT, x: 0xffff, y: 0x8000 }));
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user