From 2236c1a00c5c47bb17875ec63e7162fc721eacbb Mon Sep 17 00:00:00 2001 From: Jaby Date: Sun, 9 Jul 2023 14:40:44 +0200 Subject: [PATCH] Setup of update and render loop for CUI --- src/Tools/psxreadmap/Cargo.toml | 4 +- src/Tools/psxreadmap/src/lib.rs | 54 +++++++++++++++++ src/Tools/psxreadmap/src/main.rs | 101 +++++++++++++++++-------------- src/Tools/tool_helper/src/lib.rs | 6 ++ 4 files changed, 118 insertions(+), 47 deletions(-) create mode 100644 src/Tools/psxreadmap/src/lib.rs diff --git a/src/Tools/psxreadmap/Cargo.toml b/src/Tools/psxreadmap/Cargo.toml index 47575be5..de0875f9 100644 --- a/src/Tools/psxreadmap/Cargo.toml +++ b/src/Tools/psxreadmap/Cargo.toml @@ -7,5 +7,7 @@ edition = "2021" [dependencies] clap = {version = "*", features = ["derive"]} +crossterm = "*" readmap = {version = "*", path = "readmap"} -tool_helper = {version = "*", path = "../tool_helper"} \ No newline at end of file +tool_helper = {version = "*", path = "../tool_helper"} +tui = "*" \ No newline at end of file diff --git a/src/Tools/psxreadmap/src/lib.rs b/src/Tools/psxreadmap/src/lib.rs new file mode 100644 index 00000000..5b3ba1ae --- /dev/null +++ b/src/Tools/psxreadmap/src/lib.rs @@ -0,0 +1,54 @@ +use crossterm::event::KeyCode; +use readmap::types::Section; +use std::path::PathBuf; +use tool_helper::Error; + +pub type EventReceiver = std::sync::mpsc::Receiver>; +pub type Terminal = tui::Terminal>; + +pub enum Event { + Input(I), + Tick, +} + +pub enum UIState { + Alive, + Terminated +} + +pub struct ConsoleUI { + rx: EventReceiver, + terminal: Terminal +} + +impl ConsoleUI { + pub fn new(rx: EventReceiver, terminal: Terminal) -> ConsoleUI { + ConsoleUI{rx, terminal} + } + + pub fn update(&self) -> Result { + match self.rx.recv()? { + Event::Input(event) => { + match event.code { + KeyCode::Char('q') => Ok(UIState::Terminated), + _ => Ok(UIState::Alive) + } + }, + Event::Tick => Ok(UIState::Alive) + } + } + + pub fn render(&self) {} + + pub fn close(mut self) -> Result<(), Error> { + crossterm::terminal::disable_raw_mode()?; + self.terminal.show_cursor()?; + self.terminal.clear()?; + + Ok(()) + } +} + +pub fn load_memory_map(file_path: Option) -> Result, Error> { + readmap::scan(tool_helper::open_input(file_path)?) +} \ No newline at end of file diff --git a/src/Tools/psxreadmap/src/main.rs b/src/Tools/psxreadmap/src/main.rs index 91445a6f..9fc61015 100644 --- a/src/Tools/psxreadmap/src/main.rs +++ b/src/Tools/psxreadmap/src/main.rs @@ -1,7 +1,9 @@ use clap::Parser; -use readmap::types::{Content::*, Section}; +use crossterm::{event::{self, Event as CEvent}, terminal}; +use psxreadmap::{ConsoleUI, Event, EventReceiver, Terminal, load_memory_map}; +use std::{io, path::PathBuf, sync::mpsc, thread, time::{Duration, Instant}}; use tool_helper::{Error, exit_with_error}; -use std::{fs::File, io::{BufWriter, Write}, path::PathBuf}; +use tui::backend::CrosstermBackend; #[derive(Parser)] #[clap(about = "Opens and scans a MAP file to print extended information", long_about = None)] @@ -10,50 +12,6 @@ struct CommandLine { input: Option } -fn run_main(cmd: CommandLine) -> Result<(), Error> { - fn value_to_hex(value: T) -> String { - return format!("0x{:X}", value); - } - - fn option_to_hex(value: Option) -> String { - if let Some(value) = value { - return value_to_hex(value); - } - - else { - return String::from(""); - } - } - - let sections = readmap::scan(tool_helper::open_input(cmd.input)?)?; - let mut file = tool_helper::open_output_file(&PathBuf::from("./planschi.txt"))?; - - for section in sections { - fn print_content(tab_level: usize, file: &mut BufWriter, section: Section) -> Result<(), Error> { - for content in section.content { - match content { - Fill(fill) => { - writeln!(file, "{:>tab_level$}*fill* @{}, {}", ' ', value_to_hex(fill.adr), value_to_hex(fill.size), tab_level=tab_level*4)?; - }, - Section(section) => { - writeln!(file, "{:>tab_level$} {} @{}, {}", ' ', section.name, option_to_hex(section.adr), option_to_hex(section.size), tab_level=tab_level*4)?; - print_content(tab_level + 1, file, section)?; - }, - Symbol(symbol) => { - writeln!(file, "{:>tab_level$}{} @{}", ' ', symbol.name, value_to_hex(symbol.adr), tab_level=tab_level*4)?; - } - } - } - - Ok(()) - } - - writeln!(file, "{}: @{}, {}", section.name, option_to_hex(section.adr), option_to_hex(section.size))?; - print_content(1, &mut file, section)?; - } - Ok(()) -} - pub fn main() { match CommandLine::try_parse() { Ok(cmd_line) => { @@ -66,4 +24,55 @@ pub fn main() { println!("{}", error) } } +} + +fn run_main(cmd: CommandLine) -> Result<(), Error> { + let _sections = load_memory_map(cmd.input)?; + let rx = start_event_loop(); + let terminal = setup_console()?; + let console_ui = ConsoleUI::new(rx, terminal); + + while matches!(console_ui.update()?, psxreadmap::UIState::Alive) { + console_ui.render(); + } + + console_ui.close() +} + +fn start_event_loop() -> EventReceiver { + // Set up a mpsc (multiproducer, single consumer) channel to communicate between the input handler and the rendering loop. + let (tx, rx) = mpsc::channel(); + let tick_rate = Duration::from_millis(200); + thread::spawn(move || { + let mut last_tick = Instant::now(); + loop { + let timeout = tick_rate.checked_sub(last_tick.elapsed()).unwrap_or_else(|| Duration::from_secs(0)); + + if event::poll(timeout).expect("Event poll working") { + if let CEvent::Key(key) = event::read().expect("Can read key input") { + tx.send(Event::Input(key)).expect("Can send KeyInput"); + } + } + + if last_tick.elapsed() >= tick_rate { + if let Ok(_) = tx.send(Event::Tick) { + last_tick = Instant::now(); + } + } + } + }); + + rx +} + +fn setup_console() -> Result { + terminal::enable_raw_mode()?; + + // Setup Crossterm for the Terminal + let stdout = io::stdout(); + let backend = CrosstermBackend::new(stdout); + let mut terminal = Terminal::new(backend)?; + + terminal.clear()?; + Ok(terminal) } \ No newline at end of file diff --git a/src/Tools/tool_helper/src/lib.rs b/src/Tools/tool_helper/src/lib.rs index 12d8c1a6..e76d919c 100644 --- a/src/Tools/tool_helper/src/lib.rs +++ b/src/Tools/tool_helper/src/lib.rs @@ -114,6 +114,12 @@ impl std::convert::From for Error { } } +impl std::convert::From for Error { + fn from(error: std::sync::mpsc::RecvError) -> Self { + Error::from_error(error) + } +} + pub fn exit_with_error(error: Error) { error.print_to_std_err(); std::process::exit(error.exit_code);