Joystick handling refactor.

Use the SDL GameController API with SDL events instead of polling
manually. Run SDL_PollEvent() in the panel OnIdle().

The API to the GUI remains the same, the sending of wxSDLJoyEvent
events. Except there are no more hat events, we just use the
GameController buttons. Also the GUI now has to make arrangements for
wxSDLJoy::Poll() to be called periodically, for the config dialog this
is done with a timer.

All Xbox 360 controller buttons and axes are now in the defaults, and
the SDL GameController API will map them to the appropriate keys on
other controllers.

As a consequence of using SDL events, controller attach/detach from the
system is now also handled correctly. It is no longer necessary to have
the controller attached and turned on when the program launches.

Signed-off-by: Rafael Kitover <rkitover@gmail.com>
This commit is contained in:
Rafael Kitover 2019-11-21 16:37:03 +00:00
parent d458e75ca6
commit 02520fb63e
14 changed files with 1067 additions and 440 deletions

4
.gitignore vendored
View File

@ -4,9 +4,7 @@ src/wx/cmd-evtable.h
src/wx/cmdhandlers.h
src/wx/cmdtab.cpp
src/wx/wxvbam.xrs
build/*
build32/*
vsbuild/*
*build*/*
cmake-build*
dependencies/*
vcpkg/*

View File

@ -8,7 +8,7 @@ msgid ""
msgstr ""
"Project-Id-Version: PACKAGE VERSION\n"
"Report-Msgid-Bugs-To: \n"
"POT-Creation-Date: 2019-09-06 19:39-0300\n"
"POT-Creation-Date: 2019-11-22 23:25+0000\n"
"PO-Revision-Date: YEAR-MO-DA HO:MI+ZONE\n"
"Last-Translator: FULL NAME <EMAIL@ADDRESS>\n"
"Language-Team: LANGUAGE <LL@li.org>\n"
@ -166,12 +166,12 @@ msgstr ""
msgid "ROM+HuC-1"
msgstr ""
#: ../src/wx/cmdevents.cpp:746 ../src/wx/guiinit.cpp:1829
#: ../src/wx/cmdevents.cpp:746 ../src/wx/guiinit.cpp:1839
#: ../src/wx/xrc/DisplayConfig.xrc:85 ../src/wx/xrc/DisplayConfig.xrc:135
#: ../src/wx/xrc/DisplayConfig.xrc:221
#: ../src/wx/xrc/GameBoyAdvanceConfig.xrc:32
#: ../src/wx/xrc/GameBoyAdvanceConfig.xrc:204
#: ../src/wx/xrc/GeneralConfig.xrc:152 ../src/wx/xrc/SoundConfig.xrc:219
#: ../src/wx/xrc/GeneralConfig.xrc:114 ../src/wx/xrc/SoundConfig.xrc:219
#: ../src/wx/xrc/SoundConfig.xrc:309 ../src/wx/xrc/SpeedupConfig.xrc:45
#: ../src/wx/xrc/SpeedupConfig.xrc:120
msgid "None"
@ -204,7 +204,7 @@ msgstr ""
msgid "Confirm import"
msgstr ""
#: ../src/wx/cmdevents.cpp:898 ../src/wx/panel.cpp:344
#: ../src/wx/cmdevents.cpp:898 ../src/wx/panel.cpp:350
#, c-format
msgid "Loaded battery %s"
msgstr ""
@ -287,7 +287,7 @@ msgstr ""
msgid "Wrote battery %s"
msgstr ""
#: ../src/wx/cmdevents.cpp:1091 ../src/wx/panel.cpp:651
#: ../src/wx/cmdevents.cpp:1091 ../src/wx/panel.cpp:656
#, c-format
msgid "Error writing battery %s"
msgstr ""
@ -405,18 +405,18 @@ msgstr ""
msgid "Using interframe blending #%d"
msgstr ""
#: ../src/wx/cmdevents.cpp:2747
#: ../src/wx/cmdevents.cpp:2752
msgid "Nintendo GameBoy (+Color+Advance) emulator."
msgstr ""
#: ../src/wx/cmdevents.cpp:2748
#: ../src/wx/cmdevents.cpp:2753
msgid ""
"Copyright (C) 1999-2003 Forgotten\n"
"Copyright (C) 2004-2006 VBA development team\n"
"Copyright (C) 2007-2017 VBA-M development team"
msgstr ""
#: ../src/wx/cmdevents.cpp:2749
#: ../src/wx/cmdevents.cpp:2754
msgid ""
"This program is free software: you can redistribute it and/or modify\n"
"it under the terms of the GNU General Public License as published by\n"
@ -432,11 +432,11 @@ msgid ""
"along with this program. If not, see http://www.gnu.org/licenses ."
msgstr ""
#: ../src/wx/cmdevents.cpp:3011
#: ../src/wx/cmdevents.cpp:3018
msgid "LAN link is already active. Disable link mode to disconnect."
msgstr ""
#: ../src/wx/cmdevents.cpp:3017
#: ../src/wx/cmdevents.cpp:3024
msgid "Network is not supported in local mode."
msgstr ""
@ -554,7 +554,7 @@ msgstr ""
msgid "VBA cheat lists (*.clt)|*.clt|CHT cheat lists (*.cht)|*.cht"
msgstr ""
#: ../src/wx/guiinit.cpp:239 ../src/wx/panel.cpp:401
#: ../src/wx/guiinit.cpp:239 ../src/wx/panel.cpp:407
msgid "Loaded cheats"
msgstr ""
@ -640,122 +640,122 @@ msgstr ""
msgid "Default device"
msgstr ""
#: ../src/wx/guiinit.cpp:1709
#: ../src/wx/guiinit.cpp:1719
msgid "Desktop mode"
msgstr ""
#: ../src/wx/guiinit.cpp:1716
#: ../src/wx/guiinit.cpp:1726
#, c-format
msgid "%d x %d - %dbpp @ %dHz"
msgstr ""
#: ../src/wx/guiinit.cpp:1870
#: ../src/wx/guiinit.cpp:1880
#, c-format
msgid "No usable rpi plugins found in %s"
msgstr ""
#: ../src/wx/guiinit.cpp:1890 ../src/wx/xrc/DisplayConfig.xrc:107
#: ../src/wx/guiinit.cpp:1900 ../src/wx/xrc/DisplayConfig.xrc:107
msgid "Plugin"
msgstr ""
#: ../src/wx/guiinit.cpp:1918
#: ../src/wx/guiinit.cpp:1928
msgid "Please select a plugin or a different filter"
msgstr ""
#: ../src/wx/guiinit.cpp:1919
#: ../src/wx/guiinit.cpp:1929
msgid "Plugin selection error"
msgstr ""
#: ../src/wx/guiinit.cpp:2118
#: ../src/wx/guiinit.cpp:2128
msgid "This will clear all user-defined accelerators. Are you sure?"
msgstr ""
#: ../src/wx/guiinit.cpp:2118
#: ../src/wx/guiinit.cpp:2128
msgid "Confirm"
msgstr ""
#: ../src/wx/guiinit.cpp:2712
#: ../src/wx/guiinit.cpp:2722
msgid "Main icon not found"
msgstr ""
#: ../src/wx/guiinit.cpp:2722
#: ../src/wx/guiinit.cpp:2732
msgid "Browse"
msgstr ""
#: ../src/wx/guiinit.cpp:2736
#: ../src/wx/guiinit.cpp:2746
msgid "Main display panel not found"
msgstr ""
#: ../src/wx/guiinit.cpp:2875
#: ../src/wx/guiinit.cpp:2895
#, c-format
msgid "Duplicate menu accelerator: %s for %s and %s; keeping first"
msgstr ""
#: ../src/wx/guiinit.cpp:2889
#: ../src/wx/guiinit.cpp:2909
#, c-format
msgid "Menu accelerator %s for %s overrides default for %s ; keeping menu"
msgstr ""
#: ../src/wx/guiinit.cpp:3013
#: ../src/wx/guiinit.cpp:3039
#, c-format
msgid "Invalid menu item %s; removing"
msgstr ""
#: ../src/wx/guiinit.cpp:3208
#: ../src/wx/guiinit.cpp:3234
msgid "Code"
msgstr ""
#: ../src/wx/guiinit.cpp:3217
#: ../src/wx/guiinit.cpp:3243
msgid "Description"
msgstr ""
#: ../src/wx/guiinit.cpp:3291 ../src/wx/xrc/CheatAdd.xrc:31
#: ../src/wx/guiinit.cpp:3317 ../src/wx/xrc/CheatAdd.xrc:31
msgid "Address"
msgstr ""
#: ../src/wx/guiinit.cpp:3292
#: ../src/wx/guiinit.cpp:3318
msgid "Old Value"
msgstr ""
#: ../src/wx/guiinit.cpp:3293
#: ../src/wx/guiinit.cpp:3319
msgid "New Value"
msgstr ""
#: ../src/wx/guiinit.cpp:3836
#: ../src/wx/guiinit.cpp:3839
msgid "Menu commands"
msgstr ""
#: ../src/wx/guiinit.cpp:3859
#: ../src/wx/guiinit.cpp:3862
msgid "Other commands"
msgstr ""
#: ../src/wx/guiinit.cpp:3970
#: ../src/wx/guiinit.cpp:3973
msgid "JoyBus host invalid; disabling"
msgstr ""
#: ../src/wx/opts.cpp:555 ../src/wx/opts.cpp:851
#: ../src/wx/opts.cpp:548 ../src/wx/opts.cpp:849
#, c-format
msgid "Invalid value %s for option %s; valid values are %s%s%s"
msgstr ""
#: ../src/wx/opts.cpp:572 ../src/wx/opts.cpp:863
#: ../src/wx/opts.cpp:565 ../src/wx/opts.cpp:861
#, c-format
msgid "Invalid value %d for option %s; valid values are %d - %d"
msgstr ""
#: ../src/wx/opts.cpp:579 ../src/wx/opts.cpp:588 ../src/wx/opts.cpp:871
#: ../src/wx/opts.cpp:879
#: ../src/wx/opts.cpp:572 ../src/wx/opts.cpp:581 ../src/wx/opts.cpp:869
#: ../src/wx/opts.cpp:877
#, c-format
msgid "Invalid value %f for option %s; valid values are %f - %f"
msgstr ""
#: ../src/wx/opts.cpp:648 ../src/wx/opts.cpp:669 ../src/wx/opts.cpp:948
#: ../src/wx/opts.cpp:974
#: ../src/wx/opts.cpp:641 ../src/wx/opts.cpp:662 ../src/wx/opts.cpp:946
#: ../src/wx/opts.cpp:972
#, c-format
msgid "Invalid key binding %s for %s"
msgstr ""
#: ../src/wx/opts.cpp:834
#: ../src/wx/opts.cpp:832
#, c-format
msgid "Invalid flag option %s - %s ignored"
msgstr ""
@ -774,7 +774,7 @@ msgstr ""
msgid "Unable to load Game Boy ROM %s"
msgstr ""
#: ../src/wx/panel.cpp:185 ../src/wx/panel.cpp:275
#: ../src/wx/panel.cpp:185 ../src/wx/panel.cpp:281
#, c-format
msgid "Could not load BIOS %s"
msgstr ""
@ -784,103 +784,103 @@ msgstr ""
msgid "Unable to load Game Boy Advance ROM %s"
msgstr ""
#: ../src/wx/panel.cpp:434
#: ../src/wx/panel.cpp:439
msgid " player "
msgstr ""
#: ../src/wx/panel.cpp:599
#: ../src/wx/panel.cpp:604
#, c-format
msgid "Loaded state %s"
msgstr ""
#: ../src/wx/panel.cpp:599
#: ../src/wx/panel.cpp:604
#, c-format
msgid "Error loading state %s"
msgstr ""
#: ../src/wx/panel.cpp:623
#: ../src/wx/panel.cpp:628
#, c-format
msgid "Saved state %s"
msgstr ""
#: ../src/wx/panel.cpp:623
#: ../src/wx/panel.cpp:628
#, c-format
msgid "Error saving state %s"
msgstr ""
#: ../src/wx/panel.cpp:827
#: ../src/wx/panel.cpp:832
#, c-format
msgid "Fullscreen mode %dx%d-%d@%d not supported; looking for another"
msgstr ""
#: ../src/wx/panel.cpp:865
#: ../src/wx/panel.cpp:870
#, c-format
msgid "Fullscreen mode %dx%d-%d@%d not supported"
msgstr ""
#: ../src/wx/panel.cpp:870
#: ../src/wx/panel.cpp:875
#, c-format
msgid "Valid mode: %dx%d-%d@%d"
msgstr ""
#: ../src/wx/panel.cpp:878
#: ../src/wx/panel.cpp:883
#, c-format
msgid "Chose mode %dx%d-%d@%d"
msgstr ""
#: ../src/wx/panel.cpp:882
#: ../src/wx/panel.cpp:887
#, c-format
msgid "Failed to change mode to %dx%d-%d@%d"
msgstr ""
#: ../src/wx/panel.cpp:966
#: ../src/wx/panel.cpp:971
msgid "Not a valid GBA cartridge"
msgstr ""
#: ../src/wx/panel.cpp:1107
#: ../src/wx/panel.cpp:1113
msgid "No memory for rewinding"
msgstr ""
#: ../src/wx/panel.cpp:1117
#: ../src/wx/panel.cpp:1123
msgid "Error writing rewind state"
msgstr ""
#: ../src/wx/panel.cpp:2329
#: ../src/wx/panel.cpp:2334
msgid "memory allocation error"
msgstr ""
#: ../src/wx/panel.cpp:2332
#: ../src/wx/panel.cpp:2337
msgid "error initializing codec"
msgstr ""
#: ../src/wx/panel.cpp:2335
#: ../src/wx/panel.cpp:2340
msgid "error writing to output file"
msgstr ""
#: ../src/wx/panel.cpp:2338
#: ../src/wx/panel.cpp:2343
msgid "can't guess output format from file name"
msgstr ""
#: ../src/wx/panel.cpp:2343
#: ../src/wx/panel.cpp:2348
msgid "programming error; aborting!"
msgstr ""
#: ../src/wx/panel.cpp:2360 ../src/wx/panel.cpp:2391
#: ../src/wx/panel.cpp:2365 ../src/wx/panel.cpp:2396
#, c-format
msgid "Unable to begin recording to %s (%s)"
msgstr ""
#: ../src/wx/panel.cpp:2419
#: ../src/wx/panel.cpp:2424
#, c-format
msgid "Error in audio/video recording (%s); aborting"
msgstr ""
#: ../src/wx/panel.cpp:2425
#: ../src/wx/panel.cpp:2430
#, c-format
msgid "Error in audio recording (%s); aborting"
msgstr ""
#: ../src/wx/panel.cpp:2435
#: ../src/wx/panel.cpp:2440
#, c-format
msgid "Error in video recording (%s); aborting"
msgstr ""
@ -925,37 +925,37 @@ msgstr ""
msgid "%d%%"
msgstr ""
#: ../src/wx/sys.cpp:757 ../src/wx/xrc/GBPrinter.xrc:65
#: ../src/wx/sys.cpp:760 ../src/wx/xrc/GBPrinter.xrc:65
msgid "&Discard"
msgstr ""
#: ../src/wx/sys.cpp:791
#: ../src/wx/sys.cpp:794
msgid "Image files (*.bmp;*.jpg;*.png)|*.bmp;*.jpg;*.png|"
msgstr ""
#: ../src/wx/sys.cpp:800
#: ../src/wx/sys.cpp:803
msgid "Save printer image to"
msgstr ""
#: ../src/wx/sys.cpp:814 ../src/wx/sys.cpp:997
#: ../src/wx/sys.cpp:817 ../src/wx/sys.cpp:1000
#, c-format
msgid "Wrote printer output to %s"
msgstr ""
#: ../src/wx/sys.cpp:819 ../src/wx/sys.cpp:890
#: ../src/wx/sys.cpp:822 ../src/wx/sys.cpp:893
msgid "&Close"
msgstr ""
#: ../src/wx/sys.cpp:885
#: ../src/wx/sys.cpp:888
msgid "Printed"
msgstr ""
#: ../src/wx/sys.cpp:1187
#: ../src/wx/sys.cpp:1190
#, c-format
msgid "Error opening pseudo tty: %s"
msgstr ""
#: ../src/wx/sys.cpp:1286
#: ../src/wx/sys.cpp:1289
#, c-format
msgid "Error setting up server socket (%d)"
msgstr ""
@ -1048,51 +1048,51 @@ msgstr ""
msgid "B:"
msgstr ""
#: ../src/wx/wxvbam.cpp:203
#: ../src/wx/wxvbam.cpp:222
msgid "visualboyadvance-m"
msgstr ""
#: ../src/wx/wxvbam.cpp:417
#: ../src/wx/wxvbam.cpp:436
msgid "Could not create main window"
msgstr ""
#: ../src/wx/wxvbam.cpp:484
#: ../src/wx/wxvbam.cpp:507
msgid "Save built-in XRC file and exit"
msgstr ""
#: ../src/wx/wxvbam.cpp:487
#: ../src/wx/wxvbam.cpp:510
msgid "Save built-in vba-over.ini and exit"
msgstr ""
#: ../src/wx/wxvbam.cpp:490
#: ../src/wx/wxvbam.cpp:513
msgid "Print configuration path and exit"
msgstr ""
#: ../src/wx/wxvbam.cpp:493
#: ../src/wx/wxvbam.cpp:516
msgid "Start in full-screen mode"
msgstr ""
#: ../src/wx/wxvbam.cpp:497
#: ../src/wx/wxvbam.cpp:520
msgid "Delete shared link state first, if it exists"
msgstr ""
#: ../src/wx/wxvbam.cpp:504
#: ../src/wx/wxvbam.cpp:527
msgid "List all settable options and exit"
msgstr ""
#: ../src/wx/wxvbam.cpp:507
#: ../src/wx/wxvbam.cpp:530
msgid "ROM file"
msgstr ""
#: ../src/wx/wxvbam.cpp:509
#: ../src/wx/wxvbam.cpp:532
msgid "<config>=<value>"
msgstr ""
#: ../src/wx/wxvbam.cpp:540
#: ../src/wx/wxvbam.cpp:563
msgid "Configuration/build error: can't find built-in xrc"
msgstr ""
#: ../src/wx/wxvbam.cpp:548
#: ../src/wx/wxvbam.cpp:571
#, c-format
msgid ""
"Wrote built-in configuration to %s.\n"
@ -1101,11 +1101,11 @@ msgid ""
"built-in:"
msgstr ""
#: ../src/wx/wxvbam.cpp:563
#: ../src/wx/wxvbam.cpp:586
msgid "Configuration is read from, in order:"
msgstr ""
#: ../src/wx/wxvbam.cpp:577
#: ../src/wx/wxvbam.cpp:600
#, c-format
msgid ""
"Wrote built-in override file to %s\n"
@ -1113,13 +1113,13 @@ msgid ""
"from search path:"
msgstr ""
#: ../src/wx/wxvbam.cpp:583
#: ../src/wx/wxvbam.cpp:606
msgid ""
"\n"
"\tbuilt-in"
msgstr ""
#: ../src/wx/wxvbam.cpp:594
#: ../src/wx/wxvbam.cpp:617
msgid ""
"Options set from the command line are saved if any configuration changes are "
"made in the user interface.\n"
@ -1128,13 +1128,13 @@ msgid ""
"\n"
msgstr ""
#: ../src/wx/wxvbam.cpp:615
#: ../src/wx/wxvbam.cpp:638
msgid ""
"The commands available for the Keyboard/* option are:\n"
"\n"
msgstr ""
#: ../src/wx/wxvbam.cpp:649
#: ../src/wx/wxvbam.cpp:672
msgid "Bad configuration option or multiple ROM files given:\n"
msgstr ""
@ -1203,6 +1203,16 @@ msgstr ""
msgid "CONTROL"
msgstr ""
#: ../src/wx/widgets/sdljoy.cpp:105
#, c-format
msgid "Connected game controller %d"
msgstr ""
#: ../src/wx/widgets/sdljoy.cpp:115
#, c-format
msgid "Disconnected game controller %d"
msgstr ""
#: ../src/wx/xrc/AccelConfig.xrc:4
msgid "Key Shortcuts"
msgstr ""
@ -2356,152 +2366,136 @@ msgid "General"
msgstr ""
#: ../src/wx/xrc/GeneralConfig.xrc:26
msgid "Check for updates:"
msgstr ""
#: ../src/wx/xrc/GeneralConfig.xrc:33
msgid "&Never"
msgstr ""
#: ../src/wx/xrc/GeneralConfig.xrc:42
msgid "&Daily"
msgstr ""
#: ../src/wx/xrc/GeneralConfig.xrc:50
msgid "&Weekly"
msgstr ""
#: ../src/wx/xrc/GeneralConfig.xrc:64
msgid "Screenshot Format:"
msgstr ""
#: ../src/wx/xrc/GeneralConfig.xrc:71
#: ../src/wx/xrc/GeneralConfig.xrc:33
msgid "&PNG"
msgstr ""
#: ../src/wx/xrc/GeneralConfig.xrc:80
#: ../src/wx/xrc/GeneralConfig.xrc:42
msgid "&BMP"
msgstr ""
#: ../src/wx/xrc/GeneralConfig.xrc:95
#: ../src/wx/xrc/GeneralConfig.xrc:57
msgid "&Rewind interval :"
msgstr ""
#: ../src/wx/xrc/GeneralConfig.xrc:96
#: ../src/wx/xrc/GeneralConfig.xrc:58
msgid "If not empty or 0, enable rewind (seconds)"
msgstr ""
#: ../src/wx/xrc/GeneralConfig.xrc:105
#: ../src/wx/xrc/GeneralConfig.xrc:67
msgid "seconds (0-600); 0 = disable"
msgstr ""
#: ../src/wx/xrc/GeneralConfig.xrc:116
#: ../src/wx/xrc/GeneralConfig.xrc:78
msgid "&Throttle"
msgstr ""
#: ../src/wx/xrc/GeneralConfig.xrc:134 ../src/wx/xrc/SpeedupConfig.xrc:27
#: ../src/wx/xrc/GeneralConfig.xrc:96 ../src/wx/xrc/SpeedupConfig.xrc:27
#, c-format
msgid "% of normal:"
msgstr ""
#: ../src/wx/xrc/GeneralConfig.xrc:144
#: ../src/wx/xrc/GeneralConfig.xrc:106
msgid "0 = no throttle"
msgstr ""
#: ../src/wx/xrc/GeneralConfig.xrc:153 ../src/wx/xrc/SpeedupConfig.xrc:46
#: ../src/wx/xrc/GeneralConfig.xrc:115 ../src/wx/xrc/SpeedupConfig.xrc:46
msgid "25%"
msgstr ""
#: ../src/wx/xrc/GeneralConfig.xrc:154 ../src/wx/xrc/SpeedupConfig.xrc:47
#: ../src/wx/xrc/GeneralConfig.xrc:116 ../src/wx/xrc/SpeedupConfig.xrc:47
msgid "50%"
msgstr ""
#: ../src/wx/xrc/GeneralConfig.xrc:155 ../src/wx/xrc/SpeedupConfig.xrc:48
#: ../src/wx/xrc/GeneralConfig.xrc:117 ../src/wx/xrc/SpeedupConfig.xrc:48
msgid "75%"
msgstr ""
#: ../src/wx/xrc/GeneralConfig.xrc:156 ../src/wx/xrc/SoundConfig.xrc:45
#: ../src/wx/xrc/GeneralConfig.xrc:118 ../src/wx/xrc/SoundConfig.xrc:45
#: ../src/wx/xrc/SpeedupConfig.xrc:49
msgid "100%"
msgstr ""
#: ../src/wx/xrc/GeneralConfig.xrc:157 ../src/wx/xrc/SpeedupConfig.xrc:50
#: ../src/wx/xrc/GeneralConfig.xrc:119 ../src/wx/xrc/SpeedupConfig.xrc:50
msgid "125%"
msgstr ""
#: ../src/wx/xrc/GeneralConfig.xrc:158 ../src/wx/xrc/SpeedupConfig.xrc:51
#: ../src/wx/xrc/GeneralConfig.xrc:120 ../src/wx/xrc/SpeedupConfig.xrc:51
msgid "150%"
msgstr ""
#: ../src/wx/xrc/GeneralConfig.xrc:159 ../src/wx/xrc/SpeedupConfig.xrc:52
#: ../src/wx/xrc/GeneralConfig.xrc:121 ../src/wx/xrc/SpeedupConfig.xrc:52
msgid "175%"
msgstr ""
#: ../src/wx/xrc/GeneralConfig.xrc:160 ../src/wx/xrc/SpeedupConfig.xrc:53
#: ../src/wx/xrc/GeneralConfig.xrc:122 ../src/wx/xrc/SpeedupConfig.xrc:53
msgid "200%"
msgstr ""
#: ../src/wx/xrc/GeneralConfig.xrc:161 ../src/wx/xrc/SpeedupConfig.xrc:54
#: ../src/wx/xrc/GeneralConfig.xrc:123 ../src/wx/xrc/SpeedupConfig.xrc:54
msgid "225%"
msgstr ""
#: ../src/wx/xrc/GeneralConfig.xrc:162 ../src/wx/xrc/SpeedupConfig.xrc:55
#: ../src/wx/xrc/GeneralConfig.xrc:124 ../src/wx/xrc/SpeedupConfig.xrc:55
msgid "250%"
msgstr ""
#: ../src/wx/xrc/GeneralConfig.xrc:163 ../src/wx/xrc/SpeedupConfig.xrc:56
#: ../src/wx/xrc/GeneralConfig.xrc:125 ../src/wx/xrc/SpeedupConfig.xrc:56
msgid "275%"
msgstr ""
#: ../src/wx/xrc/GeneralConfig.xrc:164 ../src/wx/xrc/SpeedupConfig.xrc:57
#: ../src/wx/xrc/GeneralConfig.xrc:126 ../src/wx/xrc/SpeedupConfig.xrc:57
msgid "300%"
msgstr ""
#: ../src/wx/xrc/GeneralConfig.xrc:165 ../src/wx/xrc/SpeedupConfig.xrc:58
#: ../src/wx/xrc/GeneralConfig.xrc:127 ../src/wx/xrc/SpeedupConfig.xrc:58
msgid "325%"
msgstr ""
#: ../src/wx/xrc/GeneralConfig.xrc:166 ../src/wx/xrc/SpeedupConfig.xrc:59
#: ../src/wx/xrc/GeneralConfig.xrc:128 ../src/wx/xrc/SpeedupConfig.xrc:59
msgid "350%"
msgstr ""
#: ../src/wx/xrc/GeneralConfig.xrc:167 ../src/wx/xrc/SpeedupConfig.xrc:60
#: ../src/wx/xrc/GeneralConfig.xrc:129 ../src/wx/xrc/SpeedupConfig.xrc:60
msgid "375%"
msgstr ""
#: ../src/wx/xrc/GeneralConfig.xrc:168 ../src/wx/xrc/SpeedupConfig.xrc:61
#: ../src/wx/xrc/GeneralConfig.xrc:130 ../src/wx/xrc/SpeedupConfig.xrc:61
msgid "400%"
msgstr ""
#: ../src/wx/xrc/GeneralConfig.xrc:169 ../src/wx/xrc/SpeedupConfig.xrc:62
#: ../src/wx/xrc/GeneralConfig.xrc:131 ../src/wx/xrc/SpeedupConfig.xrc:62
msgid "425%"
msgstr ""
#: ../src/wx/xrc/GeneralConfig.xrc:170 ../src/wx/xrc/SpeedupConfig.xrc:63
#: ../src/wx/xrc/GeneralConfig.xrc:132 ../src/wx/xrc/SpeedupConfig.xrc:63
msgid "450%"
msgstr ""
#: ../src/wx/xrc/GeneralConfig.xrc:171 ../src/wx/xrc/SpeedupConfig.xrc:64
#: ../src/wx/xrc/GeneralConfig.xrc:133 ../src/wx/xrc/SpeedupConfig.xrc:64
msgid "475%"
msgstr ""
#: ../src/wx/xrc/GeneralConfig.xrc:172 ../src/wx/xrc/SpeedupConfig.xrc:65
#: ../src/wx/xrc/GeneralConfig.xrc:134 ../src/wx/xrc/SpeedupConfig.xrc:65
msgid "500%"
msgstr ""
#: ../src/wx/xrc/GeneralConfig.xrc:173 ../src/wx/xrc/SpeedupConfig.xrc:66
#: ../src/wx/xrc/GeneralConfig.xrc:135 ../src/wx/xrc/SpeedupConfig.xrc:66
msgid "525%"
msgstr ""
#: ../src/wx/xrc/GeneralConfig.xrc:174 ../src/wx/xrc/SpeedupConfig.xrc:67
#: ../src/wx/xrc/GeneralConfig.xrc:136 ../src/wx/xrc/SpeedupConfig.xrc:67
msgid "550%"
msgstr ""
#: ../src/wx/xrc/GeneralConfig.xrc:175 ../src/wx/xrc/SpeedupConfig.xrc:68
#: ../src/wx/xrc/GeneralConfig.xrc:137 ../src/wx/xrc/SpeedupConfig.xrc:68
msgid "575%"
msgstr ""
#: ../src/wx/xrc/GeneralConfig.xrc:176 ../src/wx/xrc/SpeedupConfig.xrc:69
#: ../src/wx/xrc/GeneralConfig.xrc:138 ../src/wx/xrc/SpeedupConfig.xrc:69
msgid "600%"
msgstr ""
@ -3413,6 +3407,10 @@ msgstr ""
msgid "Translations"
msgstr ""
#: ../src/wx/xrc/MainMenu.xrc:709
msgid "Check for updates"
msgstr ""
#: ../src/wx/xrc/MainMenu.xrc:712
msgid "&Factory Reset..."
msgstr ""

View File

@ -17,6 +17,7 @@
#include <cmath>
#include <iostream>
#include <SDL_events.h>
#include "SoundSDL.h"
#include "ConfigManager.h"
#include "../gba/Globals.h"
@ -148,6 +149,12 @@ bool SoundSDL::init(long sampleRate) {
data_available = SDL_CreateSemaphore(0);
data_read = SDL_CreateSemaphore(1);
// turn off audio events because we are not processing them
#if SDL_VERSION_ATLEAST(2, 0, 4)
SDL_EventState(SDL_AUDIODEVICEADDED, SDL_IGNORE);
SDL_EventState(SDL_AUDIODEVICEREMOVED, SDL_IGNORE);
#endif
return initialized = true;
}

10
src/common/contains.h Normal file
View File

@ -0,0 +1,10 @@
#ifndef CONTAINS_HPP_
#define CONTAINS_HPP_
template <class C, class V>
bool contains(const C& container, const V& val)
{
return (container.find(val) != container.end());
}
#endif /* CONTAINS_HPP_ */

669
src/common/range.hpp Normal file
View File

@ -0,0 +1,669 @@
/*
Range
=====
Copyright (c) 2009-2011 Khaled Alshaya
Distributed under the Boost Software License, version 1.0
(See the license at: http://www.boost.org/license_1_0.txt).
*/
/*
Rationale
=========
In Python, there is a beautiful function called "range".
"range" allows the programmer to iterate over a range elegantly.
This concept is not as general as "for-loops" in C++,
but non the less, it expresses the intent of the programmer
clearer than the general "for-loops" in many cases.
Design
======
Range is made to be STL-like library. In fact, it is
built on top of the concepts of STL. The library is designed to
work with STL algorithms as well. Range is more flexible
than the Python "range", because:
Range is an "immutable ordered random access container"
Specifications
==============
Range satisfies the following requirements:
* Immutable.
* Random Access Container.
* Random Access Iterator Interface.
* Constant Time Complexity Operations.
Range models an ordered sequence of elements,
where a range is defined by:
[begin, end)
* begin: the first element in the range. (Inclusive)
* end : the last element in the range. (Exclusive)
* step : the distance between two consecutive elements in a range.
where each element in the range is defined by:
element = begin + step * i
* i: is the index of the element in range.
The following precondition must be met for the sequence
to be a valid range:
step != 0
&&
(
begin <= end && step > 0
||
begin >= end && step < 0
)
Portability
===========
Range Generator is written in standard C++ (C++98). It depends
-only- on the standard C++ library.
*/
#ifndef range_h__
#define range_h__
// using std::range
// using std::size_t from <cstddef>
// using std::ceil from <cmath>
#include <iterator>
#include <stdexcept>
#include <cstddef>
#include <cmath>
namespace Range
{
template <class IntegerType>
struct basic_range
{
struct const_iterator_impl
{
typedef IntegerType value_type;
typedef std::size_t size_type;
typedef IntegerType difference_type;
typedef value_type* pointer;
typedef value_type& reference;
typedef
std::random_access_iterator_tag
iterator_category;
const_iterator_impl(): r(0), index(0)
{ }
const_iterator_impl(const const_iterator_impl& rhs)
: r(rhs.r), index(rhs.index)
{ }
const_iterator_impl(basic_range<IntegerType> const * p_range, size_type p_index)
:r(p_range), index(p_index)
{ }
const_iterator_impl& operator=(const const_iterator_impl& rhs)
{
r = rhs.r;
index = rhs.index;
return *this;
}
bool operator==(const const_iterator_impl& rhs) const
{
return *r == *(rhs.r) && index == rhs.index;
}
bool operator!=(const const_iterator_impl& rhs) const
{
return !(*this == rhs);
}
bool operator<(const const_iterator_impl& rhs) const
{
return index < rhs.index;
}
bool operator>(const const_iterator_impl& rhs) const
{
return index > rhs.index;
}
bool operator<=(const const_iterator_impl& rhs) const
{
return index <= rhs.index;
}
bool operator>=(const const_iterator_impl& rhs) const
{
return index >= rhs.index;
}
value_type operator*() const
{
return r->m_first_element + r->m_step*index;
}
// operator->
// is not implemented because the value_type is an integer type
// and primitive types in C++ don't define member functions.
const_iterator_impl& operator++()
{
++index;
return *this;
}
const_iterator_impl operator++(int)
{
const_iterator_impl temp = *this;
++index;
return temp;
}
const_iterator_impl& operator--()
{
--index;
return *this;
}
const_iterator_impl operator--(int)
{
const_iterator_impl temp = *this;
--index;
return temp;
}
const_iterator_impl& operator+=(difference_type increment)
{
index += increment;
return *this;
}
// operator+
// is friend operator but operator-
// is not, because we want to allow the following for "+":
// iterator+5
// 5+iterator
// For the "-" it is not correct to do so, because
// iterator-5 != 5-iterator
friend const_iterator_impl operator+
(const const_iterator_impl& lhs, difference_type increment)
{
const_iterator_impl sum;
sum.r = lhs.r;
sum.index = lhs.index + increment;
return sum;
}
const_iterator_impl& operator-=(difference_type decrement)
{
index -= decrement;
return *this;
}
const_iterator_impl operator-(difference_type decrement) const
{
const_iterator_impl shifted_iterator;
shifted_iterator.r = r;
shifted_iterator.index = index - decrement;
return shifted_iterator;
}
difference_type operator-(const const_iterator_impl& rhs) const
{
return index - rhs.index;
}
value_type operator[](difference_type offset) const
{
size_type new_index = index + offset;
return r->m_first_element + r->m_step*new_index;
}
private:
basic_range<IntegerType> const * r;
size_type index;
};
struct const_reverse_iterator_impl
{
typedef IntegerType value_type;
typedef std::size_t size_type;
typedef IntegerType difference_type;
typedef value_type* pointer;
typedef value_type& reference;
typedef
std::random_access_iterator_tag
iterator_category;
const_reverse_iterator_impl(): r(0), index(0)
{ }
const_reverse_iterator_impl(const const_reverse_iterator_impl& rhs)
: r(rhs.r), index(rhs.index)
{ }
const_reverse_iterator_impl(basic_range<IntegerType> const * p_range, size_type p_index)
:r(p_range), index(p_index)
{ }
const_reverse_iterator_impl& operator=(const const_reverse_iterator_impl& rhs)
{
r = rhs.r;
index = rhs.index;
return *this;
}
bool operator==(const const_reverse_iterator_impl& rhs) const
{
return *r == *(rhs.r) && index == rhs.index;
}
bool operator!=(const const_reverse_iterator_impl& rhs) const
{
return !(*this == rhs);
}
bool operator<(const const_reverse_iterator_impl& rhs) const
{
return index < rhs.index;
}
bool operator>(const const_reverse_iterator_impl& rhs) const
{
return index > rhs.index;
}
bool operator<=(const const_reverse_iterator_impl& rhs) const
{
return index <= rhs.index;
}
bool operator>=(const const_reverse_iterator_impl& rhs) const
{
return index >= rhs.index;
}
value_type operator*() const
{
size_type reverse_index
= (r->m_element_count - 1) - index;
return r->m_first_element + r->m_step*reverse_index;
}
// operator->
// is not implemented because the value_type is integer type
// and primitive types in C++ don't define member functions.
const_reverse_iterator_impl& operator++()
{
++index;
return *this;
}
const_reverse_iterator_impl operator++(int)
{
const_reverse_iterator_impl temp = *this;
++index;
return temp;
}
const_reverse_iterator_impl& operator--()
{
--index;
return *this;
}
const_reverse_iterator_impl operator--(int)
{
const_reverse_iterator_impl temp = *this;
--index;
return temp;
}
const_reverse_iterator_impl& operator+=(difference_type increment)
{
index += increment;
return *this;
}
// operator+
// is friend operator but operator-
// is not, because we want to allow the following for "+":
// iterator+5
// 5+iterator
// For the "-" it is not correct to do so, because
// iterator-5 != 5-iterator
friend const_reverse_iterator_impl operator+
(const const_reverse_iterator_impl& lhs, difference_type increment)
{
const_reverse_iterator_impl sum;
sum.r = lhs.r;
sum.index = lhs.index + increment;
return sum;
}
const_reverse_iterator_impl& operator-=(difference_type decrement)
{
index -= decrement;
return *this;
}
const_reverse_iterator_impl operator-(difference_type decrement) const
{
const_reverse_iterator_impl shifted_iterator;
shifted_iterator.r = r;
shifted_iterator.index = index - decrement;
return shifted_iterator;
}
difference_type operator-(const const_reverse_iterator_impl& rhs) const
{
return index - rhs.index;
}
value_type operator[](difference_type offset) const
{
size_type new_reverse_index
= (r->m_element_count - 1) - (index + offset);
return r->m_first_element + r->m_step*new_reverse_index;
}
private:
basic_range<IntegerType> const * r;
size_type index;
};
typedef IntegerType value_type;
typedef const_iterator_impl iterator;
typedef const_iterator_impl const_iterator;
typedef const_reverse_iterator_impl reverse_iterator;
typedef const_reverse_iterator_impl const_reverse_iterator;
typedef value_type& reference;
typedef const value_type& const_reference;
typedef value_type* pointer;
typedef IntegerType difference_type;
typedef std::size_t size_type;
// In the case of default construction,
// the range is considered as an empty range with no elements.
// step can be anything other than 0. 1 is
// an implementation convention, and it doesn't have
// a significance in this case because the range is empty.
basic_range(): m_first_element(0), m_element_count(0), m_step(1)
{ }
// first_element: is begin in specifications.
// last_element: is end in specifications.
basic_range(value_type first_element, value_type last_element, value_type step)
: m_first_element(first_element),
m_step(step)
{
// We need to count the number of elements.
// The only case where a range is invalid,
// when the step=0. It means that the range
// is infinite, because the number of elements
// in a range, is the length of that range
// divided by the difference between
// every two successive elements.
if(step == 0)
throw std::out_of_range("Invalid Range: step can't be equal to zero!");
if(first_element < last_element && step < 0)
throw std::out_of_range("Invalid Range: step can't be backward, while the range is forward!");
if(first_element > last_element && step > 0)
throw std::out_of_range("Invalid Range: step can't be forward, while the range is backward!");
m_element_count = (last_element-first_element)/step;
if( (last_element-first_element)%step != 0 )
++m_element_count;
}
// The following constructor, determines the step
// automatically. If the range is forward, then
// step will be one. If the range is backward,
// step will be minus one. If the begin is equal
// to end, then the step must not equal to zero
// and it is set to one as a convention.
basic_range(value_type first_element, value_type last_element)
: m_first_element(first_element)
{
if(last_element >= first_element) *this = basic_range<IntegerType>(first_element, last_element, 1);
else *this = basic_range<IntegerType>(first_element, last_element, -1);
}
// The following constructor is a shortcut
// if you want the first element as zero.
// the step is determined automatically, based
// on the last element. If the last element is
// positive, then step is one, but if it is negative
// then step is minus one.
basic_range<IntegerType>(value_type last_element)
: m_first_element(0)
{
if(last_element >= m_first_element) *this = basic_range<IntegerType>(m_first_element, last_element, 1);
else *this = basic_range<IntegerType>(m_first_element, last_element, -1);
}
basic_range<IntegerType>(const basic_range<IntegerType>& r)
: m_first_element(r.m_first_element),
m_element_count(r.m_element_count),
m_step(r.m_step)
{ }
basic_range<IntegerType>& operator=(const basic_range<IntegerType>& r)
{
m_first_element = r.m_first_element;
m_element_count = r.m_element_count;
m_step = r.m_step;
return *this;
}
bool operator==(const basic_range<IntegerType>& r) const
{
return m_first_element == r.m_first_element
&&
m_element_count == r.m_element_count
&&
m_step == r.m_step;
}
bool operator!=(const basic_range<IntegerType>& r) const
{
return !(*this == r);
}
// The following four functions enable the user to compare
// ranges using ( <, >, <=, >=).
// The comparison between two ranges is a simple lexicographical
// comparison(element by element). By convention, if two ranges
// R1, R2 where R1 has a smaller number of elements. Then if
// R1 contains more elements but all R1 elements are found in R2
// R1 is considered less than R2.
bool operator<(const basic_range<IntegerType>& r) const
{
// ********** This function needs refactoring.
if(m_element_count == 0 && r.m_element_count == 0)
return false;
if(m_element_count == 0 && r.m_element_count > 0)
return true;
if(m_element_count > 0 && r.m_element_count == 0)
return false;
// At this point, both has at least one element.
if(m_first_element < r.m_first_element)
return true;
if(m_first_element > r.m_first_element)
return false;
// At this point, the first element of both are equal.
if(m_element_count == 1 && r.m_element_count == 1)
return false;
if(m_element_count == 1 && r.m_element_count > 1)
return true;
if(m_element_count > 1 && r.m_element_count == 1)
return false;
// At this point, both have at least two elements with
// a similar first element. Note than the final answer
// in this case depends on the second element only, because
// we don't need to compare the elements further.
// Note that the second element is at (index == 1), because
// the first element is at (index == 0).
if(m_first_element+m_step*1 < r.m_first_element+r.m_step*1)
return true;
if(m_first_element+m_step*1 > r.m_first_element+r.m_step*1)
return false;
// if the first two elements of both ranges are equal, then
// they are co-linear ranges(because the step is constant).
// In that case, they comparison depends only on
// the size of the ranges by convention.
return m_element_count < r.m_element_count;
}
bool operator>(const basic_range<IntegerType>& r) const
{
// ********** This function needs refactoring.
if(m_element_count == 0 && r.m_element_count == 0)
return false;
if(m_element_count == 0 && r.m_element_count > 0)
return false;
if(m_element_count > 0 && r.m_element_count == 0)
return true;
// At this point, both has at least one element.
if(m_first_element < r.m_first_element)
return false;
if(m_first_element > r.m_first_element)
return true;
// At this point, the first element of both are equal.
if(m_element_count == 1 && r.m_element_count == 1)
return false;
if(m_element_count == 1 && r.m_element_count > 1)
return false;
if(m_element_count > 1 && r.m_element_count == 1)
return true;
// At this point, both have at least two elements with
// a similar first element. Note than the final answer
// in this case depends on the second element only, because
// we don't need to compare the elements further.
// Note that the second element is at (index == 1), because
// the first element is at (index == 0).
if(m_first_element+m_step*1 < r.m_first_element+r.m_step*1)
return false;
if(m_first_element+m_step*1 > r.m_first_element+r.m_step*1)
return true;
// if the first two elements of both ranges are equal, then
// they are co-linear ranges(because the step is constant).
// In that case, they comparison depends only on
// the size of the ranges by convention.
return m_element_count > r.m_element_count;
}
bool operator <=(const basic_range<IntegerType>& r) const
{
return !(*this > r);
}
bool operator >=(const basic_range<IntegerType>& r) const
{
return !(*this < r);
}
const_iterator begin() const
{
return const_iterator(this, 0);
}
const_iterator end() const
{
return const_iterator(this, m_element_count);
}
const_reverse_iterator rbegin() const
{
return const_reverse_iterator(this, 0);
}
const_reverse_iterator rend() const
{
return const_reverse_iterator(this, m_element_count);
}
size_type size() const
{
return m_element_count;
}
size_type max_size() const
{
// Because this is an immutable container,
// max_size() == size()
return m_element_count;
}
bool empty() const
{
return m_element_count == 0;
}
// exist() and find() are similar except that
// find() returns the index of the element.
iterator find(value_type element) const
{
value_type element_index = (element - m_first_element) / m_step;
bool in_range = element_index >= 0 && element_index < m_element_count &&
(element - m_first_element) % m_step == 0;
if(in_range)
return begin() + element_index;
return end();
}
bool exist(value_type element) const
{
return find(element) != end();
}
// In the standard, the operator[]
// should return a const reference.
// Because Range Generator doesn't store its elements
// internally, we return a copy of the value.
// In any case, this doesn't affect the semantics of the operator.
value_type operator[](size_type index) const
{
return m_first_element + m_step*index;
}
private:
// m_first_element: begin (see specifications).
// m_element_count: (end - begin) / step
value_type m_first_element, m_element_count, m_step;
};
// This is the default type of range!
typedef basic_range<int> range;
}
#endif // range_h__

View File

@ -681,6 +681,7 @@ endif()
set(
HDR_WX
wxvbam.h
wxlogdebug.h
drawing.h
filters.h
ioregs.h
@ -698,6 +699,8 @@ set(
# from external source with minor modifications
widgets/wx/checkedlistctrl.h
../common/version_cpp.h
../common/range.hpp
../common/contains.h
)
if(WIN32 AND (AMD64 OR X86_32) AND ENABLE_ONLINEUPDATES)

View File

@ -1669,11 +1669,8 @@ public:
if (defkeys_keyboard[i].key)
a.push_back(defkeys_keyboard[i]);
if (defkeys_joystick[i].joy)
a.push_back(defkeys_joystick[i]);
if (extrakeys_joystick[i].joy)
a.push_back(extrakeys_joystick[i]);
for (auto bind : defkeys_joystick[i])
a.push_back(bind);
tc->SetValue(wxJoyKeyTextCtrl::ToString(a));
}
@ -1681,6 +1678,19 @@ public:
}
} JoyPadConfigHandler[4];
class JoystickPoller : public wxTimer {
public:
void Notify() {
wxGetApp().frame->PollJoysticks();
}
void ShowDialog(wxShowEvent& ev) {
if (ev.IsShown())
Start(50);
else
Stop();
}
};
// manage fullscreen mode widget
// technically, it's more than a validator: it modifies the widget as well
class ScreenModeList : public wxValidator {
@ -3787,6 +3797,14 @@ bool MainFrame::BindControls()
wxCommandEventHandler(JoyPadConfig_t::JoypadConfigButtons),
NULL, &JoyPadConfigHandler[i]);
}
// poll the joystick
JoystickPoller* jpoll = new JoystickPoller();
joyDialog->Connect(wxID_ANY, wxEVT_SHOW,
wxShowEventHandler(JoystickPoller::ShowDialog),
jpoll, jpoll);
joyDialog->Fit();
}

View File

@ -1,5 +1,6 @@
#include "../common/ConfigManager.h"
#include "wxvbam.h"
#include <vector>
#include <algorithm>
#include <wx/display.h>
#include "strutils.h"
@ -151,22 +152,20 @@ wxJoyKeyBinding defkeys_keyboard[NUM_KEYS] = {
WJKB(WXK_SPACE), WJKB(0), WJKB(0)
};
wxJoyKeyBinding defkeys_joystick[NUM_KEYS] = {
WJKB(0, WXJB_HAT_N, 1), WJKB(0, WXJB_HAT_S, 1), WJKB(0, WXJB_HAT_W, 1), WJKB(0, WXJB_HAT_E, 1),
WJKB(0, WXJB_BUTTON, 1), WJKB(1, WXJB_BUTTON, 1), WJKB(4, WXJB_BUTTON, 1), WJKB(5, WXJB_BUTTON, 1),
WJKB(6, WXJB_BUTTON, 1), WJKB(7, WXJB_BUTTON, 1),
WJKB(0), WJKB(0), WJKB(0), WJKB(0),
WJKB(0), WJKB(0), WJKB(0), WJKB(0),
WJKB(0), WJKB(0), WJKB(0)
};
wxJoyKeyBinding extrakeys_joystick[NUM_KEYS] = {
WJKB(1, WXJB_AXIS_MINUS, 1), WJKB(1, WXJB_AXIS_PLUS, 1), WJKB(0, WXJB_AXIS_MINUS, 1), WJKB(0, WXJB_AXIS_PLUS, 1),
WJKB(0), WJKB(0), WJKB(0), WJKB(0),
WJKB(0), WJKB(0),
WJKB(0), WJKB(0), WJKB(0), WJKB(0),
WJKB(0), WJKB(0), WJKB(0), WJKB(0),
WJKB(0), WJKB(0), WJKB(0)
std::vector<std::vector<wxJoyKeyBinding>> defkeys_joystick = {
{ WJKB(11, WXJB_BUTTON, 1), WJKB(1, WXJB_AXIS_MINUS, 1), WJKB(3, WXJB_AXIS_MINUS, 1) },
{ WJKB(12, WXJB_BUTTON, 1), WJKB(1, WXJB_AXIS_PLUS, 1), WJKB(3, WXJB_AXIS_PLUS, 1) },
{ WJKB(13, WXJB_BUTTON, 1), WJKB(0, WXJB_AXIS_MINUS, 1), WJKB(2, WXJB_AXIS_MINUS, 1) },
{ WJKB(14, WXJB_BUTTON, 1), WJKB(0, WXJB_AXIS_PLUS, 1), WJKB(2, WXJB_AXIS_PLUS, 1) },
{ WJKB(0, WXJB_BUTTON, 1) },
{ WJKB(1, WXJB_BUTTON, 1) },
{ WJKB(2, WXJB_BUTTON, 1), WJKB( 9, WXJB_BUTTON, 1), WJKB(4, WXJB_AXIS_PLUS, 1) },
{ WJKB(3, WXJB_BUTTON, 1), WJKB(10, WXJB_BUTTON, 1), WJKB(5, WXJB_AXIS_PLUS, 1) },
{ WJKB(4, WXJB_BUTTON, 1) },
{ WJKB(6, WXJB_BUTTON, 1) },
{}, {}, {}, {},
{}, {}, {}, {},
{}, {}, {}
};
wxAcceleratorEntry_v sys_accels;
@ -372,12 +371,12 @@ bool opt_lt(const opt_desc& opt1, const opt_desc& opt2)
void set_default_keys()
{
for (int i = 0; i < NUM_KEYS; i++) {
gopts.joykey_bindings[0][i].clear();
if (defkeys_keyboard[i].key)
gopts.joykey_bindings[0][i].push_back(defkeys_keyboard[i]);
if (defkeys_joystick[i].joy)
gopts.joykey_bindings[0][i].push_back(defkeys_joystick[i]);
if (extrakeys_joystick[i].joy)
gopts.joykey_bindings[0][i].push_back(extrakeys_joystick[i]);
for (auto bind : defkeys_joystick[i])
gopts.joykey_bindings[0][i].push_back(bind);
}
}

View File

@ -1,11 +1,13 @@
#ifndef WX_OPTS_H
#define WX_OPTS_H
#include <vector>
#define NUM_KEYS 21
extern const wxString joynames[NUM_KEYS];
extern wxJoyKeyBinding defkeys_keyboard[NUM_KEYS]; // keyboard defaults
extern wxJoyKeyBinding defkeys_joystick[NUM_KEYS]; // joystick defaults
extern wxJoyKeyBinding extrakeys_joystick[NUM_KEYS];// extra joystick defaults
extern std::vector<std::vector<wxJoyKeyBinding>> defkeys_joystick; // joystick defaults
extern struct opts_t {
opts_t();

View File

@ -955,13 +955,13 @@ void GameArea::Resume()
void GameArea::OnIdle(wxIdleEvent& event)
{
wxString pl = wxGetApp().pending_load;
MainFrame* mf = wxGetApp().frame;
if (pl.size()) {
// sometimes this gets into a loop if LoadGame() called before
// clearing pending_load. weird.
wxGetApp().pending_load = wxEmptyString;
LoadGame(pl);
MainFrame* mf = wxGetApp().frame;
#ifndef NO_DEBUGGER
if (gdbBreakOnLoad)
@ -1095,6 +1095,7 @@ void GameArea::OnIdle(wxIdleEvent& event)
CheckLinkConnection();
#endif
mf->PollJoysticks();
} else {
was_paused = true;
@ -1122,7 +1123,6 @@ void GameArea::OnIdle(wxIdleEvent& event)
wxLogInfo(_("Error writing rewind state"));
else {
if (!num_rewind_states) {
MainFrame* mf = wxGetApp().frame;
mf->cmd_enable |= CMDEN_REWIND;
mf->enable_menus();
}

View File

@ -1,103 +1,126 @@
#include "wxvbam.h"
#include "wx/sdljoy.h"
#include "SDL.h"
#include <SDL_joystick.h>
#include <SDL_events.h>
#include <SDL_gamecontroller.h>
#include <wx/window.h>
#include "../common/range.hpp"
#include "../common/contains.h"
using namespace Range;
DEFINE_EVENT_TYPE(wxEVT_SDLJOY)
struct wxSDLJoyState {
SDL_Joystick* dev;
int nax, nhat, nbut;
short* curval;
short* initial_val;
bool is_valid;
~wxSDLJoyState()
{
if (dev)
SDL_JoystickClose(dev);
if (curval)
delete[] curval;
if (initial_val)
delete[] initial_val;
}
wxSDLJoyState()
{
dev = NULL;
nax = nhat = nbut = 0;
curval = NULL;
initial_val = NULL;
is_valid = true;
}
};
wxSDLJoy::wxSDLJoy(bool analog)
wxSDLJoy::wxSDLJoy()
: wxTimer()
, digital(!analog)
, joystate(0)
, evthandler(0)
, nosticks(true)
, evthandler(nullptr)
{
// Start up joystick if not already started
// FIXME: check for errors
SDL_Init(SDL_INIT_JOYSTICK);
// but we'll have to manage it manually
SDL_JoystickEventState(SDL_IGNORE);
// now query joystick config and open joysticks
// there is no way to reread this later (e.g. if joystick plugged in),
// since SDL won't
njoy = SDL_NumJoysticks();
if (!njoy)
return;
joystate = new wxSDLJoyState[njoy];
for (int i = 0; i < njoy; i++) {
SDL_Joystick* dev = joystate[i].dev = SDL_JoystickOpen(i);
int nctrl = 0, axes = 0, hats = 0, buttons = 0;
axes = SDL_JoystickNumAxes(dev);
hats = SDL_JoystickNumHats(dev);
buttons = SDL_JoystickNumButtons(dev);
if (buttons <= 0 && axes <= 0 && hats <= 0) {
joystate[i].is_valid = false;
SDL_JoystickClose(dev);
joystate[i].dev = NULL;
continue;
}
nctrl += joystate[i].nax = axes < 0 ? 0 : axes;
nctrl += joystate[i].nhat = hats < 0 ? 0 : hats;
nctrl += joystate[i].nbut = buttons < 0 ? 0 : buttons;
joystate[i].curval = new short[nctrl]{};
// set initial values array
// see below for 360 trigger handling
#if SDL_VERSION_ATLEAST(2, 0, 6)
joystate[i].initial_val = new short[nctrl]{};
for (int j = 0; j < joystate[i].nax; j++) {
int16_t initial_state = 0;
SDL_JoystickGetAxisInitialState(dev, j, &initial_state);
joystate[i].initial_val[j] = initial_state;
// curval is 0 which is what initial state maps to ATM
}
#endif
}
SDL_Init(SDL_INIT_GAMECONTROLLER);
SDL_GameControllerEventState(SDL_ENABLE);
}
wxSDLJoy::~wxSDLJoy()
{
delete[] joystate;
// SDL_QuitSubSystem(SDL_INIT_JOYSTICK);
}
static int axisval(int x)
{
if (x > 0x1fff)
return 1;
else if (x < -0x1fff)
return -1;
return 0;
}
void wxSDLJoy::Poll()
{
wxEvtHandler* handler = evthandler ? evthandler : wxWindow::FindFocus();
SDL_Event e;
while (SDL_PollEvent(&e)) {
switch (e.type) {
case SDL_CONTROLLERBUTTONDOWN:
case SDL_CONTROLLERBUTTONUP:
{
auto joy = e.cbutton.which;
if (contains(joystate, joy)) {
auto but = e.cbutton.button;
auto val = e.cbutton.state;
auto prev_val = !val;
if (handler) {
wxSDLJoyEvent* ev = new wxSDLJoyEvent(wxEVT_SDLJOY);
ev->joy = joy;
ev->ctrl_type = WXSDLJOY_BUTTON;
ev->ctrl_idx = but;
ev->ctrl_val = val;
ev->prev_val = prev_val;
handler->QueueEvent(ev);
}
wxLogDebug("GOT SDL_CONTROLLERBUTTON: joy:%d but:%d val:%d prev_val:%d", joy, but, val, prev_val);
}
break;
}
case SDL_CONTROLLERAXISMOTION:
{
auto joy = e.caxis.which;
if (contains(joystate, joy)) {
auto axis = e.caxis.axis;
auto val = axisval(e.caxis.value);
auto prev_val = joystate[joy].axis[axis];
if (handler && val != prev_val) {
wxSDLJoyEvent* ev = new wxSDLJoyEvent(wxEVT_SDLJOY);
ev->joy = joy;
ev->ctrl_type = WXSDLJOY_AXIS;
ev->ctrl_idx = axis;
ev->ctrl_val = val;
ev->prev_val = prev_val;
handler->QueueEvent(ev);
joystate[joy].axis[axis] = val;
wxLogDebug("GOT SDL_CONTROLLERAXISMOTION: joy:%d axis:%d val:%d prev_val:%d", joy, axis, val, prev_val);
}
}
break;
}
case SDL_CONTROLLERDEVICEADDED:
case SDL_CONTROLLERDEVICEREMAPPED:
{
auto joy = e.cdevice.which;
if (add_all || contains(joystate, joy)) {
joystate[joy].dev = SDL_GameControllerOpen(joy);
systemScreenMessage(wxString::Format(_("Connected game controller %d"), joy + 1));
}
break;
}
case SDL_CONTROLLERDEVICEREMOVED:
{
auto joy = e.cdevice.which;
if (contains(joystate, joy)) {
joystate[joy].dev = nullptr;
systemScreenMessage(wxString::Format(_("Disconnected game controller %d"), joy + 1));
}
break;
}
}
}
}
wxEvtHandler* wxSDLJoy::Attach(wxEvtHandler* handler)
{
wxEvtHandler* prev = evthandler;
@ -105,151 +128,56 @@ wxEvtHandler* wxSDLJoy::Attach(wxEvtHandler* handler)
return prev;
}
void wxSDLJoy::Add(int joy)
void wxSDLJoy::Add(int8_t joy_n)
{
if (joy >= njoy || !njoy)
return;
if (joy_n < 0) {
for (uint8_t joy : range(0, SDL_NumJoysticks()))
joystate[joy].dev = SDL_GameControllerOpen(joy);
if (joy < 0) {
for (int i = 0; i < njoy; i++)
Add(i);
add_all = true;
return;
}
if (!joystate[joy].dev)
joystate[joy].dev = SDL_JoystickOpen(joy);
if (nosticks && joystate[joy].dev) {
Start(poll_time_ms);
nosticks = false;
}
joystate[joy_n].dev = SDL_GameControllerOpen(joy_n);
}
void wxSDLJoy::Remove(int joy)
void wxSDLJoy::Remove(int8_t joy_n)
{
if (joy >= njoy || !njoy)
return;
add_all = false;
if (joy < 0) {
for (int i = 0; i < njoy; i++)
if (joystate[i].dev) {
SDL_JoystickClose(joystate[i].dev);
joystate[i].dev = NULL;
}
Stop();
nosticks = true;
if (joy_n < 0) {
joystate.clear();
return;
}
if (!joystate[joy].dev)
return;
joystate.erase(joy_n);
}
SDL_JoystickClose(joystate[joy].dev);
joystate[joy].dev = NULL;
void wxSDLJoy::SetRumble(bool do_rumble)
{
rumbling = do_rumble;
for (int i = 0; i < njoy; i++)
if (joystate[i].dev)
return;
Stop();
nosticks = true;
#if SDL_VERSION_ATLEAST(2, 0, 9)
// do rumble only on device 0
auto dev = joystate[0].dev;
if (dev) {
if (rumbling) {
SDL_GameControllerRumble(dev, 0xFFFF, 0xFFFF, 300);
if (!IsRunning())
Start(150);
}
else {
SDL_GameControllerRumble(dev, 0, 0, 0);
Stop();
}
}
else
Stop();
#endif
}
void wxSDLJoy::Notify()
{
if (nosticks)
return;
SDL_JoystickUpdate();
wxEvtHandler* handler = evthandler ? evthandler : wxWindow::FindFocus();
for (int i = 0; i < njoy; i++) {
if (!joystate[i].is_valid) continue;
SDL_Joystick* dev = joystate[i].dev;
if (dev) {
int nax = joystate[i].nax, nhat = joystate[i].nhat,
nbut = joystate[i].nbut;
short val;
for (int j = 0; j < nax; j++) {
val = SDL_JoystickGetAxis(dev, j);
if (digital) {
if (val > 0x3fff)
val = 0x7fff;
else if (val <= -0x3fff)
val = -0x7fff;
else
val = 0;
}
// This is for the 360 and similar triggers which return a
// value on initial state of the trigger axis. This hack may be
// insufficient, we may need to expand the joy event API to
// support initial values.
if (joystate[i].initial_val && val == joystate[i].initial_val[j])
val = 0;
if (handler && val != joystate[i].curval[j]) {
wxSDLJoyEvent ev(wxEVT_SDLJOY, GetId());
ev.joy = i;
ev.ctrl_type = WXSDLJOY_AXIS;
ev.ctrl_idx = j;
ev.ctrl_val = val;
ev.prev_val = joystate[i].curval[j];
handler->ProcessEvent(ev);
}
joystate[i].curval[j] = val;
}
for (int j = 0; j < nhat; j++) {
val = SDL_JoystickGetHat(dev, j);
if (handler && val != joystate[i].curval[nax + j]) {
wxSDLJoyEvent ev(wxEVT_SDLJOY, GetId());
ev.joy = i;
ev.ctrl_type = WXSDLJOY_HAT;
ev.ctrl_idx = j;
ev.ctrl_val = val;
ev.prev_val = joystate[i].curval[nax + j];
handler->ProcessEvent(ev);
}
joystate[i].curval[nax + j] = val;
}
for (int j = 0; j < nbut; j++) {
val = SDL_JoystickGetButton(dev, j);
if (handler && val != joystate[i].curval[nax + nhat + j]) {
wxSDLJoyEvent ev(wxEVT_SDLJOY, GetId());
ev.joy = i;
ev.ctrl_type = WXSDLJOY_BUTTON;
ev.ctrl_idx = j;
ev.ctrl_val = val;
ev.prev_val = joystate[i].curval[nax + nhat + j];
handler->ProcessEvent(ev);
}
joystate[i].curval[nax + nhat + j] = val;
}
}
}
// do rumble only on device 0
#if SDL_VERSION_ATLEAST(2, 0, 9)
SDL_Joystick* dev = joystate[0].dev;
if (dev) {
if (rumbling)
SDL_JoystickRumble(dev, 0xFFFF, 0xFFFF, poll_time_ms * 2);
else
SDL_JoystickRumble(dev, 0, 0, 0);
}
#endif
SetRumble(rumbling);
}

View File

@ -4,28 +4,26 @@
// This is my own SDL-based joystick handler, since wxJoystick is brain-dead.
// It's geared towards keyboard emulation
// To use, create a wxSDLJoy object Add() the joysticks you want to monitor.
// wxSDLJoy derives from wxTimer, so you can pause it with Stop() and
// resume with Start().
// To use, create a wxSDLJoy object Add() the joysticks you want to monitor and
// Attach() the target window to receive events.
//
// The target window will receive EVT_SDLJOY events of type wxSDLJoyEvent.
#include <vector>
#include <unordered_map>
#include <wx/event.h>
#include <wx/timer.h>
#include <SDL_gamecontroller.h>
#include "../common/contains.h"
struct wxSDLJoyState;
struct wxSDLJoyState {
SDL_GameController* dev = nullptr;
std::unordered_map<uint8_t, int8_t> axis;
};
class wxSDLJoy : public wxTimer {
public:
// if analog, send events for all axis movement
// otherwise, only send events when values cross the 50% mark
// and max out values
wxSDLJoy(bool analog = false);
// but flag can be set later
void SetAnalog(bool analog = true)
{
digital = !analog;
};
wxSDLJoy();
// send events to this handler
// If NULL (default), send to window with keyboard focus
wxEvtHandler* Attach(wxEvtHandler*);
@ -33,43 +31,29 @@ public:
// -1 == add all
// If joy > # of joysticks, it is ignored
// This will start polling if a valid joystick is selected
void Add(int joy = -1);
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(int joy = -1);
void Remove(int8_t joy = -1);
// query if a stick is being polled
bool IsPolling(int joy);
// query # of joysticks
int GetNumJoysticks()
{
return njoy;
}
// query # of axes on given joystick
// 0 is returned if joy is invalid
int GetNumAxes(int joy);
// query # of hats on given joystick
// 0 is returned if joy is invalid
int GetNumHats(int joy);
// query # of buttons on given joystick
// 0 is returned if joy is invalid
int GetNumButtons(int joy);
bool IsPolling(uint8_t joy) { return contains(joystate, joy); }
// true = currently rumbling, false = turn off rumbling
void SetRumble(bool b) { rumbling = b; }
void SetRumble(bool do_rumble);
void Poll();
virtual ~wxSDLJoy();
protected:
bool digital;
int njoy;
wxSDLJoyState* joystate;
wxEvtHandler* evthandler;
bool nosticks;
bool rumbling = false;
// used to continue rumbling on a timer
void Notify();
uint16_t poll_time_ms = 50;
private:
std::unordered_map<uint8_t, wxSDLJoyState> joystate;
wxEvtHandler* evthandler;
bool add_all = false, rumbling = false;
};
enum {
@ -86,8 +70,8 @@ class wxSDLJoyEvent : public wxCommandEvent {
public:
// Default constructor
wxSDLJoyEvent(wxEventType commandType = wxEVT_NULL, int id = 0)
: wxCommandEvent(commandType, id)
wxSDLJoyEvent(wxEventType commandType = wxEVT_NULL)
: wxCommandEvent(commandType)
{
}
// accessors

23
src/wx/wxlogdebug.h Normal file
View File

@ -0,0 +1,23 @@
#ifndef WXLOGDEBUG_H_
#define WXLOGDEBUG_H_
#include <wx/log.h>
// make wxLogDebug work on non-debug builds of Wx, and make it use the console
// on Windows
// (this works on 2.8 too!)
#if !defined(NDEBUG) && (!wxDEBUG_LEVEL || defined(__WXMSW__))
#ifdef __WXMSW__
#define VBAM_DEBUG_STREAM stdout
#else
#define VBAM_DEBUG_STREAM stderr
#endif
#undef wxLogDebug
#define wxLogDebug(...) \
do { \
fputs(wxString::Format(wxDateTime::UNow().Format(wxT("%X")) + wxT(": Debug: ") + __VA_ARGS__).utf8_str(), VBAM_DEBUG_STREAM); \
fputc('\n', VBAM_DEBUG_STREAM); \
} while(0)
#endif
#endif /* WXLOGDEBUG_H_ */

View File

@ -32,22 +32,7 @@
#include "../gba/Globals.h"
#include "../gba/Sound.h"
// make wxLogDebug work on non-debug builds of Wx, and make it use the console
// on Windows
// (this works on 2.8 too!)
#if !defined(NDEBUG) && (!wxDEBUG_LEVEL || defined(__WXMSW__))
#ifdef __WXMSW__
#define VBAM_DEBUG_STREAM stdout
#else
#define VBAM_DEBUG_STREAM stderr
#endif
#undef wxLogDebug
#define wxLogDebug(...) \
do { \
fputs(wxString::Format(wxDateTime::UNow().Format(wxT("%X")) + wxT(": Debug: ") + __VA_ARGS__).utf8_str(), VBAM_DEBUG_STREAM); \
fputc('\n', VBAM_DEBUG_STREAM); \
} while(0)
#endif
#include "wxlogdebug.h"
template <typename T>
void CheckPointer(T pointer)
@ -328,15 +313,18 @@ public:
virtual void SetJoystickRumble(bool b) { joy.SetRumble(b); }
bool IsPaused(bool incendental = false)
{
return (paused && !pause_next && !incendental) || menus_opened || dialog_opened;
}
void PollJoysticks() { joy.Poll(); }
// required for building from xrc
DECLARE_DYNAMIC_CLASS(MainFrame);
// required for event handling
DECLARE_EVENT_TABLE();
bool IsPaused(bool incendental = false)
{
return (paused && !pause_next && !incendental) || menus_opened || dialog_opened;
}
protected:
virtual void BindAppIcon();