continued to work on new UI library
- implemented CgStatusBar widget which is a specific version of the CgIndicatorBar widget with predefined fields - std::io::Screen is now an enum that makes switching between display modes more intuitive - created a basic CgLineEdit implementation that allows for a user to type in a character and have it re-render that widget - other more minor changes like fixes for existing apps to work with new features
This commit is contained in:
@@ -38,7 +38,7 @@ pub struct ColorCode(u8);
|
||||
|
||||
impl ColorCode {
|
||||
pub fn new(foreground: Color, background: Color) -> ColorCode {
|
||||
ColorCode((background as u8) << 5 | (foreground as u8))
|
||||
ColorCode((background as u8) << 4 | (foreground as u8))
|
||||
}
|
||||
}
|
||||
|
||||
@@ -207,11 +207,23 @@ impl Renderer {
|
||||
self.temp_colour = None;
|
||||
}
|
||||
|
||||
pub fn cursor_position(&mut self, x: u8, y: u8) -> Result<(), RenderError> {
|
||||
// check that x and y are within bounds
|
||||
if x >= 80 || x < 0 || y >= 25 || y < 0 {
|
||||
return Err(RenderError::OutOfBounds(
|
||||
x >= 80 || x < 0,
|
||||
y >= 25 || y < 0
|
||||
))
|
||||
}
|
||||
self.internal_set_cursor_position(x, y);
|
||||
Ok(())
|
||||
}
|
||||
|
||||
// INTERNAL API ONLY
|
||||
|
||||
fn set_cursor_position(&mut self, row: usize, col: usize) {
|
||||
fn internal_set_cursor_position(&mut self, x: u8, y: u8) {
|
||||
use x86_64::instructions::port::Port;
|
||||
let cursor_position: u16 = (row as u16) * 80 + (col as u16);
|
||||
let cursor_position: u16 = (y as u16) * 80 + (x as u16);
|
||||
|
||||
unsafe {// Write the high byte of the cursor position to register 14
|
||||
let mut control_port = Port::<u8>::new(0x3D4);
|
||||
@@ -302,7 +314,7 @@ impl Renderer {
|
||||
}
|
||||
}
|
||||
}
|
||||
self.set_cursor_position(BUFFER_HEIGHT - 1, self.col_pos);
|
||||
self.internal_set_cursor_position(self.col_pos as u8, BUFFER_HEIGHT as u8 - 1);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -11,10 +11,12 @@ use core::{pin::Pin, task::{Poll, Context}};
|
||||
use futures_util::stream::Stream;
|
||||
use futures_util::task::AtomicWaker;
|
||||
use futures_util::stream::StreamExt;
|
||||
use pc_keyboard::{layouts, DecodedKey, HandleControl, Keyboard, ScancodeSet1};
|
||||
use pc_keyboard::{layouts, DecodedKey, HandleControl, Keyboard, ScancodeSet1, KeyCode};
|
||||
use crate::print;
|
||||
use crate::kernel::render::RENDERER;
|
||||
use alloc::{string::String};
|
||||
use core::ascii::Char;
|
||||
use crate::kernel::tasks::keyboard::CharOrKeystroke::Char;
|
||||
|
||||
static WAKER: AtomicWaker = AtomicWaker::new();
|
||||
static SCANCODE_QUEUE: OnceCell<ArrayQueue<u8>> = OnceCell::uninit();
|
||||
@@ -29,6 +31,42 @@ pub struct KeyboardHandler {
|
||||
keyboard: Keyboard<layouts::Uk105Key, ScancodeSet1>,
|
||||
}
|
||||
|
||||
enum CharOrKeystroke {
|
||||
Char(char),
|
||||
Keystroke(KeyCode),
|
||||
}
|
||||
|
||||
pub enum KeyStroke {
|
||||
Char(char),
|
||||
Ctrl,
|
||||
RCtrl,
|
||||
Alt,
|
||||
RAlt,
|
||||
Shift,
|
||||
RShift,
|
||||
Meta,
|
||||
RMeta,
|
||||
None,
|
||||
}
|
||||
|
||||
impl KeyStroke {
|
||||
pub fn from_keycode(key: KeyCode) -> KeyStroke {
|
||||
match key {
|
||||
KeyCode::ControlLeft => KeyStroke::Ctrl,
|
||||
KeyCode::ControlRight => KeyStroke::RCtrl,
|
||||
KeyCode::AltLeft => KeyStroke::Alt,
|
||||
KeyCode::AltRight => KeyStroke::RAlt,
|
||||
KeyCode::ShiftLeft => KeyStroke::Shift,
|
||||
KeyCode::ShiftRight => KeyStroke::RShift,
|
||||
KeyCode::WindowsLeft => KeyStroke::Meta,
|
||||
KeyCode::WindowsRight => KeyStroke::RMeta,
|
||||
_ => KeyStroke::None,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
|
||||
impl KeyboardHandler {
|
||||
pub fn new() -> KeyboardHandler {
|
||||
KeyboardHandler {
|
||||
@@ -37,7 +75,7 @@ impl KeyboardHandler {
|
||||
}
|
||||
}
|
||||
|
||||
pub async fn get_keystroke_inner(&mut self) -> Option<char> {
|
||||
pub async fn get_keystroke_inner(&mut self) -> Option<KeyStroke> {
|
||||
loop {
|
||||
if let Some(scancode) = self.scancodes.next().await {
|
||||
if let Ok(Some(key_event)) = self.keyboard.add_byte(scancode) {
|
||||
@@ -50,10 +88,15 @@ impl KeyboardHandler {
|
||||
});
|
||||
return None;
|
||||
} else {
|
||||
return Some(character);
|
||||
return Some(KeyStroke::Char(character));
|
||||
}
|
||||
},
|
||||
DecodedKey::RawKey(key) => {
|
||||
print!("{:?}", key)
|
||||
match key {
|
||||
KeyCode::NOn
|
||||
}
|
||||
},
|
||||
DecodedKey::RawKey(key) => { print!("{:?}", key) },
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -61,16 +104,22 @@ impl KeyboardHandler {
|
||||
}
|
||||
}
|
||||
|
||||
pub async fn get_keystroke(&mut self) -> char {
|
||||
pub async fn get_keystroke(&mut self) -> KeyStroke {
|
||||
loop {
|
||||
match self.get_keystroke_inner().await {
|
||||
Some(c) => return c,
|
||||
Some(c) => match c {
|
||||
CharOrKeystroke::Char(c) => return KeyStroke::Char(c),
|
||||
CharOrKeystroke::Keystroke(c) => match KeyStroke::from_keycode(c) {
|
||||
KeyStroke::None => (),
|
||||
key => return key
|
||||
}
|
||||
},
|
||||
None => ()
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
pub fn try_keystroke(&mut self) -> Option<char> {
|
||||
pub fn try_keystroke(&mut self) -> Option<KeyStroke> {
|
||||
if let Some(scancode) = self.scancodes.try_next() {
|
||||
if let Ok(Some(key_event)) = self.keyboard.add_byte(scancode) {
|
||||
if let Some(key) = self.keyboard.process_keyevent(key_event) {
|
||||
@@ -82,10 +131,16 @@ impl KeyboardHandler {
|
||||
});
|
||||
return None;
|
||||
} else {
|
||||
return Some(character);
|
||||
return Some(KeyStroke::Char(character));
|
||||
}
|
||||
},
|
||||
DecodedKey::RawKey(key) => {
|
||||
print!("{:?}", key);
|
||||
match KeyStroke::from_keycode(key) {
|
||||
KeyStroke::None => (),
|
||||
key => return Some(key)
|
||||
}
|
||||
},
|
||||
DecodedKey::RawKey(key) => { print!("{:?}", key) },
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -101,15 +156,19 @@ impl KeyboardHandler {
|
||||
Some(c) => { c },
|
||||
None => { val.pop(); continue; },
|
||||
};
|
||||
print!("{}", character);
|
||||
let (character, execute): (char, bool) = match character {
|
||||
'\n' => (character, true),
|
||||
_ => (character, false),
|
||||
};
|
||||
val.push(character);
|
||||
if execute {
|
||||
return val;
|
||||
|
||||
if let CharOrKeystroke::Char(c) = character {
|
||||
print!("{}", character);
|
||||
let (c, execute): (char, bool) = match c {
|
||||
'\n' => (c, true),
|
||||
_ => (c, false),
|
||||
};
|
||||
val.push(c);
|
||||
if execute {
|
||||
return val;
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@@ -137,7 +137,7 @@ impl Frame {
|
||||
pub fn dimensions(&self) -> Dimensions {
|
||||
self.dimensions
|
||||
}
|
||||
pub fn write_pos(&mut self, position: Position, char: ColouredChar) {
|
||||
pub fn write(&mut self, position: Position, char: ColouredChar) {
|
||||
self.frame[position.y][position.x] = char
|
||||
}
|
||||
pub fn render_element(&mut self, other: &Frame) {
|
||||
|
||||
+27
-7
@@ -1,6 +1,6 @@
|
||||
use crate::{
|
||||
kernel::render::{RENDERER, self},
|
||||
kernel::tasks::keyboard::KEYBOARD,
|
||||
kernel::tasks::keyboard::{KEYBOARD, KeyStroke},
|
||||
};
|
||||
|
||||
use alloc::string::String;
|
||||
@@ -12,19 +12,24 @@ use crate::kernel::serial::serial_reply;
|
||||
|
||||
use lazy_static::lazy_static;
|
||||
use spin::Mutex;
|
||||
use crate::kernel::render::Renderer;
|
||||
use crate::std::frame::RenderError;
|
||||
|
||||
pub struct Stdin {}
|
||||
impl Stdin {
|
||||
/// waits for the user to type in a string and press enter | blocking
|
||||
pub async fn readline() -> String {
|
||||
let string = KEYBOARD.lock().get_string().await;
|
||||
string
|
||||
}
|
||||
|
||||
/// waits for a keystroke | blocking
|
||||
pub async fn keystroke() -> char {
|
||||
let chr = KEYBOARD.lock().get_keystroke().await;
|
||||
chr
|
||||
}
|
||||
|
||||
/// gets the next keystroke if any is present | non blocking
|
||||
pub fn try_keystroke() -> Option<char> {
|
||||
let chr = KEYBOARD.lock().try_keystroke();
|
||||
chr
|
||||
@@ -39,15 +44,30 @@ impl Serial {
|
||||
}
|
||||
}
|
||||
|
||||
pub struct Screen {}
|
||||
/// enum with a terminal and application mode
|
||||
pub enum Screen {
|
||||
Terminal,
|
||||
Application,
|
||||
}
|
||||
impl Screen {
|
||||
pub fn terminal_mode() {
|
||||
RENDERER.lock().terminal_mode().unwrap();
|
||||
/// mode can be set for the kernel using this method
|
||||
pub fn set_mode(&self) -> Result<(), RenderError> {
|
||||
match self {
|
||||
Screen::Terminal => RENDERER.lock().terminal_mode(),
|
||||
Screen::Application => RENDERER.lock().application_mode(),
|
||||
}
|
||||
}
|
||||
pub fn application_mode() {
|
||||
RENDERER.lock().application_mode().unwrap();
|
||||
|
||||
/// returns the current display mode
|
||||
pub fn get_mode() -> Screen {
|
||||
match RENDERER.lock().mode_is_app() {
|
||||
true => Screen::Application,
|
||||
false => Screen::Terminal,
|
||||
}
|
||||
}
|
||||
pub fn switch() {
|
||||
|
||||
/// switches between modes
|
||||
pub fn switch(&self) {
|
||||
if RENDERER.lock().mode_is_app() == true {
|
||||
RENDERER.lock().terminal_mode().unwrap();
|
||||
} else {
|
||||
|
||||
Reference in New Issue
Block a user