feat(userspace): programs in /bin w/o .elf; bin manifests (needs = display) so shell runs snake; Ctrl+C kills foreground via terminal interrupt notification; ctrl keys; sleep
This commit is contained in:
@@ -17,10 +17,17 @@ pub trait Console {
|
||||
fn flush_later(&mut self);
|
||||
/// Blocks until a key is typed: a byte, `\n` for Enter, 8 for Backspace.
|
||||
fn next_key(&mut self) -> u8;
|
||||
/// A key already typed, without waiting.
|
||||
fn poll_key(&mut self) -> Option<u8>;
|
||||
/// Takes input focus back from whoever took it, and clears the screen.
|
||||
fn take_focus(&mut self);
|
||||
fn interrupt(&mut self, notification: CapSlot);
|
||||
}
|
||||
|
||||
pub const BACKSPACE: u8 = 8;
|
||||
const MAX_LINE: usize = 256;
|
||||
/// Keys typed while no line is being read, kept for the next one.
|
||||
const TYPEAHEAD: usize = 64;
|
||||
|
||||
pub struct TerminalServer<C: Console> {
|
||||
console: C,
|
||||
@@ -30,6 +37,10 @@ pub struct TerminalServer<C: Console> {
|
||||
line_len: usize,
|
||||
line_read: usize,
|
||||
flush_pending: bool,
|
||||
typeahead: [u8; TYPEAHEAD],
|
||||
typeahead_len: usize,
|
||||
/// Told about Ctrl+C typed while no line is being read.
|
||||
on_interrupt: Option<CapSlot>,
|
||||
}
|
||||
|
||||
/// An empty reply, whose tag is not text.
|
||||
@@ -50,6 +61,9 @@ impl<C: Console> TerminalServer<C> {
|
||||
line_len: 0,
|
||||
line_read: 0,
|
||||
flush_pending: false,
|
||||
typeahead: [0; TYPEAHEAD],
|
||||
typeahead_len: 0,
|
||||
on_interrupt: None,
|
||||
};
|
||||
s.redraw();
|
||||
s.console.flush();
|
||||
@@ -60,6 +74,11 @@ impl<C: Console> TerminalServer<C> {
|
||||
&self.console
|
||||
}
|
||||
|
||||
#[cfg(test)]
|
||||
pub fn console_mut_for_test(&mut self) -> &mut C {
|
||||
&mut self.console
|
||||
}
|
||||
|
||||
pub fn grid(&self) -> &Grid {
|
||||
&self.grid
|
||||
}
|
||||
@@ -88,13 +107,52 @@ impl<C: Console> TerminalServer<C> {
|
||||
}
|
||||
}
|
||||
|
||||
/// Edits a line from the keyboard, echoing it, until Enter.
|
||||
/// A key typed ahead, or the next one.
|
||||
fn next_key(&mut self) -> u8 {
|
||||
if self.typeahead_len == 0 {
|
||||
return self.console.next_key();
|
||||
}
|
||||
let key = self.typeahead[0];
|
||||
self.typeahead.copy_within(1..self.typeahead_len, 0);
|
||||
self.typeahead_len -= 1;
|
||||
key
|
||||
}
|
||||
|
||||
/// Keys typed while nobody reads a line: Ctrl+C interrupts, the rest
|
||||
/// wait for the next line.
|
||||
fn keys_arrived(&mut self) {
|
||||
while let Some(key) = self.console.poll_key() {
|
||||
if key == types::terminal::CTRL_C {
|
||||
self.typeahead_len = 0;
|
||||
self.write(b"^C\n");
|
||||
if let Some(n) = self.on_interrupt {
|
||||
self.console.interrupt(n);
|
||||
}
|
||||
} else if self.typeahead_len < TYPEAHEAD {
|
||||
self.typeahead[self.typeahead_len] = key;
|
||||
self.typeahead_len += 1;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// Edits a line from the keyboard, echoing it, until Enter. Ctrl+C
|
||||
/// drops it and reads as an empty line.
|
||||
fn read_line(&mut self) {
|
||||
self.console.flush();
|
||||
// Flushed now; a flush timer still running is harmless.
|
||||
self.flush_pending = false;
|
||||
self.line_len = 0;
|
||||
self.line_read = 0;
|
||||
loop {
|
||||
match self.console.next_key() {
|
||||
match self.next_key() {
|
||||
types::terminal::CTRL_C => {
|
||||
self.line[0] = b'\n';
|
||||
self.line_len = 1;
|
||||
for &b in b"^C\n" {
|
||||
self.grid.put(b);
|
||||
}
|
||||
break;
|
||||
}
|
||||
b'\n' => {
|
||||
self.line[self.line_len] = b'\n';
|
||||
self.line_len += 1;
|
||||
@@ -148,9 +206,23 @@ impl<C: Console> Server for TerminalServer<C> {
|
||||
Done
|
||||
}
|
||||
|
||||
fn handle_call(&mut self, _badge: usize, words: [usize; 4], _cap: Option<CapSlot>) -> ([usize; 4], Option<libkos::syscall::Give>) {
|
||||
fn handle_call(&mut self, _badge: usize, words: [usize; 4], cap: Option<CapSlot>) -> ([usize; 4], Option<libkos::syscall::Give>) {
|
||||
let reply = match words[0] {
|
||||
types::terminal::READ => self.read(),
|
||||
types::terminal::REDRAW => {
|
||||
self.console.take_focus();
|
||||
self.grid.touch_all();
|
||||
self.redraw();
|
||||
self.console.flush();
|
||||
[0; 4]
|
||||
}
|
||||
types::terminal::ON_INTERRUPT => match (cap, self.on_interrupt) {
|
||||
(Some(n), None) => {
|
||||
self.on_interrupt = Some(n);
|
||||
[0; 4]
|
||||
}
|
||||
_ => [MALFORMED, 0, 0, 0],
|
||||
},
|
||||
types::terminal::CLEAR => {
|
||||
self.handle(Request::Clear);
|
||||
[0; 4]
|
||||
@@ -166,10 +238,15 @@ impl<C: Console> Server for TerminalServer<C> {
|
||||
(reply, None)
|
||||
}
|
||||
|
||||
/// The flush timer: show a burst of output in one frame.
|
||||
fn signal(&mut self, _bits: usize) {
|
||||
self.flush_pending = false;
|
||||
self.console.flush();
|
||||
/// Keys, or the flush timer: show a burst of output in one frame.
|
||||
fn signal(&mut self, bits: usize) {
|
||||
if bits & types::terminal::KEYS_BIT != 0 {
|
||||
self.keys_arrived();
|
||||
}
|
||||
if bits & types::terminal::TIMER_BIT != 0 {
|
||||
self.flush_pending = false;
|
||||
self.console.flush();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
Reference in New Issue
Block a user