Better joystick hotplug support.
Combine SDL events with polling the axes and buttons of controllers every 10 milliseconds, this is necessary because of this bug in SDL: https://bugzilla.libsdl.org/show_bug.cgi?id=4886 which causes SDL to not generation button and axes events after a joystick is disconnected and then reconnected to the system. Change the axes state from a unordered_map to an std::array because the number of axes is fixed, add analogous buttons array. Keep controller state synchronized between the event receiving code and the polling code. Signed-off-by: Rafael Kitover <rkitover@gmail.com>
This commit is contained in:
parent
f306cc63b0
commit
0577f8afc2
|
@ -2,7 +2,6 @@
|
||||||
#include "wx/sdljoy.h"
|
#include "wx/sdljoy.h"
|
||||||
#include "SDL.h"
|
#include "SDL.h"
|
||||||
#include <SDL_events.h>
|
#include <SDL_events.h>
|
||||||
#include <SDL_gamecontroller.h>
|
|
||||||
#include <wx/window.h>
|
#include <wx/window.h>
|
||||||
#include "../common/range.hpp"
|
#include "../common/range.hpp"
|
||||||
#include "../common/contains.h"
|
#include "../common/contains.h"
|
||||||
|
@ -26,7 +25,7 @@ wxSDLJoy::~wxSDLJoy()
|
||||||
// SDL_QuitSubSystem(SDL_INIT_JOYSTICK);
|
// SDL_QuitSubSystem(SDL_INIT_JOYSTICK);
|
||||||
}
|
}
|
||||||
|
|
||||||
static int axisval(int x)
|
static int16_t axisval(int16_t x)
|
||||||
{
|
{
|
||||||
if (x > 0x1fff)
|
if (x > 0x1fff)
|
||||||
return 1;
|
return 1;
|
||||||
|
@ -41,6 +40,8 @@ void wxSDLJoy::Poll()
|
||||||
wxEvtHandler* handler = evthandler ? evthandler : wxWindow::FindFocus();
|
wxEvtHandler* handler = evthandler ? evthandler : wxWindow::FindFocus();
|
||||||
SDL_Event e;
|
SDL_Event e;
|
||||||
|
|
||||||
|
bool got_event = false;
|
||||||
|
|
||||||
while (SDL_PollEvent(&e)) {
|
while (SDL_PollEvent(&e)) {
|
||||||
switch (e.type) {
|
switch (e.type) {
|
||||||
case SDL_CONTROLLERBUTTONDOWN:
|
case SDL_CONTROLLERBUTTONDOWN:
|
||||||
|
@ -51,9 +52,9 @@ void wxSDLJoy::Poll()
|
||||||
if (contains(joystate, joy)) {
|
if (contains(joystate, joy)) {
|
||||||
auto but = e.cbutton.button;
|
auto but = e.cbutton.button;
|
||||||
auto val = e.cbutton.state;
|
auto val = e.cbutton.state;
|
||||||
auto prev_val = !val;
|
auto prev_val = joystate[joy].button[but];
|
||||||
|
|
||||||
if (handler) {
|
if (handler && val != prev_val) {
|
||||||
wxSDLJoyEvent ev(wxEVT_SDLJOY);
|
wxSDLJoyEvent ev(wxEVT_SDLJOY);
|
||||||
ev.joy = joy;
|
ev.joy = joy;
|
||||||
ev.ctrl_type = WXSDLJOY_BUTTON;
|
ev.ctrl_type = WXSDLJOY_BUTTON;
|
||||||
|
@ -64,8 +65,13 @@ void wxSDLJoy::Poll()
|
||||||
handler->ProcessEvent(ev);
|
handler->ProcessEvent(ev);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
joystate[joy].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", joy, but, val, prev_val);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
got_event = true;
|
||||||
|
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
case SDL_CONTROLLERAXISMOTION:
|
case SDL_CONTROLLERAXISMOTION:
|
||||||
|
@ -92,6 +98,9 @@ void wxSDLJoy::Poll()
|
||||||
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", joy, axis, val, prev_val);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
got_event = true;
|
||||||
|
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
case SDL_CONTROLLERDEVICEADDED:
|
case SDL_CONTROLLERDEVICEADDED:
|
||||||
|
@ -105,6 +114,9 @@ void wxSDLJoy::Poll()
|
||||||
|
|
||||||
systemScreenMessage(wxString::Format(_("Connected game controller %d"), joy + 1));
|
systemScreenMessage(wxString::Format(_("Connected game controller %d"), joy + 1));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
got_event = true;
|
||||||
|
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
case SDL_CONTROLLERDEVICEREMOVED:
|
case SDL_CONTROLLERDEVICEREMOVED:
|
||||||
|
@ -116,10 +128,71 @@ void wxSDLJoy::Poll()
|
||||||
|
|
||||||
systemScreenMessage(wxString::Format(_("Disconnected game controller %d"), joy + 1));
|
systemScreenMessage(wxString::Format(_("Disconnected game controller %d"), joy + 1));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
got_event = true;
|
||||||
|
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// I am only doing this shit because hotplug support is fucking broken in SDL right now.
|
||||||
|
|
||||||
|
bool do_poll = false;
|
||||||
|
wxLongLong tm = wxGetUTCTimeMillis();
|
||||||
|
|
||||||
|
if (got_event)
|
||||||
|
last_poll = tm;
|
||||||
|
else if (tm - last_poll > POLL_TIME_MS) {
|
||||||
|
do_poll = true;
|
||||||
|
last_poll = tm;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (do_poll) {
|
||||||
|
for (auto&& joy : joystate) {
|
||||||
|
for (uint8_t but = 0; but < SDL_CONTROLLER_BUTTON_MAX; but++) {
|
||||||
|
auto last_state = joy.second.button[but];
|
||||||
|
auto state = SDL_GameControllerGetButton(joy.second.dev, static_cast<SDL_GameControllerButton>(but));
|
||||||
|
|
||||||
|
if (last_state != state) {
|
||||||
|
if (handler) {
|
||||||
|
wxSDLJoyEvent ev(wxEVT_SDLJOY);
|
||||||
|
ev.joy = joy.first;
|
||||||
|
ev.ctrl_type = WXSDLJOY_BUTTON;
|
||||||
|
ev.ctrl_idx = but;
|
||||||
|
ev.ctrl_val = state;
|
||||||
|
ev.prev_val = last_state;
|
||||||
|
|
||||||
|
handler->ProcessEvent(ev);
|
||||||
|
}
|
||||||
|
|
||||||
|
joy.second.button[but] = state;
|
||||||
|
|
||||||
|
wxLogDebug("POLLED SDL_CONTROLLERBUTTON: joy:%d but:%d val:%d prev_val:%d", joy.first, but, state, last_state);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
for (uint8_t axis = 0; axis < SDL_CONTROLLER_AXIS_MAX; axis++) {
|
||||||
|
auto val = axisval(SDL_GameControllerGetAxis(joy.second.dev, static_cast<SDL_GameControllerAxis>(axis)));
|
||||||
|
auto prev_val = joy.second.axis[axis];
|
||||||
|
|
||||||
|
if (handler && val != prev_val) {
|
||||||
|
wxSDLJoyEvent ev(wxEVT_SDLJOY);
|
||||||
|
ev.joy = joy.first;
|
||||||
|
ev.ctrl_type = WXSDLJOY_AXIS;
|
||||||
|
ev.ctrl_idx = axis;
|
||||||
|
ev.ctrl_val = val;
|
||||||
|
ev.prev_val = prev_val;
|
||||||
|
|
||||||
|
handler->ProcessEvent(ev);
|
||||||
|
|
||||||
|
joy.second.axis[axis] = val;
|
||||||
|
|
||||||
|
wxLogDebug("POLLED SDL_CONTROLLERAXISMOTION: joy:%d axis:%d val:%d prev_val:%d", joy.first, axis, val, prev_val);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
void wxSDLJoy::ConnectController(uint8_t joy)
|
void wxSDLJoy::ConnectController(uint8_t joy)
|
||||||
|
|
|
@ -9,8 +9,10 @@
|
||||||
//
|
//
|
||||||
// The target window will receive EVT_SDLJOY events of type wxSDLJoyEvent.
|
// The target window will receive EVT_SDLJOY events of type wxSDLJoyEvent.
|
||||||
|
|
||||||
|
#include <array>
|
||||||
#include <vector>
|
#include <vector>
|
||||||
#include <unordered_map>
|
#include <unordered_map>
|
||||||
|
#include <wx/time.h>
|
||||||
#include <wx/event.h>
|
#include <wx/event.h>
|
||||||
#include <wx/timer.h>
|
#include <wx/timer.h>
|
||||||
#include <SDL_gamecontroller.h>
|
#include <SDL_gamecontroller.h>
|
||||||
|
@ -18,7 +20,8 @@
|
||||||
|
|
||||||
struct wxSDLJoyState {
|
struct wxSDLJoyState {
|
||||||
SDL_GameController* dev = nullptr;
|
SDL_GameController* dev = nullptr;
|
||||||
std::unordered_map<uint8_t, int8_t> axis;
|
std::array<int16_t, SDL_CONTROLLER_AXIS_MAX> axis{};
|
||||||
|
std::array<uint8_t, SDL_CONTROLLER_BUTTON_MAX> button{};
|
||||||
};
|
};
|
||||||
|
|
||||||
class wxSDLJoy : public wxTimer {
|
class wxSDLJoy : public wxTimer {
|
||||||
|
@ -52,10 +55,15 @@ protected:
|
||||||
void Notify();
|
void Notify();
|
||||||
void ConnectController(uint8_t joy);
|
void ConnectController(uint8_t joy);
|
||||||
void DisconnectController(uint8_t joy);
|
void DisconnectController(uint8_t joy);
|
||||||
|
|
||||||
|
const uint8_t POLL_TIME_MS = 10;
|
||||||
|
|
||||||
private:
|
private:
|
||||||
std::unordered_map<uint8_t, wxSDLJoyState> joystate;
|
std::unordered_map<uint8_t, wxSDLJoyState> joystate;
|
||||||
wxEvtHandler* evthandler;
|
wxEvtHandler* evthandler;
|
||||||
bool add_all = false, rumbling = false;
|
bool add_all = false, rumbling = false;
|
||||||
|
|
||||||
|
wxLongLong last_poll = wxGetUTCTimeMillis();
|
||||||
};
|
};
|
||||||
|
|
||||||
enum {
|
enum {
|
||||||
|
|
Loading…
Reference in New Issue