mirror of
https://github.com/JanNeuendorf/SVC16.git
synced 2025-06-06 03:25:28 +00:00
Macroquad port (#10)
I ported the gui to the macroquad crate. This has the following advantages: We can easily draw text. This means we can show the number of instructions on screen. The filter mode can be set to linear for a different look. The dependencies are a lot cleaner now. Just depending on the latest stable version of macroquad makes it a lot more likely that the app builds properly on different platforms (and will continue to do so in the future). I also use the gilrs crate directly for the gamepad input.
This commit is contained in:
parent
24c192f129
commit
6abfcbc811
2288
Cargo.lock
generated
2288
Cargo.lock
generated
File diff suppressed because it is too large
Load Diff
10
Cargo.toml
10
Cargo.toml
@ -1,6 +1,6 @@
|
|||||||
[package]
|
[package]
|
||||||
name = "svc16"
|
name = "svc16"
|
||||||
version = "0.7.3"
|
version = "0.8.0"
|
||||||
edition = "2021"
|
edition = "2021"
|
||||||
authors = ["Jan Neuendorf"]
|
authors = ["Jan Neuendorf"]
|
||||||
description = "An emulator for a simple virtual computer"
|
description = "An emulator for a simple virtual computer"
|
||||||
@ -11,13 +11,9 @@ anyhow = "1.0.93"
|
|||||||
clap = { version = "4.5.21", features = ["derive"] }
|
clap = { version = "4.5.21", features = ["derive"] }
|
||||||
flate2 = "1.0.35"
|
flate2 = "1.0.35"
|
||||||
gilrs = { version = "0.11.0",optional=true}
|
gilrs = { version = "0.11.0",optional=true}
|
||||||
# There seems to be some incompatibility with the latest crates.io version of pixels?
|
macroquad = "0.4.13"
|
||||||
pixels = { git = "https://github.com/parasyte/pixels.git", rev = "d4df286"}
|
|
||||||
thiserror = "2.0.3"
|
thiserror = "2.0.3"
|
||||||
winit = "0.29.15"
|
|
||||||
winit-input-map = {version="0.4.1",optional=true}
|
|
||||||
winit_input_helper = "0.16.0"
|
|
||||||
|
|
||||||
[features]
|
[features]
|
||||||
default=[]
|
default=[]
|
||||||
gamepad = ["gilrs","winit-input-map"]
|
gamepad = ["gilrs"]
|
||||||
|
12
src/cli.rs
12
src/cli.rs
@ -7,7 +7,7 @@ pub struct Cli {
|
|||||||
pub program: String,
|
pub program: String,
|
||||||
|
|
||||||
#[arg(short, long, default_value = "1", help = "Set initial window scaling")]
|
#[arg(short, long, default_value = "1", help = "Set initial window scaling")]
|
||||||
pub scaling: u32,
|
pub scaling: i32,
|
||||||
|
|
||||||
#[arg(
|
#[arg(
|
||||||
short,
|
short,
|
||||||
@ -27,14 +27,14 @@ pub struct Cli {
|
|||||||
short,
|
short,
|
||||||
long,
|
long,
|
||||||
default_value_t = false,
|
default_value_t = false,
|
||||||
help = "Output performance metrics"
|
help = "Show performance metrics"
|
||||||
)]
|
)]
|
||||||
pub verbose: bool,
|
pub verbose: bool,
|
||||||
#[arg(
|
#[arg(
|
||||||
short,
|
|
||||||
long,
|
long,
|
||||||
default_value = "3000000",
|
short,
|
||||||
help = "Change the maximum instructions per frame"
|
default_value_t = false,
|
||||||
|
help = "Use linear filtering (instead of pixel-perfect) this enables fractional scaling"
|
||||||
)]
|
)]
|
||||||
pub max_ipf: usize,
|
pub linear_filtering: bool,
|
||||||
}
|
}
|
||||||
|
232
src/main.rs
232
src/main.rs
@ -1,138 +1,148 @@
|
|||||||
mod cli;
|
mod cli;
|
||||||
mod engine;
|
mod engine;
|
||||||
|
mod ui;
|
||||||
mod utils;
|
mod utils;
|
||||||
|
#[allow(unused)]
|
||||||
use anyhow::{anyhow, Result};
|
use anyhow::{anyhow, Context, Result};
|
||||||
use clap::Parser;
|
use clap::Parser;
|
||||||
use cli::Cli;
|
use cli::Cli;
|
||||||
use engine::Engine;
|
use engine::Engine;
|
||||||
#[cfg(feature = "gamepad")]
|
#[cfg(feature = "gamepad")]
|
||||||
use gilrs::Gilrs;
|
use gilrs::Gilrs;
|
||||||
use pixels::{Pixels, SurfaceTexture};
|
use macroquad::prelude::*;
|
||||||
use std::time::{Duration, Instant};
|
use std::time::{Duration, Instant};
|
||||||
|
use ui::Layout;
|
||||||
use utils::*;
|
use utils::*;
|
||||||
use winit::dpi::LogicalSize;
|
const MAX_IPF: usize = 3000000;
|
||||||
use winit::event_loop::EventLoop;
|
|
||||||
use winit::keyboard::{Key, KeyCode};
|
|
||||||
use winit::window::WindowBuilder;
|
|
||||||
use winit_input_helper::WinitInputHelper;
|
|
||||||
const RES: usize = 256;
|
|
||||||
const FRAMETIME: Duration = Duration::from_nanos((1000000000. / 30.) as u64);
|
const FRAMETIME: Duration = Duration::from_nanos((1000000000. / 30.) as u64);
|
||||||
|
|
||||||
fn main() -> Result<()> {
|
fn window_conf() -> Conf {
|
||||||
let cli = Cli::parse();
|
let cli = Cli::parse();
|
||||||
#[cfg(feature = "gamepad")]
|
if cli.fullscreen {}
|
||||||
let mut girls = Gilrs::new().expect("Could not read gamepad inputs.");
|
|
||||||
|
Conf {
|
||||||
|
window_title: "SVC16".to_owned(),
|
||||||
|
window_width: 256 * cli.scaling,
|
||||||
|
window_height: 256 * cli.scaling,
|
||||||
|
fullscreen: cli.fullscreen,
|
||||||
|
|
||||||
|
..Default::default()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
#[macroquad::main(window_conf)]
|
||||||
|
async fn main() -> Result<()> {
|
||||||
|
let mut cli = Cli::parse();
|
||||||
|
|
||||||
|
let mut buffer = [Color::from_rgba(255, 255, 255, 255); 256 * 256];
|
||||||
|
let mut image = Image::gen_image_color(256, 256, Color::from_rgba(0, 0, 0, 255));
|
||||||
|
let texture = Texture2D::from_image(&image);
|
||||||
|
if cli.linear_filtering {
|
||||||
|
texture.set_filter(FilterMode::Linear);
|
||||||
|
} else {
|
||||||
|
texture.set_filter(FilterMode::Nearest);
|
||||||
|
}
|
||||||
|
|
||||||
|
let mut raw_buffer = [0 as u16; 256 * 256];
|
||||||
let initial_state = read_u16s_from_file(&cli.program)?;
|
let initial_state = read_u16s_from_file(&cli.program)?;
|
||||||
// The initial state is cloned, so we keep it around for a restart.
|
|
||||||
let mut engine = Engine::new(initial_state.clone());
|
let mut engine = Engine::new(initial_state.clone());
|
||||||
|
|
||||||
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"));
|
|
||||||
}
|
|
||||||
let window = {
|
|
||||||
let size = LogicalSize::new(
|
|
||||||
(RES as u32 * cli.scaling) as f64,
|
|
||||||
(RES as u32 * cli.scaling) as f64,
|
|
||||||
);
|
|
||||||
let min_size = LogicalSize::new((RES) as f64, (RES) as f64);
|
|
||||||
WindowBuilder::new()
|
|
||||||
.with_title("SVC16")
|
|
||||||
.with_inner_size(size)
|
|
||||||
.with_min_inner_size(min_size)
|
|
||||||
.build(&event_loop)?
|
|
||||||
};
|
|
||||||
window.set_cursor_visible(cli.cursor);
|
|
||||||
if cli.fullscreen {
|
|
||||||
window.set_fullscreen(Some(winit::window::Fullscreen::Borderless(None)));
|
|
||||||
}
|
|
||||||
let mut pixels = {
|
|
||||||
let window_size = window.inner_size();
|
|
||||||
let surface_texture = SurfaceTexture::new(window_size.width, window_size.height, &window);
|
|
||||||
Pixels::new(RES as u32, RES as u32, surface_texture)?
|
|
||||||
};
|
|
||||||
|
|
||||||
let mut raw_buffer = [0 as u16; engine::MEMSIZE];
|
|
||||||
let mut paused = false;
|
let mut paused = false;
|
||||||
|
let mut ipf = 0;
|
||||||
|
|
||||||
event_loop.run(|event, elwt| {
|
#[cfg(feature = "gamepad")]
|
||||||
|
let mut gilrs = match Gilrs::new() {
|
||||||
|
Ok(g) => g,
|
||||||
|
_ => return Err(anyhow!("Gamepad could not be loaded")),
|
||||||
|
};
|
||||||
|
|
||||||
|
loop {
|
||||||
let start_time = Instant::now();
|
let start_time = Instant::now();
|
||||||
if input.update(&event) {
|
if is_key_pressed(KeyCode::Escape) {
|
||||||
if input.key_pressed(KeyCode::Escape) || input.close_requested() {
|
break;
|
||||||
elwt.exit();
|
}
|
||||||
return;
|
if is_key_pressed(KeyCode::P) {
|
||||||
}
|
paused = !paused;
|
||||||
if input.key_pressed_logical(Key::Character("p")) {
|
}
|
||||||
paused = !paused;
|
if is_key_pressed(KeyCode::R) {
|
||||||
if paused {
|
engine = Engine::new(initial_state.clone());
|
||||||
window.set_title("SVC16 (paused)");
|
paused = false;
|
||||||
} else {
|
}
|
||||||
window.set_title("SVC16");
|
if is_key_pressed(KeyCode::V) {
|
||||||
}
|
cli.verbose = !cli.verbose;
|
||||||
}
|
}
|
||||||
if input.key_pressed_logical(Key::Character("r")) {
|
if is_key_pressed(KeyCode::C) {
|
||||||
engine = Engine::new(initial_state.clone());
|
cli.cursor = !cli.cursor;
|
||||||
paused = false;
|
}
|
||||||
}
|
|
||||||
|
|
||||||
if let Some(size) = input.window_resized() {
|
let layout = Layout::generate(cli.linear_filtering);
|
||||||
if let Err(_) = pixels.resize_surface(size.width, size.height) {
|
if !paused {
|
||||||
handle_event_loop_error(&elwt, "Resize error");
|
ipf = 0;
|
||||||
return;
|
while !engine.wants_to_sync() && ipf <= MAX_IPF {
|
||||||
}
|
engine.step()?;
|
||||||
}
|
|
||||||
|
|
||||||
let mut ipf = 0;
|
|
||||||
let engine_start = Instant::now();
|
|
||||||
while !engine.wants_to_sync() && ipf <= cli.max_ipf && !paused {
|
|
||||||
match engine.step() {
|
|
||||||
Err(e) => {
|
|
||||||
handle_event_loop_error(
|
|
||||||
&elwt,
|
|
||||||
format!("{} (after {} instructions)", e, ipf),
|
|
||||||
);
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
_ => {}
|
|
||||||
}
|
|
||||||
ipf += 1;
|
ipf += 1;
|
||||||
}
|
}
|
||||||
let engine_elapsed = engine_start.elapsed();
|
|
||||||
|
#[cfg(feature = "gamepad")]
|
||||||
|
while let Some(event) = gilrs.next_event() {
|
||||||
|
gilrs.update(&event);
|
||||||
|
}
|
||||||
#[cfg(not(feature = "gamepad"))]
|
#[cfg(not(feature = "gamepad"))]
|
||||||
let (c1, c2) = get_input_code(&input, &pixels);
|
let (mpos, keycode) = get_input_code_no_gamepad(&layout);
|
||||||
#[cfg(feature = "gamepad")]
|
|
||||||
gamepad.update_with_gilrs(&mut girls);
|
|
||||||
#[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);
|
|
||||||
|
|
||||||
let elapsed = start_time.elapsed();
|
#[cfg(feature = "gamepad")]
|
||||||
if cli.verbose {
|
let (mpos, keycode) = get_input_code_gamepad(&layout, &gilrs);
|
||||||
println!(
|
|
||||||
"Instructions: {} Frametime: {}ms (Engine only: {}ms)",
|
engine.perform_sync(mpos, keycode, &mut raw_buffer);
|
||||||
ipf,
|
update_image_buffer(&mut buffer, &raw_buffer);
|
||||||
elapsed.as_millis(),
|
image.update(&buffer);
|
||||||
engine_elapsed.as_millis()
|
texture.update(&image);
|
||||||
);
|
|
||||||
}
|
|
||||||
if elapsed < FRAMETIME {
|
|
||||||
std::thread::sleep(FRAMETIME - elapsed);
|
|
||||||
}
|
|
||||||
window.request_redraw();
|
|
||||||
match pixels.render() {
|
|
||||||
Err(_) => {
|
|
||||||
handle_event_loop_error(&elwt, "Rendering error");
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
_ => {}
|
|
||||||
};
|
|
||||||
}
|
}
|
||||||
})?;
|
clear_background(BLACK);
|
||||||
|
|
||||||
|
if layout.cursor_in_window() {
|
||||||
|
show_mouse(cli.cursor);
|
||||||
|
} else {
|
||||||
|
show_mouse(true);
|
||||||
|
}
|
||||||
|
|
||||||
|
draw_texture_ex(
|
||||||
|
&texture,
|
||||||
|
layout.x,
|
||||||
|
layout.y,
|
||||||
|
WHITE,
|
||||||
|
DrawTextureParams {
|
||||||
|
dest_size: Some(vec2(layout.size, layout.size)),
|
||||||
|
|
||||||
|
..Default::default()
|
||||||
|
},
|
||||||
|
);
|
||||||
|
if cli.verbose {
|
||||||
|
draw_rectangle(
|
||||||
|
layout.rect_x,
|
||||||
|
layout.rect_y,
|
||||||
|
0.25 * layout.size,
|
||||||
|
layout.font_size,
|
||||||
|
Color::from_rgba(0, 0, 0, 200),
|
||||||
|
);
|
||||||
|
|
||||||
|
draw_text(
|
||||||
|
&format!("{}", ipf),
|
||||||
|
layout.font_x,
|
||||||
|
layout.font_y,
|
||||||
|
layout.font_size,
|
||||||
|
LIME,
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
// Wait for the next frame
|
||||||
|
let elapsed = start_time.elapsed();
|
||||||
|
if elapsed < FRAMETIME {
|
||||||
|
std::thread::sleep(FRAMETIME - elapsed);
|
||||||
|
} else {
|
||||||
|
if cli.verbose {
|
||||||
|
println!("Frame was not processed in time");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
next_frame().await;
|
||||||
|
}
|
||||||
Ok(())
|
Ok(())
|
||||||
}
|
}
|
||||||
|
47
src/ui.rs
Normal file
47
src/ui.rs
Normal file
@ -0,0 +1,47 @@
|
|||||||
|
use macroquad::prelude::*;
|
||||||
|
pub struct Layout {
|
||||||
|
pub x: f32,
|
||||||
|
pub y: f32,
|
||||||
|
pub size: f32,
|
||||||
|
pub font_y: f32,
|
||||||
|
pub font_x: f32,
|
||||||
|
pub rect_x: f32,
|
||||||
|
pub rect_y: f32,
|
||||||
|
pub font_size: f32,
|
||||||
|
}
|
||||||
|
impl Layout {
|
||||||
|
pub fn generate(linear: bool) -> Self {
|
||||||
|
let (width, height) = (screen_width(), screen_height());
|
||||||
|
let minsize = width.min(height);
|
||||||
|
let image_size = match linear {
|
||||||
|
false => ((minsize / 256.).floor() * 256.).max(256.),
|
||||||
|
true => minsize.max(256.),
|
||||||
|
};
|
||||||
|
let x = (0. as f32).max((width - image_size) / 2.);
|
||||||
|
let y = (0. as f32).max((height - image_size) / 2.);
|
||||||
|
let font_y = y + image_size / 15.;
|
||||||
|
Self {
|
||||||
|
x,
|
||||||
|
y,
|
||||||
|
size: image_size,
|
||||||
|
font_y,
|
||||||
|
font_x: x + 0.01 * image_size,
|
||||||
|
rect_x: x + 0.005 * image_size,
|
||||||
|
rect_y: y + 0.01 * image_size,
|
||||||
|
font_size: image_size / 15.,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
pub fn clamp_mouse(&self) -> (f32, f32) {
|
||||||
|
let (raw_x, raw_y) = mouse_position();
|
||||||
|
let clamped_x = (raw_x.clamp(self.x, self.x + self.size) - self.x) / self.size * 255.;
|
||||||
|
let clamped_y = (raw_y.clamp(self.y, self.y + self.size) - self.y) / self.size * 255.;
|
||||||
|
(clamped_x, clamped_y)
|
||||||
|
}
|
||||||
|
pub fn cursor_in_window(&self) -> bool {
|
||||||
|
let mp = mouse_position();
|
||||||
|
mp.0 >= self.x
|
||||||
|
&& mp.0 < (self.x + self.size)
|
||||||
|
&& mp.1 >= self.y
|
||||||
|
&& mp.1 < (self.y + self.size)
|
||||||
|
}
|
||||||
|
}
|
201
src/utils.rs
201
src/utils.rs
@ -1,61 +1,14 @@
|
|||||||
use std::io::Read;
|
#[allow(unused)]
|
||||||
|
use crate::ui::Layout;
|
||||||
use crate::RES;
|
|
||||||
use anyhow::Result;
|
use anyhow::Result;
|
||||||
use flate2::read::GzDecoder;
|
use flate2::read::GzDecoder;
|
||||||
use pixels::Pixels;
|
use macroquad::color::Color;
|
||||||
|
use macroquad::prelude::*;
|
||||||
use std::fs::File;
|
use std::fs::File;
|
||||||
use winit::{
|
use std::io::Read;
|
||||||
event::MouseButton,
|
const RES: usize = 256;
|
||||||
event_loop::EventLoopWindowTarget,
|
|
||||||
keyboard::{Key, KeyCode},
|
|
||||||
};
|
|
||||||
use winit_input_helper::WinitInputHelper;
|
|
||||||
#[cfg(feature = "gamepad")]
|
#[cfg(feature = "gamepad")]
|
||||||
use winit_input_map::{input_map, GamepadAxis, GamepadButton, InputCode, InputMap};
|
use gilrs::{Axis, Button, Gilrs};
|
||||||
|
|
||||||
#[cfg(feature = "gamepad")]
|
|
||||||
#[derive(Debug, std::hash::Hash, PartialEq, Eq, Clone, Copy)]
|
|
||||||
pub enum NesInput {
|
|
||||||
Up,
|
|
||||||
Down,
|
|
||||||
Left,
|
|
||||||
Right,
|
|
||||||
A,
|
|
||||||
B,
|
|
||||||
Start,
|
|
||||||
Select,
|
|
||||||
}
|
|
||||||
|
|
||||||
#[cfg(feature = "gamepad")]
|
|
||||||
pub fn build_gamepad_map() -> InputMap<NesInput> {
|
|
||||||
input_map!(
|
|
||||||
(NesInput::A, GamepadButton::East),
|
|
||||||
(NesInput::B, GamepadButton::South),
|
|
||||||
(NesInput::Select, GamepadButton::Select),
|
|
||||||
(NesInput::Start, GamepadButton::Start),
|
|
||||||
(
|
|
||||||
NesInput::Up,
|
|
||||||
GamepadButton::DPadUp,
|
|
||||||
InputCode::gamepad_axis_pos(GamepadAxis::LeftStickY)
|
|
||||||
),
|
|
||||||
(
|
|
||||||
NesInput::Down,
|
|
||||||
GamepadButton::DPadDown,
|
|
||||||
InputCode::gamepad_axis_neg(GamepadAxis::LeftStickY)
|
|
||||||
),
|
|
||||||
(
|
|
||||||
NesInput::Left,
|
|
||||||
GamepadButton::DPadLeft,
|
|
||||||
InputCode::gamepad_axis_neg(GamepadAxis::LeftStickX)
|
|
||||||
),
|
|
||||||
(
|
|
||||||
NesInput::Right,
|
|
||||||
GamepadButton::DPadRight,
|
|
||||||
InputCode::gamepad_axis_pos(GamepadAxis::LeftStickX)
|
|
||||||
)
|
|
||||||
)
|
|
||||||
}
|
|
||||||
|
|
||||||
pub fn read_u16s_from_file(file_path: &str) -> Result<Vec<u16>> {
|
pub fn read_u16s_from_file(file_path: &str) -> Result<Vec<u16>> {
|
||||||
let mut file = File::open(file_path)?;
|
let mut file = File::open(file_path)?;
|
||||||
@ -86,110 +39,116 @@ fn rgb565_to_argb(rgb565: u16) -> (u8, u8, u8) {
|
|||||||
(r, g, b)
|
(r, g, b)
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn update_image_buffer(imbuff: &mut [u8], screen: &[u16; RES * RES]) {
|
pub fn update_image_buffer(imbuff: &mut [Color; RES * RES], screen: &[u16; RES * RES]) {
|
||||||
for i in 0..RES * RES {
|
for i in 0..RES * RES {
|
||||||
let col = rgb565_to_argb(screen[i]);
|
let col = rgb565_to_argb(screen[i]);
|
||||||
*imbuff.get_mut(4 * i).expect("Error with image buffer") = col.0;
|
imbuff[i] = Color {
|
||||||
*imbuff.get_mut(4 * i + 1).expect("Error with image buffer") = col.1;
|
r: col.0 as f32 / 255.,
|
||||||
*imbuff.get_mut(4 * i + 2).expect("Error with image buffer") = col.2;
|
g: col.1 as f32 / 255.,
|
||||||
*imbuff.get_mut(4 * i + 3).expect("Error with image buffer") = 255;
|
b: col.2 as f32 / 255.,
|
||||||
|
a: 1.,
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
#[cfg(feature = "gamepad")]
|
#[cfg(feature = "gamepad")]
|
||||||
pub fn get_input_code_gamepad(
|
pub fn get_input_code_gamepad(layout: &Layout, gilrs: &Gilrs) -> (u16, u16) {
|
||||||
input: &WinitInputHelper,
|
#[cfg(not(feature = "gamepad"))]
|
||||||
gamepad: &InputMap<NesInput>,
|
return get_input_code_no_gamepad();
|
||||||
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;
|
let mut key_code = 0_u16;
|
||||||
if input.key_held(KeyCode::Space)
|
let mp = layout.clamp_mouse();
|
||||||
|| input.mouse_held(MouseButton::Left)
|
let pos_code = (mp.1 as u16 * 256) + mp.0 as u16;
|
||||||
|| gamepad.pressing(NesInput::A)
|
let Some(gamepad) = gilrs.gamepads().next().map(|t| t.1) else {
|
||||||
|
return get_input_code_no_gamepad(layout);
|
||||||
|
};
|
||||||
|
let tol = 0.5;
|
||||||
|
let axis_horizontal = gamepad
|
||||||
|
.axis_data(Axis::LeftStickX)
|
||||||
|
.map(|a| a.value())
|
||||||
|
.unwrap_or(0.);
|
||||||
|
let axis_vertical = gamepad
|
||||||
|
.axis_data(Axis::LeftStickY)
|
||||||
|
.map(|a| a.value())
|
||||||
|
.unwrap_or(0.);
|
||||||
|
if is_key_down(KeyCode::Space)
|
||||||
|
|| is_mouse_button_down(MouseButton::Left)
|
||||||
|
|| gamepad.is_pressed(Button::East)
|
||||||
{
|
{
|
||||||
key_code += 1;
|
key_code += 1;
|
||||||
}
|
}
|
||||||
if input.key_held_logical(Key::Character("b"))
|
if is_key_down(KeyCode::B)
|
||||||
|| input.mouse_held(MouseButton::Right)
|
|| is_mouse_button_down(MouseButton::Right)
|
||||||
|| gamepad.pressing(NesInput::B)
|
|| gamepad.is_pressed(Button::South)
|
||||||
{
|
{
|
||||||
key_code += 2;
|
key_code += 2;
|
||||||
}
|
}
|
||||||
if input.key_held_logical(Key::Character("w"))
|
if is_key_down(KeyCode::W)
|
||||||
|| input.key_held(KeyCode::ArrowUp)
|
|| is_key_down(KeyCode::Up)
|
||||||
|| gamepad.pressing(NesInput::Up)
|
|| gamepad.is_pressed(Button::DPadUp)
|
||||||
|
|| axis_vertical > tol
|
||||||
{
|
{
|
||||||
key_code += 4;
|
key_code += 4
|
||||||
}
|
}
|
||||||
if input.key_held_logical(Key::Character("s"))
|
if is_key_down(KeyCode::S)
|
||||||
|| input.key_held(KeyCode::ArrowDown)
|
|| is_key_down(KeyCode::Down)
|
||||||
|| gamepad.pressing(NesInput::Down)
|
|| gamepad.is_pressed(Button::DPadDown)
|
||||||
|
|| axis_vertical < -tol
|
||||||
{
|
{
|
||||||
key_code += 8;
|
key_code += 8
|
||||||
}
|
}
|
||||||
if input.key_held_logical(Key::Character("a"))
|
if is_key_down(KeyCode::A)
|
||||||
|| input.key_held(KeyCode::ArrowLeft)
|
|| is_key_down(KeyCode::Left)
|
||||||
|| gamepad.pressing(NesInput::Left)
|
|| gamepad.is_pressed(Button::DPadLeft)
|
||||||
|
|| axis_horizontal < -tol
|
||||||
{
|
{
|
||||||
key_code += 16;
|
key_code += 16
|
||||||
}
|
}
|
||||||
if input.key_held_logical(Key::Character("d"))
|
if is_key_down(KeyCode::D)
|
||||||
|| input.key_held(KeyCode::ArrowRight)
|
|| is_key_down(KeyCode::Right)
|
||||||
|| gamepad.pressing(NesInput::Right)
|
|| gamepad.is_pressed(Button::DPadRight)
|
||||||
|
|| axis_horizontal > tol
|
||||||
{
|
{
|
||||||
key_code += 32;
|
key_code += 32
|
||||||
}
|
}
|
||||||
if input.key_held_logical(Key::Character("n")) || gamepad.pressing(NesInput::Select) {
|
if is_key_down(KeyCode::N) || gamepad.is_pressed(Button::Select) {
|
||||||
key_code += 64;
|
key_code += 64
|
||||||
}
|
}
|
||||||
if input.key_held_logical(Key::Character("m")) || gamepad.pressing(NesInput::Start) {
|
if is_key_down(KeyCode::M) || gamepad.is_pressed(Button::Start) {
|
||||||
key_code += 128;
|
key_code += 128
|
||||||
}
|
}
|
||||||
|
|
||||||
(pos_code, key_code)
|
(pos_code, key_code)
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn handle_event_loop_error(handle: &EventLoopWindowTarget<()>, msg: impl AsRef<str>) {
|
pub fn get_input_code_no_gamepad(layout: &Layout) -> (u16, u16) {
|
||||||
eprintln!("{}", msg.as_ref());
|
let mp = layout.clamp_mouse();
|
||||||
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 pos_code = (mp.1 as u16 * 256) + mp.0 as u16;
|
||||||
let mut key_code = 0_u16;
|
let mut key_code = 0_u16;
|
||||||
if input.key_held(KeyCode::Space) || input.mouse_held(MouseButton::Left) {
|
if is_key_down(KeyCode::Space) || is_mouse_button_down(MouseButton::Left) {
|
||||||
key_code += 1;
|
key_code += 1;
|
||||||
}
|
}
|
||||||
if input.key_held_logical(Key::Character("b")) || input.mouse_held(MouseButton::Right) {
|
if is_key_down(KeyCode::B) || is_mouse_button_down(MouseButton::Right) {
|
||||||
key_code += 2;
|
key_code += 2;
|
||||||
}
|
}
|
||||||
if input.key_held_logical(Key::Character("w")) || input.key_held(KeyCode::ArrowUp) {
|
if is_key_down(KeyCode::W) || is_key_down(KeyCode::Up) {
|
||||||
key_code += 4;
|
key_code += 4
|
||||||
}
|
}
|
||||||
if input.key_held_logical(Key::Character("s")) || input.key_held(KeyCode::ArrowDown) {
|
if is_key_down(KeyCode::S) || is_key_down(KeyCode::Down) {
|
||||||
key_code += 8;
|
key_code += 8
|
||||||
}
|
}
|
||||||
if input.key_held_logical(Key::Character("a")) || input.key_held(KeyCode::ArrowLeft) {
|
if is_key_down(KeyCode::A) || is_key_down(KeyCode::Left) {
|
||||||
key_code += 16;
|
key_code += 16
|
||||||
}
|
}
|
||||||
if input.key_held_logical(Key::Character("d")) || input.key_held(KeyCode::ArrowRight) {
|
if is_key_down(KeyCode::D) || is_key_down(KeyCode::Right) {
|
||||||
key_code += 32;
|
key_code += 32
|
||||||
}
|
}
|
||||||
if input.key_held_logical(Key::Character("n")) {
|
if is_key_down(KeyCode::N) {
|
||||||
key_code += 64;
|
key_code += 64
|
||||||
}
|
}
|
||||||
if input.key_held_logical(Key::Character("m")) {
|
if is_key_down(KeyCode::M) {
|
||||||
key_code += 128;
|
key_code += 128
|
||||||
}
|
}
|
||||||
|
|
||||||
(pos_code, key_code)
|
(pos_code, key_code)
|
||||||
}
|
}
|
||||||
|
Loading…
x
Reference in New Issue
Block a user