Moved the controller support into a cargo feature that needs to be turned on explicitely. This makes the emulator more portable.

This commit is contained in:
jan 2024-12-23 16:26:33 +01:00
parent 890c2b7b79
commit f5ea5b88fe
5 changed files with 60 additions and 9 deletions

2
Cargo.lock generated
View File

@ -1813,7 +1813,7 @@ checksum = "7da8b5736845d9f2fcb837ea5d9e2628564b3b043a70948a3f0b778838c5fb4f"
[[package]]
name = "svc16"
version = "0.6.1"
version = "0.7.0"
dependencies = [
"anyhow",
"clap",

View File

@ -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"]

View File

@ -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

View File

@ -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);

View File

@ -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<NesInput> {
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<NesInput>,
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)
}