Compare commits

...
97 Commits
Author SHA1 Message Date
Ty Lamontagne c8c7ac45d1 CDVD: Touch-Up GZip logging messages. 2022-06-21 23:49:27 +01:00
Connor McLaughlin 14181ec70d InputManager: Fix chord bindings when activating in reverse 2022-06-21 21:53:02 +01:00
Connor McLaughlin b20e5a1e01 VMManager: Convert to fmt
Also adds an error message when trying to save state in the BIOS.
2022-06-21 21:53:02 +01:00
Connor McLaughlin 64d222a1e4 Qt: Support binding numpad keys independently of number row 2022-06-21 21:53:02 +01:00
refractionpcsx2 e4554fe9ca CDVD: Load GZip index location direct from ini.
Stops the Qt game list from putting indexes in the default location when a custom one is used.
2022-06-21 21:51:25 +01:00
refractionpcsx2 8ec736789c GUI: Allow memcard names shorter than 4 2022-06-21 21:51:07 +01:00
refractionpcsx2 9a323a9a8d GUI: Only add extension to new memcards on save 2022-06-21 21:51:07 +01:00
MomosuWarosu 298e626b6e GameDB: Upscaling fixes added for all retail versions of Shutokou Battle 01
GameDB: Upscaling fixes for Shutokou Battle 01

GameDB: Upscaling fixes for Shutokou Battle 01
2022-06-21 21:22:03 +01:00
Blackbird88 af7308e395 GameDB: Fix name of "The Document of Metal Gear Solid 2" 2022-06-21 21:21:43 +01:00
Mrlinkwii 7c80fcacba GameDB: add missing upscaling fixes to "Xenosaga Episode III" 2022-06-21 21:21:27 +01:00
Silent beb23efa88 XInput: Fix axis inversion when using SCP extensions 2022-06-21 17:16:44 +01:00
Christian Murphy 1b65f5f164 PAD/Core bugfix for #6472
Bugfix for https://github.com/PCSX2/pcsx2/issues/6472 which was introduced by hold turbo changes
2022-06-21 09:36:06 +01:00
PCSX2 Bot bb336f1bc7 PAD: Update to latest controller database. 2022-06-20 21:16:38 +02:00
Connor McLaughlin 36d386b451 Qt: Fix hide mouse cursor not working with render-to-main off 2022-06-20 15:14:23 +01:00
Connor McLaughlin 9199f48a3c HostDisplay: Remove DestroyRenderDevice()/move to destructor
Saves having to remember to call this any time you destroy the display
object.
2022-06-20 15:14:15 +01:00
Connor McLaughlin 5c88c585a0 ContextWGL: Use pbuffers when we don't have a surface
Fixes context restore error tripping when confirming shutdown while
fullscreen in Qt.
2022-06-20 15:14:15 +01:00
Connor McLaughlin 771b1490d2 Qt: Get rid of redundant resume on confirm shutdown 2022-06-20 10:34:28 +01:00
Connor McLaughlin 4bbdbf6332 Qt: Fix crash on confirm shutdown when fullscreen 2022-06-20 10:34:28 +01:00
refractionpcsx2 7dc10fbe46 Docs: Add normal readme and rename documentation readme 2022-06-20 09:12:08 +01:00
Ali Mahdavi 90ca83bd96 Qt: Change the word “Save Slot” to “Load Slot” in Load State menu
First off, I must admit, I **HAVE NOT** compiled the project to see if my change breaks the whole thing or not. I just searched the entire repository for `Save Slot` and renamed the one related to `loadSaveStateSlot`.
2022-06-20 09:56:36 +02:00
Christian Murphy 4ee92fc637 Qt: Turbo (Hold) Hotkey binding
Fixes #5538
Acts as a temporary flip-flop switch while held that either enables turbo while held or disables turbo while held if you've already toggled turbo.

Default bind is Keyboard/Period but happy to make this unbound if preferred.
2022-06-20 09:55:53 +02:00
Florin9doi 1d39488061 Gamedb: DB Updates 2022-06-20 08:50:56 +01:00
MomosuWarosu d87cd32ff4 GameDB: Upscaling fixes for Tokyo Xtreme Racer 3 2022-06-20 07:50:00 +01:00
Connor McLaughlin 2a50ae905d VMManager: Don't try to load state when none exists 2022-06-20 07:38:19 +01:00
Connor McLaughlin f536584d7a VulkanHostDisplay: Fix uploading unaligned texture sizes 2022-06-20 07:28:12 +01:00
Connor McLaughlin fa3bd58b7f GS: Don't show GPU OSD when timing init fails 2022-06-20 07:28:12 +01:00
Connor McLaughlin 1f5d2c49fc Qt: Split texture replacement settings to its own page 2022-06-20 07:28:12 +01:00
Connor McLaughlin 3e585c3438 Qt: Add folder settings 2022-06-20 07:28:12 +01:00
Connor McLaughlin c23e792f68 Qt: Add folder selection to memory card settings 2022-06-20 07:28:12 +01:00
Connor McLaughlin 02d3c93c2c Qt: Allow editing folder settings 2022-06-20 07:28:12 +01:00
Connor McLaughlin a07ef0f5ee Qt: Move logging setup into core
Sharable between frontends this way.
2022-06-20 07:28:12 +01:00
Connor McLaughlin e415251f30 Qt: Add pad deadzone 2022-06-20 07:28:12 +01:00
Connor McLaughlin ddbc143178 GS/DX12: Fix creating display textures mid-frame crashing 2022-06-20 07:28:12 +01:00
CharlesThobe d54715a9c3 Updater: Do not extract portable.ini
DIO
2022-06-19 20:18:34 +01:00
refractionpcsx2 eb9507707d GS: Unset scanmsk_used after 2 frames 2022-06-19 19:36:02 +01:00
lightningterror 3377de1fe3 gitignore: Ignore dmp files in bin directory. 2022-06-19 19:05:43 +02:00
refractionpcsx2 b5807e0788 GS-PCRTC: Add option to enable/disable Anti-Blur code.
For... accuracy?

Also clean up FIELD nonsense in the merge circuit.
Fixed up offset code for interlacing
2022-06-19 16:25:13 +01:00
Connor McLaughlin c86e82039a GS: Fix possible crash when changing settings with readbacks disabled 2022-06-19 09:32:31 +02:00
Connor McLaughlin 999b2267c0 Wx: Remove sync to host refresh rate option
Still available in Qt obviously. Apparently there's COM reinitialization
issues on the CPU thread in wx, and I don't want to deal with that nonsense.
2022-06-19 09:31:51 +02:00
Connor McLaughlin fbf9585b5a VMManager: Auto switch to 16:9 when ws patches are enabled/found 2022-06-19 02:18:08 +01:00
Connor McLaughlin 470365644f Qt: Implement mouse->controller binding
Also implements forwarding mouse events to imgui.
2022-06-19 02:17:42 +01:00
Connor McLaughlin 7b3847cc5c Qt: Fix incorrect input popup when no vibration sources available 2022-06-18 22:58:27 +02:00
Connor McLaughlin 2b0e1a1dec Qt: Fix compiling with spaces in path on Windows 2022-06-18 22:57:51 +02:00
kmicki 316e140329 PAD: changed freeze data size in Linux for cross-platform compatibility 2022-06-18 22:56:59 +02:00
Mrlinkwii 02c4c3b03d Gamedb: add "EETimingHack" for "Strikers 1945 1-2" and gamefixes for 'Star wars episode 3 " 2022-06-18 21:51:51 +01:00
TellowKrinkle e5405e191b Metal: Fix ImGui renderer
Turns out those were bitcasts, not conversions
2022-06-18 15:32:46 -05:00
TheLastRar 839ea61d55 DEV9: Formatting 2022-06-17 19:12:35 +02:00
TheLastRar 02f27f8cbc DEV9: Have internal DHCP use AdapterUtils 2022-06-17 19:12:35 +02:00
TheLastRar 35b5138593 DEV9: Have internal DNS use AdapterUtils 2022-06-17 19:12:35 +02:00
TheLastRar 809c5ed051 DEV9: Have pcap backend use AdapterUtils 2022-06-17 19:12:35 +02:00
TheLastRar 412451f9d6 DEV9: Have Sockets backend use AdapterUtils 2022-06-17 19:12:35 +02:00
TheLastRar 35802f2089 DEV9: Add AdapterUtils 2022-06-17 19:12:35 +02:00
TheLastRar 8f6f0f8401 DEV9: Exclude pcap NPF_ Prefix from GUID on Windows 2022-06-17 19:12:35 +02:00
refractionpcsx2 44d95badc4 GUI-WX: Add Blit description in WX gamefix panel. 2022-06-17 00:30:06 +01:00
Mrlinkwii d622faba17 GameDB: add autoflush and upscaling fixes for "Mega Man X8" 2022-06-16 20:29:23 +01:00
noigeaR b5945d3d5c GameDB: add missing Japanese game serials
GameDB: add missing Japanese game serials
2022-06-16 17:52:07 +01:00
Silent 52cd6fb1dd GameDB: Port the Midnight Club 3 patch to NTSC-u v2.00 2022-06-16 15:05:25 +01:00
TellowKrinkle 04681babf1 Common: Add assertion failure message to crashlogs 2022-06-15 16:23:32 -05:00
Connor McLaughlin e63c068720 GameDatabase: Add option to force blit-based FPS detection 2022-06-15 16:21:31 +01:00
refractionpcsx2 fb5b465a4e GS: Fix overflow calculation from errantly going off. 2022-06-15 15:48:37 +01:00
RedPanda4552 59ea3c7949 GameDB: Add HPO Special (Texture - Aggressive) to Batman Vengeance 2022-06-15 15:48:18 +01:00
Mrlinkwii 6f5306dbdc GameDB: Fix game names "ICO" and add "Monster Rancher Evo" fixes 2022-06-15 15:48:01 +01:00
refractionpcsx2 b5388fdf2f GS: Only enable scanmsk offset on frames that need it 2022-06-14 23:07:21 +01:00
lightningterror d2904c1fd5 GS: Fix up wave filter shaders. 2022-06-14 21:25:47 +02:00
kmicki 7718feedd5 PAD Linux: correct conversion of analog stick values. 2022-06-14 20:59:11 +02:00
Riccardo Marcangelo 56add277f9 GS: Migrate function pointer to lambda
Compilers can inline lambda for superior performance.
2022-06-14 20:48:37 +02:00
refractionpcsx2 86f772ad38 GS: Avoid Div by 0 on WriteImage 2022-06-14 17:51:04 +01:00
refractionpcsx2 24c99551c7 GS: Fix God of War regression from #6389 (v1.7.2937) 2022-06-14 02:12:26 +01:00
refractionpcsx2 9441d2a33a IPU: Remove some DMA hacks 2022-06-13 23:47:47 +01:00
refractionpcsx2 3922091a58 GS: New state opt optimisation pass 2022-06-13 23:47:34 +01:00
refractionpcsx2 1107ce8fd8 GS: Check whole state before flushing draws 2022-06-13 23:47:34 +01:00
Mrlinkwii 3d10cd1adf Gamedb: fix 'Sega Sports Tennis' name 2022-06-13 23:24:22 +01:00
refractionpcsx2 02ac3943f2 INTC: Give grace period after event for reading INTC
If a game is in a tight loop waiting for a bit of INTC, we could skip a bunch of cycles incorrectly.
2022-06-13 23:02:44 +01:00
refractionpcsx2 44b672b6f5 Counters/GS: Adjust FIELD swap timing for PAL
Console tests show about 3 PAL scanlines of time.
2022-06-13 23:02:44 +01:00
PCSX2 Bot 0239d8350d PAD: Update to latest controller database. 2022-06-13 18:01:50 +02:00
Mrlinkwii bada5fe941 Gamedb: remove gamefixes form "Axel Impact - The Extreme Racing" 2022-06-12 21:48:49 +01:00
Mrlinkwii 6db871958e Gamedb: remove no longer needed patches 2022-06-12 15:40:33 +01:00
lightningterror 0b557fe265 GS/Core: Purge frameskipping.
Get rid of frameskipping, won't be added to Qt.
2022-06-12 11:43:10 +02:00
refractionpcsx2 ce08627396 GS: Reduce false positives in dev overflow errors 2022-06-12 08:03:14 +01:00
Christian Murphy 200ec5dcfb Qt: Update GameList scrolling to be per pixel to better indicate direction of scroll
Addressing issue https://github.com/PCSX2/pcsx2/issues/6237
2022-06-12 06:47:21 +01:00
Christian Murphy 8a6f55a63e Save/Load States via hotkey in QT
Addressing https://github.com/PCSX2/pcsx2/issues/6268.

Adds the commands for loading/saving all numbered states via hotkeys, leaving them all unbound by default.
2022-06-12 02:29:24 +01:00
Mrlinkwii e327f5e6f0 Gamedb: remove patches and add upscaling fixes for 'Onimusha - Dawn of Dreams' 2022-06-12 02:28:45 +01:00
Στέφανος "Coornio/8924th" Βλαστός 5849726913 GameDB: upscale fixes, adding missing serials, name corrections, etc
See PR for details
2022-06-12 02:10:05 +01:00
Mrlinkwii e063613a7f GameDB: add 'Axel Impact - The Extreme Racing' 2022-06-12 02:06:34 +01:00
Connor McLaughlin 48d2cb4975 Qt: Implement input profiles 2022-06-11 14:37:57 +01:00
Connor McLaughlin 59412b1673 Qt: Implement multitap 2022-06-11 14:37:57 +01:00
Connor McLaughlin d1a235272e SettingsInterface: Add ContainsValue() and copy helpers 2022-06-11 14:37:57 +01:00
Mrlinkwii 7db9627ff6 Gamedb: Fix games names 2022-06-10 23:23:42 +01:00
lightningterror 83550cd153 github labeler: Remove nsis. 2022-06-10 15:12:03 +02:00
TellowKrinkle 77471bd9d6 GS: Avoid using both blending and fbfetch 2022-06-10 14:40:30 +02:00
lightningterror 0e94211ff1 GS: Fix Wunused-variable warnings. 2022-06-10 14:16:11 +02:00
Mrlinkwii f6e7bba354 GameDB: Remove patch for Panzer Dragoon 2022-06-10 11:33:27 +01:00
noigeaR b2255e5b27 GameDB: add missing serials, upscaling fixes, name corrections (#6385) 2022-06-10 09:46:25 +01:00
Mrlinkwii 7b493ff4c8 Gamedb: add upscaling fixes to 'Fast and the Furious' 2022-06-09 15:23:33 +01:00
Mrlinkwii aeef163349 GameDB: add missing serials 2022-06-08 19:25:30 +01:00
RedDevilus 26f2352059 GameDB: Fix flag + add missing US serial
Someone in the Discord said that they had the question mark flags for these 2 serials, 1 is Virtua Fighter 4 Evolution PAL which had a typo and another is NBA Live '09 NTSC version. Quite surprising to see a missing American release in the GameDB still.
2022-06-08 15:27:53 +01:00
arcum42 67e38d8de2 Build: Update build.sh to remove a no longer used flag and add a few more. (#6124)
* Build: Update build.sh. Remove a no longer used flag and add a few new ones.

* Fix spacing on --use-system and --use-bundled.
2022-06-07 21:45:40 -07:00
166 changed files with 6023 additions and 3771 deletions
-2
View File
@@ -40,8 +40,6 @@
'GameDB':
- '**/GameIndex.*'
'Installer | Package':
- 'nsis/*'
- 'nsis/**/*'
- 'build.sh'
# Tools / Features
@@ -37,6 +37,7 @@ allowed_game_fixes = [
"VUSyncHack",
"VUOverflowHack",
"XGKickHack",
"BlitInternalFPSHack",
]
allowed_gs_hw_fixes = [
"autoFlush",
+1
View File
@@ -68,6 +68,7 @@ oprofile_data/
/.vscode*
/bin/**/*.dll
/bin/**/*.dmp
/bin/**/*.exp
/bin/**/*.ilk
/bin/**/*.lib
File diff suppressed because it is too large Load Diff
+101 -79
View File
@@ -47,10 +47,10 @@
03000000c82d00000130000000000000,8BitDo SF30,a:b1,b:b0,back:b10,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b2,leftshoulder:b6,leftstick:b13,lefttrigger:b8,leftx:a0,lefty:a1,rightshoulder:b7,rightstick:b14,righttrigger:b9,rightx:a2,righty:a5,start:b11,x:b4,y:b3,platform:Windows,
03000000c82d00000060000000000000,8BitDo SF30 Pro,a:b1,b:b0,back:b10,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b6,leftstick:b13,lefttrigger:b8,leftx:a0,lefty:a1,rightshoulder:b7,rightstick:b14,righttrigger:b9,rightx:a2,righty:a3,start:b11,x:b4,y:b3,platform:Windows,
03000000c82d00000061000000000000,8BitDo SF30 Pro,a:b1,b:b0,back:b10,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b2,leftshoulder:b6,leftstick:b13,lefttrigger:b8,leftx:a0,lefty:a1,rightshoulder:b7,rightstick:b14,righttrigger:b9,rightx:a2,righty:a3,start:b11,x:b4,y:b3,platform:Windows,
03000000102800000900000000000000,8BitDo SFC30,a:b1,b:b0,back:b10,leftshoulder:b6,leftx:a0,lefty:a1,rightshoulder:b7,start:b11,x:b4,y:b3,platform:Windows,
03000000c82d000021ab000000000000,8BitDo SFC30,a:b1,b:b0,back:b10,leftshoulder:b6,leftx:a0,lefty:a1,rightshoulder:b7,start:b11,x:b4,y:b3,platform:Windows,
03000000c82d00003028000000000000,8BitDo SFC30,a:b1,b:b0,back:b10,leftshoulder:b6,leftx:a0,lefty:a1,rightshoulder:b7,start:b11,x:b4,y:b3,platform:Windows,
03000000c82d00000030000000000000,8BitDo SN30,a:b1,b:b0,back:b10,leftshoulder:b6,leftx:a0,lefty:a1,rightshoulder:b7,start:b11,x:b4,y:b3,platform:Windows,
03000000102800000900000000000000,8BitDo SFC30,a:b1,b:b0,back:b10,leftshoulder:b6,dpdown:+a1,dpleft:-a0,dpright:+a0,dpup:-a1,rightshoulder:b7,start:b11,x:b4,y:b3,platform:Windows,
03000000c82d000021ab000000000000,8BitDo SFC30,a:b1,b:b0,back:b10,leftshoulder:b6,dpdown:+a1,dpleft:-a0,dpright:+a0,dpup:-a1,rightshoulder:b7,start:b11,x:b4,y:b3,platform:Windows,
03000000c82d00003028000000000000,8BitDo SFC30,a:b1,b:b0,back:b10,leftshoulder:b6,dpdown:+a1,dpleft:-a0,dpright:+a0,dpup:-a1,rightshoulder:b7,start:b11,x:b4,y:b3,platform:Windows,
03000000c82d00000030000000000000,8BitDo SN30,a:b1,b:b0,back:b10,leftshoulder:b6,dpdown:+a1,dpleft:-a0,dpright:+a0,dpup:-a1,rightshoulder:b7,start:b11,x:b4,y:b3,platform:Windows,
03000000c82d00001290000000000000,8BitDo SN30,a:b1,b:b0,back:b10,dpdown:+a1,dpleft:-a0,dpright:+a0,dpup:-a1,leftshoulder:b6,rightshoulder:b7,start:b11,x:b4,y:b3,platform:Windows,
03000000c82d000020ab000000000000,8BitDo SN30,a:b1,b:b0,back:b10,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b2,leftshoulder:b6,leftstick:b13,lefttrigger:b8,leftx:a0,lefty:a1,rightshoulder:b7,rightstick:b14,righttrigger:b9,rightx:a2,righty:a5,start:b11,x:b4,y:b3,platform:Windows,
03000000c82d00004028000000000000,8BitDo SN30,a:b1,b:b0,back:b10,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b2,leftshoulder:b6,leftstick:b13,lefttrigger:b8,leftx:a0,lefty:a1,rightshoulder:b7,rightstick:b14,righttrigger:b9,rightx:a2,righty:a5,start:b11,x:b4,y:b3,platform:Windows,
@@ -63,7 +63,7 @@
03000000c82d00000260000000000000,8BitDo SN30 Pro+,a:b1,b:b0,back:b10,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b2,leftshoulder:b6,leftstick:b13,lefttrigger:b8,leftx:a0,lefty:a1,rightshoulder:b7,rightstick:b14,righttrigger:b9,rightx:a2,righty:a3,start:b11,x:b4,y:b3,platform:Windows,
03000000c82d00000261000000000000,8BitDo SN30 Pro+,a:b1,b:b0,back:b10,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b2,leftshoulder:b6,leftstick:b13,lefttrigger:b8,leftx:a0,lefty:a1,rightshoulder:b7,rightstick:b14,righttrigger:b9,rightx:a2,righty:a3,start:b11,x:b4,y:b3,platform:Windows,
03000000a00500003232000000000000,8BitDo Zero,a:b0,b:b1,back:b10,dpdown:+a2,dpleft:-a0,dpright:+a0,dpup:-a2,leftshoulder:b6,rightshoulder:b7,start:b11,x:b3,y:b4,platform:Windows,
03000000c82d00001890000000000000,8BitDo Zero 2,a:b1,b:b0,back:b10,leftshoulder:b6,leftx:a0,lefty:a1,rightshoulder:b7,start:b11,x:b4,y:b3,platform:Windows,
03000000c82d00001890000000000000,8BitDo Zero 2,a:b1,b:b0,back:b10,leftshoulder:b6,dpdown:+a1,dpleft:-a0,dpright:+a0,dpup:-a1,rightshoulder:b7,start:b11,x:b4,y:b3,platform:Windows,
03000000c82d00003032000000000000,8BitDo Zero 2,a:b1,b:b0,back:b10,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b6,leftx:a0,lefty:a1,rightshoulder:b7,rightx:a2,righty:a3,start:b11,x:b4,y:b3,platform:Windows,
030000008f0e00001200000000000000,Acme GA02,a:b0,b:b1,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b4,leftstick:b10,lefttrigger:b5,leftx:a0,lefty:a1,rightshoulder:b6,rightstick:b11,righttrigger:b7,rightx:a3,righty:a2,start:b9,x:b2,y:b3,platform:Windows,
03000000c01100000355000000000000,Acrux,a:b1,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b4,leftstick:b10,lefttrigger:b6,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:b7,rightx:a2,righty:a3,start:b9,x:b0,y:b3,platform:Windows,
@@ -91,7 +91,7 @@
03000000120c0000100e000000000000,Armor 3,a:b1,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b4,leftstick:b10,lefttrigger:a3,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:a4,rightx:a2,righty:a5,start:b9,x:b0,y:b3,platform:Windows,
03000000490b00004406000000000000,ASCII Seamic Controller,a:b0,b:b1,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b6,lefttrigger:b7,leftx:a0,lefty:a1,rightshoulder:b5,righttrigger:b2,start:b9,x:b3,y:b4,platform:Windows,
03000000869800002500000000000000,Astro C40 TR PS4 Controller,a:b1,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b4,leftstick:b10,lefttrigger:a3,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:a4,rightx:a2,righty:a5,start:b9,x:b0,y:b3,platform:Windows,
03000000a30c00002700000000000000,Astro City Mini,a:b2,b:b1,back:b8,leftx:a3,lefty:a4,rightshoulder:b4,righttrigger:b5,start:b9,x:b3,y:b0,platform:Windows,
03000000a30c00002700000000000000,Astro City Mini,a:b2,b:b1,back:b8,dpdown:+a4,dpleft:-a3,dpright:+a3,dpup:-a4,rightshoulder:b4,righttrigger:b5,start:b9,x:b3,y:b0,platform:Windows,
03000000a30c00002800000000000000,Astro City Mini,a:b2,b:b1,back:b8,leftx:a3,lefty:a4,rightshoulder:b4,righttrigger:b5,start:b9,x:b3,y:b0,platform:Windows,
03000000e4150000103f000000000000,Batarang,a:b0,b:b1,back:b6,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b4,leftstick:b8,lefttrigger:b10,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b9,righttrigger:b11,rightx:a3,righty:a4,start:b7,x:b2,y:b3,platform:Windows,
03000000d6200000e557000000000000,Batarang PlayStation Controller,a:b1,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b4,leftstick:b10,lefttrigger:b6,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:b7,rightx:a2,righty:a3,start:b9,x:b0,y:b3,platform:Windows,
@@ -117,7 +117,7 @@
030000006b1400000103000000000000,Bigben PS3 Controller,a:b0,b:b1,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b4,leftstick:b10,lefttrigger:b6,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:b7,rightx:a2,righty:a3,start:b9,x:b3,y:b2,platform:Windows,
03000000120c0000200e000000000000,Brook Mars PS4 Controller,a:b1,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b4,leftstick:b10,lefttrigger:b6,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:b7,rightx:a2,righty:a3,start:b9,x:b0,y:b3,platform:Windows,
03000000120c0000210e000000000000,Brook Mars PS4 Controller,a:b1,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b4,leftstick:b10,lefttrigger:b6,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:b7,rightx:a2,righty:a3,start:b9,x:b0,y:b3,platform:Windows,
03000000120c0000310c000000000000,Brook Super Converter,a:b2,b:b1,back:b8,leftshoulder:b6,lefttrigger:b4,leftx:a0,lefty:a1,rightshoulder:b7,righttrigger:b5,start:b9,x:b3,y:b0,platform:Windows,
03000000120c0000310c000000000000,Brook Super Converter,a:b2,b:b1,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b6,lefttrigger:b4,leftx:a0,lefty:a1,rightshoulder:b7,righttrigger:b5,start:b9,x:b3,y:b0,platform:Windows,
03000000d81d00000b00000000000000,Buffalo BSGP1601 Series,a:b5,b:b3,back:b12,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b8,leftstick:b10,lefttrigger:b6,leftx:a0,lefty:a1,rightshoulder:b9,rightstick:b11,righttrigger:b7,rightx:a2,righty:a3,start:b13,x:b4,y:b2,platform:Windows,
030000005b1c00002400000000000000,Capcom Home Arcade Controller,a:b3,b:b4,back:b7,leftshoulder:b2,leftx:a0,lefty:a1,rightshoulder:b5,start:b6,x:b0,y:b1,platform:Windows,
030000005b1c00002500000000000000,Capcom Home Arcade Controller,a:b3,b:b4,back:b7,leftshoulder:b2,leftx:a0,lefty:a1,rightshoulder:b5,start:b6,x:b0,y:b1,platform:Windows,
@@ -178,7 +178,7 @@
03000000151900004000000000000000,Flydigi Vader 2,a:b11,b:b10,back:b3,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b7,leftstick:b1,lefttrigger:b5,leftx:a0,lefty:a1,rightshoulder:b6,rightstick:b0,righttrigger:b4,rightx:a3,righty:a4,start:b2,x:b9,y:b8,platform:Windows,
03000000b40400001124000000000000,Flydigi Vader 2,a:b0,b:b1,back:b10,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b6,leftstick:b12,lefttrigger:b8,leftx:a0,lefty:a1,paddle1:b4,paddle2:b5,paddle4:b17,rightshoulder:b7,rightstick:b13,righttrigger:b9,rightx:a3,righty:a4,start:b11,x:b2,y:b3,platform:Windows,
03000000b40400001224000000000000,Flydigi Vader 2 Pro,a:b0,b:b1,back:b10,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b6,leftstick:b12,lefttrigger:a5,leftx:a0,lefty:a1,paddle1:b15,paddle2:b16,paddle3:b17,paddle4:b18,rightshoulder:b7,rightstick:b13,righttrigger:a4,rightx:a2,righty:a3,start:b11,x:b3,y:b4,platform:Windows,
030000008305000000a0000000000000,G08XU,a:b0,b:b1,back:b4,leftshoulder:b6,leftx:a0,lefty:a1,rightshoulder:b7,start:b5,x:b2,y:b3,platform:Windows,
030000008305000000a0000000000000,G08XU,a:b0,b:b1,back:b4,leftshoulder:b6,dpdown:+a1,dpleft:-a0,dpright:+a0,dpup:-a1,rightshoulder:b7,start:b5,x:b2,y:b3,platform:Windows,
0300000066f700000100000000000000,Game VIB Joystick,a:b2,b:b3,back:b10,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b4,leftstick:b8,lefttrigger:b6,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b9,righttrigger:b7,rightx:a3,righty:a2,start:b11,x:b0,y:b1,platform:Windows,
03000000260900002625000000000000,GameCube Controller,a:b0,b:b1,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b6,lefttrigger:a4,leftx:a0,lefty:a1,righttrigger:a5,rightx:a2,righty:a3,start:b7,x:b2,y:b3,platform:Windows,
03000000341a000005f7000000000000,GameCube Controller,a:b2,b:b3,dpdown:b14,dpleft:b15,dpright:b13,dpup:b12,lefttrigger:a3,leftx:a0,lefty:a1,rightshoulder:b7,righttrigger:a4,rightx:a5,righty:a2,start:b9,x:b1,y:b0,platform:Windows,
@@ -186,7 +186,7 @@
03000000790000004718000000000000,GameCube Controller,a:b1,b:b0,dpdown:b14,dpleft:b15,dpright:b13,dpup:b12,lefttrigger:a3,leftx:a0,lefty:a1,rightshoulder:b7,righttrigger:a4,rightx:a5,righty:a2,start:b9,x:b2,y:b3,platform:Windows,
03000000790000004618000000000000,GameCube Controller Adapter,a:b1,b:b0,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,lefttrigger:a3,leftx:a0,lefty:a1,rightshoulder:b7,righttrigger:a4,rightx:a5,righty:a2,start:b9,x:b2,y:b3,platform:Windows,
030000008f0e00000d31000000000000,Gamepad 3 Turbo,a:b1,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b4,leftstick:b10,lefttrigger:b6,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:b7,rightx:a2,righty:a3,start:b9,x:b0,y:b3,platform:Windows,
03000000280400000140000000000000,GamePad Pro,a:b1,b:b2,back:b8,leftshoulder:b4,lefttrigger:b6,leftx:a0,lefty:a1,rightshoulder:b5,righttrigger:b7,start:b9,x:b0,y:b3,platform:Windows,
03000000280400000140000000000000,GamePad Pro,a:b1,b:b2,back:b8,leftshoulder:b4,lefttrigger:b6,dpdown:+a1,dpleft:-a0,dpright:+a3,dpup:-a4,rightshoulder:b5,righttrigger:b7,start:b9,x:b0,y:b3,platform:Windows,
03000000ac0500003d03000000000000,GameSir G3,a:b0,b:b1,back:b10,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b6,leftstick:b13,lefttrigger:b8,leftx:a0,lefty:a1,rightshoulder:b7,rightstick:b14,righttrigger:b9,rightx:a3,righty:a4,start:b11,x:b3,y:b4,platform:Windows,
03000000ac0500005b05000000000000,GameSir G3w,a:b2,b:b1,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b4,leftstick:b10,lefttrigger:b6,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:b7,rightx:a2,righty:a3,start:b9,x:b3,y:b0,platform:Windows,
03000000ac0500002d02000000000000,GameSir G4,a:b0,b:b1,back:b10,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b6,leftstick:b13,lefttrigger:b8,leftx:a0,lefty:a1,rightshoulder:b7,rightstick:b14,righttrigger:b9,rightx:a2,righty:a5,start:b11,x:b3,y:b4,platform:Windows,
@@ -211,13 +211,11 @@
030000004f04000026b3000000000000,GP XID,a:b0,b:b1,back:b6,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b4,leftstick:b8,lefttrigger:a2,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b9,righttrigger:a5,rightx:a3,righty:a4,start:b7,x:b2,y:b3,platform:Windows,
0300000079000000d418000000000000,GPD Win,a:b0,b:b1,back:b6,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b4,leftstick:b8,lefttrigger:a2,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b9,righttrigger:a5,rightx:a3,righty:a4,start:b7,x:b2,y:b3,platform:Windows,
03000000c6240000025b000000000000,GPX,a:b0,b:b1,back:b6,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b4,leftstick:b8,lefttrigger:a2,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b9,righttrigger:a5,rightx:a3,righty:a4,start:b7,x:b2,y:b3,platform:Windows,
030000007d0400000540000000000000,Gravis Eliminator Pro,a:b1,b:b2,back:b8,leftshoulder:b4,lefttrigger:b6,leftx:a0,lefty:a1,rightshoulder:b5,righttrigger:b7,start:b9,x:b0,y:b3,platform:Windows,
030000007d0400000540000000000000,Gravis Eliminator Pro,a:b1,b:b2,back:b8,leftshoulder:b4,lefttrigger:b6,dpdown:+a1,dpleft:-a0,dpright:+a0,dpup:-a1,rightshoulder:b5,righttrigger:b7,start:b9,x:b0,y:b3,platform:Windows,
030000008f0e00000610000000000000,GreenAsia,a:b2,b:b1,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b6,leftstick:b9,lefttrigger:b4,leftx:a0,lefty:a1,rightshoulder:b7,rightstick:b10,righttrigger:b5,rightx:a5,righty:a2,start:b11,x:b3,y:b0,platform:Windows,
03000000ac0500006b05000000000000,GT2a,a:b0,b:b1,back:b10,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b6,leftstick:b13,lefttrigger:b8,leftx:a0,lefty:a1,rightshoulder:b7,rightstick:b14,righttrigger:b9,rightx:a2,righty:a5,start:b11,x:b3,y:b4,platform:Windows,
03000000341a00000302000000000000,Hama Scorpad,a:b1,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b4,leftstick:b10,lefttrigger:b6,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:b7,rightx:a2,righty:a3,start:b9,x:b0,y:b3,platform:Windows,
030000000d0f00004900000000000000,Hatsune Miku Sho PS3 Controller,a:b1,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b4,leftstick:b10,lefttrigger:b6,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:b7,rightx:a2,righty:a3,start:b9,x:b0,y:b3,platform:Windows,
030000000d0f0000a500000000000000,Hori Miku Project Diva X HD PS4 Controller,a:b1,b:b2,y:b3,x:b0,start:b9,guide:b12,back:b8,leftstick:b10,rightstick:b11,leftshoulder:b4,rightshoulder:b5,dpup:h0.1,dpleft:h0.8,dpdown:h0.4,dpright:h0.2,leftx:a0,lefty:a1,rightx:a2,righty:a5,lefttrigger:b6,righttrigger:b7,platform:Windows,
030000000d0f0000a600000000000000,Hori Miku Project Diva X HD PS4 Controller,a:b1,b:b2,y:b3,x:b0,start:b9,guide:b12,back:b8,leftstick:b10,rightstick:b11,leftshoulder:b4,rightshoulder:b5,dpup:h0.1,dpleft:h0.8,dpdown:h0.4,dpright:h0.2,leftx:a0,lefty:a1,rightx:a2,righty:a3,lefttrigger:b6,righttrigger:b7,platform:Windows,
030000001008000001e1000000000000,Havit HV G60,a:b2,b:b1,back:b8,dpdown:+a1,dpleft:-a0,dpright:+a0,dpup:-a1,leftshoulder:b4,lefttrigger:b6,rightshoulder:b5,righttrigger:b7,start:b9,x:b3,y:b0,platform:Windows,
030000000d0f00000c00000000000000,HEXT,a:b0,b:b1,back:b6,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b4,leftstick:b8,lefttrigger:b10,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b9,righttrigger:b11,rightx:a3,righty:a4,start:b7,x:b2,y:b3,platform:Windows,
03000000d81400000862000000000000,HitBox Edition Cthulhu,a:b1,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b5,lefttrigger:b4,rightshoulder:b7,righttrigger:b6,start:b9,x:b0,y:b3,platform:Windows,
@@ -233,7 +231,6 @@
030000000d0f00008600000000000000,Hori Fighting Commander Xbox 360,a:b0,b:b1,back:b6,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b4,leftstick:b8,lefttrigger:a2,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b9,righttrigger:a5,rightx:a3,righty:a4,start:b7,x:b2,y:b3,platform:Windows,
030000000d0f0000ba00000000000000,Hori Fighting Commander Xbox 360,a:b0,b:b1,back:b6,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b4,leftstick:b8,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b9,rightx:a3,righty:a4,start:b7,x:b2,y:b3,platform:Windows,
030000000d0f00001000000000000000,Hori Fightstick,a:b1,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b4,leftstick:b10,lefttrigger:b6,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:b7,rightx:a2,righty:a5,start:b9,x:b0,y:b3,platform:Windows,
030000000f0d00000010000000000000,Hori Fightstick 3,a:b1,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b4,leftstick:b10,lefttrigger:b6,rightshoulder:b5,rightstick:b11,righttrigger:b7,start:b9,x:b0,y:b3,platform:Windows,
030000000d0f00003200000000000000,Hori Fightstick 3W,a:b1,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b4,leftstick:b10,lefttrigger:b6,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:b7,rightx:a2,righty:a5,start:b9,x:b0,y:b3,platform:Windows,
030000000d0f0000c000000000000000,Hori Fightstick 4,a:b0,b:b1,back:b6,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b4,leftstick:b8,lefttrigger:a2,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b9,righttrigger:a5,rightx:a3,righty:a4,start:b7,x:b2,y:b3,platform:Windows,
030000000d0f00000d00000000000000,Hori Fightstick EX2,a:b0,b:b1,back:b7,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b4,leftstick:b8,lefttrigger:b10,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b9,righttrigger:b11,rightx:a3,righty:a4,start:b6,x:b2,y:b3,platform:Windows,
@@ -244,6 +241,8 @@
030000000d0f00002100000000000000,Hori Fightstick V3,a:b1,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b4,leftstick:b10,lefttrigger:b6,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:b7,rightx:a2,righty:a5,start:b9,x:b0,y:b3,platform:Windows,
030000000d0f00002700000000000000,Hori Fightstick V3,a:b1,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b4,lefttrigger:b6,rightshoulder:b5,righttrigger:b7,start:b9,x:b0,y:b3,platform:Windows,
030000000d0f0000a000000000000000,Hori Grip TAC4,a:b1,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b4,leftstick:b10,lefttrigger:a3,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:a4,rightx:a2,righty:a5,start:b13,x:b0,y:b3,platform:Windows,
030000000d0f0000a500000000000000,Hori Miku Project Diva X HD PS4 Controller,a:b1,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b4,leftstick:b10,lefttrigger:b6,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:b7,rightx:a2,righty:a5,start:b9,x:b0,y:b3,platform:Windows,
030000000d0f0000a600000000000000,Hori Miku Project Diva X HD PS4 Controller,a:b1,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b4,leftstick:b10,lefttrigger:b6,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:b7,rightx:a2,righty:a3,start:b9,x:b0,y:b3,platform:Windows,
030000000d0f00000101000000000000,Hori Mini Hatsune Miku FT,a:b1,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b4,leftstick:b10,lefttrigger:b6,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:b7,rightx:a2,righty:a3,start:b9,x:b0,y:b3,platform:Windows,
030000000d0f00005400000000000000,Hori Pad 3,a:b1,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b4,leftstick:b10,lefttrigger:b6,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:b7,rightx:a2,righty:a3,start:b9,x:b0,y:b3,platform:Windows,
030000000d0f00000900000000000000,Hori Pad 3 Turbo,a:b1,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b4,leftstick:b10,lefttrigger:b6,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:b7,rightx:a2,righty:a3,start:b9,x:b0,y:b3,platform:Windows,
@@ -285,7 +284,7 @@
030000000d0f00006700000000000000,Horipad One,a:b0,b:b1,back:b6,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b4,leftstick:b9,lefttrigger:a2,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b10,righttrigger:a5,rightx:a3,righty:a4,start:b7,x:b2,y:b3,platform:Windows,
030000000d0f0000dc00000000000000,Horipad Switch,a:b0,b:b1,back:b6,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b4,leftstick:b8,lefttrigger:a2,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b9,righttrigger:a5,rightx:a3,righty:a4,start:b7,x:b2,y:b3,platform:Windows,
030000008f0e00001330000000000000,HuiJia SNES Controller,a:b2,b:b1,back:b8,dpdown:+a1,dpleft:-a0,dpright:+a0,dpup:-a1,leftshoulder:b6,rightshoulder:b7,start:b9,x:b3,y:b0,platform:Windows,
03000000790000004e95000000000000,Hyperkin N64 Adapter,a:b1,b:b2,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b7,lefttrigger:b4,leftx:a0,lefty:a1,rightshoulder:b5,rightx:a5,righty:a2,start:b9,platform:Windows,
03000000790000004e95000000000000,Hyperkin N64 Controller Adapter,a:b1,b:b2,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b7,lefttrigger:b4,leftx:a0,lefty:a1,rightshoulder:b5,rightx:a5,righty:a2,start:b9,platform:Windows,
03000000d81d00000e00000000000000,iBuffalo AC02 Arcade Joystick,a:b0,b:b1,back:b9,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b6,leftstick:b10,lefttrigger:b7,leftx:a0,lefty:a1,rightshoulder:b2,rightstick:b11,righttrigger:b3,rightx:a2,righty:a5,start:b8,x:b4,y:b5,platform:Windows,
03000000d81d00000f00000000000000,iBuffalo BSGP1204 Series,a:b2,b:b1,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b6,leftstick:b10,lefttrigger:b4,leftx:a0,lefty:a1,rightshoulder:b7,rightstick:b11,righttrigger:b5,rightx:a2,righty:a3,start:b9,x:b3,y:b0,platform:Windows,
03000000d81d00001000000000000000,iBuffalo BSGP1204P Series,a:b2,b:b1,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b6,leftstick:b10,lefttrigger:b4,leftx:a0,lefty:a1,rightshoulder:b7,rightstick:b11,righttrigger:b5,rightx:a2,righty:a3,start:b9,x:b3,y:b0,platform:Windows,
@@ -293,9 +292,10 @@
03000000696400006964000000000000,iDroidCon Controller,a:b1,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b4,leftstick:b10,lefttrigger:b6,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:b7,rightx:a2,righty:a3,start:b9,x:b0,y:b3,platform:Windows,
03000000b50700001403000000000000,Impact Black,a:b2,b:b3,back:b8,leftshoulder:b4,leftstick:b10,lefttrigger:b5,leftx:a0,lefty:a1,rightshoulder:b6,rightstick:b11,righttrigger:b7,rightx:a3,righty:a2,start:b9,x:b0,y:b1,platform:Windows,
030000006f0e00002401000000000000,Injustice Fightstick PS3 Controller,a:b1,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b4,lefttrigger:b6,rightshoulder:b5,righttrigger:b7,start:b9,x:b0,y:b3,platform:Windows,
03000000830500005130000000000000,InterAct ActionPad,a:b0,b:b1,back:b8,leftshoulder:b6,lefttrigger:b7,leftx:a0,lefty:a1,rightshoulder:b5,righttrigger:b2,start:b9,x:b3,y:b4,platform:Windows,
03000000830500005130000000000000,InterAct ActionPad,a:b0,b:b1,back:b8,leftshoulder:b6,lefttrigger:b7,dpdown:+a1,dpleft:-a0,dpright:+a0,dpup:-a1,rightshoulder:b5,righttrigger:b2,start:b9,x:b3,y:b4,platform:Windows,
03000000ef0500000300000000000000,InterAct AxisPad,a:b2,b:b3,back:b10,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b4,leftstick:b8,lefttrigger:b6,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b9,righttrigger:b7,rightx:a3,righty:a2,start:b11,x:b0,y:b1,platform:Windows,
03000000fd0500000230000000000000,InterAct AxisPad,a:b2,b:b3,back:b10,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b4,leftstick:b8,lefttrigger:b6,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b9,righttrigger:b7,rightx:a2,righty:a5,start:b11,x:b0,y:b1,platform:Windows,
03000000fd0500000030000000000000,Interact GoPad,a:b3,b:b4,x:b0,y:b1,leftshoulder:b6,rightshoulder:b2,leftx:a0,lefty:a1,lefttrigger:b7,righttrigger:b5,platform:Windows,
03000000fd0500003902000000000000,InterAct Hammerhead,a:b3,b:b4,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b6,leftstick:b2,lefttrigger:b8,rightshoulder:b7,rightstick:b5,righttrigger:b9,start:b10,x:b0,y:b1,platform:Windows,
03000000fd0500002a26000000000000,InterAct Hammerhead FX,a:b3,b:b4,back:b10,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b6,lefttrigger:b8,leftx:a0,lefty:a1,rightshoulder:b7,righttrigger:b9,rightx:a2,righty:a5,start:b11,x:b0,y:b1,platform:Windows,
03000000fd0500002f26000000000000,InterAct Hammerhead FX,a:b4,b:b5,back:b10,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b6,lefttrigger:b8,leftx:a0,lefty:a1,rightshoulder:b7,righttrigger:b9,rightx:a2,righty:a5,start:b11,x:b1,y:b2,platform:Windows,
@@ -344,7 +344,6 @@
03000000380700008483000000000000,Mad Catz Fightstick TE S PS4,a:b1,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b4,leftstick:b10,lefttrigger:a3,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:a4,rightx:a2,righty:a5,start:b9,x:b0,y:b3,platform:Windows,
03000000380700008134000000000000,Mad Catz Fightstick TE2 PS3,a:b1,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b7,leftstick:b10,lefttrigger:b6,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:b4,rightx:a2,righty:a3,start:b9,x:b0,y:b3,platform:Windows,
03000000380700008184000000000000,Mad Catz Fightstick TE2 PS4,a:b1,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b5,leftstick:b10,lefttrigger:a4,leftx:a0,lefty:a1,rightshoulder:b4,rightstick:b11,righttrigger:b7,rightx:a2,righty:a5,start:b9,x:b0,y:b3,platform:Windows,
78696e70757403000000000000000000,Mad Catz Fightstick TES,a:b0,b:b1,back:b6,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b10,leftshoulder:b4,lefttrigger:a2,rightshoulder:b5,righttrigger:a5,start:b7,x:b2,y:b3,platform:Windows,
03000000380700006252000000000000,Mad Catz Micro CTRLR,a:b1,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b4,leftstick:b10,lefttrigger:b6,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:b7,rightx:a3,righty:a4,start:b9,x:b0,y:b3,platform:Windows,
03000000380700008232000000000000,Mad Catz PlayStation Brawlpad,a:b1,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b4,lefttrigger:b6,leftx:a0,lefty:a1,rightshoulder:b5,righttrigger:b7,rightx:a2,righty:a3,start:b9,x:b0,y:b3,platform:Windows,
03000000380700008731000000000000,Mad Catz PlayStation Fightstick,a:b1,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b4,lefttrigger:b6,leftx:a0,lefty:a1,rightshoulder:b5,righttrigger:b7,rightx:a2,righty:a3,start:b9,x:b0,y:b3,platform:Windows,
@@ -365,11 +364,12 @@
0300000079000000d218000000000000,Mayflash Magic NS,a:b2,b:b1,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b4,leftstick:b10,lefttrigger:b6,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:b7,rightx:a2,righty:a3,start:b9,x:b3,y:b0,platform:Windows,
03000000d620000010a7000000000000,Mayflash Magic NS,a:b1,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b4,leftstick:b10,lefttrigger:b6,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:b7,rightx:a2,righty:a3,start:b9,x:b0,y:b3,platform:Windows,
030000008f0e00001030000000000000,Mayflash Sega Saturn Adapter,a:b0,b:b1,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b6,lefttrigger:b5,rightshoulder:b2,righttrigger:b7,start:b9,x:b3,y:b4,platform:Windows,
0300000025090000e803000000000000,Mayflash Wii Classic Controller,a:b1,b:b0,back:b8,dpdown:b13,dpleft:b12,dpright:b14,dpup:b11,guide:b10,leftshoulder:b4,lefttrigger:b6,leftx:a0,lefty:a1,rightshoulder:b5,righttrigger:b7,rightx:a2,righty:a3,start:b9,x:b3,y:b2,platform:Windows,
03000000790000000018000000000000,Mayflash WiiU Pro Adapter,a:b1,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b4,leftstick:b10,lefttrigger:b6,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:b7,rightx:a2,righty:a3,start:b9,x:b0,y:b3,platform:Windows,
0300000025090000e803000000000000,Mayflash Wii Classic Adapter,a:b1,b:b0,back:b8,dpdown:b13,dpleft:b12,dpright:b14,dpup:b11,guide:b10,leftshoulder:b4,lefttrigger:b6,leftx:a0,lefty:a1,rightshoulder:b5,righttrigger:b7,rightx:a2,righty:a3,start:b9,x:b3,y:b2,platform:Windows,
03000000790000000318000000000000,Mayflash Wii DolphinBar,a:b2,b:b3,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b11,leftshoulder:b4,lefttrigger:b6,leftx:a0,lefty:a1,rightshoulder:b5,righttrigger:b7,rightx:a2,righty:a3,start:b9,x:b0,y:b1,platform:Windows,
03000000790000000018000000000000,Mayflash Wii U Pro Adapter,a:b1,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b4,leftstick:b10,lefttrigger:b6,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:b7,rightx:a2,righty:a3,start:b9,x:b0,y:b3,platform:Windows,
03000000790000002418000000000000,Mega Drive Controller,a:b0,b:b1,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b6,rightshoulder:b2,start:b9,x:b3,y:b4,platform:Windows,
0300000079000000ae18000000000000,Mega Drive Controller,a:b0,b:b1,back:b7,dpdown:b14,dpleft:b15,dpright:b13,dpup:b2,rightshoulder:b6,righttrigger:b2,start:b9,x:b3,y:b4,platform:Windows,
03000000c0160000990a000000000000,Mega Drive Controller,a:b0,b:b1,leftx:a0,lefty:a1,righttrigger:b2,start:b3,platform:Windows,
03000000c0160000990a000000000000,Mega Drive Controller,a:b0,b:b1,dpdown:+a1,dpleft:-a0,dpright:+a0,dpup:-a1,righttrigger:b2,start:b3,platform:Windows,
030000005e0400002800000000000000,Microsoft Dual Strike,a:b3,b:b2,back:b4,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b6,lefttrigger:b8,rightshoulder:b7,rightx:a0,righty:a1~,start:b5,x:b1,y:b0,platform:Windows,
030000005e0400000300000000000000,Microsoft SideWinder,a:b0,b:b1,back:b9,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b6,lefttrigger:b7,leftx:a0,lefty:a1,rightshoulder:b5,righttrigger:b2,start:b8,x:b3,y:b4,platform:Windows,
030000005e0400000700000000000000,Microsoft SideWinder,a:b0,b:b1,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b6,lefttrigger:b7,leftx:a0,lefty:a1,rightshoulder:b5,righttrigger:b2,start:b9,x:b3,y:b4,platform:Windows,
@@ -389,6 +389,7 @@
03000000c62400001b89000000000000,Moga XP5X Plus,a:b0,b:b1,back:b10,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b6,leftstick:b13,lefttrigger:b8,leftx:a0,lefty:a1,rightshoulder:b7,rightstick:b14,righttrigger:b9,rightx:a3,righty:a4,start:b11,x:b3,y:b4,platform:Windows,
03000000efbe0000edfe000000000000,Monect Virtual Controller,a:b2,b:b1,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b4,leftstick:b10,lefttrigger:a2,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:a5,rightx:a3,righty:a4,start:b9,x:b3,y:b0,platform:Windows,
03000000250900006688000000000000,MP-8866 Super Dual Box,a:b2,b:b1,back:b9,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b6,leftstick:b10,lefttrigger:b4,leftx:a0,lefty:a1,rightshoulder:b7,rightstick:b11,righttrigger:b5,rightx:a2,righty:a3,start:b8,x:b3,y:b0,platform:Windows,
03000000091200004488000000000000,MUSIA PlayStation 2 Input Display,a:b0,b:b2,back:b4,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b8,leftstick:b6,lefttrigger:b10,leftx:a0,lefty:a1,rightshoulder:b9,rightstick:b7,righttrigger:b11,rightx:a2,righty:a3,start:b5,x:b1,y:b3,platform:Windows,
03000000f70600000100000000000000,N64 Adaptoid,+rightx:b2,+righty:b1,-rightx:b4,-righty:b5,a:b0,b:b3,dpdown:b11,dpleft:b12,dpright:b13,dpup:b10,leftshoulder:b6,lefttrigger:b9,leftx:a0,lefty:a1,rightshoulder:b7,start:b8,platform:Windows,
030000006b140000010c000000000000,Nacon GC 400ES,a:b0,b:b1,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b4,leftstick:b10,lefttrigger:b6,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:b7,rightx:a2,righty:a3,start:b9,x:b2,y:b3,platform:Windows,
030000006b1400001106000000000000,Nacon Revolution 3 PS4 Controller,a:b1,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b4,leftstick:b10,lefttrigger:b6,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:b7,rightx:a2,righty:a5,start:b9,x:b0,y:b3,platform:Windows,
@@ -399,9 +400,8 @@
0300000038070000efbe000000000000,NEO SE,a:b0,b:b1,back:b6,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b4,leftstick:b8,lefttrigger:a2,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b9,righttrigger:a2,rightx:a3,righty:a4,start:b7,x:b2,y:b3,platform:Windows,
0300000092120000474e000000000000,NeoGeo X Arcade Stick,a:b0,b:b1,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,start:b9,x:b3,y:b2,platform:Windows,
03000000921200004b46000000000000,NES 2 port Adapter,a:b1,b:b0,back:b10,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,start:b11,platform:Windows,
03000000000f00000100000000000000,NES Controller,a:b1,b:b0,back:b2,leftx:a0,lefty:a1,start:b3,platform:Windows,
03000000571d00002100000000000000,NES Controller,a:b0,b:b1,back:b2,leftx:a0,lefty:a1,start:b3,platform:Windows,
03000000921200004346000000000000,NES Controller,a:b0,b:b1,back:b2,leftx:a0,lefty:a1,start:b3,platform:Windows,
03000000000f00000100000000000000,NES Controller,a:b1,b:b0,back:b2,dpdown:+a1,dpleft:-a0,dpright:+a0,dpup:-a1,start:b3,platform:Windows,
03000000921200004346000000000000,NES Controller,a:b0,b:b1,back:b2,dpdown:+a1,dpleft:-a0,dpright:+a0,dpup:-a1,start:b3,platform:Windows,
03000000790000004518000000000000,NEXILUX GameCube Controller Adapter,a:b1,b:b0,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,lefttrigger:a3,leftx:a0,lefty:a1,rightshoulder:b7,righttrigger:a4,rightx:a5,righty:a2,start:b9,x:b2,y:b3,platform:Windows,
030000001008000001e5000000000000,NEXT SNES Controller,a:b2,b:b1,back:b8,dpdown:+a1,dpleft:-a0,dpright:+a0,dpup:-a1,leftshoulder:b4,rightshoulder:b5,righttrigger:b6,start:b9,x:b3,y:b0,platform:Windows,
03000000050b00000045000000000000,Nexus,a:b0,b:b1,back:b9,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b4,leftstick:b7,lefttrigger:a5,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b8,righttrigger:a4,rightx:a2,righty:a3,start:b10,x:b2,y:b3,platform:Windows,
@@ -503,7 +503,7 @@
03000000222c00000023000000000000,Qanba Obsidian Arcade Stick PS4,a:b1,b:b2,back:b13,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b4,leftstick:b10,lefttrigger:a3,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:a4,rightx:a2,righty:a5,start:b9,x:b0,y:b3,platform:Windows,
030000008a2400006682000000000000,R1 Mobile Controller,a:b3,b:b1,back:b7,leftx:a0,lefty:a1,start:b6,x:b4,y:b0,platform:Windows,
03000000086700006626000000000000,RadioShack,a:b2,b:b1,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b4,leftstick:b10,lefttrigger:b6,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:b7,rightx:a2,righty:a5,start:b9,x:b3,y:b0,platform:Windows,
03000000ff1100004733000000000000,Ramox FPS Controller,a:b2,b:b1,x:b3,y:b0,back:b8,start:b9,leftstick:b10,leftshoulder:b4,rightshoulder:b5,dpup:h0.1,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,leftx:a0,lefty:a1,lefttrigger:b6,righttrigger:b7,platform:Windows,
03000000ff1100004733000000000000,Ramox FPS Controller,a:b2,b:b1,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b4,leftstick:b10,lefttrigger:b6,leftx:a0,lefty:a1,rightshoulder:b5,righttrigger:b7,start:b9,x:b3,y:b0,platform:Windows,
030000009b2800002300000000000000,Raphnet 3DO Adapter,a:b0,b:b1,back:b4,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b6,lefttrigger:b5,leftx:a0,lefty:a1,rightshoulder:b2,start:b3,platform:Windows,
030000009b2800006900000000000000,Raphnet 3DO Adapter,a:b0,b:b1,back:b4,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b6,lefttrigger:b5,leftx:a0,lefty:a1,rightshoulder:b2,start:b3,platform:Windows,
030000009b2800000800000000000000,Raphnet Dreamcast Adapter,a:b2,b:b1,dpdown:b5,dpleft:b6,dpright:b7,dpup:b4,lefttrigger:a2,leftx:a0,righttrigger:a3,righty:a1,start:b3,x:b10,y:b9,platform:Windows,
@@ -511,12 +511,12 @@
030000009b2800006000000000000000,Raphnet GC and N64 Adapter,a:b0,b:b7,dpdown:b11,dpleft:b12,dpright:b13,dpup:b10,lefttrigger:+a5,leftx:a0,lefty:a1,rightshoulder:b2,righttrigger:+a2,rightx:a3,righty:a4,start:b3,x:b1,y:b8,platform:Windows,
030000009b2800001800000000000000,Raphnet Jaguar Adapter,a:b2,b:b1,back:b4,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b13,lefttrigger:b8,leftx:a0,lefty:a1,rightshoulder:b0,righttrigger:b10,start:b3,x:b11,y:b12,platform:Windows,
030000009b2800000200000000000000,Raphnet NES Adapter,a:b7,b:b6,back:b5,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftx:a0,lefty:a1,start:b4,platform:Windows,
030000009b2800004400000000000000,Raphnet PS1 and PS2 Adapter,a:b1,b:b2,x:b0,y:b3,back:b5,start:b4,leftstick:b10,rightstick:b11,leftshoulder:b6,rightshoulder:b7,dpup:b12,dpdown:b13,dpleft:b14,dpright:b15,leftx:a0,lefty:a1,rightx:a3,righty:a4,lefttrigger:b8,righttrigger:b9,platform:Windows,
030000009b2800004400000000000000,Raphnet PS1 and PS2 Adapter,a:b1,b:b2,back:b5,dpdown:b13,dpleft:b14,dpright:b15,dpup:b12,leftshoulder:b6,leftstick:b10,lefttrigger:b8,leftx:a0,lefty:a1,rightshoulder:b7,rightstick:b11,righttrigger:b9,rightx:a3,righty:a4,start:b4,x:b0,y:b3,platform:Windows,
030000009b2800004300000000000000,Raphnet Saturn,a:b0,b:b1,dpdown:b13,dpleft:b14,dpright:b15,dpup:b12,leftshoulder:b6,lefttrigger:b7,leftx:a0,lefty:a1,rightshoulder:b5,righttrigger:b2,start:b8,x:b3,y:b4,platform:Windows,
030000009b2800000500000000000000,Raphnet Saturn Adapter 2.0,a:b1,b:b2,leftshoulder:b6,lefttrigger:b4,leftx:a0,lefty:a1,rightshoulder:b7,righttrigger:b5,start:b9,x:b0,y:b3,platform:Windows,
030000009b2800000500000000000000,Raphnet Saturn Adapter 2.0,a:b1,b:b2,leftshoulder:b6,lefttrigger:b4,dpdown:+a1,dpleft:-a0,dpright:+a0,dpup:-a1,rightshoulder:b7,righttrigger:b5,start:b9,x:b0,y:b3,platform:Windows,
030000009b2800000300000000000000,Raphnet SNES Adapter,a:b0,b:b4,back:b2,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b6,leftx:a0,lefty:a1,rightshoulder:b7,start:b3,x:b1,y:b5,platform:Windows,
030000009b2800005600000000000000,Raphnet SNES Adapter,a:b1,b:b4,back:b2,leftshoulder:b6,leftx:a0,lefty:a1,rightshoulder:b7,start:b3,x:b0,y:b5,platform:Windows,
030000009b2800005700000000000000,Raphnet SNES Adapter,a:b1,b:b4,back:b2,leftshoulder:b6,leftx:a0,lefty:a1,rightshoulder:b7,start:b3,x:b0,y:b5,platform:Windows,
030000009b2800005600000000000000,Raphnet SNES Adapter,a:b1,b:b4,back:b2,leftshoulder:b6,dpdown:+a1,dpleft:-a0,dpright:+a0,dpup:-a1,rightshoulder:b7,start:b3,x:b0,y:b5,platform:Windows,
030000009b2800005700000000000000,Raphnet SNES Adapter,a:b1,b:b4,back:b2,leftshoulder:b6,dpdown:+a1,dpleft:-a0,dpright:+a0,dpup:-a1,rightshoulder:b7,start:b3,x:b0,y:b5,platform:Windows,
030000009b2800001e00000000000000,Raphnet Vectrex Adapter,a:b0,b:b1,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftx:a1,lefty:a2,x:b2,y:b3,platform:Windows,
030000009b2800002b00000000000000,Raphnet Wii Classic Adapter,a:b1,b:b4,back:b2,dpdown:b13,dpleft:b14,dpright:b15,dpup:b12,leftshoulder:b6,lefttrigger:b8,leftx:a0,lefty:a1,rightshoulder:b7,righttrigger:b9,rightx:a3,righty:a4,start:b3,x:b0,y:b5,platform:Windows,
030000009b2800002c00000000000000,Raphnet Wii Classic Adapter,a:b1,b:b4,back:b2,dpdown:b13,dpleft:b14,dpright:b15,dpup:b12,leftshoulder:b6,lefttrigger:b8,leftx:a0,lefty:a1,rightshoulder:b7,righttrigger:b9,rightx:a3,righty:a4,start:b3,x:b0,y:b5,platform:Windows,
@@ -532,12 +532,13 @@
03000000321500000910000000000000,Razer Raiju UE,a:b1,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b4,leftstick:b10,lefttrigger:a3,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:a4,rightx:a2,righty:a5,start:b9,x:b0,y:b3,platform:Windows,
03000000321500000011000000000000,Razer Raion PS4 Fightpad,a:b1,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b4,leftstick:b10,lefttrigger:a3,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:a4,rightx:a2,righty:a5,start:b9,x:b0,y:b3,platform:Windows,
03000000321500000009000000000000,Razer Serval,+lefty:+a2,-lefty:-a1,a:b0,b:b1,back:b12,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b10,leftshoulder:b4,leftstick:b8,leftx:a0,rightshoulder:b5,rightstick:b9,rightx:a3,righty:a4,start:b7,x:b2,y:b3,platform:Windows,
03000000921200004547000000000000,Retro Bit Sega Genesis Controller Adapter,a:b0,b:b1,x:b3,y:b4,start:b6,rightshoulder:b5,dpup:-a1,dpdown:+a1,dpleft:-a0,dpright:+a0,lefttrigger:b7,righttrigger:b2,platform:Windows,
03000000790000001100000000000000,Retro Controller,a:b1,b:b2,back:b8,dpdown:+a4,dpleft:-a3,dpright:+a3,dpup:-a4,leftshoulder:b6,lefttrigger:b7,rightshoulder:b4,righttrigger:b5,start:b9,x:b0,y:b3,platform:Windows,
03000000830500006020000000000000,Retro Controller,a:b0,b:b1,back:b6,dpdown:+a1,dpleft:-a0,dpright:+a0,dpup:-a1,leftshoulder:b4,lefttrigger:b5,rightshoulder:b8,righttrigger:b9,start:b7,x:b2,y:b3,platform:Windows,
03000000bd12000013d0000000000000,Retrolink Sega Saturn Classic Controller,a:b0,b:b1,dpdown:+a1,dpleft:-a0,dpright:+a0,dpup:-a1,leftshoulder:b5,lefttrigger:b6,rightshoulder:b2,righttrigger:b7,start:b8,x:b3,y:b4,platform:Windows,
03000000bd12000015d0000000000000,Retrolink SNES Controller,a:b2,b:b1,back:b8,dpdown:+a1,dpleft:-a0,dpright:+a0,dpup:-a1,leftshoulder:b4,rightshoulder:b5,start:b9,x:b3,y:b0,platform:Windows,
0300000000f000000300000000000000,RetroUSB RetroPad,a:b1,b:b5,back:b2,leftshoulder:b6,leftx:a0,lefty:a1,rightshoulder:b7,start:b3,x:b0,y:b4,platform:Windows,
0300000000f00000f100000000000000,RetroUSB Super RetroPort,a:b1,b:b5,back:b2,leftshoulder:b6,leftx:a0,lefty:a1,rightshoulder:b7,start:b3,x:b0,y:b4,platform:Windows,
0300000000f000000300000000000000,RetroUSB RetroPad,a:b1,b:b5,back:b2,leftshoulder:b6,dpdown:+a1,dpleft:-a0,dpright:+a0,dpup:-a1,rightshoulder:b7,start:b3,x:b0,y:b4,platform:Windows,
0300000000f00000f100000000000000,RetroUSB Super RetroPort,a:b1,b:b5,back:b2,leftshoulder:b6,dpdown:+a1,dpleft:-a0,dpright:+a0,dpup:-a1,rightshoulder:b7,start:b3,x:b0,y:b4,platform:Windows,
03000000830500000960000000000000,Revenger,a:b0,b:b1,leftshoulder:b6,lefttrigger:b7,leftx:a0,lefty:a1,rightshoulder:b2,righttrigger:b3,x:b4,y:b5,platform:Windows,
030000006b140000010d000000000000,Revolution Pro Controller,a:b1,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b4,leftstick:b10,lefttrigger:a3,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:a4,rightx:a2,righty:a5,start:b9,x:b0,y:b3,platform:Windows,
030000006b140000020d000000000000,Revolution Pro Controller 2,a:b1,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b4,leftstick:b10,lefttrigger:a3,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:a4,rightx:a2,righty:a5,start:b9,x:b0,y:b3,platform:Windows,
@@ -579,25 +580,25 @@
03000000411200004550000000000000,Sanwa Micro Grip Pro,a:b0,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b4,leftstick:b10,lefttrigger:b5,leftx:a0,lefty:a1,rightshoulder:b6,rightstick:b11,righttrigger:b7,rightx:a1,righty:a2,start:b9,x:b1,y:b3,platform:Windows,
03000000c01100004150000000000000,Sanwa Micro Grip Pro,a:b0,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b4,leftstick:b10,lefttrigger:b5,leftx:a0,lefty:a1,rightshoulder:b6,rightstick:b11,righttrigger:b7,rightx:a2,righty:a3,start:b9,x:b1,y:b3,platform:Windows,
03000000c01100004450000000000000,Sanwa Online Grip,a:b0,b:b1,back:b12,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b6,leftstick:b10,lefttrigger:b7,leftx:a0,lefty:a1,rightshoulder:b8,rightstick:b11,righttrigger:b9,rightx:a3,righty:a2,start:b14,x:b3,y:b4,platform:Windows,
03000000730700000401000000000000,Sanwa PlayOnline Mobile,a:b0,b:b1,back:b2,leftx:a0,lefty:a1,start:b3,platform:Windows,
03000000730700000401000000000000,Sanwa PlayOnline Mobile,a:b0,b:b1,back:b2,dpdown:+a1,dpleft:-a0,dpright:+a0,dpup:-a1,start:b3,platform:Windows,
03000000830500006120000000000000,Sanwa Smart Grip II,a:b0,b:b2,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b4,lefttrigger:b6,leftx:a0,lefty:a1,rightshoulder:b5,righttrigger:b7,x:b1,y:b3,platform:Windows,
03000000c01100000051000000000000,Satechi Controller,a:b0,b:b1,back:b10,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b6,leftstick:b13,lefttrigger:b8,leftx:a0,lefty:a1,rightshoulder:b7,rightstick:b14,righttrigger:b9,rightx:a2,righty:a5,start:b11,x:b3,y:b4,platform:Windows,
030000004f04000028b3000000000000,Score A,a:b1,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b4,leftstick:b10,lefttrigger:b6,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:b7,rightx:a2,righty:a5,start:b9,x:b0,y:b3,platform:Windows,
03000000952e00002577000000000000,Scuf PS4 Controller,a:b1,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b4,leftstick:b10,lefttrigger:a3,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:a4,rightx:a2,righty:a5,start:b9,x:b0,y:b3,platform:Windows,
03000000a30c00002500000000000000,Sega Genesis Mini 3B Controller,a:b2,b:b1,dpdown:+a4,dpleft:-a3,dpright:+a3,dpup:-a4,righttrigger:b5,start:b9,platform:Windows,
03000000a30c00002400000000000000,Sega Mega Drive Mini 6B Controller,a:b2,b:b1,dpdown:+a4,dpleft:-a3,dpright:+a3,dpup:-a4,rightshoulder:b4,righttrigger:b5,start:b9,x:b3,y:b0,platform:Windows,
0300000000050000289b000000000000,Sega Saturn Adapter,a:b1,b:b2,leftshoulder:b6,lefttrigger:b4,leftx:a0,lefty:a1,rightshoulder:b7,righttrigger:b5,start:b9,x:b0,y:b3,platform:Windows,
0300000000f000000800000000000000,Sega Saturn Controller,a:b1,b:b2,leftx:a0,lefty:a1,rightshoulder:b7,righttrigger:b3,start:b0,x:b5,y:b6,platform:Windows,
0300000000050000289b000000000000,Sega Saturn Adapter,a:b1,b:b2,leftshoulder:b6,lefttrigger:b4,dpdown:+a1,dpleft:-a0,dpright:+a0,dpup:-a1,rightshoulder:b7,righttrigger:b5,start:b9,x:b0,y:b3,platform:Windows,
0300000000f000000800000000000000,Sega Saturn Controller,a:b1,b:b2,dpdown:+a1,dpleft:-a0,dpright:+a0,dpup:-a1,rightshoulder:b7,righttrigger:b3,start:b0,x:b5,y:b6,platform:Windows,
03000000730700000601000000000000,Sega Saturn Controller,a:b0,b:b1,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b6,lefttrigger:b7,leftx:a0,lefty:a1,rightshoulder:b5,righttrigger:b2,start:b9,x:b3,y:b4,platform:Windows,
03000000b40400000a01000000000000,Sega Saturn Controller,a:b0,b:b1,leftshoulder:b6,lefttrigger:b7,leftx:a0,lefty:a1,rightshoulder:b5,righttrigger:b2,start:b8,x:b3,y:b4,platform:Windows,
03000000b40400000a01000000000000,Sega Saturn Controller,a:b0,b:b1,leftshoulder:b6,lefttrigger:b7,dpdown:+a1,dpleft:-a0,dpright:+a0,dpup:-a1,rightshoulder:b5,righttrigger:b2,start:b8,x:b3,y:b4,platform:Windows,
030000003b07000004a1000000000000,SFX,a:b0,b:b2,back:b7,leftshoulder:b6,lefttrigger:b4,leftx:a0,lefty:a1,rightshoulder:b9,righttrigger:b5,start:b8,x:b1,y:b3,platform:Windows,
03000000f82100001900000000000000,Shogun Bros Chameleon X1,a:b2,b:b1,back:b9,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b6,lefttrigger:b4,leftx:a0,lefty:a1,rightshoulder:b7,righttrigger:b5,rightx:a2,righty:a3,start:b8,x:b3,y:b0,platform:Windows,
03000000120c00001c1e000000000000,SnakeByte 4S PS4 Controller,a:b1,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b4,leftstick:b10,lefttrigger:a3,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:a4,rightx:a2,righty:a5,start:b9,x:b0,y:b3,platform:Windows,
0300000003040000c197000000000000,SNES Controller,a:b0,b:b4,back:b2,leftshoulder:b6,leftx:a0,lefty:a1,rightshoulder:b7,start:b3,x:b1,y:b5,platform:Windows,
03000000571d00002000000000000000,SNES Controller,a:b0,b:b1,back:b6,leftshoulder:b4,leftx:a0,lefty:a1,rightshoulder:b5,start:b7,x:b2,y:b3,platform:Windows,
0300000081170000960a000000000000,SNES Controller,a:b4,b:b0,back:b2,leftshoulder:b6,leftx:a0,lefty:a1,rightshoulder:b7,start:b3,x:b5,y:b1,platform:Windows,
0300000003040000c197000000000000,SNES Controller,a:b0,b:b4,back:b2,leftshoulder:b6,dpdown:+a1,dpleft:-a0,dpright:+a0,dpup:-a1,rightshoulder:b7,start:b3,x:b1,y:b5,platform:Windows,
0300000081170000960a000000000000,SNES Controller,a:b4,b:b0,back:b2,leftshoulder:b6,dpdown:+a1,dpleft:-a0,dpright:+a0,dpup:-a1,rightshoulder:b7,start:b3,x:b5,y:b1,platform:Windows,
03000000811700009d0a000000000000,SNES Controller,a:b0,b:b4,back:b2,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b6,leftx:a0,lefty:a1,rightshoulder:b7,start:b3,x:b1,y:b5,platform:Windows,
030000008b2800000300000000000000,SNES Controller,a:b0,b:b4,back:b2,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b6,leftx:a0,lefty:a1,rightshoulder:b7,start:b3,x:b1,y:b5,platform:Windows,
03000000921200004653000000000000,SNES Controller,a:b0,b:b4,back:b2,leftshoulder:b6,leftx:a0,lefty:a1,rightshoulder:b7,start:b3,x:b1,y:b5,platform:Windows,
03000000921200004653000000000000,SNES Controller,a:b0,b:b4,back:b2,leftshoulder:b6,dpdown:+a1,dpleft:-a0,dpright:+a0,dpup:-a1,rightshoulder:b7,start:b3,x:b1,y:b5,platform:Windows,
03000000ff000000cb01000000000000,Sony PlayStation Portable,a:b0,b:b1,back:b6,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b4,leftx:a0,lefty:a1,rightshoulder:b5,start:b7,x:b2,y:b3,platform:Windows,
03000000341a00000208000000000000,Speedlink 6555,a:b0,b:b1,back:b6,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b4,leftstick:b8,lefttrigger:-a4,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b9,righttrigger:a4,rightx:a3,righty:a2,start:b7,x:b2,y:b3,platform:Windows,
03000000341a00000908000000000000,Speedlink 6566,a:b0,b:b1,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b4,leftstick:b10,lefttrigger:b6,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:b7,rightx:a2,righty:a3,start:b9,x:b2,y:b3,platform:Windows,
@@ -611,18 +612,20 @@
03000000110100001914000000000000,SteelSeries,a:b0,b:b1,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftstick:b13,lefttrigger:b6,leftx:a0,lefty:a1,rightstick:b14,righttrigger:b7,rightx:a3,righty:a4,start:b11,x:b3,y:b4,platform:Windows,
03000000381000001214000000000000,SteelSeries Free,a:b0,b:b1,back:b12,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b6,leftx:a0,lefty:a1,rightshoulder:b7,rightx:a2,righty:a3,start:b11,x:b3,y:b4,platform:Windows,
03000000110100003114000000000000,SteelSeries Stratus Duo,a:b0,b:b1,back:b10,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b6,leftstick:b13,lefttrigger:b8,leftx:a0,lefty:a1,rightshoulder:b7,rightstick:b14,righttrigger:b9,rightx:a3,righty:a4,start:b11,x:b3,y:b4,platform:Windows,
03000000381000003014000000000000,SteelSeries Stratus Duo,a:b0,b:b1,back:b6,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b10,leftshoulder:b4,leftstick:b8,lefttrigger:a2,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b9,righttrigger:a5,rightx:a3,righty:a4,start:b7,x:b2,y:b3,platform:Windows,
03000000381000003114000000000000,SteelSeries Stratus Duo,a:b0,b:b1,back:b6,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b10,leftshoulder:b4,leftstick:b8,lefttrigger:a2,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b9,righttrigger:a5,rightx:a3,righty:a4,start:b7,x:b2,y:b3,platform:Windows,
03000000381000001814000000000000,SteelSeries Stratus XL,a:b0,b:b1,back:b18,dpdown:b13,dpleft:b14,dpright:b15,dpup:b12,guide:b19,leftshoulder:b4,leftstick:b10,lefttrigger:a3,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:a4,rightx:a2,righty:a5,start:b9,x:b2,y:b3,platform:Windows,
03000000790000001c18000000000000,STK 7024X,a:b0,b:b1,back:b10,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b6,leftstick:b13,lefttrigger:b8,leftx:a0,lefty:a1,rightshoulder:b7,rightstick:b14,righttrigger:b9,rightx:a3,righty:a4,start:b11,x:b3,y:b4,platform:Windows,
03000000381000003014000000000000,Stratus Duo,a:b0,b:b1,back:b6,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b4,leftstick:b8,lefttrigger:a2,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b9,righttrigger:a5,rightx:a3,righty:a4,start:b7,x:b2,y:b3,platform:Windows,
03000000381000003114000000000000,Stratus Duo,a:b0,b:b1,back:b6,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b4,leftstick:b8,lefttrigger:a2,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b9,righttrigger:a5,rightx:a3,righty:a4,start:b7,x:b2,y:b3,platform:Windows,
03000000380700003847000000000000,Street Fighter Fightstick TE,a:b0,b:b1,back:b6,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b4,lefttrigger:b10,leftx:a0,lefty:a1,rightshoulder:b5,righttrigger:b11,start:b7,x:b2,y:b3,platform:Windows,
030000001f08000001e4000000000000,Super Famicom Controller,a:b2,b:b1,back:b8,leftshoulder:b4,leftx:a0,lefty:a1,rightshoulder:b5,start:b9,x:b3,y:b0,platform:Windows,
030000001f08000001e4000000000000,Super Famicom Controller,a:b2,b:b1,back:b8,leftshoulder:b4,dpdown:+a1,dpleft:-a0,dpright:+a0,dpup:-a1,rightshoulder:b5,start:b9,x:b3,y:b0,platform:Windows,
03000000790000000418000000000000,Super Famicom Controller,a:b0,b:b1,back:b6,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b33,rightshoulder:b5,start:b7,x:b2,y:b3,platform:Windows,
03000000341200001300000000000000,Super Racer,a:b2,b:b3,back:b8,leftshoulder:b5,lefttrigger:b6,leftx:a0,lefty:a1,rightshoulder:b4,righttrigger:b7,x:b0,y:b1,platform:Windows,
03000000d620000011a7000000000000,Switch Controller,a:b1,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b4,leftstick:b10,lefttrigger:b6,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:b7,rightx:a2,righty:a3,start:b9,x:b0,y:b3,platform:Windows,
030000000d0f0000f600000000000000,Switch Hori,a:b0,b:b1,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b4,leftstick:b10,lefttrigger:b6,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:b7,rightx:a2,righty:a3,start:b9,x:b2,y:b3,platform:Windows,
030000000d0f0000f600000000000000,Switch Hori Pad,a:b0,b:b1,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b4,leftstick:b10,lefttrigger:b6,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:b7,rightx:a2,righty:a3,start:b9,x:b2,y:b3,platform:Windows,
03000000457500002211000000000000,Szmy Power PC Gamepad,a:b1,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b4,leftstick:b10,lefttrigger:b6,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:b7,rightx:a2,righty:a3,start:b9,x:b0,y:b3,platform:Windows,
030000004f0400000ab1000000000000,T16000M,a:b0,b:b1,back:b12,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b11,leftshoulder:b4,lefttrigger:b9,leftx:a0,lefty:a1,rightshoulder:b6,righttrigger:b7,start:b10,x:b2,y:b3,platform:Windows,
030000000d0f00007b00000000000000,TAC GEAR,a:b1,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b4,leftstick:b10,lefttrigger:b6,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:b7,rightx:a2,righty:a5,start:b9,x:b0,y:b3,platform:Windows,
03000000e40a00000207000000000000,Taito Egret II Mini Controller,a:b4,b:b2,back:b6,dpdown:+a1,dpleft:-a0,dpright:+a0,dpup:-a1,guide:b9,rightshoulder:b0,righttrigger:b1,start:b7,x:b8,y:b3,platform:Windows,
03000000d814000001a0000000000000,TE Kitty,a:b1,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b4,leftstick:b10,lefttrigger:b6,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:b7,rightx:a2,righty:a3,start:b9,x:b0,y:b3,platform:Windows,
03000000fa1900000706000000000000,Team 5,a:b2,b:b1,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b4,leftstick:b10,lefttrigger:b6,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:b7,rightx:a2,righty:a3,start:b9,x:b3,y:b0,platform:Windows,
03000000b50700001203000000000000,Techmobility X6-38V,a:b2,b:b3,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b4,leftstick:b10,lefttrigger:b5,leftx:a0,lefty:a1,rightshoulder:b6,rightstick:b11,righttrigger:b7,rightx:a3,righty:a2,start:b9,x:b0,y:b1,platform:Windows,
@@ -640,6 +643,8 @@
030000006d04000088ca000000000000,Thunderpad,a:b0,b:b1,back:b7,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b5,leftstick:b8,lefttrigger:b10,leftx:a0,lefty:a1,rightshoulder:b4,rightstick:b9,righttrigger:b11,rightx:a3,righty:a4,start:b6,x:b2,y:b3,platform:Windows,
03000000666600000488000000000000,TigerGame PlayStation Adapter,a:b2,b:b1,back:b9,dpdown:b14,dpleft:b15,dpright:b13,dpup:b12,leftshoulder:b6,leftstick:b10,lefttrigger:b4,leftx:a0,lefty:a1,rightshoulder:b7,rightstick:b11,righttrigger:b5,rightx:a2,righty:a3,start:b8,x:b3,y:b0,platform:Windows,
030000004f04000007d0000000000000,TMini,a:b1,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b4,leftstick:b10,lefttrigger:b6,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:b7,rightx:a2,righty:a3,start:b9,x:b0,y:b3,platform:Windows,
03000000571d00002100000000000000,Tomee NES Controller Adapter,a:b1,b:b0,back:b2,dpdown:+a4,dpleft:-a0,dpright:+a0,dpup:-a4,start:b3,platform:Windows,
03000000571d00002000000000000000,Tomee SNES Controller Adapter,a:b0,b:b1,back:b6,dpdown:+a4,dpleft:-a0,dpright:+a0,dpup:-a4,leftshoulder:b4,rightshoulder:b5,start:b7,x:b2,y:b3,platform:Windows,
03000000d62000006000000000000000,Tournament PS3 Controller,a:b1,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b4,leftstick:b10,lefttrigger:b6,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:b7,rightx:a2,righty:a3,start:b9,x:b0,y:b3,platform:Windows,
03000000c01100000055000000000000,Tronsmart,a:b2,b:b1,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b4,leftstick:b10,lefttrigger:b6,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:b7,rightx:a2,righty:a3,start:b9,x:b3,y:b0,platform:Windows,
030000005f140000c501000000000000,Trust Gamepad,a:b2,b:b1,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b4,leftstick:b10,lefttrigger:b6,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:b7,rightx:a2,righty:a3,start:b9,x:b3,y:b0,platform:Windows,
@@ -668,7 +673,7 @@
030000006f0e00000702000000000000,Victrix PS4 Pro Fightstick,a:b1,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b4,leftstick:b10,lefttrigger:b6,rightshoulder:b5,righttrigger:b7,start:b9,x:b0,y:b3,platform:Windows,
0300000034120000adbe000000000000,vJoy Device,a:b0,b:b1,back:b15,dpdown:b6,dpleft:b7,dpright:b8,dpup:b5,guide:b16,leftshoulder:b9,leftstick:b13,lefttrigger:b11,leftx:a0,lefty:a1,rightshoulder:b10,rightstick:b14,righttrigger:b12,rightx:a3,righty:a4,start:b4,x:b2,y:b3,platform:Windows,
03000000120c0000ab57000000000000,Warrior Joypad JS083,a:b1,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b4,leftstick:b10,lefttrigger:a3,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:a4,rightx:a2,righty:a5,start:b9,x:b0,y:b3,platform:Windows,
030000007e0500003003000000000000,WiiU Pro,a:b0,b:b1,back:b8,dpdown:b14,dpleft:b15,dpright:b16,dpup:b13,leftshoulder:b6,leftstick:b11,lefttrigger:b4,leftx:a0,lefty:a1,rightshoulder:b7,rightstick:b12,righttrigger:b5,rightx:a2,righty:a3,start:b9,x:b3,y:b2,platform:Windows,
030000007e0500003003000000000000,Wii U Pro,a:b0,b:b1,back:b8,dpdown:b14,dpleft:b15,dpright:b16,dpup:b13,leftshoulder:b6,leftstick:b11,lefttrigger:b4,leftx:a0,lefty:a1,rightshoulder:b7,rightstick:b12,righttrigger:b5,rightx:a2,righty:a3,start:b9,x:b3,y:b2,platform:Windows,
0300000032150000030a000000000000,Wildcat,a:b0,b:b1,back:b6,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b4,leftstick:b8,lefttrigger:a2,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b9,righttrigger:a5,rightx:a3,righty:a4,start:b7,x:b2,y:b3,platform:Windows,
0300000032150000140a000000000000,Wolverine,a:b0,b:b1,back:b6,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b4,leftstick:b8,lefttrigger:a2,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b9,righttrigger:a5,rightx:a3,righty:a4,start:b7,x:b2,y:b3,platform:Windows,
030000002e160000efbe000000000000,Xbox 360 Controller,a:b0,b:b1,back:b6,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b4,lefttrigger:b10,rightshoulder:b5,righttrigger:b11,start:b7,x:b2,y:b3,platform:Windows,
@@ -724,7 +729,6 @@ xinput,XInput Controller,a:b0,b:b1,back:b6,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,
030000007d0400000340000000000000,Xterminator Digital Gamepad,a:b0,b:b1,back:b10,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:-a4,lefttrigger:+a4,leftx:a0,lefty:a1,paddle1:b7,paddle2:b6,rightshoulder:b5,rightstick:b9,righttrigger:b2,rightx:a3,righty:a5,start:b8,x:b3,y:b4,platform:Windows,
03000000790000004f18000000000000,ZDT Android Controller,a:b0,b:b1,back:b10,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b6,leftstick:b13,lefttrigger:b8,leftx:a0,lefty:a1,rightshoulder:b7,rightstick:b14,righttrigger:b9,rightx:a3,righty:a4,start:b11,x:b3,y:b4,platform:Windows,
03000000120c0000101e000000000000,Zeroplus P4 Gamepad,a:b1,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b4,leftstick:b10,lefttrigger:a3,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:a4,rightx:a2,righty:a5,start:b9,x:b0,y:b3,platform:Windows,
03000000091200004488000000000000,MUSIA PlayStation 2 Input Display,a:b0,b:b2,y:b3,x:b1,start:b5,back:b4,leftstick:b6,rightstick:b7,leftshoulder:b8,rightshoulder:b9,dpup:h0.1,dpleft:h0.8,dpdown:h0.4,dpright:h0.2,leftx:a0,lefty:a1,rightx:a2,righty:a3,lefttrigger:b10,righttrigger:b11,platform:Windows,
# Mac OS X
030000008f0e00000300000009010000,2In1 Joystick,a:b2,b:b1,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b4,leftstick:b10,lefttrigger:b6,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:b7,rightx:a2,righty:a3,start:b9,x:b3,y:b0,platform:Mac OS X,
@@ -732,8 +736,9 @@ xinput,XInput Controller,a:b0,b:b1,back:b6,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,
03000000c82d00000531000000020000,8BitDo Adapter 2,a:b0,b:b1,back:b10,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b2,leftshoulder:b6,leftstick:b13,lefttrigger:b8,leftx:a0,lefty:a1,rightshoulder:b7,rightstick:b14,righttrigger:b9,rightx:a2,righty:a3,start:b11,x:b3,y:b4,platform:Mac OS X,
03000000c82d00000090000001000000,8BitDo FC30 Pro,a:b1,b:b0,back:b10,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b6,leftstick:b13,lefttrigger:a4,leftx:a0,lefty:a1,rightshoulder:b7,rightstick:b14,righttrigger:a5,rightx:a2,righty:a3,start:b11,x:b4,y:b3,platform:Mac OS X,
03000000c82d00001038000000010000,8BitDo FC30 Pro,a:b1,b:b0,back:b10,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b6,leftstick:b13,lefttrigger:a5,leftx:a0,lefty:a1,rightshoulder:b7,rightstick:b14,righttrigger:a4,rightx:a2,righty:a3,start:b11,x:b4,y:b3,platform:Mac OS X,
03000000a30c00002400000006020000,8BitDo M30,a:b2,b:b1,dpdown:+a4,dpleft:-a3,dpright:+a3,dpup:-a4,guide:b9,leftshoulder:b6,lefttrigger:b5,rightshoulder:b4,righttrigger:b7,start:b8,x:b3,y:b0,platform:Mac OS X,
03000000c82d00000650000001000000,8BitDo M30,a:b0,b:b1,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b8,lefttrigger:b9,leftx:a0,lefty:a1,rightshoulder:b6,righttrigger:b7,start:b11,x:b3,y:b4,platform:Mac OS X,
03000000c82d00005106000000010000,8BitDo M30,a:b1,b:b0,back:b10,guide:b2,leftshoulder:b6,lefttrigger:a5,leftx:a0,lefty:a1,rightshoulder:b7,righttrigger:a4,start:b11,x:b4,y:b3,platform:Mac OS X,
03000000c82d00005106000000010000,8BitDo M30,a:b1,b:b0,back:b10,guide:b2,leftshoulder:b6,lefttrigger:a5,dpdown:+a1,dpleft:-a0,dpright:+a0,dpup:-a1,rightshoulder:b7,righttrigger:a4,start:b11,x:b4,y:b3,platform:Mac OS X,
03000000c82d00001590000001000000,8BitDo N30 Pro 2,a:b1,b:b0,back:b10,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b2,leftshoulder:b6,leftstick:b13,lefttrigger:a4,leftx:a0,lefty:a1,rightshoulder:b7,rightstick:b14,righttrigger:a5,rightx:a2,righty:a3,start:b11,x:b4,y:b3,platform:Mac OS X,
03000000c82d00006528000000010000,8BitDo N30 Pro 2,a:b1,b:b0,back:b10,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b2,leftshoulder:b6,leftstick:b13,lefttrigger:a5,leftx:a0,lefty:a1,rightshoulder:b7,rightstick:b14,righttrigger:a4,rightx:a2,righty:a3,start:b11,x:b4,y:b3,platform:Mac OS X,
030000003512000012ab000001000000,8BitDo NES30,a:b1,b:b0,back:b10,dpdown:+a1,dpleft:-a0,dpright:+a0,dpup:-a1,leftshoulder:b6,rightshoulder:b7,start:b11,x:b4,y:b3,platform:Mac OS X,
@@ -748,8 +753,8 @@ xinput,XInput Controller,a:b0,b:b1,back:b6,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,
03000000c82d00000231000001000000,8BitDo Receiver,a:b1,b:b0,back:b10,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b2,leftshoulder:b6,leftstick:b13,lefttrigger:b8,leftx:a0,lefty:a1,rightshoulder:b7,rightstick:b14,righttrigger:b9,rightx:a2,righty:a3,start:b11,x:b4,y:b3,platform:Mac OS X,
03000000c82d00000331000001000000,8BitDo Receiver,a:b1,b:b0,back:b10,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b2,leftshoulder:b6,leftstick:b13,lefttrigger:b8,leftx:a0,lefty:a1,rightshoulder:b7,rightstick:b14,righttrigger:b9,rightx:a2,righty:a3,start:b11,x:b4,y:b3,platform:Mac OS X,
03000000c82d00000431000001000000,8BitDo Receiver,a:b1,b:b0,back:b10,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b2,leftshoulder:b6,leftstick:b13,lefttrigger:b8,leftx:a0,lefty:a1,rightshoulder:b7,rightstick:b14,righttrigger:b9,rightx:a2,righty:a3,start:b11,x:b4,y:b3,platform:Mac OS X,
03000000102800000900000000000000,8BitDo SFC30 Joystick,a:b1,b:b0,back:b10,leftshoulder:b6,leftx:a0,lefty:a1,rightshoulder:b7,start:b11,x:b4,y:b3,platform:Mac OS X,
03000000c82d00001290000001000000,8BitDo SN30,a:b1,b:b0,back:b10,leftshoulder:b6,leftx:a0,lefty:a1,rightshoulder:b7,start:b11,x:b4,y:b3,platform:Mac OS X,
03000000102800000900000000000000,8BitDo SFC30 Joystick,a:b1,b:b0,back:b10,leftshoulder:b6,dpdown:+a1,dpleft:-a0,dpright:+a0,dpup:-a1,rightshoulder:b7,start:b11,x:b4,y:b3,platform:Mac OS X,
03000000c82d00001290000001000000,8BitDo SN30,a:b1,b:b0,back:b10,leftshoulder:b6,dpdown:+a1,dpleft:-a0,dpright:+a0,dpup:-a1,rightshoulder:b7,start:b11,x:b4,y:b3,platform:Mac OS X,
03000000c82d00004028000000010000,8BitDo SN30,a:b1,b:b0,back:b10,dpdown:+a1,dpleft:-a0,dpright:+a0,dpup:-a1,leftshoulder:b6,rightshoulder:b7,start:b11,x:b4,y:b3,platform:Mac OS X,
03000000c82d00000160000001000000,8BitDo SN30 Pro,a:b1,b:b0,back:b10,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b6,leftstick:b13,lefttrigger:a4,leftx:a0,lefty:a1,rightshoulder:b7,rightstick:b14,righttrigger:a5,rightx:a2,righty:a3,start:b11,x:b4,y:b3,platform:Mac OS X,
03000000c82d00000161000000010000,8BitDo SN30 Pro,a:b1,b:b0,back:b10,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b2,leftshoulder:b6,leftstick:b13,lefttrigger:b8,leftx:a0,lefty:a1,rightshoulder:b7,rightstick:b14,righttrigger:b9,rightx:a2,righty:a5,start:b11,x:b4,y:b3,platform:Mac OS X,
@@ -757,11 +762,11 @@ xinput,XInput Controller,a:b0,b:b1,back:b6,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,
03000000c82d00000261000000010000,8BitDo SN30 Pro+,a:b1,b:b0,back:b10,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b2,leftshoulder:b6,leftstick:b13,lefttrigger:b8,leftx:a0,lefty:a1,rightshoulder:b7,rightstick:b14,righttrigger:b9,rightx:a2,righty:a3,start:b11,x:b4,y:b3,platform:Mac OS X,
03000000a00500003232000008010000,8BitDo Zero,a:b0,b:b1,back:b10,dpdown:+a1,dpleft:-a0,dpright:+a0,dpup:-a1,leftshoulder:b6,rightshoulder:b7,start:b11,x:b3,y:b4,platform:Mac OS X,
03000000a00500003232000009010000,8BitDo Zero,a:b0,b:b1,back:b10,dpdown:+a1,dpleft:-a0,dpright:+a0,dpup:-a1,leftshoulder:b6,rightshoulder:b7,start:b11,x:b3,y:b4,platform:Mac OS X,
03000000c82d00001890000001000000,8BitDo Zero 2,a:b1,b:b0,back:b10,leftshoulder:b6,leftx:a0,lefty:a1,rightshoulder:b7,start:b11,x:b4,y:b3,platform:Mac OS X,
03000000c82d00001890000001000000,8BitDo Zero 2,a:b1,b:b0,back:b10,leftshoulder:b6,dpdown:+a1,dpleft:-a0,dpright:+a0,dpup:-a1,rightshoulder:b7,start:b11,x:b4,y:b3,platform:Mac OS X,
03000000c82d00003032000000010000,8BitDo Zero 2,a:b1,b:b0,back:b10,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b6,leftx:a0,lefty:a1,rightshoulder:b7,rightx:a2,righty:a31,start:b11,x:b4,y:b3,platform:Mac OS X,
03000000491900001904000001010000,Amazon Luna Controller,a:b0,b:b1,back:b6,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b8,leftshoulder:b4,leftstick:b10,lefttrigger:a3,leftx:a0,lefty:a1,misc1:b9,rightshoulder:b5,rightstick:b11,righttrigger:a4,rightx:a2,righty:a5,start:b7,x:b2,y:b3,platform:Mac OS X,
03000000710100001904000000010000,Amazon Luna Controller,a:b0,b:b1,back:b11,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b10,leftshoulder:b4,leftstick:b7,lefttrigger:a5,leftx:a0,lefty:a1,misc1:b9,rightshoulder:b5,rightstick:b8,righttrigger:a4,rightx:a2,righty:a3,start:b6,x:b2,y:b3,platform:Mac OS X,
03000000a30c00002700000003030000,Astro City Mini,a:b2,b:b1,back:b8,leftx:a3,lefty:a4,rightshoulder:b4,righttrigger:b5,start:b9,x:b3,y:b0,platform:Mac OS X,
03000000a30c00002700000003030000,Astro City Mini,a:b2,b:b1,back:b8,dpdown:+a4,dpleft:-a3,dpright:+a3,dpup:-a4,rightshoulder:b4,righttrigger:b5,start:b9,x:b3,y:b0,platform:Mac OS X,
03000000a30c00002800000003030000,Astro City Mini,a:b2,b:b1,back:b8,leftx:a3,lefty:a4,rightshoulder:b4,righttrigger:b5,start:b9,x:b3,y:b0,platform:Mac OS X,
03000000050b00000045000031000000,ASUS Gamepad,a:b0,b:b1,back:b10,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b4,leftstick:b7,lefttrigger:a5,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b8,righttrigger:a4,rightx:a2,righty:a3,start:b9,x:b2,y:b3,platform:Mac OS X,
03000000c62400001a89000000010000,BDA MOGA XP5-X Plus,a:b0,b:b1,back:b12,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b14,leftshoulder:b6,leftstick:b15,lefttrigger:a5,leftx:a0,lefty:a1,rightshoulder:b7,rightstick:b16,righttrigger:a4,rightx:a2,righty:a3,start:b13,x:b3,y:b4,platform:Mac OS X,
@@ -781,7 +786,7 @@ xinput,XInput Controller,a:b0,b:b1,back:b6,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,
0500000047532047616d657061640000,GameStop Gamepad,a:b0,b:b1,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b4,leftstick:b10,lefttrigger:b6,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:b7,rightx:a2,righty:a3,start:b9,x:b2,y:b3,platform:Mac OS X,
03000000c01100000140000000010000,GameStop PS4 Fun Controller,a:b1,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b4,leftstick:b10,lefttrigger:a3,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:a4,rightx:a2,righty:a5,start:b9,x:b0,y:b3,platform:Mac OS X,
030000006f0e00000102000000000000,GameStop Xbox 360 Controller,a:b0,b:b1,back:b9,dpdown:b12,dpleft:b13,dpright:b14,dpup:b11,guide:b10,leftshoulder:b4,leftstick:b6,lefttrigger:a2,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b7,righttrigger:a5,rightx:a3,righty:a4,start:b8,x:b2,y:b3,platform:Mac OS X,
030000007d0400000540000001010000,Gravis Eliminator Pro,a:b1,b:b2,back:b8,leftshoulder:b4,lefttrigger:b6,leftx:a0,lefty:a1,rightshoulder:b5,righttrigger:b7,start:b9,x:b0,y:b3,platform:Mac OS X,
030000007d0400000540000001010000,Gravis Eliminator Pro,a:b1,b:b2,back:b8,leftshoulder:b4,lefttrigger:b6,dpdown:+a1,dpleft:-a0,dpright:+a0,dpup:-a1,rightshoulder:b5,righttrigger:b7,start:b9,x:b0,y:b3,platform:Mac OS X,
03000000280400000140000000020000,Gravis Gamepad Pro,a:b1,b:b2,back:b8,dpdown:+a1,dpleft:-a0,dpright:+a0,dpup:-a1,leftshoulder:b4,lefttrigger:b6,rightshoulder:b5,righttrigger:b7,start:b9,x:b0,y:b3,platform:Mac OS X,
030000008f0e00000300000007010000,GreenAsia Joystick,a:b2,b:b3,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b4,lefttrigger:b5,leftx:a0,lefty:a1,rightshoulder:b6,righttrigger:b7,rightx:a3,righty:a2,start:b9,x:b0,y:b1,platform:Mac OS X,
030000000d0f00002d00000000100000,Hori Fighting Commander 3 Pro,a:b1,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b4,lefttrigger:b6,leftx:a0,lefty:a1,rightshoulder:b5,righttrigger:b7,rightx:a2,righty:a3,start:b9,x:b0,y:b3,platform:Mac OS X,
@@ -792,15 +797,17 @@ xinput,XInput Controller,a:b0,b:b1,back:b6,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,
030000000d0f00008400000000010000,Hori Fighting Commander PS3,a:b1,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b4,leftstick:b10,lefttrigger:a3,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:a4,rightx:a2,righty:a5,start:b9,x:b0,y:b3,platform:Mac OS X,
030000000d0f00008500000000010000,Hori Fighting Commander PS3,a:b1,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b4,leftstick:b10,lefttrigger:b6,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:b7,rightx:a2,righty:a3,start:b9,x:b0,y:b3,platform:Mac OS X,
030000000d0f00004d00000000000000,Hori Gem Pad 3,a:b1,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b4,leftstick:b10,lefttrigger:b6,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:b7,rightx:a2,righty:a3,start:b9,x:b0,y:b3,platform:Mac OS X,
030000000d0f00003801000008010000,Hori PC Engine Mini Controller,a:b1,b:b2,back:b8,start:b9,dpup:h0.1,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,platform:Mac OS X,
030000000d0f00003801000008010000,Hori PC Engine Mini Controller,a:b1,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,start:b9,platform:Mac OS X,
030000000d0f00009200000000010000,Hori Pokken Tournament DX Pro,a:b1,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b4,lefttrigger:b6,rightshoulder:b5,righttrigger:b7,start:b9,x:b0,y:b3,platform:Mac OS X,
030000000d0f00006e00000000010000,Horipad 4 PS3,a:b1,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b4,leftstick:b10,lefttrigger:b6,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:b7,rightx:a2,righty:a3,start:b9,x:b0,y:b3,platform:Mac OS X,
030000000d0f00006600000000010000,Horipad 4 PS4,a:b1,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b4,leftstick:b10,lefttrigger:a3,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:a4,rightx:a2,righty:a5,start:b9,x:b0,y:b3,platform:Mac OS X,
030000000d0f00006600000000000000,Horipad FPS Plus 4,a:b1,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b4,leftstick:b10,lefttrigger:b6,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:a4,rightx:a2,righty:a5,start:b9,x:b0,y:b3,platform:Mac OS X,
030000000d0f0000ee00000000010000,Horipad Mini 4,a:b1,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b4,leftstick:b10,lefttrigger:b6,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:b7,rightx:a2,righty:a5,start:b9,x:b0,y:b3,platform:Mac OS X,
030000008f0e00001330000011010000,HuiJia SNES Controller,a:b4,b:b2,back:b16,dpdown:+a2,dpleft:-a0,dpright:+a0,dpup:-a2,leftshoulder:b12,rightshoulder:b14,start:b18,x:b6,y:b0,platform:Mac OS X,
03000000830500006020000000000000,iBuffalo Gamepad,a:b1,b:b0,back:b6,leftshoulder:b4,leftx:a0,lefty:a1,rightshoulder:b5,start:b7,x:b3,y:b2,platform:Mac OS X,
03000000790000004e95000000010000,Hyperkin N64 Controller Adapter,a:b1,b:b2,dpdown:b14,dpleft:b15,dpright:b13,dpup:b12,leftshoulder:b7,lefttrigger:a3,leftx:a0,lefty:a1,rightshoulder:b5,rightx:a5,righty:a2,start:b9,platform:Mac OS X,
03000000830500006020000000000000,iBuffalo Gamepad,a:b1,b:b0,back:b6,leftshoulder:b4,dpdown:+a1,dpleft:-a0,dpright:+a0,dpup:-a1,rightshoulder:b5,start:b7,x:b3,y:b2,platform:Mac OS X,
03000000ef0500000300000000020000,InterAct AxisPad,a:b2,b:b3,back:b10,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b4,leftstick:b8,lefttrigger:b6,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b9,righttrigger:b7,rightx:a3,righty:a2,start:b11,x:b0,y:b1,platform:Mac OS X,
03000000fd0500000030000010010000,Interact GoPad,a:b3,b:b4,x:b0,y:b1,leftshoulder:b6,rightshoulder:b2,leftx:a0,lefty:a1,lefttrigger:b7,righttrigger:b5,platform:Mac OS X,
030000007e0500000620000001000000,Joy-Con (L),+leftx:h0.2,+lefty:h0.4,-leftx:h0.8,-lefty:h0.1,a:b0,b:b1,back:b13,leftshoulder:b4,leftstick:b10,rightshoulder:b5,start:b8,x:b2,y:b3,platform:Mac OS X,
030000007e0500000720000001000000,Joy-Con (R),+leftx:h0.2,+lefty:h0.4,-leftx:h0.8,-lefty:h0.1,a:b0,b:b1,back:b12,leftshoulder:b4,leftstick:b11,rightshoulder:b5,start:b9,x:b2,y:b3,platform:Mac OS X,
03000000242f00002d00000007010000,JYS Adapter,a:b2,b:b1,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b4,leftstick:b10,lefttrigger:b6,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:b7,rightx:a2,righty:a3,start:b9,x:b3,y:b0,platform:Mac OS X,
@@ -823,9 +830,10 @@ xinput,XInput Controller,a:b0,b:b1,back:b6,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,
03000000242f00007300000000020000,Mayflash Magic NS,a:b1,b:b4,back:b10,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b6,leftstick:b13,lefttrigger:b8,rightshoulder:b7,rightstick:b14,righttrigger:b9,rightx:a2,righty:a3,start:b11,x:b0,y:b3,platform:Mac OS X,
0300000079000000d218000026010000,Mayflash Magic NS,a:b2,b:b1,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b4,leftstick:b10,lefttrigger:b6,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:b7,rightx:a2,righty:a3,start:b9,x:b3,y:b0,platform:Mac OS X,
03000000d620000010a7000003010000,Mayflash Magic NS,a:b1,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b4,leftstick:b10,lefttrigger:b6,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:b7,rightx:a2,righty:a3,start:b9,x:b0,y:b3,platform:Mac OS X,
0300000025090000e803000000000000,Mayflash Wii Classic Controller,a:b1,b:b0,back:b8,dpdown:b13,dpleft:b12,dpright:b14,dpup:b11,guide:b10,leftshoulder:b4,lefttrigger:b6,leftx:a0,lefty:a1,rightshoulder:b5,righttrigger:b7,rightx:a2,righty:a3,start:b9,x:b3,y:b2,platform:Mac OS X,
03000000790000000018000000010000,Mayflash Wii U Pro Controller Adapter,a:b4,b:b8,back:b32,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b16,leftstick:b40,lefttrigger:b24,leftx:a0,lefty:a4,rightshoulder:b20,rightstick:b44,righttrigger:b28,rightx:a8,righty:a12,start:b36,x:b0,y:b12,platform:Mac OS X,
03000000790000000018000000000000,Mayflash WiiU Pro Adapter,a:b4,b:b8,back:b32,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b16,leftstick:b40,lefttrigger:b24,leftx:a0,lefty:a4,rightshoulder:b20,rightstick:b44,righttrigger:b28,rightx:a8,righty:a12,start:b36,x:b0,y:b12,platform:Mac OS X,
0300000025090000e803000000000000,Mayflash Wii Classic Adapter,a:b1,b:b0,back:b8,dpdown:b13,dpleft:b12,dpright:b14,dpup:b11,guide:b10,leftshoulder:b4,lefttrigger:b6,leftx:a0,lefty:a1,rightshoulder:b5,righttrigger:b7,rightx:a2,righty:a3,start:b9,x:b3,y:b2,platform:Mac OS X,
03000000790000000318000000010000,Mayflash Wii DolphinBar,a:b8,b:b12,back:b32,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b44,leftshoulder:b16,lefttrigger:b24,leftx:a0,lefty:a4,rightshoulder:b20,righttrigger:b28,rightx:a8,righty:a12,start:b36,x:b0,y:b4,platform:Mac OS X,
03000000790000000018000000000000,Mayflash Wii U Pro Adapter,a:b4,b:b8,back:b32,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b16,leftstick:b40,lefttrigger:b24,leftx:a0,lefty:a4,rightshoulder:b20,rightstick:b44,righttrigger:b28,rightx:a8,righty:a12,start:b36,x:b0,y:b12,platform:Mac OS X,
03000000790000000018000000010000,Mayflash Wii U Pro Adapter,a:b4,b:b8,back:b32,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b16,leftstick:b40,lefttrigger:b24,leftx:a0,lefty:a4,rightshoulder:b20,rightstick:b44,righttrigger:b28,rightx:a8,righty:a12,start:b36,x:b0,y:b12,platform:Mac OS X,
030000005e0400002800000002010000,Microsoft Dual Strike,a:b3,b:b2,back:b4,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b6,lefttrigger:b8,rightshoulder:b7,rightx:a0,righty:a1~,start:b5,x:b1,y:b0,platform:Mac OS X,
030000005e0400002700000001010000,Microsoft SideWinder Plug and Play,a:b0,b:b1,dpdown:+a1,dpleft:-a0,dpright:+a0,dpup:-a1,lefttrigger:b4,righttrigger:b5,x:b2,y:b3,platform:Mac OS X,
03000000d62000007162000001000000,Moga Pro 2,a:b0,b:b1,back:b9,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b4,leftstick:b7,lefttrigger:a5,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b8,righttrigger:a4,rightx:a2,righty:a3,start:b6,x:b2,y:b3,platform:Mac OS X,
@@ -843,7 +851,7 @@ xinput,XInput Controller,a:b0,b:b1,back:b6,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,
03000000550900001472000025050000,NVIDIA Controller,a:b0,b:b1,back:b17,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b15,leftshoulder:b4,leftstick:b7,lefttrigger:a3,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b8,righttrigger:a4,rightx:a2,righty:a5,start:b6,x:b2,y:b3,platform:Mac OS X,
030000006f0e00000901000002010000,PDP Versus Fighting,a:b1,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b4,lefttrigger:b6,rightshoulder:b5,righttrigger:b7,start:b9,x:b0,y:b3,platform:Mac OS X,
030000008f0e00000300000000000000,Piranha Xtreme PS3 Controller,a:b2,b:b1,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b6,leftstick:b10,lefttrigger:b4,leftx:a0,lefty:a1,rightshoulder:b7,rightstick:b11,righttrigger:b5,rightx:a3,righty:a2,start:b9,x:b3,y:b0,platform:Mac OS X,
030000004c050000da0c000000010000,PlayStation Classic Controller,a:b2,b:b1,back:b8,leftshoulder:b6,lefttrigger:b4,leftx:a0,lefty:a1,rightshoulder:b7,righttrigger:b5,start:b9,x:b3,y:b0,platform:Mac OS X,
030000004c050000da0c000000010000,PlayStation Classic Controller,a:b2,b:b1,back:b8,leftshoulder:b6,lefttrigger:b4,dpdown:+a1,dpleft:-a0,dpright:+a0,dpup:-a1,rightshoulder:b7,righttrigger:b5,start:b9,x:b3,y:b0,platform:Mac OS X,
030000004c0500003713000000010000,PlayStation Vita,a:b1,b:b2,back:b8,dpdown:b13,dpleft:b15,dpright:b14,dpup:b12,leftshoulder:b4,leftx:a0,lefty:a1,rightshoulder:b5,rightx:a3,righty:a4,start:b9,x:b0,y:b3,platform:Mac OS X,
03000000d62000006dca000000010000,PowerA Pro Ex,a:b1,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b4,leftstick:b10,lefttrigger:b6,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:b7,rightx:a2,righty:a3,start:b9,x:b0,y:b3,platform:Mac OS X,
03000000100800000300000006010000,PS2 Adapter,a:b2,b:b1,back:b8,leftshoulder:b6,leftstick:b10,lefttrigger:b4,leftx:a0,lefty:a1,rightshoulder:b7,rightstick:b11,righttrigger:b5,rightx:a4,righty:a3,start:b9,x:b3,y:b0,platform:Mac OS X,
@@ -866,6 +874,8 @@ xinput,XInput Controller,a:b0,b:b1,back:b6,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,
03000000321500000009000000020000,Razer Serval,a:b0,b:b1,back:b6,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b8,leftshoulder:b4,leftstick:b9,lefttrigger:a5,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b10,righttrigger:a4,rightx:a2,righty:a3,start:b7,x:b2,y:b3,platform:Mac OS X,
030000003215000000090000163a0000,Razer Serval,a:b0,b:b1,back:b6,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b8,leftshoulder:b4,leftstick:b9,lefttrigger:a5,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b10,righttrigger:a4,rightx:a2,righty:a3,start:b7,x:b2,y:b3,platform:Mac OS X,
0300000032150000030a000000000000,Razer Wildcat,a:b0,b:b1,back:b9,dpdown:b12,dpleft:b13,dpright:b14,dpup:b11,guide:b10,leftshoulder:b4,leftstick:b6,lefttrigger:a2,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b7,righttrigger:a5,rightx:a3,righty:a4,start:b8,x:b2,y:b3,platform:Mac OS X,
030000000d0f0000c100000072050000,Retro Bit Sega Genesis 6B Controller,a:b2,b:b1,dpdown:+a1,dpleft:-a0,dpright:+a0,dpup:-a1,lefttrigger:b8,rightshoulder:b6,righttrigger:b7,start:b9,x:b3,y:b0,platform:Mac OS X,
03000000921200004547000000020000,Retro Bit Sega Genesis Controller Adapter,a:b0,b:b2,x:b6,y:b8,start:b12,rightshoulder:b10,dpup:-a2,dpdown:+a2,dpleft:-a0,dpright:+a0,lefttrigger:b14,righttrigger:b4,platform:Mac OS X,
03000000790000001100000000000000,Retro Controller,a:b1,b:b2,back:b8,dpdown:+a4,dpleft:-a3,dpright:+a3,dpup:-a4,leftshoulder:b6,lefttrigger:b7,rightshoulder:b4,righttrigger:b5,start:b9,x:b0,y:b3,platform:Mac OS X,
03000000790000001100000005010000,Retro Controller,a:b1,b:b2,back:b8,dpdown:+a4,dpleft:-a3,dpright:+a3,dpup:-a4,leftshoulder:b6,lefttrigger:b7,rightshoulder:b5,righttrigger:b4,start:b9,x:b0,y:b3,platform:Mac OS X,
03000000830500006020000000010000,Retro Controller,a:b0,b:b1,back:b6,dpdown:+a1,dpleft:-a0,dpright:+a0,dpup:-a1,leftshoulder:b4,lefttrigger:b5,rightshoulder:b8,righttrigger:b9,start:b7,x:b2,y:b3,platform:Mac OS X,
@@ -873,30 +883,36 @@ xinput,XInput Controller,a:b0,b:b1,back:b6,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,
030000006b140000010d000000010000,Revolution Pro Controller,a:b1,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b4,leftstick:b10,lefttrigger:a3,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:a4,rightx:a2,righty:a5,start:b9,x:b0,y:b3,platform:Mac OS X,
030000006b140000130d000000010000,Revolution Pro Controller 3,a:b1,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b4,leftstick:b10,lefttrigger:a3,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:a4,rightx:a2,righty:a5,start:b9,x:b0,y:b3,platform:Mac OS X,
03000000c6240000fefa000000000000,Rock Candy PS3,a:b0,b:b1,back:b9,dpdown:b12,dpleft:b13,dpright:b14,dpup:b11,guide:b10,leftshoulder:b4,leftstick:b6,lefttrigger:a2,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b7,righttrigger:a5,rightx:a3,righty:a4,start:b8,x:b2,y:b3,platform:Mac OS X,
03000000730700000401000000010000,Sanwa PlayOnline Mobile,a:b0,b:b1,back:b2,leftx:a0,lefty:a1,start:b3,platform:Mac OS X,
03000000730700000401000000010000,Sanwa PlayOnline Mobile,a:b0,b:b1,back:b2,dpdown:+a1,dpleft:-a0,dpright:+a0,dpup:-a1,start:b3,platform:Mac OS X,
03000000a30c00002500000006020000,Sega Genesis Mini 3B Controller,a:b2,b:b1,dpdown:+a4,dpleft:-a3,dpright:+a3,dpup:-a4,righttrigger:b5,start:b9,platform:Mac OS X,
03000000811700007e05000000000000,Sega Saturn,a:b2,b:b4,dpdown:b16,dpleft:b15,dpright:b14,dpup:b17,leftshoulder:b8,lefttrigger:a5,leftx:a0,lefty:a2,rightshoulder:b9,righttrigger:a4,start:b13,x:b0,y:b6,platform:Mac OS X,
03000000b40400000a01000000000000,Sega Saturn,a:b0,b:b1,back:b5,guide:b2,leftshoulder:b6,leftx:a0,lefty:a1,rightshoulder:b7,start:b8,x:b3,y:b4,platform:Mac OS X,
030000003512000021ab000000000000,SFC30 Joystick,a:b1,b:b0,back:b10,leftshoulder:b6,leftx:a0,lefty:a1,rightshoulder:b7,start:b11,x:b4,y:b3,platform:Mac OS X,
03000000b40400000a01000000000000,Sega Saturn,a:b0,b:b1,back:b5,guide:b2,leftshoulder:b6,dpdown:+a1,dpleft:-a0,dpright:+a0,dpup:-a1,rightshoulder:b7,start:b8,x:b3,y:b4,platform:Mac OS X,
030000003512000021ab000000000000,SFC30 Joystick,a:b1,b:b0,back:b10,leftshoulder:b6,dpdown:+a1,dpleft:-a0,dpright:+a0,dpup:-a1,rightshoulder:b7,start:b11,x:b4,y:b3,platform:Mac OS X,
0300000000f00000f100000000000000,SNES RetroPort,a:b2,b:b3,back:b4,dpdown:+a1,dpleft:-a0,dpright:+a0,dpup:-a1,leftshoulder:b5,rightshoulder:b7,start:b6,x:b0,y:b1,platform:Mac OS X,
030000004c050000a00b000000000000,Sony DualShock 4 Adapter,a:b1,b:b2,back:b13,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b4,leftstick:b10,lefttrigger:a3,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:a4,rightx:a2,righty:a5,start:b9,x:b0,y:b3,platform:Mac OS X,
030000004c050000cc09000000000000,Sony DualShock 4 V2,a:b1,b:b2,back:b13,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b4,leftstick:b10,lefttrigger:a3,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:a4,rightx:a2,righty:a5,start:b9,x:b0,y:b3,platform:Mac OS X,
03000000d11800000094000000010000,Stadia Controller,a:b0,b:b1,back:b6,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b8,leftshoulder:b4,leftstick:b9,lefttrigger:a5,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b10,righttrigger:a4,rightx:a2,righty:a3,start:b7,x:b2,y:b3,platform:Mac OS X,
030000005e0400008e02000001000000,Steam Virtual Gamepad,a:b0,b:b1,back:b9,dpdown:b12,dpleft:b13,dpright:b14,dpup:b11,leftshoulder:b4,leftstick:b6,lefttrigger:a2,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b7,righttrigger:a5,rightx:a3,righty:a4,start:b8,x:b2,y:b3,platform:Mac OS X,
03000000110100002014000000000000,SteelSeries Nimbus,a:b0,b:b1,dpdown:b9,dpleft:b11,dpright:b10,dpup:b8,leftshoulder:b4,lefttrigger:b6,leftx:a0,lefty:a1,rightshoulder:b5,righttrigger:b7,rightx:a2,righty:a3,start:b12,x:b2,y:b3,platform:Mac OS X,
03000000110100002014000000000000,SteelSeries Nimbus,a:b0,b:b1,dpdown:b9,dpleft:b11,dpright:b10,dpup:b8,guide:b12,leftshoulder:b4,lefttrigger:b6,leftx:a0,lefty:a1~,rightshoulder:b5,righttrigger:b7,rightx:a2,righty:a3~,x:b2,y:b3,platform:Mac OS X,
03000000110100002014000001000000,SteelSeries Nimbus,a:b0,b:b1,dpdown:b9,dpleft:b11,dpright:b10,dpup:b8,guide:b12,leftshoulder:b4,lefttrigger:b6,leftx:a0,lefty:a1~,rightshoulder:b5,righttrigger:b7,rightx:a2,righty:a3~,x:b2,y:b3,platform:Mac OS X,
03000000381000002014000001000000,SteelSeries Nimbus,a:b0,b:b1,dpdown:b9,dpleft:b11,dpright:b10,dpup:b8,guide:b12,leftshoulder:b4,lefttrigger:b6,leftx:a0,lefty:a1~,rightshoulder:b5,righttrigger:b7,rightx:a2,righty:a3~,x:b2,y:b3,platform:Mac OS X,
05000000484944204465766963650000,SteelSeries Nimbus Plus,a:b0,b:b1,back:b15,dpdown:b11,dpleft:b13,dpright:b12,dpup:b10,guide:b16,leftshoulder:b4,leftstick:b8,lefttrigger:b6,leftx:a0,lefty:a1~,rightshoulder:b5,rightstick:b9,righttrigger:b7,rightx:a2,righty:a3~,start:b14,x:b2,y:b3,platform:Mac OS X,
050000004e696d6275732b0000000000,SteelSeries Nimbus Plus,a:b0,b:b1,back:b15,dpdown:b11,dpleft:b13,dpright:b12,dpup:b10,guide:b16,leftshoulder:b4,leftstick:b8,lefttrigger:b6,leftx:a0,lefty:a1~,rightshoulder:b5,rightstick:b9,righttrigger:b7,rightx:a2,righty:a3~,start:b14,x:b2,y:b3,platform:Mac OS X,
050000004e696d6275732b008b000000,SteelSeries Nimbus Plus,a:b0,b:b1,back:b15,dpdown:b11,dpleft:b13,dpright:b12,dpup:b10,guide:b16,leftshoulder:b4,leftstick:b8,lefttrigger:b6,leftx:a0,lefty:a1~,rightshoulder:b5,rightstick:b9,righttrigger:b7,rightx:a2,righty:a3~,start:b14,x:b2,y:b3,platform:Mac OS X,
05000000556e6b6e6f776e2048494400,SteelSeries Nimbus Plus,a:b0,b:b1,back:b15,dpdown:b11,dpleft:b13,dpright:b12,dpup:b10,guide:b16,leftshoulder:b4,leftstick:b8,lefttrigger:b6,leftx:a0,lefty:a1~,rightshoulder:b5,rightstick:b9,righttrigger:b7,rightx:a2,righty:a3~,start:b14,x:b2,y:b3,platform:Mac OS X,
03000000381000003014000000000000,SteelSeries Stratus Duo,a:b0,b:b1,back:b9,dpdown:b12,dpleft:b13,dpright:b14,dpup:b11,guide:b10,leftshoulder:b4,leftstick:b6,lefttrigger:a2,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b7,righttrigger:a5,rightx:a3,righty:a4,start:b8,x:b2,y:b3,platform:Mac OS X,
03000000381000003114000000000000,SteelSeries Stratus Duo,a:b0,b:b1,back:b9,dpdown:b12,dpleft:b13,dpright:b14,dpup:b11,guide:b10,leftshoulder:b4,leftstick:b6,lefttrigger:a2,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b7,righttrigger:a5,rightx:a3,righty:a4,start:b8,x:b2,y:b3,platform:Mac OS X,
03000000110100001714000000000000,SteelSeries Stratus XL,a:b0,b:b1,dpdown:b9,dpleft:b11,dpright:b10,dpup:b8,leftshoulder:b4,lefttrigger:b6,leftx:a0,lefty:a1~,rightshoulder:b5,righttrigger:b7,rightx:a2,righty:a3~,start:b12,x:b2,y:b3,platform:Mac OS X,
03000000110100001714000020010000,SteelSeries Stratus XL,a:b0,b:b1,dpdown:b9,dpleft:b11,dpright:b10,dpup:b8,leftshoulder:b4,lefttrigger:b6,leftx:a0,lefty:a1~,rightshoulder:b5,righttrigger:b7,rightx:a2,righty:a3~,start:b12,x:b2,y:b3,platform:Mac OS X,
030000000d0f0000f600000000010000,Switch Hori Pad,a:b0,b:b1,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b4,leftstick:b10,lefttrigger:b6,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:b7,rightx:a2,righty:a3,start:b9,x:b2,y:b3,platform:Mac OS X,
03000000457500002211000000010000,SZMY Power PC Gamepad,a:b1,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b4,leftstick:b10,lefttrigger:b6,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:b7,rightx:a2,righty:a3,start:b9,x:b0,y:b3,platform:Mac OS X,
030000004f04000015b3000000000000,Thrustmaster Dual Analog 3.2,a:b0,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b4,leftstick:b10,lefttrigger:b5,leftx:a0,lefty:a1,rightshoulder:b6,rightstick:b11,righttrigger:b7,rightx:a2,righty:a3,start:b9,x:b1,y:b3,platform:Mac OS X,
030000004f0400000ed0000000020000,ThrustMaster eSwap Pro Controller,a:b1,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b4,leftstick:b10,lefttrigger:a3,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:a4,rightx:a2,righty:a5,start:b9,x:b0,y:b3,platform:Mac OS X,
030000004f04000000b3000000000000,Thrustmaster Firestorm Dual Power,a:b0,b:b2,back:b9,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b8,leftshoulder:b4,leftstick:b11,lefttrigger:b5,leftx:a0,lefty:a1,rightshoulder:b6,righttrigger:b7,rightx:a2,righty:a3,start:b10,x:b1,y:b3,platform:Mac OS X,
03000000571d00002100000021000000,Tomee NES Controller Adapter,a:b1,b:b0,back:b2,dpdown:+a4,dpleft:-a0,dpright:+a0,dpup:-a4,start:b3,platform:Mac OS X,
03000000bd12000015d0000000010000,Tomee Retro Controller,a:b2,b:b1,back:b8,dpdown:+a1,dpleft:-a0,dpright:+a0,dpup:-a1,leftshoulder:b4,rightshoulder:b5,start:b9,x:b3,y:b0,platform:Mac OS X,
03000000bd12000015d0000000000000,Tomee SNES Controller,a:b2,b:b1,back:b8,dpdown:+a1,dpleft:-a0,dpright:+a0,dpup:-a1,leftshoulder:b4,rightshoulder:b5,start:b9,x:b3,y:b0,platform:Mac OS X,
03000000571d00002000000021000000,Tomee SNES Controller Adapter,a:b0,b:b1,back:b6,dpdown:+a4,dpleft:-a0,dpright:+a0,dpup:-a4,leftshoulder:b4,rightshoulder:b5,start:b7,x:b2,y:b3,platform:Mac OS X,
03000000100800000100000000000000,Twin USB Joystick,a:b4,b:b2,back:b16,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b12,leftstick:b20,lefttrigger:b8,leftx:a0,lefty:a2,rightshoulder:b14,rightstick:b22,righttrigger:b10,rightx:a6,righty:a4,start:b18,x:b6,y:b0,platform:Mac OS X,
030000006f0e00000302000025040000,Victrix PS4 Pro Fightstick,a:b1,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b4,lefttrigger:b6,rightshoulder:b5,righttrigger:b7,start:b9,x:b0,y:b3,platform:Mac OS X,
030000006f0e00000702000003060000,Victrix PS4 Pro Fightstick,a:b1,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b4,leftstick:b10,lefttrigger:b6,rightshoulder:b5,righttrigger:b7,start:b9,x:b0,y:b3,platform:Mac OS X,
@@ -920,6 +936,7 @@ xinput,XInput Controller,a:b0,b:b1,back:b6,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,
030000005e040000130b000001050000,Xbox Series Controller,a:b0,b:b1,back:b10,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b6,leftstick:b13,lefttrigger:a5,leftx:a0,lefty:a1,rightshoulder:b7,rightstick:b14,righttrigger:a4,rightx:a2,righty:a3,start:b11,x:b3,y:b4,platform:Mac OS X,
030000005e040000130b000005050000,Xbox Series Controller,a:b0,b:b1,back:b10,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b6,leftstick:b13,lefttrigger:a5,leftx:a0,lefty:a1,rightshoulder:b7,rightstick:b14,righttrigger:a4,rightx:a2,righty:a3,start:b11,x:b3,y:b4,platform:Mac OS X,
030000005e040000130b000009050000,Xbox Series Controller,a:b0,b:b1,back:b10,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b6,leftstick:b13,lefttrigger:a5,leftx:a0,lefty:a1,rightshoulder:b7,rightstick:b14,righttrigger:a4,rightx:a2,righty:a3,start:b11,x:b3,y:b4,platform:Mac OS X,
030000005e040000130b000013050000,Xbox Series Controller,a:b0,b:b1,back:b10,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b6,leftstick:b13,lefttrigger:a5,leftx:a0,lefty:a1,rightshoulder:b7,rightstick:b14,righttrigger:a4,rightx:a2,righty:a3,start:b11,x:b3,y:b4,platform:Mac OS X,
03000000172700004431000029010000,XiaoMi Controller,a:b0,b:b1,back:b10,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b15,leftshoulder:b6,leftstick:b13,lefttrigger:b8,leftx:a0,lefty:a1,rightshoulder:b7,rightstick:b14,righttrigger:a6,rightx:a2,righty:a5,start:b11,x:b3,y:b4,platform:Mac OS X,
03000000120c0000100e000000010000,Zeroplus P4,a:b1,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b4,leftstick:b10,lefttrigger:a3,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:a4,rightx:a2,righty:a5,start:b9,x:b0,y:b3,platform:Mac OS X,
03000000120c0000101e000000010000,Zeroplus P4,a:b1,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b4,leftstick:b10,lefttrigger:a3,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:a4,rightx:a2,righty:a5,start:b9,x:b0,y:b3,platform:Mac OS X,
@@ -934,7 +951,7 @@ xinput,XInput Controller,a:b0,b:b1,back:b6,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,
05000000c82d00005106000000010000,8BitDo M30,a:b0,b:b1,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b8,lefttrigger:b9,leftx:a0,lefty:a1,rightshoulder:b6,righttrigger:b7,start:b11,x:b3,y:b4,platform:Linux,
03000000c82d00001590000011010000,8BitDo N30 Pro 2,a:b1,b:b0,back:b10,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b6,leftstick:b13,lefttrigger:b8,leftx:a0,lefty:a1,rightshoulder:b7,rightstick:b14,righttrigger:b9,rightx:a2,righty:a3,start:b11,x:b4,y:b3,platform:Linux,
05000000c82d00006528000000010000,8BitDo N30 Pro 2,a:b1,b:b0,back:b10,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b6,leftstick:b13,lefttrigger:b8,leftx:a0,lefty:a1,rightshoulder:b7,rightstick:b14,righttrigger:b9,rightx:a2,righty:a3,start:b11,x:b4,y:b3,platform:Linux,
03000000008000000210000011010000,8BitDo NES30,a:b1,b:b2,back:b8,leftshoulder:b4,lefttrigger:b6,leftx:a0,lefty:a1,rightshoulder:b5,righttrigger:b7,start:b9,x:b0,y:b3,platform:Linux,
03000000008000000210000011010000,8BitDo NES30,a:b1,b:b2,back:b8,leftshoulder:b4,lefttrigger:b6,dpdown:+a1,dpleft:-a0,dpright:+a0,dpup:-a1,rightshoulder:b5,righttrigger:b7,start:b9,x:b0,y:b3,platform:Linux,
03000000c82d00000310000011010000,8BitDo NES30,a:b0,b:b1,back:b10,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b7,lefttrigger:b6,rightshoulder:b9,righttrigger:b8,start:b11,x:b3,y:b4,platform:Linux,
05000000c82d00008010000000010000,8BitDo NES30,a:b0,b:b1,back:b10,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b7,lefttrigger:b6,rightshoulder:b9,righttrigger:b8,start:b11,x:b3,y:b4,platform:Linux,
03000000022000000090000011010000,8BitDo NES30 Pro,a:b1,b:b0,back:b10,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b6,leftstick:b13,lefttrigger:b8,leftx:a0,lefty:a1,rightshoulder:b7,rightstick:b14,righttrigger:b9,rightx:a2,righty:a3,start:b11,x:b4,y:b3,platform:Linux,
@@ -951,9 +968,9 @@ xinput,XInput Controller,a:b0,b:b1,back:b6,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,
05000000c82d00000061000000010000,8BitDo SF30 Pro,a:b1,b:b0,back:b10,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b2,leftshoulder:b6,leftstick:b13,lefttrigger:a5,leftx:a0,lefty:a1,rightshoulder:b7,rightstick:b14,righttrigger:a4,rightx:a2,righty:a3,start:b11,x:b4,y:b3,platform:Linux,
030000003512000012ab000010010000,8BitDo SFC30,a:b2,b:b1,back:b6,dpdown:+a1,dpleft:-a0,dpright:+a0,dpup:-a1,leftshoulder:b4,rightshoulder:b5,start:b7,x:b3,y:b0,platform:Linux,
030000003512000021ab000010010000,8BitDo SFC30,a:b1,b:b0,back:b10,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b6,leftx:a0,lefty:a1,rightshoulder:b7,start:b11,x:b4,y:b3,platform:Linux,
03000000c82d000021ab000010010000,8BitDo SFC30,a:b1,b:b0,back:b10,leftshoulder:b6,leftx:a0,lefty:a1,rightshoulder:b7,start:b11,x:b4,y:b3,platform:Linux,
05000000102800000900000000010000,8BitDo SFC30,a:b1,b:b0,back:b10,leftshoulder:b6,leftx:a0,lefty:a1,rightshoulder:b7,start:b11,x:b4,y:b3,platform:Linux,
05000000c82d00003028000000010000,8BitDo SFC30,a:b1,b:b0,back:b10,leftshoulder:b6,leftx:a0,lefty:a1,rightshoulder:b7,start:b11,x:b4,y:b3,platform:Linux,
03000000c82d000021ab000010010000,8BitDo SFC30,a:b1,b:b0,back:b10,leftshoulder:b6,dpdown:+a1,dpleft:-a0,dpright:+a0,dpup:-a1,rightshoulder:b7,start:b11,x:b4,y:b3,platform:Linux,
05000000102800000900000000010000,8BitDo SFC30,a:b1,b:b0,back:b10,leftshoulder:b6,dpdown:+a1,dpleft:-a0,dpright:+a0,dpup:-a1,rightshoulder:b7,start:b11,x:b4,y:b3,platform:Linux,
05000000c82d00003028000000010000,8BitDo SFC30,a:b1,b:b0,back:b10,leftshoulder:b6,dpdown:+a1,dpleft:-a0,dpright:+a0,dpup:-a1,rightshoulder:b7,start:b11,x:b4,y:b3,platform:Linux,
03000000c82d00000160000000000000,8BitDo SN30 Pro,a:b1,b:b0,back:b10,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b6,leftstick:b13,lefttrigger:b8,leftx:a0,lefty:a1,rightshoulder:b7,rightstick:b14,righttrigger:b9,rightx:a3,righty:a4,start:b11,x:b4,y:b3,platform:Linux,
03000000c82d00000160000011010000,8BitDo SN30 Pro,a:b1,b:b0,back:b10,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b6,leftstick:b13,lefttrigger:b8,leftx:a0,lefty:a1,rightshoulder:b7,rightstick:b14,righttrigger:b9,rightx:a2,righty:a3,start:b11,x:b4,y:b3,platform:Linux,
03000000c82d00000161000000000000,8BitDo SN30 Pro,a:b1,b:b0,back:b10,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b6,leftstick:b13,lefttrigger:b8,leftx:a0,lefty:a1,rightshoulder:b7,rightstick:b14,righttrigger:b9,rightx:a3,righty:a4,start:b11,x:b4,y:b3,platform:Linux,
@@ -963,9 +980,9 @@ xinput,XInput Controller,a:b0,b:b1,back:b6,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,
03000000c82d00000260000011010000,8BitDo SN30 Pro+,a:b1,b:b0,back:b10,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b6,leftstick:b13,lefttrigger:b8,leftx:a0,lefty:a1,rightshoulder:b7,rightstick:b14,righttrigger:b9,rightx:a2,righty:a3,start:b11,x:b4,y:b3,platform:Linux,
05000000c82d00000261000000010000,8BitDo SN30 Pro+,a:b1,b:b0,back:b10,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b6,leftstick:b13,lefttrigger:b8,leftx:a0,lefty:a1,rightshoulder:b7,rightstick:b14,righttrigger:b9,rightx:a2,righty:a3,start:b11,x:b4,y:b3,platform:Linux,
05000000202800000900000000010000,8BitDo SNES30,a:b1,b:b0,back:b10,dpdown:b122,dpleft:b119,dpright:b120,dpup:b117,leftshoulder:b6,rightshoulder:b7,start:b11,x:b4,y:b3,platform:Linux,
05000000a00500003232000001000000,8BitDo Zero,a:b0,b:b1,back:b10,leftshoulder:b6,leftx:a0,lefty:a1,rightshoulder:b7,start:b11,x:b3,y:b4,platform:Linux,
05000000a00500003232000001000000,8BitDo Zero,a:b0,b:b1,back:b10,leftshoulder:b6,dpdown:+a1,dpleft:-a0,dpright:+a0,dpup:-a1,rightshoulder:b7,start:b11,x:b3,y:b4,platform:Linux,
05000000a00500003232000008010000,8BitDo Zero,a:b0,b:b1,back:b10,dpdown:+a1,dpleft:-a0,dpright:+a0,dpup:-a1,leftshoulder:b6,rightshoulder:b7,start:b11,x:b3,y:b4,platform:Linux,
03000000c82d00001890000011010000,8BitDo Zero 2,a:b1,b:b0,back:b10,leftshoulder:b6,leftx:a0,lefty:a1,rightshoulder:b7,start:b11,x:b4,y:b3,platform:Linux,
03000000c82d00001890000011010000,8BitDo Zero 2,a:b1,b:b0,back:b10,leftshoulder:b6,dpdown:+a1,dpleft:-a0,dpright:+a0,dpup:-a1,rightshoulder:b7,start:b11,x:b4,y:b3,platform:Linux,
050000005e040000e002000030110000,8BitDo Zero 2,a:b0,b:b1,back:b6,dpdown:+a1,dpleft:-a0,dpright:+a0,dpup:-a1,leftshoulder:b4,rightshoulder:b5,start:b7,x:b2,y:b3,platform:Linux,
05000000c82d00003032000000010000,8BitDo Zero 2,a:b1,b:b0,back:b10,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b6,leftx:a0,lefty:a1,rightshoulder:b7,rightx:a2,righty:a3,start:b11,x:b4,y:b3,platform:Linux,
03000000c01100000355000011010000,Acrux Gamepad,a:b1,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b4,leftstick:b10,lefttrigger:b6,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:b7,rightx:a2,righty:a3,start:b9,x:b0,y:b3,platform:Linux,
@@ -979,7 +996,7 @@ xinput,XInput Controller,a:b0,b:b1,back:b6,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,
03000000491900001904000011010000,Amazon Luna Controller,a:b0,b:b1,back:b6,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b8,leftshoulder:b4,leftstick:b10,lefttrigger:a3,leftx:a0,lefty:a1,misc1:b9,rightshoulder:b5,rightstick:b11,righttrigger:a4,rightx:a2,righty:a5,start:b7,x:b2,y:b3,platform:Linux,
05000000710100001904000000010000,Amazon Luna Controller,a:b0,b:b1,back:b9,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b10,leftshoulder:b4,leftstick:b7,lefttrigger:a5,leftx:a0,lefty:a1,misc1:b11,rightshoulder:b5,rightstick:b8,righttrigger:a4,rightx:a2,righty:a3,start:b6,x:b2,y:b3,platform:Linux,
03000000790000003018000011010000,Arcade Fightstick F300,a:b1,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b4,lefttrigger:b6,leftx:a0,lefty:a1,rightshoulder:b5,righttrigger:b7,start:b9,x:b0,y:b3,platform:Linux,
03000000a30c00002700000011010000,Astro City Mini,a:b2,b:b1,back:b8,leftx:a0,lefty:a1,rightshoulder:b4,righttrigger:b5,start:b9,x:b3,y:b0,platform:Linux,
03000000a30c00002700000011010000,Astro City Mini,a:b2,b:b1,back:b8,dpdown:+a1,dpleft:-a0,dpright:+a0,dpup:-a1,rightshoulder:b4,righttrigger:b5,start:b9,x:b3,y:b0,platform:Linux,
03000000a30c00002800000011010000,Astro City Mini,a:b2,b:b1,back:b8,leftx:a0,lefty:a1,rightshoulder:b4,righttrigger:b5,start:b9,x:b3,y:b0,platform:Linux,
05000000050b00000045000031000000,Asus Gamepad,a:b0,b:b1,back:b9,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b6,leftshoulder:b4,leftstick:b7,lefttrigger:a5,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b8,righttrigger:a4,rightx:a2,righty:a3,start:b10,x:b2,y:b3,platform:Linux,
05000000050b00000045000040000000,Asus Gamepad,a:b0,b:b1,back:b9,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b6,leftshoulder:b4,leftstick:b7,lefttrigger:a5,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b8,righttrigger:a4,rightx:a2,righty:a3,start:b10,x:b2,y:b3,platform:Linux,
@@ -1031,14 +1048,14 @@ xinput,XInput Controller,a:b0,b:b1,back:b6,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,
03000000451300000010000010010000,Genius Maxfire Grandias 12,a:b0,b:b1,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b4,leftstick:b10,lefttrigger:b6,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:b7,rightx:a2,righty:a3,start:b9,x:b2,y:b3,platform:Linux,
03000000f0250000c183000010010000,Goodbetterbest Controller,a:b1,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b4,leftstick:b10,lefttrigger:b6,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:b7,rightx:a2,righty:a3,start:b9,x:b0,y:b3,platform:Linux,
0300000079000000d418000000010000,GPD Win 2 Controller,a:b0,b:b1,back:b6,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b8,leftshoulder:b4,leftstick:b9,lefttrigger:a2,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b10,righttrigger:a5,rightx:a3,righty:a4,start:b7,x:b2,y:b3,platform:Linux,
030000007d0400000540000000010000,Gravis Eliminator Pro,a:b1,b:b2,back:b8,leftshoulder:b4,lefttrigger:b6,leftx:a0,lefty:a1,rightshoulder:b5,righttrigger:b7,start:b9,x:b0,y:b3,platform:Linux,
03000000280400000140000000010000,Gravis Pro,a:b1,b:b2,back:b8,leftshoulder:b4,lefttrigger:b6,leftx:a0,lefty:a1,rightshoulder:b5,righttrigger:b7,start:b9,x:b0,y:b3,platform:Linux,
030000007d0400000540000000010000,Gravis Eliminator Pro,a:b1,b:b2,back:b8,leftshoulder:b4,lefttrigger:b6,dpdown:+a1,dpleft:-a0,dpright:+a0,dpup:-a1,rightshoulder:b5,righttrigger:b7,start:b9,x:b0,y:b3,platform:Linux,
03000000280400000140000000010000,Gravis Pro,a:b1,b:b2,back:b8,leftshoulder:b4,lefttrigger:b6,dpdown:+a1,dpleft:-a0,dpright:+a0,dpup:-a1,rightshoulder:b5,righttrigger:b7,start:b9,x:b0,y:b3,platform:Linux,
030000008f0e00000610000000010000,GreenAsia Electronics Controller,a:b2,b:b1,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b6,leftstick:b9,lefttrigger:b4,leftx:a0,lefty:a1,rightshoulder:b7,rightstick:b10,righttrigger:b5,rightx:a3,righty:a2,start:b11,x:b3,y:b0,platform:Linux,
030000008f0e00001200000010010000,GreenAsia Joystick,a:b0,b:b1,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b4,leftstick:b10,lefttrigger:b5,leftx:a0,lefty:a1,rightshoulder:b6,rightstick:b11,righttrigger:b7,rightx:a3,righty:a2,start:b9,x:b2,y:b3,platform:Linux,
0500000047532067616d657061640000,GS gamepad,a:b0,b:b1,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b8,leftshoulder:b4,leftstick:b10,lefttrigger:b6,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:b7,rightx:a2,righty:a3,start:b9,x:b2,y:b3,platform:Linux,
03000000f0250000c383000010010000,GT VX2,a:b2,b:b1,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b4,leftstick:b10,lefttrigger:b6,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:b7,rightx:a2,righty:a3,start:b9,x:b3,y:b0,platform:Linux,
06000000adde0000efbe000002010000,Hidromancer Controller,a:b0,b:b1,back:b6,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b8,leftshoulder:b4,leftstick:b9,lefttrigger:a2,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b10,righttrigger:a5,rightx:a3,righty:a4,start:b7,x:b2,y:b3,platform:Linux,
03000000d81400000862000011010000,HitBox PS3 PC Analog Mode,a:b1,b:b2,back:b8,guide:b9,leftshoulder:b4,lefttrigger:b6,leftx:a0,lefty:a1,rightshoulder:b5,righttrigger:b7,start:b12,x:b0,y:b3,platform:Linux,
03000000d81400000862000011010000,HitBox PS3 PC Analog Mode,a:b1,b:b2,back:b8,guide:b9,leftshoulder:b4,lefttrigger:b6,dpdown:+a1,dpleft:-a0,dpright:+a0,dpup:-a1,rightshoulder:b5,righttrigger:b7,start:b12,x:b0,y:b3,platform:Linux,
03000000c9110000f055000011010000,HJC Gamepad,a:b0,b:b1,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b4,leftstick:b10,lefttrigger:b6,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:b7,rightx:a2,righty:a3,start:b9,x:b2,y:b3,platform:Linux,
03000000632500002605000010010000,HJDX,a:b0,b:b1,back:b10,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b6,leftstick:b13,lefttrigger:a4,leftx:a0,lefty:a1,rightshoulder:b7,rightstick:b14,righttrigger:a5,rightx:a2,righty:a3,start:b11,x:b3,y:b4,platform:Linux,
030000000d0f00000d00000000010000,Hori,a:b0,b:b6,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b10,leftshoulder:b3,rightshoulder:b7,start:b9,x:b1,y:b2,platform:Linux,
@@ -1055,6 +1072,7 @@ xinput,XInput Controller,a:b0,b:b1,back:b6,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,
030000000d0f00003801000011010000,Hori PC Engine Mini Controller,a:b1,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,start:b9,platform:Linux,
030000000d0f00009200000011010000,Hori Pokken Tournament DX Pro,a:b1,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b4,lefttrigger:b6,rightshoulder:b5,righttrigger:b7,start:b9,x:b0,y:b3,platform:Linux,
030000000d0f0000aa00000011010000,Hori Real Arcade Pro,a:b2,b:b1,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b4,leftstick:b10,lefttrigger:b6,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:b7,rightx:a2,righty:a3,start:b9,x:b3,y:b0,platform:Linux,
030000000d0f00001100000011010000,Hori Real Arcade Pro 3,a:b1,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b4,leftstick:b10,lefttrigger:b6,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:b7,rightx:a2,righty:a3,start:b9,x:b0,y:b3,platform:Linux,
030000000d0f00002200000011010000,Hori Real Arcade Pro 3,a:b1,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b4,lefttrigger:b6,rightshoulder:b5,righttrigger:b7,start:b9,x:b0,y:b3,platform:Linux,
030000000d0f00006a00000011010000,Hori Real Arcade Pro 4,a:b1,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b4,leftstick:b10,lefttrigger:a3,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:a4,rightx:a2,righty:a5,start:b9,x:b0,y:b3,platform:Linux,
030000000d0f00006b00000011010000,Hori Real Arcade Pro 4,a:b1,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b4,leftstick:b10,lefttrigger:b6,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:b7,rightx:a2,righty:a3,start:b9,x:b0,y:b3,platform:Linux,
@@ -1068,12 +1086,13 @@ xinput,XInput Controller,a:b0,b:b1,back:b6,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,
03000000341a000005f7000010010000,HuiJia GameCube Controller Adapter,a:b1,b:b2,dpdown:b14,dpleft:b15,dpright:b13,dpup:b12,lefttrigger:a3,leftx:a0,lefty:a1,rightshoulder:b7,righttrigger:a4,rightx:a5,righty:a2,start:b9,x:b0,y:b3,platform:Linux,
030000008f0e00001330000010010000,HuiJia SNES Controller,a:b2,b:b1,back:b8,dpdown:+a1,dpleft:-a0,dpright:+a0,dpup:-a1,leftshoulder:b6,rightshoulder:b7,start:b9,x:b3,y:b0,platform:Linux,
03000000242e00008816000001010000,Hyperkin X91,a:b0,b:b1,back:b6,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b8,leftshoulder:b4,leftstick:b9,lefttrigger:a2,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b10,righttrigger:a5,rightx:a3,righty:a4,start:b7,x:b2,y:b3,platform:Linux,
03000000830500006020000010010000,iBuffalo SNES Controller,a:b1,b:b0,back:b6,dpdown:+a1,dpleft:-a0,dpright:+a0,dpup:-a1,leftshoulder:b4,rightshoulder:b5,start:b7,x:b3,y:b2,platform:Linux,
050000006964726f69643a636f6e0000,idroidcon Controller,a:b1,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b4,leftstick:b10,lefttrigger:b6,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:b7,rightx:a2,righty:a3,start:b9,x:b0,y:b3,platform:Linux,
03000000b50700001503000010010000,Impact,a:b2,b:b3,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b4,leftstick:b10,lefttrigger:b5,leftx:a0,lefty:a1,rightshoulder:b6,rightstick:b11,righttrigger:b7,rightx:a3,righty:a2,start:b9,x:b0,y:b1,platform:Linux,
03000000d80400008200000003000000,IMS PCU0,a:b1,b:b0,back:b4,dpdown:+a1,dpleft:-a0,dpright:+a0,dpup:-a1,start:b5,x:b3,y:b2,platform:Linux,
03000000120c00000500000010010000,InterAct AxisPad,a:b2,b:b3,back:b10,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b4,leftstick:b8,lefttrigger:b6,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b9,righttrigger:b7,rightx:a3,righty:a2,start:b11,x:b0,y:b1,platform:Linux,
03000000ef0500000300000000010000,InterAct AxisPad,a:b2,b:b3,back:b10,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b4,leftstick:b8,lefttrigger:b6,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b9,righttrigger:b7,rightx:a3,righty:a2,start:b11,x:b0,y:b1,platform:Linux,
03000000fd0500000030000000010000,InterAct GoPad I73000,a:b3,b:b4,back:b6,leftx:a0,lefty:a1,rightshoulder:b2,righttrigger:b5,start:b7,x:b0,y:b1,platform:Linux,
03000000fd0500000030000000010000,InterAct GoPad,a:b3,b:b4,x:b0,y:b1,leftshoulder:b6,rightshoulder:b2,leftx:a0,lefty:a1,lefttrigger:b7,righttrigger:b5,platform:Linux,
03000000fd0500002a26000000010000,InterAct HammerHead FX,a:b3,b:b4,back:b10,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b6,leftstick:b2,lefttrigger:b8,leftx:a0,lefty:a1,rightshoulder:b7,rightstick:b5,righttrigger:b9,rightx:a2,righty:a3,start:b11,x:b0,y:b1,platform:Linux,
0500000049190000020400001b010000,Ipega PG 9069,a:b0,b:b1,back:b10,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b161,leftshoulder:b6,leftstick:b13,lefttrigger:a5,leftx:a0,lefty:a1,rightshoulder:b7,rightstick:b14,righttrigger:a4,rightx:a2,righty:a3,start:b11,x:b3,y:b4,platform:Linux,
03000000632500007505000011010000,Ipega PG 9099,a:b2,b:b1,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b4,leftstick:b10,lefttrigger:b6,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:b7,rightx:a2,righty:a3,start:b9,x:b3,y:b0,platform:Linux,
@@ -1123,12 +1142,13 @@ xinput,XInput Controller,a:b0,b:b1,back:b6,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,
0300000079000000d218000011010000,Mayflash Magic NS,a:b1,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b4,leftstick:b10,lefttrigger:b6,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:b7,rightx:a2,righty:a3,start:b9,x:b0,y:b3,platform:Linux,
03000000d620000010a7000011010000,Mayflash Magic NS,a:b1,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b4,leftstick:b10,lefttrigger:b6,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:b7,rightx:a2,righty:a3,start:b9,x:b0,y:b3,platform:Linux,
03000000242f0000f700000001010000,Mayflash Magic S Pro,a:b0,b:b1,back:b6,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b8,leftshoulder:b4,leftstick:b9,lefttrigger:a2,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b10,righttrigger:a5,rightx:a3,righty:a4,start:b7,x:b2,y:b3,platform:Linux,
0300000025090000e803000001010000,Mayflash Wii Classic Controller,a:b1,b:b0,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b10,leftshoulder:a4,lefttrigger:b6,leftx:a0,lefty:a1,rightshoulder:a5,righttrigger:b7,rightx:a2,righty:a3,start:b9,x:b3,y:b2,platform:Linux,
0300000025090000e803000001010000,Mayflash Wii Classic Adapter,a:b1,b:b0,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b10,leftshoulder:a4,lefttrigger:b6,leftx:a0,lefty:a1,rightshoulder:a5,righttrigger:b7,rightx:a2,righty:a3,start:b9,x:b3,y:b2,platform:Linux,
03000000790000000318000011010000,Mayflash Wii DolphinBar,a:b2,b:b3,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b11,leftshoulder:b4,lefttrigger:b6,leftx:a0,lefty:a1,rightshoulder:b5,righttrigger:b7,rightx:a2,righty:a3,start:b9,x:b0,y:b1,platform:Linux,
03000000b50700001203000010010000,Mega World Logic 3 Controller,a:b2,b:b3,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b4,leftstick:b10,lefttrigger:b5,leftx:a0,lefty:a1,rightshoulder:b6,rightstick:b11,righttrigger:b7,rightx:a3,righty:a2,start:b9,x:b0,y:b1,platform:Linux,
03000000780000000600000010010000,Microntek Joystick,a:b2,b:b1,back:b8,leftshoulder:b6,lefttrigger:b4,leftx:a0,lefty:a1,rightshoulder:b7,righttrigger:b5,start:b9,x:b3,y:b0,platform:Linux,
030000005e0400002800000000010000,Microsoft Dual Strike,a:b3,b:b2,back:b4,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b6,lefttrigger:b8,rightshoulder:b7,rightx:a0,righty:a1~,start:b5,x:b1,y:b0,platform:Linux,
030000005e0400000e00000000010000,Microsoft SideWinder,a:b0,b:b1,back:b9,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b6,rightshoulder:b7,start:b8,x:b3,y:b4,platform:Linux,
030000005e0400000700000000010000,Microsoft SideWinder Gamepad,a:b0,b:b1,back:b8,leftshoulder:b6,lefttrigger:b7,leftx:a0,lefty:a1,rightshoulder:b5,righttrigger:b2,start:b9,x:b3,y:b4,platform:Linux,
030000005e0400000700000000010000,Microsoft SideWinder Gamepad,a:b0,b:b1,back:b8,leftshoulder:b6,lefttrigger:b7,dpdown:+a1,dpleft:-a0,dpright:+a0,dpup:-a1,rightshoulder:b5,righttrigger:b2,start:b9,x:b3,y:b4,platform:Linux,
030000005e0400002700000000010000,Microsoft SideWinder Plug and Play,a:b0,b:b1,dpdown:+a1,dpleft:-a0,dpright:+a0,dpup:-a1,lefttrigger:b4,righttrigger:b5,x:b2,y:b3,platform:Linux,
030000005e0400008502000000010000,Microsoft Xbox,a:b0,b:b1,back:b6,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b5,leftstick:b8,lefttrigger:a2,leftx:a0,lefty:a1,rightshoulder:b2,rightstick:b9,righttrigger:a5,rightx:a3,righty:a4,start:b7,x:b3,y:b4,platform:Linux,
030000005e0400008e02000001000000,Microsoft Xbox 360,a:b0,b:b1,back:b6,dpdown:h0.1,dpleft:h0.2,dpright:h0.8,dpup:h0.4,leftshoulder:b4,leftstick:b9,lefttrigger:a2,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b10,righttrigger:a5,rightx:a3,righty:a4,start:b7,x:b2,y:b3,platform:Linux,
@@ -1155,7 +1175,7 @@ xinput,XInput Controller,a:b0,b:b1,back:b6,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,
05000000c62400002a89000000010000,MOGA XP5A Plus,a:b0,b:b1,back:b10,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b22,leftshoulder:b6,leftstick:b13,lefttrigger:a5,leftx:a0,lefty:a1,rightshoulder:b7,rightstick:b14,righttrigger:a4,rightx:a2,righty:a3,start:b11,x:b3,y:b4,platform:Linux,
05000000c62400001a89000000010000,MOGA XP5X Plus,a:b0,b:b1,back:b10,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b6,leftstick:b13,lefttrigger:a5,leftx:a0,lefty:a1,rightshoulder:b7,rightstick:b14,righttrigger:a4,rightx:a2,righty:a3,start:b11,x:b3,y:b4,platform:Linux,
03000000250900006688000000010000,MP8866 Super Dual Box,a:b2,b:b1,back:b9,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b6,leftstick:b10,lefttrigger:b4,leftx:a0,lefty:a1,rightshoulder:b7,rightstick:b11,righttrigger:b5,rightx:a2,righty:a3,start:b8,x:b3,y:b0,platform:Linux,
030000005e0400008e02000010020000,MSI GC20 V2,a:b0,b:b1,y:b3,x:b2,start:b7,back:b6,guide:b8,leftstick:b9,rightstick:b10,leftshoulder:b4,rightshoulder:b5,dpup:b12,dpleft:b14,dpdown:b13,dpright:b15,leftx:a0,lefty:a1,rightx:a3,righty:a4,lefttrigger:a2,righttrigger:a5,platform:Linux,
030000005e0400008e02000010020000,MSI GC20 V2,a:b0,b:b1,back:b6,dpdown:b13,dpleft:b14,dpright:b15,dpup:b12,guide:b8,leftshoulder:b4,leftstick:b9,lefttrigger:a2,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b10,righttrigger:a5,rightx:a3,righty:a4,start:b7,x:b2,y:b3,platform:Linux,
030000006b1400000906000014010000,Nacon Asymmetric Wireless PS4 Controller,a:b0,b:b1,back:b6,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b8,leftshoulder:b4,leftstick:b9,lefttrigger:a2,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b10,righttrigger:a5,rightx:a3,righty:a4,start:b7,x:b2,y:b3,platform:Linux,
030000006b140000010c000010010000,Nacon GC 400ES,a:b0,b:b1,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b4,leftstick:b10,lefttrigger:b6,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:b7,rightx:a2,righty:a3,start:b9,x:b2,y:b3,platform:Linux,
03000000853200000706000012010000,Nacon GC-100,a:b0,b:b1,back:b6,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b8,leftshoulder:b4,leftstick:b9,lefttrigger:a2,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b10,righttrigger:a5,rightx:a3,righty:a4,start:b7,x:b2,y:b3,platform:Linux,
@@ -1164,7 +1184,6 @@ xinput,XInput Controller,a:b0,b:b1,back:b6,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,
0300000092120000474e000000010000,NeoGeo X Arcade Stick,a:b0,b:b1,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,start:b9,x:b3,y:b2,platform:Linux,
03000000790000004518000010010000,Nexilux GameCube Controller Adapter,a:b1,b:b0,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,lefttrigger:a3,leftx:a0,lefty:a1,rightshoulder:b7,righttrigger:a4,rightx:a5,righty:a2,start:b9,x:b2,y:b3,platform:Linux,
030000001008000001e5000010010000,NEXT SNES Controller,a:b2,b:b1,back:b8,dpdown:+a1,dpleft:-a0,dpright:+a0,dpup:-a1,leftshoulder:b4,rightshoulder:b5,righttrigger:b6,start:b9,x:b3,y:b0,platform:Linux,
050000004e696d6275732b0000000000,Nimbus Plus,a:b0,b:b1,back:b10,guide:b11,leftshoulder:b4,leftstick:b8,lefttrigger:b6,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b9,righttrigger:b7,rightx:a2,righty:a3,start:b12,x:b2,y:b3,platform:Linux,
060000007e0500003713000000000000,Nintendo 3DS,a:b0,b:b1,back:b8,dpdown:b11,dpleft:b12,dpright:b13,dpup:b10,leftshoulder:b4,leftx:a0,lefty:a1,rightshoulder:b5,rightx:a2,righty:a3,start:b9,x:b3,y:b2,platform:Linux,
030000007e0500003703000000016800,Nintendo GameCube Controller,a:b0,b:b2,dpdown:b6,dpleft:b4,dpright:b5,dpup:b7,lefttrigger:a4,leftx:a0,lefty:a1~,rightshoulder:b9,righttrigger:a5,rightx:a2,righty:a3~,start:b8,x:b1,y:b3,platform:Linux,
03000000790000004618000010010000,Nintendo GameCube Controller Adapter,a:b1,b:b0,dpdown:b14,dpleft:b15,dpright:b13,dpup:b12,lefttrigger:b4,leftx:a0,lefty:a1,rightshoulder:b7,righttrigger:b5,rightx:a5~,righty:a2~,start:b9,x:b2,y:b3,platform:Linux,
@@ -1256,6 +1275,8 @@ xinput,XInput Controller,a:b0,b:b1,back:b6,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,
050000004c050000e60c000000810000,PS5 Controller,a:b0,b:b1,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b10,leftshoulder:b4,leftstick:b11,lefttrigger:a2,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b12,righttrigger:a5,rightx:a3,righty:a4,start:b9,x:b3,y:b2,platform:Linux,
03000000300f00001211000011010000,Qanba Arcade Joystick,a:b2,b:b0,back:b10,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b8,leftshoulder:b5,lefttrigger:b4,leftx:a0,lefty:a1,rightshoulder:b7,righttrigger:b6,start:b9,x:b1,y:b3,platform:Linux,
03000000300f00001210000010010000,Qanba Joystick Plus,a:b0,b:b1,back:b8,leftshoulder:b5,lefttrigger:b7,leftx:a0,lefty:a1,rightshoulder:b4,righttrigger:b6,start:b9,x:b2,y:b3,platform:Linux,
03000000222c00000223000011010000,Qanba Obsidian Arcade Joystick (PS3),a:b1,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b4,leftstick:b10,lefttrigger:b6,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:b7,rightx:a2,righty:a3,start:b9,x:b0,y:b3,platform:Linux,
03000000222c00000023000011010000,Qanba Obsidian Arcade Joystick (PS4),a:b1,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b4,leftstick:b10,lefttrigger:a3,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:a4,rightx:a2,righty:a5,start:b9,x:b0,y:b3,platform:Linux,
030000009b2800000300000001010000,Raphnet 4nes4snes,a:b0,b:b4,back:b2,leftshoulder:b6,leftx:a0,lefty:a1,rightshoulder:b7,start:b3,x:b1,y:b5,platform:Linux,
030000009b2800004200000001010000,Raphnet Dual NES Adapter,a:b0,b:b1,back:b2,dpdown:+a1,dpleft:-a0,dpright:+a0,dpup:-a1,start:b3,platform:Linux,
030000009b2800003200000001010000,Raphnet GC and N64 Adapter,a:b0,b:b7,dpdown:b11,dpleft:b12,dpright:b13,dpup:b10,lefttrigger:b4,leftx:a0,lefty:a1,rightshoulder:b2,righttrigger:b5,rightx:a3,righty:a4,start:b3,x:b1,y:b8,platform:Linux,
@@ -1299,8 +1320,8 @@ xinput,XInput Controller,a:b0,b:b1,back:b6,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,
03000000a30c00002500000011010000,Sega Genesis Mini 3B Controller,a:b2,b:b1,dpdown:+a4,dpleft:-a3,dpright:+a3,dpup:-a4,righttrigger:b5,start:b9,platform:Linux,
03000000790000001100000011010000,Sega Saturn,a:b1,b:b2,back:b8,dpdown:+a1,dpleft:-a0,dpright:+a0,dpup:-a1,leftshoulder:b6,lefttrigger:b7,rightshoulder:b5,righttrigger:b4,start:b9,x:b0,y:b3,platform:Linux,
03000000790000002201000011010000,Sega Saturn,a:b0,b:b1,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b4,lefttrigger:b5,rightshoulder:b6,righttrigger:b7,start:b9,x:b2,y:b3,platform:Linux,
03000000b40400000a01000000010000,Sega Saturn,a:b0,b:b1,leftshoulder:b6,lefttrigger:b7,leftx:a0,lefty:a1,rightshoulder:b5,righttrigger:b2,start:b8,x:b3,y:b4,platform:Linux,
030000001f08000001e4000010010000,SFC Controller,a:b2,b:b1,back:b8,leftshoulder:b4,leftx:a0,lefty:a1,rightshoulder:b5,start:b9,x:b3,y:b0,platform:Linux,
03000000b40400000a01000000010000,Sega Saturn,a:b0,b:b1,leftshoulder:b6,lefttrigger:b7,dpdown:+a1,dpleft:-a0,dpright:+a0,dpup:-a1,rightshoulder:b5,righttrigger:b2,start:b8,x:b3,y:b4,platform:Linux,
030000001f08000001e4000010010000,SFC Controller,a:b2,b:b1,back:b8,leftshoulder:b4,dpdown:+a1,dpleft:-a0,dpright:+a0,dpup:-a1,rightshoulder:b5,start:b9,x:b3,y:b0,platform:Linux,
03000000632500002305000010010000,ShanWan Gamepad,a:b2,b:b1,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b4,leftstick:b10,lefttrigger:b6,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:b7,rightx:a2,righty:a3,start:b9,x:b3,y:b0,platform:Linux,
03000000f025000021c1000010010000,Shanwan Gioteck PS3 Controller,a:b2,b:b1,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b4,leftstick:b10,lefttrigger:b6,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:b7,rightx:a2,righty:a3,start:b9,x:b3,y:b0,platform:Linux,
03000000632500007505000010010000,Shanwan PS3 PC,a:b2,b:b1,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b4,leftstick:b10,lefttrigger:b6,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:b7,rightx:a2,righty:a3,start:b9,x:b3,y:b0,platform:Linux,
@@ -1314,7 +1335,7 @@ xinput,XInput Controller,a:b0,b:b1,back:b6,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,
030000005e0400008e02000020200000,SpeedLink Xeox Pro Analog,a:b0,b:b1,back:b6,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b8,leftshoulder:b4,leftstick:b9,lefttrigger:a2,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b10,righttrigger:a5,rightx:a3,righty:a4,start:b7,x:b2,y:b3,platform:Linux,
03000000d11800000094000011010000,Stadia Controller,a:b0,b:b1,back:b6,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b8,leftshoulder:b4,leftstick:b9,lefttrigger:a5,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b10,righttrigger:a4,rightx:a2,righty:a3,start:b7,x:b2,y:b3,platform:Linux,
03000000de2800000112000001000000,Steam Controller,a:b0,b:b1,back:b6,dpdown:b14,dpleft:b15,dpright:b13,dpup:b12,guide:b8,leftshoulder:b4,leftstick:b9,lefttrigger:a2,leftx:a0,lefty:a1,paddle1:b11,paddle2:b10,rightshoulder:b5,righttrigger:a3,start:b7,x:b2,y:b3,platform:Linux,
03000000de2800000112000011010000,Steam Controller,a:b2,b:b3,x:b4,y:b5,back:b10,guide:b12,start:b11,leftstick:b13,rightstick:b14,leftshoulder:b6,rightshoulder:b7,dpup:-a5,dpdown:+a5,dpleft:-a4,dpright:+a4,paddle1:b15,paddle2:b16,leftx:a0,lefty:a1,rightx:a2,righty:a3,lefttrigger:a7,righttrigger:a6,platform:Linux,
03000000de2800000112000011010000,Steam Controller,a:b2,b:b3,back:b10,dpdown:+a5,dpleft:-a4,dpright:+a4,dpup:-a5,guide:b12,leftshoulder:b6,leftstick:b13,lefttrigger:a7,leftx:a0,lefty:a1,paddle1:b15,paddle2:b16,rightshoulder:b7,rightstick:b14,righttrigger:a6,rightx:a2,righty:a3,start:b11,x:b4,y:b5,platform:Linux,
03000000de2800000211000001000000,Steam Controller,a:b0,b:b1,back:b6,dpdown:b14,dpleft:b15,dpright:b13,dpup:b12,guide:b8,leftshoulder:b4,leftstick:b9,lefttrigger:a2,leftx:a0,lefty:a1,paddle1:b11,paddle2:b10,rightshoulder:b5,righttrigger:a3,start:b7,x:b2,y:b3,platform:Linux,
03000000de2800000211000011010000,Steam Controller,a:b2,b:b3,back:b10,dpdown:b18,dpleft:b19,dpright:b20,dpup:b17,guide:b12,leftshoulder:b6,leftstick:b13,lefttrigger:b8,leftx:a0,lefty:a1,paddle1:b16,paddle2:b15,rightshoulder:b7,righttrigger:b9,rightx:a2,righty:a3,start:b11,x:b4,y:b5,platform:Linux,
03000000de2800004211000001000000,Steam Controller,a:b0,b:b1,back:b6,dpdown:b14,dpleft:b15,dpright:b13,dpup:b12,guide:b8,leftshoulder:b4,leftstick:b9,lefttrigger:a2,leftx:a0,lefty:a1,paddle1:b11,paddle2:b10,rightshoulder:b5,righttrigger:a3,start:b7,x:b2,y:b3,platform:Linux,
@@ -1324,14 +1345,15 @@ xinput,XInput Controller,a:b0,b:b1,back:b6,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,
05000000de2800000511000001000000,Steam Controller,a:b0,b:b1,back:b6,dpdown:b14,dpleft:b15,dpright:b13,dpup:b12,guide:b8,leftshoulder:b4,leftstick:b9,lefttrigger:a2,leftx:a0,lefty:a1,paddle1:b11,paddle2:b10,rightshoulder:b5,righttrigger:a3,start:b7,x:b2,y:b3,platform:Linux,
05000000de2800000611000001000000,Steam Controller,a:b0,b:b1,back:b6,dpdown:b14,dpleft:b15,dpright:b13,dpup:b12,guide:b8,leftshoulder:b4,leftstick:b9,lefttrigger:a2,leftx:a0,lefty:a1,paddle1:b11,paddle2:b10,rightshoulder:b5,righttrigger:a3,start:b7,x:b2,y:b3,platform:Linux,
03000000de280000ff11000001000000,Steam Virtual Gamepad,a:b0,b:b1,back:b6,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b8,leftshoulder:b4,leftstick:b9,lefttrigger:a2,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b10,righttrigger:a5,rightx:a3,righty:a4,start:b7,x:b2,y:b3,platform:Linux,
050000004e696d6275732b0000000000,SteelSeries Nimbus Plus,a:b0,b:b1,back:b10,guide:b11,leftshoulder:b4,leftstick:b8,lefttrigger:b6,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b9,righttrigger:b7,rightx:a2,righty:a3,start:b12,x:b2,y:b3,platform:Linux,
03000000381000003014000075010000,SteelSeries Stratus Duo,a:b0,b:b1,back:b6,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b8,leftshoulder:b4,leftstick:b9,lefttrigger:a2,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b10,righttrigger:a5,rightx:a3,righty:a4,start:b7,x:b2,y:b3,platform:Linux,
03000000381000003114000075010000,SteelSeries Stratus Duo,a:b0,b:b1,back:b6,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b8,leftshoulder:b4,leftstick:b9,lefttrigger:a2,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b10,righttrigger:a5,rightx:a3,righty:a4,start:b7,x:b2,y:b3,platform:Linux,
0500000011010000311400001b010000,SteelSeries Stratus Duo,a:b0,b:b1,back:b10,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b32,leftshoulder:b6,leftstick:b13,lefttrigger:a5,leftx:a0,lefty:a1,rightshoulder:b7,rightstick:b14,righttrigger:a4,rightx:a2,righty:a3,start:b11,x:b3,y:b4,platform:Linux,
05000000110100001914000009010000,SteelSeries Stratus XL,a:b0,b:b1,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b6,leftstick:b13,lefttrigger:a5,leftx:a0,lefty:a1,rightshoulder:b7,rightstick:b14,righttrigger:a4,rightx:a2,righty:a3,start:b11,x:b3,y:b4,platform:Linux,
03000000ad1b000038f0000090040000,Street Fighter IV Fightstick TE,a:b0,b:b1,back:b6,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b8,leftshoulder:b4,lefttrigger:a2,leftx:a0,lefty:a1,rightshoulder:b5,righttrigger:a5,rightx:a3,righty:a4,start:b7,x:b2,y:b3,platform:Linux,
030000003b07000004a1000000010000,Suncom SFX Plus,a:b0,b:b2,back:b7,leftshoulder:b6,lefttrigger:b4,leftx:a0,lefty:a1,rightshoulder:b9,righttrigger:b5,start:b8,x:b1,y:b3,platform:Linux,
030000003b07000004a1000000010000,Suncom SFX Plus,a:b0,b:b2,back:b7,leftshoulder:b6,lefttrigger:b4,dpdown:+a1,dpleft:-a0,dpright:+a0,dpup:-a1,rightshoulder:b9,righttrigger:b5,start:b8,x:b1,y:b3,platform:Linux,
03000000666600000488000000010000,Super Joy Box 5 Pro,a:b2,b:b1,back:b9,dpdown:b14,dpleft:b15,dpright:b13,dpup:b12,leftshoulder:b6,leftstick:b10,lefttrigger:b4,leftx:a0,lefty:a1,rightshoulder:b7,rightstick:b11,righttrigger:b5,rightx:a2,righty:a3,start:b8,x:b3,y:b0,platform:Linux,
0300000000f00000f100000000010000,Super RetroPort,a:b1,b:b5,back:b2,leftshoulder:b6,leftx:a0,lefty:a1,rightshoulder:b7,start:b3,x:b0,y:b4,platform:Linux,
0300000000f00000f100000000010000,Super RetroPort,a:b1,b:b5,back:b2,leftshoulder:b6,dpdown:+a1,dpleft:-a0,dpright:+a0,dpup:-a1,rightshoulder:b7,start:b3,x:b0,y:b4,platform:Linux,
030000008f0e00000d31000010010000,SZMY Power 3 Turbo,a:b1,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b4,leftstick:b10,lefttrigger:b6,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:b7,rightx:a2,righty:a3,start:b9,x:b0,y:b3,platform:Linux,
03000000457500002211000010010000,SZMY Power Gamepad,a:b2,b:b1,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b4,leftstick:b10,lefttrigger:b6,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:b7,rightx:a2,righty:a3,start:b9,x:b3,y:b0,platform:Linux,
030000008f0e00001431000010010000,SZMY Power PS3,a:b1,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b4,leftstick:b10,lefttrigger:b6,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:b7,rightx:a2,righty:a3,start:b9,x:b0,y:b3,platform:Linux,
+4 -6
View File
@@ -135,12 +135,10 @@ PS_OUTPUT ps_filter_complex(PS_INPUT input) // triangular
{
PS_OUTPUT output;
float2 texdim, halfpixel;
Texture.GetDimensions(texdim.x, texdim.y);
if (ddy(input.t.y) * input.t.y > 0.5)
output.c = sample_c(input.t);
else
output.c = (0.9 - 0.4 * cos(2 * PI * input.t.y * texdim.y)) * sample_c(float2(input.t.x, (floor(input.t.y * texdim.y) + 0.5) / texdim.y));
float2 texdim;
Texture.GetDimensions(texdim.x, texdim.y);
output.c = (0.9 - 0.4 * cos(2 * PI * input.t.y * texdim.y)) * sample_c(float2(input.t.x, (floor(input.t.y * texdim.y) + 0.5) / texdim.y));
return output;
}
+2 -10
View File
@@ -119,18 +119,10 @@ void ps_filter_triangular() // triangular
#ifdef ps_filter_complex
void ps_filter_complex()
{
const float PI = 3.14159265359f;
vec2 texdim = vec2(textureSize(TextureSampler, 0));
vec4 c;
if (dFdy(PSin_t.y) * PSin_t.y > 0.5f) {
c = sample_c();
} else {
float factor = (0.9f - 0.4f * cos(2.0f * PI * PSin_t.y * texdim.y));
c = factor * texture(TextureSampler, vec2(PSin_t.x, (floor(PSin_t.y * texdim.y) + 0.5f) / texdim.y));
}
float factor = (0.9f - 0.4f * cos(2.0f * PI * PSin_t.y * texdim.y));
vec4 c = factor * texture(TextureSampler, vec2(PSin_t.x, (floor(PSin_t.y * texdim.y) + 0.5f) / texdim.y));
SV_Target0 = c;
}
+2 -4
View File
@@ -106,10 +106,8 @@ void ps_filter_complex() // triangular
{
const float PI = 3.14159265359f;
vec2 texdim = vec2(textureSize(samp0, 0));
if (dFdy(v_tex.y) * v_tex.y > 0.5)
o_col0 = sample_c(v_tex);
else
o_col0 = (0.9 - 0.4 * cos(2 * PI * v_tex.y * texdim.y)) * sample_c(vec2(v_tex.x, (floor(v_tex.y * texdim.y) + 0.5) / texdim.y));
o_col0 = (0.9 - 0.4 * cos(2 * PI * v_tex.y * texdim.y)) * sample_c(vec2(v_tex.x, (floor(v_tex.y * texdim.y) + 0.5) / texdim.y));
}
#endif
+6 -2
View File
@@ -158,9 +158,11 @@ for ARG in "$@"; do
--rel|--release ) flags="$flags -DCMAKE_BUILD_TYPE=Release" ; build="$root/build_rel";;
--prof ) flags="$flags -DCMAKE_BUILD_TYPE=RelWithDebInfo"; build="$root/build_prof";;
--strip ) flags="$flags -DCMAKE_BUILD_STRIP=TRUE" ;;
--use-system-yaml ) flags="$flags -DUSE_SYSTEM_YAML=TRUE" ;;
--asan ) flags="$flags -DUSE_ASAN=TRUE" ;;
--gtk2 ) flags="$flags -DGTK2_API=TRUE" ;;
--qt6 ) flags="$flags -DQT_BUILD=TRUE" ;;
--use-system ) flags="$flags -DUSE_SYSTEM_LIBS=ON" ;;
--use-bundled ) flags="$flags -DUSE_SYSTEM_LIBS=OFF" ;;
--lto ) flags="$flags -DCMAKE_INTERPROCEDURAL_OPTIMIZATION=TRUE" ;;
--pgo-optimize ) flags="$flags -DUSE_PGO_OPTIMIZE=TRUE" ;;
--pgo-generate ) flags="$flags -DUSE_PGO_GENERATE=TRUE" ;;
@@ -185,13 +187,15 @@ for ARG in "$@"; do
echo
echo "** Distribution Compatibilities **"
echo "--no-portaudio : Skip portaudio for SPU2."
echo "--use-system-yaml : Use system rapidyaml library."
echo
echo "** Expert Developer option **"
echo "--gtk2 : use GTK 2 instead of GTK 3."
echo "--qt6 : Experimental qt 6 ui."
echo "--no-trans : Don't regenerate mo files when building."
echo "--strip : Strip binaries to save a small amount of space."
echo "--clang : Build with Clang/llvm."
echo "--use-system : Only use system libs."
echo "--use-bundled : Only use bundled libs."
echo "--intel : Build with ICC (Intel compiler)."
echo "--lto : Use Link Time Optimization."
echo "--pgo-generate : Executable will generate profiling information when run."
+13 -11
View File
@@ -284,7 +284,7 @@ void Texture::TransitionSubresourceToState(ID3D12GraphicsCommandList* cmdlist, u
cmdlist->ResourceBarrier(1, &barrier);
}
bool Texture::BeginStreamUpdate(u32 level, u32 x, u32 y, u32 width, u32 height, void** out_data, u32* out_data_pitch)
ID3D12GraphicsCommandList* Texture::BeginStreamUpdate(ID3D12GraphicsCommandList* cmdlist, u32 level, u32 x, u32 y, u32 width, u32 height, void** out_data, u32* out_data_pitch)
{
const u32 copy_pitch = Common::AlignUpPow2(width * GetTexelSize(m_format), D3D12_TEXTURE_DATA_PITCH_ALIGNMENT);
const u32 upload_size = copy_pitch * height;
@@ -297,16 +297,19 @@ bool Texture::BeginStreamUpdate(u32 level, u32 x, u32 y, u32 width, u32 height,
if (!g_d3d12_context->GetTextureStreamBuffer().ReserveMemory(upload_size, D3D12_TEXTURE_DATA_PLACEMENT_ALIGNMENT))
{
Console.Error("Failed to reserve %u bytes for %ux%u upload", upload_size, width, height);
return false;
return nullptr;
}
// cmdlist change
return g_d3d12_context->GetInitCommandList();
}
*out_data = g_d3d12_context->GetTextureStreamBuffer().GetCurrentHostPointer();
*out_data_pitch = copy_pitch;
return true;
return cmdlist;
}
void Texture::EndStreamUpdate(u32 level, u32 x, u32 y, u32 width, u32 height)
void Texture::EndStreamUpdate(ID3D12GraphicsCommandList* cmdlist, u32 level, u32 x, u32 y, u32 width, u32 height)
{
const u32 copy_pitch = Common::AlignUpPow2(width * GetTexelSize(m_format), D3D12_TEXTURE_DATA_PITCH_ALIGNMENT);
const u32 upload_size = copy_pitch * height;
@@ -315,10 +318,10 @@ void Texture::EndStreamUpdate(u32 level, u32 x, u32 y, u32 width, u32 height)
const u32 sb_offset = sb.GetCurrentOffset();
sb.CommitMemory(upload_size);
CopyFromBuffer(level, x, y, width, height, copy_pitch, sb.GetBuffer(), sb_offset);
CopyFromBuffer(cmdlist, level, x, y, width, height, copy_pitch, sb.GetBuffer(), sb_offset);
}
void Texture::CopyFromBuffer(u32 level, u32 x, u32 y, u32 width, u32 height, u32 pitch, ID3D12Resource* buffer, u32 buffer_offset)
void Texture::CopyFromBuffer(ID3D12GraphicsCommandList* cmdlist, u32 level, u32 x, u32 y, u32 width, u32 height, u32 pitch, ID3D12Resource* buffer, u32 buffer_offset)
{
D3D12_TEXTURE_COPY_LOCATION src;
src.pResource = buffer;
@@ -337,7 +340,6 @@ void Texture::CopyFromBuffer(u32 level, u32 x, u32 y, u32 width, u32 height, u32
const D3D12_BOX src_box{0u, 0u, 0u, width, height, 1u};
const D3D12_RESOURCE_STATES old_state = m_state;
ID3D12GraphicsCommandList* cmdlist = g_d3d12_context->GetCommandList();
TransitionToState(cmdlist, D3D12_RESOURCE_STATE_COPY_DEST);
cmdlist->CopyTextureRegion(&dst, x, y, 0, &src, &src_box);
TransitionToState(cmdlist, old_state);
@@ -381,7 +383,7 @@ static ID3D12Resource* CreateStagingBuffer(u32 height, const void* data, u32 pit
return resource.get();
}
bool Texture::LoadData(u32 level, u32 x, u32 y, u32 width, u32 height, const void* data, u32 pitch)
bool Texture::LoadData(ID3D12GraphicsCommandList* cmdlist, u32 level, u32 x, u32 y, u32 width, u32 height, const void* data, u32 pitch)
{
const u32 texel_size = GetTexelSize(m_format);
const u32 upload_pitch = Common::AlignUpPow2(width * texel_size, D3D12_TEXTURE_DATA_PITCH_ALIGNMENT);
@@ -392,17 +394,17 @@ bool Texture::LoadData(u32 level, u32 x, u32 y, u32 width, u32 height, const voi
if (!staging_buffer)
return false;
CopyFromBuffer(level, x, y, width, height, upload_pitch, staging_buffer, 0);
CopyFromBuffer(cmdlist, level, x, y, width, height, upload_pitch, staging_buffer, 0);
return true;
}
void* write_ptr;
u32 write_pitch;
if (!BeginStreamUpdate(level, x, y, width, height, &write_ptr, &write_pitch))
if (!(cmdlist = BeginStreamUpdate(cmdlist, level, x, y, width, height, &write_ptr, &write_pitch)))
return false;
StringUtil::StrideMemCpy(write_ptr, write_pitch, data, pitch, std::min(pitch, upload_pitch), height);
EndStreamUpdate(level, x, y, width, height);
EndStreamUpdate(cmdlist, level, x, y, width, height);
return true;
}
+4 -4
View File
@@ -74,10 +74,10 @@ namespace D3D12
Texture& operator=(Texture&& texture);
// NOTE: Does not handle compressed formats.
bool BeginStreamUpdate(u32 level, u32 x, u32 y, u32 width, u32 height, void** out_data, u32* out_data_pitch);
void EndStreamUpdate(u32 level, u32 x, u32 y, u32 width, u32 height);
bool LoadData(u32 level, u32 x, u32 y, u32 width, u32 height, const void* data, u32 pitch);
void CopyFromBuffer(u32 level, u32 x, u32 y, u32 width, u32 height, u32 pitch, ID3D12Resource* buffer, u32 buffer_offset);
ID3D12GraphicsCommandList* BeginStreamUpdate(ID3D12GraphicsCommandList* cmdlist, u32 level, u32 x, u32 y, u32 width, u32 height, void** out_data, u32* out_data_pitch);
void EndStreamUpdate(ID3D12GraphicsCommandList* cmdlist, u32 level, u32 x, u32 y, u32 width, u32 height);
bool LoadData(ID3D12GraphicsCommandList* cmdlist, u32 level, u32 x, u32 y, u32 width, u32 height, const void* data, u32 pitch);
void CopyFromBuffer(ID3D12GraphicsCommandList* cmdlist, u32 level, u32 x, u32 y, u32 width, u32 height, u32 pitch, ID3D12Resource* buffer, u32 buffer_offset);
private:
static bool CreateSRVDescriptor(ID3D12Resource* resource, u32 levels, DXGI_FORMAT format, DescriptorHandle* dh);
+1 -1
View File
@@ -138,7 +138,7 @@ void pxOnAssertFail(const char* file, int line, const char* func, const char* ms
fputs(full_msg, stderr);
fputs("\nAborting application.\n", stderr);
fflush(stderr);
abort();
AbortWithMessage(full_msg);
#endif
ResumeThreads(handle);
+142 -19
View File
@@ -13,10 +13,10 @@
* If not, see <http://www.gnu.org/licenses/>.
*/
#include "common/PrecompiledHeader.h"
#include "common/GL/ContextWGL.h"
#include "common/Assertions.h"
#include "common/Console.h"
#include "ContextWGL.h"
#include "common/ScopedGuard.h"
static void* GetProcAddressCallback(const char* name)
{
@@ -43,8 +43,7 @@ namespace GL
if (m_rc)
wglDeleteContext(m_rc);
if (m_dc)
ReleaseDC(GetHWND(), m_dc);
ReleaseDC();
}
std::unique_ptr<Context> ContextWGL::Create(const WindowInfo& wi, const Version* versions_to_try,
@@ -66,7 +65,7 @@ namespace GL
}
else
{
Console.Error("PBuffer not implemented");
Console.Error("ContextWGL must always start with a valid surface.");
return false;
}
@@ -102,11 +101,7 @@ namespace GL
{
const bool was_current = (wglGetCurrentContext() == m_rc);
if (m_dc)
{
ReleaseDC(GetHWND(), m_dc);
m_dc = {};
}
ReleaseDC();
m_wi = new_wi;
if (!InitializeDC())
@@ -187,7 +182,7 @@ namespace GL
return context;
}
bool ContextWGL::InitializeDC()
HDC ContextWGL::GetDCAndSetPixelFormat(HWND hwnd)
{
PIXELFORMATDESCRIPTOR pfd = {};
pfd.nSize = sizeof(pfd);
@@ -200,26 +195,154 @@ namespace GL
pfd.cBlueBits = 8;
pfd.cColorBits = 24;
m_dc = GetDC(GetHWND());
if (!m_dc)
HDC hDC = ::GetDC(hwnd);
if (!hDC)
{
Console.Error("GetDC() failed: 0x%08X", GetLastError());
return false;
}
const int pf = ChoosePixelFormat(m_dc, &pfd);
if (pf == 0)
if (!m_pixel_format.has_value())
{
Console.Error("ChoosePixelFormat() failed: 0x%08X", GetLastError());
return false;
const int pf = ChoosePixelFormat(hDC, &pfd);
if (pf == 0)
{
Console.Error("ChoosePixelFormat() failed: 0x%08X", GetLastError());
::ReleaseDC(hwnd, hDC);
return false;
}
m_pixel_format = pf;
}
if (!SetPixelFormat(m_dc, pf, &pfd))
if (!SetPixelFormat(hDC, m_pixel_format.value(), &pfd))
{
Console.Error("SetPixelFormat() failed: 0x%08X", GetLastError());
::ReleaseDC(hwnd, hDC);
return {};
}
return hDC;
}
bool ContextWGL::InitializeDC()
{
if (m_wi.type == WindowInfo::Type::Win32)
{
m_dc = GetDCAndSetPixelFormat(GetHWND());
if (!m_dc)
{
Console.Error("Failed to get DC for window");
return false;
}
return true;
}
else if (m_wi.type == WindowInfo::Type::Surfaceless)
{
return CreatePBuffer();
}
else
{
Console.Error("Unknown window info type %u", static_cast<unsigned>(m_wi.type));
return false;
}
}
void ContextWGL::ReleaseDC()
{
if (m_pbuffer)
{
wglReleasePbufferDCARB(m_pbuffer, m_dc);
m_dc = {};
wglDestroyPbufferARB(m_pbuffer);
m_pbuffer = {};
::ReleaseDC(m_dummy_window, m_dummy_dc);
m_dummy_dc = {};
DestroyWindow(m_dummy_window);
m_dummy_window = {};
}
else if (m_dc)
{
::ReleaseDC(GetHWND(), m_dc);
m_dc = {};
}
}
bool ContextWGL::CreatePBuffer()
{
static bool window_class_registered = false;
static const wchar_t* window_class_name = L"ContextWGLPBuffer";
if (!window_class_registered)
{
WNDCLASSEXW wc = {};
wc.cbSize = sizeof(WNDCLASSEXW);
wc.style = 0;
wc.lpfnWndProc = DefWindowProcW;
wc.cbClsExtra = 0;
wc.cbWndExtra = 0;
wc.hInstance = GetModuleHandle(nullptr);
wc.hIcon = NULL;
wc.hCursor = LoadCursor(NULL, IDC_ARROW);
wc.hbrBackground = (HBRUSH)(COLOR_WINDOW + 1);
wc.lpszMenuName = NULL;
wc.lpszClassName = window_class_name;
wc.hIconSm = NULL;
if (!RegisterClassExW(&wc))
{
Console.Error("(ContextWGL::CreatePBuffer) RegisterClassExW() failed");
return false;
}
window_class_registered = true;
}
HWND hwnd = CreateWindowExW(0, window_class_name, window_class_name, 0, 0, 0, 0, 0, NULL, NULL, NULL, NULL);
if (!hwnd)
{
Console.Error("(ContextWGL::CreatePBuffer) CreateWindowEx() failed");
return false;
}
ScopedGuard hwnd_guard([hwnd]() { DestroyWindow(hwnd); });
HDC hdc = GetDCAndSetPixelFormat(hwnd);
if (!hdc)
return false;
ScopedGuard hdc_guard([hdc, hwnd]() { ::ReleaseDC(hwnd, hdc); });
static constexpr const int pb_attribs[] = {0, 0};
pxAssertRel(m_pixel_format.has_value(), "Has pixel format for pbuffer");
HPBUFFERARB pbuffer = wglCreatePbufferARB(hdc, m_pixel_format.value(), 1, 1, pb_attribs);
if (!pbuffer)
{
Console.Error("(ContextWGL::CreatePBuffer) wglCreatePbufferARB() failed");
return false;
}
ScopedGuard pbuffer_guard([pbuffer]() { wglDestroyPbufferARB(pbuffer); });
m_dc = wglGetPbufferDCARB(pbuffer);
if (!m_dc)
{
Console.Error("(ContextWGL::CreatePbuffer) wglGetPbufferDCARB() failed");
return false;
}
m_dummy_window = hwnd;
m_dummy_dc = hdc;
m_pbuffer = pbuffer;
pbuffer_guard.Cancel();
hdc_guard.Cancel();
hwnd_guard.Cancel();
return true;
}
+13
View File
@@ -24,6 +24,7 @@
#define WIN32_LEAN_AND_MEAN
#endif
#include <windows.h>
#include <optional>
namespace GL
{
@@ -48,12 +49,24 @@ namespace GL
private:
__fi HWND GetHWND() const { return static_cast<HWND>(m_wi.window_handle); }
HDC GetDCAndSetPixelFormat(HWND hwnd);
bool Initialize(const Version* versions_to_try, size_t num_versions_to_try);
bool InitializeDC();
void ReleaseDC();
bool CreatePBuffer();
bool CreateAnyContext(HGLRC share_context, bool make_current);
bool CreateVersionContext(const Version& version, HGLRC share_context, bool make_current);
HDC m_dc = {};
HGLRC m_rc = {};
// Can't change pixel format once it's set for a RC.
std::optional<int> m_pixel_format;
// Dummy window for creating a PBuffer off when we're surfaceless.
HWND m_dummy_window = {};
HDC m_dummy_dc = {};
HPBUFFERARB m_pbuffer = {};
};
} // namespace GL
+2
View File
@@ -160,6 +160,8 @@ extern u64 GetPhysicalMemory();
extern u32 ShortSpin();
/// Number of ns to spin for before sleeping a thread
extern const u32 SPIN_TIME_NS;
/// Like C abort() but adds the given message to the crashlog
[[noreturn]] void AbortWithMessage(const char* msg);
extern std::string GetOSVersionString();
+28
View File
@@ -71,3 +71,31 @@ static u32 GetSpinTime()
}
const u32 SPIN_TIME_NS = GetSpinTime();
#ifdef __APPLE__
// https://alastairs-place.net/blog/2013/01/10/interesting-os-x-crash-report-tidbits/
// https://opensource.apple.com/source/WebKit2/WebKit2-7608.3.10.0.3/Platform/spi/Cocoa/CrashReporterClientSPI.h.auto.html
struct crash_info_t
{
u64 version;
u64 message;
u64 signature;
u64 backtrace;
u64 message2;
u64 reserved;
u64 reserved2;
};
#define CRASH_ANNOTATION __attribute__((section("__DATA,__crash_info")))
#define CRASH_VERSION 4
extern "C" crash_info_t gCRAnnotations CRASH_ANNOTATION = { CRASH_VERSION };
#endif
void AbortWithMessage(const char* msg)
{
#ifdef __APPLE__
gCRAnnotations.message = reinterpret_cast<size_t>(msg);
// Some macOS's seem to have issues displaying non-static `message`s, so throw it in here too
gCRAnnotations.backtrace = gCRAnnotations.message;
#endif
abort();
}
+65 -1
View File
@@ -42,11 +42,12 @@ public:
virtual void SetBoolValue(const char* section, const char* key, bool value) = 0;
virtual void SetStringValue(const char* section, const char* key, const char* value) = 0;
virtual std::vector<std::string> GetStringList(const char* section, const char* key) = 0;
virtual std::vector<std::string> GetStringList(const char* section, const char* key) const = 0;
virtual void SetStringList(const char* section, const char* key, const std::vector<std::string>& items) = 0;
virtual bool RemoveFromStringList(const char* section, const char* key, const char* item) = 0;
virtual bool AddToStringList(const char* section, const char* key, const char* item) = 0;
virtual bool ContainsValue(const char* section, const char* key) const = 0;
virtual void DeleteValue(const char* section, const char* key) = 0;
virtual void ClearSection(const char* section) = 0;
@@ -153,4 +154,67 @@ public:
{
value.has_value() ? SetStringValue(section, key, value.value()) : DeleteValue(section, key);
}
__fi void CopyBoolValue(const SettingsInterface& si, const char* section, const char* key)
{
bool value;
if (si.GetBoolValue(section, key, &value))
SetBoolValue(section, key, value);
else
DeleteValue(section, key);
}
__fi void CopyIntValue(const SettingsInterface& si, const char* section, const char* key)
{
int value;
if (si.GetIntValue(section, key, &value))
SetIntValue(section, key, value);
else
DeleteValue(section, key);
}
__fi void CopyUIntValue(const SettingsInterface& si, const char* section, const char* key)
{
uint value;
if (si.GetUIntValue(section, key, &value))
SetUIntValue(section, key, value);
else
DeleteValue(section, key);
}
__fi void CopyFloatValue(const SettingsInterface& si, const char* section, const char* key)
{
float value;
if (si.GetFloatValue(section, key, &value))
SetFloatValue(section, key, value);
else
DeleteValue(section, key);
}
__fi void CopyDoubleValue(const SettingsInterface& si, const char* section, const char* key)
{
double value;
if (si.GetDoubleValue(section, key, &value))
SetDoubleValue(section, key, value);
else
DeleteValue(section, key);
}
__fi void CopyStringValue(const SettingsInterface& si, const char* section, const char* key)
{
std::string value;
if (si.GetStringValue(section, key, &value))
SetStringValue(section, key, value.c_str());
else
DeleteValue(section, key);
}
__fi void CopyStringListValue(const SettingsInterface& si, const char* section, const char* key)
{
std::vector<std::string> value(si.GetStringList(section, key));
if (!value.empty())
SetStringList(section, key, value);
else
DeleteValue(section, key);
}
};
+13 -5
View File
@@ -634,8 +634,14 @@ namespace Vulkan
vkGetDeviceQueue(m_device, m_present_queue_family_index, 0, &m_present_queue);
}
m_gpu_timing_supported = (queue_family_properties[m_graphics_queue_family_index].timestampValidBits > 0);
DevCon.WriteLn("GPU timing is %s", m_gpu_timing_supported ? "supported" : "not supported");
m_gpu_timing_supported = (m_device_properties.limits.timestampComputeAndGraphics != 0 &&
queue_family_properties[m_graphics_queue_family_index].timestampValidBits > 0 &&
m_device_properties.limits.timestampPeriod > 0);
DevCon.WriteLn("GPU timing is %s (TS=%u TS valid bits=%u, TS period=%f)",
m_gpu_timing_supported ? "supported" : "not supported",
static_cast<u32>(m_device_properties.limits.timestampComputeAndGraphics),
queue_family_properties[m_graphics_queue_family_index].timestampValidBits,
m_device_properties.limits.timestampPeriod);
ProcessDeviceExtensions();
return true;
@@ -845,6 +851,7 @@ namespace Vulkan
if (res != VK_SUCCESS)
{
LOG_VULKAN_ERROR(res, "vkCreateQueryPool failed: ");
m_gpu_timing_supported = false;
return false;
}
}
@@ -959,9 +966,10 @@ namespace Vulkan
return time;
}
void Context::SetEnableGPUTiming(bool enabled)
bool Context::SetEnableGPUTiming(bool enabled)
{
m_gpu_timing_enabled = enabled && m_gpu_timing_supported;
return (enabled == m_gpu_timing_enabled);
}
void Context::WaitForCommandBufferCompletion(u32 index)
@@ -1199,8 +1207,8 @@ namespace Vulkan
// if we didn't write the timestamp at the start of the cmdbuffer (just enabled timing), the first TS will be zero
if (timestamps[0] > 0)
{
const u64 ns_diff = (timestamps[1] - timestamps[0]) * static_cast<u64>(m_device_properties.limits.timestampPeriod);
m_accumulated_gpu_time += static_cast<double>(ns_diff) / 1000000.0;
const double ns_diff = (timestamps[1] - timestamps[0]) * static_cast<double>(m_device_properties.limits.timestampPeriod);
m_accumulated_gpu_time += ns_diff / 1000000.0;
}
}
else
+1 -1
View File
@@ -221,7 +221,7 @@ namespace Vulkan
void WaitForGPUIdle();
float GetAndResetAccumulatedGPUTime();
void SetEnableGPUTiming(bool enabled);
bool SetEnableGPUTiming(bool enabled);
private:
Context(VkInstance instance, VkPhysicalDevice physical_device);
+1 -1
View File
@@ -79,7 +79,7 @@
<PropertyGroup>
<MocDefines></MocDefines>
<MocDefines Condition="!$(Configuration.Contains(Debug))">-DQT_NO_DEBUG -DNDEBUG $(MocDefines)</MocDefines>
<MocIncludes>"-I$(QtIncludeDir)" "-I$(SolutionDir)pcsx2" "-I$(SolutionDir)" -I.</MocIncludes>
<MocIncludes>-I"$(QtIncludeDir)." -I"$(SolutionDir)pcsx2" "-I$(SolutionDir)." -I.</MocIncludes>
</PropertyGroup>
<Target Name="QtMoc"
BeforeTargets="ClCompile"
+4
View File
@@ -60,12 +60,16 @@ target_sources(pcsx2-qt PRIVATE
Settings/ControllerSettingsDialog.cpp
Settings/ControllerSettingsDialog.h
Settings/ControllerSettingsDialog.ui
Settings/ControllerSettingWidgetBinder.h
Settings/CreateMemoryCardDialog.cpp
Settings/CreateMemoryCardDialog.h
Settings/CreateMemoryCardDialog.ui
Settings/EmulationSettingsWidget.cpp
Settings/EmulationSettingsWidget.h
Settings/EmulationSettingsWidget.ui
Settings/FolderSettingsWidget.cpp
Settings/FolderSettingsWidget.h
Settings/FolderSettingsWidget.ui
Settings/GameFixSettingsWidget.cpp
Settings/GameFixSettingsWidget.h
Settings/GameFixSettingsWidget.ui
+152 -30
View File
@@ -21,6 +21,7 @@
#include "EmuThread.h"
#include "MainWindow.h"
#include "QtHost.h"
#include "QtUtils.h"
#include "pcsx2/GS/GSIntrin.h" // _BitScanForward
@@ -33,7 +34,9 @@
#include <QtGui/QWindowStateChangeEvent>
#include <cmath>
#if !defined(_WIN32) && !defined(APPLE)
#if defined(_WIN32)
#include "common/RedtapeWindows.h"
#elif !defined(APPLE)
#include <qpa/qplatformnativeinterface.h>
#endif
@@ -50,7 +53,13 @@ DisplayWidget::DisplayWidget(QWidget* parent)
setMouseTracking(true);
}
DisplayWidget::~DisplayWidget() = default;
DisplayWidget::~DisplayWidget()
{
#ifdef _WIN32
if (m_clip_mouse_enabled)
ClipCursor(nullptr);
#endif
}
qreal DisplayWidget::devicePixelRatioFromScreen() const
{
@@ -110,27 +119,96 @@ std::optional<WindowInfo> DisplayWidget::getWindowInfo()
return wi;
}
void DisplayWidget::setRelativeMode(bool enabled)
void DisplayWidget::updateRelativeMode(bool master_enable)
{
if (m_relative_mouse_enabled == enabled)
bool relative_mode = master_enable && InputManager::HasPointerAxisBinds();
#ifdef _WIN32
// prefer ClipCursor() over warping movement when we're using raw input
bool clip_cursor = relative_mode && false /*InputManager::IsUsingRawInput()*/;
if (m_relative_mouse_enabled == relative_mode && m_clip_mouse_enabled == clip_cursor)
return;
if (enabled)
{
m_relative_mouse_start_position = QCursor::pos();
DevCon.WriteLn("updateRelativeMode(): relative=%s, clip=%s", relative_mode ? "yes" : "no", clip_cursor ? "yes" : "no");
const QPoint center_pos = mapToGlobal(QPoint(width() / 2, height() / 2));
QCursor::setPos(center_pos);
m_relative_mouse_last_position = center_pos;
if (!clip_cursor && m_clip_mouse_enabled)
{
m_clip_mouse_enabled = false;
ClipCursor(nullptr);
}
#else
if (m_relative_mouse_enabled == relative_mode)
return;
DevCon.WriteLn("updateRelativeMode(): relative=%s", relative_mode ? "yes" : "no");
#endif
if (relative_mode)
{
#ifdef _WIN32
m_relative_mouse_enabled = !clip_cursor;
m_clip_mouse_enabled = clip_cursor;
#else
m_relative_mouse_enabled = true;
#endif
m_relative_mouse_start_pos = QCursor::pos();
updateCenterPos();
grabMouse();
}
else
else if (m_relative_mouse_enabled)
{
QCursor::setPos(m_relative_mouse_start_position);
m_relative_mouse_enabled = false;
QCursor::setPos(m_relative_mouse_start_pos);
releaseMouse();
}
m_relative_mouse_enabled = enabled;
}
void DisplayWidget::updateCursor(bool master_enable)
{
#ifdef _WIN32
const bool hide = master_enable && (m_should_hide_cursor || m_relative_mouse_enabled || m_clip_mouse_enabled);
#else
const bool hide = master_enable && (m_should_hide_cursor || m_relative_mouse_enabled);
#endif
if (m_cursor_hidden == hide)
return;
m_cursor_hidden = hide;
if (hide)
setCursor(Qt::BlankCursor);
else
unsetCursor();
}
void DisplayWidget::updateCenterPos()
{
#ifdef _WIN32
if (m_clip_mouse_enabled)
{
RECT rc;
if (GetWindowRect(reinterpret_cast<HWND>(winId()), &rc))
ClipCursor(&rc);
}
else if (m_relative_mouse_enabled)
{
RECT rc;
if (GetWindowRect(reinterpret_cast<HWND>(winId()), &rc))
{
m_relative_mouse_center_pos.setX(((rc.right - rc.left) / 2) + rc.left);
m_relative_mouse_center_pos.setY(((rc.bottom - rc.top) / 2) + rc.top);
SetCursorPos(m_relative_mouse_center_pos.x(), m_relative_mouse_center_pos.y());
}
}
#else
if (m_relative_mouse_enabled)
{
// we do a round trip here because these coordinates are dpi-unscaled
m_relative_mouse_center_pos = mapToGlobal(QPoint((width() + 1) / 2, (height() + 1) / 2));
QCursor::setPos(m_relative_mouse_center_pos);
m_relative_mouse_center_pos = QCursor::pos();
}
#endif
}
QPaintEngine* DisplayWidget::paintEngine() const
@@ -157,7 +235,8 @@ bool DisplayWidget::event(QEvent* event)
// but I can't think of a better way of handling it, and there doesn't appear to be
// any window flag which changes this behavior that I can see.
const int key = key_event->key();
const u32 key = QtUtils::KeyEventToCode(key_event);
const Qt::KeyboardModifiers modifiers = key_event->modifiers();
const bool pressed = (key_event->type() == QEvent::KeyPress);
const auto it = std::find(m_keys_pressed_with_modifiers.begin(), m_keys_pressed_with_modifiers.end(), key);
if (it != m_keys_pressed_with_modifiers.end())
@@ -167,12 +246,15 @@ bool DisplayWidget::event(QEvent* event)
else
m_keys_pressed_with_modifiers.erase(it);
}
else if (key_event->modifiers() != Qt::NoModifier && pressed)
else if (modifiers != Qt::NoModifier && modifiers != Qt::KeypadModifier && pressed)
{
m_keys_pressed_with_modifiers.push_back(key);
}
emit windowKeyEvent(key, pressed);
Host::RunOnCPUThread([key, pressed]() {
InputManager::InvokeEvents(InputManager::MakeHostKeyboardKey(key), static_cast<float>(pressed));
});
return true;
}
@@ -184,22 +266,39 @@ bool DisplayWidget::event(QEvent* event)
{
const qreal dpr = devicePixelRatioFromScreen();
const QPoint mouse_pos = mouse_event->pos();
const int scaled_x = static_cast<int>(static_cast<qreal>(mouse_pos.x()) * dpr);
const int scaled_y = static_cast<int>(static_cast<qreal>(mouse_pos.y()) * dpr);
windowMouseMoveEvent(scaled_x, scaled_y);
const float scaled_x = static_cast<float>(static_cast<qreal>(mouse_pos.x()) * dpr);
const float scaled_y = static_cast<float>(static_cast<qreal>(mouse_pos.y()) * dpr);
InputManager::UpdatePointerAbsolutePosition(0, scaled_x, scaled_y);
}
else
{
const QPoint center_pos = mapToGlobal(QPoint((width() + 1) / 2, (height() + 1) / 2));
const QPoint mouse_pos = mapToGlobal(mouse_event->pos());
// On windows, we use winapi here. The reason being that the coordinates in QCursor
// are un-dpi-scaled, so we lose precision at higher desktop scalings.
float dx = 0.0f, dy = 0.0f;
const int dx = mouse_pos.x() - center_pos.x();
const int dy = mouse_pos.y() - center_pos.y();
m_relative_mouse_last_position.setX(m_relative_mouse_last_position.x() + dx);
m_relative_mouse_last_position.setY(m_relative_mouse_last_position.y() + dy);
windowMouseMoveEvent(m_relative_mouse_last_position.x(), m_relative_mouse_last_position.y());
QCursor::setPos(center_pos);
#ifndef _WIN32
const QPoint mouse_pos = QCursor::pos();
if (mouse_pos != m_relative_mouse_center_pos)
{
dx = static_cast<float>(mouse_pos.x() - m_relative_mouse_center_pos.x());
dy = static_cast<float>(mouse_pos.y() - m_relative_mouse_center_pos.y());
QCursor::setPos(m_relative_mouse_center_pos);
}
#else
POINT mouse_pos;
if (GetCursorPos(&mouse_pos))
{
dx = static_cast<float>(mouse_pos.x - m_relative_mouse_center_pos.x());
dy = static_cast<float>(mouse_pos.y - m_relative_mouse_center_pos.y());
SetCursorPos(m_relative_mouse_center_pos.x(), m_relative_mouse_center_pos.y());
}
#endif
if (dx != 0.0f)
InputManager::UpdatePointerRelativeDelta(0, InputPointerAxis::X, dx);
if (dy != 0.0f)
InputManager::UpdatePointerRelativeDelta(0, InputPointerAxis::Y, dy);
}
return true;
@@ -211,10 +310,16 @@ bool DisplayWidget::event(QEvent* event)
{
unsigned long button_index;
if (_BitScanForward(&button_index, static_cast<u32>(static_cast<const QMouseEvent*>(event)->button())))
emit windowMouseButtonEvent(static_cast<int>(button_index + 1u), event->type() != QEvent::MouseButtonRelease);
{
Host::RunOnCPUThread([button_index, pressed = (event->type() != QEvent::MouseButtonRelease)]() {
InputManager::InvokeEvents(InputManager::MakePointerButtonKey(0, button_index), static_cast<float>(pressed));
});
}
// don't toggle fullscreen when we're bound.. that wouldn't end well.
if (event->type() == QEvent::MouseButtonDblClick &&
static_cast<const QMouseEvent*>(event)->button() == Qt::LeftButton &&
!InputManager::HasAnyBindingsForKey(InputManager::MakePointerButtonKey(0, 0)) &&
Host::GetBoolSettingValue("UI", "DoubleClickTogglesFullscreen", true))
{
g_emu_thread->toggleFullscreen();
@@ -225,8 +330,18 @@ bool DisplayWidget::event(QEvent* event)
case QEvent::Wheel:
{
const QWheelEvent* wheel_event = static_cast<QWheelEvent*>(event);
emit windowMouseWheelEvent(wheel_event->angleDelta());
// wheel delta is 120 as in winapi
const QPoint delta_angle(static_cast<QWheelEvent*>(event)->angleDelta());
constexpr float DELTA = 120.0f;
const float dx = std::clamp(static_cast<float>(delta_angle.x()) / DELTA, -1.0f, 1.0f);
if (dx != 0.0f)
InputManager::UpdatePointerRelativeDelta(0, InputPointerAxis::WheelX, dx);
const float dy = std::clamp(static_cast<float>(delta_angle.y()) / DELTA, -1.0f, 1.0f);
if (dy != 0.0f)
InputManager::UpdatePointerRelativeDelta(0, InputPointerAxis::WheelY, dy);
return true;
}
@@ -249,6 +364,13 @@ bool DisplayWidget::event(QEvent* event)
emit windowResizedEvent(scaled_width, scaled_height, dpr);
}
updateCenterPos();
return true;
}
case QEvent::Move:
{
updateCenterPos();
return true;
}
+14 -7
View File
@@ -30,30 +30,37 @@ public:
QPaintEngine* paintEngine() const override;
__fi void setShouldHideCursor(bool hide) { m_should_hide_cursor = hide; }
int scaledWindowWidth() const;
int scaledWindowHeight() const;
qreal devicePixelRatioFromScreen() const;
std::optional<WindowInfo> getWindowInfo();
void setRelativeMode(bool enabled);
void updateRelativeMode(bool master_enable);
void updateCursor(bool master_enable);
Q_SIGNALS:
void windowFocusEvent();
void windowResizedEvent(int width, int height, float scale);
void windowRestoredEvent();
void windowKeyEvent(int key_code, bool pressed);
void windowMouseMoveEvent(int x, int y);
void windowMouseButtonEvent(int button, bool pressed);
void windowMouseWheelEvent(const QPoint& angle_delta);
protected:
bool event(QEvent* event) override;
private:
QPoint m_relative_mouse_start_position{};
QPoint m_relative_mouse_last_position{};
void updateCenterPos();
QPoint m_relative_mouse_start_pos{};
QPoint m_relative_mouse_center_pos{};
bool m_relative_mouse_enabled = false;
#ifdef _WIN32
bool m_clip_mouse_enabled = false;
#endif
bool m_should_hide_cursor = false;
bool m_cursor_hidden = false;
std::vector<int> m_keys_pressed_with_modifiers;
u32 m_last_window_width = 0;
+23 -38
View File
@@ -47,7 +47,6 @@
#include "QtUtils.h"
EmuThread* g_emu_thread = nullptr;
WindowInfo g_gs_window_info;
static std::unique_ptr<HostDisplay> s_host_display;
@@ -412,6 +411,17 @@ void EmuThread::reloadGameSettings()
}
}
void EmuThread::updateEmuFolders()
{
if (!isOnEmuThread())
{
QMetaObject::invokeMethod(this, &EmuThread::updateEmuFolders, Qt::QueuedConnection);
return;
}
Host::Internal::UpdateEmuFolders();
}
void EmuThread::loadOurSettings()
{
m_verbose_status = Host::GetBaseBoolSettingValue("UI", "VerboseStatusBar", false);
@@ -419,6 +429,8 @@ void EmuThread::loadOurSettings()
void EmuThread::checkForSettingChanges()
{
QMetaObject::invokeMethod(g_main_window, &MainWindow::checkForSettingChanges, Qt::QueuedConnection);
if (VMManager::HasValidVM())
{
const bool render_to_main = Host::GetBaseBoolSettingValue("UI", "RenderToMainWindow", true);
@@ -504,11 +516,12 @@ void EmuThread::reloadInputSources()
std::unique_lock<std::mutex> lock = Host::GetSettingsLock();
SettingsInterface* si = Host::GetSettingsInterface();
SettingsInterface* bindings_si = Host::GetSettingsInterfaceForBindings();
InputManager::ReloadSources(*si, lock);
// skip loading bindings if we're not running, since it'll get done on startup anyway
if (VMManager::HasValidVM())
InputManager::ReloadBindings(*si);
InputManager::ReloadBindings(*si, *bindings_si);
}
void EmuThread::reloadInputBindings()
@@ -525,7 +538,8 @@ void EmuThread::reloadInputBindings()
auto lock = Host::GetSettingsLock();
SettingsInterface* si = Host::GetSettingsInterface();
InputManager::ReloadBindings(*si);
SettingsInterface* bindings_si = Host::GetSettingsInterfaceForBindings();
InputManager::ReloadBindings(*si, *bindings_si);
}
void EmuThread::requestDisplaySize(float scale)
@@ -583,29 +597,11 @@ void EmuThread::connectDisplaySignals(DisplayWidget* widget)
connect(widget, &DisplayWidget::windowFocusEvent, this, &EmuThread::onDisplayWindowFocused);
connect(widget, &DisplayWidget::windowResizedEvent, this, &EmuThread::onDisplayWindowResized);
// connect(widget, &DisplayWidget::windowRestoredEvent, this, &EmuThread::redrawDisplayWindow);
connect(widget, &DisplayWidget::windowKeyEvent, this, &EmuThread::onDisplayWindowKeyEvent);
connect(widget, &DisplayWidget::windowMouseMoveEvent, this, &EmuThread::onDisplayWindowMouseMoveEvent);
connect(widget, &DisplayWidget::windowMouseButtonEvent, this, &EmuThread::onDisplayWindowMouseButtonEvent);
connect(widget, &DisplayWidget::windowMouseWheelEvent, this, &EmuThread::onDisplayWindowMouseWheelEvent);
}
void EmuThread::onDisplayWindowMouseMoveEvent(int x, int y) {}
void EmuThread::onDisplayWindowMouseButtonEvent(int button, bool pressed)
{
InputManager::InvokeEvents(InputManager::MakeHostMouseButtonKey(button), pressed ? 1.0f : 0.0f);
}
void EmuThread::onDisplayWindowMouseWheelEvent(const QPoint& delta_angle) {}
void EmuThread::onDisplayWindowKeyEvent(int key, bool pressed)
{
InputManager::InvokeEvents(InputManager::MakeHostKeyboardKey(key), pressed ? 1.0f : 0.0f);
}
void EmuThread::onDisplayWindowResized(int width, int height, float scale)
{
if (!VMManager::HasValidVM())
if (!s_host_display)
return;
GetMTGS().ResizeDisplayWindow(width, height, scale);
@@ -681,8 +677,6 @@ HostDisplay* EmuThread::acquireHostDisplay(HostDisplay::RenderAPI api)
return nullptr;
}
g_gs_window_info = s_host_display->GetWindowInfo();
Console.WriteLn(Color_StrongGreen, "%s Graphics Driver Info:", HostDisplay::RenderAPIToString(s_host_display->GetRenderAPI()));
Console.Indent().WriteLn(s_host_display->GetDriverInfo());
@@ -693,17 +687,8 @@ void EmuThread::releaseHostDisplay()
{
ImGuiManager::Shutdown();
if (s_host_display)
{
s_host_display->DestroyRenderSurface();
s_host_display->DestroyRenderDevice();
}
g_gs_window_info = WindowInfo();
emit onDestroyDisplayRequested();
s_host_display.reset();
emit onDestroyDisplayRequested();
}
HostDisplay* Host::GetHostDisplay()
@@ -967,7 +952,7 @@ SysMtgsThread& GetMTGS()
// ------------------------------------------------------------------------
BEGIN_HOTKEY_LIST(g_host_hotkeys)
DEFINE_HOTKEY("ShutdownVM", "System", "Shut Down Virtual Machine", [](bool pressed) {
DEFINE_HOTKEY("ShutdownVM", "System", "Shut Down Virtual Machine", [](s32 pressed) {
if (!pressed)
{
// run it on the host thread, that way we get the confirm prompt (if enabled)
@@ -975,16 +960,16 @@ DEFINE_HOTKEY("ShutdownVM", "System", "Shut Down Virtual Machine", [](bool press
Q_ARG(bool, true), Q_ARG(bool, true), Q_ARG(bool, true));
}
})
DEFINE_HOTKEY("TogglePause", "System", "Toggle Pause", [](bool pressed) {
DEFINE_HOTKEY("TogglePause", "System", "Toggle Pause", [](s32 pressed) {
if (!pressed)
g_emu_thread->setVMPaused(VMManager::GetState() != VMState::Paused);
})
DEFINE_HOTKEY("ToggleFullscreen", "General", "Toggle Fullscreen", [](bool pressed) {
DEFINE_HOTKEY("ToggleFullscreen", "General", "Toggle Fullscreen", [](s32 pressed) {
if (!pressed)
g_emu_thread->toggleFullscreen();
})
// Input Recording Hot Keys
DEFINE_HOTKEY("InputRecToggleMode", "Input Recording", "Toggle Recording Mode", [](bool pressed) {
DEFINE_HOTKEY("InputRecToggleMode", "Input Recording", "Toggle Recording Mode", [](s32 pressed) {
if (!pressed) // ?? - not pressed so it is on key up?
{
g_InputRecordingControls.RecordModeToggle();
+1 -4
View File
@@ -70,6 +70,7 @@ public Q_SLOTS:
void setSurfaceless(bool surfaceless);
void applySettings();
void reloadGameSettings();
void updateEmuFolders();
void toggleSoftwareRendering();
void switchRenderer(GSRendererType renderer);
void changeDisc(const QString& path);
@@ -142,12 +143,8 @@ private:
private Q_SLOTS:
void stopInThread();
void doBackgroundControllerPoll();
void onDisplayWindowMouseMoveEvent(int x, int y);
void onDisplayWindowMouseButtonEvent(int button, bool pressed);
void onDisplayWindowMouseWheelEvent(const QPoint& delta_angle);
void onDisplayWindowResized(int width, int height, float scale);
void onDisplayWindowFocused();
void onDisplayWindowKeyEvent(int key, bool pressed);
private:
QThread* m_ui_thread;
+3
View File
@@ -92,6 +92,7 @@ void GameListWidget::initialize()
m_table_view->horizontalHeader()->setContextMenuPolicy(Qt::CustomContextMenu);
m_table_view->verticalHeader()->hide();
m_table_view->setVerticalScrollBarPolicy(Qt::ScrollBarAlwaysOn);
m_table_view->setVerticalScrollMode(QAbstractItemView::ScrollMode::ScrollPerPixel);
loadTableViewColumnVisibilitySettings();
loadTableViewColumnSortSettings();
@@ -119,6 +120,8 @@ void GameListWidget::initialize()
m_list_view->setContextMenuPolicy(Qt::CustomContextMenu);
m_list_view->setFrameStyle(QFrame::NoFrame);
m_list_view->setSpacing(m_model->getCoverArtSpacing());
m_list_view->setVerticalScrollMode(QAbstractItemView::ScrollMode::ScrollPerPixel);
updateListFont();
connect(m_list_view->selectionModel(), &QItemSelectionModel::currentChanged, this,
+6 -5
View File
@@ -26,12 +26,13 @@
#include "CDVD/CDVD.h"
#include "Frontend/GameList.h"
#include "Frontend/LogSink.h"
#include "common/CrashHandler.h"
static void PrintCommandLineVersion()
{
QtHost::InitializeEarlyConsole();
Host::InitializeEarlyConsole();
std::fprintf(stderr, "%s\n", (QtHost::GetAppNameAndVersion() + QtHost::GetAppConfigSuffix()).toUtf8().constData());
std::fprintf(stderr, "https://pcsx2.net/\n");
std::fprintf(stderr, "\n");
@@ -143,7 +144,7 @@ static bool ParseCommandLineOptions(int argc, char* argv[], std::shared_ptr<VMBo
}
else if (CHECK_ARG("-earlyconsolelog"))
{
QtHost::InitializeEarlyConsole();
Host::InitializeEarlyConsole();
continue;
}
else if (CHECK_ARG("--"))
@@ -153,7 +154,7 @@ static bool ParseCommandLineOptions(int argc, char* argv[], std::shared_ptr<VMBo
}
else if (argv[i][0] == '-')
{
QtHost::InitializeEarlyConsole();
Host::InitializeEarlyConsole();
std::fprintf(stderr, "Unknown parameter: '%s'", argv[i]);
return false;
}
@@ -172,7 +173,7 @@ static bool ParseCommandLineOptions(int argc, char* argv[], std::shared_ptr<VMBo
// or disc, we don't want to actually start.
if (autoboot && !autoboot->source_type.has_value() && autoboot->filename.empty() && autoboot->elf_override.empty())
{
QtHost::InitializeEarlyConsole();
Host::InitializeEarlyConsole();
Console.Warning("Skipping autoboot due to no boot parameters.");
autoboot.reset();
}
@@ -181,7 +182,7 @@ static bool ParseCommandLineOptions(int argc, char* argv[], std::shared_ptr<VMBo
// scanning the game list).
if (QtHost::InBatchMode() && !autoboot)
{
QtHost::InitializeEarlyConsole();
Host::InitializeEarlyConsole();
Console.Warning("Disabling batch mode, because we have no autoboot.");
QtHost::SetBatchMode(false);
}
+60 -8
View File
@@ -29,6 +29,7 @@
#include "pcsx2/CDVD/CDVDaccess.h"
#include "pcsx2/Frontend/GameList.h"
#include "pcsx2/Frontend/LogSink.h"
#include "pcsx2/GSDumpReplayer.h"
#include "pcsx2/HostDisplay.h"
#include "pcsx2/HostSettings.h"
@@ -210,6 +211,7 @@ void MainWindow::connectSignals()
connect(m_ui.actionAudioSettings, &QAction::triggered, [this]() { doSettings("Audio"); });
connect(m_ui.actionMemoryCardSettings, &QAction::triggered, [this]() { doSettings("Memory Cards"); });
connect(m_ui.actionDEV9Settings, &QAction::triggered, [this]() { doSettings("Network & HDD"); });
connect(m_ui.actionFolderSettings, &QAction::triggered, [this]() { doSettings("Folders"); });
connect(
m_ui.actionControllerSettings, &QAction::triggered, [this]() { doControllerSettings(ControllerSettingsDialog::Category::GlobalSettings); });
connect(m_ui.actionHotkeySettings, &QAction::triggered, [this]() { doControllerSettings(ControllerSettingsDialog::Category::HotkeySettings); });
@@ -766,6 +768,11 @@ bool MainWindow::isRenderingToMain() const
return (m_display_widget && m_display_widget->parent() == this);
}
bool MainWindow::shouldHideMouseCursor() const
{
return isRenderingFullscreen() && Host::GetBoolSettingValue("UI", "HideMouseCursor", false);
}
void MainWindow::switchToGameListView()
{
if (centralWidget() == m_game_list_widget)
@@ -843,11 +850,11 @@ bool MainWindow::requestShutdown(bool allow_confirm /* = true */, bool allow_sav
if (!VMManager::HasValidVM())
return true;
// if we don't have a crc, we can't save state
// If we don't have a crc, we can't save state.
allow_save_to_state &= (m_current_game_crc != 0);
bool save_state = allow_save_to_state && EmuConfig.SaveStateOnShutdown;
// only confirm on UI thread because we need to display a msgbox
// Only confirm on UI thread because we need to display a msgbox.
if (allow_confirm && !GSDumpReplayer::IsReplayingDump() && Host::GetBaseBoolSettingValue("UI", "ConfirmShutdown", true))
{
VMLock lock(pauseAndLockVM());
@@ -868,20 +875,23 @@ bool MainWindow::requestShutdown(bool allow_confirm /* = true */, bool allow_sav
return false;
save_state = save_cb->isChecked();
// Don't switch back to fullscreen when we're shutting down anyway.
lock.cancelResume();
}
g_emu_thread->shutdownVM(save_state);
if (block_until_done || QtHost::InBatchMode())
{
// we need to yield here, since the display gets destroyed
// We need to yield here, since the display gets destroyed.
while (VMManager::GetState() != VMState::Shutdown)
QApplication::processEvents(QEventLoop::ExcludeUserInputEvents, 1);
}
if (QtHost::InBatchMode())
{
// closing the window should shut down everything.
// Closing the window should shut down everything.
close();
}
@@ -897,6 +907,12 @@ void MainWindow::requestExit()
close();
}
void MainWindow::checkForSettingChanges()
{
if (m_display_widget)
m_display_widget->updateRelativeMode(m_vm_valid && !m_vm_paused);
}
void Host::InvalidateSaveStateCache()
{
QMetaObject::invokeMethod(g_main_window, &MainWindow::invalidateSaveStateCache, Qt::QueuedConnection);
@@ -1241,7 +1257,7 @@ void MainWindow::onThemeChangedFromSettings()
void MainWindow::onLoggingOptionChanged()
{
QtHost::UpdateLogging();
Host::UpdateLogging();
}
void MainWindow::onInputRecNewActionTriggered()
@@ -1354,6 +1370,11 @@ void MainWindow::onVMPaused()
updateWindowTitle();
updateStatusBarWidgetVisibility();
m_status_fps_widget->setText(tr("Paused"));
if (m_display_widget)
{
m_display_widget->updateRelativeMode(false);
m_display_widget->updateCursor(false);
}
}
void MainWindow::onVMResumed()
@@ -1370,7 +1391,11 @@ void MainWindow::onVMResumed()
updateStatusBarWidgetVisibility();
m_status_fps_widget->setText(m_last_fps_status);
if (m_display_widget)
{
m_display_widget->updateRelativeMode(true);
m_display_widget->updateCursor(true);
m_display_widget->setFocus();
}
}
void MainWindow::onVMStopped()
@@ -1381,7 +1406,16 @@ void MainWindow::onVMStopped()
updateEmulationActions(false, false);
updateWindowTitle();
updateStatusBarWidgetVisibility();
switchToGameListView();
if (m_display_widget)
{
m_display_widget->updateRelativeMode(false);
m_display_widget->updateCursor(false);
}
else
{
switchToGameListView();
}
}
void MainWindow::onGameChanged(const QString& path, const QString& serial, const QString& name, quint32 crc)
@@ -1554,6 +1588,10 @@ DisplayWidget* MainWindow::createDisplay(bool fullscreen, bool render_to_main)
updateWindowTitle();
m_display_widget->setFocus();
m_display_widget->setShouldHideCursor(shouldHideMouseCursor());
m_display_widget->updateRelativeMode(m_vm_valid && !m_vm_paused);
m_display_widget->updateCursor(m_vm_valid && !m_vm_paused);
host_display->DoneRenderContextCurrent();
return m_display_widget;
}
@@ -1597,6 +1635,11 @@ DisplayWidget* MainWindow::updateDisplay(bool fullscreen, bool render_to_main, b
container->showNormal();
}
m_display_widget->setFocus();
m_display_widget->setShouldHideCursor(shouldHideMouseCursor());
m_display_widget->updateRelativeMode(m_vm_valid && !m_vm_paused);
m_display_widget->updateCursor(m_vm_valid && !m_vm_paused);
QCoreApplication::processEvents(QEventLoop::ExcludeUserInputEvents);
return m_display_widget;
}
@@ -1676,6 +1719,9 @@ DisplayWidget* MainWindow::updateDisplay(bool fullscreen, bool render_to_main, b
updateWindowTitle();
m_display_widget->setFocus();
m_display_widget->setShouldHideCursor(shouldHideMouseCursor());
m_display_widget->updateRelativeMode(m_vm_valid && !m_vm_paused);
m_display_widget->updateCursor(m_vm_valid && !m_vm_paused);
QSignalBlocker blocker(m_ui.actionFullscreen);
m_ui.actionFullscreen->setChecked(fullscreen);
@@ -2045,7 +2091,7 @@ void MainWindow::populateLoadStateMenu(QMenu* menu, const QString& filename, con
if (!FileSystem::StatFile(state_filename.c_str(), &sd))
continue;
action = menu->addAction(tr("Save Slot %1 (%2)").arg(i).arg(formatTimestampForSaveStateMenu(sd.ModificationTime)));
action = menu->addAction(tr("Load Slot %1 (%2)").arg(i).arg(formatTimestampForSaveStateMenu(sd.ModificationTime)));
connect(action, &QAction::triggered, [this, i]() { loadSaveStateSlot(i); });
}
}
@@ -2171,7 +2217,7 @@ MainWindow::VMLock::VMLock(VMLock&& lock)
, m_was_fullscreen(lock.m_was_fullscreen)
{
lock.m_dialog_parent = nullptr;
lock.m_was_paused = false;
lock.m_was_paused = true;
lock.m_was_fullscreen = false;
}
@@ -2183,3 +2229,9 @@ MainWindow::VMLock::~VMLock()
g_emu_thread->setVMPaused(false);
}
void MainWindow::VMLock::cancelResume()
{
m_was_paused = true;
m_was_fullscreen = false;
}
+2
View File
@@ -93,6 +93,7 @@ public Q_SLOTS:
void runOnUIThread(const std::function<void()>& func);
bool requestShutdown(bool allow_confirm = true, bool allow_save_to_state = true, bool block_until_done = false);
void requestExit();
void checkForSettingChanges();
private Q_SLOTS:
void onUpdateCheckComplete();
@@ -184,6 +185,7 @@ private:
bool isShowingGameList() const;
bool isRenderingFullscreen() const;
bool isRenderingToMain() const;
bool shouldHideMouseCursor() const;
void switchToGameListView();
void switchToEmulationView();
+10
View File
@@ -93,6 +93,7 @@
<addaction name="actionAudioSettings"/>
<addaction name="actionMemoryCardSettings"/>
<addaction name="actionDEV9Settings"/>
<addaction name="actionFolderSettings"/>
<addaction name="actionControllerSettings"/>
<addaction name="actionHotkeySettings"/>
<addaction name="separator"/>
@@ -570,6 +571,15 @@
<string>&amp;Network &amp;&amp; HDD</string>
</property>
</action>
<action name="actionFolderSettings">
<property name="icon">
<iconset theme="folder-open-line">
<normaloff>.</normaloff>.</iconset>
</property>
<property name="text">
<string>&amp;Folders</string>
</property>
</action>
<action name="actionViewToolbar">
<property name="checkable">
<bool>true</bool>
+2 -365
View File
@@ -38,6 +38,7 @@
#include "pcsx2/DebugTools/Debug.h"
#include "pcsx2/Frontend/GameList.h"
#include "pcsx2/Frontend/INISettingsInterface.h"
#include "pcsx2/Frontend/LogSink.h"
#include "pcsx2/HostSettings.h"
#include "pcsx2/PAD/Host/PAD.h"
@@ -227,14 +228,6 @@ void QtHost::SetDataDirectory()
EmuFolders::DataRoot = EmuFolders::AppRoot;
}
void QtHost::UpdateFolders()
{
// TODO: This should happen with the VM thread paused.
auto lock = Host::GetSettingsLock();
EmuFolders::LoadConfig(*s_base_settings_interface.get());
EmuFolders::EnsureFoldersExist();
}
bool QtHost::InitializeConfig()
{
if (!SetCriticalFolders())
@@ -260,7 +253,7 @@ bool QtHost::InitializeConfig()
// TODO: Handle reset to defaults if load fails.
EmuFolders::LoadConfig(*s_base_settings_interface.get());
EmuFolders::EnsureFoldersExist();
QtHost::UpdateLogging();
Host::UpdateLogging();
return true;
}
@@ -511,359 +504,3 @@ void QtHost::HookSignals()
std::signal(SIGINT, SignalHandler);
std::signal(SIGTERM, SignalHandler);
}
// Used on both Windows and Linux.
#ifdef _WIN32
static const wchar_t s_console_colors[][ConsoleColors_Count] = {
#define CC(x) L ## x
#else
static const char s_console_colors[][ConsoleColors_Count] = {
#define CC(x) x
#endif
CC("\033[0m"), // default
CC("\033[30m\033[1m"), // black
CC("\033[32m"), // green
CC("\033[31m"), // red
CC("\033[34m"), // blue
CC("\033[35m"), // magenta
CC("\033[35m"), // orange (FIXME)
CC("\033[37m"), // gray
CC("\033[36m"), // cyan
CC("\033[33m"), // yellow
CC("\033[37m"), // white
CC("\033[30m\033[1m"), // strong black
CC("\033[31m\033[1m"), // strong red
CC("\033[32m\033[1m"), // strong green
CC("\033[34m\033[1m"), // strong blue
CC("\033[35m\033[1m"), // strong magenta
CC("\033[35m\033[1m"), // strong orange (FIXME)
CC("\033[37m\033[1m"), // strong gray
CC("\033[36m\033[1m"), // strong cyan
CC("\033[33m\033[1m"), // strong yellow
CC("\033[37m\033[1m") // strong white
};
#undef CC
static Common::Timer::Value s_log_start_timestamp = Common::Timer::GetCurrentValue();
static bool s_log_timestamps = false;
static std::mutex s_log_mutex;
// Replacement for Console so we actually get output to our console window on Windows.
#ifdef _WIN32
static bool s_debugger_attached = false;
static bool s_console_handle_set = false;
static bool s_console_allocated = false;
static HANDLE s_console_handle = INVALID_HANDLE_VALUE;
static HANDLE s_old_console_stdin = NULL;
static HANDLE s_old_console_stdout = NULL;
static HANDLE s_old_console_stderr = NULL;
static BOOL WINAPI ConsoleCtrlHandler(DWORD dwCtrlType)
{
Console.WriteLn("Handler %u", dwCtrlType);
if (dwCtrlType != CTRL_C_EVENT)
return FALSE;
SignalHandler(SIGTERM);
return TRUE;
}
static bool EnableVirtualTerminalProcessing(HANDLE hConsole)
{
if (hConsole == INVALID_HANDLE_VALUE)
return false;
DWORD old_mode;
if (!GetConsoleMode(hConsole, &old_mode))
return false;
// already enabled?
if (old_mode & ENABLE_VIRTUAL_TERMINAL_PROCESSING)
return true;
return SetConsoleMode(hConsole, old_mode | ENABLE_VIRTUAL_TERMINAL_PROCESSING);
}
#endif
static void ConsoleQt_SetTitle(const char* title)
{
#ifdef _WIN32
SetConsoleTitleW(StringUtil::UTF8StringToWideString(title).c_str());
#else
std::fprintf(stdout, "\033]0;%s\007", title);
#endif
}
static void ConsoleQt_DoSetColor(ConsoleColors color)
{
#ifdef _WIN32
if (s_console_handle == INVALID_HANDLE_VALUE)
return;
const wchar_t* colortext = s_console_colors[static_cast<u32>(color)];
DWORD written;
WriteConsoleW(s_console_handle, colortext, std::wcslen(colortext), &written, nullptr);
#else
const char* colortext = s_console_colors[static_cast<u32>(color)];
std::fputs(colortext, stdout);
#endif
}
static void ConsoleQt_DoWrite(const char* fmt)
{
std::unique_lock lock(s_log_mutex);
#ifdef _WIN32
if (s_console_handle != INVALID_HANDLE_VALUE || s_debugger_attached)
{
// TODO: Put this on the stack.
std::wstring wfmt(StringUtil::UTF8StringToWideString(fmt));
if (s_debugger_attached)
OutputDebugStringW(wfmt.c_str());
if (s_console_handle != INVALID_HANDLE_VALUE)
{
DWORD written;
WriteConsoleW(s_console_handle, wfmt.c_str(), static_cast<DWORD>(wfmt.length()), &written, nullptr);
}
}
#else
std::fputs(fmt, stdout);
std::fputc('\n', stdout);
#endif
if (emuLog)
{
std::fputs(fmt, emuLog);
}
}
static void ConsoleQt_DoWriteLn(const char* fmt)
{
std::unique_lock lock(s_log_mutex);
// find time since start of process, but save a syscall if we're not writing timestamps
float message_time = s_log_timestamps ?
static_cast<float>(
Common::Timer::ConvertValueToSeconds(Common::Timer::GetCurrentValue() - s_log_start_timestamp)) :
0.0f;
// split newlines up
const char* start = fmt;
do
{
const char* end = std::strchr(start, '\n');
std::string_view line;
if (end)
{
line = std::string_view(start, end - start);
start = end + 1;
}
else
{
line = std::string_view(start);
start = nullptr;
}
#ifdef _WIN32
if (s_console_handle != INVALID_HANDLE_VALUE || s_debugger_attached)
{
// TODO: Put this on the stack.
std::wstring wfmt(StringUtil::UTF8StringToWideString(line));
if (s_debugger_attached)
{
// VS already timestamps logs (at least with the productivity power tools).
if (!wfmt.empty())
OutputDebugStringW(wfmt.c_str());
OutputDebugStringW(L"\n");
}
if (s_console_handle != INVALID_HANDLE_VALUE)
{
DWORD written;
if (s_log_timestamps)
{
wchar_t timestamp_text[128];
const int timestamp_len = _swprintf(timestamp_text, L"[%10.4f] ", message_time);
WriteConsoleW(s_console_handle, timestamp_text, static_cast<DWORD>(timestamp_len), &written, nullptr);
}
if (!wfmt.empty())
WriteConsoleW(s_console_handle, wfmt.c_str(), static_cast<DWORD>(wfmt.length()), &written, nullptr);
WriteConsoleW(s_console_handle, L"\n", 1, &written, nullptr);
}
}
#else
if (s_log_timestamps)
{
std::fprintf(stdout, "[%10.4f] %.*s\n", message_time, static_cast<int>(line.length()), line.data());
}
else
{
if (!line.empty())
std::fwrite(line.data(), line.length(), 1, stdout);
std::fputc('\n', stdout);
}
#endif
if (emuLog)
{
if (s_log_timestamps)
{
std::fprintf(emuLog, "[%10.4f] %.*s\n", message_time, static_cast<int>(line.length()), line.data());
}
else
{
std::fwrite(line.data(), line.length(), 1, emuLog);
std::fputc('\n', emuLog);
}
}
} while (start);
}
static void ConsoleQt_Newline()
{
ConsoleQt_DoWriteLn("");
}
static const IConsoleWriter ConsoleWriter_WinQt =
{
ConsoleQt_DoWrite,
ConsoleQt_DoWriteLn,
ConsoleQt_DoSetColor,
ConsoleQt_DoWrite,
ConsoleQt_Newline,
ConsoleQt_SetTitle,
};
static void UpdateLoggingSinks(bool system_console, bool file_log)
{
#ifdef _WIN32
const bool debugger_attached = IsDebuggerPresent();
s_debugger_attached = debugger_attached;
if (system_console)
{
if (!s_console_handle_set)
{
s_old_console_stdin = GetStdHandle(STD_INPUT_HANDLE);
s_old_console_stdout = GetStdHandle(STD_OUTPUT_HANDLE);
s_old_console_stderr = GetStdHandle(STD_ERROR_HANDLE);
bool handle_valid = (GetConsoleWindow() != NULL);
if (!handle_valid)
{
s_console_allocated = AttachConsole(ATTACH_PARENT_PROCESS) || AllocConsole();
handle_valid = (GetConsoleWindow() != NULL);
}
if (handle_valid)
{
s_console_handle = GetStdHandle(STD_OUTPUT_HANDLE);
if (s_console_handle != INVALID_HANDLE_VALUE)
{
s_console_handle_set = true;
SetConsoleCtrlHandler(ConsoleCtrlHandler, TRUE);
// This gets us unix-style coloured output.
EnableVirtualTerminalProcessing(GetStdHandle(STD_OUTPUT_HANDLE));
EnableVirtualTerminalProcessing(GetStdHandle(STD_ERROR_HANDLE));
// Redirect stdout/stderr.
std::FILE* fp;
freopen_s(&fp, "CONIN$", "r", stdin);
freopen_s(&fp, "CONOUT$", "w", stdout);
freopen_s(&fp, "CONOUT$", "w", stderr);
}
}
}
// just in case it fails
system_console = s_console_handle_set;
}
else
{
if (s_console_handle_set)
{
s_console_handle_set = false;
SetConsoleCtrlHandler(ConsoleCtrlHandler, FALSE);
// redirect stdout/stderr back to null.
std::FILE* fp;
freopen_s(&fp, "NUL:", "w", stderr);
freopen_s(&fp, "NUL:", "w", stdout);
freopen_s(&fp, "NUL:", "w", stdin);
// release console and restore state
SetStdHandle(STD_INPUT_HANDLE, s_old_console_stdin);
SetStdHandle(STD_OUTPUT_HANDLE, s_old_console_stdout);
SetStdHandle(STD_ERROR_HANDLE, s_old_console_stderr);
s_old_console_stdin = NULL;
s_old_console_stdout = NULL;
s_old_console_stderr = NULL;
if (s_console_allocated)
{
s_console_allocated = false;
FreeConsole();
}
}
}
#else
const bool debugger_attached = false;
#endif
if (file_log)
{
if (!emuLog)
{
emuLogName = Path::Combine(EmuFolders::Logs, "emulog.txt");
emuLog = FileSystem::OpenCFile(emuLogName.c_str(), "wb");
file_log = (emuLog != nullptr);
}
}
else
{
if (emuLog)
{
std::fclose(emuLog);
emuLog = nullptr;
emuLogName = {};
}
}
// Discard logs completely if there's no sinks.
if (debugger_attached || system_console || file_log)
Console_SetActiveHandler(ConsoleWriter_WinQt);
else
Console_SetActiveHandler(ConsoleWriter_Null);
}
void QtHost::InitializeEarlyConsole()
{
UpdateLoggingSinks(true, false);
}
void QtHost::UpdateLogging()
{
const bool system_console_enabled = Host::GetBaseBoolSettingValue("Logging", "EnableSystemConsole", false);
const bool file_logging_enabled = Host::GetBaseBoolSettingValue("Logging", "EnableFileLogging", false);
s_log_timestamps = Host::GetBaseBoolSettingValue("Logging", "EnableTimestamps", true);
const bool any_logging_sinks = system_console_enabled || file_logging_enabled;
DevConWriterEnabled = any_logging_sinks && (IsDevBuild || Host::GetBaseBoolSettingValue("Logging", "EnableVerbose", false));
SysConsole.eeConsole.Enabled = any_logging_sinks && Host::GetBaseBoolSettingValue("Logging", "EnableEEConsole", false);
SysConsole.iopConsole.Enabled = any_logging_sinks && Host::GetBaseBoolSettingValue("Logging", "EnableIOPConsole", false);
// Input Recording Logs
SysConsole.recordingConsole.Enabled = any_logging_sinks && Host::GetBaseBoolSettingValue("Logging", "EnableInputRecordingLogs", true);
SysConsole.controlInfo.Enabled = any_logging_sinks && Host::GetBaseBoolSettingValue("Logging", "EnableControllerLogs", false);
UpdateLoggingSinks(system_console_enabled, file_logging_enabled);
}
-6
View File
@@ -40,12 +40,6 @@ namespace QtHost
bool Initialize();
void Shutdown();
void UpdateFolders();
void UpdateLogging();
/// Initializes early console logging (for printing command line arguments).
void InitializeEarlyConsole();
/// Sets batch mode (exit after game shutdown).
bool InBatchMode();
void SetBatchMode(bool enabled);
+36 -5
View File
@@ -15,8 +15,14 @@
#include "PrecompiledHeader.h"
#include "QtUtils.h"
#include "pcsx2/Frontend/InputManager.h"
#include "common/StringUtil.h"
#include <QtGui/QKeyEvent>
struct KeyCodeName
{
int code;
@@ -465,10 +471,18 @@ static constexpr KeyCodeName s_qt_key_names[] = {
std::optional<u32> InputManager::ConvertHostKeyboardStringToCode(const std::string_view& str)
{
std::string compare_name(str);
u32 modifier_bits = 0;
if (StringUtil::StartsWith(compare_name, "Numpad"))
{
compare_name = compare_name.substr(6);
modifier_bits |= Qt::KeypadModifier;
}
for (const KeyCodeName& name : s_qt_key_names)
{
if (str == name.name)
return static_cast<u32>(name.code);
if (compare_name == name.name)
return static_cast<u32>(name.code) | modifier_bits;
}
return std::nullopt;
@@ -476,11 +490,28 @@ std::optional<u32> InputManager::ConvertHostKeyboardStringToCode(const std::stri
std::optional<std::string> InputManager::ConvertHostKeyboardCodeToString(u32 code)
{
std::optional<std::string> ret;
const u32 modifier_bits = (code & Qt::KeyboardModifierMask);
const u32 masked_code = (code & ~Qt::KeyboardModifierMask);
for (const KeyCodeName& name : s_qt_key_names)
{
if (static_cast<int>(code) == name.code)
return std::string(name.name);
if (static_cast<int>(masked_code) == name.code)
{
if (modifier_bits & Qt::KeypadModifier)
ret = fmt::format("Numpad{}", name.name);
else
ret = std::string(name.name);
break;
}
}
return std::nullopt;
return ret;
}
u32 QtUtils::KeyEventToCode(const QKeyEvent* ev)
{
return static_cast<u32>(ev->key()) | (static_cast<u32>(ev->modifiers()) & static_cast<u32>(Qt::KeypadModifier));
}
+11 -534
View File
@@ -133,540 +133,6 @@ namespace QtUtils
ResizeColumnsForView(view, widths);
}
static const std::map<int, QString> s_qt_key_names = {
{Qt::Key_Escape, QStringLiteral("Escape")},
{Qt::Key_Tab, QStringLiteral("Tab")},
{Qt::Key_Backtab, QStringLiteral("Backtab")},
{Qt::Key_Backspace, QStringLiteral("Backspace")},
{Qt::Key_Return, QStringLiteral("Return")},
{Qt::Key_Enter, QStringLiteral("Enter")},
{Qt::Key_Insert, QStringLiteral("Insert")},
{Qt::Key_Delete, QStringLiteral("Delete")},
{Qt::Key_Pause, QStringLiteral("Pause")},
{Qt::Key_Print, QStringLiteral("Print")},
{Qt::Key_SysReq, QStringLiteral("SysReq")},
{Qt::Key_Clear, QStringLiteral("Clear")},
{Qt::Key_Home, QStringLiteral("Home")},
{Qt::Key_End, QStringLiteral("End")},
{Qt::Key_Left, QStringLiteral("Left")},
{Qt::Key_Up, QStringLiteral("Up")},
{Qt::Key_Right, QStringLiteral("Right")},
{Qt::Key_Down, QStringLiteral("Down")},
{Qt::Key_PageUp, QStringLiteral("PageUp")},
{Qt::Key_PageDown, QStringLiteral("PageDown")},
{Qt::Key_Shift, QStringLiteral("Shift")},
{Qt::Key_Control, QStringLiteral("Control")},
{Qt::Key_Meta, QStringLiteral("Meta")},
{Qt::Key_Alt, QStringLiteral("Alt")},
{Qt::Key_CapsLock, QStringLiteral("CapsLock")},
{Qt::Key_NumLock, QStringLiteral("NumLock")},
{Qt::Key_ScrollLock, QStringLiteral("ScrollLock")},
{Qt::Key_F1, QStringLiteral("F1")},
{Qt::Key_F2, QStringLiteral("F2")},
{Qt::Key_F3, QStringLiteral("F3")},
{Qt::Key_F4, QStringLiteral("F4")},
{Qt::Key_F5, QStringLiteral("F5")},
{Qt::Key_F6, QStringLiteral("F6")},
{Qt::Key_F7, QStringLiteral("F7")},
{Qt::Key_F8, QStringLiteral("F8")},
{Qt::Key_F9, QStringLiteral("F9")},
{Qt::Key_F10, QStringLiteral("F10")},
{Qt::Key_F11, QStringLiteral("F11")},
{Qt::Key_F12, QStringLiteral("F12")},
{Qt::Key_F13, QStringLiteral("F13")},
{Qt::Key_F14, QStringLiteral("F14")},
{Qt::Key_F15, QStringLiteral("F15")},
{Qt::Key_F16, QStringLiteral("F16")},
{Qt::Key_F17, QStringLiteral("F17")},
{Qt::Key_F18, QStringLiteral("F18")},
{Qt::Key_F19, QStringLiteral("F19")},
{Qt::Key_F20, QStringLiteral("F20")},
{Qt::Key_F21, QStringLiteral("F21")},
{Qt::Key_F22, QStringLiteral("F22")},
{Qt::Key_F23, QStringLiteral("F23")},
{Qt::Key_F24, QStringLiteral("F24")},
{Qt::Key_F25, QStringLiteral("F25")},
{Qt::Key_F26, QStringLiteral("F26")},
{Qt::Key_F27, QStringLiteral("F27")},
{Qt::Key_F28, QStringLiteral("F28")},
{Qt::Key_F29, QStringLiteral("F29")},
{Qt::Key_F30, QStringLiteral("F30")},
{Qt::Key_F31, QStringLiteral("F31")},
{Qt::Key_F32, QStringLiteral("F32")},
{Qt::Key_F33, QStringLiteral("F33")},
{Qt::Key_F34, QStringLiteral("F34")},
{Qt::Key_F35, QStringLiteral("F35")},
{Qt::Key_Super_L, QStringLiteral("Super_L")},
{Qt::Key_Super_R, QStringLiteral("Super_R")},
{Qt::Key_Menu, QStringLiteral("Menu")},
{Qt::Key_Hyper_L, QStringLiteral("Hyper_L")},
{Qt::Key_Hyper_R, QStringLiteral("Hyper_R")},
{Qt::Key_Help, QStringLiteral("Help")},
{Qt::Key_Direction_L, QStringLiteral("Direction_L")},
{Qt::Key_Direction_R, QStringLiteral("Direction_R")},
{Qt::Key_Space, QStringLiteral("Space")},
{Qt::Key_Any, QStringLiteral("Any")},
{Qt::Key_Exclam, QStringLiteral("Exclam")},
{Qt::Key_QuoteDbl, QStringLiteral("QuoteDbl")},
{Qt::Key_NumberSign, QStringLiteral("NumberSign")},
{Qt::Key_Dollar, QStringLiteral("Dollar")},
{Qt::Key_Percent, QStringLiteral("Percent")},
{Qt::Key_Ampersand, QStringLiteral("Ampersand")},
{Qt::Key_Apostrophe, QStringLiteral("Apostrophe")},
{Qt::Key_ParenLeft, QStringLiteral("ParenLeft")},
{Qt::Key_ParenRight, QStringLiteral("ParenRight")},
{Qt::Key_Asterisk, QStringLiteral("Asterisk")},
{Qt::Key_Plus, QStringLiteral("Plus")},
{Qt::Key_Comma, QStringLiteral("Comma")},
{Qt::Key_Minus, QStringLiteral("Minus")},
{Qt::Key_Period, QStringLiteral("Period")},
{Qt::Key_Slash, QStringLiteral("Slash")},
{Qt::Key_0, QStringLiteral("0")},
{Qt::Key_1, QStringLiteral("1")},
{Qt::Key_2, QStringLiteral("2")},
{Qt::Key_3, QStringLiteral("3")},
{Qt::Key_4, QStringLiteral("4")},
{Qt::Key_5, QStringLiteral("5")},
{Qt::Key_6, QStringLiteral("6")},
{Qt::Key_7, QStringLiteral("7")},
{Qt::Key_8, QStringLiteral("8")},
{Qt::Key_9, QStringLiteral("9")},
{Qt::Key_Colon, QStringLiteral("Colon")},
{Qt::Key_Semicolon, QStringLiteral("Semicolon")},
{Qt::Key_Less, QStringLiteral("Less")},
{Qt::Key_Equal, QStringLiteral("Equal")},
{Qt::Key_Greater, QStringLiteral("Greater")},
{Qt::Key_Question, QStringLiteral("Question")},
{Qt::Key_At, QStringLiteral("At")},
{Qt::Key_A, QStringLiteral("A")},
{Qt::Key_B, QStringLiteral("B")},
{Qt::Key_C, QStringLiteral("C")},
{Qt::Key_D, QStringLiteral("D")},
{Qt::Key_E, QStringLiteral("E")},
{Qt::Key_F, QStringLiteral("F")},
{Qt::Key_G, QStringLiteral("G")},
{Qt::Key_H, QStringLiteral("H")},
{Qt::Key_I, QStringLiteral("I")},
{Qt::Key_J, QStringLiteral("J")},
{Qt::Key_K, QStringLiteral("K")},
{Qt::Key_L, QStringLiteral("L")},
{Qt::Key_M, QStringLiteral("M")},
{Qt::Key_N, QStringLiteral("N")},
{Qt::Key_O, QStringLiteral("O")},
{Qt::Key_P, QStringLiteral("P")},
{Qt::Key_Q, QStringLiteral("Q")},
{Qt::Key_R, QStringLiteral("R")},
{Qt::Key_S, QStringLiteral("S")},
{Qt::Key_T, QStringLiteral("T")},
{Qt::Key_U, QStringLiteral("U")},
{Qt::Key_V, QStringLiteral("V")},
{Qt::Key_W, QStringLiteral("W")},
{Qt::Key_X, QStringLiteral("X")},
{Qt::Key_Y, QStringLiteral("Y")},
{Qt::Key_Z, QStringLiteral("Z")},
{Qt::Key_BracketLeft, QStringLiteral("BracketLeft")},
{Qt::Key_Backslash, QStringLiteral("Backslash")},
{Qt::Key_BracketRight, QStringLiteral("BracketRight")},
{Qt::Key_AsciiCircum, QStringLiteral("AsciiCircum")},
{Qt::Key_Underscore, QStringLiteral("Underscore")},
{Qt::Key_QuoteLeft, QStringLiteral("QuoteLeft")},
{Qt::Key_BraceLeft, QStringLiteral("BraceLeft")},
{Qt::Key_Bar, QStringLiteral("Bar")},
{Qt::Key_BraceRight, QStringLiteral("BraceRight")},
{Qt::Key_AsciiTilde, QStringLiteral("AsciiTilde")},
{Qt::Key_nobreakspace, QStringLiteral("nobreakspace")},
{Qt::Key_exclamdown, QStringLiteral("exclamdown")},
{Qt::Key_cent, QStringLiteral("cent")},
{Qt::Key_sterling, QStringLiteral("sterling")},
{Qt::Key_currency, QStringLiteral("currency")},
{Qt::Key_yen, QStringLiteral("yen")},
{Qt::Key_brokenbar, QStringLiteral("brokenbar")},
{Qt::Key_section, QStringLiteral("section")},
{Qt::Key_diaeresis, QStringLiteral("diaeresis")},
{Qt::Key_copyright, QStringLiteral("copyright")},
{Qt::Key_ordfeminine, QStringLiteral("ordfeminine")},
{Qt::Key_guillemotleft, QStringLiteral("guillemotleft")},
{Qt::Key_notsign, QStringLiteral("notsign")},
{Qt::Key_hyphen, QStringLiteral("hyphen")},
{Qt::Key_registered, QStringLiteral("registered")},
{Qt::Key_macron, QStringLiteral("macron")},
{Qt::Key_degree, QStringLiteral("degree")},
{Qt::Key_plusminus, QStringLiteral("plusminus")},
{Qt::Key_twosuperior, QStringLiteral("twosuperior")},
{Qt::Key_threesuperior, QStringLiteral("threesuperior")},
{Qt::Key_acute, QStringLiteral("acute")},
{Qt::Key_mu, QStringLiteral("mu")},
{Qt::Key_paragraph, QStringLiteral("paragraph")},
{Qt::Key_periodcentered, QStringLiteral("periodcentered")},
{Qt::Key_cedilla, QStringLiteral("cedilla")},
{Qt::Key_onesuperior, QStringLiteral("onesuperior")},
{Qt::Key_masculine, QStringLiteral("masculine")},
{Qt::Key_guillemotright, QStringLiteral("guillemotright")},
{Qt::Key_onequarter, QStringLiteral("onequarter")},
{Qt::Key_onehalf, QStringLiteral("onehalf")},
{Qt::Key_threequarters, QStringLiteral("threequarters")},
{Qt::Key_questiondown, QStringLiteral("questiondown")},
{Qt::Key_Agrave, QStringLiteral("Agrave")},
{Qt::Key_Aacute, QStringLiteral("Aacute")},
{Qt::Key_Acircumflex, QStringLiteral("Acircumflex")},
{Qt::Key_Atilde, QStringLiteral("Atilde")},
{Qt::Key_Adiaeresis, QStringLiteral("Adiaeresis")},
{Qt::Key_Aring, QStringLiteral("Aring")},
{Qt::Key_AE, QStringLiteral("AE")},
{Qt::Key_Ccedilla, QStringLiteral("Ccedilla")},
{Qt::Key_Egrave, QStringLiteral("Egrave")},
{Qt::Key_Eacute, QStringLiteral("Eacute")},
{Qt::Key_Ecircumflex, QStringLiteral("Ecircumflex")},
{Qt::Key_Ediaeresis, QStringLiteral("Ediaeresis")},
{Qt::Key_Igrave, QStringLiteral("Igrave")},
{Qt::Key_Iacute, QStringLiteral("Iacute")},
{Qt::Key_Icircumflex, QStringLiteral("Icircumflex")},
{Qt::Key_Idiaeresis, QStringLiteral("Idiaeresis")},
{Qt::Key_ETH, QStringLiteral("ETH")},
{Qt::Key_Ntilde, QStringLiteral("Ntilde")},
{Qt::Key_Ograve, QStringLiteral("Ograve")},
{Qt::Key_Oacute, QStringLiteral("Oacute")},
{Qt::Key_Ocircumflex, QStringLiteral("Ocircumflex")},
{Qt::Key_Otilde, QStringLiteral("Otilde")},
{Qt::Key_Odiaeresis, QStringLiteral("Odiaeresis")},
{Qt::Key_multiply, QStringLiteral("multiply")},
{Qt::Key_Ooblique, QStringLiteral("Ooblique")},
{Qt::Key_Ugrave, QStringLiteral("Ugrave")},
{Qt::Key_Uacute, QStringLiteral("Uacute")},
{Qt::Key_Ucircumflex, QStringLiteral("Ucircumflex")},
{Qt::Key_Udiaeresis, QStringLiteral("Udiaeresis")},
{Qt::Key_Yacute, QStringLiteral("Yacute")},
{Qt::Key_THORN, QStringLiteral("THORN")},
{Qt::Key_ssharp, QStringLiteral("ssharp")},
{Qt::Key_division, QStringLiteral("division")},
{Qt::Key_ydiaeresis, QStringLiteral("ydiaeresis")},
{Qt::Key_AltGr, QStringLiteral("AltGr")},
{Qt::Key_Multi_key, QStringLiteral("Multi_key")},
{Qt::Key_Codeinput, QStringLiteral("Codeinput")},
{Qt::Key_SingleCandidate, QStringLiteral("SingleCandidate")},
{Qt::Key_MultipleCandidate, QStringLiteral("MultipleCandidate")},
{Qt::Key_PreviousCandidate, QStringLiteral("PreviousCandidate")},
{Qt::Key_Mode_switch, QStringLiteral("Mode_switch")},
{Qt::Key_Kanji, QStringLiteral("Kanji")},
{Qt::Key_Muhenkan, QStringLiteral("Muhenkan")},
{Qt::Key_Henkan, QStringLiteral("Henkan")},
{Qt::Key_Romaji, QStringLiteral("Romaji")},
{Qt::Key_Hiragana, QStringLiteral("Hiragana")},
{Qt::Key_Katakana, QStringLiteral("Katakana")},
{Qt::Key_Hiragana_Katakana, QStringLiteral("Hiragana_Katakana")},
{Qt::Key_Zenkaku, QStringLiteral("Zenkaku")},
{Qt::Key_Hankaku, QStringLiteral("Hankaku")},
{Qt::Key_Zenkaku_Hankaku, QStringLiteral("Zenkaku_Hankaku")},
{Qt::Key_Touroku, QStringLiteral("Touroku")},
{Qt::Key_Massyo, QStringLiteral("Massyo")},
{Qt::Key_Kana_Lock, QStringLiteral("Kana_Lock")},
{Qt::Key_Kana_Shift, QStringLiteral("Kana_Shift")},
{Qt::Key_Eisu_Shift, QStringLiteral("Eisu_Shift")},
{Qt::Key_Eisu_toggle, QStringLiteral("Eisu_toggle")},
{Qt::Key_Hangul, QStringLiteral("Hangul")},
{Qt::Key_Hangul_Start, QStringLiteral("Hangul_Start")},
{Qt::Key_Hangul_End, QStringLiteral("Hangul_End")},
{Qt::Key_Hangul_Hanja, QStringLiteral("Hangul_Hanja")},
{Qt::Key_Hangul_Jamo, QStringLiteral("Hangul_Jamo")},
{Qt::Key_Hangul_Romaja, QStringLiteral("Hangul_Romaja")},
{Qt::Key_Hangul_Jeonja, QStringLiteral("Hangul_Jeonja")},
{Qt::Key_Hangul_Banja, QStringLiteral("Hangul_Banja")},
{Qt::Key_Hangul_PreHanja, QStringLiteral("Hangul_PreHanja")},
{Qt::Key_Hangul_PostHanja, QStringLiteral("Hangul_PostHanja")},
{Qt::Key_Hangul_Special, QStringLiteral("Hangul_Special")},
{Qt::Key_Dead_Grave, QStringLiteral("Dead_Grave")},
{Qt::Key_Dead_Acute, QStringLiteral("Dead_Acute")},
{Qt::Key_Dead_Circumflex, QStringLiteral("Dead_Circumflex")},
{Qt::Key_Dead_Tilde, QStringLiteral("Dead_Tilde")},
{Qt::Key_Dead_Macron, QStringLiteral("Dead_Macron")},
{Qt::Key_Dead_Breve, QStringLiteral("Dead_Breve")},
{Qt::Key_Dead_Abovedot, QStringLiteral("Dead_Abovedot")},
{Qt::Key_Dead_Diaeresis, QStringLiteral("Dead_Diaeresis")},
{Qt::Key_Dead_Abovering, QStringLiteral("Dead_Abovering")},
{Qt::Key_Dead_Doubleacute, QStringLiteral("Dead_Doubleacute")},
{Qt::Key_Dead_Caron, QStringLiteral("Dead_Caron")},
{Qt::Key_Dead_Cedilla, QStringLiteral("Dead_Cedilla")},
{Qt::Key_Dead_Ogonek, QStringLiteral("Dead_Ogonek")},
{Qt::Key_Dead_Iota, QStringLiteral("Dead_Iota")},
{Qt::Key_Dead_Voiced_Sound, QStringLiteral("Dead_Voiced_Sound")},
{Qt::Key_Dead_Semivoiced_Sound, QStringLiteral("Dead_Semivoiced_Sound")},
{Qt::Key_Dead_Belowdot, QStringLiteral("Dead_Belowdot")},
{Qt::Key_Dead_Hook, QStringLiteral("Dead_Hook")},
{Qt::Key_Dead_Horn, QStringLiteral("Dead_Horn")},
{Qt::Key_Back, QStringLiteral("Back")},
{Qt::Key_Forward, QStringLiteral("Forward")},
{Qt::Key_Stop, QStringLiteral("Stop")},
{Qt::Key_Refresh, QStringLiteral("Refresh")},
{Qt::Key_VolumeDown, QStringLiteral("VolumeDown")},
{Qt::Key_VolumeMute, QStringLiteral("VolumeMute")},
{Qt::Key_VolumeUp, QStringLiteral("VolumeUp")},
{Qt::Key_BassBoost, QStringLiteral("BassBoost")},
{Qt::Key_BassUp, QStringLiteral("BassUp")},
{Qt::Key_BassDown, QStringLiteral("BassDown")},
{Qt::Key_TrebleUp, QStringLiteral("TrebleUp")},
{Qt::Key_TrebleDown, QStringLiteral("TrebleDown")},
{Qt::Key_MediaPlay, QStringLiteral("MediaPlay")},
{Qt::Key_MediaStop, QStringLiteral("MediaStop")},
{Qt::Key_MediaPrevious, QStringLiteral("MediaPrevious")},
{Qt::Key_MediaNext, QStringLiteral("MediaNext")},
{Qt::Key_MediaRecord, QStringLiteral("MediaRecord")},
{Qt::Key_MediaPause, QStringLiteral("MediaPause")},
{Qt::Key_MediaTogglePlayPause, QStringLiteral("MediaTogglePlayPause")},
{Qt::Key_HomePage, QStringLiteral("HomePage")},
{Qt::Key_Favorites, QStringLiteral("Favorites")},
{Qt::Key_Search, QStringLiteral("Search")},
{Qt::Key_Standby, QStringLiteral("Standby")},
{Qt::Key_OpenUrl, QStringLiteral("OpenUrl")},
{Qt::Key_LaunchMail, QStringLiteral("LaunchMail")},
{Qt::Key_LaunchMedia, QStringLiteral("LaunchMedia")},
{Qt::Key_Launch0, QStringLiteral("Launch0")},
{Qt::Key_Launch1, QStringLiteral("Launch1")},
{Qt::Key_Launch2, QStringLiteral("Launch2")},
{Qt::Key_Launch3, QStringLiteral("Launch3")},
{Qt::Key_Launch4, QStringLiteral("Launch4")},
{Qt::Key_Launch5, QStringLiteral("Launch5")},
{Qt::Key_Launch6, QStringLiteral("Launch6")},
{Qt::Key_Launch7, QStringLiteral("Launch7")},
{Qt::Key_Launch8, QStringLiteral("Launch8")},
{Qt::Key_Launch9, QStringLiteral("Launch9")},
{Qt::Key_LaunchA, QStringLiteral("LaunchA")},
{Qt::Key_LaunchB, QStringLiteral("LaunchB")},
{Qt::Key_LaunchC, QStringLiteral("LaunchC")},
{Qt::Key_LaunchD, QStringLiteral("LaunchD")},
{Qt::Key_LaunchE, QStringLiteral("LaunchE")},
{Qt::Key_LaunchF, QStringLiteral("LaunchF")},
{Qt::Key_MonBrightnessUp, QStringLiteral("MonBrightnessUp")},
{Qt::Key_MonBrightnessDown, QStringLiteral("MonBrightnessDown")},
{Qt::Key_KeyboardLightOnOff, QStringLiteral("KeyboardLightOnOff")},
{Qt::Key_KeyboardBrightnessUp, QStringLiteral("KeyboardBrightnessUp")},
{Qt::Key_KeyboardBrightnessDown, QStringLiteral("KeyboardBrightnessDown")},
{Qt::Key_PowerOff, QStringLiteral("PowerOff")},
{Qt::Key_WakeUp, QStringLiteral("WakeUp")},
{Qt::Key_Eject, QStringLiteral("Eject")},
{Qt::Key_ScreenSaver, QStringLiteral("ScreenSaver")},
{Qt::Key_WWW, QStringLiteral("WWW")},
{Qt::Key_Memo, QStringLiteral("Memo")},
{Qt::Key_LightBulb, QStringLiteral("LightBulb")},
{Qt::Key_Shop, QStringLiteral("Shop")},
{Qt::Key_History, QStringLiteral("History")},
{Qt::Key_AddFavorite, QStringLiteral("AddFavorite")},
{Qt::Key_HotLinks, QStringLiteral("HotLinks")},
{Qt::Key_BrightnessAdjust, QStringLiteral("BrightnessAdjust")},
{Qt::Key_Finance, QStringLiteral("Finance")},
{Qt::Key_Community, QStringLiteral("Community")},
{Qt::Key_AudioRewind, QStringLiteral("AudioRewind")},
{Qt::Key_BackForward, QStringLiteral("BackForward")},
{Qt::Key_ApplicationLeft, QStringLiteral("ApplicationLeft")},
{Qt::Key_ApplicationRight, QStringLiteral("ApplicationRight")},
{Qt::Key_Book, QStringLiteral("Book")},
{Qt::Key_CD, QStringLiteral("CD")},
{Qt::Key_Calculator, QStringLiteral("Calculator")},
{Qt::Key_ToDoList, QStringLiteral("ToDoList")},
{Qt::Key_ClearGrab, QStringLiteral("ClearGrab")},
{Qt::Key_Close, QStringLiteral("Close")},
{Qt::Key_Copy, QStringLiteral("Copy")},
{Qt::Key_Cut, QStringLiteral("Cut")},
{Qt::Key_Display, QStringLiteral("Display")},
{Qt::Key_DOS, QStringLiteral("DOS")},
{Qt::Key_Documents, QStringLiteral("Documents")},
{Qt::Key_Excel, QStringLiteral("Excel")},
{Qt::Key_Explorer, QStringLiteral("Explorer")},
{Qt::Key_Game, QStringLiteral("Game")},
{Qt::Key_Go, QStringLiteral("Go")},
{Qt::Key_iTouch, QStringLiteral("iTouch")},
{Qt::Key_LogOff, QStringLiteral("LogOff")},
{Qt::Key_Market, QStringLiteral("Market")},
{Qt::Key_Meeting, QStringLiteral("Meeting")},
{Qt::Key_MenuKB, QStringLiteral("MenuKB")},
{Qt::Key_MenuPB, QStringLiteral("MenuPB")},
{Qt::Key_MySites, QStringLiteral("MySites")},
{Qt::Key_News, QStringLiteral("News")},
{Qt::Key_OfficeHome, QStringLiteral("OfficeHome")},
{Qt::Key_Option, QStringLiteral("Option")},
{Qt::Key_Paste, QStringLiteral("Paste")},
{Qt::Key_Phone, QStringLiteral("Phone")},
{Qt::Key_Calendar, QStringLiteral("Calendar")},
{Qt::Key_Reply, QStringLiteral("Reply")},
{Qt::Key_Reload, QStringLiteral("Reload")},
{Qt::Key_RotateWindows, QStringLiteral("RotateWindows")},
{Qt::Key_RotationPB, QStringLiteral("RotationPB")},
{Qt::Key_RotationKB, QStringLiteral("RotationKB")},
{Qt::Key_Save, QStringLiteral("Save")},
{Qt::Key_Send, QStringLiteral("Send")},
{Qt::Key_Spell, QStringLiteral("Spell")},
{Qt::Key_SplitScreen, QStringLiteral("SplitScreen")},
{Qt::Key_Support, QStringLiteral("Support")},
{Qt::Key_TaskPane, QStringLiteral("TaskPane")},
{Qt::Key_Terminal, QStringLiteral("Terminal")},
{Qt::Key_Tools, QStringLiteral("Tools")},
{Qt::Key_Travel, QStringLiteral("Travel")},
{Qt::Key_Video, QStringLiteral("Video")},
{Qt::Key_Word, QStringLiteral("Word")},
{Qt::Key_Xfer, QStringLiteral("Xfer")},
{Qt::Key_ZoomIn, QStringLiteral("ZoomIn")},
{Qt::Key_ZoomOut, QStringLiteral("ZoomOut")},
{Qt::Key_Away, QStringLiteral("Away")},
{Qt::Key_Messenger, QStringLiteral("Messenger")},
{Qt::Key_WebCam, QStringLiteral("WebCam")},
{Qt::Key_MailForward, QStringLiteral("MailForward")},
{Qt::Key_Pictures, QStringLiteral("Pictures")},
{Qt::Key_Music, QStringLiteral("Music")},
{Qt::Key_Battery, QStringLiteral("Battery")},
{Qt::Key_Bluetooth, QStringLiteral("Bluetooth")},
{Qt::Key_WLAN, QStringLiteral("WLAN")},
{Qt::Key_UWB, QStringLiteral("UWB")},
{Qt::Key_AudioForward, QStringLiteral("AudioForward")},
{Qt::Key_AudioRepeat, QStringLiteral("AudioRepeat")},
{Qt::Key_AudioRandomPlay, QStringLiteral("AudioRandomPlay")},
{Qt::Key_Subtitle, QStringLiteral("Subtitle")},
{Qt::Key_AudioCycleTrack, QStringLiteral("AudioCycleTrack")},
{Qt::Key_Time, QStringLiteral("Time")},
{Qt::Key_Hibernate, QStringLiteral("Hibernate")},
{Qt::Key_View, QStringLiteral("View")},
{Qt::Key_TopMenu, QStringLiteral("TopMenu")},
{Qt::Key_PowerDown, QStringLiteral("PowerDown")},
{Qt::Key_Suspend, QStringLiteral("Suspend")},
{Qt::Key_ContrastAdjust, QStringLiteral("ContrastAdjust")},
{Qt::Key_LaunchG, QStringLiteral("LaunchG")},
{Qt::Key_LaunchH, QStringLiteral("LaunchH")},
{Qt::Key_TouchpadToggle, QStringLiteral("TouchpadToggle")},
{Qt::Key_TouchpadOn, QStringLiteral("TouchpadOn")},
{Qt::Key_TouchpadOff, QStringLiteral("TouchpadOff")},
{Qt::Key_MicMute, QStringLiteral("MicMute")},
{Qt::Key_Red, QStringLiteral("Red")},
{Qt::Key_Green, QStringLiteral("Green")},
{Qt::Key_Yellow, QStringLiteral("Yellow")},
{Qt::Key_Blue, QStringLiteral("Blue")},
{Qt::Key_ChannelUp, QStringLiteral("ChannelUp")},
{Qt::Key_ChannelDown, QStringLiteral("ChannelDown")},
{Qt::Key_Guide, QStringLiteral("Guide")},
{Qt::Key_Info, QStringLiteral("Info")},
{Qt::Key_Settings, QStringLiteral("Settings")},
{Qt::Key_MicVolumeUp, QStringLiteral("MicVolumeUp")},
{Qt::Key_MicVolumeDown, QStringLiteral("MicVolumeDown")},
{Qt::Key_New, QStringLiteral("New")},
{Qt::Key_Open, QStringLiteral("Open")},
{Qt::Key_Find, QStringLiteral("Find")},
{Qt::Key_Undo, QStringLiteral("Undo")},
{Qt::Key_Redo, QStringLiteral("Redo")},
{Qt::Key_MediaLast, QStringLiteral("MediaLast")},
{Qt::Key_Select, QStringLiteral("Select")},
{Qt::Key_Yes, QStringLiteral("Yes")},
{Qt::Key_No, QStringLiteral("No")},
{Qt::Key_Cancel, QStringLiteral("Cancel")},
{Qt::Key_Printer, QStringLiteral("Printer")},
{Qt::Key_Execute, QStringLiteral("Execute")},
{Qt::Key_Sleep, QStringLiteral("Sleep")},
{Qt::Key_Play, QStringLiteral("Play")},
{Qt::Key_Zoom, QStringLiteral("Zoom")},
{Qt::Key_Exit, QStringLiteral("Exit")},
{Qt::Key_Context1, QStringLiteral("Context1")},
{Qt::Key_Context2, QStringLiteral("Context2")},
{Qt::Key_Context3, QStringLiteral("Context3")},
{Qt::Key_Context4, QStringLiteral("Context4")},
{Qt::Key_Call, QStringLiteral("Call")},
{Qt::Key_Hangup, QStringLiteral("Hangup")},
{Qt::Key_Flip, QStringLiteral("Flip")},
{Qt::Key_ToggleCallHangup, QStringLiteral("ToggleCallHangup")},
{Qt::Key_VoiceDial, QStringLiteral("VoiceDial")},
{Qt::Key_LastNumberRedial, QStringLiteral("LastNumberRedial")},
{Qt::Key_Camera, QStringLiteral("Camera")},
{Qt::Key_CameraFocus, QStringLiteral("CameraFocus")}};
struct QtKeyModifierEntry
{
Qt::KeyboardModifier mod;
Qt::Key key;
QString name;
};
static const std::array<QtKeyModifierEntry, 5> s_qt_key_modifiers = {
{{Qt::ShiftModifier, Qt::Key_Shift, QStringLiteral("Shift")},
{Qt::ControlModifier, Qt::Key_Control, QStringLiteral("Control")},
{Qt::AltModifier, Qt::Key_Alt, QStringLiteral("Alt")},
{Qt::MetaModifier, Qt::Key_Meta, QStringLiteral("Meta")},
{Qt::KeypadModifier, static_cast<Qt::Key>(0), QStringLiteral("Keypad")}}};
QString GetKeyIdentifier(int key)
{
const auto it = s_qt_key_names.find(key);
return it == s_qt_key_names.end() ? QString() : it->second;
}
std::optional<int> GetKeyIdForIdentifier(const QString& key_identifier)
{
for (const auto& it : s_qt_key_names)
{
if (it.second == key_identifier)
return it.first;
}
return std::nullopt;
}
QString KeyEventToString(int key, Qt::KeyboardModifiers mods)
{
QString key_name = GetKeyIdentifier(key);
if (key_name.isEmpty())
return {};
QString ret;
for (const QtKeyModifierEntry& mod : s_qt_key_modifiers)
{
if (mods & mod.mod && key != mod.key)
{
ret.append(mod.name);
ret.append('+');
}
}
ret.append(key_name);
return ret;
}
std::optional<int> ParseKeyString(const QString& key_str)
{
const QStringList sections = key_str.split('+');
std::optional<int> key_id = GetKeyIdForIdentifier(sections.last());
if (!key_id)
return std::nullopt;
int ret = key_id.value();
if (sections.size() > 1)
{
const int num_modifiers = sections.size() - 1;
for (int i = 0; i < num_modifiers; i++)
{
for (const QtKeyModifierEntry& mod : s_qt_key_modifiers)
{
if (sections[i] == mod.name)
{
ret |= static_cast<int>(mod.mod);
break;
}
}
}
}
return ret;
}
int KeyEventToInt(int key, Qt::KeyboardModifiers mods)
{
int val = key;
if (mods != 0)
{
for (const QtKeyModifierEntry& mod : s_qt_key_modifiers)
{
if (mods & mod.mod && key != mod.key)
val |= static_cast<int>(mod.mod);
}
}
return val;
}
void OpenURL(QWidget* parent, const QUrl& qurl)
{
if (!QDesktopServices::openUrl(qurl))
@@ -690,4 +156,15 @@ namespace QtUtils
{
return str.empty() ? QString() : QString::fromUtf8(str.data(), str.size());
}
void SetWidgetFontForInheritedSetting(QWidget* widget, bool inherited)
{
if (widget->font().italic() != inherited)
{
QFont new_font(widget->font());
new_font.setItalic(inherited);
widget->setFont(new_font);
}
}
} // namespace QtUtils
+6 -14
View File
@@ -50,20 +50,9 @@ namespace QtUtils
void ResizeColumnsForTableView(QTableView* view, const std::initializer_list<int>& widths);
void ResizeColumnsForTreeView(QTreeView* view, const std::initializer_list<int>& widths);
/// Returns a string identifier for a Qt key ID.
QString GetKeyIdentifier(int key);
/// Returns the integer Qt key ID for an identifier.
std::optional<int> GetKeyIdForIdentifier(const QString& key_identifier);
/// Stringizes a key event.
QString KeyEventToString(int key, Qt::KeyboardModifiers mods);
/// Returns an integer id for a stringized key event. Modifiers are in the upper bits.
std::optional<int> ParseKeyString(const QString& key_str);
/// Returns a key id for a key event, including any modifiers.
int KeyEventToInt(int key, Qt::KeyboardModifiers mods);
/// Returns a key id for a key event, including any modifiers that we need (e.g. Keypad).
/// NOTE: Defined in QtKeyCodes.cpp, not QtUtils.cpp.
u32 KeyEventToCode(const QKeyEvent* ev);
/// Opens a URL with the default handler.
void OpenURL(QWidget* parent, const QUrl& qurl);
@@ -76,4 +65,7 @@ namespace QtUtils
/// Converts a std::string_view to a QString safely.
QString StringViewToQString(const std::string_view& str);
/// Sets a widget to italics if the setting value is inherited.
void SetWidgetFontForInheritedSetting(QWidget* widget, bool inherited);
} // namespace QtUtils
+73 -1
View File
@@ -20,17 +20,23 @@
#include <QtCore/QtCore>
#include <QtGui/QAction>
#include <QtWidgets/QAbstractButton>
#include <QtWidgets/QCheckBox>
#include <QtWidgets/QComboBox>
#include <QtWidgets/QDoubleSpinBox>
#include <QtWidgets/QFileDialog>
#include <QtWidgets/QLineEdit>
#include <QtWidgets/QSlider>
#include <QtWidgets/QSpinBox>
#include "common/Path.h"
#include "pcsx2/Config.h"
#include "pcsx2/HostSettings.h"
#include "EmuThread.h"
#include "QtHost.h"
#include "QtUtils.h"
#include "Settings/SettingsDialog.h"
namespace SettingWidgetBinder
@@ -228,7 +234,7 @@ namespace SettingWidgetBinder
static std::optional<QString> getNullableStringValue(const QCheckBox* widget)
{
return (widget->checkState() == Qt::PartiallyChecked) ?
std::nullopt :
std::nullopt :
std::optional<QString>(widget->isChecked() ? QStringLiteral("1") : QStringLiteral("0"));
}
static void setNullableStringValue(QCheckBox* widget, std::optional<QString> value)
@@ -769,4 +775,70 @@ namespace SettingWidgetBinder
}
}
template <typename WidgetType>
static void BindWidgetToFolderSetting(SettingsInterface* sif, WidgetType* widget,
QAbstractButton* browse_button, QAbstractButton* open_button, QAbstractButton* reset_button,
std::string section, std::string key, std::string default_value)
{
using Accessor = SettingAccessor<WidgetType>;
std::string current_path(Host::GetBaseStringSettingValue(section.c_str(), key.c_str(), default_value.c_str()));
if (!Path::IsAbsolute(current_path))
current_path = Path::Combine(EmuFolders::DataRoot, current_path);
const QString value(QString::fromStdString(current_path));
Accessor::setStringValue(widget, value);
// if we're doing per-game settings, disable the widget, we only allow folder changes in the base config
if (sif)
{
widget->setEnabled(false);
if (browse_button)
browse_button->setEnabled(false);
if (reset_button)
reset_button->setEnabled(false);
return;
}
Accessor::connectValueChanged(widget, [widget, section = std::move(section), key = std::move(key)]() {
const std::string new_value(Accessor::getStringValue(widget).toStdString());
if (!new_value.empty())
{
std::string relative_path(Path::MakeRelative(new_value, EmuFolders::DataRoot));
QtHost::SetBaseStringSettingValue(section.c_str(), key.c_str(), relative_path.c_str());
}
else
{
QtHost::RemoveBaseSettingValue(section.c_str(), key.c_str());
}
g_emu_thread->updateEmuFolders();
});
if (browse_button)
{
QObject::connect(browse_button, &QAbstractButton::clicked, browse_button, [widget, key]() {
const QString path(QDir::toNativeSeparators(QFileDialog::getExistingDirectory(QtUtils::GetRootWidget(widget),
qApp->translate("SettingWidgetBinder", "Select folder for %1").arg(QString::fromStdString(key)))));
if (path.isEmpty())
return;
Accessor::setStringValue(widget, path);
});
}
if (open_button)
{
QObject::connect(open_button, &QAbstractButton::clicked, open_button, [widget]() {
QString path(Accessor::getStringValue(widget));
if (!path.isEmpty())
QtUtils::OpenURL(QtUtils::GetRootWidget(widget), QUrl::fromLocalFile(path));
});
}
if (reset_button)
{
QObject::connect(reset_button, &QAbstractButton::clicked, reset_button, [widget, default_value = std::move(default_value)]() {
Accessor::setStringValue(widget, QString::fromStdString(Path::Combine(EmuFolders::AppRoot, default_value)));
});
}
}
} // namespace SettingWidgetBinder
+4 -36
View File
@@ -38,28 +38,17 @@ BIOSSettingsWidget::BIOSSettingsWidget(SettingsDialog* dialog, QWidget* parent)
SettingWidgetBinder::BindWidgetToBoolSetting(sif, m_ui.fastBoot, "EmuCore", "EnableFastBoot", true);
SettingWidgetBinder::BindWidgetToBoolSetting(sif, m_ui.patchRegion, "EmuCore", "PatchBios", false);
SettingWidgetBinder::BindWidgetToEnumSetting(sif, m_ui.regionComboBox, "EmuCore", "PatchRegion", BiosZoneStrings, BiosZoneBytes, BiosZoneBytes[0]);
SettingWidgetBinder::BindWidgetToFolderSetting(sif, m_ui.searchDirectory, m_ui.browseSearchDirectory, m_ui.openSearchDirectory,
m_ui.resetSearchDirectory, "Folders", "Bios", "bios");
dialog->registerWidgetHelp(m_ui.patchRegion, tr("Patch Region"),tr("Unchecked"),
dialog->registerWidgetHelp(m_ui.patchRegion, tr("Patch Region"), tr("Unchecked"),
tr("Patches the BIOS region byte in ROM. Not recommended unless you really know what you're doing."));
dialog->registerWidgetHelp(m_ui.fastBoot, tr("Fast Boot"), tr("Unchecked"),
tr("Patches the BIOS to skip the console's boot animation."));
updateSearchDirectory();
refreshList();
connect(m_ui.searchDirectory, &QLineEdit::textChanged, [this](const QString& text) {
QtHost::SetBaseStringSettingValue("Folders", "Bios", text.toUtf8().constData());
QtHost::UpdateFolders();
refreshList();
});
connect(m_ui.resetSearchDirectory, &QPushButton::clicked, [this]() {
QtHost::RemoveBaseSettingValue("Folders", "Bios");
QtHost::UpdateFolders();
updateSearchDirectory();
refreshList();
});
connect(m_ui.browseSearchDirectory, &QPushButton::clicked, this, &BIOSSettingsWidget::browseSearchDirectory);
connect(m_ui.openSearchDirectory, &QPushButton::clicked, this, &BIOSSettingsWidget::openSearchDirectory);
connect(m_ui.searchDirectory, &QLineEdit::textChanged, this, &BIOSSettingsWidget::refreshList);
connect(m_ui.refresh, &QPushButton::clicked, this, &BIOSSettingsWidget::refreshList);
connect(m_ui.fileList, &QTreeWidget::currentItemChanged, this, &BIOSSettingsWidget::listItemChanged);
@@ -89,27 +78,6 @@ void BIOSSettingsWidget::refreshList()
m_refresh_thread->start();
}
void BIOSSettingsWidget::browseSearchDirectory()
{
QString directory = QDir::toNativeSeparators(QFileDialog::getExistingDirectory(
QtUtils::GetRootWidget(this), tr("Select Directory"), m_ui.searchDirectory->text()));
if (directory.isEmpty())
return;
m_ui.searchDirectory->setText(directory);
}
void BIOSSettingsWidget::openSearchDirectory()
{
QtUtils::OpenURL(this, QUrl::fromLocalFile(m_ui.searchDirectory->text()));
}
void BIOSSettingsWidget::updateSearchDirectory()
{
// this will generate a full path
m_ui.searchDirectory->setText(QString::fromStdString(EmuFolders::Bios));
}
void BIOSSettingsWidget::listRefreshed(const QVector<BIOSInfo>& items)
{
const std::string selected_bios(Host::GetBaseStringSettingValue("Filenames", "BIOS"));
-3
View File
@@ -47,9 +47,6 @@ public:
private Q_SLOTS:
void refreshList();
void browseSearchDirectory();
void openSearchDirectory();
void updateSearchDirectory();
void listItemChanged(const QTreeWidgetItem* current, const QTreeWidgetItem* previous);
void listRefreshed(const QVector<BIOSInfo>& items);
+1 -1
View File
@@ -188,7 +188,7 @@
</layout>
</widget>
<resources>
<include location="resources/resources.qrc"/>
<include location="../resources/resources.qrc"/>
</resources>
<connections/>
</ui>
+3 -24
View File
@@ -6,7 +6,7 @@
<rect>
<x>0</x>
<y>0</y>
<width>834</width>
<width>833</width>
<height>560</height>
</rect>
</property>
@@ -61,35 +61,14 @@
</property>
</widget>
</item>
<item>
<widget class="QPushButton" name="loadProfile">
<property name="text">
<string>Load Profile</string>
</property>
<property name="icon">
<iconset theme="folder-open-line">
<normaloff>.</normaloff>.</iconset>
</property>
</widget>
</item>
<item>
<widget class="QPushButton" name="saveProfile">
<property name="text">
<string>Save Profile</string>
</property>
<property name="icon">
<iconset theme="save-3-line">
<normaloff>.</normaloff>.</iconset>
</property>
</widget>
</item>
<item>
<widget class="QPushButton" name="clearBindings">
<property name="text">
<string>Clear Bindings</string>
</property>
<property name="icon">
<iconset theme="file-reduce-line"/>
<iconset theme="file-reduce-line">
<normaloff>.</normaloff>.</iconset>
</property>
</widget>
</item>
@@ -623,10 +623,10 @@
</layout>
</widget>
</item>
<item row="2" column="1" rowspan="2" colspan="4">
<item row="2" column="3" rowspan="2" colspan="2">
<widget class="QGroupBox" name="groupBox_30">
<property name="title">
<string>Axis Scale</string>
<string>Analog Sensitivity</string>
</property>
<layout class="QHBoxLayout" name="horizontalLayout_2">
<item>
@@ -642,7 +642,33 @@
<item>
<widget class="QLabel" name="AxisScaleLabel">
<property name="text">
<string>1.0x</string>
<string>100%</string>
</property>
</widget>
</item>
</layout>
</widget>
</item>
<item row="2" column="1" rowspan="2" colspan="2">
<widget class="QGroupBox" name="groupBox_33">
<property name="title">
<string>Analog Deadzone</string>
</property>
<layout class="QHBoxLayout" name="horizontalLayout_3">
<item>
<widget class="QSlider" name="Deadzone">
<property name="maximum">
<number>100</number>
</property>
<property name="orientation">
<enum>Qt::Horizontal</enum>
</property>
</widget>
</item>
<item>
<widget class="QLabel" name="DeadzoneLabel">
<property name="text">
<string>0%</string>
</property>
</widget>
</item>
+65 -15
View File
@@ -19,19 +19,18 @@
#include <QtWidgets/QMessageBox>
#include <algorithm>
#include "ControllerBindingWidgets.h"
#include "ControllerSettingsDialog.h"
#include "Settings/ControllerBindingWidgets.h"
#include "Settings/ControllerSettingsDialog.h"
#include "Settings/ControllerSettingWidgetBinder.h"
#include "Settings/SettingsDialog.h"
#include "EmuThread.h"
#include "QtUtils.h"
#include "SettingWidgetBinder.h"
#include "SettingsDialog.h"
#include "common/StringUtil.h"
#include "pcsx2/HostSettings.h"
#include "pcsx2/PAD/Host/PAD.h"
#include "SettingWidgetBinder.h"
ControllerBindingWidget::ControllerBindingWidget(QWidget* parent, ControllerSettingsDialog* dialog, u32 port)
: QWidget(parent)
, m_dialog(dialog)
@@ -42,7 +41,9 @@ ControllerBindingWidget::ControllerBindingWidget(QWidget* parent, ControllerSett
populateControllerTypes();
onTypeChanged();
SettingWidgetBinder::BindWidgetToStringSetting(nullptr, m_ui.controllerType, m_config_section, "Type", "None");
ControllerSettingWidgetBinder::BindWidgetToInputProfileString(m_dialog->getProfileSettingsInterface(),
m_ui.controllerType, m_config_section, "Type", PAD::GetDefaultPadType(port));
connect(m_ui.controllerType, QOverload<int>::of(&QComboBox::currentIndexChanged), this, &ControllerBindingWidget::onTypeChanged);
connect(m_ui.automaticBinding, &QPushButton::clicked, this, &ControllerBindingWidget::doAutomaticBinding);
connect(m_ui.clearBindings, &QPushButton::clicked, this, &ControllerBindingWidget::doClearBindings);
@@ -50,6 +51,11 @@ ControllerBindingWidget::ControllerBindingWidget(QWidget* parent, ControllerSett
ControllerBindingWidget::~ControllerBindingWidget() = default;
QIcon ControllerBindingWidget::getIcon() const
{
return m_current_widget->getIcon();
}
void ControllerBindingWidget::populateControllerTypes()
{
for (const auto& [name, display_name] : PAD::GetControllerTypeNames())
@@ -58,15 +64,16 @@ void ControllerBindingWidget::populateControllerTypes()
void ControllerBindingWidget::onTypeChanged()
{
if (m_current_widget)
const bool is_initializing = (m_current_widget == nullptr);
m_controller_type = m_dialog->getStringValue(m_config_section.c_str(), "Type", PAD::GetDefaultPadType(m_port_number));
if (!is_initializing)
{
m_ui.verticalLayout->removeWidget(m_current_widget);
delete m_current_widget;
m_current_widget = nullptr;
}
m_controller_type = Host::GetBaseStringSettingValue(m_config_section.c_str(), "Type");
const int index = m_ui.controllerType->findData(QString::fromStdString(m_controller_type));
if (index >= 0 && index != m_ui.controllerType->currentIndex())
{
@@ -80,6 +87,10 @@ void ControllerBindingWidget::onTypeChanged()
m_current_widget = new ControllerBindingWidget_Base(this);
m_ui.verticalLayout->addWidget(m_current_widget, 1);
// no need to do this on first init, only changes
if (!is_initializing)
m_dialog->updateListDescription(m_port_number, this);
}
void ControllerBindingWidget::doAutomaticBinding()
@@ -110,15 +121,20 @@ void ControllerBindingWidget::doAutomaticBinding()
void ControllerBindingWidget::doClearBindings()
{
if (QMessageBox::question(QtUtils::GetRootWidget(this), tr("Clear Bindings"),
tr("Are you sure you want to clear all bindings for this controller? This action cannot be undone.")) != QMessageBox::Yes)
tr("Are you sure you want to clear all bindings for this controller? This action cannot be undone.")) != QMessageBox::Yes)
{
return;
}
if (m_dialog->isEditingGlobalSettings())
{
auto lock = Host::GetSettingsLock();
PAD::ClearPortBindings(*Host::Internal::GetBaseSettingsLayer(), m_port_number);
}
else
{
PAD::ClearPortBindings(*m_dialog->getProfileSettingsInterface(), m_port_number);
}
saveAndRefresh();
}
@@ -134,10 +150,17 @@ void ControllerBindingWidget::doDeviceAutomaticBinding(const QString& device)
}
bool result;
if (m_dialog->isEditingGlobalSettings())
{
auto lock = Host::GetSettingsLock();
result = PAD::MapController(*Host::Internal::GetBaseSettingsLayer(), m_port_number, mapping);
}
else
{
result = PAD::MapController(*m_dialog->getProfileSettingsInterface(), m_port_number, mapping);
m_dialog->getProfileSettingsInterface()->Save();
g_emu_thread->reloadInputBindings();
}
// force a refresh after mapping
if (result)
@@ -162,11 +185,17 @@ ControllerBindingWidget_Base::~ControllerBindingWidget_Base()
{
}
QIcon ControllerBindingWidget_Base::getIcon() const
{
return QIcon::fromTheme("artboard-2-line");
}
void ControllerBindingWidget_Base::initBindingWidgets()
{
const std::string& type = getControllerType();
const std::string& config_section = getConfigSection();
std::vector<std::string> bindings(PAD::GetControllerBinds(type));
SettingsInterface* sif = getDialog()->getProfileSettingsInterface();
for (std::string& binding : bindings)
{
@@ -178,7 +207,7 @@ void ControllerBindingWidget_Base::initBindingWidgets()
continue;
}
widget->setKey(config_section, std::move(binding));
widget->initialize(sif, config_section, std::move(binding));
}
const PAD::VibrationCapabilities vibe_caps = PAD::GetControllerVibrationCapabilities(type);
@@ -209,6 +238,21 @@ void ControllerBindingWidget_Base::initBindingWidgets()
break;
}
if (QSlider* widget = findChild<QSlider*>(QStringLiteral("Deadzone")); widget)
{
const float range = static_cast<float>(widget->maximum());
QLabel* label = findChild<QLabel*>(QStringLiteral("DeadzoneLabel"));
if (label)
{
connect(widget, &QSlider::valueChanged, this, [range, label](int value) {
label->setText(tr("%1%").arg((static_cast<float>(value) / range) * 100.0f, 0, 'f', 0));
});
}
ControllerSettingWidgetBinder::BindWidgetToInputProfileNormalized(sif, widget, config_section, "Deadzone", range,
PAD::DEFAULT_STICK_DEADZONE);
}
if (QSlider* widget = findChild<QSlider*>(QStringLiteral("AxisScale")); widget)
{
// position 1.0f at the halfway point
@@ -217,17 +261,18 @@ void ControllerBindingWidget_Base::initBindingWidgets()
if (label)
{
connect(widget, &QSlider::valueChanged, this, [range, label](int value) {
label->setText(tr("%1x").arg(static_cast<float>(value) / range, 0, 'f', 2));
label->setText(tr("%1%").arg((static_cast<float>(value) / range) * 100.0f, 0, 'f', 0));
});
}
SettingWidgetBinder::BindWidgetToNormalizedSetting(nullptr, widget, config_section, "AxisScale", range, 1.0f);
ControllerSettingWidgetBinder::BindWidgetToInputProfileNormalized(sif, widget, config_section, "AxisScale", range,
PAD::DEFAULT_STICK_SCALE);
}
if (QDoubleSpinBox* widget = findChild<QDoubleSpinBox*>(QStringLiteral("SmallMotorScale")); widget)
SettingWidgetBinder::BindWidgetToFloatSetting(nullptr, widget, config_section, "SmallMotorScale", 1.0f);
ControllerSettingWidgetBinder::BindWidgetToInputProfileFloat(sif, widget, config_section, "SmallMotorScale", 1.0f);
if (QDoubleSpinBox* widget = findChild<QDoubleSpinBox*>(QStringLiteral("LargeMotorScale")); widget)
SettingWidgetBinder::BindWidgetToFloatSetting(nullptr, widget, config_section, "LargeMotorScale", 1.0f);
ControllerSettingWidgetBinder::BindWidgetToInputProfileFloat(sif, widget, config_section, "LargeMotorScale", 1.0f);
}
ControllerBindingWidget_DualShock2::ControllerBindingWidget_DualShock2(ControllerBindingWidget* parent)
@@ -241,6 +286,11 @@ ControllerBindingWidget_DualShock2::~ControllerBindingWidget_DualShock2()
{
}
QIcon ControllerBindingWidget_DualShock2::getIcon() const
{
return QIcon::fromTheme("gamepad-line");
}
ControllerBindingWidget_Base* ControllerBindingWidget_DualShock2::createInstance(ControllerBindingWidget* parent)
{
return new ControllerBindingWidget_DualShock2(parent);
@@ -32,6 +32,8 @@ public:
ControllerBindingWidget(QWidget* parent, ControllerSettingsDialog* dialog, u32 port);
~ControllerBindingWidget();
QIcon getIcon() const;
__fi ControllerSettingsDialog* getDialog() const { return m_dialog; }
__fi const std::string& getConfigSection() const { return m_config_section; }
__fi const std::string& getControllerType() const { return m_controller_type; }
@@ -71,6 +73,8 @@ public:
__fi const std::string& getControllerType() const { return static_cast<ControllerBindingWidget*>(parent())->getControllerType(); }
__fi u32 getPortNumber() const { return static_cast<ControllerBindingWidget*>(parent())->getPortNumber(); }
virtual QIcon getIcon() const;
protected:
void initBindingWidgets();
};
@@ -83,6 +87,8 @@ public:
ControllerBindingWidget_DualShock2(ControllerBindingWidget* parent);
~ControllerBindingWidget_DualShock2();
QIcon getIcon() const override;
static ControllerBindingWidget_Base* createInstance(ControllerBindingWidget* parent);
private:
@@ -18,19 +18,53 @@
#include "Frontend/InputManager.h"
#include "Settings/ControllerGlobalSettingsWidget.h"
#include "Settings/ControllerSettingsDialog.h"
#include "Settings/ControllerSettingWidgetBinder.h"
#include "QtUtils.h"
#include "SettingWidgetBinder.h"
ControllerGlobalSettingsWidget::ControllerGlobalSettingsWidget(QWidget* parent, ControllerSettingsDialog* dialog)
: QWidget(parent)
, m_dialog(dialog)
{
m_ui.setupUi(this);
SettingWidgetBinder::BindWidgetToBoolSetting(nullptr, m_ui.enableSDLSource, "InputSources", "SDL", true);
SettingWidgetBinder::BindWidgetToBoolSetting(nullptr, m_ui.enableSDLEnhancedMode, "InputSources", "SDLControllerEnhancedMode", false);
SettingWidgetBinder::BindWidgetToBoolSetting(nullptr, m_ui.enableXInputSource, "InputSources", "XInput", false);
SettingsInterface* sif = dialog->getProfileSettingsInterface();
SettingWidgetBinder::BindWidgetToBoolSetting(sif, m_ui.enableSDLSource, "InputSources", "SDL", true);
SettingWidgetBinder::BindWidgetToBoolSetting(sif, m_ui.enableSDLEnhancedMode, "InputSources", "SDLControllerEnhancedMode", false);
SettingWidgetBinder::BindWidgetToBoolSetting(sif, m_ui.enableXInputSource, "InputSources", "XInput", false);
ControllerSettingWidgetBinder::BindWidgetToInputProfileBool(sif, m_ui.multitapPort1, "Pad", "MultitapPort1", false);
ControllerSettingWidgetBinder::BindWidgetToInputProfileBool(sif, m_ui.multitapPort2, "Pad", "MultitapPort2", false);
ControllerSettingWidgetBinder::BindWidgetToInputProfileBool(sif, m_ui.pointerXInvert, "Pad", "PointerXInvert", false);
ControllerSettingWidgetBinder::BindWidgetToInputProfileBool(sif, m_ui.pointerYInvert, "Pad", "PointerYInvert", false);
ControllerSettingWidgetBinder::BindWidgetToInputProfileFloat(sif, m_ui.pointerXScale, "Pad", "PointerXScale", 8.0f);
ControllerSettingWidgetBinder::BindWidgetToInputProfileFloat(sif, m_ui.pointerYScale, "Pad", "PointerYScale", 8.0f);
if (dialog->isEditingProfile())
{
m_ui.useProfileHotkeyBindings->setChecked(m_dialog->getBoolValue("Pad", "UseProfileHotkeyBindings", false));
connect(m_ui.useProfileHotkeyBindings, &QCheckBox::stateChanged, this, [this](int new_state) {
m_dialog->setBoolValue("Pad", "UseProfileHotkeyBindings", (new_state == Qt::Checked));
emit bindingSetupChanged();
});
}
else
{
// remove profile options from the UI.
m_ui.mainLayout->removeWidget(m_ui.profileSettings);
m_ui.profileSettings->deleteLater();
m_ui.profileSettings = nullptr;
}
connect(m_ui.enableSDLSource, &QCheckBox::stateChanged, this, &ControllerGlobalSettingsWidget::updateSDLOptionsEnabled);
for (QCheckBox* cb : {m_ui.multitapPort1, m_ui.multitapPort2})
connect(cb, &QCheckBox::stateChanged, this, [this]() { emit bindingSetupChanged(); });
connect(m_ui.pointerXScale, &QSlider::valueChanged, this, [this](int value) { m_ui.pointerXScaleLabel->setText(QStringLiteral("%1").arg(value)); });
connect(m_ui.pointerYScale, &QSlider::valueChanged, this, [this](int value) { m_ui.pointerYScaleLabel->setText(QStringLiteral("%1").arg(value)); });
m_ui.pointerXScaleLabel->setText(QStringLiteral("%1").arg(m_ui.pointerXScale->value()));
m_ui.pointerYScaleLabel->setText(QStringLiteral("%1").arg(m_ui.pointerYScale->value()));
updateSDLOptionsEnabled();
}
@@ -35,8 +35,12 @@ public:
void addDeviceToList(const QString& identifier, const QString& name);
void removeDeviceFromList(const QString& identifier);
Q_SIGNALS:
void bindingSetupChanged();
private:
void updateSDLOptionsEnabled();
Ui::ControllerGlobalSettingsWidget m_ui;
ControllerSettingsDialog* m_dialog;
};
@@ -7,13 +7,13 @@
<x>0</x>
<y>0</y>
<width>900</width>
<height>500</height>
<height>675</height>
</rect>
</property>
<property name="windowTitle">
<string>Form</string>
</property>
<layout class="QHBoxLayout" name="horizontalLayout" stretch="1,0">
<layout class="QGridLayout" name="mainLayout" columnstretch="1,0">
<property name="leftMargin">
<number>0</number>
</property>
@@ -26,122 +26,273 @@
<property name="bottomMargin">
<number>0</number>
</property>
<item>
<layout class="QVBoxLayout" name="verticalLayout_4">
<item>
<widget class="QGroupBox" name="groupBox">
<property name="title">
<string>SDL Input Source</string>
</property>
<layout class="QVBoxLayout" name="verticalLayout">
<item>
<widget class="QLabel" name="label_2">
<property name="text">
<string>The SDL input source supports most controllers, and provides advanced functionality for DualShock 4 / DualSense pads in Bluetooth mode (Vibration / LED Control).</string>
</property>
<property name="wordWrap">
<bool>true</bool>
</property>
</widget>
</item>
<item>
<widget class="QCheckBox" name="enableSDLSource">
<property name="text">
<string>Enable SDL Input Source</string>
</property>
</widget>
</item>
<item>
<widget class="QCheckBox" name="enableSDLEnhancedMode">
<property name="text">
<string>DualShock 4 / DualSense Enhanced Mode</string>
</property>
</widget>
</item>
</layout>
</widget>
</item>
<item>
<widget class="QGroupBox" name="groupBox_2">
<property name="title">
<string>XInput Source</string>
</property>
<layout class="QVBoxLayout" name="verticalLayout_2">
<item>
<widget class="QLabel" name="label_3">
<property name="text">
<string>The XInput source provides support for XBox 360 / XBox One / XBox Series controllers, and third party controllers which implement the XInput protocol.</string>
</property>
<property name="wordWrap">
<bool>true</bool>
</property>
</widget>
</item>
<item>
<widget class="QCheckBox" name="enableXInputSource">
<property name="text">
<string>Enable XInput Input Source</string>
</property>
</widget>
</item>
</layout>
</widget>
</item>
<item>
<widget class="QGroupBox" name="groupBox_4">
<property name="title">
<string>Controller Multitap</string>
</property>
<layout class="QVBoxLayout" name="verticalLayout_3">
<item>
<widget class="QLabel" name="label">
<property name="text">
<string>The multitap enables up to 8 controllers to be connected to the console. Each multitap provides 4 ports. Multitap is not supported by all games. (NOT YET IMPLEMENTED)</string>
</property>
<property name="wordWrap">
<bool>true</bool>
</property>
</widget>
</item>
<item>
<widget class="QCheckBox" name="multitapPort1">
<property name="enabled">
<bool>false</bool>
</property>
<property name="text">
<string>Multitap on Console Port 1</string>
</property>
</widget>
</item>
<item>
<widget class="QCheckBox" name="multitapPort2">
<property name="enabled">
<bool>false</bool>
</property>
<property name="text">
<string>Multitap on Console Port 2</string>
</property>
</widget>
</item>
</layout>
</widget>
</item>
<item>
<spacer name="verticalSpacer">
<property name="orientation">
<enum>Qt::Vertical</enum>
</property>
<property name="sizeHint" stdset="0">
<size>
<width>20</width>
<height>40</height>
</size>
</property>
</spacer>
</item>
</layout>
<item row="0" column="0">
<widget class="QGroupBox" name="groupBox">
<property name="title">
<string>SDL Input Source</string>
</property>
<layout class="QGridLayout" name="gridLayout_2">
<item row="0" column="0" colspan="2">
<widget class="QLabel" name="label_2">
<property name="text">
<string>The SDL input source supports most controllers, and provides advanced functionality for DualShock 4 / DualSense pads in Bluetooth mode (Vibration / LED Control).</string>
</property>
<property name="wordWrap">
<bool>true</bool>
</property>
</widget>
</item>
<item row="1" column="0">
<widget class="QCheckBox" name="enableSDLSource">
<property name="text">
<string>Enable SDL Input Source</string>
</property>
</widget>
</item>
<item row="1" column="1">
<widget class="QCheckBox" name="enableSDLEnhancedMode">
<property name="text">
<string>DualShock 4 / DualSense Enhanced Mode</string>
</property>
</widget>
</item>
</layout>
</widget>
</item>
<item>
<item row="1" column="0">
<widget class="QGroupBox" name="groupBox_2">
<property name="title">
<string>XInput Source</string>
</property>
<layout class="QGridLayout" name="gridLayout_3">
<item row="0" column="0">
<widget class="QLabel" name="label_3">
<property name="text">
<string>The XInput source provides support for XBox 360 / XBox One / XBox Series controllers, and third party controllers which implement the XInput protocol.</string>
</property>
<property name="wordWrap">
<bool>true</bool>
</property>
</widget>
</item>
<item row="1" column="0">
<widget class="QCheckBox" name="enableXInputSource">
<property name="text">
<string>Enable XInput Input Source</string>
</property>
</widget>
</item>
</layout>
</widget>
</item>
<item row="2" column="0">
<widget class="QGroupBox" name="groupBox_5">
<property name="title">
<string>Mouse/Pointer Source</string>
</property>
<layout class="QGridLayout" name="gridLayout_6">
<item row="2" column="1">
<widget class="QSlider" name="pointerYScale">
<property name="sizePolicy">
<sizepolicy hsizetype="Expanding" vsizetype="Fixed">
<horstretch>0</horstretch>
<verstretch>0</verstretch>
</sizepolicy>
</property>
<property name="minimumSize">
<size>
<width>150</width>
<height>0</height>
</size>
</property>
<property name="minimum">
<number>1</number>
</property>
<property name="maximum">
<number>30</number>
</property>
<property name="orientation">
<enum>Qt::Horizontal</enum>
</property>
</widget>
</item>
<item row="0" column="0" colspan="4">
<widget class="QLabel" name="label_7">
<property name="text">
<string>Using raw input improves precision when you bind controller sticks to the mouse pointer. Also enables multiple mice to be used.</string>
</property>
<property name="wordWrap">
<bool>true</bool>
</property>
</widget>
</item>
<item row="2" column="3">
<widget class="QCheckBox" name="pointerYInvert">
<property name="text">
<string>Invert</string>
</property>
</widget>
</item>
<item row="1" column="2">
<widget class="QLabel" name="pointerXScaleLabel">
<property name="sizePolicy">
<sizepolicy hsizetype="Minimum" vsizetype="Preferred">
<horstretch>0</horstretch>
<verstretch>0</verstretch>
</sizepolicy>
</property>
<property name="minimumSize">
<size>
<width>20</width>
<height>0</height>
</size>
</property>
<property name="text">
<string>10</string>
</property>
</widget>
</item>
<item row="1" column="3">
<widget class="QCheckBox" name="pointerXInvert">
<property name="text">
<string>Invert</string>
</property>
</widget>
</item>
<item row="2" column="0">
<widget class="QLabel" name="label_6">
<property name="text">
<string>Vertical Sensitivity:</string>
</property>
</widget>
</item>
<item row="1" column="1">
<widget class="QSlider" name="pointerXScale">
<property name="sizePolicy">
<sizepolicy hsizetype="Expanding" vsizetype="Fixed">
<horstretch>0</horstretch>
<verstretch>0</verstretch>
</sizepolicy>
</property>
<property name="minimumSize">
<size>
<width>150</width>
<height>0</height>
</size>
</property>
<property name="minimum">
<number>1</number>
</property>
<property name="maximum">
<number>30</number>
</property>
<property name="orientation">
<enum>Qt::Horizontal</enum>
</property>
</widget>
</item>
<item row="2" column="2">
<widget class="QLabel" name="pointerYScaleLabel">
<property name="sizePolicy">
<sizepolicy hsizetype="Minimum" vsizetype="Preferred">
<horstretch>0</horstretch>
<verstretch>0</verstretch>
</sizepolicy>
</property>
<property name="minimumSize">
<size>
<width>20</width>
<height>0</height>
</size>
</property>
<property name="text">
<string>10</string>
</property>
</widget>
</item>
<item row="1" column="0">
<widget class="QLabel" name="label_5">
<property name="text">
<string>Horizontal Sensitivity:</string>
</property>
</widget>
</item>
</layout>
</widget>
</item>
<item row="3" column="0">
<widget class="QGroupBox" name="groupBox_4">
<property name="title">
<string>Controller Multitap</string>
</property>
<layout class="QGridLayout" name="gridLayout_4">
<item row="0" column="0" colspan="2">
<widget class="QLabel" name="label">
<property name="text">
<string>The multitap enables up to 8 controllers to be connected to the console. Each multitap provides 4 ports. Multitap is not supported by all games.</string>
</property>
<property name="wordWrap">
<bool>true</bool>
</property>
</widget>
</item>
<item row="1" column="0">
<widget class="QCheckBox" name="multitapPort1">
<property name="text">
<string>Multitap on Console Port 1</string>
</property>
</widget>
</item>
<item row="1" column="1">
<widget class="QCheckBox" name="multitapPort2">
<property name="text">
<string>Multitap on Console Port 2</string>
</property>
</widget>
</item>
</layout>
</widget>
</item>
<item row="4" column="0">
<widget class="QGroupBox" name="profileSettings">
<property name="title">
<string>Profile Settings</string>
</property>
<layout class="QGridLayout" name="gridLayout_5">
<item row="0" column="0">
<widget class="QLabel" name="label_4">
<property name="text">
<string>When this option is enabled, hotkeys can be set in this input profile, and will be used instead of the global hotkeys. By default, hotkeys are always shared between all profiles.</string>
</property>
<property name="wordWrap">
<bool>true</bool>
</property>
</widget>
</item>
<item row="1" column="0">
<widget class="QCheckBox" name="useProfileHotkeyBindings">
<property name="text">
<string>Use Per-Profile Hotkeys</string>
</property>
</widget>
</item>
</layout>
</widget>
</item>
<item row="5" column="0">
<spacer name="verticalSpacer">
<property name="orientation">
<enum>Qt::Vertical</enum>
</property>
<property name="sizeHint" stdset="0">
<size>
<width>20</width>
<height>45</height>
</size>
</property>
</spacer>
</item>
<item row="0" column="1" rowspan="6">
<widget class="QGroupBox" name="groupBox_3">
<property name="title">
<string>Detected Devices</string>
@@ -0,0 +1,177 @@
/* PCSX2 - PS2 Emulator for PCs
* Copyright (C) 2002-2022 PCSX2 Dev Team
*
* PCSX2 is free software: you can redistribute it and/or modify it under the terms
* of the GNU Lesser General Public License as published by the Free Software Found-
* ation, either version 3 of the License, or (at your option) any later version.
*
* PCSX2 is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;
* without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR
* PURPOSE. See the GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License along with PCSX2.
* If not, see <http://www.gnu.org/licenses/>.
*/
#pragma once
#include <optional>
#include <type_traits>
#include <QtCore/QtCore>
#include <QtGui/QAction>
#include <QtWidgets/QCheckBox>
#include <QtWidgets/QComboBox>
#include <QtWidgets/QDoubleSpinBox>
#include <QtWidgets/QLineEdit>
#include <QtWidgets/QSlider>
#include <QtWidgets/QSpinBox>
#include "pcsx2/HostSettings.h"
#include "EmuThread.h"
#include "QtHost.h"
#include "SettingWidgetBinder.h"
/// This nastyness is required because input profiles aren't overlaid settings like the rest of them, it's
/// input profile *or* global, not both.
namespace ControllerSettingWidgetBinder
{
/// Interface specific method of BindWidgetToBoolSetting().
template <typename WidgetType>
static void BindWidgetToInputProfileBool(SettingsInterface* sif, WidgetType* widget, std::string section, std::string key, bool default_value)
{
using Accessor = SettingWidgetBinder::SettingAccessor<WidgetType>;
if (sif)
{
const bool value = sif->GetBoolValue(section.c_str(), key.c_str(), default_value);
Accessor::setBoolValue(widget, value);
Accessor::connectValueChanged(widget, [sif, widget, section = std::move(section), key = std::move(key)]() {
const bool new_value = Accessor::getBoolValue(widget);
sif->SetBoolValue(section.c_str(), key.c_str(), new_value);
sif->Save();
g_emu_thread->reloadGameSettings();
});
}
else
{
const bool value = Host::GetBaseBoolSettingValue(section.c_str(), key.c_str(), default_value);
Accessor::setBoolValue(widget, value);
Accessor::connectValueChanged(widget, [widget, section = std::move(section), key = std::move(key)]() {
const bool new_value = Accessor::getBoolValue(widget);
QtHost::SetBaseBoolSettingValue(section.c_str(), key.c_str(), new_value);
g_emu_thread->applySettings();
});
}
}
/// Interface specific method of BindWidgetToFloatSetting().
template <typename WidgetType>
static void BindWidgetToInputProfileFloat(SettingsInterface* sif, WidgetType* widget, std::string section, std::string key, float default_value)
{
using Accessor = SettingWidgetBinder::SettingAccessor<WidgetType>;
if (sif)
{
const float value = sif->GetFloatValue(section.c_str(), key.c_str(), default_value);
Accessor::setFloatValue(widget, value);
Accessor::connectValueChanged(widget, [sif, widget, section = std::move(section), key = std::move(key)]() {
const float new_value = Accessor::getFloatValue(widget);
sif->SetFloatValue(section.c_str(), key.c_str(), new_value);
sif->Save();
g_emu_thread->reloadGameSettings();
});
}
else
{
const float value = Host::GetBaseFloatSettingValue(section.c_str(), key.c_str(), default_value);
Accessor::setFloatValue(widget, value);
Accessor::connectValueChanged(widget, [widget, section = std::move(section), key = std::move(key)]() {
const float new_value = Accessor::getFloatValue(widget);
QtHost::SetBaseFloatSettingValue(section.c_str(), key.c_str(), new_value);
g_emu_thread->applySettings();
});
}
}
/// Interface specific method of BindWidgetToNormalizedSetting().
template <typename WidgetType>
static void BindWidgetToInputProfileNormalized(
SettingsInterface* sif, WidgetType* widget, std::string section, std::string key, float range, float default_value)
{
using Accessor = SettingWidgetBinder::SettingAccessor<WidgetType>;
if (sif)
{
const float value = sif->GetFloatValue(section.c_str(), key.c_str(), default_value);
Accessor::setIntValue(widget, static_cast<int>(value * range));
Accessor::connectValueChanged(widget, [sif, widget, section = std::move(section), key = std::move(key), range]() {
const int new_value = Accessor::getIntValue(widget);
sif->SetFloatValue(section.c_str(), key.c_str(), static_cast<float>(new_value) / range);
sif->Save();
g_emu_thread->reloadGameSettings();
});
}
else
{
const float value = Host::GetBaseFloatSettingValue(section.c_str(), key.c_str(), default_value);
Accessor::setIntValue(widget, static_cast<int>(value * range));
Accessor::connectValueChanged(widget, [widget, section = std::move(section), key = std::move(key), range]() {
const float new_value = (static_cast<float>(Accessor::getIntValue(widget)) / range);
QtHost::SetBaseFloatSettingValue(section.c_str(), key.c_str(), new_value);
g_emu_thread->applySettings();
});
}
}
/// Interface specific method of BindWidgetToStringSetting().
template <typename WidgetType>
static void BindWidgetToInputProfileString(
SettingsInterface* sif, WidgetType* widget, std::string section, std::string key, std::string default_value = std::string())
{
using Accessor = SettingWidgetBinder::SettingAccessor<WidgetType>;
if (sif)
{
const QString value(QString::fromStdString(sif->GetStringValue(section.c_str(), key.c_str(), default_value.c_str())));
Accessor::setStringValue(widget, value);
Accessor::connectValueChanged(widget, [widget, sif, section = std::move(section), key = std::move(key)]() {
const QString new_value = Accessor::getStringValue(widget);
if (!new_value.isEmpty())
sif->SetStringValue(section.c_str(), key.c_str(), new_value.toUtf8().constData());
else
sif->DeleteValue(section.c_str(), key.c_str());
sif->Save();
g_emu_thread->reloadGameSettings();
});
}
else
{
const QString value(QString::fromStdString(Host::GetBaseStringSettingValue(section.c_str(), key.c_str(), default_value.c_str())));
Accessor::setStringValue(widget, value);
Accessor::connectValueChanged(widget, [widget, section = std::move(section), key = std::move(key)]() {
const QString new_value = Accessor::getStringValue(widget);
if (!new_value.isEmpty())
QtHost::SetBaseStringSettingValue(section.c_str(), key.c_str(), new_value.toUtf8().constData());
else
QtHost::RemoveBaseSettingValue(section.c_str(), key.c_str());
g_emu_thread->applySettings();
});
}
}
} // namespace ControllerSettingWidgetBinder
+346 -11
View File
@@ -22,9 +22,21 @@
#include "Settings/ControllerBindingWidgets.h"
#include "Settings/HotkeySettingsWidget.h"
#include "pcsx2/Frontend/INISettingsInterface.h"
#include "pcsx2/PAD/Host/PAD.h"
#include "pcsx2/Sio.h"
#include "pcsx2/VMManager.h"
#include "common/Assertions.h"
#include "common/FileSystem.h"
#include <array>
#include <QtWidgets/QInputDialog>
#include <QtWidgets/QMessageBox>
#include <QtWidgets/QTextEdit>
static constexpr const std::array<char, 4> s_mtap_slot_names = {{'A', 'B', 'C', 'D'}};
ControllerSettingsDialog::ControllerSettingsDialog(QWidget* parent /* = nullptr */)
: QDialog(parent)
{
@@ -32,21 +44,17 @@ ControllerSettingsDialog::ControllerSettingsDialog(QWidget* parent /* = nullptr
setWindowFlags(windowFlags() & ~Qt::WindowContextHelpButtonHint);
m_global_settings = new ControllerGlobalSettingsWidget(m_ui.settingsContainer, this);
m_ui.settingsContainer->insertWidget(0, m_global_settings);
for (u32 i = 0; i < MAX_PORTS; i++)
{
m_port_bindings[i] = new ControllerBindingWidget(m_ui.settingsContainer, this, i);
m_ui.settingsContainer->insertWidget(i + 1, m_port_bindings[i]);
}
m_hotkey_settings = new HotkeySettingsWidget(m_ui.settingsContainer, this);
m_ui.settingsContainer->insertWidget(3, m_hotkey_settings);
refreshProfileList();
createWidgets();
m_ui.settingsCategory->setSizePolicy(QSizePolicy::Minimum, QSizePolicy::Minimum);
connect(m_ui.settingsCategory, &QListWidget::currentRowChanged, this, &ControllerSettingsDialog::onCategoryCurrentRowChanged);
connect(m_ui.currentProfile, &QComboBox::currentIndexChanged, this, &ControllerSettingsDialog::onCurrentProfileChanged);
connect(m_ui.buttonBox, &QDialogButtonBox::rejected, this, &ControllerSettingsDialog::close);
connect(m_ui.newProfile, &QPushButton::clicked, this, &ControllerSettingsDialog::onNewProfileClicked);
connect(m_ui.loadProfile, &QPushButton::clicked, this, &ControllerSettingsDialog::onLoadProfileClicked);
connect(m_ui.deleteProfile, &QPushButton::clicked, this, &ControllerSettingsDialog::onDeleteProfileClicked);
connect(m_ui.restoreDefaults, &QPushButton::clicked, this, &ControllerSettingsDialog::onRestoreDefaultsClicked);
connect(g_emu_thread, &EmuThread::onInputDevicesEnumerated, this, &ControllerSettingsDialog::onInputDevicesEnumerated);
connect(g_emu_thread, &EmuThread::onInputDeviceConnected, this, &ControllerSettingsDialog::onInputDeviceConnected);
@@ -87,6 +95,123 @@ void ControllerSettingsDialog::onCategoryCurrentRowChanged(int row)
m_ui.settingsContainer->setCurrentIndex(row);
}
void ControllerSettingsDialog::onCurrentProfileChanged(int index)
{
switchProfile((index == 0) ? 0 : m_ui.currentProfile->itemText(index));
}
void ControllerSettingsDialog::onNewProfileClicked()
{
const QString profile_name(QInputDialog::getText(this, tr("Create Input Profile"), tr("Enter the name for the new input profile:")));
if (profile_name.isEmpty())
return;
std::string profile_path(VMManager::GetInputProfilePath(profile_name.toStdString()));
if (FileSystem::FileExists(profile_path.c_str()))
{
QMessageBox::critical(this, tr("Error"), tr("A profile with the name '%1' already exists.").arg(profile_name));
return;
}
const int res = QMessageBox::question(this, tr("Create Input Profile"),
tr("Do you want to copy all bindings from the currently-selected profile to the new profile? Selecting No will create a completely empty profile."),
QMessageBox::Yes | QMessageBox::No | QMessageBox::Cancel);
if (res == QMessageBox::Cancel)
return;
INISettingsInterface temp_si(std::move(profile_path));
if (res == QMessageBox::Yes)
{
// copy from global or the current profile
if (!m_profile_interface)
{
// from global
auto lock = Host::GetSettingsLock();
PAD::CopyConfiguration(&temp_si, *Host::Internal::GetBaseSettingsLayer(), true, true, false);
}
else
{
// from profile
const bool copy_hotkey_bindings = m_profile_interface->GetBoolValue("Pad", "UseProfileHotkeyBindings", false);
temp_si.SetBoolValue("Pad", "UseProfileHotkeyBindings", copy_hotkey_bindings);
PAD::CopyConfiguration(&temp_si, *m_profile_interface, true, true, copy_hotkey_bindings);
}
}
if (!temp_si.Save())
{
QMessageBox::critical(this, tr("Error"), tr("Failed to save the new profile to '%1'.").arg(QString::fromStdString(temp_si.GetFileName())));
return;
}
refreshProfileList();
switchProfile(profile_name);
}
void ControllerSettingsDialog::onLoadProfileClicked()
{
if (QMessageBox::question(this, tr("Load Input Profile"),
tr("Are you sure you want to load the input profile named '%1'?\n\n"
"All current global bindings will be removed, and the profile bindings loaded.\n\n"
"You cannot undo this action.")
.arg(m_profile_name)) != QMessageBox::Yes)
{
return;
}
{
auto lock = Host::GetSettingsLock();
PAD::CopyConfiguration(Host::Internal::GetBaseSettingsLayer(), *m_profile_interface, true, true, false);
QtHost::QueueSettingsSave();
}
// make it visible
switchProfile({});
}
void ControllerSettingsDialog::onDeleteProfileClicked()
{
if (QMessageBox::question(this, tr("Delete Input Profile"),
tr("Are you sure you want to delete the input profile named '%1'?\n\n"
"You cannot undo this action.")
.arg(m_profile_name)) != QMessageBox::Yes)
{
return;
}
std::string profile_path(VMManager::GetInputProfilePath(m_profile_name.toStdString()));
if (!FileSystem::DeleteFilePath(profile_path.c_str()))
{
QMessageBox::critical(this, tr("Error"), tr("Failed to delete '%1'.").arg(QString::fromStdString(profile_path)));
return;
}
// switch back to global
refreshProfileList();
switchProfile({});
}
void ControllerSettingsDialog::onRestoreDefaultsClicked()
{
if (QMessageBox::question(this, tr("Restore Defaults"),
tr("Are you sure you want to restore the default controller configuration?\n\n"
"All shared bindings and configuration will be lost, but your input profiles will remain.\n\n"
"You cannot undo this action.")) != QMessageBox::Yes)
{
return;
}
// actually restore it
{
auto lock = Host::GetSettingsLock();
PAD::SetDefaultConfig(*Host::Internal::GetBaseSettingsLayer());
QtHost::QueueSettingsSave();
}
// reload all settings
switchProfile({});
}
void ControllerSettingsDialog::onInputDevicesEnumerated(const QList<QPair<QString, QString>>& devices)
{
m_device_list = devices;
@@ -128,3 +253,213 @@ void ControllerSettingsDialog::onVibrationMotorsEnumerated(const QList<InputBind
m_vibration_motors.push_back(QString::fromStdString(key_str));
}
}
bool ControllerSettingsDialog::getBoolValue(const char* section, const char* key, bool default_value) const
{
if (m_profile_interface)
return m_profile_interface->GetBoolValue(section, key, default_value);
else
return Host::GetBaseBoolSettingValue(section, key, default_value);
}
std::string ControllerSettingsDialog::getStringValue(const char* section, const char* key, const char* default_value) const
{
std::string value;
if (m_profile_interface)
value = m_profile_interface->GetStringValue(section, key, default_value);
else
value = Host::GetBaseStringSettingValue(section, key, default_value);
return value;
}
void ControllerSettingsDialog::setBoolValue(const char* section, const char* key, bool value)
{
if (m_profile_interface)
{
m_profile_interface->SetBoolValue(section, key, value);
m_profile_interface->Save();
g_emu_thread->reloadGameSettings();
}
else
{
QtHost::SetBaseBoolSettingValue(section, key, value);
g_emu_thread->applySettings();
}
}
void ControllerSettingsDialog::setStringValue(const char* section, const char* key, const char* value)
{
if (m_profile_interface)
{
m_profile_interface->SetStringValue(section, key, value);
m_profile_interface->Save();
g_emu_thread->reloadGameSettings();
}
else
{
QtHost::SetBaseStringSettingValue(key, section, value);
g_emu_thread->applySettings();
}
}
void ControllerSettingsDialog::clearSettingValue(const char* section, const char* key)
{
if (m_profile_interface)
{
m_profile_interface->DeleteValue(section, key);
m_profile_interface->Save();
g_emu_thread->reloadGameSettings();
}
else
{
QtHost::RemoveBaseSettingValue(section, key);
g_emu_thread->applySettings();
}
}
void ControllerSettingsDialog::createWidgets()
{
QSignalBlocker sb(m_ui.settingsContainer);
QSignalBlocker sb2(m_ui.settingsCategory);
while (m_ui.settingsContainer->count() > 0)
{
QWidget* widget = m_ui.settingsContainer->widget(m_ui.settingsContainer->count() - 1);
m_ui.settingsContainer->removeWidget(widget);
widget->deleteLater();
}
m_ui.settingsCategory->clear();
m_global_settings = nullptr;
m_hotkey_settings = nullptr;
{
// global settings
QListWidgetItem* item = new QListWidgetItem();
item->setText(tr("Global Settings"));
item->setIcon(QIcon::fromTheme("settings-3-line"));
m_ui.settingsCategory->addItem(item);
m_ui.settingsCategory->setCurrentRow(0);
m_global_settings = new ControllerGlobalSettingsWidget(m_ui.settingsContainer, this);
m_ui.settingsContainer->addWidget(m_global_settings);
connect(m_global_settings, &ControllerGlobalSettingsWidget::bindingSetupChanged, this, &ControllerSettingsDialog::createWidgets);
for (const QPair<QString, QString>& dev : m_device_list)
m_global_settings->addDeviceToList(dev.first, dev.second);
}
// load mtap settings
const std::array<bool, 2> mtap_enabled = {{getBoolValue("Pad", "MultitapPort1", false),
getBoolValue("Pad", "MultitapPort2", false)}};
// we reorder things a little to make it look less silly for mtap
static constexpr const std::array<u32, MAX_PORTS> mtap_port_order = {{0, 2, 3, 4, 1, 5, 6, 7}};
// create the ports
for (u32 global_slot : mtap_port_order)
{
const bool is_mtap_port = sioPadIsMultitapSlot(global_slot);
const auto [port, slot] = sioConvertPadToPortAndSlot(global_slot);
if (is_mtap_port && !mtap_enabled[port])
continue;
m_port_bindings[global_slot] = new ControllerBindingWidget(m_ui.settingsContainer, this, global_slot);
m_ui.settingsContainer->addWidget(m_port_bindings[global_slot]);
const PAD::ControllerInfo* ci = PAD::GetControllerInfo(m_port_bindings[global_slot]->getControllerType());
const QString display_name(ci ? QString::fromUtf8(ci->display_name) : QStringLiteral("Unknown"));
QListWidgetItem* item = new QListWidgetItem();
item->setText(mtap_enabled[port] ?
(tr("Controller Port %1%2\n%3").arg(port + 1).arg(s_mtap_slot_names[slot]).arg(display_name)) :
tr("Controller Port %1\n%2").arg(port + 1).arg(display_name));
item->setIcon(m_port_bindings[global_slot]->getIcon());
item->setData(Qt::UserRole, QVariant(global_slot));
m_ui.settingsCategory->addItem(item);
}
// only add hotkeys if we're editing global settings
if (!m_profile_interface || m_profile_interface->GetBoolValue("Pad", "UseProfileHotkeyBindings", false))
{
QListWidgetItem* item = new QListWidgetItem();
item->setText(tr("Hotkeys"));
item->setIcon(QIcon::fromTheme("keyboard-line"));
m_ui.settingsCategory->addItem(item);
m_hotkey_settings = new HotkeySettingsWidget(m_ui.settingsContainer, this);
m_ui.settingsContainer->addWidget(m_hotkey_settings);
}
m_ui.loadProfile->setEnabled(isEditingProfile());
m_ui.deleteProfile->setEnabled(isEditingProfile());
m_ui.restoreDefaults->setEnabled(isEditingGlobalSettings());
}
void ControllerSettingsDialog::updateListDescription(u32 global_slot, ControllerBindingWidget* widget)
{
for (int i = 0; i < m_ui.settingsCategory->count(); i++)
{
QListWidgetItem* item = m_ui.settingsCategory->item(i);
const QVariant data(item->data(Qt::UserRole));
if (data.type() == QVariant::UInt && data.toUInt() == global_slot)
{
const bool is_mtap_port = sioPadIsMultitapSlot(global_slot);
const auto [port, slot] = sioConvertPadToPortAndSlot(global_slot);
const bool mtap_enabled = getBoolValue("Pad", (port == 0) ? "MultitapPort1" : "MultitapPort2", false);
const PAD::ControllerInfo* ci = PAD::GetControllerInfo(widget->getControllerType());
const QString display_name(ci ? QString::fromUtf8(ci->display_name) : QStringLiteral("Unknown"));
item->setText(mtap_enabled ?
(tr("Controller Port %1%2\n%3").arg(port + 1).arg(s_mtap_slot_names[slot]).arg(display_name)) :
tr("Controller Port %1\n%2").arg(port + 1).arg(display_name));
item->setIcon(widget->getIcon());
break;
}
}
}
void ControllerSettingsDialog::refreshProfileList()
{
const std::vector<std::string> names(PAD::GetInputProfileNames());
QSignalBlocker sb(m_ui.currentProfile);
m_ui.currentProfile->clear();
m_ui.currentProfile->addItem(tr("Shared"));
if (isEditingGlobalSettings())
m_ui.currentProfile->setCurrentIndex(0);
for (const std::string& name : names)
{
const QString qname(QString::fromStdString(name));
m_ui.currentProfile->addItem(qname);
if (qname == m_profile_name)
m_ui.currentProfile->setCurrentIndex(m_ui.currentProfile->count() - 1);
}
}
void ControllerSettingsDialog::switchProfile(const QString& name)
{
QSignalBlocker sb(m_ui.currentProfile);
if (!name.isEmpty())
{
std::string path(VMManager::GetInputProfilePath(name.toStdString()));
if (!FileSystem::FileExists(path.c_str()))
{
QMessageBox::critical(this, tr("Error"), tr("The input profile named '%1' cannot be found.").arg(name));
return;
}
std::unique_ptr<INISettingsInterface> sif(std::make_unique<INISettingsInterface>(std::move(path)));
sif->Load();
m_profile_interface = std::move(sif);
m_ui.currentProfile->setCurrentIndex(m_ui.currentProfile->findText(name));
}
else
{
m_profile_interface.reset();
m_ui.currentProfile->setCurrentIndex(0);
}
m_profile_name = name;
createWidgets();
}
+36 -2
View File
@@ -22,11 +22,14 @@
#include <QtCore/QStringList>
#include <QtWidgets/QDialog>
#include <array>
#include <string>
class ControllerGlobalSettingsWidget;
class ControllerBindingWidget;
class HotkeySettingsWidget;
class SettingsInterface;
class ControllerSettingsDialog final : public QDialog
{
Q_OBJECT
@@ -42,29 +45,57 @@ public:
enum : u32
{
MAX_PORTS = 2
MAX_PORTS = 8
};
ControllerSettingsDialog(QWidget* parent = nullptr);
~ControllerSettingsDialog();
HotkeySettingsWidget* getHotkeySettingsWidget() const { return m_hotkey_settings; }
__fi HotkeySettingsWidget* getHotkeySettingsWidget() const { return m_hotkey_settings; }
__fi const QList<QPair<QString, QString>>& getDeviceList() const { return m_device_list; }
__fi const QStringList& getVibrationMotors() const { return m_vibration_motors; }
__fi bool isEditingGlobalSettings() const { return m_profile_name.isEmpty(); }
__fi bool isEditingProfile() const { return !m_profile_name.isEmpty(); }
__fi SettingsInterface* getProfileSettingsInterface() { return m_profile_interface.get(); }
void updateListDescription(u32 global_slot, ControllerBindingWidget* widget);
// Helper functions for updating setting values globally or in the profile.
bool getBoolValue(const char* section, const char* key, bool default_value) const;
std::string getStringValue(const char* section, const char* key, const char* default_value) const;
void setBoolValue(const char* section, const char* key, bool value);
void setStringValue(const char* section, const char* key, const char* value);
void clearSettingValue(const char* section, const char* key);
Q_SIGNALS:
void inputProfileSwitched();
public Q_SLOTS:
void setCategory(Category category);
private Q_SLOTS:
void onCategoryCurrentRowChanged(int row);
void onCurrentProfileChanged(int index);
void onNewProfileClicked();
void onLoadProfileClicked();
void onDeleteProfileClicked();
void onRestoreDefaultsClicked();
void onInputDevicesEnumerated(const QList<QPair<QString, QString>>& devices);
void onInputDeviceConnected(const QString& identifier, const QString& device_name);
void onInputDeviceDisconnected(const QString& identifier);
void onVibrationMotorsEnumerated(const QList<InputBindingKey>& motors);
void createWidgets();
private:
static QIcon getIconForType(const std::string& type);
void refreshProfileList();
void switchProfile(const QString& name);
Ui::ControllerSettingsDialog m_ui;
ControllerGlobalSettingsWidget* m_global_settings = nullptr;
@@ -73,4 +104,7 @@ private:
QList<QPair<QString, QString>> m_device_list;
QStringList m_vibration_motors;
QString m_profile_name;
std::unique_ptr<SettingsInterface> m_profile_interface;
};
+68 -53
View File
@@ -9,8 +9,8 @@
<rect>
<x>0</x>
<y>0</y>
<width>1274</width>
<height>668</height>
<width>1276</width>
<height>672</height>
</rect>
</property>
<property name="sizePolicy">
@@ -22,7 +22,7 @@
<property name="windowTitle">
<string>PCSX2 Controller Settings</string>
</property>
<layout class="QGridLayout" name="gridLayout" columnstretch="0,0">
<layout class="QGridLayout" name="gridLayout">
<item row="0" column="0">
<widget class="QListWidget" name="settingsCategory">
<property name="sizePolicy">
@@ -49,42 +49,6 @@
<height>32</height>
</size>
</property>
<item>
<property name="text">
<string>Global Settings</string>
</property>
<property name="icon">
<iconset theme="settings-3-line">
<normaloff>.</normaloff>.</iconset>
</property>
</item>
<item>
<property name="text">
<string>Controller 1</string>
</property>
<property name="icon">
<iconset theme="gamepad-line">
<normaloff>.</normaloff>.</iconset>
</property>
</item>
<item>
<property name="text">
<string>Controller 2</string>
</property>
<property name="icon">
<iconset theme="gamepad-line">
<normaloff>.</normaloff>.</iconset>
</property>
</item>
<item>
<property name="text">
<string>Hotkeys</string>
</property>
<property name="icon">
<iconset theme="keyboard-line">
<normaloff>.</normaloff>.</iconset>
</property>
</item>
</widget>
</item>
<item row="0" column="1">
@@ -95,23 +59,74 @@
<height>620</height>
</size>
</property>
<property name="currentIndex">
<number>4</number>
</property>
<widget class="QWidget" name="page"/>
<widget class="QWidget" name="page_3"/>
<widget class="QWidget" name="page_4"/>
<widget class="QWidget" name="page_5"/>
<widget class="QWidget" name="page_6"/>
<widget class="QWidget" name="page_2"/>
</widget>
</item>
<item row="1" column="0" colspan="2">
<widget class="QDialogButtonBox" name="buttonBox">
<property name="standardButtons">
<set>QDialogButtonBox::Close</set>
</property>
</widget>
<layout class="QHBoxLayout" name="horizontalLayout_2">
<item>
<layout class="QHBoxLayout" name="horizontalLayout">
<item>
<widget class="QLabel" name="label">
<property name="text">
<string>Profile:</string>
</property>
</widget>
</item>
<item>
<widget class="QComboBox" name="currentProfile"/>
</item>
<item>
<widget class="QPushButton" name="newProfile">
<property name="text">
<string>New Profile</string>
</property>
<property name="icon">
<iconset theme="file-add-line">
<normaloff>.</normaloff>.</iconset>
</property>
</widget>
</item>
<item>
<widget class="QPushButton" name="loadProfile">
<property name="text">
<string>Load Profile</string>
</property>
<property name="icon">
<iconset theme="folder-open-line"/>
</property>
</widget>
</item>
<item>
<widget class="QPushButton" name="deleteProfile">
<property name="text">
<string>Delete Profile</string>
</property>
<property name="icon">
<iconset theme="file-reduce-line">
<normaloff>.</normaloff>.</iconset>
</property>
</widget>
</item>
<item>
<widget class="QPushButton" name="restoreDefaults">
<property name="text">
<string>Restore Defaults</string>
</property>
<property name="icon">
<iconset theme="restart-line"/>
</property>
</widget>
</item>
</layout>
</item>
<item>
<widget class="QDialogButtonBox" name="buttonBox">
<property name="standardButtons">
<set>QDialogButtonBox::Close</set>
</property>
</widget>
</item>
</layout>
</item>
</layout>
</widget>
+19 -11
View File
@@ -60,20 +60,16 @@ CreateMemoryCardDialog::~CreateMemoryCardDialog() = default;
void CreateMemoryCardDialog::nameTextChanged()
{
const QString controlName(m_ui.name->text());
QString controlName(m_ui.name->text());
const int cursorPos = m_ui.name->cursorPosition();
QString nameWithoutExtension;
if (controlName.endsWith(QStringLiteral(".ps2")))
nameWithoutExtension = controlName.left(controlName.size() - 4);
else
nameWithoutExtension = controlName;
controlName.replace(".", "");
QSignalBlocker sb(m_ui.name);
if (nameWithoutExtension.isEmpty())
if (controlName.isEmpty())
m_ui.name->setText(QString());
else
m_ui.name->setText(nameWithoutExtension + QStringLiteral(".ps2"));
m_ui.name->setText(controlName);
m_ui.name->setCursorPosition(cursorPos);
updateState();
@@ -99,7 +95,7 @@ void CreateMemoryCardDialog::restoreDefaults()
void CreateMemoryCardDialog::updateState()
{
const bool okay = (m_ui.name->text().length() > 4);
const bool okay = (m_ui.name->text().length() > 0);
m_ui.buttonBox->button(QDialogButtonBox::Ok)->setEnabled(okay);
#ifdef _WIN32
@@ -109,8 +105,20 @@ void CreateMemoryCardDialog::updateState()
void CreateMemoryCardDialog::createCard()
{
const QString name(m_ui.name->text());
const std::string nameStr(name.toStdString());
QString name(m_ui.name->text());
std::string nameStr;
if (m_fileType == MemoryCardFileType::PS1)
{
name += QStringLiteral(".mcr");
}
else
{
name += QStringLiteral(".ps2");
}
nameStr = name.toStdString();
if (FileMcd_GetCardInfo(nameStr).has_value())
{
QMessageBox::critical(this, tr("Create Memory Card"),
@@ -0,0 +1,38 @@
/* PCSX2 - PS2 Emulator for PCs
* Copyright (C) 2002-2022 PCSX2 Dev Team
*
* PCSX2 is free software: you can redistribute it and/or modify it under the terms
* of the GNU Lesser General Public License as published by the Free Software Found-
* ation, either version 3 of the License, or (at your option) any later version.
*
* PCSX2 is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;
* without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR
* PURPOSE. See the GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License along with PCSX2.
* If not, see <http://www.gnu.org/licenses/>.
*/
#include "PrecompiledHeader.h"
#include <QtWidgets/QMessageBox>
#include <algorithm>
#include "FolderSettingsWidget.h"
#include "SettingWidgetBinder.h"
#include "SettingsDialog.h"
FolderSettingsWidget::FolderSettingsWidget(SettingsDialog* dialog, QWidget* parent)
: QWidget(parent)
{
SettingsInterface* sif = dialog->getSettingsInterface();
m_ui.setupUi(this);
SettingWidgetBinder::BindWidgetToFolderSetting(sif, m_ui.cache, m_ui.cacheBrowse, m_ui.cacheOpen, m_ui.cacheReset, "Folders", "Cache", "cache");
SettingWidgetBinder::BindWidgetToFolderSetting(sif, m_ui.covers, m_ui.coversBrowse, m_ui.coversOpen, m_ui.coversReset, "Folders", "Covers", "covers");
SettingWidgetBinder::BindWidgetToFolderSetting(sif, m_ui.snapshots, m_ui.snapshotsBrowse, m_ui.snapshotsOpen, m_ui.snapshotsReset, "Folders", "Snapshots", "snaps");
SettingWidgetBinder::BindWidgetToFolderSetting(sif, m_ui.saveStates, m_ui.saveStatesBrowse, m_ui.saveStatesOpen, m_ui.saveStatesReset, "Folders", "SaveStates", "sstates");
}
FolderSettingsWidget::~FolderSettingsWidget() = default;
+34
View File
@@ -0,0 +1,34 @@
/* PCSX2 - PS2 Emulator for PCs
* Copyright (C) 2002-2022 PCSX2 Dev Team
*
* PCSX2 is free software: you can redistribute it and/or modify it under the terms
* of the GNU Lesser General Public License as published by the Free Software Found-
* ation, either version 3 of the License, or (at your option) any later version.
*
* PCSX2 is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;
* without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR
* PURPOSE. See the GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License along with PCSX2.
* If not, see <http://www.gnu.org/licenses/>.
*/
#pragma once
#include <QtWidgets/QWidget>
#include "ui_FolderSettingsWidget.h"
class SettingsDialog;
class FolderSettingsWidget : public QWidget
{
Q_OBJECT
public:
FolderSettingsWidget(SettingsDialog* dialog, QWidget* parent);
~FolderSettingsWidget();
private:
Ui::FolderSettingsWidget m_ui;
};
+208
View File
@@ -0,0 +1,208 @@
<?xml version="1.0" encoding="UTF-8"?>
<ui version="4.0">
<class>FolderSettingsWidget</class>
<widget class="QWidget" name="FolderSettingsWidget">
<property name="geometry">
<rect>
<x>0</x>
<y>0</y>
<width>648</width>
<height>487</height>
</rect>
</property>
<property name="windowTitle">
<string>Form</string>
</property>
<layout class="QVBoxLayout" name="verticalLayout">
<property name="leftMargin">
<number>0</number>
</property>
<property name="topMargin">
<number>0</number>
</property>
<property name="rightMargin">
<number>0</number>
</property>
<property name="bottomMargin">
<number>0</number>
</property>
<item>
<widget class="QGroupBox" name="groupBox_3">
<property name="title">
<string>Cache Directory</string>
</property>
<layout class="QGridLayout" name="gridLayout_3">
<item row="1" column="0">
<widget class="QLineEdit" name="cache"/>
</item>
<item row="1" column="1">
<widget class="QPushButton" name="cacheBrowse">
<property name="text">
<string>Browse...</string>
</property>
</widget>
</item>
<item row="1" column="2">
<widget class="QPushButton" name="cacheOpen">
<property name="text">
<string>Open...</string>
</property>
</widget>
</item>
<item row="1" column="3">
<widget class="QPushButton" name="cacheReset">
<property name="text">
<string>Reset</string>
</property>
</widget>
</item>
<item row="0" column="0" colspan="4">
<widget class="QLabel" name="label_3">
<property name="text">
<string>Used for storing shaders, gzip indices, and game list data.</string>
</property>
</widget>
</item>
</layout>
</widget>
</item>
<item>
<widget class="QGroupBox" name="groupBox_4">
<property name="title">
<string>Covers Directory</string>
</property>
<layout class="QGridLayout" name="gridLayout_4">
<item row="1" column="0">
<widget class="QLineEdit" name="covers"/>
</item>
<item row="1" column="1">
<widget class="QPushButton" name="coversBrowse">
<property name="text">
<string>Browse...</string>
</property>
</widget>
</item>
<item row="1" column="2">
<widget class="QPushButton" name="coversOpen">
<property name="text">
<string>Open...</string>
</property>
</widget>
</item>
<item row="1" column="3">
<widget class="QPushButton" name="coversReset">
<property name="text">
<string>Reset</string>
</property>
</widget>
</item>
<item row="0" column="0" colspan="4">
<widget class="QLabel" name="label_4">
<property name="text">
<string>Used for storing covers in the game grid/Big Picture UIs.</string>
</property>
</widget>
</item>
</layout>
</widget>
</item>
<item>
<widget class="QGroupBox" name="groupBox">
<property name="title">
<string>Snapshots Directory</string>
</property>
<layout class="QGridLayout" name="gridLayout">
<item row="1" column="0">
<widget class="QLineEdit" name="snapshots"/>
</item>
<item row="1" column="1">
<widget class="QPushButton" name="snapshotsBrowse">
<property name="text">
<string>Browse...</string>
</property>
</widget>
</item>
<item row="1" column="2">
<widget class="QPushButton" name="snapshotsOpen">
<property name="text">
<string>Open...</string>
</property>
</widget>
</item>
<item row="1" column="3">
<widget class="QPushButton" name="snapshotsReset">
<property name="text">
<string>Reset</string>
</property>
</widget>
</item>
<item row="0" column="0" colspan="4">
<widget class="QLabel" name="label">
<property name="text">
<string>Used for screenshots and saving GS dumps.</string>
</property>
</widget>
</item>
</layout>
</widget>
</item>
<item>
<widget class="QGroupBox" name="groupBox_2">
<property name="title">
<string>Save States Directory</string>
</property>
<layout class="QGridLayout" name="gridLayout_2">
<item row="1" column="0">
<widget class="QLineEdit" name="saveStates"/>
</item>
<item row="1" column="1">
<widget class="QPushButton" name="saveStatesBrowse">
<property name="text">
<string>Browse...</string>
</property>
</widget>
</item>
<item row="1" column="2">
<widget class="QPushButton" name="saveStatesOpen">
<property name="text">
<string>Open...</string>
</property>
</widget>
</item>
<item row="1" column="3">
<widget class="QPushButton" name="saveStatesReset">
<property name="text">
<string>Reset</string>
</property>
</widget>
</item>
<item row="0" column="0" colspan="4">
<widget class="QLabel" name="label_2">
<property name="text">
<string>Used for storing save states.</string>
</property>
</widget>
</item>
</layout>
</widget>
</item>
<item>
<spacer name="verticalSpacer">
<property name="orientation">
<enum>Qt::Vertical</enum>
</property>
<property name="sizeHint" stdset="0">
<size>
<width>20</width>
<height>132</height>
</size>
</property>
</spacer>
</item>
</layout>
</widget>
<resources>
<include location="../resources/resources.qrc"/>
</resources>
<connections/>
</ui>
@@ -47,6 +47,7 @@ GameFixSettingsWidget::GameFixSettingsWidget(SettingsDialog* dialog, QWidget* pa
SettingWidgetBinder::BindWidgetToBoolSetting(sif, m_ui.VUSyncHack, "EmuCore/Gamefixes", "VUSyncHack", false);
SettingWidgetBinder::BindWidgetToBoolSetting(sif, m_ui.VUOverflowHack, "EmuCore/Gamefixes", "VUOverflowHack", false);
SettingWidgetBinder::BindWidgetToBoolSetting(sif, m_ui.XgKickHack, "EmuCore/Gamefixes", "XgKickHack", false);
SettingWidgetBinder::BindWidgetToBoolSetting(sif, m_ui.BlitInternalFPSHack, "EmuCore/Gamefixes", "BlitInternalFPSHack", false);
}
GameFixSettingsWidget::~GameFixSettingsWidget() = default;
@@ -141,6 +141,13 @@
</property>
</widget>
</item>
<item>
<widget class="QCheckBox" name="BlitInternalFPSHack">
<property name="text">
<string>Force Blit Internal FPS Detection (When auto-detection fails)</string>
</property>
</widget>
</item>
</layout>
</widget>
</item>
+22
View File
@@ -18,11 +18,14 @@
#include "common/StringUtil.h"
#include "Frontend/GameList.h"
#include "PAD/Host/PAD.h"
#include "GameSummaryWidget.h"
#include "SettingsDialog.h"
#include "QtHost.h"
GameSummaryWidget::GameSummaryWidget(const GameList::Entry* entry, SettingsDialog* dialog, QWidget* parent)
: m_dialog(dialog)
{
m_ui.setupUi(this);
@@ -39,6 +42,8 @@ GameSummaryWidget::GameSummaryWidget(const GameList::Entry* entry, SettingsDialo
}
populateUi(entry);
connect(m_ui.inputProfile, &QComboBox::currentIndexChanged, this, &GameSummaryWidget::onInputProfileChanged);
}
GameSummaryWidget::~GameSummaryWidget() = default;
@@ -52,4 +57,21 @@ void GameSummaryWidget::populateUi(const GameList::Entry* entry)
m_ui.type->setCurrentIndex(static_cast<int>(entry->type));
m_ui.region->setCurrentIndex(static_cast<int>(entry->region));
m_ui.compatibility->setCurrentIndex(static_cast<int>(entry->compatibility_rating));
for (const std::string& name : PAD::GetInputProfileNames())
m_ui.inputProfile->addItem(QString::fromStdString(name));
std::optional<std::string> profile(m_dialog->getStringValue("EmuCore", "InputProfileName", std::nullopt));
if (profile.has_value())
m_ui.inputProfile->setCurrentIndex(m_ui.inputProfile->findText(QString::fromStdString(profile.value())));
else
m_ui.inputProfile->setCurrentIndex(0);
}
void GameSummaryWidget::onInputProfileChanged(int index)
{
if (index == 0)
m_dialog->setStringSettingValue("EmuCore", "InputProfileName", std::nullopt);
else
m_dialog->setStringSettingValue("EmuCore", "InputProfileName", m_ui.inputProfile->itemText(index).toUtf8());
}
+3
View File
@@ -37,5 +37,8 @@ public:
private:
void populateUi(const GameList::Entry* entry);
void onInputProfileChanged(int index);
Ui::GameSummaryWidget m_ui;
SettingsDialog* m_dialog;
};
+103 -63
View File
@@ -82,6 +82,65 @@
</property>
</widget>
</item>
<item row="4" column="0">
<widget class="QLabel" name="label_5">
<property name="text">
<string>Type:</string>
</property>
</widget>
</item>
<item row="4" column="1">
<widget class="QComboBox" name="type">
<property name="enabled">
<bool>false</bool>
</property>
<property name="sizePolicy">
<sizepolicy hsizetype="Expanding" vsizetype="Fixed">
<horstretch>0</horstretch>
<verstretch>0</verstretch>
</sizepolicy>
</property>
<property name="editable">
<bool>true</bool>
</property>
<item>
<property name="text">
<string>PS2 Disc</string>
</property>
<property name="icon">
<iconset resource="../resources/resources.qrc">
<normaloff>:/icons/media-optical-24.png</normaloff>:/icons/media-optical-24.png</iconset>
</property>
</item>
<item>
<property name="text">
<string>PS1 Disc</string>
</property>
<property name="icon">
<iconset resource="../resources/resources.qrc">
<normaloff>:/icons/media-optical-24.png</normaloff>:/icons/media-optical-24.png</iconset>
</property>
</item>
<item>
<property name="text">
<string>ELF (PS2 Executable)</string>
</property>
<property name="icon">
<iconset resource="../resources/resources.qrc">
<normaloff>:/icons/applications-system-24.png</normaloff>:/icons/applications-system-24.png</iconset>
</property>
</item>
<item>
<property name="text">
<string>Playlist</string>
</property>
<property name="icon">
<iconset resource="../resources/resources.qrc">
<normaloff>:/icons/address-book-new-22.png</normaloff>:/icons/address-book-new-22.png</iconset>
</property>
</item>
</widget>
</item>
<item row="5" column="0">
<widget class="QLabel" name="label_3">
<property name="text">
@@ -94,6 +153,12 @@
<property name="enabled">
<bool>false</bool>
</property>
<property name="sizePolicy">
<sizepolicy hsizetype="Expanding" vsizetype="Fixed">
<horstretch>0</horstretch>
<verstretch>0</verstretch>
</sizepolicy>
</property>
<property name="editable">
<bool>true</bool>
</property>
@@ -251,24 +316,17 @@
</property>
</widget>
</item>
<item row="7" column="1">
<spacer name="verticalSpacer">
<property name="orientation">
<enum>Qt::Vertical</enum>
</property>
<property name="sizeHint" stdset="0">
<size>
<width>20</width>
<height>40</height>
</size>
</property>
</spacer>
</item>
<item row="6" column="1">
<widget class="QComboBox" name="compatibility">
<property name="enabled">
<bool>false</bool>
</property>
<property name="sizePolicy">
<sizepolicy hsizetype="Expanding" vsizetype="Fixed">
<horstretch>0</horstretch>
<verstretch>0</verstretch>
</sizepolicy>
</property>
<property name="editable">
<bool>true</bool>
</property>
@@ -301,62 +359,44 @@
<property name="text">
<string>Perfect</string>
</property>
</item>z
</item>
</widget>
</item>
<item row="4" column="0">
<widget class="QLabel" name="label_5">
<item row="8" column="1">
<spacer name="verticalSpacer">
<property name="orientation">
<enum>Qt::Vertical</enum>
</property>
<property name="sizeHint" stdset="0">
<size>
<width>20</width>
<height>40</height>
</size>
</property>
</spacer>
</item>
<item row="7" column="1">
<widget class="QComboBox" name="inputProfile">
<property name="sizePolicy">
<sizepolicy hsizetype="Expanding" vsizetype="Fixed">
<horstretch>0</horstretch>
<verstretch>0</verstretch>
</sizepolicy>
</property>
<item>
<property name="text">
<string>Shared</string>
</property>
</item>
</widget>
</item>
<item row="7" column="0">
<widget class="QLabel" name="label_6">
<property name="text">
<string>Type:</string>
<string>Input Profile:</string>
</property>
</widget>
</item>
<item row="4" column="1">
<widget class="QComboBox" name="type">
<property name="enabled">
<bool>false</bool>
</property>
<property name="editable">
<bool>true</bool>
</property>
<item>
<property name="text">
<string>PS2 Disc</string>
</property>
<property name="icon">
<iconset resource="../resources/resources.qrc">
<normaloff>:/icons/media-optical-24.png</normaloff>:/icons/media-optical-24.png</iconset>
</property>
</item>
<item>
<property name="text">
<string>PS1 Disc</string>
</property>
<property name="icon">
<iconset resource="../resources/resources.qrc">
<normaloff>:/icons/media-optical-24.png</normaloff>:/icons/media-optical-24.png</iconset>
</property>
</item>
<item>
<property name="text">
<string>ELF (PS2 Executable)</string>
</property>
<property name="icon">
<iconset resource="../resources/resources.qrc">
<normaloff>:/icons/applications-system-24.png</normaloff>:/icons/applications-system-24.png</iconset>
</property>
</item>
<item>
<property name="text">
<string>Playlist</string>
</property>
<property name="icon">
<iconset resource="../resources/resources.qrc">
<normaloff>:/icons/address-book-new-22.png</normaloff>:/icons/address-book-new-22.png</iconset>
</property>
</item>
</widget>
</item>
</layout>
</widget>
<resources>
+13 -3
View File
@@ -86,6 +86,12 @@ GraphicsSettingsWidget::GraphicsSettingsWidget(SettingsDialog* dialog, QWidget*
m_ui.setupUi(this);
// start hidden, fixup in updateRendererDependentOptions()
m_ui.hardwareRendererGroup->setVisible(false);
m_ui.verticalLayout->removeWidget(m_ui.hardwareRendererGroup);
m_ui.softwareRendererGroup->setVisible(false);
m_ui.verticalLayout->removeWidget(m_ui.softwareRendererGroup);
//////////////////////////////////////////////////////////////////////////
// Global Settings
//////////////////////////////////////////////////////////////////////////
@@ -105,6 +111,7 @@ GraphicsSettingsWidget::GraphicsSettingsWidget(SettingsDialog* dialog, QWidget*
SettingWidgetBinder::BindWidgetToBoolSetting(sif, m_ui.integerScaling, "EmuCore/GS", "IntegerScaling", false);
SettingWidgetBinder::BindWidgetToBoolSetting(sif, m_ui.PCRTCOffsets, "EmuCore/GS", "pcrtc_offsets", false);
SettingWidgetBinder::BindWidgetToBoolSetting(sif, m_ui.PCRTCOverscan, "EmuCore/GS", "pcrtc_overscan", false);
SettingWidgetBinder::BindWidgetToBoolSetting(sif, m_ui.PCRTCAntiBlur, "EmuCore/GS", "pcrtc_antiblur", true);
SettingWidgetBinder::BindWidgetToBoolSetting(sif, m_ui.DisableInterlaceOffset, "EmuCore/GS", "disable_interlace_offset", false);
SettingWidgetBinder::BindWidgetToBoolSetting(sif, m_ui.internalResolutionScreenshots, "EmuCore/GS", "InternalResolutionScreenshots", false);
SettingWidgetBinder::BindWidgetToFloatSetting(sif, m_ui.zoom, "EmuCore/GS", "Zoom", 100.0f);
@@ -208,6 +215,8 @@ GraphicsSettingsWidget::GraphicsSettingsWidget(SettingsDialog* dialog, QWidget*
SettingWidgetBinder::BindWidgetToBoolSetting(sif, m_ui.loadTextureReplacements, "EmuCore/GS", "LoadTextureReplacements", false);
SettingWidgetBinder::BindWidgetToBoolSetting(sif, m_ui.loadTextureReplacementsAsync, "EmuCore/GS", "LoadTextureReplacementsAsync", true);
SettingWidgetBinder::BindWidgetToBoolSetting(sif, m_ui.precacheTextureReplacements, "EmuCore/GS", "PrecacheTextureReplacements", false);
SettingWidgetBinder::BindWidgetToFolderSetting(sif, m_ui.texturesDirectory, m_ui.texturesBrowse, m_ui.texturesOpen, m_ui.texturesReset,
"Folders", "Textures", "textures");
//////////////////////////////////////////////////////////////////////////
// Advanced Settings
@@ -537,7 +546,7 @@ void GraphicsSettingsWidget::updateRendererDependentOptions()
const int current_tab = m_hardware_renderer_visible ? m_ui.hardwareRendererGroup->currentIndex() : m_ui.softwareRendererGroup->currentIndex();
// move advanced tab to the correct parent
static constexpr std::array<const char*, 3> move_tab_names = {{"Display", "On-Screen Display", "Advanced"}};
static constexpr std::array<const char*, 3> move_tab_names = {{"Display", "OSD", "Advanced"}};
const std::array<QWidget*, 3> move_tab_pointers = {{m_ui.gameDisplayTab, m_ui.osdTab, m_ui.advancedTab}};
for (size_t i = 0; i < move_tab_pointers.size(); i++)
{
@@ -564,7 +573,7 @@ void GraphicsSettingsWidget::updateRendererDependentOptions()
{
// map first two tabs over, skip hacks
m_ui.verticalLayout->insertWidget(1, m_ui.hardwareRendererGroup);
m_ui.hardwareRendererGroup->setCurrentIndex((current_tab < 2) ? current_tab : (current_tab + 2));
m_ui.hardwareRendererGroup->setCurrentIndex((current_tab < 2) ? current_tab : (current_tab + 3));
}
m_hardware_renderer_visible = is_hardware;
@@ -684,5 +693,6 @@ void GraphicsSettingsWidget::updateRendererDependentOptions()
}
m_ui.enableHWFixes->setEnabled(is_hardware);
onEnableHardwareFixesChanged();
if (is_hardware)
onEnableHardwareFixesChanged();
}
+3 -4
View File
@@ -31,8 +31,6 @@ public:
GraphicsSettingsWidget(SettingsDialog* dialog, QWidget* parent);
~GraphicsSettingsWidget();
void updateRendererDependentOptions();
Q_SIGNALS:
void fullscreenModesChanged(const QStringList& modes);
@@ -48,11 +46,12 @@ private Q_SLOTS:
private:
GSRendererType getEffectiveRenderer() const;
void updateRendererDependentOptions();
SettingsDialog* m_dialog;
Ui::GraphicsSettingsWidget m_ui;
bool m_hardware_renderer_visible = true;
bool m_software_renderer_visible = true;
bool m_hardware_renderer_visible = false;
bool m_software_renderer_visible = false;
};
+126 -53
View File
@@ -6,7 +6,7 @@
<rect>
<x>0</x>
<y>0</y>
<width>566</width>
<width>657</width>
<height>890</height>
</rect>
</property>
@@ -323,6 +323,19 @@
</property>
</widget>
</item>
<item row="4" column="1">
<widget class="QCheckBox" name="PCRTCAntiBlur">
<property name="toolTip">
<string>Enables internal Anti-Blur hacks. LEss accurate to PS2 rendering but will make a lot of games look less blurry.</string>
</property>
<property name="text">
<string>Anti-Blur</string>
</property>
<property name="shortcut">
<string>Ctrl+S</string>
</property>
</widget>
</item>
</layout>
</item>
</layout>
@@ -900,9 +913,120 @@
</item>
</layout>
</widget>
<widget class="QWidget" name="tab">
<attribute name="title">
<string>Texture Replacement</string>
</attribute>
<layout class="QVBoxLayout" name="verticalLayout_4">
<item>
<widget class="QGroupBox" name="groupBox_6">
<property name="title">
<string>Search Directory</string>
</property>
<layout class="QGridLayout" name="gridLayout_4">
<item row="1" column="0">
<widget class="QLineEdit" name="texturesDirectory"/>
</item>
<item row="1" column="1">
<widget class="QPushButton" name="texturesBrowse">
<property name="text">
<string>Browse...</string>
</property>
</widget>
</item>
<item row="1" column="2">
<widget class="QPushButton" name="texturesOpen">
<property name="text">
<string>Open...</string>
</property>
</widget>
</item>
<item row="1" column="3">
<widget class="QPushButton" name="texturesReset">
<property name="text">
<string>Reset</string>
</property>
</widget>
</item>
<item row="0" column="0" colspan="4">
<widget class="QLabel" name="label_38">
<property name="text">
<string>PCSX2 will dump and load texture replacements from this directory.</string>
</property>
</widget>
</item>
</layout>
</widget>
</item>
<item>
<widget class="QGroupBox" name="groupBox">
<property name="title">
<string>Options</string>
</property>
<layout class="QGridLayout" name="gridLayout_8">
<item row="0" column="0">
<widget class="QCheckBox" name="dumpReplaceableTextures">
<property name="text">
<string>Dump Textures</string>
</property>
</widget>
</item>
<item row="0" column="1">
<widget class="QCheckBox" name="dumpReplaceableMipmaps">
<property name="text">
<string>Dump Mipmaps</string>
</property>
</widget>
</item>
<item row="1" column="0">
<widget class="QCheckBox" name="dumpTexturesWithFMVActive">
<property name="text">
<string>Dump FMV Textures</string>
</property>
</widget>
</item>
<item row="1" column="1">
<widget class="QCheckBox" name="loadTextureReplacementsAsync">
<property name="text">
<string>Async Texture Loading</string>
</property>
</widget>
</item>
<item row="2" column="0">
<widget class="QCheckBox" name="loadTextureReplacements">
<property name="text">
<string>Load Textures</string>
</property>
</widget>
</item>
<item row="2" column="1">
<widget class="QCheckBox" name="precacheTextureReplacements">
<property name="text">
<string>Precache Textures</string>
</property>
</widget>
</item>
</layout>
</widget>
</item>
<item>
<spacer name="verticalSpacer_4">
<property name="orientation">
<enum>Qt::Vertical</enum>
</property>
<property name="sizeHint" stdset="0">
<size>
<width>20</width>
<height>40</height>
</size>
</property>
</spacer>
</item>
</layout>
</widget>
<widget class="QGroupBox" name="osdTab">
<attribute name="title">
<string>On-Screen Display</string>
<string>OSD</string>
</attribute>
<layout class="QVBoxLayout" name="verticalLayout_3">
<item>
@@ -1274,57 +1398,6 @@
</layout>
</widget>
</item>
<item>
<widget class="QGroupBox" name="groupBox">
<property name="title">
<string>Texture Replacement</string>
</property>
<layout class="QGridLayout" name="gridLayout_8">
<item row="0" column="0">
<widget class="QCheckBox" name="dumpReplaceableTextures">
<property name="text">
<string>Dump Textures</string>
</property>
</widget>
</item>
<item row="0" column="1">
<widget class="QCheckBox" name="dumpReplaceableMipmaps">
<property name="text">
<string>Dump Mipmaps</string>
</property>
</widget>
</item>
<item row="1" column="1">
<widget class="QCheckBox" name="loadTextureReplacementsAsync">
<property name="text">
<string>Async Texture Loading</string>
</property>
</widget>
</item>
<item row="2" column="1">
<widget class="QCheckBox" name="precacheTextureReplacements">
<property name="text">
<string>Precache Textures</string>
</property>
</widget>
</item>
<item row="2" column="0">
<widget class="QCheckBox" name="loadTextureReplacements">
<property name="text">
<string>Load Textures</string>
</property>
</widget>
</item>
<item row="1" column="0">
<widget class="QCheckBox" name="dumpTexturesWithFMVActive">
<property name="text">
<string>Dump FMV Textures</string>
</property>
</widget>
</item>
</layout>
</widget>
</item>
<item>
<spacer name="verticalSpacer_2">
<property name="orientation">
+2 -1
View File
@@ -29,6 +29,7 @@
HotkeySettingsWidget::HotkeySettingsWidget(QWidget* parent, ControllerSettingsDialog* dialog)
: QWidget(parent)
, m_dialog(dialog)
{
createUi();
}
@@ -79,6 +80,6 @@ void HotkeySettingsWidget::createButtons()
const int target_row = layout->count() / 2;
layout->addWidget(new QLabel(qApp->translate("Hotkeys", hotkey->display_name), container), target_row, 0);
layout->addWidget(new InputBindingWidget(container, "Hotkeys", hotkey->name), target_row, 1);
layout->addWidget(new InputBindingWidget(container, m_dialog->getProfileSettingsInterface(), "Hotkeys", hotkey->name), target_row, 1);
}
}
+1
View File
@@ -37,6 +37,7 @@ private:
void createUi();
void createButtons();
ControllerSettingsDialog* m_dialog;
QTabWidget* m_tab_widget;
struct Category
+55 -13
View File
@@ -17,6 +17,7 @@
#include "EmuThread.h"
#include "QtHost.h"
#include "QtUtils.h"
#include "Settings/InputBindingDialog.h"
#include <QtCore/QTimer>
#include <QtGui/QKeyEvent>
@@ -25,9 +26,10 @@
// _BitScanForward()
#include "pcsx2/GS/GSIntrin.h"
InputBindingDialog::InputBindingDialog(std::string section_name, std::string key_name,
InputBindingDialog::InputBindingDialog(SettingsInterface* sif, std::string section_name, std::string key_name,
std::vector<std::string> bindings, QWidget* parent)
: QDialog(parent)
, m_sif(sif)
, m_section_name(std::move(section_name))
, m_key_name(std::move(key_name))
, m_bindings(std::move(bindings))
@@ -54,7 +56,7 @@ bool InputBindingDialog::eventFilter(QObject* watched, QEvent* event)
const QEvent::Type event_type = event->type();
// if the key is being released, set the input
if (event_type == QEvent::KeyRelease || event_type == QEvent::MouseButtonPress)
if (event_type == QEvent::KeyRelease || event_type == QEvent::MouseButtonRelease)
{
addNewBinding();
stopListeningForInput();
@@ -63,20 +65,46 @@ bool InputBindingDialog::eventFilter(QObject* watched, QEvent* event)
else if (event_type == QEvent::KeyPress)
{
const QKeyEvent* key_event = static_cast<const QKeyEvent*>(event);
m_new_bindings.push_back(InputManager::MakeHostKeyboardKey(key_event->key()));
m_new_bindings.push_back(InputManager::MakeHostKeyboardKey(QtUtils::KeyEventToCode(key_event)));
return true;
}
else if (event_type == QEvent::MouseButtonPress)
else if (event_type == QEvent::MouseButtonPress || event_type == QEvent::MouseButtonDblClick)
{
// double clicks get triggered if we click bind, then click again quickly.
unsigned long button_index;
if (_BitScanForward(&button_index, static_cast<u32>(static_cast<const QMouseEvent*>(event)->button())))
m_new_bindings.push_back(InputManager::MakeHostMouseButtonKey(button_index));
m_new_bindings.push_back(InputManager::MakePointerButtonKey(0, button_index));
return true;
}
else if (event_type == QEvent::MouseButtonDblClick)
else if (event_type == QEvent::MouseMove)
{
// just eat double clicks
return true;
// if we've moved more than a decent distance from the center of the widget, bind it.
// this is so we don't accidentally bind to the mouse if you bump it while reaching for your pad.
static constexpr const s32 THRESHOLD = 50;
const QPoint diff(static_cast<QMouseEvent*>(event)->globalPos() - m_input_listen_start_position);
bool has_one = false;
if (std::abs(diff.x()) >= THRESHOLD)
{
InputBindingKey key(InputManager::MakePointerAxisKey(0, InputPointerAxis::X));
key.negative = (diff.x() < 0);
m_new_bindings.push_back(key);
has_one = true;
}
if (std::abs(diff.y()) >= THRESHOLD)
{
InputBindingKey key(InputManager::MakePointerAxisKey(0, InputPointerAxis::Y));
key.negative = (diff.y() < 0);
m_new_bindings.push_back(key);
has_one = true;
}
if (has_one)
{
addNewBinding();
stopListeningForInput();
return true;
}
}
return false;
@@ -97,6 +125,7 @@ void InputBindingDialog::onInputListenTimerTimeout()
void InputBindingDialog::startListeningForInput(u32 timeout_in_seconds)
{
m_new_bindings.clear();
m_input_listen_start_position = QCursor::pos();
m_input_listen_timer = new QTimer(this);
m_input_listen_timer->setSingleShot(false);
m_input_listen_timer->start(1000);
@@ -113,6 +142,7 @@ void InputBindingDialog::startListeningForInput(u32 timeout_in_seconds)
installEventFilter(this);
grabKeyboard();
grabMouse();
setMouseTracking(true);
hookInputManager();
}
@@ -130,6 +160,7 @@ void InputBindingDialog::stopListeningForInput()
unhookInputManager();
releaseMouse();
releaseKeyboard();
setMouseTracking(false);
removeEventFilter(this);
}
@@ -186,12 +217,23 @@ void InputBindingDialog::updateList()
void InputBindingDialog::saveListToSettings()
{
if (!m_bindings.empty())
QtHost::SetBaseStringListSettingValue(m_section_name.c_str(), m_key_name.c_str(), m_bindings);
if (m_sif)
{
if (!m_bindings.empty())
m_sif->SetStringList(m_section_name.c_str(), m_key_name.c_str(), m_bindings);
else
m_sif->DeleteValue(m_section_name.c_str(), m_key_name.c_str());
m_sif->Save();
g_emu_thread->reloadGameSettings();
}
else
QtHost::RemoveBaseSettingValue(m_section_name.c_str(), m_key_name.c_str());
g_emu_thread->reloadInputBindings();
{
if (!m_bindings.empty())
QtHost::SetBaseStringListSettingValue(m_section_name.c_str(), m_key_name.c_str(), m_bindings);
else
QtHost::RemoveBaseSettingValue(m_section_name.c_str(), m_key_name.c_str());
g_emu_thread->reloadInputBindings();
}
}
void InputBindingDialog::inputManagerHookCallback(InputBindingKey key, float value)
+5 -1
View File
@@ -21,12 +21,14 @@
#include <string>
#include <vector>
class SettingsInterface;
class InputBindingDialog : public QDialog
{
Q_OBJECT
public:
InputBindingDialog(std::string section_name, std::string key_name, std::vector<std::string> bindings, QWidget* parent);
InputBindingDialog(SettingsInterface* sif, std::string section_name, std::string key_name, std::vector<std::string> bindings, QWidget* parent);
~InputBindingDialog();
protected Q_SLOTS:
@@ -58,6 +60,7 @@ protected:
Ui::InputBindingDialog m_ui;
SettingsInterface* m_sif;
std::string m_section_name;
std::string m_key_name;
std::vector<std::string> m_bindings;
@@ -65,4 +68,5 @@ protected:
QTimer* m_input_listen_timer = nullptr;
u32 m_input_listen_remaining_seconds = 0;
QPoint m_input_listen_start_position{};
};
+77 -20
View File
@@ -19,6 +19,7 @@
#include <QtGui/QKeyEvent>
#include <QtGui/QMouseEvent>
#include <QtWidgets/QInputDialog>
#include <QtWidgets/QMessageBox>
#include <cmath>
#include <sstream>
@@ -39,7 +40,7 @@ InputBindingWidget::InputBindingWidget(QWidget* parent)
connect(this, &QPushButton::clicked, this, &InputBindingWidget::onClicked);
}
InputBindingWidget::InputBindingWidget(QWidget* parent, std::string section_name, std::string key_name)
InputBindingWidget::InputBindingWidget(QWidget* parent, SettingsInterface* sif, std::string section_name, std::string key_name)
: QPushButton(parent)
{
setMinimumWidth(225);
@@ -47,7 +48,7 @@ InputBindingWidget::InputBindingWidget(QWidget* parent, std::string section_name
connect(this, &QPushButton::clicked, this, &InputBindingWidget::onClicked);
setKey(std::move(section_name), std::move(key_name));
initialize(sif, std::move(section_name), std::move(key_name));
}
InputBindingWidget::~InputBindingWidget()
@@ -55,12 +56,12 @@ InputBindingWidget::~InputBindingWidget()
Q_ASSERT(!isListeningForInput());
}
void InputBindingWidget::setKey(std::string section_name, std::string key_name)
void InputBindingWidget::initialize(SettingsInterface* sif, std::string section_name, std::string key_name)
{
m_sif = sif;
m_section_name = std::move(section_name);
m_key_name = std::move(key_name);
m_bindings = Host::GetBaseStringListSetting(m_section_name.c_str(), m_key_name.c_str());
updateText();
reloadBinding();
}
void InputBindingWidget::updateText()
@@ -105,7 +106,7 @@ bool InputBindingWidget::eventFilter(QObject* watched, QEvent* event)
const QEvent::Type event_type = event->type();
// if the key is being released, set the input
if (event_type == QEvent::KeyRelease || event_type == QEvent::MouseButtonPress)
if (event_type == QEvent::KeyRelease || event_type == QEvent::MouseButtonRelease)
{
setNewBinding();
stopListeningForInput();
@@ -114,20 +115,46 @@ bool InputBindingWidget::eventFilter(QObject* watched, QEvent* event)
else if (event_type == QEvent::KeyPress)
{
const QKeyEvent* key_event = static_cast<const QKeyEvent*>(event);
m_new_bindings.push_back(InputManager::MakeHostKeyboardKey(key_event->key()));
m_new_bindings.push_back(InputManager::MakeHostKeyboardKey(QtUtils::KeyEventToCode(key_event)));
return true;
}
else if (event_type == QEvent::MouseButtonPress)
else if (event_type == QEvent::MouseButtonPress || event_type == QEvent::MouseButtonDblClick)
{
// double clicks get triggered if we click bind, then click again quickly.
unsigned long button_index;
if (_BitScanForward(&button_index, static_cast<u32>(static_cast<const QMouseEvent*>(event)->button())))
m_new_bindings.push_back(InputManager::MakeHostMouseButtonKey(button_index));
m_new_bindings.push_back(InputManager::MakePointerButtonKey(0, button_index));
return true;
}
else if (event_type == QEvent::MouseButtonDblClick)
else if (event_type == QEvent::MouseMove)
{
// just eat double clicks
return true;
// if we've moved more than a decent distance from the center of the widget, bind it.
// this is so we don't accidentally bind to the mouse if you bump it while reaching for your pad.
static constexpr const s32 THRESHOLD = 50;
const QPoint diff(static_cast<QMouseEvent*>(event)->globalPos() - m_input_listen_start_position);
bool has_one = false;
if (std::abs(diff.x()) >= THRESHOLD)
{
InputBindingKey key(InputManager::MakePointerAxisKey(0, InputPointerAxis::X));
key.negative = (diff.x() < 0);
m_new_bindings.push_back(key);
has_one = true;
}
if (std::abs(diff.y()) >= THRESHOLD)
{
InputBindingKey key(InputManager::MakePointerAxisKey(0, InputPointerAxis::Y));
key.negative = (diff.y() < 0);
m_new_bindings.push_back(key);
has_one = true;
}
if (has_one)
{
setNewBinding();
stopListeningForInput();
return true;
}
}
return false;
@@ -168,8 +195,17 @@ void InputBindingWidget::setNewBinding()
InputManager::ConvertInputBindingKeysToString(m_new_bindings.data(), m_new_bindings.size()));
if (!new_binding.empty())
{
QtHost::SetBaseStringSettingValue(m_section_name.c_str(), m_key_name.c_str(), new_binding.c_str());
g_emu_thread->reloadInputBindings();
if (m_sif)
{
m_sif->SetStringValue(m_section_name.c_str(), m_key_name.c_str(), new_binding.c_str());
m_sif->Save();
g_emu_thread->reloadGameSettings();
}
else
{
QtHost::SetBaseStringSettingValue(m_section_name.c_str(), m_key_name.c_str(), new_binding.c_str());
g_emu_thread->reloadInputBindings();
}
}
m_bindings.clear();
@@ -179,14 +215,25 @@ void InputBindingWidget::setNewBinding()
void InputBindingWidget::clearBinding()
{
m_bindings.clear();
QtHost::RemoveBaseSettingValue(m_section_name.c_str(), m_key_name.c_str());
g_emu_thread->reloadInputBindings();
updateText();
if (m_sif)
{
m_sif->DeleteValue(m_section_name.c_str(), m_key_name.c_str());
m_sif->Save();
g_emu_thread->reloadGameSettings();
}
else
{
QtHost::RemoveBaseSettingValue(m_section_name.c_str(), m_key_name.c_str());
g_emu_thread->reloadInputBindings();
}
reloadBinding();
}
void InputBindingWidget::reloadBinding()
{
m_bindings = Host::GetBaseStringListSetting(m_section_name.c_str(), m_key_name.c_str());
m_bindings = m_sif ?
m_sif->GetStringList(m_section_name.c_str(), m_key_name.c_str()) :
Host::GetBaseStringListSetting(m_section_name.c_str(), m_key_name.c_str());
updateText();
}
@@ -219,6 +266,7 @@ void InputBindingWidget::onInputListenTimerTimeout()
void InputBindingWidget::startListeningForInput(u32 timeout_in_seconds)
{
m_new_bindings.clear();
m_input_listen_start_position = QCursor::pos();
m_input_listen_timer = new QTimer(this);
m_input_listen_timer->setSingleShot(false);
m_input_listen_timer->start(1000);
@@ -231,17 +279,19 @@ void InputBindingWidget::startListeningForInput(u32 timeout_in_seconds)
installEventFilter(this);
grabKeyboard();
grabMouse();
setMouseTracking(true);
hookInputManager();
}
void InputBindingWidget::stopListeningForInput()
{
updateText();
reloadBinding();
delete m_input_listen_timer;
m_input_listen_timer = nullptr;
std::vector<InputBindingKey>().swap(m_new_bindings);
unhookInputManager();
setMouseTracking(false);
releaseMouse();
releaseKeyboard();
removeEventFilter(this);
@@ -293,7 +343,7 @@ void InputBindingWidget::unhookInputManager()
void InputBindingWidget::openDialog()
{
InputBindingDialog binding_dialog(m_section_name, m_key_name, m_bindings, QtUtils::GetRootWidget(this));
InputBindingDialog binding_dialog(m_sif, m_section_name, m_key_name, m_bindings, QtUtils::GetRootWidget(this));
binding_dialog.exec();
reloadBinding();
}
@@ -342,7 +392,14 @@ void InputVibrationBindingWidget::onClicked()
const QString current(QString::fromStdString(m_binding));
QStringList input_options(m_dialog->getVibrationMotors());
if (!current.isEmpty() && input_options.indexOf(current) < 0)
{
input_options.append(current);
}
else if (input_options.isEmpty())
{
QMessageBox::critical(QtUtils::GetRootWidget(this), tr("Error"), tr("No devices with vibration motors were detected."));
return;
}
QInputDialog input_dialog(this);
input_dialog.setWindowTitle(full_key);
+5 -2
View File
@@ -22,6 +22,7 @@
class QTimer;
class ControllerSettingsDialog;
class SettingsInterface;
class InputBindingWidget : public QPushButton
{
@@ -29,10 +30,10 @@ class InputBindingWidget : public QPushButton
public:
InputBindingWidget(QWidget* parent);
InputBindingWidget(QWidget* parent, std::string section_name, std::string key_name);
InputBindingWidget(QWidget* parent, SettingsInterface* sif, std::string section_name, std::string key_name);
~InputBindingWidget();
void setKey(std::string section_name, std::string key_name);
void initialize(SettingsInterface* sif, std::string section_name, std::string key_name);
public Q_SLOTS:
void clearBinding();
@@ -65,12 +66,14 @@ protected:
void hookInputManager();
void unhookInputManager();
SettingsInterface* m_sif = nullptr;
std::string m_section_name;
std::string m_key_name;
std::vector<std::string> m_bindings;
std::vector<InputBindingKey> m_new_bindings;
QTimer* m_input_listen_timer = nullptr;
u32 m_input_listen_remaining_seconds = 0;
QPoint m_input_listen_start_position{};
};
class InputVibrationBindingWidget : public QPushButton
@@ -117,7 +117,6 @@ InterfaceSettingsWidget::InterfaceSettingsWidget(SettingsDialog* dialog, QWidget
m_ui.pauseOnStart->setDisabled(true);
m_ui.pauseOnFocusLoss->setDisabled(true);
m_ui.disableWindowResizing->setDisabled(true);
m_ui.hideMouseCursor->setDisabled(true);
m_ui.language->setDisabled(true);
}
+1 -1
View File
@@ -88,7 +88,7 @@
<item row="1" column="1">
<widget class="QCheckBox" name="hideMouseCursor">
<property name="text">
<string>Hide Mouse Cursor</string>
<string>Hide Cursor In Fullscreen</string>
</property>
</widget>
</item>
@@ -16,6 +16,7 @@
#include "PrecompiledHeader.h"
#include <QtGui/QDrag>
#include <QtWidgets/QFileDialog>
#include <QtWidgets/QInputDialog>
#include <QtWidgets/QMenu>
#include <QtWidgets/QMessageBox>
@@ -48,12 +49,14 @@ MemoryCardSettingsWidget::MemoryCardSettingsWidget(SettingsDialog* dialog, QWidg
// this is a bit lame, but resizeEvent() isn't good enough to autosize our columns,
// since the group box hasn't been resized at that point.
m_ui.cardGroupBox->installEventFilter(this);
SettingWidgetBinder::BindWidgetToFolderSetting(sif, m_ui.directory, m_ui.browse, m_ui.open, m_ui.reset, "Folders", "MemoryCards", "memcards");
SettingWidgetBinder::BindWidgetToBoolSetting(sif, m_ui.autoEject, "EmuCore", "McdEnableEjection", true);
SettingWidgetBinder::BindWidgetToBoolSetting(sif, m_ui.automaticManagement, "EmuCore", "McdFolderAutoManage", true);
setupAdditionalUi();
connect(m_ui.directory, &QLineEdit::textChanged, this, &MemoryCardSettingsWidget::refresh);
m_ui.cardList->setContextMenuPolicy(Qt::CustomContextMenu);
connect(m_ui.cardList, &MemoryCardListWidget::itemSelectionChanged, this, &MemoryCardSettingsWidget::updateCardActions);
connect(m_ui.cardList, &MemoryCardListWidget::customContextMenuRequested, this, &MemoryCardSettingsWidget::listContextMenuRequested);
+36 -1
View File
@@ -6,7 +6,7 @@
<rect>
<x>0</x>
<y>0</y>
<width>796</width>
<width>639</width>
<height>443</height>
</rect>
</property>
@@ -58,6 +58,41 @@
<string>Memory Cards</string>
</property>
<layout class="QVBoxLayout" name="verticalLayout_2">
<item>
<layout class="QHBoxLayout" name="horizontalLayout">
<item>
<widget class="QLabel" name="label">
<property name="text">
<string>Folder:</string>
</property>
</widget>
</item>
<item>
<widget class="QLineEdit" name="directory"/>
</item>
<item>
<widget class="QPushButton" name="browse">
<property name="text">
<string>Browse...</string>
</property>
</widget>
</item>
<item>
<widget class="QPushButton" name="open">
<property name="text">
<string>Open...</string>
</property>
</widget>
</item>
<item>
<widget class="QPushButton" name="reset">
<property name="text">
<string>Reset</string>
</property>
</widget>
</item>
</layout>
</item>
<item>
<widget class="MemoryCardListWidget" name="cardList">
<property name="rootIsDecorated">
+15 -8
View File
@@ -37,6 +37,7 @@
#include "GameListSettingsWidget.h"
#include "GraphicsSettingsWidget.h"
#include "DEV9SettingsWidget.h"
#include "FolderSettingsWidget.h"
#include "HotkeySettingsWidget.h"
#include "InterfaceSettingsWidget.h"
#include "MemoryCardSettingsWidget.h"
@@ -133,6 +134,12 @@ void SettingsDialog::setupUi(const GameList::Entry* game)
tr("<strong>Network & HDD Settings</strong><hr>These options control the network connectivity and internal HDD storage of the console.<br><br>"
"Mouse over an option for additional information."));
if (!isPerGameSettings())
{
addWidget(m_folder_settings = new FolderSettingsWidget(this, m_ui.settingsContainer), tr("Folders"), QStringLiteral("folder-open-line"),
tr("<strong>Folder Settings</strong><hr>These options control where PCSX2 will save runtime data files."));
}
m_ui.settingsCategory->setSizePolicy(QSizePolicy::Minimum, QSizePolicy::Minimum);
m_ui.settingsCategory->setCurrentRow(0);
m_ui.settingsContainer->setCurrentIndex(0);
@@ -363,13 +370,13 @@ void SettingsDialog::setBoolSettingValue(const char* section, const char* key, s
{
value.has_value() ? m_sif->SetBoolValue(section, key, value.value()) : m_sif->DeleteValue(section, key);
m_sif->Save();
g_emu_thread->reloadGameSettings();
}
else
{
value.has_value() ? QtHost::SetBaseBoolSettingValue(section, key, value.value()) : QtHost::RemoveBaseSettingValue(section, key);
g_emu_thread->applySettings();
}
g_emu_thread->applySettings();
}
void SettingsDialog::setIntSettingValue(const char* section, const char* key, std::optional<int> value)
@@ -378,13 +385,13 @@ void SettingsDialog::setIntSettingValue(const char* section, const char* key, st
{
value.has_value() ? m_sif->SetIntValue(section, key, value.value()) : m_sif->DeleteValue(section, key);
m_sif->Save();
g_emu_thread->reloadGameSettings();
}
else
{
value.has_value() ? QtHost::SetBaseIntSettingValue(section, key, value.value()) : QtHost::RemoveBaseSettingValue(section, key);
g_emu_thread->applySettings();
}
g_emu_thread->applySettings();
}
void SettingsDialog::setFloatSettingValue(const char* section, const char* key, std::optional<float> value)
@@ -393,13 +400,13 @@ void SettingsDialog::setFloatSettingValue(const char* section, const char* key,
{
value.has_value() ? m_sif->SetFloatValue(section, key, value.value()) : m_sif->DeleteValue(section, key);
m_sif->Save();
g_emu_thread->reloadGameSettings();
}
else
{
value.has_value() ? QtHost::SetBaseFloatSettingValue(section, key, value.value()) : QtHost::RemoveBaseSettingValue(section, key);
g_emu_thread->applySettings();
}
g_emu_thread->applySettings();
}
void SettingsDialog::setStringSettingValue(const char* section, const char* key, std::optional<const char*> value)
@@ -408,13 +415,13 @@ void SettingsDialog::setStringSettingValue(const char* section, const char* key,
{
value.has_value() ? m_sif->SetStringValue(section, key, value.value()) : m_sif->DeleteValue(section, key);
m_sif->Save();
g_emu_thread->reloadGameSettings();
}
else
{
value.has_value() ? QtHost::SetBaseStringSettingValue(section, key, value.value()) : QtHost::RemoveBaseSettingValue(section, key);
g_emu_thread->applySettings();
}
g_emu_thread->applySettings();
}
void SettingsDialog::openGamePropertiesDialog(const GameList::Entry* game, const std::string_view& serial, u32 crc)
+3
View File
@@ -39,6 +39,7 @@ class GameFixSettingsWidget;
class GraphicsSettingsWidget;
class AudioSettingsWidget;
class MemoryCardSettingsWidget;
class FolderSettingsWidget;
class DEV9SettingsWidget;
class SettingsDialog final : public QDialog
@@ -65,6 +66,7 @@ public:
__fi GraphicsSettingsWidget* getGraphicsSettingsWidget() const { return m_graphics_settings; }
__fi AudioSettingsWidget* getAudioSettingsWidget() const { return m_audio_settings; }
__fi MemoryCardSettingsWidget* getMemoryCardSettingsWidget() const { return m_memory_card_settings; }
__fi FolderSettingsWidget* getFolderSettingsWidget() const { return m_folder_settings; }
__fi DEV9SettingsWidget* getDEV9SettingsWidget() const { return m_dev9_settings; }
void registerWidgetHelp(QObject* object, QString title, QString recommended_value, QString text);
@@ -122,6 +124,7 @@ private:
GraphicsSettingsWidget* m_graphics_settings = nullptr;
AudioSettingsWidget* m_audio_settings = nullptr;
MemoryCardSettingsWidget* m_memory_card_settings = nullptr;
FolderSettingsWidget* m_folder_settings = nullptr;
DEV9SettingsWidget* m_dev9_settings = nullptr;
std::array<QString, MAX_SETTINGS_WIDGETS> m_category_help_text;
+8 -1
View File
@@ -135,6 +135,7 @@
</ItemGroup>
<ItemGroup>
<ClCompile Include="EarlyHardwareCheck.cpp" />
<ClCompile Include="Settings\FolderSettingsWidget.cpp" />
<ClCompile Include="Tools\InputRecording\NewInputRecordingDlg.cpp" />
<ClCompile Include="Settings\BIOSSettingsWidget.cpp" />
<ClCompile Include="Settings\ControllerBindingWidgets.cpp" />
@@ -194,6 +195,8 @@
<QtMoc Include="Settings\DEV9DnsHostDialog.h" />
<QtMoc Include="Settings\DEV9SettingsWidget.h" />
<QtMoc Include="Settings\DEV9UiCommon.h" />
<ClInclude Include="Settings\ControllerSettingWidgetBinder.h" />
<QtMoc Include="Settings\FolderSettingsWidget.h" />
<ClInclude Include="Settings\HddCreateQt.h" />
<QtMoc Include="Settings\GameSummaryWidget.h" />
<QtMoc Include="Settings\CreateMemoryCardDialog.h" />
@@ -237,6 +240,7 @@
<ClCompile Include="$(IntDir)Settings\moc_InputBindingWidget.cpp" />
<ClCompile Include="$(IntDir)Settings\moc_AudioSettingsWidget.cpp" />
<ClCompile Include="$(IntDir)Settings\moc_MemoryCardSettingsWidget.cpp" />
<ClCompile Include="$(IntDir)Settings\moc_FolderSettingsWidget.cpp" />
<ClCompile Include="$(IntDir)Settings\moc_DEV9DnsHostDialog.cpp" />
<ClCompile Include="$(IntDir)Settings\moc_DEV9SettingsWidget.cpp" />
<ClCompile Include="$(IntDir)Settings\moc_DEV9UiCommon.cpp" />
@@ -328,7 +332,10 @@
<FileType>Document</FileType>
</QtUi>
</ItemGroup>
<ItemGroup>
<None Include="Settings\FolderSettingsWidget.ui" />
</ItemGroup>
<Import Project="$(VCTargetsPath)\Microsoft.Cpp.targets" />
<Import Project="$(SolutionDir)common\vsprops\QtCompile.targets" />
<ImportGroup Label="ExtensionTargets" />
</Project>
</Project>
+16 -1
View File
@@ -217,6 +217,12 @@
<Filter>Tools\Input Recording</Filter>
</ClCompile>
<ClCompile Include="EarlyHardwareCheck.cpp" />
<ClCompile Include="Settings\FolderSettingsWidget.cpp">
<Filter>Settings</Filter>
</ClCompile>
<ClCompile Include="$(IntDir)Settings\moc_FolderSettingsWidget.cpp">
<Filter>moc</Filter>
</ClCompile>
</ItemGroup>
<ItemGroup>
<Manifest Include="..\pcsx2\windows\PCSX2.manifest">
@@ -231,6 +237,9 @@
<ClInclude Include="Settings\HddCreateQt.h">
<Filter>Settings</Filter>
</ClInclude>
<ClInclude Include="Settings\ControllerSettingWidgetBinder.h">
<Filter>Settings</Filter>
</ClInclude>
</ItemGroup>
<ItemGroup>
<QtMoc Include="MainWindow.h" />
@@ -316,6 +325,7 @@
<QtMoc Include="Tools\InputRecording\NewInputRecordingDlg.h">
<Filter>Tools\Input Recording</Filter>
</QtMoc>
<QtMoc Include="Settings\FolderSettingsWidget.h" />
</ItemGroup>
<ItemGroup>
<QtResource Include="resources\resources.qrc">
@@ -392,4 +402,9 @@
<Filter>Tools\Input Recording</Filter>
</QtUi>
</ItemGroup>
</Project>
<ItemGroup>
<None Include="Settings\FolderSettingsWidget.ui">
<Filter>Settings</Filter>
</None>
</ItemGroup>
</Project>
+8 -5
View File
@@ -27,6 +27,8 @@
#include "gui/StringHelpers.h"
#include "gui/wxDirName.h"
#include <wx/stdpaths.h>
#else
#include "HostSettings.h"
#endif
#define CLAMP(val, minval, maxval) (std::min(maxval, std::max(minval, val)))
@@ -60,7 +62,7 @@ static Access* ReadIndexFromFile(const char* filename)
if (std::fread(index, sizeof(Access), 1, fp.get()) != 1 ||
datasize != static_cast<s64>(index->have) * static_cast<s64>(sizeof(Point)))
{
Console.Error("Error: unexpected size of gzip index, please delete it manually: '%s'.", filename);
Console.Error("Error: Unexpected size of gzip index, please delete it manually: '%s'.", filename);
free(index);
return 0;
}
@@ -134,7 +136,7 @@ static std::string ApplyTemplate(const std::string& name, const std::string& bas
|| !canEndWithKey && first == trimmedTemplate.length() - std::strlen(INDEX_TEMPLATE_KEY))
{
Console.Error("Invalid %s template '%s'.\n"
"Template must contain exactly one '%s' and must not end with it. Abotring.",
"Template must contain exactly one '%s' and must not end with it. Aborting.",
name.c_str(), trimmedTemplate.c_str(), INDEX_TEMPLATE_KEY);
return {};
}
@@ -182,10 +184,11 @@ static std::string iso2indexname(const std::string& isoname)
#ifndef PCSX2_CORE
std::string appRoot = // TODO: have only one of this in PCSX2. Right now have few...
StringUtil::wxStringToUTF8String(((wxDirName)(wxFileName(wxStandardPaths::Get().GetExecutablePath()).GetPath())).ToString());
return ApplyTemplate("gzip index", appRoot, EmuConfig.GzipIsoIndexTemplate, isoname, false);
#else
const std::string& appRoot = EmuFolders::DataRoot;
return ApplyTemplate("gzip index", appRoot, Host::GetBaseStringSettingValue("EmuCore", "GzipIsoIndexTemplate", "$(f).pindex.tmp"), isoname, false);
#endif
return ApplyTemplate("gzip index", appRoot, EmuConfig.GzipIsoIndexTemplate, isoname, false);
}
@@ -288,7 +291,7 @@ void GzippedFileReader::AsyncPrefetchCancel()
if (!CancelIo(hOverlappedFile))
{
Console.Warning("canceling gz prefetch failed. following prefetching will not work.");
Console.Warning("Canceling gz prefetch failed. Following prefetching will not work.");
return;
}
@@ -342,7 +345,7 @@ bool GzippedFileReader::OkIndex()
}
else
{
Console.Error("ERROR (%d): index could not be generated for file '%s'", len, m_filename.c_str());
Console.Error("ERROR (%d): Index could not be generated for file '%s'", len, m_filename.c_str());
free_index(index);
InitZstates();
return false;
+4
View File
@@ -343,6 +343,7 @@ set(pcsx2SPU2Headers
# DEV9 sources
set(pcsx2DEV9Sources
DEV9/AdapterUtils.cpp
DEV9/ATA/Commands/ATA_Command.cpp
DEV9/ATA/Commands/ATA_CmdDMA.cpp
DEV9/ATA/Commands/ATA_CmdExecuteDeviceDiag.cpp
@@ -387,6 +388,7 @@ set(pcsx2DEV9Sources
# DEV9 headers
set(pcsx2DEV9Headers
DEV9/AdapterUtils.h
DEV9/ATA/ATA.h
DEV9/ATA/HddCreate.h
DEV9/DEV9.h
@@ -1067,6 +1069,7 @@ if(PCSX2_CORE)
Frontend/InputManager.cpp
Frontend/InputSource.cpp
Frontend/LayeredSettingsInterface.cpp
Frontend/LogSink.cpp
GSDumpReplayer.cpp
HostSettings.cpp
VMManager.cpp
@@ -1077,6 +1080,7 @@ if(PCSX2_CORE)
Frontend/InputManager.h
Frontend/InputSource.h
Frontend/LayeredSettingsInterface.h
Frontend/LogSink.h
GSDumpReplayer.h
HostSettings.h
VMManager.h)
+5 -9
View File
@@ -44,6 +44,7 @@ enum GamefixId
Fix_VUSync,
Fix_VUOverflow,
Fix_XGKick,
Fix_BlitInternalFPS,
GamefixId_COUNT
};
@@ -433,6 +434,7 @@ struct Pcsx2Config
struct
{
bool
PCRTCAntiBlur : 1,
DisableInterlaceOffset : 1,
PCRTCOffsets : 1,
PCRTCOverscan : 1,
@@ -501,13 +503,9 @@ struct Pcsx2Config
// style. Useful for debugging potential bugs in the MTGS pipeline.
bool SynchronousMTGS{false};
bool FrameLimitEnable{true};
bool FrameSkipEnable{false};
VsyncMode VsyncEnable{VsyncMode::Off};
int FramesToDraw{2}; // number of consecutive frames (fields) to render
int FramesToSkip{2}; // number of consecutive frames (fields) to skip
double LimitScalar{1.0};
double FramerateNTSC{59.94};
double FrameratePAL{50.00};
@@ -800,7 +798,8 @@ struct Pcsx2Config
IbitHack : 1, // I bit hack. Needed to stop constant VU recompilation in some games
VUSyncHack : 1, // Makes microVU run behind the EE to avoid VU register reading/writing sync issues. Useful for M-Bit games
VUOverflowHack : 1, // Tries to simulate overflow flag checks (not really possible on x86 without soft floats)
XgKickHack : 1; // Erementar Gerad, adds more delay to VU XGkick instructions. Corrects the color of some graphics, but breaks Tri-ace games and others.
XgKickHack : 1, // Erementar Gerad, adds more delay to VU XGkick instructions. Corrects the color of some graphics, but breaks Tri-ace games and others.
BlitInternalFPSHack : 1; // Disables privileged register write-based FPS detection.
BITFIELD_END
GamefixOptions();
@@ -887,9 +886,6 @@ struct Pcsx2Config
// ------------------------------------------------------------------------
struct FramerateOptions
{
bool SkipOnLimit{false};
bool SkipOnTurbo{false};
double NominalScalar{1.0};
double TurboScalar{2.0};
double SlomoScalar{0.5};
@@ -899,7 +895,7 @@ struct Pcsx2Config
bool operator==(const FramerateOptions& right) const
{
return OpEqu(SkipOnLimit) && OpEqu(SkipOnTurbo) && OpEqu(NominalScalar) && OpEqu(TurboScalar) && OpEqu(SlomoScalar);
return OpEqu(NominalScalar) && OpEqu(TurboScalar) && OpEqu(SlomoScalar);
}
bool operator!=(const FramerateOptions& right) const
+2 -3
View File
@@ -233,9 +233,9 @@ static void vSyncInfoCalc(vSyncTimingInfo* info, double framesPerSecond, u32 sca
// Jak II - random speedups
// Shadow of Rome - FMV audio issues
const u64 HalfFrame = Frame / 2;
const u64 Blank = Scanline *((gsVideoMode == GS_VideoMode::NTSC ? 22 : 25) + static_cast<int>(gsIsInterlaced));
const u64 Blank = Scanline * ((gsVideoMode == GS_VideoMode::NTSC ? 22 : 25) + static_cast<int>(gsIsInterlaced));
const u64 Render = HalfFrame - Blank;
const u64 GSBlank = Scanline * 3.5; // GS VBlank/CSR Swap happens roughly 3.5 Scanlines after VBlank Start
const u64 GSBlank = Scanline * (gsVideoMode == GS_VideoMode::NTSC ? 3.5 : 3); // GS VBlank/CSR Swap happens roughly 3.5(NTSC) and 3(PAL) Scanlines after VBlank Start
// Important! The hRender/hBlank timers should be 50/50 for best results.
// (this appears to be what the real EE's timing crystal does anyway)
@@ -561,7 +561,6 @@ static __fi void frameLimitUpdateCore()
// Framelimiter - Measures the delta time between calls and stalls until a
// certain amount of time passes if such time hasn't passed yet.
// See the GS FrameSkip function for details on why this is here and not in the GS.
static __fi void frameLimit()
{
// Framelimiter off in settings? Framelimiter go brrr.
+511
View File
@@ -0,0 +1,511 @@
/* PCSX2 - PS2 Emulator for PCs
* Copyright (C) 2002-2022 PCSX2 Dev Team
*
* PCSX2 is free software: you can redistribute it and/or modify it under the terms
* of the GNU Lesser General Public License as published by the Free Software Found-
* ation, either version 3 of the License, or (at your option) any later version.
*
* PCSX2 is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;
* without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR
* PURPOSE. See the GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License along with PCSX2.
* If not, see <http://www.gnu.org/licenses/>.
*/
#include "PrecompiledHeader.h"
#include <algorithm>
#ifdef __POSIX__
#include <vector>
#include <fstream>
#include <net/if.h>
#include <netinet/in.h>
#include <arpa/inet.h>
#include "common/StringUtil.h"
#if defined(__FreeBSD__) || (__APPLE__)
#include <sys/param.h>
#include <sys/sysctl.h>
#include <sys/socket.h>
#include <net/if.h>
#include <net/route.h>
#include "common/Assertions.h"
#endif
#endif
#include "AdapterUtils.h"
using namespace PacketReader::IP;
#ifdef _WIN32
bool AdapterUtils::GetWin32Adapter(const std::string& name, PIP_ADAPTER_ADDRESSES adapter, std::unique_ptr<IP_ADAPTER_ADDRESSES[]>* buffer)
{
int neededSize = 128;
std::unique_ptr<IP_ADAPTER_ADDRESSES[]> AdapterInfo = std::make_unique<IP_ADAPTER_ADDRESSES[]>(neededSize);
ULONG dwBufLen = sizeof(IP_ADAPTER_ADDRESSES) * neededSize;
PIP_ADAPTER_ADDRESSES pAdapterInfo;
DWORD dwStatus = GetAdaptersAddresses(
AF_UNSPEC,
GAA_FLAG_INCLUDE_PREFIX | GAA_FLAG_INCLUDE_GATEWAYS,
NULL,
AdapterInfo.get(),
&dwBufLen);
if (dwStatus == ERROR_BUFFER_OVERFLOW)
{
DevCon.WriteLn("DEV9: GetWin32Adapter() buffer too small, resizing");
neededSize = dwBufLen / sizeof(IP_ADAPTER_ADDRESSES) + 1;
AdapterInfo = std::make_unique<IP_ADAPTER_ADDRESSES[]>(neededSize);
dwBufLen = sizeof(IP_ADAPTER_ADDRESSES) * neededSize;
DevCon.WriteLn("DEV9: New size %i", neededSize);
dwStatus = GetAdaptersAddresses(
AF_UNSPEC,
GAA_FLAG_INCLUDE_PREFIX | GAA_FLAG_INCLUDE_GATEWAYS,
NULL,
AdapterInfo.get(),
&dwBufLen);
}
if (dwStatus != ERROR_SUCCESS)
return false;
pAdapterInfo = AdapterInfo.get();
do
{
if (strcmp(pAdapterInfo->AdapterName, name.c_str()) == 0)
{
*adapter = *pAdapterInfo;
buffer->swap(AdapterInfo);
return true;
}
pAdapterInfo = pAdapterInfo->Next;
} while (pAdapterInfo);
return false;
}
bool AdapterUtils::GetWin32AdapterAuto(PIP_ADAPTER_ADDRESSES adapter, std::unique_ptr<IP_ADAPTER_ADDRESSES[]>* buffer)
{
int neededSize = 128;
std::unique_ptr<IP_ADAPTER_ADDRESSES[]> AdapterInfo = std::make_unique<IP_ADAPTER_ADDRESSES[]>(neededSize);
ULONG dwBufLen = sizeof(IP_ADAPTER_ADDRESSES) * neededSize;
PIP_ADAPTER_ADDRESSES pAdapter;
DWORD dwStatus = GetAdaptersAddresses(
AF_UNSPEC,
GAA_FLAG_INCLUDE_PREFIX | GAA_FLAG_INCLUDE_GATEWAYS,
NULL,
AdapterInfo.get(),
&dwBufLen);
if (dwStatus == ERROR_BUFFER_OVERFLOW)
{
DevCon.WriteLn("DEV9: PCAPGetWin32Adapter() buffer too small, resizing");
//
neededSize = dwBufLen / sizeof(IP_ADAPTER_ADDRESSES) + 1;
AdapterInfo = std::make_unique<IP_ADAPTER_ADDRESSES[]>(neededSize);
dwBufLen = sizeof(IP_ADAPTER_ADDRESSES) * neededSize;
DevCon.WriteLn("DEV9: New size %i", neededSize);
dwStatus = GetAdaptersAddresses(
AF_UNSPEC,
GAA_FLAG_INCLUDE_PREFIX | GAA_FLAG_INCLUDE_GATEWAYS,
NULL,
AdapterInfo.get(),
&dwBufLen);
}
if (dwStatus != ERROR_SUCCESS)
return 0;
pAdapter = AdapterInfo.get();
do
{
if (pAdapter->IfType != IF_TYPE_SOFTWARE_LOOPBACK &&
pAdapter->OperStatus == IfOperStatusUp)
{
//Search for an adapter with;
//IPv4 Address
//DNS
//Gateway
bool hasIPv4 = false;
bool hasDNS = false;
bool hasGateway = false;
//IPv4
if (GetAdapterIP(pAdapter).has_value())
hasIPv4 = true;
//DNS
if (GetDNS(pAdapter).size() > 0)
hasDNS = true;
//Gateway
if (GetGateways(pAdapter).size() > 0)
hasGateway = true;
if (hasIPv4 && hasDNS && hasGateway)
{
*adapter = *pAdapter;
buffer->swap(AdapterInfo);
return true;
}
}
pAdapter = pAdapter->Next;
} while (pAdapter);
return false;
}
#elif defined(__POSIX__)
bool AdapterUtils::GetIfAdapter(const std::string& name, ifaddrs* adapter, ifaddrs** buffer)
{
ifaddrs* adapterInfo;
ifaddrs* pAdapter;
int error = getifaddrs(&adapterInfo);
if (error)
return false;
pAdapter = adapterInfo;
do
{
if (pAdapter->ifa_addr->sa_family == AF_INET && strcmp(pAdapter->ifa_name, name.c_str()) == 0)
break;
pAdapter = pAdapter->ifa_next;
} while (pAdapter);
if (pAdapter != nullptr)
{
*adapter = *pAdapter;
*buffer = adapterInfo;
return true;
}
freeifaddrs(adapterInfo);
return false;
}
bool AdapterUtils::GetIfAdapterAuto(ifaddrs* adapter, ifaddrs** buffer)
{
ifaddrs* adapterInfo;
ifaddrs* pAdapter;
int error = getifaddrs(&adapterInfo);
if (error)
return false;
pAdapter = adapterInfo;
do
{
if ((pAdapter->ifa_flags & IFF_LOOPBACK) == 0 &&
(pAdapter->ifa_flags & IFF_UP) != 0)
{
//Search for an adapter with;
//IPv4 Address
//Gateway
bool hasIPv4 = false;
bool hasGateway = false;
if (GetAdapterIP(pAdapter).has_value())
hasIPv4 = true;
if (GetGateways(pAdapter).size() > 0)
hasGateway = true;
if (hasIPv4 && hasGateway)
{
*adapter = *pAdapter;
*buffer = adapterInfo;
return true;
}
}
pAdapter = pAdapter->ifa_next;
} while (pAdapter);
freeifaddrs(adapterInfo);
return false;
}
#endif
//AdapterIP
#ifdef _WIN32
std::optional<IP_Address> AdapterUtils::GetAdapterIP(PIP_ADAPTER_ADDRESSES adapter)
{
PIP_ADAPTER_UNICAST_ADDRESS address = nullptr;
if (adapter != nullptr)
{
address = adapter->FirstUnicastAddress;
while (address != nullptr && address->Address.lpSockaddr->sa_family != AF_INET)
address = address->Next;
}
if (address != nullptr)
{
sockaddr_in* sockaddr = (sockaddr_in*)address->Address.lpSockaddr;
return *(IP_Address*)&sockaddr->sin_addr;
}
return std::nullopt;
}
#elif defined(__POSIX__)
std::optional<IP_Address> AdapterUtils::GetAdapterIP(ifaddrs* adapter)
{
sockaddr* address = nullptr;
if (adapter != nullptr)
{
if (adapter->ifa_addr != nullptr && adapter->ifa_addr->sa_family == AF_INET)
address = adapter->ifa_addr;
}
if (address != nullptr)
{
sockaddr_in* sockaddr = (sockaddr_in*)address;
return *(IP_Address*)&sockaddr->sin_addr;
}
return std::nullopt;
}
#endif
//Gateways
#ifdef _WIN32
std::vector<IP_Address> AdapterUtils::GetGateways(PIP_ADAPTER_ADDRESSES adapter)
{
if (adapter == nullptr)
return {};
std::vector<IP_Address> collection;
PIP_ADAPTER_GATEWAY_ADDRESS address = adapter->FirstGatewayAddress;
while (address != nullptr)
{
if (address->Address.lpSockaddr->sa_family == AF_INET)
{
sockaddr_in* sockaddr = (sockaddr_in*)address->Address.lpSockaddr;
collection.push_back(*(IP_Address*)&sockaddr->sin_addr);
}
address = address->Next;
}
return collection;
}
#elif defined(__POSIX__)
#ifdef __linux__
std::vector<IP_Address> AdapterUtils::GetGateways(ifaddrs* adapter)
{
///proc/net/route contains some information about gateway addresses,
//and separates the information about by each interface.
if (adapter == nullptr)
return {};
std::vector<IP_Address> collection;
std::vector<std::string> routeLines;
std::fstream route("/proc/net/route", std::ios::in);
if (route.fail())
{
route.close();
Console.Error("DEV9: Failed to open /proc/net/route");
return collection;
}
std::string line;
while (std::getline(route, line))
routeLines.push_back(line);
route.close();
//Columns are as follows (first-line header):
//Iface Destination Gateway Flags RefCnt Use Metric Mask MTU Window IRTT
for (size_t i = 1; i < routeLines.size(); i++)
{
std::string line = routeLines[i];
if (line.rfind(adapter->ifa_name, 0) == 0)
{
std::vector<std::string_view> split = StringUtil::SplitString(line, '\t', true);
std::string gatewayIPHex{split[2]};
int addressValue = std::stoi(gatewayIPHex, 0, 16);
//Skip device routes without valid NextHop IP address
if (addressValue != 0)
{
IP_Address gwIP = *(IP_Address*)&addressValue;
collection.push_back(gwIP);
}
}
}
return collection;
}
#elif defined(__FreeBSD__) || (__APPLE__)
std::vector<IP_Address> AdapterUtils::GetGateways(ifaddrs* adapter)
{
if (adapter == nullptr)
return {};
std::vector<IP_Address> collection;
//Get index for our adapter by matching the adapter name
int ifIndex = -1;
struct if_nameindex* ifNI;
ifNI = if_nameindex();
if (ifNI == nullptr)
{
Console.Error("DEV9: if_nameindex Failed");
return collection;
}
struct if_nameindex* i = ifNI;
while (i->if_index != 0 && i->if_name != nullptr)
{
if (strcmp(i->if_name, adapter->ifa_name) == 0)
{
ifIndex = i->if_index;
break;
}
i++;
}
if_freenameindex(ifNI);
//Check if we found the adapter
if (ifIndex == -1)
{
Console.Error("DEV9: Failed to get index for adapter");
return collection;
}
//Find the gateway by looking though the routing information
int name[] = {CTL_NET, PF_ROUTE, 0, AF_INET, NET_RT_DUMP, 0};
size_t bufferLen = 0;
if (sysctl(name, 6, NULL, &bufferLen, NULL, 0) != 0)
{
Console.Error("DEV9: Failed to perform NET_RT_DUMP");
return collection;
}
//len is an estimate, double it to be safe
bufferLen *= 2;
std::unique_ptr<u8[]> buffer = std::make_unique<u8[]>(bufferLen);
if (sysctl(name, 6, buffer.get(), &bufferLen, NULL, 0) != 0)
{
Console.Error("DEV9: Failed to perform NET_RT_DUMP");
return collection;
}
rt_msghdr* hdr;
for (size_t i = 0; i < bufferLen; i += hdr->rtm_msglen)
{
hdr = (rt_msghdr*)&buffer[i];
if (hdr->rtm_flags & RTF_GATEWAY && hdr->rtm_addrs & RTA_GATEWAY && (hdr->rtm_index == ifIndex))
{
sockaddr* sockaddrs = (sockaddr*)(hdr + 1);
pxAssert(sockaddrs[RTAX_DST].sa_family == AF_INET);
//Default gateway has no destination address
sockaddr_in* sockaddr = (sockaddr_in*)&sockaddrs[RTAX_DST];
if (sockaddr->sin_addr.s_addr != 0)
continue;
sockaddr = (sockaddr_in*)&sockaddrs[RTAX_GATEWAY];
IP_Address gwIP = *(IP_Address*)&sockaddr->sin_addr;
collection.push_back(gwIP);
}
}
return collection;
}
#else
std::vector<IP_Address> AdapterUtils::GetGateways(ifaddrs* adapter)
{
Console.Error("DEV9: Unsupported OS, can't find Gateway");
return {};
}
#endif
#endif
//DNS
#ifdef _WIN32
std::vector<IP_Address> AdapterUtils::GetDNS(PIP_ADAPTER_ADDRESSES adapter)
{
if (adapter == nullptr)
return {};
std::vector<IP_Address> collection;
PIP_ADAPTER_DNS_SERVER_ADDRESS address = adapter->FirstDnsServerAddress;
while (address != nullptr)
{
if (address->Address.lpSockaddr->sa_family == AF_INET)
{
sockaddr_in* sockaddr = (sockaddr_in*)address->Address.lpSockaddr;
collection.push_back(*(IP_Address*)&sockaddr->sin_addr);
}
address = address->Next;
}
return collection;
}
#elif defined(__POSIX__)
std::vector<IP_Address> AdapterUtils::GetDNS(ifaddrs* adapter)
{
//On Linux and OSX, DNS is system wide, not adapter specific, so we can ignore adapter
// Parse /etc/resolv.conf for all of the "nameserver" entries.
// These are the DNS servers the machine is configured to use.
// On OSX, this file is not directly used by most processes for DNS
// queries/routing, but it is automatically generated instead, with
// the machine's DNS servers listed in it.
if (adapter == nullptr)
return {};
std::vector<IP_Address> collection;
std::fstream servers("/etc/resolv.conf", std::ios::in);
if (servers.fail())
{
servers.close();
Console.Error("DEV9: Failed to open /etc/resolv.conf");
return collection;
}
std::string line;
std::vector<std::string> serversLines;
while (std::getline(servers, line))
serversLines.push_back(line);
servers.close();
const IP_Address systemdDNS{127, 0, 0, 53};
for (size_t i = 1; i < serversLines.size(); i++)
{
std::string line = serversLines[i];
if (line.rfind("nameserver", 0) == 0)
{
std::vector<std::string_view> split = StringUtil::SplitString(line, '\t', true);
if (split.size() == 1)
split = StringUtil::SplitString(line, ' ', true);
std::string dns{split[1]};
IP_Address address;
if (inet_pton(AF_INET, dns.c_str(), &address) != 1)
continue;
if (address == systemdDNS)
Console.Error("DEV9: systemd-resolved DNS server is not supported");
collection.push_back(address);
}
}
return collection;
}
#endif
+50
View File
@@ -0,0 +1,50 @@
/* PCSX2 - PS2 Emulator for PCs
* Copyright (C) 2002-2022 PCSX2 Dev Team
*
* PCSX2 is free software: you can redistribute it and/or modify it under the terms
* of the GNU Lesser General Public License as published by the Free Software Found-
* ation, either version 3 of the License, or (at your option) any later version.
*
* PCSX2 is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;
* without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR
* PURPOSE. See the GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License along with PCSX2.
* If not, see <http://www.gnu.org/licenses/>.
*/
#pragma once
#ifdef _WIN32
#include <winsock2.h>
#include <iphlpapi.h>
#elif defined(__POSIX__)
#include <sys/types.h>
#include <ifaddrs.h>
#endif
#include <string>
#include <optional>
#include "DEV9/PacketReader/IP/IP_Address.h"
namespace AdapterUtils
{
#ifdef _WIN32
bool GetWin32Adapter(const std::string& name, PIP_ADAPTER_ADDRESSES adapter, std::unique_ptr<IP_ADAPTER_ADDRESSES[]>* buffer);
bool GetWin32AdapterAuto(PIP_ADAPTER_ADDRESSES adapter, std::unique_ptr<IP_ADAPTER_ADDRESSES[]>* buffer);
std::optional<PacketReader::IP::IP_Address> GetAdapterIP(PIP_ADAPTER_ADDRESSES adapter);
//Mask
std::vector<PacketReader::IP::IP_Address> GetGateways(PIP_ADAPTER_ADDRESSES adapter);
std::vector<PacketReader::IP::IP_Address> GetDNS(PIP_ADAPTER_ADDRESSES adapter);
#elif defined(__POSIX__)
bool GetIfAdapter(const std::string& name, ifaddrs* adapter, ifaddrs** buffer);
bool GetIfAdapterAuto(ifaddrs* adapter, ifaddrs** buffer);
std::optional<PacketReader::IP::IP_Address> GetAdapterIP(ifaddrs* adapter);
//Mask
std::vector<PacketReader::IP::IP_Address> GetGateways(ifaddrs* adapter);
std::vector<PacketReader::IP::IP_Address> GetDNS(ifaddrs* adapter);
#endif
}; // namespace AdapterUtils
+15 -245
View File
@@ -37,6 +37,7 @@
#include "DEV9/PacketReader/IP/UDP/DHCP/DHCP_Packet.h"
#include "DEV9/DEV9.h"
#include "DEV9/AdapterUtils.h"
using namespace PacketReader;
using namespace PacketReader::IP;
@@ -110,195 +111,6 @@ namespace InternalServers
AutoBroadcast(ps2IP, netmask);
}
#ifdef __POSIX__
//skipsEmpty
std::vector<std::string> DHCP_Server::SplitString(std::string str, char delimiter)
{
std::vector<std::string> ret;
size_t last = 0;
size_t next = 0;
std::string token;
while ((next = str.find(delimiter, last)) != std::string::npos)
{
token = str.substr(last, next - last);
if (next != last)
ret.push_back(token);
last = next + 1;
}
token = str.substr(last);
if (next != last)
ret.push_back(token);
return ret;
}
#ifdef __linux__
std::vector<IP_Address> DHCP_Server::GetGatewaysLinux(char* interfaceName)
{
///proc/net/route contains some information about gateway addresses,
//and separates the information about by each interface.
std::vector<IP_Address> collection;
std::vector<std::string> routeLines;
std::fstream route("/proc/net/route", std::ios::in);
if (route.fail())
{
route.close();
Console.Error("DHCP: Failed to open /proc/net/route");
return collection;
}
std::string line;
while (std::getline(route, line))
routeLines.push_back(line);
route.close();
//Columns are as follows (first-line header):
//Iface Destination Gateway Flags RefCnt Use Metric Mask MTU Window IRTT
for (size_t i = 1; i < routeLines.size(); i++)
{
std::string line = routeLines[i];
if (line.rfind(interfaceName, 0) == 0)
{
std::vector<std::string> split = SplitString(line, '\t');
std::string gatewayIPHex = split[2];
int addressValue = std::stoi(gatewayIPHex, 0, 16);
//Skip device routes without valid NextHop IP address
if (addressValue != 0)
{
IP_Address gwIP = *(IP_Address*)&addressValue;
collection.push_back(gwIP);
}
}
}
return collection;
}
#elif defined(__FreeBSD__) || (__APPLE__)
std::vector<IP_Address> DHCP_Server::GetGatewaysBSD(char* interfaceName)
{
std::vector<IP_Address> collection;
//Get index for our adapter by matching the adapter name
int ifIndex = -1;
struct if_nameindex* ifNI;
ifNI = if_nameindex();
if (ifNI == nullptr)
{
Console.Error("DHCP: if_nameindex Failed");
return collection;
}
struct if_nameindex* i = ifNI;
while (i->if_index != 0 && i->if_name != nullptr)
{
if (strcmp(i->if_name, interfaceName) == 0)
{
ifIndex = i->if_index;
break;
}
i++;
}
if_freenameindex(ifNI);
//Check if we found the adapter
if (ifIndex == -1)
{
Console.Error("DHCP: Failed to get index for adapter");
return collection;
}
//Find the gateway by looking though the routing information
int name[] = {CTL_NET, PF_ROUTE, 0, AF_INET, NET_RT_DUMP, 0};
size_t bufferLen = 0;
if (sysctl(name, 6, NULL, &bufferLen, NULL, 0) != 0)
{
Console.Error("DHCP: Failed to perform NET_RT_DUMP");
return collection;
}
//len is an estimate, double it to be safe
bufferLen *= 2;
std::unique_ptr<u8[]> buffer = std::make_unique<u8[]>(bufferLen);
if (sysctl(name, 6, buffer.get(), &bufferLen, NULL, 0) != 0)
{
Console.Error("DHCP: Failed to perform NET_RT_DUMP");
return collection;
}
rt_msghdr* hdr;
for (size_t i = 0; i < bufferLen; i += hdr->rtm_msglen)
{
hdr = (rt_msghdr*)&buffer[i];
if (hdr->rtm_flags & RTF_GATEWAY && hdr->rtm_addrs & RTA_GATEWAY && (hdr->rtm_index == ifIndex))
{
sockaddr* sockaddrs = (sockaddr*)(hdr + 1);
pxAssert(sockaddrs[RTAX_DST].sa_family == AF_INET);
//Default gateway has no destination address
sockaddr_in* sockaddr = (sockaddr_in*)&sockaddrs[RTAX_DST];
if (sockaddr->sin_addr.s_addr != 0)
continue;
sockaddr = (sockaddr_in*)&sockaddrs[RTAX_GATEWAY];
IP_Address gwIP = *(IP_Address*)&sockaddr->sin_addr;
collection.push_back(gwIP);
}
}
return collection;
}
#endif
std::vector<IP_Address> DHCP_Server::GetDNSUnix()
{
//On Linux and OSX, DNS is system wide, not adapter specific, so we can ignore adapter
// Parse /etc/resolv.conf for all of the "nameserver" entries.
// These are the DNS servers the machine is configured to use.
// On OSX, this file is not directly used by most processes for DNS
// queries/routing, but it is automatically generated instead, with
// the machine's DNS servers listed in it.
std::vector<IP_Address> collection;
std::fstream servers("/etc/resolv.conf", std::ios::in);
if (servers.fail())
{
servers.close();
Console.Error("DHCP: Failed to open /etc/resolv.conf");
return collection;
}
std::string line;
std::vector<std::string> serversLines;
while (std::getline(servers, line))
serversLines.push_back(line);
servers.close();
const IP_Address systemdDNS{127, 0, 0, 53};
for (size_t i = 1; i < serversLines.size(); i++)
{
std::string line = serversLines[i];
if (line.rfind("nameserver", 0) == 0)
{
std::vector<std::string> split = SplitString(line, '\t');
if (split.size() == 1)
split = SplitString(line, ' ');
std::string dns = split[1];
IP_Address address;
if (inet_pton(AF_INET, dns.c_str(), &address) != 1)
continue;
if (address == systemdDNS)
Console.Error("DHCP: systemd-resolved DNS server is not supported");
collection.push_back(address);
}
}
return collection;
}
#endif
#ifdef _WIN32
void DHCP_Server::AutoNetmask(PIP_ADAPTER_ADDRESSES adapter)
{
@@ -316,23 +128,6 @@ namespace InternalServers
}
}
}
void DHCP_Server::AutoGateway(PIP_ADAPTER_ADDRESSES adapter)
{
if (adapter != nullptr)
{
PIP_ADAPTER_GATEWAY_ADDRESS address = adapter->FirstGatewayAddress;
while (address != nullptr && address->Address.lpSockaddr->sa_family != AF_INET)
address = address->Next;
if (address != nullptr)
{
sockaddr_in* sockaddr = (sockaddr_in*)address->Address.lpSockaddr;
gateway = *(IP_Address*)&sockaddr->sin_addr;
}
}
}
#elif defined(__POSIX__)
void DHCP_Server::AutoNetmask(ifaddrs* adapter)
{
@@ -345,53 +140,28 @@ namespace InternalServers
}
}
}
void DHCP_Server::AutoGateway(ifaddrs* adapter)
{
if (adapter != nullptr)
{
#ifdef __linux__
std::vector<IP_Address> gateways = GetGatewaysLinux(adapter->ifa_name);
if (gateways.size() > 0)
gateway = gateways[0];
#elif defined(__FreeBSD__) || (__APPLE__)
std::vector<IP_Address> gateways = GetGatewaysBSD(adapter->ifa_name);
if (gateways.size() > 0)
gateway = gateways[0];
#else
Console.Error("DHCP: Unsupported OS, can't find Gateway");
#endif
}
}
#endif
#ifdef _WIN32
void DHCP_Server::AutoDNS(PIP_ADAPTER_ADDRESSES adapter, bool autoDNS1, bool autoDNS2)
void DHCP_Server::AutoGateway(PIP_ADAPTER_ADDRESSES adapter)
#elif defined(__POSIX__)
void DHCP_Server::AutoGateway(ifaddrs* adapter)
#endif
{
std::vector<IP_Address> dnsIPs;
std::vector<IP_Address> gateways = AdapterUtils::GetGateways(adapter);
if (adapter != nullptr)
{
PIP_ADAPTER_DNS_SERVER_ADDRESS address = adapter->FirstDnsServerAddress;
while (address != nullptr)
{
if (address->Address.lpSockaddr->sa_family == AF_INET)
{
sockaddr_in* sockaddr = (sockaddr_in*)address->Address.lpSockaddr;
dnsIPs.push_back(*(IP_Address*)&sockaddr->sin_addr);
}
address = address->Next;
}
}
if (gateways.size() > 0)
gateway = gateways[0];
}
#ifdef _WIN32
void DHCP_Server::AutoDNS(PIP_ADAPTER_ADDRESSES adapter, bool autoDNS1, bool autoDNS2)
#elif defined(__POSIX__)
void DHCP_Server::AutoDNS(ifaddrs* adapter, bool autoDNS1, bool autoDNS2)
{
std::vector<IP_Address> dnsIPs = GetDNSUnix();
#endif
{
std::vector<IP_Address> dnsIPs = AdapterUtils::GetDNS(adapter);
if (autoDNS1)
{
//Auto DNS1
-11
View File
@@ -61,20 +61,9 @@ namespace InternalServers
PacketReader::IP::UDP::UDP_Packet* Recv();
bool Send(PacketReader::IP::UDP::UDP_Packet* payload);
#ifdef __linux__
static std::vector<PacketReader::IP::IP_Address> GetGatewaysLinux(char* interfaceName);
#elif defined(__FreeBSD__) || (__APPLE__)
static std::vector<PacketReader::IP::IP_Address> GetGatewaysBSD(char* interfaceName);
#endif
~DHCP_Server();
private:
#ifdef __POSIX__
static std::vector<std::string> SplitString(std::string str, char delimiter);
static std::vector<PacketReader::IP::IP_Address> GetDNSUnix();
#endif
#ifdef _WIN32
void AutoNetmask(PIP_ADAPTER_ADDRESSES adapter);
void AutoGateway(PIP_ADAPTER_ADDRESSES adapter);
+6 -33
View File
@@ -38,6 +38,7 @@
#include "DEV9/PacketReader/IP/UDP/DNS/DNS_Packet.h"
#include "DEV9/DEV9.h"
#include "DEV9/AdapterUtils.h"
using namespace PacketReader;
using namespace PacketReader::IP;
@@ -101,41 +102,13 @@ namespace InternalServers
#endif
{
localhostIP = {127, 0, 0, 1};
//Find IPv4 Address
#ifdef _WIN32
PIP_ADAPTER_UNICAST_ADDRESS address = nullptr;
if (adapter != nullptr)
{
PIP_ADAPTER_ADDRESSES info = (PIP_ADAPTER_ADDRESSES)adapter;
address = info->FirstUnicastAddress;
while (address != nullptr && address->Address.lpSockaddr->sa_family != AF_INET)
address = address->Next;
}
if (address != nullptr)
{
sockaddr_in* sockaddr = (sockaddr_in*)address->Address.lpSockaddr;
localhostIP = *(IP_Address*)&sockaddr->sin_addr;
}
std::optional<IP_Address> adapterIP = AdapterUtils::GetAdapterIP(adapter);
if (adapterIP.has_value())
localhostIP = adapterIP.value();
else
Console.Error("DEV9: Failed To Get Adapter IP");
#elif defined(__POSIX__)
sockaddr* address = nullptr;
if (adapter != nullptr)
{
ifaddrs* info = (ifaddrs*)adapter;
if (info->ifa_addr != nullptr && info->ifa_addr->sa_family == AF_INET)
address = info->ifa_addr;
}
if (address != nullptr)
{
sockaddr_in* sockaddr = (sockaddr_in*)address;
localhostIP = *(IP_Address*)&sockaddr->sin_addr;
}
else
Console.Error("DEV9: Failed To Get Adapter IP");
#endif
LoadHostList();
}
@@ -229,7 +202,7 @@ namespace InternalServers
bool DNS_Server::CheckHostList(std::string url, DNS_State* state)
{
std::transform(url.begin(), url.end(), url.begin(),
[](unsigned char c) { return std::tolower(c); });
[](unsigned char c) { return std::tolower(c); });
auto f = hosts.find(url);
if (f != hosts.end())
+26 -99
View File
@@ -37,11 +37,16 @@
#include <stdarg.h>
#include "pcap_io.h"
#include "DEV9.h"
#include "AdapterUtils.h"
#include "net.h"
#ifndef PCAP_NETMASK_UNKNOWN
#define PCAP_NETMASK_UNKNOWN 0xffffffff
#endif
#ifdef _WIN32
#define PCAPPREFIX "\\Device\\NPF_"
#endif
pcap_t* adhandle;
pcap_dumper_t* dump_pcap = nullptr;
char errbuf[PCAP_ERRBUF_SIZE];
@@ -58,94 +63,6 @@ ip_address ps2_ip;
mac_address ps2_mac;
mac_address host_mac;
#ifdef _WIN32
//IP_ADAPTER_ADDRESSES is a structure that contains ptrs to data in other regions
//of the buffer, se we need to return both so the caller can free the buffer
//after it's finished reading the needed data from IP_ADAPTER_ADDRESSES
bool PCAPGetWin32Adapter(const std::string& name, PIP_ADAPTER_ADDRESSES adapter, std::unique_ptr<IP_ADAPTER_ADDRESSES[]>* buffer)
{
const int guidindex = strlen("\\Device\\NPF_");
int neededSize = 128;
std::unique_ptr<IP_ADAPTER_ADDRESSES[]> AdapterInfo = std::make_unique<IP_ADAPTER_ADDRESSES[]>(neededSize);
ULONG dwBufLen = sizeof(IP_ADAPTER_ADDRESSES) * neededSize;
PIP_ADAPTER_ADDRESSES pAdapterInfo;
DWORD dwStatus = GetAdaptersAddresses(
AF_UNSPEC,
GAA_FLAG_INCLUDE_PREFIX | GAA_FLAG_INCLUDE_GATEWAYS,
NULL,
AdapterInfo.get(),
&dwBufLen);
if (dwStatus == ERROR_BUFFER_OVERFLOW)
{
DevCon.WriteLn("DEV9: GetWin32Adapter() buffer too small, resizing");
neededSize = dwBufLen / sizeof(IP_ADAPTER_ADDRESSES) + 1;
AdapterInfo = std::make_unique<IP_ADAPTER_ADDRESSES[]>(neededSize);
dwBufLen = sizeof(IP_ADAPTER_ADDRESSES) * neededSize;
DevCon.WriteLn("DEV9: New size %i", neededSize);
dwStatus = GetAdaptersAddresses(
AF_UNSPEC,
GAA_FLAG_INCLUDE_PREFIX | GAA_FLAG_INCLUDE_GATEWAYS,
NULL,
AdapterInfo.get(),
&dwBufLen);
}
if (dwStatus != ERROR_SUCCESS)
return false;
pAdapterInfo = AdapterInfo.get();
do
{
if (0 == strcmp(pAdapterInfo->AdapterName, &name.c_str()[guidindex]))
{
*adapter = *pAdapterInfo;
buffer->swap(AdapterInfo);
return true;
}
pAdapterInfo = pAdapterInfo->Next;
} while (pAdapterInfo);
return false;
}
#elif defined(__POSIX__)
//getifaddrs is not POSIX, but is supported on MAC & Linux
bool PCAPGetIfAdapter(const std::string& name, ifaddrs* adapter, ifaddrs** buffer)
{
//Note, we don't support "any" adapter, but that also fails in pcap_io_init()
ifaddrs* adapterInfo;
ifaddrs* pAdapter;
int error = getifaddrs(&adapterInfo);
if (error)
return false;
pAdapter = adapterInfo;
do
{
if (pAdapter->ifa_addr != nullptr && pAdapter->ifa_addr->sa_family == AF_INET && strcmp(pAdapter->ifa_name, name.c_str()) == 0)
break;
pAdapter = pAdapter->ifa_next;
} while (pAdapter);
if (pAdapter != nullptr)
{
*adapter = *pAdapter;
*buffer = adapterInfo;
return true;
}
freeifaddrs(adapterInfo);
return false;
}
#endif
// Fetches the MAC address and prints it
int GetMACAddress(const std::string& adapter, mac_address* addr)
{
@@ -154,7 +71,7 @@ int GetMACAddress(const std::string& adapter, mac_address* addr)
IP_ADAPTER_ADDRESSES adapterInfo;
std::unique_ptr<IP_ADAPTER_ADDRESSES[]> buffer;
if (PCAPGetWin32Adapter(adapter, &adapterInfo, &buffer))
if (AdapterUtils::GetWin32Adapter(adapter, &adapterInfo, &buffer))
{
memcpy(addr, adapterInfo.PhysicalAddress, 6);
retval = 1;
@@ -190,8 +107,13 @@ int pcap_io_init(const std::string& adapter, bool switched, mac_address virtual_
pcap_io_switched = switched;
#ifdef _WIN32
std::string pcapAdapter = PCAPPREFIX + adapter;
#else
std::string pcapAdapter = adapter;
#endif
/* Open the adapter */
if ((adhandle = pcap_open_live(adapter.c_str(), // name of the device
if ((adhandle = pcap_open_live(pcapAdapter.c_str(), // name of the device
65536, // portion of the packet to capture.
// 65536 grants that the whole packet will be captured on all the MACs.
switched ? 1 : 0,
@@ -390,7 +312,7 @@ PCAPAdapter::PCAPAdapter()
#ifdef _WIN32
IP_ADAPTER_ADDRESSES adapter;
std::unique_ptr<IP_ADAPTER_ADDRESSES[]> buffer;
if (PCAPGetWin32Adapter(EmuConfig.DEV9.EthDevice, &adapter, &buffer))
if (AdapterUtils::GetWin32Adapter(EmuConfig.DEV9.EthDevice, &adapter, &buffer))
InitInternalServer(&adapter);
else
{
@@ -400,7 +322,7 @@ PCAPAdapter::PCAPAdapter()
#elif defined(__POSIX__)
ifaddrs adapter;
ifaddrs* buffer;
if (PCAPGetIfAdapter(EmuConfig.DEV9.EthDevice, &adapter, &buffer))
if (AdapterUtils::GetIfAdapter(EmuConfig.DEV9.EthDevice, &adapter, &buffer))
{
InitInternalServer(&adapter);
freeifaddrs(buffer);
@@ -462,14 +384,14 @@ void PCAPAdapter::reloadSettings()
#ifdef _WIN32
IP_ADAPTER_ADDRESSES adapter;
std::unique_ptr<IP_ADAPTER_ADDRESSES[]> buffer;
if (PCAPGetWin32Adapter(EmuConfig.DEV9.EthDevice, &adapter, &buffer))
if (AdapterUtils::GetWin32Adapter(EmuConfig.DEV9.EthDevice, &adapter, &buffer))
ReloadInternalServer(&adapter);
else
ReloadInternalServer(nullptr);
#elif defined(__POSIX__)
ifaddrs adapter;
ifaddrs* buffer;
if (PCAPGetIfAdapter(EmuConfig.DEV9.EthDevice, &adapter, &buffer))
if (AdapterUtils::GetIfAdapter(EmuConfig.DEV9.EthDevice, &adapter, &buffer))
{
ReloadInternalServer(&adapter);
freeifaddrs(buffer);
@@ -497,9 +419,7 @@ std::vector<AdapterEntry> PCAPAdapter::GetAdapters()
pcap_if_t* d;
if (pcap_findalldevs(&alldevs, errbuf) == -1)
{
return nic;
}
d = alldevs;
while (d != NULL)
@@ -508,12 +428,19 @@ std::vector<AdapterEntry> PCAPAdapter::GetAdapters()
entry.type = Pcsx2Config::DEV9Options::NetApi::PCAP_Switched;
#ifdef _WIN32
//guid
entry.guid = std::string(d->name);
if (!StringUtil::StartsWith(d->name, PCAPPREFIX))
{
Console.Error("PCAP: Unexpected Device: ", d->name);
d = d->next;
continue;
}
entry.guid = std::string(&d->name[strlen(PCAPPREFIX)]);
IP_ADAPTER_ADDRESSES adapterInfo;
std::unique_ptr<IP_ADAPTER_ADDRESSES[]> buffer;
if (PCAPGetWin32Adapter(entry.guid, &adapterInfo, &buffer))
if (AdapterUtils::GetWin32Adapter(entry.guid, &adapterInfo, &buffer))
entry.name = StringUtil::WideStringToUTF8String(std::wstring(adapterInfo.FriendlyName));
else
{
@@ -527,7 +454,7 @@ std::vector<AdapterEntry> PCAPAdapter::GetAdapters()
std::unique_ptr<wchar_t[]> buf = std::make_unique<wchar_t[]>(len_buf);
MultiByteToWideChar(CP_ACP, 0, d->description, len_desc, buf.get(), len_buf);
entry.name = StringUtil::WideStringToUTF8String(std::wstring(buf.get()));
}
#else
+22 -252
View File
@@ -17,7 +17,13 @@
#include "common/Assertions.h"
#include "common/StringUtil.h"
#ifdef __POSIX__
#ifdef _WIN32
#include <winsock2.h>
#include <iphlpapi.h>
#elif defined(__POSIX__)
#include <sys/types.h>
#include <ifaddrs.h>
#include <sys/socket.h>
#include <netinet/in.h>
#include <net/if.h>
@@ -27,6 +33,7 @@
#endif
#include "sockets.h"
#include "AdapterUtils.h"
#include "DEV9.h"
#include "Sessions/ICMP_Session/ICMP_Session.h"
@@ -158,7 +165,7 @@ SocketAdapter::SocketAdapter()
if (strcmp(EmuConfig.DEV9.EthDevice.c_str(), "Auto") != 0)
{
foundAdapter = GetWin32SelectedAdapter(EmuConfig.DEV9.EthDevice, &adapter, &buffer);
foundAdapter = AdapterUtils::GetWin32Adapter(EmuConfig.DEV9.EthDevice, &adapter, &buffer);
if (!foundAdapter)
{
@@ -166,17 +173,9 @@ SocketAdapter::SocketAdapter()
return;
}
PIP_ADAPTER_UNICAST_ADDRESS address = nullptr;
address = adapter.FirstUnicastAddress;
while (address != nullptr && address->Address.lpSockaddr->sa_family != AF_INET)
address = address->Next;
if (address != nullptr)
{
sockaddr_in* sockaddr = (sockaddr_in*)address->Address.lpSockaddr;
adapterIP = *(IP_Address*)&sockaddr->sin_addr;
}
std::optional<IP_Address> adIP = AdapterUtils::GetAdapterIP(&adapter);
if (adIP.has_value())
adapterIP = adIP.value();
else
{
Console.Error("DEV9: Socket: Failed To Get Adapter IP");
@@ -185,7 +184,7 @@ SocketAdapter::SocketAdapter()
}
else
{
foundAdapter = GetWin32AutoAdapter(&adapter, &buffer);
foundAdapter = AdapterUtils::GetWin32AdapterAuto(&adapter, &buffer);
adapterIP = {0};
if (!foundAdapter)
@@ -200,7 +199,7 @@ SocketAdapter::SocketAdapter()
if (strcmp(EmuConfig.DEV9.EthDevice.c_str(), "Auto") != 0)
{
foundAdapter = GetIfSelectedAdapter(EmuConfig.DEV9.EthDevice, &adapter, &buffer);
foundAdapter = AdapterUtils::GetIfAdapter(EmuConfig.DEV9.EthDevice, &adapter, &buffer);
if (!foundAdapter)
{
@@ -208,16 +207,9 @@ SocketAdapter::SocketAdapter()
return;
}
sockaddr* address = nullptr;
if (adapter.ifa_addr != nullptr && adapter.ifa_addr->sa_family == AF_INET)
address = adapter.ifa_addr;
if (address != nullptr)
{
sockaddr_in* sockaddr = (sockaddr_in*)address;
adapterIP = *(IP_Address*)&sockaddr->sin_addr;
}
std::optional<IP_Address> adIP = AdapterUtils::GetAdapterIP(&adapter);
if (adIP.has_value())
adapterIP = adIP.value();
else
{
Console.Error("DEV9: Socket: Failed To Get Adapter IP");
@@ -227,7 +219,7 @@ SocketAdapter::SocketAdapter()
}
else
{
foundAdapter = GetIfAutoAdapter(&adapter, &buffer);
foundAdapter = AdapterUtils::GetIfAdapterAuto(&adapter, &buffer);
adapterIP = {0};
if (!foundAdapter)
@@ -297,228 +289,6 @@ SocketAdapter::SocketAdapter()
initialized = true;
}
#ifdef _WIN32
bool SocketAdapter::GetWin32SelectedAdapter(const std::string& name, PIP_ADAPTER_ADDRESSES adapter, std::unique_ptr<IP_ADAPTER_ADDRESSES[]>* buffer)
{
int neededSize = 128;
std::unique_ptr<IP_ADAPTER_ADDRESSES[]> AdapterInfo = std::make_unique<IP_ADAPTER_ADDRESSES[]>(neededSize);
ULONG dwBufLen = sizeof(IP_ADAPTER_ADDRESSES) * neededSize;
PIP_ADAPTER_ADDRESSES pAdapterInfo;
DWORD dwStatus = GetAdaptersAddresses(
AF_UNSPEC,
GAA_FLAG_INCLUDE_PREFIX | GAA_FLAG_INCLUDE_GATEWAYS,
NULL,
AdapterInfo.get(),
&dwBufLen);
if (dwStatus == ERROR_BUFFER_OVERFLOW)
{
DevCon.WriteLn("DEV9: GetWin32Adapter() buffer too small, resizing");
neededSize = dwBufLen / sizeof(IP_ADAPTER_ADDRESSES) + 1;
AdapterInfo = std::make_unique<IP_ADAPTER_ADDRESSES[]>(neededSize);
dwBufLen = sizeof(IP_ADAPTER_ADDRESSES) * neededSize;
DevCon.WriteLn("DEV9: New size %i", neededSize);
dwStatus = GetAdaptersAddresses(
AF_UNSPEC,
GAA_FLAG_INCLUDE_PREFIX | GAA_FLAG_INCLUDE_GATEWAYS,
NULL,
AdapterInfo.get(),
&dwBufLen);
}
if (dwStatus != ERROR_SUCCESS)
return false;
pAdapterInfo = AdapterInfo.get();
do
{
if (strcmp(pAdapterInfo->AdapterName, name.c_str()) == 0)
{
*adapter = *pAdapterInfo;
buffer->swap(AdapterInfo);
return true;
}
pAdapterInfo = pAdapterInfo->Next;
} while (pAdapterInfo);
return false;
}
bool SocketAdapter::GetWin32AutoAdapter(PIP_ADAPTER_ADDRESSES adapter, std::unique_ptr<IP_ADAPTER_ADDRESSES[]>* buffer)
{
int neededSize = 128;
std::unique_ptr<IP_ADAPTER_ADDRESSES[]> AdapterInfo = std::make_unique<IP_ADAPTER_ADDRESSES[]>(neededSize);
ULONG dwBufLen = sizeof(IP_ADAPTER_ADDRESSES) * neededSize;
PIP_ADAPTER_ADDRESSES pAdapterInfo;
DWORD dwStatus = GetAdaptersAddresses(
AF_UNSPEC,
GAA_FLAG_INCLUDE_PREFIX | GAA_FLAG_INCLUDE_GATEWAYS,
NULL,
AdapterInfo.get(),
&dwBufLen);
if (dwStatus == ERROR_BUFFER_OVERFLOW)
{
DevCon.WriteLn("DEV9: PCAPGetWin32Adapter() buffer too small, resizing");
//
neededSize = dwBufLen / sizeof(IP_ADAPTER_ADDRESSES) + 1;
AdapterInfo = std::make_unique<IP_ADAPTER_ADDRESSES[]>(neededSize);
dwBufLen = sizeof(IP_ADAPTER_ADDRESSES) * neededSize;
DevCon.WriteLn("DEV9: New size %i", neededSize);
dwStatus = GetAdaptersAddresses(
AF_UNSPEC,
GAA_FLAG_INCLUDE_PREFIX | GAA_FLAG_INCLUDE_GATEWAYS,
NULL,
AdapterInfo.get(),
&dwBufLen);
}
if (dwStatus != ERROR_SUCCESS)
return 0;
pAdapterInfo = AdapterInfo.get();
do
{
if (pAdapterInfo->IfType != IF_TYPE_SOFTWARE_LOOPBACK &&
pAdapterInfo->OperStatus == IfOperStatusUp)
{
//Search for an adapter with;
//IPv4 Address
//DNS
//Gateway
bool hasIPv4 = false;
bool hasDNS = false;
bool hasGateway = false;
//IPv4
PIP_ADAPTER_UNICAST_ADDRESS ipAddress = nullptr;
ipAddress = pAdapterInfo->FirstUnicastAddress;
while (ipAddress != nullptr && ipAddress->Address.lpSockaddr->sa_family != AF_INET)
ipAddress = ipAddress->Next;
if (ipAddress != nullptr)
hasIPv4 = true;
//DNS
PIP_ADAPTER_DNS_SERVER_ADDRESS dnsAddress = pAdapterInfo->FirstDnsServerAddress;
while (dnsAddress != nullptr && dnsAddress->Address.lpSockaddr->sa_family != AF_INET)
dnsAddress = dnsAddress->Next;
if (dnsAddress != nullptr)
hasDNS = true;
//Gateway
PIP_ADAPTER_GATEWAY_ADDRESS gatewayAddress = pAdapterInfo->FirstGatewayAddress;
while (gatewayAddress != nullptr && gatewayAddress->Address.lpSockaddr->sa_family != AF_INET)
gatewayAddress = gatewayAddress->Next;
if (gatewayAddress != nullptr)
hasGateway = true;
if (hasIPv4 && hasDNS && hasGateway)
{
*adapter = *pAdapterInfo;
buffer->swap(AdapterInfo);
return true;
}
}
pAdapterInfo = pAdapterInfo->Next;
} while (pAdapterInfo);
return false;
}
#elif defined(__POSIX__)
bool SocketAdapter::GetIfSelectedAdapter(const std::string& name, ifaddrs* adapter, ifaddrs** buffer)
{
ifaddrs* adapterInfo;
ifaddrs* pAdapter;
int error = getifaddrs(&adapterInfo);
if (error)
return false;
pAdapter = adapterInfo;
do
{
if (pAdapter->ifa_addr->sa_family == AF_INET && strcmp(pAdapter->ifa_name, name.c_str()) == 0)
break;
pAdapter = pAdapter->ifa_next;
} while (pAdapter);
if (pAdapter != nullptr)
{
*adapter = *pAdapter;
*buffer = adapterInfo;
return true;
}
freeifaddrs(adapterInfo);
return false;
}
bool SocketAdapter::GetIfAutoAdapter(ifaddrs* adapter, ifaddrs** buffer)
{
ifaddrs* adapterInfo;
ifaddrs* pAdapter;
int error = getifaddrs(&adapterInfo);
if (error)
return false;
pAdapter = adapterInfo;
do
{
if ((pAdapter->ifa_flags & IFF_LOOPBACK) == 0 &&
(pAdapter->ifa_flags & IFF_UP) != 0)
{
//Search for an adapter with;
//IPv4 Address
//Gateway
bool hasIPv4 = false;
bool hasGateway = false;
if (pAdapter->ifa_addr->sa_family == AF_INET)
hasIPv4 = true;
#ifdef __linux__
std::vector<IP_Address> gateways = InternalServers::DHCP_Server::GetGatewaysLinux(pAdapter->ifa_name);
if (gateways.size() > 0)
hasGateway = true;
#elif defined(__FreeBSD__) || (__APPLE__)
std::vector<IP_Address> gateways = InternalServers::DHCP_Server::GetGatewaysBSD(pAdapter->ifa_name);
if (gateways.size() > 0)
hasGateway = true;
#else
Console.Error("DHCP: Unsupported OS, can't find Gateway");
#endif
if (hasIPv4 && hasGateway)
{
*adapter = *pAdapter;
*buffer = adapterInfo;
return true;
}
}
pAdapter = pAdapter->ifa_next;
} while (pAdapter);
freeifaddrs(adapterInfo);
return false;
}
#endif
bool SocketAdapter::blocks()
{
return false;
@@ -664,18 +434,18 @@ void SocketAdapter::reloadSettings()
std::unique_ptr<IP_ADAPTER_ADDRESSES[]> buffer;
if (strcmp(EmuConfig.DEV9.EthDevice.c_str(), "Auto") != 0)
foundAdapter = GetWin32SelectedAdapter(EmuConfig.DEV9.EthDevice, &adapter, &buffer);
foundAdapter = AdapterUtils::GetWin32Adapter(EmuConfig.DEV9.EthDevice, &adapter, &buffer);
else
foundAdapter = GetWin32AutoAdapter(&adapter, &buffer);
foundAdapter = AdapterUtils::GetWin32AdapterAuto(&adapter, &buffer);
#elif defined(__POSIX__)
ifaddrs adapter;
ifaddrs* buffer;
if (strcmp(EmuConfig.DEV9.EthDevice.c_str(), "Auto") != 0)
foundAdapter = GetIfSelectedAdapter(EmuConfig.DEV9.EthDevice, &adapter, &buffer);
foundAdapter = AdapterUtils::GetIfAdapter(EmuConfig.DEV9.EthDevice, &adapter, &buffer);
else
foundAdapter = GetIfAutoAdapter(&adapter, &buffer);
foundAdapter = AdapterUtils::GetIfAdapterAuto(&adapter, &buffer);
#endif
const IP_Address ps2IP = {internalIP.bytes[0], internalIP.bytes[1], internalIP.bytes[2], 100};
-16
View File
@@ -16,14 +16,6 @@
#pragma once
#include <vector>
#ifdef _WIN32
#include <winsock2.h>
#include <iphlpapi.h>
#elif defined(__POSIX__)
#include <sys/types.h>
#include <ifaddrs.h>
#endif
#include "net.h"
#include "PacketReader/IP/IP_Packet.h"
@@ -64,14 +56,6 @@ public:
static AdapterOptions GetAdapterOptions();
private:
#ifdef _WIN32
bool GetWin32SelectedAdapter(const std::string& name, PIP_ADAPTER_ADDRESSES adapter, std::unique_ptr<IP_ADAPTER_ADDRESSES[]>* buffer);
bool GetWin32AutoAdapter(PIP_ADAPTER_ADDRESSES adapter, std::unique_ptr<IP_ADAPTER_ADDRESSES[]>* buffer);
#elif defined(__POSIX__)
bool GetIfSelectedAdapter(const std::string& name, ifaddrs* adapter, ifaddrs** buffer);
bool GetIfAutoAdapter(ifaddrs* adapter, ifaddrs** buffer);
#endif // _WIN32
bool SendIP(PacketReader::IP::IP_Packet* ipPkt);
bool SendICMP(Sessions::ConnectionKey Key, PacketReader::IP::IP_Packet* ipPkt);
bool SendIGMP(Sessions::ConnectionKey Key, PacketReader::IP::IP_Packet* ipPkt);
+49
View File
@@ -0,0 +1,49 @@
# Documentation Artifacts
- Source Directory: `/pcsx2/pcsx2/Docs`
- Release Directory: `/pcsx2/bin/Docs`
Docs should be written in the source directory first in an easily editable format. Currently, Markdown is the preferred format due to its simple markup and easy portability. GitHub's built-in preview functions are a huge benefit as well.
Visual Studio Code is a cross platform text editor/development platform that can handle Markdown syntax, plus extensions are available to enable in-editor previewing and PDF generation. However, this is not a requirement since other editors will support Markdown, and worst case GitHub supports editing Markdown files in-browser.
## How to Generate
### Setup
To generate the documentation artifacts into the release directory, you will require the following:
- `pandoc`
- Converts from Markdown to PDF
- `miktex` or something similar that provides `pdflatex`
- this is what generates the PDF file
#### Linux / MacOS
Consult pandoc's official documentation - https://pandoc.org/installing.html
#### Windows
In a PowerShell shell, run the following:
```ps1
> iwr -useb get.scoop.sh | iex
> scoop install pandoc latex
```
If you prefer Chocolatey or using the installer, consult pandoc's official documentation - https://pandoc.org/installing.html
### Usage
Run the following, this assumes you have access to bash, either by virtue of running on linux or through something like `git-bash` on Windows.
```bash
> ./gen-docs.sh
```
> You can override the default output directory like so - `OUT_DIR=<PATH> ./gen-docs.sh`
### Customizing Output
For generating the PDF's this popular template is used and has many features that could be useful https://github.com/Wandmalfarbe/pandoc-latex-template#usage reference it's documentation to take advantage of those if desired.
+16 -40
View File
@@ -1,49 +1,25 @@
# Documentation Artifacts
# PCSX2
- Source Directory: `/pcsx2/pcsx2/Docs`
- Release Directory: `/pcsx2/bin/Docs`
PCSX2 is a Playstation 2 emulator for Windows, Linux and Mac. The project has been going since 2001 and still going strong, with contributions from many very clever developers over the years! PCSX2 has compatibility upwards of 95% of the Playstation 2 library being playable.
Docs should be written in the source directory first in an easily editable format. Currently, Markdown is the preferred format due to its simple markup and easy portability. GitHub's built-in preview functions are a huge benefit as well.
## Basic usage
Visual Studio Code is a cross platform text editor/development platform that can handle Markdown syntax, plus extensions are available to enable in-editor previewing and PDF generation. However, this is not a requirement since other editors will support Markdown, and worst case GitHub supports editing Markdown files in-browser.
For information on how to set up the emulator, please see our [basic setup](https://pcsx2.net/guides/basic-setup/) guide.
## How to Generate
## Contributing
Pull requests are welcome and can be submitted at our [GitHub page](https://github.com/PCSX2/pcsx2/)
### Setup
Please make sure to follow our coding style guide and fill out the pull request forms with the appropriate information.
To generate the documentation artifacts into the release directory, you will require the following:
## License
[GPL 3.0](https://www.gnu.org/licenses/gpl-3.0.en.html)
- `pandoc`
- Converts from Markdown to PDF
## Donating to the project
If you wish to donate monetarily to the project you can do so via the [GitHub Sponsors](https://github.com/sponsors/PCSX2). All donations are gratefully received!
- `miktex` or something similar that provides `pdflatex`
- this is what generates the PDF file
## Thanks to the following monthly donators
We thank everybody for their contributions to the project, but we would like to give a special shout out to:
#### Linux / MacOS
Consult pandoc's official documentation - https://pandoc.org/installing.html
#### Windows
In a PowerShell shell, run the following:
```ps1
> iwr -useb get.scoop.sh | iex
> scoop install pandoc latex
```
If you prefer Chocolatey or using the installer, consult pandoc's official documentation - https://pandoc.org/installing.html
### Usage
Run the following, this assumes you have access to bash, either by virtue of running on linux or through something like `git-bash` on Windows.
```bash
> ./gen-docs.sh
```
> You can override the default output directory like so - `OUT_DIR=<PATH> ./gen-docs.sh`
### Customizing Output
For generating the PDF's this popular template is used and has many features that could be useful https://github.com/Wandmalfarbe/pandoc-latex-template#usage reference it's documentation to take advantage of those if desired.
- noigeaR
- xTVaser
- And 1 anonymous donator!
+15 -15
View File
@@ -95,8 +95,9 @@ D3D11HostDisplay::D3D11HostDisplay() = default;
D3D11HostDisplay::~D3D11HostDisplay()
{
pxAssertMsg(!m_context, "Context should have been destroyed by now");
pxAssertMsg(!m_swap_chain, "Swap chain should have been destroyed by now");
D3D11HostDisplay::DestroyRenderSurface();
m_context.Reset();
m_device.Reset();
}
HostDisplay::RenderAPI D3D11HostDisplay::GetRenderAPI() const
@@ -332,13 +333,6 @@ bool D3D11HostDisplay::InitializeRenderDevice(std::string_view shader_cache_dire
return true;
}
void D3D11HostDisplay::DestroyRenderDevice()
{
DestroyRenderSurface();
m_context.Reset();
m_device.Reset();
}
bool D3D11HostDisplay::MakeRenderContextCurrent()
{
return true;
@@ -757,7 +751,7 @@ void D3D11HostDisplay::EndPresent()
KickTimestampQuery();
}
void D3D11HostDisplay::CreateTimestampQueries()
bool D3D11HostDisplay::CreateTimestampQueries()
{
for (u32 i = 0; i < NUM_TIMESTAMP_QUERIES; i++)
{
@@ -768,12 +762,13 @@ void D3D11HostDisplay::CreateTimestampQueries()
if (FAILED(hr))
{
m_timestamp_queries = {};
return;
return false;
}
}
}
KickTimestampQuery();
return true;
}
void D3D11HostDisplay::DestroyTimestampQueries()
@@ -835,7 +830,7 @@ void D3D11HostDisplay::PopTimestampQuery()
void D3D11HostDisplay::KickTimestampQuery()
{
if (m_timestamp_query_started)
if (m_timestamp_query_started || !m_timestamp_queries[0][0])
return;
m_context->Begin(m_timestamp_queries[m_write_timestamp_query][0].Get());
@@ -843,16 +838,21 @@ void D3D11HostDisplay::KickTimestampQuery()
m_timestamp_query_started = true;
}
void D3D11HostDisplay::SetGPUTimingEnabled(bool enabled)
bool D3D11HostDisplay::SetGPUTimingEnabled(bool enabled)
{
if (m_gpu_timing_enabled == enabled)
return;
return true;
m_gpu_timing_enabled = enabled;
if (m_gpu_timing_enabled)
CreateTimestampQueries();
{
return CreateTimestampQueries();
}
else
{
DestroyTimestampQueries();
return true;
}
}
float D3D11HostDisplay::GetAndResetAccumulatedGPUTime()
+2 -3
View File
@@ -45,7 +45,6 @@ public:
bool CreateRenderDevice(const WindowInfo& wi, std::string_view adapter_name, VsyncMode vsync, bool threaded_presentation, bool debug_device) override;
bool InitializeRenderDevice(std::string_view shader_cache_directory, bool debug_device) override;
void DestroyRenderDevice() override;
bool MakeRenderContextCurrent() override;
bool DoneRenderContextCurrent() override;
@@ -69,7 +68,7 @@ public:
bool BeginPresent(bool frame_skip) override;
void EndPresent() override;
void SetGPUTimingEnabled(bool enabled) override;
bool SetGPUTimingEnabled(bool enabled) override;
float GetAndResetAccumulatedGPUTime() override;
static AdapterAndModeList StaticGetAdapterAndModeList();
@@ -87,7 +86,7 @@ protected:
bool CreateSwapChain(const DXGI_MODE_DESC* fullscreen_mode);
bool CreateSwapChainRTV();
void CreateTimestampQueries();
bool CreateTimestampQueries();
void DestroyTimestampQueries();
void PopTimestampQuery();
void KickTimestampQuery();
+15 -18
View File
@@ -51,8 +51,12 @@ D3D12HostDisplay::D3D12HostDisplay() = default;
D3D12HostDisplay::~D3D12HostDisplay()
{
pxAssertMsg(!g_d3d12_context, "Context should have been destroyed by now");
pxAssertMsg(!m_swap_chain, "Swap chain should have been destroyed by now");
if (g_d3d12_context)
{
g_d3d12_context->WaitForGPUIdle();
D3D12HostDisplay::DestroyRenderSurface();
g_d3d12_context->Destroy();
}
}
HostDisplay::RenderAPI D3D12HostDisplay::GetRenderAPI() const
@@ -85,7 +89,8 @@ bool D3D12HostDisplay::HasRenderSurface() const
return static_cast<bool>(m_swap_chain);
}
std::unique_ptr<HostDisplayTexture> D3D12HostDisplay::CreateTexture(u32 width, u32 height, const void* data, u32 data_stride, bool dynamic /* = false */)
std::unique_ptr<HostDisplayTexture> D3D12HostDisplay::CreateTexture(
u32 width, u32 height, const void* data, u32 data_stride, bool dynamic /* = false */)
{
D3D12::Texture tex;
if (!tex.Create(width, height, 1, DXGI_FORMAT_R8G8B8A8_UNORM, DXGI_FORMAT_R8G8B8A8_UNORM, DXGI_FORMAT_UNKNOWN, DXGI_FORMAT_UNKNOWN,
@@ -94,17 +99,17 @@ std::unique_ptr<HostDisplayTexture> D3D12HostDisplay::CreateTexture(u32 width, u
return {};
}
if (data && !tex.LoadData(0, 0, 0, width, height, data, data_stride))
if (data && !tex.LoadData(g_d3d12_context->GetInitCommandList(), 0, 0, 0, width, height, data, data_stride))
return {};
return std::make_unique<D3D12HostDisplayTexture>(std::move(tex));
}
void D3D12HostDisplay::UpdateTexture(HostDisplayTexture* texture, u32 x, u32 y, u32 width, u32 height,
const void* texture_data, u32 texture_data_stride)
void D3D12HostDisplay::UpdateTexture(
HostDisplayTexture* texture, u32 x, u32 y, u32 width, u32 height, const void* texture_data, u32 texture_data_stride)
{
static_cast<D3D12HostDisplayTexture*>(texture)->GetTexture().LoadData(0, x, y, width, height, texture_data,
texture_data_stride);
static_cast<D3D12HostDisplayTexture*>(texture)->GetTexture().LoadData(
g_d3d12_context->GetCommandList(), 0, x, y, width, height, texture_data, texture_data_stride);
}
bool D3D12HostDisplay::GetHostRefreshRate(float* refresh_rate)
@@ -203,15 +208,6 @@ bool D3D12HostDisplay::InitializeRenderDevice(std::string_view shader_cache_dire
return true;
}
void D3D12HostDisplay::DestroyRenderDevice()
{
g_d3d12_context->ExecuteCommandList(true);
DestroyRenderSurface();
if (g_d3d12_context)
g_d3d12_context->Destroy();
}
bool D3D12HostDisplay::MakeRenderContextCurrent()
{
return true;
@@ -644,9 +640,10 @@ void D3D12HostDisplay::EndPresent()
m_swap_chain->Present(static_cast<UINT>(vsync), 0);
}
void D3D12HostDisplay::SetGPUTimingEnabled(bool enabled)
bool D3D12HostDisplay::SetGPUTimingEnabled(bool enabled)
{
g_d3d12_context->SetEnableGPUTiming(enabled);
return true;
}
float D3D12HostDisplay::GetAndResetAccumulatedGPUTime()
+1 -2
View File
@@ -51,7 +51,6 @@ public:
bool CreateRenderDevice(const WindowInfo& wi, std::string_view adapter_name, VsyncMode vsync, bool threaded_presentation, bool debug_device) override;
bool InitializeRenderDevice(std::string_view shader_cache_directory, bool debug_device) override;
void DestroyRenderDevice() override;
bool MakeRenderContextCurrent() override;
bool DoneRenderContextCurrent() override;
@@ -75,7 +74,7 @@ public:
bool BeginPresent(bool frame_skip) override;
void EndPresent() override;
void SetGPUTimingEnabled(bool enabled) override;
bool SetGPUTimingEnabled(bool enabled) override;
float GetAndResetAccumulatedGPUTime() override;
static AdapterAndModeList StaticGetAdapterAndModeList();
+6 -1
View File
@@ -185,6 +185,11 @@ void INISettingsInterface::SetStringValue(const char* section, const char* key,
m_ini.SetValue(section, key, value, nullptr, true);
}
bool INISettingsInterface::ContainsValue(const char* section, const char* key) const
{
return (m_ini.GetValue(section, key, nullptr) != nullptr);
}
void INISettingsInterface::DeleteValue(const char* section, const char* key)
{
m_dirty = true;
@@ -198,7 +203,7 @@ void INISettingsInterface::ClearSection(const char* section)
m_ini.SetValue(section, nullptr, nullptr);
}
std::vector<std::string> INISettingsInterface::GetStringList(const char* section, const char* key)
std::vector<std::string> INISettingsInterface::GetStringList(const char* section, const char* key) const
{
std::list<CSimpleIniA::Entry> entries;
if (!m_ini.GetAllValues(section, key, entries))
+2 -1
View File
@@ -48,10 +48,11 @@ public:
void SetDoubleValue(const char* section, const char* key, double value) override;
void SetBoolValue(const char* section, const char* key, bool value) override;
void SetStringValue(const char* section, const char* key, const char* value) override;
bool ContainsValue(const char* section, const char* key) const override;
void DeleteValue(const char* section, const char* key) override;
void ClearSection(const char* section) override;
std::vector<std::string> GetStringList(const char* section, const char* key) override;
std::vector<std::string> GetStringList(const char* section, const char* key) const override;
void SetStringList(const char* section, const char* key, const std::vector<std::string>& items) override;
bool RemoveFromStringList(const char* section, const char* key, const char* item) override;
bool AddToStringList(const char* section, const char* key, const char* item) override;
+247 -55
View File
@@ -19,15 +19,18 @@
#include <cmath>
#include <deque>
#include <mutex>
#include <unordered_map>
#include "fmt/core.h"
#include "common/StringUtil.h"
#include "common/Timer.h"
#include "imgui.h"
#include "Config.h"
#include "Counters.h"
#include "Frontend/ImGuiManager.h"
#include "Frontend/InputManager.h"
#include "GS.h"
#include "GS/GS.h"
#include "Host.h"
@@ -39,10 +42,21 @@
#include "VMManager.h"
#endif
static void SetImGuiStyle();
static bool LoadFontData();
static void UnloadFontData();
static bool AddImGuiFonts();
namespace ImGuiManager
{
static void SetStyle();
static void SetKeyMap();
static bool LoadFontData();
static void UnloadFontData();
static bool AddImGuiFonts();
static ImFont* AddTextFont(float size);
static ImFont* AddFixedFont(float size);
static bool AddIconFonts(float size);
static void AcquirePendingOSDMessages();
static void DrawOSDMessages();
static void FormatProcessorStat(std::string& text, double usage, double time);
static void DrawPerformanceOverlay();
} // namespace ImGuiManager
static float s_global_scale = 1.0f;
@@ -53,6 +67,17 @@ static std::vector<u8> s_standard_font_data;
static std::vector<u8> s_fixed_font_data;
static std::vector<u8> s_icon_font_data;
static Common::Timer s_last_render_time;
#ifdef PCSX2_CORE
// cached copies of WantCaptureKeyboard/Mouse, used to know when to dispatch events
static std::atomic_bool s_imgui_wants_keyboard{false};
static std::atomic_bool s_imgui_wants_mouse{false};
// mapping of host key -> imgui key
static std::unordered_map<u32, ImGuiKey> s_imgui_key_map;
#endif
bool ImGuiManager::Initialize()
{
if (!LoadFontData())
@@ -62,23 +87,30 @@ bool ImGuiManager::Initialize()
}
HostDisplay* display = Host::GetHostDisplay();
ImGui::CreateContext();
ImGui::GetIO().IniFilename = nullptr;
s_global_scale = std::max(1.0f, display->GetWindowScale() * static_cast<float>(EmuConfig.GS.OsdScale / 100.0));
ImGui::GetIO().DisplayFramebufferScale = ImVec2(1, 1); // We already scale things ourselves, this would double-apply scaling
ImGui::GetIO().DisplaySize.x = static_cast<float>(display->GetWindowWidth());
ImGui::GetIO().DisplaySize.y = static_cast<float>(display->GetWindowHeight());
ImGui::GetStyle() = ImGuiStyle();
ImGui::GetStyle().WindowMinSize = ImVec2(1.0f, 1.0f);
SetImGuiStyle();
ImGui::GetStyle().ScaleAllSizes(s_global_scale);
ImGui::CreateContext();
ImGuiIO& io = ImGui::GetIO();
io.IniFilename = nullptr;
#ifdef PCSX2_CORE
io.BackendFlags |= ImGuiBackendFlags_HasGamepad;
io.BackendUsingLegacyKeyArrays = 0;
io.BackendUsingLegacyNavInputArray = 0;
io.ConfigFlags |= ImGuiConfigFlags_NavEnableKeyboard | ImGuiConfigFlags_NavEnableGamepad;
#endif
io.DisplayFramebufferScale = ImVec2(1, 1); // We already scale things ourselves, this would double-apply scaling
io.DisplaySize.x = static_cast<float>(display->GetWindowWidth());
io.DisplaySize.y = static_cast<float>(display->GetWindowHeight());
SetKeyMap();
SetStyle();
if (!display->CreateImGuiContext())
{
pxFailRel("Failed to create ImGui device context");
display->DestroyImGuiContext();
ImGui::DestroyContext();
UnloadFontData();
return false;
@@ -93,6 +125,9 @@ bool ImGuiManager::Initialize()
return false;
}
// don't need the font data anymore, save some memory
ImGui::GetIO().Fonts->ClearTexData();
NewFrame();
return true;
}
@@ -104,6 +139,10 @@ void ImGuiManager::Shutdown()
display->DestroyImGuiContext();
if (ImGui::GetCurrentContext())
ImGui::DestroyContext();
s_standard_font = nullptr;
s_fixed_font = nullptr;
UnloadFontData();
}
@@ -139,7 +178,7 @@ void ImGuiManager::UpdateScale()
ImGui::GetStyle() = ImGuiStyle();
ImGui::GetStyle().WindowMinSize = ImVec2(1.0f, 1.0f);
SetImGuiStyle();
SetStyle();
ImGui::GetStyle().ScaleAllSizes(scale);
if (!AddImGuiFonts())
@@ -153,14 +192,24 @@ void ImGuiManager::UpdateScale()
void ImGuiManager::NewFrame()
{
ImGuiIO& io = ImGui::GetIO();
io.DeltaTime = s_last_render_time.GetTimeSecondsAndReset();
ImGui::NewFrame();
#ifdef PCSX2_CORE
s_imgui_wants_keyboard.store(io.WantCaptureKeyboard, std::memory_order_relaxed);
s_imgui_wants_mouse.store(io.WantCaptureMouse, std::memory_order_release);
#endif
}
void SetImGuiStyle()
void ImGuiManager::SetStyle()
{
ImGuiStyle* style = &ImGui::GetStyle();
ImVec4* colors = style->Colors;
ImGuiStyle& style = ImGui::GetStyle();
style = ImGuiStyle();
style.WindowMinSize = ImVec2(1.0f, 1.0f);
ImVec4* colors = style.Colors;
colors[ImGuiCol_Text] = ImVec4(0.95f, 0.96f, 0.98f, 1.00f);
colors[ImGuiCol_TextDisabled] = ImVec4(0.36f, 0.42f, 0.47f, 1.00f);
colors[ImGuiCol_WindowBg] = ImVec4(0.11f, 0.15f, 0.17f, 1.00f);
@@ -209,9 +258,58 @@ void SetImGuiStyle()
colors[ImGuiCol_NavWindowingHighlight] = ImVec4(1.00f, 1.00f, 1.00f, 0.70f);
colors[ImGuiCol_NavWindowingDimBg] = ImVec4(0.80f, 0.80f, 0.80f, 0.20f);
colors[ImGuiCol_ModalWindowDimBg] = ImVec4(0.80f, 0.80f, 0.80f, 0.35f);
style.ScaleAllSizes(s_global_scale);
}
bool LoadFontData()
void ImGuiManager::SetKeyMap()
{
#ifdef PCSX2_CORE
struct KeyMapping
{
int index;
const char* name;
const char* alt_name;
};
static constexpr KeyMapping mapping[] = {{ImGuiKey_LeftArrow, "Left"}, {ImGuiKey_RightArrow, "Right"}, {ImGuiKey_UpArrow, "Up"},
{ImGuiKey_DownArrow, "Down"}, {ImGuiKey_PageUp, "PageUp"}, {ImGuiKey_PageDown, "PageDown"}, {ImGuiKey_Home, "Home"},
{ImGuiKey_End, "End"}, {ImGuiKey_Insert, "Insert"}, {ImGuiKey_Delete, "Delete"}, {ImGuiKey_Backspace, "Backspace"},
{ImGuiKey_Space, "Space"}, {ImGuiKey_Enter, "Return"}, {ImGuiKey_Escape, "Escape"}, {ImGuiKey_LeftCtrl, "LeftCtrl", "Ctrl"},
{ImGuiKey_LeftShift, "LeftShift", "Shift"}, {ImGuiKey_LeftAlt, "LeftAlt", "Alt"}, {ImGuiKey_LeftSuper, "LeftSuper", "Super"},
{ImGuiKey_RightCtrl, "RightCtrl"}, {ImGuiKey_RightShift, "RightShift"}, {ImGuiKey_RightAlt, "RightAlt"},
{ImGuiKey_RightSuper, "RightSuper"}, {ImGuiKey_Menu, "Menu"}, {ImGuiKey_0, "0"}, {ImGuiKey_1, "1"}, {ImGuiKey_2, "2"},
{ImGuiKey_3, "3"}, {ImGuiKey_4, "4"}, {ImGuiKey_5, "5"}, {ImGuiKey_6, "6"}, {ImGuiKey_7, "7"}, {ImGuiKey_8, "8"}, {ImGuiKey_9, "9"},
{ImGuiKey_A, "A"}, {ImGuiKey_B, "B"}, {ImGuiKey_C, "C"}, {ImGuiKey_D, "D"}, {ImGuiKey_E, "E"}, {ImGuiKey_F, "F"}, {ImGuiKey_G, "G"},
{ImGuiKey_H, "H"}, {ImGuiKey_I, "I"}, {ImGuiKey_J, "J"}, {ImGuiKey_K, "K"}, {ImGuiKey_L, "L"}, {ImGuiKey_M, "M"}, {ImGuiKey_N, "N"},
{ImGuiKey_O, "O"}, {ImGuiKey_P, "P"}, {ImGuiKey_Q, "Q"}, {ImGuiKey_R, "R"}, {ImGuiKey_S, "S"}, {ImGuiKey_T, "T"}, {ImGuiKey_U, "U"},
{ImGuiKey_V, "V"}, {ImGuiKey_W, "W"}, {ImGuiKey_X, "X"}, {ImGuiKey_Y, "Y"}, {ImGuiKey_Z, "Z"}, {ImGuiKey_F1, "F1"},
{ImGuiKey_F2, "F2"}, {ImGuiKey_F3, "F3"}, {ImGuiKey_F4, "F4"}, {ImGuiKey_F5, "F5"}, {ImGuiKey_F6, "F6"}, {ImGuiKey_F7, "F7"},
{ImGuiKey_F8, "F8"}, {ImGuiKey_F9, "F9"}, {ImGuiKey_F10, "F10"}, {ImGuiKey_F11, "F11"}, {ImGuiKey_F12, "F12"},
{ImGuiKey_Apostrophe, "Apostrophe"}, {ImGuiKey_Comma, "Comma"}, {ImGuiKey_Minus, "Minus"}, {ImGuiKey_Period, "Period"},
{ImGuiKey_Slash, "Slash"}, {ImGuiKey_Semicolon, "Semicolon"}, {ImGuiKey_Equal, "Equal"}, {ImGuiKey_LeftBracket, "BracketLeft"},
{ImGuiKey_Backslash, "Backslash"}, {ImGuiKey_RightBracket, "BracketRight"}, {ImGuiKey_GraveAccent, "QuoteLeft"},
{ImGuiKey_CapsLock, "CapsLock"}, {ImGuiKey_ScrollLock, "ScrollLock"}, {ImGuiKey_NumLock, "NumLock"},
{ImGuiKey_PrintScreen, "PrintScreen"}, {ImGuiKey_Pause, "Pause"}, {ImGuiKey_Keypad0, "Keypad0"}, {ImGuiKey_Keypad1, "Keypad1"},
{ImGuiKey_Keypad2, "Keypad2"}, {ImGuiKey_Keypad3, "Keypad3"}, {ImGuiKey_Keypad4, "Keypad4"}, {ImGuiKey_Keypad5, "Keypad5"},
{ImGuiKey_Keypad6, "Keypad6"}, {ImGuiKey_Keypad7, "Keypad7"}, {ImGuiKey_Keypad8, "Keypad8"}, {ImGuiKey_Keypad9, "Keypad9"},
{ImGuiKey_KeypadDecimal, "KeypadPeriod"}, {ImGuiKey_KeypadDivide, "KeypadDivide"}, {ImGuiKey_KeypadMultiply, "KeypadMultiply"},
{ImGuiKey_KeypadSubtract, "KeypadMinus"}, {ImGuiKey_KeypadAdd, "KeypadPlus"}, {ImGuiKey_KeypadEnter, "KeypadReturn"},
{ImGuiKey_KeypadEqual, "KeypadEqual"}};
s_imgui_key_map.clear();
for (const KeyMapping& km : mapping)
{
std::optional<u32> map(InputManager::ConvertHostKeyboardStringToCode(km.name));
if (!map.has_value() && km.alt_name)
map = InputManager::ConvertHostKeyboardStringToCode(km.alt_name);
if (map.has_value())
s_imgui_key_map[map.value()] = km.index;
}
#endif
}
bool ImGuiManager::LoadFontData()
{
if (s_standard_font_data.empty())
{
@@ -243,14 +341,14 @@ bool LoadFontData()
return true;
}
void UnloadFontData()
void ImGuiManager::UnloadFontData()
{
std::vector<u8>().swap(s_standard_font_data);
std::vector<u8>().swap(s_fixed_font_data);
std::vector<u8>().swap(s_icon_font_data);
}
static ImFont* AddTextFont(float size /*= 15.0f*/)
ImFont* ImGuiManager::AddTextFont(float size)
{
static const ImWchar default_ranges[] = {
// Basic Latin + Latin Supplement + Central European diacritics
@@ -278,15 +376,15 @@ static ImFont* AddTextFont(float size /*= 15.0f*/)
s_standard_font_data.data(), static_cast<int>(s_standard_font_data.size()), size, &cfg, default_ranges);
}
static ImFont* AddFixedFont(float size)
ImFont* ImGuiManager::AddFixedFont(float size)
{
ImFontConfig cfg;
cfg.FontDataOwnedByAtlas = false;
return ImGui::GetIO().Fonts->AddFontFromMemoryTTF(s_fixed_font_data.data(),
static_cast<int>(s_fixed_font_data.size()), size, &cfg, nullptr);
return ImGui::GetIO().Fonts->AddFontFromMemoryTTF(
s_fixed_font_data.data(), static_cast<int>(s_fixed_font_data.size()), size, &cfg, nullptr);
}
static bool AddIconFonts(float size)
bool ImGuiManager::AddIconFonts(float size)
{
static const ImWchar range_fa[] = {ICON_MIN_FA, ICON_MAX_FA, 0};
@@ -297,11 +395,11 @@ static bool AddIconFonts(float size)
cfg.GlyphMaxAdvanceX = size * 0.75f;
cfg.FontDataOwnedByAtlas = false;
return (ImGui::GetIO().Fonts->AddFontFromMemoryTTF(s_icon_font_data.data(), static_cast<int>(s_icon_font_data.size()),
size * 0.75f, &cfg, range_fa) != nullptr);
return (ImGui::GetIO().Fonts->AddFontFromMemoryTTF(
s_icon_font_data.data(), static_cast<int>(s_icon_font_data.size()), size * 0.75f, &cfg, range_fa) != nullptr);
}
bool AddImGuiFonts()
bool ImGuiManager::AddImGuiFonts()
{
const float standard_font_size = std::ceil(15.0f * s_global_scale);
@@ -387,7 +485,7 @@ void Host::ClearOSDMessages()
s_osd_active_messages.clear();
}
static void AcquirePendingOSDMessages()
void ImGuiManager::AcquirePendingOSDMessages()
{
std::atomic_thread_fence(std::memory_order_consume);
if (s_osd_posted_messages.empty())
@@ -403,10 +501,9 @@ static void AcquirePendingOSDMessages()
{
OSDMessage& new_msg = s_osd_posted_messages.front();
std::deque<OSDMessage>::iterator iter;
if (!new_msg.key.empty() && (iter = std::find_if(s_osd_active_messages.begin(), s_osd_active_messages.end(),
[&new_msg](const OSDMessage& other) {
return new_msg.key == other.key;
})) != s_osd_active_messages.end())
if (!new_msg.key.empty() &&
(iter = std::find_if(s_osd_active_messages.begin(), s_osd_active_messages.end(),
[&new_msg](const OSDMessage& other) { return new_msg.key == other.key; })) != s_osd_active_messages.end())
{
iter->text = std::move(new_msg.text);
iter->duration = new_msg.duration;
@@ -426,7 +523,7 @@ static void AcquirePendingOSDMessages()
}
}
static void DrawOSDMessages()
void ImGuiManager::DrawOSDMessages()
{
ImFont* const font = ImGui::GetFont();
const float scale = s_global_scale;
@@ -469,13 +566,13 @@ static void DrawOSDMessages()
ImDrawList* dl = ImGui::GetBackgroundDrawList();
dl->AddRectFilled(pos, ImVec2(pos.x + size.x, pos.y + size.y), IM_COL32(0x21, 0x21, 0x21, alpha), rounding);
dl->AddRect(pos, ImVec2(pos.x + size.x, pos.y + size.y), IM_COL32(0x48, 0x48, 0x48, alpha), rounding);
dl->AddText(font, font->FontSize, ImVec2(text_rect.x, text_rect.y), IM_COL32(0xff, 0xff, 0xff, alpha),
msg.text.c_str(), msg.text.c_str() + msg.text.length(), max_width, &text_rect);
dl->AddText(font, font->FontSize, ImVec2(text_rect.x, text_rect.y), IM_COL32(0xff, 0xff, 0xff, alpha), msg.text.c_str(),
msg.text.c_str() + msg.text.length(), max_width, &text_rect);
position_y += size.y + spacing;
}
}
static void FormatProcessorStat(std::string& text, double usage, double time)
void ImGuiManager::FormatProcessorStat(std::string& text, double usage, double time)
{
// Some values, such as GPU (and even CPU to some extent) can be out of phase with the wall clock,
// which the processor time is divided by to get a utilization percentage. Let's clamp it at 100%,
@@ -486,7 +583,7 @@ static void FormatProcessorStat(std::string& text, double usage, double time)
fmt::format_to(std::back_inserter(text), "{:.1f}% ({:.2f}ms)", usage, time);
}
static void DrawPerformanceOverlay()
void ImGuiManager::DrawPerformanceOverlay()
{
const float scale = s_global_scale;
const float shadow_offset = std::ceil(1.0f * scale);
@@ -504,14 +601,11 @@ static void DrawPerformanceOverlay()
#define DRAW_LINE(font, text, color) \
do \
{ \
text_size = \
font->CalcTextSizeA(font->FontSize, std::numeric_limits<float>::max(), -1.0f, (text), nullptr, nullptr); \
dl->AddText( \
font, font->FontSize, \
text_size = font->CalcTextSizeA(font->FontSize, std::numeric_limits<float>::max(), -1.0f, (text), nullptr, nullptr); \
dl->AddText(font, font->FontSize, \
ImVec2(ImGui::GetIO().DisplaySize.x - margin - text_size.x + shadow_offset, position_y + shadow_offset), \
IM_COL32(0, 0, 0, 100), (text)); \
dl->AddText(font, font->FontSize, ImVec2(ImGui::GetIO().DisplaySize.x - margin - text_size.x, position_y), color, \
(text)); \
dl->AddText(font, font->FontSize, ImVec2(ImGui::GetIO().DisplaySize.x - margin - text_size.x, position_y), color, (text)); \
position_y += text_size.y + spacing; \
} while (0)
@@ -528,18 +622,20 @@ static void DrawPerformanceOverlay()
{
switch (PerformanceMetrics::GetInternalFPSMethod())
{
case PerformanceMetrics::InternalFPSMethod::GSPrivilegedRegister:
fmt::format_to(std::back_inserter(text), "G: {:.2f} [P] | V: {:.2f}", PerformanceMetrics::GetInternalFPS(), PerformanceMetrics::GetFPS());
break;
case PerformanceMetrics::InternalFPSMethod::GSPrivilegedRegister:
fmt::format_to(std::back_inserter(text), "G: {:.2f} [P] | V: {:.2f}", PerformanceMetrics::GetInternalFPS(),
PerformanceMetrics::GetFPS());
break;
case PerformanceMetrics::InternalFPSMethod::DISPFBBlit:
fmt::format_to(std::back_inserter(text), "G: {:.2f} [B] | V: {:.2f}", PerformanceMetrics::GetInternalFPS(), PerformanceMetrics::GetFPS());
break;
case PerformanceMetrics::InternalFPSMethod::DISPFBBlit:
fmt::format_to(std::back_inserter(text), "G: {:.2f} [B] | V: {:.2f}", PerformanceMetrics::GetInternalFPS(),
PerformanceMetrics::GetFPS());
break;
case PerformanceMetrics::InternalFPSMethod::None:
default:
fmt::format_to(std::back_inserter(text), "V: {:.2f}", PerformanceMetrics::GetFPS());
break;
case PerformanceMetrics::InternalFPSMethod::None:
default:
fmt::format_to(std::back_inserter(text), "V: {:.2f}", PerformanceMetrics::GetFPS());
break;
}
first = false;
}
@@ -651,6 +747,9 @@ static void DrawPerformanceOverlay()
void ImGuiManager::RenderOSD()
{
// acquire for IO.MousePos.
std::atomic_thread_fence(std::memory_order_acquire);
DrawPerformanceOverlay();
AcquirePendingOSDMessages();
@@ -671,3 +770,96 @@ ImFont* ImGuiManager::GetFixedFont()
{
return s_fixed_font;
}
#ifdef PCSX2_CORE
void ImGuiManager::UpdateMousePosition(float x, float y)
{
if (!ImGui::GetCurrentContext())
return;
ImGui::GetIO().MousePos = ImVec2(x, y);
std::atomic_thread_fence(std::memory_order_release);
}
bool ImGuiManager::ProcessPointerButtonEvent(InputBindingKey key, float value)
{
if (!ImGui::GetCurrentContext() || key.data >= std::size(ImGui::GetIO().MouseDown))
return false;
// still update state anyway
GetMTGS().RunOnGSThread([button = key.data, down = (value != 0.0f)]() { ImGui::GetIO().AddMouseButtonEvent(button, down); });
return s_imgui_wants_mouse.load(std::memory_order_acquire);
}
bool ImGuiManager::ProcessPointerAxisEvent(InputBindingKey key, float value)
{
if (!ImGui::GetCurrentContext() || value == 0.0f || key.data < static_cast<u32>(InputPointerAxis::WheelX))
return false;
// still update state anyway
const bool horizontal = (key.data == static_cast<u32>(InputPointerAxis::WheelX));
GetMTGS().RunOnGSThread([wheel_x = horizontal ? value : 0.0f, wheel_y = horizontal ? 0.0f : value]() {
ImGui::GetIO().AddMouseWheelEvent(wheel_x, wheel_y);
});
return s_imgui_wants_mouse.load(std::memory_order_acquire);
}
bool ImGuiManager::ProcessHostKeyEvent(InputBindingKey key, float value)
{
decltype(s_imgui_key_map)::iterator iter;
if (!ImGui::GetCurrentContext() || (iter = s_imgui_key_map.find(key.data)) == s_imgui_key_map.end())
return false;
// still update state anyway
GetMTGS().RunOnGSThread([imkey = iter->second, down = (value != 0.0f)]() { ImGui::GetIO().AddKeyEvent(imkey, down); });
return s_imgui_wants_keyboard.load(std::memory_order_acquire);
}
bool ImGuiManager::ProcessGenericInputEvent(GenericInputBinding key, float value)
{
static constexpr ImGuiKey key_map[] = {
ImGuiKey_None, // Unknown,
ImGuiKey_GamepadDpadUp, // DPadUp
ImGuiKey_GamepadDpadRight, // DPadRight
ImGuiKey_GamepadDpadLeft, // DPadLeft
ImGuiKey_GamepadDpadDown, // DPadDown
ImGuiKey_None, // LeftStickUp
ImGuiKey_None, // LeftStickRight
ImGuiKey_None, // LeftStickDown
ImGuiKey_None, // LeftStickLeft
ImGuiKey_GamepadL3, // L3
ImGuiKey_None, // RightStickUp
ImGuiKey_None, // RightStickRight
ImGuiKey_None, // RightStickDown
ImGuiKey_None, // RightStickLeft
ImGuiKey_GamepadR3, // R3
ImGuiKey_GamepadFaceUp, // Triangle
ImGuiKey_GamepadFaceRight, // Circle
ImGuiKey_GamepadFaceDown, // Cross
ImGuiKey_GamepadFaceLeft, // Square
ImGuiKey_GamepadBack, // Select
ImGuiKey_GamepadStart, // Start
ImGuiKey_None, // System
ImGuiKey_GamepadL1, // L1
ImGuiKey_GamepadL2, // L2
ImGuiKey_GamepadR1, // R1
ImGuiKey_GamepadL2, // R2
};
if (!ImGui::GetCurrentContext() || !s_imgui_wants_keyboard.load(std::memory_order_acquire))
return false;
if (static_cast<u32>(key) >= std::size(key_map) || key_map[static_cast<u32>(key)] == ImGuiKey_None)
return false;
GetMTGS().RunOnGSThread(
[key = key_map[static_cast<u32>(key)], value]() { ImGui::GetIO().AddKeyAnalogEvent(key, (value > 0.0f), value); });
return true;
}
#endif // PCSX2_CORE
+23
View File
@@ -17,6 +17,9 @@
struct ImFont;
union InputBindingKey;
enum class GenericInputBinding : u8;
namespace ImGuiManager
{
/// Initializes ImGui, creates fonts, etc.
@@ -45,5 +48,25 @@ namespace ImGuiManager
/// Returns the fixed-width font for external drawing.
ImFont* GetFixedFont();
#ifdef PCSX2_CORE
/// Called on the UI or CPU thread in response to mouse movement.
void UpdateMousePosition(float x, float y);
/// Called on the CPU thread in response to a mouse button press.
/// Returns true if ImGui intercepted the event, and regular handlers should not execute.
bool ProcessPointerButtonEvent(InputBindingKey key, float value);
/// Called on the CPU thread in response to a mouse wheel movement.
/// Returns true if ImGui intercepted the event, and regular handlers should not execute.
bool ProcessPointerAxisEvent(InputBindingKey key, float value);
/// Called on the CPU thread in response to a key press.
/// Returns true if ImGui intercepted the event, and regular handlers should not execute.
bool ProcessHostKeyEvent(InputBindingKey key, float value);
/// Called on the CPU thread when any input event fires. Allows imgui to take over controller navigation.
bool ProcessGenericInputEvent(GenericInputBinding key, float value);
#endif
} // namespace ImGuiManager
+280 -114
View File
@@ -16,10 +16,14 @@
#include "PrecompiledHeader.h"
#include "Frontend/InputManager.h"
#include "Frontend/InputSource.h"
#include "Frontend/ImGuiManager.h"
#include "PAD/Host/PAD.h"
#include "common/StringUtil.h"
#include "common/Timer.h"
#include "VMManager.h"
#include "fmt/core.h"
#include <array>
#include <memory>
#include <mutex>
@@ -36,7 +40,7 @@ enum : u32
{
MAX_KEYS_PER_BINDING = 4,
MAX_MOTORS_PER_PAD = 2,
FIRST_EXTERNAL_INPUT_SOURCE = static_cast<u32>(InputSourceType::Mouse) + 1u,
FIRST_EXTERNAL_INPUT_SOURCE = static_cast<u32>(InputSourceType::Pointer) + 1u,
LAST_EXTERNAL_INPUT_SOURCE = static_cast<u32>(InputSourceType::Count),
};
@@ -90,10 +94,8 @@ struct PadVibrationBinding
// ------------------------------------------------------------------------
namespace InputManager
{
static std::optional<InputBindingKey> ParseHostKeyboardKey(
const std::string_view& source, const std::string_view& sub_binding);
static std::optional<InputBindingKey> ParseHostMouseKey(
const std::string_view& source, const std::string_view& sub_binding);
static std::optional<InputBindingKey> ParseHostKeyboardKey(const std::string_view& source, const std::string_view& sub_binding);
static std::optional<InputBindingKey> ParsePointerKey(const std::string_view& source, const std::string_view& sub_binding);
static std::vector<std::string_view> SplitChord(const std::string_view& binding);
static bool SplitBinding(const std::string_view& binding, std::string_view* source, std::string_view* sub_binding);
@@ -105,8 +107,10 @@ namespace InputManager
static void AddHotkeyBindings(SettingsInterface& si);
static void AddPadBindings(SettingsInterface& si, u32 pad, const char* default_type);
static void UpdateContinuedVibration();
static void GenerateRelativeMouseEvents();
static bool DoEventHook(InputBindingKey key, float value);
static bool PreprocessEvent(InputBindingKey key, float value, GenericInputBinding generic_key);
} // namespace InputManager
// ------------------------------------------------------------------------
@@ -132,6 +136,24 @@ static std::array<std::unique_ptr<InputSource>, static_cast<u32>(InputSourceType
// ------------------------------------------------------------------------
static const HotkeyInfo* const s_hotkey_list[] = {g_vm_manager_hotkeys, g_gs_hotkeys, g_host_hotkeys};
// ------------------------------------------------------------------------
// Tracking host mouse movement and turning into relative events
// 4 axes: pointer left/right, wheel vertical/horizontal. Last/Next/Normalized.
// ------------------------------------------------------------------------
static constexpr const std::array<const char*, static_cast<u8>(InputPointerAxis::Count)> s_pointer_axis_names = {
{"X", "Y", "WheelX", "WheelY"}};
static constexpr const std::array<const char*, 3> s_pointer_button_names = {{"LeftButton", "RightButton", "MiddleButton"}};
struct PointerAxisState
{
std::atomic<s32> delta;
float last_value;
};
static std::array<std::array<float, static_cast<u8>(InputPointerAxis::Count)>, InputManager::MAX_POINTER_DEVICES> s_host_pointer_positions;
static std::array<std::array<PointerAxisState, static_cast<u8>(InputPointerAxis::Count)>, InputManager::MAX_POINTER_DEVICES>
s_pointer_state;
static std::array<float, static_cast<u8>(InputPointerAxis::Count)> s_pointer_axis_scale;
// ------------------------------------------------------------------------
// Binding Parsing
// ------------------------------------------------------------------------
@@ -166,8 +188,7 @@ std::vector<std::string_view> InputManager::SplitChord(const std::string_view& b
return parts;
}
bool InputManager::SplitBinding(
const std::string_view& binding, std::string_view* source, std::string_view* sub_binding)
bool InputManager::SplitBinding(const std::string_view& binding, std::string_view* source, std::string_view* sub_binding)
{
const std::string_view::size_type slash_pos = binding.find('/');
if (slash_pos == std::string_view::npos)
@@ -192,9 +213,9 @@ std::optional<InputBindingKey> InputManager::ParseInputBindingKey(const std::str
{
return ParseHostKeyboardKey(source, sub_binding);
}
else if (StringUtil::StartsWith(source, "Mouse"))
else if (StringUtil::StartsWith(source, "Pointer"))
{
return ParseHostMouseKey(source, sub_binding);
return ParsePointerKey(source, sub_binding);
}
else
{
@@ -241,17 +262,21 @@ std::string InputManager::ConvertInputBindingKeyToString(InputBindingKey key)
{
const std::optional<std::string> str(ConvertHostKeyboardCodeToString(key.data));
if (str.has_value() && !str->empty())
return StringUtil::StdStringFromFormat("Keyboard/%s", str->c_str());
return fmt::format("Keyboard/{}", str->c_str());
}
else if (key.source_type == InputSourceType::Mouse)
else if (key.source_type == InputSourceType::Pointer)
{
if (key.source_subtype == InputSubclass::MouseButton)
return StringUtil::StdStringFromFormat("Mouse%u/Button%u", key.source_index, key.data);
else if (key.source_subtype == InputSubclass::MousePointer)
return StringUtil::StdStringFromFormat("Mouse%u/Pointer%u", key.source_index, key.data);
else if (key.source_subtype == InputSubclass::MouseWheel)
return StringUtil::StdStringFromFormat(
"Mouse%u/Wheel%u%c", key.source_index, key.data, key.negative ? '-' : '+');
if (key.source_subtype == InputSubclass::PointerButton)
{
if (key.data < s_pointer_button_names.size())
return fmt::format("Pointer-{}/{}", u32{key.source_index}, s_pointer_button_names[key.data]);
else
return fmt::format("Pointer-{}/Button{}", u32{key.source_index}, key.data);
}
else if (key.source_subtype == InputSubclass::PointerAxis)
{
return fmt::format("Pointer-{}/{}{:c}", u32{key.source_index}, s_pointer_axis_names[key.data], key.negative ? '-' : '+');
}
}
else if (key.source_type < InputSourceType::Count && s_input_sources[static_cast<u32>(key.source_type)])
{
@@ -327,29 +352,31 @@ void InputManager::AddBindings(const std::vector<std::string>& bindings, const I
// Key Decoders
// ------------------------------------------------------------------------
InputBindingKey InputManager::MakeHostKeyboardKey(s32 key_code)
InputBindingKey InputManager::MakeHostKeyboardKey(u32 key_code)
{
InputBindingKey key = {};
key.source_type = InputSourceType::Keyboard;
key.data = static_cast<u32>(key_code);
key.data = key_code;
return key;
}
InputBindingKey InputManager::MakeHostMouseButtonKey(s32 button_index)
InputBindingKey InputManager::MakePointerButtonKey(u32 index, u32 button_index)
{
InputBindingKey key = {};
key.source_type = InputSourceType::Mouse;
key.source_subtype = InputSubclass::MouseButton;
key.data = static_cast<u32>(button_index);
key.source_index = index;
key.source_type = InputSourceType::Pointer;
key.source_subtype = InputSubclass::PointerButton;
key.data = button_index;
return key;
}
InputBindingKey InputManager::MakeHostMouseWheelKey(s32 axis_index)
InputBindingKey InputManager::MakePointerAxisKey(u32 index, InputPointerAxis axis)
{
InputBindingKey key = {};
key.source_type = InputSourceType::Mouse;
key.source_subtype = InputSubclass::MouseWheel;
key.data = static_cast<u32>(axis_index);
key.data = static_cast<u32>(axis);
key.source_index = index;
key.source_type = InputSourceType::Pointer;
key.source_subtype = InputSubclass::PointerAxis;
return key;
}
@@ -389,8 +416,7 @@ std::optional<InputSourceType> InputManager::ParseInputSourceString(const std::s
return std::nullopt;
}
std::optional<InputBindingKey> InputManager::ParseHostKeyboardKey(
const std::string_view& source, const std::string_view& sub_binding)
std::optional<InputBindingKey> InputManager::ParseHostKeyboardKey(const std::string_view& source, const std::string_view& sub_binding)
{
if (source != "Keyboard")
return std::nullopt;
@@ -405,14 +431,15 @@ std::optional<InputBindingKey> InputManager::ParseHostKeyboardKey(
return key;
}
std::optional<InputBindingKey> InputManager::ParseHostMouseKey(
const std::string_view& source, const std::string_view& sub_binding)
std::optional<InputBindingKey> InputManager::ParsePointerKey(const std::string_view& source, const std::string_view& sub_binding)
{
if (source != "Mouse")
const std::optional<s32> pointer_index = StringUtil::FromChars<s32>(source.substr(8));
if (!pointer_index.has_value() || pointer_index.value() < 0)
return std::nullopt;
InputBindingKey key = {};
key.source_type = InputSourceType::Mouse;
key.source_type = InputSourceType::Pointer;
key.source_index = static_cast<u32>(pointer_index.value());
if (StringUtil::StartsWith(sub_binding, "Button"))
{
@@ -420,15 +447,41 @@ std::optional<InputBindingKey> InputManager::ParseHostMouseKey(
if (!button_number.has_value() || button_number.value() < 0)
return std::nullopt;
key.source_subtype = InputSubclass::MouseButton;
key.source_subtype = InputSubclass::PointerButton;
key.data = static_cast<u32>(button_number.value());
}
else
{
return std::nullopt;
return key;
}
return key;
for (u32 i = 0; i < s_pointer_axis_names.size(); i++)
{
if (StringUtil::StartsWith(sub_binding, s_pointer_axis_names[i]))
{
key.source_subtype = InputSubclass::PointerAxis;
key.data = i;
const std::string_view dir_part(sub_binding.substr(std::strlen(s_pointer_axis_names[i])));
if (dir_part == "+")
key.negative = false;
else if (dir_part == "-")
key.negative = true;
else
return std::nullopt;
return key;
}
}
for (u32 i = 0; i < s_pointer_button_names.size(); i++)
{
if (sub_binding == s_pointer_button_names[i])
{
key.source_subtype = InputSubclass::PointerButton;
key.data = i;
return key;
}
}
return std::nullopt;
}
// ------------------------------------------------------------------------
@@ -484,17 +537,16 @@ void InputManager::AddPadBindings(SettingsInterface& si, u32 pad_index, const ch
if (!bindings.empty())
{
// we use axes for all pad bindings to simplify things, and because they are pressure sensitive
AddBindings(bindings, InputAxisEventHandler{[pad_index, bind_index, bind_names](float value) {
PAD::SetControllerState(pad_index, bind_index, value);
}});
AddBindings(bindings, InputAxisEventHandler{[pad_index, bind_index, bind_names](
float value) { PAD::SetControllerState(pad_index, bind_index, value); }});
}
}
}
for (u32 macro_button_index = 0; macro_button_index < PAD::NUM_MACRO_BUTTONS_PER_CONTROLLER; macro_button_index++)
{
const std::vector<std::string> bindings(si.GetStringList(section.c_str(),
StringUtil::StdStringFromFormat("Macro%u", macro_button_index + 1).c_str()));
const std::vector<std::string> bindings(
si.GetStringList(section.c_str(), StringUtil::StdStringFromFormat("Macro%u", macro_button_index + 1).c_str()));
if (!bindings.empty())
{
AddBindings(bindings, InputButtonEventHandler{[pad_index, macro_button_index](bool state) {
@@ -547,58 +599,43 @@ bool InputManager::HasAnyBindingsForKey(InputBindingKey key)
return (s_binding_map.find(key.MaskDirection()) != s_binding_map.end());
}
bool InputManager::HasAnyBindingsForSource(InputBindingKey key)
{
std::unique_lock lock(s_binding_map_write_lock);
for (const auto& it : s_binding_map)
{
const InputBindingKey& okey = it.first;
if (okey.source_type == key.source_type && okey.source_index == key.source_index &&
okey.source_subtype == key.source_subtype)
{
return true;
}
}
return false;
}
bool InputManager::IsAxisHandler(const InputEventHandler& handler)
{
return std::holds_alternative<InputAxisEventHandler>(handler);
}
bool InputManager::InvokeEvents(InputBindingKey key, float value)
bool InputManager::InvokeEvents(InputBindingKey key, float value, GenericInputBinding generic_key)
{
if (DoEventHook(key, value))
return true;
// If imgui ate the event, don't fire our handlers.
const bool skip_button_handlers = PreprocessEvent(key, value, generic_key);
// find all the bindings associated with this key
const InputBindingKey masked_key = key.MaskDirection();
const auto range = s_binding_map.equal_range(masked_key);
if (range.first == s_binding_map.end())
return false;
// Workaround for modifier keys. Basically, if we bind say, F1 and Shift+F1, and press shift
// and then F1, we'll fire bindings for both F1 and Shift+F1, when we really only want to fire
// the binding for Shift+F1. So, let's search through the binding list, and see if there's a
// "longer" binding (more keys), and if so, only activate that and not the shorter binding(s).
const InputBinding* longest_hotkey_binding = nullptr;
for (auto it = range.first; it != range.second; ++it)
{
InputBinding* binding = it->second.get();
if (IsAxisHandler(binding->handler))
continue;
// find the key which matches us
for (u32 i = 0; i < binding->num_keys; i++)
{
if (binding->keys[i].MaskDirection() != masked_key)
continue;
const u8 bit = static_cast<u8>(1) << i;
const bool negative = binding->keys[i].negative;
const bool new_state = (negative ? (value < 0.0f) : (value > 0.0f));
const u8 new_mask = (new_state ? (binding->current_mask | bit) : (binding->current_mask & ~bit));
const bool prev_full_state = (binding->current_mask == binding->full_mask);
const bool new_full_state = (new_mask == binding->full_mask);
// If we're activating this chord, block activation of other bindings with fewer keys.
if (prev_full_state || new_full_state)
{
if (!longest_hotkey_binding || longest_hotkey_binding->num_keys < binding->num_keys)
longest_hotkey_binding = binding;
}
break;
}
}
// Now we can actually fire/activate bindings.
u32 min_num_keys = 0;
for (auto it = range.first; it != range.second; ++it)
{
InputBinding* binding = it->second.get();
@@ -613,23 +650,8 @@ bool InputManager::InvokeEvents(InputBindingKey key, float value)
const bool negative = binding->keys[i].negative;
const bool new_state = (negative ? (value < 0.0f) : (value > 0.0f));
// Don't register the key press when we're part of a longer chord. That way,
// the state won't change, and it won't get the released event either.
if (longest_hotkey_binding && new_state && !IsAxisHandler(binding->handler) &&
binding->num_keys != longest_hotkey_binding->num_keys)
{
continue;
}
// update state based on whether the whole chord was activated
const u8 new_mask = (new_state ? (binding->current_mask | bit) : (binding->current_mask & ~bit));
const bool prev_full_state = (binding->current_mask == binding->full_mask);
const bool new_full_state = (new_mask == binding->full_mask);
binding->current_mask = new_mask;
// invert if we're negative, since the handler expects 0..1
const float value_to_pass = (negative ? ((value < 0.0f) ? -value : 0.0f) : (value > 0.0f) ? value :
0.0f);
const float value_to_pass = (negative ? ((value < 0.0f) ? -value : 0.0f) : (value > 0.0f) ? value : 0.0f);
// axes are fired regardless of a state change, unless they're zero
// (but going from not-zero to zero will still fire, because of the full state)
@@ -637,13 +659,60 @@ bool InputManager::InvokeEvents(InputBindingKey key, float value)
// and 0 on release (when the full state changes).
if (IsAxisHandler(binding->handler))
{
if (prev_full_state != new_full_state || value_to_pass >= 0.0f)
if (value_to_pass >= 0.0f)
std::get<InputAxisEventHandler>(binding->handler)(value_to_pass);
}
else
else if (binding->num_keys >= min_num_keys)
{
if (prev_full_state != new_full_state)
std::get<InputButtonEventHandler>(binding->handler)(value_to_pass > 0.0f);
// update state based on whether the whole chord was activated
const u8 new_mask = (new_state ? (binding->current_mask | bit) : (binding->current_mask & ~bit));
const bool prev_full_state = (binding->current_mask == binding->full_mask);
const bool new_full_state = (new_mask == binding->full_mask);
binding->current_mask = new_mask;
// Workaround for multi-key bindings that share the same keys.
if (binding->num_keys > 1 && new_full_state && prev_full_state != new_full_state &&
range.first != range.second)
{
// Because the binding map isn't ordered, we could iterate in the order of Shift+F1 and then
// F1, which would mean that F1 wouldn't get cancelled and still activate. So, to handle this
// case, we skip activating any future bindings with a fewer number of keys.
min_num_keys = std::max<u32>(min_num_keys, binding->num_keys);
// Basically, if we bind say, F1 and Shift+F1, and press shift and then F1, we'll fire bindings
// for both F1 and Shift+F1, when we really only want to fire the binding for Shift+F1. So,
// when we activate a multi-key chord (key press), we go through the binding map for all the
// other keys in the chord, and cancel them if they have a shorter chord. If they're longer,
// they could still activate and take precedence over us, so we leave them alone.
for (u32 i = 0; i < binding->num_keys; i++)
{
const auto range = s_binding_map.equal_range(binding->keys[i].MaskDirection());
for (auto it = range.first; it != range.second; ++it)
{
InputBinding* other_binding = it->second.get();
if (other_binding == binding || IsAxisHandler(other_binding->handler) ||
other_binding->num_keys >= binding->num_keys)
{
continue;
}
// We only need to cancel the binding if it was fully active before. Which in the above
// case of Shift+F1 / F1, it will be.
if (other_binding->current_mask == other_binding->full_mask)
std::get<InputButtonEventHandler>(other_binding->handler)(-1);
// Zero out the current bits so that we don't release this binding, if the other part
// of the chord releases first.
other_binding->current_mask = 0;
}
}
}
if (prev_full_state != new_full_state && binding->num_keys >= min_num_keys)
{
const s32 pressed = skip_button_handlers ? -1 : static_cast<s32>(value_to_pass > 0.0f);
std::get<InputButtonEventHandler>(binding->handler)(pressed);
}
}
// bail out, since we shouldn't have the same key twice in the chord
@@ -654,6 +723,86 @@ bool InputManager::InvokeEvents(InputBindingKey key, float value)
return true;
}
bool InputManager::PreprocessEvent(InputBindingKey key, float value, GenericInputBinding generic_key)
{
// does imgui want the event?
if (key.source_type == InputSourceType::Keyboard)
{
if (ImGuiManager::ProcessHostKeyEvent(key, value))
return true;
}
else if (key.source_type == InputSourceType::Pointer && key.source_subtype == InputSubclass::PointerButton)
{
if (ImGuiManager::ProcessPointerButtonEvent(key, value))
return true;
}
else if (generic_key != GenericInputBinding::Unknown)
{
if (ImGuiManager::ProcessGenericInputEvent(generic_key, value) && value != 0.0f)
return true;
}
return false;
}
void InputManager::GenerateRelativeMouseEvents()
{
for (u32 device = 0; device < MAX_POINTER_DEVICES; device++)
{
for (u32 axis = 0; axis < static_cast<u32>(static_cast<u8>(InputPointerAxis::Count)); axis++)
{
PointerAxisState& state = s_pointer_state[device][axis];
const float delta = static_cast<float>(state.delta.exchange(0, std::memory_order_acquire)) / 65536.0f;
const float unclamped_value = delta * s_pointer_axis_scale[axis];
const InputBindingKey key(MakePointerAxisKey(device, static_cast<InputPointerAxis>(axis)));
if (axis >= static_cast<u32>(InputPointerAxis::WheelX) && ImGuiManager::ProcessPointerAxisEvent(key, unclamped_value))
continue;
const float value = std::clamp(unclamped_value, -1.0f, 1.0f);
if (value != state.last_value)
{
state.last_value = value;
InvokeEvents(key, value, GenericInputBinding::Unknown);
}
}
}
}
void InputManager::UpdatePointerAbsolutePosition(u32 index, float x, float y)
{
const float dx = x - std::exchange(s_host_pointer_positions[index][static_cast<u8>(InputPointerAxis::X)], x);
const float dy = y - std::exchange(s_host_pointer_positions[index][static_cast<u8>(InputPointerAxis::Y)], y);
if (dx != 0.0f)
UpdatePointerRelativeDelta(index, InputPointerAxis::X, dx);
if (dy != 0.0f)
UpdatePointerRelativeDelta(index, InputPointerAxis::Y, dy);
ImGuiManager::UpdateMousePosition(x, y);
}
void InputManager::UpdatePointerRelativeDelta(u32 index, InputPointerAxis axis, float d, bool raw_input)
{
s_pointer_state[index][static_cast<u8>(axis)].delta.fetch_add(static_cast<s32>(d * 65536.0f), std::memory_order_release);
}
bool InputManager::HasPointerAxisBinds()
{
std::unique_lock lock(s_binding_map_write_lock);
for (const auto& it : s_binding_map)
{
const InputBindingKey& key = it.first;
if (key.source_type == InputSourceType::Pointer && key.source_subtype == InputSubclass::PointerAxis &&
key.data >= static_cast<u32>(InputPointerAxis::X) && key.data <= static_cast<u32>(InputPointerAxis::Y))
{
return true;
}
}
return false;
}
// ------------------------------------------------------------------------
// Vibration
// ------------------------------------------------------------------------
@@ -684,7 +833,8 @@ void InputManager::SetPadVibrationIntensity(u32 pad_index, float large_or_single
{
// both motors are bound to the same source, do an optimal update
large_motor.last_update_time = Common::Timer::GetCurrentValue();
large_motor.source->UpdateMotorState(large_motor.binding, small_motor.binding, large_or_single_motor_intensity, small_motor_intensity);
large_motor.source->UpdateMotorState(
large_motor.binding, small_motor.binding, large_or_single_motor_intensity, small_motor_intensity);
}
else
{
@@ -801,20 +951,18 @@ bool InputManager::DoEventHook(InputBindingKey key, float value)
return false;
const InputInterceptHook::CallbackResult action = m_event_intercept_callback(key, value);
return (action == InputInterceptHook::CallbackResult::StopProcessingEvent);
if (action >= InputInterceptHook::CallbackResult::RemoveHookAndStopProcessingEvent)
m_event_intercept_callback = {};
return (action == InputInterceptHook::CallbackResult::RemoveHookAndStopProcessingEvent ||
action == InputInterceptHook::CallbackResult::StopProcessingEvent);
}
// ------------------------------------------------------------------------
// Binding Updater
// ------------------------------------------------------------------------
// TODO(Stenzek): Find a better place for this. Maybe in PAD?
static constexpr std::array<const char*, InputManager::MAX_PAD_NUMBER> s_default_pad_types = {{
"DualShock2", // Pad 1
"None" // Pad 2
}};
void InputManager::ReloadBindings(SettingsInterface& si)
void InputManager::ReloadBindings(SettingsInterface& si, SettingsInterface& binding_si)
{
PauseVibration();
@@ -823,10 +971,25 @@ void InputManager::ReloadBindings(SettingsInterface& si)
s_binding_map.clear();
s_pad_vibration_array.clear();
AddHotkeyBindings(si);
// Hotkeys use the base configuration, except if the custom hotkeys option is enabled.
const bool use_profile_hotkeys = si.GetBoolValue("Pad", "UseProfileHotkeyBindings", false);
AddHotkeyBindings(use_profile_hotkeys ? binding_si : si);
for (u32 pad = 0; pad < MAX_PAD_NUMBER; pad++)
AddPadBindings(si, pad, s_default_pad_types[pad]);
// If there's an input profile, we load pad bindings from it alone, rather than
// falling back to the base configuration.
for (u32 pad = 0; pad < PAD::NUM_CONTROLLER_PORTS; pad++)
AddPadBindings(binding_si, pad, PAD::GetDefaultPadType(pad));
for (u32 axis = 0; axis < static_cast<u32>(InputPointerAxis::Count); axis++)
{
// From lilypad: 1 mouse pixel = 1/8th way down.
const float default_scale = (axis <= static_cast<u32>(InputPointerAxis::Y)) ? 8.0f : 1.0f;
const float invert =
si.GetBoolValue("Pad", fmt::format("Pointer{}Invert", s_pointer_axis_names[axis]).c_str(), false) ? -1.0f : 1.0f;
s_pointer_axis_scale[axis] =
invert /
std::max(si.GetFloatValue("Pad", fmt::format("Pointer{}Scale", s_pointer_axis_names[axis]).c_str(), default_scale), 1.0f);
}
}
// ------------------------------------------------------------------------
@@ -853,6 +1016,8 @@ void InputManager::PollSources()
s_input_sources[i]->PollEvents();
}
GenerateRelativeMouseEvents();
if (VMManager::GetState() == VMState::Running && !s_pad_vibration_array.empty())
UpdateContinuedVibration();
}
@@ -955,7 +1120,8 @@ GenericInputBindingMapping InputManager::GetGenericBindingMapping(const std::str
}
template <typename T>
static void UpdateInputSourceState(SettingsInterface& si, std::unique_lock<std::mutex>& settings_lock, InputSourceType type, bool default_state)
static void UpdateInputSourceState(
SettingsInterface& si, std::unique_lock<std::mutex>& settings_lock, InputSourceType type, bool default_state)
{
const bool enabled = si.GetBoolValue("InputSources", InputManager::InputSourceToString(type), default_state);
if (enabled)
+54 -21
View File
@@ -28,7 +28,7 @@
enum class InputSourceType : u32
{
Keyboard,
Mouse,
Pointer,
#ifdef _WIN32
//DInput,
XInput,
@@ -44,9 +44,8 @@ enum class InputSubclass : u32
{
None = 0,
MouseButton = 0,
MousePointer = 1,
MouseWheel = 2,
PointerButton = 0,
PointerAxis = 1,
ControllerButton = 0,
ControllerAxis = 1,
@@ -91,7 +90,7 @@ struct InputBindingKeyHash
};
/// Callback type for a binary event. Usually used for hotkeys.
using InputButtonEventHandler = std::function<void(bool value)>;
using InputButtonEventHandler = std::function<void(s32 value)>;
/// Callback types for a normalized event. Usually used for pads.
using InputAxisEventHandler = std::function<void(float value)>;
@@ -102,31 +101,41 @@ struct InputInterceptHook
enum class CallbackResult
{
StopProcessingEvent,
ContinueProcessingEvent
ContinueProcessingEvent,
RemoveHookAndStopProcessingEvent,
RemoveHookAndContinueProcessingEvent,
};
using Callback = std::function<CallbackResult(InputBindingKey key, float value)>;
};
/// Hotkeys are actions (e.g. toggle frame limit) which can be bound to keys or chords.
/// The handler is called with an integer representing the key state, where 0 means that
/// one or more keys were released, 1 means all the keys were pressed, and -1 means that
/// the hotkey was cancelled due to a chord with more keys being activated.
struct HotkeyInfo
{
const char* name;
const char* category;
const char* display_name;
void(*handler)(bool pressed);
void (*handler)(s32 pressed);
};
#define DECLARE_HOTKEY_LIST(name) extern const HotkeyInfo name[]
#define BEGIN_HOTKEY_LIST(name) const HotkeyInfo name[] = {
#define DEFINE_HOTKEY(name, category, display_name, handler) {(name), (category), (display_name), (handler)},
#define END_HOTKEY_LIST() {nullptr, nullptr, nullptr, nullptr} };
#define END_HOTKEY_LIST() \
{ \
nullptr, nullptr, nullptr, nullptr \
} \
} \
;
DECLARE_HOTKEY_LIST(g_vm_manager_hotkeys);
DECLARE_HOTKEY_LIST(g_gs_hotkeys);
DECLARE_HOTKEY_LIST(g_host_hotkeys);
/// Generic input bindings. These roughly match a DualShock 4 or XBox One controller.
/// They are used for automatic binding to PS2 controller types.
/// They are used for automatic binding to PS2 controller types, and for big picture mode navigation.
enum class GenericInputBinding : u8
{
Unknown,
@@ -169,16 +178,26 @@ enum class GenericInputBinding : u8
};
using GenericInputBindingMapping = std::vector<std::pair<GenericInputBinding, std::string>>;
/// Host mouse relative axes are X, Y, wheel horizontal, wheel vertical.
enum class InputPointerAxis : u8
{
X,
Y,
WheelX,
WheelY,
Count
};
/// External input source class.
class InputSource;
namespace InputManager
{
/// Number of emulated pads. TODO: Multitap support.
static constexpr u32 MAX_PAD_NUMBER = 2;
/// Minimum interval between vibration updates when the effect is continuous.
static constexpr double VIBRATION_UPDATE_INTERVAL_SECONDS = 0.5; // 500ms
static constexpr double VIBRATION_UPDATE_INTERVAL_SECONDS = 0.5; // 500ms
/// Maximum number of host mouse devices.
static constexpr u32 MAX_POINTER_DEVICES = 1;
/// Returns a pointer to the external input source class, if present.
InputSource* GetInputSourceInterface(InputSourceType type);
@@ -196,13 +215,14 @@ namespace InputManager
std::optional<std::string> ConvertHostKeyboardCodeToString(u32 code);
/// Creates a key for a host-specific key code.
InputBindingKey MakeHostKeyboardKey(s32 key_code);
InputBindingKey MakeHostKeyboardKey(u32 key_code);
/// Creates a key for a host-specific button.
InputBindingKey MakeHostMouseButtonKey(s32 button_index);
InputBindingKey MakePointerButtonKey(u32 index, u32 button_index);
/// Creates a key for a host-specific mouse wheel axis (0 = vertical, 1 = horizontal).
InputBindingKey MakeHostMouseWheelKey(s32 axis_index);
/// Creates a key for a host-specific mouse relative event
/// (axis 0 = horizontal, 1 = vertical, 2 = wheel horizontal, 3 = wheel vertical).
InputBindingKey MakePointerAxisKey(u32 index, InputPointerAxis axis);
/// Parses an input binding key string.
std::optional<InputBindingKey> ParseInputBindingKey(const std::string_view& binding);
@@ -226,7 +246,7 @@ namespace InputManager
GenericInputBindingMapping GetGenericBindingMapping(const std::string_view& device);
/// Re-parses the config and registers all hotkey and pad bindings.
void ReloadBindings(SettingsInterface& si);
void ReloadBindings(SettingsInterface& si, SettingsInterface& binding_si);
/// Re-parses the sources part of the config and initializes any backends.
void ReloadSources(SettingsInterface& si, std::unique_lock<std::mutex>& settings_lock);
@@ -238,12 +258,16 @@ namespace InputManager
void PollSources();
/// Returns true if any bindings exist for the specified key.
/// This is the only function which can be safely called on another thread.
/// Can be safely called on another thread.
bool HasAnyBindingsForKey(InputBindingKey key);
/// Returns true if any bindings exist for the specified source + index.
/// Can be safely called on another thread.
bool HasAnyBindingsForSource(InputBindingKey key);
/// Updates internal state for any binds for this key, and fires callbacks as needed.
/// Returns true if anything was bound to this key, otherwise false.
bool InvokeEvents(InputBindingKey key, float value);
bool InvokeEvents(InputBindingKey key, float value, GenericInputBinding generic_key = GenericInputBinding::Unknown);
/// Sets a hook which can be used to intercept events before they're processed by the normal bindings.
/// This is typically used when binding new controls to detect what gets pressed.
@@ -262,6 +286,15 @@ namespace InputManager
/// Zeros all vibration intensities. Call when pausing.
/// The pad vibration state will internally remain, so that when emulation is unpaused, the effect resumes.
void PauseVibration();
/// Updates absolute pointer position. Can call from UI thread, use when the host only reports absolute coordinates.
void UpdatePointerAbsolutePosition(u32 index, float x, float y);
/// Updates relative pointer position. Can call from the UI thread, use when host supports relative coordinate reporting.
void UpdatePointerRelativeDelta(u32 index, InputPointerAxis axis, float d, bool raw_input = false);
/// Returns true if any bindings are present which require relative mouse movement.
bool HasPointerAxisBinds();
} // namespace InputManager
namespace Host
@@ -271,4 +304,4 @@ namespace Host
/// Called when an input device is disconnected.
void OnInputDeviceDisconnected(const std::string_view& identifier);
}
} // namespace Host

Some files were not shown because too many files have changed in this diff Show More