From 35c81d5f7b273c6f4df59cbce40d89edceea723c Mon Sep 17 00:00:00 2001 From: Vincent Foulon Date: Sun, 10 Oct 2021 11:04:10 +0200 Subject: [PATCH] Use console_engine instead of crossterm fix --- Cargo.lock | 9 +- Cargo.toml | 15 ++- src/terminal.rs | 311 ++++++++++++++++++++++++------------------------ 3 files changed, 166 insertions(+), 169 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index 53c9f33..e4f36bc 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -101,9 +101,9 @@ dependencies = [ [[package]] name = "console_engine" -version = "2.0.1" +version = "2.1.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "2806f9623af3ee69d6d96e68b42167d738f5ead1dc2330cce55e84643fca1f71" +checksum = "905a8238b9ba588376ddea1b212cbcc0816341ec69da2e334d01e35ecd72faf9" dependencies = [ "crossterm", "unicode-width", @@ -111,9 +111,9 @@ dependencies = [ [[package]] name = "crossterm" -version = "0.20.0" +version = "0.21.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c0ebde6a9dd5e331cd6c6f48253254d117642c31653baa475e394657c59c1f7d" +checksum = "486d44227f71a1ef39554c0dc47e44b9f4139927c75043312690c3f476d1d788" dependencies = [ "bitflags", "crossterm_winapi", @@ -170,7 +170,6 @@ dependencies = [ "ciborium", "clap", "console_engine", - "crossterm", "drawille", "env_logger", "log", diff --git a/Cargo.toml b/Cargo.toml index cee15c9..322ca54 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -1,19 +1,18 @@ [package] -name = "gb_int" -version = "0.1.0" authors = ["Blake Loring "] edition = "2018" +name = "gb_int" +version = "0.1.0" [dependencies] -log = "0.4" +ciborium = "0.1.0" +clap = "3.0.0-beta.2" +console_engine = {version = "2.1.0", features = ['event']} +drawille = "0.3.0" env_logger = "0.7.1" +log = "0.4" sdl2 = "0.34.3" -console_engine = "2.0.1" -drawille = "0.3.0" -crossterm = "0.20.0" -clap = "3.0.0-beta.2" serde = "1.0.130" -ciborium = "0.1.0" [profile.dev] overflow-checks = false diff --git a/src/terminal.rs b/src/terminal.rs index 422a19a..0024a8d 100644 --- a/src/terminal.rs +++ b/src/terminal.rs @@ -1,16 +1,11 @@ -use crossterm::{ - cursor::{Hide, MoveTo}, - event::{poll, read, Event, KeyCode}, - execute, queue, - style::Print, - terminal::{ - disable_raw_mode, enable_raw_mode, size, Clear, ClearType, EnterAlternateScreen, - LeaveAlternateScreen, SetSize, - }, -}; +use console_engine::events::Event; +use console_engine::Color; +use console_engine::ConsoleEngine; +use console_engine::KeyCode; + +use console_engine::pixel; use drawille::{Canvas, PixelColor}; -use std::io::{self, stdout, Write}; -use std::time::Duration; +use std::io::{self}; use std::time::SystemTime; use crate::cpu::{Cpu, JOYPAD}; @@ -31,18 +26,9 @@ pub fn run( let mut pixel_buffer = vec![0; GB_SCREEN_WIDTH as usize * GB_SCREEN_HEIGHT as usize * 3]; let mut canvas = Canvas::new(GB_SCREEN_WIDTH, GB_SCREEN_HEIGHT); - let (cols, rows) = size()?; - let frame_width = canvas.frame().lines().next().unwrap().len(); let frame_height = canvas.frame().lines().count(); - - execute!( - stdout(), - SetSize(frame_width as u16, frame_height as u16), - EnterAlternateScreen, - Hide, - Clear(ClearType::All) - )?; + let mut engine = ConsoleEngine::init(frame_width as u32, frame_height as u32, 60)?; // Simulate long keypresses because the terminal does not send // keyup and keydown events @@ -61,8 +47,6 @@ pub fn run( } } - enable_raw_mode()?; - // Screen redraw is controlled by the frame timer // But we don't want the game to get too ahead of // the screen so we stop stepping the emulator after @@ -72,158 +56,173 @@ pub fn run( let mut frame_timer = FrameTimer::new(frameskip_rate); loop { - if emulator_running_fast { - if emulator_timer.should_redraw() { - emulator_running_fast = false; - } + if emulator_running_fast && emulator_timer.should_redraw() { + emulator_running_fast = false; } if !emulator_running_fast { let state = gameboy_state.step(&mut pixel_buffer); - match state { - PpuStepState::VBlank => { - let mut save = false; - let mut state = &mut gameboy_state.state.memory; - - state.start = false; - state.select = false; - - if poll(Duration::from_millis(0))? { - // It's guaranteed that the `read()` won't block when the `poll()` - // function returns `true` - match read()? { - Event::Key(event) => { - let mut fired = false; - match event.code { - KeyCode::Left => { - fired = fired | !is_key(last_left); - last_left = Some(SystemTime::now()); - } - KeyCode::Right => { - fired = fired | !is_key(last_right); - last_right = Some(SystemTime::now()); - } - KeyCode::Up => { - fired = fired | !is_key(last_up); - last_up = Some(SystemTime::now()); - } - KeyCode::Down => { - fired = fired | !is_key(last_down); - last_down = Some(SystemTime::now()); - } - KeyCode::Char('a') => { - fired = fired | !is_key(last_a); - last_a = Some(SystemTime::now()); - } - KeyCode::Char('b') => { - fired = fired | !is_key(last_b); - last_b = Some(SystemTime::now()); - } - KeyCode::Char('n') => { - state.start = true; - fired = true; - } - KeyCode::Char('m') => { - state.select = true; - fired = true; - } - KeyCode::Char('p') => { - redrawing = !redrawing; - } - KeyCode::Char('s') => { - save = true; - } - KeyCode::Char('q') => { - break; - } - _ => {} - } - if fired { - Cpu::set_interrupt_happened(state, JOYPAD); - } + if let PpuStepState::VBlank = state { + let mut save = false; + let mut state = &mut gameboy_state.state.memory; + + state.start = false; + state.select = false; + + match engine.poll() { + Event::Key(event) => { + let mut fired = false; + match event.code { + KeyCode::Left => { + fired |= !is_key(last_left); + last_left = Some(SystemTime::now()); + } + KeyCode::Right => { + fired |= !is_key(last_right); + last_right = Some(SystemTime::now()); + } + KeyCode::Up => { + fired |= !is_key(last_up); + last_up = Some(SystemTime::now()); + } + KeyCode::Down => { + fired |= !is_key(last_down); + last_down = Some(SystemTime::now()); + } + KeyCode::Char('a') => { + fired |= !is_key(last_a); + last_a = Some(SystemTime::now()); + } + KeyCode::Char('b') => { + fired |= !is_key(last_b); + last_b = Some(SystemTime::now()); + } + KeyCode::Char('n') => { + state.start = true; + fired = true; + } + KeyCode::Char('m') => { + state.select = true; + fired = true; + } + KeyCode::Char('p') => { + redrawing = !redrawing; + } + KeyCode::Char('s') => { + save = true; + } + KeyCode::Char('q') => { + break; } _ => {} } + if fired { + Cpu::set_interrupt_happened(state, JOYPAD); + } } + Event::Frame => { + // Redraw only every other frame to help with flashing + if frame_timer.should_redraw() { + canvas.clear(); + engine.clear_screen(); + + pub const WHITE: u8 = 255; + pub const MID: u8 = 128; + + fn print_greyscale( + canvas: &mut Canvas, + threshold: bool, + x: usize, + y: usize, + shade: u8, + ) { + if (!threshold & (shade > 0)) | (threshold & (shade >= MID)) { + canvas.set_colored( + x as u32, + y as u32, + PixelColor::TrueColor { + r: shade, + g: shade, + b: shade, + }, + ); + } + } - state.a = is_key(last_a); - state.b = is_key(last_b); - state.left = is_key(last_left); - state.right = is_key(last_right); - state.up = is_key(last_up); - state.down = is_key(last_down); - - if save { - gameboy_state.save_state(save_path).unwrap(); - } - - // Stop processing until the frame timer tells us we're ok again - emulator_running_fast = true; - } - _ => {} - } - } + fn print_black_or_white(canvas: &mut Canvas, x: usize, y: usize, shade: u8) { + if shade >= MID { + canvas.set(x as u32, y as u32); + } + } - // Redraw only every other frame to help with flashing - if frame_timer.should_redraw() { - canvas.clear(); - - pub const WHITE: u8 = 255; - pub const MID: u8 = 128; - - fn print_greyscale(canvas: &mut Canvas, threshold: bool, x: usize, y: usize, shade: u8) { - if (!threshold & (shade > 0)) | (threshold & (shade >= MID)) { - canvas.set_colored( - x as u32, - y as u32, - PixelColor::TrueColor { - r: shade, - g: shade, - b: shade, - }, - ); - } - } + for y in 0..GB_SCREEN_HEIGHT as usize { + for x in 0..GB_SCREEN_WIDTH as usize { + let pixel = (y * GB_SCREEN_WIDTH as usize) + x; + let pixel_offset = pixel * 3; + let pval = pixel_buffer[pixel_offset]; + + let shade = if invert { WHITE - pval } else { pval }; + if greyscale { + print_greyscale(&mut canvas, threshold, x, y, shade); + } else { + print_black_or_white(&mut canvas, x, y, shade); + } + } + } - fn print_black_or_white(canvas: &mut Canvas, x: usize, y: usize, shade: u8) { - if shade >= MID { - canvas.set(x as u32, y as u32); - } - } + let frame = canvas.frame(); + + for (idx, line) in frame.lines().enumerate() { + let mut x = 0; + let mut color = Color::Reset; + // dirty hack to remove the ANSI characters and convert them to pixels + for chr in line + .replace("\u{1b}[38;2;255;255;255m", "1") + .replace("\u{1b}[38;2;192;192;192m", "2") + .replace("\u{1b}[0m", "3") + .chars() + { + if chr == '1' || chr == '2' || chr == '3' { + color = if chr == '1' { + Color::White + } else if chr == '2' { + Color::Rgb { + r: 192, + g: 192, + b: 192, + } + } else { + Color::Reset + }; + } else { + engine.set_pxl(x, idx as i32, pixel::pxl_fg(chr, color)); + x += 1; + } + } + } - for y in 0..GB_SCREEN_HEIGHT as usize { - for x in 0..GB_SCREEN_WIDTH as usize { - let pixel = (y * GB_SCREEN_WIDTH as usize) + x; - let pixel_offset = pixel * 3; - let pval = pixel_buffer[pixel_offset]; - - let shade = if invert { WHITE - pval } else { pval }; - if greyscale { - print_greyscale(&mut canvas, threshold, x, y, shade); - } else { - print_black_or_white(&mut canvas, x, y, shade); + engine.draw(); + } } + _ => {} } - } - let frame = canvas.frame(); + state.a = is_key(last_a); + state.b = is_key(last_b); + state.left = is_key(last_left); + state.right = is_key(last_right); + state.up = is_key(last_up); + state.down = is_key(last_down); - queue!(stdout(), MoveTo(0, 0))?; - - let mut idx = 0; + if save { + gameboy_state.save_state(save_path).unwrap(); + } - for line in frame.lines() { - queue!(stdout(), MoveTo(0, idx), Print(line))?; - idx += 1; + // Stop processing until the frame timer tells us we're ok again + emulator_running_fast = true; } - - execute!(stdout())?; } } - - disable_raw_mode()?; - execute!(stdout(), SetSize(cols, rows), LeaveAlternateScreen)?; - Ok(()) }