Differentiate between SDL joy index/instance_id.
Hopefully fix various bugs caused by not differentiating between SDL joystick_index and SDL_JoystickID (the joystick instance id.) On controller disconnect, disconnect and reconnect all configured devices because the SDL joystick index may change, and this will update the mapping. Also fix creating a state entry for the configured joystick even when it's not connected, add the controller/joystick name to the connected message and increase the poll time from 10ms to 25ms. - Fix #718. - Fix #726. Signed-off-by: Rafael Kitover <rkitover@gmail.com>
This commit is contained in:
parent
11693d8381
commit
6a7142813d
|
@ -18,8 +18,7 @@ wxSDLJoy::wxSDLJoy()
|
|||
{
|
||||
// Start up joystick if not already started
|
||||
// FIXME: check for errors
|
||||
SDL_Init(SDL_INIT_JOYSTICK);
|
||||
SDL_Init(SDL_INIT_GAMECONTROLLER);
|
||||
SDL_Init(SDL_INIT_JOYSTICK|SDL_INIT_GAMECONTROLLER);
|
||||
SDL_GameControllerEventState(SDL_ENABLE);
|
||||
SDL_JoystickEventState(SDL_ENABLE);
|
||||
}
|
||||
|
@ -69,20 +68,18 @@ void wxSDLJoy::Poll()
|
|||
{
|
||||
auto joy = e.cbutton.which;
|
||||
|
||||
if (!SDL_IsGameController(joy))
|
||||
break;
|
||||
|
||||
if (contains(joystate, joy)) {
|
||||
if (contains(instance_map, joy)) {
|
||||
auto& state = *(instance_map[joy]);
|
||||
auto but = e.cbutton.button;
|
||||
auto val = e.cbutton.state;
|
||||
auto prev_val = joystate[joy].button[but];
|
||||
auto prev_val = state.button[but];
|
||||
|
||||
if (val != prev_val) {
|
||||
CreateAndSendEvent(joy, WXSDLJOY_BUTTON, but, val, prev_val);
|
||||
CreateAndSendEvent(state.index, WXSDLJOY_BUTTON, but, val, prev_val);
|
||||
|
||||
joystate[joy].button[but] = val;
|
||||
state.button[but] = val;
|
||||
|
||||
wxLogDebug("GOT SDL_CONTROLLERBUTTON: joy:%d but:%d val:%d prev_val:%d", joy, but, val, prev_val);
|
||||
wxLogDebug("GOT SDL_CONTROLLERBUTTON: joy:%d but:%d val:%d prev_val:%d", state.index, but, val, prev_val);
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -94,20 +91,18 @@ void wxSDLJoy::Poll()
|
|||
{
|
||||
auto joy = e.caxis.which;
|
||||
|
||||
if (!SDL_IsGameController(joy))
|
||||
break;
|
||||
|
||||
if (contains(joystate, joy)) {
|
||||
if (contains(instance_map, joy)) {
|
||||
auto& state = *(instance_map[joy]);
|
||||
auto axis = e.caxis.axis;
|
||||
auto val = axisval(e.caxis.value);
|
||||
auto prev_val = joystate[joy].axis[axis];
|
||||
auto prev_val = state.axis[axis];
|
||||
|
||||
if (val != prev_val) {
|
||||
CreateAndSendEvent(joy, WXSDLJOY_AXIS, axis, val, prev_val);
|
||||
CreateAndSendEvent(state.index, WXSDLJOY_AXIS, axis, val, prev_val);
|
||||
|
||||
joystate[joy].axis[axis] = val;
|
||||
state.axis[axis] = val;
|
||||
|
||||
wxLogDebug("GOT SDL_CONTROLLERAXISMOTION: joy:%d axis:%d val:%d prev_val:%d", joy, axis, val, prev_val);
|
||||
wxLogDebug("GOT SDL_CONTROLLERAXISMOTION: joy:%d axis:%d val:%d prev_val:%d", state.index, axis, val, prev_val);
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -116,18 +111,15 @@ void wxSDLJoy::Poll()
|
|||
break;
|
||||
}
|
||||
case SDL_CONTROLLERDEVICEADDED:
|
||||
case SDL_CONTROLLERDEVICEREMAPPED:
|
||||
{
|
||||
auto joy = e.cdevice.which;
|
||||
|
||||
if (!SDL_IsGameController(joy))
|
||||
break;
|
||||
|
||||
if (add_all || contains(joystate, joy)) {
|
||||
DisconnectController(joy);
|
||||
ConnectController(joy);
|
||||
RemapControllers();
|
||||
auto& state = joystate[joy];
|
||||
|
||||
systemScreenMessage(wxString::Format(_("Connected game controller %d"), joy + 1));
|
||||
if (state.dev)
|
||||
systemScreenMessage(wxString::Format(_("Connected game controller %d: %s"), joy, SDL_GameControllerName(state.dev)));
|
||||
}
|
||||
|
||||
got_event = true;
|
||||
|
@ -138,10 +130,11 @@ void wxSDLJoy::Poll()
|
|||
{
|
||||
auto joy = e.cdevice.which;
|
||||
|
||||
if (contains(joystate, joy)) {
|
||||
DisconnectController(joy);
|
||||
if (contains(instance_map, joy)) {
|
||||
auto index = instance_map[joy]->index;
|
||||
RemapControllers();
|
||||
|
||||
systemScreenMessage(wxString::Format(_("Disconnected game controller %d"), joy + 1));
|
||||
systemScreenMessage(wxString::Format(_("Disconnected game controller %d"), index));
|
||||
}
|
||||
|
||||
got_event = true;
|
||||
|
@ -156,20 +149,22 @@ void wxSDLJoy::Poll()
|
|||
{
|
||||
auto joy = e.jbutton.which;
|
||||
|
||||
if (SDL_IsGameController(joy))
|
||||
break;
|
||||
if (contains(instance_map, joy)) {
|
||||
auto& state = *(instance_map[joy]);
|
||||
|
||||
if (SDL_IsGameController(state.index))
|
||||
break;
|
||||
|
||||
if (contains(joystate, joy)) {
|
||||
auto but = e.jbutton.button;
|
||||
auto val = e.jbutton.state;
|
||||
auto prev_val = joystate[joy].button[but];
|
||||
auto prev_val = state.button[but];
|
||||
|
||||
if (val != prev_val) {
|
||||
CreateAndSendEvent(joy, WXSDLJOY_BUTTON, but, val, prev_val);
|
||||
CreateAndSendEvent(state.index, WXSDLJOY_BUTTON, but, val, prev_val);
|
||||
|
||||
joystate[joy].button[but] = val;
|
||||
state.button[but] = val;
|
||||
|
||||
wxLogDebug("GOT SDL_JOYBUTTON: joy:%d but:%d val:%d prev_val:%d", joy, but, val, prev_val);
|
||||
wxLogDebug("GOT SDL_JOYBUTTON: joy:%d but:%d val:%d prev_val:%d", state.index, but, val, prev_val);
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -181,20 +176,22 @@ void wxSDLJoy::Poll()
|
|||
{
|
||||
auto joy = e.jaxis.which;
|
||||
|
||||
if (SDL_IsGameController(joy))
|
||||
break;
|
||||
if (contains(instance_map, joy)) {
|
||||
auto& state = *(instance_map[joy]);
|
||||
|
||||
if (SDL_IsGameController(state.index))
|
||||
break;
|
||||
|
||||
if (contains(joystate, joy)) {
|
||||
auto axis = e.jaxis.axis;
|
||||
auto val = axisval(e.jaxis.value);
|
||||
auto prev_val = joystate[joy].axis[axis];
|
||||
auto prev_val = state.axis[axis];
|
||||
|
||||
if (val != prev_val) {
|
||||
CreateAndSendEvent(joy, WXSDLJOY_AXIS, axis, val, prev_val);
|
||||
CreateAndSendEvent(state.index, WXSDLJOY_AXIS, axis, val, prev_val);
|
||||
|
||||
joystate[joy].axis[axis] = val;
|
||||
state.axis[axis] = val;
|
||||
|
||||
wxLogDebug("GOT SDL_JOYAXISMOTION: joy:%d axis:%d val:%d prev_val:%d", joy, axis, val, prev_val);
|
||||
wxLogDebug("GOT SDL_JOYAXISMOTION: joy:%d axis:%d val:%d prev_val:%d", state.index, axis, val, prev_val);
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -204,16 +201,17 @@ void wxSDLJoy::Poll()
|
|||
}
|
||||
case SDL_JOYDEVICEADDED:
|
||||
{
|
||||
auto joy = e.cdevice.which;
|
||||
auto joy = e.jdevice.which;
|
||||
|
||||
if (SDL_IsGameController(joy))
|
||||
break;
|
||||
|
||||
if (add_all || contains(joystate, joy)) {
|
||||
DisconnectController(joy);
|
||||
ConnectController(joy);
|
||||
RemapControllers();
|
||||
auto& state = joystate[joy];
|
||||
|
||||
systemScreenMessage(wxString::Format(_("Connected joystick %d"), joy + 1));
|
||||
if (state.dev)
|
||||
systemScreenMessage(wxString::Format(_("Connected joystick %d: %s"), joy, SDL_JoystickName(state.dev)));
|
||||
}
|
||||
|
||||
got_event = true;
|
||||
|
@ -222,15 +220,13 @@ void wxSDLJoy::Poll()
|
|||
}
|
||||
case SDL_JOYDEVICEREMOVED:
|
||||
{
|
||||
auto joy = e.cdevice.which;
|
||||
auto joy = e.jdevice.which;
|
||||
|
||||
if (SDL_IsGameController(joy))
|
||||
break;
|
||||
if (contains(instance_map, joy)) {
|
||||
auto index = instance_map[joy]->index;
|
||||
RemapControllers();
|
||||
|
||||
if (contains(joystate, joy)) {
|
||||
DisconnectController(joy);
|
||||
|
||||
systemScreenMessage(wxString::Format(_("Disconnected joystick %d"), joy + 1));
|
||||
systemScreenMessage(wxString::Format(_("Disconnected joystick %d"), index));
|
||||
}
|
||||
|
||||
got_event = true;
|
||||
|
@ -314,34 +310,59 @@ void wxSDLJoy::Poll()
|
|||
|
||||
void wxSDLJoy::ConnectController(uint8_t joy)
|
||||
{
|
||||
if (SDL_IsGameController(joy)) {
|
||||
if (!(joystate[joy].dev = SDL_GameControllerOpen(joy))) {
|
||||
wxLogDebug("SDL_GameControllerOpen(%d) failed: %s", joy, SDL_GetError());
|
||||
return;
|
||||
SDL_Joystick* js_dev = nullptr;
|
||||
bool is_gc;
|
||||
|
||||
if ((is_gc = SDL_IsGameController(joy))) {
|
||||
auto dev = SDL_GameControllerOpen(joy);
|
||||
|
||||
if (dev) {
|
||||
joystate[joy].dev = dev;
|
||||
|
||||
js_dev = SDL_GameControllerGetJoystick(dev);
|
||||
}
|
||||
}
|
||||
else {
|
||||
if (!(joystate[joy].dev = SDL_JoystickOpen(joy))) {
|
||||
wxLogDebug("SDL_JoystickOpen(%d) failed: %s", joy, SDL_GetError());
|
||||
return;
|
||||
}
|
||||
if ((js_dev = SDL_JoystickOpen(joy)))
|
||||
joystate[joy].dev = js_dev;
|
||||
}
|
||||
|
||||
if (js_dev) {
|
||||
auto instance = SDL_JoystickInstanceID(js_dev);
|
||||
|
||||
instance_map[instance] = &(joystate[joy]);
|
||||
|
||||
joystate[joy].instance = instance;
|
||||
}
|
||||
|
||||
joystate[joy].index = joy;
|
||||
joystate[joy].is_gc = is_gc;
|
||||
}
|
||||
|
||||
void wxSDLJoy::RemapControllers()
|
||||
{
|
||||
for (auto&& joy : joystate) {
|
||||
auto& state = joy.second;
|
||||
|
||||
DisconnectController(state);
|
||||
ConnectController(joy.first);
|
||||
}
|
||||
}
|
||||
|
||||
void wxSDLJoy::DisconnectController(uint8_t joy)
|
||||
void wxSDLJoy::DisconnectController(wxSDLJoyState& state)
|
||||
{
|
||||
if (auto& dev = joystate[joy].dev) {
|
||||
if (SDL_IsGameController(joy)) {
|
||||
if (SDL_GameControllerGetAttached(dev))
|
||||
SDL_GameControllerClose(dev);
|
||||
if (auto& dev = state.dev) {
|
||||
if (state.is_gc) {
|
||||
SDL_GameControllerClose(dev);
|
||||
}
|
||||
else {
|
||||
if (SDL_JoystickGetAttached(dev))
|
||||
SDL_JoystickClose(dev);
|
||||
SDL_JoystickClose(dev);
|
||||
}
|
||||
|
||||
dev = nullptr;
|
||||
}
|
||||
|
||||
instance_map.erase(state.instance);
|
||||
}
|
||||
|
||||
void wxSDLJoy::Add(int8_t joy_n)
|
||||
|
@ -364,14 +385,14 @@ void wxSDLJoy::Remove(int8_t joy_n)
|
|||
|
||||
if (joy_n < 0) {
|
||||
for (auto&& joy : joystate)
|
||||
DisconnectController(joy.first);
|
||||
DisconnectController(joy.second);
|
||||
|
||||
joystate.clear();
|
||||
|
||||
return;
|
||||
}
|
||||
|
||||
DisconnectController(joy_n);
|
||||
DisconnectController(joystate[joy_n]);
|
||||
joystate.erase(joy_n);
|
||||
}
|
||||
|
||||
|
|
|
@ -20,25 +20,28 @@
|
|||
#include "../common/contains.h"
|
||||
|
||||
struct wxSDLJoyDev {
|
||||
private:
|
||||
union {
|
||||
SDL_GameController* dev_gc = nullptr;
|
||||
SDL_Joystick* dev_js;
|
||||
};
|
||||
public:
|
||||
operator SDL_GameController*&();
|
||||
SDL_GameController*& operator=(SDL_GameController* ptr);
|
||||
private:
|
||||
union {
|
||||
SDL_GameController* dev_gc = nullptr;
|
||||
SDL_Joystick* dev_js;
|
||||
};
|
||||
public:
|
||||
operator SDL_GameController*&();
|
||||
SDL_GameController*& operator=(SDL_GameController* ptr);
|
||||
|
||||
operator SDL_Joystick*&();
|
||||
SDL_Joystick*& operator=(SDL_Joystick* ptr);
|
||||
operator SDL_Joystick*&();
|
||||
SDL_Joystick*& operator=(SDL_Joystick* ptr);
|
||||
|
||||
operator bool();
|
||||
operator bool();
|
||||
|
||||
std::nullptr_t& operator=(std::nullptr_t&& null_ptr);
|
||||
std::nullptr_t& operator=(std::nullptr_t&& null_ptr);
|
||||
};
|
||||
|
||||
struct wxSDLJoyState {
|
||||
wxSDLJoyDev dev;
|
||||
uint8_t index = 0;
|
||||
bool is_gc = true;
|
||||
SDL_JoystickID instance = 0;
|
||||
std::unordered_map<uint8_t, int16_t> axis{};
|
||||
std::unordered_map<uint8_t, uint8_t> button{};
|
||||
};
|
||||
|
@ -70,13 +73,15 @@ protected:
|
|||
// used to continue rumbling on a timer
|
||||
void Notify();
|
||||
void ConnectController(uint8_t joy);
|
||||
void DisconnectController(uint8_t joy);
|
||||
void RemapControllers();
|
||||
void DisconnectController(wxSDLJoyState& dev);
|
||||
void CreateAndSendEvent(unsigned short joy, unsigned short ctrl_type, unsigned short ctrl_idx, short ctrl_val, short prev_val);
|
||||
|
||||
const uint8_t POLL_TIME_MS = 10;
|
||||
const uint8_t POLL_TIME_MS = 25;
|
||||
|
||||
private:
|
||||
std::unordered_map<uint8_t, wxSDLJoyState> joystate;
|
||||
std::unordered_map<SDL_JoystickID, wxSDLJoyState*> instance_map;
|
||||
bool add_all = false, rumbling = false;
|
||||
|
||||
wxLongLong last_poll = wxGetUTCTimeMillis();
|
||||
|
|
Loading…
Reference in New Issue