Support choosing which player uses a controller
This commit is contained in:
		
							parent
							
								
									9ff62af310
								
							
						
					
					
						commit
						24474fabd0
					
				| 
						 | 
					@ -349,7 +349,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
 | 
				
			||||||
| 
						 | 
					
 | 
				
			||||||
							
								
								
									
										99
									
								
								src/input.rs
								
								
								
								
							
							
						
						
									
										99
									
								
								src/input.rs
								
								
								
								
							| 
						 | 
					@ -11,6 +11,14 @@ 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 struct GamepadMapping {
 | 
					pub struct GamepadMapping {
 | 
				
			||||||
    buttons: HashMap<Code, VBKey>,
 | 
					    buttons: HashMap<Code, VBKey>,
 | 
				
			||||||
    axes: HashMap<Code, (VBKey, VBKey)>,
 | 
					    axes: HashMap<Code, (VBKey, VBKey)>,
 | 
				
			||||||
| 
						 | 
					@ -106,6 +114,7 @@ impl InputMapping {
 | 
				
			||||||
pub struct MappingProvider {
 | 
					pub struct MappingProvider {
 | 
				
			||||||
    device_mappings: Arc<RwLock<HashMap<DeviceId, Arc<RwLock<GamepadMapping>>>>>,
 | 
					    device_mappings: Arc<RwLock<HashMap<DeviceId, Arc<RwLock<GamepadMapping>>>>>,
 | 
				
			||||||
    sim_mappings: HashMap<SimId, Arc<RwLock<InputMapping>>>,
 | 
					    sim_mappings: HashMap<SimId, Arc<RwLock<InputMapping>>>,
 | 
				
			||||||
 | 
					    gamepad_info: Arc<RwLock<HashMap<GamepadId, GamepadInfo>>>,
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
impl MappingProvider {
 | 
					impl MappingProvider {
 | 
				
			||||||
| 
						 | 
					@ -136,6 +145,7 @@ impl MappingProvider {
 | 
				
			||||||
        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 +154,101 @@ 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 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)
 | 
				
			||||||
 | 
					                .write()
 | 
				
			||||||
 | 
					                .unwrap()
 | 
				
			||||||
 | 
					                .gamepads
 | 
				
			||||||
 | 
					                .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)
 | 
					        self.for_sim(sim_id)
 | 
				
			||||||
            .write()
 | 
					            .write()
 | 
				
			||||||
            .unwrap()
 | 
					            .unwrap()
 | 
				
			||||||
            .gamepads
 | 
					            .gamepads
 | 
				
			||||||
            .insert(gamepad.id(), gamepad_mapping);
 | 
					            .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()
 | 
				
			||||||
    }
 | 
					    }
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
| 
						 | 
					
 | 
				
			||||||
| 
						 | 
					@ -42,7 +42,7 @@ impl InputWindow {
 | 
				
			||||||
        }
 | 
					        }
 | 
				
			||||||
    }
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    fn render_key_bindings(&mut self, ui: &mut Ui, sim_id: SimId) {
 | 
					    fn show_key_bindings(&mut self, ui: &mut Ui, sim_id: SimId) {
 | 
				
			||||||
        let mappings = self.mappings.for_sim(sim_id);
 | 
					        let mappings = self.mappings.for_sim(sim_id);
 | 
				
			||||||
        let binding_names = {
 | 
					        let binding_names = {
 | 
				
			||||||
            let mapping = mappings.read().unwrap();
 | 
					            let mapping = mappings.read().unwrap();
 | 
				
			||||||
| 
						 | 
					@ -86,6 +86,37 @@ impl InputWindow {
 | 
				
			||||||
                }
 | 
					                }
 | 
				
			||||||
            });
 | 
					            });
 | 
				
			||||||
    }
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    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),
 | 
				
			||||||
 | 
					                    }
 | 
				
			||||||
 | 
					                }
 | 
				
			||||||
 | 
					            });
 | 
				
			||||||
 | 
					        }
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
impl AppWindow for InputWindow {
 | 
					impl AppWindow for InputWindow {
 | 
				
			||||||
| 
						 | 
					@ -104,12 +135,14 @@ impl AppWindow for InputWindow {
 | 
				
			||||||
            ui.horizontal(|ui| {
 | 
					            ui.horizontal(|ui| {
 | 
				
			||||||
                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");
 | 
				
			||||||
            });
 | 
					            });
 | 
				
			||||||
        });
 | 
					        });
 | 
				
			||||||
        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),
 | 
				
			||||||
            };
 | 
					            };
 | 
				
			||||||
        });
 | 
					        });
 | 
				
			||||||
    }
 | 
					    }
 | 
				
			||||||
| 
						 | 
					@ -130,4 +163,5 @@ impl AppWindow for InputWindow {
 | 
				
			||||||
enum InputTab {
 | 
					enum InputTab {
 | 
				
			||||||
    Player1,
 | 
					    Player1,
 | 
				
			||||||
    Player2,
 | 
					    Player2,
 | 
				
			||||||
 | 
					    Gamepads,
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
| 
						 | 
					
 | 
				
			||||||
		Loading…
	
		Reference in New Issue