From 2b99f3c753446baadf80256848d9b7b323afa78e Mon Sep 17 00:00:00 2001 From: Stenzek Date: Sat, 24 Dec 2022 14:54:23 +1000 Subject: [PATCH] pcsx2-wx: Finally remove it all --- .../workflows/scripts/linux/appimage-qt.sh | 2 +- PCSX2_suite.sln | 423 --- bin/PCSX2_keys.ini.default | 94 - bin/resources/locale/ar_SA/pcsx2_Iconized.mo | Bin 27476 -> 0 bytes bin/resources/locale/ar_SA/pcsx2_Main.mo | Bin 66552 -> 0 bytes bin/resources/locale/ca_ES/pcsx2_Iconized.mo | Bin 1758 -> 0 bytes bin/resources/locale/ca_ES/pcsx2_Main.mo | Bin 15752 -> 0 bytes bin/resources/locale/cs_CZ/pcsx2_Iconized.mo | Bin 23860 -> 0 bytes bin/resources/locale/cs_CZ/pcsx2_Main.mo | Bin 53398 -> 0 bytes bin/resources/locale/da_DK/pcsx2_Iconized.mo | Bin 27339 -> 0 bytes bin/resources/locale/da_DK/pcsx2_Main.mo | Bin 58594 -> 0 bytes bin/resources/locale/de_DE/pcsx2_Iconized.mo | Bin 29883 -> 0 bytes bin/resources/locale/de_DE/pcsx2_Main.mo | Bin 59136 -> 0 bytes bin/resources/locale/es_ES/pcsx2_Iconized.mo | Bin 32612 -> 0 bytes bin/resources/locale/es_ES/pcsx2_Main.mo | Bin 62654 -> 0 bytes bin/resources/locale/fi_FI/pcsx2_Iconized.mo | Bin 20901 -> 0 bytes bin/resources/locale/fi_FI/pcsx2_Main.mo | Bin 48698 -> 0 bytes bin/resources/locale/fr_FR/pcsx2_Iconized.mo | Bin 33565 -> 0 bytes bin/resources/locale/fr_FR/pcsx2_Main.mo | Bin 61086 -> 0 bytes bin/resources/locale/hr_HR/pcsx2_Iconized.mo | Bin 20853 -> 0 bytes bin/resources/locale/hr_HR/pcsx2_Main.mo | Bin 47926 -> 0 bytes bin/resources/locale/hu_HU/pcsx2_Iconized.mo | Bin 33182 -> 0 bytes bin/resources/locale/hu_HU/pcsx2_Main.mo | Bin 63971 -> 0 bytes bin/resources/locale/id_ID/pcsx2_Iconized.mo | Bin 603 -> 0 bytes bin/resources/locale/id_ID/pcsx2_Main.mo | Bin 45988 -> 0 bytes bin/resources/locale/it_IT/pcsx2_Iconized.mo | Bin 31888 -> 0 bytes bin/resources/locale/it_IT/pcsx2_Main.mo | Bin 62906 -> 0 bytes bin/resources/locale/ja_JP/pcsx2_Iconized.mo | Bin 35465 -> 0 bytes bin/resources/locale/ja_JP/pcsx2_Main.mo | Bin 67472 -> 0 bytes bin/resources/locale/ko_KR/pcsx2_Iconized.mo | Bin 23329 -> 0 bytes bin/resources/locale/ko_KR/pcsx2_Main.mo | Bin 52218 -> 0 bytes bin/resources/locale/lt_LT/pcsx2_Iconized.mo | Bin 31758 -> 0 bytes bin/resources/locale/lt_LT/pcsx2_Main.mo | Bin 61313 -> 0 bytes bin/resources/locale/ms_MY/pcsx2_Iconized.mo | Bin 19530 -> 0 bytes bin/resources/locale/ms_MY/pcsx2_Main.mo | Bin 45107 -> 0 bytes bin/resources/locale/nb_NO/pcsx2_Iconized.mo | Bin 31164 -> 0 bytes bin/resources/locale/nb_NO/pcsx2_Main.mo | Bin 59641 -> 0 bytes bin/resources/locale/nl_NL/pcsx2_Iconized.mo | Bin 31594 -> 0 bytes bin/resources/locale/nl_NL/pcsx2_Main.mo | Bin 59449 -> 0 bytes bin/resources/locale/pl_PL/pcsx2_Iconized.mo | Bin 32024 -> 0 bytes bin/resources/locale/pl_PL/pcsx2_Main.mo | Bin 61855 -> 0 bytes bin/resources/locale/pt_BR/pcsx2_Iconized.mo | Bin 32436 -> 0 bytes bin/resources/locale/pt_BR/pcsx2_Main.mo | Bin 62913 -> 0 bytes bin/resources/locale/ru_RU/pcsx2_Iconized.mo | Bin 41594 -> 0 bytes bin/resources/locale/ru_RU/pcsx2_Main.mo | Bin 79273 -> 0 bytes bin/resources/locale/sv_SE/pcsx2_Iconized.mo | Bin 28192 -> 0 bytes bin/resources/locale/sv_SE/pcsx2_Main.mo | Bin 59385 -> 0 bytes bin/resources/locale/th_TH/pcsx2_Iconized.mo | Bin 30649 -> 0 bytes bin/resources/locale/th_TH/pcsx2_Main.mo | Bin 72034 -> 0 bytes bin/resources/locale/tr_TR/pcsx2_Iconized.mo | Bin 33237 -> 0 bytes bin/resources/locale/tr_TR/pcsx2_Main.mo | Bin 61862 -> 0 bytes bin/resources/locale/zh_CN/pcsx2_Iconized.mo | Bin 20981 -> 0 bytes bin/resources/locale/zh_CN/pcsx2_Main.mo | Bin 50591 -> 0 bytes bin/resources/locale/zh_TW/pcsx2_Iconized.mo | Bin 30039 -> 0 bytes bin/resources/locale/zh_TW/pcsx2_Main.mo | Bin 58990 -> 0 bytes pcsx2/CMakeLists.txt | 4 +- pcsx2/DEV9/ATA/HddCreateWx.cpp | 103 - pcsx2/DEV9/ATA/HddCreateWx.h | 45 - pcsx2/DEV9/ConfigUI.cpp | 439 --- pcsx2/DEV9/DEV9Config.cpp | 129 - pcsx2/DEV9/DEV9Config.h | 17 - pcsx2/Darwin/DarwinKeyCodes.cpp | 142 - pcsx2/GS/Window/GSwxDialog.cpp | 844 ----- pcsx2/GS/Window/GSwxDialog.h | 205 -- pcsx2/Linux/LnxConsolePipe.cpp | 167 - pcsx2/Linux/LnxKeyCodes.cpp | 294 -- pcsx2/PAD/Linux/Config.cpp | 164 - pcsx2/PAD/Linux/Config.h | 133 - pcsx2/PAD/Linux/Device.cpp | 61 - pcsx2/PAD/Linux/Device.h | 107 - pcsx2/PAD/Linux/Global.h | 117 - pcsx2/PAD/Linux/Img/analog-2.png | Bin 170 -> 0 bytes pcsx2/PAD/Linux/Img/analog.png | Bin 137 -> 0 bytes pcsx2/PAD/Linux/Img/arrow_bottom.png | Bin 283 -> 0 bytes pcsx2/PAD/Linux/Img/arrow_left.png | Bin 240 -> 0 bytes pcsx2/PAD/Linux/Img/arrow_right.png | Bin 252 -> 0 bytes pcsx2/PAD/Linux/Img/arrow_up.png | Bin 269 -> 0 bytes pcsx2/PAD/Linux/Img/circle.png | Bin 916 -> 0 bytes pcsx2/PAD/Linux/Img/cross.png | Bin 923 -> 0 bytes pcsx2/PAD/Linux/Img/dp_bottom.png | Bin 278 -> 0 bytes pcsx2/PAD/Linux/Img/dp_left.png | Bin 316 -> 0 bytes pcsx2/PAD/Linux/Img/dp_right.png | Bin 307 -> 0 bytes pcsx2/PAD/Linux/Img/dp_up.png | Bin 338 -> 0 bytes pcsx2/PAD/Linux/Img/dualshock2.png | Bin 110432 -> 0 bytes pcsx2/PAD/Linux/Img/joystick_cursor.png | Bin 171 -> 0 bytes pcsx2/PAD/Linux/Img/l1.png | Bin 947 -> 0 bytes pcsx2/PAD/Linux/Img/l2.png | Bin 1096 -> 0 bytes pcsx2/PAD/Linux/Img/l3.png | Bin 884 -> 0 bytes pcsx2/PAD/Linux/Img/r1.png | Bin 949 -> 0 bytes pcsx2/PAD/Linux/Img/r2.png | Bin 1166 -> 0 bytes pcsx2/PAD/Linux/Img/r3.png | Bin 889 -> 0 bytes pcsx2/PAD/Linux/Img/select.png | Bin 173 -> 0 bytes pcsx2/PAD/Linux/Img/square.png | Bin 538 -> 0 bytes pcsx2/PAD/Linux/Img/start.png | Bin 240 -> 0 bytes pcsx2/PAD/Linux/Img/triangle.png | Bin 906 -> 0 bytes pcsx2/PAD/Linux/InputManager.cpp | 79 - pcsx2/PAD/Linux/InputManager.h | 36 - pcsx2/PAD/Linux/KeyStatus.cpp | 204 -- pcsx2/PAD/Linux/KeyStatus.h | 71 - pcsx2/PAD/Linux/PAD.cpp | 294 -- pcsx2/PAD/Linux/SDL/joystick.cpp | 261 -- pcsx2/PAD/Linux/SDL/joystick.h | 59 - pcsx2/PAD/Linux/bitwise.h | 40 - pcsx2/PAD/Linux/keyboard.cpp | 318 -- pcsx2/PAD/Linux/keyboard.h | 46 - pcsx2/PAD/Linux/state_management.cpp | 484 --- pcsx2/PAD/Linux/state_management.h | 134 - .../Linux/wx_dialog/GamepadConfiguration.cpp | 187 - .../Linux/wx_dialog/GamepadConfiguration.h | 48 - .../Linux/wx_dialog/JoystickConfiguration.cpp | 149 - .../Linux/wx_dialog/JoystickConfiguration.h | 42 - pcsx2/PAD/Linux/wx_dialog/dialog.cpp | 684 ---- pcsx2/PAD/Linux/wx_dialog/dialog.h | 89 - pcsx2/PAD/Linux/wx_dialog/opPanel.cpp | 243 -- pcsx2/PAD/Linux/wx_dialog/opPanel.h | 69 - pcsx2/PAD/Windows/Default.ini | 191 - pcsx2/PAD/Windows/DeviceEnumerator.cpp | 56 - pcsx2/PAD/Windows/DeviceEnumerator.h | 16 - pcsx2/PAD/Windows/Diagnostics.cpp | 160 - pcsx2/PAD/Windows/Diagnostics.h | 21 - pcsx2/PAD/Windows/DirectInput.cpp | 753 ---- pcsx2/PAD/Windows/DirectInput.h | 16 - pcsx2/PAD/Windows/DualShock3.cpp | 317 -- pcsx2/PAD/Windows/DualShock3.h | 21 - pcsx2/PAD/Windows/Global.h | 125 - pcsx2/PAD/Windows/HidDevice.cpp | 85 - pcsx2/PAD/Windows/HidDevice.h | 32 - pcsx2/PAD/Windows/InputManager.cpp | 783 ----- pcsx2/PAD/Windows/InputManager.h | 407 --- pcsx2/PAD/Windows/KeyboardQueue.cpp | 73 - pcsx2/PAD/Windows/KeyboardQueue.h | 32 - pcsx2/PAD/Windows/PAD.cpp | 1619 --------- pcsx2/PAD/Windows/PAD.rc | 577 --- pcsx2/PAD/Windows/PADConfig.cpp | 3120 ----------------- pcsx2/PAD/Windows/PADConfig.h | 80 - pcsx2/PAD/Windows/PADRawInput.cpp | 367 -- pcsx2/PAD/Windows/PADRawInput.h | 18 - pcsx2/PAD/Windows/Tooltips.cpp | 128 - pcsx2/PAD/Windows/Tooltips.h | 23 - pcsx2/PAD/Windows/VKey.cpp | 111 - pcsx2/PAD/Windows/VKey.h | 17 - pcsx2/PAD/Windows/WindowsKeyboard.cpp | 77 - pcsx2/PAD/Windows/WindowsKeyboard.h | 26 - pcsx2/PAD/Windows/WindowsMessaging.cpp | 222 -- pcsx2/PAD/Windows/WindowsMessaging.h | 16 - pcsx2/PAD/Windows/WindowsMouse.cpp | 106 - pcsx2/PAD/Windows/WindowsMouse.h | 40 - pcsx2/PAD/Windows/WndProcEater.cpp | 158 - pcsx2/PAD/Windows/WndProcEater.h | 63 - pcsx2/PAD/Windows/XInputEnum.cpp | 329 -- pcsx2/PAD/Windows/XInputEnum.h | 16 - pcsx2/PAD/Windows/resource_pad.h | 155 - pcsx2/PAD/Windows/usb.h | 420 --- pcsx2/{gui => }/Resources/AppIcon16.png | Bin pcsx2/{gui => }/Resources/AppIcon32.png | Bin pcsx2/{gui => }/Resources/AppIcon64.png | Bin pcsx2/{gui => }/Resources/Info.plist.in | 2 +- pcsx2/{gui => }/Resources/PCSX2.icns | Bin pcsx2/SPU2/Linux/CfgHelpers.cpp | 116 - pcsx2/SPU2/Linux/Config.cpp | 181 - pcsx2/SPU2/Linux/Config.h | 117 - pcsx2/SPU2/Linux/ConfigDebug.cpp | 176 - pcsx2/SPU2/Linux/ConfigSoundTouch.cpp | 69 - pcsx2/SPU2/Linux/Dialogs.cpp | 61 - pcsx2/SPU2/Linux/Dialogs.h | 42 - pcsx2/SPU2/Windows/CfgHelpers.cpp | 220 -- pcsx2/SPU2/Windows/Config.cpp | 533 --- pcsx2/SPU2/Windows/ConfigDebug.cpp | 292 -- pcsx2/SPU2/Windows/ConfigSoundTouch.cpp | 138 - pcsx2/SPU2/Windows/Dialogs.h | 61 - pcsx2/SPU2/Windows/RealtimeDebugger.cpp | 287 -- pcsx2/SPU2/Windows/SPU2.rc | 252 -- pcsx2/SPU2/Windows/UIHelpers.cpp | 71 - pcsx2/SPU2/Windows/WinConfig.h | 71 - pcsx2/SPU2/Windows/dsp.cpp | 185 - pcsx2/SPU2/Windows/dsp.h | 57 - pcsx2/SPU2/Windows/resource.h | 64 - pcsx2/SPU2/wx/wxConfig.cpp | 490 --- pcsx2/SPU2/wx/wxConfig.h | 97 - pcsx2/USB/USBNull.cpp | 35 - pcsx2/USB/USBNull.h | 42 - pcsx2/gui/App.h | 737 ---- pcsx2/gui/AppAccelerators.h | 183 - pcsx2/gui/AppAssert.cpp | 152 - pcsx2/gui/AppCommon.h | 34 - pcsx2/gui/AppConfig.cpp | 1395 -------- pcsx2/gui/AppConfig.h | 339 -- pcsx2/gui/AppCoreThread.cpp | 809 ----- pcsx2/gui/AppCoreThread.h | 264 -- pcsx2/gui/AppEventListeners.h | 211 -- pcsx2/gui/AppEventSources.cpp | 141 - pcsx2/gui/AppForwardDefs.h | 61 - pcsx2/gui/AppHost.cpp | 226 -- pcsx2/gui/AppHost.h | 12 - pcsx2/gui/AppInit.cpp | 748 ---- pcsx2/gui/AppMain.cpp | 964 ----- pcsx2/gui/AppRes.cpp | 180 - pcsx2/gui/AppSaveStates.h | 25 - pcsx2/gui/AppTrait.h | 39 - pcsx2/gui/AppUserMode.cpp | 301 -- pcsx2/gui/ApplyState.h | 243 -- pcsx2/gui/CheckedStaticBox.cpp | 79 - pcsx2/gui/CheckedStaticBox.h | 37 - pcsx2/gui/ConsoleLogger.cpp | 1281 ------- pcsx2/gui/ConsoleLogger.h | 247 -- pcsx2/gui/Debugger/BreakpointWindow.cpp | 347 -- pcsx2/gui/Debugger/BreakpointWindow.h | 63 - pcsx2/gui/Debugger/CtrlDisassemblyView.cpp | 1379 -------- pcsx2/gui/Debugger/CtrlDisassemblyView.h | 111 - pcsx2/gui/Debugger/CtrlMemSearch.cpp | 512 --- pcsx2/gui/Debugger/CtrlMemSearch.h | 109 - pcsx2/gui/Debugger/CtrlMemView.cpp | 830 ----- pcsx2/gui/Debugger/CtrlMemView.h | 69 - pcsx2/gui/Debugger/CtrlRegisterList.cpp | 642 ---- pcsx2/gui/Debugger/CtrlRegisterList.h | 84 - pcsx2/gui/Debugger/DebugEvents.cpp | 59 - pcsx2/gui/Debugger/DebugEvents.h | 34 - pcsx2/gui/Debugger/DebuggerLists.cpp | 821 ----- pcsx2/gui/Debugger/DebuggerLists.h | 137 - pcsx2/gui/Debugger/DisassemblyDialog.cpp | 717 ---- pcsx2/gui/Debugger/DisassemblyDialog.h | 122 - pcsx2/gui/Dialogs/AboutBoxDialog.cpp | 71 - pcsx2/gui/Dialogs/AssertionDialog.cpp | 68 - pcsx2/gui/Dialogs/BaseConfigurationDialog.cpp | 304 -- pcsx2/gui/Dialogs/BaseConfigurationDialog.inl | 27 - pcsx2/gui/Dialogs/ConfigurationDialog.h | 207 -- pcsx2/gui/Dialogs/ConfirmationDialogs.cpp | 322 -- pcsx2/gui/Dialogs/ConvertMemoryCardDialog.cpp | 242 -- pcsx2/gui/Dialogs/CreateMemoryCardDialog.cpp | 264 -- pcsx2/gui/Dialogs/FirstTimeWizard.cpp | 223 -- pcsx2/gui/Dialogs/GSDumpDialog.cpp | 983 ------ pcsx2/gui/Dialogs/ImportSettingsDialog.cpp | 70 - pcsx2/gui/Dialogs/LogOptionsDialog.cpp | 33 - pcsx2/gui/Dialogs/LogOptionsDialog.h | 47 - pcsx2/gui/Dialogs/McdConfigDialog.cpp | 154 - pcsx2/gui/Dialogs/ModalPopups.h | 225 -- pcsx2/gui/Dialogs/PINEDialog.cpp | 58 - pcsx2/gui/Dialogs/PickUserModeDialog.cpp | 46 - pcsx2/gui/Dialogs/SysConfigDialog.cpp | 242 -- pcsx2/gui/DriveList.cpp | 109 - pcsx2/gui/DriveList.h | 60 - pcsx2/gui/EmbeddedImage.h | 121 - pcsx2/gui/ExecutorThread.cpp | 514 --- pcsx2/gui/FastFormatString.cpp | 286 -- pcsx2/gui/FileUtils.cpp | 88 - pcsx2/gui/FrameForGS.cpp | 892 ----- pcsx2/gui/GSFrame.h | 138 - pcsx2/gui/GlobalCommands.cpp | 733 ---- pcsx2/gui/IniInterface.cpp | 451 --- pcsx2/gui/IniInterface.h | 200 -- pcsx2/gui/IsoDropTarget.cpp | 208 -- pcsx2/gui/IsoDropTarget.h | 41 - pcsx2/gui/MSWstuff.cpp | 44 - pcsx2/gui/MSWstuff.h | 21 - pcsx2/gui/MainFrame.cpp | 819 ----- pcsx2/gui/MainFrame.h | 216 -- pcsx2/gui/MainMenuClicks.cpp | 890 ----- pcsx2/gui/MessageBoxes.cpp | 185 - pcsx2/gui/Mutex.cpp | 346 -- .../gui/Panels/BaseApplicableConfigPanel.cpp | 224 -- pcsx2/gui/Panels/BiosSelectorPanel.cpp | 227 -- pcsx2/gui/Panels/ConfigurationPanels.h | 461 --- pcsx2/gui/Panels/CpuPanel.cpp | 398 --- pcsx2/gui/Panels/DirPickerPanel.cpp | 298 -- pcsx2/gui/Panels/GSWindowPanel.cpp | 194 - pcsx2/gui/Panels/GameFixesPanel.cpp | 187 - pcsx2/gui/Panels/LogOptionsPanels.cpp | 347 -- pcsx2/gui/Panels/LogOptionsPanels.h | 97 - pcsx2/gui/Panels/MemoryCardListPanel.cpp | 1314 ------- pcsx2/gui/Panels/MemoryCardListView.cpp | 214 -- pcsx2/gui/Panels/MemoryCardPanels.h | 310 -- pcsx2/gui/Panels/MiscPanelStuff.cpp | 187 - pcsx2/gui/Panels/PathsPanel.cpp | 94 - pcsx2/gui/Panels/SpeedhacksPanel.cpp | 366 -- pcsx2/gui/Panels/VideoPanel.cpp | 267 -- pcsx2/gui/PathDefs.h | 95 - pcsx2/gui/PathUtils.cpp | 187 - pcsx2/gui/PersistentThread.cpp | 1198 ------- pcsx2/gui/PersistentThread.h | 736 ---- pcsx2/gui/RecentIsoList.cpp | 306 -- pcsx2/gui/RecentIsoList.h | 92 - pcsx2/gui/Resources/BackgroundLogo.png | Bin 74183 -> 0 bytes pcsx2/gui/Resources/Breakpoint_Active.png | Bin 220 -> 0 bytes pcsx2/gui/Resources/Breakpoint_Inactive.png | Bin 174 -> 0 bytes pcsx2/gui/Resources/ButtonIcon_Camera.png | Bin 1132 -> 0 bytes pcsx2/gui/Resources/ConfigIcon_Cpu.png | Bin 28874 -> 0 bytes pcsx2/gui/Resources/ConfigIcon_Gamefixes.png | Bin 16191 -> 0 bytes pcsx2/gui/Resources/ConfigIcon_MemoryCard.png | Bin 17859 -> 0 bytes pcsx2/gui/Resources/ConfigIcon_Paths.png | Bin 21947 -> 0 bytes pcsx2/gui/Resources/ConfigIcon_Speedhacks.png | Bin 13943 -> 0 bytes pcsx2/gui/Resources/ConfigIcon_Video.png | Bin 35849 -> 0 bytes pcsx2/gui/Resources/Logo.png | Bin 3510 -> 0 bytes pcsx2/gui/Resources/NoIcon.png | Bin 14500 -> 0 bytes pcsx2/gui/Resources/bin2cpp.cmd | 18 - pcsx2/gui/Saveslots.cpp | 155 - pcsx2/gui/Saveslots.h | 137 - pcsx2/gui/StringHelpers.cpp | 246 -- pcsx2/gui/StringHelpers.h | 253 -- pcsx2/gui/SysCoreThread.cpp | 390 --- pcsx2/gui/SysState.cpp | 176 - pcsx2/gui/SysThreadBase.cpp | 367 -- pcsx2/gui/SysThreads.h | 290 -- pcsx2/gui/ThreadingDialogs.cpp | 85 - pcsx2/gui/ThreadingDialogs.h | 52 - pcsx2/gui/UpdateUI.cpp | 179 - pcsx2/gui/i18n.cpp | 364 -- pcsx2/gui/i18n.h | 42 - pcsx2/gui/pxCheckBox.cpp | 132 - pcsx2/gui/pxCheckBox.h | 102 - pcsx2/gui/pxEventThread.h | 307 -- pcsx2/gui/pxEvents.h | 425 --- pcsx2/gui/pxRadioPanel.cpp | 263 -- pcsx2/gui/pxRadioPanel.h | 176 - pcsx2/gui/pxStaticText.cpp | 335 -- pcsx2/gui/pxStaticText.h | 117 - pcsx2/gui/pxTranslate.cpp | 35 - pcsx2/gui/pxWindowTextWriter.cpp | 146 - pcsx2/gui/wxAppWithHelpers.cpp | 740 ---- pcsx2/gui/wxAppWithHelpers.h | 193 - pcsx2/gui/wxDirName.h | 234 -- pcsx2/gui/wxGuiTools.cpp | 652 ---- pcsx2/gui/wxGuiTools.h | 831 ----- pcsx2/gui/wxHelpers.cpp | 435 --- pcsx2/gui/wxSettingsInterface.cpp | 192 - pcsx2/gui/wxSettingsInterface.h | 64 - pcsx2/pcsx2.vcxproj | 1064 ------ pcsx2/pcsx2.vcxproj.filters | 2753 --------------- pcsx2/windows/WinConsolePipe.cpp | 239 -- pcsx2/windows/WinKeyCodes.cpp | 164 - pcsx2/windows/WinPowerProfile.cpp | 50 - 330 files changed, 4 insertions(+), 66352 deletions(-) delete mode 100644 PCSX2_suite.sln delete mode 100644 bin/PCSX2_keys.ini.default delete mode 100644 bin/resources/locale/ar_SA/pcsx2_Iconized.mo delete mode 100644 bin/resources/locale/ar_SA/pcsx2_Main.mo delete mode 100644 bin/resources/locale/ca_ES/pcsx2_Iconized.mo delete mode 100644 bin/resources/locale/ca_ES/pcsx2_Main.mo delete mode 100644 bin/resources/locale/cs_CZ/pcsx2_Iconized.mo delete mode 100644 bin/resources/locale/cs_CZ/pcsx2_Main.mo delete mode 100644 bin/resources/locale/da_DK/pcsx2_Iconized.mo delete mode 100644 bin/resources/locale/da_DK/pcsx2_Main.mo delete mode 100644 bin/resources/locale/de_DE/pcsx2_Iconized.mo delete mode 100644 bin/resources/locale/de_DE/pcsx2_Main.mo delete mode 100644 bin/resources/locale/es_ES/pcsx2_Iconized.mo delete mode 100644 bin/resources/locale/es_ES/pcsx2_Main.mo delete mode 100644 bin/resources/locale/fi_FI/pcsx2_Iconized.mo delete mode 100644 bin/resources/locale/fi_FI/pcsx2_Main.mo delete mode 100644 bin/resources/locale/fr_FR/pcsx2_Iconized.mo delete mode 100644 bin/resources/locale/fr_FR/pcsx2_Main.mo delete mode 100644 bin/resources/locale/hr_HR/pcsx2_Iconized.mo delete mode 100644 bin/resources/locale/hr_HR/pcsx2_Main.mo delete mode 100644 bin/resources/locale/hu_HU/pcsx2_Iconized.mo delete mode 100644 bin/resources/locale/hu_HU/pcsx2_Main.mo delete mode 100644 bin/resources/locale/id_ID/pcsx2_Iconized.mo delete mode 100644 bin/resources/locale/id_ID/pcsx2_Main.mo delete mode 100644 bin/resources/locale/it_IT/pcsx2_Iconized.mo delete mode 100644 bin/resources/locale/it_IT/pcsx2_Main.mo delete mode 100644 bin/resources/locale/ja_JP/pcsx2_Iconized.mo delete mode 100644 bin/resources/locale/ja_JP/pcsx2_Main.mo delete mode 100644 bin/resources/locale/ko_KR/pcsx2_Iconized.mo delete mode 100644 bin/resources/locale/ko_KR/pcsx2_Main.mo delete mode 100644 bin/resources/locale/lt_LT/pcsx2_Iconized.mo delete mode 100644 bin/resources/locale/lt_LT/pcsx2_Main.mo delete mode 100644 bin/resources/locale/ms_MY/pcsx2_Iconized.mo delete mode 100644 bin/resources/locale/ms_MY/pcsx2_Main.mo delete mode 100644 bin/resources/locale/nb_NO/pcsx2_Iconized.mo delete mode 100644 bin/resources/locale/nb_NO/pcsx2_Main.mo delete mode 100644 bin/resources/locale/nl_NL/pcsx2_Iconized.mo delete mode 100644 bin/resources/locale/nl_NL/pcsx2_Main.mo delete mode 100644 bin/resources/locale/pl_PL/pcsx2_Iconized.mo delete mode 100644 bin/resources/locale/pl_PL/pcsx2_Main.mo delete mode 100644 bin/resources/locale/pt_BR/pcsx2_Iconized.mo delete mode 100644 bin/resources/locale/pt_BR/pcsx2_Main.mo delete mode 100644 bin/resources/locale/ru_RU/pcsx2_Iconized.mo delete mode 100644 bin/resources/locale/ru_RU/pcsx2_Main.mo delete mode 100644 bin/resources/locale/sv_SE/pcsx2_Iconized.mo delete mode 100644 bin/resources/locale/sv_SE/pcsx2_Main.mo delete mode 100644 bin/resources/locale/th_TH/pcsx2_Iconized.mo delete mode 100644 bin/resources/locale/th_TH/pcsx2_Main.mo delete mode 100644 bin/resources/locale/tr_TR/pcsx2_Iconized.mo delete mode 100644 bin/resources/locale/tr_TR/pcsx2_Main.mo delete mode 100644 bin/resources/locale/zh_CN/pcsx2_Iconized.mo delete mode 100644 bin/resources/locale/zh_CN/pcsx2_Main.mo delete mode 100644 bin/resources/locale/zh_TW/pcsx2_Iconized.mo delete mode 100644 bin/resources/locale/zh_TW/pcsx2_Main.mo delete mode 100644 pcsx2/DEV9/ATA/HddCreateWx.cpp delete mode 100644 pcsx2/DEV9/ATA/HddCreateWx.h delete mode 100644 pcsx2/DEV9/ConfigUI.cpp delete mode 100644 pcsx2/DEV9/DEV9Config.cpp delete mode 100644 pcsx2/DEV9/DEV9Config.h delete mode 100644 pcsx2/Darwin/DarwinKeyCodes.cpp delete mode 100644 pcsx2/GS/Window/GSwxDialog.cpp delete mode 100644 pcsx2/GS/Window/GSwxDialog.h delete mode 100644 pcsx2/Linux/LnxConsolePipe.cpp delete mode 100644 pcsx2/Linux/LnxKeyCodes.cpp delete mode 100644 pcsx2/PAD/Linux/Config.cpp delete mode 100644 pcsx2/PAD/Linux/Config.h delete mode 100644 pcsx2/PAD/Linux/Device.cpp delete mode 100644 pcsx2/PAD/Linux/Device.h delete mode 100644 pcsx2/PAD/Linux/Global.h delete mode 100644 pcsx2/PAD/Linux/Img/analog-2.png delete mode 100644 pcsx2/PAD/Linux/Img/analog.png delete mode 100644 pcsx2/PAD/Linux/Img/arrow_bottom.png delete mode 100644 pcsx2/PAD/Linux/Img/arrow_left.png delete mode 100644 pcsx2/PAD/Linux/Img/arrow_right.png delete mode 100644 pcsx2/PAD/Linux/Img/arrow_up.png delete mode 100644 pcsx2/PAD/Linux/Img/circle.png delete mode 100644 pcsx2/PAD/Linux/Img/cross.png delete mode 100644 pcsx2/PAD/Linux/Img/dp_bottom.png delete mode 100644 pcsx2/PAD/Linux/Img/dp_left.png delete mode 100644 pcsx2/PAD/Linux/Img/dp_right.png delete mode 100644 pcsx2/PAD/Linux/Img/dp_up.png delete mode 100644 pcsx2/PAD/Linux/Img/dualshock2.png delete mode 100644 pcsx2/PAD/Linux/Img/joystick_cursor.png delete mode 100644 pcsx2/PAD/Linux/Img/l1.png delete mode 100644 pcsx2/PAD/Linux/Img/l2.png delete mode 100644 pcsx2/PAD/Linux/Img/l3.png delete mode 100644 pcsx2/PAD/Linux/Img/r1.png delete mode 100644 pcsx2/PAD/Linux/Img/r2.png delete mode 100644 pcsx2/PAD/Linux/Img/r3.png delete mode 100644 pcsx2/PAD/Linux/Img/select.png delete mode 100644 pcsx2/PAD/Linux/Img/square.png delete mode 100644 pcsx2/PAD/Linux/Img/start.png delete mode 100644 pcsx2/PAD/Linux/Img/triangle.png delete mode 100644 pcsx2/PAD/Linux/InputManager.cpp delete mode 100644 pcsx2/PAD/Linux/InputManager.h delete mode 100644 pcsx2/PAD/Linux/KeyStatus.cpp delete mode 100644 pcsx2/PAD/Linux/KeyStatus.h delete mode 100644 pcsx2/PAD/Linux/PAD.cpp delete mode 100644 pcsx2/PAD/Linux/SDL/joystick.cpp delete mode 100644 pcsx2/PAD/Linux/SDL/joystick.h delete mode 100644 pcsx2/PAD/Linux/bitwise.h delete mode 100644 pcsx2/PAD/Linux/keyboard.cpp delete mode 100644 pcsx2/PAD/Linux/keyboard.h delete mode 100644 pcsx2/PAD/Linux/state_management.cpp delete mode 100644 pcsx2/PAD/Linux/state_management.h delete mode 100644 pcsx2/PAD/Linux/wx_dialog/GamepadConfiguration.cpp delete mode 100644 pcsx2/PAD/Linux/wx_dialog/GamepadConfiguration.h delete mode 100644 pcsx2/PAD/Linux/wx_dialog/JoystickConfiguration.cpp delete mode 100644 pcsx2/PAD/Linux/wx_dialog/JoystickConfiguration.h delete mode 100644 pcsx2/PAD/Linux/wx_dialog/dialog.cpp delete mode 100644 pcsx2/PAD/Linux/wx_dialog/dialog.h delete mode 100644 pcsx2/PAD/Linux/wx_dialog/opPanel.cpp delete mode 100644 pcsx2/PAD/Linux/wx_dialog/opPanel.h delete mode 100644 pcsx2/PAD/Windows/Default.ini delete mode 100644 pcsx2/PAD/Windows/DeviceEnumerator.cpp delete mode 100644 pcsx2/PAD/Windows/DeviceEnumerator.h delete mode 100644 pcsx2/PAD/Windows/Diagnostics.cpp delete mode 100644 pcsx2/PAD/Windows/Diagnostics.h delete mode 100644 pcsx2/PAD/Windows/DirectInput.cpp delete mode 100644 pcsx2/PAD/Windows/DirectInput.h delete mode 100644 pcsx2/PAD/Windows/DualShock3.cpp delete mode 100644 pcsx2/PAD/Windows/DualShock3.h delete mode 100644 pcsx2/PAD/Windows/Global.h delete mode 100644 pcsx2/PAD/Windows/HidDevice.cpp delete mode 100644 pcsx2/PAD/Windows/HidDevice.h delete mode 100644 pcsx2/PAD/Windows/InputManager.cpp delete mode 100644 pcsx2/PAD/Windows/InputManager.h delete mode 100644 pcsx2/PAD/Windows/KeyboardQueue.cpp delete mode 100644 pcsx2/PAD/Windows/KeyboardQueue.h delete mode 100644 pcsx2/PAD/Windows/PAD.cpp delete mode 100644 pcsx2/PAD/Windows/PAD.rc delete mode 100644 pcsx2/PAD/Windows/PADConfig.cpp delete mode 100644 pcsx2/PAD/Windows/PADConfig.h delete mode 100644 pcsx2/PAD/Windows/PADRawInput.cpp delete mode 100644 pcsx2/PAD/Windows/PADRawInput.h delete mode 100644 pcsx2/PAD/Windows/Tooltips.cpp delete mode 100644 pcsx2/PAD/Windows/Tooltips.h delete mode 100644 pcsx2/PAD/Windows/VKey.cpp delete mode 100644 pcsx2/PAD/Windows/VKey.h delete mode 100644 pcsx2/PAD/Windows/WindowsKeyboard.cpp delete mode 100644 pcsx2/PAD/Windows/WindowsKeyboard.h delete mode 100644 pcsx2/PAD/Windows/WindowsMessaging.cpp delete mode 100644 pcsx2/PAD/Windows/WindowsMessaging.h delete mode 100644 pcsx2/PAD/Windows/WindowsMouse.cpp delete mode 100644 pcsx2/PAD/Windows/WindowsMouse.h delete mode 100644 pcsx2/PAD/Windows/WndProcEater.cpp delete mode 100644 pcsx2/PAD/Windows/WndProcEater.h delete mode 100644 pcsx2/PAD/Windows/XInputEnum.cpp delete mode 100644 pcsx2/PAD/Windows/XInputEnum.h delete mode 100644 pcsx2/PAD/Windows/resource_pad.h delete mode 100644 pcsx2/PAD/Windows/usb.h rename pcsx2/{gui => }/Resources/AppIcon16.png (100%) rename pcsx2/{gui => }/Resources/AppIcon32.png (100%) rename pcsx2/{gui => }/Resources/AppIcon64.png (100%) rename pcsx2/{gui => }/Resources/Info.plist.in (97%) rename pcsx2/{gui => }/Resources/PCSX2.icns (100%) delete mode 100644 pcsx2/SPU2/Linux/CfgHelpers.cpp delete mode 100644 pcsx2/SPU2/Linux/Config.cpp delete mode 100644 pcsx2/SPU2/Linux/Config.h delete mode 100644 pcsx2/SPU2/Linux/ConfigDebug.cpp delete mode 100644 pcsx2/SPU2/Linux/ConfigSoundTouch.cpp delete mode 100644 pcsx2/SPU2/Linux/Dialogs.cpp delete mode 100644 pcsx2/SPU2/Linux/Dialogs.h delete mode 100644 pcsx2/SPU2/Windows/CfgHelpers.cpp delete mode 100644 pcsx2/SPU2/Windows/Config.cpp delete mode 100644 pcsx2/SPU2/Windows/ConfigDebug.cpp delete mode 100644 pcsx2/SPU2/Windows/ConfigSoundTouch.cpp delete mode 100644 pcsx2/SPU2/Windows/Dialogs.h delete mode 100644 pcsx2/SPU2/Windows/RealtimeDebugger.cpp delete mode 100644 pcsx2/SPU2/Windows/SPU2.rc delete mode 100644 pcsx2/SPU2/Windows/UIHelpers.cpp delete mode 100644 pcsx2/SPU2/Windows/WinConfig.h delete mode 100644 pcsx2/SPU2/Windows/dsp.cpp delete mode 100644 pcsx2/SPU2/Windows/dsp.h delete mode 100644 pcsx2/SPU2/Windows/resource.h delete mode 100644 pcsx2/SPU2/wx/wxConfig.cpp delete mode 100644 pcsx2/SPU2/wx/wxConfig.h delete mode 100644 pcsx2/USB/USBNull.cpp delete mode 100644 pcsx2/USB/USBNull.h delete mode 100644 pcsx2/gui/App.h delete mode 100644 pcsx2/gui/AppAccelerators.h delete mode 100644 pcsx2/gui/AppAssert.cpp delete mode 100644 pcsx2/gui/AppCommon.h delete mode 100644 pcsx2/gui/AppConfig.cpp delete mode 100644 pcsx2/gui/AppConfig.h delete mode 100644 pcsx2/gui/AppCoreThread.cpp delete mode 100644 pcsx2/gui/AppCoreThread.h delete mode 100644 pcsx2/gui/AppEventListeners.h delete mode 100644 pcsx2/gui/AppEventSources.cpp delete mode 100644 pcsx2/gui/AppForwardDefs.h delete mode 100644 pcsx2/gui/AppHost.cpp delete mode 100644 pcsx2/gui/AppHost.h delete mode 100644 pcsx2/gui/AppInit.cpp delete mode 100644 pcsx2/gui/AppMain.cpp delete mode 100644 pcsx2/gui/AppRes.cpp delete mode 100644 pcsx2/gui/AppSaveStates.h delete mode 100644 pcsx2/gui/AppTrait.h delete mode 100644 pcsx2/gui/AppUserMode.cpp delete mode 100644 pcsx2/gui/ApplyState.h delete mode 100644 pcsx2/gui/CheckedStaticBox.cpp delete mode 100644 pcsx2/gui/CheckedStaticBox.h delete mode 100644 pcsx2/gui/ConsoleLogger.cpp delete mode 100644 pcsx2/gui/ConsoleLogger.h delete mode 100644 pcsx2/gui/Debugger/BreakpointWindow.cpp delete mode 100644 pcsx2/gui/Debugger/BreakpointWindow.h delete mode 100644 pcsx2/gui/Debugger/CtrlDisassemblyView.cpp delete mode 100644 pcsx2/gui/Debugger/CtrlDisassemblyView.h delete mode 100644 pcsx2/gui/Debugger/CtrlMemSearch.cpp delete mode 100644 pcsx2/gui/Debugger/CtrlMemSearch.h delete mode 100644 pcsx2/gui/Debugger/CtrlMemView.cpp delete mode 100644 pcsx2/gui/Debugger/CtrlMemView.h delete mode 100644 pcsx2/gui/Debugger/CtrlRegisterList.cpp delete mode 100644 pcsx2/gui/Debugger/CtrlRegisterList.h delete mode 100644 pcsx2/gui/Debugger/DebugEvents.cpp delete mode 100644 pcsx2/gui/Debugger/DebugEvents.h delete mode 100644 pcsx2/gui/Debugger/DebuggerLists.cpp delete mode 100644 pcsx2/gui/Debugger/DebuggerLists.h delete mode 100644 pcsx2/gui/Debugger/DisassemblyDialog.cpp delete mode 100644 pcsx2/gui/Debugger/DisassemblyDialog.h delete mode 100644 pcsx2/gui/Dialogs/AboutBoxDialog.cpp delete mode 100644 pcsx2/gui/Dialogs/AssertionDialog.cpp delete mode 100644 pcsx2/gui/Dialogs/BaseConfigurationDialog.cpp delete mode 100644 pcsx2/gui/Dialogs/BaseConfigurationDialog.inl delete mode 100644 pcsx2/gui/Dialogs/ConfigurationDialog.h delete mode 100644 pcsx2/gui/Dialogs/ConfirmationDialogs.cpp delete mode 100644 pcsx2/gui/Dialogs/ConvertMemoryCardDialog.cpp delete mode 100644 pcsx2/gui/Dialogs/CreateMemoryCardDialog.cpp delete mode 100644 pcsx2/gui/Dialogs/FirstTimeWizard.cpp delete mode 100644 pcsx2/gui/Dialogs/GSDumpDialog.cpp delete mode 100644 pcsx2/gui/Dialogs/ImportSettingsDialog.cpp delete mode 100644 pcsx2/gui/Dialogs/LogOptionsDialog.cpp delete mode 100644 pcsx2/gui/Dialogs/LogOptionsDialog.h delete mode 100644 pcsx2/gui/Dialogs/McdConfigDialog.cpp delete mode 100644 pcsx2/gui/Dialogs/ModalPopups.h delete mode 100644 pcsx2/gui/Dialogs/PINEDialog.cpp delete mode 100644 pcsx2/gui/Dialogs/PickUserModeDialog.cpp delete mode 100644 pcsx2/gui/Dialogs/SysConfigDialog.cpp delete mode 100644 pcsx2/gui/DriveList.cpp delete mode 100644 pcsx2/gui/DriveList.h delete mode 100644 pcsx2/gui/EmbeddedImage.h delete mode 100644 pcsx2/gui/ExecutorThread.cpp delete mode 100644 pcsx2/gui/FastFormatString.cpp delete mode 100644 pcsx2/gui/FileUtils.cpp delete mode 100644 pcsx2/gui/FrameForGS.cpp delete mode 100644 pcsx2/gui/GSFrame.h delete mode 100644 pcsx2/gui/GlobalCommands.cpp delete mode 100644 pcsx2/gui/IniInterface.cpp delete mode 100644 pcsx2/gui/IniInterface.h delete mode 100644 pcsx2/gui/IsoDropTarget.cpp delete mode 100644 pcsx2/gui/IsoDropTarget.h delete mode 100644 pcsx2/gui/MSWstuff.cpp delete mode 100644 pcsx2/gui/MSWstuff.h delete mode 100644 pcsx2/gui/MainFrame.cpp delete mode 100644 pcsx2/gui/MainFrame.h delete mode 100644 pcsx2/gui/MainMenuClicks.cpp delete mode 100644 pcsx2/gui/MessageBoxes.cpp delete mode 100644 pcsx2/gui/Mutex.cpp delete mode 100644 pcsx2/gui/Panels/BaseApplicableConfigPanel.cpp delete mode 100644 pcsx2/gui/Panels/BiosSelectorPanel.cpp delete mode 100644 pcsx2/gui/Panels/ConfigurationPanels.h delete mode 100644 pcsx2/gui/Panels/CpuPanel.cpp delete mode 100644 pcsx2/gui/Panels/DirPickerPanel.cpp delete mode 100644 pcsx2/gui/Panels/GSWindowPanel.cpp delete mode 100644 pcsx2/gui/Panels/GameFixesPanel.cpp delete mode 100644 pcsx2/gui/Panels/LogOptionsPanels.cpp delete mode 100644 pcsx2/gui/Panels/LogOptionsPanels.h delete mode 100644 pcsx2/gui/Panels/MemoryCardListPanel.cpp delete mode 100644 pcsx2/gui/Panels/MemoryCardListView.cpp delete mode 100644 pcsx2/gui/Panels/MemoryCardPanels.h delete mode 100644 pcsx2/gui/Panels/MiscPanelStuff.cpp delete mode 100644 pcsx2/gui/Panels/PathsPanel.cpp delete mode 100644 pcsx2/gui/Panels/SpeedhacksPanel.cpp delete mode 100644 pcsx2/gui/Panels/VideoPanel.cpp delete mode 100644 pcsx2/gui/PathDefs.h delete mode 100644 pcsx2/gui/PathUtils.cpp delete mode 100644 pcsx2/gui/PersistentThread.cpp delete mode 100644 pcsx2/gui/PersistentThread.h delete mode 100644 pcsx2/gui/RecentIsoList.cpp delete mode 100644 pcsx2/gui/RecentIsoList.h delete mode 100644 pcsx2/gui/Resources/BackgroundLogo.png delete mode 100644 pcsx2/gui/Resources/Breakpoint_Active.png delete mode 100644 pcsx2/gui/Resources/Breakpoint_Inactive.png delete mode 100644 pcsx2/gui/Resources/ButtonIcon_Camera.png delete mode 100644 pcsx2/gui/Resources/ConfigIcon_Cpu.png delete mode 100644 pcsx2/gui/Resources/ConfigIcon_Gamefixes.png delete mode 100644 pcsx2/gui/Resources/ConfigIcon_MemoryCard.png delete mode 100644 pcsx2/gui/Resources/ConfigIcon_Paths.png delete mode 100644 pcsx2/gui/Resources/ConfigIcon_Speedhacks.png delete mode 100644 pcsx2/gui/Resources/ConfigIcon_Video.png delete mode 100644 pcsx2/gui/Resources/Logo.png delete mode 100644 pcsx2/gui/Resources/NoIcon.png delete mode 100644 pcsx2/gui/Resources/bin2cpp.cmd delete mode 100644 pcsx2/gui/Saveslots.cpp delete mode 100644 pcsx2/gui/Saveslots.h delete mode 100644 pcsx2/gui/StringHelpers.cpp delete mode 100644 pcsx2/gui/StringHelpers.h delete mode 100644 pcsx2/gui/SysCoreThread.cpp delete mode 100644 pcsx2/gui/SysState.cpp delete mode 100644 pcsx2/gui/SysThreadBase.cpp delete mode 100644 pcsx2/gui/SysThreads.h delete mode 100644 pcsx2/gui/ThreadingDialogs.cpp delete mode 100644 pcsx2/gui/ThreadingDialogs.h delete mode 100644 pcsx2/gui/UpdateUI.cpp delete mode 100644 pcsx2/gui/i18n.cpp delete mode 100644 pcsx2/gui/i18n.h delete mode 100644 pcsx2/gui/pxCheckBox.cpp delete mode 100644 pcsx2/gui/pxCheckBox.h delete mode 100644 pcsx2/gui/pxEventThread.h delete mode 100644 pcsx2/gui/pxEvents.h delete mode 100644 pcsx2/gui/pxRadioPanel.cpp delete mode 100644 pcsx2/gui/pxRadioPanel.h delete mode 100644 pcsx2/gui/pxStaticText.cpp delete mode 100644 pcsx2/gui/pxStaticText.h delete mode 100644 pcsx2/gui/pxTranslate.cpp delete mode 100644 pcsx2/gui/pxWindowTextWriter.cpp delete mode 100644 pcsx2/gui/wxAppWithHelpers.cpp delete mode 100644 pcsx2/gui/wxAppWithHelpers.h delete mode 100644 pcsx2/gui/wxDirName.h delete mode 100644 pcsx2/gui/wxGuiTools.cpp delete mode 100644 pcsx2/gui/wxGuiTools.h delete mode 100644 pcsx2/gui/wxHelpers.cpp delete mode 100644 pcsx2/gui/wxSettingsInterface.cpp delete mode 100644 pcsx2/gui/wxSettingsInterface.h delete mode 100644 pcsx2/pcsx2.vcxproj delete mode 100644 pcsx2/pcsx2.vcxproj.filters delete mode 100644 pcsx2/windows/WinConsolePipe.cpp delete mode 100644 pcsx2/windows/WinKeyCodes.cpp delete mode 100644 pcsx2/windows/WinPowerProfile.cpp diff --git a/.github/workflows/scripts/linux/appimage-qt.sh b/.github/workflows/scripts/linux/appimage-qt.sh index 6e7bd44a5a..ba149b6ba5 100755 --- a/.github/workflows/scripts/linux/appimage-qt.sh +++ b/.github/workflows/scripts/linux/appimage-qt.sh @@ -280,7 +280,7 @@ Plugins = ../lib/plugins EOF echo "Copy desktop/icon..." -cp "$PCSX2DIR/pcsx2/gui/Resources/AppIcon64.png" "$OUTDIR/PCSX2.png" +cp "$PCSX2DIR/pcsx2/Resources/AppIcon64.png" "$OUTDIR/PCSX2.png" cp "$SCRIPTDIR/pcsx2-qt.desktop" "$OUTDIR/PCSX2.desktop" cp "$SCRIPTDIR/AppRun-qt" "$OUTDIR/AppRun" diff --git a/PCSX2_suite.sln b/PCSX2_suite.sln deleted file mode 100644 index f6e65a4b55..0000000000 --- a/PCSX2_suite.sln +++ /dev/null @@ -1,423 +0,0 @@ -Microsoft Visual Studio Solution File, Format Version 12.00 -# Visual Studio Version 17 -VisualStudioVersion = 17.0.31606.5 -MinimumVisualStudioVersion = 10.0.40219.1 -Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "3rdparty", "3rdparty", "{78EBE642-7A4D-4EA7-86BE-5639C6646C38}" - ProjectSection(SolutionItems) = preProject - 3rdparty\svn_readme.txt = 3rdparty\svn_readme.txt - EndProjectSection -EndProject -Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "Tools", "Tools", "{2D6F0A62-A247-4CCF-947F-FCD54BE16103}" -EndProject -Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "pcsx2", "pcsx2\pcsx2.vcxproj", "{1CEFD830-2B76-4596-A4EE-BCD7280A60BD}" - ProjectSection(ProjectDependencies) = postProject - {12728250-16EC-4DC6-94D7-E21DD88947F8} = {12728250-16EC-4DC6-94D7-E21DD88947F8} - {D6973076-9317-4EF2-A0B8-B7A18AC0713E} = {D6973076-9317-4EF2-A0B8-B7A18AC0713E} - EndProjectSection -EndProject -Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "SoundTouch", "3rdparty\soundtouch\SoundTouch.vcxproj", "{E9B51944-7E6D-4BCD-83F2-7BBD5A46182D}" -EndProject -Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "zlib", "3rdparty\zlib\zlib.vcxproj", "{2F6C0388-20CB-4242-9F6C-A6EBB6A83F47}" -EndProject -Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "bin2cpp", "tools\bin2cpp\bin2c.vcxproj", "{677B7D11-D5E1-40B3-88B1-9A4DF83D2213}" -EndProject -Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "libjpeg", "3rdparty\libjpeg\libjpeg.vcxproj", "{BC236261-77E8-4567-8D09-45CD02965EB6}" -EndProject -Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "wx30_config", "3rdparty\wxwidgets3.0\build\msw\wx30_config.vcxproj", "{01F4CE10-2CFB-41A8-B41F-E54337868A1D}" -EndProject -Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "wx30_base", "3rdparty\wxwidgets3.0\build\msw\wx30_base.vcxproj", "{3FCC50C2-81E9-5DB2-B8D8-2129427568B1}" -EndProject -Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "wx30_adv", "3rdparty\wxwidgets3.0\build\msw\wx30_adv.vcxproj", "{24C45343-FD20-5C92-81C1-35A2AE841E79}" -EndProject -Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "wx30_core", "3rdparty\wxwidgets3.0\build\msw\wx30_core.vcxproj", "{6744DAD8-9C70-574A-BFF2-9F8DDDB24A75}" -EndProject -Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "libpng", "3rdparty\libpng\projects\vstudio\libpng\libpng.vcxproj", "{D6973076-9317-4EF2-A0B8-B7A18AC0713E}" -EndProject -Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "pthreads4w", "3rdparty\pthreads4w\build\pthreads4w.vcxproj", "{0FAE817D-9A32-4830-857E-81DA57246E16}" -EndProject -Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "liblzma", "3rdparty\xz\liblzma.vcxproj", "{12728250-16EC-4DC6-94D7-E21DD88947F8}" -EndProject -Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "fmt", "3rdparty\fmt\fmt.vcxproj", "{449AD25E-424A-4714-BABC-68706CDCC33B}" -EndProject -Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "libchdr", "3rdparty\libchdr\libchdr.vcxproj", "{A0D2B3AD-1F72-4EE3-8B5C-F2C358DA35F0}" -EndProject -Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "jpgd", "3rdparty\jpgd\jpgd.vcxproj", "{ED2F21FD-0A36-4A8F-9B90-E7D92A2ACB63}" -EndProject -Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "common", "common\common.vcxproj", "{4639972E-424E-4E13-8B07-CA403C481346}" -EndProject -Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "glad", "3rdparty\glad\glad.vcxproj", "{C0293B32-5ACF-40F0-AA6C-E6DA6F3BF33A}" -EndProject -Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "ryml", "3rdparty\rapidyaml\ryml.vcxproj", "{DE9653B6-17DD-356A-9EE0-28A731772587}" -EndProject -Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "cubeb", "3rdparty\cubeb\cubeb.vcxproj", "{BF74C473-DC04-44B3-92E8-4145F4E77342}" -EndProject -Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "simpleini", "3rdparty\simpleini\simpleini.vcxproj", "{1EC8B3C0-8FB3-46DE-A2E0-A9121203F266}" -EndProject -Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "imgui", "3rdparty\imgui\imgui.vcxproj", "{88FB34EC-845E-4F21-A552-F1573B9ED167}" -EndProject -Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "glslang", "3rdparty\glslang\glslang.vcxproj", "{EF6834A9-11F3-4331-BC34-21B325ABB180}" -EndProject -Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "libzip", "3rdparty\libzip\libzip.vcxproj", "{20B2E9FE-F020-42A0-B324-956F5B06EA68}" -EndProject -Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "zstd", "3rdparty\zstd\zstd.vcxproj", "{52244028-937A-44E9-A76B-2BEA18FD239A}" -EndProject -Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "d3d12memalloc", "3rdparty\d3d12memalloc\d3d12memalloc.vcxproj", "{D45CEC7A-3171-40DD-975D-E1544CF16139}" -EndProject -Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "lzma", "3rdparty\lzma\lzma.vcxproj", "{A4323327-3F2B-4271-83D9-7F9A3C66B6B2}" -EndProject -Global - GlobalSection(SolutionConfigurationPlatforms) = preSolution - Debug AVX2|x64 = Debug AVX2|x64 - Debug|x64 = Debug|x64 - Devel AVX2|x64 = Devel AVX2|x64 - Devel|x64 = Devel|x64 - Release AVX2|x64 = Release AVX2|x64 - Release|x64 = Release|x64 - EndGlobalSection - GlobalSection(ProjectConfigurationPlatforms) = postSolution - {1CEFD830-2B76-4596-A4EE-BCD7280A60BD}.Debug AVX2|x64.ActiveCfg = Debug AVX2|x64 - {1CEFD830-2B76-4596-A4EE-BCD7280A60BD}.Debug AVX2|x64.Build.0 = Debug AVX2|x64 - {1CEFD830-2B76-4596-A4EE-BCD7280A60BD}.Debug|x64.ActiveCfg = Debug|x64 - {1CEFD830-2B76-4596-A4EE-BCD7280A60BD}.Debug|x64.Build.0 = Debug|x64 - {1CEFD830-2B76-4596-A4EE-BCD7280A60BD}.Devel AVX2|x64.ActiveCfg = Devel AVX2|x64 - {1CEFD830-2B76-4596-A4EE-BCD7280A60BD}.Devel AVX2|x64.Build.0 = Devel AVX2|x64 - {1CEFD830-2B76-4596-A4EE-BCD7280A60BD}.Devel|x64.ActiveCfg = Devel|x64 - {1CEFD830-2B76-4596-A4EE-BCD7280A60BD}.Devel|x64.Build.0 = Devel|x64 - {1CEFD830-2B76-4596-A4EE-BCD7280A60BD}.Release AVX2|x64.ActiveCfg = Release AVX2|x64 - {1CEFD830-2B76-4596-A4EE-BCD7280A60BD}.Release AVX2|x64.Build.0 = Release AVX2|x64 - {1CEFD830-2B76-4596-A4EE-BCD7280A60BD}.Release|x64.ActiveCfg = Release|x64 - {1CEFD830-2B76-4596-A4EE-BCD7280A60BD}.Release|x64.Build.0 = Release|x64 - {E9B51944-7E6D-4BCD-83F2-7BBD5A46182D}.Debug AVX2|x64.ActiveCfg = Debug|x64 - {E9B51944-7E6D-4BCD-83F2-7BBD5A46182D}.Debug AVX2|x64.Build.0 = Debug|x64 - {E9B51944-7E6D-4BCD-83F2-7BBD5A46182D}.Debug|x64.ActiveCfg = Debug|x64 - {E9B51944-7E6D-4BCD-83F2-7BBD5A46182D}.Debug|x64.Build.0 = Debug|x64 - {E9B51944-7E6D-4BCD-83F2-7BBD5A46182D}.Devel AVX2|x64.ActiveCfg = Devel|x64 - {E9B51944-7E6D-4BCD-83F2-7BBD5A46182D}.Devel AVX2|x64.Build.0 = Devel|x64 - {E9B51944-7E6D-4BCD-83F2-7BBD5A46182D}.Devel|x64.ActiveCfg = Devel|x64 - {E9B51944-7E6D-4BCD-83F2-7BBD5A46182D}.Devel|x64.Build.0 = Devel|x64 - {E9B51944-7E6D-4BCD-83F2-7BBD5A46182D}.Release AVX2|x64.ActiveCfg = Release|x64 - {E9B51944-7E6D-4BCD-83F2-7BBD5A46182D}.Release AVX2|x64.Build.0 = Release|x64 - {E9B51944-7E6D-4BCD-83F2-7BBD5A46182D}.Release|x64.ActiveCfg = Release|x64 - {E9B51944-7E6D-4BCD-83F2-7BBD5A46182D}.Release|x64.Build.0 = Release|x64 - {2F6C0388-20CB-4242-9F6C-A6EBB6A83F47}.Debug AVX2|x64.ActiveCfg = Debug|x64 - {2F6C0388-20CB-4242-9F6C-A6EBB6A83F47}.Debug AVX2|x64.Build.0 = Debug|x64 - {2F6C0388-20CB-4242-9F6C-A6EBB6A83F47}.Debug|x64.ActiveCfg = Debug|x64 - {2F6C0388-20CB-4242-9F6C-A6EBB6A83F47}.Debug|x64.Build.0 = Debug|x64 - {2F6C0388-20CB-4242-9F6C-A6EBB6A83F47}.Devel AVX2|x64.ActiveCfg = Devel|x64 - {2F6C0388-20CB-4242-9F6C-A6EBB6A83F47}.Devel AVX2|x64.Build.0 = Devel|x64 - {2F6C0388-20CB-4242-9F6C-A6EBB6A83F47}.Devel|x64.ActiveCfg = Devel|x64 - {2F6C0388-20CB-4242-9F6C-A6EBB6A83F47}.Devel|x64.Build.0 = Devel|x64 - {2F6C0388-20CB-4242-9F6C-A6EBB6A83F47}.Release AVX2|x64.ActiveCfg = Release|x64 - {2F6C0388-20CB-4242-9F6C-A6EBB6A83F47}.Release AVX2|x64.Build.0 = Release|x64 - {2F6C0388-20CB-4242-9F6C-A6EBB6A83F47}.Release|x64.ActiveCfg = Release|x64 - {2F6C0388-20CB-4242-9F6C-A6EBB6A83F47}.Release|x64.Build.0 = Release|x64 - {677B7D11-D5E1-40B3-88B1-9A4DF83D2213}.Debug AVX2|x64.ActiveCfg = Debug|x64 - {677B7D11-D5E1-40B3-88B1-9A4DF83D2213}.Debug AVX2|x64.Build.0 = Debug|x64 - {677B7D11-D5E1-40B3-88B1-9A4DF83D2213}.Debug|x64.ActiveCfg = Debug|x64 - {677B7D11-D5E1-40B3-88B1-9A4DF83D2213}.Debug|x64.Build.0 = Debug|x64 - {677B7D11-D5E1-40B3-88B1-9A4DF83D2213}.Devel AVX2|x64.ActiveCfg = Devel|x64 - {677B7D11-D5E1-40B3-88B1-9A4DF83D2213}.Devel AVX2|x64.Build.0 = Devel|x64 - {677B7D11-D5E1-40B3-88B1-9A4DF83D2213}.Devel|x64.ActiveCfg = Devel|x64 - {677B7D11-D5E1-40B3-88B1-9A4DF83D2213}.Devel|x64.Build.0 = Devel|x64 - {677B7D11-D5E1-40B3-88B1-9A4DF83D2213}.Release AVX2|x64.ActiveCfg = Release|x64 - {677B7D11-D5E1-40B3-88B1-9A4DF83D2213}.Release AVX2|x64.Build.0 = Release|x64 - {677B7D11-D5E1-40B3-88B1-9A4DF83D2213}.Release|x64.ActiveCfg = Release|x64 - {677B7D11-D5E1-40B3-88B1-9A4DF83D2213}.Release|x64.Build.0 = Release|x64 - {BC236261-77E8-4567-8D09-45CD02965EB6}.Debug AVX2|x64.ActiveCfg = Debug|x64 - {BC236261-77E8-4567-8D09-45CD02965EB6}.Debug AVX2|x64.Build.0 = Debug|x64 - {BC236261-77E8-4567-8D09-45CD02965EB6}.Debug|x64.ActiveCfg = Debug|x64 - {BC236261-77E8-4567-8D09-45CD02965EB6}.Debug|x64.Build.0 = Debug|x64 - {BC236261-77E8-4567-8D09-45CD02965EB6}.Devel AVX2|x64.ActiveCfg = Devel|x64 - {BC236261-77E8-4567-8D09-45CD02965EB6}.Devel AVX2|x64.Build.0 = Devel|x64 - {BC236261-77E8-4567-8D09-45CD02965EB6}.Devel|x64.ActiveCfg = Devel|x64 - {BC236261-77E8-4567-8D09-45CD02965EB6}.Devel|x64.Build.0 = Devel|x64 - {BC236261-77E8-4567-8D09-45CD02965EB6}.Release AVX2|x64.ActiveCfg = Release|x64 - {BC236261-77E8-4567-8D09-45CD02965EB6}.Release AVX2|x64.Build.0 = Release|x64 - {BC236261-77E8-4567-8D09-45CD02965EB6}.Release|x64.ActiveCfg = Release|x64 - {BC236261-77E8-4567-8D09-45CD02965EB6}.Release|x64.Build.0 = Release|x64 - {01F4CE10-2CFB-41A8-B41F-E54337868A1D}.Debug AVX2|x64.ActiveCfg = Debug|x64 - {01F4CE10-2CFB-41A8-B41F-E54337868A1D}.Debug AVX2|x64.Build.0 = Debug|x64 - {01F4CE10-2CFB-41A8-B41F-E54337868A1D}.Debug|x64.ActiveCfg = Debug|x64 - {01F4CE10-2CFB-41A8-B41F-E54337868A1D}.Debug|x64.Build.0 = Debug|x64 - {01F4CE10-2CFB-41A8-B41F-E54337868A1D}.Devel AVX2|x64.ActiveCfg = Devel|x64 - {01F4CE10-2CFB-41A8-B41F-E54337868A1D}.Devel AVX2|x64.Build.0 = Devel|x64 - {01F4CE10-2CFB-41A8-B41F-E54337868A1D}.Devel|x64.ActiveCfg = Devel|x64 - {01F4CE10-2CFB-41A8-B41F-E54337868A1D}.Devel|x64.Build.0 = Devel|x64 - {01F4CE10-2CFB-41A8-B41F-E54337868A1D}.Release AVX2|x64.ActiveCfg = Release|x64 - {01F4CE10-2CFB-41A8-B41F-E54337868A1D}.Release AVX2|x64.Build.0 = Release|x64 - {01F4CE10-2CFB-41A8-B41F-E54337868A1D}.Release|x64.ActiveCfg = Release|x64 - {01F4CE10-2CFB-41A8-B41F-E54337868A1D}.Release|x64.Build.0 = Release|x64 - {3FCC50C2-81E9-5DB2-B8D8-2129427568B1}.Debug AVX2|x64.ActiveCfg = Debug|x64 - {3FCC50C2-81E9-5DB2-B8D8-2129427568B1}.Debug AVX2|x64.Build.0 = Debug|x64 - {3FCC50C2-81E9-5DB2-B8D8-2129427568B1}.Debug|x64.ActiveCfg = Debug|x64 - {3FCC50C2-81E9-5DB2-B8D8-2129427568B1}.Debug|x64.Build.0 = Debug|x64 - {3FCC50C2-81E9-5DB2-B8D8-2129427568B1}.Devel AVX2|x64.ActiveCfg = Devel|x64 - {3FCC50C2-81E9-5DB2-B8D8-2129427568B1}.Devel AVX2|x64.Build.0 = Devel|x64 - {3FCC50C2-81E9-5DB2-B8D8-2129427568B1}.Devel|x64.ActiveCfg = Devel|x64 - {3FCC50C2-81E9-5DB2-B8D8-2129427568B1}.Devel|x64.Build.0 = Devel|x64 - {3FCC50C2-81E9-5DB2-B8D8-2129427568B1}.Release AVX2|x64.ActiveCfg = Release|x64 - {3FCC50C2-81E9-5DB2-B8D8-2129427568B1}.Release AVX2|x64.Build.0 = Release|x64 - {3FCC50C2-81E9-5DB2-B8D8-2129427568B1}.Release|x64.ActiveCfg = Release|x64 - {3FCC50C2-81E9-5DB2-B8D8-2129427568B1}.Release|x64.Build.0 = Release|x64 - {24C45343-FD20-5C92-81C1-35A2AE841E79}.Debug AVX2|x64.ActiveCfg = Debug|x64 - {24C45343-FD20-5C92-81C1-35A2AE841E79}.Debug AVX2|x64.Build.0 = Debug|x64 - {24C45343-FD20-5C92-81C1-35A2AE841E79}.Debug|x64.ActiveCfg = Debug|x64 - {24C45343-FD20-5C92-81C1-35A2AE841E79}.Debug|x64.Build.0 = Debug|x64 - {24C45343-FD20-5C92-81C1-35A2AE841E79}.Devel AVX2|x64.ActiveCfg = Devel|x64 - {24C45343-FD20-5C92-81C1-35A2AE841E79}.Devel AVX2|x64.Build.0 = Devel|x64 - {24C45343-FD20-5C92-81C1-35A2AE841E79}.Devel|x64.ActiveCfg = Devel|x64 - {24C45343-FD20-5C92-81C1-35A2AE841E79}.Devel|x64.Build.0 = Devel|x64 - {24C45343-FD20-5C92-81C1-35A2AE841E79}.Release AVX2|x64.ActiveCfg = Release|x64 - {24C45343-FD20-5C92-81C1-35A2AE841E79}.Release AVX2|x64.Build.0 = Release|x64 - {24C45343-FD20-5C92-81C1-35A2AE841E79}.Release|x64.ActiveCfg = Release|x64 - {24C45343-FD20-5C92-81C1-35A2AE841E79}.Release|x64.Build.0 = Release|x64 - {6744DAD8-9C70-574A-BFF2-9F8DDDB24A75}.Debug AVX2|x64.ActiveCfg = Debug|x64 - {6744DAD8-9C70-574A-BFF2-9F8DDDB24A75}.Debug AVX2|x64.Build.0 = Debug|x64 - {6744DAD8-9C70-574A-BFF2-9F8DDDB24A75}.Debug|x64.ActiveCfg = Debug|x64 - {6744DAD8-9C70-574A-BFF2-9F8DDDB24A75}.Debug|x64.Build.0 = Debug|x64 - {6744DAD8-9C70-574A-BFF2-9F8DDDB24A75}.Devel AVX2|x64.ActiveCfg = Devel|x64 - {6744DAD8-9C70-574A-BFF2-9F8DDDB24A75}.Devel AVX2|x64.Build.0 = Devel|x64 - {6744DAD8-9C70-574A-BFF2-9F8DDDB24A75}.Devel|x64.ActiveCfg = Devel|x64 - {6744DAD8-9C70-574A-BFF2-9F8DDDB24A75}.Devel|x64.Build.0 = Devel|x64 - {6744DAD8-9C70-574A-BFF2-9F8DDDB24A75}.Release AVX2|x64.ActiveCfg = Release|x64 - {6744DAD8-9C70-574A-BFF2-9F8DDDB24A75}.Release AVX2|x64.Build.0 = Release|x64 - {6744DAD8-9C70-574A-BFF2-9F8DDDB24A75}.Release|x64.ActiveCfg = Release|x64 - {6744DAD8-9C70-574A-BFF2-9F8DDDB24A75}.Release|x64.Build.0 = Release|x64 - {D6973076-9317-4EF2-A0B8-B7A18AC0713E}.Debug AVX2|x64.ActiveCfg = Debug|x64 - {D6973076-9317-4EF2-A0B8-B7A18AC0713E}.Debug AVX2|x64.Build.0 = Debug|x64 - {D6973076-9317-4EF2-A0B8-B7A18AC0713E}.Debug|x64.ActiveCfg = Debug|x64 - {D6973076-9317-4EF2-A0B8-B7A18AC0713E}.Debug|x64.Build.0 = Debug|x64 - {D6973076-9317-4EF2-A0B8-B7A18AC0713E}.Devel AVX2|x64.ActiveCfg = Devel|x64 - {D6973076-9317-4EF2-A0B8-B7A18AC0713E}.Devel AVX2|x64.Build.0 = Devel|x64 - {D6973076-9317-4EF2-A0B8-B7A18AC0713E}.Devel|x64.ActiveCfg = Devel|x64 - {D6973076-9317-4EF2-A0B8-B7A18AC0713E}.Devel|x64.Build.0 = Devel|x64 - {D6973076-9317-4EF2-A0B8-B7A18AC0713E}.Release AVX2|x64.ActiveCfg = Release|x64 - {D6973076-9317-4EF2-A0B8-B7A18AC0713E}.Release AVX2|x64.Build.0 = Release|x64 - {D6973076-9317-4EF2-A0B8-B7A18AC0713E}.Release|x64.ActiveCfg = Release|x64 - {D6973076-9317-4EF2-A0B8-B7A18AC0713E}.Release|x64.Build.0 = Release|x64 - {0FAE817D-9A32-4830-857E-81DA57246E16}.Debug AVX2|x64.ActiveCfg = Debug|x64 - {0FAE817D-9A32-4830-857E-81DA57246E16}.Debug AVX2|x64.Build.0 = Debug|x64 - {0FAE817D-9A32-4830-857E-81DA57246E16}.Debug|x64.ActiveCfg = Debug|x64 - {0FAE817D-9A32-4830-857E-81DA57246E16}.Debug|x64.Build.0 = Debug|x64 - {0FAE817D-9A32-4830-857E-81DA57246E16}.Devel AVX2|x64.ActiveCfg = Devel|x64 - {0FAE817D-9A32-4830-857E-81DA57246E16}.Devel AVX2|x64.Build.0 = Devel|x64 - {0FAE817D-9A32-4830-857E-81DA57246E16}.Devel|x64.ActiveCfg = Devel|x64 - {0FAE817D-9A32-4830-857E-81DA57246E16}.Devel|x64.Build.0 = Devel|x64 - {0FAE817D-9A32-4830-857E-81DA57246E16}.Release AVX2|x64.ActiveCfg = Release|x64 - {0FAE817D-9A32-4830-857E-81DA57246E16}.Release AVX2|x64.Build.0 = Release|x64 - {0FAE817D-9A32-4830-857E-81DA57246E16}.Release|x64.ActiveCfg = Release|x64 - {0FAE817D-9A32-4830-857E-81DA57246E16}.Release|x64.Build.0 = Release|x64 - {12728250-16EC-4DC6-94D7-E21DD88947F8}.Debug AVX2|x64.ActiveCfg = Debug|x64 - {12728250-16EC-4DC6-94D7-E21DD88947F8}.Debug AVX2|x64.Build.0 = Debug|x64 - {12728250-16EC-4DC6-94D7-E21DD88947F8}.Debug|x64.ActiveCfg = Debug|x64 - {12728250-16EC-4DC6-94D7-E21DD88947F8}.Debug|x64.Build.0 = Debug|x64 - {12728250-16EC-4DC6-94D7-E21DD88947F8}.Devel AVX2|x64.ActiveCfg = Devel|x64 - {12728250-16EC-4DC6-94D7-E21DD88947F8}.Devel AVX2|x64.Build.0 = Devel|x64 - {12728250-16EC-4DC6-94D7-E21DD88947F8}.Devel|x64.ActiveCfg = Devel|x64 - {12728250-16EC-4DC6-94D7-E21DD88947F8}.Devel|x64.Build.0 = Devel|x64 - {12728250-16EC-4DC6-94D7-E21DD88947F8}.Release AVX2|x64.ActiveCfg = Release|x64 - {12728250-16EC-4DC6-94D7-E21DD88947F8}.Release AVX2|x64.Build.0 = Release|x64 - {12728250-16EC-4DC6-94D7-E21DD88947F8}.Release|x64.ActiveCfg = Release|x64 - {12728250-16EC-4DC6-94D7-E21DD88947F8}.Release|x64.Build.0 = Release|x64 - {449AD25E-424A-4714-BABC-68706CDCC33B}.Debug AVX2|x64.ActiveCfg = Debug|x64 - {449AD25E-424A-4714-BABC-68706CDCC33B}.Debug AVX2|x64.Build.0 = Debug|x64 - {449AD25E-424A-4714-BABC-68706CDCC33B}.Debug|x64.ActiveCfg = Debug|x64 - {449AD25E-424A-4714-BABC-68706CDCC33B}.Debug|x64.Build.0 = Debug|x64 - {449AD25E-424A-4714-BABC-68706CDCC33B}.Devel AVX2|x64.ActiveCfg = Devel|x64 - {449AD25E-424A-4714-BABC-68706CDCC33B}.Devel AVX2|x64.Build.0 = Devel|x64 - {449AD25E-424A-4714-BABC-68706CDCC33B}.Devel|x64.ActiveCfg = Devel|x64 - {449AD25E-424A-4714-BABC-68706CDCC33B}.Devel|x64.Build.0 = Devel|x64 - {449AD25E-424A-4714-BABC-68706CDCC33B}.Release AVX2|x64.ActiveCfg = Release|x64 - {449AD25E-424A-4714-BABC-68706CDCC33B}.Release AVX2|x64.Build.0 = Release|x64 - {449AD25E-424A-4714-BABC-68706CDCC33B}.Release|x64.ActiveCfg = Release|x64 - {449AD25E-424A-4714-BABC-68706CDCC33B}.Release|x64.Build.0 = Release|x64 - {A0D2B3AD-1F72-4EE3-8B5C-F2C358DA35F0}.Debug AVX2|x64.ActiveCfg = Debug|x64 - {A0D2B3AD-1F72-4EE3-8B5C-F2C358DA35F0}.Debug AVX2|x64.Build.0 = Debug|x64 - {A0D2B3AD-1F72-4EE3-8B5C-F2C358DA35F0}.Debug|x64.ActiveCfg = Debug|x64 - {A0D2B3AD-1F72-4EE3-8B5C-F2C358DA35F0}.Debug|x64.Build.0 = Debug|x64 - {A0D2B3AD-1F72-4EE3-8B5C-F2C358DA35F0}.Devel AVX2|x64.ActiveCfg = Devel|x64 - {A0D2B3AD-1F72-4EE3-8B5C-F2C358DA35F0}.Devel AVX2|x64.Build.0 = Devel|x64 - {A0D2B3AD-1F72-4EE3-8B5C-F2C358DA35F0}.Devel|x64.ActiveCfg = Devel|x64 - {A0D2B3AD-1F72-4EE3-8B5C-F2C358DA35F0}.Devel|x64.Build.0 = Devel|x64 - {A0D2B3AD-1F72-4EE3-8B5C-F2C358DA35F0}.Release AVX2|x64.ActiveCfg = Release|x64 - {A0D2B3AD-1F72-4EE3-8B5C-F2C358DA35F0}.Release AVX2|x64.Build.0 = Release|x64 - {A0D2B3AD-1F72-4EE3-8B5C-F2C358DA35F0}.Release|x64.ActiveCfg = Release|x64 - {A0D2B3AD-1F72-4EE3-8B5C-F2C358DA35F0}.Release|x64.Build.0 = Release|x64 - {ED2F21FD-0A36-4A8F-9B90-E7D92A2ACB63}.Debug AVX2|x64.ActiveCfg = Debug|x64 - {ED2F21FD-0A36-4A8F-9B90-E7D92A2ACB63}.Debug AVX2|x64.Build.0 = Debug|x64 - {ED2F21FD-0A36-4A8F-9B90-E7D92A2ACB63}.Debug|x64.ActiveCfg = Debug|x64 - {ED2F21FD-0A36-4A8F-9B90-E7D92A2ACB63}.Debug|x64.Build.0 = Debug|x64 - {ED2F21FD-0A36-4A8F-9B90-E7D92A2ACB63}.Devel AVX2|x64.ActiveCfg = Devel|x64 - {ED2F21FD-0A36-4A8F-9B90-E7D92A2ACB63}.Devel AVX2|x64.Build.0 = Devel|x64 - {ED2F21FD-0A36-4A8F-9B90-E7D92A2ACB63}.Devel|x64.ActiveCfg = Devel|x64 - {ED2F21FD-0A36-4A8F-9B90-E7D92A2ACB63}.Devel|x64.Build.0 = Devel|x64 - {ED2F21FD-0A36-4A8F-9B90-E7D92A2ACB63}.Release AVX2|x64.ActiveCfg = Release|x64 - {ED2F21FD-0A36-4A8F-9B90-E7D92A2ACB63}.Release AVX2|x64.Build.0 = Release|x64 - {ED2F21FD-0A36-4A8F-9B90-E7D92A2ACB63}.Release|x64.ActiveCfg = Release|x64 - {ED2F21FD-0A36-4A8F-9B90-E7D92A2ACB63}.Release|x64.Build.0 = Release|x64 - {4639972E-424E-4E13-8B07-CA403C481346}.Debug AVX2|x64.ActiveCfg = Debug|x64 - {4639972E-424E-4E13-8B07-CA403C481346}.Debug AVX2|x64.Build.0 = Debug|x64 - {4639972E-424E-4E13-8B07-CA403C481346}.Debug|x64.ActiveCfg = Debug|x64 - {4639972E-424E-4E13-8B07-CA403C481346}.Debug|x64.Build.0 = Debug|x64 - {4639972E-424E-4E13-8B07-CA403C481346}.Devel AVX2|x64.ActiveCfg = Devel|x64 - {4639972E-424E-4E13-8B07-CA403C481346}.Devel AVX2|x64.Build.0 = Devel|x64 - {4639972E-424E-4E13-8B07-CA403C481346}.Devel|x64.ActiveCfg = Devel|x64 - {4639972E-424E-4E13-8B07-CA403C481346}.Devel|x64.Build.0 = Devel|x64 - {4639972E-424E-4E13-8B07-CA403C481346}.Release AVX2|x64.ActiveCfg = Release|x64 - {4639972E-424E-4E13-8B07-CA403C481346}.Release AVX2|x64.Build.0 = Release|x64 - {4639972E-424E-4E13-8B07-CA403C481346}.Release|x64.ActiveCfg = Release|x64 - {4639972E-424E-4E13-8B07-CA403C481346}.Release|x64.Build.0 = Release|x64 - {C0293B32-5ACF-40F0-AA6C-E6DA6F3BF33A}.Debug AVX2|x64.ActiveCfg = Debug|x64 - {C0293B32-5ACF-40F0-AA6C-E6DA6F3BF33A}.Debug AVX2|x64.Build.0 = Debug|x64 - {C0293B32-5ACF-40F0-AA6C-E6DA6F3BF33A}.Debug|x64.ActiveCfg = Debug|x64 - {C0293B32-5ACF-40F0-AA6C-E6DA6F3BF33A}.Debug|x64.Build.0 = Debug|x64 - {C0293B32-5ACF-40F0-AA6C-E6DA6F3BF33A}.Devel AVX2|x64.ActiveCfg = Devel|x64 - {C0293B32-5ACF-40F0-AA6C-E6DA6F3BF33A}.Devel AVX2|x64.Build.0 = Devel|x64 - {C0293B32-5ACF-40F0-AA6C-E6DA6F3BF33A}.Devel|x64.ActiveCfg = Devel|x64 - {C0293B32-5ACF-40F0-AA6C-E6DA6F3BF33A}.Devel|x64.Build.0 = Devel|x64 - {C0293B32-5ACF-40F0-AA6C-E6DA6F3BF33A}.Release AVX2|x64.ActiveCfg = Release|x64 - {C0293B32-5ACF-40F0-AA6C-E6DA6F3BF33A}.Release AVX2|x64.Build.0 = Release|x64 - {C0293B32-5ACF-40F0-AA6C-E6DA6F3BF33A}.Release|x64.ActiveCfg = Release|x64 - {C0293B32-5ACF-40F0-AA6C-E6DA6F3BF33A}.Release|x64.Build.0 = Release|x64 - {DE9653B6-17DD-356A-9EE0-28A731772587}.Debug AVX2|x64.ActiveCfg = Debug|x64 - {DE9653B6-17DD-356A-9EE0-28A731772587}.Debug AVX2|x64.Build.0 = Debug|x64 - {DE9653B6-17DD-356A-9EE0-28A731772587}.Debug|x64.ActiveCfg = Debug|x64 - {DE9653B6-17DD-356A-9EE0-28A731772587}.Debug|x64.Build.0 = Debug|x64 - {DE9653B6-17DD-356A-9EE0-28A731772587}.Devel AVX2|x64.ActiveCfg = Devel|x64 - {DE9653B6-17DD-356A-9EE0-28A731772587}.Devel AVX2|x64.Build.0 = Devel|x64 - {DE9653B6-17DD-356A-9EE0-28A731772587}.Devel|x64.ActiveCfg = Devel|x64 - {DE9653B6-17DD-356A-9EE0-28A731772587}.Devel|x64.Build.0 = Devel|x64 - {DE9653B6-17DD-356A-9EE0-28A731772587}.Release AVX2|x64.ActiveCfg = Release|x64 - {DE9653B6-17DD-356A-9EE0-28A731772587}.Release AVX2|x64.Build.0 = Release|x64 - {DE9653B6-17DD-356A-9EE0-28A731772587}.Release|x64.ActiveCfg = Release|x64 - {DE9653B6-17DD-356A-9EE0-28A731772587}.Release|x64.Build.0 = Release|x64 - {BF74C473-DC04-44B3-92E8-4145F4E77342}.Debug AVX2|x64.ActiveCfg = Debug|x64 - {BF74C473-DC04-44B3-92E8-4145F4E77342}.Debug AVX2|x64.Build.0 = Debug|x64 - {BF74C473-DC04-44B3-92E8-4145F4E77342}.Debug|x64.ActiveCfg = Debug|x64 - {BF74C473-DC04-44B3-92E8-4145F4E77342}.Debug|x64.Build.0 = Debug|x64 - {BF74C473-DC04-44B3-92E8-4145F4E77342}.Devel AVX2|x64.ActiveCfg = Devel|x64 - {BF74C473-DC04-44B3-92E8-4145F4E77342}.Devel AVX2|x64.Build.0 = Devel|x64 - {BF74C473-DC04-44B3-92E8-4145F4E77342}.Devel|x64.ActiveCfg = Devel|x64 - {BF74C473-DC04-44B3-92E8-4145F4E77342}.Devel|x64.Build.0 = Devel|x64 - {BF74C473-DC04-44B3-92E8-4145F4E77342}.Release AVX2|x64.ActiveCfg = Release|x64 - {BF74C473-DC04-44B3-92E8-4145F4E77342}.Release AVX2|x64.Build.0 = Release|x64 - {BF74C473-DC04-44B3-92E8-4145F4E77342}.Release|x64.ActiveCfg = Release|x64 - {BF74C473-DC04-44B3-92E8-4145F4E77342}.Release|x64.Build.0 = Release|x64 - {1EC8B3C0-8FB3-46DE-A2E0-A9121203F266}.Debug AVX2|x64.ActiveCfg = Debug|x64 - {1EC8B3C0-8FB3-46DE-A2E0-A9121203F266}.Debug AVX2|x64.Build.0 = Debug|x64 - {1EC8B3C0-8FB3-46DE-A2E0-A9121203F266}.Debug|x64.ActiveCfg = Debug|x64 - {1EC8B3C0-8FB3-46DE-A2E0-A9121203F266}.Debug|x64.Build.0 = Debug|x64 - {1EC8B3C0-8FB3-46DE-A2E0-A9121203F266}.Devel AVX2|x64.ActiveCfg = Devel|x64 - {1EC8B3C0-8FB3-46DE-A2E0-A9121203F266}.Devel AVX2|x64.Build.0 = Devel|x64 - {1EC8B3C0-8FB3-46DE-A2E0-A9121203F266}.Devel|x64.ActiveCfg = Devel|x64 - {1EC8B3C0-8FB3-46DE-A2E0-A9121203F266}.Devel|x64.Build.0 = Devel|x64 - {1EC8B3C0-8FB3-46DE-A2E0-A9121203F266}.Release AVX2|x64.ActiveCfg = Release|x64 - {1EC8B3C0-8FB3-46DE-A2E0-A9121203F266}.Release AVX2|x64.Build.0 = Release|x64 - {1EC8B3C0-8FB3-46DE-A2E0-A9121203F266}.Release|x64.ActiveCfg = Release|x64 - {1EC8B3C0-8FB3-46DE-A2E0-A9121203F266}.Release|x64.Build.0 = Release|x64 - {88FB34EC-845E-4F21-A552-F1573B9ED167}.Debug AVX2|x64.ActiveCfg = Debug|x64 - {88FB34EC-845E-4F21-A552-F1573B9ED167}.Debug AVX2|x64.Build.0 = Debug|x64 - {88FB34EC-845E-4F21-A552-F1573B9ED167}.Debug|x64.ActiveCfg = Debug|x64 - {88FB34EC-845E-4F21-A552-F1573B9ED167}.Debug|x64.Build.0 = Debug|x64 - {88FB34EC-845E-4F21-A552-F1573B9ED167}.Devel AVX2|x64.ActiveCfg = Devel|x64 - {88FB34EC-845E-4F21-A552-F1573B9ED167}.Devel AVX2|x64.Build.0 = Devel|x64 - {88FB34EC-845E-4F21-A552-F1573B9ED167}.Devel|x64.ActiveCfg = Devel|x64 - {88FB34EC-845E-4F21-A552-F1573B9ED167}.Devel|x64.Build.0 = Devel|x64 - {88FB34EC-845E-4F21-A552-F1573B9ED167}.Release AVX2|x64.ActiveCfg = Release|x64 - {88FB34EC-845E-4F21-A552-F1573B9ED167}.Release AVX2|x64.Build.0 = Release|x64 - {88FB34EC-845E-4F21-A552-F1573B9ED167}.Release|x64.ActiveCfg = Release|x64 - {88FB34EC-845E-4F21-A552-F1573B9ED167}.Release|x64.Build.0 = Release|x64 - {EF6834A9-11F3-4331-BC34-21B325ABB180}.Debug AVX2|x64.ActiveCfg = Debug|x64 - {EF6834A9-11F3-4331-BC34-21B325ABB180}.Debug AVX2|x64.Build.0 = Debug|x64 - {EF6834A9-11F3-4331-BC34-21B325ABB180}.Debug|x64.ActiveCfg = Debug|x64 - {EF6834A9-11F3-4331-BC34-21B325ABB180}.Debug|x64.Build.0 = Debug|x64 - {EF6834A9-11F3-4331-BC34-21B325ABB180}.Devel AVX2|x64.ActiveCfg = Devel|x64 - {EF6834A9-11F3-4331-BC34-21B325ABB180}.Devel AVX2|x64.Build.0 = Devel|x64 - {EF6834A9-11F3-4331-BC34-21B325ABB180}.Devel|x64.ActiveCfg = Devel|x64 - {EF6834A9-11F3-4331-BC34-21B325ABB180}.Devel|x64.Build.0 = Devel|x64 - {EF6834A9-11F3-4331-BC34-21B325ABB180}.Release AVX2|x64.ActiveCfg = Release|x64 - {EF6834A9-11F3-4331-BC34-21B325ABB180}.Release AVX2|x64.Build.0 = Release|x64 - {EF6834A9-11F3-4331-BC34-21B325ABB180}.Release|x64.ActiveCfg = Release|x64 - {EF6834A9-11F3-4331-BC34-21B325ABB180}.Release|x64.Build.0 = Release|x64 - {20B2E9FE-F020-42A0-B324-956F5B06EA68}.Debug AVX2|x64.ActiveCfg = Debug|x64 - {20B2E9FE-F020-42A0-B324-956F5B06EA68}.Debug AVX2|x64.Build.0 = Debug|x64 - {20B2E9FE-F020-42A0-B324-956F5B06EA68}.Debug|x64.ActiveCfg = Debug|x64 - {20B2E9FE-F020-42A0-B324-956F5B06EA68}.Debug|x64.Build.0 = Debug|x64 - {20B2E9FE-F020-42A0-B324-956F5B06EA68}.Devel AVX2|x64.ActiveCfg = Devel|x64 - {20B2E9FE-F020-42A0-B324-956F5B06EA68}.Devel AVX2|x64.Build.0 = Devel|x64 - {20B2E9FE-F020-42A0-B324-956F5B06EA68}.Devel|x64.ActiveCfg = Devel|x64 - {20B2E9FE-F020-42A0-B324-956F5B06EA68}.Devel|x64.Build.0 = Devel|x64 - {20B2E9FE-F020-42A0-B324-956F5B06EA68}.Release AVX2|x64.ActiveCfg = Release|x64 - {20B2E9FE-F020-42A0-B324-956F5B06EA68}.Release AVX2|x64.Build.0 = Release|x64 - {20B2E9FE-F020-42A0-B324-956F5B06EA68}.Release|x64.ActiveCfg = Release|x64 - {20B2E9FE-F020-42A0-B324-956F5B06EA68}.Release|x64.Build.0 = Release|x64 - {52244028-937A-44E9-A76B-2BEA18FD239A}.Debug AVX2|x64.ActiveCfg = Debug|x64 - {52244028-937A-44E9-A76B-2BEA18FD239A}.Debug AVX2|x64.Build.0 = Debug|x64 - {52244028-937A-44E9-A76B-2BEA18FD239A}.Debug|x64.ActiveCfg = Debug|x64 - {52244028-937A-44E9-A76B-2BEA18FD239A}.Debug|x64.Build.0 = Debug|x64 - {52244028-937A-44E9-A76B-2BEA18FD239A}.Devel AVX2|x64.ActiveCfg = Devel|x64 - {52244028-937A-44E9-A76B-2BEA18FD239A}.Devel AVX2|x64.Build.0 = Devel|x64 - {52244028-937A-44E9-A76B-2BEA18FD239A}.Devel|x64.ActiveCfg = Devel|x64 - {52244028-937A-44E9-A76B-2BEA18FD239A}.Devel|x64.Build.0 = Devel|x64 - {52244028-937A-44E9-A76B-2BEA18FD239A}.Release AVX2|x64.ActiveCfg = Release|x64 - {52244028-937A-44E9-A76B-2BEA18FD239A}.Release AVX2|x64.Build.0 = Release|x64 - {52244028-937A-44E9-A76B-2BEA18FD239A}.Release|x64.ActiveCfg = Release|x64 - {52244028-937A-44E9-A76B-2BEA18FD239A}.Release|x64.Build.0 = Release|x64 - {D45CEC7A-3171-40DD-975D-E1544CF16139}.Debug AVX2|x64.ActiveCfg = Debug|x64 - {D45CEC7A-3171-40DD-975D-E1544CF16139}.Debug AVX2|x64.Build.0 = Debug|x64 - {D45CEC7A-3171-40DD-975D-E1544CF16139}.Debug|x64.ActiveCfg = Debug|x64 - {D45CEC7A-3171-40DD-975D-E1544CF16139}.Debug|x64.Build.0 = Debug|x64 - {D45CEC7A-3171-40DD-975D-E1544CF16139}.Devel AVX2|x64.ActiveCfg = Devel|x64 - {D45CEC7A-3171-40DD-975D-E1544CF16139}.Devel AVX2|x64.Build.0 = Devel|x64 - {D45CEC7A-3171-40DD-975D-E1544CF16139}.Devel|x64.ActiveCfg = Devel|x64 - {D45CEC7A-3171-40DD-975D-E1544CF16139}.Devel|x64.Build.0 = Devel|x64 - {D45CEC7A-3171-40DD-975D-E1544CF16139}.Release AVX2|x64.ActiveCfg = Release|x64 - {D45CEC7A-3171-40DD-975D-E1544CF16139}.Release AVX2|x64.Build.0 = Release|x64 - {D45CEC7A-3171-40DD-975D-E1544CF16139}.Release|x64.ActiveCfg = Release|x64 - {D45CEC7A-3171-40DD-975D-E1544CF16139}.Release|x64.Build.0 = Release|x64 - {A4323327-3F2B-4271-83D9-7F9A3C66B6B2}.Debug AVX2|x64.ActiveCfg = Debug|x64 - {A4323327-3F2B-4271-83D9-7F9A3C66B6B2}.Debug AVX2|x64.Build.0 = Debug|x64 - {A4323327-3F2B-4271-83D9-7F9A3C66B6B2}.Debug|x64.ActiveCfg = Debug|x64 - {A4323327-3F2B-4271-83D9-7F9A3C66B6B2}.Debug|x64.Build.0 = Debug|x64 - {A4323327-3F2B-4271-83D9-7F9A3C66B6B2}.Devel AVX2|x64.ActiveCfg = Devel|x64 - {A4323327-3F2B-4271-83D9-7F9A3C66B6B2}.Devel AVX2|x64.Build.0 = Devel|x64 - {A4323327-3F2B-4271-83D9-7F9A3C66B6B2}.Devel|x64.ActiveCfg = Devel|x64 - {A4323327-3F2B-4271-83D9-7F9A3C66B6B2}.Devel|x64.Build.0 = Devel|x64 - {A4323327-3F2B-4271-83D9-7F9A3C66B6B2}.Release AVX2|x64.ActiveCfg = Release|x64 - {A4323327-3F2B-4271-83D9-7F9A3C66B6B2}.Release AVX2|x64.Build.0 = Release|x64 - {A4323327-3F2B-4271-83D9-7F9A3C66B6B2}.Release|x64.ActiveCfg = Release|x64 - {A4323327-3F2B-4271-83D9-7F9A3C66B6B2}.Release|x64.Build.0 = Release|x64 - EndGlobalSection - GlobalSection(SolutionProperties) = preSolution - HideSolutionNode = FALSE - EndGlobalSection - GlobalSection(NestedProjects) = preSolution - {E9B51944-7E6D-4BCD-83F2-7BBD5A46182D} = {78EBE642-7A4D-4EA7-86BE-5639C6646C38} - {2F6C0388-20CB-4242-9F6C-A6EBB6A83F47} = {78EBE642-7A4D-4EA7-86BE-5639C6646C38} - {677B7D11-D5E1-40B3-88B1-9A4DF83D2213} = {2D6F0A62-A247-4CCF-947F-FCD54BE16103} - {BC236261-77E8-4567-8D09-45CD02965EB6} = {78EBE642-7A4D-4EA7-86BE-5639C6646C38} - {01F4CE10-2CFB-41A8-B41F-E54337868A1D} = {78EBE642-7A4D-4EA7-86BE-5639C6646C38} - {3FCC50C2-81E9-5DB2-B8D8-2129427568B1} = {78EBE642-7A4D-4EA7-86BE-5639C6646C38} - {24C45343-FD20-5C92-81C1-35A2AE841E79} = {78EBE642-7A4D-4EA7-86BE-5639C6646C38} - {6744DAD8-9C70-574A-BFF2-9F8DDDB24A75} = {78EBE642-7A4D-4EA7-86BE-5639C6646C38} - {D6973076-9317-4EF2-A0B8-B7A18AC0713E} = {78EBE642-7A4D-4EA7-86BE-5639C6646C38} - {0FAE817D-9A32-4830-857E-81DA57246E16} = {78EBE642-7A4D-4EA7-86BE-5639C6646C38} - {12728250-16EC-4DC6-94D7-E21DD88947F8} = {78EBE642-7A4D-4EA7-86BE-5639C6646C38} - {449AD25E-424A-4714-BABC-68706CDCC33B} = {78EBE642-7A4D-4EA7-86BE-5639C6646C38} - {A0D2B3AD-1F72-4EE3-8B5C-F2C358DA35F0} = {78EBE642-7A4D-4EA7-86BE-5639C6646C38} - {ED2F21FD-0A36-4A8F-9B90-E7D92A2ACB63} = {78EBE642-7A4D-4EA7-86BE-5639C6646C38} - {C0293B32-5ACF-40F0-AA6C-E6DA6F3BF33A} = {78EBE642-7A4D-4EA7-86BE-5639C6646C38} - {DE9653B6-17DD-356A-9EE0-28A731772587} = {78EBE642-7A4D-4EA7-86BE-5639C6646C38} - {BF74C473-DC04-44B3-92E8-4145F4E77342} = {78EBE642-7A4D-4EA7-86BE-5639C6646C38} - {1EC8B3C0-8FB3-46DE-A2E0-A9121203F266} = {78EBE642-7A4D-4EA7-86BE-5639C6646C38} - {88FB34EC-845E-4F21-A552-F1573B9ED167} = {78EBE642-7A4D-4EA7-86BE-5639C6646C38} - {EF6834A9-11F3-4331-BC34-21B325ABB180} = {78EBE642-7A4D-4EA7-86BE-5639C6646C38} - {20B2E9FE-F020-42A0-B324-956F5B06EA68} = {78EBE642-7A4D-4EA7-86BE-5639C6646C38} - {52244028-937A-44E9-A76B-2BEA18FD239A} = {78EBE642-7A4D-4EA7-86BE-5639C6646C38} - {D45CEC7A-3171-40DD-975D-E1544CF16139} = {78EBE642-7A4D-4EA7-86BE-5639C6646C38} - {A4323327-3F2B-4271-83D9-7F9A3C66B6B2} = {78EBE642-7A4D-4EA7-86BE-5639C6646C38} - EndGlobalSection - GlobalSection(ExtensibilityGlobals) = postSolution - SolutionGuid = {0BC474EA-3628-45D3-9DBC-E22D0B7E0F77} - EndGlobalSection -EndGlobal diff --git a/bin/PCSX2_keys.ini.default b/bin/PCSX2_keys.ini.default deleted file mode 100644 index 70b275813d..0000000000 --- a/bin/PCSX2_keys.ini.default +++ /dev/null @@ -1,94 +0,0 @@ -# IMPORTANT: PCSX2 only looks for PCSX2_keys.ini at the inis folder. -# You should first copy PCSX2_keys.ini.default to the inis folder, then -# rename it to PCSX2_keys.ini, and then edit it. - -# Important: PAD bindings will override shortcuts at this file. - -# IMPORTANT: GS has fixed shortcuts. Make sure your shortcuts don't conflict. -# Also, they will affect GS also when alt/ctrl/shift are pressed, so it's -# best to completely avoid them at this file. GS uses: -# F5 - Deinterlace modes -# PAGE_UP - FXAA antialiasing (HW and SW) -# PAGE_DOWN - Cycles through dither modes (HW) -# DELETE - Software Antialiasing (AA1) -# HOME - FX shader -# INSERT - Hardware mipmapping - -# ----------------------------------------------------------------------------- -# The KB shortcuts can be a normal key (e.g. f or ] etc) or a combination of -# ALT/SHIFT/CTRL and a key, separated with a - or + (e.g. m or alt+ctrl+m or -# alt-F12). Number (key) pad names start with KP_ (e.g. KP_0 to KP_9, KP_ADD). - -# Note: If the Dev console source is enabled, keypresses (including with Alt -# etc) will be printed to the console and could be copied to PCSX2_keys.ini . - -# List of special key names (beyond KP_0 to KP_9 and normal keys): -# DEL DELETE BACK INS INSERT ENTER RETURN PGUP PGDN LEFT RIGHT UP DOWN HOME END -# SPACE TAB ESC ESCAPE CANCEL CLEAR MENU PAUSE CAPITAL SELECT PRINT EXECUTE -# SNAPSHOT HELP ADD SEPARATOR SUBTRACT DECIMAL DIVIDE NUM_LOCK SCROLL_LOCK -# PAGEUP PAGEDOWN KP_SPACE KP_TAB KP_ENTER KP_HOME KP_LEFT KP_UP KP_RIGHT -# KP_DOWN KP_PRIOR KP_PAGEUP KP_NEXT KP_PAGEDOWN KP_END KP_BEGIN KP_INSERT -# KP_DELETE KP_EQUAL KP_MULTIPLY KP_ADD KP_SEPARATOR KP_SUBTRACT KP_DECIMAL -# KP_DIVIDE WINDOWS_LEFT WINDOWS_RIGHT WINDOWS_MENU COMMAND - -# save state: freeze is save state, defrost is load state. -States_FreezeCurrentSlot = F1 -States_DefrostCurrentSlot = F3 -States_DefrostCurrentSlotBackup = Shift-F3 -States_CycleSlotForward = F2 -States_CycleSlotBackward = Shift-F2 - -FullscreenToggle = Alt-ENTER - -# Note: toggles suspend, but can resume only if the GS window is not set to hide -# when paused (Emu options -> GS window), and when the GS window is focused. -Sys_SuspendResume = ESC - -# Screenshot. Note: must not include shift or ctrl. With these held - it will -# create debug GS dump. The two other shortcuts will be added automatically. -Sys_TakeSnapshot = F8 - -# Hardware/software rendering toggle -Sys_RenderswitchToggle = F9 - -Sys_LoggingToggle = F10 -Sys_RecordingToggle = F12 - -GSwindow_CycleAspectRatio = F6 - -# Vertical stretch/squash -GSwindow_ZoomInY = Alt-Ctrl-KP_ADD -GSwindow_ZoomOutY = Alt-Ctrl-KP_SUBTRACT -GSwindow_ZoomResetY = Alt-Ctrl-KP_MULTIPLY - -# Recording Bindings -# Note - These are disabled if 'System > Enable Recording Tools' is disabled -FrameAdvance = SPACE -TogglePause = Shift-P -InputRecordingModeToggle = Shift-R - -# Note - This is disabled if no input recording is active -GoToFirstFrame = Shift-L - -# Save State Management -# Note - These are disabled if 'System > Enable Recording Tools' is disabled -States_SaveSlot0 = Shift-KP_0 -States_SaveSlot1 = Shift-KP_1 -States_SaveSlot2 = Shift-KP_2 -States_SaveSlot3 = Shift-KP_3 -States_SaveSlot4 = Shift-KP_4 -States_SaveSlot5 = Shift-KP_5 -States_SaveSlot6 = Shift-KP_6 -States_SaveSlot7 = Shift-KP_7 -States_SaveSlot8 = Shift-KP_8 -States_SaveSlot9 = Shift-KP_9 -States_LoadSlot0 = KP_0 -States_LoadSlot1 = KP_1 -States_LoadSlot2 = KP_2 -States_LoadSlot3 = KP_3 -States_LoadSlot4 = KP_4 -States_LoadSlot5 = KP_5 -States_LoadSlot6 = KP_6 -States_LoadSlot7 = KP_7 -States_LoadSlot8 = KP_8 -States_LoadSlot9 = KP_9 diff --git a/bin/resources/locale/ar_SA/pcsx2_Iconized.mo b/bin/resources/locale/ar_SA/pcsx2_Iconized.mo deleted file mode 100644 index c6a551680f21da9616d933b4f2f2c6a66aa24a6d..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 27476 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 diff --git a/bin/resources/locale/ar_SA/pcsx2_Main.mo b/bin/resources/locale/ar_SA/pcsx2_Main.mo deleted file mode 100644 index b3eef0fa812f549706db064ad0a31eff38b631e4..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 66552 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 z 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 L_~ZY=_OpKhaZ*)X diff --git a/bin/resources/locale/ca_ES/pcsx2_Main.mo b/bin/resources/locale/ca_ES/pcsx2_Main.mo deleted file mode 100644 index 1bb73cc95708eea18eba9cb16a219ed966c2b0be..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 15752 zcmbW73zQ^RdEc)<4_I5smIBz4L3bBvX4l%D*;%aw%pzH{^V%^xyOZgem0(%PHQhDc zwcB0Qs>eJ8jzJ_5BnIMO42A@+42e)Ih!MynAeqMy6PX~+Ny16ai4AdxLpUTHli(0y z66g27_jXUOq=4f-bLU@Ged|8H_xIiY>eZLN%kX)U_A|8R<;Glji7}7-xSksG?|;IW z1@Mo+=Yu!>q%m|cw}6*{uL7?DkAYW!rvu&%Ud{bFsQ%mG`5QsK_m+Tf2Y-zFUj?rO zFNEhG0X5#o!RLaHgUWgyPl4w^jq_$u@4pos2j30;74Qq-A@C1Cjdvx)UOaCf|}>`;rW}uS?=EgLVEKQDEfabeE&^Q`}z}5>-qsW5AI=6XTfFg zr@`L_#me6YwXS~y>b-A*+UK8w>L+6A{#uZs%#Gka@RgwU@j8${)8L=zv<ApMqoH_rU|;eg+pV zgD(f)0A2-t9F+Wg9$W$c1t`Akg@{_`O`zyK8Sdu;o(3f!G58$t9H{rYpy<8{o&fI$ z>)@9`mSSGU=2pR@;LE^Y0cXI+K)v_BK=Jdk>pcD>sQtbK6d%UJ^V>l2VF481+ThQF zuLrg6-vhP3uYlU$AA^$H{|aio{{s|VzYl63dk_LWzY4qqyc*Pc>!A9b2eqy@fExcT zpy>Q=P~*NY+MzYl?uhmQyR6ev1=4tyQ>Wl-xn_!6&oM?j6U03HOFL4E&A0UrQG zpLc*7=e^21Vt~ZJ^|2Ibai1{|#^&ybqMV{4OZIeje0& ze-Q9H;rZn+^Xq?Uz=NRZI0+&uW)ZypImRTQ^yRHAQhfVrz;A$}*Pno*$De`X*XW3+ z+YwOfo(47louKI50WSy3fUgH7clU$R?{|Xg_b{mOKMFn^q-KN9yRZ!#J4QkzMpvKF0!%>l z{~hph@Ef4^^KDS{`yMFz`~bw1nyXP3t^Xhh$;~9F^{s(g&jTQ$XC4mUe;L&K-vf_; zSB`nU9s@<+UjQ+^W*s~Oz6q2Z{2?ei@kgNcaqT#|1}=fOgKq~#zb}DW?^nUsf!_c{ z#~C&yKAZ$4_Yo+0tH7(k2SB~|ZVs;pz4saE<%NL8da- zAI2Afr@$Wr9|JY-XTdAMe+_Eh{{V_V-vzG*ue{m&5gYL5z`J>VHz>J!0{jW^UxK33 z*Fo|9o1oVF9T3xKz8mhZfw9OgObb(jS-`@mkeIEdifxiXn{cnR>*Z%}H|0M{k_Wy!_ zdqL6Z2&i=(3(uE9$wdrmpKk;u55ECQPX8@<68swYT5#;;{`{~VN@ycnl;9e4l~pJzew^EjyWybrt) zd=SL7nJ<7p1%4wumy*1Q`w>v%-3Ds>Q=kP~;3c}JeTcS6gSF;!H1Y8@v;(xa(DeEH zv~gOGc0cVO(EciIg0@I|BTb(+ZI1TyG|~HaX=i9!i$1?a`xNb;)BZMXg{F_zIOVRq z-2XP$AEw?x6i6+TW!80&Rx2hxQOn{Qd+@e3Q()k0xII zW7=P*Jxq&=39i#Rw3PODXv3d_+(^##d6M?e zXgBDF&z1+X9q?~~8BKEidfLy>`kxjTe~cy#b1KT^3w3%(X)h>0aR+5?#kK@M{PStIleK*Q1HzUhcYG+T(xt?{m zc>V8BUvFodmR%HChsD&li!$z-k=Mk9d6o71pR(&oC$^2O>NIVdl_Fuo5w|&Jrdq8$ zE{X)2Om#M+?ZUQ`W^B7zRnV)-3s&HXIaTynkX??-B%3r-RhiY|b9&MpB7og&v%wDJ zNz#@hwWCtu`geJ?VMpfS?s~Gt3N?s(7 z{ewJF-e}vUsBBLfvAOC29f?Ad3l=|@^F|Jf>{RnyRg`qg*!=X&=^49!WIZa%1Ljzs zZ5A=PONZestFT2}mP}WO+S4;5r)Nx%6hO4o*2E3FmZTAEPr9sm%-WT9?8)4XwjF`9 zJg<7C&2pQhjjY>~6f-1JV)u(52dt#rKPZy>{MDp2)9tKh$L(w5JTudssN2)#7qccE z(2w)BWFo(8yH=GY6e=@TEn%?{lD57c=W$xv^{ms3b0D`%)AhR!+ny7$f;IeQ#(}Ts zjvvYCVkgVa`_b!%t&`Y1>uPlNnx(9u7>ICTYxc&1Z=`Bf9y{71v1PU0W39?|SVNr_d4 zBGN@%F*8XKt)U@KVme7Tkpv8$ZaO{1=!i>pEz2Qm|I4O5)AZAvH8WYoAhm|4Zq{mb z;^OHuo0&Xnc~hZv!JBocW|!)RYrQNl3p22SZtt@rTS;kVXYKTM1Ir{?K7+Ni%P|tw zjnih_Jgcuu){K<2Q3FW5$gUIHtMXok8tCDeU6H7+I|+|FJYA2H4$PXJ9iLlTML{;4 z;`o7kNM~+O_0aRQP?v>MOJ-K8Zf3i^a-08EM^+%UBlVyzL(lq;Jkw`WN7|9;HrAZc z!tL>_DD4~*72aU=pWlh`37kT;8*Pk4_)1iWcwqO}JK1KOALxIvaB^kZ))65)12Y?1 zSO2R!qolMaG8F0=UrGO+E$H{f`czTJnHdxr>*PtDUqouZdJJLQ6kS=|R+&fszrD#W zAtZ5*ZLTM+)XdG@WjEMQh8QGUkYmmbyM_U2ym2@Hfa@Cd0PND3PJ*-z&J?C+J!~Itx$ziq*2@2$T+xI*fx8NWv zok=!^d(T&CGwM1jARcoflAM=~8V$U}LCaXhz`P@NaWVERA}Pi?PqUse9^*J$RDij~ z%Sum*HpW;&+r#^(WdiB(wuJ~G3%9s3Uq?+l+XrpL!_p?jZcjD{*|u$gAZYL$A3``_ zoc`K+-=OTOGYac&3Mm~bxH)vI?q&`HVdm@n1sh`Iq&&mSe3ms4FnCT75v5IgJZZH( zYszFT=_KX$QJj}T06EQJ{$k7QnTxdb|8uZ*loA=Vo<5Fr`R1E&=?L6eb6g%fm?`If zds;}-9Ixa~!@}hV3W68U+k-xKiaK2>Vwy1&lGfjjEi__`D`}Ks@I^aVXr&6HW)`QW z?Pi{oNSB+`Gij|V5V3`{(W%g6n>3LG!Zi$iFV7@H;<N+*Wdsl4;+uDsZVt z-$IHqd4CWt6=U(ZjPqWOuIJ_iPGqfOe5UI5ib=D$a(dP7 zcgC@{(#CgSJ#9B><-&;RosodbKnJj>L^DvVbft6g3^=K^hx^$Wu$l?OX>x66}4j?b}(E z2lIueyAR@{V@O5IDO4eOUROvoW=^%?cwFqYrz!%J_25Vj8k}o z@W@(}`L* z=i(r7DJyX7#JN~$7saidIOa*n*wIFoG;P^gLn5$ntZqIdtWDLKkZ{s0kNEXFRd62C zEXV77*=Ch+&L=I-YcI>_`H;;8>LXJwn$)!qh9`{=?upm>fz!~VGRj*q>H=Y1d^LJw zG1`A21a&U&5%v487pC5>$`SI3)YKE_o@0A@VQ#|CEzF&g95ro(2X2l9CnVE3u;U7? zp`I+EU0j--?+cb56v2g!&n=#I*4cFyJ3PsBOMhpj#-WDh(g22> zuQ#_aD(mGYs>F^7j&ezqTSbI>T|7+)OoC%p@Gp&iEZQV84WY4*-*GgG^CP zHeothOD9>&iT8iR%bPeWz3u#-@f+{&XJxw=o7FVlQW}NavqCAKBwc}EQNpDzPn}2; zY{M%TAvxEVXKPSw8#TdLVlsF&m-$zYK~Kh2pwdbSQJZ$5e#+-1esC;54$`cmp7rgt z(atm9O+MB=4Iykl_t7(qloP&w_O-^k+TAEWk2_d#u`N~wQe`emL^W$p5Z1Oa^TEi^ z9T_uN+^HrGGlVZL{#nVB+EgR>A>wJs)ySiw?Rt}&^t^HFzr#MU8JNMydoJR?su$;s ztR0gcDOiR)*M=$UOhyDh0*_^aqsEX!kkX>QbA$J zaJ!MqR9x^(8nf5h*U0Aj5Y|NiZ6)TCEs_8i-8AEkguOO%3{Ot{r0|djT(K|33`Ig} zxQzz!;zb57%t<#0$~3$rG4ICM2A777DiB*Qw@HlRCOW5JR{}3bUq;1w4}xxgL??#qZ9456(f zmKbfKrZMx{EbC61GqswW^)n`0hO_dot39nM;TF)ASP)%3(9DO(b z?!t2C3&n5~yt(6kF}NEt*+meY-ADFJ2cO&6K8vN#l;hMEnzhsL3J*0I^4*ECo5v3A zS&n6rYl}rIY1WPrtJYSsN!u<J=NjzBIN=STCqWChh zuWGa-tYm}4xt%B7J1~!3GC56qJ1_fSsJ4P3nY4rZNhp5LY<>I=15tduG|llB#BNxA2)+ENxbld|^e zczcrsxj2DKoV52G?QNYndQTIWDUaS$>UOAe-AVPdpJ~!o;l;HglXhQ7(erV*vXNj`;tn>y>*|&H_^j#®9ZtdzFz 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 U?tMXv=JtdREtM)*pKs0oKl^JqmjD0& diff --git a/bin/resources/locale/cs_CZ/pcsx2_Main.mo b/bin/resources/locale/cs_CZ/pcsx2_Main.mo deleted file mode 100644 index af92a4383eae31afe3df1f0dffb21e0849d01f6b..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 53398 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 diff --git a/bin/resources/locale/da_DK/pcsx2_Iconized.mo b/bin/resources/locale/da_DK/pcsx2_Iconized.mo deleted file mode 100644 index 4e77ac5de0af55bdd16d239bdffeb4f21d84abda..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 27339 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 II1Tmx0Q;sRB>(^b diff --git a/bin/resources/locale/da_DK/pcsx2_Main.mo b/bin/resources/locale/da_DK/pcsx2_Main.mo deleted file mode 100644 index 5c2ae91fb6c648c8ac16b14540320e46e4b4f45c..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 58594 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 diff --git a/bin/resources/locale/de_DE/pcsx2_Iconized.mo b/bin/resources/locale/de_DE/pcsx2_Iconized.mo deleted file mode 100644 index bc307a8cd8d1706dabd018417ec3c731ca9b2732..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 29883 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 a@cRPS#KMM7ZtG=s%mGmk=HlRKF}QxiXLa`g diff --git a/bin/resources/locale/es_ES/pcsx2_Iconized.mo b/bin/resources/locale/es_ES/pcsx2_Iconized.mo deleted file mode 100644 index 5375b8df1ea239e3d2f155e26a1bbb842f166db2..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 32612 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 CCuRx& diff --git a/bin/resources/locale/fi_FI/pcsx2_Iconized.mo b/bin/resources/locale/fi_FI/pcsx2_Iconized.mo deleted file mode 100644 index 50f8e111df1f7d0b498687ee2059644785bf4bcd..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 20901 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�cV~8UXN<+)xe(3g~1V z2@IP{jRO&a=(&s){t(I&L9yFobnz%l@t@%&+;VyMKoEkH5)H)6=$t{LY!=r6GFXI* z$=;~t9ZP)+37r!hUPPt}9wm-cuUs_jE&|r{xj?%lH1e_zh?eysNt15W*y!drR5Rbi zCL`-s+CnMbX??Vq%OwU|O!^NNS{8WTK?v9uNtLfH-x5v%vgZ%{n{CtAxVCY( zqk047Km}OHX~#tD`0}8#LY||6lS)> z1AL5&0uvxVG*6Zhs!p*$&C=QuShbE%f1gqZscMX1T`U1UEr^r6kT!5jOZS|@xxHl@ zt^KN9vRv!(j`_bjy)#a}>?}}PL1zm@xqylBSS@7Mww}yS=a?16zAm`*Fdfm@@k12S z#e=1UQXw_pyl7Vz8)627^9vzG!jLDZ1N|ha1ZZj!>HYIICB;y(SYqUFYxKO>Z9=Ss z84HT0fMjYs2X&y`h-~aN0l``VOEuvld8s(FdtjFWo=%kNl?o&D`9e_Bx(Zn168%gl zWj2fA0*56>3z&z*X310aM?8qKv}JJ00u2Uw9!#yQEQ6`e4d~{#Y!jgHA|AxhYdDy0 z8n|MHsL`JQo@W<-Usr4l@vWnS{+54$hgi{i zVGr6!SV1r_bzlvW5YV|YCUj0!j47(9G*{8hQedSZemI>m9zv}lmCy%(Jb7qn-EyI+ zYo-Joa?a4cRTB9Bn$HcwtRyd}vQQk2XvA)Aa9I;up&N@*0=C+?J0w*+mkxsXUpQmg zaH<<+J5(Zl7bY0P3w54gO4mb^WyBJKzoON~o?kNPwjH_uEz@GjW4RVvNT)GI* z>9rih+%1R3$1j9(#8HzEXXRWct0nkz%D^`>M(&w5M24M zB?_|%igb1LF32J>V4R!dDgk$m(lA{LMMzY+DBbkSsMDZez}*VO2o;Jde>v9(VZzRt z)ER0q<#5qJ0qWwtf}#7|@&UY%aA-4QaME+Eg!-Hi&rwtS!Dv_!K50C>#27*GBhCV2^^lsxy4g$J1fH>8ef&r+pmwK)p^gzL00@N{NeWqEt@-dE+kY z;Ou&FVN_*?a@Ci(G^%L?Z1p|$)q0Wz%?GM7G6LNbC686@UAL4V)+Ws77?_1?5FTo_ zPa#tUuw5upVZue!oRgLotwWSu9ew@r!(D|oE5QKzY%vRWmaK3yT)ad9(a%zdn!U?K zZ|^=TJ;NPRGL_wdktm+OwXw3eNxJpu;e+R2-rIZr!y0hWciKu;Qr|o*#k9<>0|x6oiXDt&!4&f2Yy4-F#rGn diff --git a/bin/resources/locale/fi_FI/pcsx2_Main.mo b/bin/resources/locale/fi_FI/pcsx2_Main.mo deleted file mode 100644 index f385f74f624e2a85ce9f7514f5f5693685ce8c02..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 48698 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

{MsuQqsRI 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 diff --git a/bin/resources/locale/fr_FR/pcsx2_Iconized.mo b/bin/resources/locale/fr_FR/pcsx2_Iconized.mo deleted file mode 100644 index 9879bae3d2b042dbebb63ce9fbea23db3b944f14..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 33565 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 diff --git a/bin/resources/locale/hr_HR/pcsx2_Main.mo b/bin/resources/locale/hr_HR/pcsx2_Main.mo deleted file mode 100644 index 2fbcec06eaf9feb259a773bbfed2614270909dfb..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 47926 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

;cZO{+YfLMS7p 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 diff --git a/bin/resources/locale/id_ID/pcsx2_Main.mo b/bin/resources/locale/id_ID/pcsx2_Main.mo deleted file mode 100644 index 20be4e3b5e347a906cafeb57d3cc3657ce5874f6..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 45988 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 NXyPY6FUe-d{|_!`q%;5k diff --git a/bin/resources/locale/it_IT/pcsx2_Iconized.mo b/bin/resources/locale/it_IT/pcsx2_Iconized.mo deleted file mode 100644 index 59a332e89711475d9c622f204c64d62dbdc4b7c5..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 31888 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 GV)+*ir&ad= diff --git a/bin/resources/locale/ja_JP/pcsx2_Iconized.mo b/bin/resources/locale/ja_JP/pcsx2_Iconized.mo deleted file mode 100644 index 6cced63a36bc5fe02b6a3ea1cd56bcc4d171d737..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 35465 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 diff --git a/bin/resources/locale/lt_LT/pcsx2_Iconized.mo b/bin/resources/locale/lt_LT/pcsx2_Iconized.mo deleted file mode 100644 index e70bc73d1a5d7cf8ab5af4c48be30129e8f1e661..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 31758 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
(=&N95y-NU~N&3wi;};h^hCRX1_^h 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 diff --git a/bin/resources/locale/ms_MY/pcsx2_Iconized.mo b/bin/resources/locale/ms_MY/pcsx2_Iconized.mo deleted file mode 100644 index 0cae630ca325bdcf755bc5f1d53002b341ee0845..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 19530 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 aHU_0AH$?pTCp0T%_u{%f1L%Z_ss9J^(+;2j diff --git a/bin/resources/locale/ms_MY/pcsx2_Main.mo b/bin/resources/locale/ms_MY/pcsx2_Main.mo deleted file mode 100644 index 7c6e7cfc6942fd305ef9aa1d31a7eefde1c39509..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 45107 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 diff --git a/bin/resources/locale/nb_NO/pcsx2_Main.mo b/bin/resources/locale/nb_NO/pcsx2_Main.mo deleted file mode 100644 index 8389f7ea63712a63d91ff21ae62ef3262c235842..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 59641 zcmb@v2Y@7Hng3ssSu#jYwZP8I!uD)Jgh5zlcXl=hc4n6>^i226G}F^Pc6IOWU_unL zXU2dT!8^}@hXD*Hy6TzFfS56hXFS81?$H0|`#f(|SI;b*-tXT~^R4%-x88cg^FHYf z`@qrry*%Pq-zSPr0bh1Z6s_4eiVp2oY!vmJ8%4{(h2T2y8gM@N7H}T;DeyS(KJX~; zXW&)fL*NGRipNCJ$>8mv;y(he0Z%$o((G9Yrq4+e*_N#?*#SUJt6*HQ04n1cr^G8Q2G4=d^Gsq;BnxQ=X-wL zpx$2&9t!q>`tF(#-URBqR|Xsd_5B8@^4|a|{WAhSH{grF{fNH>JQBPW)OX$n>N|IV z%J*JS={_66Uj$X&Z-dJBXQ00KYmn}W{sgMt4}r?(fD1hS81Pua$Ad~=0M%}f3GwSd z_2adm%5#0dCxR-+^Fe*@l_C5(P~UkgsPfzes{9`b@t*|soiBna|JOm~^8l#t{xrn@ z1=Rcd_IdtCffo}#9#s472=Uj2@K^{>hwx5N{dFU#^1K4nci#YB0{$Z?dj1b^6L`Rd zQM3^p234Oof_m>Q;920Epy>Q7pvrOZ16~RK z0$dNSy(o$Xz?XtO;Mc(8!AFt#3E+v~W^fs}1biB(a{UV^db}G{`+P2hzX={r_&-6l z_g_J^(*Y}@Xfb#ssP`@b`@!v?@_hw(7la`$|yt ze`kn)4@lQV9|YB2e*jm5N3Mz@{*QL>=MwPMpvv=2Q2G8GRK0!=j)4z>qKgupAv&yr zYS-Q1Rp2v0wd?o6Q^8+@=YdD?u{Gczcn$au;Qrvx!2%Q zpxW;iQ02b^6#ah~)cE`|I0AkbyaHUs$4&xY3o8BHpxXP3pz`}c2tNcKNcf1gUZ3N^ za|oXaD*r7YLn|5u?*m^8s$ClE{JT#Ay9hrAJOI2C)OX$os-Hgvs$TbjD#u^JbHNkV zdpS0NYOirn^uH6F2R;W}1ilPZIX(g^-B&=R`(eO8fGY1ih)j4MsPrS?>EKTA4Dc3E z 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 H7oYzhj7^g< diff --git a/bin/resources/locale/nl_NL/pcsx2_Main.mo b/bin/resources/locale/nl_NL/pcsx2_Main.mo deleted file mode 100644 index 73640c43008f19285f3b69d6c1474e7b93290280..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 59449 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 bhMx(jM7(t5UVbG&k8&)-|Cx^cRKWiMu9<@M diff --git a/bin/resources/locale/pl_PL/pcsx2_Main.mo b/bin/resources/locale/pl_PL/pcsx2_Main.mo deleted file mode 100644 index 191cdb0afac32770feb0f3be8d7352e173cbd722..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 61855 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 D;A}H< diff --git a/bin/resources/locale/pt_BR/pcsx2_Iconized.mo b/bin/resources/locale/pt_BR/pcsx2_Iconized.mo deleted file mode 100644 index 8a58f1630d6515c50c4d33cc7fd73eb82b3deae5..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 32436 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 v2^(GIk%0-%o2WTaKRzN{)|$SoX5YcFECeie12h diff --git a/bin/resources/locale/pt_BR/pcsx2_Main.mo b/bin/resources/locale/pt_BR/pcsx2_Main.mo deleted file mode 100644 index b55d491b214e9e9a5eeca2aedd91b3c242318b33..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 62913 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

}1Zm)g@~Y!U~P&$8Um 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

9W0D+ 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

i{GejuF4Xn@i)c?|@_Zug2{R*oE~uCU+2Po5 z!JTBNfgxsafuZ1`VL3~$dmH(k$%Pwc5EYb#tsBj!Uvig#u`~xX3vCrSkVi8x5Ki{b z6XvHxwCP_m5+E9x1cf#tGzPUUj;He1pzs31&3gc@KT=UCRH|V(0e}?BMvz3w z&a)J10kxh4#1L2I8wogbrzVa{L7n9$7h}D_(izDc)!?Yon1)#VC8}PQe6I_m^E;Fm9ChjvckXYGc4l@svtrWhnqLrOtcMUY&s-jW zIN}o;Fmq>r#1H6=foM{T&dnDP*f6G~3GXC@6bWWnphuwJ6D43XYqLD2H1=x5gj@aci46-jdjSCs;Vjd?b>$ZxI9_ zcYYGi;6>~()E1O56z;)Ql%wDq;(1Mk*`=JTX+@?gid&@e??2$7zqVoqpvp~3k*Qme zj0YDmA`=mh31~?E$AExc0HRLRYo28OcUwx+DFAQ=0mKAJp6ke_Sn(EiIinfF#SI(4 z@23e;q;Yp60>Cv9;y#dYfwg#n{S4p^|0!DlX;H-NU>+UzQX-{YfD6#7RkBprwt zNr&dB!W-Zi3l}h)LW{i+obgn2!ikAs%3GOV!EU}V!MAlTDAAe=ZwGe9Er$RHS0YV9 zvw~e$%w1riSYD?WWk%T@TNuaLfR#<2Y^pOBJ>1(R{bcm68#43CW8@)22xbsO?>ksk z!#KnNFMy9wykejEZZr@t^w#jKoWl@!^2M?U?dj# zcaTS?u~Yb24IL%5Xh0GG_#KBi5f@d28&M3nv6FY{gQwllL!W^xvvJdHcIp^Kz+aDi zor)TaFhi?O7MK7NdFCjyb_5X^hf!RECP4ED?;QY7%ncKb8j-oqJ48ZmCTtkM7zI9z zV;N%kEMg-XpRmP+QZW*Su7mMNAiPN9m%_1+3-y~VVT-7DIhXNa$nAL2EpI z<9~>#az>*(6j#h3FGU`-Np9?tbS#iCQCV@&;$j(KrGv^capMqamSPZmw4mAtmG|dJ z05R|=k+53SVizd^^s!`y@)s9dfjVZsY71yE60=EXxP=wmfk_P56_(2V$mdvbg_qZt zIDZFm;5k9qunckiiI%Y1s%%oOk)F7gql!1YMYP{^7u6_rj*%dCjP&!CNS;(@qr%@u zJWIxJvn}w0AKFuMVj)Yk zQm%R7!35vvK{W1gpYp>Z!;3Pt;EpkmJOX;2`eK%9!5!A;FNNdm2|Xans5^g#Y({IXr>pm%K<$KN+pWfJt4-JviB_R$ELL!V z*eedRBKei>%u2aVh4;Q|MM{ro1NW#+W}cf%5oB>Tim~z1vRlu@DQSs4Mou!Hg2P?3 zVk>vB6&UR8=IyL%s4}hIuq^k~A8N+#@*!L0y&6Yg$d_wKG`Q=bErZDJeuU53w3Wn1j^}d zZtrA-qJb^Rf3CgX#DXHiBIK@+5=VdxV=;n}c;>=k4OxMZ4JRluVUA-D{0Zh{rPqBw zbzvGYB`J7HNl9>745cy_0eOryIL#x1#z=QBZCx3_9iDpO_t=smW3?g1b98Ckb(eKr ze@zPCoYIj{GQ;hpejp!~T}*#AkQC)HW;u~+fGIaeHWSC3xJYp-V?Aaug}33y%dp^X z#2KG>%QHV3VgA_1lSW)$4R|2jUboTjxIf#l*)RwZ`|BqB>qLs^5KeC0^gm>n8?xAT z!|oygQ&Upl9Wo%-B}q~tV*oILi<8(H!LU^3u35lP;F}soqR$+XRtyP%8c^K9aAzwc zgAK$4tvtg){KDv)UB4mzd>T+cnr~nBd1J zz=Lus965heQfDW!h}r-Ji3KdYZ4&j#B!6ad&};E$CXHwDhpOcnLeV#Q8ErTN{l#4v o!5^UfJxSG?vo11*XR^EyKYRl-NTJ_fRVa|cyH~|-{@CUJ0Xr1Kp8x;= diff --git a/bin/resources/locale/th_TH/pcsx2_Main.mo b/bin/resources/locale/th_TH/pcsx2_Main.mo deleted file mode 100644 index 2fb5e125fe05ed7278b1c8fc6dfff86619bbbf28..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 72034 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 U)wNKBHDs+N8%#t)_v6L?0K<0S>;M1& diff --git a/bin/resources/locale/tr_TR/pcsx2_Iconized.mo b/bin/resources/locale/tr_TR/pcsx2_Iconized.mo deleted file mode 100644 index da809da26e3accd6b10740e841ddcb9a12d05067..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 33237 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 diff --git a/bin/resources/locale/tr_TR/pcsx2_Main.mo b/bin/resources/locale/tr_TR/pcsx2_Main.mo deleted file mode 100644 index 48846a935ad5589444858da7023246f2e8eb66c9..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 61862 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 Itjh|^KXInf7ytkO diff --git a/bin/resources/locale/zh_CN/pcsx2_Iconized.mo b/bin/resources/locale/zh_CN/pcsx2_Iconized.mo deleted file mode 100644 index 0a486e734f1b91ee96fc32dac7496925767a8289..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 20981 zcmd6vYj9oHb>B~2=LI`XVy9K&*v^qsMF2$rB&FC26i22gl4d1Hv zK9Kt$1x?!lNPz@Mf&e9w;QRd&DLg1rB8UgkX{Vih>2#)TCX?3Fd=SpL_qNmcLuQ&x zJDv8o);j0h3zD`~)gQ{y#Rl#!j`KYkKg$pP z4g8$rj4%!|o@D&o&pXaK#;c6~hVef#o?^WA3y$-z88`l-A&)@wx$#aai!tt-d@u{cGZUx4dxjxUBWo&rbaZbx~j4a_i zf{`{bZf3m9xSz3uaqZU~=L5z)jB@?=zk$q*|CDhn<6kho#kl4f)Bg=d@t6P3*vEK* z&B^>H1;5d!Q|L2TpzW>Z0Vl&1Z_&edH_Q7qP-{teGd|W;ad@#B5Sw8abZ}Y)b zov-m};e*>azrhFhb1;qb89ra;BmaKU2L1UsKfl5Uw{yOr-xyBe{?4!SA&eYCGyeB; z{6@&BEq{#>cTm3;AD4f>X@mY;%}>HU{`U*~wvNv)h2MNG93N$TgwL#Fj z6K*l*cIC3gRJP=~I}<5>>2bSrdAHc>@yUCMq}-DC3Vpe3vZdjP4T)@*m)7SJ#iEz# zE9w;bZQVoM+2Q3gscfQXi~MmZRdly**!JCZZYq=Uk|{2v`&$~E#!b0&(qqB?KF`f% z)BR2Et6p;|oAlmvGhQZ_?{~Wr`K0dB%NAX4hsRgNR7Upax%pByD|;^Fx{Es#dC%?Y zO=Nq#LW|qcn<}_{dG5~^3wm$;blv$}CVn_qD!RFDx6qg9@>KdlVh2-1du#7XHo5J# zmkqKPChjh!)2rE3DV=n)xuTm$CfnS{3(op<+U-l1dQ#bfn<*8FZZA{0I}+(s(k*yt zudA5KaY=RY*#MRm-dPtzZQxKwu5Ak7gmb~vZ9Yd!8IeQ;nKN+DRM(D+nwquLmjE4-LZKaQ)iR8ozJ@5iYeVxH^0f&#HVb61iMCGsZ2s#kQmA5 z(r(h*kqUi;J@Ej0f4-luyA!1}p-_9eRLnK^vS1|pI?T^3XcK!77dXO>RIwj1LVKlI zMXX`IiaTSjbRyq_3uKdSXDPkC-;Fo!?u@M|yWY5MgNv|ziJA~zrj$+;b9q@HOU%gT z&{VxC(OSY?`*pYd`4!&X(pOmLcBj(bPkZ#Kb)WjtuRr~%AH}J&Nr}Q|e!>(R!ypoo zv8}`w;%B$J?H%Ya1hztdwo78UD^y!oK2hjJ|7fx&pXlpNbtTfSm(S<&V1dnuH8E@k z|Lnp|aHj%3+SRKL2qF}^@*ZYWwYcA7PhCW8p_szMi@Ba2 zJYHZWoFthl^rZ=l1mKbl5zowF_1%sSU*+_S;gzu!X z>X>++Mz|+P6yDmc;ENN#p-3irN~b^_TqBj~%c<9RL{t0#!sPI#o%s|9gS%%eNLfCL z0kE#*6`pHoXcUjU4{r%=*aCvTygX)y6-FYyIZ%dYu+k?u{*w(h!tOV3;hMsS&Oq7 zBzZUiO?_sjm-n!MdIo zceZTVA{X9By+z37`#;g_FQj_F=`;r1kxIMU5WYZz%wGWE7?-;FMJ z!ZyOlLqI&Y{=Qc|!EYR9BeRNkaP#_9#%T|RgTy6C)Gy~0EAU|j#!Y!bOjRVh=Ap=USGyghe5b@}(%pv7 zw&#>)2+kIO8I}{Kg}xFyfC$;J>%kCIXqIS$ILeNR=Dfv++@AHikU(-cRtOm=!2#`B z?e>BW$RZ#Bz{X;Oe0e~w2G%e`Pf)YeDaogp66F|xOSCJ)(|8T7m-m2Y-5wL0F2YTv zL2D*TbTa+wC^4boT$<(N<|x~sGI$1FZd?!0VwrqW87|sL^{{(P)0F~Zx<#LEVSB2t zFI1>NHx7n^T3pRr`aS9w9U3kbL~t*Pwm8qlpr%-%rqg}2=WuWRPJyR8l?~aMY&t@I za|3eaE5@hWjW#yI!-|QfHW<0*ZY$xwufJBO6%embM|#nQmk+gp+PhNSl4@-xVhL)` zvs4p#FKK@KaK{j1OLlOr4#3OA84@50HBOw@SG$nDkTw=lGor6@pm=mL$2 zBzzP!qTb8po7_COT|{|1A-%nkx9d6)WMbe%r?D7Tv5l%}>j73Od5BpNn%9lO!m2iz z)8JE_jCWuw!qb=pnFP2?_)l1;ia9K>1$bGo#ap5J4YP5hn@U0x(z#rp z&YMgMpK;f``OMl)8`ebj+b+08J4ZdS7kuSj?QLm9CCLQvXqf{V(kl&<&RkL$nAoqU zl+bWAH#P5y<}FBK4q=@-Pf8QfwWMB1KHhHF2fV%pVAg#T*~D*hhn5|N64pZt^FbEU9Ina+Mc)eS zmMmaH!l82cC(s9tw*tXNZd9xgStNJ``%?KrvBi0{gjHXEZLI_gNT8@17Tk?t?p)if zjlLFlvzJ8)%6N@qmHOnd4O?Fm!bkJxDZw$CfKo`I+pwz#UP!g1%FM*fWVOVEB z9aXW07>X9UD7b2rO*%JQ!n#;M9X-$(2#LEr4nZsG-NHEC0+>R_hzvn56k4LYX0J)f zC9*l)HK?;qxS}`_G@xK~{>KnX+pyFC#>sl95pesHB+5)R@c;Ytmn)*9(WYY+A;XhP9^Xlt)^62k&U=E(@)0_UX`M>ZDHKH`8z zU~g*@uGdADqRmD|RLco*9 z>zYOKHM8b+7OYDq&3>f~Vwg=5(#;*xY_|?T=m;xPGc%i;vl;1m$@JFmRXmk?RdJ3! z#~bTkeR<2vn;(7Dh2%=ce&t0;?n&?*Y+SYerI%L4IYgsdrecGjW|!Bj?NN0+?6F$J z6>Ui_j#RQD2vj+2czvjkgs^%z25CZ7Lx)PJLEFj|qjq|jo3)2R6>EAlt|mT3CCjvy zHE~Zkv|tja2HMenHY8FqiCq;?*IHE^#p-!+_(E$jmrHkI6!j6VX~8XWZCcy9=_$8S zCT*3*InbiEeE1%8ZN=Mku1A=SDUNd%4tnULbpceFJ^>9a(8CE$X%!g%!LrjQY=#OzTHql8HGNn}Kk77;1M z(L}~9hHz1#od99+Em+AAj?b;8OC81wZoIm!7i6(w9KYxZr(^Ox-Eulycc5 z)us=e&=R;f8S;rs(kJDJOZ+hGA+#2RJAh|dM$Z*MmS!rRVTb9;dZ7_ zbX{M)SD5h@XJ6({RkTxEw3zlDjd}(l5Y#f5Wh!ycl8Pnz#BZ2Z8^a-qSgoAg93tvR@(hlV&EcJN z!}M%P&gMloh?dGyMzhsKUXH~j&q{AuE=ECK*BO$y)y!MyEftmAisKMYFp|)S58)y$ zaYq3^sCx-O@JBsQcz|RDkxBER5nXj_50G5c4$5P(20#+0X`rfWxKpJAg~ZN8=|v2 zayz-+G@?&%cMNvgX&2G&6FGc6!j)uS?-HhG)Abq%uK!{bQwkZSFQr zY*xtSvXBa` zs38%x%M+B;)REfBlUi5ZjS@TZZfVBY8MCRP#dRVpaeyE&7&B*y6(deETU~?3mBZ2& zcRd>d_!ClG>rVCUjL2!EZU8BwsHx-00tfQu4y7Jsn>Q9>@~m6YdQcUvf_?fHlTy#0 zL5B%o49}tr@|C@7wzF3TwkC?bQG%057obCn5giP(t94}}Gbz_;QF#T0Y<^N87^cS) zbOM25YiB8+A?l)jS`-K7j@l3vQ^mLAiqdSP!xNq38|FmMSvNbfQxEz#3J3Ok<_7OV|Y9%2dqlI!d?U2ke|w~ z;$oOCaweoIqVkAc#f!H%{~*U{!Z+QuYu2<;abgivM1f1$AJznHO1`sqb{HVh(&>@H zkK<4|^=fHYFK244&r9pqX3|-=WLr6mYGC5^(R&-sa-o^tkG9>6$#kY6*v(RgSx+wq z$~Uez!mGurXmGpN$I+=#WJ40GSbC%@AsZrP8d+&-X>i?_U+LHw0*_T5$$UadS_)gy zN2;wEl@}{vKZ88<_yrlBSn~wM^NU`8XAb%Z|LA!2CHG19<=5J`u79C<>r1a~YjZ=X zR^NBAeap-7RWxz`!IMinN8?W7^#!Ij_YE)QwqxYCdQU_E|ZS&l82;e63?s^E0*k^faFy`9@x|6Ux=*KGT^h zHhh;?N_eNS*o=+fkIBMIY_iSmdvoKneBPw>mPBQ>+qP-F0yUF0+FV;Cp5%ENlG6EN z0?{9jS)UYAg*9{`cU{Z6mZ$3`gdKPF3cNMr>(OK3rnc2Cu5E*&WHPyIgY%fbv{*iJ z`LQ;)Jb$%3KUtm}@$U^)mQI$h?f2gu_2&+MJg}?bF@Nl6@X;`L`7@_0(?{%2yGyT^ z=ZAyI3o^@%{r>2!>cD`%cg}o9t~-s(^F!6+|XQ-PoO26s5&qb?0=6X zE7N!U(IfuedDF2!bgMcsSsfZJFHTiYUkFYwiBw4Mv&isA!R%}>Jm#OeR+*LuP(AuH z9r)u1O_9OH0d!dtUKPoXf}?l+g|X`3l0P&WUkygj`lrW4tg&gf$VQiE4~A;6wT2e3 zgWHJMZrz)h?#BcB{PB_H`J)S#{&--2lk60^mS-n}$xGGqtR`EQhE4hUgg=*LNGkOJUhVm;hU8D_GEmIx&0@5 z{waN(^3y+W?^J&3=fwzn_lUphi$H&E1$4f0yMfoHPVNJi-+P6i$su zm=fben=KRUzJ)ZvgP48pq+O~k9Se?$1ah}|UwGllp7P0~{?zW}`S<1BM-jd*m@UZ7 zQSeNZ#UAX*8ZtUyN98tfR6aR{HDV`H67L3+7bV&++%U;idEx#3*mZwsurf7^28kAb zc*a%&48@*O<&*9p`SSc(nZt0TJaRXf+PAzkC95dL@;kH1JaZ@Q9yBG2 z(`Yf1@_fVc;vSLu?pcG4&;b-GWVL~-U`cgis(Rs=+}#kI8UPEikJ>^ z&o3_?d2l}}>75VCXYsDP;NSA>e!EEk`pB@p&=^e4moK03hX?#&{O*{4@rJy77hJgF z-<_{sKg8dlg8$9|_bK__zjGrvHSJ(`nRM|c>8^fWoAJm@`P^VI_)&H8@bbdB%Jglw z(e&eP*zEda`^?ErG<}gVI5Q}TZvIg9W z2WK^w%5!(i<9mWTcb4zoZ2HMtZeRH>qd$2iICmaBmv=2x_Z~uG)WGYEKT+(5y~pS_ zk}VO9^k04Np1V$^t$(qRy^bC87ca1iKYXIeUP1H^?k_LAXU=4?8HQ~1%3|fiw}ZLy z^3-|c1ARmHGM;er44z}OK+Lc2G}?r?3xq(Ubg+8k7!L69z#ywsPoG27A1$1$4xI33 zXT??K4#puA2AOV&98x`fhI@FNoiEIlcMtpXbCszn+n|8czzA7O(^zopTJZK5^ykVh zRN&8EB?8S~0W^QP3IgEhaIj zB>bQ@!N`7gW~{(h2v&#R=MPUnL{MD)p6d5%?8YGE{QHn0K&vt(49Y|kGDFrlKUKMN z3+2dOVuV*EhOj{1RT3d^YdNAWHG~VIWD)U1aAYRD*@%E9c{2$#hY96=VTCZhxeM%{ zGAn$ouwD$Kc*{$t%SVpGW6-Q%&VjwKID5EqV7Gtrwu*AtUui2%^$?Wl2(m9TbDYQI zRl)j|MC(g?$v&ah=MGnMyM>% zVAvl$hbP%pM1k1&V>6(pzwf+JJ_1x!W0gGeVtCuXF-@M)<@6mIe`Gp1^?q=g^d_v= z6wcECy0Wj+XM`VXJ^{zU+{)sjEUJ%@` z;w?Xa>I`y<7r3n zAyytbX4h~*s->`MF{1;-q9|?;QAOhO(%Fzr>GD_!zhKw#<^<=gmSJDP2j?ZPPF^C) z%Tp5=i(Bto*pzJ!7E?5n#wIoV16-8?;XD*Dvf_b}>N`^kt0>Cgkhw^Lu;|*3qWZ%> zFyxQluoA>78*cW8?kq39E8er`7RD7qKQ|uZyg1h6F<&iikAzmKCt$T`kVvQ(9!x`F z0C-%Tu`7F2+@g-q5jYPo(BwW!7!}!`M5+GZn15NqC$vheD=JoFk;{ux=b^C!L&5&{ zV|mDJ!SHoh1iu(v3~>ub6_{wRmUEY9 z=kb&3JHwJn7e__cdxnB@v;L_W|MWoo3Y`eWYj#?9f8gJ{UOp&HAOCT`EickZ%i!ca z3en)h{dQHXCkm39$sfDoUpgw5w;Buu5ADYB{NZ=_a(Rw$qi66o1Oa@N=)1e$AAyvP zN`y@hF3(-XcgiPcg-_4FE5$enrF#&o>MFDv;&rX(Ad`$A6ju_=Cmf-;vXE?p7R1DD z;>{K%KgVB|NA}7Rr_b6wSZZ=6n0T+rwOU^Ec63IZjb(9^^8WXE)Jh}LNEoE@T3px= ziQXe$E4B-Bylj{h$WBnj*Z~9zMcJpJk0h`bbB~zZAo7FECKO<0Z4s3ogZ{;t>REauQd?j$l4wXk zs8H3NP@G+rm>e3m29|L*(O}$Dc|7S77Qt1tvJa!h!6%@tWoS(KME6kv8V8jG%GfDv zZjW1u*%r?t7Nof~c`?i+cf&51@uc`RaUCNkvV}X-{=K(bsRaB*rX3YbJ0%=alsbMT zLf(@kl;<3?T;-!%0KGh`WWrbiW&&zZBl?LcQ2hh28& zr$*rxJS#7+xyGb)pOcr2=Mq6uBrcBn1og-$tFPoU)|r8n+jS;Qoe&%H$RQ&_3QxGn{8r!W5BknBwOWB`aa{z<#u`Wto7E zDaw{s$lREOh^oXq@h1q*8rm}_qMSHm;|5_S!6@P!b-4g_ z8TO0X>%Ny%pe(|iwbj2v2D=lS8x#A6FPqtHt=3X(a_#=m;y-rrVB)gBG$D?>c!VOT zGQBT2I!SZPvas!iIch+qQcA}F{M8-Ri)Jy8fQri}gNdsM6lE=iu7wlUxyDm}>ee)O z#u|}bOr5D7AF`YpoLiv(z?Q2Q56bTfc@k~foe{`ev94YSR}8bTJ)ttV`YxF{yg&W6 z7=@1F>C4tu_s{GqpS*27GLc7d)bN~sCfqt9;#;*GDp;>k2iFm-D0v<{~#TLsA!J%r+uRbj3=efkzq>%byaYE zsRnWpXyt`>g}+5*TV?4CM-*Y{6SeqdO{;n}8zCQcK|Q)zhXIQUIC7%!;T@6c%C6

zelf(Ldj7=u@teG2;;&TQgZCwBL@$Dbh51>O0$K4EGhp4M*5t$vf#Bex19jYTDd@BO zL%RnOVm~(iqEJsl{ENSHG;H@dj#fkG_0g?cx2{?f;G6-#u3fuoIQUP17;FB0+;@}2 z?g@Md^N5VUW!&Fa+O0p+*L6Y=l&E5enO>IZ z8~W@3)wU1&W*t~;Qf-ro=JD z0RF+N(7Q1Eh~UM2VIz%cS8e} z@ldz(gbIKtw_Zp9DUv9(vQ3(0EWL=f!V~CPKYOaJo997 z{Sqg1qkhpRdVc^-)5d98kO%<}vi#$>d31@rdS$P0(yhD93;6O(9_qDP6{?j*m|Qm{ z0NFJPdsD>dod&NR^x)_;RWOY>J_`KAaR7*HfyfA?QSxP3KY*^^B6Iz(p3c~x696Ew zg-`QU>_eRU-$epb9vfdN&8vT+sNellsO)e-KUe_XaLdi!Shkck&L4vPBq`2C`|W1rv8gPvo_RX)9I{Q66`^w#~IotY7;hSWj$ z3i-i{UFkQbr#;wzzy{0WUG5KcKlzHbsu(Z&ehQyk`Ad|b6*$dy&H#Yg?YG}P%Vhbl z@kuw~lir6!qGio|HSSM-rT`4-{WzH;1jdQq;+fR_vGLbm*()jfJGy__DxNry!xO~q z4HO;Nw@=*m`VAYSi00r`KD69$cFuvldz;W|2Eyfw!oh(esjqlJY8C-;U%g&`?)8lM zIRyY}NVwfT=l5EUQ$iYiiL6{6jnE)>FMfk3&E}<}DxH+#6(@UiUgkjS5?Av3eIJr; zU_S>ZjX&UB(GPvxo+NlO(>ECr6d>6Dns^=Vy7e0p3tpfP1YxXTamj-{uQp(*;!!Ds zK$g@)0nfbRdCCuUUH{6RCnyB@rUL*?V&Gssgf{LGyWQ51IDd--vsskk+8^~34%#NqV4?3oRW`f(on`2!++tN8sPjaFLU51nli5WVaBm#^{(-Az7l zl~1Jl=*M0j8ZLdgG(7xve1@Ixde^%Soyu6gDFHz3y6di+wO#j59N!;lHk;qY5&3f@ zd<)4-$x)!89!;k+f^+Ew4`P156A_G!zvbLd6F%XjPh!f~>y7#)=LLG|`TK+3@X>Dz zbSHgcoqnpTd=kWecBDM=XH!!fcd#J%(}x~?M1aeley zT|VMfE(49f{t2Jpgt|Ye7N{j-{$cC)|Cg=S?GQZ4{mU%>Xh#rVlI~kKIr;73kr7Is z{p;!J>BrB;*uE(wK-_lk+^H>%jQkoJ0qaM~M-t^H)_f}*f4dVR1 zgt>!P}VUbF&;xLk0*-wgrNF%d|L)IQdR z`8!bLr$c{VW{Ru`@gD;quF~a3FK$c%fW&|NI=7Daj{%T~4|LxDGl2NZ-U5>F078|( zI{+EH#5-Kjj~|}P=5l|xacW}@?|BB#{t6nyN6zMWzA02dvR!%Qm93eXnQ!3k7g~+R z$lToA^SJ-}B9}mtg5-RE>MxSHu76zdo8h=6}c-^d@I6b`ALkPLK8%FM?lEp4x__-@7`iwkk z4KnnGip9sqC&$w=lXchkKYzjoyvw1W3bv|jRLbNSC?d&>YIZjXQa+Y^DRzCU2>62mYKOiWCqbGh6W z9N-2y3_mo%=yc;?07LeG4X{NtF zJUl#$Ho>i)=iG$SpoD~z5CDuSs&>4Agzk^MgP_M7=)TyYXF@3i0FU14MIN1l|NRID z_-mzdxmL*MuT(WHBXPb9zwpHIg@so?bec&VXR^H|0nq=9k390oM#lL^&?UGG0r8Ry z@T!?kmtO!Il3^6NzktNgBGEHQMgaM<0E345h+5xg=eTpYx(B z0aBA9iZsI%7JU{3Cm`gI=qY4o1F39!K|s>NMj}(ytrnis#B0=0hkIzuF^V}*(kF5` zxgCJVZSHvoB~^V2ul34W==YOp|6~A=Y!5&DFmZSZi8hKNc??bRbto0+4NE0d#zPP; zy>WP0%h2P#Pw7Qu$5Tlk$}CZ$GOP*$hidW6(+Vd9#4Mh7K=u9Eg=TZ^uDkAPt@X2> zsQr@!z-n#h&Ykkm(9p2KSizHJ6J$c6v_002ovPDHLkV1kKo`V9a8 diff --git a/pcsx2/gui/Resources/ConfigIcon_MemoryCard.png b/pcsx2/gui/Resources/ConfigIcon_MemoryCard.png deleted file mode 100644 index fc54c88a68965f9d9b6c6077ab905377e69a769d..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 17859 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 diff --git a/pcsx2/gui/Resources/ConfigIcon_Paths.png b/pcsx2/gui/Resources/ConfigIcon_Paths.png deleted file mode 100644 index 424e0e3c8e9b066cb1f099ab76a693fe9d4620f9..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 21947 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 R>!bhx002ovPDHLkV1i}_E#&|J diff --git a/pcsx2/gui/Resources/ConfigIcon_Video.png b/pcsx2/gui/Resources/ConfigIcon_Video.png deleted file mode 100644 index ca5763057e4bacdaa827481a2577ee58923bc249..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 35849 zcmXtfby!s2_w}8iyCfur?vO@m08v3oX+b)qOS*bgb=v(2quv}XYLLMRA6{wNgKm` z%!Aa<|2k4Pim1cfgg_GS%=VD7V<|Jo1 z{G`)5-gT{gnk+xmXE^>z~f;r>IK3^&@0i4->g^K!+W{(sL$IKV+L`eqgy=ihaI zR&&6xe?yz$CiT%;4M28WEctur>(3fFH2qG!zYjV+J1cx$^{JsD&hM$X(ooaga& z-%8T6(5t7!A1Yv#)X}cPVQP zl;ZZke-9`^dXCon`%xjYPBREl(s8(|#S zz*UcTi$b-&Xa8^clTm5ekH`55O0JbJ-r8>wzpx++2(&b_e=p6~Z*5?$Blb5z`d?;G z+Ig`W`XLB0<%yf%Il+)#SivmCXUFp3(Z5>y428kB5G0+Pk%RWVtM9>5hlTIaS|95U zsSE|1sMxzG6cUMKP2~*vZ_(m`a{J-^mgOK;{kf0ogs>bFzioD&GI|N`f5wf?%@#}T zz80Nk-QdQirqxA{vA%FTil-QaqB zkRmQ&=~Zhvc{W{J@1*CS_$XoqI&Xu#KH<;6VG+|^^lS7(rQgoN5OJ3!HF3dz_p2)J zavc@xoXWV29REmd?Iu8;SXvCl(_ntXAp9GsO&^MRTcUYaP%^l>9BTGEkaH1E@nFkgcsPQpc6x zz@7T=<7dM^_E%7bP~68tqD_uWr-6x(?<-Hn{#!Dk^SwCWv&O+kh;V#4lr`VwP2PF& zt1ogvjJKPKQA*e&T!J5^#?4jIhfid}_Rzof}JtlV8yyxiOea#gMu9|dRTzU zu#i~Q@Pdi|zStxAfXi9{_gHyidUo~#Hw0^xJPP#|45tKBN_^s$Z`pR;ndYv$t;v5K z8**Pee6W%eXvm5i&)s!rd`1(FPpksq%|{Rte#M3Py&O4^bwboC3y*SClx-Ozk&W^2 z)$u|li!8-ot2y-Pi>GsQQ&S#KCHeX(fdP+C_ZvX*8G5ruP;IgL&(dsgy7Co~YF>m& zF>kOqX@%`$A3B4r3zo1b0DC41&&hvc=6An>1`O9c+4J@NU!qQ4 zSK%}bIr}(|73BTN!X_;$yfqx&n_G>_X9~JBcU+{|zBiEz%n0jN?=_JqI)XcY+ZR9H z%1!nEw_S>fbLqh#3 z+(^0fWlb^yO#YtW-=fTxmRnNB{9pcCj={%?9ztpasgZm}y|JZ&6GqVr=~zFHX0Ckm zP48V0K@s)g9YGD#C~n_d5R&dWGu(}pjsVv7BnS}|>_^PlHs)=dJ2=`|y!Lrva&(n@ zcPDV;m~fMS$nxk4E9V<5QHq|hRKv4X3JIlvReXh)kn;o!)qG#zA>+E-EvfI)IJq|g zM>D2MYU%LEg1L-GVV_HKZ7JdJv6M9zo&tv>B4g41>^K|OXS=;dOnRg={D2Q{{vRqi znk(iA4=^xxa#(cm^T&y%rugzX?e1R6#YY8pzIEDB?hHZq>n=wX1+};bD4}3yMiEJZ zOqoFi4-pDAM7U*z5MBW4BA}$^asd~YN0$i~$^}L7Fl&<*tTaTE^JiEjkpeGwy5Y;j z&i1FS*mX)m(XZ|LlR(hH$&wdoS7ugxTy0f^$`9E}s5{JJh@kEgIEWUa>!`9{gRVJ^ zd9FZf%$OX%JWZem06(b-3X=13a#30OSN`}WbeDTGW9(OEnS5Nsm{$OS{&AchW!;)$ zJ{DhQe(zpZ4wjnvTL(+72U}PK|DquFgwVW9eTQxxB4oi|tO8QV(-M0Ng6Mfgp^Pku zIs^>!DtN_$O$Izcdv(KbVJluF*9?;&W&-&8Ql(mxtXygDSe10nJRe22M_BHIu9CRV z2mt554G&Q`tb$W zSXhmM8-NgRlwAld@>QNt&EK~yX0EcaojyHQ5K0Or zHTcem&62bNUv7AlNrUR#2gB?k3s+6wR?}KY2;h81&5mlBpkpm>wuNS=7w>%)hBRTM z1Jsg0fYmsa#fJ$nKnB)veqw)z2XJ9!rpup)oR~Y5Jo_<^qpb35cq(5fC^&cT_7^p^ z+x!k#z&O3%cfXU#?6-~v+G9I0!+LIO2w1(o56?Cu%IuBVM_q|W_h_cIN+N*Ydy=cL;}aB zd(&%qj}mDHg1q|)xAUx2`Ngi(mfKkq?eBfO_yz$cj3=9qGiB!MCI+n9bthD7tKk!iMA z=&0P+5mIh6snpG+8n*pW%_iWAvd+QcLjgj9i7FJQZDKOfk2Uj47#$^z7m`E;->war z#;Lzgl1gQ7VCiNgT!D?mCR1P0u^?ftToI0;8S5)rB{9G!#VLiLBjRpGdY41YDRvK~ z@%CN!QJeCmUbUZ$QmntS0YP|}z{s(nbCGq=X6HXV969^|GA&pvpZgCY?IFGzkd5Bs z)OJJ5Y76ur5 z^v`C7DnjP@UWjt3vbrP-nG}~&u#&DhVUl8&m0Y+laUJ(dfhhQjGLkg32f#@PF*EWl zfQUK2o*t6(D2PFZxVa8k1JPO^2qM6-eJt78+22pkTe=t|7G0HiDBh9+k?iC`bU~kyP6+Cs zXS`?0(MohO{wpQ73JaY zT-b`7?56)VIk2x7>_esKkJSk*h!-0Nq@Rtyk}Ty4^ziTyRnHQ}%Exr?+CLEia)JP& zTs{ln_yDZrN3x2ziUP15Y(8h?p?Jjl4Z6Q;tS6!BCk>syselKE)J<}~zZ(CG1b8ge zxEHjNth-2PklbIHmEbpQ$o5$yz3{!c{%>=;f+T!g6~QM~ub;1IrHsVT(97V_w-3w~ zusumbX_bC|V0FR*#N3-P)$4l9ozRM%?I9KWVQ1`v*dAh*py1g^DaWhXM zYB=Co!Szp_B5BREl>ob&sl=vBDyJbA56MzLK_XK2Hwz+9_$~gDMv9bt=Uh!9OwJ#w zi(%%JBQwAa4&CrBxcg4p<}dS({;1ZWCT>)KM8X(N8FcsgO{RRXTy^L5jt&!0hyDA0 z=SxjwRe&NJ;6s9gI53O0#>9>c)3va?M3Vw?KI=UqoK@ahqn#Upb24{Fb)~x^6SP~| zU+`ORlVt?f0QePDrzWfXjFGb(~tFpd7Hd@sH$-o#f3ac6&;Q(D=h*gSH98So*HFI3t|r7OoMpCPx}Tv>jcoVL0&mPhJ~6^F|7FbM?7=#N#rbFN?AmsGw7OChb462yE zgETJ@*3cY(CMRgU_@{C?n47Y;FyeSHEI<6|);799^X2{Hzlw#JslSc{_@cK_XH^%L zwJABYy{3F)*#e3rg6;6yFlY?`xJp>qh5>|Ulq`#yHUKwTdu;abvOyrv(3I+Lx5Jp; z_n;vwqTYE95d_`W^z;XHw_8PtEHsrmWU5hJ2c0+Hh4t^vCw_gvMu;UTA~*_FnSsmW z%`)>|f*>gD+p$Bc{O>fI1IT>p@(AN(YOrKZATw%q40U*%3Vrtr=I{ zC~Nq|EKFaPnL9SAFUSR#QW#JK1FvSLenKI)v`&=62N!F1RveDYKhw}zbV2uT)q(Eh z6B(I9Q?Zw9dm}akkRjj@2_d1`eZ4~fXT?(Q6B>99flm|7ail6MryX~ptsgf#*?lg$ zEuHbOx;{)yZy1{z8k%*Im*50eCex(&Op|*RjcknAIi~r!t z%%C2i=<;msqXef$@I=X-s8gLT$>&&V1=;w1=0Hg_&D|eo?d>&i%Rh=V6rl$J85mrR zwU%lWJnV;#_mv(#bxxwft#4c${9M1+0QA%%Lb9l)wFy-vf4BAaQP2a721b1z4;_4N zT>LI?)$?e^k|P^l?Bc+y_{&g|k zHe?O$=f*(1IE(_n2I!$?X_b!WYMnXMHvm^!9E7fvapOq$)qcC&f}{FJYi-pRnE-B> zB8Pof-YXjbYV$R3f@j#BVX8wIrTTZ76AuMxZ;by4XwJH#1?}ffRf(AH5K)Pp%Q%Q} zXl)jVf>VRi2Jl6rLf}>(>Vc>d-rD=h$TvZky-d|rdS4tIZ+bfKm%EYDW6S=@|{;dTHg0q%ikjQQ>`v>gSj=0t4 z#zVg+_V=DWC=k2^U_pLk_vn<9%IE zkUrPpN%NxH9bV$zQO zkqpfjCA%s!GfP5_24sOwJ)I%dyIO=0uF$~H(^rNeXshvG>*l>S)QK{NeSsvpN54VT zEy0l1zryGfE71}Zpp^&wROvnofh&GUM#%MmRtRXYXrCZ|9wfabLlX8m* z3R$i|a_BD3)n6S>nX+XFlY~ZpuZ+-dV%%LFc2XWWfjeirQa2+P8%!-`)nUAXKh4EJ zNUQ`OW;m6ya<$g1sqQ0-@hX&yLYItzy;n;8Ulg! zu{>L*F4-g8)6hmio>-lc3~{1E)mYH_p4a(&ViR|lG1I1+WoszrRH>y5I=^>+J-G`} zN_$psxvWzT3@3uRZgSiE^7J?wM7E%&{uEok@gn#P3P>D1KuihoiMlG7nDcF=@=6t#J#)pr2F`sWQj zu%z0U55PpR6r*6e_!zQ)`fxrtH(a9Kjzz#k`T6!Ce${H-vzx=|HaVE?RZ6V4_~V<& z1P>q9MvDp(fSTXZpgr3B$GdbjymW`|WX8qk_|B{aW;$lQzEkrdScGZ?X5~Fk;mR@! z+t?M-k32y^$A->m^rxXLI2F?sA}kTDtPu`a1455L^=R(jX=FeoKi>123v()B;M2oe zv>SdJ*7$L;N(vN^X={!?2=rzNx^YUWv-cF?&>SpmneF?V@gl&#Y|G7B=RPtw@HohS zSQf)`hPf#}%t;d3Z$W@sQ%0Q`i7)Q>c}*mLWHs<$2(OGm3N&>MQu`>b zE@VQ7Yc-tCFBEg?TibvQgbRc#1x3L~A44l%;Zg;khS@Cr)Bp0*M*U0Rb|vg57C{hFt0p6A>BeP>7^7jaCLAmkOaGUx%Bb<;mM#TzG{Dt6v+ z{^VB=QhNQ~gqyl@>vBSfrZ^K@T+a*}tl1HymGG4CcYSU$LK3)bXGuS3|B7&^aScD7 zvfZC@2W&<%A8{ljPHiPB$-#gME>N$O!~*DWqwiguB)uAm4*V`CjXlM15EThD$uaaH znlej?Mx6`nq?c;6wIX%$zl>{{HfcvKMW_b;cI{b2#z_HuZ$@fe8I@k7Tu)PwEYw-l%n(UJ5#y9Fo#NQ*{w|mGcTGgWr~N5z zzr;2m19)Y_g}u5mH)`^oC;I|>e~ez{M=MM`JKmK!KC+vZRlmZqJXlC@aEQQ#RKF(X zTL)_2$_W+z)Y7f5J(aoSOO?li+LW4U%j~+p6XZ)F-@PCU4bfe#a%3q+t8vfv{~V__ ztv26Jp<3(*t*n?+M@if)*T`Tbjzh>?YW}hD-;pfVAsaQY-$NY4w?}HiuYja2Z2D}l zc@I?T>D&96Kh%$PY%9hNTj*;@5}nE+;@hr-PL0zX&QyP ztts@cVOjxW!bU-8={A!=dd*ufRxa59V6FvUsIS&JYG!*epg2p>Z3h3 zi|=2{uj6yUyQZHl_Sbc9PYZ?ZZ~rWwzb6407#Jp`orEFfI506Uw&oD{^QSBXLPsM} zFC>9QsFK9WIAuXBGbbzpKp*BhT{5}kAzJSKJ*n(0&iaVeJ^b$_O7G}UDe!Ve;#+Gj zN>M*kWNE%^U(@3}D{XoCS#P-DdaNc;8-VA!q^QY;`zSqN#Z&g~dQ4R(|IG*W1xExa z3BH4zmylV~-%FAD&@FG@r$YFN^1hQ};DE~sd<-lJ>j`+fZhB{bcY*brye0;SAP-7G zWhmgeAtFu^*y|G-d=3HvabOcfl2)MbSe-|LU8%kD_e#7=#|spP=`9+_V=DEaOP-Zo z$enq&WZU0arW;5H2t(Kij+K0dQs% zUme%Pi_aEqz{zRU3X-1wYRaz~ZK#8!w`R0@YHF{%gB|XeTnxOV9&;*R+f0R?8qhja z53fq2O&f%)N$4e$vLSG_47zWP{|hYM!0BF;at~>N9TN!CgKK&7dP& z8dpOJV(%I73l8O>M?5WP7xYWPe^+k#G$pz@tP%BJZhltnA4+2eYa8t#r~I?~6vQK2 z4%b0^Gb|iL-#&8DkP+XvH_W!w^9EPe@U^$El%AE1Tx3apoVCmh6x;nv9u?^bP`ET3 zLQFkYT}HWr0sPPJNxHhC6NAgSegKm1wzNJ&k^sac0s{fhN5z4;UfccVJ4$pJF?q|K zKH0_6LU>D;CCk$3UvWNz%)}q=Bnkx|wb9RJh*#1N&R15te`j7jPQ06QRLwCXC@Z{e zlu28-{UQYzH-7KMibGsA$dch5X1b@dJyT<*+2>?tO@mtb z{q1FO#~o)2kER6QZjMpbdDxO2T;9c8o=G%Y%#0X|(l^LEf5U0{yPC#Nb-U6mCv)$k zQVXt%uOIiTSS-j1bj$jny34?4RIvPX8EW8J$O)L?;UFSBIwZ+qv+3JJLQPwF*8Z-^ z#i0%!`{yYq2HP4qgsHpi3D&B*>^19ln-}_4912;x-!5iq6L{~#Nc0bT;;0tyI>e3r zOVBYjDV&<6*T9w~GZe6s0&J-;T$sydgRGtP05?k~^fW_#F@b08Vo3^AW27VnNOkqY zn(lF5J5+P`C(Irq@9r>aU=Y2$fcY?HukK>SElWprk%2V6w)u9ZtsMr3+i2w%vV3ng z))svkOVy@>n(ws>cv3ra%3oDo>#(ciM2Nz*C>IJU&k|IyK->4z$9ZRT!ldK2KfLxn zuMU>+`m9h}x7RoE04r@@j#)~XY(T%&5Xx~6VjPNzpe*rNl8#Ct++!}1eW?%_!OLI^ zKsAIUN`9Dr zRv+^qH%mc5&=iN%5uQXiRd;P>W`R}i@&0w79;`qR?DD`8q<7d5r7AhMy0)beJa8}T zfBr-T&Vl0G(PScGH*9fcXlrXTBe%bc!7|H~!{r%%4j|Q^)8gl}DJN67PLsG+bsqw6{*%kDV;>W1C(JYrV!U(vF(Kep@9E9&7< zZ?`2dK33bnrY`Ee?WJ6OJf)klqbP9qA^2Y;DLZD&c(oXT$c1onMCgeq?<1IgP<*^ebkQ`+mKCp!r}v7(@Ik zVX8&Ew?d%F^PnsMi2-6oVz@BmUmJTO(^7ix8M+9s1|9!^&m@_Gq=%qAm=P=jlT3iG z>SXluehKwIs4_eFviiKYtmyXozssg#Yg&Fg80f`m7I5GEfP`1T)ot}k4j9QteJWwU zTG^cl_BF(bY!ot)lmLVGP-Nz&s1B5z{EJtJY9D^QRaqEWYBmO!TS61{1u z+}_sP-Q;55gK~(??1168`lqR+AV7k7+BHahlU2TzO)3nw5l!G)+7>qm%2dl4)iM-) z#G}MB@CqWLcE_&0G$sZb-l*B1Oy~3F8gal&QEL31Sd=?{bA{(j`y8BpHOpe^vk@JJP+qA$0r>g$6w^OKZ8M2WHZ1tsSuXO>_Cr zF?JK9_ZVgw!=9{0P{#(^K|-DbhYZBPLATJbD+Ggbc%WBMgiMi>z&r>FqF7MjpRK^ z{xL~LgP?k;>rn8jZw{z&c6=0s016GJ89UVn)0}uDi?1M7%3;dgMW{o`EQT#r{HAs? z!c&ik3r9;Vh2Jja8-@sY?ax1AGAG8gBmluwB=>UFg4`h*h>w87u$+r%ksI`nMZ-QTTRcH4skAg zj{2xmTym_yTAeTa{lCrKANGCu>Dn#uArMaCi>4MZ zrrC)&i53d4cz8i~GLm464xuWH^mZG8KIhIQXmPN%n7ol)Hw%@*w`Z?xTp2{yTRw%O#H~;)e^*i~kuRKnKs}Y#y zDRSQSL6^l_$wTE6doc9(=FK2Hg;Vt~BLq}_-p)P}wNWST(h)cS4(Z{?{fW0!XFOZQ_&_Qg@h4P8xt((l><;PH_VZB9voxvkYc zPFeb0q1z`FS(d-$1WOzP)X4dCydiZFIfr7WkA0^vrBnktMgkC$fwrbI&q|9Q#Xw=* zz~5ORmj@BxHzmKOgPrY%^JmtSAP|S){NjZeVeD%Xr#COXkOkj)KE|XjyULI+h{1qB zvbnw&A9;P;-{2+68}^u@ubu=P7p1%cA=v0~5I}(mG4gklgB!+T^Ezh;R3OsZE5}bO z4wTOlWtPihm{e5z`c6=znpx}G8CGRGM~TV(7CMe1_>IIm5My$ig4$F(6`Mfh-EC;M)5o<3}C-HESZ} zoF*0@WYFJ%2Uvhm;AdXNkYoOxs*bM8Ebs%>n$VD^{Utk%Qg>EZnwgMn^53~ZdH~Sp^P6QUNnC zcIa~k#NVGEql(&>FC1Y8g>OEyMd z!We|eA*9>SXb=i2EUyIY<^-6E>TvZX*#c+a9>S08EsRX&ODz=~;^O)UzTydu;Q$s; zb*!-`KB;Z0jkm9K*$qRw3CAhb?EZK*Fq?zfsAwNpH+{rad&hN>(NVm%Yi1_ZGS~ej zF0x?Z&?0MLo4viY;{BhRk=_lJ1g*DH(6`CDU-V*pf5Tx<84@2KZmm=QDKQM{&Q3>-)I zjQxmzsLe!l^(~#xkR^Qz8~i~I_c4nKeAP&p@0B19Vv>a$dP%~x^$M5?3c^o^a*H_`a6-;H1`)qBkKvl{>RZ7h|9#CVXE)PhIIhDV4dJ zbM5vxDU<0v%FVaJzb{VKx-`r~5>VR&gnT2z((yDVH@R}3xLi?@CM7)bJ4CsIX$S}9 zlTroBxEYf1Y2O&>Ez*nd@YBEeQd5a@v0;#@G`<(L+tb}%7MmfSddP=^I6ntFcl3^d zD{+900~Su$|3*byP-%xq8UI-usK)r~E1hH0yL*-PwuVSl#-{6puGGkzREj!>iGgo* z$tjs>a*RScjE^hJQVf9JW+K98M9c~{ujF{uU6ML^^zL+Q26Da z4kh$`Q}nB_oG+A}_@~DCe8G9M#zs{a6mX&kwkRxq%$R1`-q;fpJV>2cR$OJS;-r$r z!~Wqx)BRNU^{>&4(J%SZ5Zu&j*}Y}MA2Vm{?YF1E{R$-I$d>8jYfQ$6{{R)pATYO; zH5aObcA_hGMV0M!5jEdU<3$Gm&!mlS2Z^3dEMix-{Z0gOM4sUQo*HuUz7N@g;WYfX zC*a44kQ(Q88)O_|S;C7U+v_Vk{${uQHpU-EKY=3C7g$;EP$U;wW}NHm>ru(35rEmw z9zBPLhgpx#vjFdx-`G_?l)N+Ng|VrF|G2$vnl0qXoR_I>-1}WuSETj0Ymger7G<&a zczX&?A^~dFul4K&OnjTeHE$Q*-Tsy4j3o)ta+FtpOApZDMR(Hv=M*#;Rsn=CX;0Ly z5U9yQOmp8md8fwO)0s+fBCD1t%S%V0QuL(A;wMwIZCAoRIq(Uinw=S7;p;&Ga` z;`SiOXUQEWrSx&gr!SQGYgaGhNYX%;Bd~2In3kkl_^Q|__}p={uw@tt7RAyh#47O` zQRPi|GIpwYmzq~rW^xofN(3H@u#awWd###X2~8}QZ3n})1IL~L3q_zMy-c7UW0>;ufCMFhGXSB;GA+IHB40K{x^p(d@PwhVohQ*F!+qhM zg$!lpF1u3jHJ3uQZ5;OHE+mk&5Gh-;;duY6fYLVJhTcBw;c+sKJ{JAA8!d5nv z)@#B^L#AH>#PO328#zAx7ka}sr=4oDz`^^j7MXgMnr`awwXmv;yMD1vI!1WstEHxV zT=lBZz8|p&LLr%Kz(#rVCAEKxE8t@e_&28>O@o%d~ zq^mnRa0NBE{q}gc4|07N&oFoCDE}ZQzxR0f#K0$n6$7wF{+bO3tfq*;X9z1B*crJ@ zP_#Ld1ju9|yBFSdYSS|wq)Pc`#yZmuwGnup-euw`V#RMKoxlxYXB(Ro@9U=Q1_CGw zxCycBWAkd)OPu_MYr;Y;1ZWHqPtMNv;jb!sof53Sy?d`xF?9>#fVuW8SKf6}M17gN zQN=ULV}e(0tXA7D^mz^KmN9obDHb}%`g};blTS}ZB)bfds=Ssk`<(PbutP^C)w^?5 zmtM|*YEXVyppdkHf(V-Y{DP>HbR+u{cv9vaFr*ez50XBY%;`b7wkr))%%){#eO?!R zeX7G@{>86m^gdNKM#h>dA)9kU$l8lBB^)nFNyOGjW3E`W<>TA4_@U9%efKYf)*qGU zo%RY649#BcJPBBSe(?|r-oBfN2uMMqw0VJuwtx z)QLs`#how8SnZhM?vde^MRz+9(oeWzp8~Jl>zKflkP!IKbB_`>SjUUn%x_vbVeXPi z&HBE%OuJ^17>_mN+i`h%*0kob9z&h@_hO@|q*TkkI7{hGQn~kAG&Rn6l&wGIeIJ0} z#2D0k&52bAzw#D-zN)AIJov(u5z@c9+CBAib-MOhTx zQj4FXSZ{de8hqgZQmpHDB>JjvKFb~8T5Ebc!BQ81zPm6Qj(0Rx{K`6(Fq z1V!f{q?^+Z%Ut}`8!unzqg?M}>1{?){I5*hZwITs?JnG~RTE=7=WvCW``1!Ztq}al zC5$rhtPS=Dz2Rh-qcn&?KQ!|5d~?v4Vs$%jhb~~!3qAM$Vd;VOBdrS&0|FY=hGMo5 zb2Uwi93C(eagyBw3g|_hy*l0B({Hr1&1BYK8E_1NW0WmTcwh4=Dgz#0{>i`8mbp zO6;@D)koK>(2}X-CV$pqwH%hkWe*_8X_;)!skyk<`A&(&S!F3Rj{@oND`tr(JAkgclJ$VckG_@SXeNCi9Lite z4^ZOOzs1|!nBbv-Ge)GhY62-k?CIK$bW%h79ttkIj#>t>>EVhMrCPST5{*ogN`H0lc{hS;iG<}d9;NtHZ0u=wu zv>&TJLXXGSxQi5+j^7gq zjGCKB_l6IuIULCnk?Q=ec?|spg_&;aa=@9pt1YGI|Kx9~6~sV7e}F58YMfq3lJ34l z|7gy}78>Rwug**mXhwz8jU46+Fay^eaQlQ5_#|jz$#ZD`%f$7~IE;;-t8PZ`nZe zOCP3^F*Xl;kcrp4wZ)b9axH`bhj)#&AWqQ20ydtGr)Ad(hGyrI$SRUHp2tpd=0@KI z5^KEMaC>J;R+d1uHg2dzVVCEhqCNfv@*rMU=;8NAlOJeDLKP`$Y>9y?ZGyLJjs;!5E_g#qg|2tD(-n_7nO> z(=D@^fG3cU)(^t|pgswy8&L{{BP=*R(Ps`&phBhrvMx-SetG%(ENiGk1u@4@I+v@5 zzRv0a#le? z-s_QTHA_?~s9+LD39-;}y8frD!DypQ`bQvCA^-W&p)8 ziwsT2!H0SrZ%Z@Kj=QXFzK!_X)I~z5inOs^hdGzS(8Vmk0tQ!AR-Ot+k4tUmZp)|v z2jtq(O#9OqcOkp=(t>o_kX!Um~)KY z6tu~dOVO8%_uk%FmG^KsP=%4PLlo2@2{ZsH8`aft8o4qQf02^wIBMbjYfAD|Bg=2c z3vau=3jflOir1BUk@I5S;uhOt5;D-JoxCQY&H->0zzGYul)-9^^2Q|iY(raw-Noaz z-b){hoQgP?%~-fb<=|idf*J#$_d?rz=6d7)o@-ioH!T-<6e|qD!VFB8mpK@)5GBR! zIAOB`gy@t)=9g-x{u|E2BQXN(-;Bf+Bh$w$enm}!9zr#_3xpy*s7;+dZ>oi*xYz!& zY+F34HP(3cauD-Z(}<;NdJP8dHLGVL*nW@)1rDmBFCGzL8#eKICfaffGN2TA`w9Cq zFH$C{r3hfaK+6#~02o&EeNvex<2BcLx|DAQIp33uvNpp`_}rM_^zof(tZY6Q>RztN zO@O9v&$I+N$neCsD#-E=RCDBOQVe{^68_NKSvBhkHzC`&H8mA$z&TALn+}L?MC# z_+B?YF9y#-fE}$yhgpHwGT%TevAor!CtdAsEkY0uwR3gA9ni>8^=9Sv6C&})Zo`f$ zs62dhr}>l+=AtzBBB@8(_Z3G|CDHoSmx2NfMuPf4>4c#zEHU4z0N74m} zQ5M3Ip_EtDO85bmX?DT1HA@-(hb0T?xIn>oMmi#{{|ovV!^6r&jI-OpYwe>ab>2- zJ2k!$qkoG(ynWu8H&;}4x}))}5>Cq9$$F5Y7HZb9c(AtRa5vY|6s6hWnBj^~Y z@36|sX@bNjEdzgxUHbwF-Rgj%ND`($tJNa@S^mtyYjkbn3}k2hTo``lR3VpJnlG^L zVy4-~3!`n{=)!QaSz1p7%mBVkw$xlK-p9Z6w`6rp6=u>+NN_kPmmW#qz0-s7*5l!R z_kxfX*M~%738ngdCIj}Cm*UQFYREmGbO04$n%Sb65TP{{_{@b}t$RO%w@C!9~6W z^-(mqGJT*exFsZEM=kZB=E6z}Kse_}cOhKHa(**K0;GGV4S}?Joc~9TMjb z5*`*52#dypf4Y3|V5QFzJ9-Q+as{c+ML0KR2p>Dr)43)fn!8Q*DYZP!R9ye{Cga#> zJ|x}~ibGA%c`PB}2CQJ3pBgSd7W|}w%fIg15YO!PgWq0#Mfr~Np4!ON;RD{rvc}`b zXCxG{lr>$fS2&?w1)ESDL`TP5OUc87fLX%ZKF$X=L;t1!z*`QG;c`v)Kug!%ev3QM ziqSc;kFVw$G$0}5JVE!%Br7;AjLL-rLN~@FQ=8zFE&sDCl$ z{6CVu!lB9T{rkQbFnTm1F&d;xLWEIDgQApl3rgcdvk_8)bPEG1l@38AMoB6I2@9A= zgHqDwcdc2Ow4PtsguNpVWF^tmkN!t_(vgB}l~Iu%b( z5_W_}ifW%JPfzwYIFZ-(X{*KO`_$+suFn7pK$RaBCmeVV)3`S7JFx&zovK6*;kFEw z|Dr%eE7h*gEM@+Q;VFIW9Qz?Xa;X-YL0%!D@1JM+1Q|;nc>n3)?r?hxU~ZA>tMeSv z=z3y&oc<|d>=AJEQK8e9(fr8RbVM>5y^5sf10s*-URYd)(dApkgTNQp&|Im0=J~jc z-bg>?dak|ak0OF@C3;iSJXK(q0UzcSV$0_sCvCfjFWj|T3hPcoueNjtwis{~mI!zU zQ3JuBOi5h-HbcYh{KP1lp#CtGdo0v7iu~lb_Tq0K&##V|6xk=A#~64`!l82}nadP6*Zq7o0i@h5ozWsq2q=&$S4CMkzS>nenp8B!x zw$z3Hajo4{7#`-%hsfDAIL4avDVz=KuddZ56h+Fira}ptKb+0k2)?DyMW(sq4y^~p z!|xt1c~hePk;gDZ>C|&XX$7O=u(g8!{(Fm?oVD)C7aMjr!=Hp-F8ST6H<7tCFxR~T zLrYv`+B+)v%TfSCw4GfEK5mbPeJumX*8%%Ith9BhoM($L|l>+Cx$ zdTUrBjENJGRiPzQ1yuY6c%NNti50+e^Fzpf&uwh@vb=J+1t~r)WyJjh=iUHw6MqH*^s4>s#ytu94j5OiI zJgruH+2OTKhkkjKpKMgq&PDkw(@NRceMGh+@IQ-*gPe01cIfV#X`gi;QWqwK&~VN) zy>)v3PNbzD@HvJb^`$`G{TG0~b?3&Nh*@eJ0=QGkTD0}iuG71+ZN2Ov)8(Pm!}m$c zZ}I8oOm=r8RzIsr>YXR|M8!Wit#5e7Zpf*E^iCQx*5wYKJ5g}JH8C%BmfllOzMnCr zVqcM76l{*X+l-C0_umtT-`(=1UhG#8 zF;cat+R7g#&mzAMZybmt^ZdU!_+DQd7gU!yVe0id1Z?9`u?|r`}xmgkN90 zI%p*W7kk8NFm$VWnNoNqUkSl<=cSZ^1h+_S(JJ3{UCP4m6P_umdFqOzOikwMJ-+EH z6U@p7s~ZWLt|+N%;cXJ9jQmZw?{?a5adg0!@78ClJdtv5NSPEp)Hc7J6!Wk0rdxP_6d4`Un?8WdaH37gdcfEwL$=>_KV|7exPoSP) zb-*9Jy=7<~qBB4k&=d_1&iqgiD`Fx#L}B=Zy-0_}19fYjpeeO4YEP4$Q?~B&@wc2W z%ALWUs0`QQjFQuSPBxZl70SR{*)!+Rk|AWa zCne`BSKX8eNFu&rZ7+!pZi!oFj7E zP&iQj<%A}W`oK}aCuY{t)DA0h(_dok$G4vAGTYXTl4JC0;}d%=0dhcc`*2_`B5Q8u zwB=FA%SncyZ zpAHCHx6A(j&qe&doA^7rkl}+OXCy7az1%dm@k5iP-t`lEi9PpO(hG~OPF82HyBo$7 z=*Jm17b&?IxX$qfTdgPS1Y3tQTh2}B>$@Y=JncMJuWm2+PaHyCpTjS<3+^ntVjfUv zMtR)7BKleOv6Q*5Ci9kjghA;erjz)?Hf!iG2QX}XELsTOgF^|Yntgj^no_R@32!&J zg5{U%ZooUV7L2rh$d`YD!6*yr6{$F@0G>QvsEq(EF#8j>+E?5jN~?O*nsk+fD`zTf z1Rl$L*F`6HnwpU=x5DMa@Ec07gcG~aKP`o}AA-JIRY*dj%Fm6K4%C!@2SU4XMLDGY zM3~^Wqwg3l6F%_Qdo^>Xi`Tjst#1eh$4>IDcxHr-315BuXS* zni#58vbN?(urKNPjeWQ%zk2|$)p|&5wwbM1eqa#5Z!Y!k9+7| zxg1d438DNc=hnA`y~~?UJ;shWy68`mZu`;+pKm|H6TZ7SK$WEk{(rVMS*eL%|7IM0 zuX*FU6!_(704jC-Gf0?^Q`ct)OkgA^@=V=Uw_UhFG<#(H@Z0ZjTaC3V{8A_ulWVw%5lcV;?g8!3lzJhtoUMG7t!l-P zTi|Op6*14)^BvvYFb4<>AS%Bf>TQ161V*VyU?%vC-QWo-;j_OgMIk%WUIO@xbxtt9}4M6KFMm| z><3KDC5`rEsqK5>C&@9Zgii|G-Jj-_u5s}zT?iW1mAIsfl6paBTp@nS-gkHISH5^X z>6=)0oj)kx(NXjtJ?=*bWBtsX!w)Vz?CxkmfBJQAZ&(C;KDqzqZh-4|);r#^MXn(h zwtNFYRSnjCfCmS)M~W)?bs2H|JTDB&Vq(9}S#<$S<>lc1-KFiC>k$frS+&{Gh9<-%9 z2Y!0m?Cggd9T8jnf`Qqn;WFWiN6$qi2as;!kTo8TQ#zPnvLkygd_|al@k#kiZ`6*F z9`!I4i^)<2I<^o?R-p2WDKqqHqqFW5kMJMU!tm%Y9ouZDdyKJnqhk3|mz)6m_Zl+= z1x!WV)GT*xv_7N#y}Q1uyCJ5^r6IcS{3*zB>?~y^ghl_AsB^FK#JS(3xkR?a@UQF} zb@YcSnNTc2kwoYUze)nbr|;9#wNS%>_N=kLjejC%H*AG?0q%>{HzWX;>W*}3f@wIp zy)1~n698o24H2O0SniHf-!Xi59^xsv;aply&XCBRh@hZl(&8uGPxLyseYN6})_zs! z0;aF?Bh$S3<|Ehm;ae~$5il8|2NoGh&}PPK$v;Sl)bm>jN>$6)?}@TdZR3CQts<>& zxws0xe2WTu?GnA`@0#KOsX4z|<_vR+Qi*AVMarfapP-_ze948CMd&;j_k(MAA%c)pg} ztm&x!hbg0MRO0c@ctqo1bdU3%zV_q^0+#>lot3M-;P+WP)SXR7F7{Ba7$(cj>wUn9 zEd*03@QQ$9$`haWvttYw;De32+tix_gUjlh^YAqh!wie%)OY(z)hYZQiWwP2-bIS(db$sQk4O5>o(+7yUl(f)j*R+qPr|z z$x!r08GF9fEC4Lzc8c7hGxpys7mo1otP|-7i{KxI`R9C5A7UkDmwQvVQn$E7AZ=d` z#2kc$tV-^8#xE%T>%KOMJd0JrfYJ3zus`AUPGf8fG`rSo|Qe^?y25W2yn1%`JqOp zN_3lXP^j9@i3wG(5K0!ul_1)(8KPg8BqWmc zzvHV&YF$s(Q#cMv=|fCjGP7rqAiz8O4D>P7c!xtvlVVI^{pApy$r3wJ7Kol`+C<|{ z=+>p7!g}5?h?GFT0;>{DUVjQ*<~(6oR_Hoo-#c7UG(*7 zxUeCv%8*#+cBFj+)pC$70{%I&P}y@#F1hs?kLO`Kpwkg{)N5C(_IjD|Ir^8ygYOMy zv$o&A0#b$QbV_#nPpJg{)m-GeXO*H)CdPAArk@zyV9UjzcIg-Ikr#i> zF?}2%a79?|irjU)QZXGMq&)t^m-+{7=l!3v-?Cw4ovhKw+Ot1lZwKBM1GrbOD2gw~ zoPvTL*98g79o;VQ0Zw?(8V@YTv&>X#`VUFS(e^6c9n;J?O~03V1JOnrg(s%mSdYkz zLAv-C-n#uQ=5<1kU8E*5`x+|TfuzWwS#r-d?&RGd2oMh*38KYEClhF!LNBLSxUyY7 zbs7sR+#NancEOI+%8MDAixB*R5^8`p6lyXcqQXbtl=Lun1f20#$kW;XX^LP#hSL_R zQWXi3l8ur&@QXS2wk$b1gpW*!gbF7iDH4IR^UCMMCG+70Yzql$Ldu$cL~#H+5}_2a zbUA+#z1P;_+6t?WZk5M{0jWRP>fYZMOS{Z(ly*VIFkY6_35_I6=v?Hi8a}t9aGAp(n+}6{g?6|XCjOK4?LQ?NrkH3G zC+KKR$@kf0nbzOt;zPftKiCTWbr&XwT%poM`#q-9do+vDt-87mXSGHF#OL#&h>q}! zRHK>zbpP^I}={B*(MS z&t&{2CRS(Gn+T?j7D{rA1PFBPSBM-PUbbm8pY;I~WocOSbghm4Ma-Q~VCpVYW_=B~qmPN>vCvY6hb08;qv5j}AEIC~d>Jha$>CeJ+V)Lq%Q7Cuq5U1#^{ zSPY{~Nx_1Ph0(O+eU)P~4qtEZi-ee}yH>-_5vAwM1N3QtRf;Bdf_CFjXQF$m6e7REcl&x zNPIUgIerd67`k0k5&n-ExAvv-cloFDqfxi;F_CbgGM7d*N+}u$Z)P!EeGO-)C?ZE? z%Pa7gyGXI*$n{ip{QK3a&iiZSF0q7+D<(s5F&xExr%O!teUDkE0F%3ubBk*AN7orv z_NfR&)ReV^6jTgAjcw$EZ?o0Dy59FXZf(eHObNW2)PCX-rdIj*%QrLvQkD7ZpipCf z(f4dtXlx+#A}kNL=iF4OJ58eKL4YF&Ip(=@(9NfYMyx=!5>R#)6Yt5r)jQKUEBcmM z(bFq$k;m>|X~_%vuvVJxsyZIFdZ`E6?==e+Dks8bPwO5z)BC6PMp4{Qc4VdY>ZcEf zRVDf48HYHUXfm&_-FuNorT*V_LFT!yJVTCqZ@NZU)fkBJuReYP6sX~XUPFtlgcnZ& zGw1dG1Y!t<&k`Dq*567^DY&@+2D>zlC651{eNf`COybCx!|4D&yKqbzKa?rC^Ou%bFP3Zu2@sT;wNvdotxp` zYX9BQjum&Aq8GziU5lS2dT5^~nk-Z98zk0TUYqK|8)yfNThqE$q8P*ms6fo}nM2zz z=!Eg-f3gLm7_u|q)iQ{RyKFpx2G~o9M2rFfuF+EnU7IO3Sf>T26Q8DF2^C=0S=@~M%2(?H^$B6Y>I1~+T@S{AMvJ0)GvOyTrzqp;X_|Q5zK+& zi!`^8|4C;m0Uw_Hq3n`Sd94IYm_D*50TqV0N)h<3%iC&i|LAot{%T6}YvGi11dR}o zG)rUw6^}`uJyBZ>OuNL-OK9B*vsv5EE)vL6MQ9Hfc>JWUN|(f0BLR~){oIo}%zWGK zhHw9XQ@&iothx~zKbx5E|Hbq~$}<95Rd0T#y|t}ea!cRcI5m9{F6H$cizdGoZHcP~ zTlRBiG|7mWp{pN=xfrAN=HLJIb2|UuTp~Ub)*KJT&s7^gQCtiF@ZV{2Rq1X;WADO< zP;OCA*4-S2$JRQBF9QsXW$0A0cwm&=f2ct9UG92_}Gy%Lv zsY)fxI`y08T5SdG%#7<2^{akyqwWx0ji=|TKs^BDt}?rCQWi=|Xn?+HFza*Q1H;r9wMaUccx{?pIgNYLpRD`jEMmC$vo2M0j-%70{E6<|QNR z8yU~)$+l(CUCVP-jhBWh0ry@9(^ClN-W;Ijn7HSZ^Jsz zeg$t!zT8LXZ#5HxXp}mGi4ckB&gP=T3E*Hjx`KnWz2{G1l|F+johLGHxBf1(4kwW> zNvR%kSS^f2nYzV6v9j3LQoz&f$q&rWjU32qF{|l*BSfSTXo~ozE;sZ@-2wh9J*TjUZWDvz6{0tJ?sZ$dnhRp@lM6vK>EKK$aGP#dRp&hr(hd)tQbkAid5|Gb#fSBfxxCW zqrsPT({I0dt2ofYNF|wJi~(cWyGPgE3oWT_K0Y{O2hp(>fC98lBo(n3obch(0kZv{ z*<8rmkSk9UxSqY{xuq3b3S`b}0Yr8+2Ht3Fh(ch~@m<5H5H!k``nW{jWitDPh@J$U zm!luM@`OF#Pio>LdAq+_otMI@dulnvv5T_N8Nz@ z%-BO=a@-tg*&y&NgcftgbTzvfX)nA;h0+#^1j@-64bwsNMU<4`y@<w?s>(dK4(RuW0U_Ttx|#?qi}@0R#_+;21PvKRV)9FNe>l%$B|w;@w-QNyDRBE(7o>zCCId4#6wb1iM($rE z?e@$AyW$w2RQTmqk$fAjfZIwzTM%8s}t-38~FRn>Ps2;p8877MtSggFS`F z<1@lKo^MppQL3UcwU$#5dkF}b0ylyKCd~AV8FvKAO#>86rG?OObL=Si8S)w)*^D#E z{O5SASMf9aVU1T@Nf}h~PDPO=s(^<J z0dDdY6DzGVpg5&U3kaDk3gUk;Zn5Ox(l8ITh;mAd8JK@US(X#;LAtC#j8x$Iy`ALl z5Wm+SM!SjpZWOl_Q9jf4H5t&@OX8q23T^KtwF2;*QNUnfjyzJSS`;#OMLABGc{2b? zk`St&KMH~w{2~6rZ=DCnR7EoN0i?iB$6yK181rb5=_EHN%`GS zOgyvqAO_X)96d-@NTe03iNrtg%d%Zmf(Fep2mcHoE9ieqnwycr2(JK0(taaY9`^N$ zMZ+-#_Cp{WZHYT1kHYynk}U?0?oD3udpGGcbPq}Ga5ayQvQFt7&fFVkN$swg9!`=3uk9eAIuHj~4b+LHTZm8Y`sNX=I>lYtKqCE;`+mMrh zq`FJSd*ok7ROo-(Bu}9eag9_%D-dtF zVD^9i$_~Bbwf-?`IJm9dExCc?7!gp(Bcev5bc^;@J_6-#OUI-$eKXP;=pNxfUibdf zJ@RAdqkkBZXir4!VWBblH?R1g-Q+n{g~7i~=UDl9`!KW14aXZf29b2ttr%mR$hK&UeiNR;2C#!l{Dk!U#JNjk57W6cG33cn@160fOxCsDy^mEsa-m(zad zy@xddfUfWJCWYBJ~ zoc8si`MVNXG<_S1cK8}nE8rFuik}bv7e))1Qy-&I-7ENyTBNAv%+5dy{F5e#X!=6w z#7-gK--R1f>Wk#G|7~#Y{gLdV+hADID<)%M^0mDtCt5?Y_|P|-FrZ#CQ1^B)fQTXb zp?yxz0*AersJki9JVUn}CByS4HUPaZf^3XYJ;9v*_-rX> z{gX67W<&s-c1%QwJnu#1@}#|HRq6yrw9-VFbHMoTiM;iH=RcqN0qkrD=X+gb{3G@t zE{*8-y4r4^b2Rs+<=2DB;Ht>~xcsNd=z7Iy&OL){Wa7gs}pGsVX}47!B|MJgn{Rpj1$WStqSa>%kt~!^oS%TUS{3cP0D< zfb9WFo$q^=FQOX~kY45~O|h_=yzpIlXMvd+m+LqvbRoQ3dZG3w4 zxs_p>{unD?D=s~XaEfG1PiM4?*L7;2Y$?2@2DhEWF%Dgq{X+l)@KScGm)wF4lS8`d zIvLv+Md)sAye<5hqrOH{U@&l$xOIyf|K-6BSsc{#lqz?aZ1|7#AGQY@-q*P|o#0>3 zfyN=chYJY6X@P;~UFJVsvN=e?5NB94Ew`#zVc7L6qIS^qat9!Y1i+g?4}++30pw~q zod|3%WZrnDTn;O$)$lYdQq=E!KkL&nlEa9{1h+wo6%|@Hzx`Lwiy^wG)`XT6%Rap} zZ?^Ylc4Oxoc+7<0`1u#3-X~rJ#A%W0(ixW@PSoL4wy(D^B1`8EGrVL(*h}ee9N1|& zyRXl6ci>g@pnj|En~8$LNLN8THLBYK$^jT113n8-QvT_ON^Qu7@vEhck`SbiA`>Ci z4Ou*|H8;^@u{<4XWE@ewA~KqTl$17w%tsh>$}LA4W?E_%sgj6ucAsO=oo zM=0wHFeFDQ-|<8wXqaWE8eC&2_RL|!wEximx-a-ag%5E=$0&$OeXvnxYLd7nqSB%i;e~Eb9hYufkzBp8IlJ?S}RzqK3A4X}3_n19t1R(jg zb9a94VMGY`Zv4G*H8!%uRE+$}FldSq&=nq0gh)dX3ooqbD@Ik^DY*gk;*G&ag6_mL zA4ER+N&SJeiAMN!H8dMY)NfH8XWZ7@dOUD>>fMzt0{coz&YQblzH~C6hLzqNDxs&P z{Ijk8E3iAva1hzk0J__dL4tX|l+@K3W59;k<`_zbZ#+7BEE3P}?+noU^lR;-F-SqF zv}5$fzVqx!B59>C;muSlcMwq-r9>XPJ`NyJel&eQrkw;%+j}|Lyxpp})C<^z{MPSg__iIda z)F^|QzqTFc6GmF*>vG%%j39(&5RE}+{7t<=6GvP_h^T>Jtcv!W<~y;sX(l#EBuOo- zhR5XVD3-YN=jWrZzbNf3T5Tb42#h{ub8))w&aVgdUX~EJF$R+Kk)HYt_{CbVNF^P; zgR95bYero`xza0NMHnJ7TIY#$I?w(&m7QWVwis88pHjVY6WNCj`T}i#KGY3(Uo0P& zQu$cfs!`m06HCRKxFkzbQws^>6B7J_72V8~Dr9|n@Cbv}(?k=#eG|cB@aj(}pRQba z7-w&`6Gc}tv4u;ppu=L_bce^)u>em}QyLym4mUQ~m_FsvG)nY zH6(QzLgq6+t3W*2U@e>?(P@lb{`EU6r}D-%7ukkbz6vxE&eB@|358bo@}I-waE2PD z7yy%5j7Gh=+6!iMC#dr{lQk-+Ia1}97VMreK8bYKI9e%@gfIdm&E3F- zy8a?k0(bCtHc9-!TM`wlp946*ll)LRkG7E6fJX|)GvHoo(M}8d<;NC?2&Z5Qc9(N_ zD6IVi@l*`6Y}w~LH`sN*C=P=(noNFQgcRQVJ$m?R-vH=c=6qJCFWzQPE=0Wl{M29d z4ns0qd@LK39CjO{@kl~z8M}D47vPGzLB^OCW3h8Sxk%iQmU)n>&U4aind)LWMouIl zA*>slF+yDDe7?HuZ7=6;GzP5Q1>cgI#>&dd8c(HY0ZjpC<$(YGtNSH+aBZ%q?$>gU z2LyybLxh_-o*YRxz#QQ`Y%wK+X_-jC*%TFkm}b06m74R|o-GP&v6j2T?s=2P42`b$ zf-?Gpl$>{yIAvnK`+6A$AY`NN-+&77ff`e8l(t8wXLJEoeAjgDz$&D%?v~_OpPOb8 z+(`a%HwR|1Sjp0ao({{J! z*TD`GBzoJ0j?w?ousR9_h2+uq3M4DTTy&j4lA6tt0VCGc;`XBHpv+f21f}Gb55#19 zC5<=Kg`4Rb`SDqsJ@vC)X2?@~-#zAPo=)E?RZ>~uI@McOK(4WqSGG#=7Ks6qa(eya zfNi0!R>1Cf!aIJ&SQ9&yQLT&a6LeRsZ^(o(&1_qW6`%fG`5x@q`t--Zo3`{cf8Um$ z;)({J>9ZW%m2{=oTu?$zxy4eM6}={ku*=JXTC%~!b-npK>54>tohZca4K$($WpH%G z0`DuvXgEE1%X9X-jka`&&+l4_zFGH15yG{WNfZ96a^2~wSJmYdJdN-K&-!Z$v5Yt(m>gqrodLLQkJ`MaX&wZM}Ivbr^WK@pq=KYpclr@4MEP zKwmSAS*m$=&Z1cfn$4$9Qzvm4oMFY2y3^`rjVt@-P+GTx4A_l7Sx z&t(mM(7gGAGWI5UM-)DUI;aB;TThx!yPrBDDXndNei|t?-|qYV_~a86!wS&c+8WyQ zU{49)Gy_!gzV&8+M!_J#UYn`Z25JQvBAhupqyvhD4;UtXH$HZYW{y?%U)3S4vgEDI#2r0D?@B_ugk!-TM&F-7=Ii#)zc*@qhV3Gze2FN^j*WkrU1nb6|V#*p=Q z$@gK)j}G37C$UO$sX2XoHcfIUy4|gPiXKC2andPOLuC>~lBAU}D0VFjQrq907o{BSs(Z7+jDaZdNWw-1@C_H|^AfTv;v5fZ6VRPbaC`0T=y7sIW;i-P zgE>4q@II4SwZ(YI!8f;JwSZN^+JS>Aof!l^Iw)2_%oC){tcOYC3~q71Iu%9bngL}9 zLIh0jOSU970h-X$eOcRT*86a|JbZs#-!=4tR}}FsV|rmd?4}=QXszr2QERle<1ut3zcIDenp-}pCY060mW~>*Y`_{Ob<%N{B~~ytEz^&%`%%?OLx6{ z7hDpKsQ5-6j19c{8TB8wLLyEoRjImVGIQctl=1@QIVUpH3}Z~%tuo#23Pnf)B%z9K zwv<5NqpWAEPvJYf_~mu@%XuzY(YOA?jornBcHsbWt~E(6g}+YAOF3Qa;UsYm^GCa1 z(M5_7cD*TMt7s{FwRfS$up^yXASh zWYD0qefqH`vDCA3FR2@!_0tV>6nX)5``mZ8=Ng6$l?{k? z@0SkiArKSq{V>L78o#v`K2M2Ka<9_I^myrh;WV$0st+2c*pqxSmH=aAL35DpYN28h zKcL7h7M`fTsck3eQBmgHC;BmmO~*>VN4EWa*nQgW`YB)D<7b<%f2GljkQ9LnYf*?P z3L%G!vyFZF{(1=|$O}kggmFe+YF(I-0q?+vkf^5mXUM?7~w?0$-nL)uQ7!;?ae+L&D7Yq!B`0+UN zBHycR%+w7GB#wabwGCa8sO9e(Rsl12f3$Y**~}kmI8qB%7{z4Uq-leF#*rsqdRIOb zDEOWo?J$ZB2`&4*qo3|NSERWMY1poh>RS~+oAabU6_&(~QK9!s3r4KT6Zz1{ZS+f_ zQqtdLAgASs4?FzUr& z;i1u1dBv0|qM~iI?*ldo_1EULHXq5?B|mI7ex#CYukU9FdMyyfC;-9!^9A3n;*OQM zt~YBSp^cEB?jTt6ru)>xyG#r)KhF9XVf!_zOm&#M7QYtRKy4-lWffvD>N{=errq~) zo)*N!tD`go%?%-{^a!n|40-WK_uhKXb6Fy?zQPgr7PT8Z{Vl;p?HsK-1uuF+$}iqd zeOMUe9@7o?M~}`3_swnO-6qO(O~t;s{$P26?4aXG*;WN{%H@3eAZi5T%6p5GpA5@$ z-GbZ`^MQ7fbI69g23{E>z>y@w=38`uK$MO}bVtE&F7|Fkd1~%yE+}i>P07Ic0fa8P znS_j@ik<;KlFr|qXE;G%t?qe-HP!kM@8JwWy!BnmON!(XN8x$|NxtOJLI1?kw89|I zB|$FfZiISzSs5KY_z&C5zr;r+52ixYXXE57CJ~LQy_Hc0#Ex5Qu9{}%am4rmO zW7Tzdxz5eltzI|>1q$l`_7}ff{TB9i|7XCUdMC|>1jeMoh!R<^0W`xWWDPalM{s`KIsqQTY6-=-_chNGGszArqF;rGPpq%BDYXKv0LhZmCG;Tb@)r z7{I+2YDwLBsHmqN@6XwGg_bZ%P!UiTO$E>ZJ&?Irxl1xAM1aGIAy=yU&?{)2h=t3n#}B`6n|!37zr9{o(=`SmZiITV>Hh%?}7TxbK*wrhriyNnjsH4b~F=S+ycUmPw%bhLky)LJm?(? z^=t?#=d8R+*u8`YA~8d%t5+eG2y3PjNG43HgVPGlViS1`!zGRa{M7s)Zlk;w&p#fT zQ)f3h-1LY3xUTEsB$kvoiHiBEmbrAtBCYuTQxXFni);XvaABsBEm4pd$mA@$(8`E) zQQ#jlMzQkt1>#(kLO9)dkwvdWd}nO_<2k6GPmc^+W=-WG%LkBhO?FhYdc||hP~mH= zHUZ;RSH0SPRj5ak84hUj^Zj57O7tp?ek9i*L}zfPmFaj@MH2NGNodUsWF@5EG3V?s zZ;*rm*!Q{_g3RqJ^o!+QIzK~?_Lc%T)1&+VpYZQmb=CWJ4shZPffv=IY*i4Fw8&{J z8Td|~;6p0Qnr(c#;dZTbOL0x@_T2B@u@N-;;ZXfBBX;V=l>@BMFwSTi>^KCDsh5zz z@U6bVo8~z}B#4yQ2-1upy7LL=krpZ+Kx>Vk{BZi)%B^Pk&=jLf?F^Fk9kfE3$nOQ8 z?+kEv`TkoSkF-fino5sIKRM@zg;5Zqb7PKv%KXBO-DQf$1Z~rwL3|sL0MoPDtEyo^ zK66kTD#HrBCYcPBy{o{p4BU|AZ`(kYb>(dipeB?)?J~zM;>wh#_%XN0fPba{*?WNC zy-l<5%L`L|h#)oCMy~w#(|15~`e&hac9KCVb>?+2-dI#(qa?K~N8dJX6$6uVMB0=- zgac9W;PvlUi^6YmHTP}BpxQ4^HqsOc_{ucC+w7vNcUn2E$wLa{#9;GB0O^R@U@%M= zl3+}3;Kp=`Rl}<;hDy>!+uC-a^pM6DjVa6qe39GOn?EMGI&K&G+1Td`N9IeQKOL^r zas7_PQN-vY$`FHrQd2xIxx(D{3kxs+UZ#Z+f9Or_a;3Wb3}& z#_9JIH9x4$QLGjHlL2CT`B?Y4f6rpvT346#*xhW!`!Ecy96kaoiBiPBzW?TP$94}D zcT?mUF`)9g!aS>q;2=+@hc)r< zhpi%2@45EY$PY&>E5kG*NT#2k6XI7- zUdZ&0-wu06JNwMSieB+zAxUyBbeq`iR!tmjsO@(C@VGWmJs(A8%_JOwF2!6jYCN?s zLtSFFPnC@BgSdIY-{8%~uJzY%RNp@O*7)SqEk$HQJM-&_P$0M%@+U+C*YVmw_V~9L zki)i}7AKb>4FgxeZ?2yUujH)+bU5daeAUolEf@2F?`FhuM4qfwb~Tc|M>01+V*Au;W33>#V2O{-CauC}ByeGNybQk;055I(1SzmDe2`yQy3`bJO z?E5Ybrp>Bd>^#4`AbD!~P@n*tV;8rGU_a7p{!;9?(n8<9DBQK;TO;5IbT=w`Z4IcY z{v&8U&z&2xZh8@hd!{%A$6S6cvW+4=BXw^th-#VVWzvh;FQFOnShJN8!h0?E9F*}! zc}{Lc17Pi3f#LeJ!#XjQ%inV45MUvTaV4pb8lMr#)!M!=F?8u5EY_@=-Nz?+G@p*P z){cI(J6m9_YdqHls%*!rSU}XI+TN(_GNo;?1d3grROkFUO? z{3)a33G#nZq~1TbQCB}xm|kD~NlM`xS#5)UF`J4(+AlgSjKx*|WH|i@mafp~$9;zZ z-Vd=>3J3k~^mrnys8C-2BLM3h4gP&#+(DtDv%(l9HpHzFVu>V1i`v^@YCNZ_Egw=o zI5T?r0Mv@`5?JgD`TkMOyby6O3$WOocVvBYvS95i7)c7pGbdt>K&>-s{(9n-ZC%|3 zF>I>Y%~zLoH7l>Ci@Es|7IUUrbGfja5f3N;uy-R>iz%mwL2znD389_7J+5!zhX3-_(99=((c26)Uc z)r{%67rNEJpAU`9M&DbDCmrJpVDRX$)c5VA^Xy&sp!IL?7tfO~=^1uOj7Nz~Kxfe* z+rM|nd$FXw>fIcB;;KW}$RCl8vX56B+~DVzJ`bM;Qic))x+eEf0ILh3ll3s)K4V_WfOf^G zN?*8~`}uk8d+UKX#l5t6T^YD${EKE*bnT25Aku>$e|ekEN&(0?rpLa6qlP4GAciPs zBhY)l{65|$T9qE_L63Q0pmyuB=?Q4PUp5=du3;EyK7+b^A;;s+K(Dhv8x#NZnS7fT z<`#4zq-#4?GHi(H0Z>So5GTMiyjI>p?;_j(FZB>&iSwNLyw5;8{@pDIb=ThX=A zw7j=>eB8iUbFJf^IH!lFXdwX|9&b|-)ko;lkx#vy>Kk{KPOv%)p zIa9o)O%S-kmqIKTgzmiewkbqzhgfwLM;nBHXN_6N@WfNbGlIoP_l+-oK(okH8GL)h z?qc@?sZ^YI`Y8=06FcWXFXkf{b}K`+|F8P8dQU#!{H%J2`x>NVOf2s0`e@#9tR_Kn&9=5KmXCVU=%96_-8@~8)4 zpX7buWG(HWlZ+qLYYl)D4vM|Fq#d;$*cFN{aso>Xm@*A~TILzYBAE|8Za=cFvmYaKe}yBBzbyrlgvMaErCw6S=N6^r`0ah9xPZvU^396QQm|Z;xiw9 z_}Gi|6Fm$braz~n*)31zLoUSEQN*Z_bwn9hQG_|tVYKj5hPhh6s>;JA;0}NQYPsv! z?cVtJDTEYwQzPtpNfEo0Lz&4yr)|Y~`=FX#jNw3a>@18$~!;9n~#P7A7 zk^wg9zd_yjI{H*%zA$Xob8_tSSVF^*m4d@v5cfMPo$}d6bm;De-6eb7zl*27xBmo} z%`-1jeL`lwL}-#Rx+quRuEFd{0jibc-1~Mj7_dxJGfdk0!LmRWlRmGFP87n&On1hi z3IriKU0J+Ds-t5V!e{^Vl+r2^m}&I={qZt%vtb|=QMR-fm-<~hV6Mlrr<6uG-Fapr zE;v={ShisHx81vXNWJsgL^++)@R}blMYsMRGBItKHAyIi1+z@H-458gQ+fs4wm~N~ zQ~43Akb`kSnkh<1WShjlyY{6MQ*X>K|CZ<2o^*~6y-G9R`xQQ|X^uZ6sWl3GQZNyR z5PnWfH0}+H)`@KdH}Xf?S%4hAv;NJ-9O!EnV67AH3YTCj>?ata`|uS6o$62T4x&To zDYV>4k#t!|?RcnyWYl~aw`nJU7tP6#^c!UnpME2C>tv&C5xO^D#$26sdt&u3o6HFb*EY=*_B29)} zmMfWo5Qn%e#UO0ua-x0W3M8C9YKOs@c#Y5;_lu}9dO80=~K%P_m zqqFm}zTQq$qp`{M>5iHGA1h?C5Iw;R&@_Pk<-1q)B#pj!DpWIaU@>bO`UHQxt`6tcAPpHi| zUVQW*&Q~f6-Lt-q(^rwhSo6HQTxIWjq{2?HdKtLW#*JmYCi>q|tJ=-6?&nO$Z-k|vH7HcSh5=|Jj7_!G`+!$@YLO9R zs4OuP54_|gC*d=5Ys&hW6~$ecwMMEIfN5)wyoLng8^}U(P~3M5Sx}d$JhT2i3%Qc* z+fkNHt~AxjoYRMPPLveR20RI60;#g^kvb!I?R$?2#_8#@@}Lg6DC9;B-Pf>Q8im~D zxD%-y`#H#@BXJh{_&6e0q7D-Nq9+q&25OY666TKla9J8 z?$8x;E&SS@s8#`Jwu{YH1cpEa=FFMX6L=~WS&sw+LfwG4efd%%#0p3%0F>ktX919P z@zIZdRLv0pAOd(3FD!y2K%8OBm@z%#!pV<7<-vmozhd2t-M*4&c|HOM`NC0Vc5ov^rn9 zpjiR5MA7>0;{XeQ4-$&YGdT0iGo9yw2p}*BQ3(NBe6;*@Jed6*9UVR7w_@Y!N|~qCEx=r0d(O> zAj=S~K8-m*90EgppL*&k^Ru7*jIQU_J$v?SGQA|PV|Ad*6k7#U&x!S#Rlr+c7g^(o zZH(VCa0x(g0MMA407T#-(9+UkuD<%}$Zy0$gq8y>SSZTr|NF?D#PpY+LqvdD=QIL< z;GIkiin9igrRPiAMvej8HHrz~zZx-OL_uBwgvXlrX5G4VNb!x<+5hsJyLa#2K(~V5 z7W(;OeW*ns*ZTs9M$}J=^>HLOMi&nG{aDllAdh@Z03t91nS!teFnSQCBm#tpLZdZ_ zLsGLpg!D;Ud7tSY8U*BZ`V0#oX&{h}fz~>1n6E78893vNGYXD{{{qCulCB9tfkM7M ztPKeF&Ye4>Bp+CF7VjKCe*8=O_U(Jl6hHS;2y)7H+437xEDl{JUK}LBLtP4&c%&x3 z{NYuPeD45WAkv7>NEo{G(o1_<7DSM#Puhnp&Htp)eP8Pz8U*BZ=>nt)1Ol+4Xs&=z zD*#Kx0}#Q7&ky3GVM-bM)<(FS>X3Ut;<6FiFK~`}9c`W{! z>LE*6_raGL2p1r&0H9UIh`SKd`$i(Vh)-4;y7=Jh=UaW8_q%rew``$+f%N&QE=?QW z>TB+Si~PnEl2Yy?`{%W!BiR+H6}~NXieyay9N;biJ|i{&i(tZp36Wa>A;3e2V3bTE zO6vmnzRR~!J!C2Caqw9IAe^h8J_HZ|%IHgC2>`GHKzhpLBj7)P7{B=9i&09iHSuZF zffFZA{M~PU^PB&q+hF{Jr_Y?&b?mzZx5*f_>rlE6xxl4Hcv|m^E6DRCMoj=bkk$a8 z2>~8z5(2UcvYrVzX{6uR_a7Prly&?jIipV!zy-iP3bg`oKBNhQ_%ch7h>r+AT6{}% ze$Dq-m*ciqUU>zXU7YwvEJ+BY$(z|23S=fKFZdu zTg`LNJtv6|!h`tjDK1G1`nh@q>H@x>Q?|L>1DDItXQ&B43gkx=USpULAXwPe))u7{ zpLNz*MP@6^Uqr%@VTh8Z@4r+FAQUcZ0LO9-*wH|9@R8q-B?`TY3#7;KKzs>&kF2Q! z2M$ETc46jIa4!!Kf2}3{BW3xcs{l0tC;^&9fQNbd^yyKVIdf)DU$$&HNeEDgAmy|G zT}O(#vZUny2VTr2=BHwEj6 z^t6qx7d-_i*q4gyI`Ere8G-l+=pl0tEj@gE(anm09y0hq{Qdp%N1uYI2|$L(&p5Qi zmTT<8-bk~~($rTICr&g|rc8;(8X&m{LV)5M2TxO5b5okXeJ88#KM7xVAdXAp0%%Eu zcV!CFwcz(420-P|p+jc(?%ihBu3b@`gpgbX)7{$t(3TCB_@$56uAu-m0Voq@&z{|E zNpX!O#pi8l1VJDOL=sEg(WkV zLx|wyJ5zy00LuVkfY<@L7%7fG76SFl^E`dtYP%333gm7OcorX-=#&J7)*an+h|(j7 z2hx)$@Iu75rNh?SLHbRbHf@N#m>SB0ngH}O7B5~5>j33^OIoV;hybPEQk@D!02TsF zatH)S4^hB+YN_!ZdhsPdzPbdp;QTX4Od$GH-9}iOI>-$mi<~+#WasU1I@9*z>T7!<701ObUT)A?HB{YPfF<-{n&yBV~7@A$= z*WNk6q0Rr#bMhtm?ec9~I`Z``dGfd0^7d`F-PV!#ht<%ps0qL+gnRC}rxD$cjQovu zb)%MGM9>6)y5BhYmoH!5-rv8khU`%jfEsG3V$=knh8n6EH36uhhAPJY1I1$yzvXNw QyZ`_I07*qoM6N<$f+eme7XSbN diff --git a/pcsx2/gui/Resources/Logo.png b/pcsx2/gui/Resources/Logo.png deleted file mode 100644 index ac9ba41cddf469031742e6baa7e3214f91b306f0..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 3510 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 ai|;FT>EOYA;K_dsV5n=V^Ipp#_J08L+?mb* diff --git a/pcsx2/gui/Resources/NoIcon.png b/pcsx2/gui/Resources/NoIcon.png deleted file mode 100644 index 0c393a2abb4c2c4a2a1ce41e0eeaa414237e9ac1..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 14500 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 diff --git a/pcsx2/gui/Resources/bin2cpp.cmd b/pcsx2/gui/Resources/bin2cpp.cmd deleted file mode 100644 index 41b7663c1c..0000000000 --- a/pcsx2/gui/Resources/bin2cpp.cmd +++ /dev/null @@ -1,18 +0,0 @@ - -:: Probably self-explanatory: This batch file compiles a single souce image into a -:: CPP header file for use by pcsx2. -:: -:: bin2cpp.cmd SrcImage -:: -:: Parameters -:: SrcImage - Complete filename with extension. -:: - -@echo off - -SETLOCAL ENABLEEXTENSIONS - -cd "%~0\..\" -"..\..\..\tools\bin\bin2cpp.exe" %1 - -ENDLOCAL diff --git a/pcsx2/gui/Saveslots.cpp b/pcsx2/gui/Saveslots.cpp deleted file mode 100644 index d8c7d8941b..0000000000 --- a/pcsx2/gui/Saveslots.cpp +++ /dev/null @@ -1,155 +0,0 @@ -/* PCSX2 - PS2 Emulator for PCs - * Copyright (C) 2002-2010 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 . - */ - -#include "PrecompiledHeader.h" -#include "App.h" -#include "AppSaveStates.h" -#include "ConsoleLogger.h" -#include "MainFrame.h" - -#include "Common.h" - -#include "GS.h" -#include "Elfheader.h" -#include "Saveslots.h" - -// -------------------------------------------------------------------------------------- -// Saveslot Section -// -------------------------------------------------------------------------------------- - -static int StatesC = 0; - -#ifdef USE_NEW_SAVESLOTS_UI -std::array saveslot_cache = {0, 1, 2, 3, 4, 5, 6, 7, 8, 9}; -#endif - -// FIXME : Use of the IsSavingOrLoading flag is mostly a hack until we implement a -// complete thread to manage queuing savestate tasks, and zipping states to disk. --air -static std::atomic IsSavingOrLoading(false); - -class SysExecEvent_ClearSavingLoadingFlag : public SysExecEvent -{ -public: - wxString GetEventName() const { return L"ClearSavingLoadingFlag"; } - - virtual ~SysExecEvent_ClearSavingLoadingFlag() = default; - SysExecEvent_ClearSavingLoadingFlag() { } - SysExecEvent_ClearSavingLoadingFlag *Clone() const { return new SysExecEvent_ClearSavingLoadingFlag(); } - -protected: - void InvokeEvent() - { - IsSavingOrLoading = false; - UI_UpdateSysControls(); - } -}; - -void States_FreezeCurrentSlot() -{ - // FIXME : Use of the IsSavingOrLoading flag is mostly a hack until we implement a - // complete thread to manage queuing savestate tasks, and zipping states to disk. --air - if (!SysHasValidState()) - { - Console.WriteLn("Save state: Aborting (VM is not active)."); - return; - } - - if (wxGetApp().HasPendingSaves() || IsSavingOrLoading.exchange(true)) - { - Console.WriteLn("Load or save action is already pending."); - return; - } - - StateCopy_SaveToSlot(StatesC); - -#ifdef USE_NEW_SAVESLOTS_UI - saveslot_cache[StatesC].Used(); -#endif - - GetSysExecutorThread().PostIdleEvent(SysExecEvent_ClearSavingLoadingFlag()); - - States_updateLoadBackupMenuItem(); -} - -void _States_DefrostCurrentSlot(bool isFromBackup) -{ - if (!SysHasValidState()) - { - Console.WriteLn("Load state: Aborting (VM is not active)."); - return; - } - - if (IsSavingOrLoading.exchange(true)) - { - Console.WriteLn("Load or save action is already pending."); - return; - } - - StateCopy_LoadFromSlot(StatesC, isFromBackup); - - GetSysExecutorThread().PostIdleEvent(SysExecEvent_ClearSavingLoadingFlag()); - - States_updateLoadBackupMenuItem(); -} - -void States_DefrostCurrentSlot() -{ - _States_DefrostCurrentSlot(false); -} - -void States_DefrostCurrentSlotBackup() -{ - _States_DefrostCurrentSlot(true); -} - -void States_updateLoadBackupMenuItem() -{ - wxString file(StringUtil::UTF8StringToWxString(SaveStateBase::GetSavestateFolder(StatesC) + ".backup")); - - sMainFrame.EnableMenuItem(MenuId_State_LoadBackup, wxFileExists(file)); - sMainFrame.SetMenuItemLabel(MenuId_State_LoadBackup, wxsFormat(L"%s %d", _("Backup"), StatesC)); -} - -static void OnSlotChanged() -{ - OSDlog(Color_StrongGreen, true, " > Selected savestate slot %d", StatesC); - States_updateLoadBackupMenuItem(); -} - -int States_GetCurrentSlot() -{ - return StatesC; -} - -void States_SetCurrentSlot(int slot_num) -{ - StatesC = std::min(std::max(slot_num, 0), StateSlotsCount); - for (Saveslot& slot : saveslot_cache) - { - sMainFrame.CheckMenuItem(slot.load_item_id, slot.slot_num == slot_num); - sMainFrame.CheckMenuItem(slot.save_item_id, slot.slot_num == slot_num); - } - OnSlotChanged(); -} - -void States_CycleSlotForward() -{ - States_SetCurrentSlot((StatesC + 1) % StateSlotsCount); -} - -void States_CycleSlotBackward() -{ - States_SetCurrentSlot((StatesC + StateSlotsCount - 1) % StateSlotsCount); -} diff --git a/pcsx2/gui/Saveslots.h b/pcsx2/gui/Saveslots.h deleted file mode 100644 index 8a848b4cf1..0000000000 --- a/pcsx2/gui/Saveslots.h +++ /dev/null @@ -1,137 +0,0 @@ -/* PCSX2 - PS2 Emulator for PCs - * Copyright (C) 2002-2018 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 . - */ - - -#pragma once - -#include "System.h" -#include "Elfheader.h" -#include "App.h" -#include "common/StringUtil.h" -#include - -// Uncomment to turn on the new saveslot UI. -#define USE_NEW_SAVESLOTS_UI - -// Uncomment to turn on the extra UI updates *without* the UI. -//#define USE_SAVESLOT_UI_UPDATES - -#ifdef USE_NEW_SAVESLOTS_UI -// Should always be enabled if the new saveslots are. -#define USE_SAVESLOT_UI_UPDATES -#endif - -#ifdef USE_SAVESLOT_UI_UPDATES -// Uncomment to add saveslot logging and comment to turn off. -//#define SAVESLOT_LOGS -#endif - -extern std::string DiscSerial; -static const int StateSlotsCount = 10; - -class Saveslot -{ -public: - int slot_num; - bool empty; - wxDateTime updated; - u32 crc; - wxString serialName; - bool menu_update, invalid_cache; - int load_item_id, save_item_id; - - Saveslot() = delete; - - Saveslot(int i) - { - slot_num = i; - empty = true; - updated = wxInvalidDateTime; - crc = 0; - serialName = L""; - menu_update = false; - invalid_cache = true; - load_item_id = MenuId_State_Load01 + i + 1; - save_item_id = MenuId_State_Save01 + i + 1; - } - - bool isUsed() - { - return wxFileExists(StringUtil::UTF8StringToWxString(SaveStateBase::GetSavestateFolder(slot_num, false))); - } - - wxDateTime GetTimestamp() - { - if (!isUsed()) return wxInvalidDateTime; - - return wxDateTime(wxFileModificationTime(StringUtil::UTF8StringToWxString(SaveStateBase::GetSavestateFolder(slot_num, false)))); - } - - void UpdateCache() - { - empty = !isUsed(); - updated = GetTimestamp(); - crc = ElfCRC; - serialName = StringUtil::UTF8StringToWxString(DiscSerial); - invalid_cache = false; - } - - wxString SlotName() - { - if (empty) return wxsFormat(_("Slot %d - Empty"), slot_num); - - if (updated.IsValid()) return wxsFormat(_("Slot %d - %s %s"), slot_num, updated.FormatDate(), updated.FormatTime()); - - return wxsFormat(_("Slot %d - Unknown Time"), slot_num); - } - - void ConsoleDump() - { - Console.WriteLn("Slot %i information:", slot_num); - Console.WriteLn("Internal CRC = %i; Current CRC = %i.", crc, ElfCRC); - if (empty) - Console.WriteLn("Slot cache says it is empty."); - else - Console.WriteLn("Slot cache says it is used."); - - if (updated != wxInvalidDateTime) - Console.WriteLn(wxsFormat(_("Write time is %s %s."), updated.FormatDate(), updated.FormatTime()).ToStdString()); - - //if (isUsed()) - // Console.WriteLn(wxsFormat(_("The disk has a file on it dated %s %s."), GetTimestamp().FormatDate(), GetTimestamp().FormatTime())); - } - - void Used() - { - // Update the saveslot cache with the new saveslot, and give it the current timestamp, - // Because we aren't going to be able to get the real timestamp from disk right now. - empty = false; - updated = wxDateTime::Now(); - crc = ElfCRC; - - // Update the slot next time we run through the UI update. - menu_update = true; - } -}; - -extern std::array saveslot_cache; -extern void States_DefrostCurrentSlotBackup(); -extern void States_DefrostCurrentSlot(); -extern void States_FreezeCurrentSlot(); -extern void States_CycleSlotForward(); -extern void States_CycleSlotBackward(); -extern void States_SetCurrentSlot(int slot_num); -extern int States_GetCurrentSlot(); -extern void States_updateLoadBackupMenuItem(); diff --git a/pcsx2/gui/StringHelpers.cpp b/pcsx2/gui/StringHelpers.cpp deleted file mode 100644 index 2a5dfde57c..0000000000 --- a/pcsx2/gui/StringHelpers.cpp +++ /dev/null @@ -1,246 +0,0 @@ -/* PCSX2 - PS2 Emulator for PCs - * Copyright (C) 2002-2010 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 . - */ - -#include // for wxPoint/wxRect stuff -#include "common/Exceptions.h" -#include "common/StringUtil.h" -#include "common/Pcsx2Defs.h" -#include "PersistentThread.h" -#include "StringHelpers.h" - -__fi wxString fromUTF8(const char* src) -{ - // IMPORTANT: We cannot use wxString::FromUTF8 because it *stupidly* relies on a C++ global instance of - // wxMBConvUTF8(). C++ initializes and destroys these globals at random, so any object constructor or - // destructor that attempts to do logging may crash the app (either during startup or during exit) unless - // we use a LOCAL instance of wxMBConvUTF8(). --air - - // Performance? No worries. wxMBConvUTF8() is virtually free. Initializing a stack copy of the class - // is just as efficient as passing a pointer to a pre-instanced global. (which makes me wonder wh wxWidgets - // uses the stupid globals in the first place!) --air - - return wxString(src, wxMBConvUTF8()); -} - -__fi wxString fromUTF8(const std::string& str) -{ - return wxString(str.data(), wxMBConvUTF8(), str.size()); -} - -__fi wxString fromAscii(const char* src) -{ - return wxString::FromAscii(src); -} - -// Splits a string into parts and adds the parts into the given SafeList. -// This list is not cleared, so concatenating many splits into a single large list is -// the 'default' behavior, unless you manually clear the SafeList prior to subsequent calls. -// -// Note: wxWidgets 2.9 / 3.0 has a wxSplit function, but we're using 2.8 so I had to make -// my own. -void SplitString(wxArrayString& dest, const wxString& src, const wxString& delims, wxStringTokenizerMode mode) -{ - wxStringTokenizer parts(src, delims, mode); - while (parts.HasMoreTokens()) - dest.Add(parts.GetNextToken()); -} - -// Joins a list of strings into one larger string, using the given string concatenation -// character as a separator. If you want to be able to split the string later then the -// concatenation string needs to be a single character. -// -// Note: wxWidgets 2.9 / 3.0 has a wxJoin function, but we're using 2.8 so I had to make -// my own. -wxString JoinString(const wxArrayString& src, const wxString& separator) -{ - wxString dest; - for (int i = 0, len = src.GetCount(); i < len; ++i) - { - if (src[i].IsEmpty()) - continue; - if (!dest.IsEmpty()) - dest += separator; - dest += src[i]; - } - return dest; -} - -wxString JoinString(const wxChar** src, const wxString& separator) -{ - wxString dest; - while (*src != NULL) - { - if (*src[0] == 0) - continue; - - if (!dest.IsEmpty()) - dest += separator; - dest += *src; - ++src; - } - return dest; -} - - -// Attempts to parse and return a value for the given template type, and throws a ParseError -// exception if the parse fails. The template type can be anything that is supported/ -// implemented via one of the TryParse() method overloads. -// -// This, so far, include types such as wxPoint, wxRect, and wxSize. -// -template -T Parse(const wxString& src, const wxString& separators = L",") -{ - T retval; - if (!TryParse(retval, src, separators)) - throw Exception::ParseError(StringUtil::StdStringFromFormat("Parse failure on call to %s: %ls", __WXFUNCTION__, WX_STR(src))); - return retval; -} - - -// -------------------------------------------------------------------------------------- -// ToString helpers for wxString! -// -------------------------------------------------------------------------------------- - -// Converts a wxPoint into a comma-delimited string! -wxString ToString(const wxPoint& src, const wxString& separator) -{ - return wxString() << src.x << separator << src.y; -} - -wxString ToString(const wxSize& src, const wxString& separator) -{ - return wxString() << src.GetWidth() << separator << src.GetHeight(); -} - -// Converts a wxRect into a comma-delimited string! -// Example: 32,64,128,5 -wxString ToString(const wxRect& src, const wxString& separator) -{ - return ToString(src.GetLeftTop(), separator) << separator << ToString(src.GetSize(), separator); -} - -// -------------------------------------------------------------------------------------- -// Parse helpers for wxString! -// -------------------------------------------------------------------------------------- - -bool TryParse(wxPoint& dest, wxStringTokenizer& parts) -{ - long result[2]; - - if (!parts.HasMoreTokens() || !parts.GetNextToken().ToLong(&result[0])) - return false; - if (!parts.HasMoreTokens() || !parts.GetNextToken().ToLong(&result[1])) - return false; - dest.x = result[0]; - dest.y = result[1]; - - return true; -} - -bool TryParse(wxSize& dest, wxStringTokenizer& parts) -{ - long result[2]; - - if (!parts.HasMoreTokens() || !parts.GetNextToken().ToLong(&result[0])) - return false; - if (!parts.HasMoreTokens() || !parts.GetNextToken().ToLong(&result[1])) - return false; - dest.SetWidth(result[0]); - dest.SetHeight(result[1]); - - return true; -} - -// Tries to parse the given string into a wxPoint value at 'dest.' If the parse fails, the -// method aborts and returns false. -bool TryParse(wxPoint& dest, const wxString& src, const wxPoint& defval, const wxString& separators) -{ - dest = defval; - wxStringTokenizer parts(src, separators); - return TryParse(dest, parts); -} - -bool TryParse(wxSize& dest, const wxString& src, const wxSize& defval, const wxString& separators) -{ - dest = defval; - wxStringTokenizer parts(src, separators); - return TryParse(dest, parts); -} - -bool TryParse(wxRect& dest, const wxString& src, const wxRect& defval, const wxString& separators) -{ - dest = defval; - - wxStringTokenizer parts(src, separators); - - wxPoint point; - wxSize size; - - if (!TryParse(point, parts)) - return false; - if (!TryParse(size, parts)) - return false; - - dest = wxRect(point, size); - return true; -} - -// Performs a cross-platform puts operation, which adds CRs to naked LFs on Win32 platforms, -// so that Notepad won't throw a fit and Rama can read the logs again! On Unix and Mac platforms, -// the input string is written unmodified. -// -// PCSX2 generally uses Unix-style newlines -- LF (\n) only -- hence there's no need to strip CRs -// from incoming data. Mac platforms may need an implementation of their own that converts -// newlines to CRs...? -// -void px_fputs(FILE* fp, const char* src) -{ - if (fp == NULL) - return; - -#ifdef _WIN32 - // Windows needs CR's partnered with all newlines, or else notepad.exe can't view - // the stupid logfile. Best way is to write one char at a time.. >_< - - const char* curchar = src; - bool prevcr = false; - while (*curchar != 0) - { - if (*curchar == '\r') - { - prevcr = true; - } - else - { - // Only write a CR/LF pair if the current LF is not prefixed nor - // post-fixed by a CR. - if (*curchar == '\n' && !prevcr && (*(curchar + 1) != '\r')) - fputs("\r\n", fp); - else - fputc(*curchar, fp); - - prevcr = false; - } - ++curchar; - } - -#else - // Linux is happy with plain old LFs. Not sure about Macs... does OSX still - // go by the old school Mac style of using Crs only? - - fputs(src, fp); // fputs does not do automatic newlines, so it's ok! -#endif -} diff --git a/pcsx2/gui/StringHelpers.h b/pcsx2/gui/StringHelpers.h deleted file mode 100644 index 274b230d43..0000000000 --- a/pcsx2/gui/StringHelpers.h +++ /dev/null @@ -1,253 +0,0 @@ -/* PCSX2 - PS2 Emulator for PCs - * Copyright (C) 2002-2010 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 . - */ - -#pragma once - -#include -#include -#include -#include -#include "common/Pcsx2Defs.h" -#include "common/SafeArray.h" -#include "common/AlignedMalloc.h" - -// This should prove useful.... -#define wxsFormat wxString::Format - -#define WX_STR(str) ((str).wc_str()) - -extern void px_fputs(FILE* fp, const char* src); - -// wxWidgets lacks one of its own... -extern const wxRect wxDefaultRect; - -extern void SplitString(wxArrayString& dest, const wxString& src, const wxString& delims, wxStringTokenizerMode mode = wxTOKEN_RET_EMPTY_ALL); -extern wxString JoinString(const wxArrayString& src, const wxString& separator); -extern wxString JoinString(const wxChar** src, const wxString& separator); - -extern wxString ToString(const wxPoint& src, const wxString& separator = L","); -extern wxString ToString(const wxSize& src, const wxString& separator = L","); -extern wxString ToString(const wxRect& src, const wxString& separator = L","); - -extern bool TryParse(wxPoint& dest, const wxStringTokenizer& parts); -extern bool TryParse(wxSize& dest, const wxStringTokenizer& parts); - -extern bool TryParse(wxPoint& dest, const wxString& src, const wxPoint& defval = wxDefaultPosition, const wxString& separators = L","); -extern bool TryParse(wxSize& dest, const wxString& src, const wxSize& defval = wxDefaultSize, const wxString& separators = L","); -extern bool TryParse(wxRect& dest, const wxString& src, const wxRect& defval = wxDefaultRect, const wxString& separators = L","); - - -// ====================================================================================== -// FastFormatAscii / FastFormatUnicode (overview!) -// ====================================================================================== -// Fast formatting of ASCII or Unicode text. These classes uses a series of thread-local -// format buffers that are allocated only once and grown to accommodate string formatting -// needs. Because the buffers are thread-local, no thread synch objects are required in -// order to format strings, allowing for multi-threaded string formatting operations to be -// performed with maximum efficiency. This class also reduces the overhead typically required -// to allocate string buffers off the heap. -// -// Drawbacks: -// * Some overhead is added to the creation and destruction of threads, however since thread -// construction is a typically slow process, and often avoided to begin with, this should -// be a sound trade-off. -// -// Notes: -// * conversion to wxString requires a heap allocation. -// * FastFormatUnicode can accept either UTF8 or UTF16/32 (wchar_t) input, but FastFormatAscii -// accepts Ascii/UTF8 only. -// - -typedef AlignedBuffer CharBufferType; -// -------------------------------------------------------------------------------------- -// FastFormatAscii -// -------------------------------------------------------------------------------------- - -class FastFormatAscii -{ -protected: - CharBufferType m_dest; - -public: - FastFormatAscii(); - ~FastFormatAscii() = default; - FastFormatAscii& Write(const char* fmt, ...); - FastFormatAscii& WriteV(const char* fmt, va_list argptr); - - void Clear(); - bool IsEmpty() const; - - const char* c_str() const { return m_dest.GetPtr(); } - operator const char*() const { return m_dest.GetPtr(); } - - const wxString GetString() const; - //operator wxString() const; - - FastFormatAscii& operator+=(const wxString& s) - { - Write("%s", WX_STR(s)); - return *this; - } - - FastFormatAscii& operator+=(const wxChar* psz) - { - Write("%ls", psz); - return *this; - } - - FastFormatAscii& operator+=(const char* psz) - { - Write("%s", psz); - return *this; - } -}; - -// -------------------------------------------------------------------------------------- -// FastFormatUnicode -// -------------------------------------------------------------------------------------- -class FastFormatUnicode -{ -protected: - CharBufferType m_dest; - uint m_Length; - -public: - FastFormatUnicode(); - ~FastFormatUnicode() = default; - - FastFormatUnicode& Write(const char* fmt, ...); - FastFormatUnicode& Write(const wxChar* fmt, ...); - FastFormatUnicode& Write(const wxString fmt, ...); - FastFormatUnicode& WriteV(const char* fmt, va_list argptr); - FastFormatUnicode& WriteV(const wxChar* fmt, va_list argptr); - - void Clear(); - bool IsEmpty() const; - uint Length() const { return m_Length; } - - FastFormatUnicode& ToUpper(); - FastFormatUnicode& ToLower(); - - const wxChar* c_str() const { return (const wxChar*)m_dest.GetPtr(); } - operator const wxChar*() const { return (const wxChar*)m_dest.GetPtr(); } - operator wxString() const { return (const wxChar*)m_dest.GetPtr(); } - - FastFormatUnicode& operator+=(const wxString& s) - { - Write(L"%s", WX_STR(s)); - return *this; - } - - FastFormatUnicode& operator+=(const wxChar* psz) - { - Write(L"%s", psz); - return *this; - } - - FastFormatUnicode& operator+=(const char* psz); - - wxScopedCharBuffer ToUTF8() const { return wxString(m_dest.GetPtr()).ToUTF8(); } - std::string ToStdString() const { return wxString(m_dest.GetPtr()).ToStdString(); } -}; - -#define pxsFmt FastFormatUnicode().Write -#define pxsFmtV FastFormatUnicode().WriteV -#define pxsPtr(ptr) pxsFmt("0x%016" PRIXPTR, (ptr)).c_str() - -extern wxString& operator+=(wxString& str1, const FastFormatUnicode& str2); -extern wxString operator+(const wxString& str1, const FastFormatUnicode& str2); -extern wxString operator+(const wxChar* str1, const FastFormatUnicode& str2); -extern wxString operator+(const FastFormatUnicode& str1, const wxString& str2); -extern wxString operator+(const FastFormatUnicode& str1, const wxChar* str2); - -extern wxString fromUTF8(const std::string& str); -extern wxString fromUTF8(const char* src); -extern wxString fromAscii(const char* src); - - -// -------------------------------------------------------------------------------------- -// _(x) / _t(x) / _d(x) / pxL(x) / pxLt(x) [macros] -// -------------------------------------------------------------------------------------- -// Define pxWex's own i18n helpers. These override the wxWidgets helpers and provide -// additional functionality. Define them FIRST THING, to make sure that wx's own gettext -// macros aren't in place. -// -// _ is for standard translations -// _t is for tertiary low priority translations -// _d is for debug/devel build translations - -#define WXINTL_NO_GETTEXT_MACRO - -#ifndef _ -#define _(s) pxGetTranslation(_T(s)) -#endif - -#ifndef _t -#define _t(s) pxGetTranslation(_T(s)) -#endif - -#ifndef _d -#define _d(s) pxGetTranslation(_T(s)) -#endif - -// pxL / pxLt / pxDt -- macros provided for tagging translation strings, without actually running -// them through the translator (which the _() does automatically, and sometimes we don't -// want that). This is a shorthand replacement for wxTRANSLATE. pxL is a standard translation -// moniker. pxLt is for tertiary strings that have a very low translation priority. pxDt is for -// debug/devel specific translations. -// -#ifndef pxL -#define pxL(a) wxT(a) -#endif - -#ifndef pxLt -#define pxLt(a) wxT(a) -#endif - -#ifndef pxDt -#define pxDt(a) wxT(a) -#endif - -// -------------------------------------------------------------------------------------- -// pxE(msg) and pxEt(msg) [macros] => now same as _/_t/_d -// -------------------------------------------------------------------------------------- -#define pxE(english) pxExpandMsg((english)) - -// For use with tertiary translations (low priority). -#define pxEt(english) pxExpandMsg((english)) - -// For use with Dev/debug build translations (low priority). -#define pxE_dev(english) pxExpandMsg((english)) - - -extern const wxChar* pxExpandMsg(const wxChar* message); -extern const wxChar* pxGetTranslation(const wxChar* message); -extern bool pxIsEnglish(int id); - -namespace StringUtil -{ - /// Converts a wxString to a UTF-8 std::string. - static inline std::string wxStringToUTF8String(const wxString& str) - { - const wxScopedCharBuffer buf(str.ToUTF8()); - return std::string(buf.data(), buf.length()); - } - - /// Converts a UTF-8 std::string to a wxString. - static inline wxString UTF8StringToWxString(const std::string_view& str) - { - return wxString::FromUTF8(str.data(), str.length()); - } -} diff --git a/pcsx2/gui/SysCoreThread.cpp b/pcsx2/gui/SysCoreThread.cpp deleted file mode 100644 index 4a62792fd5..0000000000 --- a/pcsx2/gui/SysCoreThread.cpp +++ /dev/null @@ -1,390 +0,0 @@ -/* PCSX2 - PS2 Emulator for PCs - * Copyright (C) 2002-2010 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 . - */ - -#include "PrecompiledHeader.h" -#include "Common.h" -#include "gui/App.h" -#include "IopBios.h" -#include "R5900.h" - -#include "common/Timer.h" -#include "common/WindowInfo.h" -extern WindowInfo g_gs_window_info; - -#include "Counters.h" -#include "GS.h" -#include "Elfheader.h" -#include "Patch.h" -#include "SysThreads.h" -#include "MTVU.h" -#include "PINE.h" -#include "FW.h" -#include "SPU2/spu2.h" -#include "DEV9/DEV9.h" -#include "USB/USBNull.h" -#include "MemoryCardFile.h" -#include "PAD/Gamepad.h" -#include "PerformanceMetrics.h" - -#include "DebugTools/MIPSAnalyst.h" -#include "DebugTools/SymbolMap.h" - -#include "common/PageFaultSource.h" -#include "common/Threading.h" -#include "IopBios.h" - -#ifdef __WXMSW__ -#include -#endif - -#include "common/emitter/x86_intrin.h" - -bool g_CDVDReset = false; - -namespace PINESettings -{ - unsigned int slot = PINE_DEFAULT_SLOT; -}; - -// -------------------------------------------------------------------------------------- -// SysCoreThread *External Thread* Implementations -// (Called from outside the context of this thread) -// -------------------------------------------------------------------------------------- - -SysCoreThread::SysCoreThread() - : m_hasActiveMachine(false) -{ - m_name = L"EE Core"; - m_resetRecompilers = true; - m_resetProfilers = true; - m_resetVsyncTimers = true; - m_resetVirtualMachine = true; -} - -SysCoreThread::~SysCoreThread() -{ - try - { - SysCoreThread::Cancel(); - } - DESTRUCTOR_CATCHALL -} - -void SysCoreThread::Cancel(bool isBlocking) -{ - m_hasActiveMachine = false; - R3000A::ioman::reset(); - _parent::Cancel(); -} - -bool SysCoreThread::Cancel(const wxTimeSpan& span) -{ - m_hasActiveMachine = false; - R3000A::ioman::reset(); - return _parent::Cancel(span); -} - -void SysCoreThread::OnStart() -{ - _parent::OnStart(); -} - -void SysCoreThread::OnSuspendInThread() -{ - // We deliberately don't tear down GS here, because the state isn't saved. - // Which means when it reopens, you'll lose everything in VRAM. Anything - // which needs GS to be torn down should manually save its state. - TearDownSystems(static_cast(-1 & ~System_GS)); // All systems -} - -void SysCoreThread::Start() -{ - SPU2init(); - PADinit(); - DEV9init(); - USBinit(); - _parent::Start(); -} - -// Resumes the core execution state, or does nothing is the core is already running. If -// settings were changed, resets will be performed as needed and emulation state resumed from -// memory savestates. -// -// Exceptions (can occur on first call only): -// ThreadCreationError - Insufficient system resources to create thread. -// -void SysCoreThread::OnResumeReady() -{ - if (m_resetVirtualMachine) - m_hasActiveMachine = false; - - if (!m_hasActiveMachine) - m_resetRecompilers = true; -} - -// This function *will* reset the emulator in order to allow the specified elf file to -// take effect. This is because it really doesn't make sense to change the elf file outside -// the context of a reset/restart. -void SysCoreThread::SetElfOverride(const wxString& elf) -{ - //pxAssertDev( !m_hasValidMachine, "Thread synchronization error while assigning ELF override." ); - m_elf_override = elf; - - - Hle_SetElfPath(elf.ToUTF8()); -} - -// Performs a quicker reset that does not deallocate memory associated with PS2 virtual machines -// or cpu providers (recompilers). -void SysCoreThread::ResetQuick() -{ - Suspend(); - - m_resetVirtualMachine = true; - m_hasActiveMachine = false; - R3000A::ioman::reset(); -} - -void SysCoreThread::Reset() -{ - ResetQuick(); - SysClearExecutionCache(); - sApp.PostAppMethod(&Pcsx2App::leaveDebugMode); - g_FrameCount = 0; -} - - -// Applies a full suite of new settings, which will automatically facilitate the necessary -// resets of the core and components. The scope of resetting -// is determined by comparing the current settings against the new settings, so that only -// real differences are applied. -void SysCoreThread::ApplySettings(const Pcsx2Config& src) -{ - if (src == EmuConfig) - return; - - if (!pxAssertDev(IsPaused() | IsSelf(), "CoreThread is not paused; settings cannot be applied.")) - return; - - m_resetRecompilers = (src.Cpu != EmuConfig.Cpu) || (src.Gamefixes != EmuConfig.Gamefixes) || (src.Speedhacks != EmuConfig.Speedhacks); - m_resetProfilers = (src.Profiler != EmuConfig.Profiler); - m_resetVsyncTimers = (src.GS != EmuConfig.GS); - - const bool gs_settings_changed = !src.GS.OptionsAreEqual(EmuConfig.GS); - - Pcsx2Config old_config; - old_config.CopyConfig(EmuConfig); - EmuConfig.CopyConfig(src); - - // handle DEV9 setting changes - DEV9CheckChanges(old_config); - - // handle GS setting changes - if (GetMTGS().IsOpen() && gs_settings_changed) - { - Console.WriteLn("Applying GS settings..."); - GetMTGS().ApplySettings(); - GetMTGS().UpdateVSyncMode(); - } -} - -// -------------------------------------------------------------------------------------- -// SysCoreThread *Worker* Implementations -// (Called from the context of this thread only) -// -------------------------------------------------------------------------------------- -bool SysCoreThread::HasPendingStateChangeRequest() const -{ - return !m_hasActiveMachine || _parent::HasPendingStateChangeRequest(); -} - -void SysCoreThread::_reset_stuff_as_needed() -{ - // Note that resetting recompilers along with the virtual machine is only really needed - // because of changes to the TLB. We don't actually support the TLB, however, so rec - // resets aren't in fact *needed* ... yet. But might as well, no harm. --air - - if (m_resetVirtualMachine || m_resetRecompilers || m_resetProfilers) - { - SysClearExecutionCache(); - memBindConditionalHandlers(); - SetCPUState(EmuConfig.Cpu.sseMXCSR, EmuConfig.Cpu.sseVUMXCSR); - - m_resetRecompilers = false; - m_resetProfilers = false; - } - - if (m_resetVirtualMachine) - { - DoCpuReset(); - - m_resetVirtualMachine = false; - m_resetVsyncTimers = false; - - ForgetLoadedPatches(); - } - - if (m_resetVsyncTimers) - { - GetMTGS().UpdateVSyncMode(); - UpdateVSyncRate(); - frameLimitReset(); - - m_resetVsyncTimers = false; - } -} - -void SysCoreThread::DoCpuReset() -{ - AffinityAssert_AllowFromSelf(pxDiagSpot); - cpuReset(); -} - -// This is called from the PS2 VM at the start of every vsync (either 59.94 or 50 hz by PS2 -// clock scale, which does not correlate to the actual host machine vsync). -// -// Default tasks: Updates PADs and applies vsync patches. Derived classes can override this -// to change either PAD and/or Patching behaviors. -// -// [TODO]: Should probably also handle profiling and debugging updates, once those are -// re-implemented. -// -void SysCoreThread::VsyncInThread() -{ - ApplyLoadedPatches(PPT_CONTINUOUSLY); - ApplyLoadedPatches(PPT_COMBINED_0_1); -} - -void SysCoreThread::GameStartingInThread() -{ - GetMTGS().SendGameCRC(ElfCRC); - - MIPSAnalyst::ScanForFunctions(R5900SymbolMap, ElfTextRange.first, ElfTextRange.first + ElfTextRange.second, true); - R5900SymbolMap.UpdateActiveSymbols(); - R3000SymbolMap.UpdateActiveSymbols(); - sApp.PostAppMethod(&Pcsx2App::resetDebugger); - - ApplyLoadedPatches(PPT_ONCE_ON_LOAD); - ApplyLoadedPatches(PPT_COMBINED_0_1); -#ifdef USE_SAVESLOT_UI_UPDATES - UI_UpdateSysControls(); -#endif - if (EmuConfig.EnablePINE && m_PineState == OFF) - { - m_PineState = ON; - m_pineServer = std::make_unique(this, PINESettings::slot); - } - if (m_PineState == ON && m_pineServer->m_end) - m_pineServer->Start(); -} - -bool SysCoreThread::StateCheckInThread() -{ - return _parent::StateCheckInThread() && (_reset_stuff_as_needed(), true); -} - -// Runs CPU cycles indefinitely, until the user or another thread requests execution to break. -// Rationale: This very short function allows an override point and solves an SEH -// "exception-type boundary" problem (can't mix SEH and C++ exceptions in the same function). -void SysCoreThread::DoCpuExecute() -{ - m_hasActiveMachine = true; - UI_EnableSysActions(); - Cpu->Execute(); -} - -void SysCoreThread::ExecuteTaskInThread() -{ - Threading::EnableHiresScheduler(); // Note that *something* in SPU2 and GS also set the timer resolution to 1ms. - m_sem_event.WaitForWork(); - - m_mxcsr_saved.bitmask = _mm_getcsr(); - - PCSX2_PAGEFAULT_PROTECT - { - while (true) - { - StateCheckInThread(); - DoCpuExecute(); - } - } - PCSX2_PAGEFAULT_EXCEPT; -} - -void SysCoreThread::TearDownSystems(SystemsMask systemsToTearDown) -{ - if (systemsToTearDown & System_DEV9) DEV9close(); - if (systemsToTearDown & System_USB) USBclose(); - if (systemsToTearDown & System_CDVD) DoCDVDclose(); - if (systemsToTearDown & System_FW) FWclose(); - if (systemsToTearDown & System_PAD) PADclose(); - if (systemsToTearDown & System_SPU2) SPU2close(); - if (systemsToTearDown & System_MCD) FileMcd_EmuClose(); - - if (GetMTGS().IsOpen()) - GetMTGS().WaitGS(); - - PerformanceMetrics::SetCPUThread(Threading::ThreadHandle()); -} - -void SysCoreThread::OnResumeInThread(SystemsMask systemsToReinstate) -{ - PerformanceMetrics::SetCPUThread(Threading::ThreadHandle::GetForCallingThread()); - PerformanceMetrics::Reset(); - - // if GS is open, wait for it to finish (could be applying settings) - if (GetMTGS().IsOpen()) - GetMTGS().WaitGS(); - else - GetMTGS().WaitForOpen(); - - if (systemsToReinstate & System_DEV9) DEV9open(); - if (systemsToReinstate & System_USB) USBopen(g_gs_window_info); - if (systemsToReinstate & System_FW) FWopen(); - if (systemsToReinstate & System_SPU2) SPU2open(); - if (systemsToReinstate & System_PAD) PADopen(g_gs_window_info); - if (systemsToReinstate & System_MCD) FileMcd_EmuOpen(); -} - - -// Invoked by the pthread_exit or pthread_cancel. -void SysCoreThread::OnCleanupInThread() -{ - m_ExecMode = ExecMode_Closing; - - m_hasActiveMachine = false; - m_resetVirtualMachine = true; - - R3000A::ioman::reset(); - vu1Thread.Close(); - USBclose(); - SPU2close(); - PADclose(); - DEV9close(); - DoCDVDclose(); - FWclose(); - FileMcd_EmuClose(); - GetMTGS().WaitForClose(); - USBshutdown(); - SPU2shutdown(); - PADshutdown(); - DEV9shutdown(); - GetMTGS().ShutdownThread(); - - _mm_setcsr(m_mxcsr_saved.bitmask); - Threading::DisableHiresScheduler(); - _parent::OnCleanupInThread(); - - m_ExecMode = ExecMode_NoThreadYet; -} diff --git a/pcsx2/gui/SysState.cpp b/pcsx2/gui/SysState.cpp deleted file mode 100644 index ccb33980fb..0000000000 --- a/pcsx2/gui/SysState.cpp +++ /dev/null @@ -1,176 +0,0 @@ -/* PCSX2 - PS2 Emulator for PCs - * Copyright (C) 2002-2010 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 . - */ - -#include "PrecompiledHeader.h" -#include "MemoryTypes.h" -#include "App.h" - -#include "SysThreads.h" -#include "SaveState.h" -#include "VUmicro.h" - -#include "common/StringUtil.h" -#include "SPU2/spu2.h" -#include "USB/USBNull.h" -#include "PAD/Gamepad.h" - -#include "ConsoleLogger.h" - -#include -#include - -#include "Patch.h" - -// Required for savestate folder creation -#include "CDVD/CDVD.h" -#include "ps2/BiosTools.h" -#include "Elfheader.h" - -class SysExecEvent_SaveState : public SysExecEvent -{ -protected: - wxString m_filename; - -public: - wxString GetEventName() const { return L"VM_Download"; } - - SysExecEvent_SaveState(const wxString& filename) : m_filename(filename) {} - virtual ~SysExecEvent_SaveState() = default; - - SysExecEvent_SaveState* Clone() const { return new SysExecEvent_SaveState(*this); } - - bool IsCriticalEvent() const { return true; } - bool AllowCancelOnExit() const { return false; } - -protected: - void InvokeEvent() - { - ScopedCoreThreadPause paused_core; - std::unique_ptr elist = SaveState_DownloadState(); - UI_EnableStateActions(); - paused_core.AllowResume(); - - std::thread kickoff(&SysExecEvent_SaveState::ZipThreadProc, - std::move(elist), StringUtil::wxStringToUTF8String(m_filename)); - kickoff.detach(); - } - - static void ZipThreadProc(std::unique_ptr elist, std::string filename) - { - wxGetApp().StartPendingSave(); - if (SaveState_ZipToDisk(std::move(elist), nullptr, filename.c_str())) - Console.WriteLn("(gzipThread) Data saved to disk without error."); - else - Console.Error("Failed to zip state to '%s'", filename.c_str()); - wxGetApp().ClearPendingSave(); - } -}; - -// -------------------------------------------------------------------------------------- -// SysExecEvent_UnzipFromDisk -// -------------------------------------------------------------------------------------- -// Note: Unzipping always goes directly into the SysCoreThread's static VM state, and is -// always a blocking action on the SysExecutor thread (the system cannot execute other -// commands while states are unzipping or uploading into the system). -// -class SysExecEvent_UnzipFromDisk : public SysExecEvent -{ -protected: - wxString m_filename; - -public: - wxString GetEventName() const { return L"VM_UnzipFromDisk"; } - - virtual ~SysExecEvent_UnzipFromDisk() = default; - SysExecEvent_UnzipFromDisk* Clone() const { return new SysExecEvent_UnzipFromDisk(*this); } - SysExecEvent_UnzipFromDisk(const wxString& filename) - : m_filename(filename) - { - } - - wxString GetStreamName() const { return m_filename; } - -protected: - void InvokeEvent() - { - // We use direct Suspend/Resume control here, since it's desirable that emulation - // *ALWAYS* start execution after the new savestate is loaded. - GetCoreThread().Pause({}); - SaveState_UnzipFromDisk(StringUtil::wxStringToUTF8String(m_filename)); - GetCoreThread().Resume(); // force resume regardless of emulation state earlier. - } -}; - -// ===================================================================================================== -// StateCopy Public Interface -// ===================================================================================================== - -void StateCopy_SaveToFile(const wxString& file) -{ - UI_DisableStateActions(); - GetSysExecutorThread().PostEvent(new SysExecEvent_SaveState(file)); -} - -void StateCopy_LoadFromFile(const wxString& file) -{ - UI_DisableSysActions(); - GetSysExecutorThread().PostEvent(new SysExecEvent_UnzipFromDisk(file)); -} - -// Saves recovery state info to the given saveslot, or saves the active emulation state -// (if one exists) and no recovery data was found. This is needed because when a recovery -// state is made, the emulation state is usually reset so the only persisting state is -// the one in the memory save. :) -void StateCopy_SaveToSlot(uint num) -{ - const wxString file(StringUtil::UTF8StringToWxString(SaveStateBase::GetSavestateFolder(num, true))); - - // Backup old Savestate if one exists. - if (wxFileExists(file) && EmuConfig.BackupSavestate) - { - const wxString copy(StringUtil::UTF8StringToWxString(SaveStateBase::GetSavestateFolder(num, true)) + pxsFmt(L".backup")); - - Console.Indent().WriteLn(Color_StrongGreen, "Backing up existing state in slot %d.", num); - wxRenameFile(file, copy); - } - - OSDlog(Color_StrongGreen, true, "Saving savestate to slot %d...", num); - Console.Indent().WriteLn(Color_StrongGreen, "filename: %ls", WX_STR(file)); - - StateCopy_SaveToFile(file); -#ifdef USE_NEW_SAVESLOTS_UI - UI_UpdateSysControls(); -#endif -} - -void StateCopy_LoadFromSlot(uint slot, bool isFromBackup) -{ - wxString file(StringUtil::UTF8StringToWxString(SaveStateBase::GetSavestateFolder(slot, true)) + wxString(isFromBackup ? L".backup" : L"")); - - if (!wxFileExists(file)) - { - OSDlog(Color_StrongGreen, true, "Savestate slot %d%s is empty.", slot, isFromBackup ? " (backup)" : ""); - return; - } - - OSDlog(Color_StrongGreen, true, "Loading savestate from slot %d...%s", slot, isFromBackup ? " (backup)" : ""); - Console.Indent().WriteLn(Color_StrongGreen, "filename: %ls", WX_STR(file)); - - StateCopy_LoadFromFile(file); -#ifdef USE_NEW_SAVESLOTS_UI - UI_UpdateSysControls(); -#endif -} - diff --git a/pcsx2/gui/SysThreadBase.cpp b/pcsx2/gui/SysThreadBase.cpp deleted file mode 100644 index 4f3bcabb9b..0000000000 --- a/pcsx2/gui/SysThreadBase.cpp +++ /dev/null @@ -1,367 +0,0 @@ -/* PCSX2 - PS2 Emulator for PCs - * Copyright (C) 2002-2010 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 . - */ - -#include "PrecompiledHeader.h" - -#include "System.h" -#include "SysThreads.h" -#include "CDVD/CDVDcommon.h" - -// -------------------------------------------------------------------------------------- -// SysThreadBase *External Thread* Implementations -// (Called form outside the context of this thread) -// -------------------------------------------------------------------------------------- - -SysThreadBase::SysThreadBase() - : m_ExecMode(ExecMode_NoThreadYet) - , m_ExecModeMutex() -{ -} - -void SysThreadBase::Start() -{ - _parent::Start(); - - Sleep(1); - - pxAssertDev((m_ExecMode == ExecMode_Closing) || (m_ExecMode == ExecMode_Closed), - "Unexpected thread status during SysThread startup."); - - m_sem_event.NotifyOfWork(); -} - - -void SysThreadBase::OnStart() -{ - if (!pxAssertDev(m_ExecMode == ExecMode_NoThreadYet, "SysSustainableThread:Start(): Invalid execution mode")) - return; - - m_sem_Resume.Reset(); - m_sem_ChangingExecMode.Reset(); - - FrankenMutex(m_ExecModeMutex); - FrankenMutex(m_RunningLock); - - _parent::OnStart(); -} - -// Suspends emulation and closes the emulation state at the next PS2 vsync, -// and returns control to the calling thread; or does nothing if the core is already suspended. -// -// Parameters: -// isNonblocking - if set to true then the function will not block for emulation suspension. -// Defaults to false if parameter is not specified. Performing non-blocking suspension -// is mostly useful for starting certain non-Emu related gui activities (improves gui -// responsiveness). -// -// Returns: -// The previous suspension state; true if the thread was running or false if it was -// suspended. -// -// Exceptions: -// CancelEvent - thrown if the thread is already in a Paused or Closing state. Because -// actions that pause emulation typically rely on subcomponents remaining loaded/active, -// Suspension must cancel itself forcefully or risk crashing whatever other action is -// in progress. -// -void SysThreadBase::Suspend(bool isBlocking) -{ - if (!pxAssertDev(!IsSelf(), "Suspend/Resume are not allowed from this thread.")) - return; - if (!IsRunning()) - return; - - // shortcut ExecMode check to avoid deadlocking on redundant calls to Suspend issued - // from Resume or OnResumeReady code. - if (m_ExecMode == ExecMode_Closed) - return; - - { - ScopedLock locker(m_ExecModeMutex); - - switch (m_ExecMode.load()) - { - // Invalid thread state, nothing to suspend - case ExecMode_NoThreadYet: - // Check again -- status could have changed since above. - case ExecMode_Closed: - return; - - case ExecMode_Pausing: - case ExecMode_Paused: - if (!isBlocking) - throw Exception::CancelEvent("Cannot suspend in non-blocking fashion: Another thread is pausing the VM state."); - - m_ExecMode = ExecMode_Closing; - m_sem_Resume.Post(); - m_sem_ChangingExecMode.Wait(); - break; - - case ExecMode_Opened: - m_ExecMode = ExecMode_Closing; - break; - - case ExecMode_Closing: - break; - } - - pxAssertDev(m_ExecMode == ExecMode_Closing, "ExecMode should be nothing other than Closing..."); - m_sem_event.NotifyOfWork(); - } - - if (isBlocking) - m_RunningLock.Wait(); -} - -// Returns: -// The previous suspension state; true if the thread was running or false if it was -// closed, not running, or paused. -// -void SysThreadBase::Pause(SystemsMask systemsToTearDown, bool debug) -{ - if (IsSelf() || !IsRunning()) - return; - - // shortcut ExecMode check to avoid deadlocking on redundant calls to Suspend issued - // from Resume or OnResumeReady code. - if ((m_ExecMode == ExecMode_Closed) || (m_ExecMode == ExecMode_Paused)) - return; - - { - ScopedLock locker(m_ExecModeMutex); - - // Check again -- status could have changed since above. - if ((m_ExecMode == ExecMode_Closed) || (m_ExecMode == ExecMode_Paused)) - return; - - m_SystemsToTearDown.store(systemsToTearDown, std::memory_order_relaxed); - if (m_ExecMode == ExecMode_Opened) - m_ExecMode = ExecMode_Pausing; - - pxAssertDev(m_ExecMode == ExecMode_Pausing, "ExecMode should be nothing other than Pausing..."); - - if (debug) - OnPauseDebug(); - else - OnPause(); - m_sem_event.NotifyOfWork(); - } - - m_RunningLock.Wait(); -} - -void SysThreadBase::PauseSelf() -{ - if (!IsSelf() || !IsRunning()) - return; - - { - ScopedLock locker(m_ExecModeMutex); - - if (m_ExecMode == ExecMode_Opened) - m_ExecMode = ExecMode_Pausing; - - OnPause(); - m_sem_event.NotifyOfWork(); - } -} - -void SysThreadBase::PauseSelfDebug() -{ - if (!IsSelf() || !IsRunning()) - return; - - { - ScopedLock locker(m_ExecModeMutex); - - if (m_ExecMode == ExecMode_Opened) - m_ExecMode = ExecMode_Pausing; - - OnPauseDebug(); - m_sem_event.NotifyOfWork(); - } -} - -// Resumes the core execution state, or does nothing is the core is already running. If -// settings were changed, resets will be performed as needed and emulation state resumed from -// memory savestates. -// -// Note that this is considered a non-blocking action. Most times the state is safely resumed -// on return, but in the case of re-entrant or nested message handling the function may return -// before the thread has resumed. If you need explicit behavior tied to the completion of the -// Resume, you'll need to bind callbacks to either OnResumeReady or OnResumeInThread. -// -// Exceptions: -// ThreadCreationError - Insufficient system resources to create thread. -// -void SysThreadBase::Resume() -{ - if (IsSelf()) - return; - if (m_ExecMode == ExecMode_Opened) - return; - - ScopedLock locker(m_ExecModeMutex); - - // Implementation Note: - // The entire state coming out of a Wait is indeterminate because of user input - // and pending messages being handled. So after each call we do some seemingly redundant - // sanity checks against m_ExecMode/m_Running status, and if something doesn't feel - // right, we should abort; the user may have canceled the action before it even finished. - - switch (m_ExecMode.load()) - { - case ExecMode_Opened: - return; - - case ExecMode_NoThreadYet: - { - Start(); - if (!m_running || (m_ExecMode == ExecMode_NoThreadYet)) - throw Exception::ThreadCreationError(this); - if (m_ExecMode == ExecMode_Opened) - return; - } - [[fallthrough]]; - - case ExecMode_Closing: - case ExecMode_Pausing: - // we need to make sure and wait for the emuThread to enter a fully suspended - // state before continuing... - - m_RunningLock.Wait(); - if (!m_running) - return; - if ((m_ExecMode != ExecMode_Closed) && (m_ExecMode != ExecMode_Paused)) - return; - break; - - case ExecMode_Paused: - case ExecMode_Closed: - break; - } - - pxAssertDev((m_ExecMode == ExecMode_Closed) || (m_ExecMode == ExecMode_Paused), - "SysThreadBase is not in a closed/paused state? wtf!"); - - OnResumeReady(); - m_ExecMode = ExecMode_Opened; - m_sem_Resume.Post(); -} - - -// -------------------------------------------------------------------------------------- -// SysThreadBase *Worker* Implementations -// (Called from the context of this thread only) -// -------------------------------------------------------------------------------------- - -void SysThreadBase::OnStartInThread() -{ - m_RunningLock.Acquire(); - _parent::OnStartInThread(); - m_ExecMode = ExecMode_Closing; -} - -void SysThreadBase::OnCleanupInThread() -{ - m_ExecMode = ExecMode_NoThreadYet; - _parent::OnCleanupInThread(); - m_RunningLock.Release(); -} - -void SysThreadBase::OnSuspendInThread() {} -void SysThreadBase::OnResumeInThread(SystemsMask systemsToReinstate) {} - -// Tests for Pause and Suspend/Close requests. If the thread is trying to be paused or -// closed, it will enter a wait/holding pattern here in this method until the managing -// thread releases it. Use the return value to detect if changes to the thread's state -// may have been changed (based on the rule that other threads are not allowed to modify -// this thread's state without pausing or closing it first, to prevent race conditions). -// -// Return value: -// TRUE if the thread was paused or closed; FALSE if the thread -// continued execution unimpeded. -bool SysThreadBase::StateCheckInThread() -{ - SystemsMask systemsToTearDown {}; // Used for Pausing/Paused - - switch (m_ExecMode.load()) - { - -#ifdef PCSX2_DEVBUILD // optimize out handlers for these cases in release builds. - case ExecMode_NoThreadYet: - // threads should never have this state set while the thread is in any way - // active or alive. (for obvious reasons!!) - pxFailDev("Invalid execution state detected."); - return false; -#endif - - case ExecMode_Opened: - // Other cases don't need TestCancel() because its built into the various - // threading wait/signal actions. - TestCancel(); - return false; - - // ------------------------------------- - case ExecMode_Pausing: - { - systemsToTearDown = m_SystemsToTearDown.exchange({}, std::memory_order_relaxed); - - OnPauseInThread(systemsToTearDown); - m_ExecMode = ExecMode_Paused; - m_RunningLock.Release(); - } - [[fallthrough]]; - - case ExecMode_Paused: - while (m_ExecMode == ExecMode_Paused) - m_sem_Resume.WaitWithoutYield(); - - m_RunningLock.Acquire(); - if (m_ExecMode != ExecMode_Closing) - { - // AppCoreThread deals with Reseting CDVD - OnResumeInThread(g_CDVDReset ? static_cast(systemsToTearDown & ~(System_CDVD)) : systemsToTearDown); - g_CDVDReset = false; - break; - } - m_sem_ChangingExecMode.Post(); - - [[fallthrough]]; - - // fallthrough if we're switching to closing state... - // ------------------------------------- - case ExecMode_Closing: - { - OnSuspendInThread(); - m_ExecMode = ExecMode_Closed; - m_RunningLock.Release(); - } - [[fallthrough]]; - - case ExecMode_Closed: - while (m_ExecMode == ExecMode_Closed) - m_sem_Resume.WaitWithoutYield(); - - m_RunningLock.Acquire(); - OnResumeInThread(static_cast(-1)); // All systems - g_CDVDReset = false; - break; - - jNO_DEFAULT; - } - - return true; -} diff --git a/pcsx2/gui/SysThreads.h b/pcsx2/gui/SysThreads.h deleted file mode 100644 index 15922ca3a7..0000000000 --- a/pcsx2/gui/SysThreads.h +++ /dev/null @@ -1,290 +0,0 @@ -/* PCSX2 - PS2 Emulator for PCs - * Copyright (C) 2002-2010 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 . - */ - -#pragma once - -#include "System.h" - -#include "common/emitter/tools.h" -#include "PersistentThread.h" -#include "PINE.h" - - -using namespace Threading; - -typedef SafeArray VmStateBuffer; - -enum SystemsMask : uint8_t -{ - System_GS = 1 << 0, - System_SPU2 = 1 << 1, - System_PAD = 1 << 2, - System_FW = 1 << 3, - System_CDVD = 1 << 4, - System_USB = 1 << 5, - System_DEV9 = 1 << 6, - System_MCD = 1 << 7, -}; - -// -------------------------------------------------------------------------------------- -// SysThreadBase -// -------------------------------------------------------------------------------------- - -class SysThreadBase : public pxThread -{ - typedef pxThread _parent; - -public: - // Important: The order of these enumerations matters! Optimized tests are used for both - // Closed and Paused states. - enum ExecutionMode - { - // Thread has not been created yet. Typically this is the same as IsRunning() - // returning FALSE. - ExecMode_NoThreadYet, - - // Thread is safely paused, with subcomponents in a "closed" state, and waiting for a - // resume/open signal. - ExecMode_Closed, - - // Thread is safely paused, with subcomponents in an "open" state, and waiting for a - // resume/open signal. - ExecMode_Paused, - - // Thread is active and running, with pluigns in an "open" state. - ExecMode_Opened, - - // Close signal has been sent to the thread, but the thread's response is still - // pending (thread is busy/running). - ExecMode_Closing, - - // Pause signal has been sent to the thread, but the thread's response is still - // pending (thread is busy/running). - ExecMode_Pausing, - }; - -protected: - std::atomic m_ExecMode; - std::atomic m_SystemsToTearDown {}; - - // This lock is used to avoid simultaneous requests to Suspend/Resume/Pause from - // contending threads. - MutexRecursive m_ExecModeMutex; - - // Used to wake up the thread from sleeping when it's in a suspended state. - Semaphore m_sem_Resume; - - // Used to synchronize inline changes from paused to suspended status. - Semaphore m_sem_ChangingExecMode; - - // Locked whenever the thread is not in a suspended state (either closed or paused). - // Issue a Wait against this mutex for performing actions that require the thread - // to be suspended. - Mutex m_RunningLock; - -public: - explicit SysThreadBase(); - virtual ~SysThreadBase() = default; - - // Thread safety for IsOpen / IsClosed: The execution mode can change at any time on - // any thread, so the actual status may have already changed by the time this function - // returns its result. Typically this isn't of major concern. However if you need - // more assured execution mode status, issue a lock against the ExecutionModeMutex() - // first. - bool IsOpen() const - { - return IsRunning() && (m_ExecMode > ExecMode_Closed); - } - - bool IsClosed() const { return !IsOpen(); } - bool IsPaused() const { return !IsRunning() || (m_ExecMode <= ExecMode_Paused); } - - bool IsClosing() const - { - return !IsRunning() || (m_ExecMode <= ExecMode_Closed) || (m_ExecMode == ExecMode_Closing); - } - - bool HasPendingStateChangeRequest() const - { - return m_ExecMode >= ExecMode_Closing; - } - - ExecutionMode GetExecutionMode() const { return m_ExecMode.load(); } - Mutex& ExecutionModeMutex() { return m_ExecModeMutex; } - - virtual void Suspend(bool isBlocking = true); - virtual void Resume(); - virtual void Pause(SystemsMask systemsToTearDown, bool debug = false); - virtual void PauseSelf(); - virtual void PauseSelfDebug(); - -protected: - virtual void OnStart(); - - // This function is called by Resume immediately prior to releasing the suspension of - // the core emulation thread. You should overload this rather than Resume(), since - // Resume() has a lot of checks and balances to prevent re-entrance and race conditions. - virtual void OnResumeReady() {} - virtual void OnPause() {} - virtual void OnPauseDebug() {} - - virtual bool StateCheckInThread(); - virtual void OnCleanupInThread(); - virtual void OnStartInThread(); - - // Used internally from Resume(), so let's make it private here. - virtual void Start(); - - // Extending classes should implement this, but should not call it. The parent class - // handles invocation by the following guidelines: Called *in thread* from StateCheckInThread() - // prior to suspending the thread (ie, when Suspend() has been called on a separate - // thread, requesting this thread suspend itself temporarily). After this is called, - // the thread enters a waiting state on the m_sem_Resume semaphore. - virtual void OnSuspendInThread() = 0; - - // Extending classes should implement this, but should not call it. The parent class - // handles invocation by the following guidelines: Called *in thread* from StateCheckInThread() - // prior to pausing the thread (ie, when Pause() has been called on a separate thread, - // requesting this thread pause itself temporarily). After this is called, the thread - // enters a waiting state on the m_sem_Resume semaphore. - // Parameter: - // systemsToTearDown - a bitmask of systems to call Close functions on. - virtual void OnPauseInThread(SystemsMask systemsToTearDown) = 0; - - // Extending classes should implement this, but should not call it. The parent class - // handles invocation by the following guidelines: Called from StateCheckInThread() after the - // thread has been suspended and then subsequently resumed. - // Parameter: - // systemsToTearDown - a bitmask of systems to call Open functions on. - virtual void OnResumeInThread(SystemsMask systemsToReinstate) = 0; -}; - -// -------------------------------------------------------------------------------------- -// SysCoreThread class -// -------------------------------------------------------------------------------------- -class SysCoreThread : public SysThreadBase -{ - typedef SysThreadBase _parent; - -protected: - bool m_resetRecompilers; - bool m_resetProfilers; - bool m_resetVsyncTimers; - bool m_resetVirtualMachine; - - // Stores the state of the PINE thread. - std::unique_ptr m_pineServer; - - // Current state of the PINE thread - enum StatePINE - { - OFF, - ON - }; - StatePINE m_PineState = OFF; - - // Indicates if the system has an active virtual machine state. Pretty much always - // true anytime between subcomponents being initialized and being shutdown. Gets - // set false when they are shutdown, the corethread is canceled, or when an error - // occurs while trying to upload a new state into the VM. - std::atomic m_hasActiveMachine; - - wxString m_elf_override; - - SSE_MXCSR m_mxcsr_saved; - -public: - explicit SysCoreThread(); - virtual ~SysCoreThread(); - - bool HasPendingStateChangeRequest() const; - - virtual void OnResumeReady() override; - virtual void Reset(); - virtual void ResetQuick(); - virtual void Cancel(bool isBlocking = true) override; - virtual bool Cancel(const wxTimeSpan& timeout) override; - - virtual bool StateCheckInThread() override; - virtual void VsyncInThread(); - virtual void GameStartingInThread(); - - virtual void ApplySettings(const Pcsx2Config& src); - - virtual bool HasActiveMachine() const { return m_hasActiveMachine; } - - virtual const wxString& GetElfOverride() const { return m_elf_override; } - virtual void SetElfOverride(const wxString& elf); - -protected: - void _reset_stuff_as_needed(); - - virtual void Start() override; - virtual void OnStart() override; - virtual void OnSuspendInThread() override; - virtual void OnPauseInThread(SystemsMask systemsToTearDown) override { TearDownSystems(systemsToTearDown); } - virtual void OnResumeInThread(SystemsMask systemsToReinstate) override; - virtual void OnCleanupInThread() override; - virtual void ExecuteTaskInThread() override; - virtual void DoCpuReset(); - virtual void DoCpuExecute(); - - void _StateCheckThrows(); - -private: - void TearDownSystems(SystemsMask systemsToTearDown); -}; - - - -struct SysStateUnlockedParams -{ - SysStateUnlockedParams() {} -}; - -// -------------------------------------------------------------------------------------- -// IEventListener_SaveStateThread -// -------------------------------------------------------------------------------------- -class IEventListener_SysState : public IEventDispatcher -{ -public: - typedef SysStateUnlockedParams EvtParams; - -public: - IEventListener_SysState() {} - virtual ~IEventListener_SysState() = default; - - virtual void DispatchEvent(const SysStateUnlockedParams& status) - { - SysStateAction_OnUnlocked(); - } - -protected: - virtual void SysStateAction_OnUnlocked(); -}; - -// GetCoreThread() is a required external implementation. This function is *NOT* -// provided by the PCSX2 core library. It provides an interface for the linking User -// Interface apps or DLLs to reference their own instance of SysCoreThread (also allowing -// them to extend the class and override virtual methods). -// -extern SysCoreThread& GetCoreThread(); - -extern bool g_CDVDReset; - -namespace PINESettings -{ - extern unsigned int slot; -}; diff --git a/pcsx2/gui/ThreadingDialogs.cpp b/pcsx2/gui/ThreadingDialogs.cpp deleted file mode 100644 index 5138049870..0000000000 --- a/pcsx2/gui/ThreadingDialogs.cpp +++ /dev/null @@ -1,85 +0,0 @@ -/* PCSX2 - PS2 Emulator for PCs - * Copyright (C) 2002-2010 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 . - */ - -#include "PrecompiledHeader.h" -#include "gui/ThreadingDialogs.h" -#include "gui/pxStaticText.h" - -using namespace pxSizerFlags; - -wxDEFINE_EVENT(pxEvt_ThreadedTaskComplete, wxCommandEvent); - -// -------------------------------------------------------------------------------------- -// WaitForTaskDialog Implementations -// -------------------------------------------------------------------------------------- -wxIMPLEMENT_DYNAMIC_CLASS(WaitForTaskDialog, wxDialogWithHelpers); - -Threading::WaitForTaskDialog::WaitForTaskDialog(const wxString& title, const wxString& heading) - : wxDialogWithHelpers(NULL, _("Waiting for tasks...")) -//, m_Timer(this) -{ - SetMinWidth(300); - - //m_sem = sem; - //m_mutex = mutex; - - wxString m_title(title); - wxString m_heading(heading); - - if (m_title.IsEmpty()) - m_title = _("Waiting for task..."); - if (m_heading.IsEmpty()) - m_heading = m_title; - - Bind(pxEvt_ThreadedTaskComplete, &WaitForTaskDialog::OnTaskComplete, this); - - *this += 12; - *this += Heading(m_heading).Unwrapped() | StdExpand(); - *this += 12; - - // TODO : Implement a cancel button. Not quite sure the best way to do - // that, since it requires a thread or event handler context, or something. - - //applyDlg += new wxButton( &applyDlg, wxID_CANCEL ) | pxCenter; - //applyDlg += 6; - - //Bind(wxEVT_TIMER, &WaitForTaskDialog::OnTimer, this, m_Timer.GetId()); - //m_Timer.Start( 200 ); - //GetSysExecutorThread().PostEvent( new SysExecEvent_ApplyPlugins( this, m_sync ) ); -} - -void Threading::WaitForTaskDialog::OnTaskComplete(wxCommandEvent& evt) -{ - evt.Skip(); - - // Note: we don't throw exceptions from the pending task here. - // Instead we wait until we exit the modal loop below -- this gives - // the caller a chance to handle the exception themselves, and if - // not the exception will still fall back on the standard app-level - // exception handler. - - // (this also avoids any sticky business with the modal dialog not getting - // closed out right due to stack unwinding skipping dialog closure crap) - - m_sync.WaitForResult_NoExceptions(); - EndModal(wxID_OK); -} - -int Threading::WaitForTaskDialog::ShowModal() -{ - int result = _parent::ShowModal(); - m_sync.RethrowException(); - return result; -} diff --git a/pcsx2/gui/ThreadingDialogs.h b/pcsx2/gui/ThreadingDialogs.h deleted file mode 100644 index baba36a861..0000000000 --- a/pcsx2/gui/ThreadingDialogs.h +++ /dev/null @@ -1,52 +0,0 @@ -/* PCSX2 - PS2 Emulator for PCs - * Copyright (C) 2002-2010 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 . - */ - -#pragma once - -#include "common/Threading.h" -#include "pcsx2/gui/wxAppWithHelpers.h" - -wxDECLARE_EVENT(pxEvt_ThreadedTaskComplete, wxCommandEvent); - -namespace Threading -{ - // -------------------------------------------------------------------------------------- - // WaitForTaskDialog - // -------------------------------------------------------------------------------------- - // This dialog is displayed whenever the main thread is recursively waiting on multiple - // mutexes or semaphores. wxwidgets does not support recursive yielding to pending events - // but it *does* support opening a modal dialog, which disables the interface (preventing - // the user from starting additional actions), and processes messages (allowing the system - // to continue to manage threads and process logging). - // - class WaitForTaskDialog : public wxDialogWithHelpers - { - wxDECLARE_DYNAMIC_CLASS_NO_COPY(WaitForTaskDialog); - - typedef wxDialogWithHelpers _parent; - - protected: - SynchronousActionState m_sync; - - public: - WaitForTaskDialog(const wxString& title = wxEmptyString, const wxString& heading = wxEmptyString); - virtual ~WaitForTaskDialog() = default; - virtual int ShowModal(); - - protected: - void OnTaskComplete(wxCommandEvent& evt); - //void OnTimer( wxTimerEvent& evt ); - }; -} // namespace Threading diff --git a/pcsx2/gui/UpdateUI.cpp b/pcsx2/gui/UpdateUI.cpp deleted file mode 100644 index e922950db0..0000000000 --- a/pcsx2/gui/UpdateUI.cpp +++ /dev/null @@ -1,179 +0,0 @@ -/* PCSX2 - PS2 Emulator for PCs - * Copyright (C) 2002-2010 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 . - */ - -#include "PrecompiledHeader.h" -#include "MainFrame.h" -#include "GSFrame.h" -#include "Saveslots.h" - -// General Notes: -// * It's very important that we re-discover menu items by ID every time we change them, -// because the modern era of configurable GUIs means that we can't be assured the IDs -// exist anymore. - - -// This is necessary because this stupid wxWidgets thing has implicit debug errors -// in the FindItem call that asserts if the menu options are missing. This is bad -// mojo for configurable/dynamic menus. >_< -void MainEmuFrame::EnableMenuItem(int id, bool enable) -{ - if (wxMenuItem *item = m_menubar.FindItem(id)) - item->Enable(enable); -} - -void MainEmuFrame::SetMenuItemLabel(int id, wxString str) -{ - if (wxMenuItem *item = m_menubar.FindItem(id)) - item->SetItemLabel(str); -} - -void MainEmuFrame::CheckMenuItem(int id, bool checked) -{ - if (wxMenuItem *item = m_menubar.FindItem(id)) - item->Check(checked); -} - -static void _SaveLoadStuff(bool enabled) -{ - sMainFrame.EnableMenuItem(MenuId_Sys_LoadStates, enabled); - sMainFrame.EnableMenuItem(MenuId_Sys_SaveStates, enabled); - -#ifdef USE_NEW_SAVESLOTS_UI - const wxString wxDiscSerial(StringUtil::UTF8StringToWxString(DiscSerial)); - bool crcChanged = false; - // Run though all the slots. Update if they need updating or the crc changed. - for (Saveslot &slot : saveslot_cache) - { - if (!SysHasValidState()) - { - return; - } - - // We need to reload the file information if the crc or serial # changed. - // Invalidate slot cache when using full boot (g_GameLoading) otherwise it won't see the new folder path - if ((g_GameLoading || slot.crc != ElfCRC) || (slot.serialName != wxDiscSerial)) - { - slot.invalid_cache = true; - crcChanged = true; - } - - // Either the cache needs updating, or the menu items do, or both. - if (slot.menu_update || slot.invalid_cache) - { - #ifdef SAVESLOT_LOGS - Console.WriteLn("Updating slot %i.", slot.slot_num); - if (slot.menu_update) Console.WriteLn("Menu update needed."); - if (slot.invalid_cache) Console.WriteLn("Invalid cache. (CRC different or just initialized.)"); - #endif - - if (slot.invalid_cache) - { - // Pull everything from disk. - slot.UpdateCache(); - - #ifdef SAVESLOT_LOGS - slot.ConsoleDump(); - #endif - } - - // Update from the cached information. - slot.menu_update = false; - slot.crc = ElfCRC; - - sMainFrame.EnableMenuItem(slot.load_item_id, !slot.empty); - sMainFrame.CheckMenuItem(slot.load_item_id, slot.slot_num == States_GetCurrentSlot()); - sMainFrame.SetMenuItemLabel(slot.load_item_id, slot.SlotName()); - sMainFrame.SetMenuItemLabel(slot.save_item_id, slot.SlotName()); - } - } - if (crcChanged) - { - States_updateLoadBackupMenuItem(); - } -#endif -} - -// Updates the enable/disable status of all System related controls: menus, toolbars, -// etc. Typically called by SysEvtHandler whenever the message pump becomes idle. -void UI_UpdateSysControls() -{ - #ifdef SAVESLOT_LOGS - Console.WriteLn("In the routine for updating the UI."); - #endif - - if (wxGetApp().Rpc_TryInvokeAsync(&UI_UpdateSysControls)) - return; - - sApp.PostAction(CoreThreadStatusEvent(CoreThread_Indeterminate)); - - _SaveLoadStuff(SysHasValidState()); -} - -void UI_DisableSysShutdown() -{ - if (wxGetApp().Rpc_TryInvokeAsync(&UI_DisableSysShutdown)) - return; - - sMainFrame.EnableMenuItem(MenuId_Sys_Shutdown, false); - sMainFrame.EnableMenuItem(MenuId_IsoBrowse, !g_Conf->AskOnBoot); - wxGetApp().GetRecentIsoManager().EnableItems(!g_Conf->AskOnBoot); -} - -void UI_EnableSysShutdown() -{ - if (wxGetApp().Rpc_TryInvokeAsync(&UI_EnableSysShutdown)) - return; - - sMainFrame.EnableMenuItem(MenuId_Sys_Shutdown, true); -} - - -void UI_DisableSysActions() -{ - if (wxGetApp().Rpc_TryInvokeAsync(&UI_DisableSysActions)) - return; - - sMainFrame.EnableMenuItem(MenuId_Sys_Shutdown, false); - - _SaveLoadStuff(false); -} - -void UI_EnableSysActions() -{ - if (wxGetApp().Rpc_TryInvokeAsync(&UI_EnableSysActions)) - return; - - sMainFrame.EnableMenuItem(MenuId_Sys_Shutdown, true); - sMainFrame.EnableMenuItem(MenuId_IsoBrowse, true); - wxGetApp().GetRecentIsoManager().EnableItems(true); - - _SaveLoadStuff(true); -} - -void UI_DisableStateActions() -{ - if (wxGetApp().Rpc_TryInvokeAsync(&UI_DisableStateActions)) - return; - - _SaveLoadStuff(false); -} - -void UI_EnableStateActions() -{ - if (wxGetApp().Rpc_TryInvokeAsync(&UI_EnableStateActions)) - return; - - _SaveLoadStuff(true); -} diff --git a/pcsx2/gui/i18n.cpp b/pcsx2/gui/i18n.cpp deleted file mode 100644 index 6c88c4f7a6..0000000000 --- a/pcsx2/gui/i18n.cpp +++ /dev/null @@ -1,364 +0,0 @@ -/* PCSX2 - PS2 Emulator for PCs - * Copyright (C) 2002-2010 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 . - */ - -#include "PrecompiledHeader.h" -#include "i18n.h" -#include "AppConfig.h" -#include "StringHelpers.h" -#include "wxGuiTools.h" -#include "common/SafeArray.h" -#include - -// Some of the codes provided by wxWidgets are 'obsolete' -- effectively replaced by more specific -// region-qualified language codes. This function can be used to filter them out. -bool i18n_IsLegacyLanguageId( wxLanguage lang ) -{ - return - (lang == wxLANGUAGE_ENGLISH) || - (lang == wxLANGUAGE_CHINESE) || - (lang == wxLANGUAGE_CHINESE_TRADITIONAL) || - (lang == wxLANGUAGE_SERBIAN) || - (lang == wxLANGUAGE_SPANISH); -} - -static wxString i18n_GetBetterLanguageName( const wxLanguageInfo* info ) -{ - switch (info->Language) - { - case wxLANGUAGE_CHINESE: return L"Chinese (Traditional)"; -#if !wxCHECK_VERSION(3, 1, 6) - case wxLANGUAGE_CHINESE_TRADITIONAL: return L"Chinese (Traditional)"; -#endif - case wxLANGUAGE_CHINESE_TAIWAN: return L"Chinese (Traditional)"; - case wxLANGUAGE_CHINESE_HONGKONG: return L"Chinese (Traditional, Hong Kong)"; - case wxLANGUAGE_CHINESE_MACAU: return L"Chinese (Traditional, Macau)"; - } - - return info->Description; -} - -LangPackEnumeration::LangPackEnumeration( wxLanguage langId ) - : wxLangId(langId) -{ - if (const wxLanguageInfo* info = wxLocale::GetLanguageInfo( wxLangId )) - { - canonicalName = info->CanonicalName; - englishName = i18n_GetBetterLanguageName(info); - } -} - -LangPackEnumeration::LangPackEnumeration() - : wxLangId(wxLANGUAGE_DEFAULT) - , canonicalName(L"default") - , englishName(L"System Default" + _(" (default)")) -{ - int sysLang = wxLocale::GetSystemLanguage(); - - if (sysLang == wxLANGUAGE_UNKNOWN) - sysLang = wxLANGUAGE_ENGLISH_US; - - //if (const wxLanguageInfo* info = wxLocale::GetLanguageInfo( sysLang )) - // englishName += L" [" + i18n_GetBetterLanguageName(info) + L"]"; -} - -static void i18n_DoPackageCheck( wxLanguage wxLangId, LangPackList& langs, bool& valid_stat ) -{ - valid_stat = false; - if( i18n_IsLegacyLanguageId( wxLangId ) ) return; - - // note: wx preserves the current locale for us, so creating a new locale and deleting - // will not affect program status. - std::unique_ptr locale(new wxLocale(wxLangId, 0)); - - // Force the msgIdLanguage param to wxLANGUAGE_UNKNOWN to disable wx's automatic english - // matching logic, which will bypass the catalog loader for all english-based dialects, and - // (wrongly) enumerate a bunch of locales that don't actually exist. - - if ((locale->GetLanguage() == wxLANGUAGE_ENGLISH_US) || - (locale->IsOk() && locale->AddCatalog( L"pcsx2_Main", wxLANGUAGE_UNKNOWN, wxEmptyString )) ) - { - langs.push_back( LangPackEnumeration( wxLangId ) ); - valid_stat = true; - } -} - -// Finds all valid PCSX2 language packs, and enumerates them for configuration selection. -// Note: On linux there's no easy way to reliably enumerate language packs, since every distro -// could use its own location for installing pcsx2.mo files (wtcrap?). Furthermore wxWidgets -// doesn't give us a public API for checking what the language search paths are. So the only -// safe way to enumerate the languages is by forcibly loading every possible locale in the wx -// database. Anything which hasn't been installed will fail to load. -// -// Because loading and hashing the entire pcsx2 translation for every possible language would be -// asinine and slow, I've decided to use a file dedicated to being a translation detection anchor. -// This file is named pcsx2ident.mo, and contains only the name of the language in the language -// native form. Additional translation files are only loaded if the user picks the language (or -// if it's the default language of the user's OS installation). -// -void i18n_EnumeratePackages( LangPackList& langs ) -{ - wxDoNotLogInThisScope here; // wx generates verbose errors if languages don't exist, so disable them here. - langs.push_back( LangPackEnumeration() ); - bool valid_stat_dummy; - - for( int li=wxLANGUAGE_UNKNOWN+1; li_< - - //i18n_DoPackageCheck( wxLANGUAGE_VALENCIAN, englishNames, xlatedNames ); - //i18n_DoPackageCheck( wxLANGUAGE_SAMI, englishNames, xlatedNames ); -} - -#if 0 -// warning: wxWidgets uses duplicated canonical codes for many languages, and has some bizarre -// matching heuristics. Using this function doesn't really match the language and sublanguage -// (dialect) that the user selected. -bool i18n_SetLanguage( const wxString& langCode ) -{ - if (langCode.IsEmpty() || langCode.CmpNoCase(L"default")) - { - wxLanguage sysLang = (wxLanguage)wxLocale::GetSystemLanguage(); - - if (sysLang == wxLANGUAGE_UNKNOWN) - sysLang = wxLANGUAGE_ENGLISH; - } - - const wxLanguageInfo* woot = wxLocale::FindLanguageInfo( langCode ); - if (!woot) return false; - return i18n_SetLanguage( woot->Language ); -} -#endif - -// Multiple language ID are actually mostly identical languages. In case a translation is not -// yet provided for the dialect fallback to the main one. It would reduce the burden of the -// translators and reduce the language pack. -static wxLanguage i18n_FallbackToAnotherLang( wxLanguage wxLangId ) -{ - // if some translations are provided, do not change the language - LangPackList dummy_lang; - bool valid_stat = false; - i18n_DoPackageCheck(wxLangId, dummy_lang, valid_stat); - if (valid_stat) return wxLangId; - - switch(wxLangId) - { - case wxLANGUAGE_ARABIC_ALGERIA : - case wxLANGUAGE_ARABIC_BAHRAIN : - case wxLANGUAGE_ARABIC_EGYPT : - case wxLANGUAGE_ARABIC_IRAQ : - case wxLANGUAGE_ARABIC_JORDAN : - case wxLANGUAGE_ARABIC_KUWAIT : - case wxLANGUAGE_ARABIC_LEBANON : - case wxLANGUAGE_ARABIC_LIBYA : - case wxLANGUAGE_ARABIC_MOROCCO : - case wxLANGUAGE_ARABIC_OMAN : - case wxLANGUAGE_ARABIC_QATAR : - case wxLANGUAGE_ARABIC_SAUDI_ARABIA : - case wxLANGUAGE_ARABIC_SUDAN : - case wxLANGUAGE_ARABIC_SYRIA : - case wxLANGUAGE_ARABIC_TUNISIA : - case wxLANGUAGE_ARABIC_UAE : - case wxLANGUAGE_ARABIC_YEMEN : return wxLANGUAGE_ARABIC_SAUDI_ARABIA; - - case wxLANGUAGE_CHINESE_HONGKONG : - case wxLANGUAGE_CHINESE_MACAU : return wxLANGUAGE_CHINESE_TRADITIONAL; - case wxLANGUAGE_CHINESE_SINGAPORE : return wxLANGUAGE_CHINESE_SIMPLIFIED; - - // case wxLANGUAGE_SAMI : - // case wxLANGUAGE_DANISH : - case wxLANGUAGE_NORWEGIAN_NYNORSK : return wxLANGUAGE_NORWEGIAN_BOKMAL; - - case wxLANGUAGE_SWEDISH_FINLAND : return wxLANGUAGE_SWEDISH; - - case wxLANGUAGE_AFRIKAANS : - //case wxLANG_FRISIAN : - // case wxLANGUAGE_LUXEMBOURGISH : - // - not supported by wxWidgets (3.0.0). - case wxLANGUAGE_DUTCH_BELGIAN : return wxLANGUAGE_DUTCH; - - case wxLANGUAGE_GALICIAN : - case wxLANGUAGE_PORTUGUESE : return wxLANGUAGE_PORTUGUESE_BRAZILIAN; - - // Overkill 9000? - case wxLANGUAGE_GERMAN_AUSTRIAN : - case wxLANGUAGE_GERMAN_BELGIUM : - case wxLANGUAGE_GERMAN_LIECHTENSTEIN : - case wxLANGUAGE_GERMAN_LUXEMBOURG : - // case wxLANGUAGE_LOWER_SORBIAN : - // case wxLANGUAGE_UPPER_SORBIAN : - // - Sorbian is not supported by wxWidgets (3.0.0). - case wxLANGUAGE_GERMAN_SWISS : return wxLANGUAGE_GERMAN; - - case wxLANGUAGE_BASQUE: - case wxLANGUAGE_CATALAN: - case wxLANGUAGE_SPANISH_ARGENTINA: - case wxLANGUAGE_SPANISH_BOLIVIA: - case wxLANGUAGE_SPANISH_CHILE: - case wxLANGUAGE_SPANISH_COLOMBIA: - case wxLANGUAGE_SPANISH_COSTA_RICA: - case wxLANGUAGE_SPANISH_DOMINICAN_REPUBLIC: - case wxLANGUAGE_SPANISH_ECUADOR: - case wxLANGUAGE_SPANISH_EL_SALVADOR: - case wxLANGUAGE_SPANISH_GUATEMALA: - case wxLANGUAGE_SPANISH_HONDURAS: - case wxLANGUAGE_SPANISH_MEXICAN: - case wxLANGUAGE_SPANISH_NICARAGUA: - case wxLANGUAGE_SPANISH_PANAMA: - case wxLANGUAGE_SPANISH_PARAGUAY: - case wxLANGUAGE_SPANISH_PERU: - case wxLANGUAGE_SPANISH_PUERTO_RICO: - case wxLANGUAGE_SPANISH_URUGUAY: - case wxLANGUAGE_SPANISH_VENEZUELA: return wxLANGUAGE_SPANISH_MODERN; - - case wxLANGUAGE_ITALIAN_SWISS: return wxLANGUAGE_ITALIAN; - - case wxLANGUAGE_CORSICAN: - case wxLANGUAGE_FRENCH_BELGIAN: - case wxLANGUAGE_FRENCH_CANADIAN: - case wxLANGUAGE_FRENCH_LUXEMBOURG: - case wxLANGUAGE_FRENCH_MONACO: - case wxLANGUAGE_FRENCH_SWISS: return wxLANGUAGE_FRENCH; - - case wxLANGUAGE_RUSSIAN_UKRAINE: return wxLANGUAGE_RUSSIAN; - - case wxLANGUAGE_JAVANESE: return wxLANGUAGE_INDONESIAN; - - case wxLANGUAGE_MALAY_BRUNEI_DARUSSALAM: return wxLANGUAGE_MALAY_MALAYSIA; - - default : break; - } - return wxLangId; -} - -// This method sets the requested language, based on wxLanguage id and an optional 'confirmation' -// canonical code. If the canonical code is provided, it is used to confirm that the ID matches -// the intended language/dialect. If the ID and canonical do not match, this method will use -// wx's FindLAnguageInfo to provide a "best guess" canonical match (usually relying on the user's -// operating system default). -// -// Rationale: wxWidgets language IDs are just simple enums, and not especially unique. Future -// versions of PCSX2 may have language ID changes if built against new/different versions of wx. -// To prevent PCSX2 from selecting a completely wrong language when upgraded, we double-check -// the wxLanguage code against the canonical name. We can't simply use canonical names either -// because those are not unique (dialects of chinese, for example), and wx returns the generic -// form over a specific dialect, when given a choice. Thus a two-tier check is required. -// -// wxLanguage for specific dialect, and canonical as a fallback/safeguard in case the wxLanguage -// id appears to be out of date. -// -// -bool i18n_SetLanguage( wxLanguage wxLangId, const wxString& langCode ) -{ - const wxLanguageInfo* info = wxLocale::GetLanguageInfo(wxLangId); - - // Check if you can load a similar language in case the current one is not yet provided - if (info) { - wxLanguage LangId_fallback = i18n_FallbackToAnotherLang((wxLanguage)info->Language); - if (LangId_fallback != (wxLanguage)info->Language) - info = wxLocale::GetLanguageInfo(LangId_fallback); - } - - // note: language canonical name mismatch probably means wxWidgets version changed since - // the user's ini file was provided. Missing/invalid ID probably means the same thing. - // If either is true, and the caller provided a canonical name, then let wx do a best - // match based on the canonical name. - - if (!info || (!langCode.IsEmpty() && (langCode.CmpNoCase(info->CanonicalName) != 0))) - { - if (!info) - Console.Warning( "Invalid language identifier (wxID=%d)", wxLangId ); - - if (!langCode.IsEmpty() && (langCode.CmpNoCase(L"default")!=0)) - { - info = wxLocale::FindLanguageInfo(langCode); - if (!info) - Console.Warning( "Unrecognized language canonical name '%ls'", WX_STR(langCode) ); - } - } - - if (!info) return false; - if (wxGetLocale() && (info->Language == wxGetLocale()->GetLanguage())) return true; - - std::unique_ptr locale(new wxLocale(info->Language)); - - if( !locale->IsOk() ) - { - Console.Warning( "SetLanguage: '%ls' [%ls] is not supported by the operating system", - WX_STR(i18n_GetBetterLanguageName(info)), WX_STR(locale->GetCanonicalName()) - ); - return false; - } - - wxLangId = (wxLanguage)locale->GetLanguage(); - - if (wxLangId == wxLANGUAGE_UNKNOWN) - { - Console.WriteLn("System-default language is unknown? Defaulting back to English/US."); - wxLangId = wxLANGUAGE_ENGLISH_US; - } - - // English/US is built in, so no need to load MO/PO files. - if( pxIsEnglish(wxLangId) ) - { - locale.release(); - return true; - } - - Console.WriteLn( "Loading language translation databases for '%ls' [%ls]", - WX_STR(i18n_GetBetterLanguageName(info)), WX_STR(locale->GetCanonicalName()) - ); - - static const wxChar* dictFiles[] = - { - L"pcsx2_Main", - L"pcsx2_Iconized" - }; - - bool foundone = false; - for (uint i=0; iAddCatalog(dictFiles[i])) - Console.Indent().WriteLn(Color_StrongYellow, "%ls not found -- translation dictionary may be incomplete.", dictFiles[i]); - else - foundone = true; - } - - if (!foundone) - { - Console.Warning("SetLanguage: Requested translation is not implemented yet."); - return false; - } - - locale.release(); - return true; -} - -// This method sets the lookup path to search l10n files -void i18n_SetLanguagePath() -{ - wxLocale::AddCatalogLookupPathPrefix(PathDefs::GetLangs().ToString()); -} diff --git a/pcsx2/gui/i18n.h b/pcsx2/gui/i18n.h deleted file mode 100644 index 10e513f323..0000000000 --- a/pcsx2/gui/i18n.h +++ /dev/null @@ -1,42 +0,0 @@ -/* PCSX2 - PS2 Emulator for PCs - * Copyright (C) 2002-2009 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 . - */ - -#pragma once - -#include - - -class LangPackEnumeration -{ -public: - wxLanguage wxLangId; - wxString canonicalName; - wxString englishName; - - // [TODO] : Might be nice, but have no idea how to implement it... - //wxString xlatedName; - -public: - LangPackEnumeration( wxLanguage langId ); - LangPackEnumeration(); -}; - -typedef std::vector LangPackList; - -extern bool i18n_SetLanguage( wxLanguage wxLangId, const wxString& langCode=wxEmptyString ); -extern void i18n_EnumeratePackages( LangPackList& langs ); -extern bool i18n_IsLegacyLanguageId( wxLanguage lang ); -extern void i18n_SetLanguagePath(); - diff --git a/pcsx2/gui/pxCheckBox.cpp b/pcsx2/gui/pxCheckBox.cpp deleted file mode 100644 index 0a57f5abee..0000000000 --- a/pcsx2/gui/pxCheckBox.cpp +++ /dev/null @@ -1,132 +0,0 @@ -/* PCSX2 - PS2 Emulator for PCs - * Copyright (C) 2002-2010 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 . - */ - -#include "PrecompiledHeader.h" -#include "gui/pxCheckBox.h" -#include "gui/pxStaticText.h" - -using namespace pxSizerFlags; - -// -------------------------------------------------------------------------------------- -// pxCheckBox Implementations -// -------------------------------------------------------------------------------------- - -pxCheckBox::pxCheckBox(wxWindow* parent, const wxString& label, const wxString& subtext, int flags) - : wxPanelWithHelpers(parent, wxVERTICAL) -{ - Init(label, subtext, flags); -} - -pxCheckBox::pxCheckBox(wxWindow* parent, const wxString& label, int flags) - : wxPanelWithHelpers(parent, wxVERTICAL) -{ - Init(label, wxEmptyString, flags); -} - -void pxCheckBox::Init(const wxString& label, const wxString& subtext, int flags) -{ - m_subtext = NULL; - m_subPadding = StdPadding * 2; - m_checkbox = new wxCheckBox(this, wxID_ANY, label, wxDefaultPosition, wxDefaultSize, flags); - - *this += m_checkbox | pxSizerFlags::StdExpand(); - - static const int Indentation = 23; - if (!subtext.IsEmpty()) - { - m_subtext = new pxStaticText(this, subtext, wxALIGN_LEFT); - - wxFlexGridSizer& spaced(*new wxFlexGridSizer(3)); - spaced.AddGrowableCol(1); - spaced += Indentation; - m_sizerItem_subtext = spaced.Add(m_subtext, pxBorder(wxBOTTOM, m_subPadding).Expand()); - //spaced += pxSizerFlags::StdPadding; - - *this += &spaced | pxExpand; - } - - Bind(wxEVT_CHECKBOX, &pxCheckBox::OnCheckpartCommand, this, m_checkbox->GetId()); -} - -pxCheckBox& pxCheckBox::SetSubPadding(int pad) -{ - m_subPadding = pad; - if (m_sizerItem_subtext) - { - m_sizerItem_subtext->SetBorder(m_subPadding); - Fit(); - } - return *this; -} - -// applies the tooltip to both both the checkbox and it's static subtext (if present), and -// performs word wrapping on platforms that need it (eg mswindows). -pxCheckBox& pxCheckBox::SetToolTip(const wxString& tip) -{ - pxSetToolTip(m_checkbox, tip); - pxSetToolTip(m_subtext, tip); - return *this; -} - -pxCheckBox& pxCheckBox::SetValue(bool val) -{ - pxAssert(m_checkbox); - m_checkbox->SetValue(val); - return *this; -} - -pxCheckBox& pxCheckBox::SetIndeterminate() -{ - pxAssert(m_checkbox); - m_checkbox->Set3StateValue(wxCHK_UNDETERMINED); - return *this; -} - - -pxCheckBox& pxCheckBox::SetState(wxCheckBoxState state) -{ - pxAssert(m_checkbox); - m_checkbox->Set3StateValue(state); - return *this; -} - -// Forwards checkbox actions on the internal checkbox (called 'checkpart') to listeners -// bound to the pxCheckBox "parent" panel. This helps the pxCheckBox behave more like a -// traditional checkbox. -void pxCheckBox::OnCheckpartCommand(wxCommandEvent& evt) -{ - evt.Skip(); - - wxCommandEvent newevt(evt); - newevt.SetEventObject(this); - newevt.SetId(GetId()); - GetEventHandler()->ProcessEvent(newevt); -} - -void operator+=(wxSizer& target, pxCheckBox* src) -{ - if (src) - target.Add(src, pxExpand); -} - -void operator+=(wxSizer& target, pxCheckBox& src) -{ - target.Add(&src, pxExpand); -} - -void operator+=(wxSizer* target, pxCheckBox& src) -{ - target->Add(&src, pxExpand); -} diff --git a/pcsx2/gui/pxCheckBox.h b/pcsx2/gui/pxCheckBox.h deleted file mode 100644 index 331d89c03e..0000000000 --- a/pcsx2/gui/pxCheckBox.h +++ /dev/null @@ -1,102 +0,0 @@ -/* PCSX2 - PS2 Emulator for PCs - * Copyright (C) 2002-2010 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 . - */ - -#pragma once - -#include -#include "common/Assertions.h" -#include "gui/wxGuiTools.h" - -// ------------------------------------------------------------------------------------- -// pxCheckBox -// -------------------------------------------------------------------------------------- -// The checkbox panel created uses the default spacer setting for adding checkboxes (see -// SizerFlags). The SetToolTip API provided by this function applies the tooltip to both -// both the checkbox and it's static subtext (if present), and performs word wrapping on -// platforms that need it (eg mswindows). -// -class pxCheckBox : public wxPanelWithHelpers -{ -protected: - wxCheckBox* m_checkbox; - pxStaticText* m_subtext; - - // padding below the subtext (if there's subtext). If there's no subtext, this value is unused. - int m_subPadding; - - wxSizerItem* m_sizerItem_subtext; - -public: - pxCheckBox(wxWindow* parent, const wxString& label, const wxString& subtext = wxEmptyString, int flags = wxCHK_2STATE); - pxCheckBox(wxWindow* parent, const wxString& label, int flags); - virtual ~pxCheckBox() = default; - - bool HasSubText() const { return m_subtext != NULL; } - const pxStaticText* GetSubText() const { return m_subtext; } - - pxCheckBox& SetSubPadding(int pad); - pxCheckBox& SetToolTip(const wxString& tip); - pxCheckBox& SetValue(bool val); - pxCheckBox& SetIndeterminate(); - pxCheckBox& SetState(wxCheckBoxState state); - - wxCheckBoxState GetState() const - { - pxAssert(m_checkbox != NULL); - return m_checkbox->Get3StateValue(); - } - bool GetValue() const - { - pxAssert(m_checkbox != NULL); - return m_checkbox->GetValue(); - } - bool IsChecked() const - { - pxAssert(m_checkbox != NULL); - return m_checkbox->IsChecked(); - } - bool IsIndeterminate() const - { - pxAssert(m_checkbox != NULL); - return m_checkbox->Get3StateValue() == wxCHK_UNDETERMINED; - } - operator wxCheckBox&() - { - pxAssert(m_checkbox != NULL); - return *m_checkbox; - } - operator const wxCheckBox&() const - { - pxAssert(m_checkbox != NULL); - return *m_checkbox; - } - - wxCheckBox* GetWxPtr() { return m_checkbox; } - const wxCheckBox* GetWxPtr() const { return m_checkbox; } - - //wxWindowID GetId() const { pxAssert( m_checkbox != NULL ); return m_checkbox->GetId(); } - -protected: - void Init(const wxString& label, const wxString& subtext, int flags); - void OnCheckpartCommand(wxCommandEvent& evt); -}; - -extern void operator+=(wxSizer& target, pxCheckBox& src); - -template <> -inline void operator+=(wxSizer& target, const pxWindowAndFlags& src) -{ - target.Add(src.window, src.flags); -} diff --git a/pcsx2/gui/pxEventThread.h b/pcsx2/gui/pxEventThread.h deleted file mode 100644 index 02ef673c2f..0000000000 --- a/pcsx2/gui/pxEventThread.h +++ /dev/null @@ -1,307 +0,0 @@ -/* PCSX2 - PS2 Emulator for PCs - * Copyright (C) 2002-2010 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 . - */ - -#pragma once - -#include "gui/PersistentThread.h" -#include "gui/pxEvents.h" - -#include -#include - -// TODO!! Make the system defined in this header system a bit more generic, and then move -// it to the Utilities library. - -class pxEvtQueue; -class SysExecEvent; - -// -------------------------------------------------------------------------------------- -// pxEvtLog / ConsoleLogSource_Event -// -------------------------------------------------------------------------------------- - -class ConsoleLogSource_Event : ConsoleLogSource -{ - typedef ConsoleLogSource _parent; - -public: - using _parent::IsActive; - - ConsoleLogSource_Event(); - - bool Write( const pxEvtQueue* evtHandler, const SysExecEvent* evt, const wxChar* msg ); - bool Warn( const pxEvtQueue* evtHandler, const SysExecEvent* evt, const wxChar* msg ); - bool Error( const pxEvtQueue* evtHandler, const SysExecEvent* evt, const wxChar* msg ); -}; - -extern ConsoleLogSource_Event pxConLog_Event; - -#define pxEvtLog pxConLog_Event.IsActive() && pxConLog_Event - - -// -------------------------------------------------------------------------------------- -// SysExecEvent -// -------------------------------------------------------------------------------------- -// Base class for all pxEvtQueue processable events. -// -// Rules for deriving: -// * Override InvokeEvent(), *NOT* _DoInvokeEvent(). _DoInvokeEvent() performs setup and -// wraps exceptions for transport to the invoking context/thread, and then itself calls -// InvokeEvent() to perform the derived class implementation. -// -// * Derived classes must implement their own versions of an empty constructor and -// Clone(), or else the class will fail to be copied to the event handler's thread -// context correctly. -// -// * This class is not abstract, and gives no error if the invocation method is not -// overridden: It can be used as a simple ping device against the event queue, Re- -// awaking the invoking thread as soon as the queue has caught up to and processed -// the event. -// -// * Avoid using virtual class inheritence to 'cleverly' bind a SysExecEvent to another -// existing class. Multiple inheritence is unreliable at best, and virtual inheritence -// is even worse. Create a SysExecEvent wrapper class instead, and embed an instance of -// the other class you want to turn into an event within it. It might feel like more work -// but it *will* be less work in the long run. -// -class SysExecEvent -{ -protected: - SynchronousActionState* m_sync; - -public: - virtual ~SysExecEvent() = default; - virtual SysExecEvent* Clone() const { return new SysExecEvent( *this ); } - - SysExecEvent( SynchronousActionState* sync=NULL ) - { - m_sync = sync; - } - - SysExecEvent( SynchronousActionState& sync ) - { - m_sync = &sync; - } - - const SynchronousActionState* GetSyncState() const { return m_sync; } - SynchronousActionState* GetSyncState() { return m_sync; } - - SysExecEvent& SetSyncState( SynchronousActionState* obj ) { m_sync = obj; return *this; } - SysExecEvent& SetSyncState( SynchronousActionState& obj ) { m_sync = &obj; return *this; } - - // Tells the Event Handler whether or not this event can be skipped when the system - // is being quit or reset. Typically set this to true for events which shut down the - // system, since program crashes can occur if the program tries to exit while threads - // are running. - virtual bool IsCriticalEvent() const { return false; } - - // Tells the Event Handler whether or not this event can be canceled. Typically events - // should not prohibit cancellation, since it expedites program termination and helps - // avoid permanent deadlock. Some actions like saving states and shutdown procedures - // should not allow cancellation since they could result in program crashes or corrupted - // data. - virtual bool AllowCancelOnExit() const { return true; } - - virtual void _DoInvokeEvent(); - virtual void PostResult() const; - - virtual wxString GetEventName() const; - virtual wxString GetEventMessage() const; - - virtual int GetResult() - { - if( !pxAssertDev( m_sync != NULL, "SysEvent: Expecting return value, but no sync object provided." ) ) return 0; - return m_sync->return_value; - } - - virtual void SetException( BaseException* ex ); - - void SetException( const BaseException& ex ); - -protected: - virtual void InvokeEvent(); - virtual void CleanupEvent(); -}; - -// -------------------------------------------------------------------------------------- -// SysExecEvent_MethodVoid -// -------------------------------------------------------------------------------------- -class SysExecEvent_MethodVoid : public SysExecEvent -{ -protected: - FnType_Void* m_method; - bool m_IsCritical; - wxString m_TraceName; - -public: - wxString GetEventName() const { return m_TraceName; } - - virtual ~SysExecEvent_MethodVoid() = default; - SysExecEvent_MethodVoid* Clone() const { return new SysExecEvent_MethodVoid( *this ); } - - bool AllowCancelOnExit() const { return !m_IsCritical; } - bool IsCriticalEvent() const { return m_IsCritical; } - - explicit SysExecEvent_MethodVoid( FnType_Void* method = NULL, const wxChar* traceName=NULL ) - : m_TraceName( traceName ? traceName : L"VoidMethod" ) - { - m_method = method; - m_IsCritical = false; - } - - SysExecEvent_MethodVoid& Critical() - { - m_IsCritical = true; - return *this; - } - -protected: - void InvokeEvent() - { - if( m_method ) m_method(); - } -}; - -typedef std::list pxEvtList; - -// -------------------------------------------------------------------------------------- -// pxEvtQueue -// -------------------------------------------------------------------------------------- -// Thread-safe platform-independent message queue, intended to act as a proxy between -// control threads (such as the Main/UI thread) and worker threads. -// -// Proxy message queues provide a safe environment for queuing tasks that must be executed -// in sequential order (in blocking fashion) on one or more worker threads. The queue is -// deadlock-free, which means the Main/UI thread can queue events into this EventQueue without -// fear of causing unresponsiveness within the user interface. -// -// Rationales: -// * Using the main event handler of wxWidgets is dangerous because it must call itself -// recursively when waiting on threaded events such as semaphore and mutexes. Thus, -// tasks such as suspending the VM would invoke the event queue while waiting, -// running events that expect the suspend to be complete while the suspend was still -// pending. -// -// * wxWidgets Event Queue (wxEvtHandler) isn't thread-safe and isn't even intended for -// use for anything other than wxWindow events (it uses static vars and checks/modifies -// wxApp globals while processing), so it's useless to us. Have to roll our own. -_- -// -class pxEvtQueue -{ -protected: - pxEvtList m_pendingEvents; - pxEvtList m_idleEvents; - - Threading::MutexRecursive m_mtx_pending; - Threading::Semaphore m_wakeup; - wxThreadIdType m_OwnerThreadId; - std::atomic m_Quitting; - - // Used for performance measuring the execution of individual events, - // and also for detecting deadlocks during message processing. - // Clang-3.7 failed to link (maybe 64 bits atomic isn't supported on 32 bits) - // std::atomic m_qpc_Start; - u64 m_qpc_Start; - -public: - pxEvtQueue(); - virtual ~pxEvtQueue() = default; - - virtual wxString GetEventHandlerName() const { return L"pxEvtQueue"; } - - virtual void ShutdownQueue(); - bool IsShuttingDown() const { return !!m_Quitting; } - - void ProcessEvents( pxEvtList& list, bool isIdle=false ); - void ProcessPendingEvents(); - void ProcessIdleEvents(); - void Idle(); - - void AddPendingEvent( SysExecEvent& evt ); - void PostEvent( SysExecEvent* evt ); - void PostEvent( const SysExecEvent& evt ); - void PostIdleEvent( SysExecEvent* evt ); - void PostIdleEvent( const SysExecEvent& evt ); - - void ProcessEvent( SysExecEvent* evt ); - void ProcessEvent( SysExecEvent& evt ); - - bool Rpc_TryInvokeAsync( FnType_Void* method, const wxChar* traceName=NULL ); - bool Rpc_TryInvoke( FnType_Void* method, const wxChar* traceName=NULL ); - void SetActiveThread(); - -protected: - virtual void _DoIdle() {} -}; - -// -------------------------------------------------------------------------------------- -// ExecutorThread -// -------------------------------------------------------------------------------------- -// Threaded wrapper class for implementing a pxEvtQueue on its own thread, to serve as a -// proxy between worker threads and response threads (which cannot be allowed to stall or -// deadlock). Simply create the desired EvtHandler, start the thread, and enjoy queued -// event execution in fully blocking fashion. -// -// Deadlock Protection Notes: -// The ExecutorThread utilizes an underlying pxEventQueue, which only locks the queue for -// adding and removing items only. Events are processed during an unlocked queue state, -// which allows other threads to add events with a guarantee that the add operation will -// take very little time. -// -// Implementation Notes: -// We use object encapsulation instead of multiple inheritance in order to avoid the many -// unavoidable catastrophes and pitfalls involved in multi-inheritance that would ruin our -// will to live. The alternative requires manually exposing the interface of the underlying -// instance of pxEventQueue; and that's ok really when you consider the alternative. --air -// -class ExecutorThread : public Threading::pxThread -{ - typedef Threading::pxThread _parent; - -protected: - std::unique_ptr m_ExecutorTimer; - std::unique_ptr m_EvtHandler; - -public: - ExecutorThread( pxEvtQueue* evtandler = NULL ); - virtual ~ExecutorThread() = default; - - virtual void ShutdownQueue(); - bool IsRunning() const; - - void PostEvent( SysExecEvent* evt ); - void PostEvent( const SysExecEvent& evt ); - - void PostIdleEvent( SysExecEvent* evt ); - void PostIdleEvent( const SysExecEvent& evt ); - - void ProcessEvent( SysExecEvent* evt ); - void ProcessEvent( SysExecEvent& evt ); - - bool Rpc_TryInvokeAsync( void (*evt)(), const wxChar* traceName=NULL ) - { - return m_EvtHandler ? m_EvtHandler->Rpc_TryInvokeAsync( evt, traceName ) : false; - } - - bool Rpc_TryInvoke( void (*evt)(), const wxChar* traceName=NULL ) - { - return m_EvtHandler ? m_EvtHandler->Rpc_TryInvoke( evt, traceName ) : false; - } - -protected: - void OnStart(); - void ExecuteTaskInThread(); - void OnCleanupInThread(); -}; - diff --git a/pcsx2/gui/pxEvents.h b/pcsx2/gui/pxEvents.h deleted file mode 100644 index 527df4e0d0..0000000000 --- a/pcsx2/gui/pxEvents.h +++ /dev/null @@ -1,425 +0,0 @@ -/* PCSX2 - PS2 Emulator for PCs - * Copyright (C) 2002-2010 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 . - */ - -#pragma once - -#include -#include "common/General.h" -#include "common/Pcsx2Defs.h" -#include "common/Threading.h" -#include "gui/PersistentThread.h" - -wxDECLARE_EVENT(pxEvt_StartIdleEventTimer, wxCommandEvent); -wxDECLARE_EVENT(pxEvt_DeleteObject, wxCommandEvent); -wxDECLARE_EVENT(pxEvt_DeleteThread, wxCommandEvent); - -typedef void FnType_Void(); - -// -------------------------------------------------------------------------------------- -// SynchronousActionState -// -------------------------------------------------------------------------------------- -class SynchronousActionState -{ - DeclareNoncopyableObject(SynchronousActionState); - -protected: - bool m_posted; - Threading::Semaphore m_sema; - ScopedExcept m_exception; - -public: - sptr return_value; - - SynchronousActionState() - { - m_posted = false; - return_value = 0; - } - - virtual ~SynchronousActionState() = default; - - void SetException(const BaseException& ex); - void SetException(BaseException* ex); - - Threading::Semaphore& GetSemaphore() { return m_sema; } - const Threading::Semaphore& GetSemaphore() const { return m_sema; } - - void RethrowException() const; - int WaitForResult(); - int WaitForResult_NoExceptions(); - void PostResult(int res); - void ClearResult(); - void PostResult(); -}; - - -// -------------------------------------------------------------------------------------- -// pxSimpleEvent - non-abstract implementation of wxEvent -// -------------------------------------------------------------------------------------- -// Why? I mean, why is wxEvent abstract? Over-designed OO is such a bad habit. And while -// I'm on my high horse (and it's so very high!), why use 'private'? Ever? Seriously, it -// sucks. Stop using it, people. -// -class pxSimpleEvent : public wxEvent -{ - wxDECLARE_DYNAMIC_CLASS_NO_ASSIGN(pxSimpleEvent); - -public: - explicit pxSimpleEvent(int evtid = 0) - : wxEvent(0, evtid) - { - } - - pxSimpleEvent(wxWindowID winId, int evtid) - : wxEvent(winId, evtid) - { - } - - pxSimpleEvent(const pxSimpleEvent&) = default; - virtual wxEvent* Clone() const { return new pxSimpleEvent(*this); } -}; - -// -------------------------------------------------------------------------------------- -// pxActionEvent -// -------------------------------------------------------------------------------------- -class pxActionEvent; - -wxDECLARE_EVENT(pxEvt_InvokeAction, pxActionEvent); - -class pxActionEvent : public wxEvent -{ - wxDECLARE_DYNAMIC_CLASS_NO_ASSIGN(pxActionEvent); - -protected: - SynchronousActionState* m_state; - -public: - virtual ~pxActionEvent() = default; - virtual pxActionEvent* Clone() const { return new pxActionEvent(*this); } - - explicit pxActionEvent(SynchronousActionState* sema = NULL, int msgtype = pxEvt_InvokeAction); - explicit pxActionEvent(SynchronousActionState& sema, int msgtype = pxEvt_InvokeAction); - pxActionEvent(const pxActionEvent& src); - - Threading::Semaphore* GetSemaphore() const { return m_state ? &m_state->GetSemaphore() : NULL; } - - const SynchronousActionState* GetSyncState() const { return m_state; } - SynchronousActionState* GetSyncState() { return m_state; } - - void SetSyncState(SynchronousActionState* obj) { m_state = obj; } - void SetSyncState(SynchronousActionState& obj) { m_state = &obj; } - - virtual void SetException(BaseException* ex); - void SetException(const BaseException& ex); - - virtual void _DoInvokeEvent(); - -protected: - // Extending classes should implement this method to perform whatever action it is - // the event is supposed to do. :) Thread affinity is guaranteed to be the Main/UI - // thread, and exceptions will be handled automatically. - // - // Exception note: exceptions are passed back to the thread that posted the event - // to the queue, when possible. If the calling thread is not blocking for a result - // from this event, then the exception will be posted to the Main/UI thread instead. - virtual void InvokeEvent() {} -}; - - -// -------------------------------------------------------------------------------------- -// pxExceptionEvent -// -------------------------------------------------------------------------------------- -class pxExceptionEvent : public pxActionEvent -{ - typedef pxActionEvent _parent; - -protected: - BaseException* m_except; - -public: - pxExceptionEvent(BaseException* ex = NULL) - { - m_except = ex; - } - - pxExceptionEvent(const BaseException& ex); - - virtual ~pxExceptionEvent() - { - } - - virtual pxExceptionEvent* Clone() const { return new pxExceptionEvent(*this); } - -protected: - void InvokeEvent(); -}; - -// -------------------------------------------------------------------------------------- -// pxSynchronousCommandEvent -// -------------------------------------------------------------------------------------- - -class pxSynchronousCommandEvent : public wxCommandEvent -{ - wxDECLARE_DYNAMIC_CLASS_NO_ASSIGN(pxSynchronousCommandEvent); - -protected: - SynchronousActionState* m_sync; - wxEventType m_realEvent; - -public: - virtual ~pxSynchronousCommandEvent() = default; - virtual pxSynchronousCommandEvent* Clone() const { return new pxSynchronousCommandEvent(*this); } - - pxSynchronousCommandEvent(SynchronousActionState* sema = NULL, wxEventType commandType = wxEVT_NULL, int winid = 0); - pxSynchronousCommandEvent(SynchronousActionState& sema, wxEventType commandType = wxEVT_NULL, int winid = 0); - - pxSynchronousCommandEvent(SynchronousActionState* sema, const wxCommandEvent& evt); - pxSynchronousCommandEvent(SynchronousActionState& sema, const wxCommandEvent& evt); - - pxSynchronousCommandEvent(const pxSynchronousCommandEvent& src); - - Threading::Semaphore* GetSemaphore() { return m_sync ? &m_sync->GetSemaphore() : NULL; } - wxEventType GetRealEventType() const { return m_realEvent; } - - void SetException(BaseException* ex); - void SetException(const BaseException& ex); -}; - -wxDECLARE_EVENT(pxEvt_SynchronousCommand, pxSynchronousCommandEvent); - -// -------------------------------------------------------------------------------------- -// BaseMessageBoxEvent -// -------------------------------------------------------------------------------------- -class BaseMessageBoxEvent : public pxActionEvent -{ - typedef pxActionEvent _parent; - wxDECLARE_DYNAMIC_CLASS_NO_ASSIGN(BaseMessageBoxEvent); - -protected: - wxString m_Content; - -public: - virtual ~BaseMessageBoxEvent() = default; - virtual BaseMessageBoxEvent* Clone() const { return new BaseMessageBoxEvent(*this); } - - explicit BaseMessageBoxEvent(const wxString& content = wxEmptyString, SynchronousActionState* instdata = NULL); - BaseMessageBoxEvent(const wxString& content, SynchronousActionState& instdata); - BaseMessageBoxEvent(const BaseMessageBoxEvent& event); - -protected: - virtual void InvokeEvent(); - virtual int _DoDialog() const; -}; - -// -------------------------------------------------------------------------------------- -// MsgButtons -// -------------------------------------------------------------------------------------- -class MsgButtons -{ -protected: - BITFIELD32() - bool - m_OK : 1, - m_Cancel : 1, - m_Yes : 1, - m_No : 1, - m_AllowToAll : 1, - m_Apply : 1, - m_Abort : 1, - m_Retry : 1, - m_Ignore : 1, - m_Reset : 1, - m_Close : 1; - BITFIELD_END - - wxString m_CustomLabel; - wxString m_CustomLabelId; - -public: - MsgButtons() { bitset = 0; } - - MsgButtons& OK() - { - m_OK = true; - return *this; - } - MsgButtons& Cancel() - { - m_Cancel = true; - return *this; - } - MsgButtons& Apply() - { - m_Apply = true; - return *this; - } - MsgButtons& Yes() - { - m_Yes = true; - return *this; - } - MsgButtons& No() - { - m_No = true; - return *this; - } - MsgButtons& ToAll() - { - m_AllowToAll = true; - return *this; - } - - MsgButtons& Abort() - { - m_Abort = true; - return *this; - } - MsgButtons& Retry() - { - m_Retry = true; - return *this; - } - MsgButtons& Ignore() - { - m_Ignore = true; - return *this; - } - MsgButtons& Reset() - { - m_Reset = true; - return *this; - } - MsgButtons& Close() - { - m_Close = true; - return *this; - } - - // label - native language label displayed to user - // id - raw ASCII identifier used in the config file (do not translate, hence char*) - MsgButtons& Custom(const wxString& label, const char* id) - { - m_CustomLabel = label; - m_CustomLabelId = fromUTF8(id); - return *this; - } - - MsgButtons& OKCancel() - { - m_OK = m_Cancel = true; - return *this; - } - MsgButtons& YesNo() - { - m_Yes = m_No = true; - return *this; - } - - bool HasOK() const { return m_OK; } - bool HasCancel() const { return m_Cancel; } - bool HasApply() const { return m_Apply; } - bool HasYes() const { return m_Yes; } - bool HasNo() const { return m_No; } - bool AllowsToAll() const { return m_AllowToAll; } - - bool HasAbort() const { return m_Abort; } - bool HasRetry() const { return m_Retry; } - bool HasIgnore() const { return m_Ignore; } - bool HasReset() const { return m_Reset; } - bool HasClose() const { return m_Close; } - - bool HasCustom() const { return !m_CustomLabel.IsEmpty(); } - const wxString& GetCustomLabel() const { return m_CustomLabel; } - const wxString& GetCustomLabelId() const { return m_CustomLabelId; } - - bool Allows(wxWindowID id) const; - void SetBestFocus(wxWindow* dialog) const; - void SetBestFocus(wxWindow& dialog) const; - - bool operator==(const MsgButtons& right) const - { - return OpEqu(bitset); - } - - bool operator!=(const MsgButtons& right) const - { - return !OpEqu(bitset); - } -}; - -// -------------------------------------------------------------------------------------- -// pxMessageBoxEvent -// -------------------------------------------------------------------------------------- -// This event type is used to transfer message boxes to the main UI thread, and return the -// result of the box. It's the only way a message box can be issued from non-main threads -// with complete safety in wx2.8. -// -// For simplicity sake this message box only supports two basic designs. The main design -// is a generic message box with confirmation buttons of your choosing. Additionally you -// can specify a "scrollableContent" text string, which is added into a read-only richtext -// control similar to the console logs and such. -// -// Future consideration: If wxWidgets 3.0 has improved thread safety, then it should probably -// be reasonable for it to work with a more flexable model where the dialog can be created -// on a child thread, passed to the main thread, where ShowModal() is run (keeping the nested -// message pumps on the main thread where they belong). But so far this is not possible, -// because of various subtle issues in wx2.8 design. -// -class pxMessageBoxEvent : public BaseMessageBoxEvent -{ - typedef BaseMessageBoxEvent _parent; - wxDECLARE_DYNAMIC_CLASS_NO_ASSIGN(pxMessageBoxEvent); - -protected: - wxString m_Title; - MsgButtons m_Buttons; - -public: - virtual ~pxMessageBoxEvent() = default; - virtual pxMessageBoxEvent* Clone() const { return new pxMessageBoxEvent(*this); } - - pxMessageBoxEvent() {} - pxMessageBoxEvent(const wxString& title, const wxString& content, const MsgButtons& buttons, SynchronousActionState& instdata); - pxMessageBoxEvent(const wxString& title, const wxString& content, const MsgButtons& buttons, SynchronousActionState* instdata = NULL); - pxMessageBoxEvent(const pxMessageBoxEvent& event) = default; - -protected: - int _DoDialog() const; -}; - -// -------------------------------------------------------------------------------------- -// pxAssertionEvent -// -------------------------------------------------------------------------------------- -class pxAssertionEvent : public BaseMessageBoxEvent -{ - typedef BaseMessageBoxEvent _parent; - wxDECLARE_DYNAMIC_CLASS_NO_ASSIGN(pxAssertionEvent); - -protected: - wxString m_Stacktrace; - -public: - virtual ~pxAssertionEvent() = default; - virtual pxAssertionEvent* Clone() const { return new pxAssertionEvent(*this); } - - pxAssertionEvent(const wxString& content = wxEmptyString, const wxString& trace = wxEmptyString, SynchronousActionState* instdata = NULL); - pxAssertionEvent(const wxString& content, const wxString& trace, SynchronousActionState& instdata); - pxAssertionEvent(const pxAssertionEvent& event) = default; - - pxAssertionEvent& SetStacktrace(const wxString& trace); - -protected: - int _DoDialog() const; -}; diff --git a/pcsx2/gui/pxRadioPanel.cpp b/pcsx2/gui/pxRadioPanel.cpp deleted file mode 100644 index 2035d4caf6..0000000000 --- a/pcsx2/gui/pxRadioPanel.cpp +++ /dev/null @@ -1,263 +0,0 @@ -/* PCSX2 - PS2 Emulator for PCs - * Copyright (C) 2002-2010 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 . - */ - -#include "PrecompiledHeader.h" -#include "gui/pxRadioPanel.h" -#include "gui/pxStaticText.h" -#include "common/SafeArray.inl" - -template class SafeArray; - -// =========================================================================================== -// pxRadioPanel Implementations -// =========================================================================================== - -#define VerifyRealizedState() \ - pxAssertDev(m_IsRealized, "Invalid object state: RadioButtonGroup as not been realized.") - -void pxRadioPanel::Init(const RadioPanelItem* srcArray, int arrsize) -{ - m_DefaultIdx = -1; - m_IsRealized = false; - - // FIXME: This probably needs to be platform-dependent, and/or based on font size. - m_Indentation = 23; - - for (int i = 0; i < arrsize; ++i) - Append(srcArray[i]); -} - -pxRadioPanel& pxRadioPanel::Append(const RadioPanelItem& entry) -{ - m_buttonStrings.push_back(entry); - return *this; -} - -void pxRadioPanel::Reset() -{ - m_IsRealized = false; - const int numbuttons = m_buttonStrings.size(); - if (numbuttons == 0) - return; - - for (int i = 0; i < numbuttons; ++i) - { - safe_delete(m_objects[i].LabelObj); - safe_delete(m_objects[i].SubTextObj); - } - m_buttonStrings.clear(); -} - -void pxRadioPanel::Realize() -{ - const int numbuttons = m_buttonStrings.size(); - if (numbuttons == 0) - return; - if (m_IsRealized) - return; - m_IsRealized = true; - - m_objects.MakeRoomFor(numbuttons); - - // Add all RadioButtons in one pass, and then go back and create all the subtext - // objects. This ensures the radio buttons have consecutive tab order IDs, which - // is the "preferred" method when using grouping features of the native window - // managers (GTK tends to not care either way, but Win32 definitely prefers a - // linear tab order). - - // first object has the group flag set to ensure it's the start of a radio group. - m_objects[0].LabelObj = new wxRadioButton(this, wxID_ANY, m_buttonStrings[0].Label, wxDefaultPosition, wxDefaultSize, wxRB_GROUP); - for (int i = 1; i < numbuttons; ++i) - m_objects[i].LabelObj = new wxRadioButton(this, wxID_ANY, m_buttonStrings[i].Label); - - for (int i = 0; i < numbuttons; ++i) - { - m_objects[i].SubTextObj = NULL; - if (m_buttonStrings[i].SubText.IsEmpty()) - continue; - m_objects[i].SubTextObj = new pxStaticText(this, m_buttonStrings[i].SubText, wxALIGN_LEFT); - } - - pxAssert(GetSizer() != NULL); - - for (int i = 0; i < numbuttons; ++i) - { - *this += m_objects[i].LabelObj | pxSizerFlags::StdExpand(); - - if (pxStaticText* subobj = m_objects[i].SubTextObj) - { - *this += subobj | pxBorder(wxLEFT, m_Indentation).Expand(); - *this += 9 + m_padding.GetHeight(); - } - if (!m_buttonStrings[i].ToolTip.IsEmpty()) - _setToolTipImmediate(i, m_buttonStrings[i].ToolTip); - } - - _RealizeDefaultOption(); -} - -void pxRadioPanel::_setToolTipImmediate(int idx, const wxString& tip) -{ - if (wxRadioButton* woot = m_objects[idx].LabelObj) - woot->SetToolTip(tip); - - if (pxStaticText* woot = m_objects[idx].SubTextObj) - woot->SetToolTip(tip); -} - -// The SetToolTip API provided by this function applies the tooltip to both the radio -// button and it's static subtext (if present), and performs word wrapping on platforms -// that need it (eg mswindows). -pxRadioPanel& pxRadioPanel::SetToolTip(int idx, const wxString& tip) -{ - m_buttonStrings[idx].SetToolTip(tip); - - if (m_IsRealized) - _setToolTipImmediate(idx, tip); - - return *this; -} - -pxRadioPanel& pxRadioPanel::SetSelection(int idx) -{ - if (!VerifyRealizedState()) - return *this; - - pxAssert(m_objects[idx].LabelObj != NULL); - m_objects[idx].LabelObj->SetValue(true); - return *this; -} - -void pxRadioPanel::_RealizeDefaultOption() -{ - if (m_IsRealized && m_DefaultIdx != -1) - { - wxFont def(GetFont()); - def.SetWeight(wxFONTWEIGHT_BOLD); - //def.SetStyle( wxFONTSTYLE_ITALIC ); - m_objects[m_DefaultIdx].LabelObj->SetFont(def); - m_objects[m_DefaultIdx].LabelObj->SetForegroundColour(wxColour(20, 128, 40)); - } -} - -// Highlights (bold) the text of the default radio. -// Not intended for restoring default value at later time. -pxRadioPanel& pxRadioPanel::SetDefaultItem(int idx) -{ - if (idx == m_DefaultIdx) - return *this; - - if (m_IsRealized && m_DefaultIdx != -1) - { - wxFont def(GetFont()); - m_objects[m_DefaultIdx].LabelObj->SetFont(def); - m_objects[m_DefaultIdx].LabelObj->SetForegroundColour(GetForegroundColour()); - } - - m_DefaultIdx = idx; - _RealizeDefaultOption(); - - return *this; -} - -pxRadioPanel& pxRadioPanel::EnableItem(int idx, bool enable) -{ - pxAssertDev(m_IsRealized, "RadioPanel must be realized first, prior to enabling or disabling individual items."); - - if (m_objects[idx].LabelObj) - m_objects[idx].LabelObj->Enable(enable); - - if (m_objects[idx].SubTextObj) - m_objects[idx].SubTextObj->Enable(enable); - - return *this; -} - -const RadioPanelItem& pxRadioPanel::Item(int idx) const -{ - return m_buttonStrings[idx]; -} - -RadioPanelItem& pxRadioPanel::Item(int idx) -{ - return m_buttonStrings[idx]; -} - -int pxRadioPanel::GetSelection() const -{ - if (!VerifyRealizedState()) - return 0; - - for (uint i = 0; i < m_buttonStrings.size(); ++i) - { - if (wxRadioButton* woot = m_objects[i].LabelObj) - if (woot->GetValue()) - return i; - } - - // Technically radio buttons should never allow for a case where none are selected. - // However it *can* happen on some platforms if the program code doesn't explicitly - // select one of the members of the group (which is, as far as I'm concerned, a - // programmer error!). so Assert here in such cases, and return 0 as the assumed - // default, so that calling code has a "valid" return code in release builds. - - pxFailDev("No valid selection was found in this group!"); - return 0; -} - -// Returns the wxWindowID for the currently selected radio button. -wxWindowID pxRadioPanel::GetSelectionId() const -{ - if (!VerifyRealizedState()) - return 0; - return m_objects[GetSelection()].LabelObj->GetId(); -} - - -bool pxRadioPanel::IsSelected(int idx) const -{ - if (!VerifyRealizedState()) - return false; - pxAssert(m_objects[idx].LabelObj != NULL); - return m_objects[idx].LabelObj->GetValue(); -} - -pxStaticText* pxRadioPanel::GetSubText(int idx) -{ - if (!VerifyRealizedState()) - return NULL; - return m_objects[idx].SubTextObj; -} - -const pxStaticText* pxRadioPanel::GetSubText(int idx) const -{ - if (!VerifyRealizedState()) - return NULL; - return m_objects[idx].SubTextObj; -} - -wxRadioButton* pxRadioPanel::GetButton(int idx) -{ - if (!VerifyRealizedState()) - return NULL; - return m_objects[idx].LabelObj; -} - -const wxRadioButton* pxRadioPanel::GetButton(int idx) const -{ - if (!VerifyRealizedState()) - return NULL; - return m_objects[idx].LabelObj; -} diff --git a/pcsx2/gui/pxRadioPanel.h b/pcsx2/gui/pxRadioPanel.h deleted file mode 100644 index 08828c0ce7..0000000000 --- a/pcsx2/gui/pxRadioPanel.h +++ /dev/null @@ -1,176 +0,0 @@ -/* PCSX2 - PS2 Emulator for PCs - * Copyright (C) 2002-2010 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 . - */ - -#pragma once - -#include -#include "common/SafeArray.h" -#include "gui/wxGuiTools.h" - -// -------------------------------------------------------------------------------------- -// RadioPanelItem -// -------------------------------------------------------------------------------------- - -struct RadioPanelItem -{ - wxString Label; - wxString SubText; - wxString ToolTip; - - int SomeInt; - void* SomePtr; - - RadioPanelItem(const wxString& label, const wxString& subtext = wxEmptyString, const wxString& tooltip = wxEmptyString) - : Label(label) - , SubText(subtext) - , ToolTip(tooltip) - { - SomeInt = 0; - SomePtr = NULL; - } - - RadioPanelItem& SetToolTip(const wxString& tip) - { - ToolTip = tip; - return *this; - } - - RadioPanelItem& SetSubText(const wxString& text) - { - SubText = text; - return *this; - } - - RadioPanelItem& SetInt(int intval) - { - SomeInt = intval; - return *this; - } - - RadioPanelItem& SetPtr(void* ptrval) - { - SomePtr = ptrval; - return *this; - } -}; - - -// Used as a cache for the "original" labels and subtexts, so that text can be properly -// wrapped and re-wrapped with multiple calls to OnResize(). -struct RadioPanelObjects -{ - wxRadioButton* LabelObj; - pxStaticText* SubTextObj; -}; - -// -------------------------------------------------------------------------------------- -// pxRadioPanel -// -------------------------------------------------------------------------------------- -// -// Rationale: -// Radio buttons work best when they are created consecutively, and then their subtext -// created in a second sweep (this keeps the radio buttons together in the parent window's -// child list, and avoids potentially unwanted behavior with radio buttons failing to -// group expectedly). Because of this, our radio button helper is shaped as a panel of -// a group of radio buttons only, instead of bothering with the lower level per-button -// design. This makes a few other things nicer as well, such as binding a single message -// handler to all radio buttons in the panel. -// -class pxRadioPanel : public wxPanelWithHelpers -{ -protected: - typedef std::vector ButtonArray; - typedef SafeArray ButtonObjArray; - - ButtonArray m_buttonStrings; - ButtonObjArray m_objects; - - bool m_IsRealized; - - wxSize m_padding; - int m_Indentation; - int m_DefaultIdx; // index of the default option (gets specific color/font treatment) - -public: - template - pxRadioPanel(wxWindow* parent, const RadioPanelItem (&src)[size]) - : wxPanelWithHelpers(parent, wxVERTICAL) - { - Init(src, size); - } - - pxRadioPanel(wxWindow* parent) - : wxPanelWithHelpers(parent, wxVERTICAL) - { - Init(); - } - - virtual ~pxRadioPanel() = default; - - void Reset(); - void Realize(); - - pxStaticText* GetSubText(int idx); - const pxStaticText* GetSubText(int idx) const; - pxRadioPanel& Append(const RadioPanelItem& entry); - - pxRadioPanel& SetToolTip(int idx, const wxString& tip); - pxRadioPanel& SetSelection(int idx); - pxRadioPanel& SetDefaultItem(int idx); - pxRadioPanel& EnableItem(int idx, bool enable = true); - - const RadioPanelItem& Item(int idx) const; - RadioPanelItem& Item(int idx); - - int GetSelection() const; - wxWindowID GetSelectionId() const; - bool IsSelected(int idx) const; - - const RadioPanelItem& SelectedItem() const { return Item(GetSelection()); } - RadioPanelItem& SelectedItem() { return Item(GetSelection()); } - - wxRadioButton* GetButton(int idx); - const wxRadioButton* GetButton(int idx) const; - - int GetPaddingVert() const { return m_padding.GetHeight(); } - int GetIndentation() const { return m_Indentation; } - - pxRadioPanel& SetPaddingHoriz(int newpad) - { - m_padding.SetHeight(newpad); - return *this; - } - - pxRadioPanel& SetIndentation(int newdent) - { - m_Indentation = newdent; - return *this; - } - - bool HasSubText(int idx) const - { - return !m_buttonStrings[idx].SubText.IsEmpty(); - } - - pxRadioPanel& Append(const wxString& label, const wxString& subtext = wxEmptyString, const wxString& tooltip = wxEmptyString) - { - return Append(RadioPanelItem(label, subtext, tooltip)); - } - -protected: - void Init(const RadioPanelItem* srcArray = NULL, int arrsize = 0); - void _setToolTipImmediate(int idx, const wxString& tip); - void _RealizeDefaultOption(); -}; diff --git a/pcsx2/gui/pxStaticText.cpp b/pcsx2/gui/pxStaticText.cpp deleted file mode 100644 index 62709890fd..0000000000 --- a/pcsx2/gui/pxStaticText.cpp +++ /dev/null @@ -1,335 +0,0 @@ -/* PCSX2 - PS2 Emulator for PCs - * Copyright (C) 2002-2010 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 . - */ - -#include "PrecompiledHeader.h" - -#include -#include "common/Assertions.h" -#include "gui/pxStaticText.h" - -// -------------------------------------------------------------------------------------- -// pxStaticText (implementations) -// -------------------------------------------------------------------------------------- -pxStaticText::pxStaticText(wxWindow* parent) - : _parent(parent, wxID_ANY, wxDefaultPosition, wxDefaultSize, wxNO_BORDER) -{ - m_align = wxALIGN_CENTRE_HORIZONTAL; - m_autowrap = true; - m_wrappedWidth = -1; - m_heightInLines = 1; - - SetPaddingDefaults(); -} - -pxStaticText::pxStaticText(wxWindow* parent, const wxString& label, wxAlignment align) - : _parent(parent, wxID_ANY, wxDefaultPosition, wxDefaultSize, wxNO_BORDER) -{ - m_heightInLines = 1; - m_align = align; - - SetPaddingDefaults(); - Init(label); -} - -void pxStaticText::Init(const wxString& label) -{ - m_autowrap = true; - m_wrappedWidth = -1; - m_label = label; - Bind(wxEVT_PAINT, &pxStaticText::paintEvent, this); -} - -// we need to refresh the window after changing its size as the standard -// control doesn't always update itself properly (fials typically on window resizes where -// the control is expanded to fit -- ie the control's size changes but the position does not) -void pxStaticText::DoSetSize(int x, int y, int w, int h, int sizeFlags) -{ - _parent::DoSetSize(x, y, w, h, sizeFlags); - Refresh(); -} - -void pxStaticText::SetPaddingDefaults() -{ - m_paddingPix_horiz = 7; - m_paddingPix_vert = 1; - - m_paddingPct_horiz = 0.0f; - m_paddingPct_vert = 0.0f; -} - -pxStaticText& pxStaticText::SetMinWidth(int width) -{ - SetMinSize(wxSize(width, GetMinHeight())); - return *this; -} - -pxStaticText& pxStaticText::SetMinHeight(int height) -{ - SetMinSize(wxSize(GetMinWidth(), height)); - return *this; -} - -pxStaticText& pxStaticText::SetHeight(int lines) -{ - if (!pxAssert(lines > 0)) - lines = 2; - m_heightInLines = lines; - - const int newHeight = (pxGetCharHeight(this) * m_heightInLines) + (m_paddingPix_vert * 2); - SetMinSize(wxSize(GetMinWidth(), newHeight)); - - return *this; -} - -pxStaticText& pxStaticText::Align(wxAlignment align) -{ - m_align = align; - return *this; -} - -pxStaticText& pxStaticText::Bold() -{ - wxFont bold(GetFont()); - bold.SetWeight(wxFONTWEIGHT_BOLD); - SetFont(bold); - return *this; -} - -pxStaticText& pxStaticText::Unwrapped() -{ - m_autowrap = false; - UpdateWrapping(false); - return *this; -} - -int pxStaticText::calcPaddingWidth(int newWidth) const -{ - return (int)(newWidth * m_paddingPct_horiz * 2) + (m_paddingPix_horiz * 2); -} - -int pxStaticText::calcPaddingHeight(int newHeight) const -{ - return (int)(newHeight * m_paddingPct_vert * 2) + (m_paddingPix_vert * 2); -} - -// Overloaded form wxPanel and friends. -wxSize pxStaticText::DoGetBestSize() const -{ - wxClientDC dc(const_cast(this)); - dc.SetFont(GetFontOk()); - - wxSize best; - - if (m_autowrap) - { - best = GetBestWrappedSize(dc); - //best.x = wxDefaultCoord; - } - else - { - // No autowrapping, so we can force a specific size here! - best = dc.GetMultiLineTextExtent(GetLabel()); - best.x += calcPaddingWidth(best.x); - } - - best.y += calcPaddingHeight(best.y); - - CacheBestSize(best); - return best; -} - -wxSize pxStaticText::GetBestWrappedSize(const wxClientDC& dc) const -{ - pxAssert(m_autowrap); - - // Find an ideal(-ish) width, based on a search of all parent controls and their - // valid Minimum sizes. - - int idealWidth = wxDefaultCoord; - int parentalAdjust = 0; - double parentalFactor = 1.0; - const wxWindow* millrun = this; - - while (millrun) - { - // IMPORTANT : wxWizard changes its min size and then expects everything else - // to play nice and NOT resize according to the new min size. (wtf stupid) - // Anyway, this fixes it -- ignore min size specifier on wxWizard! - if (wxIsKindOf(millrun, wxWizard)) - break; - - int min = (int)((millrun->GetMinWidth() - parentalAdjust) * parentalFactor); - - if (min > 0 && ((idealWidth < 0) || (min < idealWidth))) - { - idealWidth = min; - } - - parentalAdjust += pxSizerFlags::StdPadding * 2; - millrun = millrun->GetParent(); - } - - if (idealWidth <= 0) - { - // FIXME: The minimum size of this control is unknown, so let's just pick a guess based on - // the size of the user's display area. - - idealWidth = (int)(wxGetDisplaySize().GetWidth() * 0.66) - (parentalAdjust * 2); - } - - return dc.GetMultiLineTextExtent(pxTextWrapper().Wrap(this, m_label, idealWidth - calcPaddingWidth(idealWidth)).GetResult()); -} - -bool pxStaticText::_updateWrapping(bool textChanged) -{ - if (!m_autowrap) - { - //m_wrappedLabel = wxEmptyString; - //m_wrappedWidth = -1; - return false; - } - - wxString wrappedLabel; - int newWidth = GetSize().GetWidth(); - - if (!textChanged && (newWidth == m_wrappedWidth)) - return false; - - // Note: during various stages of sizer-calc, width can be 1, 0, or -1. - // We ignore wrapping in these cases. (the PaintEvent also checks the wrapping - // and updates it if needed, in case the control's size isn't figured out prior - // to being painted). - - m_wrappedWidth = newWidth; - if (m_wrappedWidth > 1) - wrappedLabel = pxTextWrapper().Wrap(this, m_label, m_wrappedWidth).GetResult(); - - if (m_wrappedLabel == wrappedLabel) - return false; - m_wrappedLabel = wrappedLabel; - - return true; -} - -void pxStaticText::UpdateWrapping(bool textChanged) -{ - if (_updateWrapping(textChanged)) - Refresh(); -} - -void pxStaticText::SetLabel(const wxString& label) -{ - const bool labelChanged(label != m_label); - if (labelChanged) - { - m_label = label; - Refresh(); - } - - // Always update wrapping, in case window width or something else also changed. - UpdateWrapping(labelChanged); - InvalidateBestSize(); -} - -wxFont pxStaticText::GetFontOk() const -{ - wxFont font(GetFont()); - if (!font.Ok()) - return wxSystemSettings::GetFont(wxSYS_DEFAULT_GUI_FONT); - return font; -} - -bool pxStaticText::Enable(bool enabled) -{ - if (_parent::Enable(enabled)) - { - Refresh(); - return true; - } - return false; -} - -void pxStaticText::paintEvent(wxPaintEvent& evt) -{ - wxPaintDC dc(this); - const int dcWidth = dc.GetSize().GetWidth(); - const int dcHeight = dc.GetSize().GetHeight(); - if (dcWidth < 1) - return; - dc.SetFont(GetFontOk()); - - if (IsEnabled()) - dc.SetTextForeground(GetForegroundColour()); - else - dc.SetTextForeground(*wxLIGHT_GREY); - - pxWindowTextWriter writer(dc); - writer.Align(m_align); - - const wxString& label(m_autowrap ? m_wrappedLabel : m_label); - if (m_autowrap) - _updateWrapping(false); - - int tWidth, tHeight; - dc.GetMultiLineTextExtent(label, &tWidth, &tHeight); - - writer.Align(m_align); - if (m_align & wxALIGN_CENTER_VERTICAL) - writer.SetY((dcHeight - tHeight) / 2); - else - writer.SetY((int)(dcHeight * m_paddingPct_vert) + m_paddingPix_vert); - - writer.WriteLn(label); // without formatting please. - - //dc.SetBrush( *wxTRANSPARENT_BRUSH ); - //dc.DrawRectangle(wxPoint(), dc.GetSize()); -} - -// -------------------------------------------------------------------------------------- -// pxStaticHeading (implementations) -// -------------------------------------------------------------------------------------- -pxStaticHeading::pxStaticHeading(wxWindow* parent, const wxString& label) - : _parent(parent) -{ - m_align = wxALIGN_CENTER; - - SetPaddingDefaults(); - Init(label); -} - -void pxStaticHeading::SetPaddingDefaults() -{ - m_paddingPix_horiz = 4; - m_paddingPix_vert = 1; - - m_paddingPct_horiz = 0.08f; - m_paddingPct_vert = 0.0f; -} - -void operator+=(wxSizer& target, pxStaticText* src) -{ - if (src) - target.Add(src, pxExpand); -} - -void operator+=(wxSizer& target, pxStaticText& src) -{ - target.Add(&src, pxExpand); -} - -void operator+=(wxSizer* target, pxStaticText& src) -{ - target->Add(&src, pxExpand); -} diff --git a/pcsx2/gui/pxStaticText.h b/pcsx2/gui/pxStaticText.h deleted file mode 100644 index 8aeaedb958..0000000000 --- a/pcsx2/gui/pxStaticText.h +++ /dev/null @@ -1,117 +0,0 @@ -/* PCSX2 - PS2 Emulator for PCs - * Copyright (C) 2002-2010 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 . - */ - -#pragma once - -#include -#include "wxGuiTools.h" - -// -------------------------------------------------------------------------------------- -// pxStaticText -// -------------------------------------------------------------------------------------- -// This class's purpose is to overcome two fundamental annoyances in wxWidgets design: -// -// * An inability to wrap text to conform to a fitted window (a limitation imposed by -// wxWidgets inability to fit individual directions, ie fit widths and then fit heights, -// which would allow a textbox to wrap text to a sizer-determined width, and then grow -// the sizers vertically to fit the calculated text-wrapped height). -// -// * Textbox alignment requires aligning both the textbox contents, and aligning the text -// control within it's containing sizer. If both alignment flags do not match the result -// is typically undesirable. -// -class pxStaticText : public wxControl -{ - typedef wxControl _parent; - -protected: - wxString m_label; - wxString m_wrappedLabel; - - wxAlignment m_align; - bool m_autowrap; - int m_wrappedWidth; - int m_heightInLines; - - int m_paddingPix_horiz; - int m_paddingPix_vert; - float m_paddingPct_horiz; - float m_paddingPct_vert; - -protected: - explicit pxStaticText(wxWindow* parent = NULL); - - // wxWindow overloads! - bool AcceptsFocus() const { return false; } - bool HasTransparentBackground() { return true; } - void DoSetSize(int x, int y, int w, int h, int sizeFlags = wxSIZE_AUTO); - wxSize DoGetBestSize() const; - -public: - pxStaticText(wxWindow* parent, const wxString& label, wxAlignment align = wxALIGN_CENTRE_HORIZONTAL); - pxStaticText(wxWindow* parent, int heightInLines, const wxString& label, wxAlignment align = wxALIGN_CENTRE_HORIZONTAL); - virtual ~pxStaticText() = default; - - wxFont GetFontOk() const; - bool Enable(bool enabled = true); - - virtual void SetLabel(const wxString& label); - virtual wxString GetLabel() const { return m_label; } - - pxStaticText& SetMinWidth(int width); - pxStaticText& SetMinHeight(int height); - - pxStaticText& SetHeight(int lines); - pxStaticText& Align(wxAlignment align); - pxStaticText& Bold(); - - pxStaticText& Unwrapped(); - -protected: - void SetPaddingDefaults(); - void Init(const wxString& label); - - wxSize GetBestWrappedSize(const wxClientDC& dc) const; - - int calcPaddingWidth(int newWidth) const; - int calcPaddingHeight(int newHeight) const; - - void paintEvent(wxPaintEvent& evt); - - void UpdateWrapping(bool textChanged); - bool _updateWrapping(bool textChanged); -}; - - -class pxStaticHeading : public pxStaticText -{ - typedef pxStaticText _parent; - -public: - pxStaticHeading(wxWindow* parent = NULL, const wxString& label = wxEmptyString); - pxStaticHeading(wxWindow* parent, int heightInLines, const wxString& label = wxEmptyString); - virtual ~pxStaticHeading() = default; - -protected: - void SetPaddingDefaults(); -}; - -extern void operator+=(wxSizer& target, pxStaticText& src); - -template <> -inline void operator+=(wxSizer& target, const pxWindowAndFlags& src) -{ - target.Add(src.window, src.flags); -} diff --git a/pcsx2/gui/pxTranslate.cpp b/pcsx2/gui/pxTranslate.cpp deleted file mode 100644 index cb7c23daaf..0000000000 --- a/pcsx2/gui/pxTranslate.cpp +++ /dev/null @@ -1,35 +0,0 @@ -/* PCSX2 - PS2 Emulator for PCs - * Copyright (C) 2002-2010 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 . - */ - -#include - -bool pxIsEnglish(int id) -{ - return (id == wxLANGUAGE_ENGLISH || id == wxLANGUAGE_ENGLISH_US); -} - -// -------------------------------------------------------------------------------------- -// pxExpandMsg -- an Iconized Text Translator -// Was replaced by a standard implementation of wxGetTranslation -// -------------------------------------------------------------------------------------- -const wxChar* pxExpandMsg(const wxChar* message) -{ - return wxGetTranslation(message).wc_str(); -} - -const wxChar* pxGetTranslation(const wxChar* message) -{ - return wxGetTranslation(message).wc_str(); -} diff --git a/pcsx2/gui/pxWindowTextWriter.cpp b/pcsx2/gui/pxWindowTextWriter.cpp deleted file mode 100644 index 17778eea5a..0000000000 --- a/pcsx2/gui/pxWindowTextWriter.cpp +++ /dev/null @@ -1,146 +0,0 @@ -/* PCSX2 - PS2 Emulator for PCs - * Copyright (C) 2002-2010 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 . - */ - -#include "PrecompiledHeader.h" - -#include -#include "gui/StringHelpers.h" -#include "gui/wxGuiTools.h" - -// -------------------------------------------------------------------------------------- -// pxWindowTextWriter Implementations -// -------------------------------------------------------------------------------------- -pxWindowTextWriter::pxWindowTextWriter(wxDC& dc) - : m_dc(dc) -{ - m_curpos = wxPoint(); - m_align = wxALIGN_CENTER; - m_leading = 0; - - OnFontChanged(); -} - -void pxWindowTextWriter::OnFontChanged() -{ -} - -pxWindowTextWriter& pxWindowTextWriter::SetWeight(wxFontWeight weight) -{ - wxFont curfont(m_dc.GetFont()); - curfont.SetWeight(weight); - m_dc.SetFont(curfont); - - return *this; -} - -pxWindowTextWriter& pxWindowTextWriter::SetStyle(wxFontStyle style) -{ - wxFont curfont(m_dc.GetFont()); - curfont.SetStyle(style); - m_dc.SetFont(curfont); - return *this; -} - -pxWindowTextWriter& pxWindowTextWriter::Normal() -{ - wxFont curfont(m_dc.GetFont()); - curfont.SetStyle(wxFONTSTYLE_NORMAL); - curfont.SetWeight(wxFONTWEIGHT_NORMAL); - m_dc.SetFont(curfont); - - return *this; -} - -pxWindowTextWriter& pxWindowTextWriter::SetPos(const wxPoint& pos) -{ - m_curpos = pos; - return *this; -} - -pxWindowTextWriter& pxWindowTextWriter::MovePos(const wxSize& delta) -{ - m_curpos += delta; - return *this; -} - -pxWindowTextWriter& pxWindowTextWriter::SetY(int ypos) -{ - m_curpos.y = ypos; - return *this; -} - -void pxWindowTextWriter::_DoWriteLn(const wxString& msg) -{ - int tWidth, tHeight; - m_dc.GetMultiLineTextExtent(msg, &tWidth, &tHeight); - - wxPoint dispos(m_curpos); - - if (m_align & wxALIGN_CENTER_HORIZONTAL) - { - dispos.x = (m_dc.GetSize().GetWidth() - tWidth) / 2; - } - else if (m_align & wxALIGN_RIGHT) - { - dispos.x = m_dc.GetSize().GetWidth() - tWidth; - } - - m_dc.DrawText(msg, dispos); - m_curpos.y += tHeight + m_leading; -} - -// Splits incoming multi-line strings into pieces, and dispatches each line individually -// to the text writer. -void pxWindowTextWriter::_DoWrite(const wxChar* msg) -{ - pxAssert(msg); - - wxArrayString parts; - SplitString(parts, msg, L'\n'); - - for (size_t i = 0; i < parts.GetCount(); ++i) - _DoWriteLn(parts[i]); -} - -pxWindowTextWriter& pxWindowTextWriter::SetFont(const wxFont& font) -{ - m_dc.SetFont(font); - return *this; -} - -pxWindowTextWriter& pxWindowTextWriter::Align(const wxAlignment& align) -{ - m_align = align; - m_curpos.x = 0; - return *this; -} - -pxWindowTextWriter& pxWindowTextWriter::WriteLn() -{ - _DoWriteLn(L""); - return *this; -} - -pxWindowTextWriter& pxWindowTextWriter::WriteLn(const wxChar* fmt) -{ - _DoWrite(fmt); - return *this; -} - -pxWindowTextWriter& pxWindowTextWriter::WriteLn(const wxString fmt) -{ - _DoWrite(fmt.wc_str()); - return *this; -} diff --git a/pcsx2/gui/wxAppWithHelpers.cpp b/pcsx2/gui/wxAppWithHelpers.cpp deleted file mode 100644 index 02a6367ad7..0000000000 --- a/pcsx2/gui/wxAppWithHelpers.cpp +++ /dev/null @@ -1,740 +0,0 @@ -/* PCSX2 - PS2 Emulator for PCs - * Copyright (C) 2002-2010 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 . - */ - -#include "PrecompiledHeader.h" -#include "gui/wxAppWithHelpers.h" -#include "gui/PersistentThread.h" - -wxDEFINE_EVENT(pxEvt_StartIdleEventTimer, wxCommandEvent); -wxDEFINE_EVENT(pxEvt_DeleteObject, wxCommandEvent); -wxDEFINE_EVENT(pxEvt_DeleteThread, wxCommandEvent); -wxDEFINE_EVENT(pxEvt_InvokeAction, pxActionEvent); -wxDEFINE_EVENT(pxEvt_SynchronousCommand, pxSynchronousCommandEvent); - -wxIMPLEMENT_DYNAMIC_CLASS(pxSimpleEvent, wxEvent); - -ConsoleLogSource_App::ConsoleLogSource_App() -{ - static const TraceLogDescriptor myDesc = - { - "AppEvents", "App Events", - "Includes idle event processing and some other uncommon event usages."}; - - m_Descriptor = &myDesc; -} - -ConsoleLogSource_App pxConLog_App; - -// -------------------------------------------------------------------------------------- -// BaseDeletableObject Implementation -// -------------------------------------------------------------------------------------- -// This code probably deserves a better home. It's general purpose non-GUI code (the single -// wxApp/Gui dependency is in wxGuiTools.cpp for now). -// -bool BaseDeletableObject::MarkForDeletion() -{ - return !m_IsBeingDeleted.exchange(true); -} - -void BaseDeletableObject::DeleteSelf() -{ - if (MarkForDeletion()) - DoDeletion(); -} - -BaseDeletableObject::BaseDeletableObject() -{ -#ifdef _MSC_VER - // Bleh, this fails because _CrtIsValidHeapPointer calls HeapValidate on the - // pointer, but the pointer is a virtual base class, so it's not a valid block. >_< - //pxAssertDev( _CrtIsValidHeapPointer( this ), "BaseDeletableObject types cannot be created on the stack or as temporaries!" ); -#endif - - m_IsBeingDeleted.store(false, std::memory_order_relaxed); -} - -BaseDeletableObject::~BaseDeletableObject() -{ - AffinityAssert_AllowFrom_MainUI(); -} - -void BaseDeletableObject::DoDeletion() -{ - wxAppWithHelpers* app = wxDynamicCast(wxApp::GetInstance(), wxAppWithHelpers); - pxAssert(app != NULL); - app->DeleteObject(*this); -} - -// -------------------------------------------------------------------------------------- -// SynchronousActionState Implementations -// -------------------------------------------------------------------------------------- - -void SynchronousActionState::SetException(const BaseException& ex) -{ - m_exception = ScopedExcept(ex.Clone()); -} - -void SynchronousActionState::SetException(BaseException* ex) -{ - if (!m_posted) - { - m_exception = ScopedExcept(ex); - } - else if (wxTheApp) - { - // transport the exception to the main thread, since the message is fully - // asynchronous, or has already entered an asynchronous state. Message is sent - // as a non-blocking action since proper handling of user errors on async messages - // is *usually* to log/ignore it (hah), or to suspend emulation and issue a dialog - // box to the user. - - pxExceptionEvent ev(ex); - wxTheApp->AddPendingEvent(ev); - } -} - -void SynchronousActionState::RethrowException() const -{ - if (m_exception) - m_exception->Rethrow(); -} - -int SynchronousActionState::WaitForResult() -{ - m_sema.WaitNoCancel(); - RethrowException(); - return return_value; -} - -int SynchronousActionState::WaitForResult_NoExceptions() -{ - m_sema.WaitNoCancel(); - return return_value; -} - -void SynchronousActionState::PostResult(int res) -{ - return_value = res; - PostResult(); -} - -void SynchronousActionState::ClearResult() -{ - m_posted = false; - m_exception = NULL; -} - -void SynchronousActionState::PostResult() -{ - if (m_posted) - return; - m_posted = true; - m_sema.Post(); -} - -// -------------------------------------------------------------------------------------- -// pxActionEvent Implementations -// -------------------------------------------------------------------------------------- - -wxIMPLEMENT_DYNAMIC_CLASS(pxActionEvent, wxEvent); - -pxActionEvent::pxActionEvent(SynchronousActionState* sema, int msgtype) - : wxEvent(0, msgtype) -{ - m_state = sema; -} - -pxActionEvent::pxActionEvent(SynchronousActionState& sema, int msgtype) - : wxEvent(0, msgtype) -{ - m_state = &sema; -} - -pxActionEvent::pxActionEvent(const pxActionEvent& src) - : wxEvent(src) -{ - m_state = src.m_state; -} - -void pxActionEvent::SetException(const BaseException& ex) -{ - SetException(ex.Clone()); -} - -void pxActionEvent::SetException(BaseException* ex) -{ - ex->DiagMsg() = StringUtil::wxStringToUTF8String(GetClassInfo()->GetClassName()) + ex->DiagMsg(); - - if (!m_state) - { - ScopedExcept exptr(ex); // auto-delete it after handling. - ex->Rethrow(); - } - - m_state->SetException(ex); -} - -// -------------------------------------------------------------------------------------- -// pxSynchronousCommandEvent -// -------------------------------------------------------------------------------------- -wxIMPLEMENT_DYNAMIC_CLASS(pxSynchronousCommandEvent, wxCommandEvent); - -pxSynchronousCommandEvent::pxSynchronousCommandEvent(SynchronousActionState* sema, wxEventType commandType, int winid) - : wxCommandEvent(pxEvt_SynchronousCommand, winid) -{ - m_sync = sema; - m_realEvent = commandType; -} - -pxSynchronousCommandEvent::pxSynchronousCommandEvent(SynchronousActionState& sema, wxEventType commandType, int winid) - : wxCommandEvent(pxEvt_SynchronousCommand) -{ - m_sync = &sema; - m_realEvent = commandType; -} - -pxSynchronousCommandEvent::pxSynchronousCommandEvent(SynchronousActionState* sema, const wxCommandEvent& evt) - : wxCommandEvent(evt) -{ - m_sync = sema; - m_realEvent = evt.GetEventType(); - SetEventType(pxEvt_SynchronousCommand); -} - -pxSynchronousCommandEvent::pxSynchronousCommandEvent(SynchronousActionState& sema, const wxCommandEvent& evt) - : wxCommandEvent(evt) -{ - m_sync = &sema; - m_realEvent = evt.GetEventType(); - SetEventType(pxEvt_SynchronousCommand); -} - -pxSynchronousCommandEvent::pxSynchronousCommandEvent(const pxSynchronousCommandEvent& src) - : wxCommandEvent(src) -{ - m_sync = src.m_sync; - m_realEvent = src.m_realEvent; -} - -void pxSynchronousCommandEvent::SetException(const BaseException& ex) -{ - if (!m_sync) - ex.Rethrow(); - m_sync->SetException(ex); -} - -void pxSynchronousCommandEvent::SetException(BaseException* ex) -{ - if (!m_sync) - { - ScopedExcept exptr(ex); // auto-delete it after handling. - ex->Rethrow(); - } - - m_sync->SetException(ex); -} - -// -------------------------------------------------------------------------------------- -// pxRpcEvent -// -------------------------------------------------------------------------------------- -// Unlike pxPingEvent, the Semaphore belonging to this event is typically posted when the -// invoked method is completed. If the method can be executed in non-blocking fashion then -// it should leave the semaphore postback NULL. -// -class pxRpcEvent : public pxActionEvent -{ - wxDECLARE_DYNAMIC_CLASS_NO_ASSIGN(pxRpcEvent); - - typedef pxActionEvent _parent; - -protected: - void (*m_Method)(); - -public: - virtual ~pxRpcEvent() = default; - virtual pxRpcEvent* Clone() const { return new pxRpcEvent(*this); } - - explicit pxRpcEvent(void (*method)() = NULL, SynchronousActionState* sema = NULL) - : pxActionEvent(sema) - { - m_Method = method; - } - - explicit pxRpcEvent(void (*method)(), SynchronousActionState& sema) - : pxActionEvent(sema) - { - m_Method = method; - } - - pxRpcEvent(const pxRpcEvent& src) - : pxActionEvent(src) - { - m_Method = src.m_Method; - } - - void SetMethod(void (*method)()) - { - m_Method = method; - } - -protected: - void InvokeEvent() - { - if (m_Method) - m_Method(); - } -}; - -wxIMPLEMENT_DYNAMIC_CLASS(pxRpcEvent, pxActionEvent); - -// -------------------------------------------------------------------------------------- -// pxExceptionEvent implementations -// -------------------------------------------------------------------------------------- -pxExceptionEvent::pxExceptionEvent(const BaseException& ex) -{ - m_except = ex.Clone(); -} - -void pxExceptionEvent::InvokeEvent() -{ - ScopedExcept deleteMe(m_except); - if (deleteMe) - deleteMe->Rethrow(); -} - -// -------------------------------------------------------------------------------------- -// wxAppWithHelpers Implementation -// -------------------------------------------------------------------------------------- -// -// TODO : Ping dispatch and IdleEvent dispatch can be unified into a single dispatch, which -// would mean checking only one list of events per idle event, instead of two. (ie, ping -// events can be appended to the idle event list, instead of into their own custom list). -// -wxIMPLEMENT_DYNAMIC_CLASS(wxAppWithHelpers, wxApp); - - -// Posts a method to the main thread; non-blocking. Post occurs even when called from the -// main thread. -void wxAppWithHelpers::PostMethod(FnType_Void* method) -{ - PostEvent(pxRpcEvent(method)); -} - -// Posts a method to the main thread; non-blocking. Post occurs even when called from the -// main thread. -void wxAppWithHelpers::PostIdleMethod(FnType_Void* method) -{ - pxRpcEvent evt(method); - AddIdleEvent(evt); -} - -// Invokes the specified void method, or posts the method to the main thread if the calling -// thread is not Main. Action is blocking. For non-blocking method execution, use -// AppRpc_TryInvokeAsync. -// -// This function works something like setjmp/longjmp, in that the return value indicates if the -// function actually executed the specified method or not. -// -// Returns: -// FALSE if the method was not invoked (meaning this IS the main thread!) -// TRUE if the method was invoked. -// - -bool wxAppWithHelpers::Rpc_TryInvoke(FnType_Void* method) -{ - if (wxThread::IsMain()) - return false; - - SynchronousActionState sync; - PostEvent(pxRpcEvent(method, sync)); - sync.WaitForResult(); - - return true; -} - -// Invokes the specified void method, or posts the method to the main thread if the calling -// thread is not Main. Action is non-blocking (asynchronous). For blocking method execution, -// use AppRpc_TryInvoke. -// -// This function works something like setjmp/longjmp, in that the return value indicates if the -// function actually executed the specified method or not. -// -// Returns: -// FALSE if the method was not posted to the main thread (meaning this IS the main thread!) -// TRUE if the method was posted. -// -bool wxAppWithHelpers::Rpc_TryInvokeAsync(FnType_Void* method) -{ - if (wxThread::IsMain()) - return false; - PostEvent(pxRpcEvent(method)); - return true; -} - -void wxAppWithHelpers::ProcessMethod(FnType_Void* method) -{ - if (wxThread::IsMain()) - { - method(); - return; - } - - SynchronousActionState sync; - PostEvent(pxRpcEvent(method, sync)); - sync.WaitForResult(); -} - -void wxAppWithHelpers::PostEvent(const wxEvent& evt) -{ - // Const Cast is OK! - // Truth is, AddPendingEvent should be a const-qualified parameter, as - // it makes an immediate clone copy of the event -- but wxWidgets - // fails again in structured C/C++ design design. So I'm forcing it as such - // here. -- air - - _parent::AddPendingEvent(const_cast(evt)); -} - -bool wxAppWithHelpers::ProcessEvent(wxEvent& evt) -{ - // Note: We can't do an automatic blocking post of the message here, because wxWidgets - // isn't really designed for it (some events return data to the caller via the event - // struct, and posting the event would require a temporary clone, where changes would - // be lost). - - AffinityAssert_AllowFrom_MainUI(); - return _parent::ProcessEvent(evt); -} - -bool wxAppWithHelpers::ProcessEvent(wxEvent* evt) -{ - AffinityAssert_AllowFrom_MainUI(); - std::unique_ptr deleteMe(evt); - return _parent::ProcessEvent(*deleteMe); -} - -bool wxAppWithHelpers::ProcessEvent(pxActionEvent& evt) -{ - if (wxThread::IsMain()) - return _parent::ProcessEvent(evt); - else - { - SynchronousActionState sync; - evt.SetSyncState(sync); - AddPendingEvent(evt); - sync.WaitForResult(); - return true; - } -} - -bool wxAppWithHelpers::ProcessEvent(pxActionEvent* evt) -{ - if (wxThread::IsMain()) - { - std::unique_ptr deleteMe(evt); - return _parent::ProcessEvent(*deleteMe); - } - else - { - SynchronousActionState sync; - evt->SetSyncState(sync); - AddPendingEvent(*evt); - sync.WaitForResult(); - return true; - } -} - - -void wxAppWithHelpers::CleanUp() -{ - // I'm pretty sure the message pump is dead by now, which means we need to run through - // idle event list by hand and process the pending Deletion messages (all others can be - // ignored -- it's only deletions we want handled, and really we *could* ignore those too - // but I like to be tidy. -- air - - //IdleEventDispatcher( "CleanUp" ); - //DeletionDispatcher(); - _parent::CleanUp(); -} - -// Executes the event with exception handling. If the event throws an exception, the exception -// will be neatly packaged and transported back to the thread that posted the event. -// This function is virtual, however overloading it is not recommended. Derrived classes -// should overload InvokeEvent() instead. -void pxActionEvent::_DoInvokeEvent() -{ - AffinityAssert_AllowFrom_MainUI(); - - try - { - InvokeEvent(); - } - catch (BaseException& ex) - { - SetException(ex); - } - catch (std::runtime_error& ex) - { - SetException(new Exception::RuntimeError(ex)); - } - - if (m_state) - m_state->PostResult(); -} - -void wxAppWithHelpers::OnSynchronousCommand(pxSynchronousCommandEvent& evt) -{ - AffinityAssert_AllowFrom_MainUI(); - - pxAppLog.Write("(App) Executing command event synchronously..."); - evt.SetEventType(evt.GetRealEventType()); - - try - { - ProcessEvent(evt); - } - catch (BaseException& ex) - { - evt.SetException(ex); - } - catch (std::runtime_error& ex) - { - evt.SetException(new Exception::RuntimeError(ex, StringUtil::wxStringToUTF8String(evt.GetClassInfo()->GetClassName()).c_str())); - } - - if (Semaphore* sema = evt.GetSemaphore()) - sema->Post(); -} - -void wxAppWithHelpers::AddIdleEvent(const wxEvent& evt) -{ - ScopedLock lock(m_IdleEventMutex); - if (m_IdleEventQueue.empty()) - PostEvent(wxCommandEvent(pxEvt_StartIdleEventTimer)); - - m_IdleEventQueue.push_back(evt.Clone()); -} - -void wxAppWithHelpers::OnStartIdleEventTimer(wxCommandEvent& evt) -{ - ScopedLock lock(m_IdleEventMutex); - if (!m_IdleEventQueue.empty()) - m_IdleEventTimer.Start(100, true); -} - -void wxAppWithHelpers::IdleEventDispatcher(const wxChar* action) -{ - // Recursion is possible thanks to modal dialogs being issued from the idle event handler. - // (recursion shouldn't hurt anything anyway, since the node system re-creates the iterator - // on each pass) - - //static int __guard=0; - //RecursionGuard guard(__guard); - //if( !pxAssertDev(!guard.IsReentrant(), "Re-entrant call to IdleEventdispatcher caught on camera!") ) return; - - wxEventList postponed; - wxEventList::iterator node; - - ScopedLock lock(m_IdleEventMutex); - - while (node = m_IdleEventQueue.begin(), node != m_IdleEventQueue.end()) - { - std::unique_ptr deleteMe(*node); - m_IdleEventQueue.erase(node); - - lock.Release(); - if (!Threading::AllowDeletions() && (deleteMe->GetEventType() == pxEvt_DeleteThread)) - { - // Threads that have active semaphores or mutexes (other threads are waiting on them) cannot - // be deleted because those mutex/sema objects will become invalid and cause the pending - // thread to crash. So we disallow deletions when those waits are in action, and continue - // to postpone the deletion of the thread until such time that it is safe. - - pxThreadLog.Write(((pxThread*)((wxCommandEvent*)deleteMe.get())->GetClientData())->GetName(), L"Deletion postponed due to mutex or semaphore dependency."); - postponed.push_back(deleteMe.release()); - } - else - { - pxAppLog.Write("(AppIdleQueue%s) Dispatching event '%ls'", action, deleteMe->GetClassInfo()->GetClassName()); - ProcessEvent(*deleteMe); // dereference to prevent auto-deletion by ProcessEvent - } - lock.Acquire(); - } - - m_IdleEventQueue = postponed; - if (!m_IdleEventQueue.empty()) - pxAppLog.Write("(AppIdleQueue%s) %d events postponed due to dependencies.", action, m_IdleEventQueue.size()); -} - -void wxAppWithHelpers::OnIdleEvent(wxIdleEvent& evt) -{ - m_IdleEventTimer.Stop(); - IdleEventDispatcher(); -} - -void wxAppWithHelpers::OnIdleEventTimeout(wxTimerEvent& evt) -{ - IdleEventDispatcher(L"[Timeout]"); -} - -void wxAppWithHelpers::Ping() -{ - pxThreadLog.Write(pxGetCurrentThreadName().c_str(), L"App Event Ping Requested."); - - SynchronousActionState sync; - pxActionEvent evt(sync); - AddIdleEvent(evt); - sync.WaitForResult(); -} - -void wxAppWithHelpers::PostCommand(void* clientData, int evtType, int intParam, long longParam, const wxString& stringParam) -{ - wxCommandEvent evt(evtType); - evt.SetClientData(clientData); - evt.SetInt(intParam); - evt.SetExtraLong(longParam); - evt.SetString(stringParam); - AddPendingEvent(evt); -} - -void wxAppWithHelpers::PostCommand(int evtType, int intParam, long longParam, const wxString& stringParam) -{ - PostCommand(NULL, evtType, intParam, longParam, stringParam); -} - -sptr wxAppWithHelpers::ProcessCommand(void* clientData, int evtType, int intParam, long longParam, const wxString& stringParam) -{ - SynchronousActionState sync; - pxSynchronousCommandEvent evt(sync, evtType); - - evt.SetClientData(clientData); - evt.SetInt(intParam); - evt.SetExtraLong(longParam); - evt.SetString(stringParam); - AddPendingEvent(evt); - sync.WaitForResult(); - - return sync.return_value; -} - -sptr wxAppWithHelpers::ProcessCommand(int evtType, int intParam, long longParam, const wxString& stringParam) -{ - return ProcessCommand(NULL, evtType, intParam, longParam, stringParam); -} - -void wxAppWithHelpers::PostAction(const pxActionEvent& evt) -{ - PostEvent(evt); -} - -void wxAppWithHelpers::ProcessAction(pxActionEvent& evt) -{ - if (!wxThread::IsMain()) - { - SynchronousActionState sync; - evt.SetSyncState(sync); - AddPendingEvent(evt); - sync.WaitForResult(); - } - else - evt._DoInvokeEvent(); -} - - -void wxAppWithHelpers::DeleteObject(BaseDeletableObject& obj) -{ - pxAssert(!obj.IsBeingDeleted()); - wxCommandEvent evt(pxEvt_DeleteObject); - evt.SetClientData((void*)&obj); - AddIdleEvent(evt); -} - -void wxAppWithHelpers::DeleteThread(pxThread& obj) -{ - pxThreadLog.Write(obj.GetName(), L"Scheduling for deletion..."); - wxCommandEvent evt(pxEvt_DeleteThread); - evt.SetClientData((void*)&obj); - AddIdleEvent(evt); -} - -bool wxAppWithHelpers::OnInit() -{ - Bind(pxEvt_SynchronousCommand, &wxAppWithHelpers::OnSynchronousCommand, this); - Bind(pxEvt_InvokeAction, &wxAppWithHelpers::OnInvokeAction, this); - - Bind(pxEvt_StartIdleEventTimer, &wxAppWithHelpers::OnStartIdleEventTimer, this); - - Bind(pxEvt_DeleteObject, &wxAppWithHelpers::OnDeleteObject, this); - Bind(pxEvt_DeleteThread, &wxAppWithHelpers::OnDeleteThread, this); - - Bind(wxEVT_IDLE, &wxAppWithHelpers::OnIdleEvent, this); - - Bind(wxEVT_TIMER, &wxAppWithHelpers::OnIdleEventTimeout, this, m_IdleEventTimer.GetId()); - - return _parent::OnInit(); -} - -void wxAppWithHelpers::OnInvokeAction(pxActionEvent& evt) -{ - evt._DoInvokeEvent(); // wow this is easy! -} - -void wxAppWithHelpers::OnDeleteObject(wxCommandEvent& evt) -{ - if (evt.GetClientData() == NULL) - return; - delete (BaseDeletableObject*)evt.GetClientData(); -} - -// In theory we create a Pcsx2App object which inherit from wxAppWithHelpers, -// so Pcsx2App::CreateTraits must be used instead. -// -// However it doesn't work this way because wxAppWithHelpers constructor will -// be called first. This constructor will build some wx objects (here wxTimer) -// that require a trait. In others word, wxAppWithHelpers::CreateTraits will be -// called instead -wxAppTraits* wxAppWithHelpers::CreateTraits() -{ - return new Pcsx2AppTraits; -} - -// Threads have their own deletion handler that propagates exceptions thrown by the thread to the UI. -// (thus we have a fairly automatic threaded exception system!) -void wxAppWithHelpers::OnDeleteThread(wxCommandEvent& evt) -{ - std::unique_ptr thr((pxThread*)evt.GetClientData()); - if (!thr) - { - pxThreadLog.Write(L"null", L"OnDeleteThread: NULL thread object received (and ignored)."); - return; - } - - pxThreadLog.Write(thr->GetName(), wxString(wxString(L"Thread object deleted successfully") + (thr->HasPendingException() ? L" [exception pending!]" : L"")).wc_str()); - thr->RethrowException(); -} - -wxAppWithHelpers::wxAppWithHelpers() - : m_IdleEventTimer(this) -{ -#ifdef __WXMSW__ - // This variable assignment ensures that MSVC links in the TLS setup stubs even in - // full optimization builds. Without it, DLLs that use TLS won't work because the - // FS segment register won't have been initialized by the main exe, due to tls_insurance - // being optimized away >_< --air - - static thread_local int tls_insurance = 0; - tls_insurance = 1; -#endif -} diff --git a/pcsx2/gui/wxAppWithHelpers.h b/pcsx2/gui/wxAppWithHelpers.h deleted file mode 100644 index 31d4e4a234..0000000000 --- a/pcsx2/gui/wxAppWithHelpers.h +++ /dev/null @@ -1,193 +0,0 @@ -/* PCSX2 - PS2 Emulator for PCs - * Copyright (C) 2002-2010 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 . - */ - -#pragma once - -#include - -#include "common/Threading.h" -#include "gui/pxEvents.h" -#include "gui/wxGuiTools.h" -#include "gui/AppTrait.h" - -using namespace Threading; - -class pxSynchronousCommandEvent; - -// -------------------------------------------------------------------------------------- -// BaseDeletableObject -// -------------------------------------------------------------------------------------- -// Oh the fruits and joys of multithreaded C++ coding conundrums! This class provides a way -// to be deleted from arbitraty threads, or to delete themselves (which is considered unsafe -// in C++, though it does typically work). It also gives objects a second recourse for -// doing fully virtualized cleanup, something C++ also makes impossible because of how it -// implements it's destructor hierarchy. -// -// To utilize virtual destruction, override DoDeletion() and be sure to invoke the base class -// implementation of DoDeletion(). -// -// Assertions: -// This class generates an assertion of the destructor is called from anything other than -// the main/gui thread. -// -// Rationale: -// wxWidgets provides a pending deletion feature, but it's specific to wxCore (not wxBase) -// which means it requires wxApp and all that, which is bad for plugins and the possibility -// of linking PCSX2 core against a non-WX gui in the future. It's also not thread safe -// (sigh). And, finally, it requires quite a bit of red tape to implement wxObjects because -// of the wx-custom runtime type information. So I made my own. -// -class BaseDeletableObject -{ -protected: - std::atomic m_IsBeingDeleted; - -public: - BaseDeletableObject(); - virtual ~BaseDeletableObject(); - - virtual void DeleteSelf(); - virtual bool IsBeingDeleted() { return !!m_IsBeingDeleted; } - - // Returns FALSE if the object is already marked for deletion, or TRUE if the app - // should schedule the object for deletion. Only schedule if TRUE is returned, otherwise - // the object could get deleted twice if two threads try to schedule it at the same time. - bool MarkForDeletion(); - -protected: - // This function is GUI implementation dependent! It's implemented by PCSX2's AppHost, - // but if the SysCore is being linked to another front end, you'll need to implement this - // yourself. Most GUIs have built in message pumps. If a platform lacks one then you'll - // need to implement one yourself (yay?). - virtual void DoDeletion(); -}; - -// -------------------------------------------------------------------------------------- -// pxAppLog / ConsoleLogSource_App -// -------------------------------------------------------------------------------------- - -class ConsoleLogSource_App : public ConsoleLogSource -{ - typedef ConsoleLogSource _parent; - -public: - ConsoleLogSource_App(); -}; - -extern ConsoleLogSource_App pxConLog_App; - -#define pxAppLog pxConLog_App.IsActive() && pxConLog_App - - -// -------------------------------------------------------------------------------------- -// ModalButtonPanel -// -------------------------------------------------------------------------------------- -class ModalButtonPanel : public wxPanelWithHelpers -{ -public: - ModalButtonPanel(wxWindow* window, const MsgButtons& buttons); - virtual ~ModalButtonPanel() = default; - - virtual void AddActionButton(wxWindowID id); - virtual void AddCustomButton(wxWindowID id, const wxString& label); - - virtual void OnActionButtonClicked(wxCommandEvent& evt); -}; - -typedef std::list wxEventList; - -// -------------------------------------------------------------------------------------- -// wxAppWithHelpers -// -------------------------------------------------------------------------------------- -class wxAppWithHelpers : public wxApp -{ - typedef wxApp _parent; - - wxDECLARE_DYNAMIC_CLASS(wxAppWithHelpers); - -protected: - wxEventList m_IdleEventQueue; - Threading::MutexRecursive m_IdleEventMutex; - wxTimer m_IdleEventTimer; - -public: - wxAppWithHelpers(); - virtual ~wxAppWithHelpers() {} - - wxAppTraits* CreateTraits(); - - void CleanUp(); - - void DeleteObject(BaseDeletableObject& obj); - void DeleteObject(BaseDeletableObject* obj) - { - if (obj == NULL) - return; - DeleteObject(*obj); - } - - void DeleteThread(Threading::pxThread& obj); - void DeleteThread(Threading::pxThread* obj) - { - if (obj == NULL) - return; - DeleteThread(*obj); - } - - void PostCommand(void* clientData, int evtType, int intParam = 0, long longParam = 0, const wxString& stringParam = wxEmptyString); - void PostCommand(int evtType, int intParam = 0, long longParam = 0, const wxString& stringParam = wxEmptyString); - void PostMethod(FnType_Void* method); - void PostIdleMethod(FnType_Void* method); - void ProcessMethod(FnType_Void* method); - - bool Rpc_TryInvoke(FnType_Void* method); - bool Rpc_TryInvokeAsync(FnType_Void* method); - - sptr ProcessCommand(void* clientData, int evtType, int intParam = 0, long longParam = 0, const wxString& stringParam = wxEmptyString); - sptr ProcessCommand(int evtType, int intParam = 0, long longParam = 0, const wxString& stringParam = wxEmptyString); - - void ProcessAction(pxActionEvent& evt); - void PostAction(const pxActionEvent& evt); - - void Ping(); - bool OnInit(); - //int OnExit(); - - void AddIdleEvent(const wxEvent& evt); - - void PostEvent(const wxEvent& evt); - bool ProcessEvent(wxEvent& evt); - bool ProcessEvent(wxEvent* evt); - - bool ProcessEvent(pxActionEvent& evt); - bool ProcessEvent(pxActionEvent* evt); - -protected: - void IdleEventDispatcher(const wxChar* action = wxEmptyString); - - void OnIdleEvent(wxIdleEvent& evt); - void OnStartIdleEventTimer(wxCommandEvent& evt); - void OnIdleEventTimeout(wxTimerEvent& evt); - void OnDeleteObject(wxCommandEvent& evt); - void OnDeleteThread(wxCommandEvent& evt); - void OnSynchronousCommand(pxSynchronousCommandEvent& evt); - void OnInvokeAction(pxActionEvent& evt); -}; - -namespace Msgbox -{ - extern int ShowModal(BaseMessageBoxEvent& evt); - extern int ShowModal(const wxString& title, const wxString& content, const MsgButtons& buttons); -} // namespace Msgbox diff --git a/pcsx2/gui/wxDirName.h b/pcsx2/gui/wxDirName.h deleted file mode 100644 index 3083ae8f00..0000000000 --- a/pcsx2/gui/wxDirName.h +++ /dev/null @@ -1,234 +0,0 @@ -/* PCSX2 - PS2 Emulator for PCs - * Copyright (C) 2002-2010 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 . - */ - -#pragma once - -#include "common/Pcsx2Defs.h" - -#include - -#define g_MaxPath 255 // 255 is safer with antiquated Win32 ASCII APIs. - -// -------------------------------------------------------------------------------------- -// wxDirName -// -------------------------------------------------------------------------------------- -class wxDirName : protected wxFileName -{ -public: - explicit wxDirName(const wxFileName& src) - { - Assign(src.GetPath(), wxEmptyString); - } - - wxDirName() - : wxFileName() - { - } - wxDirName(const wxDirName& src) - : wxFileName(src) - { - } - explicit wxDirName(const char* src) { Assign(wxString(src, wxMBConvUTF8())); } - explicit wxDirName(const wxString& src) { Assign(src); } - - // ------------------------------------------------------------------------ - void Assign(const wxString& volume, const wxString& path) - { - wxFileName::Assign(volume, path, wxEmptyString); - } - - void Assign(const wxString& path) - { - wxFileName::Assign(path, wxEmptyString); - } - - void Assign(const wxDirName& path) - { - wxFileName::Assign(path); - } - - void Clear() { wxFileName::Clear(); } - - wxCharBuffer ToUTF8() const { return GetPath().ToUTF8(); } - wxCharBuffer ToAscii() const { return GetPath().ToAscii(); } - wxString ToString() const { return GetPath(); } - - // ------------------------------------------------------------------------ - bool IsWritable() const { return IsDirWritable(); } - bool IsReadable() const { return IsDirReadable(); } - bool Exists() const { return DirExists(); } - bool FileExists() const { return wxFileName::FileExists(); } - bool IsOk() const { return wxFileName::IsOk(); } - bool IsRelative() const { return wxFileName::IsRelative(); } - bool IsAbsolute() const { return wxFileName::IsAbsolute(); } - - bool SameAs(const wxDirName& filepath) const - { - return wxFileName::SameAs(filepath); - } - - //Returns true if the file is somewhere inside this directory (and both file and directory are not relative). - bool IsContains(const wxFileName& file) const - { - if (this->IsRelative() || file.IsRelative()) - return false; - - wxFileName f(file); - - while (1) - { - if (this->SameAs(wxDirName(f.GetPath()))) - return true; - - if (f.GetDirCount() == 0) - return false; - - f.RemoveLastDir(); - } - - return false; - } - - bool IsContains(const wxDirName& dir) const - { - return IsContains((wxFileName)dir); - } - - - //Auto relative works as follows: - // 1. if either base or subject are relative, return subject (should never be used with relative paths). - // 2. else if subject is somewhere inside base folder, then result is subject relative to base. - // 3. (windows only, implicitly) else if subject is on the same driveletter as base, result is absolute path of subject without the driveletter. - // 4. else, result is absolute path of subject. - // - // returns ok if both this and base are absolute paths. - static wxString MakeAutoRelativeTo(const wxFileName _subject, const wxString& pathbase) - { - wxFileName subject(_subject); - wxDirName base(pathbase); - if (base.IsRelative() || subject.IsRelative()) - return subject.GetFullPath(); - - wxString bv(base.GetVolume()); - bv.MakeUpper(); - wxString sv(subject.GetVolume()); - sv.MakeUpper(); - - if (base.IsContains(subject)) - { - subject.MakeRelativeTo(base.GetFullPath()); - } - else if (base.HasVolume() && subject.HasVolume() && bv == sv) - { - wxString unusedVolume; - wxString pathSansVolume; - subject.SplitVolume(subject.GetFullPath(), &unusedVolume, &pathSansVolume); - subject = pathSansVolume; - } - //implicit else: this stays fully absolute - - return subject.GetFullPath(); - } - - static wxString MakeAutoRelativeTo(const wxDirName subject, const wxString& pathbase) - { - return MakeAutoRelativeTo(wxFileName(subject), pathbase); - } - - // Returns the number of sub folders in this directory path - size_t GetCount() const { return GetDirCount(); } - - // ------------------------------------------------------------------------ - wxFileName Combine(const wxFileName& right) const; - wxDirName Combine(const wxDirName& right) const; - - // removes the lastmost directory from the path - void RemoveLast() { wxFileName::RemoveDir(GetCount() - 1); } - - wxDirName& Normalize(int flags = wxPATH_NORM_ALL, const wxString& cwd = wxEmptyString); - wxDirName& MakeRelativeTo(const wxString& pathBase = wxEmptyString); - wxDirName& MakeAbsolute(const wxString& cwd = wxEmptyString); - - // ------------------------------------------------------------------------ - - void AssignCwd(const wxString& volume = wxEmptyString) { wxFileName::AssignCwd(volume); } - bool SetCwd() { return wxFileName::SetCwd(); } - - // wxWidgets is missing the const qualifier for this one! Shame! - void Rmdir() const; - bool Mkdir() const; - - // ------------------------------------------------------------------------ - - wxDirName& operator=(const wxDirName& dirname) - { - Assign(dirname); - return *this; - } - wxDirName& operator=(const wxString& dirname) - { - Assign(dirname); - return *this; - } - wxDirName& operator=(const char* dirname) - { - Assign(wxString(dirname, wxMBConvUTF8())); - return *this; - } - - wxFileName operator+(const wxFileName& right) const { return Combine(right); } - wxDirName operator+(const wxDirName& right) const { return Combine(right); } - wxFileName operator+(const wxString& right) const { return Combine(wxFileName(right)); } - wxFileName operator+(const char* right) const { return Combine(wxFileName(wxString(right, wxMBConvUTF8()))); } - - bool operator==(const wxDirName& filename) const { return SameAs(filename); } - bool operator!=(const wxDirName& filename) const { return !SameAs(filename); } - - bool operator==(const wxFileName& filename) const { return SameAs(wxDirName(filename)); } - bool operator!=(const wxFileName& filename) const { return !SameAs(wxDirName(filename)); } - - // compare with a filename string interpreted as a native file name - bool operator==(const wxString& filename) const { return SameAs(wxDirName(filename)); } - bool operator!=(const wxString& filename) const { return !SameAs(wxDirName(filename)); } - - const wxFileName& GetFilename() const { return *this; } - wxFileName& GetFilename() { return *this; } -}; - -// -------------------------------------------------------------------------------------- -// Path Namespace -// -------------------------------------------------------------------------------------- -// Cross-platform utilities for manipulation of paths and filenames. Mostly these fall -// back on wxWidgets APIs internally, but are still helpful because some of wx's file stuff -// has minor glitches, or requires sloppy wxFileName typecasting. -// -namespace Path -{ - extern bool IsRelative(const wxString& path); - extern s64 GetFileSize(const wxString& path); - - extern wxString Normalize(const wxString& srcpath); - extern wxString Normalize(const wxDirName& srcpath); - extern wxString MakeAbsolute(const wxString& srcpath); - - extern wxString CombineWx(const wxString& srcPath, const wxString& srcFile); - extern wxString CombineWx(const wxDirName& srcPath, const wxFileName& srcFile); - extern wxString CombineWx(const wxString& srcPath, const wxDirName& srcFile); - extern wxString ReplaceFilename(const wxString& src, const wxString& newfilename); - extern wxString GetFilename(const wxString& src); - extern wxString GetDirectory(const wxString& src); - extern wxString GetFilenameWithoutExt(const wxString& src); - extern wxString GetRootDirectory(const wxString& src); -} // namespace Path diff --git a/pcsx2/gui/wxGuiTools.cpp b/pcsx2/gui/wxGuiTools.cpp deleted file mode 100644 index cc0f07790c..0000000000 --- a/pcsx2/gui/wxGuiTools.cpp +++ /dev/null @@ -1,652 +0,0 @@ -/* PCSX2 - PS2 Emulator for PCs - * Copyright (C) 2002-2010 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 . - */ - -#include "PrecompiledHeader.h" - -#include "gui/wxGuiTools.h" -#include "gui/pxStaticText.h" - -#include -#include -#include - -const pxAlignmentType - pxCentre = {pxAlignmentType::Center}, // Horizontal centered alignment - pxCenter = pxCentre, - pxMiddle = {pxAlignmentType::Middle}, // vertical centered alignment - - pxAlignLeft = {pxAlignmentType::Left}, - pxAlignRight = {pxAlignmentType::Right}, - pxAlignTop = {pxAlignmentType::Top}, - pxAlignBottom = {pxAlignmentType::Bottom}; - -const pxStretchType - pxShrink = {pxStretchType::Shrink}, - pxExpand = {pxStretchType::Expand}, - pxShaped = {pxStretchType::Shaped}, - pxReserveHidden = {pxStretchType::ReserveHidden}, - pxFixedMinimum = {pxStretchType::FixedMinimum}; - -wxSizerFlags pxAlignmentType::Apply(wxSizerFlags flags) const -{ - switch (intval) - { - case Centre: - flags.Align(flags.GetFlags() | wxALIGN_CENTRE_HORIZONTAL); - break; - - case Middle: - flags.Align(flags.GetFlags() | wxALIGN_CENTRE_VERTICAL); - break; - - case Left: - flags.Left(); - break; - - case Right: - flags.Right(); - break; - - case Top: - flags.Top(); - break; - - case Bottom: - flags.Bottom(); - break; - } - return flags; -} - -wxSizerFlags pxStretchType::Apply(wxSizerFlags flags) const -{ - switch (intval) - { - case Shrink: - //pxFail( "wxSHRINK is an ignored stretch flag." ); - break; - - case Expand: - flags.Expand(); - break; - - case Shaped: - flags.Shaped(); - break; - - case ReserveHidden: - flags.ReserveSpaceEvenIfHidden(); - break; - - case FixedMinimum: - flags.FixedMinSize(); - break; - - //case Tile: - // pxAssert( "pxTile is an unsupported stretch tag (ignored)." ); - //break; - } - return flags; -} - -wxSizerFlags operator&(const wxSizerFlags& _flgs, const wxSizerFlags& _flgs2) -{ - //return align.Apply( _flgs ); - wxSizerFlags retval; - - uint allflags = (_flgs.GetFlags() | _flgs2.GetFlags()); - - retval.Align(allflags & wxALIGN_MASK); - if (allflags & wxEXPAND) - retval.Expand(); - if (allflags & wxSHAPED) - retval.Shaped(); - if (allflags & wxFIXED_MINSIZE) - retval.FixedMinSize(); - if (allflags & wxRESERVE_SPACE_EVEN_IF_HIDDEN) - retval.ReserveSpaceEvenIfHidden(); - - // Compounding borders is probably a fair approach: - retval.Border(allflags & wxALL, _flgs.GetBorderInPixels() + _flgs2.GetBorderInPixels()); - - // Compounding proportions works as well, I figure. - retval.Proportion(_flgs.GetProportion() + _flgs2.GetProportion()); - - return retval; -} - -// ---------------------------------------------------------------------------- -// Reference/Handle versions! - -void operator+=(wxSizer& target, wxWindow* src) -{ - target.Add(src); -} - -void operator+=(wxSizer& target, wxSizer* src) -{ - target.Add(src); -} - -void operator+=(wxSizer& target, wxWindow& src) -{ - target.Add(&src); -} - -void operator+=(wxSizer& target, wxSizer& src) -{ - target.Add(&src); -} - -void operator+=(wxSizer& target, int spacer) -{ - target.AddSpacer(spacer); -} - -void operator+=(wxSizer& target, const pxStretchSpacer& spacer) -{ - target.AddStretchSpacer(spacer.proportion); -} - -void operator+=(wxWindow& target, int spacer) -{ - if (!pxAssert(target.GetSizer() != NULL)) - return; - target.GetSizer()->AddSpacer(spacer); -} - -void operator+=(wxWindow& target, const pxStretchSpacer& spacer) -{ - if (!pxAssert(target.GetSizer() != NULL)) - return; - target.GetSizer()->AddStretchSpacer(spacer.proportion); -} - -// ---------------------------------------------------------------------------- -// Pointer versions! (note that C++ requires one of the two operator params be a -// "poper" object type (non-pointer), so that's why there's only a couple of these. - -void operator+=(wxSizer* target, wxWindow& src) -{ - if (!pxAssert(target != NULL)) - return; - target->Add(&src); -} - -void operator+=(wxSizer* target, wxSizer& src) -{ - if (!pxAssert(target != NULL)) - return; - target->Add(&src); -} - -// ---------------------------------------------------------------------------- - -// Returns FALSE if the window position is considered invalid, which means that it's title -// bar is most likely not easily grabble. Such a window should be moved to a valid or -// default position. -bool pxIsValidWindowPosition(const wxWindow& window, const wxPoint& windowPos) -{ - // The height of the window is only revlevant to the height of a title bar, which is - // all we need visible for the user to be able to drag the window into view. But - // there's no way to get that info from wx, so we'll just have to guesstimate... - - const wxSize sizeMatters(window.GetSize().GetWidth(), 32); - - for (unsigned int i = 0; i < wxDisplay::GetCount(); i++) - { - const auto rect = wxDisplay(i).GetGeometry(); - - if (rect.Contains(wxRect(windowPos, sizeMatters))) - return true; - } - - return false; -} - -// Retrieves the area of the screen, which can be used to enforce a valid zone for -// window positioning. (top/left coords are almost always (0,0) and bottom/right -// is the resolution of the desktop). -wxRect wxGetDisplayArea() -{ - return wxRect(wxPoint(), wxGetDisplaySize()); -} - -// -------------------------------------------------------------------------------------- -// pxSizerFlags -// -------------------------------------------------------------------------------------- -// FlagsAccessors - Provides read-write copies of standard sizer flags for our interface. -// These standard definitions provide a consistent and pretty interface for our GUI. -// Without them things look compacted, misaligned, and yucky! -// -// Implementation Note: Accessors are all provisioned as dynamic (realtime) sizer calculations. -// I've preferred this over cstatic const variables on the premise that spacing logic could -// in the future become a dynamic value (currently it is affixed to 6 for most items). -// -wxSizerFlags pxSizerFlags::StdSpace() -{ - return wxSizerFlags().Border(wxALL, StdPadding); -} - -wxSizerFlags pxSizerFlags::StdCenter() -{ - return wxSizerFlags().Align(wxALIGN_CENTER).DoubleBorder(); -} - -wxSizerFlags pxSizerFlags::StdExpand() -{ - return StdSpace().Expand(); -} - -// A good sizer flags setting for top-level static boxes or top-level picture boxes. -// Gives a generous border to the left, right, and bottom. Top border can be configured -// manually by using a spacer. -wxSizerFlags pxSizerFlags::TopLevelBox() -{ - return pxBorder(wxLEFT | wxBOTTOM | wxRIGHT, StdPadding).Expand(); -} - -// Flags intended for use on grouped StaticBox controls. These flags are ideal for -// StaticBoxes that are part of sub-panels or children of other static boxes, but may -// not be best for parent StaticBoxes on dialogs (left and right borders feel a bit -// "tight"). -wxSizerFlags pxSizerFlags::SubGroup() -{ - // Groups look better with a slightly smaller margin than standard. - // (basically this accounts for the group's frame) - return pxBorder(wxLEFT | wxBOTTOM | wxRIGHT, StdPadding - 2).Expand(); -} - -// This force-aligns the std button sizer to the right, where (at least) us win32 platform -// users always expect it to be. Most likely Mac platforms expect it on the left side -// just because it's *not* where win32 sticks it. Too bad! -wxSizerFlags pxSizerFlags::StdButton() -{ - return pxBorder().Align(wxALIGN_RIGHT); -} - -wxSizerFlags pxSizerFlags::Checkbox() -{ - return StdExpand(); -} - -void pxSizerFlags::SetBestPadding() -{ - if (wxSystemSettings::GetMetric(wxSYS_SCREEN_X) > 1024 && wxSystemSettings::GetMetric(wxSYS_SCREEN_Y) > 864) - StdPadding = 4; - else - StdPadding = 1; -} - -// -------------------------------------------------------------------------------------- -// pxTextWrapper / pxTextWrapperBase (implementations) -// -------------------------------------------------------------------------------------- - -static bool is_cjk_char(const uint ch) -{ - /** - * You can check these range at http://unicode.org/charts/ - * see the "East Asian Scripts" part. - * Notice that not all characters in that part is still in use today, so don't list them all here. - */ - - // FIXME: add range from Japanese-specific and Korean-specific section if you know the - // characters are used today. - - if (ch < 0x2e80) - return false; // shortcut for common non-CJK - - return - // Han Ideographs: all except Supplement - (ch >= 0x4e00 && ch < 0x9fcf) || - (ch >= 0x3400 && ch < 0x4dbf) || - (ch >= 0x20000 && ch < 0x2a6df) || - (ch >= 0xf900 && ch < 0xfaff) || - (ch >= 0x3190 && ch < 0x319f) || - - // Radicals: all except Ideographic Description - (ch >= 0x2e80 && ch < 0x2eff) || - (ch >= 0x2f00 && ch < 0x2fdf) || - (ch >= 0x31c0 && ch < 0x31ef) || - - // Chinese-specific: Bopomofo - (ch >= 0x3000 && ch < 0x303f) || - - // Japanese-specific: Halfwidth Katakana - (ch >= 0xff00 && ch < 0xffef) || - - // Japanese-specific: Hiragana, Katakana - (ch >= 0x3040 && ch <= 0x309f) || - (ch >= 0x30a0 && ch <= 0x30ff) || - - // Korean-specific: Hangul Syllables, Halfwidth Jamo - (ch >= 0xac00 && ch < 0xd7af) || - (ch >= 0xff00 && ch < 0xffef); -} - -/* - * According to Kinsoku-Shori, Japanese rules about line-breaking: - * - * * the following characters cannot begin a line (so we will never break before them): - * 、。,.)〕]}〉》」』】’”ゝゞヽヾ々?!:;ぁぃぅぇぉゃゅょゎァィゥェォャュョヮっヵッヶ・…ー - * - * * the following characters cannot end a line (so we will never break after them): - * (〔[{〈《「『【‘“ - * - * Unicode range that concerns word wrap for Chinese: - * 全角ASCII、全角中英文标点 (Fullwidth Character for ASCII, English punctuations and part of Chinese punctuations) - * http://www.unicode.org/charts/PDF/UFF00.pdf - * CJK 标点符号 (CJK punctuations) - * http://www.unicode.org/charts/PDF/U3000.pdf - */ -static bool no_break_after(const uint ch) -{ - // clang-format off - switch (ch) { - /** - * don't break after these Japanese/Chinese characters - */ - case 0x2018: case 0x201c: case 0x3008: case 0x300a: - case 0x300c: case 0x300e: case 0x3010: case 0x3014: - case 0x3016: case 0x301a: case 0x301d: - case 0xff08: case 0xff3b: case 0xff5b: - - /** - * FIXME don't break after these Korean characters - */ - - return true; - } - // clang-format on - - return false; -} - -static bool no_break_before(const uint ch) -{ - // clang-format off - switch (ch) { - /** - * don't break before these Japanese characters - */ - case 0x2019: case 0x201d: case 0x2026: case 0x3001: case 0x3002: - case 0x3005: case 0x3009: case 0x300b: case 0x300d: case 0x300f: - case 0x301c: case 0x3011: case 0x3015: case 0x3017: case 0x301b: - case 0x301e: case 0x3041: case 0x3043: case 0x3045: - case 0x3047: case 0x3049: case 0x3063: case 0x3083: case 0x3085: - case 0x3087: case 0x308e: case 0x309d: case 0x309e: case 0x30a1: - case 0x30a3: case 0x30a5: case 0x30a7: case 0x30a9: case 0x30c3: - case 0x30e3: case 0x30e5: case 0x30e7: case 0x30ee: case 0x30f5: - case 0x30f6: case 0x30fb: case 0x30fc: case 0x30fd: case 0x30fe: - case 0xff01: case 0xff09: case 0xff0c: case 0xff0d: case 0xff0e: case 0xff1a: - case 0xff1b: case 0xff1f: case 0xff3d: case 0xff5d: case 0xff64: case 0xff65: - - /** - * FIXME don't break before these Korean characters - */ - - /** - * don't break before these Chinese characters - * contains - * many Chinese punctuations that should not start a line - * and right side of different kinds of brackets, quotes - */ - - return true; - } - // clang-format on - - return false; -} - -pxTextWrapperBase& pxTextWrapperBase::Wrap(const wxWindow& win, const wxString& text, int widthMax) -{ - if (text.empty()) - return *this; - - bool wasWrapped = false; - - wxString line; - line.reserve(text.length() + 12); - - wxString::const_iterator lastSpace = text.end(); - wxString::const_iterator lineStart = text.begin(); - for (wxString::const_iterator p = lineStart;; ++p) - { - if (IsStartOfNewLine()) - { - OnNewLine(); - - lastSpace = text.end(); - lineStart = p; - - if (wasWrapped) - line = m_indent; - else - line.clear(); - } - - if (p == text.end() || *p == L'\n') - { - wasWrapped = false; - DoOutputLine(line); - - if (p == text.end()) - break; - } - else - { // not EOL - if (is_cjk_char(*p)) - { - if (!no_break_before(*p)) - { - if (p == lineStart || !no_break_after(*(p - 1))) - lastSpace = p; - } - } - else if (*p == L' ' || *p == L',' || *p == L'/') - lastSpace = p; - - line += *p; - - if (widthMax >= 0 && lastSpace != text.end()) - { - int width; - win.GetTextExtent(line, &width, NULL); - - if (width > widthMax) - { - wasWrapped = true; - - // remove the last word from this line - line.erase(lastSpace - lineStart, p + 1 - lineStart); - DoOutputLine(line); - - // go back to the last word of this line which we didn't - // output yet - p = lastSpace; - - if (*p != L' ') - p--; - } - } - //else: no wrapping at all or impossible to wrap - } - } - - return *this; -} - -void pxTextWrapperBase::DoOutputLine(const wxString& line) -{ - OnOutputLine(line); - m_linecount++; - m_eol = true; -} - -// this function is a destructive inspector: when it returns true it also -// resets the flag to false so calling it again wouldn't return true any -// more -bool pxTextWrapperBase::IsStartOfNewLine() -{ - if (!m_eol) - return false; - - m_eol = false; - return true; -} - -pxTextWrapper& pxTextWrapper::Wrap(const wxWindow& win, const wxString& text, int widthMax) -{ - _parent::Wrap(win, text, widthMax); - return *this; -} - -pxTextWrapper& pxTextWrapper::Wrap(const wxWindow* win, const wxString& text, int widthMax) -{ - if (win) - _parent::Wrap(*win, text, widthMax); - return *this; -} - -void pxTextWrapper::OnOutputLine(const wxString& line) -{ - m_text += line; -} - -void pxTextWrapper::OnNewLine() -{ - m_text += L'\n'; -} - -// -------------------------------------------------------------------------------------- -// ScopedBusyCursor (implementations) -// -------------------------------------------------------------------------------------- - -std::stack ScopedBusyCursor::m_cursorStack; -BusyCursorType ScopedBusyCursor::m_defBusyType; - -ScopedBusyCursor::ScopedBusyCursor(BusyCursorType busytype) -{ - pxAssert(wxTheApp != NULL); - - BusyCursorType curtype = Cursor_NotBusy; - if (!m_cursorStack.empty()) - curtype = m_cursorStack.top(); - - if (curtype < busytype) - SetManualBusyCursor(curtype = busytype); - - m_cursorStack.push(curtype); -} - -ScopedBusyCursor::~ScopedBusyCursor() -{ - if (!pxAssert(wxTheApp != NULL)) - return; - - if (!pxAssert(!m_cursorStack.empty())) - { - SetManualBusyCursor(m_defBusyType); - return; - } - - BusyCursorType curtype = m_cursorStack.top(); - m_cursorStack.pop(); - - if (m_cursorStack.empty()) - SetManualBusyCursor(m_defBusyType); - else if (m_cursorStack.top() != curtype) - SetManualBusyCursor(m_cursorStack.top()); -} - -void ScopedBusyCursor::SetDefault(BusyCursorType busytype) -{ - if (busytype == m_defBusyType) - return; - m_defBusyType = busytype; - - if (m_cursorStack.empty()) - SetManualBusyCursor(busytype); -} - -void ScopedBusyCursor::SetManualBusyCursor(BusyCursorType busytype) -{ - switch (busytype) - { - case Cursor_NotBusy: - wxSetCursor(wxNullCursor); - break; - case Cursor_KindaBusy: - wxSetCursor(StockCursors.GetArrowWait()); - break; - case Cursor_ReallyBusy: - wxSetCursor(*wxHOURGLASS_CURSOR); - break; - } -} - -const wxCursor& MoreStockCursors::GetArrowWait() -{ - if (!m_arrowWait) - m_arrowWait = std::make_unique(wxCURSOR_ARROWWAIT); - return *m_arrowWait; -} - -MoreStockCursors StockCursors; - -// -------------------------------------------------------------------------------------- -// pxSetToolTip -// -------------------------------------------------------------------------------------- -// This is the preferred way to assign tooltips to wxWidgets-based objects. On Windows it -// extends the tooltip time to the maximum possible. GTK seems to have indefinite -// tooltips, I don't know about OS X. - -void pxSetToolTip(wxWindow* wind, const wxString& src) -{ - if (wind == NULL) - return; // Silently ignore nulls - wind->SetToolTip(src); - -// Make tooltips show for as long as possible on Windows. Linux (GTK) can -// show tooltips indefinitely. -#ifdef __WXMSW__ - wind->GetToolTip()->SetAutoPop(32767); -#endif -} - -void pxSetToolTip(wxWindow& wind, const wxString& src) -{ - pxSetToolTip(&wind, src); -} - - -wxFont pxGetFixedFont(int ptsize, wxFontWeight weight, bool underline) -{ - return wxFont( - ptsize, wxFONTFAMILY_MODERN, wxFONTSTYLE_NORMAL, weight, underline -#ifdef __WXMSW__ - , - L"Lucida Console" // better than courier new (win32 only) -#endif - ); -} - - -wxString pxGetAppName() -{ - pxAssert(wxTheApp); - return wxTheApp->GetAppName(); -} diff --git a/pcsx2/gui/wxGuiTools.h b/pcsx2/gui/wxGuiTools.h deleted file mode 100644 index 6f0f89b62a..0000000000 --- a/pcsx2/gui/wxGuiTools.h +++ /dev/null @@ -1,831 +0,0 @@ -/* PCSX2 - PS2 Emulator for PCs - * Copyright (C) 2002-2010 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 . - */ - -#pragma once - -#include -#include -#include -#include "common/Assertions.h" -#include "common/Pcsx2Defs.h" - -// ---------------------------------------------------------------------------- -// wxGuiTools.h -// -// This file is meant to contain utility classes for users of the wxWidgets library. -// All classes in this file are dependent on wxBase and wxCore libraries! Meaning -// you will have to use wxCore header files and link against wxCore (GUI) to build -// them. For tools which require only wxBase, see wxBaseTools.h -// ---------------------------------------------------------------------------- - -class pxStaticText; -class pxStaticHeading; -class pxCheckBox; -class wxSpinCtrl; - -namespace pxSizerFlags -{ - static int StdPadding = 4; - - extern wxSizerFlags StdSpace(); - extern wxSizerFlags StdCenter(); - extern wxSizerFlags StdExpand(); - extern wxSizerFlags TopLevelBox(); - extern wxSizerFlags SubGroup(); - extern wxSizerFlags StdButton(); - extern wxSizerFlags Checkbox(); - extern void SetBestPadding(); -}; // namespace pxSizerFlags - -#define wxSF wxSizerFlags() - -// -------------------------------------------------------------------------------------- -// pxAlignment / pxStretchType -// -------------------------------------------------------------------------------------- -// These are full blown class types instead of enumerations because wxSizerFlags has an -// implicit conversion from integer (silly design flaw creating more work for me!) -// -struct pxAlignmentType -{ - enum - { - Centre, - Center = Centre, - Middle, - Left, - Right, - Top, - Bottom - }; - - int intval; - - wxSizerFlags Apply(wxSizerFlags flags = wxSizerFlags()) const; - - wxSizerFlags operator&(const wxSizerFlags& _flgs) const - { - return Apply(_flgs); - } - - wxSizerFlags Expand() const - { - return Apply().Expand(); - } - - wxSizerFlags Border(int dir, int padding) const - { - return Apply().Border(dir, padding); - } - - wxSizerFlags Proportion() const - { - return Apply().Proportion(intval); - } - - operator wxSizerFlags() const - { - return Apply(); - } -}; - -struct pxStretchType -{ - enum - { - Shrink, - Expand, - Shaped, - ReserveHidden, - FixedMinimum - }; - - int intval; - - wxSizerFlags Apply(wxSizerFlags flags = wxSizerFlags()) const; - - wxSizerFlags operator&(const wxSizerFlags& _flgs) const - { - return Apply(_flgs); - } - - wxSizerFlags Border(int dir, int padding) const - { - return Apply().Border(dir, padding); - } - - wxSizerFlags Proportion() const - { - return Apply().Proportion(intval); - } - - operator wxSizerFlags() const - { - return Apply(); - } -}; - -static __fi wxSizerFlags pxProportion(int prop) -{ - return wxSizerFlags(prop); -} - -static __fi wxSizerFlags pxBorder(int dir = wxALL, int pad = pxSizerFlags::StdPadding) -{ - return wxSizerFlags().Border(dir, pad); -} - -class pxStretchSpacer -{ -public: - int proportion; - - pxStretchSpacer(int prop = 0) - { - proportion = prop; - } -}; - -extern const pxAlignmentType - pxCentre, // Horizontal centered alignment - pxCenter, - pxMiddle, // vertical centered alignment - - pxAlignLeft, - pxAlignRight, - pxAlignTop, - pxAlignBottom; - -extern const pxStretchType - pxShrink, - pxExpand, - pxShaped, - pxReserveHidden, - pxFixedMinimum; - -// -------------------------------------------------------------------------------------- -// pxWindowAndFlags -// -------------------------------------------------------------------------------------- -// This struct is a go-between for combining windows and sizer flags in "neat" fashion. -// To create the struct, use the | operator, like so: -// -// myPanel | wxSizerFlags().Expand() -// -// Implementation Note: This struct is a template as it allows us to use a special -// version of the += operator that retains the type information of the window, in case -// the window implements its own += overloads (one example is pxStaticText). Without the -// template, the type of the window would only be known as "wxWindow" when it's added to the -// sizer, and would thus fail to invoke the correct operator overload. -// -template -struct pxWindowAndFlags -{ - WinType* window; - wxSizerFlags flags; -}; - - -extern wxSizerFlags operator&(const wxSizerFlags& _flgs, const wxSizerFlags& _flgs2); - -template ::value>::type> -pxWindowAndFlags operator|(WinType* _win, const wxSizerFlags& _flgs) -{ - pxWindowAndFlags result = {_win, _flgs}; - return result; -} - -template ::value>::type> -pxWindowAndFlags operator|(WinType& _win, const wxSizerFlags& _flgs) -{ - pxWindowAndFlags result = {&_win, _flgs}; - return result; -} - -// -------------------------------------------------------------------------------------- -// wxSizer Operator += .. a wxSizer.Add() Substitute -// -------------------------------------------------------------------------------------- -// This set of operators is the *recommended* method for adding windows to sizers, not just -// because it's a lot prettier but also because it allows controls like pxStaticText to over- -// ride default sizerflags behavior. -// -// += operator works on either sizers, wxDialogs or wxPanels. In the latter case, the window -// is added to the dialog/panel's toplevel sizer (wxPanel.GetSizer() is used). If the panel -// has no sizer set via SetSizer(), an assertion is generated. -// - -extern void operator+=(wxSizer& target, wxWindow* src); -extern void operator+=(wxSizer& target, wxSizer* src); -extern void operator+=(wxSizer& target, wxWindow& src); -extern void operator+=(wxSizer& target, wxSizer& src); - -extern void operator+=(wxSizer* target, wxWindow& src); -extern void operator+=(wxSizer* target, wxSizer& src); - -extern void operator+=(wxSizer& target, int spacer); -extern void operator+=(wxWindow& target, int spacer); -extern void operator+=(wxSizer& target, const pxStretchSpacer& spacer); -extern void operator+=(wxWindow& target, const pxStretchSpacer& spacer); - -// ---------------------------------------------------------------------------- -// Important: This template is needed in order to retain window type information and -// invoke the proper overloaded version of += (which is used by pxStaticText and other -// classes to perform special actions when added to sizers). -template -void operator+=(wxWindow& target, WinType* src) -{ - if (!pxAssert(target.GetSizer() != NULL)) - return; - *target.GetSizer() += src; -} - -template -void operator+=(wxWindow& target, WinType& src) -{ - if (!pxAssert(target.GetSizer() != NULL)) - return; - *target.GetSizer() += src; -} - -template -void operator+=(wxWindow& target, const pxWindowAndFlags& src) -{ - if (!pxAssert(target.GetSizer() != NULL)) - return; - *target.GetSizer() += src; -} - -template -void operator+=(wxSizer& target, const pxWindowAndFlags& src) -{ - target.Add(src.window, src.flags); -} - -wxDECLARE_EVENT(pxEvt_OnDialogCreated, wxCommandEvent); - -// -------------------------------------------------------------------------------------- -// pxDialogCreationFlags -// -------------------------------------------------------------------------------------- -class pxDialogCreationFlags -{ -public: - wxSize MinimumSize; - wxOrientation BoxSizerOrient; - - bool isResizable; - bool hasContextHelp; - bool hasCaption; - bool hasMinimizeBox; - bool hasMaximizeBox; - bool hasSystemMenu; - bool hasCloseBox; - -public: - virtual ~pxDialogCreationFlags() = default; - - pxDialogCreationFlags() - { - MinimumSize = wxDefaultSize; - BoxSizerOrient = wxVERTICAL; - isResizable = false; - hasContextHelp = false; - - hasCloseBox = true; - hasSystemMenu = true; - hasMinimizeBox = false; - hasMaximizeBox = false; - hasCaption = true; - } - - pxDialogCreationFlags& SetSizerOrient(wxOrientation orient) - { - BoxSizerOrient = orient; - return *this; - } - - pxDialogCreationFlags& SetResize(bool enable = true) - { - isResizable = enable; - return *this; - } - - pxDialogCreationFlags& SetMinimize(bool enable = true) - { - hasMinimizeBox = enable; - return *this; - } - - pxDialogCreationFlags& SetMaximize(bool enable = true) - { - hasMaximizeBox = enable; - return *this; - } - - // NOTE: Enabling system menu on dialogs usually doesn't work, and might cause - // other unwanted behavior, such as a missing close button. - pxDialogCreationFlags& SetSystemMenu(bool enable = true) - { - hasSystemMenu = enable; - return *this; - } - - pxDialogCreationFlags& SetCaption(bool enable = true) - { - hasCaption = enable; - return *this; - } - - pxDialogCreationFlags& SetCloseBox(bool enable = true) - { - hasCloseBox = enable; - return *this; - } - - pxDialogCreationFlags SetContextHelp(bool enabled = true) - { - hasContextHelp = enabled; - return *this; - } - - pxDialogCreationFlags& SetMinWidth(int width) - { - if (width > MinimumSize.x) - MinimumSize.SetWidth(width); - return *this; - } - - pxDialogCreationFlags& SetMinHeight(int height) - { - if (height > MinimumSize.y) - MinimumSize.SetHeight(height); - return *this; - } - - pxDialogCreationFlags& SetMinSize(const wxSize& size) - { - return SetMinWidth(size.x).SetMinHeight(size.y); - } - - - pxDialogCreationFlags Horizontal() const - { - return pxDialogCreationFlags(*this).SetSizerOrient(wxHORIZONTAL); - } - - pxDialogCreationFlags Vertical() const - { - return pxDialogCreationFlags(*this).SetSizerOrient(wxVERTICAL); - } - - pxDialogCreationFlags NoSizer() const - { - return pxDialogCreationFlags(*this).SetSizerOrient((wxOrientation)0); - } - - pxDialogCreationFlags Resize(bool enable = true) const - { - return pxDialogCreationFlags(*this).SetResize(enable); - } - - pxDialogCreationFlags Minimize(bool enable = true) const - { - return pxDialogCreationFlags(*this).SetMinimize(enable); - } - - pxDialogCreationFlags Maximize(bool enable = true) const - { - return pxDialogCreationFlags(*this).SetMaximize(enable); - } - - // NOTE: Enabling system menu on dialogs usually doesn't work, and might cause - // other unwanted behavior, such as a missing close button. - pxDialogCreationFlags SystemMenu(bool enable = true) const - { - return pxDialogCreationFlags(*this).SetSystemMenu(false); - } - - pxDialogCreationFlags Caption(bool enable = true) const - { - return pxDialogCreationFlags(*this).SetCaption(enable); - } - - pxDialogCreationFlags CloseBox(bool enable = true) const - { - return pxDialogCreationFlags(*this).SetCloseBox(enable); - } - - pxDialogCreationFlags NoResize() const - { - return pxDialogCreationFlags(*this).SetResize(false); - } - - pxDialogCreationFlags NoMinimize() const - { - return pxDialogCreationFlags(*this).SetMinimize(false); - } - - pxDialogCreationFlags NoMaximize() const - { - return pxDialogCreationFlags(*this).SetMaximize(false); - } - - pxDialogCreationFlags NoSystemMenu() const - { - return pxDialogCreationFlags(*this).SetSystemMenu(false); - } - - pxDialogCreationFlags NoCaption() const - { - return pxDialogCreationFlags(*this).SetCaption(false); - } - - pxDialogCreationFlags NoCloseBox() const - { - return pxDialogCreationFlags(*this).SetCloseBox(false); - } - - pxDialogCreationFlags MinWidth(int width) const - { - return pxDialogCreationFlags(*this).SetMinWidth(width); - } - - pxDialogCreationFlags MinHeight(int height) const - { - return pxDialogCreationFlags(*this).SetMinHeight(height); - } - - pxDialogCreationFlags MinSize(const wxSize& size) const - { - return pxDialogCreationFlags(*this).SetMinSize(size); - } - - pxDialogCreationFlags MinSize(int width, int height) const - { - return pxDialogCreationFlags(*this).SetMinWidth(width).SetMinHeight(height); - } - - int GetWxWindowFlags() const - { - int retval = 0; - if (isResizable) - retval |= wxRESIZE_BORDER; - if (hasCaption) - retval |= wxCAPTION; - if (hasMaximizeBox) - retval |= wxMAXIMIZE_BOX; - if (hasMinimizeBox) - retval |= wxMINIMIZE_BOX; - if (hasSystemMenu) - retval |= wxSYSTEM_MENU; - if (hasCloseBox) - retval |= wxCLOSE_BOX; - - return retval; - } -}; - -// -------------------------------------------------------------------------------------- -// wxDialogWithHelpers -// -------------------------------------------------------------------------------------- -class wxDialogWithHelpers : public wxDialog -{ - typedef wxDialog _parent; - wxDECLARE_DYNAMIC_CLASS_NO_COPY(wxDialogWithHelpers); - -protected: - bool m_hasContextHelp; - wxBoxSizer* m_extraButtonSizer; - wxRect m_CreatedRect; - -public: - wxDialogWithHelpers(); - wxDialogWithHelpers(wxWindow* parent, const wxString& title, const pxDialogCreationFlags& cflags = pxDialogCreationFlags()); - virtual ~wxDialogWithHelpers() = default; - - void Init(const pxDialogCreationFlags& cflags); - void AddOkCancel(wxSizer& sizer, bool hasApply = false); - void AddOkCancel(wxSizer* sizer = NULL, bool hasApply = false); - void RememberPosition(); - - virtual void SmartCenterFit(); - virtual int ShowModal(); - virtual bool Show(bool show = true); - - // Must return the same thing as GetNameStatic; a name ideal for use in uniquely - // identifying dialogs. (this version is the 'instance' version, which is called - // by BaseConfigurationDialog to assign the wxWidgets dialog name, and for saving - // screenshots to disk) - virtual wxString GetDialogName() const; - - virtual wxStaticText& Label(const wxString& label); - virtual pxStaticText& Text(const wxString& label); - virtual pxStaticText& Heading(const wxString& label); - - wxDialogWithHelpers& SetMinWidth(int newWidth); - wxDialogWithHelpers& SetMinHeight(int newHeight); - - int GetCharHeight() const; - -protected: - void OnDialogCreated(wxCommandEvent& evt); - void OnOkCancel(wxCommandEvent& evt); - - bool ShouldPreventAppExit() const { return false; } - - void DoAutoCenter(); -}; - -// -------------------------------------------------------------------------------------- -// wxPanelWithHelpers -// -------------------------------------------------------------------------------------- -// Overview of Helpers provided by this class: -// * Simpler constructors that have wxID, position, and size parameters removed (We never -// use them in pcsx2) -// -// * Automatic 'primary box sizer' creation, assigned via SetSizer() -- use GetSizer() -// to retrieve it, or use the "*this += window;" syntax to add windows directly to it. -// -// * Built-in support for StaticBoxes (aka groupboxes). Create one at construction with -// a wxString label, or add one "after the fact" using AddFrame. -// -// * Propagates IdealWidth settings from parenting wxPanelWithHelpers classes, and auto- -// matically adjusts the width based on the sizer type (groupsizers get truncated to -// account for borders). -// -class wxPanelWithHelpers : public wxPanel -{ - wxDECLARE_DYNAMIC_CLASS_NO_COPY(wxPanelWithHelpers); - -public: - wxPanelWithHelpers(wxWindow* parent, wxOrientation orient, const wxString& staticBoxLabel); - wxPanelWithHelpers(wxWindow* parent, wxOrientation orient); - wxPanelWithHelpers(wxWindow* parent, const wxPoint& pos, const wxSize& size = wxDefaultSize); - explicit wxPanelWithHelpers(wxWindow* parent = NULL); - - wxPanelWithHelpers* AddFrame(const wxString& label, wxOrientation orient = wxVERTICAL); - - wxStaticText& Label(const wxString& label); - pxStaticText& Text(const wxString& label); - pxStaticText& Heading(const wxString& label); - - virtual wxPanelWithHelpers& SetMinWidth(int newWidth); - -protected: - void Init(); -}; - - -// -------------------------------------------------------------------------------------- -// pxTextWrapperBase -// -------------------------------------------------------------------------------------- -// this class is used to wrap the text on word boundary: wrapping is done by calling -// OnStartLine() and OnOutputLine() functions. This class by itself can be used as a -// line counting tool, but produces no formatted text output. -// -// [class "borrowed" from wxWidgets private code, made public, and renamed to avoid possible -// conflicts with future editions of wxWidgets which might make it public. Why this isn't -// publicly available already in wxBase I'll never know-- air] -// -class pxTextWrapperBase -{ -protected: - bool m_eol; - int m_linecount; - wxString m_indent; - -public: - virtual ~pxTextWrapperBase() = default; - - pxTextWrapperBase(const wxString& indent = wxEmptyString) - : m_indent(indent) - { - m_eol = false; - m_linecount = 0; - } - - // win is used for getting the font, text is the text to wrap, width is the - // max line width or -1 to disable wrapping - pxTextWrapperBase& Wrap(const wxWindow& win, const wxString& text, int widthMax); - - int GetLineCount() const - { - return m_linecount; - } - -protected: - // line may be empty - virtual void OnOutputLine(const wxString& line) {} - - // called at the start of every new line (except the very first one) - virtual void OnNewLine() {} - - void DoOutputLine(const wxString& line); - bool IsStartOfNewLine(); -}; - -// -------------------------------------------------------------------------------------- -// pxTextWrapper -// -------------------------------------------------------------------------------------- -// This class extends pxTextWrapperBase and adds the ability to retrieve the formatted -// result of word wrapping. -// -class pxTextWrapper : public pxTextWrapperBase -{ - typedef pxTextWrapperBase _parent; - -protected: - wxString m_text; - -public: - pxTextWrapper(const wxString& wrapPrefix = wxEmptyString) - : pxTextWrapperBase(wrapPrefix) - { - } - - virtual ~pxTextWrapper() = default; - - const wxString& GetResult() const - { - return m_text; - } - - pxTextWrapper& Wrap(const wxWindow& win, const wxString& text, int widthMax); - pxTextWrapper& Wrap(const wxWindow* win, const wxString& text, int widthMax); - -protected: - void OnOutputLine(const wxString& line); - void OnNewLine(); -}; - -// -------------------------------------------------------------------------------------- -// pxWindowTextWriter -// -------------------------------------------------------------------------------------- -class pxWindowTextWriter -{ -protected: - wxDC& m_dc; - - wxAlignment m_align; - wxPoint m_curpos; - int m_leading; - - virtual void _DoWriteLn(const wxString& msg); - void _DoWrite(const wxChar* msg); - -public: - pxWindowTextWriter(wxDC& dc); - virtual ~pxWindowTextWriter() = default; - - virtual void OnFontChanged(); - - pxWindowTextWriter& WriteLn(); - pxWindowTextWriter& WriteLn(const wxChar* fmt); - pxWindowTextWriter& SetFont(const wxFont& font); - pxWindowTextWriter& Align(const wxAlignment& align); - pxWindowTextWriter& WriteLn(const wxString fmt); - - pxWindowTextWriter& SetLeading(int lead) - { - m_leading = lead; - return *this; - } - - pxWindowTextWriter& SetWeight(wxFontWeight weight); - pxWindowTextWriter& SetStyle(wxFontStyle style); - pxWindowTextWriter& Normal(); - - pxWindowTextWriter& Bold() - { - return SetWeight(wxFONTWEIGHT_BOLD); - } - - pxWindowTextWriter& Italic() - { - return SetStyle(wxFONTSTYLE_ITALIC); - } - - pxWindowTextWriter& SetPos(const wxPoint& pos); - pxWindowTextWriter& MovePos(const wxSize& delta); - - pxWindowTextWriter& SetPos(int xpos, int ypos) - { - return SetPos(wxPoint(xpos, ypos)); - } - - pxWindowTextWriter& MovePos(int xdelta, int ydelta) - { - return MovePos(wxSize(xdelta, ydelta)); - } - - pxWindowTextWriter& SetY(int ypos); -}; - -// -------------------------------------------------------------------------------------- -// MoreStockCursors -// -------------------------------------------------------------------------------------- -// Because (inexplicably) the ArrowWait cursor isn't in wxWidgets stock list. -// -class MoreStockCursors -{ -protected: - std::unique_ptr m_arrowWait; - -public: - MoreStockCursors() {} - virtual ~MoreStockCursors() = default; - const wxCursor& GetArrowWait(); -}; - -enum BusyCursorType -{ - Cursor_NotBusy, - Cursor_KindaBusy, - Cursor_ReallyBusy, -}; - -extern MoreStockCursors StockCursors; - -// -------------------------------------------------------------------------------------- -// ScopedBusyCursor -// -------------------------------------------------------------------------------------- -// ... because wxWidgets wxBusyCursor doesn't really do proper nesting (doesn't let me -// override a partially-busy cursor with a really busy one) - -class ScopedBusyCursor -{ -protected: - static std::stack m_cursorStack; - static BusyCursorType m_defBusyType; - -public: - ScopedBusyCursor(BusyCursorType busytype); - virtual ~ScopedBusyCursor(); - - static void SetDefault(BusyCursorType busytype); - static void SetManualBusyCursor(BusyCursorType busytype); -}; - -// -------------------------------------------------------------------------------------- -// wxDoNotLogInThisScope -// -------------------------------------------------------------------------------------- -// This class is used to disable wx's sometimes inappropriate amount of forced error logging -// during specific activities. For example, when using wxDynamicLibrary to detect the -// validity of DLLs, wx will log errors for missing symbols. (sigh) -// -// Usage: Basic auto-cleanup destructor class. Create an instance inside a scope, and -// logging will be re-enabled when scope is terminated. :) -// -class wxDoNotLogInThisScope -{ - DeclareNoncopyableObject(wxDoNotLogInThisScope); - -protected: - bool m_prev; - -public: - wxDoNotLogInThisScope() - { - m_prev = wxLog::EnableLogging(false); - } - - virtual ~wxDoNotLogInThisScope() - { - wxLog::EnableLogging(m_prev); - } -}; - -// -------------------------------------------------------------------------------------- -// pxFitToDigits -// -------------------------------------------------------------------------------------- -// Fits a given text or spinner control to the number of digits requested, since by default -// they're usually way over-sized. - -extern void pxFitToDigits(wxWindow* win, int digits); -extern void pxFitToDigits(wxSpinCtrl* win, int digits); -extern wxTextCtrl* CreateNumericalTextCtrl(wxWindow* parent, int digits, long flags = wxTE_RIGHT); - - -////////////////////////////////////////////////////////////////////////////////////////////// - -extern bool pxIsValidWindowPosition(const wxWindow& window, const wxPoint& windowPos); -extern wxRect wxGetDisplayArea(); -extern wxString pxGetAppName(); - -extern int pxGetCharHeight(const wxWindow* wind, int rows = 1); -extern int pxGetCharHeight(const wxWindow& wind, int rows = 1); - -extern void pxSetToolTip(wxWindow* wind, const wxString& src); -extern void pxSetToolTip(wxWindow& wind, const wxString& src); -extern wxFont pxGetFixedFont(int ptsize = 8, wxFontWeight weight = wxFONTWEIGHT_NORMAL, bool underline = false); - -extern pxDialogCreationFlags pxDialogFlags(); diff --git a/pcsx2/gui/wxHelpers.cpp b/pcsx2/gui/wxHelpers.cpp deleted file mode 100644 index dd103bd0f5..0000000000 --- a/pcsx2/gui/wxHelpers.cpp +++ /dev/null @@ -1,435 +0,0 @@ -/* PCSX2 - PS2 Emulator for PCs - * Copyright (C) 2002-2010 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 . - */ - -#include "PrecompiledHeader.h" - -#include -#include -#include -#include "common/General.h" -#include "common/Threading.h" -#include "gui/IniInterface.h" -#include "gui/wxGuiTools.h" -#include "gui/pxStaticText.h" - -using namespace pxSizerFlags; - -pxDialogCreationFlags pxDialogFlags() -{ - return pxDialogCreationFlags().CloseBox().Caption().Vertical(); -} - - -// -------------------------------------------------------------------------------------- - - -// Creates a text control which is right-justified and has it's minimum width configured to suit -// the number of digits requested. -wxTextCtrl* CreateNumericalTextCtrl(wxWindow* parent, int digits, long flags) -{ - wxTextCtrl* ctrl = new wxTextCtrl(parent, wxID_ANY); - ctrl->SetWindowStyleFlag(flags); - pxFitToDigits(ctrl, digits); - return ctrl; -} - -void pxFitToDigits(wxWindow* win, int digits) -{ - int ex; - win->GetTextExtent(wxString(L'0', digits + 1), &ex, NULL); - win->SetMinSize(wxSize(ex + 10, wxDefaultCoord)); // +10 for text control borders/insets and junk. -} - -void pxFitToDigits(wxSpinCtrl* win, int digits) -{ - // HACK!! The better way would be to create a pxSpinCtrl class that extends wxSpinCtrl and thus - // have access to wxSpinButton::DoGetBestSize(). But since I don't want to do that, we'll just - // make/fake it with a value it's pretty common to Win32/GTK/Mac: - - static const int MagicSpinnerSize = 18; - - int ex; - win->GetTextExtent(wxString(L'0', digits + 1), &ex, NULL); - win->SetMinSize(wxSize(ex + 10 + MagicSpinnerSize, wxDefaultCoord)); // +10 for text control borders/insets and junk. -} - -// ===================================================================================================== -// wxDialogWithHelpers Class Implementations -// ===================================================================================================== - -wxDEFINE_EVENT(pxEvt_OnDialogCreated, wxCommandEvent); - -wxIMPLEMENT_DYNAMIC_CLASS(wxDialogWithHelpers, wxDialog); - -wxDialogWithHelpers::wxDialogWithHelpers() -{ - m_hasContextHelp = false; - m_extraButtonSizer = NULL; - - Init(pxDialogFlags()); -} - -wxDialogWithHelpers::wxDialogWithHelpers(wxWindow* parent, const wxString& title, const pxDialogCreationFlags& cflags) - : wxDialog(parent, wxID_ANY, title, wxDefaultPosition, wxDefaultSize, cflags.GetWxWindowFlags()) -{ - m_hasContextHelp = cflags.hasContextHelp; - if ((int)cflags.BoxSizerOrient != 0) - { - SetSizer(new wxBoxSizer(cflags.BoxSizerOrient)); - *this += StdPadding; - } - - Init(cflags); - SetMinSize(cflags.MinimumSize); -} - -void wxDialogWithHelpers::Init(const pxDialogCreationFlags& cflags) -{ -// Note to self: if any comments indicate platform specific behaviour then -// ifdef them out to see if they fix the issue. I wasted too much time -// figuring out why the close box wouldn't work on wxGTK modal dialogs that -// had a minimise button. -#if _WIN32 - // This fixes it so that the dialogs show up in the task bar in Vista: - // (otherwise they go stupid iconized mode if the user minimizes them) - if (cflags.hasMinimizeBox) - SetExtraStyle(GetExtraStyle() & ~wxTOPLEVEL_EX_DIALOG); -#endif - - m_extraButtonSizer = NULL; - - if (m_hasContextHelp) - delete wxHelpProvider::Set(new wxSimpleHelpProvider()); - - Bind(pxEvt_OnDialogCreated, &wxDialogWithHelpers::OnDialogCreated, this); - - Bind(wxEVT_BUTTON, &wxDialogWithHelpers::OnOkCancel, this, wxID_OK); - Bind(wxEVT_BUTTON, &wxDialogWithHelpers::OnOkCancel, this, wxID_CANCEL); - - wxCommandEvent createEvent(pxEvt_OnDialogCreated); - createEvent.SetId(GetId()); - AddPendingEvent(createEvent); -} - -void wxDialogWithHelpers::OnDialogCreated(wxCommandEvent& evt) -{ - evt.Skip(); - if ((evt.GetId() == GetId()) && !GetDialogName().IsEmpty()) - SetName(L"Dialog:" + GetDialogName()); -} - -wxString wxDialogWithHelpers::GetDialogName() const -{ - return wxEmptyString; -} - -void wxDialogWithHelpers::DoAutoCenter() -{ - // Smart positioning logic! If our parent window is larger than our window by some - // good amount, then we center on that. If not, center relative to the screen. This - // avoids the popup automatically eclipsing the parent window (which happens in PCSX2 - // a lot since the main window is small). - - bool centerfail = true; - if (wxWindow* parent = GetParent()) - { - const wxSize parentSize(parent->GetSize()); - - if ((parentSize.x > ((int)GetSize().x * 1.5)) || (parentSize.y > ((int)GetSize().y * 1.5))) - { - CenterOnParent(); - centerfail = false; - } - } - - if (centerfail) - CenterOnScreen(); -} - -void wxDialogWithHelpers::SmartCenterFit() -{ - Fit(); - - const wxString dlgName(GetDialogName()); - if (dlgName.IsEmpty()) - { - DoAutoCenter(); - return; - } - - if (wxConfigBase* cfg = wxConfigBase::Get(false)) - { - wxRect screenRect(GetScreenRect()); - - IniLoader loader(cfg); - ScopedIniGroup group(loader, L"DialogPositions"); - cfg->SetRecordDefaults(false); - - if (GetWindowStyle() & wxRESIZE_BORDER) - { - wxSize size; - loader.Entry(dlgName + L"_Size", size, screenRect.GetSize()); - SetSize(size); - } - - if (!cfg->Exists(dlgName + L"_Pos")) - DoAutoCenter(); - else - { - wxPoint pos; - loader.Entry(dlgName + L"_Pos", pos, screenRect.GetPosition()); - SetPosition(pos); - } - cfg->SetRecordDefaults(true); - } -} - -// Overrides wxDialog behavior to include automatic Fit() and CenterOnParent/Screen. The centering -// is based on a heuristic the centers against the parent window if the parent window is at least -// 75% larger than the fitted dialog. -int wxDialogWithHelpers::ShowModal() -{ - SmartCenterFit(); - m_CreatedRect = GetScreenRect(); - return wxDialog::ShowModal(); -} - -// Overrides wxDialog behavior to include automatic Fit() and CenterOnParent/Screen. The centering -// is based on a heuristic the centers against the parent window if the parent window is at least -// 75% larger than the fitted dialog. -bool wxDialogWithHelpers::Show(bool show) -{ - if (show) - { - SmartCenterFit(); - m_CreatedRect = GetScreenRect(); - } - return wxDialog::Show(show); -} - -wxStaticText& wxDialogWithHelpers::Label(const wxString& label) -{ - return *new wxStaticText(this, wxID_ANY, label, wxDefaultPosition, wxDefaultSize, wxALIGN_CENTER_VERTICAL); -} - -pxStaticText& wxDialogWithHelpers::Text(const wxString& label) -{ - return *new pxStaticText(this, label); -} - -pxStaticText& wxDialogWithHelpers::Heading(const wxString& label) -{ - return *new pxStaticHeading(this, label); -} - -void wxDialogWithHelpers::RememberPosition() -{ - // Save the dialog position if the dialog is named... - // FIXME : This doesn't get called if the app is exited by alt-f4'ing the main app window. - // ... not sure how to fix that yet. I could register a list of open windows into wxAppWithHelpers - // that systematically get closed. Seems like work, maybe later. --air - - if (wxConfigBase* cfg = IsIconized() ? NULL : wxConfigBase::Get(false)) - { - const wxString dlgName(GetDialogName()); - const wxRect screenRect(GetScreenRect()); - if (!dlgName.IsEmpty() && (m_CreatedRect != screenRect)) - { - wxPoint pos(screenRect.GetPosition()); - IniSaver saver(cfg); - ScopedIniGroup group(saver, L"DialogPositions"); - - if (GetWindowStyle() & wxRESIZE_BORDER) - { - wxSize size(screenRect.GetSize()); - saver.Entry(dlgName + L"_Size", size, screenRect.GetSize()); - } - saver.Entry(dlgName + L"_Pos", pos, screenRect.GetPosition()); - } - } -} - -void wxDialogWithHelpers::OnOkCancel(wxCommandEvent& evt) -{ - RememberPosition(); - - // Modal dialogs should be destroyed after ShowModal returns, otherwise there - // might be double delete problems if the dialog was declared on the stack. - if (!IsModal()) - Destroy(); - else - evt.Skip(); -} - -void wxDialogWithHelpers::AddOkCancel(wxSizer& sizer, bool hasApply) -{ - wxStdDialogButtonSizer& s_buttons(*new wxStdDialogButtonSizer()); - - s_buttons.AddButton(new wxButton(this, wxID_OK)); - s_buttons.AddButton(new wxButton(this, wxID_CANCEL)); - - if (hasApply) - s_buttons.AddButton(new wxButton(this, wxID_APPLY)); - - m_extraButtonSizer = new wxBoxSizer(wxHORIZONTAL); - - // Add the context-sensitive help button on the caption for the platforms - // which support it (currently MSW only) - if (m_hasContextHelp) - { - SetExtraStyle(wxDIALOG_EX_CONTEXTHELP); -#ifndef __WXMSW__ - *m_extraButtonSizer += new wxContextHelpButton(this) | StdButton(); -#endif - } - - // create a sizer to hold the help and ok/cancel buttons, for platforms - // that need a custom help icon. [fixme: help icon prolly better off somewhere else] - wxFlexGridSizer& flex(*new wxFlexGridSizer(2)); - flex.AddGrowableCol(0, 1); - flex.AddGrowableCol(1, 15); - - flex += m_extraButtonSizer | pxAlignLeft; - flex += s_buttons | (pxExpand & pxCenter); - - sizer += flex | StdExpand(); - - s_buttons.Realize(); -} - -void wxDialogWithHelpers::AddOkCancel(wxSizer* sizer, bool hasApply) -{ - if (sizer == NULL) - sizer = GetSizer(); - pxAssert(sizer); - AddOkCancel(*sizer, hasApply); -} - -wxDialogWithHelpers& wxDialogWithHelpers::SetMinWidth(int newWidth) -{ - SetMinSize(wxSize(newWidth, GetMinHeight())); - if (wxSizer* sizer = GetSizer()) - sizer->SetMinSize(wxSize(newWidth, sizer->GetMinSize().GetHeight())); - return *this; -} - -wxDialogWithHelpers& wxDialogWithHelpers::SetMinHeight(int newHeight) -{ - SetMinSize(wxSize(GetMinWidth(), newHeight)); - if (wxSizer* sizer = GetSizer()) - sizer->SetMinSize(wxSize(sizer->GetMinSize().GetWidth(), newHeight)); - return *this; -} - -int wxDialogWithHelpers::GetCharHeight() const -{ - return pxGetCharHeight(this, 1); -} - -// -------------------------------------------------------------------------------------- -// wxPanelWithHelpers Implementations -// -------------------------------------------------------------------------------------- - -wxIMPLEMENT_DYNAMIC_CLASS(wxPanelWithHelpers, wxPanel); - -void wxPanelWithHelpers::Init() -{ -} - -// Creates a Static Box container for this panel. the static box sizer becomes the default -// sizer for this panel. If the panel already has a sizer set, then that sizer will be -// transfered to the new StaticBoxSizer (and will be the first item in it's list, retaining -// consistent and expected layout) -wxPanelWithHelpers* wxPanelWithHelpers::AddFrame(const wxString& label, wxOrientation orient) -{ - wxSizer* oldSizer = GetSizer(); - - SetSizer(new wxStaticBoxSizer(orient, this, label), false); - Init(); - - if (oldSizer) - *this += oldSizer | pxExpand; - - return this; -} - -wxStaticText& wxPanelWithHelpers::Label(const wxString& label) -{ - return *new wxStaticText(this, wxID_ANY, label); -} - -pxStaticText& wxPanelWithHelpers::Text(const wxString& label) -{ - return *new pxStaticText(this, label); -} - -pxStaticText& wxPanelWithHelpers::Heading(const wxString& label) -{ - return *new pxStaticHeading(this, label); -} - -wxPanelWithHelpers::wxPanelWithHelpers(wxWindow* parent, wxOrientation orient, const wxString& staticBoxLabel) - : wxPanel(parent) -{ - SetSizer(new wxStaticBoxSizer(orient, this, staticBoxLabel)); - Init(); -} - -wxPanelWithHelpers::wxPanelWithHelpers(wxWindow* parent, wxOrientation orient) - : wxPanel(parent) -{ - SetSizer(new wxBoxSizer(orient)); - Init(); -} - -wxPanelWithHelpers::wxPanelWithHelpers(wxWindow* parent) - : wxPanel(parent) -{ - Init(); -} - -wxPanelWithHelpers::wxPanelWithHelpers(wxWindow* parent, const wxPoint& pos, const wxSize& size) - : wxPanel(parent, wxID_ANY, pos, size) -{ - Init(); -} - -wxPanelWithHelpers& wxPanelWithHelpers::SetMinWidth(int newWidth) -{ - SetMinSize(wxSize(newWidth, GetMinHeight())); - if (wxSizer* sizer = GetSizer()) - sizer->SetMinSize(wxSize(newWidth, sizer->GetMinSize().GetHeight())); - return *this; -} - -int pxGetCharHeight(const wxWindow* wind, int rows) -{ - if (!wind) - return 0; - wxClientDC dc(wx_const_cast(wxWindow*, wind)); - dc.SetFont(wind->GetFont()); -#ifdef __linux__ - // It seems there is a bad detection of the size of the font (non standard dpi ???). Font are cut in top or bottom. - // Add a correction factor to leave enough room. Visualy 1.7 seems fine but feel free to tune it -- Gregory - return (dc.GetCharHeight() * 1.7 + 1) * rows; -#else - return (dc.GetCharHeight() + 1) * rows; -#endif -} - -int pxGetCharHeight(const wxWindow& wind, int rows) -{ - return pxGetCharHeight(&wind, rows); -} diff --git a/pcsx2/gui/wxSettingsInterface.cpp b/pcsx2/gui/wxSettingsInterface.cpp deleted file mode 100644 index 8505150b24..0000000000 --- a/pcsx2/gui/wxSettingsInterface.cpp +++ /dev/null @@ -1,192 +0,0 @@ -/* PCSX2 - PS2 Emulator for PCs - * Copyright (C) 2021 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 . - */ - -#include "PrecompiledHeader.h" - -#include "StringHelpers.h" -#include "wxSettingsInterface.h" -#include "common/Assertions.h" -#include "common/StringUtil.h" - -wxSettingsInterface::wxSettingsInterface(wxConfigBase* config) - : m_config(config) -{ - m_config->SetPath(wxEmptyString); -} - -wxSettingsInterface::~wxSettingsInterface() -{ - m_config->SetPath(wxEmptyString); -} - -void wxSettingsInterface::CheckPath(const char* section) const -{ - if (m_current_path.compare(section) == 0) - return; - - m_current_path = section; - m_config->SetPath(wxString::Format("/%s", section)); -} - -bool wxSettingsInterface::Save() -{ - pxFailRel("Not implemented"); - return false; -} - -void wxSettingsInterface::Clear() -{ - pxFailRel("Not implemented"); -} - -bool wxSettingsInterface::GetIntValue(const char* section, const char* key, int* value) const -{ - CheckPath(section); - return m_config->Read(key, value); -} - -bool wxSettingsInterface::GetUIntValue(const char* section, const char* key, uint* value) const -{ - CheckPath(section); - - long lvalue; - if (!m_config->Read(key, &lvalue)) - return false; - - *value = static_cast(lvalue); - return true; -} - -bool wxSettingsInterface::GetFloatValue(const char* section, const char* key, float* value) const -{ - CheckPath(section); - return m_config->Read(key, value); -} - -bool wxSettingsInterface::GetDoubleValue(const char* section, const char* key, double* value) const -{ - CheckPath(section); - return m_config->Read(key, value); -} - -bool wxSettingsInterface::GetBoolValue(const char* section, const char* key, bool* value) const -{ - CheckPath(section); - wxString wxKey(key); - if (!m_config->HasEntry(wxKey)) - return false; - - wxString ret = m_config->Read(wxKey); - *value = (ret == wxT("enabled") || ret == wxT("1")); - return true; -} - -bool wxSettingsInterface::GetStringValue(const char* section, const char* key, std::string* value) const -{ - CheckPath(section); - wxString wxKey(key); - if (!m_config->HasEntry(wxKey)) - return false; - - wxString ret = m_config->Read(wxKey); - *value = StringUtil::wxStringToUTF8String(ret); - return true; -} - -void wxSettingsInterface::SetIntValue(const char* section, const char* key, int value) -{ - CheckPath(section); - m_config->Write(key, value); -} - -void wxSettingsInterface::SetUIntValue(const char* section, const char* key, uint value) -{ - CheckPath(section); - m_config->Write(key, static_cast(value)); -} - -void wxSettingsInterface::SetFloatValue(const char* section, const char* key, float value) -{ - CheckPath(section); - m_config->Write(key, value); -} - -void wxSettingsInterface::SetDoubleValue(const char* section, const char* key, double value) -{ - CheckPath(section); - m_config->Write(key, value); -} - -void wxSettingsInterface::SetBoolValue(const char* section, const char* key, bool value) -{ - CheckPath(section); - m_config->Write(key, value ? wxT("enabled") : wxT("disabled")); -} - -void wxSettingsInterface::SetStringValue(const char* section, const char* key, const char* value) -{ - CheckPath(section); - m_config->Write(key, wxString::FromUTF8(value)); -} - -std::vector wxSettingsInterface::GetStringList(const char* section, const char* key) const -{ - pxFailRel("Not implemented"); - return {}; -} - -void wxSettingsInterface::SetStringList(const char* section, const char* key, const std::vector& items) -{ - pxFailRel("Not implemented"); -} - -bool wxSettingsInterface::RemoveFromStringList(const char* section, const char* key, const char* item) -{ - pxFailRel("Not implemented"); - return false; -} - -bool wxSettingsInterface::AddToStringList(const char* section, const char* key, const char* item) -{ - return false; -} - -std::vector> wxSettingsInterface::GetKeyValueList(const char* section) const -{ - pxFailRel("Not implemented"); - return {}; -} - -void wxSettingsInterface::SetKeyValueList(const char* section, const std::vector>& items) -{ - pxFailRel("Not implemented"); -} - -bool wxSettingsInterface::ContainsValue(const char* section, const char* key) const -{ - CheckPath(section); - return m_config->Exists(key); -} - -void wxSettingsInterface::DeleteValue(const char* section, const char* key) -{ - CheckPath(section); - m_config->DeleteEntry(key); -} - -void wxSettingsInterface::ClearSection(const char* section) -{ - pxFailRel("Not implemented"); -} diff --git a/pcsx2/gui/wxSettingsInterface.h b/pcsx2/gui/wxSettingsInterface.h deleted file mode 100644 index bfdbac14e1..0000000000 --- a/pcsx2/gui/wxSettingsInterface.h +++ /dev/null @@ -1,64 +0,0 @@ -/* PCSX2 - PS2 Emulator for PCs - * Copyright (C) 2021 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 . - */ - -#pragma once - -#include -#include - -#include "common/SettingsInterface.h" - -class wxSettingsInterface : public SettingsInterface -{ -public: - wxSettingsInterface(wxConfigBase* config); - ~wxSettingsInterface(); - - bool Save() override; - void Clear() override; - - bool GetIntValue(const char* section, const char* key, int* value) const override; - bool GetUIntValue(const char* section, const char* key, uint* value) const override; - bool GetFloatValue(const char* section, const char* key, float* value) const override; - bool GetDoubleValue(const char* section, const char* key, double* value) const override; - bool GetBoolValue(const char* section, const char* key, bool* value) const override; - bool GetStringValue(const char* section, const char* key, std::string* value) const override; - - void SetIntValue(const char* section, const char* key, int value) override; - void SetUIntValue(const char* section, const char* key, uint value) override; - void SetFloatValue(const char* section, const char* key, float value) override; - 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; - - std::vector GetStringList(const char* section, const char* key) const override; - void SetStringList(const char* section, const char* key, const std::vector& 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; - - std::vector> GetKeyValueList(const char* section) const override; - void SetKeyValueList(const char* section, const std::vector>& items) 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; - -private: - void CheckPath(const char* section) const; - - wxConfigBase* m_config; - mutable wxString m_current_path; -}; diff --git a/pcsx2/pcsx2.vcxproj b/pcsx2/pcsx2.vcxproj deleted file mode 100644 index 58f21e84b3..0000000000 --- a/pcsx2/pcsx2.vcxproj +++ /dev/null @@ -1,1064 +0,0 @@ - - - - - - - {1CEFD830-2B76-4596-A4EE-BCD7280A60BD} - - - - Application - Unicode - $(DefaultPlatformToolset) - true - true - false - - - - - - - - - - - - - - - - AllRules.ruleset - $(EXEString) - - - - $(SolutionDir)3rdparty\wxWidgets3.0\include;%(AdditionalIncludeDirectories) - $(SolutionDir)3rdparty\xbyak;%(AdditionalIncludeDirectories) - $(SolutionDir)3rdparty\xz\xz\src\liblzma\api;%(AdditionalIncludeDirectories) - $(SolutionDir)3rdparty\zlib;%(AdditionalIncludeDirectories) - $(SolutionDir)3rdparty\libpng;%(AdditionalIncludeDirectories) - $(SolutionDir)3rdparty\glad\include;%(AdditionalIncludeDirectories) - $(SolutionDir)3rdparty\cubeb\include;%(AdditionalIncludeDirectories) - $(SolutionDir)3rdparty\imgui\include;$(SolutionDir)3rdparty\imgui\include;%(AdditionalIncludeDirectories) - $(SolutionDir)3rdparty\libzip;$(SolutionDir)3rdparty\libzip\libzip\lib;%(AdditionalIncludeDirectories) - $(SolutionDir)3rdparty\d3d12memalloc\include;%(AdditionalIncludeDirectories) - %(AdditionalIncludeDirectories);$(SolutionDir)3rdparty\zstd\zstd\lib - %(AdditionalIncludeDirectories);$(SolutionDir)3rdparty\rapidyaml\rapidyaml\ext\c4core\src\c4\ext\fast_float\include - %(AdditionalIncludeDirectories);$(SolutionDir)3rdparty\ffmpeg\include - Async - Use - PrecompiledHeader.h - PrecompiledHeader.h;%(ForcedIncludeFiles) - NoExtensions - /Zc:externConstexpr %(AdditionalOptions) - WIN32_LEAN_AND_MEAN;ZIP_STATIC;LZMA_API_STATIC;BUILD_DX=1;ENABLE_OPENGL;ENABLE_VULKAN;SPU2X_CUBEB;DIRECTINPUT_VERSION=0x0800;%(PreprocessorDefinitions) - PCSX2_DEBUG;PCSX2_DEVBUILD;_SECURE_SCL_=1;%(PreprocessorDefinitions) - PCSX2_DEVEL;PCSX2_DEVBUILD;NDEBUG;_SECURE_SCL_=1;%(PreprocessorDefinitions) - NDEBUG;_SECURE_SCL_=0;%(PreprocessorDefinitions) - PCSX2_CI;%(PreprocessorDefinitions) - - __SSE4_1__;%(PreprocessorDefinitions) - NotSet - AdvancedVectorExtensions2 - false - $(IntDir)%(RelativeDir) - - - Precise - - - Yes - comctl32.lib;ws2_32.lib;shlwapi.lib;winmm.lib;rpcrt4.lib;iphlpapi.lib;dsound.lib;%(AdditionalDependencies) - dxguid.lib;dinput8.lib;hid.lib;PowrProf.lib;d3dcompiler.lib;d3d11.lib;dxgi.lib;strmiids.lib;opengl32.lib;comsuppw.lib;OneCore.lib;%(AdditionalDependencies) - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - cmd.exe /c %(RelativeDir)bin2cpp.cmd %(Filename)%(Extension) - %(RelativeDir)%(Filename).h - - - cmd.exe /c %(RelativeDir)bin2cpp.cmd %(Filename)%(Extension) - %(RelativeDir)%(Filename).h - - - cmd.exe /c %(RelativeDir)bin2cpp.cmd %(Filename)%(Extension) - %(RelativeDir)%(Filename).h - - - cmd.exe /c %(RelativeDir)bin2cpp.cmd %(Filename)%(Extension) - %(RelativeDir)%(Filename).h - - - cmd.exe /c %(RelativeDir)bin2cpp.cmd %(Filename)%(Extension) - %(RelativeDir)%(Filename).h - - - cmd.exe /c %(RelativeDir)bin2cpp.cmd %(Filename)%(Extension) - %(RelativeDir)%(Filename).h - - - cmd.exe /c %(RelativeDir)bin2cpp.cmd %(Filename)%(Extension) - %(RelativeDir)%(Filename).h - - - cmd.exe /c %(RelativeDir)bin2cpp.cmd %(Filename)%(Extension) - %(RelativeDir)%(Filename).h - - - cmd.exe /c %(RelativeDir)bin2cpp.cmd %(Filename)%(Extension) - %(RelativeDir)%(Filename).h - - - cmd.exe /c %(RelativeDir)bin2cpp.cmd %(Filename)%(Extension) - %(RelativeDir)%(Filename).h - - - cmd.exe /c %(RelativeDir)bin2cpp.cmd %(Filename)%(Extension) - %(RelativeDir)%(Filename).h - - - cmd.exe /c %(RelativeDir)bin2cpp.cmd %(Filename)%(Extension) - %(RelativeDir)%(Filename).h - - - cmd.exe /c %(RelativeDir)bin2cpp.cmd %(Filename)%(Extension) - %(RelativeDir)%(Filename).h - - - cmd.exe /c %(RelativeDir)bin2cpp.cmd %(Filename)%(Extension) - %(RelativeDir)%(Filename).h - - - cmd.exe /c %(RelativeDir)bin2cpp.cmd %(Filename)%(Extension) - %(RelativeDir)%(Filename).h - - - - - - - - - - - - - - - - true - - - true - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - true - - - true - - - - - - - - - - - - true - - - - - - - Create - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - true - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - true - - - true - - - true - - - true - - - true - - - true - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - true - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - true - - - true - - - true - - - true - - - true - - - true - - - true - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - $(SolutionDir)3rdparty\wxwidgets3.0\$(PlatformName);$(SolutionDir)3rdparty\wxwidgets3.0\include;%(AdditionalIncludeDirectories) - - - - - - - - - - {449ad25e-424a-4714-babc-68706cdcc33b} - - - {bc236261-77e8-4567-8d09-45cd02965eb6} - - - {d6973076-9317-4ef2-a0b8-b7a18ac0713e} - - - {0fae817d-9a32-4830-857e-81da57246e16} - false - - - {e9b51944-7e6d-4bcd-83f2-7bbd5a46182d} - - - {24c45343-fd20-5c92-81c1-35a2ae841e79} - - - {3fcc50c2-81e9-5db2-b8d8-2129427568b1} - - - {6744dad8-9c70-574a-bff2-9f8dddb24a75} - - - {12728250-16ec-4dc6-94d7-e21dd88947f8} - - - {a0d2b3ad-1f72-4ee3-8b5c-f2c358da35f0} - - - {2f6c0388-20cb-4242-9f6c-a6ebb6a83f47} - false - - - {677b7d11-d5e1-40b3-88b1-9a4df83d2213} - false - - - {ed2f21fd-0a36-4a8f-9b90-e7d92a2acb63} - - - {bf74c473-dc04-44b3-92e8-4145f4e77342} - - - {de9653b6-17dd-356a-9ee0-28a731772587} - - - {88fb34ec-845e-4f21-a552-f1573b9ed167} - - - {20b2e9fe-f020-42a0-b324-956f5b06ea68} - - - {4639972e-424e-4e13-8b07-ca403c481346} - - - {c0293b32-5acf-40f0-aa6c-e6da6f3bf33a} - - - - - - - - \ No newline at end of file diff --git a/pcsx2/pcsx2.vcxproj.filters b/pcsx2/pcsx2.vcxproj.filters deleted file mode 100644 index 87d517f3f7..0000000000 --- a/pcsx2/pcsx2.vcxproj.filters +++ /dev/null @@ -1,2753 +0,0 @@ - - - - - {d1e5f218-107b-4e8c-9527-55cb9388b544} - - - {5dc872c1-db13-4c70-9d44-a3fc4b6fc241} - - - {3cda6808-a2bd-48a7-912e-161a7d0f5bce} - - - {a3f5cf44-2d2b-4822-b530-57286c85374a} - - - {a7187804-2892-4f90-9360-cf2bd6c31540} - - - {8cb5090c-c891-498b-bec2-183d4deca91f} - - - {1c7a3f63-9285-4202-84c5-278a854db932} - - - {33efddb9-1e44-44dc-b857-45b49b22c750} - - - {c1691a12-a08a-4ca7-ba66-b4ff4c7f30a5} - - - {57bf864f-d216-4d97-968d-4c0ff4f166ec} - - - {7865c84c-6431-4781-a016-45eeac5cad2c} - - - {c4ee1612-1ce9-4c74-8323-f98851014558} - - - {a210a307-e8bf-4bbc-8770-e720cc7fc953} - - - {e14056ea-b126-4e5b-94e0-3ce09463970b} - - - {4ba4f2e2-5195-471b-9a0d-d84fa3fb96aa} - - - {088d96f1-2b69-4c53-9f14-026db3368905} - - - {259f9912-d495-45da-b894-8312fde18364} - - - {5f98cd09-41cd-4c1e-ad7d-06a8859e770c} - - - {620afa6f-3aea-4093-bed0-46fd37a5ad6d} - - - {f9de9bc0-4bc5-4fb5-8a1f-04bc31a82b07} - - - {a6b34a13-67af-45fd-a2fb-2c86a762dd39} - - - {89c4908d-b0a1-4c69-9104-00e0af69a714} - - - {f5c8a322-8f48-44a8-97ad-4bd0017bdfe7} - - - {79aa85ae-b75b-421a-838c-8144d9dacd76} - - - {094486fe-8ffc-4b98-81d6-6c381554fd8e} - - - {409f8654-a1cd-4624-9ede-3ae83a707876} - - - {e373743e-ee9f-421c-aa28-2d02a08bb252} - - - {5a3ec813-4719-4ca1-9988-3587ea402a2b} - - - {a11bcdd7-008e-40aa-9376-d1cc2bf4e67c} - - - {8aa116a4-8bae-4ec0-a919-be8a320491eb} - - - {4dab2d06-69e0-4f3e-b6d3-45e5e85af940} - - - {67e51016-d1db-44d2-910d-349d2833f798} - - - {5602cc18-9d1c-49c8-9509-7e4cf9ecd91b} - - - {ffa6c572-1615-4dad-80c3-333e3eb476f2} - - - {bba4d866-e26f-49c8-83fb-fb970c33798b} - - - {96937113-fef0-4e63-92e5-a30edcc78dc2} - - - {5e741f2d-9e0b-4e43-b2a2-44a8370e1546} - - - {c5237754-2509-4291-b869-c5dacc1a8aba} - - - {99a8dd4e-22db-4818-8799-7646b363b725} - - - {03bc7561-2b23-469a-936d-0463fb0ee9d2} - - - {1f18feb4-85f0-455d-927a-d4d6b226d8a5} - - - {ebbed875-840b-4b7b-96e4-4da2d4e38d4b} - - - {510ab530-0308-44f7-a7e3-08e474065651} - - - {485e39c6-aa13-4094-8ec8-3113cd0a1734} - - - {f8e51d3e-dc60-4aa9-9d37-c7c1ccaacd02} - - - {7a0a1104-f9ee-4cd1-aaba-f8267eba1658} - - - {7b917318-5a5e-44c0-8463-1dfab67f3b67} - - - {be861049-a142-4650-85a5-a2fdd4eef011} - - - {a5904fb6-e846-4cbf-940c-ca1c604140a0} - - - {157740ae-b338-4d7f-81cb-cffa9d39a756} - - - {bed493d6-96dc-4057-a1f2-31f88ec927d9} - - - {85c5a0d2-6404-439f-8756-d908a1442b96} - - - {8d5454f9-590c-4c53-aae1-8391c6465e50} - - - {bc20f567-851d-4440-a3fd-ef470241962e} - - - {9e9b52d7-7b1c-44b2-82d8-1e0d8085e7e0} - - - {93da3d51-9457-4fde-b374-c2fbe4780f59} - - - {57c4be94-9a2c-469b-9b02-b3b324857fc5} - - - {12c15e0b-4647-4ce2-9bc4-89e6e9e8f531} - - - {a0723e13-c839-464d-8cfa-6c440ae03537} - - - {928400ad-4db4-44ca-9a6d-c65e489c8ea0} - - - {7c01f24d-2141-4054-b3f9-1d9c67ae7ad6} - - - {db2fc75d-6552-4991-9155-885abd8796e4} - - - {d426ae2b-9ad6-473d-adba-57b7bdac87db} - - - {229bf045-e378-4ec5-bdf8-43938762511e} - - - {64b05697-98cd-48ed-b5a5-cb0f6044ca15} - - - {18d51d37-34bc-489c-b84f-31362bbb9243} - - - {7e1e6046-08a0-456e-8e03-47bb3813d9b0} - - - {7b86e42b-816a-4d08-9de8-fa308d2b8d88} - - - {df9de75c-2272-4f73-b2a0-4f9f492ba1e9} - - - {e1cbcaf6-9f65-4f14-9e89-27dd0f10e047} - - - {38f3b97b-eac6-4085-b6b0-567ec5081242} - - - {f65f5552-af5b-47b0-9029-48d4dc33c2f9} - - - {85d4539e-fc10-4369-98f0-0ee9a6a9cc00} - - - {5be27550-9ed4-4c27-9e69-e5933613c4e8} - - - {8a0a142d-a4e0-4e8d-afbf-955fe1697eca} - - - {c140280a-87c6-4433-b877-f0bf8cca9345} - - - {81af12a1-9605-4dd8-a70e-fd4ae81fbdd5} - - - {eaa22029-6839-4c09-93e1-faea8d8c0d7c} - - - {78f5077b-255e-435e-9a51-352171dfaee8} - - - {65f21394-287a-471b-a0c1-d8f0d5d95a81} - - - {98829aa9-bb81-4564-bd6e-128719c4faa0} - - - {0ba8b3c9-0915-4228-a572-3526190efb00} - - - {7699b8f4-a217-4d8a-bf08-e28e58b3ae07} - - - {8c7c08ed-6c87-4b10-82ca-c832e84828c4} - - - {9cb1aa40-043d-47f7-86fd-837e10012e75} - - - {6e0dc702-cb44-4a22-a595-4cbd335f31a4} - - - {eb697f5b-85f5-424a-a7e4-8d8b73d3426e} - - - - - Misc - - - Docs - - - System\Ps2\EmotionEngine\Hardware - - - System\Ps2\EmotionEngine\VU\Dynarec\microVU - - - System\Ps2\EmotionEngine\VU\Dynarec\microVU - - - System\Ps2\EmotionEngine\VU\Dynarec\microVU - - - System\Ps2\EmotionEngine\VU\Dynarec\microVU - - - System\Ps2\EmotionEngine\VU\Dynarec\microVU - - - System\Ps2\EmotionEngine\VU\Dynarec\microVU - - - System\Ps2\EmotionEngine\VU\Dynarec\microVU - - - System\Ps2\EmotionEngine\VU\Dynarec\microVU - - - System\Ps2\EmotionEngine\VU\Dynarec\microVU - - - System\Ps2\EmotionEngine\VU\Dynarec\microVU - - - System\Ps2\EmotionEngine\VU\Dynarec\microVU - - - System\Ps2\EmotionEngine\VU\Dynarec\microVU - - - System\Ps2\EmotionEngine\VU\Dynarec\microVU - - - AppHost\Dialogs - - - System\Ps2\PAD - - - System\Ps2\GS\Shaders\OpenGL - - - System\Ps2\GS\Shaders\OpenGL - - - System\Ps2\GS\Shaders\OpenGL - - - System\Ps2\GS\Shaders\OpenGL - - - System\Ps2\GS\Shaders\OpenGL - - - System\Ps2\GS\Shaders\OpenGL - - - System\Ps2\GS\Shaders\OpenGL - - - System\Ps2\GS\Shaders\Common - - - System\Ps2\GS\Shaders\Vulkan - - - System\Ps2\GS\Shaders\Vulkan - - - System\Ps2\GS\Shaders\Vulkan - - - System\Ps2\GS\Shaders\Vulkan - - - System\Ps2\GS\Shaders\Direct3D - - - System\Ps2\GS\Shaders\Direct3D - - - System\Ps2\GS\Shaders\Direct3D - - - System\Ps2\GS\Shaders\Direct3D - - - System\Ps2\GS\Shaders\Direct3D - - - - - Misc - - - Misc - - - Misc - - - Misc - - - System - - - System - - - System - - - System - - - System - - - System - - - System - - - System\ISO - - - System\Ps2 - - - System\Ps2 - - - System\Ps2\EmotionEngine - - - System\Ps2\EmotionEngine\Hardware - - - System\Ps2\EmotionEngine\Hardware - - - System\Ps2\EmotionEngine\Hardware - - - System\Ps2\EmotionEngine\Hardware - - - System\Ps2\EmotionEngine\Memory - - - System\Ps2\EmotionEngine\Memory - - - System\Ps2\EmotionEngine\Memory - - - System\Ps2\EmotionEngine\Memory - - - System\Ps2\EmotionEngine\VU - - - System\Ps2\EmotionEngine\VU - - - System\Ps2\EmotionEngine\VU - - - System\Ps2\EmotionEngine\VU\Dynarec\microVU - - - System\Ps2\EmotionEngine\VU\Interpreter - - - System\Ps2\EmotionEngine\VU\Interpreter - - - System\Ps2\EmotionEngine\VU\Interpreter - - - System\Ps2\EmotionEngine\VU\Interpreter - - - System\Ps2\EmotionEngine\VU\Interpreter - - - System\Ps2\EmotionEngine\VU\Interpreter - - - System\Ps2\EmotionEngine\VU\Interpreter - - - System\Ps2\EmotionEngine\DMAC\Sif - - - System\Ps2\EmotionEngine\DMAC\Sif - - - System\Ps2\EmotionEngine\DMAC\Sif - - - System\Ps2\EmotionEngine\DMAC\Vif - - - System\Ps2\EmotionEngine\DMAC\Vif - - - System\Ps2\EmotionEngine\DMAC\Vif - - - System\Ps2\EmotionEngine\DMAC\Vif - - - System\Ps2\EmotionEngine\DMAC\Vif - - - System\Ps2\EmotionEngine\DMAC\Vif - - - System\Ps2\EmotionEngine\DMAC\Vif\Unpack - - - System\Ps2\EmotionEngine\DMAC\Vif\Unpack\newVif - - - System\Ps2\EmotionEngine\DMAC\Vif\Unpack\newVif\Dynarec - - - System\Ps2\EmotionEngine\DMAC\Vif\Unpack\newVif\Dynarec - - - System\Ps2\EmotionEngine\DMAC\SPR - - - System\Ps2\EmotionEngine\DMAC\Gif - - - System\Ps2\EmotionEngine\EE - - - System\Ps2\EmotionEngine\EE\Dynarec - - - System\Ps2\EmotionEngine\EE\Dynarec - - - System\Ps2\EmotionEngine\EE\Dynarec - - - System\Ps2\EmotionEngine\EE\Dynarec - - - System\Ps2\EmotionEngine\EE\Dynarec - - - System\Ps2\EmotionEngine\EE\Dynarec - - - System\Ps2\EmotionEngine\EE\Dynarec\ix86-32 - - - System\Ps2\EmotionEngine\EE\Dynarec\ix86-32 - - - System\Ps2\EmotionEngine\EE\Dynarec\ix86-32 - - - System\Ps2\EmotionEngine\EE\Dynarec\ix86-32 - - - System\Ps2\EmotionEngine\EE\Dynarec\ix86-32 - - - System\Ps2\EmotionEngine\EE\Dynarec\ix86-32 - - - System\Ps2\EmotionEngine\EE\Dynarec\ix86-32 - - - System\Ps2\EmotionEngine\EE\Dynarec\ix86-32 - - - System\Ps2\EmotionEngine\EE\Dynarec\ix86-32 - - - System\Ps2\EmotionEngine\EE\Dynarec\ix86-32 - - - System\Ps2\EmotionEngine\EE\Interpreter - - - System\Ps2\EmotionEngine\EE\Interpreter - - - System\Ps2\EmotionEngine\EE\Interpreter - - - System\Ps2\EmotionEngine\EE\Interpreter - - - System\Ps2\EmotionEngine\EE\Interpreter - - - System\Ps2\EmotionEngine\EE\Interpreter - - - System\Ps2\EmotionEngine\EE\Interpreter - - - System\Ps2\Iop - - - System\Ps2\Iop - - - System\Ps2\Iop - - - System\Ps2\Iop - - - System\Ps2\Iop - - - System\Ps2\Iop - - - System\Ps2\Iop - - - System\Ps2\Iop - - - System\Ps2\Iop - - - System\Ps2\Iop\Dynarec - - - System\Ps2\Iop\Dynarec - - - System\Ps2\Iop\Hardware - - - System\Ps2\Iop\Hardware - - - System\Ps2\Iop\Hardware - - - System\Ps2\Iop\CDVD - - - System\Ps2\Iop\CDVD - - - System\Ps2\Iop\CDVD - - - System\Ps2\Iop\CDVD - - - System\Ps2\IPU - - - System\Ps2\IPU - - - System\Ps2\IPU - - - System\Ps2\IPU - - - System\Ps2\IPU\mpeg2lib - - - System\Ps2\IPU\mpeg2lib - - - System\Ps2\GS\GIF - - - System\Ps2\GS\GIF - - - System\Ps2\Debug - - - System\Ps2\Debug - - - System\Ps2\Debug - - - System\Ps2\Debug - - - System\Ps2\Debug\rdebug - - - System\Ps2\Debug\rdebug - - - System\Ps2\Debug\rdebug - - - System\Ps2\Debug\rdebug - - - System\Ps2\Debug\rdebug - - - System\Ps2\Debug\rdebug - - - System\Ps2\iCore - - - System\Ps2\iCore - - - System\IsoFS - - - System\IsoFS - - - System\IsoFS - - - AppHost - - - AppHost - - - AppHost - - - AppHost - - - AppHost - - - AppHost - - - AppHost - - - AppHost - - - AppHost - - - AppHost - - - AppHost - - - AppHost - - - AppHost - - - AppHost - - - AppHost - - - AppHost - - - AppHost - - - AppHost - - - AppHost\Dialogs - - - AppHost\Dialogs - - - AppHost\Dialogs - - - AppHost\Dialogs - - - AppHost\Dialogs - - - AppHost\Dialogs - - - AppHost\Dialogs - - - AppHost\Dialogs - - - AppHost\Dialogs - - - AppHost\Dialogs - - - AppHost\Dialogs - - - AppHost\Panels - - - AppHost\Panels - - - AppHost\Panels - - - AppHost\Panels - - - AppHost\Panels - - - AppHost\Panels - - - AppHost\Panels - - - AppHost\Panels - - - AppHost\Panels - - - AppHost\Panels - - - AppHost\Panels - - - AppHost\Panels - - - AppHost\Win32 - - - AppHost\Win32 - - - AppHost\Linux - - - AppHost - - - AppHost - - - AppHost - - - Misc - - - Misc - - - System\Ps2\IPU - - - System\Ps2 - - - AppHost - - - AppHost\Dialogs - - - AppHost\Panels - - - System\Ps2\GS\GIF - - - System\Ps2\GS - - - System\ISO - - - System\ISO - - - System\ISO - - - System\ISO - - - System\ISO - - - System\ISO - - - System\Ps2\Debug - - - System\Ps2\Debug - - - System\Ps2\Debug - - - AppHost\Debugger - - - AppHost\Debugger - - - System\Ps2\Debug - - - System\Ps2\Debug - - - AppHost\Debugger - - - AppHost\Debugger - - - AppHost\Debugger - - - AppHost\Debugger - - - System\Ps2\Debug - - - AppHost\Debugger - - - System\Ps2\EmotionEngine\DMAC\Sif - - - AppHost\Debugger - - - System\ISO - - - System\Ps2\Debug - - - System\Ps2\Debug - - - System\Ps2\Debug - - - System\Ps2\Debug - - - System\ISO - - - System\ISO - - - System\ISO - - - System\ISO - - - AppHost\Win32 - - - AppHost\Win32 - - - AppHost\Linux - - - System\Ps2\Iop - - - System\Ps2 - - - System\Ps2\Iop - - - System\Ps2\Iop - - - Recording - - - Recording - - - Recording - - - System\Ps2\IPU - - - Recording - - - System\Ps2\Iop\CDVD - - - System\Ps2\Iop\CDVD - - - System\Ps2\Iop\CDVD\Windows - - - System\Ps2\Iop\CDVD\Windows - - - System\Ps2\Iop\CDVD\Linux - - - System\Ps2\Iop\CDVD\Linux - - - AppHost - - - System - - - System\Ps2\Iop\FW - - - System\Ps2\SPU2 - - - System\Ps2\SPU2 - - - System\Ps2\SPU2 - - - System\Ps2\SPU2 - - - System\Ps2\SPU2 - - - System\Ps2\SPU2 - - - System\Ps2\SPU2 - - - System\Ps2\SPU2 - - - System\Ps2\SPU2 - - - System\Ps2\SPU2 - - - System\Ps2\SPU2 - - - System\Ps2\SPU2 - - - System\Ps2\SPU2 - - - System\Ps2\SPU2 - - - System\Ps2\SPU2 - - - System\Ps2\SPU2 - - - System\Ps2\SPU2 - - - System\Ps2\SPU2 - - - System\Ps2\SPU2 - - - System\Ps2\SPU2 - - - System\Ps2\SPU2 - - - System\Ps2\SPU2 - - - System\Ps2\SPU2 - - - System\Ps2\SPU2 - - - System\Ps2\SPU2 - - - System\Ps2\DEV9 - - - System\Ps2\DEV9\ATA\Commands - - - System\Ps2\DEV9\ATA\Commands - - - System\Ps2\DEV9\ATA\Commands - - - System\Ps2\DEV9\ATA\Commands - - - System\Ps2\DEV9\ATA\Commands - - - System\Ps2\DEV9\ATA\Commands - - - System\Ps2\DEV9\ATA\Commands - - - System\Ps2\DEV9\ATA - - - System\Ps2\DEV9\ATA - - - System\Ps2\DEV9\ATA - - - System\Ps2\DEV9\ATA - - - System\Ps2\DEV9\ATA - - - System\Ps2\DEV9 - - - System\Ps2\DEV9 - - - System\Ps2\DEV9 - - - System\Ps2\DEV9 - - - System\Ps2\DEV9\InternalServers - - - System\Ps2\DEV9\InternalServers - - - System\Ps2\DEV9\InternalServers - - - System\Ps2\DEV9\PacketReader\ARP - - - System\Ps2\DEV9\PacketReader\IP\ICMP - - - System\Ps2\DEV9\PacketReader\IP\TCP - - - System\Ps2\DEV9\PacketReader\IP\TCP - - - System\Ps2\DEV9\PacketReader\IP\UDP\DHCP - - - System\Ps2\DEV9\PacketReader\IP\UDP\DHCP - - - System\Ps2\DEV9\PacketReader\IP\UDP\DNS - - - System\Ps2\DEV9\PacketReader\IP\UDP\DNS - - - System\Ps2\DEV9\PacketReader\IP\UDP - - - System\Ps2\DEV9\PacketReader\IP - - - System\Ps2\DEV9\PacketReader\IP - - - System\Ps2\DEV9\PacketReader - - - System\Ps2\DEV9\PacketReader - - - System\Ps2\DEV9 - - - System\Ps2\DEV9\Sessions - - - System\Ps2\DEV9\Sessions\ICMP_Session - - - System\Ps2\DEV9\Sessions\TCP_Session - - - System\Ps2\DEV9\Sessions\TCP_Session - - - System\Ps2\DEV9\Sessions\TCP_Session - - - System\Ps2\DEV9\Sessions\UDP_Session - - - System\Ps2\DEV9\Sessions\UDP_Session - - - System\Ps2\DEV9 - - - System\Ps2\DEV9 - - - System\Ps2\DEV9 - - - System\Ps2\DEV9 - - - System\Ps2\DEV9 - - - System\Ps2\PAD - - - System\Ps2\PAD - - - System\Ps2\PAD - - - System\Ps2\PAD - - - System\Ps2\PAD - - - System\Ps2\PAD - - - System\Ps2\PAD - - - System\Ps2\PAD - - - System\Ps2\PAD - - - System\Ps2\PAD - - - System\Ps2\PAD - - - System\Ps2\PAD - - - System\Ps2\PAD - - - System\Ps2\PAD - - - System\Ps2\PAD - - - System\Ps2\PAD - - - System\Ps2\PAD - - - System\Ps2\GS - - - System\Ps2\GS - - - System\Ps2\GS - - - System\Ps2\GS - - - System\Ps2\GS - - - System\Ps2\GS - - - System\Ps2\GS - - - System\Ps2\GS - - - System\Ps2\GS - - - System\Ps2\GS - - - System\Ps2\GS - - - System\Ps2\GS - - - System\Ps2\GS - - - System\Ps2\GS - - - System\Ps2\GS - - - System\Ps2\GS - - - System\Ps2\GS - - - System\Ps2\GS - - - System\Ps2\GS - - - AppHost\Dialogs - - - AppHost\Dialogs - - - System\ISO - - - Recording\Utilities - - - System\Ps2\GS\Renderers\Direct3D11 - - - System\Ps2\GS\Renderers\Direct3D11 - - - System\Ps2\GS\Renderers\Direct3D11 - - - System\Ps2\GS\Renderers\OpenGL - - - System\Ps2\GS\Renderers\OpenGL - - - System\Ps2\GS\Renderers\OpenGL - - - System\Ps2\GS\Renderers\OpenGL - - - System\Ps2\GS\Renderers\Software - - - System\Ps2\GS\Renderers\Software - - - System\Ps2\GS\Renderers\Software - - - System\Ps2\GS\Renderers\Software - - - System\Ps2\GS\Renderers\Software - - - System\Ps2\GS\Renderers\Software - - - System\Ps2\GS\Renderers\Software - - - System\Ps2\GS\Renderers\Software - - - System\Ps2\GS\Renderers\Software - - - System\Ps2\GS\Renderers\Software - - - System\Ps2\GS\Renderers\Software - - - System\Ps2\GS\Renderers\Software - - - System\Ps2\GS\Renderers\Null - - - System\Ps2\GS\Renderers\Null - - - System\Ps2\GS\Renderers\Null - - - System\Ps2\GS\Renderers\Hardware - - - System\Ps2\GS\Renderers\Hardware - - - System\Ps2\GS\Renderers\Hardware - - - System\Ps2\GS\Renderers\Hardware - - - System\Ps2\GS\Renderers\Common - - - System\Ps2\GS\Renderers\Common - - - System\Ps2\GS\Renderers\Common - - - System\Ps2\GS\Renderers\Common - - - System\Ps2\GS\Renderers\Common - - - System\Ps2\GS\Renderers\Common - - - System\Ps2\GS\Renderers\Common - - - System\Ps2\GS\Renderers\Common - - - System\Ps2\GS\Window - - - System\Ps2\GS\Window - - - System\Ps2\GS\Window - - - System\Ps2\GS\Renderers\Direct3D11 - - - AppHost - - - AppHost - - - AppHost - - - AppHost - - - AppHost - - - System\Ps2\Iop - - - System\Ps2\Iop - - - AppHost - - - AppHost - - - AppHost - - - AppHost - - - AppHost - - - AppHost - - - System - - - System\Ps2\SPU2 - - - Host - - - Host - - - Host - - - AppHost - - - Host - - - Host - - - Host - - - System\Ps2\GS\Renderers\Vulkan - - - System\Ps2\GS\Renderers\Vulkan - - - System\Ps2\GS\Renderers\Hardware - - - System\Ps2\GS\Renderers\Hardware - - - System\Ps2\EmotionEngine\EE\Dynarec - - - AppHost - - - Host - - - System\Ps2\GS\Renderers\Direct3D12 - - - System\Ps2\GS\Renderers\Direct3D12 - - - AppHost - - - AppHost - - - Host - - - Host - - - Host - - - Host - - - AppHost - - - AppHost - - - AppHost - - - AppHost - - - AppHost - - - Host - - - System\Ps2\Iop - - - System\Ps2\Iop - - - System\Ps2\USB - - - - - Misc - - - Misc - - - Misc - - - System\ISO - - - System\ISO - - - System\Include - - - System\Include - - - System\Include - - - System\Include - - - System\Include - - - System\Include - - - System\Include - - - System\Include - - - System\Ps2\EmotionEngine - - - System\Ps2\EmotionEngine\Hardware - - - System\Ps2\EmotionEngine\Hardware - - - System\Ps2\EmotionEngine\Hardware - - - System\Ps2\EmotionEngine\Hardware - - - System\Ps2\EmotionEngine\Memory - - - System\Ps2\EmotionEngine\Memory - - - System\Ps2\EmotionEngine\Memory - - - System\Ps2\EmotionEngine\VU - - - System\Ps2\EmotionEngine\VU - - - System\Ps2\EmotionEngine\VU - - - System\Ps2\EmotionEngine\VU\Dynarec\microVU - - - System\Ps2\EmotionEngine\VU\Dynarec\microVU - - - System\Ps2\EmotionEngine\VU\Dynarec\microVU - - - System\Ps2\EmotionEngine\VU\Interpreter - - - System\Ps2\EmotionEngine\VU\Interpreter - - - System\Ps2\EmotionEngine\DMAC\Sif - - - System\Ps2\EmotionEngine\DMAC\Sif - - - System\Ps2\EmotionEngine\DMAC\Vif - - - System\Ps2\EmotionEngine\DMAC\Vif - - - System\Ps2\EmotionEngine\DMAC\Vif\Unpack - - - System\Ps2\EmotionEngine\DMAC\Vif\Unpack\newVif - - - System\Ps2\EmotionEngine\DMAC\Vif\Unpack\newVif - - - System\Ps2\EmotionEngine\DMAC\Vif\Unpack\newVif\Dynarec - - - System\Ps2\EmotionEngine\DMAC\SPR - - - System\Ps2\EmotionEngine\DMAC\Gif - - - System\Ps2\EmotionEngine\EE - - - System\Ps2\EmotionEngine\EE - - - System\Ps2\EmotionEngine\EE - - - System\Ps2\EmotionEngine\EE - - - System\Ps2\EmotionEngine\EE\Dynarec - - - System\Ps2\EmotionEngine\EE\Dynarec - - - System\Ps2\EmotionEngine\EE\Dynarec - - - System\Ps2\EmotionEngine\EE\Dynarec - - - System\Ps2\EmotionEngine\EE\Dynarec - - - System\Ps2\EmotionEngine\EE\Dynarec - - - System\Ps2\EmotionEngine\EE\Dynarec - - - System\Ps2\EmotionEngine\EE\Dynarec - - - System\Ps2\EmotionEngine\EE\Dynarec - - - System\Ps2\EmotionEngine\EE\Dynarec - - - System\Ps2\EmotionEngine\EE\Dynarec - - - System\Ps2\EmotionEngine\EE\Dynarec - - - System\Ps2\Iop - - - System\Ps2\Iop - - - System\Ps2\Iop - - - System\Ps2\Iop - - - System\Ps2\Iop - - - System\Ps2\Iop - - - System\Ps2\Iop\Dynarec - - - System\Ps2\Iop\Hardware - - - System\Ps2\Iop\Hardware - - - System\Ps2\Iop\CDVD - - - System\Ps2\Iop\CDVD - - - System\Ps2\Iop\CDVD - - - System\Ps2\Iop\CDVD - - - System\Ps2\Iop\CDVD - - - System\Ps2\IPU - - - System\Ps2\IPU - - - System\Ps2\IPU - - - System\Ps2\IPU - - - System\Ps2\IPU\mpeg2lib - - - System\Ps2\IPU\mpeg2lib - - - System\Ps2\Debug - - - System\Ps2\Debug - - - System\Ps2\Debug - - - System\Ps2\Debug - - - System\Ps2\Include - - - System\Ps2\Include - - - System\Ps2\Include - - - System\Ps2\iCore - - - System\IsoFS - - - System\IsoFS - - - System\IsoFS - - - System\IsoFS - - - System\IsoFS - - - System\IsoFS - - - AppHost\Dialogs - - - AppHost\Dialogs - - - AppHost\Dialogs - - - AppHost\Dialogs - - - AppHost\Resources - - - AppHost\Panels - - - AppHost\Panels - - - AppHost\Include - - - AppHost\Include - - - AppHost\Include - - - AppHost\Include - - - AppHost\Include - - - AppHost\Include - - - AppHost\Include - - - AppHost\Include - - - AppHost\Include - - - AppHost\Include - - - AppHost\Include - - - AppHost\Include - - - AppHost\Include - - - AppHost\Include - - - AppHost\Include - - - AppHost\Include - - - AppHost\Include - - - AppHost\Include - - - Misc - - - System\Ps2\IPU - - - AppHost - - - AppHost\Panels - - - System\Ps2\GS\GIF - - - System\Ps2\EmotionEngine\VU\Dynarec\microVU - - - System\ISO - - - System\Ps2\Debug - - - System\Ps2\Debug - - - System\Ps2\Debug - - - AppHost\Debugger - - - AppHost\Debugger - - - System\Ps2\Debug - - - System\Ps2\Debug - - - AppHost\Debugger - - - AppHost\Debugger - - - AppHost\Debugger - - - AppHost\Debugger - - - System\Ps2\Debug - - - AppHost\Debugger - - - AppHost\Debugger - - - System\ISO - - - System\Ps2\Debug - - - System\Ps2\Debug - - - System\Ps2\Debug - - - System\Ps2\Debug - - - System\ISO - - - System\ISO - - - System\ISO - - - System\ISO - - - System\ISO - - - System\ISO - - - System\ISO - - - System\Include - - - System\Ps2\Iop - - - System\Ps2 - - - System\Ps2\Iop - - - Recording - - - Recording - - - Recording - - - Recording - - - System\Ps2\Iop\CDVD - - - AppHost\Include - - - System\Include - - - System\Ps2\Iop\FW - - - System\Ps2\SPU2 - - - System\Ps2\SPU2 - - - System\Ps2\SPU2 - - - System\Ps2\SPU2 - - - System\Ps2\SPU2 - - - System\Ps2\SPU2 - - - System\Ps2\SPU2 - - - System\Ps2\SPU2 - - - System\Ps2\SPU2 - - - System\Ps2\SPU2 - - - System\Ps2\SPU2 - - - System\Ps2\SPU2 - - - System\Ps2\SPU2 - - - System\Ps2\SPU2 - - - System\Ps2\SPU2 - - - System\Ps2\SPU2 - - - Recording\Utilities - - - System\Ps2\DEV9 - - - System\Ps2\DEV9\ATA - - - System\Ps2\DEV9\ATA - - - System\Ps2\DEV9\ATA - - - System\Ps2\DEV9 - - - System\Ps2\DEV9 - - - System\Ps2\DEV9\InternalServers - - - System\Ps2\DEV9\InternalServers - - - System\Ps2\DEV9\InternalServers - - - System\Ps2\DEV9 - - - System\Ps2\DEV9\PacketReader\ARP - - - System\Ps2\DEV9\PacketReader\IP\ICMP - - - System\Ps2\DEV9\PacketReader\IP\TCP - - - System\Ps2\DEV9\PacketReader\IP\TCP - - - System\Ps2\DEV9\PacketReader\IP\UDP\DHCP - - - System\Ps2\DEV9\PacketReader\IP\UDP\DHCP - - - System\Ps2\DEV9\PacketReader\IP\UDP\DNS - - - System\Ps2\DEV9\PacketReader\IP\UDP\DNS - - - System\Ps2\DEV9\PacketReader\IP\UDP\DNS - - - System\Ps2\DEV9\PacketReader\IP\UDP - - - System\Ps2\DEV9\PacketReader\IP - - - System\Ps2\DEV9\PacketReader\IP - - - System\Ps2\DEV9\PacketReader\IP - - - System\Ps2\DEV9\PacketReader\IP - - - System\Ps2\DEV9\PacketReader - - - System\Ps2\DEV9\PacketReader - - - System\Ps2\DEV9\PacketReader - - - System\Ps2\DEV9 - - - System\Ps2\DEV9\Sessions - - - System\Ps2\DEV9\Sessions\ICMP_Session - - - System\Ps2\DEV9\Sessions\TCP_Session - - - System\Ps2\DEV9\Sessions\UDP_Session - - - System\Ps2\DEV9\Sessions\UDP_Session - - - System\Ps2\DEV9\Sessions\UDP_Session - - - System\Ps2\DEV9 - - - System\Ps2\DEV9 - - - System\Ps2\DEV9 - - - System\Ps2\DEV9 - - - System\Ps2\DEV9 - - - System\Ps2\DEV9 - - - System\Ps2\PAD - - - System\Ps2\PAD - - - System\Ps2\PAD - - - System\Ps2\PAD - - - System\Ps2\PAD - - - System\Ps2\PAD - - - System\Ps2\PAD - - - System\Ps2\PAD - - - System\Ps2\PAD - - - System\Ps2\PAD - - - System\Ps2\PAD - - - System\Ps2\PAD - - - System\Ps2\PAD - - - System\Ps2\PAD - - - System\Ps2\PAD - - - System\Ps2\PAD - - - System\Ps2\PAD - - - System\Ps2\PAD - - - System\Ps2\PAD - - - System\Ps2\GS - - - System\Ps2\GS - - - System\Ps2\GS - - - System\Ps2\GS - - - System\Ps2\GS - - - System\Ps2\GS - - - System\Ps2\GS - - - System\Ps2\GS - - - System\Ps2\GS - - - System\Ps2\GS - - - System\Ps2\GS - - - System\Ps2\GS - - - System\Ps2\GS - - - System\Ps2\GS - - - System\Ps2\GS - - - System\Ps2\GS - - - System\Ps2\GS - - - System\Ps2\GS - - - System\Ps2\GS - - - System\Ps2\GS - - - System\Ps2\GS - - - System\Ps2\GS - - - System\Ps2\GS - - - System\Ps2\GS - - - System\Ps2\GS - - - System\Ps2\GS - - - System\Ps2\GS - - - System\Ps2\GS - - - System\Ps2\GS - - - System\Ps2\GS - - - System\Ps2\GS\GIF - - - System\Ps2\GS\Renderers\Direct3D11 - - - System\Ps2\GS\Renderers\Direct3D11 - - - System\Ps2\GS\Renderers\OpenGL - - - System\Ps2\GS\Renderers\OpenGL - - - System\Ps2\GS\Renderers\OpenGL - - - System\Ps2\GS\Renderers\OpenGL - - - System\Ps2\GS\Renderers\Software - - - System\Ps2\GS\Renderers\Software - - - System\Ps2\GS\Renderers\Software - - - System\Ps2\GS\Renderers\Software - - - System\Ps2\GS\Renderers\Software - - - System\Ps2\GS\Renderers\Software - - - System\Ps2\GS\Renderers\Software - - - System\Ps2\GS\Renderers\Software - - - System\Ps2\GS\Renderers\Software - - - System\Ps2\GS\Renderers\Software - - - System\Ps2\GS\Renderers\Software - - - System\Ps2\GS\Renderers\Software - - - System\Ps2\GS\Renderers\Software - - - System\Ps2\GS\Renderers\Software - - - System\Ps2\GS\Renderers\Null - - - System\Ps2\GS\Renderers\Null - - - System\Ps2\GS\Renderers\Null - - - System\Ps2\GS\Renderers\Hardware - - - System\Ps2\GS\Renderers\Hardware - - - System\Ps2\GS\Renderers\Hardware - - - System\Ps2\GS\Renderers\Common - - - System\Ps2\GS\Renderers\Common - - - System\Ps2\GS\Renderers\Common - - - System\Ps2\GS\Renderers\Common - - - System\Ps2\GS\Renderers\Common - - - System\Ps2\GS\Renderers\Common - - - System\Ps2\GS\Renderers\Common - - - System\Ps2\GS\Renderers\Common - - - System\Ps2\GS\Window - - - System\Ps2\GS\Window - - - System\Ps2\GS - - - System\Ps2\GS\Window - - - System\Ps2\GS\Renderers\Direct3D11 - - - AppHost - - - AppHost - - - AppHost - - - AppHost - - - AppHost - - - AppHost - - - System\Ps2\Iop - - - System\Ps2\Iop - - - AppHost - - - AppHost - - - AppHost - - - System\Ps2\PAD - - - System\Include - - - Host - - - Host - - - Host - - - Host - - - AppHost\Include - - - Host - - - Host - - - System\Ps2\GS\Renderers\Vulkan - - - System\Ps2\GS\Renderers\Vulkan - - - - System\Ps2\GS\Renderers\Hardware - - - System\Ps2\EmotionEngine\EE\Dynarec - - - AppHost - - - Host - - - System\Ps2\GS\Renderers\Direct3D12 - - - System\Ps2\GS\Renderers\Direct3D12 - - - AppHost - - - Host - - - Host - - - Host - - - Host - - - AppHost - - - Host - - - System\Include - - - System\Ps2\Iop - - - System\Ps2\Iop - - - System\Ps2\USB - - - - - AppHost\Resources - - - AppHost\Resources - - - AppHost\Resources - - - System\Ps2\SPU2 - - - System\Ps2\PAD - - - System\Ps2\GS - - - - - System\Ps2\Debug\rdebug - - - System\Ps2\Debug\rdebug - - - System\Ps2\Debug\rdebug - - - System\Ps2\Debug\rdebug - - - System\Ps2\Debug\rdebug - - - System\Ps2\Debug\rdebug - - - System\Ps2\Debug\rdebug - - - - - AppHost\Resources - - - AppHost\Resources - - - AppHost\Resources - - - AppHost\Resources - - - AppHost\Resources - - - AppHost\Resources - - - AppHost\Resources - - - AppHost\Resources - - - AppHost\Resources - - - AppHost\Resources - - - AppHost\Resources - - - AppHost\Resources - - - AppHost\Resources - - - AppHost\Resources - - - AppHost\Resources - - - - - AppHost\Resources - - - \ No newline at end of file diff --git a/pcsx2/windows/WinConsolePipe.cpp b/pcsx2/windows/WinConsolePipe.cpp deleted file mode 100644 index cf49f908a3..0000000000 --- a/pcsx2/windows/WinConsolePipe.cpp +++ /dev/null @@ -1,239 +0,0 @@ -/* PCSX2 - PS2 Emulator for PCs - * Copyright (C) 2002-2010 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 . - */ - -#include "PrecompiledHeader.h" -#include "Win32.h" - -#include "gui/App.h" -#include "gui/ConsoleLogger.h" - -// -------------------------------------------------------------------------------------- -// Win32 Console Pipes -// As a courtesy and convenience, we redirect stdout/stderr to the console and logfile. -// -------------------------------------------------------------------------------------- - -using namespace Threading; - -// -------------------------------------------------------------------------------------- -// WinPipeThread -// -------------------------------------------------------------------------------------- -class WinPipeThread : public pxThread -{ - typedef pxThread _parent; - -protected: - const HANDLE& m_outpipe; - const ConsoleColors m_color; - -public: - WinPipeThread( const HANDLE& outpipe, ConsoleColors color ) - : m_outpipe( outpipe ) - , m_color( color ) - { - m_name = (m_color == Color_Red) ? L"Redirect_Stderr" : L"Redirect_Stdout"; - } - - virtual ~WinPipeThread() - { - _parent::Cancel(); - } - -protected: - void ExecuteTaskInThread() - { - ::SetThreadPriority( ::GetCurrentThread(), THREAD_PRIORITY_BELOW_NORMAL ); - if( m_outpipe == INVALID_HANDLE_VALUE ) return; - - try - { - if (ConnectNamedPipe(m_outpipe, nullptr) == 0 && GetLastError() != ERROR_PIPE_CONNECTED) - throw Exception::RuntimeError().SetDiagMsg("ConnectNamedPipe failed."); - - char s8_Buf[2049]; - DWORD u32_Read = 0; - - while( true ) - { - if( !ReadFile(m_outpipe, s8_Buf, sizeof(s8_Buf)-1, &u32_Read, NULL) ) - { - DWORD result = GetLastError(); - if( result == ERROR_HANDLE_EOF || result == ERROR_BROKEN_PIPE ) break; - if( result == ERROR_IO_PENDING ) - { - Yield( 10 ); - continue; - } - - throw Exception::WinApiError().SetDiagMsg("ReadFile from pipe failed."); - } - - if( u32_Read <= 3 ) - { - // Windows has a habit of sending 1 or 2 characters of every message, and then sending - // the rest in a second message. This is "ok" really, except our Console class is hardly - // free of overhead, so it's helpful if we can concatenate the couple of messages together. - // But we don't want to break the ability to print progressive status bars, like '....' - // so I use a clever Yield/Peek loop combo that keeps reading as long as there's new data - // immediately being fed to our pipe. :) --air - - DWORD u32_avail = 0; - - do - { - Yield(); - if( !PeekNamedPipe(m_outpipe, 0, 0, 0, &u32_avail, 0) ) - throw Exception::WinApiError().SetDiagMsg("Error peeking Pipe."); - - if( u32_avail == 0 ) break; - - DWORD loopread; - if( !ReadFile(m_outpipe, &s8_Buf[u32_Read], sizeof(s8_Buf)-u32_Read-1, &loopread, NULL) ) break; - u32_Read += loopread; - - } while( u32_Read < sizeof(s8_Buf)-32 ); - } - - // ATTENTION: The Console always prints ANSI to the pipe independent if compiled as UNICODE or MBCS! - s8_Buf[u32_Read] = 0; - - ConsoleColorScope cs(m_color); - Console.DoWriteFromStdout( s8_Buf ); - - TestCancel(); - } - } - catch( Exception::RuntimeError& ex ) - { - // Log error, and fail silently. It's not really important if the - // pipe fails. PCSX2 will run fine without it in any case. - Console.Error( ex.FormatDiagnosticMessage() ); - } - } -}; - -// -------------------------------------------------------------------------------------- -// WinPipeRedirection -// -------------------------------------------------------------------------------------- -class WinPipeRedirection : public PipeRedirectionBase -{ - DeclareNoncopyableObject( WinPipeRedirection ); - -protected: - HANDLE m_readpipe; - FILE* m_fp; - - WinPipeThread m_Thread; - -public: - WinPipeRedirection( FILE* stdstream ); - virtual ~WinPipeRedirection(); - - void Cleanup() noexcept; -}; - -WinPipeRedirection::WinPipeRedirection( FILE* stdstream ) - : m_readpipe(INVALID_HANDLE_VALUE) - , m_fp(nullptr) - , m_Thread(m_readpipe, (stdstream == stderr) ? Color_Red : Color_Black) -{ - pxAssert( (stdstream == stderr) || (stdstream == stdout) ); - - try - { - // freopen requires a pathname, so a named pipe must be used. The - // pathname needs to be unique to allow multiple instances of PCSX2 to - // work properly. - const wxString stream_name(stdstream == stderr ? L"stderr" : L"stdout"); - const wxString pipe_name(wxString::Format(L"\\\\.\\pipe\\pcsx2_%s%d", stream_name, GetCurrentProcessId())); - - m_readpipe = CreateNamedPipe(pipe_name, PIPE_ACCESS_INBOUND, 0, 1, 2048, 2048, 0, nullptr); - if (m_readpipe == INVALID_HANDLE_VALUE) - throw Exception::WinApiError().SetDiagMsg("CreateNamedPipe failed."); - - m_Thread.Start(); - - // Binary flag set to prevent multiple \r characters before each \n. - m_fp = _wfreopen(pipe_name, L"wb", stdstream); - if (m_fp == nullptr) - throw Exception::RuntimeError().SetDiagMsg("_wfreopen returned NULL."); - - setvbuf(stdstream, nullptr, _IONBF, 0); - } - catch( Exception::BaseThreadError& ex ) - { - // thread object will become invalid because of scoping after we leave - // the constructor, so re-pack a new exception: - - Cleanup(); - throw Exception::RuntimeError().SetDiagMsg( ex.FormatDiagnosticMessage() ).SetUserMsg( ex.FormatDisplayMessage() ); - } - catch( BaseException& ex ) - { - Cleanup(); - ex.DiagMsg() = (std::string)((stdstream==stdout) ? "STDOUT" : "STDERR") + " Redirection Init failed: " + ex.DiagMsg(); - throw; - } - catch( ... ) - { - // C++ doesn't execute the object destructor automatically, because it's fail++ - // (and I'm *not* encapsulating each handle into its own object >_<) - - Cleanup(); - throw; - } -} - -WinPipeRedirection::~WinPipeRedirection() -{ - Cleanup(); -} - -void WinPipeRedirection::Cleanup() noexcept -{ - // Cleanup Order Notes: - // * The redirection thread is most likely blocking on ReadFile(), so we can't Cancel yet, lest we deadlock -- - // Closing the writepipe (either directly or through the fp/crt handles) issues an EOF to the thread, - // so it's safe to Cancel afterward. - - if( m_fp != NULL ) - { - fclose( m_fp ); - m_fp = NULL; - } - - m_Thread.Cancel(); - - if( m_readpipe != INVALID_HANDLE_VALUE ) - { - CloseHandle( m_readpipe ); - m_readpipe = INVALID_HANDLE_VALUE; - } -} - -// The win32 specific implementation of PipeRedirection. -PipeRedirectionBase* NewPipeRedir( FILE* stdstream ) -{ - try - { - return new WinPipeRedirection( stdstream ); - } - catch( Exception::RuntimeError& ex ) - { - // Entirely non-critical errors. Log 'em and move along. - Console.Error( ex.FormatDiagnosticMessage() ); - } - - return NULL; -} diff --git a/pcsx2/windows/WinKeyCodes.cpp b/pcsx2/windows/WinKeyCodes.cpp deleted file mode 100644 index 9e891c376d..0000000000 --- a/pcsx2/windows/WinKeyCodes.cpp +++ /dev/null @@ -1,164 +0,0 @@ -/* PCSX2 - PS2 Emulator for PCs -* Copyright (C) 2002-2010 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 . -*/ - -#include "PrecompiledHeader.h" -#include "common/Assertions.h" -#include "common/RedtapeWindows.h" - -// VK values without MS constants. see - https://msdn.microsoft.com/en-us/library/windows/desktop/dd375731%28v=vs.85%29.aspx -#define PX_VK_A 0x41 -#define PX_VK_Z 0x5A - -#define PX_VK2WX_SIZE 256 -static int vk2wx[PX_VK2WX_SIZE] = { 0 }; -static bool initialized = false; - -static void addTx(UINT vkCode, int wxCode) -{ - pxAssert(vkCode < PX_VK2WX_SIZE); - pxAssert(!vk2wx[vkCode]); // we shouldn't override entries here - vk2wx[vkCode] = wxCode; -} - -static void initTable() -{ - // Shift, Control, Alt and Menu - addTx(VK_SHIFT, WXK_SHIFT); - addTx(VK_LSHIFT, WXK_SHIFT); - addTx(VK_RSHIFT, WXK_SHIFT); - - addTx(VK_CONTROL, WXK_CONTROL); - addTx(VK_LCONTROL, WXK_CONTROL); - addTx(VK_RCONTROL, WXK_CONTROL); - - addTx(VK_MENU, WXK_ALT); - addTx(VK_LMENU, WXK_ALT); - addTx(VK_RMENU, WXK_ALT); - - addTx(VK_LWIN, WXK_WINDOWS_LEFT); - addTx(VK_RWIN, WXK_WINDOWS_RIGHT); - - addTx(VK_APPS, WXK_WINDOWS_MENU); - - // Scroll, Caps and numlocks - addTx(VK_SCROLL, WXK_SCROLL); - addTx(VK_CAPITAL, WXK_CAPITAL); - addTx(VK_NUMLOCK, WXK_NUMLOCK); - - // cursor and other extended keyboard keys - addTx(VK_PRIOR, WXK_PAGEUP); - addTx(VK_NEXT, WXK_PAGEDOWN); - addTx(VK_HOME, WXK_HOME); - addTx(VK_END, WXK_END); - addTx(VK_LEFT, WXK_LEFT); - addTx(VK_UP, WXK_UP); - addTx(VK_RIGHT, WXK_RIGHT); - addTx(VK_DOWN, WXK_DOWN); - addTx(VK_INSERT, WXK_INSERT); - addTx(VK_DELETE, WXK_DELETE); - - // numpad keys - addTx(VK_NUMPAD0, WXK_NUMPAD0); - addTx(VK_NUMPAD1, WXK_NUMPAD1); - addTx(VK_NUMPAD2, WXK_NUMPAD2); - addTx(VK_NUMPAD3, WXK_NUMPAD3); - addTx(VK_NUMPAD4, WXK_NUMPAD4); - addTx(VK_NUMPAD5, WXK_NUMPAD5); - addTx(VK_NUMPAD6, WXK_NUMPAD6); - addTx(VK_NUMPAD7, WXK_NUMPAD7); - addTx(VK_NUMPAD8, WXK_NUMPAD8); - addTx(VK_NUMPAD9, WXK_NUMPAD9); - addTx(VK_MULTIPLY, WXK_NUMPAD_MULTIPLY); - addTx(VK_ADD, WXK_NUMPAD_ADD); - addTx(VK_SEPARATOR, WXK_NUMPAD_SEPARATOR); - addTx(VK_SUBTRACT, WXK_NUMPAD_SUBTRACT); - addTx(VK_DECIMAL, WXK_NUMPAD_DECIMAL); - addTx(VK_DIVIDE, WXK_NUMPAD_DIVIDE); - - // Function keys - addTx(VK_F1, WXK_F1); - addTx(VK_F2, WXK_F2); - addTx(VK_F3, WXK_F3); - addTx(VK_F4, WXK_F4); - addTx(VK_F5, WXK_F5); - addTx(VK_F6, WXK_F6); - addTx(VK_F7, WXK_F7); - addTx(VK_F8, WXK_F8); - addTx(VK_F9, WXK_F9); - addTx(VK_F10, WXK_F10); - addTx(VK_F11, WXK_F11); - addTx(VK_F12, WXK_F12); - addTx(VK_F13, WXK_F13); - addTx(VK_F14, WXK_F14); - addTx(VK_F15, WXK_F15); - addTx(VK_F16, WXK_F16); - addTx(VK_F17, WXK_F17); - addTx(VK_F18, WXK_F18); - addTx(VK_F19, WXK_F19); - addTx(VK_F20, WXK_F20); - addTx(VK_F21, WXK_F21); - addTx(VK_F22, WXK_F22); - addTx(VK_F23, WXK_F23); - addTx(VK_F24, WXK_F24); - - // various other special keys - addTx(VK_BACK, WXK_BACK); - addTx(VK_TAB, WXK_TAB); - addTx(VK_RETURN, WXK_RETURN); - addTx(VK_ESCAPE, WXK_ESCAPE); - addTx(VK_SNAPSHOT, WXK_SNAPSHOT); - addTx(VK_PAUSE, WXK_PAUSE); - - // Not sure what these keys are - addTx(VK_CLEAR, WXK_CLEAR); - addTx(VK_HELP, WXK_HELP); - addTx(VK_SELECT, WXK_SELECT); - addTx(VK_EXECUTE, WXK_EXECUTE); - addTx(VK_PRINT, WXK_PRINT); - - // symbol-only keys on all keyboards - return ascii - addTx(VK_OEM_PERIOD, '.'); - addTx(VK_OEM_PLUS, '='); - addTx(VK_OEM_MINUS, '-'); - addTx(VK_OEM_COMMA, ','); - - // symbol-only keys on US keyboards - return ascii - addTx(VK_OEM_1, ';'); - addTx(VK_OEM_2, '/'); - addTx(VK_OEM_3, '`'); - addTx(VK_OEM_4, '['); - addTx(VK_OEM_5, '\\'); - addTx(VK_OEM_6, ']'); - addTx(VK_OEM_7, '\''); - - pxAssert(PX_VK_A < PX_VK_Z); - // VK codes for letter keys - return lower case ascii - for (UINT i = PX_VK_A; i <= PX_VK_Z; i++) - addTx(i, i + 'a' - PX_VK_A); - - initialized = true; -} - - -// Returns a WXK_* keycode translated from a VK_* virtual key. wxCharCodeMSWToWx was -// removed from wxWidgets 3, this should work as a replacement. -// Where an ascii code for a printable char exists, we try to return it -int TranslateVKToWXK(u32 keysym) -{ - if (!initialized) - initTable(); - - return keysym < PX_VK2WX_SIZE ? vk2wx[keysym] : 0; -} diff --git a/pcsx2/windows/WinPowerProfile.cpp b/pcsx2/windows/WinPowerProfile.cpp deleted file mode 100644 index a83f310fb4..0000000000 --- a/pcsx2/windows/WinPowerProfile.cpp +++ /dev/null @@ -1,50 +0,0 @@ -/* PCSX2 - PS2 Emulator for PCs -* Copyright (C) 2002-2021 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 . -*/ - -#include "PrecompiledHeader.h" -#include "common/StringUtil.h" -#include "common/RedtapeWindows.h" - -#include -#include - -using unique_any_guidptr = wil::unique_any; - -// Checks the current active power plan -// This function fails silently -void CheckIsUserOnHighPerfPowerPlan() -{ - unique_any_guidptr pPwrGUID; - DWORD ret = PowerGetActiveScheme(NULL, pPwrGUID.put()); - if (ret != ERROR_SUCCESS) - return; - - UCHAR aBuffer[2048]; - DWORD aBufferSize = sizeof(aBuffer); - ret = PowerReadFriendlyName(NULL, pPwrGUID.get(), &NO_SUBGROUP_GUID, NULL, aBuffer, &aBufferSize); - std::string friendlyName(StringUtil::WideStringToUTF8String((wchar_t*)aBuffer)); - if (ret != ERROR_SUCCESS) - return; - - DWORD acMax = 0,acMin = 0,dcMax = 0,dcMin = 0; - - if (PowerReadACValueIndex(NULL, pPwrGUID.get(), &GUID_PROCESSOR_SETTINGS_SUBGROUP, &GUID_PROCESSOR_THROTTLE_MAXIMUM, &acMax) || - PowerReadACValueIndex(NULL, pPwrGUID.get(), &GUID_PROCESSOR_SETTINGS_SUBGROUP, &GUID_PROCESSOR_THROTTLE_MINIMUM, &acMin) || - PowerReadDCValueIndex(NULL, pPwrGUID.get(), &GUID_PROCESSOR_SETTINGS_SUBGROUP, &GUID_PROCESSOR_THROTTLE_MAXIMUM, &dcMax) || - PowerReadDCValueIndex(NULL, pPwrGUID.get(), &GUID_PROCESSOR_SETTINGS_SUBGROUP, &GUID_PROCESSOR_THROTTLE_MINIMUM, &dcMin)) - return; - - Console.WriteLn("The current power profile is '%s'.\nThe current min / max processor states\nAC: %d%% / %d%%\nBattery: %d%% / %d%%\n", friendlyName.c_str(), acMin, acMax, dcMin, dcMax); -}

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 H;~V{d(Hq=_ diff --git a/bin/resources/locale/zh_TW/pcsx2_Iconized.mo b/bin/resources/locale/zh_TW/pcsx2_Iconized.mo deleted file mode 100644 index 9971409a440c191639adfe43d18d617ac575c1b5..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 30039 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 diff --git a/bin/resources/locale/zh_TW/pcsx2_Main.mo b/bin/resources/locale/zh_TW/pcsx2_Main.mo deleted file mode 100644 index 8490902a4f0bf8c1c75080b86e893953edb7c1a1..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 58990 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 XDnAvxmZ5MQalv0JUSMDmCgA%IqjR`s diff --git a/pcsx2/CMakeLists.txt b/pcsx2/CMakeLists.txt index 8f3340988d..db548e96a1 100644 --- a/pcsx2/CMakeLists.txt +++ b/pcsx2/CMakeLists.txt @@ -1373,11 +1373,11 @@ function(setup_main_executable target) set_target_properties(${target} PROPERTIES MACOSX_BUNDLE true - MACOSX_BUNDLE_INFO_PLIST "${PCSX2_SOURCE_DIR}/gui/Resources/Info.plist.in" + MACOSX_BUNDLE_INFO_PLIST "${PCSX2_SOURCE_DIR}/Resources/Info.plist.in" OUTPUT_NAME PCSX2 ) - pcsx2_resource(${target} ${PCSX2_SOURCE_DIR}/gui/Resources/PCSX2.icns ${PCSX2_SOURCE_DIR}/gui/Resources) + pcsx2_resource(${target} ${PCSX2_SOURCE_DIR}/Resources/PCSX2.icns ${PCSX2_SOURCE_DIR}/Resources) # If they say to skip postprocess bundle, leave the target in but make it so they have # to manually run it diff --git a/pcsx2/DEV9/ATA/HddCreateWx.cpp b/pcsx2/DEV9/ATA/HddCreateWx.cpp deleted file mode 100644 index 885bbe1bc3..0000000000 --- a/pcsx2/DEV9/ATA/HddCreateWx.cpp +++ /dev/null @@ -1,103 +0,0 @@ -/* PCSX2 - PS2 Emulator for PCs - * Copyright (C) 2002-2020 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 . - */ - -#include "PrecompiledHeader.h" - -#include "HddCreateWx.h" -#include "gui/App.h" - -void HddCreateWx::Init() -{ - //This can be called from the EE Core thread - //ensure that UI creation/deletaion is done on main thread - //Also block calling thread untill ui is ready - if (!wxIsMainThread()) - { - dialogReady = false; - wxTheApp->CallAfter([&] { Init(); }); - //Block until done - std::unique_lock loadLock(dialogMutex); - dialogCV.wait(loadLock, [&] { return dialogReady; }); - return; - } - - reqMiB = (neededSize + ((1024 * 1024) - 1)) / (1024 * 1024); - //This creates a modeless dialog - progressDialog = new wxProgressDialog(_("Creating HDD file"), _("Creating HDD file"), reqMiB, nullptr, wxPD_APP_MODAL | wxPD_AUTO_HIDE | wxPD_CAN_ABORT | wxPD_ELAPSED_TIME | wxPD_REMAINING_TIME); - { - std::lock_guard dialogLock1(dialogMutex); - dialogReady = true; - } - dialogCV.notify_all(); -} - -void HddCreateWx::SetFileProgress(u64 currentSize) -{ - if (!wxIsMainThread()) - { - wxTheApp->CallAfter([&, currentSize] { SetFileProgress(currentSize); }); - return; - } - - wxString msg; - - const int writtenMB = (currentSize + ((1024 * 1024) - 1)) / (1024 * 1024); - msg.Printf(_("%i / %i MiB"), writtenMB, reqMiB); - - if (!progressDialog->Update(writtenMB, msg)) - SetCanceled(); -} - -void HddCreateWx::SetError() -{ - if (!wxIsMainThread()) - { - dialogReady = false; - wxTheApp->CallAfter([&] { SetError(); }); - //Block until done - std::unique_lock loadLock(dialogMutex); - dialogCV.wait(loadLock, [&] { return dialogReady; }); - return; - } - - wxMessageDialog dialog(nullptr, _("Failed to create HDD file"), _("Info"), wxOK); - dialog.ShowModal(); - { - std::lock_guard dialogLock1(dialogMutex); - dialogReady = true; - } - dialogCV.notify_all(); -} - -void HddCreateWx::Cleanup() -{ - if (!wxIsMainThread()) - { - dialogReady = false; - wxTheApp->CallAfter([&] { Cleanup(); }); - //Block until done - std::unique_lock loadLock(dialogMutex); - dialogCV.wait(loadLock, [&] { return dialogReady; }); - return; - } - - delete progressDialog; - - { - std::lock_guard dialogLock1(dialogMutex); - dialogReady = true; - } - dialogCV.notify_all(); -} diff --git a/pcsx2/DEV9/ATA/HddCreateWx.h b/pcsx2/DEV9/ATA/HddCreateWx.h deleted file mode 100644 index 876f6c7dbf..0000000000 --- a/pcsx2/DEV9/ATA/HddCreateWx.h +++ /dev/null @@ -1,45 +0,0 @@ -/* PCSX2 - PS2 Emulator for PCs - * Copyright (C) 2002-2020 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 . - */ - -#pragma once - -#include - -#include -#include - -#include "DEV9/ATA/HddCreate.h" - -class HddCreateWx : public HddCreate -{ -public: - HddCreateWx(){}; - virtual ~HddCreateWx(){}; - -private: - wxProgressDialog* progressDialog; - - std::mutex dialogMutex; - std::condition_variable dialogCV; - bool dialogReady = false; - - int reqMiB; - -protected: - virtual void Init(); - virtual void Cleanup(); - virtual void SetFileProgress(u64 currentSize); - virtual void SetError(); -}; diff --git a/pcsx2/DEV9/ConfigUI.cpp b/pcsx2/DEV9/ConfigUI.cpp deleted file mode 100644 index 2df7d91496..0000000000 --- a/pcsx2/DEV9/ConfigUI.cpp +++ /dev/null @@ -1,439 +0,0 @@ -/* PCSX2 - PS2 Emulator for PCs - * Copyright (C) 2002-2021 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 . - */ - -#include "PrecompiledHeader.h" - -#include - -#include - -#include -#include -#include -#include -#include -#include - -#include "common/FileSystem.h" -#include "common/Path.h" -#include "common/StringUtil.h" - -#include "Config.h" -#include "DEV9.h" -#include "pcap_io.h" -#include "sockets.h" -#include "net.h" -#include "PacketReader/IP/IP_Address.h" -#include "gui/AppCoreThread.h" -#ifdef _WIN32 - #include "Win32/tap.h" -#endif - -#include "ATA/HddCreateWx.h" - -using PacketReader::IP::IP_Address; - -class DEV9Dialog : public wxDialog -{ - static void IPControl_SetValue(wxTextCtrl* ctl, IP_Address value) - { - ctl->SetValue(wxString::Format("%u.%u.%u.%u", value.bytes[0], value.bytes[1], value.bytes[2], value.bytes[3])); - } - - static IP_Address IPControl_GetValue(wxTextCtrl* ctl) - { - IP_Address ret; - if (4 == wxSscanf(ctl->GetValue(), "%hhu.%hhu.%hhu.%hhu", &ret.bytes[0], &ret.bytes[1], &ret.bytes[2], &ret.bytes[3])) - return ret; - Console.Error("Invalid IP address entered"); - return {}; - } - - struct IPInputWithAutoCheck - { - wxTextCtrl* m_ip; - wxCheckBox* m_auto; - void create(int col, wxWindow* window, wxGridBagSizer* sizer, const wxString& label) - { - auto* label_box = new wxStaticText(window, wxID_ANY, label); - m_ip = new wxTextCtrl(window, wxID_ANY); - m_auto = new wxCheckBox(window, wxID_ANY, _("Auto")); - sizer->Add(label_box, wxGBPosition(col, 0), wxDefaultSpan, wxALIGN_RIGHT | wxALIGN_CENTRE_VERTICAL); - sizer->Add(m_ip, wxGBPosition(col, 1), wxDefaultSpan, wxEXPAND); - sizer->Add(m_auto, wxGBPosition(col, 2), wxDefaultSpan, wxEXPAND); - } - void setEnabled(bool enabled) - { - m_auto->Enable(enabled); - m_ip->Enable(enabled && !m_auto->GetValue()); - } - void load(const IP_Address& ip, bool is_auto) - { - IPControl_SetValue(m_ip, ip); - m_auto->SetValue(is_auto); - } - void save(IP_Address& ip, bool& is_auto) - { - ip = IPControl_GetValue(m_ip); - is_auto = m_auto->GetValue(); - } - }; - wxCheckBox* m_eth_enable; - wxChoice* m_eth_adapter_api; - wxChoice* m_eth_adapter; - std::vector m_api_list; - std::vector> m_adapter_list; - wxCheckBox* m_intercept_dhcp; - wxTextCtrl* m_ps2_address; - IPInputWithAutoCheck m_subnet_mask; - IPInputWithAutoCheck m_gateway_address; - IPInputWithAutoCheck m_dns1_address; - IPInputWithAutoCheck m_dns2_address; - - wxCheckBox* m_hdd_enable; - wxFilePickerCtrl* m_hdd_file; - wxSpinCtrl* m_hdd_size_spin; - wxSlider* m_hdd_size_slider; - - void addAdapter(const AdapterEntry& adapter) - { - if (std::find(m_api_list.begin(), m_api_list.end(), adapter.type) == m_api_list.end()) - m_api_list.push_back(adapter.type); - u32 idx = static_cast(adapter.type); - if (m_adapter_list.size() <= idx) - m_adapter_list.resize(idx + 1); - m_adapter_list[idx].push_back(adapter); - } - -public: - DEV9Dialog() - : wxDialog(nullptr, wxID_ANY, _("Network and HDD Settings"), wxDefaultPosition, wxDefaultSize, wxCAPTION | wxCLOSE_BOX | wxRESIZE_BORDER) - { - auto* padding = new wxBoxSizer(wxVERTICAL); - auto* top_box = new wxBoxSizer(wxVERTICAL); - - int space = wxSizerFlags().Border().GetBorderInPixels(); - int two_space = wxSizerFlags().DoubleBorder().GetBorderInPixels(); - - // Ethernet section - auto* eth_sizer = new wxStaticBoxSizer(wxVERTICAL, this, _("Ethernet")); - - m_eth_enable = new wxCheckBox(this, wxID_ANY, _("Enabled")); - eth_sizer->Add(m_eth_enable); - eth_sizer->AddSpacer(space); - - auto* eth_adapter_box = new wxGridBagSizer(space, space); - for (const AdapterEntry& adapter : PCAPAdapter::GetAdapters()) - addAdapter(adapter); -#ifdef _WIN32 - for (const AdapterEntry& adapter : TAPAdapter::GetAdapters()) - addAdapter(adapter); -#endif - for (const AdapterEntry& adapter : SocketAdapter::GetAdapters()) - addAdapter(adapter); - - std::sort(m_api_list.begin(), m_api_list.end()); - for (auto& list : m_adapter_list) - std::sort(list.begin(), list.end(), [](const AdapterEntry& a, AdapterEntry& b){ return a.name < b.name; }); - wxArrayString adapter_api_name_list; - adapter_api_name_list.Add(""); - for (const Pcsx2Config::DEV9Options::NetApi& type : m_api_list) - adapter_api_name_list.Add(Pcsx2Config::DEV9Options::NetApiNames[(int)type]); - - auto* eth_adapter_api_label = new wxStaticText(this, wxID_ANY, _("Ethernet Device Type:")); - auto* eth_adapter_label = new wxStaticText(this, wxID_ANY, _("Ethernet Device:")); - auto* intercept_dhcp_label = new wxStaticText(this, wxID_ANY, _("Intercept DHCP:")); - auto* ps2_addr_label = new wxStaticText(this, wxID_ANY, _("PS2 Address:")); - m_eth_adapter_api = new wxChoice(this, wxID_ANY, wxDefaultPosition, wxDefaultSize, adapter_api_name_list); - m_eth_adapter = new wxChoice(this, wxID_ANY); - m_intercept_dhcp = new wxCheckBox(this, wxID_ANY, _("Enabled")); - m_ps2_address = new wxTextCtrl(this, wxID_ANY); - m_ps2_address->SetMinSize(wxSize(150, -1)); - - eth_adapter_box->Add(eth_adapter_api_label, wxGBPosition(0, 0), wxDefaultSpan, wxALIGN_RIGHT | wxALIGN_CENTRE_VERTICAL); - eth_adapter_box->Add(eth_adapter_label, wxGBPosition(1, 0), wxDefaultSpan, wxALIGN_RIGHT | wxALIGN_CENTRE_VERTICAL); - eth_adapter_box->Add(intercept_dhcp_label, wxGBPosition(2, 0), wxDefaultSpan, wxALIGN_RIGHT | wxALIGN_CENTRE_VERTICAL); - eth_adapter_box->Add(ps2_addr_label, wxGBPosition(3, 0), wxDefaultSpan, wxALIGN_RIGHT | wxALIGN_CENTRE_VERTICAL); - eth_adapter_box->Add(m_eth_adapter_api, wxGBPosition(0, 1), wxGBSpan(1, 2), wxEXPAND); - eth_adapter_box->Add(m_eth_adapter, wxGBPosition(1, 1), wxGBSpan(1, 2), wxEXPAND); - eth_adapter_box->Add(m_intercept_dhcp, wxGBPosition(2, 1), wxDefaultSpan, wxEXPAND); - eth_adapter_box->Add(m_ps2_address, wxGBPosition(3, 1), wxDefaultSpan, wxEXPAND); - - m_subnet_mask .create(4, this, eth_adapter_box, _("Subnet Mask:")); - m_gateway_address.create(5, this, eth_adapter_box, _("Gateway Address:")); - m_dns1_address .create(6, this, eth_adapter_box, _("DNS1 Address:")); - m_dns2_address .create(7, this, eth_adapter_box, _("DNS2 Address:")); - - eth_adapter_box->AddGrowableCol(2); - eth_sizer->Add(eth_adapter_box, wxSizerFlags().Expand()); - - // HDD section - auto* hdd_sizer = new wxStaticBoxSizer(wxVERTICAL, this, _("Hard Disk Drive")); - - m_hdd_enable = new wxCheckBox(this, wxID_ANY, _("Enabled")); - - auto* hdd_file_label = new wxStaticText(this, wxID_ANY, _("HDD File")); - m_hdd_file = new wxFilePickerCtrl(this, wxID_ANY, wxEmptyString, _("HDD image file"), "HDD|*.raw", wxDefaultPosition, wxDefaultSize, wxFLP_SAVE | wxFLP_USE_TEXTCTRL); - auto* hdd_size_label = new wxStaticText(this, wxID_ANY, _("HDD Size (GiB)")); - auto* hdd_size_box = new wxBoxSizer(wxHORIZONTAL); - m_hdd_size_slider = new wxSlider(this, wxID_ANY, HDD_MIN_GB, HDD_MIN_GB, HDD_MAX_GB, wxDefaultPosition, wxDefaultSize, wxSL_HORIZONTAL | wxSL_AUTOTICKS); - m_hdd_size_slider->SetPageSize(10); - m_hdd_size_slider->SetTickFreq(10); - m_hdd_size_spin = new wxSpinCtrl(this, wxID_ANY, wxEmptyString, wxDefaultPosition, wxDefaultSize, wxSP_ARROW_KEYS, HDD_MIN_GB, HDD_MAX_GB, HDD_MIN_GB); - m_hdd_size_spin->SetSizeHints(m_hdd_size_spin->GetSizeFromTextSize(30)); - hdd_size_box->Add(m_hdd_size_slider, wxSizerFlags(1).Centre()); - hdd_size_box->AddSpacer(space); - hdd_size_box->Add(m_hdd_size_spin, wxSizerFlags(0).Centre()); - - hdd_sizer->Add(m_hdd_enable); - hdd_sizer->AddSpacer(two_space); - hdd_sizer->Add(hdd_file_label, wxSizerFlags().Left()); - hdd_sizer->AddSpacer(space); - hdd_sizer->Add(m_hdd_file, wxSizerFlags().Expand()); - hdd_sizer->AddSpacer(two_space); - hdd_sizer->Add(hdd_size_label, wxSizerFlags().Left()); - hdd_sizer->AddSpacer(space); - hdd_sizer->Add(hdd_size_box, wxSizerFlags().Expand()); - - top_box->Add(eth_sizer, wxSizerFlags().Expand()); - top_box->AddSpacer(space); - top_box->Add(hdd_sizer, wxSizerFlags().Expand()); - top_box->AddSpacer(space); - top_box->Add(CreateStdDialogButtonSizer(wxOK | wxCANCEL), wxSizerFlags().Right()); - - padding->Add(top_box, wxSizerFlags().Expand().Border()); - SetSizerAndFit(padding); - SetMaxSize(wxSize(wxDefaultCoord, GetMinSize().y)); - - Bind(wxEVT_CHECKBOX, &DEV9Dialog::OnCheck, this); - Bind(wxEVT_SLIDER, &DEV9Dialog::OnSlide, this); - Bind(wxEVT_SPINCTRL, &DEV9Dialog::OnSpin, this); - Bind(wxEVT_CHOICE, &DEV9Dialog::OnChoice, this); - Bind(wxEVT_BUTTON, &DEV9Dialog::OnOK, this, wxID_OK); - } - - void Load(const Pcsx2Config::DEV9Options& config) - { - m_eth_enable->SetValue(config.EthEnable); - m_eth_adapter_api->SetSelection(0); - for (size_t i = 0; i < m_api_list.size(); i++) - { - if (config.EthApi == m_api_list[i]) - m_eth_adapter_api->SetSelection(i + 1); - } - UpdateAdapters(); - m_eth_adapter->SetSelection(0); - if (static_cast(config.EthApi) < m_adapter_list.size()) - { - const auto& list = m_adapter_list[static_cast(config.EthApi)]; - for (size_t i = 0; i < list.size(); i++) - { - if (list[i].guid == config.EthDevice) - { - m_eth_adapter->SetSelection(i + 1); - break; - } - } - } - m_intercept_dhcp->SetValue(config.InterceptDHCP); - IPControl_SetValue(m_ps2_address, *(IP_Address*)config.PS2IP); - m_subnet_mask .load(*(IP_Address*)config.Mask, config.AutoMask); - m_gateway_address.load(*(IP_Address*)config.Gateway, config.AutoGateway); - m_dns1_address .load(*(IP_Address*)config.DNS1, config.ModeDNS1 == Pcsx2Config::DEV9Options::DnsMode::Auto); - m_dns2_address .load(*(IP_Address*)config.DNS2, config.ModeDNS2 == Pcsx2Config::DEV9Options::DnsMode::Auto); - - m_hdd_enable->SetValue(config.HddEnable); - wxString wxHddFile = StringUtil::UTF8StringToWxString(config.HddFile); - m_hdd_file->SetInitialDirectory(wxHddFile); - m_hdd_file->SetPath(wxHddFile); - m_hdd_size_spin->SetValue((u64)config.HddSizeSectors * 512 / (1024 * 1024 * 1024)); - m_hdd_size_slider->SetValue((u64)config.HddSizeSectors * 512 / (1024 * 1024 * 1024)); - - UpdateEnable(); - } - - void Save(Pcsx2Config::DEV9Options& config) - { - config.EthEnable = m_eth_enable->GetValue(); - int api = m_eth_adapter_api->GetSelection(); - int eth = m_eth_adapter->GetSelection(); - if (api && eth) - { - const AdapterEntry& adapter = m_adapter_list[static_cast(m_api_list[api - 1])][eth - 1]; - config.EthDevice = adapter.guid; - config.EthApi = adapter.type; - } - else - { - config.EthDevice = ""; - config.EthApi = Pcsx2Config::DEV9Options::NetApi::Unset; - } - config.InterceptDHCP = m_intercept_dhcp->GetValue(); - *(IP_Address*)&config.PS2IP = IPControl_GetValue(m_ps2_address); - m_subnet_mask .save(*(IP_Address*)config.Mask, config.AutoMask); - m_gateway_address.save(*(IP_Address*)config.Gateway, config.AutoGateway); - - bool autoDNS1; - bool autoDNS2; - m_dns1_address.save(*(IP_Address*)config.DNS1, autoDNS1); - m_dns2_address.save(*(IP_Address*)config.DNS2, autoDNS2); - config.ModeDNS1 = autoDNS1 ? Pcsx2Config::DEV9Options::DnsMode::Auto : Pcsx2Config::DEV9Options::DnsMode::Manual; - config.ModeDNS2 = autoDNS2 ? Pcsx2Config::DEV9Options::DnsMode::Auto : Pcsx2Config::DEV9Options::DnsMode::Manual; - - config.HddEnable = m_hdd_enable->GetValue(); - config.HddFile = StringUtil::wxStringToUTF8String(m_hdd_file->GetPath()); - config.HddSizeSectors = (u64)m_hdd_size_spin->GetValue() * 1024 * 1024 * 1024 / 512; - } - - void UpdateEnable() - { - AdapterOptions adapterOptions = AdapterOptions::None; - const int api = m_eth_adapter_api->GetSelection(); - if (api) - { - const Pcsx2Config::DEV9Options::NetApi netApi = m_api_list[api - 1]; - switch (netApi) - { -#ifdef _WIN32 - case Pcsx2Config::DEV9Options::NetApi::TAP: - adapterOptions = TAPAdapter::GetAdapterOptions(); - break; -#endif - case Pcsx2Config::DEV9Options::NetApi::PCAP_Bridged: - case Pcsx2Config::DEV9Options::NetApi::PCAP_Switched: - adapterOptions = PCAPAdapter::GetAdapterOptions(); - break; - case Pcsx2Config::DEV9Options::NetApi::Sockets: - adapterOptions = SocketAdapter::GetAdapterOptions(); - break; - default: - break; - } - } - - bool eth_enable = m_eth_enable->GetValue(); - bool hdd_enable = m_hdd_enable->GetValue(); - bool dhcp_enable = eth_enable && (m_intercept_dhcp->GetValue() || ((adapterOptions & AdapterOptions::DHCP_ForcedOn) == AdapterOptions::DHCP_ForcedOn)); - - m_eth_adapter_api->Enable(eth_enable); - m_eth_adapter->Enable(eth_enable); - m_intercept_dhcp->Enable(eth_enable && ((adapterOptions & AdapterOptions::DHCP_ForcedOn) == AdapterOptions::None)); - m_ps2_address->Enable(dhcp_enable && ((adapterOptions & AdapterOptions::DHCP_OverrideIP) == AdapterOptions::None)); - m_subnet_mask.setEnabled(dhcp_enable && ((adapterOptions & AdapterOptions::DHCP_OverideSubnet) == AdapterOptions::None)); - m_gateway_address.setEnabled(dhcp_enable && ((adapterOptions & AdapterOptions::DHCP_OverideGateway) == AdapterOptions::None)); - m_dns1_address.setEnabled(dhcp_enable); - m_dns2_address.setEnabled(dhcp_enable); - m_hdd_file->Enable(hdd_enable); - m_hdd_size_spin->Enable(hdd_enable); - m_hdd_size_slider->Enable(hdd_enable); - } - - void UpdateAdapters() - { - int api = m_eth_adapter_api->GetSelection(); - int selection = 0; - wxArrayString options; - options.Add(""); - if (api) - { - const auto& list = m_adapter_list[static_cast(m_api_list[api - 1])]; - wxString current; - if (m_eth_adapter->GetCount()) - current = m_eth_adapter->GetString(m_eth_adapter->GetSelection()); - if (current.empty()) - current = StringUtil::UTF8StringToWxString(g_Conf->EmuOptions.DEV9.EthDevice); - for (size_t i = 0; i < list.size(); i++) - { - wxString wxAdapterName = StringUtil::UTF8StringToWxString(list[i].name); - options.Add(wxAdapterName); - if (wxAdapterName == current) - selection = i + 1; - } - } - m_eth_adapter->Set(options); - m_eth_adapter->SetSelection(selection); - // Update minimum sizes for the possibly increased dropdown size - // Nothing else seems to update the minimum size of the window - SetSizerAndFit(GetSizer(), false); - } - - void OnCheck(wxCommandEvent&) - { - UpdateEnable(); - } - - void OnSlide(wxCommandEvent&) - { - m_hdd_size_spin->SetValue(m_hdd_size_slider->GetValue()); - } - - void OnSpin(wxCommandEvent&) - { - m_hdd_size_slider->SetValue(m_hdd_size_spin->GetValue()); - } - - void OnChoice(wxCommandEvent& ev) - { - if (ev.GetEventObject() == m_eth_adapter_api) - { - UpdateAdapters(); - UpdateEnable(); - } - } - - void OnOK(wxCommandEvent& ev) - { - const wxChar* msg = nullptr; - - if (m_eth_enable->GetValue() && !m_eth_adapter->GetSelection()) - msg = _("Please select an ethernet adapter or disable ethernet"); - if (m_hdd_enable->GetValue() && m_hdd_file->GetPath().empty()) - msg = _("Please specify a HDD file or disable the hard drive"); - - if (msg) - wxMessageDialog(this, msg).ShowModal(); - else - ev.Skip(); - } -}; - -void DEV9configure() -{ - ScopedCoreThreadPause paused_core; - - DEV9Dialog dialog; - dialog.Load(g_Conf->EmuOptions.DEV9); - if (dialog.ShowModal() == wxID_OK) - { - dialog.Save(g_Conf->EmuOptions.DEV9); - - std::string hddPath(g_Conf->EmuOptions.DEV9.HddFile); - - if (!Path::IsAbsolute(hddPath)) - hddPath = Path::Combine(EmuFolders::Settings, hddPath); - - if (g_Conf->EmuOptions.DEV9.HddEnable && !FileSystem::FileExists(hddPath.c_str())) - { - HddCreateWx hddCreator; - hddCreator.filePath = hddPath; - hddCreator.neededSize = ((u64)g_Conf->EmuOptions.DEV9.HddSizeSectors) * 512; - hddCreator.Start(); - } - - AppSaveSettings(); - } - - paused_core.AllowResume(); -} diff --git a/pcsx2/DEV9/DEV9Config.cpp b/pcsx2/DEV9/DEV9Config.cpp deleted file mode 100644 index 5cf511177c..0000000000 --- a/pcsx2/DEV9/DEV9Config.cpp +++ /dev/null @@ -1,129 +0,0 @@ -/* PCSX2 - PS2 Emulator for PCs - * Copyright (C) 2002-2021 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 . - */ - -#include "PrecompiledHeader.h" - -#include "common/StringUtil.h" -#include - -#include "DEV9.h" - -#ifndef PCSX2_CORE -#include "gui/AppConfig.h" -#include "gui/IniInterface.h" -#endif - -#ifdef _WIN32 -#include "ws2tcpip.h" -#elif defined(__POSIX__) -#include -#endif - -#if defined(__FreeBSD__) -#include -#endif - -void SaveDnsHosts() -{ -#ifndef PCSX2_CORE - std::unique_ptr hini(OpenFileConfig(StringUtil::UTF8StringToWxString(Path::Combine(EmuFolders::Settings, "DEV9Hosts.ini")))); -#else - std::unique_ptr hini(new wxFileConfig(wxEmptyString, wxEmptyString, EmuFolders::Settings.Combine(wxString("DEV9Hosts.ini")).GetFullPath(), wxEmptyString, wxCONFIG_USE_RELATIVE_PATH)); -#endif - IniSaver ini((wxConfigBase*)hini.get()); - - for (size_t i = 0; i < config.EthHosts.size(); i++) - { - std::wstring groupName(L"Host" + std::to_wstring(i)); - ScopedIniGroup iniEntry(ini, groupName); - ConfigHost entry = config.EthHosts[i]; - - wxString url(fromUTF8(entry.Url)); - ini.Entry(L"Url", url); - //TODO UTF8(?) - wxString desc(fromUTF8(entry.Desc)); - ini.Entry(L"Desc", desc); - - char addrBuff[INET_ADDRSTRLEN]; - inet_ntop(AF_INET, entry.Address, addrBuff, INET_ADDRSTRLEN); - wxString address(addrBuff); - ini.Entry(L"Address", address); - - ini.Entry(L"Enabled", entry.Enabled); - } - ini.Flush(); -} - -void LoadDnsHosts() -{ - wxFileName iniPath = StringUtil::UTF8StringToWxString(Path::Combine(EmuFolders::Settings, "DEV9Hosts.ini")); - config.EthHosts.clear(); - //If no file exists, create one to provide an example config - if (!iniPath.FileExists()) - { - //Load Default settings - ConfigHost exampleHost; - exampleHost.Url = "www.example.com"; - exampleHost.Desc = "Set DNS to 192.0.2.1 to use this host list"; - memset(exampleHost.Address, 0, 4); - exampleHost.Enabled = false; - config.EthHosts.push_back(exampleHost); - SaveDnsHosts(); - return; - } - -#ifndef PCSX2_CORE - std::unique_ptr hini(OpenFileConfig(iniPath.GetFullPath())); -#else - std::unique_ptr hini(new wxFileConfig(wxEmptyString, wxEmptyString, iniPath.GetFullPath(), wxEmptyString, wxCONFIG_USE_RELATIVE_PATH)); -#endif - IniLoader ini((wxConfigBase*)hini.get()); - - int i = 0; - while (true) - { - std::wstring groupName(L"Host" + std::to_wstring(i)); - ScopedIniGroup iniEntry(ini, groupName); - wxString tmp = wxEmptyString; - ini.Entry(L"Url", tmp, wxEmptyString); - //An empty url means we tried to read beyond end of the host list - if (tmp.IsEmpty()) - break; - - ConfigHost entry; - entry.Url = tmp.ToUTF8(); - - ini.Entry(L"Desc", tmp, wxEmptyString); - entry.Desc = tmp.ToUTF8(); - - ini.Entry(L"Address", tmp, L"0.0.0.0"); - - int ret = inet_pton(AF_INET, tmp.ToUTF8(), entry.Address); - //Only check Enabled if valid ip - if (ret != 1) - { - memset(entry.Address, 0, 4); - entry.Enabled = false; - } - else - ini.Entry(L"Enabled", entry.Enabled, false); - - if (EmuConfig.DEV9.EthLogDNS && entry.Enabled) - Console.WriteLn("DEV9: Host entry %i: url %s mapped to %s", i, entry.Url.c_str(), tmp.ToStdString().c_str()); - - config.EthHosts.push_back(entry); - i++; - } -} diff --git a/pcsx2/DEV9/DEV9Config.h b/pcsx2/DEV9/DEV9Config.h deleted file mode 100644 index 041bceea70..0000000000 --- a/pcsx2/DEV9/DEV9Config.h +++ /dev/null @@ -1,17 +0,0 @@ -/* PCSX2 - PS2 Emulator for PCs - * Copyright (C) 2002-2020 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 . - */ - -void SaveDnsHosts(); -void LoadDnsHosts(); diff --git a/pcsx2/Darwin/DarwinKeyCodes.cpp b/pcsx2/Darwin/DarwinKeyCodes.cpp deleted file mode 100644 index 1e585fddc9..0000000000 --- a/pcsx2/Darwin/DarwinKeyCodes.cpp +++ /dev/null @@ -1,142 +0,0 @@ -/* 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 . - */ - -#include "PrecompiledHeader.h" -#include - -int TranslateOSXtoWXK(u32 keysym) -{ - switch (keysym) - { - case kVK_ANSI_A: return 'A'; - case kVK_ANSI_S: return 'S'; - case kVK_ANSI_D: return 'D'; - case kVK_ANSI_F: return 'F'; - case kVK_ANSI_H: return 'H'; - case kVK_ANSI_G: return 'G'; - case kVK_ANSI_Z: return 'Z'; - case kVK_ANSI_X: return 'X'; - case kVK_ANSI_C: return 'C'; - case kVK_ANSI_V: return 'V'; - case kVK_ANSI_B: return 'B'; - case kVK_ANSI_Q: return 'Q'; - case kVK_ANSI_W: return 'W'; - case kVK_ANSI_E: return 'E'; - case kVK_ANSI_R: return 'R'; - case kVK_ANSI_Y: return 'Y'; - case kVK_ANSI_T: return 'T'; - case kVK_ANSI_1: return '1'; - case kVK_ANSI_2: return '2'; - case kVK_ANSI_3: return '3'; - case kVK_ANSI_4: return '4'; - case kVK_ANSI_6: return '6'; - case kVK_ANSI_5: return '5'; - case kVK_ANSI_Equal: return '='; - case kVK_ANSI_9: return '9'; - case kVK_ANSI_7: return '7'; - case kVK_ANSI_Minus: return '-'; - case kVK_ANSI_8: return '8'; - case kVK_ANSI_0: return '0'; - case kVK_ANSI_RightBracket: return ']'; - case kVK_ANSI_O: return 'O'; - case kVK_ANSI_U: return 'U'; - case kVK_ANSI_LeftBracket: return '['; - case kVK_ANSI_I: return 'I'; - case kVK_ANSI_P: return 'P'; - case kVK_ANSI_L: return 'L'; - case kVK_ANSI_J: return 'J'; - case kVK_ANSI_Quote: return '\''; - case kVK_ANSI_K: return 'K'; - case kVK_ANSI_Semicolon: return ';'; - case kVK_ANSI_Backslash: return '\\'; - case kVK_ANSI_Comma: return ','; - case kVK_ANSI_Slash: return '/'; - case kVK_ANSI_N: return 'N'; - case kVK_ANSI_M: return 'M'; - case kVK_ANSI_Period: return '.'; - case kVK_ANSI_Grave: return '`'; - case kVK_ANSI_KeypadDecimal: return WXK_NUMPAD_DECIMAL; - case kVK_ANSI_KeypadMultiply: return WXK_NUMPAD_MULTIPLY; - case kVK_ANSI_KeypadPlus: return WXK_NUMPAD_ADD; - case kVK_ANSI_KeypadClear: return WXK_CLEAR; - case kVK_ANSI_KeypadDivide: return WXK_NUMPAD_DIVIDE; - case kVK_ANSI_KeypadEnter: return WXK_NUMPAD_ENTER; - case kVK_ANSI_KeypadMinus: return WXK_NUMPAD_SUBTRACT; - case kVK_ANSI_KeypadEquals: return WXK_NUMPAD_EQUAL; - case kVK_ANSI_Keypad0: return WXK_NUMPAD0; - case kVK_ANSI_Keypad1: return WXK_NUMPAD1; - case kVK_ANSI_Keypad2: return WXK_NUMPAD2; - case kVK_ANSI_Keypad3: return WXK_NUMPAD3; - case kVK_ANSI_Keypad4: return WXK_NUMPAD4; - case kVK_ANSI_Keypad5: return WXK_NUMPAD5; - case kVK_ANSI_Keypad6: return WXK_NUMPAD6; - case kVK_ANSI_Keypad7: return WXK_NUMPAD7; - case kVK_ANSI_Keypad8: return WXK_NUMPAD8; - case kVK_ANSI_Keypad9: return WXK_NUMPAD9; - - case kVK_Return: return WXK_RETURN; - case kVK_Tab: return WXK_TAB; - case kVK_Space: return WXK_SPACE; - case kVK_Delete: return WXK_BACK; - case kVK_Escape: return WXK_ESCAPE; - case kVK_Command: return WXK_COMMAND; - case kVK_Shift: return WXK_SHIFT; - case kVK_CapsLock: return WXK_CAPITAL; - case kVK_Option: return WXK_ALT; - case kVK_Control: return WXK_RAW_CONTROL; - case kVK_RightCommand: return WXK_COMMAND; - case kVK_RightShift: return WXK_SHIFT; - case kVK_RightOption: return WXK_ALT; - case kVK_RightControl: return WXK_RAW_CONTROL; - case kVK_Function: return 0; - case kVK_F17: return WXK_F17; -#if wxCHECK_VERSION(3, 1, 0) - case kVK_VolumeUp: return WXK_VOLUME_UP; - case kVK_VolumeDown: return WXK_VOLUME_DOWN; - case kVK_Mute: return WXK_VOLUME_MUTE; -#endif - case kVK_F18: return WXK_F18; - case kVK_F19: return WXK_F19; - case kVK_F20: return WXK_F20; - case kVK_F5: return WXK_F5; - case kVK_F6: return WXK_F6; - case kVK_F7: return WXK_F7; - case kVK_F3: return WXK_F3; - case kVK_F8: return WXK_F8; - case kVK_F9: return WXK_F9; - case kVK_F11: return WXK_F11; - case kVK_F13: return WXK_F13; - case kVK_F16: return WXK_F16; - case kVK_F14: return WXK_F14; - case kVK_F10: return WXK_F10; - case kVK_F12: return WXK_F12; - case kVK_F15: return WXK_F15; - case kVK_Help: return WXK_HELP; - case kVK_Home: return WXK_HOME; - case kVK_PageUp: return WXK_PAGEUP; - case kVK_ForwardDelete: return WXK_DELETE; - case kVK_F4: return WXK_F4; - case kVK_End: return WXK_END; - case kVK_F2: return WXK_F2; - case kVK_PageDown: return WXK_PAGEDOWN; - case kVK_F1: return WXK_F1; - case kVK_LeftArrow: return WXK_LEFT; - case kVK_RightArrow: return WXK_RIGHT; - case kVK_DownArrow: return WXK_DOWN; - case kVK_UpArrow: return WXK_UP; - default: - return 0; - } -} diff --git a/pcsx2/GS/Window/GSwxDialog.cpp b/pcsx2/GS/Window/GSwxDialog.cpp deleted file mode 100644 index f5ebba2669..0000000000 --- a/pcsx2/GS/Window/GSwxDialog.cpp +++ /dev/null @@ -1,844 +0,0 @@ -/* PCSX2 - PS2 Emulator for PCs - * Copyright (C) 2002-2021 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 . - */ - -#include "PrecompiledHeader.h" -#include "GSwxDialog.h" -#include "gui/AppConfig.h" -#include "gui/StringHelpers.h" -#include "GS/GSUtil.h" -#include "HostDisplay.h" - -#ifdef _WIN32 -#include "Frontend/D3D11HostDisplay.h" -#include "Frontend/D3D12HostDisplay.h" -#endif -#include "GS/Renderers/Metal/GSMetalCPPAccessible.h" - -#ifdef ENABLE_VULKAN -#include "Frontend/VulkanHostDisplay.h" -#endif - -using namespace GSSettingsDialog; - -namespace -{ - void add_tooltip(wxWindow* widget, int tooltip) - { - if (tooltip != -1) - widget->SetToolTip(dialog_message(tooltip)); - } - - void add_settings_to_array_string(const std::vector& s, wxArrayString& arr) - { - for (const GSSetting& setting : s) - { - if (!setting.note.empty()) - arr.Add(fromUTF8(setting.name + " (" + setting.note + ")")); - else - arr.Add(fromUTF8(setting.name)); - } - } - - size_t get_config_index(const std::vector& s, int value) - { - for (size_t i = 0; i < s.size(); i++) - { - if (s[i].value == value) - return i; - } - return 0; - } - - void set_config_from_choice(const wxChoice* choice, const std::vector& s, const char* str) - { - int idx = choice->GetSelection(); - - if (idx == wxNOT_FOUND) - return; - - theApp.SetConfig(str, s[idx].value); - } - - void add_label(wxWindow* parent, wxSizer* sizer, const char* str, int tooltip = -1, wxSizerFlags flags = wxSizerFlags().Centre().Right(), long style = wxALIGN_RIGHT | wxALIGN_CENTRE_VERTICAL) - { - auto* temp_text = new wxStaticText(parent, wxID_ANY, str, wxDefaultPosition, wxDefaultSize, style); - add_tooltip(temp_text, tooltip); - sizer->Add(temp_text, flags); - } - - /// wxBoxSizer with padding - template - struct PaddedBoxSizer - { - OuterSizer* outer; - wxBoxSizer* inner; - - // Make static analyzers happy (memory management is handled by WX) - // (There's no actual reason we couldn't use the default copy constructor, except cppcheck screams when you do and it's easier to delete than implement one) - PaddedBoxSizer(const PaddedBoxSizer&) = delete; - - template - PaddedBoxSizer(wxOrientation orientation, Args&&... args) - : outer(new OuterSizer(orientation, std::forward(args)...)) - , inner(new wxBoxSizer(orientation)) - { - wxSizerFlags flags = wxSizerFlags().Expand(); -#ifdef __APPLE__ - if (!std::is_same::value) // wxMac already adds padding to static box sizers -#endif - flags.Border(); - outer->Add(inner, flags); - } - - wxBoxSizer* operator->() { return inner; } - }; - - struct CheckboxPrereq - { - wxCheckBox* box; - explicit CheckboxPrereq(wxCheckBox* box) - : box(box) - { - } - - bool operator()() - { - return box->GetValue(); - } - }; -} // namespace - -GSUIElementHolder::GSUIElementHolder(wxWindow* window) - : m_window(window) -{ -} - -wxStaticText* GSUIElementHolder::addWithLabel(wxControl* control, UIElem::Type type, wxSizer* sizer, const char* label, const char* config_name, int tooltip, std::function prereq, wxSizerFlags flags) -{ - add_tooltip(control, tooltip); - wxStaticText* text = new wxStaticText(m_window, wxID_ANY, label, wxDefaultPosition, wxDefaultSize, wxALIGN_RIGHT); - add_tooltip(text, tooltip); - sizer->Add(text, wxSizerFlags().Centre().Right()); - sizer->Add(control, flags); - m_elems.emplace_back(type, control, config_name, prereq); - return text; -} - -wxCheckBox* GSUIElementHolder::addCheckBox(wxSizer* sizer, const char* label, const char* config_name, int tooltip, std::function prereq) -{ - wxCheckBox* box = new wxCheckBox(m_window, wxID_ANY, label); - add_tooltip(box, tooltip); - if (sizer) - sizer->Add(box); - m_elems.emplace_back(UIElem::Type::CheckBox, box, config_name, prereq); - return box; -} - -std::pair GSUIElementHolder::addComboBoxAndLabel(wxSizer* sizer, const char* label, const char* config_name, const std::vector* settings, int tooltip, std::function prereq) -{ - wxArrayString temp; - add_settings_to_array_string(*settings, temp); - wxChoice* choice = new GSwxChoice(m_window, wxID_ANY, wxDefaultPosition, wxDefaultSize, temp, settings); - return std::make_pair(choice, addWithLabel(choice, UIElem::Type::Choice, sizer, label, config_name, tooltip, prereq)); -} - -wxSpinCtrl* GSUIElementHolder::addSpin(wxSizer* sizer, const char* config_name, int min, int max, int initial, int tooltip, std::function prereq) -{ - wxSpinCtrl* spin = new wxSpinCtrl(m_window, wxID_ANY, wxEmptyString, wxDefaultPosition, wxDefaultSize, wxSP_ARROW_KEYS, min, max, initial); - add_tooltip(spin, tooltip); - if (sizer) - sizer->Add(spin, wxSizerFlags(1)); - m_elems.emplace_back(UIElem::Type::Spin, spin, config_name, prereq); - return spin; -} - -std::pair GSUIElementHolder::addSpinAndLabel(wxSizer* sizer, const char* label, const char* config_name, int min, int max, int initial, int tooltip, std::function prereq) -{ - wxSpinCtrl* spin = new wxSpinCtrl(m_window, wxID_ANY, wxEmptyString, wxDefaultPosition, wxDefaultSize, wxSP_ARROW_KEYS, min, max, initial); - return std::make_pair(spin, addWithLabel(spin, UIElem::Type::Spin, sizer, label, config_name, tooltip, prereq, wxSizerFlags().Centre().Left().Expand())); -} - -std::pair GSUIElementHolder::addSliderAndLabel(wxSizer* sizer, const char* label, const char* config_name, int min, int max, int initial, int tooltip, std::function prereq) -{ - wxSlider* slider = new wxSlider(m_window, wxID_ANY, initial, min, max, wxDefaultPosition, wxDefaultSize, wxSL_HORIZONTAL | wxSL_VALUE_LABEL); - return std::make_pair(slider, addWithLabel(slider, UIElem::Type::Slider, sizer, label, config_name, tooltip, prereq)); -} - -std::pair GSUIElementHolder::addFilePickerAndLabel(wxSizer* sizer, const char* label, const char* config_name, int tooltip, std::function prereq) -{ - wxFilePickerCtrl* picker = new wxFilePickerCtrl(m_window, wxID_ANY); - return std::make_pair(picker, addWithLabel(picker, UIElem::Type::File, sizer, label, config_name, tooltip, prereq)); -} - -std::pair GSUIElementHolder::addDirPickerAndLabel(wxSizer* sizer, const char* label, const char* config_name, int tooltip, std::function prereq) -{ - wxDirPickerCtrl* picker = new wxDirPickerCtrl(m_window, wxID_ANY); - return std::make_pair(picker, addWithLabel(picker, UIElem::Type::Directory, sizer, label, config_name, tooltip, prereq)); -} - -void GSUIElementHolder::Load() -{ - for (const UIElem& elem : m_elems) - { - switch (elem.type) - { - case UIElem::Type::CheckBox: - static_cast(elem.control)->SetValue(theApp.GetConfigB(elem.config)); - break; - case UIElem::Type::Choice: - { - GSwxChoice* choice = static_cast(elem.control); - choice->SetSelection(get_config_index(choice->settings, theApp.GetConfigI(elem.config))); - break; - } - case UIElem::Type::Spin: - static_cast(elem.control)->SetValue(theApp.GetConfigI(elem.config)); - break; - case UIElem::Type::Slider: - static_cast(elem.control)->SetValue(theApp.GetConfigI(elem.config)); - break; - case UIElem::Type::File: - case UIElem::Type::Directory: - { - auto* picker = static_cast(elem.control); - picker->SetInitialDirectory(fromUTF8(theApp.GetConfigS(elem.config))); - picker->SetPath(fromUTF8(theApp.GetConfigS(elem.config))); - break; - } - } - } -} - -void GSUIElementHolder::Save() -{ - for (const UIElem& elem : m_elems) - { - switch (elem.type) - { - case UIElem::Type::CheckBox: - theApp.SetConfig(elem.config, static_cast(elem.control)->GetValue()); - break; - case UIElem::Type::Choice: - { - GSwxChoice* choice = static_cast(elem.control); - set_config_from_choice(choice, choice->settings, elem.config); - break; - } - case UIElem::Type::Spin: - theApp.SetConfig(elem.config, static_cast(elem.control)->GetValue()); - break; - case UIElem::Type::Slider: - theApp.SetConfig(elem.config, static_cast(elem.control)->GetValue()); - break; - case UIElem::Type::File: - case UIElem::Type::Directory: - theApp.SetConfig(elem.config, static_cast(elem.control)->GetPath().ToUTF8()); - break; - } - } -} - -void GSUIElementHolder::Update() -{ - for (const UIElem& elem : m_elems) - { - if (elem.prereq) - elem.control->Enable(elem.prereq()); - } -} - -void GSUIElementHolder::DisableAll() -{ - for (const UIElem& elem : m_elems) - { - if (elem.prereq) - elem.control->Enable(false); - } -} - -RendererTab::RendererTab(wxWindow* parent) - : wxPanel(parent, wxID_ANY) - , m_ui(this) -{ - const int space = wxSizerFlags().Border().GetBorderInPixels(); - auto hw_prereq = [this]{ return m_is_hardware; }; - auto sw_prereq = [this]{ return !m_is_hardware; }; - - PaddedBoxSizer tab_box(wxVERTICAL); - PaddedBoxSizer general_box(wxVERTICAL, this, "General GS Settings"); - PaddedBoxSizer hardware_box(wxVERTICAL, this, "Hardware Mode"); - PaddedBoxSizer software_box(wxVERTICAL, this, "Software Mode"); - - auto* hw_checks_box = new wxWrapSizer(wxHORIZONTAL); - - auto* paltex_prereq = m_ui.addCheckBox(hw_checks_box, "GPU Palette Conversion", "paltex", IDC_PALTEX, hw_prereq); - m_ui.addCheckBox(hw_checks_box, "Spin GPU During Readbacks", "HWSpinGPUForReadbacks", IDC_SPIN_GPU); - m_ui.addCheckBox(hw_checks_box, "Spin CPU During Readbacks", "HWSpinCPUForReadbacks", IDC_SPIN_CPU); - auto aniso_prereq = [this, paltex_prereq]{ return m_is_hardware && paltex_prereq->GetValue() == false; }; - - auto* hw_choice_grid = new wxFlexGridSizer(2, space, space); - - m_internal_resolution = m_ui.addComboBoxAndLabel(hw_choice_grid, "Internal Resolution:", "upscale_multiplier", &theApp.m_gs_upscale_multiplier, -1, hw_prereq).first; - - m_ui.addComboBoxAndLabel(hw_choice_grid, "Anisotropic Filtering:", "MaxAnisotropy", &theApp.m_gs_max_anisotropy, IDC_AFCOMBO, aniso_prereq); - m_ui.addComboBoxAndLabel(hw_choice_grid, "Dithering (PgDn):", "dithering_ps2", &theApp.m_gs_dithering, IDC_DITHERING, hw_prereq); - m_ui.addComboBoxAndLabel(hw_choice_grid, "Mipmapping:", "mipmap_hw", &theApp.m_gs_hw_mipmapping, IDC_MIPMAP_HW, hw_prereq); - m_ui.addComboBoxAndLabel(hw_choice_grid, "Trilinear Filtering:", "TriFilter", &theApp.m_gs_trifilter, IDC_TRI_FILTER, hw_prereq); - m_ui.addComboBoxAndLabel(hw_choice_grid, "CRC Hack Level:", "crc_hack_level", &theApp.m_gs_crc_level, IDC_CRC_LEVEL, hw_prereq); - m_ui.addComboBoxAndLabel(hw_choice_grid, "Blending Accuracy:", "accurate_blending_unit", &theApp.m_gs_acc_blend_level, IDC_ACCURATE_BLEND_UNIT, hw_prereq); - m_ui.addComboBoxAndLabel(hw_choice_grid, "Texture Preloading:", "texture_preloading", &theApp.m_gs_texture_preloading, IDC_PRELOAD_TEXTURES, hw_prereq); - - hardware_box->Add(hw_checks_box, wxSizerFlags().Centre()); - hardware_box->AddSpacer(space); - hardware_box->Add(hw_choice_grid, wxSizerFlags().Centre()); - - auto* sw_checks_box = new wxWrapSizer(wxHORIZONTAL); - m_ui.addCheckBox(sw_checks_box, "Auto Flush", "autoflush_sw", IDC_AUTO_FLUSH_SW, sw_prereq); - m_ui.addCheckBox(sw_checks_box, "Mipmapping", "mipmap", IDC_MIPMAP_SW, sw_prereq); - - software_box->Add(sw_checks_box, wxSizerFlags().Centre()); - software_box->AddSpacer(space); - - // Rendering threads - auto* thread_box = new wxFlexGridSizer(2, space, space); - m_ui.addSpinAndLabel(thread_box, "Extra Rendering threads:", "extrathreads", 0, 32, 2, IDC_SWTHREADS, sw_prereq); - software_box->Add(thread_box, wxSizerFlags().Centre()); - - // General GS Settings box - auto* pcrtc_checks_box = new wxWrapSizer(wxHORIZONTAL); - - m_ui.addCheckBox(pcrtc_checks_box, "Screen Offsets", "pcrtc_offsets", IDC_PCRTC_OFFSETS); - m_ui.addCheckBox(pcrtc_checks_box, "Show Overscan", "pcrtc_overscan", IDC_PCRTC_OVERSCAN); - m_ui.addCheckBox(pcrtc_checks_box, "Disable Interlace Offset", "disable_interlace_offset", IDC_DISABLE_INTERLACE_OFFSETS); - m_ui.addCheckBox(pcrtc_checks_box, "Anti-Blur", "pcrtc_antiblur", IDC_PCRTC_ANTIBLUR); - general_box->Add(pcrtc_checks_box, wxSizerFlags().Center()); - - tab_box->Add(hardware_box.outer, wxSizerFlags().Expand()); - tab_box->Add(software_box.outer, wxSizerFlags().Expand()); - tab_box->Add(general_box.outer, wxSizerFlags().Expand()); - - SetSizerAndFit(tab_box.outer); -} - -HacksTab::HacksTab(wxWindow* parent) - : wxPanel(parent, wxID_ANY) - , m_ui(this) -{ - const int space = wxSizerFlags().Border().GetBorderInPixels(); - PaddedBoxSizer tab_box(wxVERTICAL); - - auto hw_prereq = [this]{ return m_is_hardware; }; - auto* hacks_check_box = m_ui.addCheckBox(tab_box.inner, "Manual HW Hacks (Disables automatic settings if checked)", "UserHacks", -1, hw_prereq); - m_ui.addCheckBox(tab_box.inner, "Skip Presenting Duplicate Frames", "SkipDuplicateFrames", -1); - - auto hacks_prereq = [this, hacks_check_box]{ return m_is_hardware && hacks_check_box->GetValue(); }; - auto upscale_hacks_prereq = [this, hacks_check_box]{ return !m_is_native_res && hacks_check_box->GetValue(); }; - - PaddedBoxSizer rend_hacks_box (wxVERTICAL, this, "Renderer Hacks"); - PaddedBoxSizer upscale_hacks_box(wxVERTICAL, this, "Upscale Hacks"); - - auto* rend_hacks_grid = new wxFlexGridSizer(2, space, space); - auto* upscale_hacks_grid = new wxFlexGridSizer(3, space, space); - - // Renderer Hacks - m_ui.addCheckBox(rend_hacks_grid, "Auto Flush", "UserHacks_AutoFlush", IDC_AUTO_FLUSH_HW, hacks_prereq); - m_ui.addCheckBox(rend_hacks_grid, "Frame Buffer Conversion", "UserHacks_CPU_FB_Conversion", IDC_CPU_FB_CONVERSION, hacks_prereq); - m_ui.addCheckBox(rend_hacks_grid, "Disable Depth Emulation", "UserHacks_DisableDepthSupport", IDC_TC_DEPTH, hacks_prereq); - m_ui.addCheckBox(rend_hacks_grid, "Disable Safe Features", "UserHacks_Disable_Safe_Features", IDC_SAFE_FEATURES, hacks_prereq); - m_ui.addCheckBox(rend_hacks_grid, "Preload Frame Data", "preload_frame_with_gs_data", IDC_PRELOAD_GS, hacks_prereq); - m_ui.addCheckBox(rend_hacks_grid, "Disable Partial Invalidation", "UserHacks_DisablePartialInvalidation", IDC_DISABLE_PARTIAL_TC_INV, hacks_prereq); - m_ui.addCheckBox(rend_hacks_grid, "Texture Inside RT", "UserHacks_TextureInsideRt", IDC_TEX_IN_RT, hacks_prereq); - - // Upscale - m_ui.addCheckBox(upscale_hacks_grid, "Align Sprite", "UserHacks_align_sprite_X", IDC_ALIGN_SPRITE, upscale_hacks_prereq); - m_ui.addCheckBox(upscale_hacks_grid, "Merge Sprite", "UserHacks_merge_pp_sprite", IDC_MERGE_PP_SPRITE, upscale_hacks_prereq); - m_ui.addCheckBox(upscale_hacks_grid, "Wild Arms Hack", "UserHacks_WildHack", IDC_WILDHACK, upscale_hacks_prereq); - - auto* rend_hack_choice_grid = new wxFlexGridSizer(2, space, space); - auto* upscale_hack_choice_grid = new wxFlexGridSizer(2, space, space); - rend_hack_choice_grid ->AddGrowableCol(1); - upscale_hack_choice_grid->AddGrowableCol(1); - - // Renderer Hacks: - m_ui.addComboBoxAndLabel(rend_hack_choice_grid, "Half Screen Fix:", "UserHacks_Half_Bottom_Override", &theApp.m_gs_generic_list, IDC_HALF_SCREEN_TS, hacks_prereq); - - // Skipdraw Range - add_label(this, rend_hack_choice_grid, "Skipdraw Range:", IDC_SKIPDRAWEND); - auto* skip_box = new wxBoxSizer(wxHORIZONTAL); - skip_x_spin = m_ui.addSpin(skip_box, "UserHacks_SkipDraw_Start", 0, 10000, 0, IDC_SKIPDRAWSTART, hacks_prereq); - skip_y_spin = m_ui.addSpin(skip_box, "UserHacks_SkipDraw_End", 0, 10000, 0, IDC_SKIPDRAWEND, hacks_prereq); - - rend_hack_choice_grid->Add(skip_box, wxSizerFlags().Expand()); - - // Upscale Hacks: - m_ui.addComboBoxAndLabel(upscale_hack_choice_grid, "Half-Pixel Offset:", "UserHacks_HalfPixelOffset", &theApp.m_gs_offset_hack, IDC_OFFSETHACK, upscale_hacks_prereq); - m_ui.addComboBoxAndLabel(upscale_hack_choice_grid, "Round Sprite:", "UserHacks_round_sprite_offset", &theApp.m_gs_hack, IDC_ROUND_SPRITE, upscale_hacks_prereq); - - // Texture Offsets - add_label(this, upscale_hack_choice_grid, "Texture Offsets:", IDC_TCOFFSETX); - auto* tex_off_box = new wxBoxSizer(wxHORIZONTAL); - add_label(this, tex_off_box, "X:", IDC_TCOFFSETX, wxSizerFlags().Centre()); - tex_off_box->AddSpacer(space); - m_ui.addSpin(tex_off_box, "UserHacks_TCOffsetX", 0, 10000, 0, IDC_TCOFFSETX, upscale_hacks_prereq); - tex_off_box->AddSpacer(space); - add_label(this, tex_off_box, "Y:", IDC_TCOFFSETY, wxSizerFlags().Centre()); - tex_off_box->AddSpacer(space); - m_ui.addSpin(tex_off_box, "UserHacks_TCOffsetY", 0, 10000, 0, IDC_TCOFFSETY, upscale_hacks_prereq); - - upscale_hack_choice_grid->Add(tex_off_box, wxSizerFlags().Expand()); - - rend_hacks_box->Add(rend_hacks_grid, wxSizerFlags().Centre()); - rend_hacks_box->AddSpacer(space); - rend_hacks_box->Add(rend_hack_choice_grid, wxSizerFlags().Expand()); - - upscale_hacks_box->Add(upscale_hacks_grid, wxSizerFlags().Centre()); - upscale_hacks_box->AddSpacer(space); - upscale_hacks_box->Add(upscale_hack_choice_grid, wxSizerFlags().Expand()); - - tab_box->Add(rend_hacks_box.outer, wxSizerFlags().Expand()); - tab_box->Add(upscale_hacks_box.outer, wxSizerFlags().Expand()); - - SetSizerAndFit(tab_box.outer); -} - -void HacksTab::DoUpdate() -{ - m_ui.Update(); - - if (skip_x_spin->GetValue() == 0) - skip_y_spin->SetValue(0); - if (skip_y_spin->GetValue() < skip_x_spin->GetValue()) - skip_y_spin->SetValue(skip_x_spin->GetValue()); -} - -RecTab::RecTab(wxWindow* parent) - : wxPanel(parent, wxID_ANY) - , m_ui(this) -{ - const int space = wxSizerFlags().Border().GetBorderInPixels(); - PaddedBoxSizer tab_box(wxVERTICAL); - - auto* record_check = m_ui.addCheckBox(tab_box.inner, "Enable Recording (F12)", "capture_enabled"); - CheckboxPrereq record_prereq(record_check); - PaddedBoxSizer record_box(wxVERTICAL, this, "Recording"); - auto* record_grid_box = new wxFlexGridSizer(2, space, space); - record_grid_box->AddGrowableCol(1); - - // Resolution - add_label(this, record_grid_box, "Resolution:"); - auto* res_box = new wxBoxSizer(wxHORIZONTAL); - m_ui.addSpin(res_box, "CaptureWidth", 256, 8192, 640, -1, record_prereq); - m_ui.addSpin(res_box, "CaptureHeight", 256, 8192, 480, -1, record_prereq); - - record_grid_box->Add(res_box, wxSizerFlags().Expand()); - - m_ui.addSpinAndLabel(record_grid_box, "Saving Threads:", "capture_threads", 1, 32, 4, -1, record_prereq); - m_ui.addSpinAndLabel(record_grid_box, "PNG Compression Level:", "png_compression_level", 1, 9, 1, -1, record_prereq); - - m_ui.addDirPickerAndLabel(record_grid_box, "Output Directory:", "capture_out_dir", -1, record_prereq); - - record_box->Add(record_grid_box, wxSizerFlags().Expand()); - - tab_box->Add(record_box.outer, wxSizerFlags().Expand()); - SetSizerAndFit(tab_box.outer); -} - -PostTab::PostTab(wxWindow* parent) - : wxPanel(parent, wxID_ANY) - , m_ui(this) -{ - const int space = wxSizerFlags().Border().GetBorderInPixels(); - PaddedBoxSizer tab_box(wxVERTICAL); - PaddedBoxSizer shader_box(wxVERTICAL, this, "Shader Options"); - - // Bilinear filtering - auto* bil_filter_grid_box = new wxFlexGridSizer(2, space, space); - bil_filter_grid_box->AddGrowableCol(1); - - m_ui.addComboBoxAndLabel(bil_filter_grid_box, "Bilinear Filtering:", "linear_present_mode", &theApp.m_gs_tex_display_list, IDC_LINEAR_PRESENT); - - shader_box->Add(bil_filter_grid_box, wxSizerFlags().Expand()); - - // Sharpening, fxaa - PaddedBoxSizer sharpening_box(wxVERTICAL, this, "Sharpening/Anti-aliasing"); - auto* casmode_grid = new wxFlexGridSizer(2, space, space); - casmode_grid->AddGrowableCol(1); - - m_ui.addComboBoxAndLabel(casmode_grid, "Contrast Adaptive Sharpening:", "CASMode", &theApp.m_gs_casmode); - - sharpening_box->Add(casmode_grid, wxSizerFlags().Expand()); - - auto* sharpness_grid = new wxFlexGridSizer(2, space, space); - sharpness_grid->AddGrowableCol(1); - - m_ui.addSliderAndLabel(sharpness_grid, "Sharpness:", "CASSharpness", 0, 100, 50, -1); - - sharpening_box->Add(sharpness_grid, wxSizerFlags().Expand()); - - auto* fxaa_grid = new wxFlexGridSizer(2, space, space); - fxaa_grid->AddGrowableCol(1); - - m_ui.addCheckBox(fxaa_grid, "FXAA (PgUp)", "fxaa", IDC_FXAA); - - sharpening_box->Add(fxaa_grid, wxSizerFlags().Expand()); - - shader_box->Add(sharpening_box.outer, wxSizerFlags().Expand()); - - // Shade boost - CheckboxPrereq shade_boost_check(m_ui.addCheckBox(shader_box.inner, "Enable Shade Boost", "ShadeBoost", IDC_SHADEBOOST)); - PaddedBoxSizer shade_boost_box(wxVERTICAL, this, "Shade Boost"); - auto* shader_boost_grid = new wxFlexGridSizer(2, space, space); - shader_boost_grid->AddGrowableCol(1); - - m_ui.addSliderAndLabel(shader_boost_grid, "Brightness:", "ShadeBoost_Brightness", 0, 100, 50, -1, shade_boost_check); - m_ui.addSliderAndLabel(shader_boost_grid, "Contrast:", "ShadeBoost_Contrast", 0, 100, 50, -1, shade_boost_check); - m_ui.addSliderAndLabel(shader_boost_grid, "Saturation:", "ShadeBoost_Saturation", 0, 100, 50, -1, shade_boost_check); - - shade_boost_box->Add(shader_boost_grid, wxSizerFlags().Expand()); - shader_box->Add(shade_boost_box.outer, wxSizerFlags().Expand()); - - // TV Shader - auto* tv_box = new wxFlexGridSizer(2, space, space); - tv_box->AddGrowableCol(1); - - m_ui.addComboBoxAndLabel(tv_box, "TV Shader:", "TVShader", &theApp.m_gs_tv_shaders); - - shader_box->Add(tv_box, wxSizerFlags().Expand()); - tab_box->Add(shader_box.outer, wxSizerFlags().Expand()); - - SetSizerAndFit(tab_box.outer); -} - -OSDTab::OSDTab(wxWindow* parent) - : wxPanel(parent, wxID_ANY) - , m_ui(this) -{ - const int space = wxSizerFlags().Border().GetBorderInPixels(); - PaddedBoxSizer tab_box(wxVERTICAL); - - PaddedBoxSizer font_box(wxVERTICAL, this, "Visuals"); - auto* font_grid = new wxFlexGridSizer(2, space, space); - font_grid->AddGrowableCol(1); - - m_ui.addSliderAndLabel(font_grid, "OSD Scale:", "OsdScale", 50, 300, 100, -1); - - font_box->Add(font_grid, wxSizerFlags().Expand()); - tab_box->Add(font_box.outer, wxSizerFlags().Expand()); - - PaddedBoxSizer log_box(wxVERTICAL, this, "Log Messages"); - auto* log_grid = new wxFlexGridSizer(2, space, space); - log_grid->AddGrowableCol(1); - - m_ui.addCheckBox(log_grid, "Show Notifications", "OsdShowMessages", -1); - m_ui.addCheckBox(log_grid, "Show Speed", "OsdShowSpeed", -1); - m_ui.addCheckBox(log_grid, "Show FPS", "OsdShowFPS", -1); - m_ui.addCheckBox(log_grid, "Show CPU Usage", "OsdShowCPU", -1); - m_ui.addCheckBox(log_grid, "Show GPU Usage", "OsdShowGPU", -1); - m_ui.addCheckBox(log_grid, "Show Resolution", "OsdShowResolution", -1); - m_ui.addCheckBox(log_grid, "Show Statistics", "OsdShowGSStats", -1); - m_ui.addCheckBox(log_grid, "Show Indicators", "OsdShowIndicators", -1); - m_ui.addCheckBox(log_grid, "Show Settings", "OsdShowSettings", -1); - m_ui.addCheckBox(log_grid, "Show Inputs", "OsdShowInputs", -1); - - log_box->Add(log_grid, wxSizerFlags().Expand()); - tab_box->Add(log_box.outer, wxSizerFlags().Expand()); - - SetSizerAndFit(tab_box.outer); -} - -DebugTab::DebugTab(wxWindow* parent) - : wxPanel(parent, wxID_ANY) - , m_ui(this) -{ - const int space = wxSizerFlags().Border().GetBorderInPixels(); - PaddedBoxSizer tab_box(wxVERTICAL); - - auto vk_ogl_hw_prereq = [this] { return m_is_ogl_hw || m_is_vk_hw; }; - - if (g_Conf->DevMode || IsDevBuild) - { - PaddedBoxSizer debug_box(wxVERTICAL, this, "Debug"); - auto* debug_check_box = new wxFlexGridSizer(2, space, space); - m_ui.addCheckBox(debug_check_box, "Disable Dual-Source Blending", "DisableDualSourceBlend"); - m_ui.addCheckBox(debug_check_box, "Disable Framebuffer Fetch", "DisableFramebufferFetch"); - m_ui.addCheckBox(debug_check_box, "Disable Shader Cache", "disable_shader_cache"); - m_ui.addCheckBox(debug_check_box, "Use Blit Swap Chain", "UseBlitSwapChain"); - m_ui.addCheckBox(debug_check_box, "Dump GS data", "dump"); - m_ui.addCheckBox(debug_check_box, "Use Debug Device", "UseDebugDevice"); - - auto* debug_save_check_box = new wxWrapSizer(wxHORIZONTAL); - m_ui.addCheckBox(debug_save_check_box, "Save RT", "save"); - m_ui.addCheckBox(debug_save_check_box, "Save Frame", "savef"); - m_ui.addCheckBox(debug_save_check_box, "Save Texture", "savet"); - m_ui.addCheckBox(debug_save_check_box, "Save Depth", "savez"); - - debug_box->Add(debug_check_box); - debug_box->AddSpacer(space); - debug_box->Add(debug_save_check_box); - - auto* dump_grid = new wxFlexGridSizer(2, space, space); - - m_ui.addSpinAndLabel(dump_grid, "Start of Dump:", "saven", 0, pow(10, 9), 0); - m_ui.addSpinAndLabel(dump_grid, "Length of Dump:", "savel", 1, pow(10, 5), 5000); - - debug_box->AddSpacer(space); - debug_box->Add(dump_grid); - - tab_box->Add(debug_box.outer, wxSizerFlags().Expand()); - } - - PaddedBoxSizer ogl_box(wxVERTICAL, this, "Overrides"); - auto* ogl_grid = new wxFlexGridSizer(2, space, space); - m_ui.addComboBoxAndLabel(ogl_grid, "Texture Barriers:", "OverrideTextureBarriers", &theApp.m_gs_generic_list, -1, vk_ogl_hw_prereq); - m_ui.addComboBoxAndLabel(ogl_grid, "Geometry Shader:", "OverrideGeometryShaders", &theApp.m_gs_generic_list, IDC_GEOMETRY_SHADER_OVERRIDE, vk_ogl_hw_prereq); - m_ui.addComboBoxAndLabel(ogl_grid, "Hardware Download Mode:", "HWDownloadMode", &theApp.m_gs_hw_download_mode, -1); - m_ui.addComboBoxAndLabel(ogl_grid, "Dump Compression:", "GSDumpCompression", &theApp.m_gs_dump_compression, -1); - ogl_box->Add(ogl_grid); - - tab_box->Add(ogl_box.outer, wxSizerFlags().Expand()); - - PaddedBoxSizer tex_box(wxVERTICAL, this, "Texture Replacements"); - auto* tex_grid = new wxFlexGridSizer(2, space, space); - m_ui.addCheckBox(tex_grid, "Dump Textures", "DumpReplaceableTextures", -1); - m_ui.addCheckBox(tex_grid, "Dump Mipmaps", "DumpReplaceableMipmaps", -1); - m_ui.addCheckBox(tex_grid, "Dump FMV Textures", "DumpTexturesWithFMVActive", -1); - m_ui.addCheckBox(tex_grid, "Async Texture Loading", "LoadTextureReplacementsAsync", -1); - m_ui.addCheckBox(tex_grid, "Load Textures", "LoadTextureReplacements", -1); - m_ui.addCheckBox(tex_grid, "Precache Textures", "PrecacheTextureReplacements", -1); - tex_box->Add(tex_grid); - - tab_box->Add(tex_box.outer, wxSizerFlags().Expand()); - - SetSizerAndFit(tab_box.outer); -} - -void DebugTab::DoUpdate() -{ - m_ui.Update(); -} - -Dialog::Dialog() - : wxDialog(nullptr, wxID_ANY, "Graphics Settings", wxDefaultPosition, wxDefaultSize, wxCAPTION | wxCLOSE_BOX | wxRESIZE_BORDER) - , m_ui(this) -{ - const int space = wxSizerFlags().Border().GetBorderInPixels(); - auto* padding = new wxBoxSizer(wxVERTICAL); - m_top_box = new wxBoxSizer(wxVERTICAL); - - auto* top_grid = new wxFlexGridSizer(2, space, space); - top_grid->SetFlexibleDirection(wxHORIZONTAL); - - m_renderer_select = m_ui.addComboBoxAndLabel(top_grid, "Renderer:", "Renderer", &theApp.m_gs_renderers).first; - m_renderer_select->Bind(wxEVT_CHOICE, &Dialog::OnRendererChange, this); - - add_label(this, top_grid, "Adapter:"); - m_adapter_select = new wxChoice(this, wxID_ANY, wxDefaultPosition, wxDefaultSize, {}); - top_grid->Add(m_adapter_select, wxSizerFlags().Expand()); - - m_ui.addComboBoxAndLabel(top_grid, "Deinterlacing (F5):", "deinterlace_mode", &theApp.m_gs_deinterlace); - - m_bifilter_select = m_ui.addComboBoxAndLabel(top_grid, "Texture Filtering:", "filter", &theApp.m_gs_bifilter, IDC_FILTER).first; - - auto* book = new wxNotebook(this, wxID_ANY, wxDefaultPosition, wxDefaultSize); - - m_renderer_panel = new RendererTab(book); - m_hacks_panel = new HacksTab(book); - m_rec_panel = new RecTab(book); - m_post_panel = new PostTab(book); - m_osd_panel = new OSDTab(book); - m_debug_panel = new DebugTab(book); - - book->AddPage(m_renderer_panel, "Renderer", true); - book->AddPage(m_hacks_panel, "Hacks"); - book->AddPage(m_post_panel, "Shader"); - book->AddPage(m_osd_panel, "OSD"); - book->AddPage(m_rec_panel, "Recording"); - book->AddPage(m_debug_panel, "Advanced"); - - m_top_box->Add(top_grid, wxSizerFlags().Centre()); - m_top_box->AddSpacer(space); - m_top_box->Add(book, wxSizerFlags(1).Expand()); - - padding->Add(m_top_box, wxSizerFlags(1).Expand().Border()); - - m_top_box->Add(CreateStdDialogButtonSizer(wxOK | wxCANCEL), wxSizerFlags().Right()); - - SetSizerAndFit(padding); - Bind(wxEVT_CHECKBOX, &Dialog::CallUpdate, this); - Bind(wxEVT_SPINCTRL, &Dialog::CallUpdate, this); - Bind(wxEVT_CHOICE, &Dialog::CallUpdate, this); -} - -Dialog::~Dialog() -{ -} - -void Dialog::CallUpdate(wxCommandEvent&) -{ - Update(); -} - -void Dialog::OnRendererChange(wxCommandEvent&) -{ - RendererChange(); - Update(); -} - -GSRendererType Dialog::GetSelectedRendererType() -{ - const int index = m_renderer_select->GetSelection(); - - // there is no currently selected renderer or the combo box has more entries than the renderer list or the current selection is negative - // make sure you haven't made a mistake initializing everything - ASSERT(index < static_cast(theApp.m_gs_renderers.size()) || index >= 0); - - const GSRendererType type = static_cast(theApp.m_gs_renderers[index].value); - return (type == GSRendererType::Auto) ? GSUtil::GetPreferredRenderer() : type; -} - -void Dialog::RendererChange() -{ - const GSRendererType renderer = GetSelectedRendererType(); - const std::string current_adapter(theApp.GetConfigS("Adapter")); - - HostDisplay::AdapterAndModeList list; - switch (renderer) - { -#ifdef _WIN32 - case GSRendererType::DX11: - list = D3D11HostDisplay::StaticGetAdapterAndModeList(); - break; - case GSRendererType::DX12: - list = D3D12HostDisplay::StaticGetAdapterAndModeList(); - break; -#endif -#ifdef ENABLE_VULKAN - case GSRendererType::VK: - list = VulkanHostDisplay::StaticGetAdapterAndModeList(nullptr); - break; -#endif -#ifdef __APPLE__ - case GSRendererType::Metal: - list = GetMetalAdapterAndModeList(); - break; -#endif - default: - break; - } - - m_adapter_select->Clear(); - m_adapter_select->Insert(_("Default Adapter"), 0); - if (current_adapter.empty()) - m_adapter_select->SetSelection(0); - - for (const std::string& name : list.adapter_names) - { - m_adapter_select->Insert(fromUTF8(name), m_adapter_select->GetCount()); - if (current_adapter == name) - m_adapter_select->SetSelection(m_adapter_select->GetCount() - 1); - } - - m_adapter_select->Enable(!list.adapter_names.empty()); - -#ifdef _WIN32 - m_renderer_panel->Layout(); // The version of wx we use on Windows is dumb and something prevents relayout from happening to notebook pages -#endif -} - -void Dialog::Load() -{ - m_ui.Load(); - - const GSRendererType renderer = GSRendererType(theApp.GetConfigI("Renderer")); - m_renderer_select->SetSelection(get_config_index(theApp.m_gs_renderers, static_cast(renderer))); - - RendererChange(); - - m_hacks_panel->Load(); - m_renderer_panel->Load(); - m_rec_panel->Load(); - m_post_panel->Load(); - m_osd_panel->Load(); - m_debug_panel->Load(); -} - -void Dialog::Save() -{ - m_ui.Save(); - // only save the adapter when it makes sense to - // prevents changing the adapter, switching to another renderer and saving - if (m_adapter_select->GetCount() > 1) - { - // First option is system default - if (m_adapter_select->GetSelection() == 0) - theApp.SetConfig("Adapter", ""); - else - theApp.SetConfig("Adapter", m_adapter_select->GetStringSelection().c_str()); - } - - m_hacks_panel->Save(); - m_renderer_panel->Save(); - m_rec_panel->Save(); - m_post_panel->Save(); - m_osd_panel->Save(); - m_debug_panel->Save(); -} - -void Dialog::Update() -{ - GSRendererType renderer = GetSelectedRendererType(); - if (renderer == GSRendererType::Null) - { - m_ui.DisableAll(); - m_renderer_select->Enable(); - m_hacks_panel->m_ui.DisableAll(); - m_renderer_panel->m_ui.DisableAll(); - m_rec_panel->m_ui.DisableAll(); - m_post_panel->m_ui.DisableAll(); - m_osd_panel->m_ui.DisableAll(); - m_debug_panel->m_ui.DisableAll(); - } - else - { - // cross-tab dependencies yay - const bool is_hw = renderer == GSRendererType::OGL || renderer == GSRendererType::DX11 || renderer == GSRendererType::VK || renderer == GSRendererType::Metal || renderer == GSRendererType::DX12; - const bool is_upscale = m_renderer_panel->m_internal_resolution->GetSelection() != 0; - m_hacks_panel->m_is_native_res = !is_hw || !is_upscale; - m_hacks_panel->m_is_hardware = is_hw; - m_renderer_panel->m_is_hardware = is_hw; - m_debug_panel->m_is_ogl_hw = renderer == GSRendererType::OGL; - m_debug_panel->m_is_vk_hw = renderer == GSRendererType::VK; - - m_ui.Update(); - m_hacks_panel->DoUpdate(); - m_renderer_panel->DoUpdate(); - m_rec_panel->DoUpdate(); - m_post_panel->DoUpdate(); - m_osd_panel->DoUpdate(); - m_debug_panel->DoUpdate(); - } -} - -bool RunwxDialog() -{ - Dialog GSSettingsDialog; - - GSSettingsDialog.Load(); - GSSettingsDialog.Update(); - if (GSSettingsDialog.ShowModal() == wxID_OK) - GSSettingsDialog.Save(); - - return true; -} diff --git a/pcsx2/GS/Window/GSwxDialog.h b/pcsx2/GS/Window/GSwxDialog.h deleted file mode 100644 index 7800f9f2cf..0000000000 --- a/pcsx2/GS/Window/GSwxDialog.h +++ /dev/null @@ -1,205 +0,0 @@ -/* PCSX2 - PS2 Emulator for PCs - * Copyright (C) 2002-2021 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 . - */ - -#pragma once - -#include "GS.h" -#include "GSSetting.h" - -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include - -class GSUIElementHolder -{ - class GSwxChoice : public wxChoice - { - public: - const std::vector& settings; - - GSwxChoice( - wxWindow* parent, - wxWindowID id, - const wxPoint& pos, - const wxSize& size, - const wxArrayString& choices, - const std::vector* settings, - long style = 0, - const wxValidator& validator = wxDefaultValidator, - const wxString& name = wxChoiceNameStr) - : wxChoice(parent, id, pos, size, choices, style, validator, name) - , settings(*settings) - { - } - }; - - struct UIElem - { - enum class Type - { - CheckBox, - Choice, - Spin, - Slider, - File, - Directory, - }; - - Type type; - wxControl* control; - const char* config; - std::function prereq; - - UIElem(Type type, wxControl* control, const char* config, std::function prereq) - : type(type), control(control), config(config), prereq(prereq) - { - } - }; - - static bool noPrereq() { return true; } - - wxWindow* m_window; - std::vector m_elems; - - wxStaticText* addWithLabel(wxControl* control, UIElem::Type type, wxSizer* sizer, const char* label, const char* config_name, int tooltip, std::function prereq, wxSizerFlags flags = wxSizerFlags().Centre().Expand().Left()); - -public: - GSUIElementHolder(wxWindow* window); - wxCheckBox* addCheckBox(wxSizer* sizer, const char* label, const char* config_name, int tooltip = -1, std::function prereq = noPrereq); - std::pair addComboBoxAndLabel(wxSizer* sizer, const char* label, const char* config_name, const std::vector* settings, int tooltip = -1, std::function prereq = noPrereq); - wxSpinCtrl* addSpin(wxSizer* sizer, const char* config_name, int min, int max, int initial, int tooltip = -1, std::function prereq = noPrereq); - std::pair addSpinAndLabel(wxSizer* sizer, const char* label, const char* config_name, int min, int max, int initial, int tooltip = -1, std::function prereq = noPrereq); - std::pair addSliderAndLabel(wxSizer* sizer, const char* label, const char* config_name, int min, int max, int initial, int tooltip = -1, std::function prereq = noPrereq); - std::pair addFilePickerAndLabel(wxSizer* sizer, const char* label, const char* config_name, int tooltip = -1, std::function prereq = noPrereq); - std::pair addDirPickerAndLabel(wxSizer* sizer, const char* label, const char* config_name, int tooltip = -1, std::function prereq = noPrereq); - - void Load(); - void Save(); - void Update(); - void DisableAll(); -}; - -namespace GSSettingsDialog -{ - class RendererTab : public wxPanel - { - public: - GSUIElementHolder m_ui; - wxChoice* m_internal_resolution; - bool m_is_hardware = false; - - RendererTab(wxWindow* parent); - void Load() { m_ui.Load(); } - void Save() { m_ui.Save(); } - void DoUpdate() { m_ui.Update(); } - }; - - class HacksTab : public wxPanel - { - public: - GSUIElementHolder m_ui; - wxSpinCtrl *skip_x_spin, *skip_y_spin; - bool m_is_hardware = false; - bool m_is_native_res = false; - - HacksTab(wxWindow* parent); - void Load() { m_ui.Load(); } - void Save() { m_ui.Save(); } - void DoUpdate(); - }; - - class DebugTab : public wxPanel - { - public: - GSUIElementHolder m_ui; - bool m_is_ogl_hw = false; - bool m_is_vk_hw = false; - - DebugTab(wxWindow* parent); - void Load() { m_ui.Load(); } - void Save() { m_ui.Save(); } - void DoUpdate(); - }; - - class RecTab : public wxPanel - { - public: - GSUIElementHolder m_ui; - - RecTab(wxWindow* parent); - void Load() { m_ui.Load(); } - void Save() { m_ui.Save(); } - void DoUpdate() { m_ui.Update(); } - }; - - class PostTab : public wxPanel - { - public: - GSUIElementHolder m_ui; - - PostTab(wxWindow* parent); - void Load() { m_ui.Load(); } - void Save() { m_ui.Save(); } - void DoUpdate() { m_ui.Update(); } - }; - - class OSDTab : public wxPanel - { - public: - GSUIElementHolder m_ui; - - OSDTab(wxWindow* parent); - void Load() { m_ui.Load(); } - void Save() { m_ui.Save(); } - void DoUpdate() { m_ui.Update(); } - }; - - class Dialog : public wxDialog - { - GSUIElementHolder m_ui; - - wxBoxSizer* m_top_box; - wxChoice* m_renderer_select; - wxChoice* m_adapter_select; - wxChoice* m_bifilter_select; - RendererTab* m_renderer_panel; - HacksTab* m_hacks_panel; - DebugTab* m_debug_panel; - RecTab* m_rec_panel; - PostTab* m_post_panel; - OSDTab* m_osd_panel; - - public: - Dialog(); - ~Dialog(); - void Load(); - void Save(); - void Update(); - void CallUpdate(wxCommandEvent& event); - void OnRendererChange(wxCommandEvent& event); - void RendererChange(); - GSRendererType GetSelectedRendererType(); - }; - -} // namespace GSSettingsDialog - -extern bool RunwxDialog(); diff --git a/pcsx2/Linux/LnxConsolePipe.cpp b/pcsx2/Linux/LnxConsolePipe.cpp deleted file mode 100644 index 8b3510262c..0000000000 --- a/pcsx2/Linux/LnxConsolePipe.cpp +++ /dev/null @@ -1,167 +0,0 @@ -/* PCSX2 - PS2 Emulator for PCs - * Copyright (C) 2002-2015 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 . - */ - -#include "PrecompiledHeader.h" - -#include "common/StringUtil.h" -#include "gui/App.h" -#include "gui/ConsoleLogger.h" -#include - -using namespace Threading; - -// Redirects and reads data sent to stdout/stderr and sends it to the PCSX2 -// console. -class LinuxPipeThread : public pxThread -{ - typedef pxThread _parent; - -protected: - FILE* m_stdstream; - FILE* m_fp; - const ConsoleColors m_color; - int m_pipe_fd[2]; - - void ExecuteTaskInThread(); -public: - LinuxPipeThread(FILE* stdstream); - virtual ~LinuxPipeThread(); -}; - -LinuxPipeThread::LinuxPipeThread(FILE* stdstream) - : pxThread(stdstream == stderr? L"Redirect_Stderr" : L"Redirect_Stdout") - , m_stdstream(stdstream) - , m_fp(nullptr) - , m_color(stdstream == stderr? Color_Red : Color_Black) - , m_pipe_fd{-1, -1} -{ - const wxChar* stream_name = stdstream == stderr? L"stderr" : L"stdout"; - - // Save the original stdout/stderr file descriptor - int dup_fd = dup(fileno(stdstream)); - if (dup_fd == -1) - throw Exception::RuntimeError().SetDiagMsg(StringUtil::StdStringFromFormat( - "Redirect %s failed: dup: %s", stream_name, strerror(errno))); - m_fp = fdopen(dup_fd, "w"); - if (m_fp == nullptr) { - int error = errno; - close(dup_fd); - throw Exception::RuntimeError().SetDiagMsg(StringUtil::StdStringFromFormat( - "Redirect %s failed: fdopen: %s", stream_name, strerror(error))); - } - - if (pipe(m_pipe_fd) == -1) { - int error = errno; - fclose(m_fp); - throw Exception::RuntimeError().SetDiagMsg(StringUtil::StdStringFromFormat( - "Redirect %s failed: pipe: %s", stream_name, strerror(error))); - } -} - -LinuxPipeThread::~LinuxPipeThread() -{ - // Close write end of the pipe first so the redirection thread starts - // finishing up and restore the original stdout/stderr file descriptor so - // messages aren't lost. - dup2(fileno(m_fp), m_pipe_fd[1]); - - if (m_stdstream == stdout) - Console_SetStdout(stdout); - fclose(m_fp); - - // Read end of pipe should only be closed after the thread terminates to - // prevent messages being lost. - m_mtx_InThread.Wait(); - close(m_pipe_fd[0]); -} - -void LinuxPipeThread::ExecuteTaskInThread() -{ - const wxChar* stream_name = m_stdstream == stderr? L"stderr" : L"stdout"; - - // Redirect stdout/stderr - int stdstream_fd = fileno(m_stdstream); - if (dup2(m_pipe_fd[1], stdstream_fd) != stdstream_fd) { - Console.Error(wxString::Format(L"Redirect %s failed: dup2: %s", - stream_name, strerror(errno))); - return; - } - - close(m_pipe_fd[1]); - m_pipe_fd[1] = stdstream_fd; - - // Send console output to the original stdout, otherwise there'll be an - // infinite loop. - if (m_stdstream == stdout) - Console_SetStdout(m_fp); - - char buffer[2049]; - while (ssize_t bytes_read = read(m_pipe_fd[0], buffer, sizeof(buffer) - 1)) { - if (bytes_read == -1) { - if (errno == EINTR) { - continue; - } else { - // Should never happen. - Console.Error(wxString::Format(L"Redirect %s failed: read: %s", - stream_name, strerror(errno))); - break; - } - } - - buffer[bytes_read] = 0; - - { - ConsoleColorScope cs(m_color); - Console.WriteRaw(fromUTF8(buffer)); - } - } -} - -class LinuxPipeRedirection : public PipeRedirectionBase -{ - DeclareNoncopyableObject(LinuxPipeRedirection); - -protected: - LinuxPipeThread m_thread; - -public: - LinuxPipeRedirection(FILE* stdstream); - virtual ~LinuxPipeRedirection() = default; -}; - -LinuxPipeRedirection::LinuxPipeRedirection(FILE* stdstream) - : m_thread(stdstream) -{ - pxAssert((stdstream == stderr) || (stdstream == stdout)); - - try { - m_thread.Start(); - } catch (Exception::BaseThreadError& ex) { - // thread object will become invalid because of scoping after we leave - // the constructor, so re-pack a new exception: - throw Exception::RuntimeError().SetDiagMsg(ex.FormatDiagnosticMessage()); - } -} - -PipeRedirectionBase* NewPipeRedir(FILE* stdstream) -{ - try { - return new LinuxPipeRedirection(stdstream); - } catch (Exception::RuntimeError& ex) { - Console.Error(ex.FormatDiagnosticMessage()); - } - - return nullptr; -} diff --git a/pcsx2/Linux/LnxKeyCodes.cpp b/pcsx2/Linux/LnxKeyCodes.cpp deleted file mode 100644 index 226a12c2c0..0000000000 --- a/pcsx2/Linux/LnxKeyCodes.cpp +++ /dev/null @@ -1,294 +0,0 @@ -/* PCSX2 - PS2 Emulator for PCs - * Copyright (C) 2002-2010 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 . - */ - -#include "PrecompiledHeader.h" -#include "gui/ConsoleLogger.h" - -#include - -// Returns a WXK_* keycode, given some kinda GDK input mess! -int TranslateGDKtoWXK( u32 keysym ) -{ - int key_code; - - switch ( keysym ) - { - // Shift, Control and Alt don't generate the CHAR events at all - case GDK_KEY_Shift_L: - case GDK_KEY_Shift_R: - key_code = WXK_SHIFT; - break; - case GDK_KEY_Control_L: - case GDK_KEY_Control_R: - key_code = WXK_CONTROL; - break; - case GDK_KEY_Meta_L: - case GDK_KEY_Meta_R: - case GDK_KEY_Alt_L: - case GDK_KEY_Alt_R: - case GDK_KEY_Super_L: - case GDK_KEY_Super_R: - key_code = WXK_ALT; - break; - - // neither do the toggle modifies - case GDK_KEY_Scroll_Lock: - key_code = WXK_SCROLL; - break; - - case GDK_KEY_Caps_Lock: - key_code = WXK_CAPITAL; - break; - - case GDK_KEY_Num_Lock: - key_code = WXK_NUMLOCK; - break; - - - // various other special keys - case GDK_KEY_Menu: - key_code = WXK_MENU; - break; - - case GDK_KEY_Help: - key_code = WXK_HELP; - break; - - case GDK_KEY_BackSpace: - key_code = WXK_BACK; - break; - - case GDK_KEY_ISO_Left_Tab: - case GDK_KEY_Tab: - key_code = WXK_TAB; - break; - - case GDK_KEY_Linefeed: - case GDK_KEY_Return: - key_code = WXK_RETURN; - break; - - case GDK_KEY_Clear: - key_code = WXK_CLEAR; - break; - - case GDK_KEY_Pause: - key_code = WXK_PAUSE; - break; - - case GDK_KEY_Select: - key_code = WXK_SELECT; - break; - - case GDK_KEY_Print: - key_code = WXK_PRINT; - break; - - case GDK_KEY_Execute: - key_code = WXK_EXECUTE; - break; - - case GDK_KEY_Escape: - key_code = WXK_ESCAPE; - break; - - // cursor and other extended keyboard keys - case GDK_KEY_Delete: - key_code = WXK_DELETE; - break; - - case GDK_KEY_Home: - key_code = WXK_HOME; - break; - - case GDK_KEY_Left: - key_code = WXK_LEFT; - break; - - case GDK_KEY_Up: - key_code = WXK_UP; - break; - - case GDK_KEY_Right: - key_code = WXK_RIGHT; - break; - - case GDK_KEY_Down: - key_code = WXK_DOWN; - break; - - case GDK_KEY_Prior: // == GDK_KEY_Page_Up - key_code = WXK_PAGEUP; - break; - - case GDK_KEY_Next: // == GDK_KEY_Page_Down - key_code = WXK_PAGEDOWN; - break; - - case GDK_KEY_End: - key_code = WXK_END; - break; - - case GDK_KEY_Begin: - key_code = WXK_HOME; - break; - - case GDK_KEY_Insert: - key_code = WXK_INSERT; - break; - - - // numpad keys - case GDK_KEY_KP_0: - case GDK_KEY_KP_1: - case GDK_KEY_KP_2: - case GDK_KEY_KP_3: - case GDK_KEY_KP_4: - case GDK_KEY_KP_5: - case GDK_KEY_KP_6: - case GDK_KEY_KP_7: - case GDK_KEY_KP_8: - case GDK_KEY_KP_9: - key_code = WXK_NUMPAD0 + keysym - GDK_KEY_KP_0; - break; - - case GDK_KEY_KP_Space: - key_code = WXK_NUMPAD_SPACE; - break; - - case GDK_KEY_KP_Tab: - key_code = WXK_NUMPAD_TAB; - break; - - case GDK_KEY_KP_Enter: - key_code = WXK_NUMPAD_ENTER; - break; - - case GDK_KEY_KP_F1: - key_code = WXK_NUMPAD_F1; - break; - - case GDK_KEY_KP_F2: - key_code = WXK_NUMPAD_F2; - break; - - case GDK_KEY_KP_F3: - key_code = WXK_NUMPAD_F3; - break; - - case GDK_KEY_KP_F4: - key_code = WXK_NUMPAD_F4; - break; - - case GDK_KEY_KP_Home: - key_code = WXK_NUMPAD_HOME; - break; - - case GDK_KEY_KP_Left: - key_code = WXK_NUMPAD_LEFT; - break; - - case GDK_KEY_KP_Up: - key_code = WXK_NUMPAD_UP; - break; - - case GDK_KEY_KP_Right: - key_code = WXK_NUMPAD_RIGHT; - break; - - case GDK_KEY_KP_Down: - key_code = WXK_NUMPAD_DOWN; - break; - - case GDK_KEY_KP_Prior: // == GDK_KEY_KP_Page_Up - key_code = WXK_NUMPAD_PAGEUP; - break; - - case GDK_KEY_KP_Next: // == GDK_KEY_KP_Page_Down - key_code = WXK_NUMPAD_PAGEDOWN; - break; - - case GDK_KEY_KP_End: - key_code = WXK_NUMPAD_END; - break; - - case GDK_KEY_KP_Begin: - key_code = WXK_NUMPAD_BEGIN; - break; - - case GDK_KEY_KP_Insert: - key_code = WXK_NUMPAD_INSERT; - break; - - case GDK_KEY_KP_Delete: - key_code = WXK_NUMPAD_DELETE; - break; - - case GDK_KEY_KP_Equal: - key_code = WXK_NUMPAD_EQUAL; - break; - - case GDK_KEY_KP_Multiply: - key_code = WXK_NUMPAD_MULTIPLY; - break; - - case GDK_KEY_KP_Add: - key_code = WXK_NUMPAD_ADD; - break; - - case GDK_KEY_KP_Separator: - // FIXME: what is this? - // - // Some numeric keyboards have a comma on them. I believe this is the symbol - // for the comma, to distinguish it from the period on the numeric keypad. - // --arcum42 - key_code = WXK_NUMPAD_SEPARATOR; - break; - - case GDK_KEY_KP_Subtract: - key_code = WXK_NUMPAD_SUBTRACT; - break; - - case GDK_KEY_KP_Decimal: - key_code = WXK_NUMPAD_DECIMAL; - break; - - case GDK_KEY_KP_Divide: - key_code = WXK_NUMPAD_DIVIDE; - break; - - - // function keys - case GDK_KEY_F1: - case GDK_KEY_F2: - case GDK_KEY_F3: - case GDK_KEY_F4: - case GDK_KEY_F5: - case GDK_KEY_F6: - case GDK_KEY_F7: - case GDK_KEY_F8: - case GDK_KEY_F9: - case GDK_KEY_F10: - case GDK_KEY_F11: - case GDK_KEY_F12: - key_code = WXK_F1 + keysym - GDK_KEY_F1; - break; - - default: - key_code = 0; - } - - return key_code; -} diff --git a/pcsx2/PAD/Linux/Config.cpp b/pcsx2/PAD/Linux/Config.cpp deleted file mode 100644 index f74af285a8..0000000000 --- a/pcsx2/PAD/Linux/Config.cpp +++ /dev/null @@ -1,164 +0,0 @@ -/* PCSX2 - PS2 Emulator for PCs - * Copyright (C) 2002-2020 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 . - */ - -#include "pcsx2/Config.h" -#include "Global.h" -#include "Device.h" -#include "keyboard.h" -#include "common/Path.h" -#ifdef __APPLE__ -#include -#endif - -void DefaultKeyboardValues() -{ -#ifdef __APPLE__ - set_keyboard_key(0, kVK_ANSI_A, PAD_L2); - set_keyboard_key(0, kVK_ANSI_Semicolon, PAD_R2); - set_keyboard_key(0, kVK_ANSI_W, PAD_L1); - set_keyboard_key(0, kVK_ANSI_P, PAD_R1); - set_keyboard_key(0, kVK_ANSI_I, PAD_TRIANGLE); - set_keyboard_key(0, kVK_ANSI_L, PAD_CIRCLE); - set_keyboard_key(0, kVK_ANSI_K, PAD_CROSS); - set_keyboard_key(0, kVK_ANSI_J, PAD_SQUARE); - set_keyboard_key(0, kVK_ANSI_V, PAD_SELECT); - set_keyboard_key(0, kVK_ANSI_N, PAD_START); - set_keyboard_key(0, kVK_ANSI_E, PAD_UP); - set_keyboard_key(0, kVK_ANSI_F, PAD_RIGHT); - set_keyboard_key(0, kVK_ANSI_D, PAD_DOWN); - set_keyboard_key(0, kVK_ANSI_S, PAD_LEFT); -#else - set_keyboard_key(0, XK_a, PAD_L2); - set_keyboard_key(0, XK_semicolon, PAD_R2); - set_keyboard_key(0, XK_w, PAD_L1); - set_keyboard_key(0, XK_p, PAD_R1); - set_keyboard_key(0, XK_i, PAD_TRIANGLE); - set_keyboard_key(0, XK_l, PAD_CIRCLE); - set_keyboard_key(0, XK_k, PAD_CROSS); - set_keyboard_key(0, XK_j, PAD_SQUARE); - set_keyboard_key(0, XK_v, PAD_SELECT); - set_keyboard_key(0, XK_n, PAD_START); - set_keyboard_key(0, XK_e, PAD_UP); - set_keyboard_key(0, XK_f, PAD_RIGHT); - set_keyboard_key(0, XK_d, PAD_DOWN); - set_keyboard_key(0, XK_s, PAD_LEFT); -#endif -} - -void PADSaveConfig() -{ - FILE* f; - - const std::string iniFile = Path::Combine(EmuFolders::Settings, "PAD.ini"); // default path, just in case - f = fopen(iniFile.c_str(), "w"); - if (f == NULL) - { - Console.Warning("PAD: failed to save ini %s", iniFile.c_str()); - return; - } - - fprintf(f, "first_time_wizard = %d\n", g_conf.ftw); - fprintf(f, "options = %d\n", g_conf.packed_options); - fprintf(f, "mouse_sensibility = %d\n", g_conf.get_sensibility()); - fprintf(f, "ff_intensity = %g\n", g_conf.get_ff_intensity()); - fprintf(f, "uid[0] = %zu\n", g_conf.get_joy_uid(0)); - fprintf(f, "uid[1] = %zu\n", g_conf.get_joy_uid(1)); - - for (u32 pad = 0; pad < GAMEPAD_NUMBER; pad++) - for (auto const& it : g_conf.keysym_map[pad]) - fprintf(f, "PAD %d:KEYSYM 0x%x = %d\n", pad, it.first, it.second); - - for (auto const& it : g_conf.sdl2_mapping) - fprintf(f, "SDL2 = %s\n", it.c_str()); - - for (auto const& pair : g_conf.sdl2_hints) - fprintf(f, "SDL_HINT_%s = %s\n", pair.first.c_str(), pair.second.c_str()); - - fclose(f); -} - -void PADLoadConfig() -{ - FILE* f; - bool have_user_setting = false; - - g_conf.init(); - - - const std::string iniFile = Path::Combine(EmuFolders::Settings, "PAD.ini"); // default path, just in case - f = fopen(iniFile.c_str(), "r"); - if (f == nullptr) - { - Console.Warning("PAD: failed to load ini %s", iniFile.c_str()); - PADSaveConfig(); //save and return - return; - } - - u32 value; - float fvalue; - - if (fscanf(f, "first_time_wizard = %u\n", &value) == 1) - g_conf.ftw = value; - - if (fscanf(f, "options = %u\n", &value) == 1) - g_conf.packed_options = value; - - if (fscanf(f, "mouse_sensibility = %u\n", &value) == 1) - g_conf.set_sensibility(value); - - if (fscanf(f, "ff_intensity = %f\n", &fvalue) == 1) - { - if (fvalue > 1) - fvalue /= 0x7fff; // Old config - g_conf.set_ff_intensity(fvalue); - } - - size_t uid; - if (fscanf(f, "uid[0] = %zu\n", &uid) == 1) - g_conf.set_joy_uid(0, uid); - if (fscanf(f, "uid[1] = %zu\n", &uid) == 1) - g_conf.set_joy_uid(1, uid); - - u32 pad; - u32 keysym; - u32 index; - - while (fscanf(f, "PAD %u:KEYSYM 0x%x = %u\n", &pad, &keysym, &index) == 3) - { - set_keyboard_key(pad & 1, keysym, index); - if (pad == 0) - have_user_setting = true; - } - - char extra_name[512]; - char extra_value[512]; - while (fscanf(f, "%511[^ =] = %511[^\n]\n", extra_name, extra_value) == 2) - { - static constexpr const char* HINT_PREFIX = "SDL_HINT_"; - if (strcmp(extra_name, "SDL2") == 0) - { - g_conf.sdl2_mapping.push_back(std::string(extra_value)); - } - else if (strncmp(extra_name, HINT_PREFIX, strlen(HINT_PREFIX)) == 0) - { - g_conf.sdl2_hints.push_back({extra_name + strlen(HINT_PREFIX), extra_value}); - } - } - - if (!have_user_setting) - DefaultKeyboardValues(); - - fclose(f); -} diff --git a/pcsx2/PAD/Linux/Config.h b/pcsx2/PAD/Linux/Config.h deleted file mode 100644 index 594f5449c4..0000000000 --- a/pcsx2/PAD/Linux/Config.h +++ /dev/null @@ -1,133 +0,0 @@ -/* PCSX2 - PS2 Emulator for PCs - * Copyright (C) 2002-2020 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 . - */ - -#pragma once - -#include "Global.h" - -class PADconf -{ - float ff_intensity; - u32 sensibility; - -public: - union - { - struct - { - u16 forcefeedback : 1; - u16 reverse_lx : 1; - u16 reverse_ly : 1; - u16 reverse_rx : 1; - u16 reverse_ry : 1; - u16 mouse_l : 1; - u16 mouse_r : 1; - u16 _free : 9; // The 9 remaining bits are unused, do what you wish with them ;) - } pad_options[GAMEPAD_NUMBER]; // One for each pads - u32 packed_options; // Only first 8 bits of each 16 bits series are really used, rest is padding - }; - - u32 ftw; - std::map keysym_map[GAMEPAD_NUMBER]; - std::array unique_id; - std::vector sdl2_mapping; - std::vector> sdl2_hints; - - PADconf() { init(); } - - void init() - { - ftw = 1; - packed_options = 0; - ff_intensity = 1.0; // set it at max value by default - sensibility = 100; - for (u32 pad = 0; pad < GAMEPAD_NUMBER; pad++) - { - keysym_map[pad].clear(); - } - unique_id.fill(0); - sdl2_mapping.clear(); - } - - void set_joy_uid(u32 pad, size_t uid) - { - if (pad < GAMEPAD_NUMBER) - unique_id[pad] = uid; - } - - size_t get_joy_uid(u32 pad) - { - if (pad < GAMEPAD_NUMBER) - return unique_id[pad]; - else - return 0; - } - - /** - * Return (a copy of) private memner ff_instensity - **/ - float get_ff_intensity() - { - return ff_intensity; - } - - /** - * Set intensity while checking that the new value is within - * valid range, more than 0x7FFF will cause pad not to rumble(and less than 0 is obviously bad) - **/ - void set_ff_intensity(float new_intensity) - { - ff_intensity = std::min(std::max(new_intensity, 0.f), 1.f); - } - - /** - * Set sensibility value. - * There will be an upper range, and less than 0 is obviously wrong. - * We are doing object oriented code, so members are definitely not supposed to be public. - **/ - void set_sensibility(u32 new_sensibility) - { - if (new_sensibility > 0) - { - sensibility = new_sensibility; - } - else - { - sensibility = 1; - } - } - - u32 get_sensibility() - { - return sensibility; - } -}; -extern PADconf g_conf; - -static __forceinline void set_keyboard_key(int pad, int keysym, int index) -{ - g_conf.keysym_map[pad][keysym] = index; -} - -static __forceinline int get_keyboard_key(int pad, int keysym) -{ - // You must use find instead of [] - // [] will create an element if the key does not exist and return 0 - std::map::iterator it = g_conf.keysym_map[pad].find(keysym); - if (it != g_conf.keysym_map[pad].end()) - return it->second; - else - return -1; -} diff --git a/pcsx2/PAD/Linux/Device.cpp b/pcsx2/PAD/Linux/Device.cpp deleted file mode 100644 index 6ae52ff703..0000000000 --- a/pcsx2/PAD/Linux/Device.cpp +++ /dev/null @@ -1,61 +0,0 @@ -/* PCSX2 - PS2 Emulator for PCs - * Copyright (C) 2002-2020 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 . - */ - -#include "Device.h" -#ifdef SDL_BUILD -#include "SDL/joystick.h" -#endif - -/** - * Following static methods are just forwarders to their backend - * This is where link between agnostic and specific code is done - **/ - -/** - * Safely dispatch to the Rumble method above - **/ -void Device::DoRumble(unsigned type, unsigned pad) -{ - int index = uid_to_index(pad); - if (index >= 0) - device_manager.devices[index]->Rumble(type, pad); -} - -size_t Device::index_to_uid(int index) -{ - if ((index >= 0) && (index < (int)device_manager.devices.size())) - return device_manager.devices[index]->GetUniqueIdentifier(); - else - return 0; -} - -int Device::uid_to_index(int pad) -{ - size_t uid = g_conf.get_joy_uid(pad); - - for (int i = 0; i < (int)device_manager.devices.size(); ++i) - { - if (device_manager.devices[i]->GetUniqueIdentifier() == uid) - return i; - } - - // Current uid wasn't found maybe the pad was unplugged. Or - // user didn't select it. Fallback to 1st pad for - // 1st player. And 2nd pad for 2nd player. - if ((int)device_manager.devices.size() > pad) - return pad; - - return -1; -} diff --git a/pcsx2/PAD/Linux/Device.h b/pcsx2/PAD/Linux/Device.h deleted file mode 100644 index 38e8c2ed12..0000000000 --- a/pcsx2/PAD/Linux/Device.h +++ /dev/null @@ -1,107 +0,0 @@ -/* PCSX2 - PS2 Emulator for PCs - * Copyright (C) 2002-2020 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 . - */ - -#pragma once - -#include "Global.h" -#include "InputManager.h" - -#ifdef SDL_BUILD -#include -#endif - -enum DeviceAPI -{ - NO_API = 0, - KEYBOARD_API = 16, - SDL_AUTO = 17 -}; - -enum DeviceType -{ - NO_DEVICE = 0, - KEYBOARD = 1, - MOUSE = 2, - OTHER = 3 -}; - -class Device -{ -public: - Device() - : m_unique_id(0) - , m_device_name("") - , api(NO_API) - , type(NO_DEVICE) - , m_deadzone(1500) - , m_no_error(false) - { - } - - virtual ~Device() - { - } - - Device(const Device&); // copy constructor - Device& operator=(const Device&); // assignment - - /* - * Update state of every attached devices - */ - virtual void UpdateDeviceState() = 0; - - /* - * Causes devices to rumble - * Rumble will differ according to type which is either 0(small motor) or 1(big motor) - */ - virtual void Rumble(unsigned type, unsigned pad) {} - /* - * Safely dispatch to the Rumble method above - */ - static void DoRumble(unsigned type, unsigned pad); - - /* - * Used for GUI checkbox to give feedback to the user - */ - virtual bool TestForce(float strength = 0.6) { return false; } - - virtual const char* GetName() = 0; - - virtual int GetInput(gamePadValues input) = 0; - - int GetDeadzone() - { - return m_deadzone; - } - - virtual size_t GetUniqueIdentifier() = 0; - - static size_t index_to_uid(int index); - static int uid_to_index(int pad); - - bool IsProperlyInitialized() - { - return m_no_error; - } - - size_t m_unique_id; - std::string m_device_name; - DeviceAPI api; - DeviceType type; - -protected: - int m_deadzone; - bool m_no_error; -}; diff --git a/pcsx2/PAD/Linux/Global.h b/pcsx2/PAD/Linux/Global.h deleted file mode 100644 index c2ba2a9aac..0000000000 --- a/pcsx2/PAD/Linux/Global.h +++ /dev/null @@ -1,117 +0,0 @@ -/* PCSX2 - PS2 Emulator for PCs - * Copyright (C) 2002-2021 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 . - */ - -#pragma once - -#include -#include -#include -#include -#include -#include -#include -#include -#include - -#include "common/Pcsx2Defs.h" -#include "bitwise.h" - -#include "common/Console.h" -#include "common/mt_queue.h" -#include "DebugTools/Debug.h" - -#define PADdefs - -static const u32 GAMEPAD_NUMBER = 2; -static const u32 MAX_KEYS = 24; - -#include "Config.h" - -enum gamePadValues -{ - PAD_L2 = 0, // L2 button - PAD_R2, // R2 button - PAD_L1, // L1 button - PAD_R1, // R1 button - PAD_TRIANGLE, // Triangle button ▲ - PAD_CIRCLE, // Circle button ● - PAD_CROSS, // Cross button ✖ - PAD_SQUARE, // Square button ■ - PAD_SELECT, // Select button - PAD_L3, // Left joystick button (L3) - PAD_R3, // Right joystick button (R3) - PAD_START, // Start button - PAD_UP, // Directional pad ↑ - PAD_RIGHT, // Directional pad → - PAD_DOWN, // Directional pad ↓ - PAD_LEFT, // Directional pad ← - PAD_L_UP, // Left joystick (Up) ↑ - PAD_L_RIGHT, // Left joystick (Right) → - PAD_L_DOWN, // Left joystick (Down) ↓ - PAD_L_LEFT, // Left joystick (Left) ← - PAD_R_UP, // Right joystick (Up) ↑ - PAD_R_RIGHT, // Right joystick (Right) → - PAD_R_DOWN, // Right joystick (Down) ↓ - PAD_R_LEFT // Right joystick (Left) ← -}; - -static const std::array all_keys = -{ - PAD_L2, // L2 button - PAD_R2, // R2 button - PAD_L1, // L1 button - PAD_R1, // R1 button - PAD_TRIANGLE, // Triangle button ▲ - PAD_CIRCLE, // Circle button ● - PAD_CROSS, // Cross button ✖ - PAD_SQUARE, // Square button ■ - PAD_SELECT, // Select button - PAD_L3, // Left joystick button (L3) - PAD_R3, // Right joystick button (R3) - PAD_START, // Start button - PAD_UP, // Directional pad ↑ - PAD_RIGHT, // Directional pad → - PAD_DOWN, // Directional pad ↓ - PAD_LEFT, // Directional pad ← - PAD_L_UP, // Left joystick (Up) ↑ - PAD_L_RIGHT, // Left joystick (Right) → - PAD_L_DOWN, // Left joystick (Down) ↓ - PAD_L_LEFT, // Left joystick (Left) ← - PAD_R_UP, // Right joystick (Up) ↑ - PAD_R_RIGHT, // Right joystick (Right) → - PAD_R_DOWN, // Right joystick (Down) ↓ - PAD_R_LEFT // Right joystick (Left) ← -}; - -enum PadOptions -{ - PADOPTION_FORCEFEEDBACK = 0x1, - PADOPTION_REVERSELX = 0x2, - PADOPTION_REVERSELY = 0x4, - PADOPTION_REVERSERX = 0x8, - PADOPTION_REVERSERY = 0x10, - PADOPTION_MOUSE_L = 0x20, - PADOPTION_MOUSE_R = 0x40, -}; - -static inline bool IsAnalogKey(int index) -{ - return ((index >= PAD_L_UP) && (index <= PAD_R_LEFT)); -} - -#include "KeyStatus.h" - -void PADLoadConfig(); -void PADSaveConfig(); diff --git a/pcsx2/PAD/Linux/Img/analog-2.png b/pcsx2/PAD/Linux/Img/analog-2.png deleted file mode 100644 index 0263c1ce1666756234301c77d8f0e1b20c758dfe..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 170 zcmeAS@N?(olHy`uVBq!ia0vp^LO{&T!3HFY#P6~IDYhhUcNd2LAh=-f^2tCE&H|6f zVxTHv5N2eUHAey{$X?><>&kwgTSh>LF|f=o5-4Qu>EaloaXvX>)si3o?U|WfXEXwV z;IRcF28IEgJyIr%c#~$HIDcG0QO|K3wOm&K6{tV5@m|`IFUqfkrTRy85}S Ib4q9e0Btfc&Hw-a diff --git a/pcsx2/PAD/Linux/Img/analog.png b/pcsx2/PAD/Linux/Img/analog.png deleted file mode 100644 index dbd8c193139aa09cfa08ff7e476446d645015803..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 137 zcmeAS@N?(olHy`uVBq!ia0vp^0zk~n!3HE}ZR`&LQfx`y?k)`fL2$v|<&%LToCO|{ z#S9GG!XV7ZFl&wkP>{XE)7O>#KDUejr&5%9%SND(n5Ts0Kj#m8nBT~B;E03b cf*TABO|zI-cCM7z1ysS{>FVdQ&MBb@06||QCjbBd diff --git a/pcsx2/PAD/Linux/Img/arrow_bottom.png b/pcsx2/PAD/Linux/Img/arrow_bottom.png deleted file mode 100644 index 8ebc6d553540f1a077af019d88f958506a8a3e13..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 283 zcmeAS@N?(olHy`uVBq!ia0vp^d_b(k!3HGr)n3d2Qk(@Ik;OpqBOuH;Rhv&5D0tS> z#WBR9_wD7~yv+t8tq<=jvT7SRm?iMe;+dc&$)V{R6_&tyt0chCA#8!8x@*o!rDJNb zTL;mp%oeUpTU) z`I?HE_`XFeYp?_}{jlZa+Wzbr`;8y=)k0^aFX&k4man?YaQlMTbm@ehWp=SFr5l^q zDsAw(+m+5>yZFQ*Z-%&S2Yd{wGD@yrT)+N$VglQ{xRULWyI6KUNVWb^)wl3M&AZ2k cd!38#S$%zDm2AM}0rVJyr>mdKI;Vst0QOL6MgRZ+ diff --git a/pcsx2/PAD/Linux/Img/arrow_left.png b/pcsx2/PAD/Linux/Img/arrow_left.png deleted file mode 100644 index 4000cb476f280b9eb26d20b58b7f2e0d824f3d34..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 240 zcmeAS@N?(olHy`uVBq!ia0vp^NAn8f#cW>zV^xTS}HBF@!B}oZqIPt>huL zY3}58Ti%^4w{uS6@P4Qf(`xnY&wa%;Eq)aoT)XcyxLI(XH+NVlzAC0+YXM(Z`%cf; zJwj0rjTk+qF)tANc1YL!0ka_Ap}isrCtNSZ9Y3ZMWy~~H_F8B3udg+q9hm}dwtizZ kiTik4X#&HPCzq=k)l@c1274Aq0A0)A>FVdQ&MBb@0NANkvH$=8 diff --git a/pcsx2/PAD/Linux/Img/arrow_right.png b/pcsx2/PAD/Linux/Img/arrow_right.png deleted file mode 100644 index c391d0025b28a7b4ba2b32dc836a68a663a9a2e6..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 252 zcmeAS@N?(olHy`uVBq!ia0vp^NFdh=fQe5)P(bK7Ybj8u)6>N<#NzbXt9u0*6Gd7dw)<{t z)pcOMH9^C{WsTUv5bmf6r#eg)uyk6^zHw(m&Hb5{59d}s|2OBf2UqTnme?0lLL%B* z+$~xnH7BUW#2av)FW~uH-t}31{-H%|6AnJ#z~!4c;o$ZSyt+P4Q?APXUKtf~I^$L3 tHTjZ?pBI0I31=ub=XAOM+3vrdeM@R_N%(<^B|t|pc)I$ztaD0e0sz`+TnGRF diff --git a/pcsx2/PAD/Linux/Img/arrow_up.png b/pcsx2/PAD/Linux/Img/arrow_up.png deleted file mode 100644 index dc8de6ccde5abf63c3adf61d47ae9799dac10a6d..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 269 zcmeAS@N?(olHy`uVBq!ia0vp^d_b(k!3HGr)n3d2Qk(@Ik;OpqBOuH;Rhv&5D7fF# z#WBR9_wA*Pyv+sztq<=n{K&;~fNhQAWX2VfW{GZM(%!(Nec{r?F2+qvyj7VwC%X)0 z=&`ErJyt$HWiIbTp*<|?BkbGOJb0XR{(zgpbT!e`Cd-D+3f_&S4K=SsEbq)KsK8nOW^dr)i?GDAS#!X7|#A+;Jc3ABb;w|0!g*$?fGp PuP}JJ`njxgN@xNAnVn_J diff --git a/pcsx2/PAD/Linux/Img/circle.png b/pcsx2/PAD/Linux/Img/circle.png deleted file mode 100644 index cabc2681f576b9cce4a5f2aa45e75af973b97ea8..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 916 zcmV;F18e+=P)3#uwS~@1XM7ks(#}x3A)MqUckVLZx#yhwopT8kA&=|p zM1#=?N~?t^2;evlY%+;EE8htmmb`9j6Gb4e_SEGP%Lbe;s9JkT)5R+?e9kGB0=~cH z!cq0vh}SFnW@ZlDv|@K)`(zz7g7j~WBGEeiA?JaH>g!ZvL^1G&VZ`fxzv@qx>28pf zt)O^8bv|a&LfrZa*UUuTN@RSzXlfMugNurd-&%TL(9?OlFR9ZY#s|^j|EOm2gB4Ko zSK#U(syCAI_qtUNDmsgVk%D(}8~`xa*#~o+17T@}A(@ib?j{2`J3pCI2_hHx;^n)j z+G1t%CfFc6pT={ZKJ4vF@1~yYoiuq@5 ziL6Mq&BBQbP5=-{0`%B}qPZl-2LS*&HifXaH)HN*C=37~hn>z0-!I!8ux?60$0g=V zVo~>(3G5mu38=C~bq!^;*?>qy9$>3ZLzgz=!c@srE^N&KIvS<~FOS{u`2YYuK%}xs zXWh=gycz>fk!8sxF_R7zoYbFZF5=!9TgJQs#kj-E*RCA0|JrI*7_>acEI||s>=>n> zF~_;G0L*q>lKY&BTgkT!lQB@cF!mY1feEKjF~?cCua+Cgjc7DuAjQW!WHxK=F5SeI zHUL1pLbB}0=%_LsXXk<7l|fiHlMqM(wf;v%gNNFCVIdm<0NbfWMVcl6LNy4<-_sEQ zAO#U?4yJhX?@=(T(F&Qwfd!Bn*eKhAd+48GJeAZNrhCGVG8 zp)m2!bo}cq1K#F2{B)d4i8Tj_Xtpm-o9SS-@X$JXKC)7LZJI|78CVyZs5bb#=?ghOaf136_~1IM%dJ~4 z3gYE?jhg3vL4%aG6RmkX4x8>@TL2bbz6*9V1M3y;< za1Dn4EvQab0dP+4;f^fP_M!X1LQV(v^~Z~-jlrdP_AHAfoCE-@G9K}Z3;?!u9AjB~ zb5eM1V88X<8eWjFpNO(uPH=!oco8ms18^o#&Aniuqkw^#15}xZZTmI)e9Bq@owHhG zJV8Yl%x+f2#$$d*ceS$PC`;R09Bm2^F5UB4`QTuTm_%DO0N>hiJSp6A!t9BYv^p;7 zY@@Q{NYq~h#l~Z+ZMD&9`Nn^&MHlS-Cgn-zM`EA+tW^ui7%Wy zQqIEeb~5?SGtWF;c;@1!7yp~*TJ~mt{3x^oQL9|XCAkxTQGBC59E@o%KXjK*Y777y z&01EOSCa}5AhfpNmRxJWGx-q*2dJxA&S>%3N{XE)7O>#KDUejKa;sI^M9bwVow*x5Rc<;r&{wd8S=0mKWW&| z%4%gH)E<3g`D32~Hi4~~41p|bH(T&_sfueF)m8cB92DUCRVgNL+V&*ta}iGGgABXN zmho`1Zhca?^MX$FFOg_nA^s2BnI5tFiE=Q+8VB+h1TENFdF_sU{KN`Pznj7u5=W=V zKg+eOc>jI<2d?!W>RZbn3+62Etvdht`kt!wODFdgVpIb(NUyEtkYF41oNlzEY5Rc<;uUc~*au7K7v3}ZF z-YwoWEL+ayvK&0VR;PAB!rMZTCPT|sr(?%j6ap8eZU5AqIjvECb1)A>cbPZKKD}4Z z#8uXp9wQP2l*P_E^+aa#m%Uy-7Ym+wdR@II)qbjaI}_uZcIJ&t zy?yFsjtUG?hb7$w&F>4hww{?JqCMfG?B9j+A2%${Q8=c4N^o(2q#>KFGK1HqGZvpJ z+WX)A6ZiO7+HPRdB_jOvCR^>rUH3A@Pwt3a@AJ4jc=><6Hob{!Uz}cd6X+=hPgg&e IbxsLQ01#h#UjP6A diff --git a/pcsx2/PAD/Linux/Img/dp_right.png b/pcsx2/PAD/Linux/Img/dp_right.png deleted file mode 100644 index 23fa90cbc06b468c1e3d72bf8dca68f027d9b769..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 307 zcmeAS@N?(olHy`uVBq!ia0vp^Qa~)u!3HEnufz)gDYhhUcNd2LAh=-f^2tCE&H|6f zVg?3oVGw3ym^DWND9B#o>FdgVpIb(NPt4@fv%^541D-C9As)xyUUB3)WFWwL!T#VE zmR;FVrOUr~blp3(G^#*d?&PYkV$=F|M;V$d_1RREP?jj?Q^)jWh4(Z==H@DM$<4wR zYH0=+-ubD_U#7&tTw`FUExpOyaD9Hc$5oH)h=sX7TT1>qoSW{XE)7O>#KDUejuXLi{$-O|K`<^b2As)xyUfIugFhJz^$N6Qd zFT2_nF7&$0_jUdIM^~3@>~QIDY+AZ`Ycl&oR-F}jeT~NyoL)+*{8cO8d073s+V?v= z?|i?obuQ|NS7K^<%((Zh9y5c)6mOU9e~(PNoh8U{Ao%K$LZ%ZFyHzCi@%L@oDZ^r* z%Aq9Br*Jg*&*4KK{w!Bte!7+4o|VxcKT1db`eQE6!`X)`9^}Q|-Y~~1D=*Tf;L4)Q z&l(q}Ej!8V?pyjdxkTSnzopr0DWAF761SM diff --git a/pcsx2/PAD/Linux/Img/dualshock2.png b/pcsx2/PAD/Linux/Img/dualshock2.png deleted file mode 100644 index a64cf8edc27017a48c5857f4a4d7af0480ec494b..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 110432 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 diff --git a/pcsx2/PAD/Linux/Img/joystick_cursor.png b/pcsx2/PAD/Linux/Img/joystick_cursor.png deleted file mode 100644 index 84733981029966f47f3f1d34fbd51e1d393bcc72..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 171 zcmeAS@N?(olHy`uVBq!ia0vp^tRT$61|)m))t&+=wj^(N7l!{JxM1({$v_d#0*}aI z1_o|n5N2eUHAey{$X?><>&kwgOOj7tC&|4w3n*ma>Eal|aXs~nA=d!|0p^1T-|yjH z_U%3spTy1+qKzTlN4yk5W-P4fvR;#xncTYEl3njjc}n@edbu4I^2`Oni#UL0FnGH9 KxvX+}I00006VoOIv0RI60 z0RN!9r;`8x010qNS#tmY3ljhU3ljkVnw%H_000McNliru-wPZNFFXB`uT=m513*bc zK~z}7-I&c!6G0fppP8M0VWEXm#6qg&OOUE)j4{!8F!A6;#CY&z^gr-F0B@f3=uJ&b z^rEN-3fV3;{9CvqS=eP5|&%W<4tWw`2 zE=qd5R@$S1iR{#JkFes^@lmwH~Bx%VRsXTi> z{bDGwRy^UueNA7QWjfMojKVIBd;Lf_JrFa~6Hi2C^;vy{l?9f<=p71#`r3@|SZn$i zXuxO9HGJ&lm!?(0_8*E;PfW*p(oi6T7gFHNn;2UbYg-2 zI-pc0_4-H;>Mvfw=Kb~drcMkj2W#x`Q%x$ls2P>ojsJ&k1)K9bJKwq1yWaci;1&Xr z*<$E+s2u_uR7y%jCgXWmCdwMror9gU(O`)Rkuqi`3%hG(5*u9v%I55H^L027f~g2n zT2M;O&9b2AWB?fFMZb9Ns}mvTk+bv6wTZ2(XgmzBA93zP$5gz)jhD<>-5UUiz&VGe z6f_~Qed0&icEHpGzWVF%t||y6Wp<&Q!|8Nkxou@!bD}vlzTmpJ(Y1{*{+FV(sKmO$fR-03w1A0v&&u&wN>#44l%+w}Q0|Ky0*hZftDw0i=*{dqIjoq;lb^ zl)_x1FqRK-&N&$4V4T6hfJmZv=(dgL?@hWl;caCtl$3CLJOv>?SX~bX5ip$}J18pR zxe98joHZ({kZ@JNvMgAc47!FUnO!^Ge<(^jr;}(&j4XP8ux1O)bQ+8?fC$3jKzs7% zg6b&;`?#y|dDKiDs7WO;;i<$AhZ~*x7+Z_3`l>T{ztZhG-ZD0_qEhjn&Y;%R?<9;~ z0DVgx-Lwsi+TY@pp}4=mVqh!=_9@u7=rhl(b&XhD*LOCOvBOUk22mLFHX1DOqCb}< VKGl8vMl$QV!0`U+K2n3=}Jn<_qzkt7h zR3s|F1Cc-=gu)Xb3N0$F?KF0UoF<`3W2b2x+qvux4~`vLU*g(XCuv5qyuQuMerLY( zof#7>u+h;`E?6sa(X;vPmZ`>tx$jC|{ z%J$7)W0oYJ==Ndo+Wd~)gJ{>mT*$%6XM)T+xE2rq@aEH_mVhy^G!NFntb@m29K6Xo zKIu3Z@l!r7C*fPmBeXD$-L{u*#g7Asmh<}M`Ou%1fpPK7i3$J%OY_+4c5XH`T+X-I ziV@pafuC6izc-h(EOGN61G(5TW`CQ({2y~xgov^Z{`TczZV8M;#J^bubC$2-jgLPl zDamJ1_|Et(i}Ob6GFS(H_sX-}w3@IEZZ%-7&9YfFN&Ykmr_+hR+1`#&>8sw8c=z`S zstZOtK(s@X@$qrE-EM?JAvnWY$726*@;SWz<0Y!1$!-7v0M|Zx2b!j#P$+=T^{sa@ z_G=TD@cpZ2`M%1S7z*%{i??x5?Mu%STa|H8fXJ(MOt=D7?>}`&*{Y1)qDZIhZ$}Rz z`NjIunuz!h1Q2n}3!hT^G?6{*f&u`?moo=~B8Figu#&CaV@b6HhH7lI9AHfG2qmx}NIuW9qT+|rW}5JJGevV`v9%8mk&bRUJv46M{``l7@-eg-hI zowJlDi1qa~7={6GNrwo4R1^p);V=yZ7u!3n-qze$?y|u!3>bz1W(LY2N(rT_3;mhd zmV947>_#|Op8+%lQsSC-WboddCQ57rRySyYdl>|_S>2e^1Jg7?0J>DQX%%GVh6)1| zp{O=nQ_l8+^qzR2DpHPM1so|Q8ZD}nvhHW;Ls|)p(4FahyCFW9d)(=PVHmL4?dZ?` zyVVHx1fWW#(U2$#0JKx*i;P3Vg3VFkySd4E(=;)BGgs9ET?siEL?9waAt0oLf1?Ob zA=h>;lkTHfEfgT6gw1Y8K9@tJaJS)p@z7vQ9_hvE>MA0MY{!PQpNE62b-VD^t?R9g z2YLbxDW3EkL;sbT`d~~S@gtKj;`HPa?L~Tg{lWmhJ(JiGUooc#;n+eo7+>`C;r!J! z?WcX?!cZTNP!Yjw%d#G&DSTRYxU*mF!cZT7xR`#Z-<0=@s(HqKxARxiL{lJ|0$ucm znmCmnt8UaDMSK5pssc>P8?5=AUpF4v+uuf&P6atL=&JnP5es{(gyTOBa+`FYB(`e+ O0000? zjU*rm#^=Tjm4yqK$#qv+I?&E^3Ulv0$ys#LHuJmZ|3CLJL%70N?dv`K=ktX$O@pdx zFiiu7VL&}OIhyoISD_%=NR2ZF*v`#~6w8Ifj1SOYfE9Gz5$I)^i+FGV787yT zL9KFmc1Jk}IEIsG96u6@+W;r%O4tLp_@xl#4Hu`xb&Epo1x+nIE0 zz;`Ayo&>H;S3T_&IlJZax}M(7WViu#r9kUrqn?+vrN`3_J)R8)xCAcG<*_KU?P;ET zY}enGo-XJ*_rSIamkkVXE$j37COXLK%3B0k*OPgbycfbD;eY{PX(+)ith9(#wyJiy z($9N)garnGg;0>yK>K@oMP^2q-(M?lk)?fJ;tBvfyWvUr^v6%GG%Ht6LmwU-5CKF0EykP; zz}7?xMO`n05u9otyNN-`*J{Y6`$t=1FWGr_Wg&J+vGnq=Zu9gWRTJT0000< KMNUMnLSTX*rIb1V diff --git a/pcsx2/PAD/Linux/Img/r1.png b/pcsx2/PAD/Linux/Img/r1.png deleted file mode 100644 index 7f162ba21a4278f079731a362b861eedd68773b1..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 949 zcmV;m14{gfP)+}I00006VoOIv0RI60 z0RN!9r;`8x010qNS#tmY3ljhU3ljkVnw%H_000McNliru-wPZNC>Hk&W#<3@142ne zK~z}7-IvdA8$}$&KQlYK4$j6lb!?+xOjBq|OG~McDnJ~1DWZUADndgREBBT~b^ zz!f322q95brS7E%B#<~DR8bN^l^;#5P^)sXDt7wA^^e{44u@3EZ_+r90~<1+t+?$tK-;#qbuI!^Leo@+4FDg)yIoh z!&Bj%kI2})PW-zsGhq>3i?CRBx#+M}D7<%)9bcS5Y?p*hb~$7xi2}H;ix6T{>@EQ= z9(q}KYC+d^0hl;)f~$w$? zShG>DBMpPN6lCvL9%_;4{sBA1VfKP5o&smOAu=X);(YQLz*JY^UxVzdV&_ ze{|{aL9KKA>+{QtnIHboC8&KHJF_!us<`vY%?A!}x~KS_dwow^#qyqnKD{s7Rf*#t X)!aa{0L0(@00000NkvXXu0mjfk08Oq diff --git a/pcsx2/PAD/Linux/Img/r2.png b/pcsx2/PAD/Linux/Img/r2.png deleted file mode 100644 index 62ff3b217b8c855f772307e36a3d5fa848c80114..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 1166 zcmV;91abR`P)n3`qp_QJR!E@P}RC@`gRS%K0~YAJ2c@9{+`<+S0kvd1Gu0o6SbC?*jt^ES>SEWw6+!ue{Mt-BY)bNqeu|h9e2%(yJEA)9H|D z0Es}v?DFjaTx)s>zLkgg_~sbPDvT?q+o}70b|#++#8x0%&2D-g@Beo9z;omKak}_& zGm!~ICOi@l2els8UBu$O&}pW$!1(3O7l=%t4`xTx9`M4D8*?{i;s0wD+TSW;U_NnA zfJ`9!YgRfGj9=e6MPvfKJ9#T(K~{BEgTDR>05Ja9rRzu%9kv%Szh z|1zcjPLWL2pUTJtO1PR_J<~<+j16X0_;Rug zFjQf*)R+@6V#_Ue3td77>%ZerO63HMx96PEeTMJ9fE_?jBBxp{gbBkjm_2i8z4=Ue zH3D2f(=?E#ML#K8twaRQq5`jN^lfc(#y~*y(Vz3ptKs1SbSiSFc#1;7_|lq8Hvqt) zH=xDuIn+&@hGU3?!_Z`A5+H}@QcWhm0?u^tRUQwsTog*cH0 zQFT_-BD|p~O4A@Tway93688VG(|1Y~oLm#KTyc@lH#A2G0U;$C7iZE7!`V?s=B3%B zj`yCb+Lj#$G5gak@UM8+`y8_c+4`4%IAx}eh#x1G=MN6UVW}ibno|B!T@yaMagQCv zQs{4Opwldf`ek==7<=hP%H^%18rP`+y?3VB%0Z(%o g^|#aob7k)T1Chn4HQn|roW&$)N*ogpw#3+^$>3jzc| z0M8337G=otSv>jmo51W!i6Y&Yj!_D99PeUxXwvJU<^c7H^oQH62(($`5(%30xO;B^ zmM6lRf;NjxDA;}YbTD8DY;B}`qAGaB&4U76A9bSe`8lQ8?S-<^BI|9nITP=So!iBS znHhb+weHHAgU@OV-pjDn@i7|J27EgjrDlPq0Nqq;_04$PRKVp}zdfZ4JRR_x61W@- zEmm=)m*-)<=5fxdzyOd-BreDC#y@UqTi4m#Jx1Hw+bUUZW%CR6&W1yl0|tOa(My|V z+KO3jlZU$AY!XdU4%J&!Gn#4i=a( zbZxj|Zx*awZE@zHlOA{Hdt_#p&29&`Iy!R15_Df*Ke$4Fzn3Lwp69^{g20r$@9yk? zv)S#el{L7(g#vrfvMe(N{L|$ES1cBpf-aRx?5)TShKJc(oP4=rV{`4z0l@oMjIHhs z0H^sp6R^42*$VXIqa(rs^lAQ2<$ao!y1uYo1OQ7u(bB-LZ@Q}CD`IZn8vs@!6P5%{ z`^Res;G&p2%L0JqaL7`?Dbd?#5b9;6Y&SZXZ@Bh;?ijQza7!8Wk*i*@H11R8)?6ZC zO5ly@n7Z;b8}Qa#`|nK1Wx=gf$DA$=p$poMM)i5lt+~10{4_Ei*Vl<}CnG%toDK%4 zf)eOluUt3i)8i9GgLYq%>aMM>tr;^9cT&@}dUkw52tZaM5!Jx0t~XOQ6jW8es&+%? z_xA|_$okYRLz>mfWCR2t4-dYnYFL_V0ZY4kga9NP3hMM~=m!DF{MUU=y0fN7mem=^ zgo1S7>L6tKAMSnn-0{6K0Y4snKRmnN;i1-Zxp9&tscENuu(xa2|Cav&_=JI<>&kwgTSh=cRQ8+iBA}3!r;B5V#`(9G3`H3fc#d3BG42D MPgg&ebxsLQ004q8?f?J) diff --git a/pcsx2/PAD/Linux/Img/square.png b/pcsx2/PAD/Linux/Img/square.png deleted file mode 100644 index bcbed6d51b24a07e25a439541534e3f93450c7d2..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 538 zcmV+#0_FXQP)4s$;M_0`U3#q=HLj! z@WBmxV_#U>-N8z}fX`5nQWb->C6N3D2LOpiFxogQYzC^e=go?NRJ$N_Bj7{=v#W~+ zt72hqLkQ@3KihQ|dB44H-USYijUP^olcxl>UtT=}*Wwa+dIYP5;sC_JKLIbn^WiYK zCdE7>a0>!Y7Io$o0ku?SgAoIlirjbLY&6nXlr|?MGn2jJ8#?QL70(Y)*K0-AbW|YuPgg~ZW#e#OD=p&Q(pXEWO?u;qr*X|s%zTLOcGeG6Y3)_ ex-e@K6N6H3rMOzb@=ZX8FnGH9xvX`VMV>r zSz|5vr@PHt5F{ zP(nw)(dbg6OCu_%fRdzQA>ES5!Lbf+xOP+3J|O_ZM#ES<1x;6P;3zP0-e#xi>W#QS zoTa9i?$O3H_m`=-$OrTEC_sO{5jlY&YbRYB6;PtNMN;GI1NJW5+AIL3wK=%8Ssbu; zsd`6Y0A7}rjf*^-XSu~fYf)XhwR|>a6o}lu^7?&VuU-#;-n+=OHYdp2^~=r3xyGa- zL`pWy-mG2+fX=tX)Tw$lcz>hwErs!tafJlXzoAIT^yQrnd@JDhdS(EqU-%N7`_NF{ z=@0_ZtuOf+bb-k2&s=Herfw4erSeZEnh%EY&A0lP%*X}c^xP*pFIk0LAql77HTvMZ zW??#lAG#JQn-GMvq-4ByX2udv=y2;T9_DWZV8`z{-kxoUDz~$vn|Ikd01o~5%!8KZ z$Plip3kM)#{x!D;@km-S6hBKR_QjRWyfcKXk;wCVS!miH_i&-VpsM{I9%(sYR{nA) zG-*J_BWbB>znA)FG_n%&`F@$_)?*13KDQnt-!F&lRSlq`qX(~MIqa%Ev%(K6AnVm= zsOacX2f%7;V=TK60E4_pZ5 zkzqJgY8u}=>9YJ=X!I1A0ND2DJMYgNQB!TPz0<*mtO5Xzes|G*@pKsfNvY{$)J&-B zdd&Q)3;-JECs@0Z>yxE#t1`J?Xa=CrCo^}lCL$9?0H|)e%hSAK(m{JXvkad<9+`?? zSS%j{i?4EvNEe0b9^SI~b67o~fYM^KGqLiaq>Ie*Pg*Zuh+Z}STkEH5^qI|HRaZp+ zgFpwS@g!sz4I;h(ZmdS`Z=)swAo?Fb1l2kH(T4 gsM. - */ - -#include "InputManager.h" -#include "Device.h" -#include "keyboard.h" -#include "state_management.h" - -#ifdef SDL_BUILD -#include "SDL/joystick.h" -#endif - -InputDeviceManager device_manager; - -// Needs to be moved to individual device code, as does the keyboard input. -void PollForJoystickInput(int cpad) -{ - int index = Device::uid_to_index(cpad); - if (index < 0) - return; - - auto& gamePad = device_manager.devices[index]; - - gamePad->UpdateDeviceState(); - - for (u32 i = 0; i < MAX_KEYS; i++) - { - s32 value = gamePad->GetInput((gamePadValues)i); - if (value != 0) - g_key_status.press(cpad, i, value); - else - g_key_status.release(cpad, i); - } -} - -void InputDeviceManager::Update() -{ - // Poll keyboard/mouse event. There is currently no way to separate pad0 from pad1 event. - // So we will populate both pad in the same time - for (u32 cpad = 0; cpad < GAMEPAD_NUMBER; cpad++) - { - g_key_status.keyboard_state_acces(cpad); - } - UpdateKeyboardInput(); - - // Get joystick state + Commit - for (u32 cpad = 0; cpad < GAMEPAD_NUMBER; cpad++) - { - g_key_status.joystick_state_acces(cpad); - - PollForJoystickInput(cpad); - - g_key_status.commit_status(cpad); - } - - Pad::rumble_all(); -} - -/* - * Find and set up joysticks, potentially other devices. - */ -void EnumerateDevices() -{ -#ifdef SDL_BUILD - JoystickInfo::EnumerateJoysticks(device_manager.devices); -#endif -} diff --git a/pcsx2/PAD/Linux/InputManager.h b/pcsx2/PAD/Linux/InputManager.h deleted file mode 100644 index 4373a4f864..0000000000 --- a/pcsx2/PAD/Linux/InputManager.h +++ /dev/null @@ -1,36 +0,0 @@ -/* PCSX2 - PS2 Emulator for PCs - * Copyright (C) 2002-2021 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 . - */ - -#pragma once - -#include -#include - -class Device; - -class InputDeviceManager -{ -public: - void Update(); - - std::vector> devices; -}; - -extern InputDeviceManager device_manager; - -/* - * Find every interesting device and create right structure for them(depends on backend) - */ -extern void EnumerateDevices(); diff --git a/pcsx2/PAD/Linux/KeyStatus.cpp b/pcsx2/PAD/Linux/KeyStatus.cpp deleted file mode 100644 index 5bb92f4fcb..0000000000 --- a/pcsx2/PAD/Linux/KeyStatus.cpp +++ /dev/null @@ -1,204 +0,0 @@ -/* PCSX2 - PS2 Emulator for PCs - * Copyright (C) 2002-2020 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 . - */ - -#include "KeyStatus.h" -#include "Config.h" - -void KeyStatus::Init() -{ - for (u32 pad = 0; pad < GAMEPAD_NUMBER; pad++) - { - m_button[pad] = 0xFFFF; - m_internal_button_kbd[pad] = 0xFFFF; - m_internal_button_joy[pad] = 0xFFFF; - m_state_acces[pad] = false; - - for (u32 index = 0; index < MAX_KEYS; index++) - { - m_button_pressure[pad][index] = 0xFF; - m_internal_button_pressure[pad][index] = 0xFF; - } - - m_analog[pad].lx = m_analog_released_val; - m_analog[pad].ly = m_analog_released_val; - m_analog[pad].rx = m_analog_released_val; - m_analog[pad].ry = m_analog_released_val; - m_internal_analog_kbd[pad].lx = m_analog_released_val; - m_internal_analog_kbd[pad].ly = m_analog_released_val; - m_internal_analog_kbd[pad].rx = m_analog_released_val; - m_internal_analog_kbd[pad].ry = m_analog_released_val; - m_internal_analog_joy[pad].lx = m_analog_released_val; - m_internal_analog_joy[pad].ly = m_analog_released_val; - m_internal_analog_joy[pad].rx = m_analog_released_val; - m_internal_analog_joy[pad].ry = m_analog_released_val; - } -} - -void KeyStatus::press(u32 pad, u32 index, s32 value) -{ - if (!IsAnalogKey(index)) - { - m_internal_button_pressure[pad][index] = value; - if (m_state_acces[pad]) - clear_bit(m_internal_button_kbd[pad], index); - else - clear_bit(m_internal_button_joy[pad], index); - } - else - { - // clamp value - if (value > MAX_ANALOG_VALUE) - value = MAX_ANALOG_VALUE; - else if (value < -MAX_ANALOG_VALUE) - value = -MAX_ANALOG_VALUE; - - // Left -> -- -> Right - // Value range : FFFF8002 -> 0 -> 7FFE - // Force range : 80 -> 0 -> 7F - // Normal mode : expect value 0 -> 80 -> FF - // Reverse mode: expect value FF -> 7F -> 0 - u8 force = (value >> 8); - if (analog_is_reversed(pad, index)) - analog_set(pad, index, m_analog_released_val - force); - else - analog_set(pad, index, m_analog_released_val + force + 1); - } -} - -void KeyStatus::release(u32 pad, u32 index) -{ - if (!IsAnalogKey(index)) - { - if (m_state_acces[pad]) - set_bit(m_internal_button_kbd[pad], index); - else - set_bit(m_internal_button_joy[pad], index); - } - else - { - analog_set(pad, index, m_analog_released_val); - } -} - -u16 KeyStatus::get(u32 pad) -{ - return m_button[pad]; -} - -void KeyStatus::analog_set(u32 pad, u32 index, u8 value) -{ - PADAnalog* m_internal_analog_ref; - if (m_state_acces[pad]) - m_internal_analog_ref = &m_internal_analog_kbd[pad]; - else - m_internal_analog_ref = &m_internal_analog_joy[pad]; - - switch (index) - { - case PAD_R_LEFT: - case PAD_R_RIGHT: - m_internal_analog_ref->rx = value; - break; - - case PAD_R_DOWN: - case PAD_R_UP: - m_internal_analog_ref->ry = value; - break; - - case PAD_L_LEFT: - case PAD_L_RIGHT: - m_internal_analog_ref->lx = value; - break; - - case PAD_L_DOWN: - case PAD_L_UP: - m_internal_analog_ref->ly = value; - break; - - default: - break; - } -} - -bool KeyStatus::analog_is_reversed(u32 pad, u32 index) -{ - switch (index) - { - case PAD_L_RIGHT: - case PAD_L_LEFT: - return (g_conf.pad_options[pad].reverse_lx); - - case PAD_R_LEFT: - case PAD_R_RIGHT: - return (g_conf.pad_options[pad].reverse_rx); - - case PAD_L_UP: - case PAD_L_DOWN: - return (g_conf.pad_options[pad].reverse_ly); - - case PAD_R_DOWN: - case PAD_R_UP: - return (g_conf.pad_options[pad].reverse_ry); - - default: - return false; - } -} - -u8 KeyStatus::get(u32 pad, u32 index) -{ - switch (index) - { - case PAD_R_LEFT: - case PAD_R_RIGHT: - return m_analog[pad].rx; - - case PAD_R_DOWN: - case PAD_R_UP: - return m_analog[pad].ry; - - case PAD_L_LEFT: - case PAD_L_RIGHT: - return m_analog[pad].lx; - - case PAD_L_DOWN: - case PAD_L_UP: - return m_analog[pad].ly; - - default: - return m_button_pressure[pad][index]; - } -} - -u8 KeyStatus::analog_merge(u8 kbd, u8 joy) -{ - if (kbd != m_analog_released_val) - return kbd; - else - return joy; -} - -void KeyStatus::commit_status(u32 pad) -{ - m_button[pad] = m_internal_button_kbd[pad] & m_internal_button_joy[pad]; - - for (u32 index = 0; index < MAX_KEYS; index++) - m_button_pressure[pad][index] = m_internal_button_pressure[pad][index]; - - m_analog[pad].lx = analog_merge(m_internal_analog_kbd[pad].lx, m_internal_analog_joy[pad].lx); - m_analog[pad].ly = analog_merge(m_internal_analog_kbd[pad].ly, m_internal_analog_joy[pad].ly); - m_analog[pad].rx = analog_merge(m_internal_analog_kbd[pad].rx, m_internal_analog_joy[pad].rx); - m_analog[pad].ry = analog_merge(m_internal_analog_kbd[pad].ry, m_internal_analog_joy[pad].ry); -} diff --git a/pcsx2/PAD/Linux/KeyStatus.h b/pcsx2/PAD/Linux/KeyStatus.h deleted file mode 100644 index 2893beb558..0000000000 --- a/pcsx2/PAD/Linux/KeyStatus.h +++ /dev/null @@ -1,71 +0,0 @@ -/* PCSX2 - PS2 Emulator for PCs - * Copyright (C) 2002-2020 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 . - */ - -#pragma once - -#include "Global.h" - -typedef struct -{ - u8 lx, ly; - u8 rx, ry; -} PADAnalog; - -#define MAX_ANALOG_VALUE 32766 - -class KeyStatus -{ -private: - const u8 m_analog_released_val; - - u16 m_button[GAMEPAD_NUMBER]; - u16 m_internal_button_kbd[GAMEPAD_NUMBER]; - u16 m_internal_button_joy[GAMEPAD_NUMBER]; - - u8 m_button_pressure[GAMEPAD_NUMBER][MAX_KEYS]; - u8 m_internal_button_pressure[GAMEPAD_NUMBER][MAX_KEYS]; - - bool m_state_acces[GAMEPAD_NUMBER]; - - PADAnalog m_analog[GAMEPAD_NUMBER]; - PADAnalog m_internal_analog_kbd[GAMEPAD_NUMBER]; - PADAnalog m_internal_analog_joy[GAMEPAD_NUMBER]; - - void analog_set(u32 pad, u32 index, u8 value); - bool analog_is_reversed(u32 pad, u32 index); - u8 analog_merge(u8 kbd, u8 joy); - -public: - KeyStatus() - : m_analog_released_val(0x7F) - { - Init(); - } - void Init(); - - void keyboard_state_acces(u32 pad) { m_state_acces[pad] = true; } - void joystick_state_acces(u32 pad) { m_state_acces[pad] = false; } - - void press(u32 pad, u32 index, s32 value = 0xFF); - void release(u32 pad, u32 index); - - u16 get(u32 pad); - u8 get(u32 pad, u32 index); - - - void commit_status(u32 pad); -}; - -extern KeyStatus g_key_status; diff --git a/pcsx2/PAD/Linux/PAD.cpp b/pcsx2/PAD/Linux/PAD.cpp deleted file mode 100644 index 496766c455..0000000000 --- a/pcsx2/PAD/Linux/PAD.cpp +++ /dev/null @@ -1,294 +0,0 @@ -/* PCSX2 - PS2 Emulator for PCs - * Copyright (C) 2002-2020 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 . - */ - -#include -#include -#include -#include - -// NOTE: Must come first because of the None enum -#include "common/WindowInfo.h" - -#include "keyboard.h" -#include "../Gamepad.h" -#include "state_management.h" - -#if defined(__unix__) || defined(__APPLE__) -#include "Device.h" -#endif - -#ifdef __linux__ -#include -#endif - -#include "gui/AppCoreThread.h" -#include "wx_dialog/dialog.h" - -#ifndef __APPLE__ -Display* GSdsp = nullptr; -Window GSwin; -#endif - -const u32 revision = 3; -const u32 build = 0; // increase that with each version -#define PAD_SAVE_STATE_VERSION ((revision << 8) | (build << 0)) - -PADconf g_conf; -HostKeyEvent event; - -static HostKeyEvent s_event; -std::string s_padstrLogPath("logs/"); -#ifndef PCSX2_CORE -// PADclose is called from the core thread but PADupdate is called from the main thread -// I kind of hate this solution but it only needs to be here until we switch to Qt so whatever -static std::mutex s_pad_lock; -#endif - -KeyStatus g_key_status; - -MtQueue g_ev_fifo; - -s32 PADinit() -{ - PADLoadConfig(); - - Pad::reset_all(); - - query.reset(); - - for (int port = 0; port < 2; port++) - slots[port] = 0; - - return 0; -} - -void PADshutdown() -{ -} - -s32 PADopen(const WindowInfo& wi) -{ - memset(&event, 0, sizeof(event)); - g_key_status.Init(); - - g_ev_fifo.reset(); - -#if defined(__unix__) || defined(__APPLE__) - EnumerateDevices(); -#endif -#ifndef __APPLE__ - if (wi.type != WindowInfo::Type::X11) - return -1; - - GSdsp = static_cast(wi.display_connection); - GSwin = reinterpret_cast(wi.window_handle); -#endif - - return 0; -} - -void PADclose() -{ -#ifndef PCSX2_CORE - std::lock_guard guard(s_pad_lock); -#endif - device_manager.devices.clear(); -} - -s32 PADsetSlot(u8 port, u8 slot) -{ - port--; - slot--; - if (port > 1 || slot > 3) - { - return 0; - } - // Even if no pad there, record the slot, as it is the active slot regardless. - slots[port] = slot; - - return 1; -} - -s32 PADfreeze(FreezeAction mode, freezeData* data) -{ - if (!data) - return -1; - - if (mode == FreezeAction::Size) - { - data->size = sizeof(PadFullFreezeData); - } - else if (mode == FreezeAction::Load) - { - PadFullFreezeData* pdata = (PadFullFreezeData*)(data->data); - - Pad::stop_vibrate_all(); - - if (data->size != sizeof(PadFullFreezeData) || pdata->version != PAD_SAVE_STATE_VERSION || - strncmp(pdata->format, "LinPad", sizeof(pdata->format))) - return 0; - - query = pdata->query; - if (pdata->query.slot < 4) - { - query = pdata->query; - } - - // Tales of the Abyss - pad fix - // - restore data for both ports - for (int port = 0; port < 2; port++) - { - for (int slot = 0; slot < 4; slot++) - { - u8 mode = pdata->padData[port][slot].mode; - - if (mode != MODE_DIGITAL && mode != MODE_ANALOG && mode != MODE_DS2_NATIVE) - { - break; - } - - memcpy(&pads[port][slot], &pdata->padData[port][slot], sizeof(PadFreezeData)); - } - - if (pdata->slot[port] < 4) - slots[port] = pdata->slot[port]; - } - } - else if (mode == FreezeAction::Save) - { - if (data->size != sizeof(PadFullFreezeData)) - return 0; - - PadFullFreezeData* pdata = (PadFullFreezeData*)(data->data); - - // Tales of the Abyss - pad fix - // - PCSX2 only saves port0 (save #1), then port1 (save #2) - - memset(pdata, 0, data->size); - strncpy(pdata->format, "LinPad", sizeof(pdata->format)); - pdata->version = PAD_SAVE_STATE_VERSION; - pdata->query = query; - - for (int port = 0; port < 2; port++) - { - for (int slot = 0; slot < 4; slot++) - { - pdata->padData[port][slot] = pads[port][slot]; - } - - pdata->slot[port] = slots[port]; - } - } - else - { - return -1; - } - - return 0; -} - -u8 PADstartPoll(int port, int slot) -{ - return pad_start_poll(port, slot); -} - -u8 PADpoll(u8 value) -{ - return pad_poll(value); -} - -bool PADcomplete() -{ - return pad_complete(); -} - -// PADkeyEvent is called every vsync (return NULL if no event) -HostKeyEvent* PADkeyEvent() -{ -#ifdef SDL_BUILD - // Take the opportunity to handle hot plugging here - SDL_Event events; - while (SDL_PollEvent(&events)) - { - switch (events.type) - { - case SDL_CONTROLLERDEVICEADDED: - case SDL_CONTROLLERDEVICEREMOVED: - EnumerateDevices(); - break; - default: - break; - } - } -#endif -#if defined(__unix__) || defined(__APPLE__) - if (g_ev_fifo.size() == 0) - { - //PAD_LOG("No events in queue, returning empty event"); - s_event = event; - event.type = HostKeyEvent::Type::NoEvent; - event.key = 0; - return &s_event; - } - s_event = g_ev_fifo.dequeue(); - - AnalyzeKeyEvent(s_event); - //PAD_LOG("Returning Event. Event Type: %d, Key: %d", s_event.type, s_event.key); - return &s_event; -#endif -} - -#if defined(__unix__) || defined(__APPLE__) -void PADWriteEvent(HostKeyEvent& evt) -{ - // if (evt.evt != 6) { // Skip mouse move events for logging - // PAD_LOG("Pushing Event. Event Type: %d, Key: %d", evt.type, evt.key); - // } - g_ev_fifo.push(evt); -} -#endif - -void PADupdate(int pad) -{ -#ifndef __APPLE__ - // Gamepad inputs don't count as an activity. Therefore screensaver will - // be fired after a couple of minute. - // Emulate an user activity - static int count = 0; - count++; - if (GSdsp && (count & 0xFFF) == 0) - { - // 1 call every 4096 Vsync is enough - XResetScreenSaver(GSdsp); - } -#endif - -#ifndef PCSX2_CORE - std::lock_guard guard(s_pad_lock); -#endif - // Actually PADupdate is always call with pad == 0. So you need to update both - // pads -- Gregory - device_manager.Update(); -} - -void PADconfigure() -{ - ScopedCoreThreadPause paused_core; - PADLoadConfig(); - - DisplayDialog(); - paused_core.AllowResume(); - return; -} diff --git a/pcsx2/PAD/Linux/SDL/joystick.cpp b/pcsx2/PAD/Linux/SDL/joystick.cpp deleted file mode 100644 index 17f93ba174..0000000000 --- a/pcsx2/PAD/Linux/SDL/joystick.cpp +++ /dev/null @@ -1,261 +0,0 @@ -/* PCSX2 - PS2 Emulator for PCs - * Copyright (C) 2002-2020 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 . - */ - -#include "joystick.h" -#include "Host.h" -#include // sigaction - -#if !SDL_VERSION_ATLEAST(2, 0, 9) - #error PCSX2 requires SDL2 2.0.9 or higher -#endif - -////////////////////////// -// Joystick definitions // -////////////////////////// - -// opens handles to all possible joysticks -void JoystickInfo::EnumerateJoysticks(std::vector>& vjoysticks) -{ - uint32_t flag = SDL_INIT_JOYSTICK | SDL_INIT_HAPTIC | SDL_INIT_EVENTS | SDL_INIT_GAMECONTROLLER; - - if ((SDL_WasInit(0) & flag) != flag) - { - // Tell SDL to catch event even if the windows isn't focussed - SDL_SetHint(SDL_HINT_JOYSTICK_ALLOW_BACKGROUND_EVENTS, "1"); - // Enable rumble on PS4 controllers (note: breaks DirectInput handling) - SDL_SetHint(SDL_HINT_JOYSTICK_HIDAPI_PS4_RUMBLE, "1"); - // We want buttons to come in as positions, not labels, where possible - // New as of SDL 2.0.12, so use string - SDL_SetHint("SDL_GAMECONTROLLER_USE_BUTTON_LABELS", "0"); - // Super annoying to have a bright blue LED shining in your face all the time - // New as of SDL 2.0.18, so use string - SDL_SetHint("SDL_JOYSTICK_HIDAPI_SWITCH_HOME_LED", "0"); - // Enable Wii U Pro Controller support - // New as of SDL 2.26, so use string - SDL_SetHint("SDL_JOYSTICK_HIDAPI_WII", "1"); - - for (const auto& hint : g_conf.sdl2_hints) - SDL_SetHint(hint.first.c_str(), hint.second.c_str()); - - if (SDL_Init(flag) < 0) - return; - - // WTF! Give me back the control of my system - struct sigaction action = {}; - action.sa_handler = SIG_DFL; - sigaction(SIGINT, &action, nullptr); - sigaction(SIGTERM, &action, nullptr); - - SDL_JoystickEventState(SDL_QUERY); - SDL_GameControllerEventState(SDL_QUERY); - SDL_EventState(SDL_CONTROLLERDEVICEADDED, SDL_ENABLE); - SDL_EventState(SDL_CONTROLLERDEVICEREMOVED, SDL_ENABLE); - - { // Support as much Joystick as possible - auto file = Host::ReadResourceFile("game_controller_db.txt"); - if (file.has_value()) - SDL_GameControllerAddMappingsFromRW(SDL_RWFromMem(file->data(), file->size()), 1); - else - Console.Warning("Failed to load SDL Game Controller DB file!"); - - // Add user mapping too - for (auto const& map : g_conf.sdl2_mapping) - SDL_GameControllerAddMapping(map.c_str()); - } - } - - vjoysticks.clear(); - - for (int i = 0; i < SDL_NumJoysticks(); ++i) - { - vjoysticks.push_back(std::unique_ptr(new JoystickInfo(i))); - // Something goes wrong in the init, let's drop it - if (!vjoysticks.back()->IsProperlyInitialized()) - vjoysticks.pop_back(); - } -} - -void JoystickInfo::Rumble(unsigned type, unsigned pad) -{ - if (type >= std::size(m_rumble_end)) - return; - - if (!(g_conf.pad_options[pad].forcefeedback)) - return; - - m_rumble_enabled[type] = true; - m_rumble_end[type] = SDL_GetTicks() + 125; // 125ms feels quite near to original - UpdateRumble(true); -} - -void JoystickInfo::UpdateRumble(bool needs_update) -{ - u16 rumble_amt[2] = {0, 0}; - u32 rumble_time = 0; - - u32 now = SDL_GetTicks(); - for (size_t i = 0; i < std::size(m_rumble_end); i++) - { - s32 remaining = static_cast(m_rumble_end[i] - now); - if (m_rumble_enabled[i]) - { - if (remaining > 0) - { - rumble_amt[i] = std::min(g_conf.get_ff_intensity() * UINT16_MAX, UINT16_MAX); - rumble_time = std::max(rumble_time, static_cast(remaining)); - } - else - { - m_rumble_enabled[i] = false; - needs_update = true; - } - } - } - - if (needs_update) - SDL_GameControllerRumble(m_controller, rumble_amt[1], rumble_amt[0], rumble_time); -} - -JoystickInfo::~JoystickInfo() -{ - if (m_controller != nullptr) - { - SDL_GameControllerClose(m_controller); - } -} - -JoystickInfo::JoystickInfo(int id) - : Device() - , m_controller(nullptr) - , m_unique_id(0) -{ - SDL_Joystick* joy = nullptr; - // Values are hardcoded currently but it could be later extended to allow remapping of the buttons - m_pad_to_sdl[PAD_L2] = SDL_CONTROLLER_AXIS_TRIGGERLEFT; - m_pad_to_sdl[PAD_R2] = SDL_CONTROLLER_AXIS_TRIGGERRIGHT; - m_pad_to_sdl[PAD_L1] = SDL_CONTROLLER_BUTTON_LEFTSHOULDER; - m_pad_to_sdl[PAD_R1] = SDL_CONTROLLER_BUTTON_RIGHTSHOULDER; - m_pad_to_sdl[PAD_TRIANGLE] = SDL_CONTROLLER_BUTTON_Y; - m_pad_to_sdl[PAD_CIRCLE] = SDL_CONTROLLER_BUTTON_B; - m_pad_to_sdl[PAD_CROSS] = SDL_CONTROLLER_BUTTON_A; - m_pad_to_sdl[PAD_SQUARE] = SDL_CONTROLLER_BUTTON_X; - m_pad_to_sdl[PAD_SELECT] = SDL_CONTROLLER_BUTTON_BACK; - m_pad_to_sdl[PAD_L3] = SDL_CONTROLLER_BUTTON_LEFTSTICK; - m_pad_to_sdl[PAD_R3] = SDL_CONTROLLER_BUTTON_RIGHTSTICK; - m_pad_to_sdl[PAD_START] = SDL_CONTROLLER_BUTTON_START; - m_pad_to_sdl[PAD_UP] = SDL_CONTROLLER_BUTTON_DPAD_UP; - m_pad_to_sdl[PAD_RIGHT] = SDL_CONTROLLER_BUTTON_DPAD_RIGHT; - m_pad_to_sdl[PAD_DOWN] = SDL_CONTROLLER_BUTTON_DPAD_DOWN; - m_pad_to_sdl[PAD_LEFT] = SDL_CONTROLLER_BUTTON_DPAD_LEFT; - m_pad_to_sdl[PAD_L_UP] = SDL_CONTROLLER_AXIS_LEFTY; - m_pad_to_sdl[PAD_L_RIGHT] = SDL_CONTROLLER_AXIS_LEFTX; - m_pad_to_sdl[PAD_L_DOWN] = SDL_CONTROLLER_AXIS_LEFTY; - m_pad_to_sdl[PAD_L_LEFT] = SDL_CONTROLLER_AXIS_LEFTX; - m_pad_to_sdl[PAD_R_UP] = SDL_CONTROLLER_AXIS_RIGHTY; - m_pad_to_sdl[PAD_R_RIGHT] = SDL_CONTROLLER_AXIS_RIGHTX; - m_pad_to_sdl[PAD_R_DOWN] = SDL_CONTROLLER_AXIS_RIGHTY; - m_pad_to_sdl[PAD_R_LEFT] = SDL_CONTROLLER_AXIS_RIGHTX; - - if (SDL_IsGameController(id)) - { - m_controller = SDL_GameControllerOpen(id); - joy = SDL_GameControllerGetJoystick(m_controller); - } - else - { - joy = SDL_JoystickOpen(id); - } - - if (joy == nullptr) - { - Console.Warning("PAD: failed to open joystick %d", id); - return; - } - - // Collect Device Information - char guid[64]; - SDL_JoystickGetGUIDString(SDL_JoystickGetGUID(joy), guid, 64); - m_device_name = SDL_JoystickNameForIndex(id); - - if (m_controller == nullptr) - { - Console.Warning("PAD: Joystick (%s,GUID:%s) isn't yet supported by the SDL2 game controller API\n" - "You can use SDL2 Gamepad Tool (https://www.generalarcade.com/gamepadtool/) or Steam to configure your joystick\n" - "The mapping can be stored in PAD.ini as 'SDL2 = <...mapping description...>'\n" - "Please post the new generated mapping to (https://github.com/gabomdq/SDL_GameControllerDB) so it can be added to the database.", - m_device_name.c_str(), guid); - - SDL_JoystickClose(joy); - - return; - } - - std::hash hash_me; - m_unique_id = hash_me(std::string(guid)); - - bool rumble_support = SDL_GameControllerRumble(m_controller, 0, 0, 1) >= 0; - - Console.WriteLn("PAD: controller (%s) detected%s, GUID:%s", - m_device_name.c_str(), rumble_support ? " with rumble support" : "", guid); - - m_no_error = true; -} - -const char* JoystickInfo::GetName() -{ - return SDL_JoystickName(SDL_GameControllerGetJoystick(m_controller)); -} - -size_t JoystickInfo::GetUniqueIdentifier() -{ - return m_unique_id; -} - -bool JoystickInfo::TestForce(float strength = 0.60) -{ - u16 u16strength = static_cast(UINT16_MAX * strength); - - return SDL_GameControllerRumble(m_controller, u16strength, u16strength, 400) >= 0; -} - -int JoystickInfo::GetInput(gamePadValues input) -{ - float k = g_conf.get_sensibility() / 100.0; // convert sensibility to float - - // Handle analog inputs which range from -32k to +32k. Range conversion is handled later in the controller - if (IsAnalogKey(input)) - { - int value = SDL_GameControllerGetAxis(m_controller, (SDL_GameControllerAxis)m_pad_to_sdl[input]); - value *= k; - return (abs(value) > m_deadzone) ? value : 0; - } - - // Handle triggers which range from 0 to +32k. They must be converted to 0-255 range - if (input == PAD_L2 || input == PAD_R2) - { - int value = SDL_GameControllerGetAxis(m_controller, (SDL_GameControllerAxis)m_pad_to_sdl[input]); - return (value > m_deadzone) ? value / 128 : 0; - } - - // Remain buttons - int value = SDL_GameControllerGetButton(m_controller, (SDL_GameControllerButton)m_pad_to_sdl[input]); - return value ? 0xFF : 0; // Max pressure -} - -void JoystickInfo::UpdateDeviceState() -{ - UpdateRumble(false); - SDL_GameControllerUpdate(); -} diff --git a/pcsx2/PAD/Linux/SDL/joystick.h b/pcsx2/PAD/Linux/SDL/joystick.h deleted file mode 100644 index 6755d9bd2c..0000000000 --- a/pcsx2/PAD/Linux/SDL/joystick.h +++ /dev/null @@ -1,59 +0,0 @@ -/* PCSX2 - PS2 Emulator for PCs - * Copyright (C) 2002-2020 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 . - */ - -#pragma once - -#include -#include - -#include "../Global.h" -#include "../Device.h" - -#define NB_EFFECT 2 // Don't use more than two, ps2 only has one for big motor and one for small(like most systems) - -// holds all joystick info -class JoystickInfo : public Device -{ -public: - JoystickInfo(int id); - ~JoystickInfo(); - - JoystickInfo(const JoystickInfo&) = delete; // copy constructor - JoystickInfo& operator=(const JoystickInfo&) = delete; // assignment - - - // opens handles to all possible joysticks - static void EnumerateJoysticks(std::vector>& vjoysticks); - - void Rumble(unsigned type, unsigned pad) override; - void UpdateRumble(bool needs_update); - - bool TestForce(float) override; - - const char* GetName() final; - - int GetInput(gamePadValues input) final; - - void UpdateDeviceState() final; - - size_t GetUniqueIdentifier() final; - -private: - SDL_GameController* m_controller; - u32 m_rumble_end[NB_EFFECT] = {}; - bool m_rumble_enabled[NB_EFFECT] = {}; - size_t m_unique_id; - std::array m_pad_to_sdl; -}; diff --git a/pcsx2/PAD/Linux/bitwise.h b/pcsx2/PAD/Linux/bitwise.h deleted file mode 100644 index a9a48169b7..0000000000 --- a/pcsx2/PAD/Linux/bitwise.h +++ /dev/null @@ -1,40 +0,0 @@ -/* PCSX2 - PS2 Emulator for PCs - * Copyright (C) 2002-2020 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 . - */ - -#pragma once - -template -static void __forceinline set_bit(T& value, int bit) -{ - value |= (1 << bit); -} - -template -static void __forceinline clear_bit(T& value, int bit) -{ - value &= ~(1 << bit); -} - -template -static void __forceinline toggle_bit(T& value, int bit) -{ - value ^= (1 << bit); -} - -template -static bool __forceinline test_bit(T& value, int bit) -{ - return (value & (1 << bit)); -} diff --git a/pcsx2/PAD/Linux/keyboard.cpp b/pcsx2/PAD/Linux/keyboard.cpp deleted file mode 100644 index ce3c4baf15..0000000000 --- a/pcsx2/PAD/Linux/keyboard.cpp +++ /dev/null @@ -1,318 +0,0 @@ -/* PCSX2 - PS2 Emulator for PCs - * Copyright (C) 2002-2020 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 . - */ - -/* - * Theoretically, this header is for anything to do with keyboard input. - * Pragmatically, event handing's going in here too. - */ - -#include "Global.h" -#include "keyboard.h" -#include "../Gamepad.h" - -extern HostKeyEvent event; -extern MtQueue g_ev_fifo; - -/// g_key_status.press but with proper handling for analog buttons -static void PressButton(u32 pad, u32 button) -{ - // Analog controls. - if (IsAnalogKey(button)) - { - switch (button) - { - case PAD_R_LEFT: - case PAD_R_UP: - case PAD_L_LEFT: - case PAD_L_UP: - g_key_status.press(pad, button, -MAX_ANALOG_VALUE); - break; - case PAD_R_RIGHT: - case PAD_R_DOWN: - case PAD_L_RIGHT: - case PAD_L_DOWN: - g_key_status.press(pad, button, MAX_ANALOG_VALUE); - break; - } - } - else - { - g_key_status.press(pad, button); - } -} - -#if defined(__APPLE__) -// Mac keyboard input code is based on Dolphin's Source/Core/InputCommon/ControllerInterface/Quartz/QuartzKeyboardAndMouse.mm -// Copyright 2016 Dolphin Emulator Project -// Licensed under GPLv2+ - -// All keycodes are 16 bits or less, so we use top 16 bits to differentiate source -// Keyboard keys use discriminator 0 -// Mouse buttons use discriminator 1 - -void UpdateKeyboardInput() -{ - g_ev_fifo.consume_all(AnalyzeKeyEvent); -} - -bool PollForNewKeyboardKeys(u32& pkey) -{ - // All keycodes in are 0x7e or lower. If you notice - // keys that aren't being recognized, bump this number up! - for (int key = 0; key < 0x80; key++) - { - if (CGEventSourceKeyState(kCGEventSourceStateHIDSystemState, key)) - { - pkey = key == kVK_Escape ? UINT32_MAX : key; - return true; - } - } -#define CHECK(button, value) \ - if (CGEventSourceButtonState(kCGEventSourceStateHIDSystemState, (button))) \ - { \ - pkey = (value); \ - return true; \ - } - CHECK(kCGMouseButtonLeft, 0x10001) - CHECK(kCGMouseButtonCenter, 0x10002) - CHECK(kCGMouseButtonRight, 0x10003) -#undef CHECK - return false; -} -#elif defined(__unix__) -static bool s_grab_input = false; -static bool s_Shift = false; - -void UpdateKeyboardInput() -{ - HostKeyEvent evt = {}; - XEvent E = {0}; - - // Keyboard input send by PCSX2 - g_ev_fifo.consume_all(AnalyzeKeyEvent); - - // keyboard input - if (!GSdsp) - return; - - while (XPending(GSdsp) > 0) - { - XNextEvent(GSdsp, &E); - - // Change the format of the structure to be compatible with GSOpen2 - // mode (event come from pcsx2 not X) - evt.type = static_cast(E.type); - switch (E.type) - { - case MotionNotify: - evt.key = (E.xbutton.x & 0xFFFF) | (E.xbutton.y << 16); - evt.type = HostKeyEvent::Type::MouseMove; - break; - case ButtonRelease: - evt.key = E.xbutton.button | 0x10000; - evt.type = HostKeyEvent::Type::MouseReleased; - break; - case ButtonPress: - evt.key = E.xbutton.button | 0x10000; - evt.type = HostKeyEvent::Type::MousePressed; - break; - case KeyPress: - evt.key = (int)XLookupKeysym(&E.xkey, 0); - evt.type = HostKeyEvent::Type::KeyPressed; - break; - case KeyRelease: - evt.key = (int)XLookupKeysym(&E.xkey, 0); - evt.type = HostKeyEvent::Type::KeyReleased; - default: - continue; - } - - AnalyzeKeyEvent(evt); - } -} - -bool PollForNewKeyboardKeys(u32& pkey) -{ - GdkEvent* ev = gdk_event_get(); - - if (ev != NULL) - { - if (ev->type == GDK_KEY_PRESS) - { - pkey = ev->key.keyval != GDK_KEY_Escape ? ev->key.keyval : UINT32_MAX; - return true; - } - else if (ev->type == GDK_BUTTON_PRESS) - { - pkey = ev->button.button | 0x10000; - return true; - } - } - - return false; -} -#endif - -static unsigned int s_previous_mouse_x = 0; -static unsigned int s_previous_mouse_y = 0; - -void AnalyzeKeyEvent(HostKeyEvent& evt) -{ - int pad = 0; - int index = -1; - - for (u32 cpad = 0; cpad < GAMEPAD_NUMBER; cpad++) - { - int tmp_index = get_keyboard_key(cpad, evt.key); - if (tmp_index != -1) - { - pad = cpad; - index = tmp_index; - } - } - - switch (evt.type) - { - case HostKeyEvent::Type::KeyPressed: - // Shift F12 is not yet use by pcsx2. So keep it to grab/ungrab input - // I found it very handy vs the automatic fullscreen detection - // 1/ Does not need to detect full-screen - // 2/ Can use a debugger in full-screen - // 3/ Can grab input in window without the need of a pixelated full-screen -#ifdef __unix__ - if (evt.key == XK_Shift_R || evt.key == XK_Shift_L) - s_Shift = true; - if (evt.key == XK_F12 && s_Shift && GSdsp) - { - if (!s_grab_input) - { - s_grab_input = true; - XGrabPointer(GSdsp, GSwin, True, ButtonPressMask, GrabModeAsync, GrabModeAsync, GSwin, None, CurrentTime); - XGrabKeyboard(GSdsp, GSwin, True, GrabModeAsync, GrabModeAsync, CurrentTime); - } - else - { - s_grab_input = false; - XUngrabPointer(GSdsp, CurrentTime); - XUngrabKeyboard(GSdsp, CurrentTime); - } - } -#endif - - if (index != -1) - PressButton(pad, index); - - //PAD_LOG("Key pressed:%d", index); - - event.type = HostKeyEvent::Type::KeyPressed; - event.key = evt.key; - break; - - case HostKeyEvent::Type::KeyReleased: -#ifdef __unix__ - if (evt.key == XK_Shift_R || evt.key == XK_Shift_L) - s_Shift = false; -#endif - - if (index != -1) - g_key_status.release(pad, index); - - event.type = HostKeyEvent::Type::KeyReleased; - event.key = evt.key; - break; - - case HostKeyEvent::Type::FocusGained: - break; - - case HostKeyEvent::Type::FocustLost: -#ifdef __unix__ - s_Shift = false; -#endif - break; - - case HostKeyEvent::Type::MousePressed: - if (index != -1) - g_key_status.press(pad, index); - break; - - case HostKeyEvent::Type::MouseReleased: - if (index != -1) - g_key_status.release(pad, index); - break; - - case HostKeyEvent::Type::MouseMove: - // FIXME: How to handle when the mouse does not move, no event generated!!! - // 1/ small move == no move. Cons : can not do small movement - // 2/ use a watchdog timer thread - // 3/ ??? idea welcome ;) - if (g_conf.pad_options[pad].mouse_l | g_conf.pad_options[pad].mouse_r) - { - unsigned int pad_x; - unsigned int pad_y; - // Note when both PADOPTION_MOUSE_R and PADOPTION_MOUSE_L are set, take only the right one - if (g_conf.pad_options[pad].mouse_r) - { - pad_x = PAD_R_RIGHT; - pad_y = PAD_R_UP; - } - else - { - pad_x = PAD_L_RIGHT; - pad_y = PAD_L_UP; - } - - unsigned x = evt.key & 0xFFFF; - unsigned int value = (s_previous_mouse_x > x) ? s_previous_mouse_x - x : x - s_previous_mouse_x; - value *= g_conf.get_sensibility(); - - if (x == 0) - g_key_status.press(pad, pad_x, -MAX_ANALOG_VALUE); - else if (x == 0xFFFF) - g_key_status.press(pad, pad_x, MAX_ANALOG_VALUE); - else if (x < (s_previous_mouse_x - 2)) - g_key_status.press(pad, pad_x, -value); - else if (x > (s_previous_mouse_x + 2)) - g_key_status.press(pad, pad_x, value); - else - g_key_status.release(pad, pad_x); - - - unsigned y = evt.key >> 16; - value = (s_previous_mouse_y > y) ? s_previous_mouse_y - y : y - s_previous_mouse_y; - value *= g_conf.get_sensibility(); - - if (y == 0) - g_key_status.press(pad, pad_y, -MAX_ANALOG_VALUE); - else if (y == 0xFFFF) - g_key_status.press(pad, pad_y, MAX_ANALOG_VALUE); - else if (y < (s_previous_mouse_y - 2)) - g_key_status.press(pad, pad_y, -value); - else if (y > (s_previous_mouse_y + 2)) - g_key_status.press(pad, pad_y, value); - else - g_key_status.release(pad, pad_y); - - s_previous_mouse_x = x; - s_previous_mouse_y = y; - } - - break; - - case HostKeyEvent::Type::NoEvent: - case HostKeyEvent::Type::MouseWheelDown: - case HostKeyEvent::Type::MouseWheelUp: - break; - } -} diff --git a/pcsx2/PAD/Linux/keyboard.h b/pcsx2/PAD/Linux/keyboard.h deleted file mode 100644 index 3896a08bb0..0000000000 --- a/pcsx2/PAD/Linux/keyboard.h +++ /dev/null @@ -1,46 +0,0 @@ -/* PCSX2 - PS2 Emulator for PCs - * Copyright (C) 2002-2020 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 . - */ - -#pragma once - -#include "common/Pcsx2Defs.h" -#include "Host.h" - -#if defined(__unix__) || defined(__APPLE__) - -#ifndef __APPLE__ -#include -#include -#include - -// x11 is dumb like that -#undef DisableScreenSaver -#endif - -extern void AnalyzeKeyEvent(HostKeyEvent& evt); -extern void UpdateKeyboardInput(); -extern bool PollForNewKeyboardKeys(u32& pkey); -#endif - -#if defined(__unix__) -#include -#include - -extern Display* GSdsp; -extern Window GSwin; - -#elif defined(__APPLE__) -#include -#endif diff --git a/pcsx2/PAD/Linux/state_management.cpp b/pcsx2/PAD/Linux/state_management.cpp deleted file mode 100644 index 8cfc47c7a4..0000000000 --- a/pcsx2/PAD/Linux/state_management.cpp +++ /dev/null @@ -1,484 +0,0 @@ -/* PCSX2 - PS2 Emulator for PCs - * Copyright (C) 2002-2020 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 . - */ - -#include "state_management.h" -#include "Device.h" - -// Typical packet response on the bus -static const u8 ConfigExit[7] = {0x5A, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00}; -static const u8 noclue[7] = {0x5A, 0x00, 0x00, 0x02, 0x00, 0x00, 0x5A}; -static const u8 setMode[7] = {0x5A, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00}; -static const u8 queryModelDS2[7] = {0x5A, 0x03, 0x02, 0x00, 0x02, 0x01, 0x00}; -static const u8 queryModelDS1[7] = {0x5A, 0x01, 0x02, 0x00, 0x02, 0x01, 0x00}; -static const u8 queryComb[7] = {0x5A, 0x00, 0x00, 0x02, 0x00, 0x01, 0x00}; -static const u8 queryMode[7] = {0x5A, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00}; -static const u8 setNativeMode[7] = {0x5A, 0x00, 0x00, 0x00, 0x00, 0x00, 0x5A}; - -static u8 queryMaskMode[7] = {0x5A, 0xFF, 0xFF, 0x03, 0x00, 0x00, 0x5A}; - -static const u8 queryAct[2][7] = { - {0x5A, 0x00, 0x00, 0x01, 0x02, 0x00, 0x0A}, - {0x5A, 0x00, 0x00, 0x01, 0x01, 0x01, 0x14}}; - -QueryInfo query; -Pad pads[2][4]; -int slots[2] = {0, 0}; - -////////////////////////////////////////////////////////////////////// -// QueryInfo implementation -////////////////////////////////////////////////////////////////////// - -void QueryInfo::reset() -{ - port = 0; - slot = 0; - lastByte = 1; - currentCommand = 0; - numBytes = 0; - queryDone = 1; - memset(response, 0xF3, sizeof(response)); -} - -u8 QueryInfo::start_poll(int _port, int _slot) -{ - if (port > 1) - { - reset(); - return 0; - } - - queryDone = 0; - port = _port; - slot = _slot; - numBytes = 2; - lastByte = 0; - - return 0xFF; -} - -////////////////////////////////////////////////////////////////////// -// Pad implementation -////////////////////////////////////////////////////////////////////// - -void Pad::set_mode(int _mode) -{ - mode = _mode; -} - -void Pad::set_vibrate(int motor, u8 val) -{ - nextVibrate[motor] = val; -} - -void Pad::reset_vibrate() -{ - set_vibrate(0, 0); - set_vibrate(1, 0); - memset(vibrate, 0xFF, sizeof(vibrate)); - vibrate[0] = 0x5A; -} - -void Pad::reset() -{ - memset(this, 0, sizeof(PadFreezeData)); - - set_mode(MODE_DIGITAL); - umask[0] = 0xFF; - umask[1] = 0xFF; - umask[2] = 0x03; - - // Sets up vibrate variable. - reset_vibrate(); -} - -void Pad::rumble(unsigned port) -{ - for (unsigned motor = 0; motor < 2; motor++) - { - // TODO: Probably be better to send all of these at once. - if (nextVibrate[motor] | currentVibrate[motor]) - { - currentVibrate[motor] = nextVibrate[motor]; - - Device::DoRumble(motor, port); - } - } -} - -void Pad::stop_vibrate_all() -{ -#if 0 - for (int i=0; i<8; i++) { - SetVibrate(i&1, i>>1, 0, 0); - SetVibrate(i&1, i>>1, 1, 0); - } -#endif - // FIXME equivalent ? - for (int port = 0; port < 2; port++) - for (int slot = 0; slot < 4; slot++) - pads[port][slot].reset_vibrate(); -} - -void Pad::reset_all() -{ - for (int port = 0; port < 2; port++) - for (int slot = 0; slot < 4; slot++) - pads[port][slot].reset(); -} - -void Pad::rumble_all() -{ - for (unsigned port = 0; port < 2; port++) - for (unsigned slot = 0; slot < 4; slot++) - pads[port][slot].rumble(port); -} - -////////////////////////////////////////////////////////////////////// -// Pad implementation -////////////////////////////////////////////////////////////////////// - -inline bool IsDualshock2() -{ -// FIXME -#if 0 - return config.padConfigs[query.port][query.slot].type == Dualshock2Pad || - (config.padConfigs[query.port][query.slot].type == GuitarPad && config.GH2); -#else - return true; -#endif -} - -u8 pad_start_poll(u8 port, u8 slot) -{ - return query.start_poll(port, slot); -} - -bool pad_complete() -{ - return query.queryDone; -} - -u8 pad_poll(u8 value) -{ - if (query.lastByte + 1 >= query.numBytes) - { - return 0; - } - if (query.lastByte && query.queryDone) - { - return query.response[++query.lastByte]; - } - - Pad* pad = &pads[query.port][query.slot]; - - if (query.lastByte == 0) - { - query.lastByte++; - query.currentCommand = value; - - switch (value) - { - case CMD_CONFIG_MODE: - if (pad->config) - { - // In config mode. Might not actually be leaving it. - query.set_result(ConfigExit); - return 0xF3; - } - [[fallthrough]]; // fallthrough on purpose (but I don't know why) - - case CMD_READ_DATA_AND_VIBRATE: - { - query.response[2] = 0x5A; -#if 0 - int i; - Update(query.port, query.slot); - ButtonSum *sum = &pad->sum; - - u8 b1 = 0xFF, b2 = 0xFF; - for (i = 0; i<4; i++) { - b1 -= (sum->buttons[i] > 0) << i; - } - for (i = 0; i<8; i++) { - b2 -= (sum->buttons[i+4] > 0) << i; - } -#endif - -// FIXME -#if 0 - if (config.padConfigs[query.port][query.slot].type == GuitarPad && !config.GH2) { - sum->buttons[15] = 255; - // Not sure about this. Forces wammy to be from 0 to 0x7F. - // if (sum->sticks[2].vert > 0) sum->sticks[2].vert = 0; - } -#endif - -#if 0 - for (i = 4; i<8; i++) { - b1 -= (sum->buttons[i+8] > 0) << i; - } -#endif - -// FIXME -#if 0 - //Left, Right and Down are always pressed on Pop'n Music controller. - if (config.padConfigs[query.port][query.slot].type == PopnPad) - b1=b1 & 0x1f; -#endif - - uint16_t buttons = g_key_status.get(query.port); - - query.numBytes = 5; - - query.response[3] = (buttons >> 8) & 0xFF; - query.response[4] = (buttons >> 0) & 0xFF; - - if (pad->mode != MODE_DIGITAL) - { // ANALOG || DS2 native - query.numBytes = 9; - - query.response[5] = g_key_status.get(query.port, PAD_R_RIGHT); - query.response[6] = g_key_status.get(query.port, PAD_R_UP); - query.response[7] = g_key_status.get(query.port, PAD_L_RIGHT); - query.response[8] = g_key_status.get(query.port, PAD_L_UP); - - if (pad->mode != MODE_ANALOG) - { // DS2 native - query.numBytes = 21; - - query.response[9] = !test_bit(buttons, 13) ? g_key_status.get(query.port, PAD_RIGHT) : 0; - query.response[10] = !test_bit(buttons, 15) ? g_key_status.get(query.port, PAD_LEFT) : 0; - query.response[11] = !test_bit(buttons, 12) ? g_key_status.get(query.port, PAD_UP) : 0; - query.response[12] = !test_bit(buttons, 14) ? g_key_status.get(query.port, PAD_DOWN) : 0; - - query.response[13] = !test_bit(buttons, 4) ? g_key_status.get(query.port, PAD_TRIANGLE) : 0; - query.response[14] = !test_bit(buttons, 5) ? g_key_status.get(query.port, PAD_CIRCLE) : 0; - query.response[15] = !test_bit(buttons, 6) ? g_key_status.get(query.port, PAD_CROSS) : 0; - query.response[16] = !test_bit(buttons, 7) ? g_key_status.get(query.port, PAD_SQUARE) : 0; - query.response[17] = !test_bit(buttons, 2) ? g_key_status.get(query.port, PAD_L1) : 0; - query.response[18] = !test_bit(buttons, 3) ? g_key_status.get(query.port, PAD_R1) : 0; - query.response[19] = !test_bit(buttons, 0) ? g_key_status.get(query.port, PAD_L2) : 0; - query.response[20] = !test_bit(buttons, 1) ? g_key_status.get(query.port, PAD_R2) : 0; - } - } - -#if 0 - query.response[3] = b1; - query.response[4] = b2; - - query.numBytes = 5; - if (pad->mode != MODE_DIGITAL) { - query.response[5] = Cap((sum->sticks[0].horiz+255)/2); - query.response[6] = Cap((sum->sticks[0].vert+255)/2); - query.response[7] = Cap((sum->sticks[1].horiz+255)/2); - query.response[8] = Cap((sum->sticks[1].vert+255)/2); - - query.numBytes = 9; - if (pad->mode != MODE_ANALOG) { - // Good idea? No clue. - //query.response[3] &= pad->mask[0]; - //query.response[4] &= pad->mask[1]; - - // No need to cap these, already done int CapSum(). - query.response[9] = (unsigned char)sum->buttons[13]; //D-pad right - query.response[10] = (unsigned char)sum->buttons[15]; //D-pad left - query.response[11] = (unsigned char)sum->buttons[12]; //D-pad up - query.response[12] = (unsigned char)sum->buttons[14]; //D-pad down - - query.response[13] = (unsigned char) sum->buttons[8]; - query.response[14] = (unsigned char) sum->buttons[9]; - query.response[15] = (unsigned char) sum->buttons[10]; - query.response[16] = (unsigned char) sum->buttons[11]; - query.response[17] = (unsigned char) sum->buttons[6]; - query.response[18] = (unsigned char) sum->buttons[7]; - query.response[19] = (unsigned char) sum->buttons[4]; - query.response[20] = (unsigned char) sum->buttons[5]; - query.numBytes = 21; - } - } -#endif - } - - query.lastByte = 1; - return pad->mode; - - case CMD_SET_VREF_PARAM: - query.set_final_result(noclue); - break; - - case CMD_QUERY_DS2_ANALOG_MODE: - // Right? Wrong? No clue. - if (pad->mode == MODE_DIGITAL) - { - queryMaskMode[1] = queryMaskMode[2] = queryMaskMode[3] = 0; - queryMaskMode[6] = 0x00; - } - else - { - queryMaskMode[1] = pad->umask[0]; - queryMaskMode[2] = pad->umask[1]; - queryMaskMode[3] = pad->umask[2]; - // Not entirely sure about this. - //queryMaskMode[3] = 0x01 | (pad->mode == MODE_DS2_NATIVE)*2; - queryMaskMode[6] = 0x5A; - } - query.set_final_result(queryMaskMode); - break; - - case CMD_SET_MODE_AND_LOCK: - query.set_result(setMode); - pad->reset_vibrate(); - break; - - case CMD_QUERY_MODEL_AND_MODE: - if (IsDualshock2()) - { - query.set_final_result(queryModelDS2); - } - else - { - query.set_final_result(queryModelDS1); - } - // Not digital mode. - query.response[5] = (pad->mode & 0xF) != 1; - break; - - case CMD_QUERY_ACT: - query.set_result(queryAct[0]); - break; - - case CMD_QUERY_COMB: - query.set_final_result(queryComb); - break; - - case CMD_QUERY_MODE: - query.set_result(queryMode); - break; - - case CMD_VIBRATION_TOGGLE: - memcpy(query.response + 2, pad->vibrate, 7); - query.numBytes = 9; - //query.set_result(pad->vibrate); // warning copy 7b not 8 (but it is really important?) - pad->reset_vibrate(); - break; - - case CMD_SET_DS2_NATIVE_MODE: - if (IsDualshock2()) - { - query.set_result(setNativeMode); - } - else - { - query.set_final_result(setNativeMode); - } - break; - - default: - query.numBytes = 0; - query.queryDone = 1; - break; - } - - return 0xF3; - } - else - { - query.lastByte++; - - switch (query.currentCommand) - { - case CMD_READ_DATA_AND_VIBRATE: - if (query.lastByte == pad->vibrateI[0]) - pad->set_vibrate(1, 255 * (value & 1)); - else if (query.lastByte == pad->vibrateI[1]) - pad->set_vibrate(0, value); - - break; - - case CMD_CONFIG_MODE: - if (query.lastByte == 3) - { - query.queryDone = 1; - pad->config = value; - } - break; - - case CMD_SET_MODE_AND_LOCK: - if (query.lastByte == 3 && value < 2) - { - pad->set_mode(value ? MODE_ANALOG : MODE_DIGITAL); - } - else if (query.lastByte == 4) - { - if (value == 3) - pad->modeLock = 3; - else - pad->modeLock = 0; - - query.queryDone = 1; - } - break; - - case CMD_QUERY_ACT: - if (query.lastByte == 3) - { - if (value < 2) - query.set_result(queryAct[value]); - // bunch of 0's - // else query.set_result(setMode); - query.queryDone = 1; - } - break; - - case CMD_QUERY_MODE: - if (query.lastByte == 3 && value < 2) - { - query.response[6] = 4 + value * 3; - query.queryDone = 1; - } - // bunch of 0's - //else data = setMode; - break; - - case CMD_VIBRATION_TOGGLE: - if (query.lastByte >= 3) - { - if (value == 0) - { - pad->vibrateI[0] = (u8)query.lastByte; - } - else if (value == 1) - { - pad->vibrateI[1] = (u8)query.lastByte; - } - pad->vibrate[query.lastByte - 2] = value; - } - break; - - case CMD_SET_DS2_NATIVE_MODE: - if (query.lastByte > 2 && query.lastByte < 6) - { - pad->umask[query.lastByte - 3] = value; - } - pad->set_mode(MODE_DS2_NATIVE); - break; - - default: - return 0; - } - - return query.response[query.lastByte]; - } -} diff --git a/pcsx2/PAD/Linux/state_management.h b/pcsx2/PAD/Linux/state_management.h deleted file mode 100644 index 005c554d37..0000000000 --- a/pcsx2/PAD/Linux/state_management.h +++ /dev/null @@ -1,134 +0,0 @@ -/* PCSX2 - PS2 Emulator for PCs - * Copyright (C) 2002-2020 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 . - */ - -#pragma once - -#include "Global.h" - -#define MODE_DIGITAL 0x41 -#define MODE_ANALOG 0x73 -#define MODE_DS2_NATIVE 0x79 - -enum PadCommands -{ - CMD_SET_VREF_PARAM = 0x40, - CMD_QUERY_DS2_ANALOG_MODE = 0x41, - CMD_READ_DATA_AND_VIBRATE = 0x42, - CMD_CONFIG_MODE = 0x43, - CMD_SET_MODE_AND_LOCK = 0x44, - CMD_QUERY_MODEL_AND_MODE = 0x45, - CMD_QUERY_ACT = 0x46, // ?? - CMD_QUERY_COMB = 0x47, // ?? - CMD_QUERY_MODE = 0x4C, // QUERY_MODE ?? - CMD_VIBRATION_TOGGLE = 0x4D, - CMD_SET_DS2_NATIVE_MODE = 0x4F // SET_DS2_NATIVE_MODE -}; - -// The state of the PS2 bus -struct QueryInfo -{ - u8 port; - u8 slot; - u8 lastByte; - u8 currentCommand; - u8 numBytes; - u8 queryDone; - u8 response[42]; - - void reset(); - u8 start_poll(int port, int slot); - - template - void set_result(const u8 (&rsp)[S]) - { - memcpy(response + 2, rsp, S); - numBytes = 2 + S; - } - - template - void set_final_result(const u8 (&rsp)[S]) - { - set_result(rsp); - queryDone = 1; - } -}; - -// Freeze data, for a single pad. Basically has all pad state that -// a PS2 can set. -struct PadFreezeData -{ - // Digital / Analog / DS2 Native - u8 mode; - - u8 modeLock; - - // In config mode - u8 config; - - u8 vibrate[8]; - u8 umask[3]; - - // Vibration indices. - u8 vibrateI[2]; - - // Last vibration value sent to controller. - // Only used so as not to call vibration - // functions when old and new values are both 0. - u8 currentVibrate[2]; - - // Next vibrate val to send to controller. If next and current are - // both 0, nothing is sent to the controller. Otherwise, it's sent - // on every update. - u8 nextVibrate[2]; -}; - -class Pad : public PadFreezeData -{ -public: - // Lilypad store here the state of PC pad - - void rumble(unsigned port); - void set_vibrate(int motor, u8 val); - void reset_vibrate(); - void reset(); - - void set_mode(int mode); - - static void reset_all(); - static void stop_vibrate_all(); - static void rumble_all(); -}; - -// Full state to manage save state -struct PadFullFreezeData -{ - char format[8]; - u32 version; - // active slot for port - u8 slot[2]; - PadFreezeData padData[2][4]; - QueryInfo query; - // unused padding data, present only so that data size - // matches Windows version for cross-platform save/load - char padding[8]; -}; - -extern QueryInfo query; -extern Pad pads[2][4]; -extern int slots[2]; - -extern u8 pad_start_poll(u8 port, u8 slot); -extern u8 pad_poll(u8 value); -extern bool pad_complete(); diff --git a/pcsx2/PAD/Linux/wx_dialog/GamepadConfiguration.cpp b/pcsx2/PAD/Linux/wx_dialog/GamepadConfiguration.cpp deleted file mode 100644 index d32d0a6b1c..0000000000 --- a/pcsx2/PAD/Linux/wx_dialog/GamepadConfiguration.cpp +++ /dev/null @@ -1,187 +0,0 @@ -/* PCSX2 - PS2 Emulator for PCs - * Copyright (C) 2002-2020 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 . - */ - -#include "GamepadConfiguration.h" - -GamepadConfiguration::GamepadConfiguration(int pad, wxWindow* parent) - : wxDialog(parent, wxID_ANY, _T("Gamepad"), wxDefaultPosition, wxDefaultSize, - wxSYSTEM_MENU | wxCAPTION | wxCLOSE_BOX | wxCLIP_CHILDREN) - , m_pad_id(pad) - -{ - wxBoxSizer* gamepad_box = new wxBoxSizer(wxVERTICAL); - - wxArrayString choices; - for (const auto& j : device_manager.devices) - { - choices.Add(j->GetName()); - } - - m_joy_map = new wxChoice(this, wxID_ANY, wxDefaultPosition, wxDefaultSize, choices); - m_cb_rumble = new wxCheckBox(this, enable_rumble_id, _T("&Enable rumble")); - - wxStaticBoxSizer* rumble_box = new wxStaticBoxSizer(wxVERTICAL, this, wxT("Rumble intensity")); - m_sl_rumble_intensity = new wxSlider(this, rumble_slider_id, 0, 0, 0x7FFF, wxDefaultPosition, wxDefaultSize, - wxSL_HORIZONTAL | wxSL_LABELS | wxSL_BOTTOM); - - wxStaticBoxSizer* joy_box = new wxStaticBoxSizer(wxVERTICAL, this, wxT("Joystick sensibility")); - m_sl_joystick_sensibility = new wxSlider(this, joy_slider_id, 0, 0, 200, wxDefaultPosition, wxDefaultSize, - wxSL_HORIZONTAL | wxSL_LABELS | wxSL_BOTTOM); - - gamepad_box->Add(m_joy_map, wxSizerFlags().Expand().Border(wxALL, 5)); - gamepad_box->Add(m_cb_rumble, wxSizerFlags().Expand()); - - rumble_box->Add(m_sl_rumble_intensity, wxSizerFlags().Expand().Border(wxALL, 5)); - joy_box->Add(m_sl_joystick_sensibility, wxSizerFlags().Expand().Border(wxALL, 5)); - - gamepad_box->Add(rumble_box, wxSizerFlags().Expand().Border(wxALL, 5)); - gamepad_box->Add(joy_box, wxSizerFlags().Expand().Border(wxALL, 5)); - - Bind(wxEVT_UPDATE_UI, &GamepadConfiguration::OnUpdateEvent, this); - Bind(wxEVT_SCROLL_THUMBRELEASE, &GamepadConfiguration::OnSliderReleased, this); - Bind(wxEVT_CHECKBOX, &GamepadConfiguration::OnCheckboxChange, this); - Bind(wxEVT_CHOICE, &GamepadConfiguration::OnChoiceChange, this); - - repopulate(); - - SetSizerAndFit(gamepad_box); -} - -/** - * Initialize the frame - * Check if a gamepad is detected - * Check if the gamepad support rumbles - */ -void GamepadConfiguration::InitGamepadConfiguration() -{ - repopulate(); // Set label and fit simulated key array - /* - * Check if there exist at least one pad available - * if the pad id is 0, you need at least 1 gamepad connected, - * if the pad id is 1, you need at least 2 gamepads connected, - * Prevent to use a non-initialized value (core dump) - */ - if (device_manager.devices.size() >= m_pad_id + 1) - { - /* - * Determine if the device can use rumble - * Use TestForce with a very low strength (can't be felt) - * May be better to create a new function in order to check only that - */ - - // Bad idea. Some connected devices might support rumble but not all connected devices. - // if (!device_manager.devices[m_pad_id]->TestForce(0.001f)) { - // wxMessageBox(L"Rumble is not available for your device."); - // m_cb_rumble->Disable(); // disable the rumble checkbox - // m_sl_rumble_intensity->Disable(); // disable the rumble intensity slider - // } - } - else - { - wxMessageBox(L"No gamepad detected."); - m_sl_joystick_sensibility->Disable(); // disable the joystick sensibility slider - m_cb_rumble->Disable(); // disable the rumble checkbox - m_sl_rumble_intensity->Disable(); // disable the rumble intensity slider - } -} - -void GamepadConfiguration::OnUpdateEvent(wxCommandEvent& event) -{ - // Makes sure joystick rumble testing works properly - SDL_GameControllerUpdate(); -} - -/** - * Slider event, called when the use release the slider button - * @FIXME The current solution can't change the joystick sensibility and the rumble intensity - * for a specific gamepad. The same value is used for both - */ -void GamepadConfiguration::OnSliderReleased(wxCommandEvent& event) -{ - wxSlider* sl_tmp = (wxSlider*)event.GetEventObject(); - int sl_id = sl_tmp->GetId(); - if (!sl_tmp->IsEnabled()) // wxCocoa sends events even when the button is disabled - return; - - if (sl_id == rumble_slider_id) - { - float value = static_cast(m_sl_rumble_intensity->GetValue()) / 0x7FFF; - g_conf.set_ff_intensity(value); - - // convert in a float value between 0 and 1, and run rumble feedback. - // 0 to 1 scales to 0x0 to 0x7FFF - device_manager.devices[m_pad_id]->TestForce(value); - } - else if (sl_id == joy_slider_id) - { - g_conf.set_sensibility(m_sl_joystick_sensibility->GetValue()); - } -} - -/** - * Checkbox event, called when the value of the checkbox change - */ -void GamepadConfiguration::OnCheckboxChange(wxCommandEvent& event) -{ - wxCheckBox* cb_tmp = (wxCheckBox*)event.GetEventObject(); // get the slider object - int cb_id = cb_tmp->GetId(); - - if (cb_id == enable_rumble_id) - { - g_conf.pad_options[m_pad_id].forcefeedback = (m_cb_rumble->GetValue()) ? (u32)1 : (u32)0; - if (m_cb_rumble->GetValue()) - { - device_manager.devices[m_pad_id]->TestForce(); - m_sl_rumble_intensity->Enable(); - } - else - { - m_sl_rumble_intensity->Disable(); - } - } -} - -/** - * Checkbox event, called when the value of the choice box change - */ -void GamepadConfiguration::OnChoiceChange(wxCommandEvent& event) -{ - wxChoice* choice_tmp = (wxChoice*)event.GetEventObject(); - int id = choice_tmp->GetSelection(); - if (id != wxNOT_FOUND) - { - g_conf.set_joy_uid(m_pad_id, Device::index_to_uid(id)); - } -} - -/****************************************/ -/*********** Methods functions **********/ -/****************************************/ - -// Set button values -void GamepadConfiguration::repopulate() -{ - m_cb_rumble->SetValue(g_conf.pad_options[m_pad_id].forcefeedback); - - m_sl_rumble_intensity->SetValue(g_conf.get_ff_intensity() * 0x7FFF); - m_sl_joystick_sensibility->SetValue(g_conf.get_sensibility()); - - u32 joyid = Device::uid_to_index(m_pad_id); - if (joyid < m_joy_map->GetCount() && !m_joy_map->IsEmpty()) - m_joy_map->SetSelection(joyid); - - // enable rumble intensity slider if the checkbox is checked - m_sl_rumble_intensity->Enable(m_cb_rumble->GetValue()); -} diff --git a/pcsx2/PAD/Linux/wx_dialog/GamepadConfiguration.h b/pcsx2/PAD/Linux/wx_dialog/GamepadConfiguration.h deleted file mode 100644 index 57bc5a2850..0000000000 --- a/pcsx2/PAD/Linux/wx_dialog/GamepadConfiguration.h +++ /dev/null @@ -1,48 +0,0 @@ -/* PCSX2 - PS2 Emulator for PCs - * Copyright (C) 2002-2020 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 . - */ - -#pragma once - -#include - -#include "../Device.h" -#include "../keyboard.h" -#include "../Global.h" - -static const s32 rumble_slider_id = wxID_HIGHEST + 200 + 1; -static const s32 joy_slider_id = wxID_HIGHEST + 200 + 2; -static const s32 enable_rumble_id = wxID_HIGHEST + 200 + 3; - -class GamepadConfiguration : public wxDialog -{ - wxCheckBox* m_cb_rumble; - wxSlider *m_sl_rumble_intensity, *m_sl_joystick_sensibility; - wxChoice* m_joy_map; - - u32 m_pad_id; - - // Methods - void repopulate(); - - // Events - void OnUpdateEvent(wxCommandEvent&); - void OnSliderReleased(wxCommandEvent&); - void OnCheckboxChange(wxCommandEvent&); - void OnChoiceChange(wxCommandEvent&); - -public: - GamepadConfiguration(int, wxWindow*); - void InitGamepadConfiguration(); -}; diff --git a/pcsx2/PAD/Linux/wx_dialog/JoystickConfiguration.cpp b/pcsx2/PAD/Linux/wx_dialog/JoystickConfiguration.cpp deleted file mode 100644 index d6404f2726..0000000000 --- a/pcsx2/PAD/Linux/wx_dialog/JoystickConfiguration.cpp +++ /dev/null @@ -1,149 +0,0 @@ -/* PCSX2 - PS2 Emulator for PCs - * Copyright (C) 2002-2020 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 . - */ - -#include "JoystickConfiguration.h" - -static const s32 x_check_id = wxID_HIGHEST + 100 + 1; -static const s32 y_check_id = wxID_HIGHEST + 100 + 2; -static const s32 joy_check_id = wxID_HIGHEST + 100 + 3; - -// Constructor of JoystickConfiguration -JoystickConfiguration::JoystickConfiguration(int pad, bool left, wxWindow* parent) - : wxDialog(parent, wxID_ANY, _T("Joystick configuration"), wxDefaultPosition, wxDefaultSize, - wxSYSTEM_MENU | wxCAPTION | wxCLOSE_BOX | wxCLIP_CHILDREN) - , m_pad_id(pad) -{ - m_isForLeftJoystick = left; - - wxBoxSizer* joy_conf_box = new wxBoxSizer(wxVERTICAL); - - if (m_isForLeftJoystick) - { - m_cb_reverse_x = new wxCheckBox(this, x_check_id, _T("Reverse Lx")); - m_cb_reverse_y = new wxCheckBox(this, y_check_id, _T("Reverse Ly")); - m_cb_mouse_joy = new wxCheckBox(this, joy_check_id, _T("Use mouse for left analog joystick")); - } - else - { - m_cb_reverse_x = new wxCheckBox(this, x_check_id, _T("Reverse Rx")); - m_cb_reverse_y = new wxCheckBox(this, y_check_id, _T("Reverse Ry")); - m_cb_mouse_joy = new wxCheckBox(this, joy_check_id, _T("Use mouse for right analog joystick")); - } - - joy_conf_box->Add(m_cb_reverse_x, wxSizerFlags().Expand().Border(wxLEFT | wxRIGHT, 5)); - joy_conf_box->Add(m_cb_reverse_y, wxSizerFlags().Expand().Border(wxLEFT | wxRIGHT, 5)); - joy_conf_box->Add(m_cb_mouse_joy, wxSizerFlags().Expand().Border(wxLEFT | wxRIGHT, 5)); - - Bind(wxEVT_CHECKBOX, &JoystickConfiguration::OnCheckboxChange, this); - - SetSizerAndFit(joy_conf_box); -} - -/** - * Initialize the frame - * Check if a gamepad is detected - */ -void JoystickConfiguration::InitJoystickConfiguration() -{ - repopulate(); // Set label and fit simulated key array - /* - * Check if there exist at least one pad available - * if the pad id is 0, you need at least 1 gamepad connected, - * if the pad id is 1, you need at least 2 gamepads connected, - * Prevent using a non-initialized value (core dump) - */ - if (device_manager.devices.size() < m_pad_id + 1) - { - if (device_manager.devices.empty()) - wxMessageBox(L"No gamepad detected."); - else - wxMessageBox(L"No second gamepad detected."); - - // disable all checkboxes - m_cb_reverse_y->Disable(); - m_cb_reverse_x->Disable(); - } -} - -/** - * Checkbox event, called when the value of the checkbox change - */ -void JoystickConfiguration::OnCheckboxChange(wxCommandEvent& event) -{ - wxCheckBox* cb_tmp = (wxCheckBox*)event.GetEventObject(); // get the slider object - int cb_id = cb_tmp->GetId(); - - if (m_isForLeftJoystick) - { - switch (cb_id) - { - case x_check_id: - g_conf.pad_options[m_pad_id].reverse_lx = m_cb_reverse_x->GetValue(); - break; - - case y_check_id: - g_conf.pad_options[m_pad_id].reverse_ly = m_cb_reverse_y->GetValue(); - break; - - case joy_check_id: - g_conf.pad_options[m_pad_id].mouse_l = m_cb_mouse_joy->GetValue(); - break; - - default: - break; - } - } - else - { - switch (cb_id) - { - case x_check_id: - g_conf.pad_options[m_pad_id].reverse_rx = m_cb_reverse_x->GetValue(); - break; - - case y_check_id: - g_conf.pad_options[m_pad_id].reverse_ry = m_cb_reverse_y->GetValue(); - break; - - case joy_check_id: - g_conf.pad_options[m_pad_id].mouse_r = m_cb_mouse_joy->GetValue(); - break; - - default: - break; - } - } -} - -/****************************************/ -/*********** Methods functions **********/ -/****************************************/ - -// Set button values -void JoystickConfiguration::repopulate() -{ - if (m_isForLeftJoystick) - { - m_cb_reverse_x->SetValue(g_conf.pad_options[m_pad_id].reverse_lx); - m_cb_reverse_y->SetValue(g_conf.pad_options[m_pad_id].reverse_ly); - m_cb_mouse_joy->SetValue(g_conf.pad_options[m_pad_id].mouse_l); - } - else - { - m_cb_reverse_x->SetValue(g_conf.pad_options[m_pad_id].reverse_rx); - m_cb_reverse_y->SetValue(g_conf.pad_options[m_pad_id].reverse_ry); - m_cb_mouse_joy->SetValue(g_conf.pad_options[m_pad_id].mouse_r); - } -} diff --git a/pcsx2/PAD/Linux/wx_dialog/JoystickConfiguration.h b/pcsx2/PAD/Linux/wx_dialog/JoystickConfiguration.h deleted file mode 100644 index d5cb3371bf..0000000000 --- a/pcsx2/PAD/Linux/wx_dialog/JoystickConfiguration.h +++ /dev/null @@ -1,42 +0,0 @@ -/* PCSX2 - PS2 Emulator for PCs - * Copyright (C) 2002-2020 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 . - */ - -#pragma once - -#include - -#include "../Global.h" -#include "../Device.h" -#include "../keyboard.h" - -class JoystickConfiguration : public wxDialog -{ - wxCheckBox *m_cb_reverse_x, *m_cb_reverse_y, - *m_cb_mouse_joy; // Use mouse for joystick - - u32 m_pad_id; - // isForLeftJoystick -> true is for Left Joystick, false is for Right Joystick - bool m_isForLeftJoystick; - - // Methods - void repopulate(); - - // Events - void OnCheckboxChange(wxCommandEvent&); - -public: - JoystickConfiguration(int, bool, wxWindow*); - void InitJoystickConfiguration(); -}; diff --git a/pcsx2/PAD/Linux/wx_dialog/dialog.cpp b/pcsx2/PAD/Linux/wx_dialog/dialog.cpp deleted file mode 100644 index 302c601667..0000000000 --- a/pcsx2/PAD/Linux/wx_dialog/dialog.cpp +++ /dev/null @@ -1,684 +0,0 @@ -/* PCSX2 - PS2 Emulator for PCs - * Copyright (C) 2002-2020 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 . - */ - -#include "dialog.h" - -#ifdef __APPLE__ -#include - -static std::string KeyName(int pad, int key, int keysym) -{ - // Mouse - if (keysym >> 16) - { - switch (keysym & 0xFFFF) - { - case 0: - return "Mouse ???"; - case 1: - return "Mouse Left"; - case 2: - return "Mouse Middle"; - case 3: - return "Mouse Right"; - default: // Use only number for extra button - return "Mouse " + std::to_string((keysym & 0xFFFF) + 1); - } - } - - - // clang-format off - switch (keysym) - { - case kVK_ANSI_A: return "A"; - case kVK_ANSI_B: return "B"; - case kVK_ANSI_C: return "C"; - case kVK_ANSI_D: return "D"; - case kVK_ANSI_E: return "E"; - case kVK_ANSI_F: return "F"; - case kVK_ANSI_G: return "G"; - case kVK_ANSI_H: return "H"; - case kVK_ANSI_I: return "I"; - case kVK_ANSI_J: return "J"; - case kVK_ANSI_K: return "K"; - case kVK_ANSI_L: return "L"; - case kVK_ANSI_M: return "M"; - case kVK_ANSI_N: return "N"; - case kVK_ANSI_O: return "O"; - case kVK_ANSI_P: return "P"; - case kVK_ANSI_Q: return "Q"; - case kVK_ANSI_R: return "R"; - case kVK_ANSI_S: return "S"; - case kVK_ANSI_T: return "T"; - case kVK_ANSI_U: return "U"; - case kVK_ANSI_V: return "V"; - case kVK_ANSI_W: return "W"; - case kVK_ANSI_X: return "X"; - case kVK_ANSI_Y: return "Y"; - case kVK_ANSI_Z: return "Z"; - case kVK_ANSI_0: return "0"; - case kVK_ANSI_1: return "1"; - case kVK_ANSI_2: return "2"; - case kVK_ANSI_3: return "3"; - case kVK_ANSI_4: return "4"; - case kVK_ANSI_5: return "5"; - case kVK_ANSI_6: return "6"; - case kVK_ANSI_7: return "7"; - case kVK_ANSI_8: return "8"; - case kVK_ANSI_9: return "9"; - case kVK_ANSI_Grave: return "`"; - case kVK_ANSI_Minus: return "-"; - case kVK_ANSI_Equal: return "="; - case kVK_ANSI_LeftBracket: return "["; - case kVK_ANSI_RightBracket: return "]"; - case kVK_ANSI_Backslash: return "\\"; - case kVK_ANSI_Semicolon: return ";"; - case kVK_ANSI_Quote: return "'"; - case kVK_ANSI_Comma: return ","; - case kVK_ANSI_Period: return "."; - case kVK_ANSI_Slash: return "/"; - case kVK_Escape: return "⎋"; - case kVK_Tab: return "⇥"; - case kVK_Delete: return "⌫"; - case kVK_ForwardDelete: return "⌦"; - case kVK_Return: return "↩"; - case kVK_Space: return "␣"; - case kVK_ANSI_KeypadDecimal: return "Keypad ."; - case kVK_ANSI_KeypadMultiply: return "Keypad *"; - case kVK_ANSI_KeypadPlus: return "Keypad +"; - case kVK_ANSI_KeypadClear: return "⌧"; - case kVK_ANSI_KeypadDivide: return "Keypad /"; - case kVK_ANSI_KeypadEnter: return "⌤"; - case kVK_ANSI_KeypadMinus: return "Keypad -"; - case kVK_ANSI_KeypadEquals: return "Keypad ="; - case kVK_ANSI_Keypad0: return "Keypad 0"; - case kVK_ANSI_Keypad1: return "Keypad 1"; - case kVK_ANSI_Keypad2: return "Keypad 2"; - case kVK_ANSI_Keypad3: return "Keypad 3"; - case kVK_ANSI_Keypad4: return "Keypad 4"; - case kVK_ANSI_Keypad5: return "Keypad 5"; - case kVK_ANSI_Keypad6: return "Keypad 6"; - case kVK_ANSI_Keypad7: return "Keypad 7"; - case kVK_ANSI_Keypad8: return "Keypad 8"; - case kVK_ANSI_Keypad9: return "Keypad 9"; - case kVK_Command: return "Left ⌘"; - case kVK_Shift: return "Left ⇧"; - case kVK_CapsLock: return "⇪"; - case kVK_Option: return "Left ⌥"; - case kVK_Control: return "Left ⌃"; - case kVK_RightCommand: return "Right ⌘"; - case kVK_RightShift: return "Right ⇧"; - case kVK_RightOption: return "Right ⌥"; - case kVK_RightControl: return "Right ⌃"; - case kVK_Function: return "fn"; - case kVK_VolumeUp: return "Volume Up"; - case kVK_VolumeDown: return "Volume Down"; - case kVK_Mute: return "Mute"; - case kVK_F1: return "F1"; - case kVK_F2: return "F2"; - case kVK_F3: return "F3"; - case kVK_F4: return "F4"; - case kVK_F5: return "F5"; - case kVK_F6: return "F6"; - case kVK_F7: return "F7"; - case kVK_F8: return "F8"; - case kVK_F9: return "F9"; - case kVK_F10: return "F10"; - case kVK_F11: return "F11"; - case kVK_F12: return "F12"; - case kVK_F13: return "F13"; - case kVK_F14: return "F14"; - case kVK_F15: return "F15"; - case kVK_F16: return "F16"; - case kVK_F17: return "F17"; - case kVK_F18: return "F18"; - case kVK_F19: return "F19"; - case kVK_F20: return "F20"; - case kVK_Help: return "Help"; - case kVK_Home: return "↖"; - case kVK_PageUp: return "⇞"; - case kVK_End: return "↘"; - case kVK_PageDown: return "⇟"; - case kVK_LeftArrow: return "←"; - case kVK_RightArrow: return "→"; - case kVK_DownArrow: return "↓"; - case kVK_UpArrow: return "↑"; - case kVK_ISO_Section: return "Section"; - case kVK_JIS_Yen: return "¥"; - case kVK_JIS_Underscore: return "_"; - case kVK_JIS_KeypadComma: return "Keypad ,"; - case kVK_JIS_Eisu: return "英数"; - case kVK_JIS_Kana: return "かな"; - default: return "Key " + std::to_string(keysym); - } - // clang-format on -} -#else -static std::string KeyName(int pad, int key, int keysym) -{ - // Mouse - if (keysym >> 16) - { - switch (keysym & 0xFFFF) - { - case 0: - return "Mouse ???"; - case 1: - return "Mouse Left"; - case 2: - return "Mouse Middle"; - case 3: - return "Mouse Right"; - default: // Use only number for extra button - return "Mouse " + std::to_string((keysym & 0xFFFF) + 1); - } - } - - return std::string(XKeysymToString(keysym)); -} -#endif - -// Construtor of Dialog -PADDialog::PADDialog() - : wxDialog(NULL, // Parent - wxID_ANY, // ID - _T("Gamepad Settings"), // Title - wxDefaultPosition, // Position - wxSize(DEFAULT_WIDTH, DEFAULT_HEIGHT), // Width + Lenght - // Style - wxSYSTEM_MENU | - wxCAPTION | - wxCLOSE_BOX | - wxCLIP_CHILDREN) -{ - - /* - * Define the size and the position of each button : - * padding[ButtonID][0] : Width - * padding[ButtonID][1] : Height - * padding[ButtonID][2] : x position - * padding[ButtonID][3] : y position - */ - int padding[BUTTONS_LENGHT][4]; - - // L1 - padding[PAD_L1][0] = 218; // Width - padding[PAD_L1][1] = 28; // Height - padding[PAD_L1][2] = 50; // X - padding[PAD_L1][3] = 175; // Y - - // L2 - padding[PAD_L2][0] = 218; // Width - padding[PAD_L2][1] = 28; // Height - padding[PAD_L2][2] = 50; // X - padding[PAD_L2][3] = 104; // Y - - // R1 - padding[PAD_R1][0] = 218; // Width - padding[PAD_R1][1] = 28; // Height - padding[PAD_R1][2] = 726; // X - padding[PAD_R1][3] = 175; // Y - - // R2 - padding[PAD_R2][0] = 218; // Width - padding[PAD_R2][1] = 28; // Height - padding[PAD_R2][2] = 726; // X - padding[PAD_R2][3] = 104; // Y - - // Triangle - padding[PAD_TRIANGLE][0] = 218; // Width - padding[PAD_TRIANGLE][1] = 28; // Height - padding[PAD_TRIANGLE][2] = 726; // X - padding[PAD_TRIANGLE][3] = 246; // Y - - // Circle - padding[PAD_CIRCLE][0] = 218; // Width - padding[PAD_CIRCLE][1] = 28; // Height - padding[PAD_CIRCLE][2] = 726; // X - padding[PAD_CIRCLE][3] = 319; // Y - - // Cross - padding[PAD_CROSS][0] = 218; // Width - padding[PAD_CROSS][1] = 28; // Height - padding[PAD_CROSS][2] = 726; // X - padding[PAD_CROSS][3] = 391; // Y - - // Square - padding[PAD_SQUARE][0] = 218; // Width - padding[PAD_SQUARE][1] = 28; // Height - padding[PAD_SQUARE][2] = 726; // X - padding[PAD_SQUARE][3] = 463; // Y - - // Directional pad up - padding[PAD_UP][0] = 100; // Width - padding[PAD_UP][1] = 25; // Height - padding[PAD_UP][2] = 108; // X - padding[PAD_UP][3] = 290; // Y - - // Directional pad down - padding[PAD_DOWN][0] = 100; // Width - padding[PAD_DOWN][1] = 25; // Height - padding[PAD_DOWN][2] = 108; // X - padding[PAD_DOWN][3] = 340; // Y - - // Directional pad right - padding[PAD_RIGHT][0] = 109; // Width - padding[PAD_RIGHT][1] = 25; // Height - padding[PAD_RIGHT][2] = 159; // X - padding[PAD_RIGHT][3] = 315; // Y - - // Directional pad left - padding[PAD_LEFT][0] = 109; // Width - padding[PAD_LEFT][1] = 25; // Height - padding[PAD_LEFT][2] = 50; // X - padding[PAD_LEFT][3] = 315; // Y - - // Left Joystick up - padding[PAD_L_UP][0] = 100; // Width - padding[PAD_L_UP][1] = 25; // Height - padding[PAD_L_UP][2] = 325; // X - padding[PAD_L_UP][3] = 527; // Y - - // Left Joystick down - padding[PAD_L_DOWN][0] = 100; // Width - padding[PAD_L_DOWN][1] = 25; // Height - padding[PAD_L_DOWN][2] = 325; // X - padding[PAD_L_DOWN][3] = 577; // Y - - // Left Joystick right - padding[PAD_L_RIGHT][0] = 109; // Width - padding[PAD_L_RIGHT][1] = 25; // Height - padding[PAD_L_RIGHT][2] = 377; // X - padding[PAD_L_RIGHT][3] = 552; // Y - - // Left Joystick left - padding[PAD_L_LEFT][0] = 109; // Width - padding[PAD_L_LEFT][1] = 25; // Height - padding[PAD_L_LEFT][2] = 268; // X - padding[PAD_L_LEFT][3] = 552; // Y - - // L3 - padding[PAD_L3][0] = 218; // Width - padding[PAD_L3][1] = 28; // Height - padding[PAD_L3][2] = 268; // X - padding[PAD_L3][3] = 641; // Y - - // Right Joystick up - padding[PAD_R_UP][0] = 100; // Width - padding[PAD_R_UP][1] = 25; // Height - padding[PAD_R_UP][2] = 555; // X - padding[PAD_R_UP][3] = 527; // Y - - // Right Joystick down - padding[PAD_R_DOWN][0] = 100; // Width - padding[PAD_R_DOWN][1] = 25; // Height - padding[PAD_R_DOWN][2] = 555; // X - padding[PAD_R_DOWN][3] = 577; // Y - - // Right Joystick right - padding[PAD_R_RIGHT][0] = 109; // Width - padding[PAD_R_RIGHT][1] = 25; // Height - padding[PAD_R_RIGHT][2] = 607; // X - padding[PAD_R_RIGHT][3] = 552; // Y - - // Right Joystick left - padding[PAD_R_LEFT][0] = 109; // Width - padding[PAD_R_LEFT][1] = 25; // Height - padding[PAD_R_LEFT][2] = 498; // X - padding[PAD_R_LEFT][3] = 552; // Y - - // R3 - padding[PAD_R3][0] = 218; // Width - padding[PAD_R3][1] = 28; // Height - padding[PAD_R3][2] = 498; // X - padding[PAD_R3][3] = 641; // Y - - // Start - padding[PAD_START][0] = 218; // Width - padding[PAD_START][1] = 28; // Height - padding[PAD_START][2] = 503; // X - padding[PAD_START][3] = 34; // Y - - // Select - padding[PAD_SELECT][0] = 218; // Width - padding[PAD_SELECT][1] = 28; // Height - padding[PAD_SELECT][2] = 273; // X - padding[PAD_SELECT][3] = 34; // Y - - // Analog - padding[Analog][0] = 218; // Width - padding[Analog][1] = 28; // Height - padding[Analog][2] = 50; // X - padding[Analog][3] = 452; // Y - - // Left Joystick Configuration - padding[JoyL_config][0] = 180; // Width - padding[JoyL_config][1] = 28; // Height - padding[JoyL_config][2] = 50; // X - padding[JoyL_config][3] = 550; // Y - - // Right Joystick Configuration - padding[JoyR_config][0] = 180; // Width - padding[JoyR_config][1] = 28; // Height - padding[JoyR_config][2] = 764; // X - padding[JoyR_config][3] = 550; // Y - - // Gamepad Configuration - padding[Gamepad_config][0] = 180; // Width - padding[Gamepad_config][1] = 28; // Height - padding[Gamepad_config][2] = 50; // X - padding[Gamepad_config][3] = 585; // Y - - // Set All Buttons - padding[Set_all][0] = 180; // Width - padding[Set_all][1] = 28; // Height - padding[Set_all][2] = 764; // X - padding[Set_all][3] = 585; // Y - - // Apply modifications without exit - padding[Apply][0] = 70; // Width - padding[Apply][1] = 28; // Height - padding[Apply][2] = 833; // X - padding[Apply][3] = 642; // Y - - // Ok button - padding[Ok][0] = 70; // Width - padding[Ok][1] = 28; // Height - padding[Ok][2] = 913; // X - padding[Ok][3] = 642; // Y - - // Cancel button - padding[Cancel][0] = 70; // Width - padding[Cancel][1] = 28; // Height - padding[Cancel][2] = 753; // X - padding[Cancel][3] = 642; // Y - - // create a new Notebook - m_tab_gamepad = new wxNotebook(this, wxID_ANY); - for (u32 i = 0; i < GAMEPAD_NUMBER; ++i) - { - // Tabs panels - m_pan_tabs[i] = new opPanel( - m_tab_gamepad, - wxID_ANY, - wxDefaultPosition, - wxSize(DEFAULT_WIDTH, DEFAULT_HEIGHT)); - // Add new page - // Define label - std::stringstream sstm; - std::string label = "Gamepad "; - sstm << label << i; - // New page creation - m_tab_gamepad->AddPage( - m_pan_tabs[i], // Parent - wxString(sstm.str().c_str(), wxConvUTF8) // Title - ); - - for (int j = 0; j < BUTTONS_LENGHT; ++j) - { - // Gamepad buttons - m_bt_gamepad[i][j] = new wxButton( - m_pan_tabs[i], // Parent - wxID_HIGHEST + j + 1, // ID - _T("Undefined"), // Label - wxPoint(padding[j][2], padding[j][3]), // Position - wxSize(padding[j][0], padding[j][1]) // Size - ); - } - // Redefine others gui buttons label - m_bt_gamepad[i][JoyL_config]->SetLabel(_T("&Left Joystick Config")); - m_bt_gamepad[i][JoyR_config]->SetLabel(_T("&Right Joystick Config")); - m_bt_gamepad[i][Gamepad_config]->SetLabel(_T("&Gamepad Configuration")); - m_bt_gamepad[i][Set_all]->SetLabel(_T("&Set All Buttons")); - m_bt_gamepad[i][Cancel]->SetLabel(_T("&Cancel")); - m_bt_gamepad[i][Apply]->SetLabel(_T("&Apply")); - m_bt_gamepad[i][Ok]->SetLabel(_T("&Ok")); - - // Disable analog button (not yet supported) - m_bt_gamepad[i][Analog]->Disable(); - } - - Bind(wxEVT_BUTTON, &PADDialog::OnButtonClicked, this); - - for (u32 i = 0; i < GAMEPAD_NUMBER; ++i) - { - for (int j = 0; j < NB_IMG; ++j) - { - m_pressed[i][j] = false; - } - } -} - -void PADDialog::InitDialog() -{ - EnumerateDevices(); // activate gamepads - PADLoadConfig(); // Load configuration from the ini file - repopulate(); // Set label and fit simulated key array -} - -/****************************************/ -/*********** Events functions ***********/ -/****************************************/ - -void PADDialog::OnButtonClicked(wxCommandEvent& event) -{ - // Display a message each time the button is clicked - wxButton* bt_tmp = (wxButton*)event.GetEventObject(); // get the button object - int bt_id = bt_tmp->GetId() - wxID_HIGHEST - 1; // get the real ID - int gamepad_id = m_tab_gamepad->GetSelection(); // get the tab ID (equivalent to the gamepad id) - if (bt_id >= 0 && bt_id <= PAD_R_LEFT) - { // if the button ID is a gamepad button - bt_tmp->Disable(); // switch the button state to "Disable" - config_key(gamepad_id, bt_id); - bt_tmp->Enable(); // switch the button state to "Enable" - } - else if (bt_id == Gamepad_config) - { // If the button ID is equals to the Gamepad_config button ID - GamepadConfiguration gamepad_config(gamepad_id, this); - - gamepad_config.InitGamepadConfiguration(); - gamepad_config.ShowModal(); - } - else if (bt_id == JoyL_config) - { // If the button ID is equals to the JoyL_config button ID - JoystickConfiguration joystick_config(gamepad_id, true, this); - - joystick_config.InitJoystickConfiguration(); - joystick_config.ShowModal(); - } - else if (bt_id == JoyR_config) - { // If the button ID is equals to the JoyR_config button ID - JoystickConfiguration joystick_config(gamepad_id, false, this); - - joystick_config.InitJoystickConfiguration(); - joystick_config.ShowModal(); - } - else if (bt_id == Set_all) - { // If the button ID is equals to the Set_all button ID - for (u32 i = 0; i < MAX_KEYS; ++i) - { - bt_tmp = m_bt_gamepad[gamepad_id][i]; - switch (i) - { - case PAD_L_UP: // Left joystick (Up) ↑ - m_pan_tabs[gamepad_id]->ShowImg(img_l_arrow_up); - break; - case PAD_L_RIGHT: // Left joystick (Right) → - m_pan_tabs[gamepad_id]->ShowImg(img_l_arrow_right); - break; - case PAD_L_DOWN: // Left joystick (Down) ↓ - m_pan_tabs[gamepad_id]->ShowImg(img_l_arrow_bottom); - break; - case PAD_L_LEFT: // Left joystick (Left) ← - m_pan_tabs[gamepad_id]->ShowImg(img_l_arrow_left); - break; - case PAD_R_UP: // Right joystick (Up) ↑ - m_pan_tabs[gamepad_id]->ShowImg(img_r_arrow_up); - break; - case PAD_R_RIGHT: // Right joystick (Right) → - m_pan_tabs[gamepad_id]->ShowImg(img_r_arrow_right); - break; - case PAD_R_DOWN: // Right joystick (Down) ↓ - m_pan_tabs[gamepad_id]->ShowImg(img_r_arrow_bottom); - break; - case PAD_R_LEFT: // Right joystick (Left) ← - m_pan_tabs[gamepad_id]->ShowImg(img_r_arrow_left); - break; - default: - m_pan_tabs[gamepad_id]->ShowImg(i); - break; - } - m_pan_tabs[gamepad_id]->Refresh(); - m_pan_tabs[gamepad_id]->Update(); - bool key_captured = config_key(gamepad_id, i); - switch (i) - { - case PAD_L_UP: // Left joystick (Up) ↑ - m_pan_tabs[gamepad_id]->HideImg(img_l_arrow_up); - break; - case PAD_L_RIGHT: // Left joystick (Right) → - m_pan_tabs[gamepad_id]->HideImg(img_l_arrow_right); - break; - case PAD_L_DOWN: // Left joystick (Down) ↓ - m_pan_tabs[gamepad_id]->HideImg(img_l_arrow_bottom); - break; - case PAD_L_LEFT: // Left joystick (Left) ← - m_pan_tabs[gamepad_id]->HideImg(img_l_arrow_left); - break; - case PAD_R_UP: // Right joystick (Up) ↑ - m_pan_tabs[gamepad_id]->HideImg(img_r_arrow_up); - break; - case PAD_R_RIGHT: // Right joystick (Right) → - m_pan_tabs[gamepad_id]->HideImg(img_r_arrow_right); - break; - case PAD_R_DOWN: // Right joystick (Down) ↓ - m_pan_tabs[gamepad_id]->HideImg(img_r_arrow_bottom); - break; - case PAD_R_LEFT: // Right joystick (Left) ← - m_pan_tabs[gamepad_id]->HideImg(img_r_arrow_left); - break; - default: - m_pan_tabs[gamepad_id]->HideImg(i); - break; - } - m_pan_tabs[gamepad_id]->Refresh(); - m_pan_tabs[gamepad_id]->Update(); - if (!key_captured) - { // if ESC is hit, abort Set_all and return user control - break; - } - usleep(500000); // give enough time to the user to release the button - } - } - else if (bt_id == Ok) - { // If the button ID is equals to the Ok button ID - PADSaveConfig(); // Save the configuration - Close(); // Close the window - } - else if (bt_id == Apply) - { // If the button ID is equals to the Apply button ID - PADSaveConfig(); // Save the configuration - } - else if (bt_id == Cancel) - { // If the button ID is equals to the cancel button ID - Close(); // Close the window - } -} - -/****************************************/ -/*********** Methods functions **********/ -/****************************************/ - -bool PADDialog::config_key(int pad, int key) -{ - bool captured = false; - u32 key_pressed = 0; - - while (!captured) - { - if (PollForNewKeyboardKeys(key_pressed)) - { - // special case for keyboard/mouse to handle multiple keys - // Note: key_pressed == UINT32_MAX when ESC is hit to abort the capture - if (key_pressed != UINT32_MAX) - { - clear_key(pad, key); - set_keyboard_key(pad, key_pressed, key); - m_simulatedKeys[pad][key] = key_pressed; - } - else - { - return captured; - } - captured = true; - } - } - m_bt_gamepad[pad][key]->SetLabel( - KeyName(pad, key, m_simulatedKeys[pad][key]).c_str()); - - return captured; -} - -void PADDialog::clear_key(int pad, int key) -{ - // Erase the keyboard binded key - u32 keysim = m_simulatedKeys[pad][key]; - m_simulatedKeys[pad][key] = 0; - - // erase gamepad entry (keysim map) - g_conf.keysym_map[pad].erase(keysim); -} - - -// Set button values -void PADDialog::repopulate() -{ - for (u32 gamepad_id = 0; gamepad_id < GAMEPAD_NUMBER; ++gamepad_id) - { - // keyboard/mouse key - for (const auto& it : g_conf.keysym_map[gamepad_id]) - { - int keysym = it.first; - int key = it.second; - - m_bt_gamepad[gamepad_id][key]->SetLabel( - KeyName(gamepad_id, key, keysym).c_str()); - - m_simulatedKeys[gamepad_id][key] = keysym; - } - } -} - -// Main -void DisplayDialog() -{ - if (g_conf.ftw) - { - wxString info("The PAD GUI is provided to map the keyboard/mouse to the virtual PS2 pad.\n\n" - "Gamepads/Joysticks are plug and play. Re-mapping of Gamepads/Joysticks is currently not supported in the PAD GUI.\n\n" - "The active gamepad can be selected in the 'Gamepad Configuration' panel.\n\n"); - - wxMessageDialog ftw(nullptr, info); - ftw.ShowModal(); - - g_conf.ftw = 0; - PADSaveConfig(); - } - - PADDialog dialog; - - dialog.InitDialog(); - dialog.ShowModal(); -} diff --git a/pcsx2/PAD/Linux/wx_dialog/dialog.h b/pcsx2/PAD/Linux/wx_dialog/dialog.h deleted file mode 100644 index 642cecfc2f..0000000000 --- a/pcsx2/PAD/Linux/wx_dialog/dialog.h +++ /dev/null @@ -1,89 +0,0 @@ -/* PCSX2 - PS2 Emulator for PCs - * Copyright (C) 2002-2020 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 . - */ - -#pragma once - -#include -#include -#include -#include -#include -#include -#include -#include -#include - -#include - -#include "../InputManager.h" -#include "../keyboard.h" -#include "../Global.h" -#include "opPanel.h" - -#include "GamepadConfiguration.h" -#include "JoystickConfiguration.h" - -// Allow to found quickly button id -// e.g L2 → 0, triangle → 4, ... -// see Global.h for more details about gamepad button id - -enum gui_buttons -{ - Analog = PAD_R_LEFT + 1, // Analog button (not yet supported ?) - JoyL_config, // Left Joystick Configuration - JoyR_config, // Right Joystick Configuration - Gamepad_config, // Gamepad Configuration - Set_all, // Set all buttons - Apply, // Apply modifications without exit - Ok, // Apply modifications and exit - Cancel // Exit without apply modificatons -}; - -#define BUTTONS_LENGHT 32 // numbers of buttons on the gamepad -#define UPDATE_TIME 5 -#define DEFAULT_WIDTH 1000 -#define DEFAULT_HEIGHT 760 - -class PADDialog : public wxDialog -{ - // Panels - opPanel* m_pan_tabs[GAMEPAD_NUMBER]; // Gamepad Tabs box - // Notebooks - wxNotebook* m_tab_gamepad; // Joysticks Tabs - // Buttons - wxButton* m_bt_gamepad[GAMEPAD_NUMBER][BUTTONS_LENGHT]; // Joystick button use to modify the button mapping - // Contain all simulated key - u32 m_simulatedKeys[GAMEPAD_NUMBER][MAX_KEYS]; - // Timer - wxTimer m_time_update_gui; - // Check if the gui must display feddback image - bool m_pressed[GAMEPAD_NUMBER][NB_IMG]; - - // methods - bool config_key(int, int); - void clear_key(int, int); - void repopulate(); - - // Events - void OnButtonClicked(wxCommandEvent&); - void JoystickEvent(wxTimerEvent&); - -public: - PADDialog(); - void InitDialog(); - void show(); -}; - -extern void DisplayDialog(); // Main function diff --git a/pcsx2/PAD/Linux/wx_dialog/opPanel.cpp b/pcsx2/PAD/Linux/wx_dialog/opPanel.cpp deleted file mode 100644 index c7c6d0ea5f..0000000000 --- a/pcsx2/PAD/Linux/wx_dialog/opPanel.cpp +++ /dev/null @@ -1,243 +0,0 @@ -/* PCSX2 - PS2 Emulator for PCs - * Copyright (C) 2002-2020 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 . - */ - -#include "opPanel.h" -#include "ImgHeader/dualshock2.h" -#include "ImgHeader/cross.h" -#include "ImgHeader/circle.h" -#include "ImgHeader/square.h" -#include "ImgHeader/triangle.h" -#include "ImgHeader/dp_left.h" -#include "ImgHeader/dp_right.h" -#include "ImgHeader/dp_up.h" -#include "ImgHeader/dp_bottom.h" -#include "ImgHeader/l1.h" -#include "ImgHeader/r1.h" -#include "ImgHeader/l2.h" -#include "ImgHeader/l3.h" -#include "ImgHeader/r2.h" -#include "ImgHeader/r3.h" -#include "ImgHeader/start.h" -#include "ImgHeader/select.h" -#include "ImgHeader/analog.h" -#include "ImgHeader/joystick_cursor.h" -#include "ImgHeader/arrow_up.h" -#include "ImgHeader/arrow_right.h" -#include "ImgHeader/arrow_bottom.h" -#include "ImgHeader/arrow_left.h" - -opPanel::opPanel(wxWindow* parent, - wxWindowID id = wxID_ANY, - const wxPoint& pos = wxDefaultPosition, - const wxSize& size = wxDefaultSize) - : wxPanel(parent, id, pos, size) -{ - m_picture[img_background] = EmbeddedImage().Get(); - - m_picture[img_start] = EmbeddedImage().Get(); - m_picture[img_select] = EmbeddedImage().Get(); - m_picture[img_analog] = EmbeddedImage().Get(); - - m_picture[img_dp_left] = EmbeddedImage().Get(); - m_picture[img_dp_right] = EmbeddedImage().Get(); - m_picture[img_dp_up] = EmbeddedImage().Get(); - m_picture[img_dp_bottom] = EmbeddedImage().Get(); - - m_picture[img_square] = EmbeddedImage().Get(); - m_picture[img_circle] = EmbeddedImage().Get(); - m_picture[img_cross] = EmbeddedImage().Get(); - m_picture[img_triangle] = EmbeddedImage().Get(); - - m_picture[img_l1] = EmbeddedImage().Get(); - m_picture[img_l3] = EmbeddedImage().Get(); - m_picture[img_l2] = EmbeddedImage().Get(); - - m_picture[img_r1] = EmbeddedImage().Get(); - m_picture[img_r3] = EmbeddedImage().Get(); - m_picture[img_r2] = EmbeddedImage().Get(); - - m_picture[img_left_cursor] = EmbeddedImage().Get(); - m_picture[img_right_cursor] = EmbeddedImage().Get(); - - m_picture[img_l_arrow_up] = EmbeddedImage().Get(); - m_picture[img_l_arrow_right] = EmbeddedImage().Get(); - m_picture[img_l_arrow_bottom] = EmbeddedImage().Get(); - m_picture[img_l_arrow_left] = EmbeddedImage().Get(); - - m_picture[img_r_arrow_up] = EmbeddedImage().Get(); - m_picture[img_r_arrow_right] = EmbeddedImage().Get(); - m_picture[img_r_arrow_bottom] = EmbeddedImage().Get(); - m_picture[img_r_arrow_left] = EmbeddedImage().Get(); - - for (int i = 0; i < NB_IMG; ++i) - { - m_show_image[i] = false; - HideImg(i); - } - ShowImg(img_background); - m_show_image[img_background] = true; - - m_left_cursor_x = 0; - m_left_cursor_y = 0; - m_right_cursor_x = 0; - m_right_cursor_y = 0; -} - -void opPanel::HideImg(int id) -{ - if (id < NB_IMG) - { - m_show_image[id] = false; - Refresh(); - } -} - -void opPanel::ShowImg(int id) -{ - if (id < NB_IMG) - { - m_show_image[id] = true; - Refresh(); - } -} - -void opPanel::MoveJoystick(int axe, int value) -{ - if (axe == 0) - { - m_left_cursor_x = value * 30 / 40000; - } - else if (axe == 1) - { - m_left_cursor_y = value * 30 / 40000; - } - else if (axe == 2) - { - m_right_cursor_x = value * 30 / 40000; - } - else - { - m_right_cursor_y = value * 30 / 40000; - } -} - -wxBEGIN_EVENT_TABLE(opPanel, wxPanel) - EVT_PAINT(opPanel::OnPaint) - wxEND_EVENT_TABLE() - - void opPanel::OnPaint(wxPaintEvent& event) -{ - wxPaintDC dc(this); - - wxMemoryDC temp_background, temp_start, temp_select, temp_analog, temp_dp_left, - temp_dp_right, temp_dp_up, temp_dp_bottom, temp_l1, temp_r1, temp_L3, temp_l2_2, - temp_R3, temp_r2_2, temp_square, temp_circle, temp_cross, temp_triangle, - temp_left_cursor, temp_right_cursor, temp_l_arrow_up, temp_l_arrow_right, - temp_l_arrow_bottom, temp_l_arrow_left, temp_r_arrow_up, temp_r_arrow_right, - temp_r_arrow_bottom, temp_r_arrow_left; - - temp_background.SelectObject(m_picture[img_background]); - temp_start.SelectObject(m_picture[img_start]); - temp_select.SelectObject(m_picture[img_select]); - temp_analog.SelectObject(m_picture[img_analog]); - temp_dp_left.SelectObject(m_picture[img_dp_left]); - - temp_dp_right.SelectObject(m_picture[img_dp_right]); - temp_dp_up.SelectObject(m_picture[img_dp_up]); - temp_dp_bottom.SelectObject(m_picture[img_dp_bottom]); - temp_l1.SelectObject(m_picture[img_l1]); - temp_r1.SelectObject(m_picture[img_r1]); - temp_L3.SelectObject(m_picture[img_l3]); - temp_l2_2.SelectObject(m_picture[img_l2]); - - temp_R3.SelectObject(m_picture[img_r3]); - temp_r2_2.SelectObject(m_picture[img_r2]); - temp_square.SelectObject(m_picture[img_square]); - temp_circle.SelectObject(m_picture[img_circle]); - temp_cross.SelectObject(m_picture[img_cross]); - temp_triangle.SelectObject(m_picture[img_triangle]); - - temp_left_cursor.SelectObject(m_picture[img_left_cursor]); - temp_right_cursor.SelectObject(m_picture[img_right_cursor]); - - temp_l_arrow_up.SelectObject(m_picture[img_l_arrow_up]); - temp_l_arrow_right.SelectObject(m_picture[img_l_arrow_right]); - temp_l_arrow_bottom.SelectObject(m_picture[img_l_arrow_bottom]); - temp_l_arrow_left.SelectObject(m_picture[img_l_arrow_left]); - - temp_r_arrow_up.SelectObject(m_picture[img_r_arrow_up]); - temp_r_arrow_right.SelectObject(m_picture[img_r_arrow_right]); - temp_r_arrow_bottom.SelectObject(m_picture[img_r_arrow_bottom]); - temp_r_arrow_left.SelectObject(m_picture[img_r_arrow_left]); - - if (m_show_image[img_background]) - dc.Blit(wxPoint(0, 0), temp_background.GetSize(), &temp_background, wxPoint(0, 0), wxCOPY, true); - if (m_show_image[img_start]) - dc.Blit(wxPoint(526, 296), temp_start.GetSize(), &temp_start, wxPoint(0, 0), wxCOPY, true); - if (m_show_image[img_select]) - dc.Blit(wxPoint(450, 297), temp_select.GetSize(), &temp_select, wxPoint(0, 0), wxCOPY, true); - if (m_show_image[img_analog]) - dc.Blit(wxPoint(489, 358), temp_analog.GetSize(), &temp_analog, wxPoint(0, 0), wxCOPY, true); - if (m_show_image[img_dp_left]) - dc.Blit(wxPoint(334, 292), temp_dp_left.GetSize(), &temp_dp_left, wxPoint(0, 0), wxCOPY, true); - if (m_show_image[img_dp_right]) - dc.Blit(wxPoint(378, 292), temp_dp_right.GetSize(), &temp_dp_right, wxPoint(0, 0), wxCOPY, true); - if (m_show_image[img_dp_up]) - dc.Blit(wxPoint(358, 269), temp_dp_up.GetSize(), &temp_dp_up, wxPoint(0, 0), wxCOPY, true); - if (m_show_image[img_dp_bottom]) - dc.Blit(wxPoint(358, 312), temp_dp_bottom.GetSize(), &temp_dp_bottom, wxPoint(0, 0), wxCOPY, true); - if (m_show_image[img_l1]) - dc.Blit(wxPoint(343, 186), temp_l1.GetSize(), &temp_l1, wxPoint(0, 0), wxCOPY, true); - if (m_show_image[img_r1]) - dc.Blit(wxPoint(593, 186), temp_r1.GetSize(), &temp_r1, wxPoint(0, 0), wxCOPY, true); - if (m_show_image[img_l3]) - dc.Blit(wxPoint(409, 344), temp_L3.GetSize(), &temp_L3, wxPoint(0, 0), wxCOPY, true); - if (m_show_image[img_l2]) - dc.Blit(wxPoint(346, 158), temp_l2_2.GetSize(), &temp_l2_2, wxPoint(0, 0), wxCOPY, true); - if (m_show_image[img_r3]) - dc.Blit(wxPoint(525, 344), temp_R3.GetSize(), &temp_R3, wxPoint(0, 0), wxCOPY, true); - if (m_show_image[img_r2]) - dc.Blit(wxPoint(582, 158), temp_r2_2.GetSize(), &temp_r2_2, wxPoint(0, 0), wxCOPY, true); - if (m_show_image[img_square]) - dc.Blit(wxPoint(573, 287), temp_square.GetSize(), &temp_square, wxPoint(0, 0), wxCOPY, true); - if (m_show_image[img_circle]) - dc.Blit(wxPoint(647, 287), temp_circle.GetSize(), &temp_circle, wxPoint(0, 0), wxCOPY, true); - if (m_show_image[img_cross]) - dc.Blit(wxPoint(610, 324), temp_cross.GetSize(), &temp_cross, wxPoint(0, 0), wxCOPY, true); - if (m_show_image[img_triangle]) - dc.Blit(wxPoint(610, 250), temp_triangle.GetSize(), &temp_triangle, wxPoint(0, 0), wxCOPY, true); - if (m_show_image[img_left_cursor]) - dc.Blit(wxPoint(439 + m_left_cursor_x, 374 + m_left_cursor_y), temp_left_cursor.GetSize(), &temp_left_cursor, wxPoint(0, 0), wxCOPY, true); - if (m_show_image[img_right_cursor]) - dc.Blit(wxPoint(555 + m_right_cursor_x, 374 + m_right_cursor_y), temp_right_cursor.GetSize(), &temp_right_cursor, wxPoint(0, 0), wxCOPY, true); - - if (m_show_image[img_l_arrow_up]) - dc.Blit(wxPoint(433, 357), temp_l_arrow_up.GetSize(), &temp_l_arrow_up, wxPoint(0, 0), wxCOPY, true); - if (m_show_image[img_l_arrow_right]) - dc.Blit(wxPoint(423, 368), temp_l_arrow_right.GetSize(), &temp_l_arrow_right, wxPoint(0, 0), wxCOPY, true); - if (m_show_image[img_l_arrow_bottom]) - dc.Blit(wxPoint(433, 357), temp_l_arrow_bottom.GetSize(), &temp_l_arrow_bottom, wxPoint(0, 0), wxCOPY, true); - if (m_show_image[img_l_arrow_left]) - dc.Blit(wxPoint(423, 368), temp_l_arrow_left.GetSize(), &temp_l_arrow_left, wxPoint(0, 0), wxCOPY, true); - - if (m_show_image[img_r_arrow_up]) - dc.Blit(wxPoint(548, 357), temp_r_arrow_up.GetSize(), &temp_r_arrow_up, wxPoint(0, 0), wxCOPY, true); - if (m_show_image[img_r_arrow_right]) - dc.Blit(wxPoint(539, 368), temp_r_arrow_right.GetSize(), &temp_r_arrow_right, wxPoint(0, 0), wxCOPY, true); - if (m_show_image[img_r_arrow_bottom]) - dc.Blit(wxPoint(548, 357), temp_r_arrow_bottom.GetSize(), &temp_r_arrow_bottom, wxPoint(0, 0), wxCOPY, true); - if (m_show_image[img_r_arrow_left]) - dc.Blit(wxPoint(539, 368), temp_r_arrow_left.GetSize(), &temp_r_arrow_left, wxPoint(0, 0), wxCOPY, true); -} diff --git a/pcsx2/PAD/Linux/wx_dialog/opPanel.h b/pcsx2/PAD/Linux/wx_dialog/opPanel.h deleted file mode 100644 index 3980976cc6..0000000000 --- a/pcsx2/PAD/Linux/wx_dialog/opPanel.h +++ /dev/null @@ -1,69 +0,0 @@ -/* PCSX2 - PS2 Emulator for PCs - * Copyright (C) 2002-2020 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 . - */ - -#pragma once - -#include - -#include "gui/EmbeddedImage.h" - -enum gui_img -{ - img_l2, - img_r2, - img_l1, - img_r1, - img_triangle, - img_circle, - img_cross, - img_square, - img_select, - img_l3, - img_r3, - img_start, - img_dp_up, - img_dp_right, - img_dp_bottom, - img_dp_left, - img_left_cursor, - img_right_cursor, - img_analog, - img_background, // background pic - img_l_arrow_up, - img_l_arrow_right, - img_l_arrow_bottom, - img_l_arrow_left, - img_r_arrow_up, - img_r_arrow_right, - img_r_arrow_bottom, - img_r_arrow_left -}; - -#define NB_IMG 28 - -class opPanel : public wxPanel -{ - wxBitmap m_picture[NB_IMG]; - bool m_show_image[NB_IMG]; - int m_left_cursor_x, m_left_cursor_y, m_right_cursor_x, m_right_cursor_y; - wxDECLARE_EVENT_TABLE(); - void OnPaint(wxPaintEvent& event); - -public: - opPanel(wxWindow*, wxWindowID, const wxPoint&, const wxSize&); - void HideImg(int); - void ShowImg(int); - void MoveJoystick(int, int); -}; diff --git a/pcsx2/PAD/Windows/Default.ini b/pcsx2/PAD/Windows/Default.ini deleted file mode 100644 index 5133ff4586..0000000000 --- a/pcsx2/PAD/Windows/Default.ini +++ /dev/null @@ -1,191 +0,0 @@ -[General Settings] -Last Config Path=inis -Last Config Name=PAD.pad -Force Cursor Hide=0 -Mouse Unfocus=1 -Background=1 -Multiple Bindings=0 -DirectInput Game Devices=1 -XInput=1 -DualShock 3=0 -Multitap 1=0 -Multitap 2=0 -Logging=0 -GH2=0 -Keyboard Mode=2 -Mouse Mode=0 -[Pad Settings] -Configure On Bind=0 -[Pad 0 0] -Mode=1 -[Pad 0 1] -Mode=1 -[Pad 0 2] -Mode=1 -[Pad 0 3] -Mode=1 -[Pad 1 0] -Mode=1 -[Pad 1 1] -Mode=1 -[Pad 1 2] -Mode=1 -[Pad 1 3] -Mode=1 -[Device 0] -Display Name=XInput Pad 0 -Instance ID=XInput Pad 0 -API=4 -Type=3 -Binding 0=0x00200000, 0, 20, 65536, 0, 0, 1, 1 -Binding 1=0x00200001, 0, 22, 65536, 0, 0, 1, 1 -Binding 2=0x00200002, 0, 23, 65536, 0, 0, 1, 1 -Binding 3=0x00200003, 0, 21, 65536, 0, 0, 1, 1 -Binding 4=0x00200004, 0, 19, 65536, 0, 0, 1, 1 -Binding 5=0x00200005, 0, 16, 65536, 0, 0, 1, 1 -Binding 6=0x00200006, 0, 17, 65536, 0, 0, 1, 1 -Binding 7=0x00200007, 0, 18, 65536, 0, 0, 1, 1 -Binding 8=0x00200008, 0, 26, 65536, 0, 0, 1, 1 -Binding 9=0x00200009, 0, 27, 65536, 0, 0, 1, 1 -Binding 10=0x0020000A, 0, 40, 65536, 0, 0, 1, 1 -Binding 11=0x0020000C, 0, 30, 65536, 0, 0, 1, 1 -Binding 12=0x0020000D, 0, 29, 65536, 0, 0, 1, 1 -Binding 13=0x0020000E, 0, 31, 65536, 0, 0, 1, 1 -Binding 14=0x0020000F, 0, 28, 65536, 0, 0, 1, 1 -Binding 15=0x00200010, 0, 24, 65536, 0, 0, 1, 1 -Binding 16=0x00200011, 0, 25, 65536, 0, 0, 1, 1 -Binding 17=0x01020013, 0, 33, 87183, 0, 0, 13172, 1 -Binding 18=0x02020013, 0, 35, 87183, 0, 0, 13172, 1 -Binding 19=0x01020014, 0, 32, 87183, 0, 0, 13172, 1 -Binding 20=0x02020014, 0, 34, 87183, 0, 0, 13172, 1 -Binding 21=0x01020015, 0, 37, 87183, 0, 0, 13172, 1 -Binding 22=0x02020015, 0, 39, 87183, 0, 0, 13172, 1 -Binding 23=0x01020016, 0, 36, 87183, 0, 0, 13172, 1 -Binding 24=0x02020016, 0, 38, 87183, 0, 0, 13172, 1 -Binding 25=0x00200000, 0, 20, 65536, 0, 0, 1, 2 -Binding 26=0x00200001, 0, 22, 65536, 0, 0, 1, 2 -Binding 27=0x00200004, 0, 19, 65536, 0, 0, 1, 2 -Binding 28=0x00200005, 0, 16, 65536, 0, 0, 1, 2 -Binding 29=0x00200008, 0, 25, 65536, 0, 0, 1, 2 -Binding 30=0x00200009, 0, 28, 65536, 0, 0, 1, 2 -Binding 31=0x0020000F, 0, 30, 65536, 0, 0, 1, 2 -Binding 32=0x00200010, 0, 29, 65536, 0, 0, 1, 2 -Binding 33=0x00200011, 0, 31, 65536, 0, 0, 1, 2 -Binding 34=0x01020014, 0, 32, 65536, 0, 0, 13172, 2 -Binding 35=0x02020014, 0, 34, 65536, 0, 0, 13172, 2 -Binding 36=0x00200002, 0, 28, 65536, 0, 0, 1, 3 -Binding 37=0x00200003, 0, 27, 65536, 0, 0, 1, 3 -Binding 38=0x00200004, 0, 19, 65536, 0, 0, 1, 3 -Binding 39=0x00200005, 0, 16, 65536, 0, 0, 1, 3 -Binding 40=0x00200008, 0, 30, 65536, 0, 0, 1, 3 -Binding 41=0x00200009, 0, 31, 65536, 0, 0, 1, 3 -Binding 42=0x0020000C, 0, 25, 65536, 0, 0, 1, 3 -Binding 43=0x0020000D, 0, 24, 65536, 0, 0, 1, 3 -Binding 44=0x0020000E, 0, 26, 65536, 0, 0, 1, 3 -Binding 45=0x00200010, 0, 29, 65536, 0, 0, 1, 3 -Binding 46=0x00200011, 0, 20, 65536, 0, 0, 1, 3 -Binding 47=0x00200008, 0, 29, 65536, 0, 0, 1, 4 -Binding 48=0x00200009, 0, 30, 65536, 0, 0, 1, 4 -Binding 49=0x01020013, 0, 33, 3000, 0, 0, 13172, 4 -Binding 50=0x02020013, 0, 35, 3000, 0, 0, 13172, 4 -Binding 51=0x01020014, 0, 32, 3000, 0, 0, 13172, 4 -Binding 52=0x02020014, 0, 34, 3000, 0, 0, 13172, 4 -Binding 53=0x00200000, 0, 20, 65536, 0, 0, 1, 5 -Binding 54=0x00200001, 0, 22, 65536, 0, 0, 1, 5 -Binding 55=0x00200002, 0, 23, 65536, 0, 0, 1, 5 -Binding 56=0x00200003, 0, 21, 65536, 0, 0, 1, 5 -Binding 57=0x00200004, 0, 19, 65536, 0, 0, 1, 5 -Binding 58=0x00200008, 0, 26, 65536, 0, 0, 1, 5 -Binding 59=0x00200009, 0, 27, 65536, 0, 0, 1, 5 -Binding 60=0x0020000C, 0, 30, 65536, 0, 0, 1, 5 -Binding 61=0x0020000D, 0, 29, 65536, 0, 0, 1, 5 -Binding 62=0x0020000E, 0, 31, 65536, 0, 0, 1, 5 -Binding 63=0x0020000F, 0, 28, 65536, 0, 0, 1, 5 -Binding 64=0x01020013, 0, 33, 87183, 0, 0, 13172, 5 -Binding 65=0x02020013, 0, 35, 87183, 0, 0, 13172, 5 -FF Binding 0=Constant 0, 0, 0, 1, 0, 65536, 1, 0 -FF Binding 1=Constant 0, 1, 0, 1, 0, 0, 1, 65536 -[Device 1] -Display Name=XInput Pad 1 -Instance ID=XInput Pad 1 -API=4 -Type=3 -Binding 0=0x00200000, 1, 20, 65536, 0, 0, 1, 1 -Binding 1=0x00200001, 1, 22, 65536, 0, 0, 1, 1 -Binding 2=0x00200002, 1, 23, 65536, 0, 0, 1, 1 -Binding 3=0x00200003, 1, 21, 65536, 0, 0, 1, 1 -Binding 4=0x00200004, 1, 19, 65536, 0, 0, 1, 1 -Binding 5=0x00200005, 1, 16, 65536, 0, 0, 1, 1 -Binding 6=0x00200006, 1, 17, 65536, 0, 0, 1, 1 -Binding 7=0x00200007, 1, 18, 65536, 0, 0, 1, 1 -Binding 8=0x00200008, 1, 26, 65536, 0, 0, 1, 1 -Binding 9=0x00200009, 1, 27, 65536, 0, 0, 1, 1 -Binding 10=0x0020000A, 1, 40, 65536, 0, 0, 1, 1 -Binding 11=0x0020000C, 1, 30, 65536, 0, 0, 1, 1 -Binding 12=0x0020000D, 1, 29, 65536, 0, 0, 1, 1 -Binding 13=0x0020000E, 1, 31, 65536, 0, 0, 1, 1 -Binding 14=0x0020000F, 1, 28, 65536, 0, 0, 1, 1 -Binding 15=0x00200010, 1, 24, 65536, 0, 0, 1, 1 -Binding 16=0x00200011, 1, 25, 65536, 0, 0, 1, 1 -Binding 17=0x01020013, 1, 33, 87183, 0, 0, 13172, 1 -Binding 18=0x02020013, 1, 35, 87183, 0, 0, 13172, 1 -Binding 19=0x01020014, 1, 32, 87183, 0, 0, 13172, 1 -Binding 20=0x02020014, 1, 34, 87183, 0, 0, 13172, 1 -Binding 21=0x01020015, 1, 37, 87183, 0, 0, 13172, 1 -Binding 22=0x02020015, 1, 39, 87183, 0, 0, 13172, 1 -Binding 23=0x01020016, 1, 36, 87183, 0, 0, 13172, 1 -Binding 24=0x02020016, 1, 38, 87183, 0, 0, 13172, 1 -Binding 25=0x00200000, 1, 20, 65536, 0, 0, 1, 2 -Binding 26=0x00200001, 1, 22, 65536, 0, 0, 1, 2 -Binding 27=0x00200004, 1, 19, 65536, 0, 0, 1, 2 -Binding 28=0x00200005, 1, 16, 65536, 0, 0, 1, 2 -Binding 29=0x00200008, 1, 25, 65536, 0, 0, 1, 2 -Binding 30=0x00200009, 1, 28, 65536, 0, 0, 1, 2 -Binding 31=0x0020000F, 1, 30, 65536, 0, 0, 1, 2 -Binding 32=0x00200010, 1, 29, 65536, 0, 0, 1, 2 -Binding 33=0x00200011, 1, 31, 65536, 0, 0, 1, 2 -Binding 34=0x01020014, 1, 32, 65536, 0, 0, 13172, 2 -Binding 35=0x02020014, 1, 34, 65536, 0, 0, 13172, 2 -Binding 36=0x00200002, 1, 28, 65536, 0, 0, 1, 3 -Binding 37=0x00200003, 1, 27, 65536, 0, 0, 1, 3 -Binding 38=0x00200004, 1, 19, 65536, 0, 0, 1, 3 -Binding 39=0x00200005, 1, 16, 65536, 0, 0, 1, 3 -Binding 40=0x00200008, 1, 30, 65536, 0, 0, 1, 3 -Binding 41=0x00200009, 1, 31, 65536, 0, 0, 1, 3 -Binding 42=0x0020000C, 1, 25, 65536, 0, 0, 1, 3 -Binding 43=0x0020000D, 1, 24, 65536, 0, 0, 1, 3 -Binding 44=0x0020000E, 1, 26, 65536, 0, 0, 1, 3 -Binding 45=0x00200010, 1, 29, 65536, 0, 0, 1, 3 -Binding 46=0x00200011, 1, 20, 65536, 0, 0, 1, 3 -Binding 47=0x00200008, 1, 29, 65536, 0, 0, 1, 4 -Binding 48=0x00200009, 1, 30, 65536, 0, 0, 1, 4 -Binding 49=0x01020013, 1, 33, 3000, 0, 0, 13172, 4 -Binding 50=0x02020013, 1, 35, 3000, 0, 0, 13172, 4 -Binding 51=0x01020014, 1, 32, 3000, 0, 0, 13172, 4 -Binding 52=0x02020014, 1, 34, 3000, 0, 0, 13172, 4 -Binding 53=0x00200000, 1, 20, 65536, 0, 0, 1, 5 -Binding 54=0x00200001, 1, 22, 65536, 0, 0, 1, 5 -Binding 55=0x00200002, 1, 23, 65536, 0, 0, 1, 5 -Binding 56=0x00200003, 1, 21, 65536, 0, 0, 1, 5 -Binding 57=0x00200004, 1, 19, 65536, 0, 0, 1, 5 -Binding 58=0x00200008, 1, 26, 65536, 0, 0, 1, 5 -Binding 59=0x00200009, 1, 27, 65536, 0, 0, 1, 5 -Binding 60=0x0020000C, 1, 30, 65536, 0, 0, 1, 5 -Binding 61=0x0020000D, 1, 29, 65536, 0, 0, 1, 5 -Binding 62=0x0020000E, 1, 31, 65536, 0, 0, 1, 5 -Binding 63=0x0020000F, 1, 28, 65536, 0, 0, 1, 5 -Binding 64=0x01020013, 1, 33, 87183, 0, 0, 13172, 5 -Binding 65=0x02020013, 1, 35, 87183, 0, 0, 13172, 5 -FF Binding 0=Constant 1, 0, 0, 1, 0, 65536, 1, 0 -FF Binding 1=Constant 1, 1, 0, 1, 0, 0, 1, 65536 -[Device 12] -Display Name=XInput Pad 2 -Instance ID=XInput Pad 2 -API=4 -Type=3 -[Device 13] -Display Name=XInput Pad 3 -Instance ID=XInput Pad 3 -API=4 -Type=3 - diff --git a/pcsx2/PAD/Windows/DeviceEnumerator.cpp b/pcsx2/PAD/Windows/DeviceEnumerator.cpp deleted file mode 100644 index da83e65815..0000000000 --- a/pcsx2/PAD/Windows/DeviceEnumerator.cpp +++ /dev/null @@ -1,56 +0,0 @@ -/* PCSX2 - PS2 Emulator for PCs - * Copyright (C) 2002-2020 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 . - */ - -#include "PrecompiledHeader.h" -#include "Global.h" -#include "InputManager.h" - -#include "DeviceEnumerator.h" -#ifdef _WIN32 -#include "WindowsMessaging.h" -#include "DirectInput.h" -#include "PADRawInput.h" -#include "XInputEnum.h" -#include "HidDevice.h" -#include "DualShock3.h" -#endif - -#ifdef __linux__ -#include "Linux/KeyboardMouse.h" -#include "Linux/JoyEvdev.h" -#endif - -void EnumDevices(int hideDXXinput) -{ - // Needed for enumeration of some device types. - dm->ReleaseInput(); - InputDeviceManager* oldDm = dm; - dm = new InputDeviceManager(); - -#ifdef _MSC_VER - EnumWindowsMessagingDevices(); - EnumRawInputDevices(); - EnumDualShock3s(); - EnumXInputDevices(); - EnumDirectInputDevices(hideDXXinput); -#else - EnumLnx(); - EnumJoystickEvdev(); -#endif - - dm->CopyBindings(oldDm->numDevices, oldDm->devices); - - delete oldDm; -} diff --git a/pcsx2/PAD/Windows/DeviceEnumerator.h b/pcsx2/PAD/Windows/DeviceEnumerator.h deleted file mode 100644 index d7f663e9ee..0000000000 --- a/pcsx2/PAD/Windows/DeviceEnumerator.h +++ /dev/null @@ -1,16 +0,0 @@ -/* PCSX2 - PS2 Emulator for PCs - * Copyright (C) 2002-2020 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 . - */ - -void EnumDevices(int hideDXXinput); diff --git a/pcsx2/PAD/Windows/Diagnostics.cpp b/pcsx2/PAD/Windows/Diagnostics.cpp deleted file mode 100644 index ae0fa2eaaa..0000000000 --- a/pcsx2/PAD/Windows/Diagnostics.cpp +++ /dev/null @@ -1,160 +0,0 @@ -/* PCSX2 - PS2 Emulator for PCs - * Copyright (C) 2002-2020 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 . - */ - -#include "PrecompiledHeader.h" -#include "common/RedtapeWindows.h" - -#include "Global.h" -#include "DeviceEnumerator.h" -#include "KeyboardQueue.h" - -#include "resource_pad.h" -#include "InputManager.h" -#include "WndProcEater.h" - -Device* dev; - -INT_PTR CALLBACK DiagDialog(HWND hWnd, unsigned int uMsg, WPARAM wParam, LPARAM lParam) -{ - HWND hWndList = GetDlgItem(hWnd, IDC_DIAG_LIST); - if (dev) - { - static int fullRefresh; - switch (uMsg) - { - case WM_INITDIALOG: - { - fullRefresh = 1; - SetWindowText(hWnd, dev->displayName); - LVCOLUMNW c; - c.mask = LVCF_TEXT | LVCF_WIDTH; - c.cx = 151; - c.pszText = L"Control"; - ListView_InsertColumn(hWndList, 0, &c); - c.cx = 90; - c.pszText = L"Value"; - ListView_InsertColumn(hWndList, 1, &c); - ListView_DeleteAllItems(hWndList); - LVITEM item; - item.mask = LVIF_TEXT; - item.iSubItem = 0; - for (int i = 0; i < dev->numVirtualControls; i++) - { - item.pszText = dev->GetVirtualControlName(dev->virtualControls + i); - item.iItem = i; - ListView_InsertItem(hWndList, &item); - } - SetTimer(hWnd, 1, 200, 0); - } - //break; - case WM_TIMER: - { - hWndButtonProc.SetWndHandle(hWndList); - InitInfo info = {0, 1, hWnd, &hWndButtonProc}; - dm->Update(&info); - LVITEMW item; - item.mask = LVIF_TEXT; - item.iSubItem = 1; - //ShowWindow(hWndList, 0); - //LockWindowUpdate(hWndList); - if (!dev->active) - { - item.pszText = L"N/A"; - for (int i = 0; i < dev->numVirtualControls; i++) - { - item.iItem = i; - ListView_SetItem(hWndList, &item); - } - fullRefresh = 1; - } - else - { - for (int i = 0; i < dev->numVirtualControls; i++) - { - if (fullRefresh || dev->virtualControlState[i] != dev->oldVirtualControlState[i]) - { - - VirtualControl* c = dev->virtualControls + i; - wchar_t temp[50]; - int val = dev->virtualControlState[i]; - if (c->uid & (UID_POV)) - { - wsprintfW(temp, L"%i", val); - } - else - { - wchar_t* sign = L""; - if (val < 0) - { - sign = L"-"; - val = -val; - } - val = (int)floor(0.5 + val * 1000.0 / (double)FULLY_DOWN); - wsprintfW(temp, L"%s%i.%03i", sign, val / 1000, val % 1000); - } - item.pszText = temp; - item.iItem = i; - ListView_SetItem(hWndList, &item); - } - } - dm->PostRead(); - fullRefresh = 0; - } - //LockWindowUpdate(0); - //ShowWindow(hWndList, 1); - //UpdateWindow(hWnd); - } - break; - case WM_NOTIFY: - { - NMLVKEYDOWN* n = (NMLVKEYDOWN*)lParam; - // Don't always get the notification when testing DirectInput non-keyboard devices. - // Don't get it (Or want it) when testing keyboards. - if (n->hdr.idFrom != IDC_DIAG_LIST || n->hdr.code != LVN_KEYDOWN || n->wVKey != VK_ESCAPE) - break; - } - case WM_ACTIVATE: - if (uMsg == WM_ACTIVATE && wParam != WA_INACTIVE) - break; - break; - case WM_CLOSE: - KillTimer(hWnd, 1); - dm->ReleaseInput(); - // Prevents reaching this branch again. - dev = 0; - EndDialog(hWnd, 1); - break; - default: - break; - } - } - return 0; -} - -void Diagnose(int id, HWND hWnd) -{ - // init = 0; - dev = dm->devices[id]; - for (int i = 0; i < dm->numDevices; i++) - { - if (i != id) - dm->DisableDevice(i); - // Shouldn't be needed. - else - dm->EnableDevice(i); - } - DialogBox(GetModuleHandle(nullptr), MAKEINTRESOURCE(IDD_DIAG), hWnd, DiagDialog); - ClearKeyQueue(); -} diff --git a/pcsx2/PAD/Windows/Diagnostics.h b/pcsx2/PAD/Windows/Diagnostics.h deleted file mode 100644 index b2bfd21bd8..0000000000 --- a/pcsx2/PAD/Windows/Diagnostics.h +++ /dev/null @@ -1,21 +0,0 @@ -/* PCSX2 - PS2 Emulator for PCs - * Copyright (C) 2002-2020 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 . - */ - -#ifndef DIAGNOSTICS_H -#define DIAGNOSTICS_H - -void Diagnose(int id, HWND hWnd); - -#endif diff --git a/pcsx2/PAD/Windows/DirectInput.cpp b/pcsx2/PAD/Windows/DirectInput.cpp deleted file mode 100644 index b42100c956..0000000000 --- a/pcsx2/PAD/Windows/DirectInput.cpp +++ /dev/null @@ -1,753 +0,0 @@ -/* PCSX2 - PS2 Emulator for PCs - * Copyright (C) 2002-2020 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 . - */ - -#include "PrecompiledHeader.h" -#include "Global.h" -#define DIRECTINPUT_VERSION 0x0800 -#include -#include "InputManager.h" -#include "PADConfig.h" - -#include "VKey.h" -#include "DirectInput.h" -#include "DeviceEnumerator.h" -#include "common/Pcsx2Defs.h" - -// All for getting GUIDs of XInput devices.... -#include -#include -// MS's code imports wmsstd.h, thus requiring the entire windows -// media SDK also be installed for a simple macro. This is -// simpler and less silly. -#ifndef SAFE_RELEASE -#define SAFE_RELEASE(p) \ - { \ - if (p) \ - { \ - (p)->Release(); \ - (p) = NULL; \ - } \ - } -#endif - -// Aka htons, without the winsock dependency. -inline static u16 flipShort(u16 s) -{ - return (s >> 8) | (s << 8); -} - -// Aka htonl, without the winsock dependency. -inline static u32 flipLong(u32 l) -{ - return (((u32)flipShort((u16)l)) << 16) | flipShort((u16)(l >> 16)); -} - -static void GUIDtoString(wchar_t* data, const GUID* pg) -{ - wsprintfW(data, L"%08X-%04X-%04X-%04X-%04X%08X", - pg->Data1, (u32)pg->Data2, (u32)pg->Data3, - flipShort(((u16*)pg->Data4)[0]), - flipShort(((u16*)pg->Data4)[1]), - flipLong(((u32*)pg->Data4)[1])); -} - -struct DirectInput8Data -{ - IDirectInput8* lpDI8; - int refCount; - int deviceCount; -}; - -DirectInput8Data di8d = {0, 0, 0}; - -IDirectInput8* GetDirectInput() -{ - if (!di8d.lpDI8) - { - if (FAILED(DirectInput8Create(GetModuleHandle(nullptr), 0x800, IID_IDirectInput8, (void**)&di8d.lpDI8, 0))) - return 0; - } - di8d.refCount++; - return di8d.lpDI8; -} -void ReleaseDirectInput() -{ - if (di8d.refCount) - { - di8d.refCount--; - if (!di8d.refCount) - { - di8d.lpDI8->Release(); - di8d.lpDI8 = 0; - } - } -} - -static int StringToGUID(GUID* pg, wchar_t* dataw) -{ - char data[100]; - if (wcslen(dataw) > 50) - return 0; - int w = 0; - while (dataw[w]) - { - data[w] = (char)dataw[w]; - w++; - } - data[w] = 0; - u32 temp[5]; - sscanf(data, "%08X-%04X-%04X-%04X-%04X%08X", - &pg->Data1, temp, temp + 1, - temp + 2, temp + 3, temp + 4); - pg->Data2 = (u16)temp[0]; - pg->Data3 = (u16)temp[1]; - ((u16*)pg->Data4)[0] = flipShort((u16)temp[2]); - ((u16*)pg->Data4)[1] = flipShort((u16)temp[3]); - ((u32*)pg->Data4)[1] = flipLong(temp[4]); - return 1; -} - - -struct DI8Effect -{ - IDirectInputEffect* die; - int scale; -}; - -BOOL CALLBACK EnumDeviceObjectsCallback(LPCDIDEVICEOBJECTINSTANCE lpddoi, LPVOID pvRef); -BOOL CALLBACK EnumEffectsCallback(LPCDIEFFECTINFOW pdei, LPVOID pvRef); -class DirectInputDevice : public Device -{ -public: - DI8Effect* diEffects; - - IDirectInputDevice8* did; - GUID guidInstance; - DirectInputDevice(DeviceType type, IDirectInputDevice8* did, wchar_t* displayName, wchar_t* instanceID, wchar_t* productID, GUID guid) - : Device(DI, type, displayName, instanceID, productID) - , guidInstance(guid) - { - diEffects = 0; - this->did = 0; - did->EnumEffects(EnumEffectsCallback, this, DIEFT_ALL); - did->EnumObjects(EnumDeviceObjectsCallback, this, DIDFT_ALL); - did->Release(); - } - - void SetEffect(ForceFeedbackBinding* binding, unsigned char force) - { - int index = 0; - if (!diEffects) - { - return; - } - for (int port = 0; port < 2; port++) - { - for (int slot = 0; slot < 4; slot++) - { - int padtype = config.padConfigs[port][slot].type; - unsigned int diff = binding - pads[port][slot][padtype].ffBindings; - if (diff < (unsigned int)pads[port][slot][padtype].numFFBindings) - { - index += diff; - port = 2; - break; - } - index += pads[port][slot][padtype].numFFBindings; - } - } - IDirectInputEffect* die = diEffects[index].die; - if (die) - { - DIEFFECT dieffect; - memset(&dieffect, 0, sizeof(dieffect)); - union - { - DIPERIODIC periodic; - DIRAMPFORCE ramp; - DICONSTANTFORCE constant; - }; - - dieffect.dwSize = sizeof(dieffect); - dieffect.lpvTypeSpecificParams = &periodic; - int magnitude = abs((int)((force * 10000 * (__int64)diEffects[index].scale) / BASE_SENSITIVITY / 255)); - if (magnitude > 10000) - magnitude = 10000; - int type = ffEffectTypes[binding->effectIndex].type; - if (type == EFFECT_CONSTANT) - { - dieffect.cbTypeSpecificParams = sizeof(DICONSTANTFORCE); - constant.lMagnitude = magnitude; - } - else if (type == EFFECT_PERIODIC) - { - dieffect.cbTypeSpecificParams = sizeof(DIPERIODIC); - periodic.dwMagnitude = 0; - periodic.lOffset = magnitude; - periodic.dwPhase = 0; - periodic.dwPeriod = 2000000; - } - else if (type == EFFECT_RAMP) - { - dieffect.cbTypeSpecificParams = sizeof(DIRAMPFORCE); - ramp.lEnd = ramp.lStart = magnitude; - } - dieffect.dwFlags = DIEFF_CARTESIAN | DIEFF_OBJECTIDS; - dieffect.dwDuration = 2000000; - die->SetParameters(&dieffect, DIEP_TYPESPECIFICPARAMS | DIEP_START); - } - } - - int Activate(InitInfo* initInfo) - { - int i; - IDirectInput8* di8 = GetDirectInput(); - Deactivate(); - if (!di8) - return 0; - if (DI_OK != di8->CreateDevice(guidInstance, &did, 0)) - { - ReleaseDirectInput(); - did = 0; - return 0; - } - - { - DIOBJECTDATAFORMAT* formats = (DIOBJECTDATAFORMAT*)calloc(numPhysicalControls, sizeof(DIOBJECTDATAFORMAT)); - for (i = 0; i < numPhysicalControls; i++) - { - formats[i].dwType = physicalControls[i].type | DIDFT_MAKEINSTANCE(physicalControls[i].id); - formats[i].dwOfs = 4 * i; - } - DIDATAFORMAT format; - format.dwSize = sizeof(format); - format.dwDataSize = 4 * numPhysicalControls; - format.dwObjSize = sizeof(DIOBJECTDATAFORMAT); - format.dwFlags = 0; - format.dwNumObjs = numPhysicalControls; - format.rgodf = formats; - int res = did->SetDataFormat(&format); - for (i = 0; i < numPhysicalControls; i++) - { - if (physicalControls[i].type == ABSAXIS) - { - DIPROPRANGE prop; - prop.diph.dwHeaderSize = sizeof(DIPROPHEADER); - prop.diph.dwSize = sizeof(DIPROPRANGE); - prop.diph.dwObj = formats[i].dwType; - prop.diph.dwHow = DIPH_BYID; - prop.lMin = -FULLY_DOWN; - prop.lMax = FULLY_DOWN; - did->SetProperty(DIPROP_RANGE, &prop.diph); - - // May do something like this again, if there's any need. - /* - if (FAILED(DI->did->SetProperty(DIPROP_RANGE, &prop.diph))) { - if (FAILED(DI->did->GetProperty(DIPROP_RANGE, &prop.diph))) { - // ???? - DI->objects[i].min = prop.lMin; - DI->objects[i].max = prop.lMax; - continue; - } - } - DI->objects[i].min = prop.lMin; - DI->objects[i].max = prop.lMax; - //*/ - } - } - free(formats); - } - // Note: Have to use hWndTop to properly hide cursor for mouse device. - if (type == OTHER) - { - did->SetCooperativeLevel(initInfo->hWndTop, DISCL_BACKGROUND | DISCL_EXCLUSIVE); - } - else if (type == KEYBOARD) - { - did->SetCooperativeLevel(initInfo->hWndTop, DISCL_FOREGROUND); - } - else - { - did->SetCooperativeLevel(initInfo->hWndTop, DISCL_FOREGROUND | DISCL_EXCLUSIVE); - } - if (did->Acquire() != DI_OK) - { - did->Release(); - did = 0; - ReleaseDirectInput(); - return 0; - } - AllocState(); - int count = GetFFBindingCount(); - diEffects = (DI8Effect*)calloc(count, sizeof(DI8Effect)); - i = 0; - for (int port = 0; port < 2; port++) - { - for (int slot = 0; slot < 4; slot++) - { - int padtype = config.padConfigs[port][slot].type; - int subIndex = i; - for (int j = 0; j < pads[port][slot][padtype].numFFBindings; j++) - { - ForceFeedbackBinding* b = 0; - b = &pads[port][slot][padtype].ffBindings[i - subIndex]; - ForceFeedbackEffectType* eff = ffEffectTypes + b->effectIndex; - GUID guid; - if (!StringToGUID(&guid, eff->effectID)) - continue; - - DIEFFECT dieffect; - memset(&dieffect, 0, sizeof(dieffect)); - dieffect.dwSize = sizeof(dieffect); - dieffect.dwFlags = DIEFF_CARTESIAN | DIEFF_OBJECTIDS; - dieffect.dwDuration = 1000000; - dieffect.dwGain = 10000; - dieffect.dwTriggerButton = DIEB_NOTRIGGER; - union - { - DIPERIODIC pediodic; - DIRAMPFORCE ramp; - DICONSTANTFORCE constant; - } stuff = {0, 0, 0, 0}; - - if (eff->type == EFFECT_CONSTANT) - { - dieffect.cbTypeSpecificParams = sizeof(DICONSTANTFORCE); - } - else if (eff->type == EFFECT_PERIODIC) - { - dieffect.cbTypeSpecificParams = sizeof(DIPERIODIC); - } - else if (eff->type == EFFECT_RAMP) - { - dieffect.cbTypeSpecificParams = sizeof(DIRAMPFORCE); - } - dieffect.lpvTypeSpecificParams = &stuff; - - int maxForce = 0; - int numAxes = 0; - int* axes = (int*)malloc(sizeof(int) * 3 * numFFAxes); - DWORD* axisIDs = (DWORD*)(axes + numFFAxes); - LONG* dirList = (LONG*)(axisIDs + numFFAxes); - dieffect.rgdwAxes = axisIDs; - dieffect.rglDirection = dirList; - for (int k = 0; k < numFFAxes; k++) - { - if (b->axes[k].force) - { - int force = abs(b->axes[k].force); - if (force > maxForce) - { - maxForce = force; - } - axes[numAxes] = k; - axisIDs[numAxes] = ffAxes[k].id; - dirList[numAxes] = b->axes[k].force; - numAxes++; - } - } - if (!numAxes) - { - free(axes); - continue; - } - dieffect.cAxes = numAxes; - diEffects[i].scale = maxForce; - if (!SUCCEEDED(did->CreateEffect(guid, &dieffect, &diEffects[i].die, 0))) - { - diEffects[i].die = 0; - diEffects[i].scale = 0; - } - - free(axes); - axes = 0; - i++; - } - } - } - active = 1; - return 1; - } - - int Update() - { - if (!active) - return 0; - if (numPhysicalControls) - { - HRESULT res = did->Poll(); - // ?? - if ((res != DI_OK && res != DI_NOEFFECT) || - DI_OK != did->GetDeviceState(4 * numPhysicalControls, physicalControlState)) - { - Deactivate(); - return 0; - } - for (int i = 0; i < numPhysicalControls; i++) - { - if (physicalControls[i].type & RELAXIS) - { - physicalControlState[i] *= (FULLY_DOWN / 3); - } - else if (physicalControls[i].type & BUTTON) - { - physicalControlState[i] = (physicalControlState[i] & 0x80) * FULLY_DOWN / 128; - } - } - } - return 1; - } - - int GetFFBindingCount() - { - int count = 0; - for (int port = 0; port < 2; port++) - { - for (int slot = 0; slot < 4; slot++) - { - int padtype = config.padConfigs[port][slot].type; - count += pads[port][slot][padtype].numFFBindings; - } - } - return count; - } - - void Deactivate() - { - FreeState(); - if (diEffects) - { - int count = GetFFBindingCount(); - for (int i = 0; i < count; i++) - { - if (diEffects[i].die) - { - diEffects[i].die->Stop(); - diEffects[i].die->Release(); - } - } - free(diEffects); - diEffects = 0; - } - if (active) - { - did->Unacquire(); - did->Release(); - ReleaseDirectInput(); - did = 0; - active = 0; - } - } - - ~DirectInputDevice() - { - } -}; - -BOOL CALLBACK EnumEffectsCallback(LPCDIEFFECTINFOW pdei, LPVOID pvRef) -{ - DirectInputDevice* did = (DirectInputDevice*)pvRef; - EffectType type; - int diType = DIEFT_GETTYPE(pdei->dwEffType); - if (diType == DIEFT_CONSTANTFORCE) - { - type = EFFECT_CONSTANT; - } - else if (diType == DIEFT_RAMPFORCE) - { - type = EFFECT_RAMP; - } - else if (diType == DIEFT_PERIODIC) - { - type = EFFECT_PERIODIC; - } - else - { - return DIENUM_CONTINUE; - } - wchar_t guidString[50]; - GUIDtoString(guidString, &pdei->guid); - did->AddFFEffectType(pdei->tszName, guidString, type); - - return DIENUM_CONTINUE; -} - -BOOL CALLBACK EnumDeviceObjectsCallback(LPCDIDEVICEOBJECTINSTANCE lpddoi, LPVOID pvRef) -{ - DirectInputDevice* did = (DirectInputDevice*)pvRef; - if (lpddoi->dwType & DIDFT_FFACTUATOR) - { - did->AddFFAxis(lpddoi->tszName, lpddoi->dwType); - } - - ControlType type; - - if (lpddoi->dwType & DIDFT_POV) - type = POV; - else if (lpddoi->dwType & DIDFT_ABSAXIS) - type = ABSAXIS; - else if (lpddoi->dwType & DIDFT_RELAXIS) - type = RELAXIS; - else if (lpddoi->dwType & DIDFT_PSHBUTTON) - type = PSHBTN; - else if (lpddoi->dwType & DIDFT_TGLBUTTON) - type = TGLBTN; - else - return DIENUM_CONTINUE; - // If too many objects, ignore extra buttons. - if ((did->numPhysicalControls > 255 && DIDFT_GETINSTANCE(lpddoi->dwType) > 255) && (type & (DIDFT_PSHBUTTON | DIDFT_TGLBUTTON))) - { - int i; - for (i = did->numPhysicalControls - 1; i > did->numPhysicalControls - 4; i--) - { - if (!lpddoi->tszName[0]) - break; - const wchar_t* s1 = lpddoi->tszName; - const wchar_t* s2 = did->physicalControls[i].name; - while (*s1 && *s1 == *s2) - { - s1++; - s2++; - } - // If perfect match with one of last 4 names, break. - if (!*s1 && !*s2) - break; - - while (s1 != lpddoi->tszName && (s1[-1] >= '0' && s1[-1] <= '9')) - s1--; - int check = 0; - while (*s1 >= '0' && *s1 <= '9') - { - check = check * 10 + *s1 - '0'; - s1++; - } - while (*s2 >= '0' && *s2 <= '9') - { - s2++; - } - // If perfect match other than final number > 30, then break. - // takes care of "button xx" case without causing issues with F keys. - if (!*s1 && !*s2 && check > 30) - break; - } - if (i != did->numPhysicalControls - 4) - { - return DIENUM_CONTINUE; - } - } - - int vkey = 0; - if (lpddoi->tszName[0] && did->type == KEYBOARD) - { - for (u32 i = 0; i < 256; i++) - { - wchar_t* t = GetVKStringW((u8)i); - if (!wcsicmp(lpddoi->tszName, t)) - { - vkey = i; - break; - } - } - } - did->AddPhysicalControl(type, DIDFT_GETINSTANCE(lpddoi->dwType), vkey, lpddoi->tszName); - return DIENUM_CONTINUE; -} - -// Evil code from MS's site. If only they'd just made a way to get -// an XInput device's GUID directly in the first place... -BOOL IsXInputDevice(const GUID* pGuidProductFromDirectInput) -{ - IWbemLocator* pIWbemLocator = NULL; - IEnumWbemClassObject* pEnumDevices = NULL; - IWbemClassObject* pDevices[20] = {0}; - IWbemServices* pIWbemServices = NULL; - BSTR bstrNamespace = NULL; - BSTR bstrDeviceID = NULL; - BSTR bstrClassName = NULL; - DWORD uReturned = 0; - bool bIsXinputDevice = false; - UINT iDevice = 0; - VARIANT var; - HRESULT hr; - - // CoInit if needed - hr = CoInitialize(NULL); - bool bCleanupCOM = SUCCEEDED(hr); - - // Create WMI - hr = CoCreateInstance(__uuidof(WbemLocator), - NULL, - CLSCTX_INPROC_SERVER, - __uuidof(IWbemLocator), - (LPVOID*)&pIWbemLocator); - if (FAILED(hr) || pIWbemLocator == NULL) - goto LCleanup; - - bstrNamespace = SysAllocString(L"\\\\.\\root\\cimv2"); - if (bstrNamespace == NULL) - goto LCleanup; - bstrClassName = SysAllocString(L"Win32_PNPEntity"); - if (bstrClassName == NULL) - goto LCleanup; - bstrDeviceID = SysAllocString(L"DeviceID"); - if (bstrDeviceID == NULL) - goto LCleanup; - - // Connect to WMI - hr = pIWbemLocator->ConnectServer(bstrNamespace, NULL, NULL, 0L, - 0L, NULL, NULL, &pIWbemServices); - if (FAILED(hr) || pIWbemServices == NULL) - goto LCleanup; - - // Switch security level to IMPERSONATE. - CoSetProxyBlanket(pIWbemServices, RPC_C_AUTHN_WINNT, RPC_C_AUTHZ_NONE, NULL, - RPC_C_AUTHN_LEVEL_CALL, RPC_C_IMP_LEVEL_IMPERSONATE, NULL, EOAC_NONE); - - hr = pIWbemServices->CreateInstanceEnum(bstrClassName, 0, NULL, &pEnumDevices); - if (FAILED(hr) || pEnumDevices == NULL) - goto LCleanup; - - // Loop over all devices - for (;;) - { - // Get 20 at a time - hr = pEnumDevices->Next(10000, 20, pDevices, &uReturned); - if (FAILED(hr)) - goto LCleanup; - if (uReturned == 0) - break; - - for (iDevice = 0; iDevice < uReturned; iDevice++) - { - // For each device, get its device ID - hr = pDevices[iDevice]->Get(bstrDeviceID, 0L, &var, NULL, NULL); - if (SUCCEEDED(hr) && var.vt == VT_BSTR && var.bstrVal != NULL) - { - // Check if the device ID contains "IG_". If it does, then it's an XInput device - // This information can not be found from DirectInput - if (wcsstr(var.bstrVal, L"IG_")) - { - // If it does, then get the VID/PID from var.bstrVal - DWORD dwPid = 0, dwVid = 0; - WCHAR* strVid = wcsstr(var.bstrVal, L"VID_"); - if (strVid) - { - dwVid = wcstoul(strVid + 4, 0, 16); - } - WCHAR* strPid = wcsstr(var.bstrVal, L"PID_"); - if (strPid) - { - dwPid = wcstoul(strPid + 4, 0, 16); - } - - // Compare the VID/PID to the DInput device - DWORD dwVidPid = MAKELONG(dwVid, dwPid); - if (dwVidPid == pGuidProductFromDirectInput->Data1) - { - bIsXinputDevice = true; - goto LCleanup; - } - } - } - SAFE_RELEASE(pDevices[iDevice]); - } - } - -LCleanup: - if (bstrNamespace) - SysFreeString(bstrNamespace); - if (bstrDeviceID) - SysFreeString(bstrDeviceID); - if (bstrClassName) - SysFreeString(bstrClassName); - for (iDevice = 0; iDevice < 20; iDevice++) - SAFE_RELEASE(pDevices[iDevice]); - SAFE_RELEASE(pEnumDevices); - SAFE_RELEASE(pIWbemLocator); - SAFE_RELEASE(pIWbemServices); - - if (bCleanupCOM) - CoUninitialize(); - - return bIsXinputDevice; -} - - -struct DeviceEnumInfo -{ - IDirectInput8* di8; - int ignoreXInput; -}; - -BOOL CALLBACK EnumCallback(LPCDIDEVICEINSTANCE lpddi, LPVOID pvRef) -{ - IDirectInput8* di8 = ((DeviceEnumInfo*)pvRef)->di8; - const wchar_t* name; - wchar_t temp[40]; - //if (((DeviceEnumInfo*)pvRef)->ignoreXInput && lpddi-> - if (lpddi->tszInstanceName[0]) - { - name = lpddi->tszInstanceName; - } - else if (lpddi->tszProductName[0]) - { - name = lpddi->tszProductName; - } - else - { - wsprintfW(temp, L"Device %i", di8d.deviceCount); - name = temp; - } - di8d.deviceCount++; - wchar_t* fullName = (wchar_t*)malloc((wcslen(name) + 4) * sizeof(wchar_t)); - wsprintf(fullName, L"DX %s", name); - wchar_t instanceID[100]; - wchar_t productID[100]; - GUIDtoString(instanceID, &lpddi->guidInstance); - GUIDtoString(productID, &lpddi->guidProduct); - DeviceType type = OTHER; - if ((lpddi->dwDevType & 0xFF) == DI8DEVTYPE_KEYBOARD) - { - type = KEYBOARD; - } - - IDirectInputDevice8* did; - if (DI_OK == di8->CreateDevice(lpddi->guidInstance, &did, 0)) - { - DirectInputDevice* dev = new DirectInputDevice(type, did, fullName, instanceID, productID, lpddi->guidInstance); - if (dev->numPhysicalControls || dev->numFFAxes) - { - dm->AddDevice(dev); - } - else - { - delete dev; - } - } - free(fullName); - return DIENUM_CONTINUE; -} - -void EnumDirectInputDevices(int ignoreXInput) -{ - DeviceEnumInfo enumInfo; - enumInfo.di8 = GetDirectInput(); - if (!enumInfo.di8) - return; - enumInfo.ignoreXInput = ignoreXInput; - di8d.deviceCount = 0; - enumInfo.di8->EnumDevices(DI8DEVCLASS_ALL, EnumCallback, &enumInfo, DIEDFL_ATTACHEDONLY); - ReleaseDirectInput(); -} diff --git a/pcsx2/PAD/Windows/DirectInput.h b/pcsx2/PAD/Windows/DirectInput.h deleted file mode 100644 index 8b1fb5aab6..0000000000 --- a/pcsx2/PAD/Windows/DirectInput.h +++ /dev/null @@ -1,16 +0,0 @@ -/* PCSX2 - PS2 Emulator for PCs - * Copyright (C) 2002-2020 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 . - */ - -void EnumDirectInputDevices(int ignoreXInput); diff --git a/pcsx2/PAD/Windows/DualShock3.cpp b/pcsx2/PAD/Windows/DualShock3.cpp deleted file mode 100644 index e90c0fa342..0000000000 --- a/pcsx2/PAD/Windows/DualShock3.cpp +++ /dev/null @@ -1,317 +0,0 @@ -/* PCSX2 - PS2 Emulator for PCs - * Copyright (C) 2002-2020 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 . - */ - -#include "PrecompiledHeader.h" -#include "Global.h" -#include "InputManager.h" -#include "PADConfig.h" - -#include "HidDevice.h" - -#define VID 0x054c -#define PID 0x0268 - -//Sixaxis driver commands. -//All commands must be sent via WriteFile with 49-byte buffer containing output report. -//Byte 0 indicates reportId and must always be 0. -//Byte 1 indicates some command, supported values are specified below. - -//This command allows to set user LEDs. -//Bytes 5,6.7.8 contain mode for corresponding LED: 0 value means LED is OFF, 1 means LEDs in ON and 2 means LEDs is flashing. -//Bytes 9-16 specify 64-bit LED flash period in 100 ns units if some LED is flashing, otherwise not used. -#define SIXAXIS_COMMAND_SET_LEDS 1 -//This command allows to set left and right motors. -//Byte 5 is right motor duration (0-255) and byte 6, if not zero, activates right motor. Zero value disables right motor. -//Byte 7 is left motor duration (0-255) and byte 8 is left motor amplitude (0-255). -#define SIXAXIS_COMMAND_SET_MOTORS 2 -//This command allows to block/unblock setting device LEDs by applications. -//Byte 5 is used as parameter - any non-zero value blocks LEDs, zero value will unblock LEDs. -#define SIXAXIS_COMMAND_BLOCK_LEDS 3 -//This command refreshes driver settings. No parameters used. -//When sixaxis driver loads it reads 'CurrentDriverSetting' binary value from 'HKLM\System\CurrentControlSet\Services\sixaxis\Parameters' registry key. -//If the key is not present then default values are used. Sending this command forces sixaxis driver to re-read the registry and update driver settings. -#define SIXAXIS_COMMAND_REFRESH_DRIVER_SETTING 9 -//This command clears current bluetooth pairing. No parameters used. -#define SIXAXIS_COMMAND_CLEAR_PAIRING 10 - -int DualShock3Possible() -{ - return 1; -} - -int CharToAxis(unsigned char c) -{ - return ((c - 128) * FULLY_DOWN) >> 7; -} - -int CharToButton(unsigned char c) -{ - const int v = (int)c + ((unsigned int)c >> 7); - return (v * FULLY_DOWN) >> 8; -} - -class DualShock3Device : public Device -{ - // Cached last vibration values by pad and motor. - // Need this, as only one value is changed at a time. - int ps2Vibration[2][4][2]; - int vibration[2]; - -public: - int index; - HANDLE hFile; - unsigned char getState[49]; - - DualShock3Device(int index, wchar_t* name, wchar_t* path) - : Device(DS3, OTHER, name, path, L"DualShock 3") - { - memset(ps2Vibration, 0, sizeof(ps2Vibration)); - vibration[0] = vibration[1] = 0; - this->index = index; - int i; - for (i = 0; i < 16; i++) - { - if (i != 14 && i != 15 && i != 8 && i != 9) - { - AddPhysicalControl(PRESSURE_BTN, i, 0); - } - else - { - AddPhysicalControl(PSHBTN, i, 0); - } - } - for (; i < 23; i++) - { - AddPhysicalControl(ABSAXIS, i, 0); - } - AddFFAxis(L"Big Motor", 0); - AddFFAxis(L"Small Motor", 1); - AddFFEffectType(L"Constant Effect", L"Constant", EFFECT_CONSTANT); - hFile = INVALID_HANDLE_VALUE; - } - - wchar_t* GetPhysicalControlName(PhysicalControl* c) - { - const static wchar_t* names[] = { - L"Square", - L"Cross", - L"Circle", - L"Triangle", - L"R1", - L"L1", - L"R2", - L"L2", - L"R3", - L"L3", - L"Left", - L"Down", - L"Right", - L"Up", - L"Start", - L"Select", - L"L-Stick X", - L"L-Stick Y", - L"R-Stick X", - L"R-Stick Y", - L"Left/Right Tilt", - L"Forward/Back Tilt", - L"???", - }; - unsigned int i = (unsigned int)(c - physicalControls); - if (i < sizeof(names) / sizeof(names[0])) - { - return (wchar_t*)names[i]; - } - return Device::GetPhysicalControlName(c); - } - - int Activate(InitInfo* initInfo) - { - if (active) - Deactivate(); - hFile = CreateFileW(instanceID, GENERIC_READ | GENERIC_WRITE, FILE_SHARE_READ | FILE_SHARE_WRITE, NULL, OPEN_EXISTING, 0, NULL); - if (hFile == INVALID_HANDLE_VALUE) - { - Deactivate(); - return 0; - } - active = 1; - AllocState(); - return 1; - } - - int Update() - { - if (!active) - return 0; - - unsigned char Buffer[50]; - - Buffer[0] = 0; //reportId - - if (!HidD_GetFeature(hFile, - Buffer, - sizeof(Buffer))) - { - Deactivate(); - return 0; - } - - unsigned char* getState = &Buffer[1]; - - physicalControlState[0] = CharToButton(getState[25]); //Square - physicalControlState[1] = CharToButton(getState[24]); //Cross - physicalControlState[2] = CharToButton(getState[23]); //Circle - physicalControlState[3] = CharToButton(getState[22]); //Triangle - physicalControlState[4] = CharToButton(getState[21]); //R1 - physicalControlState[5] = CharToButton(getState[20]); //L1 - physicalControlState[6] = CharToButton(getState[19]); //R2 - physicalControlState[7] = CharToButton(getState[18]); //L2 - physicalControlState[10] = CharToButton(getState[17]); //Dpad Left - physicalControlState[11] = CharToButton(getState[16]); //Dpad Down - physicalControlState[12] = CharToButton(getState[15]); //Dpad Right - physicalControlState[13] = CharToButton(getState[14]); //Dpad Up - physicalControlState[8] = ((getState[2] & 4) / 4) * FULLY_DOWN; //R3 - physicalControlState[9] = ((getState[2] & 2) / 2) * FULLY_DOWN; //L3 - physicalControlState[15] = ((getState[2] & 1) / 1) * FULLY_DOWN; //SELECT - physicalControlState[14] = ((getState[2] & 8) / 8) * FULLY_DOWN; //START - physicalControlState[16] = CharToAxis(getState[6]); //Left Stick X - physicalControlState[17] = CharToAxis(getState[7]); //Left Stick Y - physicalControlState[18] = CharToAxis(getState[8]); //Right Stick X - physicalControlState[19] = CharToAxis(getState[9]); //Right Stick Y - //Compared to libusb all sensor values on sixaxis driver are little-endian and X axis is inversed - physicalControlState[20] = CharToAxis(128 - getState[41]); //Accel X (Left/Right Tilt) - physicalControlState[21] = CharToAxis(getState[43] + 128); //Accel Y (Forward/Back Tilt) - physicalControlState[22] = CharToAxis(getState[45] + 128); //Accel Z - - return 1; - } - - void SetEffects(unsigned char port, unsigned int slot, unsigned char motor, unsigned char force) - { - ps2Vibration[port][slot][motor] = force; - vibration[0] = vibration[1] = 0; - for (int p = 0; p < 2; p++) - { - for (int s = 0; s < 4; s++) - { - int padtype = config.padConfigs[p][s].type; - for (int i = 0; i < pads[p][s][padtype].numFFBindings; i++) - { - // Technically should also be a *65535/BASE_SENSITIVITY, but that's close enough to 1 for me. - ForceFeedbackBinding* ffb = &pads[p][s][padtype].ffBindings[i]; - vibration[0] += (int)((ffb->axes[0].force * (__int64)ps2Vibration[p][s][ffb->motor]) / 255); - vibration[1] += (int)((ffb->axes[1].force * (__int64)ps2Vibration[p][s][ffb->motor]) / 255); - } - } - } - - unsigned char outputReport[49]; - - //Clear all data and set reportId to 0 - memset(outputReport, 0, sizeof(outputReport)); - - //This is command for sixaxis driver to set motors - outputReport[1] = SIXAXIS_COMMAND_SET_MOTORS; - - outputReport[5] = 0x50; //right_duration - - outputReport[6] = (unsigned char)(vibration[1] >= FULLY_DOWN / 2); //right_motor_on - - outputReport[7] = 0x50; //left_duration - - int bigForce = vibration[0] * 256 / FULLY_DOWN; - if (bigForce > 255) - bigForce = 255; - - outputReport[8] = (unsigned char)bigForce; //left_motor_force - - DWORD lpNumberOfBytesWritten = 0; - - WriteFile(hFile, outputReport, sizeof(outputReport), &lpNumberOfBytesWritten, NULL); - } - - void SetEffect(ForceFeedbackBinding* binding, unsigned char force) - { - PadBindings pBackup = pads[0][0][0]; - pads[0][0][0].ffBindings = binding; - pads[0][0][0].numFFBindings = 1; - SetEffects(0, 0, binding->motor, 255); - pads[0][0][0] = pBackup; - } - - void Deactivate() - { - if (hFile != INVALID_HANDLE_VALUE) - { - CancelIo(hFile); - CloseHandle(hFile); - hFile = INVALID_HANDLE_VALUE; - } - - memset(ps2Vibration, 0, sizeof(ps2Vibration)); - vibration[0] = vibration[1] = 0; - - FreeState(); - active = 0; - } - - ~DualShock3Device() - { - } -}; - -void EnumDualShock3s() -{ - HidDeviceInfo* foundDevs = 0; - - int numDevs = FindHids(&foundDevs, VID, PID); - if (!numDevs) - return; - int index = 0; - for (int i = 0; i < numDevs; i++) - { - if (foundDevs[i].caps.FeatureReportByteLength == 50 && - foundDevs[i].caps.OutputReportByteLength == 49) - { - HANDLE hDevice = CreateFileW(foundDevs[i].path, - GENERIC_READ | GENERIC_WRITE, - FILE_SHARE_READ | FILE_SHARE_WRITE, - NULL, - OPEN_EXISTING, - 0, NULL); - if (hDevice != INVALID_HANDLE_VALUE) - { - unsigned char Buffer[50]; - - Buffer[0] = 0; //reportId - - if (HidD_GetFeature(hDevice, - Buffer, - sizeof(Buffer))) - { - wchar_t temp[100]; - wsprintfW(temp, L"DualShock 3 #%i", index + 1); - dm->AddDevice(new DualShock3Device(index, temp, foundDevs[i].path)); - index++; - } - - CloseHandle(hDevice); - } - } - free(foundDevs[i].path); - } - free(foundDevs); -} diff --git a/pcsx2/PAD/Windows/DualShock3.h b/pcsx2/PAD/Windows/DualShock3.h deleted file mode 100644 index 152d6dec9a..0000000000 --- a/pcsx2/PAD/Windows/DualShock3.h +++ /dev/null @@ -1,21 +0,0 @@ -/* PCSX2 - PS2 Emulator for PCs - * Copyright (C) 2002-2020 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 . - */ - -int DualShock3Possible(); -void EnumDualShock3s(); - -// May move elsewhere in the future. -int InitLibUsb(); -void UninitLibUsb(); diff --git a/pcsx2/PAD/Windows/Global.h b/pcsx2/PAD/Windows/Global.h deleted file mode 100644 index 6287746489..0000000000 --- a/pcsx2/PAD/Windows/Global.h +++ /dev/null @@ -1,125 +0,0 @@ -/* PCSX2 - PS2 Emulator for PCs - * Copyright (C) 2002-2020 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 . - */ - -#pragma once - -#include -#include -#include -#include -#include -#include "common/Console.h" -#include "common/RedtapeWindows.h" -#include -#include - -#include -#include -#include -#include -#include -#include -#include - -#include "../Gamepad.h" - -struct HostKeyEvent; - -#ifdef __linux__ -// Seriously why there is no standard -#include "stdint.h" -typedef uint32_t DWORD; -typedef uint16_t USHORT; -#ifndef __INTEL_COMPILER -typedef int64_t __int64; -#endif - -#define MAX_PATH (256) // random value - -#include - -#define VK_SHIFT XK_Shift_L -#define VK_LSHIFT XK_Shift_L -#define VK_RSHIFT XK_Shift_R -#define VK_LMENU XK_Menu -#define VK_RMENU XK_Menu -#define VK_MENU XK_Menu -#define VK_CONTROL XK_Control_L -#define VK_TAB XK_Tab -#define VK_ESCAPE XK_Escape -#define VK_F4 XK_F4 - -#include -#include - -template -void wsprintfW(Array& buf, const wchar_t* format, ...) -{ - va_list a; - va_start(a, format); - - vswprintf(buf, sizeof(buf) / sizeof(buf[0]), format, a); - - va_end(a); -} - -template -void wsprintf(Array& buf, const wchar_t* format, ...) -{ - va_list a; - va_start(a, format); - - vswprintf(buf, sizeof(buf) / sizeof(buf[0]), format, a); - - va_end(a); -} - -static inline int wcsicmp(const wchar_t* w1, const wchar_t* w2) -{ - // I didn't find a way to put ignore case ... - return wcscmp(w1, w2); -} - -#include -static inline unsigned int timeGetTime() -{ - struct timeval now; - gettimeofday(&now, NULL); - uint64_t ms = (now.tv_usec / 1000) + ((uint64_t)now.tv_sec * 1000); - return (ms & 0xFFFFFFFF); // MS code is u32 ... -} - -#include "Utilities/Dependencies.h" -#include "Utilities/StringHelpers.h" -#include "Utilities/Path.h" - -#include - -extern Display* GSdsp; -extern Window GSwin; - -#endif - -#ifdef _MSC_VER -#include -#include -#include -#include -// Only needed for DBT_DEVNODES_CHANGED -#include -#endif - -// Needed for config screen -void GetNameAndVersionString(wchar_t* out); diff --git a/pcsx2/PAD/Windows/HidDevice.cpp b/pcsx2/PAD/Windows/HidDevice.cpp deleted file mode 100644 index 984ac1167d..0000000000 --- a/pcsx2/PAD/Windows/HidDevice.cpp +++ /dev/null @@ -1,85 +0,0 @@ -/* PCSX2 - PS2 Emulator for PCs - * Copyright (C) 2002-2020 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 . - */ - -#include "PrecompiledHeader.h" -#include "Global.h" -#include "HidDevice.h" - -int FindHids(HidDeviceInfo** foundDevs, int vid, int pid) -{ - GUID GUID_DEVINTERFACE_HID; - int numFoundDevs = 0; - *foundDevs = 0; - HidD_GetHidGuid(&GUID_DEVINTERFACE_HID); - HDEVINFO hdev = SetupDiGetClassDevs(&GUID_DEVINTERFACE_HID, 0, 0, DIGCF_PRESENT | DIGCF_DEVICEINTERFACE); - if (hdev != INVALID_HANDLE_VALUE) - { - SP_DEVICE_INTERFACE_DATA devInterfaceData; - devInterfaceData.cbSize = sizeof(SP_DEVICE_INTERFACE_DATA); - for (int i = 0; SetupDiEnumDeviceInterfaces(hdev, 0, &GUID_DEVINTERFACE_HID, i, &devInterfaceData); i++) - { - - DWORD size = 0; - SetupDiGetDeviceInterfaceDetail(hdev, &devInterfaceData, 0, 0, &size, 0); - if (GetLastError() != ERROR_INSUFFICIENT_BUFFER || !size) - continue; - SP_DEVICE_INTERFACE_DETAIL_DATA* devInterfaceDetails = (SP_DEVICE_INTERFACE_DETAIL_DATA*)malloc(size); - if (!devInterfaceDetails) - continue; - - devInterfaceDetails->cbSize = sizeof(*devInterfaceDetails); - SP_DEVINFO_DATA devInfoData; - devInfoData.cbSize = sizeof(SP_DEVINFO_DATA); - - if (!SetupDiGetDeviceInterfaceDetail(hdev, &devInterfaceData, devInterfaceDetails, size, &size, &devInfoData)) - continue; - - HANDLE hfile = CreateFile(devInterfaceDetails->DevicePath, GENERIC_READ | GENERIC_WRITE, FILE_SHARE_READ | FILE_SHARE_WRITE, 0, OPEN_EXISTING, 0, 0); - if (hfile != INVALID_HANDLE_VALUE) - { - HIDD_ATTRIBUTES attributes; - attributes.Size = sizeof(attributes); - if (HidD_GetAttributes(hfile, &attributes)) - { - if (attributes.VendorID == vid && attributes.ProductID == pid) - { - PHIDP_PREPARSED_DATA pData; - HIDP_CAPS caps; - if (HidD_GetPreparsedData(hfile, &pData)) - { - if (HidP_GetCaps(pData, &caps) == HIDP_STATUS_SUCCESS) - { - if (numFoundDevs % 32 == 0) - { - *foundDevs = (HidDeviceInfo*)realloc(*foundDevs, sizeof(HidDeviceInfo) * (32 + numFoundDevs)); - } - HidDeviceInfo* dev = &foundDevs[0][numFoundDevs++]; - dev->caps = caps; - dev->vid = attributes.VendorID; - dev->pid = attributes.ProductID; - dev->path = wcsdup(devInterfaceDetails->DevicePath); - } - HidD_FreePreparsedData(pData); - } - } - } - CloseHandle(hfile); - } - free(devInterfaceDetails); - } - SetupDiDestroyDeviceInfoList(hdev); - } - return numFoundDevs; -} diff --git a/pcsx2/PAD/Windows/HidDevice.h b/pcsx2/PAD/Windows/HidDevice.h deleted file mode 100644 index 9cde5735aa..0000000000 --- a/pcsx2/PAD/Windows/HidDevice.h +++ /dev/null @@ -1,32 +0,0 @@ -/* PCSX2 - PS2 Emulator for PCs - * Copyright (C) 2002-2020 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 . - */ - -#ifndef HID_DEVICE_H -#define HID_DEVICE_H - -#include -#include - -struct HidDeviceInfo -{ - HIDP_CAPS caps; - wchar_t* path; - unsigned short vid; - unsigned short pid; -}; - -int FindHids(HidDeviceInfo** foundDevs, int vid, int pid); - -#endif diff --git a/pcsx2/PAD/Windows/InputManager.cpp b/pcsx2/PAD/Windows/InputManager.cpp deleted file mode 100644 index d9c22c5a3c..0000000000 --- a/pcsx2/PAD/Windows/InputManager.cpp +++ /dev/null @@ -1,783 +0,0 @@ -/* PCSX2 - PS2 Emulator for PCs - * Copyright (C) 2002-2020 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 . - */ - -#include "PrecompiledHeader.h" -#include "Global.h" -#include "InputManager.h" -#include "KeyboardQueue.h" -#include "PADConfig.h" -#include "Host.h" - -InputDeviceManager* dm = 0; - -InputDeviceManager::InputDeviceManager() -{ - memset(this, 0, sizeof(*this)); -} - -void InputDeviceManager::ClearDevices() -{ - for (int i = 0; i < numDevices; i++) - { - delete devices[i]; - } - free(devices); - devices = 0; - numDevices = 0; -} - -InputDeviceManager::~InputDeviceManager() -{ - ClearDevices(); -} - -Device::Device(DeviceAPI api, DeviceType d, const wchar_t* displayName, const wchar_t* instanceID, const wchar_t* productID) -{ - memset(pads, 0, sizeof(pads)); - this->api = api; - type = d; - this->displayName = wcsdup(displayName); - if (instanceID) - this->instanceID = wcsdup(instanceID); - else - this->instanceID = wcsdup(displayName); - this->productID = 0; - if (productID) - this->productID = wcsdup(productID); - active = 0; - attached = 1; - enabled = 0; - -#ifdef _MSC_VER - hWndProc = 0; -#endif - - virtualControls = 0; - numVirtualControls = 0; - virtualControlState = 0; - oldVirtualControlState = 0; - - physicalControls = 0; - numPhysicalControls = 0; - physicalControlState = 0; - - ffEffectTypes = 0; - numFFEffectTypes = 0; - ffAxes = 0; - numFFAxes = 0; -} - -void Device::FreeState() -{ - if (virtualControlState) - free(virtualControlState); - virtualControlState = 0; - oldVirtualControlState = 0; - physicalControlState = 0; -} - -Device::~Device() -{ - Deactivate(); - // Generally called by deactivate, but just in case... - FreeState(); - int i; - for (int port = 0; port < 2; port++) - { - for (int slot = 0; slot < 4; slot++) - { - for (int padtype = 0; padtype < numPadTypes; padtype++) - { - free(pads[port][slot][padtype].bindings); - for (i = 0; i < pads[port][slot][padtype].numFFBindings; i++) - { - free(pads[port][slot][padtype].ffBindings[i].axes); - } - free(pads[port][slot][padtype].ffBindings); - } - } - } - free(virtualControls); - - for (i = numPhysicalControls - 1; i >= 0; i--) - { - if (physicalControls[i].name) - free(physicalControls[i].name); - } - free(physicalControls); - - free(displayName); - free(instanceID); - free(productID); - if (ffAxes) - { - for (i = 0; i < numFFAxes; i++) - { - free(ffAxes[i].displayName); - } - free(ffAxes); - } - if (ffEffectTypes) - { - for (i = 0; i < numFFEffectTypes; i++) - { - free(ffEffectTypes[i].displayName); - free(ffEffectTypes[i].effectID); - } - free(ffEffectTypes); - } -} - -void Device::AddFFEffectType(const wchar_t* displayName, const wchar_t* effectID, EffectType type) -{ - ffEffectTypes = (ForceFeedbackEffectType*)realloc(ffEffectTypes, sizeof(ForceFeedbackEffectType) * (numFFEffectTypes + 1)); - ffEffectTypes[numFFEffectTypes].displayName = wcsdup(displayName); - ffEffectTypes[numFFEffectTypes].effectID = wcsdup(effectID); - ffEffectTypes[numFFEffectTypes].type = type; - numFFEffectTypes++; -} - -void Device::AddFFAxis(const wchar_t* displayName, int id) -{ - ffAxes = (ForceFeedbackAxis*)realloc(ffAxes, sizeof(ForceFeedbackAxis) * (numFFAxes + 1)); - ffAxes[numFFAxes].id = id; - ffAxes[numFFAxes].displayName = wcsdup(displayName); - numFFAxes++; - int bindingsExist = 0; - for (int port = 0; port < 2; port++) - { - for (int slot = 0; slot < 4; slot++) - { - for (int padtype = 0; padtype < numPadTypes; padtype++) - { - for (int i = 0; i < pads[port][slot][padtype].numFFBindings; i++) - { - ForceFeedbackBinding* b = pads[port][slot][padtype].ffBindings + i; - b->axes = (AxisEffectInfo*)realloc(b->axes, sizeof(AxisEffectInfo) * (numFFAxes)); - memset(b->axes + (numFFAxes - 1), 0, sizeof(AxisEffectInfo)); - bindingsExist = 1; - } - } - } - } - // Generally the case when not loading a binding file. - if (!bindingsExist) - { - int i = numFFAxes - 1; - ForceFeedbackAxis temp = ffAxes[i]; - while (i && temp.id < ffAxes[i - 1].id) - { - ffAxes[i] = ffAxes[i - 1]; - i--; - } - ffAxes[i] = temp; - } -} - -void Device::AllocState() -{ - FreeState(); - virtualControlState = (int*)calloc(numVirtualControls + numVirtualControls + numPhysicalControls, sizeof(int)); - oldVirtualControlState = virtualControlState + numVirtualControls; - physicalControlState = oldVirtualControlState + numVirtualControls; -} - -void Device::FlipState() -{ - memcpy(oldVirtualControlState, virtualControlState, sizeof(int) * numVirtualControls); -} - -void Device::PostRead() -{ - FlipState(); -} - -void Device::CalcVirtualState() -{ - for (int i = 0; i < numPhysicalControls; i++) - { - PhysicalControl* c = physicalControls + i; - int index = c->baseVirtualControlIndex; - int val = physicalControlState[i]; - if (c->type & BUTTON) - { - virtualControlState[index] = val; - // DirectInput keyboard events only. - if (this->api == DI && this->type == KEYBOARD) - { - if (!(virtualControlState[index] >> 15) != !(oldVirtualControlState[index] >> 15) && c->vkey) - { - // Check for alt-F4 to avoid toggling skip mode incorrectly. - if (c->vkey == VK_F4) - { - int i; - for (i = 0; i < numPhysicalControls; i++) - { - if (virtualControlState[physicalControls[i].baseVirtualControlIndex] && - (physicalControls[i].vkey == VK_MENU || - physicalControls[i].vkey == VK_RMENU || - physicalControls[i].vkey == VK_LMENU)) - { - break; - } - } - if (i < numPhysicalControls) - continue; - } - HostKeyEvent::Type event = HostKeyEvent::Type::KeyPressed; - if (!(virtualControlState[index] >> 15)) - event = HostKeyEvent::Type::KeyReleased; - QueueKeyEvent(c->vkey, event); - } - } - } - else if (c->type & ABSAXIS) - { - virtualControlState[index] = (val + FULLY_DOWN) / 2; - // Positive. Overkill. - virtualControlState[index + 1] = (val & ~(val >> 31)); - // Negative - virtualControlState[index + 2] = (-val & (val >> 31)); - } - else if (c->type & RELAXIS) - { - int delta = val - oldVirtualControlState[index]; - virtualControlState[index] = val; - // Positive - virtualControlState[index + 1] = (delta & ~(delta >> 31)); - // Negative - virtualControlState[index + 2] = (-delta & (delta >> 31)); - } - else if (c->type & POV) - { - virtualControlState[index] = val; - int iSouth = 0; - int iEast = 0; - if ((unsigned int)val <= 37000) - { - double angle = val * (3.141592653589793 / 18000.0); - double East = sin(angle); - double South = -cos(angle); - // Normalize so greatest direction is 1. - double mul = FULLY_DOWN / std::max(fabs(South), fabs(East)); - - iEast = (int)floor(East * mul + 0.5); - iSouth = (int)floor(South * mul + 0.5); - } - // N - virtualControlState[index + 1] = (-iSouth & (iSouth >> 31)); - // S - virtualControlState[index + 3] = (iSouth & ~(iSouth >> 31)); - // E - virtualControlState[index + 2] = (iEast & ~(iEast >> 31)); - // W - virtualControlState[index + 4] = (-iEast & (iEast >> 31)); - } - } -} - -VirtualControl* Device::GetVirtualControl(unsigned int uid) -{ - for (int i = 0; i < numVirtualControls; i++) - { - if (virtualControls[i].uid == uid) - return virtualControls + i; - } - return 0; -} - -VirtualControl* Device::AddVirtualControl(unsigned int uid, int physicalControlIndex) -{ - // Not really necessary, as always call AllocState when activated, but doesn't hurt. - FreeState(); - - if (numVirtualControls % 16 == 0) - { - virtualControls = (VirtualControl*)realloc(virtualControls, sizeof(VirtualControl) * (numVirtualControls + 16)); - } - VirtualControl* c = virtualControls + numVirtualControls; - - c->uid = uid; - c->physicalControlIndex = physicalControlIndex; - - numVirtualControls++; - return c; -} - -PhysicalControl* Device::AddPhysicalControl(ControlType type, unsigned short id, unsigned short vkey, const wchar_t* name) -{ - // Not really necessary, as always call AllocState when activated, but doesn't hurt. - FreeState(); - - if (numPhysicalControls % 16 == 0) - { - physicalControls = (PhysicalControl*)realloc(physicalControls, sizeof(PhysicalControl) * (numPhysicalControls + 16)); - } - PhysicalControl* control = physicalControls + numPhysicalControls; - - memset(control, 0, sizeof(PhysicalControl)); - control->type = type; - control->id = id; - if (name) - control->name = wcsdup(name); - control->baseVirtualControlIndex = numVirtualControls; - unsigned int uid = id | (type << 16); - if (type & BUTTON) - { - AddVirtualControl(uid, numPhysicalControls); - control->vkey = vkey; - } - else if (type & AXIS) - { - AddVirtualControl(uid | UID_AXIS, numPhysicalControls); - AddVirtualControl(uid | UID_AXIS_POS, numPhysicalControls); - AddVirtualControl(uid | UID_AXIS_NEG, numPhysicalControls); - } - else if (type & POV) - { - AddVirtualControl(uid | UID_POV, numPhysicalControls); - AddVirtualControl(uid | UID_POV_N, numPhysicalControls); - AddVirtualControl(uid | UID_POV_E, numPhysicalControls); - AddVirtualControl(uid | UID_POV_S, numPhysicalControls); - AddVirtualControl(uid | UID_POV_W, numPhysicalControls); - } - numPhysicalControls++; - return control; -} - -void Device::SetEffects(unsigned char port, unsigned int slot, unsigned char motor, unsigned char force) -{ - int padtype = config.padConfigs[port][slot].type; - for (int i = 0; i < pads[port][slot][padtype].numFFBindings; i++) - { - ForceFeedbackBinding* binding = pads[port][slot][padtype].ffBindings + i; - if (binding->motor == motor) - { - SetEffect(binding, force); - } - } -} - -wchar_t* GetDefaultControlName(unsigned short id, int type) -{ - static wchar_t name[20]; - if (type & BUTTON) - { - wsprintfW(name, L"Button %i", id); - } - else if (type & AXIS) - { - wsprintfW(name, L"Axis %i", id); - } - else if (type & POV) - { - wsprintfW(name, L"POV %i", id); - } - else - { - wcscpy(name, L"Unknown"); - } - return name; -} - -wchar_t* Device::GetVirtualControlName(VirtualControl* control) -{ - static wchar_t temp[100]; - wchar_t* baseName = 0; - if (control->physicalControlIndex >= 0) - { - baseName = physicalControls[control->physicalControlIndex].name; - if (!baseName) - baseName = GetPhysicalControlName(&physicalControls[control->physicalControlIndex]); - } - unsigned int uid = control->uid; - if (!baseName) - { - baseName = GetDefaultControlName(uid & 0xFFFF, (uid >> 16) & 0x1F); - } - uid &= 0xFF000000; - int len = (int)wcslen(baseName); - if (len > 99) - len = 99; - memcpy(temp, baseName, len * sizeof(wchar_t)); - temp[len] = 0; - if (uid) - { - if (len > 95) - len = 95; - wchar_t* out = temp + len; - if (uid == UID_AXIS_POS) - { - wcscpy(out, L" +"); - } - else if (uid == UID_AXIS_NEG) - { - wcscpy(out, L" -"); - } - else if (uid == UID_POV_N) - { - wcscpy(out, L" N"); - } - else if (uid == UID_POV_E) - { - wcscpy(out, L" E"); - } - else if (uid == UID_POV_S) - { - wcscpy(out, L" S"); - } - else if (uid == UID_POV_W) - { - wcscpy(out, L" W"); - } - } - return temp; -} - -wchar_t* Device::GetPhysicalControlName(PhysicalControl* control) -{ - if (control->name) - return control->name; - return GetDefaultControlName(control->id, control->type); -} - -void InputDeviceManager::AddDevice(Device* d) -{ - devices = (Device**)realloc(devices, sizeof(Device*) * (numDevices + 1)); - devices[numDevices++] = d; -} - -void InputDeviceManager::Update(InitInfo* info) -{ - for (int i = 0; i < numDevices; i++) - { - if (devices[i]->enabled) - { - if (!devices[i]->active) - { - if (!devices[i]->Activate(info) || !devices[i]->Update()) - continue; - devices[i]->CalcVirtualState(); - devices[i]->PostRead(); - } - if (devices[i]->Update()) - devices[i]->CalcVirtualState(); - } - } -} - -void InputDeviceManager::PostRead() -{ - for (int i = 0; i < numDevices; i++) - { - if (devices[i]->active) - devices[i]->PostRead(); - } -} - -Device* InputDeviceManager::GetActiveDevice(InitInfo* info, unsigned int* uid, int* index, int* value) -{ - int i, j; - Update(info); - int bestDiff = FULLY_DOWN / 2; - Device* bestDevice = 0; - for (i = 0; i < numDevices; i++) - { - if (devices[i]->active) - { - for (j = 0; j < devices[i]->numVirtualControls; j++) - { - if (devices[i]->virtualControlState[j] == devices[i]->oldVirtualControlState[j]) - continue; - if (devices[i]->virtualControls[j].uid & UID_POV) - continue; - // Fix for releasing button used to click on bind button - if (!((devices[i]->virtualControls[j].uid >> 16) & (POV | RELAXIS | ABSAXIS))) - { - if (abs(devices[i]->oldVirtualControlState[j]) > abs(devices[i]->virtualControlState[j])) - { - devices[i]->oldVirtualControlState[j] = 0; - } - } - int diff = abs(devices[i]->virtualControlState[j] - devices[i]->oldVirtualControlState[j]); - // Make it require a bit more work to bind relative axes. - if (((devices[i]->virtualControls[j].uid >> 16) & 0xFF) == RELAXIS) - { - diff = diff / 4 + 1; - } - // Less pressure needed to bind DS3/SCP buttons. - if ((devices[i]->api == DS3 || devices[i]->api == XINPUT) && (((devices[i]->virtualControls[j].uid >> 16) & 0xFF) & BUTTON)) - { - diff *= 4; - } - if (diff > bestDiff) - { - if (devices[i]->virtualControls[j].uid & UID_AXIS) - { - if ((((devices[i]->virtualControls[j].uid >> 16) & 0xFF) != ABSAXIS)) - continue; - // Very picky when binding entire axes. Prefer binding half-axes. - if (!((devices[i]->oldVirtualControlState[j] < FULLY_DOWN / 32 && devices[i]->virtualControlState[j] > FULLY_DOWN / 8) || - (devices[i]->oldVirtualControlState[j] > 31 * FULLY_DOWN / 32 && devices[i]->virtualControlState[j] < 7 * FULLY_DOWN / 8))) - { - continue; - } - } - else if ((((devices[i]->virtualControls[j].uid >> 16) & 0xFF) == ABSAXIS)) - { - if (devices[i]->oldVirtualControlState[j] > 15 * FULLY_DOWN / 16) - continue; - } - bestDiff = diff; - *uid = devices[i]->virtualControls[j].uid; - *index = j; - bestDevice = devices[i]; - if (value) - { - if ((devices[i]->virtualControls[j].uid >> 16) & RELAXIS) - { - *value = devices[i]->virtualControlState[j] - devices[i]->oldVirtualControlState[j]; - } - else - { - *value = devices[i]->virtualControlState[j]; - } - } - } - } - } - } - // Don't call when binding. - // PostRead(); - return bestDevice; -} - -void InputDeviceManager::ReleaseInput() -{ - for (int i = 0; i < numDevices; i++) - { - if (devices[i]->active) - devices[i]->Deactivate(); - } -} - -void InputDeviceManager::EnableDevices(DeviceType type, DeviceAPI api) -{ - for (int i = 0; i < numDevices; i++) - { - if (devices[i]->api == api && devices[i]->type == type) - { - EnableDevice(i); - } - } -} - -void InputDeviceManager::DisableAllDevices() -{ - for (int i = 0; i < numDevices; i++) - { - DisableDevice(i); - } -} - -void InputDeviceManager::DisableDevice(int index) -{ - devices[index]->enabled = 0; - if (devices[index]->active) - { - devices[index]->Deactivate(); - } -} - -ForceFeedbackEffectType* Device::GetForcefeedbackEffect(wchar_t* id) -{ - for (int i = 0; i < numFFEffectTypes; i++) - { - if (!wcsicmp(id, ffEffectTypes[i].effectID)) - { - return &ffEffectTypes[i]; - } - } - return 0; -} - -ForceFeedbackAxis* Device::GetForceFeedbackAxis(int id) -{ - for (int i = 0; i < numFFAxes; i++) - { - if (ffAxes[i].id == id) - return &ffAxes[i]; - } - return 0; -} - -void InputDeviceManager::CopyBindings(int numOldDevices, Device** oldDevices) -{ - int* oldMatches = (int*)malloc(sizeof(int) * numOldDevices); - int* matches = (int*)malloc(sizeof(int) * numDevices); - int i, j, port, slot; - Device *old, *dev; - for (i = 0; i < numDevices; i++) - { - matches[i] = -1; - } - for (i = 0; i < numOldDevices; i++) - { - oldMatches[i] = -2; - old = oldDevices[i]; - for (port = 0; port < 2; port++) - { - for (slot = 0; slot < 4; slot++) - { - for (int padtype = 0; padtype < numPadTypes; padtype++) - { - if (old->pads[port][slot][padtype].numBindings + old->pads[port][slot][padtype].numFFBindings) - { - // Means that there are bindings. - oldMatches[i] = -1; - } - } - } - } - } - // Loops through ids looking for match, from most specific to most general. - for (int id = 0; id < 3; id++) - { - for (i = 0; i < numOldDevices; i++) - { - if (oldMatches[i] >= 0) - continue; - for (j = 0; j < numDevices; j++) - { - if (matches[j] >= 0) - { - continue; - } - wchar_t* id1 = devices[j]->IDs[id]; - wchar_t* id2 = oldDevices[i]->IDs[id]; - if (!id1 || !id2) - { - continue; - } - if (!wcsicmp(id1, id2)) - { - matches[j] = i; - oldMatches[i] = j; - break; - } - } - } - } - - for (i = 0; i < numOldDevices; i++) - { - if (oldMatches[i] == -2) - continue; - old = oldDevices[i]; - if (oldMatches[i] < 0) - { - dev = new Device(old->api, old->type, old->displayName, old->instanceID, old->productID); - dev->attached = 0; - AddDevice(dev); - for (j = 0; j < old->numVirtualControls; j++) - { - VirtualControl* c = old->virtualControls + j; - dev->AddVirtualControl(c->uid, -1); - } - for (j = 0; j < old->numFFEffectTypes; j++) - { - ForceFeedbackEffectType* effect = old->ffEffectTypes + j; - dev->AddFFEffectType(effect->displayName, effect->effectID, effect->type); - } - for (j = 0; j < old->numFFAxes; j++) - { - ForceFeedbackAxis* axis = old->ffAxes + j; - dev->AddFFAxis(axis->displayName, axis->id); - } - // Just steal the old bindings directly when there's no matching device. - // Indices will be the same. - memcpy(dev->pads, old->pads, sizeof(old->pads)); - memset(old->pads, 0, sizeof(old->pads)); - } - else - { - dev = devices[oldMatches[i]]; - for (port = 0; port < 2; port++) - { - for (slot = 0; slot < 4; slot++) - { - for (int padtype = 0; padtype < numPadTypes; padtype++) - { - if (old->pads[port][slot][padtype].numBindings) - { - dev->pads[port][slot][padtype].bindings = (Binding*)malloc(old->pads[port][slot][padtype].numBindings * sizeof(Binding)); - for (int j = 0; j < old->pads[port][slot][padtype].numBindings; j++) - { - Binding* bo = old->pads[port][slot][padtype].bindings + j; - Binding* bn = dev->pads[port][slot][padtype].bindings + dev->pads[port][slot][padtype].numBindings; - VirtualControl* cn = dev->GetVirtualControl(old->virtualControls[bo->controlIndex].uid); - if (cn) - { - *bn = *bo; - bn->controlIndex = cn - dev->virtualControls; - dev->pads[port][slot][padtype].numBindings++; - } - } - } - if (old->pads[port][slot][padtype].numFFBindings) - { - dev->pads[port][slot][padtype].ffBindings = (ForceFeedbackBinding*)malloc(old->pads[port][slot][padtype].numFFBindings * sizeof(ForceFeedbackBinding)); - for (int j = 0; j < old->pads[port][slot][padtype].numFFBindings; j++) - { - ForceFeedbackBinding* bo = old->pads[port][slot][padtype].ffBindings + j; - ForceFeedbackBinding* bn = dev->pads[port][slot][padtype].ffBindings + dev->pads[port][slot][padtype].numFFBindings; - ForceFeedbackEffectType* en = dev->GetForcefeedbackEffect(old->ffEffectTypes[bo->effectIndex].effectID); - if (en) - { - *bn = *bo; - bn->effectIndex = en - dev->ffEffectTypes; - bn->axes = (AxisEffectInfo*)calloc(dev->numFFAxes, sizeof(AxisEffectInfo)); - for (int k = 0; k < old->numFFAxes; k++) - { - ForceFeedbackAxis* newAxis = dev->GetForceFeedbackAxis(old->ffAxes[k].id); - if (newAxis) - { - bn->axes[newAxis - dev->ffAxes] = bo->axes[k]; - } - } - dev->pads[port][slot][padtype].numFFBindings++; - } - } - } - } - } - } - } - } - free(oldMatches); - free(matches); -} - -void InputDeviceManager::SetEffect(unsigned char port, unsigned int slot, unsigned char motor, unsigned char force) -{ - for (int i = 0; i < numDevices; i++) - { - Device* dev = devices[i]; - if (dev->enabled && dev->numFFEffectTypes) - { - dev->SetEffects(port, slot, motor, force); - } - } -} diff --git a/pcsx2/PAD/Windows/InputManager.h b/pcsx2/PAD/Windows/InputManager.h deleted file mode 100644 index 640515fc76..0000000000 --- a/pcsx2/PAD/Windows/InputManager.h +++ /dev/null @@ -1,407 +0,0 @@ -/* PCSX2 - PS2 Emulator for PCs - * Copyright (C) 2002-2020 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 . - */ - -#ifndef INPUT_MANAGER_H -#define INPUT_MANAGER_H - -// Both of these are hard coded in a lot of places, so don't modify them. -// Base sensitivity means that a sensitivity of that corresponds to a factor of 1. -// Fully down means that value corresponds to a button being fully down (255). -// a value of 128 or more corresponds to that button being pressed, for binary -// values. -#define BASE_SENSITIVITY (1 << 16) -#define BASE_ANALOG_SENSITIVITY (87183) -#define FULLY_DOWN (1 << 16) - -#define DEFAULT_DEADZONE (BASE_SENSITIVITY * 201 / 1000) - -/* Idea is for this file and the associated cpp file to be Windows independent. - * Still more effort than it's worth to port to Linux, however. - */ - -enum PadType -{ - DisabledPad, - Dualshock2Pad, - GuitarPad, - PopnPad, - MousePad, - neGconPad, - numPadTypes // total number of PadTypes. Add new PadType above this line. -}; - -// Mostly match DirectInput8 values. Note that these are for physical controls. -// One physical axis maps to 3 virtual ones, and one physical POV control maps to -// 4 virtual ones. -enum ControlType -{ - NO_CONTROL = 0, - // Axes are ints. Relative axes are for mice, mice wheels, etc, - // and are always reported relative to their last value. - // Absolute axes range from -65536 to 65536 and are absolute positions, - // like for joysticks and pressure sensitive buttons. - RELAXIS = 1, - ABSAXIS = 2, - - // Buttons range from 0 to 65536. - PSHBTN = 4, - TGLBTN = 8, - - // POV controls are ints, values range from -1 to 36000. - // -1 means not pressed, otherwise it's an angle. - // For easy DirectInput compatibility, anything outside. - // that range is treated as -1 (Though 36000-37000 is treated - // like 0 to 1000, just in case). - POV = 16, - - // Pressure sensitive buttons. Only a different type because - // they have configurable dead zones, unlike push or toggle buttons. - PRESSURE_BTN = 32, -}; - -// Masks to determine button type. Don't need one for POV. -#define BUTTON (PSHBTN | TGLBTN | PRESSURE_BTN) -#define BINARY_BUTTON (PSHBTN | TGLBTN) -#define AXIS 3 - -struct Binding -{ - int controlIndex; - int command; - int sensitivity; - int deadZone; - int skipDeadZone; - unsigned char rapidFire; -}; - -#define UID_AXIS (1U << 31) -#define UID_POV (1 << 30) - -#define UID_AXIS_POS (1U << 24) -#define UID_AXIS_NEG (2U << 24) -#define UID_POV_N (3 << 24) -#define UID_POV_E (4 << 24) -#define UID_POV_S (5 << 24) -#define UID_POV_W (6 << 24) - -// One of these exists for each bindable object. -// Bindable objects consist of buttons, axis, pov controls, -// and individual axis/pov directions. Not that pov controls -// cannot actually be bound, but when trying to bind as an axis, -// all directions are assigned individually. -struct VirtualControl -{ - // Unique id for control, given device. Based on source control's id, - // source control type, axis/pov flags if it's a pov/axis (Rather than - // a button or a pov/axis control's individual button), and an index, - // if the control is split. - unsigned int uid; - // virtual key code. 0 if none. - int physicalControlIndex; -}; - -// Need one for each button, axis, and pov control. -// API-specific code creates the PhysicalControls and -// updates their state, standard function then populates -// the VirtualControls and queues the keyboard messages, if -// needed. -struct PhysicalControl -{ - // index of the first virtual control corresponding to this. - // Buttons have 1 virtual control, axes 3, and povs 5, all - // in a row. - int baseVirtualControlIndex; - ControlType type; - // id. Must be unique for control type. - // short so can be combined with other values to get - // uid for virtual controls. - unsigned short id; - unsigned short vkey; - wchar_t* name; -}; - -enum DeviceAPI -{ - NO_API = 0, - DI = 1, - WM = 2, - RAW = 3, - XINPUT = 4, - DS3 = 5, - // Not currently used. - LLHOOK = 6, - // Not a real API, obviously. Only used with keyboards, - // to ignore individual buttons. Wrapper itself takes care - // of ignoring bound keys. Otherwise, works normally. - IGNORE_KEYBOARD = 7, - // XXX - LNX_KEYBOARD = 16, - LNX_JOY = 17, -}; - -enum DeviceType -{ - NO_DEVICE = 0, - KEYBOARD = 1, - MOUSE = 2, - OTHER = 3 -}; - -enum EffectType -{ - EFFECT_CONSTANT, - EFFECT_PERIODIC, - EFFECT_RAMP -}; - -// force range sfrom -BASE_SENSITIVITY to BASE_SENSITIVITY. -// Order matches ForceFeedbackAxis order. force of 0 means to -// ignore that axis completely. Force of 1 or -1 means to initialize -// the axis with minimum force (Possibly 0 force), if applicable. -struct AxisEffectInfo -{ - int force; -}; - -struct ForceFeedbackBinding -{ - AxisEffectInfo* axes; - int effectIndex; - unsigned char motor; -}; - -// Bindings listed by effect, so I don't have to bother with -// indexing effects. -struct ForceFeedbackEffectType -{ - wchar_t* displayName; - // Because I'm lazy, can only have ASCII characters and no spaces. - wchar_t* effectID; - // constant, ramp, or periodic - EffectType type; -}; - - -struct ForceFeedbackAxis -{ - wchar_t* displayName; - int id; -}; - -// Used both for active devices and for sets of settings for devices. -// Way things work: -// LoadSettings() will delete all device info, then load settings to get -// one set of generic devices. Then I enumerate all devices. Then I merge -// them, moving settings from the generic devices to the enumerated ones. - -struct PadBindings -{ - Binding* bindings; - int numBindings; - ForceFeedbackBinding* ffBindings; - int numFFBindings; -}; - -class WndProcEater; - -struct InitInfo -{ - // 1 when binding key to ignore. - int bindingIgnore; - // 1 when binding. - int binding; - -#ifdef _MSC_VER - HWND hWndTop; - - // For config screen, need to eat button's message handling. - //HWND hWndButton; - - WndProcEater* hWndProc; -#else - // Linux equivalent to HWND - Display* GSdsp; - Window GSwin; -#endif -}; - - -// Mostly self-contained, but bindings are modified by config.cpp, to make -// updating the ListView simpler. -class Device -{ -public: - DeviceAPI api; - DeviceType type; - char active; - char attached; - // Based on input modes. - char enabled; - -#ifdef _MSC_VER - // Not all devices need to subclass the windproc, but most do so might as well - // put it here... --air - WndProcEater* hWndProc; -#endif - - union - { - // Allows for one loop to compare all 3 in order. - wchar_t* IDs[3]; - struct - { - // Same as DisplayName, when not given. Absolutely must be unique. - // Used for loading/saving controls. If matches, all other strings - // are ignored, so must be unique. - wchar_t* instanceID; - // Not required. Used when a device's instance id changes, doesn't have to - // be unique. For devices that can only have one instance, not needed. - wchar_t* productID; - - wchar_t* displayName; - }; - }; - - PadBindings pads[2][4][numPadTypes]; - - // Virtual controls. All basically act like pressure sensitivity buttons, with - // values between 0 and 2^16. 2^16 is fully down, 0 is up. Larger values - // are allowed, but *only* for absolute axes (Which don't support the flip checkbox). - // Each control on a device must have a unique id, used for binding. - VirtualControl* virtualControls; - int numVirtualControls; - - int* virtualControlState; - int* oldVirtualControlState; - - PhysicalControl* physicalControls; - int numPhysicalControls; - int* physicalControlState; - - ForceFeedbackEffectType* ffEffectTypes; - int numFFEffectTypes; - ForceFeedbackAxis* ffAxes; - int numFFAxes; - void AddFFAxis(const wchar_t* displayName, int id); - void AddFFEffectType(const wchar_t* displayName, const wchar_t* effectID, EffectType type); - - Device(DeviceAPI, DeviceType, const wchar_t* displayName, const wchar_t* instanceID = 0, const wchar_t* deviceID = 0); - virtual ~Device(); - - // Allocates memory for old and new state, sets everything to 0. - // all old states are in one array, buttons, axes, and then POVs. - // start of each section is int aligned. This makes it DirectInput - // compatible. - void AllocState(); - - // Doesn't actually flip. Copies current state to old state. - void FlipState(); - - // Frees state variables. - void FreeState(); - - ForceFeedbackEffectType* GetForcefeedbackEffect(wchar_t* id); - ForceFeedbackAxis* GetForceFeedbackAxis(int id); - - VirtualControl* GetVirtualControl(unsigned int uid); - - PhysicalControl* AddPhysicalControl(ControlType type, unsigned short id, unsigned short vkey, const wchar_t* name = 0); - VirtualControl* AddVirtualControl(unsigned int uid, int physicalControlIndex); - - virtual wchar_t* GetVirtualControlName(VirtualControl* c); - virtual wchar_t* GetPhysicalControlName(PhysicalControl* c); - - void CalcVirtualState(); - - virtual int Activate(InitInfo* args) - { - return 0; - } - - inline virtual void Deactivate() - { - FreeState(); - active = 0; - } - - // Default update proc. All that's needed for post-based APIs. - inline virtual int Update() - { - return active; - } - - // force is from -FULLY_DOWN to FULLY_DOWN. - // Either function can be overridden. Second one by default calls the first - // for every bound effect that's affected. - - // Note: Only used externally for binding, so if override the other one, can assume - // all other forces are currently 0. - inline virtual void SetEffect(ForceFeedbackBinding* binding, unsigned char force) {} - virtual void SetEffects(unsigned char port, unsigned int slot, unsigned char motor, unsigned char force); - - // Called after reading. Basically calls FlipState(). - // Some device types (Those that don't incrementally update) - // could call FlipState elsewhere, but this makes it simpler to ignore - // while binding. - virtual void PostRead(); -}; - -class InputDeviceManager -{ -public: - Device** devices; - int numDevices; - - void ClearDevices(); - - // When refreshing devices, back up old devices, then - // populate this with new devices, then call copy bindings. - // All old bindings are copied to matching devices. - - // When old devices are missing, I do a slightly more careful search - // using productIDs and then (in desperation) displayName. - // Finally create new dummy devices if no matches found. - void CopyBindings(int numDevices, Device** devices); - - - InputDeviceManager(); - ~InputDeviceManager(); - - void AddDevice(Device* d); - Device* GetActiveDevice(InitInfo* info, unsigned int* uid, int* index, int* value); - void Update(InitInfo* initInfo); - - // Called after reading state, after Update(). - void PostRead(); - - void SetEffect(unsigned char port, unsigned int slot, unsigned char motor, unsigned char force); - - // Update does this as needed. - // void GetInput(void *v); - void ReleaseInput(); - - void DisableDevice(int index); - inline void EnableDevice(int i) - { - devices[i]->enabled = 1; - } - - void EnableDevices(DeviceType type, DeviceAPI api); - void DisableAllDevices(); -}; - -extern InputDeviceManager* dm; - -#endif diff --git a/pcsx2/PAD/Windows/KeyboardQueue.cpp b/pcsx2/PAD/Windows/KeyboardQueue.cpp deleted file mode 100644 index 46a0b63e1c..0000000000 --- a/pcsx2/PAD/Windows/KeyboardQueue.cpp +++ /dev/null @@ -1,73 +0,0 @@ -/* PCSX2 - PS2 Emulator for PCs - * Copyright (C) 2002-2020 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 . - */ - -#include "PrecompiledHeader.h" -#include "Global.h" -// This is undoubtedly completely unnecessary. -#include "KeyboardQueue.h" - -static std::mutex cSection; - -#define EVENT_QUEUE_LEN 16 -// Actually points one beyond the last queued event. -static u8 lastQueuedEvent = 0; -static u8 nextQueuedEvent = 0; -static HostKeyEvent queuedEvents[EVENT_QUEUE_LEN]; - -void QueueKeyEvent(u32 key, HostKeyEvent::Type event) -{ - std::lock_guard lock(cSection); - - // Don't queue events if escape is on top of queue. This is just for safety - // purposes when a game is killing the emulator for whatever reason. - if (nextQueuedEvent == lastQueuedEvent || - queuedEvents[nextQueuedEvent].key != VK_ESCAPE || - queuedEvents[nextQueuedEvent].type != HostKeyEvent::Type::KeyPressed) - { - // Clear queue on escape down, bringing escape to front. May do something - // with shift/ctrl/alt and F-keys, later. - if (event == HostKeyEvent::Type::KeyPressed && key == VK_ESCAPE) - { - nextQueuedEvent = lastQueuedEvent; - } - - queuedEvents[lastQueuedEvent].key = key; - queuedEvents[lastQueuedEvent].type = event; - - lastQueuedEvent = (lastQueuedEvent + 1) % EVENT_QUEUE_LEN; - // If queue wrapped around, remove last element. - if (nextQueuedEvent == lastQueuedEvent) - { - nextQueuedEvent = (nextQueuedEvent + 1) % EVENT_QUEUE_LEN; - } - } -} - -int GetQueuedKeyEvent(HostKeyEvent* event) -{ - if (lastQueuedEvent == nextQueuedEvent) - return 0; - - std::lock_guard lock(cSection); - - *event = queuedEvents[nextQueuedEvent]; - nextQueuedEvent = (nextQueuedEvent + 1) % EVENT_QUEUE_LEN; - return 1; -} - -void ClearKeyQueue() -{ - lastQueuedEvent = nextQueuedEvent; -} diff --git a/pcsx2/PAD/Windows/KeyboardQueue.h b/pcsx2/PAD/Windows/KeyboardQueue.h deleted file mode 100644 index b352814eb8..0000000000 --- a/pcsx2/PAD/Windows/KeyboardQueue.h +++ /dev/null @@ -1,32 +0,0 @@ -/* PCSX2 - PS2 Emulator for PCs - * Copyright (C) 2002-2020 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 . - */ - - -// This entire thing isn't really needed, -// but takes little enough effort to be safe... - -#include "Host.h" - -void QueueKeyEvent(u32 key, HostKeyEvent::Type event); -int GetQueuedKeyEvent(HostKeyEvent* event); - -// Cleans up as well as clears queue. -void ClearKeyQueue(); - -#ifdef __linux__ -void R_QueueKeyEvent(const HostKeyEvent& event); -int R_GetQueuedKeyEvent(HostKeyEvent* event); -void R_ClearKeyQueue(); -#endif diff --git a/pcsx2/PAD/Windows/PAD.cpp b/pcsx2/PAD/Windows/PAD.cpp deleted file mode 100644 index 612551f123..0000000000 --- a/pcsx2/PAD/Windows/PAD.cpp +++ /dev/null @@ -1,1619 +0,0 @@ -/* PCSX2 - PS2 Emulator for PCs - * Copyright (C) 2002-2020 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 . - */ - -#include "PrecompiledHeader.h" -#include "Global.h" -#include "Common/WindowInfo.h" - -#include -#include -#include - -#include "resource_pad.h" -#include "InputManager.h" -#include "PADConfig.h" -#include "Host.h" - -#include "DeviceEnumerator.h" -#ifdef _MSC_VER -#include "WndProcEater.h" -#include "HidDevice.h" -#endif -#include "KeyboardQueue.h" -#include "DualShock3.h" - -#ifndef PCSX2_CORE -#include "gui/AppCoreThread.h" -#endif - -#define WMA_FORCE_UPDATE (WM_APP + 0x537) -#define FORCE_UPDATE_WPARAM ((WPARAM)0x74328943) -#define FORCE_UPDATE_LPARAM ((LPARAM)0x89437437) - -static WindowInfo s_window_info; - -#ifdef _WIN32 -HWND hWndTop; - -WndProcEater hWndGSProc; -WndProcEater hWndTopProc; - -// ButtonProc is used mostly by the Config panel for eating the procedures of the -// button with keyboard focus. -WndProcEater hWndButtonProc; -#endif - -// Keeps the various sources for Update polling (PADpoll, PADupdate, etc) from wreaking -// havoc on each other... -static std::mutex updateLock; -// Used to toggle mouse listening. -u8 miceEnabled; - -// 2 when both pads are initialized, 1 for one pad, etc. -int openCount = 0; - -int activeWindow = 0; -#ifdef _MSC_VER -int windowThreadId = 0; -int updateQueued = 0; -#endif - -u32 bufSize = 0; -unsigned char outBuf[50]; -unsigned char inBuf[50]; - -// windowThreadId = GetWindowThreadProcessId(hWnd, 0); - -#define MODE_PS1_MOUSE 0x12 -#define MODE_NEGCON 0x23 -#define MODE_DIGITAL 0x41 -#define MODE_ANALOG 0x73 -#define MODE_DS2_NATIVE 0x79 - -void DEBUG_TEXT_OUT(const char* text) -{ - if (!config.debug) - return; - - std::ofstream file("logs/padLog.txt", std::ios::app); - if (!file.good()) - return; - file << text; -} - -void DEBUG_NEW_SET() -{ - if (config.debug && bufSize > 1) - { - std::ofstream file("logs/padLog.txt", std::ios::app); - if (file.good()) - { - std::stringstream stream; - stream.setf(std::ios::hex, std::ios::basefield); - stream.setf(std::ios::uppercase); - stream.fill('0'); - - unsigned char* buffer[2] = {inBuf, outBuf}; - for (const auto& buf : buffer) - { - // Port/FF - stream << std::setw(2) << int(buf[0]); - // Active slots/Enabled (only relevant for multitap) - stream << " (" << std::setw(2) << int(buf[1]) << ')'; - - // Command/Response - for (u32 n = 2; n < bufSize; ++n) - stream << ' ' << std::setw(2) << int(buf[n]); - stream << '\n'; - } - stream << '\n'; - file << stream.rdbuf(); - } - } - bufSize = 0; -} - -inline void DEBUG_IN(unsigned char c) -{ - if (bufSize < sizeof(inBuf)) - inBuf[bufSize] = c; -} -inline void DEBUG_OUT(unsigned char c) -{ - if (bufSize < sizeof(outBuf)) - outBuf[bufSize++] = c; -} - -struct Stick -{ - int horiz; - int vert; -}; - -// Sum of states of all controls for a pad (Not including toggles). -struct ButtonSum -{ - int buttons[16]; - Stick sticks[2]; -}; - -#define PAD_SAVE_STATE_VERSION 5 - -// Freeze data, for a single pad. Basically has all pad state that -// a PS2 can set. -struct PadFreezeData -{ - // Digital / Analog / DS2 Native - u8 mode; - - u8 previousType; - - u8 modeLock; - - // In config mode - u8 config; - - u8 vibrate[8]; - u8 umask[3]; - - // Vibration indices. - u8 vibrateI[2]; - - // Last vibration value sent to controller. - // Only used so as not to call vibration - // functions when old and new values are both 0. - u8 currentVibrate[2]; - - // Next vibrate val to send to controller. If next and current are - // both 0, nothing is sent to the controller. Otherwise, it's sent - // on every update. - u8 nextVibrate[2]; -}; - -class Pad : public PadFreezeData -{ -public: - // Current pad state. - ButtonSum sum; - // State of locked buttons. Already included by sum, used - // as initial value of sum. - ButtonSum lockedSum; - - // Flags for which controls (buttons or axes) are locked, if any. - DWORD lockedState; - - // Used to keep track of which pads I'm running. - // Note that initialized pads *can* be disabled. - // I keep track of state of non-disabled non-initialized - // pads, but should never be asked for their state. - u8 initialized; - - // Set to 1 if the state of this pad has been updated since its state - // was last queried. - char stateUpdated; - - // initialized and not disabled (and mtap state for slots > 0). - u8 enabled; -} pads[2][4]; - -// Active slots for each port. -int slots[2]; -// Which ports we're running on. -int portInitialized[2]; - -// Force value to be from 0 to 255. -u8 Cap(int i) -{ - if (i < 0) - return 0; - if (i > 255) - return 255; - return (u8)i; -} - -inline void ReleaseModifierKeys() -{ - QueueKeyEvent(VK_SHIFT, HostKeyEvent::Type::KeyReleased); - QueueKeyEvent(VK_MENU, HostKeyEvent::Type::KeyReleased); - QueueKeyEvent(VK_CONTROL, HostKeyEvent::Type::KeyReleased); -} - -// RefreshEnabledDevices() enables everything that can potentially -// be bound to, as well as the "Ignore keyboard" device. -// -// This enables everything that input should be read from while the -// emulator is running. Takes into account mouse and focus state -// and which devices have bindings for enabled pads. Releases -// keyboards if window is not focused. Releases game devices if -// background monitoring is not checked. -// And releases games if not focused and config.background is not set. -void UpdateEnabledDevices(int updateList = 0) -{ - // Enable all devices I might want. Can ignore the rest. - RefreshEnabledDevices(updateList); - // Figure out which pads I'm getting input for. - for (int port = 0; port < 2; port++) - { - for (int slot = 0; slot < 4; slot++) - { - if (slot > 0 && !config.multitap[port]) - { - pads[port][slot].enabled = 0; - } - else - { - pads[port][slot].enabled = pads[port][slot].initialized && config.padConfigs[port][slot].type != DisabledPad; - } - } - } - for (int i = 0; i < dm->numDevices; i++) - { - Device* dev = dm->devices[i]; - - if (!dev->enabled) - continue; - if (!dev->attached) - { - dm->DisableDevice(i); - continue; - } - - // Disable ignore keyboard if don't have focus or there are no keys to ignore. - if (dev->api == IGNORE_KEYBOARD) - { - if ((config.keyboardApi == NO_API || !dev->pads[0][0][0].numBindings) || !activeWindow) - { - dm->DisableDevice(i); - } - continue; - } - // Keep for PCSX2 keyboard shotcuts, unless unfocused. - if (dev->type == KEYBOARD) - { - if (!activeWindow) - dm->DisableDevice(i); - } - // Keep for cursor hiding consistency, unless unfocused. - // miceEnabled tracks state of mouse enable/disable button, not if mouse API is set to disabled. - else if (dev->type == MOUSE) - { - if (!miceEnabled || !activeWindow) - dm->DisableDevice(i); - } - else if (!activeWindow && !config.background) - dm->DisableDevice(i); - else - { - int numActiveBindings = 0; - for (int port = 0; port < 2; port++) - { - for (int slot = 0; slot < 4; slot++) - { - int padtype = config.padConfigs[port][slot].type; - if (pads[port][slot].enabled) - { - numActiveBindings += dev->pads[port][slot][padtype].numBindings + dev->pads[port][slot][padtype].numFFBindings; - } - } - } - if (!numActiveBindings) - dm->DisableDevice(i); - } - } -} - -void AddForce(ButtonSum* sum, u8 cmd, int delta = 255) -{ - if (!delta) - return; - if (cmd < 0x14) - { - sum->buttons[cmd - 0x10] += delta; - } - // D-pad. Command numbering is based on ordering of digital values. - else if (cmd < 0x18) - { - sum->buttons[cmd - 0x08] += delta; - } - else if (cmd < 0x20) - { - sum->buttons[cmd - 0x10 - 4] += delta; - } - // Left stick. - else if (cmd < 0x24) - { - if (cmd == 0x20) - { // Up - sum->sticks[1].vert -= delta; - } - else if (cmd == 0x21) - { // Right - sum->sticks[1].horiz += delta; - } - else if (cmd == 0x22) - { // Down - sum->sticks[1].vert += delta; - } - else if (cmd == 0x23) - { // Left - sum->sticks[1].horiz -= delta; - } - } - // Right stick. - else if (cmd < 0x28) - { - if (cmd == 0x24) - { // Up - sum->sticks[0].vert -= delta; - } - else if (cmd == 0x25) - { // Right - sum->sticks[0].horiz += delta; - } - else if (cmd == 0x26) - { // Down - sum->sticks[0].vert += delta; - } - else if (cmd == 0x27) - { // Left - sum->sticks[0].horiz -= delta; - } - } -} - -void ProcessButtonBinding(Binding* b, ButtonSum* sum, int value) -{ - if (value < b->deadZone || value == 0) - { - return; - } - if (b->skipDeadZone > b->deadZone) - { - value = std::min((int)(((__int64)value * (FULLY_DOWN - (__int64)b->skipDeadZone)) / FULLY_DOWN) + b->skipDeadZone, FULLY_DOWN); - } - - if (b->command == 0x2D) - { // Turbo key - static unsigned int LastCheck = 0; - unsigned int t = timeGetTime(); - if (t - LastCheck < 300) - return; - QueueKeyEvent(VK_TAB, HostKeyEvent::Type::KeyPressed); - LastCheck = t; - } - - int sensitivity = b->sensitivity; - if (sensitivity < 0) - { - sensitivity = -sensitivity; - value = (1 << 16) - value; - } - if (value < 0) - return; - - /* Note: Value ranges of FULLY_DOWN, and sensitivity of - * BASE_SENSITIVITY corresponds to an axis/button being exactly fully down. - * Math in next line takes care of those two conditions, rounding as necessary. - * Done using __int64s because overflows will occur when - * sensitivity > BASE_SENSITIVITY and/or value > FULLY_DOWN. Latter only happens - * for relative axis. - */ - int force = (int)((((sensitivity * (255 * (__int64)value)) + BASE_SENSITIVITY / 2) / BASE_SENSITIVITY + FULLY_DOWN / 2) / FULLY_DOWN); - AddForce(sum, b->command, force); -} - -// Restricts d-pad/analog stick values to be from -255 to 255 and button values to be from 0 to 255. -// With D-pad in DS2 native mode, the negative and positive ranges are both independently from 0 to 255, -// which is why I use 9 bits of all sticks. For left and right sticks, I have to remove a bit before sending. -void CapSum(ButtonSum* sum) -{ - int i; - for (i = 0; i < 2; i++) - { - int div = std::max(abs(sum->sticks[i].horiz), abs(sum->sticks[i].vert)); - if (div > 255) - { - sum->sticks[i].horiz = sum->sticks[i].horiz * 255 / div; - sum->sticks[i].vert = sum->sticks[i].vert * 255 / div; - } - } - for (i = 0; i < 16; i++) - { - sum->buttons[i] = Cap(sum->buttons[i]); - } -} - -// Counter similar to stateUpdated for each pad, except used for PADkeyEvent instead. -// Only matters when GS thread updates is disabled (Just like summed pad values -// for pads beyond the first slot). - -// Values, in order, correspond to PADkeyEvent, PADupdate(0), PADupdate(1), and -// WndProc(WMA_FORCE_UPDATE). Last is always 0. -char padReadKeyUpdated[4] = {0, 0, 0, 0}; - -#define LOCK_DIRECTION 2 -#define LOCK_BUTTONS 4 -#define LOCK_BOTH 1 - -void Update(unsigned int port, unsigned int slot) -{ - char* stateUpdated; - if (port < 2) - { - stateUpdated = &pads[port][slot].stateUpdated; - } - else if (port < 6) - { - stateUpdated = padReadKeyUpdated + port - 2; - } - else - return; - - if (*stateUpdated > 0) - { - stateUpdated[0]--; - return; - } - -// Lock prior to timecheck code to avoid pesky race conditions. - std::lock_guard lock(updateLock); - - static unsigned int LastCheck = 0; - unsigned int t = timeGetTime(); - if (t - LastCheck < 15 || !openCount) - return; - -#ifdef _MSC_VER - if (windowThreadId != GetCurrentThreadId()) - { - if (stateUpdated[0] < 0) - { - if (!updateQueued) - { - updateQueued = 1; - PostMessage(static_cast(s_window_info.window_handle), - WMA_FORCE_UPDATE, FORCE_UPDATE_WPARAM, FORCE_UPDATE_LPARAM); - } - } - else - { - stateUpdated[0]--; - } - return; - } -#endif - - LastCheck = t; - - int i; - ButtonSum s[2][4]; - u8 lockStateChanged[2][4]; - memset(lockStateChanged, 0, sizeof(lockStateChanged)); - - for (i = 0; i < 8; i++) - { - s[i & 1][i >> 1] = pads[i & 1][i >> 1].lockedSum; - } -#ifdef __linux__ - InitInfo info = { - 0, 0, GSdsp, GSwin}; -#else - InitInfo info = { - 0, 0, hWndTop, &hWndGSProc}; -#endif - dm->Update(&info); - static int rapidFire = 0; - rapidFire++; - static bool anyDeviceActiveAndBound = true; - bool currentDeviceActiveAndBound = false; - for (i = 0; i < dm->numDevices; i++) - { - Device* dev = dm->devices[i]; - // Skip both disabled devices and inactive enabled devices. - // Shouldn't be any of the latter, in general, but just in case... - if (!dev->active) - continue; - for (int port = 0; port < 2; port++) - { - for (int slot = 0; slot < 4; slot++) - { - int padtype = config.padConfigs[port][slot].type; - if (padtype == DisabledPad || !pads[port][slot].initialized) - continue; - for (int j = 0; j < dev->pads[port][slot][padtype].numBindings; j++) - { - Binding* b = dev->pads[port][slot][padtype].bindings + j; - int cmd = b->command; - int state = dev->virtualControlState[b->controlIndex]; - if (!(rapidFire & b->rapidFire)) - { - if ((cmd > 0x0F && cmd < 0x2A && cmd != 0x28) || cmd > 0x2C) - { - ProcessButtonBinding(b, &s[port][slot], state); - } - else if ((state >> 15) && !(dev->oldVirtualControlState[b->controlIndex] >> 15)) - { - if (cmd == 0x0F) - { // Mouse - miceEnabled = !miceEnabled; - UpdateEnabledDevices(); - } - else if (cmd == 0x2A) - { // Lock Buttons - lockStateChanged[port][slot] |= LOCK_BUTTONS; - } - else if (cmd == 0x2B) - { // Lock Input - lockStateChanged[port][slot] |= LOCK_BOTH; - } - else if (cmd == 0x2C) - { // Lock Direction - lockStateChanged[port][slot] |= LOCK_DIRECTION; - } - else if (cmd == 0x28 && !pads[port][slot].modeLock && padtype == Dualshock2Pad) - { // Analog - if (pads[port][slot].mode == MODE_ANALOG) - pads[port][slot].mode = MODE_DIGITAL; - else if (pads[port][slot].mode == MODE_DIGITAL) - pads[port][slot].mode = MODE_ANALOG; - } - } - } - } - } - } - if (dev->attached && dev->pads[0][0][config.padConfigs[0][0].type].numBindings > 0) - { - if (!anyDeviceActiveAndBound) - { - anyDeviceActiveAndBound = true; - } - currentDeviceActiveAndBound = true; - } - } - if (!currentDeviceActiveAndBound && activeWindow) - { - if (anyDeviceActiveAndBound) - Console.Warning("PAD: Warning! No controls are bound to a currently attached device!\nPlease attach a controller that has been setup for use with PAD or go to GamePad Settings and setup new controls.\n"); - anyDeviceActiveAndBound = false; - } - dm->PostRead(); - - { - for (int port = 0; port < 2; port++) - { - for (int slot = 0; slot < 4; slot++) - { - for (int motor = 0; motor < 2; motor++) - { - // TODO: Probably be better to send all of these at once. - if (pads[port][slot].nextVibrate[motor] | pads[port][slot].currentVibrate[motor]) - { - pads[port][slot].currentVibrate[motor] = pads[port][slot].nextVibrate[motor]; - dm->SetEffect(port, slot, motor, pads[port][slot].nextVibrate[motor]); - } - } - } - } - - for (int port = 0; port < 2; port++) - { - for (int slot = 0; slot < 4; slot++) - { - pads[port][slot].stateUpdated = 1; - if (config.padConfigs[port][slot].type == DisabledPad || !pads[port][slot].initialized) - continue; - if (config.padConfigs[port][slot].type == GuitarPad) - { - if (!config.GH2) - { - s[port][slot].sticks[0].vert = -s[port][slot].sticks[0].vert; - } - else // GH2 hack. - { - const unsigned int oldIdList[5] = {ID_R2, ID_CIRCLE, ID_TRIANGLE, ID_CROSS, ID_SQUARE}; - const unsigned int idList[5] = {ID_L2, ID_L1, ID_R1, ID_R2, ID_CROSS}; - int values[5]; - int i; - for (i = 0; i < 5; i++) - { - int id = oldIdList[i] - ID_DPAD_UP; - values[i] = s[port][slot].buttons[id]; - s[port][slot].buttons[id] = 0; - } - s[port][slot].buttons[ID_TRIANGLE - ID_DPAD_UP] = values[1]; - for (i = 0; i < 5; i++) - { - int id = idList[i] - ID_DPAD_UP; - s[port][slot].buttons[id] = values[i]; - } - if (s[port][slot].buttons[14] <= 48 && s[port][slot].buttons[12] <= 48) - { - for (int i = 0; i < 5; i++) - { - unsigned int id = idList[i] - ID_DPAD_UP; - if (pads[port][slot].sum.buttons[id] < s[port][slot].buttons[id]) - { - s[port][slot].buttons[id] = pads[port][slot].sum.buttons[id]; - } - } - } - else if (pads[port][slot].sum.buttons[14] <= 48 && pads[port][slot].sum.buttons[12] <= 48) - { - for (int i = 0; i < 5; i++) - { - unsigned int id = idList[i] - ID_DPAD_UP; - if (pads[port][slot].sum.buttons[id]) - { - s[port][slot].buttons[id] = 0; - } - } - } - } - } - - if (pads[port][slot].mode == MODE_DIGITAL) - { - for (int i = 0; i <= 1; i++) - { - if (s[port][slot].sticks[i].horiz >= 100) - s[port][slot].buttons[13] += s[port][slot].sticks[i].horiz; - if (s[port][slot].sticks[i].horiz <= -100) - s[port][slot].buttons[15] -= s[port][slot].sticks[i].horiz; - if (s[port][slot].sticks[i].vert >= 100) - s[port][slot].buttons[14] += s[port][slot].sticks[i].vert; - if (s[port][slot].sticks[i].vert <= -100) - s[port][slot].buttons[12] -= s[port][slot].sticks[i].vert; - } - } - - CapSum(&s[port][slot]); - if (lockStateChanged[port][slot]) - { - if (lockStateChanged[port][slot] & LOCK_BOTH) - { - if (pads[port][slot].lockedState != (LOCK_DIRECTION | LOCK_BUTTONS)) - { - // Enable the one that's not enabled. - lockStateChanged[port][slot] ^= pads[port][slot].lockedState ^ (LOCK_DIRECTION | LOCK_BUTTONS); - } - else - { - // Disable both - lockStateChanged[port][slot] ^= LOCK_DIRECTION | LOCK_BUTTONS; - } - } - if (lockStateChanged[port][slot] & LOCK_DIRECTION) - { - if (pads[port][slot].lockedState & LOCK_DIRECTION) - { - memset(pads[port][slot].lockedSum.sticks, 0, sizeof(pads[port][slot].lockedSum.sticks)); - } - else - { - memcpy(pads[port][slot].lockedSum.sticks, s[port][slot].sticks, sizeof(pads[port][slot].lockedSum.sticks)); - } - pads[port][slot].lockedState ^= LOCK_DIRECTION; - } - if (lockStateChanged[port][slot] & LOCK_BUTTONS) - { - if (pads[port][slot].lockedState & LOCK_BUTTONS) - { - memset(pads[port][slot].lockedSum.buttons, 0, sizeof(pads[port][slot].lockedSum.buttons)); - } - else - { - memcpy(pads[port][slot].lockedSum.buttons, s[port][slot].buttons, sizeof(pads[port][slot].lockedSum.buttons)); - } - pads[port][slot].lockedState ^= LOCK_BUTTONS; - } - for (i = 0; i < (int)sizeof(pads[port][slot].lockedSum) / 4; i++) - { - if (((int*)&pads[port][slot].lockedSum)[i]) - break; - } - if (i == sizeof(pads[port][slot].lockedSum) / 4) - { - pads[port][slot].lockedState = 0; - } - } - } - } - } - for (i = 0; i < 8; i++) - { - pads[i & 1][i >> 1].sum = s[i & 1][i >> 1]; - } - - padReadKeyUpdated[0] = padReadKeyUpdated[1] = padReadKeyUpdated[2] = 1; - - if (stateUpdated[0] > 0) - --stateUpdated[0]; -} - -void PADupdate(int port) -{ - Update(port + 3, 0); -} - -inline void SetVibrate(int port, int slot, int motor, u8 val) -{ - pads[port][slot].nextVibrate[motor] = val; -} - -#ifdef _MSC_VER -// Used in about and config screens. -void GetNameAndVersionString(wchar_t* out) -{ - wsprintfW(out, L"GamePad"); -} -#endif - - -void PADshutdown() -{ - for (int i = 0; i < 8; i++) - pads[i & 1][i >> 1].initialized = 0; - portInitialized[0] = portInitialized[1] = 0; - UnloadConfigs(); -} - -inline void StopVibrate() -{ - for (int i = 0; i < 8; i++) - { - SetVibrate(i & 1, i >> 1, 0, 0); - SetVibrate(i & 1, i >> 1, 1, 0); - } -} - -inline void ResetVibrate(int port, int slot) -{ - SetVibrate(port, slot, 0, 0); - SetVibrate(port, slot, 1, 0); - pads[port][slot].vibrate[0] = 0x5A; - for (int i = 1; i < 8; ++i) - pads[port][slot].vibrate[i] = 0xFF; -} - -void ResetPad(int port, int slot) -{ - // Lines before memset currently don't do anything useful, - // but allow this function to be called at any time. - - // Need to backup, so can be called at any point. - u8 enabled = pads[port][slot].enabled; - - // Currently should never do anything. - SetVibrate(port, slot, 0, 0); - SetVibrate(port, slot, 1, 0); - - memset(&pads[port][slot], 0, sizeof(pads[0][0])); - if (config.padConfigs[port][slot].type == MousePad) - pads[port][slot].mode = MODE_PS1_MOUSE; - else if (config.padConfigs[port][slot].type == neGconPad) - pads[port][slot].mode = MODE_NEGCON; - else - pads[port][slot].mode = MODE_DIGITAL; - - pads[port][slot].umask[0] = 0xFF; - pads[port][slot].umask[1] = 0xFF; - pads[port][slot].umask[2] = 0x03; - // Sets up vibrate variable. - ResetVibrate(port, slot); - pads[port][slot].initialized = 1; - - pads[port][slot].enabled = enabled; - - pads[port][slot].previousType = config.padConfigs[port][slot].type; - - pads[port][slot].config = 0; -} - - -struct QueryInfo -{ - u8 port; - u8 slot; - u8 lastByte; - u8 currentCommand; - u8 numBytes; - u8 queryDone; - u8 response[42]; -} query = {0, 0, 0, 0, 0, 0xFF, {0xF3}}; - -s32 PADinit() -{ - // Note: Won't load settings if already loaded. - if (LoadSettings() < 0) - { - return -1; - } - - for (int i = 2; i > 0; i--) - { - int port = i; - port--; - - for (int slot = 0; slot < 4; slot++) - ResetPad(port, slot); - slots[port] = 0; - portInitialized[port] = 1; - - query.lastByte = 1; - query.numBytes = 0; - ClearKeyQueue(); -#ifdef __linux__ - R_ClearKeyQueue(); -#endif - // Just in case, when resuming emulation. - ReleaseModifierKeys(); - } - - return 0; -} - - - -// Note to self: Has to be a define for the sizeof() to work right. -// Note to self 2: All are the same size, anyways, except for longer full DS2 response -// and shorter digital mode response. -#define SET_RESULT(a) \ - { \ - memcpy(query.response + 2, a, sizeof(a)); \ - query.numBytes = 2 + sizeof(a); \ - } - -#define SET_FINAL_RESULT(a) \ - { \ - memcpy(query.response + 2, a, sizeof(a)); \ - query.numBytes = 2 + sizeof(a); \ - query.queryDone = 1; \ - } - -static const u8 ConfigExit[7] = {0x5A, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00}; -//static const u8 ConfigExit[7] = {0x5A, 0xFF, 0xFF, 0x00, 0x00, 0x00, 0x00}; - -static const u8 noclue[7] = {0x5A, 0x00, 0x00, 0x02, 0x00, 0x00, 0x5A}; -static u8 queryMaskMode[7] = {0x5A, 0xFF, 0xFF, 0x00, 0x00, 0x00, 0x5A}; -//static const u8 DSNonNativeMode[7] = {0x5A, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00}; -static const u8 setMode[7] = {0x5A, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00}; - -// DS2 -static const u8 queryModelDS2[7] = {0x5A, 0x03, 0x02, 0x00, 0x02, 0x01, 0x00}; -// DS1 -static const u8 queryModelDS1[7] = {0x5A, 0x01, 0x02, 0x00, 0x02, 0x01, 0x00}; - -static const u8 queryAct[2][7] = {{0x5A, 0x00, 0x00, 0x01, 0x02, 0x00, 0x0A}, - {0x5A, 0x00, 0x00, 0x01, 0x01, 0x01, 0x14}}; - -static const u8 queryComb[7] = {0x5A, 0x00, 0x00, 0x02, 0x00, 0x01, 0x00}; - -static const u8 queryMode[7] = {0x5A, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00}; - - -static const u8 setNativeMode[7] = {0x5A, 0x00, 0x00, 0x00, 0x00, 0x00, 0x5A}; - -#ifdef _MSC_VER -// Useful sequence before changing into active/inactive state. -// Handles hooking/unhooking of mouse and KB and also mouse cursor visibility. -// towardsActive==true indicates we're gaining activity (on focus etc), false is for losing activity (on close, kill focus, etc). -void PrepareActivityState(bool towardsActive) -{ - if (!towardsActive) - ReleaseModifierKeys(); - activeWindow = towardsActive; - UpdateEnabledDevices(); -} - -// responsible for monitoring device addition/removal, focus changes, and viewport closures. -ExtraWndProcResult StatusWndProc(HWND hWnd, UINT uMsg, WPARAM wParam, LPARAM lParam, LRESULT* output) -{ - switch (uMsg) - { - case WMA_FORCE_UPDATE: - if (wParam == FORCE_UPDATE_WPARAM && lParam == FORCE_UPDATE_LPARAM) - { - if (updateQueued) - { - updateQueued = 0; - Update(5, 0); - } - return NO_WND_PROC; - } - case WM_DEVICECHANGE: - if (wParam == DBT_DEVNODES_CHANGED) - { - UpdateEnabledDevices(1); - } - break; - case WM_ACTIVATE: - // Release any buttons PCSX2 may think are down when - // losing/gaining focus. - // Note - I never managed to see this case entered, but SET/KILLFOCUS are entered. - avih 2014-04-16 - PrepareActivityState(LOWORD(wParam) != WA_INACTIVE); - break; - case WM_DESTROY: - QueueKeyEvent(VK_ESCAPE, HostKeyEvent::Type::KeyPressed); - break; - case WM_KILLFOCUS: - PrepareActivityState(false); - break; - case WM_SETFOCUS: - PrepareActivityState(true); - break; - default: - break; - } - return CONTINUE_BLISSFULLY; -} -#endif - -#ifndef PCSX2_CORE -void PADconfigure() -{ - ScopedCoreThreadPause paused_core(SystemsMask::System_PAD); - Configure(); - paused_core.AllowResume(); -} -#endif - -s32 PADopen(const WindowInfo& wi) -{ - if (openCount++) - return 0; - - miceEnabled = !config.mouseUnfocus; -#ifdef _MSC_VER - if (wi.type != WindowInfo::Type::Win32) - { - openCount = 0; - MessageBoxA(GetActiveWindow(), - "Invalid Window handle passed to PAD.\n" - "\n" - "Either your emulator or gs plugin is buggy,\n" - "Despite the fact the emulator is about to\n" - "blame PAD for failing to initialize.", - "Non-PAD Error", MB_OK | MB_ICONERROR); - return -1; - } - - const HWND hWnd = static_cast(wi.window_handle); - hWndTop = GetAncestor(hWnd, GA_ROOT); - - if (!hWndGSProc.SetWndHandle(hWnd)) - { - openCount = 0; - return -1; - } - - // Implements most hacks, as well as enabling/disabling mouse - // capture when focus changes. - updateQueued = 0; - hWndGSProc.Eat(StatusWndProc, 0); - - windowThreadId = GetWindowThreadProcessId(hWndTop, 0); - s_window_info = wi; - -#endif - for (int port = 0; port < 2; port++) - { - for (int slot = 0; slot < 4; slot++) - { - memset(&pads[port][slot].sum, 0, sizeof(pads[port][slot].sum)); - memset(&pads[port][slot].lockedSum, 0, sizeof(pads[port][slot].lockedSum)); - pads[port][slot].lockedState = 0; - - if (config.padConfigs[port][slot].type != pads[port][slot].previousType) - { - ResetPad(port, slot); - } - } - } - -#ifdef _MSC_VER -// I'd really rather use this line, but GetActiveWindow() does not have complete specs. -// It *seems* to return null when no window from this thread has focus, but the -// Microsoft specs seem to imply it returns the window from this thread that would have focus, -// if any window did (topmost in this thread?). Which isn't what I want, and doesn't seem -// to be what it actually does. -// activeWindow = GetActiveWindow() == hWnd; - -// activeWindow = (GetAncestor(hWnd, GA_ROOT) == GetAncestor(GetForegroundWindow(), GA_ROOT)); -#endif - activeWindow = 1; - UpdateEnabledDevices(); - return 0; -} - -void PADclose() -{ - if (openCount && !--openCount) - { -#ifdef _MSC_VER - updateQueued = 0; - hWndGSProc.Release(); - hWndTopProc.Release(); - dm->ReleaseInput(); - s_window_info = WindowInfo(); -#else - R_ClearKeyQueue(); -#endif - ClearKeyQueue(); - } -} - -bool PADcomplete() -{ - return query.queryDone; -} - -u8 PADstartPoll(int port, int slot) -{ - DEBUG_NEW_SET(); - - if ((unsigned int)port <= 1 && pads[port][slot].enabled) - { - query.queryDone = 0; - query.port = port; - query.slot = slot; - query.numBytes = 2; - query.lastByte = 0; - DEBUG_IN(port); - DEBUG_OUT(0xFF); - DEBUG_IN(slot); - DEBUG_OUT(pads[port][slot].enabled); - return 0xFF; - } - else - { - query.queryDone = 1; - query.numBytes = 0; - query.lastByte = 1; - DEBUG_IN(0); - DEBUG_OUT(0); - DEBUG_IN(port); - DEBUG_OUT(0); - return 0; - } -} - -inline int IsDualshock2(u8 port, u8 slot) -{ - return config.padConfigs[query.port][query.slot].type == Dualshock2Pad || - (config.padConfigs[query.port][query.slot].type == GuitarPad && config.GH2); -} - -u8 PADpoll(u8 value) -{ - DEBUG_IN(value); - if (query.lastByte + 1 >= query.numBytes) - { - DEBUG_OUT(0); - return 0; - } - if (query.lastByte && query.queryDone) - { - DEBUG_OUT(query.response[1 + query.lastByte]); - return query.response[++query.lastByte]; - } - - Pad* pad = &pads[query.port][query.slot]; - int padtype = config.padConfigs[query.port][query.slot].type; - - if (query.lastByte == 0) - { - query.lastByte++; - query.currentCommand = value; - - // Only the 0x42(read input and vibration) and 0x43(enter or exit config mode) command cases work outside of config mode, the other cases will be avoided. - if ((!pad->config && value != 0x42 && value != 0x43) || (padtype == neGconPad && (value < 0x40 || value > 0x45))) - { - query.numBytes = 0; - query.queryDone = 1; - DEBUG_OUT(0xF3); - return 0xF3; - } - switch (value) - { - // CONFIG_MODE - case 0x43: - if (pad->config && padtype != neGconPad) - { - // In config mode. Might not actually be leaving it. - SET_RESULT(ConfigExit); - DEBUG_OUT(0xF3); - return 0xF3; - } - // Fall through - - // READ_DATA_AND_VIBRATE - case 0x42: - query.response[2] = 0x5A; - { - Update(query.port, query.slot); - ButtonSum* sum = &pad->sum; - - if (padtype == MousePad) - { - u8 b1 = 0xFC; - if (sum->buttons[9] > 0) // Left button - b1 -= 8; - if (sum->buttons[10] > 0) // Right button - b1 -= 4; - - query.response[3] = 0xFF; - query.response[4] = b1; - query.response[5] = sum->sticks[1].horiz / 2; - query.response[6] = sum->sticks[1].vert / 2; - query.numBytes = 7; - query.lastByte = 1; - DEBUG_OUT(MODE_PS1_MOUSE); - return MODE_PS1_MOUSE; - } - if (padtype == neGconPad) - { - u8 b1 = 0xFF, b2 = 0xFF; - b1 -= (sum->buttons[3] > 0) << 3; // Start - - for (int i = 3; i < 6; i++) - { - b2 -= (sum->buttons[i + 4] > 0) << i; // R, A, B - } - for (int i = 4; i < 8; i++) - { - b1 -= (sum->buttons[i + 8] > 0) << i; // D-pad Up, Right, Down, Left - } - - query.response[3] = b1; - query.response[4] = b2; - query.response[5] = Cap((sum->sticks[1].horiz + 255) / 2); // Swivel - query.response[6] = (unsigned char)sum->buttons[10]; // I - query.response[7] = (unsigned char)sum->buttons[11]; // II - query.response[8] = (unsigned char)sum->buttons[6]; // L - - query.numBytes = 9; - query.lastByte = 1; - DEBUG_OUT(MODE_NEGCON); - return MODE_NEGCON; - } - - u8 b1 = 0xFF, b2 = 0xFF; - for (int i = 0; i < 4; i++) - { - b1 -= (sum->buttons[i] > 0) << i; - } - for (int i = 0; i < 8; i++) - { - b2 -= (sum->buttons[i + 4] > 0) << i; - } - - if (padtype == GuitarPad && !config.GH2) - { - sum->buttons[15] = 255; - // Not sure about this. Forces wammy to be from 0 to 0x7F. - // if (sum->sticks[2].vert > 0) sum->sticks[2].vert = 0; - } - - for (int i = 4; i < 8; i++) - { - b1 -= (sum->buttons[i + 8] > 0) << i; - } - - //Left, Right and Down are always pressed on Pop'n Music controller. - if (padtype == PopnPad) - b1 = b1 & 0x1f; - - query.response[3] = b1; - query.response[4] = b2; - - query.numBytes = 5; - if (pad->mode != MODE_DIGITAL) - { - query.response[5] = Cap((sum->sticks[0].horiz + 255) / 2); // Right stick: left & right - query.response[6] = Cap((sum->sticks[0].vert + 255) / 2); // Right stick: up & down - query.response[7] = Cap((sum->sticks[1].horiz + 255) / 2); // Left stick: left & right - query.response[8] = Cap((sum->sticks[1].vert + 255) / 2); // Left stick: up & down - - query.numBytes = 9; - if (pad->mode != MODE_ANALOG && !pad->config) - { - // Good idea? No clue. - //query.response[3] &= pad->mask[0]; - //query.response[4] &= pad->mask[1]; - - // No need to cap these, already done int CapSum(). - query.response[9] = (unsigned char)sum->buttons[13]; // D-pad right - query.response[10] = (unsigned char)sum->buttons[15]; // D-pad left - query.response[11] = (unsigned char)sum->buttons[12]; // D-pad up - query.response[12] = (unsigned char)sum->buttons[14]; // D-pad down - - query.response[13] = (unsigned char)sum->buttons[8]; // Triangle - query.response[14] = (unsigned char)sum->buttons[9]; // Circle - query.response[15] = (unsigned char)sum->buttons[10]; // Cross - query.response[16] = (unsigned char)sum->buttons[11]; // Square - - query.response[17] = (unsigned char)sum->buttons[6]; // L1 - query.response[18] = (unsigned char)sum->buttons[7]; // R1 - query.response[19] = (unsigned char)sum->buttons[4]; // L2 - query.response[20] = (unsigned char)sum->buttons[5]; // R2 - query.numBytes = 21; - } - } - } - query.lastByte = 1; - DEBUG_OUT(pad->mode); - return pad->mode; - // SET_VREF_PARAM - case 0x40: - SET_FINAL_RESULT(noclue); - break; - // QUERY_DS2_ANALOG_MODE - case 0x41: - // Right? Wrong? No clue. - if (pad->mode == MODE_DIGITAL || pad->mode == MODE_PS1_MOUSE || pad->mode == MODE_NEGCON) - { - queryMaskMode[1] = queryMaskMode[2] = queryMaskMode[3] = 0; - queryMaskMode[6] = 0x00; - } - else - { - queryMaskMode[1] = pad->umask[0]; - queryMaskMode[2] = pad->umask[1]; - queryMaskMode[3] = pad->umask[2]; - // Not entirely sure about this. - //queryMaskMode[3] = 0x01 | (pad->mode == MODE_DS2_NATIVE)*2; - queryMaskMode[6] = 0x5A; - } - SET_FINAL_RESULT(queryMaskMode); - break; - // SET_MODE_AND_LOCK - case 0x44: - SET_RESULT(setMode); - ResetVibrate(query.port, query.slot); - break; - // QUERY_MODEL_AND_MODE - case 0x45: - if (IsDualshock2(query.port, query.slot)) - { - SET_FINAL_RESULT(queryModelDS2); - } - else - { - SET_FINAL_RESULT(queryModelDS1); - } - // Not digital mode. - query.response[5] = (pad->mode & 0xF) != 1; - break; - // QUERY_ACT - case 0x46: - SET_RESULT(queryAct[0]); - break; - // QUERY_COMB - case 0x47: - SET_FINAL_RESULT(queryComb); - break; - // QUERY_MODE - case 0x4C: - SET_RESULT(queryMode); - break; - // VIBRATION_TOGGLE - case 0x4D: - memcpy(query.response + 2, pad->vibrate, 7); - query.numBytes = 9; - ResetVibrate(query.port, query.slot); - break; - // SET_DS2_NATIVE_MODE - case 0x4F: - if (IsDualshock2(query.port, query.slot)) - { - SET_RESULT(setNativeMode); - } - else - { - SET_FINAL_RESULT(setNativeMode); - } - break; - default: - query.numBytes = 0; - query.queryDone = 1; - break; - } - DEBUG_OUT(0xF3); - return 0xF3; - } - else - { - query.lastByte++; - - // Only the 0x42(read input and vibration) and 0x43(enter or exit config mode) command cases work outside of config mode, the other cases will be avoided. - if ((!pad->config && query.currentCommand != 0x42 && query.currentCommand != 0x43) || (padtype == neGconPad && (query.currentCommand < 0x40 || query.currentCommand > 0x45))) - { - DEBUG_OUT(query.response[query.lastByte]); - return query.response[query.lastByte]; - } - switch (query.currentCommand) - { - // READ_DATA_AND_VIBRATE - case 0x42: - if (query.lastByte == pad->vibrateI[0]) - { - SetVibrate(query.port, query.slot, 1, 255 * (value & 1)); - } - else if (query.lastByte == pad->vibrateI[1]) - { - SetVibrate(query.port, query.slot, 0, value); - } - break; - // CONFIG_MODE - case 0x43: - if (query.lastByte == 3) - { - query.queryDone = 1; - pad->config = value; - } - break; - // SET_MODE_AND_LOCK - case 0x44: - if (query.lastByte == 3 && value < 2) - { - if (padtype == MousePad) - { - pad->mode = MODE_PS1_MOUSE; - } - else if (padtype == neGconPad) - { - pad->mode = MODE_NEGCON; - } - else - { - static const u8 modes[2] = {MODE_DIGITAL, MODE_ANALOG}; - pad->mode = modes[value]; - } - } - else if (query.lastByte == 4) - { - if (value == 3) - { - pad->modeLock = 3; - } - else - { - pad->modeLock = 0; - } - query.queryDone = 1; - } - break; - // QUERY_ACT - case 0x46: - if (query.lastByte == 3) - { - if (value < 2) - SET_RESULT(queryAct[value]) - // bunch of 0's - // else SET_RESULT(setMode); - query.queryDone = 1; - } - break; - // QUERY_MODE - case 0x4C: - if (query.lastByte == 3 && value < 2) - { - query.response[6] = 4 + value * 3; - query.queryDone = 1; - } - // bunch of 0's - //else data = setMode; - break; - // VIBRATION_TOGGLE - case 0x4D: - if (query.lastByte >= 3) - { - if (value == 0) - { - pad->vibrateI[0] = (u8)query.lastByte; - } - else if (value == 1) - { - pad->vibrateI[1] = (u8)query.lastByte; - } - pad->vibrate[query.lastByte - 2] = value; - } - break; - // SET_DS2_NATIVE_MODE - case 0x4F: - if (query.lastByte >2 && query.lastByte < 6) - { - pad->umask[query.lastByte - 3] = value; - } - pad->mode = MODE_DS2_NATIVE; - break; - default: - DEBUG_OUT(0); - return 0; - } - DEBUG_OUT(query.response[query.lastByte]); - return query.response[query.lastByte]; - } -} - -HostKeyEvent* PADkeyEvent() -{ - // If running both pads, ignore every other call. So if two keys pressed in same interval... - static char eventCount = 0; - eventCount++; - if (eventCount < openCount) - { - return 0; - } - eventCount = 0; - - Update(2, 0); - static HostKeyEvent ev; - if (!GetQueuedKeyEvent(&ev)) - return 0; - -#ifdef _MSC_VER - static char shiftDown = 0; - static char altDown = 0; - if (!activeWindow) - altDown = shiftDown = 0; - - if (miceEnabled && (ev.key == VK_ESCAPE || (int)ev.key == -2) && ev.type == HostKeyEvent::Type::KeyPressed) - { - // Disable mouse/KB hooks on escape (before going into paused mode). - // This is a hack, since PADclose (which is called on pause) should enevtually also deactivate the - // mouse/kb capture. In practice, WindowsMessagingMouse::Deactivate is called from PADclose, but doesn't - // manage to release the mouse, maybe due to the thread from which it's called or some - // state or somehow being too late. - // This explicitly triggers inactivity (releasing mouse/kb hooks) before PCSX2 starts to close subcomponents. - // Regardless, the mouse/kb hooks will get re-enabled on resume if required without need for further hacks. - - PrepareActivityState(false); - } - - // So don't change skip mode on alt-F4. - if (ev.key == VK_F4 && altDown) - { - return 0; - } - - if (ev.key == VK_LSHIFT || ev.key == VK_RSHIFT || ev.key == VK_SHIFT) - { - ev.key = VK_SHIFT; - shiftDown = (ev.type == HostKeyEvent::Type::KeyReleased); - } - else if (ev.key == VK_LCONTROL || ev.key == VK_RCONTROL) - { - ev.key = VK_CONTROL; - } - else if (ev.key == VK_LMENU || ev.key == VK_RMENU || ev.key == VK_SHIFT) - { - ev.key = VK_MENU; - altDown = (ev.type == HostKeyEvent::Type::KeyPressed); - } -#endif - return &ev; -} - -struct PadFullFreezeData -{ - char format[8]; - // Currently all different versions are incompatible. - // May split into major/minor with some compatibility rules. - u32 version; - u8 port; - // active slot for port - u8 slot[2]; - PadFreezeData padData[2][4]; - QueryInfo query; -}; - -s32 PADfreeze(FreezeAction mode, freezeData* data) -{ - if (!data) - { - Console.Warning("PAD savestate null pointer!\n"); - return -1; - } - - if (mode == FreezeAction::Size) - { - data->size = sizeof(PadFullFreezeData); - } - else if (mode == FreezeAction::Load) - { - PadFullFreezeData& pdata = *(PadFullFreezeData*)(data->data); - StopVibrate(); - if (data->size != sizeof(PadFullFreezeData) || - pdata.version != PAD_SAVE_STATE_VERSION || - strcmp(pdata.format, "PadMode")) - return 0; - - if (pdata.port >= 2) - return 0; - - query = pdata.query; - if (pdata.query.slot < 4) - { - query = pdata.query; - } - - // Tales of the Abyss - pad fix - // - restore data for both ports - for (int port = 0; port < 2; port++) - { - for (int slot = 0; slot < 4; slot++) - { - u8 mode = pdata.padData[port][slot].mode; - - if (mode != MODE_DIGITAL && mode != MODE_ANALOG && mode != MODE_DS2_NATIVE && mode != MODE_PS1_MOUSE && mode != MODE_NEGCON) - { - break; - } - - // Not sure if the cast is strictly necessary, but feel safest with it there... - *(PadFreezeData*)&pads[port][slot] = pdata.padData[port][slot]; - } - - if (pdata.slot[port] < 4) - slots[port] = pdata.slot[port]; - } - } - else if (mode == FreezeAction::Save) - { - if (data->size != sizeof(PadFullFreezeData)) - return 0; - PadFullFreezeData& pdata = *(PadFullFreezeData*)(data->data); - - - // Tales of the Abyss - pad fix - // - PCSX2 only saves port0 (save #1), then port1 (save #2) - - memset(&pdata, 0, sizeof(pdata)); - strcpy(pdata.format, "PadMode"); - pdata.version = PAD_SAVE_STATE_VERSION; - pdata.port = 0; - pdata.query = query; - - for (int port = 0; port < 2; port++) - { - for (int slot = 0; slot < 4; slot++) - { - pdata.padData[port][slot] = pads[port][slot]; - } - - pdata.slot[port] = slots[port]; - } - } - else - return -1; - return 0; -} - -s32 PADsetSlot(u8 port, u8 slot) -{ - port--; - slot--; - if (port > 1 || slot > 3) - { - return 0; - } - // Even if no pad there, record the slot, as it is the active slot regardless. - slots[port] = slot; - // First slot always allowed. - // return pads[port][slot].enabled | !slot; - return 1; -} diff --git a/pcsx2/PAD/Windows/PAD.rc b/pcsx2/PAD/Windows/PAD.rc deleted file mode 100644 index 26c0d58e43..0000000000 --- a/pcsx2/PAD/Windows/PAD.rc +++ /dev/null @@ -1,577 +0,0 @@ -// Microsoft Visual C++ generated resource script. -// -#include "resource_pad.h" - -#define APSTUDIO_READONLY_SYMBOLS -///////////////////////////////////////////////////////////////////////////// -// -// Generated from the TEXTINCLUDE 2 resource. -// -#include - -///////////////////////////////////////////////////////////////////////////// -#undef APSTUDIO_READONLY_SYMBOLS - -///////////////////////////////////////////////////////////////////////////// -// English (United States) resources - -#if !defined(AFX_RESOURCE_DLL) || defined(AFX_TARG_ENU) -LANGUAGE LANG_ENGLISH, SUBLANG_ENGLISH_US -#pragma code_page(1252) - -#ifdef APSTUDIO_INVOKED -///////////////////////////////////////////////////////////////////////////// -// -// TEXTINCLUDE -// - -1 TEXTINCLUDE -BEGIN - "resource.h\0" -END - -2 TEXTINCLUDE -BEGIN - "#include \r\n" - "\0" -END - -3 TEXTINCLUDE -BEGIN - "\r\n" - "\0" -END - -#endif // APSTUDIO_INVOKED - - -///////////////////////////////////////////////////////////////////////////// -// -// Dialog -// - -IDD_CONFIG DIALOGEX 0, 0, 424, 283 -STYLE DS_SETFONT | DS_MODALFRAME | DS_FIXEDSYS | WS_POPUP | WS_CAPTION -FONT 8, "MS Shell Dlg", 0, 0, 0x1 -BEGIN - CONTROL "",IDC_BINDINGS_LIST,"SysListView32",LVS_REPORT | LVS_SHOWSELALWAYS | LVS_NOSORTHEADER | WS_BORDER | WS_VSCROLL | WS_TABSTOP,7,7,183,237,WS_EX_CLIENTEDGE - // Input Bindings: - PUSHBUTTON "Select",ID_SELECT,328,34,34,15 - PUSHBUTTON "Start",ID_START,366,34,34,15 - PUSHBUTTON "Analog",ID_ANALOG,328,51,34,15 - PUSHBUTTON "Mouse",ID_MOUSE,366,51,34,15 - GROUPBOX "Shoulder buttons",IDC_SHOULDER,196,24,108,46 - PUSHBUTTON "L1",ID_L1,210,34,34,15 - PUSHBUTTON "R1",ID_R1,254,34,34,15 - PUSHBUTTON "L2",ID_L2,210,51,34,15 - PUSHBUTTON "R2",ID_R2,254,51,34,15 - GROUPBOX "D-Pad",IDC_DPAD,196,70,108,70 - PUSHBUTTON "Up",ID_DPAD_UP,233,81,34,15 - PUSHBUTTON "Left",ID_DPAD_LEFT,214,100,34,15 - PUSHBUTTON "Right",ID_DPAD_RIGHT,252,100,34,15 - PUSHBUTTON "Down",ID_DPAD_DOWN,233,118,34,15 - GROUPBOX "Face buttons",IDC_FACE,310,70,108,70 - PUSHBUTTON "Triangle",ID_TRIANGLE,347,81,34,15 - PUSHBUTTON "Square",ID_SQUARE,328,100,34,15 - PUSHBUTTON "Circle",ID_CIRCLE,366,100,34,15 - PUSHBUTTON "Cross",ID_CROSS,347,119,34,15 - GROUPBOX "Left Analog Stick",IDC_LSTICK,196,141,108,70 - PUSHBUTTON "Up",ID_LSTICK_UP,234,152,30,15 - PUSHBUTTON "Left",ID_LSTICK_LEFT,202,171,30,15 - PUSHBUTTON "L3",ID_L3,234,171,30,15 - PUSHBUTTON "Right",ID_LSTICK_RIGHT,266,171,30,15 - PUSHBUTTON "Down",ID_LSTICK_DOWN,234,191,30,15 - GROUPBOX "Right Analog Stick",IDC_RSTICK,310,141,108,70 - PUSHBUTTON "Up",ID_RSTICK_UP,348,152,30,15 - PUSHBUTTON "Left",ID_RSTICK_LEFT,316,171,30,15 - PUSHBUTTON "R3",ID_R3,348,171,30,15 - PUSHBUTTON "Right",ID_RSTICK_RIGHT,380,171,30,15 - PUSHBUTTON "Down",ID_RSTICK_DOWN,348,191,30,15 - // Force Feedback bindings: - GROUPBOX "Add Force Feedback Effect",ID_FORCEFEEDBACK_BOX,196,215,221,30 - COMBOBOX IDC_FORCEFEEDBACK,202,225,96,118,CBS_DROPDOWNLIST | WS_VSCROLL | WS_TABSTOP - PUSHBUTTON "Big Motor",ID_BIG_MOTOR,304,224,50,14 - PUSHBUTTON "Small Motor",ID_SMALL_MOTOR,360,224,50,14 - // Input options: - PUSHBUTTON "Quick Setup",ID_QUICK_SETUP,358,7,59,15 - CTEXT "",IDC_QUICK_SETUP_TEXT,196,7,156,15,WS_BORDER - CONTROL "Configure on bind",IDC_CONFIGURE_ON_BIND,"Button",BS_AUTOCHECKBOX | WS_TABSTOP,347,266,70,15 - PUSHBUTTON "Show Special Inputs",ID_SPECIAL_INPUTS,196,249,90,15 - COMBOBOX IDC_DEVICE_SELECT,304,250,113,70,CBS_DROPDOWNLIST | WS_VSCROLL | WS_TABSTOP - // Special bindings: - PUSHBUTTON "Lock Input",ID_LOCK_ALL_INPUT,7,249,58,15 - PUSHBUTTON "Lock Direction",ID_LOCK_DIRECTION,69,249,58,15 - PUSHBUTTON "Lock Buttons",ID_LOCK_BUTTONS,131,249,58,15 - PUSHBUTTON "Turbo",ID_TURBO_KEY,7,265,58,15 - PUSHBUTTON "Exclude Input",ID_EXCLUDE,131,265,58,15 - // Force Feedback & Input configurations: - PUSHBUTTON "Back to Controls",ID_CONTROLS,196,260,59,15 - PUSHBUTTON "Reset Configuration",ID_RESET_CONFIG,259,260,72,15 - // Force Feedback configuration: - GROUPBOX "",ID_FF,195,9,222,248 - COMBOBOX IDC_FF_EFFECT,203,23,206,106,CBS_DROPDOWNLIST | WS_VSCROLL | WS_TABSTOP - CONTROL "",IDC_FF_AXIS1,"msctls_trackbar32",WS_TABSTOP,199,43,214,17 - CONTROL "Axis 1",IDC_FF_AXIS1_ENABLED,"Button",BS_AUTOCHECKBOX | WS_TABSTOP,205,63,91,10 - CONTROL "Flip",IDC_FF_AXIS1_FLIP,"Button",BS_AUTOCHECKBOX | WS_TABSTOP,302,63,35,10 - EDITTEXT IDC_FF_AXIS1_SCALE,375,63,33,12,ES_RIGHT | ES_READONLY | NOT WS_BORDER | NOT WS_TABSTOP,WS_EX_RTLREADING - CONTROL "",IDC_FF_AXIS2,"msctls_trackbar32",WS_TABSTOP,199,79,214,17 - CONTROL "Axis 2",IDC_FF_AXIS2_ENABLED,"Button",BS_AUTOCHECKBOX | WS_TABSTOP,205,99,91,10 - CONTROL "Flip",IDC_FF_AXIS2_FLIP,"Button",BS_AUTOCHECKBOX | WS_TABSTOP,302,99,35,10 - EDITTEXT IDC_FF_AXIS2_SCALE,375,99,33,12,ES_RIGHT | ES_READONLY | NOT WS_BORDER | NOT WS_TABSTOP,WS_EX_RTLREADING - CONTROL "",IDC_FF_AXIS3,"msctls_trackbar32",WS_TABSTOP,199,115,214,17 - CONTROL "Axis 3",IDC_FF_AXIS3_ENABLED,"Button",BS_AUTOCHECKBOX | WS_TABSTOP,205,135,91,10 - CONTROL "Flip",IDC_FF_AXIS3_FLIP,"Button",BS_AUTOCHECKBOX | WS_TABSTOP,302,135,35,10 - EDITTEXT IDC_FF_AXIS3_SCALE,375,135,33,12,ES_RIGHT | ES_READONLY | NOT WS_BORDER | NOT WS_TABSTOP,WS_EX_RTLREADING - CONTROL "",IDC_FF_AXIS4,"msctls_trackbar32",WS_TABSTOP,199,151,214,17 - CONTROL "Axis 4",IDC_FF_AXIS4_ENABLED,"Button",BS_AUTOCHECKBOX | WS_TABSTOP,205,171,91,10 - CONTROL "Flip",IDC_FF_AXIS4_FLIP,"Button",BS_AUTOCHECKBOX | WS_TABSTOP,302,171,35,10 - EDITTEXT IDC_FF_AXIS4_SCALE,375,171,33,12,ES_RIGHT | ES_READONLY | NOT WS_BORDER | NOT WS_TABSTOP,WS_EX_RTLREADING - CONTROL "",IDC_FF_AXIS5,"msctls_trackbar32",WS_TABSTOP,199,187,214,17 - CONTROL "Axis 5",IDC_FF_AXIS5_ENABLED,"Button",BS_AUTOCHECKBOX | WS_TABSTOP,205,207,91,10 - CONTROL "Flip",IDC_FF_AXIS5_FLIP,"Button",BS_AUTOCHECKBOX | WS_TABSTOP,302,207,35,10 - EDITTEXT IDC_FF_AXIS5_SCALE,375,207,33,12,ES_RIGHT | ES_READONLY | NOT WS_BORDER | NOT WS_TABSTOP,WS_EX_RTLREADING - CONTROL "",IDC_FF_AXIS6,"msctls_trackbar32",WS_TABSTOP,199,223,214,17 - CONTROL "Axis 6",IDC_FF_AXIS6_ENABLED,"Button",BS_AUTOCHECKBOX | WS_TABSTOP,205,243,91,10 - CONTROL "Flip",IDC_FF_AXIS6_FLIP,"Button",BS_AUTOCHECKBOX | WS_TABSTOP,302,243,35,10 - EDITTEXT IDC_FF_AXIS6_SCALE,375,243,33,12,ES_RIGHT | ES_READONLY | NOT WS_BORDER | NOT WS_TABSTOP,WS_EX_RTLREADING - PUSHBUTTON "Test",ID_TEST,335,260,59,15 - // Input configuration: - GROUPBOX "Configure Binding",ID_SENSITIVITY,195,9,222,110 - EDITTEXT IDC_AXIS_DEVICE,202,22,72,12,ES_READONLY | NOT WS_BORDER | NOT WS_TABSTOP - COMBOBOX IDC_AXIS_DIRECTION,276,20,70,47,CBS_DROPDOWNLIST | WS_TABSTOP - EDITTEXT IDC_AXIS_CONTROL,349,22,65,12,ES_READONLY | NOT WS_BORDER | NOT WS_TABSTOP - CONTROL "Flip",IDC_FLIP,"Button",BS_AUTOCHECKBOX | WS_TABSTOP,202,37,27,10 - CONTROL "Rapid Fire",IDC_RAPID_FIRE,"Button",BS_AUTOCHECKBOX | WS_TABSTOP,232,37,44,10 - LTEXT "Sensitivity",IDC_LABEL_SENSITIVITY,202,51,42,8 - CONTROL "",IDC_SLIDER_SENSITIVITY,"msctls_trackbar32",WS_TABSTOP,240,51,131,17 - EDITTEXT IDC_AXIS_SENSITIVITY,372,53,33,12,ES_RIGHT | ES_READONLY | NOT WS_BORDER | NOT WS_TABSTOP,WS_EX_RTLREADING - LTEXT "Dead Zone",IDC_LABEL_DEADZONE,202,71,42,8 - CONTROL "",IDC_SLIDER_DEADZONE,"msctls_trackbar32",WS_TABSTOP,240,71,131,17 - EDITTEXT IDC_AXIS_DEADZONE,372,73,33,12,ES_RIGHT | ES_READONLY | NOT WS_BORDER | NOT WS_TABSTOP,WS_EX_RTLREADING - LTEXT "Skip Dead Zone",IDC_LABEL_SKIP_DEADZONE,202,92,42,16 - CONTROL "",IDC_SLIDER_SKIP_DEADZONE,"msctls_trackbar32",WS_TABSTOP,240,92,131,17 - EDITTEXT IDC_AXIS_SKIP_DEADZONE,372,94,33,12,ES_RIGHT | ES_READONLY | NOT WS_BORDER | NOT WS_TABSTOP,WS_EX_RTLREADING - LTEXT "Off",IDC_SKIP_DEADZONE_OFF,390,94,20,12 -END - -IDD_CONFIG_POPN DIALOGEX 0, 0, 424, 283 -STYLE DS_SETFONT | DS_MODALFRAME | DS_FIXEDSYS | WS_POPUP | WS_CAPTION -FONT 8, "MS Shell Dlg", 0, 0, 0x1 -BEGIN - CONTROL "",IDC_BINDINGS_LIST,"SysListView32",LVS_REPORT | LVS_SHOWSELALWAYS | LVS_NOSORTHEADER | WS_BORDER | WS_VSCROLL | WS_TABSTOP,7,7,183,237,WS_EX_CLIENTEDGE - // Input Bindings: - PUSHBUTTON "Select",ID_SELECT,262,25,43,15 - PUSHBUTTON "Start",ID_START,306,25,43,15 - PUSHBUTTON "White L",ID_TRIANGLE,196,69,43,15 - PUSHBUTTON "Yellow L",ID_CIRCLE,218,51,43,15 - PUSHBUTTON "Green L",ID_R1,240,69,43,15 - PUSHBUTTON "Blue L",ID_CROSS,262,51,43,15 - PUSHBUTTON "Red",ID_L1,284,69,43,15 - PUSHBUTTON "Blue R",ID_SQUARE,306,51,43,15 - PUSHBUTTON "Green R",ID_R2,328,69,43,15 - PUSHBUTTON "Yellow R",ID_DPAD_UP,350,51,43,15 - PUSHBUTTON "White R",ID_L2,372,69,43,15 - // Force Feedback bindings: - GROUPBOX "Add Force Feedback Effect",ID_FORCEFEEDBACK_BOX,196,215,221,30 - COMBOBOX IDC_FORCEFEEDBACK,202,225,96,118,CBS_DROPDOWNLIST | WS_VSCROLL | WS_TABSTOP - PUSHBUTTON "Big Motor",ID_BIG_MOTOR,304,224,50,14 - PUSHBUTTON "Small Motor",ID_SMALL_MOTOR,360,224,50,14 - // Input options: - PUSHBUTTON "Quick Setup",ID_QUICK_SETUP,358,7,59,15 - CTEXT "",IDC_QUICK_SETUP_TEXT,196,7,156,15,WS_BORDER - CONTROL "Configure on bind",IDC_CONFIGURE_ON_BIND,"Button",BS_AUTOCHECKBOX | WS_TABSTOP,347,266,70,15 - PUSHBUTTON "Show Special Inputs",ID_SPECIAL_INPUTS,196,249,90,15 - COMBOBOX IDC_DEVICE_SELECT,304,250,113,70,CBS_DROPDOWNLIST | WS_VSCROLL | WS_TABSTOP - // Special bindings: - PUSHBUTTON "Lock Input",ID_LOCK_ALL_INPUT,7,249,58,15 - PUSHBUTTON "Lock Direction",ID_LOCK_DIRECTION,69,249,58,15 - PUSHBUTTON "Lock Buttons",ID_LOCK_BUTTONS,131,249,58,15 - PUSHBUTTON "Turbo",ID_TURBO_KEY,7,265,58,15 - PUSHBUTTON "Exclude Input",ID_EXCLUDE,131,265,58,15 - // Force Feedback & Input configurations: - PUSHBUTTON "Back to Controls",ID_CONTROLS,196,260,59,15 - PUSHBUTTON "Reset Configuration",ID_RESET_CONFIG,259,260,72,15 - // Force Feedback configuration: - GROUPBOX "",ID_FF,195,9,222,248 - COMBOBOX IDC_FF_EFFECT,203,23,206,106,CBS_DROPDOWNLIST | WS_VSCROLL | WS_TABSTOP - CONTROL "",IDC_FF_AXIS1,"msctls_trackbar32",WS_TABSTOP,199,43,214,17 - CONTROL "Axis 1",IDC_FF_AXIS1_ENABLED,"Button",BS_AUTOCHECKBOX | WS_TABSTOP,205,63,91,10 - CONTROL "Flip",IDC_FF_AXIS1_FLIP,"Button",BS_AUTOCHECKBOX | WS_TABSTOP,302,63,35,10 - EDITTEXT IDC_FF_AXIS1_SCALE,375,63,33,12,ES_RIGHT | ES_READONLY | NOT WS_BORDER | NOT WS_TABSTOP,WS_EX_RTLREADING - CONTROL "",IDC_FF_AXIS2,"msctls_trackbar32",WS_TABSTOP,199,79,214,17 - CONTROL "Axis 2",IDC_FF_AXIS2_ENABLED,"Button",BS_AUTOCHECKBOX | WS_TABSTOP,205,99,91,10 - CONTROL "Flip",IDC_FF_AXIS2_FLIP,"Button",BS_AUTOCHECKBOX | WS_TABSTOP,302,99,35,10 - EDITTEXT IDC_FF_AXIS2_SCALE,375,99,33,12,ES_RIGHT | ES_READONLY | NOT WS_BORDER | NOT WS_TABSTOP,WS_EX_RTLREADING - CONTROL "",IDC_FF_AXIS3,"msctls_trackbar32",WS_TABSTOP,199,115,214,17 - CONTROL "Axis 3",IDC_FF_AXIS3_ENABLED,"Button",BS_AUTOCHECKBOX | WS_TABSTOP,205,135,91,10 - CONTROL "Flip",IDC_FF_AXIS3_FLIP,"Button",BS_AUTOCHECKBOX | WS_TABSTOP,302,135,35,10 - EDITTEXT IDC_FF_AXIS3_SCALE,375,135,33,12,ES_RIGHT | ES_READONLY | NOT WS_BORDER | NOT WS_TABSTOP,WS_EX_RTLREADING - CONTROL "",IDC_FF_AXIS4,"msctls_trackbar32",WS_TABSTOP,199,151,214,17 - CONTROL "Axis 4",IDC_FF_AXIS4_ENABLED,"Button",BS_AUTOCHECKBOX | WS_TABSTOP,205,171,91,10 - CONTROL "Flip",IDC_FF_AXIS4_FLIP,"Button",BS_AUTOCHECKBOX | WS_TABSTOP,302,171,35,10 - EDITTEXT IDC_FF_AXIS4_SCALE,375,171,33,12,ES_RIGHT | ES_READONLY | NOT WS_BORDER | NOT WS_TABSTOP,WS_EX_RTLREADING - CONTROL "",IDC_FF_AXIS5,"msctls_trackbar32",WS_TABSTOP,199,187,214,17 - CONTROL "Axis 5",IDC_FF_AXIS5_ENABLED,"Button",BS_AUTOCHECKBOX | WS_TABSTOP,205,207,91,10 - CONTROL "Flip",IDC_FF_AXIS5_FLIP,"Button",BS_AUTOCHECKBOX | WS_TABSTOP,302,207,35,10 - EDITTEXT IDC_FF_AXIS5_SCALE,375,207,33,12,ES_RIGHT | ES_READONLY | NOT WS_BORDER | NOT WS_TABSTOP,WS_EX_RTLREADING - CONTROL "",IDC_FF_AXIS6,"msctls_trackbar32",WS_TABSTOP,199,223,214,17 - CONTROL "Axis 6",IDC_FF_AXIS6_ENABLED,"Button",BS_AUTOCHECKBOX | WS_TABSTOP,205,243,91,10 - CONTROL "Flip",IDC_FF_AXIS6_FLIP,"Button",BS_AUTOCHECKBOX | WS_TABSTOP,302,243,35,10 - EDITTEXT IDC_FF_AXIS6_SCALE,375,243,33,12,ES_RIGHT | ES_READONLY | NOT WS_BORDER | NOT WS_TABSTOP,WS_EX_RTLREADING - PUSHBUTTON "Test",ID_TEST,335,260,59,15 - // Input configuration: - GROUPBOX "Configure Binding",ID_SENSITIVITY,195,9,222,110 - EDITTEXT IDC_AXIS_DEVICE,202,22,72,12,ES_READONLY | NOT WS_BORDER | NOT WS_TABSTOP - COMBOBOX IDC_AXIS_DIRECTION,276,20,70,47,CBS_DROPDOWNLIST | WS_TABSTOP - EDITTEXT IDC_AXIS_CONTROL,349,22,65,12,ES_READONLY | NOT WS_BORDER | NOT WS_TABSTOP - CONTROL "Flip",IDC_FLIP,"Button",BS_AUTOCHECKBOX | WS_TABSTOP,202,37,27,10 - CONTROL "Rapid Fire",IDC_RAPID_FIRE,"Button",BS_AUTOCHECKBOX | WS_TABSTOP,232,37,44,10 - LTEXT "Sensitivity",IDC_LABEL_SENSITIVITY,202,51,42,8 - CONTROL "",IDC_SLIDER_SENSITIVITY,"msctls_trackbar32",WS_TABSTOP,240,51,131,17 - EDITTEXT IDC_AXIS_SENSITIVITY,372,53,33,12,ES_RIGHT | ES_READONLY | NOT WS_BORDER | NOT WS_TABSTOP,WS_EX_RTLREADING - LTEXT "Dead Zone",IDC_LABEL_DEADZONE,202,71,42,8 - CONTROL "",IDC_SLIDER_DEADZONE,"msctls_trackbar32",WS_TABSTOP,240,71,131,17 - EDITTEXT IDC_AXIS_DEADZONE,372,73,33,12,ES_RIGHT | ES_READONLY | NOT WS_BORDER | NOT WS_TABSTOP,WS_EX_RTLREADING - LTEXT "Skip Dead Zone",IDC_LABEL_SKIP_DEADZONE,202,92,42,16 - CONTROL "",IDC_SLIDER_SKIP_DEADZONE,"msctls_trackbar32",WS_TABSTOP,240,92,131,17 - EDITTEXT IDC_AXIS_SKIP_DEADZONE,372,94,33,12,ES_RIGHT | ES_READONLY | NOT WS_BORDER | NOT WS_TABSTOP,WS_EX_RTLREADING - LTEXT "Off",IDC_SKIP_DEADZONE_OFF,390,94,20,12 -END - -IDD_CONFIG_GUITAR DIALOGEX 0, 0, 424, 283 -STYLE DS_SETFONT | DS_MODALFRAME | DS_FIXEDSYS | WS_POPUP | WS_CAPTION -FONT 8, "MS Shell Dlg", 0, 0, 0x1 -BEGIN - CONTROL "",IDC_BINDINGS_LIST,"SysListView32",LVS_REPORT | LVS_SHOWSELALWAYS | LVS_NOSORTHEADER | WS_BORDER | WS_VSCROLL | WS_TABSTOP,7,7,183,237,WS_EX_CLIENTEDGE - // Input Bindings: - PUSHBUTTON "Fret 1",ID_R2,283,30,45,15 - PUSHBUTTON "Fret 2",ID_CIRCLE,283,53,45,15 - PUSHBUTTON "Fret 3",ID_TRIANGLE,283,76,45,15 - PUSHBUTTON "Fret 4",ID_CROSS,283,99,45,15 - PUSHBUTTON "Fret 5",ID_SQUARE,283,122,45,15 - PUSHBUTTON "Start",ID_START,256,145,45,15 - PUSHBUTTON "Select/Tilt",ID_SELECT,307,145,45,15 - PUSHBUTTON "Whammy Bar Up",ID_LSTICK_UP,219,168,69,15 - PUSHBUTTON "Whammy Bar Down",ID_LSTICK_DOWN,219,191,69,15 - PUSHBUTTON "Strum Bar Up",ID_DPAD_UP,336,168,58,15 - PUSHBUTTON "Strum Bar Down",ID_DPAD_DOWN,336,191,58,15 - // Force Feedback bindings: - GROUPBOX "Add Force Feedback Effect",ID_FORCEFEEDBACK_BOX,196,215,221,30 - COMBOBOX IDC_FORCEFEEDBACK,202,225,96,118,CBS_DROPDOWNLIST | WS_VSCROLL | WS_TABSTOP - PUSHBUTTON "Big Motor",ID_BIG_MOTOR,304,224,50,14 - PUSHBUTTON "Small Motor",ID_SMALL_MOTOR,360,224,50,14 - // Input options: - PUSHBUTTON "Quick Setup",ID_QUICK_SETUP,358,7,59,15 - CTEXT "",IDC_QUICK_SETUP_TEXT,196,7,156,15,WS_BORDER - CONTROL "Configure on bind",IDC_CONFIGURE_ON_BIND,"Button",BS_AUTOCHECKBOX | WS_TABSTOP,347,266,70,15 - PUSHBUTTON "Show Special Inputs",ID_SPECIAL_INPUTS,196,249,90,15 - COMBOBOX IDC_DEVICE_SELECT,304,250,113,70,CBS_DROPDOWNLIST | WS_VSCROLL | WS_TABSTOP - // Special bindings: - PUSHBUTTON "Lock Input",ID_LOCK_ALL_INPUT,7,249,58,15 - PUSHBUTTON "Lock Direction",ID_LOCK_DIRECTION,69,249,58,15 - PUSHBUTTON "Lock Buttons",ID_LOCK_BUTTONS,131,249,58,15 - PUSHBUTTON "Turbo",ID_TURBO_KEY,7,265,58,15 - PUSHBUTTON "Exclude Input",ID_EXCLUDE,131,265,58,15 - // Force Feedback & Input configurations: - PUSHBUTTON "Back to Controls",ID_CONTROLS,196,260,59,15 - PUSHBUTTON "Reset Configuration",ID_RESET_CONFIG,259,260,72,15 - // Force Feedback configuration: - GROUPBOX "",ID_FF,195,9,222,248 - COMBOBOX IDC_FF_EFFECT,203,23,206,106,CBS_DROPDOWNLIST | WS_VSCROLL | WS_TABSTOP - CONTROL "",IDC_FF_AXIS1,"msctls_trackbar32",WS_TABSTOP,199,43,214,17 - CONTROL "Axis 1",IDC_FF_AXIS1_ENABLED,"Button",BS_AUTOCHECKBOX | WS_TABSTOP,205,63,91,10 - CONTROL "Flip",IDC_FF_AXIS1_FLIP,"Button",BS_AUTOCHECKBOX | WS_TABSTOP,302,63,35,10 - EDITTEXT IDC_FF_AXIS1_SCALE,375,63,33,12,ES_RIGHT | ES_READONLY | NOT WS_BORDER | NOT WS_TABSTOP,WS_EX_RTLREADING - CONTROL "",IDC_FF_AXIS2,"msctls_trackbar32",WS_TABSTOP,199,79,214,17 - CONTROL "Axis 2",IDC_FF_AXIS2_ENABLED,"Button",BS_AUTOCHECKBOX | WS_TABSTOP,205,99,91,10 - CONTROL "Flip",IDC_FF_AXIS2_FLIP,"Button",BS_AUTOCHECKBOX | WS_TABSTOP,302,99,35,10 - EDITTEXT IDC_FF_AXIS2_SCALE,375,99,33,12,ES_RIGHT | ES_READONLY | NOT WS_BORDER | NOT WS_TABSTOP,WS_EX_RTLREADING - CONTROL "",IDC_FF_AXIS3,"msctls_trackbar32",WS_TABSTOP,199,115,214,17 - CONTROL "Axis 3",IDC_FF_AXIS3_ENABLED,"Button",BS_AUTOCHECKBOX | WS_TABSTOP,205,135,91,10 - CONTROL "Flip",IDC_FF_AXIS3_FLIP,"Button",BS_AUTOCHECKBOX | WS_TABSTOP,302,135,35,10 - EDITTEXT IDC_FF_AXIS3_SCALE,375,135,33,12,ES_RIGHT | ES_READONLY | NOT WS_BORDER | NOT WS_TABSTOP,WS_EX_RTLREADING - CONTROL "",IDC_FF_AXIS4,"msctls_trackbar32",WS_TABSTOP,199,151,214,17 - CONTROL "Axis 4",IDC_FF_AXIS4_ENABLED,"Button",BS_AUTOCHECKBOX | WS_TABSTOP,205,171,91,10 - CONTROL "Flip",IDC_FF_AXIS4_FLIP,"Button",BS_AUTOCHECKBOX | WS_TABSTOP,302,171,35,10 - EDITTEXT IDC_FF_AXIS4_SCALE,375,171,33,12,ES_RIGHT | ES_READONLY | NOT WS_BORDER | NOT WS_TABSTOP,WS_EX_RTLREADING - CONTROL "",IDC_FF_AXIS5,"msctls_trackbar32",WS_TABSTOP,199,187,214,17 - CONTROL "Axis 5",IDC_FF_AXIS5_ENABLED,"Button",BS_AUTOCHECKBOX | WS_TABSTOP,205,207,91,10 - CONTROL "Flip",IDC_FF_AXIS5_FLIP,"Button",BS_AUTOCHECKBOX | WS_TABSTOP,302,207,35,10 - EDITTEXT IDC_FF_AXIS5_SCALE,375,207,33,12,ES_RIGHT | ES_READONLY | NOT WS_BORDER | NOT WS_TABSTOP,WS_EX_RTLREADING - CONTROL "",IDC_FF_AXIS6,"msctls_trackbar32",WS_TABSTOP,199,223,214,17 - CONTROL "Axis 6",IDC_FF_AXIS6_ENABLED,"Button",BS_AUTOCHECKBOX | WS_TABSTOP,205,243,91,10 - CONTROL "Flip",IDC_FF_AXIS6_FLIP,"Button",BS_AUTOCHECKBOX | WS_TABSTOP,302,243,35,10 - EDITTEXT IDC_FF_AXIS6_SCALE,375,243,33,12,ES_RIGHT | ES_READONLY | NOT WS_BORDER | NOT WS_TABSTOP,WS_EX_RTLREADING - PUSHBUTTON "Test",ID_TEST,335,260,59,15 - // Input configuration: - GROUPBOX "Configure Binding",ID_SENSITIVITY,195,9,222,110 - EDITTEXT IDC_AXIS_DEVICE,202,22,72,12,ES_READONLY | NOT WS_BORDER | NOT WS_TABSTOP - COMBOBOX IDC_AXIS_DIRECTION,276,20,70,47,CBS_DROPDOWNLIST | WS_TABSTOP - EDITTEXT IDC_AXIS_CONTROL,349,22,65,12,ES_READONLY | NOT WS_BORDER | NOT WS_TABSTOP - CONTROL "Flip",IDC_FLIP,"Button",BS_AUTOCHECKBOX | WS_TABSTOP,202,37,27,10 - CONTROL "Rapid Fire",IDC_RAPID_FIRE,"Button",BS_AUTOCHECKBOX | WS_TABSTOP,232,37,44,10 - LTEXT "Sensitivity",IDC_LABEL_SENSITIVITY,202,51,42,8 - CONTROL "",IDC_SLIDER_SENSITIVITY,"msctls_trackbar32",WS_TABSTOP,240,51,131,17 - EDITTEXT IDC_AXIS_SENSITIVITY,372,53,33,12,ES_RIGHT | ES_READONLY | NOT WS_BORDER | NOT WS_TABSTOP,WS_EX_RTLREADING - LTEXT "Dead Zone",IDC_LABEL_DEADZONE,202,71,42,8 - CONTROL "",IDC_SLIDER_DEADZONE,"msctls_trackbar32",WS_TABSTOP,240,71,131,17 - EDITTEXT IDC_AXIS_DEADZONE,372,73,33,12,ES_RIGHT | ES_READONLY | NOT WS_BORDER | NOT WS_TABSTOP,WS_EX_RTLREADING - LTEXT "Skip Dead Zone",IDC_LABEL_SKIP_DEADZONE,202,92,42,16 - CONTROL "",IDC_SLIDER_SKIP_DEADZONE,"msctls_trackbar32",WS_TABSTOP,240,92,131,17 - EDITTEXT IDC_AXIS_SKIP_DEADZONE,372,94,33,12,ES_RIGHT | ES_READONLY | NOT WS_BORDER | NOT WS_TABSTOP,WS_EX_RTLREADING - LTEXT "Off",IDC_SKIP_DEADZONE_OFF,390,94,20,12 -END - -IDD_CONFIG_PS1_MOUSE DIALOGEX 0, 0, 424, 283 -STYLE DS_SETFONT | DS_MODALFRAME | DS_FIXEDSYS | WS_POPUP | WS_CAPTION -FONT 8, "MS Shell Dlg", 0, 0, 0x1 -BEGIN - CONTROL "",IDC_BINDINGS_LIST,"SysListView32",LVS_REPORT | LVS_SHOWSELALWAYS | LVS_NOSORTHEADER | WS_BORDER | WS_VSCROLL | WS_TABSTOP,7,7,183,237,WS_EX_CLIENTEDGE - // Input bindings: - PUSHBUTTON "Mouse",ID_MOUSE,379,28,34,15 - GROUPBOX "Mouse buttons",IDC_FACE,242,46,124,34 - PUSHBUTTON "Left-click",ID_CIRCLE,256,57,45,15 - PUSHBUTTON "Right-click",ID_CROSS,307,57,45,15 - GROUPBOX "Mouse Axis",IDC_LSTICK,242,81,124,74 - PUSHBUTTON "X-axis Left",ID_LSTICK_LEFT,256,112,45,15 - PUSHBUTTON "X-axis Right",ID_LSTICK_RIGHT,307,112,45,15 - PUSHBUTTON "Y-axis Up",ID_LSTICK_UP,283,92,45,15 - PUSHBUTTON "Y-axis Down",ID_LSTICK_DOWN,283,132,45,15 - // Force Feedback bindings: - GROUPBOX "Add Force Feedback Effect",ID_FORCEFEEDBACK_BOX,196,215,221,30 - COMBOBOX IDC_FORCEFEEDBACK,202,225,96,118,CBS_DROPDOWNLIST | WS_VSCROLL | WS_TABSTOP - PUSHBUTTON "Big Motor",ID_BIG_MOTOR,304,224,50,14 - PUSHBUTTON "Small Motor",ID_SMALL_MOTOR,360,224,50,14 - // Input options: - PUSHBUTTON "Quick Setup",ID_QUICK_SETUP,358,7,59,15 - CTEXT "",IDC_QUICK_SETUP_TEXT,196,7,156,15,WS_BORDER - CONTROL "Configure on bind",IDC_CONFIGURE_ON_BIND,"Button",BS_AUTOCHECKBOX | WS_TABSTOP,347,266,70,15 - PUSHBUTTON "Show Special Inputs",ID_SPECIAL_INPUTS,196,249,90,15 - COMBOBOX IDC_DEVICE_SELECT,304,250,113,70,CBS_DROPDOWNLIST | WS_VSCROLL | WS_TABSTOP - // Special bindings: - PUSHBUTTON "Lock Input",ID_LOCK_ALL_INPUT,7,249,58,15 - PUSHBUTTON "Lock Direction",ID_LOCK_DIRECTION,69,249,58,15 - PUSHBUTTON "Lock Buttons",ID_LOCK_BUTTONS,131,249,58,15 - PUSHBUTTON "Turbo",ID_TURBO_KEY,7,265,58,15 - PUSHBUTTON "Exclude Input",ID_EXCLUDE,131,265,58,15 - // Force Feedback & Input configurations: - PUSHBUTTON "Back to Controls",ID_CONTROLS,196,260,59,15 - PUSHBUTTON "Reset Configuration",ID_RESET_CONFIG,259,260,72,15 - // Input configuration: - GROUPBOX "Configure Binding",ID_SENSITIVITY,195,9,222,110 - EDITTEXT IDC_AXIS_DEVICE,202,22,72,12,ES_READONLY | NOT WS_BORDER | NOT WS_TABSTOP - COMBOBOX IDC_AXIS_DIRECTION,276,20,70,47,CBS_DROPDOWNLIST | WS_TABSTOP - EDITTEXT IDC_AXIS_CONTROL,349,22,65,12,ES_READONLY | NOT WS_BORDER | NOT WS_TABSTOP - CONTROL "Flip",IDC_FLIP,"Button",BS_AUTOCHECKBOX | WS_TABSTOP,202,37,27,10 - CONTROL "Rapid Fire",IDC_RAPID_FIRE,"Button",BS_AUTOCHECKBOX | WS_TABSTOP,232,37,44,10 - LTEXT "Sensitivity",IDC_LABEL_SENSITIVITY,202,51,42,8 - CONTROL "",IDC_SLIDER_SENSITIVITY,"msctls_trackbar32",WS_TABSTOP,240,51,131,17 - EDITTEXT IDC_AXIS_SENSITIVITY,372,53,33,12,ES_RIGHT | ES_READONLY | NOT WS_BORDER | NOT WS_TABSTOP,WS_EX_RTLREADING - LTEXT "Dead Zone",IDC_LABEL_DEADZONE,202,71,42,8 - CONTROL "",IDC_SLIDER_DEADZONE,"msctls_trackbar32",WS_TABSTOP,240,71,131,17 - EDITTEXT IDC_AXIS_DEADZONE,372,73,33,12,ES_RIGHT | ES_READONLY | NOT WS_BORDER | NOT WS_TABSTOP,WS_EX_RTLREADING - LTEXT "Skip Dead Zone",IDC_LABEL_SKIP_DEADZONE,202,92,42,16 - CONTROL "",IDC_SLIDER_SKIP_DEADZONE,"msctls_trackbar32",WS_TABSTOP,240,92,131,17 - EDITTEXT IDC_AXIS_SKIP_DEADZONE,372,94,33,12,ES_RIGHT | ES_READONLY | NOT WS_BORDER | NOT WS_TABSTOP,WS_EX_RTLREADING - LTEXT "Off",IDC_SKIP_DEADZONE_OFF,390,94,20,12 -END - -IDD_CONFIG_NEGCON DIALOGEX 0, 0, 424, 283 -STYLE DS_SETFONT | DS_MODALFRAME | DS_FIXEDSYS | WS_POPUP | WS_CAPTION -FONT 8, "MS Shell Dlg", 0, 0, 0x1 -BEGIN - CONTROL "",IDC_BINDINGS_LIST,"SysListView32",LVS_REPORT | LVS_SHOWSELALWAYS | LVS_NOSORTHEADER | WS_BORDER | WS_VSCROLL | WS_TABSTOP,7,7,183,237,WS_EX_CLIENTEDGE - // Input bindings: - GROUPBOX "Shoulder buttons",IDC_SHOULDER,220,30,173,34 - PUSHBUTTON "Analog L",ID_L1,233,41,34,15 - PUSHBUTTON "Digital R",ID_R1,346,41,34,15 - GROUPBOX "D-Pad",IDC_DPAD,196,65,108,70 - PUSHBUTTON "Up",ID_DPAD_UP,233,76,34,15 - PUSHBUTTON "Left",ID_DPAD_LEFT,214,95,34,15 - PUSHBUTTON "Right",ID_DPAD_RIGHT,252,95,34,15 - PUSHBUTTON "Down",ID_DPAD_DOWN,233,114,34,15 - GROUPBOX "Digital Face buttons",IDC_FACE,310,65,108,34 - PUSHBUTTON "B",ID_TRIANGLE,327,76,34,15 - PUSHBUTTON "A",ID_CIRCLE,367,76,34,15 - GROUPBOX "Analog Face buttons",IDC_FACE_ANALOG,310,101,108,34 - PUSHBUTTON "II",ID_SQUARE,327,112,34,15 - PUSHBUTTON "I",ID_CROSS,367,112,34,15 - GROUPBOX "Analog Rotating Section",IDC_LSTICK,247,136,124,34 - PUSHBUTTON "Left",ID_LSTICK_LEFT,261,147,30,15 - PUSHBUTTON "Right",ID_LSTICK_RIGHT,327,147,30,15 - PUSHBUTTON "Start",ID_START,207,139,34,15 - PUSHBUTTON "Mouse",ID_MOUSE,207,158,34,15 - // Force Feedback bindings: - GROUPBOX "Add Force Feedback Effect",ID_FORCEFEEDBACK_BOX,196,215,221,30 - COMBOBOX IDC_FORCEFEEDBACK,202,225,96,118,CBS_DROPDOWNLIST | WS_VSCROLL | WS_TABSTOP - PUSHBUTTON "Big Motor",ID_BIG_MOTOR,304,224,50,14 - PUSHBUTTON "Small Motor",ID_SMALL_MOTOR,360,224,50,14 - // Input options: - PUSHBUTTON "Quick Setup",ID_QUICK_SETUP,358,7,59,15 - CTEXT "",IDC_QUICK_SETUP_TEXT,196,7,156,15,WS_BORDER - CONTROL "Configure on bind",IDC_CONFIGURE_ON_BIND,"Button",BS_AUTOCHECKBOX | WS_TABSTOP,347,266,70,15 - PUSHBUTTON "Show Special Inputs",ID_SPECIAL_INPUTS,196,249,90,15 - COMBOBOX IDC_DEVICE_SELECT,304,250,113,70,CBS_DROPDOWNLIST | WS_VSCROLL | WS_TABSTOP - // Special bindings: - PUSHBUTTON "Lock Input",ID_LOCK_ALL_INPUT,7,249,58,15 - PUSHBUTTON "Lock Direction",ID_LOCK_DIRECTION,69,249,58,15 - PUSHBUTTON "Lock Buttons",ID_LOCK_BUTTONS,131,249,58,15 - PUSHBUTTON "Turbo",ID_TURBO_KEY,7,265,58,15 - PUSHBUTTON "Exclude Input",ID_EXCLUDE,131,265,58,15 - // Force Feedback & Input configurations: - PUSHBUTTON "Back to Controls",ID_CONTROLS,196,260,59,15 - PUSHBUTTON "Reset Configuration",ID_RESET_CONFIG,259,260,72,15 - // Input configuration: - GROUPBOX "Configure Binding",ID_SENSITIVITY,195,9,222,110 - EDITTEXT IDC_AXIS_DEVICE,202,22,72,12,ES_READONLY | NOT WS_BORDER | NOT WS_TABSTOP - COMBOBOX IDC_AXIS_DIRECTION,276,20,70,47,CBS_DROPDOWNLIST | WS_TABSTOP - EDITTEXT IDC_AXIS_CONTROL,349,22,65,12,ES_READONLY | NOT WS_BORDER | NOT WS_TABSTOP - CONTROL "Flip",IDC_FLIP,"Button",BS_AUTOCHECKBOX | WS_TABSTOP,202,37,27,10 - CONTROL "Rapid Fire",IDC_RAPID_FIRE,"Button",BS_AUTOCHECKBOX | WS_TABSTOP,232,37,44,10 - LTEXT "Sensitivity",IDC_LABEL_SENSITIVITY,202,51,42,8 - CONTROL "",IDC_SLIDER_SENSITIVITY,"msctls_trackbar32",WS_TABSTOP,240,51,131,17 - EDITTEXT IDC_AXIS_SENSITIVITY,372,53,33,12,ES_RIGHT | ES_READONLY | NOT WS_BORDER | NOT WS_TABSTOP,WS_EX_RTLREADING - LTEXT "Dead Zone",IDC_LABEL_DEADZONE,202,71,42,8 - CONTROL "",IDC_SLIDER_DEADZONE,"msctls_trackbar32",WS_TABSTOP,240,71,131,17 - EDITTEXT IDC_AXIS_DEADZONE,372,73,33,12,ES_RIGHT | ES_READONLY | NOT WS_BORDER | NOT WS_TABSTOP,WS_EX_RTLREADING - LTEXT "Skip Dead Zone",IDC_LABEL_SKIP_DEADZONE,202,92,42,16 - CONTROL "",IDC_SLIDER_SKIP_DEADZONE,"msctls_trackbar32",WS_TABSTOP,240,92,131,17 - EDITTEXT IDC_AXIS_SKIP_DEADZONE,372,94,33,12,ES_RIGHT | ES_READONLY | NOT WS_BORDER | NOT WS_TABSTOP,WS_EX_RTLREADING - LTEXT "Off",IDC_SKIP_DEADZONE_OFF,390,94,20,12 -END - -IDD_GENERAL DIALOGEX 0, 0, 424, 283 -STYLE DS_SETFONT | DS_MODALFRAME | DS_FIXEDSYS | WS_POPUP | WS_CAPTION -FONT 8, "MS Shell Dlg", 0, 0, 0x1 -BEGIN - GROUPBOX "Input APIs",IDC_STATIC,7,0,410,133 - GROUPBOX "Keyboard API",IDC_STATIC,16,16,192,39 - CONTROL "Windows messaging (Recommended)",IDC_KB_WM,"Button",BS_AUTORADIOBUTTON | WS_GROUP,22,28,137,10 - CONTROL "Raw input",IDC_KB_RAW,"Button",BS_AUTORADIOBUTTON,22,40,112,10 - GROUPBOX "Game Device APIs",IDC_STATIC,16,59,191,62 - CONTROL "DirectInput (Legacy)",IDC_G_DI,"Button",BS_AUTOCHECKBOX | WS_TABSTOP,22,71,100,10 - CONTROL "XInput (Modern)",IDC_G_XI,"Button",BS_AUTOCHECKBOX | WS_TABSTOP,22,82,100,10 - CONTROL "DualShock 3 native mode (Requires sixaxis)",IDC_G_DS3, - "Button",BS_AUTOCHECKBOX | WS_TABSTOP,22,94,155,10 - CONTROL "Monitor when in background",IDC_BACKGROUND,"Button",BS_AUTOCHECKBOX | WS_TABSTOP,22,105,107,10 - GROUPBOX "Mouse API",IDC_STATIC,216,16,192,61 - CONTROL "Windows messaging (Recommended)",IDC_M_WM,"Button",BS_AUTORADIOBUTTON | WS_GROUP,223,27,137,10 - CONTROL "Raw input",IDC_M_RAW,"Button",BS_AUTORADIOBUTTON,223,39,112,10 - CONTROL "Disable",IDC_M_DISABLE,"Button",BS_AUTORADIOBUTTON,223,51,39,10 - CONTROL "Start without mouse focus",IDC_MOUSE_UNFOCUS,"Button",BS_AUTOCHECKBOX | WS_TABSTOP,223,63,98,10 - GROUPBOX "Pads",IDC_STATIC,7,142,410,62 - CONTROL "Port 1 Multitap",IDC_MULTITAP1,"Button",BS_AUTOCHECKBOX | WS_TABSTOP,14,152,63,10 - CONTROL "Port 2 Multitap",IDC_MULTITAP2,"Button",BS_AUTOCHECKBOX | WS_TABSTOP,14,164,63,10 - CONTROL "Multiple bindings",IDC_MULTIPLE_BINDING,"Button",BS_AUTOCHECKBOX | WS_TABSTOP,14,176,66,10 - CONTROL "",IDC_PAD_LIST,"SysListView32",LVS_REPORT | LVS_SINGLESEL | LVS_SHOWSELALWAYS | LVS_ALIGNLEFT | WS_TABSTOP,81,151,183,48,WS_EX_CLIENTEDGE - COMBOBOX IDC_PAD_TYPE,270,151,140,118,CBS_DROPDOWNLIST | WS_VSCROLL | WS_TABSTOP - GROUPBOX "Device Diagnostics",IDC_STATIC,78,206,264,74 - CONTROL "",IDC_DIAG_LIST,"SysListView32",LVS_LIST | LVS_SINGLESEL | LVS_SHOWSELALWAYS | LVS_NOSORTHEADER | WS_BORDER | WS_VSCROLL | WS_TABSTOP,84,217,252,57,WS_EX_CLIENTEDGE - GROUPBOX "Hacks and advanced features",IDC_STATIC,216,82,192,39 - CONTROL "Enable logging",IDC_DEBUG_FILE,"Button",BS_AUTOCHECKBOX | WS_TABSTOP,223,94,79,10 - CONTROL "Guitar Hero 2 Hack",IDC_GH2_HACK,"Button",BS_AUTOCHECKBOX | WS_TABSTOP,223,106,79,10 - PUSHBUTTON "Restore Defaults",ID_RESTORE_DEFAULTS,306,168,62,15 - PUSHBUTTON "Load Bindings",ID_LOAD,271,186,62,15 - PUSHBUTTON "Save Bindings",ID_SAVE,345,186,62,15 -END - -IDD_DIAG DIALOGEX 0, 0, 190, 178 -STYLE DS_SETFONT | DS_MODALFRAME | DS_FIXEDSYS | WS_POPUP | WS_CAPTION | WS_SYSMENU -CAPTION " " -FONT 8, "MS Shell Dlg", 400, 0, 0x1 -BEGIN - CONTROL "",IDC_DIAG_LIST,"SysListView32",LVS_REPORT | LVS_SINGLESEL | LVS_NOSORTHEADER | WS_BORDER | WS_VSCROLL | WS_TABSTOP,7,7,176,164,WS_EX_CLIENTEDGE -END - - -///////////////////////////////////////////////////////////////////////////// -// -// DESIGNINFO -// - -#ifdef APSTUDIO_INVOKED -GUIDELINES DESIGNINFO -BEGIN - IDD_CONFIG, DIALOG - BEGIN - LEFTMARGIN, 7 - RIGHTMARGIN, 417 - TOPMARGIN, 7 - END - - IDD_CONFIG_POPN, DIALOG - BEGIN - LEFTMARGIN, 7 - RIGHTMARGIN, 417 - TOPMARGIN, 7 - END - - IDD_CONFIG_GUITAR, DIALOG - BEGIN - LEFTMARGIN, 7 - RIGHTMARGIN, 417 - TOPMARGIN, 7 - END - - IDD_CONFIG_PS1_MOUSE, DIALOG - BEGIN - LEFTMARGIN, 7 - RIGHTMARGIN, 417 - TOPMARGIN, 7 - END - - IDD_CONFIG_NEGCON, DIALOG - BEGIN - LEFTMARGIN, 7 - RIGHTMARGIN, 417 - TOPMARGIN, 7 - END - - IDD_GENERAL, DIALOG - BEGIN - LEFTMARGIN, 7 - RIGHTMARGIN, 417 - TOPMARGIN, 7 - END - - IDD_DIAG, DIALOG - BEGIN - LEFTMARGIN, 7 - RIGHTMARGIN, 183 - TOPMARGIN, 7 - BOTTOMMARGIN, 171 - END -END -#endif // APSTUDIO_INVOKED - - -///////////////////////////////////////////////////////////////////////////// -// -// RCDATA -// - -IDR_INI1 RCDATA "Default.ini" - - -///////////////////////////////////////////////////////////////////////////// -// -// Icon -// - -// Icon with lowest ID value placed first to ensure application icon -// remains consistent on all systems. - -#endif // English (United States) resources -///////////////////////////////////////////////////////////////////////////// - - - -#ifndef APSTUDIO_INVOKED -///////////////////////////////////////////////////////////////////////////// -// -// Generated from the TEXTINCLUDE 3 resource. -// - - -///////////////////////////////////////////////////////////////////////////// -#endif // not APSTUDIO_INVOKED diff --git a/pcsx2/PAD/Windows/PADConfig.cpp b/pcsx2/PAD/Windows/PADConfig.cpp deleted file mode 100644 index 2793dd5185..0000000000 --- a/pcsx2/PAD/Windows/PADConfig.cpp +++ /dev/null @@ -1,3120 +0,0 @@ -/* PCSX2 - PS2 Emulator for PCs - * Copyright (C) 2002-2020 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 . - */ - -#include "PrecompiledHeader.h" -#include "Global.h" - -#include "common/Path.h" -#include "common/StringUtil.h" - -#include "resource_pad.h" -#include "InputManager.h" -#include "PADConfig.h" - -#include "Tooltips.h" -#include "Diagnostics.h" -#include "DeviceEnumerator.h" -#include "KeyboardQueue.h" -#include "WndProcEater.h" -#include "DualShock3.h" - -// Needed to know if raw input is available. It requires XP or higher. -#include "PADRawInput.h" -#include "Config.h" - -//max len 24 wchar_t -const wchar_t* padTypes[] = { - L"Unplugged", - L"Dualshock 2", - L"Guitar", - L"Pop'n Music controller", - L"PS1 Mouse", - L"neGcon"}; - -GeneralConfig config; -HWND hWndProp = 0; -int selected = 0; -bool quickSetup = false; - -// Older versions of PCSX2 don't always create the ini dir on startup, so PAD does it -// for it. But if PCSX2 sets the ini path with a call to setSettingsDir, then it means -// we shouldn't make our own. -bool createIniDir = true; - -HWND hWnds[2][4][numPadTypes]; -HWND hWndGeneral = 0; - -struct GeneralSettingsBool -{ - wchar_t* name; - unsigned int ControlId; - u8 defaultValue; -}; - -// Ties together config data structure, config files, and general config -// dialog. -const GeneralSettingsBool BoolOptionsInfo[] = { - {L"Mouse Unfocus", IDC_MOUSE_UNFOCUS, 1}, - {L"Background", IDC_BACKGROUND, 1}, - {L"Multiple Bindings", IDC_MULTIPLE_BINDING, 0}, - - {L"DirectInput Game Devices", IDC_G_DI, 1}, - {L"XInput", IDC_G_XI, 1}, - {L"DualShock 3", IDC_G_DS3, 0}, - - {L"Multitap 1", IDC_MULTITAP1, 0}, - {L"Multitap 2", IDC_MULTITAP2, 0}, - - {L"Logging", IDC_DEBUG_FILE, 0}, - - {L"GH2", IDC_GH2_HACK, 0}, -}; - -void Populate(int port, int slot, int padtype); - -void SetupLogSlider(HWND hWndSlider) -{ - SendMessage(hWndSlider, TBM_SETRANGEMIN, 0, 1); - SendMessage(hWndSlider, TBM_SETPAGESIZE, 0, 1 << 13); - SendMessage(hWndSlider, TBM_SETRANGEMAX, 1, 1 << 22); - SendMessage(hWndSlider, TBM_SETTIC, 0, 1 << 21); - SendMessage(hWndSlider, TBM_SETTIC, 0, 1 << 20); - SendMessage(hWndSlider, TBM_SETTIC, 0, 3 << 20); -} - -int GetLogSliderVal(HWND hWnd, int id) -{ - HWND hWndSlider = GetDlgItem(hWnd, id); - int val = SendMessage(hWndSlider, TBM_GETPOS, 0, 0); - if (val <= (1 << 21)) - { - val = (val + 16) >> 5; - } - else - { - double v = (val - (1 << 21)) / (double)((1 << 22) - (1 << 21)); - double max = log((double)(1 << 23)); - double min = log((double)BASE_SENSITIVITY); - v = v * (max - min) + min; - // exp is not implemented in ntdll, so use pow instead. - // More than accurate enough for my needs. - // v = exp(v); - v = pow(2.7182818284590451, v); - if (v > (1 << 23)) - v = (1 << 23); - val = (int)floor(v + 0.5); - } - if (!val) - val = 1; - if (IsDlgButtonChecked(hWnd, id + 1) == BST_CHECKED) - { - val = -val; - } - return val; -} - -void SetLogSliderVal(HWND hWnd, int id, HWND hWndText, int val) -{ - HWND hWndSlider = GetDlgItem(hWnd, id); - int sliderPos = 0; - wchar_t temp[30]; - EnableWindow(hWndSlider, val != 0); - EnableWindow(hWndText, val != 0); - EnableWindow(GetDlgItem(hWnd, id + 1), val != 0); - if (!val) - val = BASE_SENSITIVITY; - CheckDlgButton(hWnd, id + 1, BST_CHECKED * (val < 0)); - if (val < 0) - { - val = -val; - } - if (val <= BASE_SENSITIVITY) - { - sliderPos = val << 5; - } - else - { - double max = log((double)(1 << 23)); - double min = log((double)BASE_SENSITIVITY); - double v = log((double)(val)); - v = (v - min) / (max - min); - v = ((1 << 22) - (1 << 21)) * v + (1 << 21); - // _ftol - sliderPos = (int)(floor(v + 0.5)); - } - SendMessage(hWndSlider, TBM_SETPOS, 1, sliderPos); - int val2 = (int)(1000 * (__int64)val / BASE_SENSITIVITY); - wsprintfW(temp, L"%i.%03i", val2 / 1000, val2 % 1000); - SetWindowTextW(hWndText, temp); -} - -void RefreshEnabledDevices(int updateDeviceList) -{ - // Clears all device state. - static int lastXInputState = -1; - if (updateDeviceList || lastXInputState != config.gameApis.xInput) - { - EnumDevices(config.gameApis.xInput); - lastXInputState = config.gameApis.xInput; - } - - for (int i = 0; i < dm->numDevices; i++) - { - Device* dev = dm->devices[i]; - - if (!dev->attached && dev->displayName[0] != '[') - { - wchar_t* newName = (wchar_t*)malloc(sizeof(wchar_t) * (wcslen(dev->displayName) + 12)); - wsprintfW(newName, L"[Detached] %s", dev->displayName); - free(dev->displayName); - dev->displayName = newName; - } - - if ((dev->type == KEYBOARD && dev->api == IGNORE_KEYBOARD) || - (dev->type == KEYBOARD && dev->api == config.keyboardApi) || - (dev->type == MOUSE && dev->api == config.mouseApi) || - (dev->type == OTHER && - ((dev->api == DI && config.gameApis.directInput) || - (dev->api == DS3 && config.gameApis.dualShock3) || - (dev->api == XINPUT && config.gameApis.xInput)))) - { - if (config.gameApis.dualShock3 && dev->api == DI && dev->displayName && - !wcsicmp(dev->displayName, L"DX PLAYSTATION(R)3 Controller")) - { - dm->DisableDevice(i); - } - else - { - dm->EnableDevice(i); - } - } - else - { - dm->DisableDevice(i); - } - } -} - -// Disables/enables devices as necessary. Also updates diagnostic list -// and pad1/pad2 bindings lists, depending on params. Also updates device -// list, if indicated. - -// Must be called at some point when entering config mode, as I disable -// (non-keyboard/mice) devices with no active bindings when the emulator's running. -void RefreshEnabledDevicesAndDisplay(int updateDeviceList = 0, HWND hWnd = 0, int populate = 0) -{ - RefreshEnabledDevices(updateDeviceList); - if (hWnd) - { - HWND hWndList = GetDlgItem(hWnd, IDC_DIAG_LIST); - ListView_SetExtendedListViewStyleEx(hWndList, LVS_EX_DOUBLEBUFFER, LVS_EX_DOUBLEBUFFER); - int count = ListView_GetItemCount(hWndList); - LVITEM item; - item.iItem = 0; - item.iSubItem = 0; - item.mask = LVIF_TEXT | LVIF_PARAM; - for (int j = 0; j < dm->numDevices; j++) - { - if (dm->devices[j]->enabled && dm->devices[j]->api != IGNORE_KEYBOARD) - { - item.lParam = j; - item.pszText = dm->devices[j]->displayName; - if (count > 0) - { - ListView_SetItem(hWndList, &item); - count--; - } - else - { - ListView_InsertItem(hWndList, &item); - } - item.iItem++; - } - } - // This way, won't scroll list to start. - while (count > 0) - { - ListView_DeleteItem(hWndList, item.iItem); - count--; - } - } - if (populate) - { - for (int j = 0; j < numPadTypes; j++) - { - for (int i = 0; i < 8; i++) - { - Populate(i & 1, i >> 1, j); - } - } - } -} - -wchar_t* GetCommandStringW(u8 command, int port, int slot) -{ - int padtype = config.padConfigs[port][slot].type; - static wchar_t temp[34]; - if (command >= 0x20 && command <= 0x27) - { - if (padtype == GuitarPad && (command == 0x20 || command == 0x22)) - { - HWND hWnd = GetDlgItem(hWnds[port][slot][padtype], 0x40F0 + command); - int res = GetWindowTextW(hWnd, temp, 20); - if ((unsigned int)res - 1 <= 18) - return temp; - } - static wchar_t* stick[2] = {L"L-Stick", L"R-Stick"}; - static wchar_t* dir[] = { - L"Up", L"Right", - L"Down", L"Left"}; - wsprintfW(temp, L"%s %s", padtype == neGconPad ? L"Rotate" : stick[(command - 0x20) / 4], dir[command & 3]); - return temp; - } - /* Get text from the buttons. */ - if (command >= 0x0F && command <= 0x2D) - { - HWND hWnd = GetDlgItem(hWnds[port][slot][padtype], 0x40F0 + command); - if (!hWnd || ((padtype == Dualshock2Pad || padtype == neGconPad) && command >= 0x14 && command <= 0x17)) - { - wchar_t* strings[] = { - L"Mouse", // 0x0F (15) - L"Select", // 0x10 (16) - L"L3", // 0x11 (17) - L"R3", // 0x12 (18) - L"Start", // 0x13 (19) - L"D-Pad Up", // 0x14 (20) - L"D-Pad Right", // 0x15 (21) - L"D-Pad Down", // 0x16 (22) - L"D-Pad Left", // 0x17 (23) - L"L2", // 0x18 (24) - L"R2", // 0x19 (25) - L"L1", // 0x1A (26) - L"R1", // 0x1B (27) - L"Triangle", // 0x1C (28) - L"Circle", // 0x1D (29) - L"Square", // 0x1E (30) - L"Cross", // 0x1F (31) - L"L-Stick Up", // 0x20 (32) - L"L-Stick Right", // 0x21 (33) - L"L-Stick Down", // 0x22 (34) - L"L-Stick Left", // 0x23 (35) - L"R-Stick Up", // 0x24 (36) - L"R-Stick Right", // 0x25 (37) - L"R-Stick Down", // 0x26 (38) - L"R-Stick Left", // 0x27 (39) - L"Analog", // 0x28 (40) - L"Excluded Input", // 0x29 (41) - L"Lock Buttons", // 0x2A (42) - L"Lock Input", // 0x2B (43) - L"Lock Direction", // 0x2C (44) - L"Turbo", // 0x2D (45) - }; - return strings[command - 0xF]; - } - int res = GetWindowTextW(hWnd, temp, 20); - if ((unsigned int)res - 1 <= 18) - return temp; - } - return L""; -} - -static wchar_t iniFileUSB[MAX_PATH * 2] = L"inis/PAD.ini"; - -// we disregard dir and use global settings instead, we should probably clean that up someday... -void PADsetSettingsDir(const char* dir) -{ - //swprintf_s( iniFileUSB, L"%S", (dir==NULL) ? "inis" : dir ); - - //uint targlen = MultiByteToWideChar(CP_ACP, 0, dir, -1, NULL, 0); - StrCpyNW(iniFileUSB, StringUtil::UTF8StringToWideString(Path::Combine(EmuFolders::Settings, "PAD.ini")).c_str(), std::size(iniFileUSB)); - - createIniDir = false; - - FILE* temp = nullptr; - _wfopen_s(&temp, iniFileUSB, L"r"); - if (!temp) - { // File not found, possibly. - HRSRC res = FindResource(GetModuleHandle(nullptr), MAKEINTRESOURCE(IDR_INI1), RT_RCDATA); - if (!res) - return; - HGLOBAL data = LoadResource(GetModuleHandle(nullptr), res); - if (!data) - return; - size_t size = SizeofResource(GetModuleHandle(nullptr), res); - u8* fdata = (u8*)LockResource(data); - _wfopen_s(&temp, iniFileUSB, L"w"); - if (!temp) - return; - fwrite(fdata, 1, size, temp); - fclose(temp); - } - else - { - fclose(temp); - } -} - -int GetBinding(int port, int slot, int index, Device*& dev, Binding*& b, ForceFeedbackBinding*& ffb); -int BindCommand(Device* dev, unsigned int uid, unsigned int port, unsigned int slot, unsigned int padtype, int command, int sensitivity, int rapidFire, int deadZone, int skipDeadZone); - -int CreateEffectBinding(Device* dev, wchar_t* effectName, unsigned int port, unsigned int slot, unsigned int padtype, unsigned int motor, ForceFeedbackBinding** binding); - -void SelChanged(int port, int slot) -{ - int padtype = config.padConfigs[port][slot].type; - HWND hWnd = hWnds[port][slot][padtype]; - if (!hWnd) - return; - HWND hWndTemp, hWndList = GetDlgItem(hWnd, IDC_BINDINGS_LIST); - int i = ListView_GetSelectedCount(hWndList); - wchar_t* devName = L"N/A"; - wchar_t* key = L"N/A"; - wchar_t* command = L"N/A"; - // Second value is now rapidFire. - int rapidFire = -1; - int sensitivity = 0; - int deadZone = 0; - int skipDeadZone = 0; - // Set if sensitivity != 0, but need to disable flip anyways. - // Only used to relative axes. - int disableFlip = 0; - wchar_t temp[4][1000]; - Device* dev; - int bFound = 0; - ForceFeedbackBinding* ffb = 0; - Binding* b = 0; - if (i >= 1) - { - int index = -1; - int flipped = 0; - int nonButtons = 0; - int ffbFound = 0; - while (1) - { - if (!(config.bind && (!config.configureOnBind || quickSetup))) - index = ListView_GetNextItem(hWndList, index, LVNI_SELECTED); - if (index < 0) - break; - LVITEMW item; - item.iItem = index; - item.mask = LVIF_TEXT; - item.pszText = temp[3]; - int j = 0; - for (; j < 3; j++) - { - item.iSubItem = j; - item.cchTextMax = sizeof(temp[0]) / sizeof(temp[3][0]); - if (!ListView_GetItem(hWndList, &item)) - break; - if (!bFound && !ffbFound) - wcscpy(temp[j], temp[3]); - else if (wcsicmp(temp[j], temp[3])) - { - int q = 0; - while (temp[j][q] == temp[3][q]) - q++; - if (q && temp[j][q - 1] == ' ' && temp[j][q] && temp[j][q + 1] == 0) - q--; - if (j == 1) - { - // Really ugly, but merges labels for multiple directions for same axis. - if ((temp[j][q] == 0 || (temp[j][q] == ' ' && temp[j][q + 2] == 0)) && - (temp[3][q] == 0 || (temp[3][q] == ' ' && temp[3][q + 2] == 0))) - { - temp[j][q] = 0; - continue; - } - } - // Merge different directions for same stick. - else if (j == 2 && q > 4) - { - temp[j][q] = 0; - continue; - } - wcscpy(temp[j], L"*"); - } - } - if (j == 3) - { - devName = temp[0]; - key = temp[1]; - command = temp[2]; - if (GetBinding(port, slot, index, dev, b, ffb)) - { - if (b) - { - bFound++; - VirtualControl* control = &dev->virtualControls[b->controlIndex]; - // Only relative axes can't have negative sensitivity. - if (((control->uid >> 16) & 0xFF) == RELAXIS) - { - disableFlip = 1; - } - rapidFire += b->rapidFire; - if (b->sensitivity < 0) - { - flipped++; - sensitivity -= b->sensitivity; - } - else - { - sensitivity += b->sensitivity; - } - if (((control->uid >> 16) & 0xFF) != PSHBTN && ((control->uid >> 16) & 0xFF) != TGLBTN) - { - deadZone += b->deadZone; - skipDeadZone += b->skipDeadZone; - nonButtons++; - } - } - else - ffbFound++; - } - } - } - if ((bFound && ffbFound) || ffbFound > 1) - { - ffb = 0; - rapidFire = -1; - deadZone = 0; - skipDeadZone = 0; - sensitivity = 0; - disableFlip = 1; - bFound = ffbFound = 0; - } - else if (bFound) - { - rapidFire++; - sensitivity /= bFound; - if (nonButtons) - { - deadZone /= nonButtons; - skipDeadZone /= nonButtons; - } - if (bFound > 1) - disableFlip = 1; - else if (flipped) - { - sensitivity = -sensitivity; - } - } - } - config.bind = false; - - // Input & Special Bindings: - SetWindowTextW(GetDlgItem(hWnd, ID_SPECIAL_INPUTS), config.specialInputs[port][slot] ? L"Hide Special Inputs" : L"Show Special Inputs"); - for (i = IDC_DPAD; i <= ID_SPECIAL_INPUTS; i++) - { - hWndTemp = GetDlgItem(hWnd, i); - if (hWndTemp) - { - if (i >= ID_EXCLUDE && i <= ID_TURBO_KEY) - ShowWindow(hWndTemp, !ffb && !b && config.specialInputs[port][slot]); - else - ShowWindow(hWndTemp, !ffb && !b); - } - } - // Input configuration: - for (i = IDC_SLIDER_SENSITIVITY; i <= IDC_AXIS_SKIP_DEADZONE; i++) - { - hWndTemp = GetDlgItem(hWnd, i); - if (hWndTemp) - ShowWindow(hWndTemp, !ffb && b); - if (i == IDC_SKIP_DEADZONE_OFF && skipDeadZone > deadZone) - ShowWindow(hWndTemp, 0); - if (i == IDC_AXIS_SKIP_DEADZONE && skipDeadZone <= deadZone) - ShowWindow(hWndTemp, 0); - } - // Pad Force Feedback configuration: - for (i = ID_TEST; i <= IDC_FF_AXIS8_SCALE; i++) - { - hWndTemp = GetDlgItem(hWnd, i); - if (hWndTemp) - { - int enable = ffb != 0; - if (ffb && i >= IDC_FF_AXIS1_ENABLED) - { - int index = (i - IDC_FF_AXIS1_ENABLED) / 16; - if (index >= dev->numFFAxes) - { - enable = 0; - } - else - { - int type = i - index * 16; - AxisEffectInfo* info = 0; - if (dev->numFFAxes > index) - { - info = ffb->axes + index; - } - if (type == IDC_FF_AXIS1) - { - // takes care of IDC_FF_AXIS1_FLIP as well. - SetupLogSlider(hWndTemp); - int force = 0; - if (info) - { - force = info->force; - } - SetLogSliderVal(hWnd, i, GetDlgItem(hWnd, i - IDC_FF_AXIS1 + IDC_FF_AXIS1_SCALE), force); - } - else if (type == IDC_FF_AXIS1_ENABLED) - { - CheckDlgButton(hWnd, i, BST_CHECKED * (info->force != 0)); - wsprintfW(temp[0], L"Axis %i", index + 1); - if (dev->ffAxes[index].displayName && wcslen(dev->ffAxes[index].displayName) < 50) - { - wchar_t* end = wcschr(temp[0], 0); - wsprintfW(end, L": %s", dev->ffAxes[index].displayName); - } - SetWindowText(hWndTemp, temp[0]); - } - } - } - ShowWindow(hWndTemp, enable); - } - } - ShowWindow(GetDlgItem(hWnd, ID_CONTROLS), ffb || b); - ShowWindow(GetDlgItem(hWnd, ID_RESET_CONFIG), ffb || b); - - if (!ffb) - { - SetLogSliderVal(hWnd, IDC_SLIDER_SENSITIVITY, GetDlgItem(hWnd, IDC_AXIS_SENSITIVITY), sensitivity); - SetLogSliderVal(hWnd, IDC_SLIDER_DEADZONE, GetDlgItem(hWnd, IDC_AXIS_DEADZONE), deadZone); - SetLogSliderVal(hWnd, IDC_SLIDER_SKIP_DEADZONE, GetDlgItem(hWnd, IDC_AXIS_SKIP_DEADZONE), skipDeadZone); - - if (disableFlip) - EnableWindow(GetDlgItem(hWnd, IDC_FLIP), 0); - - EnableWindow(GetDlgItem(hWnd, IDC_RAPID_FIRE), rapidFire >= 0); - if (rapidFire > 0 && rapidFire < bFound) - { - SendMessage(GetDlgItem(hWnd, IDC_RAPID_FIRE), BM_SETSTYLE, BS_AUTO3STATE, 0); - CheckDlgButton(hWnd, IDC_RAPID_FIRE, BST_INDETERMINATE); - } - else - { - SendMessage(GetDlgItem(hWnd, IDC_RAPID_FIRE), BM_SETSTYLE, BS_AUTOCHECKBOX, 0); - CheckDlgButton(hWnd, IDC_RAPID_FIRE, BST_CHECKED * (bFound && rapidFire == bFound)); - } - HWND hWndCombo = GetDlgItem(hWnd, IDC_AXIS_DIRECTION); - int enableCombo = 0; - SendMessage(hWndCombo, CB_RESETCONTENT, 0, 0); - if (b && bFound == 1) - { - VirtualControl* control = &dev->virtualControls[b->controlIndex]; - unsigned int uid = control->uid; - if (((uid >> 16) & 0xFF) == ABSAXIS) - { - enableCombo = 1; - wchar_t* endings[3] = {L" -", L" +", L""}; - wchar_t* string = temp[3]; - wcscpy(string, key); - wchar_t* end = wcschr(string, 0); - int sel = 2; - if (!(uid & UID_AXIS)) - { - end[-2] = 0; - sel = (end[-1] == '+'); - end -= 2; - } - for (int i = 0; i < 3; i++) - { - wcscpy(end, endings[i]); - SendMessage(hWndCombo, CB_ADDSTRING, 0, (LPARAM)string); - if (i == sel) - SendMessage(hWndCombo, CB_SETCURSEL, i, 0); - } - } - } - EnableWindow(hWndCombo, enableCombo); - if (!enableCombo) - { - SendMessage(hWndCombo, CB_ADDSTRING, 0, (LPARAM)key); - SendMessage(hWndCombo, CB_SELECTSTRING, i, (LPARAM)key); - } - - SetDlgItemText(hWnd, IDC_AXIS_DEVICE, devName); - SetDlgItemText(hWnd, IDC_AXIS_CONTROL, command); - } - else - { - wchar_t temp2[2000]; - wsprintfW(temp2, L"%s / %s", devName, command); - SetDlgItemText(hWnd, ID_FF, temp2); - - hWndTemp = GetDlgItem(hWnd, IDC_FF_EFFECT); - SendMessage(hWndTemp, CB_RESETCONTENT, 0, 0); - for (i = 0; i < dev->numFFEffectTypes; i++) - { - SendMessage(hWndTemp, CB_INSERTSTRING, i, (LPARAM)dev->ffEffectTypes[i].displayName); - } - SendMessage(hWndTemp, CB_SETCURSEL, ffb->effectIndex, 0); - EnableWindow(hWndTemp, dev->numFFEffectTypes > 1); - } -} - -void UnselectAll(HWND hWnd) -{ - int i = ListView_GetSelectedCount(hWnd); - while (i-- > 0) - { - int index = ListView_GetNextItem(hWnd, -1, LVNI_SELECTED); - ListView_SetItemState(hWnd, index, 0, LVIS_SELECTED); - } -} - - -int GetItemIndex(int port, int slot, Device* dev, ForceFeedbackBinding* binding) -{ - int count = 0; - int padtype = config.padConfigs[port][slot].type; - int selectedDevice = config.deviceSelect[port][slot]; - for (int i = 0; i < dm->numDevices; i++) - { - Device* dev2 = dm->devices[i]; - if (!dev2->enabled || selectedDevice >= 0 && dm->devices[selectedDevice] != dev2) - continue; - if (dev2 != dev) - { - count += dev2->pads[port][slot][padtype].numBindings + dev2->pads[port][slot][padtype].numFFBindings; - continue; - } - return count += dev2->pads[port][slot][padtype].numBindings + (binding - dev2->pads[port][slot][padtype].ffBindings); - } - return -1; -} -int GetItemIndex(int port, int slot, Device* dev, Binding* binding) -{ - int count = 0; - int padtype = config.padConfigs[port][slot].type; - int selectedDevice = config.deviceSelect[port][slot]; - for (int i = 0; i < dm->numDevices; i++) - { - Device* dev2 = dm->devices[i]; - if (!dev2->enabled || selectedDevice >= 0 && dm->devices[selectedDevice] != dev2) - continue; - if (dev2 != dev) - { - count += dev2->pads[port][slot][padtype].numBindings + dev2->pads[port][slot][padtype].numFFBindings; - continue; - } - return count += binding - dev->pads[port][slot][padtype].bindings; - } - return -1; -} - -// Doesn't check if already displayed. -int ListBoundCommand(int port, int slot, Device* dev, Binding* b) -{ - int padtype = config.padConfigs[port][slot].type; - if (!hWnds[port][slot][padtype]) - return -1; - HWND hWndList = GetDlgItem(hWnds[port][slot][padtype], IDC_BINDINGS_LIST); - int index = -1; - if (hWndList) - { - index = GetItemIndex(port, slot, dev, b); - if (index >= 0) - { - LVITEM item; - item.mask = LVIF_TEXT; - item.pszText = dev->displayName; - item.iItem = index; - item.iSubItem = 0; - SendMessage(hWndList, LVM_INSERTITEM, 0, (LPARAM)&item); - item.mask = LVIF_TEXT; - item.iSubItem = 1; - item.pszText = dev->GetVirtualControlName(&dev->virtualControls[b->controlIndex]); - SendMessage(hWndList, LVM_SETITEM, 0, (LPARAM)&item); - item.iSubItem = 2; - item.pszText = GetCommandStringW(b->command, port, slot); - SendMessage(hWndList, LVM_SETITEM, 0, (LPARAM)&item); - } - } - return index; -} - -int ListBoundEffect(int port, int slot, Device* dev, ForceFeedbackBinding* b) -{ - int padtype = config.padConfigs[port][slot].type; - if (!hWnds[port][slot][padtype]) - return -1; - HWND hWndList = GetDlgItem(hWnds[port][slot][padtype], IDC_BINDINGS_LIST); - int index = -1; - if (hWndList) - { - index = GetItemIndex(port, slot, dev, b); - if (index >= 0) - { - LVITEM item; - item.mask = LVIF_TEXT; - item.pszText = dev->displayName; - item.iItem = index; - item.iSubItem = 0; - SendMessage(hWndList, LVM_INSERTITEM, 0, (LPARAM)&item); - item.mask = LVIF_TEXT; - item.iSubItem = 1; - item.pszText = dev->ffEffectTypes[b->effectIndex].displayName; - SendMessage(hWndList, LVM_SETITEM, 0, (LPARAM)&item); - item.iSubItem = 2; - wchar_t* ps2Motors[2] = {L"Big Motor", L"Small Motor"}; - item.pszText = ps2Motors[b->motor]; - SendMessage(hWndList, LVM_SETITEM, 0, (LPARAM)&item); - } - } - return index; -} - -// Only for use with control bindings. Affects all highlighted bindings. -void ChangeValue(int port, int slot, int* newSensitivity, int* newRapidFire, int* newDeadZone, int* newSkipDeadZone) -{ - int padtype = config.padConfigs[port][slot].type; - if (!hWnds[port][slot][padtype]) - return; - HWND hWndList = GetDlgItem(hWnds[port][slot][padtype], IDC_BINDINGS_LIST); - int count = ListView_GetSelectedCount(hWndList); - if (count < 1) - return; - int index = -1; - while (1) - { - index = ListView_GetNextItem(hWndList, index, LVNI_SELECTED); - if (index < 0) - break; - Device* dev; - Binding* b; - ForceFeedbackBinding* ffb; - if (!GetBinding(port, slot, index, dev, b, ffb) || ffb) - return; - if (newSensitivity) - { - // Don't change flip state when modifying multiple controls. - if (count > 1 && b->sensitivity < 0) - b->sensitivity = -*newSensitivity; - else - b->sensitivity = *newSensitivity; - } - if (newDeadZone) - { - if (b->deadZone) - { - b->deadZone = *newDeadZone; - } - } - if (newSkipDeadZone) - { - if (b->skipDeadZone) - { - b->skipDeadZone = *newSkipDeadZone; - } - } - if (newRapidFire) - { - b->rapidFire = *newRapidFire; - } - } - PropSheet_Changed(hWndProp, hWnds[port][slot][padtype]); - SelChanged(port, slot); -} - -// Only for use with effect bindings. -void ChangeEffect(int port, int slot, int id, int* newForce, unsigned int* newEffectType) -{ - int padtype = config.padConfigs[port][slot].type; - if (!hWnds[port][slot][padtype]) - return; - HWND hWndList = GetDlgItem(hWnds[port][slot][padtype], IDC_BINDINGS_LIST); - int i = ListView_GetSelectedCount(hWndList); - if (i != 1) - return; - int index = ListView_GetNextItem(hWndList, -1, LVNI_SELECTED); - Device* dev; - Binding* b; - ForceFeedbackBinding* ffb; - if (!GetBinding(port, slot, index, dev, b, ffb) || b) - return; - if (newForce) - { - unsigned int axisIndex = (id - IDC_FF_AXIS1_ENABLED) / 16; - if (axisIndex < (unsigned int)dev->numFFAxes) - { - ffb->axes[axisIndex].force = *newForce; - } - } - if (newEffectType && *newEffectType < (unsigned int)dev->numFFEffectTypes) - { - ffb->effectIndex = *newEffectType; - ListView_DeleteItem(hWndList, index); - index = ListBoundEffect(port, slot, dev, ffb); - ListView_SetItemState(hWndList, index, LVIS_SELECTED, LVIS_SELECTED); - } - PropSheet_Changed(hWndProp, hWnds[port][slot][padtype]); - SelChanged(port, slot); -} - - - -void Populate(int port, int slot, int padtype) -{ - if (!hWnds[port][slot][padtype]) - return; - HWND hWnd = GetDlgItem(hWnds[port][slot][padtype], IDC_BINDINGS_LIST); - ListView_DeleteAllItems(hWnd); - - int multipleBinding = config.multipleBinding; - config.multipleBinding = 1; - int selectedDevice = config.deviceSelect[port][slot]; - for (int j = 0; j < dm->numDevices; j++) - { - Device* dev = dm->devices[j]; - if (!dev->enabled || selectedDevice >= 0 && dm->devices[selectedDevice] != dev) - continue; - for (int i = 0; i < dev->pads[port][slot][padtype].numBindings; i++) - { - ListBoundCommand(port, slot, dev, dev->pads[port][slot][padtype].bindings + i); - } - for (int i = 0; i < dev->pads[port][slot][padtype].numFFBindings; i++) - { - ListBoundEffect(port, slot, dev, dev->pads[port][slot][padtype].ffBindings + i); - } - } - config.multipleBinding = multipleBinding; - - hWnd = GetDlgItem(hWnds[port][slot][padtype], IDC_FORCEFEEDBACK); - SendMessage(hWnd, CB_RESETCONTENT, 0, 0); - int added = 0; - bool enable = false; - for (int i = 0; i < dm->numDevices; i++) - { - Device* dev = dm->devices[i]; - if (dev->enabled && dev->numFFAxes && dev->numFFEffectTypes) - { - SendMessage(hWnd, CB_INSERTSTRING, added, (LPARAM)dev->displayName); - SendMessage(hWnd, CB_SETITEMDATA, added, i); - added++; - - int selectedDevice = config.deviceSelect[port][slot]; - if (selectedDevice == -1 || dm->devices[selectedDevice] == dev) - { - enable = true; - } - } - } - SendMessage(hWnd, CB_SETCURSEL, 0, 0); - EnableWindow(hWnd, added != 0 && enable); - EnableWindow(GetDlgItem(hWnds[port][slot][padtype], ID_BIG_MOTOR), added != 0 && enable); - EnableWindow(GetDlgItem(hWnds[port][slot][padtype], ID_SMALL_MOTOR), added != 0 && enable); - SelChanged(port, slot); -} - -int WritePrivateProfileInt(wchar_t* s1, wchar_t* s2, int v, wchar_t* ini) -{ - wchar_t temp[100]; - _itow(v, temp, 10); - return WritePrivateProfileStringW(s1, s2, temp, ini); -} - -int SaveSettings(wchar_t* file = 0) -{ - - // Need this either way for saving path. - if (!file) - { - file = iniFileUSB; - } - else - { - wchar_t* c = wcsrchr(file, '\\'); - if (*c) - { - *c = 0; - wcscpy(config.lastSaveConfigPath, file); - wcscpy(config.lastSaveConfigFileName, c + 1); - *c = '\\'; - } - } - DeleteFileW(file); - - WritePrivateProfileStringW(L"General Settings", L"Last Config Path", config.lastSaveConfigPath, iniFileUSB); - WritePrivateProfileStringW(L"General Settings", L"Last Config Name", config.lastSaveConfigFileName, iniFileUSB); - - // Just check first, last, and all pad bindings. Should be more than enough. No real need to check - // config path. - int noError = 1; - - for (int i = 0; i < sizeof(BoolOptionsInfo) / sizeof(BoolOptionsInfo[0]); i++) - { - noError &= WritePrivateProfileInt(L"General Settings", BoolOptionsInfo[i].name, config.bools[i], file); - } - - WritePrivateProfileInt(L"General Settings", L"Keyboard Mode", config.keyboardApi, file); - WritePrivateProfileInt(L"General Settings", L"Mouse Mode", config.mouseApi, file); - - WritePrivateProfileInt(L"Pad Settings", L"Configure On Bind", config.configureOnBind, file); - - for (int port = 0; port < 2; port++) - { - for (int slot = 0; slot < 4; slot++) - { - wchar_t temp[50]; - wsprintf(temp, L"Pad %i %i", port, slot); - WritePrivateProfileInt(temp, L"Mode", config.padConfigs[port][slot].type, file); - } - } - - for (int i = 0; i < dm->numDevices; i++) - { - wchar_t id[50]; - wchar_t temp[50], temp2[1000]; - wsprintfW(id, L"Device %i", i); - Device* dev = dm->devices[i]; - wchar_t* name = dev->displayName; - while (name[0] == '[') - { - wchar_t* name2 = wcschr(name, ']'); - if (!name2) - break; - name = name2 + 1; - while (iswspace(name[0])) - name++; - } - WritePrivateProfileStringW(id, L"Display Name", name, file); - WritePrivateProfileStringW(id, L"Instance ID", dev->instanceID, file); - if (dev->productID) - { - WritePrivateProfileStringW(id, L"Product ID", dev->productID, file); - } - WritePrivateProfileInt(id, L"API", dev->api, file); - WritePrivateProfileInt(id, L"Type", dev->type, file); - int ffBindingCount = 0; - int bindingCount = 0; - for (int port = 0; port < 2; port++) - { - for (int slot = 0; slot < 4; slot++) - { - for (int padtype = 0; padtype < numPadTypes; padtype++) - { - for (int j = 0; j < dev->pads[port][slot][padtype].numBindings; j++) - { - Binding* b = dev->pads[port][slot][padtype].bindings + j; - VirtualControl* c = &dev->virtualControls[b->controlIndex]; - wsprintfW(temp, L"Binding %i", bindingCount++); - wsprintfW(temp2, L"0x%08X, %i, %i, %i, %i, %i, %i, %i, %i", c->uid, port, b->command, b->sensitivity, b->rapidFire, slot, b->deadZone, b->skipDeadZone, padtype); - noError &= WritePrivateProfileStringW(id, temp, temp2, file); - } - for (int j = 0; j < dev->pads[port][slot][padtype].numFFBindings; j++) - { - ForceFeedbackBinding* b = dev->pads[port][slot][padtype].ffBindings + j; - ForceFeedbackEffectType* eff = &dev->ffEffectTypes[b->effectIndex]; - wsprintfW(temp, L"FF Binding %i", ffBindingCount++); - wsprintfW(temp2, L"%s %i, %i, %i, %i", eff->effectID, port, b->motor, slot, padtype); - for (int k = 0; k < dev->numFFAxes; k++) - { - ForceFeedbackAxis* axis = dev->ffAxes + k; - AxisEffectInfo* info = b->axes + k; - wsprintfW(wcschr(temp2, 0), L", %i, %i", axis->id, info->force); - } - noError &= WritePrivateProfileStringW(id, temp, temp2, file); - } - } - } - } - } - if (!noError) - { - MessageBoxA(hWndProp, "Unable to save settings. Make sure the disk is not full or write protected, the file isn't write protected, and that the app has permissions to write to the directory. Try running in administrator mode.", "Error Writing Configuration File", MB_OK | MB_ICONERROR); - } - return !noError; -} - -u8 GetPrivateProfileBool(wchar_t* s1, wchar_t* s2, int def, wchar_t* ini) -{ - return (0 != GetPrivateProfileIntW(s1, s2, def, ini)); -} - -int LoadSettings(int force, wchar_t* file) -{ - if (dm && !force) - return 0; - - if (createIniDir) - { - PADsetSettingsDir("inis"); - createIniDir = false; - } - - // Could just do ClearDevices() instead, but if I ever add any extra stuff, - // this will still work. - UnloadConfigs(); - dm = new InputDeviceManager(); - - if (!file) - { - file = iniFileUSB; - GetPrivateProfileStringW(L"General Settings", L"Last Config Path", L"inis", config.lastSaveConfigPath, sizeof(config.lastSaveConfigPath), file); - GetPrivateProfileStringW(L"General Settings", L"Last Config Name", L"PAD.pad", config.lastSaveConfigFileName, sizeof(config.lastSaveConfigFileName), file); - } - else - { - wchar_t* c = wcsrchr(file, '\\'); - if (c) - { - *c = 0; - wcscpy(config.lastSaveConfigPath, file); - wcscpy(config.lastSaveConfigFileName, c + 1); - *c = '\\'; - WritePrivateProfileStringW(L"General Settings", L"Last Config Path", config.lastSaveConfigPath, iniFileUSB); - WritePrivateProfileStringW(L"General Settings", L"Last Config Name", config.lastSaveConfigFileName, iniFileUSB); - } - } - - for (int i = 0; i < sizeof(BoolOptionsInfo) / sizeof(BoolOptionsInfo[0]); i++) - { - config.bools[i] = GetPrivateProfileBool(L"General Settings", BoolOptionsInfo[i].name, BoolOptionsInfo[i].defaultValue, file); - } - - config.keyboardApi = (DeviceAPI)GetPrivateProfileIntW(L"General Settings", L"Keyboard Mode", WM, file); - if (!config.keyboardApi) - config.keyboardApi = WM; - config.mouseApi = (DeviceAPI)GetPrivateProfileIntW(L"General Settings", L"Mouse Mode", 0, file); - - config.configureOnBind = (DeviceAPI)GetPrivateProfileIntW(L"Pad Settings", L"Configure On Bind", 0, file); - - if (config.debug) - { - CreateDirectory(L"logs", 0); - } - - for (int port = 0; port < 2; port++) - { - for (int slot = 0; slot < 4; slot++) - { - wchar_t temp[50]; - wsprintf(temp, L"Pad %i %i", port, slot); - config.padConfigs[port][slot].type = (PadType)GetPrivateProfileInt(temp, L"Mode", Dualshock2Pad, file); - } - } - - bool oldIni = false; - int i = 0; - int multipleBinding = config.multipleBinding; - // Disabling multiple binding only prevents new multiple bindings. - config.multipleBinding = 1; - while (1) - { - wchar_t id[50]; - wchar_t temp[50], temp2[1000], temp3[1000], temp4[1000]; - wsprintfW(id, L"Device %i", i++); - if (!GetPrivateProfileStringW(id, L"Display Name", 0, temp2, 1000, file) || !temp2[0] || - !GetPrivateProfileStringW(id, L"Instance ID", 0, temp3, 1000, file) || !temp3[0]) - { - if (i >= 100) - break; - continue; - } - wchar_t* id2 = 0; - if (GetPrivateProfileStringW(id, L"Product ID", 0, temp4, 1000, file) && temp4[0]) - id2 = temp4; - - int api = GetPrivateProfileIntW(id, L"API", 0, file); - int type = GetPrivateProfileIntW(id, L"Type", 0, file); - if (!api || !type) - continue; - - Device* dev = new Device((DeviceAPI)api, (DeviceType)type, temp2, temp3, id2); - dev->attached = 0; - dm->AddDevice(dev); - int j = 0; - int last = 0; - while (1) - { - wsprintfW(temp, L"Binding %i", j++); - if (!GetPrivateProfileStringW(id, temp, 0, temp2, 1000, file)) - { - if (j >= 100) - { - if (!last) - break; - last = 0; - } - continue; - } - last = 1; - unsigned int uid; - int port, command, sensitivity, rapidFire, slot = 0, deadZone = 0, skipDeadZone = 0, padtype = 0; - int w = 0; - char string[1000]; - while (temp2[w]) - { - string[w] = (char)temp2[w]; - w++; - } - string[w] = 0; - int len = sscanf(string, " %i , %i , %i , %i , %i , %i , %i , %i , %i", &uid, &port, &command, &sensitivity, &rapidFire, &slot, &deadZone, &skipDeadZone, &padtype); - if (len >= 5 && type) - { - VirtualControl* c = dev->GetVirtualControl(uid); - if (!c) - c = dev->AddVirtualControl(uid, -1); - if (c) - { - if (len < 8) - { // If ini file is imported from older version, make sure bindings aren't applied to "Unplugged" padtype. - oldIni = true; - if (config.padConfigs[port][slot].type != 0) - { - padtype = config.padConfigs[port][slot].type; - } - else - { - padtype = 1; - } - } - else if (len == 8) - { - padtype = skipDeadZone; - skipDeadZone = 0; - } - BindCommand(dev, uid, port, slot, padtype, command, sensitivity, rapidFire, deadZone, skipDeadZone); - } - } - } - j = 0; - while (1) - { - wsprintfW(temp, L"FF Binding %i", j++); - if (!GetPrivateProfileStringW(id, temp, 0, temp2, 1000, file)) - { - if (j >= 10) - { - if (!last) - break; - last = 0; - } - continue; - } - last = 1; - int port, slot, motor, padtype; - int w = 0; - char string[1000]; - char effect[1000]; - while (temp2[w]) - { - string[w] = (char)temp2[w]; - w++; - } - string[w] = 0; - // wcstok not in ntdll. More effore than its worth to shave off - // whitespace without it. - if (sscanf(string, " %100s %i , %i , %i , %i", effect, &port, &motor, &slot, &padtype) == 5) - { - char* s; - if (oldIni) - { // Make sure bindings aren't applied to "Unplugged" padtype and FF settings are read from old location. - if (config.padConfigs[port][slot].type != 0) - { - padtype = config.padConfigs[port][slot].type; - } - else - { - padtype = 1; - } - s = strchr(strchr(strchr(string, ',') + 1, ',') + 1, ','); - } - else - { - s = strchr(strchr(strchr(strchr(string, ',') + 1, ',') + 1, ',') + 1, ','); - } - if (!s) - continue; - s++; - w = 0; - while (effect[w]) - { - temp2[w] = effect[w]; - w++; - } - temp2[w] = 0; - ForceFeedbackEffectType* eff = dev->GetForcefeedbackEffect(temp2); - if (!eff) - { - // At the moment, don't record effect types. - // Only used internally, anyways, so not an issue. - dev->AddFFEffectType(temp2, temp2, EFFECT_CONSTANT); - // eff = &dev->ffEffectTypes[dev->numFFEffectTypes-1]; - } - ForceFeedbackBinding* b; - CreateEffectBinding(dev, temp2, port, slot, padtype, motor, &b); - if (b) - { - while (1) - { - int axisID = atoi(s); - if (!(s = strchr(s, ','))) - break; - s++; - int force = atoi(s); - int i; - for (i = 0; i < dev->numFFAxes; i++) - { - if (axisID == dev->ffAxes[i].id) - break; - } - if (i == dev->numFFAxes) - { - dev->AddFFAxis(L"?", axisID); - } - b->axes[i].force = force; - if (!(s = strchr(s, ','))) - break; - s++; - } - } - } - } - } - config.multipleBinding = multipleBinding; - - RefreshEnabledDevicesAndDisplay(1); - - return 0; -} - -inline int GetPort(HWND hWnd, int* slot) -{ - if (sizeof(hWnds) / sizeof(hWnds[0][0][0]) != (2 * 4 * numPadTypes)) - MessageBoxA(hWndProp, "The number of detected configurations does not equal the expected number of configurations.", "Pad configurations error", MB_OK | MB_ICONERROR); - for (int j = 0; j < numPadTypes; j++) - { - for (int i = 0; i < 8; i++) - { - if (hWnds[i & 1][i >> 1][j] == hWnd) - { - *slot = i >> 1; - return i & 1; - } - } - } - *slot = 0; - return 0; -} - -void Diagnostics(HWND hWnd) -{ - HWND hWndList = GetDlgItem(hWnd, IDC_DIAG_LIST); - if (!hWndList) - return; - int index = ListView_GetNextItem(hWndList, -1, LVNI_SELECTED); - if (index < 0) - return; - LVITEM item; - memset(&item, 0, sizeof(item)); - item.mask = LVIF_PARAM; - item.iSubItem = 0; - item.iItem = index; - if (!ListView_GetItem(hWndList, &item)) - return; - Diagnose(item.lParam, hWnd); - RefreshEnabledDevicesAndDisplay(0, hWnd, 1); -} - -// Tooltips will only show if the TOOLINFO cbSize <= the struct size. If it's -// smaller some functionality might be disabled. So let's try and use the -// correct size. -UINT GetTooltipStructSize() -{ - DLLGETVERSIONPROC dllGetVersion = (DLLGETVERSIONPROC)GetProcAddress(GetModuleHandle(L"ComCtl32.dll"), "DllGetVersion"); - if (dllGetVersion) - { - DLLVERSIONINFO2 dllversion = {0}; - dllversion.info1.cbSize = sizeof(DLLVERSIONINFO2); - - if (dllGetVersion((DLLVERSIONINFO*)&dllversion) == S_OK) - { - // Minor, then major version. - DWORD version = MAKELONG(dllversion.info1.dwMinorVersion, dllversion.info1.dwMajorVersion); - DWORD tooltip_v3 = MAKELONG(0, 6); - if (version >= tooltip_v3) - return TTTOOLINFOA_V3_SIZE; - } - } - // Should be fine for XP and onwards, comctl versions >= 4.7 should at least - // be this size. - return TTTOOLINFOA_V2_SIZE; -} - -void AddTooltip(UINT id, HWND hWnd) -{ - static UINT tooltipStructSize = GetTooltipStructSize(); - bool hasTooltip; - LPWSTR message = pad_dialog_message(id, &hasTooltip); - if (!hasTooltip) - return; - - HWND hWndTooltip = GetDlgItem(hWnd, id); - if (hWndTooltip == NULL) - return; - - // TTS_NOPREFIX allows tabs and '&' to be used. - HWND hWndTip = CreateWindowEx(WS_EX_TOPMOST, TOOLTIPS_CLASS, NULL, - TTS_ALWAYSTIP | TTS_NOPREFIX, - CW_USEDEFAULT, CW_USEDEFAULT, CW_USEDEFAULT, CW_USEDEFAULT, - hWnd, NULL, GetModuleHandle(nullptr), NULL); - if (hWndTip == NULL) - return; - - TOOLINFO toolInfo = {0}; - toolInfo.cbSize = tooltipStructSize; - toolInfo.hwnd = hWnd; - toolInfo.uFlags = TTF_IDISHWND | TTF_SUBCLASS; - toolInfo.uId = (UINT_PTR)hWndTooltip; - toolInfo.lpszText = message; - SendMessage(hWndTip, TTM_ADDTOOL, 0, (LPARAM)&toolInfo); - // 32.767s is the max show time. - SendMessage(hWndTip, TTM_SETDELAYTIME, TTDT_AUTOPOP, 32767); - // Setting a maximum width is necessary for displaying longer messages. - SendMessage(hWndTip, TTM_SETMAXTIPWIDTH, 0, 400); -} - -int GetBinding(int port, int slot, int index, Device*& dev, Binding*& b, ForceFeedbackBinding*& ffb) -{ - ffb = 0; - b = 0; - int padtype = config.padConfigs[port][slot].type; - int selectedDevice = config.deviceSelect[port][slot]; - for (int i = 0; i < dm->numDevices; i++) - { - dev = dm->devices[i]; - if (!dev->enabled || selectedDevice >= 0 && dm->devices[selectedDevice] != dev) - continue; - if (index < dev->pads[port][slot][padtype].numBindings) - { - b = dev->pads[port][slot][padtype].bindings + index; - return 1; - } - index -= dev->pads[port][slot][padtype].numBindings; - - if (index < dev->pads[port][slot][padtype].numFFBindings) - { - ffb = dev->pads[port][slot][padtype].ffBindings + index; - return 1; - } - index -= dev->pads[port][slot][padtype].numFFBindings; - } - return 0; -} - -// Only used when deleting things from ListView. Will remove from listview if needed. -void DeleteBinding(int port, int slot, int padtype, Device* dev, Binding* b) -{ - if (dev->enabled && hWnds[port][slot][padtype]) - { - int count = GetItemIndex(port, slot, dev, b); - if (count >= 0) - { - HWND hWndList = GetDlgItem(hWnds[port][slot][padtype], IDC_BINDINGS_LIST); - if (hWndList) - { - ListView_DeleteItem(hWndList, count); - } - } - } - Binding* bindings = dev->pads[port][slot][padtype].bindings; - int i = b - bindings; - memmove(bindings + i, bindings + i + 1, sizeof(Binding) * (dev->pads[port][slot][padtype].numBindings - i - 1)); - dev->pads[port][slot][padtype].numBindings--; -} - -void DeleteFFBinding(int port, int slot, Device* dev, ForceFeedbackBinding* b) -{ - int padtype = config.padConfigs[port][slot].type; - if (dev->enabled && hWnds[port][slot][padtype]) - { - int count = GetItemIndex(port, slot, dev, b); - if (count >= 0) - { - HWND hWndList = GetDlgItem(hWnds[port][slot][padtype], IDC_BINDINGS_LIST); - if (hWndList) - { - ListView_DeleteItem(hWndList, count); - } - } - } - ForceFeedbackBinding* bindings = dev->pads[port][slot][padtype].ffBindings; - int i = b - bindings; - memmove(bindings + i, bindings + i + 1, sizeof(Binding) * (dev->pads[port][slot][padtype].numFFBindings - i - 1)); - dev->pads[port][slot][padtype].numFFBindings--; -} - -int DeleteByIndex(int port, int slot, int index) -{ - int padtype = config.padConfigs[port][slot].type; - ForceFeedbackBinding* ffb; - Binding* b; - Device* dev; - if (GetBinding(port, slot, index, dev, b, ffb)) - { - if (b) - { - DeleteBinding(port, slot, padtype, dev, b); - } - else - { - DeleteFFBinding(port, slot, dev, ffb); - } - return 1; - } - return 0; -} - -int DeleteSelected(int port, int slot) -{ - int padtype = config.padConfigs[port][slot].type; - if (!hWnds[port][slot][padtype]) - return 0; - HWND hWnd = GetDlgItem(hWnds[port][slot][padtype], IDC_BINDINGS_LIST); - int changes = 0; - while (1) - { - int index = ListView_GetNextItem(hWnd, -1, LVNI_SELECTED); - if (index < 0) - break; - changes += DeleteByIndex(port, slot, index); - } - //ShowScrollBar(hWnd, SB_VERT, 1); - return changes; -} - -int CreateEffectBinding(Device* dev, wchar_t* effectID, unsigned int port, unsigned int slot, unsigned int padtype, unsigned int motor, ForceFeedbackBinding** binding) -{ - // Checks needed because I use this directly when loading bindings. - // Note: dev->numFFAxes *can* be 0, for loading from file. - *binding = 0; - if (port > 1 || slot > 3 || motor > 1 || !dev->numFFEffectTypes) - { - return -1; - } - ForceFeedbackEffectType* eff = 0; - if (effectID) - { - eff = dev->GetForcefeedbackEffect(effectID); - } - if (!eff) - { - eff = dev->ffEffectTypes; - } - if (!eff) - { - return -1; - } - int effectIndex = eff - dev->ffEffectTypes; - dev->pads[port][slot][padtype].ffBindings = (ForceFeedbackBinding*)realloc(dev->pads[port][slot][padtype].ffBindings, (dev->pads[port][slot][padtype].numFFBindings + 1) * sizeof(ForceFeedbackBinding)); - int newIndex = dev->pads[port][slot][padtype].numFFBindings; - while (newIndex && dev->pads[port][slot][padtype].ffBindings[newIndex - 1].motor >= motor) - { - dev->pads[port][slot][padtype].ffBindings[newIndex] = dev->pads[port][slot][padtype].ffBindings[newIndex - 1]; - newIndex--; - } - ForceFeedbackBinding* b = dev->pads[port][slot][padtype].ffBindings + newIndex; - b->axes = (AxisEffectInfo*)calloc(dev->numFFAxes, sizeof(AxisEffectInfo)); - b->motor = motor; - b->effectIndex = effectIndex; - dev->pads[port][slot][padtype].numFFBindings++; - if (binding) - *binding = b; - return ListBoundEffect(port, slot, dev, b); -} - -int BindCommand(Device* dev, unsigned int uid, unsigned int port, unsigned int slot, unsigned int padtype, int command, int sensitivity, int rapidFire, int deadZone, int skipDeadZone) -{ - // Checks needed because I use this directly when loading bindings. - if (port > 1 || slot > 3 || padtype >= numPadTypes) - return -1; - - if (!sensitivity) - { - if (((uid >> 16) & 0xFF) == ABSAXIS) - { - sensitivity = BASE_ANALOG_SENSITIVITY; - } - else - { - sensitivity = BASE_SENSITIVITY; - } - } - if ((uid >> 16) & (PSHBTN | TGLBTN)) - { - deadZone = 0; - skipDeadZone = 0; - } - else if (!deadZone) - { - if ((uid >> 16) & PRESSURE_BTN) - { - deadZone = 1; - } - else - { - deadZone = DEFAULT_DEADZONE; - } - } - else if (!skipDeadZone) - { - skipDeadZone = 1; - } - - // Relative axes can have negative sensitivity. - else if (((uid >> 16) & 0xFF) == RELAXIS) - { - sensitivity = abs(sensitivity); - } - VirtualControl* c = dev->GetVirtualControl(uid); - if (!c) - return -1; - // Add before deleting. Means I won't scroll up one line when scrolled down to bottom. - int controlIndex = c - dev->virtualControls; - int index = 0; - PadBindings* p = dev->pads[port][slot] + padtype; - p->bindings = (Binding*)realloc(p->bindings, (p->numBindings + 1) * sizeof(Binding)); - for (index = p->numBindings; index > 0; index--) - { - if (p->bindings[index - 1].controlIndex < controlIndex) - break; - p->bindings[index] = p->bindings[index - 1]; - } - Binding* b = p->bindings + index; - p->numBindings++; - b->command = command; - b->controlIndex = controlIndex; - b->rapidFire = rapidFire; - b->sensitivity = sensitivity; - b->deadZone = deadZone; - b->skipDeadZone = skipDeadZone; - // Where it appears in listview. - int count = ListBoundCommand(port, slot, dev, b); - - int newBindingIndex = index; - index = 0; - while (index < p->numBindings) - { - if (index == newBindingIndex) - { - index++; - continue; - } - b = p->bindings + index; - int nuke = 0; - if (config.multipleBinding) - { - if (b->controlIndex == controlIndex && b->command == command) - nuke = 1; - } - else - { - int uid2 = dev->virtualControls[b->controlIndex].uid; - if (b->controlIndex == controlIndex || (!((uid2 ^ uid) & 0xFFFFFF) && ((uid | uid2) & (UID_POV | UID_AXIS)))) - nuke = 1; - } - if (!nuke) - { - index++; - continue; - } - if (index < newBindingIndex) - { - newBindingIndex--; - count--; - } - DeleteBinding(port, slot, padtype, dev, b); - } - if (!config.multipleBinding) - { - for (int port2 = 0; port2 < 2; port2++) - { - for (int slot2 = 0; slot2 < 4; slot2++) - { - if (port2 == port && slot2 == slot) - continue; - int padtype2 = config.padConfigs[port2][slot2].type; - PadBindings* p = dev->pads[port2][slot2] + padtype2; - for (int i = 0; i < p->numBindings; i++) - { - Binding* b = p->bindings + i; - int uid2 = dev->virtualControls[b->controlIndex].uid; - if (b->controlIndex == controlIndex || (!((uid2 ^ uid) & 0xFFFFFF) && ((uid | uid2) & (UID_POV | UID_AXIS)))) - { - DeleteBinding(port2, slot2, padtype2, dev, b); - i--; - } - } - } - } - } - - return count; -} - -// Does nothing, but makes sure I'm overriding the dialog's window proc, to block -// default key handling. -ExtraWndProcResult DoNothingWndProc(HWND hWnd, UINT uMsg, WPARAM wParam, LPARAM lParam, LRESULT* output) -{ - return CONTINUE_BLISSFULLY; -} - -void EndBinding(HWND hWnd) -{ - if (selected) - { - KillTimer(hWnd, 1); - - int needRefreshDevices = 0; - // If binding ignore keyboard, this will disable it and enable other devices. - // 0xFF is used for testing force feedback. I disable all other devices first - // just so I don't needlessly steal the mouse. - if (selected == 0x7F || selected == 0xFF) - { - needRefreshDevices = 1; - } - selected = 0; - - quickSetup = false; - SetWindowTextW(GetDlgItem(hWnd, ID_QUICK_SETUP), L"Quick Setup"); - SetWindowTextW(GetDlgItem(hWnd, IDC_QUICK_SETUP_TEXT), L""); - - dm->ReleaseInput(); - ClearKeyQueue(); - hWndButtonProc.Release(); - - // Safest to do this last. - if (needRefreshDevices) - { - RefreshEnabledDevices(); - } - } -} - - -INT_PTR CALLBACK DialogProc(HWND hWnd, unsigned int msg, WPARAM wParam, LPARAM lParam) -{ - int index = (hWnd == PropSheet_IndexToHwnd(hWndProp, 1)); - int slot; - int port = GetPort(hWnd, &slot); - int padtype = config.padConfigs[port][slot].type; - HWND hWndList = GetDlgItem(hWnd, IDC_BINDINGS_LIST); - switch (msg) - { - case WM_INITDIALOG: - { - ListView_SetExtendedListViewStyleEx(hWndList, LVS_EX_DOUBLEBUFFER, LVS_EX_DOUBLEBUFFER); - LVCOLUMN c; - c.mask = LVCF_TEXT | LVCF_WIDTH; - c.cx = 90; - c.pszText = L"Device"; - ListView_InsertColumn(hWndList, 0, &c); - c.cx = 70; - c.pszText = L"PC Control"; - ListView_InsertColumn(hWndList, 1, &c); - c.pszText = L"PS2 Control"; - ListView_InsertColumn(hWndList, 2, &c); - selected = 0; - port = (int)((PROPSHEETPAGE*)lParam)->lParam & 1; - slot = (int)((PROPSHEETPAGE*)lParam)->lParam >> 1; - padtype = config.padConfigs[port][slot].type; - hWnds[port][slot][padtype] = hWnd; - SendMessage(hWndList, LVM_SETEXTENDEDLISTVIEWSTYLE, 0, LVS_EX_FULLROWSELECT); - SetupLogSlider(GetDlgItem(hWnd, IDC_SLIDER_SENSITIVITY)); - SetupLogSlider(GetDlgItem(hWnd, IDC_SLIDER_DEADZONE)); - SetupLogSlider(GetDlgItem(hWnd, IDC_SLIDER_SKIP_DEADZONE)); - CheckDlgButton(hWnd, IDC_CONFIGURE_ON_BIND, BST_CHECKED * config.configureOnBind); - - AddTooltip(IDC_BINDINGS_LIST, hWnd); - AddTooltip(IDC_DEVICE_SELECT, hWnd); - AddTooltip(IDC_CONFIGURE_ON_BIND, hWnd); - AddTooltip(ID_MOUSE, hWnd); - AddTooltip(ID_ANALOG, hWnd); - AddTooltip(ID_LOCK_ALL_INPUT, hWnd); - AddTooltip(ID_LOCK_DIRECTION, hWnd); - AddTooltip(ID_LOCK_BUTTONS, hWnd); - AddTooltip(ID_TURBO_KEY, hWnd); - AddTooltip(ID_EXCLUDE, hWnd); - AddTooltip(IDC_RAPID_FIRE, hWnd); - AddTooltip(IDC_FLIP, hWnd); - AddTooltip(IDC_SLIDER_DEADZONE, hWnd); - AddTooltip(IDC_SLIDER_SKIP_DEADZONE, hWnd); - AddTooltip(IDC_SLIDER_SENSITIVITY, hWnd); - - config.bind = false; - config.deviceSelect[port][slot] = -1; - HWND hWndDS = GetDlgItem(hWnds[port][slot][padtype], IDC_DEVICE_SELECT); - SendMessage(hWndDS, CB_RESETCONTENT, 0, 0); - int added = 0; - SendMessage(hWndDS, CB_INSERTSTRING, added, (LPARAM)L"Allow All Devices"); - SendMessage(hWndDS, CB_SETITEMDATA, added, -1); - added++; - for (int i = 0; i < dm->numDevices; i++) - { - Device* dev = dm->devices[i]; - if (dev->enabled && dev->api != IGNORE_KEYBOARD) - { - SendMessage(hWndDS, CB_INSERTSTRING, added, (LPARAM)dev->displayName); - SendMessage(hWndDS, CB_SETITEMDATA, added, i); - added++; - } - } - SendMessage(hWndDS, CB_SETCURSEL, 0, 0); - EnableWindow(hWndDS, added != 0); - - Populate(port, slot, padtype); - ListView_SetColumnWidth(hWndList, 2, LVSCW_AUTOSIZE_USEHEADER); - } - break; - case WM_DEVICECHANGE: - if (wParam == DBT_DEVNODES_CHANGED) - { - EndBinding(hWnd); - RefreshEnabledDevicesAndDisplay(1, hWndGeneral, 1); - } - break; - case WM_TIMER: - // ignore generic timer callback and handle the hwnd-specific one which comes later - if (hWnd == 0) - return 0; - - if (!selected || selected == 0xFF) - { - // !selected is mostly for device added/removed when binding. - selected = 0xFF; - EndBinding(hWnd); - } - else - { - unsigned int uid; - int value; - - // The old code re-bound our button hWndProcEater to GetDlgItem(hWnd, selected). But at best hWnd - // was null (WM_TIMER is passed twice, once with a null parameter, and a second time that is hWnd - // specific), and at worst 'selected' is a post-processed code "based" on cmd, so GetDlgItem - // *always* returned NULL anyway. This resulted in hWndButton being null, which meant Device code - // used hWnd instead. This may have caused odd behavior since the callbacks were still all eaten - // by the initial GetDlgItem(hWnd, cmd) selection made when the timer was initialized. - - InitInfo info = {selected == 0x7F, 1, hWndProp, &hWndButtonProc}; - Device* dev = dm->GetActiveDevice(&info, &uid, &index, &value); - static unsigned int prev_uid = !uid; - - if (dev == nullptr || (quickSetup && (uid == prev_uid || uid & UID_AXIS))) - return 0; - - //Check the bindings for an excluded input, and ignore it if found. - PadBindings* p_c = dev->pads[port][slot] + padtype; - for (int i = 0; i < p_c->numBindings; i++) - { - Binding* b2 = p_c->bindings + i; - int uid2 = dev->virtualControls[b2->controlIndex].uid; - if (b2->command == 0x29 && uid == uid2) - { - return 0; - } - } - - int command = selected; - // Good idea to do this first, as BindCommand modifies the ListView, which will - // call it anyways, which is a bit funky. - if (!quickSetup) - EndBinding(hWnd); - UnselectAll(hWndList); - int index = -1; - if (command < 0x30) - { - index = BindCommand(dev, uid, port, slot, padtype, command, BASE_SENSITIVITY, 0, 0, 0); - } - if (index >= 0) - { - PropSheet_Changed(hWndProp, hWnds[port][slot][padtype]); - ListView_SetItemState(hWndList, index, LVIS_SELECTED, LVIS_SELECTED); - ListView_EnsureVisible(hWndList, index, 0); - config.bind = true; - ListView_SetColumnWidth(hWndList, 2, LVSCW_AUTOSIZE_USEHEADER); - - if (quickSetup) - { - HWND hWndS = GetDlgItem(hWnds[port][slot][padtype], selected + 0x40F0); - for (selected++; selected <= (ID_ANALOG - (ID_SELECT - 0x10)); selected++) - { - HWND hWndS = GetDlgItem(hWnds[port][slot][padtype], selected + 0x40F0); - if (hWndS) - { - break; - } - } - if (!hWndS || selected > (ID_ANALOG - (ID_SELECT - 0x10))) - { - prev_uid = !uid; - EndBinding(hWnd); - SetWindowTextW(GetDlgItem(hWnd, IDC_QUICK_SETUP_TEXT), L"Finished"); - } - else - { - wchar_t temp[60]; - wsprintfW(temp, L"Please enter an input for %ws", GetCommandStringW(selected, port, slot)); - SetWindowTextW(GetDlgItem(hWnd, IDC_QUICK_SETUP_TEXT), temp); - Sleep(80); - for (int i = 0; i < dm->numDevices; i++) - { - if (dm->devices[i]->active && (dm->devices[i]->api != WM || dm->devices[i]->api != RAW)) - dm->devices[i]->Deactivate(); - } - prev_uid = uid; - } - } - else - { - SetWindowTextW(GetDlgItem(hWnd, IDC_QUICK_SETUP_TEXT), L"Succeeded"); - } - } - } - break; - case WM_SYSKEYDOWN: - case WM_KEYDOWN: - EndBinding(hWnd); - break; - case PSM_CANCELTOCLOSE: - // Load(); - break; - case WM_ACTIVATE: - EndBinding(hWnd); - break; - case WM_NOTIFY: - { - PSHNOTIFY* n = (PSHNOTIFY*)lParam; - if (n->hdr.hwndFrom == hWndProp) - { - switch (n->hdr.code) - { - case PSN_QUERYCANCEL: - case PSN_KILLACTIVE: - EndBinding(hWnd); - return 0; - case PSN_SETACTIVE: - SetWindowTextW(GetDlgItem(hWnd, IDC_QUICK_SETUP_TEXT), L""); - ListView_SetColumnWidth(hWndList, 2, LVSCW_AUTOSIZE_USEHEADER); - return 0; - case PSN_APPLY: - SetWindowLongPtr(hWnd, DWLP_MSGRESULT, PSNRET_NOERROR); - return 1; - } - break; - } - else if (n->hdr.idFrom == IDC_BINDINGS_LIST) - { - static int NeedUpdate = 0; - if (n->hdr.code == LVN_KEYDOWN) - { - NMLVKEYDOWN* key = (NMLVKEYDOWN*)n; - if (key->wVKey == VK_DELETE || - key->wVKey == VK_BACK) - { - - if (DeleteSelected(port, slot)) - { - PropSheet_Changed(hWndProp, hWnds[0]); - ListView_SetColumnWidth(hWndList, 2, LVSCW_AUTOSIZE_USEHEADER); - } - } - } - // Update sensitivity and motor/binding display on redraw - // rather than on itemchanged. This reduces blinking, as - // I get 3 LVN_ITEMCHANGED messages, and first is sent before - // the new item is set as being selected. - else if (n->hdr.code == LVN_ITEMCHANGED) - { - NeedUpdate = 1; - } - else if (n->hdr.code == NM_CUSTOMDRAW && NeedUpdate) - { - NeedUpdate = 0; - SelChanged(port, slot); - } - if (n->hdr.code == NM_RCLICK) - { - HMENU hMenu = CreatePopupMenu(); - if (!hMenu) - break; - MENUITEMINFOW info; - wchar_t text[40]; - info.cbSize = sizeof(info); - info.fMask = MIIM_STRING | MIIM_ID; - info.dwTypeData = text; - info.wID = 1; - wsprintfW(text, L"Delete Selected"); - InsertMenuItemW(hMenu, 0, 1, &info); - info.wID = 2; - wsprintfW(text, L"Clear All"); - InsertMenuItemW(hMenu, 1, 1, &info); - POINT pos; - GetCursorPos(&pos); - short res = TrackPopupMenuEx(hMenu, TPM_NONOTIFY | TPM_RETURNCMD, pos.x, pos.y, hWndProp, 0); - DestroyMenu(hMenu); - if (res == 0) - break; - else if (res == 1) - { - if (DeleteSelected(port, slot)) - PropSheet_Changed(hWndProp, hWnd); - } - else if (res == 2) - { - while (DeleteByIndex(port, slot, 0)) - PropSheet_Changed(hWndProp, hWnd); - } - } - } - } - break; - case WM_HSCROLL: - { - int id = GetDlgCtrlID((HWND)lParam); - int val = GetLogSliderVal(hWnd, id); - if (id == IDC_SLIDER_SENSITIVITY) - { - ChangeValue(port, slot, &val, 0, 0, 0); - } - else if (id == IDC_SLIDER_DEADZONE) - { - ChangeValue(port, slot, 0, 0, &val, 0); - } - else if (id == IDC_SLIDER_SKIP_DEADZONE) - { - ChangeValue(port, slot, 0, 0, 0, &val); - } - else - { - ChangeEffect(port, slot, id, &val, 0); - } - } - break; - case WM_COMMAND: - if (HIWORD(wParam) == CBN_SELCHANGE && LOWORD(wParam) == IDC_AXIS_DIRECTION) - { - int index = ListView_GetNextItem(hWndList, -1, LVNI_SELECTED); - if (index >= 0) - { - int cbsel = SendMessage((HWND)lParam, CB_GETCURSEL, 0, 0); - if (cbsel >= 0) - { - ForceFeedbackBinding* ffb; - Binding* b; - Device* dev; - if (GetBinding(port, slot, index, dev, b, ffb)) - { - const static unsigned int axisUIDs[3] = {UID_AXIS_NEG, UID_AXIS_POS, UID_AXIS}; - int uid = dev->virtualControls[b->controlIndex].uid; - uid = (uid & 0x00FFFFFF) | axisUIDs[cbsel]; - Binding backup = *b; - DeleteSelected(port, slot); - int index = BindCommand(dev, uid, port, slot, padtype, backup.command, backup.sensitivity, backup.rapidFire, backup.deadZone, backup.skipDeadZone); - ListView_SetItemState(hWndList, index, LVIS_SELECTED, LVIS_SELECTED); - PropSheet_Changed(hWndProp, hWnd); - } - } - } - } - else if (HIWORD(wParam) == CBN_SELCHANGE && LOWORD(wParam) == IDC_DEVICE_SELECT) - { - int selectedDev = SendMessage(GetDlgItem(hWnd, IDC_DEVICE_SELECT), CB_GETCURSEL, 0, 0); - config.deviceSelect[port][slot] = SendMessage(GetDlgItem(hWnd, IDC_DEVICE_SELECT), CB_GETITEMDATA, selectedDev, 0); - RefreshEnabledDevicesAndDisplay(1, hWndGeneral, 1); - if (selectedDev > 0) - { - for (int i = 0; i < dm->numDevices; i++) - { - Device* dev = dm->devices[config.deviceSelect[port][slot]]; - if (dm->devices[i] != dev) - { - dm->DisableDevice(i); - } - else - { - dm->EnableDevice(i); - } - } - } - ListView_SetColumnWidth(hWndList, 2, LVSCW_AUTOSIZE_USEHEADER); - } - else if (HIWORD(wParam) == CBN_SELCHANGE && LOWORD(wParam) == IDC_FF_EFFECT) - { - int typeIndex = SendMessage((HWND)lParam, CB_GETCURSEL, 0, 0); - if (typeIndex >= 0) - ChangeEffect(port, slot, 0, 0, (unsigned int*)&typeIndex); - } - else if (HIWORD(wParam) == BN_CLICKED) - { - int cmd = LOWORD(wParam); - if (quickSetup && cmd == ID_QUICK_SETUP) - { - SetWindowTextW(GetDlgItem(hWnd, IDC_QUICK_SETUP_TEXT), L"Stopped"); - EndBinding(hWnd); - return 0; - } - EndBinding(hWnd); - if (cmd == ID_BIG_MOTOR || cmd == ID_SMALL_MOTOR) - { - int i = (int)SendMessage(GetDlgItem(hWnd, IDC_FORCEFEEDBACK), CB_GETCURSEL, 0, 0); - if (i >= 0) - { - unsigned int index = (unsigned int)SendMessage(GetDlgItem(hWnd, IDC_FORCEFEEDBACK), CB_GETITEMDATA, i, 0); - if (index < (unsigned int)dm->numDevices) - { - Device* dev = dm->devices[index]; - ForceFeedbackBinding* b; - wchar_t* effectID = 0; - if (dev->api == DI) - { - // Constant effect. - if (cmd == ID_BIG_MOTOR) - effectID = L"13541C20-8E33-11D0-9AD0-00A0C9A06E35"; - // Square. - else - effectID = L"13541C22-8E33-11D0-9AD0-00A0C9A06E35"; - } - int count = CreateEffectBinding(dev, effectID, port, slot, padtype, cmd - ID_BIG_MOTOR, &b); - if (b) - { - int needSet = 1; - if (dev->api == XINPUT && dev->numFFAxes == 2) - { - needSet = 0; - if (cmd == ID_BIG_MOTOR) - { - b->axes[0].force = BASE_SENSITIVITY; - } - else - { - b->axes[1].force = BASE_SENSITIVITY; - } - } - else if (dev->api == DS3 && dev->numFFAxes == 2) - { - needSet = 0; - if (cmd == ID_BIG_MOTOR) - { - b->axes[0].force = BASE_SENSITIVITY; - } - else - { - b->axes[1].force = BASE_SENSITIVITY; - } - } - else if (dev->api == DI) - { - int bigIndex = 0, littleIndex = 0; - int j; - for (j = 0; j < dev->numFFAxes; j++) - { - // DI object instance. 0 is x-axis, 1 is y-axis. - int instance = (dev->ffAxes[j].id >> 8) & 0xFFFF; - if (instance == 0) - { - bigIndex = j; - } - else if (instance == 1) - { - littleIndex = j; - } - } - needSet = 0; - if (cmd == ID_BIG_MOTOR) - { - b->axes[bigIndex].force = BASE_SENSITIVITY; - b->axes[littleIndex].force = 1; - } - else - { - b->axes[bigIndex].force = 1; - b->axes[littleIndex].force = BASE_SENSITIVITY; - } - } - if (needSet) - { - for (int j = 0; j < 2 && j < dev->numFFAxes; j++) - { - b->axes[j].force = BASE_SENSITIVITY; - } - } - UnselectAll(hWndList); - ListView_SetItemState(hWndList, count, LVIS_SELECTED, LVIS_SELECTED); - ListView_EnsureVisible(hWndList, count, 0); - } - PropSheet_Changed(hWndProp, hWnd); - } - } - } - else if (cmd == ID_CONTROLS) - { - UnselectAll(hWndList); - SetWindowTextW(GetDlgItem(hWnd, IDC_QUICK_SETUP_TEXT), L""); - ListView_SetColumnWidth(hWndList, 2, LVSCW_AUTOSIZE_USEHEADER); - } - else if (cmd == ID_TEST) - { - // Just in case... - if (selected) - break; - int selIndex = ListView_GetNextItem(hWndList, -1, LVNI_SELECTED); - if (selIndex >= 0) - { - Device* dev; - Binding* b; - ForceFeedbackBinding* ffb = 0; - if (GetBinding(port, slot, selIndex, dev, b, ffb)) - { - selected = 0xFF; - hWndButtonProc.SetWndHandle(GetDlgItem(hWnd, cmd)); - InitInfo info = {0, 1, hWndProp, &hWndButtonProc}; - for (int i = 0; i < dm->numDevices; i++) - { - if (dm->devices[i] != dev) - { - dm->DisableDevice(i); - } - } - dm->Update(&info); - dm->PostRead(); - dev->SetEffect(ffb, 255); - Sleep(200); - dm->Update(&info); - SetTimer(hWnd, 1, 3000, 0); - } - } - } - else if (cmd >= IDC_DPAD && cmd <= ID_QUICK_SETUP) - { // || cmd == ID_FORCE_FEEDBACK) { - // Messes up things, unfortunately. - // End binding on a bunch of notification messages, and - // this will send a bunch. - // UnselectAll(hWndList); - EndBinding(hWnd); - SetWindowTextW(GetDlgItem(hWnd, IDC_QUICK_SETUP_TEXT), L""); - - RefreshEnabledDevices(0); - int selectedDev = SendMessage(GetDlgItem(hWnd, IDC_DEVICE_SELECT), CB_GETCURSEL, 0, 0); - if (selectedDev > 0) - { - for (int i = 0; i < dm->numDevices; i++) - { - Device* dev = dm->devices[config.deviceSelect[port][slot]]; - if (dm->devices[i] != dev) - { - dm->DisableDevice(i); - } - else - { - dm->EnableDevice(i); - } - } - } - - if (cmd != ID_QUICK_SETUP) - { - selected = cmd - (ID_SELECT - 0x10); - } - else - { - quickSetup = true; - HWND hWndS; - for (selected = 0x10; selected <= (ID_ANALOG - (ID_SELECT - 0x10)); selected++) - { - hWndS = GetDlgItem(hWnds[port][slot][padtype], selected + 0x40F0); - if (hWndS) - { - break; - } - } - if (hWndS) - { - SetWindowTextW(GetDlgItem(hWnd, ID_QUICK_SETUP), L"Stop"); - } - else - { - EndBinding(hWnd); - return 0; - } - } - wchar_t temp[60]; - wsprintfW(temp, L"Please enter an input for %ws", GetCommandStringW(selected, port, slot)); - SetWindowTextW(GetDlgItem(hWnd, IDC_QUICK_SETUP_TEXT), temp); - - hWndButtonProc.SetWndHandle(GetDlgItem(hWnd, cmd)); - hWndButtonProc.Eat(DoNothingWndProc, 0); - InitInfo info = {selected == 0x7F, 1, hWndProp, &hWndButtonProc}; - int w = timeGetTime(); - dm->Update(&info); - dm->PostRead(); - // Workaround for things that return 0 on first poll and something else ever after. - Sleep(80); - dm->Update(&info); - dm->PostRead(); - SetTimer(hWnd, 1, 30, 0); - } - else if (cmd == IDC_CONFIGURE_ON_BIND) - { - config.configureOnBind = IsDlgButtonChecked(hWnd, IDC_CONFIGURE_ON_BIND); - } - else if (cmd == ID_SPECIAL_INPUTS) - { - config.specialInputs[port][slot] = !config.specialInputs[port][slot]; - // FIXME: Fix DPI Scaling. Below code is commented out and a fixed size for inputs window is used to avoid DPI scaling issues. - // SetWindowPos(hWndList, NULL, 0, 0, 275, config.specialInputs[port][slot] ? 324 : 440, SWP_NOZORDER | SWP_NOMOVE); - // ListView_SetColumnWidth(hWndList, 2, LVSCW_AUTOSIZE_USEHEADER); - // UnselectAll(hWndList); - SelChanged(port, slot); - } - if (cmd == IDC_RAPID_FIRE) - { - // Don't allow setting it back to indeterminate. - SendMessage(GetDlgItem(hWnd, IDC_RAPID_FIRE), BM_SETSTYLE, BS_AUTOCHECKBOX, 0); - int rapidFire = (IsDlgButtonChecked(hWnd, IDC_RAPID_FIRE) == BST_CHECKED); - ChangeValue(port, slot, 0, &rapidFire, 0, 0); - } - else if (cmd == IDC_FLIP) - { - int val = GetLogSliderVal(hWnd, IDC_SLIDER_SENSITIVITY); - ChangeValue(port, slot, &val, 0, 0, 0); - } - else if (cmd == ID_RESET_CONFIG) - { - // Just in case... - if (selected) - break; - int selIndex = ListView_GetNextItem(hWndList, -1, LVNI_SELECTED); - if (selIndex >= 0) - { - Device* dev; - Binding* b; - ForceFeedbackBinding* ffb = 0; - if (GetBinding(port, slot, selIndex, dev, b, ffb)) - { - selected = 0xFF; - if (b) - { - VirtualControl* control = &dev->virtualControls[b->controlIndex]; - for (int i = IDC_SLIDER_SENSITIVITY; i <= IDC_SLIDER_SKIP_DEADZONE; i++) - { - if (i == IDC_SLIDER_SENSITIVITY) - { - int val = BASE_SENSITIVITY; - if (((control->uid >> 16) & 0xFF) == ABSAXIS) - { - val = BASE_ANALOG_SENSITIVITY; - } - ChangeValue(port, slot, &val, 0, 0, 0); - } - else if (i == IDC_SLIDER_DEADZONE) - { - int val = DEFAULT_DEADZONE; - if ((control->uid >> 16) & (PSHBTN | TGLBTN)) - { - val = 0; - } - else if ((control->uid >> 16) & PRESSURE_BTN) - { - val = 1; - } - ChangeValue(port, slot, 0, 0, &val, 0); - } - else if (i == IDC_SLIDER_SKIP_DEADZONE) - { - int val = 1; - if ((control->uid >> 16) & (PSHBTN | TGLBTN)) - { - val = 0; - } - ChangeValue(port, slot, 0, 0, 0, &val); - } - else if (i == IDC_RAPID_FIRE) - { - int val = 0; - ChangeValue(port, slot, 0, &val, 0, 0); - } - } - } - else if (ffb) - { - for (int i = IDC_FF_AXIS1; i <= IDC_FF_AXIS8; i += 0x10) - { - int val = BASE_SENSITIVITY; - if (ffb->motor != ((i - IDC_FF_AXIS1) / 0x10) || IsDlgButtonChecked(hWnd, i - 1) != BST_CHECKED) - { - val = 0; - } - ChangeEffect(port, slot, i, &val, 0); - } - } - } - } - } - else if (cmd >= IDC_FF_AXIS1_ENABLED && cmd < IDC_FF_AXIS8_ENABLED + 16) - { - int index = (cmd - IDC_FF_AXIS1_ENABLED) / 16; - int val = GetLogSliderVal(hWnd, 16 * index + IDC_FF_AXIS1); - if (IsDlgButtonChecked(hWnd, 16 * index + IDC_FF_AXIS1_ENABLED) != BST_CHECKED) - { - val = 0; - } - ChangeEffect(port, slot, cmd, &val, 0); - } - } - break; - default: - break; - } - return 0; -} - -// Returns 0 if pad doesn't exist due to mtap settings, as a convenience. -int GetPadString(wchar_t* string, unsigned int port, unsigned int slot) -{ - if (!slot && !config.multitap[port]) - { - wsprintfW(string, L"Pad %i", port + 1); - } - else - { - wsprintfW(string, L"Pad %i%c", port + 1, 'A' + slot); - if (!config.multitap[port]) - return 0; - } - return 1; -} - -// Returns 0 if pad doesn't exist due to mtap settings, as a convenience. -int GetPadTypeString(wchar_t* string, unsigned int port, unsigned int slot, unsigned int padtype) -{ - if (!slot && !config.multitap[port]) - { - wsprintfW(string, L"Pad %i, %s", port + 1, padTypes[padtype]); - } - else - { - wsprintfW(string, L"Pad %i%c, %s", port + 1, 'A' + slot, padTypes[padtype]); - if (!config.multitap[port]) - return 0; - } - return 1; -} - -bool ProfilesBindingCheck(unsigned int port, unsigned int slot, unsigned int padtype) -{ - bool showWarning = true; - int count = 0; - for (int i = 0; i < dm->numDevices; i++) - { - for (int portCheck = 0; portCheck < 2; portCheck++) - { - int maxslot = 1; - if (config.multitap[portCheck]) - maxslot = 4; - if (portCheck == port && !config.multitap[portCheck]) - continue; - for (int slotCheck = 0; slotCheck < maxslot; slotCheck++) - { - if (portCheck == port && slotCheck == slot) - continue; - int padtypeCheck = config.padConfigs[portCheck][slotCheck].type; - PadBindings* pCheck = dm->devices[i]->pads[portCheck][slotCheck] + padtypeCheck; - PadBindings* pActive = dm->devices[i]->pads[port][slot] + padtype; - for (int j = 0; j < pCheck->numBindings; j++) - { - for (int k = 0; k < pActive->numBindings; k++) - { - Binding* bCheck = pCheck->bindings + j; - Binding* bActive = pActive->bindings + k; - int uidCheck = dm->devices[i]->virtualControls[bCheck->controlIndex].uid; - int uidActive = dm->devices[i]->virtualControls[bActive->controlIndex].uid; - if (bCheck->controlIndex == bActive->controlIndex || (!((uidActive ^ uidCheck) & 0xFFFFFF) && ((uidCheck | uidActive) & (UID_POV | UID_AXIS)))) - { - if (showWarning) - { - int msgboxID = MessageBoxA(hWndProp, "Warning! You have selected a pad type that has one or several bindings that conflict with the active pad type of the opposing port or slot(s).\n\n" - "Do you want to keep the bindings of the pad type you are switching to?\n" - "Click \"Yes\" to continue without deleting any binding.\n" - "Click \"No\" to continue and delete any conflicting bindings from the selected pad type.\n" - "Click \"Cancel\" to revert to the previously selected pad type and avoid any further action.\n\n" - "Note: Enable the 'Allow binding multiple PS2 controls to one PC control' option to allow conflicting bindings between opposing ports and slots, and avoid this warning and the possibility of bindings getting deleted.", - "Duplicate Binding Warning", MB_YESNOCANCEL | MB_DEFBUTTON3 | MB_ICONWARNING); - switch (msgboxID) - { - case IDCANCEL: - return false; - break; - case IDYES: - return true; - break; - case IDNO: - showWarning = false; - break; - } - } - DeleteBinding(port, slot, padtype, dm->devices[i], bActive); - k--; - count++; - } - } - } - } - } - } - return true; -} - -void UpdatePadPages() -{ - HPROPSHEETPAGE pages[10]; - int count = 0; - memset(hWnds, 0, sizeof(hWnds)); - for (int port = 0; port < 2; port++) - { - for (int slot = 0; slot < 4; slot++) - { - if (config.padConfigs[port][slot].type == DisabledPad) - continue; - wchar_t title[20]; - if (!GetPadString(title, port, slot)) - continue; - - PROPSHEETPAGE psp; - ZeroMemory(&psp, sizeof(psp)); - psp.dwSize = sizeof(psp); - psp.dwFlags = PSP_USETITLE | PSP_PREMATURE; - psp.hInstance = GetModuleHandle(nullptr); - psp.pfnDlgProc = DialogProc; - psp.lParam = port | (slot << 1); - psp.pszTitle = title; - if (config.padConfigs[port][slot].type == GuitarPad) - psp.pszTemplate = MAKEINTRESOURCE(IDD_CONFIG_GUITAR); - else if (config.padConfigs[port][slot].type == PopnPad) - psp.pszTemplate = MAKEINTRESOURCE(IDD_CONFIG_POPN); - else if (config.padConfigs[port][slot].type == MousePad) - psp.pszTemplate = MAKEINTRESOURCE(IDD_CONFIG_PS1_MOUSE); - else if (config.padConfigs[port][slot].type == neGconPad) - psp.pszTemplate = MAKEINTRESOURCE(IDD_CONFIG_NEGCON); - else - psp.pszTemplate = MAKEINTRESOURCE(IDD_CONFIG); - - pages[count] = CreatePropertySheetPage(&psp); - if (pages[count]) - count++; - } - } - while (SendMessage(hWndProp, PSM_INDEXTOPAGE, 1, 0)) - { - PropSheet_RemovePage(hWndProp, 1, 0); - } - for (int i = 0; i < count; i++) - { - PropSheet_AddPage(hWndProp, pages[i]); - } -} - -int ListIndexToPortAndSlot(int index, int* port, int* slot) -{ - if (index < 0 || index >= 2 + 3 * (config.multitap[0] + config.multitap[1])) - { - *port = 0; - *slot = 0; - return 0; - } - if (index < 1 + 3 * config.multitap[0]) - { - *port = 0; - *slot = index; - } - else - { - *port = 1; - *slot = index - 1 - 3 * config.multitap[0]; - } - return 1; -} - -void UpdatePadList(HWND hWnd) -{ - static u8 recurse = 0; - if (recurse) - return; - recurse = 1; - HWND hWndList = GetDlgItem(hWnd, IDC_PAD_LIST); - HWND hWndCombo = GetDlgItem(hWnd, IDC_PAD_TYPE); - int slot; - int port; - int index = 0; - - for (port = 0; port < 2; port++) - { - for (slot = 0; slot < 4; slot++) - { - int padtype = config.padConfigs[port][slot].type; - wchar_t text[25]; - if (!GetPadString(text, port, slot)) - continue; - LVITEM item; - item.iItem = index; - item.iSubItem = 0; - item.mask = LVIF_TEXT; - item.pszText = text; - if (SendMessage(hWndList, LVM_GETITEMCOUNT, 0, 0) <= index) - { - ListView_InsertItem(hWndList, &item); - } - else - { - ListView_SetItem(hWndList, &item); - } - - item.iSubItem = 1; - if (numPadTypes - 1 < (unsigned int)padtype) - padtype = Dualshock2Pad; - wcsncpy(item.pszText, padTypes[padtype], 25); - //if (!slot && !config.padConfigs[port][slot].type) - // item.pszText = L"Unplugged (Kinda)"; - - ListView_SetItem(hWndList, &item); - - item.iSubItem = 2; - int count = 0; - int selectedDevice = config.deviceSelect[port][slot]; - for (int i = 0; i < dm->numDevices; i++) - { - Device* dev = dm->devices[i]; - if (!dev->enabled) - continue; - count += dev->pads[port][slot][padtype].numBindings + dev->pads[port][slot][padtype].numFFBindings; - } - wsprintf(text, L"%i", count); - item.pszText = text; - ListView_SetItem(hWndList, &item); - index++; - } - } - while (ListView_DeleteItem(hWndList, index)) - ; - int sel = ListView_GetNextItem(hWndList, -1, LVNI_SELECTED); - - int enable; - if (!ListIndexToPortAndSlot(sel, &port, &slot)) - { - enable = 0; - SendMessage(hWndCombo, CB_SETCURSEL, -1, 0); - } - else - { - enable = 1; - SendMessage(hWndCombo, CB_SETCURSEL, config.padConfigs[port][slot].type, 0); - } - EnableWindow(hWndCombo, enable); - //ListView_SetExtendedListViewStyleEx(hWndList, LVS_EX_DOUBLEBUFFER|LVS_EX_ONECLICKACTIVATE, LVS_EX_DOUBLEBUFFER|LVS_EX_ONECLICKACTIVATE); - recurse = 0; -} - -INT_PTR CALLBACK GeneralDialogProc(HWND hWnd, unsigned int msg, WPARAM wParam, LPARAM lParam) -{ - HWND hWndList = GetDlgItem(hWnd, IDC_PAD_LIST); - switch (msg) - { - case WM_INITDIALOG: - { - HWND hWndCombo = GetDlgItem(hWnd, IDC_PAD_TYPE); - if (SendMessage(hWndCombo, CB_GETCOUNT, 0, 0) == 0) - { - LVCOLUMN c; - c.mask = LVCF_TEXT | LVCF_WIDTH; - c.cx = 50; - c.pszText = L"Pad"; - ListView_InsertColumn(hWndList, 0, &c); - c.cx = 120; - c.pszText = L"Type"; - ListView_InsertColumn(hWndList, 1, &c); - c.pszText = L"Bindings"; - ListView_InsertColumn(hWndList, 2, &c); - ListView_SetColumnWidth(hWndList, 2, LVSCW_AUTOSIZE_USEHEADER); - selected = 0; - ListView_SetExtendedListViewStyleEx(hWndList, LVS_EX_FULLROWSELECT | LVS_EX_DOUBLEBUFFER, LVS_EX_FULLROWSELECT | LVS_EX_DOUBLEBUFFER); - SendMessage(hWndList, LVM_SETEXTENDEDLISTVIEWSTYLE, 0, LVS_EX_FULLROWSELECT); - for (int i = 0; i < numPadTypes; i++) - SendMessage(hWndCombo, CB_ADDSTRING, 0, (LPARAM)padTypes[i]); - } - } - UpdatePadPages(); - hWndGeneral = hWnd; - RefreshEnabledDevicesAndDisplay(0, hWnd, 0); - UpdatePadList(hWnd); - - if (!DualShock3Possible()) - { - config.gameApis.dualShock3 = 0; - EnableWindow(GetDlgItem(hWnd, IDC_G_DS3), 0); - } - - for (int j = 0; j < sizeof(BoolOptionsInfo) / sizeof(BoolOptionsInfo[0]); j++) - { - CheckDlgButton(hWnd, BoolOptionsInfo[j].ControlId, BST_CHECKED * config.bools[j]); - } - - AddTooltip(IDC_M_WM, hWnd); - AddTooltip(IDC_M_RAW, hWnd); - AddTooltip(IDC_MOUSE_UNFOCUS, hWnd); - AddTooltip(IDC_MULTIPLE_BINDING, hWnd); - AddTooltip(IDC_PAD_LIST, hWnd); - AddTooltip(IDC_PAD_TYPE, hWnd); - AddTooltip(IDC_DIAG_LIST, hWnd); - AddTooltip(IDC_G_DI, hWnd); - AddTooltip(IDC_G_XI, hWnd); - AddTooltip(ID_RESTORE_DEFAULTS, hWnd); - - if (config.keyboardApi < 0 || config.keyboardApi > 3) - config.keyboardApi = NO_API; - CheckRadioButton(hWnd, IDC_KB_DISABLE, IDC_KB_RAW, IDC_KB_DISABLE + config.keyboardApi); - if (config.mouseApi < 0 || config.mouseApi > 3) - config.mouseApi = NO_API; - CheckRadioButton(hWnd, IDC_M_DISABLE, IDC_M_RAW, IDC_M_DISABLE + config.mouseApi); - - break; - case WM_DEVICECHANGE: - if (wParam == DBT_DEVNODES_CHANGED) - { - RefreshEnabledDevicesAndDisplay(1, hWndGeneral, 1); - UpdatePadList(hWnd); - } - break; - case WM_COMMAND: - if (LOWORD(wParam) == IDC_PAD_TYPE) - { - if (HIWORD(wParam) == CBN_SELCHANGE) - { - HWND hWndCombo = GetDlgItem(hWnd, IDC_PAD_TYPE); - int index = ListView_GetNextItem(hWndList, -1, LVNI_SELECTED); - int sel = SendMessage(hWndCombo, CB_GETCURSEL, 0, 0); - int port, slot; - if (sel < 0 || !ListIndexToPortAndSlot(index, &port, &slot)) - break; - if (sel != config.padConfigs[port][slot].type) - { - if (!config.multipleBinding && !ProfilesBindingCheck(port, slot, sel)) - break; - config.padConfigs[port][slot].type = (PadType)sel; - UpdatePadList(hWnd); - UpdatePadPages(); - RefreshEnabledDevicesAndDisplay(0, hWnd, 1); - PropSheet_Changed(hWndProp, hWnd); - } - } - } - else if (HIWORD(wParam) == BN_CLICKED && (LOWORD(wParam) == ID_LOAD || LOWORD(wParam) == ID_SAVE)) - { - OPENFILENAMEW ofn; - memset(&ofn, 0, sizeof(ofn)); - ofn.lStructSize = sizeof(ofn); - ofn.hwndOwner = hWnd; - ofn.lpstrFilter = L"PAD Config Files\0*.pad\0All Files\0*.*\0\0"; - wchar_t file[MAX_PATH + 1]; - ofn.lpstrFile = file; - ofn.nMaxFile = MAX_PATH; - wcscpy(file, config.lastSaveConfigFileName); - ofn.lpstrInitialDir = config.lastSaveConfigPath; - ofn.Flags = OFN_DONTADDTORECENT | OFN_LONGNAMES | OFN_NOCHANGEDIR; - ofn.lpstrDefExt = L"pad"; - if (LOWORD(wParam) == ID_LOAD) - { - ofn.lpstrTitle = L"Load PAD Configuration"; - ofn.Flags |= OFN_FILEMUSTEXIST; - if (GetOpenFileNameW(&ofn)) - { - LoadSettings(1, ofn.lpstrFile); - GeneralDialogProc(hWnd, WM_INITDIALOG, 0, 0); - PropSheet_Changed(hWndProp, hWnd); - } - } - else - { - ofn.lpstrTitle = L"Save PAD Configuration"; - ofn.Flags |= OFN_OVERWRITEPROMPT; - if (GetSaveFileNameW(&ofn)) - { - if (SaveSettings(ofn.lpstrFile) == -1) - { - MessageBox(hWnd, L"Save fail", L"Couldn't save file.", MB_OK); - } - } - } - break; - } - else if (HIWORD(wParam) == BN_CLICKED && LOWORD(wParam) == ID_RESTORE_DEFAULTS) - { - int msgboxID = MessageBoxA(hWndProp, "This will delete all current settings and revert back to the default settings of PAD. Continue?", - "Restore Defaults Confirmation", MB_YESNO | MB_DEFBUTTON2 | MB_ICONEXCLAMATION); - switch (msgboxID) - { - case IDNO: - break; - case IDYES: - char iniLocation[MAX_PATH * 2] = "inis/PAD.ini"; - remove(iniLocation); - createIniDir = true; - LoadSettings(1); - GeneralDialogProc(hWnd, WM_INITDIALOG, 0, 0); - PropSheet_Changed(hWndProp, hWnd); - break; - } - } - else - { - - int mtap = config.multitap[0] + 2 * config.multitap[1]; - - for (int j = 0; j < sizeof(BoolOptionsInfo) / sizeof(BoolOptionsInfo[0]); j++) - { - config.bools[j] = (IsDlgButtonChecked(hWnd, BoolOptionsInfo[j].ControlId) == BST_CHECKED); - } - - for (int i = 0; i < 4; i++) - { - if (i && IsDlgButtonChecked(hWnd, IDC_KB_DISABLE + i) == BST_CHECKED) - { - config.keyboardApi = (DeviceAPI)i; - } - if (IsDlgButtonChecked(hWnd, IDC_M_DISABLE + i) == BST_CHECKED) - { - config.mouseApi = (DeviceAPI)i; - } - } - - if (mtap != config.multitap[0] + 2 * config.multitap[1]) - { - UpdatePadPages(); - } - RefreshEnabledDevicesAndDisplay(0, hWnd, 1); - UpdatePadList(hWnd); - - PropSheet_Changed(hWndProp, hWnd); - - if (mtap != config.multitap[0] + 2 * config.multitap[1]) - { - ListView_SetColumnWidth(hWndList, 2, LVSCW_AUTOSIZE_USEHEADER); - } - } - break; - case WM_NOTIFY: - { - PSHNOTIFY* n = (PSHNOTIFY*)lParam; - if (n->hdr.hwndFrom == hWndProp) - { - switch (n->hdr.code) - { - case PSN_QUERYCANCEL: - case PSN_KILLACTIVE: - EndBinding(hWnd); - return 0; - case PSN_SETACTIVE: - //selected = 0; - UpdatePadList(hWnd); - return 0; - case PSN_APPLY: - selected = 0; - if (SaveSettings()) - { - SetWindowLongPtr(hWnd, DWLP_MSGRESULT, PSNRET_INVALID_NOCHANGEPAGE); - return 0; - } - SetWindowLongPtr(hWnd, DWLP_MSGRESULT, PSNRET_NOERROR); - return 1; - } - } - else if (n->hdr.idFrom == IDC_DIAG_LIST) - { - if (n->hdr.code == NM_DBLCLK) - { - Diagnostics(hWnd); - } - else if (n->hdr.code == NM_RCLICK) - { - HMENU hMenu = CreatePopupMenu(); - if (!hMenu) - break; - MENUITEMINFOW info; - wchar_t text[40]; - info.cbSize = sizeof(info); - info.fMask = MIIM_STRING | MIIM_ID; - info.dwTypeData = text; - info.wID = 1; - wsprintfW(text, L"Test Device"); - InsertMenuItemW(hMenu, 0, 1, &info); - info.wID = 2; - wsprintfW(text, L"Refresh"); - InsertMenuItemW(hMenu, 1, 1, &info); - POINT pos; - GetCursorPos(&pos); - short res = TrackPopupMenuEx(hMenu, TPM_NONOTIFY | TPM_RETURNCMD, pos.x, pos.y, hWndProp, 0); - DestroyMenu(hMenu); - if (res == 0) - break; - else if (res == 1) - { - Diagnostics(hWnd); - RefreshEnabledDevices(); - } - else if (res == 2) - { - RefreshEnabledDevicesAndDisplay(1, hWnd, 1); - UpdatePadList(hWnd); - } - } - } - else if (n->hdr.idFrom == IDC_PAD_LIST) - { - if (n->hdr.code == LVN_ITEMCHANGED) - { - UpdatePadList(hWnd); - } - if (n->hdr.code == NM_DBLCLK) - { - UpdatePadList(hWnd); - int index = ListView_GetNextItem(hWndList, -1, LVNI_SELECTED); - PropSheet_SetCurSel(hWndProp, 0, index + 1); - } - else if (n->hdr.code == NM_RCLICK) - { - UpdatePadList(hWnd); - int index = ListView_GetNextItem(hWndList, -1, LVNI_SELECTED); - int port1, slot1, port2, slot2, padtype1, padtype2; - if (!ListIndexToPortAndSlot(index, &port1, &slot1)) - break; - padtype1 = config.padConfigs[port1][slot1].type; - HMENU hMenu = CreatePopupMenu(); - if (!hMenu) - break; - MENUITEMINFOW info; - for (int IndividualPadtypes = 1; IndividualPadtypes >= 0; IndividualPadtypes--) - { - info.fMask = MIIM_TYPE; - info.fType = MFT_SEPARATOR; - InsertMenuItemW(hMenu, 0, 1, &info); - for (port2 = 1; port2 >= 0; port2--) - { - for (slot2 = 3; slot2 >= 0; slot2--) - { - padtype2 = config.padConfigs[port2][slot2].type; - wchar_t text[60]; - wchar_t pad[40]; - if (IndividualPadtypes == 0) - { - if (!GetPadString(pad, port2, slot2)) - continue; - } - else - { - if (!GetPadTypeString(pad, port2, slot2, padtype2)) - continue; - } - info.cbSize = sizeof(info); - info.fMask = MIIM_STRING | MIIM_ID; - info.dwTypeData = text; - if (port2 == port1 && slot2 == slot1 && padtype2 == padtype1) - { - int index = GetMenuItemCount(hMenu); - wsprintfW(text, L"Clear %s Bindings", pad); - if (IndividualPadtypes == 0) - { - info.wID = -1; - wsprintfW(text, L"Clear all %s Bindings", pad); - } - else - { - info.wID = -2; - wsprintfW(text, L"Clear %s Bindings only", pad); - } - InsertMenuItemW(hMenu, index, 1, &info); - info.fMask = MIIM_TYPE; - info.fType = MFT_SEPARATOR; - InsertMenuItemW(hMenu, index, 1, &info); - } - else if (!(port2 == port1 && slot2 == slot1)) - { - info.wID = port2 + 2 * slot2 + 1; - if (IndividualPadtypes == 1) - { - if (!config.multipleBinding && padtype2 != padtype1) - continue; - info.wID += 8; - wsprintfW(text, L"Swap with %s bindings only", pad); - } - else - { - wsprintfW(text, L"Swap all bindings with %s", pad); - } - InsertMenuItemW(hMenu, 0, 1, &info); - } - } - } - } - POINT pos; - GetCursorPos(&pos); - short res = TrackPopupMenuEx(hMenu, TPM_NONOTIFY | TPM_RETURNCMD, pos.x, pos.y, hWndProp, 0); - DestroyMenu(hMenu); - if (!res) - break; - bool swapIndividualPadtypes = false; - if (res > 0) - { - if (res > 8) - { - res -= 8; - swapIndividualPadtypes = true; - } - res--; - slot2 = res / 2; - port2 = res & 1; - padtype2 = config.padConfigs[port2][slot2].type; - PadConfig padCfgTemp = config.padConfigs[port1][slot1]; - config.padConfigs[port1][slot1] = config.padConfigs[port2][slot2]; - config.padConfigs[port2][slot2] = padCfgTemp; - if (swapIndividualPadtypes) - { - config.padConfigs[port2][slot2].type = config.padConfigs[port1][slot1].type; - config.padConfigs[port1][slot1].type = padCfgTemp.type; - } - for (int i = 0; i < dm->numDevices; i++) - { - if (dm->devices[i]->type == IGNORE) - continue; - if (swapIndividualPadtypes) - { - PadBindings bindings = dm->devices[i]->pads[port1][slot1][padtype1]; - dm->devices[i]->pads[port1][slot1][padtype1] = dm->devices[i]->pads[port2][slot2][padtype2]; - dm->devices[i]->pads[port2][slot2][padtype2] = bindings; - } - else - { - for (int padtype = 0; padtype < numPadTypes; padtype++) - { - PadBindings bindings = dm->devices[i]->pads[port1][slot1][padtype]; - dm->devices[i]->pads[port1][slot1][padtype] = dm->devices[i]->pads[port2][slot2][padtype]; - dm->devices[i]->pads[port2][slot2][padtype] = bindings; - } - } - } - } - else - { - if (res == -2) - { - swapIndividualPadtypes = true; - } - for (int i = 0; i < dm->numDevices; i++) - { - if (dm->devices[i]->type == IGNORE) - continue; - if (swapIndividualPadtypes) - { - free(dm->devices[i]->pads[port1][slot1][padtype1].bindings); - for (int j = 0; j < dm->devices[i]->pads[port1][slot1][padtype1].numFFBindings; j++) - { - free(dm->devices[i]->pads[port1][slot1][padtype1].ffBindings[j].axes); - } - free(dm->devices[i]->pads[port1][slot1][padtype1].ffBindings); - memset(&dm->devices[i]->pads[port1][slot1][padtype1], 0, sizeof(dm->devices[i]->pads[port1][slot1][padtype1])); - } - else - { - for (int padtype = 0; padtype < numPadTypes; padtype++) - { - free(dm->devices[i]->pads[port1][slot1][padtype].bindings); - for (int j = 0; j < dm->devices[i]->pads[port1][slot1][padtype].numFFBindings; j++) - { - free(dm->devices[i]->pads[port1][slot1][padtype].ffBindings[j].axes); - } - free(dm->devices[i]->pads[port1][slot1][padtype].ffBindings); - memset(&dm->devices[i]->pads[port1][slot1][padtype], 0, sizeof(dm->devices[i]->pads[port1][slot1][padtype])); - } - } - } - } - UpdatePadPages(); - UpdatePadList(hWnd); - PropSheet_Changed(hWndProp, hWnd); - } - } - } - break; - default: - break; - } - return 0; -} - -int CALLBACK PropSheetProc(HWND hWnd, UINT msg, LPARAM lParam) -{ - if (hWnd) - hWndProp = hWnd; - return 0; -} - -void Configure() -{ - // Can end up here without PADinit() being called first. - LoadSettings(); - // Can also end up here after running emulator a bit, and possibly - // disabling some devices due to focus changes, or releasing mouse. - RefreshEnabledDevices(0); - - memset(hWnds, 0, sizeof(hWnds)); - - PROPSHEETPAGE psp; - ZeroMemory(&psp, sizeof(psp)); - psp.dwSize = sizeof(psp); - psp.dwFlags = PSP_USETITLE | PSP_PREMATURE; - psp.hInstance = GetModuleHandle(nullptr); - psp.pfnDlgProc = GeneralDialogProc; - psp.pszTitle = L"General"; - psp.pszTemplate = MAKEINTRESOURCE(IDD_GENERAL); - HPROPSHEETPAGE page = CreatePropertySheetPage(&psp); - if (!page) - return; - - PROPSHEETHEADER psh; - ZeroMemory(&psh, sizeof(psh)); - psh.dwFlags = PSH_DEFAULT | PSH_USECALLBACK | PSH_NOCONTEXTHELP; - psh.dwSize = sizeof(PROPSHEETHEADER); - psh.pfnCallback = PropSheetProc; - psh.hwndParent = GetActiveWindow(); - psh.nPages = 1; - psh.phpage = &page; - wchar_t title[200]; - GetNameAndVersionString(title); - wcscat(title, L" Settings"); - psh.pszCaption = title; - PropertySheet(&psh); - LoadSettings(1); - memset(hWnds, 0, sizeof(hWnds)); -} - -void UnloadConfigs() -{ - if (dm) - { - delete dm; - dm = 0; - } -} diff --git a/pcsx2/PAD/Windows/PADConfig.h b/pcsx2/PAD/Windows/PADConfig.h deleted file mode 100644 index c5e3a59f31..0000000000 --- a/pcsx2/PAD/Windows/PADConfig.h +++ /dev/null @@ -1,80 +0,0 @@ -/* PCSX2 - PS2 Emulator for PCs - * Copyright (C) 2002-2020 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 . - */ - -#ifndef CONFIG_H -#define CONFIG_H - -extern const wchar_t* padTypes[numPadTypes]; - -struct PadConfig -{ - PadType type; -}; - -struct GeneralConfig -{ -public: - PadConfig padConfigs[2][4]; - - int deviceSelect[2][4]; - - DeviceAPI keyboardApi; - DeviceAPI mouseApi; - - // Derived value, calculated by GetInput(). - u8 configureOnBind; - bool bind; - - bool specialInputs[2][4]; - - union - { - struct - { - u8 mouseUnfocus; - u8 background; - u8 multipleBinding; - - struct - { - u8 directInput; - u8 xInput; - u8 dualShock3; - } gameApis; - - u8 multitap[2]; - - u8 debug; - - u8 GH2; - }; - u8 bools[15]; - }; - - wchar_t lastSaveConfigPath[MAX_PATH + 1]; - wchar_t lastSaveConfigFileName[MAX_PATH + 1]; -}; - -extern GeneralConfig config; - -void UnloadConfigs(); - -int LoadSettings(int force = 0, wchar_t* file = 0); - -// Refreshes the set of enabled devices. -void RefreshEnabledDevices(int updateDeviceList = 0); - -void Configure(); -#endif diff --git a/pcsx2/PAD/Windows/PADRawInput.cpp b/pcsx2/PAD/Windows/PADRawInput.cpp deleted file mode 100644 index 62a57d100e..0000000000 --- a/pcsx2/PAD/Windows/PADRawInput.cpp +++ /dev/null @@ -1,367 +0,0 @@ -/* PCSX2 - PS2 Emulator for PCs - * Copyright (C) 2002-2020 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 . - */ - -#include "PrecompiledHeader.h" -#include "Global.h" -#include "InputManager.h" -#include "WindowsMessaging.h" -#include "VKey.h" -#include "DeviceEnumerator.h" -#include "WndProcEater.h" -#include "WindowsKeyboard.h" -#include "WindowsMouse.h" - -ExtraWndProcResult RawInputWndProc(HWND hWnd, UINT uMsg, WPARAM wParam, LPARAM lParam, LRESULT* output); - -int GetRawKeyboards(HWND hWnd) -{ - RAWINPUTDEVICE Rid; - Rid.hwndTarget = hWnd; - - Rid.dwFlags = 0; - Rid.usUsagePage = 0x01; - Rid.usUsage = 0x06; - return RegisterRawInputDevices(&Rid, 1, sizeof(Rid)); -} - -void ReleaseRawKeyboards() -{ - RAWINPUTDEVICE Rid; - Rid.hwndTarget = 0; - - Rid.dwFlags = RIDEV_REMOVE; - Rid.usUsagePage = 0x01; - Rid.usUsage = 0x06; - RegisterRawInputDevices(&Rid, 1, sizeof(Rid)); -} - -int GetRawMice(HWND hWnd) -{ - RAWINPUTDEVICE Rid; - Rid.hwndTarget = hWnd; - - Rid.dwFlags = RIDEV_NOLEGACY | RIDEV_CAPTUREMOUSE; - Rid.usUsagePage = 0x01; - Rid.usUsage = 0x02; - return RegisterRawInputDevices(&Rid, 1, sizeof(Rid)); -} - -void ReleaseRawMice() -{ - RAWINPUTDEVICE Rid; - Rid.hwndTarget = 0; - - Rid.dwFlags = RIDEV_REMOVE; - Rid.usUsagePage = 0x01; - Rid.usUsage = 0x02; - RegisterRawInputDevices(&Rid, 1, sizeof(Rid)); -} - -// Count of active raw keyboard devices. -// when it gets to 0, release them all. -static int rawKeyboardActivatedCount = 0; -// Same for mice. -static int rawMouseActivatedCount = 0; - -class RawInputKeyboard : public WindowsKeyboard -{ -public: - HANDLE hDevice; - - RawInputKeyboard(HANDLE hDevice, wchar_t* name, wchar_t* instanceID = 0) - : WindowsKeyboard(RAW, name, instanceID) - { - this->hDevice = hDevice; - } - - int Activate(InitInfo* initInfo) - { - Deactivate(); - - hWndProc = initInfo->hWndProc; - - active = 1; - if (!rawKeyboardActivatedCount++) - { - if (!rawMouseActivatedCount) - hWndProc->Eat(RawInputWndProc, EATPROC_NO_UPDATE_WHILE_UPDATING_DEVICES); - - if (!GetRawKeyboards(hWndProc->hWndEaten)) - { - Deactivate(); - return 0; - } - } - - InitState(); - return 1; - } - - void Deactivate() - { - FreeState(); - if (active) - { - active = 0; - rawKeyboardActivatedCount--; - if (!rawKeyboardActivatedCount) - { - ReleaseRawKeyboards(); - if (!rawMouseActivatedCount) - hWndProc->ReleaseExtraProc(RawInputWndProc); - } - } - } -}; - -class RawInputMouse : public WindowsMouse -{ -public: - HANDLE hDevice; - - RawInputMouse(HANDLE hDevice, wchar_t* name, wchar_t* instanceID = 0, wchar_t* productID = 0) - : WindowsMouse(RAW, 0, name, instanceID, productID) - { - this->hDevice = hDevice; - } - - int Activate(InitInfo* initInfo) - { - Deactivate(); - - hWndProc = initInfo->hWndProc; - - active = 1; - - // Have to be careful with order. At worst, one unmatched call to ReleaseRawMice on - // EatWndProc fail. In all other cases, no unmatched initialization/cleanup - // lines. - if (!rawMouseActivatedCount++) - { - GetMouseCapture(hWndProc->hWndEaten); - if (!rawKeyboardActivatedCount) - hWndProc->Eat(RawInputWndProc, EATPROC_NO_UPDATE_WHILE_UPDATING_DEVICES); - - if (!GetRawMice(hWndProc->hWndEaten)) - { - Deactivate(); - return 0; - } - } - - AllocState(); - return 1; - } - - void Deactivate() - { - FreeState(); - if (active) - { - active = 0; - rawMouseActivatedCount--; - if (!rawMouseActivatedCount) - { - ReleaseRawMice(); - ReleaseMouseCapture(); - if (!rawKeyboardActivatedCount) - { - hWndProc->ReleaseExtraProc(RawInputWndProc); - } - } - } - } -}; - -ExtraWndProcResult RawInputWndProc(HWND hWnd, UINT uMsg, WPARAM wParam, LPARAM lParam, LRESULT* output) -{ - if (uMsg == WM_INPUT) - { - if (GET_RAWINPUT_CODE_WPARAM(wParam) == RIM_INPUT) - { - RAWINPUT in; - unsigned int size = sizeof(RAWINPUT); - if (GetRawInputData((HRAWINPUT)lParam, RID_INPUT, &in, &size, sizeof(RAWINPUTHEADER)) > 0) - { - for (int i = 0; i < dm->numDevices; i++) - { - Device* dev = dm->devices[i]; - if (dev->api != RAW || !dev->active) - continue; - if (in.header.dwType == RIM_TYPEKEYBOARD && dev->type == KEYBOARD) - { - RawInputKeyboard* rik = (RawInputKeyboard*)dev; - if (rik->hDevice != in.header.hDevice) - continue; - - u32 uMsg = in.data.keyboard.Message; - if (!(in.data.keyboard.VKey >> 8)) - rik->UpdateKey((u8)in.data.keyboard.VKey, (uMsg == WM_KEYDOWN || uMsg == WM_SYSKEYDOWN)); - } - else if (in.header.dwType == RIM_TYPEMOUSE && dev->type == MOUSE) - { - RawInputMouse* rim = (RawInputMouse*)dev; - if (rim->hDevice != in.header.hDevice) - continue; - if (in.data.mouse.usFlags) - { - // Never been set for me, and specs on what most of them - // actually mean is sorely lacking. Also, specs erroneously - // indicate MOUSE_MOVE_RELATIVE is a flag, when it's really - // 0... - continue; - } - - unsigned short buttons = in.data.mouse.usButtonFlags & 0x3FF; - int button = 0; - while (buttons) - { - if (buttons & 3) - { - // 2 is up, 1 is down. Up takes precedence over down. - rim->UpdateButton(button, !(buttons & 2)); - } - button++; - buttons >>= 2; - } - if (in.data.mouse.usButtonFlags & RI_MOUSE_WHEEL) - { - rim->UpdateAxis(2, ((short)in.data.mouse.usButtonData) / WHEEL_DELTA); - } - if (in.data.mouse.lLastX || in.data.mouse.lLastY) - { - rim->UpdateAxis(0, in.data.mouse.lLastX); - rim->UpdateAxis(1, in.data.mouse.lLastY); - } - } - } - } - } - } - else if (uMsg == WM_ACTIVATE) - { - for (int i = 0; i < dm->numDevices; i++) - { - Device* dev = dm->devices[i]; - if (dev->api != RAW || dev->physicalControlState == 0) - continue; - memset(dev->physicalControlState, 0, sizeof(int) * dev->numPhysicalControls); - } - } - else if (uMsg == WM_SIZE && rawMouseActivatedCount) - { - // Doesn't really matter for raw mice, as I disable legacy stuff, but shouldn't hurt. - WindowsMouse::WindowResized(hWnd); - } - - return CONTINUE_BLISSFULLY; -} - -void EnumRawInputDevices() -{ - int count = 0; - if (GetRawInputDeviceList(0, (unsigned int*)&count, sizeof(RAWINPUTDEVICELIST)) != (UINT)-1 && count > 0) - { - wchar_t* instanceID = (wchar_t*)malloc(41000 * sizeof(wchar_t)); - wchar_t* keyName = instanceID + 11000; - wchar_t* displayName = keyName + 10000; - wchar_t* productID = displayName + 10000; - - RAWINPUTDEVICELIST* list = (RAWINPUTDEVICELIST*)malloc(sizeof(RAWINPUTDEVICELIST) * count); - count = GetRawInputDeviceList(list, (unsigned int*)&count, sizeof(RAWINPUTDEVICELIST)); - - // Not necessary, but reminder that count is -1 on failure. - if (count > 0) - { - int keyboardCount = 1; - int mouseCount = 1; - for (int i = 0; i < count; i++) - { - if (list[i].dwType != RIM_TYPEKEYBOARD && list[i].dwType != RIM_TYPEMOUSE) - continue; - - UINT bufferLen = 10000; - int nameLen = GetRawInputDeviceInfo(list[i].hDevice, RIDI_DEVICENAME, instanceID, &bufferLen); - if (nameLen >= 4) - { - // nameLen includes terminating null. - nameLen--; - - // Strip out GUID parts of instanceID to make it a generic product id, - // and reformat it to point to registry entry containing device description. - wcscpy(productID, instanceID); - wchar_t* temp = 0; - for (int j = 0; j < 3; j++) - { - wchar_t* s = wcschr(productID, '#'); - if (!s) - break; - *s = '\\'; - if (j == 2) - { - *s = 0; - } - if (j == 1) - temp = s; - } - - wsprintfW(keyName, L"SYSTEM\\CurrentControlSet\\Enum%s", productID + 3); - if (temp) - *temp = 0; - int haveDescription = 0; - HKEY hKey; - if (ERROR_SUCCESS == RegOpenKeyExW(HKEY_LOCAL_MACHINE, keyName, 0, KEY_QUERY_VALUE, &hKey)) - { - DWORD type; - DWORD len = 10000 * sizeof(wchar_t); - if (ERROR_SUCCESS == RegQueryValueExW(hKey, L"DeviceDesc", 0, &type, (BYTE*)displayName, &len) && - len && type == REG_SZ) - { - wchar_t* temp2 = wcsrchr(displayName, ';'); - if (!temp2) - temp2 = displayName; - else - temp2++; - // Could do without this, but more effort than it's worth. - wcscpy(keyName, temp2); - haveDescription = 1; - } - RegCloseKey(hKey); - } - if (list[i].dwType == RIM_TYPEKEYBOARD) - { - if (!haveDescription) - wsprintfW(displayName, L"Raw Keyboard %i", keyboardCount++); - else - wsprintfW(displayName, L"Raw KB: %s", keyName); - dm->AddDevice(new RawInputKeyboard(list[i].hDevice, displayName, instanceID)); - } - else if (list[i].dwType == RIM_TYPEMOUSE) - { - if (!haveDescription) - wsprintfW(displayName, L"Raw Mouse %i", mouseCount++); - else - wsprintfW(displayName, L"Raw MS: %s", keyName); - dm->AddDevice(new RawInputMouse(list[i].hDevice, displayName, instanceID, productID)); - } - } - } - } - free(list); - free(instanceID); - dm->AddDevice(new RawInputKeyboard(0, L"Simulated Keyboard")); - dm->AddDevice(new RawInputMouse(0, L"Simulated Mouse")); - } -} diff --git a/pcsx2/PAD/Windows/PADRawInput.h b/pcsx2/PAD/Windows/PADRawInput.h deleted file mode 100644 index 4d325c18ef..0000000000 --- a/pcsx2/PAD/Windows/PADRawInput.h +++ /dev/null @@ -1,18 +0,0 @@ -/* PCSX2 - PS2 Emulator for PCs - * Copyright (C) 2002-2020 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 . - */ - -// Can't enumerate raw devices, can only detect them when -// receiving data from them, so just use the list from before. -void EnumRawInputDevices(); diff --git a/pcsx2/PAD/Windows/Tooltips.cpp b/pcsx2/PAD/Windows/Tooltips.cpp deleted file mode 100644 index 27c13c89ea..0000000000 --- a/pcsx2/PAD/Windows/Tooltips.cpp +++ /dev/null @@ -1,128 +0,0 @@ -/* PCSX2 - PS2 Emulator for PCs - * Copyright (C) 2002-2020 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 . - */ - -#include "PrecompiledHeader.h" -#include "Global.h" -#include "resource_pad.h" - -LPWSTR pad_dialog_message(int ID, bool* updateText) -{ - if (updateText) - *updateText = true; - switch (ID) - { - // General tab - case IDC_M_WM: - case IDC_M_RAW: - return L"Enables mouse inputs to be used as pad controls.\n\n" - L"The mouse needs to be in focus to be used for playing. By default this is not the case as the \"Start without mouse focus\" checkbox is enabled. " - L"Either disable this checkbox or enable/disable the mouse while playing by assigning a key to the \"Mouse\" button on the Pad tabs.\n\n" - L"Note 1: By default PCSX2 uses a double-click by the left mouse button to toggle fullscreen mode, this makes the left mouse button unusable as an input." - L"To disable this option in PCSX2 go to Config > Emulation Settings > GS Window tab, and disable the \"Double-click toggles fullscreen mode\" checkbox.\n\n" - L"Note 2: This does not enable the mouse to function as an in-game mouse in PS2 games that support a USB mouse or lightgun." - L"This requires a USB device."; - case IDC_MOUSE_UNFOCUS: - return L"Enabled: Mouse is unfocused and can be used for emulation and outside it.\n\n" - L"Disabled: Mouse is focused and can be used for emulation."; - case IDC_MULTIPLE_BINDING: - return L"Allows binding multiple PS2 controls to one PC control, and binding conflicting controls on opposing ports and/or slots.\n\n" - L"Also enables swapping different kinds of pad types(for example, between DS2 and Guitar) when right-clicking in the pad list."; - case IDC_PAD_LIST: - return L"Left-click on one of the available pads to enable the pad specific options on the right." - L"These options include being able to select the pad type(DS2, Guitar, etc.) and enabling automatic analog mode for PS1 emulation.\n\n" - L"Right-click to show a pop-up menu that allows for swapping all the settings and bindings, or just the bindings of individual pad types," - L"between the selected pad and one of other active pads, and clearing all the settings and bindings from the selected pad or just the bindings from a selected pad type.\n\n" - L"Note: To allow swapping different kinds of pad types(like DS2 to Guitar), the \"Allow binding multiple PS2 Controls...\" option needs to be enabled as well."; - case IDC_PAD_TYPE: - return L"\"Unplugged\" disables the controller and removes the corresponding pad tab.\n\n" - L"\"Dualshock 2\" emulates the default PS2 controller for use in both PS1 and PS2 games.\n\n" - L"\"Guitar\" emulates a PS2 controller used in the Guitar Hero and Rock Band series of games.\n\n" - L"\"Pop'n Music controller\" emulates a PS2 controller used exclusively in the Japanese Pop'n Music series of games.\n\n" - L"\"PS1 Mouse\" emulates the PlayStation Mouse. This controller can only be used in a number of PS1 games like \"Command & Conquer: Red Alert\" and \"Myst\".\n\n" - L"\"neGcon\" emulates a controller that can be used in a number of PS1 games and PS2 games like the \"Ridge Racer\" and \"Ace Combat\" series."; - case IDC_DIAG_LIST: - return L"Shows a list of currently available input devices.\n\n" - L"Double-click a device in the list or right-click it and select \"Test Device\" to display a continuously updated list of the state of all inputs on the selected device.\n" - L"Use this option to check if all the inputs on a controller function properly.\n\n" - L"Right-click and select \"Refresh\" to update the list of devices in case a recently connected device has not shown up yet."; - case IDC_G_DI: - return L"(Legacy) Enable this if your gamepad doesn't support Xinput.\n\n" - L"Disable for DualShock 4 (PS4 controllers) and probably others."; - case IDC_G_XI: - return L"For Xbox 360/ Xbox One controllers (or devices supporting Xinput).\n\n" - L"If it doesn't support Xinput then running through Steam as a non-Steam game might be required for the controllers to work properly.\n\n" - L"https://gamepad-tester.com/ to test your controller and check if it only says 'Xinput' on top."; - case ID_RESTORE_DEFAULTS: - return L"Restores the default contents of PAD.ini, undoing all settings changes and bindings that have been set up."; - // Pad tabs - case IDC_BINDINGS_LIST: - return L"Shows a list of currently bound inputs of the selected Pad.\n\n" - L"Left-click on one of the bindings in the list to configure it.\n\n" - L"Right-click and select \"Delete Selected\" to remove the selected input from the list.\n\n" - L"Right-click and select \"Clear All\" to remove all the inputs from the list.\n\n" - L"Note: Use Shift/Ctrl + Left-click to select multiple bindings. Changing the displayed configuration will now copy it to all selected bindings."; - case IDC_DEVICE_SELECT: - return L"Select a single device to hide the bindings from other devices in the bindings list, and to only be able to add new bindings for the selected device.\n\n" - L"This can also avoid input conflict issues when one controller is recognized as several devices through different APIs."; - case IDC_CONFIGURE_ON_BIND: - return L"Immediately go to the configuration setup when you create a new binding."; - case ID_MOUSE: - return L"Bind a key that releases or captures the mouse.\n\n" - L"Pressing the assigned button when the mouse is in focus, it releases the mouse from use in-game and makes the cursor visible so it can move/resize the emulator window.\n\n" - L"Alt-tabbing to another application also makes the cursor visible, but focusing the emulation window hides it again.\n\n" - L"Pressing the button when the mouse is out of focus and visible, it captures the mouse so that it can be used as a controller again.\n\n" - L"Note 1: Though the binding appears on the page of a specific pad, pressing the button affects all mice.\n\n" - L"Note 2: By default PCSX2 uses a double-click by the left mouse button to toggle fullscreen mode, this makes the left mouse button unusable as an input." - L"To disable this option in PCSX2 go to Config > Emulation Settings > GS Window tab, and disable the \"Double-click toggles fullscreen mode\" checkbox."; - case ID_ANALOG: - return L"Bind a keys that switches the pad from digital mode to analog mode and vice versa.\n\n" - L"This option is useful when analog mode is enabled in a game that does not support it, as this causes the game to not recognise any input or to not even detect a controller.\n\n" - L"This option can also be used to enable analog mode in games that support, but do not automatically enable analog mode.\n\n" - L"Note: Analog mode enables the analog sticks to function on a DualShock controller, while in digital mode it behaves as an original PlayStation controller.\n\n"; - case ID_TURBO_KEY: - return L"Sets a key to send a TAB press to the emulator, which toggles Turbo mode(200% speed) in PCSX2."; - case ID_EXCLUDE: - return L"Disables an input so it will be ignored when trying to bind another input.\n\n" - L"This is helpful when binding controls for a device with an input that's difficult to center like an accelerator, or just always active like a faulty button or analog stick."; - case ID_LOCK_ALL_INPUT: - return L"Locks the current state of the pad. Any further input is handled normally, but the initial pad state is the locked state instead of a state with no buttons pressed. " - L"Pressing it again releases the old pad state, if the old pad state had any keys pressed. Otherwise, it's released automatically."; - case ID_LOCK_DIRECTION: - return L"Locks the current state of the d-pad and analog sticks. Pressing this when all input is locked unlocks only the pad and sticks." - L"Pressing it again will lock them again, keeping the buttons locked."; - case ID_LOCK_BUTTONS: - return L"Locks the current state of the buttons. Pressing this when all input is locked unlocks only the buttons. " - L"Pressing it again will lock them again, keeping the d-pad and analog sticks locked."; - case IDC_RAPID_FIRE: - return L"Automatically presses/releases the input every other time the button is polled."; - case IDC_FLIP: - return L"Inverts a button or axis, making down up and up down."; - case IDC_SLIDER_DEADZONE: - return L"Decreases or increases the range of an input where no input is recognised.\n\n" - L"Increasing the dead zone requires the input to be pressed harder or moved more before it is applied, decreasing it makes it recognise a softer press or a shorter movement."; - case IDC_SLIDER_SKIP_DEADZONE: - return L"Skips and avoids the dead zone to detect input earlier.\n\n" - L"Note: This is useful when a controller input requires too much movement/pressure before there's a corresponding action in-game."; - case IDC_SLIDER_SENSITIVITY: - return L"Sets how hard an axis or button is pressed.\n\n" - L"Note 1: What the default sensitivity value of \"1.00\" means depends on the device itself. The default is high enough that relative axes (which are primarily used by mice) are generally either considered fully up or down." - L"For absolute axes (and force feedback devices), which are used by most game devices, a value of 1.0 should map the device's extreme values to the extreme values of a stick/pad.\n\n" - L"Note 2: Setting the sensitivity of PC button bindings only really has an effect for PS2 analog sticks or when playing a game with full DS2 pressure sensitivity support."; - default: - if (updateText) - *updateText = false; - return L""; - } -} diff --git a/pcsx2/PAD/Windows/Tooltips.h b/pcsx2/PAD/Windows/Tooltips.h deleted file mode 100644 index a0933a7f57..0000000000 --- a/pcsx2/PAD/Windows/Tooltips.h +++ /dev/null @@ -1,23 +0,0 @@ -/* PCSX2 - PS2 Emulator for PCs - * Copyright (C) 2002-2020 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 . - */ - -#ifndef TOOLTIPS_H -#define TOOLTIPS_H - -#include "Global.h" - -LPWSTR pad_dialog_message(int ID, bool* updateText = false); - -#endif diff --git a/pcsx2/PAD/Windows/VKey.cpp b/pcsx2/PAD/Windows/VKey.cpp deleted file mode 100644 index e05f2ea632..0000000000 --- a/pcsx2/PAD/Windows/VKey.cpp +++ /dev/null @@ -1,111 +0,0 @@ -/* PCSX2 - PS2 Emulator for PCs - * Copyright (C) 2002-2020 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 . - */ - -#include "PrecompiledHeader.h" -#include "Global.h" -#include "VKey.h" - -wchar_t* GetVKStringW(unsigned char vk) -{ - int flag; - static wchar_t t[20]; - switch (vk) - { - case 0x0C: - return L"Clear"; - case 0x13: - return L"Pause"; - - case 0x21: // return "Page Up"; - case 0x22: // return "Page Down"; - case 0x23: // return "End"; - case 0x24: // return "Home"; - case 0x25: // return "Left"; - case 0x26: // return "Up"; - case 0x27: // return "Right"; - case 0x28: // return "Down"; - case 0x2D: // return "Insert"; - case 0x2E: // return "Delete"; - case 0x5B: // return "Left Windows"; - case 0x5C: // return "Right Windows"; - case 0x5D: // return "Application"; - case 0x6F: // return "Num /"; - flag = 1 << 24; - break; - - case 0x29: - return L"Select"; - case 0x2A: - return L"Print"; - case 0x2B: - return L"Execute"; - case 0x2C: - return L"Prnt Scrn"; - case 0x2F: - return L"Help"; - - case 0x6C: - return L"|"; - case 0x90: - return L"Num Lock"; - - case 0xA0: - return L"Left Shift"; - case 0xA1: - return L"Right Shift"; - case 0xA2: - return L"Left Ctrl"; - case 0xA3: - return L"Right Ctrl"; - case 0xA4: - return L"Left Alt"; - case 0xA5: - return L"Right Alt"; - - case 0xA6: - return L"Back"; - case 0xA7: - return L"Forward"; - case 0xA8: - return L"Refresh"; - case 0xA9: - return L"Stop"; - case 0xAA: - return L"Search"; - case 0xAB: - return L"Favorites"; - case 0xAC: - return L"Browser"; - - case 0xFA: - return L"Play"; - case 0xFB: - return L"Zoom"; - default: - flag = 0; - break; - } - int res = MapVirtualKey(vk, MAPVK_VK_TO_VSC); - if (res && GetKeyNameText((res << 16) | flag, t, 20)) - { - // don't trust windows - t[19] = 0; - } - else - { - wsprintfW(t, L"Key %i", vk); - } - return t; -} diff --git a/pcsx2/PAD/Windows/VKey.h b/pcsx2/PAD/Windows/VKey.h deleted file mode 100644 index dc47c9ad4e..0000000000 --- a/pcsx2/PAD/Windows/VKey.h +++ /dev/null @@ -1,17 +0,0 @@ -/* PCSX2 - PS2 Emulator for PCs - * Copyright (C) 2002-2020 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 . - */ - -// Maps virtual key codes to strings. -wchar_t* GetVKStringW(unsigned char vk); diff --git a/pcsx2/PAD/Windows/WindowsKeyboard.cpp b/pcsx2/PAD/Windows/WindowsKeyboard.cpp deleted file mode 100644 index 7a2a606a47..0000000000 --- a/pcsx2/PAD/Windows/WindowsKeyboard.cpp +++ /dev/null @@ -1,77 +0,0 @@ -/* PCSX2 - PS2 Emulator for PCs - * Copyright (C) 2002-2020 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 . - */ - -#include "PrecompiledHeader.h" -#include "Global.h" -#include "InputManager.h" -#include "VKey.h" -#include "WindowsKeyboard.h" -#include "KeyboardQueue.h" - -WindowsKeyboard::WindowsKeyboard(DeviceAPI api, wchar_t* displayName, wchar_t* instanceID, wchar_t* deviceID) - : Device(api, KEYBOARD, displayName, instanceID, deviceID) -{ - for (int i = 0; i < 256; i++) - { - AddPhysicalControl(PSHBTN, i, i); - } -} - -wchar_t* WindowsKeyboard::GetPhysicalControlName(PhysicalControl* control) -{ - int id = control->id; - if (control->type == PSHBTN && id >= 0 && id < 256) - { - wchar_t* w = GetVKStringW(id); - if (w) - return w; - } - return Device::GetPhysicalControlName(control); -} - -void WindowsKeyboard::UpdateKey(int vkey, int state) -{ - if (vkey > 7 && vkey < 256) - { - int newState = state * FULLY_DOWN; - if (newState != physicalControlState[vkey]) - { - // Check for alt-F4 to avoid toggling skip mode incorrectly. - if (vkey != VK_F4 || !(physicalControlState[VK_MENU] || physicalControlState[VK_RMENU] || physicalControlState[VK_LMENU])) - { - HostKeyEvent::Type event = HostKeyEvent::Type::KeyPressed; - if (!newState) - event = HostKeyEvent::Type::KeyReleased; - QueueKeyEvent(vkey, event); - } - } - physicalControlState[vkey] = newState; - } -} - -void WindowsKeyboard::InitState() -{ - AllocState(); - for (int vkey = 5; vkey < 256; vkey++) - { - int value = (unsigned short)(((short)GetAsyncKeyState(vkey)) >> 15); - value += value & 1; - if (vkey == VK_CONTROL || vkey == VK_MENU || vkey == VK_SHIFT) - { - value = 0; - } - physicalControlState[vkey] = value; - } -} diff --git a/pcsx2/PAD/Windows/WindowsKeyboard.h b/pcsx2/PAD/Windows/WindowsKeyboard.h deleted file mode 100644 index 9c87743855..0000000000 --- a/pcsx2/PAD/Windows/WindowsKeyboard.h +++ /dev/null @@ -1,26 +0,0 @@ -/* PCSX2 - PS2 Emulator for PCs - * Copyright (C) 2002-2020 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 . - */ - -// Shared functionality for WM and RAW keyboards. -class WindowsKeyboard : public Device -{ -public: - WindowsKeyboard(DeviceAPI api, wchar_t* displayName, wchar_t* instanceID = 0, wchar_t* deviceID = 0); - wchar_t* GetPhysicalControlName(PhysicalControl* control); - void UpdateKey(int vkey, int state); - // Calls AllocState() and initializes to current keyboard state using - // GetAsyncKeyState(). - void InitState(); -}; diff --git a/pcsx2/PAD/Windows/WindowsMessaging.cpp b/pcsx2/PAD/Windows/WindowsMessaging.cpp deleted file mode 100644 index 8a58faaf3f..0000000000 --- a/pcsx2/PAD/Windows/WindowsMessaging.cpp +++ /dev/null @@ -1,222 +0,0 @@ -/* PCSX2 - PS2 Emulator for PCs - * Copyright (C) 2002-2020 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 . - */ - -#include "PrecompiledHeader.h" -#include "Global.h" -#include "InputManager.h" -#include "WindowsMessaging.h" -#include "VKey.h" -#include "DeviceEnumerator.h" -#include "WndProcEater.h" -#include "WindowsKeyboard.h" -#include "WindowsMouse.h" - -ExtraWndProcResult WindowsMessagingWndProc(HWND hWnd, UINT uMsg, WPARAM wParam, LPARAM lParam, LRESULT* output); - -class WindowsMessagingKeyboard; -class WindowsMessagingMouse; - -static WindowsMessagingKeyboard* wmk = 0; -static WindowsMessagingMouse* wmm = 0; - -class WindowsMessagingKeyboard : public WindowsKeyboard -{ -public: - WindowsMessagingKeyboard() - : WindowsKeyboard(WM, L"WM Keyboard") - { - } - - int Activate(InitInfo* initInfo) - { - // Redundant. Should match the next line. - // Deactivate(); - if (wmk) - wmk->Deactivate(); - - hWndProc = initInfo->hWndProc; - - if (!wmm) - hWndProc->Eat(WindowsMessagingWndProc, EATPROC_NO_UPDATE_WHILE_UPDATING_DEVICES); - - wmk = this; - InitState(); - - active = 1; - return 1; - } - - void Deactivate() - { - if (active) - { - if (!wmm) - hWndProc->ReleaseExtraProc(WindowsMessagingWndProc); - wmk = 0; - active = 0; - FreeState(); - } - } - - - void CheckKey(int vkey) - { - UpdateKey(vkey, 1 & (((unsigned short)GetAsyncKeyState(vkey)) >> 15)); - } -}; - -class WindowsMessagingMouse : public WindowsMouse -{ -public: - WindowsMessagingMouse() - : WindowsMouse(WM, 1, L"WM Mouse") - { - } - - int Activate(InitInfo* initInfo) - { - // Redundant. Should match the next line. - // Deactivate(); - if (wmm) - wmm->Deactivate(); - hWndProc = initInfo->hWndProc; - - if (!wmk) - hWndProc->Eat(WindowsMessagingWndProc, EATPROC_NO_UPDATE_WHILE_UPDATING_DEVICES); - - GetMouseCapture(hWndProc->hWndEaten); - - active = 1; - - wmm = this; - AllocState(); - - return 1; - } - - void Deactivate() - { - if (active) - { - if (!wmk) - hWndProc->ReleaseExtraProc(WindowsMessagingWndProc); - ReleaseMouseCapture(); - wmm = 0; - active = 0; - FreeState(); - } - } -}; - -ExtraWndProcResult WindowsMessagingWndProc(HWND hWnd, UINT uMsg, WPARAM wParam, LPARAM lParam, LRESULT* output) -{ - if (wmk) - { - if (uMsg == WM_KEYDOWN || uMsg == WM_SYSKEYDOWN || uMsg == WM_KEYUP || uMsg == WM_SYSKEYUP) - { - if (wParam == VK_SHIFT) - { - wmk->CheckKey(VK_RSHIFT); - wmk->CheckKey(VK_LSHIFT); - } - else if (wParam == VK_CONTROL) - { - wmk->CheckKey(VK_RCONTROL); - wmk->CheckKey(VK_LCONTROL); - } - else if (wParam == VK_MENU) - { - wmk->CheckKey(VK_RMENU); - wmk->CheckKey(VK_LMENU); - } - else - wmk->UpdateKey(wParam, (uMsg == WM_KEYDOWN || uMsg == WM_SYSKEYDOWN)); - return NO_WND_PROC; - } - // Needed to prevent default handling of keys in some situations. - else if (uMsg == WM_CHAR || uMsg == WM_UNICHAR) - { - return NO_WND_PROC; - } - else if (uMsg == WM_ACTIVATE) - { - // Not really needed, but doesn't hurt. - memset(wmk->physicalControlState, 0, sizeof(int) * wmk->numPhysicalControls); - } - } - if (wmm) - { - if (uMsg == WM_MOUSEMOVE) - { - POINT p; - GetCursorPos(&p); - // Need check to prevent cursor movement cascade. - if (p.x != wmm->center.x || p.y != wmm->center.y) - { - wmm->UpdateAxis(0, p.x - wmm->center.x); - wmm->UpdateAxis(1, p.y - wmm->center.y); - - SetCursorPos(wmm->center.x, wmm->center.y); - } - return NO_WND_PROC; - } - else if (uMsg == WM_LBUTTONDOWN || uMsg == WM_LBUTTONUP) - { - wmm->UpdateButton(0, uMsg == WM_LBUTTONDOWN); - return NO_WND_PROC; - } - else if (uMsg == WM_RBUTTONDOWN || uMsg == WM_RBUTTONUP) - { - wmm->UpdateButton(1, uMsg == WM_RBUTTONDOWN); - return NO_WND_PROC; - } - else if (uMsg == WM_MBUTTONDOWN || uMsg == WM_MBUTTONUP) - { - wmm->UpdateButton(2, uMsg == WM_MBUTTONDOWN); - return NO_WND_PROC; - } - else if (uMsg == WM_XBUTTONDOWN || uMsg == WM_XBUTTONUP) - { - wmm->UpdateButton(3 + ((wParam >> 16) == XBUTTON2), uMsg == WM_XBUTTONDOWN); - return NO_WND_PROC; - } - else if (uMsg == WM_MOUSEWHEEL) - { - wmm->UpdateAxis(2, ((int)wParam >> 16) / WHEEL_DELTA); - return NO_WND_PROC; - } - else if (uMsg == WM_MOUSEHWHEEL) - { - wmm->UpdateAxis(3, ((int)wParam >> 16) / WHEEL_DELTA); - return NO_WND_PROC; - } - else if (uMsg == WM_SIZE && wmm->active) - { - WindowsMouse::WindowResized(hWnd); - } - // Taken care of elsewhere. When binding, killing focus means stop reading input. - // When running PCSX2, I release all mouse and keyboard input elsewhere. - /*else if (uMsg == WM_KILLFOCUS) { - wmm->Deactivate(); - }//*/ - } - return CONTINUE_BLISSFULLY; -} - -void EnumWindowsMessagingDevices() -{ - dm->AddDevice(new WindowsMessagingKeyboard()); - dm->AddDevice(new WindowsMessagingMouse()); -} diff --git a/pcsx2/PAD/Windows/WindowsMessaging.h b/pcsx2/PAD/Windows/WindowsMessaging.h deleted file mode 100644 index e5664dfa46..0000000000 --- a/pcsx2/PAD/Windows/WindowsMessaging.h +++ /dev/null @@ -1,16 +0,0 @@ -/* PCSX2 - PS2 Emulator for PCs - * Copyright (C) 2002-2020 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 . - */ - -void EnumWindowsMessagingDevices(); diff --git a/pcsx2/PAD/Windows/WindowsMouse.cpp b/pcsx2/PAD/Windows/WindowsMouse.cpp deleted file mode 100644 index 1e58058ab1..0000000000 --- a/pcsx2/PAD/Windows/WindowsMouse.cpp +++ /dev/null @@ -1,106 +0,0 @@ -/* PCSX2 - PS2 Emulator for PCs - * Copyright (C) 2002-2020 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 . - */ - -#include "PrecompiledHeader.h" -#include "Global.h" - -#include "InputManager.h" -#include "WindowsMouse.h" - -POINT WindowsMouse::origCursorPos; -POINT WindowsMouse::center; - -WindowsMouse::WindowsMouse(DeviceAPI api, int hWheel, wchar_t* displayName, wchar_t* instanceID, wchar_t* deviceID) - : Device(api, MOUSE, displayName, instanceID, deviceID) -{ - int i; - for (i = 0; i < 5; i++) - { - AddPhysicalControl(PSHBTN, i, i); - } - - for (i = 0; i < 3 + hWheel; i++) - { - AddPhysicalControl(RELAXIS, i + 5, i + 5); - } -} - -wchar_t* WindowsMouse::GetPhysicalControlName(PhysicalControl* control) -{ - if (control->id < 9) - { - wchar_t* names[9] = { - L"L Button", - L"R Button", - L"M Button", - L"Mouse 4", - L"Mouse 5", - L"X Axis", - L"Y Axis", - L"Y Wheel", - L"X Wheel"}; - - return names[control->id]; - } - return Device::GetPhysicalControlName(control); -} - -void WindowsMouse::UpdateButton(unsigned int button, int state) -{ - if (button > 4) - return; - physicalControlState[button] = (state << 16); -} - -void WindowsMouse::UpdateAxis(unsigned int axis, int delta) -{ - if (axis > 3) - return; - // 1 mouse pixel = 1/8th way down. - physicalControlState[5 + axis] += (delta << (16 - 3 * (axis < 2))); -} - -void WindowsMouse::WindowResized(HWND hWnd) -{ - RECT r; - GetWindowRect(hWnd, &r); - ClipCursor(&r); - center.x = (r.left + r.right) / 2; - center.y = (r.top + r.bottom) / 2; - SetCursorPos(center.x, center.y); -} - -void WindowsMouse::GetMouseCapture(HWND hWnd) -{ - SetCapture(hWnd); - ShowCursor(0); - - GetCursorPos(&origCursorPos); - - RECT r; - GetWindowRect(hWnd, &r); - ClipCursor(&r); - center.x = (r.left + r.right) / 2; - center.y = (r.top + r.bottom) / 2; - SetCursorPos(center.x, center.y); -} - -void WindowsMouse::ReleaseMouseCapture() -{ - ClipCursor(0); - ReleaseCapture(); - ShowCursor(1); - SetCursorPos(origCursorPos.x, origCursorPos.y); -} diff --git a/pcsx2/PAD/Windows/WindowsMouse.h b/pcsx2/PAD/Windows/WindowsMouse.h deleted file mode 100644 index 9158b002a0..0000000000 --- a/pcsx2/PAD/Windows/WindowsMouse.h +++ /dev/null @@ -1,40 +0,0 @@ -/* PCSX2 - PS2 Emulator for PCs - * Copyright (C) 2002-2020 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 . - */ - -// Shared functionality for WM and RAW keyboards. -class WindowsMouse : public Device -{ -public: - // Used by GetMouseCapture()/ReleaseMouseCapture() - // Static because can have multiple raw mice active at once, - // and only get/release capture once. - static POINT origCursorPos; - static POINT center; - - static void GetMouseCapture(HWND hWnd); - static void WindowResized(HWND hWnd); - static void ReleaseMouseCapture(); - - // hWheel variable lets me display no horizontal wheel for raw input, just to make it clear - // that it's not supported. - WindowsMouse(DeviceAPI api, int hWheel, wchar_t* displayName, wchar_t* instanceID = 0, wchar_t* deviceID = 0); - wchar_t* GetPhysicalControlName(PhysicalControl* control); - // State is 0 for up, 1 for down. - void UpdateButton(unsigned int button, int state); - // 0/1 are x/y. 2 is vert wheel, 3 is horiz wheel. - // Delta is in my micro units. change of (1<<16) is 1 full unit, with - // the default sensitivity. - void UpdateAxis(unsigned int axis, int delta); -}; diff --git a/pcsx2/PAD/Windows/WndProcEater.cpp b/pcsx2/PAD/Windows/WndProcEater.cpp deleted file mode 100644 index ea4b8ba948..0000000000 --- a/pcsx2/PAD/Windows/WndProcEater.cpp +++ /dev/null @@ -1,158 +0,0 @@ -/* PCSX2 - PS2 Emulator for PCs - * Copyright (C) 2002-2020 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 . - */ - -#include "PrecompiledHeader.h" -#include "Global.h" -#include "WndProcEater.h" - -WndProcEater::WndProcEater() - : hWndEaten(0) - , eatenWndProc(0) - , extraProcs(0) - , numExtraProcs(0) - , hMutex(CreateMutex(0, 0, L"PAD")) -{ -} - -WndProcEater::~WndProcEater() throw() -{ - if (hMutex) - { - ReleaseMutex(hMutex); - CloseHandle(hMutex); - } -} - -void WndProcEater::ReleaseExtraProc(ExtraWndProc proc) -{ - // Probably isn't needed, but just in case... - if (hMutex) - WaitForSingleObject(hMutex, 100); - - for (int i = 0; i < numExtraProcs; i++) - { - if (extraProcs[i].proc == proc) - { - extraProcs[i] = extraProcs[--numExtraProcs]; - break; - } - } - if (!numExtraProcs && eatenWndProc) - { - free(extraProcs); - extraProcs = 0; - // As numExtraProcs is 0, won't cause recursion if called from Release(). - Release(); - } -} - -void WndProcEater::Release() -{ - while (numExtraProcs) - ReleaseExtraProc(extraProcs[0].proc); - if (hWndEaten && IsWindow(hWndEaten)) - { - RemoveProp(hWndEaten, L"PADHaxxor"); - SetWindowLongPtr(hWndEaten, GWLP_WNDPROC, (LONG_PTR)eatenWndProc); - hWndEaten = 0; - eatenWndProc = 0; - } -} - -LRESULT WndProcEater::_OverrideWndProc(HWND hWnd, UINT uMsg, WPARAM wParam, LPARAM lParam) -{ - if (hWnd != hWndEaten) - fprintf(stderr, "Totally mismatched window handles on OverrideWndProc!\n"); - - ExtraWndProcResult res = CONTINUE_BLISSFULLY; - LRESULT out = 0; - // Here because want it for binding, even when no keyboard mode is selected. - if (uMsg == WM_GETDLGCODE) - { - return DLGC_WANTALLKEYS | CallWindowProc(eatenWndProc, hWnd, uMsg, wParam, lParam); - } - - for (int i = 0; i < numExtraProcs; i++) - { - // Note: Second bit of deviceUpdateQueued is only set when I receive a device change - // notification, which is handled in the GS thread in one of the extraProcs, so this - // is all I need to prevent bad things from happening while updating devices. No mutex needed. - // if ((deviceUpdateQueued&2) && (extraProcs[i].flags & EATPROC_NO_UPDATE_WHILE_UPDATING_DEVICES)) continue; - - ExtraWndProcResult res2 = extraProcs[i].proc(hWnd, uMsg, wParam, lParam, &out); - if (res2 != res) - { - if (res2 == CONTINUE_BLISSFULLY_AND_RELEASE_PROC) - { - ReleaseExtraProc(extraProcs[i].proc); - i--; - } - else if (res2 > res) - res = res2; - } - } - - if (res != NO_WND_PROC) - { - if (out == WM_DESTROY) - { - Release(); - } - if (res == CONTINUE_BLISSFULLY) - out = CallWindowProc(eatenWndProc, hWnd, uMsg, wParam, lParam); - else if (res == USE_DEFAULT_WND_PROC) - out = DefWindowProc(hWnd, uMsg, wParam, lParam); - } - return out; -} - -static LRESULT CALLBACK OverrideWndProc(HWND hWnd, UINT uMsg, WPARAM wParam, LPARAM lParam) -{ - WndProcEater* obj = (WndProcEater*)GetProp(hWnd, L"PADHaxxor"); - return (obj == NULL) ? - DefWindowProc(hWnd, uMsg, wParam, lParam) : - obj->_OverrideWndProc(hWnd, uMsg, wParam, lParam); -} - -bool WndProcEater::SetWndHandle(HWND hWnd) -{ - if (hWnd == hWndEaten) - return true; - - Release(); - SetProp(hWnd, L"PADHaxxor", (HANDLE)this); - - eatenWndProc = (WNDPROC)SetWindowLongPtr(hWnd, GWLP_WNDPROC, (LONG_PTR)OverrideWndProc); - hWndEaten = (eatenWndProc) ? hWnd : 0; - - return !!hWndEaten; -} - -void WndProcEater::Eat(ExtraWndProc proc, DWORD flags) -{ - - // check if Subclassing failed to init during SetWndHandle - if (!hWndEaten) - return; - - // Probably isn't needed, but just in case... - if (hMutex) - WaitForSingleObject(hMutex, 100); - - extraProcs = (ExtraWndProcInfo*)realloc(extraProcs, sizeof(ExtraWndProcInfo) * (numExtraProcs + 1)); - extraProcs[numExtraProcs].proc = proc; - extraProcs[numExtraProcs].flags = flags; - numExtraProcs++; -} diff --git a/pcsx2/PAD/Windows/WndProcEater.h b/pcsx2/PAD/Windows/WndProcEater.h deleted file mode 100644 index e14b08e96e..0000000000 --- a/pcsx2/PAD/Windows/WndProcEater.h +++ /dev/null @@ -1,63 +0,0 @@ -/* PCSX2 - PS2 Emulator for PCs - * Copyright (C) 2002-2020 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 .4 - */ - -#include "common/RedtapeWindows.h" - -#define EATPROC_NO_UPDATE_WHILE_UPDATING_DEVICES 1 - -/* Need this to let window be subclassed multiple times but still clean up nicely. - */ -enum ExtraWndProcResult -{ - CONTINUE_BLISSFULLY, - // Calls ReleaseExtraProc without messing up order. - CONTINUE_BLISSFULLY_AND_RELEASE_PROC, - USE_DEFAULT_WND_PROC, - NO_WND_PROC -}; - -typedef ExtraWndProcResult (*ExtraWndProc)(HWND hWnd, UINT uMsg, WPARAM wParam, LPARAM lParam, LRESULT* out); - -struct ExtraWndProcInfo -{ - ExtraWndProc proc; - DWORD flags; -}; - -class WndProcEater -{ -public: - HWND hWndEaten; - WNDPROC eatenWndProc; - ExtraWndProcInfo* extraProcs; - int numExtraProcs; - - HANDLE hMutex; - -public: - WndProcEater(); - virtual ~WndProcEater() throw(); - - bool SetWndHandle(HWND hWnd); - void Eat(ExtraWndProc proc, DWORD flags); - void ReleaseExtraProc(ExtraWndProc proc); - void Release(); - - LRESULT _OverrideWndProc(HWND hWnd, UINT uMsg, WPARAM wParam, LPARAM lParam); -}; - -extern WndProcEater hWndGSProc; -extern WndProcEater hWndTopProc; -extern WndProcEater hWndButtonProc; diff --git a/pcsx2/PAD/Windows/XInputEnum.cpp b/pcsx2/PAD/Windows/XInputEnum.cpp deleted file mode 100644 index e39d1a5e26..0000000000 --- a/pcsx2/PAD/Windows/XInputEnum.cpp +++ /dev/null @@ -1,329 +0,0 @@ -/* PCSX2 - PS2 Emulator for PCs - * Copyright (C) 2002-2020 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 . - */ - -#include "PrecompiledHeader.h" -#include "Global.h" -#include -#include -#include "VKey.h" -#include "InputManager.h" -#include "XInputEnum.h" -#include "PADConfig.h" - -// Extra enum -#define XINPUT_GAMEPAD_GUIDE 0x0400 - -typedef struct -{ - float SCP_UP; - float SCP_RIGHT; - float SCP_DOWN; - float SCP_LEFT; - - float SCP_LX; - float SCP_LY; - - float SCP_L1; - float SCP_L2; - float SCP_L3; - - float SCP_RX; - float SCP_RY; - - float SCP_R1; - float SCP_R2; - float SCP_R3; - - float SCP_T; - float SCP_C; - float SCP_X; - float SCP_S; - - float SCP_SELECT; - float SCP_START; - - float SCP_PS; - -} SCP_EXTN; - - -// This way, I don't require that XInput junk be installed. -typedef void(CALLBACK* _XInputEnable)(BOOL enable); -typedef DWORD(CALLBACK* _XInputGetStateEx)(DWORD dwUserIndex, XINPUT_STATE* pState); -typedef DWORD(CALLBACK* _XInputGetExtended)(DWORD dwUserIndex, SCP_EXTN* pPressure); -typedef DWORD(CALLBACK* _XInputSetState)(DWORD dwUserIndex, XINPUT_VIBRATION* pVibration); - -_XInputEnable pXInputEnable = 0; -_XInputGetStateEx pXInputGetStateEx = 0; -_XInputGetExtended pXInputGetExtended = 0; -_XInputSetState pXInputSetState = 0; -static bool xinputNotInstalled = false; - -static int xInputActiveCount = 0; - -// Completely unncessary, really. -__forceinline int ShortToAxis(int v) -{ - // If positive and at least 1 << 14, increment. - v += (!((v >> 15) & 1)) & ((v >> 14) & 1); - // Just double. - return v * 2; -} - -class XInputDevice : public Device -{ - // Cached last vibration values by pad and motor. - // Need this, as only one value is changed at a time. - int ps2Vibration[2][4][2]; - // Minor optimization - cache last set vibration values - // When there's no change, no need to do anything. - XINPUT_VIBRATION xInputVibration; - -public: - int index; - - XInputDevice(int index, wchar_t* displayName) - : Device(XINPUT, OTHER, displayName) - { - memset(ps2Vibration, 0, sizeof(ps2Vibration)); - memset(&xInputVibration, 0, sizeof(xInputVibration)); - this->index = index; - int i; - for (i = 0; i < 17; i++) - { // Skip empty bit - AddPhysicalControl(PRESSURE_BTN, i + (i > 10), 0); - } - for (; i < 21; i++) - { - AddPhysicalControl(ABSAXIS, i + 2, 0); - } - AddFFAxis(L"Slow Motor", 0); - AddFFAxis(L"Fast Motor", 1); - AddFFEffectType(L"Constant Effect", L"Constant", EFFECT_CONSTANT); - } - - wchar_t* GetPhysicalControlName(PhysicalControl* c) - { - unsigned int i = (unsigned int)(c - physicalControls); - if (i < 21) - { - const static wchar_t* names[] = { - L"D-pad Up", - L"D-pad Down", - L"D-pad Left", - L"D-pad Right", - L"Start", - L"Back", - L"Left Thumb", - L"Right Thumb", - L"Left Shoulder", - L"Right Shoulder", - L"Guide", - L"A", - L"B", - L"X", - L"Y", - L"Left Trigger", - L"Right Trigger", - L"Left Thumb X", - L"Left Thumb Y", - L"Right Thumb X", - L"Right Thumb Y", - }; - - return (wchar_t*)names[i]; - } - return Device::GetPhysicalControlName(c); - } - - int Activate(InitInfo* initInfo) - { - if (active) - Deactivate(); - if (!xInputActiveCount) - { - pXInputEnable(1); - } - xInputActiveCount++; - active = 1; - AllocState(); - return 1; - } - - int Update() - { - if (!active) - return 0; - SCP_EXTN pressure; - if (!pXInputGetExtended || (ERROR_SUCCESS != pXInputGetExtended(index, &pressure))) - { - XINPUT_STATE state; - if (ERROR_SUCCESS != pXInputGetStateEx(index, &state)) - { - Deactivate(); - return 0; - } - - int buttons = state.Gamepad.wButtons; - for (int i = 0; i < 15; i++) - { - physicalControlState[i] = ((buttons >> physicalControls[i].id) & 1) << 16; - } - physicalControlState[15] = (int)(state.Gamepad.bLeftTrigger * 257.005); - physicalControlState[16] = (int)(state.Gamepad.bRightTrigger * 257.005); - physicalControlState[17] = ShortToAxis(state.Gamepad.sThumbLX); - physicalControlState[18] = ShortToAxis(state.Gamepad.sThumbLY); - physicalControlState[19] = ShortToAxis(state.Gamepad.sThumbRX); - physicalControlState[20] = ShortToAxis(state.Gamepad.sThumbRY); - } - else - { - physicalControlState[0] = (int)(pressure.SCP_UP * FULLY_DOWN); - physicalControlState[1] = (int)(pressure.SCP_DOWN * FULLY_DOWN); - physicalControlState[2] = (int)(pressure.SCP_LEFT * FULLY_DOWN); - physicalControlState[3] = (int)(pressure.SCP_RIGHT * FULLY_DOWN); - physicalControlState[4] = (int)(pressure.SCP_START * FULLY_DOWN); - physicalControlState[5] = (int)(pressure.SCP_SELECT * FULLY_DOWN); - physicalControlState[6] = (int)(pressure.SCP_L3 * FULLY_DOWN); - physicalControlState[7] = (int)(pressure.SCP_R3 * FULLY_DOWN); - physicalControlState[8] = (int)(pressure.SCP_L1 * FULLY_DOWN); - physicalControlState[9] = (int)(pressure.SCP_R1 * FULLY_DOWN); - physicalControlState[10] = (int)(pressure.SCP_PS * FULLY_DOWN); - physicalControlState[11] = (int)(pressure.SCP_X * FULLY_DOWN); - physicalControlState[12] = (int)(pressure.SCP_C * FULLY_DOWN); - physicalControlState[13] = (int)(pressure.SCP_S * FULLY_DOWN); - physicalControlState[14] = (int)(pressure.SCP_T * FULLY_DOWN); - physicalControlState[15] = (int)(pressure.SCP_L2 * FULLY_DOWN); - physicalControlState[16] = (int)(pressure.SCP_R2 * FULLY_DOWN); - physicalControlState[17] = (int)(pressure.SCP_LX * FULLY_DOWN); - physicalControlState[18] = (int)(pressure.SCP_LY * FULLY_DOWN); - physicalControlState[19] = (int)(pressure.SCP_RX * FULLY_DOWN); - physicalControlState[20] = (int)(pressure.SCP_RY * FULLY_DOWN); - } - return 1; - } - - void SetEffects(unsigned char port, unsigned int slot, unsigned char motor, unsigned char force) - { - ps2Vibration[port][slot][motor] = force; - int newVibration[2] = {0, 0}; - for (int p = 0; p < 2; p++) - { - for (int s = 0; s < 4; s++) - { - int padtype = config.padConfigs[p][s].type; - for (int i = 0; i < pads[p][s][padtype].numFFBindings; i++) - { - // Technically should also be a *65535/BASE_SENSITIVITY, but that's close enough to 1 for me. - ForceFeedbackBinding* ffb = &pads[p][s][padtype].ffBindings[i]; - newVibration[0] += (int)((ffb->axes[0].force * (__int64)ps2Vibration[p][s][ffb->motor]) / 255); - newVibration[1] += (int)((ffb->axes[1].force * (__int64)ps2Vibration[p][s][ffb->motor]) / 255); - } - } - } - newVibration[0] = abs(newVibration[0]); - if (newVibration[0] > 65535) - { - newVibration[0] = 65535; - } - newVibration[1] = abs(newVibration[1]); - if (newVibration[1] > 65535) - { - newVibration[1] = 65535; - } - if (newVibration[0] || newVibration[1] || newVibration[0] != xInputVibration.wLeftMotorSpeed || newVibration[1] != xInputVibration.wRightMotorSpeed) - { - XINPUT_VIBRATION newv = {(WORD)newVibration[0], (WORD)newVibration[1]}; - if (ERROR_SUCCESS == pXInputSetState(index, &newv)) - { - xInputVibration = newv; - } - } - } - - void SetEffect(ForceFeedbackBinding* binding, unsigned char force) - { - PadBindings pBackup = pads[0][0][0]; - pads[0][0][0].ffBindings = binding; - pads[0][0][0].numFFBindings = 1; - SetEffects(0, 0, binding->motor, 255); - pads[0][0][0] = pBackup; - } - - void Deactivate() - { - memset(&xInputVibration, 0, sizeof(xInputVibration)); - memset(ps2Vibration, 0, sizeof(ps2Vibration)); - pXInputSetState(index, &xInputVibration); - - FreeState(); - if (active) - { - if (!--xInputActiveCount) - { - pXInputEnable(0); - } - active = 0; - } - } - - ~XInputDevice() - { - } -}; - -void EnumXInputDevices() -{ - wchar_t temp[30]; - if (!pXInputSetState) - { - // XInput not installed, so don't repeatedly try to load it. - if (xinputNotInstalled) - return; - - // Prefer XInput 1.3 since SCP only has an XInput 1.3 wrapper right now. - // Also use LoadLibrary and not LoadLibraryEx for XInput 1.3, since some - // Windows 7 systems have issues with it. - // FIXME: Missing FreeLibrary call. - HMODULE hMod = LoadLibrary(L"xinput1_3.dll"); - if (hMod == nullptr && IsWindows8OrGreater()) - { - hMod = LoadLibraryEx(L"XInput1_4.dll", nullptr, LOAD_LIBRARY_SEARCH_APPLICATION_DIR | LOAD_LIBRARY_SEARCH_SYSTEM32); - } - - if (hMod) - { - if ((pXInputEnable = (_XInputEnable)GetProcAddress(hMod, "XInputEnable")) && - ((pXInputGetStateEx = (_XInputGetStateEx)GetProcAddress(hMod, (LPCSTR)100)) || // Try Ex version first - (pXInputGetStateEx = (_XInputGetStateEx)GetProcAddress(hMod, "XInputGetState")))) - { - pXInputGetExtended = (_XInputGetExtended)GetProcAddress(hMod, "XInputGetExtended"); - pXInputSetState = (_XInputSetState)GetProcAddress(hMod, "XInputSetState"); - } - } - if (!pXInputSetState) - { - xinputNotInstalled = true; - return; - } - } - pXInputEnable(1); - for (int i = 0; i < 4; i++) - { - wsprintfW(temp, L"XInput Pad %i", i); - dm->AddDevice(new XInputDevice(i, temp)); - } - pXInputEnable(0); -} diff --git a/pcsx2/PAD/Windows/XInputEnum.h b/pcsx2/PAD/Windows/XInputEnum.h deleted file mode 100644 index 736b925c77..0000000000 --- a/pcsx2/PAD/Windows/XInputEnum.h +++ /dev/null @@ -1,16 +0,0 @@ -/* PCSX2 - PS2 Emulator for PCs - * Copyright (C) 2002-2020 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 . - */ - -void EnumXInputDevices(); diff --git a/pcsx2/PAD/Windows/resource_pad.h b/pcsx2/PAD/Windows/resource_pad.h deleted file mode 100644 index d51b4347cb..0000000000 --- a/pcsx2/PAD/Windows/resource_pad.h +++ /dev/null @@ -1,155 +0,0 @@ -//{{NO_DEPENDENCIES}} -// fichier Include Microsoft Visual C++. -// Utilis� par PAD.rc -// -// clang-format off -#define IDD_GENERAL 0x366 -#define IDD_CONFIG 0x369 -#define IDD_CONFIG_GUITAR 0x36A -#define IDD_PROPPAGE_LARGE 0x36B -#define IDC_CURSOR1 0x36C -#define IDD_DIAG 0x36D -#define IDR_INI1 0x36E -#define IDD_CONFIG_POPN 0x36F -#define IDD_CONFIG_PS1_MOUSE 0x370 -#define IDD_CONFIG_NEGCON 0x371 -#define IDC_KB_DISABLE 0x344C -#define IDC_KB_WM 0x344E -#define IDC_KB_RAW 0x344F -#define IDC_DISABLE_PAD1 0x3450 -#define IDC_M_DISABLE 0x3451 -#define IDC_M_WM 0x3453 -#define IDC_M_RAW 0x3454 -#define IDC_G_XI 0x3455 -#define IDC_G_DI 0x3456 -#define IDC_G_DS3 0x3457 -#define IDC_DEBUG_FILE 0x3458 -#define IDC_GUITAR1 0x3459 -#define IDC_MULTITAP1 0x345A -#define IDC_MOUSE_UNFOCUS 0x345B -#define IDC_AXIS_BUTTONS 0x345C -#define IDC_MULTITAP2 0x345D -#define IDC_BACKGROUND 0x345E -#define IDC_MULTIPLE_BINDING 0x345F -#define IDC_GH2_HACK 0x3461 -#define IDC_PAD_LIST 0x3462 -#define IDC_COMBO1 0x3463 -#define IDC_PAD_TYPE 0x3464 -#define IDC_DIAG_LIST 0x3465 -#define ID_SAVE 0x3466 -#define ID_LOAD 0x3467 -#define ID_RESTORE_DEFAULTS 0x3468 -#define IDC_BINDINGS_LIST 0x3F00 -#define IDC_SLIDER_SENSITIVITY 0x4000 -#define IDC_FLIP 0x4001 -#define IDC_AXIS_DIRECTION 0x4002 -#define IDC_AXIS_CONTROL 0x4003 -#define IDC_AXIS_SENSITIVITY 0x4004 -#define IDC_RAPID_FIRE 0x4005 -#define IDC_AXIS_DEVICE 0x4006 -#define ID_SENSITIVITY 0x4007 -#define IDC_SLIDER_DEADZONE 0x4008 -#define IDC_AXIS_DEADZONE 0x4009 -#define IDC_LABEL_SENSITIVITY 0x400A -#define IDC_LABEL_DEADZONE 0x400B -#define IDC_LABEL_SKIP_DEADZONE 0x400C -#define IDC_SKIP_DEADZONE_OFF 0x400D -#define IDC_SLIDER_SKIP_DEADZONE 0x400E -#define IDC_AXIS_SKIP_DEADZONE 0x400F -#define IDC_DPAD 0x40D0 -#define IDC_LSTICK 0x40D1 -#define IDC_RSTICK 0x40D2 -#define IDC_FACE 0x40D3 -#define IDC_SHOULDER 0x40D4 -#define IDC_FACE_ANALOG 0x40D5 -#define ID_MOUSE 0x40FF -#define ID_SELECT 0x4100 -#define ID_L3 0x4101 -#define ID_R3 0x4102 -#define ID_START 0x4103 -#define ID_DPAD_UP 0x4104 -#define ID_DPAD_RIGHT 0x4105 -#define ID_DPAD_DOWN 0x4106 -#define ID_DPAD_LEFT 0x4107 -#define ID_L2 0x4108 -#define ID_R2 0x4109 -#define ID_L1 0x410A -#define ID_R1 0x410B -#define ID_TRIANGLE 0x410C -#define ID_CIRCLE 0x410D -#define ID_CROSS 0x410E -#define ID_SQUARE 0x410F -#define ID_LSTICK_UP 0x4110 -#define ID_LSTICK_RIGHT 0x4111 -#define ID_LSTICK_DOWN 0x4112 -#define ID_LSTICK_LEFT 0x4113 -#define ID_RSTICK_UP 0x4114 -#define ID_RSTICK_RIGHT 0x4115 -#define ID_RSTICK_DOWN 0x4116 -#define ID_RSTICK_LEFT 0x4117 -#define ID_ANALOG 0x4118 -#define ID_EXCLUDE 0x4119 -#define ID_LOCK_BUTTONS 0x411A -#define ID_LOCK_ALL_INPUT 0x411B -#define ID_LOCK_DIRECTION 0x411C -#define ID_TURBO_KEY 0x411D -#define ID_QUICK_SETUP 0x411E -#define IDC_QUICK_SETUP_TEXT 0x411F -#define IDC_FORCEFEEDBACK 0x4200 -#define IDC_FORCEFEEDBACK_FUNCTION 0x4201 -#define ID_BIG_MOTOR 0x4202 -#define ID_SMALL_MOTOR 0x4203 -#define ID_FORCEFEEDBACK_BOX 0x4204 -#define IDC_CONFIGURE_ON_BIND 0x4230 -#define IDC_DEVICE_SELECT 0x4231 -#define ID_CLEAR 0x4232 -#define ID_SPECIAL_INPUTS 0x4233 -#define ID_TEST 0x4300 -#define ID_FF 0x4301 -#define IDC_FF_EFFECT 0x4302 -#define IDC_FF_AXIS1_ENABLED 0x4310 -#define IDC_FF_AXIS1 0x4311 -#define IDC_FF_AXIS1_FLIP 0x4312 -#define IDC_FF_AXIS1_SCALE 0x4313 -#define IDC_FF_AXIS2_ENABLED 0x4320 -#define IDC_FF_AXIS2 0x4321 -#define IDC_FF_AXIS2_FLIP 0x4322 -#define IDC_FF_AXIS2_SCALE 0x4323 -#define IDC_FF_AXIS3_ENABLED 0x4330 -#define IDC_FF_AXIS3 0x4331 -#define IDC_FF_AXIS3_FLIP 0x4332 -#define IDC_FF_AXIS3_SCALE 0x4333 -#define IDC_FF_AXIS4_ENABLED 0x4340 -#define IDC_FF_AXIS4 0x4341 -#define IDC_FF_AXIS4_FLIP 0x4342 -#define IDC_FF_AXIS4_SCALE 0x4343 -#define IDC_FF_AXIS5_ENABLED 0x4350 -#define IDC_FF_AXIS5 0x4351 -#define IDC_FF_AXIS5_FLIP 0x4352 -#define IDC_FF_AXIS5_SCALE 0x4353 -#define IDC_FF_AXIS6_ENABLED 0x4360 -#define IDC_FF_AXIS6 0x4361 -#define IDC_FF_AXIS6_FLIP 0x4362 -#define IDC_FF_AXIS6_SCALE 0x4363 -#define IDC_FF_AXIS7_ENABLED 0x4370 -#define IDC_FF_AXIS7 0x4371 -#define IDC_FF_AXIS7_FLIP 0x4372 -#define IDC_FF_AXIS7_SCALE 0x4373 -#define IDC_FF_AXIS8_ENABLED 0x4380 -#define IDC_FF_AXIS8 0x4381 -#define IDC_FF_AXIS8_FLIP 0x4382 -#define IDC_FF_AXIS8_SCALE 0x4383 -#define ID_CONTROLS 0x4390 -#define ID_RESET_CONFIG 0x4391 - -// Next default values for new objects -// -#ifdef APSTUDIO_INVOKED -#ifndef APSTUDIO_READONLY_SYMBOLS -#define _APS_NEXT_RESOURCE_VALUE 116 -#define _APS_NEXT_COMMAND_VALUE 40001 -#define _APS_NEXT_CONTROL_VALUE 5010 -#define _APS_NEXT_SYMED_VALUE 101 -#endif -#endif -// clang-format on diff --git a/pcsx2/PAD/Windows/usb.h b/pcsx2/PAD/Windows/usb.h deleted file mode 100644 index 09c8ef3a84..0000000000 --- a/pcsx2/PAD/Windows/usb.h +++ /dev/null @@ -1,420 +0,0 @@ -/* PCSX2 - PS2 Emulator for PCs - * Copyright (C) 2002-2020 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 . - */ - -#ifndef __USB_H__ -#define __USB_H__ - -/* - * 'interface' is defined somewhere in the Windows header files. This macro - * is deleted here to avoid conflicts and compile errors. - */ - -#ifdef interface -#undef interface -#endif - -/* - * PATH_MAX from limits.h can't be used on Windows if the dll and - * import libraries are build/used by different compilers - */ - -#define LIBUSB_PATH_MAX 512 - - -/* - * USB spec information - * - * This is all stuff grabbed from various USB specs and is pretty much - * not subject to change - */ - -/* - * Device and/or Interface Class codes - */ -#define USB_CLASS_PER_INTERFACE 0 /* for DeviceClass */ -#define USB_CLASS_AUDIO 1 -#define USB_CLASS_COMM 2 -#define USB_CLASS_HID 3 -#define USB_CLASS_PRINTER 7 -#define USB_CLASS_MASS_STORAGE 8 -#define USB_CLASS_HUB 9 -#define USB_CLASS_DATA 10 -#define USB_CLASS_VENDOR_SPEC 0xff - -/* - * Descriptor types - */ -#define USB_DT_DEVICE 0x01 -#define USB_DT_CONFIG 0x02 -#define USB_DT_STRING 0x03 -#define USB_DT_INTERFACE 0x04 -#define USB_DT_ENDPOINT 0x05 - -#define USB_DT_HID 0x21 -#define USB_DT_REPORT 0x22 -#define USB_DT_PHYSICAL 0x23 -#define USB_DT_HUB 0x29 - -/* - * Descriptor sizes per descriptor type - */ -#define USB_DT_DEVICE_SIZE 18 -#define USB_DT_CONFIG_SIZE 9 -#define USB_DT_INTERFACE_SIZE 9 -#define USB_DT_ENDPOINT_SIZE 7 -#define USB_DT_ENDPOINT_AUDIO_SIZE 9 /* Audio extension */ -#define USB_DT_HUB_NONVAR_SIZE 7 - - -/* ensure byte-packed structures */ -#include - - -/* All standard descriptors have these 2 fields in common */ -struct usb_descriptor_header -{ - unsigned char bLength; - unsigned char bDescriptorType; -}; - -/* String descriptor */ -struct usb_string_descriptor -{ - unsigned char bLength; - unsigned char bDescriptorType; - unsigned short wData[1]; -}; - -/* HID descriptor */ -struct usb_hid_descriptor -{ - unsigned char bLength; - unsigned char bDescriptorType; - unsigned short bcdHID; - unsigned char bCountryCode; - unsigned char bNumDescriptors; -}; - -/* Endpoint descriptor */ -#define USB_MAXENDPOINTS 32 -struct usb_endpoint_descriptor -{ - unsigned char bLength; - unsigned char bDescriptorType; - unsigned char bEndpointAddress; - unsigned char bmAttributes; - unsigned short wMaxPacketSize; - unsigned char bInterval; - unsigned char bRefresh; - unsigned char bSynchAddress; - - unsigned char* extra; /* Extra descriptors */ - int extralen; -}; - -#define USB_ENDPOINT_ADDRESS_MASK 0x0f /* in bEndpointAddress */ -#define USB_ENDPOINT_DIR_MASK 0x80 - -#define USB_ENDPOINT_TYPE_MASK 0x03 /* in bmAttributes */ -#define USB_ENDPOINT_TYPE_CONTROL 0 -#define USB_ENDPOINT_TYPE_ISOCHRONOUS 1 -#define USB_ENDPOINT_TYPE_BULK 2 -#define USB_ENDPOINT_TYPE_INTERRUPT 3 - -/* Interface descriptor */ -#define USB_MAXINTERFACES 32 -struct usb_interface_descriptor -{ - unsigned char bLength; - unsigned char bDescriptorType; - unsigned char bInterfaceNumber; - unsigned char bAlternateSetting; - unsigned char bNumEndpoints; - unsigned char bInterfaceClass; - unsigned char bInterfaceSubClass; - unsigned char bInterfaceProtocol; - unsigned char iInterface; - - struct usb_endpoint_descriptor* endpoint; - - unsigned char* extra; /* Extra descriptors */ - int extralen; -}; - -#define USB_MAXALTSETTING 128 /* Hard limit */ - -struct usb_interface -{ - struct usb_interface_descriptor* altsetting; - - int num_altsetting; -}; - -/* Configuration descriptor information.. */ -#define USB_MAXCONFIG 8 -struct usb_config_descriptor -{ - unsigned char bLength; - unsigned char bDescriptorType; - unsigned short wTotalLength; - unsigned char bNumInterfaces; - unsigned char bConfigurationValue; - unsigned char iConfiguration; - unsigned char bmAttributes; - unsigned char MaxPower; - - struct usb_interface* interface; - - unsigned char* extra; /* Extra descriptors */ - int extralen; -}; - -/* Device descriptor */ -struct usb_device_descriptor -{ - unsigned char bLength; - unsigned char bDescriptorType; - unsigned short bcdUSB; - unsigned char bDeviceClass; - unsigned char bDeviceSubClass; - unsigned char bDeviceProtocol; - unsigned char bMaxPacketSize0; - unsigned short idVendor; - unsigned short idProduct; - unsigned short bcdDevice; - unsigned char iManufacturer; - unsigned char iProduct; - unsigned char iSerialNumber; - unsigned char bNumConfigurations; -}; - -struct usb_ctrl_setup -{ - unsigned char bRequestType; - unsigned char bRequest; - unsigned short wValue; - unsigned short wIndex; - unsigned short wLength; -}; - -/* - * Standard requests - */ -#define USB_REQ_GET_STATUS 0x00 -#define USB_REQ_CLEAR_FEATURE 0x01 -/* 0x02 is reserved */ -#define USB_REQ_SET_FEATURE 0x03 -/* 0x04 is reserved */ -#define USB_REQ_SET_ADDRESS 0x05 -#define USB_REQ_GET_DESCRIPTOR 0x06 -#define USB_REQ_SET_DESCRIPTOR 0x07 -#define USB_REQ_GET_CONFIGURATION 0x08 -#define USB_REQ_SET_CONFIGURATION 0x09 -#define USB_REQ_GET_INTERFACE 0x0A -#define USB_REQ_SET_INTERFACE 0x0B -#define USB_REQ_SYNCH_FRAME 0x0C - -#define USB_TYPE_STANDARD (0x00 << 5) -#define USB_TYPE_CLASS (0x01 << 5) -#define USB_TYPE_VENDOR (0x02 << 5) -#define USB_TYPE_RESERVED (0x03 << 5) - -#define USB_RECIP_DEVICE 0x00 -#define USB_RECIP_INTERFACE 0x01 -#define USB_RECIP_ENDPOINT 0x02 -#define USB_RECIP_OTHER 0x03 - -/* - * Various libusb API related stuff - */ - -#define USB_ENDPOINT_IN 0x80 -#define USB_ENDPOINT_OUT 0x00 - -/* Error codes */ -#define USB_ERROR_BEGIN 500000 - -/* - * This is supposed to look weird. This file is generated from autoconf - * and I didn't want to make this too complicated. - */ -#define USB_LE16_TO_CPU(x) - -/* Data types */ -/* struct usb_device; */ -/* struct usb_bus; */ - -struct usb_device -{ - struct usb_device *next, *prev; - - char filename[LIBUSB_PATH_MAX]; - - struct usb_bus* bus; - - struct usb_device_descriptor descriptor; - struct usb_config_descriptor* config; - - void* dev; /* Darwin support */ - - unsigned char devnum; - - unsigned char num_children; - struct usb_device** children; -}; - -struct usb_bus -{ - struct usb_bus *next, *prev; - - char dirname[LIBUSB_PATH_MAX]; - - struct usb_device* devices; - unsigned long location; - - struct usb_device* root_dev; -}; - -/* Version information, Windows specific */ -struct usb_version -{ - struct - { - int major; - int minor; - int micro; - int nano; - } dll; - struct - { - int major; - int minor; - int micro; - int nano; - } driver; -}; - - -struct usb_dev_handle; -typedef struct usb_dev_handle usb_dev_handle; - -/* Variables */ -#ifndef __USB_C__ -#define usb_busses usb_get_busses() -#endif - - - -#include - -#ifdef GOAT -#ifdef __cplusplus -extern "C" { -#endif - -/* Function prototypes */ - -/* usb.c */ -usb_dev_handle* usb_open(struct usb_device* dev); -int usb_close(usb_dev_handle* dev); -int usb_get_string(usb_dev_handle* dev, int index, int langid, char* buf, - size_t buflen); -int usb_get_string_simple(usb_dev_handle* dev, int index, char* buf, - size_t buflen); - -/* descriptors.c */ -int usb_get_descriptor_by_endpoint(usb_dev_handle* udev, int ep, - unsigned char type, unsigned char index, - void* buf, int size); -int usb_get_descriptor(usb_dev_handle* udev, unsigned char type, - unsigned char index, void* buf, int size); - -/* .c */ -int usb_bulk_write(usb_dev_handle* dev, int ep, char* bytes, int size, - int timeout); -int usb_bulk_read(usb_dev_handle* dev, int ep, char* bytes, int size, - int timeout); -int usb_interrupt_write(usb_dev_handle* dev, int ep, char* bytes, int size, - int timeout); -int usb_interrupt_read(usb_dev_handle* dev, int ep, char* bytes, int size, - int timeout); -int usb_control_msg(usb_dev_handle* dev, int requesttype, int request, - int value, int index, char* bytes, int size, - int timeout); -int usb_set_configuration(usb_dev_handle* dev, int configuration); -int usb_claim_interface(usb_dev_handle* dev, int interface); -int usb_release_interface(usb_dev_handle* dev, int interface); -int usb_set_altinterface(usb_dev_handle* dev, int alternate); -int usb_resetep(usb_dev_handle* dev, unsigned int ep); -int usb_clear_halt(usb_dev_handle* dev, unsigned int ep); -int usb_reset(usb_dev_handle* dev); - -char* usb_strerror(void); - -void usb_init(void); -void usb_set_debug(int level); -int usb_find_busses(void); -int usb_find_devices(void); -struct usb_device* usb_device(usb_dev_handle* dev); -struct usb_bus* usb_get_busses(void); - - -/* Windows specific functions */ - -#define LIBUSB_HAS_INSTALL_SERVICE_NP 1 -int usb_install_service_np(void); -void CALLBACK usb_install_service_np_rundll(HWND wnd, HINSTANCE instance, - LPSTR cmd_line, int cmd_show); - -#define LIBUSB_HAS_UNINSTALL_SERVICE_NP 1 -int usb_uninstall_service_np(void); -void CALLBACK usb_uninstall_service_np_rundll(HWND wnd, HINSTANCE instance, - LPSTR cmd_line, int cmd_show); - -#define LIBUSB_HAS_INSTALL_DRIVER_NP 1 -int usb_install_driver_np(const char* inf_file); -void CALLBACK usb_install_driver_np_rundll(HWND wnd, HINSTANCE instance, - LPSTR cmd_line, int cmd_show); - -#define LIBUSB_HAS_TOUCH_INF_FILE_NP 1 -int usb_touch_inf_file_np(const char* inf_file); -void CALLBACK usb_touch_inf_file_np_rundll(HWND wnd, HINSTANCE instance, - LPSTR cmd_line, int cmd_show); - -#define LIBUSB_HAS_INSTALL_NEEDS_RESTART_NP 1 -int usb_install_needs_restart_np(void); - -const struct usb_version* usb_get_version(void); - -int usb_isochronous_setup_async(usb_dev_handle* dev, void** context, - unsigned char ep, int pktsize); -int usb_bulk_setup_async(usb_dev_handle* dev, void** context, - unsigned char ep); -int usb_interrupt_setup_async(usb_dev_handle* dev, void** context, - unsigned char ep); - -int usb_submit_async(void* context, char* bytes, int size); -int usb_reap_async(void* context, int timeout); -int usb_reap_async_nocancel(void* context, int timeout); -int usb_cancel_async(void* context); -int usb_free_async(void** context); - - -#ifdef __cplusplus -} -#endif -#endif - -#endif /* __USB_H__ */ diff --git a/pcsx2/gui/Resources/AppIcon16.png b/pcsx2/Resources/AppIcon16.png similarity index 100% rename from pcsx2/gui/Resources/AppIcon16.png rename to pcsx2/Resources/AppIcon16.png diff --git a/pcsx2/gui/Resources/AppIcon32.png b/pcsx2/Resources/AppIcon32.png similarity index 100% rename from pcsx2/gui/Resources/AppIcon32.png rename to pcsx2/Resources/AppIcon32.png diff --git a/pcsx2/gui/Resources/AppIcon64.png b/pcsx2/Resources/AppIcon64.png similarity index 100% rename from pcsx2/gui/Resources/AppIcon64.png rename to pcsx2/Resources/AppIcon64.png diff --git a/pcsx2/gui/Resources/Info.plist.in b/pcsx2/Resources/Info.plist.in similarity index 97% rename from pcsx2/gui/Resources/Info.plist.in rename to pcsx2/Resources/Info.plist.in index 3747df09a5..39afe99d1f 100644 --- a/pcsx2/gui/Resources/Info.plist.in +++ b/pcsx2/Resources/Info.plist.in @@ -39,7 +39,7 @@ ${CMAKE_OSX_DEPLOYMENT_TARGET} NSHighResolutionCapable - LSApplicationCategoryType + LSApplicationCategoryType public.app-category.games diff --git a/pcsx2/gui/Resources/PCSX2.icns b/pcsx2/Resources/PCSX2.icns similarity index 100% rename from pcsx2/gui/Resources/PCSX2.icns rename to pcsx2/Resources/PCSX2.icns diff --git a/pcsx2/SPU2/Linux/CfgHelpers.cpp b/pcsx2/SPU2/Linux/CfgHelpers.cpp deleted file mode 100644 index 5f3bd622c9..0000000000 --- a/pcsx2/SPU2/Linux/CfgHelpers.cpp +++ /dev/null @@ -1,116 +0,0 @@ -/* PCSX2 - PS2 Emulator for PCs - * Copyright (C) 2002-2020 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 . - */ - -#include "PrecompiledHeader.h" -#include "Config.h" -#include "Dialogs.h" -#include "pcsx2/Config.h" -#include "common/Path.h" -#include "gui/StringHelpers.h" -#include - -wxFileConfig* spuConfig = nullptr; -wxString path(L"SPU2.ini"); -bool pathSet = false; - -void initIni() -{ - if (!pathSet) - { - path = StringUtil::UTF8StringToWxString(Path::Combine(EmuFolders::Settings, "SPU2.ini")); - pathSet = true; - } - if (spuConfig == nullptr) - spuConfig = new wxFileConfig(L"", L"", path, L"", wxCONFIG_USE_LOCAL_FILE); -} - -void setIni(const wchar_t* Section) -{ - initIni(); - spuConfig->SetPath(wxString::Format(L"/%s", Section)); -} - -void CfgSetSettingsDir(const char* dir) -{ - FileLog("CfgSetSettingsDir(%s)\n", dir); - path = StringUtil::UTF8StringToWxString(Path::Combine((dir == nullptr) ? std::string("inis") : std::string(dir), "SPU2.ini")); - pathSet = true; -} - -void CfgWriteBool(const wchar_t* Section, const wchar_t* Name, bool Value) -{ - setIni(Section); - spuConfig->Write(Name, Value); -} - -void CfgWriteInt(const wchar_t* Section, const wchar_t* Name, int Value) -{ - setIni(Section); - spuConfig->Write(Name, Value); -} - -void CfgWriteFloat(const wchar_t* Section, const wchar_t* Name, float Value) -{ - setIni(Section); - spuConfig->Write(Name, (double)Value); -} - -void CfgWriteStr(const wchar_t* Section, const wchar_t* Name, const wxString& Data) -{ - setIni(Section); - spuConfig->Write(Name, Data); -} - -bool CfgReadBool(const wchar_t* Section, const wchar_t* Name, bool Default) -{ - bool ret; - - setIni(Section); - spuConfig->Read(Name, &ret, Default); - - return ret; -} - -int CfgReadInt(const wchar_t* Section, const wchar_t* Name, int Default) -{ - int ret; - - setIni(Section); - spuConfig->Read(Name, &ret, Default); - - return ret; -} - -float CfgReadFloat(const wchar_t* Section, const wchar_t* Name, float Default) -{ - double ret; - - setIni(Section); - spuConfig->Read(Name, &ret, (double)Default); - - return (float)ret; -} - -void CfgReadStr(const wchar_t* Section, const wchar_t* Name, wchar_t* Data, int DataSize, const wchar_t* Default) -{ - setIni(Section); - wcscpy(Data, spuConfig->Read(Name, Default).wc_str()); -} - -void CfgReadStr(const wchar_t* Section, const wchar_t* Name, wxString& Data, const wchar_t* Default) -{ - setIni(Section); - Data = spuConfig->Read(Name, Default); -} diff --git a/pcsx2/SPU2/Linux/Config.cpp b/pcsx2/SPU2/Linux/Config.cpp deleted file mode 100644 index afd32ec5f2..0000000000 --- a/pcsx2/SPU2/Linux/Config.cpp +++ /dev/null @@ -1,181 +0,0 @@ -/* PCSX2 - PS2 Emulator for PCs - * Copyright (C) 2002-2021 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 . - */ - -#include "PrecompiledHeader.h" -#include "SPU2/Global.h" -#include "Dialogs.h" -#include "Config.h" -#include "gui/StringHelpers.h" - -#if defined(__unix__) || defined(__APPLE__) -#include -#include -#include "SPU2/wx/wxConfig.h" -#endif - -int AutoDMAPlayRate[2] = {0, 0}; - -// Default settings. - -// MIXING -int Interpolation = 5; -/* values: - 0: No interpolation (uses nearest) - 1. Linear interpolation - 2. Cubic interpolation - 3. Hermite interpolation - 4. Catmull-Rom interpolation - 5. Gaussian interpolation -*/ - -float FinalVolume; // global -bool AdvancedVolumeControl; -float VolumeAdjustFLdb; // Decibels settings, because audiophiles love that. -float VolumeAdjustCdb; -float VolumeAdjustFRdb; -float VolumeAdjustBLdb; -float VolumeAdjustBRdb; -float VolumeAdjustSLdb; -float VolumeAdjustSRdb; -float VolumeAdjustLFEdb; -float VolumeAdjustFL; // Linear coefficients calculated from decibels, -float VolumeAdjustC; -float VolumeAdjustFR; -float VolumeAdjustBL; -float VolumeAdjustBR; -float VolumeAdjustSL; -float VolumeAdjustSR; -float VolumeAdjustLFE; - -bool _visual_debug_enabled = false; // Windows-only feature - -// OUTPUT -u32 OutputModule = 0; -int SndOutLatencyMS = 100; -int SynchMode = 0; // Time Stretch, Async or Disabled. - -int numSpeakers = 0; -int dplLevel = 0; -bool temp_debug_state; - -/*****************************************************************************/ - -void ReadSettings() -{ - // For some reason this can be called before we know what ini file we're writing to. - // Let's not try to read it if that happens. - if (!pathSet) - initIni(); - - Interpolation = CfgReadInt(L"MIXING", L"Interpolation", 5); - FinalVolume = ((float)CfgReadInt(L"MIXING", L"FinalVolume", 100)) / 100; - if (FinalVolume > 2.0f) - FinalVolume = 2.0f; - - AdvancedVolumeControl = CfgReadBool(L"MIXING", L"AdvancedVolumeControl", false); - VolumeAdjustCdb = CfgReadFloat(L"MIXING", L"VolumeAdjustC(dB)", 0); - VolumeAdjustFLdb = CfgReadFloat(L"MIXING", L"VolumeAdjustFL(dB)", 0); - VolumeAdjustFRdb = CfgReadFloat(L"MIXING", L"VolumeAdjustFR(dB)", 0); - VolumeAdjustBLdb = CfgReadFloat(L"MIXING", L"VolumeAdjustBL(dB)", 0); - VolumeAdjustBRdb = CfgReadFloat(L"MIXING", L"VolumeAdjustBR(dB)", 0); - VolumeAdjustSLdb = CfgReadFloat(L"MIXING", L"VolumeAdjustSL(dB)", 0); - VolumeAdjustSRdb = CfgReadFloat(L"MIXING", L"VolumeAdjustSR(dB)", 0); - VolumeAdjustLFEdb = CfgReadFloat(L"MIXING", L"VolumeAdjustLFE(dB)", 0); - VolumeAdjustC = powf(10, VolumeAdjustCdb / 10); - VolumeAdjustFL = powf(10, VolumeAdjustFLdb / 10); - VolumeAdjustFR = powf(10, VolumeAdjustFRdb / 10); - VolumeAdjustBL = powf(10, VolumeAdjustBLdb / 10); - VolumeAdjustBR = powf(10, VolumeAdjustBRdb / 10); - VolumeAdjustSL = powf(10, VolumeAdjustSLdb / 10); - VolumeAdjustSR = powf(10, VolumeAdjustSRdb / 10); - VolumeAdjustLFE = powf(10, VolumeAdjustLFEdb / 10); - -#ifdef SPU2X_CUBEB - const SndOutModule* const defaultModule = CubebOut; -#else - const SndOutModule* const defaultModule = NullOut; -#endif - - wxString temp; - CfgReadStr(L"OUTPUT", L"Output_Module", temp, StringUtil::UTF8StringToWxString(defaultModule->GetIdent()).wc_str()); - OutputModule = FindOutputModuleById(temp.ToUTF8()); // Find the driver index of this module... - - SndOutLatencyMS = CfgReadInt(L"OUTPUT", L"Latency", 100); - SynchMode = CfgReadInt(L"OUTPUT", L"Synch_Mode", 0); - numSpeakers = CfgReadInt(L"OUTPUT", L"SpeakerConfiguration", 0); - - SoundtouchCfg::ReadSettings(); - DebugConfig::ReadSettings(); - - // Sanity Checks - // ------------- - - Clampify(SndOutLatencyMS, LATENCY_MIN, LATENCY_MAX); - - if (mods[OutputModule] == nullptr) - { - Console.Warning("* SPU2: Unknown output module '%s' specified in configuration file.", temp.wc_str()); - Console.Warning("* SPU2: Defaulting to %s (%s).", defaultModule->GetLongName(), defaultModule->GetIdent()); - OutputModule = FindOutputModuleById(defaultModule->GetIdent()); - } - - WriteSettings(); - spuConfig->Flush(); -} - -/*****************************************************************************/ - -void WriteSettings() -{ - if (!pathSet) - { - FileLog("Write called without the path set.\n"); - return; - } - - CfgWriteInt(L"MIXING", L"Interpolation", Interpolation); - CfgWriteInt(L"MIXING", L"FinalVolume", (int)(FinalVolume * 100)); - - CfgWriteBool(L"MIXING", L"AdvancedVolumeControl", AdvancedVolumeControl); - CfgWriteFloat(L"MIXING", L"VolumeAdjustC(dB)", VolumeAdjustCdb); - CfgWriteFloat(L"MIXING", L"VolumeAdjustFL(dB)", VolumeAdjustFLdb); - CfgWriteFloat(L"MIXING", L"VolumeAdjustFR(dB)", VolumeAdjustFRdb); - CfgWriteFloat(L"MIXING", L"VolumeAdjustBL(dB)", VolumeAdjustBLdb); - CfgWriteFloat(L"MIXING", L"VolumeAdjustBR(dB)", VolumeAdjustBRdb); - CfgWriteFloat(L"MIXING", L"VolumeAdjustSL(dB)", VolumeAdjustSLdb); - CfgWriteFloat(L"MIXING", L"VolumeAdjustSR(dB)", VolumeAdjustSRdb); - CfgWriteFloat(L"MIXING", L"VolumeAdjustLFE(dB)", VolumeAdjustLFEdb); - - CfgWriteStr(L"OUTPUT", L"Output_Module", mods[OutputModule]->GetIdent()); - CfgWriteInt(L"OUTPUT", L"Latency", SndOutLatencyMS); - CfgWriteInt(L"OUTPUT", L"Synch_Mode", SynchMode); - CfgWriteInt(L"OUTPUT", L"SpeakerConfiguration", numSpeakers); - - SoundtouchCfg::WriteSettings(); - DebugConfig::WriteSettings(); -} - -void configure() -{ - auto* dialog = new Dialog; - - initIni(); - ReadSettings(); - dialog->Display(); - WriteSettings(); - delete spuConfig; - spuConfig = nullptr; - wxDELETE(dialog); -} diff --git a/pcsx2/SPU2/Linux/Config.h b/pcsx2/SPU2/Linux/Config.h deleted file mode 100644 index 3bac2dcd7d..0000000000 --- a/pcsx2/SPU2/Linux/Config.h +++ /dev/null @@ -1,117 +0,0 @@ -/* PCSX2 - PS2 Emulator for PCs - * Copyright (C) 2002-2021 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 . - */ - -#ifndef CONFIG_H_INCLUDED -#define CONFIG_H_INCLUDED - -#include -#include - -extern bool DebugEnabled; - -extern bool _MsgToConsole; -extern bool _MsgKeyOnOff; -extern bool _MsgVoiceOff; -extern bool _MsgDMA; -extern bool _MsgAutoDMA; -extern bool _MsgOverruns; -extern bool _MsgCache; - -extern bool _AccessLog; -extern bool _DMALog; -extern bool _WaveLog; - -extern bool _CoresDump; -extern bool _MemDump; -extern bool _RegDump; -extern bool _visual_debug_enabled; - -/*static __forceinline bool MsgToConsole() { return _MsgToConsole & DebugEnabled; } - -static __forceinline bool MsgKeyOnOff() { return _MsgKeyOnOff & MsgToConsole(); } -static __forceinline bool MsgVoiceOff() { return _MsgVoiceOff & MsgToConsole(); } -static __forceinline bool MsgDMA() { return _MsgDMA & MsgToConsole(); } -static __forceinline bool MsgAutoDMA() { return _MsgAutoDMA & MsgDMA(); } -static __forceinline bool MsgOverruns() { return _MsgOverruns & MsgToConsole(); } -static __forceinline bool MsgCache() { return _MsgCache & MsgToConsole(); } - -static __forceinline bool AccessLog() { return _AccessLog & DebugEnabled; } -static __forceinline bool DMALog() { return _DMALog & DebugEnabled; } -static __forceinline bool WaveLog() { return _WaveLog & DebugEnabled; } - -static __forceinline bool CoresDump() { return _CoresDump & DebugEnabled; } -static __forceinline bool MemDump() { return _MemDump & DebugEnabled; } -static __forceinline bool RegDump() { return _RegDump & DebugEnabled; }*/ - - -//extern wchar_t AccessLogFileName[255]; -//extern wchar_t WaveLogFileName[255]; -//extern wchar_t DMA4LogFileName[255]; -//extern wchar_t DMA7LogFileName[255]; -//extern wchar_t CoresDumpFileName[255]; -//extern wchar_t MemDumpFileName[255]; -//extern wchar_t RegDumpFileName[255]; - -extern int Interpolation; -extern float FinalVolume; - -extern int AutoDMAPlayRate[2]; - -extern u32 OutputModule; -extern int SndOutLatencyMS; - -extern wchar_t dspPlugin[]; -extern int dspPluginModule; - -extern bool dspPluginEnabled; -extern int SynchMode; - -#ifdef PCSX2_DEVBUILD -const int LATENCY_MAX = 3000; -#else -const int LATENCY_MAX = 200; -#endif - -const int LATENCY_MIN = 3; -const int LATENCY_MIN_TIMESTRETCH = 15; - -namespace SoundtouchCfg -{ - extern const int SequenceLen_Min; - extern const int SequenceLen_Max; - - extern const int SeekWindow_Min; - extern const int SeekWindow_Max; - - extern const int Overlap_Min; - extern const int Overlap_Max; - - extern int SequenceLenMS; - extern int SeekWindowMS; - extern int OverlapMS; - - void ReadSettings(); - void WriteSettings(); -}; // namespace SoundtouchCfg - -void ReadSettings(); -void WriteSettings(); -void DisplayDialog(); - -void configure(); -extern wxFileConfig* spuConfig; -extern bool pathSet; -extern void initIni(); -#endif // CONFIG_H_INCLUDED diff --git a/pcsx2/SPU2/Linux/ConfigDebug.cpp b/pcsx2/SPU2/Linux/ConfigDebug.cpp deleted file mode 100644 index a2fa78da88..0000000000 --- a/pcsx2/SPU2/Linux/ConfigDebug.cpp +++ /dev/null @@ -1,176 +0,0 @@ -/* PCSX2 - PS2 Emulator for PCs - * Copyright (C) 2002-2020 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 . - */ - -#include "PrecompiledHeader.h" -#include "SPU2/Global.h" -#include "Dialogs.h" -#include "Config.h" -#include "pcsx2/Config.h" -#include "gui/StringHelpers.h" -#include "gui/wxDirName.h" -#include "common/FileSystem.h" -#include "common/StringUtil.h" -#include "common/Path.h" - -bool DebugEnabled = false; -bool _MsgToConsole = false; -bool _MsgKeyOnOff = false; -bool _MsgVoiceOff = false; -bool _MsgDMA = false; -bool _MsgAutoDMA = false; -bool _MsgOverruns = false; -bool _MsgCache = false; - -bool _AccessLog = false; -bool _DMALog = false; -bool _WaveLog = false; - -bool _CoresDump = false; -bool _MemDump = false; -bool _RegDump = false; - -// this is set true if PCSX2 invokes the SetLogDir callback, which tells SPU2 to use that over -// the configured crap in the ini file. -static bool LogLocationSetByPcsx2 = false; - -static wxDirName LogsFolder; -static wxDirName DumpsFolder; - -std::string AccessLogFileName; -std::string WaveLogFileName; -std::string DMA4LogFileName; -std::string DMA7LogFileName; - -std::string CoresDumpFileName; -std::string MemDumpFileName; -std::string RegDumpFileName; - -void CfgSetLogDir(const char* dir) -{ - LogsFolder = (dir == nullptr) ? wxString(L"logs") : wxString(dir, wxConvFile); - DumpsFolder = (dir == nullptr) ? wxString(L"logs") : wxString(dir, wxConvFile); - LogLocationSetByPcsx2 = (dir != nullptr); -} - -FILE* OpenBinaryLog(const char* logfile) -{ - return FileSystem::OpenCFile(Path::Combine(EmuFolders::Logs, logfile).c_str(), "wb"); -} - -FILE* OpenLog(const char* logfile) -{ - return FileSystem::OpenCFile(Path::Combine(EmuFolders::Logs, logfile).c_str(), "w"); -} - -FILE* OpenDump(const char* logfile) -{ - return FileSystem::OpenCFile(Path::Combine(EmuFolders::Logs, logfile).c_str(), "w"); -} - -namespace DebugConfig -{ - static const wchar_t* Section = L"DEBUG"; - - static void set_default_filenames() - { - AccessLogFileName = "SPU2Log.txt"; - WaveLogFileName = "SPU2log.wav"; - DMA4LogFileName = "SPU2dma4.dat"; - DMA7LogFileName = "SPU2dma7.dat"; - - CoresDumpFileName = "SPU2Cores.txt"; - MemDumpFileName = "SPU2mem.dat"; - RegDumpFileName = "SPU2regs.dat"; - } - - void ReadSettings() - { - DebugEnabled = CfgReadBool(Section, L"Global_Enable", 0); - _MsgToConsole = CfgReadBool(Section, L"Show_Messages", 0); - _MsgKeyOnOff = CfgReadBool(Section, L"Show_Messages_Key_On_Off", 0); - _MsgVoiceOff = CfgReadBool(Section, L"Show_Messages_Voice_Off", 0); - _MsgDMA = CfgReadBool(Section, L"Show_Messages_DMA_Transfer", 0); - _MsgAutoDMA = CfgReadBool(Section, L"Show_Messages_AutoDMA", 0); - _MsgOverruns = CfgReadBool(Section, L"Show_Messages_Overruns", 0); - _MsgCache = CfgReadBool(Section, L"Show_Messages_CacheStats", 0); - - _AccessLog = CfgReadBool(Section, L"Log_Register_Access", 0); - _DMALog = CfgReadBool(Section, L"Log_DMA_Transfers", 0); - _WaveLog = CfgReadBool(Section, L"Log_WAVE_Output", 0); - - _CoresDump = CfgReadBool(Section, L"Dump_Info", 0); - _MemDump = CfgReadBool(Section, L"Dump_Memory", 0); - _RegDump = CfgReadBool(Section, L"Dump_Regs", 0); - - set_default_filenames(); - - wxString wxAccessLogFileName; - wxString wxWaveLogFileName; - wxString wxDMA4LogFileName; - wxString wxDMA7LogFileName; - wxString wxCoresDumpFileName; - wxString wxMemDumpFileName; - wxString wxRegDumpFileName; - CfgReadStr(Section, L"Access_Log_Filename", wxAccessLogFileName, L"SPU2Log.txt"); - CfgReadStr(Section, L"DMA4Log_Filename", wxDMA4LogFileName, L"SPU2dma4.dat"); - CfgReadStr(Section, L"WaveLog_Filename", wxWaveLogFileName, L"logs/SPU2log.wav"); - CfgReadStr(Section, L"DMA7Log_Filename", wxDMA7LogFileName, L"SPU2dma7.dat"); - - CfgReadStr(Section, L"Info_Dump_Filename", wxCoresDumpFileName, L"SPU2Cores.txt"); - CfgReadStr(Section, L"Mem_Dump_Filename", wxMemDumpFileName, L"SPU2mem.dat"); - CfgReadStr(Section, L"Reg_Dump_Filename", wxRegDumpFileName, L"SPU2regs.dat"); - - AccessLogFileName = StringUtil::wxStringToUTF8String(wxAccessLogFileName); - WaveLogFileName = StringUtil::wxStringToUTF8String(wxAccessLogFileName); - DMA4LogFileName = StringUtil::wxStringToUTF8String(wxDMA4LogFileName); - DMA7LogFileName = StringUtil::wxStringToUTF8String(wxDMA7LogFileName); - CoresDumpFileName = StringUtil::wxStringToUTF8String(wxCoresDumpFileName); - MemDumpFileName = StringUtil::wxStringToUTF8String(wxMemDumpFileName); - RegDumpFileName = StringUtil::wxStringToUTF8String(wxRegDumpFileName); - } - - - void WriteSettings() - { - CfgWriteBool(Section, L"Global_Enable", DebugEnabled); - - CfgWriteBool(Section, L"Show_Messages", _MsgToConsole); - CfgWriteBool(Section, L"Show_Messages_Key_On_Off", _MsgKeyOnOff); - CfgWriteBool(Section, L"Show_Messages_Voice_Off", _MsgVoiceOff); - CfgWriteBool(Section, L"Show_Messages_DMA_Transfer", _MsgDMA); - CfgWriteBool(Section, L"Show_Messages_AutoDMA", _MsgAutoDMA); - CfgWriteBool(Section, L"Show_Messages_Overruns", _MsgOverruns); - CfgWriteBool(Section, L"Show_Messages_CacheStats", _MsgCache); - - CfgWriteBool(Section, L"Log_Register_Access", _AccessLog); - CfgWriteBool(Section, L"Log_DMA_Transfers", _DMALog); - CfgWriteBool(Section, L"Log_WAVE_Output", _WaveLog); - - CfgWriteBool(Section, L"Dump_Info", _CoresDump); - CfgWriteBool(Section, L"Dump_Memory", _MemDump); - CfgWriteBool(Section, L"Dump_Regs", _RegDump); - - set_default_filenames(); - CfgWriteStr(Section, L"Access_Log_Filename", StringUtil::UTF8StringToWxString(AccessLogFileName)); - CfgWriteStr(Section, L"WaveLog_Filename", StringUtil::UTF8StringToWxString(WaveLogFileName)); - CfgWriteStr(Section, L"DMA4Log_Filename", StringUtil::UTF8StringToWxString(DMA4LogFileName)); - CfgWriteStr(Section, L"DMA7Log_Filename", StringUtil::UTF8StringToWxString(DMA7LogFileName)); - - CfgWriteStr(Section, L"Info_Dump_Filename", StringUtil::UTF8StringToWxString(CoresDumpFileName)); - CfgWriteStr(Section, L"Mem_Dump_Filename", StringUtil::UTF8StringToWxString(MemDumpFileName)); - CfgWriteStr(Section, L"Reg_Dump_Filename", StringUtil::UTF8StringToWxString(RegDumpFileName)); - } - -} // namespace DebugConfig diff --git a/pcsx2/SPU2/Linux/ConfigSoundTouch.cpp b/pcsx2/SPU2/Linux/ConfigSoundTouch.cpp deleted file mode 100644 index f9a2e39c0d..0000000000 --- a/pcsx2/SPU2/Linux/ConfigSoundTouch.cpp +++ /dev/null @@ -1,69 +0,0 @@ -/* PCSX2 - PS2 Emulator for PCs - * Copyright (C) 2002-2020 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 . - */ - -#include "PrecompiledHeader.h" -#include "SPU2/Global.h" -#include "Dialogs.h" -#include "Config.h" -#include "SoundTouch.h" - -namespace SoundtouchCfg -{ - // Timestretch Slider Bounds, Min/Max - const int SequenceLen_Min = 20; - const int SequenceLen_Max = 100; - - const int SeekWindow_Min = 10; - const int SeekWindow_Max = 30; - - const int Overlap_Min = 5; - const int Overlap_Max = 15; - - int SequenceLenMS = 30; - int SeekWindowMS = 20; - int OverlapMS = 10; - - static void ClampValues() - { - Clampify(SequenceLenMS, SequenceLen_Min, SequenceLen_Max); - Clampify(SeekWindowMS, SeekWindow_Min, SeekWindow_Max); - Clampify(OverlapMS, Overlap_Min, Overlap_Max); - } - - void ApplySettings(soundtouch::SoundTouch& sndtouch) - { - sndtouch.setSetting(SETTING_SEQUENCE_MS, SequenceLenMS); - sndtouch.setSetting(SETTING_SEEKWINDOW_MS, SeekWindowMS); - sndtouch.setSetting(SETTING_OVERLAP_MS, OverlapMS); - } - - void ReadSettings() - { - SequenceLenMS = CfgReadInt(L"SOUNDTOUCH", L"SequenceLengthMS", 30); - SeekWindowMS = CfgReadInt(L"SOUNDTOUCH", L"SeekWindowMS", 20); - OverlapMS = CfgReadInt(L"SOUNDTOUCH", L"OverlapMS", 10); - - ClampValues(); - WriteSettings(); - } - - void WriteSettings() - { - CfgWriteInt(L"SOUNDTOUCH", L"SequenceLengthMS", SequenceLenMS); - CfgWriteInt(L"SOUNDTOUCH", L"SeekWindowMS", SeekWindowMS); - CfgWriteInt(L"SOUNDTOUCH", L"OverlapMS", OverlapMS); - } - -} // namespace SoundtouchCfg diff --git a/pcsx2/SPU2/Linux/Dialogs.cpp b/pcsx2/SPU2/Linux/Dialogs.cpp deleted file mode 100644 index 945fd786b1..0000000000 --- a/pcsx2/SPU2/Linux/Dialogs.cpp +++ /dev/null @@ -1,61 +0,0 @@ -/* PCSX2 - PS2 Emulator for PCs - * Copyright (C) 2002-2020 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 . - */ - -// To be continued... - -#include "PrecompiledHeader.h" -#include "Dialogs.h" -#include - -#if defined(__unix__) || defined(__APPLE__) -#include - -void SysMessage(const char* fmt, ...) -{ - va_list list; - char msg[512]; - - va_start(list, fmt); - vsprintf(msg, fmt, list); - va_end(list); - - if (msg[strlen(msg) - 1] == '\n') - msg[strlen(msg) - 1] = 0; - - wxMessageDialog dialog(nullptr, msg, "Info", wxOK); - dialog.ShowModal(); -} - -void SysMessage(const wchar_t* fmt, ...) -{ - va_list list; - va_start(list, fmt); - wxString msg; - msg.PrintfV(fmt, list); - va_end(list); - - wxMessageDialog dialog(nullptr, msg, "Info", wxOK); - dialog.ShowModal(); -} -#endif - -void DspUpdate() -{ -} - -s32 DspLoadLibrary(wchar_t* fileName, int modnum) -{ - return 0; -} diff --git a/pcsx2/SPU2/Linux/Dialogs.h b/pcsx2/SPU2/Linux/Dialogs.h deleted file mode 100644 index 3176931116..0000000000 --- a/pcsx2/SPU2/Linux/Dialogs.h +++ /dev/null @@ -1,42 +0,0 @@ -/* PCSX2 - PS2 Emulator for PCs - * Copyright (C) 2002-2020 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 . - */ - -#ifndef DIALOG_H_INCLUDED -#define DIALOG_H_INCLUDED - -#include "SPU2/Global.h" -#include "SPU2/Config.h" - -namespace DebugConfig -{ - extern void ReadSettings(); - extern void WriteSettings(); -} // namespace DebugConfig - -extern void CfgSetSettingsDir(const char* dir); -extern void CfgSetLogDir(const char* dir); - -extern void CfgWriteBool(const wchar_t* Section, const wchar_t* Name, bool Value); -extern void CfgWriteInt(const wchar_t* Section, const wchar_t* Name, int Value); -extern void CfgWriteFloat(const wchar_t* Section, const wchar_t* Name, float Value); -extern void CfgWriteStr(const wchar_t* Section, const wchar_t* Name, const wxString& Data); - -extern bool CfgReadBool(const wchar_t* Section, const wchar_t* Name, bool Default); -extern void CfgReadStr(const wchar_t* Section, const wchar_t* Name, wxString& Data, const wchar_t* Default); -//extern void CfgReadStr(const wchar_t* Section, const wchar_t* Name, wchar_t* Data, int DataSize, const wchar_t* Default); -extern int CfgReadInt(const wchar_t* Section, const wchar_t* Name, int Default); -extern float CfgReadFloat(const wchar_t* Section, const wchar_t* Name, float Default); - -#endif diff --git a/pcsx2/SPU2/Windows/CfgHelpers.cpp b/pcsx2/SPU2/Windows/CfgHelpers.cpp deleted file mode 100644 index 8211ae41b0..0000000000 --- a/pcsx2/SPU2/Windows/CfgHelpers.cpp +++ /dev/null @@ -1,220 +0,0 @@ -/* PCSX2 - PS2 Emulator for PCs - * Copyright (C) 2002-2020 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 . - */ - -#include "PrecompiledHeader.h" -#include "Config.h" -#include "SPU2/Global.h" -#include "Dialogs.h" -#include "common/StringUtil.h" -#include "gui/StringHelpers.h" - -void SysMessage(const char* fmt, ...) -{ - va_list list; - char tmp[512]; - wchar_t wtmp[512]; - - va_start(list, fmt); - vsprintf_s(tmp, fmt, list); - va_end(list); - swprintf_s(wtmp, L"%S", tmp); - - // TODO: Move this into app/host. - MessageBox(NULL, wtmp, L"SPU2 System Message", MB_OK | MB_SETFOREGROUND); -} - -void SysMessage(const wchar_t* fmt, ...) -{ - va_list list; - va_start(list, fmt); - wxString wtmp; - wtmp.PrintfV(fmt, list); - va_end(list); - MessageBox(NULL, wtmp, L"SPU2 System Message", MB_OK | MB_SETFOREGROUND); -} - -////// - -#include "common/Path.h" - -wxString CfgFile(L"SPU2.ini"); -bool pathSet = false; - -void initIni() -{ - if (!pathSet) - { - CfgFile = StringUtil::UTF8StringToWxString(Path::Combine(EmuFolders::Settings, "SPU2.ini")); - pathSet = true; - } -} - -void CfgSetSettingsDir(const char* dir) -{ - initIni(); -} - - -/*| Config File Format: |¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯*\ -+--+---------------------+------------------------+ -| | -| Option=Value | -| | -| | -| Boolean Values: TRUE,YES,1,T,Y mean 'true', | -| everything else means 'false'. | -| | -| All Values are limited to 255 chars. | -| | -+-------------------------------------------------+ -\*_____________________________________________*/ - - -void CfgWriteBool(const TCHAR* Section, const TCHAR* Name, bool Value) -{ - initIni(); - const TCHAR* Data = Value ? L"TRUE" : L"FALSE"; - WritePrivateProfileString(Section, Name, Data, CfgFile); -} - -void CfgWriteInt(const TCHAR* Section, const TCHAR* Name, int Value) -{ - initIni(); - TCHAR Data[32]; - _itow(Value, Data, 10); - WritePrivateProfileString(Section, Name, Data, CfgFile); -} - -void CfgWriteFloat(const TCHAR* Section, const TCHAR* Name, float Value) -{ - initIni(); - TCHAR Data[32]; - _swprintf(Data, L"%f", Value); - WritePrivateProfileString(Section, Name, Data, CfgFile); -} - -/*void CfgWriteStr(const TCHAR* Section, const TCHAR* Name, const TCHAR *Data) -{ -WritePrivateProfileString( Section, Name, Data, CfgFile ); -}*/ - -void CfgWriteStr(const TCHAR* Section, const TCHAR* Name, const wxString& Data) -{ - initIni(); - WritePrivateProfileString(Section, Name, Data, CfgFile); -} - -/*****************************************************************************/ - -bool CfgReadBool(const TCHAR* Section, const TCHAR* Name, bool Default) -{ - initIni(); - TCHAR Data[255] = {0}; - - GetPrivateProfileString(Section, Name, L"", Data, 255, CfgFile); - Data[254] = 0; - if (wcslen(Data) == 0) - { - CfgWriteBool(Section, Name, Default); - return Default; - } - - if (wcscmp(Data, L"1") == 0) - return true; - if (wcscmp(Data, L"Y") == 0) - return true; - if (wcscmp(Data, L"T") == 0) - return true; - if (wcscmp(Data, L"YES") == 0) - return true; - if (wcscmp(Data, L"TRUE") == 0) - return true; - return false; -} - - -int CfgReadInt(const TCHAR* Section, const TCHAR* Name, int Default) -{ - initIni(); - TCHAR Data[255] = {0}; - GetPrivateProfileString(Section, Name, L"", Data, 255, CfgFile); - Data[254] = 0; - - if (wcslen(Data) == 0) - { - CfgWriteInt(Section, Name, Default); - return Default; - } - - return _wtoi(Data); -} - -float CfgReadFloat(const TCHAR* Section, const TCHAR* Name, float Default) -{ - initIni(); - TCHAR Data[255] = {0}; - GetPrivateProfileString(Section, Name, L"", Data, 255, CfgFile); - Data[254] = 0; - - if (wcslen(Data) == 0) - { - CfgWriteFloat(Section, Name, Default); - return Default; - } - - return (float)_wtof(Data); -} - -void CfgReadStr(const TCHAR* Section, const TCHAR* Name, TCHAR* Data, int DataSize, const TCHAR* Default) -{ - initIni(); - GetPrivateProfileString(Section, Name, L"", Data, DataSize, CfgFile); - - if (wcslen(Data) == 0) - { - swprintf_s(Data, DataSize, L"%s", Default); - CfgWriteStr(Section, Name, Data); - } -} - -void CfgReadStr(const TCHAR* Section, const TCHAR* Name, wxString& Data, const TCHAR* Default) -{ - initIni(); - wchar_t workspace[512]; - GetPrivateProfileString(Section, Name, L"", workspace, std::size(workspace), CfgFile); - - Data = workspace; - - if (Data.empty()) - { - Data = Default; - CfgWriteStr(Section, Name, Default); - } -} - -// Tries to read the requested value. -// Returns FALSE if the value isn't found. -bool CfgFindName(const TCHAR* Section, const TCHAR* Name) -{ - initIni(); - // Only load 24 characters. No need to load more. - TCHAR Data[24] = {0}; - GetPrivateProfileString(Section, Name, L"", Data, 24, CfgFile); - Data[23] = 0; - - if (wcslen(Data) == 0) - return false; - return true; -} diff --git a/pcsx2/SPU2/Windows/Config.cpp b/pcsx2/SPU2/Windows/Config.cpp deleted file mode 100644 index 209a9581b9..0000000000 --- a/pcsx2/SPU2/Windows/Config.cpp +++ /dev/null @@ -1,533 +0,0 @@ -/* PCSX2 - PS2 Emulator for PCs - * Copyright (C) 2002-2021 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 . - */ - -#include "PrecompiledHeader.h" -#include "SPU2/Global.h" -#include "Dialogs.h" -#include "common/StringUtil.h" - -#ifdef PCSX2_DEVBUILD -static const int LATENCY_MAX = 3000; -#else -static const int LATENCY_MAX = 200; -#endif - -static const int LATENCY_MIN = 3; -static const int LATENCY_MIN_TS = 15; - -// MIXING -int Interpolation = 5; -/* values: - 0: No interpolation (uses nearest) - 1. Linear interpolation - 2. Cubic interpolation - 3. Hermite interpolation - 4. Catmull-Rom interpolation - 5. Gaussian interpolation -*/ - -float FinalVolume; // Global -bool AdvancedVolumeControl; -float VolumeAdjustFLdb; // Decibels settings, because audiophiles love that. -float VolumeAdjustCdb; -float VolumeAdjustFRdb; -float VolumeAdjustBLdb; -float VolumeAdjustBRdb; -float VolumeAdjustSLdb; -float VolumeAdjustSRdb; -float VolumeAdjustLFEdb; -float VolumeAdjustFL; // Linear coefficients calculated from decibels, -float VolumeAdjustC; -float VolumeAdjustFR; -float VolumeAdjustBL; -float VolumeAdjustBR; -float VolumeAdjustSL; -float VolumeAdjustSR; -float VolumeAdjustLFE; - -// OUTPUT -int SndOutLatencyMS = 100; -int SynchMode = 0; // Time Stretch, Async or Disabled. - -u32 OutputModule = 0; - -CONFIG_XAUDIO2 Config_XAudio2; - -// DSP -bool dspPluginEnabled = false; -int dspPluginModule = 0; -wchar_t dspPlugin[256]; - -int numSpeakers = 0; - -int dplLevel = 0; - -/*****************************************************************************/ - -void ReadSettings() -{ - Interpolation = CfgReadInt(L"MIXING", L"Interpolation", 5); - - FinalVolume = ((float)CfgReadInt(L"MIXING", L"FinalVolume", 100)) / 100; - if (FinalVolume > 2.0f) - FinalVolume = 2.0f; - - AdvancedVolumeControl = CfgReadBool(L"MIXING", L"AdvancedVolumeControl", false); - VolumeAdjustCdb = CfgReadFloat(L"MIXING", L"VolumeAdjustC(dB)", 0); - VolumeAdjustFLdb = CfgReadFloat(L"MIXING", L"VolumeAdjustFL(dB)", 0); - VolumeAdjustFRdb = CfgReadFloat(L"MIXING", L"VolumeAdjustFR(dB)", 0); - VolumeAdjustBLdb = CfgReadFloat(L"MIXING", L"VolumeAdjustBL(dB)", 0); - VolumeAdjustBRdb = CfgReadFloat(L"MIXING", L"VolumeAdjustBR(dB)", 0); - VolumeAdjustSLdb = CfgReadFloat(L"MIXING", L"VolumeAdjustSL(dB)", 0); - VolumeAdjustSRdb = CfgReadFloat(L"MIXING", L"VolumeAdjustSR(dB)", 0); - VolumeAdjustLFEdb = CfgReadFloat(L"MIXING", L"VolumeAdjustLFE(dB)", 0); - VolumeAdjustC = powf(10, VolumeAdjustCdb / 10); - VolumeAdjustFL = powf(10, VolumeAdjustFLdb / 10); - VolumeAdjustFR = powf(10, VolumeAdjustFRdb / 10); - VolumeAdjustBL = powf(10, VolumeAdjustBLdb / 10); - VolumeAdjustBR = powf(10, VolumeAdjustBRdb / 10); - VolumeAdjustSL = powf(10, VolumeAdjustSLdb / 10); - VolumeAdjustSR = powf(10, VolumeAdjustSRdb / 10); - VolumeAdjustLFE = powf(10, VolumeAdjustLFEdb / 10); - - SynchMode = CfgReadInt(L"OUTPUT", L"Synch_Mode", 0); - numSpeakers = CfgReadInt(L"OUTPUT", L"SpeakerConfiguration", 0); - dplLevel = CfgReadInt(L"OUTPUT", L"DplDecodingLevel", 0); - SndOutLatencyMS = CfgReadInt(L"OUTPUT", L"Latency", 100); - - if ((SynchMode == 0) && (SndOutLatencyMS < LATENCY_MIN_TS)) // Can't use low-latency with timestretcher at the moment. - SndOutLatencyMS = LATENCY_MIN_TS; - else if (SndOutLatencyMS < LATENCY_MIN) - SndOutLatencyMS = LATENCY_MIN; - - wchar_t omodid[128]; - - // Portaudio occasionally has issues selecting the proper default audio device. - // Let's use xaudio2 until this is sorted (rama). - - // CfgReadStr(L"OUTPUT", L"Output_Module", omodid, 127, PortaudioOut->GetIdent()); - CfgReadStr(L"OUTPUT", L"Output_Module", omodid, 127, StringUtil::UTF8StringToWideString(CubebOut->GetIdent()).c_str()); - - // Find the driver index of this module: - OutputModule = FindOutputModuleById(StringUtil::WideStringToUTF8String(omodid).c_str()); - - CfgReadStr(L"DSP PLUGIN", L"Filename", dspPlugin, 255, L""); - dspPluginModule = CfgReadInt(L"DSP PLUGIN", L"ModuleNum", 0); - dspPluginEnabled = CfgReadBool(L"DSP PLUGIN", L"Enabled", false); - - SoundtouchCfg::ReadSettings(); - DebugConfig::ReadSettings(); - - // Sanity Checks - // ------------- - - Clampify(SndOutLatencyMS, LATENCY_MIN, LATENCY_MAX); - - if (mods[OutputModule] == nullptr) - { - // Unsupported or legacy module. - Console.Warning("* SPU2: Unknown output module '%s' specified in configuration file.", omodid); - Console.Warning("* SPU2: Defaulting to XAudio (%s).", XAudio2Out->GetIdent()); - OutputModule = FindOutputModuleById(XAudio2Out->GetIdent()); - } -} - -/*****************************************************************************/ - -void WriteSettings() -{ - CfgWriteInt(L"MIXING", L"Interpolation", Interpolation); - - CfgWriteInt(L"MIXING", L"FinalVolume", (int)(FinalVolume * 100)); - - CfgWriteBool(L"MIXING", L"AdvancedVolumeControl", AdvancedVolumeControl); - CfgWriteFloat(L"MIXING", L"VolumeAdjustC(dB)", VolumeAdjustCdb); - CfgWriteFloat(L"MIXING", L"VolumeAdjustFL(dB)", VolumeAdjustFLdb); - CfgWriteFloat(L"MIXING", L"VolumeAdjustFR(dB)", VolumeAdjustFRdb); - CfgWriteFloat(L"MIXING", L"VolumeAdjustBL(dB)", VolumeAdjustBLdb); - CfgWriteFloat(L"MIXING", L"VolumeAdjustBR(dB)", VolumeAdjustBRdb); - CfgWriteFloat(L"MIXING", L"VolumeAdjustSL(dB)", VolumeAdjustSLdb); - CfgWriteFloat(L"MIXING", L"VolumeAdjustSR(dB)", VolumeAdjustSRdb); - CfgWriteFloat(L"MIXING", L"VolumeAdjustLFE(dB)", VolumeAdjustLFEdb); - - CfgWriteStr(L"OUTPUT", L"Output_Module", mods[OutputModule]->GetIdent()); - CfgWriteInt(L"OUTPUT", L"Latency", SndOutLatencyMS); - CfgWriteInt(L"OUTPUT", L"Synch_Mode", SynchMode); - CfgWriteInt(L"OUTPUT", L"SpeakerConfiguration", numSpeakers); - CfgWriteInt(L"OUTPUT", L"DplDecodingLevel", dplLevel); - - CfgWriteStr(L"DSP PLUGIN", L"Filename", dspPlugin); - CfgWriteInt(L"DSP PLUGIN", L"ModuleNum", dspPluginModule); - CfgWriteBool(L"DSP PLUGIN", L"Enabled", dspPluginEnabled); - - SoundtouchCfg::WriteSettings(); - DebugConfig::WriteSettings(); -} - -void CheckOutputModule(HWND window) -{ - OutputModule = SendMessage(GetDlgItem(window, IDC_OUTPUT), CB_GETCURSEL, 0, 0); - const bool IsConfigurable = mods[OutputModule] == CubebOut; - const bool AudioExpansion = - mods[OutputModule] == XAudio2Out || - mods[OutputModule] == CubebOut; - - EnableWindow(GetDlgItem(window, IDC_OUTCONF), IsConfigurable); - EnableWindow(GetDlgItem(window, IDC_SPEAKERS), AudioExpansion); - EnableWindow(GetDlgItem(window, IDC_SPEAKERS_TEXT), AudioExpansion); -} - -static BOOL CALLBACK CubebConfigProc(HWND hWnd, UINT uMsg, WPARAM wParam, LPARAM lParam) -{ - switch (uMsg) - { - case WM_INITDIALOG: - { - static constexpr wchar_t* cubeb_backend_names[][2] = { - {L"Unspecified", L""}, - {L"WASAPI", L"wasapi"}, - {L"WinMM", L"winmm"}}; - - const bool minimalLatency = CfgReadBool(L"Cubeb", L"MinimalSuggestedLatency", false); - const int suggestedLatencyMS = CfgReadInt(L"Cubeb", L"ManualSuggestedLatencyMS", 20); - wxString backend; - CfgReadStr(L"Cubeb", L"BackendName", backend, L""); - - SendMessage(GetDlgItem(hWnd, IDC_BACKEND), CB_RESETCONTENT, 0, 0); - - for (size_t i = 0; i < std::size(cubeb_backend_names); i++) - { - SendMessageW(GetDlgItem(hWnd, IDC_BACKEND), CB_ADDSTRING, (WPARAM)i, (LPARAM)cubeb_backend_names[i][0]); - SendMessageW(GetDlgItem(hWnd, IDC_BACKEND), CB_SETITEMDATA, (WPARAM)i, (LPARAM)cubeb_backend_names[i][1]); - if (backend == cubeb_backend_names[i][1]) - SendMessage(GetDlgItem(hWnd, IDC_BACKEND), CB_SETCURSEL, (WPARAM)i, 0); - } - - INIT_SLIDER(IDC_LATENCY, 10, 200, 10, 1, 1); - SendMessage(GetDlgItem(hWnd, IDC_LATENCY), TBM_SETPOS, TRUE, suggestedLatencyMS); - - wchar_t temp[128]; - swprintf_s(temp, L"%d ms", suggestedLatencyMS); - SetWindowText(GetDlgItem(hWnd, IDC_LATENCY_LABEL), temp); - - if (minimalLatency) - SET_CHECK(IDC_MINIMIZE, true); - else - SET_CHECK(IDC_MANUAL, true); - - return TRUE; - } - break; - - case WM_COMMAND: - { - const DWORD wmId = LOWORD(wParam); - const DWORD wmEvent = HIWORD(wParam); - // Parse the menu selections: - switch (wmId) - { - case IDOK: - { - int idx = (int)SendMessage(GetDlgItem(hWnd, IDC_BACKEND), CB_GETCURSEL, 0, 0); - const wchar_t* backend = (const wchar_t*)SendMessage(GetDlgItem(hWnd, IDC_BACKEND), CB_GETITEMDATA, idx, 0); - CfgWriteStr(L"Cubeb", L"BackendName", backend); - - const int suggestedLatencyMS = (int)SendMessage(GetDlgItem(hWnd, IDC_LATENCY), TBM_GETPOS, 0, 0); - const bool minimalLatency = SendMessage(GetDlgItem(hWnd, IDC_MINIMIZE), BM_GETCHECK, 0, 0) == BST_CHECKED; - CfgWriteBool(L"Cubeb", L"MinimalSuggestedLatency", minimalLatency); - CfgWriteInt(L"Cubeb", L"ManualSuggestedLatencyMS", suggestedLatencyMS); - - EndDialog(hWnd, 0); - return TRUE; - } - break; - - case IDCANCEL: - EndDialog(hWnd, 0); - return TRUE; - - default: - return FALSE; - } - } - break; - - case WM_HSCROLL: - { - const DWORD wmId = LOWORD(wParam); - DWORD wmEvent = HIWORD(wParam); - switch (wmId) - { - case TB_LINEUP: - case TB_LINEDOWN: - case TB_PAGEUP: - case TB_PAGEDOWN: - case TB_TOP: - case TB_BOTTOM: - wmEvent = (int)SendMessage((HWND)lParam, TBM_GETPOS, 0, 0); - case TB_THUMBPOSITION: - case TB_THUMBTRACK: - { - wchar_t temp[128]; - if (wmEvent < 10) - wmEvent = 10; - if (wmEvent > 200) - wmEvent = 200; - SendMessage((HWND)lParam, TBM_SETPOS, TRUE, wmEvent); - swprintf_s(temp, L"%d ms", wmEvent); - SetWindowText(GetDlgItem(hWnd, IDC_LATENCY_LABEL), temp); - break; - } - default: - return FALSE; - } - - return TRUE; - } - break; - - default: - return FALSE; - } - return FALSE; -} - -BOOL CALLBACK ConfigProc(HWND hWnd, UINT uMsg, WPARAM wParam, LPARAM lParam) -{ - int wmId, wmEvent; - wchar_t temp[384] = {0}; - - switch (uMsg) - { - case WM_PAINT: - return FALSE; - - case WM_INITDIALOG: - { - SendDialogMsg(hWnd, IDC_INTERPOLATE, CB_RESETCONTENT, 0, 0); - SendDialogMsg(hWnd, IDC_INTERPOLATE, CB_ADDSTRING, 0, (LPARAM)L"0 - Nearest (Fastest / worst quality)"); - SendDialogMsg(hWnd, IDC_INTERPOLATE, CB_ADDSTRING, 0, (LPARAM)L"1 - Linear (Simple / okay sound)"); - SendDialogMsg(hWnd, IDC_INTERPOLATE, CB_ADDSTRING, 0, (LPARAM)L"2 - Cubic (Fake highs / okay sound)"); - SendDialogMsg(hWnd, IDC_INTERPOLATE, CB_ADDSTRING, 0, (LPARAM)L"3 - Hermite (Better highs / okay sound)"); - SendDialogMsg(hWnd, IDC_INTERPOLATE, CB_ADDSTRING, 0, (LPARAM)L"4 - Catmull-Rom (PS2-like / good sound)"); - SendDialogMsg(hWnd, IDC_INTERPOLATE, CB_ADDSTRING, 0, (LPARAM)L"5 - Gaussian (PS2-like / great sound)"); - SendDialogMsg(hWnd, IDC_INTERPOLATE, CB_SETCURSEL, Interpolation, 0); - - SendDialogMsg(hWnd, IDC_SYNCHMODE, CB_RESETCONTENT, 0, 0); - SendDialogMsg(hWnd, IDC_SYNCHMODE, CB_ADDSTRING, 0, (LPARAM)L"TimeStretch (Recommended)"); - SendDialogMsg(hWnd, IDC_SYNCHMODE, CB_ADDSTRING, 0, (LPARAM)L"Async Mix (Breaks some games!)"); - SendDialogMsg(hWnd, IDC_SYNCHMODE, CB_ADDSTRING, 0, (LPARAM)L"None (Audio can skip.)"); - SendDialogMsg(hWnd, IDC_SYNCHMODE, CB_SETCURSEL, SynchMode, 0); - - SendDialogMsg(hWnd, IDC_SPEAKERS, CB_RESETCONTENT, 0, 0); - SendDialogMsg(hWnd, IDC_SPEAKERS, CB_ADDSTRING, 0, (LPARAM)L"Stereo (None, Default)"); - SendDialogMsg(hWnd, IDC_SPEAKERS, CB_ADDSTRING, 0, (LPARAM)L"Quadrafonic"); - SendDialogMsg(hWnd, IDC_SPEAKERS, CB_ADDSTRING, 0, (LPARAM)L"Surround 5.1"); - SendDialogMsg(hWnd, IDC_SPEAKERS, CB_ADDSTRING, 0, (LPARAM)L"Surround 7.1"); - SendDialogMsg(hWnd, IDC_SPEAKERS, CB_SETCURSEL, numSpeakers, 0); - - SendDialogMsg(hWnd, IDC_OUTPUT, CB_RESETCONTENT, 0, 0); - - int modidx = 0; - while (mods[modidx] != nullptr) - { - swprintf_s(temp, 72, L"%d - %s", modidx, StringUtil::UTF8StringToWideString(mods[modidx]->GetLongName()).c_str()); - SendDialogMsg(hWnd, IDC_OUTPUT, CB_ADDSTRING, 0, (LPARAM)temp); - ++modidx; - } - SendDialogMsg(hWnd, IDC_OUTPUT, CB_SETCURSEL, OutputModule, 0); - - float minlat = (SynchMode == 0) ? LATENCY_MIN_TS : LATENCY_MIN; - int minexp = (int)(pow(minlat + 1, 1.0 / 3.0) * 128.0); - int maxexp = (int)(pow((float)LATENCY_MAX + 1, 1.0 / 3.0) * 128.0); - INIT_SLIDER(IDC_LATENCY_SLIDER, minexp, maxexp, 54, 10, 11); - - SendDialogMsg(hWnd, IDC_LATENCY_SLIDER, TBM_SETPOS, TRUE, (int)((pow((float)SndOutLatencyMS, 1.0 / 3.0) * 128.0) + 1)); - swprintf_s(temp, L"%d ms (avg)", SndOutLatencyMS); - SetWindowText(GetDlgItem(hWnd, IDC_LATENCY_LABEL), temp); - - int configvol = (int)(FinalVolume * 100); - INIT_SLIDER(IDC_VOLUME_SLIDER, 0, 200, 10, 5, 1); - - SendDialogMsg(hWnd, IDC_VOLUME_SLIDER, TBM_SETPOS, TRUE, configvol); - swprintf_s(temp, L"%d%%", configvol); - SetWindowText(GetDlgItem(hWnd, IDC_VOLUME_LABEL), temp); - - CheckOutputModule(hWnd); - - EnableWindow(GetDlgItem(hWnd, IDC_OPEN_CONFIG_SOUNDTOUCH), (SynchMode == 0)); - EnableWindow(GetDlgItem(hWnd, IDC_OPEN_CONFIG_DEBUG), DebugEnabled); - - SET_CHECK(IDC_DEBUG_ENABLE, DebugEnabled); - SET_CHECK(IDC_DSP_ENABLE, dspPluginEnabled); - } - break; - - case WM_COMMAND: - wmId = LOWORD(wParam); - wmEvent = HIWORD(wParam); - // Parse the menu selections: - switch (wmId) - { - case IDOK: - { - float res = ((int)SendDialogMsg(hWnd, IDC_LATENCY_SLIDER, TBM_GETPOS, 0, 0)) / 128.0; - SndOutLatencyMS = (int)pow(res, 3.0); - Clampify(SndOutLatencyMS, LATENCY_MIN, LATENCY_MAX); - FinalVolume = (float)(SendDialogMsg(hWnd, IDC_VOLUME_SLIDER, TBM_GETPOS, 0, 0)) / 100; - Interpolation = (int)SendDialogMsg(hWnd, IDC_INTERPOLATE, CB_GETCURSEL, 0, 0); - OutputModule = (int)SendDialogMsg(hWnd, IDC_OUTPUT, CB_GETCURSEL, 0, 0); - SynchMode = (int)SendDialogMsg(hWnd, IDC_SYNCHMODE, CB_GETCURSEL, 0, 0); - numSpeakers = (int)SendDialogMsg(hWnd, IDC_SPEAKERS, CB_GETCURSEL, 0, 0); - - WriteSettings(); - EndDialog(hWnd, 0); - } - break; - - case IDCANCEL: - EndDialog(hWnd, 0); - break; - - case IDC_OUTPUT: - if (wmEvent == CBN_SELCHANGE) - { - CheckOutputModule(hWnd); - } - break; - - case IDC_OUTCONF: - { - const int module = (int)SendMessage(GetDlgItem(hWnd, IDC_OUTPUT), CB_GETCURSEL, 0, 0); - if (mods[module] != CubebOut) - break; - - if (DialogBoxParam(nullptr, MAKEINTRESOURCE(IDD_CUBEB), hWnd, (DLGPROC)CubebConfigProc, 1) == -1) - MessageBox(hWnd, L"Error Opening the config dialog.", L"Error", MB_OK | MB_SETFOREGROUND); - } - break; - - case IDC_OPEN_CONFIG_DEBUG: - { - // Quick Hack -- DebugEnabled is re-loaded with the DebugConfig's API, - // so we need to override it here: - - bool dbgtmp = DebugEnabled; - DebugConfig::OpenDialog(); - DebugEnabled = dbgtmp; - } - break; - - case IDC_SYNCHMODE: - { - if (wmEvent == CBN_SELCHANGE) - { - int sMode = (int)SendDialogMsg(hWnd, IDC_SYNCHMODE, CB_GETCURSEL, 0, 0); - float minlat = (sMode == 0) ? LATENCY_MIN_TS : LATENCY_MIN; - int minexp = (int)(pow(minlat + 1, 1.0 / 3.0) * 128.0); - int maxexp = (int)(pow((float)LATENCY_MAX + 1, 1.0 / 3.0) * 128.0); - INIT_SLIDER(IDC_LATENCY_SLIDER, minexp, maxexp, 54, 10, 11); - - int curpos = (int)SendMessage(GetDlgItem(hWnd, IDC_LATENCY_SLIDER), TBM_GETPOS, 0, 0); - float res = pow(curpos / 128.0, 3.0); - curpos = (int)res; - swprintf_s(temp, L"%d ms (avg)", curpos); - SetDlgItemText(hWnd, IDC_LATENCY_LABEL, temp); - bool soundtouch = sMode == 0; - EnableWindow(GetDlgItem(hWnd, IDC_OPEN_CONFIG_SOUNDTOUCH), soundtouch); - } - } - break; - - - case IDC_OPEN_CONFIG_SOUNDTOUCH: - SoundtouchCfg::OpenDialog(hWnd); - break; - - HANDLE_CHECK(IDC_DSP_ENABLE, dspPluginEnabled); - HANDLE_CHECKNB(IDC_DEBUG_ENABLE, DebugEnabled); - DebugConfig::EnableControls(hWnd); - EnableWindow(GetDlgItem(hWnd, IDC_OPEN_CONFIG_DEBUG), DebugEnabled); - break; - - default: - return FALSE; - } - break; - - case WM_HSCROLL: - { - wmEvent = LOWORD(wParam); - HWND hwndDlg = (HWND)lParam; - - int curpos = HIWORD(wParam); - - switch (wmEvent) - { - case TB_LINEUP: - case TB_LINEDOWN: - case TB_PAGEUP: - case TB_PAGEDOWN: - case TB_TOP: - case TB_BOTTOM: - curpos = (int)SendMessage(hwndDlg, TBM_GETPOS, 0, 0); - - case TB_THUMBPOSITION: - case TB_THUMBTRACK: - Clampify(curpos, - (int)SendMessage(hwndDlg, TBM_GETRANGEMIN, 0, 0), - (int)SendMessage(hwndDlg, TBM_GETRANGEMAX, 0, 0)); - - SendMessage((HWND)lParam, TBM_SETPOS, TRUE, curpos); - - if (hwndDlg == GetDlgItem(hWnd, IDC_LATENCY_SLIDER)) - { - float res = pow(curpos / 128.0, 3.0); - curpos = (int)res; - swprintf_s(temp, L"%d ms (avg)", curpos); - SetDlgItemText(hWnd, IDC_LATENCY_LABEL, temp); - } - - if (hwndDlg == GetDlgItem(hWnd, IDC_VOLUME_SLIDER)) - { - swprintf_s(temp, L"%d%%", curpos); - SetDlgItemText(hWnd, IDC_VOLUME_LABEL, temp); - } - break; - - default: - return FALSE; - } - } - break; - - default: - return FALSE; - } - return TRUE; -} - -void configure() -{ - INT_PTR ret; - ReadSettings(); - ret = DialogBoxParam(nullptr, MAKEINTRESOURCE(IDD_CONFIG), GetActiveWindow(), (DLGPROC)ConfigProc, 1); - if (ret == -1) - { - MessageBox(GetActiveWindow(), L"Error Opening the config dialog.", L"OMG ERROR!", MB_OK | MB_SETFOREGROUND); - return; - } - ReadSettings(); -} diff --git a/pcsx2/SPU2/Windows/ConfigDebug.cpp b/pcsx2/SPU2/Windows/ConfigDebug.cpp deleted file mode 100644 index 614f988356..0000000000 --- a/pcsx2/SPU2/Windows/ConfigDebug.cpp +++ /dev/null @@ -1,292 +0,0 @@ -/* PCSX2 - PS2 Emulator for PCs - * Copyright (C) 2002-2020 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 . - */ - -#include "PrecompiledHeader.h" -#include "SPU2/Global.h" -#include "Dialogs.h" -#include "pcsx2/Config.h" -#include "common/FileSystem.h" -#include "common/StringUtil.h" -#include "common/Path.h" -#include "gui/StringHelpers.h" -#include "gui/wxDirName.h" - - -bool DebugEnabled = false; -bool _MsgToConsole = false; -bool _MsgKeyOnOff = false; -bool _MsgVoiceOff = false; -bool _MsgDMA = false; -bool _MsgAutoDMA = false; -bool _MsgOverruns = false; -bool _MsgCache = false; - -bool _AccessLog = false; -bool _DMALog = false; -bool _WaveLog = false; - -bool _CoresDump = false; -bool _MemDump = false; -bool _RegDump = false; - -bool _visual_debug_enabled = false; - -// this is set true if PCSX2 invokes the SetLogDir callback, which tells SPU2 to use that over -// the configured crap in the ini file. -static bool LogLocationSetByPcsx2 = false; - -static wxString CfgLogsFolder; -static wxString CfgDumpsFolder; - -static wxDirName LogsFolder; -static wxDirName DumpsFolder; - -std::string AccessLogFileName; -std::string DMA4LogFileName; -std::string DMA7LogFileName; - -std::string CoresDumpFileName; -std::string MemDumpFileName; -std::string RegDumpFileName; - -void CfgSetLogDir(const char* dir) -{ - LogsFolder = (dir == nullptr) ? wxString(L"logs") : wxString(dir, wxConvFile); - DumpsFolder = (dir == nullptr) ? wxString(L"logs") : wxString(dir, wxConvFile); - LogLocationSetByPcsx2 = (dir != nullptr); -} - -FILE* OpenBinaryLog(const char* logfile) -{ - return FileSystem::OpenCFile(Path::Combine(EmuFolders::Logs, logfile).c_str(), "wb"); -} - -FILE* OpenLog(const char* logfile) -{ - return FileSystem::OpenCFile(Path::Combine(EmuFolders::Logs, logfile).c_str(), "w"); -} - -FILE* OpenDump(const char* logfile) -{ - return FileSystem::OpenCFile(Path::Combine(EmuFolders::Logs, logfile).c_str(), "w"); -} - -namespace DebugConfig -{ - - static const wxChar* Section = L"DEBUG"; - - void ReadSettings() - { - DebugEnabled = CfgReadBool(Section, L"Global_Enable", 0); - _MsgToConsole = CfgReadBool(Section, L"Show_Messages", 0); - _MsgKeyOnOff = CfgReadBool(Section, L"Show_Messages_Key_On_Off", 0); - _MsgVoiceOff = CfgReadBool(Section, L"Show_Messages_Voice_Off", 0); - _MsgDMA = CfgReadBool(Section, L"Show_Messages_DMA_Transfer", 0); - _MsgAutoDMA = CfgReadBool(Section, L"Show_Messages_AutoDMA", 0); - _MsgOverruns = CfgReadBool(Section, L"Show_Messages_Overruns", 0); - _MsgCache = CfgReadBool(Section, L"Show_Messages_CacheStats", 0); - - _AccessLog = CfgReadBool(Section, L"Log_Register_Access", 0); - _DMALog = CfgReadBool(Section, L"Log_DMA_Transfers", 0); - _WaveLog = CfgReadBool(Section, L"Log_WAVE_Output", 0); - - _CoresDump = CfgReadBool(Section, L"Dump_Info", 0); - _MemDump = CfgReadBool(Section, L"Dump_Memory", 0); - _RegDump = CfgReadBool(Section, L"Dump_Regs", 0); - - _visual_debug_enabled = CfgReadBool(Section, L"Visual_Debug_Enabled", 0); - - CfgReadStr(Section, L"Logs_Folder", CfgLogsFolder, L"logs"); - CfgReadStr(Section, L"Dumps_Folder", CfgDumpsFolder, L"logs"); - - wxString wxAccessLogFileName; - wxString wxDMA4LogFileName; - wxString wxDMA7LogFileName; - wxString wxCoresDumpFileName; - wxString wxMemDumpFileName; - wxString wxRegDumpFileName; - CfgReadStr(Section, L"Access_Log_Filename", wxAccessLogFileName, L"SPU2Log.txt"); - CfgReadStr(Section, L"DMA4Log_Filename", wxDMA4LogFileName, L"SPU2dma4.dat"); - CfgReadStr(Section, L"DMA7Log_Filename", wxDMA7LogFileName, L"SPU2dma7.dat"); - - CfgReadStr(Section, L"Info_Dump_Filename", wxCoresDumpFileName, L"SPU2Cores.txt"); - CfgReadStr(Section, L"Mem_Dump_Filename", wxMemDumpFileName, L"SPU2mem.dat"); - CfgReadStr(Section, L"Reg_Dump_Filename", wxRegDumpFileName, L"SPU2regs.dat"); - - AccessLogFileName = StringUtil::wxStringToUTF8String(wxAccessLogFileName); - DMA4LogFileName = StringUtil::wxStringToUTF8String(wxDMA4LogFileName); - DMA7LogFileName = StringUtil::wxStringToUTF8String(wxDMA7LogFileName); - CoresDumpFileName = StringUtil::wxStringToUTF8String(wxCoresDumpFileName); - MemDumpFileName = StringUtil::wxStringToUTF8String(wxMemDumpFileName); - RegDumpFileName = StringUtil::wxStringToUTF8String(wxRegDumpFileName); - - if (!LogLocationSetByPcsx2) - { - LogsFolder = CfgLogsFolder; - DumpsFolder = CfgLogsFolder; - } - } - - - void WriteSettings() - { - CfgWriteBool(Section, L"Global_Enable", DebugEnabled); - - CfgWriteBool(Section, L"Show_Messages", _MsgToConsole); - CfgWriteBool(Section, L"Show_Messages_Key_On_Off", _MsgKeyOnOff); - CfgWriteBool(Section, L"Show_Messages_Voice_Off", _MsgVoiceOff); - CfgWriteBool(Section, L"Show_Messages_DMA_Transfer", _MsgDMA); - CfgWriteBool(Section, L"Show_Messages_AutoDMA", _MsgAutoDMA); - CfgWriteBool(Section, L"Show_Messages_Overruns", _MsgOverruns); - CfgWriteBool(Section, L"Show_Messages_CacheStats", _MsgCache); - - CfgWriteBool(Section, L"Log_Register_Access", _AccessLog); - CfgWriteBool(Section, L"Log_DMA_Transfers", _DMALog); - CfgWriteBool(Section, L"Log_WAVE_Output", _WaveLog); - - CfgWriteBool(Section, L"Dump_Info", _CoresDump); - CfgWriteBool(Section, L"Dump_Memory", _MemDump); - CfgWriteBool(Section, L"Dump_Regs", _RegDump); - - CfgWriteBool(Section, L"Visual_Debug_Enabled", _visual_debug_enabled); - - // None of the logs strings are changable via GUI, so no point in bothering to - // write them back out. - CfgWriteStr(Section, L"Logs_Folder", CfgLogsFolder); - CfgWriteStr(Section, L"Dumps_Folder", CfgDumpsFolder); - - CfgWriteStr(Section, L"Access_Log_Filename", StringUtil::UTF8StringToWxString(AccessLogFileName)); - CfgWriteStr(Section, L"DMA4Log_Filename", StringUtil::UTF8StringToWxString(DMA4LogFileName)); - CfgWriteStr(Section, L"DMA7Log_Filename", StringUtil::UTF8StringToWxString(DMA7LogFileName)); - - CfgWriteStr(Section, L"Info_Dump_Filename", StringUtil::UTF8StringToWxString(CoresDumpFileName)); - CfgWriteStr(Section, L"Mem_Dump_Filename", StringUtil::UTF8StringToWxString(MemDumpFileName)); - CfgWriteStr(Section, L"Reg_Dump_Filename", StringUtil::UTF8StringToWxString(RegDumpFileName)); - } - - static void EnableMessages(HWND hWnd) - { - ENABLE_CONTROL(IDC_MSGSHOW, DebugEnabled); - ENABLE_CONTROL(IDC_MSGKEY, MsgToConsole()); - ENABLE_CONTROL(IDC_MSGVOICE, MsgToConsole()); - ENABLE_CONTROL(IDC_MSGDMA, MsgToConsole()); - ENABLE_CONTROL(IDC_MSGADMA, MsgToConsole()); - ENABLE_CONTROL(IDC_DBG_OVERRUNS, MsgToConsole()); - ENABLE_CONTROL(IDC_DBG_CACHE, MsgToConsole()); - } - - void EnableControls(HWND hWnd) - { - EnableMessages(hWnd); - ENABLE_CONTROL(IDC_LOGDMA, DebugEnabled); - ENABLE_CONTROL(IDC_LOGREGS, IsDevBuild ? DebugEnabled : false); - ENABLE_CONTROL(IDC_LOGWAVE, IsDevBuild ? DebugEnabled : false); - ENABLE_CONTROL(IDC_DUMPCORE, DebugEnabled); - ENABLE_CONTROL(IDC_DUMPMEM, DebugEnabled); - ENABLE_CONTROL(IDC_DUMPREGS, DebugEnabled); - ENABLE_CONTROL(IDC_DEBUG_VISUAL, IsDevBuild ? DebugEnabled : false); - } - - static BOOL CALLBACK DialogProc(HWND hWnd, UINT uMsg, WPARAM wParam, LPARAM lParam) - { - int wmId; - //wchar_t temp[384]={0}; - - switch (uMsg) - { - case WM_PAINT: - return FALSE; - - case WM_INITDIALOG: - { - EnableControls(hWnd); - - // Debugging / Logging Flags: - SET_CHECK(IDC_DEBUG, DebugEnabled); - SET_CHECK(IDC_MSGSHOW, _MsgToConsole); - SET_CHECK(IDC_MSGKEY, _MsgKeyOnOff); - SET_CHECK(IDC_MSGVOICE, _MsgVoiceOff); - SET_CHECK(IDC_MSGDMA, _MsgDMA); - SET_CHECK(IDC_MSGADMA, _MsgAutoDMA); - SET_CHECK(IDC_DBG_OVERRUNS, _MsgOverruns); - SET_CHECK(IDC_DBG_CACHE, _MsgCache); - SET_CHECK(IDC_LOGREGS, _AccessLog); - SET_CHECK(IDC_LOGDMA, _DMALog); - SET_CHECK(IDC_LOGWAVE, _WaveLog); - SET_CHECK(IDC_DUMPCORE, _CoresDump); - SET_CHECK(IDC_DUMPMEM, _MemDump); - SET_CHECK(IDC_DUMPREGS, _RegDump); - SET_CHECK(IDC_DEBUG_VISUAL, _visual_debug_enabled); - - ShowWindow(GetDlgItem(hWnd, IDC_MSG_PUBLIC_BUILD), !IsDevBuild); - } - break; - - case WM_COMMAND: - wmId = LOWORD(wParam); - // Parse the menu selections: - switch (wmId) - { - case IDOK: - WriteSettings(); - EndDialog(hWnd, 0); - break; - - case IDCANCEL: - EndDialog(hWnd, 0); - break; - - HANDLE_CHECKNB(IDC_MSGSHOW, _MsgToConsole); - EnableMessages(hWnd); - break; - - HANDLE_CHECK(IDC_MSGKEY, _MsgKeyOnOff); - HANDLE_CHECK(IDC_MSGVOICE, _MsgVoiceOff); - HANDLE_CHECK(IDC_MSGDMA, _MsgDMA); - HANDLE_CHECK(IDC_MSGADMA, _MsgAutoDMA); - break; - - HANDLE_CHECK(IDC_DBG_OVERRUNS, _MsgOverruns); - HANDLE_CHECK(IDC_DBG_CACHE, _MsgCache); - HANDLE_CHECK(IDC_LOGREGS, _AccessLog); - HANDLE_CHECK(IDC_LOGDMA, _DMALog); - HANDLE_CHECK(IDC_LOGWAVE, _WaveLog); - HANDLE_CHECK(IDC_DUMPCORE, _CoresDump); - HANDLE_CHECK(IDC_DUMPMEM, _MemDump); - HANDLE_CHECK(IDC_DUMPREGS, _RegDump); - HANDLE_CHECK(IDC_DEBUG_VISUAL, _visual_debug_enabled); - default: - return FALSE; - } - break; - - default: - return FALSE; - } - return TRUE; - } - - void OpenDialog() - { - INT_PTR ret = DialogBoxParam(nullptr, MAKEINTRESOURCE(IDD_CONFIG_DEBUG), GetActiveWindow(), (DLGPROC)DialogProc, 1); - if (ret == -1) - { - MessageBox(GetActiveWindow(), L"Error Opening the debug configuration dialog.", L"OMG ERROR!", MB_OK | MB_SETFOREGROUND); - return; - } - ReadSettings(); - } -} // namespace DebugConfig diff --git a/pcsx2/SPU2/Windows/ConfigSoundTouch.cpp b/pcsx2/SPU2/Windows/ConfigSoundTouch.cpp deleted file mode 100644 index 0a8df6e0f2..0000000000 --- a/pcsx2/SPU2/Windows/ConfigSoundTouch.cpp +++ /dev/null @@ -1,138 +0,0 @@ -/* PCSX2 - PS2 Emulator for PCs - * Copyright (C) 2002-2020 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 . - */ - -#include "PrecompiledHeader.h" -#include "SPU2/Global.h" -#include "Dialogs.h" - -#include "SoundTouch.h" - -static int SequenceLenMS = 30; -static int SeekWindowMS = 20; -static int OverlapMS = 10; - -// Timestretch Slider Bounds, Min/Max -static const int SequenceLen_Min = 20; -static const int SequenceLen_Max = 100; - -static const int SeekWindow_Min = 10; -static const int SeekWindow_Max = 30; - -static const int Overlap_Min = 5; -static const int Overlap_Max = 15; - -void SoundtouchCfg::ApplySettings(soundtouch::SoundTouch& sndtouch) -{ - sndtouch.setSetting(SETTING_SEQUENCE_MS, SequenceLenMS); - sndtouch.setSetting(SETTING_SEEKWINDOW_MS, SeekWindowMS); - sndtouch.setSetting(SETTING_OVERLAP_MS, OverlapMS); -} - -static void ClampValues() -{ - Clampify(SequenceLenMS, SequenceLen_Min, SequenceLen_Max); - Clampify(SeekWindowMS, SeekWindow_Min, SeekWindow_Max); - Clampify(OverlapMS, Overlap_Min, Overlap_Max); -} - -void SoundtouchCfg::ReadSettings() -{ - SequenceLenMS = CfgReadInt(L"SOUNDTOUCH", L"SequenceLengthMS", 30); - SeekWindowMS = CfgReadInt(L"SOUNDTOUCH", L"SeekWindowMS", 20); - OverlapMS = CfgReadInt(L"SOUNDTOUCH", L"OverlapMS", 10); - - ClampValues(); -} - -void SoundtouchCfg::WriteSettings() -{ - CfgWriteInt(L"SOUNDTOUCH", L"SequenceLengthMS", SequenceLenMS); - CfgWriteInt(L"SOUNDTOUCH", L"SeekWindowMS", SeekWindowMS); - CfgWriteInt(L"SOUNDTOUCH", L"OverlapMS", OverlapMS); -} - -BOOL CALLBACK SoundtouchCfg::DialogProc(HWND hWnd, UINT uMsg, WPARAM wParam, LPARAM lParam) -{ - int wmId; - //wchar_t temp[384]={0}; - - switch (uMsg) - { - case WM_PAINT: - return FALSE; - - case WM_INITDIALOG: - { - INIT_SLIDER(IDC_SEQLEN_SLIDER, SequenceLen_Min, SequenceLen_Max, 20, 5, 1); - INIT_SLIDER(IDC_SEEKWIN_SLIDER, SeekWindow_Min, SeekWindow_Max, 5, 2, 1); - INIT_SLIDER(IDC_OVERLAP_SLIDER, Overlap_Min, Overlap_Max, 3, 2, 1); - - SendDialogMsg(hWnd, IDC_SEQLEN_SLIDER, TBM_SETPOS, TRUE, SequenceLenMS); - SendDialogMsg(hWnd, IDC_SEEKWIN_SLIDER, TBM_SETPOS, TRUE, SeekWindowMS); - SendDialogMsg(hWnd, IDC_OVERLAP_SLIDER, TBM_SETPOS, TRUE, OverlapMS); - } - - case WM_COMMAND: - wmId = LOWORD(wParam); - // Parse the menu selections: - if (wmId == IDOK) - { - SequenceLenMS = (int)SendDialogMsg(hWnd, IDC_SEQLEN_SLIDER, TBM_GETPOS, 0, 0); - SeekWindowMS = (int)SendDialogMsg(hWnd, IDC_SEEKWIN_SLIDER, TBM_GETPOS, 0, 0); - OverlapMS = (int)SendDialogMsg(hWnd, IDC_OVERLAP_SLIDER, TBM_GETPOS, 0, 0); - - ClampValues(); - WriteSettings(); - EndDialog(hWnd, 0); - } - else if (wmId == IDC_RESET_DEFAULTS) - { - SequenceLenMS = 30; - SeekWindowMS = 20; - OverlapMS = 10; - - SendDialogMsg(hWnd, IDC_SEQLEN_SLIDER, TBM_SETPOS, TRUE, SequenceLenMS); - SendDialogMsg(hWnd, IDC_SEEKWIN_SLIDER, TBM_SETPOS, TRUE, SeekWindowMS); - SendDialogMsg(hWnd, IDC_OVERLAP_SLIDER, TBM_SETPOS, TRUE, OverlapMS); - - AssignSliderValue((HWND)lParam, hWnd, SequenceLenMS); - } - else if (wmId == IDCANCEL) - { - EndDialog(hWnd, 0); - } - break; - - case WM_HSCROLL: - DoHandleScrollMessage(hWnd, wParam, lParam); - break; - - default: - return FALSE; - } - return TRUE; -} - -void SoundtouchCfg::OpenDialog(HWND hWnd) -{ - INT_PTR ret; - ret = DialogBox(nullptr, MAKEINTRESOURCE(IDD_CONFIG_SOUNDTOUCH), hWnd, (DLGPROC)DialogProc); - if (ret == -1) - { - MessageBox(GetActiveWindow(), L"Error Opening the Soundtouch advanced dialog.", L"OMG ERROR!", MB_OK | MB_SETFOREGROUND); - return; - } - ReadSettings(); -} diff --git a/pcsx2/SPU2/Windows/Dialogs.h b/pcsx2/SPU2/Windows/Dialogs.h deleted file mode 100644 index 7747834655..0000000000 --- a/pcsx2/SPU2/Windows/Dialogs.h +++ /dev/null @@ -1,61 +0,0 @@ -/* PCSX2 - PS2 Emulator for PCs - * Copyright (C) 2002-2020 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 . - */ - -#pragma once - -#ifdef _WIN32 -#include "WinConfig.h" -#else -#include "LnxConfig.h" -#endif - -namespace DebugConfig -{ - extern void ReadSettings(); - extern void WriteSettings(); - extern void OpenDialog(); - extern void EnableControls(HWND hWnd); -} // namespace DebugConfig - -namespace SoundtouchCfg -{ - extern void ReadSettings(); - extern void WriteSettings(); - extern void OpenDialog(HWND hWnd); - extern BOOL CALLBACK DialogProc(HWND hWnd, UINT uMsg, WPARAM wParam, LPARAM lParam); -} // namespace SoundtouchCfg - -extern int SendDialogMsg(HWND hwnd, int dlgId, UINT code, WPARAM wParam, LPARAM lParam); - -extern void AssignSliderValue(HWND idcwnd, HWND hwndDisplay, int value); -extern void AssignSliderValue(HWND hWnd, int idc, int editbox, int value); -extern int GetSliderValue(HWND hWnd, int idc); -extern BOOL DoHandleScrollMessage(HWND hwndDisplay, WPARAM wParam, LPARAM lParam); - -extern void CfgSetSettingsDir(const char* dir); -extern void CfgSetLogDir(const char* dir); - -extern bool CfgFindName(const TCHAR* Section, const TCHAR* Name); - -extern void CfgWriteBool(const TCHAR* Section, const TCHAR* Name, bool Value); -extern void CfgWriteInt(const TCHAR* Section, const TCHAR* Name, int Value); -extern void CfgWriteFloat(const TCHAR* Section, const TCHAR* Name, float Value); -extern void CfgWriteStr(const TCHAR* Section, const TCHAR* Name, const wxString& Data); - -extern bool CfgReadBool(const TCHAR* Section, const TCHAR* Name, bool Default); -extern void CfgReadStr(const TCHAR* Section, const TCHAR* Name, wxString& Data, const TCHAR* Default); -extern void CfgReadStr(const TCHAR* Section, const TCHAR* Name, TCHAR* Data, int DataSize, const TCHAR* Default); -extern int CfgReadInt(const TCHAR* Section, const TCHAR* Name, int Default); -extern float CfgReadFloat(const TCHAR* Section, const TCHAR* Name, float Default); diff --git a/pcsx2/SPU2/Windows/RealtimeDebugger.cpp b/pcsx2/SPU2/Windows/RealtimeDebugger.cpp deleted file mode 100644 index 7b25e04867..0000000000 --- a/pcsx2/SPU2/Windows/RealtimeDebugger.cpp +++ /dev/null @@ -1,287 +0,0 @@ -/* PCSX2 - PS2 Emulator for PCs - * Copyright (C) 2002-2020 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 . - */ - -#include "PrecompiledHeader.h" -#include "SPU2/Global.h" -#include "Dialogs.h" - -bool debugDialogOpen = false; -HWND hDebugDialog = nullptr; - -#ifdef PCSX2_DEVBUILD - -int FillRectangle(HDC dc, int left, int top, int width, int height) -{ - RECT r = {left, top, left + width, top + height}; - - return FillRect(dc, &r, (HBRUSH)GetStockObject(DC_BRUSH)); -} - -BOOL DrawRectangle(HDC dc, int left, int top, int width, int height) -{ - RECT r = {left, top, left + width, top + height}; - - POINT p[5] = { - {r.left, r.top}, - {r.right, r.top}, - {r.right, r.bottom}, - {r.left, r.bottom}, - {r.left, r.top}, - }; - - return Polyline(dc, p, 5); -} - - -HFONT hf = nullptr; -int lCount = 0; -void UpdateDebugDialog() -{ - if (!debugDialogOpen) - return; - - lCount++; - if (lCount >= (SampleRate / 100)) // Increase to SampleRate/200 for smooth display. - { - HDC hdc = GetDC(hDebugDialog); - - if (!hf) - { - hf = CreateFont(12, 0, 0, 0, 0, FALSE, FALSE, FALSE, ANSI_CHARSET, OUT_DEFAULT_PRECIS, CLIP_DEFAULT_PRECIS, - DEFAULT_QUALITY, DEFAULT_PITCH | FF_SWISS, L"Lucida Console"); - } - - SelectObject(hdc, hf); - SelectObject(hdc, GetStockObject(DC_BRUSH)); - SelectObject(hdc, GetStockObject(DC_PEN)); - - for (int c = 0; c < 2; c++) - { - V_Core& cx(Cores[c]); - V_CoreDebug& cd(DebugCores[c]); - - for (int v = 0; v < 24; v++) - { - int cc = c * 2 + (v / 12); - int vv = v % 12; - int IX = 8 + 128 * cc; - int IY = 8 + 48 * vv; - V_Voice& vc(cx.Voices[v]); - V_VoiceDebug& vcd(cd.Voices[v]); - - SetDCBrushColor(hdc, RGB(0, 0, 0)); - if ((vc.ADSR.Phase > 0) && (vc.ADSR.Phase < 6)) - { - SetDCBrushColor(hdc, RGB(0, 0, 128)); // light blue for playing voice - if (vc.Modulated) - { - SetDCBrushColor(hdc, RGB(0, 128, 0)); // light green for playing voice with modulation enabled - } - if (vc.Noise) - { - SetDCBrushColor(hdc, RGB(128, 0, 0)); // light red for playing voice with noise enabled - } - } - /* - else - { - if(vcd.lastStopReason==1) - { - SetDCBrushColor(hdc,RGB(128, 0, 0)); - } - if(vcd.lastStopReason==2) - { - SetDCBrushColor(hdc,RGB( 0,128, 0)); - } - }*/ - - FillRectangle(hdc, IX, IY, 124, 46); - - SetDCPenColor(hdc, RGB(255, 128, 32)); - - DrawRectangle(hdc, IX, IY, 124, 46); - - SetDCBrushColor(hdc, RGB(0, 255, 0)); - - int vl = abs(((vc.Volume.Left.Value >> 16) * 38) >> 15); - int vr = abs(((vc.Volume.Right.Value >> 16) * 38) >> 15); - - FillRectangle(hdc, IX + 58, IY + 42 - vl, 4, vl); - FillRectangle(hdc, IX + 62, IY + 42 - vr, 4, vr); - - int adsr = ((vc.ADSR.Value >> 16) * 38) / 32768; - - FillRectangle(hdc, IX + 66, IY + 42 - adsr, 4, adsr); - - int peak = (vcd.displayPeak * 38) / 32768; - - if (vcd.displayPeak >= 32700) // leave a little bit of margin - { - SetDCBrushColor(hdc, RGB(255, 0, 0)); - } - - FillRectangle(hdc, IX + 70, IY + 42 - peak, 4, peak); - - if (vc.ADSR.Value > 0) - { - if (vc.SBuffer) - for (int i = 0; i < 28; i++) - { - int val = ((int)vc.SBuffer[i] * 20) / 32768; - - int y = 0; - - if (val > 0) - { - y = val; - } - else - val = -val; - - if (val != 0) - { - FillRectangle(hdc, IX + 90 + i, IY + 24 - y, 1, val); - } - } - } - - SetTextColor(hdc, RGB(0, 255, 0)); - SetBkColor(hdc, RGB(0, 0, 0)); - - static wchar_t t[256]; - - swprintf_s(t, L"%06x", vc.StartA); - TextOut(hdc, IX + 4, IY + 4, t, 6); - - swprintf_s(t, L"%06x", vc.NextA); - TextOut(hdc, IX + 4, IY + 18, t, 6); - - swprintf_s(t, L"%06x", vc.LoopStartA); - TextOut(hdc, IX + 4, IY + 32, t, 6); - - vcd.displayPeak = 0; - } - - // top now: 400 - int JX = 8 + c * 256; - int JY = 584; - - SetDCBrushColor(hdc, RGB(0, 0, 0)); - SetDCPenColor(hdc, RGB(255, 128, 32)); - - FillRectangle(hdc, JX, JY, 252, 60); - DrawRectangle(hdc, JX, JY, 252, 60); - - SetTextColor(hdc, RGB(255, 255, 255)); - SetBkColor(hdc, RGB(0, 0, 0)); - - static wchar_t t[256]; - TextOut(hdc, JX + 4, JY + 4, L"REVB", 4); - TextOut(hdc, JX + 4, JY + 18, L"IRQE", 4); - TextOut(hdc, JX + 4, JY + 32, L"ADMA", 4); - swprintf_s(t, L"DMA%s", c == 0 ? L"4" : L"7"); - TextOut(hdc, JX + 4, JY + 46, t, 4); - - SetTextColor(hdc, RGB(0, 255, 0)); - memset(t, 0, sizeof(t)); - swprintf_s(t, L"ESA %x", cx.EffectsStartA); - TextOut(hdc, JX + 56, JY + 4, t, 9); - memset(t, 0, sizeof(t)); - swprintf_s(t, L"EEA %x", cx.EffectsEndA); - TextOut(hdc, JX + 128, JY + 4, t, 9); - memset(t, 0, sizeof(t)); - swprintf_s(t, L"(%x)", cx.EffectsBufferSize); - TextOut(hdc, JX + 200, JY + 4, t, 7); - - memset(t, 0, sizeof(t)); - swprintf_s(t, L"IRQA %x", cx.IRQA); - TextOut(hdc, JX + 56, JY + 18, t, 12); - - SetTextColor(hdc, RGB(255, 255, 255)); - SetDCBrushColor(hdc, RGB(255, 0, 0)); - - if (cx.FxEnable) - { - FillRectangle(hdc, JX + 40, JY + 4, 10, 10); - } - if (cx.IRQEnable) - { - FillRectangle(hdc, JX + 40, JY + 18, 10, 10); - } - if (cx.AutoDMACtrl != 0) - { - FillRectangle(hdc, JX + 40, JY + 32, 10, 10); - - for (int j = 0; j < 64; j++) - { - int i = j * 256 / 64; - int val = (cd.admaWaveformL[i] * 26) / 32768; - int y = 0; - - if (val > 0) - y = val; - else - val = -val; - - if (val != 0) - { - FillRectangle(hdc, JX + 60 + j, JY + 30 - y, 1, val); - } - } - - for (int j = 0; j < 64; j++) - { - int i = j * 256 / 64; - int val = (cd.admaWaveformR[i] * 26) / 32768; - int y = 0; - - if (val > 0) - y = val; - else - val = -val; - - if (val != 0) - { - FillRectangle(hdc, JX + 136 + j, JY + 30 - y, 1, val); - } - } - } - if (cd.dmaFlag > 0) // So it shows x times this is called, since dmas are so fast - { - swprintf_s(t, L"size = %d", cd.lastsize); - - TextOut(hdc, JX + 64, JY + 46, t, wcslen(t)); - FillRectangle(hdc, JX + 40, JY + 46, 10, 10); - cd.dmaFlag--; - } - } - - ReleaseDC(hDebugDialog, hdc); - lCount = 0; - } - - MSG msg; - while (PeekMessage(&msg, hDebugDialog, 0, 0, PM_REMOVE)) - { - TranslateMessage(&msg); - DispatchMessage(&msg); - } -} -#else -void UpdateDebugDialog() -{ - // Release mode. Nothing to do -} -#endif diff --git a/pcsx2/SPU2/Windows/SPU2.rc b/pcsx2/SPU2/Windows/SPU2.rc deleted file mode 100644 index 829c66854a..0000000000 --- a/pcsx2/SPU2/Windows/SPU2.rc +++ /dev/null @@ -1,252 +0,0 @@ -// Microsoft Visual C++ generated resource script. -// -#include "resource.h" - -#define APSTUDIO_READONLY_SYMBOLS -///////////////////////////////////////////////////////////////////////////// -// -// Generated from the TEXTINCLUDE 2 resource. -// -#include "common/afxresmw.h" -///////////////////////////////////////////////////////////////////////////// -#undef APSTUDIO_READONLY_SYMBOLS - -///////////////////////////////////////////////////////////////////////////// -// English (United States) resources - -#if !defined(AFX_RESOURCE_DLL) || defined(AFX_TARG_ENU) -LANGUAGE LANG_ENGLISH, SUBLANG_ENGLISH_US -#pragma code_page(1252) - -///////////////////////////////////////////////////////////////////////////// -// -// Dialog -// - -IDD_CONFIG DIALOGEX 0, 0, 325, 195 -STYLE DS_SETFONT | DS_MODALFRAME | DS_FIXEDSYS | WS_POPUP | WS_CAPTION | WS_SYSMENU -CAPTION "Audio Settings" -FONT 8, "MS Shell Dlg", 400, 0, 0x0 -BEGIN - PUSHBUTTON "OK",IDOK,113,174,50,14,NOT WS_TABSTOP - PUSHBUTTON "Cancel",IDCANCEL,173,174,50,14,NOT WS_TABSTOP - GROUPBOX "Mixing Settings",IDC_STATIC,6,5,157,115 - LTEXT "Interpolation:",IDC_STATIC,12,16,61,10,NOT WS_GROUP - COMBOBOX IDC_INTERPOLATE,12,26,142,84,CBS_DROPDOWNLIST | WS_VSCROLL | WS_TABSTOP - GROUPBOX "",IDC_STATIC,6,124,157,45 - CHECKBOX "Enable Debug Options",IDC_DEBUG_ENABLE,11,135,118,10,NOT WS_TABSTOP - PUSHBUTTON "Configure...",IDC_OPEN_CONFIG_DEBUG,11,147,52,13 - GROUPBOX "Output Settings",IDC_STATIC,173,5,145,164 - LTEXT "Module:",IDC_STATIC,179,16,50,9,NOT WS_GROUP - COMBOBOX IDC_OUTPUT,179,26,130,120,CBS_DROPDOWNLIST | WS_VSCROLL | WS_TABSTOP - PUSHBUTTON "Configure...",IDC_OUTCONF,179,42,52,13 - LTEXT "Volume:",IDC_STATIC,204,61,27,8,NOT WS_GROUP - CTEXT "100%",IDC_VOLUME_LABEL,242,61,58,9 - CONTROL "",IDC_VOLUME_SLIDER,"msctls_trackbar32",TBS_AUTOTICKS | WS_TABSTOP,181,72,129,10 - LTEXT "Latency:",IDC_STATIC,204,86,29,8,NOT WS_GROUP - CTEXT "100 ms (avg)",IDC_LATENCY_LABEL,241,86,58,9 - CONTROL "Slider2",IDC_LATENCY_SLIDER,"msctls_trackbar32",TBS_AUTOTICKS | WS_TABSTOP,181,96,129,10 - CONTROL "Synchronizing Mode:",IDC_STATIC,"Static",SS_LEFTNOWORDWRAP | WS_GROUP,12,42,133,8 - COMBOBOX IDC_SYNCHMODE,12,54,142,62,CBS_DROPDOWNLIST | WS_VSCROLL | WS_TABSTOP - PUSHBUTTON "Advanced...",IDC_OPEN_CONFIG_SOUNDTOUCH,12,72,52,13 - LTEXT "Audio Expansion Mode:",IDC_SPEAKERS_TEXT,12,90,137,10,NOT WS_GROUP - COMBOBOX IDC_SPEAKERS,12,102,142,84,CBS_DROPDOWNLIST | WS_VSCROLL | WS_TABSTOP - CONTROL "Use a Winamp DSP plugin",IDC_DSP_ENABLE,"Button",BS_AUTOCHECKBOX | WS_TABSTOP,181,110,129,11 - LTEXT "(currently requires manual configuration via the ini file)",IDC_STATIC,193,122,100,20 -END - -IDD_DEBUG DIALOGEX 0, 0, 303, 473 -STYLE DS_SETFONT | DS_MODALFRAME | WS_POPUP | WS_CAPTION | WS_SYSMENU -CAPTION "SPU2 Debug" -FONT 9, "Lucida Console", 400, 0, 0x0 -BEGIN - DEFPUSHBUTTON "Close",IDOK,246,451,50,14 -END - -IDD_CONFIG_SOUNDTOUCH DIALOGEX 0, 0, 206, 223 -STYLE DS_SETFONT | DS_MODALFRAME | DS_FIXEDSYS | WS_POPUP | WS_CAPTION | WS_SYSMENU -CAPTION "Soundtouch Advanced Configuration - SPU2" -FONT 8, "MS Shell Dlg", 400, 0, 0x1 -BEGIN - DEFPUSHBUTTON "OK",IDOK,50,201,50,14 - PUSHBUTTON "Cancel",IDCANCEL,106,201,50,14 - CTEXT "These are advanced configuration options for fine tuning time stretching behavior. Larger values are better for slowdown, while smaller values are better for speed-up (better than 60fps).\n\nAll options are in milliseconds (ms).",IDC_STATIC,5,5,196,52 - CONTROL "",IDC_SEQLEN_SLIDER,"msctls_trackbar32",TBS_AUTOTICKS | WS_TABSTOP,50,98,105,10 - CTEXT "Sequence Length",IDC_STATIC,72,86,64,9 - CONTROL "",IDC_SEEKWIN_SLIDER,"msctls_trackbar32",TBS_AUTOTICKS | WS_TABSTOP,50,134,105,10 - CTEXT "Seekwindow Size",IDC_STATIC,70,123,66,9 - CONTROL "",IDC_OVERLAP_SLIDER,"msctls_trackbar32",TBS_AUTOTICKS | WS_TABSTOP,50,168,105,10 - CTEXT "Overlap",IDC_STATIC,86,160,34,9 - LTEXT "20",IDC_STATIC,50,110,9,8 - LTEXT "100",IDC_STATIC,146,110,13,8 - LTEXT "10",IDC_STATIC,50,147,9,8 - LTEXT "30",IDC_STATIC,146,147,9,8 - LTEXT "5",IDC_STATIC,52,182,8,8 - LTEXT "15",IDC_STATIC,146,182,9,8 - PUSHBUTTON "Reset Defaults",IDC_RESET_DEFAULTS,61,62,82,12 -END - -IDD_CONFIG_DEBUG DIALOGEX 0, 0, 292, 239 -STYLE DS_SETFONT | DS_MODALFRAME | DS_FIXEDSYS | WS_POPUP | WS_CAPTION | WS_SYSMENU -CAPTION "SPU2 Debugging Options" -FONT 8, "MS Shell Dlg", 400, 0, 0x1 -BEGIN - DEFPUSHBUTTON "OK",IDOK,93,217,50,14 - PUSHBUTTON "Cancel",IDCANCEL,149,217,50,14 - GROUPBOX "",IDC_STATIC,5,5,135,93 - GROUPBOX "Logfile-only Logs",IDC_STATIC,151,5,136,53 - GROUPBOX "Dumps (on close)",IDC_STATIC,151,60,135,54 - CONTROL "Show In Console",IDC_MSGSHOW,"Button",BS_AUTOCHECKBOX | WS_TABSTOP,11,5,69,10 - CHECKBOX "KeyOn/Off Events",IDC_MSGKEY,17,18,74,9,NOT WS_TABSTOP - CONTROL "Voice Stop Events",IDC_MSGVOICE,"Button",BS_AUTOCHECKBOX | WS_TABSTOP,17,31,75,9 - CONTROL "DMA Operations",IDC_MSGDMA,"Button",BS_AUTOCHECKBOX | WS_TABSTOP,17,44,68,9 - CONTROL "AutoDMA Operations",IDC_MSGADMA,"Button",BS_AUTOCHECKBOX | WS_TABSTOP,17,57,83,9 - CONTROL "Buffer Over/Underruns",IDC_DBG_OVERRUNS,"Button",BS_AUTOCHECKBOX | WS_TABSTOP,17,70,97,9 - CONTROL "ADPCM Cache Statistics",IDC_DBG_CACHE,"Button",BS_AUTOCHECKBOX | WS_TABSTOP,17,83,114,9 - CHECKBOX "Dump Core and Voice State",IDC_DUMPCORE,159,74,104,10,NOT WS_TABSTOP - CHECKBOX "Dump Memory Contents",IDC_DUMPMEM,159,87,91,10,NOT WS_TABSTOP - CHECKBOX "Dump Register Data",IDC_DUMPREGS,159,100,80,10,NOT WS_TABSTOP - CHECKBOX "Log Register/DMA Actions",IDC_LOGREGS,159,18,101,10,WS_GROUP | NOT WS_TABSTOP - CHECKBOX "Log DMA Writes",IDC_LOGDMA,159,31,68,10,NOT WS_TABSTOP - CHECKBOX "Log Audio Output",IDC_LOGWAVE,159,44,71,10,NOT WS_TABSTOP - LTEXT "Note: This is a non-devel build. For performance reasons, some logging options are disabled; and only available in devel/debug builds.",IDC_MSG_PUBLIC_BUILD,9,187,174,30 - GROUPBOX "Others",IDC_DEBUG_OTHERS,5,104,135,68 - CONTROL "Show Core Activity",IDC_DEBUG_VISUAL,"Button",BS_AUTOCHECKBOX | WS_TABSTOP,17,116,90,11 -END - - -///////////////////////////////////////////////////////////////////////////// -// -// DESIGNINFO -// - -#ifdef APSTUDIO_INVOKED -GUIDELINES DESIGNINFO -BEGIN - IDD_CONFIG, DIALOG - BEGIN - LEFTMARGIN, 6 - RIGHTMARGIN, 324 - VERTGUIDE, 218 - VERTGUIDE, 282 - BOTTOMMARGIN, 194 - END - - IDD_DEBUG, DIALOG - BEGIN - LEFTMARGIN, 7 - RIGHTMARGIN, 296 - TOPMARGIN, 7 - BOTTOMMARGIN, 465 - END - - IDD_CONFIG_SOUNDTOUCH, DIALOG - BEGIN - LEFTMARGIN, 5 - RIGHTMARGIN, 201 - TOPMARGIN, 5 - BOTTOMMARGIN, 218 - END - - IDD_CONFIG_DEBUG, DIALOG - BEGIN - LEFTMARGIN, 5 - RIGHTMARGIN, 287 - TOPMARGIN, 5 - BOTTOMMARGIN, 234 - END -END -#endif // APSTUDIO_INVOKED - -#endif // English (United States) resources -///////////////////////////////////////////////////////////////////////////// - - -///////////////////////////////////////////////////////////////////////////// -// English (United Kingdom) resources - -#if !defined(AFX_RESOURCE_DLL) || defined(AFX_TARG_ENG) -LANGUAGE LANG_ENGLISH, SUBLANG_ENGLISH_UK -#pragma code_page(1252) - -///////////////////////////////////////////////////////////////////////////// -// -// Dialog -// - -IDD_CUBEB DIALOGEX 0, 0, 316, 166 -STYLE DS_SETFONT | DS_MODALFRAME | DS_FIXEDSYS | WS_POPUP | WS_CAPTION | WS_SYSMENU -CAPTION "Cubeb Output Module Settings" -FONT 8, "MS Shell Dlg", 400, 0, 0x1 -BEGIN - DEFPUSHBUTTON "OK",IDOK,105,144,50,14 - PUSHBUTTON "Cancel",IDCANCEL,161,144,50,14 - COMBOBOX IDC_BACKEND,7,17,95,62,CBS_DROPDOWNLIST | WS_VSCROLL | WS_TABSTOP - LTEXT "Backend:",IDC_STATIC,7,7,32,8 - CONTROL "",IDC_LATENCY,"msctls_trackbar32",TBS_AUTOTICKS | WS_TABSTOP,96,93,155,10 - LTEXT "NOTE: Depending on the hardware and drivers, the suggested manual latency might not be used, and Cubeb will choose the closest possible value.",IDC_STATIC,12,111,286,19 - CTEXT "20ms",IDC_LATENCY_LABEL,264,93,35,11 - GROUPBOX "Output Latency",IDC_STATIC,7,57,302,79 - CONTROL "Use smallest possible (may cause issues if the actual value is too small)",IDC_MINIMIZE, - "Button",BS_AUTORADIOBUTTON | WS_GROUP,12,75,258,10 - CONTROL "Use this latency:",IDC_MANUAL,"Button",BS_AUTORADIOBUTTON,12,93,69,10 -END - - -///////////////////////////////////////////////////////////////////////////// -// -// DESIGNINFO -// - -#ifdef APSTUDIO_INVOKED -GUIDELINES DESIGNINFO -BEGIN - IDD_CUBEB, DIALOG - BEGIN - LEFTMARGIN, 7 - RIGHTMARGIN, 309 - TOPMARGIN, 7 - BOTTOMMARGIN, 159 - END -END -#endif // APSTUDIO_INVOKED - -#endif // English (United Kingdom) resources -///////////////////////////////////////////////////////////////////////////// - - -///////////////////////////////////////////////////////////////////////////// -// Spanish (Spain, International Sort) resources - -#if !defined(AFX_RESOURCE_DLL) || defined(AFX_TARG_ESN) -LANGUAGE LANG_SPANISH, SUBLANG_SPANISH_MODERN -#pragma code_page(1252) - -#ifdef APSTUDIO_INVOKED -///////////////////////////////////////////////////////////////////////////// -// -// TEXTINCLUDE -// - -1 TEXTINCLUDE -BEGIN - "resource.h\0" -END - -2 TEXTINCLUDE -BEGIN - "#include ""svnrev.h""\r\n" - "#include ""afxresmw.h""\0" -END - -3 TEXTINCLUDE -BEGIN - "\0" -END - -#endif // APSTUDIO_INVOKED - -#endif // Spanish (Spain, International Sort) resources -///////////////////////////////////////////////////////////////////////////// - - diff --git a/pcsx2/SPU2/Windows/UIHelpers.cpp b/pcsx2/SPU2/Windows/UIHelpers.cpp deleted file mode 100644 index 22c1a6c8e6..0000000000 --- a/pcsx2/SPU2/Windows/UIHelpers.cpp +++ /dev/null @@ -1,71 +0,0 @@ -/* PCSX2 - PS2 Emulator for PCs - * Copyright (C) 2002-2020 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 . - */ - -#include "PrecompiledHeader.h" -#include "SPU2/Global.h" -#include "Dialogs.h" - -int SendDialogMsg(HWND hwnd, int dlgId, UINT code, WPARAM wParam, LPARAM lParam) -{ - return SendMessage(GetDlgItem(hwnd, dlgId), code, wParam, lParam); -} - -void AssignSliderValue(HWND idcwnd, HWND hwndDisplay, int value) -{ - value = std::min(std::max(value, 0), 512); - SendMessage(idcwnd, TBM_SETPOS, TRUE, value); - - wchar_t tbox[32]; - swprintf_s(tbox, L"%d", value); - SetWindowText(hwndDisplay, tbox); -} - -void AssignSliderValue(HWND hWnd, int idc, int editbox, int value) -{ - AssignSliderValue(GetDlgItem(hWnd, idc), GetDlgItem(hWnd, editbox), value); -} - -// Generic slider/scroller message handler. This is succient so long as you -// don't need some kind of secondary event handling functionality, such as -// updating a custom label. -BOOL DoHandleScrollMessage(HWND hwndDisplay, WPARAM wParam, LPARAM lParam) -{ - int wmId = LOWORD(wParam); - int wmEvent = HIWORD(wParam); - - switch (wmId) - { - //case TB_ENDTRACK: - //case TB_THUMBPOSITION: - case TB_LINEUP: - case TB_LINEDOWN: - case TB_PAGEUP: - case TB_PAGEDOWN: - wmEvent = (int)SendMessage((HWND)lParam, TBM_GETPOS, 0, 0); - case TB_THUMBTRACK: - AssignSliderValue((HWND)lParam, hwndDisplay, wmEvent); - break; - - default: - return FALSE; - } - return TRUE; -} - -int GetSliderValue(HWND hWnd, int idc) -{ - int retval = (int)SendMessage(GetDlgItem(hWnd, idc), TBM_GETPOS, 0, 0); - return GetClamped(retval, 0, 512); -} diff --git a/pcsx2/SPU2/Windows/WinConfig.h b/pcsx2/SPU2/Windows/WinConfig.h deleted file mode 100644 index bf2aab4f5f..0000000000 --- a/pcsx2/SPU2/Windows/WinConfig.h +++ /dev/null @@ -1,71 +0,0 @@ -/* PCSX2 - PS2 Emulator for PCs - * Copyright (C) 2002-2020 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 . - */ - -#pragma once - -#ifndef _WIN32_WINNT -#define _WIN32_WINNT 0x0600 -#endif -#include -#include -#include -#include -#include -#include - -#include "resource.h" - -extern HINSTANCE hInstance; - -#define SET_CHECK(idc, value) SendMessage(GetDlgItem(hWnd, idc), BM_SETCHECK, ((value) == 0) ? BST_UNCHECKED : BST_CHECKED, 0) -#define HANDLE_CHECK(idc, hvar) \ - case idc: \ - (hvar) = !(hvar); \ - SendMessage(GetDlgItem(hWnd, idc), BM_SETCHECK, (hvar) ? BST_CHECKED : BST_UNCHECKED, 0); \ - break -#define HANDLE_CHECKNB(idc, hvar) \ - case idc: \ - (hvar) = !(hvar); \ - SendMessage(GetDlgItem(hWnd, idc), BM_SETCHECK, (hvar) ? BST_CHECKED : BST_UNCHECKED, 0) -#define ENABLE_CONTROL(idc, value) EnableWindow(GetDlgItem(hWnd, idc), value) - -#define INIT_SLIDER(idc, minrange, maxrange, tickfreq, pagesize, linesize) \ - SendMessage(GetDlgItem(hWnd, idc), TBM_SETRANGEMIN, FALSE, minrange); \ - SendMessage(GetDlgItem(hWnd, idc), TBM_SETRANGEMAX, FALSE, maxrange); \ - SendMessage(GetDlgItem(hWnd, idc), TBM_SETTICFREQ, tickfreq, 0); \ - SendMessage(GetDlgItem(hWnd, idc), TBM_SETPAGESIZE, 0, pagesize); \ - SendMessage(GetDlgItem(hWnd, idc), TBM_SETLINESIZE, 0, linesize) - -#define HANDLE_SCROLL_MESSAGE(idc, idcDisplay) \ - if ((HWND)lParam == GetDlgItem(hWnd, idc)) \ - return DoHandleScrollMessage(GetDlgItem(hWnd, idcDisplay), wParam, lParam) - - -// *** BEGIN DRIVER-SPECIFIC CONFIGURATION *** -// ------------------------------------------- - -struct CONFIG_XAUDIO2 -{ - wxString Device; - s8 NumBuffers; - - CONFIG_XAUDIO2() - : Device() - , NumBuffers(2) - { - } -}; - -extern CONFIG_XAUDIO2 Config_XAudio2; diff --git a/pcsx2/SPU2/Windows/dsp.cpp b/pcsx2/SPU2/Windows/dsp.cpp deleted file mode 100644 index 7eec069b3d..0000000000 --- a/pcsx2/SPU2/Windows/dsp.cpp +++ /dev/null @@ -1,185 +0,0 @@ -/* PCSX2 - PS2 Emulator for PCs - * Copyright (C) 2002-2020 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 . - */ - -#include "PrecompiledHeader.h" -#include "SPU2/Global.h" - -#include -#include - - -extern "C" { -#include "dsp.h" - -typedef winampDSPHeader* (*pWinampDSPGetHeader2)(); -} - -HMODULE hLib = nullptr; -pWinampDSPGetHeader2 pGetHeader = nullptr; -winampDSPHeader* pHeader = nullptr; - -winampDSPModule* pModule = nullptr; - -HWND hTemp; - -#define USE_A_THREAD -#ifdef USE_A_THREAD - -HANDLE hUpdateThread; -DWORD UpdateThreadId; - -bool running; - -DWORD WINAPI DspUpdateThread(PVOID param); -#endif -s32 DspLoadLibrary(wchar_t* fileName, int modNum) -#ifdef USE_A_THREAD -{ - if (!dspPluginEnabled) - return -1; - - running = true; - hUpdateThread = CreateThread(nullptr, 0, DspUpdateThread, nullptr, 0, &UpdateThreadId); - return (hUpdateThread == INVALID_HANDLE_VALUE); -} - -s32 DspLoadLibrary2(wchar_t* fileName, int modNum) -#endif -{ - if (!dspPluginEnabled) - return -1; - - hLib = LoadLibraryW(fileName); - if (!hLib) - { - return 1; - } - - pGetHeader = (pWinampDSPGetHeader2)GetProcAddress(hLib, "winampDSPGetHeader2"); - - if (!pGetHeader) - { - FreeLibrary(hLib); - hLib = nullptr; - return 1; - } - - pHeader = pGetHeader(); - - pModule = pHeader->getModule(modNum); - - if (!pModule) - { - pGetHeader = nullptr; - pHeader = nullptr; - FreeLibrary(hLib); - hLib = nullptr; - return -1; - } - - pModule->hDllInstance = hLib; - pModule->hwndParent = 0; - pModule->Init(pModule); - - return 0; -} - -void DspCloseLibrary() -#ifdef USE_A_THREAD -{ - if (!dspPluginEnabled) - return; - - PostThreadMessage(UpdateThreadId, WM_QUIT, 0, 0); - running = false; - if (WaitForSingleObject(hUpdateThread, 1000) == WAIT_TIMEOUT) - { - ConLog("SPU2: WARNING: DSP Thread did not close itself in time. Assuming hung. Terminating.\n"); - TerminateThread(hUpdateThread, 1); - } -} - -void DspCloseLibrary2() -#endif -{ - if (!dspPluginEnabled) - return; - - if (hLib) - { - pModule->Quit(pModule); - FreeLibrary(hLib); - } - pModule = nullptr; - pHeader = nullptr; - pGetHeader = nullptr; - hLib = nullptr; -} - -int DspProcess(s16* buffer, int samples) -{ - if (!dspPluginEnabled) - return samples; - - if (hLib) - { - return pModule->ModifySamples(pModule, buffer, samples, 16, 2, SampleRate); - } - return samples; -} - -void DspUpdate() -#ifdef USE_A_THREAD -{ -} - -DWORD WINAPI DspUpdateThread(PVOID param) -{ - if (!dspPluginEnabled) - return -1; - - if (DspLoadLibrary2(dspPlugin, dspPluginModule)) - return -1; - - MSG msg; - while (running) - { - GetMessage(&msg, 0, 0, 0); - if ((msg.hwnd == nullptr) && (msg.message == WM_QUIT)) - { - break; - } - TranslateMessage(&msg); - DispatchMessage(&msg); - } - - DspCloseLibrary2(); - return 0; -} - -#else -{ - if (!dspPluginEnabled) - return; - - MSG msg; - while (PeekMessage(&msg, 0, 0, 0, PM_REMOVE)) - { - TranslateMessage(&msg); - DispatchMessage(&msg); - } -} - -#endif diff --git a/pcsx2/SPU2/Windows/dsp.h b/pcsx2/SPU2/Windows/dsp.h deleted file mode 100644 index 67eeffa857..0000000000 --- a/pcsx2/SPU2/Windows/dsp.h +++ /dev/null @@ -1,57 +0,0 @@ -/* PCSX2 - PS2 Emulator for PCs - * Copyright (C) 2002-2020 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 . - */ - -// DSP plugin interface - -// notes: -// any window that remains in foreground should optimally pass unused -// keystrokes to the parent (winamp's) window, so that the user -// can still control it. As for storing configuration, -// Configuration data should be stored in \plugin.ini -// (look at the vis plugin for configuration code) - -#pragma once - -typedef struct winampDSPModule -{ - char* description; // description - HWND hwndParent; // parent window (filled in by calling app) - HINSTANCE hDllInstance; // instance handle to this DLL (filled in by calling app) - - void (*Config)(struct winampDSPModule* this_mod); // configuration dialog (if needed) - int (*Init)(struct winampDSPModule* this_mod); // 0 on success, creates window, etc (if needed) - - // modify waveform samples: returns number of samples to actually write - // (typically numsamples, but no more than twice numsamples, and no less than half numsamples) - // numsamples should always be at least 128. should, but I'm not sure - int (*ModifySamples)(struct winampDSPModule* this_mod, short int* samples, int numsamples, int bps, int nch, int srate); - - void (*Quit)(struct winampDSPModule* this_mod); // called when unloading - - void* userData; // user data, optional -} winampDSPModule; - -typedef struct -{ - int version; // DSP_HDRVER - char* description; // description of library - winampDSPModule* (*getModule)(int); // module retrieval function -} winampDSPHeader; - -// exported symbols -typedef winampDSPHeader* (*winampDSPGetHeaderType)(); - -// header version: 0x20 == 0.20 == winamp 2.0 -#define DSP_HDRVER 0x20 diff --git a/pcsx2/SPU2/Windows/resource.h b/pcsx2/SPU2/Windows/resource.h deleted file mode 100644 index 32c75f3c5e..0000000000 --- a/pcsx2/SPU2/Windows/resource.h +++ /dev/null @@ -1,64 +0,0 @@ -//{{NO_DEPENDENCIES}} -// Microsoft Visual C++ generated include file. -// Used by SPU2.rc -#define IDD_CONFIG 9 -#define IDD_DEBUG 105 -#define IDD_CONFIG_SOUNDTOUCH 117 -#define IDD_CONFIG_DEBUG 118 -#define IDD_CUBEB 119 -#define IDC_DUMPREGS 1003 -#define IDC_DUMPMEM 1004 -#define IDC_DUMPCORE 1005 -#define IDC_LOGWAVE 1006 -#define IDC_LOGDMA 1007 -#define IDC_LOGREGS 1008 -#define IDC_DEBUG 1009 -#define IDC_DEBUG_ENABLE 1010 -#define IDC_INTERPOLATE 1011 -#define IDC_OUTPUT 1013 -#define IDC_BUFFERS_SLIDER 1014 -#define IDC_SPEAKERS 1015 -#define IDC_SPEAKERS_TEXT 1016 -#define IDC_MSGKEY 1020 -#define IDC_MSGDMA 1021 -#define IDC_MSGADMA 1022 -#define IDC_MSGVOICE 1023 -#define IDC_MSGSHOW 1024 -#define IDC_OUTCONF 1028 -#define IDC_DSP_ENABLE 1029 -#define IDC_DS_DEVICE 1032 -#define IDC_DBG_OVERRUNS 1038 -#define IDC_DBG_CACHE 1039 -#define IDC_LATENCY_SLIDER 1040 -#define IDC_LATENCY_LABEL 1041 -#define ICD_LR_CENTER_SLIDER 1042 -#define IDC_SEQLEN_SLIDER 1043 -#define IDC_SEEKWIN_SLIDER 1044 -#define IDC_OVERLAP_SLIDER 1045 -#define IDC_MSG_PUBLIC_BUILD 1048 -#define IDC_RESET_DEFAULTS 1057 -#define IDC_OPEN_CONFIG_SOUNDTOUCH 1058 -#define IDC_OPEN_CONFIG_DEBUG 1059 -#define IDC_GLOBALFOCUS_DISABLE 1060 -#define IDC_GLOBALFOCUS_DISABLE2 1061 -#define IDC_USE_HARDWARE 1062 -#define IDC_SYNCHMODE 1064 -#define IDC_DEBUG_OTHERS 1065 -#define IDC_DEBUG_VISUAL 1066 -#define IDC_VOLUME_LABEL 1067 -#define IDC_VOLUME_SLIDER 1068 -#define IDC_MINIMIZE 1069 -#define IDC_MANUAL 1070 -#define IDC_BACKEND 1071 -#define IDC_LATENCY 1072 - -// Next default values for new objects -// -#ifdef APSTUDIO_INVOKED -#ifndef APSTUDIO_READONLY_SYMBOLS -#define _APS_NEXT_RESOURCE_VALUE 120 -#define _APS_NEXT_COMMAND_VALUE 40001 -#define _APS_NEXT_CONTROL_VALUE 1074 -#define _APS_NEXT_SYMED_VALUE 101 -#endif -#endif diff --git a/pcsx2/SPU2/wx/wxConfig.cpp b/pcsx2/SPU2/wx/wxConfig.cpp deleted file mode 100644 index 9465ec7ca6..0000000000 --- a/pcsx2/SPU2/wx/wxConfig.cpp +++ /dev/null @@ -1,490 +0,0 @@ -/* PCSX2 - PS2 Emulator for PCs - * Copyright (C) 2002-2021 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 . - */ - -#include "PrecompiledHeader.h" -#include "SPU2/Config.h" -#if !defined(_WIN32) // BSD, Macos -#include "SPU2/Linux/Config.h" -#include "SPU2/Linux/Dialogs.h" -#endif -#include "SPU2/Global.h" -#include "wxConfig.h" - -static const wchar_t* s_backend_names[][2] = { - {L"Automatic", L""}, -#ifdef __linux__ - {L"ALSA", L"alsa"}, - {L"JACK", L"jack"}, - {L"PulseAudio", L"pulseaudio"}, -#elif defined(__APPLE__) - {L"AudioUnit", L"audiounit"}, -#endif -}; - -MixerTab::MixerTab(wxWindow* parent) - : wxPanel(parent, wxID_ANY) -{ - auto* top_box = new wxBoxSizer(wxVERTICAL); - - // Mixing Settings - top_box->Add(new wxStaticText(this, wxID_ANY, "Interpolation"), wxSizerFlags().Centre()); - - wxArrayString interpolation_entries; - interpolation_entries.Add("Nearest (Fastest / worst quality)"); - interpolation_entries.Add("Linear (Simple / okay sound)"); - interpolation_entries.Add("Cubic (Fake highs / okay sound)"); - interpolation_entries.Add("Hermite (Better highs / okay sound)"); - interpolation_entries.Add("Catmull-Rom (PS2-like / good sound)"); - interpolation_entries.Add("Gaussian (PS2-like / great sound)"); - - m_inter_select = new wxChoice(this, wxID_ANY, wxDefaultPosition, wxDefaultSize, interpolation_entries); - - // Latency Slider - const int min_latency = SynchMode == 0 ? LATENCY_MIN_TIMESTRETCH : LATENCY_MIN; - - m_latency_box = new wxStaticBoxSizer(wxVERTICAL, this, "Latency"); - m_latency_slider = new wxSlider(this, wxID_ANY, SndOutLatencyMS, min_latency, LATENCY_MAX, wxDefaultPosition, wxDefaultSize, wxSL_LABELS); - m_latency_box->Add(m_latency_slider, wxSizerFlags().Expand()); - - // Volume Slider - m_volume_box = new wxStaticBoxSizer(wxVERTICAL, this, "Volume"); - m_volume_slider = new wxSlider(this, wxID_ANY, FinalVolume * 100, 0, 200, wxDefaultPosition, wxDefaultSize, wxSL_LABELS); - m_volume_box->Add(m_volume_slider, wxSizerFlags().Expand()); - - m_audio_box = new wxBoxSizer(wxVERTICAL); - m_audio_box->Add(new wxStaticText(this, wxID_ANY, "Audio Expansion Mode"), wxSizerFlags().Centre()); - - wxArrayString audio_entries; - audio_entries.Add("Stereo (None, Default)"); - audio_entries.Add("Quadrafonic"); - audio_entries.Add("Surround 5.1"); - audio_entries.Add("Surround 7.1"); - m_audio_select = new wxChoice(this, wxID_ANY, wxDefaultPosition, wxDefaultSize, audio_entries); - m_audio_box->Add(m_audio_select, wxSizerFlags().Expand()); - - top_box->Add(m_inter_select, wxSizerFlags().Centre()); - top_box->Add(m_latency_box, wxSizerFlags().Expand()); - top_box->Add(m_volume_box, wxSizerFlags().Expand()); - top_box->Add(m_audio_box, wxSizerFlags().Expand()); - - SetSizerAndFit(top_box); -} - -void MixerTab::Load() -{ - m_inter_select->SetSelection(Interpolation); - - m_audio_select->SetSelection(numSpeakers); - - m_volume_slider->SetValue(FinalVolume * 100); - m_latency_slider->SetValue(SndOutLatencyMS); -} - -void MixerTab::Save() -{ - Interpolation = m_inter_select->GetSelection(); - - numSpeakers = m_audio_select->GetSelection(); - - FinalVolume = m_volume_slider->GetValue() / 100.0; - SndOutLatencyMS = m_latency_slider->GetValue(); -} - -void MixerTab::Update() -{ -} - -void MixerTab::CallUpdate(wxCommandEvent& /*event*/) -{ - Update(); -} - -SyncTab::SyncTab(wxWindow* parent) - : wxPanel(parent, wxID_ANY) -{ - auto* top_box = new wxBoxSizer(wxVERTICAL); - - top_box->Add(new wxStaticText(this, wxID_ANY, "Synchronization"), wxSizerFlags().Centre()); - - wxArrayString sync_entries; - sync_entries.Add("TimeStretch (Recommended)"); - sync_entries.Add("Async Mix (Breaks some games!)"); - sync_entries.Add("None (Audio can skip.)"); - m_sync_select = new wxChoice(this, wxID_ANY, wxDefaultPosition, wxDefaultSize, sync_entries); - - auto* adv_box = new wxStaticBoxSizer(wxVERTICAL, this, "Advanced"); - - auto* babble_label = new wxStaticText(this, wxID_ANY, - "For fine-tuning time stretching.\n" - "Larger is better for slowdown, && smaller for speedup (60+ fps).\n" - "All options in microseconds.", - wxDefaultPosition, wxDefaultSize, wxALIGN_CENTRE_HORIZONTAL); - babble_label->Wrap(300); - - adv_box->Add(babble_label, wxSizerFlags().Centre()); - - auto* soundtouch_grid = new wxFlexGridSizer(2, 10, 50); - - seq_spin = new wxSpinCtrl(this, wxID_ANY, wxEmptyString, wxDefaultPosition, wxDefaultSize, wxALIGN_RIGHT, SoundtouchCfg::SequenceLen_Min, SoundtouchCfg::SequenceLen_Max, SoundtouchCfg::SequenceLenMS); - auto* seq_label = new wxStaticText(this, wxID_ANY, "Sequence Length"); - - seek_spin = new wxSpinCtrl(this, wxID_ANY, wxEmptyString, wxDefaultPosition, wxDefaultSize, wxALIGN_RIGHT, SoundtouchCfg::SeekWindow_Min, SoundtouchCfg::SeekWindow_Max, SoundtouchCfg::SeekWindowMS); - auto* seek_label = new wxStaticText(this, wxID_ANY, "Seek Window Size"); - - overlap_spin = new wxSpinCtrl(this, wxID_ANY, wxEmptyString, wxDefaultPosition, wxDefaultSize, wxALIGN_RIGHT, SoundtouchCfg::Overlap_Min, SoundtouchCfg::Overlap_Max, SoundtouchCfg::OverlapMS); - auto* overlap_label = new wxStaticText(this, wxID_ANY, "Overlap"); - - soundtouch_grid->Add(seq_label, wxSizerFlags().Border(wxALL, 5)); - soundtouch_grid->Add(seq_spin, wxSizerFlags().Expand().Right()); - soundtouch_grid->Add(seek_label, wxSizerFlags().Border(wxALL, 5)); - soundtouch_grid->Add(seek_spin, wxSizerFlags().Expand().Right()); - soundtouch_grid->Add(overlap_label, wxSizerFlags().Border(wxALL, 5)); - soundtouch_grid->Add(overlap_spin, wxSizerFlags().Expand().Right()); - - adv_box->Add(soundtouch_grid); - - reset_button = new wxButton(this, wxID_ANY, "Reset To Defaults"); - adv_box->Add(reset_button, wxSizerFlags().Centre().Border(wxALL, 5)); - - top_box->Add(m_sync_select, wxSizerFlags().Centre()); - top_box->Add(adv_box, wxSizerFlags().Expand().Centre()); - SetSizerAndFit(top_box); - Bind(wxEVT_BUTTON, &SyncTab::OnButtonClicked, this); - Bind(wxEVT_CHOICE, &SyncTab::CallUpdate, this); -} - -void SyncTab::Load() -{ - m_sync_select->SetSelection(SynchMode); - - SoundtouchCfg::ReadSettings(); - seq_spin->SetValue(SoundtouchCfg::SequenceLenMS); - seek_spin->SetValue(SoundtouchCfg::SeekWindowMS); - overlap_spin->SetValue(SoundtouchCfg::OverlapMS); -} - -void SyncTab::Save() -{ - SynchMode = m_sync_select->GetSelection(); - - SoundtouchCfg::SequenceLenMS = seq_spin->GetValue(); - SoundtouchCfg::SeekWindowMS = seek_spin->GetValue(); - SoundtouchCfg::OverlapMS = overlap_spin->GetValue(); - SoundtouchCfg::WriteSettings(); -} - -void SyncTab::Update() -{ - seq_spin->Enable(m_sync_select->GetCurrentSelection() == 0); - seek_spin->Enable(m_sync_select->GetCurrentSelection() == 0); - overlap_spin->Enable(m_sync_select->GetCurrentSelection() == 0); -} - -void SyncTab::CallUpdate(wxCommandEvent& /*event*/) -{ - Update(); -} - -void SyncTab::OnButtonClicked(wxCommandEvent& event) -{ - seq_spin->SetValue(30); - seek_spin->SetValue(20); - overlap_spin->SetValue(10); -} - -DebugTab::DebugTab(wxWindow* parent) - : wxPanel(parent, wxID_ANY) -{ - auto* top_box = new wxBoxSizer(wxVERTICAL); - - debug_check = new wxCheckBox(this, wxID_ANY, "Enable Debug Options"); - show_check = new wxCheckBox(this, wxID_ANY, "Show in console"); - top_box->Add(debug_check, wxSizerFlags().Expand()); - top_box->Add(show_check); - - m_console_box = new wxStaticBoxSizer(wxVERTICAL, this, "Events"); - auto* console_grid = new wxFlexGridSizer(2, 0, 0); - - key_check = new wxCheckBox(this, wxID_ANY, "Key On/Off"); - voice_check = new wxCheckBox(this, wxID_ANY, "Voice Stop"); - dma_check = new wxCheckBox(this, wxID_ANY, "DMA Operations"); - autodma_check = new wxCheckBox(this, wxID_ANY, "AutoDMA Operations"); - buffer_check = new wxCheckBox(this, wxID_ANY, "Buffer Over/Underruns"); - adpcm_check = new wxCheckBox(this, wxID_ANY, "ADPCM Cache"); - - console_grid->Add(key_check); - console_grid->Add(voice_check); - console_grid->Add(dma_check); - console_grid->Add(autodma_check); - console_grid->Add(buffer_check); - console_grid->Add(adpcm_check); - m_console_box->Add(console_grid); - - m_log_only_box = new wxStaticBoxSizer(wxVERTICAL, this, "Log Only"); - auto* log_grid = new wxFlexGridSizer(2, 0, 0); - - dma_actions_check = new wxCheckBox(this, wxID_ANY, "Register/DMA Actions"); - dma_writes_check = new wxCheckBox(this, wxID_ANY, "DMA Writes"); - auto_output_check = new wxCheckBox(this, wxID_ANY, "Audio Output"); - - log_grid->Add(dma_actions_check); - log_grid->Add(dma_writes_check); - log_grid->Add(auto_output_check); - m_log_only_box->Add(log_grid); - - dump_box = new wxStaticBoxSizer(wxVERTICAL, this, "Dump on Close"); - auto* dump_grid = new wxFlexGridSizer(2, 0, 0); - - core_voice_check = new wxCheckBox(this, wxID_ANY, "Core && Voice Stats"); - memory_check = new wxCheckBox(this, wxID_ANY, "Memory Contents"); - register_check = new wxCheckBox(this, wxID_ANY, "Register Data"); - dump_grid->Add(core_voice_check); - dump_grid->Add(memory_check); - dump_grid->Add(register_check); - dump_box->Add(dump_grid); - - top_box->Add(m_console_box, wxSizerFlags().Expand()); - top_box->Add(m_log_only_box, wxSizerFlags().Expand()); - top_box->Add(dump_box, wxSizerFlags().Expand()); - - SetSizerAndFit(top_box); - Bind(wxEVT_CHECKBOX, &DebugTab::CallUpdate, this); -} - -void DebugTab::Load() -{ - debug_check->SetValue(DebugEnabled); - - show_check->SetValue(_MsgToConsole); - key_check->SetValue(_MsgKeyOnOff); - voice_check->SetValue(_MsgVoiceOff); - dma_check->SetValue(_MsgDMA); - autodma_check->SetValue(_MsgAutoDMA); - buffer_check->SetValue(_MsgOverruns); - adpcm_check->SetValue(_MsgCache); - - dma_actions_check->SetValue(_AccessLog); - dma_writes_check->SetValue(_DMALog); - auto_output_check->SetValue(_WaveLog); - - core_voice_check->SetValue(_CoresDump); - memory_check->SetValue(_MemDump); - register_check->SetValue(_RegDump); - Update(); -} - -void DebugTab::Save() -{ - DebugEnabled = debug_check->GetValue(); - - _MsgToConsole = show_check->GetValue(); - _MsgKeyOnOff = key_check->GetValue(); - _MsgVoiceOff = voice_check->GetValue(); - _MsgDMA = dma_check->GetValue(); - _MsgAutoDMA = autodma_check->GetValue(); - _MsgOverruns = buffer_check->GetValue(); - _MsgCache = adpcm_check->GetValue(); - - _AccessLog = dma_actions_check->GetValue(); - _DMALog = dma_writes_check->GetValue(); - _WaveLog = auto_output_check->GetValue(); - - _CoresDump = core_voice_check->GetValue(); - _MemDump = memory_check->GetValue(); - _RegDump = register_check->GetValue(); -} - -void DebugTab::Update() -{ - if (debug_check->GetValue()) - { - show_check->Enable(); - - key_check->Enable(); - voice_check->Enable(); - dma_check->Enable(); - autodma_check->Enable(); - buffer_check->Enable(); - adpcm_check->Enable(); - - dma_actions_check->Enable(); - dma_writes_check->Enable(); - auto_output_check->Enable(); - - core_voice_check->Enable(); - memory_check->Enable(); - register_check->Enable(); - } - else - { - show_check->Disable(); - - key_check->Disable(); - voice_check->Disable(); - dma_check->Disable(); - autodma_check->Disable(); - buffer_check->Disable(); - adpcm_check->Disable(); - - dma_actions_check->Disable(); - dma_writes_check->Disable(); - auto_output_check->Disable(); - - core_voice_check->Disable(); - memory_check->Disable(); - register_check->Disable(); - } -} - -void DebugTab::CallUpdate(wxCommandEvent& /*event*/) -{ - Update(); -} - -Dialog::Dialog() - : wxDialog(nullptr, wxID_ANY, "Audio Settings", wxDefaultPosition, wxDefaultSize, wxCAPTION | wxCLOSE_BOX) -{ - m_top_box = new wxBoxSizer(wxVERTICAL); - auto* module_box = new wxBoxSizer(wxVERTICAL); - - // Module - module_box->Add(new wxStaticText(this, wxID_ANY, "Module"), wxSizerFlags().Centre()); - - wxArrayString module_entries; - module_entries.Add("No Sound (Emulate SPU2 only)"); -#ifdef SPU2X_CUBEB - module_entries.Add("Cubeb (Cross-platform)"); -#endif - m_module_select = new wxChoice(this, wxID_ANY, wxDefaultPosition, wxDefaultSize, module_entries); - module_box->Add(m_module_select, wxSizerFlags().Centre()); - -#ifdef SPU2X_CUBEB - // Portaudio - m_cubeb_box = new wxBoxSizer(wxVERTICAL); - m_cubeb_text = new wxStaticText(this, wxID_ANY, "Backend"); - m_cubeb_box->Add(m_cubeb_text, wxSizerFlags().Centre()); - - m_cubeb_select = new wxChoice(this, wxID_ANY, wxDefaultPosition, wxDefaultSize); - for (size_t i = 0; i < std::size(s_backend_names); i++) - m_cubeb_select->Append(s_backend_names[i][0]); - m_cubeb_box->Add(m_cubeb_select, wxSizerFlags().Centre()); -#endif - -#ifdef SPU2X_CUBEB - module_box->Add(m_cubeb_box, wxSizerFlags().Expand()); -#endif - - m_top_box->Add(module_box, wxSizerFlags().Centre().Border(wxALL, 5)); - - auto* book = new wxNotebook(this, wxID_ANY, wxDefaultPosition, wxDefaultSize); - m_mixer_panel = new MixerTab(book); - m_sync_panel = new SyncTab(book); - m_debug_panel = new DebugTab(book); - - book->AddPage(m_mixer_panel, "Mixing", true); - book->AddPage(m_sync_panel, "Sync"); - book->AddPage(m_debug_panel, "Debug"); - - m_top_box->Add(book, wxSizerFlags().Centre()); - m_top_box->Add(CreateStdDialogButtonSizer(wxOK | wxCANCEL), wxSizerFlags().Right()); - - SetSizerAndFit(m_top_box); - - Bind(wxEVT_CHOICE, &Dialog::CallReconfigure, this); - Bind(wxEVT_CHECKBOX, &Dialog::CallReconfigure, this); -} - -Dialog::~Dialog() -{ -} - -void Dialog::Reconfigure() -{ - const int mod = m_module_select->GetCurrentSelection(); - bool show_cubeb = false; - - switch (mod) - { - case 0: - show_cubeb = false; - break; - - case 1: - show_cubeb = true; - break; - - default: - show_cubeb = false; - break; - } -#ifdef SPU2X_CUBEB - m_top_box->Show(m_cubeb_box, show_cubeb, true); -#endif - - // Recalculating both of these accounts for if neither was showing initially. - m_top_box->Layout(); - SetSizerAndFit(m_top_box); -} - -void Dialog::CallReconfigure(wxCommandEvent& event) -{ - Reconfigure(); -} - -void Dialog::Load() -{ - m_module_select->SetSelection(OutputModule); -#ifdef SPU2X_CUBEB - wxString backend; - CfgReadStr(L"Cubeb", L"BackendName", backend, L""); - for (size_t i = 0; i < std::size(s_backend_names); i++) - { - if (backend == s_backend_names[i][1]) - { - m_cubeb_select->SetSelection(static_cast(i)); - break; - } - } -#endif - - m_mixer_panel->Load(); - m_sync_panel->Load(); - m_debug_panel->Load(); - - Reconfigure(); -} - -void Dialog::Save() -{ - OutputModule = m_module_select->GetSelection(); - -#ifdef SPU2X_CUBEB - const int backend_selection = m_cubeb_select->GetSelection(); - if (backend_selection >= 0) - CfgWriteStr(L"Cubeb", L"BackendName", s_backend_names[backend_selection][1]); -#endif - - m_mixer_panel->Save(); - m_sync_panel->Save(); - m_debug_panel->Save(); -} - -// Main -void Dialog::Display() -{ - Load(); - if (ShowModal() == wxID_OK) - Save(); -} diff --git a/pcsx2/SPU2/wx/wxConfig.h b/pcsx2/SPU2/wx/wxConfig.h deleted file mode 100644 index 8f25ee02f7..0000000000 --- a/pcsx2/SPU2/wx/wxConfig.h +++ /dev/null @@ -1,97 +0,0 @@ -/* PCSX2 - PS2 Emulator for PCs - * Copyright (C) 2002-2021 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 . - */ - -#pragma once - -#include -#include -#include -#include -#include - -#if defined(__unix__) || defined(__APPLE__) -#include -#include -#include "Config.h" -#endif - -class MixerTab : public wxPanel -{ -public: - wxChoice *m_inter_select, *m_audio_select; - wxSlider *m_latency_slider, *m_volume_slider; - wxStaticBoxSizer *m_volume_box, *m_latency_box; - wxBoxSizer* m_audio_box; - - MixerTab(wxWindow* parent); - void Load(); - void Save(); - void Update(); - void CallUpdate(wxCommandEvent& event); -}; - -class SyncTab : public wxPanel -{ -public: - wxChoice* m_sync_select; - wxButton *launch_adv_dialog, *reset_button; - wxSpinCtrl *seq_spin, *seek_spin, *overlap_spin; - - SyncTab(wxWindow* parent); - void Load(); - void Save(); - void Update(); - void CallUpdate(wxCommandEvent& event); - void OnButtonClicked(wxCommandEvent& event); -}; - -class DebugTab : public wxPanel -{ -public: - wxCheckBox* debug_check; - wxButton* launch_debug_dialog; - - wxStaticBoxSizer *m_console_box, *m_log_only_box, *dump_box; - wxCheckBox* show_check; - wxCheckBox *key_check, *voice_check, *dma_check, *autodma_check, *buffer_check, *adpcm_check; - wxCheckBox *dma_actions_check, *dma_writes_check, *auto_output_check; - wxCheckBox *core_voice_check, *memory_check, *register_check; - - DebugTab(wxWindow* parent); - void Load(); - void Save(); - void Update(); - void CallUpdate(wxCommandEvent& event); -}; - -class Dialog : public wxDialog -{ - wxBoxSizer *m_top_box, *m_cubeb_box; - wxChoice *m_module_select, *m_cubeb_select; - wxStaticText *m_cubeb_text; - - MixerTab* m_mixer_panel; - SyncTab* m_sync_panel; - DebugTab* m_debug_panel; - -public: - Dialog(); - ~Dialog(); - void Display(); - void Load(); - void Save(); - void Reconfigure(); - void CallReconfigure(wxCommandEvent& event); -}; diff --git a/pcsx2/USB/USBNull.cpp b/pcsx2/USB/USBNull.cpp deleted file mode 100644 index 9c7d7b5695..0000000000 --- a/pcsx2/USB/USBNull.cpp +++ /dev/null @@ -1,35 +0,0 @@ -/* PCSX2 - PS2 Emulator for PCs - * Copyright (C) 2020 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 . - */ - -#include "PrecompiledHeader.h" - -#include "USBNull.h" - -void USBconfigure() {} - -s32 USBinit() { return 0; } -void USBasync(u32 cycles) {} -void USBshutdown() {} -void USBclose() {} -void USBreset() {} -s32 USBopen(const WindowInfo& wi) { return 0; } -s32 USBfreeze(FreezeAction mode, freezeData* data) { return 0; } - -u8 USBread8(u32 addr) { return 0; } -u16 USBread16(u32 addr) { return 0; } -u32 USBread32(u32 addr) { return 0; } -void USBwrite8(u32 addr, u8 value) {} -void USBwrite16(u32 addr, u16 value) {} -void USBwrite32(u32 addr, u32 value) {} diff --git a/pcsx2/USB/USBNull.h b/pcsx2/USB/USBNull.h deleted file mode 100644 index 2148c002a7..0000000000 --- a/pcsx2/USB/USBNull.h +++ /dev/null @@ -1,42 +0,0 @@ -/* PCSX2 - PS2 Emulator for PCs - * Copyright (C) 2002-2020 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 . - */ - -#pragma once - -#include -#include -#include -#include - -#include "SaveState.h" - -struct WindowInfo; - -void USBconfigure(); - -s32 USBinit(); -void USBasync(u32 cycles); -void USBshutdown(); -void USBclose(); -void USBreset(); -s32 USBopen(const WindowInfo& wi); -s32 USBfreeze(FreezeAction mode, freezeData* data); - -u8 USBread8(u32 addr); -u16 USBread16(u32 addr); -u32 USBread32(u32 addr); -void USBwrite8(u32 addr, u8 value); -void USBwrite16(u32 addr, u16 value); -void USBwrite32(u32 addr, u32 value); diff --git a/pcsx2/gui/App.h b/pcsx2/gui/App.h deleted file mode 100644 index 4505887a24..0000000000 --- a/pcsx2/gui/App.h +++ /dev/null @@ -1,737 +0,0 @@ -/* PCSX2 - PS2 Emulator for PCs - * Copyright (C) 2002-2021 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 . - */ - -#pragma once - -#include "wxAppWithHelpers.h" -#include "common/WindowInfo.h" - -#include -#include -#include - -#include "SysThreads.h" -#include "pxEventThread.h" - -#include "AppCommon.h" -#include "AppCoreThread.h" -#include "RecentIsoList.h" -#include "DriveList.h" - -class DisassemblyDialog; -struct HostKeyEvent; - -#include "GS.h" -#include "System.h" - -// TODO: Not the best location for this, but it needs to be accessed by MTGS etc. -extern WindowInfo g_gs_window_info; - -typedef void FnType_OnThreadComplete(const wxCommandEvent& evt); -typedef void (Pcsx2App::*FnPtr_Pcsx2App)(); - -wxDECLARE_EVENT(pxEvt_SetSettingsPage, wxCommandEvent); - -// This is used when GS is handling its own window. Messages from the PAD -// are piped through to an app-level message handler, which dispatches them through -// the universal Accelerator table. -static const int pxID_PadHandler_Keydown = 8030; - -// ID and return code used for modal popups that have a custom button. -static const wxWindowID pxID_CUSTOM = wxID_LOWEST - 1; - -// Return code used by first time wizard if the dialog needs to be automatically recreated -// (assigned an arbitrary value) -static const wxWindowID pxID_RestartWizard = wxID_LOWEST - 100; - - -// ------------------------------------------------------------------------ -// All Menu Options for the Main Window! :D -// ------------------------------------------------------------------------ - -enum TopLevelMenuIndices -{ - TopLevelMenu_Pcsx2 = 0, - TopLevelMenu_Cdvd, - TopLevelMenu_Config, - TopLevelMenu_Window, - TopLevelMenu_Capture, - TopLevelMenu_InputRecording, - TopLevelMenu_Help -}; - -enum MenuIdentifiers -{ - // Main Menu Section - MenuId_Boot = 1, - MenuId_Emulation, - MenuId_Config, // General config. - MenuId_Video, // Video options filled in by GS. - MenuId_Audio, // audio options filled in by SPU2. - MenuId_Misc, // Misc options and help! - - MenuId_Exit = wxID_EXIT, - MenuId_About = wxID_ABOUT, - - MenuId_EndTopLevel = 20, - - // Run SubSection - MenuId_Cdvd_Source, - MenuId_Src_Iso, - MenuId_Src_Disc, - MenuId_Src_NoDisc, - MenuId_Boot_Iso, // Opens submenu with Iso browser, and recent isos. - MenuId_RecentIsos_reservedStart, - MenuId_IsoBrowse = MenuId_RecentIsos_reservedStart + 100, // Open dialog, runs selected iso. - MenuId_IsoClear, - MenuId_IsoClearMissing, - MenuId_DriveSelector, - MenuId_DriveListRefresh, - MenuId_Ask_On_Booting, - MenuId_Boot_CDVD, - MenuId_Boot_CDVD2, - MenuId_Boot_ELF, - //MenuId_Boot_Recent, // Menu populated with recent source bootings - - - MenuId_Sys_SuspendResume, // suspends/resumes active emulation - MenuId_Sys_Shutdown, // Closes virtual machine, wipes states - MenuId_Sys_LoadStates, // Opens load states submenu - MenuId_Sys_SaveStates, // Opens save states submenu - MenuId_EnableBackupStates, // Checkbox to enable/disables savestates backup - MenuId_GameSettingsSubMenu, - MenuId_EnablePatches, - MenuId_EnableCheats, - MenuId_GSDump, - MenuId_EnableWideScreenPatches, - MenuId_EnableInputRecording, - MenuId_EnableLuaTools, - MenuId_EnableHostFs, - - MenuId_State_Load, - MenuId_State_LoadFromFile, - MenuId_State_Load01, // first of many load slots - MenuId_State_LoadBackup = MenuId_State_Load01 + 20, - MenuId_State_Save, - MenuId_State_SaveToFile, - MenuId_State_Save01, // first of many save slots - - MenuId_State_EndSlotSection = MenuId_State_Save01 + 20, - - // Config Subsection - MenuId_Config_SysSettings, - MenuId_Config_McdSettings, - MenuId_Config_AppSettings, - MenuId_Config_Language, - - MenuId_Config_GS, - MenuId_Config_PAD, - MenuId_Config_SPU2, - MenuId_Config_CDVD, - MenuId_Config_USB, - MenuId_Config_FireWire, - MenuId_Config_DEV9, - MenuId_Config_Patches, - - MenuId_Config_Multitap0Toggle, - MenuId_Config_Multitap1Toggle, - MenuId_Config_FastBoot, - - MenuId_Help_GetStarted, - MenuId_Help_Compatibility, - MenuId_Help_Forums, - MenuId_Help_Website, - MenuId_Help_Wiki, - MenuId_Help_Github, - - MenuId_Video_CoreSettings = 0x200, // includes frame timings and skippings settings - MenuId_Video_WindowSettings, - - // Miscellaneous Menu! (Misc) - MenuId_Console, // Enable console - MenuId_ChangeLang, // Change language (resets first time wizard to show on next start) - MenuId_Console_Stdio, // Enable Stdio - - // Debug Subsection - MenuId_Debug_Open, // opens the debugger window / starts a debug session - MenuId_Debug_MemoryDump, - MenuId_Debug_Logging, // dialog for selection additional log options - MenuId_Debug_CreateBlockdump, - MenuId_Config_ResetAll, - - // Capture Subsection - MenuId_Capture_Video, - MenuId_Capture_Video_Record, - MenuId_Capture_Video_Stop, - MenuId_Capture_Video_IncludeAudio, - MenuId_Capture_Screenshot, - MenuId_Capture_Screenshot_Screenshot, - MenuId_Capture_Screenshot_Screenshot_As, - - // Input Recording Subsection - MenuId_Recording_New, - MenuId_Recording_Play, - MenuId_Recording_Stop, - MenuId_Recording_Settings, - MenuId_Recording_Config_FrameAdvance, - MenuId_Recording_TogglePause, - MenuId_Recording_FrameAdvance, - MenuId_Recording_ToggleRecordingMode, - MenuId_Recording_VirtualPad_Port0, - MenuId_Recording_VirtualPad_Port1, - - // Subsection - MenuId_PINE, - MenuId_PINE_Enable, - MenuId_PINE_Settings, - -}; - -// -------------------------------------------------------------------------------------- -// AppImageIds - Config and Toolbar Images and Icons -// -------------------------------------------------------------------------------------- -struct AppImageIds -{ - struct ConfigIds - { - int Paths, - Speedhacks, - Gamefixes, - MemoryCard, - Video, - Cpu; - - ConfigIds() - { - Paths = - Speedhacks = Gamefixes = - Video = Cpu = - MemoryCard = -1; - } - } Config; - - struct ToolbarIds - { - int Settings, - Play, - Resume; - - ToolbarIds() - { - Settings = -1; - Play = -1; - Resume = -1; - } - } Toolbars; -}; - -// ------------------------------------------------------------------------------------------- -// pxAppResources -// ------------------------------------------------------------------------------------------- -// Container class for resources that should (or must) be unloaded prior to the ~wxApp() destructor. -// (typically this object is deleted at OnExit() or just prior to OnExit()). -// -class pxAppResources -{ -public: - AppImageIds ImageId; - - std::unique_ptr ConfigImages; - std::unique_ptr ToolbarImages; - std::unique_ptr IconBundle; - std::unique_ptr Bitmap_Logo; - std::unique_ptr ScreenshotBitmap; - - pxAppResources(); - virtual ~pxAppResources(); -}; - -class StartupOptions -{ -public: - bool ForceWizard; - bool ForceConsole; - bool PortableMode; - - // Disables the fast boot option when auto-running games. This option only applies - // if SysAutoRun is also true. - bool NoFastBoot; - - // Specifies the Iso file to boot; used only if SysAutoRun is enabled and CdvdSource - // is set to ISO. - wxString IsoFile; - - wxString ElfFile; - - wxString GameLaunchArgs; - - // Specifies the CDVD source type to use when AutoRunning - CDVD_SourceType CdvdSource; - - // Indicates if PCSX2 should autorun the configured CDVD source and/or ISO file. - bool SysAutoRun; - bool SysAutoRunElf; - bool SysAutoRunIrx; - - StartupOptions() - { - ForceWizard = false; - ForceConsole = false; - PortableMode = false; - NoFastBoot = false; - SysAutoRun = false; - SysAutoRunElf = false; - SysAutoRunIrx = false; - CdvdSource = CDVD_SourceType::NoDisc; - } -}; - -enum GsWindowMode_t -{ - GsWinMode_Unspecified = 0, - GsWinMode_Windowed, - GsWinMode_Fullscreen, -}; - -class CommandlineOverrides -{ -public: - wxDirName SettingsFolder; - wxFileName VmSettingsFile; - - bool DisableSpeedhacks; - bool ProfilingMode; - - // Note that gamefixes in this array should only be honored if the - // "HasCustomGamefixes" boolean is also enabled. - Pcsx2Config::GamefixOptions Gamefixes; - bool ApplyCustomGamefixes; - - GsWindowMode_t GsWindowMode; - -public: - CommandlineOverrides() - { - DisableSpeedhacks = false; - ApplyCustomGamefixes = false; - GsWindowMode = GsWinMode_Unspecified; - ProfilingMode = false; - } - - // Returns TRUE if either speedhacks or gamefixes are being overridden. - bool HasCustomHacks() const - { - return DisableSpeedhacks || ApplyCustomGamefixes; - } - - void RemoveCustomHacks() - { - DisableSpeedhacks = false; - ApplyCustomGamefixes = false; - } - - bool HasSettingsOverride() const - { - return SettingsFolder.IsOk() || VmSettingsFile.IsOk(); - } - -}; - -// ===================================================================================================== -// Pcsx2App - main wxApp class -// ===================================================================================================== -class Pcsx2App : public wxAppWithHelpers -{ - typedef wxAppWithHelpers _parent; - - // ---------------------------------------------------------------------------- - // Event Sources! - // These need to be at the top of the App class, because a lot of other things depend - // on them and they are, themselves, fairly self-contained. - -protected: - EventSource m_evtsrc_CoreThreadStatus; - EventSource m_evtsrc_AppStatus; - -public: - void AddListener(IEventListener_CoreThread& listener) - { - m_evtsrc_CoreThreadStatus.Add(listener); - } - - void AddListener(IEventListener_AppStatus& listener) - { - m_evtsrc_AppStatus.Add(listener); - } - - void RemoveListener(IEventListener_CoreThread& listener) - { - m_evtsrc_CoreThreadStatus.Remove(listener); - } - - void RemoveListener(IEventListener_AppStatus& listener) - { - m_evtsrc_AppStatus.Remove(listener); - } - - void AddListener(IEventListener_CoreThread* listener) - { - m_evtsrc_CoreThreadStatus.Add(listener); - } - - void AddListener(IEventListener_AppStatus* listener) - { - m_evtsrc_AppStatus.Add(listener); - } - - void RemoveListener(IEventListener_CoreThread* listener) - { - m_evtsrc_CoreThreadStatus.Remove(listener); - } - - void RemoveListener(IEventListener_AppStatus* listener) - { - m_evtsrc_AppStatus.Remove(listener); - } - - void DispatchEvent(AppEventType evt); - void DispatchEvent(CoreThreadStatus evt); - void DispatchUiSettingsEvent(IniInterface& ini); - void DispatchVmSettingsEvent(IniInterface& ini); - - bool HasGUI() { return m_UseGUI; }; - bool ExitPromptWithNoGUI() { return m_NoGuiExitPrompt; }; - - // ---------------------------------------------------------------------------- -protected: - int m_PendingSaves; - bool m_ScheduledTermination; - bool m_UseGUI; - bool m_NoGuiExitPrompt; - - Threading::Mutex m_mtx_Resources; - Threading::Mutex m_mtx_LoadingGameDB; - -public: - std::unique_ptr GlobalCommands; - std::unique_ptr GlobalAccels; - - StartupOptions Startup; - CommandlineOverrides Overrides; - - std::unique_ptr m_timer_Termination; - -protected: - std::unique_ptr m_StdoutRedirHandle; - std::unique_ptr m_StderrRedirHandle; - - std::unique_ptr m_RecentIsoList; - std::unique_ptr m_DriveList; - std::unique_ptr m_Resources; - -public: - // Executor Thread for complex VM/System tasks. This thread is used to execute such tasks - // in parallel to the main message pump, to allow the main pump to run without fear of - // blocked threads stalling the GUI. - ExecutorThread SysExecutorThread; - std::unique_ptr m_CpuProviders; - std::unique_ptr m_VmReserve; - -protected: - wxWindowID m_id_MainFrame; - wxWindowID m_id_GsFrame; - wxWindowID m_id_ProgramLogBox; - wxWindowID m_id_Disassembler; - wxWindowID m_id_NewRecordingFrame; - - wxKeyEvent m_kevt; - -public: - Pcsx2App(); - virtual ~Pcsx2App(); - - void PostMenuAction(MenuIdentifiers menu_id) const; - void PostAppMethod(FnPtr_Pcsx2App method); - void PostIdleAppMethod(FnPtr_Pcsx2App method); - - void SysApplySettings(); - void SysExecute(); - void SysExecute(CDVD_SourceType cdvdsrc, const wxString& elf_override = wxEmptyString); - void LogicalVsync(); - - SysMainMemory& GetVmReserve(); - - GSFrame& GetGsFrame() const; - MainEmuFrame& GetMainFrame() const; - - GSFrame* GetGsFramePtr() const { return (GSFrame*)wxWindow::FindWindowById(m_id_GsFrame); } - MainEmuFrame* GetMainFramePtr() const { return (MainEmuFrame*)wxWindow::FindWindowById(m_id_MainFrame); } - DisassemblyDialog* GetDisassemblyPtr() const { return (DisassemblyDialog*)wxWindow::FindWindowById(m_id_Disassembler); } - - void enterDebugMode(); - void leaveDebugMode(); - void resetDebugger(); - - bool HasMainFrame() const { return GetMainFramePtr() != NULL; } - - void OpenGsPanel(); - void CloseGsPanel(); - void OnGsFrameClosed(wxWindowID id); - void OnGsFrameDestroyed(wxWindowID id); - void OnMainFrameClosed(wxWindowID id); - - // -------------------------------------------------------------------------- - // Startup / Shutdown Helpers - // -------------------------------------------------------------------------- - - void DetectCpuAndUserMode(); - void OpenProgramLog(); - void OpenMainFrame(); - void PrepForExit(); - void CleanupRestartable(); - void CleanupResources(); - void WipeUserModeSettings(); - bool TestUserPermissionsRights(const wxDirName& testFolder, wxString& createFailedStr, wxString& accessFailedStr); - void EstablishAppUserMode(); - void ForceFirstTimeWizardOnNextRun(); - - wxConfigBase* OpenInstallSettingsFile(); - wxConfigBase* TestForPortableInstall(); - - bool HasPendingSaves() const; - void StartPendingSave(); - void ClearPendingSave(); - - // -------------------------------------------------------------------------- - // App-wide Resources - // -------------------------------------------------------------------------- - // All of these accessors cache the resources on first use and retain them in - // memory until the program exits. - - wxMenu& GetRecentIsoMenu(); - RecentIsoManager& GetRecentIsoManager(); - wxMenu& GetDriveListMenu(); - - pxAppResources& GetResourceCache(); - const wxIconBundle& GetIconBundle(); - const wxBitmap& GetLogoBitmap(); - const wxBitmap& GetScreenshotBitmap(); - wxImageList& GetImgList_Config(); - wxImageList& GetImgList_Toolbars(); - - const AppImageIds& GetImgId() const; - - // -------------------------------------------------------------------------- - // Overrides of wxApp virtuals: - // -------------------------------------------------------------------------- - wxAppTraits* CreateTraits(); - bool OnInit(); - int OnExit(); - void CleanUp(); - - void OnInitCmdLine(wxCmdLineParser& parser); - bool OnCmdLineParsed(wxCmdLineParser& parser); - bool OnCmdLineError(wxCmdLineParser& parser); - bool ParseOverrides(wxCmdLineParser& parser); - -#ifdef __WXDEBUG__ - void OnAssertFailure(const wxChar* file, int line, const wxChar* func, const wxChar* cond, const wxChar* msg); -#endif - - Threading::MutexRecursive m_mtx_ProgramLog; - ConsoleLogFrame* m_ptr_ProgramLog; - - // ---------------------------------------------------------------------------- - // Console / Program Logging Helpers - // ---------------------------------------------------------------------------- - ConsoleLogFrame* GetProgramLog(); - const ConsoleLogFrame* GetProgramLog() const; - void ProgramLog_PostEvent(wxEvent& evt); - Threading::Mutex& GetProgramLogLock(); - - void EnableAllLogging(); - void DisableWindowLogging() const; - void DisableDiskLogging() const; - void OnProgramLogClosed(wxWindowID id); - -protected: - bool AppRpc_TryInvoke(FnPtr_Pcsx2App method); - bool AppRpc_TryInvokeAsync(FnPtr_Pcsx2App method); - - void AllocateCoreStuffs(); - void InitDefaultGlobalAccelerators(); - void BuildCommandHash(); - bool TryOpenConfigCwd(); - void CleanupOnExit(); - void OpenWizardConsole(); - void PadKeyDispatch(const HostKeyEvent& ev); - -protected: - void HandleEvent(wxEvtHandler* handler, wxEventFunction func, wxEvent& event) const; - void HandleEvent(wxEvtHandler* handler, wxEventFunction func, wxEvent& event); - - void OnScheduledTermination(wxTimerEvent& evt); - void OnEmuKeyDown(wxKeyEvent& evt); - void OnSysExecutorTaskTimeout(wxTimerEvent& evt); - void OnDestroyWindow(wxWindowDestroyEvent& evt); - - // ---------------------------------------------------------------------------- - // Override wx default exception handling behavior - // ---------------------------------------------------------------------------- - - // Just rethrow exceptions in the main loop, so that we can handle them properly in our - // custom catch clauses in OnRun(). (ranting note: wtf is the point of this functionality - // in wx? Why would anyone ever want a generic catch-all exception handler that *isn't* - // the unhandled exception handler? Using this as anything besides a re-throw is terrible - // program design and shouldn't even be allowed -- air) - bool OnExceptionInMainLoop() { throw; } - - // Just rethrow unhandled exceptions to cause immediate debugger fail. - void OnUnhandledException() { throw; } -}; - - -wxDECLARE_APP(Pcsx2App); - -// -------------------------------------------------------------------------------------- -// s* macros! ['s' stands for 'shortcut'] -// -------------------------------------------------------------------------------------- -// Use these for "silent fail" invocation of PCSX2 Application-related constructs. If the -// construct (albeit wxApp, MainFrame, CoreThread, etc) is null, the requested method will -// not be invoked, and an optional "else" clause can be affixed for handling the end case. -// -// Usage Examples: -// sMainFrame.ApplySettings(); -// sMainFrame.ApplySettings(); else Console.WriteLn( "Judge Wapner" ); // 'else' clause for handling NULL scenarios. -// -// Note! These macros are not "syntax complete", which means they could generate unexpected -// syntax errors in some situations, and more importantly, they cannot be used for invoking -// functions with return values. -// -// Rationale: There are a lot of situations where we want to invoke a void-style method on -// various volatile object pointers (App, Corethread, MainFrame, etc). Typically if these -// objects are NULL the most intuitive response is to simply ignore the call request and -// continue running silently. These macros make that possible without any extra boilerplate -// conditionals or temp variable defines in the code. -// -#define sApp \ - if (Pcsx2App* __app_ = (Pcsx2App*)wxApp::GetInstance()) \ - (*__app_) - -#define sLogFrame \ - if (ConsoleLogFrame* __conframe_ = wxGetApp().GetProgramLog()) \ - (*__conframe_) - -#define sMainFrame \ - if (MainEmuFrame* __frame_ = GetMainFramePtr()) \ - (*__frame_) - -// Use this within the scope of a wxWindow (wxDialog or wxFrame). If the window has a valid menu -// bar, the command will run, otherwise it will be silently ignored. :) -#define sMenuBar \ - if (wxMenuBar* __menubar_ = GetMenuBar()) \ - (*__menubar_) - -// -------------------------------------------------------------------------------------- -// AppOpenDialog -// -------------------------------------------------------------------------------------- -// Returns a wxWindow handle to the opened window. -// -template -wxWindow* AppOpenDialog(wxWindow* parent = NULL) -{ - wxWindow* window = wxFindWindowByName(L"Dialog:" + DialogType::GetNameStatic()); - - if (!window) - window = new DialogType(parent); - - window->Show(); - window->SetFocus(); - return window; -} - -// -------------------------------------------------------------------------------------- -// AppOpenModalDialog -// -------------------------------------------------------------------------------------- -// Returns the ID of the button used to close the dialog. -// -template -int AppOpenModalDialog(wxString panel_name, wxWindow* parent = NULL) -{ - if (wxWindow* window = wxFindWindowByName(L"Dialog:" + DialogType::GetNameStatic())) - { - window->SetFocus(); - if (wxDialog* dialog = wxDynamicCast(window, wxDialog)) - { - // Switch to the requested panel. - if (panel_name != wxEmptyString) - { - wxCommandEvent evt(pxEvt_SetSettingsPage); - evt.SetString(panel_name); - dialog->GetEventHandler()->ProcessEvent(evt); - } - - // It's legal to call ShowModal on a non-modal dialog, therefore making - // it modal in nature for the needs of whatever other thread of action wants - // to block against it: - if (!dialog->IsModal()) - { - int result = dialog->ShowModal(); - dialog->Destroy(); - return result; - } - } - pxFailDev("Can only show wxDialog class windows as modal!"); - return wxID_CANCEL; - } - else - return DialogType(parent).ShowModal(); -} - -// -------------------------------------------------------------------------------------- -// External App-related Globals and Shortcuts -// -------------------------------------------------------------------------------------- - -extern bool SysHasValidState(); -extern void SysUpdateIsoSrcFile(const wxString& newIsoFile); -extern void SysUpdateDiscSrcDrive(const wxString& newDiscDrive); -extern void SysStatus(const wxString& text); - -extern bool HasMainFrame(); -extern MainEmuFrame& GetMainFrame(); -extern MainEmuFrame* GetMainFramePtr(); - -alignas(16) extern AppCoreThread CoreThread; -alignas(16) extern SysMtgsThread mtgsThread; - -extern void UI_UpdateSysControls(); - -extern void UI_DisableStateActions(); -extern void UI_EnableStateActions(); - -extern void UI_DisableSysActions(); -extern void UI_EnableSysActions(); - -extern void UI_DisableSysShutdown(); - -#define AffinityAssert_AllowFrom_SysExecutor() \ - pxAssertMsg(wxGetApp().SysExecutorThread.IsSelf(), "Thread affinity violation: Call allowed from SysExecutor thread only.") - -#define AffinityAssert_DisallowFrom_SysExecutor() \ - pxAssertMsg(!wxGetApp().SysExecutorThread.IsSelf(), "Thread affinity violation: Call is *not* allowed from SysExecutor thread.") - -extern ExecutorThread& GetSysExecutorThread(); - -extern bool g_ConfigPanelChanged; //Indicates that the main config panel is open and holds unapplied changes. diff --git a/pcsx2/gui/AppAccelerators.h b/pcsx2/gui/AppAccelerators.h deleted file mode 100644 index a097fdb271..0000000000 --- a/pcsx2/gui/AppAccelerators.h +++ /dev/null @@ -1,183 +0,0 @@ -/* PCSX2 - PS2 Emulator for PCs - * Copyright (C) 2002-2010 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 . - */ - -#pragma once - -#include "AppCommon.h" - -#include -#include - -// -------------------------------------------------------------------------------------- -// KeyAcceleratorCode -// A custom keyboard accelerator that I like better than wx's wxAcceleratorEntry. -// -------------------------------------------------------------------------------------- -struct KeyAcceleratorCode -{ - union - { - struct - { - u16 keycode; - u16 win : 1, // win32 only. - cmd : 1, // ctrl in win32, Command in Mac - alt : 1, - shift : 1; - }; - u32 val32; - }; - - KeyAcceleratorCode() - : val32(0) - { - } - KeyAcceleratorCode(const wxKeyEvent& evt); - - //grab event attributes only - KeyAcceleratorCode(const wxAcceleratorEntry& right) - { - val32 = 0; - keycode = right.GetKeyCode(); - if (right.GetFlags() & wxACCEL_ALT) - Alt(); - if (right.GetFlags() & wxACCEL_CMD) - Cmd(); - if (right.GetFlags() & wxACCEL_SHIFT) - Shift(); - } - - KeyAcceleratorCode(wxKeyCode code) - { - val32 = 0; - keycode = code; - } - - KeyAcceleratorCode(u32 value) - { - val32 = value; - } - - KeyAcceleratorCode& Shift(bool enabled = true) - { - shift = enabled; - return *this; - } - - KeyAcceleratorCode& Alt(bool enabled = true) - { - alt = enabled; - return *this; - } - - KeyAcceleratorCode& Win(bool enabled = true) - { - win = enabled; - return *this; - } - - KeyAcceleratorCode& Cmd(bool enabled = true) - { - cmd = enabled; - return *this; - } - - wxString ToString() const; - - // Capitalizes the key portion of an accelerator code for displaying to the UI - // ie. Shift-a becomes Shift-A / Ctrl+Shift+a becomes Ctrl+Shift+A - wxString toTitleizedString() const - { - if (val32 == 0) - return wxEmptyString; - - std::vector tokens; - wxStringTokenizer tokenizer(ToString(), "+"); - while (tokenizer.HasMoreTokens()) - tokens.push_back(tokenizer.GetNextToken()); - - if (tokens.size() == 1) - return tokens.at(0); - else if (tokens.size() < 1) - return wxEmptyString; - - wxString lastToken = tokens.at(tokens.size() - 1); - // If the final token is a key that is multiple characters. For example 'Tab' or 'Esc'. There is no need to modify it - // Otherwise, it could be a normal letter key, so we capitalize it for stylistic reasons. - if (lastToken.Length() == 1) - { - tokens.at(tokens.size() - 1) = lastToken[0] = wxToupper(lastToken[0]); - } - wxString modifiedKeyCode; - for (int i = 0; i < (int)tokens.size(); i++) - { - if (i == (int)(tokens.size() - 1)) - modifiedKeyCode.append(tokens.at(i)); - else - modifiedKeyCode.append(wxString::Format("%s+", tokens.at(i))); - } - return modifiedKeyCode; - } -}; - - -// -------------------------------------------------------------------------------------- -// GlobalCommandDescriptor -// -------------------------------------------------------------------------------------- -// Describes a global command which can be invoked from the main GUI. - -struct GlobalCommandDescriptor -{ - const char* Id; // Identifier string - void (*Invoke)(); // Do it!! Do it NOW!!! - - const wxChar* Fullname; // Name displayed in pulldown menus - const wxChar* Tooltip; // text displayed in toolbar tooltips and menu status bars. - - bool AlsoApplyToGui; // Indicates that the GUI should be updated if possible. - - wxString keycodeString; -}; - -// -------------------------------------------------------------------------------------- -// CommandDictionary -// -------------------------------------------------------------------------------------- -class CommandDictionary : public std::unordered_map -{ - typedef std::unordered_map _parent; - -protected: -public: - using _parent::operator[]; - virtual ~CommandDictionary() = default; -}; - -// -------------------------------------------------------------------------------------- -// -// -------------------------------------------------------------------------------------- -class AcceleratorDictionary : public std::unordered_map -{ - typedef std::unordered_map _parent; - -protected: -public: - using _parent::operator[]; - - virtual ~AcceleratorDictionary() = default; - void Map(const KeyAcceleratorCode& acode, const char* searchfor); - // Searches the dictionary _by the value (command ID string)_ and returns - // the associated KeyAcceleratorCode. Do not expect constant time lookup - // Returns a blank KeyAcceleratorCode if nothing is found - KeyAcceleratorCode findKeycodeWithCommandId(const char* commandId); -}; diff --git a/pcsx2/gui/AppAssert.cpp b/pcsx2/gui/AppAssert.cpp deleted file mode 100644 index d17df1ad6d..0000000000 --- a/pcsx2/gui/AppAssert.cpp +++ /dev/null @@ -1,152 +0,0 @@ -/* PCSX2 - PS2 Emulator for PCs - * Copyright (C) 2002-2010 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 . - */ - -#include "PrecompiledHeader.h" -#include "App.h" -#include "common/Threading.h" -#include "StringHelpers.h" - -#include - -#if wxUSE_STACKWALKER -class StackDump : public wxStackWalker -{ -protected: - FastFormatUnicode m_stackTrace; - wxString m_srcFuncName; - bool m_ignoreDone; - int m_skipped; - -public: - StackDump( const char* src_function_name ) - { - if( src_function_name != NULL ) - m_srcFuncName = fromUTF8(src_function_name); - - m_ignoreDone = false; - m_skipped = 0; - } - - const wxChar* GetStackTrace() const { return m_stackTrace.c_str(); } - -protected: - virtual void OnStackFrame(const wxStackFrame& frame) - { - /*if( m_srcFuncName.IsEmpty() || m_srcFuncName == name ) - { - // FIXME: This logic isn't reliable yet. - // It's possible for our debug information to not match the function names returned by - // __pxFUNCTION__ (might happen in linux a lot, and could happen in win32 due to - // inlining on Dev aserts). The better approach is a system the queues up all the - // stacktrace info in individual wxStrings, and does a two-pass check -- first pass - // for the function name and, if not found, a second pass that just skips the first - // few stack entries. - - // It's important we only walk the stack once because Linux (argh, always linux!) has - // a really god aweful slow stack walker. - - // I'm not doing it right now because I've worked on this mess enough for one week. --air - - m_ignoreDone = true; - } - - if( !m_ignoreDone ) - { - m_skipped++; - return; - }*/ - - m_stackTrace.Write(pxsFmt( L"[%02d]", frame.GetLevel()-m_skipped).c_str()); - - if (!frame.GetName().IsEmpty()) - m_stackTrace.Write(pxsFmt( L" %-44s", WX_STR(frame.GetName()) ).c_str()); - else - m_stackTrace.Write(pxsFmt( L" 0x%-42p", frame.GetAddress() ).c_str()); - - if( frame.HasSourceLocation() ) - { - wxFileName wxfn(frame.GetFileName()); - - wxfn.SetVolume( wxEmptyString ); - for (int i = 0; i < 2 && wxfn.GetDirCount() > 0; ++i) - wxfn.RemoveDir(0); - - m_stackTrace.Write( L" %s:%d", WX_STR(wxfn.GetFullPath()), frame.GetLine() ); - } - - m_stackTrace.Write(L"\n"); - } -}; - -static wxString pxGetStackTrace( const char* calledFrom ) -{ - StackDump dump( calledFrom ); - dump.Walk( 3 ); - return dump.GetStackTrace(); -} -#endif - -#ifdef __WXDEBUG__ - -// This override of wx's implementation provides thread safe assertion message reporting. -// If we aren't on the main gui thread then the assertion message box needs to be passed -// off to the main gui thread via messages. -void Pcsx2App::OnAssertFailure( const wxChar *file, int line, const wxChar *func, const wxChar *cond, const wxChar *msg ) -{ - // Re-entrant assertions are bad mojo -- trap immediately. - static thread_local int _reentrant_lock( 0 ); - RecursionGuard guard( _reentrant_lock ); - if( guard.IsReentrant() ) pxTrap(); - - std::string nfile(StringUtil::wxStringToUTF8String(file)); - std::string nfunc(StringUtil::wxStringToUTF8String(func)); - std::string ncond(StringUtil::wxStringToUTF8String(cond)); - std::string nmsg(StringUtil::wxStringToUTF8String(msg)); - pxOnAssertFail(nfile.c_str(), line, nfunc.c_str(), nmsg.empty() ? ncond.c_str() : nmsg.c_str()); -} - -#endif - -bool AppDoAssert( const DiagnosticOrigin& origin, const char *msg ) -{ - // Used to allow the user to suppress future assertions during this application's session. - static bool disableAsserts = false; - if( disableAsserts ) return false; - -#if wxUSE_STACKWALKER - wxString trace( pxGetStackTrace(origin.function) ); -#else - wxString trace( "Warning: Platform doesn't support wx stackwalker" ); -#endif - std::string dbgmsg( origin.ToString( msg ) ); - - wxMessageOutputDebug().Printf( "%s", dbgmsg.c_str() ); - - Console.Error( dbgmsg ); - Console.WriteLn( "%ls", WX_STR(trace) ); - - wxString windowmsg( L"Assertion failed: " ); - if( msg != NULL ) - windowmsg += msg; - else if( origin.condition != NULL ) - windowmsg += origin.condition; - - int retval = Msgbox::Assertion( windowmsg, StringUtil::UTF8StringToWxString(dbgmsg) + L"\nStacktrace:\n" + trace ); - - if( retval == wxID_YES ) return true; - if( retval == wxID_IGNORE ) disableAsserts = true; - - return false; -} diff --git a/pcsx2/gui/AppCommon.h b/pcsx2/gui/AppCommon.h deleted file mode 100644 index ab2e08c018..0000000000 --- a/pcsx2/gui/AppCommon.h +++ /dev/null @@ -1,34 +0,0 @@ -/* PCSX2 - PS2 Emulator for PCs - * Copyright (C) 2002-2010 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 . - */ - -#pragma once - -#include "common/SafeArray.h" -#include "common/EventSource.h" - -#include "gui/wxGuiTools.h" -#include "gui/PersistentThread.h" -#include "gui/pxRadioPanel.h" -#include "gui/pxCheckBox.h" -#include "gui/pxStaticText.h" -#include "gui/CheckedStaticBox.h" - -#include "AppForwardDefs.h" -#include "AppConfig.h" -#include "AppEventListeners.h" - -extern wxString AddAppName( const wxString& fmt ); -extern wxString AddAppName( const wxChar* fmt ); -extern wxString AddAppName( const char* fmt ); diff --git a/pcsx2/gui/AppConfig.cpp b/pcsx2/gui/AppConfig.cpp deleted file mode 100644 index 487d895a52..0000000000 --- a/pcsx2/gui/AppConfig.cpp +++ /dev/null @@ -1,1395 +0,0 @@ -/* PCSX2 - PS2 Emulator for PCs - * Copyright (C) 2002-2010 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 . - */ - -#include "PrecompiledHeader.h" -#include "App.h" -#include "MainFrame.h" -#include "IniInterface.h" -#include "common/FileSystem.h" -#include "common/SettingsWrapper.h" -#include "wxSettingsInterface.h" - -#include -#include "DebugTools/Debug.h" -#include -#include - -////////////////////////////////////////////////////////////////////////////////////////// -// PathDefs Namespace -- contains default values for various pcsx2 path names and locations. -// -// Note: The members of this namespace are intended for default value initialization only. -// Most of the time you should use the path folder assignments in Conf() instead, since those -// are user-configurable. -// -namespace PathDefs -{ - namespace Base - { - const wxDirName& Snapshots() - { - static const wxDirName retval(L"snaps"); - return retval; - } - - const wxDirName& Savestates() - { - static const wxDirName retval(L"sstates"); - return retval; - } - - const wxDirName& MemoryCards() - { - static const wxDirName retval(L"memcards"); - return retval; - } - - const wxDirName& Settings() - { - static const wxDirName retval(L"inis"); - return retval; - } - - const wxDirName& Logs() - { - static const wxDirName retval(L"logs"); - return retval; - } - - const wxDirName& Bios() - { - static const wxDirName retval(L"bios"); - return retval; - } - - const wxDirName& Cheats() - { - static const wxDirName retval(L"cheats"); - return retval; - } - - const wxDirName& CheatsWS() - { - static const wxDirName retval(L"cheats_ws"); - return retval; - } - - const wxDirName& Langs() - { - static const wxDirName retval(L"locale"); - return retval; - } - - const wxDirName& Dumps() - { - static const wxDirName retval(L"dumps"); - return retval; - } - - const wxDirName& Docs() - { - static const wxDirName retval(L"docs"); - return retval; - } - - const wxDirName& Resources() - { - static const wxDirName retval(L"resources"); - return retval; - } - - const wxDirName& Cache() - { - static const wxDirName retval(L"cache"); - return retval; - } - - const wxDirName& Textures() - { - static const wxDirName retval(L"textures"); - return retval; - } - }; - - // Specifies the root folder for the application install. - // (currently it's the CWD, but in the future I intend to move all binaries to a "bin" - // sub folder, in which case the approot will become "..") [- Air?] - - //The installer installs the folders which are relative to AppRoot (that's langs) - // relative to the exe folder, and not relative to cwd. So the exe should be default AppRoot. - avih - const wxDirName& AppRoot() - { - //AffinityAssert_AllowFrom_MainUI(); - /* - if (InstallationMode == InstallMode_Registered) - { - static const wxDirName cwdCache( (wxDirName)Path::Normalize(wxGetCwd()) ); - return cwdCache; - } - else if (InstallationMode == InstallMode_Portable) -*/ - if (InstallationMode == InstallMode_Registered || InstallationMode == InstallMode_Portable) - { - static const wxDirName appCache((wxDirName) - wxFileName(wxStandardPaths::Get().GetExecutablePath()) - .GetPath()); - return appCache; - } - else - pxFail("Unimplemented user local folder mode encountered."); - - static const wxDirName dotFail(L"."); - return dotFail; - } - - // Specifies the main configuration folder. - wxDirName GetUserLocalDataDir() - { - return wxDirName(wxStandardPaths::Get().GetUserLocalDataDir()); - } - - // Fetches the path location for user-consumable documents -- stuff users are likely to want to - // share with other programs: screenshots, memory cards, and savestates. - wxDirName GetDocuments(DocsModeType mode) - { - switch (mode) - { -#if defined(XDG_STD) || defined(__APPLE__) // Expected location for this kind of stuff on macOS - // Move all user data file into central configuration directory (XDG_CONFIG_DIR) - case DocsFolder_User: - return GetUserLocalDataDir(); -#else - case DocsFolder_User: - return (wxDirName)Path::CombineWx(wxStandardPaths::Get().GetDocumentsDir(), pxGetAppName()); -#endif - case DocsFolder_Custom: - return CustomDocumentsFolder; - - jNO_DEFAULT - } - - return wxDirName(); - } - - wxDirName GetDocuments() - { - return GetDocuments(DocsFolderMode); - } - - wxDirName GetProgramDataDir() - { -#ifdef __APPLE__ - return wxDirName(wxStandardPaths::Get().GetResourcesDir()); -#elif !defined(PCSX2_APP_DATADIR) - return AppRoot(); -#else - // Each linux distributions have his rules for path so we give them the possibility to - // change it with compilation flags. -- Gregory - return wxDirName(PCSX2_APP_DATADIR).MakeAbsolute(AppRoot().ToString()); -#endif - } - - wxDirName GetSnapshots() - { - return GetDocuments() + Base::Snapshots(); - } - - wxDirName GetBios() - { - return GetDocuments() + Base::Bios(); - } - - wxDirName GetCheats() - { - return GetDocuments() + Base::Cheats(); - } - - wxDirName GetCheatsWS() - { - return GetDocuments() + Base::CheatsWS(); - } - - wxDirName GetDocs() - { -#if !defined(PCSX2_APP_DOCDIR) - return AppRoot() + Base::Docs(); -#else - return wxDirName(PCSX2_APP_DOCDIR).MakeAbsolute(AppRoot().ToString()); -#endif - } - - wxDirName GetSavestates() - { - return GetDocuments() + Base::Savestates(); - } - - wxDirName GetMemoryCards() - { - return GetDocuments() + Base::MemoryCards(); - } - - wxDirName GetSettings() - { - return GetDocuments() + Base::Settings(); - } - - wxDirName GetLogs() - { - return GetDocuments() + Base::Logs(); - } - - wxDirName GetResources() - { - // ifdef is only needed here because mac doesn't put its resources in a subdirectory.. -#ifdef __APPLE__ - return wxDirName(wxStandardPaths::Get().GetResourcesDir()); -#else - return GetProgramDataDir() + Base::Resources(); -#endif - } - - wxDirName GetLangs() - { - return GetResources() + Base::Langs(); - } - - wxDirName GetCache() - { - return GetDocuments() + Base::Cache(); - } - - wxDirName GetTextures() - { - return GetDocuments() + Base::Textures(); - } - - wxDirName Get(FoldersEnum_t folderidx) - { - switch (folderidx) - { - case FolderId_Settings: - return GetSettings(); - case FolderId_Bios: - return GetBios(); - case FolderId_Snapshots: - return GetSnapshots(); - case FolderId_Savestates: - return GetSavestates(); - case FolderId_MemoryCards: - return GetMemoryCards(); - case FolderId_Logs: - return GetLogs(); - case FolderId_Langs: - return GetLangs(); - case FolderId_Cheats: - return GetCheats(); - case FolderId_CheatsWS: - return GetCheatsWS(); - case FolderId_Cache: - return GetCache(); - case FolderId_Textures: - return GetTextures(); - - case FolderId_Documents: - return CustomDocumentsFolder; - - jNO_DEFAULT - } - return wxDirName(); - } -}; // namespace PathDefs - - -// -------------------------------------------------------------------------------------- -// Default Filenames -// -------------------------------------------------------------------------------------- -namespace FilenameDefs -{ - wxFileName GetUiConfig() - { - return pxGetAppName() + L"_ui.ini"; - } - - wxFileName GetUiKeysConfig() - { - return pxGetAppName() + L"_keys.ini"; - } - - wxFileName GetVmConfig() - { - return pxGetAppName() + L"_vm.ini"; - } - - wxFileName GetUsermodeConfig() - { - return wxFileName(L"usermode.ini"); - } - - const wxFileName& Memcard(uint port, uint slot) - { - static const wxFileName retval[2][4] = - { - { - wxFileName(L"Mcd001.ps2"), - wxFileName(L"Mcd003.ps2"), - wxFileName(L"Mcd005.ps2"), - wxFileName(L"Mcd007.ps2"), - }, - { - wxFileName(L"Mcd002.ps2"), - wxFileName(L"Mcd004.ps2"), - wxFileName(L"Mcd006.ps2"), - wxFileName(L"Mcd008.ps2"), - }}; - - pxAssert(port < 2); - pxAssert(slot < 4); - - return retval[port][slot]; - } -}; // namespace FilenameDefs - -static wxDirName GetResolvedFolder(FoldersEnum_t id) -{ - return g_Conf->Folders.IsDefault(id) ? PathDefs::Get(id) : g_Conf->Folders[id]; -} - -static wxDirName GetSettingsFolder() -{ - if (wxGetApp().Overrides.SettingsFolder.IsOk()) - return wxGetApp().Overrides.SettingsFolder; - - return UseDefaultSettingsFolder ? PathDefs::GetSettings() : SettingsFolder; -} - -wxString GetVmSettingsFilename() -{ - wxFileName fname(wxGetApp().Overrides.VmSettingsFile.IsOk() ? wxGetApp().Overrides.VmSettingsFile : FilenameDefs::GetVmConfig()); - return GetSettingsFolder().Combine(fname).GetFullPath(); -} - -wxString GetUiSettingsFilename() -{ - wxFileName fname(FilenameDefs::GetUiConfig()); - return GetSettingsFolder().Combine(fname).GetFullPath(); -} - -wxString GetUiKeysFilename() -{ - wxFileName fname(FilenameDefs::GetUiKeysConfig()); - return GetSettingsFolder().Combine(fname).GetFullPath(); -} - -wxDirName& AppConfig::FolderOptions::operator[](FoldersEnum_t folderidx) -{ - switch (folderidx) - { - case FolderId_Settings: - return SettingsFolder; - case FolderId_Bios: - return Bios; - case FolderId_Snapshots: - return Snapshots; - case FolderId_Savestates: - return Savestates; - case FolderId_MemoryCards: - return MemoryCards; - case FolderId_Logs: - return Logs; - case FolderId_Langs: - return Langs; - case FolderId_Cheats: - return Cheats; - case FolderId_CheatsWS: - return CheatsWS; - case FolderId_Cache: - return Cache; - case FolderId_Textures: - return Textures; - - case FolderId_Documents: - return CustomDocumentsFolder; - - jNO_DEFAULT - } - return SettingsFolder; // unreachable, but suppresses warnings. -} - -const wxDirName& AppConfig::FolderOptions::operator[](FoldersEnum_t folderidx) const -{ - return const_cast(this)->operator[](folderidx); -} - -bool AppConfig::FolderOptions::IsDefault(FoldersEnum_t folderidx) const -{ - switch (folderidx) - { - case FolderId_Settings: - return UseDefaultSettingsFolder; - case FolderId_Bios: - return UseDefaultBios; - case FolderId_Snapshots: - return UseDefaultSnapshots; - case FolderId_Savestates: - return UseDefaultSavestates; - case FolderId_MemoryCards: - return UseDefaultMemoryCards; - case FolderId_Logs: - return UseDefaultLogs; - case FolderId_Langs: - return UseDefaultLangs; - case FolderId_Cheats: - return UseDefaultCheats; - case FolderId_CheatsWS: - return UseDefaultCheatsWS; - case FolderId_Cache: - return UseDefaultCache; - case FolderId_Textures: - return UseDefaultTextures; - - case FolderId_Documents: - return false; - - jNO_DEFAULT - } - return false; -} - -void AppConfig::FolderOptions::Set(FoldersEnum_t folderidx, const wxString& src, bool useDefault) -{ - switch (folderidx) - { - case FolderId_Settings: - SettingsFolder = src; - UseDefaultSettingsFolder = useDefault; - EmuFolders::Settings = GetSettingsFolder().ToUTF8(); - break; - - case FolderId_Bios: - Bios = src; - UseDefaultBios = useDefault; - EmuFolders::Bios = GetResolvedFolder(FolderId_Bios).ToUTF8(); - break; - - case FolderId_Snapshots: - Snapshots = src; - UseDefaultSnapshots = useDefault; - EmuFolders::Snapshots = GetResolvedFolder(FolderId_Snapshots).ToUTF8(); - break; - - case FolderId_Savestates: - Savestates = src; - UseDefaultSavestates = useDefault; - EmuFolders::Savestates = GetResolvedFolder(FolderId_Savestates).ToUTF8(); - break; - - case FolderId_MemoryCards: - MemoryCards = src; - UseDefaultMemoryCards = useDefault; - EmuFolders::MemoryCards = GetResolvedFolder(FolderId_MemoryCards).ToUTF8(); - break; - - case FolderId_Logs: - Logs = src; - UseDefaultLogs = useDefault; - EmuFolders::Logs = GetResolvedFolder(FolderId_Logs).ToUTF8(); - break; - - case FolderId_Langs: - Langs = src; - UseDefaultLangs = useDefault; - EmuFolders::Langs = GetResolvedFolder(FolderId_Langs).ToUTF8(); - break; - - case FolderId_Documents: - CustomDocumentsFolder = src; - break; - - case FolderId_Cheats: - Cheats = src; - UseDefaultCheats = useDefault; - EmuFolders::Cheats = GetResolvedFolder(FolderId_Cheats).ToUTF8(); - break; - - case FolderId_CheatsWS: - CheatsWS = src; - UseDefaultCheatsWS = useDefault; - EmuFolders::CheatsWS = GetResolvedFolder(FolderId_CheatsWS).ToUTF8(); - break; - - case FolderId_Cache: - Cache = src; - UseDefaultCache = useDefault; - EmuFolders::Cache = GetResolvedFolder(FolderId_Cache).ToUTF8(); - FileSystem::CreateDirectoryPath(EmuFolders::Cache.c_str(), false); - break; - - case FolderId_Textures: - Textures = src; - UseDefaultTextures = useDefault; - EmuFolders::Textures = GetResolvedFolder(FolderId_Textures).ToUTF8(); - FileSystem::CreateDirectoryPath(EmuFolders::Textures.c_str(), false); - break; - - jNO_DEFAULT - } -} - -bool IsPortable() -{ - return InstallationMode == InstallMode_Portable; -} - -AppConfig::AppConfig() - : MainGuiPosition(wxDefaultPosition) - , SysSettingsTabName(L"Cpu") - , McdSettingsTabName(L"none") - , AppSettingsTabName(L"none") - , GameDatabaseTabName(L"none") - , LanguageId(wxLANGUAGE_DEFAULT) - , LanguageCode(L"default") - , CdvdSource(CDVD_SourceType::Iso) -{ - RecentIsoCount = 20; - Listbook_ImageSize = 32; - Toolbar_ImageSize = 24; - Toolbar_ShowLabels = true; - - EnableSpeedHacks = true; - EnableGameFixes = false; - EnableFastBoot = true; - - DevMode = false; - - EnablePresets = true; - PresetIndex = 1; -} - -// ------------------------------------------------------------------------ -void App_LoadSaveInstallSettings(IniInterface& ini) -{ - // Portable installs of PCSX2 should not save any of the following information to - // the INI file. Only the Run First Time Wizard option is saved, and that's done - // from EstablishAppUserMode code. All other options have assumed (fixed) defaults in - // portable mode which cannot be changed/saved. - - // Note: Settins are still *loaded* from portable.ini, in case the user wants to do - // low-level overrides of the default behavior of portable mode installs. - - if (ini.IsSaving() && (InstallationMode == InstallMode_Portable)) - return; - - static const wxChar* DocsFolderModeNames[] = - { - L"User", - L"Custom", - // WARNING: array must be NULL terminated to compute it size - NULL}; - - ini.EnumEntry(L"DocumentsFolderMode", DocsFolderMode, DocsFolderModeNames, (InstallationMode == InstallMode_Registered) ? DocsFolder_User : DocsFolder_Custom); - - ini.Entry(L"CustomDocumentsFolder", CustomDocumentsFolder, PathDefs::AppRoot()); - - ini.Entry(L"UseDefaultSettingsFolder", UseDefaultSettingsFolder, true); - ini.Entry(L"SettingsFolder", SettingsFolder, PathDefs::GetSettings()); - - // "Install_Dir" conforms to the NSIS standard install directory key name. - ini.Entry(L"Install_Dir", InstallFolder, (wxDirName)(wxFileName(wxStandardPaths::Get().GetExecutablePath()).GetPath())); - SetFullBaseDir(InstallFolder); - - ini.Flush(); -} - -void App_LoadInstallSettings(wxConfigBase* ini) -{ - IniLoader loader(ini); - App_LoadSaveInstallSettings(loader); -} - -void App_SaveInstallSettings(wxConfigBase* ini) -{ - IniSaver saver(ini); - App_LoadSaveInstallSettings(saver); -} - -// ------------------------------------------------------------------------ -const wxChar* CDVD_SourceLabels[] = - { - L"ISO", - L"Disc", - L"NoDisc", - NULL}; - -void AppConfig::LoadSaveRootItems(IniInterface& ini) -{ - IniEntry(MainGuiPosition); - IniEntry(SysSettingsTabName); - IniEntry(McdSettingsTabName); - IniEntry(ComponentsTabName); - IniEntry(AppSettingsTabName); - IniEntry(GameDatabaseTabName); - ini.EnumEntry(L"LanguageId", LanguageId, NULL, LanguageId); - IniEntry(LanguageCode); - IniEntry(RecentIsoCount); - ini.Entry(wxT("GzipIsoIndexTemplate"), EmuConfig.GzipIsoIndexTemplate, EmuConfig.GzipIsoIndexTemplate); - IniEntry(Listbook_ImageSize); - IniEntry(Toolbar_ImageSize); - IniEntry(Toolbar_ShowLabels); - - wxFileName res(CurrentIso); - ini.Entry(L"CurrentIso", res, res, ini.IsLoading() || IsPortable()); - CurrentIso = res.GetFullPath(); - - ini.Entry(wxT("CurrentBlockdump"), EmuConfig.CurrentBlockdump, EmuConfig.CurrentBlockdump); - IniEntry(CurrentELF); - ini.Entry(wxT("CurrentIRX"), EmuConfig.CurrentIRX, EmuConfig.CurrentIRX); - - IniEntry(EnableSpeedHacks); - IniEntry(EnableGameFixes); - IniEntry(EnableFastBoot); - - IniEntry(EnablePresets); - IniEntry(PresetIndex); - IniEntry(AskOnBoot); - - IniEntry(DevMode); - - ini.EnumEntry(L"CdvdSource", CdvdSource, CDVD_SourceLabels, CdvdSource); - -#ifdef __WXMSW__ - ini.Entry(wxT("McdCompressNTFS"), EmuOptions.McdCompressNTFS, EmuOptions.McdCompressNTFS); -#endif -} - -// ------------------------------------------------------------------------ -void AppConfig::LoadSave(IniInterface& ini, SettingsWrapper& wrap) -{ - // do all the wx stuff first so it doesn't screw with the wrapper's path - LoadSaveRootItems(ini); - ProgLogBox.LoadSave(ini, L"ProgramLog"); - Folders.LoadSave(ini); - - GSWindow.LoadSave(ini); - inputRecording.loadSave(ini); - AudioCapture.LoadSave(ini); - Templates.LoadSave(ini); - - // Process various sub-components: - EmuOptions.LoadSaveMemcards(wrap); - EmuOptions.BaseFilenames.LoadSave(wrap); - EmuOptions.Framerate.LoadSave(wrap); - - ini.Flush(); -} - -// ------------------------------------------------------------------------ -AppConfig::ConsoleLogOptions::ConsoleLogOptions() - : DisplayPosition(wxDefaultPosition) - , DisplaySize(wxSize(680, 560)) - , Theme(L"Default") -{ - Visible = true; - AutoDock = true; - FontSize = 8; -} - -void AppConfig::ConsoleLogOptions::LoadSave(IniInterface& ini, const wxChar* logger) -{ - ScopedIniGroup path(ini, logger); - - IniEntry(Visible); - IniEntry(AutoDock); - IniEntry(DisplayPosition); - IniEntry(DisplaySize); - IniEntry(FontSize); - IniEntry(Theme); -} - -void AppConfig::FolderOptions::ApplyDefaults() -{ - if (UseDefaultBios) - Bios = PathDefs::GetBios(); - if (UseDefaultSnapshots) - Snapshots = PathDefs::GetSnapshots(); - if (UseDefaultSavestates) - Savestates = PathDefs::GetSavestates(); - if (UseDefaultMemoryCards) - MemoryCards = PathDefs::GetMemoryCards(); - if (UseDefaultLogs) - Logs = PathDefs::GetLogs(); - if (UseDefaultLangs) - Langs = PathDefs::GetLangs(); - if (UseDefaultCheats) - Cheats = PathDefs::GetCheats(); - if (UseDefaultCheatsWS) - CheatsWS = PathDefs::GetCheatsWS(); -} - -// ------------------------------------------------------------------------ -AppConfig::FolderOptions::FolderOptions() - : Bios(PathDefs::GetBios()) - , Snapshots(PathDefs::GetSnapshots()) - , Savestates(PathDefs::GetSavestates()) - , MemoryCards(PathDefs::GetMemoryCards()) - , Langs(PathDefs::GetLangs()) - , Logs(PathDefs::GetLogs()) - , Cheats(PathDefs::GetCheats()) - , CheatsWS(PathDefs::GetCheatsWS()) - , Resources(PathDefs::GetResources()) - , Cache(PathDefs::GetCache()) - - , RunIso(PathDefs::GetDocuments()) // raw default is always the Documents folder. - , RunELF(PathDefs::GetDocuments()) // raw default is always the Documents folder. - , RunDisc() -{ - bitset = 0xffffffff; -} - -void AppConfig::FolderOptions::LoadSave(IniInterface& ini) -{ - ScopedIniGroup path(ini, L"Folders"); - - if (ini.IsSaving()) - { - ApplyDefaults(); - } - - IniBitBool(UseDefaultBios); - IniBitBool(UseDefaultSnapshots); - IniBitBool(UseDefaultSavestates); - IniBitBool(UseDefaultMemoryCards); - IniBitBool(UseDefaultLogs); - IniBitBool(UseDefaultLangs); - IniBitBool(UseDefaultCheats); - IniBitBool(UseDefaultCheatsWS); - IniBitBool(UseDefaultTextures); - - //when saving in portable mode, we save relative paths if possible - // --> on load, these relative paths will be expanded relative to the exe folder. - bool rel = (ini.IsLoading() || IsPortable()); - - IniEntryDirFile(Bios, rel); - IniEntryDirFile(Snapshots, rel); - IniEntryDirFile(Savestates, rel); - IniEntryDirFile(MemoryCards, rel); - IniEntryDirFile(Logs, rel); - IniEntryDirFile(Langs, rel); - IniEntryDirFile(Cheats, rel); - IniEntryDirFile(CheatsWS, rel); - IniEntryDirFile(Cache, rel); - IniEntryDirFile(Textures, rel); - - IniEntryDirFile(RunIso, rel); - IniEntryDirFile(RunELF, rel); - IniEntry(RunDisc); - - if (ini.IsLoading()) - { - ApplyDefaults(); - - for (int i = 0; i < FolderId_COUNT; ++i) - operator[]((FoldersEnum_t)i).Normalize(); - - AppSetEmuFolders(); - } -} - -void AppSetEmuFolders() -{ - EmuFolders::Settings = GetSettingsFolder().ToUTF8(); - EmuFolders::Bios = GetResolvedFolder(FolderId_Bios).ToUTF8(); - EmuFolders::Snapshots = GetResolvedFolder(FolderId_Snapshots).ToUTF8(); - EmuFolders::Savestates = GetResolvedFolder(FolderId_Savestates).ToUTF8(); - EmuFolders::MemoryCards = GetResolvedFolder(FolderId_MemoryCards).ToUTF8(); - EmuFolders::Logs = GetResolvedFolder(FolderId_Logs).ToUTF8(); - EmuFolders::Langs = GetResolvedFolder(FolderId_Langs).ToUTF8(); - EmuFolders::Cheats = GetResolvedFolder(FolderId_Cheats).ToUTF8(); - EmuFolders::CheatsWS = GetResolvedFolder(FolderId_CheatsWS).ToUTF8(); - EmuFolders::Resources = g_Conf->Folders.Resources.ToUTF8(); - EmuFolders::Cache = GetResolvedFolder(FolderId_Cache).ToUTF8(); - EmuFolders::Textures = GetResolvedFolder(FolderId_Textures).ToUTF8(); - - // Ensure cache directory exists, since we're going to write to it (e.g. game database) - FileSystem::CreateDirectoryPath(EmuFolders::Cache.c_str(), false); -} - -// ------------------------------------------------------------------------ -AppConfig::GSWindowOptions::GSWindowOptions() - : WindowSize(wxSize(640, 480)) - , WindowPos(wxDefaultPosition) -{ - CloseOnEsc = true; - DefaultToFullscreen = false; - AlwaysHideMouse = false; - DisableResizeBorders = false; - DisableScreenSaver = true; - - IsMaximized = false; - IsFullscreen = false; - EnableVsyncWindowFlag = false; - - IsToggleFullscreenOnDoubleClick = true; -} - -void AppConfig::GSWindowOptions::SanityCheck() -{ - // Ensure Conformation of various options... - - WindowSize.x = std::max(WindowSize.x, 8); - WindowSize.x = std::min(WindowSize.x, wxGetDisplayArea().GetWidth() - 16); - - WindowSize.y = std::max(WindowSize.y, 8); - WindowSize.y = std::min(WindowSize.y, wxGetDisplayArea().GetHeight() - 48); - - // Make sure the upper left corner of the window is visible enought o grab and - // move into view: - if (!wxGetDisplayArea().Contains(wxRect(WindowPos, wxSize(48, 48)))) - WindowPos = wxDefaultPosition; -} - -void AppConfig::GSWindowOptions::LoadSave(IniInterface& ini) -{ - ScopedIniGroup path(ini, L"GSWindow"); - - IniEntry(CloseOnEsc); - IniEntry(DefaultToFullscreen); - IniEntry(AlwaysHideMouse); - IniEntry(DisableResizeBorders); - IniEntry(DisableScreenSaver); - - IniEntry(WindowSize); - IniEntry(WindowPos); - IniEntry(IsMaximized); - IniEntry(IsFullscreen); - IniEntry(EnableVsyncWindowFlag); - - IniEntry(IsToggleFullscreenOnDoubleClick); - - static const wxChar* AspectRatioNames[] = - { - L"Stretch", - L"Auto 4:3/3:2 (Progressive)", - L"4:3", - L"16:9", - // WARNING: array must be NULL terminated to compute it size - NULL}; - - g_Conf->EmuOptions.GS.SyncToHostRefreshRate = ini.EntryBitBool(L"SyncToHostRefreshRate", g_Conf->EmuOptions.GS.SyncToHostRefreshRate, g_Conf->EmuOptions.GS.SyncToHostRefreshRate); - ini.EnumEntry(L"AspectRatio", g_Conf->EmuOptions.GS.AspectRatio, AspectRatioNames, g_Conf->EmuOptions.GS.AspectRatio); - if (ini.IsLoading()) - EmuConfig.CurrentAspectRatio = g_Conf->EmuOptions.GS.AspectRatio; - - static const wxChar* FMVAspectRatioSwitchNames[] = - { - L"Off", - L"Auto 4:3/3:2 (Progressive)", - L"4:3", - L"16:9", - // WARNING: array must be NULL terminated to compute it size - NULL}; - ini.EnumEntry(L"FMVAspectRatioSwitch", g_Conf->EmuOptions.GS.FMVAspectRatioSwitch, FMVAspectRatioSwitchNames, g_Conf->EmuOptions.GS.FMVAspectRatioSwitch); - - if (ini.IsLoading()) - SanityCheck(); -} - -AppConfig::InputRecordingOptions::InputRecordingOptions() - : VirtualPadPosition(wxDefaultPosition) - , m_frame_advance_amount(1) -{ -} - -void AppConfig::InputRecordingOptions::loadSave(IniInterface& ini) -{ - ScopedIniGroup path(ini, L"InputRecording"); - - IniEntry(VirtualPadPosition); - IniEntry(m_frame_advance_amount); -} - -AppConfig::CaptureOptions::CaptureOptions() -{ - EnableAudio = true; -} - -void AppConfig::CaptureOptions::LoadSave(IniInterface& ini) -{ - ScopedIniGroup path(ini, L"Capture"); - - IniEntry(EnableAudio); -} - -AppConfig::UiTemplateOptions::UiTemplateOptions() - : LimiterUnlimited(L"Max") - , LimiterTurbo(L"Turbo") - , LimiterSlowmo(L"Slowmo") - , LimiterNormal(L"Normal") - , OutputFrame(L"Frame") - , OutputField(L"Field") - , OutputProgressive(L"Progressive") - , OutputInterlaced(L"Interlaced") - , Paused(L" ") - , TitleTemplate(L"Slot: ${slot} | Speed: ${speed} (${vfps}) | ${videomode} | Limiter: ${limiter} | ${gs} | ${omodei} | ${cpuusage}") - , RecordingTemplate(L"Slot: ${slot} | Frame: ${frame}/${maxFrame} | Rec. Mode: ${mode} | Speed: ${speed} (${vfps}) | Limiter: ${limiter}") -{ -} - -void AppConfig::UiTemplateOptions::LoadSave(IniInterface& ini) -{ - ScopedIniGroup path(ini, L"UiTemplates"); - - IniEntry(LimiterUnlimited); - IniEntry(LimiterTurbo); - IniEntry(LimiterSlowmo); - IniEntry(LimiterNormal); - IniEntry(OutputFrame); - IniEntry(OutputField); - IniEntry(OutputProgressive); - IniEntry(OutputInterlaced); - IniEntry(Paused); - IniEntry(TitleTemplate); - IniEntry(RecordingTemplate); -} - -int AppConfig::GetMaxPresetIndex() -{ - return 2; -} - -bool AppConfig::isOkGetPresetTextAndColor(int n, wxString& label, wxColor& c) -{ - const wxString presetNamesAndColors[][2] = - { - {_t("Safest (No hacks)"), L"Blue"}, - {_t("Safe (Default)"), L"Dark Green"}, - {_t("Balanced"), L"Forest Green"} - } ; - n = std::clamp(n, 0, GetMaxPresetIndex()); - - label = wxsFormat(L"%d - ", n + 1) + presetNamesAndColors[n][0]; - c = wxColor(presetNamesAndColors[n][1]); - - return true; -} - - -// Apply one of several (currently 6) configuration subsets. -// The scope of the subset which each preset controlls is hardcoded here. -// Use ignoreMTVU to avoid updating the MTVU field. -// Main purpose is for the preset enforcement at launch, to avoid overwriting a user's setting. -bool AppConfig::IsOkApplyPreset(int n, bool ignoreMTVU) -{ - if (n < 0 || n > GetMaxPresetIndex()) - { - Console.WriteLn("DEV Warning: ApplyPreset(%d): index out of range, Aborting.", n); - return false; - } - - //Console.WriteLn("Applying Preset %d ...", n); - - //Have some original and default values at hand to be used later. - Pcsx2Config::FramerateOptions original_Framerate = EmuOptions.Framerate; - Pcsx2Config::SpeedhackOptions original_SpeedHacks = EmuOptions.Speedhacks; - AppConfig default_AppConfig; - Pcsx2Config default_Pcsx2Config; - - // NOTE: Because the system currently only supports passing of an entire AppConfig to the GUI panels/menus to apply/reflect, - // the GUI entities should be aware of the settings which the presets control, such that when presets are used: - // 1. The panels/entities should prevent manual modifications (by graying out) of settings which the presets control. - // 2. The panels should not apply values which the presets don't control if the value is initiated by a preset. - // Currently controlled by the presets: - // - AppConfig: Framerate (except turbo/slowmo factors), EnableSpeedHacks, EnableGameFixes. - // - EmuOptions: Cpu, Gamefixes, SpeedHacks (except mtvu), EnablePatches, GS (except for FrameLimitEnable and VsyncEnable). - // - // This essentially currently covers all the options on all the panels except for framelimiter which isn't - // controlled by the presets, and the entire GSWindow panel which also isn't controlled by presets - // - // So, if changing the scope of the presets (making them affect more or less values), the relevant GUI entities - // should me modified to support it. - - - //Force some settings as a (current) base for all presets. - - EmuOptions.Framerate = default_Pcsx2Config.Framerate; - EmuOptions.Framerate.SlomoScalar = original_Framerate.SlomoScalar; - EmuOptions.Framerate.TurboScalar = original_Framerate.TurboScalar; - - EnableGameFixes = false; - - EmuOptions.EnablePatches = true; - - EmuOptions.GS.SynchronousMTGS = default_Pcsx2Config.GS.SynchronousMTGS; - - EmuOptions.Cpu = default_Pcsx2Config.Cpu; - EmuOptions.Gamefixes = default_Pcsx2Config.Gamefixes; - EmuOptions.Speedhacks = default_Pcsx2Config.Speedhacks; - EmuOptions.Speedhacks.bitset = 0; //Turn off individual hacks to make it visually clear they're not used. - EmuOptions.Speedhacks.vuThread = original_SpeedHacks.vuThread; - EmuOptions.Speedhacks.vu1Instant = original_SpeedHacks.vu1Instant; - EnableSpeedHacks = true; - - // Actual application of current preset over the base settings which all presets use (mostly pcsx2's default values). - - bool isMTVUSet = ignoreMTVU ? true : false; // used to prevent application of specific lower preset values on fallthrough. - switch (n) // Settings will waterfall down to the Safe preset, then stop. So, Balanced and higher will inherit any settings through Safe. - { - case 2: // Balanced - isMTVUSet ? 0 : (isMTVUSet = true, EmuOptions.Speedhacks.vuThread = true); // Enable MTVU - [[fallthrough]]; - - case 1: // Safe (Default) - EmuOptions.Speedhacks.IntcStat = true; - EmuOptions.Speedhacks.WaitLoop = true; - EmuOptions.Speedhacks.vuFlagHack = true; - EmuOptions.Speedhacks.vu1Instant = true; - - // If waterfalling from > Safe, break to avoid MTVU disable. - if (n > 1) - break; - [[fallthrough]]; - - case 0: // Safest - if (n == 0) - EmuOptions.Speedhacks.vu1Instant = false; - isMTVUSet ? 0 : (isMTVUSet = true, EmuOptions.Speedhacks.vuThread = false); // Disable MTVU - break; - - default: - Console.WriteLn("Developer Warning: Preset #%d is not implemented. (--> Using application default).", n); - } - - - EnablePresets = true; - PresetIndex = n; - - return true; -} - - -wxFileConfig* OpenFileConfig(const wxString& filename) -{ - return new wxFileConfig(wxEmptyString, wxEmptyString, filename, wxEmptyString, wxCONFIG_USE_RELATIVE_PATH); -} - -void RelocateLogfile() -{ - g_Conf->Folders.Logs.Mkdir(); - - std::string newlogname(StringUtil::wxStringToUTF8String(Path::CombineWx(g_Conf->Folders.Logs.ToString(), L"emuLog.txt"))); - - if ((emuLog != NULL) && (emuLogName != newlogname)) - { - Console.WriteLn("\nRelocating Logfile...\n\tFrom: %ls\n\tTo : %ls\n", emuLogName.c_str(), newlogname.c_str()); - wxGetApp().DisableDiskLogging(); - - fclose(emuLog); - emuLog = NULL; - } - - if (emuLog == NULL) - { - emuLogName = newlogname; - emuLog = FileSystem::OpenCFile(emuLogName.c_str(), "wb"); - } - - wxGetApp().EnableAllLogging(); -} - -// Parameters: -// overwrite - this option forces the current settings to overwrite any existing settings -// that might be saved to the configured ini/settings folder. -// -// Notes: -// The overwrite option applies to PCSX2 options only. -// -void AppConfig_OnChangedSettingsFolder(bool overwrite) -{ - PathDefs::GetDocuments().Mkdir(); - GetSettingsFolder().Mkdir(); - - const wxString iniFilename(GetUiSettingsFilename()); - - if (overwrite) - { - if (wxFileExists(iniFilename) && !wxRemoveFile(iniFilename)) - throw Exception::AccessDenied(StringUtil::wxStringToUTF8String(iniFilename)) - .SetBothMsgs("Failed to overwrite existing settings file; permission was denied."); - - const wxString vmIniFilename(GetVmSettingsFilename()); - - if (wxFileExists(vmIniFilename) && !wxRemoveFile(vmIniFilename)) - throw Exception::AccessDenied(StringUtil::wxStringToUTF8String(vmIniFilename)) - .SetBothMsgs("Failed to overwrite existing settings file; permission was denied."); - } - - // Bind into wxConfigBase to allow wx to use our config internally, and delete whatever - // comes out (cleans up prev config, if one). - delete wxConfigBase::Set(OpenFileConfig(iniFilename)); - GetAppConfig()->SetRecordDefaults(true); - - if (!overwrite) - AppLoadSettings(); - else - AppSetEmuFolders(); - - AppApplySettings(); - AppSaveSettings(); //Make sure both ini files are created if needed. -} - -// -------------------------------------------------------------------------------------- -// pxDudConfig -// -------------------------------------------------------------------------------------- -// Used to handle config actions prior to the creation of the ini file (for example, the -// first time wizard). Attempts to save ini settings are simply ignored through this -// class, which allows us to give the user a way to set everything up in the wizard, apply -// settings as usual, and only *save* something once the whole wizard is complete. -// -class pxDudConfig : public wxConfigBase -{ -protected: - wxString m_empty; - -public: - virtual ~pxDudConfig() = default; - - virtual void SetPath(const wxString&) {} - virtual const wxString& GetPath() const { return m_empty; } - - virtual bool GetFirstGroup(wxString&, long&) const { return false; } - virtual bool GetNextGroup(wxString&, long&) const { return false; } - virtual bool GetFirstEntry(wxString&, long&) const { return false; } - virtual bool GetNextEntry(wxString&, long&) const { return false; } - virtual size_t GetNumberOfEntries(bool) const { return 0; } - virtual size_t GetNumberOfGroups(bool) const { return 0; } - - virtual bool HasGroup(const wxString&) const { return false; } - virtual bool HasEntry(const wxString&) const { return false; } - - virtual bool Flush(bool) { return false; } - - virtual bool RenameEntry(const wxString&, const wxString&) { return false; } - - virtual bool RenameGroup(const wxString&, const wxString&) { return false; } - - virtual bool DeleteEntry(const wxString&, bool bDeleteGroupIfEmpty = true) { return false; } - virtual bool DeleteGroup(const wxString&) { return false; } - virtual bool DeleteAll() { return false; } - -protected: - virtual bool DoReadString(const wxString&, wxString*) const { return false; } - virtual bool DoReadLong(const wxString&, long*) const { return false; } - - virtual bool DoWriteString(const wxString&, const wxString&) { return false; } - virtual bool DoWriteLong(const wxString&, long) { return false; } - -#if wxUSE_BASE64 - virtual bool DoReadBinary(const wxString& key, wxMemoryBuffer* buf) const - { - return false; - } - virtual bool DoWriteBinary(const wxString& key, const wxMemoryBuffer& buf) { return false; } -#endif -}; - -static pxDudConfig _dud_config; - -// -------------------------------------------------------------------------------------- -// AppIniSaver / AppIniLoader -// -------------------------------------------------------------------------------------- -class AppIniSaver : public IniSaver -{ -public: - AppIniSaver(); - virtual ~AppIniSaver() = default; -}; - -class AppIniLoader : public IniLoader -{ -public: - AppIniLoader(); - virtual ~AppIniLoader() = default; -}; - -AppIniSaver::AppIniSaver() - : IniSaver((GetAppConfig() != NULL) ? *GetAppConfig() : _dud_config) -{ -} - -AppIniLoader::AppIniLoader() - : IniLoader((GetAppConfig() != NULL) ? *GetAppConfig() : _dud_config) -{ -} - -static void LoadUiSettings() -{ - AppIniLoader loader; - ConLog_LoadSaveSettings(loader); - SysTraceLog_LoadSaveSettings(loader); - - { - wxSettingsInterface wxsi(&loader.GetConfig()); - SettingsLoadWrapper wrapper(wxsi); - g_Conf = std::make_unique(); - g_Conf->LoadSave(loader, wrapper); - } - - if (!wxFile::Exists(g_Conf->CurrentIso)) - { - g_Conf->CurrentIso.clear(); - } - - sApp.DispatchUiSettingsEvent(loader); -} - -static void LoadVmSettings() -{ - // Load virtual machine options and apply some defaults overtop saved items, which - // are regulated by the PCSX2 UI. - - std::unique_ptr vmini(OpenFileConfig(GetVmSettingsFilename())); - IniLoader vmloader(vmini.get()); - { - wxSettingsInterface wxsi(vmini.get()); - SettingsLoadWrapper vmwrapper(wxsi); - g_Conf->EmuOptions.LoadSave(vmwrapper); - g_Conf->EmuOptions.GS.LimitScalar = g_Conf->EmuOptions.Framerate.NominalScalar; - } - - if (g_Conf->EnablePresets) - { - g_Conf->IsOkApplyPreset(g_Conf->PresetIndex, true); - } - - sApp.DispatchVmSettingsEvent(vmloader); -} - -void AppLoadSettings() -{ - if (wxGetApp().Rpc_TryInvoke(AppLoadSettings)) - return; - - LoadUiSettings(); - LoadVmSettings(); -} - -static void SaveUiSettings() -{ - if (!wxFile::Exists(g_Conf->CurrentIso)) - { - g_Conf->CurrentIso.clear(); - } - - sApp.GetRecentIsoManager().Add(g_Conf->CurrentIso); - - AppIniSaver saver; - { - wxSettingsInterface wxsi(&saver.GetConfig()); - SettingsSaveWrapper wrapper(wxsi); - g_Conf->LoadSave(saver, wrapper); - } - ConLog_LoadSaveSettings(saver); - SysTraceLog_LoadSaveSettings(saver); - - sApp.DispatchUiSettingsEvent(saver); -} - -static void SaveVmSettings() -{ - std::unique_ptr vmini(OpenFileConfig(GetVmSettingsFilename())); - IniSaver vmsaver(vmini.get()); - { - wxSettingsInterface wxsi(vmini.get()); - SettingsSaveWrapper vmwrapper(wxsi); - g_Conf->EmuOptions.LoadSave(vmwrapper); - } - - sApp.DispatchVmSettingsEvent(vmsaver); -} - -void AppSaveSettings() -{ - // If multiple SaveSettings messages are requested, we want to ignore most of them. - // Saving settings once when the GUI is idle should be fine. :) - - static std::atomic isPosted(false); - - if (!wxThread::IsMain()) - { - if (!isPosted.exchange(true)) - wxGetApp().PostIdleMethod(AppSaveSettings); - - return; - } - - //Console.WriteLn("Saving ini files..."); - - SaveUiSettings(); - SaveVmSettings(); - - isPosted = false; -} - - -// Returns the current application configuration file. This is preferred over using -// wxConfigBase::GetAppConfig(), since it defaults to *not* creating a config file -// automatically (which is typically highly undesired behavior in our system) -wxConfigBase* GetAppConfig() -{ - return wxConfigBase::Get(false); -} - - -//Tests if a string is a valid name for a new file within a specified directory. -//returns true if: -// - the file name has a minimum length of minNumCharacters chars (default is 5 chars: at least 1 char + '.' + 3-chars extension) -// and - the file name is within the basepath directory (doesn't contain .. , / , \ , etc) -// and - file name doesn't already exist -// and - can be created on current system (it is actually created and deleted for this test). -bool isValidNewFilename(wxString filenameStringToTest, wxDirName atBasePath, wxString& out_errorMessage, uint minNumCharacters) -{ - if (filenameStringToTest.Length() < 1 || filenameStringToTest.Length() < minNumCharacters) - { - out_errorMessage = _("File name empty or too short"); - return false; - } - - if ((atBasePath + wxFileName(filenameStringToTest)).GetFullPath() != (atBasePath + wxFileName(filenameStringToTest).GetFullName()).GetFullPath()) - { - out_errorMessage = _("File name outside of required directory"); - return false; - } - - if (wxFileExists((atBasePath + wxFileName(filenameStringToTest)).GetFullPath())) - { - out_errorMessage = _("File name already exists"); - return false; - } - if (wxDirExists((atBasePath + wxFileName(filenameStringToTest)).GetFullPath())) - { - out_errorMessage = _("File name already exists"); - return false; - } - - wxFile fp; - if (!fp.Create((atBasePath + wxFileName(filenameStringToTest)).GetFullPath())) - { - out_errorMessage = _("The Operating-System prevents this file from being created"); - return false; - } - fp.Close(); - wxRemoveFile((atBasePath + wxFileName(filenameStringToTest)).GetFullPath()); - - out_errorMessage = L"[OK - New file name is valid]"; //shouldn't be displayed on success, hence not translatable. - return true; -} diff --git a/pcsx2/gui/AppConfig.h b/pcsx2/gui/AppConfig.h deleted file mode 100644 index ba7fc1875c..0000000000 --- a/pcsx2/gui/AppConfig.h +++ /dev/null @@ -1,339 +0,0 @@ -/* PCSX2 - PS2 Emulator for PCs - * Copyright (C) 2002-2010 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 . - */ - -#pragma once - -#include "AppForwardDefs.h" -#include "Config.h" -#include "PathDefs.h" -#include "CDVD/CDVDcommon.h" -#include "common/General.h" -#include "common/Path.h" - -#include -#include -#include - -class SettingsWrapper; - -enum DocsModeType -{ - // uses /home/user or /cwd for the program data. This is the default mode and is the most - // friendly to modern computing security requirements; as it isolates all file modification - // to a zone of the hard drive that has granted write permissions to the user. - DocsFolder_User, - - // uses a custom location for program data. Typically the custom folder is either the - // absolute or relative location of the program -- absolute is preferred because it is - // considered more secure by MSW standards, due to DLL search rules. - // - // To enable PCSX2's "portable" mode, use this setting and specify "." for the custom - // documents folder. - DocsFolder_Custom, -}; - -namespace PathDefs -{ - // complete pathnames are returned by these functions. - // These are used for initial default values when first configuring PCSX2, or when the - // user checks "Use Default paths" option provided on most path selectors. These are not - // used otherwise, in favor of the user-configurable specifications in the ini files. - - extern wxDirName GetUserLocalDataDir(); - extern wxDirName GetProgramDataDir(); - extern wxDirName GetDocuments(); - extern wxDirName GetDocuments( DocsModeType mode ); -} - -extern DocsModeType DocsFolderMode; // -extern bool UseDefaultSettingsFolder; // when TRUE, pcsx2 derives the settings folder from the DocsFolderMode - -extern wxDirName CustomDocumentsFolder; // allows the specification of a custom home folder for PCSX2 documents files. -extern wxDirName SettingsFolder; // dictates where the settings folder comes from, *if* UseDefaultSettingsFolder is FALSE. - -extern wxDirName InstallFolder; - -extern wxString GetVmSettingsFilename(); -extern wxString GetUiSettingsFilename(); -extern wxString GetUiKeysFilename(); - -enum InstallationModeType -{ - // Use the user defined folder selections. These can be anywhere on a user's hard drive, - // though by default thee user files (screenshots, inis) are in the user's documents - // folder. All folders are changable within the GUI. - InstallMode_Registered, - - // In this mode, both Install_Dir and UserDocuments folders default the directory containing - // PCSX2.exe, or the current working directory (if the PCSX2 directory could not be determined). - // Folders cannot be changed from within the gui, however the fixed defaults can be manually - // specified in the portable.ini by power users/devs. - // - // This mode is typically enabled by the presence of a 'portable.ini' in the folder. - InstallMode_Portable, -}; -bool IsPortable(); - -extern InstallationModeType InstallationMode; - -extern const wxChar* CDVD_SourceLabels[]; - -// ===================================================================================================== -// Pcsx2 Application Configuration. -// ===================================================================================================== - -class AppConfig -{ -public: - // ------------------------------------------------------------------------ - struct ConsoleLogOptions - { - bool Visible; - // if true, DisplayPos is ignored and the console is automatically docked to the main window. - bool AutoDock; - // Display position used if AutoDock is false (ignored otherwise) - wxPoint DisplayPosition; - wxSize DisplaySize; - - // Size of the font in points. - int FontSize; - - // Color theme by name! - wxString Theme; - - ConsoleLogOptions(); - void LoadSave( IniInterface& conf, const wxChar* title ); - }; - - // ------------------------------------------------------------------------ - struct FolderOptions - { - BITFIELD32() - bool - UseDefaultBios:1, - UseDefaultSnapshots:1, - UseDefaultSavestates:1, - UseDefaultMemoryCards:1, - UseDefaultLogs:1, - UseDefaultLangs:1, - UseDefaultCheats:1, - UseDefaultCheatsWS:1, - UseDefaultCache:1, - UseDefaultTextures:1; - BITFIELD_END - - wxDirName - Bios, - Snapshots, - Savestates, - MemoryCards, - Langs, - Logs, - Cheats, - CheatsWS, - Resources, - Cache, - Textures; - - wxDirName RunIso; // last used location for Iso loading. - wxDirName RunELF; // last used location for ELF loading. - wxString RunDisc; // last used location for Disc loading. - - FolderOptions(); - void LoadSave( IniInterface& conf ); - void ApplyDefaults(); - - void Set( FoldersEnum_t folderidx, const wxString& src, bool useDefault ); - - const wxDirName& operator[]( FoldersEnum_t folderidx ) const; - wxDirName& operator[]( FoldersEnum_t folderidx ); - bool IsDefault( FoldersEnum_t folderidx ) const; - }; - - // ------------------------------------------------------------------------ - // The GS window receives much love from the land of Options and Settings. - // - struct GSWindowOptions - { - // Closes the GS/Video port on escape (good for fullscreen activity) - bool CloseOnEsc; - bool DefaultToFullscreen; - bool AlwaysHideMouse; - bool DisableResizeBorders; - bool DisableScreenSaver; - - wxSize WindowSize; - wxPoint WindowPos; - - bool IsMaximized; - bool IsFullscreen; - bool EnableVsyncWindowFlag; - bool IsToggleFullscreenOnDoubleClick; - - GSWindowOptions(); - - void LoadSave( IniInterface& conf ); - void SanityCheck(); - }; - - struct InputRecordingOptions - { - wxPoint VirtualPadPosition; - int m_frame_advance_amount; - - InputRecordingOptions(); - void loadSave(IniInterface& conf); - }; - - struct UiTemplateOptions { - UiTemplateOptions(); - void LoadSave(IniInterface& conf); - - wxString LimiterUnlimited; - wxString LimiterTurbo; - wxString LimiterSlowmo; - wxString LimiterNormal; - wxString OutputFrame; - wxString OutputField; - wxString OutputProgressive; - wxString OutputInterlaced; - wxString Paused; - wxString TitleTemplate; - wxString RecordingTemplate; - }; - - struct CaptureOptions - { - bool EnableAudio; - - CaptureOptions(); - - void LoadSave(IniInterface& conf); - }; - -public: - wxPoint MainGuiPosition; - - // Because remembering the last used tab on the settings panel is cool (tab is remembered - // by it's UTF/ASCII name). - wxString SysSettingsTabName; - wxString McdSettingsTabName; - wxString ComponentsTabName; - wxString AppSettingsTabName; - wxString GameDatabaseTabName; - - // Currently selected language ID -- wxWidgets version-specific identifier. This is one side of - // a two-part configuration that also includes LanguageCode. - wxLanguage LanguageId; - - // Current language in use (correlates to the universal language codes, such as "en_US", "de_DE", etc). - // This code is not always unique, which is why we use the language ID also. - wxString LanguageCode; - - int RecentIsoCount; // number of files displayed in the Recent Isos list. - - // Specifies the size of icons used in Listbooks; specifically the PCSX2 Properties dialog box. - // Realistic values range from 96x96 to 24x24. - int Listbook_ImageSize; - - // Specifies the size of each toolbar icon, in pixels (any value >= 2 is valid, but realistically - // values should be between 64 and 16 for usability reasons) - int Toolbar_ImageSize; - - // Enables display of toolbar text labels. - bool Toolbar_ShowLabels; - - // Master toggle for enabling or disabling all speedhacks in one fail-free swoop. - // (the toggle is applied when a new EmuConfig is sent through AppCoreThread::ApplySettings) - bool EnableSpeedHacks; - bool EnableGameFixes; - bool EnableFastBoot; - - // Presets try to prevent users from overwhelming when they want to change settings (usually to make a game run faster). - // The presets allow to modify the balance between emulation accuracy and emulation speed using a pseudo-linear control. - // It's pseudo since there's no way to arrange groups of all of pcsx2's settings such that each next group makes it slighty faster and slightly less compatiible for all games. - //However, By carefully selecting these preset config groups, it's hopefully possible to achieve this goal for a reasonable percentage (hopefully above 50%) of the games. - //when presets are enabled, the user has practically no control over the emulation settings, and can only choose the preset to use. - - // The next 2 vars enable/disable presets alltogether, and select/reflect current preset, respectively. - bool EnablePresets; - int PresetIndex; - - bool AskOnBoot; - - bool DevMode; // Enable extra options in config menus - - - wxString CurrentIso; - wxString CurrentELF; - CDVD_SourceType CdvdSource; - - ConsoleLogOptions ProgLogBox; - FolderOptions Folders; - GSWindowOptions GSWindow; - InputRecordingOptions inputRecording; - UiTemplateOptions Templates; - CaptureOptions AudioCapture; - - // PCSX2-core emulation options, which are passed to the emu core prior to initiating - // an emulation session. Note these are the options saved into the GUI ini file and - // which are shown as options in the gui preferences, but *not* necessarily the options - // used by emulation. The gui allows temporary per-game and commandline level overrides. - Pcsx2Config EmuOptions; - -public: - AppConfig(); - std::string FullpathToSaveState(const std::string& serialName, const std::string& CRCvalue) const; - void LoadSave(IniInterface& ini, SettingsWrapper& wrap); - void LoadSaveRootItems(IniInterface& ini); - - static int GetMaxPresetIndex(); - static bool isOkGetPresetTextAndColor(int n, wxString& label, wxColor& c); - - bool IsOkApplyPreset(int n, bool ignoreMTVU); - - - //The next 2 flags are used with ApplyConfigToGui which the presets system use: - - //Indicates that the scope is only for preset-related items. - static const int APPLY_FLAG_FROM_PRESET = 0x01; - - //Indicates that the change should manually propagate to sub items because it's called directly and not as an event. - //Currently used by some panels which contain sub-panels which are affected by presets. - static const int APPLY_FLAG_MANUALLY_PROPAGATE = 0x02; - -}; - -extern void AppLoadSettings(); -extern void AppSaveSettings(); -extern void AppApplySettings( const AppConfig* oldconf=NULL ); -extern void AppSetEmuFolders(); - -extern void App_LoadSaveInstallSettings( IniInterface& ini ); -extern void App_SaveInstallSettings( wxConfigBase* ini ); -extern void App_LoadInstallSettings( wxConfigBase* ini ); - -extern void ConLog_LoadSaveSettings( IniInterface& ini ); -extern void SysTraceLog_LoadSaveSettings( IniInterface& ini ); - - -extern wxFileConfig* OpenFileConfig( const wxString& filename ); -extern void RelocateLogfile(); -extern void AppConfig_OnChangedSettingsFolder( bool overwrite = false ); -extern wxConfigBase* GetAppConfig(); - -extern std::unique_ptr g_Conf; - -extern bool isValidNewFilename(wxString filenameStringToTest, wxDirName atBasePath, wxString& out_errorMessage, uint minNumCharacters = 5); diff --git a/pcsx2/gui/AppCoreThread.cpp b/pcsx2/gui/AppCoreThread.cpp deleted file mode 100644 index bccc33ad59..0000000000 --- a/pcsx2/gui/AppCoreThread.cpp +++ /dev/null @@ -1,809 +0,0 @@ -/* PCSX2 - PS2 Emulator for PCs - * Copyright (C) 2002-2010 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 . - */ - -#include "PrecompiledHeader.h" -#include "App.h" -#include "AppSaveStates.h" -#include "GameDatabase.h" - -#include -#include -#include "fmt/core.h" - -#include "common/FileSystem.h" -#include "common/StringUtil.h" -#include "common/Threading.h" - -#include "Host.h" -#include "ps2/BiosTools.h" -#include "GS.h" - -#include "CDVD/CDVD.h" -#include "USB/USBNull.h" -#include "Elfheader.h" -#include "Patch.h" -#include "R5900Exceptions.h" -#include "Sio.h" - -#include "Recording/InputRecordingControls.h" - -alignas(16) SysMtgsThread mtgsThread; -alignas(16) AppCoreThread CoreThread; - -static std::vector s_widescreen_cheats_data; - -typedef void (AppCoreThread::*FnPtr_CoreThreadMethod)(); - -SysCoreThread& GetCoreThread() -{ - return CoreThread; -} - -SysMtgsThread& GetMTGS() -{ - return mtgsThread; -} - -namespace GameInfo -{ - wxString gameName; - wxString gameSerial; - wxString gameCRC; - wxString gameVersion; -}; // namespace GameInfo - -// -------------------------------------------------------------------------------------- -// SysExecEvent_InvokeCoreThreadMethod -// -------------------------------------------------------------------------------------- -class SysExecEvent_InvokeCoreThreadMethod : public SysExecEvent -{ -protected: - FnPtr_CoreThreadMethod m_method; - bool m_IsCritical; - -public: - wxString GetEventName() const { return L"CoreThreadMethod"; } - virtual ~SysExecEvent_InvokeCoreThreadMethod() = default; - SysExecEvent_InvokeCoreThreadMethod* Clone() const { return new SysExecEvent_InvokeCoreThreadMethod(*this); } - - bool AllowCancelOnExit() const { return false; } - bool IsCriticalEvent() const { return m_IsCritical; } - - SysExecEvent_InvokeCoreThreadMethod(FnPtr_CoreThreadMethod method, bool critical = false) - { - m_method = method; - m_IsCritical = critical; - } - - SysExecEvent_InvokeCoreThreadMethod& Critical() - { - m_IsCritical = true; - return *this; - } - -protected: - void InvokeEvent() - { - if (m_method) - (CoreThread.*m_method)(); - } -}; - -static void PostCoreStatus(CoreThreadStatus pevt) -{ - sApp.PostAction(CoreThreadStatusEvent(pevt)); -} - -// -------------------------------------------------------------------------------------- -// AppCoreThread Implementations -// -------------------------------------------------------------------------------------- -AppCoreThread::AppCoreThread() - : SysCoreThread() - , m_resetCdvd(false) -{ -} - -AppCoreThread::~AppCoreThread() -{ - try - { - _parent::Cancel(); // use parent's, skips thread affinity check. - } - DESTRUCTOR_CATCHALL -} - -static void _Cancel() -{ - GetCoreThread().Cancel(); -} - -void AppCoreThread::Cancel(bool isBlocking) -{ - if (GetSysExecutorThread().IsRunning() && !GetSysExecutorThread().Rpc_TryInvoke(_Cancel, L"AppCoreThread::Cancel")) - _parent::Cancel(wxTimeSpan(0, 0, 4, 0)); -} - -void AppCoreThread::Reset() -{ - if (!GetSysExecutorThread().IsSelf()) - { - GetSysExecutorThread().PostEvent(SysExecEvent_InvokeCoreThreadMethod(&AppCoreThread::Reset)); - return; - } - - _parent::Reset(); -} - -void AppCoreThread::ResetQuick() -{ - if (!GetSysExecutorThread().IsSelf()) - { - GetSysExecutorThread().PostEvent(SysExecEvent_InvokeCoreThreadMethod(&AppCoreThread::ResetQuick)); - return; - } - - _parent::ResetQuick(); -} - -ExecutorThread& GetSysExecutorThread() -{ - return wxGetApp().SysExecutorThread; -} - -static void _Suspend() -{ - GetCoreThread().Suspend(true); -} - -void AppCoreThread::Suspend(bool isBlocking) -{ - if (IsClosed()) - return; - - if (IsSelf()) - { - // this should never fail... - bool result = GetSysExecutorThread().Rpc_TryInvokeAsync(_Suspend, L"AppCoreThread::Suspend"); - pxAssert(result); - } - else if (!GetSysExecutorThread().Rpc_TryInvoke(_Suspend, L"AppCoreThread::Suspend")) - _parent::Suspend(true); -} - -void AppCoreThread::Resume() -{ - if (!GetSysExecutorThread().IsSelf()) - { - GetSysExecutorThread().PostEvent(SysExecEvent_InvokeCoreThreadMethod(&AppCoreThread::Resume)); - return; - } - _parent::Resume(); -} - -void AppCoreThread::ChangeCdvdSource() -{ - if (!GetSysExecutorThread().IsSelf()) - { - GetSysExecutorThread().PostEvent(new SysExecEvent_InvokeCoreThreadMethod(&AppCoreThread::ChangeCdvdSource)); - return; - } - - CDVD_SourceType cdvdsrc(g_Conf->CdvdSource); - if (cdvdsrc == CDVDsys_GetSourceType()) - return; - - // Fast change of the CDVD source only -- a Pause will suffice. - - ScopedCoreThreadPause paused_core; - CDVDsys_ChangeSource(cdvdsrc); - paused_core.AllowResume(); - - // TODO: Add a listener for CDVDsource changes? Or should we bother? -} - -void Pcsx2App::SysApplySettings() -{ - if (AppRpc_TryInvoke(&Pcsx2App::SysApplySettings)) - return; - CoreThread.ApplySettings(g_Conf->EmuOptions); - - const CDVD_SourceType cdvdsrc(g_Conf->CdvdSource); - const std::string currentIso(StringUtil::wxStringToUTF8String(g_Conf->CurrentIso)); - const std::string currentDisc(StringUtil::wxStringToUTF8String(g_Conf->Folders.RunDisc)); - if (cdvdsrc != CDVDsys_GetSourceType() || - CDVDsys_GetFile(CDVD_SourceType::Iso) != currentIso || - CDVDsys_GetFile(CDVD_SourceType::Disc) != currentDisc) - { - CoreThread.ResetCdvd(); - } - - CDVDsys_SetFile(CDVD_SourceType::Iso, currentIso); - CDVDsys_SetFile(CDVD_SourceType::Disc, currentDisc); -} - -void AppCoreThread::OnResumeReady() -{ - wxGetApp().SysApplySettings(); - wxGetApp().PostMethod(AppSaveSettings); - sApp.PostAppMethod(&Pcsx2App::leaveDebugMode); - _parent::OnResumeReady(); -} - -void AppCoreThread::OnPause() -{ - //sApp.PostAppMethod( &Pcsx2App::enterDebugMode ); - _parent::OnPause(); -} - -void AppCoreThread::OnPauseDebug() -{ - sApp.PostAppMethod(&Pcsx2App::enterDebugMode); - _parent::OnPause(); -} - -// Load Game Settings found in database -// (game fixes, round modes, clamp modes, etc...) -// Returns number of gamefixes set -static int loadGameSettings(Pcsx2Config& dest, const GameDatabaseSchema::GameEntry& game) -{ - int gf = 0; - - gf += game.applyGameFixes(dest, dest.EnablePatches); - gf += game.applyGSHardwareFixes(dest.GS); - - return gf; -} - -// Used to track the current game serial/id, and used to disable verbose logging of -// applied patches if the game info hasn't changed. (avoids spam when suspending/resuming -// or using TAB or other things), but gets verbose again when booting (even if the same game). -// File scope since it gets reset externally when rebooting -#define _UNKNOWN_GAME_KEY (L"_UNKNOWN_GAME_KEY") -static wxString curGameKey = _UNKNOWN_GAME_KEY; - -void PatchesVerboseReset() -{ - curGameKey = _UNKNOWN_GAME_KEY; -} - -// PatchesCon points to either Console or ConsoleWriter_Null, such that if we're in Devel mode -// or the user enabled the devel/verbose console it prints all patching info whenever it's applied, -// else it prints the patching info only once - right after boot. -const IConsoleWriter* PatchesCon = &Console; - -static void SetupPatchesCon(bool verbose) -{ -#ifdef PCSX2_DEVBUILD - PatchesCon = &Console; -#else - if (verbose || DevConWriterEnabled) - PatchesCon = &Console; - else - PatchesCon = &ConsoleWriter_Null; -#endif -} - -// fixup = src + command line overrides + game overrides (according to elfCRC). -// While at it, also [re]loads the relevant patches (but doesn't apply them), -// updates the console title, and, for good measures, does some (static) sio stuff. -// Oh, and updates curGameKey. I think that's it. -// It doesn't require that the emulation is paused, and console writes/title should -// be thread safe, but it's best if things don't move around much while it runs. -static Threading::Mutex mtx__ApplySettings; -static void _ApplySettings(const Pcsx2Config& src, Pcsx2Config& fixup) -{ - Threading::ScopedLock lock(mtx__ApplySettings); - // 'fixup' is the EmuConfig we're going to upload to the emulator, which very well may - // differ from the user-configured EmuConfig settings. So we make a copy here and then - // we apply the commandline overrides and database gamefixes, and then upload 'fixup' - // to the global EmuConfig. - // - // Note: It's important that we apply the commandline overrides *before* database fixes. - // The database takes precedence (if enabled). - - fixup.CopyConfig(src); - - const CommandlineOverrides& overrides(wxGetApp().Overrides); - if (overrides.DisableSpeedhacks || !g_Conf->EnableSpeedHacks) - fixup.Speedhacks.DisableAll(); - - if (overrides.ApplyCustomGamefixes) - { - for (GamefixId id = GamefixId_FIRST; id < pxEnumEnd; ++id) - fixup.Gamefixes.Set(id, overrides.Gamefixes.Get(id)); - } - else if (!g_Conf->EnableGameFixes) - fixup.Gamefixes.DisableAll(); - - if (overrides.ProfilingMode) - { - fixup.GS.FrameLimitEnable = false; - fixup.GS.VsyncEnable = VsyncMode::Off; - } - - // Remove any user-specified hacks in the config (we don't want stale/conflicting values when it's globally disabled). - fixup.GS.MaskUserHacks(); - fixup.GS.MaskUpscalingHacks(); - - wxString gamePatch; - wxString gameFixes; - wxString gameCheats; - wxString gameWsHacks; - - wxString gameCompat; - wxString gameMemCardFilter; - - // The CRC can be known before the game actually starts (at the bios), so when - // we have the CRC but we're still at the bios and the settings are changed - // (e.g. the user presses TAB to speed up emulation), we don't want to apply the - // settings as if the game is already running (title, loadeding patches, etc). - bool ingame = (ElfCRC && (g_GameLoading || g_GameStarted)); - if (ingame) - GameInfo::gameCRC.Printf(L"%8.8X", ElfCRC); - else - GameInfo::gameCRC = L""; // Needs to be reset when rebooting otherwise previously loaded patches may load - - if (ingame && !DiscSerial.empty()) - GameInfo::gameSerial = StringUtil::UTF8StringToWxString(DiscSerial); - - const wxString newGameKey(StringUtil::UTF8StringToWxString(ingame ? SysGetDiscID() : SysGetBiosDiscID())); - const bool verbose(newGameKey != curGameKey && ingame); - //Console.WriteLn(L"------> patches verbose: %d prev: '%s' new: '%s'", (int)verbose, WX_STR(curGameKey), WX_STR(newGameKey)); - SetupPatchesCon(verbose); - - curGameKey = newGameKey; - - ForgetLoadedPatches(); - - if (!curGameKey.IsEmpty()) - { - auto game = GameDatabase::findGame(std::string(curGameKey.ToUTF8())); - if (game) - { - GameInfo::gameName = StringUtil::UTF8StringToWxString(StringUtil::StdStringFromFormat("%s (%s)", game->name.c_str(), game->region.c_str())); - gameCompat.Printf(" [Status = %s]", game->compatAsString()); - gameMemCardFilter = StringUtil::UTF8StringToWxString(game->memcardFiltersAsString()); - - if (fixup.EnablePatches) - { - const std::string* patches = ingame ? game->findPatch(ElfCRC) : 0; - int numPatches; - if (patches && (numPatches = LoadPatchesFromString(*patches)) > 0) - { - gamePatch.Printf(L" [%d Patches]", numPatches); - PatchesCon->WriteLn(Color_Green, "(GameDB) Patches Loaded: %d", numPatches); - } - } - if (int fixes = loadGameSettings(fixup, *game)) - gameFixes.Printf(L" [%d Fixes]", fixes); - } - else - { - // Set correct title for loading standalone/homebrew ELFs - GameInfo::gameName = StringUtil::UTF8StringToWxString(LastELF).AfterLast('\\'); - } - } - - if (!gameMemCardFilter.IsEmpty()) - sioSetGameSerial(StringUtil::wxStringToUTF8String(gameMemCardFilter)); - else - sioSetGameSerial(StringUtil::wxStringToUTF8String(curGameKey)); - - if (GameInfo::gameName.IsEmpty() && GameInfo::gameSerial.IsEmpty() && GameInfo::gameCRC.IsEmpty()) - { - // if all these conditions are met, it should mean that we're currently running BIOS code. - // Chances are the BiosChecksum value is still zero or out of date, however -- because - // the BIos isn't loaded until after initial calls to ApplySettings. - - GameInfo::gameName = L"Booting PS2 BIOS... "; - } - - //Till the end of this function, entry CRC will be 00000000 - if (!GameInfo::gameCRC.Length()) - { - Console.WriteLn(Color_Gray, "Patches: No CRC found, using 00000000 instead."); - GameInfo::gameCRC = L"00000000"; - } - - // regular cheat patches - if (fixup.EnableCheats) - gameCheats.Printf(L" [%d Cheats]", LoadPatchesFromDir(StringUtil::wxStringToUTF8String(GameInfo::gameCRC), EmuFolders::Cheats, "Cheats", true)); - - // wide screen patches - if (fixup.EnableWideScreenPatches) - { - if (int numberLoadedWideScreenPatches = LoadPatchesFromDir(StringUtil::wxStringToUTF8String(GameInfo::gameCRC), EmuFolders::CheatsWS, "Widescreen hacks", false)) - { - gameWsHacks.Printf(L" [%d widescreen hacks]", numberLoadedWideScreenPatches); - Console.WriteLn(Color_Gray, "Found widescreen patches in the cheats_ws folder --> skipping cheats_ws.zip"); - } - else - { - // No ws cheat files found at the cheats_ws folder, try the ws cheats zip file. - std::optional> data = Host::ReadResourceFile("cheats_ws.zip"); - if (data.has_value()) - s_widescreen_cheats_data = std::move(data.value()); - - if (!s_widescreen_cheats_data.empty()) - { - int numberDbfCheatsLoaded = LoadPatchesFromZip(StringUtil::wxStringToUTF8String(GameInfo::gameCRC), s_widescreen_cheats_data.data(), s_widescreen_cheats_data.size()); - PatchesCon->WriteLn(Color_Green, "(Wide Screen Cheats DB) Patches Loaded: %d", numberDbfCheatsLoaded); - gameWsHacks.Printf(L" [%d widescreen hacks]", numberDbfCheatsLoaded); - } - } - } - - // When we're booting, the bios loader will set a a title which would be more interesting than this - // to most users - with region, version, etc, so don't overwrite it with patch info. That's OK. Those - // users which want to know the status of the patches at the bios can check the console content. - wxString consoleTitle = GameInfo::gameName + L" [" + GameInfo::gameSerial + L"]"; - consoleTitle += L" [" + GameInfo::gameCRC + L"]" + gameCompat + gameFixes + gamePatch + gameCheats + gameWsHacks; - if (ingame) - Console.SetTitle(consoleTitle.ToUTF8()); - - gsUpdateFrequency(fixup); -} - -// FIXME: This function is not for general consumption. Its only consumer (and -// the only place it's declared at) is i5900-32.cpp . -// It's here specifically to allow loading the patches synchronously (to the caller) -// when the recompiler detects the game's elf entry point, such that the patches -// are applied to the elf in memory before it's getting recompiled. -// TODO: Find a way to pause the recompiler once it detects the elf entry, then -// make AppCoreThread::ApplySettings run more normally, and then resume -// the recompiler. -// The key point is that the patches should be loaded exactly before the elf -// is recompiled. Loading them earlier might incorrectly patch the bios memory, -// and later might be too late since the code was already recompiled -void LoadAllPatchesAndStuff(const Pcsx2Config& cfg) -{ - Pcsx2Config dummy; - PatchesVerboseReset(); - _ApplySettings(cfg, dummy); - - // And I'm hacking in updating the UI here too. -#ifdef USE_SAVESLOT_UI_UPDATES - UI_UpdateSysControls(); -#endif -} - -void AppCoreThread::ApplySettings(const Pcsx2Config& src) -{ - // Re-entry guard protects against cases where code wants to manually set core settings - // which are not part of g_Conf. The subsequent call to apply g_Conf settings (which is - // usually the desired behavior) will be ignored. - Pcsx2Config fixup; - _ApplySettings(src, fixup); - - static int localc = 0; - RecursionGuard guard(localc); - if (guard.IsReentrant()) - return; - if (fixup == EmuConfig) - return; - - if (m_ExecMode >= ExecMode_Opened && !IsSelf()) - { - ScopedCoreThreadPause paused_core; - _parent::ApplySettings(fixup); - paused_core.AllowResume(); - } - else - { - _parent::ApplySettings(fixup); - } -} - -// -------------------------------------------------------------------------------------- -// AppCoreThread *Worker* Implementations -// (Called from the context of this thread only) -// -------------------------------------------------------------------------------------- - -void AppCoreThread::DoCpuReset() -{ - PostCoreStatus(CoreThread_Reset); - _parent::DoCpuReset(); -} - -void AppCoreThread::OnResumeInThread(SystemsMask systemsToReinstate) -{ - if (m_resetCdvd) - { - CDVDsys_ChangeSource(g_Conf->CdvdSource); - DoCDVDopen(); - cdvdCtrlTrayOpen(); - m_resetCdvd = false; - } - else if (systemsToReinstate & System_CDVD) - DoCDVDopen(); - - _parent::OnResumeInThread(systemsToReinstate); - PostCoreStatus(CoreThread_Resumed); -} - -void AppCoreThread::OnSuspendInThread() -{ - _parent::OnSuspendInThread(); - PostCoreStatus(CoreThread_Suspended); -} - -// Called whenever the thread has terminated, for either regular or irregular reasons. -// Typically the thread handles all its own errors, so there's no need to have error -// handling here. However it's a good idea to update the status of the GUI to reflect -// the new (lack of) thread status, so this posts a message to the App to do so. -void AppCoreThread::OnCleanupInThread() -{ - m_ExecMode = ExecMode_Closing; - PostCoreStatus(CoreThread_Stopped); - _parent::OnCleanupInThread(); -} - -void AppCoreThread::VsyncInThread() -{ - wxGetApp().LogicalVsync(); - _parent::VsyncInThread(); -} - -void AppCoreThread::GameStartingInThread() -{ - // Simulate a Close/Resume, so that settings get re-applied and the database - // lookups and other game-based detections are done. - - m_ExecMode = ExecMode_Paused; - OnResumeReady(); - _reset_stuff_as_needed(); - AutoEject::ClearAll(); // probably safe to do this when a game boots, eliminates annoying prompts - m_ExecMode = ExecMode_Opened; - - _parent::GameStartingInThread(); -} - -bool AppCoreThread::StateCheckInThread() -{ - return _parent::StateCheckInThread(); -} - -static uint m_except_threshold = 0; - -void AppCoreThread::ExecuteTaskInThread() -{ - PostCoreStatus(CoreThread_Started); - m_except_threshold = 0; - _parent::ExecuteTaskInThread(); -} - -void AppCoreThread::DoCpuExecute() -{ - try - { - _parent::DoCpuExecute(); - } - catch (BaseR5900Exception& ex) - { - Console.Error(ex.FormatMessage()); - - // [TODO] : Debugger Hook! - - if (++m_except_threshold > 6) - { - // If too many TLB Misses occur, we're probably going to crash and - // the game is probably running miserably. - - m_except_threshold = 0; - //Suspend(); - - // [TODO] Issue error dialog to the user here... - Console.Error("Too many execution errors. VM execution has been suspended!"); - - // Hack: this keeps the EE thread from running more code while the SysExecutor - // thread catches up and signals it for suspension. - m_ExecMode = ExecMode_Closing; - } - } -} - -// -------------------------------------------------------------------------------------- -// BaseSysExecEvent_ScopedCore / SysExecEvent_CoreThreadClose / SysExecEvent_CoreThreadPause -// -------------------------------------------------------------------------------------- -void BaseSysExecEvent_ScopedCore::_post_and_wait(IScopedCoreThread& core) -{ - DoScopedTask(); - - ScopedLock lock(m_mtx_resume); - PostResult(); - - if (m_resume) - { - // If the sender of the message requests a non-blocking resume, then we need - // to deallocate the m_sync object, since the sender will likely leave scope and - // invalidate it. - switch (m_resume->WaitForResult()) - { - case ScopedCore_BlockingResume: - if (m_sync) - m_sync->ClearResult(); - core.AllowResume(); - break; - - case ScopedCore_NonblockingResume: - m_sync = NULL; - core.AllowResume(); - break; - - case ScopedCore_SkipResume: - m_sync = NULL; - break; - } - } -} - - -void SysExecEvent_CoreThreadClose::InvokeEvent() -{ - ScopedCoreThreadClose closed_core; - _post_and_wait(closed_core); - closed_core.AllowResume(); -} - - -void SysExecEvent_CoreThreadPause::InvokeEvent() -{ - ScopedCoreThreadPause paused_core(m_systemsToTearDown); - _post_and_wait(paused_core); - paused_core.AllowResume(); -} - - -// -------------------------------------------------------------------------------------- -// ScopedCoreThreadClose / ScopedCoreThreadPause -// -------------------------------------------------------------------------------------- - -static thread_local bool ScopedCore_IsPaused = false; -static thread_local bool ScopedCore_IsFullyClosed = false; - -BaseScopedCoreThread::BaseScopedCoreThread() -{ - //AffinityAssert_AllowFrom_MainUI(); - - m_allowResume = false; - m_alreadyStopped = false; - m_alreadyScoped = false; -} - -BaseScopedCoreThread::~BaseScopedCoreThread() = default; - -// Allows the object to resume execution upon object destruction. Typically called as the last thing -// in the object's scope. Any code prior to this call that causes exceptions will not resume the emulator, -// which is *typically* the intended behavior when errors occur. -void BaseScopedCoreThread::AllowResume() -{ - m_allowResume = true; -} - -void BaseScopedCoreThread::DisallowResume() -{ - m_allowResume = false; -} - -void BaseScopedCoreThread::DoResume() -{ - if (m_alreadyStopped) - return; - if (!GetSysExecutorThread().IsSelf()) - { - //DbgCon.WriteLn("(ScopedCoreThreadPause) Threaded Scope Created!"); - m_sync_resume.PostResult(m_allowResume ? ScopedCore_NonblockingResume : ScopedCore_SkipResume); - m_mtx_resume.Wait(); - } - else - CoreThread.Resume(); -} - -// Returns TRUE if the event is posted to the SysExecutor. -// Returns FALSE if the thread *is* the SysExecutor (no message is posted, calling code should -// handle the code directly). -bool BaseScopedCoreThread::PostToSysExec(std::unique_ptr msg) -{ - if (!msg || GetSysExecutorThread().IsSelf()) - return false; - - msg->SetSyncState(m_sync); - msg->SetResumeStates(m_sync_resume, m_mtx_resume); - - GetSysExecutorThread().PostEvent(msg.release()); - m_sync.WaitForResult(); - m_sync.RethrowException(); - - return true; -} - -ScopedCoreThreadClose::ScopedCoreThreadClose() -{ - if (ScopedCore_IsFullyClosed) - { - // tracks if we're already in scope or not. - m_alreadyScoped = true; - return; - } - - if (!PostToSysExec(std::make_unique())) - { - m_alreadyStopped = CoreThread.IsClosed(); - if (!m_alreadyStopped) - CoreThread.Suspend(); - } - - ScopedCore_IsFullyClosed = true; -} - -ScopedCoreThreadClose::~ScopedCoreThreadClose() -{ - if (m_alreadyScoped) - return; - try - { - _parent::DoResume(); - ScopedCore_IsFullyClosed = false; - } - DESTRUCTOR_CATCHALL -} - -ScopedCoreThreadPause::ScopedCoreThreadPause(SystemsMask systemsToTearDown) -{ - if (ScopedCore_IsFullyClosed || ScopedCore_IsPaused) - { - // tracks if we're already in scope or not. - m_alreadyScoped = true; - return; - } - - if (!PostToSysExec(std::make_unique(systemsToTearDown))) - { - m_alreadyStopped = CoreThread.IsPaused(); - if (!m_alreadyStopped) - CoreThread.Pause(systemsToTearDown); - } - - ScopedCore_IsPaused = true; -} - -ScopedCoreThreadPause::~ScopedCoreThreadPause() -{ - if (m_alreadyScoped) - return; - try - { - _parent::DoResume(); - ScopedCore_IsPaused = false; - } - DESTRUCTOR_CATCHALL -} - -ScopedCoreThreadPopup::ScopedCoreThreadPopup() - : m_scoped_core(std::unique_ptr(new ScopedCoreThreadPause())) -{ -}; - -void ScopedCoreThreadPopup::AllowResume() -{ - if (m_scoped_core) - m_scoped_core->AllowResume(); -} - -void ScopedCoreThreadPopup::DisallowResume() -{ - if (m_scoped_core) - m_scoped_core->DisallowResume(); -} diff --git a/pcsx2/gui/AppCoreThread.h b/pcsx2/gui/AppCoreThread.h deleted file mode 100644 index b7fe1dac15..0000000000 --- a/pcsx2/gui/AppCoreThread.h +++ /dev/null @@ -1,264 +0,0 @@ -/* PCSX2 - PS2 Emulator for PCs - * Copyright (C) 2002-2010 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 . - */ - -#pragma once - -#include "SysThreads.h" -#include "pxEventThread.h" - -#include "AppCommon.h" -#include "SaveState.h" - -#include - -#define AffinityAssert_AllowFrom_CoreThread() \ - pxAssertMsg(GetCoreThread().IsSelf(), "Thread affinity violation: Call allowed from SysCoreThread only.") - -#define AffinityAssert_DisallowFrom_CoreThread() \ - pxAssertMsg(!GetCoreThread().IsSelf(), "Thread affinity violation: Call is *not* allowed from SysCoreThread.") - -class IScopedCoreThread; -class BaseScopedCoreThread; - -enum ScopedCoreResumeType -{ - ScopedCore_BlockingResume, - ScopedCore_NonblockingResume, - ScopedCore_SkipResume -}; - -namespace GameInfo -{ - extern wxString gameName; - extern wxString gameSerial; - extern wxString gameCRC; - extern wxString gameVersion; -}; // namespace GameInfo - - -// -------------------------------------------------------------------------------------- -// BaseSysExecEvent_ScopedCore -// -------------------------------------------------------------------------------------- -class BaseSysExecEvent_ScopedCore : public SysExecEvent -{ -protected: - SynchronousActionState* m_resume; - Threading::Mutex* m_mtx_resume; - -public: - virtual ~BaseSysExecEvent_ScopedCore() = default; - - BaseSysExecEvent_ScopedCore& SetResumeStates(SynchronousActionState* sync, Threading::Mutex* mutex) - { - m_resume = sync; - m_mtx_resume = mutex; - return *this; - } - - BaseSysExecEvent_ScopedCore& SetResumeStates(SynchronousActionState& sync, Threading::Mutex& mutex) - { - m_resume = &sync; - m_mtx_resume = &mutex; - return *this; - } - -protected: - BaseSysExecEvent_ScopedCore(SynchronousActionState* sync = NULL, SynchronousActionState* resume_sync = NULL, Threading::Mutex* mtx_resume = NULL) - : SysExecEvent(sync) - { - m_resume = resume_sync; - m_mtx_resume = mtx_resume; - } - - void _post_and_wait(IScopedCoreThread& core); - - virtual void DoScopedTask() {} -}; - - -// -------------------------------------------------------------------------------------- -// SysExecEvent_CoreThreadClose -// -------------------------------------------------------------------------------------- -class SysExecEvent_CoreThreadClose : public BaseSysExecEvent_ScopedCore -{ -public: - wxString GetEventName() const { return L"CloseCoreThread"; } - - virtual ~SysExecEvent_CoreThreadClose() = default; - SysExecEvent_CoreThreadClose* Clone() const { return new SysExecEvent_CoreThreadClose(*this); } - - SysExecEvent_CoreThreadClose(SynchronousActionState* sync = NULL, SynchronousActionState* resume_sync = NULL, Threading::Mutex* mtx_resume = NULL) - : BaseSysExecEvent_ScopedCore(sync, resume_sync, mtx_resume) - { - } - -protected: - void InvokeEvent(); -}; - -// -------------------------------------------------------------------------------------- -// SysExecEvent_CoreThreadPause -// -------------------------------------------------------------------------------------- -class SysExecEvent_CoreThreadPause : public BaseSysExecEvent_ScopedCore -{ -public: - wxString GetEventName() const { return L"PauseCoreThread"; } - - virtual ~SysExecEvent_CoreThreadPause() = default; - SysExecEvent_CoreThreadPause* Clone() const { return new SysExecEvent_CoreThreadPause(*this); } - - SysExecEvent_CoreThreadPause(SystemsMask systemsToTearDown, SynchronousActionState* sync = NULL, SynchronousActionState* resume_sync = NULL, Threading::Mutex* mtx_resume = NULL) - : BaseSysExecEvent_ScopedCore(sync, resume_sync, mtx_resume) - , m_systemsToTearDown(systemsToTearDown) - { - } - -protected: - void InvokeEvent(); - -private: - SystemsMask m_systemsToTearDown; -}; - -// -------------------------------------------------------------------------------------- -// AppCoreThread class -// -------------------------------------------------------------------------------------- -class AppCoreThread : public SysCoreThread -{ - typedef SysCoreThread _parent; - -protected: - std::atomic m_resetCdvd; - -public: - AppCoreThread(); - virtual ~AppCoreThread(); - - void ResetCdvd() { m_resetCdvd = true; } - - virtual void Suspend(bool isBlocking = false) override; - virtual void Resume() override; - virtual void Reset() override; - virtual void ResetQuick() override; - virtual void Cancel(bool isBlocking = true) override; - virtual bool StateCheckInThread() override; - virtual void ChangeCdvdSource(); - - virtual void ApplySettings(const Pcsx2Config& src) override; - -protected: - virtual void DoCpuExecute() override; - - virtual void OnResumeReady() override; - virtual void OnPause() override; - virtual void OnPauseDebug() override; - virtual void OnResumeInThread(SystemsMask systemsToReinstate) override; - virtual void OnSuspendInThread() override; - virtual void OnCleanupInThread() override; - virtual void VsyncInThread() override; - virtual void GameStartingInThread() override; - virtual void ExecuteTaskInThread() override; - virtual void DoCpuReset() override; -}; - -// -------------------------------------------------------------------------------------- -// IScopedCoreThread / BaseScopedCoreThread -// -------------------------------------------------------------------------------------- -class IScopedCoreThread -{ -protected: - IScopedCoreThread() {} - -public: - virtual ~IScopedCoreThread() = default; - ; - virtual void AllowResume() = 0; - virtual void DisallowResume() = 0; -}; - -class BaseScopedCoreThread : public IScopedCoreThread -{ - DeclareNoncopyableObject(BaseScopedCoreThread); - -protected: - bool m_allowResume; - bool m_alreadyStopped; - bool m_alreadyScoped; - SynchronousActionState m_sync; - SynchronousActionState m_sync_resume; - Threading::Mutex m_mtx_resume; - - BaseScopedCoreThread(); - -public: - virtual ~BaseScopedCoreThread() = 0; - virtual void AllowResume(); - virtual void DisallowResume(); - - virtual bool PostToSysExec(std::unique_ptr msg); - -protected: - // Called from destructors -- do not make virtual!! - void DoResume(); -}; - -// -------------------------------------------------------------------------------------- -// ScopedCoreThreadClose / ScopedCoreThreadPause / ScopedCoreThreadPopup -// -------------------------------------------------------------------------------------- -// This class behaves a bit differently from other scoped classes due to the "standard" -// assumption that we actually do *not* want to resume CoreThread operations when an -// exception occurs. Because of this, the destructor of this class does *not* unroll the -// suspend operation. Instead you must manually instruct the class to resume using a call -// to the provisioned AllowResume() method. -// -// If the class leaves scope without having been resumed, a log is written to the console. -// This can be useful for troubleshooting, and also allows the log a second line of info -// indicating the status of CoreThread execution at the time of the exception. -// -// ScopedCoreThreadPopup is intended for use where message boxes are popped up to the user. -// The old style GUI (without GSopen2) must use a full close of the CoreThread, in order to -// ensure that the GS window isn't blocking the popup, and to avoid crashes if the GS window -// is maximized or fullscreen. -// -class ScopedCoreThreadClose : public BaseScopedCoreThread -{ - typedef BaseScopedCoreThread _parent; - -public: - ScopedCoreThreadClose(); - virtual ~ScopedCoreThreadClose(); -}; - -struct ScopedCoreThreadPause : public BaseScopedCoreThread -{ - typedef BaseScopedCoreThread _parent; - -public: - ScopedCoreThreadPause(SystemsMask systemsToTearDown = {}); - virtual ~ScopedCoreThreadPause(); -}; - -struct ScopedCoreThreadPopup : public IScopedCoreThread -{ -protected: - std::unique_ptr m_scoped_core; - -public: - ScopedCoreThreadPopup(); - virtual ~ScopedCoreThreadPopup() = default; - - virtual void AllowResume(); - virtual void DisallowResume(); -}; diff --git a/pcsx2/gui/AppEventListeners.h b/pcsx2/gui/AppEventListeners.h deleted file mode 100644 index 79bc53f326..0000000000 --- a/pcsx2/gui/AppEventListeners.h +++ /dev/null @@ -1,211 +0,0 @@ -/* PCSX2 - PS2 Emulator for PCs - * Copyright (C) 2002-2010 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 . - */ - -#pragma once - -#include "common/EventSource.h" -#include "gui/IniInterface.h" -#include "gui/pxEvents.h" - -enum CoreThreadStatus -{ - CoreThread_Indeterminate, - CoreThread_Started, - CoreThread_Resumed, - CoreThread_Suspended, - CoreThread_Reset, - CoreThread_Stopped, -}; - -enum AppEventType -{ - AppStatus_UiSettingsLoaded, - AppStatus_UiSettingsSaved, - AppStatus_VmSettingsLoaded, - AppStatus_VmSettingsSaved, - - AppStatus_SettingsApplied, - AppStatus_Exiting -}; - -struct AppEventInfo -{ - AppEventType evt_type; - - AppEventInfo( AppEventType type ) - { - evt_type = type; - } -}; - -struct AppSettingsEventInfo : AppEventInfo -{ - IniInterface& m_ini; - - AppSettingsEventInfo( IniInterface& ini, AppEventType evt_type ); - - IniInterface& GetIni() const - { - return const_cast(m_ini); - } -}; - -// -------------------------------------------------------------------------------------- -// IEventListener_CoreThread -// -------------------------------------------------------------------------------------- -class IEventListener_CoreThread : public IEventDispatcher -{ -public: - typedef CoreThreadStatus EvtParams; - -public: - virtual ~IEventListener_CoreThread() = default; - - virtual void DispatchEvent( const CoreThreadStatus& status ); - -protected: - virtual void CoreThread_OnStarted() {} - virtual void CoreThread_OnResumed() {} - virtual void CoreThread_OnSuspended() {} - virtual void CoreThread_OnReset() {} - virtual void CoreThread_OnStopped() {} -}; - -class EventListener_CoreThread : public IEventListener_CoreThread -{ -public: - EventListener_CoreThread(); - virtual ~EventListener_CoreThread(); -}; - -// -------------------------------------------------------------------------------------- -// IEventListener_AppStatus -// -------------------------------------------------------------------------------------- -class IEventListener_AppStatus : public IEventDispatcher -{ -public: - typedef AppEventInfo EvtParams; - -public: - virtual ~IEventListener_AppStatus() = default; - - virtual void DispatchEvent( const AppEventInfo& evtinfo ); - -protected: - virtual void AppStatusEvent_OnUiSettingsLoadSave( const AppSettingsEventInfo& evtinfo ) {} - virtual void AppStatusEvent_OnVmSettingsLoadSave( const AppSettingsEventInfo& evtinfo ) {} - - virtual void AppStatusEvent_OnSettingsApplied() {} - virtual void AppStatusEvent_OnExit() {} -}; - -class EventListener_AppStatus : public IEventListener_AppStatus -{ -public: - EventListener_AppStatus(); - virtual ~EventListener_AppStatus(); -}; - -// -------------------------------------------------------------------------------------- -// EventListenerHelpers (CoreThread / AppStatus) -// -------------------------------------------------------------------------------------- -// Welcome to the awkward world of C++ multi-inheritence. wxWidgets' Connect() system is -// incompatible because of limitations in C++ class member function pointers, so we need -// this second layer class to act as a bridge between the event system and the class's -// handler implementations. -// -// Explained in detail: The class that wants to listen to shit will implement its expected -// virtual overrides from the listener classes (OnCoreThread_Started, for example), and then -// it adds an instance of the EventListenerHelper_CoreThread class to itself, instead of -// *inheriting* from it. Thusly, the Helper gets initialized when the class is created, -// and when events are dispatched to the listener, it forwards the event to the main class. -// --air - -template< typename TypeToDispatchTo > -class EventListenerHelper_CoreThread : public EventListener_CoreThread -{ -public: - TypeToDispatchTo& Owner; - -public: - EventListenerHelper_CoreThread( TypeToDispatchTo& dispatchTo ) - : Owner( dispatchTo ) { } - - EventListenerHelper_CoreThread( TypeToDispatchTo* dispatchTo ) - : Owner( *dispatchTo ) - { - pxAssert(dispatchTo != NULL); - } - - virtual ~EventListenerHelper_CoreThread() = default; - -protected: - void CoreThread_OnIndeterminate() { Owner.OnCoreThread_Indeterminate(); } - void CoreThread_OnStarted() { Owner.OnCoreThread_Started(); } - void CoreThread_OnResumed() { Owner.OnCoreThread_Resumed(); } - void CoreThread_OnSuspended() { Owner.OnCoreThread_Suspended(); } - void CoreThread_OnReset() { Owner.OnCoreThread_Reset(); } - void CoreThread_OnStopped() { Owner.OnCoreThread_Stopped(); } -}; - -template< typename TypeToDispatchTo > -class EventListenerHelper_AppStatus : public EventListener_AppStatus -{ -public: - TypeToDispatchTo& Owner; - -public: - EventListenerHelper_AppStatus( TypeToDispatchTo& dispatchTo ) - : Owner( dispatchTo ) { } - - EventListenerHelper_AppStatus( TypeToDispatchTo* dispatchTo ) - : Owner( *dispatchTo ) - { - pxAssert(dispatchTo != NULL); - } - - virtual ~EventListenerHelper_AppStatus() = default; - -protected: - virtual void AppStatusEvent_OnUiSettingsLoadSave( const AppSettingsEventInfo& evtinfo ) { Owner.AppStatusEvent_OnUiSettingsLoadSave( evtinfo ); } - virtual void AppStatusEvent_OnVmSettingsLoadSave( const AppSettingsEventInfo& evtinfo ) { Owner.AppStatusEvent_OnVmSettingsLoadSave( evtinfo ); } - virtual void AppStatusEvent_OnSettingsApplied() { Owner.AppStatusEvent_OnSettingsApplied(); } - virtual void AppStatusEvent_OnExit() { Owner.AppStatusEvent_OnExit(); } -}; - - -// -------------------------------------------------------------------------------------- -// CoreThreadStatusEvent -// -------------------------------------------------------------------------------------- -class CoreThreadStatusEvent : public pxActionEvent -{ - typedef pxActionEvent _parent; - -protected: - CoreThreadStatus m_evt; - -public: - virtual ~CoreThreadStatusEvent() = default; - CoreThreadStatusEvent* Clone() const { return new CoreThreadStatusEvent( *this ); } - - explicit CoreThreadStatusEvent( CoreThreadStatus evt, SynchronousActionState* sema=NULL ); - explicit CoreThreadStatusEvent( CoreThreadStatus evt, SynchronousActionState& sema ); - - void SetEventType( CoreThreadStatus evt ) { m_evt = evt; } - CoreThreadStatus GetEventType() { return m_evt; } - -protected: - void InvokeEvent(); -}; diff --git a/pcsx2/gui/AppEventSources.cpp b/pcsx2/gui/AppEventSources.cpp deleted file mode 100644 index c1195e7084..0000000000 --- a/pcsx2/gui/AppEventSources.cpp +++ /dev/null @@ -1,141 +0,0 @@ -/* PCSX2 - PS2 Emulator for PCs - * Copyright (C) 2002-2010 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 . - */ - -#include "PrecompiledHeader.h" -#include "App.h" -#include "IniInterface.h" -#include "common/EventSource.inl" - -template class EventSource< IEventListener_CoreThread >; -template class EventSource< IEventListener_AppStatus >; - -AppSettingsEventInfo::AppSettingsEventInfo( IniInterface& ini, AppEventType evt_type ) - : AppEventInfo( evt_type ) - , m_ini( ini ) -{ -} - -EventListener_CoreThread::EventListener_CoreThread() -{ - wxGetApp().AddListener( this ); -} - -EventListener_CoreThread::~EventListener_CoreThread() -{ - wxGetApp().RemoveListener( this ); -} - -void IEventListener_CoreThread::DispatchEvent( const CoreThreadStatus& status ) -{ - switch( status ) - { - case CoreThread_Indeterminate: break; - - case CoreThread_Started: CoreThread_OnStarted(); break; - case CoreThread_Resumed: CoreThread_OnResumed(); break; - case CoreThread_Suspended: CoreThread_OnSuspended(); break; - case CoreThread_Reset: CoreThread_OnReset(); break; - case CoreThread_Stopped: CoreThread_OnStopped(); break; - - jNO_DEFAULT; - } -} - -EventListener_AppStatus::EventListener_AppStatus() -{ - wxGetApp().AddListener( this ); -} - -EventListener_AppStatus::~EventListener_AppStatus() -{ - wxGetApp().RemoveListener( this ); -} - -void IEventListener_AppStatus::DispatchEvent( const AppEventInfo& evtinfo ) -{ - switch( evtinfo.evt_type ) - { - case AppStatus_UiSettingsLoaded: - case AppStatus_UiSettingsSaved: - AppStatusEvent_OnUiSettingsLoadSave( (const AppSettingsEventInfo&)evtinfo ); - break; - - case AppStatus_VmSettingsLoaded: - case AppStatus_VmSettingsSaved: - AppStatusEvent_OnVmSettingsLoadSave( (const AppSettingsEventInfo&)evtinfo ); - break; - - case AppStatus_SettingsApplied: - AppStatusEvent_OnSettingsApplied(); - break; - - case AppStatus_Exiting: - AppStatusEvent_OnExit(); - break; - } -} - -void Pcsx2App::DispatchEvent( AppEventType evt ) -{ - if( !AffinityAssert_AllowFrom_MainUI() ) return; - m_evtsrc_AppStatus.Dispatch( AppEventInfo( evt ) ); -} - -void Pcsx2App::DispatchEvent( CoreThreadStatus evt ) -{ - // Clear the sticky key statuses, because hell knows what'll change while PAD - // is suspended. - - m_kevt.m_shiftDown = false; - m_kevt.m_controlDown = false; - m_kevt.m_altDown = false; - - m_evtsrc_CoreThreadStatus.Dispatch( evt ); - ScopedBusyCursor::SetDefault( Cursor_NotBusy ); - CoreThread.RethrowException(); -} - -void Pcsx2App::DispatchUiSettingsEvent( IniInterface& ini ) -{ - if( !AffinityAssert_AllowFrom_MainUI() ) return; - m_evtsrc_AppStatus.Dispatch( AppSettingsEventInfo( ini, ini.IsSaving() ? AppStatus_UiSettingsSaved : AppStatus_UiSettingsLoaded ) ); -} - -void Pcsx2App::DispatchVmSettingsEvent( IniInterface& ini ) -{ - if( !AffinityAssert_AllowFrom_MainUI() ) return; - m_evtsrc_AppStatus.Dispatch( AppSettingsEventInfo( ini, ini.IsSaving() ? AppStatus_VmSettingsSaved : AppStatus_VmSettingsLoaded ) ); -} - - -// -------------------------------------------------------------------------------------- -// CoreThreadStatusEvent Implementations -// -------------------------------------------------------------------------------------- -CoreThreadStatusEvent::CoreThreadStatusEvent( CoreThreadStatus evt, SynchronousActionState* sema ) - : pxActionEvent( sema ) -{ - m_evt = evt; -} - -CoreThreadStatusEvent::CoreThreadStatusEvent( CoreThreadStatus evt, SynchronousActionState& sema ) - : pxActionEvent( sema ) -{ - m_evt = evt; -} - -void CoreThreadStatusEvent::InvokeEvent() -{ - sApp.DispatchEvent( m_evt ); -} diff --git a/pcsx2/gui/AppForwardDefs.h b/pcsx2/gui/AppForwardDefs.h deleted file mode 100644 index 4b93e06a5a..0000000000 --- a/pcsx2/gui/AppForwardDefs.h +++ /dev/null @@ -1,61 +0,0 @@ -/* PCSX2 - PS2 Emulator for PCs - * Copyright (C) 2002-2010 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 . - */ - -#pragma once - -// AppForwardDefs.h -// -// Purpose: -// This header file is meant to be a dependency-free include that provides a relatively -// full compliment of forward defines for PCSX2/App and wxwidgets types. When -// forward defined in this way, these types can be used by method and class definitions -// as either pointers or handles without running into complicated header file -// inter-dependence. -// - -class Pcsx2App; -class MainEmuFrame; -class GSFrame; -class ConsoleLogFrame; -class PipeRedirectionBase; -class AppCoreThread; -class Pcsx2AppMethodEvent; -class pxAppResources; -class IScopedCoreThread; - -struct KeyAcceleratorCode; -struct GlobalCommandDescriptor; -class CommandDictionary; -class AcceleratorDictionary; - -class IniInterface; - -// wxWidgets forward declarations - -class wxConfigBase; -class wxFileConfig; -class wxDirPickerCtrl; -class wxFilePickerCtrl; -class wxFileDirPickerEvent; -class wxListBox; -class wxListCtrl; -class wxListView; -class wxListbook; -class wxSpinCtrl; -class wxBookCtrlBase; - -class wxListEvent; -class wxImageList; -class wxBitmap; diff --git a/pcsx2/gui/AppHost.cpp b/pcsx2/gui/AppHost.cpp deleted file mode 100644 index f82f0f4522..0000000000 --- a/pcsx2/gui/AppHost.cpp +++ /dev/null @@ -1,226 +0,0 @@ -/* PCSX2 - PS2 Emulator for PCs - * Copyright (C) 2002-2021 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 . - */ - -#include "PrecompiledHeader.h" - -#include "common/Console.h" -#include "common/FileSystem.h" -#include "common/Path.h" -#include "common/StringUtil.h" - -#include "Host.h" -#include "HostSettings.h" -#include "HostDisplay.h" -#include "GS/GS.h" - -#include "common/Assertions.h" -#include "Frontend/ImGuiManager.h" - -#include "gui/App.h" -#include "gui/AppHost.h" -#include "gui/pxEvents.h" - -#include -#include - -#include "gui/App.h" -#include "gui/AppConfig.h" -#include "gui/pxEvents.h" - -static auto OpenResourceCFile(const char* filename, const char* mode) -{ - const std::string full_filename(Path::Combine(EmuFolders::Resources, filename)); - auto fp = FileSystem::OpenManagedCFile(full_filename.c_str(), mode); - if (!fp) - Console.Error("Failed to open resource file '%s'", filename); - - return fp; -} - -std::optional> Host::ReadResourceFile(const char* filename) -{ - auto fp = OpenResourceCFile(filename, "rb"); - if (!fp) - return std::nullopt; - - const size_t size = FileSystem::FSize64(fp.get()); - std::vector ret(size); - if (std::fread(ret.data(), size, 1, fp.get()) != 1) - { - Console.Error("Failed to read resource file '%s'", filename); - return std::nullopt; - } - - return ret; -} - -std::optional Host::ReadResourceFileToString(const char* filename) -{ - auto fp = OpenResourceCFile(filename, "rb"); - if (!fp) - return std::nullopt; - - const size_t size = FileSystem::FSize64(fp.get()); - std::string ret; - ret.resize(size); - if (std::fread(ret.data(), size, 1, fp.get()) != 1) - { - Console.Error("Failed to read resource file '%s' to string", filename); - return std::nullopt; - } - - return ret; -} - -std::optional Host::GetResourceFileTimestamp(const char* filename) -{ - const std::string path(Path::Combine(EmuFolders::Resources, filename)); - FILESYSTEM_STAT_DATA sd; - if (!FileSystem::StatFile(filename, &sd)) - return std::nullopt; - - return sd.ModificationTime; -} - -bool Host::GetBoolSettingValue(const char* section, const char* key, bool default_value /* = false */) -{ - return default_value; -} - -std::string Host::GetStringSettingValue(const char* section, const char* key, const char* default_value /* = "" */) -{ - return default_value; -} - -void Host::ReportErrorAsync(const std::string_view& title, const std::string_view& message) -{ - wxGetApp().PostEvent(pxMessageBoxEvent( - title.empty() ? wxString() : wxString::FromUTF8(title.data(), title.length()), - message.empty() ? wxString() : wxString::FromUTF8(message.data(), message.length()), - MsgButtons().OK())); -} - -bool Host::ConfirmMessage(const std::string_view& title, const std::string_view& message) -{ - return true; -} - -bool Host::AcquireHostDisplay(RenderAPI api, bool clear_state_on_fail) -{ - sApp.OpenGsPanel(); - - // can't go anywhere if we don't have a window to render into! - if (g_gs_window_info.type == WindowInfo::Type::Surfaceless) - return false; - - g_host_display = HostDisplay::CreateForAPI(api); - if (!g_host_display) - return false; - - if (!g_host_display->CreateDevice(g_gs_window_info, Host::GetEffectiveVSyncMode()) || - !g_host_display->SetupDevice() || !ImGuiManager::Initialize()) - { - g_host_display.reset(); - return false; - } - - Console.WriteLn(Color_StrongGreen, "%s Graphics Driver Info:", HostDisplay::RenderAPIToString(g_host_display->GetRenderAPI())); - Console.Indent().WriteLn(g_host_display->GetDriverInfo()); - - return true; -} - -void Host::ReleaseHostDisplay(bool clear_state_on_fail) -{ - ImGuiManager::Shutdown(clear_state_on_fail); - - if (g_host_display) - g_host_display.reset(); - - sApp.CloseGsPanel(); -} - -bool Host::BeginPresentFrame(bool frame_skip) -{ - CheckForGSWindowResize(); - - if (!g_host_display->BeginPresent(frame_skip)) - { - // if we're skipping a frame, we need to reset imgui's state, since - // we won't be calling EndPresentFrame(). - ImGuiManager::NewFrame(); - return false; - } - - return true; -} - -void Host::EndPresentFrame() -{ - ImGuiManager::RenderOSD(); - g_host_display->EndPresent(); - ImGuiManager::NewFrame(); -} - -void Host::UpdateHostDisplay() -{ - // not used for wx -} - -void Host::ResizeHostDisplay(u32 new_window_width, u32 new_window_height, float new_window_scale) -{ - // not used for wx (except for osd scale changes) - ImGuiManager::WindowResized(); -} - -static std::atomic_bool s_gs_window_resized{false}; -static std::mutex s_gs_window_resized_lock; -static int s_new_gs_window_width = 0; -static int s_new_gs_window_height = 0; -static float s_new_gs_window_scale = 1; - -void Host::GSWindowResized(int width, int height, float scale) -{ - std::unique_lock lock(s_gs_window_resized_lock); - s_new_gs_window_width = width; - s_new_gs_window_height = height; - s_new_gs_window_scale = scale; - s_gs_window_resized.store(true); -} - -void Host::CheckForGSWindowResize() -{ - if (!s_gs_window_resized.load()) - return; - - int width, height; - float scale; - { - std::unique_lock lock(s_gs_window_resized_lock); - width = s_new_gs_window_width; - height = s_new_gs_window_height; - scale = s_new_gs_window_scale; - s_gs_window_resized.store(false); - } - - if (!g_host_display) - return; - - GSResetAPIState(); - g_host_display->ResizeWindow(width, height, scale); - GSRestoreAPIState(); - ImGuiManager::WindowResized(); -} - diff --git a/pcsx2/gui/AppHost.h b/pcsx2/gui/AppHost.h deleted file mode 100644 index 91bf5a6f9a..0000000000 --- a/pcsx2/gui/AppHost.h +++ /dev/null @@ -1,12 +0,0 @@ -#pragma once -#include "Host.h" - -namespace Host -{ - // UI thread - void GSWindowResized(int width, int height, float scale); - - // MTGS thread - void CheckForGSWindowResize(); -} // namespace Host - diff --git a/pcsx2/gui/AppInit.cpp b/pcsx2/gui/AppInit.cpp deleted file mode 100644 index acd1ee395f..0000000000 --- a/pcsx2/gui/AppInit.cpp +++ /dev/null @@ -1,748 +0,0 @@ -/* PCSX2 - PS2 Emulator for PCs - * Copyright (C) 2002-2021 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 . - */ - -#include "PrecompiledHeader.h" -#include "MainFrame.h" -#include "AppAccelerators.h" -#include "ConsoleLogger.h" -#include "MSWstuff.h" -#include "MTVU.h" // for thread cancellation on shutdown -#include "IniInterface.h" -#include "StringHelpers.h" - -#include "common/FileSystem.h" -#include "common/StringUtil.h" -#include "DebugTools/Debug.h" -#include "Dialogs/ModalPopups.h" - -#include "Debugger/DisassemblyDialog.h" - -#include "Recording/InputRecording.h" - -#include -#include -#include -#include - -#ifdef __WXGTK__ -#include -#ifdef GDK_WINDOWING_X11 -#include -#endif -#endif // __WXGTK__ - -#ifdef SDL_BUILD -#include -#endif - -using namespace pxSizerFlags; - -static void HookSignals() -{ -#ifdef __linux__ - // Ignore SIGCHLD by default on Linux, since we kick off xdg-screensaver asynchronously. - struct sigaction sa_chld = {}; - sigemptyset(&sa_chld.sa_mask); - sa_chld.sa_flags = SA_SIGINFO | SA_RESTART | SA_NOCLDSTOP | SA_NOCLDWAIT; - sigaction(SIGCHLD, &sa_chld, nullptr); -#endif -} - -void Pcsx2App::DetectCpuAndUserMode() -{ - AffinityAssert_AllowFrom_MainUI(); - -#ifdef _M_X86 - x86caps.Identify(); - x86caps.CountCores(); - x86caps.SIMD_EstablishMXCSRmask(); - - if (!x86caps.hasStreamingSIMD4Extensions) - { - // This code will probably never run if the binary was correctly compiled for SSE4 - // SSE4 is required for any decent speed and is supported by more than decade old x86 CPUs - throw Exception::RuntimeError() - .SetDiagMsg("Critical Failure: SSE4.1 Extensions not available.") - .SetUserMsg("SSE4 extensions are not available. PCSX2 requires a cpu that supports the SSE4.1 instruction set."); - } -#endif - - EstablishAppUserMode(); - - // Check that the resources directory exists and contains our data files. - if (!FileSystem::DirectoryExists(EmuFolders::Resources.c_str())) - { - throw Exception::RuntimeError() - .SetDiagMsg("Resources directory does not exist.") - .SetUserMsg("Resources directory does not exist. Your installation is incomplete."); - } -} - -void Pcsx2App::OpenMainFrame() -{ - if (AppRpc_TryInvokeAsync(&Pcsx2App::OpenMainFrame)) - return; - - if (GetMainFramePtr() != NULL) - return; - - MainEmuFrame* mainFrame = new MainEmuFrame(NULL, pxGetAppName()); - m_id_MainFrame = mainFrame->GetId(); - - DisassemblyDialog* disassembly = new DisassemblyDialog(mainFrame); - m_id_Disassembler = disassembly->GetId(); - - if (g_Conf->EmuOptions.Debugger.ShowDebuggerOnStart) - disassembly->Show(); - - PostIdleAppMethod(&Pcsx2App::OpenProgramLog); - - SetTopWindow(mainFrame); // not really needed... - SetExitOnFrameDelete(false); // but being explicit doesn't hurt... - mainFrame->Show(); -} - -void Pcsx2App::OpenProgramLog() -{ - if (AppRpc_TryInvokeAsync(&Pcsx2App::OpenProgramLog)) - return; - - if (/*ConsoleLogFrame* frame =*/GetProgramLog()) - { - //pxAssume( ); - return; - } - - wxWindow* m_current_focus = wxGetActiveWindow(); - - ScopedLock lock(m_mtx_ProgramLog); - m_ptr_ProgramLog = new ConsoleLogFrame(GetMainFramePtr(), L"PCSX2 Program Log", g_Conf->ProgLogBox); - m_id_ProgramLogBox = m_ptr_ProgramLog->GetId(); - EnableAllLogging(); - - if (m_current_focus) - m_current_focus->SetFocus(); - - // This is test code for printing out all supported languages and their canonical names in wiki-fied - // format. I might use it again soon, so I'm leaving it in for now... --air - /* - for( int li=wxLANGUAGE_UNKNOWN+1; liLanguage)) continue; - Console.WriteLn( L"|| %-30s || %-8s ||", info->Description.c_str(), info->CanonicalName.c_str() ); - } - } - */ -} - -void Pcsx2App::AllocateCoreStuffs() -{ - if (AppRpc_TryInvokeAsync(&Pcsx2App::AllocateCoreStuffs)) - return; - - SysLogMachineCaps(); - AppApplySettings(); - - if (!GetVmReserve().Allocate()) - pxFailRel("Failed to allocate memory."); - - if (!m_CpuProviders) - { - // FIXME : Some or all of SysCpuProviderPack should be run from the SysExecutor thread, - // so that the thread is safely blocked from being able to start emulation. - - m_CpuProviders = std::make_unique(); - } -} - - -void Pcsx2App::OnInitCmdLine(wxCmdLineParser& parser) -{ - parser.SetLogo(AddAppName(" >> %s -- A PlayStation 2 Emulator for the PC <<") + L"\n\n" + - _("All options are for the current session only and will not be saved.\n")); - - wxString fixlist(L" "); - for (GamefixId i = GamefixId_FIRST; i < pxEnumEnd; ++i) - { - if (i != GamefixId_FIRST) - fixlist += L","; - fixlist += EnumToString(i); - } - - parser.AddParam(_("IsoFile"), wxCMD_LINE_VAL_STRING, wxCMD_LINE_PARAM_OPTIONAL); - parser.AddSwitch(L"h", L"help", _("displays this list of command line options"), wxCMD_LINE_OPTION_HELP); - parser.AddSwitch(wxEmptyString, L"console", _("forces the program log/console to be visible"), wxCMD_LINE_VAL_STRING); - parser.AddSwitch(wxEmptyString, L"fullscreen", _("use fullscreen GS mode")); - parser.AddSwitch(wxEmptyString, L"windowed", _("use windowed GS mode")); - - parser.AddSwitch(wxEmptyString, L"nogui", _("disables display of the gui while running games")); - parser.AddSwitch(wxEmptyString, L"noguiprompt", _("when nogui - prompt before exiting on suspend")); - - parser.AddOption(wxEmptyString, L"elf", _("executes an ELF image"), wxCMD_LINE_VAL_STRING); - parser.AddOption(wxEmptyString, L"irx", _("executes an IRX image"), wxCMD_LINE_VAL_STRING); - parser.AddSwitch(wxEmptyString, L"nodisc", _("boots an empty DVD tray; use to enter the PS2 system menu")); - parser.AddSwitch(wxEmptyString, L"usecd", _("boots from the disc drive (overrides IsoFile parameter)")); - - parser.AddSwitch(wxEmptyString, L"nohacks", _("disables all speedhacks")); - parser.AddOption(wxEmptyString, L"gamefixes", _("use the specified comma or pipe-delimited list of gamefixes.") + fixlist, wxCMD_LINE_VAL_STRING); - parser.AddSwitch(wxEmptyString, L"fullboot", _("disables fast booting")); - parser.AddOption(wxEmptyString, L"gameargs", _("passes the specified space-delimited string of launch arguments to the game"), wxCMD_LINE_VAL_STRING); - - parser.AddOption(wxEmptyString, L"cfgpath", _("changes the configuration file path"), wxCMD_LINE_VAL_STRING); - parser.AddOption(wxEmptyString, L"cfg", _("specifies the PCSX2 configuration file to use"), wxCMD_LINE_VAL_STRING); - parser.AddSwitch(wxEmptyString, L"forcewiz", AddAppName(_("forces %s to start the First-time Wizard"))); - parser.AddSwitch(wxEmptyString, L"portable", _("enables portable mode operation (requires admin/root access)")); - - parser.AddSwitch(wxEmptyString, L"profiling", _("update options to ease profiling (debug)")); - - parser.SetSwitchChars(L"-"); -} - -bool Pcsx2App::OnCmdLineError(wxCmdLineParser& parser) -{ - wxApp::OnCmdLineError(parser); - return false; -} - -static void SetGameFixOptionsFromString(Pcsx2Config::GamefixOptions& dest, const wxString& list, bool enabled) -{ - wxStringTokenizer izer(list, L",|", wxTOKEN_STRTOK); - - while (izer.HasMoreTokens()) - { - wxString token(izer.GetNextToken()); - - GamefixId i; - for (i = GamefixId_FIRST; i < pxEnumEnd; ++i) - { - if (token.CmpNoCase(EnumToString(i)) == 0) - break; - } - if (i < pxEnumEnd) - dest.Set(i); - } -} - -bool Pcsx2App::ParseOverrides(wxCmdLineParser& parser) -{ - wxString dest; - bool parsed = true; - - if (parser.Found(L"cfgpath", &dest) && !dest.IsEmpty()) - { - Console.Warning("Config path override: %ls", WX_STR(dest)); - Overrides.SettingsFolder = dest; - } - - if (parser.Found(L"cfg", &dest) && !dest.IsEmpty()) - { - Console.Warning("Config file override: %ls", WX_STR(dest)); - Overrides.VmSettingsFile = dest; - } - - Overrides.DisableSpeedhacks = parser.Found(L"nohacks"); - - Overrides.ProfilingMode = parser.Found(L"profiling"); - - if (parser.Found(L"gamefixes", &dest)) - { - Overrides.ApplyCustomGamefixes = true; - SetGameFixOptionsFromString(Overrides.Gamefixes, dest, true); - } - - if (parser.Found(L"fullscreen")) - Overrides.GsWindowMode = GsWinMode_Fullscreen; - if (parser.Found(L"windowed")) - Overrides.GsWindowMode = GsWinMode_Windowed; - - return parsed; -} - -bool Pcsx2App::OnCmdLineParsed(wxCmdLineParser& parser) -{ - if (parser.Found(L"console")) - { - Startup.ForceConsole = true; - OpenProgramLog(); - } - - // Suppress wxWidgets automatic options parsing since none of them pertain to PCSX2 needs. - //wxApp::OnCmdLineParsed( parser ); - - m_UseGUI = !parser.Found(L"nogui"); - m_NoGuiExitPrompt = parser.Found(L"noguiprompt"); // by default no prompt for exit with nogui. - - if (!ParseOverrides(parser)) - return false; - - // --- Parse Startup/Autoboot options --- - - Startup.NoFastBoot = parser.Found(L"fullboot"); - Startup.ForceWizard = parser.Found(L"forcewiz"); - Startup.PortableMode = parser.Found(L"portable"); - - if (parser.GetParamCount() >= 1) - { - Startup.IsoFile = parser.GetParam(0); - Startup.CdvdSource = CDVD_SourceType::Iso; - Startup.SysAutoRun = true; - } - else - { - wxString elf_file; - if (parser.Found(L"elf", &elf_file) && !elf_file.IsEmpty()) - Startup.SysAutoRunElf = true; - else if (parser.Found(L"irx", &elf_file) && !elf_file.IsEmpty()) - Startup.SysAutoRunIrx = true; - - if (!elf_file.IsEmpty()) - { - auto path = wxFileName(elf_file); - path.Normalize(); - Startup.ElfFile = path.GetFullPath(); - } - } - - wxString game_args; - if (parser.Found(L"gameargs", &game_args) && !game_args.IsEmpty()) - Startup.GameLaunchArgs = game_args; - - if (parser.Found(L"usecd")) - { - Startup.CdvdSource = CDVD_SourceType::Disc; - Startup.SysAutoRun = true; - } - - if (parser.Found(L"nodisc")) - { - Startup.CdvdSource = CDVD_SourceType::NoDisc; - Startup.SysAutoRun = true; - } - - return true; -} - -typedef void (wxEvtHandler::*pxInvokeAppMethodEventFunction)(Pcsx2AppMethodEvent&); -typedef void (wxEvtHandler::*pxStuckThreadEventHandler)(pxMessageBoxEvent&); - -bool Pcsx2App::OnInit() -{ - HookSignals(); - EnableAllLogging(); - Console.WriteLn("Interface is initializing. Entering Pcsx2App::OnInit!"); - - InitCPUTicks(); - - g_Conf = std::make_unique(); - wxInitAllImageHandlers(); - - Console.WriteLn("Applying operating system default language..."); - { - // The PCSX2 log system hasn't been set up yet, so error messages might - // pop up that could cause some alarm amongst users. Let's avoid that. - wxDoNotLogInThisScope please; - i18n_SetLanguage(wxLANGUAGE_DEFAULT); - } - - Console.WriteLn("Command line parsing..."); - if (!_parent::OnInit()) - return false; - Console.WriteLn("Command line parsed!"); - - i18n_SetLanguagePath(); - - Bind(wxEVT_KEY_DOWN, &Pcsx2App::OnEmuKeyDown, this, pxID_PadHandler_Keydown); - Bind(wxEVT_DESTROY, &Pcsx2App::OnDestroyWindow, this); - - // User/Admin Mode Dual Setup: - // PCSX2 now supports two fundamental modes of operation. The default is Classic mode, - // which uses the Current Working Directory (CWD) for all user data files, and requires - // Admin access on Vista (and some Linux as well). The second mode is the Vista- - // compatible \documents folder usage. The mode is determined by the presence and - // contents of a usermode.ini file in the CWD. If the ini file is missing, we assume - // the user is setting up a classic install. If the ini is present, we read the value of - // the UserMode and SettingsPath vars. - // - // Conveniently this dual mode setup applies equally well to most modern Linux distros. - - try - { - InitDefaultGlobalAccelerators(); - delete wxLog::SetActiveTarget(new pxLogConsole()); - - SysExecutorThread.Start(); - DetectCpuAndUserMode(); - - // Set Manual Exit Handling - // ---------------------------- - // PCSX2 has a lot of event handling logistics, so we *cannot* depend on wxWidgets automatic event - // loop termination code. We have a much safer system in place that continues to process messages - // until all "important" threads are closed out -- not just until the main frame is closed(-ish). - m_timer_Termination = std::make_unique(this, wxID_ANY); - Bind(wxEVT_TIMER, &Pcsx2App::OnScheduledTermination, this, m_timer_Termination->GetId()); - SetExitOnFrameDelete(false); - - - // Start GUI and/or Direct Emulation - // ------------------------------------- - pxSizerFlags::SetBestPadding(); - if (Startup.ForceConsole) - g_Conf->ProgLogBox.Visible = true; - OpenProgramLog(); - AllocateCoreStuffs(); - if (m_UseGUI) - OpenMainFrame(); - - - // By default no IRX injection - EmuConfig.CurrentIRX.clear(); - - if (Startup.SysAutoRun) - { - g_Conf->EmuOptions.UseBOOT2Injection = !Startup.NoFastBoot; - g_Conf->CdvdSource = Startup.CdvdSource; - if (Startup.CdvdSource == CDVD_SourceType::Iso) - SysUpdateIsoSrcFile(Startup.IsoFile); - sApp.SysExecute(Startup.CdvdSource); - EmuConfig.CurrentGameArgs = StringUtil::wxStringToUTF8String(Startup.GameLaunchArgs); - } - else if (Startup.SysAutoRunElf) - { - g_Conf->EmuOptions.UseBOOT2Injection = true; - - // wxPATH_NATIVE is broken on msw, it can delete the first directory after the volume - // EX: P://dir1/dir2/elf.elf -> P://dir2/ ??? -#ifdef _WIN32 - wxFileName elfFile = wxFileName(Startup.ElfFile, wxPATH_WIN); -#else - wxFileName elfFile = wxFileName(Startup.ElfFile, wxPATH_NATIVE); -#endif - - if (!elfFile.FileExists()) - { - wxMessageBox(wxString::Format(_("Specified elf file %s does not exist!"), Startup.ElfFile), "PCSX2", wxICON_ERROR); - } - else - { - g_Conf->Folders.RunELF = elfFile.GetPath(); - sApp.SysExecute(Startup.CdvdSource, Startup.ElfFile); - if (Startup.ElfFile.Find(' ') == -1) - EmuConfig.CurrentGameArgs = StringUtil::wxStringToUTF8String(Startup.GameLaunchArgs); - } - } - else if (Startup.SysAutoRunIrx) - { - g_Conf->EmuOptions.UseBOOT2Injection = true; - - EmuConfig.CurrentIRX = StringUtil::wxStringToUTF8String(Startup.ElfFile); - - // FIXME: ElfFile is an irx it will crash - sApp.SysExecute(Startup.CdvdSource, Startup.ElfFile); - if (Startup.ElfFile.Find(' ') == -1) - EmuConfig.CurrentGameArgs = StringUtil::wxStringToUTF8String(Startup.GameLaunchArgs); - } - } - // ---------------------------------------------------------------------------- - catch (Exception::StartupAborted& ex) // user-aborted, no popups needed. - { - Console.Warning(ex.FormatDiagnosticMessage()); - CleanupOnExit(); - return false; - } - // ---------------------------------------------------------------------------- - // Failures on the core initialization procedure (typically OutOfMemory errors) are bad, - // since it means the emulator is completely non-functional. Let's pop up an error and - // exit gracefully-ish. - // - catch (Exception::RuntimeError& ex) - { - Console.Error(ex.FormatDiagnosticMessage()); - Msgbox::Alert(StringUtil::UTF8StringToWxString(ex.FormatDisplayMessage()) + L"\n\n" + AddAppName(_("Press OK to close %s.")), - AddAppName(_("%s Critical Error")), wxICON_ERROR); - CleanupOnExit(); - return false; - } - -#ifdef SDL_BUILD - // MacOS Game Controller framework requires a few runs of the main event loop after interest in game controllers is first indicated to connect controllers - // Since OnePad doesn't currently handle connection/disconnection events and requires controllers to be connected on start, we need to initialize SDL before OnePad looks at the controller list - SDL_SetHint(SDL_HINT_NO_SIGNAL_HANDLERS, "1"); - SDL_Init(SDL_INIT_GAMECONTROLLER); -#endif - return true; -} - -static int m_term_threshold = 20; - -void Pcsx2App::OnScheduledTermination(wxTimerEvent& evt) -{ - if (!pxAssertDev(m_ScheduledTermination, "Scheduled Termination check is inconsistent with ScheduledTermination status.")) - { - m_timer_Termination->Stop(); - return; - } - - if (m_PendingSaves != 0) - { - if (--m_term_threshold > 0) - { - Console.WriteLn("(App) %d saves are still pending; exit postponed...", m_PendingSaves); - return; - } - - Console.Error("(App) %s pending saves have exceeded OnExit threshold and are being prematurely terminated!", m_PendingSaves); - } - - m_timer_Termination->Stop(); - Exit(); -} - - -// Common exit handler which can be called from any event (though really it should -// be called only from CloseWindow handlers since that's the more appropriate way -// to handle cancelable window closures) -// -// returns true if the app can close, or false if the close event was canceled by -// the glorious user, whomever (s)he-it might be. -void Pcsx2App::PrepForExit() -{ - if (m_ScheduledTermination) - return; - m_ScheduledTermination = true; - - DispatchEvent(AppStatus_Exiting); - - CoreThread.Cancel(); - SysExecutorThread.ShutdownQueue(); - - m_timer_Termination->Start(500); -} - -// This cleanup procedure can only be called when the App message pump is still active. -// OnExit() must use CleanupOnExit instead. -void Pcsx2App::CleanupRestartable() -{ - AffinityAssert_AllowFrom_MainUI(); - - CoreThread.Cancel(); - SysExecutorThread.ShutdownQueue(); - IdleEventDispatcher(L"Cleanup"); - - if (g_Conf) - AppSaveSettings(); -} - -// This cleanup handler can be called from OnExit (it doesn't need a running message pump), -// but should not be called from the App destructor. It's needed because wxWidgets doesn't -// always call OnExit(), so I had to make CleanupRestartable, and then encapsulate it here -// to be friendly to the OnExit scenario (no message pump). -void Pcsx2App::CleanupOnExit() -{ - AffinityAssert_AllowFrom_MainUI(); - - try - { - CleanupRestartable(); - CleanupResources(); - } - catch (Exception::CancelEvent&) - { - throw; - } - catch (Exception::RuntimeError& ex) - { - // Handle runtime errors gracefully during shutdown. Mostly these are things - // that we just don't care about by now, and just want to "get 'er done!" so - // we can exit the app. ;) - - Console.Error("Runtime exception handled during CleanupOnExit:\n"); - Console.Indent().Error(ex.FormatDiagnosticMessage()); - } - - // FIXME: performing a wxYield() here may fix that problem. -- air - - Console_SetActiveHandler(ConsoleWriter_Stdout); -} - -void Pcsx2App::CleanupResources() -{ - ScopedBusyCursor cursor(Cursor_ReallyBusy); - //delete wxConfigBase::Set( NULL ); - - while (wxGetLocale() != NULL) - delete wxGetLocale(); - - m_mtx_LoadingGameDB.Wait(); - ScopedLock lock(m_mtx_Resources); - m_Resources = NULL; -} - -int Pcsx2App::OnExit() -{ - CleanupOnExit(); - return wxApp::OnExit(); -} - -void Pcsx2App::OnDestroyWindow(wxWindowDestroyEvent& evt) -{ - // Precautions: - // * Whenever windows are destroyed, make sure to check if it matches our "active" - // console logger. If so, we need to disable logging to the console window, or else - // it'll crash. (this is because the console log system uses a cached window handle - // instead of looking the window up via it's ID -- fast but potentially unsafe). - - - OnProgramLogClosed(evt.GetId()); - OnGsFrameClosed(evt.GetId()); - evt.Skip(); -} - -// -------------------------------------------------------------------------------------- -// SysEventHandler -// -------------------------------------------------------------------------------------- -class SysEvtHandler : public pxEvtQueue -{ -public: - wxString GetEvtHandlerName() const { return L"SysExecutor"; } - -protected: - // When the SysExec message queue is finally empty, we should check the state of - // the menus and make sure they're all consistent to the current emulation states. - void _DoIdle() - { - UI_UpdateSysControls(); - } -}; - - -Pcsx2App::Pcsx2App() - : SysExecutorThread(new SysEvtHandler()) - , m_id_MainFrame(wxID_ANY) - , m_id_GsFrame(wxID_ANY) - , m_id_ProgramLogBox(wxID_ANY) - , m_id_Disassembler(wxID_ANY) -{ -// Warning: Do not delete this comment block! Gettext will parse it to allow -// the translation of some wxWidget internal strings. -- greg -#if 0 - { - // Some common labels provided by wxWidgets. wxWidgets translation files are chucked full - // of worthless crap, and tally more than 200k each. We only need these couple. - - _("OK"); - _("&OK"); - _("Cancel"); - _("&Cancel"); - _("&Apply"); - _("&Next >"); - _("< &Back"); - _("&Back"); - _("&Finish"); - _("&Yes"); - _("&No"); - _("Browse"); - _("&Save"); - _("Save &As..."); - _("&Help"); - _("&Home"); - _("&Window"); - - _("Show about dialog.") - } -#endif - -#ifdef GDK_WINDOWING_X11 - // This *must* be done in the constructor, before wx starts making X calls. - XInitThreads(); -#endif - - m_PendingSaves = 0; - m_ScheduledTermination = false; - m_UseGUI = true; - m_NoGuiExitPrompt = true; - - m_ptr_ProgramLog = NULL; - - SetAppName(L"PCSX2"); - BuildCommandHash(); -} - -Pcsx2App::~Pcsx2App() -{ -} - -void Pcsx2App::CleanUp() -{ - CleanupResources(); - m_Resources = NULL; - m_RecentIsoList = NULL; - - DisableDiskLogging(); - - if (emuLog != NULL) - { - fclose(emuLog); - emuLog = NULL; - } - - _parent::CleanUp(); -} - -__fi wxString AddAppName(const wxString& fmt) -{ - return pxsFmt(fmt, WX_STR(pxGetAppName())); -} - -__fi wxString AddAppName(const wxChar* fmt) -{ - return pxsFmt(fmt, WX_STR(pxGetAppName())); -} - -__fi wxString AddAppName(const char* fmt) -{ - return pxsFmt(fromUTF8(fmt), WX_STR(pxGetAppName())); -} - -// ------------------------------------------------------------------------------------------ -// Using the MSVCRT to track memory leaks: -// ------------------------------------------------------------------------------------------ -// When exiting PCSX2 normally, the CRT will make a list of all memory that's leaked. The -// number inside {} can be pasted into the line below to cause MSVC to breakpoint on that -// allocation at the time it's made. And then using a stacktrace you can figure out what -// leaked! :D -// -// Limitations: Unfortunately, wxWidgets gui uses a lot of heap allocations while handling -// messages, and so any mouse movements will pretty much screw up the leak value. So to use -// this feature you need to execute pcsx in no-gui mode, and then not move the mouse or use -// the keyboard until you get to the leak. >_< -// -// (but this tool is still better than nothing!) - -#ifdef PCSX2_DEBUG -struct CrtDebugBreak -{ - CrtDebugBreak(int spot) - { -#ifdef __WXMSW__ - _CrtSetBreakAlloc(spot); -#endif - } -}; - -//CrtDebugBreak breakAt( 11549 ); - -#endif diff --git a/pcsx2/gui/AppMain.cpp b/pcsx2/gui/AppMain.cpp deleted file mode 100644 index b849c709d2..0000000000 --- a/pcsx2/gui/AppMain.cpp +++ /dev/null @@ -1,964 +0,0 @@ -/* PCSX2 - PS2 Emulator for PCs - * Copyright (C) 2002-2021 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 . - */ - -#include "PrecompiledHeader.h" -#include "App.h" -#include "MainFrame.h" -#include "GSFrame.h" -#include "GS.h" -#include "Host.h" -#include "AppSaveStates.h" -#include "AppAccelerators.h" -#include "IniInterface.h" -#include "PAD/Gamepad.h" - -#include "ps2/BiosTools.h" - -#include "Dialogs/ModalPopups.h" -#include "Dialogs/ConfigurationDialog.h" -#include "Dialogs/LogOptionsDialog.h" - -#include "Debugger/DisassemblyDialog.h" - -#include "Recording/InputRecordingControls.h" -#include "Recording/InputRecording.h" - -#include "common/FileSystem.h" -#include "common/StringUtil.h" -#include "AppTrait.h" - -#include - -#ifdef __WXMSW__ -# include // needed to implement the app! -#endif - -#ifdef __WXGTK__ -#include -#include - -#ifdef GDK_WINDOWING_WAYLAND -#include -#endif -#endif - -// Safe to remove these lines when this is handled properly. -#ifdef __WXMAC__ -// Great joy.... -#undef EBP -#undef ESP -#undef EDI -#undef ESI -#undef EDX -#undef EAX -#undef EBX -#undef ECX -#include // needed to implement the app! -#endif -wxIMPLEMENT_APP(Pcsx2App); - -std::unique_ptr g_Conf; - -WindowInfo g_gs_window_info; - -static bool CheckForBIOS() -{ - if (IsBIOSAvailable(g_Conf->EmuOptions.FullpathToBios())) - return true; - - wxString error = pxE(L"PCSX2 requires a PS2 BIOS in order to run. For legal reasons, you *must* obtain a BIOS from an actual PS2 unit that you own (borrowing doesn't count). Please consult the FAQs and Guides for further instructions."); - - Msgbox::Alert(error, _("PS2 BIOS Error")); - return false; -} - -// Allows for activating menu actions from anywhere in PCSX2. -// And it's Thread Safe! -void Pcsx2App::PostMenuAction( MenuIdentifiers menu_id ) const -{ - MainEmuFrame* mainFrame = GetMainFramePtr(); - if( mainFrame == NULL ) return; - - wxCommandEvent joe( wxEVT_MENU, menu_id ); - if( wxThread::IsMain() ) - mainFrame->GetEventHandler()->ProcessEvent( joe ); - else - mainFrame->GetEventHandler()->AddPendingEvent( joe ); -} - -#ifdef _WIN32 - -// -------------------------------------------------------------------------------------- -// Exception::WinApiError (implementations) -// -------------------------------------------------------------------------------------- -Exception::WinApiError::WinApiError() -{ - ErrorId = GetLastError(); - m_message_diag = "Unspecified Windows API error."; -} - -std::string Exception::WinApiError::GetMsgFromWindows() const -{ - if (!ErrorId) - return "No valid error number was assigned to this exception!"; - - const DWORD BUF_LEN = 2048; - wchar_t t_Msg[BUF_LEN]; - if (FormatMessageW(FORMAT_MESSAGE_FROM_SYSTEM, 0, ErrorId, 0, t_Msg, BUF_LEN, 0)) - return fmt::format("Win32 Error #{}: {}", ErrorId, StringUtil::WideStringToUTF8String(t_Msg)); - - return fmt::format("Win32 Error #{} (no text msg available)", ErrorId); -} - -std::string Exception::WinApiError::FormatDisplayMessage() const -{ - return m_message_user + "\n\n" + GetMsgFromWindows(); -} - -std::string Exception::WinApiError::FormatDiagnosticMessage() const -{ - return m_message_diag + "\n\t" + GetMsgFromWindows(); -} - -#endif - - -// -------------------------------------------------------------------------------------- -// Pcsx2AppMethodEvent -// -------------------------------------------------------------------------------------- -// Unlike pxPingEvent, the Semaphore belonging to this event is typically posted when the -// invoked method is completed. If the method can be executed in non-blocking fashion then -// it should leave the semaphore postback NULL. -// -class Pcsx2AppMethodEvent : public pxActionEvent -{ - typedef pxActionEvent _parent; - wxDECLARE_DYNAMIC_CLASS_NO_ASSIGN(Pcsx2AppMethodEvent); - -protected: - FnPtr_Pcsx2App m_Method; - -public: - virtual ~Pcsx2AppMethodEvent() = default; - virtual Pcsx2AppMethodEvent *Clone() const { return new Pcsx2AppMethodEvent(*this); } - - explicit Pcsx2AppMethodEvent( FnPtr_Pcsx2App method=NULL, SynchronousActionState* sema=NULL ) - : pxActionEvent( sema ) - { - m_Method = method; - } - - explicit Pcsx2AppMethodEvent( FnPtr_Pcsx2App method, SynchronousActionState& sema ) - : pxActionEvent( sema ) - { - m_Method = method; - } - - Pcsx2AppMethodEvent( const Pcsx2AppMethodEvent& src ) - : pxActionEvent( src ) - { - m_Method = src.m_Method; - } - - void SetMethod( FnPtr_Pcsx2App method ) - { - m_Method = method; - } - -protected: - void InvokeEvent() - { - if( m_Method ) (wxGetApp().*m_Method)(); - } -}; - - -wxIMPLEMENT_DYNAMIC_CLASS( Pcsx2AppMethodEvent, pxActionEvent ); - -#ifdef __WXMSW__ -extern int TranslateVKToWXK(u32 keysym); -#elif defined(__WXGTK__) -extern int TranslateGDKtoWXK(u32 keysym); -#elif defined(__APPLE__) -extern int TranslateOSXtoWXK(u32 keysym); -#endif - -void Pcsx2App::PadKeyDispatch(const HostKeyEvent& ev) -{ - m_kevt.SetEventType( ( ev.type == HostKeyEvent::Type::KeyPressed ) ? wxEVT_KEY_DOWN : wxEVT_KEY_UP ); - -//returns 0 for normal keys and a WXK_* value for special keys -#ifdef __WXMSW__ - const int vkey = TranslateVKToWXK(ev.key); -#elif defined( __WXMAC__ ) - const int vkey = TranslateOSXtoWXK(ev.key); -#elif defined( __WXGTK__ ) - const int vkey = TranslateGDKtoWXK(ev.key); -#else -# error Unsupported Target Platform. -#endif - - // Don't rely on current event handling to get the state of those specials keys. - // Typical linux bug: hit ctrl-alt key to switch the desktop. Key will be released - // outside of the window so the app isn't aware of the current key state. - m_kevt.m_shiftDown = wxGetKeyState(WXK_SHIFT); - m_kevt.m_controlDown = wxGetKeyState(WXK_CONTROL); - m_kevt.m_altDown = wxGetKeyState(WXK_MENU) || wxGetKeyState(WXK_ALT); - - m_kevt.m_keyCode = vkey? vkey : ev.key; - - if (DevConWriterEnabled && m_kevt.GetEventType() == wxEVT_KEY_DOWN) { - wxString strFromCode = wxAcceleratorEntry( - (m_kevt.m_shiftDown ? wxACCEL_SHIFT : 0) | (m_kevt.m_controlDown ? wxACCEL_CTRL : 0) | (m_kevt.m_altDown ? wxACCEL_ALT : 0), - m_kevt.m_keyCode - ).ToString(); - - if (strFromCode.EndsWith(L"\\")) - strFromCode += L"\\"; // If copied into PCSX2_keys.ini, \ needs escaping - - Console.WriteLn(StringUtil::wxStringToUTF8String(wxString::Format("> Key: %s (Code: %ld)", WX_STR(strFromCode), m_kevt.m_keyCode))); - } - - if( m_kevt.GetEventType() == wxEVT_KEY_DOWN ) - { - if( GSFrame* gsFrame = wxGetApp().GetGsFramePtr() ) - { - gsFrame->GetViewport()->DirectKeyCommand( m_kevt ); - } - else - { - m_kevt.SetId( pxID_PadHandler_Keydown ); - wxGetApp().ProcessEvent( m_kevt ); - } - } -} - -// -------------------------------------------------------------------------------------- -// Pcsx2AppTraits (implementations) [includes pxMessageOutputMessageBox] -// -------------------------------------------------------------------------------------- -// This is here to override pxMessageOutputMessageBox behavior, which itself is ONLY used -// by wxWidgets' command line processor. The default edition is totally inadequate for -// displaying a readable --help command line list, so I replace it here with a custom one -// that formats things nicer. -// - -// This is only used in Windows. It's not possible to have wxWidgets show a localised -// command line help message in cmd/powershell/mingw bash. It can be done in English -// locales ( using AttachConsole, WriteConsole, FreeConsole combined with -// wxMessageOutputStderr), but completely fails for some other languages (i.e. Japanese). -#ifdef _WIN32 -class pxMessageOutputMessageBox : public wxMessageOutput -{ -public: - pxMessageOutputMessageBox() { } - - // DoPrintf in wxMessageOutputBase (wxWidgets 3.0) uses this. - virtual void Output(const wxString &out); -}; - -// EXTRAORDINARY HACK! wxWidgets does not provide a clean way of overriding the commandline options -// display dialog. The default one uses operating system built-in message/notice windows, which are -// appaling, ugly, and not at all suited to a large number of command line options. Fortunately, -// wxMessageOutputMessageBox::PrintF is only used in like two places, so we can just check for the -// commandline window using an identifier we know is contained in it, and then format our own window -// display. :D --air - -void pxMessageOutputMessageBox::Output(const wxString& out) -{ - using namespace pxSizerFlags; - - wxString isoFormatted; - isoFormatted.Printf(L"[%s]", _("IsoFile")); - - int pos = out.Find(isoFormatted.c_str()); - - // I've no idea when this is true. - if (pos == wxNOT_FOUND) - { - Msgbox::Alert( out ); return; - } - - pos += isoFormatted.Length(); - - wxDialogWithHelpers popup( NULL, AddAppName(_("%s Commandline Options")) ); - popup.SetMinWidth( 640 ); - popup += popup.Heading(out.Mid(0, pos)); - //popup += ; - //popup += popup.Text(out.Mid(pos, out.Length())).Align( wxALIGN_LEFT ) | pxExpand.Border(wxALL, StdPadding*3); - - wxTextCtrl* traceArea = new wxTextCtrl( - &popup, wxID_ANY, wxEmptyString, wxDefaultPosition, wxDefaultSize, - wxTE_READONLY | wxTE_MULTILINE | wxTE_RICH2 | wxHSCROLL - ); - - traceArea->SetDefaultStyle( wxTextAttr( wxNullColour, wxNullColour, pxGetFixedFont(9) ) ); - traceArea->SetFont( pxGetFixedFont(9) ); - - int fonty = traceArea->GetCharHeight(); - - traceArea->SetMinSize( wxSize( traceArea->GetMinWidth(), (fonty+1)*18 ) ); - traceArea->WriteText( pxTextWrapper(wxString(L' ', 18)).Wrap(traceArea, out.Mid(pos, out.Length()), 600).GetResult() ); - traceArea->SetInsertionPoint( 0 ); - traceArea->ShowPosition( 0 ); - - popup += traceArea | pxExpand.Border(wxALL, StdPadding*3); - - pxIssueConfirmation(popup, MsgButtons().Close() ); -} -#endif - -wxMessageOutput* Pcsx2AppTraits::CreateMessageOutput() -{ -#ifdef __UNIX__ - return _parent::CreateMessageOutput(); -#else - return new pxMessageOutputMessageBox; -#endif -} - -// -------------------------------------------------------------------------------------- -// Pcsx2StandardPaths -// -------------------------------------------------------------------------------------- -#ifdef wxUSE_STDPATHS -#ifndef __APPLE__ // macOS uses wx's defaults -class Pcsx2StandardPaths : public wxStandardPaths -{ -public: - wxString GetResourcesDir() const - { - return Path::CombineWx( GetDataDir(), L"Langs" ); - } - -#ifdef __POSIX__ - wxString GetUserLocalDataDir() const - { - // I got memory corruption inside wxGetEnv when I heavily toggle the GS renderer (F9). It seems wxGetEnv - // isn't thread safe? To avoid any issue on this read only variable, I cache the result. - static wxString cache_dir; - if (!cache_dir.IsEmpty()) return cache_dir; - - // Note: GetUserLocalDataDir() on linux return $HOME/.pcsx2 unfortunately it does not follow the XDG standard - // So we re-implement it, to follow the standard. - wxDirName user_local_dir; - wxDirName default_config_dir = (wxDirName)Path::CombineWx( L".config", pxGetAppName() ); - wxString xdg_home_value; - if( wxGetEnv(L"XDG_CONFIG_HOME", &xdg_home_value) ) { - if ( xdg_home_value.IsEmpty() ) { - // variable exist but it is empty. So use the default value - user_local_dir = (wxDirName)Path::CombineWx( GetUserConfigDir() , default_config_dir); - } else { - user_local_dir = (wxDirName)Path::CombineWx( xdg_home_value, pxGetAppName()); - } - } else { - // variable do not exist - user_local_dir = (wxDirName)Path::CombineWx( GetUserConfigDir() , default_config_dir); - } - - cache_dir = user_local_dir.ToString(); - - return cache_dir; - } -#endif - -}; -#endif // ifdef __APPLE__ - -wxStandardPaths& Pcsx2AppTraits::GetStandardPaths() -{ -#ifdef __APPLE__ - return _parent::GetStandardPaths(); -#else - static Pcsx2StandardPaths stdPaths; - return stdPaths; -#endif -} -#endif - -wxAppTraits* Pcsx2App::CreateTraits() -{ - return new Pcsx2AppTraits; -} - -// ---------------------------------------------------------------------------- -// Pcsx2App Event Handlers -// ---------------------------------------------------------------------------- - -// LogicalVsync - Event received from the AppCoreThread (EEcore) for each vsync, -// roughly 50/60 times a second when frame limiting is enabled, and up to 10,000 -// times a second if not (ok, not quite, but you get the idea... I hope.) -void Pcsx2App::LogicalVsync() -{ - if( AppRpc_TryInvokeAsync( &Pcsx2App::LogicalVsync ) ) return; - - if( !SysHasValidState() ) return; - - if( (wxGetApp().GetGsFramePtr() != NULL) ) - PADupdate(0); - - while( const HostKeyEvent* ev = PADkeyEvent() ) - { - if( ev->key == 0 ) break; - - // in the past, in the plugin api, all plugins would have a first chance at treating the - // input here, with the ui eventually dealing with it otherwise. Obviously this solution - // sucked and we had multiple components battling for input processing. I managed to make - // most of them go away during the plugin merge but GS still needs to process the inputs, - // we might want to move all the input handling in a frontend-specific file in the future -- govanify - GSkeyEvent(*ev); - PadKeyDispatch( *ev ); - } -} - -void Pcsx2App::OnEmuKeyDown( wxKeyEvent& evt ) -{ - const GlobalCommandDescriptor* cmd = NULL; - if (GlobalAccels) - { - std::unordered_map::const_iterator iter(GlobalAccels->find(KeyAcceleratorCode(evt).val32)); - if (iter != GlobalAccels->end()) - cmd = iter->second; - } - - if( cmd == NULL ) - { - evt.Skip(); - return; - } - - DbgCon.WriteLn( "(app) Invoking command: %s", cmd->Id ); - cmd->Invoke(); -} - -void Pcsx2App::HandleEvent(wxEvtHandler* handler, wxEventFunction func, wxEvent& event) const -{ - const_cast(this)->HandleEvent( handler, func, event ); -} - -void Pcsx2App::HandleEvent(wxEvtHandler* handler, wxEventFunction func, wxEvent& event) -{ - try - { - (handler->*func)(event); - } - // ---------------------------------------------------------------------------- - catch( Exception::StartupAborted& ex ) // user-aborted, no popups needed. - { - Console.Warning( ex.FormatDiagnosticMessage() ); - Exit(); - } - // ---------------------------------------------------------------------------- - catch( Exception::SaveStateLoadError& ex) - { - // Saved state load failed prior to the system getting corrupted (ie, file not found - // or some zipfile error) -- so log it and resume emulation. - Console.Warning( ex.FormatDiagnosticMessage() ); - CoreThread.Resume(); - } - // ---------------------------------------------------------------------------- - #if 0 - catch( Exception::ThreadDeadlock& ex ) - { - // [TODO] Bind a listener to the CoreThread status, and automatically close the dialog - // if the thread starts responding while we're waiting (not hard in fact, but I'm getting - // a little tired, so maybe later!) --air - - Console.Warning( ex.FormatDiagnosticMessage() ); - wxDialogWithHelpers dialog( NULL, _("PCSX2 Unresponsive Thread"), wxVERTICAL ); - - dialog += dialog.Heading( ex.FormatDisplayMessage() + L"\n\n" + - pxE( L"'Ignore' to continue waiting for the thread to respond.\n'Cancel' to attempt to cancel the thread.\n'Terminate' to quit PCSX2 immediately.\n" - ) - ); - - int result = pxIssueConfirmation( dialog, MsgButtons().Ignore().Cancel().Custom( _("Terminate") ) ); - - if( result == pxID_CUSTOM ) - { - // fastest way to kill the process! (works in Linux and win32, thanks to windows having very - // limited Posix Signals support) - // - // (note: SIGTERM is a "handled" kill that performs shutdown stuff, which typically just crashes anyway) - wxKill( wxGetProcessId(), wxSIGKILL ); - } - else if( result == wxID_CANCEL ) - { - // Attempt to cancel the thread: - ex.Thread().Cancel(); - } - - // Ignore does nothing... - } - #endif - // ---------------------------------------------------------------------------- - catch( Exception::CancelEvent& ex ) - { - Console.Warning( ex.FormatDiagnosticMessage() ); - } - catch( Exception::RuntimeError& ex ) - { - // Runtime errors which have been unhandled should still be safe to recover from, - // so lets issue a message to the user and then continue the message pump. - - // Test case (Windows only, Linux has an uncaught exception for some - // reason): Run PSX ISO using fast boot - if (GSFrame* gsframe = wxGetApp().GetGsFramePtr()) - gsframe->Close(); - - Console.Error( ex.FormatDiagnosticMessage() ); - // I should probably figure out how to have the error message as well. - if (wxGetApp().HasGUI()) - Msgbox::Alert(StringUtil::UTF8StringToWxString(ex.FormatDisplayMessage())); - } -} - -bool Pcsx2App::HasPendingSaves() const -{ - AffinityAssert_AllowFrom_MainUI(); - return !!m_PendingSaves; -} - -// A call to this method informs the app that there is a pending save operation that must be -// finished prior to exiting the app, or else data loss will occur. Any call to this method -// should be matched by a call to ClearPendingSave(). -void Pcsx2App::StartPendingSave() -{ - if( AppRpc_TryInvokeAsync(&Pcsx2App::StartPendingSave) ) return; - ++m_PendingSaves; -} - -// If this method is called inappropriately then the entire pending save system will become -// unreliable and data loss can occur on app exit. Devel and debug builds will assert if -// such calls are detected (though the detection is far from fool-proof). -void Pcsx2App::ClearPendingSave() -{ - if( AppRpc_TryInvokeAsync(&Pcsx2App::ClearPendingSave) ) return; - - --m_PendingSaves; - pxAssertDev( m_PendingSaves >= 0, "Pending saves count mismatch (pending count is less than 0)" ); - - if( (m_PendingSaves == 0) && m_ScheduledTermination ) - { - Console.WriteLn( "App: All pending saves completed; exiting!" ); - Exit(); - } -} - -// This method generates debug assertions if the MainFrame handle is NULL (typically -// indicating that PCSX2 is running in NoGUI mode, or that the main frame has been -// closed). In most cases you'll want to use HasMainFrame() to test for thread -// validity first, or use GetMainFramePtr() and manually check for NULL (choice -// is a matter of programmer preference). -MainEmuFrame& Pcsx2App::GetMainFrame() const -{ - MainEmuFrame* mainFrame = GetMainFramePtr(); - - pxAssert(mainFrame != NULL); - pxAssert(((uptr)GetTopWindow()) == ((uptr)mainFrame)); - return *mainFrame; -} - -GSFrame& Pcsx2App::GetGsFrame() const -{ - GSFrame* gsFrame = (GSFrame*)wxWindow::FindWindowById( m_id_GsFrame ); - pxAssert(gsFrame != NULL); - return *gsFrame; -} - -void Pcsx2App::enterDebugMode() -{ - DisassemblyDialog* dlg = GetDisassemblyPtr(); - if (dlg) - dlg->setDebugMode(true,false); -} - -void Pcsx2App::leaveDebugMode() -{ - DisassemblyDialog* dlg = GetDisassemblyPtr(); - if (dlg) - dlg->setDebugMode(false,false); -} - -void Pcsx2App::resetDebugger() -{ - DisassemblyDialog* dlg = GetDisassemblyPtr(); - if (dlg) - dlg->reset(); -} - -void AppApplySettings( const AppConfig* oldconf ) -{ - AffinityAssert_AllowFrom_MainUI(); - - ScopedCoreThreadPause paused_core; - - g_Conf->Folders.ApplyDefaults(); - - // Ensure existence of necessary documents folders. - // Other parts of PCSX2 rely on them. - - g_Conf->Folders.MemoryCards.Mkdir(); - g_Conf->Folders.Savestates.Mkdir(); - g_Conf->Folders.Snapshots.Mkdir(); - g_Conf->Folders.Cheats.Mkdir(); - g_Conf->Folders.CheatsWS.Mkdir(); - g_Conf->Folders.Textures.Mkdir(); - - RelocateLogfile(); - - if( (oldconf == NULL) || (oldconf->LanguageCode.CmpNoCase(g_Conf->LanguageCode)) ) - { - wxDoNotLogInThisScope please; - i18n_SetLanguage( g_Conf->LanguageId, g_Conf->LanguageCode ); - } - - // Update the compression attribute on the Memcards folder. - // Memcards generally compress very well via NTFS compression. - - #ifdef _WIN32 - FileSystem::SetPathCompression( g_Conf->Folders.MemoryCards.ToUTF8(), g_Conf->EmuOptions.McdCompressNTFS ); - #endif - sApp.DispatchEvent( AppStatus_SettingsApplied ); - - paused_core.AllowResume(); -} - -// Invokes the specified Pcsx2App method, or posts the method to the main thread if the calling -// thread is not Main. Action is blocking. For non-blocking method execution, use -// AppRpc_TryInvokeAsync. -// -// This function works something like setjmp/longjmp, in that the return value indicates if the -// function actually executed the specified method or not. -// -// Returns: -// FALSE if the method was not posted to the main thread (meaning this IS the main thread!) -// TRUE if the method was posted. -// -bool Pcsx2App::AppRpc_TryInvoke( FnPtr_Pcsx2App method ) -{ - if( wxThread::IsMain() ) return false; - - SynchronousActionState sync; - PostEvent( Pcsx2AppMethodEvent( method, sync ) ); - sync.WaitForResult(); - - return true; -} - -// Invokes the specified Pcsx2App method, or posts the method to the main thread if the calling -// thread is not Main. Action is non-blocking. For blocking method execution, use -// AppRpc_TryInvoke. -// -// This function works something like setjmp/longjmp, in that the return value indicates if the -// function actually executed the specified method or not. -// -// Returns: -// FALSE if the method was not posted to the main thread (meaning this IS the main thread!) -// TRUE if the method was posted. -// -bool Pcsx2App::AppRpc_TryInvokeAsync( FnPtr_Pcsx2App method ) -{ - if( wxThread::IsMain() ) return false; - PostEvent( Pcsx2AppMethodEvent( method ) ); - return true; -} - -// Posts a method to the main thread; non-blocking. Post occurs even when called from the -// main thread. -void Pcsx2App::PostAppMethod( FnPtr_Pcsx2App method ) -{ - PostEvent( Pcsx2AppMethodEvent( method ) ); -} - -// Posts a method to the main thread; non-blocking. Post occurs even when called from the -// main thread. -void Pcsx2App::PostIdleAppMethod( FnPtr_Pcsx2App method ) -{ - Pcsx2AppMethodEvent evt( method ); - AddIdleEvent( evt ); -} - -SysMainMemory& Pcsx2App::GetVmReserve() -{ - if (!m_VmReserve) m_VmReserve = std::unique_ptr(new SysMainMemory()); - return *m_VmReserve; -} - -void Pcsx2App::OpenGsPanel() -{ - if( AppRpc_TryInvoke( &Pcsx2App::OpenGsPanel ) ) return; - - GSFrame* gsFrame = GetGsFramePtr(); - if( gsFrame == NULL ) - { - gsFrame = new GSFrame(GetAppName() ); - m_id_GsFrame = gsFrame->GetId(); - - switch( wxGetApp().Overrides.GsWindowMode ) - { - case GsWinMode_Windowed: - g_Conf->GSWindow.IsFullscreen = false; - break; - - case GsWinMode_Fullscreen: - g_Conf->GSWindow.IsFullscreen = true; - break; - - case GsWinMode_Unspecified: - g_Conf->GSWindow.IsFullscreen = g_Conf->GSWindow.DefaultToFullscreen; - break; - } - } - else - { - // This is an attempt to hackfix a bug in nvidia's 195.xx drivers: When using - // Aero and DX10, the driver fails to update the window after the device has changed, - // until some event like a hide/show or resize event is posted to the window. - // Presumably this forces the driver to re-cache the visibility info. - // Notes: - // Doing an immediate hide/show didn't work. So now I'm trying a resize. Because - // wxWidgets is "clever" (grr!) it optimizes out just force-setting the same size - // over again, so instead I resize it to size-1 and then back to the original size. - // - // FIXME: GS memory leaks in DX10 have been fixed. This code may not be needed - // anymore. - - const wxSize oldsize( gsFrame->GetSize() ); - wxSize newsize( oldsize ); - newsize.DecBy(1); - - gsFrame->SetSize( newsize ); - gsFrame->SetSize( oldsize ); - } - - gsFrame->ShowFullScreen(g_Conf->GSWindow.IsFullscreen); - wxApp::ProcessPendingEvents(); - - std::optional wi = gsFrame->GetViewport()->GetWindowInfo(); - pxAssertDev(wi.has_value(), "GS frame has a valid native window"); - g_gs_window_info = std::move(*wi); -} - - -void Pcsx2App::CloseGsPanel() -{ - if (AppRpc_TryInvoke(&Pcsx2App::CloseGsPanel)) - return; - - GSFrame* gsFrame = GetGsFramePtr(); - if (gsFrame) - { - // we unreference the window first, that way it doesn't try to suspend on close and deadlock - OnGsFrameDestroyed(gsFrame->GetId()); - gsFrame->Destroy(); - } -} - -void Pcsx2App::OnGsFrameClosed(wxWindowID id) -{ - if ((m_id_GsFrame == wxID_ANY) || (m_id_GsFrame != id)) - return; - - CoreThread.Suspend(); - - if (!m_UseGUI) - { - // The user is prompted before suspending (at Sys_Suspend() ), because - // right now there's no way to resume from suspend without GUI. - PrepForExit(); - } -} - -void Pcsx2App::OnGsFrameDestroyed(wxWindowID id) -{ - if ((m_id_GsFrame == wxID_ANY) || (m_id_GsFrame != id)) - return; - - m_id_GsFrame = wxID_ANY; - g_gs_window_info = {}; -} - -void Pcsx2App::OnProgramLogClosed( wxWindowID id ) -{ - if( (m_id_ProgramLogBox == wxID_ANY) || (m_id_ProgramLogBox != id) ) return; - - ScopedLock lock( m_mtx_ProgramLog ); - m_id_ProgramLogBox = wxID_ANY; - DisableWindowLogging(); -} - -void Pcsx2App::OnMainFrameClosed( wxWindowID id ) -{ - // Nothing threaded depends on the mainframe (yet) -- it all passes through the main wxApp - // message handler. But that might change in the future. - if( m_id_MainFrame != id ) return; - m_id_MainFrame = wxID_ANY; -} - -// -------------------------------------------------------------------------------------- -// SysExecEvent_Execute -// -------------------------------------------------------------------------------------- -class SysExecEvent_Execute : public SysExecEvent -{ -protected: - bool m_UseCDVDsrc; - bool m_UseELFOverride; - CDVD_SourceType m_cdvdsrc_type; - wxString m_elf_override; - -public: - virtual ~SysExecEvent_Execute() = default; - SysExecEvent_Execute* Clone() const { return new SysExecEvent_Execute(*this); } - - wxString GetEventName() const - { - return L"SysExecute"; - } - - wxString GetEventMessage() const - { - return _("Executing PS2 Virtual Machine..."); - } - - SysExecEvent_Execute() - : m_UseCDVDsrc(false) - , m_UseELFOverride(false) - , m_cdvdsrc_type(CDVD_SourceType::Iso) - { - } - - SysExecEvent_Execute( CDVD_SourceType srctype, const wxString& elf_override ) - : m_UseCDVDsrc(true) - , m_UseELFOverride(true) - , m_cdvdsrc_type(srctype) - , m_elf_override( elf_override ) - { - } - -protected: - void InvokeEvent() - { - wxGetApp().ProcessMethod( AppSaveSettings ); - - DbgCon.WriteLn( Color_Gray, "(SysExecute) received." ); - - CoreThread.ResetQuick(); - R5900SymbolMap.Clear(); - R3000SymbolMap.Clear(); - CBreakPoints::SetSkipFirst(BREAKPOINT_EE, 0); - CBreakPoints::SetSkipFirst(BREAKPOINT_IOP, 0); - // This function below gets called again from AppCoreThread.cpp and will pass the current ISO regardless if we - // are starting an ELF. In terms of symbol loading this doesn't matter because AppCoreThread.cpp doesn't clear the symbol map - // and we _only_ read symbols if the map is empty - CDVDsys_SetFile(CDVD_SourceType::Disc, StringUtil::wxStringToUTF8String(g_Conf->Folders.RunDisc) ); - CDVDsys_SetFile(CDVD_SourceType::Iso, StringUtil::wxStringToUTF8String(m_UseELFOverride ? m_elf_override : g_Conf->CurrentIso) ); - if( m_UseCDVDsrc ) - CDVDsys_ChangeSource( m_cdvdsrc_type ); - else if( CDVD == NULL ) - CDVDsys_ChangeSource(CDVD_SourceType::NoDisc); - - if( m_UseELFOverride && !CoreThread.HasActiveMachine() ) - CoreThread.SetElfOverride( m_elf_override ); - - CoreThread.Resume(); - } -}; - -// This command performs a full closure of any existing VM state and starts a -// fresh VM with the requested sources. -void Pcsx2App::SysExecute() -{ - if (!CheckForBIOS()) - return; - - SysExecutorThread.PostEvent( new SysExecEvent_Execute() ); -} - -// Executes the specified cdvd source and optional elf file. This command performs a -// full closure of any existing VM state and starts a fresh VM with the requested -// sources. -void Pcsx2App::SysExecute( CDVD_SourceType cdvdsrc, const wxString& elf_override ) -{ - if (!CheckForBIOS()) - return; - - SysExecutorThread.PostEvent( new SysExecEvent_Execute(cdvdsrc, elf_override) ); -} - -// Returns true if there is a "valid" virtual machine state from the user's perspective. This -// means the user has started the emulator and not issued a full reset. -// Thread Safety: The state of the system can change in parallel to execution of the -// main thread. If you need to perform an extended length activity on the execution -// state (such as saving it), you *must* suspend the Corethread first! -__fi bool SysHasValidState() -{ - return CoreThread.HasActiveMachine(); -} - -// Writes text to console and updates the window status bar and/or HUD or whateverness. -// FIXME: This probably isn't thread safe. >_< -void SysStatus( const wxString& text ) -{ - // mirror output to the console! - Console.WriteLn( text.ToStdString() ); - sMainFrame.SetStatusText( text ); -} - -// Applies a new active iso source file -void SysUpdateIsoSrcFile( const wxString& newIsoFile ) -{ - g_Conf->CurrentIso = newIsoFile; - sMainFrame.UpdateStatusBar(); - sMainFrame.UpdateCdvdSrcSelection(); -} - -void SysUpdateDiscSrcDrive( const wxString& newDiscDrive ) -{ - g_Conf->Folders.RunDisc = newDiscDrive; - AppSaveSettings(); - sMainFrame.UpdateCdvdSrcSelection(); -} - -bool HasMainFrame() -{ - return wxTheApp && wxGetApp().HasMainFrame(); -} - -// This method generates debug assertions if either the wxApp or MainFrame handles are -// NULL (typically indicating that PCSX2 is running in NoGUI mode, or that the main -// frame has been closed). In most cases you'll want to use HasMainFrame() to test -// for gui validity first, or use GetMainFramePtr() and manually check for NULL (choice -// is a matter of programmer preference). -MainEmuFrame& GetMainFrame() -{ - return wxGetApp().GetMainFrame(); -} - -// Returns a pointer to the main frame of the GUI (frame may be hidden from view), or -// NULL if no main frame exists (NoGUI mode and/or the frame has been destroyed). If -// the wxApp is NULL then this will also return NULL. -MainEmuFrame* GetMainFramePtr() -{ - return wxTheApp ? wxGetApp().GetMainFramePtr() : NULL; -} - -SysMainMemory& GetVmMemory() -{ - return wxGetApp().GetVmReserve(); -} - -SysCpuProviderPack& GetCpuProviders() -{ - return *wxGetApp().m_CpuProviders; -} diff --git a/pcsx2/gui/AppRes.cpp b/pcsx2/gui/AppRes.cpp deleted file mode 100644 index abe10df6cf..0000000000 --- a/pcsx2/gui/AppRes.cpp +++ /dev/null @@ -1,180 +0,0 @@ -/* PCSX2 - PS2 Emulator for PCs - * Copyright (C) 2002-2021 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 . - */ - -#include "PrecompiledHeader.h" -#include "MainFrame.h" - -#include -#include -#include -#include - -#include "MSWstuff.h" - -#include "gui/EmbeddedImage.h" -#include "gui/Resources/BackgroundLogo.h" -#include "gui/Resources/ButtonIcon_Camera.h" - -#include "gui/Resources/ConfigIcon_Cpu.h" -#include "gui/Resources/ConfigIcon_Video.h" -#include "gui/Resources/ConfigIcon_Speedhacks.h" -#include "gui/Resources/ConfigIcon_Gamefixes.h" -#include "gui/Resources/ConfigIcon_Paths.h" -#include "gui/Resources/ConfigIcon_MemoryCard.h" - -#include "gui/Resources/AppIcon16.h" -#include "gui/Resources/AppIcon32.h" -#include "gui/Resources/AppIcon64.h" - -RecentIsoList::RecentIsoList(int firstIdForMenuItems_or_wxID_ANY) - : Menu(std::unique_ptr(new wxMenu())) -{ - Menu->Append(MenuId_IsoBrowse, _("Browse..."), _("Browse for an ISO that is not in your recent history.")); - Menu->AppendSeparator(); - Menu->AppendCheckItem(MenuId_Ask_On_Booting, _("Always ask when booting"), _("Manually select an ISO upon boot ignoring the selection from recent ISO list.")); - Menu->Check(MenuId_Ask_On_Booting, g_Conf->AskOnBoot); - - Manager = std::unique_ptr(new RecentIsoManager(Menu.get(), firstIdForMenuItems_or_wxID_ANY)); -} - -pxAppResources::pxAppResources() -{ -} - -pxAppResources::~pxAppResources() = default; - -wxMenu& Pcsx2App::GetRecentIsoMenu() -{ - if (!m_RecentIsoList) - m_RecentIsoList = std::unique_ptr(new RecentIsoList(MenuId_RecentIsos_reservedStart)); - return *m_RecentIsoList->Menu; -} - -RecentIsoManager& Pcsx2App::GetRecentIsoManager() -{ - if (!m_RecentIsoList) - m_RecentIsoList = std::unique_ptr(new RecentIsoList(MenuId_RecentIsos_reservedStart)); - return *m_RecentIsoList->Manager; -} - -wxMenu& Pcsx2App::GetDriveListMenu() -{ - if (!m_DriveList) - { - m_DriveList = std::unique_ptr(new DriveList()); - } - - return *m_DriveList->Menu; -} - -pxAppResources& Pcsx2App::GetResourceCache() -{ - ScopedLock lock(m_mtx_Resources); - if (!m_Resources) - m_Resources = std::unique_ptr(new pxAppResources()); - - return *m_Resources; -} - -const wxIconBundle& Pcsx2App::GetIconBundle() -{ - std::unique_ptr& bundle(GetResourceCache().IconBundle); - if (!bundle) - { - bundle = std::unique_ptr(new wxIconBundle()); - bundle->AddIcon(EmbeddedImage().GetIcon()); - bundle->AddIcon(EmbeddedImage().GetIcon()); - bundle->AddIcon(EmbeddedImage().GetIcon()); - } - - return *bundle; -} - -const wxBitmap& Pcsx2App::GetLogoBitmap() -{ - std::unique_ptr& logo(GetResourceCache().Bitmap_Logo); - if (logo) - return *logo; - - wxImage img = EmbeddedImage().Get(); - float scale = MSW_GetDPIScale(); // 1.0 for non-Windows - logo = std::unique_ptr(new wxBitmap(img.Scale(img.GetWidth() * scale, img.GetHeight() * scale, wxIMAGE_QUALITY_HIGH))); - - return *logo; -} - -const wxBitmap& Pcsx2App::GetScreenshotBitmap() -{ - std::unique_ptr& screenshot(GetResourceCache().ScreenshotBitmap); - if (screenshot) - return *screenshot; - - wxImage img = EmbeddedImage().Get(); - float scale = MSW_GetDPIScale(); // 1.0 for non-Windows - screenshot = std::unique_ptr(new wxBitmap(img.Scale(img.GetWidth() * scale, img.GetHeight() * scale, wxIMAGE_QUALITY_HIGH))); - - return *screenshot; -} - -wxImageList& Pcsx2App::GetImgList_Config() -{ - std::unique_ptr& images(GetResourceCache().ConfigImages); - if (!images) - { - int image_size = MSW_GetDPIScale() * g_Conf->Listbook_ImageSize; - images = std::unique_ptr(new wxImageList(image_size, image_size)); - -#undef FancyLoadMacro -#define FancyLoadMacro(name) \ - { \ - wxImage img = EmbeddedImage().Get(); \ - img.Rescale(image_size, image_size, wxIMAGE_QUALITY_HIGH); \ - m_Resources->ImageId.Config.name = images->Add(img); \ - } - - FancyLoadMacro(Paths); - FancyLoadMacro(Gamefixes); - FancyLoadMacro(Speedhacks); - FancyLoadMacro(MemoryCard); - FancyLoadMacro(Video); - FancyLoadMacro(Cpu); - } - return *images; -} - -// This stuff seems unused? -wxImageList& Pcsx2App::GetImgList_Toolbars() -{ - std::unique_ptr& images(GetResourceCache().ToolbarImages); - - if (!images) - { - const int imgSize = g_Conf->Toolbar_ImageSize ? 64 : 32; - images = std::unique_ptr(new wxImageList(imgSize, imgSize)); - -#undef FancyLoadMacro -#define FancyLoadMacro(name) \ - { \ - wxImage img = EmbeddedImage(imgSize, imgSize).Get(); \ - m_Resources.ImageId.Toolbars.name = images->Add(img); \ - } - } - return *images; -} - -const AppImageIds& Pcsx2App::GetImgId() const -{ - return m_Resources->ImageId; -} diff --git a/pcsx2/gui/AppSaveStates.h b/pcsx2/gui/AppSaveStates.h deleted file mode 100644 index 361b564ea6..0000000000 --- a/pcsx2/gui/AppSaveStates.h +++ /dev/null @@ -1,25 +0,0 @@ -/* PCSX2 - PS2 Emulator for PCs - * Copyright (C) 2002-2010 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 . - */ - -#pragma once - -#include "App.h" -#include "SaveState.h" -#include "Saveslots.h" - -extern void StateCopy_SaveToFile(const wxString& file); -extern void StateCopy_LoadFromFile(const wxString& file); -extern void StateCopy_SaveToSlot(uint num); -extern void StateCopy_LoadFromSlot(uint slot, bool isFromBackup = false); diff --git a/pcsx2/gui/AppTrait.h b/pcsx2/gui/AppTrait.h deleted file mode 100644 index 2d837449b8..0000000000 --- a/pcsx2/gui/AppTrait.h +++ /dev/null @@ -1,39 +0,0 @@ -/* PCSX2 - PS2 Emulator for PCs - * Copyright (C) 2002-2014 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 . - */ - -#pragma once - -#include - -// -------------------------------------------------------------------------------------- -// Pcsx2AppTraits -// -------------------------------------------------------------------------------------- -// Overrides and customizes some default wxWidgets behaviors. This class is instanized by -// calls to Pcsx2App::CreateTraits(), which is called from wxWidgets as-needed. wxWidgets -// does cache an instance of the traits, so the object construction need not be trivial -// (translation: it can be complicated-ish -- it won't affect performance). -// -class Pcsx2AppTraits : public wxGUIAppTraits -{ - typedef wxGUIAppTraits _parent; - -public: - virtual ~Pcsx2AppTraits() {} - wxMessageOutput* CreateMessageOutput(); - -#ifdef wxUSE_STDPATHS - wxStandardPaths& GetStandardPaths(); -#endif -}; diff --git a/pcsx2/gui/AppUserMode.cpp b/pcsx2/gui/AppUserMode.cpp deleted file mode 100644 index 5084df229e..0000000000 --- a/pcsx2/gui/AppUserMode.cpp +++ /dev/null @@ -1,301 +0,0 @@ -/* PCSX2 - PS2 Emulator for PCs - * Copyright (C) 2002-2010 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 . - */ - -#include "PrecompiledHeader.h" -#include "MainFrame.h" -#include "gui/IniInterface.h" -#include "Dialogs/ModalPopups.h" - -#include - -#ifdef __WXMSW__ -#include "wx/msw/regconf.h" -#endif - -DocsModeType DocsFolderMode = DocsFolder_User; -bool UseDefaultSettingsFolder = true; - - -wxDirName CustomDocumentsFolder; -wxDirName SettingsFolder; - -wxDirName InstallFolder; - -// The UserLocalData folder can be redefined depending on whether or not PCSX2 is in -// "portable install" mode or not. when PCSX2 has been configured for portable install, the -// UserLocalData folder is the current working directory. -// -InstallationModeType InstallationMode; - -static wxFileName GetPortableIniPath() -{ - wxString programFullPath = wxStandardPaths::Get().GetExecutablePath(); - wxDirName programDir( wxFileName(programFullPath).GetPath() ); - - return programDir + "portable.ini"; -} - -static wxString GetMsg_PortableModeRights() -{ - return pxE( L"Please ensure that these folders are created and that your user account is granted write permissions to them -- or re-run PCSX2 with elevated (administrator) rights, which should grant PCSX2 the ability to create the necessary folders itself. If you do not have elevated rights on this computer, then you will need to switch to User Documents mode (click button below)." - ); -}; - -bool Pcsx2App::TestUserPermissionsRights( const wxDirName& testFolder, wxString& createFailedStr, wxString& accessFailedStr ) -{ - // We need to individually verify read/write permission for each PCSX2 user documents folder. - // If any of the folders are not writable, then the user should be informed asap via - // friendly and courteous dialog box! - - const wxDirName PermissionFolders[] = - { - PathDefs::Base::Settings(), - PathDefs::Base::MemoryCards(), - PathDefs::Base::Savestates(), - PathDefs::Base::Snapshots(), - PathDefs::Base::Logs(), - PathDefs::Base::CheatsWS(), - #ifdef PCSX2_DEVBUILD - PathDefs::Base::Dumps(), - #endif - }; - - FastFormatUnicode createme, accessme; - - for (uint i=0; i conf_portable( OpenFileConfig( portableIniFile.GetFullPath() ) ); - conf_portable->SetRecordDefaults(false); - - while( true ) - { - wxString accessFailedStr, createFailedStr; - if (TestUserPermissionsRights( portableDocsFolder, createFailedStr, accessFailedStr )) break; - - wxDialogWithHelpers dialog( NULL, AddAppName(_("Portable mode error - %s")) ); - - wxTextCtrl* scrollText = new wxTextCtrl( - &dialog, wxID_ANY, wxEmptyString, wxDefaultPosition, wxDefaultSize, - wxTE_READONLY | wxTE_MULTILINE | wxTE_WORDWRAP - ); - - if (!createFailedStr.IsEmpty()) - scrollText->AppendText( createFailedStr + L"\n" ); - - if (!accessFailedStr.IsEmpty()) - scrollText->AppendText( accessFailedStr + L"\n" ); - - dialog += dialog.Heading( _("PCSX2 has been installed as a portable application but cannot run due to the following errors:" ) ); - dialog += scrollText | pxExpand.Border(wxALL, 16); - dialog += 6; - dialog += dialog.Text( GetMsg_PortableModeRights() ); - - // [TODO] : Add url for platform-relevant user permissions tutorials? (low priority) - - wxWindowID result = pxIssueConfirmation( dialog, - MsgButtons().Retry().Cancel().Custom(_("Switch to User Documents Mode"), "switchmode") - ); - - switch (result) - { - case wxID_CANCEL: - throw Exception::StartupAborted( "User canceled portable mode due to insufficient user access/permissions." ); - - case wxID_RETRY: - // do nothing (continues while loop) - break; - - case pxID_CUSTOM: - wxDialogWithHelpers dialog2( NULL, AddAppName(_("%s is switching to local install mode.")) ); - dialog2 += dialog2.Heading( _("Try to remove the file called \"portable.ini\" from your installation directory manually." ) ); - dialog2 += 6; - pxIssueConfirmation( dialog2, MsgButtons().OK() ); - - return NULL; - } - - } - - // Success -- all user-based folders have write access. PCSX2 should be able to run error-free! - // Force-set the custom documents mode, and set the - - InstallationMode = InstallMode_Portable; - DocsFolderMode = DocsFolder_Custom; - CustomDocumentsFolder = portableDocsFolder; - return conf_portable.release(); - } - - return NULL; -} - -// Reset RunWizard so the FTWizard is run again on next PCSX2 start. -void Pcsx2App::WipeUserModeSettings() -{ - if (InstallationMode == InstallMode_Portable) - { - // Remove the portable.ini entry "RunWizard" conforming to this instance of PCSX2. - wxFileName portableIniFile( GetPortableIniPath() ); - std::unique_ptr conf_portable( OpenFileConfig( portableIniFile.GetFullPath() ) ); - conf_portable->DeleteEntry(L"RunWizard"); - } - else - { - // Remove the registry entry "RunWizard" conforming to this instance of PCSX2. - std::unique_ptr conf_install( OpenInstallSettingsFile() ); - conf_install->DeleteEntry(L"RunWizard"); - } -} - -static void DoFirstTimeWizard() -{ - // first time startup, so give the user the choice of user mode: - while(true) - { - // PCSX2's FTWizard allows improptu restarting of the wizard without cancellation. - // This is typically used to change the user's language selection. - - FirstTimeWizard wiz( NULL ); - if( wiz.RunWizard( wiz.GetFirstPage() ) ) break; - if (wiz.GetReturnCode() != pxID_RestartWizard) - throw Exception::StartupAborted( "User canceled FirstTime Wizard." ); - - Console.WriteLn( Color_StrongBlack, "Restarting First Time Wizard!" ); - } -} - -wxConfigBase* Pcsx2App::OpenInstallSettingsFile() -{ - std::unique_ptr conf_install; - -#ifdef __WXMSW__ - conf_install = std::unique_ptr(new wxRegConfig()); -#else - // FIXME!! Linux / Mac - // Where the heck should this information be stored? - - wxDirName usrlocaldir( PathDefs::GetUserLocalDataDir() ); - //wxDirName usrlocaldir( wxStandardPaths::Get().GetDataDir() ); - if( !usrlocaldir.Exists() ) - { - Console.WriteLn( L"Creating UserLocalData folder: " + usrlocaldir.ToString() ); - usrlocaldir.Mkdir(); - } - - wxFileName usermodefile( GetAppName() + L"-reg.ini" ); - usermodefile.SetPath( usrlocaldir.ToString() ); - conf_install = std::unique_ptr(OpenFileConfig( usermodefile.GetFullPath() )); -#endif - - return conf_install.release(); -} - - -void Pcsx2App::ForceFirstTimeWizardOnNextRun() -{ - std::unique_ptr conf_install; - - conf_install = std::unique_ptr(TestForPortableInstall()); - if (!conf_install) - conf_install = std::unique_ptr(OpenInstallSettingsFile()); - - conf_install->Write( L"RunWizard", true ); -} - -void Pcsx2App::EstablishAppUserMode() -{ - std::unique_ptr conf_install; - - conf_install = std::unique_ptr(TestForPortableInstall()); - if (!conf_install) - conf_install = std::unique_ptr(OpenInstallSettingsFile()); - - conf_install->SetRecordDefaults(false); - - // Run the First Time Wizard! - // ---------------------------- - // Wizard is only run once. The status of the wizard having been run is stored in - // the installation ini file, which can be either the portable install (useful for admins) - // or the registry/user local documents position. - - bool runWiz; - conf_install->Read( L"RunWizard", &runWiz, true ); - - App_LoadInstallSettings( conf_install.get() ); - - if( !Startup.ForceWizard && !runWiz ) - { - AppConfig_OnChangedSettingsFolder( false ); - return; - } - - DoFirstTimeWizard(); - - // Save user's new settings - App_SaveInstallSettings( conf_install.get() ); - AppConfig_OnChangedSettingsFolder( true ); - AppSaveSettings(); - - // Wizard completed successfully, so let's not torture the user with this crap again! - conf_install->Write( L"RunWizard", false ); -} - diff --git a/pcsx2/gui/ApplyState.h b/pcsx2/gui/ApplyState.h deleted file mode 100644 index ea8bb7e286..0000000000 --- a/pcsx2/gui/ApplyState.h +++ /dev/null @@ -1,243 +0,0 @@ -/* PCSX2 - PS2 Emulator for PCs - * Copyright (C) 2002-2021 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 . - */ - -#pragma once - -#include -#include - -#include "AppCommon.h" - -class BaseApplicableConfigPanel; -class BaseApplicableDialog; - -wxDECLARE_EVENT(pxEvt_ApplySettings, wxCommandEvent); - -namespace Exception -{ - // -------------------------------------------------------------------------- - // Exception used to perform an abort of Apply/Ok action on settings panels. - // When thrown, the user receives a popup message containing the information - // specified in the exception message, and is returned to the settings dialog - // to correct the invalid input fields. - // - class CannotApplySettings : public BaseException - { - DEFINE_EXCEPTION_COPYTORS( CannotApplySettings, BaseException ) - DEFINE_EXCEPTION_MESSAGES( CannotApplySettings ) - - public: - bool IsVerbose; - - protected: - BaseApplicableConfigPanel* m_Panel; - - protected: - CannotApplySettings() { IsVerbose = true; } - - public: - explicit CannotApplySettings( BaseApplicableConfigPanel* thispanel ) - { - SetBothMsgs("Cannot apply new settings, one of the settings is invalid."); - m_Panel = thispanel; - IsVerbose = true; - } - - virtual CannotApplySettings& Quiet() { IsVerbose = false; return *this; } - BaseApplicableConfigPanel* GetPanel() { return m_Panel; } - }; -} - -typedef std::list PanelApplyList_t; - -struct ApplyStateStruct -{ - // collection of ApplicableConfigPanels that belong to a dialog box. - PanelApplyList_t PanelList; - - // Current book page being initialized. Any apply objects created will use - // this page as their "go here on error" page. (used to take the user to the - // page with the option that failed apply validation). - int CurOwnerPage; - - // TODO : Rename me to CurOwnerBook, or rename the one above to ParentPage. - wxBookCtrlBase* ParentBook; - - ApplyStateStruct() - { - CurOwnerPage = wxID_NONE; - ParentBook = NULL; - } - - void SetCurrentPage( int page ) - { - CurOwnerPage = page; - } - - void ClearCurrentPage() - { - CurOwnerPage = wxID_NONE; - } - - void StartBook( wxBookCtrlBase* book ); - void StartWizard(); - bool ApplyAll(); - bool ApplyPage( int pageid ); - void DoCleanup() noexcept; -}; - -class IApplyState -{ -protected: - ApplyStateStruct m_ApplyState; - -public: - virtual ApplyStateStruct& GetApplyState() { return m_ApplyState; } -}; - -// -------------------------------------------------------------------------------------- -// BaseApplicableDialog -// -------------------------------------------------------------------------------------- -class BaseApplicableDialog - : public wxDialogWithHelpers - , public IApplyState -{ - wxDECLARE_DYNAMIC_CLASS_NO_COPY(BaseApplicableDialog); - -public: - BaseApplicableDialog() {} - virtual ~BaseApplicableDialog(); - - // Must return the same thing as GetNameStatic; a name ideal for use in uniquely - // identifying dialogs. (this version is the 'instance' version, which is called - // by BaseConfigurationDialog to assign the wxWidgets dialog name, and for saving - // screenshots to disk) - virtual wxString GetDialogName() const; - -protected: - void Init(); - - BaseApplicableDialog(wxWindow* parent, const wxString& title, const pxDialogCreationFlags& cflags = pxDialogCreationFlags() ); - - virtual void OnSettingsApplied( wxCommandEvent& evt ); - - // Note: This method *will* be called automatically after a successful Apply, but will not - // be called after a failed Apply (canceled due to error). - virtual void AppStatusEvent_OnSettingsApplied() {} - - virtual void AppStatusEvent_OnUiSettingsLoadSave( const AppSettingsEventInfo& ) {} - virtual void AppStatusEvent_OnExit() {} -}; - -// -------------------------------------------------------------------------------------- -// BaseApplicableConfigPanel -// -------------------------------------------------------------------------------------- -// Extends the Panel class to add an Apply() method, which is invoked from the parent -// window (usually the ConfigurationDialog) when either Ok or Apply is clicked. -// -// Thread Safety: None. This class is only safe when used from the GUI thread, as it uses -// static vars and assumes that only one ApplicableConfig system is available to the -// user at any time (ie, a singular modal dialog). -// -class BaseApplicableConfigPanel - : public wxPanelWithHelpers -{ -protected: - int m_OwnerPage; - wxBookCtrlBase* m_OwnerBook; - - EventListenerHelper_AppStatus m_AppStatusHelper; - -public: - virtual ~BaseApplicableConfigPanel(); - - BaseApplicableConfigPanel( wxWindow* parent, wxOrientation orient=wxVERTICAL ); - BaseApplicableConfigPanel( wxWindow* parent, wxOrientation orient, const wxString& staticLabel ); - - int GetOwnerPage() const { return m_OwnerPage; } - wxBookCtrlBase* GetOwnerBook() { return m_OwnerBook; } - - void SetFocusToMe(); - IApplyState* FindApplyStateManager() const; - - // Returns true if this ConfigPanel belongs to the specified page. Useful for doing - // selective application of options for specific pages. - bool IsOnPage( int pageid ) { return m_OwnerPage == pageid; } - - // This method attempts to assign the settings for the panel into the given - // configuration structure (which is typically a copy of g_Conf). If validation - // of form contents fails, the function should throw Exception::CannotApplySettings. - // If no exceptions are thrown, then the operation is assumed a success. :) - virtual void Apply()=0; - - //Enable the apply button manually if required (in case the auto-trigger doesn't kick in, e.g. when clicking a button) - void SomethingChanged(); - - - void Init(); - - // Mandatory override: As a rule for proper interface design, all deriving classes need - // to implement this OnSettingsApplied function. There's no implementation of an options/ - // settings panel that does not heed the changes of application status/settings changes. ;) - // - // Note: This method *will* be called automatically after a successful Apply, but will not - // be called after a failed Apply (canceled due to error). - virtual void AppStatusEvent_OnSettingsApplied()=0; - - virtual void AppStatusEvent_OnUiSettingsLoadSave( const AppSettingsEventInfo& ) {} - virtual void AppStatusEvent_OnVmSettingsLoadSave( const AppSettingsEventInfo& ) {} - virtual void AppStatusEvent_OnExit() {} - - virtual bool IsSpecificConfig(){return false;} //lame RTTI - -protected: - virtual void OnSettingsApplied( wxCommandEvent& evt ); -}; - -class BaseApplicableConfigPanel_SpecificConfig : public BaseApplicableConfigPanel -{ - //This class only differs from BaseApplicableConfigPanel by systematically allowing the gui to be updated - //from a specific config (used by the presets system to trash not g_Conf in case the user pressed "Cancel"). - //Every panel that the Presets affect should be derived from this and not from BaseApplicableConfigPanel. - // - //Multiple inheritance would have been better (also for cases of non BaseApplicableConfigPanel which are effected by presets), - //but multiple inheritance sucks. So, subclass. - //NOTE: because ApplyConfigToGui is called manually and not via an event, it must consider manuallyPropagate and call sub panels. -public: - virtual bool IsSpecificConfig(){return true;}; - BaseApplicableConfigPanel_SpecificConfig( wxWindow* parent, wxOrientation orient=wxVERTICAL ); - BaseApplicableConfigPanel_SpecificConfig( wxWindow* parent, wxOrientation orient, const wxString& staticLabel ); - - //possible flags are: AppConfig: APPLY_FLAG_MANUALLY_PROPAGATE and APPLY_FLAG_IS_FROM_PRESET - virtual void ApplyConfigToGui(AppConfig& configToApply, int flags=0) = 0; -}; - -class ApplicableWizardPage : public wxWizardPageSimple, public IApplyState -{ - wxDECLARE_DYNAMIC_CLASS_NO_COPY(ApplicableWizardPage); - -public: - ApplicableWizardPage( - wxWizard* parent=NULL, - wxWizardPage *prev = NULL, - wxWizardPage *next = NULL, - const wxBitmap& bitmap = wxNullBitmap - ); - - virtual ~ApplicableWizardPage() { m_ApplyState.DoCleanup(); } - - virtual bool PrepForApply(); -}; - diff --git a/pcsx2/gui/CheckedStaticBox.cpp b/pcsx2/gui/CheckedStaticBox.cpp deleted file mode 100644 index d2d707c8a5..0000000000 --- a/pcsx2/gui/CheckedStaticBox.cpp +++ /dev/null @@ -1,79 +0,0 @@ -/* PCSX2 - PS2 Emulator for PCs - * Copyright (C) 2002-2010 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 . - */ - -#include "PrecompiledHeader.h" -#include "gui/CheckedStaticBox.h" - -CheckedStaticBox::CheckedStaticBox(wxWindow* parent, int orientation, const wxString& title) - : wxPanelWithHelpers(parent, wxVERTICAL) - , ThisSizer(*new wxStaticBoxSizer(orientation, this)) - , ThisToggle(*new wxCheckBox(this, wxID_ANY, title, wxPoint(8, 0))) -{ - *this += ThisToggle; - *this += ThisSizer | pxExpand; - - // Ensure that the right-side of the static group box isn't too cozy: - SetMinWidth(ThisToggle.GetSize().GetWidth() + 32); - - Bind(wxEVT_CHECKBOX, &CheckedStaticBox::MainToggle_Click, this, ThisToggle.GetId()); -} - -// Event handler for click events for the main checkbox (default behavior: enables/disables all child controls) -// This function can be overridden to implement custom handling of check enable/disable behavior. -void CheckedStaticBox::MainToggle_Click(wxCommandEvent& evt) -{ - SetValue(evt.IsChecked()); - evt.Skip(); -} - -// Sets the main checkbox status, and enables/disables all child controls -// bound to the StaticBox accordingly. -void CheckedStaticBox::SetValue(bool val) -{ - wxWindowList& list = GetChildren(); - - for (wxWindowList::iterator iter = list.begin(); iter != list.end(); ++iter) - { - wxWindow* current = *iter; - if (current != &ThisToggle) - current->Enable(IsEnabled() && val); - } - ThisToggle.SetValue(val); -} - -bool CheckedStaticBox::GetValue() const -{ - return ThisToggle.GetValue(); -} - -// This override is here so to only enable the children if both the main toggle and -// the enable request are true. If not, disable them! -bool CheckedStaticBox::Enable(bool enable) -{ - if (!_parent::Enable(enable)) - return false; - - bool val = enable && ThisToggle.GetValue(); - wxWindowList& list = GetChildren(); - - for (wxWindowList::iterator iter = list.begin(); iter != list.end(); ++iter) - { - wxWindow* current = *iter; - if (current != &ThisToggle) - current->Enable(val); - } - - return true; -} diff --git a/pcsx2/gui/CheckedStaticBox.h b/pcsx2/gui/CheckedStaticBox.h deleted file mode 100644 index 2fa3a14a64..0000000000 --- a/pcsx2/gui/CheckedStaticBox.h +++ /dev/null @@ -1,37 +0,0 @@ -/* PCSX2 - PS2 Emulator for PCs - * Copyright (C) 2002-2010 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 . - */ - -#pragma once - -#include "gui/wxGuiTools.h" - -class CheckedStaticBox : public wxPanelWithHelpers -{ - typedef wxPanelWithHelpers _parent; - -public: - wxBoxSizer& ThisSizer; // Boxsizer which holds all child items. - wxCheckBox& ThisToggle; // toggle which can enable/disable all child controls - -public: - CheckedStaticBox(wxWindow* parent, int orientation, const wxString& title = wxEmptyString); - - void SetValue(bool val); - bool GetValue() const; - bool Enable(bool enable = true); - -public: - virtual void MainToggle_Click(wxCommandEvent& evt); -}; diff --git a/pcsx2/gui/ConsoleLogger.cpp b/pcsx2/gui/ConsoleLogger.cpp deleted file mode 100644 index 93ad6050d0..0000000000 --- a/pcsx2/gui/ConsoleLogger.cpp +++ /dev/null @@ -1,1281 +0,0 @@ -/* PCSX2 - PS2 Emulator for PCs - * Copyright (C) 2002-2021 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 . - */ - -#include "PrecompiledHeader.h" -#include "App.h" -#include "MainFrame.h" -#include "ConsoleLogger.h" -#include "MSWstuff.h" -#include "Host.h" -#include "IniInterface.h" - -#include "common/Console.h" -#include "common/SafeArray.inl" -#include "Dialogs/LogOptionsDialog.h" -#include "DebugTools/Debug.h" -#include - -wxDECLARE_EVENT(pxEvt_SetTitleText, wxCommandEvent); -wxDECLARE_EVENT(pxEvt_FlushQueue, wxCommandEvent); - -wxDEFINE_EVENT(pxEvt_SetTitleText, wxCommandEvent); -wxDEFINE_EVENT(pxEvt_FlushQueue, wxCommandEvent); - -// C++ requires abstract destructors to exist, even though they're abstract. -PipeRedirectionBase::~PipeRedirectionBase() = default; - -// Writes text to the Visual Studio Output window (Microsoft Windows only). -// On all other platforms this pipes to Stdout instead. -void MSW_OutputDebugString(const wxString& text) -{ -#if defined(__WXMSW__) && !defined(__WXMICROWIN__) - static bool hasDebugger = wxIsDebuggerRunning(); - if (hasDebugger) - OutputDebugString(text); -#else - fputs(text.utf8_str(), stdout); - fflush(stdout); -#endif -} - -// ---------------------------------------------------------------------------- -// -void pxLogConsole::DoLogRecord(wxLogLevel level, const wxString &message, const wxLogRecordInfo &info) -{ - switch ( level ) - { - case wxLOG_Trace: - case wxLOG_Debug: - { - wxString str; - TimeStamp( &str ); - MSW_OutputDebugString( str + message + L"\n" ); - } - break; - - case wxLOG_FatalError: - // This one is unused by wx, and unused by PCSX2 (we prefer exceptions, thanks). - pxFailDev( "Stop using FatalError and use assertions or exceptions instead." ); - break; - - case wxLOG_Status: - // Also unsed by wx, and unused by PCSX2 also (we prefer direct API calls to our main window!) - pxFailDev( "Stop using wxLogStatus just access the Pcsx2App functions directly instead." ); - break; - - case wxLOG_Info: - if ( !GetVerbose() ) return; - // fallthrough! - - case wxLOG_Message: - Console.WriteLn( "[wx] %ls", WX_STR(message)); - break; - - case wxLOG_Error: - Console.Error("[wx] %ls", WX_STR(message)); - break; - - case wxLOG_Warning: - Console.Warning("[wx] %ls", WX_STR(message)); - break; - } -} - - -// ---------------------------------------------------------------------------- -void ConsoleTestThread::ExecuteTaskInThread() -{ - static int numtrack = 0; - - while( !m_done ) - { - // Two lines, both formatted, and varied colors. This makes for a fairly realistic - // worst case scenario (without being entirely unrealistic). - Console.WriteLn( "This is a threaded logging test. Something bad could happen... %d", ++numtrack ); - Console.Warning( "Testing high stress loads %s", "(multi-color)" ); - Yield( 0 ); - } -} - -// ---------------------------------------------------------------------------- -// Pass an uninitialized file object. The function will ask the user for the -// filename and try to open it. It returns true on success (file was opened), -// false if file couldn't be opened/created and -1 if the file selection -// dialog was canceled. -// -static bool OpenLogFile(wxFile& file, wxString& filename, wxWindow *parent) -{ - filename = wxSaveFileSelector(L"log", L"txt", L"log.txt", parent); - if ( !filename ) return false; // canceled - - if( wxFile::Exists(filename) ) - { - bool bAppend = false; - wxString strMsg; - strMsg.Printf(L"Append log to file '%s' (choosing [No] will overwrite it)?", - filename.c_str()); - - switch ( Msgbox::ShowModal( _("Save log question."), strMsg, MsgButtons().YesNo().Cancel() ) ) - { - case wxID_YES: - bAppend = true; - break; - - case wxID_NO: - bAppend = false; - break; - - case wxID_CANCEL: - return false; - - default: - pxFailDev( "Invalid message box return value." ); - } - - return ( bAppend ) ? - file.Open(filename, wxFile::write_append) : - file.Create(filename, true /* overwrite */); - } - - return file.Create(filename); -} - -// -------------------------------------------------------------------------------------- -// ConsoleLogFrame::ColorArray (implementations) -// -------------------------------------------------------------------------------------- - -// fontsize - size of the font specified in points. -// (actual font used is the system-selected fixed-width font) -// -ConsoleLogFrame::ColorArray::ColorArray( int fontsize ) -{ - SetFont( fontsize ); -} - -void ConsoleLogFrame::ColorArray::SetFont( int fontsize ) -{ - const wxFont fixed( pxGetFixedFont( fontsize ) ); - const wxFont fixedB( pxGetFixedFont( fontsize+1, wxFONTWEIGHT_BOLD ) ); - - // Standard R, G, B format: - for (size_t i = 0; i < Color_StrongBlack; ++i) - m_table[i].SetFont(fixed); - - for (size_t i = Color_StrongBlack; i < ConsoleColors_Count; ++i) - m_table[i].SetFont(fixedB); -} - -void ConsoleLogFrame::ColorArray::SetColorScheme_Dark() -{ - m_table[Color_Default] .SetTextColour(wxColor( 208, 208, 208 )); - m_table[Color_Black] .SetTextColour(wxColor( 255, 255, 255 )); - m_table[Color_Red] .SetTextColour(wxColor( 255, 87, 87 )); - m_table[Color_Green] .SetTextColour(wxColor( 58, 194, 121 )); - m_table[Color_Blue] .SetTextColour(wxColor( 14, 105, 230 )); - m_table[Color_Magenta] .SetTextColour(wxColor( 160, 0, 160 )); - m_table[Color_Orange] .SetTextColour(wxColor( 160, 120, 0 )); - m_table[Color_Gray] .SetTextColour(wxColor( 128, 128, 128 )); - - m_table[Color_Cyan] .SetTextColour(wxColor( 128, 180, 180 )); - m_table[Color_Yellow] .SetTextColour(wxColor( 194, 194, 72 )); - m_table[Color_White] .SetTextColour(wxColor( 160, 160, 160 )); - - m_table[Color_StrongBlack] .SetTextColour(wxColor( 255, 255, 255 )); - m_table[Color_StrongRed] .SetTextColour(wxColor( 230, 23, 23 )); - m_table[Color_StrongGreen] .SetTextColour(wxColor( 4, 185, 4 )); - m_table[Color_StrongBlue] .SetTextColour(wxColor( 63, 173, 232 )); - m_table[Color_StrongMagenta].SetTextColour(wxColor( 160, 0, 160 )); - m_table[Color_StrongOrange] .SetTextColour(wxColor( 160, 120, 0 )); - m_table[Color_StrongGray] .SetTextColour(wxColor( 128, 128, 128 )); - - m_table[Color_StrongCyan] .SetTextColour(wxColor( 128, 180, 180 )); - m_table[Color_StrongYellow] .SetTextColour(wxColor( 194, 194, 72 )); - m_table[Color_StrongWhite] .SetTextColour(wxColor( 160, 160, 160 )); -} - -void ConsoleLogFrame::ColorArray::SetColorScheme_Light() -{ - m_table[Color_Default] .SetTextColour(wxColor( 0, 0, 0 )); - m_table[Color_Black] .SetTextColour(wxColor( 0, 0, 0 )); - m_table[Color_Red] .SetTextColour(wxColor( 128, 0, 0 )); - m_table[Color_Green] .SetTextColour(wxColor( 0, 128, 0 )); - m_table[Color_Blue] .SetTextColour(wxColor( 0, 0, 128 )); - m_table[Color_Magenta] .SetTextColour(wxColor( 160, 0, 160 )); - m_table[Color_Orange] .SetTextColour(wxColor( 160, 120, 0 )); - m_table[Color_Gray] .SetTextColour(wxColor( 108, 108, 108 )); - - m_table[Color_Cyan] .SetTextColour(wxColor( 128, 180, 180 )); - m_table[Color_Yellow] .SetTextColour(wxColor( 180, 180, 128 )); - m_table[Color_White] .SetTextColour(wxColor( 160, 160, 160 )); - - m_table[Color_StrongBlack] .SetTextColour(wxColor( 0, 0, 0 )); - m_table[Color_StrongRed] .SetTextColour(wxColor( 128, 0, 0 )); - m_table[Color_StrongGreen] .SetTextColour(wxColor( 0, 128, 0 )); - m_table[Color_StrongBlue] .SetTextColour(wxColor( 0, 0, 128 )); - m_table[Color_StrongMagenta].SetTextColour(wxColor( 160, 0, 160 )); - m_table[Color_StrongOrange] .SetTextColour(wxColor( 160, 120, 0 )); - m_table[Color_StrongGray] .SetTextColour(wxColor( 108, 108, 108 )); - - m_table[Color_StrongCyan] .SetTextColour(wxColor( 128, 180, 180 )); - m_table[Color_StrongYellow] .SetTextColour(wxColor( 180, 180, 128 )); - m_table[Color_StrongWhite] .SetTextColour(wxColor( 160, 160, 160 )); -} - -// fixme - not implemented yet. -void ConsoleLogFrame::ColorArray::SetFont( const wxFont& font ) -{ - //for( int i=0; iEnabled initial value is ignored when loading. - if (ini.IsSaving() && !ConLogInitialized) - log->Enabled = ConLogDefaults[i]; - ini.Entry(StringUtil::UTF8StringToWxString(log->GetCategory() + "." + log->GetShortName()), log->Enabled, ConLogDefaults[i]); - } - } - - ConLogInitialized = true; -} - -// -------------------------------------------------------------------------------------- -// ConsoleLogFrame (implementations) -// -------------------------------------------------------------------------------------- -ConsoleLogFrame::ConsoleLogFrame( MainEmuFrame *parent, const wxString& title, AppConfig::ConsoleLogOptions& options ) - : wxFrame(parent, wxID_ANY, title) - , m_conf( options ) - , m_TextCtrl( *new wxTextCtrl(this, wxID_ANY, wxEmptyString, wxDefaultPosition, wxDefaultSize, - wxTE_MULTILINE | wxTE_READONLY | wxTE_RICH2 | wxTE_NOHIDESEL) ) - , m_timer_FlushUnlocker( this ) - , m_ColorTable( options.FontSize ) - - , m_pendingFlushMsg(false) - , m_WaitingThreadsForFlush(0) - , m_QueueColorSection( "ConsoleLog::QueueColorSection" ) - , m_QueueBuffer( "ConsoleLog::QueueBuffer" ) - , m_threadlogger( EnableThreadedLoggingTest ? new ConsoleTestThread() : NULL ) -{ - m_CurQueuePos = 0; - m_FlushRefreshLocked = false; - - // create Log menu (contains most options) - wxMenuBar *pMenuBar = new wxMenuBar(); - SetMenuBar( pMenuBar ); - SetIcons( wxGetApp().GetIconBundle() ); - - if (m_conf.Theme.CmpNoCase(L"Dark") == 0) - { - m_ColorTable.SetColorScheme_Dark(); - m_TextCtrl.SetBackgroundColour(darkThemeBgColor); - m_TextCtrl.SetDefaultStyle(wxTextAttr(wxNullColour, darkThemeBgColor)); - } - else - { - m_ColorTable.SetColorScheme_Light(); - m_TextCtrl.SetBackgroundColour(lightThemeBgColor); - m_TextCtrl.SetDefaultStyle(wxTextAttr(wxNullColour, lightThemeBgColor)); - } - - // TODO - SetDefaultStyle _should_ be returning a wxTextAttr with both foreground and background color info - // but I don't want to mess with it too much in fear of breaking existing functionality. - // instead, i just set the background color individually above - m_TextCtrl.SetDefaultStyle(m_ColorTable[DefaultConsoleColor]); - - // SetDefaultStyle only sets the style of text in the control. We need to - // also set the font of the control, so that sizing logic knows what font we use: - m_TextCtrl.SetFont( m_ColorTable[DefaultConsoleColor].GetFont() ); - - wxMenu& menuLog (*new wxMenu()); - wxMenu& menuAppear (*new wxMenu()); - wxMenu& menuSources (*new wxMenu()); - wxMenu& menuFontSizes( menuAppear ); - - // create Appearance menu and submenus - - menuFontSizes.Append( MenuId_FontSize_Small, _("&Small"), _t("7px font size."), - wxITEM_RADIO )->Check( options.FontSize == 7 ); - menuFontSizes.Append( MenuId_FontSize_Normal, _("&Normal Font"),_t("8px font size."), - wxITEM_RADIO )->Check( options.FontSize == 8 ); - menuFontSizes.Append( MenuId_FontSize_Large, _("&Large"), _t("10px font size."), - wxITEM_RADIO )->Check( options.FontSize == 10 ); - menuFontSizes.Append( MenuId_FontSize_Huge, _("&Huge"), _t("12px font size."), - wxITEM_RADIO )->Check( options.FontSize == 12 ); - - menuFontSizes.AppendSeparator(); - menuFontSizes.Append( MenuId_ColorScheme_Light, _("&Light Theme"), _t("Default soft-tone color scheme."), wxITEM_RADIO ); - menuFontSizes.Append( MenuId_ColorScheme_Dark, _("&Dark Theme"), _t("Modern dark color scheme."), wxITEM_RADIO ); - - // The "Always on Top" option currently doesn't work - //menuAppear.AppendSeparator(); - //menuAppear.Append( wxID_ANY, _("Always on Top"), - // _t("When checked the log window will be visible over other foreground windows."), wxITEM_CHECK ); - - menuLog.Append(wxID_SAVE, _("&Save..."), _("Save log contents to file.")); -#ifdef PCSX2_DEVBUILD - menuLog.Append(MenuId_Log_Settings, _("&Settings..."), _("Open the logging settings dialog.")); -#endif - menuLog.Append(wxID_CLEAR, _("C&lear"), _("Clear the log window contents.")); - menuLog.AppendSeparator(); - menuLog.AppendCheckItem(MenuId_AutoDock, _("Auto&dock"), _("Dock log window to main PCSX2 window."))->Check(m_conf.AutoDock); - menuLog.AppendSubMenu( &menuAppear, _("&Appearance") ); - menuLog.AppendSeparator(); - menuLog.Append(wxID_CLOSE, _("&Close"), _("Close this log window; contents are preserved.")); - - // Source Selection/Toggle menu - - menuSources.Append( MenuId_LogSource_Devel, _("Dev/&Verbose"), _("Shows PCSX2 developer logs."), wxITEM_CHECK ); - menuSources.Append( MenuId_LogSource_CDVD_Info, _("&CDVD reads"), _("Shows disk read activity."), wxITEM_CHECK ); - - menuSources.AppendSeparator(); - - uint srcnt = std::size(ConLogSources); - for (uint i=0; iGetName(), log->GetDescription(), wxITEM_CHECK ); - Bind(wxEVT_MENU, &ConsoleLogFrame::OnToggleSource, this, MenuId_LogSource_Start + i); - } - else - menuSources.AppendSeparator(); - } - - menuSources.AppendSeparator(); - menuSources.Append( MenuId_LogSource_EnableAll, _("&Enable All"), _("Enables all log source filters.") ); - menuSources.Append( MenuId_LogSource_DisableAll, _("&Disable All"), _("Disables all log source filters.") ); - menuSources.Append( MenuId_LogSource_SetDefault, _("&Restore Default"), _("Restore default source filters.") ); - - pMenuBar->Append(&menuLog, _("&Log")); - pMenuBar->Append(&menuSources, _("&Sources")); - - // status bar for menu prompts - CreateStatusBar(); - - SetSize( wxRect( options.DisplayPosition, options.DisplaySize ) ); - Show( options.Visible ); - - // Bind Events: - - Bind(wxEVT_MENU, &ConsoleLogFrame::OnOpen, this, wxID_OPEN); - Bind(wxEVT_MENU, &ConsoleLogFrame::OnClose, this, wxID_CLOSE); - Bind(wxEVT_MENU, &ConsoleLogFrame::OnSave, this, wxID_SAVE); - Bind(wxEVT_MENU, &ConsoleLogFrame::OnClear, this, wxID_CLEAR); -#ifdef PCSX2_DEVBUILD - Bind(wxEVT_MENU, &ConsoleLogFrame::OnLogSettings, this, MenuId_Log_Settings); -#endif - - Bind(wxEVT_MENU, &ConsoleLogFrame::OnFontSize, this, MenuId_FontSize_Small, MenuId_FontSize_Huge); - Bind(wxEVT_MENU, &ConsoleLogFrame::OnToggleTheme, this, MenuId_ColorScheme_Light, MenuId_ColorScheme_Dark); - - Bind(wxEVT_MENU, &ConsoleLogFrame::OnAutoDock, this, MenuId_AutoDock); - - Bind(wxEVT_MENU, &ConsoleLogFrame::OnToggleSource, this, MenuId_LogSource_Devel); - Bind(wxEVT_MENU, &ConsoleLogFrame::OnToggleCDVDInfo, this, MenuId_LogSource_CDVD_Info); - Bind(wxEVT_MENU, &ConsoleLogFrame::OnEnableAllLogging, this, MenuId_LogSource_EnableAll); - Bind(wxEVT_MENU, &ConsoleLogFrame::OnDisableAllLogging, this, MenuId_LogSource_DisableAll); - Bind(wxEVT_MENU, &ConsoleLogFrame::OnSetDefaultLogging, this, MenuId_LogSource_SetDefault); - - Bind(wxEVT_CLOSE_WINDOW, &ConsoleLogFrame::OnCloseWindow, this); - Bind(wxEVT_MOVE, &ConsoleLogFrame::OnMoveAround, this); - Bind(wxEVT_SIZE, &ConsoleLogFrame::OnResize, this); - Bind(wxEVT_ACTIVATE, &ConsoleLogFrame::OnActivate, this); - - Bind(pxEvt_SetTitleText, &ConsoleLogFrame::OnSetTitle, this); - Bind(pxEvt_FlushQueue, &ConsoleLogFrame::OnFlushEvent, this); - - Bind(wxEVT_TIMER, &ConsoleLogFrame::OnFlushUnlockerTimer, this, m_timer_FlushUnlocker.GetId()); - - if( m_threadlogger != NULL ) - m_threadlogger->Start(); - - OnLoggingChanged(); - - if (0==m_conf.Theme.CmpNoCase(L"Dark")) - { - pMenuBar->Check(MenuId_ColorScheme_Dark, true); - } - else //if ((0==m_conf.Theme.CmpNoCase("Default")) || (0==m_conf.Theme.CmpNoCase("Light"))) - { - pMenuBar->Check(MenuId_ColorScheme_Light, true); - } -} - -ConsoleLogFrame::~ConsoleLogFrame() -{ - ScopedLogLock locker; - wxGetApp().OnProgramLogClosed( GetId() ); -} - -void ConsoleLogFrame::OnEnableAllLogging(wxCommandEvent& evt) -{ - uint srcnt = std::size(ConLogSources); - for (uint i=0; iEnabled = true; - } - DevConWriterEnabled = true; - // Safe to change - it's read only in the core thread and only affects log output. - const_cast(EmuConfig).CdvdVerboseReads = g_Conf->EmuOptions.CdvdVerboseReads = true; - - OnLoggingChanged(); - evt.Skip(); -} - -void ConsoleLogFrame::OnDisableAllLogging(wxCommandEvent& evt) -{ - uint srcnt = std::size(ConLogSources); - for (uint i=0; iEnabled = false; - } - DevConWriterEnabled = false; - // Safe to change - it's read only in the core thread and only affects log output. - const_cast(EmuConfig).CdvdVerboseReads = g_Conf->EmuOptions.CdvdVerboseReads = false; - - OnLoggingChanged(); - evt.Skip(); -} - -void ConsoleLogFrame::OnSetDefaultLogging(wxCommandEvent& evt) -{ - uint srcnt = std::size(ConLogSources); - for (uint i = 0; iEnabled = ConLogDefaults[i]; - } - DevConWriterEnabled = false; - // Safe to change - it's read only in the core thread and only affects log output. - const_cast(EmuConfig).CdvdVerboseReads = g_Conf->EmuOptions.CdvdVerboseReads = false; - - OnLoggingChanged(); - evt.Skip(); -} - -void ConsoleLogFrame::OnLoggingChanged() -{ - if (!GetMenuBar()) return; - - if( wxMenuItem* item = GetMenuBar()->FindItem(MenuId_LogSource_Devel) ) - item->Check( DevConWriterEnabled ); - - if( wxMenuItem* item = GetMenuBar()->FindItem(MenuId_LogSource_CDVD_Info) ) - item->Check( g_Conf->EmuOptions.CdvdVerboseReads ); - - uint srcnt = std::size(ConLogSources); - for (uint i=0; iCheck( MenuId_LogSource_Start+i, log->IsActive() ); - } - GetMenuBar()->Enable( MenuId_LogSource_Start + MenuId_LogSources_Offset_recordingConsole, g_Conf->EmuOptions.EnableRecordingTools); - GetMenuBar()->Enable( MenuId_LogSource_Start + MenuId_LogSources_Offset_controlInfo, g_Conf->EmuOptions.EnableRecordingTools); - } -} - -void ConsoleLogFrame::UpdateLogList() -{ - OnLoggingChanged(); -} - -// Implementation note: Calls SetColor and Write( text ). Override those virtuals -// and this one will magically follow suite. :) -bool ConsoleLogFrame::Write( ConsoleColors color, const wxString& text ) -{ - pthread_testcancel(); - - ScopedLock lock( m_mtx_Queue ); - - if( m_QueueColorSection.GetLength() == 0 ) - { - pxAssertMsg( m_CurQueuePos == 0, "Queue's character position didn't get reset in sync with it's ColorSection table." ); - } - - if( (m_QueueColorSection.GetLength() == 0) || ((color != Color_Current) && (m_QueueColorSection.GetLast().color != color)) ) - { - ++m_CurQueuePos; // Don't overwrite the NULL; - m_QueueColorSection.Add( ColorSection(color, m_CurQueuePos) ); - } - - int endpos = m_CurQueuePos + text.Length(); - m_QueueBuffer.MakeRoomFor( endpos + 1 ); // and the null!! - memcpy( &m_QueueBuffer[m_CurQueuePos], text.wc_str(), sizeof(wxChar) * text.Length() ); - m_CurQueuePos = endpos; - - // this NULL may be overwritten if the next message sent doesn't perform a color change. - m_QueueBuffer[m_CurQueuePos] = 0; - - // Idle events don't always pass (wx blocks them when moving windows or using menus, for - // example). So let's hackfix it so that an alternate message is posted if the queue is - // "piling up." - - if( !m_pendingFlushMsg ) - { - m_pendingFlushMsg = true; - - // wxWidgets may have aggressive locks on event processing, so best to release - // our own mutex lest wx get hung for an extended period of time and cause all - // of our own stuff to get sluggish. - lock.Release(); - - wxCommandEvent evt( pxEvt_FlushQueue ); - evt.SetInt( 0 ); - if( wxThread::IsMain() ) - { - OnFlushEvent( evt ); - return false; - } - else - GetEventHandler()->AddPendingEvent( evt ); - - lock.Acquire(); - } - - if( !wxThread::IsMain() ) - { - // Too many color changes causes huge slowdowns when decorating the rich textview, so - // include a secondary check to avoid having a colorful log spam killing gui responsiveness. - - if( m_CurQueuePos > 0x100000 || m_QueueColorSection.GetLength() > 256 ) - { - ++m_WaitingThreadsForFlush; - lock.Release(); - - // Note: if the queue flushes, we need to return TRUE, so that our thread sleeps - // until the main thread has had a chance to repaint the console window contents. - // [TODO] : It'd be a lot better if the console window repaint released the lock - // once its task were complete, but thats been problematic, so for now this hack is - // what we get. - if( m_sem_QueueFlushed.Wait( wxTimeSpan( 0,0,0,250 ) ) ) return true; - - // If we're here it means QueueFlush wait timed out, so remove us from the waiting - // threads count. This way no thread permanently deadlocks against the console - // logger. They just run quite slow, but should remain responsive to user input. - lock.Acquire(); - if( m_WaitingThreadsForFlush != 0 ) --m_WaitingThreadsForFlush; - } - } - - return false; -} - -bool ConsoleLogFrame::Newline() -{ - return Write( Color_Current, L"\n" ); -} - -// ================================================================================= -// Section : Event Handlers -// * Misc Window Events (Move, Resize,etc) -// * Menu Events -// * Logging Events -// ================================================================================= - -void ConsoleLogFrame::OnMoveAround( wxMoveEvent& evt ) -{ - if( IsBeingDeleted() || !IsVisible() || IsIconized() ) return; - - // Docking check! If the window position is within some amount - // of the main window, enable docking. - - if( wxWindow* main = GetParent() ) - { - wxPoint topright( main->GetRect().GetTopRight() ); - wxRect snapzone( topright - wxSize( 8,8 ), wxSize( 16,16 ) ); - - m_conf.AutoDock = snapzone.Contains( GetPosition() ); - //Console.WriteLn( "DockCheck: %d", g_Conf->ConLogBox.AutoDock ); - if( m_conf.AutoDock ) - { - SetPosition( topright + wxSize( 1,0 ) ); - m_conf.AutoDock = true; - } - } - if (!IsMaximized()) - m_conf.DisplayPosition = GetPosition(); - evt.Skip(); -} - -void ConsoleLogFrame::OnResize( wxSizeEvent& evt ) -{ - if (!IsMaximized()) - m_conf.DisplaySize = GetSize(); - evt.Skip(); -} - -// ---------------------------------------------------------------------------- -// OnFocus / OnActivate : Special implementation to "connect" the console log window -// with the main frame window. When one is clicked, the other is assured to be brought -// to the foreground with it. (Currently only MSW only, as wxWidgets appears to have no -// equivalent to this). We don't bother with OnFocus here because it doesn't propagate -// up the window hierarchy anyway, so it always gets swallowed by the text control. -// But no matter: the console doesn't have the same problem as the Main Window of missing -// the initial activation event. - -/*void ConsoleLogFrame::OnFocus( wxFocusEvent& evt ) -{ - if( MainEmuFrame* mainframe = GetMainFramePtr() ) - MSW_SetWindowAfter( mainframe->GetHandle(), GetHandle() ); - - evt.Skip(); -}*/ - -void ConsoleLogFrame::OnActivate( wxActivateEvent& evt ) -{ - if( MainEmuFrame* mainframe = GetMainFramePtr() ) - MSW_SetWindowAfter( mainframe->GetHandle(), GetHandle() ); - - evt.Skip(); -} -// ---------------------------------------------------------------------------- - -void ConsoleLogFrame::OnCloseWindow(wxCloseEvent& event) -{ - if( event.CanVeto() ) - { - // instead of closing just hide the window to be able to Show() it later - Show( false ); - - // In the nogui case there might not be a Main frame window. - if (MainEmuFrame* mainframe = GetMainFramePtr()) - mainframe->OnLogBoxHidden(); - } - else - { - // This is sent when the app is exiting typically, so do a full close. - m_threadlogger = NULL; - wxGetApp().OnProgramLogClosed( GetId() ); - event.Skip(); - } -} - -void ConsoleLogFrame::OnOpen(wxCommandEvent& WXUNUSED(event)) -{ - Show(true); -} - -void ConsoleLogFrame::OnClose( wxCommandEvent& event ) -{ - Close( false ); -} - -void ConsoleLogFrame::OnSave(wxCommandEvent& WXUNUSED(event)) -{ - wxString filename; - wxFile file; - bool rc = OpenLogFile( file, filename, this ); - if ( !rc ) - { - // canceled - return; - } - - // retrieve text and save it - // ------------------------- - int nLines = m_TextCtrl.GetNumberOfLines(); - for ( int nLine = 0; nLine < nLines; nLine++ ) - { - if( !file.Write(m_TextCtrl.GetLineText(nLine) + wxTextFile::GetEOL()) ) - { - wxLogError( L"Can't save log contents to file." ); - return; - } - } -} - -void ConsoleLogFrame::OnClear(wxCommandEvent& WXUNUSED(event)) -{ - m_TextCtrl.Clear(); -} - -void ConsoleLogFrame::OnLogSettings(wxCommandEvent& WXUNUSED(event)) -{ - AppOpenDialog( this ); -} - -void ConsoleLogFrame::OnToggleCDVDInfo( wxCommandEvent& evt ) -{ - evt.Skip(); - - if (!GetMenuBar()) return; - - if (evt.GetId() == MenuId_LogSource_CDVD_Info) - { - if( wxMenuItem* item = GetMenuBar()->FindItem(evt.GetId()) ) - { - g_Conf->EmuOptions.CdvdVerboseReads = item->IsChecked(); - const_cast(EmuConfig).CdvdVerboseReads = g_Conf->EmuOptions.CdvdVerboseReads; // read-only in core thread, so it's safe to modify. - } - } -} - -void ConsoleLogFrame::OnToggleSource( wxCommandEvent& evt ) -{ - evt.Skip(); - - if (!GetMenuBar()) return; - - if (evt.GetId() == MenuId_LogSource_Devel) - { - if( wxMenuItem* item = GetMenuBar()->FindItem(evt.GetId()) ) - DevConWriterEnabled = item->IsChecked(); - - return; - } - - uint srcid = evt.GetId() - MenuId_LogSource_Start; - - if (!pxAssertDev( std::size(ConLogSources) > srcid, "Invalid source log index (out of bounds)" )) return; - if (!pxAssertDev( ConLogSources[srcid] != NULL, "Invalid source log index (NULL pointer [separator])" )) return; - - if( wxMenuItem* item = GetMenuBar()->FindItem(evt.GetId()) ) - { - pxAssertDev( item->IsCheckable(), "Uncheckable log source menu item? Seems fishy!" ); - ConLogSources[srcid]->Enabled = item->IsChecked(); - } -} - -void ConsoleLogFrame::OnToggleTheme(wxCommandEvent& evt) -{ - evt.Skip(); - const wxChar* newTheme = L"Default"; - switch (evt.GetId()) - { - case MenuId_ColorScheme_Dark: - newTheme = L"Dark"; - m_ColorTable.SetColorScheme_Dark(); - m_TextCtrl.SetBackgroundColour(darkThemeBgColor); - m_TextCtrl.SetDefaultStyle(wxTextAttr(wxNullColour, darkThemeBgColor)); - m_TextCtrl.Clear(); - break; - case MenuId_ColorScheme_Light: - default: - newTheme = L"Default"; - m_ColorTable.SetColorScheme_Light(); - m_TextCtrl.SetBackgroundColour(lightThemeBgColor); - m_TextCtrl.SetDefaultStyle(wxTextAttr(wxNullColour, lightThemeBgColor)); - m_TextCtrl.Clear(); - break; - } - - if (m_conf.Theme.CmpNoCase(newTheme) == 0) - return; - m_conf.Theme = newTheme; - m_ColorTable.SetFont(m_conf.FontSize); -} - -void ConsoleLogFrame::OnFontSize( wxCommandEvent& evt ) -{ - evt.Skip(); - - int ptsize = 8; - switch( evt.GetId() ) - { - case MenuId_FontSize_Small: ptsize = 7; break; - case MenuId_FontSize_Normal: ptsize = 8; break; - case MenuId_FontSize_Large: ptsize = 10; break; - case MenuId_FontSize_Huge: ptsize = 12; break; - } - - if( ptsize == m_conf.FontSize ) return; - - m_conf.FontSize = ptsize; - m_ColorTable.SetFont( ptsize ); - m_TextCtrl.SetDefaultStyle( m_ColorTable[Color_White] ); - - // TODO: Process the attributes of each character and upgrade the font size, - // while still retaining color and bold settings... (might be slow but then - // it hardly matters being a once-in-a-bluemoon action). -} - -void ConsoleLogFrame::OnAutoDock(wxCommandEvent& evt) -{ - if (auto menuBar = GetMenuBar()) - m_conf.AutoDock = menuBar->IsChecked(MenuId_AutoDock); - evt.Skip(); -} - -// ---------------------------------------------------------------------------- -// Logging Events (typically received from Console class interfaces) -// ---------------------------------------------------------------------------- - -void ConsoleLogFrame::OnSetTitle( wxCommandEvent& event ) -{ - SetTitle( event.GetString() ); -} - -void ConsoleLogFrame::DoFlushEvent( bool isPending ) -{ - // recursion guard needed due to Mutex lock/acquire code below, which can end up yielding - // to the gui and attempting to process more messages (which in turn would result in re- - // entering this handler). - - static int recursion_counter = 0; - RecursionGuard recguard( recursion_counter ); - if( recguard.IsReentrant() ) return; - - ScopedLock locker( m_mtx_Queue ); - - if( m_CurQueuePos != 0 ) - { - DoFlushQueue(); - } - - // Implementation note: I tried desperately to move this into wxEVT_IDLE, on the theory that - // we don't actually want to wake up pending threads until after the GUI's finished all its - // paperwork. But wxEVT_IDLE doesn't work when you click menus or the title bar of a window, - // making it pretty well annoyingly useless for just about anything. >_< - - if( m_WaitingThreadsForFlush > 0 ) - { - do { - m_sem_QueueFlushed.Post(); - } while( --m_WaitingThreadsForFlush > 0 ); - - int count = m_sem_QueueFlushed.Count(); - while( count < 0 ) { - m_sem_QueueFlushed.Post(); - count = m_sem_QueueFlushed.Count(); - } - } - - m_pendingFlushMsg = isPending; -} - -void ConsoleLogFrame::OnFlushUnlockerTimer( wxTimerEvent& ) -{ - m_FlushRefreshLocked = false; - DoFlushEvent( false ); -} - -void ConsoleLogFrame::OnFlushEvent( wxCommandEvent& ) -{ - if( m_FlushRefreshLocked ) return; - - DoFlushEvent( true ); - - m_FlushRefreshLocked = true; - m_timer_FlushUnlocker.Start( 100, true ); -} - -void ConsoleLogFrame::DoFlushQueue() -{ - int len = m_QueueColorSection.GetLength(); - pxAssert( len != 0 ); - - // Note, freezing/thawing actually seems to cause more overhead than it solves. - // It might be useful if we're posting like dozens of messages, but in our case - // we only post 1-4 typically, so better to leave things enabled. - //m_TextCtrl.Freeze(); - - // Manual InsertionPoint tracking avoids a lot of overhead in SetInsertionPointEnd() - wxTextPos insertPoint = m_TextCtrl.GetLastPosition(); - - // cap at 512k for now... - // fixme - 512k runs well on win32 but appears to be very sluggish on linux (but that could - // be a result of my using Xming + CoLinux). Might need platform dependent defaults here. --air - - static const int BufferSize = 0x80000; - if( (insertPoint + m_CurQueuePos) > BufferSize ) - { - int toKeep = BufferSize - m_CurQueuePos; - if( toKeep <= 10 ) - { - m_TextCtrl.Clear(); - insertPoint = 0; - } - else - { - int toRemove = BufferSize - toKeep; - if( toRemove < BufferSize / 4 ) toRemove = BufferSize; - m_TextCtrl.Remove( 0, toRemove ); - insertPoint -= toRemove; - } - } - - m_TextCtrl.SetInsertionPoint( insertPoint ); - - // fixme : Writing a lot of colored logs to the console can be quite slow when "spamming" - // is happening, due to the overhead of SetDefaultStyle and WriteText calls. I'm not sure - // if there's a better way to go about this? Using Freeze/Thaw helps a little bit, but it's - // still magnitudes slower than dumping a straight run. --air - - if( len > 64 ) m_TextCtrl.Freeze(); - - for( int i=0; i 64 ) m_TextCtrl.Thaw(); - - m_QueueColorSection.Clear(); - m_CurQueuePos = 0; -} - -ConsoleLogFrame* Pcsx2App::GetProgramLog() -{ - return (ConsoleLogFrame*) wxWindow::FindWindowById( m_id_ProgramLogBox ); -} - -const ConsoleLogFrame* Pcsx2App::GetProgramLog() const -{ - return (const ConsoleLogFrame*) wxWindow::FindWindowById( m_id_ProgramLogBox ); -} - -void Pcsx2App::ProgramLog_PostEvent( wxEvent& evt ) -{ - if( ConsoleLogFrame* proglog = GetProgramLog() ) - proglog->GetEventHandler()->AddPendingEvent( evt ); -} - -// -------------------------------------------------------------------------------------- -// ConsoleImpl_ToFile -// -------------------------------------------------------------------------------------- - -static void ConsoleToFile_Newline() -{ -#if defined(__POSIX__) - if ((g_Conf) && (g_Conf->EmuOptions.ConsoleToStdio)) ConsoleWriter_Stdout.Newline(); -#endif - -#if defined(__POSIX__) - fputc( '\n', emuLog ); -#else - fputs( "\r\n", emuLog ); -#endif -} - -static void ConsoleToFile_DoWrite( const char* fmt ) -{ -#if defined(__POSIX__) - if ((g_Conf) && (g_Conf->EmuOptions.ConsoleToStdio)) ConsoleWriter_Stdout.WriteRaw(fmt); -#endif - - px_fputs( emuLog, fmt ); -} - -static void ConsoleToFile_DoWriteLn( const char* fmt ) -{ - ConsoleToFile_DoWrite( fmt ); - ConsoleToFile_Newline(); - - if (emuLog != NULL) fflush( emuLog ); -} - -static void ConsoleToFile_SetTitle( const char* title ) -{ - ConsoleWriter_Stdout.SetTitle(title); -} - -static void ConsoleToFile_DoSetColor( ConsoleColors color ) -{ - ConsoleWriter_Stdout.DoSetColor(color); -} - -extern const IConsoleWriter ConsoleWriter_File; -const IConsoleWriter ConsoleWriter_File = -{ - ConsoleToFile_DoWrite, - ConsoleToFile_DoWriteLn, - ConsoleToFile_DoSetColor, - - ConsoleToFile_DoWrite, - ConsoleToFile_Newline, - ConsoleToFile_SetTitle, - - 0 -}; - -Mutex& Pcsx2App::GetProgramLogLock() -{ - return m_mtx_ProgramLog; -} - -// -------------------------------------------------------------------------------------- -// ConsoleToWindow Implementations -// -------------------------------------------------------------------------------------- -template< const IConsoleWriter& secondary > -static void ConsoleToWindow_SetTitle( const char* title ) -{ - secondary.SetTitle(title); - wxCommandEvent evt( pxEvt_SetTitleText ); - evt.SetString( wxString::FromUTF8(title) ); - wxGetApp().ProgramLog_PostEvent( evt ); -} - -template< const IConsoleWriter& secondary > -static void ConsoleToWindow_DoSetColor( ConsoleColors color ) -{ - secondary.DoSetColor(color); -} - -template< const IConsoleWriter& secondary > -static void ConsoleToWindow_Newline() -{ - secondary.Newline(); - - ScopedLogLock locker; - bool needsSleep = locker.WindowPtr && locker.WindowPtr->Newline(); - locker.Release(); - if( needsSleep ) wxGetApp().Ping(); -} - -template< const IConsoleWriter& secondary > -static void ConsoleToWindow_DoWrite( const char* fmt ) -{ - if( secondary.WriteRaw != NULL ) - secondary.WriteRaw( fmt ); - - ScopedLogLock locker; - bool needsSleep = locker.WindowPtr && locker.WindowPtr->Write( Console.GetColor(), wxString::FromUTF8(fmt) ); - locker.Release(); - if( needsSleep ) wxGetApp().Ping(); -} - -template< const IConsoleWriter& secondary > -static void ConsoleToWindow_DoWriteLn( const char* fmt ) -{ - if( secondary.DoWriteLn != NULL ) - secondary.DoWriteLn( fmt ); - - ScopedLogLock locker; - bool needsSleep = locker.WindowPtr && locker.WindowPtr->Write( Console.GetColor(), wxString::FromUTF8(fmt) + L'\n' ); - locker.Release(); - if( needsSleep ) wxGetApp().Ping(); -} - -typedef void DoWriteFn(const wxString&); - -static const IConsoleWriter ConsoleWriter_Window = -{ - ConsoleToWindow_DoWrite, - ConsoleToWindow_DoWriteLn, - ConsoleToWindow_DoSetColor, - - ConsoleToWindow_DoWrite, - ConsoleToWindow_Newline, - ConsoleToWindow_SetTitle, - - 0 -}; - -static const IConsoleWriter ConsoleWriter_WindowAndFile = -{ - ConsoleToWindow_DoWrite, - ConsoleToWindow_DoWriteLn, - ConsoleToWindow_DoSetColor, - - ConsoleToWindow_DoWrite, - ConsoleToWindow_Newline, - ConsoleToWindow_SetTitle, - - 0 -}; - -void Pcsx2App::EnableAllLogging() -{ - AffinityAssert_AllowFrom_MainUI(); - - const bool logBoxOpen = (m_ptr_ProgramLog != NULL); - const IConsoleWriter* newHandler = NULL; - - if( emuLog ) - { - if( !m_StdoutRedirHandle ) m_StdoutRedirHandle = std::unique_ptr(NewPipeRedir(stdout)); - if( !m_StderrRedirHandle ) m_StderrRedirHandle = std::unique_ptr(NewPipeRedir(stderr)); - newHandler = logBoxOpen ? (IConsoleWriter*)&ConsoleWriter_WindowAndFile : (IConsoleWriter*)&ConsoleWriter_File; - } - else - { - if( logBoxOpen ) - { - if (!m_StdoutRedirHandle) m_StdoutRedirHandle = std::unique_ptr(NewPipeRedir(stdout)); - if (!m_StderrRedirHandle) m_StderrRedirHandle = std::unique_ptr(NewPipeRedir(stderr)); - newHandler = &ConsoleWriter_Window; - } - else - newHandler = &ConsoleWriter_Stdout; - } - Console_SetActiveHandler( *newHandler ); -} - -// Used to disable the emuLog disk logger, typically used when disabling or re-initializing the -// emuLog file handle. Call SetConsoleLogging to re-enable the disk logger when finished. -void Pcsx2App::DisableDiskLogging() const -{ - AffinityAssert_AllowFrom_MainUI(); - - const bool logBoxOpen = (GetProgramLog() != NULL); - Console_SetActiveHandler( logBoxOpen ? (IConsoleWriter&)ConsoleWriter_Window : (IConsoleWriter&)ConsoleWriter_Stdout ); - - // Semi-hack: It's possible, however very unlikely, that a secondary thread could attempt - // to write to the logfile just before we disable logging, and would thus have a pending write - // operation to emuLog file handle at the same time we're trying to re-initialize it. The CRT - // has some guards of its own, and PCSX2 itself typically suspends the "log happy" threads - // when changing settings, so the chance for problems is low. We minimize it further here - // by sleeping off 5ms, which should allow any pending log-to-disk events to finish up. - // - // (the most correct solution would be a mutex lock in the Disk logger itself, but for now I - // am going to try and keep the logger lock-free and use this semi-hack instead). - - Threading::Sleep( 5 ); -} - -void Pcsx2App::DisableWindowLogging() const -{ - AffinityAssert_AllowFrom_MainUI(); - Console_SetActiveHandler( (emuLog!=NULL) ? (IConsoleWriter&)ConsoleWriter_File : (IConsoleWriter&)ConsoleWriter_Stdout ); -} - -void OSDlog(ConsoleColors color, bool console, const std::string& str) -{ - Host::AddOSDMessage(str, 15.0f); - - if (console) - Console.WriteLn(color, str.c_str()); -} diff --git a/pcsx2/gui/ConsoleLogger.h b/pcsx2/gui/ConsoleLogger.h deleted file mode 100644 index 02a0cfa008..0000000000 --- a/pcsx2/gui/ConsoleLogger.h +++ /dev/null @@ -1,247 +0,0 @@ -/* PCSX2 - PS2 Emulator for PCs - * Copyright (C) 2002-2010 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 . - */ - -#pragma once - -#include "App.h" -#include -#include -#include - -static const bool EnableThreadedLoggingTest = false; //true; - -class LogWriteEvent; - -// -------------------------------------------------------------------------------------- -// PipeRedirectionBase -// -------------------------------------------------------------------------------------- -// Implementations for this class are found in Win/Lnx specific modules. Class creation -// should be done using NewPipeRedir() only (hence the protected constructor in this class). -// -class PipeRedirectionBase -{ - DeclareNoncopyableObject( PipeRedirectionBase ); - -public: - virtual ~PipeRedirectionBase() =0; // abstract destructor, forces abstract class behavior - -protected: - PipeRedirectionBase() {} -}; - -extern PipeRedirectionBase* NewPipeRedir( FILE* stdstream ); - -// -------------------------------------------------------------------------------------- -// pxLogConsole -// -------------------------------------------------------------------------------------- -// This is a custom logging facility that pipes wxLog messages to our very own console -// log window. Useful for catching and redirecting wx's internal logs (although like -// 3/4ths of them are worthless and we would probably rather ignore them anyway). -// -class pxLogConsole : public wxLog -{ -public: - pxLogConsole() {} - -protected: - virtual void DoLogRecord(wxLogLevel level, const wxString &message, const wxLogRecordInfo &info); -}; - - -// -------------------------------------------------------------------------------------- -// ConsoleThreadTest -- useful class for unit testing the thread safety and general performance -// of the console logger. -// -------------------------------------------------------------------------------------- - -class ConsoleTestThread : public Threading::pxThread -{ - typedef pxThread _parent; - -protected: - std::atomic m_done; - void ExecuteTaskInThread(); - -public: - ConsoleTestThread() : - m_done( false ) - { - } - - ~ConsoleTestThread() - { - m_done = true; - } -}; - -// -------------------------------------------------------------------------------------- -// ConsoleLogFrame -- Because the one built in wx is poop. -// -------------------------------------------------------------------------------------- -class ConsoleLogFrame : public wxFrame -{ - DeclareNoncopyableObject(ConsoleLogFrame); - -public: - typedef AppConfig::ConsoleLogOptions ConLogConfig; - -protected: - class ColorArray - { - DeclareNoncopyableObject(ColorArray); - - protected: - std::array m_table; - - public: - virtual ~ColorArray() = default; - ColorArray( int fontsize=8 ); - - void SetFont( const wxFont& font ); - void SetFont( int fontsize ); - u32 GetRGBA( const ConsoleColors color ); - - const wxTextAttr& operator[]( ConsoleColors coloridx ) const - { - return m_table[(int)coloridx]; - } - - void SetColorScheme_Dark(); - void SetColorScheme_Light(); - }; - - class ColorSection - { - public: - ConsoleColors color; - int startpoint; - - ColorSection() : color(Color_Default), startpoint(0) {} - ColorSection( ConsoleColors _color, int msgptr ) : color(_color), startpoint(msgptr) { } - }; - -private: - wxColor lightThemeBgColor = wxColor(230, 235, 242); - wxColor darkThemeBgColor = wxColor(38, 41, 48); - -protected: - ConLogConfig& m_conf; - wxTextCtrl& m_TextCtrl; - wxTimer m_timer_FlushLimiter; - wxTimer m_timer_FlushUnlocker; - ColorArray m_ColorTable; - - int m_flushevent_counter; - bool m_FlushRefreshLocked; - - // ---------------------------------------------------------------------------- - // Queue State Management Vars - // ---------------------------------------------------------------------------- - - // Boolean indicating if a flush message is already in the Main message queue. Used - // to prevent spamming the main thread with redundant messages. - std::atomic m_pendingFlushMsg; - - // This is a counter of the number of threads waiting for the Queue to flush. - std::atomic m_WaitingThreadsForFlush; - - // Indicates to the main thread if a child thread is actively writing to the log. If - // true the main thread will sleep briefly to allow the child a chance to accumulate - // more messages (helps avoid rapid successive flushes on high volume logging). - std::atomic m_ThreadedLogInQueue; - - // Used by threads waiting on the queue to flush. - Semaphore m_sem_QueueFlushed; - - // Lock object for accessing or modifying the following three vars: - // m_QueueBuffer, m_QueueColorSelection, m_CurQueuePos - Mutex m_mtx_Queue; - - // Describes a series of colored text sections in the m_QueueBuffer. - SafeList m_QueueColorSection; - - // Series of Null-terminated strings, each one has a corresponding entry in - // m_QueueColorSelection. - SafeArray m_QueueBuffer; - - // Current write position into the m_QueueBuffer; - int m_CurQueuePos; - - // Threaded log spammer, useful for testing console logging performance. - // (alternatively you can enable Disasm logging in any recompiler and achieve - // a similar effect) - std::unique_ptr m_threadlogger; - -public: - // ctor & dtor - ConsoleLogFrame( MainEmuFrame *pParent, const wxString& szTitle, ConLogConfig& options ); - virtual ~ConsoleLogFrame(); - - // Retrieves the current configuration options settings for this box. - // (settings change if the user moves the window or changes the font size) - const ConLogConfig& GetConfig() const { return m_conf; } - u32 GetRGBA( const ConsoleColors color ) { return m_ColorTable.GetRGBA( color ); } - - bool Write( ConsoleColors color, const wxString& text ); - bool Newline(); - - void UpdateLogList(); - -protected: - // menu callbacks - void OnOpen (wxCommandEvent& event); - void OnClose(wxCommandEvent& event); - void OnSave (wxCommandEvent& event); - void OnClear(wxCommandEvent& event); - void OnLogSettings(wxCommandEvent& event); - - void OnEnableAllLogging(wxCommandEvent& event); - void OnDisableAllLogging(wxCommandEvent& event); - void OnSetDefaultLogging(wxCommandEvent& event); - - void OnToggleTheme(wxCommandEvent& event); - void OnFontSize(wxCommandEvent& event); - void OnAutoDock(wxCommandEvent& event); - void OnToggleSource(wxCommandEvent& event); - void OnToggleCDVDInfo(wxCommandEvent& event); - - virtual void OnCloseWindow(wxCloseEvent& event); - - void OnSetTitle( wxCommandEvent& event ); - void OnFlushUnlockerTimer( wxTimerEvent& evt ); - void OnFlushEvent( wxCommandEvent& event ); - - void DoFlushQueue(); - void DoFlushEvent( bool isPending ); - - void OnMoveAround( wxMoveEvent& evt ); - void OnResize( wxSizeEvent& evt ); - void OnActivate( wxActivateEvent& evt ); - - void OnLoggingChanged(); -}; - -void OSDlog(ConsoleColors color, bool console, const std::string& str); - -template -void OSDlog(ConsoleColors color, bool console, const std::string& format, Args ... args) { - if (!console) return; - - size_t size = snprintf( nullptr, 0, format.c_str(), args ... ) + 1; // Extra space for '\0' - std::vector buf(size); - snprintf( buf.data(), size, format.c_str(), args ... ); - - OSDlog(color, console, buf.data()); -} - - diff --git a/pcsx2/gui/Debugger/BreakpointWindow.cpp b/pcsx2/gui/Debugger/BreakpointWindow.cpp deleted file mode 100644 index 729643ce71..0000000000 --- a/pcsx2/gui/Debugger/BreakpointWindow.cpp +++ /dev/null @@ -1,347 +0,0 @@ -/* PCSX2 - PS2 Emulator for PCs - * Copyright (C) 2002-2014 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 . - */ - -#include "PrecompiledHeader.h" -#include "BreakpointWindow.h" - - -wxBEGIN_EVENT_TABLE(BreakpointWindow, wxDialog) - EVT_RADIOBUTTON(wxID_ANY, BreakpointWindow::onRadioChange) - EVT_BUTTON(wxID_OK, BreakpointWindow::onButtonOk) -wxEND_EVENT_TABLE() - - -BreakpointWindow::BreakpointWindow( wxWindow* parent, DebugInterface* _cpu ) - : wxDialog(parent,wxID_ANY,L"Breakpoint"), cpu(_cpu) -{ - wxBoxSizer* topLevelSizer = new wxBoxSizer(wxVERTICAL); - - wxBoxSizer* upperPart = new wxBoxSizer(wxHORIZONTAL); - - wxFlexGridSizer* leftSizer = new wxFlexGridSizer(2,2,7,7); - - // address - wxStaticText* addressText = new wxStaticText(this,wxID_ANY,L"Address"); - editAddress = new wxTextCtrl(this,wxID_ANY,L""); - - leftSizer->Add(addressText,0,wxALIGN_CENTER_VERTICAL|wxBOTTOM,1); - leftSizer->Add(editAddress,1); - - // size - wxStaticText* sizeText = new wxStaticText(this,wxID_ANY,L"Size"); - editSize = new wxTextCtrl(this,wxID_ANY,L""); - leftSizer->Add(sizeText,0,wxALIGN_CENTER_VERTICAL|wxBOTTOM,1); - leftSizer->Add(editSize,1); - - // right part - wxFlexGridSizer* rightSizer = new wxFlexGridSizer(3,2,7,7); - - radioMemory = new wxRadioButton(this,wxID_ANY,L"Memory",wxDefaultPosition,wxDefaultSize,wxRB_GROUP); - radioExecute = new wxRadioButton(this,wxID_ANY,L"Execute"); - rightSizer->Add(radioMemory,1); - rightSizer->Add(radioExecute,1,wxLEFT,8); - - checkRead = new wxCheckBox(this,wxID_ANY,L"Read"); - checkWrite = new wxCheckBox(this,wxID_ANY,L"Write"); - checkOnChange = new wxCheckBox(this,wxID_ANY,L"On change"); - rightSizer->Add(checkRead,1); - rightSizer->Add(new wxStaticText(this,wxID_ANY,L""),1); - rightSizer->Add(checkWrite,1); - rightSizer->Add(checkOnChange,1,wxLEFT,8); - - upperPart->Add(leftSizer,1,wxLEFT|wxTOP|wxRIGHT,8); - upperPart->Add(rightSizer,1,wxLEFT|wxTOP|wxRIGHT,8); - - // bottom part - wxBoxSizer* conditionSizer = new wxBoxSizer(wxHORIZONTAL); - wxStaticText* conditionText = new wxStaticText(this,wxID_ANY,L"Condition"); - editCondition = new wxTextCtrl(this,wxID_ANY,L""); - conditionSizer->Add(conditionText,0,wxALIGN_CENTER_VERTICAL|wxBOTTOM,1); - conditionSizer->AddSpacer(7); - conditionSizer->Add(editCondition,1,wxEXPAND); - - wxBoxSizer* bottomRowSizer = new wxBoxSizer(wxHORIZONTAL); - checkEnabled = new wxCheckBox(this,wxID_ANY,L"Enabled"); - checkLog = new wxCheckBox(this,wxID_ANY,L"Log"); - buttonOk = new wxButton(this,wxID_OK,L"OK"); - buttonCancel = new wxButton(this,wxID_CANCEL,L"Cancel"); - bottomRowSizer->AddStretchSpacer(); - bottomRowSizer->Add(checkEnabled,0,wxALIGN_CENTER_VERTICAL|wxBOTTOM,1); - bottomRowSizer->AddSpacer(8); - bottomRowSizer->Add(checkLog,0,wxALIGN_CENTER_VERTICAL|wxBOTTOM,1); - bottomRowSizer->AddSpacer(8); - bottomRowSizer->Add(buttonOk,1); - bottomRowSizer->AddSpacer(4); - bottomRowSizer->Add(buttonCancel,1); - - // align labels - int minWidth = std::max(addressText->GetSize().x,sizeText->GetSize().x); - minWidth = std::max(minWidth,conditionText->GetSize().x); - addressText->SetMinSize(wxSize(minWidth,addressText->GetSize().y)); - sizeText->SetMinSize(wxSize(minWidth,sizeText->GetSize().y)); - conditionText->SetMinSize(wxSize(minWidth,conditionText->GetSize().y)); - - // set tab order - radioMemory->MoveAfterInTabOrder(editAddress); - radioExecute->MoveAfterInTabOrder(radioMemory); - editSize->MoveAfterInTabOrder(radioExecute); - checkRead->MoveAfterInTabOrder(editSize); - checkWrite->MoveAfterInTabOrder(checkRead); - checkOnChange->MoveAfterInTabOrder(checkWrite); - editCondition->MoveAfterInTabOrder(checkOnChange); - checkEnabled->MoveAfterInTabOrder(editCondition); - checkLog->MoveAfterInTabOrder(checkEnabled); - buttonOk->MoveAfterInTabOrder(checkLog); - buttonCancel->MoveAfterInTabOrder(buttonOk); - - topLevelSizer->Add(upperPart,0); - topLevelSizer->AddSpacer(5); - topLevelSizer->Add(conditionSizer,0,wxLEFT|wxTOP|wxRIGHT|wxEXPAND,8); - topLevelSizer->AddSpacer(5); - topLevelSizer->Add(bottomRowSizer,0,wxLEFT|wxTOP|wxRIGHT|wxBOTTOM,8); - SetSizer(topLevelSizer); - topLevelSizer->Fit(this); - - buttonOk->SetDefault(); - - // default values - memory = true; - onChange = false; - read = write = true; - enabled = log = true; - address = -1; - size = 1; - condition[0] = 0; - setDefaultValues(); -} - -void BreakpointWindow::setDefaultValues() -{ - radioExecute->SetValue(!memory); - radioMemory->SetValue(memory); - checkRead->SetValue(read); - checkWrite->SetValue(write); - checkOnChange->SetValue(onChange); - checkEnabled->SetValue(enabled); - checkLog->SetValue(log); - - checkRead->Enable(memory); - checkWrite->Enable(memory); - checkOnChange->Enable(memory); - checkRead->Enable(memory); - editSize->Enable(memory); - editCondition->Enable(!memory); - checkLog->Enable(memory); - - wchar_t str[64]; - if (address != 0xFFFFFFFF) - { - swprintf(str,64,L"0x%08X",address); - editAddress->SetValue(str); - } - - swprintf(str,64,L"0x%08X",size); - editSize->SetValue(str); - editCondition->SetValue(wxString(condition,wxConvUTF8)); -} - -void BreakpointWindow::loadFromMemcheck(MemCheck& memcheck) -{ - memory = true; - - read = (memcheck.cond & MEMCHECK_READ) != 0; - write = (memcheck.cond & MEMCHECK_WRITE) != 0; - onChange = (memcheck.cond & MEMCHECK_WRITE_ONCHANGE) != 0; - - switch (memcheck.result) - { - case MEMCHECK_BOTH: - log = enabled = true; - break; - case MEMCHECK_LOG: - log = true; - enabled = false; - break; - case MEMCHECK_BREAK: - log = false; - enabled = true; - break; - case MEMCHECK_IGNORE: - log = enabled = false; - break; - } - - address = memcheck.start; - size = memcheck.end-address; - - setDefaultValues(); -} - -void BreakpointWindow::loadFromBreakpoint(BreakPoint& breakpoint) -{ - memory = false; - - enabled = breakpoint.enabled; - address = breakpoint.addr; - size = 1; - - if (breakpoint.hasCond) - { - strcpy(condition,breakpoint.cond.expressionString); - } else { - condition[0] = 0; - } - - setDefaultValues(); -} - -void BreakpointWindow::initBreakpoint(u32 _address) -{ - memory = false; - enabled = true; - address = _address; - size = 1; - condition[0] = 0; - - setDefaultValues(); -} - -void BreakpointWindow::onRadioChange(wxCommandEvent& evt) -{ - memory = radioMemory->GetValue(); - - checkRead->Enable(memory); - checkWrite->Enable(memory); - checkOnChange->Enable(memory); - checkRead->Enable(memory); - editSize->Enable(memory); - editCondition->Enable(!memory); - checkLog->Enable(memory); -} - -bool BreakpointWindow::fetchDialogData() -{ - wchar_t errorMessage[512]; - PostfixExpression exp; - - memory = radioMemory->GetValue(); - read = checkRead->GetValue(); - write = checkWrite->GetValue(); - enabled = checkEnabled->GetValue(); - log = checkLog->GetValue(); - onChange = checkOnChange->GetValue(); - - // parse address - wxCharBuffer addressText = editAddress->GetValue().ToUTF8(); - if (!cpu->initExpression(addressText,exp)) - { - swprintf(errorMessage,512,L"Invalid expression \"%s\".",editAddress->GetValue().wchar_str().data()); - wxMessageBox(errorMessage,L"Error",wxICON_ERROR); - return false; - } - - u64 value; - if (!cpu->parseExpression(exp,value)) - { - swprintf(errorMessage,512,L"Invalid expression \"%s\".",editAddress->GetValue().wchar_str().data()); - wxMessageBox(errorMessage,L"Error",wxICON_ERROR); - return false; - } - address = value; - - if (memory) - { - // parse size - wxCharBuffer sizeText = editSize->GetValue().ToUTF8(); - if (!cpu->initExpression(sizeText,exp)) - { - swprintf(errorMessage,512,L"Invalid expression \"%s\".",editSize->GetValue().wchar_str().data()); - wxMessageBox(errorMessage,L"Error",wxICON_ERROR); - return false; - } - - if (!cpu->parseExpression(exp,value)) - { - swprintf(errorMessage,512,L"Invalid expression \"%s\".",editSize->GetValue().wchar_str().data()); - wxMessageBox(errorMessage,L"Error",wxICON_ERROR); - return false; - } - size = value; - } - - // condition - wxCharBuffer conditionText = editCondition->GetValue().ToUTF8(); - strncpy(condition,conditionText,sizeof(condition)); - condition[sizeof(condition)-1] = 0; - - compiledCondition.clear(); - if (condition[0] != 0) - { - if (!cpu->initExpression(condition,compiledCondition)) - { - swprintf(errorMessage,512,L"Invalid expression \"%s\".",editCondition->GetValue().wchar_str().data()); - wxMessageBox(errorMessage,L"Error",wxICON_ERROR); - return false; - } - } - - return true; -} - -void BreakpointWindow::onButtonOk(wxCommandEvent& evt) -{ - if (fetchDialogData()) - evt.Skip(); -} - -void BreakpointWindow::addBreakpoint() -{ - if (memory) - { - // add memcheck - int cond = 0; - if (read) - cond |= MEMCHECK_READ; - if (write) - cond |= MEMCHECK_WRITE; - if (onChange) - cond |= MEMCHECK_WRITE_ONCHANGE; - - MemCheckResult result; - if (log && enabled) result = MEMCHECK_BOTH; - else if (log) result = MEMCHECK_LOG; - else if (enabled) result = MEMCHECK_BREAK; - else result = MEMCHECK_IGNORE; - - CBreakPoints::AddMemCheck(cpu->getCpuType(), address, address + size, (MemCheckCondition)cond, result); - } else { - // add breakpoint - CBreakPoints::AddBreakPoint(cpu->getCpuType(), address,false); - - if (condition[0] != 0) - { - BreakPointCond cond; - cond.debug = cpu; - strcpy(cond.expressionString,condition); - cond.expression = compiledCondition; - CBreakPoints::ChangeBreakPointAddCond(cpu->getCpuType(), address,cond); - } - - if (!enabled) - { - CBreakPoints::ChangeBreakPoint(cpu->getCpuType(), address,false); - } - } -} diff --git a/pcsx2/gui/Debugger/BreakpointWindow.h b/pcsx2/gui/Debugger/BreakpointWindow.h deleted file mode 100644 index 482fc2f828..0000000000 --- a/pcsx2/gui/Debugger/BreakpointWindow.h +++ /dev/null @@ -1,63 +0,0 @@ -/* PCSX2 - PS2 Emulator for PCs - * Copyright (C) 2002-2014 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 . - */ - -#pragma once -#include -#include "DebugTools/DebugInterface.h" -#include "DebugTools/Breakpoints.h" - -class BreakpointWindow : public wxDialog -{ -public: - BreakpointWindow( wxWindow* parent, DebugInterface* _cpu ); - void loadFromMemcheck(MemCheck& memcheck); - void loadFromBreakpoint(BreakPoint& breakpoint); - void initBreakpoint(u32 _address); - void addBreakpoint(); - - wxDECLARE_EVENT_TABLE(); -protected: - void onRadioChange(wxCommandEvent& evt); - void onButtonOk(wxCommandEvent& evt); -private: - void setDefaultValues(); - bool fetchDialogData(); - - DebugInterface* cpu; - - wxTextCtrl* editAddress; - wxTextCtrl* editSize; - wxRadioButton* radioMemory; - wxRadioButton* radioExecute; - wxCheckBox* checkRead; - wxCheckBox* checkWrite; - wxCheckBox* checkOnChange; - wxTextCtrl* editCondition; - wxCheckBox* checkEnabled; - wxCheckBox* checkLog; - wxButton* buttonOk; - wxButton* buttonCancel; - - bool memory; - bool read; - bool write; - bool enabled; - bool log; - bool onChange; - u32 address; - u32 size; - char condition[128]; - PostfixExpression compiledCondition; -}; diff --git a/pcsx2/gui/Debugger/CtrlDisassemblyView.cpp b/pcsx2/gui/Debugger/CtrlDisassemblyView.cpp deleted file mode 100644 index 18a68740a2..0000000000 --- a/pcsx2/gui/Debugger/CtrlDisassemblyView.cpp +++ /dev/null @@ -1,1379 +0,0 @@ -/* PCSX2 - PS2 Emulator for PCs - * Copyright (C) 2002-2021 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 . - */ - -#include "PrecompiledHeader.h" -#include "CtrlDisassemblyView.h" -#include "DebugTools/Breakpoints.h" -#include "DebugTools/Debug.h" -#include "DebugTools/MipsAssembler.h" - -#include "DebugEvents.h" -#include "BreakpointWindow.h" -#include "gui/AppConfig.h" -#include "System.h" -#include "DisassemblyDialog.h" - -#include -#include -#include - -#include "gui/Resources/Breakpoint_Active.h" -#include "gui/Resources/Breakpoint_Inactive.h" - -wxBEGIN_EVENT_TABLE(CtrlDisassemblyView, wxWindow) - EVT_PAINT(CtrlDisassemblyView::paintEvent) - EVT_MOUSEWHEEL(CtrlDisassemblyView::mouseEvent) - EVT_LEFT_DOWN(CtrlDisassemblyView::mouseEvent) - EVT_LEFT_DCLICK(CtrlDisassemblyView::mouseEvent) - EVT_RIGHT_DOWN(CtrlDisassemblyView::mouseEvent) - EVT_RIGHT_UP(CtrlDisassemblyView::mouseEvent) - EVT_MOTION(CtrlDisassemblyView::mouseEvent) - EVT_KEY_DOWN(CtrlDisassemblyView::keydownEvent) - EVT_CHAR(CtrlDisassemblyView::keydownEvent) - EVT_SCROLLWIN_LINEUP(CtrlDisassemblyView::scrollbarEvent) - EVT_SCROLLWIN_LINEDOWN(CtrlDisassemblyView::scrollbarEvent) - EVT_SCROLLWIN_PAGEUP(CtrlDisassemblyView::scrollbarEvent) - EVT_SCROLLWIN_PAGEDOWN(CtrlDisassemblyView::scrollbarEvent) - EVT_SIZE(CtrlDisassemblyView::sizeEvent) - EVT_SET_FOCUS(CtrlDisassemblyView::focusEvent) - EVT_KILL_FOCUS(CtrlDisassemblyView::focusEvent) -wxEND_EVENT_TABLE() - -enum DisassemblyMenuIdentifiers -{ - ID_DISASM_COPYADDRESS = 1, - ID_DISASM_COPYINSTRUCTIONHEX, - ID_DISASM_COPYINSTRUCTIONDISASM, - ID_DISASM_DISASSEMBLETOFILE, - ID_DISASM_ASSEMBLE, - ID_DISASM_RUNTOHERE, - ID_DISASM_SETPCTOHERE, - ID_DISASM_TOGGLEBREAKPOINT, - ID_DISASM_FOLLOWBRANCH, - ID_DISASM_GOTOADDRESS, - ID_DISASM_GOTOINMEMORYVIEW, - ID_DISASM_KILLFUNCTION, - ID_DISASM_RENAMEFUNCTION, - ID_DISASM_REMOVEFUNCTION, - ID_DISASM_ADDFUNCTION -}; - -class NonAutoSelectTextCtrl : public wxTextCtrl -{ -public: - NonAutoSelectTextCtrl(wxWindow* parent, wxWindowID id, - const wxString& value = wxEmptyString, - const wxPoint& pos = wxDefaultPosition, - const wxSize& size = wxDefaultSize, - long style = 0, - const wxValidator& validator = wxDefaultValidator, - const wxString& name = wxTextCtrlNameStr) - : wxTextCtrl(parent, id, value, pos, size, style, validator, name) - { - } -#ifdef _WIN32 - -#define WM_GETDLGCODE 0x0087 -#define DLGC_HASSETSEL 0x0008 - - virtual WXLRESULT MSWWindowProc(WXUINT nMsg, WXWPARAM wParam, WXLPARAM lParam) - { - WXLRESULT result = wxTextCtrl::MSWWindowProc(nMsg, wParam, lParam); - if (nMsg == WM_GETDLGCODE) - result &= ~DLGC_HASSETSEL; - return result; - } -#endif -}; - -class TextEntryDialog : public wxDialog -{ -public: - TextEntryDialog(wxWindow* parent, wxString title, wxString defaultText) - : wxDialog(parent, wxID_ANY, title) - { - wxBoxSizer* sizer = new wxBoxSizer(wxVERTICAL); - - textControl = new NonAutoSelectTextCtrl(this, wxID_ANY, L"", wxDefaultPosition, wxDefaultSize, 0); - textControl->SetValue(defaultText); - textControl->SetSelection(textControl->GetLastPosition(), textControl->GetLastPosition()); - sizer->Add(textControl, 0, wxEXPAND); - - wxBoxSizer* buttons = new wxBoxSizer(wxHORIZONTAL); - okButton = new wxButton(this, wxID_OK, L"OK"); - okButton->SetDefault(); - cancelButton = new wxButton(this, wxID_CANCEL, L"Cancel"); - - buttons->Add(okButton); - buttons->Add(cancelButton); - sizer->Add(buttons, 0, wxEXPAND); - - SetSizer(sizer); - sizer->Layout(); - sizer->Fit(this); - - textControl->SetFocus(); - textControl->SetFocusFromKbd(); - } - wxString getText() { return textControl->GetValue(); } - -private: - NonAutoSelectTextCtrl* textControl; - wxButton* okButton; - wxButton* cancelButton; -}; - -inline wxIcon _wxGetIconFromMemory(const unsigned char* data, int length) -{ - wxMemoryInputStream is(data, length); - wxBitmap b = wxBitmap(wxImage(is, wxBITMAP_TYPE_ANY, -1), -1); - wxIcon icon; - icon.CopyFromBitmap(b); - return icon; -} - -CtrlDisassemblyView::CtrlDisassemblyView(wxWindow* parent, DebugInterface* _cpu) - : wxWindow(parent, wxID_ANY, wxDefaultPosition, wxDefaultSize, wxWANTS_CHARS | wxBORDER_SIMPLE | wxVSCROLL) - , cpu(_cpu) -{ - manager.setCpu(cpu); - windowStart = 0x100000; - rowHeight = getDebugFontHeight() + 2; - charWidth = getDebugFontWidth(); - displaySymbols = true; - visibleRows = 1; - - bpEnabled = _wxGetIconFromMemory(res_Breakpoint_Active::Data, res_Breakpoint_Active::Length); - bpDisabled = _wxGetIconFromMemory(res_Breakpoint_Inactive::Data, res_Breakpoint_Inactive::Length); - - menu.Append(ID_DISASM_COPYADDRESS, L"Copy Address"); - menu.Append(ID_DISASM_COPYINSTRUCTIONHEX, L"Copy Instruction (Hex)"); - menu.Append(ID_DISASM_COPYINSTRUCTIONDISASM, L"Copy Instruction (Disasm)"); - menu.Append(ID_DISASM_DISASSEMBLETOFILE, L"Disassemble to File"); - menu.AppendSeparator(); - menu.Append(ID_DISASM_ASSEMBLE, L"Assemble Opcode(s)"); - menu.AppendSeparator(); - menu.Append(ID_DISASM_RUNTOHERE, L"Run to Cursor"); - menu.Append(ID_DISASM_SETPCTOHERE, L"Jump to Cursor"); - menu.Append(ID_DISASM_TOGGLEBREAKPOINT, L"Toggle Breakpoint"); - menu.Append(ID_DISASM_FOLLOWBRANCH, L"Follow Branch"); - menu.AppendSeparator(); - menu.Append(ID_DISASM_GOTOADDRESS, L"Go to Address"); - menu.Append(ID_DISASM_GOTOINMEMORYVIEW, L"Go to in Memory View"); - menu.AppendSeparator(); - menu.Append(ID_DISASM_ADDFUNCTION, L"Add Function Here"); - menu.Append(ID_DISASM_RENAMEFUNCTION, L"Rename Function"); - menu.Append(ID_DISASM_REMOVEFUNCTION, L"Remove Function"); - menu.Bind(wxEVT_MENU, &CtrlDisassemblyView::onPopupClick, this); - - SetScrollbar(wxVERTICAL, 100, 1, 201, true); - SetDoubleBuffered(true); - calculatePixelPositions(); - setCurAddress(windowStart); -} - -#ifdef _WIN32 -WXLRESULT CtrlDisassemblyView::MSWWindowProc(WXUINT nMsg, WXWPARAM wParam, WXLPARAM lParam) -{ - switch (nMsg) - { - case 0x0104: // WM_SYSKEYDOWN, make f10 usable - if (wParam == 0x79) // f10 - { - postEvent(debEVT_STEPOVER, 0); - return 0; - } - break; - } - - return wxWindow::MSWWindowProc(nMsg, wParam, lParam); -} -#endif - -void CtrlDisassemblyView::scanFunctions() -{ - if (!cpu->isAlive()) - return; - - manager.analyze(windowStart, manager.getNthNextAddress(windowStart, visibleRows) - windowStart); -} - -void CtrlDisassemblyView::postEvent(wxEventType type, wxString text) -{ - wxCommandEvent event(type, GetId()); - event.SetEventObject(this); - event.SetClientData(cpu); - event.SetString(text); - wxPostEvent(this, event); -} - -void CtrlDisassemblyView::postEvent(wxEventType type, int value) -{ - wxCommandEvent event(type, GetId()); - event.SetEventObject(this); - event.SetClientData(cpu); - event.SetInt(value); - wxPostEvent(this, event); -} - -void CtrlDisassemblyView::paintEvent(wxPaintEvent& evt) -{ - wxPaintDC dc(this); - render(dc); -} - -void CtrlDisassemblyView::redraw() -{ - wxClientDC dc(this); - render(dc); -} - -bool CtrlDisassemblyView::getDisasmAddressText(u32 address, char* dest, bool abbreviateLabels, bool showData) -{ - if (displaySymbols) - { - const std::string addressSymbol = cpu->GetSymbolMap().GetLabelString(address); - if (!addressSymbol.empty()) - { - for (int k = 0; addressSymbol[k] != 0; k++) - { - // abbreviate long names - if (abbreviateLabels && k == 16 && addressSymbol[k + 1] != 0) - { - *dest++ = '+'; - break; - } - *dest++ = addressSymbol[k]; - } - *dest++ = ':'; - *dest = 0; - return true; - } - else - { - sprintf(dest, " %08X", address); - return false; - } - } - else - { - if (showData) - sprintf(dest, "%08X %08X", address, cpu->read32(address)); - else - sprintf(dest, "%08X", address); - return false; - } -} - -wxColor scaleColor(wxColor color, float factor) -{ - unsigned char r = color.Red(); - unsigned char g = color.Green(); - unsigned char b = color.Blue(); - unsigned char a = color.Alpha(); - - r = std::min(255, std::max((int)(r * factor), 0)); - g = std::min(255, std::max((int)(g * factor), 0)); - b = std::min(255, std::max((int)(b * factor), 0)); - - return wxColor(r, g, b, a); -} - -void CtrlDisassemblyView::drawBranchLine(wxDC& dc, std::map& addressPositions, BranchLine& line) -{ - u32 windowEnd = manager.getNthNextAddress(windowStart, visibleRows); - - int winBottom = GetSize().GetHeight(); - - int topY; - int bottomY; - if (line.first < windowStart) - { - topY = -1; - } - else if (line.first >= windowEnd) - { - topY = GetSize().GetHeight() + 1; - } - else - { - topY = addressPositions[line.first] + rowHeight / 2; - } - - if (line.second < windowStart) - { - bottomY = -1; - } - else if (line.second >= windowEnd) - { - bottomY = GetSize().GetHeight() + 1; - } - else - { - bottomY = addressPositions[line.second] + rowHeight / 2; - } - - if ((topY < 0 && bottomY < 0) || (topY > winBottom && bottomY > winBottom)) - { - return; - } - - // highlight line in a different color if it affects the currently selected opcode - wxColor color; - if (line.first == curAddress || line.second == curAddress) - { - color = wxColor(0xFF257AFA); - } - else - { - color = wxColor(0xFFFF3020); - } - - wxPen pen = wxPen(color); - dc.SetBrush(wxBrush(color)); - dc.SetPen(wxPen(color)); - - int x = pixelPositions.arrowsStart + line.laneIndex * 8; - - if (topY < 0) // first is not visible, but second is - { - dc.DrawLine(x - 2, bottomY, x + 2, bottomY); - dc.DrawLine(x + 2, bottomY, x + 2, 0); - - if (line.type == LINE_DOWN) - { - dc.DrawLine(x, bottomY - 4, x - 4, bottomY); - dc.DrawLine(x - 4, bottomY, x + 1, bottomY + 5); - } - } - else if (bottomY > winBottom) // second is not visible, but first is - { - dc.DrawLine(x - 2, topY, x + 2, topY); - dc.DrawLine(x + 2, topY, x + 2, winBottom); - - if (line.type == LINE_UP) - { - dc.DrawLine(x, topY - 4, x - 4, topY); - dc.DrawLine(x - 4, topY, x + 1, topY + 5); - } - } - else - { // both are visible - if (line.type == LINE_UP) - { - dc.DrawLine(x - 2, bottomY, x + 2, bottomY); - dc.DrawLine(x + 2, bottomY, x + 2, topY); - dc.DrawLine(x + 2, topY, x - 4, topY); - - dc.DrawLine(x, topY - 4, x - 4, topY); - dc.DrawLine(x - 4, topY, x + 1, topY + 5); - } - else - { - dc.DrawLine(x - 2, topY, x + 2, topY); - dc.DrawLine(x + 2, topY, x + 2, bottomY); - dc.DrawLine(x + 2, bottomY, x - 4, bottomY); - - dc.DrawLine(x, bottomY - 4, x - 4, bottomY); - dc.DrawLine(x - 4, bottomY, x + 1, bottomY + 5); - } - } -} - -int CtrlDisassemblyView::getBackgroundColor(unsigned int address) -{ - u32 colors[6] = {0xFFe0FFFF, 0xFFFFe0e0, 0xFFe8e8FF, 0xFFFFe0FF, 0xFFe0FFe0, 0xFFFFFFe0}; - int n = cpu->GetSymbolMap().GetFunctionNum(address); - if (n == -1) - return 0xFFFFFFFF; - return colors[n % 6]; -} - -std::set CtrlDisassemblyView::getSelectedLineArguments() -{ - std::set args; - - DisassemblyLineInfo line = DisassemblyLineInfo(); - for (u32 addr = selectRangeStart; addr < selectRangeEnd; addr += 4) - { - manager.getLine(addr, displaySymbols, line); - size_t p = 0, nextp = line.params.find(','); - while (nextp != line.params.npos) - { - args.insert(line.params.substr(p, nextp - p)); - p = nextp + 1; - nextp = line.params.find(',', p); - } - - if (p < line.params.size()) - { - args.insert(line.params.substr(p)); - } - - // check for registers in memory opcodes - p = line.params.find('('); - nextp = line.params.find(')'); - if (p != line.params.npos && nextp != line.params.npos && nextp > p) - { - args.insert(line.params.substr(p + 1, nextp - p - 1)); - } - } - - return args; -} - -void CtrlDisassemblyView::drawArguments(wxDC& dc, const DisassemblyLineInfo& line, int x, int y, wxColor& textColor, - const std::set& currentArguments) -{ - if (line.params.empty()) - return; - - // Don't highlight the selected lines. - if (isInInterval(selectRangeStart, selectRangeEnd - selectRangeStart, line.info.opcodeAddress)) - { - dc.DrawText(wxString(line.params.c_str(), wxConvUTF8), x, y); - return; - } - - wxColor highlightedColor = wxColor(textColor == 0xFF0000FF ? 0xFFAABB77 : 0xFFAABB00); - - size_t p = 0, nextp = line.params.find(','); - while (nextp != line.params.npos) - { - const std::string arg = line.params.substr(p, nextp - p); - if (currentArguments.find(arg) != currentArguments.end() && textColor != 0xFFFFFFFF) - { - dc.SetTextForeground(highlightedColor); - } - dc.DrawText(wxString(arg.c_str(), wxConvUTF8), x, y); - x += arg.size() * charWidth; - - p = nextp + 1; - nextp = line.params.find(',', p); - - dc.SetTextForeground(textColor); - dc.DrawText(L",", x, y); - x += charWidth; - } - if (p < line.params.size()) - { - const std::string arg = line.params.substr(p); - if (currentArguments.find(arg) != currentArguments.end() && textColor != 0xFFFFFFFF) - { - dc.SetTextForeground(highlightedColor); - } - dc.DrawText(wxString(arg.c_str(), wxConvUTF8), x, y); - dc.SetTextForeground(textColor); - } -} - -void CtrlDisassemblyView::render(wxDC& dc) -{ - // init stuff - int totalWidth, totalHeight; - GetSize(&totalWidth, &totalHeight); - visibleRows = totalHeight / rowHeight; - - // clear background - wxColor white = wxColor(0xFFFFFFFF); - - dc.SetBrush(wxBrush(white)); - dc.SetPen(wxPen(white)); - - int width, height; - dc.GetSize(&width, &height); - dc.DrawRectangle(0, 0, width, height); - - if (!cpu->isAlive()) - return; - - wxFont font = pxGetFixedFont(8); - wxFont boldFont = pxGetFixedFont(8, wxFONTWEIGHT_BOLD); - font.SetPixelSize(wxSize(charWidth, rowHeight - 2)); - boldFont.SetPixelSize(wxSize(charWidth, rowHeight - 2)); - - bool hasFocus = wxWindow::FindFocus() == this; - - std::map addressPositions; - - unsigned int address = windowStart; - - const std::set currentArguments = getSelectedLineArguments(); - DisassemblyLineInfo line = DisassemblyLineInfo(); - for (int i = 0; i < visibleRows + 1; i++) - { - manager.getLine(address, displaySymbols, line); - - int rowY1 = rowHeight * i; - - addressPositions[address] = rowY1; - - wxColor backgroundColor = wxColor(getBackgroundColor(address)); - wxColor textColor = wxColor(0xFF000000); - - if (isInInterval(address, line.totalSize, cpu->getPC())) - { - backgroundColor = scaleColor(backgroundColor, 1.05f); - } - - if (address >= selectRangeStart && address < selectRangeEnd) - { - if (hasFocus) - { - backgroundColor = address == curAddress ? 0xFFFF8822 : 0xFFFF9933; - textColor = 0xFFFFFFFF; - } - else - { - backgroundColor = 0xFFC0C0C0; - } - } - - // display whether the condition of a branch is met - if (line.info.isConditional && address == cpu->getPC()) - { - line.params += line.info.conditionMet ? " ; true" : " ; false"; - } - - // draw background - dc.SetBrush(wxBrush(backgroundColor)); - dc.SetPen(wxPen(backgroundColor)); - dc.DrawRectangle(0, rowY1, totalWidth, rowHeight); - - // display address/symbol - bool enabled; - if (CBreakPoints::IsAddressBreakPoint(cpu->getCpuType(), address, &enabled)) - { - if (enabled) - textColor = 0x0000FF; - int yOffset = std::max(-1, (rowHeight - 14 + 1) / 2); - dc.DrawIcon(enabled ? bpEnabled : bpDisabled, 2, rowY1 + 1 + yOffset); - } - - dc.SetTextForeground(textColor); - - char addressText[64]; - getDisasmAddressText(address, addressText, true, line.type == DISTYPE_OPCODE); - - dc.SetFont(font); - dc.DrawText(wxString(addressText, wxConvUTF8), pixelPositions.addressStart, rowY1 + 2); - drawArguments(dc, line, pixelPositions.argumentsStart, rowY1 + 2, textColor, currentArguments); - - if (isInInterval(address, line.totalSize, cpu->getPC())) - dc.DrawText(L"\u25A0", pixelPositions.opcodeStart - (charWidth + 1), rowY1); - - dc.SetFont(boldFont); - dc.DrawText(wxString(line.name.c_str(), wxConvUTF8), pixelPositions.opcodeStart, rowY1 + 2); - - address += line.totalSize; - } - - std::vector branchLines = manager.getBranchLines(windowStart, address - windowStart); - for (size_t i = 0; i < branchLines.size(); i++) - { - drawBranchLine(dc, addressPositions, branchLines[i]); - } -} - -void CtrlDisassemblyView::gotoAddress(u32 addr) -{ - u32 windowEnd = manager.getNthNextAddress(windowStart, visibleRows); - u32 newAddress = manager.getStartAddress(addr); - - if (newAddress < windowStart || newAddress >= windowEnd) - { - windowStart = manager.getNthPreviousAddress(newAddress, visibleRows / 2); - } - - setCurAddress(addr); - scanFunctions(); - redraw(); -} - -void CtrlDisassemblyView::scrollAddressIntoView() -{ - u32 windowEnd = manager.getNthNextAddress(windowStart, visibleRows); - - if (curAddress < windowStart) - windowStart = curAddress; - else if (curAddress >= windowEnd) - windowStart = manager.getNthPreviousAddress(curAddress, visibleRows - 1); - - scanFunctions(); -} - -void CtrlDisassemblyView::calculatePixelPositions() -{ - pixelPositions.addressStart = 16; - pixelPositions.opcodeStart = pixelPositions.addressStart + 18 * charWidth; - pixelPositions.argumentsStart = pixelPositions.opcodeStart + 9 * charWidth; - pixelPositions.arrowsStart = pixelPositions.argumentsStart + 30 * charWidth; -} - - -void CtrlDisassemblyView::followBranch() -{ - DisassemblyLineInfo line = DisassemblyLineInfo(); - manager.getLine(curAddress, true, line); - - if (line.type == DISTYPE_OPCODE || line.type == DISTYPE_MACRO) - { - if (line.info.isBranch) - { - jumpStack.push_back(curAddress); - gotoAddress(line.info.branchTarget); - } - else if (line.info.hasRelevantAddress) - { - // well, not exactly a branch, but we can do something anyway - postEvent(debEVT_GOTOINMEMORYVIEW, line.info.releventAddress); - } - } - else if (line.type == DISTYPE_DATA) - { - // jump to the start of the current line - postEvent(debEVT_GOTOINMEMORYVIEW, curAddress); - } -} - -void CtrlDisassemblyView::assembleOpcode(u32 address, std::string defaultText) -{ - u32 encoded; - - if (!cpu->isCpuPaused()) - { - wxMessageBox(L"Cannot change code while the core is running", L"Error.", wxICON_ERROR); - return; - } - - TextEntryDialog entry(this, L"Assemble opcode", wxString(defaultText.c_str(), wxConvUTF8)); - entry.Layout(); - - if (entry.ShowModal() != wxID_OK) - return; - - wxString op = entry.getText(); - std::string errorText; - bool result = MipsAssembleOpcode(op.To8BitData(), cpu, address, encoded, errorText); - if (result) - { - SysClearExecutionCache(); - - if ((selectRangeEnd - selectRangeStart) > 4) - { - for (u32 addr = selectRangeStart; addr < selectRangeEnd; addr += 0x4) - { - cpu->write32(addr, encoded); - } - } - else - { - cpu->write32(address, encoded); - } - - redraw(); - } - else - { - wxMessageBox(wxString(errorText.c_str(), wxConvUTF8), L"Error.", wxICON_ERROR); - } -} - - -void CtrlDisassemblyView::onPopupClick(wxCommandEvent& evt) -{ - switch (evt.GetId()) - { - case ID_DISASM_FOLLOWBRANCH: - followBranch(); - break; - case ID_DISASM_COPYADDRESS: - if (wxTheClipboard->Open()) - { - wchar_t text[64]; - swprintf(text, 64, L"%08X", curAddress); - - wxTheClipboard->SetData(new wxTextDataObject(text)); - wxTheClipboard->Close(); - } - break; - case ID_DISASM_GOTOADDRESS: - postEvent(debEVT_GOTOADDRESS, 0); - break; - case ID_DISASM_GOTOINMEMORYVIEW: - postEvent(debEVT_GOTOINMEMORYVIEW, curAddress); - break; - case ID_DISASM_COPYINSTRUCTIONHEX: - copyInstructions(selectRangeStart, selectRangeEnd, false); - break; - case ID_DISASM_COPYINSTRUCTIONDISASM: - copyInstructions(selectRangeStart, selectRangeEnd, true); - break; - case ID_DISASM_SETPCTOHERE: - cpu->setPc(curAddress); - redraw(); - break; - case ID_DISASM_RUNTOHERE: - postEvent(debEVT_RUNTOPOS, curAddress); - break; - case ID_DISASM_TOGGLEBREAKPOINT: - toggleBreakpoint(false); - break; - case ID_DISASM_DISASSEMBLETOFILE: - disassembleToFile(); - break; - case ID_DISASM_RENAMEFUNCTION: - { - u32 funcBegin = cpu->GetSymbolMap().GetFunctionStart(curAddress); - if (funcBegin != 0xFFFFFFFF) - { - wxString newName = wxGetTextFromUser(L"Enter the new function name", L"New function name", - wxString(cpu->GetSymbolMap().GetLabelString(funcBegin).c_str(), wxConvUTF8), this); - - if (!newName.empty()) - { - const wxCharBuffer converted = newName.ToUTF8(); - cpu->GetSymbolMap().SetLabelName(converted, funcBegin); - postEvent(debEVT_MAPLOADED, 0); - redraw(); - } - } - else - { - wxMessageBox(L"No symbol selected", L"Error", wxICON_ERROR); - } - break; - } - case ID_DISASM_REMOVEFUNCTION: - { - u32 funcBegin = cpu->GetSymbolMap().GetFunctionStart(curAddress); - if (funcBegin != 0xFFFFFFFF) - { - u32 prevBegin = cpu->GetSymbolMap().GetFunctionStart(funcBegin - 1); - if (prevBegin != 0xFFFFFFFF) - { - u32 expandedSize = cpu->GetSymbolMap().GetFunctionSize(prevBegin) + cpu->GetSymbolMap().GetFunctionSize(funcBegin); - cpu->GetSymbolMap().SetFunctionSize(prevBegin, expandedSize); - } - - cpu->GetSymbolMap().RemoveFunction(funcBegin, true); - cpu->GetSymbolMap().SortSymbols(); - cpu->GetSymbolMap().UpdateActiveSymbols(); - manager.clear(); - - postEvent(debEVT_MAPLOADED, 0); - } - else - { - postEvent(debEVT_SETSTATUSBARTEXT, L"WARNING: unable to find function symbol here"); - } - - redraw(); - break; - } - case ID_DISASM_ADDFUNCTION: - { - u32 prevBegin = cpu->GetSymbolMap().GetFunctionStart(curAddress); - if (prevBegin != 0xFFFFFFFF) - { - if (prevBegin == curAddress) - { - postEvent(debEVT_SETSTATUSBARTEXT, L"WARNING: There's already a function entry point at this adress"); - } - else - { - char symname[128]; - u32 prevSize = cpu->GetSymbolMap().GetFunctionSize(prevBegin); - u32 newSize = curAddress - prevBegin; - cpu->GetSymbolMap().SetFunctionSize(prevBegin, newSize); - - newSize = prevSize - newSize; - sprintf(symname, "u_un_%08X", curAddress); - cpu->GetSymbolMap().AddFunction(symname, curAddress, newSize); - cpu->GetSymbolMap().SortSymbols(); - cpu->GetSymbolMap().UpdateActiveSymbols(); - manager.clear(); - - postEvent(debEVT_MAPLOADED, 0); - } - } - else - { - char symname[128]; - int newSize = selectRangeEnd - selectRangeStart; - sprintf(symname, "u_un_%08X", selectRangeStart); - cpu->GetSymbolMap().AddFunction(symname, selectRangeStart, newSize); - cpu->GetSymbolMap().SortSymbols(); - cpu->GetSymbolMap().UpdateActiveSymbols(); - - postEvent(debEVT_MAPLOADED, 0); - } - - redraw(); - break; - } - case ID_DISASM_ASSEMBLE: - assembleOpcode(curAddress, disassembleCurAddress()); - break; - default: - wxMessageBox(L"Unimplemented.", L"Unimplemented.", wxICON_INFORMATION); - break; - } -} - -void CtrlDisassemblyView::keydownEvent(wxKeyEvent& evt) -{ - u32 windowEnd = manager.getNthNextAddress(windowStart, visibleRows); - - switch (evt.GetKeyCode()) - { - case 'g': - case 'G': - { - u64 addr; - if (!executeExpressionWindow(this, cpu, addr)) - return; - gotoAddress(addr); - } - break; - case 'd': - case 'D': - toggleBreakpoint(true); - break; - case 'e': - case 'E': - editBreakpoint(); - break; - case 'b': - case 'B': - { - BreakpointWindow bpw(this, cpu); - if (bpw.ShowModal() == wxID_OK) - { - bpw.addBreakpoint(); - postEvent(debEVT_UPDATE, 0); - } - } - break; - case 'm': - case 'M': - { - assembleOpcode(curAddress, ""); - } - break; - case WXK_LEFT: - if (jumpStack.empty()) - { - gotoAddress(cpu->getPC()); - } - else - { - u32 addr = jumpStack[jumpStack.size() - 1]; - jumpStack.pop_back(); - gotoAddress(addr); - } - break; - case WXK_RIGHT: - followBranch(); - break; - case WXK_UP: - setCurAddress(manager.getNthPreviousAddress(curAddress, 1), wxGetKeyState(WXK_SHIFT)); - scrollAddressIntoView(); - scanFunctions(); - break; - case WXK_DOWN: - setCurAddress(manager.getNthNextAddress(curAddress, 1), wxGetKeyState(WXK_SHIFT)); - scrollAddressIntoView(); - scanFunctions(); - break; - case WXK_TAB: - displaySymbols = !displaySymbols; - break; - case WXK_PAGEUP: - if (curAddress != windowStart && curAddressIsVisible()) - { - setCurAddress(windowStart, wxGetKeyState(WXK_SHIFT)); - scrollAddressIntoView(); - } - else - { - setCurAddress(manager.getNthPreviousAddress(windowStart, visibleRows), wxGetKeyState(WXK_SHIFT)); - scrollAddressIntoView(); - } - scanFunctions(); - break; - case WXK_PAGEDOWN: - if (manager.getNthNextAddress(curAddress, 1) != windowEnd && curAddressIsVisible()) - { - setCurAddress(manager.getNthPreviousAddress(windowEnd, 1), wxGetKeyState(WXK_SHIFT)); - scrollAddressIntoView(); - } - else - { - setCurAddress(manager.getNthNextAddress(windowEnd, visibleRows - 1), wxGetKeyState(WXK_SHIFT)); - scrollAddressIntoView(); - } - scanFunctions(); - break; - case WXK_F8: - postEvent(debEVT_STEPOUT, 0); - break; - case WXK_F10: - postEvent(debEVT_STEPOVER, 0); - return; - case WXK_F11: - if (evt.ShiftDown()) - postEvent(debEVT_STEPOUT, 0); - else - postEvent(debEVT_STEPINTO, 0); - return; - default: - evt.Skip(); - break; - } - - redraw(); -} - -void CtrlDisassemblyView::scrollbarEvent(wxScrollWinEvent& evt) -{ - int type = evt.GetEventType(); - if (type == wxEVT_SCROLLWIN_LINEUP) - { - windowStart = manager.getNthPreviousAddress(windowStart, 1); - scanFunctions(); - } - else if (type == wxEVT_SCROLLWIN_LINEDOWN) - { - windowStart = manager.getNthNextAddress(windowStart, 1); - scanFunctions(); - } - else if (type == wxEVT_SCROLLWIN_PAGEUP) - { - windowStart = manager.getNthPreviousAddress(windowStart, visibleRows); - scanFunctions(); - } - else if (type == wxEVT_SCROLLWIN_PAGEDOWN) - { - windowStart = manager.getNthNextAddress(windowStart, visibleRows); - scanFunctions(); - } - - redraw(); -} - -void CtrlDisassemblyView::toggleBreakpoint(bool toggleEnabled) -{ - bool enabled; - if (CBreakPoints::IsAddressBreakPoint(cpu->getCpuType(), curAddress, &enabled)) - { - if (!enabled) - { - // enable disabled breakpoints - CBreakPoints::ChangeBreakPoint(cpu->getCpuType(), curAddress, true); - } - else if (!toggleEnabled && CBreakPoints::GetBreakPointCondition(cpu->getCpuType(), curAddress) != NULL) - { - // don't just delete a breakpoint with a custom condition - CBreakPoints::RemoveBreakPoint(cpu->getCpuType(), curAddress); - } - else if (toggleEnabled) - { - // disable breakpoint - CBreakPoints::ChangeBreakPoint(cpu->getCpuType(), curAddress, false); - } - else - { - // otherwise just remove breakpoint - CBreakPoints::RemoveBreakPoint(cpu->getCpuType(), curAddress); - } - } - else - { - CBreakPoints::AddBreakPoint(cpu->getCpuType(), curAddress); - } -} - - -void CtrlDisassemblyView::updateStatusBarText() -{ - char text[512]; - DisassemblyLineInfo line = DisassemblyLineInfo(); - manager.getLine(curAddress, true, line); - - text[0] = 0; - if (line.type == DISTYPE_OPCODE || line.type == DISTYPE_MACRO) - { - if (line.info.isDataAccess) - { - if (!cpu->isValidAddress(line.info.dataAddress)) - { - sprintf(text, "Invalid address %08X", line.info.dataAddress); - } - else if (line.info.lrType == MIPSAnalyst::LOADSTORE_NORMAL && line.info.dataAddress % line.info.dataSize) - { - sprintf(text, "Unaligned address %08X", line.info.dataAddress); - } - else - { - switch (line.info.dataSize) - { - case 1: - sprintf(text, "[%08X] = %02X", line.info.dataAddress, cpu->read8(line.info.dataAddress)); - break; - case 2: - sprintf(text, "[%08X] = %04X", line.info.dataAddress, cpu->read16(line.info.dataAddress)); - break; - case 4: - { - u32 data; - if (line.info.lrType != MIPSAnalyst::LOADSTORE_NORMAL) - { - u32 address = line.info.dataAddress; - data = cpu->read32(address & ~3) >> (address & 3) * 8; - data |= cpu->read32((address + 3) & ~3) << (4 - (address & 3)) * 8; - } - else - { - data = cpu->read32(line.info.dataAddress); - } - - const std::string addressSymbol = cpu->GetSymbolMap().GetLabelString(data); - if (!addressSymbol.empty()) - { - sprintf(text, "[%08X] = %s (%08X)", line.info.dataAddress, addressSymbol.c_str(), data); - } - else - { - sprintf(text, "[%08X] = %08X", line.info.dataAddress, data); - } - break; - } - case 8: - { - u64 data; - if (line.info.lrType != MIPSAnalyst::LOADSTORE_NORMAL) - { - u32 address = line.info.dataAddress; - data = cpu->read64(address & ~7) >> (address & 7) * 8; - data |= cpu->read64((address + 7) & ~7) << (8 - (address & 7)) * 8; - } - else - { - data = cpu->read64(line.info.dataAddress); - } - - sprintf(text, "[%08X] = %016" PRIX64, line.info.dataAddress, data); - break; - } - case 16: - { - alignas(16) u128 data = cpu->read128(line.info.dataAddress); - sprintf(text, "[%08X] = %016" PRIX64 "%016" PRIX64, line.info.dataAddress, data._u64[1], data._u64[0]); - break; - } - } - } - } - - if (line.info.isBranch) - { - const std::string addressSymbol = cpu->GetSymbolMap().GetLabelString(line.info.branchTarget); - if (addressSymbol.empty()) - { - sprintf(text, "%08X", line.info.branchTarget); - } - else - { - sprintf(text, "%08X = %s", line.info.branchTarget, addressSymbol.c_str()); - } - } - } - else if (line.type == DISTYPE_DATA) - { - u32 start = cpu->GetSymbolMap().GetDataStart(curAddress); - if (start == 0xFFFFFFFF) - start = curAddress; - - u32 diff = curAddress - start; - const std::string label = cpu->GetSymbolMap().GetLabelString(start); - - if (!label.empty()) - { - if (diff != 0) - sprintf(text, "%08X (%s) + %08X", start, label.c_str(), diff); - else - sprintf(text, "%08X (%s)", start, label.c_str()); - } - else - { - if (diff != 0) - sprintf(text, "%08X + %08X", start, diff); - else - sprintf(text, "%08X", start); - } - } - - postEvent(debEVT_SETSTATUSBARTEXT, wxString(text, wxConvUTF8)); -} - -void CtrlDisassemblyView::mouseEvent(wxMouseEvent& evt) -{ - // left button - wxEventType type = evt.GetEventType(); - bool hasFocus = wxWindow::FindFocus() == this; - - if (type == wxEVT_LEFT_DOWN || type == wxEVT_LEFT_DCLICK || type == wxEVT_RIGHT_DOWN) - { - u32 newAddress = yToAddress(evt.GetY()); - bool setNewAddress = true; - - if (type == wxEVT_RIGHT_DOWN && !wxGetKeyState(WXK_SHIFT)) - { - // Maintain the current selection if right clicking into it. - if (newAddress >= selectRangeStart && newAddress < selectRangeEnd) - setNewAddress = false; - } - else - { - if (curAddress == newAddress && hasFocus) - toggleBreakpoint(false); - } - - if (setNewAddress) - setCurAddress(newAddress, wxGetKeyState(WXK_SHIFT)); - - SetFocus(); - SetFocusFromKbd(); - } - else if (evt.GetEventType() == wxEVT_RIGHT_UP) - { - PopupMenu(&menu, evt.GetPosition()); - return; - } - else if (evt.GetEventType() == wxEVT_MOUSEWHEEL) - { - if (evt.GetWheelRotation() > 0) - { - windowStart = manager.getNthPreviousAddress(windowStart, 3); - scanFunctions(); - } - else if (evt.GetWheelRotation() < 0) - { - windowStart = manager.getNthNextAddress(windowStart, 3); - scanFunctions(); - } - } - else if (evt.GetEventType() == wxEVT_MOTION) - { - if (evt.ButtonIsDown(wxMOUSE_BTN_LEFT)) - { - int newAddress = yToAddress(evt.GetY()); - setCurAddress(newAddress, wxGetKeyState(WXK_SHIFT)); - } - else - return; - } - else - { - evt.Skip(); - return; - } - - redraw(); -} - -void CtrlDisassemblyView::sizeEvent(wxSizeEvent& evt) -{ - wxSize s = evt.GetSize(); - visibleRows = s.GetWidth() / rowHeight; -} - -u32 CtrlDisassemblyView::yToAddress(int y) -{ - int line = y / rowHeight; - return manager.getNthNextAddress(windowStart, line); -} - -bool CtrlDisassemblyView::curAddressIsVisible() -{ - u32 windowEnd = manager.getNthNextAddress(windowStart, visibleRows); - return curAddress >= windowStart && curAddress < windowEnd; -} - -void CtrlDisassemblyView::scrollStepping(u32 newPc) -{ - u32 windowEnd = manager.getNthNextAddress(windowStart, visibleRows); - - newPc = manager.getStartAddress(newPc); - if (newPc >= windowEnd || newPc >= manager.getNthPreviousAddress(windowEnd, 1)) - { - windowStart = manager.getNthPreviousAddress(newPc, visibleRows - 2); - } -} - -std::string CtrlDisassemblyView::disassembleRange(u32 start, u32 size) -{ - std::string result; - - // gather all branch targets without labels - std::set branchAddresses; - for (u32 i = 0; i < size; i += 4) - { - MIPSAnalyst::MipsOpcodeInfo info = MIPSAnalyst::GetOpcodeInfo(cpu, start + i); - - if (info.isBranch && cpu->GetSymbolMap().GetLabelString(info.branchTarget).empty()) - { - if (branchAddresses.find(info.branchTarget) == branchAddresses.end()) - { - branchAddresses.insert(info.branchTarget); - } - } - } - - u32 disAddress = start; - bool previousLabel = true; - DisassemblyLineInfo line = DisassemblyLineInfo(); - while (disAddress < start + size) - { - char addressText[64], buffer[512]; - - manager.getLine(disAddress, displaySymbols, line); - bool isLabel = getDisasmAddressText(disAddress, addressText, false, line.type == DISTYPE_OPCODE); - - if (isLabel) - { - if (!previousLabel) - result += "\r\n"; - sprintf(buffer, "%s\r\n\r\n", addressText); - result += buffer; - } - else if (branchAddresses.find(disAddress) != branchAddresses.end()) - { - if (!previousLabel) - result += "\r\n"; - sprintf(buffer, "pos_%08X:\r\n\r\n", disAddress); - result += buffer; - } - - if (line.info.isBranch && !line.info.isBranchToRegister && cpu->GetSymbolMap().GetLabelString(line.info.branchTarget).empty() && branchAddresses.find(line.info.branchTarget) != branchAddresses.end()) - { - sprintf(buffer, "pos_%08X", line.info.branchTarget); - line.params = line.params.substr(0, line.params.find("0x")) + buffer; - } - - sprintf(buffer, "\t%s\t%s\r\n", line.name.c_str(), line.params.c_str()); - result += buffer; - previousLabel = isLabel; - disAddress += line.totalSize; - } - - return result; -} - -std::string CtrlDisassemblyView::disassembleCurAddress() -{ - DisassemblyLineInfo line = DisassemblyLineInfo(); - manager.getLine(curAddress, displaySymbols, line); - return line.name + (line.params.length() > 0 ? " " + line.params : ""); -} - -void CtrlDisassemblyView::copyInstructions(u32 startAddr, u32 endAddr, bool withDisasm) -{ - if (!wxTheClipboard->Open()) - { - wxMessageBox(L"Could not open clipboard.", L"Error", wxICON_ERROR); - return; - } - - if (!withDisasm) - { - int instructionSize = 4; - int count = (endAddr - startAddr) / instructionSize; - int space = count * 32; - char* temp = new char[space]; - - char* p = temp; - for (u32 pos = startAddr; pos < endAddr; pos += instructionSize) - { - p += sprintf(p, "%08X", cpu->read32(pos)); - - // Don't leave a trailing newline. - if (pos + instructionSize < endAddr) - p += sprintf(p, "\r\n"); - } - - wxTheClipboard->SetData(new wxTextDataObject(wxString(temp, wxConvUTF8))); - delete[] temp; - } - else - { - std::string disassembly = disassembleRange(startAddr, endAddr - startAddr); - wxTheClipboard->SetData(new wxTextDataObject(wxString(disassembly.c_str(), wxConvUTF8))); - } - - wxTheClipboard->Close(); -} - -void CtrlDisassemblyView::disassembleToFile() -{ - wxFileDialog dlg(this, wxEmptyString, wxEmptyString, wxEmptyString, L"*.*", wxFD_SAVE); - - if (dlg.ShowModal() == wxID_CANCEL) - return; - - std::string disassembly = disassembleRange(selectRangeStart, selectRangeEnd - selectRangeStart); - wxFile output(dlg.GetPath(), wxFile::write); - output.Write(wxString(disassembly.c_str(), wxConvUTF8)); -} - -void CtrlDisassemblyView::editBreakpoint() -{ - BreakpointWindow win(this, cpu); - - bool exists = false; - if (CBreakPoints::IsAddressBreakPoint(cpu->getCpuType(), curAddress)) - { - auto breakpoints = CBreakPoints::GetBreakpoints(); - for (size_t i = 0; i < breakpoints.size(); i++) - { - if (breakpoints[i].addr == curAddress) - { - win.loadFromBreakpoint(breakpoints[i]); - exists = true; - break; - } - } - } - - if (!exists) - win.initBreakpoint(curAddress); - - if (win.ShowModal() == wxID_OK) - { - if (exists) - CBreakPoints::RemoveBreakPoint(cpu->getCpuType(), curAddress); - win.addBreakpoint(); - postEvent(debEVT_UPDATE, 0); - } -} - -void CtrlDisassemblyView::getOpcodeText(u32 address, char* dest) -{ - DisassemblyLineInfo line = DisassemblyLineInfo(); - address = manager.getStartAddress(address); - manager.getLine(address, displaySymbols, line); - sprintf(dest, "%s %s", line.name.c_str(), line.params.c_str()); -} diff --git a/pcsx2/gui/Debugger/CtrlDisassemblyView.h b/pcsx2/gui/Debugger/CtrlDisassemblyView.h deleted file mode 100644 index 97b9e34d3c..0000000000 --- a/pcsx2/gui/Debugger/CtrlDisassemblyView.h +++ /dev/null @@ -1,111 +0,0 @@ -/* PCSX2 - PS2 Emulator for PCs - * Copyright (C) 2002-2014 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 . - */ - -#pragma once -#include - -#include "DebugTools/DebugInterface.h" -#include "DebugTools/DisassemblyManager.h" - -class CtrlDisassemblyView: public wxWindow -{ -public: - CtrlDisassemblyView(wxWindow* parent, DebugInterface* _cpu); - - void mouseEvent(wxMouseEvent& evt); - void paintEvent(wxPaintEvent & evt); - void keydownEvent(wxKeyEvent& evt); - void scrollbarEvent(wxScrollWinEvent& evt); - void sizeEvent(wxSizeEvent& evt); - void focusEvent(wxFocusEvent& evt) { Refresh(); }; -#ifdef _WIN32 - WXLRESULT MSWWindowProc(WXUINT nMsg, WXWPARAM wParam, WXLPARAM lParam); -#endif - - void scanFunctions(); - void clearFunctions() { manager.clear(); }; - void redraw(); - void getOpcodeText(u32 address, char* dest); - - u32 getInstructionSizeAt(u32 address) - { - u32 start = manager.getStartAddress(address); - u32 next = manager.getNthNextAddress(start,1); - return next-address; - } - - void gotoAddress(u32 addr); - void gotoPc() { gotoAddress(cpu->getPC()); }; - void scrollStepping(u32 newPc); - wxDECLARE_EVENT_TABLE(); -private: - void drawBranchLine(wxDC& dc, std::map& addressPositions, BranchLine& line); - void render(wxDC& dc); - void calculatePixelPositions(); - bool getDisasmAddressText(u32 address, char* dest, bool abbreviateLabels, bool showData); - u32 yToAddress(int y); - bool curAddressIsVisible(); - void followBranch(); - void toggleBreakpoint(bool toggleEnabled); - void updateStatusBarText(); - std::string disassembleRange(u32 start, u32 size); - std::string disassembleCurAddress(); - void copyInstructions(u32 startAddr, u32 endAddr, bool withDisasm); - void disassembleToFile(); - void editBreakpoint(); - std::set getSelectedLineArguments(); - void drawArguments(wxDC& dc, const DisassemblyLineInfo &line, int x, int y, wxColor& textColor, - const std::set ¤tArguments); - void assembleOpcode(u32 address, std::string defaultText); - int getBackgroundColor(unsigned int address); - - void postEvent(wxEventType type, wxString text); - void postEvent(wxEventType type, int value); - - void onPopupClick(wxCommandEvent& evt); - - void setCurAddress(u32 newAddress, bool extend = false) - { - newAddress = manager.getStartAddress(newAddress); - u32 after = manager.getNthNextAddress(newAddress,1); - curAddress = newAddress; - selectRangeStart = extend ? std::min(selectRangeStart, newAddress) : newAddress; - selectRangeEnd = extend ? std::max(selectRangeEnd, after) : after; - updateStatusBarText(); - } - - void scrollAddressIntoView(); - struct { - int addressStart; - int opcodeStart; - int argumentsStart; - int arrowsStart; - } pixelPositions; - - DebugInterface* cpu; - DisassemblyManager manager; - u32 windowStart; - u32 curAddress; - u32 selectRangeStart; - u32 selectRangeEnd; - int visibleRows; - int rowHeight; - int charWidth; - bool displaySymbols; - std::vector jumpStack; - - wxIcon bpEnabled,bpDisabled; - wxMenu menu; -}; diff --git a/pcsx2/gui/Debugger/CtrlMemSearch.cpp b/pcsx2/gui/Debugger/CtrlMemSearch.cpp deleted file mode 100644 index 8937c1f153..0000000000 --- a/pcsx2/gui/Debugger/CtrlMemSearch.cpp +++ /dev/null @@ -1,512 +0,0 @@ -/* PCSX2 - PS2 Emulator for PCs - * Copyright (C) 2002-2021 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 . - */ - -#include "PrecompiledHeader.h" -#include "CtrlMemSearch.h" -#include "DebugTools/Debug.h" -#include "gui/AppConfig.h" -#include "common/BitCast.h" -#include "common/StringUtil.h" -#include "Patch.h" // Required for SwapEndian :( - - -#include "BreakpointWindow.h" -#include "DebugEvents.h" -#include "DisassemblyDialog.h" -#include -#include - -wxDEFINE_EVENT(pxEvt_SearchFinished, wxCommandEvent); - -wxBEGIN_EVENT_TABLE(CtrlMemSearch, wxWindow) - EVT_SET_FOCUS(CtrlMemSearch::focusEvent) - EVT_KILL_FOCUS(CtrlMemSearch::focusEvent) - EVT_SIZE(CtrlMemSearch::sizeEvent) -wxEND_EVENT_TABLE() - -enum MemoryViewMenuIdentifiers { - ID_MEMVIEW_GOTOINDISASM = 1, - ID_MEMVIEW_GOTOADDRESS, - ID_MEMVIEW_COPYADDRESS, - ID_MEMVIEW_FOLLOWADDRESS, - ID_MEMVIEW_DISPLAYVALUE_8, - ID_MEMVIEW_DISPLAYVALUE_16, - ID_MEMVIEW_DISPLAYVALUE_32, - ID_MEMVIEW_DISPLAYVALUE_64, - ID_MEMVIEW_DISPLAYVALUE_128, - ID_MEMVIEW_COPYVALUE_8, - ID_MEMVIEW_COPYVALUE_16, - ID_MEMVIEW_COPYVALUE_32, - ID_MEMVIEW_COPYVALUE_64, - ID_MEMVIEW_COPYVALUE_128, - ID_MEMVIEW_ALIGNWINDOW, -}; - -// Kind of lame, but it works. -const wxString wxStringTypes[] = - { - "Byte", - "2 Bytes", - "4 Bytes", - "8 Bytes", - "Float", - "Double", - "String"}; -const wxArrayString SearchTypes = wxArrayString(7, wxStringTypes); - -// StringUtils::FromChars doesn't appreciate the leading 0x. -__fi wxString CheckHexadecimalString(wxString s) -{ - if(s.StartsWith("0x")) - return s.Remove(0, 2); - return s; -} - -CtrlMemSearch::CtrlMemSearch(wxWindow* parent, DebugInterface* _cpu) - : wxWindow(parent, wxID_ANY, wxDefaultPosition, wxDefaultSize) - , cpu(_cpu) -{ - - m_SearchThread.reset(new SearchThread(this, _cpu)); - m_SearchThread->SetName("Debugger Memory Search"); - - Bind(pxEvt_SearchFinished, &CtrlMemSearch::onSearchFinished, this); - - wxBoxSizer* mainSizer = new wxBoxSizer(wxVERTICAL); - - txtSearch = new wxTextCtrl(this, wxID_ANY, wxEmptyString, wxDefaultPosition, wxSize(250, -1)); - btnSearch = new wxButton(this, wxID_ANY, L"Search", wxDefaultPosition); - - Bind(wxEVT_BUTTON, &CtrlMemSearch::Search, this, btnSearch->GetId()); - - cmbSearchType = new wxComboBox(this, wxID_ANY, wxEmptyString, wxDefaultPosition, wxSize(250, -1), 0, NULL, wxCB_READONLY); - cmbSearchType->Set(SearchTypes); - cmbSearchType->SetSelection(0); - - Bind(wxEVT_COMBOBOX, &CtrlMemSearch::onSearchTypeChanged, this, cmbSearchType->GetId()); - - chkHexadecimal = new wxCheckBox(this, wxID_ANY, L"Hex"); - chkHexadecimal->SetValue(true); - - radBigEndian = new wxRadioButton(this, wxID_ANY, L"Big Endian", wxDefaultPosition, wxDefaultSize, wxRB_GROUP); - radLittleEndian = new wxRadioButton(this, wxID_ANY, L"Little Endian"); - radLittleEndian->SetValue(true); - - txtMemoryStart = new wxTextCtrl(this, wxID_ANY, wxEmptyString, wxDefaultPosition, wxSize(250, -1)); - txtMemoryStart->SetValue(L"0x0"); - txtMemoryEnd = new wxTextCtrl(this, wxID_ANY, wxEmptyString, wxDefaultPosition, wxSize(250, -1)); - if (_cpu->getCpuType() == BREAKPOINT_EE) - txtMemoryEnd->SetValue(L"0x200000"); - else - txtMemoryEnd->SetValue(L"0x20000"); - - lblSearchHits = new wxStaticText(this, wxID_ANY, L"Hits: 0"); - lblSearchHitSelected = new wxStaticText(this, wxID_ANY, L"Cur: 0"); - - btnNext = new wxButton(this, wxID_ANY, L"Next", wxDefaultPosition); - btnNext->Enable(false); - Bind(wxEVT_BUTTON, &CtrlMemSearch::onSearchNext, this, btnNext->GetId()); - btnPrev = new wxButton(this, wxID_ANY, L"Prev", wxDefaultPosition); - btnPrev->Enable(false); - Bind(wxEVT_BUTTON, &CtrlMemSearch::onSearchPrev, this, btnPrev->GetId()); - - // GUI design - - // Search box and search button - wxBoxSizer* searchSizer = new wxBoxSizer(wxHORIZONTAL); - searchSizer->Add(txtSearch, 0); - searchSizer->Add(btnSearch, 0, wxLEFT, 5); - - mainSizer->Add(searchSizer, 0, wxUP | wxLEFT | wxRIGHT, 5); - - // Search type combo box and hexadecimal checkbox - wxBoxSizer* searchTypeSizer = new wxBoxSizer(wxHORIZONTAL); - searchTypeSizer->Add(cmbSearchType, 0, wxEXPAND); - searchTypeSizer->Add(chkHexadecimal, 0, wxLEFT | wxALIGN_CENTER_VERTICAL, 5); - - mainSizer->Add(searchTypeSizer, 0, wxUP | wxLEFT | wxRIGHT, 5); - - // Big / Little Endianess radio button - wxBoxSizer* endianessSizer = new wxBoxSizer(wxHORIZONTAL); - endianessSizer->Add(radLittleEndian, 0, wxALIGN_CENTER_VERTICAL); - endianessSizer->Add(radBigEndian, 0, wxLEFT | wxALIGN_CENTER_VERTICAL, 5); - - mainSizer->Add(endianessSizer, 0, wxUP | wxLEFT | wxRIGHT, 5); - - // Address range type text boxes - wxFlexGridSizer* addrRangeSizer = new wxFlexGridSizer(2, 2, 5); - - addrRangeSizer->Add(new wxStaticText(this, wxID_ANY, L"Start Address"), 0, wxALIGN_RIGHT | wxALIGN_CENTER_VERTICAL); - addrRangeSizer->Add(txtMemoryStart, 0, wxEXPAND); - - addrRangeSizer->Add(new wxStaticText(this, wxID_ANY, L"End Address"), 0, wxALIGN_RIGHT | wxALIGN_CENTER_VERTICAL); - addrRangeSizer->Add(txtMemoryEnd, 0, wxEXPAND); - - mainSizer->Add(addrRangeSizer, 0, wxUP | wxLEFT | wxRIGHT, 5); - - wxBoxSizer* resultSizer = new wxBoxSizer(wxHORIZONTAL); - resultSizer->Add(lblSearchHits, 0, wxALIGN_CENTER_VERTICAL | wxRIGHT, 5); - resultSizer->Add(lblSearchHitSelected, 0, wxALIGN_CENTER_VERTICAL | wxRIGHT, 5); - resultSizer->Add(btnPrev, 0, wxRIGHT, 5); - resultSizer->Add(btnNext, 0, wxRIGHT, 5); - - mainSizer->Add(resultSizer, 0, wxUP | wxLEFT | wxRIGHT, 5); - this->SetSizer(mainSizer); - SetDoubleBuffered(true); -} - -void CtrlMemSearch::postEvent(wxEventType type, wxString text) -{ - wxCommandEvent event(type, GetId()); - event.SetEventObject(this); - event.SetClientData(cpu); - event.SetString(text); - wxPostEvent(this, event); -} - -void CtrlMemSearch::postEvent(wxEventType type, int value) -{ - wxCommandEvent event(type, GetId()); - event.SetEventObject(this); - event.SetClientData(cpu); - event.SetInt(value); - wxPostEvent(this, event); -} - -// Set up the thread with out UI params -// Then execute the thread -void CtrlMemSearch::Search(wxCommandEvent& evt) -{ - if (!m_searchResultsMutex.try_lock()) - { - // Uh, if it doesn't lock then means we somehow tried to search while - // the search was still running. - return; - } - m_searchResults.clear(); - m_searchResultsMutex.unlock(); - - lblSearchHits->SetLabelText(L"Hits: 0"); - btnNext->Enable(false); - btnPrev->Enable(false); - - u64 startAddress = 0; - u64 endAddress = 0; - - const auto resStart = StringUtil::FromChars(CheckHexadecimalString(txtMemoryStart->GetValue()).ToStdString(), 16); - if (!resStart.has_value()) - { - wxMessageBox(L"Invalid start address", L"Error", wxOK | wxICON_ERROR); - return; - } - startAddress = resStart.value(); - - const auto resEnd = StringUtil::FromChars(CheckHexadecimalString(txtMemoryEnd->GetValue()).ToStdString(), 16); - if (!resStart.has_value()) - { - wxMessageBox(L"Invalid end address", L"Error", wxOK | wxICON_ERROR); - return; - } - endAddress = resEnd.value(); - - m_SearchThread->m_start = startAddress; - m_SearchThread->m_end = endAddress; - m_SearchThread->m_type = static_cast(cmbSearchType->GetSelection()); - - // Prepare the search value for the thread - - wxString searchString = txtSearch->GetValue(); - if (m_SearchThread->m_type == SEARCHTYPE::STRING) - { - m_SearchThread->m_value_string = searchString.ToUTF8(); - } - else if (chkHexadecimal->IsChecked()) - { - const auto res = StringUtil::FromChars(CheckHexadecimalString(txtSearch->GetValue()).ToStdString(), 16); - if (!res.has_value()) - { - wxMessageBox(L"Invalid hexadecimal value", L"Error", wxOK | wxICON_ERROR); - return; - } - m_SearchThread->m_value = radBigEndian->GetValue() ? SwapEndian(res.value(), SEARCHTYPEBITS[cmbSearchType->GetSelection()]) : res.value(); - } - else - { - // According to my logic, we end up here if we aren't searching for a string - // Aren't in hexadecimal mode. - - // Let's check if we're searching for a signed or unsigned value - // The thread will handle the interpretation for us. - m_SearchThread->m_signed = txtSearch->GetValue().First('-'); - - // Now we can convert the string to a number - - if (m_SearchThread->m_type == SEARCHTYPE::FLOAT || m_SearchThread->m_type == SEARCHTYPE::DOUBLE) - { - // We're searching for a float or double, so we need to convert the string to a double - const auto res = StringUtil::FromChars(txtSearch->GetValue().ToStdString()); - if (!res.has_value()) - { - wxMessageBox(L"Invalid floating point value", L"Error", wxOK | wxICON_ERROR); - return; - } - - if (m_SearchThread->m_type == SEARCHTYPE::FLOAT) - { - const u32 u32SearchValue = bit_cast(res.value()); - m_SearchThread->m_value = u32SearchValue; - } - else - { - const u64 u64SearchValue = bit_cast(res.value()); - m_SearchThread->m_value = u64SearchValue; - } - } - else - { - // We're searching for either a BYTE, WORD, DWORD or QWORD - - const auto res = StringUtil::FromChars(txtSearch->GetValue().ToStdString()); - if(!res.has_value()) - { - wxMessageBox(L"Invalid decimal search value", L"Error", wxOK | wxICON_ERROR); - return; - } - - // We don't need to bitcast here, because the thread already has the correct value type of u64 - m_SearchThread->m_value = radBigEndian->GetValue() ? SwapEndian(res.value(), SEARCHTYPEBITS[cmbSearchType->GetSelection()]) : res.value(); - } - } - - m_SearchThread->Start(); -}; - -void CtrlMemSearch::onSearchFinished(wxCommandEvent& evt) -{ - // We're done searching, so let's update the UI - - m_searchResultsMutex.lock(); - - lblSearchHits->SetLabelText(wxString::Format(L"Hits: %zu", m_searchResults.size())); - - // Enable the buttons only if we have results - // -1 indicates we haven't jumped to a result yet - m_searchIter = -1; - lblSearchHitSelected->SetLabelText(L"Cur: 0"); - btnNext->Enable(m_searchResults.size() > 0); - btnPrev->Enable(m_searchResults.size() > 0); - - m_searchResultsMutex.unlock(); - - this->Layout(); // This makes sure the search hit count label doesn't get cut off -} - -void CtrlMemSearch::onSearchNext(wxCommandEvent& evt) -{ - m_searchIter++; - if (m_searchIter == m_searchResults.size()) - { - m_searchIter = 0; - } - - lblSearchHitSelected->SetLabelText(wxString::Format(L"Cur: %d",m_searchIter + 1)); - this->Layout(); - postEvent(debEVT_GOTOINMEMORYVIEW, m_searchResults[m_searchIter]); -} - -void CtrlMemSearch::onSearchPrev(wxCommandEvent& evt) -{ - m_searchIter--; - // Unsigned underflow - if (m_searchIter > m_searchResults.size()) - { - m_searchIter = m_searchResults.size() - 1; - } - - lblSearchHitSelected->SetLabelText(wxString::Format(L"Cur: %d",m_searchIter + 1)); - this->Layout(); - postEvent(debEVT_GOTOINMEMORYVIEW, m_searchResults[m_searchIter]); -} - -void CtrlMemSearch::onSearchTypeChanged(wxCommandEvent& evt) -{ - // Update the search value text box to match the search type - switch (static_cast(cmbSearchType->GetSelection())) - { - case SEARCHTYPE::BYTE: - case SEARCHTYPE::WORD: - case SEARCHTYPE::DWORD: - case SEARCHTYPE::QWORD: - radBigEndian->Enable(true); - radLittleEndian->Enable(true); - chkHexadecimal->Enable(true); - break; - case SEARCHTYPE::FLOAT: - case SEARCHTYPE::DOUBLE: - case SEARCHTYPE::STRING: - radBigEndian->Enable(false); - radLittleEndian->Enable(false); - chkHexadecimal->Enable(false); - chkHexadecimal->SetValue(false); - break; - } -} - -// -- SearchThread - -// There might be a prettier way to read from the vtlb -template -void memSearch(std::vector& out, u32 start, u32 end, T value, DebugInterface* cpu) -{ - for (u32 addr = start; addr < end; addr += sizeof(T)) - { - if (!cpu->isValidAddress(addr)) - { - continue; - } - - T val = 0; - if (sizeof(T) == 1) - { - val = cpu->read8(addr); - } - else if (sizeof(T) == 2) - { - val = cpu->read16(addr); - } - else if (sizeof(T) == 4) - { - if (std::is_same::value) - { - float fpVal = bit_cast(value); - // Love floating point - if (bit_cast(cpu->read32(addr)) < fpVal + 0.001f && bit_cast(cpu->read32(addr)) > fpVal - 0.001f) - { - out.push_back(addr); - } - continue; - } - - val = cpu->read32(addr); - } - else if (sizeof(T) == 8) - { - if (std::is_same::value) - { - double dbVal = bit_cast(value); - if (bit_cast(cpu->read64(addr)) < dbVal + 0.001 && bit_cast(cpu->read64(addr)) > dbVal - 0.001) - { - out.push_back(addr); - } - continue; - } - - val = cpu->read64(addr); - } - - if (val == value) - { - out.push_back(addr); - } - } -} - -void SearchThread::ExecuteTaskInThread() -{ - // First, make sure we can lock the mutex - if (!m_parent->m_searchResultsMutex.try_lock()) - { - // We couldn't lock the mutex, this thread either didn't unlock, or we've somehow raced - // with the main UI thread ? - assert("Failed to lock our memory search mutex?"); - return; - } - - switch (m_type) - { - case SEARCHTYPE::BYTE: - if (m_signed) - { - memSearch(m_parent->m_searchResults, m_start, m_end, m_value, m_cpu); - } - else - { - memSearch(m_parent->m_searchResults, m_start, m_end, m_value, m_cpu); - } - break; - case SEARCHTYPE::WORD: - if (m_signed) - { - memSearch(m_parent->m_searchResults, m_start, m_end, m_value, m_cpu); - } - else - { - memSearch(m_parent->m_searchResults, m_start, m_end, m_value, m_cpu); - } - break; - case SEARCHTYPE::DWORD: - if (m_signed) - { - memSearch(m_parent->m_searchResults, m_start, m_end, m_value, m_cpu); - } - else - { - memSearch(m_parent->m_searchResults, m_start, m_end, m_value, m_cpu); - } - break; - case SEARCHTYPE::QWORD: - if (m_signed) - { - memSearch(m_parent->m_searchResults, m_start, m_end, m_value, m_cpu); - } - else - { - memSearch(m_parent->m_searchResults, m_start, m_end, m_value, m_cpu); - } - break; - case SEARCHTYPE::FLOAT: - memSearch(m_parent->m_searchResults, m_start, m_end, m_value, m_cpu); - break; - case SEARCHTYPE::DOUBLE: - memSearch(m_parent->m_searchResults, m_start, m_end, m_value, m_cpu); - break; - case SEARCHTYPE::STRING: - for (u32 addr = m_start; addr < m_end; addr++) - { - // There is no easy way to do this with the vtable. - bool match = true; - for (u32 stringIndex = 0; stringIndex < m_value_string.size(); stringIndex++) - { - if ((char)m_cpu->read8(addr + stringIndex) != m_value_string[stringIndex]) - { - match = false; - break; - } - } - - if (match) - { - m_parent->m_searchResults.push_back(addr); - addr += m_value_string.size() - 1; - } - } - break; - } - - m_parent->m_searchResultsMutex.unlock(); - - wxCommandEvent done(pxEvt_SearchFinished); - m_parent->GetEventHandler()->AddPendingEvent(done); -} diff --git a/pcsx2/gui/Debugger/CtrlMemSearch.h b/pcsx2/gui/Debugger/CtrlMemSearch.h deleted file mode 100644 index 7411ac73d4..0000000000 --- a/pcsx2/gui/Debugger/CtrlMemSearch.h +++ /dev/null @@ -1,109 +0,0 @@ -/* PCSX2 - PS2 Emulator for PCs - * Copyright (C) 2002-2021 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 . - */ - -#pragma once -#include -#include "DebugTools/DebugInterface.h" -#include "DebugTools/DisassemblyManager.h" -#include "gui/PersistentThread.h" - -enum class SEARCHTYPE -{ - BYTE, - WORD, - DWORD, - QWORD, - FLOAT, - DOUBLE, - STRING, -}; - -class CtrlMemSearch; - -class SearchThread : public Threading::pxThread -{ - CtrlMemSearch* m_parent; - DebugInterface* m_cpu; -public: - virtual ~SearchThread() - { - try - { - pxThread::Cancel(); - } - DESTRUCTOR_CATCHALL - } - SearchThread(CtrlMemSearch* parent, DebugInterface* cpu) { m_parent = parent; m_cpu = cpu; }; - - SEARCHTYPE m_type; - // Only guaranteed to be valid if m_type == STRING - std::string m_value_string; - u64 m_value; - bool m_signed; - u64 m_start,m_end; -protected: - void ExecuteTaskInThread(); -}; - -class CtrlMemSearch : public wxWindow -{ -public: - CtrlMemSearch(wxWindow* parent, DebugInterface* _cpu); - - std::vector m_searchResults; - std::mutex m_searchResultsMutex; - wxDECLARE_EVENT_TABLE(); - - -private: - void setRowSize(int bytesInRow); - void postEvent(wxEventType type, wxString text); - void postEvent(wxEventType type, int value); - void onPopupClick(wxCommandEvent& evt); - void focusEvent(wxFocusEvent& evt) { Refresh(); }; - void sizeEvent(wxSizeEvent& evt) - { - // Why in the world do I have to do this. - // Without this the window isn't redrawn during a size event - Refresh(); // Reloads the window, fixes the imporper drawing but ruins the layout - Layout(); // Fix the layout of the window - }; - void onSearchFinished(wxCommandEvent& evt); - void onSearchNext(wxCommandEvent& evt); - void onSearchPrev(wxCommandEvent& evt); - void onSearchTypeChanged(wxCommandEvent& evt); - - void Search(wxCommandEvent& evt); - const u8 SEARCHTYPEBITS[8] = {8,16,32,64,32,64,0}; - - DebugInterface* cpu; - wxFont font, underlineFont; - wxTextCtrl* txtSearch; - wxButton* btnSearch; - wxComboBox* cmbSearchType; - wxCheckBox* chkHexadecimal; - wxTextCtrl* txtMemoryStart; - wxTextCtrl* txtMemoryEnd; - wxRadioButton* radBigEndian; - wxRadioButton* radLittleEndian; - wxStaticText* lblSearchHits; - wxStaticText* lblSearchHitSelected; - wxButton* btnNext; - wxButton* btnPrev; - - u64 m_searchIter = -1; - std::unique_ptr m_SearchThread; - wxMenu menu; -}; diff --git a/pcsx2/gui/Debugger/CtrlMemView.cpp b/pcsx2/gui/Debugger/CtrlMemView.cpp deleted file mode 100644 index 7bb1e0e5c9..0000000000 --- a/pcsx2/gui/Debugger/CtrlMemView.cpp +++ /dev/null @@ -1,830 +0,0 @@ -/* PCSX2 - PS2 Emulator for PCs - * Copyright (C) 2002-2021 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 . - */ - -#include "PrecompiledHeader.h" -#include "CtrlMemView.h" -#include "DebugTools/Debug.h" -#include "gui/AppConfig.h" - -#include "BreakpointWindow.h" -#include "DebugEvents.h" -#include "DisassemblyDialog.h" -#include -#include - - -wxBEGIN_EVENT_TABLE(CtrlMemView, wxWindow) - EVT_PAINT(CtrlMemView::paintEvent) - EVT_MOUSEWHEEL(CtrlMemView::mouseEvent) - EVT_LEFT_DOWN(CtrlMemView::mouseEvent) - EVT_LEFT_DCLICK(CtrlMemView::mouseEvent) - EVT_RIGHT_DOWN(CtrlMemView::mouseEvent) - EVT_RIGHT_UP(CtrlMemView::mouseEvent) - EVT_KEY_DOWN(CtrlMemView::keydownEvent) - EVT_CHAR(CtrlMemView::charEvent) - EVT_SET_FOCUS(CtrlMemView::focusEvent) - EVT_KILL_FOCUS(CtrlMemView::focusEvent) - EVT_SCROLLWIN_LINEUP(CtrlMemView::scrollbarEvent) - EVT_SCROLLWIN_LINEDOWN(CtrlMemView::scrollbarEvent) - EVT_SCROLLWIN_PAGEUP(CtrlMemView::scrollbarEvent) - EVT_SCROLLWIN_PAGEDOWN(CtrlMemView::scrollbarEvent) -wxEND_EVENT_TABLE() - -enum MemoryViewMenuIdentifiers -{ - ID_MEMVIEW_GOTOINDISASM = 1, - ID_MEMVIEW_GOTOADDRESS, - ID_MEMVIEW_COPYADDRESS, - ID_MEMVIEW_FOLLOWADDRESS, - ID_MEMVIEW_DISPLAYVALUE_8, - ID_MEMVIEW_DISPLAYVALUE_16, - ID_MEMVIEW_DISPLAYVALUE_32, - ID_MEMVIEW_DISPLAYVALUE_64, - ID_MEMVIEW_DISPLAYVALUE_128, - ID_MEMVIEW_COPYVALUE_8, - ID_MEMVIEW_COPYVALUE_16, - ID_MEMVIEW_COPYVALUE_32, - ID_MEMVIEW_COPYVALUE_64, - ID_MEMVIEW_COPYVALUE_128, - ID_MEMVIEW_ALIGNWINDOW, -}; - -CtrlMemView::CtrlMemView(wxWindow* parent, DebugInterface* _cpu) - : wxWindow(parent, wxID_ANY, wxDefaultPosition, wxDefaultSize, wxWANTS_CHARS | wxVSCROLL) - , cpu(_cpu) -{ - rowHeight = getDebugFontHeight(); - charWidth = getDebugFontWidth(); - windowStart = 0x480000; - curAddress = windowStart; - byteGroupSize = 1; - - asciiSelected = false; - selectedNibble = 0; - addressStart = charWidth; - hexStart = addressStart + 9 * charWidth; - - setRowSize(g_Conf->EmuOptions.Debugger.MemoryViewBytesPerRow); - - font = pxGetFixedFont(8); - underlineFont = pxGetFixedFont(8, wxFONTWEIGHT_NORMAL, true); - font.SetPixelSize(wxSize(charWidth, rowHeight)); - underlineFont.SetPixelSize(wxSize(charWidth, rowHeight)); - - menu.Append(ID_MEMVIEW_GOTOINDISASM, L"Go to in Disasm"); - menu.Append(ID_MEMVIEW_GOTOADDRESS, L"Go to address"); - menu.Append(ID_MEMVIEW_COPYADDRESS, L"Copy address"); - menu.Append(ID_MEMVIEW_FOLLOWADDRESS, L"Follow address"); - menu.AppendSeparator(); - menu.AppendRadioItem(ID_MEMVIEW_DISPLAYVALUE_8, L"Display as 1 byte"); - menu.AppendRadioItem(ID_MEMVIEW_DISPLAYVALUE_16, L"Display as 2 byte"); - menu.AppendRadioItem(ID_MEMVIEW_DISPLAYVALUE_32, L"Display as 4 byte"); - menu.AppendRadioItem(ID_MEMVIEW_DISPLAYVALUE_64, L"Display as 8 byte"); - menu.AppendRadioItem(ID_MEMVIEW_DISPLAYVALUE_128, L"Display as 16 byte"); - menu.Check(ID_MEMVIEW_DISPLAYVALUE_8, true); - menu.AppendSeparator(); - menu.Append(ID_MEMVIEW_COPYVALUE_8, L"Copy Value (8 bit)"); - menu.Append(ID_MEMVIEW_COPYVALUE_16, L"Copy Value (16 bit)"); - menu.Append(ID_MEMVIEW_COPYVALUE_32, L"Copy Value (32 bit)"); - menu.Append(ID_MEMVIEW_COPYVALUE_64, L"Copy Value (64 bit)"); - menu.Append(ID_MEMVIEW_COPYVALUE_128, L"Copy Value (128 bit)"); - menu.AppendSeparator(); - menu.AppendCheckItem(ID_MEMVIEW_ALIGNWINDOW, L"Align window to row size"); - menu.Check(ID_MEMVIEW_ALIGNWINDOW, g_Conf->EmuOptions.Debugger.AlignMemoryWindowStart); - menu.Bind(wxEVT_MENU, &CtrlMemView::onPopupClick, this); - - SetScrollbar(wxVERTICAL, 100, 1, 201, true); - SetDoubleBuffered(true); -} - -void CtrlMemView::setRowSize(int bytesInRow) -{ - rowSize = (std::max(16, std::min(256, bytesInRow)) / 16) * 16; - asciiStart = hexStart + (rowSize * 3 + 1) * charWidth; -} - -void CtrlMemView::postEvent(wxEventType type, wxString text) -{ - wxCommandEvent event(type, GetId()); - event.SetEventObject(this); - event.SetClientData(cpu); - event.SetString(text); - wxPostEvent(this, event); -} - -void CtrlMemView::postEvent(wxEventType type, int value) -{ - wxCommandEvent event(type, GetId()); - event.SetEventObject(this); - event.SetClientData(cpu); - event.SetInt(value); - wxPostEvent(this, event); -} - -void CtrlMemView::paintEvent(wxPaintEvent& evt) -{ - wxPaintDC dc(this); - render(dc); -} - -void CtrlMemView::redraw() -{ - wxClientDC dc(this); - render(dc); -} - -int CtrlMemView::hexGroupPositionFromIndex(int idx) -{ - int groupPos = idx * charWidth * 2; - - int space = (charWidth / 4); - - // spaces after every byte - groupPos += idx * space; - - // spaces after every 2 bytes - groupPos += (idx / 2) * space; - - // spaces after every 4 bytes - return groupPos + (idx / 4) * space; -} - -void CtrlMemView::render(wxDC& dc) -{ - bool hasFocus = wxWindow::FindFocus() == this; - int visibleRows = GetClientSize().y / rowHeight; - - const wxColor COLOR_WHITE = wxColor(0xFFFFFFFF); - const wxColor COLOR_BLACK = wxColor(0xFF000000); - const wxColor COLOR_SELECTED_BG = wxColor(0xFFFF9933); - const wxColor COLOR_SELECTED_INACTIVE_BG = wxColor(0xFFC0C0C0); - const wxColor COLOR_REFRENCE_BG = wxColor(0xFFFFCFC8); - const wxColor COLOR_ADDRESS = wxColor(0xFF600000); - const wxColor COLOR_DELIMETER = wxColor(0xFFC0C0C0); - - dc.SetBrush(wxBrush(COLOR_WHITE)); - dc.SetPen(wxPen(COLOR_WHITE)); - - int width, height; - dc.GetSize(&width, &height); - dc.DrawRectangle(0, 0, width, height); - - const int TEMP_SIZE = 64; - wchar_t temp[TEMP_SIZE]; - - bool validCpu = cpu && cpu->isAlive(); - - // not hexGroupPositionFromIndex(byteGroupSize), because we dont need space after last symbol; - int groupWidth = hexGroupPositionFromIndex(byteGroupSize - 1) + charWidth * 2; - - for (int i = 0; i < visibleRows + 1; i++) - { - u32 rowAddress = windowStart + i * rowSize; - int rowY = rowHeight * i; - - swprintf(temp, TEMP_SIZE, L"%08X", rowAddress); - dc.SetFont(font); - dc.SetTextForeground(COLOR_ADDRESS); - dc.DrawText(temp, addressStart, rowY); - - for (int j = 0; j < rowSize; j++) - { - u32 byteAddress = rowAddress + j; - u8 byteCurrent = 0; - bool byteValid = false; - - try - { - byteValid = validCpu && cpu->isValidAddress(byteAddress); - - if (byteValid) - byteCurrent = cpu->read8(byteAddress); - } - catch (Exception::Ps2Generic&) - { - byteValid = false; - } - - // not optimized way, but more flexible than previous - - // calculate group position - int groupNum = j / byteGroupSize; - int groupPosX = hexStart + groupNum * byteGroupSize * 3 * charWidth; - - // calculate symbol position in group - int groupIndex = j % byteGroupSize; - - int symbolPosX = groupPosX + hexGroupPositionFromIndex(byteGroupSize - groupIndex - 1); - - u32 groupAddress = byteAddress - groupIndex; - - bool backgroundIsDark = false; - if (curAddress >= groupAddress && curAddress < groupAddress + byteGroupSize) - { - // if group selected, draw rectangle behind - if (groupIndex == 0) - { - if (hasFocus && !asciiSelected) - { - dc.SetPen(COLOR_SELECTED_BG); - dc.SetBrush(COLOR_SELECTED_BG); - } - else - { - dc.SetPen(COLOR_SELECTED_INACTIVE_BG); - dc.SetBrush(COLOR_SELECTED_INACTIVE_BG); - } - - dc.DrawRectangle(groupPosX, rowY, groupWidth, rowHeight); - } - - backgroundIsDark = hasFocus && !asciiSelected; - } - if (groupAddress + groupIndex == referencedAddress) - { - dc.SetPen(COLOR_REFRENCE_BG); - dc.SetBrush(COLOR_REFRENCE_BG); - dc.DrawRectangle(symbolPosX, rowY, charWidth * 2, rowHeight); - backgroundIsDark = false; - } - - dc.SetTextForeground(backgroundIsDark ? COLOR_WHITE : COLOR_BLACK); - - swprintf(temp, TEMP_SIZE, byteValid ? L"%02X" : L"??", byteCurrent); - // if selected byte, need hint current nibble - if (byteAddress == curAddress) - { - if (selectedNibble == 1) - dc.SetFont(underlineFont); - - dc.DrawText(temp + 1, symbolPosX + charWidth, rowY); - - if (selectedNibble == 1) - dc.SetFont(font); - else - dc.SetFont(underlineFont); - - temp[1] = 0; - dc.DrawText(temp, symbolPosX, rowY); - - if (selectedNibble == 0) - dc.SetFont(font); - } - else - { - dc.DrawText(temp, symbolPosX, rowY); - } - - // draw in ansii text representation table - temp[1] = 0; - temp[0] = (!byteValid || byteCurrent < 32 || byteCurrent > 128) ? '.' : byteCurrent; - - if (byteAddress == curAddress) - { - if (hasFocus && asciiSelected) - { - dc.SetPen(COLOR_SELECTED_BG); - dc.SetBrush(COLOR_SELECTED_BG); - dc.SetTextForeground(COLOR_WHITE); - } - else - { - dc.SetPen(COLOR_SELECTED_INACTIVE_BG); - dc.SetBrush(COLOR_SELECTED_INACTIVE_BG); - dc.SetTextForeground(COLOR_BLACK); - } - dc.DrawRectangle(asciiStart + j * (charWidth + 2), rowY, charWidth, rowHeight); - } - else - { - dc.SetTextForeground(COLOR_BLACK); - } - - dc.DrawText(temp, asciiStart + j * (charWidth + 2), rowY); - } - } - - dc.SetPen(COLOR_DELIMETER); - dc.SetBrush(COLOR_DELIMETER); - int linestep = std::max((u32)4, byteGroupSize); - for (int i = linestep; i < rowSize; i += linestep) - { - int x = hexStart + i * 3 * charWidth - charWidth / 2; - int y = (visibleRows + 1) * rowHeight; - dc.DrawLine(x, 0, x, y); - } -} - -void CtrlMemView::onPopupClick(wxCommandEvent& evt) -{ - wchar_t str[64]; - - switch (evt.GetId()) - { - case ID_MEMVIEW_COPYADDRESS: - if (wxTheClipboard->Open()) - { - swprintf(str, 64, L"%08X", curAddress); - wxTheClipboard->SetData(new wxTextDataObject(str)); - wxTheClipboard->Close(); - } - break; - case ID_MEMVIEW_GOTOINDISASM: - postEvent(debEVT_GOTOINDISASM, curAddress); - break; - case ID_MEMVIEW_GOTOADDRESS: - postEvent(debEVT_GOTOADDRESS, curAddress); - break; - case ID_MEMVIEW_FOLLOWADDRESS: - gotoAddress(cpu->read32(curAddress), true); - break; - case ID_MEMVIEW_DISPLAYVALUE_8: - byteGroupSize = 1; - Refresh(); - break; - case ID_MEMVIEW_DISPLAYVALUE_16: - byteGroupSize = 2; - Refresh(); - break; - case ID_MEMVIEW_DISPLAYVALUE_32: - byteGroupSize = 4; - Refresh(); - break; - case ID_MEMVIEW_DISPLAYVALUE_64: - byteGroupSize = 8; - Refresh(); - break; - case ID_MEMVIEW_DISPLAYVALUE_128: - byteGroupSize = 16; - Refresh(); - break; - case ID_MEMVIEW_COPYVALUE_8: - if (wxTheClipboard->Open()) - { - swprintf(str, 64, L"%02X", cpu->read8(curAddress)); - wxTheClipboard->SetData(new wxTextDataObject(str)); - wxTheClipboard->Close(); - } - break; - case ID_MEMVIEW_COPYVALUE_16: - if (wxTheClipboard->Open()) - { - swprintf(str, 64, L"%04X", cpu->read16(curAddress)); - wxTheClipboard->SetData(new wxTextDataObject(str)); - wxTheClipboard->Close(); - } - break; - case ID_MEMVIEW_COPYVALUE_32: - if (wxTheClipboard->Open()) - { - swprintf(str, 64, L"%08X", cpu->read32(curAddress)); - wxTheClipboard->SetData(new wxTextDataObject(str)); - wxTheClipboard->Close(); - } - break; - case ID_MEMVIEW_COPYVALUE_64: - if (wxTheClipboard->Open()) - { - swprintf(str, 64, L"%016llX", cpu->read64(curAddress)); - wxTheClipboard->SetData(new wxTextDataObject(str)); - wxTheClipboard->Close(); - } - break; - case ID_MEMVIEW_COPYVALUE_128: - if (wxTheClipboard->Open()) - { - u128 value = cpu->read128(curAddress); - swprintf(str, 64, L"%016llX%016llX", value._u64[1], value._u64[0]); - wxTheClipboard->SetData(new wxTextDataObject(str)); - wxTheClipboard->Close(); - } - break; - case ID_MEMVIEW_ALIGNWINDOW: - g_Conf->EmuOptions.Debugger.AlignMemoryWindowStart = evt.IsChecked(); - if (g_Conf->EmuOptions.Debugger.AlignMemoryWindowStart) - { - windowStart -= windowStart % rowSize; - redraw(); - } - break; - } -} - -void CtrlMemView::mouseEvent(wxMouseEvent& evt) -{ - // left button - if (evt.GetEventType() == wxEVT_LEFT_DOWN || evt.GetEventType() == wxEVT_LEFT_DCLICK || evt.GetEventType() == wxEVT_RIGHT_DOWN || evt.GetEventType() == wxEVT_RIGHT_DCLICK) - { - gotoPoint(evt.GetPosition().x, evt.GetPosition().y); - SetFocus(); - SetFocusFromKbd(); - } - else if (evt.GetEventType() == wxEVT_RIGHT_UP) - { - curAddress -= (curAddress - windowStart) % byteGroupSize; - - menu.Enable(ID_MEMVIEW_FOLLOWADDRESS, (curAddress & 3) == 0); - - menu.Check(ID_MEMVIEW_DISPLAYVALUE_8, byteGroupSize == 1); - menu.Check(ID_MEMVIEW_DISPLAYVALUE_16, byteGroupSize == 2); - menu.Check(ID_MEMVIEW_DISPLAYVALUE_32, byteGroupSize == 4); - menu.Check(ID_MEMVIEW_DISPLAYVALUE_64, byteGroupSize == 8); - menu.Check(ID_MEMVIEW_DISPLAYVALUE_128, byteGroupSize == 16); - - menu.Enable(ID_MEMVIEW_COPYVALUE_128, (curAddress & 15) == 0); - menu.Enable(ID_MEMVIEW_COPYVALUE_64, (curAddress & 7) == 0); - menu.Enable(ID_MEMVIEW_COPYVALUE_32, (curAddress & 3) == 0); - menu.Enable(ID_MEMVIEW_COPYVALUE_16, (curAddress & 1) == 0); - - menu.Check(ID_MEMVIEW_ALIGNWINDOW, g_Conf->EmuOptions.Debugger.AlignMemoryWindowStart); - - PopupMenu(&menu); - return; - } - else if (evt.GetEventType() == wxEVT_MOUSEWHEEL) - { - if (evt.ControlDown()) - { - if (evt.GetWheelRotation() > 0) - { - setRowSize(rowSize + 16); - } - else - { - setRowSize(rowSize - 16); - } - } - else - { - if (evt.GetWheelRotation() > 0) - { - scrollWindow(-3); - } - else if (evt.GetWheelRotation() < 0) - { - scrollWindow(3); - } - } - } - else - { - evt.Skip(); - return; - } - - redraw(); -} - -void CtrlMemView::keydownEvent(wxKeyEvent& evt) -{ - if (evt.ControlDown()) - { - switch (evt.GetKeyCode()) - { - case 'g': - case 'G': - { - u64 addr; - if (!executeExpressionWindow(this, cpu, addr)) - return; - - gotoAddress(addr, true); - } - break; - case 'b': - case 'B': - { - BreakpointWindow bpw(this, cpu); - if (bpw.ShowModal() == wxID_OK) - { - bpw.addBreakpoint(); - postEvent(debEVT_UPDATE, 0); - } - } - break; - case 'v': - case 'V': - pasteHex(); - break; - default: - evt.Skip(); - break; - } - return; - } - - switch (evt.GetKeyCode()) - { - case 'g': - case 'G': - { - u64 addr; - if (!executeExpressionWindow(this, cpu, addr)) - return; - gotoAddress(addr, true); - } - break; - case WXK_LEFT: - scrollCursor(-1); - break; - case WXK_RIGHT: - scrollCursor(1); - break; - case WXK_UP: - scrollCursor(-rowSize); - break; - case WXK_DOWN: - scrollCursor(rowSize); - break; - case WXK_PAGEUP: - scrollWindow(-GetClientSize().y / rowHeight); - break; - case WXK_PAGEDOWN: - scrollWindow(GetClientSize().y / rowHeight); - break; - case WXK_ESCAPE: - if (!history.empty()) - { - gotoAddress(history.top()); - history.pop(); - } - break; - default: - evt.Skip(); - break; - } -} - -void CtrlMemView::charEvent(wxKeyEvent& evt) -{ - if (evt.GetKeyCode() < 32) - return; - - if (!cpu->isValidAddress(curAddress)) - { - scrollCursor(1); - return; - } - - bool active = !cpu->isCpuPaused(); - if (active) - cpu->pauseCpu(); - - if (asciiSelected) - { - u8 newValue = evt.GetKeyCode(); - cpu->write8(curAddress, newValue); - scrollCursor(1); - } - else - { - u8 key = tolower(evt.GetKeyCode()); - int inputValue = -1; - - if (key >= '0' && key <= '9') - inputValue = key - '0'; - if (key >= 'a' && key <= 'f') - inputValue = key - 'a' + 10; - - if (inputValue >= 0) - { - int shiftAmount = (1 - selectedNibble) * 4; - - u8 oldValue = cpu->read8(curAddress); - oldValue &= ~(0xF << shiftAmount); - u8 newValue = oldValue | (inputValue << shiftAmount); - cpu->write8(curAddress, newValue); - scrollCursor(1); - } - } - - if (active) - cpu->resumeCpu(); - redraw(); -} - -void CtrlMemView::scrollbarEvent(wxScrollWinEvent& evt) -{ - int type = evt.GetEventType(); - if (type == wxEVT_SCROLLWIN_LINEUP) - { - scrollCursor(-rowSize); - } - else if (type == wxEVT_SCROLLWIN_LINEDOWN) - { - scrollCursor(rowSize); - } - else if (type == wxEVT_SCROLLWIN_PAGEUP) - { - scrollWindow(-GetClientSize().y / rowHeight); - } - else if (type == wxEVT_SCROLLWIN_PAGEDOWN) - { - scrollWindow(GetClientSize().y / rowHeight); - } - - redraw(); -} - -void CtrlMemView::scrollWindow(int lines) -{ - windowStart += lines * rowSize; - curAddress += lines * rowSize; - redraw(); -} - -void CtrlMemView::scrollCursor(int bytes) -{ - if (!asciiSelected && bytes == 1) - { - if (selectedNibble == 0) - { - selectedNibble = 1; - bytes = 0; - } - else - { - selectedNibble = 0; - } - } - else if (!asciiSelected && bytes == -1) - { - if (selectedNibble == 0) - { - selectedNibble = 1; - } - else - { - selectedNibble = 0; - bytes = 0; - } - } - - curAddress += bytes; - - int visibleRows = GetClientSize().y / rowHeight; - u32 windowEnd = windowStart + visibleRows * rowSize; - - if (curAddress < windowStart) - { - windowStart = (curAddress / rowSize) * rowSize; - } - else if (curAddress >= windowEnd) - { - windowStart = curAddress - (visibleRows - 1) * rowSize; - windowStart = (windowStart / rowSize) * rowSize; - } - - updateStatusBarText(); - redraw(); -} - -void CtrlMemView::updateStatusBarText() -{ - wchar_t text[64]; - - int needpad = (curAddress - windowStart) % byteGroupSize; - u32 addr = curAddress - needpad; - - swprintf(text, 64, L"%08X %08X", curAddress, addr); - - postEvent(debEVT_SETSTATUSBARTEXT, text); -} - -void CtrlMemView::gotoAddress(u32 addr, bool pushInHistory) -{ - if (pushInHistory) - history.push(windowStart); - - curAddress = addr; - selectedNibble = 0; - - if (g_Conf->EmuOptions.Debugger.AlignMemoryWindowStart) - { - int visibleRows = GetClientSize().y / rowHeight; - u32 windowEnd = windowStart + visibleRows * rowSize; - - if (curAddress < windowStart || curAddress >= windowEnd) - windowStart = (curAddress / rowSize) * rowSize; - } - else - { - windowStart = curAddress; - } - - updateStatusBarText(); - redraw(); -} - -void CtrlMemView::gotoPoint(int x, int y) -{ - int line = y / rowHeight; - int lineAddress = windowStart + line * rowSize; - - if (x >= asciiStart) - { - int col = (x - asciiStart) / (charWidth + 2); - if (col >= rowSize) - return; - - asciiSelected = true; - curAddress = lineAddress + col; - selectedNibble = 0; - updateStatusBarText(); - redraw(); - } - else if (x >= hexStart) - { - int col = (x - hexStart); - - int groupWidth = byteGroupSize * charWidth * 3; - int group = col / groupWidth; - - int posInGroup = col % groupWidth; - - int indexInGroup = -1; - - for (int i = 0; i < int(byteGroupSize); i++) - { - int start = hexGroupPositionFromIndex(i); - int end = start + 2 * charWidth - 1; - if (posInGroup < start) - { - return; - } - else if (posInGroup <= end) - { - selectedNibble = ((posInGroup - start) / charWidth) % 2; - indexInGroup = i; - break; - } - } - - if (indexInGroup == -1) - return; - - curAddress = lineAddress + group * byteGroupSize + (byteGroupSize - indexInGroup - 1); - - asciiSelected = false; - updateStatusBarText(); - redraw(); - } -} - -void CtrlMemView::updateReference(u32 address) -{ - referencedAddress = address; - redraw(); -} - -void CtrlMemView::pasteHex() -{ - if (wxTheClipboard->Open()) - { - if (wxTheClipboard->IsSupported(wxDF_TEXT)) - { - wxTextDataObject data; - wxTheClipboard->GetData(data); - wxString str = data.GetText(); - str.Replace(" ", ""); - str.Replace("\n", ""); - str.Replace("\r", ""); - str.Replace("\t", ""); - - bool active = !cpu->isCpuPaused(); - if (active) - cpu->pauseCpu(); - - std::size_t i; - for (i = 0; i < str.size() / 2; i++) - { - long byte; - if (str.Mid(i * 2, 2).ToLong(&byte, 16)) - { - cpu->write8(curAddress + i, static_cast(byte)); - } - else - { - break; - } - } - scrollCursor(i); - - if (active) - cpu->resumeCpu(); - } - wxTheClipboard->Close(); - } -} diff --git a/pcsx2/gui/Debugger/CtrlMemView.h b/pcsx2/gui/Debugger/CtrlMemView.h deleted file mode 100644 index 775b7a12a3..0000000000 --- a/pcsx2/gui/Debugger/CtrlMemView.h +++ /dev/null @@ -1,69 +0,0 @@ -/* PCSX2 - PS2 Emulator for PCs - * Copyright (C) 2002-2014 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 . - */ - -#pragma once -#include - -#include "DebugTools/DebugInterface.h" -#include "DebugTools/DisassemblyManager.h" - -class CtrlMemView: public wxWindow -{ -public: - CtrlMemView(wxWindow* parent, DebugInterface* _cpu); - - void paintEvent(wxPaintEvent & evt); - void mouseEvent(wxMouseEvent& evt); - void keydownEvent(wxKeyEvent& evt); - void scrollbarEvent(wxScrollWinEvent& evt); - void charEvent(wxKeyEvent& evt); - void redraw(); - void gotoAddress(u32 address, bool pushInHistory = false); - void updateReference(u32 address); - - wxDECLARE_EVENT_TABLE(); -private: - void render(wxDC& dc); - int hexGroupPositionFromIndex(int idx); - void setRowSize(int bytesInRow); - void gotoPoint(int x, int y); - void updateStatusBarText(); - void postEvent(wxEventType type, wxString text); - void postEvent(wxEventType type, int value); - void scrollWindow(int lines); - void scrollCursor(int bytes); - void onPopupClick(wxCommandEvent& evt); - void focusEvent(wxFocusEvent& evt) { Refresh(); }; - void pasteHex(); - - DebugInterface* cpu; - int rowHeight; - int charWidth; - u32 windowStart; - u32 curAddress; // current selected address - u32 referencedAddress; // refrenced by register - u32 byteGroupSize; - int rowSize; - wxFont font,underlineFont; - - int addressStart; - int hexStart; - int asciiStart; - bool asciiSelected; - int selectedNibble; - - std::stack history; - wxMenu menu; -}; diff --git a/pcsx2/gui/Debugger/CtrlRegisterList.cpp b/pcsx2/gui/Debugger/CtrlRegisterList.cpp deleted file mode 100644 index 6dbe936497..0000000000 --- a/pcsx2/gui/Debugger/CtrlRegisterList.cpp +++ /dev/null @@ -1,642 +0,0 @@ -/* PCSX2 - PS2 Emulator for PCs - * Copyright (C) 2002-2021 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 . - */ - -#include "PrecompiledHeader.h" -#include "CtrlRegisterList.h" -#include "DebugTools/Debug.h" -#include "common/BitCast.h" -#include "common/StringUtil.h" - -#include "DebugEvents.h" -#include "gui/AppConfig.h" -#include "DisassemblyDialog.h" - -wxBEGIN_EVENT_TABLE(CtrlRegisterList, wxWindow) - EVT_SIZE(CtrlRegisterList::sizeEvent) - EVT_LEFT_DOWN(CtrlRegisterList::mouseEvent) - EVT_RIGHT_DOWN(CtrlRegisterList::mouseEvent) - EVT_RIGHT_UP(CtrlRegisterList::mouseEvent) - EVT_MOTION(CtrlRegisterList::mouseEvent) - EVT_KEY_DOWN(CtrlRegisterList::keydownEvent) -wxEND_EVENT_TABLE() - -enum DisassemblyMenuIdentifiers { - ID_REGISTERLIST_RESOLVESTRINGS, - ID_REGISTERLIST_DISPLAYVU0FFLOATS, - ID_REGISTERLIST_CHANGELOWER, - ID_REGISTERLIST_CHANGEUPPER, - ID_REGISTERLIST_CHANGEVALUE, - ID_REGISTERLIST_GOTOINMEMORYVIEW, - ID_REGISTERLIST_GOTOINDISASM -}; - - -CtrlRegisterList::CtrlRegisterList(wxWindow* parent, DebugInterface* _cpu) - : wxScrolledWindow(parent, wxID_ANY, wxDefaultPosition, wxDefaultSize, wxWANTS_CHARS | wxBORDER_NONE | wxVSCROLL) - , cpu(_cpu) -{ - rowHeight = getDebugFontHeight() + 2; - charWidth = getDebugFontWidth(); - category = 0; - lastPc = 0xFFFFFFFF; - resolvePointerStrings = false; - displayVU0FAsFloat = false; - - for (int i = 0; i < cpu->getRegisterCategoryCount(); i++) - { - const int count = cpu->getRegisterCount(i); - - ChangedReg* regs = new ChangedReg[count]; - memset(regs, 0, sizeof(ChangedReg) * count); - changedCategories.push_back(regs); - - int maxLen = 0; - for (int k = 0; k < cpu->getRegisterCount(i); k++) - { - maxLen = std::max(maxLen, strlen(cpu->getRegisterName(i, k))); /* Flawfinder: ignore */ - } - - const int x = 17 + (maxLen + 2) * charWidth; - startPositions.push_back(x); - currentRows.push_back(0); - } - - SetDoubleBuffered(true); - - const wxSize optSize = getOptimalSize(); - SetVirtualSize(optSize.x,optSize.y); - SetScrollbars(1, rowHeight, optSize.x, optSize.y * MSW_GetDPIScale() / rowHeight, 0, 0); -} - -CtrlRegisterList::~CtrlRegisterList() -{ - for (auto& regs : changedCategories) - delete[] regs; -} - -wxSize CtrlRegisterList::getOptimalSize() const -{ - wxSize optRegListSize; - if (cpu->getCpuType() == BreakPointCpu::BREAKPOINT_EE) - { - optRegListSize = wxSize(331, 504); - } - else - { - // We are constructing the IOP register view - optRegListSize = wxSize(133, 504); - } - - return wxSize(optRegListSize.x, optRegListSize.y); -} - -void CtrlRegisterList::postEvent(wxEventType type, wxString text) -{ - wxCommandEvent event(type, GetId()); - event.SetEventObject(this); - event.SetString(text); - wxPostEvent(this, event); -} - -void CtrlRegisterList::postEvent(wxEventType type, int value) -{ - wxCommandEvent event(type, GetId()); - event.SetEventObject(this); - event.SetInt(value); - wxPostEvent(this, event); -} - -void CtrlRegisterList::refreshChangedRegs() -{ - if (cpu->getPC() == lastPc) - return; - - for (size_t cat = 0; cat < changedCategories.size(); cat++) - { - ChangedReg* regs = changedCategories[cat]; - const int size = cpu->getRegisterSize(category); - - for (int i = 0; i < cpu->getRegisterCount(cat); i++) - { - ChangedReg& reg = regs[i]; - memset(®.changed, 0, sizeof(reg.changed)); - - const u128 newValue = cpu->getRegister(cat, i); - - if (reg.oldValue != newValue) - { - bool changed = false; - - if (size >= 128 && (reg.oldValue._u32[3] != newValue._u32[3] || reg.oldValue._u32[2] != newValue._u32[2])) - { - changed = true; - reg.changed[3] = true; - reg.changed[2] = true; - } - - if (size >= 64 && (reg.oldValue._u32[1] != newValue._u32[1] || changed)) - { - changed = true; - reg.changed[1] = true; - } - - if (reg.oldValue._u32[0] != newValue._u32[0] || changed) - { - reg.changed[0] = true; - } - - reg.oldValue = newValue; - } - } - } - - lastPc = cpu->getPC(); -} - -void CtrlRegisterList::redraw() -{ - Update(); -} - -void CtrlRegisterList::sizeEvent(wxSizeEvent& evt) -{ - Refresh(); - evt.Skip(); -} - -void drawU32Text(wxDC& dc, u32 value, int x, int y) -{ - char str[32]; - sprintf(str, "%08X", value); - dc.DrawText(wxString(str, wxConvUTF8), x, y); -} - -void CtrlRegisterList::OnDraw(wxDC& dc) -{ - wxFont font = pxGetFixedFont(8); - font.SetPixelSize(wxSize(charWidth, rowHeight - 2)); - dc.SetFont(font); - - refreshChangedRegs(); - - wxColor colorChanged = wxColor(0xFF0000FF); - wxColor colorUnchanged = wxColor(0xFF004000); - wxColor colorNormal = wxColor(0xFF600000); - - int startRow; - GetViewStart(nullptr, &startRow); - int endRow = startRow + ceil(float(GetClientSize().y) / rowHeight); - - // draw categories - const int width = GetClientSize().x; - if (startRow == 0) - { - int piece = width / cpu->getRegisterCategoryCount(); - for (int i = 0; i < cpu->getRegisterCategoryCount(); i++) - { - const char* name = cpu->getRegisterCategoryName(i); - - int x = i * piece; - - if (i == category) - { - dc.SetBrush(wxBrush(wxColor(0xFF70FF70))); - dc.SetPen(wxPen(wxColor(0xFF000000))); - } - else - { - dc.SetBrush(wxBrush(wxColor(0xFFFFEFE8))); - dc.SetPen(wxPen(wxColor(0xFF000000))); - } - - if (i == cpu->getRegisterCategoryCount() - 1) - piece += width - piece * cpu->getRegisterCategoryCount() - 1; - - dc.DrawRectangle(x, 0, piece + 1, rowHeight); - - // center text - x += (piece - strlen(name) * charWidth) / 2; /* Flawfinder: ignore */ - dc.DrawText(wxString(name, wxConvUTF8), x, 2); - } - } - - // skip the tab row - startRow = std::max(0, startRow - 1); - endRow = std::min(cpu->getRegisterCount(category) - 1, endRow - 1); - - const wxString vu0fTitles[5] = {"W", "Z", "Y", "X"}; - int vu0fTitleCur = 0; - const int nameStart = 17; - const int valueStart = startPositions[category]; - - ChangedReg* changedRegs = changedCategories[category]; - const int registerBits = cpu->getRegisterSize(category); - DebugInterface::RegisterType type = cpu->getRegisterType(category); - - for (int i = startRow; i <= endRow; i++) - { - int x = valueStart; - // Skip a row if we are showing our VU0f registers - // This makes room for the WZYX title - const int y = rowHeight * (i + (category == EECAT_VU0F ? 2 : 1)); - - wxColor backgroundColor; - if (currentRows[category] == i) - backgroundColor = wxColor(0xFFFFCFC8); - else if (i % 2) - backgroundColor = wxColor(237, 242, 255, 255); - else - backgroundColor = wxColor(0xFFFFFFFF); - - dc.SetBrush(backgroundColor); - dc.SetPen(backgroundColor); - dc.DrawRectangle(0, y, width, rowHeight); - - const char* name = cpu->getRegisterName(category, i); - dc.SetTextForeground(colorNormal); - dc.DrawText(wxString(name, wxConvUTF8), nameStart, y + 2); - - const u128 value = cpu->getRegister(category, i); - const ChangedReg& changed = changedRegs[i]; - - switch (type) - { - case DebugInterface::SPECIAL: // let debug interface format them and just display them - { - if (changed.changed[0] || changed.changed[1] || changed.changed[2] || changed.changed[3]) - dc.SetTextForeground(colorChanged); - else - dc.SetTextForeground(colorUnchanged); - - if (category == EECAT_VU0F) - { - if (displayVU0FAsFloat) - { - char str[256]; - const u128 val = cpu->getRegister(category, i); - // Print each register part individually so we know exactly where each part starts - // and use this information for printing the VU0F titles - for (int j = 3; j >= 0; j--) - { - sprintf(str, "%7.2f", bit_cast(val._u32[j])); - dc.DrawText(wxString(str), x, y + 2); - x += charWidth * 8 + 2; - } - - // Only draw the VU0f titles when we are drawing the first row - if (i == startRow) - { - x = valueStart; // Reset our X back to the starting position - - for (int j = 0; j < 4; j++) - { - dc.SetTextForeground(colorNormal); - dc.DrawText(vu0fTitles[j], (x + charWidth * 4) - 4, rowHeight + 2); - x += charWidth * 8 + 2; - } - } - - break; - } - } - else - { - dc.DrawText(StringUtil::UTF8StringToWxString(cpu->getRegisterString(category, i)), x, y + 2); - break; - } - } - [[fallthrough]]; - case DebugInterface::NORMAL: // display them in 32 bit parts - { - switch (registerBits) - { - case 128: - { - int startIndex = 3; - - if (resolvePointerStrings && cpu->isAlive()) - { - const char* strval = cpu->stringFromPointer(value._u32[0]); - if (strval) - { - static wxColor clr = wxColor(0xFF228822); - dc.SetTextForeground(clr); - dc.DrawText(wxString(strval), width - (32 * charWidth + 12), y + 2); - startIndex = 0; - } - } - - const int actualX = width - 4 - (startIndex + 1) * (8 * charWidth + 2); - x = std::max(actualX, x); - - if (startIndex != 3) - { - bool c = false; - for (int i = 3; i > startIndex; i--) - c = c || changed.changed[i]; - - if (c) - { - dc.SetTextForeground(colorChanged); - dc.DrawText(L"+", x - charWidth, y + 2); - } - } - - for (int j = startIndex; j >= 0; j--) - { - if (changed.changed[j]) - dc.SetTextForeground(colorChanged); - else - dc.SetTextForeground(colorUnchanged); - - drawU32Text(dc, value._u32[j], x, y + 2); - - // Only draw the VU0f titles when we are drawing the first row and are on the VU0f category - if (category == EECAT_VU0F && i == startRow) - { - dc.SetTextForeground(colorNormal); - dc.DrawText(vu0fTitles[vu0fTitleCur++], x + (charWidth * 2.5), rowHeight + 2); - } - x += charWidth * 8 + 2; - } - - break; - } - case 64: - { - for (int j = 1; j >= 0; j--) - { - if (changed.changed[j]) - dc.SetTextForeground(colorChanged); - else - dc.SetTextForeground(colorUnchanged); - - drawU32Text(dc, value._u32[j], x, y + 2); - x += charWidth * 8 + 2; - } - - break; - } - case 32: - { - if (changed.changed[0]) - dc.SetTextForeground(colorChanged); - else - dc.SetTextForeground(colorUnchanged); - - drawU32Text(dc, value._u32[0], x, y + 2); - break; - } - } - break; - } - } - } -} - -void CtrlRegisterList::changeValue(RegisterChangeMode mode) -{ - wchar_t str[64]; - - u128 oldValue = cpu->getRegister(category, currentRows[category]); - - switch (mode) - { - case LOWER64: - swprintf(str, 64, L"0x%016llX", oldValue._u64[0]); - break; - case UPPER64: - swprintf(str, 64, L"0x%016llX", oldValue._u64[1]); - break; - case CHANGE32: - swprintf(str, 64, L"0x%08X", oldValue._u32[0]); - break; - } - - u64 newValue; - if (executeExpressionWindow(this, cpu, newValue, str)) - { - switch (mode) - { - case LOWER64: - oldValue._u64[0] = newValue; - break; - case UPPER64: - oldValue._u64[1] = newValue; - break; - case CHANGE32: - oldValue._u32[0] = newValue; - break; - } - - cpu->setRegister(category, currentRows[category], oldValue); - } -} - - -void CtrlRegisterList::onPopupClick(wxCommandEvent& evt) -{ - switch (evt.GetId()) - { - case ID_REGISTERLIST_RESOLVESTRINGS: - resolvePointerStrings = !resolvePointerStrings; - postEvent(debEVT_UPDATELAYOUT, 0); - Refresh(); - break; - case ID_REGISTERLIST_DISPLAYVU0FFLOATS: - displayVU0FAsFloat = !displayVU0FAsFloat; - postEvent(debEVT_UPDATELAYOUT, 0); - Refresh(); - break; - case ID_REGISTERLIST_CHANGELOWER: - changeValue(LOWER64); - Refresh(); - break; - case ID_REGISTERLIST_CHANGEUPPER: - changeValue(UPPER64); - Refresh(); - break; - case ID_REGISTERLIST_CHANGEVALUE: - if (cpu->getRegisterSize(category) == 32) - changeValue(CHANGE32); - else - changeValue(LOWER64); - Refresh(); - break; - case ID_REGISTERLIST_GOTOINMEMORYVIEW: - postEvent(debEVT_GOTOINMEMORYVIEW, cpu->getRegister(category, currentRows[category])._u32[0]); - break; - case ID_REGISTERLIST_GOTOINDISASM: - postEvent(debEVT_GOTOINDISASM, cpu->getRegister(category, currentRows[category])._u32[0]); - break; - default: - wxMessageBox(L"Unimplemented.", L"Unimplemented.", wxICON_INFORMATION); - break; - } -} - -void CtrlRegisterList::setCurrentRow(int row) -{ - char str[256]; - u128 value; - std::string text; - - const char* name = cpu->getRegisterName(category, row); - - switch (cpu->getRegisterType(category)) - { - case DebugInterface::NORMAL: - value = cpu->getRegister(category, row); - postEvent(debEVT_REFERENCEMEMORYVIEW, value._u32[0]); - - switch (cpu->getRegisterSize(category)) - { - case 128: - sprintf(str, "%s = 0x%08X%08X%08X%08X", name, value._u32[3], value._u32[2], value._u32[1], value._u32[0]); - break; - case 64: - sprintf(str, "%s = 0x%08X%08X", name, value._u32[1], value._u32[0]); - break; - case 32: - sprintf(str, "%s = 0x%08X", name, value._u32[0]); - break; - } - text = str; - break; - case DebugInterface::SPECIAL: - text = std::string(name) + " = " + cpu->getRegisterString(category, row); - break; - } - - currentRows[category] = row; - postEvent(debEVT_SETSTATUSBARTEXT, StringUtil::UTF8StringToWxString(text)); - ensureVisible(currentRows[category] + 1); //offset due to header at scroll position 0 - Refresh(); -} - -void CtrlRegisterList::mouseEvent(wxMouseEvent& evt) -{ - int xOffset, yOffset; - ((wxScrolledWindow*)this)->GetViewStart(&xOffset, &yOffset); - - wxClientDC dc(this); - wxPoint pos = evt.GetPosition(); - const int x = dc.DeviceToLogicalX(pos.x) + xOffset; - const int y = dc.DeviceToLogicalY(pos.y) + yOffset * rowHeight; - - if (evt.GetEventType() == wxEVT_RIGHT_UP) - { - if (y >= rowHeight) - { - // The ternary here is due to the fact that VU0f has an extra row for the w z y x title - const int row = (y - rowHeight * (category == EECAT_VU0F ? 2 : 1)) / rowHeight; - if (row != currentRows[category] && row < cpu->getRegisterCount(category)) - setCurrentRow(row); - } - - wxMenu menu; - - - if (category == EECAT_VU0F) - { - menu.AppendCheckItem(ID_REGISTERLIST_DISPLAYVU0FFLOATS, L"Display VU0f registers as floats")->Check(displayVU0FAsFloat); - } - else - { - menu.AppendCheckItem(ID_REGISTERLIST_RESOLVESTRINGS, L"Resolve string pointers")->Check(resolvePointerStrings); - } - - menu.AppendSeparator(); - - const int bits = cpu->getRegisterSize(category); - if (bits >= 64) - { - menu.Append(ID_REGISTERLIST_CHANGELOWER, L"Change lower 64 bit"); - menu.Append(ID_REGISTERLIST_CHANGEUPPER, L"Change upper 64 bit"); - } - else - { - menu.Append(ID_REGISTERLIST_CHANGEVALUE, L"Change value"); - } - - menu.AppendSeparator(); - menu.Append(ID_REGISTERLIST_GOTOINMEMORYVIEW, L"Follow in Memory view"); - menu.Append(ID_REGISTERLIST_GOTOINDISASM, L"Follow in Disasm"); - - menu.Bind(wxEVT_MENU, &CtrlRegisterList::onPopupClick, this); - PopupMenu(&menu, evt.GetPosition()); - return; - } - - if (evt.ButtonIsDown(wxMOUSE_BTN_LEFT) || evt.ButtonIsDown(wxMOUSE_BTN_RIGHT)) - { - if (y < rowHeight) - { - const int piece = GetSize().x / cpu->getRegisterCategoryCount(); - const int cat = std::min(x / piece, cpu->getRegisterCategoryCount() - 1); - - if (cat != category) - { - category = cat; - Refresh(); - } - } - else - { - // The ternary here is due to the fact that VU0f has an extra row for the w z y x title - const int row = (y - rowHeight * (category == EECAT_VU0F ? 2 : 1)) / rowHeight; - if (row != currentRows[category] && row < cpu->getRegisterCount(category)) - setCurrentRow(row); - } - - SetFocus(); - SetFocusFromKbd(); - } -} - -void CtrlRegisterList::ensureVisible(int index) -{ - //scroll vertically to keep a logical position visible - int x, y; - GetViewStart(&x, &y); - const int visibleOffset = floor(float(GetClientSize().y) / rowHeight) - 1; - if (index < y) - Scroll(x, index); - else if (index > y + visibleOffset) - Scroll(x, index - visibleOffset); -} - -void CtrlRegisterList::keydownEvent(wxKeyEvent& evt) -{ - switch (evt.GetKeyCode()) - { - case WXK_UP: - int x, y; - GetViewStart(&x, &y); - //If at top of rows allow scrolling an extra time to show tab header - if (currentRows[category] == 0 && y == 1) - Scroll(x, 0); - else - setCurrentRow(std::max(currentRows[category] - 1, 0)); - break; - case WXK_DOWN: - setCurrentRow(std::min(currentRows[category] + 1, cpu->getRegisterCount(category) - 1)); - break; - case WXK_TAB: - category = (category + 1) % cpu->getRegisterCategoryCount(); - Refresh(); - break; - } -} diff --git a/pcsx2/gui/Debugger/CtrlRegisterList.h b/pcsx2/gui/Debugger/CtrlRegisterList.h deleted file mode 100644 index 676176f172..0000000000 --- a/pcsx2/gui/Debugger/CtrlRegisterList.h +++ /dev/null @@ -1,84 +0,0 @@ -/* PCSX2 - PS2 Emulator for PCs - * Copyright (C) 2002-2021 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 . - */ - -#pragma once -#include - -#include "gui/MSWstuff.h" // Required for MSW_GetDPIScale() -#include "DebugTools/DebugInterface.h" -#include "DebugTools/DisassemblyManager.h" - -class CtrlRegisterList : public wxScrolledWindow -{ -public: - CtrlRegisterList(wxWindow* parent, DebugInterface* _cpu); - ~CtrlRegisterList(); - - void mouseEvent(wxMouseEvent& evt); - void keydownEvent(wxKeyEvent& evt); - void onPopupClick(wxCommandEvent& evt); - void sizeEvent(wxSizeEvent& evt); - void redraw(); - wxDECLARE_EVENT_TABLE(); - - virtual wxSize GetMinClientSize() const - { - wxSize optimalSize = getOptimalSize(); - if (GetWindowStyle() & wxVSCROLL) - optimalSize.x += wxSystemSettings::GetMetric(wxSYS_VSCROLL_X); - - return wxSize(optimalSize.x * MSW_GetDPIScale(), 0); - } - - virtual wxSize DoGetBestClientSize() const - { - return GetMinClientSize(); - } - -private: - enum RegisterChangeMode - { - LOWER64, - UPPER64, - CHANGE32 - }; - - void OnDraw(wxDC& dc); - void refreshChangedRegs(); - void setCurrentRow(int row); - void ensureVisible(int index); - void changeValue(RegisterChangeMode mode); - wxSize getOptimalSize() const; - - void postEvent(wxEventType type, wxString text); - void postEvent(wxEventType type, int value); - - struct ChangedReg - { - u128 oldValue; - bool changed[4]; - }; - - std::vector changedCategories; - std::vector startPositions; - std::vector currentRows; - - DebugInterface* cpu; - int rowHeight, charWidth; - u32 lastPc; - int category; - bool resolvePointerStrings; - bool displayVU0FAsFloat; -}; diff --git a/pcsx2/gui/Debugger/DebugEvents.cpp b/pcsx2/gui/Debugger/DebugEvents.cpp deleted file mode 100644 index 6c3a5db9d3..0000000000 --- a/pcsx2/gui/Debugger/DebugEvents.cpp +++ /dev/null @@ -1,59 +0,0 @@ -/* PCSX2 - PS2 Emulator for PCs - * Copyright (C) 2002-2014 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 . - */ - -#include "PrecompiledHeader.h" -#include "DebugEvents.h" - -wxDEFINE_EVENT(debEVT_SETSTATUSBARTEXT, wxCommandEvent); -wxDEFINE_EVENT(debEVT_UPDATELAYOUT, wxCommandEvent); -wxDEFINE_EVENT(debEVT_GOTOADDRESS, wxCommandEvent); -wxDEFINE_EVENT(debEVT_GOTOINMEMORYVIEW, wxCommandEvent); -wxDEFINE_EVENT(debEVT_REFERENCEMEMORYVIEW, wxCommandEvent); -wxDEFINE_EVENT(debEVT_GOTOINDISASM, wxCommandEvent); -wxDEFINE_EVENT(debEVT_RUNTOPOS, wxCommandEvent); -wxDEFINE_EVENT(debEVT_MAPLOADED, wxCommandEvent); -wxDEFINE_EVENT(debEVT_STEPOVER, wxCommandEvent); -wxDEFINE_EVENT(debEVT_STEPINTO, wxCommandEvent); -wxDEFINE_EVENT(debEVT_STEPOUT, wxCommandEvent); -wxDEFINE_EVENT(debEVT_UPDATE, wxCommandEvent); -wxDEFINE_EVENT(debEVT_BREAKPOINTWINDOW, wxCommandEvent); - -bool parseExpression(const char* exp, DebugInterface* cpu, u64& dest) -{ - PostfixExpression postfix; - if (!cpu->initExpression(exp,postfix)) return false; - return cpu->parseExpression(postfix,dest); -} - -void displayExpressionError(wxWindow* parent) -{ - wxMessageBox(wxString(getExpressionError(),wxConvUTF8),L"Invalid expression",wxICON_ERROR); -} - -bool executeExpressionWindow(wxWindow* parent, DebugInterface* cpu, u64& dest, const wxString& defaultValue) -{ - wxString result = wxGetTextFromUser(L"Enter expression",L"Expression",defaultValue,parent); - if (result.empty()) - return false; - - wxCharBuffer expression = result.ToUTF8(); - if (!parseExpression(expression, cpu, dest)) - { - displayExpressionError(parent); - return false; - } - - return true; -} diff --git a/pcsx2/gui/Debugger/DebugEvents.h b/pcsx2/gui/Debugger/DebugEvents.h deleted file mode 100644 index e8032ad11f..0000000000 --- a/pcsx2/gui/Debugger/DebugEvents.h +++ /dev/null @@ -1,34 +0,0 @@ -/* PCSX2 - PS2 Emulator for PCs - * Copyright (C) 2002-2014 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 . - */ - -#pragma once -#include -#include "DebugTools/DebugInterface.h" - -wxDECLARE_EVENT(debEVT_SETSTATUSBARTEXT, wxCommandEvent); -wxDECLARE_EVENT(debEVT_UPDATELAYOUT, wxCommandEvent); -wxDECLARE_EVENT(debEVT_GOTOADDRESS, wxCommandEvent); -wxDECLARE_EVENT(debEVT_GOTOINMEMORYVIEW, wxCommandEvent); -wxDECLARE_EVENT(debEVT_REFERENCEMEMORYVIEW, wxCommandEvent); -wxDECLARE_EVENT(debEVT_GOTOINDISASM, wxCommandEvent); -wxDECLARE_EVENT(debEVT_RUNTOPOS, wxCommandEvent); -wxDECLARE_EVENT(debEVT_MAPLOADED, wxCommandEvent); -wxDECLARE_EVENT(debEVT_STEPOVER, wxCommandEvent); -wxDECLARE_EVENT(debEVT_STEPINTO, wxCommandEvent); -wxDECLARE_EVENT(debEVT_STEPOUT, wxCommandEvent); -wxDECLARE_EVENT(debEVT_UPDATE, wxCommandEvent); -wxDECLARE_EVENT(debEVT_BREAKPOINTWINDOW, wxCommandEvent); - -bool executeExpressionWindow(wxWindow* parent, DebugInterface* cpu, u64& dest, const wxString& defaultValue = wxEmptyString); diff --git a/pcsx2/gui/Debugger/DebuggerLists.cpp b/pcsx2/gui/Debugger/DebuggerLists.cpp deleted file mode 100644 index b28e611486..0000000000 --- a/pcsx2/gui/Debugger/DebuggerLists.cpp +++ /dev/null @@ -1,821 +0,0 @@ -/* PCSX2 - PS2 Emulator for PCs - * Copyright (C) 2002-2021 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 . - */ - -#include "PrecompiledHeader.h" -#include "DebuggerLists.h" -#include "BreakpointWindow.h" -#include "DebugEvents.h" -#include "pcsx2/gui/StringHelpers.h" - -wxBEGIN_EVENT_TABLE(GenericListView, wxWindow) - EVT_SIZE(GenericListView::sizeEvent) - EVT_KEY_DOWN(GenericListView::keydownEvent) - EVT_RIGHT_DOWN(GenericListView::mouseEvent) - EVT_RIGHT_UP(GenericListView::mouseEvent) - EVT_LEFT_DCLICK(GenericListView::mouseEvent) - EVT_LIST_ITEM_RIGHT_CLICK(wxID_ANY, GenericListView::listEvent) -wxEND_EVENT_TABLE() - -GenericListView::GenericListView(wxWindow* parent, GenericListViewColumn* columns, int columnCount) - : wxListView(parent, wxID_ANY, wxDefaultPosition, wxDefaultSize, wxLC_VIRTUAL | wxLC_REPORT | wxLC_SINGLE_SEL | wxNO_BORDER) -{ - m_isInResizeColumn = false; - dontResizeColumnsInSizeEventHandler = false; - - insertColumns(columns, columnCount); -} - -void GenericListView::insertColumns(GenericListViewColumn* columns, int count) -{ - int totalWidth = GetSize().x; - - for (int i = 0; i < count; i++) - { - wxListItem column; - column.SetText(columns[i].name); - column.SetWidth(totalWidth * columns[i].size); - - InsertColumn(i, column); - } - - this->columns = columns; -} - -void GenericListView::resizeColumn(int col, int width) -{ - if (!m_isInResizeColumn) - { - m_isInResizeColumn = true; - - SetColumnWidth(col, width); - - m_isInResizeColumn = false; - } -} - -void GenericListView::resizeColumns(int totalWidth) -{ - for (int i = 0; i < GetColumnCount(); i++) - { - resizeColumn(i, totalWidth * columns[i].size); - } -} - -void GenericListView::sizeEvent(wxSizeEvent& evt) -{ - // HACK: On Windows, it seems that if you resize the columns in the size - // event handler when the scrollbar disappears, the listview contents may - // decide to disappear as well. So let's avoid the resize for this case. - if (!dontResizeColumnsInSizeEventHandler) - resizeColumns(GetClientSize().x); - dontResizeColumnsInSizeEventHandler = false; - evt.Skip(); -} - -void GenericListView::keydownEvent(wxKeyEvent& evt) -{ - int sel = GetFirstSelected(); - switch (evt.GetKeyCode()) - { - case WXK_UP: - if (sel > 0) - { - Select(sel - 1); - Focus(sel - 1); - } - break; - case WXK_DOWN: - if (sel + 1 < GetItemCount()) - { - Select(sel + 1); - Focus(sel + 1); - } - break; - } - - onKeyDown(evt.GetKeyCode()); -} - -void GenericListView::update() -{ - int newRows = getRowCount(); - int oldRows = GetItemCount(); - - SetItemCount(newRows); - - if (newRows != oldRows) - { - resizeColumns(GetClientSize().x); - - // wx adds the horizontal scrollbar based on the old column width, - // which changes the client width. Simply resizing the columns won't - // make the scrollbar go away, so let's make it recalculate if it needs it - SetItemCount(newRows); - } - dontResizeColumnsInSizeEventHandler = true; - Refresh(); -} - -wxString GenericListView::OnGetItemText(long item, long col) const -{ - return getColumnText(item, col); -} - -void GenericListView::postEvent(wxEventType type, int value) -{ - wxCommandEvent event(type, GetId()); - event.SetEventObject(this); - event.SetInt(value); - wxPostEvent(this, event); -} - -void GenericListView::mouseEvent(wxMouseEvent& evt) -{ - wxEventType type = evt.GetEventType(); - - if (type == wxEVT_RIGHT_DOWN) - { - clickPos = evt.GetPosition(); - evt.Skip(); - } - else if (type == wxEVT_RIGHT_UP) - { - onRightClick(GetFirstSelected(), evt.GetPosition()); - } - else if (type == wxEVT_LEFT_DCLICK) - { - onDoubleClick(GetFirstSelected(), evt.GetPosition()); - } -} - -void GenericListView::listEvent(wxListEvent& evt) -{ - onRightClick(GetFirstSelected(), clickPos); -} - -// -// BreakpointList -// - -enum -{ - BPL_TYPE, - BPL_OFFSET, - BPL_SIZELABEL, - BPL_OPCODE, - BPL_CONDITION, - BPL_HITS, - BPL_ENABLED, - BPL_COLUMNCOUNT -}; - -enum BreakpointListMenuIdentifiers -{ - ID_BREAKPOINTLIST_ENABLE = 1, - ID_BREAKPOINTLIST_EDIT, - ID_BREAKPOINTLIST_DELETE, - ID_BREAKPOINTLIST_ADDNEW, -}; - -GenericListViewColumn breakpointColumns[BPL_COLUMNCOUNT] = { - {L"Type", 0.12f}, - {L"Offset", 0.12f}, - {L"Size/Label", 0.18f}, - {L"Opcode", 0.28f}, - {L"Condition", 0.17f}, - {L"Hits", 0.05f}, - {L"Enabled", 0.08f}}; - -BreakpointList::BreakpointList(wxWindow* parent, DebugInterface* _cpu, CtrlDisassemblyView* _disassembly) - : GenericListView(parent, breakpointColumns, BPL_COLUMNCOUNT) - , cpu(_cpu) - , disasm(_disassembly) -{ -} - -int BreakpointList::getRowCount() -{ - int count = 0; - for (size_t i = 0; i < CBreakPoints::GetMemChecks().size(); i++) - { - if (displayedMemChecks_[i].cpu == cpu->getCpuType()) - count++; - } - for (size_t i = 0; i < CBreakPoints::GetBreakpoints().size(); i++) - { - if (!displayedBreakPoints_[i].temporary && displayedBreakPoints_[i].cpu == cpu->getCpuType()) - count++; - } - - return count; -} - -wxString BreakpointList::getColumnText(int item, int col) const -{ - FastFormatUnicode dest; - bool isMemory; - int index = getBreakpointIndex(item, isMemory); - if (index == -1) - return L"Invalid"; - - switch (col) - { - case BPL_TYPE: - { - if (isMemory) - { - switch ((int)displayedMemChecks_[index].cond) - { - case MEMCHECK_READ: - dest.Write("Read"); - break; - case MEMCHECK_WRITE: - dest.Write("Write"); - break; - case MEMCHECK_READWRITE: - dest.Write("Read/Write"); - break; - case MEMCHECK_WRITE | MEMCHECK_WRITE_ONCHANGE: - dest.Write("Write Change"); - break; - case MEMCHECK_READWRITE | MEMCHECK_WRITE_ONCHANGE: - dest.Write("Read/Write Change"); - break; - } - } - else - { - dest.Write(L"Execute"); - } - } - break; - case BPL_OFFSET: - { - if (isMemory) - { - dest.Write("0x%08X", displayedMemChecks_[index].start); - } - else - { - dest.Write("0x%08X", displayedBreakPoints_[index].addr); - } - } - break; - case BPL_SIZELABEL: - { - if (isMemory) - { - auto mc = displayedMemChecks_[index]; - if (mc.end == 0) - dest.Write("0x%08X", 1); - else - dest.Write("0x%08X", mc.end - mc.start); - } - else - { - const std::string sym = cpu->GetSymbolMap().GetLabelString(displayedBreakPoints_[index].addr); - if (!sym.empty()) - { - dest.Write("%s", sym.c_str()); - } - else - { - dest.Write("-"); - } - } - } - break; - case BPL_OPCODE: - { - if (isMemory) - { - dest.Write(L"-"); - } - else - { - if (!cpu->isAlive()) - break; - char temp[256]; - disasm->getOpcodeText(displayedBreakPoints_[index].addr, temp); - dest.Write("%s", temp); - } - } - break; - case BPL_CONDITION: - { - if (isMemory || !displayedBreakPoints_[index].hasCond) - { - dest.Write("-"); - } - else - { - dest.Write("%s", displayedBreakPoints_[index].cond.expressionString); - } - } - break; - case BPL_HITS: - { - if (isMemory) - { - dest.Write("%d", displayedMemChecks_[index].numHits); - } - else - { - dest.Write("-"); - } - } - break; - case BPL_ENABLED: - { - if (isMemory) - { - dest.Write("%s", displayedMemChecks_[index].result & MEMCHECK_BREAK ? "true" : "false"); - } - else - { - dest.Write("%s", displayedBreakPoints_[index].enabled ? "true" : "false"); - } - } - break; - default: - return L"Invalid"; - } - - return dest; -} - -void BreakpointList::onKeyDown(int key) -{ - int sel = GetFirstSelected(); - switch (key) - { - case WXK_DELETE: - removeBreakpoint(sel); - break; - case WXK_RETURN: - editBreakpoint(sel); - break; - case WXK_SPACE: - toggleEnabled(sel); - break; - } -} - -int BreakpointList::getBreakpointIndex(int itemIndex, bool& isMemory) const -{ - // memory breakpoints first - for (size_t i = 0; i < displayedMemChecks_.size(); i++) - { - if (displayedMemChecks_[i].cpu != cpu->getCpuType()) - continue; - if (itemIndex == 0) - { - isMemory = true; - return (int)i; - } - itemIndex--; - } - - size_t i = 0; - while (i < displayedBreakPoints_.size()) - { - if (displayedBreakPoints_[i].temporary || displayedBreakPoints_[i].cpu != cpu->getCpuType()) - { - i++; - continue; - } - - // the index is 0 when there are no more breakpoints to skip - if (itemIndex == 0) - { - isMemory = false; - return (int)i; - } - - i++; - itemIndex--; - } - - return -1; -} - -void BreakpointList::reloadBreakpoints() -{ - // Update the items we're displaying from the debugger. - displayedBreakPoints_ = CBreakPoints::GetBreakpoints(); - displayedMemChecks_ = CBreakPoints::GetMemChecks(); - update(); -} - -void BreakpointList::editBreakpoint(int itemIndex) -{ - bool isMemory; - int index = getBreakpointIndex(itemIndex, isMemory); - if (index == -1) - return; - - BreakpointWindow win(this, cpu); - if (isMemory) - { - auto mem = displayedMemChecks_[index]; - win.loadFromMemcheck(mem); - if (win.ShowModal() == wxID_OK) - { - CBreakPoints::RemoveMemCheck(cpu->getCpuType(), mem.start, mem.end); - win.addBreakpoint(); - } - } - else - { - auto bp = displayedBreakPoints_[index]; - win.loadFromBreakpoint(bp); - if (win.ShowModal() == wxID_OK) - { - CBreakPoints::RemoveBreakPoint(cpu->getCpuType(), bp.addr); - win.addBreakpoint(); - } - } -} - -void BreakpointList::toggleEnabled(int itemIndex) -{ - bool isMemory; - int index = getBreakpointIndex(itemIndex, isMemory); - if (index == -1) - return; - - if (isMemory) - { - MemCheck mcPrev = displayedMemChecks_[index]; - CBreakPoints::ChangeMemCheck(cpu->getCpuType(), mcPrev.start, mcPrev.end, mcPrev.cond, MemCheckResult(mcPrev.result ^ MEMCHECK_BREAK)); - } - else - { - BreakPoint bpPrev = displayedBreakPoints_[index]; - CBreakPoints::ChangeBreakPoint(cpu->getCpuType(), bpPrev.addr, !bpPrev.enabled); - } -} - -void BreakpointList::gotoBreakpointAddress(int itemIndex) -{ - bool isMemory; - int index = getBreakpointIndex(itemIndex, isMemory); - if (index == -1) - return; - - if (isMemory) - { - u32 address = displayedMemChecks_[index].start; - postEvent(debEVT_GOTOINMEMORYVIEW, address); - } - else - { - u32 address = displayedBreakPoints_[index].addr; - postEvent(debEVT_GOTOINDISASM, address); - } -} - -void BreakpointList::removeBreakpoint(int itemIndex) -{ - bool isMemory; - int index = getBreakpointIndex(itemIndex, isMemory); - if (index == -1) - return; - - if (isMemory) - { - auto mc = displayedMemChecks_[index]; - CBreakPoints::RemoveMemCheck(cpu->getCpuType(), mc.start, mc.end); - } - else - { - u32 address = displayedBreakPoints_[index].addr; - CBreakPoints::RemoveBreakPoint(displayedBreakPoints_[index].cpu, address); - } -} - -void BreakpointList::onPopupClick(wxCommandEvent& evt) -{ - int index = GetFirstSelected(); - switch (evt.GetId()) - { - case ID_BREAKPOINTLIST_ENABLE: - toggleEnabled(index); - break; - case ID_BREAKPOINTLIST_EDIT: - editBreakpoint(index); - break; - case ID_BREAKPOINTLIST_DELETE: - removeBreakpoint(index); - break; - case ID_BREAKPOINTLIST_ADDNEW: - postEvent(debEVT_BREAKPOINTWINDOW, 0); - break; - default: - wxMessageBox(L"Unimplemented.", L"Unimplemented.", wxICON_INFORMATION); - break; - } -} - -void BreakpointList::showMenu(const wxPoint& pos) -{ - bool isMemory; - int index = getBreakpointIndex(GetFirstSelected(), isMemory); - - wxMenu menu; - if (index != -1) - { - menu.AppendCheckItem(ID_BREAKPOINTLIST_ENABLE, L"Enable"); - menu.Append(ID_BREAKPOINTLIST_EDIT, L"Edit"); - menu.Append(ID_BREAKPOINTLIST_DELETE, L"Delete"); - menu.AppendSeparator(); - - // check if the breakpoint is enabled - bool enabled; - if (isMemory) - enabled = (displayedMemChecks_[index].result & MEMCHECK_BREAK) != 0; - else - enabled = displayedBreakPoints_[index].enabled; - - menu.Check(ID_BREAKPOINTLIST_ENABLE, enabled); - } - - menu.Append(ID_BREAKPOINTLIST_ADDNEW, L"Add new"); - - menu.Bind(wxEVT_MENU, &BreakpointList::onPopupClick, this); - PopupMenu(&menu, pos); -} - -void BreakpointList::onRightClick(int itemIndex, const wxPoint& point) -{ - showMenu(point); -} - -void BreakpointList::onDoubleClick(int itemIndex, const wxPoint& point) -{ - gotoBreakpointAddress(itemIndex); -} - - -// -// ThreadList -// - -enum -{ - TL_TID, - TL_PROGRAMCOUNTER, - TL_ENTRYPOINT, - TL_PRIORITY, - TL_STATE, - TL_WAITTYPE, - TL_COLUMNCOUNT -}; - -GenericListViewColumn threadColumns[TL_COLUMNCOUNT] = { - {L"TID", 0.08f}, - {L"PC", 0.21f}, - {L"Entry Point", 0.21f}, - {L"Priority", 0.08f}, - {L"State", 0.21f}, - {L"Wait type", 0.21f}, -}; - -ThreadList::ThreadList(wxWindow* parent, DebugInterface* _cpu) - : GenericListView(parent, threadColumns, TL_COLUMNCOUNT) - , cpu(_cpu) -{ -} - -void ThreadList::reloadThreads() -{ - threads = getEEThreads(); - update(); -} - - -int ThreadList::getRowCount() -{ - return threads.size(); -} - -wxString ThreadList::getColumnText(int item, int col) const -{ - if (item < 0 || item >= (int)threads.size()) - return L""; - - FastFormatUnicode dest; - const EEThread& thread = threads[item]; - - switch (col) - { - case TL_TID: - dest.Write("%d", thread.tid); - break; - case TL_PROGRAMCOUNTER: - if (thread.data.status == THS_RUN) - dest.Write("0x%08X", cpu->getPC()); - else - dest.Write("0x%08X", thread.data.entry); - break; - case TL_ENTRYPOINT: - dest.Write("0x%08X", thread.data.entry_init); - break; - case TL_PRIORITY: - dest.Write("0x%02X", thread.data.currentPriority); - break; - case TL_STATE: - switch (thread.data.status) - { - case THS_BAD: - dest.Write("Bad"); - break; - case THS_RUN: - dest.Write("Running"); - break; - case THS_READY: - dest.Write("Ready"); - break; - case THS_WAIT: - dest.Write("Waiting"); - break; - case THS_SUSPEND: - dest.Write("Suspended"); - break; - case THS_WAIT_SUSPEND: - dest.Write("Waiting/Suspended"); - break; - case THS_DORMANT: - dest.Write("Dormant"); - break; - } - break; - case TL_WAITTYPE: - switch (thread.data.waitType) - { - case WAIT_NONE: - dest.Write("None"); - break; - case WAIT_WAKEUP_REQ: - dest.Write("Wakeup request"); - break; - case WAIT_SEMA: - dest.Write("Semaphore"); - break; - } - break; - default: - return L"Invalid"; - } - - return dest; -} - -void ThreadList::onDoubleClick(int itemIndex, const wxPoint& point) -{ - if (itemIndex < 0 || itemIndex >= (int)threads.size()) - return; - - const EEThread& thread = threads[itemIndex]; - - switch (thread.data.status) - { - case THS_DORMANT: - case THS_BAD: - postEvent(debEVT_GOTOINDISASM, thread.data.entry_init); - break; - case THS_RUN: - postEvent(debEVT_GOTOINDISASM, cpu->getPC()); - break; - default: - postEvent(debEVT_GOTOINDISASM, thread.data.entry); - break; - } -} - -EEThread ThreadList::getRunningThread() -{ - for (size_t i = 0; i < threads.size(); i++) - { - if (threads[i].data.status == THS_RUN) - return threads[i]; - } - - EEThread thread; - memset(&thread, 0, sizeof(thread)); - thread.tid = -1; - return thread; -} - -// -// StackFramesList -// - -enum -{ - SF_ENTRY, - SF_ENTRYNAME, - SF_CURPC, - SF_CUROPCODE, - SF_CURSP, - SF_FRAMESIZE, - SF_COLUMNCOUNT -}; - -GenericListViewColumn stackFrameolumns[SF_COLUMNCOUNT] = { - {L"Entry", 0.12f}, - {L"Name", 0.24f}, - {L"PC", 0.12f}, - {L"Opcode", 0.28f}, - {L"SP", 0.12f}, - {L"Frame Size", 0.12f}}; - -StackFramesList::StackFramesList(wxWindow* parent, DebugInterface* _cpu, CtrlDisassemblyView* _disassembly) - : GenericListView(parent, stackFrameolumns, SF_COLUMNCOUNT) - , cpu(_cpu) - , disassembly(_disassembly) -{ -} - -void StackFramesList::loadStackFrames(EEThread& currentThread) -{ - frames = MipsStackWalk::Walk(cpu, cpu->getPC(), cpu->getRegister(0, 31), cpu->getRegister(0, 29), - currentThread.data.entry_init, currentThread.data.stack); - update(); -} - -int StackFramesList::getRowCount() -{ - return frames.size(); -} - -wxString StackFramesList::getColumnText(int item, int col) const -{ - if (item < 0 || item >= (int)frames.size()) - return L""; - - FastFormatUnicode dest; - const MipsStackWalk::StackFrame& frame = frames[item]; - - switch (col) - { - case SF_ENTRY: - dest.Write("0x%08X", frame.entry); - break; - case SF_ENTRYNAME: - { - const std::string sym = cpu->GetSymbolMap().GetLabelString(frame.entry); - if (!sym.empty()) - { - dest.Write("%s", sym.c_str()); - } - else - { - dest.Write("-"); - } - } - break; - case SF_CURPC: - dest.Write("0x%08X", frame.pc); - break; - case SF_CUROPCODE: - { - if (!cpu->isAlive()) - break; - char temp[512]; - disassembly->getOpcodeText(frame.pc, temp); - dest.Write("%s", temp); - } - break; - case SF_CURSP: - dest.Write("0x%08X", frame.sp); - break; - case SF_FRAMESIZE: - dest.Write("0x%08X", frame.stackSize); - break; - default: - return L"Invalid"; - } - - return dest; -} - -void StackFramesList::onDoubleClick(int itemIndex, const wxPoint& point) -{ - if (itemIndex < 0 || itemIndex >= (int)frames.size()) - return; - - postEvent(debEVT_GOTOINDISASM, frames[itemIndex].pc); -} diff --git a/pcsx2/gui/Debugger/DebuggerLists.h b/pcsx2/gui/Debugger/DebuggerLists.h deleted file mode 100644 index d6a5a7f213..0000000000 --- a/pcsx2/gui/Debugger/DebuggerLists.h +++ /dev/null @@ -1,137 +0,0 @@ -/* PCSX2 - PS2 Emulator for PCs - * Copyright (C) 2002-2014 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 . - */ - -#pragma once -#include -#include "DebugTools/DebugInterface.h" -#include "DebugTools/Breakpoints.h" -#include "DebugTools/BiosDebugData.h" -#include "DebugTools/MipsStackWalk.h" -#include "CtrlDisassemblyView.h" - -struct GenericListViewColumn -{ - const wchar_t* name; - float size; -}; - -class GenericListView : public wxListView -{ -public: - GenericListView(wxWindow* parent, GenericListViewColumn* columns, int columnCount); - void update(); - - wxDECLARE_EVENT_TABLE(); - -protected: - void sizeEvent(wxSizeEvent& evt); - void keydownEvent(wxKeyEvent& evt); - void postEvent(wxEventType type, int value); - void mouseEvent(wxMouseEvent& evt); - void listEvent(wxListEvent& evt); - - [[nodiscard]] virtual wxString getColumnText(int row, int col) const = 0; - virtual int getRowCount() = 0; - virtual void onDoubleClick(int itemIndex, const wxPoint& point){}; - virtual void onRightClick(int itemIndex, const wxPoint& point){}; - virtual void onKeyDown(int key){}; - - // This flag prevents resizing loop in the resizeColumn method of this class - // when the Windows Classic theme with some large resolutions around larger - // than 1024 x 768 have been chosen. - // - // The resizing loop will occur by the ListView_SetColumnWidth macro in the - // Windows SDK called by the wxListCtrl::SetColumnWidth method when the - // conditions above have been chosen. - bool m_isInResizeColumn; - -private: - void insertColumns(GenericListViewColumn* columns, int count); - void resizeColumn(int col, int width); - void resizeColumns(int totalWidth); - [[nodiscard]] wxString OnGetItemText(long item, long col) const override; - - GenericListViewColumn* columns; - wxPoint clickPos; - bool dontResizeColumnsInSizeEventHandler; -}; - -class BreakpointList : public GenericListView -{ -public: - BreakpointList(wxWindow* parent, DebugInterface* _cpu, CtrlDisassemblyView* _disassembly); - void reloadBreakpoints(); - -protected: - void onPopupClick(wxCommandEvent& evt); - - [[nodiscard]] wxString getColumnText(int row, int col) const override; - int getRowCount() override; - void onDoubleClick(int itemIndex, const wxPoint& point) override; - void onRightClick(int itemIndex, const wxPoint& point) override; - void onKeyDown(int key) override; - -private: - int getBreakpointIndex(int itemIndex, bool& isMemory) const; - int getTotalBreakpointCount(); - void editBreakpoint(int itemIndex); - void toggleEnabled(int itemIndex); - void gotoBreakpointAddress(int itemIndex); - void removeBreakpoint(int itemIndex); - void showMenu(const wxPoint& pos); - - std::vector displayedBreakPoints_; - std::vector displayedMemChecks_; - DebugInterface* cpu; - CtrlDisassemblyView* disasm; -}; - -class ThreadList : public GenericListView -{ -public: - ThreadList(wxWindow* parent, DebugInterface* _cpu); - void reloadThreads(); - EEThread getRunningThread(); - -protected: - void onPopupClick(wxCommandEvent& evt); - - [[nodiscard]] wxString getColumnText(int row, int col) const override; - int getRowCount() override; - void onDoubleClick(int itemIndex, const wxPoint& point) override; - -private: - DebugInterface* cpu; - std::vector threads; -}; - -class StackFramesList : public GenericListView -{ -public: - StackFramesList(wxWindow* parent, DebugInterface* _cpu, CtrlDisassemblyView* _disassembly); - void loadStackFrames(EEThread& currentThread); - -protected: - void onPopupClick(wxCommandEvent& evt); - - [[nodiscard]] wxString getColumnText(int row, int col) const override; - int getRowCount() override; - void onDoubleClick(int itemIndex, const wxPoint& point) override; - -private: - DebugInterface* cpu; - CtrlDisassemblyView* disassembly; - std::vector frames; -}; diff --git a/pcsx2/gui/Debugger/DisassemblyDialog.cpp b/pcsx2/gui/Debugger/DisassemblyDialog.cpp deleted file mode 100644 index b45065263d..0000000000 --- a/pcsx2/gui/Debugger/DisassemblyDialog.cpp +++ /dev/null @@ -1,717 +0,0 @@ -/* PCSX2 - PS2 Emulator for PCs - * Copyright (C) 2002-2021 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 . - */ - -#include "PrecompiledHeader.h" - -#include "gui/App.h" -#include "gui/MainFrame.h" -#include "gui/PathDefs.h" -#include "DisassemblyDialog.h" -#include "DebugTools/DebugInterface.h" -#include "DebugTools/DisassemblyManager.h" -#include "DebugTools/Breakpoints.h" -#include "DebugTools/MipsStackWalk.h" -#include "BreakpointWindow.h" -#include "wx/busyinfo.h" - -#ifdef _WIN32 -#include -#endif - -wxBEGIN_EVENT_TABLE(DisassemblyDialog, wxFrame) - EVT_COMMAND( wxID_ANY, debEVT_SETSTATUSBARTEXT, DisassemblyDialog::onDebuggerEvent ) - EVT_COMMAND( wxID_ANY, debEVT_UPDATELAYOUT, DisassemblyDialog::onDebuggerEvent ) - EVT_COMMAND( wxID_ANY, debEVT_GOTOADDRESS, DisassemblyDialog::onDebuggerEvent ) - EVT_COMMAND( wxID_ANY, debEVT_GOTOINMEMORYVIEW, DisassemblyDialog::onDebuggerEvent ) - EVT_COMMAND( wxID_ANY, debEVT_REFERENCEMEMORYVIEW, DisassemblyDialog::onDebuggerEvent ) - EVT_COMMAND( wxID_ANY, debEVT_RUNTOPOS, DisassemblyDialog::onDebuggerEvent ) - EVT_COMMAND( wxID_ANY, debEVT_GOTOINDISASM, DisassemblyDialog::onDebuggerEvent ) - EVT_COMMAND( wxID_ANY, debEVT_STEPOVER, DisassemblyDialog::onDebuggerEvent ) - EVT_COMMAND( wxID_ANY, debEVT_STEPINTO, DisassemblyDialog::onDebuggerEvent ) - EVT_COMMAND( wxID_ANY, debEVT_STEPOUT, DisassemblyDialog::onDebuggerEvent ) - EVT_COMMAND( wxID_ANY, debEVT_UPDATE, DisassemblyDialog::onDebuggerEvent ) - EVT_COMMAND( wxID_ANY, debEVT_BREAKPOINTWINDOW, DisassemblyDialog::onDebuggerEvent ) - EVT_COMMAND( wxID_ANY, debEVT_MAPLOADED, DisassemblyDialog::onDebuggerEvent ) - EVT_SIZE(DisassemblyDialog::onSizeEvent) - EVT_CLOSE( DisassemblyDialog::onClose ) -wxEND_EVENT_TABLE() - -wxBEGIN_EVENT_TABLE(CpuTabPage, wxPanel) - EVT_LISTBOX_DCLICK( wxID_ANY, CpuTabPage::listBoxHandler) -wxEND_EVENT_TABLE() - -DebuggerHelpDialog::DebuggerHelpDialog(wxWindow* parent) - : wxDialog(parent, wxID_ANY, L"Help") -{ - wxBoxSizer* sizer = new wxBoxSizer(wxHORIZONTAL); - - auto fileName = Path::CombineWx(PathDefs::GetDocs(), wxFileName(L"debugger.txt")); - - wxTextFile file(fileName); - wxString text(L""); - - if (file.Open()) - { - text = file.GetFirstLine(); - while (!file.Eof()) - { - text += file.GetNextLine() + L"\r\n"; - } - } - - wxTextCtrl* textControl = new wxTextCtrl(this, wxID_ANY, text, wxDefaultPosition, wxDefaultSize, wxTE_MULTILINE | wxTE_READONLY); - textControl->SetMinSize(wxSize(650, 610)); - sizer->Add(textControl, 1, wxEXPAND); - - SetSizerAndFit(sizer); -} - - -CpuTabPage::CpuTabPage(wxWindow* parent, DebugInterface* _cpu) - : wxPanel(parent), cpu(_cpu) -{ - wxBoxSizer* mainSizer = new wxBoxSizer(wxVERTICAL); - SetSizer(mainSizer); - - leftTabs = new wxNotebook(this, wxID_ANY); - bottomTabs = new wxNotebook(this, wxID_ANY); - disassembly = new CtrlDisassemblyView(this, cpu); - registerList = new CtrlRegisterList(leftTabs, cpu); - functionList = new wxListBox(leftTabs, wxID_ANY, wxDefaultPosition, wxDefaultSize, 0, NULL, wxBORDER_NONE | wxLB_SORT); - - wxPanel* memoryPanel = new wxPanel(bottomTabs); - memory = new CtrlMemView(memoryPanel, cpu); - memorySearch = new CtrlMemSearch(memoryPanel, cpu); - - // create register list and disassembly section - wxBoxSizer* middleSizer = new wxBoxSizer(wxHORIZONTAL); - - wxBoxSizer* miscStuffSizer = new wxBoxSizer(wxHORIZONTAL); - cyclesText = new wxStaticText(this, wxID_ANY, L""); - miscStuffSizer->Add(cyclesText, 0, wxLEFT | wxTOP | wxBOTTOM, 2); - - leftTabs->AddPage(registerList, L"Registers"); - leftTabs->AddPage(functionList, L"Functions"); - - wxBoxSizer* registerSizer = new wxBoxSizer(wxVERTICAL); - registerSizer->Add(miscStuffSizer, 0); - registerSizer->Add(leftTabs, 1); - - middleSizer->Add(registerSizer, 0, wxEXPAND | wxRIGHT, 2); - middleSizer->Add(disassembly, 2, wxEXPAND); - mainSizer->Add(middleSizer, 3, wxEXPAND | wxBOTTOM, 3); - - wxBoxSizer *memorySizer = new wxBoxSizer(wxHORIZONTAL); - memorySizer->Add(memory, 1, wxEXPAND); -#ifdef _WIN32 - memorySearch->SetMaxSize(wxSize(310 * MSW_GetDPIScale(), -1)); -#else - memorySearch->SetMaxSize(wxSize(330, -1)); -#endif - memorySizer->Add(memorySearch, 1, wxEXPAND); - memoryPanel->SetSizer(memorySizer); - memoryPanel->SetBackgroundColour(wxTransparentColor); - - // create bottom section - bottomTabs->AddPage(memoryPanel, L"Memory"); - - breakpointList = new BreakpointList(bottomTabs, cpu, disassembly); - bottomTabs->AddPage(breakpointList, L"Breakpoints"); - - threadList = NULL; - stackFrames = NULL; - if (cpu == &r5900Debug) - { - threadList = new ThreadList(bottomTabs, cpu); - bottomTabs->AddPage(threadList, L"Threads"); - - stackFrames = new StackFramesList(bottomTabs, cpu, disassembly); - bottomTabs->AddPage(stackFrames, L"Stack frames"); - } - - mainSizer->Add(bottomTabs, 1, wxEXPAND); - - mainSizer->Layout(); - - symbolCount = 0; - - lastCycles = 0; - loadCycles(); -} - -void CpuTabPage::clearSymbolMap() -{ - symbolCount = 0; - functionList->Clear(); -} - -void CpuTabPage::reloadSymbolMap() -{ - if (!symbolCount) - { - auto funcs = cpu->GetSymbolMap().GetAllSymbols(ST_FUNCTION); - symbolCount = funcs.size(); - for (size_t i = 0; i < funcs.size(); i++) - { - wxString name = wxString(funcs[i].name.c_str(), wxConvUTF8); - functionList->Append(name, reinterpret_cast(static_cast(funcs[i].address))); - } - } -} - -void CpuTabPage::listBoxHandler(wxCommandEvent& event) -{ - int index = functionList->GetSelection(); - if (event.GetEventObject() == functionList && index >= 0) - { - uptr pos = (uptr)functionList->GetClientData(index); - postEvent(debEVT_GOTOINDISASM, pos); - } -} - -void CpuTabPage::postEvent(wxEventType type, int value) -{ - wxCommandEvent event(type, GetId()); - event.SetEventObject(this); - event.SetClientData(cpu); - event.SetInt(value); - wxPostEvent(this, event); -} - -void CpuTabPage::setBottomTabPage(wxWindow* win) -{ - for (size_t i = 0; i < bottomTabs->GetPageCount(); i++) - { - if (bottomTabs->GetPage(i) == win) - { - bottomTabs->SetSelection(i); - break; - } - } -} - -void CpuTabPage::update() -{ - breakpointList->reloadBreakpoints(); - - if (threadList != NULL && cpu->isAlive()) - { - threadList->reloadThreads(); - - EEThread thread = threadList->getRunningThread(); - if (thread.tid != -1) - stackFrames->loadStackFrames(thread); - } - Refresh(); -} - -void CpuTabPage::loadCycles() -{ - u32 cycles = cpu->getCycles(); - - wchar_t str[64]; - swprintf(str, 64, L"Ctr: %u", cycles - lastCycles); - cyclesText->SetLabel(str); - lastCycles = cycles; -} - -u32 CpuTabPage::getStepOutAddress() -{ - if (threadList == NULL) - return (u32)-1; - - EEThread currentThread = threadList->getRunningThread(); - std::vector frames = - MipsStackWalk::Walk(cpu, cpu->getPC(), cpu->getRegister(0, 31), cpu->getRegister(0, 29), - currentThread.data.entry_init, currentThread.data.stack); - - if (frames.size() < 2) - return (u32)-1; - - return frames[1].pc; -} - -DisassemblyDialog::DisassemblyDialog(wxWindow* parent) : - wxFrame(parent, wxID_ANY, L"Debugger", wxDefaultPosition, wxDefaultSize, wxRESIZE_BORDER | wxCLOSE_BOX | wxCAPTION | wxSYSTEM_MENU | wxMINIMIZE_BOX | wxMAXIMIZE_BOX), - currentCpu(NULL) -{ - int width = g_Conf->EmuOptions.Debugger.WindowWidth; - int height = g_Conf->EmuOptions.Debugger.WindowHeight; - - topSizer = new wxBoxSizer(wxVERTICAL); - wxPanel* panel = new wxPanel(this, wxID_ANY, wxDefaultPosition, wxDefaultSize, wxTAB_TRAVERSAL, _("panel")); - panel->SetSizer(topSizer); - SetIcons(wxGetApp().GetIconBundle()); - - // create top row - wxBoxSizer* topRowSizer = new wxBoxSizer(wxHORIZONTAL); - - breakRunButton = new wxButton(panel, wxID_ANY, L"Run"); - Bind(wxEVT_BUTTON, &DisassemblyDialog::onBreakRunClicked, this, breakRunButton->GetId()); - breakRunButton->Enable(false); - topRowSizer->Add(breakRunButton); - - stepIntoButton = new wxButton(panel, wxID_ANY, L"Step Into" + wxString(" (F11)")); - stepIntoButton->Enable(false); - Bind(wxEVT_BUTTON, &DisassemblyDialog::onStepIntoClicked, this, stepIntoButton->GetId()); - topRowSizer->Add(stepIntoButton); - - stepOverButton = new wxButton(panel, wxID_ANY, L"Step Over" + wxString(" (F10)")); - stepOverButton->Enable(false); - Bind(wxEVT_BUTTON, &DisassemblyDialog::onStepOverClicked, this, stepOverButton->GetId()); - topRowSizer->Add(stepOverButton); - - stepOutButton = new wxButton(panel, wxID_ANY, L"Step Out"); - stepOutButton->Enable(false); - Bind(wxEVT_BUTTON, &DisassemblyDialog::onStepOutClicked, this, stepOutButton->GetId()); - topRowSizer->Add(stepOutButton); - - breakpointButton = new wxButton(panel, wxID_ANY, L"Breakpoint"); - Bind(wxEVT_BUTTON, &DisassemblyDialog::onBreakpointClicked, this, breakpointButton->GetId()); - topRowSizer->Add(breakpointButton); - - helpButton = new wxButton(panel, wxID_ANY, L"Help"); - Bind(wxEVT_BUTTON, &DisassemblyDialog::onHelpClicked, this, helpButton->GetId()); - topRowSizer->Add(helpButton); - - topSizer->Add(topRowSizer, 0, wxLEFT | wxRIGHT | wxTOP, 3); - - // create middle part of the window - middleBook = new wxNotebook(panel, wxID_ANY); - middleBook->SetBackgroundColour(wxColour(0xFFF0F0F0)); - eeTab = new CpuTabPage(middleBook, &r5900Debug); - iopTab = new CpuTabPage(middleBook, &r3000Debug); - middleBook->AddPage(eeTab, L"R5900"); - middleBook->AddPage(iopTab, L"R3000"); - Bind(wxEVT_NOTEBOOK_PAGE_CHANGED, &DisassemblyDialog::onPageChanging, this, middleBook->GetId()); - topSizer->Add(middleBook, 3, wxEXPAND | wxLEFT | wxRIGHT | wxBOTTOM, 3); - currentCpu = eeTab; - - CreateStatusBar(1); - - SetMinSize(wxSize(1000, 600)); - panel->GetSizer()->Fit(this); - - if (width != 0 && height != 0) - SetSize(width, height); - - setDebugMode(true, true); -} - -void DisassemblyDialog::onSizeEvent(wxSizeEvent& event) -{ - if (event.GetEventType() == wxEVT_SIZE) - { - g_Conf->EmuOptions.Debugger.WindowWidth = event.GetSize().x; - g_Conf->EmuOptions.Debugger.WindowHeight = event.GetSize().y; - } - - event.Skip(); -} - -void DisassemblyDialog::onBreakRunClicked(wxCommandEvent& evt) -{ - if (r5900Debug.isCpuPaused()) - { - // If the current PC is on a breakpoint, the user doesn't want to do nothing. - CBreakPoints::SetSkipFirst(BREAKPOINT_EE, r5900Debug.getPC()); - CBreakPoints::SetSkipFirst(BREAKPOINT_IOP, r3000Debug.getPC()); - r5900Debug.resumeCpu(); - } - else - { - r5900Debug.pauseCpu(); - gotoPc(); - } -} - -void DisassemblyDialog::onStepOverClicked(wxCommandEvent& evt) -{ - stepOver(); -} - -void DisassemblyDialog::onStepIntoClicked(wxCommandEvent& evt) -{ - stepInto(); -} - -void DisassemblyDialog::onStepOutClicked(wxCommandEvent& evt) -{ - stepOut(); -} - -void DisassemblyDialog::onHelpClicked(wxCommandEvent& evt) -{ - DebuggerHelpDialog help(this); - help.ShowModal(); -} - -void DisassemblyDialog::onPageChanging(wxCommandEvent& evt) -{ - wxNotebook* notebook = (wxNotebook*)wxWindow::FindWindowById(evt.GetId()); - wxWindow* currentPage = notebook->GetCurrentPage(); - - if (currentPage == eeTab) - currentCpu = eeTab; - else if (currentPage == iopTab) - currentCpu = iopTab; - else - currentCpu = NULL; - - if (currentCpu != NULL) - { - currentCpu->getDisassembly()->SetFocus(); - currentCpu->update(); - } -} - -void DisassemblyDialog::stepOver() -{ - if (!r5900Debug.isAlive() || !r5900Debug.isCpuPaused() || currentCpu == NULL) - return; - DebugInterface* debug = currentCpu->getCpu(); - - CtrlDisassemblyView* disassembly = currentCpu->getDisassembly(); - - // If the current PC is on a breakpoint, the user doesn't want to do nothing. - CBreakPoints::SetSkipFirst(debug->getCpuType(), debug->getPC()); - u32 currentPc = debug->getPC(); - - MIPSAnalyst::MipsOpcodeInfo info = MIPSAnalyst::GetOpcodeInfo(debug, debug->getPC()); - u32 breakpointAddress = currentPc + disassembly->getInstructionSizeAt(currentPc); - if (info.isBranch) - { - if (!info.isConditional) - { - if (info.isLinkedBranch) // jal, jalr - { - // it's a function call with a delay slot - skip that too - breakpointAddress += 4; - } - else // j, ... - { - // in case of absolute branches, set the breakpoint at the branch target - breakpointAddress = info.branchTarget; - } - } - else // beq, ... - { - if (info.conditionMet) - { - breakpointAddress = info.branchTarget; - } - else - { - breakpointAddress = currentPc + 2 * 4; - disassembly->scrollStepping(breakpointAddress); - } - } - } - else - { - disassembly->scrollStepping(breakpointAddress); - } - - CBreakPoints::AddBreakPoint(debug->getCpuType(), breakpointAddress, true); - r5900Debug.resumeCpu(); -} - - -void DisassemblyDialog::stepInto() -{ - if (!r5900Debug.isAlive() || !r5900Debug.isCpuPaused() || currentCpu == NULL) - return; - - DebugInterface* debug = currentCpu->getCpu(); - CtrlDisassemblyView* disassembly = currentCpu->getDisassembly(); - - // If the current PC is on a breakpoint, the user doesn't want to do nothing. - CBreakPoints::SetSkipFirst(debug->getCpuType(), debug->getPC()); - u32 currentPc = debug->getPC(); - - MIPSAnalyst::MipsOpcodeInfo info = MIPSAnalyst::GetOpcodeInfo(debug, debug->getPC()); - u32 breakpointAddress = currentPc + disassembly->getInstructionSizeAt(currentPc); - if (info.isBranch) - { - if (!info.isConditional) - { - breakpointAddress = info.branchTarget; - } - else - { - if (info.conditionMet) - { - breakpointAddress = info.branchTarget; - } - else - { - breakpointAddress = currentPc + 2 * 4; - disassembly->scrollStepping(breakpointAddress); - } - } - } - - if (info.isSyscall) - breakpointAddress = info.branchTarget; - - CBreakPoints::AddBreakPoint(debug->getCpuType(), breakpointAddress, true); - r5900Debug.resumeCpu(); -} - -void DisassemblyDialog::stepOut() -{ - if (!r5900Debug.isAlive() || !r5900Debug.isCpuPaused() || currentCpu == NULL) - return; - DebugInterface* debug = currentCpu->getCpu(); - // If the current PC is on a breakpoint, the user doesn't want to do nothing. - CBreakPoints::SetSkipFirst(debug->getCpuType(), debug->getPC()); - - u32 addr = currentCpu->getStepOutAddress(); - if (addr == (u32)-1) - return; - - CBreakPoints::AddBreakPoint(debug->getCpuType(), addr, true); - r5900Debug.resumeCpu(); -} - -void DisassemblyDialog::onBreakpointClicked(wxCommandEvent& evt) -{ - if (currentCpu == NULL) - return; - - BreakpointWindow bpw(this, currentCpu->getCpu()); - if (bpw.ShowModal() == wxID_OK) - { - bpw.addBreakpoint(); - update(); - } -} - -void DisassemblyDialog::onDebuggerEvent(wxCommandEvent& evt) -{ - wxEventType type = evt.GetEventType(); - - if (type == debEVT_SETSTATUSBARTEXT) - { - DebugInterface* cpu = reinterpret_cast(evt.GetClientData()); - if (cpu != NULL && currentCpu != NULL && cpu == currentCpu->getCpu()) - GetStatusBar()->SetLabel(evt.GetString()); - } - else if (type == debEVT_UPDATELAYOUT) - { - if (currentCpu != NULL) - currentCpu->GetSizer()->Layout(); - topSizer->Layout(); - update(); - } - else if (type == debEVT_GOTOADDRESS) - { - DebugInterface* cpu = reinterpret_cast(evt.GetClientData()); - u64 addr; - if (!executeExpressionWindow(this, cpu, addr)) - return; - - if (currentCpu != NULL) - { - // GetInt() is 0 when called by the disassembly view, 1 when called by the memory view - if (!evt.GetInt()) - currentCpu->getDisassembly()->gotoAddress(addr); - else - currentCpu->getMemoryView()->gotoAddress(addr); - } - update(); - } - else if (type == debEVT_GOTOINMEMORYVIEW) - { - if (currentCpu != NULL) - { - currentCpu->showMemoryView(); - - currentCpu->getMemoryView()->gotoAddress(evt.GetInt(), true); - currentCpu->getDisassembly()->SetFocus(); - } - } - else if (type == debEVT_REFERENCEMEMORYVIEW) - { - if (currentCpu != NULL) - { - currentCpu->getMemoryView()->updateReference(evt.GetInt()); - } - } - else if (type == debEVT_RUNTOPOS) - { - CBreakPoints::AddBreakPoint(currentCpu->getCpu()->getCpuType(), evt.GetInt(), true); - currentCpu->getCpu()->resumeCpu(); - } - else if (type == debEVT_GOTOINDISASM) - { - if (currentCpu != NULL) - { - u32 pos = evt.GetInt(); - currentCpu->getDisassembly()->gotoAddress(pos); - currentCpu->getDisassembly()->SetFocus(); - update(); - } - } - else if (type == debEVT_STEPOVER) - { - if (currentCpu != NULL) - stepOver(); - } - else if (type == debEVT_STEPINTO) - { - if (currentCpu != NULL) - stepInto(); - } - else if (type == debEVT_UPDATE) - { - update(); - } - else if (type == debEVT_BREAKPOINTWINDOW) - { - wxCommandEvent evt; - onBreakpointClicked(evt); - } - else if (type == debEVT_MAPLOADED) - { - wxBusyInfo wait("Please wait, Reloading ELF functions"); - eeTab->clearSymbolMap(); - iopTab->clearSymbolMap(); - eeTab->reloadSymbolMap(); - iopTab->reloadSymbolMap(); - } - else if (type == debEVT_STEPOUT) - { - if (currentCpu != NULL) - stepOut(); - } -} - -void DisassemblyDialog::onClose(wxCloseEvent& evt) -{ - auto* mainFrame = GetMainFramePtr(); - - Hide(); - mainFrame->CheckMenuItem(MenuId_Debug_Open, false); -} - -void DisassemblyDialog::update() -{ - if (currentCpu != NULL) - { - stepOverButton->Enable(true); - breakpointButton->Enable(true); - currentCpu->update(); - } - else - { - stepOverButton->Enable(false); - breakpointButton->Enable(false); - } -} - -void DisassemblyDialog::reset() -{ - eeTab->getDisassembly()->clearFunctions(); - eeTab->clearSymbolMap(); - iopTab->getDisassembly()->clearFunctions(); - iopTab->clearSymbolMap(); -} - -void DisassemblyDialog::populate() -{ - eeTab->reloadSymbolMap(); - iopTab->reloadSymbolMap(); -} - -void DisassemblyDialog::gotoPc() -{ - eeTab->getDisassembly()->gotoPc(); - iopTab->getDisassembly()->gotoPc(); -} - -void DisassemblyDialog::setDebugMode(bool debugMode, bool switchPC) -{ - bool running = r5900Debug.isAlive(); - - eeTab->Enable(running); - iopTab->Enable(running); - - if (running) - { - breakRunButton->Enable(true); - - if (currentCpu == NULL) - { - wxWindow* currentPage = middleBook->GetCurrentPage(); - - if (currentPage == eeTab) - currentCpu = eeTab; - else if (currentPage == iopTab) - currentCpu = iopTab; - - if (currentCpu != NULL) - currentCpu->update(); - } - - if (debugMode) - { - if (!CBreakPoints::GetBreakpointTriggered()) - { - wxBusyInfo wait("Please wait, Reading ELF functions"); - populate(); - } - CBreakPoints::ClearTemporaryBreakPoints(); - breakRunButton->SetLabel(L"Run"); - - stepOverButton->Enable(true); - stepIntoButton->Enable(true); - stepOutButton->Enable(currentCpu == eeTab); - - if (switchPC || CBreakPoints::GetBreakpointTriggered()) - gotoPc(); - - if (CBreakPoints::GetBreakpointTriggered()) - { - if (currentCpu != NULL) - currentCpu->getDisassembly()->SetFocus(); - CBreakPoints::SetBreakpointTriggered(false); - CBreakPoints::SetSkipFirst(BREAKPOINT_EE, 0); - CBreakPoints::SetSkipFirst(BREAKPOINT_IOP, 0); - } - - if (currentCpu != NULL) - currentCpu->loadCycles(); - } - else - { - breakRunButton->SetLabel(L"Break"); - - stepIntoButton->Enable(false); - stepOverButton->Enable(false); - stepOutButton->Enable(false); - } - } - else - { - breakRunButton->SetLabel(L"Run"); - stepIntoButton->Enable(false); - stepOverButton->Enable(false); - stepOutButton->Enable(false); - currentCpu = NULL; - } - - update(); -} diff --git a/pcsx2/gui/Debugger/DisassemblyDialog.h b/pcsx2/gui/Debugger/DisassemblyDialog.h deleted file mode 100644 index 136103d979..0000000000 --- a/pcsx2/gui/Debugger/DisassemblyDialog.h +++ /dev/null @@ -1,122 +0,0 @@ -/* PCSX2 - PS2 Emulator for PCs - * Copyright (C) 2002-2014 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 . - */ - -#pragma once -#include -#include -#include "gui/App.h" - -#include "CtrlDisassemblyView.h" -#include "CtrlRegisterList.h" -#include "CtrlMemView.h" -#include "CtrlMemSearch.h" -#include "DebugEvents.h" -#include "DebuggerLists.h" -#include "gui/MSWstuff.h" - -class DebuggerHelpDialog: public wxDialog -{ -public: - DebuggerHelpDialog(wxWindow* parent); -}; - -inline int getDebugFontWidth() -{ - return (int) ceil(g_Conf->EmuOptions.Debugger.FontWidth*MSW_GetDPIScale()); -} - -inline int getDebugFontHeight() -{ - return (int)ceil(g_Conf->EmuOptions.Debugger.FontHeight*MSW_GetDPIScale()); -} - -class CpuTabPage: public wxPanel -{ -public: - CpuTabPage(wxWindow* parent, DebugInterface* _cpu); - DebugInterface* getCpu() { return cpu; }; - CtrlDisassemblyView* getDisassembly() { return disassembly; }; - CtrlRegisterList* getRegisterList() { return registerList; }; - CtrlMemView* getMemoryView() { return memory; }; - CtrlMemSearch* getMemorySearch() { return memorySearch; }; - wxNotebook* getBottomTabs() { return bottomTabs; }; - void update(); - void showMemoryView() { setBottomTabPage(memory); }; - void loadCycles(); - void reloadSymbolMap(); - void clearSymbolMap(); - u32 getStepOutAddress(); - - void listBoxHandler(wxCommandEvent& event); - wxDECLARE_EVENT_TABLE(); -private: - void setBottomTabPage(wxWindow* win); - void postEvent(wxEventType type, int value); - - DebugInterface* cpu; - CtrlDisassemblyView* disassembly; - CtrlRegisterList* registerList; - wxListBox* functionList; - CtrlMemView* memory; - CtrlMemSearch* memorySearch; - wxNotebook* bottomTabs; - wxNotebook* leftTabs; - BreakpointList* breakpointList; - wxStaticText* cyclesText; - ThreadList* threadList; - StackFramesList* stackFrames; - u32 lastCycles; - u32 symbolCount; -}; - -class DisassemblyDialog : public wxFrame -{ -public: - DisassemblyDialog( wxWindow* parent=NULL ); - virtual ~DisassemblyDialog() = default; - - static wxString GetNameStatic() { return L"DisassemblyDialog"; } - wxString GetDialogName() const { return GetNameStatic(); } - - void update(); - void reset(); - void populate(); - void setDebugMode(bool debugMode, bool switchPC); - - wxDECLARE_EVENT_TABLE(); -protected: - void onBreakRunClicked(wxCommandEvent& evt); - void onStepOverClicked(wxCommandEvent& evt); - void onStepIntoClicked(wxCommandEvent& evt); - void onStepOutClicked(wxCommandEvent& evt); - void onHelpClicked(wxCommandEvent& evt); - void onDebuggerEvent(wxCommandEvent& evt); - void onPageChanging(wxCommandEvent& evt); - void onBreakpointClicked(wxCommandEvent& evt); - void onSizeEvent(wxSizeEvent& event); - void onClose(wxCloseEvent& evt); - void stepOver(); - void stepInto(); - void stepOut(); - void gotoPc(); -private: - CpuTabPage* eeTab; - CpuTabPage* iopTab; - CpuTabPage* currentCpu; - wxNotebook* middleBook; - - wxBoxSizer* topSizer; - wxButton *breakRunButton, *stepIntoButton, *stepOverButton, *stepOutButton, *breakpointButton, *helpButton; -}; diff --git a/pcsx2/gui/Dialogs/AboutBoxDialog.cpp b/pcsx2/gui/Dialogs/AboutBoxDialog.cpp deleted file mode 100644 index b6adb6c6fd..0000000000 --- a/pcsx2/gui/Dialogs/AboutBoxDialog.cpp +++ /dev/null @@ -1,71 +0,0 @@ -/* PCSX2 - PS2 Emulator for PCs - * Copyright (C) 2002-2010 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 . - */ - -#include "PrecompiledHeader.h" -#include "gui/App.h" -#include "gui/AppCommon.h" -#include "gui/MSWstuff.h" - -#include "gui/Dialogs/ModalPopups.h" - -#include "gui/EmbeddedImage.h" -#include "gui/Resources/Logo.h" - -#include -#include - -using namespace pxSizerFlags; - -// -------------------------------------------------------------------------------------- -// AboutBoxDialog Implementation -// -------------------------------------------------------------------------------------- - -Dialogs::AboutBoxDialog::AboutBoxDialog(wxWindow* parent) - : wxDialogWithHelpers(parent, AddAppName(_("About %s")), pxDialogFlags()) -{ - const float scale = MSW_GetDPIScale(); - SetMinWidth(scale * 460); - - wxImage img = EmbeddedImage().Get(); - img.Rescale(img.GetWidth() * scale, img.GetHeight() * scale, wxIMAGE_QUALITY_HIGH); - auto bitmap_logo = new wxStaticBitmap(this, wxID_ANY, wxBitmap(img)); - - *this += bitmap_logo | StdCenter(); - -#ifdef _WIN32 - const int padding = 15; -#else - const int padding = 8; -#endif - - wxBoxSizer& general(*new wxBoxSizer(wxHORIZONTAL)); - general += new wxHyperlinkCtrl(this, wxID_ANY, _("Website"), L"https://pcsx2.net"); - general += padding; - general += new wxHyperlinkCtrl(this, wxID_ANY, _("Support Forums"), L"https://forums.pcsx2.net"); - general += padding; - general += new wxHyperlinkCtrl(this, wxID_ANY, _("GitHub Repository"), L"https://github.com/PCSX2/pcsx2"); - general += padding; - general += new wxHyperlinkCtrl(this, wxID_ANY, _("License"), L"https://github.com/PCSX2/pcsx2/blob/master/pcsx2/Docs/License.txt"); - - *this += Text(_("PlayStation 2 Emulator:")); - *this += general | StdCenter(); - *this += Text(_("Big thanks to everyone who contributed to the project throughout the years.")); - - wxButton& closeButton = *new wxButton(this, wxID_OK, _("Close")); - closeButton.SetFocus(); - *this += closeButton | StdCenter(); - - SetSizerAndFit(GetSizer()); -} diff --git a/pcsx2/gui/Dialogs/AssertionDialog.cpp b/pcsx2/gui/Dialogs/AssertionDialog.cpp deleted file mode 100644 index 4a4b121191..0000000000 --- a/pcsx2/gui/Dialogs/AssertionDialog.cpp +++ /dev/null @@ -1,68 +0,0 @@ -/* PCSX2 - PS2 Emulator for PCs - * Copyright (C) 2002-2010 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 . - */ - -#include "PrecompiledHeader.h" -#include "gui/App.h" -#include "ModalPopups.h" -#include "gui/MSWstuff.h" - -using namespace pxSizerFlags; - -Dialogs::AssertionDialog::AssertionDialog( const wxString& text, const wxString& stacktrace ) - : wxDialogWithHelpers( NULL, _("Assertion Failure - ") + pxGetAppName(), pxDialogFlags().Resize(!stacktrace.IsEmpty()) ) -{ - SetMinWidth( MSW_GetDPIScale() * 720 ); - - wxFlexGridSizer* flexgrid = new wxFlexGridSizer( 1 ); - flexgrid->AddGrowableCol( 0 ); - SetSizer( flexgrid ); - - wxTextCtrl* traceArea = NULL; - - if( !stacktrace.IsEmpty() ) - { - flexgrid->AddGrowableRow( 1 ); - - traceArea = new wxTextCtrl( - this, wxID_ANY, wxEmptyString, wxDefaultPosition, wxDefaultSize, - wxTE_READONLY | wxTE_MULTILINE | wxTE_RICH2 | wxHSCROLL - ); - - traceArea->SetDefaultStyle( wxTextAttr( wxNullColour, wxNullColour, pxGetFixedFont() ) ); - traceArea->SetFont( pxGetFixedFont() ); - - int fonty = traceArea->GetCharHeight(); - - traceArea->WriteText( stacktrace ); - traceArea->SetMinSize( wxSize( traceArea->GetMinWidth(), (fonty+1)*18 ) ); - traceArea->SetInsertionPoint( 0 ); - traceArea->ShowPosition( 0 ); - } - - *this += Heading( text ) | StdExpand(); - - if( traceArea != NULL ) *this += traceArea | pxExpand.Border(wxTOP|wxLEFT|wxRIGHT,8); - - *this += Heading( - L"\nDo you want to stop the program [Yes/No]?" - L"\nOr press [Ignore] to suppress further assertions." - ) | StdExpand(); - - *this += new ModalButtonPanel( this, MsgButtons().YesNo().Ignore() ) | StdCenter(); - - if( wxWindow* idyes = FindWindowById( wxID_YES ) ) - idyes->SetFocus(); -} - diff --git a/pcsx2/gui/Dialogs/BaseConfigurationDialog.cpp b/pcsx2/gui/Dialogs/BaseConfigurationDialog.cpp deleted file mode 100644 index 8673504523..0000000000 --- a/pcsx2/gui/Dialogs/BaseConfigurationDialog.cpp +++ /dev/null @@ -1,304 +0,0 @@ -/* PCSX2 - PS2 Emulator for PCs - * Copyright (C) 2002-2010 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 . - */ - -#include "PrecompiledHeader.h" -#include "System.h" -#include "gui/App.h" -#include "gui/MSWstuff.h" - -#include "ConfigurationDialog.h" -#include "ModalPopups.h" -#include "gui/Panels/ConfigurationPanels.h" - -#include -#include -#include -#include - -wxDEFINE_EVENT(pxEvt_ApplySettings, wxCommandEvent); -wxDEFINE_EVENT(pxEvt_SetSettingsPage, wxCommandEvent); -wxDEFINE_EVENT(pxEvt_SomethingChanged, wxCommandEvent); - -using namespace Panels; - -// configure the orientation of the listbox based on the platform -// For now, they're all on the left. -static const int s_orient = wxLB_LEFT; - - -class ScopedOkButtonDisabler -{ -protected: - Dialogs::BaseConfigurationDialog* m_parent; - - wxWindow* m_apply; - wxWindow* m_ok; - wxWindow* m_cancel; - -public: - ScopedOkButtonDisabler( Dialogs::BaseConfigurationDialog* parent ) - { - m_parent = parent; - m_parent->AllowApplyActivation( false ); - - m_apply = m_parent->FindWindow( wxID_APPLY ); - m_ok = m_parent->FindWindow( wxID_OK ); - m_cancel = m_parent->FindWindow( wxID_CANCEL ); - - if (m_apply) m_apply ->Disable(); - if (m_ok) m_ok ->Disable(); - if (m_cancel) m_cancel->Disable(); - } - - // Use this to prevent the Apply button from being re-enabled. - void DetachApply() - { - m_apply = NULL; - } - - void DetachAll() - { - m_apply = m_ok = m_cancel = NULL; - } - - virtual ~ScopedOkButtonDisabler() - { - if (m_apply) m_apply ->Enable(); - if (m_ok) m_ok ->Enable(); - if (m_cancel) m_cancel->Enable(); - - m_parent->AllowApplyActivation( true ); - } -}; - -// -------------------------------------------------------------------------------------- -// BaseApplicableDialog (implementations) -// -------------------------------------------------------------------------------------- -wxIMPLEMENT_DYNAMIC_CLASS(BaseApplicableDialog, wxDialogWithHelpers); - -BaseApplicableDialog::BaseApplicableDialog( wxWindow* parent, const wxString& title, const pxDialogCreationFlags& cflags ) - : wxDialogWithHelpers( parent, title, cflags.MinWidth(425).Minimize() ) -{ - Init(); -} - -BaseApplicableDialog::~BaseApplicableDialog() -{ - m_ApplyState.DoCleanup(); -} - -wxString BaseApplicableDialog::GetDialogName() const -{ - pxFailDev( "This class must implement GetDialogName!" ); - return L"Unnamed"; -} - - -void BaseApplicableDialog::Init() -{ - Bind(pxEvt_ApplySettings, &BaseApplicableDialog::OnSettingsApplied, this); - - wxCommandEvent applyEvent( pxEvt_ApplySettings ); - applyEvent.SetId( GetId() ); - AddPendingEvent( applyEvent ); -} - -void BaseApplicableDialog::OnSettingsApplied( wxCommandEvent& evt ) -{ - evt.Skip(); - if( evt.GetId() == GetId() ) - AppStatusEvent_OnSettingsApplied(); -} - - -// -------------------------------------------------------------------------------------- -// BaseConfigurationDialog Implementations -// -------------------------------------------------------------------------------------- -Dialogs::BaseConfigurationDialog::BaseConfigurationDialog( wxWindow* parent, const wxString& title, int idealWidth ) - : _parent( parent, title ) -{ - float scale = MSW_GetDPIScale(); - - SetMinWidth( scale * idealWidth ); - m_listbook = NULL; - m_allowApplyActivation = true; - - Bind(wxEVT_BUTTON, &BaseConfigurationDialog::OnOk_Click, this, wxID_OK); - Bind(wxEVT_BUTTON, &BaseConfigurationDialog::OnCancel_Click, this, wxID_CANCEL); - Bind(wxEVT_BUTTON, &BaseConfigurationDialog::OnApply_Click, this, wxID_APPLY); - Bind(wxEVT_BUTTON, &BaseConfigurationDialog::OnScreenshot_Click, this, wxID_SAVE); - - Bind(pxEvt_SetSettingsPage, &BaseConfigurationDialog::OnSetSettingsPage, this); - - // ---------------------------------------------------------------------------- - // Bind a variety of standard "something probably changed" events. If the user invokes - // any of these, we'll automatically de-gray the Apply button for this dialog box. :) - - Bind(wxEVT_TEXT, &BaseConfigurationDialog::OnSomethingChanged, this); - Bind(wxEVT_TEXT_ENTER, &BaseConfigurationDialog::OnSomethingChanged, this); - - Bind(wxEVT_RADIOBUTTON, &BaseConfigurationDialog::OnSomethingChanged, this); - Bind(wxEVT_COMBOBOX, &BaseConfigurationDialog::OnSomethingChanged, this); - Bind(wxEVT_CHECKBOX, &BaseConfigurationDialog::OnSomethingChanged, this); - Bind(wxEVT_BUTTON, &BaseConfigurationDialog::OnSomethingChanged, this); - Bind(wxEVT_CHOICE, &BaseConfigurationDialog::OnSomethingChanged, this); - Bind(wxEVT_LISTBOX, &BaseConfigurationDialog::OnSomethingChanged, this); - Bind(wxEVT_SPINCTRL, &BaseConfigurationDialog::OnSomethingChanged, this); - Bind(wxEVT_SLIDER, &BaseConfigurationDialog::OnSomethingChanged, this); - Bind(wxEVT_DIRPICKER_CHANGED, &BaseConfigurationDialog::OnSomethingChanged, this); - - Bind(pxEvt_SomethingChanged, &BaseConfigurationDialog::OnSomethingChanged, this); -} - -void Dialogs::BaseConfigurationDialog::AddListbook( wxSizer* sizer ) -{ - if( !sizer ) sizer = GetSizer(); - *sizer += m_listbook | pxExpand.Border( wxLEFT | wxRIGHT, 2 ); -} - -void Dialogs::BaseConfigurationDialog::CreateListbook( wxImageList& bookicons ) -{ - m_listbook = new wxListbook( this, wxID_ANY, wxDefaultPosition, wxDefaultSize, s_orient ); - m_listbook->SetImageList( &bookicons ); - m_ApplyState.StartBook( m_listbook ); -} - -void Dialogs::BaseConfigurationDialog::AddOkCancel( wxSizer* sizer ) -{ - _parent::AddOkCancel( sizer, true ); - if( wxWindow* apply = FindWindow( wxID_APPLY ) ) apply->Disable(); - SomethingChanged_StateModified_IsChanged(); - - wxBitmapButton& screenshotButton(*new wxBitmapButton(this, wxID_SAVE, wxGetApp().GetScreenshotBitmap())); - screenshotButton.SetToolTip( _("Saves a snapshot of this settings panel to a PNG file.") ); - - *m_extraButtonSizer += screenshotButton|pxMiddle; -} - -void Dialogs::BaseConfigurationDialog::OnSetSettingsPage( wxCommandEvent& evt ) -{ - if( !m_listbook ) return; - - size_t pages = m_labels.GetCount(); - - for( size_t i=0; iSetSelection( i ); - break; - } - } -} - -void Dialogs::BaseConfigurationDialog::SomethingChanged() -{ - if( wxWindow* apply = FindWindow( wxID_APPLY ) ) apply->Enable(); - SomethingChanged_StateModified_IsChanged(); -} - -void Dialogs::BaseConfigurationDialog::OnSomethingChanged( wxCommandEvent& evt ) -{ - evt.Skip(); - if (!m_allowApplyActivation) return; - if ((evt.GetId() != wxID_OK) && (evt.GetId() != wxID_CANCEL) && (evt.GetId() != wxID_APPLY)) - SomethingChanged(); -} - -void Dialogs::BaseConfigurationDialog::AllowApplyActivation( bool allow ) -{ - m_allowApplyActivation = allow; -} - -void Dialogs::BaseConfigurationDialog::OnOk_Click( wxCommandEvent& evt ) -{ - ScopedOkButtonDisabler disabler(this); - - //same as for OnApply_Click - Apply(); - - if( m_ApplyState.ApplyAll() ) - { - if( wxWindow* apply = FindWindow( wxID_APPLY ) ) apply->Disable(); - SomethingChanged_StateModified_IsChanged(); - if( m_listbook ) GetConfSettingsTabName() = m_labels[m_listbook->GetSelection()]; - AppSaveSettings(); - disabler.DetachAll(); - evt.Skip(); - } -} - -void Dialogs::BaseConfigurationDialog::OnApply_Click( wxCommandEvent& evt ) -{ - ScopedOkButtonDisabler disabler(this); - - //if current instance also holds settings that need to be applied, apply them. - //Currently only used by SysConfigDialog, which applies the preset and derivatives (menu system). - //Needs to come before actual panels Apply since they enable/disable themselves upon Preset state, - // so the preset needs to be applied first. - Apply(); - - if( m_ApplyState.ApplyAll() ) - disabler.DetachApply(); - - if( m_listbook ) GetConfSettingsTabName() = m_labels[m_listbook->GetSelection()]; - AppSaveSettings(); - - SomethingChanged_StateModified_IsChanged(); -} - -void Dialogs::BaseConfigurationDialog::OnCancel_Click( wxCommandEvent& evt ) -{ - //same as for Ok/Apply: let SysConfigDialog clean-up the presets and derivatives (menu system) if needed. - Cancel(); - - evt.Skip(); - if( m_listbook ) GetConfSettingsTabName() = m_labels[m_listbook->GetSelection()]; -} - -void Dialogs::BaseConfigurationDialog::OnScreenshot_Click( wxCommandEvent& evt ) -{ - wxBitmap memBmp; - - { - wxWindowDC dc( this ); - wxSize dcsize( dc.GetSize() ); - wxMemoryDC memDC( memBmp = wxBitmap( dcsize.x, dcsize.y ) ); - memDC.Blit( wxPoint(), dcsize, &dc, wxPoint() ); - } - - wxString pagename( m_listbook ? (L"_" + m_listbook->GetPageText( m_listbook->GetSelection() )) : wxString() ); - wxString filenameDefault; - filenameDefault.Printf( L"%s_%s%s.png", pxGetAppName().Lower().c_str(), GetDialogName().c_str(), pagename.c_str() ); - filenameDefault.Replace( L"/", L"-" ); - - wxString filename( wxFileSelector( _("Save dialog screenshots to..."), g_Conf->Folders.Snapshots.ToString(), - filenameDefault, L"png", wxEmptyString, wxFD_SAVE | wxFD_OVERWRITE_PROMPT, this ) ); - - if( !filename.IsEmpty() ) - { - ScopedBusyCursor busy( Cursor_ReallyBusy ); -#ifdef __WXMSW__ - // HACK: This works around an actual wx3.0 bug at the cost of icon - // quality. See http://trac.wxwidgets.org/ticket/14403 . - wxImage image = memBmp.ConvertToImage(); - if (image.HasAlpha()) - image.ClearAlpha(); - image.SaveFile( filename, wxBITMAP_TYPE_PNG ); -#else - memBmp.SaveFile( filename, wxBITMAP_TYPE_PNG ); -#endif - } -} diff --git a/pcsx2/gui/Dialogs/BaseConfigurationDialog.inl b/pcsx2/gui/Dialogs/BaseConfigurationDialog.inl deleted file mode 100644 index 3aca3a124d..0000000000 --- a/pcsx2/gui/Dialogs/BaseConfigurationDialog.inl +++ /dev/null @@ -1,27 +0,0 @@ -/* PCSX2 - PS2 Emulator for PCs - * Copyright (C) 2002-2010 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 . - */ - -#pragma once - -#include - -template< typename T > -void Dialogs::BaseConfigurationDialog::AddPage( const wxChar* label, int iconid ) -{ - const int curidx = m_labels.Add( label ); - m_ApplyState.SetCurrentPage( curidx ); - m_listbook->AddPage( new T( m_listbook ), wxGetTranslation( label ), - ( 0 == GetConfSettingsTabName().CmpNoCase(label) ), iconid ); -} diff --git a/pcsx2/gui/Dialogs/ConfigurationDialog.h b/pcsx2/gui/Dialogs/ConfigurationDialog.h deleted file mode 100644 index a81ac61892..0000000000 --- a/pcsx2/gui/Dialogs/ConfigurationDialog.h +++ /dev/null @@ -1,207 +0,0 @@ -/* PCSX2 - PS2 Emulator for PCs - * Copyright (C) 2002-2021 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 . - */ - -#pragma once - -#include -#include - -#include "gui/AppCommon.h" -#include "gui/ApplyState.h" -#include "gui/App.h" - -namespace Panels -{ - class BaseSelectorPanel; - class McdConfigPanel_Toggles; - class BaseMcdListPanel; -} - -wxDECLARE_EVENT(pxEvt_SomethingChanged, wxCommandEvent); - -namespace Dialogs -{ - // -------------------------------------------------------------------------------------- - // BaseConfigurationDialog - // -------------------------------------------------------------------------------------- - class BaseConfigurationDialog : public BaseApplicableDialog - { - typedef BaseApplicableDialog _parent; - - protected: - wxListbook* m_listbook; - wxArrayString m_labels; - bool m_allowApplyActivation; - - public: - virtual ~BaseConfigurationDialog() = default; - BaseConfigurationDialog(wxWindow* parent, const wxString& title, int idealWidth); - - public: - void AddOkCancel( wxSizer* sizer=NULL ); - void AddListbook( wxSizer* sizer=NULL ); - void CreateListbook( wxImageList& bookicons ); - - virtual void SomethingChanged(); - - template< typename T > - void AddPage( const wxChar* label, int iconid ); - - void AllowApplyActivation( bool allow=true ); - virtual bool SomethingChanged_StateModified_IsChanged(){ //returns the state of the apply button. - if( wxWindow* apply = FindWindow( wxID_APPLY ) ) - return apply->IsEnabled(); - return false; - } - - protected: - void OnOk_Click( wxCommandEvent& evt ); - void OnCancel_Click( wxCommandEvent& evt ); - void OnApply_Click( wxCommandEvent& evt ); - void OnScreenshot_Click( wxCommandEvent& evt ); - - void OnSetSettingsPage( wxCommandEvent& evt ); - void OnSomethingChanged( wxCommandEvent& evt ); - - virtual wxString& GetConfSettingsTabName() const=0; - - virtual void Apply() {}; - virtual void Cancel() {}; - }; - - // -------------------------------------------------------------------------------------- - // SysConfigDialog - // -------------------------------------------------------------------------------------- - class SysConfigDialog : public BaseConfigurationDialog - { - public: - virtual ~SysConfigDialog() = default; - SysConfigDialog(wxWindow* parent=NULL); - static wxString GetNameStatic() { return L"CoreSettings"; } - wxString GetDialogName() const { return GetNameStatic(); } - void Apply(); - void Cancel(); - - //Stores the state of the apply button in a global var. - //This var will be used by KB shortcuts commands to decide if the gui should be modified (only when no intermediate changes) - virtual bool SomethingChanged_StateModified_IsChanged(){ - return g_ConfigPanelChanged = BaseConfigurationDialog::SomethingChanged_StateModified_IsChanged(); - } - - protected: - virtual wxString& GetConfSettingsTabName() const { return g_Conf->SysSettingsTabName; } - - pxCheckBox* m_check_presets; - wxSlider* m_slider_presets; - pxStaticText* m_msg_preset; - void AddPresetsControl(); - void Preset_Scroll(wxScrollEvent &event); - void Presets_Toggled(wxCommandEvent &event); - void UpdateGuiForPreset ( int presetIndex, bool presetsEnabled ); - }; - - // -------------------------------------------------------------------------------------- - // InterfaceLanguageDialog - // -------------------------------------------------------------------------------------- - class InterfaceLanguageDialog : public BaseConfigurationDialog - { - public: - InterfaceLanguageDialog(wxWindow* parent = NULL); - virtual ~InterfaceLanguageDialog() = default; - - static wxString GetNameStatic() { return L"InterfaceLanguage"; } - wxString GetDialogName() const { return GetNameStatic(); } - - protected: - virtual wxString& GetConfSettingsTabName() const { return g_Conf->AppSettingsTabName; } - }; - - - // -------------------------------------------------------------------------------------- - // McdConfigDialog - // -------------------------------------------------------------------------------------- - class McdConfigDialog : public BaseConfigurationDialog - { - typedef BaseConfigurationDialog _parent; - - protected: - Panels::BaseMcdListPanel* m_panel_mcdlist; - - public: - virtual ~McdConfigDialog() = default; - McdConfigDialog(wxWindow* parent=NULL); - static wxString GetNameStatic() { return L"McdConfig"; } - wxString GetDialogName() const { return GetNameStatic(); } - - protected: - virtual wxString& GetConfSettingsTabName() const { return g_Conf->McdSettingsTabName; } - //void OnMultitapClicked( wxCommandEvent& evt ); - }; - - // -------------------------------------------------------------------------------------- - // CreateMemoryCardDialog - // -------------------------------------------------------------------------------------- - class CreateMemoryCardDialog : public wxDialogWithHelpers - { - protected: - wxDirName m_mcdpath; - wxString m_mcdfile; - wxTextCtrl* m_text_filenameInput; - wxStaticText *m_mcd_Extension; - - //wxFilePickerCtrl* m_filepicker; - pxRadioPanel *m_radio_CardType; - -#ifdef __WXMSW__ - pxCheckBox *m_check_CompressNTFS; -#endif - - public: - virtual ~CreateMemoryCardDialog() = default; - CreateMemoryCardDialog( wxWindow* parent, const wxDirName& mcdpath, const wxString& suggested_mcdfileName); - - //duplicate of MemoryCardFile::Create. Don't know why the existing method isn't used. - avih - static bool CreateIt( const wxString& mcdFile, uint sizeInMB, bool isPSX ); - wxString result_createdMcdFilename; - //wxDirName GetPathToMcds() const; - - protected: - void CreateControls(); - void OnRadioChanged(wxCommandEvent &evt); - void OnOk_Click(wxCommandEvent &evt); - }; - - // -------------------------------------------------------------------------------------- - // ConvertMemoryCardDialog - // -------------------------------------------------------------------------------------- - class ConvertMemoryCardDialog : public wxDialogWithHelpers - { - protected: - wxDirName m_mcdPath; - wxString m_mcdSourceFilename; - wxTextCtrl *m_text_filenameInput; - pxRadioPanel *m_radio_CardType; - - public: - virtual ~ConvertMemoryCardDialog() = default; - ConvertMemoryCardDialog( wxWindow* parent, const wxDirName& mcdPath, MemoryCardType type, const wxString& sourceFilename ); - - protected: - void CreateControls( const MemoryCardType sourceType ); - void OnOk_Click( wxCommandEvent& evt ); - bool ConvertToFile( const wxFileName& sourcePath, const wxFileName& targetPath, const u32 sizeInMB ); - bool ConvertToFolder( const wxFileName& sourcePath, const wxFileName& targetPath ); - }; -} diff --git a/pcsx2/gui/Dialogs/ConfirmationDialogs.cpp b/pcsx2/gui/Dialogs/ConfirmationDialogs.cpp deleted file mode 100644 index 5fc949d9f2..0000000000 --- a/pcsx2/gui/Dialogs/ConfirmationDialogs.cpp +++ /dev/null @@ -1,322 +0,0 @@ -/* PCSX2 - PS2 Emulator for PCs - * Copyright (C) 2002-2010 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 . - */ - -#include "PrecompiledHeader.h" -#include "System.h" -#include "gui/App.h" -#include "gui/MSWstuff.h" - -#include "ModalPopups.h" -#include "gui/StringHelpers.h" - -using namespace pxSizerFlags; - -bool MsgButtons::Allows( wxWindowID id ) const -{ - switch( id ) - { - case wxID_OK: return HasOK(); - case wxID_CANCEL: return HasCancel(); - case wxID_APPLY: return HasApply(); - case wxID_YES: return HasYes(); - case wxID_NO: return HasNo(); - case wxID_YESTOALL: return HasYes() && AllowsToAll(); - case wxID_NOTOALL: return HasNo() && AllowsToAll(); - - case wxID_ABORT: return HasAbort(); - case wxID_RETRY: return HasRetry(); - - // [TODO] : maybe add in an Ignore All? - case wxID_IGNORE: return HasIgnore(); - - case wxID_RESET: return HasReset(); - case wxID_CLOSE: return HasClose(); - } - - if (id <= wxID_LOWEST) - return HasCustom(); - - return false; -} - -static wxString ResultToString( int result, const MsgButtons& buttons ) -{ - switch( result ) - { - case wxID_OK: return L"ok"; - case wxID_CANCEL: return L"cancel"; - case wxID_APPLY: return L"apply"; - case wxID_YES: return L"yes"; - case wxID_NO: return L"no"; - case wxID_YESTOALL: return L"yestoall"; - case wxID_NOTOALL: return L"notoall"; - - case wxID_ABORT: return L"abort"; - case wxID_RETRY: return L"retry"; - - // [TODO] : maybe add in an Ignore All? - case wxID_IGNORE: return L"ignore"; - - case wxID_RESET: return L"reset"; - case wxID_CLOSE: return L"close"; - } - - if (result <= wxID_LOWEST) - return buttons.GetCustomLabelId(); - - return wxEmptyString; -} - -static wxWindowID ParseThatResult( const wxString& src, const MsgButtons& validTypes ) -{ - if( !pxAssert( !src.IsEmpty() ) ) return wxID_ANY; - - static const wxWindowID retvals[] = - { - wxID_OK, wxID_CANCEL, wxID_APPLY, - wxID_YES, wxID_NO, - wxID_YESTOALL, wxID_NOTOALL, - - wxID_ABORT, wxID_RETRY, wxID_IGNORE, - - wxID_RESET, - wxID_ANY, - }; - - for (wxWindowID retval : retvals) - { - if( (validTypes.Allows(retval)) && (src == ResultToString(retval, validTypes)) ) - return retval; - } - - return wxID_NONE; -} - -static bool pxTrySetFocus( wxWindow& parent, wxWindowID id ) -{ - if( wxWindow* found = parent.FindWindowById( id ) ) - { - found->SetFocus(); - return true; - } - - return false; -} - -static bool pxTrySetFocus( wxWindow* parent, wxWindowID id ) -{ - if( parent == NULL ) return false; - return pxTrySetFocus( *parent, id ); -} - -void MsgButtons::SetBestFocus( wxWindow& dialog ) const -{ - if( HasOK() && pxTrySetFocus( dialog, wxID_OK ) ) return; - if( HasNo() && pxTrySetFocus( dialog, wxID_NO ) ) return; - if( HasClose() && pxTrySetFocus( dialog, wxID_CLOSE ) ) return; - if( HasRetry() && pxTrySetFocus( dialog, wxID_RETRY ) ) return; - - // Other confirmational types of buttons must be explicitly focused by the user or - // by an implementing dialog. We won't do it here implicitly because accidental - // "on focus" typed keys could invoke really unwanted actions. - - // (typically close/ok/retry/etc. aren't so bad that accidental clicking does terrible things) -} - -void MsgButtons::SetBestFocus( wxWindow* dialog ) const -{ - if( dialog == NULL ) return; - SetBestFocus( *dialog ); -} - - -wxWindowID pxIssueConfirmation( wxDialogWithHelpers& confirmDlg, const MsgButtons& buttons ) -{ - if( confirmDlg.GetMinWidth() <= 0 ) confirmDlg.SetMinWidth( 400 * MSW_GetDPIScale()); - - confirmDlg += new ModalButtonPanel( &confirmDlg, buttons ) | pxCenter.Border( wxTOP, 8 ); - buttons.SetBestFocus( confirmDlg ); - confirmDlg.SetSizerAndFit(confirmDlg.GetSizer()); - return confirmDlg.ShowModal(); -} - - -wxWindowID pxIssueConfirmation( wxDialogWithHelpers& confirmDlg, const MsgButtons& buttons, const wxString& disablerKey ) -{ - wxConfigBase* cfg = GetAppConfig(); - - if( cfg != NULL ) - { - cfg->SetPath( L"/PopupDisablers" ); - bool recdef = cfg->IsRecordingDefaults(); - cfg->SetRecordDefaults( false ); - - wxString result = cfg->Read( disablerKey, L"enabled" ); - - cfg->SetRecordDefaults( recdef ); - cfg->SetPath( L"/" ); - - result.MakeLower(); - wxArrayString split; - SplitString( split, result, L"," ); - - // if only one param (no comma!?) then assume the entry is invalid and force a - // re-display of the dialog. - - if( split.Count() > 1 ) - { - result = split[0]; - if( result == L"disabled" || result == L"off" || result == L"no" ) - { - int result = ParseThatResult( split[1], buttons ); - if( result != wxID_ANY ) return result; - } - } - } - - pxCheckBox* DisablerCtrl = NULL; - if( cfg != NULL ) - { - // Add an option that allows the user to disable this popup from showing again. - // (and if the config hasn't been initialized yet, then assume the dialog as non-disablable) - - DisablerCtrl = new pxCheckBox(&confirmDlg, _("Do not show this dialog again.")); - - confirmDlg += 8; - confirmDlg += DisablerCtrl | wxSF.Centre(); - - if( buttons != MsgButtons().OK() ) - pxSetToolTip(DisablerCtrl, _("Disables this popup and whatever response you select here will be automatically used from now on.")); - else - pxSetToolTip(DisablerCtrl, _("The popup will not be shown again. This setting can be undone from the settings panels.")); - - } - - int modalResult = pxIssueConfirmation( confirmDlg, buttons ); - - if( modalResult != wxID_CANCEL && cfg != NULL ) - { - wxString cfgResult = ResultToString( modalResult, buttons ); - if( DisablerCtrl->IsChecked() && !cfgResult.IsEmpty() ) - { - cfg->SetPath( L"/PopupDisablers" ); - cfg->Write( disablerKey, L"disabled," + cfgResult ); - cfg->SetPath( L"/" ); - cfg->Flush(); - } - } - return modalResult; -} - -ModalButtonPanel::ModalButtonPanel( wxWindow* parent, const MsgButtons& buttons ) - : wxPanelWithHelpers( parent, wxHORIZONTAL ) -{ - // Populate the Button Sizer. - // We prefer this over the built-in wxWidgets ButtonSizer stuff used for other types of - // dialogs because we offer more button types, and we don't want the MSW default behavior - // of right-justified buttons. - - if( buttons.HasCustom() ) - AddCustomButton( pxID_CUSTOM, buttons.GetCustomLabel() ); - - // Order of wxID_RESET and custom button have been picked fairly arbitrarily, since there's - // no standard governing those. - -#ifdef __WXGTK__ - // GTK+ / Linux inverts OK/CANCEL order -- cancel / no first, OK / Yes later. >_< - if( buttons.HasCancel() ) - AddActionButton( wxID_CANCEL ); - - if( buttons.HasNo() ) - { - AddActionButton( wxID_NO ); - if( buttons.AllowsToAll() ) AddActionButton( wxID_NOTOALL ); - } - - if( buttons.HasIgnore() ) - AddCustomButton( wxID_IGNORE, _("Ignore") ); - - if( buttons.HasOK() || buttons.HasYes() ) // Extra space between Affirm and Cancel Actions - GetSizer()->Add(0, 0, 1, wxEXPAND, 0); -#endif - - if( buttons.HasOK() ) - AddActionButton( wxID_OK ); - - if( buttons.HasYes() ) - { - AddActionButton( wxID_YES ); - if( buttons.AllowsToAll() ) - AddActionButton( wxID_YESTOALL ); - } - -#ifdef __WXGTK__ - if( buttons.HasRetry() ) - AddCustomButton( wxID_RETRY, _("Retry") ); - - if( buttons.HasAbort() ) - AddCustomButton( wxID_ABORT, _("Abort") ); -#else - if( buttons.HasAbort() ) - AddCustomButton( wxID_ABORT, _("Abort") ); - - if( buttons.HasRetry() ) - AddCustomButton( wxID_RETRY, _("Retry") ); -#endif - - if( buttons.HasReset() ) - AddCustomButton( wxID_RESET, _("Reset") ); - - if( buttons.HasClose() ) - AddActionButton( wxID_CLOSE ); - -#ifndef __WXGTK__ - if( buttons.HasNo() ) - { - AddActionButton( wxID_NO ); - if( buttons.AllowsToAll() ) - AddActionButton( wxID_NOTOALL ); - } - - if( buttons.HasIgnore() ) - AddCustomButton( wxID_IGNORE, _("Ignore") ); - - if( buttons.HasCancel() ) - AddActionButton( wxID_CANCEL ); -#endif -} - -void ModalButtonPanel::OnActionButtonClicked( wxCommandEvent& evt ) -{ - wxWindow* toplevel = wxGetTopLevelParent( this ); - if( wxDialog* dialog = wxDynamicCast( toplevel, wxDialog ) ) - dialog->EndModal( evt.GetId() ); - // If the dialog doesn't close, and you're using it for a modeless dialog - hint: - // read the name of the class. ;) -} - -void ModalButtonPanel::AddCustomButton( wxWindowID id, const wxString& label ) -{ - *this += new wxButton( this, id, label ) | pxBorder().Proportion(6); - Bind(wxEVT_BUTTON, &ModalButtonPanel::OnActionButtonClicked, this, id); -} - -// This is for buttons that are defined internally by wxWidgets, such as wxID_CANCEL, wxID_ABORT, etc. -// wxWidgets will assign the labels and stuff for us. :D -void ModalButtonPanel::AddActionButton( wxWindowID id ) -{ - *this += new wxButton( this, id ) | pxBorder().Proportion(6); - Bind(wxEVT_BUTTON, &ModalButtonPanel::OnActionButtonClicked, this, id); -} diff --git a/pcsx2/gui/Dialogs/ConvertMemoryCardDialog.cpp b/pcsx2/gui/Dialogs/ConvertMemoryCardDialog.cpp deleted file mode 100644 index 22399a8768..0000000000 --- a/pcsx2/gui/Dialogs/ConvertMemoryCardDialog.cpp +++ /dev/null @@ -1,242 +0,0 @@ -/* PCSX2 - PS2 Emulator for PCs - * Copyright (C) 2002-2015 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 . - */ - -#include "PrecompiledHeader.h" -#include "ConfigurationDialog.h" -#include "System.h" - -#include "gui/MSWstuff.h" - -#include "MemoryCardFile.h" -#include "MemoryCardFolder.h" -#include - -enum MemoryCardConversionType { - MemoryCardConversion_File_8MB, - MemoryCardConversion_File_16MB, - MemoryCardConversion_File_32MB, - MemoryCardConversion_File_64MB, - MemoryCardConversion_Folder, - MemoryCardConversion_MaxCount -}; - -Dialogs::ConvertMemoryCardDialog::ConvertMemoryCardDialog( wxWindow* parent, const wxDirName& mcdPath, MemoryCardType type, const wxString& sourceFilename ) - : wxDialogWithHelpers( parent, _( "Convert a memory card to a different format" ) ) - , m_mcdPath( mcdPath ) - , m_mcdSourceFilename( sourceFilename ) -{ - SetMinWidth( 472 * MSW_GetDPIScale()); - - CreateControls( type ); - - if ( m_radio_CardType ) m_radio_CardType->Realize(); - - wxBoxSizer& s_buttons( *new wxBoxSizer( wxHORIZONTAL ) ); - s_buttons += new wxButton( this, wxID_OK, _( "Convert" ) ) | pxProportion( 2 ); - s_buttons += pxStretchSpacer( 3 ); - s_buttons += new wxButton( this, wxID_CANCEL ) | pxProportion( 2 ); - - wxBoxSizer& s_padding( *new wxBoxSizer( wxVERTICAL ) ); - - s_padding += Heading( wxString( _( "Convert: " ) ) + ( mcdPath + m_mcdSourceFilename ).GetFullPath() ).Unwrapped() | pxSizerFlags::StdExpand(); - - wxBoxSizer& s_filename( *new wxBoxSizer( wxHORIZONTAL ) ); - s_filename += Heading( _( "To: " ) ).Unwrapped().Align(wxALIGN_RIGHT) | pxProportion(1); - m_text_filenameInput->SetValue( wxFileName( m_mcdSourceFilename ).GetName() + L"_converted" ); - s_filename += m_text_filenameInput | pxProportion(2); - s_filename += Heading( L".ps2" ).Align(wxALIGN_LEFT) | pxProportion(1); - - s_padding += s_filename | pxSizerFlags::StdExpand(); - - s_padding += m_radio_CardType | pxSizerFlags::StdExpand(); - - if ( type != MemoryCardType::File ) { - s_padding += Heading( pxE( L"Please note that the resulting file may not actually contain all saves, depending on how many are in the source memory card." ) ); - } - s_padding += Heading( pxE( L"WARNING: Converting a memory card may take a while! Please do not close the emulator during the conversion process, even if the emulator is no longer responding to input." ) ); - - s_padding += 12; - s_padding += s_buttons | pxSizerFlags::StdCenter(); - - *this += s_padding | pxSizerFlags::StdExpand(); - - Bind(wxEVT_BUTTON, &ConvertMemoryCardDialog::OnOk_Click, this, wxID_OK); - Bind(wxEVT_TEXT_ENTER, &ConvertMemoryCardDialog::OnOk_Click, this, m_text_filenameInput->GetId()); - - SetSizerAndFit(GetSizer()); - - m_text_filenameInput->SetFocus(); - m_text_filenameInput->SelectAll(); -} - -void Dialogs::ConvertMemoryCardDialog::CreateControls( const MemoryCardType sourceType ) { - m_text_filenameInput = new wxTextCtrl( this, wxID_ANY, wxEmptyString, wxDefaultPosition, wxDefaultSize, wxTE_PROCESS_ENTER ); - - RadioPanelItem toFile8MB = RadioPanelItem( _( "8 MB File (most compatible)" ), pxE( L"Convert this memory card to a standard 8 MB Memory Card .ps2 file." ) ) - .SetInt( MemoryCardConversionType::MemoryCardConversion_File_8MB ); - RadioPanelItem toFile16MB = RadioPanelItem( _( "16 MB File" ), pxE( L"Convert this memory card to a 16 MB Memory Card .ps2 file." ) ) - .SetInt( MemoryCardConversionType::MemoryCardConversion_File_16MB ); - RadioPanelItem toFile32MB = RadioPanelItem( _( "32 MB File" ), pxE( L"Convert this memory card to a 32 MB Memory Card .ps2 file." ) ) - .SetInt( MemoryCardConversionType::MemoryCardConversion_File_32MB ); - RadioPanelItem toFile64MB = RadioPanelItem( _( "64 MB File" ), pxE( L"Convert this memory card to a 64 MB Memory Card .ps2 file." ) ) - .SetInt( MemoryCardConversionType::MemoryCardConversion_File_64MB ); - RadioPanelItem toFolder = RadioPanelItem( _( "Folder" ), _( - "Convert this memory card to a folder of individual saves. " - "Unlimited capacity for saving and is not compatible with other PS2 emulators. " - "Allows direct access to the saves which makes it easy to view and back-up per game." ) ) - .SetInt( MemoryCardConversionType::MemoryCardConversion_Folder ); - - const RadioPanelItem tblForFile[] = { toFolder }; - const RadioPanelItem tblForFolder[] = { toFile8MB, toFile16MB, toFile32MB, toFile64MB }; - - switch ( sourceType ) { - case MemoryCardType::File: - m_radio_CardType = new pxRadioPanel( this, tblForFile ); - break; - case MemoryCardType::Folder: - m_radio_CardType = new pxRadioPanel( this, tblForFolder ); - break; - default: - Console.Error( "Memory Card Conversion: Invalid source type!" ); - return; - } - - m_radio_CardType->SetDefaultItem( 0 ); -} - -void Dialogs::ConvertMemoryCardDialog::OnOk_Click( wxCommandEvent& evt ) { - wxString composedName = m_text_filenameInput->GetValue().Trim() + L".ps2"; - - wxString errMsg; - if ( !isValidNewFilename( composedName, m_mcdPath, errMsg, 5 ) ) { - wxString message; - message.Printf( _( "Error (%s)" ), errMsg.c_str() ); - Msgbox::Alert( message, _( "Convert memory card" ) ); - m_text_filenameInput->SetFocus(); - m_text_filenameInput->SelectAll(); - return; - } - - bool success = false; - - wxFileName sourcePath = ( m_mcdPath + m_mcdSourceFilename ); - wxFileName targetPath = ( m_mcdPath + composedName ); - if ( m_radio_CardType ) { - MemoryCardConversionType targetType = (MemoryCardConversionType)m_radio_CardType->SelectedItem().SomeInt; - - switch ( targetType ) { - case MemoryCardConversionType::MemoryCardConversion_File_8MB: - success = ConvertToFile( sourcePath, targetPath, 8 ); - break; - case MemoryCardConversionType::MemoryCardConversion_File_16MB: - success = ConvertToFile( sourcePath, targetPath, 16 ); - break; - case MemoryCardConversionType::MemoryCardConversion_File_32MB: - success = ConvertToFile( sourcePath, targetPath, 32 ); - break; - case MemoryCardConversionType::MemoryCardConversion_File_64MB: - success = ConvertToFile( sourcePath, targetPath, 64 ); - break; - case MemoryCardConversionType::MemoryCardConversion_Folder: - success = ConvertToFolder( sourcePath, targetPath ); - break; - default: - Msgbox::Alert( _( "This target type is not supported!" ), _( "Convert memory card" ) ); - return; - } - } - - if ( !success ) { - Msgbox::Alert( _( "Memory Card conversion failed for unknown reasons." ), _( "Convert memory card" ) ); - return; - } - - EndModal( wxID_OK ); -} - -bool Dialogs::ConvertMemoryCardDialog::ConvertToFile( const wxFileName& sourcePath, const wxFileName& targetPath, const u32 sizeInMB ) { - // Conversion method: Open FolderMcd as usual, then read the raw data from it and write it to a file stream - - wxFFile targetFile( targetPath.GetFullPath(), L"wb" ); - if ( !targetFile.IsOpened() ) { - return false; - } - - FolderMemoryCard sourceFolderMemoryCard; - Pcsx2Config::McdOptions config; - config.Enabled = true; - config.Type = MemoryCardType::Folder; - sourceFolderMemoryCard.Open( StringUtil::wxStringToUTF8String(sourcePath.GetFullPath()), config, ( sizeInMB * 1024 * 1024 ) / FolderMemoryCard::ClusterSize, false, "" ); - - u8 buffer[FolderMemoryCard::PageSizeRaw]; - u32 adr = 0; - while ( adr < sourceFolderMemoryCard.GetSizeInClusters() * FolderMemoryCard::ClusterSizeRaw ) { - sourceFolderMemoryCard.Read( buffer, adr, FolderMemoryCard::PageSizeRaw ); - targetFile.Write( buffer, FolderMemoryCard::PageSizeRaw ); - adr += FolderMemoryCard::PageSizeRaw; - } - - targetFile.Close(); - sourceFolderMemoryCard.Close( false ); - - return true; -} - -bool Dialogs::ConvertMemoryCardDialog::ConvertToFolder( const wxFileName& sourcePath, const wxFileName& targetPath ) { - // Conversion method: Read all pages of the FileMcd into a FolderMcd, then just write that out with the regular methods - - wxFFile sourceFile( sourcePath.GetFullPath(), L"rb" ); - if ( !sourceFile.IsOpened() ) { - return false; - } - - u8 buffer[FolderMemoryCard::PageSizeRaw]; - FolderMemoryCard targetFolderMemoryCard; - Pcsx2Config::McdOptions config; - config.Enabled = true; - config.Type = MemoryCardType::Folder; - u32 adr = 0; - - for ( int i = 0; i < 2; ++i ) { - // Before writing the data, we first simulate the entire process without actual writes to the file system. - // This ensures that if we crash/fail due to a corrupted memory card file system or similar, we do so during - // the simulation run, and don't actually write out any partial data to the host file system. - bool simulateWrites = i == 0; - targetFolderMemoryCard.Open(StringUtil::wxStringToUTF8String(targetPath.GetFullPath()), config, 0, false, "", simulateWrites ); - - adr = 0; - sourceFile.Seek( 0 ); - while ( !sourceFile.Eof() ) { - int size = sourceFile.Read( buffer, FolderMemoryCard::PageSizeRaw ); - if ( size > 0 ) { - targetFolderMemoryCard.Save( buffer, adr, size ); - adr += size; - } - } - - targetFolderMemoryCard.Close(); - } - - sourceFile.Close(); - - if ( adr != FolderMemoryCard::TotalSizeRaw ) { - // reset memory card metrics in superblock to the default 8MB, since the converted card was different - targetFolderMemoryCard.Open(StringUtil::wxStringToUTF8String(targetPath.GetFullPath()), config, 0, true, "" ); - targetFolderMemoryCard.SetSizeInMB( 8 ); - targetFolderMemoryCard.Close(); - } - - return true; -} diff --git a/pcsx2/gui/Dialogs/CreateMemoryCardDialog.cpp b/pcsx2/gui/Dialogs/CreateMemoryCardDialog.cpp deleted file mode 100644 index 42a161c05e..0000000000 --- a/pcsx2/gui/Dialogs/CreateMemoryCardDialog.cpp +++ /dev/null @@ -1,264 +0,0 @@ -/* PCSX2 - PS2 Emulator for PCs - * Copyright (C) 2002-2018 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 . - */ - -#include "PrecompiledHeader.h" -#include "ConfigurationDialog.h" -#include "System.h" -#include "gui/MSWstuff.h" - -#include "MemoryCardFile.h" -//#include -#include - -using namespace pxSizerFlags; - -extern wxString GetMsg_McdNtfsCompress(); -/* -wxFilePickerCtrl* CreateMemoryCardFilePicker( wxWindow* parent, uint portidx, uint slotidx, const wxString& filename=wxEmptyString ) -{ - return new wxFilePickerCtrl( parent, wxID_ANY, filename, - wxsFormat(_("Select memory card for Port %u / Slot %u"), portidx+1, slotidx+1), // picker window title - L"*.ps2", // default wildcard - wxDefaultPosition, wxDefaultSize, - wxFLP_DEFAULT_STYLE & ~wxFLP_FILE_MUST_EXIST - ); - -} -*/ -Dialogs::CreateMemoryCardDialog::CreateMemoryCardDialog( wxWindow* parent, const wxDirName& mcdpath, const wxString& suggested_mcdfileName) - : wxDialogWithHelpers( parent, _("Create a new memory card") ) - , m_mcdpath( mcdpath ) - , m_mcdfile( suggested_mcdfileName )//suggested_and_result_mcdfileName.IsEmpty() ? g_Conf->Mcd[slot].Filename.GetFullName() -{ - SetMinWidth( 472 * MSW_GetDPIScale()); - //m_filepicker = NULL; - - CreateControls(); - - //m_filepicker = CreateMemoryCardFilePicker( this, m_port, m_slot, filepath ); - - // ---------------------------- - // Sizers and Layout - // ---------------------------- - - - wxBoxSizer& s_buttons( *new wxBoxSizer(wxHORIZONTAL) ); - s_buttons += new wxButton( this, wxID_OK, _("Create") ) | pxProportion(2); - s_buttons += pxStretchSpacer(3); - s_buttons += new wxButton( this, wxID_CANCEL ) | pxProportion(2); - if (m_radio_CardType) - m_radio_CardType->Realize(); - - wxBoxSizer& s_padding( *new wxBoxSizer(wxVERTICAL) ); - - //s_padding += Heading(_("Select the size for your new memory card.")); - -// if( m_filepicker ) -// s_padding += m_filepicker | StdExpand(); -// else - { - s_padding += Heading( _( "New memory card:" ) ) | StdExpand(); - s_padding += Heading( wxString(_("At folder: ")) + (m_mcdpath + m_mcdfile).GetPath() ).Unwrapped() | StdExpand(); - - wxBoxSizer& s_filename( *new wxBoxSizer(wxHORIZONTAL) ); - s_filename += Heading( _("Select file name: ")).Unwrapped().Align(wxALIGN_RIGHT) | pxProportion(1); - m_text_filenameInput->SetValue ((m_mcdpath + m_mcdfile).GetName()); - s_filename += m_text_filenameInput | pxProportion(2); - s_filename += m_mcd_Extension | pxProportion(1); - s_padding += s_filename | StdExpand(); - } - - s_padding += m_radio_CardType | StdExpand(); -#ifdef __WXMSW__ - if (m_check_CompressNTFS) - s_padding += m_check_CompressNTFS | StdExpand(); -#endif - - - s_padding += s_buttons | StdCenter(); - - *this += s_padding | StdExpand(); - - Bind(wxEVT_BUTTON, &CreateMemoryCardDialog::OnOk_Click, this, wxID_OK); - Bind(wxEVT_TEXT_ENTER, &CreateMemoryCardDialog::OnOk_Click, this, m_text_filenameInput->GetId()); - Bind(wxEVT_RADIOBUTTON, &CreateMemoryCardDialog::OnRadioChanged, this); - - // ...Typical solution to everything? Or are we doing something weird? - SetSizerAndFit(GetSizer()); - - m_text_filenameInput->SetFocus(); - m_text_filenameInput->SelectAll(); -} - -bool Dialogs::CreateMemoryCardDialog::CreateIt( const wxString& mcdFile, uint sizeInMB, bool isPSX ) -{ - //int enc[16] = {0x77,0x7f,0x7f,0x77,0x7f,0x7f,0x77,0x7f,0x7f,0x77,0x7f,0x7f,0,0,0,0}; - - // PS2 Memory Card - u8 m_effeffs[528 * 16]; - memset8<0xff>(m_effeffs); - - // PSX Memory Card; 8192 is the size in bytes of a single block of a PSX memory card (8 KiB). - u8 m_effeffs_psx[8192]; - memset8<0xff>(m_effeffs_psx); - - // Since isPSX will have a default false state, it makes more sense to check "not PSX" first - if (!isPSX) { - Console.WriteLn("(FileMcd) Creating new PS2 %uMB memory card: '%ls'", sizeInMB, WX_STR(mcdFile)); - } - else { - Console.WriteLn("(FileMcd) Creating new PSX 128 KiB memory card: '%ls'", WX_STR(mcdFile)); - } - - wxFFile fp( mcdFile, L"wb" ); - if( !fp.IsOpened() ) return false; - - static const int MC2_MBSIZE = 1024 * 528 * 2; // Size of a single megabyte of card data - - if (!isPSX) { - for (uint i = 0; i<(MC2_MBSIZE*sizeInMB) / sizeof(m_effeffs); i++) { - if (fp.Write(m_effeffs, sizeof(m_effeffs)) == 0) { - return false; - } - } - } else { - // PSX cards consist of 16 blocks, each 8 KiB in size. - for (uint i = 0; i < 16; i++) { - if (fp.Write(m_effeffs_psx, sizeof(m_effeffs_psx)) == 0) { - return false; - } - } - } - - return true; -} - -void Dialogs::CreateMemoryCardDialog::OnRadioChanged(wxCommandEvent& evt) -{ - evt.Skip(); - - m_mcd_Extension->SetLabel(m_radio_CardType->SelectedItem().SomeInt == 1 ? L".mcr" : L".ps2"); -} - -void Dialogs::CreateMemoryCardDialog::OnOk_Click( wxCommandEvent& evt ) -{ - // Save status of the NTFS compress checkbox for future reference. - // [TODO] Remove g_Conf->McdCompressNTFS, and have this dialog load/save directly from the ini. - -#ifdef __WXMSW__ - g_Conf->EmuOptions.McdCompressNTFS = m_check_CompressNTFS->GetValue(); -#endif - result_createdMcdFilename=L"_INVALID_FILE_NAME_"; - - wxString composedName = m_text_filenameInput->GetValue().Trim() + (m_radio_CardType->SelectedItem().SomeInt == 1 ? L".mcr" : L".ps2"); - - wxString errMsg; - if( !isValidNewFilename(composedName, m_mcdpath, errMsg, 5) ) - { - wxString message; - message.Printf(_("Error (%s)"), errMsg.c_str()); - Msgbox::Alert( message, _("Create memory card") ); - m_text_filenameInput->SetFocus(); - m_text_filenameInput->SelectAll(); - return; - } - - wxString fullPath = ( m_mcdpath + composedName ).GetFullPath(); - if (m_radio_CardType && m_radio_CardType->SelectedItem().SomeInt == 0) { - // user selected to create a folder memory card - if ( !wxFileName::Mkdir( fullPath ) ) { - Msgbox::Alert( - _( "Error: The directory for the memory card could not be created." ), - _( "Create memory card" ) - ); - } else { - // also create an empty superblock so we can recognize memory card folders based on if they have a superblock - wxFFile superblock( wxFileName( fullPath, L"_pcsx2_superblock" ).GetFullPath(), L"wb" ); - superblock.Close(); - } - } else { - // otherwise create a file - if (!CreateIt( - fullPath, - m_radio_CardType ? m_radio_CardType->SelectedItem().SomeInt : 8, - m_radio_CardType->SelectedItem().SomeInt == 1)) { - Msgbox::Alert( - _( "Error: The memory card could not be created." ), - _( "Create memory card" ) - ); - return; - } - } - - result_createdMcdFilename = composedName; - EndModal( wxID_OK ); -} - -void Dialogs::CreateMemoryCardDialog::CreateControls() -{ -#ifdef __WXMSW__ - m_check_CompressNTFS = new pxCheckBox( this, - _("Use NTFS compression when creating this card."), - GetMsg_McdNtfsCompress() - ); - - m_check_CompressNTFS->SetToolTip( pxEt( L"NTFS compression can be changed manually at any time by using file properties from Windows Explorer." - ) - ); - - // Initial value of the checkbox is saved between calls to the dialog box. If the user checks - // the option, it remains checked for future dialog. If the user unchecks it, ditto. - m_check_CompressNTFS->SetValue( g_Conf->EmuOptions.McdCompressNTFS ); -#endif - - m_text_filenameInput = new wxTextCtrl(this, wxID_ANY, wxEmptyString, wxDefaultPosition, wxDefaultSize, wxTE_PROCESS_ENTER); - m_mcd_Extension = new wxStaticText(this, wxID_ANY, L".ps2"); - - const RadioPanelItem tbl_CardTypes[] = - { - RadioPanelItem(_("8 MB [most compatible]"), _("This is the standard Sony-provisioned size, and is supported by all games and BIOS versions.")) - . SetToolTip(_t("Always use this option if you want the safest and surest memory card behavior.")) - . SetInt(8), - - RadioPanelItem(_("16 MB"), _("A typical size for 3rd-party memory cards which should work with most games.")) - . SetToolTip(_t("16 and 32 MB cards have roughly the same compatibility factor.")) - . SetInt(16), - - RadioPanelItem(_("32 MB"), _("A typical size for 3rd-party memory cards which should work with most games.")) - . SetToolTip(_t("16 and 32 MB cards have roughly the same compatibility factor.")) - . SetInt(32), - - RadioPanelItem(_("64 MB"), _("Low compatibility warning: Yes it's very big, but may not work with many games.")) - . SetToolTip(_t("Use at your own risk. Erratic memory card behavior is possible (though unlikely).")) - . SetInt(64), - - RadioPanelItem(_("Folder [Recommended]"), _("Store memory card contents in the host filesystem instead of a file.")) - . SetToolTip(_t("Dynamically allocate and store memory card contents in a folder.\n" - "Only exposes the save files for the running game, rather than the whole memory card.\n" - "You can see the structure and the saves with your File Explorer.\n" - "Can be used to back-up individual saves instead of all saves on a memcard.\n" - "Incompatible with PS2 memory card editing tools or savegame managers (such as MyMC, MyMCPlus).\n" - "You can always convert back between folder type and single memcard file.\n")) - . SetInt(0), - - RadioPanelItem(_("128 KiB (PS1)"), _("This is the standard Sony-provisioned size PS1 memory card, only compatible with PS1 games.")) - . SetToolTip(_t("This memory card is required by PS1 games. It is not compatible with PS2 games.")) - . SetInt(1) - }; - - m_radio_CardType = new pxRadioPanel(this, tbl_CardTypes); - m_radio_CardType->SetDefaultItem(0); -} - diff --git a/pcsx2/gui/Dialogs/FirstTimeWizard.cpp b/pcsx2/gui/Dialogs/FirstTimeWizard.cpp deleted file mode 100644 index 7c960f522d..0000000000 --- a/pcsx2/gui/Dialogs/FirstTimeWizard.cpp +++ /dev/null @@ -1,223 +0,0 @@ -/* PCSX2 - PS2 Emulator for PCs - * Copyright (C) 2002-2021 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 . - */ - -#include "PrecompiledHeader.h" -#include "System.h" -#include "gui/MSWstuff.h" - -#include "ModalPopups.h" -#include "gui/Panels/ConfigurationPanels.h" -#include -#include -#include - -using namespace Panels; -using namespace pxSizerFlags; - -wxIMPLEMENT_DYNAMIC_CLASS(ApplicableWizardPage, wxWizardPageSimple); - -ApplicableWizardPage::ApplicableWizardPage( wxWizard* parent, wxWizardPage* prev, wxWizardPage* next, const wxBitmap& bitmap ) - : wxWizardPageSimple( parent, prev, next, bitmap ) -{ -} - -// This is a hack feature substitute for prioritized apply events. This callback is issued prior -// to the apply chain being run, allowing a panel to do some pre-apply prepwork. PAnels implementing -// this function should not modify the g_conf state. -bool ApplicableWizardPage::PrepForApply() -{ - return true; -} - - -// ---------------------------------------------------------------------------- -Panels::SettingsDirPickerPanel::SettingsDirPickerPanel( wxWindow* parent ) - : DirPickerPanel( parent, FolderId_Settings, _("Settings"), AddAppName(_("Select a folder for %s settings")) ) -{ - pxSetToolTip( this, pxEt( L"This is the folder where PCSX2 saves your settings." - ) ); - - SetStaticDesc( pxE( L"You may optionally specify a location for your PCSX2 settings here. If the location contains existing PCSX2 settings, you will be given the option to import or overwrite them." - ) ); -} - -namespace Panels -{ - - class FirstTimeIntroPanel : public wxPanelWithHelpers - { - public: - FirstTimeIntroPanel( wxWindow* parent ); - virtual ~FirstTimeIntroPanel() = default; - }; -} - -Panels::FirstTimeIntroPanel::FirstTimeIntroPanel( wxWindow* parent ) - : wxPanelWithHelpers( parent, wxVERTICAL ) -{ - SetMinWidth( MSW_GetDPIScale() * 600 ); - - FastFormatUnicode faqFile; - // configFile.Write(L"file:///%s/Configuration_Guide.pdf", WX_STR(PathDefs::GetDocs().ToString())); - faqFile.Write(L"file:///%s/PCSX2_FAQ.pdf", WX_STR(PathDefs::GetDocs().ToString())); - - wxStaticBoxSizer& langSel = *new wxStaticBoxSizer( wxVERTICAL, this, _("Language selector") ); - - langSel += new Panels::LanguageSelectionPanel( this ) | StdCenter(); - langSel += Heading(_("Change the language only if you need to.\nThe system default should be fine for most operating systems.")); - langSel += 8; - - *this += langSel | StdExpand(); - *this += GetCharHeight() * 2; - - *this += Heading(_("Welcome to PCSX2!")).Bold() | StdExpand(); - *this += GetCharHeight(); - - *this += Heading(AddAppName( - pxE( L"This wizard will help guide you through configuring your BIOS. It is recommended if this is your first time installing %s that you view the readme and configuration guide." - ) ) - ); - - *this += GetCharHeight() * 2; - /* - *this += new wxHyperlinkCtrl( this, wxID_ANY, - _("Configuration Guide"), configFile.c_str() - ) | pxCenter.Border( wxALL, 5 ); - */ - *this += new wxHyperlinkCtrl( this, wxID_ANY, - _("Readme / FAQ"), faqFile.c_str() - ) | pxCenter.Border( wxALL, 5 ); - -} - -// -------------------------------------------------------------------------------------- -// FirstTimeWizard (implementations) -// -------------------------------------------------------------------------------------- -FirstTimeWizard::FirstTimeWizard( wxWindow* parent ) - : wxWizard( parent, wxID_ANY, AddAppName(_("%s First Time Configuration")) ) - , m_page_intro ( *new ApplicableWizardPage( this ) ) - , m_page_bios ( *new ApplicableWizardPage( this, &m_page_intro ) ) - - , m_panel_Intro ( *new FirstTimeIntroPanel( &m_page_intro )) - , m_panel_BiosSel ( *new BiosSelectorPanel( &m_page_bios ) ) -{ - // Page 3 - Bios Panel - - m_page_intro. SetSizer( new wxBoxSizer( wxVERTICAL ) ); - m_page_bios. SetSizer( new wxBoxSizer( wxVERTICAL ) ); - - m_page_intro += m_panel_Intro | StdExpand(); - m_page_bios += m_panel_BiosSel | StdExpand(); - - // Temporary tutorial message for the BIOS, needs proof-reading!! - m_page_bios += 12; - m_page_bios += new pxStaticHeading( &m_page_bios, - pxE( L"PCSX2 requires a *legal* copy of the PS2 BIOS in order to run games.\nYou cannot use a copy obtained from a friend or the Internet.\nYou must dump the BIOS from your *own* PlayStation 2 console." - ) - ) | StdExpand(); - - // Assign page indexes as client data - m_page_intro .SetClientData( (void*)0 ); - m_page_bios .SetClientData( (void*)1 ); - - // Build the forward chain: - // (backward chain is built during initialization above) - m_page_intro .SetNext( &m_page_bios ); - - GetPageAreaSizer() += m_page_intro; - - // this doesn't descent from wxDialogWithHelpers, so we need to explicitly - // fit and center it. :( - - Bind(wxEVT_WIZARD_PAGE_CHANGED, &FirstTimeWizard::OnPageChanged, this); - Bind(wxEVT_WIZARD_PAGE_CHANGING, &FirstTimeWizard::OnPageChanging, this); - Bind(wxEVT_LISTBOX_DCLICK, &FirstTimeWizard::OnDoubleClicked, this); - - Bind(wxEVT_BUTTON, &FirstTimeWizard::OnRestartWizard, this, pxID_RestartWizard); -} - -void FirstTimeWizard::OnRestartWizard( wxCommandEvent& evt ) -{ - EndModal( pxID_RestartWizard ); - evt.Skip(); -} - -static void _OpenConsole() -{ - g_Conf->ProgLogBox.Visible = true; - wxGetApp().OpenProgramLog(); -} - -int FirstTimeWizard::ShowModal() -{ - if( IsDebugBuild ) wxGetApp().PostIdleMethod( _OpenConsole ); - return _parent::ShowModal(); -} - -void FirstTimeWizard::OnDoubleClicked( wxCommandEvent& evt ) -{ - wxWindow* forwardButton = FindWindow( wxID_FORWARD ); - if( forwardButton == NULL ) return; - - wxCommandEvent nextpg( wxEVT_BUTTON, wxID_FORWARD ); - nextpg.SetEventObject( forwardButton ); - forwardButton->GetEventHandler()->ProcessEvent( nextpg ); -} - -void FirstTimeWizard::OnPageChanging( wxWizardEvent& evt ) -{ - if( evt.GetPage() == NULL ) return; // safety valve! - - sptr page = (sptr)evt.GetPage()->GetClientData(); - - if( evt.GetDirection() ) - { - // Moving forward: - // Apply settings from the current page... - - if( page >= 0 ) - { - if( ApplicableWizardPage* page = wxDynamicCast( GetCurrentPage(), ApplicableWizardPage ) ) - { - if( !page->PrepForApply() || !page->GetApplyState().ApplyAll() ) - { - evt.Veto(); - return; - } - } - } - - if( page == 0 ) - { - if( wxFile::Exists(GetUiSettingsFilename()) || wxFile::Exists(GetVmSettingsFilename()) ) - { - // Asks the user if they want to import or overwrite the existing settings. - - Dialogs::ImportSettingsDialog modal( this ); - if( modal.ShowModal() != wxID_OK ) - { - evt.Veto(); - return; - } - } - } - } -} - -void FirstTimeWizard::OnPageChanged( wxWizardEvent& evt ) -{ - if( ((sptr)evt.GetPage() == (sptr)&m_page_bios) ) - m_panel_BiosSel.OnShown(); -} diff --git a/pcsx2/gui/Dialogs/GSDumpDialog.cpp b/pcsx2/gui/Dialogs/GSDumpDialog.cpp deleted file mode 100644 index f490a58028..0000000000 --- a/pcsx2/gui/Dialogs/GSDumpDialog.cpp +++ /dev/null @@ -1,983 +0,0 @@ -/* PCSX2 - PS2 Emulator for PCs - * Copyright (C) 2002-2021 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 . - */ - -#include "PrecompiledHeader.h" -#include "gui/App.h" -#include "gui/AppCommon.h" -#include "gui/MSWstuff.h" - -#include "gui/Dialogs/ModalPopups.h" - - -#include "gui/EmbeddedImage.h" -#include "common/FileSystem.h" -#include "common/StringUtil.h" -#include "gui/Resources/NoIcon.h" -#include "HostDisplay.h" - -#include "gui/PathDefs.h" -#include "gui/AppConfig.h" -#include "gui/GSFrame.h" -#include "Counters.h" -#include "PerformanceMetrics.h" -#include "GameDatabase.h" - -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include - -using namespace GSDumpTypes; - -static std::optional GetPreviewImageFromDump(const std::string& filename) -{ - std::vector pixels; - u32 width, height; - if (!GSDumpFile::GetPreviewImageFromDump(filename.c_str(), &width, &height, &pixels)) - return std::nullopt; - - // strip alpha bytes because wx is dumb and stores on a separate plane - // apparently this isn't aligned? stupidity... - const u32 pitch = width * 3; - u8* wxpixels = static_cast(std::malloc(pitch * height)); - for (u32 y = 0; y < height; y++) - { - const u8* in = reinterpret_cast(pixels.data() + y * width); - u8* out = wxpixels + y * pitch; - for (u32 x = 0; x < width; x++) - { - *(out++) = in[0]; - *(out++) = in[1]; - *(out++) = in[2]; - in += sizeof(u32); - } - } - - return wxImage(wxSize(width, height), wxpixels); -} - -namespace GSDump -{ - bool isRunning = false; -} - -wxDEFINE_EVENT(EVT_CLOSE_DUMP, wxCommandEvent); - -using namespace pxSizerFlags; - -// -------------------------------------------------------------------------------------- -// GSDumpDialog Implementation -// -------------------------------------------------------------------------------------- - -static GSRendererType s_renderer_overrides[] = { - GSRendererType::SW, -#ifdef ENABLE_OPENGL - GSRendererType::OGL, -#endif -#ifdef ENABLE_VULKAN - GSRendererType::VK, -#endif -#if defined(_WIN32) - GSRendererType::DX11, - GSRendererType::DX12, -#elif defined(__APPLE__) - GSRendererType::Metal, -#endif -}; - -Dialogs::GSDumpDialog::GSDumpDialog(wxWindow* parent) - : wxDialogWithHelpers(parent, _("GS Debugger"), pxDialogFlags().SetMinimize().SetResize()) - , m_dump_list(new wxListView(this, ID_DUMP_LIST, wxDefaultPosition, wxSize(400, 300), wxLC_NO_HEADER | wxLC_REPORT | wxLC_SINGLE_SEL)) - , m_preview_image(new wxStaticBitmap(this, wxID_ANY, wxBitmap(EmbeddedImage().Get()), wxDefaultPosition, wxSize(400,250))) - , m_debug_mode(new wxCheckBox(this, ID_DEBUG_MODE, _("Debug Mode"))) - , m_renderer_overrides(new wxRadioBox()) - , m_framerate_selector(new wxSpinCtrl(this, ID_FRAMERATE, wxEmptyString, wxDefaultPosition, wxDefaultSize, wxSP_ARROW_KEYS, 0, 999, 0)) - , m_gif_list(new wxTreeCtrl(this, ID_SEL_PACKET, wxDefaultPosition, wxSize(400, 300), wxTR_HIDE_ROOT | wxTR_HAS_BUTTONS | wxTR_LINES_AT_ROOT)) - , m_gif_packet(new wxTreeCtrl(this, wxID_ANY, wxDefaultPosition, wxSize(400, 300), wxTR_HIDE_ROOT | wxTR_HAS_BUTTONS | wxTR_LINES_AT_ROOT)) - , m_start(new wxButton(this, ID_RUN_START, _("Go to Start"), wxDefaultPosition, wxSize(150,50))) - , m_step(new wxButton(this, ID_RUN_STEP, _("Step"), wxDefaultPosition, wxSize(150, 50))) - , m_selection(new wxButton(this, ID_RUN_CURSOR, _("Run to Selection"), wxDefaultPosition, wxSize(150, 50))) - , m_vsync(new wxButton(this, ID_RUN_VSYNC, _("Go to next VSync"), wxDefaultPosition, wxSize(150, 50))) - , m_settings(new wxButton(this, ID_SETTINGS, _("Open GS Settings"), wxDefaultPosition, wxSize(150, 50))) - , m_run(new wxButton(this, ID_RUN_DUMP, _("Run"), wxDefaultPosition, wxSize(150, 50))) - , m_thread(std::make_unique(this)) -{ - wxBoxSizer* dump_info = new wxBoxSizer(wxVERTICAL); - wxBoxSizer* dump_preview = new wxBoxSizer(wxVERTICAL); - wxFlexGridSizer* grid = new wxFlexGridSizer(3, StdPadding, StdPadding); - wxBoxSizer* dbg_tree = new wxBoxSizer(wxVERTICAL); - wxBoxSizer* dbg_actions = new wxBoxSizer(wxVERTICAL); - wxBoxSizer* gif = new wxBoxSizer(wxVERTICAL); - wxBoxSizer* dumps_list = new wxBoxSizer(wxVERTICAL); - wxBoxSizer* framerate_sel = new wxBoxSizer(wxHORIZONTAL); - framerate_sel->Add(new wxStaticText(this, wxID_ANY, _("Framerate:")), wxSizerFlags().Centre()); - framerate_sel->Add(m_framerate_selector, wxSizerFlags(1).Expand()); - - m_run->SetDefault(); - wxArrayString rdoverrides; - rdoverrides.Add("None"); - for (GSRendererType over : s_renderer_overrides) - rdoverrides.Add(Pcsx2Config::GSOptions::GetRendererName(over)); - m_renderer_overrides->Create(this, wxID_ANY, "Renderer overrides", wxDefaultPosition, wxDefaultSize, rdoverrides, 1); - - dbg_tree->Add(new wxStaticText(this, wxID_ANY, _("GIF Packets"))); - dbg_tree->Add(m_gif_list, StdExpand().Proportion(1)); - dbg_actions->Add(m_debug_mode); - dbg_actions->Add(m_start, StdExpand()); - dbg_actions->Add(m_step, StdExpand()); - dbg_actions->Add(m_selection, StdExpand()); - dbg_actions->Add(m_vsync, StdExpand()); - gif->Add(new wxStaticText(this, wxID_ANY, _("Packet Content"))); - gif->Add(m_gif_packet, StdExpand().Proportion(1)); - - dumps_list->Add(new wxStaticText(this, wxID_ANY, _("GS Dumps List")), StdExpand()); - dumps_list->Add(m_dump_list, StdExpand()); - dump_info->Add(m_renderer_overrides, StdExpand()); - dump_info->Add(framerate_sel, StdExpand()); - dump_info->Add(m_settings, StdExpand()); - dump_info->Add(m_run, StdExpand()); - dump_preview->Add(new wxStaticText(this, wxID_ANY, _("Preview")), StdExpand()); - dump_preview->Add(m_preview_image, StdCenter()); - - grid->Add(dumps_list, StdExpand()); - grid->Add(dump_info, StdExpand()); - grid->Add(dump_preview, StdExpand()); - - grid->Add(dbg_tree, StdExpand()); - grid->Add(dbg_actions, StdExpand()); - grid->Add(gif, StdExpand()); - - grid->AddGrowableCol(0); - grid->AddGrowableCol(2); - grid->AddGrowableRow(1); - - SetSizerAndFit(grid); - - // populate UI and setup state - m_debug_mode->Disable(); - m_start->Disable(); - m_step->Disable(); - m_selection->Disable(); - m_vsync->Disable(); - GetDumpsList(); - - m_fs_watcher.SetOwner(this); - m_fs_watcher.Add(wxFileName(g_Conf->Folders.Snapshots.ToAscii())); - wxEvtHandler::Connect(wxEVT_FSWATCHER, wxFileSystemWatcherEventHandler(Dialogs::GSDumpDialog::PathChanged)); - - Bind(wxEVT_LIST_ITEM_SELECTED, &Dialogs::GSDumpDialog::SelectedDump, this, ID_DUMP_LIST); - Bind(wxEVT_LIST_ITEM_ACTIVATED, &Dialogs::GSDumpDialog::RunDump, this, ID_DUMP_LIST); - Bind(wxEVT_BUTTON, &Dialogs::GSDumpDialog::RunDump, this, ID_RUN_DUMP); - Bind(wxEVT_BUTTON, &Dialogs::GSDumpDialog::ToStart, this, ID_RUN_START); - Bind(wxEVT_BUTTON, &Dialogs::GSDumpDialog::StepPacket, this, ID_RUN_STEP); - Bind(wxEVT_BUTTON, &Dialogs::GSDumpDialog::ToCursor, this, ID_RUN_CURSOR); - Bind(wxEVT_BUTTON, &Dialogs::GSDumpDialog::ToVSync, this, ID_RUN_VSYNC); - Bind(wxEVT_BUTTON, &Dialogs::GSDumpDialog::OpenSettings, this, ID_SETTINGS); - Bind(wxEVT_TREE_SEL_CHANGED, &Dialogs::GSDumpDialog::ParsePacket, this, ID_SEL_PACKET); - Bind(wxEVT_CHECKBOX, &Dialogs::GSDumpDialog::CheckDebug, this, ID_DEBUG_MODE); - Bind(wxEVT_SPINCTRL, &Dialogs::GSDumpDialog::UpdateFramerate, this, ID_FRAMERATE); - Bind(EVT_CLOSE_DUMP, &Dialogs::GSDumpDialog::CloseDump, this); - - UpdateFramerate(m_framerate_selector->GetValue()); -} - -void Dialogs::GSDumpDialog::GetDumpsList() -{ - m_dump_list->ClearAll(); - wxDir snaps(g_Conf->Folders.Snapshots.ToAscii()); - wxString filename; - bool cont = snaps.GetFirst(&filename); - int h = 0, j = 0; - m_dump_list->AppendColumn("Dumps"); - // set the column size to be exactly of the size of our list - m_dump_list->GetSize(&h, &j); - m_dump_list->SetColumnWidth(0, h); - std::vector dumps; - - while (cont) - { - if (filename.EndsWith(".gs")) - dumps.push_back(filename.substr(0, filename.length() - 3)); - else if (filename.EndsWith(".gs.xz")) - dumps.push_back(filename.substr(0, filename.length() - 6)); - else if (filename.EndsWith(".gs.zst")) - dumps.push_back(filename.substr(0, filename.length() - 7)); - cont = snaps.GetNext(&filename); - } - std::sort(dumps.begin(), dumps.end(), [](const wxString& a, const wxString& b) { return a.CmpNoCase(b) < 0; }); - dumps.erase(std::unique(dumps.begin(), dumps.end()), dumps.end()); // In case there was both .gs and .gs.xz - for (size_t i = 0; i < dumps.size(); i++) - m_dump_list->InsertItem(i, dumps[i]); -} - -void Dialogs::GSDumpDialog::UpdateFramerate(int val) -{ - if (val) - m_thread->m_frame_ticks = (GetTickFrequency() + (val/2)) / val; - else - m_thread->m_frame_ticks = 0; -} - -void Dialogs::GSDumpDialog::UpdateFramerate(wxCommandEvent& evt) -{ - UpdateFramerate(evt.GetInt()); -} - -void Dialogs::GSDumpDialog::SelectedDump(wxListEvent& evt) -{ - wxString filename_preview = g_Conf->Folders.Snapshots.ToAscii() + ("/" + evt.GetText()) + ".png"; - wxString filename = g_Conf->Folders.Snapshots.ToAscii() + ("/" + evt.GetText()) + ".gs"; - if (!wxFileExists(filename)) - filename.append(".xz"); - if (!wxFileExists(filename)) - filename = filename.RemoveLast(3).append(".zst"); - if (wxFileExists(filename_preview)) - { - auto img = wxImage(filename_preview); - img.Rescale(400,250, wxIMAGE_QUALITY_HIGH); - m_preview_image->SetBitmap(wxBitmap(img)); - } - else if (std::optional img = GetPreviewImageFromDump(StringUtil::wxStringToUTF8String(filename)); img.has_value()) - { - // try embedded dump - img->Rescale(400, 250, wxIMAGE_QUALITY_HIGH); - m_preview_image->SetBitmap(img.value()); - } - else - { - m_preview_image->SetBitmap(EmbeddedImage().Get()); - } - - m_selected_dump = wxString(filename); -} - -void Dialogs::GSDumpDialog::PathChanged(wxFileSystemWatcherEvent& event) -{ - int type = event.GetChangeType(); - - if (type == wxFSW_EVENT_CREATE || type == wxFSW_EVENT_DELETE || type == wxFSW_EVENT_RENAME) - GetDumpsList(); -} - -void Dialogs::GSDumpDialog::CloseDump(wxCommandEvent& event) -{ - m_debug_mode->Disable(); - m_start->Disable(); - m_step->Disable(); - m_selection->Disable(); - m_vsync->Disable(); - m_gif_list->DeleteAllItems(); - m_gif_packet->DeleteAllItems(); - m_debug_mode->SetValue(false); - m_run->Enable(); - m_settings->Enable(); -} - -// -------------------------------------------------------------------------------------- -// GSDumpDialog GUI Buttons -// -------------------------------------------------------------------------------------- - -void Dialogs::GSDumpDialog::RunDump(wxCommandEvent& event) -{ - if (!m_run->IsEnabled()) - return; - - m_thread->m_dump_file = GSDumpFile::OpenGSDump(m_selected_dump.ToUTF8()); - if (!m_thread->m_dump_file) - { - wxString s; - s.Printf(_("Failed to load the dump %s !"), m_selected_dump); - wxMessageBox(s, _("GS Debugger"), wxICON_ERROR); - return; - } - - m_run->Disable(); - m_settings->Disable(); - m_debug_mode->Enable(); - m_thread->m_renderer = m_renderer_overrides->GetSelection(); - m_thread->Start(); - return; -} - -void Dialogs::GSDumpDialog::CheckDebug(wxCommandEvent& event) -{ - if (m_debug_mode->GetValue()) - { - GenPacketList(); - m_start->Enable(); - m_step->Enable(); - m_selection->Enable(); - m_vsync->Enable(); - } - else - { - m_gif_list->DeleteAllItems(); - m_gif_packet->DeleteAllItems(); - m_gif_list->Refresh(); - m_start->Disable(); - m_step->Disable(); - m_selection->Disable(); - m_vsync->Disable(); - } - m_thread->m_debug = m_debug_mode->GetValue(); -} - -void Dialogs::GSDumpDialog::StepPacket(wxCommandEvent& event) -{ - if (m_thread->m_debug_index < m_gif_items.size() - 1) - { - m_gif_list->SelectItem(m_gif_items[m_thread->m_debug_index + 1]); - m_button_events.push_back(GSEvent{Step, 0}); - } -} - -void Dialogs::GSDumpDialog::ToCursor(wxCommandEvent& event) -{ - m_button_events.push_back(GSEvent{RunCursor, wxAtoi(m_gif_list->GetItemText(m_gif_list->GetFocusedItem()).BeforeFirst('-'))}); -} - -void Dialogs::GSDumpDialog::ToVSync(wxCommandEvent& event) -{ - if (m_thread->m_debug_index < m_gif_items.size() - 1) - { - wxTreeItemId pkt = m_gif_items[m_thread->m_debug_index]; - if (!m_gif_list->ItemHasChildren(pkt)) - pkt = m_gif_list->GetItemParent(pkt); - if (m_gif_list->GetNextSibling(pkt).IsOk()) - m_gif_list->SelectItem(m_gif_list->GetNextSibling(pkt)); - m_button_events.push_back(GSEvent{RunVSync, 0}); - } -} - -void Dialogs::GSDumpDialog::OpenSettings(wxCommandEvent& event) -{ - GSconfigure(); - - // config has to be reloaded here, otherwise it won't apply when we restart - g_Conf->EmuOptions.GS.ReloadIniSettings(); -} - -void Dialogs::GSDumpDialog::ToStart(wxCommandEvent& event) -{ - m_gif_list->SelectItem(m_gif_items[0]); - m_button_events.push_back(GSEvent{RunCursor, 0}); -} - -// -------------------------------------------------------------------------------------- -// GSDumpDialog Packet Parsing -// -------------------------------------------------------------------------------------- - -u32 Dialogs::GSDumpDialog::ReadPacketSize(const void* packet) -{ - u128 tag; - memcpy(&tag, packet, sizeof(tag)); - u32 nloop = GetBits(tag, 0, 15); - GIFFlag flg = GetBits(tag, 58, 2); - u32 nreg = GetBits(tag, 60, 4); - if (nreg == 0) - nreg = 16; - switch (flg) - { - case GIFFlag::PACKED: - return 16 + nloop * nreg * 16; - case GIFFlag::REGLIST: - return 16 + (nloop * nreg * 8 + 8) & ~15; - default: - return 16 + nloop * 16; - } -} - -void Dialogs::GSDumpDialog::GenPacketList() -{ - int i = 0; - m_gif_list->DeleteAllItems(); - m_gif_items.clear(); - wxTreeItemId mainrootId = m_gif_list->AddRoot("root"); - wxTreeItemId rootId = m_gif_list->AppendItem(mainrootId, "0 - VSync"); - for (auto& element : m_thread->m_dump_file->GetPackets()) - { - wxString s, t; - element.id == GSType::Transfer ? t.Printf(" - %s", GSDumpTypes::GetName(element.path)) : t.Printf(""); - s.Printf("%d - %s%s - %d byte", i, GSDumpTypes::GetName(element.id), t, element.length); - if (element.id == GSType::VSync) - { - m_gif_list->SetItemText(rootId, s); - rootId = m_gif_list->AppendItem(mainrootId, "VSync"); - m_gif_items.push_back(rootId); - } - else - { - wxTreeItemId tmp = m_gif_list->AppendItem(rootId, s); - m_gif_items.push_back(tmp); - } - - i++; - } - m_gif_list->Delete(rootId); - m_gif_list->SelectItem(m_gif_items[0]); -} - -void Dialogs::GSDumpDialog::GenPacketInfo(const GSDumpFile::GSData& dump) -{ - m_gif_packet->DeleteAllItems(); - wxTreeItemId rootId = m_gif_packet->AddRoot("root"); - switch (dump.id) - { - case GSType::Transfer: - { - const u8* data = dump.data; - u32 remaining = dump.length; - int idx = 0; - while (remaining >= 16) - { - wxTreeItemId trootId = m_gif_packet->AppendItem(rootId, wxString::Format("Transfer Path %s Packet %u", GSDumpTypes::GetName(dump.path), idx)); - ParseTransfer(trootId, data); - m_gif_packet->Expand(trootId); - u32 size = ReadPacketSize(data); - remaining -= size; - data += size; - idx++; - } - break; - } - case GSType::VSync: - { - wxString s; - s.Printf("Field = %u", dump.data[0]); - m_gif_packet->AppendItem(rootId, s); - break; - } - case GSType::ReadFIFO2: - { - wxString s; - s.Printf("ReadFIFO2: Size = %d byte", dump.length); - m_gif_packet->AppendItem(rootId, s); - break; - } - case GSType::Registers: - m_gif_packet->AppendItem(rootId, "Registers"); - break; - } -} - -void Dialogs::GSDumpDialog::ParseTransfer(wxTreeItemId& trootId, const u8* data) -{ - u64 tag = *(u64*)data; - u64 regs = *(u64*)(data + 8); - u32 nloop = GetBits(tag, 0, 15); - u8 eop = GetBits(tag, 15, 1); - u8 pre = GetBits(tag, 46, 1); - u32 prim = GetBits(tag, 47, 11); - GIFFlag flg = GetBits(tag, 58, 2); - u32 nreg = GetBits(tag, 60, 4); - if (nreg == 0) - nreg = 16; - - std::array infos; - m_stored_q = 1.0; - - m_gif_packet->AppendItem(trootId, wxString::Format("nloop = %u", nloop)); - m_gif_packet->AppendItem(trootId, wxString::Format("eop = %u", eop)); - m_gif_packet->AppendItem(trootId, wxString::Format("flg = %s", GSDumpTypes::GetName(flg))); - m_gif_packet->AppendItem(trootId, wxString::Format("pre = %u", pre)); - if (pre) - { - wxTreeItemId id = m_gif_packet->AppendItem(trootId, L"prim"); - ParseTreePrim(id, prim); - } - m_gif_packet->AppendItem(trootId, wxString::Format("nreg = %u", nreg)); - wxTreeItemId regId = m_gif_packet->AppendItem(trootId, L"reg"); - - int p = 16; - switch (flg) - { - case GIFFlag::PACKED: - { - for (u32 j = 0; j < nloop; j++) - { - for (u32 i = 0; i < nreg; i++) - { - u128 reg_data; - memcpy(®_data, data + p, 16); - ParseTreeReg(regId, GetBits(regs, i * 4, 4), reg_data, true); - p += 16; - } - } - break; - } - case GIFFlag::REGLIST: - { - for (u32 j = 0; j < nloop; j++) - { - for (u32 i = 0; i < nreg; i++) - { - u128 reg_data; - memcpy(®_data.lo, data + p, 8); - reg_data.hi = 0; - ParseTreeReg(regId, GetBits(regs, i * 4, 4), reg_data, false); - p += 8; - } - } - break; - } - case GIFFlag::IMAGE: - case GIFFlag::IMAGE2: - m_gif_packet->AppendItem(regId, wxString::Format("IMAGE %d bytes", nloop * 16)); - break; - } - m_gif_packet->Expand(regId); -} - -void Dialogs::GSDumpDialog::ParsePacket(wxTreeEvent& event) -{ - GenPacketInfo(m_thread->m_dump_file->GetPackets()[wxAtoi(m_gif_list->GetItemText(event.GetItem()).BeforeFirst('-'))]); -} - -void Dialogs::GSDumpDialog::ParseTreeReg(wxTreeItemId& id, GIFReg reg, u128 data, bool packed) -{ - wxTreeItemId rootId = m_gif_packet->AppendItem(id, wxString(GSDumpTypes::GetName(reg))); - switch (reg) - { - case GIFReg::PRIM: - ParseTreePrim(rootId, data.lo); - break; - case GIFReg::RGBAQ: - { - u32 r, g, b, a; - float q; - - if (packed) - { - r = GetBits(data, 0, 8); - g = GetBits(data, 32, 8); - b = GetBits(data, 64, 8); - a = GetBits(data, 96, 8); - q = m_stored_q; - } - else - { - r = GetBits(data, 0, 8); - g = GetBits(data, 8, 8); - b = GetBits(data, 16, 8); - a = GetBits(data, 24, 8); - q = BitCast(data._u32[1]); - } - - m_gif_packet->SetItemText(rootId, wxString::Format("%s - #%02x%02x%02x%02x, %g", GSDumpTypes::GetName(reg), r, g, b, a, q)); - m_gif_packet->AppendItem(rootId, wxString::Format("R = %u", r)); - m_gif_packet->AppendItem(rootId, wxString::Format("G = %u", g)); - m_gif_packet->AppendItem(rootId, wxString::Format("B = %u", b)); - m_gif_packet->AppendItem(rootId, wxString::Format("A = %u", a)); - m_gif_packet->AppendItem(rootId, wxString::Format("Q = %g", q)); - break; - } - case GIFReg::ST: - { - float s = BitCast(data._u32[0]); - float t = BitCast(data._u32[1]); - m_gif_packet->AppendItem(rootId, wxString::Format("S = %g", s)); - m_gif_packet->AppendItem(rootId, wxString::Format("T = %g", t)); - if (packed) - { - m_stored_q = BitCast(data._u32[2]); - m_gif_packet->AppendItem(rootId, wxString::Format("Q = %g", m_stored_q)); - m_gif_packet->SetItemText(rootId, wxString::Format("%s - (%g, %g, %g)", GSDumpTypes::GetName(reg), s, t, m_stored_q)); - } - else - { - m_gif_packet->SetItemText(rootId, wxString::Format("%s - (%g, %g)", GSDumpTypes::GetName(reg), s, t)); - } - break; - } - case GIFReg::UV: - { - float u = static_cast(GetBits(data, 0, 14)) / 16.f; - float v = static_cast(GetBits(data, packed ? 32 : 16, 14)) / 16.f; - m_gif_packet->AppendItem(rootId, wxString::Format("U = %g", u)); - m_gif_packet->AppendItem(rootId, wxString::Format("V = %g", v)); - m_gif_packet->SetItemText(rootId, wxString::Format("%s - (%g, %g)", GSDumpTypes::GetName(reg), u, v)); - break; - } - case GIFReg::XYZF2: - case GIFReg::XYZF3: - { - const char* name = GSDumpTypes::GetName(reg); - if (packed && (reg == GIFReg::XYZF2) && GetBits(data, 111, 1)) - name = GSDumpTypes::GetName(GIFReg::XYZF3); - - float x, y; - u32 z, f; - - if (packed) - { - x = static_cast(GetBits(data, 0, 16)) / 16.0; - y = static_cast(GetBits(data, 32, 16)) / 16.0; - z = GetBits(data, 68, 24); - f = GetBits(data, 100, 8); - } - else - { - x = static_cast(GetBits(data, 0, 16)) / 16.0; - y = static_cast(GetBits(data, 16, 16)) / 16.0; - z = GetBits(data, 32, 24); - f = GetBits(data, 56, 8); - } - - m_gif_packet->SetItemText(rootId, wxString::Format("%s - (%g, %g, %u, %u)", name, x, y, z, f)); - m_gif_packet->AppendItem(rootId, wxString::Format("X = %g", x)); - m_gif_packet->AppendItem(rootId, wxString::Format("Y = %g", y)); - m_gif_packet->AppendItem(rootId, wxString::Format("Z = %u", z)); - m_gif_packet->AppendItem(rootId, wxString::Format("F = %u", f)); - break; - } - case GIFReg::XYZ2: - case GIFReg::XYZ3: - { - const char* name = GSDumpTypes::GetName(reg); - if (packed && (reg == GIFReg::XYZ2) && GetBits(data, 111, 1)) - name = GSDumpTypes::GetName(GIFReg::XYZ3); - - float x, y; - u32 z; - std::vector xyz_infos(3); - if (packed) - { - x = static_cast(GetBits(data, 0, 16)) / 16.0; - y = static_cast(GetBits(data, 32, 16)) / 16.0; - z = data._u32[2]; - } - else - { - x = static_cast(GetBits(data, 0, 16)) / 16.0; - y = static_cast(GetBits(data, 16, 16)) / 16.0; - z = data._u32[1]; - } - - m_gif_packet->SetItemText(rootId, wxString::Format("%s - (%g, %g, %u)", name, x, y, z)); - m_gif_packet->AppendItem(rootId, wxString::Format("X = %g", x)); - m_gif_packet->AppendItem(rootId, wxString::Format("Y = %g", y)); - m_gif_packet->AppendItem(rootId, wxString::Format("Z = %u", z)); - break; - } - case GIFReg::TEX0_1: - case GIFReg::TEX0_2: - { - u32 psm = GetBits(data, 20, 6); - u32 tw = GetBits(data, 26, 4); - u32 th = GetBits(data, 30, 4); - - m_gif_packet->SetItemText(rootId, wxString::Format("%s - %ux%u %s", GSDumpTypes::GetName(reg), 1 << tw, 1 << th, GetNameTEXPSM(psm))); - m_gif_packet->AppendItem(rootId, wxString::Format("TBP0 = %u", GetBits(data, 0, 14))); - m_gif_packet->AppendItem(rootId, wxString::Format("TBW = %u", GetBits(data, 14, 6))); - m_gif_packet->AppendItem(rootId, wxString::Format("PSM = %s", GetNameTEXPSM(psm))); - m_gif_packet->AppendItem(rootId, wxString::Format("TW = %u", tw)); - m_gif_packet->AppendItem(rootId, wxString::Format("TH = %u", th)); - m_gif_packet->AppendItem(rootId, wxString::Format("TCC = %s", GetNameTEXTCC (GetBits(data, 34, 1)))); - m_gif_packet->AppendItem(rootId, wxString::Format("TFX = %s", GetNameTEXTFX (GetBits(data, 35, 2)))); - m_gif_packet->AppendItem(rootId, wxString::Format("CBP = %u", GetBits(data, 37, 14))); - m_gif_packet->AppendItem(rootId, wxString::Format("CPSM = %s", GetNameTEXCPSM(GetBits(data, 51, 4)))); - m_gif_packet->AppendItem(rootId, wxString::Format("CSM = %s", GetNameTEXCSM (GetBits(data, 55, 1)))); - m_gif_packet->AppendItem(rootId, wxString::Format("CSA = %u", GetBits(data, 56, 5))); - m_gif_packet->AppendItem(rootId, wxString::Format("CLD = %u", GetBits(data, 61, 3))); - break; - } - case GIFReg::FOG: - { - u32 f = GetBits(data, packed ? 100 : 56, 8); - m_gif_packet->AppendItem(rootId, wxString::Format("F = %u", f)); - m_gif_packet->SetItemText(rootId, wxString::Format("%s - %u", GSDumpTypes::GetName(reg), f)); - break; - } - case GIFReg::AD: - { - GIFReg nreg = GetBits(data, 64, 8); - if (nreg == GIFReg::AD) - { - wxString s; - s.Printf("NOP"); - m_gif_packet->AppendItem(id, s); - } - else - ParseTreeReg(id, nreg, data, packed); - m_gif_packet->Delete(rootId); - break; - } - default: - break; - } -} - -void Dialogs::GSDumpDialog::ParseTreePrim(wxTreeItemId& id, u32 prim) -{ - std::array prim_infos; - - prim_infos[0].Printf("Primitive Type = %s", GetNamePRIMPRIM(GetBits(prim, 0, 3))); - prim_infos[1].Printf("IIP = %s", GetNamePRIMIIP(GetBits(prim, 3, 1))); - prim_infos[2].Printf("TME = %s", GetNameBool(GetBits(prim, 4, 1))); - prim_infos[3].Printf("FGE = %s", GetNameBool(GetBits(prim, 5, 1))); - prim_infos[4].Printf("ABE = %s", GetNameBool(GetBits(prim, 6, 1))); - prim_infos[5].Printf("AA1 = %s", GetNameBool(GetBits(prim, 7, 1))); - prim_infos[6].Printf("FST = %s", GetNamePRIMFST(GetBits(prim, 8, 1))); - prim_infos[7].Printf("CTXT = %s", GetNamePRIMCTXT(GetBits(prim, 9, 1))); - prim_infos[8].Printf("FIX = %s", GetNamePRIMFIX(GetBits(prim, 10, 1))); - - for (auto& el : prim_infos) - m_gif_packet->AppendItem(id, el); - m_gif_packet->Expand(id); -} - -void Dialogs::GSDumpDialog::ProcessDumpEvent(const GSDumpFile::GSData& event, u8* regs) -{ - switch (event.id) - { - case GSType::Transfer: - { - switch (event.path) - { - case GSTransferPath::Path1Old: - { - std::unique_ptr data(new char[16384]); - int addr = 16384 - event.length; - memcpy(data.get(), event.data + addr, event.length); - GSgifTransfer1((u8*)data.get(), addr); - break; - } - case GSTransferPath::Path1New: - GSgifTransfer((u8*)event.data, event.length / 16); - break; - case GSTransferPath::Path2: - GSgifTransfer2((u8*)event.data, event.length / 16); - break; - case GSTransferPath::Path3: - GSgifTransfer3((u8*)event.data, event.length / 16); - break; - default: - break; - } - break; - } - case GSType::VSync: - { - GSvsync((*((int*)(regs + 4096)) & 0x2000) > 0 ? (u8)0 : (u8)1, false); - g_FrameCount++; - break; - } - case GSType::ReadFIFO2: - { - std::unique_ptr arr(new u8[*((int*)event.data) * 16]); - GSInitAndReadFIFO(arr.get(), *((int*)event.data)); - break; - } - case GSType::Registers: - memcpy(regs, event.data, 8192); - break; - } -} - -// -------------------------------------------------------------------------------------- -// GSThread Implementation -// -------------------------------------------------------------------------------------- - -Dialogs::GSDumpDialog::GSThread::GSThread(GSDumpDialog* dlg) - : pxThread("GSDump") - , m_root_window(dlg) -{ -} - -Dialogs::GSDumpDialog::GSThread::~GSThread() -{ - try - { - pxThread::Cancel(); - } - DESTRUCTOR_CATCHALL -} - -void Dialogs::GSDumpDialog::GSThread::OnStop() -{ - m_root_window->m_button_events.clear(); - m_dump_file = nullptr; - - wxCommandEvent event(EVT_CLOSE_DUMP); - wxPostEvent(m_root_window, event); -} - -void Dialogs::GSDumpDialog::GSThread::ExecuteTaskInThread() -{ - if (!m_dump_file->ReadFile()) - { - OnStop(); - return; - } - - GSRendererType renderer = g_Conf->EmuOptions.GS.Renderer; - if (m_renderer > 0 && static_cast(m_renderer) <= std::size(s_renderer_overrides)) - renderer = s_renderer_overrides[m_renderer - 1]; - - GSinit(); - sApp.OpenGsPanel(); - - // to gather the gs frame object we have to be a bit hacky since sApp is not syntax complete - Pcsx2App* app = (Pcsx2App*)wxApp::GetInstance(); - GSFrame* window = nullptr; - if (app) - { - PerformanceMetrics::Reset(); - window = app->GetGsFramePtr(); - g_FrameCount = 0; - } - - Pcsx2Config::GSOptions config(g_Conf->EmuOptions.GS); - config.MaskUserHacks(); - config.MaskUpscalingHacks(); - if (!m_dump_file->GetSerial().empty()) - { - if (const GameDatabaseSchema::GameEntry* entry = GameDatabase::findGame(m_dump_file->GetSerial()); entry) - { - // apply hardware fixes to config before opening (e.g. tex in rt) - entry->applyGSHardwareFixes(config); - } - } - - u8 regs[8192] = {}; - if (!m_dump_file->GetRegsData().empty()) - std::memcpy(regs, m_dump_file->GetRegsData().data(), std::min(m_dump_file->GetRegsData().size(), sizeof(regs))); - - if (!GSopen(config, renderer, regs)) - { - OnStop(); - return; - } - - GSsetGameCRC((int)m_dump_file->GetCRC(), 0); - - freezeData fd = {static_cast(m_dump_file->GetStateData().size()), - const_cast(m_dump_file->GetStateData().data())}; - if (GSfreeze(FreezeAction::Load, &fd)) - { - OnStop(); - return; - } - - GSvsync(1, false); - GSreset(false); - GSfreeze(FreezeAction::Load, &fd); - - size_t i = 0; - m_debug_index = 0; - size_t debug_idx = 0; - - GSDump::isRunning = true; - while (GSDump::isRunning) - { - if (m_debug) - { - if (m_root_window->m_button_events.size() > 0) - { - switch (m_root_window->m_button_events[0].btn) - { - case Step: - if (debug_idx >= m_dump_file->GetPackets().size()) - debug_idx = 0; - m_debug_index = debug_idx; - break; - case RunCursor: - m_debug_index = m_root_window->m_button_events[0].index; - if (debug_idx > m_debug_index) - debug_idx = 0; - break; - case RunVSync: - if (debug_idx >= m_dump_file->GetPackets().size()) - debug_idx = 1; - if ((debug_idx + 1) < m_dump_file->GetPackets().size()) - { - auto it = std::find_if(m_dump_file->GetPackets().begin() + debug_idx + 1, m_dump_file->GetPackets().end(), [](const GSDumpFile::GSData& gs) { return gs.id == GSType::Registers; }); - if (it != std::end(m_dump_file->GetPackets())) - m_debug_index = std::distance(m_dump_file->GetPackets().begin(), it); - } - break; - } - m_root_window->m_button_events.erase(m_root_window->m_button_events.begin()); - - if (debug_idx <= m_debug_index) - { - while (debug_idx <= m_debug_index) - { - m_root_window->ProcessDumpEvent(m_dump_file->GetPackets()[debug_idx++], regs); - } - if ((debug_idx + 1) < m_dump_file->GetPackets().size()) - { - auto it = std::find_if(m_dump_file->GetPackets().begin() + debug_idx + 1, m_dump_file->GetPackets().end(), [](const GSDumpFile::GSData& gs) { return gs.id == GSType::Registers; }); - if (it != std::end(m_dump_file->GetPackets())) - m_root_window->ProcessDumpEvent(*it, regs); - } - } - - // do vsync - m_root_window->ProcessDumpEvent({GSType::VSync, 0, 0, GSTransferPath::Dummy}, regs); - } - } - else if (m_dump_file->GetPackets().size()) - { - while (i < m_dump_file->GetPackets().size()) - { - const GSDumpFile::GSData& packet = m_dump_file->GetPackets()[i++]; - m_root_window->ProcessDumpEvent(packet, regs); - if (packet.id == GSType::VSync) - { - if (m_frame_ticks) - { - // Frame limiter - u64 now = GetCPUTicks(); - s64 ms = GetTickFrequency() / 1000; - s64 sleep = m_next_frame_time - now - ms; - if (sleep > ms) - Threading::Sleep(sleep / ms); - while ((now = GetCPUTicks()) < m_next_frame_time) - ShortSpin(); - m_next_frame_time = std::max(now, m_next_frame_time + m_frame_ticks); - } - break; - } - } - - if (i >= m_dump_file->GetPackets().size()) - i = 0; - } - if (window) - { - if (!window->IsShown()) - { - GSclose(); - GSshutdown(); - sApp.CloseGsPanel(); - GSDump::isRunning = false; - } - } - } - - OnStop(); - return; -} diff --git a/pcsx2/gui/Dialogs/ImportSettingsDialog.cpp b/pcsx2/gui/Dialogs/ImportSettingsDialog.cpp deleted file mode 100644 index cc9219c3f7..0000000000 --- a/pcsx2/gui/Dialogs/ImportSettingsDialog.cpp +++ /dev/null @@ -1,70 +0,0 @@ -/* PCSX2 - PS2 Emulator for PCs - * Copyright (C) 2002-2010 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 . - */ - -#include "PrecompiledHeader.h" -#include "System.h" -#include "gui/MSWstuff.h" - -#include "ModalPopups.h" - -using namespace pxSizerFlags; - -Dialogs::ImportSettingsDialog::ImportSettingsDialog( wxWindow* parent ) - : wxDialogWithHelpers( parent, _("Import Existing Settings?") ) -{ - SetMinWidth( MSW_GetDPIScale() * 440 ); - - pxStaticText& heading( Text( pxsFmt( - - /// (%s is the app name, normally PCSX2 -- omitting one or both %s is allowed) - pxE( L"Existing %s settings have been found in the configured settings folder. Would you like to import these settings or overwrite them with %s default values?\n\n(or press Cancel to select a different settings folder)" - ), WX_STR(pxGetAppName()), WX_STR(pxGetAppName()) - ))); - - wxBoxSizer& s_buttons = *new wxBoxSizer( wxHORIZONTAL ); - wxButton* b_import = new wxButton( this, wxID_ANY, _("Import") ); - wxButton* b_over = new wxButton( this, wxID_ANY, _("Overwrite") ); - - // -------------------------------------------------------------------- - // Layout Some Shizat... - - s_buttons += b_import | StdButton(); - s_buttons += 16; - s_buttons += b_over | StdButton(); - s_buttons += 16; - s_buttons += new wxButton( this, wxID_CANCEL ) | StdButton(); - - *this += 4; - *this += heading | StdExpand(); - *this += 12; - *this += &s_buttons | StdCenter(); - - Bind(wxEVT_BUTTON, &ImportSettingsDialog::OnImport_Click, this, b_import->GetId()); - Bind(wxEVT_BUTTON, &ImportSettingsDialog::OnOverwrite_Click, this, b_over->GetId()); -} - -void Dialogs::ImportSettingsDialog::OnImport_Click( wxCommandEvent& /* evt */ ) -{ - AppConfig_OnChangedSettingsFolder( false ); // ... and import existing settings - g_Conf->Folders.Bios.Mkdir(); - EndModal( wxID_OK ); -} - -void Dialogs::ImportSettingsDialog::OnOverwrite_Click( wxCommandEvent& /* evt */ ) -{ - AppConfig_OnChangedSettingsFolder( true ); // ... and overwrite any existing settings - g_Conf->Folders.Bios.Mkdir(); - EndModal( wxID_OK ); -} diff --git a/pcsx2/gui/Dialogs/LogOptionsDialog.cpp b/pcsx2/gui/Dialogs/LogOptionsDialog.cpp deleted file mode 100644 index d882aebb21..0000000000 --- a/pcsx2/gui/Dialogs/LogOptionsDialog.cpp +++ /dev/null @@ -1,33 +0,0 @@ -/* PCSX2 - PS2 Emulator for PCs - * Copyright (C) 2002-2010 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 . - */ - -#include "PrecompiledHeader.h" -#include "LogOptionsDialog.h" -#include "gui/Panels/LogOptionsPanels.h" - -#include - -using namespace Panels; -using namespace pxSizerFlags; - - -Dialogs::LogOptionsDialog::LogOptionsDialog( wxWindow* parent ) - : BaseConfigurationDialog( parent, _("Trace Logging"), 720 ) -{ - *this += new LogOptionsPanel( this ) | StdExpand(); - AddOkCancel(); - - SetSizerAndFit(GetSizer()); -} diff --git a/pcsx2/gui/Dialogs/LogOptionsDialog.h b/pcsx2/gui/Dialogs/LogOptionsDialog.h deleted file mode 100644 index 31171c3ee2..0000000000 --- a/pcsx2/gui/Dialogs/LogOptionsDialog.h +++ /dev/null @@ -1,47 +0,0 @@ -/* PCSX2 - PS2 Emulator for PCs - * Copyright (C) 2002-2010 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 . - */ - -#pragma once - -#include "gui/App.h" -#include "ConfigurationDialog.h" - -#include "gui/wxGuiTools.h" -#include "gui/CheckedStaticBox.h" - -namespace Dialogs { - -class LogOptionsDialog : public BaseConfigurationDialog -{ -public: - LogOptionsDialog( wxWindow* parent=NULL ); - virtual ~LogOptionsDialog() = default; - - static wxString GetNameStatic() { return L"TraceLogSettings"; } - wxString GetDialogName() const { return GetNameStatic(); } - -protected: - void OnOk_Click( wxCommandEvent& evt ); - void OnApply_Click( wxCommandEvent& evt ); - - wxString& GetConfSettingsTabName() const - { - static wxString bleh = L"TraceLogs"; - pxFailDev("No tabs yet!"); - return bleh; - } -}; - -} // end namespace Dialogs diff --git a/pcsx2/gui/Dialogs/McdConfigDialog.cpp b/pcsx2/gui/Dialogs/McdConfigDialog.cpp deleted file mode 100644 index 31a7e29dc7..0000000000 --- a/pcsx2/gui/Dialogs/McdConfigDialog.cpp +++ /dev/null @@ -1,154 +0,0 @@ -/* PCSX2 - PS2 Emulator for PCs - * Copyright (C) 2002-2010 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 . - */ - -#include "PrecompiledHeader.h" - -#include "ConfigurationDialog.h" -#include "BaseConfigurationDialog.inl" -#include "ModalPopups.h" -#include "gui/MSWstuff.h" - -#include "gui/Panels/ConfigurationPanels.h" -#include "gui/Panels/MemoryCardPanels.h" - -using namespace pxSizerFlags; - -wxString GetMsg_McdNtfsCompress() -{ - return pxE( L"NTFS compression is built-in, fast, and completely reliable; and typically compresses memory cards very well (this option is highly recommended)." - ); -} - -Panels::McdConfigPanel_Toggles::McdConfigPanel_Toggles(wxWindow *parent) - : _parent( parent ) -{ - m_check_Ejection = new pxCheckBox( this, - _("Auto-eject memory cards when loading savestates"), - pxE( L"Avoids broken memory card saves. May not work with some games such as Guitar Hero." - ) - ); - - m_folderAutoIndex = new pxCheckBox( this, - _( "Automatically manage saves based on running game" ), - pxE( L"(Folder type only / Card size: Auto) Loads only the relevant booted game saves, ignoring others. Avoids running out of space for saves." - ) - ); - - //m_check_SavestateBackup = new pxCheckBox( this, pxsFmt(_("Backup existing Savestate when creating a new one")) ); -/* - for( uint i=0; i<2; ++i ) - { - m_check_Multitap[i] = new pxCheckBox( this, pxsFmt(_("Use Multitap on Port %u"), i+1) ); - m_check_Multitap[i]->SetClientData( (void*)i ); - m_check_Multitap[i]->SetName(pxsFmt( L"CheckBox::Multitap%u", i )); - } - - // ------------------------------ - // Sizers and Layout Section - // ------------------------------ - - for( uint i=0; i<2; ++i ) - *this += m_check_Multitap[i]; - - // *this += 4; - - // *this += m_check_SavestateBackup; -*/ - *this += 4; - *this += new wxStaticLine( this ) | StdExpand(); - - *this += m_check_Ejection | StdExpand(); - *this += m_folderAutoIndex | StdExpand(); -} - -void Panels::McdConfigPanel_Toggles::Apply() -{ -// g_Conf->EmuOptions.MultitapPort0_Enabled = m_check_Multitap[0]->GetValue(); -// g_Conf->EmuOptions.MultitapPort1_Enabled = m_check_Multitap[1]->GetValue(); - - //g_Conf->EmuOptions.BackupSavestate = m_check_SavestateBackup->GetValue(); - g_Conf->EmuOptions.McdEnableEjection = m_check_Ejection->GetValue(); - g_Conf->EmuOptions.McdFolderAutoManage = m_folderAutoIndex->GetValue(); -} - -void Panels::McdConfigPanel_Toggles::AppStatusEvent_OnSettingsApplied() -{ -// m_check_Multitap[0] ->SetValue( g_Conf->EmuOptions.MultitapPort0_Enabled ); -// m_check_Multitap[1] ->SetValue( g_Conf->EmuOptions.MultitapPort1_Enabled ); - - //m_check_SavestateBackup ->SetValue( g_Conf->EmuOptions.BackupSavestate ); - m_check_Ejection ->SetValue( g_Conf->EmuOptions.McdEnableEjection ); - m_folderAutoIndex ->SetValue( g_Conf->EmuOptions.McdFolderAutoManage ); -} - - -using namespace Panels; -using namespace pxSizerFlags; - -Dialogs::McdConfigDialog::McdConfigDialog( wxWindow* parent ) - : BaseConfigurationDialog( parent, _("MemoryCard Manager"), 600 ) -{ - m_panel_mcdlist = new MemoryCardListPanel_Simple( this ); - SetIcons(wxGetApp().GetIconBundle()); - - wxFlexGridSizer* s_flex=new wxFlexGridSizer(3,1, 0, 0); - s_flex->AddGrowableCol(0); - s_flex->AddGrowableRow(1); - - //set own sizer to s_flex (3-rows-1-col table with growable width and growable middle-row-height) - // instead of the default vertical sizer which cannot expand vertically. - // (vertical sizers can expand horizontally and consume the minimum vertical height possible) - SetSizer(s_flex); - - wxBoxSizer* s_top = new wxBoxSizer(wxVERTICAL); - - wxString title=_("Drag cards to or from PS2-ports"); - title+=_("\nNote: Duplicate/Rename/Create/Delete will NOT be reverted with 'Cancel'."); - - *s_top += Heading(title) | StdExpand(); - *s_top += StdPadding; - - *this += s_top | StdExpand(); - *this+= m_panel_mcdlist | StdExpand(); - - wxBoxSizer* s_bottom = new wxBoxSizer(wxVERTICAL); - *s_bottom += StdPadding; - *s_bottom += new McdConfigPanel_Toggles( this ) | StdExpand(); - - *this+= s_bottom | StdExpand(); -/* - for( uint i=0; i<2; ++i ) - { - if( pxCheckBox* check = (pxCheckBox*)FindWindow(pxsFmt( L"CheckBox::Multitap%u", i )) ) - Bind(wxEVT_CHECKBOX, &McdConfigDialog::OnMultitapClicked, this, check->GetId()); - } -*/ - AddOkCancel(s_bottom); - - //make this dialog fit to current elements (else can be shrinked too much) - // [There seem to be a bug in wxWidgets which prevents Fit() to succeed by itself, - // So using the "bigger" method: SetSizerAndFit, with existing sizer (set earlier to s_flex) - avih] - SetSizerAndFit(GetSizer()); -} -/* -void Dialogs::McdConfigDialog::OnMultitapClicked( wxCommandEvent& evt ) -{ - evt.Skip(); - if( !m_panel_mcdlist ) return; - - if( pxCheckBox* box = (pxCheckBox*)evt.GetEventObject() ) - m_panel_mcdlist->SetMultitapEnabled( (int)box->GetClientData(), box->IsChecked() ); -} -*/ diff --git a/pcsx2/gui/Dialogs/ModalPopups.h b/pcsx2/gui/Dialogs/ModalPopups.h deleted file mode 100644 index 8d9b40f397..0000000000 --- a/pcsx2/gui/Dialogs/ModalPopups.h +++ /dev/null @@ -1,225 +0,0 @@ -/* PCSX2 - PS2 Emulator for PCs - * Copyright (C) 2002-2010 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 . - */ - -#pragma once - -#include "gui/App.h" -#include "ConfigurationDialog.h" -#include "gui/Panels/ConfigurationPanels.h" -#include "GS/GSLzma.h" - -#include -#include -#include - -class FirstTimeWizard : public wxWizard -{ - typedef wxWizard _parent; - -protected: - wxWizardPageSimple& m_page_intro; - wxWizardPageSimple& m_page_bios; - - wxPanelWithHelpers& m_panel_Intro; - Panels::BiosSelectorPanel& m_panel_BiosSel; - -public: - FirstTimeWizard(wxWindow* parent); - virtual ~FirstTimeWizard() = default; - - wxWizardPage* GetFirstPage() const { return &m_page_intro; } - - int ShowModal(); - -protected: - virtual void OnPageChanging(wxWizardEvent& evt); - virtual void OnPageChanged(wxWizardEvent& evt); - virtual void OnDoubleClicked(wxCommandEvent& evt); - - void OnRestartWizard(wxCommandEvent& evt); -}; - - -namespace Dialogs -{ - class AboutBoxDialog : public wxDialogWithHelpers - { - public: - AboutBoxDialog(wxWindow* parent = NULL); - virtual ~AboutBoxDialog() = default; - - static wxString GetNameStatic() { return L"AboutBox"; } - wxString GetDialogName() const { return GetNameStatic(); } - }; - - class GSDumpDialog : public wxDialogWithHelpers - { - public: - GSDumpDialog(wxWindow* parent = NULL); - virtual ~GSDumpDialog() = default; - - static wxString GetNameStatic() - { - return L"GS Debugger"; - } - wxString GetDialogName() const - { - return GetNameStatic(); - } - - protected: - wxListView* m_dump_list; - wxStaticBitmap* m_preview_image; - wxString m_selected_dump; - wxCheckBox* m_debug_mode; - wxRadioBox* m_renderer_overrides; - wxSpinCtrl* m_framerate_selector; - wxTreeCtrl* m_gif_list; - wxTreeCtrl* m_gif_packet; - wxButton* m_start; - wxButton* m_step; - wxButton* m_selection; - wxButton* m_vsync; - wxButton* m_settings; - wxButton* m_run; - long m_focused_dump; - wxFileSystemWatcher m_fs_watcher; - - void GetDumpsList(); - void UpdateFramerate(int val); - void UpdateFramerate(wxCommandEvent& evt); - void SelectedDump(wxListEvent& evt); - void RunDump(wxCommandEvent& event); - void ToStart(wxCommandEvent& event); - void StepPacket(wxCommandEvent& event); - void ToCursor(wxCommandEvent& event); - void ToVSync(wxCommandEvent& event); - void OpenSettings(wxCommandEvent& event); - void ParsePacket(wxTreeEvent& event); - void CheckDebug(wxCommandEvent& event); - void PathChanged(wxFileSystemWatcherEvent& event); - enum - { - ID_DUMP_LIST, - ID_RUN_DUMP, - ID_RUN_START, - ID_RUN_STEP, - ID_RUN_CURSOR, - ID_RUN_VSYNC, - ID_SEL_PACKET, - ID_DEBUG_MODE, - ID_SETTINGS, - ID_FRAMERATE, - }; - - enum ButtonState - { - Step, - RunCursor, - RunVSync - }; - - struct GSEvent - { - ButtonState btn; - int index; - }; - std::vector m_button_events; - std::vector m_gif_items; - - float m_stored_q = 1.0; - void ProcessDumpEvent(const GSDumpFile::GSData& event, u8* regs); - u32 ReadPacketSize(const void* packet); - void GenPacketList(); - void GenPacketInfo(const GSDumpFile::GSData& dump); - void ParseTransfer(wxTreeItemId& id, const u8* data); - void ParseTreeReg(wxTreeItemId& id, GSDumpTypes::GIFReg reg, u128 data, bool packed); - void ParseTreePrim(wxTreeItemId& id, u32 prim); - void CloseDump(wxCommandEvent& event); - class GSThread : public pxThread - { - protected: - // parent thread - typedef pxThread _parent; - void ExecuteTaskInThread(); - void OnStop(); - GSDumpDialog* m_root_window; - - public: - int m_renderer = 0; - u64 m_frame_ticks = 0; - u64 m_next_frame_time = 0; - bool m_debug = false; - size_t m_debug_index; - std::unique_ptr m_dump_file; - GSThread(GSDumpDialog* dlg); - virtual ~GSThread(); - }; - std::unique_ptr m_thread; - }; - - - class PickUserModeDialog : public BaseApplicableDialog - { - protected: - Panels::DocsFolderPickerPanel* m_panel_usersel; - Panels::LanguageSelectionPanel* m_panel_langsel; - - public: - PickUserModeDialog(wxWindow* parent); - virtual ~PickUserModeDialog() = default; - - protected: - void OnOk_Click(wxCommandEvent& evt); - }; - - - class ImportSettingsDialog : public wxDialogWithHelpers - { - public: - ImportSettingsDialog(wxWindow* parent); - virtual ~ImportSettingsDialog() = default; - - protected: - void OnImport_Click(wxCommandEvent& evt); - void OnOverwrite_Click(wxCommandEvent& evt); - }; - - class AssertionDialog : public wxDialogWithHelpers - { - public: - AssertionDialog(const wxString& text, const wxString& stacktrace); - virtual ~AssertionDialog() = default; - }; - - class PINEDialog : public wxDialogWithHelpers - { - public: - PINEDialog(wxWindow* parent = NULL); - virtual ~PINEDialog() = default; - - void OnConfirm(wxCommandEvent& evt); - static wxString GetNameStatic() { return L"PINESettings"; } - wxString GetDialogName() const { return GetNameStatic(); } - }; -} // namespace Dialogs - -wxWindowID pxIssueConfirmation(wxDialogWithHelpers& confirmDlg, const MsgButtons& buttons); -wxWindowID pxIssueConfirmation(wxDialogWithHelpers& confirmDlg, const MsgButtons& buttons, const wxString& disablerKey); - -namespace GSDump -{ - extern bool isRunning; -} diff --git a/pcsx2/gui/Dialogs/PINEDialog.cpp b/pcsx2/gui/Dialogs/PINEDialog.cpp deleted file mode 100644 index acd02aa7ab..0000000000 --- a/pcsx2/gui/Dialogs/PINEDialog.cpp +++ /dev/null @@ -1,58 +0,0 @@ -/* PCSX2 - PS2 Emulator for PCs - * Copyright (C) 2002-2010 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 . - */ - -#include "PrecompiledHeader.h" -#include "gui/App.h" -#include "gui/AppCommon.h" -#include "gui/MSWstuff.h" - -#include "gui/Dialogs/ModalPopups.h" - -#include "gui/SysThreads.h" - -#include "gui/PathDefs.h" -#include "gui/AppConfig.h" - -using namespace pxSizerFlags; -/* This dialog currently assumes the PINE server is started when launching a - * game, as such we can allow the IPC Settings window to change the slot in a - * volatile fashion so that it returns to the default but you can change it at - * each restart of the emulator to allow for multiple emulator sessions. - * If we change this behaviour we will need to change that accordingly. - */ - -// -------------------------------------------------------------------------------------- -// PINEDialog Implementation -// -------------------------------------------------------------------------------------- - -Dialogs::PINEDialog::PINEDialog(wxWindow* parent) - : wxDialogWithHelpers(parent, _("PINE Settings"), pxDialogFlags()) -{ - wxTextCtrl* ipc_slot = new wxTextCtrl(this, wxID_ANY, wxString::Format(wxT("%u"), PINESettings::slot), wxDefaultPosition, wxDefaultSize, wxTE_PROCESS_ENTER); - ipc_slot->Bind(wxEVT_TEXT_ENTER, &Dialogs::PINEDialog::OnConfirm, this); - - *this += new wxStaticText(this, wxID_ANY, _("PINE Slot")); - *this += ipc_slot; -} - -void Dialogs::PINEDialog::OnConfirm(wxCommandEvent& evt) -{ - wxTextCtrl* obj = static_cast(evt.GetEventObject()); - if (obj != nullptr) - { - PINESettings::slot = (unsigned int)atoi(obj->GetValue().ToUTF8().data()); - Destroy(); - } -} diff --git a/pcsx2/gui/Dialogs/PickUserModeDialog.cpp b/pcsx2/gui/Dialogs/PickUserModeDialog.cpp deleted file mode 100644 index fde3bcfed2..0000000000 --- a/pcsx2/gui/Dialogs/PickUserModeDialog.cpp +++ /dev/null @@ -1,46 +0,0 @@ -/* PCSX2 - PS2 Emulator for PCs - * Copyright (C) 2002-2010 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 . - */ - -#include "PrecompiledHeader.h" -#include "ModalPopups.h" - -#include - -using namespace Panels; - -Dialogs::PickUserModeDialog::PickUserModeDialog( wxWindow* parent ) - : BaseApplicableDialog( parent, _("PCSX2 First Time configuration") ) -{ - m_panel_usersel = new DocsFolderPickerPanel( this, false ); - m_panel_langsel = new LanguageSelectionPanel( this ); - - *this += Heading(AddAppName(_("%s is starting from a new or unknown folder and needs to be configured."))); - *this += m_panel_langsel | pxSizerFlags::StdCenter(); - *this += m_panel_usersel | wxSizerFlags().Expand().Border( wxALL, 8 ); - - AddOkCancel( *GetSizer() ); - - Bind(wxEVT_BUTTON, &PickUserModeDialog::OnOk_Click, this, wxID_OK); - // TODO : Add a command event handler for language changes, that dynamically re-update contents of this window. -} - -void Dialogs::PickUserModeDialog::OnOk_Click( wxCommandEvent& evt ) -{ - if( m_ApplyState.ApplyAll() ) - { - Close(); - evt.Skip(); - } -} diff --git a/pcsx2/gui/Dialogs/SysConfigDialog.cpp b/pcsx2/gui/Dialogs/SysConfigDialog.cpp deleted file mode 100644 index fed19c930f..0000000000 --- a/pcsx2/gui/Dialogs/SysConfigDialog.cpp +++ /dev/null @@ -1,242 +0,0 @@ -/* PCSX2 - PS2 Emulator for PCs - * Copyright (C) 2002-2021 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 . - */ - -#include "PrecompiledHeader.h" -#include "System.h" -#include "gui/App.h" - -#include "ConfigurationDialog.h" -#include "BaseConfigurationDialog.inl" -#include "ModalPopups.h" -#include "gui/Panels/ConfigurationPanels.h" -#include "gui/MainFrame.h" - -using namespace Panels; -using namespace pxSizerFlags; - -static void CheckHacksOverrides() -{ - if( !wxGetApp().Overrides.HasCustomHacks() ) return; - - // The user has commandline overrides enabled, so the options they see here and/or apply won't match - // the commandline overrides. Let them know! - - wxDialogWithHelpers dialog( wxFindWindowByName( L"Dialog:" + Dialogs::SysConfigDialog::GetNameStatic() ), _("Config Overrides Warning") ); - - dialog += dialog.Text( pxEt( L"Warning! You are running PCSX2 with command line options that override your configured settings. These command line options will not be reflected in the Settings dialog, and will be disabled if you apply any changes here." - )); - - // [TODO] : List command line option overrides in action? - - pxIssueConfirmation( dialog, MsgButtons().OK(), L"Dialog.SysConfig.Overrides" ); -} - -//Behavior when unchecking 'Presets' is to keep the GUI settings at the last preset (even if not yet applied). -// -//Alternative possible behavior when unchecking 'Presets' (currently not implemented) is to set the GUI to -// the last applied settings. If such behavior is to be implemented, g_Conf->EnablePresets should be set to -// false before it's applied to the GUI and then restored to it's original state such that the GUI reflects -// g_Conf's settings as if it doesn't force presets. (if a settings which has presets enable is applied to the -// GUI then most of the GUI is disabled). -void Dialogs::SysConfigDialog::UpdateGuiForPreset ( int presetIndex, bool presetsEnabled ) -{ - if( !m_listbook ) - return; - - //Console.WriteLn("Applying config to Gui: preset #%d, presets enabled: %s", presetIndex, presetsEnabled?"true":"false"); - - AppConfig preset = *g_Conf; - preset.IsOkApplyPreset( presetIndex, false ); //apply a preset to a copy of g_Conf. - preset.EnablePresets = presetsEnabled; //override IsOkApplyPreset (which always applies/enabled) to actual required state - - //update the config panels of SysConfigDialog to reflect the preset. - size_t pages = m_labels.GetCount(); - for( size_t i=0; iGetPage(i)) are of type wxNotebookPage which doesn't - // support IsSpecificConfig(), so the panels (pages) that SysConfigDialog holds must be of class - // BaseApplicableConfigPanel or derived, and not of the parent class wxNotebookPage. - if ( ((BaseApplicableConfigPanel*)(m_listbook->GetPage(i)))->IsSpecificConfig() ) - { - ((BaseApplicableConfigPanel_SpecificConfig*)(m_listbook->GetPage(i))) - ->ApplyConfigToGui( preset, AppConfig::APPLY_FLAG_FROM_PRESET | AppConfig::APPLY_FLAG_MANUALLY_PROPAGATE ); - } - } - - //Main menus behavior regarding presets and changes/cancel/apply from SysConfigDialog: - //1. As long as preset-related values were not changed at SysConfigDialog, menus behave normally. - //2. After the first preset-related change at SysConfigDialog (this function) and before Apply/Ok/Cancel: - // - The menus reflect the temporary pending values, but these preset-controlled items are grayed out even if temporarily presets is unchecked. - //3. When clicking Ok/Apply/Cancel at SysConfigDialog, the menus are re-alligned with g_Conf (including gray out or not as needed). - //NOTE: Enabling the presets and disabling them wihout clicking Apply leaves the pending menu config at last preset values - // (consistent with SysConfigDialog behavior). But unlike SysConfigDialog, the menu items stay grayed out. - // Clicking cancel will revert all pending changes, but clicking apply will commit them, and this includes the menus. - // E.g.: - // 1. Patches (menu) is disabled and presets (SysConfigDialog) is disabled. - // 2. Opening config and checking presets without apply --> patches are visually enabled and grayed out (not yet applied to g_Conf) - // 3. Unchecking presets, still without clicking apply --> patches are visually still enabled (last preset values) and grayed out. - // 4. Clicking Apply (presets still unchecked) --> patches will be enabled and not grayed out, presets are disabled. - // --> If clicking Cancel instead of Apply at 4., will revert everything to the state of 1 (preset disabled, patches disabled and not grayed out). - - bool origEnable=preset.EnablePresets; - preset.EnablePresets=true; // will cause preset-related items to be grayed out at the menus regardless of their value. - if ( GetMainFramePtr() ) - GetMainFramePtr()->ApplyConfigToGui( preset, AppConfig::APPLY_FLAG_FROM_PRESET | AppConfig::APPLY_FLAG_MANUALLY_PROPAGATE ); - - // Not really needed as 'preset' is local and dumped anyway. For the sake of future modifications of more GUI elements. - preset.EnablePresets=origEnable; - -} - -void Dialogs::SysConfigDialog::AddPresetsControl() -{ - m_slider_presets = new wxSlider( this, wxID_ANY, g_Conf->PresetIndex, 0, AppConfig::GetMaxPresetIndex(), - wxDefaultPosition, wxDefaultSize, wxHORIZONTAL /*| wxSL_AUTOTICKS | wxSL_LABELS */); - m_slider_presets->SetMinSize(wxSize(100,25)); - - const wchar_t* presetTooltip = pxEt(L"Presets apply some speed hacks that may boost speed on underpowered systems, or speed up games that have unusual performance requirements. Uncheck this box to apply settings manually.\n\n1) Safest - No speed hacks. Most reliable, but possibly slow setting.\n2) Safe - Default. A few speed hacks known to provide boosts, with minimal to no side effects.\n3) Balanced - May help quad core CPUs."); - - m_slider_presets->SetToolTip( - presetTooltip - ); - m_slider_presets->Enable(g_Conf->EnablePresets); - - m_check_presets = new pxCheckBox( this, _("Preset:"), 0); - m_check_presets->SetToolTip( - presetTooltip - ); - m_check_presets->SetValue(!!g_Conf->EnablePresets); - - g_Conf->PresetIndex = std::clamp(g_Conf->PresetIndex, 0, AppConfig::GetMaxPresetIndex()); - - //Console.WriteLn("--> SysConfigDialog::AddPresetsControl: EnablePresets: %s", g_Conf->EnablePresets?"true":"false"); - - wxString l; wxColor c(wxColour( L"Red" )); - AppConfig::isOkGetPresetTextAndColor(g_Conf->PresetIndex, l, c); - m_msg_preset = new pxStaticText(this, l, wxALIGN_LEFT); - m_msg_preset->Enable(g_Conf->EnablePresets); - m_msg_preset->SetForegroundColour( c ); - m_msg_preset->Bold(); - - //I'm unable to do without the next 2 rows.. what am I missing? - m_msg_preset->SetMinWidth(250); - m_msg_preset->Unwrapped(); - - - *m_extraButtonSizer += 20; - *m_extraButtonSizer += m_check_presets | pxMiddle; - *m_extraButtonSizer += m_slider_presets | pxMiddle; - *m_extraButtonSizer += 5; - *m_extraButtonSizer += m_msg_preset | pxMiddle; - - Bind(wxEVT_SCROLL_THUMBTRACK, &Dialogs::SysConfigDialog::Preset_Scroll, this, m_slider_presets->GetId()); - Bind(wxEVT_SCROLL_CHANGED, &Dialogs::SysConfigDialog::Preset_Scroll, this, m_slider_presets->GetId()); - Bind(wxEVT_CHECKBOX, &Dialogs::SysConfigDialog::Presets_Toggled, this, m_check_presets->GetId()); -} - - - -void Dialogs::SysConfigDialog::Presets_Toggled(wxCommandEvent &event) -{ - m_slider_presets->Enable( m_check_presets->IsChecked() ); - m_msg_preset->Enable( m_check_presets->IsChecked() ); - UpdateGuiForPreset( m_slider_presets->GetValue(), m_check_presets->IsChecked() ); - - event.Skip(); -} - - -void Dialogs::SysConfigDialog::Preset_Scroll(wxScrollEvent &event) -{ - wxString pl; - wxColor c; - AppConfig::isOkGetPresetTextAndColor(m_slider_presets->GetValue(), pl, c); - m_msg_preset->SetLabel(pl); - m_msg_preset->SetForegroundColour( c ); - - UpdateGuiForPreset( m_slider_presets->GetValue(), m_check_presets->IsChecked() ); - event.Skip(); -} - -//Write the values SysConfigDialog holds (preset index and enabled) to g_Conf. -//Make the main menu system write the presets values it holds to g_Conf (preset may have affected the gui without changing g_Conf) -//The panels will write themselves to g_Conf on apply (AFTER this function) and will also trigger a global OnSettingsApplied. -void Dialogs::SysConfigDialog::Apply() -{ - //Console.WriteLn("Applying preset to to g_Conf: Preset index: %d, EnablePresets: %s", (int)m_slider_presets->GetValue(), m_check_presets->IsChecked()?"true":"false"); - g_Conf->EnablePresets = m_check_presets->IsChecked(); - g_Conf->PresetIndex = m_slider_presets->GetValue(); - - if (GetMainFramePtr()) - GetMainFramePtr()->CommitPreset_noTrigger(); -} - -//Update the main menu system to reflect the original configuration on cancel. -//The config panels don't need this because they just reload themselves with g_Conf when re-opened next time. -//But the menu system has a mostly persistent state that reflects g_Conf (except for when presets are used). -void Dialogs::SysConfigDialog::Cancel() -{ - if (GetMainFramePtr()) - GetMainFramePtr()->ApplyConfigToGui( *g_Conf, AppConfig::APPLY_FLAG_FROM_PRESET | AppConfig::APPLY_FLAG_MANUALLY_PROPAGATE ); -} - -Dialogs::SysConfigDialog::SysConfigDialog(wxWindow* parent) - : BaseConfigurationDialog( parent, AddAppName(_("General Settings - %s")), 580 ) -{ - ScopedBusyCursor busy( Cursor_ReallyBusy ); - - CreateListbook( wxGetApp().GetImgList_Config() ); - const AppImageIds::ConfigIds& cfgid( wxGetApp().GetImgId().Config ); - SetIcons(wxGetApp().GetIconBundle()); - - //NOTE: all pages which are added to SysConfigDialog must be of class BaseApplicableConfigPanel or derived. - // see comment inside UpdateGuiForPreset implementation for more info. - AddPage ( pxL("BIOS"), cfgid.Cpu ); - AddPage ( pxL("Folders"), cfgid.Paths ); - AddPage ( pxL("EE/IOP"), cfgid.Cpu ); - AddPage ( pxL("VUs"), cfgid.Cpu ); - AddPage ( pxL("GS"), cfgid.Cpu ); - AddPage ( pxL("GS Window"), cfgid.Video ); - AddPage ( pxL("Speedhacks"), cfgid.Speedhacks ); - AddPage ( pxL("Game Fixes"), cfgid.Gamefixes ); - - AddListbook(); - AddOkCancel(); - AddPresetsControl(); - - SetSizerAndFit(GetSizer()); - - if( wxGetApp().Overrides.HasCustomHacks() ) - wxGetApp().PostMethod( CheckHacksOverrides ); -} - -Dialogs::InterfaceLanguageDialog::InterfaceLanguageDialog(wxWindow* parent) - : BaseConfigurationDialog(parent, _("Language Selector"), 400) -{ - *this += 5; - - // Keep this in English - same as the menu item. - *this += Heading(L"Language switch will only affect newly opened windows.\n"); - *this += Heading(L"Full change will not apply until PCSX2 is restarted."); - *this += new Panels::LanguageSelectionPanel(this, false) | StdCenter(); - - AddOkCancel(); - - SetSizerAndFit(GetSizer()); -} - -bool g_ConfigPanelChanged = false; diff --git a/pcsx2/gui/DriveList.cpp b/pcsx2/gui/DriveList.cpp deleted file mode 100644 index 993ca7b2b6..0000000000 --- a/pcsx2/gui/DriveList.cpp +++ /dev/null @@ -1,109 +0,0 @@ -/* PCSX2 - PS2 Emulator for PCs - * Copyright (C) 2002-2020 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 . - */ - -#include "PrecompiledHeader.h" -#include "DriveList.h" -#include "MainFrame.h" -#include "CDVD/CDVDdiscReader.h" - -DriveList::DriveList() -{ - Menu = new wxMenu(); - Manager = std::unique_ptr(new DriveListManager(Menu)); -} - -DriveListManager::DriveListManager(wxMenu* menu) - : m_Menu(menu) -{ - m_Menu->Append(MenuId_DriveListRefresh, _("&Refresh")); - m_Menu->AppendSeparator(); - RefreshList(); - - // Bind on app level so that the event can be accessed outside DriveListManager - wxGetApp().Bind(wxEVT_MENU, &DriveListManager::OnRefreshClicked, this, MenuId_DriveListRefresh); -} - -void DriveListManager::ClearList() -{ - for (uint i = 0; i < m_Items.size(); i++) - { - m_Menu->Unbind(wxEVT_MENU, &DriveListManager::OnChangedSelection, this, m_Items.at(i)->itemPtr->GetId()); - m_Menu->Destroy(m_Items.at(i)->itemPtr); - } - - m_Items.clear(); -} - -void DriveListManager::RefreshList() -{ - ClearList(); - auto drives = GetOpticalDriveList(); - bool itemChecked = false; - - for (auto i : drives) - { - std::unique_ptr dli = std::unique_ptr(new DriveListItem()); - dli->driveLetter = fromUTF8(i); - dli->itemPtr = m_Menu->AppendRadioItem(wxID_ANY, dli->driveLetter); - - // Check the last used drive item - if (g_Conf->Folders.RunDisc == dli->driveLetter) - { - dli->itemPtr->Check(true); - itemChecked = true; - } - - m_Menu->Bind(wxEVT_MENU, &DriveListManager::OnChangedSelection, this, dli->itemPtr->GetId()); - m_Items.push_back(std::move(dli)); - } - - // Last used drive not found so use first found drive - if (itemChecked == false && m_Items.size() > 0) - { - m_Items.at(0)->itemPtr->Check(true); - - SysUpdateDiscSrcDrive(m_Items.at(0)->driveLetter); - } -} - -void DriveListManager::OnChangedSelection(wxCommandEvent& evt) -{ - uint index = m_Items.size(); - - for (uint i = 0; i < m_Items.size(); i++) - { - if ((m_Items.at(i)->itemPtr != NULL) && (m_Items.at(i)->itemPtr->GetId() == evt.GetId())) - { - index = i; - break; - } - } - - if (index >= m_Items.size()) - { - evt.Skip(); - return; - } - - m_Items.at(index)->itemPtr->Check(true); - - ScopedCoreThreadPopup paused_core; - SwapOrReset_Disc(m_Menu->GetWindow(), paused_core, m_Items.at(index)->driveLetter); -} - -void DriveListManager::OnRefreshClicked(wxCommandEvent& evt) -{ - RefreshList(); -} diff --git a/pcsx2/gui/DriveList.h b/pcsx2/gui/DriveList.h deleted file mode 100644 index 177f2c6444..0000000000 --- a/pcsx2/gui/DriveList.h +++ /dev/null @@ -1,60 +0,0 @@ -/* PCSX2 - PS2 Emulator for PCs - * Copyright (C) 2002-2020 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 . - */ - -#pragma once - -#include "AppCoreThread.h" - -class DriveListManager : public wxEvtHandler -{ -protected: - struct DriveListItem - { - wxString driveLetter; - wxMenuItem* itemPtr; - - DriveListItem() { itemPtr = NULL; } - - DriveListItem(const wxString& src) - : driveLetter(src) - { - itemPtr = NULL; - } - }; - - std::vector> m_Items; - wxMenu* m_Menu; - -public: - DriveListManager(wxMenu* menu); - virtual ~DriveListManager() = default; - - void RefreshList(); - -protected: - void ClearList(); - void OnChangedSelection(wxCommandEvent& evt); - void OnRefreshClicked(wxCommandEvent& evt); -}; - -struct DriveList -{ - wxMenu* Menu; - std::unique_ptr Manager; - - DriveList(); -}; - -extern wxWindowID SwapOrReset_Disc(wxWindow* owner, IScopedCoreThread& core, const wxString driveLetter); diff --git a/pcsx2/gui/EmbeddedImage.h b/pcsx2/gui/EmbeddedImage.h deleted file mode 100644 index fc390b2162..0000000000 --- a/pcsx2/gui/EmbeddedImage.h +++ /dev/null @@ -1,121 +0,0 @@ -/* PCSX2 - PS2 Emulator for PCs - * Copyright (C) 2002-2010 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 . - */ - -#pragma once - -#include -#include -#include - -////////////////////////////////////////////////////////////////////////////////////////// -// Interface for loadable embedded images. See EmbeddedImage description for details. -// -class IEmbeddedImage -{ -public: - virtual const wxImage& Get() = 0; - virtual wxImage Scale(int width, int height) = 0; -}; - -////////////////////////////////////////////////////////////////////////////////////////// -// EmbeddedImage - Helper class for loading images which have been embedded into the binary -// manifest of the executable file. Images are only loaded when Get() is called, -// which allows this type to be passed to functions as a fallback or default action without -// incurring an unnecessary memory footprint. -// -// Note: Get() only loads the image once. All subsequent calls to Get will use the -// previously loaded image data. -// -template -class EmbeddedImage : public IEmbeddedImage -{ -protected: - wxImage m_Image; - const wxSize m_ResampleTo; - -protected: - // ------------------------------------------------------------------------ - // Internal function used to ensure the image is loaded before returning the image - // handle (called from all methods that return an image handle). - // - void _loadImage() - { - if (!m_Image.Ok()) - { - wxMemoryInputStream joe(ImageType::Data, ImageType::Length); - m_Image.LoadFile(joe, ImageType::GetFormat()); - - if (m_ResampleTo.IsFullySpecified() && (m_ResampleTo.GetWidth() != m_Image.GetWidth() || m_ResampleTo.GetHeight() != m_Image.GetHeight())) - m_Image.Rescale(m_ResampleTo.GetWidth(), m_ResampleTo.GetHeight(), wxIMAGE_QUALITY_HIGH); - } - } - -public: - EmbeddedImage() - : m_Image() - , m_ResampleTo(wxDefaultSize) - { - } - - // ------------------------------------------------------------------------ - // Constructor for creating an embedded image that gets resampled to the specified size when - // loaded. - // - // Implementation Note: This function uses wxWidgets ResamplBox method to resize the image. - // wxWidgets ResampleBicubic appears to be buggy and produces fairly poor results when down- - // sampling images (basically resembles a pixel resize). ResampleBox produces much cleaner - // results. - // - EmbeddedImage(int newWidth, int newHeight) - : m_Image() - , m_ResampleTo(newWidth, newHeight) - { - } - - // ------------------------------------------------------------------------ - // Loads and retrieves the embedded image. The embedded image is only loaded from its em- - // bedded format once. Any subsequent calls to Get(), Rescale(), or Resample() will use - // the pre-loaded copy. Translation: the png/jpeg decoding overhead happens only once, - // and only happens when the image is actually fetched. Simply creating an instance - // of an EmbeddedImage object uses no excess memory nor cpu overhead. :) - // - const wxImage& Get() - { - _loadImage(); - return m_Image; - } - - wxIcon GetIcon() - { - wxIcon retval; - retval.CopyFromBitmap(Get()); - return retval; - } - - // ------------------------------------------------------------------------ - // Performs a pixel resize of the loaded image and returns a new image handle (EmbeddedImage - // is left unmodified). - // - - wxImage Scale(int width, int height) - { - _loadImage(); - // Not strictly necessary - wxWidgets does the dimensions check anyway. - if (width != m_Image.GetWidth() || height != m_Image.GetHeight()) - return m_Image.Scale(width, height, wxIMAGE_QUALITY_HIGH); - else - return m_Image; - } -}; diff --git a/pcsx2/gui/ExecutorThread.cpp b/pcsx2/gui/ExecutorThread.cpp deleted file mode 100644 index e0de364c57..0000000000 --- a/pcsx2/gui/ExecutorThread.cpp +++ /dev/null @@ -1,514 +0,0 @@ -/* PCSX2 - PS2 Emulator for PCs - * Copyright (C) 2002-2010 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 . - */ - -#include "PrecompiledHeader.h" -#include "App.h" -#include - -using namespace pxSizerFlags; - -// -------------------------------------------------------------------------------------- -// ConsoleLogSource_Event (implementations) -// -------------------------------------------------------------------------------------- - -bool ConsoleLogSource_Event::Write( const pxEvtQueue* evtHandler, const SysExecEvent* evt, const wxChar* msg ) { - return _parent::Write("%s", (pxsFmt(L"(%s:%s) ", WX_STR(evtHandler->GetEventHandlerName()), WX_STR(evt->GetEventName())) + msg).ToUTF8().data() ); -} -bool ConsoleLogSource_Event::Warn( const pxEvtQueue* evtHandler, const SysExecEvent* evt, const wxChar* msg ) { - return _parent::Write("%s", (pxsFmt(L"(%s:%s) ", WX_STR(evtHandler->GetEventHandlerName()), WX_STR(evt->GetEventName())) + msg).ToUTF8().data() ); -} -bool ConsoleLogSource_Event::Error( const pxEvtQueue* evtHandler, const SysExecEvent* evt, const wxChar* msg ) { - return _parent::Write("%s", (pxsFmt(L"(%s:%s) ", WX_STR(evtHandler->GetEventHandlerName()), WX_STR(evt->GetEventName())) + msg).ToUTF8().data() ); -} - -ConsoleLogSource_Event::ConsoleLogSource_Event() -{ - static const TraceLogDescriptor myDesc = - { - "SysEvents", "S&ysVM Control Events", - "Logs events as they are passed to the PS2 virtual machine.", - }; - - m_Descriptor = &myDesc; -} - -ConsoleLogSource_Event pxConLog_Event; - -// -------------------------------------------------------------------------------------- -// SysExecEvent (implementations) -// -------------------------------------------------------------------------------------- -wxString SysExecEvent::GetEventName() const -{ - pxFail( "Warning: Unnamed SysExecutor Event! Please overload GetEventName() in all SysExecEvent derived classes." ); - return wxEmptyString; -} - -wxString SysExecEvent::GetEventMessage() const -{ - return GetEventName(); -} - -// This is called from _DoInvokeEvent after various affinity and state checks have verified the -// message as executable. Override this when possible. Only override _DoInvokeEvent if you -// need some kind of additional low-level ability. -void SysExecEvent::InvokeEvent() -{ -} - -// This is called by _DoInvokeEvent *always* -- even when exceptions occur during InvokeEvent(), -// making this function a bit like a C# 'finally' block (try/catch/finally -- a nice feature lacking -// from C++ prior to the new C++0x standard). -// -// This function calls PostResult by default, and should be invoked by derived classes overriding -// CleanupEvent(), unless you want to change the PostResult behavior. -void SysExecEvent::CleanupEvent() -{ - PostResult(); -} - -// Transports the specified exception to the thread/context that invoked this event. -// Events are run from a try/catch block in the event handler that automatically transports -// exceptions as neeeded, so there shouldn't be much need to use this method directly. -void SysExecEvent::SetException( BaseException* ex ) -{ - if( !ex ) return; - - ex->DiagMsg() += '('; - ex->DiagMsg() += StringUtil::wxStringToUTF8String(GetEventName()); - ex->DiagMsg() += ')'; - //ex->UserMsg() = prefix + ex->UserMsg(); - - if( m_sync ) - { - // Throws the exception inline with the message handler (this makes the exception - // look like it was thrown quite naturally). - m_sync->SetException( ex ); - } - else - { - // transport the exception to the main thread, since the message is fully - // asynchronous, or has already entered an asynchronous state. Message is sent - // as a non-blocking action since proper handling of user errors on async messages - // is *usually* to log/ignore it (hah), or to suspend emulation and issue a dialog - // box to the user. - - wxGetApp().PostEvent( pxExceptionEvent( ex ) ); - } -} - -void SysExecEvent::SetException( const BaseException& ex ) -{ - SetException( ex.Clone() ); -} - - -// This method calls _DoInvoke after performing some setup, exception handling, and -// affinity checks. For implementing behavior of your event, override _DoInvoke -// instead, which is the intended method of implementing derived class invocation. -void SysExecEvent::_DoInvokeEvent() -{ - AffinityAssert_AllowFrom_SysExecutor(); - - try { - InvokeEvent(); - } - catch( BaseException& ex ) - { - SetException( ex ); - } - catch( std::runtime_error& ex ) - { - SetException( new Exception::RuntimeError(ex) ); - } - - // Cleanup Execution -- performed regardless of exception or not above. - try { - CleanupEvent(); - } - catch( BaseException& ex ) - { - SetException( ex ); - } - catch( std::runtime_error& ex ) - { - SetException( new Exception::RuntimeError(ex) ); - } -} - -// Posts an empty result to the invoking context/thread of this message, if one exists. -// If the invoking thread posted the event in non-blocking fashion then no action is -// taken. -void SysExecEvent::PostResult() const -{ - if( m_sync ) m_sync->PostResult(); -} - -// -------------------------------------------------------------------------------------- -// pxEvtQueue Implementations -// -------------------------------------------------------------------------------------- -pxEvtQueue::pxEvtQueue() : - m_OwnerThreadId(), m_Quitting(false), m_qpc_Start(0) -{ -} - -// Puts the event queue into Shutdown mode, which does *not* immediately stop nor cancel -// the queue's processing. Instead it marks the queue inaccessible to all new events -// and continues procesing queued events for critical events that should not be ignored. -// (typically these are shutdown events critical to closing the app cleanly). Once -// all such events have been processed, the thread is stopped. -// -void pxEvtQueue::ShutdownQueue() -{ - if( m_Quitting ) return; - m_Quitting = true; - m_wakeup.Post(); -} - -struct ScopedThreadCancelDisable -{ - ScopedThreadCancelDisable() - { - int oldstate; - pthread_setcancelstate( PTHREAD_CANCEL_DISABLE, &oldstate ); - } - - ~ScopedThreadCancelDisable() - { - int oldstate; - pthread_setcancelstate( PTHREAD_CANCEL_ENABLE, &oldstate ); - } -}; - -// isIdle - parameter is useful for logging only (currently) -void pxEvtQueue::ProcessEvents( pxEvtList& list, bool isIdle ) -{ - ScopedLock synclock( m_mtx_pending ); - - pxEvtList::iterator node; - while( node = list.begin(), node != list.end() ) - { - std::unique_ptr deleteMe(*node); - - list.erase( node ); - if( !m_Quitting || deleteMe->IsCriticalEvent() ) - { - // Some messages can be blocking, so we should release the mutex lock while - // processing, to avoid having cases where the main thread deadlocks simply - // trying to add a message to the queue due to the basic mutex acquire needed. - - m_qpc_Start = GetCPUTicks(); - - synclock.Release(); - - pxEvtLog.Write( this, deleteMe.get(), wxsFormat(L"Executing... [%s]%s", - deleteMe->AllowCancelOnExit() ? L"Cancelable" : L"Noncancelable", isIdle ? L"(Idle)" : wxEmptyString).wc_str() - ); - - if( deleteMe->AllowCancelOnExit() ) - deleteMe->_DoInvokeEvent(); - else - { - ScopedThreadCancelDisable thr_cancel_scope; - deleteMe->_DoInvokeEvent(); - } - - u64 qpc_end = GetCPUTicks(); - pxEvtLog.Write( this, deleteMe.get(), wxsFormat(L"Event completed in %ums", - (u32)(((qpc_end-m_qpc_Start)*1000) / GetTickFrequency())).wc_str() - ); - - synclock.Acquire(); - m_qpc_Start = 0; // lets the main thread know the message completed. - } - else - { - pxEvtLog.Write( this, deleteMe.get(), L"Skipping Event: %s" ); - deleteMe->PostResult(); - } - } -} - -void pxEvtQueue::ProcessIdleEvents() -{ - ProcessEvents( m_idleEvents, true ); -} - -void pxEvtQueue::ProcessPendingEvents() -{ - ProcessEvents( m_pendingEvents ); -} - -// This method is provided for wxWidgets API conformance. I like to use PostEvent instead -// since it's reminiscent of PostMessage in Windows (and behaves rather similarly). -void pxEvtQueue::AddPendingEvent( SysExecEvent& evt ) -{ - PostEvent( evt ); -} - -// Adds an event to the event queue in non-blocking fashion. The thread executing this -// event queue will be woken up if it's idle/sleeping. -// IMPORTANT: The pointer version of this function will *DELETE* the event object passed -// to it automatically when the event has been executed. If you are using a scoped event -// you should use the Reference/Handle overload instead! -// -void pxEvtQueue::PostEvent( SysExecEvent* evt ) -{ - std::unique_ptr sevt(evt); - if( !sevt ) return; - - if( m_Quitting ) - { - sevt->PostResult(); - return; - } - - ScopedLock synclock( m_mtx_pending ); - - pxEvtLog.Write( this, evt, pxsFmt(L"Posting event! (pending=%d, idle=%d)", m_pendingEvents.size(), m_idleEvents.size()) ); - - m_pendingEvents.push_back( sevt.release() ); - if( m_pendingEvents.size() == 1) - m_wakeup.Post(); -} - -void pxEvtQueue::PostEvent( const SysExecEvent& evt ) -{ - PostEvent( evt.Clone() ); -} - -void pxEvtQueue::PostIdleEvent( SysExecEvent* evt ) -{ - if( !evt ) return; - - if( m_Quitting ) - { - evt->PostResult(); - return; - } - - ScopedLock synclock( m_mtx_pending ); - - pxEvtLog.Write( this, evt, pxsFmt(L"Posting event! (pending=%d, idle=%d) [idle]", m_pendingEvents.size(), m_idleEvents.size()) ); - - if( m_pendingEvents.empty() ) - { - m_pendingEvents.push_back( evt ); - m_wakeup.Post(); - } - else - m_idleEvents.push_back( evt ); -} - -void pxEvtQueue::PostIdleEvent( const SysExecEvent& evt ) -{ - PostIdleEvent( evt.Clone() ); -} - -void pxEvtQueue::ProcessEvent( SysExecEvent& evt ) -{ - if( wxThread::GetCurrentId() != m_OwnerThreadId ) - { - SynchronousActionState sync; - evt.SetSyncState( sync ); - PostEvent( evt ); - sync.WaitForResult(); - } - else - evt._DoInvokeEvent(); -} - -void pxEvtQueue::ProcessEvent( SysExecEvent* evt ) -{ - if( !evt ) return; - - if( wxThread::GetCurrentId() != m_OwnerThreadId ) - { - SynchronousActionState sync; - evt->SetSyncState( sync ); - PostEvent( evt ); - sync.WaitForResult(); - } - else - { - std::unique_ptr deleteMe(evt); - deleteMe->_DoInvokeEvent(); - } -} - -bool pxEvtQueue::Rpc_TryInvokeAsync( FnType_Void* method, const wxChar* traceName ) -{ - if( wxThread::GetCurrentId() != m_OwnerThreadId ) - { - PostEvent( new SysExecEvent_MethodVoid(method, traceName) ); - return true; - } - - return false; -} - -bool pxEvtQueue::Rpc_TryInvoke( FnType_Void* method, const wxChar* traceName ) -{ - if( wxThread::GetCurrentId() != m_OwnerThreadId ) - { - SynchronousActionState sync; - SysExecEvent_MethodVoid evt(method); - evt.Critical(); - evt.SetSyncState( sync ); - PostEvent( evt ); - sync.WaitForResult(); - - return true; - } - - return false; -} - -// This method invokes the derived class Idle implementations (if any) and then enters -// the sleep state until such time that new messages are received. -// -// Extending: Derived classes should override _DoIdle instead, unless it is necessary -// to implement post-wakeup behavior. -// -void pxEvtQueue::Idle() -{ - ProcessIdleEvents(); - _DoIdle(); - m_wakeup.WaitWithoutYield(); -} - -void pxEvtQueue::SetActiveThread() -{ - m_OwnerThreadId = wxThread::GetCurrentId(); -} - -// -------------------------------------------------------------------------------------- -// ExecutorThread Implementations -// -------------------------------------------------------------------------------------- -ExecutorThread::ExecutorThread(pxEvtQueue* evthandler) - : m_EvtHandler(evthandler) -{ -} - -bool ExecutorThread::IsRunning() const -{ - if( !m_EvtHandler ) return false; - return( !m_EvtHandler->IsShuttingDown() ); -} - -// Exposes the internal pxEvtQueue::ShutdownQueue API. See pxEvtQueue for details. -void ExecutorThread::ShutdownQueue() -{ - if( !m_EvtHandler ) return; - - if( !m_EvtHandler->IsShuttingDown() ) - m_EvtHandler->ShutdownQueue(); - - Block(); -} - -// Exposes the internal pxEvtQueue::PostEvent API. See pxEvtQueue for details. -void ExecutorThread::PostEvent( SysExecEvent* evt ) -{ - if( !pxAssert( m_EvtHandler ) ) { delete evt; return; } - m_EvtHandler->PostEvent( evt ); -} - -void ExecutorThread::PostEvent( const SysExecEvent& evt ) -{ - if( !pxAssert( m_EvtHandler ) ) return; - m_EvtHandler->PostEvent( evt ); -} - -// Exposes the internal pxEvtQueue::PostIdleEvent API. See pxEvtQueue for details. -void ExecutorThread::PostIdleEvent( SysExecEvent* evt ) -{ - if( !pxAssert( m_EvtHandler ) ) return; - m_EvtHandler->PostIdleEvent( evt ); -} - -void ExecutorThread::PostIdleEvent( const SysExecEvent& evt ) -{ - if( !pxAssert( m_EvtHandler ) ) return; - m_EvtHandler->PostIdleEvent( evt ); -} - -// Exposes the internal pxEvtQueue::ProcessEvent API. See pxEvtQueue for details. -void ExecutorThread::ProcessEvent( SysExecEvent* evt ) -{ - if( m_EvtHandler ) - m_EvtHandler->ProcessEvent( evt ); - else - { - std::unique_ptr deleteMe(evt); - deleteMe->_DoInvokeEvent(); - } -} - -void ExecutorThread::ProcessEvent( SysExecEvent& evt ) -{ - if( m_EvtHandler ) - m_EvtHandler->ProcessEvent( evt ); - else - evt._DoInvokeEvent(); -} - -void ExecutorThread::OnStart() -{ - //if( !m_ExecutorTimer ) - // m_ExecutorTimer = new wxTimer( wxTheApp, pxEvt_ThreadTaskTimeout_SysExec ); - - m_name = L"SysExecutor"; - _parent::OnStart(); -} - -void ExecutorThread::ExecuteTaskInThread() -{ - if( !pxAssertDev( m_EvtHandler, "Gimme a damn Event Handler first, object whore." ) ) return; - - m_EvtHandler->SetActiveThread(); - - while( true ) - { - if( !pxAssertDev( m_EvtHandler, "Event handler has been deallocated during SysExecutor thread execution." ) ) return; - - m_EvtHandler->Idle(); - m_EvtHandler->ProcessPendingEvents(); - if( m_EvtHandler->IsShuttingDown() ) break; - } -} - -void ExecutorThread::OnCleanupInThread() -{ - //wxGetApp().PostCommand( m_task, pxEvt_ThreadTaskComplete ); - _parent::OnCleanupInThread(); -} - -// -------------------------------------------------------------------------------------- -// Threaded Event Handlers (Pcsx2App) -// -------------------------------------------------------------------------------------- - -// This event is called when the SysExecutorThread's timer triggers, which means the -// VM/system task has taken an oddly long period of time to complete. The task is able -// to invoke a modal dialog from here that will grant the user some options for handling -// the unresponsive task. -void Pcsx2App::OnSysExecutorTaskTimeout( wxTimerEvent& evt ) -{ - if( !SysExecutorThread.IsRunning() ) return; - - //BaseThreadedInvocation* task = SysExecutorThread.GetTask(); - //if( !task ) return; - - //task->ShowModalStatus(); -} diff --git a/pcsx2/gui/FastFormatString.cpp b/pcsx2/gui/FastFormatString.cpp deleted file mode 100644 index 41ac313950..0000000000 --- a/pcsx2/gui/FastFormatString.cpp +++ /dev/null @@ -1,286 +0,0 @@ -/* PCSX2 - PS2 Emulator for PCs - * Copyright (C) 2002-2010 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 . - */ - -#include "common/Pcsx2Defs.h" -#include "common/SafeArray.inl" -#include "gui/StringHelpers.h" -#include -#include - -// Implement some very commonly used SafeArray types here -// (done here for lack of a better place) - -template class SafeArray; -template class SafeArray; -template class SafeArray; - -template class SafeAlignedArray; -template class SafeAlignedArray; -template class SafeAlignedArray; - -// Sanity check: truncate strings if they exceed 512k in length. Anything like that -// is either a bug or really horrible code that needs to be stopped before it causes -// system deadlock. -static const int MaxFormattedStringLength = 0x80000; - -#ifndef __linux__ -static __ri void format_that_ascii_mess(CharBufferType& buffer, uint writepos, const char* fmt, va_list argptr) -#else -static void format_that_ascii_mess(CharBufferType& buffer, uint writepos, const char* fmt, va_list argptr) -#endif -{ - va_list args; - while (true) - { - int size = buffer.GetLength(); - - va_copy(args, argptr); - int len = vsnprintf(buffer.GetPtr(writepos), size - writepos, fmt, args); - va_end(args); - - // some implementations of vsnprintf() don't NUL terminate - // the string if there is not enough space for it so - // always do it manually - buffer[size - 1] = '\0'; - - if (size >= MaxFormattedStringLength) - break; - - // vsnprintf() may return either -1 (traditional Unix behavior) or the - // total number of characters which would have been written if the - // buffer were large enough (newer standards such as Unix98) - - if (len < 0) - len = size + (size / 4); - - len += writepos; - if (len < size) - break; - buffer.Resize(len + 128); - }; - - // performing an assertion or log of a truncated string is unsafe, so let's not; even - // though it'd be kinda nice if we did. -} - -// returns the length of the formatted string, in characters (wxChars). -#ifndef __linux__ -static __ri uint format_that_unicode_mess(CharBufferType& buffer, uint writepos, const wxChar* fmt, va_list argptr) -#else -static uint format_that_unicode_mess(CharBufferType& buffer, uint writepos, const wxChar* fmt, va_list argptr) -#endif -{ - va_list args; - while (true) - { - int size = buffer.GetLength() / sizeof(wxChar); - - va_copy(args, argptr); - int len = wxVsnprintf((wxChar*)buffer.GetPtr(writepos * sizeof(wxChar)), size - writepos, fmt, args); - va_end(args); - - // some implementations of vsnprintf() don't NUL terminate - // the string if there is not enough space for it so - // always do it manually - ((wxChar*)buffer.GetPtr())[size - 1] = L'\0'; - - if (size >= MaxFormattedStringLength) - return size - 1; - - // vsnprintf() may return either -1 (traditional Unix behavior) or the - // total number of characters which would have been written if the - // buffer were large enough (newer standards such as Unix98) - - if (len < 0) - len = size + (size / 4); - - len += writepos; - if (len < size) - return len; - buffer.Resize((len + 128) * sizeof(wxChar)); - }; - - // performing an assertion or log of a truncated string is unsafe, so let's not; even - // though it'd be kinda nice if we did. - - pxAssume(false); - return 0; // unreachable. -} - -// -------------------------------------------------------------------------------------- -// FastFormatUnicode (implementations) -// -------------------------------------------------------------------------------------- -// [TODO] This class should actually be renamed to FastFormatNative or FastFormatString, and -// adopted to properly support 1-byte wxChar types (mostly requiring some changes to the -// WriteV functions). The current implementation is fine for wx2.8, which always defaults -// to wide-varieties of wxChar -- but wx3.0 will use UTF8 for linux distros, which will break -// this class nicely in its current state. --air - -FastFormatUnicode::FastFormatUnicode() - : m_dest(2048) -{ - Clear(); -} - -void FastFormatUnicode::Clear() -{ - m_Length = 0; - ((wxChar*)m_dest.GetPtr())[0] = 0; -} - -FastFormatUnicode& FastFormatUnicode::WriteV(const char* fmt, va_list argptr) -{ - wxString converted(fromUTF8(FastFormatAscii().WriteV(fmt, argptr))); - - const uint inspos = m_Length; - const uint convLen = converted.Length(); - m_dest.MakeRoomFor((inspos + convLen + 64) * sizeof(wxChar)); - memcpy(&((wxChar*)m_dest.GetPtr())[inspos], converted.wc_str(), (convLen + 1) * sizeof(wxChar)); - m_Length += convLen; - - return *this; -} - -FastFormatUnicode& FastFormatUnicode::WriteV(const wxChar* fmt, va_list argptr) -{ - m_Length = format_that_unicode_mess(m_dest, m_Length, fmt, argptr); - return *this; -} - -FastFormatUnicode& FastFormatUnicode::Write(const char* fmt, ...) -{ - va_list list; - va_start(list, fmt); - WriteV(fmt, list); - va_end(list); - return *this; -} - -FastFormatUnicode& FastFormatUnicode::Write(const wxChar* fmt, ...) -{ - va_list list; - va_start(list, fmt); - WriteV(fmt, list); - va_end(list); - return *this; -} - -FastFormatUnicode& FastFormatUnicode::Write(const wxString fmt, ...) -{ - va_list list; - va_start(list, fmt); - WriteV(fmt.wx_str(), list); - va_end(list); - return *this; -} - -bool FastFormatUnicode::IsEmpty() const -{ - return ((wxChar&)m_dest[0]) == 0; -} - -FastFormatUnicode& FastFormatUnicode::ToUpper() -{ - wxChar* ch = (wxChar*)m_dest.GetPtr(); - for (uint i = 0; i < m_Length; ++i, ++ch) - *ch = (wxChar)wxToupper(*ch); - - return *this; -} - -FastFormatUnicode& FastFormatUnicode::ToLower() -{ - wxChar* ch = (wxChar*)m_dest.GetPtr(); - for (uint i = 0; i < m_Length; ++i, ++ch) - *ch = (wxChar)wxTolower(*ch); - - return *this; -} - -FastFormatUnicode& FastFormatUnicode::operator+=(const char* psz) -{ - Write(L"%s", WX_STR(fromUTF8(psz))); - return *this; -} - -wxString& operator+=(wxString& str1, const FastFormatUnicode& str2) -{ - str1.Append(str2.c_str(), str2.Length()); - return str1; -} - -wxString operator+(const wxString& str1, const FastFormatUnicode& str2) -{ - wxString s = str1; - s += str2; - return s; -} - -wxString operator+(const wxChar* str1, const FastFormatUnicode& str2) -{ - wxString s = str1; - s += str2; - return s; -} - -wxString operator+(const FastFormatUnicode& str1, const wxString& str2) -{ - wxString s = str1; - s += str2; - return s; -} - -wxString operator+(const FastFormatUnicode& str1, const wxChar* str2) -{ - wxString s = str1; - s += str2; - return s; -} - - -// -------------------------------------------------------------------------------------- -// FastFormatAscii (implementations) -// -------------------------------------------------------------------------------------- -FastFormatAscii::FastFormatAscii() - : m_dest(2048) -{ - Clear(); -} - -void FastFormatAscii::Clear() -{ - m_dest.GetPtr()[0] = 0; -} - -FastFormatAscii& FastFormatAscii::WriteV(const char* fmt, va_list argptr) -{ - format_that_ascii_mess(m_dest, strlen(m_dest.GetPtr()), fmt, argptr); - return *this; -} - -FastFormatAscii& FastFormatAscii::Write(const char* fmt, ...) -{ - va_list list; - va_start(list, fmt); - WriteV(fmt, list); - va_end(list); - return *this; -} - - -bool FastFormatAscii::IsEmpty() const -{ - return m_dest[0] == 0; -} diff --git a/pcsx2/gui/FileUtils.cpp b/pcsx2/gui/FileUtils.cpp deleted file mode 100644 index 7b873f5c8c..0000000000 --- a/pcsx2/gui/FileUtils.cpp +++ /dev/null @@ -1,88 +0,0 @@ -/* PCSX2 - PS2 Emulator for PCs - * Copyright (C) 2002-2010 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 . - */ - -#include "PrecompiledHeader.h" -#include "common/Path.h" -#include "wxDirName.h" - -#include -#include - -bool CopyDirectory( const wxString& from, const wxString& to ) { - wxDir src( from ); - if ( !src.IsOpened() ) { - return false; - } - - wxMkdir( to ); - wxDir dst( to ); - if ( !dst.IsOpened() ) { - return false; - } - - wxString filename; - - // copy directories - if ( src.GetFirst( &filename, wxEmptyString, wxDIR_DIRS | wxDIR_HIDDEN ) ) { - do { - if ( !CopyDirectory( wxFileName( from, filename ).GetFullPath(), wxFileName( to, filename ).GetFullPath() ) ) { - return false; - } - } while ( src.GetNext( &filename ) ); - } - - // copy files - if ( src.GetFirst( &filename, wxEmptyString, wxDIR_FILES | wxDIR_HIDDEN ) ) { - do { - if ( !wxCopyFile( wxFileName( from, filename ).GetFullPath(), wxFileName( to, filename ).GetFullPath() ) ) { - return false; - } - } while ( src.GetNext( &filename ) ); - } - - return true; -} - -bool RemoveWxDirectory( const wxString& dirname ) { - { - wxDir dir( dirname ); - if ( !dir.IsOpened() ) { - return false; - } - - wxString filename; - - // delete subdirs recursively - if ( dir.GetFirst( &filename, wxEmptyString, wxDIR_DIRS | wxDIR_HIDDEN ) ) { - do { - if ( !RemoveWxDirectory( wxFileName( dirname, filename ).GetFullPath() ) ) { - return false; - } - } while ( dir.GetNext( &filename ) ); - } - - // delete files - if ( dir.GetFirst( &filename, wxEmptyString, wxDIR_FILES | wxDIR_HIDDEN ) ) { - do { - if ( !wxRemoveFile( wxFileName( dirname, filename ).GetFullPath() ) ) { - return false; - } - } while ( dir.GetNext( &filename ) ); - } - } - - // oddly enough this has different results compared to the more sensible dirname.Rmdir(), don't change! - return wxFileName::Rmdir( dirname ); -} diff --git a/pcsx2/gui/FrameForGS.cpp b/pcsx2/gui/FrameForGS.cpp deleted file mode 100644 index 22d7b0c26a..0000000000 --- a/pcsx2/gui/FrameForGS.cpp +++ /dev/null @@ -1,892 +0,0 @@ -/* PCSX2 - PS2 Emulator for PCs - * Copyright (C) 2002-2010 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 . - */ - -#include "PrecompiledHeader.h" -#include "App.h" -#include "GSFrame.h" -#include "AppAccelerators.h" -#include "AppHost.h" -#include "AppSaveStates.h" -#include "Counters.h" -#include "GS.h" -#include "GS/GS.h" -#include "MainFrame.h" -#include "MSWstuff.h" -#include "PAD/Gamepad.h" -#include "PerformanceMetrics.h" -#include "common/StringUtil.h" - -#include "gui/Dialogs/ModalPopups.h" - -#include "ConsoleLogger.h" - -#include "Recording/InputRecording.h" -#include "Recording/Utilities/InputRecordingLogger.h" - -#include -#include -#include -#include -#include - -#ifdef __WXGTK__ -#include -#include - -// Junk X11 macros... -#ifdef DisableScreenSaver -#undef DisableScreenSaver -#endif -#endif - -#ifdef GDK_WINDOWING_WAYLAND -#include -#endif - -//#define GSWindowScaleDebug - -static const KeyAcceleratorCode FULLSCREEN_TOGGLE_ACCELERATOR_GSPANEL=KeyAcceleratorCode( WXK_RETURN ).Alt(); - -void GSPanel::InitDefaultAccelerators() -{ - // Note: these override GlobalAccels ( Pcsx2App::InitDefaultGlobalAccelerators() ) - // For plain letters or symbols, replace e.g. WXK_F1 with e.g. wxKeyCode('q') or wxKeyCode('-') - // For plain letter keys with shift, use e.g. AAC( wxKeyCode('q') ).Shift() and NOT wxKeyCode('Q') - // For a symbol with shift (e.g. '_' which is '-' with shift) use AAC( wxKeyCode('-') ).Shift() - - typedef KeyAcceleratorCode AAC; - - if (!m_Accels) m_Accels = std::unique_ptr(new AcceleratorDictionary); - - m_Accels->Map( AAC( WXK_F1 ), "States_FreezeCurrentSlot" ); - m_Accels->Map( AAC( WXK_F3 ), "States_DefrostCurrentSlot"); - m_Accels->Map( AAC( WXK_F3 ).Shift(), "States_DefrostCurrentSlotBackup"); - m_Accels->Map( AAC( WXK_F2 ), "States_CycleSlotForward" ); - m_Accels->Map( AAC( WXK_F2 ).Shift(), "States_CycleSlotBackward" ); - - m_Accels->Map( AAC( WXK_F4 ), "Framelimiter_MasterToggle"); - m_Accels->Map( AAC( WXK_TAB ), "Framelimiter_TurboToggle" ); - m_Accels->Map( AAC( WXK_TAB ).Shift(), "Framelimiter_SlomoToggle" ); - - m_Accels->Map( AAC( WXK_F6 ), "GSwindow_CycleAspectRatio" ); - - m_Accels->Map( AAC( WXK_NUMPAD_ADD ).Cmd().Alt(), "GSwindow_ZoomInY" ); //CTRL on Windows/linux, CMD on OSX - m_Accels->Map( AAC( WXK_NUMPAD_SUBTRACT ).Cmd().Alt(), "GSwindow_ZoomOutY" ); - m_Accels->Map( AAC( WXK_NUMPAD_MULTIPLY ).Cmd().Alt(), "GSwindow_ZoomResetY" ); - - m_Accels->Map( AAC( WXK_ESCAPE ), "Sys_SuspendResume" ); - m_Accels->Map( AAC( WXK_F8 ), "Sys_TakeSnapshot" ); // also shift and ctrl-shift will be added automatically - m_Accels->Map( AAC( WXK_F9 ), "Sys_RenderswitchToggle"); - - m_Accels->Map( AAC( WXK_F10 ), "Sys_LoggingToggle" ); - // m_Accels->Map( AAC( WXK_F11 ), "Sys_FreezeGS" ); - m_Accels->Map( AAC( WXK_F12 ), "Sys_RecordingToggle" ); - - m_Accels->Map( FULLSCREEN_TOGGLE_ACCELERATOR_GSPANEL, "FullscreenToggle" ); -} - -wxString GSPanel::GetAssociatedKeyCode(const char* id) -{ - return m_Accels->findKeycodeWithCommandId(id).toTitleizedString(); -} - -GSPanel::GSPanel( wxWindow* parent ) - : wxWindow() - , m_HideMouseTimer( this ) - , m_coreRunning(false) -{ - m_CursorShown = true; - m_HasFocus = false; - - if ( !wxWindow::Create(parent, wxID_ANY) ) - throw Exception::RuntimeError().SetDiagMsg( "GSPanel constructor explode!!" ); - - SetName( L"GSPanel" ); - - InitDefaultAccelerators(); - - SetBackgroundColour(wxColour((unsigned long)0)); - if( g_Conf->GSWindow.AlwaysHideMouse ) - { - SetCursor( wxCursor(wxCURSOR_BLANK) ); - m_CursorShown = false; - } - - Bind(wxEVT_SIZE, &GSPanel::OnResize, this); -#if wxCHECK_VERSION(3, 1, 3) - Bind(wxEVT_DPI_CHANGED, &GSPanel::OnResize, this); -#endif - Bind(wxEVT_KEY_UP, &GSPanel::OnKeyDownOrUp, this); - Bind(wxEVT_KEY_DOWN, &GSPanel::OnKeyDownOrUp, this); - - Bind(wxEVT_SET_FOCUS, &GSPanel::OnFocus, this); - Bind(wxEVT_KILL_FOCUS, &GSPanel::OnFocusLost, this); - - Bind(wxEVT_TIMER, &GSPanel::OnHideMouseTimeout, this, m_HideMouseTimer.GetId()); - - // Any and all events which should result in the mouse cursor being made visible - // are connected here. If I missed one, feel free to add it in! --air - Bind(wxEVT_LEFT_DOWN, &GSPanel::OnMouseEvent, this); - Bind(wxEVT_LEFT_UP, &GSPanel::OnMouseEvent, this); - Bind(wxEVT_MIDDLE_DOWN, &GSPanel::OnMouseEvent, this); - Bind(wxEVT_MIDDLE_UP, &GSPanel::OnMouseEvent, this); - Bind(wxEVT_RIGHT_DOWN, &GSPanel::OnMouseEvent, this); - Bind(wxEVT_RIGHT_UP, &GSPanel::OnMouseEvent, this); - Bind(wxEVT_MOTION, &GSPanel::OnMouseEvent, this); - Bind(wxEVT_LEFT_DCLICK, &GSPanel::OnMouseEvent, this); - Bind(wxEVT_MIDDLE_DCLICK, &GSPanel::OnMouseEvent, this); - Bind(wxEVT_RIGHT_DCLICK, &GSPanel::OnMouseEvent, this); - Bind(wxEVT_MOUSEWHEEL, &GSPanel::OnMouseEvent, this); - - Bind(wxEVT_LEFT_DCLICK, &GSPanel::OnLeftDclick, this); -} - -GSPanel::~GSPanel() -{ - //CoreThread.Suspend( false ); // Just in case...! - -#ifdef WAYLAND_API - WaylandDestroySubsurface(); -#endif -} - -void GSPanel::DoShowMouse() -{ - if( g_Conf->GSWindow.AlwaysHideMouse ) return; - - if( !m_CursorShown ) - { - SetCursor( wxCursor( wxCURSOR_DEFAULT ) ); - m_CursorShown = true; - } - m_HideMouseTimer.Start( 1750, true ); -} - -#ifdef _WIN32 -static float GetDpiScaleForWxWindow(wxWindow* window) -{ - static UINT(WINAPI * get_dpi_for_window)(HWND hwnd); - if (!get_dpi_for_window) - { - HMODULE mod = GetModuleHandle(L"user32.dll"); - if (mod) - get_dpi_for_window = reinterpret_cast(GetProcAddress(mod, "GetDpiForWindow")); - } - if (!get_dpi_for_window) - return 1.0f; - - // less than 100% scaling seems unlikely. - const UINT dpi = get_dpi_for_window(window->GetHandle()); - return (dpi > 0) ? std::max(1.0f, static_cast(dpi) / 96.0f) : 1.0f; -} -#endif - -std::optional GSPanel::GetWindowInfo() -{ - WindowInfo ret; - - const wxSize gs_vp_size(GetClientSize()); - ret.surface_scale = static_cast(GetContentScaleFactor()); - ret.surface_width = static_cast(gs_vp_size.GetWidth()); - ret.surface_height = static_cast(gs_vp_size.GetHeight()); - -#if defined(_WIN32) - ret.type = WindowInfo::Type::Win32; - ret.window_handle = GetHandle(); - - // Windows DPI internally uses the higher pixel count, so work out by how much. - ret.surface_scale = GetDpiScaleForWxWindow(this); -#elif defined(__WXGTK__) - GtkWidget* child_window = GTK_WIDGET(GetHandle()); - - // create the widget to allow to use GDK_WINDOW_* macro - gtk_widget_realize(child_window); - - // Disable the widget double buffer, you will use the opengl one - gtk_widget_set_double_buffered(child_window, false); - - GdkWindow* draw_window = gtk_widget_get_window(child_window); - - // TODO: Do we even want to support GTK2? -#if GTK_MAJOR_VERSION < 3 - ret.type = WindowInfo::Type::X11; - ret.display_connection = GDK_WINDOW_XDISPLAY(draw_window); - ret.window_handle = reinterpret_cast(GDK_WINDOW_XWINDOW(draw_window)); -#else -#ifdef GDK_WINDOWING_X11 - if (GDK_IS_X11_WINDOW(draw_window)) - { - ret.type = WindowInfo::Type::X11; - ret.display_connection = GDK_WINDOW_XDISPLAY(draw_window); - ret.window_handle = reinterpret_cast(GDK_WINDOW_XID(draw_window)); - - // GTK/X11 seems to not scale coordinates? - ret.surface_width = static_cast(ret.surface_width * ret.surface_scale); - ret.surface_height = static_cast(ret.surface_height * ret.surface_scale); - } -#endif // GDK_WINDOWING_X11 -#if defined(GDK_WINDOWING_WAYLAND) && defined(WAYLAND_API) - if (GDK_IS_WAYLAND_WINDOW(draw_window)) - { - wl_display* display = gdk_wayland_display_get_wl_display(gdk_window_get_display(draw_window)); - wl_surface* parent_surface = gdk_wayland_window_get_wl_surface(draw_window); - if (!m_wl_child_surface && !WaylandCreateSubsurface(display, parent_surface)) - return std::nullopt; - - ret.type = WindowInfo::Type::Wayland; - ret.display_connection = display; - ret.window_handle = m_wl_child_surface; - } -#endif // GDK_WINDOWING_WAYLAND -#endif // GTK_MAJOR_VERSION >= 3 -#elif defined(__WXOSX__) - ret.type = WindowInfo::Type::MacOS; - ret.surface_width = static_cast(ret.surface_width * ret.surface_scale); - ret.surface_height = static_cast(ret.surface_height * ret.surface_scale); - ret.window_handle = GetHandle(); -#endif - - if (ret.type == WindowInfo::Type::Surfaceless) - { - Console.Error("Unknown window type for GSFrame."); - return std::nullopt; - } - - return ret; -} - -void GSPanel::OnResize(wxEvent& event) -{ - if( IsBeingDeleted() ) return; - event.Skip(); - - if (g_gs_window_info.type == WindowInfo::Type::Surfaceless) - return; - - const wxSize gs_vp_size(GetClientSize()); -#ifdef _WIN32 - const float scale = GetDpiScaleForWxWindow(this); -#else - const float scale = GetContentScaleFactor(); -#endif - int width = gs_vp_size.GetWidth(); - int height = gs_vp_size.GetHeight(); - - if (false -#ifdef __WXGTK__ - || g_gs_window_info.type == WindowInfo::Type::X11 -#endif -#ifdef __APPLE__ - || g_gs_window_info.type == WindowInfo::Type::MacOS -#endif - ) - { - width = static_cast(width * scale); - height = static_cast(height * scale); - } - - g_gs_window_info.surface_width = width; - g_gs_window_info.surface_height = height; - g_gs_window_info.surface_scale = scale; - - Host::GSWindowResized(width, height, scale); -} - -void GSPanel::OnMouseEvent( wxMouseEvent& evt ) -{ - if( IsBeingDeleted() ) return; - - // Do nothing for left-button event - if (!evt.Button(wxMOUSE_BTN_LEFT)) { - evt.Skip(); - DoShowMouse(); - } - -#if defined(__unix__) || defined(__APPLE__) - // HACK2: In gsopen2 there is one event buffer read by both wx/gui and pad. Wx deletes - // the event before the pad see it. So you send key event directly to the pad. - HostKeyEvent event; - // FIXME how to handle double click ??? - if (evt.ButtonDown()) - { - event.type = HostKeyEvent::Type::MousePressed; - event.key = evt.GetButton() | 0x10000; - } - else if (evt.ButtonUp()) - { - event.type = HostKeyEvent::Type::MouseReleased; - event.key = evt.GetButton() | 0x10000; - } - else if (evt.Moving() || evt.Dragging()) - { - event.type = HostKeyEvent::Type::MouseMove; - long x, y; - evt.GetPosition(&x, &y); - - wxCoord w, h; - wxWindowDC dc(this); - dc.GetSize(&w, &h); - - // Special case to allow continuous mouvement near the border - if (x < 10) - x = 0; - else if (x > (w - 10)) - x = 0xFFFF; - - if (y < 10) - y = 0; - else if (y > (w - 10)) - y = 0xFFFF; - - // For compatibility purpose with the existing structure. I decide to reduce - // the position to 16 bits. - event.key = ((y & 0xFFFF) << 16) | (x & 0xFFFF); - } - else - { - event.key = 0; - event.type = HostKeyEvent::Type::NoEvent; - } - - PADWriteEvent(event); -#endif -} - -void GSPanel::OnHideMouseTimeout( wxTimerEvent& evt ) -{ - if( IsBeingDeleted() || !m_HasFocus ) return; - if( CoreThread.GetExecutionMode() != SysThreadBase::ExecMode_Opened ) return; - - SetCursor( wxCursor( wxCURSOR_BLANK ) ); - m_CursorShown = false; -} - -void GSPanel::OnKeyDownOrUp( wxKeyEvent& evt ) -{ - - // HACK: PAD expect PCSX2 to ignore keyboard messages on the GS Window while - // it is open, so ignore here (PCSX2 will direct messages routed from PAD directly - // to the APP level message handler, which in turn routes them right back here -- yes it's - // silly, but oh well). - -#if defined(__unix__) || defined(__APPLE__) - // HACK2: In gsopen2 there is one event buffer read by both wx/gui and pad. Wx deletes - // the event before the pad see it. So you send key event directly to the pad. - HostKeyEvent event; - event.key = evt.GetRawKeyCode(); - if (evt.GetEventType() == wxEVT_KEY_UP) - event.type = HostKeyEvent::Type::KeyReleased; - else if (evt.GetEventType() == wxEVT_KEY_DOWN) - event.type = HostKeyEvent::Type::KeyPressed; - else - event.type = HostKeyEvent::Type::NoEvent; - - PADWriteEvent(event); -#endif - -#ifdef __WXMSW__ - // Not sure what happens on Linux, but on windows this method is called only when emulation - // is paused and the GS window is not hidden (and therefore the event doesn't arrive from - // pad and doesn't go through Pcsx2App::PadKeyDispatch). On such case (paused). - // It needs to handle two issues: - // 1. It's called both for key down and key up (linux apparently needs it this way) - but we - // don't want to execute the command twice (normally commands execute on key down only). - // 2. It has wx keycode which is upper case for ascii chars, but our command handlers expect - // lower case for non-special keys. - - // ignore key up events - if (evt.GetEventType() == wxEVT_KEY_UP) - return; - - // Make ascii keys lower case - this apparently works correctly also with modifiers (shift included) - if (evt.m_keyCode >= 'A' && evt.m_keyCode <= 'Z') - evt.m_keyCode += (int)'a' - 'A'; -#endif - - if (CoreThread.IsOpen()) - { - return; - } - - DirectKeyCommand( evt ); -} - -void GSPanel::DirectKeyCommand( const KeyAcceleratorCode& kac ) -{ - const GlobalCommandDescriptor* cmd = NULL; - - std::unordered_map::const_iterator iter(m_Accels->find(kac.val32)); - if (iter == m_Accels->end()) - return; - - cmd = iter->second; - - DbgCon.WriteLn( "(gsFrame) Invoking command: %s", cmd->Id ); - cmd->Invoke(); - - if( cmd->AlsoApplyToGui && !g_ConfigPanelChanged) - AppApplySettings(); -} - -void GSPanel::DirectKeyCommand( wxKeyEvent& evt ) -{ - DirectKeyCommand(KeyAcceleratorCode( evt )); -} - -void GSPanel::UpdateScreensaver() -{ - const bool prevent = g_Conf->GSWindow.DisableScreenSaver && m_HasFocus && m_coreRunning; - WindowInfo::InhibitScreensaver(g_gs_window_info, prevent); -} - -void GSPanel::OnFocus( wxFocusEvent& evt ) -{ - evt.Skip(); - m_HasFocus = true; - - if( g_Conf->GSWindow.AlwaysHideMouse ) - { - SetCursor( wxCursor(wxCURSOR_BLANK) ); - m_CursorShown = false; - } - else - DoShowMouse(); - -#if defined(__unix__) || defined(__APPLE__) - // HACK2: In gsopen2 there is one event buffer read by both wx/gui and pad. Wx deletes - // the event before the pad see it. So you send key event directly to the pad. - HostKeyEvent event = {HostKeyEvent::Type::FocusGained, 0}; - PADWriteEvent(event); -#endif - //Console.Warning("GS frame > focus set"); - - UpdateScreensaver(); -} - -void GSPanel::OnFocusLost( wxFocusEvent& evt ) -{ - evt.Skip(); - m_HasFocus = false; - DoShowMouse(); -#if defined(__unix__) || defined(__APPLE__) - // HACK2: In gsopen2 there is one event buffer read by both wx/gui and pad. Wx deletes - // the event before the pad see it. So you send key event directly to the pad. - HostKeyEvent event = {HostKeyEvent::Type::FocustLost, 0}; - PADWriteEvent(event); -#endif - //Console.Warning("GS frame > focus lost"); - - UpdateScreensaver(); -} - -void GSPanel::CoreThread_OnResumed() -{ - m_coreRunning = true; - UpdateScreensaver(); -} - -void GSPanel::CoreThread_OnSuspended() -{ - m_coreRunning = false; - UpdateScreensaver(); -} - -void GSPanel::AppStatusEvent_OnSettingsApplied() -{ - if( IsBeingDeleted() ) return; - DoShowMouse(); -} - -void GSPanel::OnLeftDclick(wxMouseEvent& evt) -{ - if( !g_Conf->GSWindow.IsToggleFullscreenOnDoubleClick ) - return; - - //Console.WriteLn("GSPanel::OnDoubleClick: Invoking Fullscreen-Toggle accelerator."); - DirectKeyCommand(FULLSCREEN_TOGGLE_ACCELERATOR_GSPANEL); -} - -#if defined(__WXGTK__) && defined(GDK_WINDOWING_WAYLAND) && defined(WAYLAND_API) - -void GSPanel::WaylandGlobalRegistryAddHandler(void* data, wl_registry* registry, uint32_t id, const char* interface, uint32_t version) -{ - GSPanel* pnl = static_cast(data); - if (std::strcmp(interface, wl_compositor_interface.name) == 0) - { - pnl->m_wl_compositor = static_cast(wl_registry_bind(registry, id, &wl_compositor_interface, wl_compositor_interface.version)); - } - else if (std::strcmp(interface, wl_subcompositor_interface.name) == 0) - { - pnl->m_wl_subcompositor = static_cast(wl_registry_bind(registry, id, &wl_subcompositor_interface, wl_subcompositor_interface.version)); - } -} - -void GSPanel::WaylandGlobalRegistryRemoveHandler(void* data, wl_registry* registry, uint32_t id) -{ -} - -bool GSPanel::WaylandCreateSubsurface(wl_display* display, wl_surface* surface) -{ - static constexpr wl_registry_listener registry_listener = { - &GSPanel::WaylandGlobalRegistryAddHandler, - &GSPanel::WaylandGlobalRegistryRemoveHandler}; - - wl_registry* registry = wl_display_get_registry(display); - wl_registry_add_listener(registry, ®istry_listener, this); - wl_display_flush(display); - wl_display_roundtrip(display); - if (!m_wl_compositor || !m_wl_subcompositor) - { - Console.Error("Missing wl_compositor or wl_subcompositor"); - return false; - } - - wl_registry_destroy(registry); - - m_wl_child_surface = wl_compositor_create_surface(m_wl_compositor); - if (!m_wl_child_surface) - { - Console.Error("Failed to create compositor surface"); - return false; - } - - wl_region* input_region = wl_compositor_create_region(m_wl_compositor); - if (!input_region) - { - Console.Error("Failed to create input region"); - return false; - } - wl_surface_set_input_region(m_wl_child_surface, input_region); - wl_region_destroy(input_region); - - m_wl_child_subsurface = wl_subcompositor_get_subsurface(m_wl_subcompositor, m_wl_child_surface, surface); - if (!m_wl_child_subsurface) - { - Console.Error("Failed to create subsurface"); - return false; - } - - // we want to present asynchronously to the surrounding window - wl_subsurface_set_desync(m_wl_child_subsurface); - return true; -} - -void GSPanel::WaylandDestroySubsurface() -{ - if (m_wl_child_subsurface) - { - wl_subsurface_destroy(m_wl_child_subsurface); - m_wl_child_subsurface = nullptr; - } - - if (m_wl_child_surface) - { - wl_surface_destroy(m_wl_child_surface); - m_wl_child_surface = nullptr; - } - - if (m_wl_subcompositor) - { - wl_subcompositor_destroy(m_wl_subcompositor); - m_wl_subcompositor = nullptr; - } - - if (m_wl_compositor) - { - wl_compositor_destroy(m_wl_compositor); - m_wl_compositor = nullptr; - } -} - -#endif - - -// -------------------------------------------------------------------------------------- -// GSFrame Implementation -// -------------------------------------------------------------------------------------- - -static const uint TitleBarUpdateMs = 333; -static const uint TitleBarUpdateMsWhenRecording = 50; - -GSFrame::GSFrame( const wxString& title) - : wxFrame(NULL, wxID_ANY, title, g_Conf->GSWindow.WindowPos) - , m_timer_UpdateTitle( this ) -{ - SetIcons( wxGetApp().GetIconBundle() ); - SetBackgroundColour( *wxBLACK ); - - AppStatusEvent_OnSettingsApplied(); - - GSPanel* gsPanel = new GSPanel( this ); - m_id_gspanel = gsPanel->GetId(); - gsPanel->SetPosition(wxPoint(0, 0)); - gsPanel->SetSize(GetClientSize()); - - // TODO -- Implement this GS window status window! Whee. - // (main concern is retaining proper client window sizes when closing/re-opening the window). - //m_statusbar = CreateStatusBar( 2 ); - - Bind(wxEVT_CLOSE_WINDOW, &GSFrame::OnCloseWindow, this); - Bind(wxEVT_MOVE, &GSFrame::OnMove, this); - Bind(wxEVT_SIZE, &GSFrame::OnResize, this); - Bind(wxEVT_SET_FOCUS, &GSFrame::OnFocus, this); - - Bind(wxEVT_TIMER, &GSFrame::OnUpdateTitle, this, m_timer_UpdateTitle.GetId()); -} - -void GSFrame::OnCloseWindow(wxCloseEvent& evt) -{ - // if a gs dump is running, it cleans up the window once it's hidden. - if (GSDump::isRunning) - { - Hide(); - return; - } - - // but under normal operation, we want to suspend the core thread, which will hide us - // (except if hide-on-escape is enabled, in which case we want to force hide ourself) - sApp.OnGsFrameClosed( GetId() ); - if (!IsShown()) - Hide(); -} - -void GSFrame::OnDestroyWindow(wxWindowDestroyEvent& evt) -{ - sApp.OnGsFrameDestroyed(GetId()); - evt.Skip(); -} - -bool GSFrame::ShowFullScreen(bool show, bool updateConfig) -{ - /*if( show != IsFullScreen() ) - Console.WriteLn( Color_StrongMagenta, "(gsFrame) Switching to %s mode...", show ? "Fullscreen" : "Windowed" );*/ - - if (updateConfig && g_Conf->GSWindow.IsFullscreen != show) - { - g_Conf->GSWindow.IsFullscreen = show; - wxGetApp().PostIdleMethod( AppSaveSettings ); - } - - // IMPORTANT! On MSW platforms you must ALWAYS show the window prior to calling - // ShowFullscreen(), otherwise the window will be oddly unstable (lacking input and unable - // to properly flip back into fullscreen mode after alt-enter). I don't know if that - // also happens on Linux. - - if( !IsShown() ) Show(); - - uint flags = wxFULLSCREEN_ALL; -#ifdef _WIN32 - flags |= g_Conf->GSWindow.EnableVsyncWindowFlag ? WS_POPUP : 0; -#endif - bool retval = _parent::ShowFullScreen( show, flags ); - - return retval; -} - -void GSFrame::UpdateTitleUpdateFreq() -{ - if (g_Conf->EmuOptions.EnableRecordingTools) - { - m_timer_UpdateTitle.Start(TitleBarUpdateMsWhenRecording); - } - else - { - m_timer_UpdateTitle.Start(TitleBarUpdateMs); - } -} - -void GSFrame::CoreThread_OnResumed() -{ - UpdateTitleUpdateFreq(); - if (!IsShown()) - { - Show(); - } -} - -void GSFrame::CoreThread_OnSuspended() -{ - if( !IsBeingDeleted() && g_Conf->GSWindow.CloseOnEsc ) Hide(); -} - -void GSFrame::CoreThread_OnStopped() -{ - //if( !IsBeingDeleted() ) Destroy(); -} - -// overrides base Show behavior. -bool GSFrame::Show( bool shown ) -{ - if( shown ) - { - GSPanel* gsPanel = GetViewport(); - - if( !gsPanel || gsPanel->IsBeingDeleted() ) - { - gsPanel = new GSPanel( this ); - m_id_gspanel = gsPanel->GetId(); - } - - gsPanel->SetFocus(); - - if (!m_timer_UpdateTitle.IsRunning()) - { - if (g_Conf->EmuOptions.EnableRecordingTools) - { - m_timer_UpdateTitle.Start(TitleBarUpdateMsWhenRecording); - } - else - { - m_timer_UpdateTitle.Start(TitleBarUpdateMs); - } - } - } - else - { - m_timer_UpdateTitle.Stop(); - } - - return _parent::Show( shown ); -} - -void GSFrame::AppStatusEvent_OnSettingsApplied() -{ - if( IsBeingDeleted() ) return; - - SetWindowStyle((g_Conf->GSWindow.DisableResizeBorders ? 0 : wxRESIZE_BORDER) | wxCAPTION | wxCLIP_CHILDREN | - wxSYSTEM_MENU | wxMINIMIZE_BOX | wxMAXIMIZE_BOX | wxCLOSE_BOX); - if (!IsFullScreen() && !IsMaximized()) - SetClientSize(g_Conf->GSWindow.WindowSize); - Refresh(); -} - -GSPanel* GSFrame::GetViewport() -{ - return (GSPanel*)FindWindowById( m_id_gspanel ); -} - -void GSFrame::OnUpdateTitle( wxTimerEvent& evt ) -{ - // Update the title only after the completion of at least a single Vsync, it's pointless to display the fps - // when there are have been no frames rendered and SMODE2 register seems to fresh start with 0 on all the bitfields which - // leads to the value of INT bit as 0 initially and the games are mentioned as progressive which is a bit misleading, - // to prevent such issues, let's update the title after the actual frame rendering starts. - if (g_FrameCount == 0) - return; - - FastFormatUnicode cpuUsage; - if (!IsFullScreen()) { - cpuUsage.Write(L"EE: %3.0f%%", PerformanceMetrics::GetCPUThreadUsage()); - cpuUsage.Write(L" | GS: %3.0f%%", PerformanceMetrics::GetGSThreadUsage()); - - if (THREAD_VU1) - cpuUsage.Write(L" | VU: %3.0f%%", PerformanceMetrics::GetVUThreadUsage()); - } - -#ifdef __linux__ - // Important Linux note: When the title is set in fullscreen the window is redrawn. Unfortunately - // an intermediate white screen appears too which leads to a very annoying flickering. - if (IsFullScreen()) return; -#endif - - AppConfig::UiTemplateOptions& templates = g_Conf->Templates; - - wxString limiterStr = templates.LimiterUnlimited; - - if( g_Conf->EmuOptions.GS.FrameLimitEnable ) - { - switch( EmuConfig.LimiterMode ) - { - case LimiterModeType::Nominal: limiterStr = templates.LimiterNormal; break; - case LimiterModeType::Turbo: limiterStr = templates.LimiterTurbo; break; - case LimiterModeType::Slomo: limiterStr = templates.LimiterSlowmo; break; - case LimiterModeType::Unlimited: limiterStr = templates.LimiterUnlimited; break; - } - } - - const u64& smode2 = *(u64*)PS2GS_BASE(GS_SMODE2); - wxString omodef = (smode2 & 2) ? templates.OutputFrame : templates.OutputField; - wxString omodei = (smode2 & 1) ? templates.OutputInterlaced : templates.OutputProgressive; - wxString title = templates.TitleTemplate; - - std::string gsStats; - GSgetTitleStats(gsStats); - - title.Replace(L"${slot}", pxsFmt(L"%d", States_GetCurrentSlot())); - title.Replace(L"${limiter}", limiterStr); - title.Replace(L"${speed}", pxsFmt(L"%3d%%", lround(PerformanceMetrics::GetSpeed()))); - title.Replace(L"${vfps}", pxsFmt(L"%.02f", PerformanceMetrics::GetFPS())); - title.Replace(L"${cpuusage}", cpuUsage); - title.Replace(L"${omodef}", omodef); - title.Replace(L"${omodei}", omodei); - title.Replace(L"${gs}", StringUtil::UTF8StringToWxString(gsStats)); - title.Replace(L"${videomode}", ReportVideoMode()); - if (CoreThread.IsPaused() && !GSDump::isRunning) - title = templates.Paused + title; - - SetTitle(title); -} - -void GSFrame::OnFocus( wxFocusEvent& evt ) -{ - if( IsBeingDeleted() ) return; - - evt.Skip(false); // Reject the focus message, as we pass focus to the child - if( wxWindow* gsPanel = GetViewport() ) gsPanel->SetFocus(); -} - -void GSFrame::OnMove( wxMoveEvent& evt ) -{ - if( IsBeingDeleted() ) return; - - evt.Skip(); - - g_Conf->GSWindow.IsMaximized = IsMaximized(); - - // evt.GetPosition() returns the client area position, not the window frame position. - if( !g_Conf->GSWindow.IsMaximized && !IsFullScreen() && !IsIconized() && IsVisible() ) - g_Conf->GSWindow.WindowPos = GetScreenPosition(); - - // wxGTK note: X sends gratuitous amounts of OnMove messages for various crap actions - // like selecting or deselecting a window, which muck up docking logic. We filter them - // out using 'lastpos' here. :) - - //static wxPoint lastpos( wxDefaultCoord, wxDefaultCoord ); - //if( lastpos == evt.GetPosition() ) return; - //lastpos = evt.GetPosition(); -} - -void GSFrame::SetFocus() -{ - _parent::SetFocus(); - if( GSPanel* gsPanel = GetViewport() ) - gsPanel->SetFocusFromKbd(); -} - -void GSFrame::OnResize( wxSizeEvent& evt ) -{ - if( IsBeingDeleted() ) return; - - if( !IsFullScreen() && !IsMaximized() && IsVisible() ) - { - g_Conf->GSWindow.WindowSize = GetClientSize(); - } - - // Ensure we're always in sync with the parent size. - if( GSPanel* gsPanel = GetViewport() ) - gsPanel->SetSize(evt.GetSize()); - - evt.Skip(); -} diff --git a/pcsx2/gui/GSFrame.h b/pcsx2/gui/GSFrame.h deleted file mode 100644 index 0166adf7f1..0000000000 --- a/pcsx2/gui/GSFrame.h +++ /dev/null @@ -1,138 +0,0 @@ - -/* PCSX2 - PS2 Emulator for PCs - * Copyright (C) 2002-2010 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 . - */ - -#pragma once - -#include "AppCommon.h" -#include "common/WindowInfo.h" -#include -#include - -#ifdef WAYLAND_API -#include -#endif - -// -------------------------------------------------------------------------------------- -// GSPanel -// -------------------------------------------------------------------------------------- -class GSPanel : public wxWindow - , public EventListener_AppStatus - , public EventListener_CoreThread -{ - typedef wxWindow _parent; - -protected: - std::unique_ptr m_Accels; - - wxTimer m_HideMouseTimer; - bool m_CursorShown; - bool m_HasFocus; - bool m_coreRunning; - -public: - GSPanel( wxWindow* parent ); - virtual ~GSPanel(); - - std::optional GetWindowInfo(); - - void DoShowMouse(); - void DirectKeyCommand( wxKeyEvent& evt ); - void DirectKeyCommand( const KeyAcceleratorCode& kac ); - void InitDefaultAccelerators(); - wxString GetAssociatedKeyCode(const char* id); - -protected: - void AppStatusEvent_OnSettingsApplied(); - - void OnResize(wxEvent& event); - void OnMouseEvent( wxMouseEvent& evt ); - void OnHideMouseTimeout( wxTimerEvent& evt ); - void OnKeyDownOrUp( wxKeyEvent& evt ); - void OnFocus( wxFocusEvent& evt ); - void OnFocusLost( wxFocusEvent& evt ); - void CoreThread_OnResumed(); - void CoreThread_OnSuspended(); - - void OnLeftDclick( wxMouseEvent& evt ); - - void UpdateScreensaver(); - -private: -#ifdef WAYLAND_API - static void WaylandGlobalRegistryAddHandler(void* data, wl_registry* registry, uint32_t id, const char* interface, uint32_t version); - static void WaylandGlobalRegistryRemoveHandler(void* data, wl_registry* registry, uint32_t id); - - bool WaylandCreateSubsurface(wl_display* display, wl_surface* parent_surface); - void WaylandDestroySubsurface(); - - wl_compositor* m_wl_compositor = nullptr; - wl_subcompositor* m_wl_subcompositor = nullptr; - wl_surface* m_wl_child_surface = nullptr; - wl_subsurface* m_wl_child_subsurface = nullptr; -#endif -}; - - -// -------------------------------------------------------------------------------------- -// GSFrame -// -------------------------------------------------------------------------------------- -class GSFrame : public wxFrame - , public EventListener_AppStatus - , public EventListener_CoreThread -{ - typedef wxFrame _parent; - -protected: - wxTimer m_timer_UpdateTitle; - wxWindowID m_id_gspanel; - wxStatusBar* m_statusbar; - -public: - GSFrame( const wxString& title); - virtual ~GSFrame() = default; - - GSPanel* GetViewport(); - void SetFocus(); - bool Show( bool shown=true ); - - bool ShowFullScreen(bool show, bool updateConfig = true); - void UpdateTitleUpdateFreq(); - -protected: - void OnCloseWindow( wxCloseEvent& evt ); - void OnDestroyWindow( wxWindowDestroyEvent& evt ); - void OnMove( wxMoveEvent& evt ); - void OnResize( wxSizeEvent& evt ); - void OnFocus( wxFocusEvent& evt ); - void OnUpdateTitle( wxTimerEvent& evt ); - - void AppStatusEvent_OnSettingsApplied(); - void CoreThread_OnResumed(); - void CoreThread_OnSuspended(); - void CoreThread_OnStopped(); -}; - -// -------------------------------------------------------------------------------------- -// s* macros! ['s' stands for 'shortcut'] -// -------------------------------------------------------------------------------------- -// Use these for "silent fail" invocation of PCSX2 Application-related constructs. If the -// construct (albeit wxApp, MainFrame, CoreThread, etc) is null, the requested method will -// not be invoked, and an optional "else" clause can be affixed for handling the end case. -// -// See App.h (sApp) for more details. -// -#define sGSFrame \ - if( GSFrame* __gsframe_ = wxGetApp().GetGsFramePtr() ) (*__gsframe_) diff --git a/pcsx2/gui/GlobalCommands.cpp b/pcsx2/gui/GlobalCommands.cpp deleted file mode 100644 index 56676e508f..0000000000 --- a/pcsx2/gui/GlobalCommands.cpp +++ /dev/null @@ -1,733 +0,0 @@ -/* PCSX2 - PS2 Emulator for PCs - * Copyright (C) 2002-2010 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 . - */ - -#include "PrecompiledHeader.h" -#include "MainFrame.h" -#include "GSFrame.h" -#include "Host.h" -#include "ApplyState.h" -#include "ConsoleLogger.h" - -#include "AppAccelerators.h" -#include "AppSaveStates.h" - -#include "Recording/InputRecordingControls.h" -#include "Recording/InputRecording.h" - -// Various includes needed for dumping... -#include "GS.h" -#include "Dump.h" -#include "DebugTools/Debug.h" -#include "R3000A.h" -#include "SPU2/spu2.h" -#include "gui/Dialogs/ModalPopups.h" - -static bool g_Pcsx2Recording = false; // true if recording video and sound - - -KeyAcceleratorCode::KeyAcceleratorCode(const wxKeyEvent& evt) -{ - val32 = 0; - - keycode = evt.GetKeyCode(); - - if (evt.AltDown()) - Alt(); - if (evt.CmdDown()) - Cmd(); - if (evt.ShiftDown()) - Shift(); -} - -wxString KeyAcceleratorCode::ToString() const -{ - // Let's use wx's string formatter: - - return wxAcceleratorEntry( - (cmd ? wxACCEL_CMD : 0) | - (shift ? wxACCEL_SHIFT : 0) | - (alt ? wxACCEL_ALT : 0), - keycode) - .ToString(); -} - -namespace Implementations -{ - void Framelimiter_TurboToggle() - { - ScopedCoreThreadPause pauser; - - if (!g_Conf->EmuOptions.GS.FrameLimitEnable) - { - g_Conf->EmuOptions.GS.FrameLimitEnable = true; - g_Conf->EmuOptions.LimiterMode = LimiterModeType::Turbo; - Host::AddKeyedOSDMessage("FrameLimiter", "Turbo + Frame limiter ENABLED."); - } - else if (g_Conf->EmuOptions.LimiterMode == LimiterModeType::Turbo) - { - g_Conf->EmuOptions.LimiterMode = LimiterModeType::Nominal; - Host::AddKeyedOSDMessage("FrameLimiter", "Turbo DISABLED."); - } - else - { - g_Conf->EmuOptions.LimiterMode = LimiterModeType::Turbo; - Host::AddKeyedOSDMessage("FrameLimiter", "Turbo ENABLED."); - } - - pauser.AllowResume(); - } - - void Framelimiter_SlomoToggle() - { - // Slow motion auto-enables the framelimiter even if it's disabled. - // This seems like desirable and expected behavior. - - // FIXME: Inconsistent use of g_Conf->EmuOptions vs. EmuConfig. Should figure - // out a better consistency approach... -air - - ScopedCoreThreadPause pauser; - if (g_Conf->EmuOptions.LimiterMode == LimiterModeType::Slomo) - { - g_Conf->EmuOptions.LimiterMode = LimiterModeType::Nominal; - Host::AddKeyedOSDMessage("FrameLimiter", "Slow motion DISABLED."); - } - else - { - g_Conf->EmuOptions.LimiterMode = LimiterModeType::Slomo; - Host::AddKeyedOSDMessage("FrameLimiter", "Slow motion ENABLED."); - g_Conf->EmuOptions.GS.FrameLimitEnable = true; - } - - pauser.AllowResume(); - } - - void Framelimiter_MasterToggle() - { - ScopedCoreThreadPause pauser; - g_Conf->EmuOptions.GS.FrameLimitEnable = !g_Conf->EmuOptions.GS.FrameLimitEnable; - Host::AddKeyedFormattedOSDMessage("FrameLimiter", 2.0f, "Frame limiter %s.", g_Conf->EmuOptions.GS.FrameLimitEnable ? "ENABLED" : "DISABLED"); - - // Turbo/Slowmo don't make sense when framelimiter is toggled - g_Conf->EmuOptions.LimiterMode = LimiterModeType::Nominal; - - pauser.AllowResume(); - } - - void GSwindow_CycleAspectRatio() - { - AspectRatioType& art = EmuConfig.CurrentAspectRatio; - const char* arts = "Not modified"; - switch (art) - { - case AspectRatioType::Stretch: - art = AspectRatioType::RAuto4_3_3_2; - arts = "Auto 4:3/3:2"; - break; - case AspectRatioType::RAuto4_3_3_2: - art = AspectRatioType::R4_3; - arts = "4:3"; - break; - case AspectRatioType::R4_3: - art = AspectRatioType::R16_9; - arts = "16:9"; - break; - case AspectRatioType::R16_9: - art = AspectRatioType::Stretch; - arts = "Stretch"; - break; - default: - break; - } - - // Sync the mode with the settings. This is kinda silly, since they won't be - // saved until shutdown, but it matches the behavior pre-settings-move. - g_Conf->EmuOptions.GS.AspectRatio = art; - - // Prevent GS reopening for the setting change. - EmuConfig.GS.AspectRatio = art; - - Host::AddKeyedFormattedOSDMessage("AspectRatio", 2.0f, "Aspect ratio: %s", arts); - } - - void SetZoomY(float zoom) - { - if (zoom <= 0) - return; - g_Conf->EmuOptions.GS.StretchY = zoom; - EmuConfig.GS.StretchY = zoom; - GSConfig.StretchY = zoom; - Host::AddKeyedFormattedOSDMessage("WindowVStretch", 2.0f, "Vertical stretch: %f", zoom); - } - - void GSwindow_ZoomInY() - { - SetZoomY(EmuConfig.GS.StretchY + 1); - } - void GSwindow_ZoomOutY() - { - SetZoomY(EmuConfig.GS.StretchY - 1); - } - void GSwindow_ZoomResetY() - { - SetZoomY(100); - } - - void Sys_Suspend() - { - CoreThread.Suspend(); - - GSFrame* gsframe = wxGetApp().GetGsFramePtr(); - if (gsframe && !wxGetApp().HasGUI() && g_Conf->GSWindow.CloseOnEsc) - { - // When we run with --nogui, PCSX2 only knows to exit when the gs window closes. - // However, by default suspend just hides the gs window, so PCSX2 will not exit - // and there will also be no way to exit it even if no windows are left. - // If the gs window is not set to close on suspend, then the user can still - // close it with the X button, which PCSX2 will recognize and exit. - // So if we're set to close on esc and nogui: - // If the user didn't specify --noguiprompt - exit immediately. - // else prompt to either exit or abort the suspend. - if (!wxGetApp().ExitPromptWithNoGUI() // configured to exit without a dialog - || (wxOK == wxMessageBox(_("Exit PCSX2?"), // or confirmed exit at the dialog - L"PCSX2", - wxICON_WARNING | wxOK | wxCANCEL))) - { - // Pcsx2App knows to exit if no gui and the GS window closes. - gsframe->Close(); - return; - } - else - { - // aborting suspend request - // Note: if we didn't want to suspend emulation for this confirmation dialog, - // then pressing ESC would have exited fullscreen without PCSX2 knowing about it, - // and since it's not suspended it would not re-init the fullscreen state if the - // confirmation is aborted. On such case we'd have needed to set the gsframe - // fullscreen mode here according to g_Conf->GSWindow.IsFullscreen - CoreThread.Resume(); - return; - } - } - - if (g_Conf->GSWindow.CloseOnEsc) - { - sMainFrame.SetFocus(); - } - } - - void Sys_Resume() - { - CoreThread.Resume(); - } - - void Sys_SuspendResume() - { - if (CoreThread.HasPendingStateChangeRequest()) - return; - - if (CoreThread.IsPaused()) - Sys_Resume(); - else - Sys_Suspend(); - } - - void Sys_TakeSnapshot() - { - GSQueueSnapshot(std::string(), 0); - } - - void Sys_RenderToggle() - { - if (GSDump::isRunning) - return; - static bool reentrant = false; - if (!reentrant) - { - reentrant = true; - ScopedCoreThreadPause paused_core; - GetMTGS().ToggleSoftwareRendering(); - paused_core.AllowResume(); - reentrant = false; - } - } - - void Sys_LoggingToggle() - { - // There's likely a better way to implement this, but this seemed useful. - // I might add turning EE, VU0, and VU1 recs on and off by hotkey at some point, too. - // --arcum42 - - // FIXME: Some of the trace logs will require recompiler resets to be activated properly. -#ifdef PCSX2_DEVBUILD - // This is touching the CPU thread's settings, it really shouldn't be, but it'll desync with the UI if we don't. - g_Conf->EmuOptions.Trace.Enabled = !g_Conf->EmuOptions.Trace.Enabled; - EmuConfig.Trace.Enabled = g_Conf->EmuOptions.Trace.Enabled; - Console.WriteLn(g_Conf->EmuOptions.Trace.Enabled ? "Logging Enabled." : "Logging Disabled."); -#endif - } - - void Cpu_DumpRegisters() - { -#ifdef PCSX2_DEVBUILD - iDumpRegisters(cpuRegs.pc, 0); - Console.Warning("hardware registers dumped EE:%x, IOP:%x\n", cpuRegs.pc, psxRegs.pc); -#endif - } - - void FullscreenToggle() - { - if (GSFrame* gsframe = wxGetApp().GetGsFramePtr()) - gsframe->ShowFullScreen(!gsframe->IsFullScreen()); - } - - void States_SaveSlot(int slot) - { - States_SetCurrentSlot(slot); - States_FreezeCurrentSlot(); - } - - void States_LoadSlot(int slot) - { - States_SetCurrentSlot(slot); - States_DefrostCurrentSlot(); - } - - void States_SaveSlot0() - { - States_SaveSlot(0); - } - - void States_SaveSlot1() - { - States_SaveSlot(1); - } - - void States_SaveSlot2() - { - States_SaveSlot(2); - } - - void States_SaveSlot3() - { - States_SaveSlot(3); - } - - void States_SaveSlot4() - { - States_SaveSlot(4); - } - - void States_SaveSlot5() - { - States_SaveSlot(5); - } - - void States_SaveSlot6() - { - States_SaveSlot(6); - } - - void States_SaveSlot7() - { - States_SaveSlot(7); - } - - void States_SaveSlot8() - { - States_SaveSlot(8); - } - - void States_SaveSlot9() - { - States_SaveSlot(9); - } - - void States_LoadSlot0() - { - States_LoadSlot(0); - } - - void States_LoadSlot1() - { - States_LoadSlot(1); - } - - void States_LoadSlot2() - { - States_LoadSlot(2); - } - - void States_LoadSlot3() - { - States_LoadSlot(3); - } - - void States_LoadSlot4() - { - States_LoadSlot(4); - } - - void States_LoadSlot5() - { - States_LoadSlot(5); - } - - void States_LoadSlot6() - { - States_LoadSlot(6); - } - - void States_LoadSlot7() - { - States_LoadSlot(7); - } - - void States_LoadSlot8() - { - States_LoadSlot(8); - } - - void States_LoadSlot9() - { - States_LoadSlot(9); - } -} // namespace Implementations - -// -------------------------------------------------------------------------------------- -// CommandDeclarations table -// -------------------------------------------------------------------------------------- -// This is our manualized introspection/reflection table. In a cool language like C# we'd -// have just grabbed this info from enumerating the members of a class and assigning -// properties to each method in the class. But since this is C++, we have to do the the -// goold old fashioned way! :) - -static const GlobalCommandDescriptor CommandDeclarations[] = - { - { - "States_FreezeCurrentSlot", - States_FreezeCurrentSlot, - pxL("Save state"), - pxL("Saves the virtual machine state to the current slot."), - false, - }, - - { - "States_DefrostCurrentSlot", - States_DefrostCurrentSlot, - pxL("Load state"), - pxL("Loads a virtual machine state from the current slot."), - false, - }, - - { - "States_DefrostCurrentSlotBackup", - States_DefrostCurrentSlotBackup, - pxL("Load State Backup"), - pxL("Loads virtual machine state backup for current slot."), - false, - }, - - { - "States_CycleSlotForward", - States_CycleSlotForward, - pxL("Cycle to next slot"), - pxL("Cycles the current save slot in +1 fashion!"), - false, - }, - - { - "States_CycleSlotBackward", - States_CycleSlotBackward, - pxL("Cycle to prev slot"), - pxL("Cycles the current save slot in -1 fashion!"), - false, - }, - - { - "Framelimiter_TurboToggle", - Implementations::Framelimiter_TurboToggle, - NULL, - NULL, - false, - }, - - { - "Framelimiter_SlomoToggle", - Implementations::Framelimiter_SlomoToggle, - NULL, - NULL, - false, - }, - - { - "Framelimiter_MasterToggle", - Implementations::Framelimiter_MasterToggle, - NULL, - NULL, - false, - }, - - { - "GSwindow_CycleAspectRatio", - Implementations::GSwindow_CycleAspectRatio, - NULL, - NULL, - true, - }, - - {"GSwindow_ZoomInY", Implementations::GSwindow_ZoomInY, NULL, NULL, false}, - {"GSwindow_ZoomOutY", Implementations::GSwindow_ZoomOutY, NULL, NULL, false}, - {"GSwindow_ZoomResetY", Implementations::GSwindow_ZoomResetY, NULL, NULL, false}, - - { - "Sys_SuspendResume", - Implementations::Sys_SuspendResume, - NULL, - NULL, - false, - }, - - { - "Sys_TakeSnapshot", - Implementations::Sys_TakeSnapshot, - NULL, - NULL, - false, - }, - - { - "Sys_RenderswitchToggle", - Implementations::Sys_RenderToggle, - NULL, - NULL, - false, - }, - - { - "Sys_LoggingToggle", - Implementations::Sys_LoggingToggle, - NULL, - NULL, - false, - }, - - { - "FullscreenToggle", - Implementations::FullscreenToggle, - NULL, - NULL, - false, - }, - - {"States_SaveSlot0", Implementations::States_SaveSlot0, NULL, NULL, false}, - {"States_SaveSlot1", Implementations::States_SaveSlot1, NULL, NULL, false}, - {"States_SaveSlot2", Implementations::States_SaveSlot2, NULL, NULL, false}, - {"States_SaveSlot3", Implementations::States_SaveSlot3, NULL, NULL, false}, - {"States_SaveSlot4", Implementations::States_SaveSlot4, NULL, NULL, false}, - {"States_SaveSlot5", Implementations::States_SaveSlot5, NULL, NULL, false}, - {"States_SaveSlot6", Implementations::States_SaveSlot6, NULL, NULL, false}, - {"States_SaveSlot7", Implementations::States_SaveSlot7, NULL, NULL, false}, - {"States_SaveSlot8", Implementations::States_SaveSlot8, NULL, NULL, false}, - {"States_SaveSlot9", Implementations::States_SaveSlot9, NULL, NULL, false}, - - {"States_LoadSlot0", Implementations::States_LoadSlot0, NULL, NULL, false}, - {"States_LoadSlot1", Implementations::States_LoadSlot1, NULL, NULL, false}, - {"States_LoadSlot2", Implementations::States_LoadSlot2, NULL, NULL, false}, - {"States_LoadSlot3", Implementations::States_LoadSlot3, NULL, NULL, false}, - {"States_LoadSlot4", Implementations::States_LoadSlot4, NULL, NULL, false}, - {"States_LoadSlot5", Implementations::States_LoadSlot5, NULL, NULL, false}, - {"States_LoadSlot6", Implementations::States_LoadSlot6, NULL, NULL, false}, - {"States_LoadSlot7", Implementations::States_LoadSlot7, NULL, NULL, false}, - {"States_LoadSlot8", Implementations::States_LoadSlot8, NULL, NULL, false}, - {"States_LoadSlot9", Implementations::States_LoadSlot9, NULL, NULL, false}, - // Command Declarations terminator: - // (must always be last in list!!) - {NULL}}; - -void AcceleratorDictionary::Map(const KeyAcceleratorCode& _acode, const char* searchfor) -{ - // Search override mapping at ini file - KeyAcceleratorCode acode = _acode; - wxString overrideStr; - wxAcceleratorEntry codeParser; //Provides string parsing capabilities - wxFileConfig cfg(L"", L"", L"", GetUiKeysFilename(), wxCONFIG_USE_GLOBAL_FILE); - if (cfg.Read(wxString::FromUTF8(searchfor), &overrideStr)) - { - // needs a '\t' prefix (originally used for wxMenu accelerators parsing)... - if (codeParser.FromString(wxString(L"\t") + overrideStr)) - { - // ini file contains alternative parsable key combination for current 'searchfor'. - acode = codeParser; - if (acode.keycode >= 'A' && acode.keycode <= 'Z') - { - // Note that this needs to match the key event codes at Pcsx2App::PadKeyDispatch - // Our canonical representation is the char code (at lower case if - // applicable) with a separate modifier indicator, including shift. - // The parser deviates from this by setting the keycode to upper case if - // modifiers are used with plain letters. Luckily, it sets the modifiers - // correctly, including shift (for letters without modifiers it parses lower case). - // So we only need to change upper case letters to lower case. - acode.keycode += 'a' - 'A'; - } - if (_acode.ToString() != acode.ToString()) - { - Console.WriteLn(Color_StrongGreen, "Overriding '%ls': assigning %ls (instead of %ls)", - WX_STR(fromUTF8(searchfor)), WX_STR(acode.ToString()), WX_STR(_acode.ToString())); - } - } - else - { - Console.Error("Error overriding KB shortcut for '%ls': can't understand '%ls'", - WX_STR(fromUTF8(searchfor)), WX_STR(overrideStr)); - } - } - // End of overrides section - - const GlobalCommandDescriptor* result = NULL; - - std::unordered_map::const_iterator iter(find(acode.val32)); - if (iter != end()) - result = iter->second; - - if (result != NULL) - { - Console.Warning( - "Kbd Accelerator '%ls' is mapped multiple times.\n" - "\t'Command %ls' is being replaced by '%ls'", - WX_STR(acode.ToString()), WX_STR(fromUTF8(result->Id)), WX_STR(fromUTF8(searchfor))); - } - - std::unordered_map::const_iterator acceleratorIter(wxGetApp().GlobalCommands->find(searchfor)); - - if (acceleratorIter != wxGetApp().GlobalCommands->end()) - result = acceleratorIter->second; - - if (result == NULL) - { - Console.Warning("Kbd Accelerator '%ls' is mapped to unknown command '%ls'", - WX_STR(acode.ToString()), WX_STR(fromUTF8(searchfor))); - } - else - { - if (!strcmp("Sys_TakeSnapshot", searchfor)) - { - // Sys_TakeSnapshot is special in a bad way. On its own it creates a screenshot - // but GS also checks whether shift or ctrl are held down, and for each of - // them it does a different thing (gs dumps). So we need to map a shortcut and - // also the same shortcut with shift and the same with ctrl to the same function. - // So make sure the shortcut doesn't include shift or ctrl, and then add two more - // which are derived from it. - // Also, looking at the GS code, it seems that it never cares about both shift - // and ctrl held together, but PCSX2 traditionally mapped f8, shift-f8 and ctrl-shift-f8 - // to Sys_TakeSnapshot, so let's not change it - we'll keep adding only shift and - // ctrl-shift to the base shortcut. - if (acode.cmd || acode.shift) - { - Console.Error("Cannot map %ls to Sys_TakeSnapshot - must not include Shift or Ctrl - these modifiers will be added automatically.", - WX_STR(acode.ToString())); - } - else - { - KeyAcceleratorCode shifted(acode); - shifted.Shift(); - KeyAcceleratorCode controlledShifted(shifted); - controlledShifted.Cmd(); - operator[](acode.val32) = result; - operator[](shifted.val32) = result; - operator[](controlledShifted.val32) = result; - - if (_acode.val32 != acode.val32) - { // overriding default - Console.WriteLn(Color_Green, "Sys_TakeSnapshot: automatically mapping also %ls and %ls", - WX_STR(shifted.ToString()), - WX_STR(controlledShifted.ToString())); - } - } - } - else - { - operator[](acode.val32) = result; - } - } -} - -KeyAcceleratorCode AcceleratorDictionary::findKeycodeWithCommandId(const char* commandId) -{ - for (auto entry = this->begin(); entry != this->end(); entry++) - { - if (strcmp(entry->second->Id, commandId) == 0) - { - const KeyAcceleratorCode keycode(entry->first); - return keycode; - } - } - return KeyAcceleratorCode(0); -} - -void Pcsx2App::BuildCommandHash() -{ - if (!GlobalCommands) - GlobalCommands = std::unique_ptr(new CommandDictionary); - - const GlobalCommandDescriptor* curcmd = CommandDeclarations; - while (curcmd->Invoke != NULL) - { - (*GlobalCommands)[curcmd->Id] = curcmd; - curcmd++; - } -} - -void Pcsx2App::InitDefaultGlobalAccelerators() -{ - typedef KeyAcceleratorCode AAC; - - if (!GlobalAccels) - GlobalAccels = std::unique_ptr(new AcceleratorDictionary); - - // Why do we even have those here? all of them seem to be overridden - // by GSPanel::m_Accels ( GSPanel::InitDefaultAccelerators() ) - // - One reason is because this is used to initialize shortcuts in the MainFrame's UI (see - MainFrame::AppendShortcutToMenuOption) - // this is before the GS Window has been initialized. - - GlobalAccels->Map(AAC(WXK_F1), "States_FreezeCurrentSlot"); - GlobalAccels->Map(AAC(WXK_F3), "States_DefrostCurrentSlot"); - GlobalAccels->Map(AAC(WXK_F2), "States_CycleSlotForward"); - GlobalAccels->Map(AAC(WXK_F2).Shift(), "States_CycleSlotBackward"); - - GlobalAccels->Map(AAC(WXK_F4), "Framelimiter_MasterToggle"); - - // At this early stage of startup, the application assumes installed mode, so portable mode custom keybindings may present issues. - // Relevant - https://github.com/PCSX2/pcsx2/blob/678829a5b2b8ca7a3e42d8edc9ab201bf00b0fe9/pcsx2/gui/AppInit.cpp#L479 - // Compared to L990 of GlobalCommands.cpp which also does an init for the GlobalAccelerators. - // The idea was to have: Reading from the PCSX2_keys.ini in the ini folder based on PCSX2_keys.ini.default which get overridden. - // We also need to make it easier to do custom hotkeys for both normal/portable PCSX2 in the GUI. - GlobalAccels->Map(AAC(WXK_TAB), "Framelimiter_TurboToggle"); - GlobalAccels->Map(AAC(WXK_TAB).Shift(), "Framelimiter_SlomoToggle"); - - GlobalAccels->Map(AAC(WXK_F6), "GSwindow_CycleAspectRatio"); - GlobalAccels->Map(AAC(WXK_RETURN).Alt(), "FullscreenToggle"); - - GlobalAccels->Map(AAC(WXK_ESCAPE), "Sys_SuspendResume"); - - // Fixme: GS Dumps could need a seperate label and hotkey binding or less interlinked with normal screenshots/snapshots , which messes with overloading lots of different mappings, commented the other GlobalAccels for this reason. GS hardcodes keybindings. - GlobalAccels->Map(AAC(WXK_F8), "Sys_TakeSnapshot"); - // GlobalAccels->Map(AAC(WXK_F8).Shift(), "Sys_TakeSnapshot"); - // GlobalAccels->Map(AAC(WXK_F8).Shift().Cmd(), "Sys_TakeSnapshot"); - GlobalAccels->Map(AAC(WXK_F9), "Sys_RenderswitchToggle"); - - // GlobalAccels->Map(AAC(WXK_F10), "Sys_LoggingToggle"); - // GlobalAccels->Map(AAC(WXK_F11), "Sys_FreezeGS"); - GlobalAccels->Map(AAC(WXK_F12), "Sys_RecordingToggle"); -} diff --git a/pcsx2/gui/IniInterface.cpp b/pcsx2/gui/IniInterface.cpp deleted file mode 100644 index ef60907567..0000000000 --- a/pcsx2/gui/IniInterface.cpp +++ /dev/null @@ -1,451 +0,0 @@ -/* PCSX2 - PS2 Emulator for PCs - * Copyright (C) 2002-2010 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 . - */ - -#include - -#include "gui/IniInterface.h" -#include "gui/StringHelpers.h" -#include "common/Assertions.h" -#include "common/Console.h" - -const wxRect wxDefaultRect(wxDefaultCoord, wxDefaultCoord, wxDefaultCoord, wxDefaultCoord); - -wxDirName g_fullBaseDirName = wxDirName(L""); -void SetFullBaseDir(wxDirName appRoot) -{ - g_fullBaseDirName = appRoot; -} - -static int _calcEnumLength(const wxChar* const* enumArray) -{ - int cnt = 0; - while (*enumArray != NULL) - { - enumArray++; - cnt++; - } - - return cnt; -} - -ScopedIniGroup::ScopedIniGroup(IniInterface& mommy, const wxString& group) - : m_mom(mommy) -{ - pxAssertDev(wxStringTokenize(group, L"/").Count() <= 1, "Cannot nest more than one group deep per instance of ScopedIniGroup."); - m_mom.SetPath(group); -} - -ScopedIniGroup::~ScopedIniGroup() -{ - m_mom.SetPath(L".."); -} - -// -------------------------------------------------------------------------------------- -// IniInterface (implementations) -// -------------------------------------------------------------------------------------- -IniInterface::IniInterface(wxConfigBase& config) -{ - m_Config = &config; -} - -IniInterface::IniInterface(wxConfigBase* config) -{ - m_Config = config; -} - -IniInterface::IniInterface() -{ - m_Config = wxConfigBase::Get(false); -} - -IniInterface::~IniInterface() -{ - Flush(); -} - -void IniInterface::SetPath(const wxString& path) -{ - if (m_Config) - m_Config->SetPath(path); -} - -void IniInterface::Flush() -{ - if (m_Config) - m_Config->Flush(); -} - - -// -------------------------------------------------------------------------------------- -// IniLoader (implementations) -// -------------------------------------------------------------------------------------- -IniLoader::IniLoader(wxConfigBase& config) - : IniInterface(config) -{ -} - -IniLoader::IniLoader(wxConfigBase* config) - : IniInterface(config) -{ -} - -IniLoader::IniLoader() - : IniInterface() -{ -} - -void IniLoader::Entry(const wxString& var, wxString& value, const wxString defvalue) -{ - if (m_Config) - m_Config->Read(var, &value, defvalue); - else - value = defvalue; -} - -void IniLoader::Entry(const wxString& var, wxDirName& value, const wxDirName defvalue, bool isAllowRelative) -{ - wxString dest; - if (m_Config) - m_Config->Read(var, &dest, wxEmptyString); - - if (dest.IsEmpty()) - value = defvalue; - else - { - value = dest; - if (isAllowRelative) - value = g_fullBaseDirName + value; - - if (value.IsAbsolute()) - value.Normalize(); - } -} - -void IniLoader::Entry(const wxString& var, wxFileName& value, const wxFileName defvalue, bool isAllowRelative) -{ - wxString dest(defvalue.GetFullPath()); - if (m_Config) - m_Config->Read(var, &dest, defvalue.GetFullPath()); - value = dest; - if (isAllowRelative) - value = g_fullBaseDirName + value; - - if (value.IsAbsolute()) - value.Normalize(); - - if (value.HasVolume()) - value.SetVolume(value.GetVolume().Upper()); -} - -void IniLoader::Entry(const wxString& var, int& value, const int defvalue) -{ - if (m_Config) - m_Config->Read(var, &value, defvalue); - else - value = defvalue; -} - -void IniLoader::Entry(const wxString& var, uint& value, const uint defvalue) -{ - if (m_Config) - m_Config->Read(var, (int*)&value, (int)defvalue); - else - value = defvalue; -} - -void IniLoader::Entry(const wxString& var, bool& value, const bool defvalue) -{ - // TODO : Stricter value checking on enabled/disabled? - wxString dest; - if (defvalue) - dest = wxString("enabled"); - else - dest = wxString("disabled"); - - if (m_Config) - m_Config->Read(var, &dest, dest); - value = (dest == L"enabled") || (dest == L"1"); -} - -bool IniLoader::EntryBitBool(const wxString& var, bool value, const bool defvalue) -{ - // Note: 'value' param is used by inisaver only. - bool result; - Entry(var, result, defvalue); - return result; -} - -int IniLoader::EntryBitfield(const wxString& var, int value, const int defvalue) -{ - int result; - Entry(var, result, defvalue); - return result; -} - -void IniLoader::Entry(const wxString& var, float& value, const float defvalue) -{ - auto readval = wxString::FromCDouble(static_cast(value)); - - if (m_Config) - m_Config->Read(var, &readval); - - double dvalue; - if (!readval.ToCDouble(&dvalue)) - dvalue = 0.0; - value = static_cast(dvalue); -} - -void IniLoader::Entry(const wxString& var, wxPoint& value, const wxPoint defvalue) -{ - if (!m_Config) - { - value = defvalue; - return; - } - TryParse(value, m_Config->Read(var, ToString(defvalue)), defvalue); -} - -void IniLoader::Entry(const wxString& var, wxSize& value, const wxSize defvalue) -{ - if (!m_Config) - { - value = defvalue; - return; - } - TryParse(value, m_Config->Read(var, ToString(defvalue)), defvalue); -} - -void IniLoader::Entry(const wxString& var, wxRect& value, const wxRect defvalue) -{ - if (!m_Config) - { - value = defvalue; - return; - } - TryParse(value, m_Config->Read(var, ToString(defvalue)), defvalue); -} - -void IniLoader::_EnumEntry(const wxString& var, int& value, const wxChar* const* enumArray, int defvalue) -{ - // Confirm default value sanity... - - const int cnt = _calcEnumLength(enumArray); - if (defvalue > cnt) - { - Console.Error("(LoadSettings) Default enumeration index is out of bounds. Truncating."); - defvalue = cnt - 1; - } - - // Sanity confirmed, proceed with craziness! - - if (!m_Config) - { - value = defvalue; - return; - } - - wxString retval; - m_Config->Read(var, &retval, enumArray[defvalue]); - - int i = 0; - while (enumArray[i] != NULL && (retval != enumArray[i])) - i++; - - if (enumArray[i] == NULL) - { - Console.Warning("(LoadSettings) Warning: Unrecognized value '%ls' on key '%ls'\n\tUsing the default setting of '%ls'.", - WX_STR(retval), WX_STR(var), enumArray[defvalue]); - value = defvalue; - } - else - value = i; -} - -void IniLoader::Entry(const wxString& var, std::string& value, const std::string& default_value) -{ - if (m_Config) - { - wxString temp; - m_Config->Read(var, &temp, fromUTF8(default_value)); - value = temp.ToStdString(); - } - else if (&value != &default_value) - { - value.assign(default_value); - } -} - -// -------------------------------------------------------------------------------------- -// IniSaver (implementations) -// -------------------------------------------------------------------------------------- - -IniSaver::IniSaver(wxConfigBase& config) - : IniInterface(config) -{ -} - -IniSaver::IniSaver(wxConfigBase* config) - : IniInterface(config) -{ -} - -IniSaver::IniSaver() - : IniInterface() -{ -} - -void IniSaver::Entry(const wxString& var, wxString& value, const wxString defvalue) -{ - if (!m_Config) - return; - m_Config->Write(var, value); -} - -void IniSaver::Entry(const wxString& var, wxDirName& value, const wxDirName defvalue, bool isAllowRelative) -{ - if (!m_Config) - return; - wxDirName res(value); - - if (res.IsAbsolute()) - res.Normalize(); - - if (isAllowRelative) - res = wxDirName::MakeAutoRelativeTo(res, g_fullBaseDirName.ToString()); - - - /*if( value == defvalue ) - m_Config->Write( var, wxString() ); - else*/ - m_Config->Write(var, res.ToString()); -} - -void IniSaver::Entry(const wxString& var, wxFileName& value, const wxFileName defvalue, bool isAllowRelative) -{ - if (!m_Config) - return; - wxFileName res(value); - - if (res.IsAbsolute()) - res.Normalize(); - - if (isAllowRelative) - res = wxDirName::MakeAutoRelativeTo(res, g_fullBaseDirName.ToString()); - - m_Config->Write(var, res.GetFullPath()); -} - -void IniSaver::Entry(const wxString& var, int& value, const int defvalue) -{ - if (!m_Config) - return; - m_Config->Write(var, value); -} - -void IniSaver::Entry(const wxString& var, uint& value, const uint defvalue) -{ - if (!m_Config) - return; - m_Config->Write(var, (int)value); -} - -void IniSaver::Entry(const wxString& var, bool& value, const bool defvalue) -{ - if (!m_Config) - return; - m_Config->Write(var, value ? L"enabled" : L"disabled"); -} - -bool IniSaver::EntryBitBool(const wxString& var, bool value, const bool defvalue) -{ - if (m_Config) - m_Config->Write(var, value ? L"enabled" : L"disabled"); - return value; -} - -int IniSaver::EntryBitfield(const wxString& var, int value, const int defvalue) -{ - if (m_Config) - m_Config->Write(var, value); - return value; -} - -void IniSaver::Entry(const wxString& var, float& value, const float defvalue) -{ - if (!m_Config) - return; - - m_Config->Write(var, wxString::FromCDouble(static_cast(value))); -} - -void IniSaver::Entry(const wxString& var, wxPoint& value, const wxPoint defvalue) -{ - if (!m_Config) - return; - m_Config->Write(var, ToString(value)); -} - -void IniSaver::Entry(const wxString& var, wxSize& value, const wxSize defvalue) -{ - if (!m_Config) - return; - m_Config->Write(var, ToString(value)); -} - -void IniSaver::Entry(const wxString& var, wxRect& value, const wxRect defvalue) -{ - if (!m_Config) - return; - m_Config->Write(var, ToString(value)); -} - -void IniSaver::_EnumEntry(const wxString& var, int& value, const wxChar* const* enumArray, int defvalue) -{ - const int cnt = _calcEnumLength(enumArray); - - // Confirm default value sanity... - - if (defvalue > cnt) - { - Console.Error("(SaveSettings) Default enumeration index is out of bounds. Truncating."); - defvalue = cnt - 1; - } - - if (!m_Config) - return; - - if (value >= cnt) - { - Console.Warning("(SaveSettings) An illegal enumerated index was detected when saving '%ls'", WX_STR(var)); - Console.Indent().Warning( - "Illegal Value: %d\n" - "Using Default: %d (%ls)\n", - value, defvalue, enumArray[defvalue]); - - // Cause a debug assertion, since this is a fully recoverable error. - pxAssert(value < cnt); - - value = defvalue; - } - - m_Config->Write(var, enumArray[value]); -} - -void IniSaver::Entry(const wxString& var, std::string& value, const std::string& default_value) -{ - if (!m_Config) - return; - m_Config->Write(var, fromUTF8(value)); -} diff --git a/pcsx2/gui/IniInterface.h b/pcsx2/gui/IniInterface.h deleted file mode 100644 index f1baa45e33..0000000000 --- a/pcsx2/gui/IniInterface.h +++ /dev/null @@ -1,200 +0,0 @@ -/* PCSX2 - PS2 Emulator for PCs - * Copyright (C) 2002-2010 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 . - */ - -#pragma once - -#include "common/Assertions.h" -#include "common/Path.h" -#include "wxDirName.h" -#include "StringHelpers.h" -#include -#include -#include - -// -------------------------------------------------------------------------------------- -// IniInterface (abstract base class) -// -------------------------------------------------------------------------------------- -// This is used as an interchangable interface for both loading and saving options from an -// ini/configuration file. The LoadSave code takes an IniInterface, and the interface -// implementation defines whether the options are read or written. -// -// See also: IniLoader, IniSaver -// -class IniInterface -{ -protected: - wxConfigBase* m_Config; - -public: - virtual ~IniInterface(); - explicit IniInterface(); - explicit IniInterface(wxConfigBase& config); - explicit IniInterface(wxConfigBase* config); - - void SetPath(const wxString& path); - void Flush(); - - wxConfigBase& GetConfig() - { - pxAssert(m_Config); - return *m_Config; - } - bool IsOk() const { return m_Config != NULL; } - - virtual bool IsLoading() const = 0; - bool IsSaving() const { return !IsLoading(); } - - virtual void Entry(const wxString& var, int& value, const int defvalue = 0) = 0; - virtual void Entry(const wxString& var, uint& value, const uint defvalue = 0) = 0; - virtual void Entry(const wxString& var, bool& value, const bool defvalue = false) = 0; - virtual void Entry(const wxString& var, float& value, const float defvalue = 0.0) = 0; - virtual void Entry(const wxString& var, std::string& value, const std::string& default_value = std::string()) = 0; - - // This special form of Entry is provided for bitfields, which cannot be passed by reference. - virtual bool EntryBitBool(const wxString& var, bool value, const bool defvalue = false) = 0; - virtual int EntryBitfield(const wxString& var, int value, const int defvalue = 0) = 0; - - virtual void Entry(const wxString& var, wxString& value, const wxString defvalue = wxString()) = 0; - virtual void Entry(const wxString& var, wxDirName& value, const wxDirName defvalue = wxDirName(), bool isAllowRelative = false) = 0; - virtual void Entry(const wxString& var, wxFileName& value, const wxFileName defvalue = wxFileName(), bool isAllowRelative = false) = 0; - - virtual void Entry(const wxString& var, wxPoint& value, const wxPoint defvalue = wxDefaultPosition) = 0; - virtual void Entry(const wxString& var, wxSize& value, const wxSize defvalue = wxDefaultSize) = 0; - virtual void Entry(const wxString& var, wxRect& value, const wxRect defvalue = wxDefaultRect) = 0; - - template - void EnumEntry(const wxString& var, T& value, const wxChar* const* enumArray = NULL, const T defvalue = (T)0) - { - int tstore = (int)value; - auto defaultvalue = enum_cast(defvalue); - if (enumArray == NULL) - Entry(var, tstore, defaultvalue); - else - _EnumEntry(var, tstore, enumArray, defaultvalue); - value = (T)tstore; - } - -protected: - virtual void _EnumEntry(const wxString& var, int& value, const wxChar* const* enumArray, int defvalue) = 0; -}; - -// -------------------------------------------------------------------------------------- -// ScopedIniGroup -// -------------------------------------------------------------------------------------- -class ScopedIniGroup -{ -protected: - IniInterface& m_mom; - -public: - ScopedIniGroup(IniInterface& mommy, const wxString& group); - virtual ~ScopedIniGroup(); -}; - -// -------------------------------------------------------------------------------------- -// IniLoader -// -------------------------------------------------------------------------------------- -// Implementation of the IniInterface base class, which maps ini actions to loading from -// an ini source file. -// -// See also: IniInterface -// -class IniLoader : public IniInterface -{ -public: - virtual ~IniLoader() = default; - explicit IniLoader(); - explicit IniLoader(wxConfigBase& config); - explicit IniLoader(wxConfigBase* config); - - bool IsLoading() const override { return true; } - - void Entry(const wxString& var, int& value, const int defvalue = 0) override; - void Entry(const wxString& var, uint& value, const uint defvalue = 0) override; - void Entry(const wxString& var, bool& value, const bool defvalue = false) override; - void Entry(const wxString& var, float& value, const float defvalue = 0.0) override; - void Entry(const wxString& var, std::string& value, const std::string& default_value = std::string()) override; - - bool EntryBitBool(const wxString& var, bool value, const bool defvalue = false) override; - int EntryBitfield(const wxString& var, int value, const int defvalue = 0) override; - - void Entry(const wxString& var, wxString& value, const wxString defvalue = wxEmptyString) override; - void Entry(const wxString& var, wxDirName& value, const wxDirName defvalue = wxDirName(), bool isAllowRelative = false) override; - void Entry(const wxString& var, wxFileName& value, const wxFileName defvalue = wxFileName(), bool isAllowRelative = false) override; - - void Entry(const wxString& var, wxPoint& value, const wxPoint defvalue = wxDefaultPosition) override; - void Entry(const wxString& var, wxSize& value, const wxSize defvalue = wxDefaultSize) override; - void Entry(const wxString& var, wxRect& value, const wxRect defvalue = wxDefaultRect) override; - - -protected: - void _EnumEntry(const wxString& var, int& value, const wxChar* const* enumArray, int defvalue) override; -}; - -// -------------------------------------------------------------------------------------- -// IniSaver -// -------------------------------------------------------------------------------------- -// Implementation of the IniInterface base class, which maps ini actions to saving to -// an ini dest file. -// -// See also: IniInterface -// -class IniSaver : public IniInterface -{ -public: - virtual ~IniSaver() = default; - explicit IniSaver(); - explicit IniSaver(wxConfigBase& config); - explicit IniSaver(wxConfigBase* config); - - bool IsLoading() const override { return false; } - - void Entry(const wxString& var, int& value, const int defvalue = 0) override; - void Entry(const wxString& var, uint& value, const uint defvalue = 0) override; - void Entry(const wxString& var, bool& value, const bool defvalue = false) override; - void Entry(const wxString& var, float& value, const float defvalue = 0.0) override; - void Entry(const wxString& var, std::string& value, const std::string& default_value = std::string()) override; - - bool EntryBitBool(const wxString& var, bool value, const bool defvalue = false) override; - int EntryBitfield(const wxString& var, int value, const int defvalue = 0) override; - - void Entry(const wxString& var, wxString& value, const wxString defvalue = wxString()) override; - void Entry(const wxString& var, wxDirName& value, const wxDirName defvalue = wxDirName(), bool isAllowRelative = false) override; - void Entry(const wxString& var, wxFileName& value, const wxFileName defvalue = wxFileName(), bool isAllowRelative = false) override; - - void Entry(const wxString& var, wxPoint& value, const wxPoint defvalue = wxDefaultPosition) override; - void Entry(const wxString& var, wxSize& value, const wxSize defvalue = wxDefaultSize) override; - void Entry(const wxString& var, wxRect& value, const wxRect defvalue = wxDefaultRect) override; - - -protected: - void _EnumEntry(const wxString& var, int& value, const wxChar* const* enumArray, int defvalue) override; -}; - -// ------------------------------------------------------------------------ -// GCC Note: wxT() macro is required when using string token pasting. For some reason L generates -// syntax errors. >_< -// -#define IniEntry(varname) ini.Entry(wxT(#varname), varname, varname) -#define IniEntryDirFile(varname, isAllowRelative) ini.Entry(wxT(#varname), varname, varname, isAllowRelative) -#define IniBitfield(varname) varname = ini.EntryBitfield(wxT(#varname), varname, varname) -#define IniBitBool(varname) varname = ini.EntryBitBool(wxT(#varname), !!varname, varname) - -#define IniBitfieldEx(varname, textname) varname = ini.EntryBitfield(wxT(textname), varname, varname) -#define IniBitBoolEx(varname, textname) varname = ini.EntryBitBool(wxT(textname), !!varname, varname) - -//used for filenames and folder names as ini values. -//Set to app root folder, so all files and folders which are inside appRoot will be written as relative. -void SetFullBaseDir(wxDirName appRoot); diff --git a/pcsx2/gui/IsoDropTarget.cpp b/pcsx2/gui/IsoDropTarget.cpp deleted file mode 100644 index 63c3b0b7d1..0000000000 --- a/pcsx2/gui/IsoDropTarget.cpp +++ /dev/null @@ -1,208 +0,0 @@ -/* PCSX2 - PS2 Emulator for PCs - * Copyright (C) 2002-2010 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 . - */ - -#include "PrecompiledHeader.h" -#include "IsoDropTarget.h" - -#include "Dialogs/ModalPopups.h" - -#include "common/StringUtil.h" -#include "CDVD/IsoFileFormats.h" - -#include - - -wxString GetMsg_ConfirmSysReset() -{ - return pxE( L"This action will reset the existing PS2 virtual machine state; all current progress will be lost. Are you sure?" - ); -} - -// -------------------------------------------------------------------------------------- -// DroppedTooManyFiles -// -------------------------------------------------------------------------------------- -class DroppedTooManyFiles : public pxActionEvent -{ -protected: - wxWindowID m_ownerid; - -public: - DroppedTooManyFiles( const wxWindow* window ) - : pxActionEvent() - , m_ownerid(window->GetId()) - { - } - - virtual ~DroppedTooManyFiles() = default; - virtual DroppedTooManyFiles *Clone() const { return new DroppedTooManyFiles(*this); } - -protected: - virtual void InvokeEvent() - { - ScopedCoreThreadPopup stopped_core; - - wxDialogWithHelpers dialog( wxWindow::FindWindowById(m_ownerid), _("Drag and Drop Error") ); - dialog += dialog.Heading(AddAppName(_("It is an error to drop multiple files onto a %s window. One at a time please, thank you."))); - pxIssueConfirmation( dialog, MsgButtons().Cancel() ); - } -}; - -// -------------------------------------------------------------------------------------- -// DroppedElf -// -------------------------------------------------------------------------------------- -class DroppedElf : public pxActionEvent -{ -protected: - wxWindowID m_ownerid; - -public: - DroppedElf( const wxWindow* window ) - : pxActionEvent() - , m_ownerid(window->GetId()) - { - } - - virtual ~DroppedElf() = default; - virtual DroppedElf *Clone() const { return new DroppedElf(*this); } - -protected: - virtual void InvokeEvent() - { - ScopedCoreThreadPopup stopped_core; - - bool confirmed = true; - if( SysHasValidState() ) - { - wxDialogWithHelpers dialog( wxWindow::FindWindowById(m_ownerid), _("Confirm PS2 Reset") ); - - dialog += dialog.Heading(AddAppName(_("You have dropped the following ELF binary into %s:\n\n"))); - dialog += dialog.GetCharHeight(); - dialog += dialog.Text( g_Conf->CurrentELF ); - dialog += dialog.GetCharHeight(); - dialog += dialog.Heading(GetMsg_ConfirmSysReset()); - - confirmed = (pxIssueConfirmation( dialog, MsgButtons().Reset().Cancel(), L"DragDrop.BootELF" ) != wxID_CANCEL); - } - - if( confirmed ) - { - g_Conf->EmuOptions.UseBOOT2Injection = true; - sApp.SysExecute( g_Conf->CdvdSource, g_Conf->CurrentELF ); - } - else - stopped_core.AllowResume(); - } -}; - -// -------------------------------------------------------------------------------------- -// DroppedIso -// -------------------------------------------------------------------------------------- -class DroppedIso : public pxActionEvent -{ -protected: - wxWindowID m_ownerid; - wxString m_filename; - -public: - DroppedIso( const wxWindow* window, const wxString& filename ) - : pxActionEvent() - , m_ownerid(window->GetId()) - , m_filename(filename) - { - } - - virtual ~DroppedIso() = default; - virtual DroppedIso *Clone() const { return new DroppedIso(*this); } - -protected: - virtual void InvokeEvent() - { - ScopedCoreThreadPopup stopped_core; - SwapOrReset_Iso(wxWindow::FindWindowById(m_ownerid), stopped_core, m_filename, - AddAppName(_("You have dropped the following ISO image into %s:")) - ); - } -}; - -bool IsoDropTarget::OnDropFiles(wxCoord x, wxCoord y, const wxArrayString& filenames) -{ - // WARNING: Doing *anything* from the context of OnDropFiles will result in Windows - // Explorer getting tied up waiting for a response from the application's message pump. - // So whenever possible, issue messages from this function only, such that the - // messages are processed later on after the application has allowed Explorer to resume - // itself. - // - // This most likely *includes* throwing exceptions, hence all exceptions here-in being - // caught and re-packaged as messages posted back to the application, so that the drag& - // drop procedure is free to release the Windows Explorer from the tyranny of drag&drop - // interplay. - - try - { - if( filenames.GetCount() > 1 ) - { - wxGetApp().AddIdleEvent( DroppedTooManyFiles(m_WindowBound) ); - return false; - } - - Console.WriteLn( "(Drag&Drop) Received filename: %ls", WX_STR(filenames[0]) ); - - // --------------- - // ELF CHECK - // --------------- - { - wxFileInputStream filechk( filenames[0] ); - - if( !filechk.IsOk() ) - throw Exception::CannotCreateStream(StringUtil::wxStringToUTF8String(filenames[0])); - - u8 ident[16]; - filechk.Read( ident, 16 ); - static const u8 elfIdent[4] = { 0x7f, 'E', 'L', 'F' }; - - if( ((u32&)ident) == ((u32&)elfIdent) ) - { - Console.WriteLn( "(Drag&Drop) Found ELF file type!" ); - - g_Conf->CurrentELF = filenames[0]; - - wxGetApp().PostEvent( DroppedElf(m_WindowBound) ); - return true; - } - } - - // --------------- - // ISO CHECK - // --------------- - - InputIsoFile iso; - - if (iso.Test( StringUtil::wxStringToUTF8String(filenames[0]) )) - { - DevCon.WriteLn( "(Drag&Drop) Found valid ISO file type!" ); - wxGetApp().PostEvent( DroppedIso(m_WindowBound, filenames[0]) ); - return true; - } - } - catch (BaseException& ex) - { - wxGetApp().AddIdleEvent( pxExceptionEvent(ex) ); - } - catch (std::runtime_error& ex) - { - wxGetApp().AddIdleEvent( pxExceptionEvent(Exception::RuntimeError(ex)) ); - } - return false; -} diff --git a/pcsx2/gui/IsoDropTarget.h b/pcsx2/gui/IsoDropTarget.h deleted file mode 100644 index bc51f00d9f..0000000000 --- a/pcsx2/gui/IsoDropTarget.h +++ /dev/null @@ -1,41 +0,0 @@ -/* PCSX2 - PS2 Emulator for PCs - * Copyright (C) 2002-2010 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 . - */ - -#pragma once - -#include "AppCommon.h" -#include - -// -------------------------------------------------------------------------------------- -// IsoDropTarget -// -------------------------------------------------------------------------------------- -class IsoDropTarget : public wxFileDropTarget -{ -protected: - wxWindow* m_WindowBound; - -public: - virtual ~IsoDropTarget() = default; - IsoDropTarget( wxWindow* parent ) : wxFileDropTarget() - { - m_WindowBound = parent; - } - - virtual bool OnDropFiles(wxCoord x, wxCoord y, const wxArrayString& filenames); -}; - - -extern wxString GetMsg_ConfirmSysReset(); -extern wxWindowID SwapOrReset_Iso( wxWindow* owner, IScopedCoreThread& core_control, const wxString& isoFilename, const wxString& descpart1 ); diff --git a/pcsx2/gui/MSWstuff.cpp b/pcsx2/gui/MSWstuff.cpp deleted file mode 100644 index cb80736c78..0000000000 --- a/pcsx2/gui/MSWstuff.cpp +++ /dev/null @@ -1,44 +0,0 @@ -/* PCSX2 - PS2 Emulator for PCs - * Copyright (C) 2002-2010 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 . - */ - -#include "PrecompiledHeader.h" - -#include "MSWstuff.h" - -#ifdef __WXMSW__ -# include // needed for OutputDebugString -#endif - -void MSW_SetWindowAfter( WXWidget hwnd, WXWidget hwndAfter ) -{ -#ifdef __WXMSW__ - SetWindowPos( (HWND)hwnd, (HWND)hwndAfter, 0, 0, 0, 0, SWP_NOACTIVATE | SWP_NOSIZE | SWP_NOMOVE ); -#endif -} - -// Text scales automatically on Windows but that's about it. The dialog widths -// and images need to be scaled manually. -float MSW_GetDPIScale() -{ -#ifdef __WXMSW__ - HDC screen = GetDC(0); - float scale = GetDeviceCaps(screen, LOGPIXELSX) / 96.0; // 96.0 dpi = 100% scale - ReleaseDC(NULL, screen); - - return scale; -#else - return 1.0; -#endif -} diff --git a/pcsx2/gui/MSWstuff.h b/pcsx2/gui/MSWstuff.h deleted file mode 100644 index 4779c37650..0000000000 --- a/pcsx2/gui/MSWstuff.h +++ /dev/null @@ -1,21 +0,0 @@ -/* PCSX2 - PS2 Emulator for PCs - * Copyright (C) 2002-2010 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 . - */ - -#pragma once - -// FIXME: Missing some includes. -extern void MSW_SetWindowAfter( WXWidget hwnd, WXWidget hwndAfter ); -extern void MSW_OutputDebugString( const wxString& text ); -extern float MSW_GetDPIScale(); diff --git a/pcsx2/gui/MainFrame.cpp b/pcsx2/gui/MainFrame.cpp deleted file mode 100644 index a75fa85f5c..0000000000 --- a/pcsx2/gui/MainFrame.cpp +++ /dev/null @@ -1,819 +0,0 @@ -/* PCSX2 - PS2 Emulator for PCs - * Copyright (C) 2002-2021 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 . - */ - -#include "PrecompiledHeader.h" -#include "MainFrame.h" -#include "AppSaveStates.h" -#include "ConsoleLogger.h" -#include "MSWstuff.h" - -#include "Dialogs/ModalPopups.h" -#include "IsoDropTarget.h" - -#include "fmt/core.h" -#include - -#include -#include "AppAccelerators.h" - -#include "svnrev.h" -#include "Saveslots.h" - -#include "Recording/InputRecording.h" -#include "Recording/InputRecordingControls.h" - -// ------------------------------------------------------------------------ -wxMenu* MainEmuFrame::MakeStatesSubMenu(int baseid, int loadBackupId) const -{ - wxMenu* mnuSubstates = new wxMenu(); - - for (int i = 0; i < 10; i++) - { - // Will be changed once an iso is loaded. - wxMenuItem* m = mnuSubstates->Append(baseid + i + 1, wxsFormat(_("Slot %d"), i), wxEmptyString, wxITEM_CHECK); - m->Check(i == 0); // 0 is the default selected slot - } - - if (loadBackupId >= 0) - { - mnuSubstates->AppendSeparator(); - - wxMenuItem* m = mnuSubstates->Append(loadBackupId, _("Backup")); - m->Enable(false); - } - // Implement custom hotkeys (F2) + (Shift + F2) with translatable string intact + not blank in GUI. - // baseid in the negatives will order in a different section, so if you want to increase more slots you can still easily do this, as -1 it will have the same function as opening file for savestates, which is bad - // For safety i also made them inactive aka grayed out to signify that's it's only for informational purposes - // Fixme: In the future this can still be expanded to actually cycle savestates in the GUI. - mnuSubstates->Append(baseid - 1, _("File...")); - wxMenuItem* CycleNext = mnuSubstates->Append(baseid - 2, _("Cycle to next slot") + wxString("\t") + wxGetApp().GlobalAccels->findKeycodeWithCommandId("States_CycleSlotForward").toTitleizedString()); - CycleNext->Enable(false); - wxMenuItem* CycleBack = mnuSubstates->Append(baseid - 3, _("Cycle to previous slot") + wxString("\t") + wxGetApp().GlobalAccels->findKeycodeWithCommandId("States_CycleSlotBackward").toTitleizedString()); - CycleBack->Enable(false); - return mnuSubstates; -} - -void MainEmuFrame::UpdateStatusBar() -{ - wxString temp(wxEmptyString); - - if (g_Conf->EnableFastBoot) - temp += "Fast Boot - "; - - if (g_Conf->CdvdSource == CDVD_SourceType::Iso) - temp += "Load: '" + wxFileName(g_Conf->CurrentIso).GetFullName() + "' "; - - m_statusbar.SetStatusText(temp, 0); - - if (g_Conf->EnablePresets) - m_statusbar.SetStatusText(wxString::Format(L"P:%d", g_Conf->PresetIndex + 1), 1); - else - m_statusbar.SetStatusText("---", 1); - - m_statusbar.SetStatusText(CDVD_SourceLabels[enum_cast(g_Conf->CdvdSource)], 2); - - m_statusbar.SetStatusText("x64", 3); -} - -void MainEmuFrame::UpdateCdvdSrcSelection() -{ - MenuIdentifiers cdsrc = MenuId_Src_Iso; - - switch (g_Conf->CdvdSource) - { - case CDVD_SourceType::Iso: - cdsrc = MenuId_Src_Iso; - break; - case CDVD_SourceType::Disc: - cdsrc = MenuId_Src_Disc; - break; - case CDVD_SourceType::NoDisc: - cdsrc = MenuId_Src_NoDisc; - break; - - jNO_DEFAULT - } - sMenuBar.Check(cdsrc, true); - ApplyCDVDStatus(); - UpdateStatusBar(); -} - -bool MainEmuFrame::Destroy() -{ - // Sigh: wxWidgets doesn't issue Destroy() calls for children windows when the parent - // is destroyed (it just deletes them, quite suddenly). So let's do it for them, since - // our children have configuration stuff they like to do when they're closing. - - for ( - wxWindowList::const_iterator - i = wxTopLevelWindows.begin(), - end = wxTopLevelWindows.end(); - i != end; ++i) - { - wxTopLevelWindow* const win = wx_static_cast(wxTopLevelWindow*, *i); - if (win == this) - continue; - if (win->GetParent() != this) - continue; - - win->Destroy(); - } - - return _parent::Destroy(); -} - -// ------------------------------------------------------------------------ -// MainFrame OnEvent Handlers -// ------------------------------------------------------------------------ - -// Close out the console log windows along with the main emu window. -// Note: This event only happens after a close event has occurred and was *not* veto'd. Ie, -// it means it's time to provide an unconditional closure of said window. -// -void MainEmuFrame::OnCloseWindow(wxCloseEvent& evt) -{ - // the main thread is busy suspending everything, so let's not try to call it - // when closing the emulator - //init_gspanel = false; - - if (IsBeingDeleted()) - return; - - CoreThread.Suspend(); - - //bool isClosing = false; - - if (!evt.CanVeto()) - { - // Mandatory destruction... - //isClosing = true; - } - else - { - // TODO : Add confirmation prior to exit here! - // Problem: Suspend is often slow because it needs to wait until the current EE frame - // has finished processing (if the GS or logging has incurred severe overhead this makes - // closing PCSX2 difficult). A non-blocking suspend with modal dialog might suffice - // however. --air - - //evt.Veto( true ); - } - - sApp.OnMainFrameClosed(GetId()); - - RemoveCdvdMenu(); - - RemoveEventHandler(&wxGetApp().GetRecentIsoManager()); - wxGetApp().PostIdleAppMethod(&Pcsx2App::PrepForExit); - - evt.Skip(); -} - -void MainEmuFrame::OnMoveAround(wxMoveEvent& evt) -{ - if (IsBeingDeleted() || !IsVisible() || IsIconized()) - return; - - // Uncomment this when doing logger stress testing (and then move the window around - // while the logger spams itself) - // ... makes for a good test of the message pump's responsiveness. - if (EnableThreadedLoggingTest) - Console.Warning("Threaded Logging Test! (a window move event)"); - - // evt.GetPosition() returns the client area position, not the window frame position. - // So read the window's screen-relative position directly. - g_Conf->MainGuiPosition = GetScreenPosition(); - - // wxGTK note: X sends gratuitous amounts of OnMove messages for various crap actions - // like selecting or deselecting a window, which muck up docking logic. We filter them - // out using 'lastpos' here. :) - - static wxPoint lastpos(wxDefaultCoord, wxDefaultCoord); - if (lastpos == evt.GetPosition()) - return; - lastpos = evt.GetPosition(); - - if (g_Conf->ProgLogBox.AutoDock) - { - if (ConsoleLogFrame* proglog = wxGetApp().GetProgramLog()) - { - if (!proglog->IsMaximized()) - { - g_Conf->ProgLogBox.DisplayPosition = GetRect().GetTopRight(); - proglog->SetPosition(g_Conf->ProgLogBox.DisplayPosition); - } - } - } - - evt.Skip(); -} - -void MainEmuFrame::OnLogBoxHidden() -{ - g_Conf->ProgLogBox.Visible = false; - m_MenuItem_Console.Check(false); -} - -// ------------------------------------------------------------------------ -void MainEmuFrame::ConnectMenus() -{ - // System - Bind(wxEVT_MENU, &MainEmuFrame::Menu_BootCdvd_Click, this, MenuId_Boot_CDVD); - Bind(wxEVT_MENU, &MainEmuFrame::Menu_FastBoot_Click, this, MenuId_Config_FastBoot); - Bind(wxEVT_MENU, &MainEmuFrame::Menu_OpenELF_Click, this, MenuId_Boot_ELF); - Bind(wxEVT_MENU, &MainEmuFrame::Menu_SuspendResume_Click, this, MenuId_Sys_SuspendResume); - - Bind(wxEVT_MENU, &MainEmuFrame::Menu_LoadStates_Click, this, MenuId_State_Load01 + 1, MenuId_State_Load01 + 10); - Bind(wxEVT_MENU, &MainEmuFrame::Menu_LoadStates_Click, this, MenuId_State_LoadBackup); - Bind(wxEVT_MENU, &MainEmuFrame::Menu_LoadStateFromFile_Click, this, MenuId_State_LoadFromFile); - Bind(wxEVT_MENU, &MainEmuFrame::Menu_SaveStates_Click, this, MenuId_State_Save01 + 1, MenuId_State_Save01 + 10); - Bind(wxEVT_MENU, &MainEmuFrame::Menu_SaveStateToFile_Click, this, MenuId_State_SaveToFile); - Bind(wxEVT_MENU, &MainEmuFrame::Menu_EnableBackupStates_Click, this, MenuId_EnableBackupStates); - - Bind(wxEVT_MENU, &MainEmuFrame::Menu_EnablePatches_Click, this, MenuId_EnablePatches); - Bind(wxEVT_MENU, &MainEmuFrame::Menu_EnableCheats_Click, this, MenuId_EnableCheats); - Bind(wxEVT_MENU, &MainEmuFrame::Menu_PINE_Enable_Click, this, MenuId_PINE_Enable); - Bind(wxEVT_MENU, &MainEmuFrame::Menu_PINE_Settings_Click, this, MenuId_PINE_Settings); - Bind(wxEVT_MENU, &MainEmuFrame::Menu_EnableWideScreenPatches_Click, this, MenuId_EnableWideScreenPatches); - Bind(wxEVT_MENU, &MainEmuFrame::Menu_EnableRecordingTools_Click, this, MenuId_EnableInputRecording); - Bind(wxEVT_MENU, &MainEmuFrame::Menu_EnableHostFs_Click, this, MenuId_EnableHostFs); - Bind(wxEVT_MENU, &MainEmuFrame::Menu_SysShutdown_Click, this, MenuId_Sys_Shutdown); - Bind(wxEVT_MENU, &MainEmuFrame::Menu_Exit_Click, this, MenuId_Exit); - - // CDVD - Bind(wxEVT_MENU, &MainEmuFrame::Menu_IsoBrowse_Click, this, MenuId_IsoBrowse); - Bind(wxEVT_MENU, &MainEmuFrame::Menu_IsoClear_Click, this, MenuId_IsoClear); - Bind(wxEVT_MENU, &MainEmuFrame::Menu_IsoClearMissing_Click, this, MenuId_IsoClearMissing); - Bind(wxEVT_MENU, &MainEmuFrame::Menu_CdvdSource_Click, this, MenuId_Src_Iso); - Bind(wxEVT_MENU, &MainEmuFrame::Menu_CdvdSource_Click, this, MenuId_Src_Disc); - Bind(wxEVT_MENU, &MainEmuFrame::Menu_CdvdSource_Click, this, MenuId_Src_NoDisc); - Bind(wxEVT_MENU, &MainEmuFrame::Menu_Ask_On_Boot_Click, this, MenuId_Ask_On_Booting); - Bind(wxEVT_MENU, &MainEmuFrame::Menu_Debug_CreateBlockdump_Click, this, MenuId_Debug_CreateBlockdump); - - // Config - Bind(wxEVT_MENU, &MainEmuFrame::Menu_SysSettings_Click, this, MenuId_Config_SysSettings); - Bind(wxEVT_MENU, &MainEmuFrame::Menu_McdSettings_Click, this, MenuId_Config_McdSettings); - Bind(wxEVT_MENU, &MainEmuFrame::Menu_AudioSettings_Click, this, MenuId_Config_SPU2); - Bind(wxEVT_MENU, &MainEmuFrame::Menu_NetworkSettings_Click, this, MenuId_Config_DEV9); - Bind(wxEVT_MENU, &MainEmuFrame::Menu_USBSettings_Click, this, MenuId_Config_USB); - Bind(wxEVT_MENU, &MainEmuFrame::Menu_PADSettings_Click, this, MenuId_Config_PAD); - Bind(wxEVT_MENU, &MainEmuFrame::Menu_GSSettings_Click, this, MenuId_Config_GS); - - Bind(wxEVT_MENU, &MainEmuFrame::Menu_GSSettings_Click, this, MenuId_Video_CoreSettings); - Bind(wxEVT_MENU, &MainEmuFrame::Menu_WindowSettings_Click, this, MenuId_Video_WindowSettings); - - Bind(wxEVT_MENU, &MainEmuFrame::Menu_MultitapToggle_Click, this, MenuId_Config_Multitap0Toggle); - Bind(wxEVT_MENU, &MainEmuFrame::Menu_MultitapToggle_Click, this, MenuId_Config_Multitap1Toggle); - Bind(wxEVT_MENU, &MainEmuFrame::Menu_ResetAllSettings_Click, this, MenuId_Config_ResetAll); - - // Misc - Bind(wxEVT_MENU, &MainEmuFrame::Menu_ShowConsole, this, MenuId_Console); -#if defined(__POSIX__) - Bind(wxEVT_MENU, &MainEmuFrame::Menu_ShowConsole_Stdio, this, MenuId_Console_Stdio); -#endif - - Bind(wxEVT_MENU, &MainEmuFrame::Menu_GetStarted, this, MenuId_Help_GetStarted); - Bind(wxEVT_MENU, &MainEmuFrame::Menu_Compatibility, this, MenuId_Help_Compatibility); - Bind(wxEVT_MENU, &MainEmuFrame::Menu_Forums, this, MenuId_Help_Forums); - Bind(wxEVT_MENU, &MainEmuFrame::Menu_Website, this, MenuId_Help_Website); - Bind(wxEVT_MENU, &MainEmuFrame::Menu_Github, this, MenuId_Help_Github); - Bind(wxEVT_MENU, &MainEmuFrame::Menu_Wiki, this, MenuId_Help_Wiki); - Bind(wxEVT_MENU, &MainEmuFrame::Menu_ShowAboutBox, this, MenuId_About); - Bind(wxEVT_MENU, &MainEmuFrame::Menu_ShowGSDump, this, MenuId_GSDump); - Bind(wxEVT_MENU, &MainEmuFrame::Menu_ChangeLang, this, MenuId_ChangeLang); - - // Debug - Bind(wxEVT_MENU, &MainEmuFrame::Menu_Debug_Open_Click, this, MenuId_Debug_Open); - - // Capture - Bind(wxEVT_MENU, &MainEmuFrame::Menu_Capture_Video_ToggleCapture_Click, this, MenuId_Capture_Video_Record); - Bind(wxEVT_MENU, &MainEmuFrame::Menu_Capture_Video_ToggleCapture_Click, this, MenuId_Capture_Video_Stop); - Bind(wxEVT_MENU, &MainEmuFrame::Menu_Capture_Video_IncludeAudio_Click, this, MenuId_Capture_Video_IncludeAudio); - Bind(wxEVT_MENU, &MainEmuFrame::Menu_Capture_Screenshot_Screenshot_Click, this, MenuId_Capture_Screenshot_Screenshot); - Bind(wxEVT_MENU, &MainEmuFrame::Menu_Capture_Screenshot_Screenshot_As_Click, this, MenuId_Capture_Screenshot_Screenshot_As); -} - -void MainEmuFrame::InitLogBoxPosition(AppConfig::ConsoleLogOptions& conf) -{ - conf.DisplaySize.Set( - std::min(std::max(conf.DisplaySize.GetWidth(), 160), wxGetDisplayArea().GetWidth()), - std::min(std::max(conf.DisplaySize.GetHeight(), 160), wxGetDisplayArea().GetHeight())); - - if (conf.AutoDock) - { - conf.DisplayPosition = GetScreenPosition() + wxSize(GetSize().x, 0); - } - else if (conf.DisplayPosition != wxDefaultPosition) - { - if (!wxGetDisplayArea().Contains(wxRect(conf.DisplayPosition, conf.DisplaySize))) - conf.DisplayPosition = wxDefaultPosition; - } -} - -void MainEmuFrame::DispatchEvent(const CoreThreadStatus& status) -{ - if (!pxAssertMsg(GetMenuBar() != NULL, "Mainframe menu bar is NULL!")) - return; - ApplySuspendStatus(); -} - -void MainEmuFrame::AppStatusEvent_OnSettingsApplied() -{ - ApplySettings(); -} - -void MainEmuFrame::CreatePcsx2Menu() -{ - // ------------------------------------------------------------------------ - // Some of the items in the System menu are configured by the UpdateCoreStatus() method. - - m_menuSys.Append(MenuId_Boot_CDVD, _("Initializing...")); - - m_menuSys.Append(MenuId_Sys_SuspendResume, _("Initializing...")); - - m_menuSys.Append(MenuId_Sys_Shutdown, _("Shut&down"), - _("Wipes all internal VM states.")); - m_menuSys.FindItem(MenuId_Sys_Shutdown)->Enable(false); - - m_menuSys.Append(MenuId_Boot_ELF, _("&Run ELF..."), - _("For running raw PS2 binaries directly.")); - - m_menuSys.AppendSeparator(); - - m_menuSys.Append(MenuId_Config_FastBoot, _("Fast Boot"), - _("Skips PS2 splash screens when booting from ISO or DVD media"), wxITEM_CHECK); - - m_menuSys.Append(MenuId_GameSettingsSubMenu, _("&Game Settings"), &m_GameSettingsSubmenu); - - m_GameSettingsSubmenu.Append(MenuId_EnablePatches, _("Automatic &Gamefixes"), - _("Automatically applies needed Gamefixes to known problematic games."), wxITEM_CHECK); - - m_GameSettingsSubmenu.Append(MenuId_EnableCheats, _("Enable &Cheats"), - _("Use cheats otherwise known as pnachs from the cheats folder."), wxITEM_CHECK); - - m_GameSettingsSubmenu.Append(MenuId_PINE, _("Configure &PINE"), &m_submenuPINE); - - m_submenuPINE.Append(MenuId_PINE_Enable, _("&Enable PINE"), - wxEmptyString, wxITEM_CHECK); - - m_submenuPINE.Append(MenuId_PINE_Settings, _("PINE &Settings")); - - m_GameSettingsSubmenu.Append(MenuId_EnableWideScreenPatches, _("Enable &Widescreen Patches"), - _("Enabling Widescreen Patches may occasionally cause issues."), wxITEM_CHECK); - - m_GameSettingsSubmenu.Append(MenuId_EnableInputRecording, _("Enable &Input Recording"), - _("Input Recording for controller/keyboard presses, tools for automation and playback."), wxITEM_NORMAL); - - m_GameSettingsSubmenu.Append(MenuId_EnableHostFs, _("Enable &Host Filesystem"), - wxEmptyString, wxITEM_CHECK); - - m_menuSys.AppendSeparator(); - // Implement custom hotkeys (F3) with translatable string intact + not blank in GUI. - wxMenuItem* sysLoadStateItem = m_menuSys.Append(MenuId_Sys_LoadStates, _("&Load state"), &m_LoadStatesSubmenu); - AppendShortcutToMenuOption(*sysLoadStateItem, wxGetApp().GlobalAccels->findKeycodeWithCommandId("States_DefrostCurrentSlot").toTitleizedString()); - // Implement custom hotkeys (F1) with translatable string intact + not blank in GUI. - wxMenuItem* sysSaveStateItem = m_menuSys.Append(MenuId_Sys_SaveStates, _("&Save state"), &m_SaveStatesSubmenu); - AppendShortcutToMenuOption(*sysSaveStateItem, wxGetApp().GlobalAccels->findKeycodeWithCommandId("States_FreezeCurrentSlot").toTitleizedString()); - - m_menuSys.Append(MenuId_EnableBackupStates, _("&Backup before save"), wxEmptyString, wxITEM_CHECK); - - m_menuSys.AppendSeparator(); - - m_menuSys.Append(MenuId_Exit, _("E&xit"), - AddAppName(_("Closing %s may be hazardous to your health."))); -} - -void MainEmuFrame::CreateCdvdMenu() -{ - // ------------------------------------------------------------------------ - wxMenu& isoRecents(wxGetApp().GetRecentIsoMenu()); - wxMenu& driveList(wxGetApp().GetDriveListMenu()); - - m_menuItem_RecentIsoMenu = m_menuCDVD.AppendSubMenu(&isoRecents, _("ISO &Selector")); - m_menuItem_DriveListMenu = m_menuCDVD.AppendSubMenu(&driveList, _("D&rive Selector")); - - m_menuCDVD.AppendSeparator(); - m_menuCDVD.Append(MenuId_Src_Iso, _("&ISO"), _("Makes the specified ISO image the CDVD source."), wxITEM_RADIO); - m_menuCDVD.Append(MenuId_Src_Disc, _("&Disc"), _("Uses a disc drive as the CDVD source."), wxITEM_RADIO); - m_menuCDVD.Append(MenuId_Src_NoDisc, _("&No disc"), _("Use this to boot into your virtual PS2's BIOS configuration."), wxITEM_RADIO); - -#if defined(__FREEBSD__) || defined(__APPLE__) - m_menuItem_DriveListMenu->Enable(false); - m_menuCDVD.Enable(MenuId_Src_Disc, false); -#endif -} - - -void MainEmuFrame::CreateConfigMenu() -{ - m_menuConfig.Append(MenuId_Config_SysSettings, _("General &Settings")); - m_menuConfig.Append(MenuId_Config_McdSettings, _("&Memory Cards")); - m_menuConfig.AppendSeparator(); - m_menuConfig.Append(MenuId_Config_GS, _("&Graphics Settings")); - m_menuConfig.Append(MenuId_Config_SPU2, _("&Audio Settings")); - m_menuConfig.Append(MenuId_Config_PAD, _("Game&pad Settings")); - m_menuConfig.Append(MenuId_Config_DEV9, _("&Network and HDD Settings")); - m_menuConfig.Append(MenuId_Config_USB, _("&USB Settings")); - m_menuConfig.AppendSeparator(); - - m_menuConfig.Append(MenuId_Config_Multitap0Toggle, _("Multitap &1"), wxEmptyString, wxITEM_CHECK); - m_menuConfig.Append(MenuId_Config_Multitap1Toggle, _("Multitap &2"), wxEmptyString, wxITEM_CHECK); - - m_menuConfig.AppendSeparator(); - - m_menuConfig.Append(MenuId_ChangeLang, L"Change &Language"); // Always in English - m_menuConfig.Append(MenuId_Config_ResetAll, _("&Clear All Settings"), - AddAppName(_("Clears all %s settings and re-runs the startup wizard."))); -} - -void MainEmuFrame::CreateWindowsMenu() -{ - m_menuWindow.Append(MenuId_Debug_CreateBlockdump, _("Create &Blockdump"), _("Creates a block dump for debugging purposes."), wxITEM_CHECK); - m_menuWindow.Append(MenuId_Debug_Open, _("&Show Debugger"), wxEmptyString, wxITEM_CHECK); - -#ifndef PCSX2_CI - if (IsDevBuild || g_Conf->DevMode) -#endif - m_menuWindow.Append(MenuId_GSDump, _("Show &GS Debugger")); - - m_menuWindow.Append(&m_MenuItem_Console); -#if defined(__POSIX__) - m_menuWindow.AppendSeparator(); - m_menuWindow.Append(&m_MenuItem_Console_Stdio); -#endif -} - -void MainEmuFrame::CreateCaptureMenu() -{ - m_menuCapture.Append(MenuId_Capture_Video, _("Video"), &m_submenuVideoCapture); - // Implement custom hotkeys (F12) with translatable string intact + not blank in GUI. - wxMenuItem* sysVideoCaptureItem = m_submenuVideoCapture.Append(MenuId_Capture_Video_Record, _("Start Video Capture")); - AppendShortcutToMenuOption(*sysVideoCaptureItem, wxGetApp().GlobalAccels->findKeycodeWithCommandId("Sys_RecordingToggle").toTitleizedString()); - sysVideoCaptureItem = m_submenuVideoCapture.Append(MenuId_Capture_Video_Stop, _("Stop Video Capture")); - sysVideoCaptureItem->Enable(false); - AppendShortcutToMenuOption(*sysVideoCaptureItem, wxGetApp().GlobalAccels->findKeycodeWithCommandId("Sys_RecordingToggle").toTitleizedString()); - m_submenuVideoCapture.AppendSeparator(); - m_submenuVideoCapture.Append(MenuId_Capture_Video_IncludeAudio, _("Include Audio"), - _("Enables/disables the creation of a synchronized wav audio file when capturing video footage."), wxITEM_CHECK); - // Implement custom hotkeys (F8) + (Shift + F8) + (Ctrl + Shift + F8) with translatable string intact + not blank in GUI. - // Fixme: GlobalCommands.cpp L1029-L1031 is having issues because FrameForGS already maps the hotkey first. - // Fixme: When you uncomment L1029-L1031 on that file; Linux says that Ctrl is already used for something else and will append (Shift + F8) while Windows will (Ctrl + Shift + F8) - m_menuCapture.Append(MenuId_Capture_Screenshot, _("Screenshot"), &m_submenuScreenshot); - wxMenuItem* sysScreenShotItem = m_submenuScreenshot.Append(MenuId_Capture_Screenshot_Screenshot, _("Take Screenshot")); - // HACK: in AcceleratorDictionary::Map the Sys_TakeSnapshot entry gets Shift and Cmd (Ctrl) hardcoded to it because it is similarly hardcoded in GS - // So... remove such modifiers as the GUI menu entry is only for the base keybinding without modifiers. - // We can be confident in doing so, as if a user adds these modifiers themselves, the same function rejects it. - KeyAcceleratorCode keyCode = wxGetApp().GlobalAccels->findKeycodeWithCommandId("Sys_TakeSnapshot"); - keyCode.Shift(false); - keyCode.Cmd(false); - AppendShortcutToMenuOption(*sysScreenShotItem, keyCode.toTitleizedString()); - m_submenuScreenshot.Append(MenuId_Capture_Screenshot_Screenshot_As, _("Save Screenshot As...")); -} - -void MainEmuFrame::CreateInputRecordingMenu() -{ - m_menuRecording.Append(MenuId_Recording_New, _("New"), _("Create a new input recording."))->Enable(false); - m_menuRecording.Append(MenuId_Recording_Stop, _("Stop"), _("Stop the active input recording."))->Enable(false); - m_menuRecording.Append(MenuId_Recording_Play, _("Play"), _("Playback an existing input recording."))->Enable(false); - m_menuRecording.AppendSeparator(); - - m_menuRecording.Append(MenuId_Recording_Settings, _("Settings"), &m_submenu_recording_settings); - wxString frame_advance_label = wxString(_("Configure Frame Advance")); - frame_advance_label.Append(wxString::Format(" (%d)", g_Conf->inputRecording.m_frame_advance_amount)); - m_submenu_recording_settings.Append(MenuId_Recording_Config_FrameAdvance, frame_advance_label, _("Change the amount of frames advanced each time")); - m_menuRecording.AppendSeparator(); - - m_menuRecording.Append(MenuId_Recording_TogglePause, _("Toggle Pause"), _("Pause or resume emulation on the fly."))->Enable(false); - m_menuRecording.Append(MenuId_Recording_FrameAdvance, _("Frame Advance"), _("Advance emulation forward by a single frame at a time."))->Enable(false); - m_menuRecording.Append(MenuId_Recording_ToggleRecordingMode, _("Toggle Recording Mode"), _("Save/playback inputs to/from the recording file."))->Enable(false); - m_menuRecording.AppendSeparator(); - - m_menuRecording.Append(MenuId_Recording_VirtualPad_Port0, _("Virtual Pad (Port 1)")); - m_menuRecording.Append(MenuId_Recording_VirtualPad_Port1, _("Virtual Pad (Port 2)")); -} - -void MainEmuFrame::CreateHelpMenu() -{ - m_menuHelp.Append(MenuId_Help_GetStarted, _("&Getting Started")); - m_menuHelp.Append(MenuId_Help_Compatibility, _("&Compatibility")); - m_menuHelp.AppendSeparator(); - m_menuHelp.Append(MenuId_Help_Website, _("&Website")); - m_menuHelp.Append(MenuId_Help_Wiki, _("&Wiki")); - m_menuHelp.Append(MenuId_Help_Forums, _("&Support Forums")); - m_menuHelp.Append(MenuId_Help_Github, _("&GitHub Repository")); - m_menuHelp.AppendSeparator(); - m_menuHelp.Append(MenuId_About, _("&About")); -} - -// ------------------------------------------------------------------------ -MainEmuFrame::MainEmuFrame(wxWindow* parent, const wxString& title) - : wxFrame(parent, wxID_ANY, title, wxDefaultPosition, wxDefaultSize, wxDEFAULT_FRAME_STYLE & ~(wxMAXIMIZE_BOX | wxRESIZE_BORDER)) - - , m_statusbar(*CreateStatusBar(2, 0)) - , m_background(new wxStaticBitmap(this, wxID_ANY, wxGetApp().GetLogoBitmap())) - - // All menu components must be created on the heap! - - , m_menubar(*new wxMenuBar()) - - , m_menuCDVD(*new wxMenu()) - , m_menuSys(*new wxMenu()) - , m_menuConfig(*new wxMenu()) - , m_menuWindow(*new wxMenu()) - , m_menuCapture(*new wxMenu()) - , m_submenuVideoCapture(*new wxMenu()) - , m_submenuPINE(*new wxMenu()) - , m_submenuScreenshot(*new wxMenu()) - , m_menuRecording(*new wxMenu()) - , m_submenu_recording_settings(*new wxMenu()) - , m_menuHelp(*new wxMenu()) - , m_LoadStatesSubmenu(*MakeStatesSubMenu(MenuId_State_Load01, MenuId_State_LoadBackup)) - , m_SaveStatesSubmenu(*MakeStatesSubMenu(MenuId_State_Save01)) - , m_GameSettingsSubmenu(*new wxMenu()) - - , m_MenuItem_Console(*new wxMenuItem(&m_menuWindow, MenuId_Console, _("&Show Program Log"), wxEmptyString, wxITEM_CHECK)) -#if defined(__POSIX__) - , m_MenuItem_Console_Stdio(*new wxMenuItem(&m_menuWindow, MenuId_Console_Stdio, _("&Program Log to Stdio"), wxEmptyString, wxITEM_CHECK)) -#endif - -{ - m_RestartEmuOnDelete = false; - m_capturingVideo = false; - - // ------------------------------------------------------------------------ - // Initial menubar setup. This needs to be done first so that the menu bar's visible size - // can be factored into the window size (which ends up being background+status+menus) - - m_menubar.Append(&m_menuSys, _("&System")); - m_menubar.Append(&m_menuCDVD, _("CD&VD")); - m_menubar.Append(&m_menuConfig, _("&Config")); - m_menubar.Append(&m_menuWindow, _("&Debug")); - m_menubar.Append(&m_menuCapture, _("Captu&re")); - - SetMenuBar(&m_menubar); - - m_menubar.Append(&m_menuHelp, _("&Help")); - - // ------------------------------------------------------------------------ - - // The background logo and its window size are different on Windows. Use the - // background logo size, which is what it'll eventually be resized to. - wxSize backsize(m_background->GetBitmap().GetWidth(), m_background->GetBitmap().GetHeight()); - - wxString wintitle; - if (PCSX2_isReleaseVersion) - { - // stable releases, with a simple title. - wintitle.Printf(L"%s %d.%d.%d", pxGetAppName().c_str(), PCSX2_VersionHi, PCSX2_VersionMid, PCSX2_VersionLo); - } - else if (GIT_TAGGED_COMMIT) // Nightly builds - { - // tagged commit - more modern implementation of dev build versioning - // - there is no need to include the commit - that is associated with the tag, git is implied - wintitle.Printf(L"%s Nightly - %s", pxGetAppName().c_str(), GIT_TAG); - } - else - { - // beta / development editions, which feature revision number and compile date. - if (strlen(GIT_REV) > 5) - { - wintitle.Printf(L"%s %s", pxGetAppName().c_str(), GIT_REV); - } - else - { - wintitle.Printf(L"%s %d.%d.%d-%lld (git)", pxGetAppName().c_str(), PCSX2_VersionHi, PCSX2_VersionMid, - PCSX2_VersionLo, SVN_REV); - } - } - - SetTitle(wintitle); - - // Ideally the __WXMSW__ port should use the embedded IDI_ICON2 icon, because wxWidgets sucks and - // loses the transparency information when loading bitmaps into icons. But for some reason - // I cannot get it to work despite following various examples to the letter. - SetIcons(wxGetApp().GetIconBundle()); - - int m_statusbar_widths[] = {(int)-20, (int)-3, (int)-3, (int)-3}; - m_statusbar.SetFieldsCount(4); - m_statusbar.SetStatusWidths(4, m_statusbar_widths); - m_statusbar.SetStatusText(wxEmptyString, 0); - - wxBoxSizer& joe(*new wxBoxSizer(wxVERTICAL)); - joe.Add(m_background); - SetSizerAndFit(&joe); - - // Makes no sense, but this is needed for the window size to be correct for - // 200% DPI on Windows. The SetSizerAndFit is supposed to be doing the exact - // same thing. - GetSizer()->SetSizeHints(this); - - // Use default window position if the configured windowpos is invalid (partially offscreen) - if (g_Conf->MainGuiPosition == wxDefaultPosition || !pxIsValidWindowPosition(*this, g_Conf->MainGuiPosition)) - g_Conf->MainGuiPosition = GetScreenPosition(); - else - SetPosition(g_Conf->MainGuiPosition); - - // Updating console log positions after the main window has been fitted to its sizer ensures - // proper docked positioning, since the main window's size is invalid until after the sizer - // has been set/fit. - - InitLogBoxPosition(g_Conf->ProgLogBox); - CreatePcsx2Menu(); - CreateCdvdMenu(); - CreateConfigMenu(); - CreateWindowsMenu(); - CreateCaptureMenu(); - CreateInputRecordingMenu(); - CreateHelpMenu(); - - m_MenuItem_Console.Check(g_Conf->ProgLogBox.Visible); - - ConnectMenus(); - Bind(wxEVT_MOVE, &MainEmuFrame::OnMoveAround, this); - Bind(wxEVT_CLOSE_WINDOW, &MainEmuFrame::OnCloseWindow, this); - Bind(wxEVT_SET_FOCUS, &MainEmuFrame::OnFocus, this); - Bind(wxEVT_ACTIVATE, &MainEmuFrame::OnActivate, this); - - PushEventHandler(&wxGetApp().GetRecentIsoManager()); - SetDropTarget(new IsoDropTarget(this)); - - ApplyCoreStatus(); - ApplySettings(); -} - -MainEmuFrame::~MainEmuFrame() -{ - try - { - if (m_RestartEmuOnDelete) - { - sApp.SetExitOnFrameDelete(false); - sApp.PostAppMethod(&Pcsx2App::DetectCpuAndUserMode); - sApp.WipeUserModeSettings(); - } - } - DESTRUCTOR_CATCHALL -} - -void MainEmuFrame::DoGiveHelp(const wxString& text, bool show) -{ - _parent::DoGiveHelp(text, show); - wxGetApp().GetProgramLog()->DoGiveHelp(text, show); -} - -// ---------------------------------------------------------------------------- -// OnFocus / OnActivate : Special implementation to "connect" the console log window -// with the main frame window. When one is clicked, the other is assured to be brought -// to the foreground with it. (Currently only MSW only, as wxWidgets appears to have no -// equivalent to this). Both OnFocus and OnActivate are handled because Focus events do -// not propagate up the window hierarchy, and on Activate events don't always get sent -// on the first focusing event after PCSX2 starts. - -void MainEmuFrame::OnFocus(wxFocusEvent& evt) -{ - if (ConsoleLogFrame* logframe = wxGetApp().GetProgramLog()) - MSW_SetWindowAfter(logframe->GetHandle(), GetHandle()); - - evt.Skip(); -} - -void MainEmuFrame::OnActivate(wxActivateEvent& evt) -{ - if (ConsoleLogFrame* logframe = wxGetApp().GetProgramLog()) - MSW_SetWindowAfter(logframe->GetHandle(), GetHandle()); - - evt.Skip(); -} -// ---------------------------------------------------------------------------- - -void MainEmuFrame::ApplyCoreStatus() -{ - ApplySuspendStatus(); - ApplyCDVDStatus(); -} - -void MainEmuFrame::ApplySuspendStatus() -{ - // [TODO] : Ideally each of these items would bind a listener instance to the AppCoreThread - // dispatcher, and modify their states accordingly. This is just a hack (for now) -- air - - if (wxMenuItem* susres = GetMenuBar()->FindItem(MenuId_Sys_SuspendResume)) - { - if (!CoreThread.IsClosing()) - { - susres->Enable(); - susres->SetItemLabel(_("Paus&e")); - susres->SetHelp(_("Safely pauses emulation and preserves the PS2 state.")); - } - else - { - bool ActiveVM = SysHasValidState(); - susres->Enable(ActiveVM); - if (ActiveVM) - { - susres->SetItemLabel(_("R&esume")); - susres->SetHelp(_("Resumes the suspended emulation state.")); - } - else - { - susres->SetItemLabel(_("Pause/Resume")); - susres->SetHelp(_("No emulation state is active; cannot suspend or resume.")); - } - } - // Re-init keybinding after changing the label. - AppendShortcutToMenuOption(*susres, wxGetApp().GlobalAccels->findKeycodeWithCommandId("Sys_SuspendResume").toTitleizedString()); - } -} - -void MainEmuFrame::ApplyCDVDStatus() -{ - const CDVD_SourceType Source = g_Conf->CdvdSource; - - wxMenuItem* cdvd_menu = GetMenuBar()->FindItem(MenuId_Boot_CDVD); - - wxString label; - wxString help_text = _("Use fast boot to skip PS2 startup and splash screens."); - - switch (Source) - { - case CDVD_SourceType::Iso: - label = _("Boot ISO"); - break; - case CDVD_SourceType::Disc: - label = _("Boot CDVD"); - break; - case CDVD_SourceType::NoDisc: - label = _("Boot BIOS"); - break; - default: - label = _("Boot BIOS"); - break; - } - - cdvd_menu->SetItemLabel(label); - cdvd_menu->SetHelp(help_text); -} - -//Apply a config to the menu such that the menu reflects it properly -void MainEmuFrame::ApplySettings() -{ - ApplyConfigToGui(*g_Conf); -} - -//MainEmuFrame needs to be aware which items are affected by presets if AppConfig::APPLY_FLAG_FROM_PRESET is on. -//currently only EnablePatches is affected when the settings come from a preset. -void MainEmuFrame::ApplyConfigToGui(AppConfig& configToApply, int flags) -{ - wxMenuBar& menubar(*GetMenuBar()); - - menubar.Check(MenuId_EnablePatches, configToApply.EmuOptions.EnablePatches); - menubar.Enable(MenuId_EnablePatches, !configToApply.EnablePresets); - - if (!(flags & AppConfig::APPLY_FLAG_FROM_PRESET)) - { //these should not be affected by presets - menubar.Check(MenuId_EnableBackupStates, configToApply.EmuOptions.BackupSavestate); - menubar.Check(MenuId_EnableCheats, configToApply.EmuOptions.EnableCheats); - menubar.Check(MenuId_PINE_Enable, configToApply.EmuOptions.EnablePINE); - menubar.Check(MenuId_EnableWideScreenPatches, configToApply.EmuOptions.EnableWideScreenPatches); - menubar.Check(MenuId_Capture_Video_IncludeAudio, configToApply.AudioCapture.EnableAudio); - - menubar.Check(MenuId_EnableHostFs, configToApply.EmuOptions.HostFs); - menubar.Check(MenuId_Debug_CreateBlockdump, configToApply.EmuOptions.CdvdDumpBlocks); -#if defined(__POSIX__) - menubar.Check(MenuId_Console_Stdio, configToApply.EmuOptions.ConsoleToStdio); -#endif - - menubar.Check(MenuId_Config_Multitap0Toggle, configToApply.EmuOptions.MultitapPort0_Enabled); - menubar.Check(MenuId_Config_Multitap1Toggle, configToApply.EmuOptions.MultitapPort1_Enabled); - menubar.Check(MenuId_Config_FastBoot, configToApply.EnableFastBoot); - } - - UpdateCdvdSrcSelection(); //shouldn't be affected by presets but updates from g_Conf anyway and not from configToApply, so no problem here. -} - -//write pending preset settings from the gui to g_Conf, -// without triggering an overall "settingsApplied" event. -void MainEmuFrame::CommitPreset_noTrigger() -{ - wxMenuBar& menubar(*GetMenuBar()); - g_Conf->EmuOptions.EnablePatches = menubar.IsChecked(MenuId_EnablePatches); -} - -void MainEmuFrame::AppendShortcutToMenuOption(wxMenuItem& item, wxString keyCodeStr) -{ - wxString text = item.GetItemLabel(); - const size_t tabPos = text.rfind(L'\t'); - item.SetItemLabel(text.Mid(0, tabPos) + L"\t" + keyCodeStr); -} diff --git a/pcsx2/gui/MainFrame.h b/pcsx2/gui/MainFrame.h deleted file mode 100644 index 615013ad61..0000000000 --- a/pcsx2/gui/MainFrame.h +++ /dev/null @@ -1,216 +0,0 @@ -/* PCSX2 - PS2 Emulator for PCs - * Copyright (C) 2002-2010 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 . - */ - -#pragma once - -#include "App.h" -#include "AppSaveStates.h" - -#include -#include - -// -------------------------------------------------------------------------------------- -// InvokeMenuCommand_OnSysStateUnlocked -// -------------------------------------------------------------------------------------- -class InvokeMenuCommand_OnSysStateUnlocked - : public IEventListener_SysState, - public BaseDeletableObject -{ -protected: - MenuIdentifiers m_menu_cmd; - -public: - InvokeMenuCommand_OnSysStateUnlocked(MenuIdentifiers menu_command) - { - m_menu_cmd = menu_command; - } - - virtual ~InvokeMenuCommand_OnSysStateUnlocked() = default; - - virtual void SaveStateAction_OnCreateFinished() - { - wxGetApp().PostMenuAction(m_menu_cmd); - } -}; - -// -------------------------------------------------------------------------------------- -// MainEmuFrame -// -------------------------------------------------------------------------------------- -class MainEmuFrame : public wxFrame, - public EventListener_CoreThread, - public EventListener_AppStatus -{ - typedef wxFrame _parent; - -protected: - bool m_RestartEmuOnDelete; - - wxStatusBar& m_statusbar; - wxStaticBitmap* m_background; - - wxMenuBar& m_menubar; - - wxMenu& m_menuCDVD; - wxMenu& m_menuSys; - wxMenu& m_menuConfig; - wxMenu& m_menuWindow; - - wxMenu& m_menuCapture; - wxMenu& m_submenuVideoCapture; - wxMenu& m_submenuPINE; - wxMenu& m_submenuScreenshot; - - wxMenu& m_menuRecording; - wxMenu& m_submenu_recording_settings; - wxMenu& m_menuHelp; - - wxMenu& m_LoadStatesSubmenu; - wxMenu& m_SaveStatesSubmenu; - wxMenu& m_GameSettingsSubmenu; - - wxMenuItem* m_menuItem_RecentIsoMenu; - wxMenuItem* m_menuItem_DriveListMenu; - wxMenuItem& m_MenuItem_Console; -#if defined(__POSIX__) - wxMenuItem& m_MenuItem_Console_Stdio; -#endif - - bool m_capturingVideo; - - virtual void DispatchEvent(const CoreThreadStatus& status); - virtual void AppStatusEvent_OnSettingsApplied(); - -public: - MainEmuFrame(wxWindow* parent, const wxString& title); - virtual ~MainEmuFrame(); - - void OnLogBoxHidden(); - - bool IsPaused() const { return GetMenuBar()->IsChecked(MenuId_Sys_SuspendResume); } - void UpdateCdvdSrcSelection(); - void RemoveCdvdMenu(); - void EnableMenuItem(int id, bool enable); - void CheckMenuItem(int id, bool checked); - void SetMenuItemLabel(int id, wxString str); - - void CreateCdvdMenu(); - void CreatePcsx2Menu(); - void CreateConfigMenu(); - void CreateWindowsMenu(); - void CreateCaptureMenu(); - void CreateInputRecordingMenu(); - void CreateHelpMenu(); - - bool Destroy(); - - void ApplyConfigToGui(AppConfig& configToApply, int flags = 0); - void CommitPreset_noTrigger(); - void AppendShortcutToMenuOption(wxMenuItem& item, wxString keyCodeStr); - void UpdateStatusBar(); - void VideoCaptureToggle(); - bool IsCapturing() const noexcept { return m_capturingVideo; } - -protected: - void DoGiveHelp(const wxString& text, bool show); - - //Apply here is from config to GUI. - void ApplySettings(); - void ApplyCoreStatus(); - void ApplySuspendStatus(); - void ApplyCDVDStatus(); - - void InitLogBoxPosition(AppConfig::ConsoleLogOptions& conf); - - void OnCloseWindow(wxCloseEvent& evt); - void OnMoveAround(wxMoveEvent& evt); - void OnFocus(wxFocusEvent& evt); - void OnActivate(wxActivateEvent& evt); - - void Menu_SysSettings_Click(wxCommandEvent& event); - void Menu_NetworkSettings_Click(wxCommandEvent& event); - void Menu_AudioSettings_Click(wxCommandEvent& event); - void Menu_USBSettings_Click(wxCommandEvent& event); - void Menu_PADSettings_Click(wxCommandEvent& event); - void Menu_McdSettings_Click(wxCommandEvent& event); - void Menu_WindowSettings_Click(wxCommandEvent& event); - void Menu_GSSettings_Click(wxCommandEvent& event); - void Menu_SelectBios_Click(wxCommandEvent& event); - void Menu_ResetAllSettings_Click(wxCommandEvent& event); - - void Menu_IsoBrowse_Click(wxCommandEvent& event); - void Menu_IsoClear_Click(wxCommandEvent& event); - void Menu_IsoClearMissing_Click(wxCommandEvent& event); - void Menu_EnableBackupStates_Click(wxCommandEvent& event); - void Menu_EnablePatches_Click(wxCommandEvent& event); - void Menu_EnableCheats_Click(wxCommandEvent& event); - void Menu_PINE_Enable_Click(wxCommandEvent& event); - void Menu_PINE_Settings_Click(wxCommandEvent& event); - void Menu_EnableWideScreenPatches_Click(wxCommandEvent& event); - void Menu_EnableRecordingTools_Click(wxCommandEvent& event); - void Menu_EnableHostFs_Click(wxCommandEvent& event); - - void Menu_BootCdvd_Click(wxCommandEvent& event); - void Menu_FastBoot_Click(wxCommandEvent& event); - - void Menu_OpenELF_Click(wxCommandEvent& event); - void Menu_CdvdSource_Click(wxCommandEvent& event); - void Menu_LoadStates_Click(wxCommandEvent& event); - void Menu_SaveStates_Click(wxCommandEvent& event); - void Menu_LoadStateFromFile_Click(wxCommandEvent& event); - void Menu_SaveStateToFile_Click(wxCommandEvent& event); - void Menu_Exit_Click(wxCommandEvent& event); - - void Menu_SuspendResume_Click(wxCommandEvent& event); - void Menu_SysShutdown_Click(wxCommandEvent& event); - - void Menu_MultitapToggle_Click(wxCommandEvent& event); - - void Menu_Debug_Open_Click(wxCommandEvent& event); - void Menu_Debug_MemoryDump_Click(wxCommandEvent& event); - void Menu_Debug_CreateBlockdump_Click(wxCommandEvent& event); - void Menu_Ask_On_Boot_Click(wxCommandEvent& event); - - void Menu_ShowConsole(wxCommandEvent& event); - void Menu_ChangeLang(wxCommandEvent& event); - void Menu_ShowConsole_Stdio(wxCommandEvent& event); - - void Menu_GetStarted(wxCommandEvent& event); - void Menu_Compatibility(wxCommandEvent& event); - void Menu_Forums(wxCommandEvent& event); - void Menu_Website(wxCommandEvent& event); - void Menu_Github(wxCommandEvent& event); - void Menu_Wiki(wxCommandEvent& event); - void Menu_ShowAboutBox(wxCommandEvent& event); - void Menu_ShowGSDump(wxCommandEvent& event); - - void Menu_Capture_Video_ToggleCapture_Click(wxCommandEvent& event); - void Menu_Capture_Video_IncludeAudio_Click(wxCommandEvent& event); - void Menu_Capture_Screenshot_Screenshot_Click(wxCommandEvent& event); - void Menu_Capture_Screenshot_Screenshot_As_Click(wxCommandEvent& event); - - void _DoBootCdvd(); - bool _DoSelectIsoBrowser(wxString& dest); - bool _DoSelectELFBrowser(); - - // ------------------------------------------------------------------------ - // MainEmuFram Internal API for Populating Main Menu Contents - // ------------------------------------------------------------------------ - - wxMenu* MakeStatesSubMenu(int baseid, int loadBackupId = -1) const; - - void ConnectMenus(); - - friend class Pcsx2App; -}; diff --git a/pcsx2/gui/MainMenuClicks.cpp b/pcsx2/gui/MainMenuClicks.cpp deleted file mode 100644 index 528645e812..0000000000 --- a/pcsx2/gui/MainMenuClicks.cpp +++ /dev/null @@ -1,890 +0,0 @@ -/* PCSX2 - PS2 Emulator for PCs - * Copyright (C) 2002-2021 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 . - */ - -#include "PrecompiledHeader.h" - -#include "App.h" -#include "CDVD/CDVD.h" -#include "GS.h" -#include "GSFrame.h" -#include "IniInterface.h" -#include "SPU2/spu2.h" -#include "SysThreads.h" -#include "DEV9/DEV9.h" -#include "USB/USBNull.h" -#include "PAD/Gamepad.h" - -#include "ConsoleLogger.h" -#include "MainFrame.h" -#include "IsoDropTarget.h" - -#include "Dialogs/ModalPopups.h" -#include "Dialogs/ConfigurationDialog.h" -#include "Debugger/DisassemblyDialog.h" - - -#include "fmt/core.h" -#include "wx/numdlg.h" - - -using namespace Dialogs; - -void MainEmuFrame::Menu_SysSettings_Click(wxCommandEvent& event) -{ - AppOpenModalDialog(wxEmptyString, this); -} - -void MainEmuFrame::Menu_PINE_Settings_Click(wxCommandEvent& event) -{ - AppOpenDialog(this); -} - -void MainEmuFrame::Menu_AudioSettings_Click(wxCommandEvent& event) -{ - SPU2configure(); -} - -void MainEmuFrame::Menu_McdSettings_Click(wxCommandEvent& event) -{ - ScopedCoreThreadClose closed_core; - closed_core.AllowResume(); - AppOpenModalDialog(wxEmptyString, this); -} - -void MainEmuFrame::Menu_NetworkSettings_Click(wxCommandEvent& event) -{ - DEV9configure(); -} - -void MainEmuFrame::Menu_USBSettings_Click(wxCommandEvent& event) -{ - USBconfigure(); -} - -void MainEmuFrame::Menu_PADSettings_Click(wxCommandEvent& event) -{ - PADconfigure(); -} - -void MainEmuFrame::Menu_GSSettings_Click(wxCommandEvent& event) -{ - GSconfigure(); - - // this is a bit of an ugly hack, but so is the whole of the threading nonsense. - // we need to tear down USB/PAD before we apply settings, because the window handle - // will change on renderer change. but we can't do that in ApplySettings() because - // that happens on the UI thread instead of the core thread.... - GSFrame* gs_frame = wxGetApp().GetGsFramePtr(); - const bool gs_frame_open = gs_frame && gs_frame->IsShown(); - const Pcsx2Config::GSOptions old_options(g_Conf->EmuOptions.GS); - g_Conf->EmuOptions.GS.ReloadIniSettings(); - if (!g_Conf->EmuOptions.GS.RestartOptionsAreEqual(old_options)) - { - ScopedCoreThreadPause pauser(static_cast(System_USB | System_PAD)); - wxGetApp().SysApplySettings(); - } - else - { - wxGetApp().SysApplySettings(); - } - - // re-hide the GS window after changing renderers if we were paused - gs_frame = wxGetApp().GetGsFramePtr(); - if (!gs_frame_open && gs_frame && gs_frame->IsShown()) - gs_frame->Hide(); -} - -void MainEmuFrame::Menu_WindowSettings_Click(wxCommandEvent& event) -{ - wxCommandEvent evt(pxEvt_SetSettingsPage); - evt.SetString(L"GS Window"); - AppOpenDialog(this)->GetEventHandler()->ProcessEvent(evt); -} - -void MainEmuFrame::Menu_SelectBios_Click(wxCommandEvent& event) -{ - AppOpenDialog(this); -} - -void MainEmuFrame::Menu_ChangeLang(wxCommandEvent& event) // Always in English -{ - AppOpenDialog(this); -} - -static void WipeSettings() -{ - wxGetApp().CleanupRestartable(); - wxGetApp().CleanupResources(); - - wxRemoveFile(GetUiSettingsFilename()); - wxRemoveFile(GetVmSettingsFilename()); - - // FIXME: wxRmdir doesn't seem to work here for some reason but deleting the inis folder - // manually from explorer does work. Can't think of a good work-around at the moment. --air - - //wxRmdir( GetSettingsFolder().ToString() ); - - wxGetApp().GetRecentIsoManager().Clear(); - g_Conf = std::unique_ptr(new AppConfig()); - sMainFrame.RemoveCdvdMenu(); - - sApp.WipeUserModeSettings(); -} - -void MainEmuFrame::RemoveCdvdMenu() -{ - // Delete() keeps the sub menu and delete the menu item. - // Remove() does not delete the menu item. - if (m_menuItem_RecentIsoMenu) - m_menuCDVD.Delete(m_menuItem_RecentIsoMenu); - m_menuItem_RecentIsoMenu = nullptr; -} - -void MainEmuFrame::Menu_ResetAllSettings_Click(wxCommandEvent& event) -{ - if (IsBeingDeleted() || m_RestartEmuOnDelete) - return; - - { - ScopedCoreThreadPopup suspender; - if (!Msgbox::OkCancel(pxsFmt( - pxE(L"This command clears %s settings and allows you to re-run the First-Time Wizard. You will need to manually restart %s after this operation.\n\nWARNING!! Click OK to delete *ALL* settings for %s and force-close the app, losing any current emulation progress. Are you absolutely sure?"), WX_STR(pxGetAppName()), WX_STR(pxGetAppName()), WX_STR(pxGetAppName())), - _("Reset all settings?"))) - { - suspender.AllowResume(); - return; - } - } - - WipeSettings(); - wxGetApp().PostMenuAction(MenuId_Exit); -} - -// Return values: -// wxID_CANCEL - User canceled the action outright. -// wxID_RESET - User wants to reset the emu in addition to swap discs -// (anything else) - Standard swap, no reset. (hotswap!) -wxWindowID SwapOrReset_Iso(wxWindow* owner, IScopedCoreThread& core_control, const wxString& isoFilename, const wxString& descpart1) -{ - if (GSDump::isRunning) - { - wxMessageBox("Please close the GS debugger first before playing a game", _("GS Debugger"), wxICON_ERROR); - return wxID_CANCEL; - } - wxWindowID result = wxID_CANCEL; - - if ((g_Conf->CdvdSource == CDVD_SourceType::Iso) && (isoFilename == g_Conf->CurrentIso)) - { - core_control.AllowResume(); - return result; - } - - if (SysHasValidState()) - { - core_control.DisallowResume(); - wxDialogWithHelpers dialog(owner, _("Confirm ISO image change")); - - dialog += dialog.Heading(descpart1); // New lines already applied - dialog += dialog.Text(isoFilename); - dialog += dialog.GetCharHeight(); - dialog += dialog.Heading(_("Do you want to swap discs or boot the new image (via system reset)?")); - - result = pxIssueConfirmation(dialog, MsgButtons().Reset().Cancel().Custom(_("Swap Disc"), "swap")); - if (result == wxID_CANCEL) - { - core_control.AllowResume(); - return result; - } - } - - g_CDVDReset = true; - g_Conf->CdvdSource = CDVD_SourceType::Iso; - SysUpdateIsoSrcFile(isoFilename); - - if (result == wxID_RESET) - { - core_control.DisallowResume(); - sApp.SysExecute(CDVD_SourceType::Iso); - } - else - { - Console.Indent().WriteLn("HotSwapping to new ISO src image!"); - //g_Conf->CdvdSource = CDVDsrc_Iso; - //CoreThread.ChangeCdvdSource(); - core_control.AllowResume(); - } - - return result; -} - -// Return values: -// wxID_CANCEL - User canceled the action outright. -// wxID_RESET - User wants to reset the emu in addition to swap discs -// (anything else) - Standard swap, no reset. (hotswap!) -wxWindowID SwapOrReset_Disc(wxWindow* owner, IScopedCoreThread& core, const wxString driveLetter) -{ - if (GSDump::isRunning) - { - wxMessageBox("Please close the GS debugger first before playing a game", _("GS Debugger"), wxICON_ERROR); - return wxID_CANCEL; - } - wxWindowID result = wxID_CANCEL; - - if ((g_Conf->CdvdSource == CDVD_SourceType::Disc) && (driveLetter == g_Conf->Folders.RunDisc)) - { - core.AllowResume(); - return result; - } - - if (SysHasValidState()) - { - core.DisallowResume(); - wxDialogWithHelpers dialog(owner, _("Confirm disc change")); - - dialog += dialog.Heading("New drive selected: " + driveLetter); - dialog += dialog.GetCharHeight(); - dialog += dialog.Heading(_("Do you want to swap discs or boot the new disc (via system reset)?")); - - result = pxIssueConfirmation(dialog, MsgButtons().Reset().Cancel().Custom(_("Swap Disc"), "swap")); - if (result == wxID_CANCEL) - { - core.AllowResume(); - return result; - } - } - - g_Conf->CdvdSource = CDVD_SourceType::Disc; - SysUpdateDiscSrcDrive(driveLetter); - if (result == wxID_RESET) - { - core.DisallowResume(); - sApp.SysExecute(CDVD_SourceType::Disc); - } - else - { - Console.Indent().WriteLn("Hot swapping to new disc!"); - core.AllowResume(); - } - - return result; -} - -wxWindowID SwapOrReset_CdvdSrc(wxWindow* owner, CDVD_SourceType newsrc) -{ - if (GSDump::isRunning) - { - wxMessageBox("Please close the GS debugger first before playing a game", _("GS Debugger"), wxICON_ERROR); - return wxID_CANCEL; - } - if (newsrc == g_Conf->CdvdSource) - return wxID_CANCEL; - wxWindowID result = wxID_CANCEL; - ScopedCoreThreadPopup core; - - if (SysHasValidState()) - { - wxDialogWithHelpers dialog(owner, _("Confirm CDVD source change")); - - wxString changeMsg; - changeMsg.Printf(_("You've selected to switch the CDVD source from %s to %s."), - CDVD_SourceLabels[enum_cast(g_Conf->CdvdSource)], CDVD_SourceLabels[enum_cast(newsrc)]); - - dialog += dialog.Heading(changeMsg + L"\n\n" + - _("Do you want to swap discs or boot the new image (system reset)?")); - - result = pxIssueConfirmation(dialog, MsgButtons().Reset().Cancel().Custom(_("Swap Disc"), "swap")); - - if (result == wxID_CANCEL) - { - core.AllowResume(); - sMainFrame.UpdateCdvdSrcSelection(); - return result; - } - } - - g_CDVDReset = true; - CDVD_SourceType oldsrc = g_Conf->CdvdSource; - g_Conf->CdvdSource = newsrc; - - if (result != wxID_RESET) - { - Console.Indent().WriteLn("(CdvdSource) HotSwapping CDVD source types from %ls to %ls.", - WX_STR(wxString(CDVD_SourceLabels[enum_cast(oldsrc)])), - WX_STR(wxString(CDVD_SourceLabels[enum_cast(newsrc)]))); - //CoreThread.ChangeCdvdSource(); - sMainFrame.UpdateCdvdSrcSelection(); - core.AllowResume(); - } - else - { - core.DisallowResume(); - sApp.SysExecute(g_Conf->CdvdSource); - } - - return result; -} - -static wxString JoinFiletypes(const wxChar** src) -{ - wxString dest; - while (*src != NULL) - { - if (*src[0] == 0) - continue; - if (!dest.IsEmpty()) - dest += L";"; - - dest += pxsFmt(L"*.%ls", *src); - - if (wxFileName::IsCaseSensitive()) - { - // omgosh! the filesystem is CaSE SeNSiTiVE!! - dest += pxsFmt(L";*.%ls", *src).ToUpper(); - } - - ++src; - } - - return dest; -} - -// Returns FALSE if the user canceled the action. -bool MainEmuFrame::_DoSelectIsoBrowser(wxString& result) -{ - static const wxChar* isoSupportedTypes[] = - { - L"iso", L"mdf", L"nrg", L"bin", L"img", NULL}; - - const wxString isoSupportedLabel(JoinString(isoSupportedTypes, L" ")); - const wxString isoSupportedList(JoinFiletypes(isoSupportedTypes)); - - wxArrayString isoFilterTypes; - - isoFilterTypes.Add(pxsFmt(_("All Supported (%s)"), WX_STR((isoSupportedLabel + L" .dump" + L" .gz" + L" .cso" + L" .chd")))); - isoFilterTypes.Add(isoSupportedList + L";*.dump" + L";*.gz" + L";*.cso" + L";*.chd"); - - isoFilterTypes.Add(pxsFmt(_("Disc Images (%s)"), WX_STR(isoSupportedLabel))); - isoFilterTypes.Add(isoSupportedList); - - isoFilterTypes.Add(pxsFmt(_("Blockdumps (%s)"), L".dump")); - isoFilterTypes.Add(L"*.dump"); - - isoFilterTypes.Add(pxsFmt(_("Compressed (%s)"), L".gz .cso .chd")); - isoFilterTypes.Add(L"*.gz;*.cso;*.chd"); - - isoFilterTypes.Add(_("All Files (*.*)")); - isoFilterTypes.Add(L"*.*"); - - wxFileDialog ctrl(this, _("Select disc image, compressed disc image, or block-dump..."), g_Conf->Folders.RunIso.ToString(), wxEmptyString, - JoinString(isoFilterTypes, L"|"), wxFD_OPEN | wxFD_FILE_MUST_EXIST); - - if (ctrl.ShowModal() != wxID_CANCEL) - { - result = ctrl.GetPath(); - g_Conf->Folders.RunIso = wxFileName(result).GetPath(); - return true; - } - - return false; -} - -bool MainEmuFrame::_DoSelectELFBrowser() -{ - static const wxChar* elfFilterType = L"ELF Files (.elf)|*.elf;*.ELF"; - - wxFileDialog ctrl(this, _("Select ELF file..."), g_Conf->Folders.RunELF.ToString(), wxEmptyString, - (wxString)elfFilterType + L"|" + _("All Files (*.*)") + L"|*.*", wxFD_OPEN | wxFD_FILE_MUST_EXIST); - - if (ctrl.ShowModal() != wxID_CANCEL) - { - g_Conf->Folders.RunELF = wxFileName(ctrl.GetPath()).GetPath(); - g_Conf->CurrentELF = ctrl.GetPath(); - return true; - } - - return false; -} - -void MainEmuFrame::_DoBootCdvd() -{ - if (GSDump::isRunning) - { - wxMessageBox("Please close the GS debugger first before playing a game", _("GS Debugger"), wxICON_ERROR); - return; - } - ScopedCoreThreadPause paused_core; - - if (g_Conf->CdvdSource == CDVD_SourceType::Iso) - { - bool selector = g_Conf->CurrentIso.IsEmpty(); - - if (!selector && !wxFileExists(g_Conf->CurrentIso)) - { - // User has an iso selected from a previous run, but it doesn't exist anymore. - // Issue a courtesy popup and then an Iso Selector to choose a new one. - - wxDialogWithHelpers dialog(this, _("ISO file not found!")); - dialog += dialog.Heading( - _("An error occurred while trying to open the file:") + wxString(L"\n\n") + g_Conf->CurrentIso + L"\n\n" + - _("Error: The configured ISO file does not exist. Click OK to select a new ISO source for CDVD.")); - - pxIssueConfirmation(dialog, MsgButtons().OK()); - - selector = true; - } - - if (selector || g_Conf->AskOnBoot) - { - wxString result; - if (!_DoSelectIsoBrowser(result)) - { - paused_core.AllowResume(); - return; - } - - SysUpdateIsoSrcFile(result); - } - } - - if (SysHasValidState()) - { - wxDialogWithHelpers dialog(this, _("Confirm PS2 Reset")); - dialog += dialog.Heading(GetMsg_ConfirmSysReset()); - bool confirmed = (pxIssueConfirmation(dialog, MsgButtons().Yes().Cancel(), L"BootCdvd.ConfirmReset") != wxID_CANCEL); - - if (!confirmed) - { - paused_core.AllowResume(); - return; - } - } - - sApp.SysExecute(g_Conf->CdvdSource); -} - -void MainEmuFrame::Menu_CdvdSource_Click(wxCommandEvent& event) -{ - CDVD_SourceType newsrc = CDVD_SourceType::NoDisc; - - switch (event.GetId()) - { - case MenuId_Src_Iso: - newsrc = CDVD_SourceType::Iso; - break; - case MenuId_Src_Disc: - newsrc = CDVD_SourceType::Disc; - break; - case MenuId_Src_NoDisc: - newsrc = CDVD_SourceType::NoDisc; - break; - jNO_DEFAULT - } - - SwapOrReset_CdvdSrc(this, newsrc); - ApplyCDVDStatus(); -} - -void MainEmuFrame::Menu_BootCdvd_Click(wxCommandEvent& event) -{ - g_Conf->EmuOptions.UseBOOT2Injection = g_Conf->EnableFastBoot; - _DoBootCdvd(); -} - -void MainEmuFrame::Menu_FastBoot_Click(wxCommandEvent& event) -{ - g_Conf->EnableFastBoot = GetMenuBar()->IsChecked(MenuId_Config_FastBoot); - AppApplySettings(); - AppSaveSettings(); - UpdateStatusBar(); -} - -wxString GetMsg_IsoImageChanged() -{ - return _("You have selected the following ISO image into PCSX2:\n\n"); -} - -void MainEmuFrame::Menu_IsoBrowse_Click(wxCommandEvent& event) -{ - ScopedCoreThreadPopup core; - wxString isofile; - - if (!_DoSelectIsoBrowser(isofile)) - { - core.AllowResume(); - return; - } - - SwapOrReset_Iso(this, core, isofile, GetMsg_IsoImageChanged()); - AppSaveSettings(); // save the new iso selection; update menus! -} - -void MainEmuFrame::Menu_IsoClear_Click(wxCommandEvent& event) -{ - wxDialogWithHelpers dialog(this, _("Confirm clearing ISO list")); - dialog += dialog.Heading(_("This will clear the ISO list. If an ISO is running it will remain in the list. Continue?")); - - bool confirmed = pxIssueConfirmation(dialog, MsgButtons().YesNo()) == wxID_YES; - - if (confirmed) - { - // If the CDVD mode is not ISO, or the system isn't running, wipe the CurrentIso field in INI file - if (g_Conf->CdvdSource != CDVD_SourceType::Iso || !SysHasValidState()) - SysUpdateIsoSrcFile(""); - wxGetApp().GetRecentIsoManager().Clear(); - AppSaveSettings(); - } -} - -void MainEmuFrame::Menu_IsoClearMissing_Click(wxCommandEvent& event) -{ - auto& iso_manager = wxGetApp().GetRecentIsoManager(); - const auto& missing_files = iso_manager.GetMissingFiles(); - - if (missing_files.empty()) - { - wxDialogWithHelpers dialog(this, _("Information")); - dialog += dialog.Heading(_("No files to remove.")); - pxIssueConfirmation(dialog, MsgButtons().OK()); - } - else - { - wxDialogWithHelpers dialog(this, _("Confirm clearing ISO list")); - dialog += dialog.Heading(_("The following entries will be removed from the ISO list. Continue?")); - - wxString missing_files_list; - - for (const auto& file : missing_files) - { - missing_files_list += "* "; - missing_files_list += file.Filename; - missing_files_list += "\n"; - } - - dialog += dialog.Text(missing_files_list); - - const bool confirmed = pxIssueConfirmation(dialog, MsgButtons().YesNo()) == wxID_YES; - - if (confirmed) - { - // If the CDVD mode is not ISO, or the system isn't running, wipe the CurrentIso field in INI file - if (g_Conf->CdvdSource != CDVD_SourceType::Iso || !SysHasValidState()) - SysUpdateIsoSrcFile(""); - iso_manager.ClearMissing(); - AppSaveSettings(); - } - } -} - -void MainEmuFrame::Menu_Ask_On_Boot_Click(wxCommandEvent& event) -{ - g_Conf->AskOnBoot = event.IsChecked(); - - if (SysHasValidState()) - return; - - wxGetApp().GetRecentIsoManager().EnableItems(!event.IsChecked()); - FindItemInMenuBar(MenuId_IsoBrowse)->Enable(!event.IsChecked()); -} - -void MainEmuFrame::Menu_Debug_CreateBlockdump_Click(wxCommandEvent& event) -{ - g_Conf->EmuOptions.CdvdDumpBlocks = event.IsChecked(); - if (g_Conf->EmuOptions.CdvdDumpBlocks && SysHasValidState()) - Console.Warning("VM must be rebooted to create a useful block dump."); - - AppSaveSettings(); -} - -void MainEmuFrame::Menu_MultitapToggle_Click(wxCommandEvent&) -{ - g_Conf->EmuOptions.MultitapPort0_Enabled = GetMenuBar()->IsChecked(MenuId_Config_Multitap0Toggle); - g_Conf->EmuOptions.MultitapPort1_Enabled = GetMenuBar()->IsChecked(MenuId_Config_Multitap1Toggle); - AppApplySettings(); - AppSaveSettings(); - - //evt.Skip(); -} - -void MainEmuFrame::Menu_EnableBackupStates_Click(wxCommandEvent&) -{ - g_Conf->EmuOptions.BackupSavestate = GetMenuBar()->IsChecked(MenuId_EnableBackupStates); - - //without the next line, after toggling this menu-checkbox, the change only applies from the 2nd save and onwards - // (1st save after the toggle keeps the old pre-toggle value).. - // wonder what that means for all the other menu checkboxes which only use AppSaveSettings... (avih) - AppApplySettings(); - AppSaveSettings(); -} - -void MainEmuFrame::Menu_EnablePatches_Click(wxCommandEvent&) -{ - g_Conf->EmuOptions.EnablePatches = GetMenuBar()->IsChecked(MenuId_EnablePatches); - AppApplySettings(); - AppSaveSettings(); -} - -void MainEmuFrame::Menu_EnableCheats_Click(wxCommandEvent&) -{ - g_Conf->EmuOptions.EnableCheats = GetMenuBar()->IsChecked(MenuId_EnableCheats); - AppApplySettings(); - AppSaveSettings(); -} - -void MainEmuFrame::Menu_PINE_Enable_Click(wxCommandEvent&) -{ - g_Conf->EmuOptions.EnablePINE = GetMenuBar()->IsChecked(MenuId_PINE_Enable); - AppApplySettings(); - AppSaveSettings(); -} - -void MainEmuFrame::Menu_EnableWideScreenPatches_Click(wxCommandEvent&) -{ - g_Conf->EmuOptions.EnableWideScreenPatches = GetMenuBar()->IsChecked(MenuId_EnableWideScreenPatches); - AppApplySettings(); - AppSaveSettings(); -} - -void MainEmuFrame::Menu_EnableRecordingTools_Click(wxCommandEvent& event) -{ - Msgbox::Alert("Input Recording has been deprecated from the wxWidgets version.\nUse an older dev build (before v1.7.3650) or switch to the Qt version!\nOld recordings should still play correctly!"); - return; -} - -void MainEmuFrame::Menu_EnableHostFs_Click(wxCommandEvent&) -{ - g_Conf->EmuOptions.HostFs = GetMenuBar()->IsChecked(MenuId_EnableHostFs); - AppSaveSettings(); -} - -void MainEmuFrame::Menu_OpenELF_Click(wxCommandEvent&) -{ - ScopedCoreThreadClose stopped_core; - if (_DoSelectELFBrowser()) - { - g_Conf->EmuOptions.UseBOOT2Injection = true; - sApp.SysExecute(g_Conf->CdvdSource, g_Conf->CurrentELF); - } - - stopped_core.AllowResume(); -} - -void MainEmuFrame::Menu_LoadStates_Click(wxCommandEvent& event) -{ - if (event.GetId() == MenuId_State_LoadBackup) - { - States_DefrostCurrentSlotBackup(); - return; - } - - States_SetCurrentSlot(event.GetId() - MenuId_State_Load01 - 1); - States_DefrostCurrentSlot(); -} - -void MainEmuFrame::Menu_SaveStates_Click(wxCommandEvent& event) -{ - States_SetCurrentSlot(event.GetId() - MenuId_State_Save01 - 1); - States_FreezeCurrentSlot(); -} - -void MainEmuFrame::Menu_LoadStateFromFile_Click(wxCommandEvent& event) -{ - wxFileDialog loadStateDialog(this, _("Load State"), L"", L"", - L"Savestate files (*.p2s)|*.p2s", wxFD_OPEN); - - if (loadStateDialog.ShowModal() == wxID_CANCEL) - { - return; - } - - wxString path = loadStateDialog.GetPath(); - StateCopy_LoadFromFile(path); -} - -void MainEmuFrame::Menu_SaveStateToFile_Click(wxCommandEvent& event) -{ - wxFileDialog saveStateDialog(this, _("Save State"), L"", L"", - L"Savestate files (*.p2s)|*.p2s", wxFD_OPEN); - - if (saveStateDialog.ShowModal() == wxID_CANCEL) - { - return; - } - - wxString path = saveStateDialog.GetPath(); - StateCopy_SaveToFile(path); -} - -void MainEmuFrame::Menu_Exit_Click(wxCommandEvent& event) -{ - Close(); -} - -class SysExecEvent_ToggleSuspend : public SysExecEvent -{ -public: - virtual ~SysExecEvent_ToggleSuspend() = default; - - wxString GetEventName() const { return L"ToggleSuspendResume"; } - -protected: - void InvokeEvent() - { - if (CoreThread.IsOpen()) - { - CoreThread.Suspend(); - } - else - CoreThread.Resume(); - } -}; - -void MainEmuFrame::Menu_SuspendResume_Click(wxCommandEvent& event) -{ - if (!SysHasValidState()) - return; - - // Disable the menu item. The state of the menu is indeterminate until the core thread - // has responded (it updates status after emulation has engaged successfully). - - EnableMenuItem(MenuId_Sys_SuspendResume, false); - GetSysExecutorThread().PostEvent(new SysExecEvent_ToggleSuspend()); -} - -void MainEmuFrame::Menu_SysShutdown_Click(wxCommandEvent& event) -{ - if (m_capturingVideo) - VideoCaptureToggle(); - UI_DisableSysShutdown(); - Console.SetTitle("PCSX2 Program Log"); - CoreThread.Reset(); -} - -void MainEmuFrame::Menu_Debug_Open_Click(wxCommandEvent& event) -{ - DisassemblyDialog* dlg = wxGetApp().GetDisassemblyPtr(); - if (dlg) - { - if (event.IsChecked()) - dlg->Show(); - else - dlg->Hide(); - } -} - -void MainEmuFrame::Menu_Debug_MemoryDump_Click(wxCommandEvent& event) -{ -} - -void MainEmuFrame::Menu_ShowConsole(wxCommandEvent& event) -{ - // Use messages to relay open/close commands (thread-safe) - - g_Conf->ProgLogBox.Visible = event.IsChecked(); - wxCommandEvent evt(wxEVT_MENU, g_Conf->ProgLogBox.Visible ? wxID_OPEN : wxID_CLOSE); - wxGetApp().ProgramLog_PostEvent(evt); -} - -void MainEmuFrame::Menu_ShowConsole_Stdio(wxCommandEvent& event) -{ - g_Conf->EmuOptions.ConsoleToStdio = GetMenuBar()->IsChecked(MenuId_Console_Stdio); - AppSaveSettings(); -} - -void MainEmuFrame::Menu_GetStarted(wxCommandEvent& event) -{ - wxLaunchDefaultBrowser("https://pcsx2.net/guides/basic-setup/"); -} - -void MainEmuFrame::Menu_Compatibility(wxCommandEvent& event) -{ - wxLaunchDefaultBrowser("https://pcsx2.net/compat/"); -} - -void MainEmuFrame::Menu_Forums(wxCommandEvent& event) -{ - wxLaunchDefaultBrowser("https://forums.pcsx2.net/"); -} - -void MainEmuFrame::Menu_Website(wxCommandEvent& event) -{ - wxLaunchDefaultBrowser("https://pcsx2.net/"); -} - -void MainEmuFrame::Menu_Github(wxCommandEvent& event) -{ - wxLaunchDefaultBrowser("https://github.com/PCSX2/pcsx2"); -} - -void MainEmuFrame::Menu_Wiki(wxCommandEvent& event) -{ - wxLaunchDefaultBrowser("https://wiki.pcsx2.net/Main_Page"); -} - -void MainEmuFrame::Menu_ShowAboutBox(wxCommandEvent& event) -{ - AppOpenDialog(this); -} - -void MainEmuFrame::Menu_ShowGSDump(wxCommandEvent& event) -{ - AppOpenDialog(this); -} - -void MainEmuFrame::Menu_Capture_Video_ToggleCapture_Click(wxCommandEvent& event) -{ - ScopedCoreThreadPause paused_core; - paused_core.AllowResume(); - VideoCaptureToggle(); -} - -void MainEmuFrame::Menu_Capture_Video_IncludeAudio_Click(wxCommandEvent& event) -{ - g_Conf->AudioCapture.EnableAudio = GetMenuBar()->IsChecked(MenuId_Capture_Video_IncludeAudio); - ApplySettings(); -} - -void MainEmuFrame::VideoCaptureToggle() -{ -} - -void MainEmuFrame::Menu_Capture_Screenshot_Screenshot_Click(wxCommandEvent& event) -{ - if (!CoreThread.IsOpen()) - { - return; - } - GSQueueSnapshot(std::string(), 0); -} - -void MainEmuFrame::Menu_Capture_Screenshot_Screenshot_As_Click(wxCommandEvent& event) -{ - if (!CoreThread.IsOpen()) - return; - - // Ensure emulation is paused so that the correct image is captured - bool wasPaused = CoreThread.IsPaused(); - if (!wasPaused) - CoreThread.Pause({}); - - wxFileDialog fileDialog(this, _("Select a file"), g_Conf->Folders.Snapshots.ToAscii(), wxEmptyString, "PNG files (*.png)|*.png", wxFD_SAVE | wxFD_OVERWRITE_PROMPT); - - if (fileDialog.ShowModal() == wxID_OK) - GSQueueSnapshot(StringUtil::wxStringToUTF8String(fileDialog.GetPath()), 0); - - // Resume emulation - if (!wasPaused) - CoreThread.Resume(); -} - diff --git a/pcsx2/gui/MessageBoxes.cpp b/pcsx2/gui/MessageBoxes.cpp deleted file mode 100644 index d2b0bff6a7..0000000000 --- a/pcsx2/gui/MessageBoxes.cpp +++ /dev/null @@ -1,185 +0,0 @@ -/* PCSX2 - PS2 Emulator for PCs - * Copyright (C) 2002-2010 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 . - */ - -#include "PrecompiledHeader.h" -#include "App.h" -#include "MSWstuff.h" -#include "Dialogs/ModalPopups.h" - -using namespace Threading; -using namespace pxSizerFlags; - -// Thread Safety: Must be called from the GUI thread ONLY. Will assert otherwise. -// -// [TODO] Add support for icons? -// -static int pxMessageDialog( const wxString& caption, const wxString& content, const MsgButtons& buttons ) -{ - if( !AffinityAssert_AllowFrom_MainUI() ) return wxID_CANCEL; - - // fixme: If the emulator is currently active and is running in exclusive mode (forced - // fullscreen), then we need to either: - // 1) Exit fullscreen mode before issuing the popup. - // 2) Issue the popup with wxSTAY_ON_TOP specified so that the user will see it. - // - // And in either case the emulation should be paused/suspended for the user. - - wxDialogWithHelpers dialog( NULL, caption ); - dialog.SetMinWidth( ((content.Length() > 256) ? 525 : 460) * MSW_GetDPIScale() ); - dialog += dialog.Heading( content ) | StdExpand(); - return pxIssueConfirmation( dialog, buttons ); -} - -// -------------------------------------------------------------------------------------- -// BaseMessageBoxEvent Implementation -// -------------------------------------------------------------------------------------- -wxIMPLEMENT_DYNAMIC_CLASS( BaseMessageBoxEvent, pxActionEvent ); - -BaseMessageBoxEvent::BaseMessageBoxEvent( const wxString& content, SynchronousActionState& instdata ) - : m_Content( content ) -{ - m_state = &instdata; -} - -BaseMessageBoxEvent::BaseMessageBoxEvent( const wxString& content, SynchronousActionState* instdata ) - : m_Content( content ) -{ - m_state = instdata; -} - -BaseMessageBoxEvent::BaseMessageBoxEvent( const BaseMessageBoxEvent& event ) - : pxActionEvent(), m_Content( event.m_Content ) -{ - m_state = event.m_state; -} - -// Thread Safety: Must be called from the GUI thread ONLY. -void BaseMessageBoxEvent::InvokeEvent() -{ - int result = _DoDialog(); - if( m_state ) m_state->PostResult( result ); -} - -int BaseMessageBoxEvent::_DoDialog() const -{ - pxFailDev( "Abstract Base MessageBox Event." ); - return wxID_CANCEL; -} - -// -------------------------------------------------------------------------------------- -// pxMessageBoxEvent Implementation -// -------------------------------------------------------------------------------------- -wxIMPLEMENT_DYNAMIC_CLASS( pxMessageBoxEvent, BaseMessageBoxEvent ); - -pxMessageBoxEvent::pxMessageBoxEvent( const wxString& title, const wxString& content, const MsgButtons& buttons, SynchronousActionState& instdata ) - : BaseMessageBoxEvent( content, instdata ) - , m_Title( title ) - , m_Buttons( buttons ) -{ -} - -pxMessageBoxEvent::pxMessageBoxEvent( const wxString& title, const wxString& content, const MsgButtons& buttons, SynchronousActionState* instdata ) - : BaseMessageBoxEvent( content, instdata ) - , m_Title( title ) - , m_Buttons( buttons ) -{ -} - -int pxMessageBoxEvent::_DoDialog() const -{ - return pxMessageDialog( m_Title, m_Content, m_Buttons ); -} - -// -------------------------------------------------------------------------------------- -// pxAssertionEvent Implementation -// -------------------------------------------------------------------------------------- -wxIMPLEMENT_DYNAMIC_CLASS( pxAssertionEvent, BaseMessageBoxEvent ); - -pxAssertionEvent::pxAssertionEvent( const wxString& content, const wxString& trace, SynchronousActionState& instdata ) - : BaseMessageBoxEvent( content, instdata ) - , m_Stacktrace( trace ) -{ -} - -pxAssertionEvent::pxAssertionEvent( const wxString& content, const wxString& trace, SynchronousActionState* instdata ) - : BaseMessageBoxEvent( content, instdata ) - , m_Stacktrace( trace ) -{ -} - -pxAssertionEvent& pxAssertionEvent::SetStacktrace( const wxString& trace ) -{ - m_Stacktrace = trace; - return *this; -} - -int pxAssertionEvent::_DoDialog() const -{ - return Dialogs::AssertionDialog( m_Content, m_Stacktrace ).ShowModal(); -} - -namespace Msgbox -{ - int ShowModal( BaseMessageBoxEvent& evt ) - { - SynchronousActionState instdat; - evt.SetSyncState( instdat ); - - if( wxThread::IsMain() ) - { - // main thread can handle the message immediately. - wxGetApp().ProcessEvent( evt ); - return instdat.return_value; - } - else - { - // Not on main thread, must post the message there for handling instead: - wxGetApp().AddPendingEvent( evt ); - return instdat.WaitForResult(); // Important! disable cancellation since we're using local stack vars. - } - } - - int ShowModal( const wxString& title, const wxString& content, const MsgButtons& buttons ) - { - pxMessageBoxEvent tevt( title, content, buttons ); - return ShowModal( tevt ); - } - - // Pops up an alert Dialog Box with a singular "OK" button. - // Always returns false. - bool Alert( const wxString& text, const wxString& caption, int icon ) - { - ShowModal( caption, text, MsgButtons().OK() ); - return false; - } - - // Pops up a dialog box with Ok/Cancel buttons. Returns the result of the inquiry, - // true if OK, false if cancel. - bool OkCancel( const wxString& text, const wxString& caption, int icon ) - { - return ShowModal(caption, text, MsgButtons().OKCancel()) == wxID_OK; - } - - bool YesNo( const wxString& text, const wxString& caption, int icon ) - { - return ShowModal(caption, text, MsgButtons().YesNo()) == wxID_YES; - } - - int Assertion( const wxString& text, const wxString& stacktrace ) - { - pxAssertionEvent tevt( text, stacktrace ); - return ShowModal( tevt ); - } -} diff --git a/pcsx2/gui/Mutex.cpp b/pcsx2/gui/Mutex.cpp deleted file mode 100644 index b6e8882eb2..0000000000 --- a/pcsx2/gui/Mutex.cpp +++ /dev/null @@ -1,346 +0,0 @@ -/* PCSX2 - PS2 Emulator for PCs - * Copyright (C) 2002-2010 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 . - */ - -#include "PrecompiledHeader.h" -#include "PersistentThread.h" -#include - -namespace Threading -{ - static std::atomic _attr_refcount(0); - static pthread_mutexattr_t _attr_recursive; -} // namespace Threading - -// -------------------------------------------------------------------------------------- -// Mutex Implementations -// -------------------------------------------------------------------------------------- - -#if defined(_WIN32) || (defined(_POSIX_TIMEOUTS) && _POSIX_TIMEOUTS >= 200112L) -// good, we have pthread_mutex_timedlock -#define xpthread_mutex_timedlock pthread_mutex_timedlock -#else -// We have to emulate pthread_mutex_timedlock(). This could be a serious -// performance drain if its used a lot. - -#include // gettimeofday() - -// sleep for 10ms at a time -#define TIMEDLOCK_EMU_SLEEP_NS 10000000ULL - -// Original POSIX docs: -// -// The pthread_mutex_timedlock() function shall lock the mutex object -// referenced by mutex. If the mutex is already locked, the calling thread -// shall block until the mutex becomes available as in the -// pthread_mutex_lock() function. If the mutex cannot be locked without -// waiting for another thread to unlock the mutex, this wait shall be -// terminated when the specified timeout expires. -// -// This is an implementation that emulates pthread_mutex_timedlock() via -// pthread_mutex_trylock(). -static int xpthread_mutex_timedlock( - pthread_mutex_t* mutex, - const struct timespec* abs_timeout) -{ - int err = 0; - - while ((err = pthread_mutex_trylock(mutex)) == EBUSY) - { - // check if the timeout has expired, gettimeofday() is implemented - // efficiently (in userspace) on OSX - struct timeval now; - gettimeofday(&now, NULL); - if (now.tv_sec > abs_timeout->tv_sec || (now.tv_sec == abs_timeout->tv_sec && (u64)now.tv_usec * 1000ULL > (u64)abs_timeout->tv_nsec)) - { - return ETIMEDOUT; - } - - // acquiring lock failed, sleep some - struct timespec ts; - ts.tv_sec = 0; - ts.tv_nsec = TIMEDLOCK_EMU_SLEEP_NS; - while (nanosleep(&ts, &ts) == -1) - ; - } - - return err; -} -#endif - -Threading::Mutex::Mutex() -{ - pthread_mutex_init(&m_mutex, NULL); -} - -static wxTimeSpan def_detach_timeout(0, 0, 6, 0); - -void Threading::Mutex::Detach() -{ - if (EBUSY != pthread_mutex_destroy(&m_mutex)) - return; - - if (IsRecursive()) - { - // Sanity check: Recursive locks could be held by our own thread, which would - // be considered an assertion failure, but can also be handled gracefully. - // (note: if the mutex is locked recursively more than twice then this assert won't - // detect it) - - Release(); - Release(); // in case of double recursion. - int result = pthread_mutex_destroy(&m_mutex); - if (pxAssertDev(result != EBUSY, "Detachment of a recursively-locked mutex (self-locked!).")) - return; - } - - if (Wait(def_detach_timeout)) - pthread_mutex_destroy(&m_mutex); - else - Console.Error("(Thread Log) Mutex cleanup failed due to possible deadlock."); -} - -Threading::Mutex::~Mutex() -{ - try - { - Mutex::Detach(); - } - DESTRUCTOR_CATCHALL; -} - -Threading::MutexRecursive::MutexRecursive() - : Mutex(false) -{ - if (++_attr_refcount == 1) - { - if (0 != pthread_mutexattr_init(&_attr_recursive)) - pxFailRel("pthread_mutexattr_init() failed"); - - pthread_mutexattr_settype(&_attr_recursive, PTHREAD_MUTEX_RECURSIVE); - } - - if (pthread_mutex_init(&m_mutex, &_attr_recursive)) - Console.Error("(Thread Log) Failed to initialize mutex."); -} - -Threading::MutexRecursive::~MutexRecursive() -{ - if (--_attr_refcount == 0) - pthread_mutexattr_destroy(&_attr_recursive); -} - -// This is a bit of a hackish function, which is technically unsafe, but can be useful for allowing -// the application to survive unexpected or inconvenient failures, where a mutex is deadlocked by -// a rogue thread. This function allows us to Recreate the mutex and let the deadlocked one ponder -// the deeper meanings of the universe for eternity. -void Threading::Mutex::Recreate() -{ - Detach(); - pthread_mutex_init(&m_mutex, NULL); -} - -// Returns: -// true if the mutex had to be recreated due to lock contention, or false if the mutex is safely -// unlocked. -bool Threading::Mutex::RecreateIfLocked() -{ - if (!Wait(def_detach_timeout)) - { - Recreate(); - return true; - } - return false; -} - - -// This is a direct blocking action -- very fast, very efficient, and generally very dangerous -// if used from the main GUI thread, since it typically results in an unresponsive program. -// Call this method directly only if you know the code in question will be run from threads -// other than the main thread. -void Threading::Mutex::AcquireWithoutYield() -{ - pxAssertMsg(!wxThread::IsMain(), "Unyielding mutex acquire issued from the main/gui thread. Please use Acquire() instead."); - pthread_mutex_lock(&m_mutex); -} - -bool Threading::Mutex::AcquireWithoutYield(const wxTimeSpan& timeout) -{ - wxDateTime megafail(wxDateTime::UNow() + timeout); - const timespec fail = {megafail.GetTicks(), megafail.GetMillisecond() * 1000000}; - return xpthread_mutex_timedlock(&m_mutex, &fail) == 0; -} - -void Threading::Mutex::Release() -{ - pthread_mutex_unlock(&m_mutex); -} - -bool Threading::Mutex::TryAcquire() -{ - return EBUSY != pthread_mutex_trylock(&m_mutex); -} - -// This is a wxApp-safe rendition of AcquireWithoutYield, which makes sure to execute pending app events -// and messages *if* the lock is performed from the main GUI thread. -// -void Threading::Mutex::Acquire() -{ - pthread_mutex_lock(&m_mutex); -} - -bool Threading::Mutex::Acquire(const wxTimeSpan& timeout) -{ - return AcquireWithoutYield(timeout); -} - -// Performs a wait on a locked mutex, or returns instantly if the mutex is unlocked. -// Typically this action is used to determine if a thread is currently performing some -// specific task, and to block until the task is finished (PersistentThread uses it to -// determine if the thread is running or completed, for example). -// -// Implemented internally as a simple Acquire/Release pair. -// -void Threading::Mutex::Wait() -{ - Acquire(); - Release(); -} - -// Like wait but spins for a while before sleeping the thread -void Threading::Mutex::WaitWithSpin() -{ - u32 waited = 0; - while (true) - { - if (TryAcquire()) - { - Release(); - return; - } - if (waited >= SPIN_TIME_NS) - break; - waited += ShortSpin(); - } - Wait(); -} - -void Threading::Mutex::WaitWithoutYield() -{ - AcquireWithoutYield(); - Release(); -} - -// Performs a wait on a locked mutex, or returns instantly if the mutex is unlocked. -// (Implemented internally as a simple Acquire/Release pair.) -// -// Returns: -// true if the mutex was freed and is in an unlocked state; or false if the wait timed out -// and the mutex is still locked by another thread. -// -bool Threading::Mutex::Wait(const wxTimeSpan& timeout) -{ - if (Acquire(timeout)) - { - Release(); - return true; - } - return false; -} - -bool Threading::Mutex::WaitWithoutYield(const wxTimeSpan& timeout) -{ - if (AcquireWithoutYield(timeout)) - { - Release(); - return true; - } - return false; -} - -// -------------------------------------------------------------------------------------- -// ScopedLock Implementations -// -------------------------------------------------------------------------------------- - -Threading::ScopedLock::~ScopedLock() -{ - if (m_IsLocked && m_lock) - m_lock->Release(); -} - -Threading::ScopedLock::ScopedLock(const Mutex* locker) -{ - m_IsLocked = false; - AssignAndLock(locker); -} - -Threading::ScopedLock::ScopedLock(const Mutex& locker) -{ - m_IsLocked = false; - AssignAndLock(locker); -} - -void Threading::ScopedLock::AssignAndLock(const Mutex& locker) -{ - AssignAndLock(&locker); -} - -void Threading::ScopedLock::AssignAndLock(const Mutex* locker) -{ - pxAssert(!m_IsLocked); // if we're already locked, changing the lock is bad mojo. - - m_lock = const_cast(locker); - if (!m_lock) - return; - - m_IsLocked = true; - m_lock->Acquire(); -} - -void Threading::ScopedLock::Assign(const Mutex& locker) -{ - m_lock = const_cast(&locker); -} - -void Threading::ScopedLock::Assign(const Mutex* locker) -{ - m_lock = const_cast(locker); -} - -// Provides manual unlocking of a scoped lock prior to object destruction. -void Threading::ScopedLock::Release() -{ - if (!m_IsLocked) - return; - m_IsLocked = false; - if (m_lock) - m_lock->Release(); -} - -// provides manual locking of a scoped lock, to re-lock after a manual unlocking. -void Threading::ScopedLock::Acquire() -{ - if (m_IsLocked || !m_lock) - return; - m_lock->Acquire(); - m_IsLocked = true; -} - -Threading::ScopedLock::ScopedLock(const Mutex& locker, bool isTryLock) -{ - m_lock = const_cast(&locker); - if (!m_lock) - return; - m_IsLocked = isTryLock ? m_lock->TryAcquire() : false; -} diff --git a/pcsx2/gui/Panels/BaseApplicableConfigPanel.cpp b/pcsx2/gui/Panels/BaseApplicableConfigPanel.cpp deleted file mode 100644 index c625ffa4f2..0000000000 --- a/pcsx2/gui/Panels/BaseApplicableConfigPanel.cpp +++ /dev/null @@ -1,224 +0,0 @@ -/* PCSX2 - PS2 Emulator for PCs - * Copyright (C) 2002-2010 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 . - */ - -#include "PrecompiledHeader.h" -#include "gui/App.h" - -#include "ConfigurationPanels.h" -#include "gui/Dialogs/ConfigurationDialog.h" - -#include - -using namespace Dialogs; - -// ----------------------------------------------------------------------- -// This method should be called by the parent dialog box of a configuration -// on dialog destruction. It asserts if the ApplyList hasn't been cleaned up -// and then cleans it up forcefully. -// -void ApplyStateStruct::DoCleanup() noexcept -{ - pxAssertMsg( !PanelList.empty(), "PanelList list hasn't been cleaned up." ); - PanelList.clear(); - ParentBook = NULL; -} - -void ApplyStateStruct::StartBook( wxBookCtrlBase* book ) -{ - pxAssertDev( ParentBook == NULL, "An ApplicableConfig session is already in progress." ); - ParentBook = book; -} - -void ApplyStateStruct::StartWizard() -{ - pxAssertDev( ParentBook == NULL, "An ApplicableConfig session is already in progress." ); -} - -// ----------------------------------------------------------------------- -// -// Parameters: -// pageid - identifier of the page to apply settings for. All other pages will be -// skipped. If pageid is negative (-1) then all pages are applied. -// -// Returns false if one of the panels fails input validation (in which case dialogs -// should not be closed, etc). -// -bool ApplyStateStruct::ApplyPage( int pageid ) -{ - bool retval = true; - - // Save these settings so we can restore them if the Apply fails. - - DocsModeType oldDocsMode = DocsFolderMode; - wxDirName oldSettingsFolder = SettingsFolder; - bool oldUseDefSet = UseDefaultSettingsFolder; - - AppConfig confcopy( *g_Conf ); - - try - { - PanelApplyList_t::iterator yay = PanelList.begin(); - while( yay != PanelList.end() ) - { - //DbgCon.Status( L"Writing settings for: " + (*yay)->GetLabel() ); - if( (pageid < 0) || (*yay)->IsOnPage( pageid ) ) - (*yay)->Apply(); - yay++; - } - - // If an exception is thrown above, this code below won't get run. - // (conveniently skipping any option application! :D) - - // Note: apply first, then save -- in case the apply fails. - - if( !PathDefs::GetSettings().Exists() ) - PathDefs::GetSettings().Mkdir(); - - if( !PathDefs::GetBios().Exists() ) - PathDefs::GetBios().Mkdir();//create the bios folder such that it can be opened at the first time wizard without an error message. - - AppApplySettings( &confcopy ); - } - catch( Exception::CannotApplySettings& ex ) - { - DocsFolderMode = oldDocsMode; - SettingsFolder = oldSettingsFolder; - UseDefaultSettingsFolder = oldUseDefSet; - *g_Conf = confcopy; - - if( ex.IsVerbose ) - { - Msgbox::Alert( StringUtil::UTF8StringToWxString(ex.FormatDisplayMessage()), "Cannot apply settings..." ); - - if( ex.GetPanel() != NULL ) - ex.GetPanel()->SetFocusToMe(); - } - - retval = false; - } - catch( ... ) - { - DocsFolderMode = oldDocsMode; - SettingsFolder = oldSettingsFolder; - UseDefaultSettingsFolder = oldUseDefSet; - *g_Conf = confcopy; - - throw; - } - - return retval; -} - -// Returns false if one of the panels fails input validation (in which case dialogs -// should not be closed, etc). -bool ApplyStateStruct::ApplyAll() -{ - return ApplyPage( -1 ); -} - -// -------------------------------------------------------------------------------------- -// BaseApplicableConfigPanel Implementations -// -------------------------------------------------------------------------------------- -IApplyState* BaseApplicableConfigPanel::FindApplyStateManager() const -{ - wxWindow* millrun = this->GetParent(); - while( millrun != NULL ) - { - if( BaseApplicableDialog* dialog = wxDynamicCast( millrun, BaseApplicableDialog ) ) - return (IApplyState*)dialog; - - if( ApplicableWizardPage* wizpage = wxDynamicCast( millrun, ApplicableWizardPage ) ) - return (IApplyState*)wizpage; - - millrun = millrun->GetParent(); - } - return NULL; -} - -BaseApplicableConfigPanel::~BaseApplicableConfigPanel() -{ - if( IApplyState* iapp = FindApplyStateManager() ) - iapp->GetApplyState().PanelList.remove( this ); -} - -BaseApplicableConfigPanel::BaseApplicableConfigPanel( wxWindow* parent, wxOrientation orient ) - : wxPanelWithHelpers( parent, orient ) - , m_AppStatusHelper( this ) -{ - Init(); -} - -BaseApplicableConfigPanel::BaseApplicableConfigPanel( wxWindow* parent, wxOrientation orient, const wxString& staticLabel ) - : wxPanelWithHelpers( parent, orient, staticLabel ) - , m_AppStatusHelper( this ) -{ - Init(); -} - -void BaseApplicableConfigPanel::SetFocusToMe() -{ - if( (m_OwnerBook == NULL) || (m_OwnerPage == wxID_NONE) ) return; - m_OwnerBook->SetSelection( m_OwnerPage ); -} - -void BaseApplicableConfigPanel::Init() -{ - // We need to bind to an event that occurs *after* all window and child - // window mess has been created. Unfortunately the WindowCreate event handler - // is immediate, and depends on the platform for how it "works", and thus - // useless. Solution: Create our own! :) - - //Bind( wxEVT_CREATE, &BaseApplicableConfigPanel::OnCreateWindow, this); - Bind(pxEvt_ApplySettings, &BaseApplicableConfigPanel::OnSettingsApplied, this); - - if( IApplyState* iapp = FindApplyStateManager() ) - { - ApplyStateStruct& applyState( iapp->GetApplyState() ); - m_OwnerPage = applyState.CurOwnerPage; - m_OwnerBook = applyState.ParentBook; - applyState.PanelList.push_back( this ); - } - - wxCommandEvent applyEvent( pxEvt_ApplySettings ); - applyEvent.SetId( GetId() ); - AddPendingEvent( applyEvent ); -} - -void BaseApplicableConfigPanel::OnSettingsApplied( wxCommandEvent& evt ) -{ - evt.Skip(); - if( evt.GetId() == GetId() ) AppStatusEvent_OnSettingsApplied(); -} - -void BaseApplicableConfigPanel::AppStatusEvent_OnSettingsApplied() {} - -void BaseApplicableConfigPanel::SomethingChanged() -{ - wxCommandEvent change(pxEvt_SomethingChanged); - AddPendingEvent(change); -} - - -BaseApplicableConfigPanel_SpecificConfig::BaseApplicableConfigPanel_SpecificConfig(wxWindow* parent, wxOrientation orient) - : BaseApplicableConfigPanel( parent, orient ) -{ -} - -BaseApplicableConfigPanel_SpecificConfig::BaseApplicableConfigPanel_SpecificConfig(wxWindow* parent, wxOrientation orient, const wxString& staticLabel ) - : BaseApplicableConfigPanel( parent, orient, staticLabel ) -{ -} - - diff --git a/pcsx2/gui/Panels/BiosSelectorPanel.cpp b/pcsx2/gui/Panels/BiosSelectorPanel.cpp deleted file mode 100644 index e110ce1803..0000000000 --- a/pcsx2/gui/Panels/BiosSelectorPanel.cpp +++ /dev/null @@ -1,227 +0,0 @@ -/* PCSX2 - PS2 Emulator for PCs - * Copyright (C) 2002-2021 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 . - */ - -#include "PrecompiledHeader.h" -#include "gui/App.h" -#include "ConfigurationPanels.h" - -#include "common/StringUtil.h" -#include "ps2/BiosTools.h" - -#include -#include -#include -#include - -using namespace pxSizerFlags; - -wxDECLARE_EVENT(pxEvt_BiosEnumerationFinished, wxCommandEvent); -wxDEFINE_EVENT(pxEvt_BiosEnumerationFinished, wxCommandEvent); - -// ===================================================================================================== -// BaseSelectorPanel -// ===================================================================================================== -// This base class provides event hookups and virtual functions for enumerating items in a folder. -// The most important feature of this base panel is that enumeration is done when the panel is first -// *shown*, not when it is created. This functionality allows the panel to work either as a stand alone -// dialog, a child of a complex tabbed dialog, and as a member of a wxWizard! -// -// In addition, this panel automatically intercepts and responds to DIRPICKER messages, so that your -// panel may provide a dir picker to the user. -// -// [TODO] : wxWidgets 2.9.1 provides a class for watching directory contents changes. When PCSX2 is -// upgraded to wx2.9/3.0, it should incorporate such functionality into this base class. (for now -// we just provide the user with a "refresh" button). -// -Panels::BaseSelectorPanel::BaseSelectorPanel(wxWindow* parent) - : BaseApplicableConfigPanel(parent, wxVERTICAL) -{ - Bind(wxEVT_DIRPICKER_CHANGED, &BaseSelectorPanel::OnFolderChanged, this); - Bind(wxEVT_SHOW, &BaseSelectorPanel::OnShow, this); -} - -void Panels::BaseSelectorPanel::OnShow(wxShowEvent& evt) -{ - evt.Skip(); - if (evt.IsShown()) - OnShown(); -} - -void Panels::BaseSelectorPanel::OnShown() -{ - if (!ValidateEnumerationStatus()) - DoRefresh(); -} - -bool Panels::BaseSelectorPanel::Show(bool visible) -{ - if (visible) - OnShown(); - - return BaseApplicableConfigPanel::Show(visible); -} - -void Panels::BaseSelectorPanel::RefreshSelections() -{ - ValidateEnumerationStatus(); - DoRefresh(); -} - -void Panels::BaseSelectorPanel::OnRefreshSelections(wxCommandEvent& evt) -{ - evt.Skip(); - RefreshSelections(); -} - -void Panels::BaseSelectorPanel::OnFolderChanged(wxFileDirPickerEvent& evt) -{ - evt.Skip(); - OnShown(); -} - -// ===================================================================================================== -// BiosSelectorPanel -// ===================================================================================================== -Panels::BiosSelectorPanel::BiosSelectorPanel(wxWindow* parent) - : BaseSelectorPanel(parent) -{ - SetMinWidth(480); - - m_ComboBox = new wxListBox(this, wxID_ANY, wxDefaultPosition, wxDefaultSize, 0, NULL, wxLB_SINGLE | wxLB_SORT | wxLB_NEEDED_SB); - m_FolderPicker = new DirPickerPanel(this, FolderId_Bios, - _("BIOS Search Path:"), // static box label - _("Select folder with PS2 BIOS roms") // dir picker popup label - ); - - m_ComboBox->SetFont(pxGetFixedFont(m_ComboBox->GetFont().GetPointSize() + 1)); - m_ComboBox->SetMinSize(wxSize(wxDefaultCoord, std::max(m_ComboBox->GetMinSize().GetHeight(), 96))); - - //if (InstallationMode != InstallMode_Portable) - m_FolderPicker->SetStaticDesc(_("Click the Browse button to select a different folder where PCSX2 will look for PS2 BIOS roms.")); - - wxButton* refreshButton = new wxButton(this, wxID_ANY, _("Refresh list")); - - *this += Label(_("Select a BIOS rom:")); - *this += m_ComboBox | StdExpand(); - *this += refreshButton | pxBorder(wxLEFT, StdPadding); - *this += 8; - *this += m_FolderPicker | StdExpand(); - - Bind(pxEvt_BiosEnumerationFinished, &BiosSelectorPanel::OnEnumComplete, this); - - Bind(wxEVT_BUTTON, &BiosSelectorPanel::OnRefreshSelections, this, refreshButton->GetId()); -} - -void Panels::BiosSelectorPanel::Apply() -{ - // User never visited this tab, so there's nothing to apply. - if (!m_BiosList) - return; - - int sel = m_ComboBox->GetSelection(); - if (sel == wxNOT_FOUND) - { - throw Exception::CannotApplySettings(this) - .SetDiagMsg("User did not specify a valid BIOS selection.") - .SetUserMsg("Please select a valid BIOS. If you are unable to make a valid selection then press Cancel to close the Configuration panel."); - } - - g_Conf->EmuOptions.BaseFilenames.Bios = StringUtil::wxStringToUTF8String(wxFileName((*m_BiosList)[(sptr)m_ComboBox->GetClientData(sel)]).GetFullName()); -} - -void Panels::BiosSelectorPanel::AppStatusEvent_OnSettingsApplied() -{ -} - -bool Panels::BiosSelectorPanel::ValidateEnumerationStatus() -{ - bool validated = true; - - // Impl Note: unique_ptr used so that resources get cleaned up if an exception - // occurs during file enumeration. - std::unique_ptr bioslist(new wxArrayString()); - - if (m_FolderPicker->GetPath().Exists()) - wxDir::GetAllFiles(m_FolderPicker->GetPath().ToString(), bioslist.get(), L"*.*", wxDIR_FILES); - - if (!m_BiosList || (*bioslist != *m_BiosList)) - validated = false; - - m_BiosList.swap(bioslist); - - int sel = m_ComboBox->GetSelection(); - if ((sel == wxNOT_FOUND) && !(m_ComboBox->IsEmpty())) - m_ComboBox->SetSelection(0); - - return validated; -} - -void Panels::BiosSelectorPanel::EnumThread::ExecuteTaskInThread() -{ - u32 region, version; - std::string description, zone; - for (size_t i = 0; i < m_parent.m_BiosList->GetCount(); ++i) - { - if (!IsBIOS((*m_parent.m_BiosList)[i].ToUTF8().data(), version, description, region, zone)) - continue; - Result.emplace_back(StringUtil::UTF8StringToWxString(description), i); - } - - wxCommandEvent done(pxEvt_BiosEnumerationFinished); - done.SetClientData(this); - m_parent.GetEventHandler()->AddPendingEvent(done); -} - -void Panels::BiosSelectorPanel::DoRefresh() -{ - m_ComboBox->Clear(); - if (!m_BiosList->size()) - return; - - m_ComboBox->Append(wxString("Enumerating BIOSes...")); - m_ComboBox->Update(); - - m_EnumeratorThread.reset(new EnumThread(*this)); - - m_EnumeratorThread->Start(); -} - -void Panels::BiosSelectorPanel::OnEnumComplete(wxCommandEvent& evt) -{ - auto enumThread = static_cast(evt.GetClientData()); - // Sanity check, in case m_BiosList was updated by ValidateEnumerationStatus() while the EnumThread was running - if (m_EnumeratorThread.get() != enumThread || m_BiosList->size() < enumThread->Result.size()) - return; - - const wxString currentBios(StringUtil::UTF8StringToWxString(g_Conf->EmuOptions.FullpathToBios())); - m_ComboBox->Clear(); // Clear the "Enumerating BIOSes..." - - for (const std::pair& result : enumThread->Result) - { - const int sel = m_ComboBox->Append(result.first, reinterpret_cast(static_cast(result.second))); - if (currentBios == (*m_BiosList)[result.second]) - m_ComboBox->SetSelection(sel); - } - // Select a bios if one isn't selected. - // This makes it so users don't _have_ to click on their bios, - // possibly reducing confusion. - if(m_ComboBox->GetSelection() == -1 && m_ComboBox->GetCount() > 0) - { - m_ComboBox->SetSelection(0); - } -}; - -Panels::BiosSelectorPanel::EnumThread::EnumThread(BiosSelectorPanel& parent) - : m_parent(parent){}; diff --git a/pcsx2/gui/Panels/ConfigurationPanels.h b/pcsx2/gui/Panels/ConfigurationPanels.h deleted file mode 100644 index 463e8ec637..0000000000 --- a/pcsx2/gui/Panels/ConfigurationPanels.h +++ /dev/null @@ -1,461 +0,0 @@ -/* PCSX2 - PS2 Emulator for PCs - * Copyright (C) 2002-2021 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 . - */ - -// All of the options screens in PCSX2 are implemented as panels which can be bound to -// either their own dialog boxes, or made into children of a paged Properties box. The -// paged Properties box is generally superior design, and there's a good chance we'll not -// want to deviate form that design anytime soon. But there's no harm in keeping nice -// encapsulation of panels regardless, just in case we want to shuffle things around. :) - -#pragma once - -#include -#include -#include - -#include "gui/AppCommon.h" -#include "gui/ApplyState.h" -#include "gui/i18n.h" - -namespace Panels -{ - // -------------------------------------------------------------------------------------- - // DirPickerPanel - // -------------------------------------------------------------------------------------- - // A simple panel which provides a specialized configurable directory picker with a - // "[x] Use Default setting" option, which enables or disables the panel. - // - class DirPickerPanel : public BaseApplicableConfigPanel - { - typedef BaseApplicableConfigPanel _parent; - - protected: - FoldersEnum_t m_FolderId; - wxDirPickerCtrl* m_pickerCtrl; - pxCheckBox* m_checkCtrl; - wxTextCtrl* m_textCtrl; - wxButton* b_explore; - - public: - DirPickerPanel(wxWindow* parent, FoldersEnum_t folderid, const wxString& label, const wxString& dialogLabel); - DirPickerPanel(wxWindow* parent, FoldersEnum_t folderid, const wxString& dialogLabel); - virtual ~DirPickerPanel() = default; - - void Reset(); - wxDirName GetPath() const; - void SetPath(const wxString& src); - - DirPickerPanel& SetStaticDesc(const wxString& msg); - DirPickerPanel& SetToolTip(const wxString& tip); - - wxWindowID GetId() const; - wxWindowID GetPanelId() const { return m_windowId; } - - // Overrides! - - void Apply(); - void AppStatusEvent_OnSettingsApplied(); - bool Enable(bool enable = true); - - protected: - void Init(FoldersEnum_t folderid, const wxString& dialogLabel, bool isCompact); - void InitForPortableMode(const wxString& normalized); - void InitForRegisteredMode(const wxString& normalized, const wxString& dialogLabel, bool isCompact); - - void UseDefaultPath_Click(wxCommandEvent& event); - void Explore_Click(wxCommandEvent& event); - void UpdateCheckStatus(bool someNoteworthyBoolean); - }; - - // -------------------------------------------------------------------------------------- - // DocsFolderPickerPanel / LanguageSelectionPanel - // -------------------------------------------------------------------------------------- - class DocsFolderPickerPanel : public BaseApplicableConfigPanel - { - protected: - pxRadioPanel* m_radio_UserMode; - DirPickerPanel* m_dirpicker_custom; - - public: - virtual ~DocsFolderPickerPanel() = default; - DocsFolderPickerPanel(wxWindow* parent, bool isFirstTime = true); - - void Apply(); - void AppStatusEvent_OnSettingsApplied(); - - DocsModeType GetDocsMode() const; - wxWindowID GetDirPickerId() const { return m_dirpicker_custom ? m_dirpicker_custom->GetId() : 0; } - - protected: - void OnRadioChanged(wxCommandEvent& evt); - }; - - class LanguageSelectionPanel : public BaseApplicableConfigPanel - { - protected: - LangPackList m_langs; - wxComboBox* m_picker; - - public: - virtual ~LanguageSelectionPanel() = default; - LanguageSelectionPanel(wxWindow* parent, bool showApply = true); - - void Apply(); - void AppStatusEvent_OnSettingsApplied(); - - protected: - void OnApplyLanguage_Clicked(wxCommandEvent& evt); - }; - - // -------------------------------------------------------------------------------------- - // CpuPanelEE / CpuPanelVU : Sub Panels - // -------------------------------------------------------------------------------------- - - // -------------------------------------------------------------------------------------- - // BaseAdvancedCpuOptions - // -------------------------------------------------------------------------------------- - class BaseAdvancedCpuOptions : public BaseApplicableConfigPanel_SpecificConfig - { - - protected: - pxRadioPanel* m_RoundModePanel; - pxRadioPanel* m_ClampModePanel; - - public: - BaseAdvancedCpuOptions(wxWindow* parent); - virtual ~BaseAdvancedCpuOptions() = default; - - void RestoreDefaults(); - - protected: - void OnRestoreDefaults(wxCommandEvent& evt); - void ApplyRoundmode(SSE_MXCSR& mxcsr); - }; - - // -------------------------------------------------------------------------------------- - // AdvancedOptionsFPU / AdvancedOptionsVU - // -------------------------------------------------------------------------------------- - class AdvancedOptionsFPU : public BaseAdvancedCpuOptions - { - public: - AdvancedOptionsFPU(wxWindow* parent); - virtual ~AdvancedOptionsFPU() = default; - void Apply(); - void AppStatusEvent_OnSettingsApplied(); - void ApplyConfigToGui(AppConfig& configToApply, int flags = 0); - }; - - class AdvancedOptionsVU : public BaseAdvancedCpuOptions - { - public: - AdvancedOptionsVU(wxWindow* parent); - virtual ~AdvancedOptionsVU() = default; - void Apply(); - void AppStatusEvent_OnSettingsApplied(); - void ApplyConfigToGui(AppConfig& configToApply, int flags = 0); - }; - - // -------------------------------------------------------------------------------------- - // CpuPanelEE / CpuPanelVU : Actual Panels - // -------------------------------------------------------------------------------------- - class CpuPanelEE : public BaseApplicableConfigPanel_SpecificConfig - { - protected: - pxRadioPanel* m_panel_RecEE; - pxRadioPanel* m_panel_RecIOP; - pxCheckBox* m_check_EECacheEnable; - AdvancedOptionsFPU* m_advancedOptsFpu; - wxButton* m_button_RestoreDefaults; - - public: - CpuPanelEE(wxWindow* parent); - virtual ~CpuPanelEE() = default; - - void Apply(); - void AppStatusEvent_OnSettingsApplied(); - void ApplyConfigToGui(AppConfig& configToApply, int flags = 0); - - protected: - void OnRestoreDefaults(wxCommandEvent& evt); - void EECache_Event(wxCommandEvent& evt); - }; - - class CpuPanelVU : public BaseApplicableConfigPanel_SpecificConfig - { - protected: - pxRadioPanel* m_panel_VU0; - pxRadioPanel* m_panel_VU1; - Panels::AdvancedOptionsVU* m_advancedOptsVu; - wxButton* m_button_RestoreDefaults; - - public: - CpuPanelVU(wxWindow* parent); - virtual ~CpuPanelVU() = default; - - void Apply(); - void AppStatusEvent_OnSettingsApplied(); - void ApplyConfigToGui(AppConfig& configToApply, int flags = 0); - - protected: - void OnRestoreDefaults(wxCommandEvent& evt); - }; - - // -------------------------------------------------------------------------------------- - // FramelimiterPanel - // -------------------------------------------------------------------------------------- - class FramelimiterPanel : public BaseApplicableConfigPanel_SpecificConfig - { - protected: - pxCheckBox* m_check_LimiterDisable; - wxSpinCtrl* m_spin_NominalPct; - wxSpinCtrl* m_spin_SlomoPct; - wxSpinCtrl* m_spin_TurboPct; - - wxTextCtrl* m_text_BaseNtsc; - wxTextCtrl* m_text_BasePal; - - wxCheckBox* m_SkipperEnable; - wxCheckBox* m_TurboSkipEnable; - wxSpinCtrl* m_spin_SkipThreshold; - - public: - FramelimiterPanel(wxWindow* parent); - virtual ~FramelimiterPanel() = default; - - void Apply(); - void AppStatusEvent_OnSettingsApplied(); - void ApplyConfigToGui(AppConfig& configToApply, int flags = 0); - }; - - // -------------------------------------------------------------------------------------- - // GSWindowSettingsPanel - // -------------------------------------------------------------------------------------- - class GSWindowSettingsPanel : public BaseApplicableConfigPanel_SpecificConfig - { - protected: - // Exclusive mode is currently not used (true for svn r4399). - // PCSX2 has partial infrastructure for it: - // - GS seem to support it (it supports the API and has implementation), but I don't know if it ever got called. - // - BUT, the configuration (AppConfig, and specifically GSWindowOptions) do NOT seem to have a place to store this value, - // and PCSX2's code doesn't seem to use this API anywhere. So, no exclusive mode for now. - // - avih - - wxComboBox* m_combo_AspectRatio; - wxComboBox* m_combo_FMVAspectRatioSwitch; - wxComboBox* m_combo_vsync; - - pxCheckBox* m_check_CloseGS; - pxCheckBox* m_check_SizeLock; - pxCheckBox* m_check_VsyncEnable; - pxCheckBox* m_check_Fullscreen; - - pxCheckBox* m_check_HideMouse; - pxCheckBox* m_check_DclickFullscreen; - - wxTextCtrl* m_text_WindowWidth; - wxTextCtrl* m_text_WindowHeight; - - public: - GSWindowSettingsPanel(wxWindow* parent); - virtual ~GSWindowSettingsPanel() = default; - void Apply(); - void AppStatusEvent_OnSettingsApplied(); - void ApplyConfigToGui(AppConfig& configToApply, int flags = 0); - }; - - class VideoPanel : public BaseApplicableConfigPanel_SpecificConfig - { - protected: - pxCheckBox* m_check_SynchronousGS; - wxSpinCtrl* m_spinner_VsyncQueue; - wxButton* m_restore_defaults; - FramelimiterPanel* m_fpan; - - public: - VideoPanel(wxWindow* parent); - virtual ~VideoPanel() = default; - void Apply(); - void AppStatusEvent_OnSettingsApplied(); - void Defaults_Click(wxCommandEvent& evt); - void ApplyConfigToGui(AppConfig& configToApply, int flags = 0); - - protected: - void OnOpenWindowSettings(wxCommandEvent& evt); - }; - - // -------------------------------------------------------------------------------------- - // SpeedHacksPanel - // -------------------------------------------------------------------------------------- - class SpeedHacksPanel : public BaseApplicableConfigPanel_SpecificConfig - { - protected: - wxBoxSizer* m_sizer; - wxFlexGridSizer* s_table; - - pxCheckBox* m_check_Enable; - wxButton* m_button_Defaults; - - wxPanelWithHelpers* m_eeRateSliderPanel; - wxPanelWithHelpers* m_eeSkipSliderPanel; - wxSlider* m_slider_eeRate; - wxSlider* m_slider_eeSkip; - pxStaticText* m_msg_eeRate; - pxStaticText* m_msg_eeSkip; - - pxCheckBox* m_check_intc; - pxCheckBox* m_check_waitloop; - pxCheckBox* m_check_fastCDVD; - pxCheckBox* m_check_vuFlagHack; - pxCheckBox* m_check_vuThread; - pxCheckBox* m_check_vu1Instant; - - public: - virtual ~SpeedHacksPanel() = default; - SpeedHacksPanel(wxWindow* parent); - void Apply(); - void EnableStuff(AppConfig* configToUse = NULL); - void AppStatusEvent_OnSettingsApplied(); - void ApplyConfigToGui(AppConfig& configToApply, int flags = 0); - - protected: - const wxChar* GetEECycleRateSliderMsg(int val); - const wxChar* GetEECycleSkipSliderMsg(int val); - void SetEEcycleSliderMsg(); - void SetVUcycleSliderMsg(); - - void OnEnable_Toggled(wxCommandEvent& evt); - void Defaults_Click(wxCommandEvent& evt); - void EECycleRate_Scroll(wxScrollEvent& event); - void VUCycleRate_Scroll(wxScrollEvent& event); - }; - - // -------------------------------------------------------------------------------------- - // GameFixesPanel - // -------------------------------------------------------------------------------------- - class GameFixesPanel : public BaseApplicableConfigPanel_SpecificConfig - { - protected: - pxCheckBox* m_checkbox[GamefixId_COUNT]; - pxCheckBox* m_check_Enable; - - public: - GameFixesPanel(wxWindow* parent); - virtual ~GameFixesPanel() = default; - void EnableStuff(AppConfig* configToUse = NULL); - void OnEnable_Toggled(wxCommandEvent& evt); - void Apply(); - void AppStatusEvent_OnSettingsApplied(); - void ApplyConfigToGui(AppConfig& configToApply, int flags = 0); - }; - - class SettingsDirPickerPanel : public DirPickerPanel - { - public: - SettingsDirPickerPanel(wxWindow* parent); - }; - - // -------------------------------------------------------------------------------------- - // StandardPathsPanel - // -------------------------------------------------------------------------------------- - class StandardPathsPanel : public BaseApplicableConfigPanel - { - public: - StandardPathsPanel(wxWindow* parent); - void Apply(); - void AppStatusEvent_OnSettingsApplied(); - }; - - // -------------------------------------------------------------------------------------- - // BaseSelectorPanel - // -------------------------------------------------------------------------------------- - class BaseSelectorPanel : public BaseApplicableConfigPanel - { - typedef BaseApplicableConfigPanel _parent; - - public: - virtual ~BaseSelectorPanel() = default; - BaseSelectorPanel(wxWindow* parent); - - virtual void RefreshSelections(); - - virtual bool Show(bool visible = true); - virtual void OnShown(); - virtual void OnFolderChanged(wxFileDirPickerEvent& evt); - - protected: - void OnRefreshSelections(wxCommandEvent& evt); - - // This method is called when the enumeration contents have changed. The implementing - // class should populate or re-populate listbox/selection components when invoked. - // - virtual void DoRefresh() = 0; - - // This method is called when an event has indicated that the enumeration status of the - // selector may have changed. The implementing class should re-enumerate the folder/source - // data and return either TRUE (enumeration status unchanged) or FALSE (enumeration status - // changed). - // - // If the implementation returns FALSE, then the BaseSelectorPanel will invoke a call to - // DoRefresh() [which also must be implemented] - // - virtual bool ValidateEnumerationStatus() = 0; - - void OnShow(wxShowEvent& evt); - }; - - // -------------------------------------------------------------------------------------- - // BiosSelectorPanel - // -------------------------------------------------------------------------------------- - class BiosSelectorPanel : public BaseSelectorPanel - { - protected: - std::unique_ptr m_BiosList; - wxListBox* m_ComboBox; - DirPickerPanel* m_FolderPicker; - - public: - BiosSelectorPanel(wxWindow* parent); - virtual ~BiosSelectorPanel() = default; - - class EnumThread : public Threading::pxThread - { - public: - std::vector> Result; - - virtual ~EnumThread() - { - try - { - pxThread::Cancel(); - } - DESTRUCTOR_CATCHALL - } - EnumThread(BiosSelectorPanel& parent); - - protected: - void ExecuteTaskInThread(); - BiosSelectorPanel& m_parent; - }; - - protected: - virtual void Apply(); - virtual void AppStatusEvent_OnSettingsApplied(); - virtual void DoRefresh(); - virtual void OnEnumComplete(wxCommandEvent& evt); - virtual bool ValidateEnumerationStatus(); - - std::unique_ptr m_EnumeratorThread; - }; -} // namespace Panels diff --git a/pcsx2/gui/Panels/CpuPanel.cpp b/pcsx2/gui/Panels/CpuPanel.cpp deleted file mode 100644 index 4e2c09c59c..0000000000 --- a/pcsx2/gui/Panels/CpuPanel.cpp +++ /dev/null @@ -1,398 +0,0 @@ -/* PCSX2 - PS2 Emulator for PCs - * Copyright (C) 2002-2010 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 . - */ - -#include "PrecompiledHeader.h" -#include "ConfigurationPanels.h" - -using namespace pxSizerFlags; - -Panels::BaseAdvancedCpuOptions::BaseAdvancedCpuOptions( wxWindow* parent ) - : BaseApplicableConfigPanel_SpecificConfig( parent ) -{ - wxStaticBoxSizer* s_round( new wxStaticBoxSizer( wxVERTICAL, this, _("Round Mode") ) ); - wxStaticBoxSizer* s_clamp( new wxStaticBoxSizer( wxVERTICAL, this, _("Clamping Mode") ) ); - - const RadioPanelItem RoundModeChoices[] = - { - RadioPanelItem(_("Nearest")), - RadioPanelItem(_("Negative")), - RadioPanelItem(_("Positive")), - RadioPanelItem(_("Chop / Zero")) - }; - - const RadioPanelItem ClampModeChoices[] = - { - RadioPanelItem(_("None")), - RadioPanelItem(_("Normal")), - }; - - m_RoundModePanel = new pxRadioPanel( this, RoundModeChoices ); - m_ClampModePanel = new pxRadioPanel( this, ClampModeChoices ); - - // Highlight Default Options: - - m_RoundModePanel->SetDefaultItem( 3 ); - m_ClampModePanel->SetDefaultItem( 1 ); - - // --------------------------------- - // The Fitting And Sizing Area - // --------------------------------- - - wxFlexGridSizer& grid = *new wxFlexGridSizer( 4 ); - - // Clever proportions selected for a fairly nice spacing. - - grid.AddGrowableCol( 0, 22 ); // round mode box size - grid.AddGrowableCol( 1, 22 ); // clamp mode box size - - *s_round+= m_RoundModePanel | StdExpand(); - *s_clamp+= m_ClampModePanel | StdExpand(); - - grid += s_round | SubGroup(); - grid += s_clamp | SubGroup(); - - *this += grid | StdExpand(); -} - -void Panels::BaseAdvancedCpuOptions::OnRestoreDefaults(wxCommandEvent& evt) -{ - RestoreDefaults(); - evt.Skip(); -} - -void Panels::BaseAdvancedCpuOptions::RestoreDefaults() -{ - AppConfig def; // created with default values - def.EnablePresets = false; // disable presets otherwise it'll disable some widgets - ApplyConfigToGui(def); -} - -Panels::AdvancedOptionsFPU::AdvancedOptionsFPU( wxWindow* parent ) - : BaseAdvancedCpuOptions( parent ) -{ - SetName( L"AdvancedOptionsFPU" ); - AddFrame(_("EE/FPU Advanced Recompiler Options")); - - m_ClampModePanel->Append(_("Extra + Preserve Sign")); - m_ClampModePanel->Append(_("Full")); - - m_RoundModePanel->Realize(); - m_ClampModePanel->Realize(); -} - - -Panels::AdvancedOptionsVU::AdvancedOptionsVU( wxWindow* parent ) - : BaseAdvancedCpuOptions( parent ) -{ - SetName( L"AdvancedOptionsVU" ); - AddFrame(_("VU0 / VU1 Advanced Recompiler Options")); - - m_ClampModePanel->Append(_("Extra")); - m_ClampModePanel->Append(_("Extra + Preserve Sign")); - - m_RoundModePanel->Realize(); - m_ClampModePanel->Realize(); -} - -Panels::CpuPanelEE::CpuPanelEE( wxWindow* parent ) - : BaseApplicableConfigPanel_SpecificConfig( parent ) -{ - *this += Text( pxE(L"Notice: Most games are fine with the default options.") - ) | StdExpand(); - - const RadioPanelItem tbl_CpuTypes_EE[] = - { - RadioPanelItem(_("Interpreter")) - .SetToolTip(_("Quite possibly the slowest thing in the universe.")), - - RadioPanelItem(_("Recompiler")) - .SetToolTip(_("Performs just-in-time binary translation of 64-bit MIPS-IV machine code to x86.")) - }; - - const RadioPanelItem tbl_CpuTypes_IOP[] = - { - RadioPanelItem(_("Interpreter")) - .SetToolTip(_("Pretty slow; provided for diagnostic purposes only.")), - - RadioPanelItem(_("Recompiler")) - .SetToolTip(_("Performs just-in-time binary translation of 32-bit MIPS-I machine code to x86.")) - }; - - - m_panel_RecEE = &(new pxRadioPanel( this, tbl_CpuTypes_EE ))->SetDefaultItem( 1 ); - m_panel_RecIOP = &(new pxRadioPanel( this, tbl_CpuTypes_IOP ))->SetDefaultItem( 1 ); - - m_panel_RecEE->Realize(); - m_panel_RecIOP->Realize(); - - // --------------------------------- - // The Fitting And Sizing Area - // --------------------------------- - - wxFlexGridSizer& s_recs( *new wxFlexGridSizer( 2 ) ); - - s_recs.AddGrowableCol( 0, 1 ); - s_recs.AddGrowableCol( 1, 1 ); - - // i18n: No point in translating PS2 CPU names :) - wxStaticBoxSizer& s_ee ( *new wxStaticBoxSizer( wxVERTICAL, this, L"EmotionEngine" ) ); - wxStaticBoxSizer& s_iop ( *new wxStaticBoxSizer( wxVERTICAL, this, L"IOP" ) ); - - s_ee += m_panel_RecEE | StdExpand(); - s_ee += m_check_EECacheEnable = &(new pxCheckBox( this, _("Enable EE Cache (Slower)") ))->SetToolTip(_("Interpreter only; provided for diagnostic")); - s_iop += m_panel_RecIOP | StdExpand(); - - s_recs += s_ee | SubGroup(); - s_recs += s_iop | SubGroup(); - - - *this += &s_recs | StdExpand(); - - // move following line down so EE and VU panels look more uniform. - // Use an empty Label (std expanded) so it work with custom font sizes, too. - //*this += Label(_("")) | StdExpand(); - *this += new wxStaticLine( this ) | pxExpand.Border(wxALL, 18); - *this += (m_advancedOptsFpu = new AdvancedOptionsFPU( this )) | StdExpand(); - - *this += 12; - m_button_RestoreDefaults = new wxButton(this, wxID_DEFAULT, _("Restore Defaults")); - *this += m_button_RestoreDefaults | StdButton(); - - Bind(wxEVT_BUTTON, &CpuPanelEE::OnRestoreDefaults, this, wxID_DEFAULT); - Bind(wxEVT_RADIOBUTTON, &CpuPanelEE::EECache_Event, this); -} - -Panels::CpuPanelVU::CpuPanelVU( wxWindow* parent ) - : BaseApplicableConfigPanel_SpecificConfig( parent ) -{ - *this += Text( pxE( L"Notice: Most games are fine with the default options.") - ) | StdExpand(); - - const RadioPanelItem tbl_CpuTypes_VU[] = - { - RadioPanelItem(_("Interpreter")) - .SetToolTip(_("Vector Unit Interpreter. Slow and not very compatible. Only use for diagnostics.")), - - RadioPanelItem(_("microVU Recompiler")) - .SetToolTip(_("New Vector Unit recompiler with much improved compatibility. Recommended.")) - }; - - m_panel_VU0 = &(new pxRadioPanel( this, tbl_CpuTypes_VU )) ->SetDefaultItem( 1 ); - m_panel_VU1 = &(new pxRadioPanel( this, tbl_CpuTypes_VU )) ->SetDefaultItem( 1 ); - - m_panel_VU0->Realize(); - m_panel_VU1->Realize(); - - // --------------------------------- - // The Fitting And Sizing Area - // --------------------------------- - - wxFlexGridSizer& s_recs( *new wxFlexGridSizer( 2 ) ); - - s_recs.AddGrowableCol( 0, 1 ); - s_recs.AddGrowableCol( 1, 1 ); - - wxStaticBoxSizer& s_vu0( *new wxStaticBoxSizer( wxVERTICAL, this, L"VU0" ) ); - wxStaticBoxSizer& s_vu1( *new wxStaticBoxSizer( wxVERTICAL, this, L"VU1" ) ); - - s_vu0 += m_panel_VU0 | StdExpand(); - s_vu1 += m_panel_VU1 | StdExpand(); - - s_recs += s_vu0 | SubGroup(); - s_recs += s_vu1 | SubGroup(); - - *this += &s_recs | StdExpand(); - *this += new wxStaticLine( this ) | pxExpand.Border(wxALL, 18); - *this += ( m_advancedOptsVu=new AdvancedOptionsVU( this )) | StdExpand(); - - *this += 12; - m_button_RestoreDefaults = new wxButton(this, wxID_DEFAULT, _("Restore Defaults")); - *this += m_button_RestoreDefaults | StdButton(); - - Bind(wxEVT_BUTTON, &CpuPanelVU::OnRestoreDefaults, this, wxID_DEFAULT); -} - -void Panels::CpuPanelEE::Apply() -{ - Pcsx2Config::RecompilerOptions& recOps( g_Conf->EmuOptions.Cpu.Recompiler ); - recOps.EnableEE = !!m_panel_RecEE->GetSelection(); - recOps.EnableIOP = !!m_panel_RecIOP->GetSelection(); - recOps.EnableEECache = m_check_EECacheEnable->GetValue(); -} - -void Panels::CpuPanelEE::AppStatusEvent_OnSettingsApplied() -{ - ApplyConfigToGui( *g_Conf ); -} - -void Panels::CpuPanelEE::ApplyConfigToGui( AppConfig& configToApply, int flags ) -{ - const Pcsx2Config::RecompilerOptions& recOps( configToApply.EmuOptions.Cpu.Recompiler ); - m_panel_RecEE->SetSelection( (int)recOps.EnableEE ); - m_panel_RecIOP->SetSelection( (int)recOps.EnableIOP ); - - m_panel_RecEE->Enable(!configToApply.EnablePresets); - m_panel_RecIOP->Enable(!configToApply.EnablePresets); - - //EECache option is exclusive to the EE Interpreter. - m_check_EECacheEnable->SetValue(recOps.EnableEECache); - m_check_EECacheEnable->Enable(!configToApply.EnablePresets && m_panel_RecEE->GetSelection() == 0); - m_button_RestoreDefaults->Enable(!configToApply.EnablePresets); - - if( flags & AppConfig::APPLY_FLAG_MANUALLY_PROPAGATE ) - { - m_advancedOptsFpu->ApplyConfigToGui( configToApply, true ); - } -} - -void Panels::CpuPanelEE::OnRestoreDefaults(wxCommandEvent &evt) -{ - AppConfig def; // created with default values - def.EnablePresets = false; // disable presets otherwise it'll disable some widgets - ApplyConfigToGui(def); - - if( BaseAdvancedCpuOptions* opts = (BaseAdvancedCpuOptions*)FindWindowByName(L"AdvancedOptionsFPU") ) - opts->RestoreDefaults(); - - evt.Skip(); -} - - -void Panels::CpuPanelVU::Apply() -{ - Pcsx2Config::RecompilerOptions& recOps( g_Conf->EmuOptions.Cpu.Recompiler ); - recOps.EnableVU0 = m_panel_VU0->GetSelection() > 0; - recOps.EnableVU1 = m_panel_VU1->GetSelection() > 0; -} - -void Panels::CpuPanelVU::AppStatusEvent_OnSettingsApplied() -{ - ApplyConfigToGui( *g_Conf ); -} - -void Panels::CpuPanelVU::ApplyConfigToGui( AppConfig& configToApply, int flags ) -{ - Pcsx2Config::RecompilerOptions& recOps( configToApply.EmuOptions.Cpu.Recompiler ); - m_panel_VU0->SetSelection( recOps.EnableVU0 ? 1 : 0 ); - m_panel_VU1->SetSelection( recOps.EnableVU1 ? 1 : 0 ); - m_panel_VU0->Enable(!configToApply.EnablePresets); - m_panel_VU1->Enable(!configToApply.EnablePresets); - m_button_RestoreDefaults->Enable(!configToApply.EnablePresets); - - if ( flags & AppConfig::APPLY_FLAG_MANUALLY_PROPAGATE ) - { - m_advancedOptsVu->ApplyConfigToGui( configToApply, true ); - } -} - - -void Panels::CpuPanelVU::OnRestoreDefaults(wxCommandEvent &evt) -{ - AppConfig def; // created with default values - def.EnablePresets = false; // disable presets otherwise it'll disable some widgets - ApplyConfigToGui(def); - - if( BaseAdvancedCpuOptions* opts = (BaseAdvancedCpuOptions*)FindWindowByName(L"AdvancedOptionsVU") ) - opts->RestoreDefaults(); - - evt.Skip(); -} - -void Panels::BaseAdvancedCpuOptions::ApplyRoundmode( SSE_MXCSR& mxcsr ) -{ - mxcsr.RoundingControl = m_RoundModePanel->GetSelection(); - mxcsr.DenormalsAreZero = 1; - mxcsr.FlushToZero = 1; -} - -void Panels::AdvancedOptionsFPU::Apply() -{ - Pcsx2Config::CpuOptions& cpuOps( g_Conf->EmuOptions.Cpu ); - Pcsx2Config::RecompilerOptions& recOps( cpuOps.Recompiler ); - - cpuOps.sseMXCSR = Pcsx2Config::CpuOptions().sseMXCSR; // set default - ApplyRoundmode( cpuOps.sseMXCSR ); - - const int clampSel = m_ClampModePanel->GetSelection(); - - recOps.fpuOverflow = clampSel >= 1; - recOps.fpuExtraOverflow = clampSel >= 2; - recOps.fpuFullMode = clampSel >= 3; - - cpuOps.ApplySanityCheck(); -} - -void Panels::AdvancedOptionsFPU::AppStatusEvent_OnSettingsApplied() -{ - ApplyConfigToGui( *g_Conf ); -} - -void Panels::AdvancedOptionsFPU::ApplyConfigToGui( AppConfig& configToApply, int flags ) -{ - const Pcsx2Config::CpuOptions& cpuOps( configToApply.EmuOptions.Cpu ); - const Pcsx2Config::RecompilerOptions& recOps( cpuOps.Recompiler ); - - m_RoundModePanel->SetSelection( cpuOps.sseMXCSR.RoundingControl ); - - if( recOps.fpuFullMode ) m_ClampModePanel->SetSelection( 3 ); - else if( recOps.fpuExtraOverflow ) m_ClampModePanel->SetSelection( 2 ); - else if( recOps.fpuOverflow ) m_ClampModePanel->SetSelection( 1 ); - else m_ClampModePanel->SetSelection( 0 ); - - this->Enable(!configToApply.EnablePresets); -} - -void Panels::AdvancedOptionsVU::Apply() -{ - Pcsx2Config::CpuOptions& cpuOps( g_Conf->EmuOptions.Cpu ); - Pcsx2Config::RecompilerOptions& recOps( cpuOps.Recompiler ); - - cpuOps.sseVUMXCSR = Pcsx2Config::CpuOptions().sseVUMXCSR; // set default - ApplyRoundmode( cpuOps.sseVUMXCSR ); - - const int clampSel = m_ClampModePanel->GetSelection(); - - recOps.vuOverflow = clampSel >= 1; - recOps.vuExtraOverflow = clampSel >= 2; - recOps.vuSignOverflow = clampSel >= 3; - - cpuOps.ApplySanityCheck(); -} - -void Panels::AdvancedOptionsVU::AppStatusEvent_OnSettingsApplied() -{ - ApplyConfigToGui( *g_Conf ); -} - -void Panels::AdvancedOptionsVU::ApplyConfigToGui( AppConfig& configToApply, int flags ) -{ - const Pcsx2Config::CpuOptions& cpuOps( configToApply.EmuOptions.Cpu ); - const Pcsx2Config::RecompilerOptions& recOps( cpuOps.Recompiler ); - - m_RoundModePanel->SetSelection( cpuOps.sseVUMXCSR.RoundingControl ); - - if( recOps.vuSignOverflow ) m_ClampModePanel->SetSelection( 3 ); - else if( recOps.vuExtraOverflow ) m_ClampModePanel->SetSelection( 2 ); - else if( recOps.vuOverflow ) m_ClampModePanel->SetSelection( 1 ); - else m_ClampModePanel->SetSelection( 0 ); - - this->Enable(!configToApply.EnablePresets); -} - -void Panels::CpuPanelEE::EECache_Event(wxCommandEvent& event) -{ - m_check_EECacheEnable->Enable(m_panel_RecEE->GetSelection() == 0); - event.Skip(); -} diff --git a/pcsx2/gui/Panels/DirPickerPanel.cpp b/pcsx2/gui/Panels/DirPickerPanel.cpp deleted file mode 100644 index 97e59c8e7e..0000000000 --- a/pcsx2/gui/Panels/DirPickerPanel.cpp +++ /dev/null @@ -1,298 +0,0 @@ -/* PCSX2 - PS2 Emulator for PCs - * Copyright (C) 2002-2010 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 . - */ - -#include "PrecompiledHeader.h" -#include "ConfigurationPanels.h" -#include "gui/Dialogs/ModalPopups.h" - -#include -#include -#include -#include - -using namespace pxSizerFlags; - -static wxString GetNormalizedConfigFolder( FoldersEnum_t folderId ) -{ - return Path::Normalize( g_Conf->Folders.IsDefault( folderId ) ? PathDefs::Get(folderId) : g_Conf->Folders[folderId] ); -} - -// Pass me TRUE if the default path is to be used, and the DirPickerCtrl disabled from use. -void Panels::DirPickerPanel::UpdateCheckStatus( bool someNoteworthyBoolean ) -{ - if (!m_pickerCtrl) return; - - m_pickerCtrl->Enable( !someNoteworthyBoolean ); - if (someNoteworthyBoolean) - { - wxString normalized( Path::Normalize( PathDefs::Get( m_FolderId ) ) ); - m_pickerCtrl->SetPath( normalized ); - - wxFileDirPickerEvent event( m_pickerCtrl->GetEventType(), m_pickerCtrl, m_pickerCtrl->GetId(), normalized ); - m_pickerCtrl->GetEventHandler()->ProcessEvent(event); - } -} - -void Panels::DirPickerPanel::UseDefaultPath_Click( wxCommandEvent &evt ) -{ - evt.Skip(); - pxAssert( (m_pickerCtrl != NULL) && (m_checkCtrl != NULL) ); - UpdateCheckStatus( m_checkCtrl ? m_checkCtrl->IsChecked() : false ); -} - -void Panels::DirPickerPanel::Explore_Click( wxCommandEvent &evt ) -{ - wxDirName path( GetPath() ); - - if (!pxAssertDev( path.IsOk(), "This DirPickerPanel could not find valid or meaningful path data." )) - return; - - if (!path.Exists()) - { - wxDialogWithHelpers createPathDlg( NULL, _("Path does not exist") ); - createPathDlg.SetMinWidth( 600 ); - - createPathDlg += createPathDlg.Label( path.ToString() ) | StdCenter(); - - createPathDlg += createPathDlg.Heading( pxE( L"The specified path/directory does not exist. Would you like to create it?" ) - ); - - wxWindowID result = pxIssueConfirmation( createPathDlg, - MsgButtons().Custom(_("Create"), "create").Cancel(), - L"DirPicker:CreateOnExplore" - ); - - if( result == wxID_CANCEL ) return; - path.Mkdir(); - } - - wxLaunchDefaultApplication(path.ToString()); -} - -// There are two constructors. See the details for the 'label' parameter below for details. -// -// Parameters: -// label - label for the StaticBox that surrounds the dir picker control. If the 'label' -// parameter is not specified, the layout of the panel is assumed to be "compact" which -// lacks a static box and compresses itself onto a single line. Compact mode may be useful -// for situations where the expanded format is just too invasive. -// -Panels::DirPickerPanel::DirPickerPanel( wxWindow* parent, FoldersEnum_t folderid, const wxString& label, const wxString& dialogLabel ) - : BaseApplicableConfigPanel( parent, wxVERTICAL, label ) -{ - Init( folderid, dialogLabel, false ); -} - -Panels::DirPickerPanel::DirPickerPanel( wxWindow* parent, FoldersEnum_t folderid, const wxString& dialogLabel ) - : BaseApplicableConfigPanel( parent, wxVERTICAL ) -{ - Init( folderid, dialogLabel, true ); -} - -void Panels::DirPickerPanel::Init( FoldersEnum_t folderid, const wxString& dialogLabel, bool isCompact ) -{ - m_FolderId = folderid; - m_pickerCtrl = NULL; - m_checkCtrl = NULL; - m_textCtrl = NULL; - b_explore = NULL; - - wxString normalized (GetNormalizedConfigFolder( m_FolderId )); - - if (wxFile::Exists( normalized )) - { - // The default path is invalid... What should we do here? hmm.. - } - -// if (InstallationMode == InstallMode_Portable) -// InitForPortableMode(normalized); -// else - InitForRegisteredMode(normalized, dialogLabel, isCompact); - - // wx warns when paths don't exist, but this is typically normal when the wizard - // creates its child controls. So let's ignore them. - wxDoNotLogInThisScope please; - AppStatusEvent_OnSettingsApplied(); // forces default settings based on g_Conf -} - - -// InitForPortableMode is currently unused because portable now allows fully custom folders. -void Panels::DirPickerPanel::InitForPortableMode( const wxString& normalized ) -{ - // In portable mode the path is unchangeable, and only a "open in explorer" button is provided (which - // itself is windows-only at this time). - - m_textCtrl = new wxTextCtrl( this, wxID_ANY, wxEmptyString, wxDefaultPosition, wxDefaultSize, wxTE_READONLY ); - - wxFlexGridSizer& s_lower( *new wxFlexGridSizer( 2, 0, StdPadding ) ); - s_lower.AddGrowableCol( 0, 1 ); - - s_lower += m_textCtrl | pxExpand; - if (b_explore) - s_lower += b_explore; - - *this += s_lower | pxExpand.Border(wxLEFT | wxRIGHT, StdPadding); - - m_textCtrl->Disable(); -} - -void Panels::DirPickerPanel::InitForRegisteredMode( const wxString& normalized, const wxString& dialogLabel, bool isCompact ) -{ - if (!isCompact) - { - m_checkCtrl = new pxCheckBox( this, _("Use default setting") ); - - pxSetToolTip( m_checkCtrl, pxEt( L"When checked this folder will automatically reflect the default associated with PCSX2's current usermode setting. " ) - ); - - Bind(wxEVT_CHECKBOX, &DirPickerPanel::UseDefaultPath_Click, this, m_checkCtrl->GetId()); - } - - // Force the Dir Picker to use a text control. This isn't standard on Linux/GTK but it's much - // more usable, so to hell with standards. - - m_pickerCtrl = new wxDirPickerCtrl( this, wxID_ANY, wxEmptyString, dialogLabel, - wxDefaultPosition, wxDefaultSize, wxDIRP_USE_TEXTCTRL | wxDIRP_DIR_MUST_EXIST - ); - -#ifndef __WXGTK__ - // GTK+ : The wx implementation of Explore isn't reliable, so let's not even put the - // button on the dialogs for now. - - wxButton* b_explore( new wxButton( this, wxID_ANY, _("Open in Explorer") ) ); - pxSetToolTip( b_explore, _("Open an explorer window to this folder.") ); - Bind(wxEVT_BUTTON, &DirPickerPanel::Explore_Click, this, b_explore->GetId()); -#endif - - if (isCompact) - { - wxFlexGridSizer& s_compact( *new wxFlexGridSizer( 2, 0, 4 ) ); - s_compact.AddGrowableCol( 0 ); - s_compact += m_pickerCtrl | pxExpand; -#ifndef __WXGTK__ - s_compact += b_explore; -#endif - *this += s_compact | pxExpand; - } - else - { - wxBoxSizer& s_lower( *new wxBoxSizer( wxHORIZONTAL ) ); - - s_lower += m_checkCtrl | pxMiddle; - if (b_explore) - { - s_lower += pxStretchSpacer(1); - s_lower += b_explore; - } - - *this += m_pickerCtrl | pxExpand.Border(wxLEFT | wxRIGHT | wxTOP, StdPadding); - *this += s_lower | pxExpand.Border(wxLEFT | wxRIGHT, StdPadding); - } -} - -Panels::DirPickerPanel& Panels::DirPickerPanel::SetStaticDesc( const wxString& msg ) -{ - GetSizer()->Insert( 0, &Heading( msg ), pxExpand ); - return *this; -} - -Panels::DirPickerPanel& Panels::DirPickerPanel::SetToolTip( const wxString& tip ) -{ - pxSetToolTip( this, tip ); - return *this; -} - -wxWindowID Panels::DirPickerPanel::GetId() const -{ - return m_pickerCtrl->GetId(); -} - -void Panels::DirPickerPanel::Reset() -{ - const bool isDefault = g_Conf->Folders.IsDefault( m_FolderId ); - - if (m_checkCtrl) - m_checkCtrl->SetValue( isDefault ); - - if (m_pickerCtrl) - { - // Important! The dirpicker panel stuff, due to however it's put together - // needs to check the enable status of this panel before setting the child - // panel's enable status. - - m_pickerCtrl->Enable( IsEnabled() ? ( m_checkCtrl ? !isDefault : true ) : false ); - m_pickerCtrl->SetPath( GetNormalizedConfigFolder( m_FolderId ) ); - } - - if (m_textCtrl) - { - m_textCtrl->Disable(); - m_textCtrl->SetValue( GetNormalizedConfigFolder( m_FolderId ) ); - } -} - -bool Panels::DirPickerPanel::Enable( bool enable ) -{ - if (m_pickerCtrl) - m_pickerCtrl->Enable( enable ? (!m_checkCtrl || m_checkCtrl->GetValue()) : false ); - - return _parent::Enable( enable ); -} - - -void Panels::DirPickerPanel::AppStatusEvent_OnSettingsApplied() -{ - Reset(); -} - -void Panels::DirPickerPanel::Apply() -{ - wxDirName path( GetPath() ); - - if (!path.Exists()) - { - wxDialogWithHelpers dialog( NULL, _("Create folder?") ); - dialog += dialog.Heading(AddAppName(_("A configured folder does not exist. Should %s try to create it?"))); - dialog += 12; - dialog += dialog.Heading( path.ToString() ); - - if( wxID_CANCEL == pxIssueConfirmation( dialog, MsgButtons().Custom(_("Create"), "create").Cancel(), L"CreateNewFolder" ) ) - throw Exception::CannotApplySettings( this ); - } - - path.Mkdir(); - g_Conf->Folders.Set( m_FolderId, path.ToString(), m_checkCtrl ? m_checkCtrl->GetValue() : false ); -} - -wxDirName Panels::DirPickerPanel::GetPath() const -{ - // The (x) ? y : z construct doesn't like y and z to be different types in gcc. - if (m_pickerCtrl) - return wxDirName(m_pickerCtrl->GetPath()); - - if (m_textCtrl) - return wxDirName(m_textCtrl->GetValue()); - - return wxDirName(wxEmptyString); -} - -void Panels::DirPickerPanel::SetPath( const wxString& newPath ) -{ - if (m_pickerCtrl) - m_pickerCtrl->SetPath( newPath ); - - if (m_textCtrl) - m_textCtrl->SetValue( newPath ); -} diff --git a/pcsx2/gui/Panels/GSWindowPanel.cpp b/pcsx2/gui/Panels/GSWindowPanel.cpp deleted file mode 100644 index f7fee0f7f2..0000000000 --- a/pcsx2/gui/Panels/GSWindowPanel.cpp +++ /dev/null @@ -1,194 +0,0 @@ -/* PCSX2 - PS2 Emulator for PCs - * Copyright (C) 2002-2021 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 . - */ - -#include "PrecompiledHeader.h" -#include "ConfigurationPanels.h" - -#include "fmt/core.h" -#include "gui/App.h" -#include "gui/AppAccelerators.h" - -using namespace pxSizerFlags; - -// -------------------------------------------------------------------------------------- -// GSWindowSetting Implementation -// -------------------------------------------------------------------------------------- - -Panels::GSWindowSettingsPanel::GSWindowSettingsPanel(wxWindow* parent) - : BaseApplicableConfigPanel_SpecificConfig(parent) -{ - const wxString aspect_ratio_labels[] = - { - _("Fit to Window/Screen"), - _("Auto Standard (4:3/3:2 Progressive)"), - _("Standard (4:3)"), - _("Widescreen (16:9)")}; - - const wxString fmv_aspect_ratio_switch_labels[] = - { - _("Off (Default)"), - _("Auto Standard (4:3/3:2 Progressive)"), - _("Standard (4:3)"), - _("Widescreen (16:9)")}; - - // Warning must match the order of the VsyncMode Enum - const wxString vsync_label[] = - { - _("Disabled"), - _("Standard"), - _("Adaptive"), - }; - - m_combo_AspectRatio = new wxComboBox(this, wxID_ANY, wxEmptyString, wxDefaultPosition, wxDefaultSize, - std::size(aspect_ratio_labels), aspect_ratio_labels, wxCB_READONLY); - - m_combo_FMVAspectRatioSwitch = new wxComboBox(this, wxID_ANY, wxEmptyString, wxDefaultPosition, wxDefaultSize, - std::size(fmv_aspect_ratio_switch_labels), fmv_aspect_ratio_switch_labels, wxCB_READONLY); - - m_combo_vsync = new wxComboBox(this, wxID_ANY, wxEmptyString, wxDefaultPosition, wxDefaultSize, - std::size(vsync_label), vsync_label, wxCB_READONLY); - - m_text_WindowWidth = CreateNumericalTextCtrl(this, 5); - m_text_WindowHeight = CreateNumericalTextCtrl(this, 5); - - // Linux/Mac note: Exclusive Fullscreen mode is probably Microsoft specific, though - // I'm not yet 100% certain of that. - - m_check_SizeLock = new pxCheckBox(this, _("Disable window resize border")); - m_check_HideMouse = new pxCheckBox(this, _("Always hide mouse cursor")); - m_check_CloseGS = new pxCheckBox(this, _("Hide window when paused")); - // Implement custom hotkeys (Alt + Enter) with translatable string intact + not blank in GUI. - m_check_Fullscreen = new pxCheckBox(this, _("Start in fullscreen mode by default") + wxString(" (") + wxGetApp().GlobalAccels->findKeycodeWithCommandId("FullscreenToggle").toTitleizedString() + wxString(")")); - m_check_DclickFullscreen = new pxCheckBox(this, _("Double-click toggles fullscreen mode")); - - m_combo_FMVAspectRatioSwitch->SetToolTip(pxEt(L"Off: Disables temporary aspect ratio switch. (It will use the above setting from Aspect Ratio instead of FMV Aspect Ratio Override.)\n\n" - L"Auto 4:3/3:2: Temporarily switch to a 4:3 aspect ratio while an FMV plays to correctly display a 4:3 FMV. Will use 3:2 is the resolution is 480P\n\n" - L"4:3: Temporarily switch to a 4:3 aspect ratio while an FMV plays to correctly display a 4:3 FMV. \n\n" - L"16:9: Temporarily switch to a 16:9 aspect ratio while an FMV plays to correctly display a widescreen 16:9 FMV.")); - - m_combo_vsync->SetToolTip(pxEt(L"Vsync eliminates screen tearing but typically has a big performance hit. It usually only applies to fullscreen mode.")); - - m_check_HideMouse->SetToolTip(pxEt(L"Check this to force the mouse cursor invisible inside the GS window; useful if using the mouse as a primary control device for gaming. By default the mouse auto-hides after 2 seconds of inactivity.")); - - m_check_Fullscreen->SetToolTip(pxEt(L"Enables automatic mode switch to fullscreen when starting or resuming emulation. You can still toggle fullscreen display at any time using \n" - L"Alt-Enter or double clicking the output window (game).")); - - m_check_CloseGS->SetToolTip(pxEt(L"Completely closes the often large and bulky GS window when pressing ESC or pausing the emulator.")); - - // ---------------------------------------------------------------------------- - // Layout and Positioning - - wxBoxSizer& s_customsize(*new wxBoxSizer(wxHORIZONTAL)); - s_customsize += m_text_WindowWidth; - s_customsize += Label(L"x") | StdExpand(); - s_customsize += m_text_WindowHeight; - - wxFlexGridSizer& s_AspectRatio(*new wxFlexGridSizer(2, StdPadding, StdPadding)); - //s_AspectRatio.AddGrowableCol( 0 ); - s_AspectRatio.AddGrowableCol(1); - - // Implement custom hotkeys (F6) with translatable string intact + not blank in GUI. - s_AspectRatio += Label(_("Aspect Ratio:") + wxString::Format(" (%s)", wxGetApp().GlobalAccels->findKeycodeWithCommandId("GSwindow_CycleAspectRatio").toTitleizedString())); - s_AspectRatio += m_combo_AspectRatio | pxAlignRight; - s_AspectRatio += Label(_("FMV Aspect Ratio Override:")) | pxMiddle; - s_AspectRatio += m_combo_FMVAspectRatioSwitch | pxAlignRight; - s_AspectRatio += Label(_("Custom Window Size:")) | pxMiddle; - s_AspectRatio += s_customsize | pxAlignRight; - - wxFlexGridSizer& s_vsync(*new wxFlexGridSizer(2, StdPadding, StdPadding)); - s_vsync.AddGrowableCol(1); - - s_vsync += Label(_("Wait for Vsync on refresh:")) | pxMiddle; - s_vsync += m_combo_vsync | pxAlignRight; - - *this += s_AspectRatio | StdExpand(); - *this += new wxStaticLine(this) | StdExpand(); - - *this += m_check_SizeLock; - *this += m_check_HideMouse; - *this += m_check_CloseGS; - *this += new wxStaticLine(this) | StdExpand(); - - *this += m_check_Fullscreen; - *this += m_check_DclickFullscreen; - *this += new wxStaticLine(this) | StdExpand(); - - *this += s_vsync | StdExpand(); - - wxBoxSizer* centerSizer = new wxBoxSizer(wxVERTICAL); - *centerSizer += GetSizer() | pxCenter; - SetSizer(centerSizer, false); - - AppStatusEvent_OnSettingsApplied(); -} - -void Panels::GSWindowSettingsPanel::AppStatusEvent_OnSettingsApplied() -{ - ApplyConfigToGui(*g_Conf); -} - -void Panels::GSWindowSettingsPanel::ApplyConfigToGui(AppConfig& configToApply, int flags) -{ - const Pcsx2Config::GSOptions& gsconf(configToApply.EmuOptions.GS); - const AppConfig::GSWindowOptions& conf(configToApply.GSWindow); - - if (!(flags & AppConfig::APPLY_FLAG_FROM_PRESET)) - { //Presets don't control these values - m_check_CloseGS->SetValue(conf.CloseOnEsc); - m_check_Fullscreen->SetValue(conf.DefaultToFullscreen); - m_check_HideMouse->SetValue(conf.AlwaysHideMouse); - m_check_SizeLock->SetValue(conf.DisableResizeBorders); - - m_combo_AspectRatio->SetSelection((int)gsconf.AspectRatio); - m_combo_FMVAspectRatioSwitch->SetSelection(enum_cast(gsconf.FMVAspectRatioSwitch)); - - m_check_DclickFullscreen->SetValue(conf.IsToggleFullscreenOnDoubleClick); - - m_text_WindowWidth->ChangeValue(wxsFormat(L"%d", conf.WindowSize.GetWidth())); - m_text_WindowHeight->ChangeValue(wxsFormat(L"%d", conf.WindowSize.GetHeight())); - } - - m_combo_vsync->SetSelection(enum_cast(configToApply.EmuOptions.GS.VsyncEnable)); - m_combo_vsync->Enable(true); // Always allow, regardless of preset -} - -void Panels::GSWindowSettingsPanel::Apply() -{ - AppConfig::GSWindowOptions& appconf(g_Conf->GSWindow); - Pcsx2Config::GSOptions& gsconf(g_Conf->EmuOptions.GS); - - appconf.CloseOnEsc = m_check_CloseGS->GetValue(); - appconf.DefaultToFullscreen = m_check_Fullscreen->GetValue(); - appconf.AlwaysHideMouse = m_check_HideMouse->GetValue(); - appconf.DisableResizeBorders = m_check_SizeLock->GetValue(); - - gsconf.AspectRatio = (AspectRatioType)m_combo_AspectRatio->GetSelection(); - gsconf.FMVAspectRatioSwitch = (FMVAspectRatioSwitchType)m_combo_FMVAspectRatioSwitch->GetSelection(); - EmuConfig.CurrentAspectRatio = gsconf.AspectRatio; - - gsconf.VsyncEnable = static_cast(m_combo_vsync->GetSelection()); - - appconf.IsToggleFullscreenOnDoubleClick = m_check_DclickFullscreen->GetValue(); - - long xr, yr = 1; - - if (!m_text_WindowWidth->GetValue().ToLong(&xr) || !m_text_WindowHeight->GetValue().ToLong(&yr)) - throw Exception::CannotApplySettings(this) - .SetDiagMsg("User submitted non-numeric window size parameters!") - .SetUserMsg("Invalid window dimensions specified: Size cannot contain non-numeric digits! >_<"); - - appconf.WindowSize.x = xr; - appconf.WindowSize.y = yr; -} diff --git a/pcsx2/gui/Panels/GameFixesPanel.cpp b/pcsx2/gui/Panels/GameFixesPanel.cpp deleted file mode 100644 index 3d8c302a54..0000000000 --- a/pcsx2/gui/Panels/GameFixesPanel.cpp +++ /dev/null @@ -1,187 +0,0 @@ -/* PCSX2 - PS2 Emulator for PCs - * Copyright (C) 2002-2010 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 . - */ - -#include "PrecompiledHeader.h" -#include "gui/App.h" -#include "ConfigurationPanels.h" - -using namespace pxSizerFlags; - -Panels::GameFixesPanel::GameFixesPanel( wxWindow* parent ) - : BaseApplicableConfigPanel_SpecificConfig( parent ) -{ - wxStaticBoxSizer& groupSizer = *new wxStaticBoxSizer( wxVERTICAL, this, _("Gamefixes") ); - - // NOTE: Order of checkboxes must match the order of the bits in the GamefixOptions structure! - // NOTE2: Don't make this static, because translations can change at run-time :) - // NOTE3: This panel is way to big, header text was reduced as a temporary solution. - // However if you want add more gamefix, a final solution must be found (noteboox/scrolling...) - - struct CheckTextMess - { - wxString label, tooltip; - }; - - const CheckTextMess check_text[GamefixId_COUNT] = - { - { - _("FPU Multiply Hack - For Tales of Destiny."), - wxEmptyString - }, - { - _("FPU Negative Div Hack - For Gundam games."), - wxEmptyString - }, - { - _("Preload TLB hack to avoid tlb miss on Goemon"), - wxEmptyString - }, - { - _("Switch to Software renderer for FMVs"), - wxEmptyString - }, - { - _("Skip MPEG hack - Skips videos/FMVs in games to avoid game hanging/freezes."), - wxEmptyString - }, - { - _("OPH Flag hack - Try if your game freezes showing the same frame."), - pxEt( L"Known to affect following games:\n * Bleach Blade Battler\n * Growlanser II and III\n * Wizardry" - ) - }, - { - _("EE timing hack - Multi purpose hack. Try if all else fails."), - pxEt(L"Known to affect following games:\n * Digital Devil Saga (Fixes FMV and crashes)\n * SSX (Fixes bad graphics and crashes)\n * Resident Evil: Dead Aim (Causes garbled textures)" - ) - }, - { - _("Instant DMA hack. Try if all else fails."), - pxEt(L"Known to affect following games:\n * Fire Pro Wrestling Z (Bad ring graphics)" - ) - }, - { - _("Handle DMAC writes when it is busy."), - pxEt( L"Known to affect following games:\n * Mana Khemia 1 (Going \"off campus\"), Metal Saga (Intro FMV), Pilot Down Behind Enemy Lines\n" - ) - }, - { - _("Force GIF PATH3 transfers through FIFO (Fifa Street 2)"), - wxEmptyString - }, - { - _("Simulate VIF1 FIFO read ahead. Fixes slow loading games."), - pxEt( L"Known to affect following games:\n * Test Drive Unlimited\n * Transformers" - ) - }, - { - _("Delay VIF1 Stalls (VIF1 FIFO) - For SOCOM 2 HUD and Spy Hunter loading hang."), - wxEmptyString - }, - { - _("VU Add Hack - Fixes Tri-Ace games boot crash."), - _("Games that need this hack to boot:\n * Star Ocean 3\n * Radiata Stories\n * Valkyrie Profile 2") - }, - { - _("VU I bit Hack avoid constant recompilation in some games (Scarface The World Is Yours, Crash Tag Team Racing)"), - wxEmptyString - }, - { - _("VU Sync (Run behind) to avoid sync problems when reading or writing VU registers"), - wxEmptyString - }, - { - _("VU Overflow hack to check for possible float overflows (Superman Returns)"), - wxEmptyString - }, - { - _("VU XGkick Sync - Use accurate timing for VU XGKicks (Slower)"), - pxEt(L"Fixes graphical errors on WRC, Erementar Gerad, Tennis Court Smash and others." - ) - }, - { - _("Use Blit for internal FPS"), - pxEt(L"Use alternative method to calclate internal FPS to avoid false readings in some games." - ) - } - }; - - for( int i=0; iSetToolTip( check_text[i].tooltip ); - } - - m_check_Enable = new pxCheckBox( this, _("Enable manual game fixes [Not recommended]"), - pxE( L"It's better to enable 'Automatic game fixes' at the main menu instead, and leave this page empty ('Automatic' means: selectively use specific tested fixes for specific games). Manual game fixes will NOT increase your performance. In fact they may decrease it." - ) - ); - - m_check_Enable->SetToolTip(pxE(L"Gamefixes can work around wrong emulation in some titles. \nThey may also cause compatibility or performance issues.\n\nThe safest way is to make sure that all game fixes are completely disabled.")).SetSubPadding( 1 ); - m_check_Enable->SetValue( g_Conf->EnableGameFixes ); - - *this += m_check_Enable | StdExpand(); - *this += groupSizer | pxCenter; - - Bind(wxEVT_CHECKBOX, &GameFixesPanel::OnEnable_Toggled, this, m_check_Enable->GetId()); - - EnableStuff(); -} - -void Panels::GameFixesPanel::Apply() -{ - g_Conf->EnableGameFixes = m_check_Enable->GetValue(); - - Pcsx2Config::GamefixOptions& opts( g_Conf->EmuOptions.Gamefixes ); - for (GamefixId i=GamefixId_FIRST; i < pxEnumEnd; ++i) - opts.Set((GamefixId)i, m_checkbox[i]->GetValue()); - - // make sure the user's command line specifications are disabled (if present). - wxGetApp().Overrides.ApplyCustomGamefixes = false; -} - -void Panels::GameFixesPanel::EnableStuff( AppConfig* configToUse ) -{ - if (!configToUse) configToUse = g_Conf.get(); - for (GamefixId i=GamefixId_FIRST; i < pxEnumEnd; ++i) - m_checkbox[i]->Enable(m_check_Enable->GetValue() && !configToUse->EnablePresets); - - Layout(); -} - -void Panels::GameFixesPanel::OnEnable_Toggled( wxCommandEvent& evt ) -{ - AppConfig tmp=*g_Conf; - tmp.EnablePresets=false; //if clicked, button was enabled, so not using a preset --> let EnableStuff work - - EnableStuff( &tmp ); - evt.Skip(); -} - -void Panels::GameFixesPanel::AppStatusEvent_OnSettingsApplied() -{ - ApplyConfigToGui( *g_Conf ); -} - -void Panels::GameFixesPanel::ApplyConfigToGui( AppConfig& configToApply, int flags ) -{ - const Pcsx2Config::GamefixOptions& opts( configToApply.EmuOptions.Gamefixes ); - for (GamefixId i=GamefixId_FIRST; i < pxEnumEnd; ++i) - m_checkbox[i]->SetValue( opts.Get((GamefixId)i) );//apply the use/don't-use fix values - - m_check_Enable->SetValue( configToApply.EnableGameFixes );//main gamefixes checkbox - EnableStuff( &configToApply );// enable/disable the all the fixes controls according to the main one - - this->Enable(!configToApply.EnablePresets); -} diff --git a/pcsx2/gui/Panels/LogOptionsPanels.cpp b/pcsx2/gui/Panels/LogOptionsPanels.cpp deleted file mode 100644 index f5d7330ece..0000000000 --- a/pcsx2/gui/Panels/LogOptionsPanels.cpp +++ /dev/null @@ -1,347 +0,0 @@ -/* PCSX2 - PS2 Emulator for PCs - * Copyright (C) 2002-2010 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 . - */ - -#include "PrecompiledHeader.h" -#include "LogOptionsPanels.h" - -#include "gui/IniInterface.h" -#include "DebugTools/Debug.h" - -#include "common/StringUtil.h" - -#include - - -using namespace pxSizerFlags; - -Panels::eeLogOptionsPanel::eeLogOptionsPanel( LogOptionsPanel* parent ) - : BaseCpuLogOptionsPanel( parent, L"EE Logs" ) -{ - SetMinWidth( 300 ); - - m_miscGroup = new wxStaticBoxSizer( wxVERTICAL, this, L"General" ); - - m_disasmPanel = new CheckedStaticBox( this, wxVERTICAL, L"Disasm" ); - m_hwPanel = new CheckedStaticBox( this, wxVERTICAL, L"Registers" ); - m_evtPanel = new CheckedStaticBox( this, wxVERTICAL, L"Events" ); - - wxFlexGridSizer& eeTable( *new wxFlexGridSizer( 2, 5, 0 ) ); - - eeTable.AddGrowableCol(0); - eeTable.AddGrowableCol(1); - - eeTable += m_miscGroup | SubGroup(); - eeTable += m_hwPanel | SubGroup(); - eeTable += m_evtPanel | SubGroup(); - eeTable += m_disasmPanel | SubGroup(); - - ThisSizer += 4; - ThisSizer += eeTable | pxExpand; - - SetValue( true ); -} - -Panels::iopLogOptionsPanel::iopLogOptionsPanel( LogOptionsPanel* parent ) - : BaseCpuLogOptionsPanel( parent, L"IOP Logs" ) -{ - SetMinWidth( 280 ); - - m_miscGroup = new wxStaticBoxSizer( wxVERTICAL, this, L"General" ); - - m_disasmPanel = new CheckedStaticBox( this, wxVERTICAL, L"Disasm" ); - m_hwPanel = new CheckedStaticBox( this, wxVERTICAL, L"Registers" ); - m_evtPanel = new CheckedStaticBox( this, wxVERTICAL, L"Events" ); - - wxFlexGridSizer& iopTable( *new wxFlexGridSizer( 2, 5, 0 ) ); - - iopTable.AddGrowableCol(0); - iopTable.AddGrowableCol(1); - - iopTable += m_miscGroup | SubGroup(); - iopTable += m_hwPanel | SubGroup(); - iopTable += m_evtPanel | SubGroup(); - iopTable += m_disasmPanel | SubGroup(); - - ThisSizer += 4; - ThisSizer += iopTable | pxExpand; - - SetValue( true ); -} - -CheckedStaticBox* Panels::eeLogOptionsPanel::GetStaticBox( const wxString& subgroup ) const -{ - if (0 == subgroup.CmpNoCase( L"Disasm" )) return m_disasmPanel; - if (0 == subgroup.CmpNoCase( L"Registers" )) return m_hwPanel; - if (0 == subgroup.CmpNoCase( L"Events" )) return m_evtPanel; - - return NULL; -} - -CheckedStaticBox* Panels::iopLogOptionsPanel::GetStaticBox( const wxString& subgroup ) const -{ - if (0 == subgroup.CmpNoCase( L"Disasm" )) return m_disasmPanel; - if (0 == subgroup.CmpNoCase( L"Registers" )) return m_hwPanel; - if (0 == subgroup.CmpNoCase( L"Events" )) return m_evtPanel; - - return NULL; -} - - -void Panels::eeLogOptionsPanel::OnSettingsChanged() -{ - const TraceLogFilters& conf( g_Conf->EmuOptions.Trace ); - - SetValue( conf.EE.m_EnableAll ); - - m_disasmPanel ->SetValue( conf.EE.m_EnableDisasm ); - m_evtPanel ->SetValue( conf.EE.m_EnableEvents ); - m_hwPanel ->SetValue( conf.EE.m_EnableRegisters ); -} - -void Panels::iopLogOptionsPanel::OnSettingsChanged() -{ - const TraceLogFilters& conf( g_Conf->EmuOptions.Trace ); - - SetValue( conf.IOP.m_EnableAll ); - - m_disasmPanel ->SetValue( conf.IOP.m_EnableDisasm ); - m_evtPanel ->SetValue( conf.IOP.m_EnableEvents ); - m_hwPanel ->SetValue( conf.IOP.m_EnableRegisters ); -} - -static SysTraceLog * const traceLogList[] = -{ - &SysTrace.SIF, - - &SysTrace.EE.Bios, - &SysTrace.EE.Memory, - - &SysTrace.EE.R5900, - &SysTrace.EE.COP0, - &SysTrace.EE.COP1, - &SysTrace.EE.COP2, - &SysTrace.EE.Cache, - - &SysTrace.EE.KnownHw, - &SysTrace.EE.UnknownHw, - &SysTrace.EE.DMAhw, - &SysTrace.EE.IPU, - &SysTrace.EE.GIFtag, - &SysTrace.EE.VIFcode, - &SysTrace.EE.MSKPATH3, - - &SysTrace.EE.DMAC, - &SysTrace.EE.Counters, - &SysTrace.EE.SPR, - &SysTrace.EE.VIF, - &SysTrace.EE.GIF, - - - // IOP Section - - &SysTrace.IOP.Bios, - &SysTrace.IOP.Memcards, - &SysTrace.IOP.PAD, - - &SysTrace.IOP.R3000A, - &SysTrace.IOP.COP2, - &SysTrace.IOP.Memory, - - &SysTrace.IOP.KnownHw, - &SysTrace.IOP.UnknownHw, - &SysTrace.IOP.DMAhw, - &SysTrace.IOP.DMAC, - &SysTrace.IOP.Counters, - &SysTrace.IOP.CDVD, - &SysTrace.IOP.MDEC, -}; - -static const uint traceLogCount = std::size(traceLogList); - -void SysTraceLog_LoadSaveSettings( IniInterface& ini ) -{ - ScopedIniGroup path(ini, L"TraceLogSources"); - - for (uint i=0; iGetName(), "Trace log without a name!" ); - ini.Entry( StringUtil::UTF8StringToWxString(log->GetCategory() + "." + log->GetShortName()), log->Enabled, false ); - } - } -} - -static bool traceLogEnabled( const wxString& ident ) -{ - // Brute force search for now. not enough different source logs to - // justify using a hash table, and switching over to a "complex" class - // type from the current simple array initializer requires effort to - // avoid C++ global initializer dependency hell. - - for( uint i=0; iGetCategory())) ) - return traceLogList[i]->Enabled; - } - - pxFailDev( wxsFormat(L"Invalid or unknown TraceLog identifier: %s", ident.c_str()).ToUTF8() ); - return false; -} - -// -------------------------------------------------------------------------------------- -// LogOptionsPanel Implementations -// -------------------------------------------------------------------------------------- -Panels::LogOptionsPanel::LogOptionsPanel(wxWindow* parent ) - : BaseApplicableConfigPanel( parent ) - , m_checks( new pxCheckBox*[traceLogCount] ) -{ - m_miscSection = new wxStaticBoxSizer( wxHORIZONTAL, this, L"Misc" ); - - m_eeSection = new eeLogOptionsPanel( this ); - m_iopSection = new iopLogOptionsPanel( this ); - - for( uint i = 0; iGetStaticBox(group)) - { - addsizer = &cpugroup->ThisSizer; - addparent = cpugroup; - } - else - { - addsizer = cpupanel->GetMiscGroup(); - addparent = cpupanel; - } - } - else - { - addsizer = m_miscSection; - addparent = m_miscSection->GetStaticBox(); - } - - *addsizer += m_checks[i] = new pxCheckBox( addparent, item.GetName() ); - if( m_checks[i] && item.HasDescription() ) - m_checks[i]->SetToolTip(item.GetDescription()); - } - - m_masterEnabler = new pxCheckBox( this, _("Enable Trace Logging"), - _("Trace logs are all written to emulog.txt. Toggle trace logging at any time using F10.") ); - m_masterEnabler->SetToolTip( _("Warning: Trace logging is typically very slow, and is a leading cause of 'What happened to my FPS?' problems. :)") ); - - wxFlexGridSizer& topSizer = *new wxFlexGridSizer( 2 ); - - topSizer.AddGrowableCol(0); - topSizer.AddGrowableCol(1); - - topSizer += m_eeSection | StdExpand(); - topSizer += m_iopSection | StdExpand(); - - *this += m_masterEnabler | StdExpand(); - *this += new wxStaticLine( this ) | StdExpand().Border(wxLEFT | wxRIGHT, 20); - *this += 5; - *this += topSizer | StdExpand(); - *this += m_miscSection | StdSpace().Centre(); - - Bind(wxEVT_CHECKBOX, &LogOptionsPanel::OnCheckBoxClicked, this, m_masterEnabler->GetWxPtr()->GetId()); -} - -Panels::BaseCpuLogOptionsPanel* Panels::LogOptionsPanel::GetCpuPanel( const wxString& token ) const -{ - if( token == L"EE" ) return m_eeSection; - if( token == L"IOP" ) return m_iopSection; - - return NULL; -} - -void Panels::LogOptionsPanel::AppStatusEvent_OnSettingsApplied() -{ - TraceLogFilters& conf( g_Conf->EmuOptions.Trace ); - - m_masterEnabler->SetValue( conf.Enabled ); - - m_eeSection->OnSettingsChanged(); - m_iopSection->OnSettingsChanged(); - - for (uint i=0; iSetValue(traceLogList[i]->Enabled); - } - OnUpdateEnableAll(); -} - -void Panels::LogOptionsPanel::OnUpdateEnableAll() -{ - bool enabled( m_masterEnabler->GetValue() ); - - m_eeSection->Enable( enabled ); - m_iopSection->Enable( enabled ); - m_miscSection->GetStaticBox()->Enable( enabled ); -} - -void Panels::LogOptionsPanel::OnCheckBoxClicked(wxCommandEvent &evt) -{ - OnUpdateEnableAll(); - evt.Skip(); -} - -void Panels::LogOptionsPanel::Apply() -{ - g_Conf->EmuOptions.Trace.Enabled = m_masterEnabler->GetValue(); - - m_eeSection->Apply(); - m_iopSection->Apply(); - - for( uint i = 0; iEnabled = m_checks[i]->IsChecked(); - } -} - -#define GetSet( cpu, name ) SysTrace.cpu.name.Enabled = m_##name->GetValue() - -void Panels::eeLogOptionsPanel::Apply() -{ - TraceFiltersEE& conf( g_Conf->EmuOptions.Trace.EE ); - - conf.m_EnableAll = GetValue(); - conf.m_EnableDisasm = m_disasmPanel->GetValue(); - conf.m_EnableRegisters = m_hwPanel->GetValue(); - conf.m_EnableEvents = m_evtPanel->GetValue(); -} - -void Panels::iopLogOptionsPanel::Apply() -{ - TraceFiltersIOP& conf( g_Conf->EmuOptions.Trace.IOP ); - - conf.m_EnableAll = GetValue(); - conf.m_EnableDisasm = m_disasmPanel->GetValue(); - conf.m_EnableRegisters = m_hwPanel->GetValue(); - conf.m_EnableEvents = m_evtPanel->GetValue(); -} diff --git a/pcsx2/gui/Panels/LogOptionsPanels.h b/pcsx2/gui/Panels/LogOptionsPanels.h deleted file mode 100644 index c67ba88597..0000000000 --- a/pcsx2/gui/Panels/LogOptionsPanels.h +++ /dev/null @@ -1,97 +0,0 @@ -/* PCSX2 - PS2 Emulator for PCs - * Copyright (C) 2002-2010 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 . - */ - -#pragma once - -#include "gui/AppCommon.h" -#include "gui/ApplyState.h" -#include - -namespace Panels -{ - class LogOptionsPanel; - - class BaseCpuLogOptionsPanel : public CheckedStaticBox - { - protected: - wxStaticBoxSizer* m_miscGroup; - - public: - BaseCpuLogOptionsPanel(wxWindow* parent, const wxString& title, wxOrientation orient = wxVERTICAL) - : CheckedStaticBox(parent, orient, title), m_miscGroup(NULL){} - - virtual wxStaticBoxSizer* GetMiscGroup() const { return m_miscGroup; } - virtual CheckedStaticBox* GetStaticBox( const wxString& subgroup ) const=0; - }; - - class eeLogOptionsPanel : public BaseCpuLogOptionsPanel - { - protected: - CheckedStaticBox* m_disasmPanel; - CheckedStaticBox* m_hwPanel; - CheckedStaticBox* m_evtPanel; - - public: - eeLogOptionsPanel( LogOptionsPanel* parent ); - virtual ~eeLogOptionsPanel() = default; - - CheckedStaticBox* GetStaticBox( const wxString& subgroup ) const; - - void OnSettingsChanged(); - void Apply(); - }; - - - class iopLogOptionsPanel : public BaseCpuLogOptionsPanel - { - protected: - CheckedStaticBox* m_disasmPanel; - CheckedStaticBox* m_hwPanel; - CheckedStaticBox* m_evtPanel; - - public: - iopLogOptionsPanel( LogOptionsPanel* parent ); - virtual ~iopLogOptionsPanel() = default; - - CheckedStaticBox* GetStaticBox( const wxString& subgroup ) const; - - void OnSettingsChanged(); - void Apply(); - }; - - class LogOptionsPanel : public BaseApplicableConfigPanel - { - protected: - eeLogOptionsPanel* m_eeSection; - iopLogOptionsPanel* m_iopSection; - wxStaticBoxSizer* m_miscSection; - - pxCheckBox* m_masterEnabler; - - std::unique_ptr m_checks; - - public: - LogOptionsPanel( wxWindow* parent ); - virtual ~LogOptionsPanel() = default; - - void AppStatusEvent_OnSettingsApplied(); - void OnUpdateEnableAll(); - void OnCheckBoxClicked(wxCommandEvent &event); - void Apply(); - - protected: - BaseCpuLogOptionsPanel* GetCpuPanel( const wxString& token ) const; - }; -} diff --git a/pcsx2/gui/Panels/MemoryCardListPanel.cpp b/pcsx2/gui/Panels/MemoryCardListPanel.cpp deleted file mode 100644 index d7063b7f22..0000000000 --- a/pcsx2/gui/Panels/MemoryCardListPanel.cpp +++ /dev/null @@ -1,1314 +0,0 @@ -/* PCSX2 - PS2 Emulator for PCs - * Copyright (C) 2002-2018 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 . - */ - -#include "PrecompiledHeader.h" -#include "gui/AppCoreThread.h" -#include "System.h" -#include "MemoryCardFile.h" - -#include "ConfigurationPanels.h" -#include "MemoryCardPanels.h" - -#include "gui/Dialogs/ConfigurationDialog.h" -#include "gui/IniInterface.h" -#include "common/StringUtil.h" -#include "Sio.h" - -#include -#include -#include - - -static wxDataFormat drag_drop_format(L"PCSX2McdDragDrop"); - -bool CopyDirectory(const wxString& from, const wxString& to); -bool RemoveWxDirectory(const wxString& dirname); - -using namespace pxSizerFlags; -using namespace Panels; - -static bool IsMcdFormatted(wxFFile& fhand) -{ - static const char formatted_psx[] = "MC"; - static const char formatted_string[] = "Sony PS2 Memory Card Format"; - static const int fmtstrlen = sizeof(formatted_string) - 1; - - char dest[fmtstrlen]; - - fhand.Read(dest, fmtstrlen); - - bool formatted = memcmp(formatted_string, dest, fmtstrlen) == 0; - if (!formatted) - formatted = memcmp(formatted_psx, dest, 2) == 0; - - return formatted; -} - -//sets IsPresent if the file is valid, and derived properties (filename, formatted, size, etc) -bool EnumerateMemoryCard(McdSlotItem& dest, const wxFileName& filename, const wxDirName basePath) -{ - dest.IsFormatted = false; - dest.IsPresent = false; - dest.IsPSX = false; - dest.Type = MemoryCardType::Empty; - - const wxString fullpath(filename.GetFullPath()); - //DevCon.WriteLn( fullpath ); - if (filename.FileExists()) - { - // might be a memory card file - wxFFile mcdFile(fullpath); - if (!mcdFile.IsOpened()) - { - return false; - } // wx should log the error for us. - - wxFileOffset length = mcdFile.Length(); - - if (length < (1024 * 528) && length != 0x20000) - { - Console.Warning("... Memory card appears to be truncated. Ignoring."); - return false; - } - - const int mb_ecc = 1024 * 528 * 2; - const int mb_noecc = 1024 * 512 * 2; - dest.SizeInMB = (uint)(length % mb_ecc ? length / mb_noecc : length / mb_ecc); - - if (length == 0x20000) - { - dest.IsPSX = true; // PSX memcard; - dest.SizeInMB = 1; // MegaBIT - } - - dest.Type = MemoryCardType::File; - dest.IsFormatted = IsMcdFormatted(mcdFile); - filename.GetTimes(NULL, &dest.DateModified, &dest.DateCreated); - } - else if (filename.DirExists()) - { - // might be a memory card folder - wxFileName superBlockFileName(fullpath, L"_pcsx2_superblock"); - if (!superBlockFileName.FileExists()) - { - return false; - } - wxFFile mcdFile(superBlockFileName.GetFullPath()); - if (!mcdFile.IsOpened()) - { - return false; - } - - dest.SizeInMB = 0; - - dest.Type = MemoryCardType::Folder; - dest.IsFormatted = IsMcdFormatted(mcdFile); - superBlockFileName.GetTimes(NULL, &dest.DateModified, &dest.DateCreated); - } - else - { - // is neither - return false; - } - - dest.IsPresent = true; - dest.Filename = filename; - if (filename.GetFullPath() == (basePath + filename.GetFullName()).GetFullPath()) - dest.Filename = filename.GetFullName(); - - return true; -} - -//avih: unused -/* -static int EnumerateMemoryCards( McdList& dest, const wxArrayString& files ) -{ - int pushed = 0; - Console.WriteLn( Color_StrongBlue, "Enumerating memory cards..." ); - for( size_t i=0; i 0 ) - Console.WriteLn( Color_StrongBlue, "Memory card Enumeration Complete." ); - else - Console.WriteLn( Color_StrongBlue, "No valid memory card found." ); - - return pushed; -} -*/ -// -------------------------------------------------------------------------------------- -// McdListItem (implementations) -// -------------------------------------------------------------------------------------- -bool McdSlotItem::IsMultitapSlot() const -{ - return FileMcd_IsMultitapSlot(Slot); -} - -uint McdSlotItem::GetMtapPort() const -{ - return FileMcd_GetMtapPort(Slot); -} - -uint McdSlotItem::GetMtapSlot() const -{ - return FileMcd_GetMtapSlot(Slot); -} - -// Compares two cards -- If this equality comparison is used on items where -// no filename is specified, then the check will include port and slot. -bool McdSlotItem::operator==(const McdSlotItem& right) const -{ - bool fileEqu; - - if (Filename.GetFullName().IsEmpty()) - fileEqu = OpEqu(Slot); - else - fileEqu = OpEqu(Filename); - - return fileEqu && - OpEqu(IsPresent) && OpEqu(IsEnabled) && - OpEqu(SizeInMB) && OpEqu(IsFormatted) && - OpEqu(DateCreated) && OpEqu(DateModified); -} - -bool McdSlotItem::operator!=(const McdSlotItem& right) const -{ - return operator==(right); -} - - -// ===================================================================================================== -// BaseMcdListPanel (implementations) -// ===================================================================================================== -Panels::BaseMcdListPanel::BaseMcdListPanel(wxWindow* parent) - : _parent(parent) -{ - m_FolderPicker = new DirPickerPanel(this, FolderId_MemoryCards, - //_("memory card Search Path:"), // static box label - _("Select folder with PS2 memory cards") // dir picker popup label - ); - - m_listview = NULL; - s_leftside_buttons = NULL; - s_rightside_buttons = NULL; - - m_btn_Refresh = new wxButton(this, wxID_ANY, _("Refresh list")); - - Bind(wxEVT_BUTTON, &BaseMcdListPanel::OnRefreshSelections, this, m_btn_Refresh->GetId()); -} - -void Panels::BaseMcdListPanel::RefreshMcds() const -{ - wxCommandEvent refit(wxEVT_BUTTON); - refit.SetId(m_btn_Refresh->GetId()); - GetEventHandler()->AddPendingEvent(refit); -} - -void Panels::BaseMcdListPanel::CreateLayout() -{ - //if( m_listview ) m_listview->SetMinSize( wxSize( 480, 140 ) ); - - wxFlexGridSizer* s_flex = new wxFlexGridSizer(3, 1, 0, 0); - s_flex->AddGrowableCol(0); - s_flex->AddGrowableRow(1); - SetSizer(s_flex); - - wxBoxSizer& s_buttons(*new wxBoxSizer(wxHORIZONTAL)); - s_leftside_buttons = new wxBoxSizer(wxHORIZONTAL); - s_rightside_buttons = new wxBoxSizer(wxHORIZONTAL); - - s_buttons += s_leftside_buttons | pxAlignLeft; - s_buttons += pxStretchSpacer(); - s_buttons += s_rightside_buttons | pxAlignRight; - - if (m_FolderPicker) - *this += m_FolderPicker | pxExpand; - else - *this += StdPadding; //we need the 'else' because we need these items to land into the proper rows of s_flex. - - if (m_listview) - *this += m_listview | pxExpand; - else - *this += StdPadding; - - *this += s_buttons | pxExpand; - - *s_leftside_buttons += m_btn_Refresh; - - if (m_listview) - { - IniLoader loader; - ScopedIniGroup group(loader, L"MemoryCardListPanel"); - m_listview->LoadSaveColumns(loader); - } -} - -void Panels::BaseMcdListPanel::Apply() -{ - // Save column widths to the configuration file. Since these are used *only* by this - // dialog, we use a direct local ini save approach, instead of going through g_conf. - if (m_listview) - { - IniSaver saver; - ScopedIniGroup group(saver, L"MemoryCardListPanel"); - m_listview->LoadSaveColumns(saver); - } -} - -void Panels::BaseMcdListPanel::AppStatusEvent_OnSettingsApplied() -{ - if ((m_MultitapEnabled[0] != g_Conf->EmuOptions.MultitapPort0_Enabled) || - (m_MultitapEnabled[1] != g_Conf->EmuOptions.MultitapPort1_Enabled)) - { - m_MultitapEnabled[0] = g_Conf->EmuOptions.MultitapPort0_Enabled; - m_MultitapEnabled[1] = g_Conf->EmuOptions.MultitapPort1_Enabled; - - RefreshMcds(); - } -} - -class McdDropTarget : public wxDropTarget -{ -protected: - BaseMcdListView* m_listview; - -public: - McdDropTarget(BaseMcdListView* listview = NULL) - { - m_listview = listview; - SetDataObject(new wxCustomDataObject(drag_drop_format)); - } - - virtual wxDragResult OnData(wxCoord x, wxCoord y, wxDragResult def) - { - int flags = 0; - - int destViewIndex = m_listview->HitTest(wxPoint(x, y), flags); - if (wxNOT_FOUND == destViewIndex) - destViewIndex = -1; //non list item target is the filesystem placeholder. - - if (!GetData()) - return wxDragNone; - - wxCustomDataObject* dobj = static_cast(GetDataObject()); - - if (dobj->GetDataSize() != sizeof(int)) - return wxDragNone; - - int sourceViewIndex; - dobj->GetDataHere(&sourceViewIndex); - - wxDragResult result = OnDropMcd( - m_listview->GetMcdProvider().GetCardForViewIndex(sourceViewIndex), - m_listview->GetMcdProvider().GetCardForViewIndex(destViewIndex), - def); - - if (wxDragNone == result) - return wxDragNone; - - m_listview->GetMcdProvider().RefreshMcds(); - return result; - } - - - virtual wxDragResult OnDropMcd(McdSlotItem& src, McdSlotItem& dest, wxDragResult def) - { - if (src.Slot == dest.Slot) - return wxDragNone; - //if( !pxAssert( (src.Slot >= 0) && (dest.Slot >= 0) ) ) return wxDragNone; - const wxDirName basepath(m_listview->GetMcdProvider().GetMcdPath()); - - if (wxDragCopy == def) - { - if (!m_listview->GetMcdProvider().UiDuplicateCard(src, dest)) - return wxDragNone; - } - else if (wxDragMove == def) - { // source can only be an existing card. - // if dest is a ps2-port (empty or not) -> swap cards between ports. - // is dest is a non-ps2-port -> remove card from port. - - // Note: For the sake of usability, automatically enable dest if a ps2-port. - if (src.IsPresent) - { - wxFileName tmpFilename = dest.Filename; - bool tmpPresent = dest.IsPresent; - if (src.Slot < 0 && m_listview->GetMcdProvider().isFileAssignedToInternalSlot(src.Filename)) - m_listview->GetMcdProvider().RemoveCardFromSlot(src.Filename); - - dest.Filename = src.Filename; - dest.IsEnabled = dest.IsPresent ? dest.IsEnabled : true; - dest.IsPresent = src.IsPresent; - - if (dest.Slot >= 0) - { //2 internal slots: swap - src.Filename = tmpFilename; - src.IsPresent = tmpPresent; - } - else - { //dest is at the filesystem (= remove card from slot) - src.Filename = L""; - src.IsPresent = false; - src.IsEnabled = false; - } - } - } - - return def; - } -}; - - -enum McdMenuId -{ - McdMenuId_Create = 0x888, - //McdMenuId_Mount, - McdMenuId_Rename, - McdMenuId_RefreshList, - McdMenuId_AssignUnassign, - McdMenuId_Duplicate, - McdMenuId_Convert, -}; - - -Panels::MemoryCardListPanel_Simple* g_uglyPanel = NULL; -void g_uglyFunc() -{ - if (g_uglyPanel) - g_uglyPanel->OnChangedListSelection(); -} - -Panels::MemoryCardListPanel_Simple::~MemoryCardListPanel_Simple() { g_uglyPanel = NULL; } - -Panels::MemoryCardListPanel_Simple::MemoryCardListPanel_Simple(wxWindow* parent) - : _parent(parent) -{ - m_MultitapEnabled[0] = false; - m_MultitapEnabled[1] = false; - - m_listview = new MemoryCardListView_Simple(this); - - m_listview->SetMinSize(wxSize(620, m_listview->GetCharHeight() * 13)); // 740 is nice for default font sizes - - m_listview->SetDropTarget(new McdDropTarget(m_listview)); - - //m_button_Mount = new wxButton(this, wxID_ANY, _("Enable port")); - - m_button_AssignUnassign = new wxButton(this, wxID_ANY, _("Eject")); - m_button_Duplicate = new wxButton(this, wxID_ANY, _("Duplicate ...")); - m_button_Rename = new wxButton(this, wxID_ANY, _("Rename ...")); - m_button_Create = new wxButton(this, wxID_ANY, _("Create ...")); - m_button_Convert = new wxButton(this, wxID_ANY, _("Convert ...")); - - // ------------------------------------ - // Sizer / Layout Section - // ------------------------------------ - - CreateLayout(); - - *s_leftside_buttons += 20; - //*s_leftside_buttons += m_button_Mount; - //*s_leftside_buttons += 20; - - *s_leftside_buttons += Label(_("Card: ")) | pxMiddle; - *s_leftside_buttons += m_button_AssignUnassign; - *s_leftside_buttons += 20; - *s_leftside_buttons += m_button_Duplicate; - *s_leftside_buttons += 2; - *s_leftside_buttons += m_button_Rename; - *s_leftside_buttons += 2; - *s_leftside_buttons += m_button_Create; - *s_leftside_buttons += 2; - *s_leftside_buttons += m_button_Convert; - SetSizerAndFit(GetSizer()); - - parent->SetWindowStyle(parent->GetWindowStyle() | wxRESIZE_BORDER); - - Bind(wxEVT_LIST_BEGIN_DRAG, &MemoryCardListPanel_Simple::OnListDrag, this, m_listview->GetId()); - Bind(wxEVT_LIST_ITEM_SELECTED, &MemoryCardListPanel_Simple::OnListSelectionChanged, this, m_listview->GetId()); - Bind(wxEVT_LIST_ITEM_ACTIVATED, &MemoryCardListPanel_Simple::OnItemActivated, this, m_listview->GetId()); //enter or double click - - //Deselected is not working for some reason (e.g. when clicking an empty row at the table?) - avih - // wxMSW bug for virtual listviews. Works fine on Linux: http://trac.wxwidgets.org/ticket/1919 - turtleli - Bind(wxEVT_LIST_ITEM_DESELECTED, &MemoryCardListPanel_Simple::OnListSelectionChanged, this, m_listview->GetId()); - - Bind(wxEVT_LIST_ITEM_RIGHT_CLICK, &MemoryCardListPanel_Simple::OnOpenItemContextMenu, this, m_listview->GetId()); - - //Bind(wxEVT_BUTTON, &MemoryCardListPanel_Simple::OnMountCard, this, m_button_Mount->GetId()); - Bind(wxEVT_BUTTON, &MemoryCardListPanel_Simple::OnCreateOrDeleteCard, this, m_button_Create->GetId()); - Bind(wxEVT_BUTTON, &MemoryCardListPanel_Simple::OnConvertCard, this, m_button_Convert->GetId()); - Bind(wxEVT_BUTTON, &MemoryCardListPanel_Simple::OnRenameFile, this, m_button_Rename->GetId()); - Bind(wxEVT_BUTTON, &MemoryCardListPanel_Simple::OnDuplicateFile, this, m_button_Duplicate->GetId()); - Bind(wxEVT_BUTTON, &MemoryCardListPanel_Simple::OnAssignUnassignFile, this, m_button_AssignUnassign->GetId()); - - // Popup Menu Connections! - //Bind(McdMenuId_Mount, &MemoryCardListPanel_Simple::OnMountCard, this, McdMenuId_Mount); - Bind(wxEVT_MENU, &MemoryCardListPanel_Simple::OnCreateOrDeleteCard, this, McdMenuId_Create); - Bind(wxEVT_MENU, &MemoryCardListPanel_Simple::OnConvertCard, this, McdMenuId_Convert); - Bind(wxEVT_MENU, &MemoryCardListPanel_Simple::OnRenameFile, this, McdMenuId_Rename); - Bind(wxEVT_MENU, &MemoryCardListPanel_Simple::OnDuplicateFile, this, McdMenuId_Duplicate); - Bind(wxEVT_MENU, &MemoryCardListPanel_Simple::OnAssignUnassignFile, this, McdMenuId_AssignUnassign); - - Bind(wxEVT_MENU, &MemoryCardListPanel_Simple::OnRefreshSelections, this, McdMenuId_RefreshList); - - //because the wxEVT_LIST_ITEM_DESELECTED doesn't work (buttons stay enabled when clicking an empty area of the list), - // m_listview can send us an event that indicates a change at the list. Ugly, but works. - g_uglyPanel = this; - m_listview->setExternHandler(g_uglyFunc); -} - -void Panels::MemoryCardListPanel_Simple::UpdateUI() -{ - if (!m_listview) - return; - - int sel = m_listview->GetFirstSelected(); - - m_button_Create->Enable(); - - if (wxNOT_FOUND == sel) - { - m_button_Create->SetLabel(_("Create ...")); - pxSetToolTip(m_button_Create, _("Create a new memory card.")); - - // m_button_Mount->Disable(); - m_button_Rename->Disable(); - m_button_Duplicate->Disable(); - m_button_AssignUnassign->Disable(); - m_button_Convert->Disable(); - return; - } - - const McdSlotItem& card(GetCardForViewIndex(sel)); - - - m_button_Rename->Enable(card.IsPresent); - wxString renameTip = _("Rename this memory card ..."); - pxSetToolTip(m_button_Rename, renameTip); - - m_button_AssignUnassign->Enable(card.IsPresent); - m_button_AssignUnassign->SetLabel(card.Slot >= 0 ? _("Eject") : _("Insert ...")); - wxString assignTip = (card.Slot >= 0) ? _("Eject the card from this port") : _("Insert this card to a port ..."); - pxSetToolTip(m_button_AssignUnassign, assignTip); - - m_button_Duplicate->Enable(card.IsPresent); - wxString dupTip = _("Create a duplicate of this memory card ..."); - pxSetToolTip(m_button_Duplicate, dupTip); - - m_button_Convert->Enable(card.IsPresent && card.IsFormatted && !card.IsPSX); - pxSetToolTip(m_button_Convert, _("Convert this memory card to or from a folder memory card. Creates a duplicate of the current memory card in the new type.\n\n" - "Note: Only available when a memory card is formatted. Not available for PSX memory cards.")); - - //m_button_Create->Enable( card.Slot>=0 || card.IsPresent); - m_button_Create->SetLabel(card.IsPresent ? _("Delete") : _("Create ...")); - - if (card.IsPresent) - pxSetToolTip(m_button_Create, _("Permanently delete this memory card from disk (all contents are lost)")); - else if (card.Slot >= 0) - pxSetToolTip(m_button_Create, _("Create a new memory card and assign it to this Port.")); - else - pxSetToolTip(m_button_Create, _("Create a new memory card.")); - - /* - m_button_Mount->Enable( card.IsPresent && card.Slot>=0); - m_button_Mount->SetLabel( card.IsEnabled ? _("Disable Port") : _("Enable Port") ); - pxSetToolTip( m_button_Mount, - card.IsEnabled - ? _("Disable the selected PS2-Port (this memory card will be invisible to games/BIOS).") - : _("Enable the selected PS2-Port (games/BIOS will see this memory card).") - ); -*/ -} - -void Panels::MemoryCardListPanel_Simple::Apply() -{ - _parent::Apply(); - - int used = 0; - Console.WriteLn("Apply memory cards:"); - for (uint slot = 0; slot < 8; ++slot) - { - g_Conf->EmuOptions.Mcd[slot].Type = m_Cards[slot].Type; - g_Conf->EmuOptions.Mcd[slot].Enabled = m_Cards[slot].IsEnabled && m_Cards[slot].IsPresent; - if (m_Cards[slot].IsPresent) - g_Conf->EmuOptions.Mcd[slot].Filename = StringUtil::wxStringToUTF8String(m_Cards[slot].Filename.GetFullName()); - else - g_Conf->EmuOptions.Mcd[slot].Filename.clear(); - - if (g_Conf->EmuOptions.Mcd[slot].Enabled) - { - used++; - Console.WriteLn("slot[%d]='%s'", slot, g_Conf->EmuOptions.Mcd[slot].Filename.c_str()); - } - } - if (!used) - Console.WriteLn("No active slots."); - - AutoEject::SetAll(); -} - -void Panels::MemoryCardListPanel_Simple::AppStatusEvent_OnSettingsApplied() -{ - for (uint slot = 0; slot < 8; ++slot) - { - m_Cards[slot].IsEnabled = g_Conf->EmuOptions.Mcd[slot].Enabled; - m_Cards[slot].Filename = StringUtil::UTF8StringToWxString(g_Conf->EmuOptions.Mcd[slot].Filename); - - // Automatically create the enabled but non-existing file such that it can be managed (else will get created anyway on boot) - wxString targetFile = (GetMcdPath() + m_Cards[slot].Filename.GetFullName()).GetFullPath(); - if (m_Cards[slot].IsEnabled && !(wxFileExists(targetFile) || wxDirExists(targetFile))) - { - wxString errMsg; - if (isValidNewFilename(m_Cards[slot].Filename.GetFullName(), GetMcdPath(), errMsg, 5)) - { - if (!Dialogs::CreateMemoryCardDialog::CreateIt(targetFile, 8, false)) - { - Console.Error("Automatic creation of memory card '%ls' failed. Hope for the best...", WX_STR(targetFile)); - } - else - { - Console.WriteLn("Memory card created: '%ls'.", WX_STR(targetFile)); - } - } - else - { - Console.Error("Memory card was enabled, but it had an invalid file name. Aborting automatic creation. Hope for the best... (%ls)", WX_STR(errMsg)); - } - } - - if (!m_Cards[slot].IsEnabled || !(wxFileExists(targetFile) || wxDirExists(targetFile))) - { - m_Cards[slot].IsEnabled = false; - m_Cards[slot].IsPresent = false; - m_Cards[slot].Filename = L""; - } - } - DoRefresh(); - - _parent::AppStatusEvent_OnSettingsApplied(); -} - - -//BUG: the next function is never reached because, for some reason, IsoDropTarget::OnDropFiles is called instead. -// Interestingly, IsoDropTarget::OnDropFiles actually "detects" a memory card file as a valid Audio-CD ISO... - avih -bool Panels::MemoryCardListPanel_Simple::OnDropFiles(wxCoord x, wxCoord y, const wxArrayString& filenames) -{ - if (filenames.GetCount() == 1 && wxFileName(filenames[0]).IsDir()) - { - m_FolderPicker->SetPath(filenames[0]); - return true; - } - return false; -} - -bool Panels::MemoryCardListPanel_Simple::ValidateEnumerationStatus() -{ - if (m_listview) - m_listview->SetMcdProvider(NULL); - return false; -} - -void Panels::MemoryCardListPanel_Simple::DoRefresh() -{ - for (uint slot = 0; slot < 8; ++slot) - { - //if( FileMcd_IsMultitapSlot(slot) && !m_MultitapEnabled[FileMcd_GetMtapPort(slot)] ) - // continue; - - //wxFileName fullpath( m_FolderPicker->GetPath() + g_Conf->Mcd[slot].Filename.GetFullName() ); - wxFileName fullpath = m_FolderPicker->GetPath() + m_Cards[slot].Filename.GetFullName(); - - EnumerateMemoryCard(m_Cards[slot], fullpath, m_FolderPicker->GetPath()); - m_Cards[slot].Slot = slot; - } - - ReadFilesAtMcdFolder(); - - - if (m_listview) - m_listview->SetMcdProvider(this); - UpdateUI(); -} - - -// ===================================================================================================== -// MemoryCardListPanel_Simple (implementations) -// ===================================================================================================== - -void Panels::MemoryCardListPanel_Simple::UiCreateNewCard(McdSlotItem& card) -{ - // card can also be the filesystem placeholder. On that case, the changes - // made to it will be reverted on refresh, and we'll just have a new card at the folder. - if (card.IsPresent) - { - Console.WriteLn("Error: Aborted: create memory card invoked, but a file is already associated."); - return; - } - - Dialogs::CreateMemoryCardDialog dialog(this, m_FolderPicker->GetPath(), L"my memory card"); - wxWindowID result = dialog.ShowModal(); - - if (result != wxID_CANCEL) - { - card.IsEnabled = true; - card.Filename = dialog.result_createdMcdFilename; - card.IsPresent = true; - if (card.Slot >= 0) - { - Console.WriteLn("Setting new memory card to slot %u: '%ls'", card.Slot, WX_STR(card.Filename.GetFullName())); - } - else - { - Console.WriteLn("Created a new unassigned memory card file: '%ls'", WX_STR(card.Filename.GetFullName())); - } - } - else - { - card.IsEnabled = false; - } - - Apply(); - RefreshSelections(); -} - -void Panels::MemoryCardListPanel_Simple::UiConvertCard(McdSlotItem& card) -{ - if (!card.IsPresent) - { - Console.WriteLn("Error: Aborted: Convert memory card invoked, but a file is not associated."); - return; - } - - Dialogs::ConvertMemoryCardDialog dialog(this, m_FolderPicker->GetPath(), card.Type, card.Filename.GetFullName()); - wxWindowID result = dialog.ShowModal(); - - if (result != wxID_CANCEL) - { - Apply(); - RefreshSelections(); - } -} - -void Panels::MemoryCardListPanel_Simple::UiDeleteCard(McdSlotItem& card) -{ - if (!card.IsPresent) - { - Console.WriteLn("Error: Aborted: delete memory card invoked, but a file is not associated."); - return; - } - - bool result = true; - if (card.IsFormatted) - { - wxString content; - content.Printf( - pxE(L"You are about to delete the formatted memory card '%s'. All data on this card will be lost! Are you absolutely and quite positively sure?"), WX_STR(card.Filename.GetFullName())); - - result = Msgbox::YesNo(content, _("Delete memory file?")); - } - - if (result) - { - - wxFileName fullpath(m_FolderPicker->GetPath() + card.Filename.GetFullName()); - - card.IsEnabled = false; - Apply(); - - if (fullpath.FileExists()) - { - wxRemoveFile(fullpath.GetFullPath()); - } - else - { - RemoveWxDirectory(fullpath.GetFullPath()); - } - - RefreshSelections(); - } -} - -bool Panels::MemoryCardListPanel_Simple::UiDuplicateCard(McdSlotItem& src, McdSlotItem& dest) -{ - wxDirName basepath = GetMcdPath(); - if (!src.IsPresent) - { - Msgbox::Alert(_("Failed: Can only duplicate an existing card."), _("Duplicate memory card")); - return false; - } - - if (dest.IsPresent && dest.Slot != -1) - { - wxString content; - content.Printf( - pxE(L"Failed: Duplicate is only allowed to an empty PS2-Port or to the file system.")); - - Msgbox::Alert(content, _("Duplicate memory card")); - return false; - } - - while (1) - { - wxString newFilename = L""; - newFilename = wxGetTextFromUser(_("Select a name for the duplicate\n( '.ps2' will be added automatically)"), _("Duplicate memory card")); - if (newFilename == L"") - { - //Msgbox::Alert( _("Duplicate canceled"), _("Duplicate memory card") ); - return false; - } - newFilename += L".ps2"; - - //check that the name is valid for a new file - wxString errMsg; - if (!isValidNewFilename(newFilename, basepath, errMsg, 5)) - { - wxString message; - message.Printf(_("Failed: %s"), WX_STR(errMsg)); - Msgbox::Alert(message, _("Duplicate memory card")); - continue; - } - - dest.Filename = newFilename; - break; - } - - wxFileName srcfile(basepath + src.Filename); - wxFileName destfile(basepath + dest.Filename); - - ScopedBusyCursor doh(Cursor_ReallyBusy); - - if (!((srcfile.FileExists() && wxCopyFile(srcfile.GetFullPath(), destfile.GetFullPath(), true)) || (!srcfile.FileExists() && CopyDirectory(srcfile.GetFullPath(), destfile.GetFullPath())))) - { - wxString heading; - heading.Printf(pxE(L"Failed: Destination memory card '%s' is in use."), - WX_STR(dest.Filename.GetFullName()), dest.Slot); - - wxString content; - - Msgbox::Alert(heading + L"\n\n" + content, _("Copy failed!")); - - return false; - } - - // Destination memcard isEnabled state is the same now as the source's - wxString success; - success.Printf(_("Memory card '%s' duplicated to '%s'."), - WX_STR(src.Filename.GetFullName()), - WX_STR(dest.Filename.GetFullName())); - Msgbox::Alert(success, _("Success")); - dest.IsPresent = true; - dest.IsEnabled = true; - - Apply(); - DoRefresh(); - return true; -} - -void Panels::MemoryCardListPanel_Simple::UiRenameCard(McdSlotItem& card) -{ - if (!card.IsPresent) - { - Console.WriteLn("Error: Aborted: Rename memory card invoked, but no file is associated."); - return; - } - - const wxDirName basepath(m_listview->GetMcdProvider().GetMcdPath()); - wxString newFilename; - while (1) - { - wxString title; - title.Printf(_("Select a new name for the memory card '%s'\n( '.ps2' will be added automatically)"), - WX_STR(card.Filename.GetFullName())); - newFilename = wxGetTextFromUser(title, _("Rename memory card")); - if (newFilename == L"") - return; - - newFilename += L".ps2"; - - //check that the name is valid for a new file - wxString errMsg; - if (!isValidNewFilename(newFilename, basepath, errMsg, 5)) - { - wxString message; - message.Printf(_("Error (%s)"), WX_STR(errMsg)); - Msgbox::Alert(message, _("Rename memory card")); - continue; - } - - break; - } - - bool origEnabled = card.IsEnabled; - card.IsEnabled = false; - Apply(); - if (!wxRenameFile((basepath + card.Filename).GetFullPath(), (basepath + wxFileName(newFilename)).GetFullPath(), false)) - { - card.IsEnabled = origEnabled; - Apply(); - Msgbox::Alert(_("Error: Rename could not be completed.\n"), _("Rename memory card")); - - return; - } - - card.Filename = newFilename; - card.IsEnabled = origEnabled; - Apply(); - - RefreshSelections(); -} - -void Panels::MemoryCardListPanel_Simple::OnCreateOrDeleteCard(wxCommandEvent& evt) -{ - int selectedViewIndex = m_listview->GetFirstSelected(); - if (wxNOT_FOUND == selectedViewIndex) - selectedViewIndex = -1; //get filesystem placeholder, just create a new card at the filesystem. - - McdSlotItem& card(GetCardForViewIndex(selectedViewIndex)); - - if (card.IsPresent) - UiDeleteCard(card); - else - UiCreateNewCard(card); -} - -void Panels::MemoryCardListPanel_Simple::OnConvertCard(wxCommandEvent& evt) -{ - int selectedViewIndex = m_listview->GetFirstSelected(); - if (wxNOT_FOUND == selectedViewIndex) - { - return; - } - - McdSlotItem& card(GetCardForViewIndex(selectedViewIndex)); - if (card.IsPresent) - { - UiConvertCard(card); - } -} - -//enable/disapbe port -/* -void Panels::MemoryCardListPanel_Simple::OnMountCard(wxCommandEvent& evt) -{ - evt.Skip(); - - const int selectedViewIndex = m_listview->GetFirstSelected(); - if( wxNOT_FOUND == selectedViewIndex ) return; - - McdSlotItem& card( GetCardForViewIndex(selectedViewIndex) ); - - card.IsEnabled = !card.IsEnabled; - m_listview->RefreshItem(selectedViewIndex); - UpdateUI(); -} -*/ -/* -//text dialog: can be used for rename: wxGetTextFromUser - avih -void Panels::MemoryCardListPanel_Simple::OnRelocateCard(wxCommandEvent& evt) -{ - evt.Skip(); - - const int slot = m_listview->GetFirstSelected(); - if( wxNOT_FOUND == slot ) return; - - // Issue a popup to the user that allows them to pick a new .PS2 file to serve as - // the new host memorycard file for the slot. The dialog has a number of warnings - // present to reiterate that this is an advanced operation that PCSX2 may not - // support very well (ie, might be buggy). - - m_listview->RefreshItem(slot); - UpdateUI(); -} -*/ - -// enter/double-click -void Panels::MemoryCardListPanel_Simple::OnItemActivated(wxListEvent& evt) -{ - const int viewIndex = m_listview->GetFirstSelected(); - if (wxNOT_FOUND == viewIndex) - return; - McdSlotItem& card(GetCardForViewIndex(viewIndex)); - - if (card.IsPresent) - UiRenameCard(card); - else if (card.Slot >= 0) - UiCreateNewCard(card); // IsPresent==false can only happen for an internal slot (vs filename on the HD), so a card can be created. -} - -void Panels::MemoryCardListPanel_Simple::OnDuplicateFile(wxCommandEvent& evt) -{ - const int viewIndex = m_listview->GetFirstSelected(); - if (wxNOT_FOUND == viewIndex) - return; - McdSlotItem& card(GetCardForViewIndex(viewIndex)); - - pxAssert(card.IsPresent); - McdSlotItem dummy; - UiDuplicateCard(card, dummy); -} - -wxString GetPortName(int slotIndex) -{ - if (slotIndex == 0 || slotIndex == 1) - return pxsFmt(wxString(L" ") + _("Port-%u / Multitap-%u--Port-1"), FileMcd_GetMtapPort(slotIndex) + 1, FileMcd_GetMtapPort(slotIndex) + 1); - return pxsFmt(wxString(L" ") + _(" Multitap-%u--Port-%u"), FileMcd_GetMtapPort(slotIndex) + 1, FileMcd_GetMtapSlot(slotIndex) + 1); -} - -void Panels::MemoryCardListPanel_Simple::UiAssignUnassignFile(McdSlotItem& card) -{ - pxAssert(card.IsPresent); - - if (card.Slot >= 0) - { //eject - card.IsEnabled = false; - card.IsPresent = false; - card.Filename = L""; - DoRefresh(); - } - else - { //insert into a (UI) selected slot - wxArrayString selections; - int i; - for (i = 0; i < GetNumVisibleInternalSlots(); i++) - { - McdSlotItem& selCard = GetCardForViewIndex(i); - wxString sel = GetPortName(selCard.Slot) + L" ( "; - if (selCard.IsPresent) - sel += selCard.Filename.GetFullName(); - else - sel += _("Empty"); - sel += L" )"; - - selections.Add(sel); - } - wxString title; - title.Printf(_("Select a target port for '%s'"), WX_STR(card.Filename.GetFullName())); - int res = wxGetSingleChoiceIndex(title, _("Insert card"), selections, this); - if (res < 0) - return; - - McdSlotItem& target = GetCardForViewIndex(res); - bool en = target.IsPresent ? target.IsEnabled : true; - RemoveCardFromSlot(card.Filename); - target.Filename = card.Filename; - target.IsPresent = true; - target.IsEnabled = en; - DoRefresh(); - } -} -void Panels::MemoryCardListPanel_Simple::OnAssignUnassignFile(wxCommandEvent& evt) -{ - const int viewIndex = m_listview->GetFirstSelected(); - if (wxNOT_FOUND == viewIndex) - return; - McdSlotItem& card(GetCardForViewIndex(viewIndex)); - - UiAssignUnassignFile(card); -} - -void Panels::MemoryCardListPanel_Simple::OnRenameFile(wxCommandEvent& evt) -{ - const int viewIndex = m_listview->GetFirstSelected(); - if (wxNOT_FOUND == viewIndex) - return; - McdSlotItem& card(GetCardForViewIndex(viewIndex)); - - UiRenameCard(card); -} - - -void Panels::MemoryCardListPanel_Simple::OnListDrag(wxListEvent& evt) -{ - int selectionViewIndex = m_listview->GetFirstSelected(); - - if (selectionViewIndex < 0) - return; - wxCustomDataObject my_data(drag_drop_format); - my_data.SetData(sizeof(int), &selectionViewIndex); - - wxDropSource dragSource(m_listview); - dragSource.SetData(my_data); - /*wxDragResult result = */ dragSource.DoDragDrop(wxDrag_DefaultMove); -} - -void Panels::MemoryCardListPanel_Simple::OnListSelectionChanged(wxListEvent& evt) -{ - UpdateUI(); -} - -void Panels::MemoryCardListPanel_Simple::OnOpenItemContextMenu(wxListEvent& evt) -{ - int idx = evt.GetIndex(); - - wxMenu* junk = new wxMenu(); - - if (idx != wxNOT_FOUND) - { - const McdSlotItem& card(GetCardForViewIndex(idx)); - - if (card.IsPresent) - { - junk->Append(McdMenuId_AssignUnassign, card.Slot >= 0 ? _("&Eject card") : _("&Insert card ...")); - junk->Append(McdMenuId_Duplicate, _("D&uplicate card ...")); - junk->Append(McdMenuId_Rename, _("&Rename card ...")); - junk->Append(McdMenuId_Create, _("&Delete card")); - if (card.IsFormatted && !card.IsPSX) - { - junk->Append(McdMenuId_Convert, _("&Convert card")); - } - } - else - { - junk->Append(McdMenuId_Create, _("&Create a new card ...")); - } - } - else - { - junk->Append(McdMenuId_Create, _("&Create a new card ...")); - } - - junk->AppendSeparator(); - - junk->Append(McdMenuId_RefreshList, _("Re&fresh List")); - - PopupMenu(junk); - m_listview->RefreshItem(idx); - UpdateUI(); -} - -void Panels::MemoryCardListPanel_Simple::ReadFilesAtMcdFolder() -{ - //Dir enumeration/iteration code courtesy of cotton. - avih. - while (!m_allFilesystemCards.empty()) - m_allFilesystemCards.pop_back(); - - m_filesystemPlaceholderCard.Slot = -1; - m_filesystemPlaceholderCard.IsEnabled = false; - m_filesystemPlaceholderCard.IsPresent = false; - m_filesystemPlaceholderCard.Filename = L""; - - - wxArrayString memcardList; - wxString filename = m_FolderPicker->GetPath().ToString(); - wxDir memcardDir(filename); - if (memcardDir.IsOpened()) - { - // add memory card files - wxDir::GetAllFiles(filename, &memcardList, L"*.ps2", wxDIR_FILES); - wxDir::GetAllFiles(filename, &memcardList, L"*.bin", wxDIR_FILES); - wxDir::GetAllFiles(filename, &memcardList, L"*.mcd", wxDIR_FILES); - wxDir::GetAllFiles(filename, &memcardList, L"*.mcr", wxDIR_FILES); - - // add memory card folders - wxString dirname; - if (memcardDir.GetFirst(&dirname, wxEmptyString, wxDIR_DIRS | wxDIR_HIDDEN)) - { - do - { - wxFileName superBlockFileName(wxFileName(filename, dirname).GetFullPath(), L"_pcsx2_superblock"); - if (superBlockFileName.FileExists()) - { - memcardList.Add(superBlockFileName.GetPath()); - } - } while (memcardDir.GetNext(&dirname)); - } - } - - - for (uint i = 0; i < memcardList.size(); i++) - { - McdSlotItem currentCardFile; - bool isOk = EnumerateMemoryCard(currentCardFile, memcardList[i], m_FolderPicker->GetPath()); - if (isOk && !isFileAssignedAndVisibleOnList(currentCardFile.Filename)) - { - currentCardFile.Slot = -1; - currentCardFile.IsEnabled = false; - m_allFilesystemCards.push_back(currentCardFile); - //DevCon.WriteLn(L"Enumerated file: '%s'", WX_STR(currentCardFile.Filename.GetFullName()) ); - } - /*else - DevCon.WriteLn(L"MCD folder card file skipped: '%s'", WX_STR(memcardList[i]) );*/ - } -} - - -bool Panels::MemoryCardListPanel_Simple::IsSlotVisible(int slotIndex) const -{ - if (slotIndex < 0 || slotIndex >= 8) - return false; - if (!m_MultitapEnabled[0] && 2 <= slotIndex && slotIndex <= 4) - return false; - if (!m_MultitapEnabled[1] && 5 <= slotIndex && slotIndex <= 7) - return false; - return true; -} -//whether or not this filename appears on the ports at the list (takes into account MT enabled/disabled) -bool Panels::MemoryCardListPanel_Simple::isFileAssignedAndVisibleOnList(const wxFileName cardFile) const -{ - int i; - for (i = 0; i < 8; i++) - if (IsSlotVisible(i) && cardFile.GetFullName() == m_Cards[i].Filename.GetFullName()) - return true; - - return false; -} - -//whether or not this filename is assigned to a ports (regardless if MT enabled/disabled) -bool Panels::MemoryCardListPanel_Simple::isFileAssignedToInternalSlot(const wxFileName cardFile) const -{ - int i; - for (i = 0; i < 8; i++) - if (cardFile.GetFullName() == m_Cards[i].Filename.GetFullName()) - return true; - - return false; -} - -void Panels::MemoryCardListPanel_Simple::RemoveCardFromSlot(const wxFileName cardFile) -{ - int i; - for (i = 0; i < 8; i++) - if (cardFile.GetFullName() == m_Cards[i].Filename.GetFullName()) - { - m_Cards[i].Filename = L""; - m_Cards[i].IsPresent = false; - m_Cards[i].IsEnabled = false; - } -} - -int Panels::MemoryCardListPanel_Simple::GetNumFilesVisibleAsFilesystem() const -{ - return m_allFilesystemCards.size(); -} - - -bool Panels::MemoryCardListPanel_Simple::IsNonEmptyFilesystemCards() const -{ - return GetNumFilesVisibleAsFilesystem() > 0; -} - -McdSlotItem& Panels::MemoryCardListPanel_Simple::GetNthVisibleFilesystemCard(int n) -{ - return m_allFilesystemCards.at(n); -} - -int Panels::MemoryCardListPanel_Simple::GetNumVisibleInternalSlots() const -{ - uint baselen = 2; - if (m_MultitapEnabled[0]) - baselen += 3; - if (m_MultitapEnabled[1]) - baselen += 3; - return baselen; -} - -// Interface Implementation for IMcdList -int Panels::MemoryCardListPanel_Simple::GetLength() const -{ - uint baselen = GetNumVisibleInternalSlots(); - baselen++; //filesystem placeholder - baselen += GetNumFilesVisibleAsFilesystem(); - return baselen; -} - - -//Translates a list-view index (idx) to an internal memory card slot. -//This method effectively defines the arrangement of the card slots at the list view. -//The internal card slots array is fixed as sollows: -// slot 0: mcd1 (= MT1 slot 1) -// slot 1: mcd2 (= MT2 slot 1) -// slots 2,3,4: MT1 slots 2,3,4 -// slots 5,6,7: MT2 slots 2,3,4 -// -//however, the list-view doesn't show MT slots when this MT is disabled, -// so the view-index should "shift" to point at the real card slot. -//While we're at it, we can alternatively enforce any other arrangment of the view by -// using any other set of 'view-index-to-card-slot' translating that we'd like. -int Panels::MemoryCardListPanel_Simple::GetSlotIndexForViewIndex(int listViewIndex) -{ - pxAssert(0 <= listViewIndex && listViewIndex < GetNumVisibleInternalSlots()); - int targetSlot = -1; - - /* - //this list-view arrangement is mostly kept aligned with the internal slots indexes, and only takes care - // of the case where MT1 is disabled (hence the MT2 slots 2,3,4 "move backwards" 3 places on the view-index) - // However, this arrangement it's not very intuitive to use... - if (!m_MultitapEnabled[0] && listViewIndex>=2) - { - //we got an MT2 slot. - assert(listViewIndex < 5); - targetSlot = listViewIndex+3; - } - else - { - targetSlot=listViewIndex;//identical view-index and card slot. - } -*/ - - //This arrangement of list-view is as follows: - //mcd1(=MT1 port 1) - //[MT1 port 2,3,4 if MT1 is enabled] - //mcd2(=MT2 slot 1) - //[MT2 port 2,3,4 if MT2 is enabled] - - if (m_MultitapEnabled[0]) - { - //MT1 enabled: - if (1 <= listViewIndex && listViewIndex <= 3) - { //MT1 ports 2/3/4 move one place backwards - targetSlot = listViewIndex + 1; - } - else if (listViewIndex == 4) - { //mcd2 (=MT2 port 1) moves 3 places forward - targetSlot = 1; - } - else - { //mcd1 keeps it's pos as first, MT2 ports keep their pos at the end of the list. - targetSlot = listViewIndex; - } - } - else - { - //MT1 disabled: mcd1 and mcd2 stay put, MT2 ports 2,3,4 come next (move backwards 3 places) - if (2 <= listViewIndex && listViewIndex <= 4) - targetSlot = listViewIndex + 3; - else - targetSlot = listViewIndex; - } - - pxAssert(0 <= targetSlot && targetSlot <= 7); - return targetSlot; -} - - -McdSlotItem& Panels::MemoryCardListPanel_Simple::GetCardForViewIndex(int idx) -{ - pxAssert(-1 <= idx && idx < GetNumVisibleInternalSlots() + 1 + GetNumFilesVisibleAsFilesystem()); - - if (0 <= idx && idx < GetNumVisibleInternalSlots()) - return m_Cards[GetSlotIndexForViewIndex(idx)]; - - if (idx == -1 || idx == GetNumVisibleInternalSlots()) - return this->m_filesystemPlaceholderCard; - - return this->m_allFilesystemCards.at(idx - GetNumVisibleInternalSlots() - 1); -} diff --git a/pcsx2/gui/Panels/MemoryCardListView.cpp b/pcsx2/gui/Panels/MemoryCardListView.cpp deleted file mode 100644 index 2a1e6c1d45..0000000000 --- a/pcsx2/gui/Panels/MemoryCardListView.cpp +++ /dev/null @@ -1,214 +0,0 @@ -/* PCSX2 - PS2 Emulator for PCs - * Copyright (C) 2002-2010 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 . - */ - -#include "PrecompiledHeader.h" -#include "ConfigurationPanels.h" -#include "MemoryCardPanels.h" - -#include "gui/Dialogs/ConfigurationDialog.h" - -#include "gui/IniInterface.h" - -using namespace pxSizerFlags; -using namespace Panels; - -// -------------------------------------------------------------------------------------- -// BaseMcdListView (implementations) -// -------------------------------------------------------------------------------------- - -void BaseMcdListView::SetMcdProvider( IMcdList* face ) -{ - m_CardProvider = face; - SetCardCount( m_CardProvider ? m_CardProvider->GetLength() : 0 ); -} - -IMcdList& BaseMcdListView::GetMcdProvider() -{ - pxAssert( m_CardProvider ); - return *m_CardProvider; -} - -void BaseMcdListView::LoadSaveColumns( IniInterface& ini ) -{ - FastFormatUnicode label; - uint colcnt = GetColumnCount(); - for( uint col=0; colGetCardForViewIndex(item) ); - wxString prefix=L" "; - - switch( column ) - { - case McdColS_PortSlot: - if (it.Slot>=0) - { - if( !it.IsMultitapSlot() ) - return pxsFmt(wxString(L" ") + _("Port-%u / Multitap-%u--Port-1"), it.GetMtapPort()+1, it.GetMtapPort()+1); - return pxsFmt(wxString(L" ")+_(" Multitap-%u--Port-%u"), it.GetMtapPort()+1, it.GetMtapSlot()+1); - } - - return L""; -/* - case McdColS_Status: - { - wxString res = prefix + (it.IsEnabled ? _("Enabled") : _("Disabled")); - if( !it.IsPresent && it.Slot>=0 ) - res = prefix + _("Empty"); - else if ( it.Slot == -1 ) - res= L""; - return prefix + res; - } -*/ - case McdColS_Size: return prefix + ( !it.IsPresent ? L"" : (it.IsPSX? pxsFmt( L"%u MBit", it.SizeInMB ) : ( it.SizeInMB > 0 ? pxsFmt( L"%u MiB", it.SizeInMB ) : L"Auto" ) ) ); - case McdColS_Formatted: return prefix + ( !it.IsPresent ? L"" : ( it.IsFormatted ? _("Yes") : _("No")) ); - case McdColS_Type: return prefix + ( !it.IsPresent ? L"" : ( it.IsPSX? _("PS1") : _("PS2")) ); - case McdColS_DateModified: return prefix + ( !it.IsPresent || !it.DateModified.IsValid() ? L"" : it.DateModified.FormatDate() ); - case McdColS_DateCreated: return prefix + ( !it.IsPresent || !it.DateCreated.IsValid() ? L"" : it.DateCreated.FormatDate() ); - - case McdColS_Filename: - { - if (!it.IsPresent && it.Slot!=-1) return L""; - else if (!it.IsPresent && it.Slot==-1) - { - //UGLY #2: because this method must be const to be used at the list, - // it cannot access GetMcdProvider() which isn't (and shouldn't be) const. - //so.. a plain old cast does the trick. Hope it's not too bad. - avih. - if (((MemoryCardListView_Simple*)this)->GetMcdProvider().IsNonEmptyFilesystemCards()) - return _("[-- Disabled cards --]"); - else - return _("[-- No cards --]"); - } - - wxDirName filepath( it.Filename.GetPath() ); - - if (filepath.SameAs(g_Conf->Folders.MemoryCards)) - return prefix + it.Filename.GetFullName(); - else - return prefix + it.Filename.GetFullPath(); - } - } - - pxFail( "Unknown column index in MemoryCardListView -- returning an empty string." ); - return wxEmptyString; -} - -// return the icon for the given item. In report view, OnGetItemImage will -// only be called for the first column. See OnGetItemColumnImage for -// details. -int MemoryCardListView_Simple::OnGetItemImage(long item) const -{ - return _parent::OnGetItemImage( item ); -} - -// return the icon for the given item and column. -int MemoryCardListView_Simple::OnGetItemColumnImage(long item, long column) const -{ - return _parent::OnGetItemColumnImage( item, column ); -} - -static wxListItemAttr m_ItemAttr; - -// return the attribute for the item (may return NULL if none) -wxListItemAttr* MemoryCardListView_Simple::OnGetItemAttr(long item) const -{ - //m_disabled.SetTextColour( wxLIGHT_GREY ); - //m_targeted.SetBackgroundColour( wxColour(L"Yellow") ); - - if( !m_CardProvider ) return _parent::OnGetItemAttr(item); - const McdSlotItem& it( m_CardProvider->GetCardForViewIndex(item) ); - - m_ItemAttr = wxListItemAttr(); // Wipe it clean! - - if( it.Slot==-1 || it.IsPresent && !it.IsEnabled) - m_ItemAttr.SetTextColour( *wxLIGHT_GREY ); - - return &m_ItemAttr; -} diff --git a/pcsx2/gui/Panels/MemoryCardPanels.h b/pcsx2/gui/Panels/MemoryCardPanels.h deleted file mode 100644 index 2ea360a01c..0000000000 --- a/pcsx2/gui/Panels/MemoryCardPanels.h +++ /dev/null @@ -1,310 +0,0 @@ -/* PCSX2 - PS2 Emulator for PCs - * Copyright (C) 2002-2010 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 . - */ - -#pragma once - -#include "gui/AppCommon.h" - -#include -#include - -struct ListViewColumnInfo -{ - const wxChar* name; - int width; - wxListColumnFormat align; -}; - -// -------------------------------------------------------------------------------------- -// McdListItem / IMcdList -// -// These are the items at the list-view. -// Each item holds: -// - An internal slot association (or -1 if it isn't associated with an internal slot) -// - Properties of the internal slot (when associated with one) -// - Properties of the file associated with this list item (when associated with one) -// -------------------------------------------------------------------------------------- -struct McdSlotItem -{ - int Slot; //0-7: internal slot. -1: unrelated to an internal slot (the rest of the files at the folder). - bool IsPresent; //Whether or not a file is associated with this item (true/false when 0<=Slot<=7. Always true when Slot==-1) - MemoryCardType Type; //The implementation used for this memory card - - //Only meaningful when IsPresent==true (a file exists for this item): - wxFileName Filename; // full pathname - bool IsFormatted; - bool IsPSX; // False: PS2 memory card, True: PSX memory card - uint SizeInMB; // size, in megabytes! - wxDateTime DateCreated; - wxDateTime DateModified; - - //Only meaningful when 0<=Slot<=7 (associated with an internal slot): - // Properties of an internal slot, and translation from internal slot index to ps2 physical port (0-based-indexes). - bool IsEnabled; //This slot is enabled/disabled - bool IsMultitapSlot() const; - uint GetMtapPort() const; - uint GetMtapSlot() const; - - bool operator==( const McdSlotItem& right ) const; - bool operator!=( const McdSlotItem& right ) const; - - McdSlotItem() - { - Slot = -1; - SizeInMB = 0; - Type = MemoryCardType::Empty; - - IsPSX = false; - IsPresent = false; - IsEnabled = false; - IsFormatted = false; - } - -}; - -typedef std::vector McdList; - -class IMcdList -{ -public: - virtual void RefreshMcds() const=0; - virtual int GetLength() const=0; - virtual McdSlotItem& GetCardForViewIndex( int idx )=0; - virtual int GetSlotIndexForViewIndex( int listViewIndex )=0; - virtual wxDirName GetMcdPath() const=0; - virtual void PublicApply() =0; - virtual bool isFileAssignedToInternalSlot(const wxFileName cardFile) const =0; - virtual void RemoveCardFromSlot(const wxFileName cardFile) =0; - virtual bool IsNonEmptyFilesystemCards() const =0; - virtual bool UiDuplicateCard( McdSlotItem& src, McdSlotItem& dest ) =0; - -}; - -// -------------------------------------------------------------------------------------- -// BaseMcdListView -// -------------------------------------------------------------------------------------- -class BaseMcdListView : public wxListView -{ - typedef wxListView _parent; - -protected: - IMcdList* m_CardProvider; - -public: - void (*m_externHandler)(void); - void setExternHandler(void (*f)(void)){m_externHandler=f;}; - void OnChanged(wxEvent& evt){if (m_externHandler) m_externHandler(); evt.Skip();} - - virtual ~BaseMcdListView() = default; - BaseMcdListView( wxWindow* parent ) - : _parent( parent, wxID_ANY, wxDefaultPosition, wxDefaultSize, wxLC_REPORT | wxLC_SINGLE_SEL | wxLC_VIRTUAL ) - { - m_externHandler=NULL; - Bind(wxEVT_LEFT_UP, &BaseMcdListView::OnChanged, this, this->GetId()); - - m_CardProvider = NULL; - } - - virtual void SetCardCount( int length )=0; - virtual void SetMcdProvider( IMcdList* face ); - - virtual void LoadSaveColumns( IniInterface& ini ); - virtual const ListViewColumnInfo& GetDefaultColumnInfo( uint idx ) const=0; - - virtual IMcdList& GetMcdProvider(); -}; - -// -------------------------------------------------------------------------------------- -// MemoryCardListView_Simple -// -------------------------------------------------------------------------------------- -class MemoryCardListView_Simple : public BaseMcdListView -{ - typedef BaseMcdListView _parent; - -public: - virtual ~MemoryCardListView_Simple() = default; - MemoryCardListView_Simple( wxWindow* parent ); - - void CreateColumns(); - - virtual void SetCardCount( int length ); - virtual const ListViewColumnInfo& GetDefaultColumnInfo( uint idx ) const; - -protected: - // Overrides for wxLC_VIRTUAL - virtual wxString OnGetItemText(long item, long column) const; - virtual int OnGetItemImage(long item) const; - virtual int OnGetItemColumnImage(long item, long column) const; - virtual wxListItemAttr *OnGetItemAttr(long item) const; -}; - - -namespace Panels -{ - // -------------------------------------------------------------------------------------- - // BaseMcdListPanel - // -------------------------------------------------------------------------------------- - class BaseMcdListPanel - : public BaseSelectorPanel - , public wxFileDropTarget - , public IMcdList // derived classes need to implement this - { - typedef BaseSelectorPanel _parent; - - protected: - DirPickerPanel* m_FolderPicker; - BaseMcdListView* m_listview; - wxButton* m_btn_Refresh; - - wxBoxSizer* s_leftside_buttons; - wxBoxSizer* s_rightside_buttons; - - bool m_MultitapEnabled[2]; - - virtual void RefreshMcds() const; - - virtual wxDirName GetMcdPath() const - { - pxAssert(m_FolderPicker); - return m_FolderPicker->GetPath(); - } - - public: - virtual ~BaseMcdListPanel() = default; - BaseMcdListPanel( wxWindow* parent ); - - void CreateLayout(); - void SetMultitapEnabled( uint port, bool enabled ) - { - m_MultitapEnabled[port] = enabled; - RefreshMcds(); - } - - virtual void Apply(); - virtual void AppStatusEvent_OnSettingsApplied(); - }; - - // -------------------------------------------------------------------------------------- - // MemoryCardListPanel_Simple - // -------------------------------------------------------------------------------------- - class MemoryCardListPanel_Simple - : public BaseMcdListPanel - { - typedef BaseMcdListPanel _parent; - - protected: - McdSlotItem m_Cards[8]; - - wxButton* m_button_Rename; - - // Doubles as Create and Delete buttons - wxButton* m_button_Create; - - wxButton* m_button_Convert; - - // Doubles as Mount and Unmount buttons ("Enable"/"Disable" port) -// wxButton* m_button_Mount; - - wxButton* m_button_AssignUnassign; //insert/eject card - wxButton* m_button_Duplicate; - - - public: - virtual ~MemoryCardListPanel_Simple(); - MemoryCardListPanel_Simple( wxWindow* parent ); - - void UpdateUI(); - - // Interface Implementation for IMcdList - virtual int GetLength() const; - - virtual McdSlotItem& GetCardForViewIndex( int idx ); - virtual int GetSlotIndexForViewIndex( int viewIndex ); - virtual void PublicApply(){ Apply(); }; - void OnChangedListSelection(){ UpdateUI(); }; - virtual bool UiDuplicateCard( McdSlotItem& src, McdSlotItem& dest ); - - protected: - void OnCreateOrDeleteCard(wxCommandEvent& evt); - void OnConvertCard(wxCommandEvent& evt); - void OnMountCard(wxCommandEvent& evt); -// void OnRelocateCard(wxCommandEvent& evt); - void OnRenameFile(wxCommandEvent& evt); - void OnDuplicateFile(wxCommandEvent& evt); - void OnAssignUnassignFile(wxCommandEvent& evt); - - void OnListDrag(wxListEvent& evt); - void OnListSelectionChanged(wxListEvent& evt); - void OnOpenItemContextMenu(wxListEvent& evt); - void OnItemActivated(wxListEvent& evt); - - virtual bool OnDropFiles(wxCoord x, wxCoord y, const wxArrayString& filenames); - - std::vector m_allFilesystemCards; - McdSlotItem m_filesystemPlaceholderCard; - - virtual int GetNumFilesVisibleAsFilesystem() const; - virtual int GetNumVisibleInternalSlots() const; - virtual McdSlotItem& GetNthVisibleFilesystemCard(int n); - virtual bool IsSlotVisible(int slotIndex) const; - virtual void ReadFilesAtMcdFolder(); - virtual bool isFileAssignedAndVisibleOnList(const wxFileName cardFile) const; - virtual bool isFileAssignedToInternalSlot(const wxFileName cardFile) const; - virtual void RemoveCardFromSlot(const wxFileName cardFile); - virtual bool IsNonEmptyFilesystemCards() const; - - virtual void Apply(); - virtual void AppStatusEvent_OnSettingsApplied(); - virtual void DoRefresh(); - virtual bool ValidateEnumerationStatus(); - - virtual void UiRenameCard( McdSlotItem& card ); - virtual void UiCreateNewCard( McdSlotItem& card ); - virtual void UiConvertCard( McdSlotItem& card ); - virtual void UiDeleteCard( McdSlotItem& card ); - virtual void UiAssignUnassignFile( McdSlotItem& card ); - - }; - - // -------------------------------------------------------------------------------------- - // McdConfigPanel_Toggles / McdConfigPanel_Standard / McdConfigPanel_Multitap - // -------------------------------------------------------------------------------------- - class McdConfigPanel_Toggles : public BaseApplicableConfigPanel - { - typedef BaseApplicableConfigPanel _parent; - - protected: - //pxCheckBox* m_check_Multitap[2]; - pxCheckBox* m_check_Ejection; - pxCheckBox* m_folderAutoIndex; - - //moved to the system menu, just below "Save State" - //pxCheckBox* m_check_SavestateBackup; - - public: - McdConfigPanel_Toggles( wxWindow* parent ); - virtual ~McdConfigPanel_Toggles() = default; - void Apply(); - - protected: - void AppStatusEvent_OnSettingsApplied(); - void OnMultitapClicked(); - }; - -}; - -//avih: is the first one used?? -extern bool EnumerateMemoryCard( McdSlotItem& dest, const wxFileName& filename ); -//extern bool EnumerateMemoryCard( SimpleMcdItem& dest, const wxFileName& filename ); diff --git a/pcsx2/gui/Panels/MiscPanelStuff.cpp b/pcsx2/gui/Panels/MiscPanelStuff.cpp deleted file mode 100644 index 3c1d57c0e8..0000000000 --- a/pcsx2/gui/Panels/MiscPanelStuff.cpp +++ /dev/null @@ -1,187 +0,0 @@ -/* PCSX2 - PS2 Emulator for PCs - * Copyright (C) 2002-2010 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 . - */ - -#include "PrecompiledHeader.h" -#include "gui/App.h" -#include "ConfigurationPanels.h" - -#include "gui/Dialogs/ConfigurationDialog.h" - -#include "ps2/BiosTools.h" - -#include -#include - -using namespace Dialogs; -using namespace pxSizerFlags; - -// -------------------------------------------------------------------------------------- -// DocsFolderPickerPanel -// -------------------------------------------------------------------------------------- -Panels::DocsFolderPickerPanel::DocsFolderPickerPanel( wxWindow* parent, bool isFirstTime ) - : BaseApplicableConfigPanel( parent, wxVERTICAL, _("Usermode Selection") ) -{ - const wxString usermodeExplained( pxE( L"Please select your preferred default location for PCSX2 user-level documents below (includes memory cards, screenshots, settings, and savestates). These folder locations can be overridden at any time using the BIOS Selector panel." - ) ); - - const wxString usermodeWarning( pxE( L"You can change the preferred default location for PCSX2 user-level documents here (includes memory cards, screenshots, settings, and savestates). This option only affects Standard Paths which are set to use the installation default value." - ) ); - - const RadioPanelItem UsermodeOptions[] = - { - RadioPanelItem( - _("User Documents (recommended)"), - _("Location: ") + PathDefs::GetDocuments(DocsFolder_User).ToString() - ), - - RadioPanelItem( - _("Custom folder:"), - wxEmptyString, - _("This setting may require administration privileges from your operating system, depending on how your system is configured.") - ) - }; - - m_radio_UserMode = new pxRadioPanel( this, UsermodeOptions ); - m_radio_UserMode->SetPaddingHoriz( m_radio_UserMode->GetPaddingVert() + 4 ); - m_radio_UserMode->Realize(); - if( pxStaticText* woot = m_radio_UserMode->GetSubText(0) ) woot->Unwrapped(); // wrapping sucks for path names! - - m_dirpicker_custom = new DirPickerPanel( this, FolderId_Documents, AddAppName(_("Select a document root for %s")) ); - - *this += Heading( isFirstTime ? usermodeExplained : usermodeWarning ); - *this += m_radio_UserMode | StdExpand(); - *this += m_dirpicker_custom | pxExpand.Border( wxLEFT, StdPadding + m_radio_UserMode->GetIndentation() ); - *this += 4; - - Bind(wxEVT_RADIOBUTTON, &DocsFolderPickerPanel::OnRadioChanged, this); -} - -DocsModeType Panels::DocsFolderPickerPanel::GetDocsMode() const -{ - return (DocsModeType) m_radio_UserMode->GetSelection(); -} - -void Panels::DocsFolderPickerPanel::Apply() -{ - DocsFolderMode = (DocsModeType) m_radio_UserMode->GetSelection(); - CustomDocumentsFolder = m_dirpicker_custom->GetPath(); -} - -void Panels::DocsFolderPickerPanel::AppStatusEvent_OnSettingsApplied() -{ - if( m_radio_UserMode ) m_radio_UserMode->SetSelection( DocsFolderMode ); - if( m_dirpicker_custom ) m_dirpicker_custom->Enable( DocsFolderMode == DocsFolder_Custom ); -} - -void Panels::DocsFolderPickerPanel::OnRadioChanged( wxCommandEvent& evt ) -{ - evt.Skip(); - - if( !m_radio_UserMode ) return; - - if( m_dirpicker_custom ) - m_dirpicker_custom->Enable( m_radio_UserMode->GetSelection() == (int)DocsFolder_Custom ); - - if( pxStaticText* woot = m_radio_UserMode->GetSubText(0) ) - woot->Enable( m_radio_UserMode->GetSelection() == (int)DocsFolder_User ); -} - -// -------------------------------------------------------------------------------------- -// LanguageSelectionPanel -// -------------------------------------------------------------------------------------- -Panels::LanguageSelectionPanel::LanguageSelectionPanel( wxWindow* parent, bool showApply ) - : BaseApplicableConfigPanel( parent, wxHORIZONTAL ) -{ - m_picker = NULL; - i18n_EnumeratePackages( m_langs ); - - int size = m_langs.size(); - std::unique_ptr compiled( new wxString[size] ); - - for( int i=0; iSetToolTip(_("Make this language my default right now!")); - - *this += applyButton | pxSizerFlags::StdSpace(); - *this += 5; - } - - Bind(wxEVT_BUTTON, &LanguageSelectionPanel::OnApplyLanguage_Clicked, this, pxID_RestartWizard); - - m_picker->SetSelection( 0 ); // always default to System Default -} - -void Panels::LanguageSelectionPanel::OnApplyLanguage_Clicked( wxCommandEvent& evt ) -{ - evt.Skip(); - Apply(); -} - -void Panels::LanguageSelectionPanel::Apply() -{ - if( !m_picker ) return; - - // The combo box's order is sorted and may not match our m_langs order, so - // we have to do a string comparison to find a match: - - wxString sel( m_picker->GetString( m_picker->GetSelection() ) ); - - g_Conf->LanguageCode = L"default"; // use this if no matches found - g_Conf->LanguageId = wxLANGUAGE_DEFAULT; - int size = m_langs.size(); - for( int i=0; iLanguageCode = m_langs[i].canonicalName; - g_Conf->LanguageId = m_langs[i].wxLangId; - } - break; - } - } - -} - -void Panels::LanguageSelectionPanel::AppStatusEvent_OnSettingsApplied() -{ - if (m_picker) - { - m_picker->SetSelection( 0 ); // always default to System Default - - if (g_Conf->LanguageCode.IsEmpty()) - g_Conf->LanguageCode = L"default"; - - for (uint i=0; iLanguageCode)) - { - m_picker->SetSelection( i ); - } - } - } -} diff --git a/pcsx2/gui/Panels/PathsPanel.cpp b/pcsx2/gui/Panels/PathsPanel.cpp deleted file mode 100644 index c35fd2c791..0000000000 --- a/pcsx2/gui/Panels/PathsPanel.cpp +++ /dev/null @@ -1,94 +0,0 @@ -/* PCSX2 - PS2 Emulator for PCs - * Copyright (C) 2002-2021 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 . - */ - -#include "PrecompiledHeader.h" -#include "ConfigurationPanels.h" - -#include - -using namespace pxSizerFlags; - -static const int BetweenFolderSpace = 5; - -// -------------------------------------------------------------------------------------- -// Panels::StandardPathsPanel (implementations) -// -------------------------------------------------------------------------------------- -Panels::StandardPathsPanel::StandardPathsPanel( wxWindow* parent ) - : BaseApplicableConfigPanel( parent ) -{ - *this += BetweenFolderSpace; - *this += (new DirPickerPanel( this, FolderId_Cheats, - _("Cheats:"), - _("Select folder for Cheats") ))-> - SetToolTip( pxEt( L"This folder is where PCSX2 loads cheats from." - ) - ) | SubGroup(); - - *this += BetweenFolderSpace; - *this += (new DirPickerPanel( this, FolderId_Savestates, - _("Savestates:"), - _("Select folder for Savestates") ))-> - SetToolTip( pxEt( L"This folder is where PCSX2 records savestates; which are recorded either by using menus/toolbars, or by pressing F1/F3 (save/load)." - ) - ) | SubGroup(); - - *this += BetweenFolderSpace; - *this += (new DirPickerPanel( this, FolderId_Snapshots, - _("Snapshots:"), - _("Select a folder for Snapshots") ))-> - SetToolTip( pxEt( L"This folder is where PCSX2 saves screenshots." - ) - ) | SubGroup(); - - *this += BetweenFolderSpace; - *this += (new DirPickerPanel( this, FolderId_Logs, - _("Logs/Dumps:" ), - _("Select a folder for logs/dumps") ))-> - SetToolTip( pxEt( L"This folder is where PCSX2 saves its logfiles and diagnostic dumps." - ) - ) | SubGroup(); - - *this += BetweenFolderSpace; - *this += (new DirPickerPanel( this, FolderId_Textures, - _("Textures:"), - _("Select a folder for textures") ))-> - SetToolTip( pxEt( L"This folder is where PCSX2 saves texture dumps and replacements." - ) - ) | SubGroup(); - - /* - *this += BetweenFolderSpace; - *this += (new DirPickerPanel( this, FolderId_MemoryCards, - _("Memorycards:"), - _("Select a default Memorycards folder") ))-> - SetToolTip( pxE( L"This is the default path where PCSX2 loads or creates its memory cards, and can be overridden in the MemoryCard Configuration by using absolute filenames." - ) - ) | SubGroup();*/ - - *this += BetweenFolderSpace; - - GetSizer()->SetMinSize( wxSize( 475, GetSizer()->GetMinSize().GetHeight() ) ); -} - -void Panels::StandardPathsPanel::Apply() -{ - -} - -void Panels::StandardPathsPanel::AppStatusEvent_OnSettingsApplied() -{ - -} - diff --git a/pcsx2/gui/Panels/SpeedhacksPanel.cpp b/pcsx2/gui/Panels/SpeedhacksPanel.cpp deleted file mode 100644 index bc29649f12..0000000000 --- a/pcsx2/gui/Panels/SpeedhacksPanel.cpp +++ /dev/null @@ -1,366 +0,0 @@ -/* PCSX2 - PS2 Emulator for PCs - * Copyright (C) 2002-2010 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 . - */ - -#include "PrecompiledHeader.h" -#include "gui/App.h" -#include "ConfigurationPanels.h" - -using namespace pxSizerFlags; - -const wxChar* Panels::SpeedHacksPanel::GetEECycleRateSliderMsg( int val ) -{ - switch( val ) - { - case -3: - { - m_msg_eeRate->SetForegroundColour(wxColour(L"Red")); - return pxEt(L"50% cyclerate. Significant reduction of CPU requirements. Speedup for very lightweight games, slows down others. FMVs and audio may stutter or skip."); - } - case -2: - { - const wxColour LightRed = wxColour(255, 80, 0); - m_msg_eeRate->SetForegroundColour(LightRed); - return pxEt(L"60% cyclerate. Moderate reduction of CPU requirements. Speedup for lightweight games, slows down others. FMVs and audio may stutter or skip."); - } - case -1: - { - const wxColour Orange = wxColour(255, 120, 0); - m_msg_eeRate->SetForegroundColour(Orange); - return pxEt(L"75% cyclerate. Slight reduction of CPU requirements. Speedup for less demanding games, slows down others."); - } - case 0: - { - const wxColour DarkSeaGreen = wxColour(14, 158, 19); - m_msg_eeRate->SetForegroundColour(DarkSeaGreen); - return pxEt(L"Default cyclerate. Runs the emulated PS2 Emotion Engine at normal speed."); - } - case 1: - { - const wxColour Orange = wxColour(255, 120, 0); - m_msg_eeRate->SetForegroundColour(Orange); - return pxEt(L"130% cyclerate. Moderate increase of CPU requirements. Variable framerate games may have higher internal framerates."); - } - case 2: - { - const wxColour LightRed = wxColour(255, 80, 0); - m_msg_eeRate->SetForegroundColour(LightRed); - return pxEt(L"180% cyclerate. Significant increase of CPU requirements. Variable framerate games will have higher internal framerates. FMVs may be slow. May cause stability problems."); - } - case 3: - { - m_msg_eeRate->SetForegroundColour(wxColour(L"Red")); - return pxEt(L"300% cyclerate. Extreme increase of CPU requirements. Variable framerate games will have higher internal framerates. FMVs may be slow. May cause stability problems."); - } - default: - break; - } - - return L"Unreachable Warning Suppressor!!"; -} - -const wxChar* Panels::SpeedHacksPanel::GetEECycleSkipSliderMsg( int val ) -{ - switch( val ) - { - case 0: - { - const wxColour DarkSeaGreen = wxColour(14, 158, 19); - m_msg_eeSkip->SetForegroundColour(DarkSeaGreen); - return pxEt(L"Disables EE Cycle Skipping. Most compatible setting."); - } - case 1: - { - const wxColour Orange = wxColour(255, 120, 0); - m_msg_eeSkip->SetForegroundColour(Orange); - return pxEt(L"Mild EE Cycle Skipping. Mild slow down for most games, but may help some games with mild VU starvation problems run at full speed."); - } - case 2: - { - const wxColour LightRed = wxColour(255, 80, 0); - m_msg_eeSkip->SetForegroundColour(LightRed); - return pxEt(L"Moderate EE Cycle Skipping. Slow down for most games, but may help some games with moderate VU starvation problems run at full speed."); - } - default: - break; - } - - return L"Unreachable Warning Suppressor!!"; -} - -void Panels::SpeedHacksPanel::SetEEcycleSliderMsg() -{ - m_msg_eeRate->SetLabel( GetEECycleRateSliderMsg(m_slider_eeRate->GetValue()) ); -} - -void Panels::SpeedHacksPanel::SetVUcycleSliderMsg() -{ - m_msg_eeSkip->SetLabel( GetEECycleSkipSliderMsg(m_slider_eeSkip->GetValue()) ); -} - -Panels::SpeedHacksPanel::SpeedHacksPanel( wxWindow* parent ) - : BaseApplicableConfigPanel_SpecificConfig( parent ) -{ - const wxSizerFlags sliderFlags( wxSizerFlags().Border( wxLEFT | wxRIGHT, 8 ).Expand() ); - - m_check_Enable = new pxCheckBox( - this, _("Enable speedhacks"), - pxE( L"Speedhacks usually improve emulation speed"", but can cause glitches, broken audio, and false FPS readings. When having emulation problems, disable this panel first." - ) - ); - m_check_Enable->SetToolTip(_("A safe and easy way to make sure that all speedhacks are completely disabled.")).SetSubPadding( 1 ); - - - m_button_Defaults = new wxButton( this, wxID_DEFAULT, _("Restore Defaults") ); - - // ------------------------------------------------------------------------ - // EE Cyclerate Hack Section: - - // Misc help text that I might find a home for later: - // Cycle stealing works by 'fast-forwarding' the EE by an arbitrary number of cycles whenever VU1 micro-programs - // are run, which works as a rough-guess skipping of what would normally be idle time spent running on the EE. - - m_eeRateSliderPanel = new wxPanelWithHelpers( this, wxVERTICAL, _("EE Cyclerate [Not Recommended]") ); - - m_slider_eeRate = new wxSlider( m_eeRateSliderPanel, wxID_ANY, 0, -3, 3, - wxDefaultPosition, wxDefaultSize, wxHORIZONTAL | wxSL_AUTOTICKS | wxSL_LABELS ); - - m_msg_eeRate = new pxStaticHeading( m_eeRateSliderPanel ); - const wxChar* eeRate_tooltip = pxEt( L"Modifies the emulated Emotion Engine CPU clock. Higher values may increase the internal framerate in games with variable framerates, but will increase CPU requirements substantially. Lower values will reduce the CPU load allowing lightweight games to run full speed on weaker CPUs." ); - pxSetToolTip( m_slider_eeRate, eeRate_tooltip ); - pxSetToolTip( m_msg_eeRate, eeRate_tooltip ); - - // ------------------------------------------------------------------------ - // EE Cycle Skipping Hack Section: - - m_eeSkipSliderPanel = new wxPanelWithHelpers( this, wxVERTICAL, _("EE Cycle Skipping [Not Recommended]") ); - - m_slider_eeSkip = new wxSlider(m_eeSkipSliderPanel, wxID_ANY, 0, 0, 2, wxDefaultPosition, wxDefaultSize, - wxHORIZONTAL | wxSL_AUTOTICKS | wxSL_LABELS ); - - m_msg_eeSkip = new pxStaticHeading(m_eeSkipSliderPanel); - - const wxChar* eeSkip_tooltip = pxEt( L"Makes the emulated Emotion Engine skip cycles, allowing VU microprograms to execute at faster intervals. Helps a small subset of games with VU starvation problems, E.g. Shadow of the Colossus. More often than not this is harmful to performance and causes FPS readouts to be inaccurate." ); - - pxSetToolTip( m_slider_eeSkip, eeSkip_tooltip ); - pxSetToolTip( m_msg_eeSkip, eeSkip_tooltip ); - - // ------------------------------------------------------------------------ - // microVU Hacks Section: - - wxPanelWithHelpers* vuHacksPanel = new wxPanelWithHelpers( this, wxVERTICAL, _("microVU Hacks") ); - - m_check_vuFlagHack = new pxCheckBox( vuHacksPanel, _("mVU Flag Hack"), - _("Good Speedup and High Compatibility; may cause bad graphics... [Recommended]" ) ); - - m_check_vuThread = new pxCheckBox( vuHacksPanel, _("MTVU (Multi-Threaded microVU1)"), - _("Good Speedup and High Compatibility; may cause hanging... [Recommended on 3+ cores]") ); - - m_check_vu1Instant = new pxCheckBox(vuHacksPanel, _("Instant VU1"), - _("Good Speedup and High Compatibility; may cause some graphical errors")); - - m_check_vuFlagHack->SetToolTip( pxEt( L"Updates Status Flags only on blocks which will read them, instead of all the time. This is safe most of the time." - ) ); - - m_check_vuThread->SetToolTip( pxEt( L"Runs VU1 on its own thread. Generally a speedup on CPUs with 3 or more cores. This is safe for most games, but a few games are incompatible and may hang. In the case of GS limited games, it may be a slowdown (especially on dual core CPUs)." - ) ); - - m_check_vu1Instant->SetToolTip(pxEt(L"Runs VU1 instantly. Provides a modest speed improvement in most games. This is safe for most games, but a few games may exhibit graphical errors." - )); - - // ------------------------------------------------------------------------ - // All other hacks Section: - - wxPanelWithHelpers* miscHacksPanel = new wxPanelWithHelpers( this, wxVERTICAL, _("Other Hacks") ); - - m_check_intc = new pxCheckBox( miscHacksPanel, _("Enable INTC Spin Detection"), - _("Huge speedup for some games, with almost no compatibility side effects. [Recommended]") ); - - m_check_waitloop = new pxCheckBox( miscHacksPanel, _("Enable Wait Loop Detection"), - _("Moderate speedup for some games, with no known side effects. [Recommended]" ) ); - - m_check_fastCDVD = new pxCheckBox( miscHacksPanel, _("Enable fast CDVD"), - _("Fast disc access, less loading times. [Not Recommended]") ); - - - m_check_intc->SetToolTip( pxEt( L"This hack works best for games that use the INTC Status register to wait for vsyncs, which includes primarily non-3D RPG titles. Games that do not use this method of vsync will see little or no speedup from this hack." - ) ); - - m_check_waitloop->SetToolTip( pxEt( L"Primarily targetting the EE idle loop at address 0x81FC0 in the kernel, this hack attempts to detect loops whose bodies are guaranteed to result in the same machine state for every iteration until a scheduled event triggers emulation of another unit. After a single iteration of such loops, we advance to the time of the next event or the end of the processor's timeslice, whichever comes first." - ) ); - - m_check_fastCDVD->SetToolTip( pxEt( L"Check HDLoader compatibility lists for known games that have issues with this (often marked as needing 'mode 1' or 'slow DVD')." - ) ); - - - *m_eeRateSliderPanel += m_slider_eeRate | sliderFlags; - *m_eeRateSliderPanel += m_msg_eeRate | sliderFlags; - - *m_eeSkipSliderPanel += m_slider_eeSkip | sliderFlags; - *m_eeSkipSliderPanel += m_msg_eeSkip | sliderFlags; - - *vuHacksPanel += m_check_vuFlagHack | StdExpand(); - *vuHacksPanel += m_check_vuThread | StdExpand(); - *vuHacksPanel += m_check_vu1Instant | StdExpand(); - - *miscHacksPanel += m_check_intc | StdExpand(); - *miscHacksPanel += m_check_waitloop | StdExpand(); - *miscHacksPanel += m_check_fastCDVD | StdExpand(); - - s_table = new wxFlexGridSizer( 3, 2, 0, 0 ); - s_table->Add(m_eeRateSliderPanel, StdExpand()); - s_table->Add(m_eeSkipSliderPanel, StdExpand()); - s_table->Add(miscHacksPanel, StdExpand()); - s_table->Add(vuHacksPanel, StdExpand()); - s_table->Add(new wxStaticText(this, -1, "")); - s_table->Add(m_button_Defaults, StdButton()); - - m_sizer = new wxBoxSizer(wxVERTICAL); - m_sizer->Add(m_check_Enable, StdExpand()); - m_sizer->Add(s_table); - - SetSizer(m_sizer); - // ------------------------------------------------------------------------ - - Bind(wxEVT_SCROLL_CHANGED, &SpeedHacksPanel::EECycleRate_Scroll, this, m_slider_eeRate->GetId()); - Bind(wxEVT_SCROLL_CHANGED, &SpeedHacksPanel::VUCycleRate_Scroll, this, m_slider_eeSkip->GetId()); - Bind(wxEVT_CHECKBOX, &SpeedHacksPanel::OnEnable_Toggled, this, m_check_Enable->GetId()); - Bind(wxEVT_BUTTON, &SpeedHacksPanel::Defaults_Click, this, wxID_DEFAULT); -} - -// Doesn't modify values - only locks(gray out)/unlocks as necessary. -void Panels::SpeedHacksPanel::EnableStuff( AppConfig* configToUse ) -{ - if (!configToUse) configToUse = g_Conf.get(); - - bool hasPreset = configToUse->EnablePresets; - bool hacksEnabled = configToUse->EnableSpeedHacks; - bool HacksEnabledAndNoPreset = hacksEnabled && !hasPreset; - - // Main checkbox and Restore-defaults - locked only if presets are enabled - m_check_Enable->Enable(!hasPreset); - m_button_Defaults->Enable(!hasPreset); - - // lock/unlock the slider panels rather than the sliders themselves - // in order to affect both sliders and texts - m_eeRateSliderPanel->Enable(HacksEnabledAndNoPreset); - m_eeSkipSliderPanel->Enable(HacksEnabledAndNoPreset); - - // checkboxes - m_check_vuFlagHack->Enable(HacksEnabledAndNoPreset); - m_check_intc->Enable(HacksEnabledAndNoPreset); - m_check_waitloop->Enable(HacksEnabledAndNoPreset); - m_check_fastCDVD->Enable(HacksEnabledAndNoPreset); - - // Grayout MTVU on safest preset - m_check_vuThread->Enable(hacksEnabled && (!hasPreset || configToUse->PresetIndex != 0)); - - // Layout necessary to ensure changed slider text gets re-aligned properly - // and to properly gray/ungray pxStaticText stuff (I suspect it causes a - // paint event to be sent on Windows) - Layout(); -} - -void Panels::SpeedHacksPanel::AppStatusEvent_OnSettingsApplied() -{ - //Console.WriteLn("SpeedHacksPanel::AppStatusEvent_OnSettingsApplied()"); - ApplyConfigToGui( *g_Conf ); -} - -void Panels::SpeedHacksPanel::ApplyConfigToGui( AppConfig& configToApply, int flags ) -{ - Pcsx2Config::SpeedhackOptions& opts=configToApply.EmuOptions.Speedhacks; - - // First, set the values of the widgets (checked/unchecked etc). - m_check_Enable->SetValue(configToApply.EnableSpeedHacks); - - m_slider_eeRate ->SetValue( opts.EECycleRate ); - - opts.EECycleSkip = std::clamp(opts.EECycleSkip, 0, 2); - m_slider_eeSkip ->SetValue( opts.EECycleSkip ); - - SetEEcycleSliderMsg(); - SetVUcycleSliderMsg(); - - m_check_vuFlagHack->SetValue(opts.vuFlagHack); - m_check_intc->SetValue(opts.IntcStat); - m_check_waitloop->SetValue(opts.WaitLoop); - m_check_fastCDVD->SetValue(opts.fastCDVD); - m_check_vuThread->SetValue(opts.vuThread); - m_check_vu1Instant->SetValue(opts.vu1Instant); - - // Then, lock(gray out)/unlock the widgets as necessary. - EnableStuff( &configToApply ); -} - -// Apply the values from the widgets to the config, -// regardless if locked (grayed out) or not. -void Panels::SpeedHacksPanel::Apply() -{ - g_Conf->EnableSpeedHacks = m_check_Enable->GetValue(); - - Pcsx2Config::SpeedhackOptions& opts( g_Conf->EmuOptions.Speedhacks ); - - opts.EECycleRate = m_slider_eeRate->GetValue(); - opts.EECycleSkip = m_slider_eeSkip->GetValue(); - - opts.WaitLoop = m_check_waitloop->GetValue(); - opts.fastCDVD = m_check_fastCDVD->GetValue(); - opts.IntcStat = m_check_intc->GetValue(); - opts.vuFlagHack = m_check_vuFlagHack->GetValue(); - opts.vuThread = m_check_vuThread->GetValue(); - opts.vu1Instant = m_check_vu1Instant->GetValue(); - - // If the user has a command line override specified, we need to disable it - // so that their changes take effect - wxGetApp().Overrides.DisableSpeedhacks = false; -} - -void Panels::SpeedHacksPanel::OnEnable_Toggled( wxCommandEvent& evt ) -{ - AppConfig tmp=*g_Conf; - tmp.EnablePresets=false; //if clicked, button was enabled, so not using a preset --> let EnableStuff work - tmp.EnableSpeedHacks = m_check_Enable->GetValue(); - - EnableStuff( &tmp ); - evt.Skip(); -} - -void Panels::SpeedHacksPanel::Defaults_Click( wxCommandEvent& evt ) -{ - //Can only get here presets are disabled at the GUI (= the 'Defaults' button is enabled). - AppConfig currentConfigWithHacksReset = *g_Conf; - currentConfigWithHacksReset.EmuOptions.Speedhacks = Pcsx2Config::SpeedhackOptions(); - currentConfigWithHacksReset.EnablePresets=false;//speed hacks gui depends on preset, apply it as if presets are disabled - ApplyConfigToGui( currentConfigWithHacksReset ); - evt.Skip(); -} - -void Panels::SpeedHacksPanel::EECycleRate_Scroll(wxScrollEvent &event) -{ - SetEEcycleSliderMsg(); - - Layout(); - - event.Skip(); -} - -void Panels::SpeedHacksPanel::VUCycleRate_Scroll(wxScrollEvent &event) -{ - SetVUcycleSliderMsg(); - - Layout(); - - event.Skip(); -} diff --git a/pcsx2/gui/Panels/VideoPanel.cpp b/pcsx2/gui/Panels/VideoPanel.cpp deleted file mode 100644 index f241b9e3cd..0000000000 --- a/pcsx2/gui/Panels/VideoPanel.cpp +++ /dev/null @@ -1,267 +0,0 @@ -/* PCSX2 - PS2 Emulator for PCs - * Copyright (C) 2002-2021 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 . - */ - -#include "PrecompiledHeader.h" -#include "gui/App.h" -#include "gui/AppAccelerators.h" -#include "gui/Dialogs/ConfigurationDialog.h" -#include "ConfigurationPanels.h" - -#include -#include "fmt/core.h" - -using namespace pxSizerFlags; - -// -------------------------------------------------------------------------------------- -// FramelimiterPanel Implementations -// -------------------------------------------------------------------------------------- - -Panels::FramelimiterPanel::FramelimiterPanel( wxWindow* parent ) - : BaseApplicableConfigPanel_SpecificConfig( parent ) -{ - // Implement custom hotkeys (F4) with translatable string intact + not blank in GUI. - m_check_LimiterDisable = new pxCheckBox( this, _("Disable Framelimiting") + wxString(" (") + wxGetApp().GlobalAccels->findKeycodeWithCommandId("Framelimiter_MasterToggle").toTitleizedString()+ wxString(")"), - _("Uncaps FPS. Useful for running benchmarks.") ); - - m_check_LimiterDisable->SetToolTip( pxEt( L"Note that when Framelimiting is disabled, Turbo and SlowMotion modes will not be available either." - ) ); - - m_spin_NominalPct = new wxSpinCtrl(this); - m_spin_SlomoPct = new wxSpinCtrl(this); - m_spin_TurboPct = new wxSpinCtrl(this); - - m_text_BaseNtsc = CreateNumericalTextCtrl( this, 7 ); - m_text_BasePal = CreateNumericalTextCtrl( this, 7 ); - - m_spin_NominalPct ->SetRange( 10, 1000 ); - m_spin_SlomoPct ->SetRange( 5, 1000 ); - m_spin_TurboPct ->SetRange( 10, 1000 ); - - // ------------------------------------------------------------ - // Sizers and Layouts - - *this += m_check_LimiterDisable | StdExpand(); - - wxFlexGridSizer& s_spins( *new wxFlexGridSizer( 5 ) ); - s_spins.AddGrowableCol( 0 ); - - s_spins += Label(_("Base Framerate Adjust:")) | StdExpand(); - s_spins += 5; - s_spins += m_spin_NominalPct | pxBorder(wxTOP, 3); - s_spins += Label(L"%") | StdExpand(); - s_spins += 5; - - // Implement custom hotkeys (Shift + Tab) with translatable string intact + not blank in GUI. - - s_spins += Label(_("Slow Motion Adjust:") + wxString::Format(" (%s)", wxGetApp().GlobalAccels->findKeycodeWithCommandId("Framelimiter_SlomoToggle").toTitleizedString())) | StdExpand(); - s_spins += 5; - s_spins += m_spin_SlomoPct | pxBorder(wxTOP, 3); - s_spins += Label(L"%") | StdExpand(); - s_spins += 5; - - // Implement custom hotkeys (Tab) with translatable string intact + not blank in GUI. - - s_spins += Label(_("Turbo Adjust:") + wxString::Format(" (%s)", wxGetApp().GlobalAccels->findKeycodeWithCommandId("Framelimiter_TurboToggle").toTitleizedString())) | StdExpand(); - s_spins += 5; - s_spins += m_spin_TurboPct | pxBorder(wxTOP, 3); - s_spins += Label(L"%") | StdExpand(); - s_spins += 5; - - wxFlexGridSizer& s_fps( *new wxFlexGridSizer( 5 ) ); - s_fps.AddGrowableCol( 0 ); - - s_fps += Label(_("NTSC Framerate:")) | StdExpand(); - s_fps += 5; - s_fps += m_text_BaseNtsc | pxBorder(wxTOP, 2).Right(); - s_fps += Label(_("FPS")) | StdExpand(); - s_fps += 5; - - s_fps += Label(_("PAL Framerate:")) | StdExpand(); - s_fps += 5; - s_fps += m_text_BasePal | pxBorder(wxTOP, 2).Right(); - s_fps += Label(_("FPS")) | StdExpand(); - s_fps += 5; - - *this += s_spins | pxExpand; - *this += 5; - *this += s_fps | pxExpand; - - AppStatusEvent_OnSettingsApplied(); -} - -void Panels::FramelimiterPanel::AppStatusEvent_OnSettingsApplied() -{ - ApplyConfigToGui( *g_Conf ); -} - -void Panels::FramelimiterPanel::ApplyConfigToGui( AppConfig& configToApply, int flags ) -{ - const Pcsx2Config::FramerateOptions& appfps( configToApply.EmuOptions.Framerate ); - const Pcsx2Config::GSOptions& gsconf( configToApply.EmuOptions.GS ); - - if( ! (flags & AppConfig::APPLY_FLAG_FROM_PRESET) ) - { //Presets don't control these: only change if config doesn't come from preset. - - m_check_LimiterDisable->SetValue(!gsconf.FrameLimitEnable); - - m_spin_TurboPct->SetValue(appfps.TurboScalar * 100.0); - m_spin_SlomoPct->SetValue(appfps.SlomoScalar * 100.0); - - m_spin_TurboPct->Enable(true); - m_spin_SlomoPct->Enable(true); - } - - m_text_BaseNtsc->ChangeValue(wxString::FromDouble(gsconf.FramerateNTSC, 2)); - m_text_BasePal->ChangeValue(wxString::FromDouble(gsconf.FrameratePAL, 2)); - - m_spin_NominalPct->SetValue(appfps.NominalScalar * 100.0); - m_spin_NominalPct->Enable(!configToApply.EnablePresets); - - m_text_BaseNtsc->Enable(!configToApply.EnablePresets); - m_text_BasePal->Enable(!configToApply.EnablePresets); -} - -void Panels::FramelimiterPanel::Apply() -{ - Pcsx2Config::FramerateOptions& appfps( g_Conf->EmuOptions.Framerate ); - Pcsx2Config::GSOptions& gsconf( g_Conf->EmuOptions.GS ); - - gsconf.FrameLimitEnable = !m_check_LimiterDisable->GetValue(); - - appfps.NominalScalar = static_cast(m_spin_NominalPct->GetValue()) / 100.0; - appfps.TurboScalar = static_cast(m_spin_TurboPct->GetValue()) / 100.0; - appfps.SlomoScalar = static_cast(m_spin_SlomoPct->GetValue()) / 100.0; - - wxString ntsc_framerate_string = m_text_BaseNtsc->GetValue(); - wxString pal_framerate_string = m_text_BasePal->GetValue(); - - double framerate = 0.0; - - if (ntsc_framerate_string.ToDouble(&framerate)) - gsconf.FramerateNTSC = framerate; - - if (pal_framerate_string.ToDouble(&framerate)) - gsconf.FrameratePAL = framerate; - - appfps.SanityCheck(); - - // If the user has a command line override specified, we need to disable it - // so that their changes take effect - wxGetApp().Overrides.ProfilingMode = false; -} - -// -------------------------------------------------------------------------------------- -// VideoPanel Implementation -// -------------------------------------------------------------------------------------- - -Panels::VideoPanel::VideoPanel( wxWindow* parent ) : - BaseApplicableConfigPanel_SpecificConfig( parent ) -{ - wxPanelWithHelpers* left = new wxPanelWithHelpers( this, wxVERTICAL ); -#ifdef PCSX2_DEVBUILD - m_check_SynchronousGS = new pxCheckBox( left, _("Use Synchronized MTGS"), - _t("For troubleshooting potential bugs in the MTGS only, as it is potentially very slow.") - ); -#endif - - m_spinner_VsyncQueue = new wxSpinCtrl(left); - m_spinner_VsyncQueue->SetRange(0, 3); - - m_restore_defaults = new wxButton(this, wxID_DEFAULT, _("Restore Defaults")); - - m_spinner_VsyncQueue->SetToolTip( pxEt(L"Setting this to a lower value improves input lag, a value around 2 or 3 will slightly improve framerates. (Default is 2)")); -#ifdef PCSX2_DEVBUILD - m_check_SynchronousGS->SetToolTip( pxEt( L"Enable this if you think MTGS thread sync is causing crashes or graphical errors. For debugging to see if GS is running at the correct speed.")); -#endif - - //GSWindowSettingsPanel* winpan = new GSWindowSettingsPanel( left ); - //winpan->AddFrame(_("Display/Window")); - - m_fpan = new FramelimiterPanel( left ); - m_fpan->AddFrame(_("Framelimiter")); - - wxFlexGridSizer* s_table = new wxFlexGridSizer( 2 ); - wxGridSizer* s_vsyncs = new wxGridSizer( 2 ); - s_table->AddGrowableCol( 0, 1 ); - s_table->AddGrowableCol( 1, 1 ); - - *left += m_fpan | pxExpand; - *left += 5; - - *s_vsyncs += left->Label(_("Vsyncs in MTGS Queue:")) | StdExpand(); - *s_vsyncs += m_spinner_VsyncQueue | pxBorder(wxTOP, -2).Right(); - *left += s_vsyncs | StdExpand(); -#ifdef PCSX2_DEVBUILD - *left += 2; - *left += m_check_SynchronousGS | StdExpand(); -#endif - - *s_table += left | StdExpand(); - - *this += s_table | pxExpand; - - *this += 12; - *this += m_restore_defaults | StdButton(); - - Bind(wxEVT_BUTTON, &VideoPanel::Defaults_Click, this, wxID_DEFAULT); - AppStatusEvent_OnSettingsApplied(); -} - -void Panels::VideoPanel::Defaults_Click(wxCommandEvent& evt) -{ - AppConfig config = *g_Conf; - config.EmuOptions.GS = Pcsx2Config::GSOptions(); - config.EmuOptions.Framerate = Pcsx2Config::FramerateOptions(); - VideoPanel::ApplyConfigToGui(config); - m_fpan->ApplyConfigToGui(config); - evt.Skip(); -} - -void Panels::VideoPanel::OnOpenWindowSettings( wxCommandEvent& evt ) -{ - AppOpenDialog( this ); - - // don't evt.skip, this prevents the Apply button from being activated. :) -} - -void Panels::VideoPanel::Apply() -{ -#ifdef PCSX2_DEVBUILD - g_Conf->EmuOptions.GS.SynchronousMTGS = m_check_SynchronousGS->GetValue(); -#endif - g_Conf->EmuOptions.GS.VsyncQueueSize = m_spinner_VsyncQueue->GetValue(); -} - -void Panels::VideoPanel::AppStatusEvent_OnSettingsApplied() -{ - ApplyConfigToGui(*g_Conf); -} - -void Panels::VideoPanel::ApplyConfigToGui( AppConfig& configToApply, int flags ){ - -#ifdef PCSX2_DEVBUILD - m_check_SynchronousGS->SetValue( configToApply.EmuOptions.GS.SynchronousMTGS ); - m_check_SynchronousGS->Enable(!configToApply.EnablePresets); -#endif - m_spinner_VsyncQueue->SetValue( configToApply.EmuOptions.GS.VsyncQueueSize ); - - if( flags & AppConfig::APPLY_FLAG_MANUALLY_PROPAGATE ) - { - m_fpan->ApplyConfigToGui( configToApply, true ); - } - - Layout(); -} - diff --git a/pcsx2/gui/PathDefs.h b/pcsx2/gui/PathDefs.h deleted file mode 100644 index 7502fb81c2..0000000000 --- a/pcsx2/gui/PathDefs.h +++ /dev/null @@ -1,95 +0,0 @@ -/* PCSX2 - PS2 Emulator for PCs - * Copyright (C) 2002-2009 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 . - */ - -#pragma once - -#include "wxDirName.h" - -enum FoldersEnum_t -{ - // FIXME : Settings folder is no longer part of the user-local - // ini storage. They have been moved to an app-local ini storage class (which is currently - // handled lazily via global variables) - FolderId_Settings = 0, - - FolderId_Bios, - FolderId_Snapshots, - FolderId_Savestates, - FolderId_MemoryCards, - FolderId_Logs, - FolderId_Langs, - - FolderId_Documents, - - FolderId_Cheats, - FolderId_CheatsWS, - FolderId_Cache, - FolderId_Textures, - - FolderId_COUNT -}; - -// -------------------------------------------------------------------------------------- -// PathDefs (namespace) -// -------------------------------------------------------------------------------------- -// Contains default values for various pcsx2 path names and locations. -// -// Note: The members of this namespace are intended for default value initialization only. -// Everything else should use the path folder assignments in g_Conf instead, since those -// are user-configurable. -// -namespace PathDefs -{ - // complete pathnames are returned by these functions (may or may not be absolute) - - extern wxDirName GetSnapshots(); - extern wxDirName GetBios(); - extern wxDirName GetSavestates(); - extern wxDirName GetMemoryCards(); - extern wxDirName GetSettings(); - extern wxDirName GetLogs(); - extern wxDirName GetLangs(); - extern wxDirName GetCheats(); - extern wxDirName GetCheatsWS(); - extern wxDirName GetDocs(); - - extern wxDirName Get( FoldersEnum_t folderidx ); - - // Base folder names used to extend out the documents/approot folder base into a complete - // path. These are typically for internal AppConfig use only, barring a few special cases. - namespace Base - { - extern const wxDirName& Snapshots(); - extern const wxDirName& Savestates(); - extern const wxDirName& MemoryCards(); - extern const wxDirName& Settings(); - extern const wxDirName& Logs(); - extern const wxDirName& Dumps(); - extern const wxDirName& Langs(); - extern const wxDirName& Cheats(); - extern const wxDirName& CheatsWS(); - extern const wxDirName& Docs(); - } -} - -namespace FilenameDefs -{ - extern wxFileName GetUiConfig(); - extern wxFileName GetUiKeysConfig(); - extern wxFileName GetVmConfig(); - extern wxFileName GetUsermodeConfig(); - extern const wxFileName& Memcard( uint port, uint slot ); -}; - diff --git a/pcsx2/gui/PathUtils.cpp b/pcsx2/gui/PathUtils.cpp deleted file mode 100644 index dedddeb40a..0000000000 --- a/pcsx2/gui/PathUtils.cpp +++ /dev/null @@ -1,187 +0,0 @@ -/* PCSX2 - PS2 Emulator for PCs - * Copyright (C) 2002-2010 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 . - */ - -#include "common/Path.h" -#include "common/Assertions.h" -#include "common/Exceptions.h" -#include "gui/PersistentThread.h" -#include "wxDirName.h" - -#include -#include - -// --------------------------------------------------------------------------------- -// wxDirName (implementations) -// --------------------------------------------------------------------------------- - -wxFileName wxDirName::Combine(const wxFileName& right) const -{ - pxAssertMsg(IsDir(), "Warning: Malformed directory name detected during wxDirName concatenation."); - if (right.IsAbsolute()) - return right; - - // Append any directory parts from right, and then set the filename. - // Except we can't do that because our m_members are private (argh!) and there is no API - // for getting each component of the path. So instead let's use Normalize: - - wxFileName result(right); - result.Normalize(wxPATH_NORM_ENV_VARS | wxPATH_NORM_DOTS | wxPATH_NORM_ABSOLUTE, GetPath()); - return result; -} - -wxDirName wxDirName::Combine(const wxDirName& right) const -{ - pxAssertMsg(IsDir() && right.IsDir(), "Warning: Malformed directory name detected during wDirName concatenation."); - - wxDirName result(right); - result.Normalize(wxPATH_NORM_ENV_VARS | wxPATH_NORM_DOTS | wxPATH_NORM_ABSOLUTE, GetPath()); - return result; -} - -wxDirName& wxDirName::Normalize(int flags, const wxString& cwd) -{ - pxAssertMsg(IsDir(), "Warning: Malformed directory name detected during wDirName normalization."); - if (!wxFileName::Normalize(flags, cwd)) - throw Exception::ParseError().SetDiagMsg("wxDirName::Normalize operation failed."); - return *this; -} - -wxDirName& wxDirName::MakeRelativeTo(const wxString& pathBase) -{ - pxAssertMsg(IsDir(), "Warning: Malformed directory name detected during wDirName normalization."); - if (!wxFileName::MakeRelativeTo(pathBase)) - throw Exception::ParseError().SetDiagMsg("wxDirName::MakeRelativeTo operation failed."); - return *this; -} - -wxDirName& wxDirName::MakeAbsolute(const wxString& cwd) -{ - pxAssertMsg(IsDir(), "Warning: Malformed directory name detected during wDirName normalization."); - if (!wxFileName::MakeAbsolute(cwd)) - throw Exception::ParseError().SetDiagMsg("wxDirName::MakeAbsolute operation failed."); - return *this; -} - -void wxDirName::Rmdir() const -{ - if (!Exists()) - return; - wxFileName::Rmdir(); - // TODO : Throw exception if operation failed? Do we care? -} - -bool wxDirName::Mkdir() const -{ -// wxWidgets recurses directory creation for us. - -// only exist in wx2.9 and above -#ifndef wxS_DIR_DEFAULT -#define wxS_DIR_DEFAULT 0777 -#endif - - if (Exists()) - return true; - return wxFileName::Mkdir(wxS_DIR_DEFAULT, wxPATH_MKDIR_FULL); -} - - -// --------------------------------------------------------------------------------- -// Path namespace (wxFileName helpers) -// --------------------------------------------------------------------------------- - - -bool Path::IsRelative(const wxString& path) -{ - return wxDirName(path).IsRelative(); -} - -// Returns -1 if the file does not exist. -s64 Path::GetFileSize(const wxString& path) -{ - if (!wxFile::Exists(path.c_str())) - return -1; - return (s64)wxFileName::GetSize(path).GetValue(); -} - - -wxString Path::Normalize(const wxString& src) -{ - wxFileName normalize(src); - normalize.Normalize(); - return normalize.GetFullPath(); -} - -wxString Path::Normalize(const wxDirName& src) -{ - return wxDirName(src).Normalize().ToString(); -} - -wxString Path::MakeAbsolute(const wxString& src) -{ - wxFileName absolute(src); - absolute.MakeAbsolute(); - return absolute.GetFullPath(); -} - -// Concatenates two pathnames together, inserting delimiters (backslash on win32) -// as needed! Assumes the 'dest' is allocated to at least g_MaxPath length. -// -wxString Path::CombineWx(const wxString& srcPath, const wxString& srcFile) -{ - return (wxDirName(srcPath) + srcFile).GetFullPath(); -} - -wxString Path::CombineWx(const wxDirName& srcPath, const wxFileName& srcFile) -{ - return (srcPath + srcFile).GetFullPath(); -} - -wxString Path::CombineWx(const wxString& srcPath, const wxDirName& srcFile) -{ - return (wxDirName(srcPath) + srcFile).ToString(); -} - -wxString Path::ReplaceFilename(const wxString& src, const wxString& newfilename) -{ - wxFileName jojo(src); - jojo.SetFullName(newfilename); - return jojo.GetFullPath(); -} - -wxString Path::GetFilename(const wxString& src) -{ - return wxFileName(src).GetFullName(); -} - -wxString Path::GetFilenameWithoutExt(const wxString& src) -{ - return wxFileName(src).GetName(); -} - -wxString Path::GetDirectory(const wxString& src) -{ - return wxFileName(src).GetPath(); -} - -// returns the base/root directory of the given path. -// Example /this/that/something.txt -> dest == "/" -wxString Path::GetRootDirectory(const wxString& src) -{ - size_t pos = src.find_first_of(wxFileName::GetPathSeparators()); - if (pos == wxString::npos) - return wxString(); - else - return wxString(src.begin(), src.begin() + pos); -} diff --git a/pcsx2/gui/PersistentThread.cpp b/pcsx2/gui/PersistentThread.cpp deleted file mode 100644 index 5c1e1b66fe..0000000000 --- a/pcsx2/gui/PersistentThread.cpp +++ /dev/null @@ -1,1198 +0,0 @@ -/* PCSX2 - PS2 Emulator for PCs - * Copyright (C) 2002-2010 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 . - */ - -#include "PrecompiledHeader.h" - -#ifdef __linux__ -#include // for pthread_kill, which is in pthread.h on w32-pthreads -#endif - -#if defined(__APPLE__) -#include -#include // semaphore_create() and semaphore_destroy() -#include // semaphore_*() -#include // mach_error_string() -#include // mach_absolute_time() -#include -#endif - -#include "PersistentThread.h" -#include "common/EventSource.inl" -#include "common/General.h" -#include - -using namespace Threading; - -template class EventSource; - -// 100ms interval for waitgui (issued from blocking semaphore waits on the main thread, -// to avoid gui deadlock). -const wxTimeSpan Threading::def_yieldgui_interval(0, 0, 0, 100); - -ConsoleLogSource_Threading::ConsoleLogSource_Threading() -{ - static const TraceLogDescriptor myDesc = - { - "p&xThread", "pxThread", - "Threading activity: start, detach, sync, deletion, etc."}; - - m_Descriptor = &myDesc; -} - -ConsoleLogSource_Threading pxConLog_Thread; - - -class StaticMutex : public Mutex -{ -protected: - bool& m_DeletedFlag; - -public: - StaticMutex(bool& deletedFlag) - : m_DeletedFlag(deletedFlag) - { - } - - virtual ~StaticMutex() - { - m_DeletedFlag = true; - } -}; - -static pthread_key_t curthread_key = 0; -static s32 total_key_count = 0; - -static bool tkl_destructed = false; -static StaticMutex total_key_lock(tkl_destructed); - -static void make_curthread_key(const pxThread* thr) -{ - pxAssumeDev(!tkl_destructed, "total_key_lock is destroyed; program is shutting down; cannot create new thread key."); - - ScopedLock lock(total_key_lock); - if (total_key_count++ != 0) - return; - - if (0 != pthread_key_create(&curthread_key, NULL)) - { - pxThreadLog.Error(thr->GetName(), L"Thread key creation failed (probably out of memory >_<)"); - curthread_key = 0; - } -} - -static void unmake_curthread_key() -{ - ScopedLock lock; - if (!tkl_destructed) - lock.AssignAndLock(total_key_lock); - - if (--total_key_count > 0) - return; - - if (curthread_key) - pthread_key_delete(curthread_key); - - curthread_key = 0; -} - -// make life easier for people using VC++ IDE by using this format, which allows double-click -// response times from the Output window... -std::string DiagnosticOrigin::ToString(const char* msg) const -{ - std::string message; - - fmt::format_to(std::back_inserter(message), "{}({}) : assertion failed:\n", srcfile, line); - - if (function) - fmt::format_to(std::back_inserter(message), " Function: {}\n", function); - - if (condition) - fmt::format_to(std::back_inserter(message), " Condition: {}\n", condition); - - if (msg) - fmt::format_to(std::back_inserter(message), " Message: {}\n", msg); - - return message; -} - -void Threading::pxTestCancel() -{ - pthread_testcancel(); -} - -// Returns a handle to the current persistent thread. If the current thread does not belong -// to the pxThread table, NULL is returned. Since the main/ui thread is not created -// through pxThread it will also return NULL. Callers can use wxThread::IsMain() to -// test if the NULL thread is the main thread. -pxThread* Threading::pxGetCurrentThread() -{ - return !curthread_key ? NULL : (pxThread*)pthread_getspecific(curthread_key); -} - -// returns the name of the current thread, or "Unknown" if the thread is neither a pxThread -// nor the Main/UI thread. -wxString Threading::pxGetCurrentThreadName() -{ - if (pxThread* thr = pxGetCurrentThread()) - { - return thr->GetName(); - } - else if (wxThread::IsMain()) - { - return L"Main/UI"; - } - - return L"Unknown"; -} - -// (intended for internal use only) -// Returns true if the Wait is recursive, or false if the Wait is safe and should be -// handled via normal yielding methods. -bool Threading::_WaitGui_RecursionGuard(const wxChar* name) -{ - AffinityAssert_AllowFrom_MainUI(); - - // In order to avoid deadlock we need to make sure we cut some time to handle messages. - // But this can result in recursive yield calls, which would crash the app. Protect - // against them here and, if recursion is detected, perform a standard blocking wait. - - static int __Guard = 0; - RecursionGuard guard(__Guard); - - //if( pxAssertDev( !guard.IsReentrant(), "Recursion during UI-bound threading wait object." ) ) return false; - - if (!guard.IsReentrant()) - return false; - pxThreadLog.Write(pxGetCurrentThreadName(), - pxsFmt(L"Yield recursion in %s; opening modal dialog.", name)); - return true; -} - -void Threading::pxThread::_pt_callback_cleanup(void* handle) -{ - ((pxThread*)handle)->_ThreadCleanup(); -} - -Threading::pxThread::pxThread(const wxString& name) - : m_name(name) - , m_thread() - , m_detached(true) // start out with m_thread in detached/invalid state - , m_running(false) -{ -} - -// This destructor performs basic "last chance" cleanup, which is a blocking join -// against the thread. Extending classes should almost always implement their own -// thread closure process, since any pxThread will, by design, not terminate -// unless it has been properly canceled (resulting in deadlock). -// -// Thread safety: This class must not be deleted from its own thread. That would be -// like marrying your sister, and then cheating on her with your daughter. -Threading::pxThread::~pxThread() -{ - try - { - pxThreadLog.Write(GetName(), L"Executing default destructor!"); - - if (m_running) - { - pxThreadLog.Write(GetName(), L"Waiting for running thread to end..."); - m_mtx_InThread.Wait(); - pxThreadLog.Write(GetName(), L"Thread ended gracefully."); - } - Threading::Sleep(1); - Detach(); - } - DESTRUCTOR_CATCHALL -} - -bool Threading::pxThread::AffinityAssert_AllowFromSelf(const DiagnosticOrigin& origin) const -{ - if (IsSelf()) - return true; - - if (IsDevBuild) - pxOnAssertFail(origin.srcfile, origin.line, origin.function, pxsFmt(L"Thread affinity violation: Call allowed from '%s' thread only.", WX_STR(GetName())).ToUTF8().data()); - - return false; -} - -bool Threading::pxThread::AffinityAssert_DisallowFromSelf(const DiagnosticOrigin& origin) const -{ - if (!IsSelf()) - return true; - - if (IsDevBuild) - pxOnAssertFail(origin.srcfile, origin.line, origin.function, pxsFmt(L"Thread affinity violation: Call is *not* allowed from '%s' thread.", WX_STR(GetName())).ToUTF8().data()); - - return false; -} - -void Threading::pxThread::FrankenMutex(Mutex& mutex) -{ - if (mutex.RecreateIfLocked()) - { - // Our lock is bupkis, which means the previous thread probably deadlocked. - // Let's create a new mutex lock to replace it. - - pxThreadLog.Error(GetName(), L"Possible deadlock detected on restarted mutex!"); - } -} - -// Main entry point for starting or e-starting a persistent thread. This function performs necessary -// locks and checks for avoiding race conditions, and then calls OnStart() immediately before -// the actual thread creation. Extending classes should generally not override Start(), and should -// instead override DoPrepStart instead. -// -// This function should not be called from the owner thread. -void Threading::pxThread::Start() -{ - // Prevents sudden parallel startup, and or parallel startup + cancel: - ScopedLock startlock(m_mtx_start); - if (m_running) - { - pxThreadLog.Write(GetName(), L"Start() called on running thread; ignorning..."); - return; - } - - Detach(); // clean up previous thread handle, if one exists. - OnStart(); - - m_except = NULL; - - pxThreadLog.Write(GetName(), L"Calling pthread_create..."); - if (pthread_create(&m_thread, NULL, _internal_callback, this) != 0) - throw Exception::ThreadCreationError(this).SetDiagMsg(StringUtil::StdStringFromFormat("Thread creation error: %s" , std::strerror(errno))); - -#ifdef ASAN_WORKAROUND - // Recent Asan + libc6 do pretty bad stuff on the thread init => https://gcc.gnu.org/bugzilla/show_bug.cgi?id=77982 - // - // In our case, the semaphore was posted (counter is 1) but thread is still - // waiting... So waits 100ms and checks the counter value manually - if (!m_sem_startup.WaitWithoutYield(wxTimeSpan(0, 0, 0, 100))) - { - if (m_sem_startup.Count() == 0) - throw Exception::ThreadCreationError(this).SetDiagMsg("Thread creation error: %s thread never posted startup semaphore."); - } -#else - if (!m_sem_startup.WaitWithoutYield(wxTimeSpan(0, 0, 3, 0))) - { - RethrowException(); - - // And if the thread threw nothing of its own: - throw Exception::ThreadCreationError(this).SetDiagMsg("Thread creation error: %s thread never posted startup semaphore."); - } -#endif - - // Event Rationale (above): Performing this semaphore wait on the created thread is "slow" in the - // sense that it stalls the calling thread completely until the new thread is created - // (which may not always be desirable). But too bad. In order to safely use 'running' locks - // and detachment management, this *has* to be done. By rule, starting new threads shouldn't - // be done very often anyway, hence the concept of Threadpooling for rapidly rotating tasks. - // (and indeed, this semaphore wait might, in fact, be very swift compared to other kernel - // overhead in starting threads). - - // (this could also be done using operating system specific calls, since any threaded OS has - // functions that allow us to see if a thread is running or not, and to block against it even if - // it's been detached -- removing the need for m_mtx_InThread and the semaphore wait above. But - // pthreads kinda lacks that stuff, since pthread_join() has no timeout option making it im- - // possible to safely block against a running thread) -} - -// Returns: TRUE if the detachment was performed, or FALSE if the thread was -// already detached or isn't running at all. -// This function should not be called from the owner thread. -bool Threading::pxThread::Detach() -{ - AffinityAssert_DisallowFromSelf(pxDiagSpot); - - if (m_detached.exchange(true)) - return false; - pthread_detach(m_thread); - return true; -} - -bool Threading::pxThread::_basecancel() -{ - if (!m_running) - return false; - - if (m_detached) - { - pxThreadLog.Warn(GetName(), L"Ignoring attempted cancellation of detached thread."); - return false; - } - - pthread_cancel(m_thread); - return true; -} - -// Remarks: -// Provision of non-blocking Cancel() is probably academic, since destroying a pxThread -// object performs a blocking Cancel regardless of if you explicitly do a non-blocking Cancel() -// prior, since the ExecuteTaskInThread() method requires a valid object state. If you really need -// fire-and-forget behavior on threads, use pthreads directly for now. -// -// This function should not be called from the owner thread. -// -// Parameters: -// isBlocking - indicates if the Cancel action should block for thread completion or not. -// -// Exceptions raised by the blocking thread will be re-thrown into the main thread. If isBlocking -// is false then no exceptions will occur. -// -void Threading::pxThread::Cancel(bool isBlocking) -{ - AffinityAssert_DisallowFromSelf(pxDiagSpot); - - // Prevent simultaneous startup and cancel, necessary to avoid - ScopedLock startlock(m_mtx_start); - - if (!_basecancel()) - return; - - if (isBlocking) - { - WaitOnSelf(m_mtx_InThread); - Detach(); - } -} - -bool Threading::pxThread::Cancel(const wxTimeSpan& timespan) -{ - AffinityAssert_DisallowFromSelf(pxDiagSpot); - - // Prevent simultaneous startup and cancel: - ScopedLock startlock(m_mtx_start); - - if (!_basecancel()) - return true; - - if (!WaitOnSelf(m_mtx_InThread, timespan)) - return false; - Detach(); - return true; -} - - -// Blocks execution of the calling thread until this thread completes its task. The -// caller should make sure to signal the thread to exit, or else blocking may deadlock the -// calling thread. Classes which extend pxThread should override this method -// and signal any necessary thread exit variables prior to blocking. -// -// Returns the return code of the thread. -// This method is roughly the equivalent of pthread_join(). -// -// Exceptions raised by the blocking thread will be re-thrown into the main thread. -// -void Threading::pxThread::Block() -{ - AffinityAssert_DisallowFromSelf(pxDiagSpot); - WaitOnSelf(m_mtx_InThread); -} - -bool Threading::pxThread::Block(const wxTimeSpan& timeout) -{ - AffinityAssert_DisallowFromSelf(pxDiagSpot); - return WaitOnSelf(m_mtx_InThread, timeout); -} - -bool Threading::pxThread::IsSelf() const -{ - // Detached threads may have their pthread handles recycled as newer threads, causing - // false IsSelf reports. - return !m_detached && (pthread_self() == m_thread); -} - -bool Threading::pxThread::IsRunning() const -{ - return m_running; -} - -void Threading::pxThread::AddListener(EventListener_Thread& evt) -{ - evt.SetThread(this); - m_evtsrc_OnDelete.Add(evt); -} - -// Throws an exception if the thread encountered one. Uses the BaseException's Rethrow() method, -// which ensures the exception type remains consistent. Debuggable stacktraces will be lost, since -// the thread will have allowed itself to terminate properly. -void Threading::pxThread::RethrowException() const -{ - // Thread safety note: always detach the m_except pointer. If we checked it for NULL, the - // pointer might still be invalid after detachment, so might as well just detach and check - // after. - - ScopedExcept ptr(const_cast(this)->m_except.DetachPtr()); - if (ptr) - ptr->Rethrow(); -} - -static bool m_BlockDeletions = false; - -bool Threading::AllowDeletions() -{ - AffinityAssert_AllowFrom_MainUI(); - return !m_BlockDeletions; -} - -void Threading::YieldToMain() -{ - m_BlockDeletions = true; - wxTheApp->Yield(true); - m_BlockDeletions = false; -} - -void Threading::pxThread::_selfRunningTest(const wxChar* name) const -{ - if (HasPendingException()) - { - pxThreadLog.Error(GetName(), pxsFmt(L"An exception was thrown while waiting on a %s.", name)); - RethrowException(); - } - - if (!m_running) - { - throw Exception::CancelEvent(pxsFmt( - L"Blocking thread %s was terminated while another thread was waiting on a %s.", - WX_STR(GetName()), name).ToStdString()); - } - - // Thread is still alive and kicking (for now) -- yield to other messages and hope - // that impending chaos does not ensue. [it shouldn't since we block pxThread - // objects from being deleted until outside the scope of a mutex/semaphore wait). - - if ((wxTheApp != NULL) && wxThread::IsMain() && !_WaitGui_RecursionGuard(L"WaitForSelf")) - Threading::YieldToMain(); -} - -// This helper function is a deadlock-safe method of waiting on a semaphore in a pxThread. If the -// thread is terminated or canceled by another thread or a nested action prior to the semaphore being -// posted, this function will detect that and throw a CancelEvent exception is thrown. -// -// Note: Use of this function only applies to semaphores which are posted by the worker thread. Calling -// this function from the context of the thread itself is an error, and a dev assertion will be generated. -// -// Exceptions: -// This function will rethrow exceptions raised by the persistent thread, if it throws an error -// while the calling thread is blocking (which also means the persistent thread has terminated). -// -void Threading::pxThread::WaitOnSelf(Semaphore& sem) const -{ - if (!AffinityAssert_DisallowFromSelf(pxDiagSpot)) - return; - - while (true) - { - if (sem.WaitWithoutYield(wxTimeSpan(0, 0, 0, 333))) - return; - _selfRunningTest(L"semaphore"); - } -} - -// This helper function is a deadlock-safe method of waiting on a mutex in a pxThread. -// If the thread is terminated or canceled by another thread or a nested action prior to the -// mutex being unlocked, this function will detect that and a CancelEvent exception is thrown. -// -// Note: Use of this function only applies to mutexes which are acquired by a worker thread. -// Calling this function from the context of the thread itself is an error, and a dev assertion -// will be generated. -// -// Exceptions: -// This function will rethrow exceptions raised by the persistent thread, if it throws an -// error while the calling thread is blocking (which also means the persistent thread has -// terminated). -// -void Threading::pxThread::WaitOnSelf(Mutex& mutex) const -{ - if (!AffinityAssert_DisallowFromSelf(pxDiagSpot)) - return; - - while (true) - { - if (mutex.WaitWithoutYield(wxTimeSpan(0, 0, 0, 333))) - return; - _selfRunningTest(L"mutex"); - } -} - -static const wxTimeSpan SelfWaitInterval(0, 0, 0, 333); - -bool Threading::pxThread::WaitOnSelf(Semaphore& sem, const wxTimeSpan& timeout) const -{ - if (!AffinityAssert_DisallowFromSelf(pxDiagSpot)) - return true; - - wxTimeSpan runningout(timeout); - - while (runningout.GetMilliseconds() > 0) - { - const wxTimeSpan interval((SelfWaitInterval < runningout) ? SelfWaitInterval : runningout); - if (sem.WaitWithoutYield(interval)) - return true; - _selfRunningTest(L"semaphore"); - runningout -= interval; - } - return false; -} - -bool Threading::pxThread::WaitOnSelf(Mutex& mutex, const wxTimeSpan& timeout) const -{ - if (!AffinityAssert_DisallowFromSelf(pxDiagSpot)) - return true; - - wxTimeSpan runningout(timeout); - - while (runningout.GetMilliseconds() > 0) - { - const wxTimeSpan interval((SelfWaitInterval < runningout) ? SelfWaitInterval : runningout); - if (mutex.WaitWithoutYield(interval)) - return true; - _selfRunningTest(L"mutex"); - runningout -= interval; - } - return false; -} - -// Inserts a thread cancellation point. If the thread has received a cancel request, this -// function will throw an SEH exception designed to exit the thread (so make sure to use C++ -// object encapsulation for anything that could leak resources, to ensure object unwinding -// and cleanup, or use the DoThreadCleanup() override to perform resource cleanup). -void Threading::pxThread::TestCancel() const -{ - AffinityAssert_AllowFromSelf(pxDiagSpot); - pthread_testcancel(); -} - -// Executes the virtual member method -void Threading::pxThread::_try_virtual_invoke(void (pxThread::*method)()) -{ - try - { - (this->*method)(); - } - - // ---------------------------------------------------------------------------- - // Neat repackaging for STL Runtime errors... - // - catch (std::runtime_error& ex) - { - m_except = new Exception::RuntimeError(ex, GetName().ToUTF8()); - } - - // ---------------------------------------------------------------------------- - catch (Exception::RuntimeError& ex) - { - BaseException* woot = ex.Clone(); - woot->DiagMsg() += pxsFmt(L"(thread:%s)", WX_STR(GetName())).ToUTF8(); - m_except = woot; - } -#ifndef PCSX2_DEVBUILD - // ---------------------------------------------------------------------------- - // Bleh... don't bother with std::exception. runtime_error should catch anything - // useful coming out of the core STL libraries anyway, and these are best handled by - // the MSVC debugger (or by silent random annoying fail on debug-less linux). - /*catch( std::logic_error& ex ) - { - throw BaseException( pxsFmt( L"STL Logic Error (thread:%s): %s", - WX_STR(GetName()), WX_STR(fromUTF8( ex.what() )) ) - ); - } - catch( std::exception& ex ) - { - throw BaseException( pxsFmt( L"STL exception (thread:%s): %s", - WX_STR(GetName()), WX_STR(fromUTF8( ex.what() )) ) - ); - }*/ - // ---------------------------------------------------------------------------- - // BaseException -- same deal as LogicErrors. - // - catch (BaseException& ex) - { - BaseException* woot = ex.Clone(); - woot->DiagMsg() += pxsFmt(L"(thread:%s)", WX_STR(GetName())).ToUTF8(); - m_except = woot; - } -#endif -} - -// invoked internally when canceling or exiting the thread. Extending classes should implement -// OnCleanupInThread() to extend cleanup functionality. -void Threading::pxThread::_ThreadCleanup() -{ - AffinityAssert_AllowFromSelf(pxDiagSpot); - _try_virtual_invoke(&pxThread::OnCleanupInThread); - m_mtx_InThread.Release(); - - // Must set m_running LAST, as thread destructors depend on this value (it is used - // to avoid destruction of the thread until all internal data use has stopped. - m_running = false; -} - -wxString Threading::pxThread::GetName() const -{ - ScopedLock lock(m_mtx_ThreadName); - return m_name; -} - -void Threading::pxThread::SetName(const wxString& newname) -{ - ScopedLock lock(m_mtx_ThreadName); - m_name = newname; -} - -// This override is called by PeristentThread when the thread is first created, prior to -// calling ExecuteTaskInThread, and after the initial InThread lock has been claimed. -// This code is also executed within a "safe" environment, where the creating thread is -// blocked against m_sem_event. Make sure to do any necessary variable setup here, without -// worry that the calling thread might attempt to test the status of those variables -// before initialization has completed. -// -void Threading::pxThread::OnStartInThread() -{ - m_detached = false; - m_running = true; -} - -void Threading::pxThread::_internal_execute() -{ - m_mtx_InThread.Acquire(); - - _DoSetThreadName(GetName()); - make_curthread_key(this); - if (curthread_key) - pthread_setspecific(curthread_key, this); - - OnStartInThread(); - m_sem_startup.Post(); - - _try_virtual_invoke(&pxThread::ExecuteTaskInThread); -} - -// Called by Start, prior to actual starting of the thread, and after any previous -// running thread has been canceled or detached. -void Threading::pxThread::OnStart() -{ - FrankenMutex(m_mtx_InThread); - m_sem_event.Reset(); - m_sem_startup.Reset(); -} - -// Extending classes that override this method should always call it last from their -// personal implementations. -void Threading::pxThread::OnCleanupInThread() -{ - if (curthread_key) - pthread_setspecific(curthread_key, NULL); - - unmake_curthread_key(); - - m_evtsrc_OnDelete.Dispatch(0); -} - -// passed into pthread_create, and is used to dispatch the thread's object oriented -// callback function -void* Threading::pxThread::_internal_callback(void* itsme) -{ - if (!pxAssert(itsme != NULL)) - return NULL; - - internal_callback_helper(itsme); - return nullptr; -} - -// __try is used in pthread_cleanup_push when CLEANUP_SEH is used as the cleanup model. -// That can't be used in a function that has objects that require unwinding (compile -// error C2712), so move it into a separate function. -void Threading::pxThread::internal_callback_helper(void* itsme) -{ - pxThread& owner = *static_cast(itsme); - - pthread_cleanup_push(_pt_callback_cleanup, itsme); - owner._internal_execute(); - pthread_cleanup_pop(true); -} - -void Threading::pxThread::_DoSetThreadName(const wxString& name) -{ - _DoSetThreadName(static_cast(name.ToUTF8())); -} - -// -------------------------------------------------------------------------------------- -// pthread Cond is an evil api that is not suited for Pcsx2 needs. -// Let's not use it. (Air) -// -------------------------------------------------------------------------------------- - -#if 0 -Threading::WaitEvent::WaitEvent() -{ - int err = 0; - - err = pthread_cond_init(&cond, NULL); - err = pthread_mutex_init(&mutex, NULL); -} - -Threading::WaitEvent::~WaitEvent() -{ - pthread_cond_destroy( &cond ); - pthread_mutex_destroy( &mutex ); -} - -void Threading::WaitEvent::Set() -{ - pthread_mutex_lock( &mutex ); - pthread_cond_signal( &cond ); - pthread_mutex_unlock( &mutex ); -} - -void Threading::WaitEvent::Wait() -{ - pthread_mutex_lock( &mutex ); - pthread_cond_wait( &cond, &mutex ); - pthread_mutex_unlock( &mutex ); -} -#endif - -// -------------------------------------------------------------------------------------- -// BaseThreadError -// -------------------------------------------------------------------------------------- - -std::string Exception::BaseThreadError::FormatDiagnosticMessage() const -{ - // This is dangerous and stupid. Thanks wx/px/nonsense. - wxString thread_name = (m_thread == NULL) ? L"Null Thread Object" : m_thread->GetName(); - return StringUtil::StdStringFromFormat(m_message_diag.c_str(), thread_name.ToUTF8().data()); -} - -std::string Exception::BaseThreadError::FormatDisplayMessage() const -{ - wxString thread_name = (m_thread == NULL) ? L"Null Thread Object" : m_thread->GetName(); - return StringUtil::StdStringFromFormat(m_message_diag.c_str(), thread_name.ToUTF8().data()); -} - -pxThread& Exception::BaseThreadError::Thread() -{ - pxAssertDev(m_thread != NULL, "NULL thread object on ThreadError exception."); - return *m_thread; -} -const pxThread& Exception::BaseThreadError::Thread() const -{ - pxAssertDev(m_thread != NULL, "NULL thread object on ThreadError exception."); - return *m_thread; -} - -#ifndef __APPLE__ - -Threading::Semaphore::Semaphore() -{ - sem_init(&m_sema, false, 0); -} - -Threading::Semaphore::~Semaphore() -{ - sem_destroy(&m_sema); -} - -void Threading::Semaphore::Reset() -{ - sem_destroy(&m_sema); - sem_init(&m_sema, false, 0); -} - -void Threading::Semaphore::Post() -{ - sem_post(&m_sema); -} - -void Threading::Semaphore::Post(int multiple) -{ -#if defined(_MSC_VER) - sem_post_multiple(&m_sema, multiple); -#else - // Only w32pthreads has the post_multiple, but it's easy enough to fake: - while (multiple > 0) - { - multiple--; - sem_post(&m_sema); - } -#endif -} - -void Threading::Semaphore::WaitWithoutYield() -{ - pxAssertMsg(!wxThread::IsMain(), "Unyielding semaphore wait issued from the main/gui thread. Please use Wait() instead."); - sem_wait(&m_sema); -} - -bool Threading::Semaphore::WaitWithoutYield(const wxTimeSpan& timeout) -{ - wxDateTime megafail(wxDateTime::UNow() + timeout); - const timespec fail = {megafail.GetTicks(), megafail.GetMillisecond() * 1000000}; - return sem_timedwait(&m_sema, &fail) == 0; -} - - -// This is a wxApp-safe implementation of Wait, which makes sure and executes the App's -// pending messages *if* the Wait is performed on the Main/GUI thread. This ensures that -// user input continues to be handled and that windoes continue to repaint. If the Wait is -// called from another thread, no message pumping is performed. -// -void Threading::Semaphore::Wait() -{ -#if wxUSE_GUI - if (!wxThread::IsMain() || (wxTheApp == NULL)) - { - sem_wait(&m_sema); - } - else if (_WaitGui_RecursionGuard(L"Semaphore::Wait")) - { - sem_wait(&m_sema); - } - else - { - //ScopedBusyCursor hourglass( Cursor_KindaBusy ); - while (!WaitWithoutYield(def_yieldgui_interval)) - YieldToMain(); - } -#else - sem_wait(&m_sema); -#endif -} - -// This is a wxApp-safe implementation of WaitWithoutYield, which makes sure and executes the App's -// pending messages *if* the Wait is performed on the Main/GUI thread. This ensures that -// user input continues to be handled and that windows continue to repaint. If the Wait is -// called from another thread, no message pumping is performed. -// -// Returns: -// false if the wait timed out before the semaphore was signaled, or true if the signal was -// reached prior to timeout. -// -bool Threading::Semaphore::Wait(const wxTimeSpan& timeout) -{ -#if wxUSE_GUI - if (!wxThread::IsMain() || (wxTheApp == NULL)) - { - return WaitWithoutYield(timeout); - } - else if (_WaitGui_RecursionGuard(L"Semaphore::TimedWait")) - { - return WaitWithoutYield(timeout); - } - else - { - //ScopedBusyCursor hourglass( Cursor_KindaBusy ); - wxTimeSpan countdown((timeout)); - - do - { - if (WaitWithoutYield(def_yieldgui_interval)) - break; - YieldToMain(); - countdown -= def_yieldgui_interval; - } while (countdown.GetMilliseconds() > 0); - - return countdown.GetMilliseconds() > 0; - } -#else - return WaitWithoutYield(timeout); -#endif -} - -// Performs an uncancellable wait on a semaphore; restoring the thread's previous cancel state -// after the wait has completed. Useful for situations where the semaphore itself is stored on -// the stack and passed to another thread via GUI message or such, avoiding complications where -// the thread might be canceled and the stack value becomes invalid. -// -// Performance note: this function has quite a bit more overhead compared to Semaphore::WaitWithoutYield(), so -// consider manually specifying the thread as uncancellable and using WaitWithoutYield() instead if you need -// to do a lot of no-cancel waits in a tight loop worker thread, for example. -void Threading::Semaphore::WaitNoCancel() -{ - int oldstate; - pthread_setcancelstate(PTHREAD_CANCEL_DISABLE, &oldstate); - //WaitWithoutYield(); - Wait(); - pthread_setcancelstate(oldstate, NULL); -} - -void Threading::Semaphore::WaitNoCancel(const wxTimeSpan& timeout) -{ - int oldstate; - pthread_setcancelstate(PTHREAD_CANCEL_DISABLE, &oldstate); - //WaitWithoutYield( timeout ); - Wait(timeout); - pthread_setcancelstate(oldstate, NULL); -} - -bool Threading::Semaphore::TryWait() -{ - return sem_trywait(&m_sema) == 0; -} - -int Threading::Semaphore::Count() -{ - int retval; - sem_getvalue(&m_sema, &retval); - return retval; -} - -#else - -static void MACH_CHECK(kern_return_t mach_retval) -{ - switch (mach_retval) - { - case KERN_SUCCESS: - break; - case KERN_ABORTED: // Awoken due reason unrelated to semaphore (e.g. pthread_cancel) - pthread_testcancel(); // Unlike sem_wait, mach semaphore ops aren't cancellation points - // fallthrough - default: - fprintf(stderr, "mach error: %s", mach_error_string(mach_retval)); - assert(mach_retval == KERN_SUCCESS); - } -} - -/// Wait up to the given time -/// Returns true if successful, false if timed out -static bool WaitUpTo(semaphore_t sema, wxTimeSpan wxtime) -{ - mach_timespec_t time; - u64 ms = wxtime.GetMilliseconds().GetValue(); - time.tv_sec = ms / 1000; - time.tv_nsec = (ms % 1000) * 1000000; - kern_return_t res = semaphore_timedwait(sema, time); - if (res == KERN_OPERATION_TIMED_OUT) - return false; - MACH_CHECK(res); - return true; -} - -Threading::Semaphore::Semaphore() -{ - // other platforms explicitly make a thread-private (unshared) semaphore - // here. But it seems Mach doesn't support that. - MACH_CHECK(semaphore_create(mach_task_self(), (semaphore_t*)&m_sema, SYNC_POLICY_FIFO, 0)); - __atomic_store_n(&m_counter, 0, __ATOMIC_RELEASE); -} - -Threading::Semaphore::~Semaphore() -{ - MACH_CHECK(semaphore_destroy(mach_task_self(), (semaphore_t)m_sema)); -} - -void Threading::Semaphore::Reset() -{ - MACH_CHECK(semaphore_destroy(mach_task_self(), (semaphore_t)m_sema)); - MACH_CHECK(semaphore_create(mach_task_self(), (semaphore_t*)&m_sema, SYNC_POLICY_FIFO, 0)); - __atomic_store_n(&m_counter, 0, __ATOMIC_SEQ_CST); -} - -void Threading::Semaphore::Post() -{ - if (__atomic_fetch_add(&m_counter, 1, __ATOMIC_RELEASE) < 0) - MACH_CHECK(semaphore_signal(m_sema)); -} - -void Threading::Semaphore::Post(int multiple) -{ - for (int i = 0; i < multiple; ++i) - { - Post(); - } -} - -void Threading::Semaphore::WaitWithoutYield() -{ - pxAssertMsg(!wxThread::IsMain(), "Unyielding semaphore wait issued from the main/gui thread. Please use Wait() instead."); - if (__atomic_sub_fetch(&m_counter, 1, __ATOMIC_ACQUIRE) < 0) - MACH_CHECK(semaphore_wait(m_sema)); -} - -bool Threading::Semaphore::WaitWithoutYield(const wxTimeSpan& timeout) -{ - // This method is the reason why there has to be a special Darwin - // implementation of Semaphore. Note that semaphore_timedwait() is prone - // to returning with KERN_ABORTED, which basically signifies that some - // signal has worken it up. The best official "documentation" for - // semaphore_timedwait() is the way it's used in Grand Central Dispatch, - // which is open-source. - - if (__atomic_sub_fetch(&m_counter, 1, __ATOMIC_ACQUIRE) >= 0) - return true; - - // on x86 platforms, mach_absolute_time() returns nanoseconds - // TODO(aktau): on iOS a scale value from mach_timebase_info will be necessary - u64 const kOneThousand = 1000; - u64 const kOneBillion = kOneThousand * kOneThousand * kOneThousand; - u64 const delta = timeout.GetMilliseconds().GetValue() * (kOneThousand * kOneThousand); - mach_timespec_t ts; - kern_return_t kr = KERN_ABORTED; - for (u64 now = mach_absolute_time(), deadline = now + delta; - kr == KERN_ABORTED; now = mach_absolute_time()) - { - if (now > deadline) - { - // timed out by definition - kr = KERN_OPERATION_TIMED_OUT; - break; - } - - u64 timeleft = deadline - now; - ts.tv_sec = timeleft / kOneBillion; - ts.tv_nsec = timeleft % kOneBillion; - - // possible return values of semaphore_timedwait() (from XNU sources): - // internal kernel val -> return value - // THREAD_INTERRUPTED -> KERN_ABORTED - // THREAD_TIMED_OUT -> KERN_OPERATION_TIMED_OUT - // THREAD_AWAKENED -> KERN_SUCCESS - // THREAD_RESTART -> KERN_TERMINATED - // default -> KERN_FAILURE - kr = semaphore_timedwait(m_sema, ts); - } - - if (kr == KERN_OPERATION_TIMED_OUT) - { - int orig = __atomic_load_n(&m_counter, __ATOMIC_RELAXED); - while (orig < 0) - { - if (__atomic_compare_exchange_n(&m_counter, &orig, orig + 1, true, __ATOMIC_ACQUIRE, __ATOMIC_RELAXED)) - return false; - } - // Semaphore was signalled between our wait expiring and now, keep kernel sema in sync - kr = semaphore_wait(m_sema); - } - - // while it's entirely possible to have KERN_FAILURE here, we should - // probably assert so we can study and correct the actual error here - // (the thread dying while someone is wainting for it). - MACH_CHECK(kr); - return true; -} - -// This is a wxApp-safe implementation of Wait, which makes sure and executes the App's -// pending messages *if* the Wait is performed on the Main/GUI thread. This ensures that -// user input continues to be handled and that windows continue to repaint. If the Wait is -// called from another thread, no message pumping is performed. -void Threading::Semaphore::Wait() -{ -#if wxUSE_GUI - if (!wxThread::IsMain() || (wxTheApp == NULL)) - { - WaitWithoutYield(); - } - else if (_WaitGui_RecursionGuard(L"Semaphore::Wait")) - { - WaitWithoutYield(); - } - else - { - if (__atomic_sub_fetch(&m_counter, 1, __ATOMIC_ACQUIRE) >= 0) - return; - while (!WaitUpTo(m_sema, def_yieldgui_interval)) - { - YieldToMain(); - } - } -#else - WaitWithoutYield(); -#endif -} - -// This is a wxApp-safe implementation of WaitWithoutYield, which makes sure and executes the App's -// pending messages *if* the Wait is performed on the Main/GUI thread. This ensures that -// user input continues to be handled and that windows continue to repaint. If the Wait is -// called from another thread, no message pumping is performed. -// -// Returns: -// false if the wait timed out before the semaphore was signaled, or true if the signal was -// reached prior to timeout. -// -bool Threading::Semaphore::Wait(const wxTimeSpan& timeout) -{ -#if wxUSE_GUI - if (!wxThread::IsMain() || (wxTheApp == NULL)) - { - return WaitWithoutYield(timeout); - } - else if (_WaitGui_RecursionGuard(L"Semaphore::TimedWait")) - { - return WaitWithoutYield(timeout); - } - else - { - wxTimeSpan countdown((timeout)); - - do - { - if (WaitWithoutYield(def_yieldgui_interval)) - break; - YieldToMain(); - countdown -= def_yieldgui_interval; - } while (countdown.GetMilliseconds() > 0); - - return countdown.GetMilliseconds() > 0; - } -#else - return WaitWithoutYield(timeout); -#endif -} - -bool Threading::Semaphore::TryWait() -{ - int counter = __atomic_load_n(&m_counter, __ATOMIC_RELAXED); - while (counter > 0 && !__atomic_compare_exchange_n(&m_counter, &counter, counter - 1, true, __ATOMIC_ACQUIRE, __ATOMIC_RELAXED)) - ; - return counter > 0; -} - -// Performs an uncancellable wait on a semaphore; restoring the thread's previous cancel state -// after the wait has completed. Useful for situations where the semaphore itself is stored on -// the stack and passed to another thread via GUI message or such, avoiding complications where -// the thread might be canceled and the stack value becomes invalid. -// -// Performance note: this function has quite a bit more overhead compared to Semaphore::WaitWithoutYield(), so -// consider manually specifying the thread as uncancellable and using WaitWithoutYield() instead if you need -// to do a lot of no-cancel waits in a tight loop worker thread, for example. -// -// I'm unsure how to do this with pure Mach primitives, the docs in -// osfmk/man seem a bit out of date so perhaps there's a possibility, but -// since as far as I know Mach threads are 1-to-1 on BSD uthreads (and thus -// POSIX threads), this should work. -- aktau -void Threading::Semaphore::WaitNoCancel() -{ - int oldstate; - pthread_setcancelstate(PTHREAD_CANCEL_DISABLE, &oldstate); - Wait(); - pthread_setcancelstate(oldstate, NULL); -} - -void Threading::Semaphore::WaitNoCancel(const wxTimeSpan& timeout) -{ - int oldstate; - pthread_setcancelstate(PTHREAD_CANCEL_DISABLE, &oldstate); - Wait(timeout); - pthread_setcancelstate(oldstate, NULL); -} - -int Threading::Semaphore::Count() -{ - return __atomic_load_n(&m_counter, __ATOMIC_RELAXED); -} - -#endif diff --git a/pcsx2/gui/PersistentThread.h b/pcsx2/gui/PersistentThread.h deleted file mode 100644 index 1ced6e68e2..0000000000 --- a/pcsx2/gui/PersistentThread.h +++ /dev/null @@ -1,736 +0,0 @@ -/* PCSX2 - PS2 Emulator for PCs - * Copyright (C) 2002-2010 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 . - */ - -#pragma once - -#include "common/Threading.h" -#include "common/EventSource.h" -#include "common/Exceptions.h" -#include "common/Console.h" -#include "common/TraceLog.h" -#include "StringHelpers.h" -#include -#include - -#ifdef __APPLE__ -#include -#else -#include -#endif - -// -------------------------------------------------------------------------------------- -// DiagnosticOrigin -// -------------------------------------------------------------------------------------- -struct DiagnosticOrigin -{ - const char* srcfile; - const char* function; - const char* condition; - int line; - - DiagnosticOrigin(const char* _file, int _line, const char* _func, const char* _cond = nullptr) - : srcfile(_file) - , function(_func) - , condition(_cond) - , line(_line) - { - } - - std::string ToString(const char* msg = nullptr) const; -}; - -#define pxDiagSpot DiagnosticOrigin(__FILE__, __LINE__, __pxFUNCTION__) -#define pxAssertSpot(cond) DiagnosticOrigin(__FILE__, __LINE__, __pxFUNCTION__, #cond) - -namespace Threading -{ -class pxThread; -} - -#undef Yield // release the burden of windows.h global namespace spam. - -#define AffinityAssert_AllowFrom_MainUI() \ - pxAssertMsg(wxThread::IsMain(), "Thread affinity violation: Call allowed from main thread only.") - -// -------------------------------------------------------------------------------------- -// pxThreadLog / ConsoleLogSource_Threading -// -------------------------------------------------------------------------------------- - -class ConsoleLogSource_Threading : ConsoleLogSource -{ - typedef ConsoleLogSource _parent; - -public: - using _parent::IsActive; - - ConsoleLogSource_Threading(); - - bool Write(const wxString& thrname, const wxChar* msg) - { - return _parent::Write("%s", (wxsFormat(L"(thread:%s) ", WX_STR(thrname)) + msg).ToUTF8().data()); - } - bool Warn(const wxString& thrname, const wxChar* msg) - { - return _parent::Write("%s", (wxsFormat(L"(thread:%s) ", WX_STR(thrname)) + msg).ToUTF8().data()); - } - bool Error(const wxString& thrname, const wxChar* msg) - { - return _parent::Write("%s", (wxsFormat(L"(thread:%s) ", WX_STR(thrname)) + msg).ToUTF8().data()); - } -}; - -extern ConsoleLogSource_Threading pxConLog_Thread; - -#define pxThreadLog pxConLog_Thread.IsActive() && pxConLog_Thread - -using ScopedExcept = std::unique_ptr; - -namespace Exception -{ - // -------------------------------------------------------------------------------------- - // CancelAppEvent - Exception for canceling an event in a non-verbose fashion - // -------------------------------------------------------------------------------------- - // Typically the PCSX2 interface issues popup dialogs for runtime errors. This exception - // instead issues a "silent" cancelation that is handled by the app gracefully (generates - // log, and resumes messages queue processing). - // - // I chose to have this exception derive from RuntimeError, since if one is thrown from outside - // an App message loop we'll still want it to be handled in a reasonably graceful manner. - class CancelEvent : public RuntimeError - { - DEFINE_RUNTIME_EXCEPTION(CancelEvent, RuntimeError, "No reason given.") - - public: - explicit CancelEvent(std::string logmsg) - { - m_message_diag = std::move(logmsg); - // overridden message formatters only use the diagnostic version... - } - - virtual std::string FormatDisplayMessage() const override - { - return "Action canceled: " + m_message_diag; - } - - virtual std::string FormatDiagnosticMessage() const override - { - return "Action canceled: " + m_message_diag; - } - }; - - class ParseError : public RuntimeError - { - DEFINE_RUNTIME_EXCEPTION(ParseError, RuntimeError, "Parse error"); - }; - -#ifdef _WIN32 - // -------------------------------------------------------------------------------------- - // Exception::WinApiError - // -------------------------------------------------------------------------------------- - class WinApiError : public RuntimeError - { - DEFINE_EXCEPTION_COPYTORS(WinApiError, RuntimeError) - DEFINE_EXCEPTION_MESSAGES(WinApiError) - - public: - int ErrorId; - - public: - WinApiError(); - - std::string GetMsgFromWindows() const; - virtual std::string FormatDisplayMessage() const override; - virtual std::string FormatDiagnosticMessage() const override; - }; -#endif - - // -------------------------------------------------------------------------- - // Exception used to perform an "errorless" termination of the app during OnInit - // procedures. This happens when a user cancels out of startup prompts/wizards. - // - class StartupAborted : public CancelEvent - { - DEFINE_RUNTIME_EXCEPTION(StartupAborted, CancelEvent, "Startup initialization was aborted by the user.") - - public: - StartupAborted(std::string reason) - { - m_message_diag = "Startup aborted: " + reason; - } - }; - - class BaseThreadError : public RuntimeError - { - DEFINE_EXCEPTION_COPYTORS(BaseThreadError, RuntimeError) - DEFINE_EXCEPTION_MESSAGES(BaseThreadError) - - public: - Threading::pxThread* m_thread; - - protected: - BaseThreadError() - { - m_thread = NULL; - } - - public: - explicit BaseThreadError(Threading::pxThread* _thread) - { - m_thread = _thread; - m_message_diag = "An unspecified thread-related error occurred (thread=%s)"; - } - - explicit BaseThreadError(Threading::pxThread& _thread) - { - m_thread = &_thread; - m_message_diag = "An unspecified thread-related error occurred (thread=%s)"; - } - - virtual std::string FormatDiagnosticMessage() const override; - virtual std::string FormatDisplayMessage() const override; - - Threading::pxThread& Thread(); - const Threading::pxThread& Thread() const; - }; - - class ThreadCreationError : public BaseThreadError - { - DEFINE_EXCEPTION_COPYTORS(ThreadCreationError, BaseThreadError) - - public: - explicit ThreadCreationError(Threading::pxThread* _thread) - { - m_thread = _thread; - SetBothMsgs("Thread creation failure. An unspecified error occurred while trying to create the %s thread."); - } - - explicit ThreadCreationError(Threading::pxThread& _thread) - { - m_thread = &_thread; - SetBothMsgs("Thread creation failure. An unspecified error occurred while trying to create the %s thread."); - } - }; -} // namespace Exception - -// -------------------------------------------------------------------------------------- -// DESTRUCTOR_CATCHALL - safe destructor helper -// -------------------------------------------------------------------------------------- -// In C++ destructors *really* need to be "nothrow" garaunteed, otherwise you can have -// disasterous nested exception throws during the unwinding process of an originating -// exception. Use this macro to dispose of these dangerous exceptions, and generate a -// friendly error log in their wake. -// -// Note: Console can also fire an Exception::OutOfMemory -#define __DESTRUCTOR_CATCHALL(funcname) \ - catch (BaseException & ex) \ - { \ - try \ - { \ - Console.Error("Unhandled BaseException in %s (ignored!):", funcname); \ - Console.Error(ex.FormatDiagnosticMessage()); \ - } \ - catch (...) \ - { \ - fprintf(stderr, "ERROR: (out of memory?)\n"); \ - } \ - } \ - catch (std::exception & ex) \ - { \ - try \ - { \ - Console.Error("Unhandled std::exception in %s (ignored!):", funcname); \ - Console.Error(ex.what()); \ - } \ - catch (...) \ - { \ - fprintf(stderr, "ERROR: (out of memory?)\n"); \ - } \ - } \ - catch (...) \ - { \ - /* Unreachable code */ \ - } - -#define DESTRUCTOR_CATCHALL __DESTRUCTOR_CATCHALL(__pxFUNCTION__) - -namespace Threading -{ - extern void pxTestCancel(); - extern void YieldToMain(); - - extern const wxTimeSpan def_yieldgui_interval; - - extern pxThread* pxGetCurrentThread(); - extern wxString pxGetCurrentThreadName(); - extern bool _WaitGui_RecursionGuard(const wxChar* name); - - extern bool AllowDeletions(); - - class Mutex - { - protected: - pthread_mutex_t m_mutex; - - public: - Mutex(); - virtual ~Mutex(); - virtual bool IsRecursive() const { return false; } - - void Recreate(); - bool RecreateIfLocked(); - void Detach(); - - void Acquire(); - bool Acquire(const wxTimeSpan& timeout); - bool TryAcquire(); - void Release(); - - void AcquireWithoutYield(); - bool AcquireWithoutYield(const wxTimeSpan& timeout); - - void Wait(); - void WaitWithSpin(); - bool Wait(const wxTimeSpan& timeout); - void WaitWithoutYield(); - bool WaitWithoutYield(const wxTimeSpan& timeout); - - protected: - // empty constructor used by MutexLockRecursive - Mutex(bool) {} - }; - - class MutexRecursive : public Mutex - { - public: - MutexRecursive(); - virtual ~MutexRecursive(); - virtual bool IsRecursive() const { return true; } - }; - - // -------------------------------------------------------------------------------------- - // ScopedLock - // -------------------------------------------------------------------------------------- - // Helper class for using Mutexes. Using this class provides an exception-safe (and - // generally clean) method of locking code inside a function or conditional block. The lock - // will be automatically released on any return or exit from the function. - // - // Const qualification note: - // ScopedLock takes const instances of the mutex, even though the mutex is modified - // by locking and unlocking. Two rationales: - // - // 1) when designing classes with accessors (GetString, GetValue, etc) that need mutexes, - // this class needs a const hack to allow those accessors to be const (which is typically - // *very* important). - // - // 2) The state of the Mutex is guaranteed to be unchanged when the calling function or - // scope exits, by any means. Only via manual calls to Release or Acquire does that - // change, and typically those are only used in very special circumstances of their own. - // - class ScopedLock - { - DeclareNoncopyableObject(ScopedLock); - - protected: - Mutex* m_lock; - bool m_IsLocked; - - public: - virtual ~ScopedLock(); - explicit ScopedLock(const Mutex* locker = NULL); - explicit ScopedLock(const Mutex& locker); - void AssignAndLock(const Mutex& locker); - void AssignAndLock(const Mutex* locker); - - void Assign(const Mutex& locker); - void Assign(const Mutex* locker); - - void Release(); - void Acquire(); - - bool IsLocked() const { return m_IsLocked; } - - protected: - // Special constructor used by ScopedTryLock - ScopedLock(const Mutex& locker, bool isTryLock); - }; - - // -------------------------------------------------------------------------------------- - // ScopedPtrMT - // -------------------------------------------------------------------------------------- - - template - class ScopedPtrMT - { - DeclareNoncopyableObject(ScopedPtrMT); - - protected: - std::atomic m_ptr; - Threading::Mutex m_mtx; - - public: - typedef T element_type; - - wxEXPLICIT ScopedPtrMT(T* ptr = nullptr) - { - m_ptr = ptr; - } - - ~ScopedPtrMT() { _Delete_unlocked(); } - - ScopedPtrMT& Reassign(T* ptr = nullptr) - { - T* doh = m_ptr.exchange(ptr); - if (ptr != doh) - delete doh; - return *this; - } - - ScopedPtrMT& Delete() noexcept - { - ScopedLock lock(m_mtx); - _Delete_unlocked(); - } - - // Removes the pointer from scoped management, but does not delete! - // (ScopedPtr will be nullptr after this method) - T* DetachPtr() - { - ScopedLock lock(m_mtx); - - return m_ptr.exchange(nullptr); - } - - // Returns the managed pointer. Can return nullptr as a valid result if the ScopedPtrMT - // has no object in management. - T* GetPtr() const - { - return m_ptr; - } - - void SwapPtr(ScopedPtrMT& other) - { - ScopedLock lock(m_mtx); - m_ptr.exchange(other.m_ptr.exchange(m_ptr.load())); - T* const tmp = other.m_ptr; - other.m_ptr = m_ptr; - m_ptr = tmp; - } - - // ---------------------------------------------------------------------------- - // ScopedPtrMT Operators - // ---------------------------------------------------------------------------- - // I've decided to use the ATL's approach to pointer validity tests, opposed to - // the wx/boost approach (which uses some bizarre member method pointer crap, and can't - // allow the T* implicit casting. - - bool operator!() const noexcept - { - return m_ptr.load() == nullptr; - } - - // Equality - bool operator==(T* pT) const noexcept - { - return m_ptr == pT; - } - - // Inequality - bool operator!=(T* pT) const noexcept - { - return !operator==(pT); - } - - // Convenient assignment operator. ScopedPtrMT = nullptr will issue an automatic deletion - // of the managed pointer. - ScopedPtrMT& operator=(T* src) - { - return Reassign(src); - } - - #if 0 - operator T*() const - { - return m_ptr; - } - - // Dereference operator, returns a handle to the managed pointer. - // Generates a debug assertion if the object is nullptr! - T& operator*() const - { - pxAssert(m_ptr != nullptr); - return *m_ptr; - } - - T* operator->() const - { - pxAssert(m_ptr != nullptr); - return m_ptr; - } - #endif - - protected: - void _Delete_unlocked() noexcept - { - delete m_ptr.exchange(nullptr); - } - }; - - - // ---------------------------------------------------------------------------------------- - // RecursionGuard - Basic protection against function recursion - // ---------------------------------------------------------------------------------------- - // Thread safety note: If used in a threaded environment, you shoud use a handle to a __threadlocal - // storage variable (protects aaginst race conditions and, in *most* cases, is more desirable - // behavior as well. - // - // Rationale: wxWidgets has its own wxRecursionGuard, but it has a sloppy implementation with - // entirely unnecessary assertion checks. - // - class RecursionGuard - { - public: - int& Counter; - - RecursionGuard(int& counter) - : Counter(counter) - { - ++Counter; - } - - virtual ~RecursionGuard() - { - --Counter; - } - - bool IsReentrant() const { return Counter > 1; } - }; - - // -------------------------------------------------------------------------------------- - // ThreadDeleteEvent - // -------------------------------------------------------------------------------------- - class EventListener_Thread : public IEventDispatcher - { - public: - typedef int EvtParams; - - protected: - pxThread* m_thread; - - public: - EventListener_Thread() - { - m_thread = NULL; - } - - virtual ~EventListener_Thread() = default; - - void SetThread(pxThread& thr) { m_thread = &thr; } - void SetThread(pxThread* thr) { m_thread = thr; } - - void DispatchEvent(const int& params) - { - OnThreadCleanup(); - } - - protected: - // Invoked by the pxThread when the thread execution is ending. This is - // typically more useful than a delete listener since the extended thread information - // provided by virtualized functions/methods will be available. - // Important! This event is executed *by the thread*, so care must be taken to ensure - // thread sync when necessary (posting messages to the main thread, etc). - virtual void OnThreadCleanup() = 0; - }; - - class Semaphore - { - protected: -#ifdef __APPLE__ - semaphore_t m_sema; - int m_counter; -#else - sem_t m_sema; -#endif - - public: - Semaphore(); - virtual ~Semaphore(); - - void Reset(); - void Post(); - void Post(int multiple); - - void WaitWithoutYield(); - bool WaitWithoutYield(const wxTimeSpan& timeout); - void WaitNoCancel(); - void WaitNoCancel(const wxTimeSpan& timeout); - void WaitWithoutYieldWithSpin() - { - u32 waited = 0; - while (true) - { - if (TryWait()) - return; - if (waited >= SPIN_TIME_NS) - break; - waited += ShortSpin(); - } - WaitWithoutYield(); - } - bool TryWait(); - int Count(); - - void Wait(); - bool Wait(const wxTimeSpan& timeout); - }; - - // -------------------------------------------------------------------------------------- - // pxThread - Helper class for the basics of starting/managing persistent threads. - // -------------------------------------------------------------------------------------- - // This class is meant to be a helper for the typical threading model of "start once and - // reuse many times." This class incorporates a lot of extra overhead in stopping and - // starting threads, but in turn provides most of the basic thread-safety and event-handling - // functionality needed for a threaded operation. In practice this model is usually an - // ideal one for efficiency since Operating Systems themselves typically subscribe to a - // design where sleeping, suspending, and resuming threads is very efficient, but starting - // new threads has quite a bit of overhead. - // - // To use this as a base class for your threaded procedure, overload the following virtual - // methods: - // void OnStart(); - // void ExecuteTaskInThread(); - // void OnCleanupInThread(); - // - // Use the public methods Start() and Cancel() to start and shutdown the thread, and use - // m_sem_event internally to post/receive events for the thread (make a public accessor for - // it in your derived class if your thread utilizes the post). - // - // Notes: - // * Constructing threads as static global vars isn't recommended since it can potentially - // confuse w32pthreads, if the static initializers are executed out-of-order (C++ offers - // no dependency options for ensuring correct static var initializations). Use heap - // allocation to create thread objects instead. - // - class pxThread - { - DeclareNoncopyableObject(pxThread); - - protected: - wxString m_name; // diagnostic name for our thread. - pthread_t m_thread; - - WorkSema m_sem_event; // general wait event that's needed by most threads - Semaphore m_sem_startup; // startup sync tool - Mutex m_mtx_InThread; // used for canceling and closing threads in a deadlock-safe manner - MutexRecursive m_mtx_start; // used to lock the Start() code from starting simultaneous threads accidentally. - Mutex m_mtx_ThreadName; - - std::atomic m_detached; // a boolean value which indicates if the m_thread handle is valid - std::atomic m_running; // set true by Start(), and set false by Cancel(), Block(), etc. - - // exception handle, set non-NULL if the thread terminated with an exception - // Use RethrowException() to re-throw the exception using its original exception type. - ScopedPtrMT m_except; - - EventSource m_evtsrc_OnDelete; - - - public: - virtual ~pxThread(); - pxThread(const wxString& name = L"pxThread"); - - pthread_t GetId() const { return m_thread; } - - virtual void Start(); - virtual void Cancel(bool isBlocking = true); - virtual bool Cancel(const wxTimeSpan& timeout); - virtual bool Detach(); - virtual void Block(); - virtual bool Block(const wxTimeSpan& timeout); - virtual void RethrowException() const; - - void AddListener(EventListener_Thread& evt); - void AddListener(EventListener_Thread* evt) - { - if (evt == NULL) - return; - AddListener(*evt); - } - - void WaitOnSelf(Semaphore& mutex) const; - void WaitOnSelf(Mutex& mutex) const; - bool WaitOnSelf(Semaphore& mutex, const wxTimeSpan& timeout) const; - bool WaitOnSelf(Mutex& mutex, const wxTimeSpan& timeout) const; - - bool IsRunning() const; - bool IsSelf() const; - bool HasPendingException() const { return !!m_except; } - - wxString GetName() const; - void SetName(const wxString& newname); - - protected: - // Extending classes should always implement your own OnStart(), which is called by - // Start() once necessary locks have been obtained. Do not override Start() directly - // unless you're really sure that's what you need to do. ;) - virtual void OnStart(); - - virtual void OnStartInThread(); - - // This is called when the thread has been canceled or exits normally. The pxThread - // automatically binds it to the pthread cleanup routines as soon as the thread starts. - virtual void OnCleanupInThread(); - - // Implemented by derived class to perform actual threaded task! - virtual void ExecuteTaskInThread() = 0; - - void TestCancel() const; - - // Yields this thread to other threads and checks for cancellation. A sleeping thread should - // always test for cancellation, however if you really don't want to, you can use Threading::Sleep() - // or better yet, disable cancellation of the thread completely with DisableCancellation(). - // - // Parameters: - // ms - 'minimum' yield time in milliseconds (rough -- typically yields are longer by 1-5ms - // depending on operating system/platform). If ms is 0 or unspecified, then a single - // timeslice is yielded to other contending threads. If no threads are contending for - // time when ms==0, then no yield is done, but cancellation is still tested. - void Yield(int ms = 0) - { - pxAssert(IsSelf()); - Threading::Sleep(ms); - TestCancel(); - } - - void FrankenMutex(Mutex& mutex); - - bool AffinityAssert_AllowFromSelf(const DiagnosticOrigin& origin) const; - bool AffinityAssert_DisallowFromSelf(const DiagnosticOrigin& origin) const; - - // ---------------------------------------------------------------------------- - // Section of methods for internal use only. - - bool _basecancel(); - void _selfRunningTest(const wxChar* name) const; - void _DoSetThreadName(const wxString& name); - void _DoSetThreadName(const char* name) { SetNameOfCurrentThread(name); } - void _internal_execute(); - void _try_virtual_invoke(void (pxThread::*method)()); - void _ThreadCleanup(); - - static void* _internal_callback(void* func); - static void internal_callback_helper(void* func); - static void _pt_callback_cleanup(void* handle); - }; -} // namespace Threading diff --git a/pcsx2/gui/RecentIsoList.cpp b/pcsx2/gui/RecentIsoList.cpp deleted file mode 100644 index 1c5909a7b9..0000000000 --- a/pcsx2/gui/RecentIsoList.cpp +++ /dev/null @@ -1,306 +0,0 @@ -/* PCSX2 - PS2 Emulator for PCs - * Copyright (C) 2002-2010 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 . - */ - -#include "PrecompiledHeader.h" -#include "App.h" -#include "AppCoreThread.h" -#include "RecentIsoList.h" -#include "IsoDropTarget.h" -#include "IniInterface.h" - -extern wxString GetMsg_IsoImageChanged(); - -// FIXME : This needs to handle removed/missing ISOs somehow, although I'm not sure the -// best approach. I think I'd prefer for missing entries to only be removed when they -// are selected. This also means we'll need to add some sort of "no current selection" -// menu option that's defaulted to when a selection is deemed missing (since just randomly -// selecting another iso would be undesirable). - - -RecentIsoManager::RecentIsoManager( wxMenu* menu, int firstIdForMenuItems_or_wxID_ANY ) - : m_Menu( menu ) - , m_MaxLength( g_Conf->RecentIsoCount ) - , m_firstIdForMenuItems_or_wxID_ANY ( firstIdForMenuItems_or_wxID_ANY ) -{ - m_cursel = 0; - m_Separator = nullptr; - m_ClearSeparator = nullptr; - m_Clear = nullptr; - m_ClearMissing = nullptr; - - IniLoader loader; - LoadListFrom(loader); - - Bind(wxEVT_MENU, &RecentIsoManager::OnChangedSelection, this); -} - -RecentIsoManager::~RecentIsoManager() -{ - Unbind(wxEVT_MENU, &RecentIsoManager::OnChangedSelection, this); -} - -void RecentIsoManager::OnChangedSelection( wxCommandEvent& evt ) -{ - uint cnt = m_Items.size(); - uint i=0; - for( ; iGetId() == evt.GetId()) ) break; - } - - if( i >= m_Items.size() ) - { - evt.Skip(); - return; - } - - // Actually there is no change on the selection so the event can be skip - // Note: It also avoids a deadlock which appears when the core thread is already paused - // and ScopedCoreThreadPopup try to stop the thread (GSOpen1 code path) - if( (g_Conf->CdvdSource == CDVD_SourceType::Iso) && (m_Items[i].Filename == g_Conf->CurrentIso) ) - { - evt.Skip(); - return; - } - - - m_cursel = i; - - ScopedCoreThreadPopup stopped_core; - - SwapOrReset_Iso( m_Menu->GetWindow(), stopped_core, m_Items[i].Filename, GetMsg_IsoImageChanged()); - - stopped_core.AllowResume(); -} - -void RecentIsoManager::RemoveAllFromMenu() -{ - if( m_Menu == NULL ) return; - - int cnt = m_Items.size(); - // Note: Go backwards to work around https://trac.wxwidgets.org/ticket/18772 - // Switch it back to forwards once that's fixed in a relased WX version - for( int i=cnt-1; i>=0; --i ) - { - RecentItem& curitem( m_Items[i] ); - if( curitem.ItemPtr == NULL ) continue; - m_Menu->Destroy( curitem.ItemPtr ); - curitem.ItemPtr = NULL; - } - - if( m_Separator != nullptr ) - { - m_Menu->Destroy( m_Separator ); - m_Separator = nullptr; - } - - if ( m_ClearSeparator != nullptr ) - { - m_Menu->Destroy( m_ClearSeparator ); - m_ClearSeparator = nullptr; - } - - if ( m_Clear != nullptr ) - { - m_Menu->Destroy( m_Clear ); - m_Clear = nullptr; - } - - if (m_ClearMissing != nullptr) - { - m_Menu->Destroy(m_ClearMissing); - m_ClearMissing = nullptr; - } -} - -void RecentIsoManager::Clear() -{ - RemoveAllFromMenu(); - m_Items.clear(); - Repopulate(); -} - -RecentIsoManager::VectorType RecentIsoManager::GetMissingFiles() const -{ - VectorType missing_files; - - std::copy_if( - m_Items.begin(), - m_Items.end(), - std::back_inserter(missing_files), - [](const RecentItem& item) { - return !wxFileExists(item.Filename); - }); - - return missing_files; -} - -void RecentIsoManager::ClearMissing() -{ - RemoveAllFromMenu(); - - VectorType existing_files; - - std::copy_if( - m_Items.begin(), - m_Items.end(), - std::back_inserter(existing_files), - [](const RecentItem& item) { - return wxFileExists(item.Filename); - }); - - m_Items = existing_files; - - Repopulate(); -} - -void RecentIsoManager::Repopulate() -{ - int cnt = m_Items.size(); - if( cnt <= 0 ) return; - - m_Separator = m_Menu->AppendSeparator(); - - //Note: the internal recent iso list (m_Items) has the most recent item last (also at the INI file) - // but the menu is composed in reverse order such that the most recent item appears at the top. - for( int i=cnt-1; i>=0; --i ) - InsertIntoMenu( i ); - - m_ClearSeparator = m_Menu->AppendSeparator(); - m_ClearMissing = m_Menu->Append(MenuIdentifiers::MenuId_IsoClearMissing, _("Clear missing files")); - m_Clear = m_Menu->Append(MenuIdentifiers::MenuId_IsoClear, _("Clear ISO list")); -} - -void RecentIsoManager::Add( const wxString& src ) -{ - if( src.IsEmpty() ) return; - - wxString normalized( Path::Normalize( src ) ); - - int cnt = m_Items.size(); - - if( cnt <= 0 ) - { - pxAssert( m_Separator == NULL ); - m_Separator = m_Menu->AppendSeparator(); - } - else - { - for( int i=0; iCheck(); - return; - } - } - } - - //New item doesn't exist at the menu/internal-list - add it, and refresh the menu. - RemoveAllFromMenu(); - - m_Items.push_back( RecentItem( normalized ) ); - - while( m_Items.size() > m_MaxLength ) - m_Items.erase( m_Items.begin() ); - - Repopulate(); - m_Items[m_cursel = m_Items.size()-1].ItemPtr->Check(); -} - -//id here is the position index at the internal list of recent ISOs (m_Items) -void RecentIsoManager::InsertIntoMenu( int id ) -{ - if( m_Menu == NULL ) return; - RecentItem& curitem( m_Items[id] ); - - int wxid=wxID_ANY; - if (this->m_firstIdForMenuItems_or_wxID_ANY != wxID_ANY) - wxid = this->m_firstIdForMenuItems_or_wxID_ANY + id; - - wxString filename = Path::GetFilename(curitem.Filename); - // & is used to specify the keyboard shortcut key in menu labels. && must - // be used to display an &. - filename.Replace("&", "&&", true); - - curitem.ItemPtr = m_Menu->AppendRadioItem( wxid, filename, curitem.Filename ); - curitem.ItemPtr->Enable(wxFileExists(curitem.Filename) && !g_Conf->AskOnBoot); -} - -void RecentIsoManager::EnableItems(bool display) -{ - for (const auto& list : m_Items) - { - // Files which don't exist still need to be grayed out. - list.ItemPtr->Enable(display && wxFileExists(list.Filename)); - } -} - -void RecentIsoManager::LoadListFrom( IniInterface& ini ) -{ - if (!ini.IsOk()) return; - - ini.GetConfig().SetRecordDefaults( false ); - - RemoveAllFromMenu(); - - m_MaxLength = g_Conf->RecentIsoCount; - ScopedIniGroup groupie( ini, L"RecentIso" ); - for( uint i=0; iCurrentIso ); - - ini.GetConfig().SetRecordDefaults( true ); -} - -void RecentIsoManager::AppStatusEvent_OnSettingsApplied() -{ - // TODO : Implement application of Recent Iso List "maximum" history option -} - -void RecentIsoManager::AppStatusEvent_OnUiSettingsLoadSave( const AppSettingsEventInfo& evt ) -{ - IniInterface& ini( evt.GetIni() ); - - if( ini.IsSaving() ) - { - // Wipe existing recent iso list if we're saving, because our size might have changed - // and that could leave some residual entries in the config. - - ini.GetConfig().SetRecordDefaults( false ); - - ini.GetConfig().DeleteGroup( L"RecentIso" ); - ScopedIniGroup groupie( ini, L"RecentIso" ); - - int cnt = m_Items.size(); - for( int i=0; i. - */ - -#pragma once - -#include "AppCommon.h" - -// -------------------------------------------------------------------------------------- -// RecentIsoManager -// -------------------------------------------------------------------------------------- -class RecentIsoManager : public wxEvtHandler, - public EventListener_AppStatus -{ -protected: - struct RecentItem - { - wxString Filename; - wxMenuItem* ItemPtr; - - RecentItem() { ItemPtr = NULL; } - - RecentItem( const wxString& src ) - : Filename( src ) - { - ItemPtr = NULL; - } - }; - -public: - using VectorType = std::vector; - -protected: - VectorType m_Items; - - wxMenu* m_Menu; - uint m_MaxLength; - int m_cursel; - - int m_firstIdForMenuItems_or_wxID_ANY; - - wxMenuItem* m_Separator; - wxMenuItem* m_ClearSeparator; - wxMenuItem* m_Clear; - wxMenuItem* m_ClearMissing; - -public: - RecentIsoManager( wxMenu* menu , int firstIdForMenuItems_or_wxID_ANY ); - virtual ~RecentIsoManager(); - - VectorType GetMissingFiles() const; - - void RemoveAllFromMenu(); - void EnableItems(bool display); - void Repopulate(); - void Clear(); - void ClearMissing(); - void Add( const wxString& src ); - -protected: - void InsertIntoMenu( int id ); - void OnChangedSelection( wxCommandEvent& evt ); - void LoadListFrom( IniInterface& ini ); - - void AppStatusEvent_OnUiSettingsLoadSave( const AppSettingsEventInfo& ini ); - void AppStatusEvent_OnSettingsApplied(); -}; - - -// -------------------------------------------------------------------------------------- -// RecentIsoList -// -------------------------------------------------------------------------------------- -struct RecentIsoList -{ - std::unique_ptr Manager; - std::unique_ptr Menu; - - RecentIsoList(int firstIdForMenuItems_or_wxID_ANY); - virtual ~RecentIsoList() = default; -}; - diff --git a/pcsx2/gui/Resources/BackgroundLogo.png b/pcsx2/gui/Resources/BackgroundLogo.png deleted file mode 100644 index 5784c125476c1c0ac0085ad414f79c8dc9be7e20..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 74183 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 JPDHLkV1hwP=ivYV diff --git a/pcsx2/gui/Resources/Breakpoint_Active.png b/pcsx2/gui/Resources/Breakpoint_Active.png deleted file mode 100644 index c76aa7ec00327f8b64b68d41a58d74481456e508..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 220 zcmeAS@N?(olHy`uVBq!ia0vp^JRr=$1SD^YpWXnZ*h+%@f*JmUpm~IJEl`BBz$3Dl zfr0M`2s2LA=92~r)_A%&hE&Wco#4oK*nop2J@9|-pTceI>PIpsPt|ub=$VkqFyY8d zy>%|-DiS=E@(d}_m!wKW7jQU8>x(_M-)}R2{i;=~_B*8A`gfXY%%kgdRCt+UA{$@bTEcP#ZMAFar!IxW7{$fb$Z{P;|uI~Y7& L{an^LB{Ts5g(6Ah diff --git a/pcsx2/gui/Resources/Breakpoint_Inactive.png b/pcsx2/gui/Resources/Breakpoint_Inactive.png deleted file mode 100644 index e0984ddeb75bcc9784ede36e1475a2ee928dc552..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 174 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 DXk7EN diff --git a/pcsx2/gui/Resources/ConfigIcon_Gamefixes.png b/pcsx2/gui/Resources/ConfigIcon_Gamefixes.png deleted file mode 100644 index ec699b7f9317244e578daca85bc0ed17cd1dd7a4..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 16191 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