use smart pointers to avoid crash when a gamepad is disconnected

This commit is contained in:
Flyinghead 2019-02-21 14:49:27 +01:00
parent ecc22b8213
commit 4ae11053ce
9 changed files with 100 additions and 66 deletions

View File

@ -27,7 +27,7 @@ extern u16 kcode[4];
extern u8 rt[4], lt[4];
extern s8 joyx[4], joyy[4];
std::vector<GamepadDevice *> GamepadDevice::_gamepads;
std::vector<std::shared_ptr<GamepadDevice>> GamepadDevice::_gamepads;
std::mutex GamepadDevice::_gamepads_mutex;
bool GamepadDevice::gamepad_btn_input(u32 code, bool pressed)
@ -183,10 +183,10 @@ int GamepadDevice::GetGamepadCount()
return count;
}
GamepadDevice *GamepadDevice::GetGamepad(int index)
std::shared_ptr<GamepadDevice> GamepadDevice::GetGamepad(int index)
{
_gamepads_mutex.lock();
GamepadDevice *dev;
std::shared_ptr<GamepadDevice> dev;
if (index >= 0 && index < _gamepads.size())
dev = _gamepads[index];
else

View File

@ -18,6 +18,7 @@
*/
#pragma once
#include <memory>
#include <mutex>
#include "types.h"
#include "mapping.h"
@ -32,17 +33,8 @@ public:
void set_maple_port(int port) { _maple_port = port; }
virtual bool gamepad_btn_input(u32 code, bool pressed);
bool gamepad_axis_input(u32 code, int value);
virtual ~GamepadDevice() {
save_mapping();
_gamepads_mutex.lock();
for (auto it = _gamepads.begin(); it != _gamepads.end(); it++)
if (*it == this)
{
_gamepads.erase(it);
break;
}
_gamepads_mutex.unlock();
}
virtual ~GamepadDevice() {}
void detect_btn_input(input_detected_cb button_pressed)
{
_input_detected = button_pressed;
@ -61,16 +53,33 @@ public:
void save_mapping();
bool remappable() { return _remappable; }
static void Register(std::shared_ptr<GamepadDevice> gamepad)
{
_gamepads_mutex.lock();
_gamepads.push_back(gamepad);
_gamepads_mutex.unlock();
}
static void Unregister(std::shared_ptr<GamepadDevice> gamepad)
{
gamepad->save_mapping();
_gamepads_mutex.lock();
for (auto it = _gamepads.begin(); it != _gamepads.end(); it++)
if (*it == gamepad)
{
_gamepads.erase(it);
break;
}
_gamepads_mutex.unlock();
}
static int GetGamepadCount();
static GamepadDevice *GetGamepad(int index);
static std::shared_ptr<GamepadDevice> GetGamepad(int index);
protected:
GamepadDevice(int maple_port, const char *api_name, bool remappable = true)
: _api_name(api_name), _maple_port(maple_port), input_mapper(NULL), _input_detected(NULL), _remappable(remappable)
{
_gamepads_mutex.lock();
_gamepads.push_back(this);
_gamepads_mutex.unlock();
}
bool find_mapping(const char *custom_mapping = NULL);
@ -92,6 +101,6 @@ private:
input_detected_cb _input_detected;
bool _remappable;
static std::vector<GamepadDevice *> _gamepads;
static std::vector<std::shared_ptr<GamepadDevice>> _gamepads;
static std::mutex _gamepads_mutex;
};

View File

@ -18,19 +18,20 @@ static void input_evdev_add_device(const char *devnode)
int fd = open(devnode, O_RDWR);
if (fd >= 0)
{
new EvdevGamepadDevice(maple_port, devnode, fd);
std::shared_ptr<EvdevGamepadDevice> gamepad = std::make_shared<EvdevGamepadDevice>(maple_port, devnode, fd);
if (maple_port < 3)
maple_port++;
EvdevGamepadDevice::AddDevice(gamepad);
}
}
static void input_evdev_remove_device(const char *devnode)
{
EvdevGamepadDevice *gamepad = EvdevGamepadDevice::GetControllerForDevnode(devnode);
std::shared_ptr<EvdevGamepadDevice> gamepad = EvdevGamepadDevice::GetControllerForDevnode(devnode);
if (gamepad != NULL)
{
maple_port = gamepad->maple_port(); // Reuse the maple port for the next device connected
delete gamepad;
EvdevGamepadDevice::RemoveDevice(gamepad);
}
}
@ -212,7 +213,7 @@ bool input_evdev_handle(u32 port)
void input_evdev_rumble(u32 port, u16 pow_strong, u16 pow_weak)
{
EvdevGamepadDevice *dev = EvdevGamepadDevice::GetControllerForPort(port);
std::shared_ptr<EvdevGamepadDevice> dev = EvdevGamepadDevice::GetControllerForPort(port);
if (dev != NULL)
dev->Rumble(pow_strong, pow_weak);
}

View File

@ -54,16 +54,11 @@ public:
}
else
printf("using custom mapping '%s'\n", input_mapper->name.c_str());
auto it = evdev_gamepads.find(_devnode);
if (it != evdev_gamepads.end())
delete it->second;
evdev_gamepads[_devnode] = this;
}
virtual ~EvdevGamepadDevice() override
{
printf("evdev: Device '%s' on port %d disconnected\n", _name.c_str(), maple_port());
close(_fd);
evdev_gamepads.erase(_devnode);
}
// FIXME add to base class
@ -99,7 +94,7 @@ public:
}
}
static EvdevGamepadDevice *GetControllerForPort(int port)
static std::shared_ptr<EvdevGamepadDevice> GetControllerForPort(int port)
{
for (auto pair : evdev_gamepads)
if (pair.second->maple_port() == port)
@ -107,7 +102,7 @@ public:
return NULL;
}
static EvdevGamepadDevice *GetControllerForDevnode(const char *devnode)
static std::shared_ptr<EvdevGamepadDevice> GetControllerForDevnode(const char *devnode)
{
auto it = evdev_gamepads.find(devnode);
if (it == evdev_gamepads.end())
@ -124,7 +119,18 @@ public:
static void CloseDevices()
{
while (!evdev_gamepads.empty())
delete evdev_gamepads.begin()->second;
RemoveDevice(evdev_gamepads.begin()->second);
}
static void AddDevice(std::shared_ptr<EvdevGamepadDevice> gamepad)
{
evdev_gamepads[gamepad->_devnode] = gamepad;
GamepadDevice::Register(gamepad);
}
static void RemoveDevice(std::shared_ptr<EvdevGamepadDevice> gamepad)
{
evdev_gamepads.erase(gamepad->_devnode);
GamepadDevice::Unregister(gamepad);
}
protected:
@ -167,7 +173,7 @@ private:
int _fd;
std::string _devnode;
int _rumble_effect_id;
static std::map<std::string, EvdevGamepadDevice*> evdev_gamepads;
static std::map<std::string, std::shared_ptr<EvdevGamepadDevice>> evdev_gamepads;
};
std::map<std::string, EvdevGamepadDevice*> EvdevGamepadDevice::evdev_gamepads;
std::map<std::string, std::shared_ptr<EvdevGamepadDevice>> EvdevGamepadDevice::evdev_gamepads;

View File

@ -397,7 +397,7 @@ static MapleDeviceType maple_expansion_device_type_from_index(int idx)
}
}
static GamepadDevice *mapped_device;
static std::shared_ptr<GamepadDevice> mapped_device;
static u32 mapped_code;
static double map_start_time;
@ -429,16 +429,20 @@ static void detect_input_popup(int index, bool analog)
}
else
mapped_device->get_input_mapping()->set_button(button_keys[index], mapped_code);
mapped_device = NULL;
ImGui::CloseCurrentPopup();
}
else if (now - map_start_time >= 5)
{
mapped_device = NULL;
ImGui::CloseCurrentPopup();
}
ImGui::EndPopup();
}
ImGui::PopStyleVar(2);
}
static void controller_mapping_popup(GamepadDevice *gamepad)
static void controller_mapping_popup(std::shared_ptr<GamepadDevice> gamepad)
{
ImGui::SetNextWindowPos(ImVec2(0, 0));
ImGui::SetNextWindowSize(ImVec2(screen_width, screen_height));
@ -715,8 +719,8 @@ static void gui_display_settings()
ImGui::NextColumn();
for (int i = 0; i < GamepadDevice::GetGamepadCount(); i++)
{
GamepadDevice *gamepad = GamepadDevice::GetGamepad(i);
if (gamepad == NULL)
std::shared_ptr<GamepadDevice> gamepad = GamepadDevice::GetGamepad(i);
if (!gamepad)
continue;
ImGui::Text("%s", gamepad->api_name().c_str());
ImGui::NextColumn();

View File

@ -52,14 +52,15 @@ static void sdl_open_joystick(int index)
printf("SDL: Cannot open joystick %d\n", index + 1);
return;
}
new SDLGamepadDevice(index < MAPLE_PORTS ? index : -1, pJoystick);
std::shared_ptr<SDLGamepadDevice> gamepad = std::make_shared<SDLGamepadDevice>(index < MAPLE_PORTS ? index : -1, pJoystick);
SDLGamepadDevice::AddSDLGamepad(gamepad);
}
static void sdl_close_joystick(SDL_JoystickID instance)
{
SDLGamepadDevice *device = SDLGamepadDevice::GetSDLGamepad(instance);
if (device != NULL)
delete device;
std::shared_ptr<SDLGamepadDevice> gamepad = SDLGamepadDevice::GetSDLGamepad(instance);
if (gamepad != NULL)
gamepad->Close();
}
void input_sdl_init()
@ -91,6 +92,7 @@ static void set_mouse_position(int x, int y)
}
}
// FIXME this shouldn't be done by port. Need something like: handle_events() then get_port(0), get_port(2), ...
void input_sdl_handle(u32 port)
{
#define SET_FLAG(field, mask, expr) field =((expr) ? (field & ~mask) : (field | mask))
@ -117,14 +119,14 @@ void input_sdl_handle(u32 port)
case SDL_JOYBUTTONDOWN:
case SDL_JOYBUTTONUP:
{
SDLGamepadDevice *device = SDLGamepadDevice::GetSDLGamepad((SDL_JoystickID)event.jbutton.which);
std::shared_ptr<SDLGamepadDevice> device = SDLGamepadDevice::GetSDLGamepad((SDL_JoystickID)event.jbutton.which);
if (device != NULL)
device->gamepad_btn_input(event.jbutton.button, event.type == SDL_JOYBUTTONDOWN);
}
break;
case SDL_JOYAXISMOTION:
{
SDLGamepadDevice *device = SDLGamepadDevice::GetSDLGamepad((SDL_JoystickID)event.jaxis.which);
std::shared_ptr<SDLGamepadDevice> device = SDLGamepadDevice::GetSDLGamepad((SDL_JoystickID)event.jaxis.which);
if (device != NULL)
device->gamepad_axis_input(event.jaxis.axis, event.jaxis.value);
}

View File

@ -65,20 +65,24 @@ public:
}
else
printf("using custom mapping '%s'\n", input_mapper->name.c_str());
auto it = sdl_gamepads.find(sdl_joystick_instance);
if (it != sdl_gamepads.end())
delete it->second;
sdl_gamepads[sdl_joystick_instance] = this;
}
virtual ~SDLGamepadDevice() override
SDL_JoystickID sdl_instance() { return sdl_joystick_instance; }
void Close()
{
printf("SDL: Joystick '%s' on port %d disconnected\n", _name.c_str(), maple_port());
SDL_JoystickClose(sdl_joystick);
GamepadDevice::Unregister(gamepad);
sdl_gamepads.erase(sdl_joystick_instance);
}
SDL_JoystickID sdl_instance() { return sdl_joystick_instance; }
static SDLGamepadDevice *GetSDLGamepad(SDL_JoystickID id)
static void AddSDLGamepad(std::shared_ptr<SDLGamepadDevice> gamepad)
{
sdl_gamepads[gamepad->sdl_joystick_instance] = gamepad;
GamepadDevice::Register(gamepad);
}
static std::shared_ptr<SDLGamepadDevice> GetSDLGamepad(SDL_JoystickID id)
{
auto it = sdl_gamepads.find(id);
if (it != sdl_gamepads.end())
@ -97,10 +101,10 @@ protected:
private:
SDL_Joystick* sdl_joystick;
SDL_JoystickID sdl_joystick_instance;
static std::map<SDL_JoystickID, SDLGamepadDevice*> sdl_gamepads;
static std::map<SDL_JoystickID, std::shared_ptr<SDLGamepadDevice>> sdl_gamepads;
};
std::map<SDL_JoystickID, SDLGamepadDevice*> SDLGamepadDevice::sdl_gamepads;
std::map<SDL_JoystickID, std::shared_ptr<SDLGamepadDevice>> SDLGamepadDevice::sdl_gamepads;
class KbInputMapping : public InputMapping
{

View File

@ -717,27 +717,28 @@ void os_DebugBreak()
JNIEXPORT void JNICALL Java_com_reicast_emulator_periph_InputDeviceManager_joystickAdded(JNIEnv *env, jobject obj, jint id, jstring name, jint maple_port)
{
const char* joyname = env->GetStringUTFChars(name,0);
new AndroidGamepadDevice(maple_port, id, joyname);
std::shared_ptr<AndroidGamepadDevice> gamepad = std::make_shared<AndroidGamepadDevice>(maple_port, id, joyname);
AndroidGamepadDevice::AddAndroidGamepad(gamepad);
env->ReleaseStringUTFChars(name, joyname);
}
JNIEXPORT void JNICALL Java_com_reicast_emulator_periph_InputDeviceManager_joystickRemoved(JNIEnv *env, jobject obj, jint id)
{
AndroidGamepadDevice *device = AndroidGamepadDevice::GetAndroidGamepad(id);
std::shared_ptr<AndroidGamepadDevice> device = AndroidGamepadDevice::GetAndroidGamepad(id);
if (device != NULL)
delete device;
AndroidGamepadDevice::RemoveAndroidGamepad(device);
}
JNIEXPORT void JNICALL Java_com_reicast_emulator_periph_InputDeviceManager_virtualGamepadEvent(JNIEnv *env, jobject obj, jint kcode, jint joyx, jint joyy, jint lt, jint rt)
{
AndroidGamepadDevice *device = AndroidGamepadDevice::GetAndroidGamepad(AndroidGamepadDevice::VIRTUAL_GAMEPAD_ID);
std::shared_ptr<AndroidGamepadDevice> device = AndroidGamepadDevice::GetAndroidGamepad(AndroidGamepadDevice::VIRTUAL_GAMEPAD_ID);
if (device != NULL)
device->virtual_gamepad_event(kcode, joyx, joyy, lt, rt);
}
JNIEXPORT jboolean JNICALL Java_com_reicast_emulator_periph_InputDeviceManager_joystickButtonEvent(JNIEnv *env, jobject obj, jint id, jint key, jboolean pressed)
{
AndroidGamepadDevice *device = AndroidGamepadDevice::GetAndroidGamepad(id);
std::shared_ptr<AndroidGamepadDevice> device = AndroidGamepadDevice::GetAndroidGamepad(id);
if (device != NULL)
return device->gamepad_btn_input(key, pressed);
else
@ -746,7 +747,7 @@ JNIEXPORT jboolean JNICALL Java_com_reicast_emulator_periph_InputDeviceManager_j
}
JNIEXPORT jboolean JNICALL Java_com_reicast_emulator_periph_InputDeviceManager_joystickAxisEvent(JNIEnv *env, jobject obj, jint id, jint key, jint value)
{
AndroidGamepadDevice *device = AndroidGamepadDevice::GetAndroidGamepad(id);
std::shared_ptr<AndroidGamepadDevice> device = AndroidGamepadDevice::GetAndroidGamepad(id);
if (device != NULL)
return device->gamepad_axis_input(key, value);
else

View File

@ -92,18 +92,13 @@ public:
}
else
printf("using custom mapping '%s'\n", input_mapper->name.c_str());
auto it = android_gamepads.find(id);
if (it != android_gamepads.end())
delete it->second;
android_gamepads[id] = this;
}
virtual ~AndroidGamepadDevice() override
{
printf("Android: Joystick '%s' on port %d disconnected\n", _name.c_str(), maple_port());
android_gamepads.erase(android_id);
}
static AndroidGamepadDevice *GetAndroidGamepad(int id)
static std::shared_ptr<AndroidGamepadDevice> GetAndroidGamepad(int id)
{
auto it = android_gamepads.find(id);
if (it != android_gamepads.end())
@ -112,6 +107,18 @@ public:
return NULL;
}
static void AddAndroidGamepad(std::shared_ptr<AndroidGamepadDevice> gamepad)
{
android_gamepads[gamepad->android_id] = gamepad;
GamepadDevice::Register(gamepad);
};
static void RemoveAndroidGamepad(std::shared_ptr<AndroidGamepadDevice> gamepad)
{
android_gamepads.erase(gamepad->android_id);
GamepadDevice::Unregister(gamepad);
};
void virtual_gamepad_event(int kcode, int joyx, int joyy, int lt, int rt)
{
// No virtual gamepad when the GUI is open: touch events only
@ -150,11 +157,11 @@ protected:
private:
int android_id;
static std::map<int, AndroidGamepadDevice*> android_gamepads;
static std::map<int, std::shared_ptr<AndroidGamepadDevice>> android_gamepads;
u16 previous_kcode = 0xffff;
};
std::map<int, AndroidGamepadDevice*> AndroidGamepadDevice::android_gamepads;
std::map<int, std::shared_ptr<AndroidGamepadDevice>> AndroidGamepadDevice::android_gamepads;
class MouseInputMapping : public InputMapping
{