diff --git a/po/wxvbam/wxvbam.pot b/po/wxvbam/wxvbam.pot index 55c6e73b..3bc57a52 100644 --- a/po/wxvbam/wxvbam.pot +++ b/po/wxvbam/wxvbam.pot @@ -8,7 +8,7 @@ msgid "" msgstr "" "Project-Id-Version: PACKAGE VERSION\n" "Report-Msgid-Bugs-To: \n" -"POT-Creation-Date: 2020-04-27 20:23+0000\n" +"POT-Creation-Date: 2020-05-04 01:24+0000\n" "PO-Revision-Date: YEAR-MO-DA HO:MI+ZONE\n" "Last-Translator: FULL NAME \n" "Language-Team: LANGUAGE \n" @@ -831,11 +831,11 @@ msgid "" "along with this program. If not, see http://www.gnu.org/licenses ." msgstr "" -#: cmdevents.cpp:3069 +#: cmdevents.cpp:3076 msgid "LAN link is already active. Disable link mode to disconnect." msgstr "" -#: cmdevents.cpp:3075 +#: cmdevents.cpp:3082 msgid "Network is not supported in local mode." msgstr "" @@ -1016,78 +1016,78 @@ msgstr "" msgid "Not a valid GBA cartridge" msgstr "" -#: panel.cpp:1146 +#: panel.cpp:1150 msgid "No memory for rewinding" msgstr "" -#: panel.cpp:1156 +#: panel.cpp:1160 msgid "Error writing rewind state" msgstr "" -#: panel.cpp:2253 +#: panel.cpp:2257 msgid "Failed to set glXSwapIntervalEXT" msgstr "" -#: panel.cpp:2262 +#: panel.cpp:2266 msgid "Failed to set glXSwapIntervalSGI" msgstr "" -#: panel.cpp:2271 +#: panel.cpp:2275 msgid "Failed to set glXSwapIntervalMESA" msgstr "" -#: panel.cpp:2278 +#: panel.cpp:2282 msgid "No support for wglGetExtensionsString" msgstr "" -#: panel.cpp:2280 +#: panel.cpp:2284 msgid "No support for WGL_EXT_swap_control" msgstr "" -#: panel.cpp:2289 +#: panel.cpp:2293 msgid "Failed to set wglSwapIntervalEXT" msgstr "" -#: panel.cpp:2295 +#: panel.cpp:2299 msgid "No VSYNC available on this platform" msgstr "" -#: panel.cpp:2391 +#: panel.cpp:2395 msgid "memory allocation error" msgstr "" -#: panel.cpp:2394 +#: panel.cpp:2398 msgid "error initializing codec" msgstr "" -#: panel.cpp:2397 +#: panel.cpp:2401 msgid "error writing to output file" msgstr "" -#: panel.cpp:2400 +#: panel.cpp:2404 msgid "can't guess output format from file name" msgstr "" -#: panel.cpp:2405 +#: panel.cpp:2409 msgid "programming error; aborting!" msgstr "" -#: panel.cpp:2417 panel.cpp:2446 +#: panel.cpp:2421 panel.cpp:2450 #, c-format msgid "Unable to begin recording to %s (%s)" msgstr "" -#: panel.cpp:2474 +#: panel.cpp:2478 #, c-format msgid "Error in audio/video recording (%s); aborting" msgstr "" -#: panel.cpp:2480 +#: panel.cpp:2484 #, c-format msgid "Error in audio recording (%s); aborting" msgstr "" -#: panel.cpp:2490 +#: panel.cpp:2494 #, c-format msgid "Error in video recording (%s); aborting" msgstr "" @@ -1148,16 +1148,26 @@ msgstr "" msgid "CONTROL" msgstr "" -#: widgets/sdljoy.cpp:115 +#: widgets/sdljoy.cpp:129 #, c-format msgid "Connected game controller %d" msgstr "" -#: widgets/sdljoy.cpp:129 +#: widgets/sdljoy.cpp:143 #, c-format msgid "Disconnected game controller %d" msgstr "" +#: widgets/sdljoy.cpp:229 +#, c-format +msgid "Connected joystick %d" +msgstr "" + +#: widgets/sdljoy.cpp:246 +#, c-format +msgid "Disconnected joystick %d" +msgstr "" + #: xaudio2.cpp:35 msgid "XAudio2: Enumerating devices failed!" msgstr "" diff --git a/src/wx/widgets/joyedit.cpp b/src/wx/widgets/joyedit.cpp index 437dbb8a..6376228e 100644 --- a/src/wx/widgets/joyedit.cpp +++ b/src/wx/widgets/joyedit.cpp @@ -68,7 +68,18 @@ int wxJoyKeyTextCtrl::DigitalButton(wxSDLJoyEvent& event) void wxJoyKeyTextCtrl::OnJoy(wxSDLJoyEvent& event) { - short val = event.GetControlValue(); + static wxLongLong last_event = 0; + + int val = event.GetControlValue(); + int type = event.GetControlType(); + + // Filter consecutive axis motions within 300ms, as this adds two bindings + // +1/-1 instead of the one intended. + if (type == WXSDLJOY_AXIS && wxGetUTCTimeMillis() - last_event < 300) + return; + + last_event = wxGetUTCTimeMillis(); + int mod = DigitalButton(event); int key = event.GetControlIndex(), joy = event.GetJoy() + 1; diff --git a/src/wx/widgets/sdljoy.cpp b/src/wx/widgets/sdljoy.cpp index 1c1a144c..444f3a64 100644 --- a/src/wx/widgets/sdljoy.cpp +++ b/src/wx/widgets/sdljoy.cpp @@ -1,3 +1,4 @@ +#include #include "wxvbam.h" #include "wx/sdljoy.h" #include "SDL.h" @@ -8,6 +9,9 @@ using namespace Range; +// For testing a GameController as a Joystick: +//#define SDL_IsGameController(x) false + DEFINE_EVENT_TYPE(wxEVT_SDLJOY) wxSDLJoy::wxSDLJoy() @@ -16,8 +20,10 @@ wxSDLJoy::wxSDLJoy() { // Start up joystick if not already started // FIXME: check for errors + SDL_Init(SDL_INIT_JOYSTICK); SDL_Init(SDL_INIT_GAMECONTROLLER); SDL_GameControllerEventState(SDL_ENABLE); + SDL_JoystickEventState(SDL_ENABLE); } wxSDLJoy::~wxSDLJoy() @@ -49,6 +55,9 @@ void wxSDLJoy::Poll() { auto joy = e.cbutton.which; + if (!SDL_IsGameController(joy)) + break; + if (contains(joystate, joy)) { auto but = e.cbutton.button; auto val = e.cbutton.state; @@ -78,6 +87,9 @@ void wxSDLJoy::Poll() { auto joy = e.caxis.which; + if (!SDL_IsGameController(joy)) + break; + if (contains(joystate, joy)) { auto axis = e.caxis.axis; auto val = axisval(e.caxis.value); @@ -108,6 +120,9 @@ void wxSDLJoy::Poll() { auto joy = e.cdevice.which; + if (!SDL_IsGameController(joy)) + break; + if (add_all || contains(joystate, joy)) { DisconnectController(joy); ConnectController(joy); @@ -133,11 +148,112 @@ void wxSDLJoy::Poll() break; } + + // Joystck events for non-GameControllers. + + case SDL_JOYBUTTONDOWN: + case SDL_JOYBUTTONUP: + { + auto joy = e.jbutton.which; + + if (SDL_IsGameController(joy)) + break; + + if (contains(joystate, joy)) { + auto but = e.jbutton.button; + auto val = e.jbutton.state; + auto prev_val = joystate[joy].button[but]; + + if (handler && val != prev_val) { + wxSDLJoyEvent ev(wxEVT_SDLJOY); + ev.joy = joy; + ev.ctrl_type = WXSDLJOY_BUTTON; + ev.ctrl_idx = but; + ev.ctrl_val = val; + ev.prev_val = prev_val; + + handler->ProcessEvent(ev); + } + + joystate[joy].button[but] = val; + + wxLogDebug("GOT SDL_JOYBUTTON: joy:%d but:%d val:%d prev_val:%d", joy, but, val, prev_val); + } + + got_event = true; + + break; + } + case SDL_JOYAXISMOTION: + { + auto joy = e.jaxis.which; + + if (SDL_IsGameController(joy)) + break; + + if (contains(joystate, joy)) { + auto axis = e.jaxis.axis; + auto val = axisval(e.jaxis.value); + auto prev_val = joystate[joy].axis[axis]; + + if (handler && val != prev_val) { + wxSDLJoyEvent ev(wxEVT_SDLJOY); + ev.joy = joy; + ev.ctrl_type = WXSDLJOY_AXIS; + ev.ctrl_idx = axis; + ev.ctrl_val = val; + ev.prev_val = prev_val; + + handler->ProcessEvent(ev); + + joystate[joy].axis[axis] = val; + + wxLogDebug("GOT SDL_JOYAXISMOTION: joy:%d axis:%d val:%d prev_val:%d", joy, axis, val, prev_val); + } + } + + got_event = true; + + break; + } + case SDL_JOYDEVICEADDED: + { + auto joy = e.cdevice.which; + + if (SDL_IsGameController(joy)) + break; + + if (add_all || contains(joystate, joy)) { + DisconnectController(joy); + ConnectController(joy); + + systemScreenMessage(wxString::Format(_("Connected joystick %d"), joy + 1)); + } + + got_event = true; + + break; + } + case SDL_JOYDEVICEREMOVED: + { + auto joy = e.cdevice.which; + + if (SDL_IsGameController(joy)) + break; + + if (contains(joystate, joy)) { + DisconnectController(joy); + + systemScreenMessage(wxString::Format(_("Disconnected joystick %d"), joy + 1)); + } + + got_event = true; + + break; + } } } - // I am only doing this shit because hotplug support is fucking broken in SDL right now. - bool do_poll = false; wxLongLong tm = wxGetUTCTimeMillis(); @@ -152,45 +268,90 @@ void wxSDLJoy::Poll() for (auto&& joy : joystate) { if (!joy.second.dev) continue; - 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(but)); + 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(but)); - if (last_state != state) { - if (handler) { + 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(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_BUTTON; - ev.ctrl_idx = but; - ev.ctrl_val = state; - ev.prev_val = last_state; + 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); } - - joy.second.button[but] = state; - - wxLogDebug("POLLED SDL_CONTROLLERBUTTON: joy:%d but:%d val:%d prev_val:%d", joy.first, but, state, last_state); } } + 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); - for (uint8_t axis = 0; axis < SDL_CONTROLLER_AXIS_MAX; axis++) { - auto val = axisval(SDL_GameControllerGetAxis(joy.second.dev, static_cast(axis))); - auto prev_val = joy.second.axis[axis]; + 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; - 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); + } - handler->ProcessEvent(ev); + joy.second.button[but] = state; - joy.second.axis[axis] = val; + wxLogDebug("POLLED SDL_JOYBUTTON: joy:%d but:%d val:%d prev_val:%d", joy.first, but, state, last_state); + } + } - wxLogDebug("POLLED SDL_CONTROLLERAXISMOTION: joy:%d axis:%d val:%d prev_val:%d", joy.first, axis, val, prev_val); + 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 (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_JOYAXISMOTION: joy:%d axis:%d val:%d prev_val:%d", joy.first, axis, val, prev_val); + } } } } @@ -199,15 +360,31 @@ void wxSDLJoy::Poll() void wxSDLJoy::ConnectController(uint8_t joy) { - if (!(joystate[joy].dev = SDL_GameControllerOpen(joy))) - wxLogDebug("SDL_GameControllerOpen(%d) failed: %s", joy, SDL_GetError()); + if (SDL_IsGameController(joy)) { + if (!(joystate[joy].dev = SDL_GameControllerOpen(joy))) { + wxLogDebug("SDL_GameControllerOpen(%d) failed: %s", joy, SDL_GetError()); + return; + } + } + else { + if (!(joystate[joy].dev = SDL_JoystickOpen(joy))) { + wxLogDebug("SDL_JoystickOpen(%d) failed: %s", joy, SDL_GetError()); + return; + } + } } void wxSDLJoy::DisconnectController(uint8_t joy) { if (auto& dev = joystate[joy].dev) { - if (SDL_GameControllerGetAttached(dev)) - SDL_GameControllerClose(dev); + if (SDL_IsGameController(joy)) { + if (SDL_GameControllerGetAttached(dev)) + SDL_GameControllerClose(dev); + } + else { + if (SDL_JoystickGetAttached(dev)) + SDL_JoystickClose(dev); + } dev = nullptr; } @@ -256,9 +433,9 @@ void wxSDLJoy::SetRumble(bool do_rumble) rumbling = do_rumble; #if SDL_VERSION_ATLEAST(2, 0, 9) - // do rumble only on device 0 + // Do rumble only on device 0, and only if it's a GameController. auto dev = joystate[0].dev; - if (dev) { + if (dev && SDL_IsGameController(0)) { if (rumbling) { SDL_GameControllerRumble(dev, 0xFFFF, 0xFFFF, 300); if (!IsRunning()) @@ -269,8 +446,6 @@ void wxSDLJoy::SetRumble(bool do_rumble) Stop(); } } - else - Stop(); #endif } @@ -278,3 +453,37 @@ void wxSDLJoy::Notify() { SetRumble(rumbling); } + +wxSDLJoyDev::operator SDL_GameController*&() +{ + return dev_gc; +} + +SDL_GameController*& wxSDLJoyDev::operator=(SDL_GameController* ptr) +{ + dev_gc = ptr; + return dev_gc; +} + + +wxSDLJoyDev::operator SDL_Joystick*&() +{ + return dev_js; +} + +SDL_Joystick*& wxSDLJoyDev::operator=(SDL_Joystick* ptr) +{ + dev_js = ptr; + return dev_js; +} + +wxSDLJoyDev::operator bool() +{ + return dev_gc != nullptr; +} + +std::nullptr_t& wxSDLJoyDev::operator=(std::nullptr_t&& null_ptr) +{ + dev_gc = null_ptr; + return null_ptr; +} diff --git a/src/wx/widgets/wx/sdljoy.h b/src/wx/widgets/wx/sdljoy.h index 82551aa2..0fc7beb6 100644 --- a/src/wx/widgets/wx/sdljoy.h +++ b/src/wx/widgets/wx/sdljoy.h @@ -9,19 +9,39 @@ // // The target window will receive EVT_SDLJOY events of type wxSDLJoyEvent. +#include #include #include #include #include #include #include +#include #include #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); + + operator SDL_Joystick*&(); + SDL_Joystick*& operator=(SDL_Joystick* ptr); + + operator bool(); + + std::nullptr_t& operator=(std::nullptr_t&& null_ptr); +}; + struct wxSDLJoyState { - SDL_GameController* dev = nullptr; - std::array axis{}; - std::array button{}; + wxSDLJoyDev dev; + std::unordered_map axis{}; + std::unordered_map button{}; }; class wxSDLJoy : public wxTimer {