From bf9c9be88de115fda2f27df02beff0cf7371c2bf Mon Sep 17 00:00:00 2001 From: FantasyPvP <80643031+FantasyPvP@users.noreply.github.com> Date: Mon, 2 Oct 2023 00:56:18 +0100 Subject: [PATCH] made a game made a snake game and rewrote some rendering stuff again --- Cargo.lock | 23 +++ Cargo.toml | 1 + src/main.rs | 5 +- src/system/kernel/mod.rs | 1 - src/system/kernel/render.rs | 35 +++- .../kernel/{threading => threading2}/mod.rs | 4 +- .../thread_switch.rs | 0 .../{threading => threading2}/thread_switch.s | 0 src/system/std/application.rs | 1 + src/system/std/frame.rs | 154 ++++++++++++++++ src/system/std/io.rs | 151 ++-------------- src/system/std/mod.rs | 1 + src/user/bin/crystal_rpg/init.rs | 26 +-- src/user/bin/crystalfetch.rs | 34 ++-- src/user/bin/mod.rs | 1 + src/user/bin/shell.rs | 8 +- src/user/bin/shellrewrite.rs | 168 ++++++++++++------ src/user/bin/snake.rs | 150 ++++++++++++++++ src/user/lib/libgui/libgui_core.rs | 10 +- 19 files changed, 532 insertions(+), 241 deletions(-) rename src/system/kernel/{threading => threading2}/mod.rs (97%) rename src/system/kernel/{threading => threading2}/thread_switch.rs (100%) rename src/system/kernel/{threading => threading2}/thread_switch.s (100%) create mode 100644 src/system/std/frame.rs create mode 100644 src/user/bin/snake.rs diff --git a/Cargo.lock b/Cargo.lock index b4e28c4..04446de 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -25,6 +25,7 @@ dependencies = [ "rgb", "spin", "uart_16550", + "uchan", "volatile 0.2.7", "x86_64", ] @@ -96,6 +97,12 @@ version = "1.14.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "374d28ec25809ee0e23827c2ab573d729e293f281dfe393500e7ad618baa61c6" +[[package]] +name = "cache-padded" +version = "1.3.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "981520c98f422fcc584dc1a95c334e6953900b9106bc47a9839b81790009eb21" + [[package]] name = "cfg-if" version = "0.1.10" @@ -343,6 +350,12 @@ dependencies = [ "lock_api", ] +[[package]] +name = "sptr" +version = "0.3.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3b9b39299b249ad65f3b7e96443bad61c02ca5cd3589f46cb6d610a0fd6c0d6a" + [[package]] name = "syn" version = "2.0.37" @@ -365,6 +378,16 @@ dependencies = [ "x86_64", ] +[[package]] +name = "uchan" +version = "0.1.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5045a07b95977d6b1e0a6aa8d0d68a84017e2b8ae01d6bfcafcaa1c0a65e19dc" +dependencies = [ + "cache-padded", + "sptr", +] + [[package]] name = "unicode-ident" version = "1.0.12" diff --git a/Cargo.toml b/Cargo.toml index 591e5e7..c3c88cf 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -30,6 +30,7 @@ hashbrown = "0.13.2" cmos-rtc = "0.1.2" libm = "0.2.7" log = "0.4.20" +uchan = { version = "0.1.4", default-features = false } [dependencies.lazy_static] version = "1.0" diff --git a/src/main.rs b/src/main.rs index e0c80db..caa4455 100644 --- a/src/main.rs +++ b/src/main.rs @@ -5,7 +5,7 @@ #![reexport_test_harness_main = "test_main"] use core::panic::PanicInfo; -use CrystalOS::{println, print, println_log, print_log}; +use CrystalOS::{println, print, println_log, print_log, kernel, printerr}; use CrystalOS::kernel::tasks::{Task, executor::Executor}; use bootloader::{BootInfo, entry_point}; extern crate alloc; @@ -14,7 +14,8 @@ use CrystalOS::user::bin::shell; #[cfg(not(test))] #[panic_handler] fn panic(_info: &PanicInfo) -> ! { - println!("{}", _info); + kernel::render::RENDERER.lock().terminal_mode_force(); + printerr!("{}", _info); CrystalOS::hlt(); } diff --git a/src/system/kernel/mod.rs b/src/system/kernel/mod.rs index 4ebd509..2bf27d1 100644 --- a/src/system/kernel/mod.rs +++ b/src/system/kernel/mod.rs @@ -3,7 +3,6 @@ pub mod fs; pub mod gdt; pub mod interrupts; pub mod memory; -//pub mod render; pub mod serial; pub mod tasks; pub mod sysinit; diff --git a/src/system/kernel/render.rs b/src/system/kernel/render.rs index 49ecc03..51eeb6f 100644 --- a/src/system/kernel/render.rs +++ b/src/system/kernel/render.rs @@ -42,24 +42,30 @@ impl ColorCode { #[derive(Debug, Clone, Copy, PartialEq, Eq)] #[repr(C)] -struct ScreenChar { - character: u8, - colour: ColorCode, +pub struct ScreenChar { + pub character: u8, + pub colour: ColorCode, } impl ScreenChar { - fn null() -> ScreenChar { + pub fn null() -> ScreenChar { ScreenChar { character: 0u8, colour: ColorCode::new(Color::White, Color::Black), } } - fn white(character: u8) -> ScreenChar { + pub fn white(character: u8) -> ScreenChar { ScreenChar { character, colour: ColorCode::new(Color::White, Color::Black), } } + pub fn new(character: u8, colour: ColorCode) -> ScreenChar { + ScreenChar { + character, + colour, + } + } } pub const BUFFER_HEIGHT: usize = 25; @@ -92,19 +98,26 @@ lazy_static! { impl Renderer { // EXTERNAL API : for use by standard library and other parts of the kernel - pub fn render_frame(&mut self, frame: [[char; BUFFER_WIDTH]; BUFFER_HEIGHT]) { // renders the given frame to the app buffer + pub fn render_frame(&mut self, 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().enumerate() { for (j, col) in row.iter().enumerate() { - processed_frame[i][j] = match self.special_char(*col) { - Some(c) => ScreenChar::white(c as u8), - None => ScreenChar::white(*col as u8) + processed_frame[i][j] = match self.special_char(col.character as char) { + Some(c) => ScreenChar::new(c as u8, col.colour), + None => *col, }; } } self.app_buffer = processed_frame; + self.internal_render(); + } + + pub fn terminal_mode_force(&mut self) { // THIS SHOULD ONLY BE USED WHEN THE KERNEL PANICS + // TODO: find a way to make this function kernel only + self.application_mode = false; + self.internal_render(); } pub fn application_mode(&mut self) -> Result<(), ()> { @@ -132,11 +145,13 @@ impl Renderer { } pub fn write_char(&mut self, ch: u8, col: Option) { // default colour if no colour is selected for character + if self.application_mode { return; }; self.write_byte(ch, col); self.internal_render(); } pub fn write_string(&mut self, string: &str, col: Option) { + if self.application_mode { return; }; for ch in string.chars() { match self.special_char(ch) { Some(c) => self.write_byte(c, col), @@ -150,6 +165,7 @@ impl Renderer { } pub fn backspace(&mut self) -> Result<(), ()> { + if self.application_mode { return Ok(()); }; loop { if self.internal_backspace()? { @@ -163,6 +179,7 @@ impl Renderer { pub fn clear(&mut self) { // clears the screen and all scroll-back if self.application_mode { return; }; + self.term_buffer = vec![[ScreenChar::null(); BUFFER_WIDTH]; BUFFER_HEIGHT]; self.internal_render(); } diff --git a/src/system/kernel/threading/mod.rs b/src/system/kernel/threading2/mod.rs similarity index 97% rename from src/system/kernel/threading/mod.rs rename to src/system/kernel/threading2/mod.rs index dcfd0c1..518d558 100644 --- a/src/system/kernel/threading/mod.rs +++ b/src/system/kernel/threading2/mod.rs @@ -1,4 +1,4 @@ -/* + pub mod thread_switch; @@ -12,4 +12,4 @@ pub struct Thread { stack_pointer: Option, stack_bounds: Option, } -*/ + diff --git a/src/system/kernel/threading/thread_switch.rs b/src/system/kernel/threading2/thread_switch.rs similarity index 100% rename from src/system/kernel/threading/thread_switch.rs rename to src/system/kernel/threading2/thread_switch.rs diff --git a/src/system/kernel/threading/thread_switch.s b/src/system/kernel/threading2/thread_switch.s similarity index 100% rename from src/system/kernel/threading/thread_switch.s rename to src/system/kernel/threading2/thread_switch.s diff --git a/src/system/std/application.rs b/src/system/std/application.rs index 12fa3ce..8195b32 100644 --- a/src/system/std/application.rs +++ b/src/system/std/application.rs @@ -14,6 +14,7 @@ pub trait Application { pub enum Error { UnknownCommand(String), CommandFailed(String), + ApplicationError(String), EmptyCommand, } diff --git a/src/system/std/frame.rs b/src/system/std/frame.rs new file mode 100644 index 0000000..fa2895c --- /dev/null +++ b/src/system/std/frame.rs @@ -0,0 +1,154 @@ +use alloc::string::String; +use alloc::vec::Vec; +use lazy_static::lazy_static; +use crate::kernel::render::{BUFFER_HEIGHT, BUFFER_WIDTH, RENDERER, ScreenChar}; +use crate::println; +use spin::Mutex; + +/// TODO: get a working implementation for CLI apps +/// elements can be created using their from_str() method +/// you can then render the element to the current frame using the render() method +/// the position of the element by passing a tuple (x,y) to render() +/// +/// nothing will appear on the screen until the frame is actually rendered by +/// the render_frame method on the renderer +/// +pub type Frame = [ [ ScreenChar; BUFFER_WIDTH ]; BUFFER_HEIGHT]; + +#[derive(Clone)] +pub struct Element { + frame: Vec>, + dimensions: (u8, u8) +} + +impl Element { + pub fn from_str(elemstr: String) -> Self { + let mut element = Element { frame: Vec::>::new(), dimensions: (0, 0) }; + + for line in elemstr.split("\n") { + let mut ln = Vec::::new(); + for col in line.chars() { + ln.push(col) + }; + element.frame.push(ln); + } + + for row in element.clone().frame { + let n = row.len(); + if n > element.dimensions.0 as usize { + element.dimensions.0 = n as u8; + } + } + element + } + + pub fn generate(frame: Vec::>, dims: (u8, u8)) -> Self { + Element { frame, dimensions: dims } + } + + pub fn render(&mut self, pos: (u8, u8)) { // x,y + for (i, row) in self.frame.iter().enumerate() { + for (j, col) in row.iter().enumerate() { + //println!("{} {} {}", i, j, col); + FRAMEGEN.lock().frame[i + pos.1 as usize][j + pos.0 as usize] = ScreenChar::white(*col as u8); + }; + } + FRAMEGEN.lock().render_frame(); + } +} + +#[derive(Clone)] +pub struct ColouredElement { + frame: Vec>, + dimensions: (u8, u8) +} + +impl ColouredElement { + pub fn from_str(elemstr: String) -> Self { + let mut element = ColouredElement { frame: Vec::>::new(), dimensions: (0, 0) }; + + for line in elemstr.split("\n") { + let mut ln = Vec::::new(); + for col in line.chars() { + ln.push(ScreenChar::white(col as u8)) + }; + element.frame.push(ln); + } + + for row in element.clone().frame { + let n = row.len(); + if n > element.dimensions.0 as usize { + element.dimensions.0 = n as u8; + } + } + element + } + + pub fn generate(frame: Vec::>, dims: (u8, u8)) -> Self { + ColouredElement { frame, dimensions: dims } + } + + pub fn render(&mut self, pos: (u8, u8)) -> Result<(), ()> { // x,y + + // this block returns an error if any characters will be drawn out of the bounds of the screen + if self.dimensions.0 + pos.0 > BUFFER_WIDTH as u8 { + return Err(()); + } else if self.dimensions.1 + pos.1 > BUFFER_HEIGHT as u8 { + return Err(()); + } else if self.frame.len() != self.dimensions.1 as usize { + return Err(()) + } else if self.frame.iter().map(|r| r.len()).max().ok_or_else(|| ())? > self.dimensions.0 as usize { + return Err(()) + } + + for (i, row) in self.frame.iter().enumerate() { + for (j, col) in row.iter().enumerate() { + //println!("{} {} {}", i, j, col); + FRAMEGEN.lock().frame[i + pos.1 as usize][j + pos.0 as usize] = *col; + }; + } + FRAMEGEN.lock().render_frame(); + Ok(()) + } +} + + +#[derive(Clone, Copy)] +pub struct FrameGen { + frame: Frame, +} + + +impl FrameGen { + pub fn render_frame(&self) { + RENDERER.lock().render_frame(self.frame) + } + + fn new() -> Self { + let mut frame: [[ScreenChar; BUFFER_WIDTH]; BUFFER_HEIGHT] = [[ScreenChar::null(); BUFFER_WIDTH]; BUFFER_HEIGHT]; + Self { frame: Frame::from(frame) } + } + + fn set_frame(&mut self, frame: Frame) { + self.frame = frame; + } + + pub fn get_frame(&self) -> &[ [ ScreenChar; BUFFER_WIDTH ]; BUFFER_HEIGHT] { + &self.frame + } +} + +lazy_static! { + pub static ref FRAMEGEN: Mutex = Mutex::new(FrameGen::new() ); +} + + +impl core::fmt::Display for FrameGen { + fn fmt(&self, _: &mut core::fmt::Formatter<'_>) -> core::fmt::Result { + println!(" "); + for row in &self.frame { + println!("{}", row.iter().map(|c| c.character as char ).collect::()); + }; + Ok(()) + } +} diff --git a/src/system/std/io.rs b/src/system/std/io.rs index c4d541a..2f4cab8 100644 --- a/src/system/std/io.rs +++ b/src/system/std/io.rs @@ -1,9 +1,9 @@ use crate::{ - kernel::render::{RENDERER, BUFFER_WIDTH, BUFFER_HEIGHT, ColorCode}, + kernel::render::{RENDERER, self}, kernel::tasks::keyboard::KEYBOARD, }; -use alloc::{boxed::Box, string::{String, ToString}, vec::Vec}; +use alloc::string::String; pub use crate::{print, println, serial_print, serial_println}; pub use crate::kernel::render::Color; @@ -45,112 +45,6 @@ impl Screen { } - - - - - - - -/// TODO: get a working implementation for CLI apps -/// elements can be created using their from_str() method -/// you can then render the element to the current frame using the render() method -/// the position of the element by passing a tuple (x,y) to render() -/// -/// nothing will appear on the screen until the frame is actually rendered by -/// the render_frame method on the renderer -/// -pub type Frame = [ [ char; BUFFER_WIDTH ]; BUFFER_HEIGHT]; - -#[derive(Clone)] -pub struct Element { - frame: Vec>, - dimensions: (u8, u8) -} - -impl Element { - pub fn from_str(elemstr: String) -> Self { - let mut element = Element { frame: Vec::>::new(), dimensions: (0, 0) }; - - for line in elemstr.split("\n") { - let mut ln = Vec::::new(); - for col in line.chars() { - ln.push(col) - }; - element.frame.push(ln); - } - - for row in element.clone().frame { - let n = row.len(); - if n > element.dimensions.0 as usize { - element.dimensions.0 = n as u8; - } - } - element - } - - pub fn render(&mut self, pos: (u8, u8)) { // x,y - for (i, row) in self.frame.iter().enumerate() { - for (j, col) in row.iter().enumerate() { - println!("{} {} {}", i, j, col); - FRAMEGEN.lock().frame[i + pos.1 as usize][j + pos.0 as usize] = *col; - }; - } - } -} - - -lazy_static! { - pub static ref FRAMEGEN: Mutex = Mutex::new(FrameGen::new() ); -} - - -#[derive(Clone, Copy)] -pub struct FrameGen { - frame: Frame, -} - - -impl FrameGen { - pub fn render_frame(&self) { - RENDERER.lock().render_frame(self.frame) - } - - fn new() -> Self { - let mut frame: [[char; BUFFER_WIDTH]; BUFFER_HEIGHT] = [[' '; BUFFER_WIDTH]; BUFFER_HEIGHT]; - for i in 0..BUFFER_WIDTH { - frame[0][i] = "┌──────────────────────────────────────────────────────────────────────────────┐".chars().collect::>()[i]; - frame[BUFFER_HEIGHT -1][i] = "└──────────────────────────────────────────────────────────────────────────────┘".chars().collect::>()[i]; - } - - for j in 1..BUFFER_HEIGHT -1 { - for i in 0..BUFFER_WIDTH { - frame[j][i] = "│ │".chars().collect::>()[i]; - } - } - - Self { frame: Frame::from(frame) } - } - - pub fn get_frame(&self) -> &[ [ char; BUFFER_WIDTH ]; BUFFER_HEIGHT] { - &self.frame - } - -} - - -impl core::fmt::Display for FrameGen { - fn fmt(&self, _: &mut core::fmt::Formatter<'_>) -> core::fmt::Result { - println!(" "); - for row in &self.frame { - println!("{}", row.iter().collect::()); - }; - Ok(()) - } -} - - - #[macro_export] macro_rules! println_log { () => ($crate::print_log!("/n")); @@ -173,43 +67,26 @@ macro_rules! print { ($($arg:tt)*) => ($crate::std::io::_print(format_args!($($arg)*))); } - +#[macro_export] +macro_rules! printerr { + ($($arg:tt)*) => ($crate::std::io::_printerr(format_args!($($arg)*))); +} #[doc(hidden)] pub fn _print(args: core::fmt::Arguments) { - use core::fmt::Write; - use x86_64::instructions::interrupts; + render::write(args, (Color::White, Color::Black)); +} - interrupts::without_interrupts(|| { - let mut writer = RENDERER.lock(); - writer.write_fmt(args).unwrap(); - //WRITER.lock().write_fmt(args).unwrap(); - //writer.col_code = crate::kernel::render2::ColorCode::new(Color::White, Color::Black); - - }); +#[doc(hidden)] +pub fn _printerr(args: core::fmt::Arguments) { + render::write(args, (Color::Yellow, Color::Black)); } #[doc(hidden)] pub fn _log(args: core::fmt::Arguments) { - use core::fmt::Write; - use x86_64::instructions::interrupts; - - interrupts::without_interrupts(|| { - let mut writer = RENDERER.lock(); - writer.write_fmt(args).unwrap(); - //writer.col_code = crate::kernel::render2::ColorCode::new(Color::Yellow, Color::Black); - //WRITER.lock().write_fmt(args).unwrap(); - }); + render::write(args, (Color::White, Color::Black)); } - - -pub fn write(args: core::fmt::Arguments, cols: (Color, Color)) { - crate::kernel::render::write(args, cols); -} - -pub fn mkfs() { - use crate::kernel::fs; - fs::mkfs(); - println!("{:?}", *(fs::FILESYSTEM.lock())); +pub fn write(args: core::fmt::Arguments, color: (Color, Color)) { + render::write(args, color); } \ No newline at end of file diff --git a/src/system/std/mod.rs b/src/system/std/mod.rs index 989f186..79b68bc 100644 --- a/src/system/std/mod.rs +++ b/src/system/std/mod.rs @@ -3,6 +3,7 @@ pub mod random; pub mod application; pub mod tasks; pub mod os; +pub mod frame; // this is where the standard library for the operating system will be defined diff --git a/src/user/bin/crystal_rpg/init.rs b/src/user/bin/crystal_rpg/init.rs index 0c39991..dfef4fd 100644 --- a/src/user/bin/crystal_rpg/init.rs +++ b/src/user/bin/crystal_rpg/init.rs @@ -1,24 +1,24 @@ use async_trait::async_trait; -use rand::prelude::*; - use super::{ - engine::{eventcheck, Choice, Event}, - entity::{Entity, Enemy, EntityObject}, + engine::{Choice, Event, eventcheck}, + entity::{Enemy, Entity, EntityObject}, player::Player, }; -use alloc::{boxed::Box, string::{String, ToString}, vec::Vec, format, borrow::ToOwned}; +use alloc::{borrow::ToOwned, format, string::{String, ToString}, vec::Vec, boxed::Box}; -use crate::{ - std::application::{ - Application, - Error, - }, +use crate::{ std::{ - io::{self, println, serial_println, FRAMEGEN, Element}, - random, + io::{self, println, serial_println}, + frame::FRAMEGEN, + random, + }, + std::application::{ + Application, + Error, }, }; +use crate::system::std::frame::Element; pub struct GameLoop; @@ -91,7 +91,7 @@ impl Application for GameLoop { for row in fr { let mut r = String::new(); for col in row { - r.push(col); + r.push(col.character as char); } string.push_str(&r); string.push('\n') diff --git a/src/user/bin/crystalfetch.rs b/src/user/bin/crystalfetch.rs index d26faef..5e24366 100644 --- a/src/user/bin/crystalfetch.rs +++ b/src/user/bin/crystalfetch.rs @@ -8,21 +8,21 @@ use crate::{std::os::OS, std::io::{Color, write, Screen}, println, std::applicat }, std}; const CRYSTAL_LOGO: &str = -" $$$$$$\\ $$\\ $$\\ $$$$$$\\ $$$$$$\\ -$$ __$$\\ $$ | $$ $$ __$$\\$$ __$$\\ -$$ / \\__|$$$$$$\\ $$\\ $$\\ $$$$$$$\\$$$$$$\\ $$$$$$\\ $$ $$ / $$ $$ / \\__| -$$ | $$ __$$\\$$ | $$ $$ _____\\_$$ _| \\____$$\\$$ $$ | $$ \\$$$$$$\\ -$$ | $$ | \\__$$ | $$ \\$$$$$$\\ $$ | $$$$$$$ $$ $$ | $$ |\\____$$\\ -$$ | $$\\$$ | $$ | $$ |\\____$$\\ $$ |$$\\$$ __$$ $$ $$ | $$ $$\\ $$ | -\\$$$$$$ $$ | \\$$$$$$$ $$$$$$$ | \\$$$$ \\$$$$$$$ $$ |$$$$$$ \\$$$$$$ | - \\______/\\__| \\____$$ \\_______/ \\$$$$$$\\_______\\__|\\______/ \\______/ - $$\\ $$ | $$ __$$\\ - \\$$$$$$ | $$\\ $$\\__/ $$ | - \\______/ \\$$\\ $$ $$$$$$ | - \\$$\\$$ $$ ____/ - \\$$$ /$$ | - \\$ / $$$$$$$$\\ - \\_/ \\________| "; + "\n $$$$$$\\ $$\\ $$\\ $$$$$$\\ $$$$$$\\ + $$ __$$\\ $$ | $$ $$ __$$\\$$ __$$\\ + $$ / \\__|$$$$$$\\ $$\\ $$\\ $$$$$$$\\$$$$$$\\ $$$$$$\\ $$ $$ / $$ $$ / \\__| + $$ | $$ __$$\\$$ | $$ $$ _____\\_$$ _| \\____$$\\$$ $$ | $$ \\$$$$$$\\ + $$ | $$ | \\__$$ | $$ \\$$$$$$\\ $$ | $$$$$$$ $$ $$ | $$ |\\____$$\\ + $$ | $$\\$$ | $$ | $$ |\\____$$\\ $$ |$$\\$$ __$$ $$ $$ | $$ $$\\ $$ | + \\$$$$$$ $$ | \\$$$$$$$ $$$$$$$ | \\$$$$ \\$$$$$$$ $$ |$$$$$$ \\$$$$$$ | + \\______/\\__| \\____$$ \\_______/ \\$$$$$$\\_______\\__|\\______/ \\______/ + $$\\ $$ | $$ __$$\\ + \\$$$$$$ | $$\\ $$\\__/ $$ | + \\______/ \\$$\\ $$ $$$$$$ | + \\$$\\$$ $$ ____/ + \\$$$ /$$ | + \\$ / $$$$$$$$\\ + \\_/ \\________| "; const ZXQ5_LOGO: &str = " @@ -59,10 +59,10 @@ impl Application for CrystalFetch { [ Author » FantasyPvP / ZXQ5", os, version); // write to output - let spacer = "\n".repeat(24 - logo_string.lines().count() - 4 - info_string.lines().count()); + let spacer = "\n".repeat(25 - logo_string.lines().count() - 4 - info_string.lines().count()); // write values to console write(format_args!("{}", logo_string), (Color::Cyan, Color::Black)); - println!("\n\n"); + println!("\n"); println!("{}", info_string); println!("{}", spacer); diff --git a/src/user/bin/mod.rs b/src/user/bin/mod.rs index 4bb531c..f90700c 100644 --- a/src/user/bin/mod.rs +++ b/src/user/bin/mod.rs @@ -7,3 +7,4 @@ pub mod shell; pub mod tasks; mod gigachad_detector; mod shellrewrite; +mod snake; diff --git a/src/user/bin/shell.rs b/src/user/bin/shell.rs index f3b6282..a1e1763 100644 --- a/src/user/bin/shell.rs +++ b/src/user/bin/shell.rs @@ -125,13 +125,13 @@ async fn exec() -> Result<(), Error> { "switch" => { Screen::switch(); } + "snake" => { + let mut game = snake::Game::new(); + game.run(Vec::new()).await; + } "gigachad?" => { let mut gigachad_detector = GigachadDetector::new(); gigachad_detector.run(args).await?; - } - "filesystem" => { - use crate::std::io; - io::mkfs(); } "test_features" => { use crate::std::random::Random; diff --git a/src/user/bin/shellrewrite.rs b/src/user/bin/shellrewrite.rs index accc895..17dfd1d 100644 --- a/src/user/bin/shellrewrite.rs +++ b/src/user/bin/shellrewrite.rs @@ -4,11 +4,7 @@ use lazy_static::lazy_static; use spin::Mutex; use x86_64::instructions::interrupts; -use alloc::{ - boxed::Box, - string::{String, ToString}, - vec::Vec, -}; +use alloc::{boxed::Box, string::{String, ToString}, vec, vec::Vec}; use crate::{ kernel::tasks::{executor::Executor, Task}, @@ -16,6 +12,8 @@ use crate::{ std::io::{print, println, Stdin, Screen}, user::bin::*, }; +use crate::std::io::{Color, write}; +use crate::user::bin::gigachad_detector::GigachadDetector; use super::*; @@ -34,59 +32,127 @@ use super::*; /// starts the shell /// this function should be directly called by main.rs or by an init system -fn new_function() { - -} -pub fn userspace() -> Result<(), String> { - let mut executor = Executor::new(); - - // - // executor.spawn(Task::new(new_function())); - // loop { - // executor.try_run() - // } +fn run_task(task_name: String, args: Vec) -> Result<(), String> { Ok(()) } -// struct Shell {} -// -// impl Application for Shell { -// fn new() -> Shell { -// Shell {} -// } -// async fn run(&mut self, _: Vec) -> Result<(), Error> { -// Ok(()) -// } -// } +pub async fn userspace() -> Result<(), String> { + let mut executor = Executor::new(); + let mut shell = Shell::new(); + shell.run(vec![]).await.unwrap(); - - -fn parse_args(command: String) -> Result<(String, Vec), String> { - let mut args: Vec = Vec::new(); - - for arg in command.split(" ").collect::>() { - match arg { - "" => {} - x => args.push(x.to_string()), - } - } - - let cmd: String; - if args.len() > 0 { - cmd = args[0].clone(); - args.remove(0); - } else { - return Err("command was empty.".to_string()); - }; - - Ok((cmd, args)) + Ok(()) +} + +struct Shell { + history: Vec, +} + +#[async_trait] +impl Application for Shell { + fn new() -> Shell { + Shell { + history: Vec::new(), + } + } + async fn run(&mut self, _: Vec) -> Result<(), Error> { + loop { + self.prompt(); + let input = Stdin::readline().await; + let (cmd, args) = self.parse_args(input).unwrap(); + self.run_cmd(cmd, args).await.unwrap(); + } + } +} + +impl Shell { + fn prompt(&mut self) { + write(format_args!("\n Crystal> "), (Color::Cyan, Color::Black)); + } + + // fn exec R>(command: T) -> Result { // this command runs when a shell command is executed + // Ok(command()) + // } + async fn run_cmd(&mut self, cmd: String, args: Vec) -> Result<(), Error> { + match cmd.as_str() { + "calculate" | "calc" | "solve" => { + let mut cmd = calc::Calculator::new(); + cmd.run(args).await?; + } + "rickroll" => { + let mut cmd = rickroll::Rickroll::new(); + cmd.run(args).await?; + } + "crystalfetch" => { + let mut cmd = crystalfetch::CrystalFetch::new(); + cmd.run(args).await?; + } + "tasks" => { + let mut cmd = tasks::Tasks::new(); + cmd.run(args).await?; + } + "play" => { + let mut gameloop = crystal_rpg::init::GameLoop::new(); + gameloop.run(args).await?; + } + "echo" => { + println!( + "Crystal: '{}'", + " ".join(args) + ) + } + "clear" => { + Screen::clear(); + } + "print" => { + use crate::std::os::OS; + let x: String = OS.lock().version.clone(); + println!("{}", x); + } + "snake" => { + let mut game = snake::Game::new(); + game.run(Vec::new()).await?; + } + "gigachad?" => { + let mut gigachad_detector = GigachadDetector::new(); + gigachad_detector.run(args).await?; + } + "test_features" => { + use crate::std::random::Random; + println!("{}", Random::int(0, 10)); + } + _ => { + return Err(Error::UnknownCommand( + "command not yet implemented".to_string(), + )) + } + } + Ok(()) + } + fn parse_args(&self, command: String) -> Result<(String, Vec), String> { + let mut args: Vec = Vec::new(); + + for arg in command.split(" ").collect::>() { + match arg { + "" => {} + x => args.push(x.to_string()), + } + } + + let cmd: String; + if args.len() > 0 { + cmd = args[0].clone(); + args.remove(0); + } + else { + return Err("command was empty.".to_string()); + }; + + Ok((cmd, args)) + } } -//fn run_binary(binary: dyn Application) -> Result, String> { -// binary.run(); -// Ok(Vec::) -//} diff --git a/src/user/bin/snake.rs b/src/user/bin/snake.rs new file mode 100644 index 0000000..6d0b888 --- /dev/null +++ b/src/user/bin/snake.rs @@ -0,0 +1,150 @@ +use alloc::string::String; +use alloc::{format, vec, vec::Vec, boxed::Box}; +use async_trait::async_trait; +use crate::std::io::{Color, Screen}; +use crate::kernel::tasks::keyboard::KEYBOARD; +use crossbeam_queue::SegQueue; +use crate::kernel::render::{ColorCode, ScreenChar}; +use crate::std::application::{Application, Error}; +use crate::std::random::Random; +use crate::system::std::frame::ColouredElement; + +#[derive(Clone, Debug, PartialEq)] +struct Point { + x: i8, + y: i8, +} + +pub struct Game { + snake: SegQueue, + head: Point, + poi: Point, + score: u8 +} + +#[async_trait] +impl Application for Game { + fn new() -> Self { + Self { + snake: SegQueue::new(), + head: Point { x: 5, y: 5 }, + poi: Point { x: 0, y: 0 }, + score: 0 + } + } + + async fn run(&mut self, _: Vec) -> Result<(), Error> { + Screen::application_mode(); + + (0..=2).for_each(|x| { + self.snake.push(Point { x, y: 5 }); + }); + self.head = Point { x: 2, y: 5 }; + self.new_poi(); + + 'gameloop: loop { + let chr = KEYBOARD.lock().get_keystroke().await; + match chr { + 'w' => self.head.y -= 1, + 'a' => self.head.x -= 1, + 's' => self.head.y += 1, + 'd' => self.head.x += 1, + 'x' => break, + _ => continue, + } + self.snake.push(Point { x: self.head.x, y: self.head.y }); // new head added + + if self.head == self.poi { + self.new_poi(); + self.score += 1 + } else { + self.snake.pop().unwrap(); // tail removed if score does not increase + } + + if self.lose_condition() { + self.render_end_screen().map_err(|_| Error::ApplicationError(String::from("failed to render game over screen")))?; + while let chr = KEYBOARD.lock().get_keystroke().await { + match chr { + 'x' => break 'gameloop, + _ => continue, + } + } + } + + let clone = self.clone_snake(); + self.render(clone).map_err(|_| Error::ApplicationError(String::from("failed to render game screen")))?; + }; + + Screen::terminal_mode(); + Ok(()) + } +} + +impl Game { + fn new_poi(&mut self) { + self.poi = Point { x: Random::int(3, 76) as i8, y: Random::int(3, 21) as i8 } + } + + fn render(&mut self, snake: Vec) -> Result<(), ()> { + let mut frame = vec![vec![ScreenChar::null(); 80]; 25]; + snake.into_iter().for_each(|p| { + frame[p.y as usize][p.x as usize] = ScreenChar::new('@' as u8, ColorCode::new(Color::Cyan, Color::Black)); + }); + + frame[self.poi.y as usize][self.poi.x as usize] = ScreenChar::new('o' as u8, ColorCode::new(Color::Red, Color::Black)); + let literal = format!("snake go brr score: {}", self.score); + let msg = Game::centre_text(80, literal); + frame[1] = msg.chars().map(|c| ScreenChar::new(c as u8, ColorCode::new(Color::LightGreen, Color::Black))).collect(); + + let mut elem = ColouredElement::generate(frame, (80, 25)); + elem.render((0,0)) + } + + fn lose_condition(&mut self) -> bool { + let cloned = self.clone_snake(); + let snake_overlaps = (1..cloned.len()).any(|i| cloned[i..].contains(&cloned[i - 1])); // checks if any part of the snake overlaps itself + let out_of_bounds = cloned.iter().filter(|p| p.x < 0 || p.y < 0 || p.x > 79 || p.y > 24).count() > 0; // checks if the snake goes out of bounds + + snake_overlaps || out_of_bounds + } + + fn centre_text(dims: usize, text: String) -> String { // centres text in a string of whitespace of a given length + let max_pad = dims / 2; + let mut msg = String::new(); + msg.push_str(" ".repeat(max_pad - round_up(text.len() as f64 / 2.0)).as_str()); + msg.push_str(text.as_str()); + msg.push_str(" ".repeat(max_pad - round_down(text.len() as f64 / 2.0 + 0.51)).as_str()); + msg + } + + fn render_end_screen(&mut self) -> Result<(), ()> { + let mut frame = vec![vec![ScreenChar::null(); 80]; 25]; + + frame[10] = Game::centre_text(80, String::from("u lost")).chars().map(|c| ScreenChar::new(c as u8, ColorCode::new(Color::Red, Color::Black))).collect(); + frame[12] = Game::centre_text(80, String::from(format!("ur score was {}", self.score))).chars().map(|c| ScreenChar::new(c as u8, ColorCode::new(Color::LightGreen, Color::Black))).collect(); + frame[14] = Game::centre_text(80, String::from("L bozo")).chars().map(|c| ScreenChar::new(c as u8, ColorCode::new(Color::Red, Color::Black))).collect(); + + + let mut elem = ColouredElement::generate(frame, (80, 25)); + elem.render((0,0)) + } + + fn clone_snake(&mut self) -> Vec { + let mut cloned= Vec::new(); + let mut snake = SegQueue::new(); + while !self.snake.is_empty() { + let item = self.snake.pop().unwrap(); + cloned.push(item.clone()); + snake.push(item); + } + self.snake = snake; + cloned + } +} + +fn round_up(n: f64) -> usize { + (n + 0.99) as usize +} +fn round_down(n: f64) -> usize { + n as usize +} diff --git a/src/user/lib/libgui/libgui_core.rs b/src/user/lib/libgui/libgui_core.rs index cf9b45b..458eb82 100644 --- a/src/user/lib/libgui/libgui_core.rs +++ b/src/user/lib/libgui/libgui_core.rs @@ -1,5 +1,5 @@ -use crate::kernel::render::{BUFFER_HEIGHT, BUFFER_WIDTH, RENDERER}; -use crate::std::io::Frame; +use crate::kernel::render::{BUFFER_HEIGHT, BUFFER_WIDTH, RENDERER, ScreenChar}; +use crate::system::std::frame::Frame; use crate::{print, println}; use alloc::{ boxed::Box, @@ -154,7 +154,7 @@ impl Element for IndicatorBar { // rendered. pub fn render_frame(elements: Vec) { - let mut buffer: Frame = [[' '; BUFFER_WIDTH]; BUFFER_HEIGHT]; + let mut buffer: Frame = [[ScreenChar::null(); BUFFER_WIDTH]; BUFFER_HEIGHT]; for frame in elements.iter() { let f = frame.render(); @@ -162,8 +162,8 @@ pub fn render_frame(elements: Vec) { for (i, row) in f.0.iter().enumerate() { for (j, chr) in row.iter().enumerate() { let mut current = &buffer[i + f.1.y][j + f.1.x]; - let newchar = overlap_check(*current, *chr); - buffer[i + f.1.y][j + f.1.x] = newchar; + let newchar = overlap_check(current.character as char, *chr); + buffer[i + f.1.y][j + f.1.x] = ScreenChar::white(newchar as u8); //print!("{}", buffer[i+frame.position.1][j+frame.position.0]); }