mirror of
https://github.com/jsgroth/jgenesis.git
synced 2026-01-09 06:01:07 +08:00
1077 lines
41 KiB
Rust
1077 lines
41 KiB
Rust
use crate::app::{App, NumericTextEdit, OpenWindow};
|
|
use crate::emuthread::{EmuThreadCommand, GenericInput, InputType};
|
|
use egui::{Color32, Context, Grid, Slider, Ui, Window};
|
|
use gb_core::inputs::GameBoyButton;
|
|
use genesis_core::GenesisControllerType;
|
|
use genesis_core::input::GenesisButton;
|
|
use jgenesis_common::input::Player;
|
|
use jgenesis_native_config::input::InputAppConfig;
|
|
use jgenesis_native_driver::config::input::{
|
|
InputConfig, JoystickInput, KeyboardInput, KeyboardOrMouseInput, NesControllerType,
|
|
SnesControllerType,
|
|
};
|
|
use jgenesis_native_driver::input::Hotkey;
|
|
use nes_core::input::NesButton;
|
|
use smsgg_core::SmsGgButton;
|
|
use snes_core::input::{SnesButton, SnesControllerButton, SuperScopeButton};
|
|
|
|
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
|
|
pub enum GenericButton {
|
|
SmsGg(SmsGgButton, Player),
|
|
Genesis(GenesisButton, Player),
|
|
Nes(NesButton, Player),
|
|
Snes(SnesButton, Player),
|
|
GameBoy(GameBoyButton),
|
|
Hotkey(Hotkey),
|
|
}
|
|
|
|
fn set_input<Button, KC, JC>(
|
|
input: GenericInput,
|
|
button: Button,
|
|
player: Player,
|
|
keyboard: &mut KC,
|
|
joystick: &mut JC,
|
|
) where
|
|
KC: InputConfig<Button = Button, Input = KeyboardInput>,
|
|
JC: InputConfig<Button = Button, Input = JoystickInput>,
|
|
{
|
|
match input {
|
|
GenericInput::Keyboard(input) => {
|
|
keyboard.set_input(button, player, input);
|
|
}
|
|
GenericInput::Joystick(input) => {
|
|
joystick.set_input(button, player, input);
|
|
}
|
|
GenericInput::KeyboardOrMouse(_) => {
|
|
log::error!("keyboard/mouse input set from unexpected code path");
|
|
}
|
|
}
|
|
}
|
|
|
|
pub trait InputAppConfigExt {
|
|
fn set_input(&mut self, input: GenericInput, button: GenericButton);
|
|
|
|
fn set_hotkey(&mut self, input: KeyboardInput, hotkey: Hotkey);
|
|
}
|
|
|
|
impl InputAppConfigExt for InputAppConfig {
|
|
fn set_input(&mut self, input: GenericInput, button: GenericButton) {
|
|
match button {
|
|
GenericButton::SmsGg(button, player) => {
|
|
set_input(
|
|
input,
|
|
button,
|
|
player,
|
|
&mut self.smsgg_keyboard,
|
|
&mut self.smsgg_joystick,
|
|
);
|
|
}
|
|
GenericButton::Genesis(button, player) => {
|
|
set_input(
|
|
input,
|
|
button,
|
|
player,
|
|
&mut self.genesis_keyboard,
|
|
&mut self.genesis_joystick,
|
|
);
|
|
}
|
|
GenericButton::Nes(button, player) => match &input {
|
|
GenericInput::KeyboardOrMouse(key_or_mouse_input) => {
|
|
self.nes_zapper.set_input(button, Some(key_or_mouse_input.clone()));
|
|
}
|
|
_ => {
|
|
set_input(
|
|
input,
|
|
button,
|
|
player,
|
|
&mut self.nes_keyboard,
|
|
&mut self.nes_joystick,
|
|
);
|
|
}
|
|
},
|
|
GenericButton::Snes(button, player) => {
|
|
match button {
|
|
SnesButton::Controller(button) => set_input(
|
|
input,
|
|
button,
|
|
player,
|
|
&mut self.snes_keyboard,
|
|
&mut self.snes_joystick,
|
|
),
|
|
SnesButton::SuperScope(button) => {
|
|
if let GenericInput::KeyboardOrMouse(input) = input {
|
|
self.snes_super_scope.set_button(button, input);
|
|
}
|
|
}
|
|
};
|
|
}
|
|
GenericButton::GameBoy(button) => {
|
|
set_input(input, button, Player::One, &mut self.gb_keyboard, &mut self.gb_joystick);
|
|
}
|
|
GenericButton::Hotkey(hotkey) => {
|
|
if let GenericInput::Keyboard(input) = input {
|
|
self.set_hotkey(input, hotkey);
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
fn set_hotkey(&mut self, input: KeyboardInput, hotkey: Hotkey) {
|
|
match hotkey {
|
|
Hotkey::Quit => {
|
|
self.hotkeys.quit = Some(input);
|
|
}
|
|
Hotkey::ToggleFullscreen => {
|
|
self.hotkeys.toggle_fullscreen = Some(input);
|
|
}
|
|
Hotkey::SaveState => {
|
|
self.hotkeys.save_state = Some(input);
|
|
}
|
|
Hotkey::LoadState => {
|
|
self.hotkeys.load_state = Some(input);
|
|
}
|
|
Hotkey::NextSaveStateSlot => {
|
|
self.hotkeys.next_save_state_slot = Some(input);
|
|
}
|
|
Hotkey::PrevSaveStateSlot => {
|
|
self.hotkeys.prev_save_state_slot = Some(input);
|
|
}
|
|
Hotkey::SoftReset => {
|
|
self.hotkeys.soft_reset = Some(input);
|
|
}
|
|
Hotkey::HardReset => {
|
|
self.hotkeys.hard_reset = Some(input);
|
|
}
|
|
Hotkey::Pause => {
|
|
self.hotkeys.pause = Some(input);
|
|
}
|
|
Hotkey::StepFrame => {
|
|
self.hotkeys.step_frame = Some(input);
|
|
}
|
|
Hotkey::FastForward => {
|
|
self.hotkeys.fast_forward = Some(input);
|
|
}
|
|
Hotkey::Rewind => {
|
|
self.hotkeys.rewind = Some(input);
|
|
}
|
|
Hotkey::OpenDebugger => {
|
|
self.hotkeys.open_debugger = Some(input);
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
impl App {
|
|
pub(super) fn render_smsgg_keyboard_settings(&mut self, ctx: &Context) {
|
|
let mut open = true;
|
|
Window::new("SMS/GG Keyboard Settings").open(&mut open).resizable(false).show(ctx, |ui| {
|
|
ui.add_enabled_ui(self.state.waiting_for_input.is_none(), |ui| {
|
|
Grid::new("smsgg_keyboard_grid").spacing([50.0, 0.0]).show(ui, |ui| {
|
|
for (grid_id, heading, player) in [
|
|
("smsgg_p1_keyboard_grid", "Player 1", Player::One),
|
|
("smsgg_p2_keyboard_grid", "Player 2", Player::Two),
|
|
] {
|
|
Grid::new(grid_id).show(ui, |ui| {
|
|
ui.heading(heading);
|
|
ui.end_row();
|
|
|
|
for button in SmsGgButton::ALL {
|
|
if button == SmsGgButton::Pause {
|
|
continue;
|
|
}
|
|
|
|
let current_value = self
|
|
.config
|
|
.inputs
|
|
.smsgg_keyboard
|
|
.get_input(button, player)
|
|
.cloned();
|
|
self.keyboard_input_button(
|
|
current_value,
|
|
&button.to_string(),
|
|
GenericButton::SmsGg(button, player),
|
|
ui,
|
|
);
|
|
}
|
|
});
|
|
}
|
|
});
|
|
|
|
ui.add_space(20.0);
|
|
|
|
Grid::new("smsgg_pause_keyboard_grid").show(ui, |ui| {
|
|
self.keyboard_input_button(
|
|
self.config.inputs.smsgg_keyboard.pause.clone(),
|
|
"Start/Pause",
|
|
GenericButton::SmsGg(SmsGgButton::Pause, Player::One),
|
|
ui,
|
|
);
|
|
});
|
|
});
|
|
});
|
|
if !open {
|
|
self.state.open_windows.remove(&OpenWindow::SmsGgKeyboard);
|
|
}
|
|
}
|
|
|
|
pub(super) fn render_smsgg_gamepad_settings(&mut self, ctx: &Context) {
|
|
let mut open = true;
|
|
Window::new("SMS/GG Gamepad Settings").open(&mut open).resizable(false).show(ctx, |ui| {
|
|
ui.add_enabled_ui(self.state.waiting_for_input.is_none(), |ui| {
|
|
Grid::new("smsgg_gamepad_grid").spacing([50.0, 0.0]).show(ui, |ui| {
|
|
for (grid_id, heading, player) in [
|
|
("smsgg_p1_gamepad_grid", "Player 1", Player::One),
|
|
("smsgg_p2_gamepad_grid", "Player 2", Player::Two),
|
|
] {
|
|
Grid::new(grid_id).show(ui, |ui| {
|
|
ui.heading(heading);
|
|
ui.end_row();
|
|
|
|
for button in SmsGgButton::ALL {
|
|
if button == SmsGgButton::Pause {
|
|
continue;
|
|
}
|
|
|
|
let current_value = self
|
|
.config
|
|
.inputs
|
|
.smsgg_joystick
|
|
.get_input(button, player)
|
|
.cloned();
|
|
self.gamepad_input_button(
|
|
current_value,
|
|
&button.to_string(),
|
|
GenericButton::SmsGg(button, player),
|
|
ui,
|
|
);
|
|
}
|
|
});
|
|
}
|
|
});
|
|
|
|
ui.add_space(20.0);
|
|
|
|
Grid::new("smsgg_pause_gamepad_grid").show(ui, |ui| {
|
|
self.gamepad_input_button(
|
|
self.config.inputs.smsgg_joystick.pause.clone(),
|
|
"Start/Pause",
|
|
GenericButton::SmsGg(SmsGgButton::Pause, Player::One),
|
|
ui,
|
|
);
|
|
});
|
|
|
|
ui.add_space(20.0);
|
|
self.render_axis_deadzone_input(ui);
|
|
});
|
|
});
|
|
if !open {
|
|
self.state.open_windows.remove(&OpenWindow::SmsGgGamepad);
|
|
}
|
|
}
|
|
|
|
pub(super) fn render_genesis_keyboard_settings(&mut self, ctx: &Context) {
|
|
let mut open = true;
|
|
Window::new("Genesis Keyboard Settings").open(&mut open).resizable(false).show(ctx, |ui| {
|
|
ui.add_enabled_ui(self.state.waiting_for_input.is_none(), |ui| {
|
|
Grid::new("genesis_keyboard_grid").spacing([50.0, 0.0]).show(ui, |ui| {
|
|
for (grid_id, heading, player) in [
|
|
("genesis_p1_keyboard_grid", "Player 1", Player::One),
|
|
("genesis_p2_keyboard_grid", "Player 2", Player::Two),
|
|
] {
|
|
Grid::new(grid_id).show(ui, |ui| {
|
|
ui.heading(heading);
|
|
ui.end_row();
|
|
|
|
for button in GenesisButton::ALL {
|
|
let current_value = self
|
|
.config
|
|
.inputs
|
|
.genesis_keyboard
|
|
.get_input(button, player)
|
|
.cloned();
|
|
self.keyboard_input_button(
|
|
current_value,
|
|
&button.to_string(),
|
|
GenericButton::Genesis(button, player),
|
|
ui,
|
|
);
|
|
}
|
|
});
|
|
}
|
|
});
|
|
|
|
ui.add_space(30.0);
|
|
|
|
self.controller_type_input("Player 1 controller", Player::One, ui);
|
|
self.controller_type_input("Player 2 controller", Player::Two, ui);
|
|
});
|
|
});
|
|
if !open {
|
|
self.state.open_windows.remove(&OpenWindow::GenesisKeyboard);
|
|
}
|
|
}
|
|
|
|
pub(super) fn render_genesis_gamepad_settings(&mut self, ctx: &Context) {
|
|
let mut open = true;
|
|
Window::new("Genesis Gamepad Settings").open(&mut open).resizable(false).show(ctx, |ui| {
|
|
ui.add_enabled_ui(self.state.waiting_for_input.is_none(), |ui| {
|
|
Grid::new("genesis_gamepad_grid").spacing([50.0, 0.0]).show(ui, |ui| {
|
|
for (grid_id, heading, player) in [
|
|
("genesis_p1_gamepad_grid", "Player 1", Player::One),
|
|
("genesis_p2_gamepad_grid", "Player 2", Player::Two),
|
|
] {
|
|
Grid::new(grid_id).show(ui, |ui| {
|
|
ui.heading(heading);
|
|
ui.end_row();
|
|
|
|
for button in GenesisButton::ALL {
|
|
let current_value = self
|
|
.config
|
|
.inputs
|
|
.genesis_joystick
|
|
.get_input(button, player)
|
|
.cloned();
|
|
self.gamepad_input_button(
|
|
current_value,
|
|
&button.to_string(),
|
|
GenericButton::Genesis(button, player),
|
|
ui,
|
|
);
|
|
}
|
|
});
|
|
}
|
|
});
|
|
|
|
ui.add_space(30.0);
|
|
|
|
self.render_axis_deadzone_input(ui);
|
|
|
|
ui.add_space(20.0);
|
|
|
|
self.controller_type_input("Player 1 controller", Player::One, ui);
|
|
self.controller_type_input("Player 2 controller", Player::Two, ui);
|
|
});
|
|
});
|
|
if !open {
|
|
self.state.open_windows.remove(&OpenWindow::GenesisGamepad);
|
|
}
|
|
}
|
|
|
|
pub(super) fn render_nes_keyboard_settings(&mut self, ctx: &Context) {
|
|
let mut open = true;
|
|
Window::new("NES Keyboard Settings").open(&mut open).resizable(false).show(ctx, |ui| {
|
|
ui.add_enabled_ui(self.state.waiting_for_input.is_none(), |ui| {
|
|
Grid::new("nes_keyboard_grid").spacing([50.0, 0.0]).show(ui, |ui| {
|
|
for (grid_id, heading, player) in [
|
|
("nes_p1_keyboard_grid", "Player 1", Player::One),
|
|
("nes_p2_keyboard_grid", "Player 2", Player::Two),
|
|
] {
|
|
Grid::new(grid_id).show(ui, |ui| {
|
|
ui.heading(heading);
|
|
ui.end_row();
|
|
|
|
for button in NesButton::ALL {
|
|
let current_value = self
|
|
.config
|
|
.inputs
|
|
.nes_keyboard
|
|
.get_input(button, player)
|
|
.cloned();
|
|
self.keyboard_input_button(
|
|
current_value,
|
|
&button.to_string(),
|
|
GenericButton::Nes(button, player),
|
|
ui,
|
|
);
|
|
}
|
|
});
|
|
}
|
|
});
|
|
});
|
|
});
|
|
if !open {
|
|
self.state.open_windows.remove(&OpenWindow::NesKeyboard);
|
|
}
|
|
}
|
|
|
|
pub(super) fn render_nes_joystick_settings(&mut self, ctx: &Context) {
|
|
let mut open = true;
|
|
Window::new("NES Gamepad Settings").open(&mut open).resizable(false).show(ctx, |ui| {
|
|
ui.add_enabled_ui(self.state.waiting_for_input.is_none(), |ui| {
|
|
Grid::new("nes_gamepad_grid").spacing([50.0, 0.0]).show(ui, |ui| {
|
|
for (grid_id, heading, player) in [
|
|
("nes_p1_gamepad_grid", "Player 1", Player::One),
|
|
("nes_p2_gamepad_grid", "Player 2", Player::Two),
|
|
] {
|
|
Grid::new(grid_id).show(ui, |ui| {
|
|
ui.heading(heading);
|
|
ui.end_row();
|
|
|
|
for button in NesButton::ALL {
|
|
let current_value = self
|
|
.config
|
|
.inputs
|
|
.nes_joystick
|
|
.get_input(button, player)
|
|
.cloned();
|
|
self.gamepad_input_button(
|
|
current_value,
|
|
&button.to_string(),
|
|
GenericButton::Nes(button, player),
|
|
ui,
|
|
);
|
|
}
|
|
});
|
|
}
|
|
});
|
|
|
|
ui.add_space(30.0);
|
|
|
|
self.render_axis_deadzone_input(ui);
|
|
});
|
|
});
|
|
if !open {
|
|
self.state.open_windows.remove(&OpenWindow::NesGamepad);
|
|
}
|
|
}
|
|
|
|
pub(super) fn render_nes_peripheral_settings(&mut self, ctx: &Context) {
|
|
let mut open = true;
|
|
Window::new("NES Peripheral Settings").open(&mut open).resizable(false).show(ctx, |ui| {
|
|
ui.add_enabled_ui(self.state.waiting_for_input.is_none(), |ui| {
|
|
ui.group(|ui| {
|
|
ui.label("P2 input device");
|
|
|
|
ui.horizontal(|ui| {
|
|
ui.radio_value(
|
|
&mut self.config.inputs.nes_p2_type,
|
|
NesControllerType::Gamepad,
|
|
"Gamepad",
|
|
);
|
|
ui.radio_value(
|
|
&mut self.config.inputs.nes_p2_type,
|
|
NesControllerType::Zapper,
|
|
"Zapper",
|
|
);
|
|
});
|
|
});
|
|
|
|
ui.add_space(10.0);
|
|
|
|
ui.heading("Zapper");
|
|
|
|
Grid::new("zapper_grid").show(ui, |ui| {
|
|
self.zapper_button(
|
|
self.config.inputs.nes_zapper.fire.clone(),
|
|
"Pull trigger",
|
|
NesButton::ZapperFire,
|
|
ui,
|
|
);
|
|
|
|
self.zapper_button(
|
|
self.config.inputs.nes_zapper.force_offscreen.clone(),
|
|
"Force offscreen (while held)",
|
|
NesButton::ZapperForceOffscreen,
|
|
ui,
|
|
);
|
|
});
|
|
});
|
|
});
|
|
if !open {
|
|
self.state.open_windows.remove(&OpenWindow::NesPeripherals);
|
|
}
|
|
}
|
|
|
|
pub(super) fn render_snes_keyboard_settings(&mut self, ctx: &Context) {
|
|
let mut open = true;
|
|
Window::new("SNES Keyboard Settings").open(&mut open).resizable(false).show(ctx, |ui| {
|
|
ui.add_enabled_ui(self.state.waiting_for_input.is_none(), |ui| {
|
|
Grid::new("snes_keyboard_grid").spacing([50.0, 0.0]).show(ui, |ui| {
|
|
for (grid_id, heading, player) in [
|
|
("snes_p1_keyboard_grid", "Player 1", Player::One),
|
|
("snes_p2_keyboard_grid", "Player 2", Player::Two),
|
|
] {
|
|
Grid::new(grid_id).show(ui, |ui| {
|
|
ui.heading(heading);
|
|
ui.end_row();
|
|
|
|
for button in SnesControllerButton::ALL {
|
|
let current_value = self
|
|
.config
|
|
.inputs
|
|
.snes_keyboard
|
|
.get_input(button, player)
|
|
.cloned();
|
|
self.keyboard_input_button(
|
|
current_value,
|
|
&button.to_string(),
|
|
GenericButton::Snes(SnesButton::Controller(button), player),
|
|
ui,
|
|
);
|
|
}
|
|
});
|
|
}
|
|
});
|
|
});
|
|
});
|
|
if !open {
|
|
self.state.open_windows.remove(&OpenWindow::SnesKeyboard);
|
|
}
|
|
}
|
|
|
|
pub(super) fn render_snes_gamepad_settings(&mut self, ctx: &Context) {
|
|
let mut open = true;
|
|
Window::new("SNES Gamepad Settings").open(&mut open).resizable(false).show(ctx, |ui| {
|
|
ui.add_enabled_ui(self.state.waiting_for_input.is_none(), |ui| {
|
|
Grid::new("snes_gamepad_grid").spacing([50.0, 0.0]).show(ui, |ui| {
|
|
for (grid_id, heading, player) in [
|
|
("snes_p1_gamepad_grid", "Player 1", Player::One),
|
|
("snes_p2_gamepad_grid", "Player 2", Player::Two),
|
|
] {
|
|
Grid::new(grid_id).show(ui, |ui| {
|
|
ui.heading(heading);
|
|
ui.end_row();
|
|
|
|
for button in SnesControllerButton::ALL {
|
|
let current_value = self
|
|
.config
|
|
.inputs
|
|
.snes_joystick
|
|
.get_input(button, player)
|
|
.cloned();
|
|
self.gamepad_input_button(
|
|
current_value,
|
|
&button.to_string(),
|
|
GenericButton::Snes(SnesButton::Controller(button), player),
|
|
ui,
|
|
);
|
|
}
|
|
});
|
|
}
|
|
});
|
|
|
|
ui.add_space(30.0);
|
|
|
|
self.render_axis_deadzone_input(ui);
|
|
});
|
|
});
|
|
if !open {
|
|
self.state.open_windows.remove(&OpenWindow::SnesGamepad);
|
|
}
|
|
}
|
|
|
|
pub(super) fn render_snes_peripheral_settings(&mut self, ctx: &Context) {
|
|
let mut open = true;
|
|
Window::new("SNES Peripheral Settings").open(&mut open).resizable(false).show(ctx, |ui| {
|
|
ui.add_enabled_ui(self.state.waiting_for_input.is_none(), |ui| {
|
|
ui.group(|ui| {
|
|
ui.label("P2 input device");
|
|
|
|
ui.horizontal(|ui| {
|
|
ui.radio_value(
|
|
&mut self.config.inputs.snes_p2_type,
|
|
SnesControllerType::Gamepad,
|
|
"Gamepad",
|
|
);
|
|
ui.radio_value(
|
|
&mut self.config.inputs.snes_p2_type,
|
|
SnesControllerType::SuperScope,
|
|
"Super Scope",
|
|
);
|
|
});
|
|
});
|
|
|
|
ui.add_space(10.0);
|
|
|
|
ui.heading("Super Scope");
|
|
|
|
Grid::new("super_scope_grid").show(ui, |ui| {
|
|
self.super_scope_button(
|
|
self.config.inputs.snes_super_scope.fire.clone(),
|
|
"Fire",
|
|
SuperScopeButton::Fire,
|
|
ui,
|
|
);
|
|
self.super_scope_button(
|
|
self.config.inputs.snes_super_scope.cursor.clone(),
|
|
"Cursor",
|
|
SuperScopeButton::Cursor,
|
|
ui,
|
|
);
|
|
self.super_scope_button(
|
|
self.config.inputs.snes_super_scope.pause.clone(),
|
|
"Pause",
|
|
SuperScopeButton::Pause,
|
|
ui,
|
|
);
|
|
self.super_scope_button(
|
|
self.config.inputs.snes_super_scope.turbo_toggle.clone(),
|
|
"Turbo (Toggle)",
|
|
SuperScopeButton::TurboToggle,
|
|
ui,
|
|
);
|
|
});
|
|
});
|
|
});
|
|
if !open {
|
|
self.state.open_windows.remove(&OpenWindow::SnesPeripherals);
|
|
}
|
|
}
|
|
|
|
pub(super) fn render_gb_keyboard_settings(&mut self, ctx: &Context) {
|
|
let mut open = true;
|
|
Window::new("Game Boy Keyboard Settings").open(&mut open).resizable(false).show(
|
|
ctx,
|
|
|ui| {
|
|
ui.add_enabled_ui(self.state.waiting_for_input.is_none(), |ui| {
|
|
Grid::new("gb_keyboard_grid").show(ui, |ui| {
|
|
for button in GameBoyButton::ALL {
|
|
let current_value = self
|
|
.config
|
|
.inputs
|
|
.gb_keyboard
|
|
.get_input(button, Player::One)
|
|
.cloned();
|
|
self.keyboard_input_button(
|
|
current_value,
|
|
&button.to_string(),
|
|
GenericButton::GameBoy(button),
|
|
ui,
|
|
);
|
|
}
|
|
});
|
|
});
|
|
},
|
|
);
|
|
if !open {
|
|
self.state.open_windows.remove(&OpenWindow::GameBoyKeyboard);
|
|
}
|
|
}
|
|
|
|
pub(super) fn render_gb_joystick_settings(&mut self, ctx: &Context) {
|
|
let mut open = true;
|
|
Window::new("Game Boy Joystick Settings").open(&mut open).resizable(false).show(
|
|
ctx,
|
|
|ui| {
|
|
ui.add_enabled_ui(self.state.waiting_for_input.is_none(), |ui| {
|
|
Grid::new("gb_joystick_grid").show(ui, |ui| {
|
|
for button in GameBoyButton::ALL {
|
|
let current_value = self
|
|
.config
|
|
.inputs
|
|
.gb_joystick
|
|
.get_input(button, Player::One)
|
|
.cloned();
|
|
self.gamepad_input_button(
|
|
current_value,
|
|
&button.to_string(),
|
|
GenericButton::GameBoy(button),
|
|
ui,
|
|
);
|
|
}
|
|
});
|
|
|
|
ui.add_space(30.0);
|
|
|
|
self.render_axis_deadzone_input(ui);
|
|
});
|
|
},
|
|
);
|
|
if !open {
|
|
self.state.open_windows.remove(&OpenWindow::GameBoyGamepad);
|
|
}
|
|
}
|
|
|
|
pub(super) fn render_hotkey_settings(&mut self, ctx: &Context) {
|
|
let mut open = true;
|
|
Window::new("Hotkey Settings").open(&mut open).resizable(false).show(ctx, |ui| {
|
|
ui.add_enabled_ui(self.state.waiting_for_input.is_none(), |ui| {
|
|
Grid::new("hotkeys_grid").show(ui, |ui| {
|
|
self.hotkey_button(
|
|
self.config.inputs.hotkeys.quit.clone(),
|
|
"Quit",
|
|
Hotkey::Quit,
|
|
ui,
|
|
);
|
|
self.hotkey_button(
|
|
self.config.inputs.hotkeys.toggle_fullscreen.clone(),
|
|
"Toggle fullscreen",
|
|
Hotkey::ToggleFullscreen,
|
|
ui,
|
|
);
|
|
self.hotkey_button(
|
|
self.config.inputs.hotkeys.save_state.clone(),
|
|
"Save state",
|
|
Hotkey::SaveState,
|
|
ui,
|
|
);
|
|
self.hotkey_button(
|
|
self.config.inputs.hotkeys.load_state.clone(),
|
|
"Load state",
|
|
Hotkey::LoadState,
|
|
ui,
|
|
);
|
|
self.hotkey_button(
|
|
self.config.inputs.hotkeys.next_save_state_slot.clone(),
|
|
"Next save state slot",
|
|
Hotkey::NextSaveStateSlot,
|
|
ui,
|
|
);
|
|
self.hotkey_button(
|
|
self.config.inputs.hotkeys.prev_save_state_slot.clone(),
|
|
"Previous save state slot",
|
|
Hotkey::PrevSaveStateSlot,
|
|
ui,
|
|
);
|
|
self.hotkey_button(
|
|
self.config.inputs.hotkeys.soft_reset.clone(),
|
|
"Soft reset",
|
|
Hotkey::SoftReset,
|
|
ui,
|
|
);
|
|
self.hotkey_button(
|
|
self.config.inputs.hotkeys.hard_reset.clone(),
|
|
"Hard reset",
|
|
Hotkey::HardReset,
|
|
ui,
|
|
);
|
|
self.hotkey_button(
|
|
self.config.inputs.hotkeys.pause.clone(),
|
|
"Pause/Unpause",
|
|
Hotkey::Pause,
|
|
ui,
|
|
);
|
|
self.hotkey_button(
|
|
self.config.inputs.hotkeys.step_frame.clone(),
|
|
"Step frame while paused",
|
|
Hotkey::StepFrame,
|
|
ui,
|
|
);
|
|
self.hotkey_button(
|
|
self.config.inputs.hotkeys.fast_forward.clone(),
|
|
"Fast forward",
|
|
Hotkey::FastForward,
|
|
ui,
|
|
);
|
|
self.hotkey_button(
|
|
self.config.inputs.hotkeys.rewind.clone(),
|
|
"Rewind",
|
|
Hotkey::Rewind,
|
|
ui,
|
|
);
|
|
self.hotkey_button(
|
|
self.config.inputs.hotkeys.open_debugger.clone(),
|
|
"Open memory viewer",
|
|
Hotkey::OpenDebugger,
|
|
ui,
|
|
);
|
|
});
|
|
|
|
ui.add_space(20.0);
|
|
|
|
ui.horizontal(|ui| {
|
|
ui.add(
|
|
NumericTextEdit::new(
|
|
&mut self.state.ff_multiplier_text,
|
|
&mut self.config.common.fast_forward_multiplier,
|
|
&mut self.state.ff_multiplier_invalid,
|
|
)
|
|
.with_validation(|value| value != 0)
|
|
.desired_width(30.0),
|
|
);
|
|
|
|
ui.label("Fast forward multiplier");
|
|
});
|
|
if self.state.ff_multiplier_invalid {
|
|
ui.colored_label(
|
|
Color32::RED,
|
|
"Fast forward multiplier must be a positive integer",
|
|
);
|
|
}
|
|
|
|
ui.horizontal(|ui| {
|
|
ui.add(
|
|
NumericTextEdit::new(
|
|
&mut self.state.rewind_buffer_len_text,
|
|
&mut self.config.common.rewind_buffer_length_seconds,
|
|
&mut self.state.rewind_buffer_len_invalid,
|
|
)
|
|
.desired_width(30.0),
|
|
);
|
|
|
|
ui.label("Rewind buffer length in seconds");
|
|
});
|
|
if self.state.rewind_buffer_len_invalid {
|
|
ui.colored_label(
|
|
Color32::RED,
|
|
"Rewind buffer length must be a non-negative integer",
|
|
);
|
|
}
|
|
});
|
|
});
|
|
if !open {
|
|
self.state.open_windows.remove(&OpenWindow::Hotkeys);
|
|
}
|
|
}
|
|
|
|
fn render_axis_deadzone_input(&mut self, ui: &mut Ui) {
|
|
ui.horizontal(|ui| {
|
|
ui.label("Joystick axis deadzone:");
|
|
ui.add(Slider::new(&mut self.config.inputs.axis_deadzone, 0..=32767));
|
|
});
|
|
}
|
|
|
|
fn keyboard_input_button(
|
|
&mut self,
|
|
current_value: Option<KeyboardInput>,
|
|
label: &str,
|
|
button: GenericButton,
|
|
ui: &mut Ui,
|
|
) {
|
|
let text = current_value.map_or_else(|| "<None>".into(), |input| input.keycode);
|
|
self.input_button(&text, label, InputType::Keyboard, button, ui);
|
|
}
|
|
|
|
fn gamepad_input_button(
|
|
&mut self,
|
|
current_value: Option<JoystickInput>,
|
|
label: &str,
|
|
button: GenericButton,
|
|
ui: &mut Ui,
|
|
) {
|
|
let text = current_value.map_or_else(
|
|
|| "<None>".into(),
|
|
|input| format!("{} ({})", input.action, input.device),
|
|
);
|
|
self.input_button(&text, label, InputType::Joystick, button, ui);
|
|
}
|
|
|
|
fn input_button(
|
|
&mut self,
|
|
text: &str,
|
|
label: &str,
|
|
input_type: InputType,
|
|
button: GenericButton,
|
|
ui: &mut Ui,
|
|
) {
|
|
ui.label(format!("{label}:"));
|
|
|
|
if ui.button(text).clicked() {
|
|
log::debug!("Sending collect input command for button {button:?}");
|
|
self.emu_thread.send(EmuThreadCommand::CollectInput {
|
|
input_type,
|
|
axis_deadzone: self.config.inputs.axis_deadzone,
|
|
});
|
|
self.state.waiting_for_input = Some(button);
|
|
}
|
|
|
|
if ui.button("Clear").clicked() {
|
|
log::debug!("Clearing button {button:?} for input_type {input_type:?}");
|
|
self.clear_button_in_config(button, input_type);
|
|
}
|
|
|
|
ui.end_row();
|
|
}
|
|
|
|
fn clear_button_in_config(&mut self, button: GenericButton, input_type: InputType) {
|
|
match button {
|
|
GenericButton::SmsGg(button, player) => match input_type {
|
|
InputType::Keyboard => {
|
|
self.config.inputs.smsgg_keyboard.clear_input(button, player);
|
|
}
|
|
InputType::Joystick => {
|
|
self.config.inputs.smsgg_joystick.clear_input(button, player);
|
|
}
|
|
InputType::KeyboardOrMouse => {}
|
|
},
|
|
GenericButton::Genesis(button, player) => match input_type {
|
|
InputType::Keyboard => {
|
|
self.config.inputs.genesis_keyboard.clear_input(button, player);
|
|
}
|
|
InputType::Joystick => {
|
|
self.config.inputs.genesis_joystick.clear_input(button, player);
|
|
}
|
|
InputType::KeyboardOrMouse => {}
|
|
},
|
|
GenericButton::Nes(button, player) => match input_type {
|
|
InputType::Keyboard => self.config.inputs.nes_keyboard.clear_input(button, player),
|
|
InputType::Joystick => self.config.inputs.nes_joystick.clear_input(button, player),
|
|
InputType::KeyboardOrMouse => self.config.inputs.nes_zapper.set_input(button, None),
|
|
},
|
|
GenericButton::Snes(button, player) => match (input_type, button) {
|
|
(InputType::Keyboard, SnesButton::Controller(button)) => {
|
|
self.config.inputs.snes_keyboard.clear_input(button, player);
|
|
}
|
|
(InputType::Joystick, SnesButton::Controller(button)) => {
|
|
self.config.inputs.snes_joystick.clear_input(button, player);
|
|
}
|
|
(InputType::KeyboardOrMouse, SnesButton::SuperScope(button)) => {
|
|
self.config.inputs.snes_super_scope.clear_button(button);
|
|
}
|
|
_ => {}
|
|
},
|
|
GenericButton::GameBoy(button) => match input_type {
|
|
InputType::Keyboard => self.config.inputs.gb_keyboard.clear_button(button),
|
|
InputType::Joystick => self.config.inputs.gb_joystick.clear_button(button),
|
|
InputType::KeyboardOrMouse => {}
|
|
},
|
|
GenericButton::Hotkey(hotkey) => match hotkey {
|
|
Hotkey::Quit => {
|
|
self.config.inputs.hotkeys.quit = None;
|
|
}
|
|
Hotkey::ToggleFullscreen => {
|
|
self.config.inputs.hotkeys.toggle_fullscreen = None;
|
|
}
|
|
Hotkey::SaveState => {
|
|
self.config.inputs.hotkeys.save_state = None;
|
|
}
|
|
Hotkey::LoadState => {
|
|
self.config.inputs.hotkeys.load_state = None;
|
|
}
|
|
Hotkey::NextSaveStateSlot => {
|
|
self.config.inputs.hotkeys.next_save_state_slot = None;
|
|
}
|
|
Hotkey::PrevSaveStateSlot => {
|
|
self.config.inputs.hotkeys.prev_save_state_slot = None;
|
|
}
|
|
Hotkey::SoftReset => {
|
|
self.config.inputs.hotkeys.soft_reset = None;
|
|
}
|
|
Hotkey::HardReset => {
|
|
self.config.inputs.hotkeys.hard_reset = None;
|
|
}
|
|
Hotkey::Pause => {
|
|
self.config.inputs.hotkeys.pause = None;
|
|
}
|
|
Hotkey::StepFrame => {
|
|
self.config.inputs.hotkeys.step_frame = None;
|
|
}
|
|
Hotkey::FastForward => {
|
|
self.config.inputs.hotkeys.fast_forward = None;
|
|
}
|
|
Hotkey::Rewind => {
|
|
self.config.inputs.hotkeys.rewind = None;
|
|
}
|
|
Hotkey::OpenDebugger => {
|
|
self.config.inputs.hotkeys.open_debugger = None;
|
|
}
|
|
},
|
|
}
|
|
}
|
|
|
|
fn controller_type_input(&mut self, label: &str, player: Player, ui: &mut Ui) {
|
|
ui.group(|ui| {
|
|
ui.label(label);
|
|
|
|
let controller_type_field = match player {
|
|
Player::One => &mut self.config.inputs.genesis_p1_type,
|
|
Player::Two => &mut self.config.inputs.genesis_p2_type,
|
|
};
|
|
|
|
ui.horizontal(|ui| {
|
|
ui.radio_value(
|
|
controller_type_field,
|
|
GenesisControllerType::ThreeButton,
|
|
"3-button",
|
|
);
|
|
ui.radio_value(controller_type_field, GenesisControllerType::SixButton, "6-button");
|
|
ui.radio_value(controller_type_field, GenesisControllerType::None, "None");
|
|
});
|
|
});
|
|
}
|
|
|
|
fn hotkey_button(
|
|
&mut self,
|
|
current_value: Option<KeyboardInput>,
|
|
label: &str,
|
|
hotkey: Hotkey,
|
|
ui: &mut Ui,
|
|
) {
|
|
ui.label(format!("{label}:"));
|
|
|
|
let text = match current_value {
|
|
Some(value) => value.keycode,
|
|
None => "<None>".into(),
|
|
};
|
|
if ui.button(text).clicked() {
|
|
log::debug!("Sending collect input command for hotkey {hotkey:?}");
|
|
self.emu_thread.send(EmuThreadCommand::CollectInput {
|
|
input_type: InputType::Keyboard,
|
|
axis_deadzone: self.config.inputs.axis_deadzone,
|
|
});
|
|
self.state.waiting_for_input = Some(GenericButton::Hotkey(hotkey));
|
|
}
|
|
|
|
if ui.button("Clear").clicked() {
|
|
self.clear_button_in_config(GenericButton::Hotkey(hotkey), InputType::Keyboard);
|
|
}
|
|
|
|
ui.end_row();
|
|
}
|
|
|
|
fn zapper_button(
|
|
&mut self,
|
|
current_value: Option<KeyboardOrMouseInput>,
|
|
label: &str,
|
|
button: NesButton,
|
|
ui: &mut Ui,
|
|
) {
|
|
ui.label(format!("{label}:"));
|
|
|
|
let text = match current_value {
|
|
Some(value) => value.to_string(),
|
|
None => "<None>".into(),
|
|
};
|
|
if ui.button(text).clicked() {
|
|
log::debug!("Sending collect input request for Zapper button {button:?}");
|
|
self.emu_thread.send(EmuThreadCommand::CollectInput {
|
|
input_type: InputType::KeyboardOrMouse,
|
|
axis_deadzone: self.config.inputs.axis_deadzone,
|
|
});
|
|
self.state.waiting_for_input = Some(GenericButton::Nes(button, Player::One));
|
|
}
|
|
|
|
if ui.button("Clear").clicked() {
|
|
self.clear_button_in_config(
|
|
GenericButton::Nes(button, Player::One),
|
|
InputType::KeyboardOrMouse,
|
|
);
|
|
}
|
|
|
|
ui.end_row();
|
|
}
|
|
|
|
fn super_scope_button(
|
|
&mut self,
|
|
current_value: Option<KeyboardOrMouseInput>,
|
|
label: &str,
|
|
button: SuperScopeButton,
|
|
ui: &mut Ui,
|
|
) {
|
|
ui.label(format!("{label}:"));
|
|
|
|
let text = match current_value {
|
|
Some(value) => value.to_string(),
|
|
None => "<None>".into(),
|
|
};
|
|
if ui.button(text).clicked() {
|
|
log::debug!("Sending collect input request for Super Scope button {button:?}");
|
|
self.emu_thread.send(EmuThreadCommand::CollectInput {
|
|
input_type: InputType::KeyboardOrMouse,
|
|
axis_deadzone: self.config.inputs.axis_deadzone,
|
|
});
|
|
self.state.waiting_for_input =
|
|
Some(GenericButton::Snes(SnesButton::SuperScope(button), Player::One));
|
|
}
|
|
|
|
if ui.button("Clear").clicked() {
|
|
self.clear_button_in_config(
|
|
GenericButton::Snes(SnesButton::SuperScope(button), Player::One),
|
|
InputType::KeyboardOrMouse,
|
|
);
|
|
}
|
|
|
|
ui.end_row();
|
|
}
|
|
}
|