feat(display): input routed via display server to focused app; input Subscribe; uses caps badged + Delegate
This commit is contained in:
56
crates/servers/display-server/src/focus.rs
Normal file
56
crates/servers/display-server/src/focus.rs
Normal file
@@ -0,0 +1,56 @@
|
||||
//! Who gets input: the display server is the input server's only client
|
||||
//! and passes events on to the app in focus, the one that subscribed last.
|
||||
//! Apps out of focus get nothing.
|
||||
|
||||
/// Events received and not yet polled by the app in focus.
|
||||
const QUEUED: usize = 32;
|
||||
|
||||
pub struct Focus<N: Copy> {
|
||||
/// The app in focus: its badge and the notification it gave to be told.
|
||||
holder: Option<(usize, N)>,
|
||||
events: [[usize; 4]; QUEUED],
|
||||
head: usize,
|
||||
len: usize,
|
||||
}
|
||||
|
||||
impl<N: Copy> Focus<N> {
|
||||
pub const fn new() -> Self {
|
||||
Self { holder: None, events: [[0; 4]; QUEUED], head: 0, len: 0 }
|
||||
}
|
||||
|
||||
/// `badge` takes focus; what the last holder had not read is dropped.
|
||||
pub fn subscribe(&mut self, badge: usize, notify: N) {
|
||||
self.holder = Some((badge, notify));
|
||||
self.len = 0;
|
||||
}
|
||||
|
||||
/// Queues an event (as the input server sends it) for the holder;
|
||||
/// returns whom to tell. With no holder the event goes nowhere.
|
||||
pub fn push(&mut self, event: [usize; 4]) -> Option<N> {
|
||||
let (_, notify) = self.holder?;
|
||||
if self.len == QUEUED {
|
||||
self.head = (self.head + 1) % QUEUED;
|
||||
self.len -= 1;
|
||||
}
|
||||
self.events[(self.head + self.len) % QUEUED] = event;
|
||||
self.len += 1;
|
||||
Some(notify)
|
||||
}
|
||||
|
||||
/// The next event for `badge`, if it is in focus and one is queued.
|
||||
pub fn poll(&mut self, badge: usize) -> Option<[usize; 4]> {
|
||||
if self.holder.is_none_or(|(b, _)| b != badge) || self.len == 0 {
|
||||
return None;
|
||||
}
|
||||
let event = self.events[self.head];
|
||||
self.head = (self.head + 1) % QUEUED;
|
||||
self.len -= 1;
|
||||
Some(event)
|
||||
}
|
||||
}
|
||||
|
||||
impl<N: Copy> Default for Focus<N> {
|
||||
fn default() -> Self {
|
||||
Self::new()
|
||||
}
|
||||
}
|
||||
@@ -1,5 +1,6 @@
|
||||
#![no_std]
|
||||
|
||||
pub mod focus;
|
||||
pub mod font;
|
||||
pub mod protocol;
|
||||
pub mod server;
|
||||
|
||||
@@ -1,19 +1,29 @@
|
||||
use server_framework::Server;
|
||||
use server_framework::{Decode, Encode, MALFORMED, Server};
|
||||
use types::bounded::CapSlot;
|
||||
|
||||
use crate::protocol::{DisplayRequest, DisplayResponse};
|
||||
use port_framebuffer::Framebuffer;
|
||||
use types::display::text::Text;
|
||||
use types::display::{Dimension, ScreenCoord};
|
||||
|
||||
use crate::focus::Focus;
|
||||
use crate::font;
|
||||
|
||||
pub struct DisplayServer<F: Framebuffer> {
|
||||
fb: F,
|
||||
/// The input server, which signals this server's bound notification.
|
||||
input: Option<CapSlot>,
|
||||
focus: Focus<CapSlot>,
|
||||
}
|
||||
|
||||
impl<F: Framebuffer> DisplayServer<F> {
|
||||
pub fn new(fb: F) -> Self {
|
||||
Self { fb }
|
||||
Self { fb, input: None, focus: Focus::new() }
|
||||
}
|
||||
|
||||
/// A server that passes `input`'s events on to the app in focus.
|
||||
pub fn with_input(fb: F, input: CapSlot) -> Self {
|
||||
Self { input: Some(input), ..Self::new(fb) }
|
||||
}
|
||||
|
||||
pub fn framebuffer(&self) -> &F {
|
||||
@@ -47,6 +57,39 @@ impl<F: Framebuffer> Server for DisplayServer<F> {
|
||||
type Request = DisplayRequest;
|
||||
type Response = DisplayResponse;
|
||||
|
||||
fn handle_call(&mut self, badge: usize, words: [usize; 4], cap: Option<CapSlot>) -> ([usize; 4], Option<CapSlot>) {
|
||||
use types::display::input::{POLL, SUBSCRIBE};
|
||||
match (words[0], cap) {
|
||||
(SUBSCRIBE, Some(notify)) => {
|
||||
self.focus.subscribe(badge, notify);
|
||||
(DisplayResponse::Ok.encode(), None)
|
||||
}
|
||||
(SUBSCRIBE, None) => (DisplayResponse::Error.encode(), None),
|
||||
(POLL, _) => (self.focus.poll(badge).unwrap_or([0; 4]), None),
|
||||
_ => match DisplayRequest::decode(words) {
|
||||
Some(request) => (self.handle(request).encode(), None),
|
||||
None => ([MALFORMED, 0, 0, 0], None),
|
||||
},
|
||||
}
|
||||
}
|
||||
|
||||
/// The input server has events: take them all, tell the app in focus.
|
||||
fn signal(&mut self, _bits: usize) {
|
||||
let Some(input) = self.input else {
|
||||
return;
|
||||
};
|
||||
let mut told = None;
|
||||
while let Ok(event) = libkos::syscall::request(input, [types::input::POLL_EVENT, 0, 0, 0]) {
|
||||
if event[0] == 0 {
|
||||
break;
|
||||
}
|
||||
told = self.focus.push(event).or(told);
|
||||
}
|
||||
if let Some(notify) = told {
|
||||
let _ = libkos::syscall::signal(notify);
|
||||
}
|
||||
}
|
||||
|
||||
fn handle(&mut self, request: DisplayRequest) -> DisplayResponse {
|
||||
match request {
|
||||
DisplayRequest::DrawRect { x, y, w, h, color } => {
|
||||
|
||||
36
crates/servers/display-server/src/tests/focus.rs
Normal file
36
crates/servers/display-server/src/tests/focus.rs
Normal file
@@ -0,0 +1,36 @@
|
||||
use crate::focus::Focus;
|
||||
|
||||
const KEY: [usize; 4] = [1, 9, b'a' as usize, 0];
|
||||
|
||||
#[test]
|
||||
fn only_the_app_in_focus_gets_events() {
|
||||
let mut f: Focus<u8> = Focus::new();
|
||||
assert_eq!(f.push(KEY), None, "nobody to give it to");
|
||||
f.subscribe(1, 10);
|
||||
assert_eq!(f.push(KEY), Some(10));
|
||||
assert_eq!(f.poll(2), None);
|
||||
assert_eq!(f.poll(1), Some(KEY));
|
||||
assert_eq!(f.poll(1), None);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn a_new_subscriber_takes_focus_and_the_old_queue_is_dropped() {
|
||||
let mut f: Focus<u8> = Focus::new();
|
||||
f.subscribe(1, 10);
|
||||
f.push(KEY);
|
||||
f.subscribe(2, 20);
|
||||
assert_eq!(f.poll(1), None);
|
||||
assert_eq!(f.poll(2), None);
|
||||
assert_eq!(f.push(KEY), Some(20));
|
||||
assert_eq!(f.poll(2), Some(KEY));
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn a_full_queue_drops_the_oldest() {
|
||||
let mut f: Focus<u8> = Focus::new();
|
||||
f.subscribe(1, 10);
|
||||
for i in 0..40 {
|
||||
f.push([1, 9, i, 0]);
|
||||
}
|
||||
assert_eq!(f.poll(1), Some([1, 9, 8, 0]));
|
||||
}
|
||||
@@ -1,2 +1,3 @@
|
||||
mod focus;
|
||||
mod protocol;
|
||||
mod server;
|
||||
|
||||
Reference in New Issue
Block a user