2024-11-26 05:38:03 +00:00
|
|
|
use egui::{
|
|
|
|
Button, CentralPanel, Context, Label, Layout, TopBottomPanel, Ui, ViewportBuilder, ViewportId,
|
|
|
|
};
|
|
|
|
use egui_extras::{Column, TableBuilder};
|
2024-11-29 23:54:26 +00:00
|
|
|
use gilrs::{EventType, GamepadId};
|
|
|
|
use std::sync::RwLock;
|
2024-11-26 05:38:03 +00:00
|
|
|
|
|
|
|
use crate::{
|
|
|
|
emulator::{SimId, VBKey},
|
2024-11-29 23:54:26 +00:00
|
|
|
input::{MappingProvider, Mappings},
|
2024-11-26 05:38:03 +00:00
|
|
|
};
|
|
|
|
|
|
|
|
use super::AppWindow;
|
|
|
|
|
|
|
|
pub struct InputWindow {
|
|
|
|
mappings: MappingProvider,
|
2024-11-29 23:54:26 +00:00
|
|
|
now_binding: Option<VBKey>,
|
2024-11-26 05:38:03 +00:00
|
|
|
active_tab: InputTab,
|
|
|
|
}
|
|
|
|
|
|
|
|
const KEY_NAMES: [(VBKey, &str); 14] = [
|
|
|
|
(VBKey::LU, "Up"),
|
|
|
|
(VBKey::LD, "Down"),
|
|
|
|
(VBKey::LL, "Left"),
|
|
|
|
(VBKey::LR, "Right"),
|
|
|
|
(VBKey::SEL, "Select"),
|
|
|
|
(VBKey::STA, "Start"),
|
|
|
|
(VBKey::B, "B"),
|
|
|
|
(VBKey::A, "A"),
|
|
|
|
(VBKey::LT, "L-Trigger"),
|
|
|
|
(VBKey::RT, "R-Trigger"),
|
|
|
|
(VBKey::RU, "R-Up"),
|
|
|
|
(VBKey::RD, "R-Down"),
|
|
|
|
(VBKey::RL, "R-Left"),
|
|
|
|
(VBKey::RR, "R-Right"),
|
|
|
|
];
|
|
|
|
|
|
|
|
impl InputWindow {
|
|
|
|
pub fn new(mappings: MappingProvider) -> Self {
|
|
|
|
Self {
|
|
|
|
mappings,
|
|
|
|
now_binding: None,
|
|
|
|
active_tab: InputTab::Player1,
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2024-11-29 23:54:26 +00:00
|
|
|
fn show_bindings<T: Mappings>(
|
|
|
|
&mut self,
|
|
|
|
ui: &mut Ui,
|
|
|
|
mappings: &RwLock<T>,
|
|
|
|
bind_message: &str,
|
|
|
|
) {
|
2024-11-30 00:09:00 +00:00
|
|
|
ui.horizontal(|ui| {
|
|
|
|
if ui.button("Use defaults").clicked() {
|
|
|
|
mappings.write().unwrap().use_default_mappings();
|
2024-12-12 04:44:14 +00:00
|
|
|
self.mappings.save();
|
2024-11-30 00:09:00 +00:00
|
|
|
self.now_binding = None;
|
|
|
|
}
|
|
|
|
if ui.button("Clear all").clicked() {
|
|
|
|
mappings.write().unwrap().clear_all_mappings();
|
2024-12-12 04:44:14 +00:00
|
|
|
self.mappings.save();
|
2024-11-30 00:09:00 +00:00
|
|
|
self.now_binding = None;
|
|
|
|
}
|
|
|
|
});
|
|
|
|
ui.separator();
|
2024-11-29 23:54:26 +00:00
|
|
|
let mut names = {
|
2024-11-26 05:38:03 +00:00
|
|
|
let mapping = mappings.read().unwrap();
|
2024-11-29 23:54:26 +00:00
|
|
|
mapping.mapping_names()
|
2024-11-26 05:38:03 +00:00
|
|
|
};
|
|
|
|
TableBuilder::new(ui)
|
|
|
|
.column(Column::remainder())
|
|
|
|
.column(Column::remainder())
|
|
|
|
.cell_layout(Layout::left_to_right(egui::Align::Center))
|
|
|
|
.body(|mut body| {
|
|
|
|
for keys in KEY_NAMES.chunks_exact(2) {
|
|
|
|
body.row(20.0, |mut row| {
|
|
|
|
for (key, name) in keys {
|
2024-11-29 23:54:26 +00:00
|
|
|
let binding = names.remove(key).map(|mut s| {
|
|
|
|
s.sort();
|
|
|
|
s.join(", ")
|
|
|
|
});
|
2024-11-26 05:38:03 +00:00
|
|
|
row.col(|ui| {
|
|
|
|
let size = ui.available_size_before_wrap();
|
|
|
|
let width = size.x;
|
|
|
|
let height = size.y;
|
|
|
|
ui.add_sized((width * 0.2, height), Label::new(*name));
|
2024-11-29 23:54:26 +00:00
|
|
|
let label_text = if self.now_binding == Some(*key) {
|
|
|
|
bind_message
|
2024-11-26 05:38:03 +00:00
|
|
|
} else {
|
2024-11-29 23:54:26 +00:00
|
|
|
binding.as_deref().unwrap_or("")
|
2024-11-26 05:38:03 +00:00
|
|
|
};
|
|
|
|
if ui
|
|
|
|
.add_sized((width * 0.6, height), Button::new(label_text))
|
|
|
|
.clicked()
|
|
|
|
{
|
2024-11-29 23:54:26 +00:00
|
|
|
self.now_binding = Some(*key);
|
2024-11-26 05:38:03 +00:00
|
|
|
}
|
|
|
|
if ui
|
|
|
|
.add_sized(ui.available_size(), Button::new("Clear"))
|
|
|
|
.clicked()
|
|
|
|
{
|
|
|
|
let mut mapping = mappings.write().unwrap();
|
2024-11-29 23:54:26 +00:00
|
|
|
mapping.clear_mappings(*key);
|
2024-12-12 04:44:14 +00:00
|
|
|
drop(mapping);
|
|
|
|
self.mappings.save();
|
2024-11-29 23:54:26 +00:00
|
|
|
self.now_binding = None;
|
2024-11-26 05:38:03 +00:00
|
|
|
}
|
|
|
|
});
|
|
|
|
}
|
|
|
|
});
|
|
|
|
}
|
|
|
|
});
|
|
|
|
}
|
2024-11-29 19:36:46 +00:00
|
|
|
|
2024-11-29 23:54:26 +00:00
|
|
|
fn show_key_bindings(&mut self, ui: &mut Ui, sim_id: SimId) {
|
|
|
|
let mappings = self.mappings.for_sim(sim_id).clone();
|
|
|
|
self.show_bindings(ui, &mappings, "Press any key");
|
|
|
|
}
|
|
|
|
|
2024-11-29 19:36:46 +00:00
|
|
|
fn show_gamepads(&mut self, ui: &mut Ui) {
|
|
|
|
let mut gamepads = self.mappings.gamepad_info();
|
|
|
|
gamepads.sort_by_key(|g| usize::from(g.id));
|
|
|
|
|
|
|
|
if gamepads.is_empty() {
|
|
|
|
ui.label("No gamepads connected.");
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
|
|
|
for (index, gamepad) in gamepads.into_iter().enumerate() {
|
|
|
|
ui.horizontal(|ui| {
|
|
|
|
ui.label(format!("Gamepad {index}: {}", gamepad.name));
|
|
|
|
let mut bound_to = gamepad.bound_to;
|
|
|
|
let mut rebind = false;
|
|
|
|
rebind |= ui
|
|
|
|
.selectable_value(&mut bound_to, Some(SimId::Player1), "Player 1")
|
|
|
|
.changed();
|
|
|
|
rebind |= ui
|
|
|
|
.selectable_value(&mut bound_to, Some(SimId::Player2), "Player 2")
|
|
|
|
.changed();
|
|
|
|
rebind |= ui.selectable_value(&mut bound_to, None, "Nobody").changed();
|
|
|
|
if rebind {
|
|
|
|
match bound_to {
|
|
|
|
Some(sim_id) => self.mappings.assign_gamepad(gamepad.id, sim_id),
|
|
|
|
None => self.mappings.unassign_gamepad(gamepad.id),
|
|
|
|
}
|
|
|
|
}
|
2024-11-29 23:54:26 +00:00
|
|
|
ui.separator();
|
|
|
|
if ui.button("Rebind").clicked() {
|
|
|
|
self.active_tab = InputTab::RebindGamepad(gamepad.id);
|
|
|
|
}
|
2024-11-29 19:36:46 +00:00
|
|
|
});
|
|
|
|
}
|
|
|
|
}
|
2024-11-29 23:54:26 +00:00
|
|
|
|
|
|
|
fn show_gamepad_bindings(&mut self, ui: &mut Ui, gamepad_id: GamepadId) {
|
|
|
|
let Some(mappings) = self.mappings.for_gamepad(gamepad_id) else {
|
|
|
|
self.active_tab = InputTab::Gamepads;
|
|
|
|
return;
|
|
|
|
};
|
|
|
|
self.show_bindings(ui, &mappings, "Press any input");
|
|
|
|
}
|
2024-11-26 05:38:03 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
impl AppWindow for InputWindow {
|
|
|
|
fn viewport_id(&self) -> ViewportId {
|
|
|
|
ViewportId::from_hash_of("input")
|
|
|
|
}
|
|
|
|
|
|
|
|
fn initial_viewport(&self) -> ViewportBuilder {
|
|
|
|
ViewportBuilder::default()
|
|
|
|
.with_title("Bind Inputs")
|
|
|
|
.with_inner_size((600.0, 400.0))
|
|
|
|
}
|
|
|
|
|
|
|
|
fn show(&mut self, ctx: &Context) {
|
|
|
|
TopBottomPanel::top("options").show(ctx, |ui| {
|
|
|
|
ui.horizontal(|ui| {
|
2024-11-29 23:54:26 +00:00
|
|
|
let old_active_tab = self.active_tab;
|
2024-11-26 05:38:03 +00:00
|
|
|
ui.selectable_value(&mut self.active_tab, InputTab::Player1, "Player 1");
|
|
|
|
ui.selectable_value(&mut self.active_tab, InputTab::Player2, "Player 2");
|
2024-11-29 19:36:46 +00:00
|
|
|
ui.selectable_value(&mut self.active_tab, InputTab::Gamepads, "Gamepads");
|
2024-11-29 23:54:26 +00:00
|
|
|
if matches!(self.active_tab, InputTab::RebindGamepad(_)) {
|
|
|
|
let tab = self.active_tab;
|
|
|
|
ui.selectable_value(&mut self.active_tab, tab, "Rebind Gamepad");
|
|
|
|
}
|
|
|
|
if old_active_tab != self.active_tab {
|
|
|
|
self.now_binding = None;
|
|
|
|
}
|
2024-11-26 05:38:03 +00:00
|
|
|
});
|
|
|
|
});
|
|
|
|
CentralPanel::default().show(ctx, |ui| {
|
|
|
|
match self.active_tab {
|
2024-11-29 19:36:46 +00:00
|
|
|
InputTab::Player1 => self.show_key_bindings(ui, SimId::Player1),
|
|
|
|
InputTab::Player2 => self.show_key_bindings(ui, SimId::Player2),
|
|
|
|
InputTab::Gamepads => self.show_gamepads(ui),
|
2024-11-29 23:54:26 +00:00
|
|
|
InputTab::RebindGamepad(id) => self.show_gamepad_bindings(ui, id),
|
2024-11-26 05:38:03 +00:00
|
|
|
};
|
|
|
|
});
|
|
|
|
}
|
|
|
|
|
2025-02-11 04:08:35 +00:00
|
|
|
fn handle_key_event(&mut self, event: &winit::event::KeyEvent) -> bool {
|
2024-11-26 05:38:03 +00:00
|
|
|
if !event.state.is_pressed() {
|
2025-02-11 04:08:35 +00:00
|
|
|
return false;
|
2024-11-26 05:38:03 +00:00
|
|
|
}
|
2024-11-29 23:54:26 +00:00
|
|
|
let sim_id = match self.active_tab {
|
|
|
|
InputTab::Player1 => SimId::Player1,
|
|
|
|
InputTab::Player2 => SimId::Player2,
|
|
|
|
_ => {
|
2025-02-11 04:08:35 +00:00
|
|
|
return false;
|
2024-11-29 23:54:26 +00:00
|
|
|
}
|
|
|
|
};
|
|
|
|
let Some(vb) = self.now_binding.take() else {
|
2025-02-11 04:08:35 +00:00
|
|
|
return false;
|
2024-11-26 05:38:03 +00:00
|
|
|
};
|
|
|
|
let mut mappings = self.mappings.for_sim(sim_id).write().unwrap();
|
|
|
|
mappings.add_keyboard_mapping(vb, event.physical_key);
|
2024-12-12 04:44:14 +00:00
|
|
|
drop(mappings);
|
|
|
|
self.mappings.save();
|
2025-02-11 04:08:35 +00:00
|
|
|
true
|
2024-11-26 05:38:03 +00:00
|
|
|
}
|
2024-11-29 23:54:26 +00:00
|
|
|
|
2025-02-11 04:08:35 +00:00
|
|
|
fn handle_gamepad_event(&mut self, event: &gilrs::Event) -> bool {
|
2024-11-29 23:54:26 +00:00
|
|
|
let InputTab::RebindGamepad(gamepad_id) = self.active_tab else {
|
2025-02-11 04:08:35 +00:00
|
|
|
return false;
|
2024-11-29 23:54:26 +00:00
|
|
|
};
|
|
|
|
if gamepad_id != event.id {
|
2025-02-11 04:08:35 +00:00
|
|
|
return false;
|
2024-11-29 23:54:26 +00:00
|
|
|
}
|
|
|
|
let Some(mappings) = self.mappings.for_gamepad(gamepad_id) else {
|
2025-02-11 04:08:35 +00:00
|
|
|
return false;
|
2024-11-29 23:54:26 +00:00
|
|
|
};
|
|
|
|
let Some(vb) = self.now_binding else {
|
2025-02-11 04:08:35 +00:00
|
|
|
return false;
|
2024-11-29 23:54:26 +00:00
|
|
|
};
|
|
|
|
match event.event {
|
|
|
|
EventType::ButtonPressed(_, code) => {
|
|
|
|
let mut mapping = mappings.write().unwrap();
|
|
|
|
mapping.add_button_mapping(vb, code);
|
|
|
|
self.now_binding.take();
|
2025-02-11 04:08:35 +00:00
|
|
|
true
|
2024-11-29 23:54:26 +00:00
|
|
|
}
|
|
|
|
EventType::AxisChanged(_, value, code) => {
|
|
|
|
if value < -0.75 {
|
|
|
|
let mut mapping = mappings.write().unwrap();
|
|
|
|
mapping.add_axis_neg_mapping(vb, code);
|
|
|
|
self.now_binding.take();
|
2025-02-11 04:08:35 +00:00
|
|
|
true
|
|
|
|
} else if value > 0.75 {
|
2024-11-29 23:54:26 +00:00
|
|
|
let mut mapping = mappings.write().unwrap();
|
|
|
|
mapping.add_axis_pos_mapping(vb, code);
|
|
|
|
self.now_binding.take();
|
2025-02-11 04:08:35 +00:00
|
|
|
true
|
|
|
|
} else {
|
|
|
|
false
|
2024-11-29 23:54:26 +00:00
|
|
|
}
|
|
|
|
}
|
2025-02-11 04:08:35 +00:00
|
|
|
_ => false,
|
2024-11-29 23:54:26 +00:00
|
|
|
}
|
|
|
|
}
|
2024-11-26 05:38:03 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
#[derive(Clone, Copy, PartialEq, Eq)]
|
|
|
|
enum InputTab {
|
|
|
|
Player1,
|
|
|
|
Player2,
|
2024-11-29 19:36:46 +00:00
|
|
|
Gamepads,
|
2024-11-29 23:54:26 +00:00
|
|
|
RebindGamepad(GamepadId),
|
2024-11-26 05:38:03 +00:00
|
|
|
}
|