progress on terminal app
This commit is contained in:
@@ -52,6 +52,7 @@ pub enum RenderError {
|
|||||||
InvalidCharacter,
|
InvalidCharacter,
|
||||||
InvalidColour,
|
InvalidColour,
|
||||||
InvalidRenderMode,
|
InvalidRenderMode,
|
||||||
|
Other(&'static str),
|
||||||
}
|
}
|
||||||
|
|
||||||
impl ScreenChar {
|
impl ScreenChar {
|
||||||
@@ -111,19 +112,16 @@ lazy_static! {
|
|||||||
|
|
||||||
impl Renderer {
|
impl Renderer {
|
||||||
// EXTERNAL API : for use by standard library and other parts of the kernel
|
// EXTERNAL API : for use by standard library and other parts of the kernel
|
||||||
pub fn render_frame(&mut self, frame: [[ScreenChar; BUFFER_WIDTH]; BUFFER_HEIGHT]) { // renders the given frame to the app buffer
|
pub fn render_frame(&mut self, mut frame: [[ScreenChar; BUFFER_WIDTH]; BUFFER_HEIGHT]) { // renders the given frame to the app buffer
|
||||||
let mut processed_frame: [[ScreenChar; BUFFER_WIDTH]; BUFFER_HEIGHT] = [[ScreenChar::null(); BUFFER_WIDTH]; BUFFER_HEIGHT];
|
for (i, row) in frame.iter_mut().enumerate() {
|
||||||
|
for (j, col) in row.iter_mut().enumerate() {
|
||||||
for (i, row) in frame.iter().enumerate() {
|
if let Some(c) = special_char(col.character as char) {
|
||||||
for (j, col) in row.iter().enumerate() {
|
col.character = c as u8;
|
||||||
processed_frame[i][j] = match special_char(col.character as char) {
|
}
|
||||||
Some(c) => ScreenChar::new(c as u8, col.colour),
|
|
||||||
None => *col,
|
|
||||||
};
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
self.app_buffer = processed_frame;
|
self.app_buffer = frame;
|
||||||
self.internal_render();
|
self.internal_render();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -206,7 +206,10 @@ impl KeyboardHandler {
|
|||||||
pub(crate) fn add_scancode(scancode: u8) {
|
pub(crate) fn add_scancode(scancode: u8) {
|
||||||
if let Ok(queue) = SCANCODE_QUEUE.try_get() {
|
if let Ok(queue) = SCANCODE_QUEUE.try_get() {
|
||||||
if let Err(_) = queue.push(scancode) {
|
if let Err(_) = queue.push(scancode) {
|
||||||
println!("WARNING: queue is full - ignoring input");
|
let _ = queue.pop();
|
||||||
|
if let Err(_) = queue.push(scancode) {
|
||||||
|
println!("WARNING: scancode queue is full");
|
||||||
|
}
|
||||||
} else {
|
} else {
|
||||||
WAKER.wake();
|
WAKER.wake();
|
||||||
}
|
}
|
||||||
@@ -221,7 +224,7 @@ pub struct ScanCodeStream {
|
|||||||
|
|
||||||
impl ScanCodeStream {
|
impl ScanCodeStream {
|
||||||
pub fn new() -> Self {
|
pub fn new() -> Self {
|
||||||
SCANCODE_QUEUE.try_init_once(|| ArrayQueue::new(100))
|
SCANCODE_QUEUE.try_init_once(|| ArrayQueue::new(3))
|
||||||
.expect("ScanCodeStream::new has already been called once");
|
.expect("ScanCodeStream::new has already been called once");
|
||||||
ScanCodeStream { _private: () }
|
ScanCodeStream { _private: () }
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -45,13 +45,12 @@ impl Window {
|
|||||||
}
|
}
|
||||||
|
|
||||||
pub fn open(&mut self) -> Result<(), RenderError> {
|
pub fn open(&mut self) -> Result<(), RenderError> {
|
||||||
if self.open {
|
RENDERER.lock().application_mode();
|
||||||
return Err(RenderError::InvalidRenderMode);
|
|
||||||
}
|
if self.open { return Err(RenderError::InvalidRenderMode); }
|
||||||
|
|
||||||
self.open = true;
|
self.open = true;
|
||||||
|
self.render(&Frame::from_window(self))?;
|
||||||
let mut frame: [[ScreenChar; BUFFER_WIDTH]; BUFFER_HEIGHT] = [[ScreenChar::null(); BUFFER_WIDTH]; BUFFER_HEIGHT];
|
|
||||||
|
|
||||||
Ok(())
|
Ok(())
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -165,7 +164,12 @@ impl Window {
|
|||||||
}
|
}
|
||||||
Ok(())
|
Ok(())
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl Drop for Window {
|
||||||
|
fn drop(&mut self) {
|
||||||
|
RENDERER.lock().terminal_mode();
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
#[derive(Clone, Copy, Debug, PartialEq)]
|
#[derive(Clone, Copy, Debug, PartialEq)]
|
||||||
|
|||||||
@@ -20,7 +20,6 @@ pub struct Editor {
|
|||||||
command: String,
|
command: String,
|
||||||
mode: Mode,
|
mode: Mode,
|
||||||
unsaved: bool,
|
unsaved: bool,
|
||||||
display: Display,
|
|
||||||
lineno_width: i32,
|
lineno_width: i32,
|
||||||
window: Window,
|
window: Window,
|
||||||
}
|
}
|
||||||
@@ -37,7 +36,7 @@ impl core::fmt::Display for Mode {
|
|||||||
match self {
|
match self {
|
||||||
Mode::Normal => write!(f, "Normal"),
|
Mode::Normal => write!(f, "Normal"),
|
||||||
Mode::Insert => write!(f, "Insert"),
|
Mode::Insert => write!(f, "Insert"),
|
||||||
Mode::Command => write!(f, "Commnd"),
|
Mode::Command => write!(f, "Cmmd "),
|
||||||
Mode::Diff => write!(f, "Diff "),
|
Mode::Diff => write!(f, "Diff "),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -45,8 +44,6 @@ impl core::fmt::Display for Mode {
|
|||||||
|
|
||||||
impl Editor {
|
impl Editor {
|
||||||
fn move_cursor(&mut self, dx: i32, dy: i32) {
|
fn move_cursor(&mut self, dx: i32, dy: i32) {
|
||||||
serial_println!("trying to move");
|
|
||||||
|
|
||||||
if dy != 0
|
if dy != 0
|
||||||
&& self.cursor_pos.y + dy >= 0
|
&& self.cursor_pos.y + dy >= 0
|
||||||
&& self.cursor_pos.y + dy <= self.buffer.len() as i32
|
&& self.cursor_pos.y + dy <= self.buffer.len() as i32
|
||||||
@@ -86,14 +83,10 @@ impl Editor {
|
|||||||
self.offset_pos.y -= 1;
|
self.offset_pos.y -= 1;
|
||||||
}
|
}
|
||||||
|
|
||||||
serial_println!("talking to kernel");
|
|
||||||
|
|
||||||
self.window.move_cursor(
|
self.window.move_cursor(
|
||||||
self.cursor_pos.x - self.offset_pos.x + self.lineno_width + 2,
|
self.cursor_pos.x - self.offset_pos.x + self.lineno_width + 2,
|
||||||
self.cursor_pos.y - self.offset_pos.y
|
self.cursor_pos.y - self.offset_pos.y
|
||||||
).unwrap();
|
).unwrap();
|
||||||
|
|
||||||
serial_println!("done talking to kernel");
|
|
||||||
}
|
}
|
||||||
|
|
||||||
fn delete_char(&mut self) {
|
fn delete_char(&mut self) {
|
||||||
@@ -158,7 +151,6 @@ impl Application for Editor {
|
|||||||
command: String::new(),
|
command: String::new(),
|
||||||
mode: Mode::Normal,
|
mode: Mode::Normal,
|
||||||
unsaved: false,
|
unsaved: false,
|
||||||
display: Display::borrow(),
|
|
||||||
lineno_width: 0,
|
lineno_width: 0,
|
||||||
window: Window::new(),
|
window: Window::new(),
|
||||||
}
|
}
|
||||||
@@ -173,6 +165,7 @@ impl Application for Editor {
|
|||||||
self.window.set_dimensions(60, 23);
|
self.window.set_dimensions(60, 23);
|
||||||
self.window.set_position(10, 1);
|
self.window.set_position(10, 1);
|
||||||
self.window.set_title("Editor");
|
self.window.set_title("Editor");
|
||||||
|
self.window.open();
|
||||||
|
|
||||||
if let Some(s) = args.get(0) {
|
if let Some(s) = args.get(0) {
|
||||||
self.buffer = s.lines().map(|l| l.chars().collect()).collect::<Vec<Vec<char>>>()
|
self.buffer = s.lines().map(|l| l.chars().collect()).collect::<Vec<Vec<char>>>()
|
||||||
@@ -195,19 +188,12 @@ impl Application for Editor {
|
|||||||
KeyStroke::Char('i') => self.mode = Mode::Insert,
|
KeyStroke::Char('i') => self.mode = Mode::Insert,
|
||||||
KeyStroke::Char(':') => self.mode = Mode::Command,
|
KeyStroke::Char(':') => self.mode = Mode::Command,
|
||||||
KeyStroke::Char('d') => self.mode = Mode::Diff,
|
KeyStroke::Char('d') => self.mode = Mode::Diff,
|
||||||
KeyStroke::Char('`') => {
|
KeyStroke::Char('`') => return Ok(()),
|
||||||
// TODO: End terminal session
|
|
||||||
// ncurses::endwin();
|
|
||||||
return Ok(());
|
|
||||||
}
|
|
||||||
_ => {}
|
_ => {}
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
Mode::Insert => {
|
Mode::Insert => {
|
||||||
match keystroke {
|
match keystroke {
|
||||||
KeyStroke::Enter => {
|
|
||||||
// TODO: newline function
|
|
||||||
},
|
|
||||||
KeyStroke::Char(c) => {
|
KeyStroke::Char(c) => {
|
||||||
match c {
|
match c {
|
||||||
// escape
|
// escape
|
||||||
|
|||||||
+103
-7
@@ -1,15 +1,34 @@
|
|||||||
use alloc::{string::String, vec::Vec, boxed::Box};
|
use alloc::{string::String, vec::Vec, boxed::Box};
|
||||||
|
use core::any::Any;
|
||||||
use crate::std::{application::{Application, Error}, render::Window};
|
use crate::std::{application::{Application, Error}, render::Window};
|
||||||
|
|
||||||
use async_trait::async_trait;
|
use async_trait::async_trait;
|
||||||
|
use vga::writers::PrimitiveDrawing;
|
||||||
|
use crate::apps::calc::Calculator;
|
||||||
|
use crate::apps::editor::Editor;
|
||||||
|
use crate::apps::grapher::Grapher;
|
||||||
|
use crate::apps::tasks::Tasks;
|
||||||
|
use crate::games::gameoflife::GameOfLife;
|
||||||
|
use crate::games::paper_rs::GameBoard;
|
||||||
|
use crate::{games, println, utils};
|
||||||
|
use crate::std::io::{Display, Serial};
|
||||||
|
use crate::std::render::{ColouredChar, Frame, RenderError};
|
||||||
|
use crate::std::time::timer;
|
||||||
|
use crate::user::lib::libgui::cg_core::CgComponent;
|
||||||
|
use crate::utils::crystalfetch::CrystalFetch;
|
||||||
|
use crate::utils::gigachad_detector::GigachadDetector;
|
||||||
|
use crate::utils::rickroll::Rickroll;
|
||||||
|
|
||||||
pub struct ZxqSH {
|
pub struct ZxqSH {
|
||||||
history: Vec<String>,
|
history: Vec<String>,
|
||||||
idx: u32,
|
history_idx: usize,
|
||||||
|
|
||||||
|
row_pos: usize,
|
||||||
|
col_pos: usize,
|
||||||
|
|
||||||
window: Window,
|
window: Window,
|
||||||
|
buffer: Vec<Vec<ColouredChar>>,
|
||||||
|
buffer_width: usize,
|
||||||
}
|
}
|
||||||
|
|
||||||
#[async_trait]
|
#[async_trait]
|
||||||
@@ -32,19 +51,96 @@ impl ZxqSH {
|
|||||||
|
|
||||||
// update cycle for the shell
|
// update cycle for the shell
|
||||||
|
|
||||||
// TOOD: prompt
|
// TODO: prompt
|
||||||
|
let command = String::new();
|
||||||
|
|
||||||
// TODO: exit if necessar
|
// TODO: exit if necessary
|
||||||
|
|
||||||
// TODO: execute command
|
// TODO: execute command
|
||||||
|
self.execute(command, Vec::new()).await;
|
||||||
// return
|
// return
|
||||||
|
|
||||||
Ok(false)
|
Ok(false)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
async fn write_char(&mut self, ch: char) {
|
||||||
|
self.buffer[self.row_pos][self.col_pos] = ColouredChar::new(ch);
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
async fn execute(&self, cmd: String, args: Vec<String>) -> Result<(), Error> {
|
||||||
|
|
||||||
|
let window = Window::new();
|
||||||
|
|
||||||
|
match cmd.as_str() {
|
||||||
|
"calculate" | "calc" | "solve" => Calculator::new().run(Some(window), args).await?,
|
||||||
|
"games/connect4" => crate::user::bin::games::connect4::Game::new().run(Some(window), args).await?,
|
||||||
|
"rickroll" => Rickroll::new().run(Some(window), args).await?,
|
||||||
|
"crystalfetch" => CrystalFetch::new().run(Some(window), args).await?,
|
||||||
|
"tasks" => Tasks::new().run(Some(window), args).await?,
|
||||||
|
"VGA" => {
|
||||||
|
use vga::colors::Color16;
|
||||||
|
use vga::writers::{GraphicsWriter, Graphics640x480x16};
|
||||||
|
let mode = Graphics640x480x16::new();
|
||||||
|
mode.set_mode();
|
||||||
|
mode.clear_screen(Color16::Black);
|
||||||
|
mode.draw_line((80, 60), (120, 420), Color16::Cyan);
|
||||||
|
},
|
||||||
|
"graph" => Grapher::new().run(Some(window), args).await?,
|
||||||
|
"games/snake" => games::snake::Game::new().run(Some(window), args).await?,
|
||||||
|
"games/asteroids" => games::asteroids::Game::new().run(Some(window), args).await?,
|
||||||
|
"games/pong" => games::pong::Game::new().run(Some(window), args).await?,
|
||||||
|
"games/paper.rs" => games::paper_rs::GameBoard::new().run(Some(window), args).await?,
|
||||||
|
"serial" => println!("{}", Serial::reply_char('e')),
|
||||||
|
"games/gameoflife" => GameOfLife::new().run(Some(window), Vec::new()).await?,
|
||||||
|
"games/tetris" => {
|
||||||
|
// games::tetris::TetrisEngine::new().run(Vec::new()).await?;
|
||||||
|
}
|
||||||
|
"gigachad?" => utils::gigachad_detector::GigachadDetector::new().run(Some(window), args).await?,
|
||||||
|
"editor" => Editor::new().run(Some(window), args).await?,
|
||||||
|
|
||||||
|
// direct OS functions (not applications)
|
||||||
|
"echo" => {
|
||||||
|
println!(
|
||||||
|
"Crystal: '{}'",
|
||||||
|
args.into_iter()
|
||||||
|
.map(|mut s| {
|
||||||
|
s.push_str(" ");
|
||||||
|
s
|
||||||
|
})
|
||||||
|
.collect::<String>()
|
||||||
|
)
|
||||||
|
}
|
||||||
|
"clear" => Display::clear(),
|
||||||
|
"time" => timer(),
|
||||||
|
_ => return Err(Error::UnknownCommand(cmd))
|
||||||
|
};
|
||||||
|
|
||||||
async fn execute(&self, command: String) -> Result<(), Error> {
|
|
||||||
Ok(())
|
Ok(())
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl CgComponent for ZxqSH {
|
||||||
|
fn render(&self) -> Result<Frame, RenderError> {
|
||||||
|
let mut frame = Frame::from_window(&self.window);
|
||||||
|
|
||||||
|
let term_height = self.window.dimensions().y;
|
||||||
|
let term_width = self.window.dimensions().x;
|
||||||
|
let buffer_width = self.buffer_width;
|
||||||
|
let buffer_height = self.buffer.len();
|
||||||
|
|
||||||
|
self.window.move_cursor(self.col_pos as i32, term_height as i32)?;
|
||||||
|
|
||||||
|
// render the contents of the terminal to a frame
|
||||||
|
for (i, row) in self.buffer[buffer_height - term_height..buffer_height].iter().enumerate() {
|
||||||
|
for (j, col) in row.iter().enumerate().take(term_width) {
|
||||||
|
frame[i][j] = *col;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
Ok(frame)
|
||||||
|
}
|
||||||
|
|
||||||
|
fn as_any(&self) -> &dyn Any {
|
||||||
|
self as &dyn Any
|
||||||
|
}
|
||||||
}
|
}
|
||||||
Reference in New Issue
Block a user