diff --git a/build.rs b/build.rs index bf7ebb7b..b8408b24 100644 --- a/build.rs +++ b/build.rs @@ -211,6 +211,7 @@ fn main() { .allowlist_function("rdp_save_state") .allowlist_function("rdp_load_state") .allowlist_function("rdp_set_fps") + .allowlist_function("get_joystick_event") .parse_callbacks(Box::new(bindgen::CargoCallbacks::new())) .generate() .expect("Unable to generate bindings"); diff --git a/parallel-rdp/interface.cpp b/parallel-rdp/interface.cpp index c3f85d0f..f85880b0 100644 --- a/parallel-rdp/interface.cpp +++ b/parallel-rdp/interface.cpp @@ -98,8 +98,8 @@ static GFX_INFO gfx_info; static const uint32_t *fragment_spirv; static size_t fragment_size; -std::vector rdram_dirty; -uint64_t sync_signal; +static std::vector rdram_dirty; +static uint64_t sync_signal; static TTF_Font *message_font; static std::queue messages; @@ -114,6 +114,8 @@ static bool display_challenge_indicator; static bool display_fps; static Vulkan::ImageHandle fps_image; +static std::queue joystick_events; + typedef struct { float SourceSize[4]; float OutputSize[4]; @@ -125,6 +127,12 @@ static const unsigned cmd_len_lut[64] = { 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, }; +static void add_joystick_event(void *userdata) { + JoystickEvent *joystick_event = (JoystickEvent *)userdata; + joystick_events.push(*joystick_event); + delete joystick_event; +} + bool sdl_event_filter(void *userdata, SDL_Event *event) { if (event->type == SDL_EVENT_WINDOW_CLOSE_REQUESTED) { callback.paused = false; @@ -209,11 +217,29 @@ bool sdl_event_filter(void *userdata, SDL_Event *event) { default: break; } + } else if (event->type == SDL_EVENT_JOYSTICK_ADDED) { + JoystickEvent *joystick_event = new JoystickEvent; + joystick_event->joystick_id = event->jdevice.which; + joystick_event->connected = true; + SDL_RunOnMainThread(add_joystick_event, joystick_event, false); + } else if (event->type == SDL_EVENT_JOYSTICK_REMOVED) { + JoystickEvent *joystick_event = new JoystickEvent; + joystick_event->joystick_id = event->jdevice.which; + joystick_event->connected = false; + SDL_RunOnMainThread(add_joystick_event, joystick_event, false); } return 0; } +JoystickEvent get_joystick_event() { + if (joystick_events.empty()) + return JoystickEvent{0, false}; + JoystickEvent joystick_event = joystick_events.front(); + joystick_events.pop(); + return joystick_event; +} + void rdp_new_processor(GFX_INFO _gfx_info) { gfx_info = _gfx_info; diff --git a/parallel-rdp/interface.hpp b/parallel-rdp/interface.hpp index 7ab6569b..f29563ca 100644 --- a/parallel-rdp/interface.hpp +++ b/parallel-rdp/interface.hpp @@ -37,6 +37,11 @@ typedef struct { uint32_t save_state_slot; } CALL_BACK; +typedef struct { + uint32_t joystick_id; + bool connected; +} JoystickEvent; + typedef enum { MESSAGE_VERY_SHORT = 500, MESSAGE_SHORT = 3000, @@ -59,6 +64,8 @@ void rdp_save_state(uint8_t *state); void rdp_load_state(const uint8_t *state); void rdp_set_fps(uint32_t fps, uint32_t vis); +JoystickEvent get_joystick_event(); + void achievement_challenge_indicator_add(const char *achievement_title); void achievement_challenge_indicator_remove(const char *achievement_title); void achievement_progress_add(const char *achievement_title, diff --git a/src/device/controller.rs b/src/device/controller.rs index 5d499221..9f7b82ee 100644 --- a/src/device/controller.rs +++ b/src/device/controller.rs @@ -58,12 +58,12 @@ pub fn process(device: &mut device::Device, channel: usize) { let offset = device.pif.channels[channel].rx_buf.unwrap(); let input = if let Some(netplay) = &mut device.netplay { if netplay.player_number as usize == channel { - let local_input = ui::input::get(&device.ui, 0); + let local_input = ui::input::get(&mut device.ui, 0); netplay::send_input(netplay, local_input); } netplay::get_input(device, channel) } else { - ui::input::get(&device.ui, channel) + ui::input::get(&mut device.ui, channel) }; device.pif.ram[offset..offset + 4].copy_from_slice(&input.data.to_ne_bytes()); diff --git a/src/ui.rs b/src/ui.rs index 1ba7c648..02eb4ba1 100644 --- a/src/ui.rs +++ b/src/ui.rs @@ -151,21 +151,25 @@ impl Ui { game_controller: std::ptr::null_mut(), joystick: std::ptr::null_mut(), rumble: false, + guid: sdl3_sys::guid::SDL_GUID::default(), }, input::Controllers { game_controller: std::ptr::null_mut(), joystick: std::ptr::null_mut(), rumble: false, + guid: sdl3_sys::guid::SDL_GUID::default(), }, input::Controllers { game_controller: std::ptr::null_mut(), joystick: std::ptr::null_mut(), rumble: false, + guid: sdl3_sys::guid::SDL_GUID::default(), }, input::Controllers { game_controller: std::ptr::null_mut(), joystick: std::ptr::null_mut(), rumble: false, + guid: sdl3_sys::guid::SDL_GUID::default(), }, ], keyboard_state: std::ptr::null_mut(), diff --git a/src/ui/input.rs b/src/ui/input.rs index af59d652..cd878f4c 100644 --- a/src/ui/input.rs +++ b/src/ui/input.rs @@ -34,6 +34,7 @@ pub struct Controllers { pub rumble: bool, pub game_controller: *mut sdl3_sys::gamepad::SDL_Gamepad, pub joystick: *mut sdl3_sys::joystick::SDL_Joystick, + pub guid: sdl3_sys::guid::SDL_GUID, } pub struct InputData { @@ -400,9 +401,59 @@ pub fn get_controller_paths(game_ui: &ui::Ui) -> Vec> { controller_paths } -pub fn get(ui: &ui::Ui, channel: usize) -> InputData { - let profile_name = ui.config.input.input_profile_binding[channel].clone(); - let profile = ui.config.input.input_profiles.get(&profile_name).unwrap(); +fn handle_joystick_events(ui: &mut ui::Ui) { + let joystick_event = unsafe { ui::video::get_joystick_event() }; + if joystick_event.joystick_id != 0 { + let joystick_id = sdl3_sys::joystick::SDL_JoystickID(joystick_event.joystick_id); + for (i, controller) in ui.input.controllers.iter_mut().enumerate() { + if joystick_event.connected { + let profile = ui + .config + .input + .input_profiles + .get(&ui.config.input.input_profile_binding[i]) + .unwrap(); + if profile.dinput { + if controller.joystick.is_null() + && controller.guid + == unsafe { sdl3_sys::joystick::SDL_GetJoystickGUIDForID(joystick_id) } + { + controller.joystick = + unsafe { sdl3_sys::joystick::SDL_OpenJoystick(joystick_id) }; + } + } else { + if controller.game_controller.is_null() + && controller.guid + == unsafe { sdl3_sys::gamepad::SDL_GetGamepadGUIDForID(joystick_id) } + { + controller.game_controller = + unsafe { sdl3_sys::gamepad::SDL_OpenGamepad(joystick_id) }; + } + } + } else { + if !controller.joystick.is_null() + && controller.joystick + == unsafe { sdl3_sys::joystick::SDL_GetJoystickFromID(joystick_id) } + { + unsafe { sdl3_sys::joystick::SDL_CloseJoystick(controller.joystick) }; + controller.joystick = std::ptr::null_mut(); + } else if !controller.game_controller.is_null() + && controller.game_controller + == unsafe { sdl3_sys::gamepad::SDL_GetGamepadFromID(joystick_id) } + { + unsafe { sdl3_sys::gamepad::SDL_CloseGamepad(controller.game_controller) }; + controller.game_controller = std::ptr::null_mut(); + } + } + } + } +} + +pub fn get(ui: &mut ui::Ui, channel: usize) -> InputData { + handle_joystick_events(ui); + + let profile_name = &ui.config.input.input_profile_binding[channel]; + let profile = ui.config.input.input_profiles.get(profile_name).unwrap(); let mut keys = 0; let controller = ui.input.controllers[channel].game_controller; let joystick = ui.input.controllers[channel].joystick; @@ -990,8 +1041,12 @@ pub fn init(ui: &mut ui::Ui) { } if joystick_id != 0 { - let profile_name = ui.config.input.input_profile_binding[i].clone(); - let profile = ui.config.input.input_profiles.get(&profile_name).unwrap(); + let profile = ui + .config + .input + .input_profiles + .get(&ui.config.input.input_profile_binding[i]) + .unwrap(); if !profile.dinput { let gamepad = unsafe { sdl3_sys::gamepad::SDL_OpenGamepad(joystick_id) }; @@ -999,6 +1054,8 @@ pub fn init(ui: &mut ui::Ui) { println!("could not connect gamepad: {}", u32::from(joystick_id)) } else { ui.input.controllers[i].game_controller = gamepad; + ui.input.controllers[i].guid = + unsafe { sdl3_sys::gamepad::SDL_GetGamepadGUIDForID(joystick_id) }; let properties = unsafe { sdl3_sys::gamepad::SDL_GetGamepadProperties(gamepad) }; if properties == 0 { @@ -1018,6 +1075,8 @@ pub fn init(ui: &mut ui::Ui) { println!("could not connect joystick: {}", u32::from(joystick_id)) } else { ui.input.controllers[i].joystick = joystick; + ui.input.controllers[i].guid = + unsafe { sdl3_sys::joystick::SDL_GetJoystickGUIDForID(joystick_id) }; let properties = unsafe { sdl3_sys::joystick::SDL_GetJoystickProperties(joystick) }; if properties == 0 {