Refactor wxSDLJoy
This is a major refactor of the wxSDLJoy class. * Move handling of SDL objects creation and destruction to its own class. This simplifies the lifespan of SDL-related objects. * Re-add handling of HATs. This is necessary for DirectInput controllers. * Rename the public API for wxSDLJoy to be clearer. * Add documentation for every class related to wxSDLJoy.
This commit is contained in:
parent
a00f258588
commit
edc34942a8
|
@ -8,7 +8,7 @@ msgid ""
|
||||||
msgstr ""
|
msgstr ""
|
||||||
"Project-Id-Version: PACKAGE VERSION\n"
|
"Project-Id-Version: PACKAGE VERSION\n"
|
||||||
"Report-Msgid-Bugs-To: \n"
|
"Report-Msgid-Bugs-To: \n"
|
||||||
"POT-Creation-Date: 2020-09-05 19:36+0000\n"
|
"POT-Creation-Date: 2020-09-05 21:42-0700\n"
|
||||||
"PO-Revision-Date: YEAR-MO-DA HO:MI+ZONE\n"
|
"PO-Revision-Date: YEAR-MO-DA HO:MI+ZONE\n"
|
||||||
"Last-Translator: FULL NAME <EMAIL@ADDRESS>\n"
|
"Last-Translator: FULL NAME <EMAIL@ADDRESS>\n"
|
||||||
"Language-Team: LANGUAGE <LL@li.org>\n"
|
"Language-Team: LANGUAGE <LL@li.org>\n"
|
||||||
|
@ -1156,17 +1156,7 @@ msgstr ""
|
||||||
msgid "CONTROL"
|
msgid "CONTROL"
|
||||||
msgstr ""
|
msgstr ""
|
||||||
|
|
||||||
#: widgets/sdljoy.cpp:122
|
#: widgets/sdljoy.cpp:107
|
||||||
#, c-format
|
|
||||||
msgid "Connected game controller %d: %s"
|
|
||||||
msgstr ""
|
|
||||||
|
|
||||||
#: widgets/sdljoy.cpp:137
|
|
||||||
#, c-format
|
|
||||||
msgid "Disconnected game controller %d"
|
|
||||||
msgstr ""
|
|
||||||
|
|
||||||
#: widgets/sdljoy.cpp:214
|
|
||||||
#, c-format
|
#, c-format
|
||||||
msgid "Connected joystick %d: %s"
|
msgid "Connected joystick %d: %s"
|
||||||
msgstr ""
|
msgstr ""
|
||||||
|
|
|
@ -2736,7 +2736,7 @@ EVT_HANDLER(EmulatorDirectories, "Directories...")
|
||||||
EVT_HANDLER(JoypadConfigure, "Joypad options...")
|
EVT_HANDLER(JoypadConfigure, "Joypad options...")
|
||||||
{
|
{
|
||||||
wxDialog* dlg = GetXRCDialog("JoypadConfig");
|
wxDialog* dlg = GetXRCDialog("JoypadConfig");
|
||||||
joy.Add();
|
joy.PollAllJoysticks();
|
||||||
|
|
||||||
auto frame = wxGetApp().frame;
|
auto frame = wxGetApp().frame;
|
||||||
bool joy_timer = frame->IsJoyPollTimerRunning();
|
bool joy_timer = frame->IsJoyPollTimerRunning();
|
||||||
|
@ -2754,7 +2754,7 @@ EVT_HANDLER(JoypadConfigure, "Joypad options...")
|
||||||
EVT_HANDLER(Customize, "Customize UI...")
|
EVT_HANDLER(Customize, "Customize UI...")
|
||||||
{
|
{
|
||||||
wxDialog* dlg = GetXRCDialog("AccelConfig");
|
wxDialog* dlg = GetXRCDialog("AccelConfig");
|
||||||
joy.Add();
|
joy.PollAllJoysticks();
|
||||||
|
|
||||||
auto frame = wxGetApp().frame;
|
auto frame = wxGetApp().frame;
|
||||||
bool joy_timer = frame->IsJoyPollTimerRunning();
|
bool joy_timer = frame->IsJoyPollTimerRunning();
|
||||||
|
|
|
@ -2582,12 +2582,14 @@ void MainFrame::set_global_accels()
|
||||||
// the menus will be added now
|
// the menus will be added now
|
||||||
|
|
||||||
// first, zero out menu item on all accels
|
// first, zero out menu item on all accels
|
||||||
|
std::unordered_set<unsigned> needed_joysticks;
|
||||||
for (size_t i = 0; i < accels.size(); ++i) {
|
for (size_t i = 0; i < accels.size(); ++i) {
|
||||||
accels[i].Set(accels[i].GetUkey(), accels[i].GetJoystick(), accels[i].GetFlags(), accels[i].GetKeyCode(), accels[i].GetCommand());
|
accels[i].Set(accels[i].GetUkey(), accels[i].GetJoystick(), accels[i].GetFlags(), accels[i].GetKeyCode(), accels[i].GetCommand());
|
||||||
if (accels[i].GetJoystick()) {
|
if (accels[i].GetJoystick()) {
|
||||||
joy.Add(accels[i].GetJoystick() - 1);
|
needed_joysticks.insert(accels[i].GetJoystick());
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
joy.PollJoysticks(needed_joysticks);
|
||||||
|
|
||||||
// yet another O(n*m) loop. I really ought to sort the accel arrays
|
// yet another O(n*m) loop. I really ought to sort the accel arrays
|
||||||
for (int i = 0; i < ncmds; i++) {
|
for (int i = 0; i < ncmds; i++) {
|
||||||
|
|
|
@ -1391,25 +1391,25 @@ void GameArea::OnSize(wxSizeEvent& ev)
|
||||||
|
|
||||||
void GameArea::OnSDLJoy(wxSDLJoyEvent& ev)
|
void GameArea::OnSDLJoy(wxSDLJoyEvent& ev)
|
||||||
{
|
{
|
||||||
int key = ev.GetControlIndex();
|
int key = ev.control_index();
|
||||||
int mod = wxJoyKeyTextCtrl::DigitalButton(ev);
|
int mod = wxJoyKeyTextCtrl::DigitalButton(ev);
|
||||||
int joy = ev.GetJoy() + 1;
|
int joy = ev.player_index();
|
||||||
|
|
||||||
// mutually exclusive key types unpress their opposite
|
// mutually exclusive key types unpress their opposite
|
||||||
if (mod == WXJB_AXIS_PLUS) {
|
if (mod == WXJB_AXIS_PLUS) {
|
||||||
process_key_press(false, key, WXJB_AXIS_MINUS, joy);
|
process_key_press(false, key, WXJB_AXIS_MINUS, joy);
|
||||||
process_key_press(ev.GetControlValue() != 0, key, mod, joy);
|
process_key_press(ev.control_value() != 0, key, mod, joy);
|
||||||
} else if (mod == WXJB_AXIS_MINUS) {
|
} else if (mod == WXJB_AXIS_MINUS) {
|
||||||
process_key_press(false, key, WXJB_AXIS_PLUS, joy);
|
process_key_press(false, key, WXJB_AXIS_PLUS, joy);
|
||||||
process_key_press(ev.GetControlValue() != 0, key, mod, joy);
|
process_key_press(ev.control_value() != 0, key, mod, joy);
|
||||||
} else if (mod >= WXJB_HAT_FIRST && mod <= WXJB_HAT_LAST) {
|
} else if (mod >= WXJB_HAT_FIRST && mod <= WXJB_HAT_LAST) {
|
||||||
int value = ev.GetControlValue();
|
int value = ev.control_value();
|
||||||
process_key_press(value & SDL_HAT_UP, key, WXJB_HAT_N, joy);
|
process_key_press(value & SDL_HAT_UP, key, WXJB_HAT_N, joy);
|
||||||
process_key_press(value & SDL_HAT_DOWN, key, WXJB_HAT_S, joy);
|
process_key_press(value & SDL_HAT_DOWN, key, WXJB_HAT_S, joy);
|
||||||
process_key_press(value & SDL_HAT_RIGHT, key, WXJB_HAT_E, joy);
|
process_key_press(value & SDL_HAT_RIGHT, key, WXJB_HAT_E, joy);
|
||||||
process_key_press(value & SDL_HAT_LEFT, key, WXJB_HAT_W, joy);
|
process_key_press(value & SDL_HAT_LEFT, key, WXJB_HAT_W, joy);
|
||||||
} else
|
} else
|
||||||
process_key_press(ev.GetControlValue() != 0, key, mod, joy);
|
process_key_press(ev.control_value() != 0, key, mod, joy);
|
||||||
|
|
||||||
// tell Linux to turn off the screensaver/screen-blank if joystick button was pressed
|
// tell Linux to turn off the screensaver/screen-blank if joystick button was pressed
|
||||||
// this shouldn't be necessary of course
|
// this shouldn't be necessary of course
|
||||||
|
|
|
@ -19,8 +19,8 @@ wxJoyKeyBinding newWxJoyKeyBinding(int key, int mod, int joy)
|
||||||
|
|
||||||
int wxJoyKeyTextCtrl::DigitalButton(wxSDLJoyEvent& event)
|
int wxJoyKeyTextCtrl::DigitalButton(wxSDLJoyEvent& event)
|
||||||
{
|
{
|
||||||
int sdlval = event.GetControlValue();
|
int16_t sdlval = event.control_value();
|
||||||
int sdltype = event.GetControlType();
|
wxSDLControl sdltype = event.control();
|
||||||
|
|
||||||
switch (sdltype) {
|
switch (sdltype) {
|
||||||
case WXSDLJOY_AXIS:
|
case WXSDLJOY_AXIS:
|
||||||
|
@ -72,8 +72,8 @@ void wxJoyKeyTextCtrl::OnJoy(wxSDLJoyEvent& event)
|
||||||
{
|
{
|
||||||
static wxLongLong last_event = 0;
|
static wxLongLong last_event = 0;
|
||||||
|
|
||||||
int val = event.GetControlValue();
|
int16_t val = event.control_value();
|
||||||
int type = event.GetControlType();
|
wxSDLControl type = event.control();
|
||||||
|
|
||||||
// Filter consecutive axis motions within 300ms, as this adds two bindings
|
// Filter consecutive axis motions within 300ms, as this adds two bindings
|
||||||
// +1/-1 instead of the one intended.
|
// +1/-1 instead of the one intended.
|
||||||
|
@ -83,7 +83,8 @@ void wxJoyKeyTextCtrl::OnJoy(wxSDLJoyEvent& event)
|
||||||
last_event = wxGetUTCTimeMillis();
|
last_event = wxGetUTCTimeMillis();
|
||||||
|
|
||||||
int mod = DigitalButton(event);
|
int mod = DigitalButton(event);
|
||||||
int key = event.GetControlIndex(), joy = event.GetJoy() + 1;
|
uint8_t key = event.control_index();
|
||||||
|
unsigned joy = event.player_index();
|
||||||
|
|
||||||
if (!val || mod < 0)
|
if (!val || mod < 0)
|
||||||
return;
|
return;
|
||||||
|
|
|
@ -1,35 +1,36 @@
|
||||||
#include <cstddef>
|
|
||||||
#include "wxvbam.h"
|
|
||||||
#include "wx/sdljoy.h"
|
#include "wx/sdljoy.h"
|
||||||
#include "SDL.h"
|
|
||||||
#include <SDL_events.h>
|
#include <algorithm>
|
||||||
#include "../common/range.hpp"
|
#include <wx/timer.h>
|
||||||
|
#include <SDL.h>
|
||||||
|
|
||||||
#include "../common/contains.h"
|
#include "../common/contains.h"
|
||||||
|
#include "../wxvbam.h"
|
||||||
|
|
||||||
using namespace Range;
|
namespace {
|
||||||
|
|
||||||
// For testing a GameController as a Joystick:
|
const std::string SDLEventTypeToDebugString(int32_t event_type) {
|
||||||
//#define SDL_IsGameController(x) false
|
switch (event_type) {
|
||||||
|
case SDL_CONTROLLERBUTTONDOWN:
|
||||||
DEFINE_EVENT_TYPE(wxEVT_SDLJOY)
|
case SDL_CONTROLLERBUTTONUP:
|
||||||
|
return "SDL_CONTROLLERBUTTON";
|
||||||
wxSDLJoy::wxSDLJoy()
|
case SDL_CONTROLLERAXISMOTION:
|
||||||
: wxTimer()
|
return "SDL_CONTROLLERAXISMOTION";
|
||||||
{
|
case SDL_JOYBUTTONDOWN:
|
||||||
// Start up joystick if not already started
|
case SDL_JOYBUTTONUP:
|
||||||
// FIXME: check for errors
|
return "SDL_JOYBUTTON";
|
||||||
SDL_Init(SDL_INIT_JOYSTICK|SDL_INIT_GAMECONTROLLER);
|
case SDL_JOYAXISMOTION:
|
||||||
SDL_GameControllerEventState(SDL_ENABLE);
|
return "SDL_JOYAXISMOTION";
|
||||||
SDL_JoystickEventState(SDL_ENABLE);
|
case SDL_JOYHATMOTION:
|
||||||
|
return "SDL_JOYHATMOTION";
|
||||||
|
default:
|
||||||
|
assert(false);
|
||||||
|
return "UNKNOWN SDL EVENT";
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
wxSDLJoy::~wxSDLJoy()
|
// Converts an axis value to a direction since we do not need analog controls.
|
||||||
{
|
static int16_t AxisValueToDirection(int16_t x) {
|
||||||
// SDL_QuitSubSystem(SDL_INIT_JOYSTICK);
|
|
||||||
}
|
|
||||||
|
|
||||||
static int16_t axisval(int16_t x)
|
|
||||||
{
|
|
||||||
if (x > 0x1fff)
|
if (x > 0x1fff)
|
||||||
return 1;
|
return 1;
|
||||||
else if (x < -0x1fff)
|
else if (x < -0x1fff)
|
||||||
|
@ -38,420 +39,460 @@ static int16_t axisval(int16_t x)
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
void wxSDLJoy::CreateAndSendEvent(unsigned short joy, unsigned short ctrl_type, unsigned short ctrl_idx, short ctrl_val, short prev_val)
|
// The interval between 2 polls in ms.
|
||||||
{
|
const wxLongLong kPollTimeInterval(25);
|
||||||
auto handler = wxGetApp().frame->GetJoyEventHandler();
|
|
||||||
|
|
||||||
if (!handler)
|
} // namespace
|
||||||
|
|
||||||
|
// For testing a GameController as a Joystick:
|
||||||
|
//#define SDL_IsGameController(x) false
|
||||||
|
|
||||||
|
DEFINE_EVENT_TYPE(wxEVT_SDLJOY)
|
||||||
|
|
||||||
|
wxSDLJoyEvent::wxSDLJoyEvent(
|
||||||
|
unsigned player_index,
|
||||||
|
wxSDLControl control,
|
||||||
|
uint8_t control_index,
|
||||||
|
int16_t control_value) :
|
||||||
|
wxCommandEvent(wxEVT_SDLJOY),
|
||||||
|
player_index_(player_index),
|
||||||
|
control_(control),
|
||||||
|
control_index_(control_index),
|
||||||
|
control_value_(control_value) {}
|
||||||
|
|
||||||
|
// Represents the current state of a joystick. This class takes care of
|
||||||
|
// initializing and destroying SDL resources on construction and destruction so
|
||||||
|
// every associated SDL state for a joystick dies with this object.
|
||||||
|
class wxSDLJoyState : public wxTimer {
|
||||||
|
public:
|
||||||
|
explicit wxSDLJoyState(int sdl_index);
|
||||||
|
~wxSDLJoyState() override;
|
||||||
|
|
||||||
|
// Disable copy constructor and assignment. This is to prevent double
|
||||||
|
// closure of the SDL objects.
|
||||||
|
wxSDLJoyState(const wxSDLJoyState&) = delete;
|
||||||
|
wxSDLJoyState& operator=(const wxSDLJoyState&) = delete;
|
||||||
|
|
||||||
|
// Returns true if this object was properly initialized.
|
||||||
|
bool IsValid() const;
|
||||||
|
|
||||||
|
// Processes an SDL event.
|
||||||
|
void ProcessEvent(int32_t event_type,
|
||||||
|
uint8_t control_index,
|
||||||
|
int16_t control_value);
|
||||||
|
|
||||||
|
// Polls the current state of the joystick and sends events as needed.
|
||||||
|
void Poll();
|
||||||
|
|
||||||
|
// Activates or deactivates rumble.
|
||||||
|
void SetRumble(bool activate_rumble);
|
||||||
|
|
||||||
|
SDL_JoystickID joystick_id() const { return joystick_id_; }
|
||||||
|
|
||||||
|
private:
|
||||||
|
// wxTimer implementation.
|
||||||
|
// Used to rumble on a timer.
|
||||||
|
void Notify() override;
|
||||||
|
|
||||||
|
// The Joystick player index.
|
||||||
|
unsigned player_index_;
|
||||||
|
|
||||||
|
// SDL Joystick ID used for events.
|
||||||
|
SDL_JoystickID joystick_id_;
|
||||||
|
|
||||||
|
// The SDL GameController instance.
|
||||||
|
SDL_GameController* game_controller_ = nullptr;
|
||||||
|
|
||||||
|
// The SDL Joystick instance.
|
||||||
|
SDL_Joystick* joystick_ = nullptr;
|
||||||
|
|
||||||
|
// Current state of Joystick axis.
|
||||||
|
std::unordered_map<uint8_t, int16_t> axis_{};
|
||||||
|
|
||||||
|
// Current state of Joystick buttons.
|
||||||
|
std::unordered_map<uint8_t, uint8_t> buttons_{};
|
||||||
|
|
||||||
|
// Current state of Joystick HAT. Unused for GameControllers.
|
||||||
|
std::unordered_map<uint8_t, uint8_t> hats_{};
|
||||||
|
|
||||||
|
// Set to true to activate joystick rumble.
|
||||||
|
bool rumbling_ = false;
|
||||||
|
};
|
||||||
|
|
||||||
|
wxSDLJoyState::wxSDLJoyState(int sdl_index)
|
||||||
|
: player_index_(sdl_index + 1) {
|
||||||
|
if (SDL_IsGameController(sdl_index)) {
|
||||||
|
game_controller_ = SDL_GameControllerOpen(sdl_index);
|
||||||
|
if (game_controller_)
|
||||||
|
joystick_ = SDL_GameControllerGetJoystick(game_controller_);
|
||||||
|
} else {
|
||||||
|
joystick_ = SDL_JoystickOpen(sdl_index);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!joystick_)
|
||||||
return;
|
return;
|
||||||
|
|
||||||
wxSDLJoyEvent *ev = new wxSDLJoyEvent(wxEVT_SDLJOY);
|
joystick_id_ = SDL_JoystickInstanceID(joystick_);
|
||||||
ev->joy = joy;
|
systemScreenMessage(
|
||||||
ev->ctrl_type = ctrl_type;
|
wxString::Format(_("Connected joystick %d: %s"),
|
||||||
ev->ctrl_idx = ctrl_idx;
|
player_index_, SDL_JoystickNameForIndex(sdl_index)));
|
||||||
ev->ctrl_val = ctrl_val;
|
|
||||||
ev->prev_val = prev_val;
|
|
||||||
|
|
||||||
wxQueueEvent(handler, ev);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
void wxSDLJoy::Poll()
|
wxSDLJoyState::~wxSDLJoyState() {
|
||||||
{
|
// Nothing to do if this object is not initialized.
|
||||||
SDL_Event e;
|
if (!joystick_)
|
||||||
|
return;
|
||||||
bool got_event = false;
|
|
||||||
|
if (game_controller_)
|
||||||
while (SDL_PollEvent(&e)) {
|
SDL_GameControllerClose(game_controller_);
|
||||||
switch (e.type) {
|
else
|
||||||
case SDL_CONTROLLERBUTTONDOWN:
|
SDL_JoystickClose(joystick_);
|
||||||
case SDL_CONTROLLERBUTTONUP:
|
|
||||||
{
|
systemScreenMessage(
|
||||||
auto joy = e.cbutton.which;
|
wxString::Format(_("Disconnected joystick %d"), player_index_));
|
||||||
|
|
||||||
if (contains(instance_map, joy)) {
|
|
||||||
auto& state = *(instance_map[joy]);
|
|
||||||
auto but = e.cbutton.button;
|
|
||||||
auto val = e.cbutton.state;
|
|
||||||
auto prev_val = state.button[but];
|
|
||||||
|
|
||||||
if (val != prev_val) {
|
|
||||||
CreateAndSendEvent(state.index, WXSDLJOY_BUTTON, but, val, prev_val);
|
|
||||||
|
|
||||||
state.button[but] = val;
|
|
||||||
|
|
||||||
wxLogDebug("GOT SDL_CONTROLLERBUTTON: joy:%d but:%d val:%d prev_val:%d", state.index, but, val, prev_val);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
got_event = true;
|
|
||||||
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
case SDL_CONTROLLERAXISMOTION:
|
|
||||||
{
|
|
||||||
auto joy = e.caxis.which;
|
|
||||||
|
|
||||||
if (contains(instance_map, joy)) {
|
|
||||||
auto& state = *(instance_map[joy]);
|
|
||||||
auto axis = e.caxis.axis;
|
|
||||||
auto val = axisval(e.caxis.value);
|
|
||||||
auto prev_val = state.axis[axis];
|
|
||||||
|
|
||||||
if (val != prev_val) {
|
|
||||||
CreateAndSendEvent(state.index, WXSDLJOY_AXIS, axis, val, prev_val);
|
|
||||||
|
|
||||||
state.axis[axis] = val;
|
|
||||||
|
|
||||||
wxLogDebug("GOT SDL_CONTROLLERAXISMOTION: joy:%d axis:%d val:%d prev_val:%d", state.index, axis, val, prev_val);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
got_event = true;
|
|
||||||
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
case SDL_CONTROLLERDEVICEADDED:
|
|
||||||
{
|
|
||||||
auto joy = e.cdevice.which;
|
|
||||||
|
|
||||||
if (add_all || contains(joystate, joy)) {
|
|
||||||
RemapControllers();
|
|
||||||
auto& state = joystate[joy];
|
|
||||||
|
|
||||||
if (state.dev)
|
|
||||||
systemScreenMessage(wxString::Format(_("Connected game controller %d: %s"), joy, SDL_GameControllerName(state.dev)));
|
|
||||||
}
|
|
||||||
|
|
||||||
got_event = true;
|
|
||||||
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
case SDL_CONTROLLERDEVICEREMOVED:
|
|
||||||
{
|
|
||||||
auto joy = e.cdevice.which;
|
|
||||||
|
|
||||||
if (contains(instance_map, joy)) {
|
|
||||||
auto index = instance_map[joy]->index;
|
|
||||||
RemapControllers();
|
|
||||||
|
|
||||||
systemScreenMessage(wxString::Format(_("Disconnected game controller %d"), index));
|
|
||||||
}
|
|
||||||
|
|
||||||
got_event = true;
|
|
||||||
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
|
|
||||||
// Joystck events for non-GameControllers.
|
|
||||||
|
|
||||||
case SDL_JOYBUTTONDOWN:
|
|
||||||
case SDL_JOYBUTTONUP:
|
|
||||||
{
|
|
||||||
auto joy = e.jbutton.which;
|
|
||||||
|
|
||||||
if (contains(instance_map, joy)) {
|
|
||||||
auto& state = *(instance_map[joy]);
|
|
||||||
|
|
||||||
if (SDL_IsGameController(state.index))
|
|
||||||
break;
|
|
||||||
|
|
||||||
auto but = e.jbutton.button;
|
|
||||||
auto val = e.jbutton.state;
|
|
||||||
auto prev_val = state.button[but];
|
|
||||||
|
|
||||||
if (val != prev_val) {
|
|
||||||
CreateAndSendEvent(state.index, WXSDLJOY_BUTTON, but, val, prev_val);
|
|
||||||
|
|
||||||
state.button[but] = val;
|
|
||||||
|
|
||||||
wxLogDebug("GOT SDL_JOYBUTTON: joy:%d but:%d val:%d prev_val:%d", state.index, but, val, prev_val);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
got_event = true;
|
|
||||||
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
case SDL_JOYAXISMOTION:
|
|
||||||
{
|
|
||||||
auto joy = e.jaxis.which;
|
|
||||||
|
|
||||||
if (contains(instance_map, joy)) {
|
|
||||||
auto& state = *(instance_map[joy]);
|
|
||||||
|
|
||||||
if (SDL_IsGameController(state.index))
|
|
||||||
break;
|
|
||||||
|
|
||||||
auto axis = e.jaxis.axis;
|
|
||||||
auto val = axisval(e.jaxis.value);
|
|
||||||
auto prev_val = state.axis[axis];
|
|
||||||
|
|
||||||
if (val != prev_val) {
|
|
||||||
CreateAndSendEvent(state.index, WXSDLJOY_AXIS, axis, val, prev_val);
|
|
||||||
|
|
||||||
state.axis[axis] = val;
|
|
||||||
|
|
||||||
wxLogDebug("GOT SDL_JOYAXISMOTION: joy:%d axis:%d val:%d prev_val:%d", state.index, axis, val, prev_val);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
got_event = true;
|
|
||||||
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
case SDL_JOYDEVICEADDED:
|
|
||||||
{
|
|
||||||
auto joy = e.jdevice.which;
|
|
||||||
|
|
||||||
if (SDL_IsGameController(joy))
|
|
||||||
break;
|
|
||||||
|
|
||||||
if (add_all || contains(joystate, joy)) {
|
|
||||||
RemapControllers();
|
|
||||||
auto& state = joystate[joy];
|
|
||||||
|
|
||||||
if (state.dev)
|
|
||||||
systemScreenMessage(wxString::Format(_("Connected joystick %d: %s"), joy, SDL_JoystickName(state.dev)));
|
|
||||||
}
|
|
||||||
|
|
||||||
got_event = true;
|
|
||||||
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
case SDL_JOYDEVICEREMOVED:
|
|
||||||
{
|
|
||||||
auto joy = e.jdevice.which;
|
|
||||||
|
|
||||||
if (contains(instance_map, joy)) {
|
|
||||||
auto index = instance_map[joy]->index;
|
|
||||||
RemapControllers();
|
|
||||||
|
|
||||||
systemScreenMessage(wxString::Format(_("Disconnected joystick %d"), index));
|
|
||||||
}
|
|
||||||
|
|
||||||
got_event = true;
|
|
||||||
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
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) {
|
|
||||||
if (!joy.second.dev) continue;
|
|
||||||
|
|
||||||
if (SDL_IsGameController(joy.first)) {
|
|
||||||
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) {
|
|
||||||
CreateAndSendEvent(joy.first, WXSDLJOY_BUTTON, but, state, last_state);
|
|
||||||
|
|
||||||
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 (val != prev_val) {
|
|
||||||
CreateAndSendEvent(joy.first, WXSDLJOY_AXIS, axis, val, prev_val);
|
|
||||||
|
|
||||||
joy.second.axis[axis] = val;
|
|
||||||
|
|
||||||
wxLogDebug("POLLED SDL_CONTROLLERAXISMOTION: joy:%d axis:%d val:%d prev_val:%d", joy.first, axis, val, prev_val);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
else {
|
|
||||||
for (uint8_t but = 0; but < SDL_JoystickNumButtons(joy.second.dev); but++) {
|
|
||||||
auto last_state = joy.second.button[but];
|
|
||||||
auto state = SDL_JoystickGetButton(joy.second.dev, but);
|
|
||||||
|
|
||||||
if (last_state != state) {
|
|
||||||
CreateAndSendEvent(joy.first, WXSDLJOY_BUTTON, but, state, last_state);
|
|
||||||
|
|
||||||
joy.second.button[but] = state;
|
|
||||||
|
|
||||||
wxLogDebug("POLLED SDL_JOYBUTTON: joy:%d but:%d val:%d prev_val:%d", joy.first, but, state, last_state);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
for (uint8_t axis = 0; axis < SDL_JoystickNumAxes(joy.second.dev); axis++) {
|
|
||||||
auto val = axisval(SDL_JoystickGetAxis(joy.second.dev, axis));
|
|
||||||
auto prev_val = joy.second.axis[axis];
|
|
||||||
|
|
||||||
if (val != prev_val) {
|
|
||||||
CreateAndSendEvent(joy.first, WXSDLJOY_AXIS, axis, val, prev_val);
|
|
||||||
|
|
||||||
joy.second.axis[axis] = val;
|
|
||||||
|
|
||||||
wxLogDebug("POLLED SDL_JOYAXISMOTION: joy:%d axis:%d val:%d prev_val:%d", joy.first, axis, val, prev_val);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
void wxSDLJoy::ConnectController(uint8_t joy)
|
bool wxSDLJoyState::IsValid() const {
|
||||||
{
|
return joystick_;
|
||||||
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 ((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()
|
void wxSDLJoyState::ProcessEvent(int32_t event_type,
|
||||||
{
|
uint8_t control_index,
|
||||||
for (auto&& joy : joystate) {
|
int16_t control_value) {
|
||||||
auto& state = joy.second;
|
int16_t previous_value = 0;
|
||||||
|
wxSDLControl control;
|
||||||
|
bool value_changed = false;
|
||||||
|
|
||||||
DisconnectController(state);
|
switch (event_type) {
|
||||||
ConnectController(joy.first);
|
case SDL_JOYBUTTONDOWN:
|
||||||
}
|
case SDL_JOYBUTTONUP:
|
||||||
}
|
// Do not process joystick events for game controllers.
|
||||||
|
if (game_controller_) {
|
||||||
void wxSDLJoy::DisconnectController(wxSDLJoyState& state)
|
return;
|
||||||
{
|
|
||||||
if (auto& dev = state.dev) {
|
|
||||||
if (state.is_gc) {
|
|
||||||
SDL_GameControllerClose(dev);
|
|
||||||
}
|
}
|
||||||
else {
|
// Fallhrough.
|
||||||
SDL_JoystickClose(dev);
|
case SDL_CONTROLLERBUTTONDOWN:
|
||||||
|
case SDL_CONTROLLERBUTTONUP:
|
||||||
|
control = wxSDLControl::WXSDLJOY_BUTTON;
|
||||||
|
previous_value = buttons_[control_index];
|
||||||
|
if (previous_value != control_value) {
|
||||||
|
buttons_[control_index] = control_value;
|
||||||
|
value_changed = true;
|
||||||
}
|
}
|
||||||
|
break;
|
||||||
|
|
||||||
dev = nullptr;
|
case SDL_JOYHATMOTION:
|
||||||
}
|
// Do not process joystick events for game controllers.
|
||||||
|
if (game_controller_) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
control = wxSDLControl::WXSDLJOY_HAT;
|
||||||
|
previous_value = hats_[control_index];
|
||||||
|
if (previous_value != control_value) {
|
||||||
|
hats_[control_index] = control_value;
|
||||||
|
value_changed = true;
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
|
||||||
instance_map.erase(state.instance);
|
case SDL_JOYAXISMOTION:
|
||||||
}
|
// Do not process joystick events for game controllers.
|
||||||
|
if (game_controller_) {
|
||||||
void wxSDLJoy::Add(int8_t joy_n)
|
return;
|
||||||
{
|
}
|
||||||
if (joy_n < 0) {
|
// Fallhrough.
|
||||||
for (uint8_t joy : range(0, SDL_NumJoysticks()))
|
case SDL_CONTROLLERAXISMOTION:
|
||||||
ConnectController(joy);
|
control = wxSDLControl::WXSDLJOY_AXIS;
|
||||||
|
previous_value = axis_[control_index];
|
||||||
add_all = true;
|
if (previous_value != control_value) {
|
||||||
|
axis_[control_index] = control_value;
|
||||||
|
value_changed = true;
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
|
||||||
|
default:
|
||||||
|
// This should never happen.
|
||||||
|
assert(false);
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
ConnectController(joy_n);
|
if (value_changed) {
|
||||||
}
|
wxLogDebug("GOT %s: joy:%d ctrl_idx:%d val:%d prev_val:%d",
|
||||||
|
SDLEventTypeToDebugString(event_type), player_index_,
|
||||||
|
control_index, control_value, previous_value);
|
||||||
|
|
||||||
void wxSDLJoy::Remove(int8_t joy_n)
|
auto handler = wxGetApp().frame->GetJoyEventHandler();
|
||||||
{
|
if (!handler)
|
||||||
add_all = false;
|
return;
|
||||||
|
|
||||||
if (joy_n < 0) {
|
wxQueueEvent(handler,
|
||||||
for (auto&& joy : joystate)
|
new wxSDLJoyEvent(
|
||||||
DisconnectController(joy.second);
|
player_index_, control, control_index, control_value));
|
||||||
|
|
||||||
joystate.clear();
|
|
||||||
|
|
||||||
return;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
DisconnectController(joystate[joy_n]);
|
|
||||||
joystate.erase(joy_n);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
void wxSDLJoy::SetRumble(bool do_rumble)
|
void wxSDLJoyState::Poll() {
|
||||||
{
|
if (game_controller_) {
|
||||||
rumbling = do_rumble;
|
for (uint8_t but = 0; but < SDL_CONTROLLER_BUTTON_MAX; but++) {
|
||||||
|
uint16_t previous_value = buttons_[but];
|
||||||
|
uint16_t current_value =
|
||||||
|
SDL_GameControllerGetButton(
|
||||||
|
game_controller_,
|
||||||
|
static_cast<SDL_GameControllerButton>(but));
|
||||||
|
|
||||||
|
if (previous_value != current_value)
|
||||||
|
ProcessEvent(SDL_CONTROLLERBUTTONUP, but, current_value);
|
||||||
|
}
|
||||||
|
|
||||||
|
for (uint8_t axis = 0; axis < SDL_CONTROLLER_AXIS_MAX; axis++) {
|
||||||
|
uint16_t previous_value = axis_[axis];
|
||||||
|
uint16_t current_value =
|
||||||
|
AxisValueToDirection(
|
||||||
|
SDL_GameControllerGetAxis(
|
||||||
|
game_controller_,
|
||||||
|
static_cast<SDL_GameControllerAxis>(axis)));
|
||||||
|
|
||||||
|
if (previous_value != current_value)
|
||||||
|
ProcessEvent(SDL_CONTROLLERAXISMOTION, axis, current_value);
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
for (uint8_t but = 0; but < SDL_JoystickNumButtons(joystick_); but++) {
|
||||||
|
uint16_t previous_value = buttons_[but];
|
||||||
|
uint16_t current_value = SDL_JoystickGetButton(joystick_, but);
|
||||||
|
|
||||||
|
if (previous_value != current_value)
|
||||||
|
ProcessEvent(SDL_JOYBUTTONUP, but, current_value);
|
||||||
|
}
|
||||||
|
|
||||||
|
for (uint8_t axis = 0; axis < SDL_JoystickNumAxes(joystick_); axis++) {
|
||||||
|
uint16_t previous_value = axis_[axis];
|
||||||
|
uint16_t current_value =
|
||||||
|
AxisValueToDirection(SDL_JoystickGetButton(joystick_, axis));
|
||||||
|
|
||||||
|
if (previous_value != current_value)
|
||||||
|
ProcessEvent(SDL_JOYAXISMOTION, axis, current_value);
|
||||||
|
}
|
||||||
|
|
||||||
|
for (uint8_t hat = 0; hat < SDL_JoystickNumHats(joystick_); hat++) {
|
||||||
|
uint16_t previous_value = hats_[hat];
|
||||||
|
uint16_t current_value = SDL_JoystickGetHat(joystick_, hat);
|
||||||
|
|
||||||
|
if (previous_value != current_value)
|
||||||
|
ProcessEvent(SDL_JOYHATMOTION, hat, current_value);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void wxSDLJoyState::SetRumble(bool activate_rumble) {
|
||||||
|
rumbling_ = activate_rumble;
|
||||||
|
|
||||||
#if SDL_VERSION_ATLEAST(2, 0, 9)
|
#if SDL_VERSION_ATLEAST(2, 0, 9)
|
||||||
// Do rumble only on device 0, and only if it's a GameController.
|
if (!game_controller_)
|
||||||
auto dev = joystate[0].dev;
|
return;
|
||||||
if (dev && SDL_IsGameController(0)) {
|
|
||||||
if (rumbling) {
|
if (rumbling_) {
|
||||||
SDL_GameControllerRumble(dev, 0xFFFF, 0xFFFF, 300);
|
SDL_GameControllerRumble(game_controller_, 0xFFFF, 0xFFFF, 300);
|
||||||
if (!IsRunning())
|
if (!IsRunning())
|
||||||
Start(150);
|
Start(150);
|
||||||
}
|
} else {
|
||||||
else {
|
SDL_GameControllerRumble(game_controller_, 0, 0, 0);
|
||||||
SDL_GameControllerRumble(dev, 0, 0, 0);
|
Stop();
|
||||||
Stop();
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
#endif
|
#endif
|
||||||
}
|
}
|
||||||
|
|
||||||
void wxSDLJoy::Notify()
|
void wxSDLJoyState::Notify() {
|
||||||
{
|
SetRumble(rumbling_);
|
||||||
SetRumble(rumbling);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
wxSDLJoyDev::operator SDL_GameController*&()
|
wxSDLJoy::wxSDLJoy() {
|
||||||
{
|
// Start up joystick if not already started
|
||||||
return dev_gc;
|
// FIXME: check for errors
|
||||||
|
SDL_Init(SDL_INIT_JOYSTICK | SDL_INIT_GAMECONTROLLER);
|
||||||
|
SDL_GameControllerEventState(SDL_ENABLE);
|
||||||
|
SDL_JoystickEventState(SDL_ENABLE);
|
||||||
}
|
}
|
||||||
|
|
||||||
SDL_GameController*& wxSDLJoyDev::operator=(SDL_GameController* ptr)
|
wxSDLJoy::~wxSDLJoy() {
|
||||||
{
|
// It is necessary to free all SDL resources before quitting SDL.
|
||||||
dev_gc = ptr;
|
joystick_states_.clear();
|
||||||
return dev_gc;
|
SDL_QuitSubSystem(SDL_INIT_JOYSTICK);
|
||||||
|
SDL_QuitSubSystem(SDL_INIT_GAMECONTROLLER);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void wxSDLJoy::Poll() {
|
||||||
|
SDL_Event e;
|
||||||
|
bool got_event = false;
|
||||||
|
|
||||||
wxSDLJoyDev::operator SDL_Joystick*&()
|
while (SDL_PollEvent(&e)) {
|
||||||
{
|
switch (e.type) {
|
||||||
return dev_js;
|
case SDL_CONTROLLERBUTTONDOWN:
|
||||||
|
case SDL_CONTROLLERBUTTONUP:
|
||||||
|
{
|
||||||
|
wxSDLJoyState* joy_state = FindJoyState(e.cbutton.which);
|
||||||
|
if (joy_state) {
|
||||||
|
joy_state->ProcessEvent(
|
||||||
|
e.type, e.cbutton.button, e.cbutton.state);
|
||||||
|
}
|
||||||
|
got_event = true;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
case SDL_CONTROLLERAXISMOTION:
|
||||||
|
{
|
||||||
|
wxSDLJoyState* joy_state = FindJoyState(e.caxis.which);
|
||||||
|
if (joy_state) {
|
||||||
|
joy_state->ProcessEvent(
|
||||||
|
e.type, e.caxis.axis, AxisValueToDirection(e.caxis.value));
|
||||||
|
}
|
||||||
|
got_event = true;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
case SDL_CONTROLLERDEVICEADDED:
|
||||||
|
case SDL_CONTROLLERDEVICEREMOVED:
|
||||||
|
// Do nothing. This will be handled with JOYDEVICEADDED and
|
||||||
|
// JOYDEVICEREMOVED events.
|
||||||
|
break;
|
||||||
|
|
||||||
|
// Joystick events for non-GameControllers.
|
||||||
|
case SDL_JOYBUTTONDOWN:
|
||||||
|
case SDL_JOYBUTTONUP:
|
||||||
|
{
|
||||||
|
wxSDLJoyState* joy_state = FindJoyState(e.jbutton.which);
|
||||||
|
if (joy_state) {
|
||||||
|
joy_state->ProcessEvent(
|
||||||
|
e.type, e.jbutton.button, e.jbutton.state);
|
||||||
|
}
|
||||||
|
got_event = true;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
case SDL_JOYAXISMOTION:
|
||||||
|
{
|
||||||
|
wxSDLJoyState* joy_state = FindJoyState(e.jaxis.which);
|
||||||
|
if (joy_state) {
|
||||||
|
joy_state->ProcessEvent(
|
||||||
|
e.type, e.jaxis.axis, AxisValueToDirection(e.jaxis.value));
|
||||||
|
}
|
||||||
|
got_event = true;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
case SDL_JOYHATMOTION:
|
||||||
|
{
|
||||||
|
wxSDLJoyState* joy_state = FindJoyState(e.jhat.which);
|
||||||
|
if (joy_state) {
|
||||||
|
joy_state->ProcessEvent(
|
||||||
|
e.type, e.jhat.hat, AxisValueToDirection(e.jhat.value));
|
||||||
|
}
|
||||||
|
got_event = true;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
case SDL_JOYDEVICEADDED:
|
||||||
|
{
|
||||||
|
// Always remap all controllers.
|
||||||
|
RemapControllers();
|
||||||
|
got_event = true;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
case SDL_JOYDEVICEREMOVED:
|
||||||
|
{
|
||||||
|
joystick_states_.erase(e.jdevice.which);
|
||||||
|
got_event = true;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
default:
|
||||||
|
// Ignore all other events.
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
wxLongLong now = wxGetUTCTimeMillis();
|
||||||
|
if (got_event) {
|
||||||
|
last_poll_ = now;
|
||||||
|
} else if (now - last_poll_ > kPollTimeInterval) {
|
||||||
|
for (auto&& joy_state : joystick_states_) {
|
||||||
|
joy_state.second->Poll();
|
||||||
|
}
|
||||||
|
last_poll_ = now;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
SDL_Joystick*& wxSDLJoyDev::operator=(SDL_Joystick* ptr)
|
void wxSDLJoy::RemapControllers() {
|
||||||
{
|
if (!is_polling_active_) {
|
||||||
dev_js = ptr;
|
// Nothing to do when we're not actively polling.
|
||||||
return dev_js;
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
joystick_states_.clear();
|
||||||
|
|
||||||
|
if (requested_sdl_indexes_.empty()) {
|
||||||
|
// Connect all joysticks.
|
||||||
|
for (int i = 0; i < SDL_NumJoysticks(); i++) {
|
||||||
|
std::unique_ptr<wxSDLJoyState> joy_state(new wxSDLJoyState(i));
|
||||||
|
if (joy_state->IsValid()) {
|
||||||
|
joystick_states_.emplace(
|
||||||
|
joy_state->joystick_id(), std::move(joy_state));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
// Only attempt to add the joysticks we care about.
|
||||||
|
for (const int& sdl_index : requested_sdl_indexes_) {
|
||||||
|
std::unique_ptr<wxSDLJoyState> joy_state(
|
||||||
|
new wxSDLJoyState(sdl_index));
|
||||||
|
if (joy_state->IsValid()) {
|
||||||
|
joystick_states_.emplace(
|
||||||
|
joy_state->joystick_id(), std::move(joy_state));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
wxSDLJoyDev::operator bool()
|
wxSDLJoyState* wxSDLJoy::FindJoyState(const SDL_JoystickID& joy_id) {
|
||||||
{
|
const auto iter = joystick_states_.find(joy_id);
|
||||||
return dev_gc != nullptr;
|
if (iter == joystick_states_.end())
|
||||||
|
return nullptr;
|
||||||
|
return iter->second.get();
|
||||||
}
|
}
|
||||||
|
|
||||||
std::nullptr_t& wxSDLJoyDev::operator=(std::nullptr_t&& null_ptr)
|
void wxSDLJoy::PollJoysticks(std::unordered_set<unsigned> indexes) {
|
||||||
{
|
// Reset the polling state.
|
||||||
dev_gc = null_ptr;
|
StopPolling();
|
||||||
return null_ptr;
|
|
||||||
|
if (indexes.empty()) {
|
||||||
|
// Nothing to poll. Return early.
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
is_polling_active_ = true;
|
||||||
|
std::for_each(
|
||||||
|
indexes.begin(), indexes.end(),
|
||||||
|
[&](const unsigned& player_index) {
|
||||||
|
requested_sdl_indexes_.insert(player_index - 1);
|
||||||
|
});
|
||||||
|
RemapControllers();
|
||||||
|
}
|
||||||
|
|
||||||
|
void wxSDLJoy::PollAllJoysticks() {
|
||||||
|
// Reset the polling state.
|
||||||
|
StopPolling();
|
||||||
|
is_polling_active_ = true;
|
||||||
|
RemapControllers();
|
||||||
|
}
|
||||||
|
|
||||||
|
void wxSDLJoy::StopPolling() {
|
||||||
|
joystick_states_.clear();
|
||||||
|
requested_sdl_indexes_.clear();
|
||||||
|
is_polling_active_ = false;
|
||||||
|
}
|
||||||
|
|
||||||
|
void wxSDLJoy::SetRumble(bool activate_rumble) {
|
||||||
|
if (joystick_states_.empty())
|
||||||
|
return;
|
||||||
|
|
||||||
|
// Do rumble only on the first device.
|
||||||
|
joystick_states_.begin()->second->SetRumble(activate_rumble);
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,138 +1,103 @@
|
||||||
#ifndef JOYEVT_H
|
#ifndef JOYEVT_H
|
||||||
#define JOYEVT_H
|
#define JOYEVT_H
|
||||||
|
|
||||||
// This is my own SDL-based joystick handler, since wxJoystick is brain-dead.
|
#include <memory>
|
||||||
// It's geared towards keyboard emulation
|
#include <unordered_set>
|
||||||
|
|
||||||
// To use, create a wxSDLJoy object Add() the joysticks you want to monitor.
|
|
||||||
//
|
|
||||||
// The target window will receive EVT_SDLJOY events of type wxSDLJoyEvent.
|
|
||||||
|
|
||||||
#include <cstddef>
|
|
||||||
#include <array>
|
|
||||||
#include <vector>
|
|
||||||
#include <unordered_map>
|
#include <unordered_map>
|
||||||
#include <wx/time.h>
|
#include <wx/time.h>
|
||||||
#include <wx/event.h>
|
#include <wx/event.h>
|
||||||
#include <wx/timer.h>
|
|
||||||
#include <SDL_joystick.h>
|
#include <SDL_joystick.h>
|
||||||
#include <SDL_gamecontroller.h>
|
#include <SDL_gamecontroller.h>
|
||||||
#include "../common/contains.h"
|
#include <SDL_events.h>
|
||||||
|
|
||||||
struct wxSDLJoyDev {
|
// The different types of supported controls.
|
||||||
private:
|
enum wxSDLControl {
|
||||||
union {
|
WXSDLJOY_AXIS, // Control value is signed 16
|
||||||
SDL_GameController* dev_gc = nullptr;
|
WXSDLJOY_HAT, // Control value is bitmask NESW/URDL
|
||||||
SDL_Joystick* dev_js;
|
WXSDLJOY_BUTTON // Control value is 0 or 1
|
||||||
};
|
};
|
||||||
|
|
||||||
|
// Represents a Joystick event.
|
||||||
|
class wxSDLJoyEvent : public wxCommandEvent {
|
||||||
public:
|
public:
|
||||||
operator SDL_GameController*&();
|
wxSDLJoyEvent(
|
||||||
SDL_GameController*& operator=(SDL_GameController* ptr);
|
unsigned player_index,
|
||||||
|
wxSDLControl control,
|
||||||
|
uint8_t control_index,
|
||||||
|
int16_t control_value);
|
||||||
|
virtual ~wxSDLJoyEvent() = default;
|
||||||
|
|
||||||
operator SDL_Joystick*&();
|
unsigned player_index() const { return player_index_; }
|
||||||
SDL_Joystick*& operator=(SDL_Joystick* ptr);
|
wxSDLControl control() const { return control_; }
|
||||||
|
uint8_t control_index() const { return control_index_; }
|
||||||
|
int16_t control_value() const { return control_value_; }
|
||||||
|
|
||||||
operator bool();
|
private:
|
||||||
|
unsigned player_index_;
|
||||||
std::nullptr_t& operator=(std::nullptr_t&& null_ptr);
|
wxSDLControl control_;
|
||||||
|
uint8_t control_index_;
|
||||||
|
int16_t control_value_;
|
||||||
};
|
};
|
||||||
|
|
||||||
struct wxSDLJoyState {
|
class 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{};
|
|
||||||
};
|
|
||||||
|
|
||||||
class wxSDLJoy : public wxTimer {
|
// This is my own SDL-based joystick handler, since wxJoystick is brain-dead.
|
||||||
|
// It's geared towards keyboard emulation.
|
||||||
|
//
|
||||||
|
// After initilization, use PollJoystick() or PollAllJoysticks() for the
|
||||||
|
// joysticks you wish to monitor. The target window will then receive
|
||||||
|
// EVT_SDLJOY events of type wxSDLJoyEvent.
|
||||||
|
// Handling of the player_index() value is different depending on the polling
|
||||||
|
// mode. After calls to PollJoysticks(), that value will remain constant for a
|
||||||
|
// given device, even if other joysticks disconnect. This ensures the joystick
|
||||||
|
// remains active during gameplay even if other joysticks disconnect.
|
||||||
|
// However, after calls to PollAllJoysticks(), all joysticks are re-connected
|
||||||
|
// on joystick connect/disconnect. This ensures the right player_index() value
|
||||||
|
// is sent to the UI during input event configuration.
|
||||||
|
class wxSDLJoy {
|
||||||
public:
|
public:
|
||||||
wxSDLJoy();
|
wxSDLJoy();
|
||||||
// add another joystick to the list of polled sticks
|
~wxSDLJoy();
|
||||||
// -1 == add all
|
|
||||||
// If joy > # of joysticks, it is ignored
|
|
||||||
// This will start polling if a valid joystick is selected
|
|
||||||
void Add(int8_t joy = -1);
|
|
||||||
// remove a joystick from the polled sticks
|
|
||||||
// -1 == remove all
|
|
||||||
// If joy > # of joysticks, it is ignored
|
|
||||||
// This will stop polling if all joysticks are disabled
|
|
||||||
void Remove(int8_t joy = -1);
|
|
||||||
// query if a stick is being polled
|
|
||||||
bool IsPolling(uint8_t joy) { return contains(joystate, joy); }
|
|
||||||
|
|
||||||
// true = currently rumbling, false = turn off rumbling
|
// Adds a set of joysticks to the list of polled joysticks.
|
||||||
void SetRumble(bool do_rumble);
|
// This will disconnect every active joysticks, and reactivates the ones
|
||||||
|
// matching an index in |indexes|. Missing joysticks will be connected if
|
||||||
|
// they connect later on.
|
||||||
|
void PollJoysticks(std::unordered_set<unsigned> indexes);
|
||||||
|
|
||||||
|
// Adds all joysticks to the list of polled joysticks. This will
|
||||||
|
// disconnect every active joysticks, reconnect them and start polling.
|
||||||
|
void PollAllJoysticks();
|
||||||
|
|
||||||
|
// Removes all joysticks from the list of polled joysticks.
|
||||||
|
// This will stop polling.
|
||||||
|
void StopPolling();
|
||||||
|
|
||||||
|
// Activates or deactivates rumble on active joysticks.
|
||||||
|
void SetRumble(bool activate_rumble);
|
||||||
|
|
||||||
|
// Polls active joysticks and empties the SDL event buffer.
|
||||||
void Poll();
|
void Poll();
|
||||||
|
|
||||||
virtual ~wxSDLJoy();
|
|
||||||
|
|
||||||
protected:
|
|
||||||
// used to continue rumbling on a timer
|
|
||||||
void Notify();
|
|
||||||
void ConnectController(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 = 25;
|
|
||||||
|
|
||||||
private:
|
private:
|
||||||
std::unordered_map<uint8_t, wxSDLJoyState> joystate;
|
// Reconnects all controllers.
|
||||||
std::unordered_map<SDL_JoystickID, wxSDLJoyState*> instance_map;
|
void RemapControllers();
|
||||||
bool add_all = false, rumbling = false;
|
|
||||||
|
|
||||||
wxLongLong last_poll = wxGetUTCTimeMillis();
|
// Helper method to find a joystick state from a joystick ID.
|
||||||
};
|
// Returns nullptr if not present.
|
||||||
|
wxSDLJoyState* FindJoyState(const SDL_JoystickID& joy_id);
|
||||||
|
|
||||||
enum {
|
// Map of SDL joystick ID to joystick state. Only contains active joysticks.
|
||||||
// The types of supported controls
|
std::unordered_map<SDL_JoystickID, std::unique_ptr<wxSDLJoyState>> joystick_states_;
|
||||||
// values are signed-16 for axis, 0/1 for button
|
|
||||||
// hat is bitmask NESW/URDL
|
|
||||||
WXSDLJOY_AXIS,
|
|
||||||
WXSDLJOY_HAT,
|
|
||||||
WXSDLJOY_BUTTON
|
|
||||||
};
|
|
||||||
|
|
||||||
class wxSDLJoyEvent : public wxCommandEvent {
|
// Set of requested SDL joystick indexes.
|
||||||
friend class wxSDLJoy;
|
std::unordered_set<int> requested_sdl_indexes_;
|
||||||
|
|
||||||
public:
|
// Set to true when we are actively polling controllers.
|
||||||
// Default constructor
|
bool is_polling_active_ = false;
|
||||||
wxSDLJoyEvent(wxEventType commandType = wxEVT_NULL)
|
|
||||||
: wxCommandEvent(commandType)
|
|
||||||
{
|
|
||||||
}
|
|
||||||
// accessors
|
|
||||||
unsigned short GetJoy()
|
|
||||||
{
|
|
||||||
return joy;
|
|
||||||
}
|
|
||||||
unsigned short GetControlType()
|
|
||||||
{
|
|
||||||
return ctrl_type;
|
|
||||||
}
|
|
||||||
unsigned short GetControlIndex()
|
|
||||||
{
|
|
||||||
return ctrl_idx;
|
|
||||||
}
|
|
||||||
short GetControlValue()
|
|
||||||
{
|
|
||||||
return ctrl_val;
|
|
||||||
}
|
|
||||||
short GetControlPrevValue()
|
|
||||||
{
|
|
||||||
return prev_val;
|
|
||||||
}
|
|
||||||
|
|
||||||
protected:
|
// Timestamp when the latest poll was done.
|
||||||
unsigned short joy;
|
wxLongLong last_poll_ = wxGetUTCTimeMillis();
|
||||||
unsigned short ctrl_type;
|
|
||||||
unsigned short ctrl_idx;
|
|
||||||
short ctrl_val;
|
|
||||||
short prev_val;
|
|
||||||
};
|
};
|
||||||
|
|
||||||
// Note: this means sdljoy can't be part of a library w/o extra work
|
// Note: this means sdljoy can't be part of a library w/o extra work
|
||||||
|
|
|
@ -109,8 +109,8 @@ static void get_config_path(wxPathList& path, bool exists = true)
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
// config is in $HOME/.vbam/
|
// config is in $HOME/.vbam/
|
||||||
add_nonstandard_path(old_config);
|
add_nonstandard_path(old_config);
|
||||||
}
|
}
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
|
@ -240,7 +240,7 @@ bool wxvbamApp::OnInit()
|
||||||
return false;
|
return false;
|
||||||
|
|
||||||
if (console_mode)
|
if (console_mode)
|
||||||
return true;
|
return true;
|
||||||
|
|
||||||
// prepare for loading xrc files
|
// prepare for loading xrc files
|
||||||
wxXmlResource* xr = wxXmlResource::Get();
|
wxXmlResource* xr = wxXmlResource::Get();
|
||||||
|
@ -449,13 +449,13 @@ bool wxvbamApp::OnInit()
|
||||||
return false;
|
return false;
|
||||||
|
|
||||||
if (x >= 0 && y >= 0 && width > 0 && height > 0)
|
if (x >= 0 && y >= 0 && width > 0 && height > 0)
|
||||||
frame->SetSize(x, y, width, height);
|
frame->SetSize(x, y, width, height);
|
||||||
|
|
||||||
if (isMaximized)
|
if (isMaximized)
|
||||||
frame->Maximize();
|
frame->Maximize();
|
||||||
|
|
||||||
if (isFullscreen && wxGetApp().pending_load != wxEmptyString)
|
if (isFullscreen && wxGetApp().pending_load != wxEmptyString)
|
||||||
frame->ShowFullScreen(isFullscreen);
|
frame->ShowFullScreen(isFullscreen);
|
||||||
frame->Show(true);
|
frame->Show(true);
|
||||||
|
|
||||||
#ifndef NO_ONLINEUPDATES
|
#ifndef NO_ONLINEUPDATES
|
||||||
|
@ -468,12 +468,12 @@ int wxvbamApp::OnRun()
|
||||||
{
|
{
|
||||||
if (console_mode)
|
if (console_mode)
|
||||||
{
|
{
|
||||||
// we could check for our own error codes here...
|
// we could check for our own error codes here...
|
||||||
return console_status;
|
return console_status;
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
return wxApp::OnRun();
|
return wxApp::OnRun();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -512,30 +512,30 @@ void wxvbamApp::OnInitCmdLine(wxCmdLineParser& cl)
|
||||||
static wxCmdLineEntryDesc opttab[] = {
|
static wxCmdLineEntryDesc opttab[] = {
|
||||||
{ wxCMD_LINE_OPTION, NULL, t("save-xrc"),
|
{ wxCMD_LINE_OPTION, NULL, t("save-xrc"),
|
||||||
N_("Save built-in XRC file and exit"),
|
N_("Save built-in XRC file and exit"),
|
||||||
wxCMD_LINE_VAL_STRING, 0 },
|
wxCMD_LINE_VAL_STRING, 0 },
|
||||||
{ wxCMD_LINE_OPTION, NULL, t("save-over"),
|
{ wxCMD_LINE_OPTION, NULL, t("save-over"),
|
||||||
N_("Save built-in vba-over.ini and exit"),
|
N_("Save built-in vba-over.ini and exit"),
|
||||||
wxCMD_LINE_VAL_STRING, 0 },
|
wxCMD_LINE_VAL_STRING, 0 },
|
||||||
{ wxCMD_LINE_SWITCH, NULL, t("print-cfg-path"),
|
{ wxCMD_LINE_SWITCH, NULL, t("print-cfg-path"),
|
||||||
N_("Print configuration path and exit"),
|
N_("Print configuration path and exit"),
|
||||||
wxCMD_LINE_VAL_NONE, 0 },
|
wxCMD_LINE_VAL_NONE, 0 },
|
||||||
{ wxCMD_LINE_SWITCH, t("f"), t("fullscreen"),
|
{ wxCMD_LINE_SWITCH, t("f"), t("fullscreen"),
|
||||||
N_("Start in full-screen mode"),
|
N_("Start in full-screen mode"),
|
||||||
wxCMD_LINE_VAL_NONE, 0 },
|
wxCMD_LINE_VAL_NONE, 0 },
|
||||||
{ wxCMD_LINE_OPTION, t("c"), t("config"),
|
{ wxCMD_LINE_OPTION, t("c"), t("config"),
|
||||||
N_("Set a configuration file"),
|
N_("Set a configuration file"),
|
||||||
wxCMD_LINE_VAL_STRING, 0 },
|
wxCMD_LINE_VAL_STRING, 0 },
|
||||||
#if !defined(NO_LINK) && !defined(__WXMSW__)
|
#if !defined(NO_LINK) && !defined(__WXMSW__)
|
||||||
{ wxCMD_LINE_SWITCH, t("s"), t("delete-shared-state"),
|
{ wxCMD_LINE_SWITCH, t("s"), t("delete-shared-state"),
|
||||||
N_("Delete shared link state first, if it exists"),
|
N_("Delete shared link state first, if it exists"),
|
||||||
wxCMD_LINE_VAL_NONE, 0 },
|
wxCMD_LINE_VAL_NONE, 0 },
|
||||||
#endif
|
#endif
|
||||||
// stupid wx cmd line parser doesn't support duplicate options
|
// stupid wx cmd line parser doesn't support duplicate options
|
||||||
// { wxCMD_LINE_OPTION, t("o"), t("option"),
|
// { wxCMD_LINE_OPTION, t("o"), t("option"),
|
||||||
// _("Set configuration option; <opt>=<value> or help for list"),
|
// _("Set configuration option; <opt>=<value> or help for list"),
|
||||||
{ wxCMD_LINE_SWITCH, t("o"), t("list-options"),
|
{ wxCMD_LINE_SWITCH, t("o"), t("list-options"),
|
||||||
N_("List all settable options and exit"),
|
N_("List all settable options and exit"),
|
||||||
wxCMD_LINE_VAL_NONE, 0 },
|
wxCMD_LINE_VAL_NONE, 0 },
|
||||||
{ wxCMD_LINE_PARAM, NULL, NULL,
|
{ wxCMD_LINE_PARAM, NULL, NULL,
|
||||||
N_("ROM file"), wxCMD_LINE_VAL_STRING, wxCMD_LINE_PARAM_OPTIONAL },
|
N_("ROM file"), wxCMD_LINE_VAL_STRING, wxCMD_LINE_PARAM_OPTIONAL },
|
||||||
{ wxCMD_LINE_PARAM, NULL, NULL,
|
{ wxCMD_LINE_PARAM, NULL, NULL,
|
||||||
|
@ -720,8 +720,8 @@ wxString wxvbamApp::GetDataDir()
|
||||||
wxvbamApp::~wxvbamApp() {
|
wxvbamApp::~wxvbamApp() {
|
||||||
if (home != NULL)
|
if (home != NULL)
|
||||||
{
|
{
|
||||||
free(home);
|
free(home);
|
||||||
home = NULL;
|
home = NULL;
|
||||||
}
|
}
|
||||||
delete overrides;
|
delete overrides;
|
||||||
|
|
||||||
|
@ -797,8 +797,8 @@ void MainFrame::OnMenu(wxContextMenuEvent& event)
|
||||||
wxPoint p(event.GetPosition());
|
wxPoint p(event.GetPosition());
|
||||||
#if 0 // wx actually recommends ignoring the position
|
#if 0 // wx actually recommends ignoring the position
|
||||||
|
|
||||||
if (p != wxDefaultPosition)
|
if (p != wxDefaultPosition)
|
||||||
p = ScreenToClient(p);
|
p = ScreenToClient(p);
|
||||||
|
|
||||||
#endif
|
#endif
|
||||||
PopupMenu(ctx_menu, p);
|
PopupMenu(ctx_menu, p);
|
||||||
|
@ -839,16 +839,16 @@ void MainFrame::OnSize(wxSizeEvent& event)
|
||||||
bool isMaximized = IsMaximized();
|
bool isMaximized = IsMaximized();
|
||||||
if (!isFullscreen && !isMaximized)
|
if (!isFullscreen && !isMaximized)
|
||||||
{
|
{
|
||||||
if (height > 0 && width > 0)
|
if (height > 0 && width > 0)
|
||||||
{
|
{
|
||||||
windowHeight = height;
|
windowHeight = height;
|
||||||
windowWidth = width;
|
windowWidth = width;
|
||||||
}
|
}
|
||||||
if (x >= 0 && y >= 0)
|
if (x >= 0 && y >= 0)
|
||||||
{
|
{
|
||||||
windowPositionX = x;
|
windowPositionX = x;
|
||||||
windowPositionY = y;
|
windowPositionY = y;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
|
@ -876,25 +876,26 @@ int MainFrame::FilterEvent(wxEvent& event)
|
||||||
evh.SetEventObject(this);
|
evh.SetEventObject(this);
|
||||||
GetEventHandler()->ProcessEvent(evh);
|
GetEventHandler()->ProcessEvent(evh);
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
else if (event.GetEventType() == wxEVT_SDLJOY && !menus_opened && !dialog_opened)
|
else if (event.GetEventType() == wxEVT_SDLJOY && !menus_opened && !dialog_opened)
|
||||||
{
|
{
|
||||||
wxSDLJoyEvent& je = (wxSDLJoyEvent&)event;
|
wxSDLJoyEvent& je = (wxSDLJoyEvent&)event;
|
||||||
if (je.GetControlValue() == 0) return -1; // joystick button UP
|
if (je.control_value() == 0) return -1; // joystick button UP
|
||||||
int key = je.GetControlIndex();
|
uint8_t key = je.control_index();
|
||||||
int mod = wxJoyKeyTextCtrl::DigitalButton(je);
|
int mod = wxJoyKeyTextCtrl::DigitalButton(je);
|
||||||
int joy = je.GetJoy() + 1;
|
int joy = je.player_index();
|
||||||
wxString label = wxJoyKeyTextCtrl::ToString(mod, key, joy);
|
wxString label = wxJoyKeyTextCtrl::ToString(mod, key, joy);
|
||||||
wxAcceleratorEntry_v accels = wxGetApp().GetAccels();
|
wxAcceleratorEntry_v accels = wxGetApp().GetAccels();
|
||||||
for (size_t i = 0; i < accels.size(); ++i) {
|
for (size_t i = 0; i < accels.size(); ++i)
|
||||||
if (label == accels[i].GetUkey())
|
{
|
||||||
{
|
if (label == accels[i].GetUkey())
|
||||||
wxCommandEvent evh(wxEVT_COMMAND_MENU_SELECTED, accels[i].GetCommand());
|
{
|
||||||
evh.SetEventObject(this);
|
wxCommandEvent evh(wxEVT_COMMAND_MENU_SELECTED, accels[i].GetCommand());
|
||||||
GetEventHandler()->ProcessEvent(evh);
|
evh.SetEventObject(this);
|
||||||
return true;
|
GetEventHandler()->ProcessEvent(evh);
|
||||||
}
|
return true;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
return -1;
|
return -1;
|
||||||
|
@ -916,8 +917,8 @@ wxString MainFrame::GetGamePath(wxString path)
|
||||||
|
|
||||||
if (!wxIsWritable(game_path))
|
if (!wxIsWritable(game_path))
|
||||||
{
|
{
|
||||||
game_path = wxGetApp().GetAbsolutePath(wxString((get_xdg_user_data_home() + DOT_DIR).c_str(), wxConvLibc));
|
game_path = wxGetApp().GetAbsolutePath(wxString((get_xdg_user_data_home() + DOT_DIR).c_str(), wxConvLibc));
|
||||||
wxFileName::Mkdir(game_path, 0777, wxPATH_MKDIR_FULL);
|
wxFileName::Mkdir(game_path, 0777, wxPATH_MKDIR_FULL);
|
||||||
}
|
}
|
||||||
|
|
||||||
return game_path;
|
return game_path;
|
||||||
|
@ -927,23 +928,26 @@ void MainFrame::SetJoystick()
|
||||||
{
|
{
|
||||||
/* Remove all attached joysticks to avoid errors while
|
/* Remove all attached joysticks to avoid errors while
|
||||||
* destroying and creating the GameArea `panel`. */
|
* destroying and creating the GameArea `panel`. */
|
||||||
joy.Remove();
|
joy.StopPolling();
|
||||||
|
|
||||||
set_global_accels();
|
set_global_accels();
|
||||||
|
|
||||||
if (!emulating)
|
if (!emulating)
|
||||||
return;
|
return;
|
||||||
|
|
||||||
for (int i = 0; i < 4; i++)
|
std::unordered_set<unsigned> needed_joysticks;
|
||||||
|
for (int i = 0; i < 4; i++) {
|
||||||
for (int j = 0; j < NUM_KEYS; j++) {
|
for (int j = 0; j < NUM_KEYS; j++) {
|
||||||
wxJoyKeyBinding_v b = gopts.joykey_bindings[i][j];
|
wxJoyKeyBinding_v b = gopts.joykey_bindings[i][j];
|
||||||
for (size_t k = 0; k < b.size(); k++) {
|
for (size_t k = 0; k < b.size(); k++) {
|
||||||
int jn = b[k].joy;
|
int jn = b[k].joy;
|
||||||
if (jn) {
|
if (jn) {
|
||||||
joy.Add(jn - 1);
|
needed_joysticks.insert(jn);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
joy.PollJoysticks(needed_joysticks);
|
||||||
}
|
}
|
||||||
|
|
||||||
void MainFrame::StopJoyPollTimer()
|
void MainFrame::StopJoyPollTimer()
|
||||||
|
|
Loading…
Reference in New Issue