Setup of update and render loop for CUI
This commit is contained in:
parent
ac4be64d56
commit
b446057dfe
|
@ -7,5 +7,7 @@ edition = "2021"
|
||||||
|
|
||||||
[dependencies]
|
[dependencies]
|
||||||
clap = {version = "*", features = ["derive"]}
|
clap = {version = "*", features = ["derive"]}
|
||||||
|
crossterm = "*"
|
||||||
readmap = {version = "*", path = "readmap"}
|
readmap = {version = "*", path = "readmap"}
|
||||||
tool_helper = {version = "*", path = "../tool_helper"}
|
tool_helper = {version = "*", path = "../tool_helper"}
|
||||||
|
tui = "*"
|
|
@ -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<Event<crossterm::event::KeyEvent>>;
|
||||||
|
pub type Terminal = tui::Terminal<tui::backend::CrosstermBackend<std::io::Stdout>>;
|
||||||
|
|
||||||
|
pub enum Event<I> {
|
||||||
|
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<UIState, Error> {
|
||||||
|
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<PathBuf>) -> Result<Vec<Section>, Error> {
|
||||||
|
readmap::scan(tool_helper::open_input(file_path)?)
|
||||||
|
}
|
|
@ -1,7 +1,9 @@
|
||||||
use clap::Parser;
|
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 tool_helper::{Error, exit_with_error};
|
||||||
use std::{fs::File, io::{BufWriter, Write}, path::PathBuf};
|
use tui::backend::CrosstermBackend;
|
||||||
|
|
||||||
#[derive(Parser)]
|
#[derive(Parser)]
|
||||||
#[clap(about = "Opens and scans a MAP file to print extended information", long_about = None)]
|
#[clap(about = "Opens and scans a MAP file to print extended information", long_about = None)]
|
||||||
|
@ -10,50 +12,6 @@ struct CommandLine {
|
||||||
input: Option<PathBuf>
|
input: Option<PathBuf>
|
||||||
}
|
}
|
||||||
|
|
||||||
fn run_main(cmd: CommandLine) -> Result<(), Error> {
|
|
||||||
fn value_to_hex<T: std::fmt::UpperHex>(value: T) -> String {
|
|
||||||
return format!("0x{:X}", value);
|
|
||||||
}
|
|
||||||
|
|
||||||
fn option_to_hex<T: std::fmt::UpperHex>(value: Option<T>) -> String {
|
|
||||||
if let Some(value) = value {
|
|
||||||
return value_to_hex(value);
|
|
||||||
}
|
|
||||||
|
|
||||||
else {
|
|
||||||
return String::from("<None>");
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
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<File>, 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() {
|
pub fn main() {
|
||||||
match CommandLine::try_parse() {
|
match CommandLine::try_parse() {
|
||||||
Ok(cmd_line) => {
|
Ok(cmd_line) => {
|
||||||
|
@ -66,4 +24,55 @@ pub fn main() {
|
||||||
println!("{}", error)
|
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, Error> {
|
||||||
|
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)
|
||||||
}
|
}
|
|
@ -114,6 +114,12 @@ impl std::convert::From<std::convert::Infallible> for Error {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
impl std::convert::From<std::sync::mpsc::RecvError> for Error {
|
||||||
|
fn from(error: std::sync::mpsc::RecvError) -> Self {
|
||||||
|
Error::from_error(error)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
pub fn exit_with_error(error: Error) {
|
pub fn exit_with_error(error: Error) {
|
||||||
error.print_to_std_err();
|
error.print_to_std_err();
|
||||||
std::process::exit(error.exit_code);
|
std::process::exit(error.exit_code);
|
||||||
|
|
Loading…
Reference in New Issue