diff options
Diffstat (limited to 'toolkit/src/lib.rs')
-rw-r--r-- | toolkit/src/lib.rs | 491 |
1 files changed, 165 insertions, 326 deletions
diff --git a/toolkit/src/lib.rs b/toolkit/src/lib.rs index a282c37..e1f9c33 100644 --- a/toolkit/src/lib.rs +++ b/toolkit/src/lib.rs @@ -1,357 +1,196 @@ -#![no_std] - -extern crate alloc; - -use alloc::string::{String, ToString}; -use alloc::{format, vec}; -use alloc::collections::BTreeMap; -use alloc::vec::Vec; -use uefi::{Char16, CStr16}; -use uefi::prelude::*; -use uefi::fs::{Path, UefiDirectoryIter}; -use uefi::proto::console::text::{Color, Key, ScanCode}; -use uefi::fs::FileSystem; -use uefi::table::runtime::ResetType; -use crate::error::{display_error, Error}; - -pub mod error; - -pub static mut CPU_ARCHITECTURE: &str = "unknown"; - -pub struct ToolkitWorkingPath { - name: String -} - -pub struct ToolkitVersionInfo { - pub version: &'static str, - pub timestamp: &'static str, - pub compiler: &'static str, - pub profile: &'static str -} - -pub struct Toolkit { - system_table: SystemTable<Boot>, - pub current_directory: ToolkitWorkingPath, - pub version: ToolkitVersionInfo, - pub globals: BTreeMap<String, String> +use std::time::Duration; +use embedded_graphics_framebuffer::FrameBufferDisplay; +use embedded_graphics::{ + pixelcolor::{Rgb888, RgbColor}, + prelude::*, +}; +use framebuffer::{Framebuffer, KdMode}; +use embedded_graphics::mono_font::ascii::FONT_10X20; +use embedded_graphics::mono_font::MonoTextStyle; +use embedded_graphics::primitives::{PrimitiveStyleBuilder, Rectangle}; +use embedded_graphics::text::Text; +use chrono::Local; +use crossterm::event::{Event, KeyCode, KeyEvent, KeyModifiers, poll, read}; + +pub struct GraphicalApp { + list_enabled: bool, + list_items: Vec<MenuItem>, + list_max_length: u32, + list_focused_item: u32, + list_name: String, + app_name: String, + display: FrameBufferDisplay } -impl Toolkit { - pub fn new(system_table: SystemTable<Boot>, version: ToolkitVersionInfo) -> Self { - unsafe { - CPU_ARCHITECTURE = "unknown"; - - #[cfg(target_arch = "x86_64")] { - CPU_ARCHITECTURE = "x86_64"; - } - - #[cfg(target_arch = "aarch64")] { - CPU_ARCHITECTURE = "aarch64"; +impl GraphicalApp { + pub fn new(app_name: &str) -> GraphicalApp { + let display = FrameBufferDisplay::new(); + + GraphicalApp { + list_enabled: false, + list_items: vec![], + list_max_length: 0, + list_name: String::new(), + list_focused_item: 0, + app_name: String::from(app_name), + display + } + } + + fn update_display(&mut self) { + let mut display: &mut FrameBufferDisplay = &mut self.display; + display.clear(RgbColor::WHITE).expect("Failed to clear the screen"); + + Rectangle::new(Point::new(-1, -1), Size::new(display.size().width + 2, 26)) + .into_styled(PrimitiveStyleBuilder::new() + .fill_color(Rgb888::CSS_DARK_GRAY) + .stroke_width(1) + .stroke_color(Rgb888::BLACK) + .build()) + .draw(display).expect("Failed to draw"); + + let date = Local::now(); + let string = date.format("%h %e %H:%M").to_string(); + let text = string.as_str(); + Text::new(text, Point::new((display.size().width - text.len() as u32 * 10u32 - 20) as i32, 18), MonoTextStyle::new(&FONT_10X20, Rgb888::BLACK)).draw(display) + .expect("Failed to draw"); + + Text::new(&self.app_name, Point::new(20, 18), MonoTextStyle::new(&FONT_10X20, Rgb888::BLACK)).draw(display) + .expect("Failed to draw"); + + if self.list_enabled { + Text::new(&self.list_name, Point::new(50, 70), MonoTextStyle::new(&FONT_10X20, Rgb888::BLACK)).draw(display) + .expect("Failed to draw"); + + for (i, item) in (&self.list_items).iter().enumerate() { + if i == self.list_focused_item as usize { + Rectangle::new(Point::new(40, (90 + (i * 20)) as i32), Size::new(self.list_max_length * 10 + 20, 20)) + .into_styled(PrimitiveStyleBuilder::new() + .fill_color(Rgb888::CSS_NAVY) + .build()) + .draw(display).expect("Failed to draw"); + + item.show(&mut display, i, Rgb888::WHITE); + } else { + item.show(&mut display, i, Rgb888::new(90, 90, 90)); + } } } - Self { - system_table, - current_directory: ToolkitWorkingPath { - name: String::from("") - }, - version, - globals: BTreeMap::new() - } - } -} - -impl Toolkit { - fn get_fs(&self) -> FileSystem { - let handle = self.system_table.boot_services().image_handle(); - let fs = self.system_table.boot_services().get_image_file_system(handle).expect("Failed to start up filesystem"); - FileSystem::new(fs) - } - - pub fn color(&mut self, fg: Color, bg: Color) { - self.system_table.stdout().set_color(fg, bg).expect("Failed to set screen color"); + display.flush().unwrap(); } - pub fn prompt(&mut self) -> String { - let mut out: String = String::from(""); - let mut chars: u32 = 0; - - loop { - let mut events = [self.system_table.stdin().wait_for_key_event().unwrap()]; - self.system_table.boot_services() - .wait_for_event(&mut events) - .discard_errdata().expect("Failed to discard errors"); + pub fn initialize_graphics<F: FnOnce(&mut GraphicalApp)>(&mut self, after_init: F) { + Framebuffer::set_kd_mode(KdMode::Graphics).unwrap(); + crossterm::terminal::enable_raw_mode().expect("Failed to configure keyboard"); - let ret = Char16::try_from('\r').unwrap(); - let bks = Char16::try_from('\x08').unwrap(); - let ctc = Char16::try_from('\u{3}').unwrap(); - let ctl = Char16::try_from('\u{c}').unwrap(); - match self.system_table.stdin().read_key().expect("Failed to read key") { - Some(Key::Printable(key)) if key == ret => { - self.print("\r\n"); - return out; - } + let mut first_updated = false; + let date = Local::now(); + let mut time = date.format("%h %e %H:%M").to_string(); - Some(Key::Printable(key)) if key == bks => { - if chars > 0 { - chars -= 1; - out = String::from(&out[..out.len() - 1]); - self.print("\x08"); - } - } + after_init(self); - Some(Key::Printable(key)) if key == ctc => { - self.print("\r\n"); - return String::from(""); - } + 'main_loop: loop { + let mut update = false || !first_updated; - Some(Key::Printable(key)) if key == ctl => { - self.print("\r\n"); - self.clear(); - return String::from(""); - } + let date = Local::now(); + let new_time = date.format("%h %e %H:%M").to_string(); - Some(Key::Printable(key)) => { - chars += 1; - out += &key.to_string(); - self.print(&key.to_string()); - } + if time != new_time { + update = true; + let date = Local::now(); + time = date.format("%h %e %H:%M").to_string(); + } - Some(Key::Special(ScanCode::ESCAPE)) => { - panic!("Pressed Escape") + if poll(Duration::from_millis(100)).unwrap() { + match read().unwrap() { + Event::Key(event) => { + match event { + KeyEvent { + code: KeyCode::Char('c'), + modifiers: KeyModifiers::CONTROL, + .. + } => { + Framebuffer::set_kd_mode(KdMode::Text).unwrap(); + crossterm::terminal::disable_raw_mode().expect("Failed to unconfigure keyboard"); + break 'main_loop; + }, + KeyEvent { + code: KeyCode::Up, + .. + } => { + if self.list_enabled { + if self.list_focused_item > 0 { + self.list_focused_item -= 1 + } else { + self.list_focused_item = (self.list_items.len() - 1) as u32; + } + } + + update = true; + }, + KeyEvent { + code: KeyCode::Down, + .. + } => { + if self.list_enabled { + if self.list_focused_item + 1 < self.list_items.len() as u32 { + self.list_focused_item += 1 + } else { + self.list_focused_item = 0; + } + } + + update = true; + }, + _ => { + update = true; + } + } + }, + _ => { + update = true; + } } - - _ => {} } - } - } - - pub fn clear(&mut self) { - let stdout = self.system_table.stdout(); - stdout.reset(false).expect("Failed to clear screen buffer"); - } - - pub fn get_cwd(&self) -> &str { - &self.current_directory.name - } - - pub fn chdir(&mut self, dir: &str) { - if dir == "." { - return; - } - - if !self.file_exists(dir) { - panic!("Attempted to change the working directory to a non-existent directory"); - } - - self.current_directory.name = format!("/{}", dir).replace('\\', "/"); - } - - pub fn uefi_version(&mut self) -> String { - self.system_table.uefi_revision().to_string() - } - - pub fn uefi_vendor(&mut self) -> String { - self.system_table.firmware_vendor().to_string() - } - - pub fn uefi_revision(&mut self) -> u32 { - self.system_table.firmware_revision() - } - - pub fn uefi_arch(&mut self) -> String { - unsafe { - CPU_ARCHITECTURE.to_string() - } - } - - pub fn set_cursor(&mut self, state: bool) { - let stdout = self.system_table.stdout(); - stdout.enable_cursor(state).expect("Failed to change cursor state"); - } - - pub fn poweroff(&mut self, reboot: bool) { - self.system_table.runtime_services().reset(if reboot { - ResetType::COLD - } else { - ResetType::SHUTDOWN - }, Status::SUCCESS, None); - } - - pub fn disable_watchdog(&self) { - self.system_table.boot_services().set_watchdog_timer(0, 65536, None).expect("Failed to disable the watchdog"); - } - - pub fn file_exists(&self, path: &str) -> bool { - if path.is_empty() { - return true; - } - let mut filesystem = self.get_fs(); - let mut buf: Vec<u16> = vec![0; path.len() + 1]; - return filesystem.try_exists(Path::new(&CStr16::from_str_with_buf(path, &mut buf).unwrap())).unwrap_or(false); - } - - pub fn rename(&mut self, old: &str, new: &str) { - let mut filesystem = self.get_fs(); - let mut buf: Vec<u16> = vec![0; old.len() + 1]; - let mut buf2: Vec<u16> = vec![0; new.len() + 1]; - filesystem.rename(Path::new(&CStr16::from_str_with_buf(old, &mut buf).unwrap()), Path::new(&CStr16::from_str_with_buf(new, &mut buf2).unwrap())).expect("Failed to move"); - } - - pub fn copy_file(&mut self, old: &str, new: &str) { - let mut filesystem = self.get_fs(); - let mut buf: Vec<u16> = vec![0; old.len() + 1]; - let mut buf2: Vec<u16> = vec![0; new.len() + 1]; - filesystem.copy(Path::new(&CStr16::from_str_with_buf(old, &mut buf).unwrap()), Path::new(&CStr16::from_str_with_buf(new, &mut buf2).unwrap())).expect("Failed to move"); - } - - pub fn is_dir(&self, path: &str) -> bool { - if path.is_empty() { - return true; - } - let mut filesystem = self.get_fs(); - let mut buf: Vec<u16> = vec![0; path.len() + 1]; - return match filesystem.metadata(Path::new(&CStr16::from_str_with_buf(path, &mut buf).unwrap())) { - Ok(b) => b.is_directory(), - Err(_) => false - }; - } - - pub fn is_file(&self, path: &str) -> bool { - if path.is_empty() { - return true; - } - let mut filesystem = self.get_fs(); - let mut buf: Vec<u16> = vec![0; path.len() + 1]; - return match filesystem.metadata(Path::new(&CStr16::from_str_with_buf(path, &mut buf).unwrap())) { - Ok(b) => b.is_regular_file(), - Err(_) => false - }; - } - - pub fn read_file(&self, path: &str) -> Option<String> { - if path.is_empty() { - return None; - } - let mut filesystem = self.get_fs(); - let mut buf: Vec<u16> = vec![0; path.len() + 1]; - return match filesystem.read_to_string(Path::new(&CStr16::from_str_with_buf(path, &mut buf).unwrap())) { - Ok(b) => Some(b), - Err(_) => None - }; - } - pub fn write_file(&self, path: &str, text: &str) { - if path.is_empty() { - return; - } - let mut filesystem = self.get_fs(); - let mut buf: Vec<u16> = vec![0; path.len() + 1]; - filesystem.write(Path::new(&CStr16::from_str_with_buf(path, &mut buf).unwrap()), text.as_bytes()).unwrap(); - } - - pub fn scandir(&self, path: &str) -> UefiDirectoryIter { - let mut filesystem = self.get_fs(); - let mut buf: Vec<u16> = vec![0; path.len() + 1]; - return match filesystem.read_dir(Path::new(&CStr16::from_str_with_buf(path, &mut buf).unwrap())) { - Ok(b) => b, - Err(_) => panic!("Failed to scan directory {}", path) - }; - } - - pub fn execute_file(&mut self, path: &str) { - if path.ends_with(".co") { - match self.read_file(&path) { - None => { - display_error(self, Error::E13); - } - Some(data) => { - self.println(&data.replace("\\r", "\\n").replace("\\n", "\\r\\n")); - } + if update { + first_updated = true; + self.update_display(); } - } else { - display_error(self, Error::E14); } } - pub fn resolve_path(&self, orig_og_path: &str) -> String { - let og_path = orig_og_path.replace('\\', "/"); - let mut final_path = self.get_cwd().to_string().replace('/', "\\"); - let path = &og_path.replace('/', "\\"); - let mut buf = vec![0; path.len() + 1]; - let cstr = CStr16::from_str_with_buf(path, &mut buf).unwrap(); - let path = Path::new(cstr); - - if og_path.starts_with('/') { - final_path = String::from(""); - } - - for i in path.components() { - if i.to_string() == ".." { - let parts = final_path.split('\\').collect::<Vec<&str>>(); - let len = parts.len() - 1; + pub fn menu(&mut self, items: Vec<MenuItem>, name: &str) { + self.list_items = items; + self.list_enabled = true; + self.list_max_length = 0; + self.list_focused_item = 0; + self.list_name = name.parse().unwrap(); - final_path = parts[..len].join("\\"); - } else if i.to_string() != "." { - final_path = format!("{}\\{}", final_path, i).replace('/', "\\"); + for item in &self.list_items { + if item.text.len() as u32 > self.list_max_length { + self.list_max_length = item.text.len() as u32; } } - if !final_path.is_empty() { - final_path[1..].to_string().replace("\\\\", "\\") - } else { - final_path.to_string().replace("\\\\", "\\") - } + self.update_display(); } +} - pub fn make_readable(&self, _size: u64) -> String { - let size = _size as f64; +pub struct MenuItem { + text: String +} - if size > 1099511627776.0 { - format!("{:.1}T", size / 1099511627776.0) - } else if size > 1073741824.0 { - format!("{:.1}G", size / 1073741824.0) - } else if size > 1048576.0 { - format!("{:.1}M", size / 1048576.0) - } else if size > 1024.0 { - format!("{:.1}K", size / 1024.0) - } else { - format!("{}B", size) +impl MenuItem { + pub fn new(text: &str) -> Self { + MenuItem { + text: String::from(text) } } - pub fn mkdir(&self, path: &str) { - let mut filesystem = self.get_fs(); - let mut buf: Vec<u16> = vec![0; path.len() + 1]; - filesystem.create_dir_all(Path::new(&CStr16::from_str_with_buf(path, &mut buf).unwrap())).unwrap(); - } - - pub fn rmdir(&mut self, path: &str) { - let mut filesystem = self.get_fs(); - let mut buf: Vec<u16> = vec![0; path.len() + 1]; - filesystem.remove_dir(Path::new(&CStr16::from_str_with_buf(path, &mut buf).unwrap())).unwrap(); - } - - pub fn unlink(&mut self, path: &str) { - let mut filesystem = self.get_fs(); - let mut buf: Vec<u16> = vec![0; path.len() + 1]; - filesystem.remove_file(Path::new(&CStr16::from_str_with_buf(path, &mut buf).unwrap())).unwrap(); - } - - pub fn recursive_rmdir(&mut self, path: &str) { - let mut filesystem = self.get_fs(); - let mut buf: Vec<u16> = vec![0; path.len() + 1]; - filesystem.remove_dir_all(Path::new(&CStr16::from_str_with_buf(path, &mut buf).unwrap())).unwrap(); - } - - pub fn print(&mut self, str: &str) { - let stdout = self.system_table.stdout(); - let mut buf = vec![0; str.len() + 1]; - stdout.output_string(CStr16::from_str_with_buf(str, &mut buf).expect("Failed to format text")).expect("Failed to print"); + pub fn show(&self, display: &mut FrameBufferDisplay, offset: usize, color: Rgb888) { + Text::new(&self.text, Point::new(50, (105 + (offset * 20)) as i32), MonoTextStyle::new(&FONT_10X20, color)).draw(display) + .expect("Failed to draw"); } - - pub fn println(&mut self, str: &str) { - self.print(str); - self.print("\r\n"); - } -} +}
\ No newline at end of file |