commit:ad412589c0e0a9150aea07b887be2a8a177807c7
author:Chip
committer:Chip
date:Fri Apr 19 22:13:45 2019 -0500
parents:d2e3efded3fac0ddab18ad96d23e8e59a2b2ebfb
Add basic line editing
diff --git a/crt/include/console.h b/crt/include/console.h
line changes: +16/-0
index 0000000..9ec5896
--- /dev/null
+++ b/crt/include/console.h
@@ -0,0 +1,16 @@
+#ifndef __CONSOLE_H
+#define __CONSOLE_H
+
+// constants for command keys returned from readkey()
+#define KEY_UP        -1024
+#define KEY_DOWN      -1025
+#define KEY_LEFT      -1026
+#define KEY_RIGHT     -1027
+#define KEY_ENTER     -1028
+#define KEY_BACKSPACE -1029
+#define KEY_TAB       -1030
+#define KEY_BREAK     -1031
+
+#define is_command_key(k) (k <= -1024 && k > -2048)
+
+#endif //__CONSOLE_H

diff --git a/crt/include/sysops.h b/crt/include/sysops.h
line changes: +1/-2
index 2c02779..77312d3
--- a/crt/include/sysops.h
+++ b/crt/include/sysops.h
@@ -5,8 +5,7 @@
 #include <stddef.h>
 
 struct console_operations {
-	int32_t (*readb)();
-	size_t (*read)(uint8_t buf[], size_t len);
+	int32_t (*readkey)();
 	size_t (*readline)(uint8_t buf[], size_t len);
 	void (*writeb)(uint8_t b);
 	void (*write)(uint8_t a[], size_t len);

diff --git a/shell/main.c b/shell/main.c
line changes: +24/-0
index 7d43bfe..3db8b52
--- a/shell/main.c
+++ b/shell/main.c
@@ -1,4 +1,5 @@
 #include <sysops.h>
+#include <console.h>
 #include <string.h>
 #include <stdio.h>
 #include <stdint.h>
@@ -47,6 +48,27 @@ int poke(lua_State *L) {
 	return 0;
 }
 
+int keydebug() {
+	while (1) {
+		int k = sys_ops->console->readkey();
+		if (k == -1) continue;
+		if (is_command_key(k)) {
+			switch(k) {
+			case KEY_UP: puts("[up]"); break;
+			case KEY_DOWN: puts("[down]"); break;
+			case KEY_LEFT: puts("[left]"); break;
+			case KEY_RIGHT: puts("[right]"); break;
+			case KEY_ENTER: puts("[enter]"); break;
+			case KEY_BACKSPACE: puts("[backspace]"); break;
+			case KEY_TAB: puts("[tab]"); break;
+			case KEY_BREAK: puts("[break]"); break;
+			}
+		} else {
+			printf("%c (%02x)\n", (char)k, k);
+		}
+	}
+}
+
 void shell_main(struct MemoryRange *r) {
 	lua_State *L;
 	char buffer[128];
@@ -98,6 +120,8 @@ void shell_main(struct MemoryRange *r) {
 				"mov r1, #0\n"
 				"sdiv r0, r0, r1\n"
 			);
+		} else if (strcmp(buffer, "keydebug") == 0) {
+			keydebug();
 		} else {
 			luaL_loadstring(L, buffer);
 			int r = lua_pcall(L, 0, LUA_MULTRET, 0);

diff --git a/src/console/interfaces.rs b/src/console/interfaces.rs
line changes: +13/-26
index fba8444..fa3ea08
--- a/src/console/interfaces.rs
+++ b/src/console/interfaces.rs
@@ -1,10 +1,10 @@
 use core::slice;
 
 use crate::console::get_global_console;
+use crate::console::Key;
 
 pub struct RustConsoleOperations {
-    pub readb: fn() -> Option<u8>,
-    pub read: fn(&mut [u8]) -> usize,
+    pub readkey: fn() -> Option<Key>,
     pub readline: fn(buf: &mut [u8]) -> usize,
     pub writeb: fn(b: u8),
     pub write: fn(a: &[u8]),
@@ -15,8 +15,7 @@ pub struct RustConsoleOperations {
 
 #[repr(C)]
 pub struct CConsoleOperations {
-    pub readb: extern "C" fn() -> i32,
-    pub read: extern "C" fn(buf: *mut u8, len: usize) -> usize,
+    pub readkey: extern "C" fn() -> i32,
     pub readline: extern "C" fn(buf: *mut u8, len: usize) -> usize,
     pub writeb: extern "C" fn(b: u8),
     pub write: extern "C" fn(a: *const u8, len: usize),
@@ -28,12 +27,8 @@ pub struct CConsoleOperations {
 /* Console driver function table */
 
 /* Rust versions */
-fn rust_readb() -> Option<u8> {
-    get_global_console().readb()
-}
-
-fn rust_read(buf: &mut [u8]) -> usize {
-    get_global_console().read(buf)
+fn rust_readkey() -> Option<Key> {
+    get_global_console().readkey()
 }
 
 fn rust_readline(buf: &mut [u8]) -> usize {
@@ -61,8 +56,7 @@ fn rust_clear() {
 }
 
 pub const RUST_CONSOLE_OPERATIONS: RustConsoleOperations = RustConsoleOperations {
-    readb: rust_readb,
-    read: rust_read,
+    readkey: rust_readkey,
     readline: rust_readline,
     writeb: rust_writeb,
     write: rust_write,
@@ -72,22 +66,16 @@ pub const RUST_CONSOLE_OPERATIONS: RustConsoleOperations = RustConsoleOperations
 };
 
 /* C versions */
-extern "C" fn c_readb() -> i32 {
-    match rust_readb() {
-        Some(b) => b as i32,
+extern "C" fn c_readkey() -> i32 {
+    match rust_readkey() {
+        Some(k) => match k {
+            Key::Char(c) => c as i32,
+            Key::Command(c) => -(1024 + c as i32),
+        }
         None => -1,
     }
 }
 
-extern "C" fn c_read(buf: *mut u8, len: usize) -> usize {
-    if len == 0 {
-        return 0;
-    }
-
-    let buf = unsafe { slice::from_raw_parts_mut(buf, len) };
-    rust_read(buf)
-}
-
 extern "C" fn c_readline(buf: *mut u8, len: usize) -> usize {
     let buf = unsafe { slice::from_raw_parts_mut(buf, len) };
     rust_readline(buf)
@@ -115,8 +103,7 @@ extern "C" fn c_clear() {
 }
 
 pub const C_CONSOLE_OPERATIONS: CConsoleOperations = CConsoleOperations {
-    readb: c_readb,
-    read: c_read,
+    readkey: c_readkey,
     readline: c_readline,
     writeb: c_writeb,
     write: c_write,

diff --git a/src/console/key.rs b/src/console/key.rs
line changes: +21/-0
index 0000000..ecf26e7
--- /dev/null
+++ b/src/console/key.rs
@@ -0,0 +1,21 @@
+pub enum Command {
+    Up,
+    Down,
+    Left,
+    Right,
+    Enter,
+    Backspace,
+    Tab,
+    Break,
+}
+
+pub enum Key {
+    Char(char),
+    Command(Command),
+}
+
+pub struct ModifierState {
+    control: bool,
+    alt: bool,
+    shift: bool,
+}

diff --git a/src/console/mod.rs b/src/console/mod.rs
line changes: +8/-10
index c39e0e3..5750efb
--- a/src/console/mod.rs
+++ b/src/console/mod.rs
@@ -1,13 +1,15 @@
 use core::fmt;
 
+mod key;
 pub mod serial;
-pub mod traits;
-pub mod interfaces;
+mod traits;
+mod interfaces;
 
 use crate::console::serial::SerialTerminal;
 
-pub use crate::console::traits::*;
-pub use crate::console::interfaces::*;
+pub use key::*;
+pub use traits::*;
+pub use interfaces::*;
 
 #[link_section = ".kgbss"]
 static mut G_CONSOLE: Option<Console> = None;
@@ -29,12 +31,8 @@ impl Console {
         }
     }
 
-    pub fn readb(&mut self) -> Option<u8> {
-        self.terminal.readb()
-    }
-
-    pub fn read(&mut self, buf: &mut [u8]) -> usize {
-        self.terminal.read(buf)
+    pub fn readkey(&mut self) -> Option<Key> {
+        self.terminal.readkey()
     }
 
     pub fn readline(&mut self, buf: &mut [u8]) -> usize {

diff --git a/src/console/serial.rs b/src/console/serial.rs
line changes: +99/-39
index 72ec5f2..f7dfd8a
--- a/src/console/serial.rs
+++ b/src/console/serial.rs
@@ -5,6 +5,7 @@ use embedded_hal::serial::*;
 use atsam3xa::interrupt;
 use cortex_m::interrupt as cm_interrupt;
 
+use crate::console::key::*;
 use crate::console::traits::*;
 use crate::console::get_global_console;
 
@@ -31,55 +32,114 @@ impl SerialTerminal {
 }
 
 impl ConsoleIO for SerialTerminal {
-    fn readb(&mut self) -> Option<u8> {
+    fn readkey(&mut self) -> Option<Key> {
         cm_interrupt::free(|_| {
-            self.input_buffer.pop_front()
+            let b = self.input_buffer.pop_front()?;
+            match b {
+                0x1b => {
+                    if self.input_buffer.len() >= 2 {
+                        // escape sequence incoming
+                        let b = self.input_buffer.pop_front().unwrap();
+                        if b != 0x5b {
+                            return None;
+                        }
+
+                        let b = self.input_buffer.pop_front().unwrap();
+                        match b {
+                            0x41 => Some(Key::Command(Command::Up)),
+                            0x42 => Some(Key::Command(Command::Down)),
+                            0x43 => Some(Key::Command(Command::Right)),
+                            0x44 => Some(Key::Command(Command::Left)),
+                            _ => None,
+                        }
+                    } else {
+                        // not enough characters yet, put the ESC back in the buffer
+                        self.input_buffer.push_front(0x1b).unwrap();
+                        None
+                    }
+                },
+                0x0d => Some(Key::Command(Command::Enter)),
+                0x08 => Some(Key::Command(Command::Backspace)),
+                0x09 => Some(Key::Command(Command::Tab)),
+                0x03 => Some(Key::Command(Command::Break)),
+                b => Some(Key::Char(b as char)),
+            }
         })
     }
 
-    fn read(&mut self, buf: &mut [u8]) -> usize {
-        let len = if buf.len() > self.input_buffer.len() {
-            self.input_buffer.len()
-        } else {
-            buf.len()
-        };
-
-        for i in 0..len {
-            buf[i] = self.readb().unwrap();
-        }
-
-        len
-    }
-
     fn readline(&mut self, buf: &mut [u8]) -> usize {
         let mut c = 0;
+        let mut cursor = 0;
 
         loop {
-            let byte = match self.readb() {
-                Some(b) => b,
+            match self.readkey() {
+                Some(k) => match k {
+                    Key::Char(x) => {
+                        if c == buf.len() {
+                            // Don't overflow the buffer
+                            continue;
+                        }
+
+                        // FIXME proper unicode output
+                        self.writeb(x as u8);
+                        if cursor < c {
+                            // reprint everything to the right of the cursor
+                            for i in cursor..c {
+                                self.writeb(buf[i]);
+                            }
+                            for i in (cursor..c).rev() {
+                                buf[i + 1] = buf[i];
+                                self.writeb(0x08);
+                            }
+                        }
+                        buf[cursor] = x as u8;
+
+                        c += 1;
+                        cursor += 1;
+                    },
+                    Key::Command(x) => match x {
+                        Command::Backspace => {
+                            if c == 0 || cursor == 0 {
+                                // buffer empty
+                                continue;
+                            }
+                            self.writeb(0x08);
+                            for i in cursor..c {
+                                // reprint everything from the cursor rightward
+                                self.writeb(buf[i]);
+                            }
+                            self.writeb(0x20);
+                            for i in cursor..c {
+                                buf[i - 1] = buf[i];
+                                self.writeb(0x08);
+                            }
+                            self.writeb(0x08);
+                            c -= 1;
+                            cursor -= 1;
+                        },
+                        Command::Enter => {
+                            break;
+                        },
+                        Command::Left => {
+                            if cursor == 0 {
+                                continue;
+                            }
+                            cursor -= 1;
+                            self.write("\x1b[1D");
+                        },
+                        Command::Right => {
+                            if cursor == c {
+                                continue;
+                            }
+                            cursor += 1;
+                            self.write("\x1b[1C");
+                        },
+                        _ => continue,
+                    }
+                },
                 None => continue,
             };
 
-            if byte == 0xd {
-                break;
-            } else if byte == 0x8 {
-                if c == 0 {
-                    // buffer empty
-                    continue;
-                }
-                self.write("\x08 \x08");
-                c -= 1;
-                continue;
-            }
-
-            if c == buf.len() {
-                // Don't overflow the buffer
-                continue;
-            }
-
-            self.writeb(byte);
-            buf[c] = byte;
-            c += 1;
         }
 
         c
@@ -93,7 +153,7 @@ impl ConsoleIO for SerialTerminal {
 
     fn pos(&mut self, x: usize, y: usize) {
         use core::fmt::Write;
-        assert!(x < 80 && y < 25);
+        assert!(x < 40 && y < 25);
         write!(self, "\x1b[{};{}H", y, x).ok();
     }
 

diff --git a/src/console/traits.rs b/src/console/traits.rs
line changes: +3/-2
index abc7942..edd4197
--- a/src/console/traits.rs
+++ b/src/console/traits.rs
@@ -1,7 +1,8 @@
+use crate::console::Key;
+
 pub trait ConsoleIO {
     // input
-    fn readb(&mut self) -> Option<u8>;
-    fn read(&mut self, buf: &mut [u8]) -> usize;
+    fn readkey(&mut self) -> Option<Key>;
     fn readline(&mut self, buf: &mut [u8]) -> usize;
     fn discard(&mut self);