-osd: Improved SDL Game Controller joystick provider:

* Allow fallback to joystick API for devices without game controller
  mappings.
* Detect controller reconnection.
* Force Switch Pro Controller ZL/ZR to act like buttons.
* Added button names for Google Stadia controller.
* Also fixed dumb copy/paste error on my part in Windows clipboard code.
This commit is contained in:
Vas Crabb 2023-01-13 17:26:33 +11:00
parent 58f41bfa47
commit e817a3d0c8
2 changed files with 380 additions and 183 deletions

View File

@ -214,6 +214,29 @@ char const *const CONTROLLER_BUTTON_XBOX360[]{
"P4", "P4",
"Touchpad" }; "Touchpad" };
[[maybe_unused]] char const *const CONTROLLER_BUTTON_STADIA[]{
"A",
"B",
"X",
"Y",
"Options",
"Logo",
"Menu",
"L3",
"R3",
"L1",
"R1",
"D-pad Up",
"D-pad Down",
"D-pad Left",
"D-pad Right",
"Capture",
"P1",
"P2",
"P3",
"P4",
"Touchpad" };
struct key_lookup_table struct key_lookup_table
{ {
int code; int code;
@ -785,6 +808,14 @@ public:
bool is_instance(SDL_JoystickID instance) const { return m_instance == instance; } bool is_instance(SDL_JoystickID instance) const { return m_instance == instance; }
bool reconnect_match(std::string_view g, char const *s) const
{
return
(0 > m_instance) &&
(id() == g) &&
((s && serial() && (*serial() == s)) || (!s && !serial()));
}
protected: protected:
sdl_joystick_device_base( sdl_joystick_device_base(
running_machine &machine, running_machine &machine,
@ -1006,7 +1037,7 @@ public:
break; break;
case SDL_JOYDEVICEREMOVED: case SDL_JOYDEVICEREMOVED:
osd_printf_verbose("Joystick: %s [GUID %s] disconnected\n", name(), id()); osd_printf_verbose("Joystick: %s [ID %s] disconnected\n", name(), id());
clear_instance(); clear_instance();
reset(); reset();
if (m_joydevice) if (m_joydevice)
@ -1028,14 +1059,6 @@ public:
return m_hapdevice != nullptr; return m_hapdevice != nullptr;
} }
bool reconnect_match(std::string_view g, char const *s) const
{
return
!m_joydevice &&
(id() == g) &&
((s && serial() && (*serial() == s)) || (!s && !serial()));
}
void attach_device(SDL_Joystick *joy) void attach_device(SDL_Joystick *joy)
{ {
assert(joy); assert(joy);
@ -1044,6 +1067,8 @@ public:
set_instance(SDL_JoystickInstanceID(joy)); set_instance(SDL_JoystickInstanceID(joy));
m_joydevice = joy; m_joydevice = joy;
m_hapdevice = SDL_HapticOpenFromJoystick(joy); m_hapdevice = SDL_HapticOpenFromJoystick(joy);
osd_printf_verbose("Joystick: %s [ID %s] reconnected\n", name(), id());
} }
protected: protected:
@ -1138,6 +1163,7 @@ public:
{ {
char const *const *axisnames = CONTROLLER_AXIS_XBOX; char const *const *axisnames = CONTROLLER_AXIS_XBOX;
char const *const *buttonnames = CONTROLLER_BUTTON_XBOX360; char const *const *buttonnames = CONTROLLER_BUTTON_XBOX360;
bool digitaltriggers = false;
#if SDL_VERSION_ATLEAST(2, 0, 12) #if SDL_VERSION_ATLEAST(2, 0, 12)
auto const ctrltype = SDL_GameControllerGetType(m_ctrldevice); auto const ctrltype = SDL_GameControllerGetType(m_ctrldevice);
switch (ctrltype) switch (ctrltype)
@ -1169,6 +1195,7 @@ public:
osd_printf_verbose("Game Controller: ... Switch Pro Controller type\n"); osd_printf_verbose("Game Controller: ... Switch Pro Controller type\n");
axisnames = CONTROLLER_AXIS_SWITCH; axisnames = CONTROLLER_AXIS_SWITCH;
buttonnames = CONTROLLER_BUTTON_SWITCH; buttonnames = CONTROLLER_BUTTON_SWITCH;
digitaltriggers = true;
break; break;
#if SDL_VERSION_ATLEAST(2, 0, 14) // TODO: support more controller types #if SDL_VERSION_ATLEAST(2, 0, 14) // TODO: support more controller types
//case SDL_CONTROLLER_TYPE_VIRTUAL: //case SDL_CONTROLLER_TYPE_VIRTUAL:
@ -1178,13 +1205,17 @@ public:
buttonnames = CONTROLLER_BUTTON_PS5; buttonnames = CONTROLLER_BUTTON_PS5;
break; break;
//case SDL_CONTROLLER_TYPE_AMAZON_LUNA: //case SDL_CONTROLLER_TYPE_AMAZON_LUNA:
//case SDL_CONTROLLER_TYPE_GOOGLE_STADIA: case SDL_CONTROLLER_TYPE_GOOGLE_STADIA:
osd_printf_verbose("Game Controller: ... Google Stadia type\n");
axisnames = CONTROLLER_AXIS_PS;
buttonnames = CONTROLLER_BUTTON_STADIA;
break;
//case SDL_CONTROLLER_TYPE_SWITCH_JOYCON_LEFT: //case SDL_CONTROLLER_TYPE_SWITCH_JOYCON_LEFT:
//case SDL_CONTROLLER_TYPE_SWITCH_JOYCON_RIGHT: //case SDL_CONTROLLER_TYPE_SWITCH_JOYCON_RIGHT:
//case SDL_CONTROLLER_TYPE_SWITCH_JOYCON_PAIR: //case SDL_CONTROLLER_TYPE_SWITCH_JOYCON_PAIR:
#endif #endif
default: // default to Xbox 360 names default: // default to Xbox 360 names
osd_printf_verbose("Game Controller: ... unrecognised type (%d)\n", int(ctrltype)); osd_printf_verbose("Game Controller: ... unrecognized type (%d)\n", int(ctrltype));
break; break;
} }
#endif #endif
@ -1199,9 +1230,9 @@ public:
{ SDL_CONTROLLER_AXIS_TRIGGERRIGHT, ITEM_ID_SLIDER2, true } }; { SDL_CONTROLLER_AXIS_TRIGGERRIGHT, ITEM_ID_SLIDER2, true } };
for (auto [axis, item, buttontest] : axes) for (auto [axis, item, buttontest] : axes)
{ {
bool avail = true; bool avail = !buttontest || !digitaltriggers;
#if SDL_VERSION_ATLEAST(2, 0, 14) #if SDL_VERSION_ATLEAST(2, 0, 14)
avail = SDL_GameControllerHasAxis(m_ctrldevice, axis); avail = avail && SDL_GameControllerHasAxis(m_ctrldevice, axis);
#endif #endif
if (avail) if (avail)
{ {
@ -1293,10 +1324,13 @@ public:
auto const binding = SDL_GameControllerGetBindForAxis(m_ctrldevice, axis); auto const binding = SDL_GameControllerGetBindForAxis(m_ctrldevice, axis);
switch (binding.bindType) switch (binding.bindType)
{ {
case SDL_CONTROLLER_BINDTYPE_NONE:
avail = false;
break;
case SDL_CONTROLLER_BINDTYPE_BUTTON: case SDL_CONTROLLER_BINDTYPE_BUTTON:
break; break;
default: default:
avail = false; avail = digitaltriggers;
} }
} }
if (avail) if (avail)
@ -1381,7 +1415,7 @@ public:
break; break;
case SDL_CONTROLLERDEVICEREMOVED: case SDL_CONTROLLERDEVICEREMOVED:
osd_printf_verbose("Game Controller: %s [GUID %s] disconnected\n", name(), id()); osd_printf_verbose("Game Controller: %s [ID %s] disconnected\n", name(), id());
clear_instance(); clear_instance();
reset(); reset();
if (m_ctrldevice) if (m_ctrldevice)
@ -1393,6 +1427,17 @@ public:
} }
} }
void attach_device(SDL_GameController *ctrl)
{
assert(ctrl);
assert(!m_ctrldevice);
set_instance(SDL_JoystickInstanceID(SDL_GameControllerGetJoystick(ctrl)));
m_ctrldevice = ctrl;
osd_printf_verbose("Game Controller: %s [ID %s] reconnected\n", name(), id());
}
private: private:
// state information for a game controller // state information for a game controller
struct sdl_controller_state struct sdl_controller_state
@ -1633,99 +1678,190 @@ public:
//============================================================ //============================================================
// sdl_joystick_module // sdl_joystick_module_base
//============================================================ //============================================================
class sdl_joystick_module : public sdl_input_module class sdl_joystick_module_base : public sdl_input_module
{ {
public: protected:
sdl_joystick_module() : sdl_joystick_module_base(char const *name) :
sdl_input_module(OSD_JOYSTICKINPUT_PROVIDER, "sdljoy"), sdl_input_module(OSD_JOYSTICKINPUT_PROVIDER, name),
m_initialized_joystick(false), m_initialized_joystick(false),
m_initialized_haptic(false) m_initialized_haptic(false)
{ {
} }
virtual void exit() override virtual ~sdl_joystick_module_base()
{ {
sdl_input_module::exit(); assert(!m_initialized_joystick);
assert(!m_initialized_haptic);
}
bool have_joystick() const { return m_initialized_joystick; }
bool have_haptic() const { return m_initialized_haptic; }
void init_joystick()
{
assert(!m_initialized_joystick);
assert(!m_initialized_haptic);
m_initialized_joystick = !SDL_InitSubSystem(SDL_INIT_JOYSTICK);
if (!m_initialized_joystick)
{
osd_printf_error("Could not initialize SDL Joystick subsystem: %s.\n", SDL_GetError());
return;
}
m_initialized_haptic = !SDL_InitSubSystem(SDL_INIT_HAPTIC);
if (!m_initialized_haptic)
osd_printf_verbose("Could not initialize SDL Haptic subsystem: %s.\n", SDL_GetError());
}
void quit_joystick()
{
if (m_initialized_joystick) if (m_initialized_joystick)
{
SDL_QuitSubSystem(SDL_INIT_JOYSTICK); SDL_QuitSubSystem(SDL_INIT_JOYSTICK);
m_initialized_joystick = false;
}
if (m_initialized_haptic) if (m_initialized_haptic)
{
SDL_QuitSubSystem(SDL_INIT_HAPTIC); SDL_QuitSubSystem(SDL_INIT_HAPTIC);
m_initialized_haptic = false;
}
}
sdl_joystick_device *create_joystick_device(running_machine &machine, int index, bool sixaxis)
{
// open the joystick device
SDL_Joystick *const joy = SDL_JoystickOpen(index);
if (!joy)
{
osd_printf_error("Joystick: Could not open SDL joystick %d: %s.\n", index, SDL_GetError());
return nullptr;
}
// get basic info
char const *const name = SDL_JoystickName(joy);
SDL_JoystickGUID guid = SDL_JoystickGetGUID(joy);
char guid_str[256];
guid_str[0] = '\0';
SDL_JoystickGetGUIDString(guid, guid_str, sizeof(guid_str) - 1);
char const *serial = nullptr;
#if SDL_VERSION_ATLEAST(2, 0, 14)
serial = SDL_JoystickGetSerial(joy);
#endif
std::string id(guid_str);
if (serial)
id.append(1, '-').append(serial);
// instantiate device
sdl_joystick_device &devinfo = sixaxis
? devicelist().create_device<sdl_sixaxis_joystick_device>(machine, name ? name : guid_str, guid_str, *this, joy, serial)
: devicelist().create_device<sdl_joystick_device>(machine, name ? name : guid_str, guid_str, *this, joy, serial);
// print some diagnostic info
osd_printf_verbose("Joystick: %s [GUID %s] Vendor ID %04X, Product ID %04X, Revision %04X, Serial %s\n",
name ? name : "<nullptr>",
guid_str,
SDL_JoystickGetVendor(joy),
SDL_JoystickGetProduct(joy),
SDL_JoystickGetProductVersion(joy),
serial ? serial : "<nullptr>");
osd_printf_verbose("Joystick: ... %d axes, %d buttons %d hats %d balls\n",
SDL_JoystickNumAxes(joy),
SDL_JoystickNumButtons(joy),
SDL_JoystickNumHats(joy),
SDL_JoystickNumBalls(joy));
if (devinfo.has_haptic())
osd_printf_verbose("Joystick: ... Has haptic capability\n");
else
osd_printf_verbose("Joystick: ... Does not have haptic capability\n");
if (SDL_JoystickNumButtons(joy) > MAX_BUTTONS)
osd_printf_verbose("Joystick: ... Has %d buttons which exceeds supported %d buttons\n", SDL_JoystickNumButtons(joy), MAX_BUTTONS);
// let it add controls
devinfo.configure();
return &devinfo;
}
void dispatch_joystick_event(SDL_Event const &event)
{
// figure out which joystick this event is destined for
sdl_joystick_device_base *const target_device = find_joystick(event.jdevice.which); // FIXME: this depends on SDL_JoystickID being the same size as Sint32
// if we find a matching joystick, dispatch the event to the joystick
if (target_device)
target_device->queue_events(&event, 1);
}
device_info *find_reconnect_match(SDL_JoystickGUID const &guid, char const *serial)
{
char guid_str[256];
guid_str[0] = '\0';
SDL_JoystickGetGUIDString(guid, guid_str, sizeof(guid_str) - 1);
auto target_device = std::find_if(
devicelist().begin(),
devicelist().end(),
[&guid_str, &serial] (auto const &device)
{
auto &devinfo = downcast<sdl_joystick_device_base &>(*device);
return devinfo.reconnect_match(guid_str, serial);
});
return (devicelist().end() != target_device) ? target_device->get() : nullptr;
}
sdl_joystick_device_base *find_joystick(SDL_JoystickID instance)
{
for (auto &ptr : devicelist())
{
sdl_joystick_device_base *const device = downcast<sdl_joystick_device_base *>(ptr.get());
if (device->is_instance(instance))
return device;
}
return nullptr;
}
private:
bool m_initialized_joystick;
bool m_initialized_haptic;
};
//============================================================
// sdl_joystick_module
//============================================================
class sdl_joystick_module : public sdl_joystick_module_base
{
public:
sdl_joystick_module() : sdl_joystick_module_base("sdljoy")
{
}
virtual void exit() override
{
sdl_joystick_module_base::exit();
quit_joystick();
} }
virtual void input_init(running_machine &machine) override virtual void input_init(running_machine &machine) override
{ {
SDL_SetHint(SDL_HINT_ACCELEROMETER_AS_JOYSTICK, "0"); SDL_SetHint(SDL_HINT_ACCELEROMETER_AS_JOYSTICK, "0");
m_initialized_joystick = !SDL_InitSubSystem(SDL_INIT_JOYSTICK); init_joystick();
if (!m_initialized_joystick) if (!have_joystick())
{
osd_printf_error("Could not initialize SDL Joystick: %s.\n", SDL_GetError());
return; return;
}
m_initialized_haptic = !SDL_InitSubSystem(SDL_INIT_HAPTIC); sdl_joystick_module_base::input_init(machine);
if (!m_initialized_haptic)
{
osd_printf_verbose("Could not initialize SDL Haptic subsystem: %s.\n", SDL_GetError());
}
sdl_input_module::input_init(machine);
bool const sixaxis_mode = downcast<const sdl_options *>(options())->sixaxis(); bool const sixaxis_mode = downcast<const sdl_options *>(options())->sixaxis();
osd_printf_verbose("Joystick: Start initialization\n"); osd_printf_verbose("Joystick: Start initialization\n");
for (int physical_stick = 0; physical_stick < SDL_NumJoysticks(); physical_stick++) for (int physical_stick = 0; physical_stick < SDL_NumJoysticks(); physical_stick++)
{ create_joystick_device(machine, physical_stick, sixaxis_mode);
SDL_Joystick *const joy = SDL_JoystickOpen(physical_stick);
char const *const name = SDL_JoystickName(joy);
SDL_JoystickGUID guid = SDL_JoystickGetGUID(joy);
char guid_str[256];
guid_str[0] = '\0';
SDL_JoystickGetGUIDString(guid, guid_str, sizeof(guid_str) - 1);
char const *serial = nullptr;
#if SDL_VERSION_ATLEAST(2, 0, 14)
serial = SDL_JoystickGetSerial(joy);
#endif
std::string id(guid_str);
if (serial)
id.append(1, '-').append(serial);
sdl_joystick_device *const devinfo = sixaxis_mode
? &devicelist().create_device<sdl_sixaxis_joystick_device>(machine, name ? name : guid_str, guid_str, *this, joy, serial)
: &devicelist().create_device<sdl_joystick_device>(machine, name ? name : guid_str, guid_str, *this, joy, serial);
if (!devinfo)
{
SDL_JoystickClose(joy);
continue;
}
osd_printf_verbose("Joystick: %s [GUID %s] Vendor ID %04X, Product ID %04X, Revision %04X, Serial %s\n",
name ? name : "<nullptr>",
guid_str,
SDL_JoystickGetVendor(joy),
SDL_JoystickGetProduct(joy),
SDL_JoystickGetProductVersion(joy),
serial ? serial : "<nullptr>");
osd_printf_verbose("Joystick: ... %d axes, %d buttons %d hats %d balls\n",
SDL_JoystickNumAxes(joy),
SDL_JoystickNumButtons(joy),
SDL_JoystickNumHats(joy),
SDL_JoystickNumBalls(joy));
if (devinfo->has_haptic())
osd_printf_verbose("Joystick: ... Has haptic capability\n");
else
osd_printf_verbose("Joystick: ... Does not have haptic capability\n");
if (SDL_JoystickNumButtons(joy) > MAX_BUTTONS)
osd_printf_verbose("Joystick: ... Has %d buttons which exceeds supported %d buttons\n", SDL_JoystickNumButtons(joy), MAX_BUTTONS);
devinfo->configure();
}
static int const event_types[] = { static int const event_types[] = {
int(SDL_JOYAXISMOTION), int(SDL_JOYAXISMOTION),
@ -1735,7 +1871,6 @@ public:
int(SDL_JOYBUTTONUP), int(SDL_JOYBUTTONUP),
int(SDL_JOYDEVICEADDED), int(SDL_JOYDEVICEADDED),
int(SDL_JOYDEVICEREMOVED) }; int(SDL_JOYDEVICEREMOVED) };
sdl_event_manager::instance().subscribe(event_types, this); sdl_event_manager::instance().subscribe(event_types, this);
osd_printf_verbose("Joystick: End initialization\n"); osd_printf_verbose("Joystick: End initialization\n");
@ -1746,29 +1881,22 @@ public:
if (SDL_JOYDEVICEADDED == sdlevent.type) if (SDL_JOYDEVICEADDED == sdlevent.type)
{ {
SDL_Joystick *const joy = SDL_JoystickOpen(sdlevent.jdevice.which); SDL_Joystick *const joy = SDL_JoystickOpen(sdlevent.jdevice.which);
if (joy) if (!joy)
{
osd_printf_error("Joystick: Could not open SDL joystick %d: %s.\n", sdlevent.jdevice.which, SDL_GetError());
}
else
{ {
SDL_JoystickGUID guid = SDL_JoystickGetGUID(joy); SDL_JoystickGUID guid = SDL_JoystickGetGUID(joy);
char guid_str[256];
guid_str[0] = '\0';
SDL_JoystickGetGUIDString(guid, guid_str, sizeof(guid_str) - 1);
char const *serial = nullptr; char const *serial = nullptr;
#if SDL_VERSION_ATLEAST(2, 0, 14) #if SDL_VERSION_ATLEAST(2, 0, 14)
serial = SDL_JoystickGetSerial(joy); serial = SDL_JoystickGetSerial(joy);
#endif #endif
auto target_device = std::find_if( auto *const target_device = find_reconnect_match(guid, serial);
devicelist().begin(), if (target_device)
devicelist().end(),
[&guid_str, &serial] (auto const &device)
{
auto &devinfo = downcast<sdl_joystick_device &>(*device);
return devinfo.reconnect_match(guid_str, serial);
});
if (devicelist().end() != target_device)
{ {
auto &devinfo = downcast<sdl_joystick_device &>(**target_device); auto &devinfo = downcast<sdl_joystick_device &>(*target_device);
devinfo.attach_device(joy); devinfo.attach_device(joy);
osd_printf_verbose("Joystick: %s [GUID %s] reconnected\n", devinfo.name(), guid_str);
} }
else else
{ {
@ -1778,29 +1906,9 @@ public:
} }
else else
{ {
// figure out which joystick this event is destined for dispatch_joystick_event(sdlevent);
sdl_joystick_device *const target_device = find_joystick(sdlevent.jdevice.which); // FIXME: this depends on SDL_JoystickID being the same size as Sint32
// if we find a matching joystick, dispatch the event to the joystick
if (target_device)
target_device->queue_events(&sdlevent, 1);
} }
} }
private:
sdl_joystick_device *find_joystick(SDL_JoystickID instance)
{
for (auto &ptr : devicelist())
{
sdl_joystick_device *const device = downcast<sdl_joystick_device *>(ptr.get());
if (device->is_instance(instance))
return device;
}
return nullptr;
}
bool m_initialized_joystick;
bool m_initialized_haptic;
}; };
@ -1808,11 +1916,11 @@ private:
// sdl_game_controller_module // sdl_game_controller_module
//============================================================ //============================================================
class sdl_game_controller_module : public sdl_input_module class sdl_game_controller_module : public sdl_joystick_module_base
{ {
public: public:
sdl_game_controller_module() : sdl_game_controller_module() :
sdl_input_module(OSD_JOYSTICKINPUT_PROVIDER, "sdlgame"), sdl_joystick_module_base("sdlgame"),
m_initialized_game_controller(false) m_initialized_game_controller(false)
{ {
} }
@ -1823,80 +1931,58 @@ public:
if (m_initialized_game_controller) if (m_initialized_game_controller)
SDL_QuitSubSystem(SDL_INIT_GAMECONTROLLER); SDL_QuitSubSystem(SDL_INIT_GAMECONTROLLER);
quit_joystick();
} }
virtual void input_init(running_machine &machine) override virtual void input_init(running_machine &machine) override
{ {
SDL_SetHint(SDL_HINT_ACCELEROMETER_AS_JOYSTICK, "0");
init_joystick();
if (!have_joystick())
return;
m_initialized_game_controller = !SDL_InitSubSystem(SDL_INIT_GAMECONTROLLER); m_initialized_game_controller = !SDL_InitSubSystem(SDL_INIT_GAMECONTROLLER);
if (!m_initialized_game_controller) if (!m_initialized_game_controller)
{ osd_printf_warning("Could not initialize SDL Game Controller: %s.\n", SDL_GetError());
osd_printf_error("Could not initialize SDL Game Controller: %s.\n", SDL_GetError());
return;
}
sdl_input_module::input_init(machine); sdl_joystick_module_base::input_init(machine);
bool const sixaxis_mode = downcast<const sdl_options *>(options())->sixaxis();
osd_printf_verbose("Game Controller: Start initialization\n"); osd_printf_verbose("Game Controller: Start initialization\n");
for (int physical_stick = 0; physical_stick < SDL_NumJoysticks(); physical_stick++) for (int physical_stick = 0; physical_stick < SDL_NumJoysticks(); physical_stick++)
{ {
if (!SDL_IsGameController(physical_stick)) // try to open as a game controller
continue; SDL_GameController *ctrl = nullptr;
if (m_initialized_game_controller && SDL_IsGameController(physical_stick))
SDL_GameController *const ctrl = SDL_GameControllerOpen(physical_stick);
char const *const name = SDL_GameControllerName(ctrl);
SDL_JoystickGUID guid = SDL_JoystickGetDeviceGUID(physical_stick);
char guid_str[256];
guid_str[0] = '\0';
SDL_JoystickGetGUIDString(guid, guid_str, sizeof(guid_str) - 1);
char const *serial = nullptr;
#if SDL_VERSION_ATLEAST(2, 0, 14)
serial = SDL_GameControllerGetSerial(ctrl);
#endif
std::string id(guid_str);
if (serial)
id.append(1, '-').append(serial);
sdl_game_controller_device *const devinfo = &devicelist().create_device<sdl_game_controller_device>(
machine,
name ? name : guid_str,
guid_str,
*this,
ctrl,
serial);
if (!devinfo)
{ {
SDL_GameControllerClose(ctrl); ctrl = SDL_GameControllerOpen(physical_stick);
continue; if (!ctrl)
osd_printf_warning("Game Controller: Could not open SDL game controller %d: %s.\n", physical_stick, SDL_GetError());
} }
osd_printf_verbose("Game Controller: %s [GUID %s] Vendor ID %04X, Product ID %04X, Revision %04X, Serial %s\n", // fall back to joystick API if necessary
name ? name : "<nullptr>", if (!ctrl)
guid_str, create_joystick_device(machine, physical_stick, sixaxis_mode);
SDL_GameControllerGetVendor(ctrl),
SDL_GameControllerGetProduct(ctrl),
SDL_GameControllerGetProductVersion(ctrl),
serial ? serial : "<nullptr>");
char *const mapping = SDL_GameControllerMapping(ctrl);
if (mapping)
{
osd_printf_verbose("Game Controller: ... mapping [%s]\n", mapping);
SDL_free(mapping);
}
else else
{ create_game_controller_device(machine, physical_stick, ctrl);
osd_printf_verbose("Game Controller: ... no mapping\n");
}
devinfo->configure();
} }
static int const event_types[] = { static int const event_types[] = {
int(SDL_JOYAXISMOTION),
int(SDL_JOYBALLMOTION),
int(SDL_JOYHATMOTION),
int(SDL_JOYBUTTONDOWN),
int(SDL_JOYBUTTONUP),
int(SDL_JOYDEVICEADDED),
int(SDL_JOYDEVICEREMOVED),
int(SDL_CONTROLLERAXISMOTION), int(SDL_CONTROLLERAXISMOTION),
int(SDL_CONTROLLERBUTTONDOWN), int(SDL_CONTROLLERBUTTONDOWN),
int(SDL_CONTROLLERBUTTONUP), int(SDL_CONTROLLERBUTTONUP),
int(SDL_CONTROLLERDEVICEADDED),
int(SDL_CONTROLLERDEVICEREMOVED) }; int(SDL_CONTROLLERDEVICEREMOVED) };
sdl_event_manager::instance().subscribe(event_types, this); sdl_event_manager::instance().subscribe(event_types, this);
osd_printf_verbose("Game Controller: End initialization\n"); osd_printf_verbose("Game Controller: End initialization\n");
@ -1904,24 +1990,135 @@ public:
virtual void handle_event(SDL_Event &sdlevent) override virtual void handle_event(SDL_Event &sdlevent) override
{ {
// figure out which game controller this event is destined for switch (sdlevent.type)
sdl_game_controller_device *const target_device = find_controller(sdlevent.jdevice.which); // FIXME: this depends on SDL_JoystickID being the same size as Sint32 {
case SDL_JOYDEVICEADDED:
{
// make sure this isn't an event for a reconnected game controller
auto const controller = find_joystick(SDL_JoystickGetDeviceInstanceID(sdlevent.jdevice.which));
if (find_joystick(SDL_JoystickGetDeviceInstanceID(sdlevent.jdevice.which)))
{
osd_printf_verbose(
"Game Controller: Got SDL joystick added event for reconnected game controller %s [ID %s]\n",
controller->name(),
controller->id());
break;
}
// if we find a matching controller, dispatch the event to the game controller SDL_Joystick *const joy = SDL_JoystickOpen(sdlevent.jdevice.which);
if (target_device) if (!joy)
target_device->queue_events(&sdlevent, 1); {
osd_printf_error("Joystick: Could not open SDL joystick %d: %s.\n", sdlevent.jdevice.which, SDL_GetError());
break;
}
SDL_JoystickGUID guid = SDL_JoystickGetGUID(joy);
char const *serial = nullptr;
#if SDL_VERSION_ATLEAST(2, 0, 14)
serial = SDL_JoystickGetSerial(joy);
#endif
auto *const target_device = find_reconnect_match(guid, serial);
if (target_device)
{
// if this downcast fails, opening as a game controller worked initially but failed on reconnection
auto *const devinfo = dynamic_cast<sdl_joystick_device *>(target_device);
if (devinfo)
devinfo->attach_device(joy);
else
SDL_JoystickClose(joy);
}
else
{
SDL_JoystickClose(joy);
}
}
break;
// for devices supported by the game controller API, this is received before the corresponding SDL_JOYDEVICEADDED
case SDL_CONTROLLERDEVICEADDED:
{
SDL_GameController *const ctrl = SDL_GameControllerOpen(sdlevent.cdevice.which);
if (!ctrl)
{
osd_printf_error("Game Controller: Could not open SDL game controller %d: %s.\n", sdlevent.cdevice.which, SDL_GetError());
break;
}
SDL_JoystickGUID guid = SDL_JoystickGetDeviceGUID(sdlevent.cdevice.which);
char const *serial = nullptr;
#if SDL_VERSION_ATLEAST(2, 0, 14)
serial = SDL_GameControllerGetSerial(ctrl);
#endif
auto *const target_device = find_reconnect_match(guid, serial);
if (target_device)
{
// downcast can fail if there was an error opening the device as a game controller the first time
auto *const devinfo = dynamic_cast<sdl_game_controller_device *>(target_device);
if (devinfo)
devinfo->attach_device(ctrl);
else
SDL_GameControllerClose(ctrl);
}
else
{
SDL_GameControllerClose(ctrl);
}
}
break;
default:
dispatch_joystick_event(sdlevent);
}
} }
private: private:
sdl_game_controller_device *find_controller(SDL_JoystickID instance) sdl_game_controller_device *create_game_controller_device(running_machine &machine, int index, SDL_GameController *ctrl)
{ {
for (auto &ptr : devicelist()) // get basic info
char const *const name = SDL_GameControllerName(ctrl);
SDL_JoystickGUID guid = SDL_JoystickGetDeviceGUID(index);
char guid_str[256];
guid_str[0] = '\0';
SDL_JoystickGetGUIDString(guid, guid_str, sizeof(guid_str) - 1);
char const *serial = nullptr;
#if SDL_VERSION_ATLEAST(2, 0, 14)
serial = SDL_GameControllerGetSerial(ctrl);
#endif
std::string id(guid_str);
if (serial)
id.append(1, '-').append(serial);
// instantiate device
sdl_game_controller_device &devinfo = devicelist().create_device<sdl_game_controller_device>(
machine,
name ? name : guid_str,
guid_str,
*this,
ctrl,
serial);
// print some diagnostic info
osd_printf_verbose("Game Controller: %s [GUID %s] Vendor ID %04X, Product ID %04X, Revision %04X, Serial %s\n",
name ? name : "<nullptr>",
guid_str,
SDL_GameControllerGetVendor(ctrl),
SDL_GameControllerGetProduct(ctrl),
SDL_GameControllerGetProductVersion(ctrl),
serial ? serial : "<nullptr>");
char *const mapping = SDL_GameControllerMapping(ctrl);
if (mapping)
{ {
sdl_game_controller_device *const device = downcast<sdl_game_controller_device *>(ptr.get()); osd_printf_verbose("Game Controller: ... mapping [%s]\n", mapping);
if (device->is_instance(instance)) SDL_free(mapping);
return device;
} }
return nullptr; else
{
osd_printf_verbose("Game Controller: ... no mapping\n");
}
// let it add controls
devinfo.configure();
return &devinfo;
} }
bool m_initialized_game_controller; bool m_initialized_game_controller;

View File

@ -218,7 +218,7 @@ std::error_condition osd_set_clipboard_text(std::string_view text) noexcept
GlobalFree(clip); GlobalFree(clip);
return win_error_to_error_condition(err); return win_error_to_error_condition(err);
} }
if (!OpenClipboard(nullptr)) if (!EmptyClipboard())
{ {
DWORD const err(GetLastError()); DWORD const err(GetLastError());
CloseClipboard(); CloseClipboard();