117 lines
3.7 KiB
Rust
117 lines
3.7 KiB
Rust
use server_framework::{Decode, Encode, MALFORMED, Server};
|
|
|
|
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;
|
|
|
|
pub struct InputServer<D: InputDevice> {
|
|
device: D,
|
|
/// The IRQ to re-arm after each interrupt, if the device has one.
|
|
irq: Option<CapSlot>,
|
|
events: [Option<InputEvent>; BUFFERED],
|
|
head: usize,
|
|
len: usize,
|
|
/// Signalled when events arrive (the display server's notification).
|
|
subscriber: Option<CapSlot>,
|
|
}
|
|
|
|
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 }
|
|
}
|
|
|
|
/// 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) }
|
|
}
|
|
|
|
fn push(&mut self, event: InputEvent) {
|
|
if self.len == BUFFERED {
|
|
// Full: the oldest event goes.
|
|
self.head = (self.head + 1) % BUFFERED;
|
|
self.len -= 1;
|
|
}
|
|
let tail = (self.head + self.len) % BUFFERED;
|
|
if let Some(slot) = self.events.get_mut(tail) {
|
|
*slot = Some(event);
|
|
self.len += 1;
|
|
}
|
|
}
|
|
|
|
fn pop(&mut self) -> Option<InputEvent> {
|
|
if self.len == 0 {
|
|
return None;
|
|
}
|
|
let event = self.events.get_mut(self.head)?.take();
|
|
self.head = (self.head + 1) % BUFFERED;
|
|
self.len -= 1;
|
|
event
|
|
}
|
|
|
|
/// The next event: buffered first, then whatever the device has now.
|
|
fn next_event(&mut self) -> Option<InputEvent> {
|
|
self.pop().or_else(|| self.device.poll())
|
|
}
|
|
|
|
pub fn device(&self) -> &D {
|
|
&self.device
|
|
}
|
|
|
|
pub fn subscriber(&self) -> Option<CapSlot> {
|
|
self.subscriber
|
|
}
|
|
|
|
#[cfg(test)]
|
|
pub fn replace_device_for_test(&mut self, device: D) {
|
|
self.device = device;
|
|
}
|
|
}
|
|
|
|
impl<D: InputDevice> Server for InputServer<D> {
|
|
type Request = InputRequest;
|
|
type Response = InputResponse;
|
|
|
|
fn handle(&mut self, request: InputRequest) -> InputResponse {
|
|
match request {
|
|
// WaitEvent cannot block: a server thread has one reply slot,
|
|
// so it answers like PollEvent.
|
|
InputRequest::PollEvent | InputRequest::WaitEvent => match self.next_event() {
|
|
Some(event) => InputResponse::Event(event),
|
|
None => InputResponse::NoEvent,
|
|
},
|
|
}
|
|
}
|
|
|
|
fn handle_call(&mut self, _badge: usize, words: [usize; 4], cap: Option<CapSlot>) -> ([usize; 4], Option<libkos::syscall::Give>) {
|
|
if words[0] == types::input::SUBSCRIBE {
|
|
self.subscriber = cap;
|
|
return ([0; 4], None);
|
|
}
|
|
match InputRequest::decode(words) {
|
|
Some(request) => (self.handle(request).encode(), None),
|
|
None => ([MALFORMED, 0, 0, 0], None),
|
|
}
|
|
}
|
|
|
|
fn signal(&mut self, _bits: usize) {
|
|
let mut arrived = false;
|
|
while let Some(event) = self.device.poll() {
|
|
self.push(event);
|
|
arrived = true;
|
|
}
|
|
if let (true, Some(subscriber)) = (arrived, self.subscriber) {
|
|
let _ = libkos::syscall::signal(subscriber);
|
|
}
|
|
self.device.acknowledge();
|
|
if let Some(irq) = self.irq {
|
|
let _ = libkos::syscall::ack_interrupt(irq);
|
|
}
|
|
}
|
|
}
|