Compare commits
4
Commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
3eb6b9cd3e | ||
|
|
0c35a1e234 | ||
|
|
ce7ba71ea0 | ||
|
|
24474fabd0 |
+1
-1
Submodule shrooms-vb-core updated: f45636a491...18b2c589e6
+17
-8
@@ -8,7 +8,7 @@ use egui::{
|
|||||||
use gilrs::{EventType, Gilrs};
|
use gilrs::{EventType, Gilrs};
|
||||||
use winit::{
|
use winit::{
|
||||||
application::ApplicationHandler,
|
application::ApplicationHandler,
|
||||||
event::{KeyEvent, WindowEvent},
|
event::WindowEvent,
|
||||||
event_loop::{ActiveEventLoop, EventLoopProxy},
|
event_loop::{ActiveEventLoop, EventLoopProxy},
|
||||||
window::Window,
|
window::Window,
|
||||||
};
|
};
|
||||||
@@ -83,7 +83,7 @@ impl ApplicationHandler<UserEvent> for Application {
|
|||||||
match &event {
|
match &event {
|
||||||
WindowEvent::KeyboardInput { event, .. } => {
|
WindowEvent::KeyboardInput { event, .. } => {
|
||||||
self.controllers.handle_key_event(event);
|
self.controllers.handle_key_event(event);
|
||||||
viewport.handle_key_event(event);
|
viewport.app.handle_key_event(event);
|
||||||
}
|
}
|
||||||
WindowEvent::Focused(new_focused) => {
|
WindowEvent::Focused(new_focused) => {
|
||||||
self.focused = new_focused.then_some(viewport_id);
|
self.focused = new_focused.then_some(viewport_id);
|
||||||
@@ -143,7 +143,17 @@ impl ApplicationHandler<UserEvent> for Application {
|
|||||||
|
|
||||||
fn user_event(&mut self, event_loop: &ActiveEventLoop, event: UserEvent) {
|
fn user_event(&mut self, event_loop: &ActiveEventLoop, event: UserEvent) {
|
||||||
match event {
|
match event {
|
||||||
UserEvent::GamepadEvent(event) => self.controllers.handle_gamepad_event(&event),
|
UserEvent::GamepadEvent(event) => {
|
||||||
|
self.controllers.handle_gamepad_event(&event);
|
||||||
|
let Some(viewport) = self
|
||||||
|
.focused
|
||||||
|
.as_ref()
|
||||||
|
.and_then(|id| self.viewports.get_mut(id))
|
||||||
|
else {
|
||||||
|
return;
|
||||||
|
};
|
||||||
|
viewport.app.handle_gamepad_event(&event);
|
||||||
|
}
|
||||||
UserEvent::OpenInput => {
|
UserEvent::OpenInput => {
|
||||||
let input = InputWindow::new(self.mappings.clone());
|
let input = InputWindow::new(self.mappings.clone());
|
||||||
self.open(event_loop, Box::new(input));
|
self.open(event_loop, Box::new(input));
|
||||||
@@ -248,10 +258,6 @@ impl Viewport {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn handle_key_event(&mut self, event: &KeyEvent) {
|
|
||||||
self.app.handle_key_event(event);
|
|
||||||
}
|
|
||||||
|
|
||||||
fn redraw(&mut self, event_loop: &ActiveEventLoop) -> Option<Action> {
|
fn redraw(&mut self, event_loop: &ActiveEventLoop) -> Option<Action> {
|
||||||
let mut input = self.state.take_egui_input(&self.window);
|
let mut input = self.state.take_egui_input(&self.window);
|
||||||
input.viewports = std::iter::once((ViewportId::ROOT, self.info.clone())).collect();
|
input.viewports = std::iter::once((ViewportId::ROOT, self.info.clone())).collect();
|
||||||
@@ -349,7 +355,10 @@ fn process_gamepad_input(mappings: MappingProvider, proxy: EventLoopProxy<UserEv
|
|||||||
let Some(gamepad) = gilrs.connected_gamepad(event.id) else {
|
let Some(gamepad) = gilrs.connected_gamepad(event.id) else {
|
||||||
continue;
|
continue;
|
||||||
};
|
};
|
||||||
mappings.map_gamepad(SimId::Player1, &gamepad);
|
mappings.handle_gamepad_connect(&gamepad);
|
||||||
|
}
|
||||||
|
if event.event == EventType::Disconnected {
|
||||||
|
mappings.handle_gamepad_disconnect(event.id);
|
||||||
}
|
}
|
||||||
if proxy.send_event(UserEvent::GamepadEvent(event)).is_err() {
|
if proxy.send_event(UserEvent::GamepadEvent(event)).is_err() {
|
||||||
// main thread has closed! we done
|
// main thread has closed! we done
|
||||||
|
|||||||
+214
-37
@@ -11,17 +11,34 @@ use crate::emulator::{SimId, VBKey};
|
|||||||
#[derive(Clone, Copy, PartialEq, Eq, Hash)]
|
#[derive(Clone, Copy, PartialEq, Eq, Hash)]
|
||||||
struct DeviceId(u16, u16);
|
struct DeviceId(u16, u16);
|
||||||
|
|
||||||
|
#[derive(Clone)]
|
||||||
|
pub struct GamepadInfo {
|
||||||
|
pub id: GamepadId,
|
||||||
|
pub name: String,
|
||||||
|
device_id: DeviceId,
|
||||||
|
pub bound_to: Option<SimId>,
|
||||||
|
}
|
||||||
|
|
||||||
|
pub trait Mappings {
|
||||||
|
fn mapping_names(&self) -> HashMap<VBKey, Vec<String>>;
|
||||||
|
fn clear_mappings(&mut self, key: VBKey);
|
||||||
|
fn clear_all_mappings(&mut self);
|
||||||
|
fn use_default_mappings(&mut self);
|
||||||
|
}
|
||||||
|
|
||||||
pub struct GamepadMapping {
|
pub struct GamepadMapping {
|
||||||
buttons: HashMap<Code, VBKey>,
|
buttons: HashMap<Code, VBKey>,
|
||||||
axes: HashMap<Code, (VBKey, VBKey)>,
|
axes: HashMap<Code, (VBKey, VBKey)>,
|
||||||
|
default_buttons: HashMap<Code, VBKey>,
|
||||||
|
default_axes: HashMap<Code, (VBKey, VBKey)>,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl GamepadMapping {
|
impl GamepadMapping {
|
||||||
fn for_gamepad(gamepad: &Gamepad) -> Self {
|
fn for_gamepad(gamepad: &Gamepad) -> Self {
|
||||||
let mut buttons = HashMap::new();
|
let mut default_buttons = HashMap::new();
|
||||||
let mut default_button = |btn: Button, key: VBKey| {
|
let mut default_button = |btn: Button, key: VBKey| {
|
||||||
if let Some(code) = gamepad.button_code(btn) {
|
if let Some(code) = gamepad.button_code(btn) {
|
||||||
buttons.insert(code, key);
|
default_buttons.insert(code, key);
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
default_button(Button::South, VBKey::A);
|
default_button(Button::South, VBKey::A);
|
||||||
@@ -31,10 +48,10 @@ impl GamepadMapping {
|
|||||||
default_button(Button::Start, VBKey::STA);
|
default_button(Button::Start, VBKey::STA);
|
||||||
default_button(Button::Select, VBKey::SEL);
|
default_button(Button::Select, VBKey::SEL);
|
||||||
|
|
||||||
let mut axes = HashMap::new();
|
let mut default_axes = HashMap::new();
|
||||||
let mut default_axis = |axis: Axis, neg: VBKey, pos: VBKey| {
|
let mut default_axis = |axis: Axis, neg: VBKey, pos: VBKey| {
|
||||||
if let Some(code) = gamepad.axis_code(axis) {
|
if let Some(code) = gamepad.axis_code(axis) {
|
||||||
axes.insert(code, (neg, pos));
|
default_axes.insert(code, (neg, pos));
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
default_axis(Axis::LeftStickX, VBKey::LL, VBKey::LR);
|
default_axis(Axis::LeftStickX, VBKey::LL, VBKey::LR);
|
||||||
@@ -44,7 +61,75 @@ impl GamepadMapping {
|
|||||||
default_axis(Axis::DPadX, VBKey::LL, VBKey::LR);
|
default_axis(Axis::DPadX, VBKey::LL, VBKey::LR);
|
||||||
default_axis(Axis::DPadY, VBKey::LD, VBKey::LU);
|
default_axis(Axis::DPadY, VBKey::LD, VBKey::LU);
|
||||||
|
|
||||||
Self { buttons, axes }
|
Self {
|
||||||
|
buttons: default_buttons.clone(),
|
||||||
|
axes: default_axes.clone(),
|
||||||
|
default_buttons,
|
||||||
|
default_axes,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn add_button_mapping(&mut self, key: VBKey, code: Code) {
|
||||||
|
let entry = self.buttons.entry(code).or_insert(VBKey::empty());
|
||||||
|
*entry = entry.union(key);
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn add_axis_neg_mapping(&mut self, key: VBKey, code: Code) {
|
||||||
|
let entry = self
|
||||||
|
.axes
|
||||||
|
.entry(code)
|
||||||
|
.or_insert((VBKey::empty(), VBKey::empty()));
|
||||||
|
entry.0 = entry.0.union(key);
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn add_axis_pos_mapping(&mut self, key: VBKey, code: Code) {
|
||||||
|
let entry = self
|
||||||
|
.axes
|
||||||
|
.entry(code)
|
||||||
|
.or_insert((VBKey::empty(), VBKey::empty()));
|
||||||
|
entry.1 = entry.1.union(key);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl Mappings for GamepadMapping {
|
||||||
|
fn mapping_names(&self) -> HashMap<VBKey, Vec<String>> {
|
||||||
|
let mut results: HashMap<VBKey, Vec<String>> = HashMap::new();
|
||||||
|
for (axis, (left_keys, right_keys)) in &self.axes {
|
||||||
|
for key in left_keys.iter() {
|
||||||
|
results.entry(key).or_default().push(format!("-{axis}"));
|
||||||
|
}
|
||||||
|
for key in right_keys.iter() {
|
||||||
|
results.entry(key).or_default().push(format!("+{axis}"));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
for (button, keys) in &self.buttons {
|
||||||
|
for key in keys.iter() {
|
||||||
|
results.entry(key).or_default().push(format!("{button}"));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
results
|
||||||
|
}
|
||||||
|
|
||||||
|
fn clear_mappings(&mut self, key: VBKey) {
|
||||||
|
self.axes.retain(|_, (left, right)| {
|
||||||
|
*left = left.difference(key);
|
||||||
|
*right = right.difference(key);
|
||||||
|
!(left.is_empty() && right.is_empty())
|
||||||
|
});
|
||||||
|
self.buttons.retain(|_, keys| {
|
||||||
|
*keys = keys.difference(key);
|
||||||
|
!keys.is_empty()
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
fn clear_all_mappings(&mut self) {
|
||||||
|
self.axes.clear();
|
||||||
|
self.buttons.clear();
|
||||||
|
}
|
||||||
|
|
||||||
|
fn use_default_mappings(&mut self) {
|
||||||
|
self.axes = self.default_axes.clone();
|
||||||
|
self.buttons = self.default_buttons.clone();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -73,15 +158,10 @@ impl InputMapping {
|
|||||||
let entry = self.keys.entry(keyboard_key).or_insert(VBKey::empty());
|
let entry = self.keys.entry(keyboard_key).or_insert(VBKey::empty());
|
||||||
*entry = entry.union(key);
|
*entry = entry.union(key);
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
|
||||||
pub fn clear_keyboard_mappings(&mut self, key: VBKey) {
|
impl Mappings for InputMapping {
|
||||||
self.keys.retain(|_, keys| {
|
fn mapping_names(&self) -> HashMap<VBKey, Vec<String>> {
|
||||||
*keys = keys.difference(key);
|
|
||||||
*keys != VBKey::empty()
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
pub fn keyboard_mapping_names(&self) -> HashMap<VBKey, String> {
|
|
||||||
let mut results: HashMap<VBKey, Vec<String>> = HashMap::new();
|
let mut results: HashMap<VBKey, Vec<String>> = HashMap::new();
|
||||||
for (keyboard_key, keys) in &self.keys {
|
for (keyboard_key, keys) in &self.keys {
|
||||||
let name = match keyboard_key {
|
let name = match keyboard_key {
|
||||||
@@ -93,29 +173,23 @@ impl InputMapping {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
results
|
results
|
||||||
.into_iter()
|
|
||||||
.map(|(k, mut v)| {
|
|
||||||
v.sort();
|
|
||||||
(k, v.join(", "))
|
|
||||||
})
|
|
||||||
.collect()
|
|
||||||
}
|
}
|
||||||
}
|
|
||||||
|
|
||||||
#[derive(Clone)]
|
fn clear_mappings(&mut self, key: VBKey) {
|
||||||
pub struct MappingProvider {
|
self.keys.retain(|_, keys| {
|
||||||
device_mappings: Arc<RwLock<HashMap<DeviceId, Arc<RwLock<GamepadMapping>>>>>,
|
*keys = keys.difference(key);
|
||||||
sim_mappings: HashMap<SimId, Arc<RwLock<InputMapping>>>,
|
!keys.is_empty()
|
||||||
}
|
});
|
||||||
|
}
|
||||||
|
|
||||||
impl MappingProvider {
|
fn clear_all_mappings(&mut self) {
|
||||||
pub fn new() -> Self {
|
self.keys.clear();
|
||||||
let mut mappings = HashMap::new();
|
}
|
||||||
|
|
||||||
let mut p1_mappings = InputMapping::default();
|
fn use_default_mappings(&mut self) {
|
||||||
let p2_mappings = InputMapping::default();
|
self.keys.clear();
|
||||||
let mut default_key = |code, key| {
|
let mut default_key = |code, key| {
|
||||||
p1_mappings.add_keyboard_mapping(key, PhysicalKey::Code(code));
|
self.keys.insert(PhysicalKey::Code(code), key);
|
||||||
};
|
};
|
||||||
default_key(KeyCode::KeyA, VBKey::SEL);
|
default_key(KeyCode::KeyA, VBKey::SEL);
|
||||||
default_key(KeyCode::KeyS, VBKey::STA);
|
default_key(KeyCode::KeyS, VBKey::STA);
|
||||||
@@ -131,11 +205,29 @@ impl MappingProvider {
|
|||||||
default_key(KeyCode::ArrowLeft, VBKey::LL);
|
default_key(KeyCode::ArrowLeft, VBKey::LL);
|
||||||
default_key(KeyCode::ArrowDown, VBKey::LD);
|
default_key(KeyCode::ArrowDown, VBKey::LD);
|
||||||
default_key(KeyCode::ArrowRight, VBKey::LR);
|
default_key(KeyCode::ArrowRight, VBKey::LR);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#[derive(Clone)]
|
||||||
|
pub struct MappingProvider {
|
||||||
|
device_mappings: Arc<RwLock<HashMap<DeviceId, Arc<RwLock<GamepadMapping>>>>>,
|
||||||
|
sim_mappings: HashMap<SimId, Arc<RwLock<InputMapping>>>,
|
||||||
|
gamepad_info: Arc<RwLock<HashMap<GamepadId, GamepadInfo>>>,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl MappingProvider {
|
||||||
|
pub fn new() -> Self {
|
||||||
|
let mut mappings = HashMap::new();
|
||||||
|
|
||||||
|
let mut p1_mappings = InputMapping::default();
|
||||||
|
p1_mappings.use_default_mappings();
|
||||||
|
let p2_mappings = InputMapping::default();
|
||||||
|
|
||||||
mappings.insert(SimId::Player1, Arc::new(RwLock::new(p1_mappings)));
|
mappings.insert(SimId::Player1, Arc::new(RwLock::new(p1_mappings)));
|
||||||
mappings.insert(SimId::Player2, Arc::new(RwLock::new(p2_mappings)));
|
mappings.insert(SimId::Player2, Arc::new(RwLock::new(p2_mappings)));
|
||||||
Self {
|
Self {
|
||||||
device_mappings: Arc::new(RwLock::new(HashMap::new())),
|
device_mappings: Arc::new(RwLock::new(HashMap::new())),
|
||||||
|
gamepad_info: Arc::new(RwLock::new(HashMap::new())),
|
||||||
sim_mappings: mappings,
|
sim_mappings: mappings,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -144,24 +236,109 @@ impl MappingProvider {
|
|||||||
self.sim_mappings.get(&sim_id).unwrap()
|
self.sim_mappings.get(&sim_id).unwrap()
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn map_gamepad(&self, sim_id: SimId, gamepad: &Gamepad) {
|
pub fn for_gamepad(&self, gamepad_id: GamepadId) -> Option<Arc<RwLock<GamepadMapping>>> {
|
||||||
|
let lock = self.gamepad_info.read().unwrap();
|
||||||
|
let device_id = lock.get(&gamepad_id)?.device_id;
|
||||||
|
drop(lock);
|
||||||
|
let lock = self.device_mappings.read().unwrap();
|
||||||
|
lock.get(&device_id).cloned()
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn handle_gamepad_connect(&self, gamepad: &Gamepad) {
|
||||||
let device_id = DeviceId(
|
let device_id = DeviceId(
|
||||||
gamepad.vendor_id().unwrap_or_default(),
|
gamepad.vendor_id().unwrap_or_default(),
|
||||||
gamepad.product_id().unwrap_or_default(),
|
gamepad.product_id().unwrap_or_default(),
|
||||||
);
|
);
|
||||||
let mut lock = self.device_mappings.write().unwrap();
|
let mut lock = self.device_mappings.write().unwrap();
|
||||||
let gamepad_mapping = match lock.entry(device_id) {
|
let mappings = match lock.entry(device_id) {
|
||||||
Entry::Occupied(entry) => entry.get().clone(),
|
|
||||||
Entry::Vacant(entry) => {
|
Entry::Vacant(entry) => {
|
||||||
let mappings = GamepadMapping::for_gamepad(gamepad);
|
let mappings = GamepadMapping::for_gamepad(gamepad);
|
||||||
entry.insert(Arc::new(RwLock::new(mappings))).clone()
|
entry.insert(Arc::new(RwLock::new(mappings)))
|
||||||
}
|
}
|
||||||
};
|
Entry::Occupied(entry) => entry.into_mut(),
|
||||||
|
}
|
||||||
|
.clone();
|
||||||
drop(lock);
|
drop(lock);
|
||||||
|
let mut lock = self.gamepad_info.write().unwrap();
|
||||||
|
let bound_to = SimId::values()
|
||||||
|
.into_iter()
|
||||||
|
.find(|sim_id| lock.values().all(|info| info.bound_to != Some(*sim_id)));
|
||||||
|
if let Entry::Vacant(entry) = lock.entry(gamepad.id()) {
|
||||||
|
let info = GamepadInfo {
|
||||||
|
id: *entry.key(),
|
||||||
|
name: gamepad.name().to_string(),
|
||||||
|
device_id,
|
||||||
|
bound_to,
|
||||||
|
};
|
||||||
|
entry.insert(info);
|
||||||
|
}
|
||||||
|
drop(lock);
|
||||||
|
if let Some(sim_id) = bound_to {
|
||||||
self.for_sim(sim_id)
|
self.for_sim(sim_id)
|
||||||
.write()
|
.write()
|
||||||
.unwrap()
|
.unwrap()
|
||||||
.gamepads
|
.gamepads
|
||||||
.insert(gamepad.id(), gamepad_mapping);
|
.insert(gamepad.id(), mappings);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn handle_gamepad_disconnect(&self, gamepad_id: GamepadId) {
|
||||||
|
let mut lock = self.gamepad_info.write().unwrap();
|
||||||
|
let Some(info) = lock.remove(&gamepad_id) else {
|
||||||
|
return;
|
||||||
|
};
|
||||||
|
if let Some(sim_id) = info.bound_to {
|
||||||
|
self.for_sim(sim_id)
|
||||||
|
.write()
|
||||||
|
.unwrap()
|
||||||
|
.gamepads
|
||||||
|
.remove(&gamepad_id);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn assign_gamepad(&self, gamepad_id: GamepadId, sim_id: SimId) {
|
||||||
|
self.unassign_gamepad(gamepad_id);
|
||||||
|
let mut lock = self.gamepad_info.write().unwrap();
|
||||||
|
let Some(info) = lock.get_mut(&gamepad_id) else {
|
||||||
|
return;
|
||||||
|
};
|
||||||
|
info.bound_to = Some(sim_id);
|
||||||
|
let device_id = info.device_id;
|
||||||
|
drop(lock);
|
||||||
|
let Some(device_mappings) = self
|
||||||
|
.device_mappings
|
||||||
|
.write()
|
||||||
|
.unwrap()
|
||||||
|
.get(&device_id)
|
||||||
|
.cloned()
|
||||||
|
else {
|
||||||
|
return;
|
||||||
|
};
|
||||||
|
self.for_sim(sim_id)
|
||||||
|
.write()
|
||||||
|
.unwrap()
|
||||||
|
.gamepads
|
||||||
|
.insert(gamepad_id, device_mappings);
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn unassign_gamepad(&self, gamepad_id: GamepadId) {
|
||||||
|
let mut lock = self.gamepad_info.write().unwrap();
|
||||||
|
let Some(info) = lock.get_mut(&gamepad_id) else {
|
||||||
|
return;
|
||||||
|
};
|
||||||
|
if let Some(sim_id) = info.bound_to {
|
||||||
|
let mut sim_mapping = self.for_sim(sim_id).write().unwrap();
|
||||||
|
sim_mapping.gamepads.remove(&gamepad_id);
|
||||||
|
}
|
||||||
|
info.bound_to = None;
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn gamepad_info(&self) -> Vec<GamepadInfo> {
|
||||||
|
self.gamepad_info
|
||||||
|
.read()
|
||||||
|
.unwrap()
|
||||||
|
.values()
|
||||||
|
.cloned()
|
||||||
|
.collect()
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -18,4 +18,7 @@ pub trait AppWindow {
|
|||||||
fn handle_key_event(&mut self, event: &KeyEvent) {
|
fn handle_key_event(&mut self, event: &KeyEvent) {
|
||||||
let _ = event;
|
let _ = event;
|
||||||
}
|
}
|
||||||
|
fn handle_gamepad_event(&mut self, event: &gilrs::Event) {
|
||||||
|
let _ = event;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
+139
-15
@@ -2,17 +2,19 @@ use egui::{
|
|||||||
Button, CentralPanel, Context, Label, Layout, TopBottomPanel, Ui, ViewportBuilder, ViewportId,
|
Button, CentralPanel, Context, Label, Layout, TopBottomPanel, Ui, ViewportBuilder, ViewportId,
|
||||||
};
|
};
|
||||||
use egui_extras::{Column, TableBuilder};
|
use egui_extras::{Column, TableBuilder};
|
||||||
|
use gilrs::{EventType, GamepadId};
|
||||||
|
use std::sync::RwLock;
|
||||||
|
|
||||||
use crate::{
|
use crate::{
|
||||||
emulator::{SimId, VBKey},
|
emulator::{SimId, VBKey},
|
||||||
input::MappingProvider,
|
input::{MappingProvider, Mappings},
|
||||||
};
|
};
|
||||||
|
|
||||||
use super::AppWindow;
|
use super::AppWindow;
|
||||||
|
|
||||||
pub struct InputWindow {
|
pub struct InputWindow {
|
||||||
mappings: MappingProvider,
|
mappings: MappingProvider,
|
||||||
now_binding: Option<(SimId, VBKey)>,
|
now_binding: Option<VBKey>,
|
||||||
active_tab: InputTab,
|
active_tab: InputTab,
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -42,11 +44,26 @@ impl InputWindow {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
fn render_key_bindings(&mut self, ui: &mut Ui, sim_id: SimId) {
|
fn show_bindings<T: Mappings>(
|
||||||
let mappings = self.mappings.for_sim(sim_id);
|
&mut self,
|
||||||
let binding_names = {
|
ui: &mut Ui,
|
||||||
|
mappings: &RwLock<T>,
|
||||||
|
bind_message: &str,
|
||||||
|
) {
|
||||||
|
ui.horizontal(|ui| {
|
||||||
|
if ui.button("Use defaults").clicked() {
|
||||||
|
mappings.write().unwrap().use_default_mappings();
|
||||||
|
self.now_binding = None;
|
||||||
|
}
|
||||||
|
if ui.button("Clear all").clicked() {
|
||||||
|
mappings.write().unwrap().clear_all_mappings();
|
||||||
|
self.now_binding = None;
|
||||||
|
}
|
||||||
|
});
|
||||||
|
ui.separator();
|
||||||
|
let mut names = {
|
||||||
let mapping = mappings.read().unwrap();
|
let mapping = mappings.read().unwrap();
|
||||||
mapping.keyboard_mapping_names()
|
mapping.mapping_names()
|
||||||
};
|
};
|
||||||
TableBuilder::new(ui)
|
TableBuilder::new(ui)
|
||||||
.column(Column::remainder())
|
.column(Column::remainder())
|
||||||
@@ -56,29 +73,33 @@ impl InputWindow {
|
|||||||
for keys in KEY_NAMES.chunks_exact(2) {
|
for keys in KEY_NAMES.chunks_exact(2) {
|
||||||
body.row(20.0, |mut row| {
|
body.row(20.0, |mut row| {
|
||||||
for (key, name) in keys {
|
for (key, name) in keys {
|
||||||
let binding = binding_names.get(key).map(|s| s.as_str());
|
let binding = names.remove(key).map(|mut s| {
|
||||||
|
s.sort();
|
||||||
|
s.join(", ")
|
||||||
|
});
|
||||||
row.col(|ui| {
|
row.col(|ui| {
|
||||||
let size = ui.available_size_before_wrap();
|
let size = ui.available_size_before_wrap();
|
||||||
let width = size.x;
|
let width = size.x;
|
||||||
let height = size.y;
|
let height = size.y;
|
||||||
ui.add_sized((width * 0.2, height), Label::new(*name));
|
ui.add_sized((width * 0.2, height), Label::new(*name));
|
||||||
let label_text = if self.now_binding == Some((sim_id, *key)) {
|
let label_text = if self.now_binding == Some(*key) {
|
||||||
"Press any input"
|
bind_message
|
||||||
} else {
|
} else {
|
||||||
binding.unwrap_or("")
|
binding.as_deref().unwrap_or("")
|
||||||
};
|
};
|
||||||
if ui
|
if ui
|
||||||
.add_sized((width * 0.6, height), Button::new(label_text))
|
.add_sized((width * 0.6, height), Button::new(label_text))
|
||||||
.clicked()
|
.clicked()
|
||||||
{
|
{
|
||||||
self.now_binding = Some((sim_id, *key))
|
self.now_binding = Some(*key);
|
||||||
}
|
}
|
||||||
if ui
|
if ui
|
||||||
.add_sized(ui.available_size(), Button::new("Clear"))
|
.add_sized(ui.available_size(), Button::new("Clear"))
|
||||||
.clicked()
|
.clicked()
|
||||||
{
|
{
|
||||||
let mut mapping = mappings.write().unwrap();
|
let mut mapping = mappings.write().unwrap();
|
||||||
mapping.clear_keyboard_mappings(*key);
|
mapping.clear_mappings(*key);
|
||||||
|
self.now_binding = None;
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
@@ -86,6 +107,54 @@ impl InputWindow {
|
|||||||
}
|
}
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
|
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");
|
||||||
|
}
|
||||||
|
|
||||||
|
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),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
ui.separator();
|
||||||
|
if ui.button("Rebind").clicked() {
|
||||||
|
self.active_tab = InputTab::RebindGamepad(gamepad.id);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
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");
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
impl AppWindow for InputWindow {
|
impl AppWindow for InputWindow {
|
||||||
@@ -102,14 +171,25 @@ impl AppWindow for InputWindow {
|
|||||||
fn show(&mut self, ctx: &Context) {
|
fn show(&mut self, ctx: &Context) {
|
||||||
TopBottomPanel::top("options").show(ctx, |ui| {
|
TopBottomPanel::top("options").show(ctx, |ui| {
|
||||||
ui.horizontal(|ui| {
|
ui.horizontal(|ui| {
|
||||||
|
let old_active_tab = self.active_tab;
|
||||||
ui.selectable_value(&mut self.active_tab, InputTab::Player1, "Player 1");
|
ui.selectable_value(&mut self.active_tab, InputTab::Player1, "Player 1");
|
||||||
ui.selectable_value(&mut self.active_tab, InputTab::Player2, "Player 2");
|
ui.selectable_value(&mut self.active_tab, InputTab::Player2, "Player 2");
|
||||||
|
ui.selectable_value(&mut self.active_tab, InputTab::Gamepads, "Gamepads");
|
||||||
|
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;
|
||||||
|
}
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
CentralPanel::default().show(ctx, |ui| {
|
CentralPanel::default().show(ctx, |ui| {
|
||||||
match self.active_tab {
|
match self.active_tab {
|
||||||
InputTab::Player1 => self.render_key_bindings(ui, SimId::Player1),
|
InputTab::Player1 => self.show_key_bindings(ui, SimId::Player1),
|
||||||
InputTab::Player2 => self.render_key_bindings(ui, SimId::Player2),
|
InputTab::Player2 => self.show_key_bindings(ui, SimId::Player2),
|
||||||
|
InputTab::Gamepads => self.show_gamepads(ui),
|
||||||
|
InputTab::RebindGamepad(id) => self.show_gamepad_bindings(ui, id),
|
||||||
};
|
};
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
@@ -118,16 +198,60 @@ impl AppWindow for InputWindow {
|
|||||||
if !event.state.is_pressed() {
|
if !event.state.is_pressed() {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
let Some((sim_id, vb)) = self.now_binding.take() else {
|
let sim_id = match self.active_tab {
|
||||||
|
InputTab::Player1 => SimId::Player1,
|
||||||
|
InputTab::Player2 => SimId::Player2,
|
||||||
|
_ => {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
};
|
||||||
|
let Some(vb) = self.now_binding.take() else {
|
||||||
return;
|
return;
|
||||||
};
|
};
|
||||||
let mut mappings = self.mappings.for_sim(sim_id).write().unwrap();
|
let mut mappings = self.mappings.for_sim(sim_id).write().unwrap();
|
||||||
mappings.add_keyboard_mapping(vb, event.physical_key);
|
mappings.add_keyboard_mapping(vb, event.physical_key);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
fn handle_gamepad_event(&mut self, event: &gilrs::Event) {
|
||||||
|
let InputTab::RebindGamepad(gamepad_id) = self.active_tab else {
|
||||||
|
return;
|
||||||
|
};
|
||||||
|
if gamepad_id != event.id {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
let Some(mappings) = self.mappings.for_gamepad(gamepad_id) else {
|
||||||
|
return;
|
||||||
|
};
|
||||||
|
let Some(vb) = self.now_binding else {
|
||||||
|
return;
|
||||||
|
};
|
||||||
|
match event.event {
|
||||||
|
EventType::ButtonPressed(_, code) => {
|
||||||
|
let mut mapping = mappings.write().unwrap();
|
||||||
|
mapping.add_button_mapping(vb, code);
|
||||||
|
self.now_binding.take();
|
||||||
|
}
|
||||||
|
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();
|
||||||
|
}
|
||||||
|
if value > 0.75 {
|
||||||
|
let mut mapping = mappings.write().unwrap();
|
||||||
|
mapping.add_axis_pos_mapping(vb, code);
|
||||||
|
self.now_binding.take();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
_ => {}
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
#[derive(Clone, Copy, PartialEq, Eq)]
|
#[derive(Clone, Copy, PartialEq, Eq)]
|
||||||
enum InputTab {
|
enum InputTab {
|
||||||
Player1,
|
Player1,
|
||||||
Player2,
|
Player2,
|
||||||
|
Gamepads,
|
||||||
|
RebindGamepad(GamepadId),
|
||||||
}
|
}
|
||||||
|
|||||||
Reference in New Issue
Block a user