From f5ea5b88fe9d06b19aba887533cdab827d7ec336 Mon Sep 17 00:00:00 2001 From: jan Date: Mon, 23 Dec 2024 16:26:33 +0100 Subject: [PATCH] Moved the controller support into a cargo feature that needs to be turned on explicitely. This makes the emulator more portable. --- Cargo.lock | 2 +- Cargo.toml | 9 ++++++--- README.md | 3 +++ src/main.rs | 9 ++++++++- src/utils.rs | 46 ++++++++++++++++++++++++++++++++++++++++++---- 5 files changed, 60 insertions(+), 9 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index 20eb915..b481647 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -1813,7 +1813,7 @@ checksum = "7da8b5736845d9f2fcb837ea5d9e2628564b3b043a70948a3f0b778838c5fb4f" [[package]] name = "svc16" -version = "0.6.1" +version = "0.7.0" dependencies = [ "anyhow", "clap", diff --git a/Cargo.toml b/Cargo.toml index ac8a394..744f1ac 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -1,6 +1,6 @@ [package] name = "svc16" -version = "0.6.1" +version = "0.7.0" edition = "2021" authors = ["Jan Neuendorf"] description = "An emulator for a simple virtual computer" @@ -9,10 +9,13 @@ license="MIT" [dependencies] anyhow = "1.0.93" clap = { version = "4.5.21", features = ["derive"] } -gilrs = { version = "0.11.0"} +gilrs = { version = "0.11.0",optional=true} # There seems to be some incompatibility with the latest crates.io version of pixels? pixels = { git = "https://github.com/parasyte/pixels.git", rev = "d4df286"} thiserror = "2.0.3" winit = "0.29.15" -winit-input-map = "0.4.1" +winit-input-map = {version="0.4.1",optional=true} winit_input_helper = "0.16.0" + +[features] +gamepad = ["gilrs","winit-input-map"] diff --git a/README.md b/README.md index 37570d3..cb81443 100644 --- a/README.md +++ b/README.md @@ -15,6 +15,9 @@ This repo contains an emulator to run games or programs. It can be installed wit cargo install --git https://github.com/JanNeuendorf/SVC16 ``` +> [!NOTE] +> For controller support, compile with `--features="gamepad"`. Support varies by platform and it might require additional libraries to be installed. + You can then run a program from the cli: ```sh diff --git a/src/main.rs b/src/main.rs index 20747c2..5436fb7 100644 --- a/src/main.rs +++ b/src/main.rs @@ -6,6 +6,7 @@ use anyhow::{anyhow, Result}; use clap::Parser; use cli::Cli; use engine::Engine; +#[cfg(feature = "gamepad")] use gilrs::Gilrs; use pixels::{Pixels, SurfaceTexture}; use std::time::{Duration, Instant}; @@ -20,6 +21,7 @@ const FRAMETIME: Duration = Duration::from_nanos((1000000000. / 30.) as u64); fn main() -> Result<()> { let cli = Cli::parse(); + #[cfg(feature = "gamepad")] let mut girls = Gilrs::new().expect("Could not read gamepad inputs."); let initial_state = read_u16s_from_file(&cli.program)?; // The initial state is cloned, so we keep it around for a restart. @@ -27,6 +29,7 @@ fn main() -> Result<()> { let event_loop = EventLoop::new()?; let mut input = WinitInputHelper::new(); + #[cfg(feature = "gamepad")] let mut gamepad = build_gamepad_map(); if cli.scaling < 1 { return Err(anyhow!("The minimal scaling factor is 1")); @@ -99,8 +102,12 @@ fn main() -> Result<()> { ipf += 1; } let engine_elapsed = engine_start.elapsed(); + #[cfg(not(feature = "gamepad"))] + let (c1, c2) = get_input_code(&input, &pixels); + #[cfg(feature = "gamepad")] gamepad.update_with_gilrs(&mut girls); - let (c1, c2) = get_input_code(&input, &gamepad, &pixels); + #[cfg(feature = "gamepad")] + let (c1, c2) = get_input_code_gamepad(&input, &gamepad, &pixels); engine.perform_sync(c1, c2, &mut raw_buffer); update_image_buffer(pixels.frame_mut(), &raw_buffer); diff --git a/src/utils.rs b/src/utils.rs index 6a0c631..7857c09 100644 --- a/src/utils.rs +++ b/src/utils.rs @@ -1,16 +1,17 @@ use crate::RES; use anyhow::Result; use pixels::Pixels; -use std::hash::Hash; use winit::{ event::MouseButton, event_loop::EventLoopWindowTarget, keyboard::{Key, KeyCode}, }; use winit_input_helper::WinitInputHelper; +#[cfg(feature = "gamepad")] use winit_input_map::{input_map, GamepadButton, InputMap}; -#[derive(Debug, Hash, PartialEq, Eq, Clone, Copy)] +#[cfg(feature = "gamepad")] +#[derive(Debug, std::hash::Hash, PartialEq, Eq, Clone, Copy)] pub enum NesInput { Up, Down, @@ -22,6 +23,7 @@ pub enum NesInput { Select, } +#[cfg(feature = "gamepad")] pub fn build_gamepad_map() -> InputMap { input_map!( (NesInput::A, GamepadButton::East), @@ -67,8 +69,8 @@ pub fn update_image_buffer(imbuff: &mut [u8], screen: &[u16; RES * RES]) { *imbuff.get_mut(4 * i + 3).expect("Error with image buffer") = 255; } } - -pub fn get_input_code( +#[cfg(feature = "gamepad")] +pub fn get_input_code_gamepad( input: &WinitInputHelper, gamepad: &InputMap, pxls: &Pixels, @@ -129,3 +131,39 @@ pub fn handle_event_loop_error(handle: &EventLoopWindowTarget<()>, msg: impl AsR eprintln!("{}", msg.as_ref()); handle.exit(); } + +#[cfg(not(feature = "gamepad"))] +pub fn get_input_code(input: &WinitInputHelper, pxls: &Pixels) -> (u16, u16) { + let raw_mp = input.cursor().unwrap_or((0., 0.)); + let mp = match pxls.window_pos_to_pixel(raw_mp) { + Ok(p) => p, + Err(ev) => pxls.clamp_pixel_pos(ev), + }; + let pos_code = (mp.1 as u16 * 256) + mp.0 as u16; + let mut key_code = 0_u16; + if input.key_held(KeyCode::Space) || input.mouse_held(MouseButton::Left) { + key_code += 1; + } + if input.key_held_logical(Key::Character("b")) || input.mouse_held(MouseButton::Right) { + key_code += 2; + } + if input.key_held_logical(Key::Character("w")) || input.key_held(KeyCode::ArrowUp) { + key_code += 4; + } + if input.key_held_logical(Key::Character("s")) || input.key_held(KeyCode::ArrowDown) { + key_code += 8; + } + if input.key_held_logical(Key::Character("a")) || input.key_held(KeyCode::ArrowLeft) { + key_code += 16; + } + if input.key_held_logical(Key::Character("d")) || input.key_held(KeyCode::ArrowRight) { + key_code += 32; + } + if input.key_held_logical(Key::Character("n")) { + key_code += 64; + } + if input.key_held_logical(Key::Character("m")) { + key_code += 128; + } + (pos_code, key_code) +}