diff --git a/ggdt/tests/manual/keyboard.rs b/ggdt/tests/manual/keyboard.rs new file mode 100644 index 0000000..5160cef --- /dev/null +++ b/ggdt/tests/manual/keyboard.rs @@ -0,0 +1,94 @@ +use crate::BACKGROUND_COLOR; +use ggdt::prelude::*; + +fn display_raw_keyboard_state(system: &mut System, x: i32, y: i32) { + let font_opts = FontRenderOpts::Color(0xffffffff); + let font = &system.res.font; + + system.res.video.print_string("Raw Keyboard State", x, x, font_opts, font); + + let mut idx = 0; + for yc in 0..16 { + for xc in 0..32 { + let state = if let Some(scancode) = num::FromPrimitive::from_u32(idx) { + system.res.keyboard.is_key_down(scancode) + } else { + false + }; + system.res.video.print_char( + if state { '1' } else { '0' }, + x + (xc * 8), + y + 10 + (yc * 8), + font_opts, + font, + ); + + idx += 1; + } + } +} + +fn display_key_state(key: Scancode, system: &mut System, x: i32, y: i32) { + system.res.video.print_string( + &format!( + "{:?} - {} {} {} {}", + key, + system.res.keyboard.is_key_down(key) as i32, + system.res.keyboard.is_key_up(key) as i32, + system.res.keyboard.is_key_pressed(key) as i32, + system.res.keyboard.is_key_released(key) as i32, + ), + x, + y, + FontRenderOpts::Color(0xffffffff), + &system.res.font, + ); +} + +#[test] +#[ignore] +fn keyboard_state() { + let config = StandardConfig::variable_screen_size(640, 480).scale_factor(2); + let mut system = SystemBuilder::new() + .window_title("Keyboard State") // + .vsync(true) + .build(config) + .unwrap(); + + while !system.do_events().unwrap() { + if system.res.keyboard.is_key_pressed(Scancode::Escape) { + break; + } + + system.res.video.clear(BACKGROUND_COLOR); + + system.update().unwrap(); + + display_raw_keyboard_state(&mut system, 2, 2); + + for (idx, key) in [ + Scancode::A, + Scancode::LCtrl, + Scancode::RCtrl, + Scancode::LAlt, + Scancode::RAlt, + Scancode::LShift, + Scancode::RShift, + Scancode::Return, + Scancode::KpEnter, + Scancode::Up, + Scancode::Down, + Scancode::Left, + Scancode::Right, + ] + .iter() + .enumerate() + { + display_key_state(*key, &mut system, 2, 160 + (idx as i32 * 10)); + } + + system.res.video.set_pixel(system.res.mouse.x(), system.res.mouse.y(), to_rgb32(255, 0, 255)); + + system.display().unwrap(); + } +} diff --git a/ggdt/tests/manual/main.rs b/ggdt/tests/manual/main.rs new file mode 100644 index 0000000..526ca09 --- /dev/null +++ b/ggdt/tests/manual/main.rs @@ -0,0 +1,53 @@ +//! These are tests that need to be manually run and interacted with by the user in order to verify functionality. +//! This is specifically for integration-style testing which is testing things like windowing, system events, input +//! devices, etc that are more difficult to test in a purely automated way. +//! +//! TODO: Is there a better way to do this, rather than marking all of these tests as ignored? We don't want them to +//! run during any automatic CI step, etc, as those should always run without manual user intervention midway. +//! +//! HACK: It is not currenly possible to run all of these together via cargo CLI, using something like +//! `cargo test -- --test-threads=1 --ignored`. Even restricting to 1 thread like this will fail. This appears +//! to be due to a bug in rust-sdl2 where `sdl2::sdl::IS_MAIN_THREAD_DECLARED` is never reset to false after +//! `SDL_Quit` is finally called (which only can happen when all subsystems are dropped). Attempting to restart +//! SDL even after a supposedly clean shutdown will always result in the error +//! "Cannot initialize `Sdl` from more than once thread." +//! However, you can run them via an alternative test runner like nextest (https://nexte.st/). +//! e.g.`cargo nextest run -j 1 --run-ignored ignored-only` + +mod keyboard; +mod mouse; +mod system_events; +mod system_resources_doslike; +mod system_resources_standard; + +use ggdt::prelude::*; + +const BACKGROUND_COLOR: u32 = 0xff2c3041; + +fn draw_base_screen( + dest: &mut BitmapType, + bg_color_1: BitmapType::PixelType, + bg_color_2: BitmapType::PixelType, + color: BitmapType::PixelType, + highlight_color: BitmapType::PixelType, +) where + BitmapType: GeneralBitmap, +{ + for y in 0..dest.height() as i32 { + dest.horiz_line(0, dest.right() as i32, y, if y % 2 == 0 { bg_color_1 } else { bg_color_2 }); + } + + dest.horiz_line(0, 16, 0, color); + dest.horiz_line(0, 16, dest.bottom() as i32, color); + dest.horiz_line(dest.right() as i32 - 16, dest.right() as i32, 0, color); + dest.horiz_line(dest.right() as i32 - 16, dest.right() as i32, dest.bottom() as i32, color); + dest.vert_line(0, 0, 16, color); + dest.vert_line(dest.right() as i32, 0, 16, color); + dest.vert_line(0, dest.bottom() as i32 - 16, dest.bottom() as i32, color); + dest.vert_line(dest.right() as i32, dest.bottom() as i32 - 16, dest.bottom() as i32, color); + + dest.set_pixel(0, 0, highlight_color); + dest.set_pixel(0, dest.bottom() as i32, highlight_color); + dest.set_pixel(dest.right() as i32, 0, highlight_color); + dest.set_pixel(dest.right() as i32, dest.bottom() as i32, highlight_color); +} diff --git a/ggdt/tests/manual/mouse.rs b/ggdt/tests/manual/mouse.rs new file mode 100644 index 0000000..ea3e569 --- /dev/null +++ b/ggdt/tests/manual/mouse.rs @@ -0,0 +1,98 @@ +use crate::BACKGROUND_COLOR; +use ggdt::prelude::*; + +fn display_mouse_state(system: &mut System) { + let font = &system.res.font; + let font_opts = FontRenderOpts::Color(0xffffffff); + + system.res.video.print_string( + &format!( + "X: {:4} (delta: {:4})\nY: {:4} (delta: {:4})", + system.res.mouse.x(), + system.res.mouse.x_delta(), + system.res.mouse.y(), + system.res.mouse.y_delta(), + ), + 10, + 10, + font_opts, + font, + ); + + system.res.video.print_string("Buttons - down/up/pressed/released", 10, 40, font_opts, font); + use MouseButton::*; + for (idx, button) in [Left, Middle, Right, X1, X2].iter().enumerate() { + system.res.video.print_string( + &format!( + "{:?} - {} {} {} {}", + button, + system.res.mouse.is_button_down(*button) as i32, + system.res.mouse.is_button_up(*button) as i32, + system.res.mouse.is_button_pressed(*button) as i32, + system.res.mouse.is_button_released(*button) as i32, + ), + 10, + 50 + (idx as i32 * 8), + font_opts, + font, + ) + } +} + +#[test] +#[ignore] // should be manually run +fn mouse_with_custom_cursor() { + let config = StandardConfig::variable_screen_size(320, 240).scale_factor(4); + let mut system = SystemBuilder::new() + .window_title("Mouse Input, using a Custom Bitmap Cursor") + .vsync(true) + .show_mouse(false) + .build(config) + .unwrap(); + + system.res.cursor.enable_cursor(true); + + while !system.do_events().unwrap() { + if system.res.keyboard.is_key_pressed(Scancode::Escape) { + break; + } + + system.res.video.clear(BACKGROUND_COLOR); + + system.update().unwrap(); + + display_mouse_state(&mut system); + system.res.video.set_pixel(system.res.mouse.x(), system.res.mouse.y(), to_rgb32(255, 0, 255)); + + system.display().unwrap(); + } +} + +#[test] +#[ignore] // should be manually run +fn mouse_with_os_cursor() { + let config = StandardConfig::variable_screen_size(320, 240).scale_factor(4); + let mut system = SystemBuilder::new() + .window_title("Mouse Input, using the OS's Native Cursor") + .vsync(true) + .show_mouse(true) + .build(config) + .unwrap(); + + system.res.cursor.enable_cursor(false); + + while !system.do_events().unwrap() { + if system.res.keyboard.is_key_pressed(Scancode::Escape) { + break; + } + + system.res.video.clear(BACKGROUND_COLOR); + + system.update().unwrap(); + + display_mouse_state(&mut system); + system.res.video.set_pixel(system.res.mouse.x(), system.res.mouse.y(), to_rgb32(255, 0, 255)); + + system.display().unwrap(); + } +} diff --git a/ggdt/tests/manual/system_events.rs b/ggdt/tests/manual/system_events.rs new file mode 100644 index 0000000..86ed970 --- /dev/null +++ b/ggdt/tests/manual/system_events.rs @@ -0,0 +1,50 @@ +use crate::BACKGROUND_COLOR; +use ggdt::prelude::*; +use std::collections::VecDeque; + +#[test] +#[ignore] // should be manually run +fn system_events_display() { + let config = StandardConfig::variable_screen_size(640, 480).scale_factor(2); + let mut system = SystemBuilder::new() + .window_title("Displaying all SystemEvents") + .vsync(true) + .show_mouse(true) + .build(config) + .unwrap(); + + let mut recent_events = VecDeque::new(); + + 'mainloop: loop { + while recent_events.len() > (system.res.video.height() as usize / system.res.font.line_height() as usize) { + recent_events.pop_back(); + } + system.res.update_event_state().unwrap(); + for event in system.event_pump.poll_iter() { + system.res.handle_event(&event).unwrap(); + recent_events.push_front(event.clone()); + if event == SystemEvent::Quit { + break 'mainloop; + } + } + system.update().unwrap(); + + if system.res.keyboard.is_key_pressed(Scancode::Escape) { + break 'mainloop; + } + + system.res.video.clear(BACKGROUND_COLOR); + + for (idx, event) in recent_events.iter().enumerate() { + system.res.video.print_string( + &format!("{:?}", event), + 2, + system.res.video.height() as i32 - 10 - (idx as i32 * 10), + FontRenderOpts::Color(to_rgb32(255, 255, 255)), + &system.res.font, + ); + } + + system.display().unwrap(); + } +} diff --git a/ggdt/tests/manual/system_resources_doslike.rs b/ggdt/tests/manual/system_resources_doslike.rs new file mode 100644 index 0000000..2c2d345 --- /dev/null +++ b/ggdt/tests/manual/system_resources_doslike.rs @@ -0,0 +1,75 @@ +use crate::draw_base_screen; +use ggdt::prelude::*; + +fn draw_state(system: &mut System) { + system.res.video.print_string( + &format!( + "{}x{} (DosLike)\n\n{:3}, {:3}", + system.res.video.width(), + system.res.video.height(), + system.res.mouse.x(), + system.res.mouse.y() + ), + 10, + 10, + FontRenderOpts::Color(15), + &system.res.font, + ); +} + +fn simple_main_loop(mut system: System) { + while !system.do_events().unwrap() { + if system.res.keyboard.is_key_pressed(Scancode::Escape) { + break; + } + + system.update().unwrap(); + + draw_base_screen(&mut system.res.video, 18, 19, 15, 12); + draw_state(&mut system); + + system.display().unwrap(); + } +} + +#[test] +#[ignore] +fn fixed_screen_size_integer_scaling() { + let config = DosLikeConfig::fixed_screen_size(320, 240, true).scale_factor(3); + let mut system = SystemBuilder::new() + .window_title("Fixed Screen Size with Integer Scaling (DosLike)") + .vsync(true) + .show_mouse(false) + .build(config) + .unwrap(); + system.res.cursor.enable_cursor(true); + simple_main_loop(system); +} + +#[test] +#[ignore] +fn fixed_screen_size_variable_scaling() { + let config = DosLikeConfig::fixed_screen_size(320, 240, false).scale_factor(3); + let mut system = SystemBuilder::new() + .window_title("Fixed Screen Size with Variable Scaling (DosLike)") + .vsync(true) + .show_mouse(false) + .build(config) + .unwrap(); + system.res.cursor.enable_cursor(true); + simple_main_loop(system); +} + +#[test] +#[ignore] +fn variable_screen_size_fixed_scale_factor() { + let config = DosLikeConfig::variable_screen_size(320, 240).scale_factor(3); + let mut system = SystemBuilder::new() + .window_title("Variable Screen Size with Fixed Scale Factor (DosLike)") + .vsync(true) + .show_mouse(false) + .build(config) + .unwrap(); + system.res.cursor.enable_cursor(true); + simple_main_loop(system); +} diff --git a/ggdt/tests/manual/system_resources_standard.rs b/ggdt/tests/manual/system_resources_standard.rs new file mode 100644 index 0000000..0873b05 --- /dev/null +++ b/ggdt/tests/manual/system_resources_standard.rs @@ -0,0 +1,81 @@ +use crate::draw_base_screen; +use ggdt::prelude::*; + +fn draw_state(system: &mut System) { + system.res.video.print_string( + &format!( + "{}x{} (Standard)\n\n{:3}, {:3}", + system.res.video.width(), + system.res.video.height(), + system.res.mouse.x(), + system.res.mouse.y() + ), + 10, + 10, + FontRenderOpts::Color(COLOR_BRIGHT_WHITE), + &system.res.font, + ); +} + +fn simple_main_loop(mut system: System) { + while !system.do_events().unwrap() { + if system.res.keyboard.is_key_pressed(Scancode::Escape) { + break; + } + + system.update().unwrap(); + + draw_base_screen( + &mut system.res.video, + to_rgb32(32, 32, 32), + to_rgb32(44, 44, 44), + COLOR_BRIGHT_WHITE, + COLOR_BRIGHT_RED, + ); + draw_state(&mut system); + + system.display().unwrap(); + } +} + +#[test] +#[ignore] +fn fixed_screen_size_integer_scaling() { + let config = StandardConfig::fixed_screen_size(320, 240, true).scale_factor(3); + let mut system = SystemBuilder::new() + .window_title("Fixed Screen Size with Integer Scaling (Standard)") + .vsync(true) + .show_mouse(false) + .build(config) + .unwrap(); + system.res.cursor.enable_cursor(true); + simple_main_loop(system); +} + +#[test] +#[ignore] +fn fixed_screen_size_variable_scaling() { + let config = StandardConfig::fixed_screen_size(320, 240, false).scale_factor(3); + let mut system = SystemBuilder::new() + .window_title("Fixed Screen Size with Variable Scaling (Standard)") + .vsync(true) + .show_mouse(false) + .build(config) + .unwrap(); + system.res.cursor.enable_cursor(true); + simple_main_loop(system); +} + +#[test] +#[ignore] +fn variable_screen_size_fixed_scale_factor() { + let config = StandardConfig::variable_screen_size(320, 240).scale_factor(3); + let mut system = SystemBuilder::new() + .window_title("Variable Screen Size with Fixed Scale Factor (Standard)") + .vsync(true) + .show_mouse(false) + .build(config) + .unwrap(); + system.res.cursor.enable_cursor(true); + simple_main_loop(system); +}