diff --git a/3rdparty/qt/.gitignore b/3rdparty/qt/.gitignore new file mode 100644 index 000000000..2d7fc1300 --- /dev/null +++ b/3rdparty/qt/.gitignore @@ -0,0 +1,3 @@ +/* +!.gitignore +!README.md diff --git a/3rdparty/qt/README.md b/3rdparty/qt/README.md new file mode 100644 index 000000000..2d19550e4 --- /dev/null +++ b/3rdparty/qt/README.md @@ -0,0 +1,11 @@ +# PCSX2 Qt Binaries + +## Windows + +You can download the binaries from https://github.com/PCSX2/pcsx2-windows-dependencies + +Grab the latest release without symbols (or, if you want to debug Qt, with symbols), and extract it to this directory. + +## Linux + +TODO \ No newline at end of file diff --git a/PCSX2_qt.sln b/PCSX2_qt.sln new file mode 100644 index 000000000..e68711dad --- /dev/null +++ b/PCSX2_qt.sln @@ -0,0 +1,656 @@ + +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}") = "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}") = "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}") = "baseclasses", "3rdparty\baseclasses\baseclasses.vcxproj", "{27F17499-A372-4408-8AFA-4F9F4584FBD3}" +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}") = "libsamplerate", "3rdparty\libsamplerate\libsamplerate.vcxproj", "{47AFDBEF-F15F-4BC0-B436-5BE443C3F80F}" +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}") = "pcsx2core", "pcsx2\pcsx2core.vcxproj", "{6C7986C4-3E4D-4DCC-B3C6-6BB12B238995}" +EndProject +Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "glad", "3rdparty\glad\glad.vcxproj", "{C0293B32-5ACF-40F0-AA6C-E6DA6F3BF33A}" +EndProject +Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "imgui", "3rdparty\imgui\imgui.vcxproj", "{88FB34EC-845E-4F21-A552-F1573B9ED167}" +EndProject +Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "pcsx2-qt", "pcsx2-qt\pcsx2-qt.vcxproj", "{2A016F21-87AE-4154-8271-1F57E91408E9}" +EndProject +Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "simpleini", "3rdparty\simpleini\simpleini.vcxproj", "{1EC8B3C0-8FB3-46DE-A2E0-A9121203F266}" +EndProject +Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "cubeb", "3rdparty\cubeb\cubeb.vcxproj", "{BF74C473-DC04-44B3-92E8-4145F4E77342}" +EndProject +Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "ryml", "3rdparty\rapidyaml\ryml.vcxproj", "{DE9653B6-17DD-356A-9EE0-28A731772587}" +EndProject +Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "glslang", "3rdparty\glslang\glslang.vcxproj", "{EF6834A9-11F3-4331-BC34-21B325ABB180}" +EndProject +Global + GlobalSection(SolutionConfigurationPlatforms) = preSolution + Debug AVX2|Win32 = Debug AVX2|Win32 + Debug AVX2|x64 = Debug AVX2|x64 + Debug|Win32 = Debug|Win32 + Debug|x64 = Debug|x64 + Devel AVX2|Win32 = Devel AVX2|Win32 + Devel AVX2|x64 = Devel AVX2|x64 + Devel|Win32 = Devel|Win32 + Devel|x64 = Devel|x64 + Release AVX2|Win32 = Release AVX2|Win32 + Release AVX2|x64 = Release AVX2|x64 + Release|Win32 = Release|Win32 + Release|x64 = Release|x64 + EndGlobalSection + GlobalSection(ProjectConfigurationPlatforms) = postSolution + {E9B51944-7E6D-4BCD-83F2-7BBD5A46182D}.Debug AVX2|Win32.ActiveCfg = Debug|Win32 + {E9B51944-7E6D-4BCD-83F2-7BBD5A46182D}.Debug AVX2|Win32.Build.0 = Debug|Win32 + {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|Win32.ActiveCfg = Debug|Win32 + {E9B51944-7E6D-4BCD-83F2-7BBD5A46182D}.Debug|Win32.Build.0 = Debug|Win32 + {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|Win32.ActiveCfg = Devel|Win32 + {E9B51944-7E6D-4BCD-83F2-7BBD5A46182D}.Devel AVX2|Win32.Build.0 = Devel|Win32 + {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|Win32.ActiveCfg = Devel|Win32 + {E9B51944-7E6D-4BCD-83F2-7BBD5A46182D}.Devel|Win32.Build.0 = Devel|Win32 + {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|Win32.ActiveCfg = Release|Win32 + {E9B51944-7E6D-4BCD-83F2-7BBD5A46182D}.Release AVX2|Win32.Build.0 = Release|Win32 + {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|Win32.ActiveCfg = Release|Win32 + {E9B51944-7E6D-4BCD-83F2-7BBD5A46182D}.Release|Win32.Build.0 = Release|Win32 + {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|Win32.ActiveCfg = Debug|Win32 + {2F6C0388-20CB-4242-9F6C-A6EBB6A83F47}.Debug AVX2|Win32.Build.0 = Debug|Win32 + {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|Win32.ActiveCfg = Debug|Win32 + {2F6C0388-20CB-4242-9F6C-A6EBB6A83F47}.Debug|Win32.Build.0 = Debug|Win32 + {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|Win32.ActiveCfg = Devel|Win32 + {2F6C0388-20CB-4242-9F6C-A6EBB6A83F47}.Devel AVX2|Win32.Build.0 = Devel|Win32 + {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|Win32.ActiveCfg = Devel|Win32 + {2F6C0388-20CB-4242-9F6C-A6EBB6A83F47}.Devel|Win32.Build.0 = Devel|Win32 + {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|Win32.ActiveCfg = Release|Win32 + {2F6C0388-20CB-4242-9F6C-A6EBB6A83F47}.Release AVX2|Win32.Build.0 = Release|Win32 + {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|Win32.ActiveCfg = Release|Win32 + {2F6C0388-20CB-4242-9F6C-A6EBB6A83F47}.Release|Win32.Build.0 = Release|Win32 + {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|Win32.ActiveCfg = Debug|Win32 + {677B7D11-D5E1-40B3-88B1-9A4DF83D2213}.Debug AVX2|Win32.Build.0 = Debug|Win32 + {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|Win32.ActiveCfg = Debug|Win32 + {677B7D11-D5E1-40B3-88B1-9A4DF83D2213}.Debug|Win32.Build.0 = Debug|Win32 + {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|Win32.ActiveCfg = Devel|Win32 + {677B7D11-D5E1-40B3-88B1-9A4DF83D2213}.Devel AVX2|Win32.Build.0 = Devel|Win32 + {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|Win32.ActiveCfg = Devel|Win32 + {677B7D11-D5E1-40B3-88B1-9A4DF83D2213}.Devel|Win32.Build.0 = Devel|Win32 + {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|Win32.ActiveCfg = Release|Win32 + {677B7D11-D5E1-40B3-88B1-9A4DF83D2213}.Release AVX2|Win32.Build.0 = Release|Win32 + {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|Win32.ActiveCfg = Release|Win32 + {677B7D11-D5E1-40B3-88B1-9A4DF83D2213}.Release|Win32.Build.0 = Release|Win32 + {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|Win32.ActiveCfg = Debug|Win32 + {BC236261-77E8-4567-8D09-45CD02965EB6}.Debug AVX2|Win32.Build.0 = Debug|Win32 + {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|Win32.ActiveCfg = Debug|Win32 + {BC236261-77E8-4567-8D09-45CD02965EB6}.Debug|Win32.Build.0 = Debug|Win32 + {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|Win32.ActiveCfg = Devel|Win32 + {BC236261-77E8-4567-8D09-45CD02965EB6}.Devel AVX2|Win32.Build.0 = Devel|Win32 + {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|Win32.ActiveCfg = Devel|Win32 + {BC236261-77E8-4567-8D09-45CD02965EB6}.Devel|Win32.Build.0 = Devel|Win32 + {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|Win32.ActiveCfg = Release|Win32 + {BC236261-77E8-4567-8D09-45CD02965EB6}.Release AVX2|Win32.Build.0 = Release|Win32 + {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|Win32.ActiveCfg = Release|Win32 + {BC236261-77E8-4567-8D09-45CD02965EB6}.Release|Win32.Build.0 = Release|Win32 + {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|Win32.ActiveCfg = Debug|Win32 + {01F4CE10-2CFB-41A8-B41F-E54337868A1D}.Debug AVX2|Win32.Build.0 = Debug|Win32 + {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|Win32.ActiveCfg = Debug|Win32 + {01F4CE10-2CFB-41A8-B41F-E54337868A1D}.Debug|Win32.Build.0 = Debug|Win32 + {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|Win32.ActiveCfg = Devel|Win32 + {01F4CE10-2CFB-41A8-B41F-E54337868A1D}.Devel AVX2|Win32.Build.0 = Devel|Win32 + {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|Win32.ActiveCfg = Devel|Win32 + {01F4CE10-2CFB-41A8-B41F-E54337868A1D}.Devel|Win32.Build.0 = Devel|Win32 + {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|Win32.ActiveCfg = Release|Win32 + {01F4CE10-2CFB-41A8-B41F-E54337868A1D}.Release AVX2|Win32.Build.0 = Release|Win32 + {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|Win32.ActiveCfg = Release|Win32 + {01F4CE10-2CFB-41A8-B41F-E54337868A1D}.Release|Win32.Build.0 = Release|Win32 + {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|Win32.ActiveCfg = Debug|Win32 + {3FCC50C2-81E9-5DB2-B8D8-2129427568B1}.Debug AVX2|Win32.Build.0 = Debug|Win32 + {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|Win32.ActiveCfg = Debug|Win32 + {3FCC50C2-81E9-5DB2-B8D8-2129427568B1}.Debug|Win32.Build.0 = Debug|Win32 + {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|Win32.ActiveCfg = Devel|Win32 + {3FCC50C2-81E9-5DB2-B8D8-2129427568B1}.Devel AVX2|Win32.Build.0 = Devel|Win32 + {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|Win32.ActiveCfg = Devel|Win32 + {3FCC50C2-81E9-5DB2-B8D8-2129427568B1}.Devel|Win32.Build.0 = Devel|Win32 + {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|Win32.ActiveCfg = Release|Win32 + {3FCC50C2-81E9-5DB2-B8D8-2129427568B1}.Release AVX2|Win32.Build.0 = Release|Win32 + {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|Win32.ActiveCfg = Release|Win32 + {3FCC50C2-81E9-5DB2-B8D8-2129427568B1}.Release|Win32.Build.0 = Release|Win32 + {3FCC50C2-81E9-5DB2-B8D8-2129427568B1}.Release|x64.ActiveCfg = Release|x64 + {3FCC50C2-81E9-5DB2-B8D8-2129427568B1}.Release|x64.Build.0 = Release|x64 + {D6973076-9317-4EF2-A0B8-B7A18AC0713E}.Debug AVX2|Win32.ActiveCfg = Debug|Win32 + {D6973076-9317-4EF2-A0B8-B7A18AC0713E}.Debug AVX2|Win32.Build.0 = Debug|Win32 + {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|Win32.ActiveCfg = Debug|Win32 + {D6973076-9317-4EF2-A0B8-B7A18AC0713E}.Debug|Win32.Build.0 = Debug|Win32 + {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|Win32.ActiveCfg = Devel|Win32 + {D6973076-9317-4EF2-A0B8-B7A18AC0713E}.Devel AVX2|Win32.Build.0 = Devel|Win32 + {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|Win32.ActiveCfg = Devel|Win32 + {D6973076-9317-4EF2-A0B8-B7A18AC0713E}.Devel|Win32.Build.0 = Devel|Win32 + {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|Win32.ActiveCfg = Release|Win32 + {D6973076-9317-4EF2-A0B8-B7A18AC0713E}.Release AVX2|Win32.Build.0 = Release|Win32 + {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|Win32.ActiveCfg = Release|Win32 + {D6973076-9317-4EF2-A0B8-B7A18AC0713E}.Release|Win32.Build.0 = Release|Win32 + {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|Win32.ActiveCfg = Debug|Win32 + {0FAE817D-9A32-4830-857E-81DA57246E16}.Debug AVX2|Win32.Build.0 = Debug|Win32 + {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|Win32.ActiveCfg = Debug|Win32 + {0FAE817D-9A32-4830-857E-81DA57246E16}.Debug|Win32.Build.0 = Debug|Win32 + {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|Win32.ActiveCfg = Devel|Win32 + {0FAE817D-9A32-4830-857E-81DA57246E16}.Devel AVX2|Win32.Build.0 = Devel|Win32 + {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|Win32.ActiveCfg = Devel|Win32 + {0FAE817D-9A32-4830-857E-81DA57246E16}.Devel|Win32.Build.0 = Devel|Win32 + {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|Win32.ActiveCfg = Release|Win32 + {0FAE817D-9A32-4830-857E-81DA57246E16}.Release AVX2|Win32.Build.0 = Release|Win32 + {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|Win32.ActiveCfg = Release|Win32 + {0FAE817D-9A32-4830-857E-81DA57246E16}.Release|Win32.Build.0 = Release|Win32 + {0FAE817D-9A32-4830-857E-81DA57246E16}.Release|x64.ActiveCfg = Release|x64 + {0FAE817D-9A32-4830-857E-81DA57246E16}.Release|x64.Build.0 = Release|x64 + {27F17499-A372-4408-8AFA-4F9F4584FBD3}.Debug AVX2|Win32.ActiveCfg = Debug|Win32 + {27F17499-A372-4408-8AFA-4F9F4584FBD3}.Debug AVX2|Win32.Build.0 = Debug|Win32 + {27F17499-A372-4408-8AFA-4F9F4584FBD3}.Debug AVX2|x64.ActiveCfg = Debug|x64 + {27F17499-A372-4408-8AFA-4F9F4584FBD3}.Debug AVX2|x64.Build.0 = Debug|x64 + {27F17499-A372-4408-8AFA-4F9F4584FBD3}.Debug|Win32.ActiveCfg = Debug|Win32 + {27F17499-A372-4408-8AFA-4F9F4584FBD3}.Debug|Win32.Build.0 = Debug|Win32 + {27F17499-A372-4408-8AFA-4F9F4584FBD3}.Debug|x64.ActiveCfg = Debug|x64 + {27F17499-A372-4408-8AFA-4F9F4584FBD3}.Debug|x64.Build.0 = Debug|x64 + {27F17499-A372-4408-8AFA-4F9F4584FBD3}.Devel AVX2|Win32.ActiveCfg = Devel|Win32 + {27F17499-A372-4408-8AFA-4F9F4584FBD3}.Devel AVX2|Win32.Build.0 = Devel|Win32 + {27F17499-A372-4408-8AFA-4F9F4584FBD3}.Devel AVX2|x64.ActiveCfg = Devel|x64 + {27F17499-A372-4408-8AFA-4F9F4584FBD3}.Devel AVX2|x64.Build.0 = Devel|x64 + {27F17499-A372-4408-8AFA-4F9F4584FBD3}.Devel|Win32.ActiveCfg = Devel|Win32 + {27F17499-A372-4408-8AFA-4F9F4584FBD3}.Devel|Win32.Build.0 = Devel|Win32 + {27F17499-A372-4408-8AFA-4F9F4584FBD3}.Devel|x64.ActiveCfg = Devel|x64 + {27F17499-A372-4408-8AFA-4F9F4584FBD3}.Devel|x64.Build.0 = Devel|x64 + {27F17499-A372-4408-8AFA-4F9F4584FBD3}.Release AVX2|Win32.ActiveCfg = Release|Win32 + {27F17499-A372-4408-8AFA-4F9F4584FBD3}.Release AVX2|Win32.Build.0 = Release|Win32 + {27F17499-A372-4408-8AFA-4F9F4584FBD3}.Release AVX2|x64.ActiveCfg = Release|x64 + {27F17499-A372-4408-8AFA-4F9F4584FBD3}.Release AVX2|x64.Build.0 = Release|x64 + {27F17499-A372-4408-8AFA-4F9F4584FBD3}.Release|Win32.ActiveCfg = Release|Win32 + {27F17499-A372-4408-8AFA-4F9F4584FBD3}.Release|Win32.Build.0 = Release|Win32 + {27F17499-A372-4408-8AFA-4F9F4584FBD3}.Release|x64.ActiveCfg = Release|x64 + {27F17499-A372-4408-8AFA-4F9F4584FBD3}.Release|x64.Build.0 = Release|x64 + {12728250-16EC-4DC6-94D7-E21DD88947F8}.Debug AVX2|Win32.ActiveCfg = Debug|Win32 + {12728250-16EC-4DC6-94D7-E21DD88947F8}.Debug AVX2|Win32.Build.0 = Debug|Win32 + {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|Win32.ActiveCfg = Debug|Win32 + {12728250-16EC-4DC6-94D7-E21DD88947F8}.Debug|Win32.Build.0 = Debug|Win32 + {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|Win32.ActiveCfg = Devel|Win32 + {12728250-16EC-4DC6-94D7-E21DD88947F8}.Devel AVX2|Win32.Build.0 = Devel|Win32 + {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|Win32.ActiveCfg = Devel|Win32 + {12728250-16EC-4DC6-94D7-E21DD88947F8}.Devel|Win32.Build.0 = Devel|Win32 + {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|Win32.ActiveCfg = Release|Win32 + {12728250-16EC-4DC6-94D7-E21DD88947F8}.Release AVX2|Win32.Build.0 = Release|Win32 + {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|Win32.ActiveCfg = Release|Win32 + {12728250-16EC-4DC6-94D7-E21DD88947F8}.Release|Win32.Build.0 = Release|Win32 + {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|Win32.ActiveCfg = Debug|Win32 + {449AD25E-424A-4714-BABC-68706CDCC33B}.Debug AVX2|Win32.Build.0 = Debug|Win32 + {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|Win32.ActiveCfg = Debug|Win32 + {449AD25E-424A-4714-BABC-68706CDCC33B}.Debug|Win32.Build.0 = Debug|Win32 + {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|Win32.ActiveCfg = Devel|Win32 + {449AD25E-424A-4714-BABC-68706CDCC33B}.Devel AVX2|Win32.Build.0 = Devel|Win32 + {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|Win32.ActiveCfg = Devel|Win32 + {449AD25E-424A-4714-BABC-68706CDCC33B}.Devel|Win32.Build.0 = Devel|Win32 + {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|Win32.ActiveCfg = Release|Win32 + {449AD25E-424A-4714-BABC-68706CDCC33B}.Release AVX2|Win32.Build.0 = Release|Win32 + {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|Win32.ActiveCfg = Release|Win32 + {449AD25E-424A-4714-BABC-68706CDCC33B}.Release|Win32.Build.0 = Release|Win32 + {449AD25E-424A-4714-BABC-68706CDCC33B}.Release|x64.ActiveCfg = Release|x64 + {449AD25E-424A-4714-BABC-68706CDCC33B}.Release|x64.Build.0 = Release|x64 + {47AFDBEF-F15F-4BC0-B436-5BE443C3F80F}.Debug AVX2|Win32.ActiveCfg = Debug|Win32 + {47AFDBEF-F15F-4BC0-B436-5BE443C3F80F}.Debug AVX2|Win32.Build.0 = Debug|Win32 + {47AFDBEF-F15F-4BC0-B436-5BE443C3F80F}.Debug AVX2|x64.ActiveCfg = Debug|x64 + {47AFDBEF-F15F-4BC0-B436-5BE443C3F80F}.Debug AVX2|x64.Build.0 = Debug|x64 + {47AFDBEF-F15F-4BC0-B436-5BE443C3F80F}.Debug|Win32.ActiveCfg = Debug|Win32 + {47AFDBEF-F15F-4BC0-B436-5BE443C3F80F}.Debug|Win32.Build.0 = Debug|Win32 + {47AFDBEF-F15F-4BC0-B436-5BE443C3F80F}.Debug|x64.ActiveCfg = Debug|x64 + {47AFDBEF-F15F-4BC0-B436-5BE443C3F80F}.Debug|x64.Build.0 = Debug|x64 + {47AFDBEF-F15F-4BC0-B436-5BE443C3F80F}.Devel AVX2|Win32.ActiveCfg = Devel|Win32 + {47AFDBEF-F15F-4BC0-B436-5BE443C3F80F}.Devel AVX2|Win32.Build.0 = Devel|Win32 + {47AFDBEF-F15F-4BC0-B436-5BE443C3F80F}.Devel AVX2|x64.ActiveCfg = Devel|x64 + {47AFDBEF-F15F-4BC0-B436-5BE443C3F80F}.Devel AVX2|x64.Build.0 = Devel|x64 + {47AFDBEF-F15F-4BC0-B436-5BE443C3F80F}.Devel|Win32.ActiveCfg = Devel|Win32 + {47AFDBEF-F15F-4BC0-B436-5BE443C3F80F}.Devel|Win32.Build.0 = Devel|Win32 + {47AFDBEF-F15F-4BC0-B436-5BE443C3F80F}.Devel|x64.ActiveCfg = Devel|x64 + {47AFDBEF-F15F-4BC0-B436-5BE443C3F80F}.Devel|x64.Build.0 = Devel|x64 + {47AFDBEF-F15F-4BC0-B436-5BE443C3F80F}.Release AVX2|Win32.ActiveCfg = Release|Win32 + {47AFDBEF-F15F-4BC0-B436-5BE443C3F80F}.Release AVX2|Win32.Build.0 = Release|Win32 + {47AFDBEF-F15F-4BC0-B436-5BE443C3F80F}.Release AVX2|x64.ActiveCfg = Release|x64 + {47AFDBEF-F15F-4BC0-B436-5BE443C3F80F}.Release AVX2|x64.Build.0 = Release|x64 + {47AFDBEF-F15F-4BC0-B436-5BE443C3F80F}.Release|Win32.ActiveCfg = Release|Win32 + {47AFDBEF-F15F-4BC0-B436-5BE443C3F80F}.Release|Win32.Build.0 = Release|Win32 + {47AFDBEF-F15F-4BC0-B436-5BE443C3F80F}.Release|x64.ActiveCfg = Release|x64 + {47AFDBEF-F15F-4BC0-B436-5BE443C3F80F}.Release|x64.Build.0 = Release|x64 + {A0D2B3AD-1F72-4EE3-8B5C-F2C358DA35F0}.Debug AVX2|Win32.ActiveCfg = Debug|Win32 + {A0D2B3AD-1F72-4EE3-8B5C-F2C358DA35F0}.Debug AVX2|Win32.Build.0 = Debug|Win32 + {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|Win32.ActiveCfg = Debug|Win32 + {A0D2B3AD-1F72-4EE3-8B5C-F2C358DA35F0}.Debug|Win32.Build.0 = Debug|Win32 + {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|Win32.ActiveCfg = Devel|Win32 + {A0D2B3AD-1F72-4EE3-8B5C-F2C358DA35F0}.Devel AVX2|Win32.Build.0 = Devel|Win32 + {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|Win32.ActiveCfg = Devel|Win32 + {A0D2B3AD-1F72-4EE3-8B5C-F2C358DA35F0}.Devel|Win32.Build.0 = Devel|Win32 + {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|Win32.ActiveCfg = Release|Win32 + {A0D2B3AD-1F72-4EE3-8B5C-F2C358DA35F0}.Release AVX2|Win32.Build.0 = Release|Win32 + {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|Win32.ActiveCfg = Release|Win32 + {A0D2B3AD-1F72-4EE3-8B5C-F2C358DA35F0}.Release|Win32.Build.0 = Release|Win32 + {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|Win32.ActiveCfg = Debug|Win32 + {ED2F21FD-0A36-4A8F-9B90-E7D92A2ACB63}.Debug AVX2|Win32.Build.0 = Debug|Win32 + {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|Win32.ActiveCfg = Debug|Win32 + {ED2F21FD-0A36-4A8F-9B90-E7D92A2ACB63}.Debug|Win32.Build.0 = Debug|Win32 + {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|Win32.ActiveCfg = Devel|Win32 + {ED2F21FD-0A36-4A8F-9B90-E7D92A2ACB63}.Devel AVX2|Win32.Build.0 = Devel|Win32 + {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|Win32.ActiveCfg = Devel|Win32 + {ED2F21FD-0A36-4A8F-9B90-E7D92A2ACB63}.Devel|Win32.Build.0 = Devel|Win32 + {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|Win32.ActiveCfg = Release|Win32 + {ED2F21FD-0A36-4A8F-9B90-E7D92A2ACB63}.Release AVX2|Win32.Build.0 = Release|Win32 + {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|Win32.ActiveCfg = Release|Win32 + {ED2F21FD-0A36-4A8F-9B90-E7D92A2ACB63}.Release|Win32.Build.0 = Release|Win32 + {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|Win32.ActiveCfg = Debug|Win32 + {4639972E-424E-4E13-8B07-CA403C481346}.Debug AVX2|Win32.Build.0 = Debug|Win32 + {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|Win32.ActiveCfg = Debug|Win32 + {4639972E-424E-4E13-8B07-CA403C481346}.Debug|Win32.Build.0 = Debug|Win32 + {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|Win32.ActiveCfg = Devel|Win32 + {4639972E-424E-4E13-8B07-CA403C481346}.Devel AVX2|Win32.Build.0 = Devel|Win32 + {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|Win32.ActiveCfg = Devel|Win32 + {4639972E-424E-4E13-8B07-CA403C481346}.Devel|Win32.Build.0 = Devel|Win32 + {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|Win32.ActiveCfg = Release|Win32 + {4639972E-424E-4E13-8B07-CA403C481346}.Release AVX2|Win32.Build.0 = Release|Win32 + {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|Win32.ActiveCfg = Release|Win32 + {4639972E-424E-4E13-8B07-CA403C481346}.Release|Win32.Build.0 = Release|Win32 + {4639972E-424E-4E13-8B07-CA403C481346}.Release|x64.ActiveCfg = Release|x64 + {4639972E-424E-4E13-8B07-CA403C481346}.Release|x64.Build.0 = Release|x64 + {6C7986C4-3E4D-4DCC-B3C6-6BB12B238995}.Debug AVX2|Win32.ActiveCfg = Debug AVX2|Win32 + {6C7986C4-3E4D-4DCC-B3C6-6BB12B238995}.Debug AVX2|Win32.Build.0 = Debug AVX2|Win32 + {6C7986C4-3E4D-4DCC-B3C6-6BB12B238995}.Debug AVX2|x64.ActiveCfg = Debug AVX2|x64 + {6C7986C4-3E4D-4DCC-B3C6-6BB12B238995}.Debug AVX2|x64.Build.0 = Debug AVX2|x64 + {6C7986C4-3E4D-4DCC-B3C6-6BB12B238995}.Debug|Win32.ActiveCfg = Debug|Win32 + {6C7986C4-3E4D-4DCC-B3C6-6BB12B238995}.Debug|Win32.Build.0 = Debug|Win32 + {6C7986C4-3E4D-4DCC-B3C6-6BB12B238995}.Debug|x64.ActiveCfg = Debug|x64 + {6C7986C4-3E4D-4DCC-B3C6-6BB12B238995}.Debug|x64.Build.0 = Debug|x64 + {6C7986C4-3E4D-4DCC-B3C6-6BB12B238995}.Devel AVX2|Win32.ActiveCfg = Devel AVX2|Win32 + {6C7986C4-3E4D-4DCC-B3C6-6BB12B238995}.Devel AVX2|Win32.Build.0 = Devel AVX2|Win32 + {6C7986C4-3E4D-4DCC-B3C6-6BB12B238995}.Devel AVX2|x64.ActiveCfg = Devel AVX2|x64 + {6C7986C4-3E4D-4DCC-B3C6-6BB12B238995}.Devel AVX2|x64.Build.0 = Devel AVX2|x64 + {6C7986C4-3E4D-4DCC-B3C6-6BB12B238995}.Devel|Win32.ActiveCfg = Devel|Win32 + {6C7986C4-3E4D-4DCC-B3C6-6BB12B238995}.Devel|Win32.Build.0 = Devel|Win32 + {6C7986C4-3E4D-4DCC-B3C6-6BB12B238995}.Devel|x64.ActiveCfg = Devel|x64 + {6C7986C4-3E4D-4DCC-B3C6-6BB12B238995}.Devel|x64.Build.0 = Devel|x64 + {6C7986C4-3E4D-4DCC-B3C6-6BB12B238995}.Release AVX2|Win32.ActiveCfg = Release AVX2|Win32 + {6C7986C4-3E4D-4DCC-B3C6-6BB12B238995}.Release AVX2|Win32.Build.0 = Release AVX2|Win32 + {6C7986C4-3E4D-4DCC-B3C6-6BB12B238995}.Release AVX2|x64.ActiveCfg = Release AVX2|x64 + {6C7986C4-3E4D-4DCC-B3C6-6BB12B238995}.Release AVX2|x64.Build.0 = Release AVX2|x64 + {6C7986C4-3E4D-4DCC-B3C6-6BB12B238995}.Release|Win32.ActiveCfg = Release|Win32 + {6C7986C4-3E4D-4DCC-B3C6-6BB12B238995}.Release|Win32.Build.0 = Release|Win32 + {6C7986C4-3E4D-4DCC-B3C6-6BB12B238995}.Release|x64.ActiveCfg = Release|x64 + {6C7986C4-3E4D-4DCC-B3C6-6BB12B238995}.Release|x64.Build.0 = Release|x64 + {C0293B32-5ACF-40F0-AA6C-E6DA6F3BF33A}.Debug AVX2|Win32.ActiveCfg = Debug|Win32 + {C0293B32-5ACF-40F0-AA6C-E6DA6F3BF33A}.Debug AVX2|Win32.Build.0 = Debug|Win32 + {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|Win32.ActiveCfg = Debug|Win32 + {C0293B32-5ACF-40F0-AA6C-E6DA6F3BF33A}.Debug|Win32.Build.0 = Debug|Win32 + {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|Win32.ActiveCfg = Devel|Win32 + {C0293B32-5ACF-40F0-AA6C-E6DA6F3BF33A}.Devel AVX2|Win32.Build.0 = Devel|Win32 + {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|Win32.ActiveCfg = Devel|Win32 + {C0293B32-5ACF-40F0-AA6C-E6DA6F3BF33A}.Devel|Win32.Build.0 = Devel|Win32 + {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|Win32.ActiveCfg = Release|Win32 + {C0293B32-5ACF-40F0-AA6C-E6DA6F3BF33A}.Release AVX2|Win32.Build.0 = Release|Win32 + {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|Win32.ActiveCfg = Release|Win32 + {C0293B32-5ACF-40F0-AA6C-E6DA6F3BF33A}.Release|Win32.Build.0 = Release|Win32 + {C0293B32-5ACF-40F0-AA6C-E6DA6F3BF33A}.Release|x64.ActiveCfg = Release|x64 + {C0293B32-5ACF-40F0-AA6C-E6DA6F3BF33A}.Release|x64.Build.0 = Release|x64 + {88FB34EC-845E-4F21-A552-F1573B9ED167}.Debug AVX2|Win32.ActiveCfg = Debug|Win32 + {88FB34EC-845E-4F21-A552-F1573B9ED167}.Debug AVX2|Win32.Build.0 = Debug|Win32 + {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|Win32.ActiveCfg = Debug|Win32 + {88FB34EC-845E-4F21-A552-F1573B9ED167}.Debug|Win32.Build.0 = Debug|Win32 + {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|Win32.ActiveCfg = Devel|Win32 + {88FB34EC-845E-4F21-A552-F1573B9ED167}.Devel AVX2|Win32.Build.0 = Devel|Win32 + {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|Win32.ActiveCfg = Devel|Win32 + {88FB34EC-845E-4F21-A552-F1573B9ED167}.Devel|Win32.Build.0 = Devel|Win32 + {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|Win32.ActiveCfg = Release|Win32 + {88FB34EC-845E-4F21-A552-F1573B9ED167}.Release AVX2|Win32.Build.0 = Release|Win32 + {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|Win32.ActiveCfg = Release|Win32 + {88FB34EC-845E-4F21-A552-F1573B9ED167}.Release|Win32.Build.0 = Release|Win32 + {88FB34EC-845E-4F21-A552-F1573B9ED167}.Release|x64.ActiveCfg = Release|x64 + {88FB34EC-845E-4F21-A552-F1573B9ED167}.Release|x64.Build.0 = Release|x64 + {2A016F21-87AE-4154-8271-1F57E91408E9}.Debug AVX2|Win32.ActiveCfg = Debug AVX2|Win32 + {2A016F21-87AE-4154-8271-1F57E91408E9}.Debug AVX2|Win32.Build.0 = Debug AVX2|Win32 + {2A016F21-87AE-4154-8271-1F57E91408E9}.Debug AVX2|x64.ActiveCfg = Debug AVX2|x64 + {2A016F21-87AE-4154-8271-1F57E91408E9}.Debug AVX2|x64.Build.0 = Debug AVX2|x64 + {2A016F21-87AE-4154-8271-1F57E91408E9}.Debug|Win32.ActiveCfg = Debug|Win32 + {2A016F21-87AE-4154-8271-1F57E91408E9}.Debug|Win32.Build.0 = Debug|Win32 + {2A016F21-87AE-4154-8271-1F57E91408E9}.Debug|x64.ActiveCfg = Debug|x64 + {2A016F21-87AE-4154-8271-1F57E91408E9}.Debug|x64.Build.0 = Debug|x64 + {2A016F21-87AE-4154-8271-1F57E91408E9}.Devel AVX2|Win32.ActiveCfg = Devel AVX2|Win32 + {2A016F21-87AE-4154-8271-1F57E91408E9}.Devel AVX2|Win32.Build.0 = Devel AVX2|Win32 + {2A016F21-87AE-4154-8271-1F57E91408E9}.Devel AVX2|x64.ActiveCfg = Devel AVX2|x64 + {2A016F21-87AE-4154-8271-1F57E91408E9}.Devel AVX2|x64.Build.0 = Devel AVX2|x64 + {2A016F21-87AE-4154-8271-1F57E91408E9}.Devel|Win32.ActiveCfg = Devel|Win32 + {2A016F21-87AE-4154-8271-1F57E91408E9}.Devel|Win32.Build.0 = Devel|Win32 + {2A016F21-87AE-4154-8271-1F57E91408E9}.Devel|x64.ActiveCfg = Devel|x64 + {2A016F21-87AE-4154-8271-1F57E91408E9}.Devel|x64.Build.0 = Devel|x64 + {2A016F21-87AE-4154-8271-1F57E91408E9}.Release AVX2|Win32.ActiveCfg = Release AVX2|Win32 + {2A016F21-87AE-4154-8271-1F57E91408E9}.Release AVX2|Win32.Build.0 = Release AVX2|Win32 + {2A016F21-87AE-4154-8271-1F57E91408E9}.Release AVX2|x64.ActiveCfg = Release AVX2|x64 + {2A016F21-87AE-4154-8271-1F57E91408E9}.Release AVX2|x64.Build.0 = Release AVX2|x64 + {2A016F21-87AE-4154-8271-1F57E91408E9}.Release|Win32.ActiveCfg = Release|Win32 + {2A016F21-87AE-4154-8271-1F57E91408E9}.Release|Win32.Build.0 = Release|Win32 + {2A016F21-87AE-4154-8271-1F57E91408E9}.Release|x64.ActiveCfg = Release|x64 + {2A016F21-87AE-4154-8271-1F57E91408E9}.Release|x64.Build.0 = Release|x64 + {1EC8B3C0-8FB3-46DE-A2E0-A9121203F266}.Debug AVX2|Win32.ActiveCfg = Debug|Win32 + {1EC8B3C0-8FB3-46DE-A2E0-A9121203F266}.Debug AVX2|Win32.Build.0 = Debug|Win32 + {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|Win32.ActiveCfg = Debug|Win32 + {1EC8B3C0-8FB3-46DE-A2E0-A9121203F266}.Debug|Win32.Build.0 = Debug|Win32 + {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|Win32.ActiveCfg = Devel|Win32 + {1EC8B3C0-8FB3-46DE-A2E0-A9121203F266}.Devel AVX2|Win32.Build.0 = Devel|Win32 + {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|Win32.ActiveCfg = Devel|Win32 + {1EC8B3C0-8FB3-46DE-A2E0-A9121203F266}.Devel|Win32.Build.0 = Devel|Win32 + {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|Win32.ActiveCfg = Release|Win32 + {1EC8B3C0-8FB3-46DE-A2E0-A9121203F266}.Release AVX2|Win32.Build.0 = Release|Win32 + {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|Win32.ActiveCfg = Release|Win32 + {1EC8B3C0-8FB3-46DE-A2E0-A9121203F266}.Release|Win32.Build.0 = Release|Win32 + {1EC8B3C0-8FB3-46DE-A2E0-A9121203F266}.Release|x64.ActiveCfg = Release|x64 + {1EC8B3C0-8FB3-46DE-A2E0-A9121203F266}.Release|x64.Build.0 = Release|x64 + {BF74C473-DC04-44B3-92E8-4145F4E77342}.Debug AVX2|Win32.ActiveCfg = Debug|Win32 + {BF74C473-DC04-44B3-92E8-4145F4E77342}.Debug AVX2|Win32.Build.0 = Debug|Win32 + {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|Win32.ActiveCfg = Debug|Win32 + {BF74C473-DC04-44B3-92E8-4145F4E77342}.Debug|Win32.Build.0 = Debug|Win32 + {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|Win32.ActiveCfg = Devel|Win32 + {BF74C473-DC04-44B3-92E8-4145F4E77342}.Devel AVX2|Win32.Build.0 = Devel|Win32 + {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|Win32.ActiveCfg = Devel|Win32 + {BF74C473-DC04-44B3-92E8-4145F4E77342}.Devel|Win32.Build.0 = Devel|Win32 + {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|Win32.ActiveCfg = Release|Win32 + {BF74C473-DC04-44B3-92E8-4145F4E77342}.Release AVX2|Win32.Build.0 = Release|Win32 + {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|Win32.ActiveCfg = Release|Win32 + {BF74C473-DC04-44B3-92E8-4145F4E77342}.Release|Win32.Build.0 = Release|Win32 + {BF74C473-DC04-44B3-92E8-4145F4E77342}.Release|x64.ActiveCfg = Release|x64 + {BF74C473-DC04-44B3-92E8-4145F4E77342}.Release|x64.Build.0 = Release|x64 + {DE9653B6-17DD-356A-9EE0-28A731772587}.Debug AVX2|Win32.ActiveCfg = Debug|Win32 + {DE9653B6-17DD-356A-9EE0-28A731772587}.Debug AVX2|Win32.Build.0 = Debug|Win32 + {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|Win32.ActiveCfg = Debug|Win32 + {DE9653B6-17DD-356A-9EE0-28A731772587}.Debug|Win32.Build.0 = Debug|Win32 + {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|Win32.ActiveCfg = Devel|Win32 + {DE9653B6-17DD-356A-9EE0-28A731772587}.Devel AVX2|Win32.Build.0 = Devel|Win32 + {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|Win32.ActiveCfg = Devel|Win32 + {DE9653B6-17DD-356A-9EE0-28A731772587}.Devel|Win32.Build.0 = Devel|Win32 + {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|Win32.ActiveCfg = Release|Win32 + {DE9653B6-17DD-356A-9EE0-28A731772587}.Release AVX2|Win32.Build.0 = Release|Win32 + {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|Win32.ActiveCfg = Release|Win32 + {DE9653B6-17DD-356A-9EE0-28A731772587}.Release|Win32.Build.0 = Release|Win32 + {DE9653B6-17DD-356A-9EE0-28A731772587}.Release|x64.ActiveCfg = Release|x64 + {DE9653B6-17DD-356A-9EE0-28A731772587}.Release|x64.Build.0 = Release|x64 + {EF6834A9-11F3-4331-BC34-21B325ABB180}.Debug AVX2|Win32.ActiveCfg = Debug|Win32 + {EF6834A9-11F3-4331-BC34-21B325ABB180}.Debug AVX2|Win32.Build.0 = Debug|Win32 + {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|Win32.ActiveCfg = Debug|Win32 + {EF6834A9-11F3-4331-BC34-21B325ABB180}.Debug|Win32.Build.0 = Debug|Win32 + {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|Win32.ActiveCfg = Devel|Win32 + {EF6834A9-11F3-4331-BC34-21B325ABB180}.Devel AVX2|Win32.Build.0 = Devel|Win32 + {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|Win32.ActiveCfg = Devel|Win32 + {EF6834A9-11F3-4331-BC34-21B325ABB180}.Devel|Win32.Build.0 = Devel|Win32 + {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|Win32.ActiveCfg = Release|Win32 + {EF6834A9-11F3-4331-BC34-21B325ABB180}.Release AVX2|Win32.Build.0 = Release|Win32 + {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|Win32.ActiveCfg = Release|Win32 + {EF6834A9-11F3-4331-BC34-21B325ABB180}.Release|Win32.Build.0 = Release|Win32 + {EF6834A9-11F3-4331-BC34-21B325ABB180}.Release|x64.ActiveCfg = Release|x64 + {EF6834A9-11F3-4331-BC34-21B325ABB180}.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} + {D6973076-9317-4EF2-A0B8-B7A18AC0713E} = {78EBE642-7A4D-4EA7-86BE-5639C6646C38} + {0FAE817D-9A32-4830-857E-81DA57246E16} = {78EBE642-7A4D-4EA7-86BE-5639C6646C38} + {27F17499-A372-4408-8AFA-4F9F4584FBD3} = {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} + {47AFDBEF-F15F-4BC0-B436-5BE443C3F80F} = {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} + {88FB34EC-845E-4F21-A552-F1573B9ED167} = {78EBE642-7A4D-4EA7-86BE-5639C6646C38} + {1EC8B3C0-8FB3-46DE-A2E0-A9121203F266} = {78EBE642-7A4D-4EA7-86BE-5639C6646C38} + {BF74C473-DC04-44B3-92E8-4145F4E77342} = {78EBE642-7A4D-4EA7-86BE-5639C6646C38} + {DE9653B6-17DD-356A-9EE0-28A731772587} = {78EBE642-7A4D-4EA7-86BE-5639C6646C38} + {EF6834A9-11F3-4331-BC34-21B325ABB180} = {78EBE642-7A4D-4EA7-86BE-5639C6646C38} + EndGlobalSection + GlobalSection(ExtensibilityGlobals) = postSolution + SolutionGuid = {0BC474EA-3628-45D3-9DBC-E22D0B7E0F77} + EndGlobalSection +EndGlobal diff --git a/bin/resources/cover-placeholder.png b/bin/resources/cover-placeholder.png new file mode 100644 index 000000000..2108b4656 Binary files /dev/null and b/bin/resources/cover-placeholder.png differ diff --git a/cmake/SearchForStuff.cmake b/cmake/SearchForStuff.cmake index 1e4e07333..63934ff50 100644 --- a/cmake/SearchForStuff.cmake +++ b/cmake/SearchForStuff.cmake @@ -232,6 +232,10 @@ if(NOT USE_SYSTEM_YAML) endif() endif() +if(QT_BUILD) + find_package(Qt6 COMPONENTS Core Gui Widgets Network LinguistTools REQUIRED) +endif() + add_subdirectory(3rdparty/libchdr/libchdr EXCLUDE_FROM_ALL) if(USE_NATIVE_TOOLS) diff --git a/common/vsprops/QtCompile.props b/common/vsprops/QtCompile.props new file mode 100644 index 000000000..a3bdfb5d2 --- /dev/null +++ b/common/vsprops/QtCompile.props @@ -0,0 +1,197 @@ + + + + $(SolutionDir)bin\ + $(SolutionDir)3rdparty\qt\6.2.2\msvc2019_64\ + $(SolutionDir)3rdparty\qt\6.2.2\msvc2019_arm64\ + $(QTDIRDefault) + $(QTDIR)\ + $(SolutionDir)3rdparty\qt\6.2.2\msvc2019_64\ + false + true + $(QTDIR)include\ + $(QTDIR)lib\ + $(QTDIR)bin\ + $(QTDIRHost)bin\ + $(QTDIR)plugins\ + $(QTDIR)translations\ + $(IntDir) + $(QtToolOutDir)moc_ + $(BinaryOutputDir)translations\ + d + $(QtDebugSuffix) + QtPlugins + + + + QT_NO_DEBUG;%(PreprocessorDefinitions) + $(ProjectDir);%(AdditionalIncludeDirectories) + $(QtToolOutDir);%(AdditionalIncludeDirectories) + $(QtIncludeDir);%(AdditionalIncludeDirectories) + + + $(QtLibDir);%(AdditionalLibraryDirectories) + Qt6Core$(QtLibSuffix).lib;Qt6Gui$(QtLibSuffix).lib;Qt6Widgets$(QtLibSuffix).lib;Qt6Network$(QtLibSuffix).lib;%(AdditionalDependencies) + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + -DQT_NO_DEBUG -DNDEBUG $(MocDefines) + "-I$(QtIncludeDir)" "-I$(SolutionDir)pcsx2" "-I$(SolutionDir)" -I. + + + + + + + + + + + + + + + + + + + + QtResource + + + QtUi + + + QtMoc + + + QtTs + + + + + + + + + + + + + + + $(BinaryOutputDir)qt.conf + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/common/vsprops/QtCompile.targets b/common/vsprops/QtCompile.targets new file mode 100644 index 000000000..75ac29f30 --- /dev/null +++ b/common/vsprops/QtCompile.targets @@ -0,0 +1,10 @@ + + + + QtResourceClean;QtUiClean;QtMocClean;QtTsClean;$(CleanDependsOn) + + + \ No newline at end of file diff --git a/common/vsprops/QtCompile.xml b/common/vsprops/QtCompile.xml new file mode 100644 index 000000000..376118df7 --- /dev/null +++ b/common/vsprops/QtCompile.xml @@ -0,0 +1,14 @@ + + + + + + + + + + + + + + \ No newline at end of file diff --git a/pcsx2-qt/AboutDialog.cpp b/pcsx2-qt/AboutDialog.cpp new file mode 100644 index 000000000..5818bf45e --- /dev/null +++ b/pcsx2-qt/AboutDialog.cpp @@ -0,0 +1,76 @@ +/* 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 "AboutDialog.h" +#include "QtUtils.h" +#include "svnrev.h" +#include +#include + +AboutDialog::AboutDialog(QWidget* parent) + : QDialog(parent) +{ + m_ui.setupUi(this); + + setWindowFlags(windowFlags() & ~Qt::WindowContextHelpButtonHint); + setFixedSize(geometry().width(), geometry().height()); + + m_ui.scmversion->setTextInteractionFlags(Qt::TextSelectableByMouse); + m_ui.scmversion->setText(GIT_REV); + + m_ui.links->setTextInteractionFlags(Qt::TextBrowserInteraction); + m_ui.links->setOpenExternalLinks(true); + m_ui.links->setText(QStringLiteral( + R"(%2 | %4 | %6 | %8)") + .arg(getWebsiteUrl()) + .arg(tr("Website")) + .arg(getSupportForumsUrl()) + .arg(tr("Support Forums")) + .arg(getGitHubRepositoryUrl()) + .arg(tr("GitHub Repository")) + .arg(getLicenseUrl()) + .arg(tr("License"))); + + connect(m_ui.buttonBox, &QDialogButtonBox::rejected, this, &QDialog::close); +} + +AboutDialog::~AboutDialog() = default; + +QString AboutDialog::getWebsiteUrl() +{ + return QStringLiteral("https://pcsx2.net/"); +} + +QString AboutDialog::getSupportForumsUrl() +{ + return QStringLiteral("https://forums.pcsx2.net/"); +} + +QString AboutDialog::getGitHubRepositoryUrl() +{ + return QStringLiteral("https://github.com/PCSX2/pcsx2"); +} + +QString AboutDialog::getLicenseUrl() +{ + return QStringLiteral("https://github.com/PCSX2/pcsx2/blob/master/pcsx2/Docs/License.txt"); +} + +QString AboutDialog::getDiscordServerUrl() +{ + return QStringLiteral("https://discord.com/invite/TCz3t9k"); +} diff --git a/pcsx2-qt/AboutDialog.h b/pcsx2-qt/AboutDialog.h new file mode 100644 index 000000000..74109bc05 --- /dev/null +++ b/pcsx2-qt/AboutDialog.h @@ -0,0 +1,37 @@ +/* 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 . + */ + +#pragma once + +#include "ui_AboutDialog.h" +#include + +class AboutDialog final : public QDialog +{ + Q_OBJECT + +public: + explicit AboutDialog(QWidget* parent = nullptr); + ~AboutDialog(); + + static QString getWebsiteUrl(); + static QString getSupportForumsUrl(); + static QString getGitHubRepositoryUrl(); + static QString getLicenseUrl(); + static QString getDiscordServerUrl(); + +private: + Ui::AboutDialog m_ui; +}; diff --git a/pcsx2-qt/AboutDialog.ui b/pcsx2-qt/AboutDialog.ui new file mode 100644 index 000000000..1212f9587 --- /dev/null +++ b/pcsx2-qt/AboutDialog.ui @@ -0,0 +1,168 @@ + + + AboutDialog + + + + 0 + 0 + 576 + 294 + + + + About PCSX2 + + + + + + + + Qt::Horizontal + + + + 40 + 20 + + + + + + + + + 260 + 260 + + + + + + + :/icons/logo.png + + + Qt::AlignCenter + + + + + + + Qt::Horizontal + + + + 40 + 20 + + + + + + + + + + SCM Version + + + Qt::AlignCenter + + + + + + + <html><head/><body><p>PCSX2 is a free and open-source PlayStation 2 (PS2) emulator. Its purpose is to emulate the PS2's hardware, using a combination of MIPS CPU Interpreters, Recompilers and a Virtual Machine which manages hardware states and PS2 system memory. This allows you to play PS2 games on your PC, with many additional features and benefits.</p></body></html> + + + Qt::AlignJustify|Qt::AlignVCenter + + + true + + + + + + + Qt::Vertical + + + + 20 + 40 + + + + + + + + <html><head/><body><p>PlayStation 2 and PS2 are registered trademarks of Sony Interactive Entertainment. This application is not affiliated in any way with Sony Interactive Entertainment.</p></body></html> + + + Qt::AlignJustify|Qt::AlignVCenter + + + true + + + + + + + Qt::Vertical + + + + 20 + 40 + + + + + + + + TextLabel + + + Qt::AlignCenter + + + + + + + Qt::Vertical + + + + 20 + 40 + + + + + + + + QDialogButtonBox::Close + + + true + + + + + + + + + + + diff --git a/pcsx2-qt/CMakeLists.txt b/pcsx2-qt/CMakeLists.txt new file mode 100644 index 000000000..0beb5bed9 --- /dev/null +++ b/pcsx2-qt/CMakeLists.txt @@ -0,0 +1,102 @@ +set(CMAKE_AUTOMOC ON) +set(CMAKE_AUTORCC ON) +set(CMAKE_AUTOUIC ON) + +add_executable(pcsx2-qt) + +if (PACKAGE_MODE) + install(TARGETS pcsx2-qt DESTINATION ${CMAKE_INSTALL_BINDIR}) +else() + install(TARGETS pcsx2-qt DESTINATION ${CMAKE_SOURCE_DIR}/bin) +endif() + +target_sources(pcsx2-qt PRIVATE + AboutDialog.cpp + AboutDialog.h + AboutDialog.ui + DisplayWidget.cpp + DisplayWidget.h + EmuThread.cpp + EmuThread.h + Main.cpp + MainWindow.cpp + MainWindow.h + MainWindow.ui + PrecompiledHeader.cpp + PrecompiledHeader.h + QtHost.cpp + QtHost.h + QtKeyCodes.cpp + QtUtils.cpp + QtUtils.h + SettingWidgetBinder.h + GameList/GameListModel.cpp + GameList/GameListModel.h + GameList/GameListRefreshThread.cpp + GameList/GameListRefreshThread.h + GameList/GameListWidget.cpp + GameList/GameListWidget.h + Settings/AdvancedSystemSettingsWidget.cpp + Settings/AdvancedSystemSettingsWidget.h + Settings/AdvancedSystemSettingsWidget.ui + Settings/BIOSSettingsWidget.cpp + Settings/BIOSSettingsWidget.h + Settings/BIOSSettingsWidget.ui + Settings/ControllerBindingWidget.ui + Settings/ControllerBindingWidget_DualShock2.ui + Settings/ControllerBindingWidgets.cpp + Settings/ControllerBindingWidgets.h + Settings/ControllerGlobalSettingsWidget.cpp + Settings/ControllerGlobalSettingsWidget.h + Settings/ControllerGlobalSettingsWidget.ui + Settings/ControllerSettingsDialog.cpp + Settings/ControllerSettingsDialog.h + Settings/ControllerSettingsDialog.ui + Settings/EmulationSettingsWidget.cpp + Settings/EmulationSettingsWidget.h + Settings/EmulationSettingsWidget.ui + Settings/GameFixSettingsWidget.cpp + Settings/GameFixSettingsWidget.h + Settings/GameFixSettingsWidget.ui + Settings/GameListSettingsWidget.cpp + Settings/GameListSettingsWidget.h + Settings/GameListSettingsWidget.ui + Settings/GraphicsSettingsWidget.cpp + Settings/GraphicsSettingsWidget.h + Settings/GraphicsSettingsWidget.ui + Settings/HotkeySettingsWidget.cpp + Settings/HotkeySettingsWidget.h + Settings/InputBindingDialog.cpp + Settings/InputBindingDialog.h + Settings/InputBindingDialog.ui + Settings/InputBindingWidget.cpp + Settings/InputBindingWidget.h + Settings/InterfaceSettingsWidget.cpp + Settings/InterfaceSettingsWidget.h + Settings/InterfaceSettingsWidget.ui + Settings/SettingsDialog.cpp + Settings/SettingsDialog.h + Settings/SettingsDialog.ui + Settings/SystemSettingsWidget.cpp + Settings/SystemSettingsWidget.h + Settings/SystemSettingsWidget.ui + resources/resources.qrc +) + +target_precompile_headers(pcsx2-qt PRIVATE PrecompiledHeader.h) + +target_include_directories(pcsx2-qt PRIVATE + ${Qt6Gui_PRIVATE_INCLUDE_DIRS} + "${CMAKE_BINARY_DIR}/common/include" + "${CMAKE_SOURCE_DIR}/pcsx2" + "${CMAKE_SOURCE_DIR}/pcsx2-qt" +) + +target_link_libraries(pcsx2-qt PRIVATE + PCSX2_FLAGS + PCSX2 + Qt6::Core + Qt6::Gui + Qt6::Widgets + Qt6::Network +) diff --git a/pcsx2-qt/DisplayWidget.cpp b/pcsx2-qt/DisplayWidget.cpp new file mode 100644 index 000000000..f96ba2596 --- /dev/null +++ b/pcsx2-qt/DisplayWidget.cpp @@ -0,0 +1,334 @@ +/* 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 "DisplayWidget.h" +#include "EmuThread.h" +#include "QtHost.h" + +#include "pcsx2/GS/GSIntrin.h" // _BitScanForward + +#include +#include +#include +#include +#include +#include +#include + +#if !defined(_WIN32) && !defined(APPLE) +#include +#endif + +DisplayWidget::DisplayWidget(QWidget* parent) + : QWidget(parent) +{ + // We want a native window for both D3D and OpenGL. + setAutoFillBackground(false); + setAttribute(Qt::WA_NativeWindow, true); + setAttribute(Qt::WA_NoSystemBackground, true); + setAttribute(Qt::WA_PaintOnScreen, true); + setFocusPolicy(Qt::StrongFocus); + setMouseTracking(true); +} + +DisplayWidget::~DisplayWidget() = default; + +qreal DisplayWidget::devicePixelRatioFromScreen() const +{ + QScreen* screen_for_ratio; +#if QT_VERSION < QT_VERSION_CHECK(5, 14, 0) + screen_for_ratio = windowHandle()->screen(); +#else + screen_for_ratio = screen(); +#endif + if (!screen_for_ratio) + screen_for_ratio = QGuiApplication::primaryScreen(); + + return screen_for_ratio ? screen_for_ratio->devicePixelRatio() : static_cast(1); +} + +int DisplayWidget::scaledWindowWidth() const +{ + return static_cast(std::ceil(static_cast(width()) * devicePixelRatioFromScreen())); +} + +int DisplayWidget::scaledWindowHeight() const +{ + return static_cast(std::ceil(static_cast(height()) * devicePixelRatioFromScreen())); +} + +std::optional DisplayWidget::getWindowInfo() const +{ + WindowInfo wi; + + // Windows and Apple are easy here since there's no display connection. +#if defined(_WIN32) + wi.type = WindowInfo::Type::Win32; + wi.window_handle = reinterpret_cast(winId()); +#elif defined(__APPLE__) + wi.type = WindowInfo::Type::MacOS; + wi.window_handle = reinterpret_cast(winId()); +#else + QPlatformNativeInterface* pni = QGuiApplication::platformNativeInterface(); + const QString platform_name = QGuiApplication::platformName(); + if (platform_name == QStringLiteral("xcb")) + { + wi.type = WindowInfo::Type::X11; + wi.display_connection = pni->nativeResourceForWindow("display", windowHandle()); + wi.window_handle = reinterpret_cast(winId()); + } + else if (platform_name == QStringLiteral("wayland")) + { + wi.type = WindowInfo::Type::Wayland; + wi.display_connection = pni->nativeResourceForWindow("display", windowHandle()); + wi.window_handle = pni->nativeResourceForWindow("surface", windowHandle()); + } + else + { + qCritical() << "Unknown PNI platform " << platform_name; + return std::nullopt; + } +#endif + + wi.surface_width = scaledWindowWidth(); + wi.surface_height = scaledWindowHeight(); + wi.surface_scale = devicePixelRatioFromScreen(); + return wi; +} + +void DisplayWidget::setRelativeMode(bool enabled) +{ + if (m_relative_mouse_enabled == enabled) + return; + + if (enabled) + { + m_relative_mouse_start_position = QCursor::pos(); + + const QPoint center_pos = mapToGlobal(QPoint(width() / 2, height() / 2)); + QCursor::setPos(center_pos); + m_relative_mouse_last_position = center_pos; + grabMouse(); + } + else + { + QCursor::setPos(m_relative_mouse_start_position); + releaseMouse(); + } + + m_relative_mouse_enabled = enabled; +} + +QPaintEngine* DisplayWidget::paintEngine() const +{ + return nullptr; +} + +bool DisplayWidget::event(QEvent* event) +{ + switch (event->type()) + { + case QEvent::KeyPress: + case QEvent::KeyRelease: + { + const QKeyEvent* key_event = static_cast(event); + if (!key_event->isAutoRepeat()) + { + emit windowKeyEvent(key_event->key(), static_cast(key_event->modifiers()), + event->type() == QEvent::KeyPress); + } + + return true; + } + + case QEvent::MouseMove: + { + const QMouseEvent* mouse_event = static_cast(event); + + if (!m_relative_mouse_enabled) + { + const qreal dpr = devicePixelRatioFromScreen(); + const QPoint mouse_pos = mouse_event->pos(); + const int scaled_x = static_cast(static_cast(mouse_pos.x()) * dpr); + const int scaled_y = static_cast(static_cast(mouse_pos.y()) * dpr); + + windowMouseMoveEvent(scaled_x, scaled_y); + } + else + { + const QPoint center_pos = mapToGlobal(QPoint((width() + 1) / 2, (height() + 1) / 2)); + const QPoint mouse_pos = mapToGlobal(mouse_event->pos()); + + const int dx = mouse_pos.x() - center_pos.x(); + const int dy = mouse_pos.y() - center_pos.y(); + m_relative_mouse_last_position.setX(m_relative_mouse_last_position.x() + dx); + m_relative_mouse_last_position.setY(m_relative_mouse_last_position.y() + dy); + windowMouseMoveEvent(m_relative_mouse_last_position.x(), m_relative_mouse_last_position.y()); + QCursor::setPos(center_pos); + } + + return true; + } + + case QEvent::MouseButtonPress: + case QEvent::MouseButtonDblClick: + case QEvent::MouseButtonRelease: + { + unsigned long button_index; + if (_BitScanForward(&button_index, static_cast(static_cast(event)->button()))) + emit windowMouseButtonEvent(static_cast(button_index + 1u), event->type() != QEvent::MouseButtonRelease); + + if (event->type() == QEvent::MouseButtonDblClick && + static_cast(event)->button() == Qt::LeftButton && + Host::GetBoolSettingValue("UI", "DoubleClickTogglesFullscreen", true)) + { + g_emu_thread->toggleFullscreen(); + } + + return true; + } + + case QEvent::Wheel: + { + const QWheelEvent* wheel_event = static_cast(event); + emit windowMouseWheelEvent(wheel_event->angleDelta()); + return true; + } + + case QEvent::Resize: + { + QWidget::event(event); + + emit windowResizedEvent(scaledWindowWidth(), scaledWindowHeight(), devicePixelRatioFromScreen()); + return true; + } + + case QEvent::Close: + { + emit windowClosedEvent(); + QWidget::event(event); + return true; + } + + case QEvent::WindowStateChange: + { + QWidget::event(event); + + if (static_cast(event)->oldState() & Qt::WindowMinimized) + emit windowRestoredEvent(); + + return true; + } + + case QEvent::FocusIn: + { + QWidget::event(event); + emit windowFocusEvent(); + return true; + } + + case QEvent::ActivationChange: + { + QWidget::event(event); + if (isActiveWindow()) + emit windowFocusEvent(); + + return true; + } + + default: + return QWidget::event(event); + } +} + +DisplayContainer::DisplayContainer() + : QStackedWidget(nullptr) +{ +} + +DisplayContainer::~DisplayContainer() = default; + +bool DisplayContainer::IsNeeded(bool fullscreen, bool render_to_main) +{ +#if defined(_WIN32) || defined(__APPLE__) + return false; +#else + if (fullscreen || render_to_main) + return false; + + // We only need this on Wayland because of client-side decorations... + const QString platform_name = QGuiApplication::platformName(); + return (platform_name == QStringLiteral("wayland")); +#endif +} + +void DisplayContainer::setDisplayWidget(DisplayWidget* widget) +{ + pxAssert(!m_display_widget); + m_display_widget = widget; + addWidget(widget); +} + +DisplayWidget* DisplayContainer::removeDisplayWidget() +{ + DisplayWidget* widget = m_display_widget; + pxAssert(widget); + m_display_widget = nullptr; + removeWidget(widget); + return widget; +} + +bool DisplayContainer::event(QEvent* event) +{ + const bool res = QStackedWidget::event(event); + if (!m_display_widget) + return res; + + switch (event->type()) + { + case QEvent::Close: + { + emit m_display_widget->windowClosedEvent(); + } + break; + + case QEvent::WindowStateChange: + { + if (static_cast(event)->oldState() & Qt::WindowMinimized) + emit m_display_widget->windowRestoredEvent(); + } + break; + + case QEvent::FocusIn: + { + emit m_display_widget->windowFocusEvent(); + } + break; + + case QEvent::ActivationChange: + { + if (isActiveWindow()) + emit m_display_widget->windowFocusEvent(); + } + break; + + default: + break; + } + + return res; +} diff --git a/pcsx2-qt/DisplayWidget.h b/pcsx2-qt/DisplayWidget.h new file mode 100644 index 000000000..cffc351f9 --- /dev/null +++ b/pcsx2-qt/DisplayWidget.h @@ -0,0 +1,77 @@ +/* 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 . + */ + +#pragma once +#include "common/WindowInfo.h" +#include +#include +#include + +class DisplayWidget final : public QWidget +{ + Q_OBJECT + +public: + explicit DisplayWidget(QWidget* parent); + ~DisplayWidget(); + + QPaintEngine* paintEngine() const override; + + int scaledWindowWidth() const; + int scaledWindowHeight() const; + qreal devicePixelRatioFromScreen() const; + + std::optional getWindowInfo() const; + + void setRelativeMode(bool enabled); + +Q_SIGNALS: + void windowFocusEvent(); + void windowResizedEvent(int width, int height, float scale); + void windowRestoredEvent(); + void windowClosedEvent(); + void windowKeyEvent(int key_code, int mods, bool pressed); + void windowMouseMoveEvent(int x, int y); + void windowMouseButtonEvent(int button, bool pressed); + void windowMouseWheelEvent(const QPoint& angle_delta); + +protected: + bool event(QEvent* event) override; + +private: + QPoint m_relative_mouse_start_position{}; + QPoint m_relative_mouse_last_position{}; + bool m_relative_mouse_enabled = false; +}; + +class DisplayContainer final : public QStackedWidget +{ + Q_OBJECT + +public: + DisplayContainer(); + ~DisplayContainer(); + + static bool IsNeeded(bool fullscreen, bool render_to_main); + + void setDisplayWidget(DisplayWidget* widget); + DisplayWidget* removeDisplayWidget(); + +protected: + bool event(QEvent* event) override; + +private: + DisplayWidget* m_display_widget = nullptr; +}; diff --git a/pcsx2-qt/EmuThread.cpp b/pcsx2-qt/EmuThread.cpp new file mode 100644 index 000000000..705553189 --- /dev/null +++ b/pcsx2-qt/EmuThread.cpp @@ -0,0 +1,755 @@ +/* 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 + +#include "common/Assertions.h" +#include "common/Console.h" +#include "common/Exceptions.h" +#include "common/SettingsWrapper.h" +#include "common/StringUtil.h" + +#include "pcsx2/CDVD/CDVD.h" +#include "pcsx2/Frontend/InputManager.h" +#include "pcsx2/Frontend/ImGuiManager.h" +#include "pcsx2/GS.h" +#include "pcsx2/HostDisplay.h" +#include "pcsx2/PAD/Host/PAD.h" +#include "pcsx2/PerformanceMetrics.h" +#include "pcsx2/VMManager.h" + +#include "pcsx2/Frontend/OpenGLHostDisplay.h" + +#ifdef _WIN32 +#include "pcsx2/Frontend/D3D11HostDisplay.h" +#endif + +#include "DisplayWidget.h" +#include "EmuThread.h" +#include "MainWindow.h" +#include "QtHost.h" + +EmuThread* g_emu_thread = nullptr; +WindowInfo g_gs_window_info; + +static std::unique_ptr s_host_display; + +EmuThread::EmuThread(QThread* ui_thread) + : QThread() + , m_ui_thread(ui_thread) +{ +} + +EmuThread::~EmuThread() = default; + +bool EmuThread::isOnEmuThread() const +{ + return QThread::currentThread() == this; +} + +void EmuThread::start() +{ + pxAssertRel(!g_emu_thread, "Emu thread does not exist"); + + g_emu_thread = new EmuThread(QThread::currentThread()); + g_emu_thread->QThread::start(); + g_emu_thread->m_started_semaphore.acquire(); + g_emu_thread->moveToThread(g_emu_thread); + g_main_window->connectVMThreadSignals(g_emu_thread); +} + +void EmuThread::stop() +{ + pxAssertRel(g_emu_thread, "Emu thread exists"); + pxAssertRel(!g_emu_thread->isOnEmuThread(), "Not called on the emu thread"); + + QMetaObject::invokeMethod(g_emu_thread, &EmuThread::stopInThread, Qt::QueuedConnection); + while (g_emu_thread->isRunning()) + QApplication::processEvents(QEventLoop::ExcludeUserInputEvents, 1); +} + +void EmuThread::stopInThread() +{ + if (VMManager::HasValidVM()) + destroyVM(); + + m_event_loop->quit(); + m_shutdown_flag.store(true); +} + +void EmuThread::startVM(std::shared_ptr boot_params) +{ + if (!isOnEmuThread()) + { + QMetaObject::invokeMethod(this, "startVM", Qt::QueuedConnection, + Q_ARG(std::shared_ptr, boot_params)); + return; + } + + pxAssertRel(!VMManager::HasValidVM(), "VM is shut down"); + + emit onVMStarting(); + + // create the display, this may take a while... + m_is_fullscreen = boot_params->fullscreen.value_or(QtHost::GetBaseBoolSettingValue("UI", "StartFullscreen", false)); + m_is_rendering_to_main = !m_is_fullscreen && QtHost::GetBaseBoolSettingValue("UI", "RenderToMainWindow", true); + if (!VMManager::Initialize(*boot_params)) + return; + + VMManager::SetState(VMState::Running); + m_event_loop->quit(); +} + +void EmuThread::resetVM() +{ + if (!isOnEmuThread()) + { + QMetaObject::invokeMethod(this, &EmuThread::resetVM, Qt::QueuedConnection); + return; + } + + VMManager::Reset(); +} + +void EmuThread::setVMPaused(bool paused) +{ + if (!isOnEmuThread()) + { + QMetaObject::invokeMethod(this, "setVMPaused", Qt::QueuedConnection, Q_ARG(bool, paused)); + return; + } + + VMManager::SetPaused(paused); +} + +void EmuThread::shutdownVM(bool allow_save_to_state /* = true */, bool blocking /* = false */) +{ + if (!isOnEmuThread()) + { + QMetaObject::invokeMethod(this, "shutdownVM", Qt::QueuedConnection, Q_ARG(bool, allow_save_to_state), + Q_ARG(bool, blocking)); + + if (blocking) + { + // we need to yield here, since the display gets destroyed + while (VMManager::HasValidVM()) + QApplication::processEvents(QEventLoop::ExcludeUserInputEvents, 1); + } + + return; + } + + const VMState state = VMManager::GetState(); + if (state == VMState::Paused) + m_event_loop->quit(); + else if (state != VMState::Running) + return; + + VMManager::SetState(VMState::Stopping); +} + +void EmuThread::loadState(const QString& filename) +{ + if (!isOnEmuThread()) + { + QMetaObject::invokeMethod(this, "loadState", Qt::QueuedConnection, Q_ARG(const QString&, filename)); + return; + } + + if (!VMManager::HasValidVM()) + return; + + VMManager::LoadState(filename.toUtf8().constData()); +} + +void EmuThread::loadStateFromSlot(qint32 slot) +{ + if (!isOnEmuThread()) + { + QMetaObject::invokeMethod(this, "loadStateFromSlot", Qt::QueuedConnection, Q_ARG(qint32, slot)); + return; + } + + if (!VMManager::HasValidVM()) + return; + + VMManager::LoadStateFromSlot(slot); +} + +void EmuThread::saveState(const QString& filename) +{ + if (!isOnEmuThread()) + { + QMetaObject::invokeMethod(this, "saveState", Qt::QueuedConnection, Q_ARG(const QString&, filename)); + return; + } + + if (!VMManager::HasValidVM()) + return; + + if (!VMManager::SaveState(filename.toUtf8().constData())) + { + // this one is usually the result of a user-chosen path, so we can display a message box safely here + Console.Error("Failed to save state"); + } +} + +void EmuThread::saveStateToSlot(qint32 slot) +{ + if (!isOnEmuThread()) + { + QMetaObject::invokeMethod(this, "saveStateToSlot", Qt::QueuedConnection, Q_ARG(qint32, slot)); + return; + } + + if (!VMManager::HasValidVM()) + return; + + VMManager::SaveStateToSlot(slot); +} + +void EmuThread::run() +{ + PerformanceMetrics::SetCPUThreadTimer(Common::ThreadCPUTimer::GetForCallingThread()); + m_event_loop = new QEventLoop(); + m_started_semaphore.release(); + + if (!VMManager::InitializeMemory()) + pxFailRel("Failed to allocate memory map"); + + // we need input sources ready for binding + reloadInputSources(); + createBackgroundControllerPollTimer(); + startBackgroundControllerPollTimer(); + + while (!m_shutdown_flag.load()) + { + if (!VMManager::HasValidVM()) + { + m_event_loop->exec(); + continue; + } + + executeVM(); + } + + stopBackgroundControllerPollTimer(); + destroyBackgroundControllerPollTimer(); + InputManager::CloseSources(); + VMManager::ReleaseMemory(); + PerformanceMetrics::SetCPUThreadTimer(Common::ThreadCPUTimer()); + moveToThread(m_ui_thread); + deleteLater(); +} + +void EmuThread::destroyVM() +{ + VMManager::Shutdown(); +} + +void EmuThread::executeVM() +{ + for (;;) + { + switch (VMManager::GetState()) + { + case VMState::Initializing: + pxFailRel("Shouldn't be in the starting state state"); + continue; + + case VMState::Paused: + m_event_loop->exec(); + continue; + + case VMState::Running: + m_event_loop->processEvents(QEventLoop::AllEvents); + VMManager::Execute(); + continue; + + case VMState::Stopping: + destroyVM(); + m_event_loop->processEvents(QEventLoop::AllEvents); + return; + } + } +} + +void EmuThread::createBackgroundControllerPollTimer() +{ + pxAssert(!m_background_controller_polling_timer); + m_background_controller_polling_timer = new QTimer(this); + m_background_controller_polling_timer->setSingleShot(false); + m_background_controller_polling_timer->setTimerType(Qt::CoarseTimer); + connect(m_background_controller_polling_timer, &QTimer::timeout, this, &EmuThread::doBackgroundControllerPoll); +} + +void EmuThread::destroyBackgroundControllerPollTimer() +{ + delete m_background_controller_polling_timer; + m_background_controller_polling_timer = nullptr; +} + +void EmuThread::startBackgroundControllerPollTimer() +{ + if (m_background_controller_polling_timer->isActive()) + return; + + m_background_controller_polling_timer->start(BACKGROUND_CONTROLLER_POLLING_INTERVAL); +} + +void EmuThread::stopBackgroundControllerPollTimer() +{ + if (!m_background_controller_polling_timer->isActive()) + return; + + m_background_controller_polling_timer->stop(); +} + +void EmuThread::doBackgroundControllerPoll() +{ + InputManager::PollSources(); +} + +void EmuThread::toggleFullscreen() +{ + if (!isOnEmuThread()) + { + QMetaObject::invokeMethod(this, &EmuThread::toggleFullscreen, Qt::QueuedConnection); + return; + } + + setFullscreen(!m_is_fullscreen); +} + +void EmuThread::setFullscreen(bool fullscreen) +{ + if (!isOnEmuThread()) + { + QMetaObject::invokeMethod(this, "setFullscreen", Qt::QueuedConnection, Q_ARG(bool, fullscreen)); + return; + } + + if (!VMManager::HasValidVM() || m_is_fullscreen == fullscreen) + return; + + // This will call back to us on the MTGS thread. + m_is_fullscreen = fullscreen; + GetMTGS().UpdateDisplayWindow(); + GetMTGS().WaitGS(); +} + +void EmuThread::applySettings() +{ + if (!isOnEmuThread()) + { + QMetaObject::invokeMethod(this, &EmuThread::applySettings, Qt::QueuedConnection); + return; + } + + checkForSettingChanges(); + VMManager::ApplySettings(); +} + +void EmuThread::checkForSettingChanges() +{ + if (VMManager::HasValidVM()) + { + const bool render_to_main = QtHost::GetBaseBoolSettingValue("UI", "RenderToMainWindow", true); + if (!m_is_fullscreen && m_is_rendering_to_main != render_to_main) + { + m_is_rendering_to_main = render_to_main; + GetMTGS().UpdateDisplayWindow(); + GetMTGS().WaitGS(); + } + } +} + +void EmuThread::toggleSoftwareRendering() +{ + if (!isOnEmuThread()) + { + QMetaObject::invokeMethod(this, &EmuThread::toggleSoftwareRendering, Qt::QueuedConnection); + return; + } + + if (!VMManager::HasValidVM()) + return; + + GetMTGS().ToggleSoftwareRendering(); +} + +void EmuThread::switchRenderer(GSRendererType renderer) +{ + if (!isOnEmuThread()) + { + QMetaObject::invokeMethod(this, "switchRenderer", Qt::QueuedConnection, Q_ARG(GSRendererType, renderer)); + return; + } + + if (!VMManager::HasValidVM()) + return; + + GetMTGS().SwitchRenderer(renderer); +} + +void EmuThread::changeDisc(const QString& path) +{ + if (!isOnEmuThread()) + { + QMetaObject::invokeMethod(this, "changeDisc", Qt::QueuedConnection, Q_ARG(const QString&, path)); + return; + } + + if (!VMManager::HasValidVM()) + return; + + VMManager::ChangeDisc(path.toStdString()); +} + +void EmuThread::reloadPatches() +{ + if (!isOnEmuThread()) + { + QMetaObject::invokeMethod(this, &EmuThread::reloadPatches, Qt::QueuedConnection); + return; + } + + if (!VMManager::HasValidVM()) + return; + + VMManager::ReloadPatches(true); +} + +void EmuThread::reloadInputSources() +{ + if (!isOnEmuThread()) + { + QMetaObject::invokeMethod(this, &EmuThread::reloadInputSources, Qt::QueuedConnection); + return; + } + + auto lock = Host::GetSettingsLock(); + SettingsInterface* si = Host::GetSettingsInterface(); + InputManager::ReloadSources(*si); + + // skip loading bindings if we're not running, since it'll get done on startup anyway + if (VMManager::HasValidVM()) + InputManager::ReloadBindings(*si); +} + +void EmuThread::reloadInputBindings() +{ + if (!isOnEmuThread()) + { + QMetaObject::invokeMethod(this, &EmuThread::reloadInputBindings, Qt::QueuedConnection); + return; + } + + // skip loading bindings if we're not running, since it'll get done on startup anyway + if (!VMManager::HasValidVM()) + return; + + auto lock = Host::GetSettingsLock(); + SettingsInterface* si = Host::GetSettingsInterface(); + InputManager::ReloadBindings(*si); +} + +void EmuThread::requestDisplaySize(float scale) +{ + if (!isOnEmuThread()) + { + QMetaObject::invokeMethod(this, "requestDisplaySize", Qt::QueuedConnection, Q_ARG(float, scale)); + return; + } + + if (!VMManager::HasValidVM()) + return; + + VMManager::RequestDisplaySize(scale); +} + +void EmuThread::enumerateInputDevices() +{ + if (!isOnEmuThread()) + { + QMetaObject::invokeMethod(this, &EmuThread::enumerateInputDevices, Qt::QueuedConnection); + return; + } + + const std::vector> devs(InputManager::EnumerateDevices()); + QList> qdevs; + qdevs.reserve(devs.size()); + for (const std::pair& dev : devs) + qdevs.emplace_back(QString::fromStdString(dev.first), QString::fromStdString(dev.second)); + + onInputDevicesEnumerated(qdevs); +} + +void EmuThread::enumerateVibrationMotors() +{ + if (!isOnEmuThread()) + { + QMetaObject::invokeMethod(this, &EmuThread::enumerateVibrationMotors, Qt::QueuedConnection); + return; + } + + const std::vector motors(InputManager::EnumerateMotors()); + QList qmotors; + qmotors.reserve(motors.size()); + for (InputBindingKey key : motors) + qmotors.push_back(key); + + onVibrationMotorsEnumerated(qmotors); +} + +void EmuThread::connectDisplaySignals(DisplayWidget* widget) +{ + widget->disconnect(this); + + connect(widget, &DisplayWidget::windowFocusEvent, this, &EmuThread::onDisplayWindowFocused); + connect(widget, &DisplayWidget::windowResizedEvent, this, &EmuThread::onDisplayWindowResized); + // connect(widget, &DisplayWidget::windowRestoredEvent, this, &EmuThread::redrawDisplayWindow); + connect(widget, &DisplayWidget::windowClosedEvent, []() { g_emu_thread->shutdownVM(true, true); }); + connect(widget, &DisplayWidget::windowKeyEvent, this, &EmuThread::onDisplayWindowKeyEvent); + connect(widget, &DisplayWidget::windowMouseMoveEvent, this, &EmuThread::onDisplayWindowMouseMoveEvent); + connect(widget, &DisplayWidget::windowMouseButtonEvent, this, &EmuThread::onDisplayWindowMouseButtonEvent); + connect(widget, &DisplayWidget::windowMouseWheelEvent, this, &EmuThread::onDisplayWindowMouseWheelEvent); +} + +void EmuThread::onDisplayWindowMouseMoveEvent(int x, int y) {} + +void EmuThread::onDisplayWindowMouseButtonEvent(int button, bool pressed) +{ + InputManager::InvokeEvents(InputManager::MakeHostMouseButtonKey(button), pressed ? 1.0f : 0.0f); +} + +void EmuThread::onDisplayWindowMouseWheelEvent(const QPoint& delta_angle) {} + +void EmuThread::onDisplayWindowKeyEvent(int key, int mods, bool pressed) +{ + InputManager::InvokeEvents(InputManager::MakeHostKeyboardKey(key), pressed ? 1.0f : 0.0f); +} + +void EmuThread::onDisplayWindowResized(int width, int height, float scale) +{ + if (!VMManager::HasValidVM()) + return; + + GetMTGS().ResizeDisplayWindow(width, height, scale); +} + +void EmuThread::onDisplayWindowFocused() {} + +void EmuThread::updateDisplay() +{ + pxAssertRel(!isOnEmuThread(), "Not on emu thread"); + + // finished with the display for now + HostDisplay* display = Host::GetHostDisplay(); + display->DoneRenderContextCurrent(); + + // but we should get it back after this call + DisplayWidget* widget = onUpdateDisplayRequested(m_is_fullscreen, !m_is_fullscreen && m_is_rendering_to_main); + if (!widget || !display->MakeRenderContextCurrent()) + { + pxFailRel("Failed to recreate context after updating"); + return; + } + + // this is always a new widget, so reconnect it + connectDisplaySignals(widget); +} + +HostDisplay* EmuThread::acquireHostDisplay(HostDisplay::RenderAPI api) +{ + s_host_display = HostDisplay::CreateDisplayForAPI(api); + if (!s_host_display) + return nullptr; + + DisplayWidget* widget = emit onCreateDisplayRequested(m_is_fullscreen, m_is_rendering_to_main); + if (!widget) + { + s_host_display.reset(); + return nullptr; + } + + connectDisplaySignals(widget); + + if (!s_host_display->MakeRenderContextCurrent()) + { + Console.Error("Failed to make render context current"); + releaseHostDisplay(); + return nullptr; + } + + if (!s_host_display->InitializeRenderDevice(StringUtil::wxStringToUTF8String(EmuFolders::Cache.ToString()), false) || + !ImGuiManager::Initialize()) + { + Console.Error("Failed to initialize device/imgui"); + releaseHostDisplay(); + return nullptr; + } + + g_gs_window_info = s_host_display->GetWindowInfo(); + + return s_host_display.get(); +} + +void EmuThread::releaseHostDisplay() +{ + ImGuiManager::Shutdown(); + + if (s_host_display) + { + s_host_display->DestroyRenderSurface(); + s_host_display->DestroyRenderDevice(); + } + + g_gs_window_info = WindowInfo(); + + emit onDestroyDisplayRequested(); + + s_host_display.reset(); +} + +HostDisplay* Host::GetHostDisplay() +{ + return s_host_display.get(); +} + +HostDisplay* Host::AcquireHostDisplay(HostDisplay::RenderAPI api) +{ + return g_emu_thread->acquireHostDisplay(api); +} + +void Host::ReleaseHostDisplay() +{ + g_emu_thread->releaseHostDisplay(); +} + +bool Host::BeginPresentFrame(bool frame_skip) +{ + return s_host_display->BeginPresent(frame_skip); +} + +void Host::EndPresentFrame() +{ + ImGuiManager::RenderOSD(); + s_host_display->EndPresent(); + ImGuiManager::NewFrame(); +} + +void Host::ResizeHostDisplay(u32 new_window_width, u32 new_window_height, float new_window_scale) +{ + s_host_display->ResizeRenderWindow(new_window_width, new_window_height, new_window_scale); + ImGuiManager::WindowResized(); +} + +void Host::RequestResizeHostDisplay(s32 width, s32 height) +{ + g_emu_thread->onResizeDisplayRequested(width, height); +} + +void Host::UpdateHostDisplay() +{ + g_emu_thread->updateDisplay(); + ImGuiManager::WindowResized(); +} + +void Host::OnVMStarting() +{ + g_emu_thread->stopBackgroundControllerPollTimer(); + emit g_emu_thread->onVMStarting(); +} + +void Host::OnVMStarted() +{ + emit g_emu_thread->onVMStarted(); +} + +void Host::OnVMDestroyed() +{ + emit g_emu_thread->onVMStopped(); + g_emu_thread->startBackgroundControllerPollTimer(); +} + +void Host::OnVMPaused() +{ + g_emu_thread->startBackgroundControllerPollTimer(); + emit g_emu_thread->onVMPaused(); +} + +void Host::OnVMResumed() +{ + // exit the event loop when we eventually return + g_emu_thread->getEventLoop()->quit(); + g_emu_thread->stopBackgroundControllerPollTimer(); + emit g_emu_thread->onVMResumed(); +} + +void Host::OnGameChanged(const std::string& disc_path, const std::string& game_serial, const std::string& game_name, + u32 game_crc) +{ + emit g_emu_thread->onGameChanged(QString::fromStdString(disc_path), QString::fromStdString(game_serial), + QString::fromStdString(game_name), game_crc); +} + +void Host::PumpMessagesOnCPUThread() +{ + g_emu_thread->getEventLoop()->processEvents(QEventLoop::AllEvents); +} + +ScopedVMPause::ScopedVMPause(bool was_paused) + : m_was_paused(was_paused) +{ + if (!m_was_paused) + g_emu_thread->setVMPaused(true); +} + +ScopedVMPause::~ScopedVMPause() +{ + if (m_was_paused) + g_emu_thread->setVMPaused(false); +} + +alignas(16) static SysMtgsThread s_mtgs_thread; + +SysMtgsThread& GetMTGS() +{ + return s_mtgs_thread; +} + +// ------------------------------------------------------------------------ +// Hotkeys +// ------------------------------------------------------------------------ + +BEGIN_HOTKEY_LIST(g_host_hotkeys) +DEFINE_HOTKEY("Screenshot", "General", "Save Screenshot", [](bool pressed) { + if (!pressed) + { + // TODO + } +}) +DEFINE_HOTKEY("TogglePause", "System", "Toggle Pause", [](bool pressed) { + if (!pressed) + g_emu_thread->setVMPaused(VMManager::GetState() != VMState::Paused); +}) +DEFINE_HOTKEY("ToggleFullscreen", "General", "Toggle Fullscreen", [](bool pressed) { + if (!pressed) + g_emu_thread->toggleFullscreen(); +}) +END_HOTKEY_LIST() diff --git a/pcsx2-qt/EmuThread.h b/pcsx2-qt/EmuThread.h new file mode 100644 index 000000000..04da15a10 --- /dev/null +++ b/pcsx2-qt/EmuThread.h @@ -0,0 +1,144 @@ +/* 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 . + */ + +#pragma once +#include "pcsx2/Host.h" +#include "pcsx2/HostDisplay.h" +#include "pcsx2/Frontend/InputManager.h" +#include +#include +#include +#include +#include +#include +#include +#include +#include + +class DisplayWidget; +struct VMBootParameters; + +class EmuThread : public QThread +{ + Q_OBJECT + +public: + explicit EmuThread(QThread* ui_thread); + ~EmuThread(); + + static void start(); + static void stop(); + + __fi QEventLoop* getEventLoop() const { return m_event_loop; } + + bool isOnEmuThread() const; + + /// Called back from the GS thread when the display state changes (e.g. fullscreen, render to main). + HostDisplay* acquireHostDisplay(HostDisplay::RenderAPI api); + void releaseHostDisplay(); + void updateDisplay(); + + void startBackgroundControllerPollTimer(); + void stopBackgroundControllerPollTimer(); + +public Q_SLOTS: + void startVM(std::shared_ptr boot_params); + void resetVM(); + void setVMPaused(bool paused); + void shutdownVM(bool allow_save_to_state = true, bool blocking = false); + void loadState(const QString& filename); + void loadStateFromSlot(qint32 slot); + void saveState(const QString& filename); + void saveStateToSlot(qint32 slot); + void toggleFullscreen(); + void setFullscreen(bool fullscreen); + void applySettings(); + void toggleSoftwareRendering(); + void switchRenderer(GSRendererType renderer); + void changeDisc(const QString& path); + void reloadPatches(); + void reloadInputSources(); + void reloadInputBindings(); + void requestDisplaySize(float scale); + void enumerateInputDevices(); + void enumerateVibrationMotors(); + +Q_SIGNALS: + DisplayWidget* onCreateDisplayRequested(bool fullscreen, bool render_to_main); + DisplayWidget* onUpdateDisplayRequested(bool fullscreen, bool render_to_main); + void onResizeDisplayRequested(qint32 width, qint32 height); + void onDestroyDisplayRequested(); + void onVMStarting(); + void onVMStarted(); + void onVMPaused(); + void onVMResumed(); + void onVMStopped(); + void onGameChanged(const QString& path, const QString& serial, const QString& name, quint32 crc); + void onInputDevicesEnumerated(const QList>& devices); + void onInputDeviceConnected(const QString& identifier, const QString& device_name); + void onInputDeviceDisconnected(const QString& identifier); + void onVibrationMotorsEnumerated(const QList& motors); + +protected: + void run(); + +private: + static constexpr u32 BACKGROUND_CONTROLLER_POLLING_INTERVAL = + 100; /// Interval at which the controllers are polled when the system is not active. + + void connectDisplaySignals(DisplayWidget* widget); + void destroyVM(); + void executeVM(); + void checkForSettingChanges(); + + void createBackgroundControllerPollTimer(); + void destroyBackgroundControllerPollTimer(); + +private Q_SLOTS: + void stopInThread(); + void doBackgroundControllerPoll(); + void onDisplayWindowMouseMoveEvent(int x, int y); + void onDisplayWindowMouseButtonEvent(int button, bool pressed); + void onDisplayWindowMouseWheelEvent(const QPoint& delta_angle); + void onDisplayWindowResized(int width, int height, float scale); + void onDisplayWindowFocused(); + void onDisplayWindowKeyEvent(int key, int mods, bool pressed); + +private: + QThread* m_ui_thread; + QSemaphore m_started_semaphore; + QEventLoop* m_event_loop = nullptr; + QTimer* m_background_controller_polling_timer = nullptr; + + std::atomic_bool m_shutdown_flag{false}; + + bool m_is_rendering_to_main = false; + bool m_is_fullscreen = false; +}; + +/// +/// Helper class to pause/unpause the emulation thread. +/// +class ScopedVMPause +{ +public: + ScopedVMPause(bool was_paused); + ~ScopedVMPause(); + +private: + bool m_was_paused; +}; + +extern EmuThread* g_emu_thread; diff --git a/pcsx2-qt/GameList/GameListModel.cpp b/pcsx2-qt/GameList/GameListModel.cpp new file mode 100644 index 000000000..663cec25f --- /dev/null +++ b/pcsx2-qt/GameList/GameListModel.cpp @@ -0,0 +1,490 @@ +/* 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 "GameListModel.h" +#include "common/FileSystem.h" +#include "common/StringUtil.h" +#include +#include +#include +#include +#include + +static constexpr std::array s_column_names = { + {"Type", "Code", "Title", "File Title", "CRC", "Size", "Region", "Compatibility", "Cover"}}; + +static constexpr int COVER_ART_WIDTH = 350; +static constexpr int COVER_ART_HEIGHT = 512; +static constexpr int COVER_ART_SPACING = 32; + +static int DPRScale(int size, float dpr) +{ + return static_cast(static_cast(size) * dpr); +} + +static int DPRUnscale(int size, float dpr) +{ + return static_cast(static_cast(size) / dpr); +} + +static void resizeAndPadPixmap(QPixmap* pm, int expected_width, int expected_height, float dpr) +{ + const int dpr_expected_width = DPRScale(expected_width, dpr); + const int dpr_expected_height = DPRScale(expected_height, dpr); + if (pm->width() == dpr_expected_width && pm->height() == dpr_expected_height) + return; + + *pm = pm->scaled(dpr_expected_width, dpr_expected_height, Qt::KeepAspectRatio, Qt::SmoothTransformation); + if (pm->width() == dpr_expected_width && pm->height() == dpr_expected_height) + return; + + // QPainter works in unscaled coordinates. + int xoffs = 0; + int yoffs = 0; + if (pm->width() < dpr_expected_width) + xoffs = DPRUnscale((dpr_expected_width - pm->width()) / 2, dpr); + if (pm->height() < dpr_expected_height) + yoffs = DPRUnscale((dpr_expected_height - pm->height()) / 2, dpr); + + QPixmap padded_image(dpr_expected_width, dpr_expected_height); + padded_image.setDevicePixelRatio(dpr); + padded_image.fill(Qt::transparent); + QPainter painter; + if (painter.begin(&padded_image)) + { + painter.setCompositionMode(QPainter::CompositionMode_Source); + painter.drawPixmap(xoffs, yoffs, *pm); + painter.setCompositionMode(QPainter::CompositionMode_Destination); + painter.fillRect(padded_image.rect(), QColor(0, 0, 0, 0)); + painter.end(); + } + + *pm = padded_image; +} + +static QPixmap createPlaceholderImage(const QPixmap& placeholder_pixmap, int width, int height, float scale, + const std::string& title) +{ + const float dpr = qApp->devicePixelRatio(); + QPixmap pm(placeholder_pixmap.copy()); + pm.setDevicePixelRatio(dpr); + if (pm.isNull()) + return QPixmap(); + + resizeAndPadPixmap(&pm, width, height, dpr); + QPainter painter; + if (painter.begin(&pm)) + { + QFont font; + font.setPointSize(std::max(static_cast(32.0f * scale), 1)); + painter.setFont(font); + painter.setPen(Qt::white); + + const QRect text_rc(0, 0, static_cast(static_cast(width)), + static_cast(static_cast(height))); + painter.drawText(text_rc, Qt::AlignCenter | Qt::TextWordWrap, QString::fromStdString(title)); + painter.end(); + } + + return pm; +} + +std::optional GameListModel::getColumnIdForName(std::string_view name) +{ + for (int column = 0; column < Column_Count; column++) + { + if (name == s_column_names[column]) + return static_cast(column); + } + + return std::nullopt; +} + +const char* GameListModel::getColumnName(Column col) +{ + return s_column_names[static_cast(col)]; +} + +GameListModel::GameListModel(QObject* parent /* = nullptr */) + : QAbstractTableModel(parent) +{ + loadCommonImages(); + setColumnDisplayNames(); +} +GameListModel::~GameListModel() = default; + +void GameListModel::setCoverScale(float scale) +{ + if (m_cover_scale == scale) + return; + + m_cover_pixmap_cache.clear(); + m_cover_scale = scale; +} + +void GameListModel::refreshCovers() +{ + m_cover_pixmap_cache.clear(); + refresh(); +} + +int GameListModel::getCoverArtWidth() const +{ + return std::max(static_cast(static_cast(COVER_ART_WIDTH) * m_cover_scale), 1); +} + +int GameListModel::getCoverArtHeight() const +{ + return std::max(static_cast(static_cast(COVER_ART_HEIGHT) * m_cover_scale), 1); +} + +int GameListModel::getCoverArtSpacing() const +{ + return std::max(static_cast(static_cast(COVER_ART_SPACING) * m_cover_scale), 1); +} + +int GameListModel::rowCount(const QModelIndex& parent) const +{ + if (parent.isValid()) + return 0; + + return static_cast(GameList::GetEntryCount()); +} + +int GameListModel::columnCount(const QModelIndex& parent) const +{ + if (parent.isValid()) + return 0; + + return Column_Count; +} + +QVariant GameListModel::data(const QModelIndex& index, int role) const +{ + if (!index.isValid()) + return {}; + + const int row = index.row(); + if (row < 0 || row >= static_cast(GameList::GetEntryCount())) + return {}; + + const auto lock = GameList::GetLock(); + const GameList::Entry* ge = GameList::GetEntryByIndex(row); + if (!ge) + return {}; + + switch (role) + { + case Qt::DisplayRole: + { + switch (index.column()) + { + case Column_Serial: + return QString::fromStdString(ge->serial); + + case Column_Title: + return QString::fromStdString(ge->title); + + case Column_FileTitle: + { + const std::string_view file_title(FileSystem::GetFileTitleFromPath(ge->path)); + return QString::fromUtf8(file_title.data(), static_cast(file_title.length())); + } + + case Column_CRC: + return QStringLiteral("%1").arg(ge->crc, 8, 16, QChar('0')); + + case Column_Size: + return QString("%1 MB").arg(static_cast(ge->total_size) / 1048576.0, 0, 'f', 2); + + case Column_Cover: + { + if (m_show_titles_for_covers) + return QString::fromStdString(ge->title); + else + return {}; + } + + default: + return {}; + } + } + + case Qt::InitialSortOrderRole: + { + switch (index.column()) + { + case Column_Type: + return static_cast(ge->type); + + case Column_Serial: + return QString::fromStdString(ge->serial); + + case Column_Title: + case Column_Cover: + return QString::fromStdString(ge->title); + + case Column_FileTitle: + { + const std::string_view file_title(FileSystem::GetFileTitleFromPath(ge->path)); + return QString::fromUtf8(file_title.data(), static_cast(file_title.length())); + } + + case Column_CRC: + return static_cast(ge->crc); + + case Column_Region: + return static_cast(ge->region); + + case Column_Compatibility: + return static_cast(ge->compatibility_rating); + + case Column_Size: + return static_cast(ge->total_size); + + default: + return {}; + } + } + + case Qt::DecorationRole: + { + switch (index.column()) + { + case Column_Type: + { + switch (ge->type) + { + case GameList::EntryType::PS1Disc: + case GameList::EntryType::PS2Disc: + // return ((ge->settings.GetUserSettingsCount() > 0) ? m_type_disc_with_settings_pixmap : // m_type_disc_pixmap); + return m_type_disc_pixmap; + case GameList::EntryType::Playlist: + return m_type_playlist_pixmap; + case GameList::EntryType::ELF: + default: + return m_type_exe_pixmap; + } + } + + case Column_Region: + { + switch (ge->region) + { + case GameList::Region::NTSC_J: + return m_region_jp_pixmap; + case GameList::Region::NTSC_UC: + return m_region_us_pixmap; + case GameList::Region::PAL: + return m_region_eu_pixmap; + case GameList::Region::Other: + default: + return m_region_other_pixmap; + } + } + + case Column_Compatibility: + { + return m_compatibiliy_pixmaps[static_cast( + (static_cast(ge->compatibility_rating) >= GameList::CompatibilityRatingCount) ? + GameList::CompatibilityRating::Unknown : + ge->compatibility_rating)]; + } + + case Column_Cover: + { + auto it = m_cover_pixmap_cache.find(ge->path); + if (it != m_cover_pixmap_cache.end()) + return it->second; + + QPixmap image; + std::string path = GameList::GetCoverImagePathForEntry(ge); + if (!path.empty()) + { + const float dpr = qApp->devicePixelRatio(); + image = QPixmap(QString::fromStdString(path)); + if (!image.isNull()) + { + image.setDevicePixelRatio(dpr); + resizeAndPadPixmap(&image, getCoverArtWidth(), getCoverArtHeight(), dpr); + } + } + + if (image.isNull()) + { + image = createPlaceholderImage(m_placeholder_pixmap, getCoverArtWidth(), getCoverArtHeight(), m_cover_scale, + ge->title); + } + + m_cover_pixmap_cache.emplace(ge->path, image); + return image; + } + break; + + default: + return {}; + } + + default: + return {}; + } + } +} + +QVariant GameListModel::headerData(int section, Qt::Orientation orientation, int role) const +{ + if (orientation != Qt::Horizontal || role != Qt::DisplayRole || section < 0 || section >= Column_Count) + return {}; + + return m_column_display_names[section]; +} + +void GameListModel::refresh() +{ + beginResetModel(); + endResetModel(); +} + +bool GameListModel::titlesLessThan(int left_row, int right_row) const +{ + if (left_row < 0 || left_row >= static_cast(GameList::GetEntryCount()) || right_row < 0 || + right_row >= static_cast(GameList::GetEntryCount())) + { + return false; + } + + const GameList::Entry* left = GameList::GetEntryByIndex(left_row); + const GameList::Entry* right = GameList::GetEntryByIndex(right_row); + return (StringUtil::Strcasecmp(left->title.c_str(), right->title.c_str()) < 0); +} + +bool GameListModel::lessThan(const QModelIndex& left_index, const QModelIndex& right_index, int column) const +{ + if (!left_index.isValid() || !right_index.isValid()) + return false; + + const int left_row = left_index.row(); + const int right_row = right_index.row(); + if (left_row < 0 || left_row >= static_cast(GameList::GetEntryCount()) || right_row < 0 || + right_row >= static_cast(GameList::GetEntryCount())) + { + return false; + } + + const auto lock = GameList::GetLock(); + const GameList::Entry* left = GameList::GetEntryByIndex(left_row); + const GameList::Entry* right = GameList::GetEntryByIndex(right_row); + if (!left || !right) + return false; + + switch (column) + { + case Column_Type: + { + if (left->type == right->type) + return titlesLessThan(left_row, right_row); + + return static_cast(left->type) < static_cast(right->type); + } + + case Column_Serial: + { + if (left->serial == right->serial) + return titlesLessThan(left_row, right_row); + return (StringUtil::Strcasecmp(left->serial.c_str(), right->serial.c_str()) < 0); + } + + case Column_Title: + { + return titlesLessThan(left_row, right_row); + } + + case Column_FileTitle: + { + const std::string_view file_title_left(FileSystem::GetFileTitleFromPath(left->path)); + const std::string_view file_title_right(FileSystem::GetFileTitleFromPath(right->path)); + if (file_title_left == file_title_right) + return titlesLessThan(left_row, right_row); + + const std::size_t smallest = std::min(file_title_left.size(), file_title_right.size()); + return (StringUtil::Strncasecmp(file_title_left.data(), file_title_right.data(), smallest) < 0); + } + + case Column_Region: + { + if (left->region == right->region) + return titlesLessThan(left_row, right_row); + return (static_cast(left->region) < static_cast(right->region)); + } + + case Column_Compatibility: + { + if (left->compatibility_rating == right->compatibility_rating) + return titlesLessThan(left_row, right_row); + + return (static_cast(left->compatibility_rating) < static_cast(right->compatibility_rating)); + } + + case Column_Size: + { + if (left->total_size == right->total_size) + return titlesLessThan(left_row, right_row); + + return (left->total_size < right->total_size); + } + + case Column_CRC: + { + if (left->crc == right->crc) + return titlesLessThan(left_row, right_row); + + return (left->crc < right->crc); + } + + default: + return false; + } +} + +void GameListModel::loadCommonImages() +{ + m_type_disc_pixmap = QIcon(QStringLiteral(":/icons/media-optical-24.png")).pixmap(QSize(24, 24)); + m_type_disc_with_settings_pixmap = QIcon(QStringLiteral(":/icons/media-optical-gear-24.png")).pixmap(QSize(24, 24)); + m_type_exe_pixmap = QIcon(QStringLiteral(":/icons/applications-system-24.png")).pixmap(QSize(24, 24)); + m_type_playlist_pixmap = QIcon(QStringLiteral(":/icons/address-book-new-22.png")).pixmap(QSize(22, 22)); + m_region_eu_pixmap = QIcon(QStringLiteral(":/icons/flag-eu.png")).pixmap(QSize(42, 30)); + m_region_jp_pixmap = QIcon(QStringLiteral(":/icons/flag-jp.png")).pixmap(QSize(42, 30)); + m_region_us_pixmap = QIcon(QStringLiteral(":/icons/flag-us.png")).pixmap(QSize(42, 30)); + m_region_other_pixmap = QIcon(QStringLiteral(":/icons/flag-other.png")).pixmap(QSize(42, 30)); + + for (u32 i = 1; i < GameList::CompatibilityRatingCount; i++) + m_compatibiliy_pixmaps[i].load(QStringLiteral(":/icons/star-%1.png").arg(i - 1)); + + m_placeholder_pixmap.load(QString::fromStdString(Path::CombineStdString(EmuFolders::Resources, "cover-placeholder.png"))); +} + +void GameListModel::setColumnDisplayNames() +{ + m_column_display_names[Column_Type] = tr("Type"); + m_column_display_names[Column_Serial] = tr("Code"); + m_column_display_names[Column_Title] = tr("Title"); + m_column_display_names[Column_FileTitle] = tr("File Title"); + m_column_display_names[Column_CRC] = tr("CRC"); + m_column_display_names[Column_Size] = tr("Size"); + m_column_display_names[Column_Region] = tr("Region"); + m_column_display_names[Column_Compatibility] = tr("Compatibility"); +} diff --git a/pcsx2-qt/GameList/GameListModel.h b/pcsx2-qt/GameList/GameListModel.h new file mode 100644 index 000000000..09a3396b7 --- /dev/null +++ b/pcsx2-qt/GameList/GameListModel.h @@ -0,0 +1,97 @@ +/* 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 . + */ + +#pragma once +#include "pcsx2/Frontend/GameList.h" +#include +#include +#include +#include +#include +#include + +class GameListModel final : public QAbstractTableModel +{ + Q_OBJECT + +public: + enum Column : int + { + Column_Type, + Column_Serial, + Column_Title, + Column_FileTitle, + Column_CRC, + Column_Size, + Column_Region, + Column_Compatibility, + Column_Cover, + + Column_Count + }; + + static std::optional getColumnIdForName(std::string_view name); + static const char* getColumnName(Column col); + + GameListModel(QObject* parent = nullptr); + ~GameListModel(); + + int rowCount(const QModelIndex& parent = QModelIndex()) const override; + int columnCount(const QModelIndex& parent = QModelIndex()) const override; + QVariant data(const QModelIndex& index, int role = Qt::DisplayRole) const override; + QVariant headerData(int section, Qt::Orientation orientation, int role = Qt::DisplayRole) const override; + + __fi const QString& getColumnDisplayName(int column) { return m_column_display_names[column]; } + + void refresh(); + + bool titlesLessThan(int left_row, int right_row) const; + + bool lessThan(const QModelIndex& left_index, const QModelIndex& right_index, int column) const; + + bool getShowCoverTitles() const { return m_show_titles_for_covers; } + void setShowCoverTitles(bool enabled) { m_show_titles_for_covers = enabled; } + + float getCoverScale() const { return m_cover_scale; } + void setCoverScale(float scale); + int getCoverArtWidth() const; + int getCoverArtHeight() const; + int getCoverArtSpacing() const; + void refreshCovers(); + +private: + void loadCommonImages(); + void setColumnDisplayNames(); + + float m_cover_scale = 1.0f; + bool m_show_titles_for_covers = false; + + std::array m_column_display_names; + + QPixmap m_type_disc_pixmap; + QPixmap m_type_disc_with_settings_pixmap; + QPixmap m_type_exe_pixmap; + QPixmap m_type_playlist_pixmap; + + QPixmap m_region_jp_pixmap; + QPixmap m_region_eu_pixmap; + QPixmap m_region_us_pixmap; + QPixmap m_region_other_pixmap; + + QPixmap m_placeholder_pixmap; + + std::array(GameList::CompatibilityRatingCount)> m_compatibiliy_pixmaps; + mutable std::unordered_map m_cover_pixmap_cache; +}; \ No newline at end of file diff --git a/pcsx2-qt/GameList/GameListRefreshThread.cpp b/pcsx2-qt/GameList/GameListRefreshThread.cpp new file mode 100644 index 000000000..255dc7ef4 --- /dev/null +++ b/pcsx2-qt/GameList/GameListRefreshThread.cpp @@ -0,0 +1,130 @@ +/* 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 "common/ProgressCallback.h" +#include "common/Timer.h" +#include "pcsx2/Frontend/GameList.h" + +#include "GameListRefreshThread.h" + +#include + +// Limit UI update times to 4 per second, so we don't spend longer redrawing the UI than scanning +static constexpr float UI_UPDATE_INTERVAL = 0.25f; + +AsyncRefreshProgressCallback::AsyncRefreshProgressCallback(GameListRefreshThread* parent) + : m_parent(parent) +{ +} + +void AsyncRefreshProgressCallback::Cancel() +{ + // Not atomic, but we don't need to cancel immediately. + m_cancelled = true; +} + +void AsyncRefreshProgressCallback::SetStatusText(const char* text) +{ + QString new_text(QString::fromUtf8(text)); + if (new_text == m_status_text) + return; + + m_status_text = new_text; + fireUpdate(); +} + +void AsyncRefreshProgressCallback::SetProgressRange(u32 range) +{ + BaseProgressCallback::SetProgressRange(range); + if (static_cast(m_progress_range) == m_last_range) + return; + + m_last_range = static_cast(m_progress_range); + fireUpdate(); +} + +void AsyncRefreshProgressCallback::SetProgressValue(u32 value) +{ + BaseProgressCallback::SetProgressValue(value); + if (static_cast(m_progress_value) == m_last_value) + return; + + m_last_value = static_cast(m_progress_value); + fireUpdate(); +} + +void AsyncRefreshProgressCallback::SetTitle(const char* title) {} + +void AsyncRefreshProgressCallback::DisplayError(const char* message) +{ + QMessageBox::critical(nullptr, QStringLiteral("Error"), QString::fromUtf8(message)); +} + +void AsyncRefreshProgressCallback::DisplayWarning(const char* message) +{ + QMessageBox::warning(nullptr, QStringLiteral("Warning"), QString::fromUtf8(message)); +} + +void AsyncRefreshProgressCallback::DisplayInformation(const char* message) +{ + QMessageBox::information(nullptr, QStringLiteral("Information"), QString::fromUtf8(message)); +} + +void AsyncRefreshProgressCallback::DisplayDebugMessage(const char* message) +{ + qDebug() << message; +} + +void AsyncRefreshProgressCallback::ModalError(const char* message) +{ + QMessageBox::critical(nullptr, QStringLiteral("Error"), QString::fromUtf8(message)); +} + +bool AsyncRefreshProgressCallback::ModalConfirmation(const char* message) +{ + return QMessageBox::question(nullptr, QStringLiteral("Question"), QString::fromUtf8(message)) == QMessageBox::Yes; +} + +void AsyncRefreshProgressCallback::ModalInformation(const char* message) +{ + QMessageBox::information(nullptr, QStringLiteral("Information"), QString::fromUtf8(message)); +} + +void AsyncRefreshProgressCallback::fireUpdate() +{ + m_parent->refreshProgress(m_status_text, m_last_value, m_last_range); +} + +GameListRefreshThread::GameListRefreshThread(bool invalidate_cache) + : QThread() + , m_progress(this) + , m_invalidate_cache(invalidate_cache) +{ +} + +GameListRefreshThread::~GameListRefreshThread() = default; + +void GameListRefreshThread::cancel() +{ + m_progress.Cancel(); +} + +void GameListRefreshThread::run() +{ + GameList::Refresh(m_invalidate_cache, &m_progress); + emit refreshComplete(); +} diff --git a/pcsx2-qt/GameList/GameListRefreshThread.h b/pcsx2-qt/GameList/GameListRefreshThread.h new file mode 100644 index 000000000..f98f03168 --- /dev/null +++ b/pcsx2-qt/GameList/GameListRefreshThread.h @@ -0,0 +1,75 @@ +/* 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 . + */ + +#pragma once + +#include +#include + +#include "common/ProgressCallback.h" +#include "common/Timer.h" + +class GameListRefreshThread; + +class AsyncRefreshProgressCallback : public BaseProgressCallback +{ +public: + AsyncRefreshProgressCallback(GameListRefreshThread* parent); + + void Cancel(); + + void SetStatusText(const char* text) override; + void SetProgressRange(u32 range) override; + void SetProgressValue(u32 value) override; + void SetTitle(const char* title) override; + void DisplayError(const char* message) override; + void DisplayWarning(const char* message) override; + void DisplayInformation(const char* message) override; + void DisplayDebugMessage(const char* message) override; + void ModalError(const char* message) override; + bool ModalConfirmation(const char* message) override; + void ModalInformation(const char* message) override; + +private: + void fireUpdate(); + + GameListRefreshThread* m_parent; + Common::Timer m_last_update_time; + QString m_status_text; + int m_last_range = 1; + int m_last_value = 0; +}; + +class GameListRefreshThread final : public QThread +{ + Q_OBJECT + +public: + GameListRefreshThread(bool invalidate_cache); + ~GameListRefreshThread(); + + void cancel(); + +Q_SIGNALS: + void refreshProgress(const QString& status, int current, int total); + void refreshComplete(); + +protected: + void run(); + +private: + AsyncRefreshProgressCallback m_progress; + bool m_invalidate_cache; +}; diff --git a/pcsx2-qt/GameList/GameListWidget.cpp b/pcsx2-qt/GameList/GameListWidget.cpp new file mode 100644 index 000000000..5aefe8ee0 --- /dev/null +++ b/pcsx2-qt/GameList/GameListWidget.cpp @@ -0,0 +1,457 @@ +/* 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 "common/StringUtil.h" + +#include "pcsx2/Frontend/GameList.h" + +#include +#include +#include +#include +#include +#include + +#include "GameListModel.h" +#include "GameListRefreshThread.h" +#include "GameListWidget.h" +#include "QtHost.h" +#include "QtUtils.h" + +class GameListSortModel final : public QSortFilterProxyModel +{ +public: + explicit GameListSortModel(GameListModel* parent) + : QSortFilterProxyModel(parent) + , m_model(parent) + { + } + + bool filterAcceptsRow(int source_row, const QModelIndex& source_parent) const override + { + // TODO: Search + return QSortFilterProxyModel::filterAcceptsRow(source_row, source_parent); + } + + bool lessThan(const QModelIndex& source_left, const QModelIndex& source_right) const override + { + return m_model->lessThan(source_left, source_right, source_left.column()); + } + +private: + GameListModel* m_model; +}; + +GameListWidget::GameListWidget(QWidget* parent /* = nullptr */) + : QStackedWidget(parent) +{ +} + +GameListWidget::~GameListWidget() = default; + +void GameListWidget::initialize() +{ + m_model = new GameListModel(this); + m_model->setCoverScale(QtHost::GetBaseFloatSettingValue("UI", "GameListCoverArtScale", 0.45f)); + m_model->setShowCoverTitles(QtHost::GetBaseBoolSettingValue("UI", "GameListShowCoverTitles", true)); + m_model->setCoverScale(0.45f); + m_model->setShowCoverTitles(true); + + m_sort_model = new GameListSortModel(m_model); + m_sort_model->setSourceModel(m_model); + m_table_view = new QTableView(this); + m_table_view->setModel(m_sort_model); + m_table_view->setSortingEnabled(true); + m_table_view->setSelectionMode(QAbstractItemView::SingleSelection); + m_table_view->setSelectionBehavior(QAbstractItemView::SelectRows); + m_table_view->setContextMenuPolicy(Qt::CustomContextMenu); + m_table_view->setAlternatingRowColors(true); + m_table_view->setShowGrid(false); + m_table_view->setCurrentIndex({}); + m_table_view->horizontalHeader()->setHighlightSections(false); + m_table_view->horizontalHeader()->setContextMenuPolicy(Qt::CustomContextMenu); + m_table_view->verticalHeader()->hide(); + m_table_view->setVerticalScrollBarPolicy(Qt::ScrollBarAlwaysOn); + + loadTableViewColumnVisibilitySettings(); + loadTableViewColumnSortSettings(); + + connect(m_table_view->selectionModel(), &QItemSelectionModel::currentChanged, this, + &GameListWidget::onSelectionModelCurrentChanged); + connect(m_table_view, &QTableView::activated, this, &GameListWidget::onTableViewItemActivated); + connect(m_table_view, &QTableView::customContextMenuRequested, this, + &GameListWidget::onTableViewContextMenuRequested); + connect(m_table_view->horizontalHeader(), &QHeaderView::customContextMenuRequested, this, + &GameListWidget::onTableViewHeaderContextMenuRequested); + connect(m_table_view->horizontalHeader(), &QHeaderView::sortIndicatorChanged, this, + &GameListWidget::onTableViewHeaderSortIndicatorChanged); + + insertWidget(0, m_table_view); + + m_list_view = new GameListGridListView(this); + m_list_view->setModel(m_sort_model); + m_list_view->setModelColumn(GameListModel::Column_Cover); + m_list_view->setSelectionMode(QAbstractItemView::ExtendedSelection); + m_list_view->setViewMode(QListView::IconMode); + m_list_view->setResizeMode(QListView::Adjust); + m_list_view->setUniformItemSizes(true); + m_list_view->setItemAlignment(Qt::AlignHCenter); + m_list_view->setContextMenuPolicy(Qt::CustomContextMenu); + m_list_view->setFrameStyle(QFrame::NoFrame); + m_list_view->setSpacing(m_model->getCoverArtSpacing()); + updateListFont(); + + connect(m_list_view->selectionModel(), &QItemSelectionModel::currentChanged, this, + &GameListWidget::onSelectionModelCurrentChanged); + connect(m_list_view, &GameListGridListView::zoomIn, this, &GameListWidget::gridZoomIn); + connect(m_list_view, &GameListGridListView::zoomOut, this, &GameListWidget::gridZoomOut); + connect(m_list_view, &QListView::activated, this, &GameListWidget::onListViewItemActivated); + connect(m_list_view, &QListView::customContextMenuRequested, this, &GameListWidget::onListViewContextMenuRequested); + + insertWidget(1, m_list_view); + + if (QtHost::GetBaseBoolSettingValue("UI", "GameListGridView", false)) + setCurrentIndex(1); + else + setCurrentIndex(0); + + resizeTableViewColumnsToFit(); +} + +bool GameListWidget::isShowingGameList() const +{ + return currentIndex() == 0; +} + +bool GameListWidget::isShowingGameGrid() const +{ + return currentIndex() == 1; +} + +bool GameListWidget::getShowGridCoverTitles() const +{ + return m_model->getShowCoverTitles(); +} + +void GameListWidget::refresh(bool invalidate_cache) +{ + if (m_refresh_thread) + { + m_refresh_thread->cancel(); + m_refresh_thread->wait(); + QApplication::processEvents(QEventLoop::ExcludeUserInputEvents); + pxAssertRel(!m_refresh_thread, "Game list thread should be unreferenced by now"); + } + + m_refresh_thread = new GameListRefreshThread(invalidate_cache); + connect(m_refresh_thread, &GameListRefreshThread::refreshProgress, this, &GameListWidget::onRefreshProgress, + Qt::QueuedConnection); + connect(m_refresh_thread, &GameListRefreshThread::refreshComplete, this, &GameListWidget::onRefreshComplete, + Qt::QueuedConnection); + m_refresh_thread->start(); +} + +void GameListWidget::onRefreshProgress(const QString& status, int current, int total) +{ + m_model->refresh(); + emit refreshProgress(status, current, total); +} + +void GameListWidget::onRefreshComplete() +{ + m_model->refresh(); + emit refreshComplete(); + + pxAssertRel(m_refresh_thread, "Has a refresh thread"); + m_refresh_thread->wait(); + delete m_refresh_thread; + m_refresh_thread = nullptr; +} + +void GameListWidget::onSelectionModelCurrentChanged(const QModelIndex& current, const QModelIndex& previous) +{ + const QModelIndex source_index = m_sort_model->mapToSource(current); + if (!source_index.isValid() || source_index.row() >= static_cast(GameList::GetEntryCount())) + return; + + emit selectionChanged(); +} + +void GameListWidget::onTableViewItemActivated(const QModelIndex& index) +{ + const QModelIndex source_index = m_sort_model->mapToSource(index); + if (!source_index.isValid() || source_index.row() >= static_cast(GameList::GetEntryCount())) + return; + + emit entryActivated(); +} + +void GameListWidget::onTableViewContextMenuRequested(const QPoint& point) +{ + emit entryContextMenuRequested(m_table_view->mapToGlobal(point)); +} + +void GameListWidget::onListViewItemActivated(const QModelIndex& index) +{ + const QModelIndex source_index = m_sort_model->mapToSource(index); + if (!source_index.isValid() || source_index.row() >= static_cast(GameList::GetEntryCount())) + return; + + emit entryActivated(); +} + +void GameListWidget::onListViewContextMenuRequested(const QPoint& point) +{ + emit entryContextMenuRequested(m_list_view->mapToGlobal(point)); +} + +void GameListWidget::onTableViewHeaderContextMenuRequested(const QPoint& point) +{ + QMenu menu; + + for (int column = 0; column < GameListModel::Column_Count; column++) + { + if (column == GameListModel::Column_Cover) + continue; + + QAction* action = menu.addAction(m_model->getColumnDisplayName(column)); + action->setCheckable(true); + action->setChecked(!m_table_view->isColumnHidden(column)); + connect(action, &QAction::toggled, [this, column](bool enabled) { + m_table_view->setColumnHidden(column, !enabled); + saveTableViewColumnVisibilitySettings(column); + resizeTableViewColumnsToFit(); + }); + } + + menu.exec(m_table_view->mapToGlobal(point)); +} + +void GameListWidget::onTableViewHeaderSortIndicatorChanged(int, Qt::SortOrder) +{ + saveTableViewColumnSortSettings(); +} + +void GameListWidget::listZoom(float delta) +{ + static constexpr float MIN_SCALE = 0.1f; + static constexpr float MAX_SCALE = 2.0f; + + const float new_scale = std::clamp(m_model->getCoverScale() + delta, MIN_SCALE, MAX_SCALE); + QtHost::SetBaseFloatSettingValue("UI", "GameListCoverArtScale", new_scale); + m_model->setCoverScale(new_scale); + updateListFont(); + + m_model->refresh(); +} + +void GameListWidget::gridZoomIn() +{ + listZoom(0.05f); +} + +void GameListWidget::gridZoomOut() +{ + listZoom(-0.05f); +} + +void GameListWidget::refreshGridCovers() +{ + m_model->refreshCovers(); +} + +void GameListWidget::showGameList() +{ + if (currentIndex() == 0) + return; + + QtHost::SetBaseBoolSettingValue("UI", "GameListGridView", false); + setCurrentIndex(0); + resizeTableViewColumnsToFit(); +} + +void GameListWidget::showGameGrid() +{ + if (currentIndex() == 1) + return; + + QtHost::SetBaseBoolSettingValue("UI", "GameListGridView", true); + setCurrentIndex(1); +} + +void GameListWidget::setShowCoverTitles(bool enabled) +{ + if (m_model->getShowCoverTitles() == enabled) + return; + + QtHost::SetBaseBoolSettingValue("UI", "GameListShowCoverTitles", enabled); + m_model->setShowCoverTitles(enabled); + if (isShowingGameGrid()) + m_model->refresh(); +} + +void GameListWidget::updateListFont() +{ + QFont font; + font.setPointSizeF(16.0f * m_model->getCoverScale()); + m_list_view->setFont(font); +} + +void GameListWidget::resizeEvent(QResizeEvent* event) +{ + QStackedWidget::resizeEvent(event); + resizeTableViewColumnsToFit(); +} + +void GameListWidget::resizeTableViewColumnsToFit() +{ + QtUtils::ResizeColumnsForTableView(m_table_view, { + 32, // type + 80, // code + -1, // title + -1, // file title + 50, // crc + 80, // size + 50, // region + 100 // compatibility + }); +} + +static std::string getColumnVisibilitySettingsKeyName(int column) +{ + return StringUtil::StdStringFromFormat("Show%s", + GameListModel::getColumnName(static_cast(column))); +} + +void GameListWidget::loadTableViewColumnVisibilitySettings() +{ + static constexpr std::array DEFAULT_VISIBILITY = {{ + true, // type + true, // code + true, // title + false, // file title + false, // crc + true, // size + true, // region + true // compatibility + }}; + + for (int column = 0; column < GameListModel::Column_Count; column++) + { + const bool visible = QtHost::GetBaseBoolSettingValue( + "GameListTableView", getColumnVisibilitySettingsKeyName(column).c_str(), DEFAULT_VISIBILITY[column]); + m_table_view->setColumnHidden(column, !visible); + } +} + +void GameListWidget::saveTableViewColumnVisibilitySettings() +{ + for (int column = 0; column < GameListModel::Column_Count; column++) + { + const bool visible = !m_table_view->isColumnHidden(column); + QtHost::SetBaseBoolSettingValue("GameListTableView", getColumnVisibilitySettingsKeyName(column).c_str(), visible); + } +} + +void GameListWidget::saveTableViewColumnVisibilitySettings(int column) +{ + const bool visible = !m_table_view->isColumnHidden(column); + QtHost::SetBaseBoolSettingValue("GameListTableView", getColumnVisibilitySettingsKeyName(column).c_str(), visible); +} + +void GameListWidget::loadTableViewColumnSortSettings() +{ + const GameListModel::Column DEFAULT_SORT_COLUMN = GameListModel::Column_Type; + const bool DEFAULT_SORT_DESCENDING = false; + + const GameListModel::Column sort_column = + GameListModel::getColumnIdForName(QtHost::GetBaseStringSettingValue("GameListTableView", "SortColumn")) + .value_or(DEFAULT_SORT_COLUMN); + const bool sort_descending = + QtHost::GetBaseBoolSettingValue("GameListTableView", "SortDescending", DEFAULT_SORT_DESCENDING); + m_table_view->sortByColumn(sort_column, sort_descending ? Qt::DescendingOrder : Qt::AscendingOrder); +} + +void GameListWidget::saveTableViewColumnSortSettings() +{ + const int sort_column = m_table_view->horizontalHeader()->sortIndicatorSection(); + const bool sort_descending = (m_table_view->horizontalHeader()->sortIndicatorOrder() == Qt::DescendingOrder); + + if (sort_column >= 0 && sort_column < GameListModel::Column_Count) + { + QtHost::SetBaseStringSettingValue( + "GameListTableView", "SortColumn", GameListModel::getColumnName(static_cast(sort_column))); + } + + QtHost::SetBaseBoolSettingValue("GameListTableView", "SortDescending", sort_descending); +} + +const GameList::Entry* GameListWidget::getSelectedEntry() const +{ + if (currentIndex() == 0) + { + const QItemSelectionModel* selection_model = m_table_view->selectionModel(); + if (!selection_model->hasSelection()) + return nullptr; + + const QModelIndexList selected_rows = selection_model->selectedRows(); + if (selected_rows.empty()) + return nullptr; + + const QModelIndex source_index = m_sort_model->mapToSource(selected_rows[0]); + if (!source_index.isValid()) + return nullptr; + + return GameList::GetEntryByIndex(source_index.row()); + } + else + { + const QItemSelectionModel* selection_model = m_list_view->selectionModel(); + if (!selection_model->hasSelection()) + return nullptr; + + const QModelIndex source_index = m_sort_model->mapToSource(selection_model->currentIndex()); + if (!source_index.isValid()) + return nullptr; + + return GameList::GetEntryByIndex(source_index.row()); + } +} + +GameListGridListView::GameListGridListView(QWidget* parent /*= nullptr*/) + : QListView(parent) +{ +} + +void GameListGridListView::wheelEvent(QWheelEvent* e) +{ + if (e->modifiers() & Qt::ControlModifier) + { + int dy = e->angleDelta().y(); + if (dy != 0) + { + if (dy < 0) + zoomOut(); + else + zoomIn(); + + return; + } + } + + QListView::wheelEvent(e); +} diff --git a/pcsx2-qt/GameList/GameListWidget.h b/pcsx2-qt/GameList/GameListWidget.h new file mode 100644 index 000000000..46567d42a --- /dev/null +++ b/pcsx2-qt/GameList/GameListWidget.h @@ -0,0 +1,111 @@ +/* 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 . + */ + +#pragma once +#include "pcsx2/Frontend/GameList.h" +#include +#include +#include + +Q_DECLARE_METATYPE(const GameList::Entry*); + +class GameListModel; +class GameListSortModel; +class GameListRefreshThread; + +class GameListGridListView : public QListView +{ + Q_OBJECT + +public: + GameListGridListView(QWidget* parent = nullptr); + +Q_SIGNALS: + void zoomOut(); + void zoomIn(); + +protected: + void wheelEvent(QWheelEvent* e); +}; + +class GameListWidget : public QStackedWidget +{ + Q_OBJECT + +public: + GameListWidget(QWidget* parent = nullptr); + ~GameListWidget(); + + __fi GameListModel* getModel() const { return m_model; } + + void initialize(); + + void refresh(bool invalidate_cache); + + bool isShowingGameList() const; + bool isShowingGameGrid() const; + + bool getShowGridCoverTitles() const; + + const GameList::Entry* getSelectedEntry() const; + +Q_SIGNALS: + void refreshProgress(const QString& status, int current, int total); + void refreshComplete(); + + void selectionChanged(); + void entryActivated(); + void entryContextMenuRequested(const QPoint& point); + +private Q_SLOTS: + void onRefreshProgress(const QString& status, int current, int total); + void onRefreshComplete(); + + void onSelectionModelCurrentChanged(const QModelIndex& current, const QModelIndex& previous); + void onTableViewItemActivated(const QModelIndex& index); + void onTableViewContextMenuRequested(const QPoint& point); + void onTableViewHeaderContextMenuRequested(const QPoint& point); + void onTableViewHeaderSortIndicatorChanged(int, Qt::SortOrder); + void onListViewItemActivated(const QModelIndex& index); + void onListViewContextMenuRequested(const QPoint& point); + +public Q_SLOTS: + void showGameList(); + void showGameGrid(); + void setShowCoverTitles(bool enabled); + void gridZoomIn(); + void gridZoomOut(); + void refreshGridCovers(); + +protected: + void resizeEvent(QResizeEvent* event); + +private: + void resizeTableViewColumnsToFit(); + void loadTableViewColumnVisibilitySettings(); + void saveTableViewColumnVisibilitySettings(); + void saveTableViewColumnVisibilitySettings(int column); + void loadTableViewColumnSortSettings(); + void saveTableViewColumnSortSettings(); + void listZoom(float delta); + void updateListFont(); + + GameListModel* m_model = nullptr; + GameListSortModel* m_sort_model = nullptr; + QTableView* m_table_view = nullptr; + GameListGridListView* m_list_view = nullptr; + + GameListRefreshThread* m_refresh_thread = nullptr; +}; diff --git a/pcsx2-qt/Main.cpp b/pcsx2-qt/Main.cpp new file mode 100644 index 000000000..80e286f9c --- /dev/null +++ b/pcsx2-qt/Main.cpp @@ -0,0 +1,264 @@ +/* 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 +#include + +#include "MainWindow.h" +#include "EmuThread.h" +#include "QtHost.h" + +#include "CDVD/CDVD.h" +#include "Frontend/GameList.h" +#include "svnrev.h" + +static void PrintCommandLineVersion() +{ + std::fprintf(stderr, "PCSX2 Version %s\n", GIT_REV); + std::fprintf(stderr, "https://pcsx2.net/\n"); + std::fprintf(stderr, "\n"); +} + +static void PrintCommandLineHelp(const char* progname) +{ + PrintCommandLineVersion(); + std::fprintf(stderr, "Usage: %s [parameters] [--] [boot filename]\n", progname); + std::fprintf(stderr, "\n"); + std::fprintf(stderr, " -help: Displays this information and exits.\n"); + std::fprintf(stderr, " -version: Displays version information and exits.\n"); + std::fprintf(stderr, " -batch: Enables batch mode (exits after powering off).\n"); + std::fprintf(stderr, " -fastboot: Force fast boot for provided filename.\n"); + std::fprintf(stderr, " -slowboot: Force slow boot for provided filename.\n"); + std::fprintf(stderr, " -resume: Load resume save state. If a boot filename is provided,\n" + " that game's resume state will be loaded, otherwise the most\n" + " recent resume save state will be loaded.\n"); + std::fprintf(stderr, " -state : Loads specified save state by index.\n"); + std::fprintf(stderr, " -statefile : Loads state from the specified filename.\n" + " No boot filename is required with this option.\n"); + std::fprintf(stderr, " -fullscreen: Enters fullscreen mode immediately after starting.\n"); + std::fprintf(stderr, " -nofullscreen: Prevents fullscreen mode from triggering if enabled.\n"); + std::fprintf(stderr, " -portable: Forces \"portable mode\", data in same directory.\n"); + std::fprintf(stderr, " -settings : Loads a custom settings configuration from the\n" + " specified filename. Default settings applied if file not found.\n"); + std::fprintf(stderr, " --: Signals that no more arguments will follow and the remaining\n" + " parameters make up the filename. Use when the filename contains\n" + " spaces or starts with a dash.\n"); + std::fprintf(stderr, "\n"); +} + +static std::shared_ptr& AutoBoot(std::shared_ptr& autoboot) +{ + if (!autoboot) + { + autoboot = std::make_shared(); + autoboot->source_type = CDVD_SourceType::NoDisc; + } + return autoboot; +} + +static bool ParseCommandLineOptions(int argc, char* argv[], std::shared_ptr& autoboot) +{ + std::optional fast_boot; + std::optional start_fullscreen; + std::optional state_index; + std::string state_filename; + bool no_more_args = false; + + for (int i = 1; i < argc; i++) + { + if (!no_more_args) + { +#define CHECK_ARG(str) !std::strcmp(argv[i], str) +#define CHECK_ARG_PARAM(str) (!std::strcmp(argv[i], str) && ((i + 1) < argc)) + + if (CHECK_ARG("-help")) + { + PrintCommandLineHelp(argv[0]); + return false; + } + else if (CHECK_ARG("-version")) + { + PrintCommandLineVersion(); + return false; + } + else if (CHECK_ARG("-batch")) + { + Console.WriteLn("Enabling batch mode."); + AutoBoot(autoboot)->batch_mode = true; + continue; + } + else if (CHECK_ARG("-fastboot")) + { + Console.WriteLn("Forcing fast boot."); + fast_boot = true; + continue; + } + else if (CHECK_ARG("-slowboot")) + { + Console.WriteLn("Forcing slow boot."); + fast_boot = false; + continue; + } + else if (CHECK_ARG("-resume")) + { + state_index = -1; + continue; + } + else if (CHECK_ARG_PARAM("-state")) + { + state_index = std::atoi(argv[++i]); + continue; + } + else if (CHECK_ARG_PARAM("-statefile")) + { + AutoBoot(autoboot)->save_state = argv[++i]; + continue; + } + else if (CHECK_ARG_PARAM("-elf")) + { + AutoBoot(autoboot)->elf_override = argv[++i]; + continue; + } + else if (CHECK_ARG("-fullscreen")) + { + Console.WriteLn("Going fullscreen after booting."); + start_fullscreen = true; + continue; + } + else if (CHECK_ARG("-nofullscreen")) + { + Console.WriteLn("Preventing fullscreen after booting."); + start_fullscreen = false; + continue; + } + else if (CHECK_ARG("-portable")) + { + Console.WriteLn("Using portable mode."); + // SetUserDirectoryToProgramDirectory(); + continue; + } + else if (CHECK_ARG("-resume")) + { + state_index = -1; + continue; + } + else if (CHECK_ARG("--")) + { + no_more_args = true; + continue; + } + else if (argv[i][0] == '-') + { + Console.Error("Unknown parameter: '%s'", argv[i]); + return false; + } + +#undef CHECK_ARG +#undef CHECK_ARG_PARAM + } + + if (!AutoBoot(autoboot)->source.empty()) + AutoBoot(autoboot)->source += ' '; + else + AutoBoot(autoboot)->source_type = CDVD_SourceType::Iso; + + AutoBoot(autoboot)->source += argv[i]; + } + + return true; +} + +int main(int argc, char* argv[]) +{ +#if QT_VERSION < QT_VERSION_CHECK(6, 0, 0) + QGuiApplication::setAttribute(Qt::AA_EnableHighDpiScaling); + QGuiApplication::setAttribute(Qt::AA_UseHighDpiPixmaps); +#if QT_VERSION >= QT_VERSION_CHECK(5, 14, 0) + QGuiApplication::setHighDpiScaleFactorRoundingPolicy(Qt::HighDpiScaleFactorRoundingPolicy::PassThrough); +#endif +#endif + + QApplication app(argc, argv); + std::shared_ptr autoboot; + if (!ParseCommandLineOptions(argc, argv, autoboot)) + return EXIT_FAILURE; + + MainWindow* main_window = new MainWindow(QApplication::style()->objectName()); + + if (!QtHost::Initialize()) + { + delete main_window; + return EXIT_FAILURE; + } + + main_window->initialize(); + EmuThread::start(); + + main_window->refreshGameList(false); + main_window->show(); + + if (autoboot) + g_emu_thread->startVM(std::move(autoboot)); + + const int result = app.exec(); + + EmuThread::stop(); + QtHost::Shutdown(); + return result; +} + +#ifdef _WIN32 + +// Apparently Qt6 got rid of this? +#include "common/RedtapeWindows.h" +#include + +/* + WinMain() - Initializes Windows and calls user's startup function main(). + NOTE: WinMain() won't be called if the application was linked as a "console" + application. +*/ + +// Convert a wchar_t to char string, equivalent to QString::toLocal8Bit() +// when passed CP_ACP. +static inline char* wideToMulti(unsigned int codePage, const wchar_t* aw) +{ + const int required = WideCharToMultiByte(codePage, 0, aw, -1, nullptr, 0, nullptr, nullptr); + char* result = new char[required]; + WideCharToMultiByte(codePage, 0, aw, -1, result, required, nullptr, nullptr); + return result; +} + +extern "C" int APIENTRY WinMain(HINSTANCE, HINSTANCE, LPSTR /*cmdParamarg*/, int /* cmdShow */) +{ + int argc = 0; + wchar_t** argvW = CommandLineToArgvW(GetCommandLineW(), &argc); + if (argvW == nullptr) + return -1; + char** argv = new char*[argc + 1]; + for (int i = 0; i != argc; ++i) + argv[i] = wideToMulti(CP_ACP, argvW[i]); + argv[argc] = nullptr; + LocalFree(argvW); + const int exitCode = main(argc, argv); + for (int i = 0; (i != argc) && (argv[i] != nullptr); ++i) + delete[] argv[i]; + delete[] argv; + return exitCode; +} + +#endif \ No newline at end of file diff --git a/pcsx2-qt/MainWindow.cpp b/pcsx2-qt/MainWindow.cpp new file mode 100644 index 000000000..9660c1cea --- /dev/null +++ b/pcsx2-qt/MainWindow.cpp @@ -0,0 +1,1372 @@ +/* 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 +#include +#include +#include +#include +#include + +#include "common/FileSystem.h" + +#include "pcsx2/CDVD/CDVDaccess.h" +#include "pcsx2/Frontend/GameList.h" +#include "pcsx2/HostDisplay.h" + +#include "AboutDialog.h" +#include "DisplayWidget.h" +#include "EmuThread.h" +#include "GameList/GameListRefreshThread.h" +#include "GameList/GameListWidget.h" +#include "MainWindow.h" +#include "QtHost.h" +#include "QtUtils.h" +#include "Settings/ControllerSettingsDialog.h" +#include "Settings/GameListSettingsWidget.h" +#include "Settings/InterfaceSettingsWidget.h" +#include "svnrev.h" + +static constexpr char DISC_IMAGE_FILTER[] = QT_TRANSLATE_NOOP( + "MainWindow", "All File Types (*.bin *.iso *.cue *.chd *.cso *.elf *.irx *.m3u);;Single-Track Raw Images (*.bin " + "*.iso);;Cue Sheets (*.cue);;MAME CHD Images (*.chd);;CSO Images (*.cso);;" + "ELF Executables (*.elf);;IRX Executables (*.irx);;Playlists (*.m3u)"); + +const char* MainWindow::DEFAULT_THEME_NAME = "darkfusion"; + +MainWindow* g_main_window = nullptr; + +MainWindow::MainWindow(const QString& unthemed_style_name) + : m_unthemed_style_name(unthemed_style_name) +{ + pxAssert(!g_main_window); + g_main_window = this; +} + +MainWindow::~MainWindow() +{ + // we compare here, since recreate destroys the window later + if (g_main_window == this) + g_main_window = nullptr; +} + +void MainWindow::initialize() +{ + setIconThemeFromSettings(); + m_ui.setupUi(this); + setupAdditionalUi(); + setStyleFromSettings(); + connectSignals(); + + restoreStateFromConfig(); + switchToGameListView(); + updateWindowTitle(); + updateSaveStateMenus(QString(), QString(), 0); +} + +void MainWindow::setupAdditionalUi() +{ + const bool toolbar_visible = QtHost::GetBaseBoolSettingValue("UI", "ShowToolbar", false); + m_ui.actionViewToolbar->setChecked(toolbar_visible); + m_ui.toolBar->setVisible(toolbar_visible); + + const bool toolbars_locked = QtHost::GetBaseBoolSettingValue("UI", "LockToolbar", false); + m_ui.actionViewLockToolbar->setChecked(toolbars_locked); + m_ui.toolBar->setMovable(!toolbars_locked); + m_ui.toolBar->setContextMenuPolicy(Qt::PreventContextMenu); + + const bool status_bar_visible = QtHost::GetBaseBoolSettingValue("UI", "ShowStatusBar", true); + m_ui.actionViewStatusBar->setChecked(status_bar_visible); + m_ui.statusBar->setVisible(status_bar_visible); + + m_game_list_widget = new GameListWidget(m_ui.mainContainer); + m_game_list_widget->initialize(); + m_ui.mainContainer->insertWidget(0, m_game_list_widget); + m_ui.mainContainer->setCurrentIndex(0); + m_ui.actionGridViewShowTitles->setChecked(m_game_list_widget->getShowGridCoverTitles()); + + m_status_progress_widget = new QProgressBar(m_ui.statusBar); + m_status_progress_widget->setSizePolicy(QSizePolicy::Preferred, QSizePolicy::Fixed); + m_status_progress_widget->setFixedSize(140, 16); + m_status_progress_widget->hide(); + + for (u32 scale = 0; scale <= 10; scale++) + { + QAction* action = + m_ui.menuWindowSize->addAction((scale == 0) ? tr("Internal Resolution") : tr("%1x Scale").arg(scale)); + connect(action, &QAction::triggered, [scale]() { g_emu_thread->requestDisplaySize(static_cast(scale)); }); + } + + updateEmulationActions(false, false); +} + +void MainWindow::connectSignals() +{ + connect(m_ui.actionStartFile, &QAction::triggered, this, &MainWindow::onStartFileActionTriggered); + connect(m_ui.actionStartBios, &QAction::triggered, this, &MainWindow::onStartBIOSActionTriggered); + connect(m_ui.actionChangeDisc, &QAction::triggered, [this] { m_ui.menuChangeDisc->exec(QCursor::pos()); }); + connect(m_ui.actionChangeDiscFromFile, &QAction::triggered, this, &MainWindow::onChangeDiscFromFileActionTriggered); + connect(m_ui.actionChangeDiscFromDevice, &QAction::triggered, this, + &MainWindow::onChangeDiscFromDeviceActionTriggered); + connect(m_ui.actionChangeDiscFromGameList, &QAction::triggered, this, + &MainWindow::onChangeDiscFromGameListActionTriggered); + connect(m_ui.menuChangeDisc, &QMenu::aboutToShow, this, &MainWindow::onChangeDiscMenuAboutToShow); + connect(m_ui.menuChangeDisc, &QMenu::aboutToHide, this, &MainWindow::onChangeDiscMenuAboutToHide); + connect(m_ui.actionPowerOff, &QAction::triggered, []() { g_emu_thread->shutdownVM(); }); + connect(m_ui.actionLoadState, &QAction::triggered, this, [this]() { m_ui.menuLoadState->exec(QCursor::pos()); }); + connect(m_ui.actionSaveState, &QAction::triggered, this, [this]() { m_ui.menuSaveState->exec(QCursor::pos()); }); + connect(m_ui.actionExit, &QAction::triggered, this, &MainWindow::close); + connect(m_ui.menuLoadState, &QMenu::aboutToShow, this, &MainWindow::onLoadStateMenuAboutToShow); + connect(m_ui.menuSaveState, &QMenu::aboutToShow, this, &MainWindow::onSaveStateMenuAboutToShow); + connect(m_ui.actionSettings, &QAction::triggered, [this]() { doSettings(SettingsDialog::Category::Count); }); + connect(m_ui.actionInterfaceSettings, &QAction::triggered, + [this]() { doSettings(SettingsDialog::Category::InterfaceSettings); }); + connect(m_ui.actionGameListSettings, &QAction::triggered, + [this]() { doSettings(SettingsDialog::Category::GameListSettings); }); + connect(m_ui.actionEmulationSettings, &QAction::triggered, + [this]() { doSettings(SettingsDialog::Category::EmulationSettings); }); + connect(m_ui.actionBIOSSettings, &QAction::triggered, + [this]() { doSettings(SettingsDialog::Category::BIOSSettings); }); + connect(m_ui.actionSystemSettings, &QAction::triggered, + [this]() { doSettings(SettingsDialog::Category::SystemSettings); }); + connect(m_ui.actionGraphicsSettings, &QAction::triggered, + [this]() { doSettings(SettingsDialog::Category::GraphicsSettings); }); + connect(m_ui.actionAudioSettings, &QAction::triggered, + [this]() { doSettings(SettingsDialog::Category::AudioSettings); }); + connect(m_ui.actionMemoryCardSettings, &QAction::triggered, + [this]() { doSettings(SettingsDialog::Category::MemoryCardSettings); }); + connect(m_ui.actionControllerSettings, &QAction::triggered, + [this]() { doControllerSettings(ControllerSettingsDialog::Category::GlobalSettings); }); + connect(m_ui.actionHotkeySettings, &QAction::triggered, + [this]() { doControllerSettings(ControllerSettingsDialog::Category::HotkeySettings); }); + connect(m_ui.actionAddGameDirectory, &QAction::triggered, + [this]() { getSettingsDialog()->getGameListSettingsWidget()->addSearchDirectory(this); }); + connect(m_ui.actionScanForNewGames, &QAction::triggered, [this]() { refreshGameList(false); }); + connect(m_ui.actionRescanAllGames, &QAction::triggered, [this]() { refreshGameList(true); }); + connect(m_ui.actionViewToolbar, &QAction::toggled, this, &MainWindow::onViewToolbarActionToggled); + connect(m_ui.actionViewLockToolbar, &QAction::toggled, this, &MainWindow::onViewLockToolbarActionToggled); + connect(m_ui.actionViewStatusBar, &QAction::toggled, this, &MainWindow::onViewStatusBarActionToggled); + connect(m_ui.actionViewGameList, &QAction::triggered, this, &MainWindow::onViewGameListActionTriggered); + connect(m_ui.actionViewGameGrid, &QAction::triggered, this, &MainWindow::onViewGameGridActionTriggered); + connect(m_ui.actionViewSystemDisplay, &QAction::triggered, this, &MainWindow::onViewSystemDisplayTriggered); + connect(m_ui.actionViewGameProperties, &QAction::triggered, this, &MainWindow::onViewGamePropertiesActionTriggered); + connect(m_ui.actionGitHubRepository, &QAction::triggered, this, &MainWindow::onGitHubRepositoryActionTriggered); + connect(m_ui.actionSupportForums, &QAction::triggered, this, &MainWindow::onSupportForumsActionTriggered); + connect(m_ui.actionDiscordServer, &QAction::triggered, this, &MainWindow::onDiscordServerActionTriggered); + connect(m_ui.actionAboutQt, &QAction::triggered, qApp, &QApplication::aboutQt); + connect(m_ui.actionAbout, &QAction::triggered, this, &MainWindow::onAboutActionTriggered); + connect(m_ui.actionCheckForUpdates, &QAction::triggered, this, &MainWindow::onCheckForUpdatesActionTriggered); + connect(m_ui.actionOpenDataDirectory, &QAction::triggered, this, &MainWindow::onToolsOpenDataDirectoryTriggered); + connect(m_ui.actionGridViewShowTitles, &QAction::triggered, m_game_list_widget, &GameListWidget::setShowCoverTitles); + connect(m_ui.actionGridViewZoomIn, &QAction::triggered, m_game_list_widget, [this]() { + if (isShowingGameList()) + m_game_list_widget->gridZoomIn(); + }); + connect(m_ui.actionGridViewZoomOut, &QAction::triggered, m_game_list_widget, [this]() { + if (isShowingGameList()) + m_game_list_widget->gridZoomOut(); + }); + connect(m_ui.actionGridViewRefreshCovers, &QAction::triggered, m_game_list_widget, + &GameListWidget::refreshGridCovers); + + // These need to be queued connections to stop crashing due to menus opening/closing and switching focus. + connect(m_game_list_widget, &GameListWidget::refreshProgress, this, &MainWindow::onGameListRefreshProgress); + connect(m_game_list_widget, &GameListWidget::refreshComplete, this, &MainWindow::onGameListRefreshComplete); + connect(m_game_list_widget, &GameListWidget::selectionChanged, this, &MainWindow::onGameListSelectionChanged, + Qt::QueuedConnection); + connect(m_game_list_widget, &GameListWidget::entryActivated, this, &MainWindow::onGameListEntryActivated, + Qt::QueuedConnection); + connect(m_game_list_widget, &GameListWidget::entryContextMenuRequested, this, + &MainWindow::onGameListEntryContextMenuRequested, Qt::QueuedConnection); +} + +void MainWindow::connectVMThreadSignals(EmuThread* thread) +{ + connect(thread, &EmuThread::onCreateDisplayRequested, this, &MainWindow::createDisplay, Qt::BlockingQueuedConnection); + connect(thread, &EmuThread::onUpdateDisplayRequested, this, &MainWindow::updateDisplay, Qt::BlockingQueuedConnection); + connect(thread, &EmuThread::onDestroyDisplayRequested, this, &MainWindow::destroyDisplay, + Qt::BlockingQueuedConnection); + connect(thread, &EmuThread::onResizeDisplayRequested, this, &MainWindow::displayResizeRequested); + connect(thread, &EmuThread::onVMStarting, this, &MainWindow::onVMStarting); + connect(thread, &EmuThread::onVMStarted, this, &MainWindow::onVMStarted); + connect(thread, &EmuThread::onVMPaused, this, &MainWindow::onVMPaused); + connect(thread, &EmuThread::onVMResumed, this, &MainWindow::onVMResumed); + connect(thread, &EmuThread::onVMStopped, this, &MainWindow::onVMStopped); + connect(thread, &EmuThread::onGameChanged, this, &MainWindow::onGameChanged); + + connect(m_ui.actionReset, &QAction::triggered, thread, &EmuThread::resetVM); + connect(m_ui.actionPause, &QAction::toggled, thread, &EmuThread::setVMPaused); + connect(m_ui.actionFullscreen, &QAction::triggered, thread, &EmuThread::toggleFullscreen); + connect(m_ui.actionToggleSoftwareRendering, &QAction::triggered, thread, &EmuThread::toggleSoftwareRendering); + connect(m_ui.actionReloadPatches, &QAction::triggered, thread, &EmuThread::reloadPatches); + + static constexpr GSRendererType renderers[] = { +#ifdef _WIN32 + GSRendererType::DX11, +#endif + GSRendererType::OGL, GSRendererType::VK, GSRendererType::SW, GSRendererType::Null}; + for (GSRendererType renderer : renderers) + { + connect( + m_ui.menuDebugSwitchRenderer->addAction(QString::fromUtf8(Pcsx2Config::GSOptions::GetRendererName(renderer))), + &QAction::triggered, [renderer] { g_emu_thread->switchRenderer(renderer); }); + } +} + +void MainWindow::recreate() +{ + if (m_vm_valid) + g_emu_thread->shutdownVM(true, true); + + close(); + g_main_window = nullptr; + + MainWindow* new_main_window = new MainWindow(m_unthemed_style_name); + new_main_window->initialize(); + new_main_window->refreshGameList(false); + new_main_window->show(); + deleteLater(); +} + +void MainWindow::setStyleFromSettings() +{ + const std::string theme(QtHost::GetBaseStringSettingValue("UI", "Theme", DEFAULT_THEME_NAME)); + + if (theme == "fusion") + { + qApp->setPalette(QApplication::style()->standardPalette()); + qApp->setStyleSheet(QString()); + qApp->setStyle(QStyleFactory::create("Fusion")); + } + else if (theme == "darkfusion") + { + // adapted from https://gist.github.com/QuantumCD/6245215 + qApp->setStyle(QStyleFactory::create("Fusion")); + + const QColor lighterGray(75, 75, 75); + const QColor darkGray(53, 53, 53); + const QColor gray(128, 128, 128); + const QColor black(25, 25, 25); + const QColor blue(198, 238, 255); + + QPalette darkPalette; + darkPalette.setColor(QPalette::Window, darkGray); + darkPalette.setColor(QPalette::WindowText, Qt::white); + darkPalette.setColor(QPalette::Base, black); + darkPalette.setColor(QPalette::AlternateBase, darkGray); + darkPalette.setColor(QPalette::ToolTipBase, darkGray); + darkPalette.setColor(QPalette::ToolTipText, Qt::white); + darkPalette.setColor(QPalette::Text, Qt::white); + darkPalette.setColor(QPalette::Button, darkGray); + darkPalette.setColor(QPalette::ButtonText, Qt::white); + darkPalette.setColor(QPalette::Link, blue); + darkPalette.setColor(QPalette::Highlight, lighterGray); + darkPalette.setColor(QPalette::HighlightedText, Qt::white); + + darkPalette.setColor(QPalette::Active, QPalette::Button, gray.darker()); + darkPalette.setColor(QPalette::Disabled, QPalette::ButtonText, gray); + darkPalette.setColor(QPalette::Disabled, QPalette::WindowText, gray); + darkPalette.setColor(QPalette::Disabled, QPalette::Text, gray); + darkPalette.setColor(QPalette::Disabled, QPalette::Light, darkGray); + + qApp->setPalette(darkPalette); + + qApp->setStyleSheet("QToolTip { color: #ffffff; background-color: #2a82da; border: 1px solid white; }"); + } + else if (theme == "darkfusionblue") + { + // adapted from https://gist.github.com/QuantumCD/6245215 + qApp->setStyle(QStyleFactory::create("Fusion")); + + const QColor lighterGray(75, 75, 75); + const QColor darkGray(53, 53, 53); + const QColor gray(128, 128, 128); + const QColor black(25, 25, 25); + const QColor blue(198, 238, 255); + const QColor blue2(0, 88, 208); + + QPalette darkPalette; + darkPalette.setColor(QPalette::Window, darkGray); + darkPalette.setColor(QPalette::WindowText, Qt::white); + darkPalette.setColor(QPalette::Base, black); + darkPalette.setColor(QPalette::AlternateBase, darkGray); + darkPalette.setColor(QPalette::ToolTipBase, blue2); + darkPalette.setColor(QPalette::ToolTipText, Qt::white); + darkPalette.setColor(QPalette::Text, Qt::white); + darkPalette.setColor(QPalette::Button, darkGray); + darkPalette.setColor(QPalette::ButtonText, Qt::white); + darkPalette.setColor(QPalette::Link, blue); + darkPalette.setColor(QPalette::Highlight, blue2); + darkPalette.setColor(QPalette::HighlightedText, Qt::white); + + darkPalette.setColor(QPalette::Active, QPalette::Button, gray.darker()); + darkPalette.setColor(QPalette::Disabled, QPalette::ButtonText, gray); + darkPalette.setColor(QPalette::Disabled, QPalette::WindowText, gray); + darkPalette.setColor(QPalette::Disabled, QPalette::Text, gray); + darkPalette.setColor(QPalette::Disabled, QPalette::Light, darkGray); + + qApp->setPalette(darkPalette); + + qApp->setStyleSheet("QToolTip { color: #ffffff; background-color: #2a82da; border: 1px solid white; }"); + } + else + { + qApp->setPalette(QApplication::style()->standardPalette()); + qApp->setStyleSheet(QString()); + qApp->setStyle(m_unthemed_style_name); + } +} + +void MainWindow::setIconThemeFromSettings() +{ + const std::string theme(QtHost::GetBaseStringSettingValue("UI", "Theme", DEFAULT_THEME_NAME)); + QString icon_theme; + + if (theme == "darkfusion" || theme == "darkfusionblue") + icon_theme = QStringLiteral("white"); + else + icon_theme = QStringLiteral("black"); + + QIcon::setThemeName(icon_theme); +} + +void MainWindow::saveStateToConfig() +{ + { + const QByteArray geometry = saveGeometry(); + const QByteArray geometry_b64 = geometry.toBase64(); + const std::string old_geometry_b64 = QtHost::GetBaseStringSettingValue("UI", "MainWindowGeometry"); + if (old_geometry_b64 != geometry_b64.constData()) + QtHost::SetBaseStringSettingValue("UI", "MainWindowGeometry", geometry_b64.constData()); + } + + { + const QByteArray state = saveState(); + const QByteArray state_b64 = state.toBase64(); + const std::string old_state_b64 = QtHost::GetBaseStringSettingValue("UI", "MainWindowState"); + if (old_state_b64 != state_b64.constData()) + QtHost::SetBaseStringSettingValue("UI", "MainWindowState", state_b64.constData()); + } +} + +void MainWindow::restoreStateFromConfig() +{ + { + const std::string geometry_b64 = QtHost::GetBaseStringSettingValue("UI", "MainWindowGeometry"); + const QByteArray geometry = QByteArray::fromBase64(QByteArray::fromStdString(geometry_b64)); + if (!geometry.isEmpty()) + restoreGeometry(geometry); + } + + { + const std::string state_b64 = QtHost::GetBaseStringSettingValue("UI", "MainWindowState"); + const QByteArray state = QByteArray::fromBase64(QByteArray::fromStdString(state_b64)); + if (!state.isEmpty()) + restoreState(state); + + { + QSignalBlocker sb(m_ui.actionViewToolbar); + m_ui.actionViewToolbar->setChecked(!m_ui.toolBar->isHidden()); + } + { + QSignalBlocker sb(m_ui.actionViewStatusBar); + m_ui.actionViewStatusBar->setChecked(!m_ui.statusBar->isHidden()); + } + } +} + +void MainWindow::updateEmulationActions(bool starting, bool running) +{ + const bool starting_or_running = starting || running; + + m_ui.actionStartFile->setDisabled(starting_or_running); + m_ui.actionStartDisc->setDisabled(starting_or_running); + m_ui.actionStartBios->setDisabled(starting_or_running); + + m_ui.actionPowerOff->setEnabled(running); + m_ui.actionReset->setEnabled(running); + m_ui.actionPause->setEnabled(running); + m_ui.actionChangeDisc->setEnabled(running); + m_ui.actionCheats->setEnabled(running); + m_ui.actionScreenshot->setEnabled(running); + m_ui.actionViewSystemDisplay->setEnabled(starting_or_running); + m_ui.menuChangeDisc->setEnabled(running); + m_ui.menuCheats->setEnabled(running); + + m_ui.actionSaveState->setEnabled(running); + m_ui.menuSaveState->setEnabled(running); + m_ui.menuWindowSize->setEnabled(starting_or_running); + + m_ui.actionFullscreen->setEnabled(starting_or_running); + m_ui.actionViewGameProperties->setEnabled(running); + + m_game_list_widget->setDisabled(starting && !running); +} + +void MainWindow::updateWindowTitle() +{ + QString title; + if (!m_vm_valid || m_current_game_name.isEmpty()) + { +#if defined(_DEBUG) + title = QStringLiteral("PCSX2 [Debug] %1").arg(GIT_REV); +#else + title = QStringLiteral("PCSX2 %1").arg(GIT_REV); +#endif + } + else + { +#if defined(_DEBUG) + title = QStringLiteral("%1 [Debug]").arg(m_current_game_name); +#else + title = m_current_game_name; +#endif + } + + if (windowTitle() != title) + setWindowTitle(title); +} + +void MainWindow::setProgressBar(int current, int total) +{ + m_status_progress_widget->setValue(current); + m_status_progress_widget->setMaximum(total); + + if (m_status_progress_widget->isVisible()) + return; + + m_status_progress_widget->show(); + m_ui.statusBar->addPermanentWidget(m_status_progress_widget); +} + +void MainWindow::clearProgressBar() +{ + if (!m_status_progress_widget->isVisible()) + return; + + m_status_progress_widget->hide(); + m_ui.statusBar->removeWidget(m_status_progress_widget); +} + +bool MainWindow::isShowingGameList() const +{ + return m_ui.mainContainer->currentIndex() == 0; +} + +void MainWindow::switchToGameListView() +{ + if ((m_display_widget && !m_display_widget->parent()) || m_ui.mainContainer->currentIndex() == 0) + return; + + if (m_vm_valid) + { + m_was_focused_on_container_switch = m_vm_paused; + if (!m_vm_paused) + g_emu_thread->setVMPaused(true); + } + + m_ui.mainContainer->setCurrentIndex(0); + m_game_list_widget->setFocus(); +} + +void MainWindow::switchToEmulationView() +{ + if (!m_display_widget || !m_display_widget->parent() || m_ui.mainContainer->currentIndex() == 1) + return; + + if (m_vm_valid) + { + m_ui.mainContainer->setCurrentIndex(1); + if (m_vm_paused && !m_was_focused_on_container_switch) + g_emu_thread->setVMPaused(false); + } + + m_display_widget->setFocus(); +} + +void MainWindow::refreshGameList(bool invalidate_cache) +{ + m_game_list_widget->refresh(invalidate_cache); +} + +void MainWindow::invalidateSaveStateCache() +{ + m_save_states_invalidated = true; +} + +void MainWindow::reportError(const QString& title, const QString& message) +{ + QMessageBox::critical(this, title, message); +} + +void Host::InvalidateSaveStateCache() +{ + QMetaObject::invokeMethod(g_main_window, &MainWindow::invalidateSaveStateCache, Qt::QueuedConnection); +} + +void MainWindow::onGameListRefreshProgress(const QString& status, int current, int total) +{ + m_ui.statusBar->showMessage(status); + setProgressBar(current, total); +} + +void MainWindow::onGameListRefreshComplete() +{ + clearProgressBar(); +} + +void MainWindow::onGameListSelectionChanged() +{ + auto lock = GameList::GetLock(); + const GameList::Entry* entry = m_game_list_widget->getSelectedEntry(); + if (!entry) + return; + + m_ui.statusBar->showMessage(QString::fromStdString(entry->path)); +} + +void MainWindow::onGameListEntryActivated() +{ + auto lock = GameList::GetLock(); + const GameList::Entry* entry = m_game_list_widget->getSelectedEntry(); + if (!entry) + return; + + if (m_vm_valid) + { + // change disc on double click + g_emu_thread->changeDisc(QString::fromStdString(entry->path)); + switchToEmulationView(); + return; + } + + // only resume if the option is enabled, and we have one for this game + const bool resume = + (VMManager::ShouldSaveResumeState() && VMManager::HasSaveStateInSlot(entry->serial.c_str(), entry->crc, -1)); + startGameListEntry(entry, resume ? std::optional(-1) : std::optional(), std::nullopt); +} + +void MainWindow::onGameListEntryContextMenuRequested(const QPoint& point) +{ + auto lock = GameList::GetLock(); + const GameList::Entry* entry = m_game_list_widget->getSelectedEntry(); + + QMenu menu; + + if (entry) + { + QAction* action = menu.addAction(tr("Properties...")); + // connect(action, &QAction::triggered, [this, entry]() { GamePropertiesDialog::showForEntry(entry, this); }); + + action = menu.addAction(tr("Open Containing Directory...")); + connect(action, &QAction::triggered, [this, entry]() { + const QFileInfo fi(QString::fromStdString(entry->path)); + QtUtils::OpenURL(this, QUrl::fromLocalFile(fi.absolutePath())); + }); + + action = menu.addAction(tr("Set Cover Image...")); + connect(action, &QAction::triggered, [this, entry]() { setGameListEntryCoverImage(entry); }); + + connect(menu.addAction(tr("Exclude From List")), &QAction::triggered, + [this, entry]() { getSettingsDialog()->getGameListSettingsWidget()->addExcludedPath(entry->path); }); + + menu.addSeparator(); + + if (!m_vm_valid) + { + action = menu.addAction(tr("Default Boot")); + connect(action, &QAction::triggered, [this, entry]() { startGameListEntry(entry); }); + + // Make bold to indicate it's the default choice when double-clicking + if (!VMManager::ShouldSaveResumeState() || !VMManager::HasSaveStateInSlot(entry->serial.c_str(), entry->crc, -1)) + QtUtils::MarkActionAsDefault(action); + + action = menu.addAction(tr("Fast Boot")); + connect(action, &QAction::triggered, [this, entry]() { startGameListEntry(entry, std::nullopt, true); }); + + action = menu.addAction(tr("Full Boot")); + connect(action, &QAction::triggered, [this, entry]() { startGameListEntry(entry, std::nullopt, false); }); + + if (m_ui.menuDebug->menuAction()->isVisible()) + { + // TODO: Hook this up once it's implemented. + action = menu.addAction(tr("Boot and Debug")); + } + + menu.addSeparator(); + populateLoadStateMenu(&menu, QString::fromStdString(entry->path), QString::fromStdString(entry->serial), + entry->crc); + } + else + { + action = menu.addAction(tr("Change Disc")); + connect(action, &QAction::triggered, [this, entry]() { + g_emu_thread->changeDisc(QString::fromStdString(entry->path)); + switchToEmulationView(); + }); + QtUtils::MarkActionAsDefault(action); + } + + menu.addSeparator(); + } + + connect(menu.addAction(tr("Add Search Directory...")), &QAction::triggered, + [this]() { getSettingsDialog()->getGameListSettingsWidget()->addSearchDirectory(this); }); + + menu.exec(point); +} + +void MainWindow::onStartFileActionTriggered() +{ + QString filename = QDir::toNativeSeparators( + QFileDialog::getOpenFileName(this, tr("Select Disc Image"), QString(), tr(DISC_IMAGE_FILTER), nullptr)); + if (filename.isEmpty()) + return; + + std::shared_ptr params = std::make_shared(); + VMManager::SetBootParametersForPath(filename.toStdString(), params.get()); + g_emu_thread->startVM(std::move(params)); +} + +void MainWindow::onStartBIOSActionTriggered() +{ + std::shared_ptr params = std::make_shared(); + params->source_type = CDVD_SourceType::NoDisc; + g_emu_thread->startVM(std::move(params)); +} + +void MainWindow::onChangeDiscFromFileActionTriggered() +{ + ScopedVMPause pauser(m_vm_paused); + + QString filename = + QFileDialog::getOpenFileName(this, tr("Select Disc Image"), QString(), tr(DISC_IMAGE_FILTER), nullptr); + if (filename.isEmpty()) + return; + + g_emu_thread->changeDisc(filename); +} + +void MainWindow::onChangeDiscFromGameListActionTriggered() +{ + switchToGameListView(); +} + +void MainWindow::onChangeDiscFromDeviceActionTriggered() +{ + // TODO +} + +void MainWindow::onChangeDiscMenuAboutToShow() +{ + // TODO: This is where we would populate the playlist if there is one. +} + +void MainWindow::onChangeDiscMenuAboutToHide() +{ +} + +void MainWindow::onLoadStateMenuAboutToShow() +{ + if (m_save_states_invalidated) + updateSaveStateMenus(m_current_disc_path, m_current_game_serial, m_current_game_crc); +} + +void MainWindow::onSaveStateMenuAboutToShow() +{ + if (m_save_states_invalidated) + updateSaveStateMenus(m_current_disc_path, m_current_game_serial, m_current_game_crc); +} + +void MainWindow::onViewToolbarActionToggled(bool checked) +{ + QtHost::SetBaseBoolSettingValue("UI", "ShowToolbar", checked); + m_ui.toolBar->setVisible(checked); +} + +void MainWindow::onViewLockToolbarActionToggled(bool checked) +{ + QtHost::SetBaseBoolSettingValue("UI", "LockToolbar", checked); + m_ui.toolBar->setMovable(!checked); +} + +void MainWindow::onViewStatusBarActionToggled(bool checked) +{ + QtHost::SetBaseBoolSettingValue("UI", "ShowStatusBar", checked); + m_ui.statusBar->setVisible(checked); +} + +void MainWindow::onViewGameListActionTriggered() +{ + switchToGameListView(); + m_game_list_widget->showGameList(); +} + +void MainWindow::onViewGameGridActionTriggered() +{ + switchToGameListView(); + m_game_list_widget->showGameGrid(); +} + +void MainWindow::onViewSystemDisplayTriggered() +{ + if (m_vm_valid) + switchToEmulationView(); +} + +void MainWindow::onViewGamePropertiesActionTriggered() +{ + if (!m_vm_valid) + return; +} + +void MainWindow::onGitHubRepositoryActionTriggered() +{ + QtUtils::OpenURL(this, AboutDialog::getGitHubRepositoryUrl()); +} + +void MainWindow::onSupportForumsActionTriggered() +{ + QtUtils::OpenURL(this, AboutDialog::getSupportForumsUrl()); +} + +void MainWindow::onDiscordServerActionTriggered() +{ + QtUtils::OpenURL(this, AboutDialog::getDiscordServerUrl()); +} + +void MainWindow::onAboutActionTriggered() +{ + AboutDialog about(this); + about.exec(); +} + +void MainWindow::onCheckForUpdatesActionTriggered() +{ +} + +void MainWindow::onToolsOpenDataDirectoryTriggered() +{ + const QString path(QtUtils::WxStringToQString(EmuFolders::DataRoot.ToString())); + QtUtils::OpenURL(this, QUrl::fromLocalFile(path)); +} + +void MainWindow::onThemeChanged() +{ + setStyleFromSettings(); + setIconThemeFromSettings(); + recreate(); +} + +void MainWindow::onThemeChangedFromSettings() +{ + // reopen the settings dialog after recreating + onThemeChanged(); + g_main_window->doSettings(SettingsDialog::Category::InterfaceSettings); +} + +void MainWindow::onVMStarting() +{ + m_vm_valid = true; + updateEmulationActions(true, false); + updateWindowTitle(); + + // prevent loading state until we're fully initialized + updateSaveStateMenus(QString(), QString(), 0); +} + +void MainWindow::onVMStarted() +{ + m_vm_valid = true; + updateEmulationActions(true, true); + updateWindowTitle(); +} + +void MainWindow::onVMPaused() +{ + // update UI + { + QSignalBlocker sb(m_ui.actionPause); + m_ui.actionPause->setChecked(true); + } + + m_vm_paused = true; + updateWindowTitle(); +} + +void MainWindow::onVMResumed() +{ + // update UI + { + QSignalBlocker sb(m_ui.actionPause); + m_ui.actionPause->setChecked(false); + } + + m_vm_paused = false; + updateWindowTitle(); +} + +void MainWindow::onVMStopped() +{ + m_vm_valid = false; + m_vm_paused = false; + updateEmulationActions(false, false); + updateWindowTitle(); + switchToGameListView(); +} + +void MainWindow::onGameChanged(const QString& path, const QString& serial, const QString& name, quint32 crc) +{ + m_current_disc_path = path; + m_current_game_serial = serial; + m_current_game_name = name; + m_current_game_crc = crc; + updateWindowTitle(); + updateSaveStateMenus(path, serial, crc); +} + +void MainWindow::closeEvent(QCloseEvent* event) +{ + g_emu_thread->shutdownVM(true, true); + saveStateToConfig(); + QMainWindow::closeEvent(event); +} + +DisplayWidget* MainWindow::createDisplay(bool fullscreen, bool render_to_main) +{ + pxAssertRel(!fullscreen || !render_to_main, "Not rendering to main and fullscreen"); + + HostDisplay* host_display = Host::GetHostDisplay(); + if (!host_display) + return nullptr; + + const std::string fullscreen_mode(QtHost::GetBaseStringSettingValue("EmuCore/GS", "FullscreenMode", "")); + const bool is_exclusive_fullscreen = (fullscreen && !fullscreen_mode.empty() && host_display->SupportsFullscreen()); + + QWidget* container; + if (DisplayContainer::IsNeeded(fullscreen, render_to_main)) + { + m_display_container = new DisplayContainer(); + m_display_widget = new DisplayWidget(m_display_container); + m_display_container->setDisplayWidget(m_display_widget); + container = m_display_container; + } + else + { + m_display_widget = new DisplayWidget((!fullscreen && render_to_main) ? m_ui.mainContainer : nullptr); + container = m_display_widget; + } + + container->setWindowTitle(windowTitle()); + container->setWindowIcon(windowIcon()); + + if (fullscreen) + { + if (!is_exclusive_fullscreen) + container->showFullScreen(); + else + container->showNormal(); + + // updateMouseMode(System::IsPaused()); + } + else if (!render_to_main) + { + restoreDisplayWindowGeometryFromConfig(); + container->showNormal(); + } + else + { + m_ui.mainContainer->insertWidget(1, m_display_widget); + switchToEmulationView(); + } + + // we need the surface visible.. this might be able to be replaced with something else + QCoreApplication::processEvents(QEventLoop::ExcludeUserInputEvents); + + std::optional wi = m_display_widget->getWindowInfo(); + if (!wi.has_value()) + { + QMessageBox::critical(this, tr("Error"), tr("Failed to get window info from widget")); + destroyDisplayWidget(); + return nullptr; + } + + if (!host_display->CreateRenderDevice(wi.value(), Host::GetStringSettingValue("EmuCore/GS", "Adapter", ""), + Host::GetBoolSettingValue("EmuCore/GS", "ThreadedPresentation", false), + Host::GetBoolSettingValue("EmuCore/GS", "UseDebugDevice", false))) + { + QMessageBox::critical(this, tr("Error"), tr("Failed to create host display device context.")); + destroyDisplayWidget(); + return nullptr; + } + + if (is_exclusive_fullscreen) + setDisplayFullscreen(fullscreen_mode); + + host_display->DoneRenderContextCurrent(); + return m_display_widget; +} + +DisplayWidget* MainWindow::updateDisplay(bool fullscreen, bool render_to_main) +{ + HostDisplay* host_display = Host::GetHostDisplay(); + const bool is_fullscreen = m_display_widget->isFullScreen(); + const bool is_rendering_to_main = (!is_fullscreen && m_display_widget->parent()); + const std::string fullscreen_mode(QtHost::GetBaseStringSettingValue("EmuCore/GS", "FullscreenMode", "")); + const bool is_exclusive_fullscreen = (fullscreen && !fullscreen_mode.empty() && host_display->SupportsFullscreen()); + if (fullscreen == is_fullscreen && is_rendering_to_main == render_to_main) + return m_display_widget; + + // Skip recreating the surface if we're just transitioning between fullscreen and windowed with render-to-main off. + const bool has_container = (m_display_container != nullptr); + const bool needs_container = DisplayContainer::IsNeeded(fullscreen, render_to_main); + if (!is_rendering_to_main && !render_to_main && !is_exclusive_fullscreen && has_container == needs_container) + { + qDebug() << "Toggling to" << (fullscreen ? "fullscreen" : "windowed") << "without recreating surface"; + if (host_display->IsFullscreen()) + host_display->SetFullscreen(false, 0, 0, 0.0f); + + if (fullscreen) + { + m_display_widget->showFullScreen(); + } + else + { + restoreDisplayWindowGeometryFromConfig(); + m_display_widget->showNormal(); + } + + QCoreApplication::processEvents(QEventLoop::ExcludeUserInputEvents); + // updateMouseMode(System::IsPaused()); + return m_display_widget; + } + + host_display->DestroyRenderSurface(); + + destroyDisplayWidget(); + + QWidget* container; + if (DisplayContainer::IsNeeded(fullscreen, render_to_main)) + { + m_display_container = new DisplayContainer(); + m_display_widget = new DisplayWidget(m_display_container); + m_display_container->setDisplayWidget(m_display_widget); + container = m_display_container; + } + else + { + m_display_widget = new DisplayWidget((!fullscreen && render_to_main) ? m_ui.mainContainer : nullptr); + container = m_display_widget; + } + + container->setWindowTitle(windowTitle()); + container->setWindowIcon(windowIcon()); + + if (fullscreen) + { + if (!is_exclusive_fullscreen) + container->showFullScreen(); + else + container->showNormal(); + + // updateMouseMode(System::IsPaused()); + } + else if (!render_to_main) + { + restoreDisplayWindowGeometryFromConfig(); + container->showNormal(); + } + else + { + m_ui.mainContainer->insertWidget(1, m_display_widget); + switchToEmulationView(); + } + + // we need the surface visible.. this might be able to be replaced with something else + QCoreApplication::processEvents(QEventLoop::ExcludeUserInputEvents); + + std::optional wi = m_display_widget->getWindowInfo(); + if (!wi.has_value()) + { + QMessageBox::critical(this, tr("Error"), tr("Failed to get new window info from widget")); + destroyDisplayWidget(); + return nullptr; + } + + if (!host_display->ChangeRenderWindow(wi.value())) + pxFailRel("Failed to recreate surface on new widget."); + + if (is_exclusive_fullscreen) + setDisplayFullscreen(fullscreen_mode); + + m_display_widget->setFocus(); + + QSignalBlocker blocker(m_ui.actionFullscreen); + m_ui.actionFullscreen->setChecked(fullscreen); + return m_display_widget; +} + +void MainWindow::displayResizeRequested(qint32 width, qint32 height) +{ + if (!m_display_widget) + return; + + // unapply the pixel scaling factor for hidpi + const float dpr = devicePixelRatioF(); + width = static_cast(std::max(static_cast(std::lroundf(static_cast(width) / dpr)), 1)); + height = static_cast(std::max(static_cast(std::lroundf(static_cast(height) / dpr)), 1)); + + if (m_display_container || !m_display_widget->parent()) + { + // no parent - rendering to separate window. easy. + getDisplayContainer()->resize(QSize(std::max(width, 1), std::max(height, 1))); + return; + } + + // we are rendering to the main window. we have to add in the extra height from the toolbar/status bar. + const s32 extra_height = this->height() - m_display_widget->height(); + resize(QSize(std::max(width, 1), std::max(height + extra_height, 1))); +} + +void MainWindow::destroyDisplay() +{ + destroyDisplayWidget(); +} + +void MainWindow::focusDisplayWidget() +{ + if (m_ui.mainContainer->currentIndex() != 1) + return; + + m_display_widget->setFocus(); +} + +QWidget* MainWindow::getDisplayContainer() const +{ + return (m_display_container ? static_cast(m_display_container) : static_cast(m_display_widget)); +} + +void MainWindow::saveDisplayWindowGeometryToConfig() +{ + const QByteArray geometry = getDisplayContainer()->saveGeometry(); + const QByteArray geometry_b64 = geometry.toBase64(); + const std::string old_geometry_b64 = QtHost::GetBaseStringSettingValue("UI", "DisplayWindowGeometry"); + if (old_geometry_b64 != geometry_b64.constData()) + QtHost::SetBaseStringSettingValue("UI", "DisplayWindowGeometry", geometry_b64.constData()); +} + +void MainWindow::restoreDisplayWindowGeometryFromConfig() +{ + const std::string geometry_b64 = QtHost::GetBaseStringSettingValue("UI", "DisplayWindowGeometry"); + const QByteArray geometry = QByteArray::fromBase64(QByteArray::fromStdString(geometry_b64)); + QWidget* container = getDisplayContainer(); + if (!geometry.isEmpty()) + container->restoreGeometry(geometry); + else + container->resize(640, 480); +} + +void MainWindow::destroyDisplayWidget() +{ + if (!m_display_widget) + return; + + if (m_display_container || (!m_display_widget->parent() && !m_display_widget->isFullScreen())) + saveDisplayWindowGeometryToConfig(); + + if (m_display_container) + m_display_container->removeDisplayWidget(); + + if (m_display_widget->parent()) + { + m_ui.mainContainer->removeWidget(m_display_widget); + m_ui.mainContainer->setCurrentIndex(0); + m_game_list_widget->setFocus(); + } + + delete m_display_widget; + m_display_widget = nullptr; + + delete m_display_container; + m_display_container = nullptr; +} + +void MainWindow::setDisplayFullscreen(const std::string& fullscreen_mode) +{ + u32 width, height; + float refresh_rate; + if (HostDisplay::ParseFullscreenMode(fullscreen_mode, &width, &height, &refresh_rate)) + { + if (Host::GetHostDisplay()->SetFullscreen(true, width, height, refresh_rate)) + { + Host::AddOSDMessage("Acquired exclusive fullscreen.", 10.0f); + } + else + { + Host::AddOSDMessage("Failed to acquire exclusive fullscreen.", 10.0f); + } + } +} + +SettingsDialog* MainWindow::getSettingsDialog() +{ + if (!m_settings_dialog) + { + m_settings_dialog = new SettingsDialog(this); + connect(m_settings_dialog->getInterfaceSettingsWidget(), &InterfaceSettingsWidget::themeChanged, this, + &MainWindow::onThemeChangedFromSettings); + } + + return m_settings_dialog; +} + +void MainWindow::doSettings(SettingsDialog::Category category) +{ + SettingsDialog* dlg = getSettingsDialog(); + if (!dlg->isVisible()) + { + dlg->setModal(false); + dlg->show(); + } + + if (category != SettingsDialog::Category::Count) + dlg->setCategory(category); +} + +ControllerSettingsDialog* MainWindow::getControllerSettingsDialog() +{ + if (!m_controller_settings_dialog) + m_controller_settings_dialog = new ControllerSettingsDialog(this); + + return m_controller_settings_dialog; +} + +void MainWindow::doControllerSettings(ControllerSettingsDialog::Category category) +{ + ControllerSettingsDialog* dlg = getControllerSettingsDialog(); + if (!dlg->isVisible()) + { + dlg->setModal(false); + dlg->show(); + } + + if (category != ControllerSettingsDialog::Category::Count) + dlg->setCategory(category); +} + +void MainWindow::startGameListEntry(const GameList::Entry* entry, std::optional save_slot, + std::optional fast_boot) +{ + std::shared_ptr params = std::make_shared(); + params->fast_boot = fast_boot; + + GameList::FillBootParametersForEntry(params.get(), entry); + + if (save_slot.has_value() && !entry->serial.empty()) + { + std::string state_filename = VMManager::GetSaveStateFileName(entry->serial.c_str(), entry->crc, save_slot.value()); + if (!FileSystem::FileExists(state_filename.c_str())) + { + QMessageBox::critical(this, tr("Error"), tr("This save state does not exist.")); + return; + } + + params->save_state = std::move(state_filename); + } + + g_emu_thread->startVM(std::move(params)); +} + +void MainWindow::setGameListEntryCoverImage(const GameList::Entry* entry) +{ + const QString filename(QFileDialog::getOpenFileName(this, tr("Select Cover Image"), QString(), + tr("All Cover Image Types (*.jpg *.jpeg *.png)"))); + if (filename.isEmpty()) + return; + + if (!GameList::GetCoverImagePathForEntry(entry).empty()) + { + if (QMessageBox::question(this, tr("Cover Already Exists"), + tr("A cover image for this game already exists, do you wish to replace it?"), + QMessageBox::Yes, QMessageBox::No) != QMessageBox::Yes) + { + return; + } + } + + const QString new_filename(QString::fromStdString(GameList::GetNewCoverImagePathForEntry(entry, filename.toUtf8().constData()))); + if (new_filename.isEmpty()) + return; + + if (QFile::exists(new_filename) && !QFile::remove(new_filename)) + { + QMessageBox::critical(this, tr("Copy Error"), tr("Failed to remove existing cover '%1'").arg(new_filename)); + return; + } + + if (!QFile::copy(filename, new_filename)) + { + QMessageBox::critical(this, tr("Copy Error"), tr("Failed to copy '%1' to '%2'").arg(filename).arg(new_filename)); + return; + } + + m_game_list_widget->refreshGridCovers(); +} + +void MainWindow::loadSaveStateSlot(s32 slot) +{ + if (m_vm_valid) + { + // easy when we're running + g_emu_thread->loadStateFromSlot(slot); + return; + } + else + { + // we're not currently running, therefore we must've right clicked in the game list + const GameList::Entry* entry = m_game_list_widget->getSelectedEntry(); + if (!entry) + return; + + startGameListEntry(entry, slot, std::nullopt); + } +} + +void MainWindow::loadSaveStateFile(const QString& filename, const QString& state_filename) +{ + if (m_vm_valid) + { + g_emu_thread->loadState(filename); + } + else + { + std::shared_ptr params = std::make_shared(); + VMManager::SetBootParametersForPath(filename.toStdString(), params.get()); + params->save_state = state_filename.toStdString(); + g_emu_thread->startVM(std::move(params)); + } +} + +static QString formatTimestampForSaveStateMenu(time_t timestamp) +{ + const QDateTime qtime(QDateTime::fromSecsSinceEpoch(static_cast(timestamp))); + return qtime.toString(QLocale::system().dateTimeFormat(QLocale::ShortFormat)); +} + +void MainWindow::populateLoadStateMenu(QMenu* menu, const QString& filename, const QString& serial, quint32 crc) +{ + if (serial.isEmpty()) + return; + + const bool is_right_click_menu = (menu != m_ui.menuLoadState); + + QAction* action = menu->addAction(is_right_click_menu ? tr("Load State File...") : tr("Load From File...")); + connect(action, &QAction::triggered, [this, filename]() { + const QString path( + QFileDialog::getOpenFileName(this, tr("Select Save State File"), QString(), tr("Save States (*.p2s)"))); + if (path.isEmpty()) + return; + + loadSaveStateFile(filename, path); + }); + + // don't include undo in the right click menu + if (!is_right_click_menu) + { + QAction* load_undo_state = menu->addAction(tr("Undo Load State")); + load_undo_state->setEnabled(false); // CanUndoLoadState() + // connect(load_undo_state, &QAction::triggered, this, &QtHostInterface::undoLoadState); + menu->addSeparator(); + } + + const QByteArray game_serial_utf8(serial.toUtf8()); + std::string state_filename; + FILESYSTEM_STAT_DATA sd; + if (is_right_click_menu) + { + state_filename = VMManager::GetSaveStateFileName(game_serial_utf8.constData(), crc, -1); + if (FileSystem::StatFile(state_filename.c_str(), &sd)) + { + action = menu->addAction(tr("Resume (%2)").arg(formatTimestampForSaveStateMenu(sd.ModificationTime))); + connect(action, &QAction::triggered, [this]() { loadSaveStateSlot(-1); }); + + // Make bold to indicate it's the default choice when double-clicking + if (VMManager::ShouldSaveResumeState()) + QtUtils::MarkActionAsDefault(action); + } + } + + for (s32 i = 1; i <= NUM_SAVE_STATE_SLOTS; i++) + { + FILESYSTEM_STAT_DATA sd; + state_filename = VMManager::GetSaveStateFileName(game_serial_utf8.constData(), crc, i); + if (!FileSystem::StatFile(state_filename.c_str(), &sd)) + continue; + + action = menu->addAction(tr("Save Slot %1 (%2)").arg(i).arg(formatTimestampForSaveStateMenu(sd.ModificationTime))); + connect(action, &QAction::triggered, [this, i]() { loadSaveStateSlot(i); }); + } +} + +void MainWindow::populateSaveStateMenu(QMenu* menu, const QString& serial, quint32 crc) +{ + if (serial.isEmpty()) + return; + + connect(menu->addAction(tr("Save To File...")), &QAction::triggered, [this]() { + const QString path( + QFileDialog::getSaveFileName(this, tr("Select Save State File"), QString(), tr("Save States (*.p2s)"))); + if (path.isEmpty()) + return; + + g_emu_thread->saveState(path); + }); + + menu->addSeparator(); + + const QByteArray game_serial_utf8(serial.toUtf8()); + for (s32 i = 1; i <= NUM_SAVE_STATE_SLOTS; i++) + { + std::string filename(VMManager::GetSaveStateFileName(game_serial_utf8.constData(), crc, i)); + FILESYSTEM_STAT_DATA sd; + QString timestamp; + if (FileSystem::StatFile(filename.c_str(), &sd)) + timestamp = formatTimestampForSaveStateMenu(sd.ModificationTime); + else + timestamp = tr("Empty"); + + QString title(tr("Save Slot %1 (%2)").arg(i).arg(timestamp)); + connect(menu->addAction(title), &QAction::triggered, [this, i]() { g_emu_thread->saveStateToSlot(i); }); + } +} + +void MainWindow::updateSaveStateMenus(const QString& filename, const QString& serial, quint32 crc) +{ + const bool load_enabled = !serial.isEmpty(); + const bool save_enabled = !serial.isEmpty() && m_vm_valid; + m_ui.menuLoadState->clear(); + m_ui.menuLoadState->setEnabled(load_enabled); + m_ui.actionLoadState->setEnabled(load_enabled); + m_ui.menuSaveState->clear(); + m_ui.menuSaveState->setEnabled(save_enabled); + m_ui.actionSaveState->setEnabled(save_enabled); + m_save_states_invalidated = false; + if (load_enabled) + populateLoadStateMenu(m_ui.menuLoadState, filename, serial, crc); + if (save_enabled) + populateSaveStateMenu(m_ui.menuSaveState, serial, crc); +} diff --git a/pcsx2-qt/MainWindow.h b/pcsx2-qt/MainWindow.h new file mode 100644 index 000000000..b6bd5db76 --- /dev/null +++ b/pcsx2-qt/MainWindow.h @@ -0,0 +1,177 @@ +/* PCSX2 - PS2 Emulator for PCs + * Copyright (C) 2002-2022 PCSX2 Dev Team + * + * PCSX2 is free software: you can redistribute it and/or modify it under the terms + * of the GNU Lesser General Public License as published by the Free Software Found- + * ation, either version 3 of the License, or (at your option) any later version. + * + * PCSX2 is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; + * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR + * PURPOSE. See the GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License along with PCSX2. + * If not, see . + */ + +#pragma once + +#include +#include + +#include "Settings/ControllerSettingsDialog.h" +#include "Settings/SettingsDialog.h" +#include "ui_MainWindow.h" + +class QProgressBar; + +class DisplayWidget; +class DisplayContainer; +class GameListWidget; +class ControllerSettingsDialog; + +class EmuThread; + +namespace GameList +{ + struct Entry; +} + +class MainWindow final : public QMainWindow +{ + Q_OBJECT + +public: + static const char* DEFAULT_THEME_NAME; + +public: + explicit MainWindow(const QString& unthemed_style_name); + ~MainWindow(); + + void initialize(); + void connectVMThreadSignals(EmuThread* thread); + +public Q_SLOTS: + void refreshGameList(bool invalidate_cache); + void invalidateSaveStateCache(); + void reportError(const QString& title, const QString& message); + +private Q_SLOTS: + DisplayWidget* createDisplay(bool fullscreen, bool render_to_main); + DisplayWidget* updateDisplay(bool fullscreen, bool render_to_main); + void displayResizeRequested(qint32 width, qint32 height); + void destroyDisplay(); + void focusDisplayWidget(); + + void onGameListRefreshComplete(); + void onGameListRefreshProgress(const QString& status, int current, int total); + void onGameListSelectionChanged(); + void onGameListEntryActivated(); + void onGameListEntryContextMenuRequested(const QPoint& point); + + void onStartFileActionTriggered(); + void onStartBIOSActionTriggered(); + void onChangeDiscFromFileActionTriggered(); + void onChangeDiscFromGameListActionTriggered(); + void onChangeDiscFromDeviceActionTriggered(); + void onChangeDiscMenuAboutToShow(); + void onChangeDiscMenuAboutToHide(); + void onLoadStateMenuAboutToShow(); + void onSaveStateMenuAboutToShow(); + void onViewToolbarActionToggled(bool checked); + void onViewLockToolbarActionToggled(bool checked); + void onViewStatusBarActionToggled(bool checked); + void onViewGameListActionTriggered(); + void onViewGameGridActionTriggered(); + void onViewSystemDisplayTriggered(); + void onViewGamePropertiesActionTriggered(); + void onGitHubRepositoryActionTriggered(); + void onSupportForumsActionTriggered(); + void onDiscordServerActionTriggered(); + void onAboutActionTriggered(); + void onCheckForUpdatesActionTriggered(); + void onToolsOpenDataDirectoryTriggered(); + void onThemeChanged(); + void onThemeChangedFromSettings(); + + void onVMStarting(); + void onVMStarted(); + void onVMPaused(); + void onVMResumed(); + void onVMStopped(); + + void onGameChanged(const QString& path, const QString& serial, const QString& name, quint32 crc); + + void recreate(); + +protected: + void closeEvent(QCloseEvent* event); + +private: + enum : s32 + { + NUM_SAVE_STATE_SLOTS = 10, + }; + + void setupAdditionalUi(); + void connectSignals(); + void setStyleFromSettings(); + void setIconThemeFromSettings(); + + void saveStateToConfig(); + void restoreStateFromConfig(); + + void updateEmulationActions(bool starting, bool running); + void updateWindowTitle(); + void setProgressBar(int current, int total); + void clearProgressBar(); + + bool isShowingGameList() const; + void switchToGameListView(); + void switchToEmulationView(); + + QWidget* getDisplayContainer() const; + void saveDisplayWindowGeometryToConfig(); + void restoreDisplayWindowGeometryFromConfig(); + void destroyDisplayWidget(); + void setDisplayFullscreen(const std::string& fullscreen_mode); + + SettingsDialog* getSettingsDialog(); + void doSettings(SettingsDialog::Category category = SettingsDialog::Category::Count); + + ControllerSettingsDialog* getControllerSettingsDialog(); + void doControllerSettings(ControllerSettingsDialog::Category category = ControllerSettingsDialog::Category::Count); + + void startGameListEntry(const GameList::Entry* entry, std::optional save_slot = std::nullopt, + std::optional fast_boot = std::nullopt); + void setGameListEntryCoverImage(const GameList::Entry* entry); + + void loadSaveStateSlot(s32 slot); + void loadSaveStateFile(const QString& filename, const QString& state_filename); + void populateLoadStateMenu(QMenu* menu, const QString& filename, const QString& serial, quint32 crc); + void populateSaveStateMenu(QMenu* menu, const QString& serial, quint32 crc); + void updateSaveStateMenus(const QString& filename, const QString& serial, quint32 crc); + + Ui::MainWindow m_ui; + + QString m_unthemed_style_name; + + GameListWidget* m_game_list_widget = nullptr; + DisplayWidget* m_display_widget = nullptr; + DisplayContainer* m_display_container = nullptr; + + SettingsDialog* m_settings_dialog = nullptr; + ControllerSettingsDialog* m_controller_settings_dialog = nullptr; + + QProgressBar* m_status_progress_widget = nullptr; + + QString m_current_disc_path; + QString m_current_game_serial; + QString m_current_game_name; + quint32 m_current_game_crc; + bool m_vm_valid = false; + bool m_vm_paused = false; + bool m_save_states_invalidated = false; + bool m_was_focused_on_container_switch = false; +}; + +extern MainWindow* g_main_window; diff --git a/pcsx2-qt/MainWindow.ui b/pcsx2-qt/MainWindow.ui new file mode 100644 index 000000000..1d9240603 --- /dev/null +++ b/pcsx2-qt/MainWindow.ui @@ -0,0 +1,648 @@ + + + MainWindow + + + + 0 + 0 + 800 + 700 + + + + true + + + PCSX2 + + + + :/icons/AppIcon.png:/icons/AppIcon.png + + + + 0 + + + + + + + + 0 + 0 + 800 + 22 + + + + + &System + + + + Change Disc + + + + + + + + + + + + + + Cheats + + + + + + + + Load State + + + + + + + + Save State + + + + + + + + + + + + + + + + + + + + + + + + + S&ettings + + + + + + + + + + + + + + + + + + + &Help + + + + + + + + + + + + + &Debug + + + + Switch Renderer + + + + + + + + + + + &View + + + + &Window Size + + + + + + + + + + + + + + + + + + + + + + + + + &Tools + + + + + + + + + + + + + toolBar + + + + 32 + 32 + + + + Qt::ToolButtonTextUnderIcon + + + TopToolBarArea + + + false + + + false + + + + + + + + + + + + + + + + + + + + + + + + Start &File... + + + + + + + + Start &Disc... + + + + + + + + Start &BIOS + + + + + + + + &Scan For New Games + + + + + + + + &Rescan All Games + + + + + + + + Shut &Down + + + + + + + + &Reset + + + + + + + + true + + + &Pause + + + + + + + + &Load State + + + + + + + + &Save State + + + + + + + + E&xit + + + + + + + + &BIOS + + + + + + + + System + + + + + + + + Emulation + + + + + + + + &Controllers + + + + + + + + &Hotkeys + + + + + + + + &Graphics + + + + + + + + &Post-Processing Settings... + + + + + Fullscreen + + + + + + + + Resolution Scale + + + + + &GitHub Repository... + + + + + Support &Forums... + + + + + &Discord Server... + + + + + + + + Check for &Updates... + + + + + + :/icons/QT.png:/icons/QT.png + + + About &Qt... + + + + + &About PCSX2... + + + + + Change Disc... + + + + + + + + Cheats... + + + + + + + + &Audio + + + + + + + + Game List + + + + + + + + Interface + + + + + + + + Add Game Directory... + + + + + + + + &Settings... + + + + + + + + From File... + + + + + From Device... + + + + + From Game List... + + + + + Remove Disc + + + + + Global State + + + + + &Screenshot + + + + + + + + &Memory Cards + + + + + + + + true + + + true + + + &Toolbar + + + + + true + + + false + + + Lock Toolbar + + + + + true + + + true + + + &Status Bar + + + + + Game &List + + + + + + + + false + + + System &Display + + + + + + + + false + + + Game &Properties + + + + + + + + Game &Grid + + + + + + + + true + + + true + + + Show Titles (Grid View) + + + + + Zoom &In (Grid View) + + + Ctrl++ + + + + + Zoom &Out (Grid View) + + + Ctrl+- + + + + + Refresh &Covers (Grid View) + + + + + Open Memory Card Directory... + + + + + Open Data Directory... + + + + + Toggle Software Rendering + + + + + Reload Cheats/Patches + + + + + + + + diff --git a/pcsx2-qt/PrecompiledHeader.cpp b/pcsx2-qt/PrecompiledHeader.cpp new file mode 100644 index 000000000..a11c9e9a2 --- /dev/null +++ b/pcsx2-qt/PrecompiledHeader.cpp @@ -0,0 +1,16 @@ +/* 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" diff --git a/pcsx2-qt/PrecompiledHeader.h b/pcsx2-qt/PrecompiledHeader.h new file mode 100644 index 000000000..518d3f9cc --- /dev/null +++ b/pcsx2-qt/PrecompiledHeader.h @@ -0,0 +1,21 @@ +/* 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 "pcsx2/PrecompiledHeader.h" + +// Needed because of moc shenanigans with pch. +#include + +#include \ No newline at end of file diff --git a/pcsx2-qt/QtHost.cpp b/pcsx2-qt/QtHost.cpp new file mode 100644 index 000000000..e44dafb43 --- /dev/null +++ b/pcsx2-qt/QtHost.cpp @@ -0,0 +1,506 @@ +/* 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 "common/Assertions.h" +#include "common/Console.h" +#include "common/FileSystem.h" +#include "common/SettingsWrapper.h" +#include "common/StringUtil.h" + +#include "pcsx2/Frontend/GameList.h" +#include "pcsx2/Frontend/INISettingsInterface.h" +#include "pcsx2/HostSettings.h" +#include "pcsx2/PAD/Host/PAD.h" + +#include +#include + +#include "EmuThread.h" +#include "GameList/GameListWidget.h" +#include "MainWindow.h" +#include "QtHost.h" + +#include "pcsx2/DebugTools/Debug.h" + +static constexpr u32 SETTINGS_VERSION = 1; +static constexpr u32 SETTINGS_SAVE_DELAY = 1000; + +////////////////////////////////////////////////////////////////////////// +// Local function declarations +////////////////////////////////////////////////////////////////////////// +static void InitializeWxRubbish(); +static bool InitializeConfig(); +static void SetDefaultConfig(); +static void QueueSettingsSave(); +static void SaveSettings(); + +////////////////////////////////////////////////////////////////////////// +// Local variable declarations +////////////////////////////////////////////////////////////////////////// +static std::unique_ptr s_settings_save_timer; +static std::unique_ptr s_base_settings_interface; + +////////////////////////////////////////////////////////////////////////// +// Initialization/Shutdown +////////////////////////////////////////////////////////////////////////// + +bool QtHost::Initialize() +{ + qRegisterMetaType>(); + qRegisterMetaType(); + qRegisterMetaType(); + qRegisterMetaType(); + + InitializeWxRubbish(); + if (!InitializeConfig()) + { + Console.WriteLn("Failed to initialize config."); + return false; + } + + return true; +} + +void QtHost::Shutdown() {} + +static bool SetCriticalFolders() +{ + std::string program_path(FileSystem::GetProgramPath()); + EmuFolders::AppRoot = wxDirName(wxFileName(StringUtil::UTF8StringToWxString(program_path))); + EmuFolders::DataRoot = EmuFolders::AppRoot; +#ifndef _WIN32 + const char* homedir = getenv("HOME"); + if (homedir) + EmuFolders::DataRoot = Path::Combine(wxString::FromUTF8(homedir), wxString(L"PCSX2")); +#endif + + EmuFolders::Settings = EmuFolders::DataRoot.Combine(wxDirName(L"inis")); + EmuFolders::Resources = EmuFolders::AppRoot.Combine(wxDirName(L"resources")); + + // the resources directory should exist, bail out if not + if (!EmuFolders::Resources.Exists()) + { + QMessageBox::critical(nullptr, QStringLiteral("Error"), + QStringLiteral("Resources directory is missing, your installation is incomplete.")); + return false; + } + + return true; +} + +void QtHost::UpdateFolders() +{ + // TODO: This should happen with the VM thread paused. + auto lock = Host::GetSettingsLock(); + EmuFolders::LoadConfig(*s_base_settings_interface.get()); + EmuFolders::EnsureFoldersExist(); +} + +bool InitializeConfig() +{ + if (!SetCriticalFolders()) + return false; + + const std::string path(Path::CombineStdString(EmuFolders::Settings, "PCSX2.ini")); + s_base_settings_interface = std::make_unique(std::move(path)); + Host::Internal::SetBaseSettingsLayer(s_base_settings_interface.get()); + + uint settings_version; + if (!s_base_settings_interface->Load() || + !s_base_settings_interface->GetUIntValue("UI", "SettingsVersion", &settings_version) || + settings_version != SETTINGS_VERSION) + { + QMessageBox::critical( + g_main_window, qApp->translate("QtHost", "Settings Reset"), + qApp->translate("QtHost", "Settings do not exist or are the incorrect version, resetting to defaults.")); + SetDefaultConfig(); + s_base_settings_interface->Save(); + } + + // TODO: Handle reset to defaults if load fails. + EmuFolders::LoadConfig(*s_base_settings_interface.get()); + EmuFolders::EnsureFoldersExist(); + QtHost::UpdateLogging(); + return true; +} + +void SetDefaultConfig() +{ + EmuConfig = Pcsx2Config(); + EmuFolders::SetDefaults(); + + SettingsInterface& si = *s_base_settings_interface.get(); + si.SetUIntValue("UI", "SettingsVersion", SETTINGS_VERSION); + + { + SettingsSaveWrapper wrapper(si); + EmuConfig.LoadSave(wrapper); + } + + EmuFolders::Save(si); + PAD::SetDefaultConfig(si); +} + +std::string QtHost::GetBaseStringSettingValue(const char* section, const char* key, const char* default_value /*= ""*/) +{ + auto lock = Host::GetSettingsLock(); + return s_base_settings_interface->GetStringValue(section, key, default_value); +} + +bool QtHost::GetBaseBoolSettingValue(const char* section, const char* key, bool default_value /*= false*/) +{ + auto lock = Host::GetSettingsLock(); + return s_base_settings_interface->GetBoolValue(section, key, default_value); +} + +int QtHost::GetBaseIntSettingValue(const char* section, const char* key, int default_value /*= 0*/) +{ + auto lock = Host::GetSettingsLock(); + return s_base_settings_interface->GetIntValue(section, key, default_value); +} + +float QtHost::GetBaseFloatSettingValue(const char* section, const char* key, float default_value /*= 0.0f*/) +{ + auto lock = Host::GetSettingsLock(); + return s_base_settings_interface->GetFloatValue(section, key, default_value); +} + +std::vector QtHost::GetBaseStringListSetting(const char* section, const char* key) +{ + auto lock = Host::GetSettingsLock(); + return s_base_settings_interface->GetStringList(section, key); +} + +void QtHost::SetBaseBoolSettingValue(const char* section, const char* key, bool value) +{ + auto lock = Host::GetSettingsLock(); + s_base_settings_interface->SetBoolValue(section, key, value); + QueueSettingsSave(); +} + +void QtHost::SetBaseIntSettingValue(const char* section, const char* key, int value) +{ + auto lock = Host::GetSettingsLock(); + s_base_settings_interface->SetIntValue(section, key, value); + QueueSettingsSave(); +} + +void QtHost::SetBaseFloatSettingValue(const char* section, const char* key, float value) +{ + auto lock = Host::GetSettingsLock(); + s_base_settings_interface->SetFloatValue(section, key, value); + QueueSettingsSave(); +} + +void QtHost::SetBaseStringSettingValue(const char* section, const char* key, const char* value) +{ + auto lock = Host::GetSettingsLock(); + s_base_settings_interface->SetStringValue(section, key, value); + QueueSettingsSave(); +} + +void QtHost::SetBaseStringListSettingValue(const char* section, const char* key, const std::vector& values) +{ + auto lock = Host::GetSettingsLock(); + s_base_settings_interface->SetStringList(section, key, values); + QueueSettingsSave(); +} + +bool QtHost::AddBaseValueToStringList(const char* section, const char* key, const char* value) +{ + auto lock = Host::GetSettingsLock(); + if (!s_base_settings_interface->AddToStringList(section, key, value)) + return false; + + QueueSettingsSave(); + return true; +} + +bool QtHost::RemoveBaseValueFromStringList(const char* section, const char* key, const char* value) +{ + auto lock = Host::GetSettingsLock(); + if (!s_base_settings_interface->RemoveFromStringList(section, key, value)) + return false; + + QueueSettingsSave(); + return true; +} + +void QtHost::RemoveBaseSettingValue(const char* section, const char* key) +{ + auto lock = Host::GetSettingsLock(); + s_base_settings_interface->DeleteValue(section, key); + QueueSettingsSave(); +} + +void SaveSettings() +{ + pxAssertRel(!g_emu_thread->isOnEmuThread(), "Saving should happen on the UI thread."); + + { + auto lock = Host::GetSettingsLock(); + if (!s_base_settings_interface->Save()) + Console.Error("Failed to save settings."); + } + + s_settings_save_timer->deleteLater(); + s_settings_save_timer.release(); +} + +void QueueSettingsSave() +{ + if (s_settings_save_timer) + return; + + s_settings_save_timer = std::make_unique(); + s_settings_save_timer->connect(s_settings_save_timer.get(), &QTimer::timeout, SaveSettings); + s_settings_save_timer->setSingleShot(true); + s_settings_save_timer->start(SETTINGS_SAVE_DELAY); +} + +std::optional> Host::ReadResourceFile(const char* filename) +{ + const std::string path(Path::CombineStdString(EmuFolders::Resources, filename)); + std::optional> ret(FileSystem::ReadBinaryFile(path.c_str())); + if (!ret.has_value()) + Console.Error("Failed to read resource file '%s'", filename); + return ret; +} + +std::optional Host::ReadResourceFileToString(const char* filename) +{ + const std::string path(Path::CombineStdString(EmuFolders::Resources, filename)); + std::optional ret(FileSystem::ReadFileToString(path.c_str())); + if (!ret.has_value()) + Console.Error("Failed to read resource file to string '%s'", filename); + return ret; +} + +void Host::ReportErrorAsync(const std::string_view& title, const std::string_view& message) +{ + if (!title.empty() && !message.empty()) + { + Console.Error("ReportErrorAsync: %*s: %*s", + static_cast(title.size()), title.data(), + static_cast(message.size()), message.data()); + } + else if (!message.empty()) + { + Console.Error("ReportErrorAsync: %*s", + static_cast(message.size()), message.data()); + } + + QMetaObject::invokeMethod(g_main_window, "reportError", Qt::QueuedConnection, + Q_ARG(const QString&, title.empty() ? QString() : QString::fromUtf8(title.data(), title.size())), + Q_ARG(const QString&, message.empty() ? QString() : QString::fromUtf8(message.data(), message.size()))); +} + +void Host::OnInputDeviceConnected(const std::string_view& identifier, const std::string_view& device_name) +{ + emit g_emu_thread->onInputDeviceConnected( + identifier.empty() ? QString() : QString::fromUtf8(identifier.data(), identifier.size()), + device_name.empty() ? QString() : QString::fromUtf8(device_name.data(), device_name.size())); +} + +void Host::OnInputDeviceDisconnected(const std::string_view& identifier) +{ + emit g_emu_thread->onInputDeviceDisconnected( + identifier.empty() ? QString() : QString::fromUtf8(identifier.data(), identifier.size())); +} + +////////////////////////////////////////////////////////////////////////// +// Interface Stuff +////////////////////////////////////////////////////////////////////////// + +const IConsoleWriter* PatchesCon = &Console; + +void LoadAllPatchesAndStuff(const Pcsx2Config& cfg) +{ + // FIXME +} + +void PatchesVerboseReset() +{ + // FIXME +} + +// Replacement for Console so we actually get output to our console window on Windows. +#ifdef _WIN32 +#include "common/RedtapeWindows.h" + +static bool s_debugger_attached = false; +static bool s_console_handle_set = false; +static HANDLE s_console_handle = nullptr; + +static void __concall ConsoleWinQt_SetTitle(const wxString& title) +{ + SetConsoleTitleW(title.wc_str()); +} + +static void __concall ConsoleWinQt_DoSetColor(ConsoleColors color) +{ + if (!s_console_handle) + return; + + static constexpr WORD colors[ConsoleColors_Count] = { + FOREGROUND_RED | FOREGROUND_GREEN | FOREGROUND_BLUE, // default + FOREGROUND_RED | FOREGROUND_GREEN | FOREGROUND_BLUE, // black + FOREGROUND_GREEN, // green + FOREGROUND_RED, // red + FOREGROUND_BLUE, // blue + FOREGROUND_RED | FOREGROUND_BLUE, // magenta + FOREGROUND_RED | FOREGROUND_GREEN, // orange + FOREGROUND_RED | FOREGROUND_GREEN | FOREGROUND_BLUE, // gray + FOREGROUND_BLUE | FOREGROUND_INTENSITY, // cyan + FOREGROUND_RED | FOREGROUND_GREEN | FOREGROUND_INTENSITY, // yellow + FOREGROUND_RED | FOREGROUND_GREEN | FOREGROUND_BLUE | FOREGROUND_INTENSITY, // white + FOREGROUND_RED | FOREGROUND_GREEN | FOREGROUND_BLUE, // strong black + FOREGROUND_RED | FOREGROUND_INTENSITY, // strong red + FOREGROUND_GREEN | FOREGROUND_INTENSITY, // strong green + FOREGROUND_BLUE | FOREGROUND_INTENSITY, // strong blue + FOREGROUND_RED | FOREGROUND_BLUE | FOREGROUND_INTENSITY, // strong magenta + FOREGROUND_RED | FOREGROUND_GREEN | FOREGROUND_INTENSITY, // strong orange + FOREGROUND_RED | FOREGROUND_GREEN | FOREGROUND_BLUE, // strong gray + FOREGROUND_BLUE | FOREGROUND_INTENSITY, // strong cyan + FOREGROUND_RED | FOREGROUND_GREEN | FOREGROUND_INTENSITY, // strong yellow + FOREGROUND_RED | FOREGROUND_GREEN | FOREGROUND_BLUE | FOREGROUND_INTENSITY, // strong white + }; + + SetConsoleTextAttribute(s_console_handle, colors[static_cast(color)]); +} + +static void __concall ConsoleWinQt_Newline() +{ + if (!s_console_handle) + return; + + if (s_debugger_attached) + OutputDebugStringW(L"\n"); + + DWORD written; + WriteConsoleW(s_console_handle, L"\n", 1, &written, nullptr); +} + +static void __concall ConsoleWinQt_DoWrite(const wxString& fmt) +{ + if (!s_console_handle) + return; + + if (s_debugger_attached) + OutputDebugStringW(fmt.wc_str()); + + DWORD written; + WriteConsoleW(s_console_handle, fmt.wc_str(), static_cast(fmt.size()), &written, nullptr); +} + +static void __concall ConsoleWinQt_DoWriteLn(const wxString& fmt) +{ + if (!s_console_handle) + return; + + if (s_debugger_attached) + { + OutputDebugStringW(fmt.wc_str()); + OutputDebugStringW(L"\n"); + } + + DWORD written; + WriteConsoleW(s_console_handle, fmt.wc_str(), static_cast(fmt.size()), &written, nullptr); + WriteConsoleW(s_console_handle, L"\n", 1, &written, nullptr); +} + +static const IConsoleWriter ConsoleWriter_WinQt = + { + ConsoleWinQt_DoWrite, + ConsoleWinQt_DoWriteLn, + ConsoleWinQt_DoSetColor, + + ConsoleWinQt_DoWrite, + ConsoleWinQt_Newline, + ConsoleWinQt_SetTitle, +}; +#endif + +void QtHost::UpdateLogging() +{ + // TODO: Make this an actual option. + bool console_logging_enabled = false; + +#if defined(_DEBUG) || defined(PCSX2_DEVBUILD) + console_logging_enabled = true; +#endif + + DevConWriterEnabled = console_logging_enabled; + SysConsole.eeConsole.Enabled = console_logging_enabled; + SysConsole.iopConsole.Enabled = console_logging_enabled; + + if (console_logging_enabled) + { +#ifdef _WIN32 + s_debugger_attached = IsDebuggerPresent(); + if (!s_console_handle_set) + { + bool handle_valid = (GetConsoleWindow() != NULL); + if (!handle_valid) + { + AllocConsole(); + handle_valid = (freopen("CONIN$", "r", stdin) != nullptr); + handle_valid = (freopen("CONOUT$", "w", stdout) != nullptr); + handle_valid = (freopen("CONOUT$", "w", stderr) != nullptr); + } + + if (handle_valid) + { + s_console_handle = GetStdHandle(STD_OUTPUT_HANDLE); + s_console_handle_set = true; + } + } + + if (!s_console_handle && !s_debugger_attached) + Console_SetActiveHandler(ConsoleWriter_Null); + else + Console_SetActiveHandler(ConsoleWriter_WinQt); +#else + Console_SetActiveHandler(ConsoleWriter_Stdout); +#endif + } + else + { + Console_SetActiveHandler(ConsoleWriter_Null); + } +} + +#include + +#ifdef _WIN32 +extern "C" HINSTANCE wxGetInstance(); +extern void wxSetInstance(HINSTANCE hInst); +#endif + +void InitializeWxRubbish() +{ + wxLog::DoCreateOnDemand(); + wxLog::GetActiveTarget(); + +#ifdef _WIN32 + if (!wxGetInstance()) + wxSetInstance(::GetModuleHandle(NULL)); +#endif // _WIN32 + + wxModule::RegisterModules(); + wxModule::InitializeModules(); +} diff --git a/pcsx2-qt/QtHost.h b/pcsx2-qt/QtHost.h new file mode 100644 index 000000000..91108cde8 --- /dev/null +++ b/pcsx2-qt/QtHost.h @@ -0,0 +1,54 @@ +/* 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 . + */ + +#pragma once +#include "pcsx2/Host.h" +#include "pcsx2/HostDisplay.h" +#include "pcsx2/HostSettings.h" +#include "pcsx2/Frontend/InputManager.h" +#include "pcsx2/VMManager.h" +#include + +class SettingsInterface; + +class EmuThread; + +Q_DECLARE_METATYPE(GSRendererType); +Q_DECLARE_METATYPE(std::shared_ptr); +Q_DECLARE_METATYPE(InputBindingKey); + +namespace QtHost +{ + bool Initialize(); + void Shutdown(); + + void UpdateFolders(); + void UpdateLogging(); + + /// Thread-safe settings access. + std::string GetBaseStringSettingValue(const char* section, const char* key, const char* default_value = ""); + bool GetBaseBoolSettingValue(const char* section, const char* key, bool default_value = false); + int GetBaseIntSettingValue(const char* section, const char* key, int default_value = 0); + float GetBaseFloatSettingValue(const char* section, const char* key, float default_value = 0.0f); + std::vector GetBaseStringListSetting(const char* section, const char* key); + void SetBaseBoolSettingValue(const char* section, const char* key, bool value); + void SetBaseIntSettingValue(const char* section, const char* key, int value); + void SetBaseFloatSettingValue(const char* section, const char* key, float value); + void SetBaseStringSettingValue(const char* section, const char* key, const char* value); + void SetBaseStringListSettingValue(const char* section, const char* key, const std::vector& values); + bool AddBaseValueToStringList(const char* section, const char* key, const char* value); + bool RemoveBaseValueFromStringList(const char* section, const char* key, const char* value); + void RemoveBaseSettingValue(const char* section, const char* key); +} // namespace QtHost diff --git a/pcsx2-qt/QtKeyCodes.cpp b/pcsx2-qt/QtKeyCodes.cpp new file mode 100644 index 000000000..525652cef --- /dev/null +++ b/pcsx2-qt/QtKeyCodes.cpp @@ -0,0 +1,486 @@ +/* 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 "pcsx2/Frontend/InputManager.h" + +struct KeyCodeName +{ + int code; + const char* name; +}; + +static constexpr KeyCodeName s_qt_key_names[] = { + {Qt::Key_Escape, "Escape"}, + {Qt::Key_Tab, "Tab"}, + {Qt::Key_Backtab, "Backtab"}, + {Qt::Key_Backspace, "Backspace"}, + {Qt::Key_Return, "Return"}, + {Qt::Key_Enter, "Enter"}, + {Qt::Key_Insert, "Insert"}, + {Qt::Key_Delete, "Delete"}, + {Qt::Key_Pause, "Pause"}, + {Qt::Key_Print, "Print"}, + {Qt::Key_SysReq, "SysReq"}, + {Qt::Key_Clear, "Clear"}, + {Qt::Key_Home, "Home"}, + {Qt::Key_End, "End"}, + {Qt::Key_Left, "Left"}, + {Qt::Key_Up, "Up"}, + {Qt::Key_Right, "Right"}, + {Qt::Key_Down, "Down"}, + {Qt::Key_PageUp, "PageUp"}, + {Qt::Key_PageDown, "PageDown"}, + {Qt::Key_Shift, "Shift"}, + {Qt::Key_Control, "Control"}, + {Qt::Key_Meta, "Meta"}, + {Qt::Key_Alt, "Alt"}, + {Qt::Key_CapsLock, "CapsLock"}, + {Qt::Key_NumLock, "NumLock"}, + {Qt::Key_ScrollLock, "ScrollLock"}, + {Qt::Key_F1, "F1"}, + {Qt::Key_F2, "F2"}, + {Qt::Key_F3, "F3"}, + {Qt::Key_F4, "F4"}, + {Qt::Key_F5, "F5"}, + {Qt::Key_F6, "F6"}, + {Qt::Key_F7, "F7"}, + {Qt::Key_F8, "F8"}, + {Qt::Key_F9, "F9"}, + {Qt::Key_F10, "F10"}, + {Qt::Key_F11, "F11"}, + {Qt::Key_F12, "F12"}, + {Qt::Key_F13, "F13"}, + {Qt::Key_F14, "F14"}, + {Qt::Key_F15, "F15"}, + {Qt::Key_F16, "F16"}, + {Qt::Key_F17, "F17"}, + {Qt::Key_F18, "F18"}, + {Qt::Key_F19, "F19"}, + {Qt::Key_F20, "F20"}, + {Qt::Key_F21, "F21"}, + {Qt::Key_F22, "F22"}, + {Qt::Key_F23, "F23"}, + {Qt::Key_F24, "F24"}, + {Qt::Key_F25, "F25"}, + {Qt::Key_F26, "F26"}, + {Qt::Key_F27, "F27"}, + {Qt::Key_F28, "F28"}, + {Qt::Key_F29, "F29"}, + {Qt::Key_F30, "F30"}, + {Qt::Key_F31, "F31"}, + {Qt::Key_F32, "F32"}, + {Qt::Key_F33, "F33"}, + {Qt::Key_F34, "F34"}, + {Qt::Key_F35, "F35"}, + {Qt::Key_Super_L, "Super_L"}, + {Qt::Key_Super_R, "Super_R"}, + {Qt::Key_Menu, "Menu"}, + {Qt::Key_Hyper_L, "Hyper_L"}, + {Qt::Key_Hyper_R, "Hyper_R"}, + {Qt::Key_Help, "Help"}, + {Qt::Key_Direction_L, "Direction_L"}, + {Qt::Key_Direction_R, "Direction_R"}, + {Qt::Key_Space, "Space"}, + {Qt::Key_Any, "Any"}, + {Qt::Key_Exclam, "Exclam"}, + {Qt::Key_QuoteDbl, "QuoteDbl"}, + {Qt::Key_NumberSign, "NumberSign"}, + {Qt::Key_Dollar, "Dollar"}, + {Qt::Key_Percent, "Percent"}, + {Qt::Key_Ampersand, "Ampersand"}, + {Qt::Key_Apostrophe, "Apostrophe"}, + {Qt::Key_ParenLeft, "ParenLeft"}, + {Qt::Key_ParenRight, "ParenRight"}, + {Qt::Key_Asterisk, "Asterisk"}, + {Qt::Key_Plus, "Plus"}, + {Qt::Key_Comma, "Comma"}, + {Qt::Key_Minus, "Minus"}, + {Qt::Key_Period, "Period"}, + {Qt::Key_Slash, "Slash"}, + {Qt::Key_0, "0"}, + {Qt::Key_1, "1"}, + {Qt::Key_2, "2"}, + {Qt::Key_3, "3"}, + {Qt::Key_4, "4"}, + {Qt::Key_5, "5"}, + {Qt::Key_6, "6"}, + {Qt::Key_7, "7"}, + {Qt::Key_8, "8"}, + {Qt::Key_9, "9"}, + {Qt::Key_Colon, "Colon"}, + {Qt::Key_Semicolon, "Semicolon"}, + {Qt::Key_Less, "Less"}, + {Qt::Key_Equal, "Equal"}, + {Qt::Key_Greater, "Greater"}, + {Qt::Key_Question, "Question"}, + {Qt::Key_At, "At"}, + {Qt::Key_A, "A"}, + {Qt::Key_B, "B"}, + {Qt::Key_C, "C"}, + {Qt::Key_D, "D"}, + {Qt::Key_E, "E"}, + {Qt::Key_F, "F"}, + {Qt::Key_G, "G"}, + {Qt::Key_H, "H"}, + {Qt::Key_I, "I"}, + {Qt::Key_J, "J"}, + {Qt::Key_K, "K"}, + {Qt::Key_L, "L"}, + {Qt::Key_M, "M"}, + {Qt::Key_N, "N"}, + {Qt::Key_O, "O"}, + {Qt::Key_P, "P"}, + {Qt::Key_Q, "Q"}, + {Qt::Key_R, "R"}, + {Qt::Key_S, "S"}, + {Qt::Key_T, "T"}, + {Qt::Key_U, "U"}, + {Qt::Key_V, "V"}, + {Qt::Key_W, "W"}, + {Qt::Key_X, "X"}, + {Qt::Key_Y, "Y"}, + {Qt::Key_Z, "Z"}, + {Qt::Key_BracketLeft, "BracketLeft"}, + {Qt::Key_Backslash, "Backslash"}, + {Qt::Key_BracketRight, "BracketRight"}, + {Qt::Key_AsciiCircum, "AsciiCircum"}, + {Qt::Key_Underscore, "Underscore"}, + {Qt::Key_QuoteLeft, "QuoteLeft"}, + {Qt::Key_BraceLeft, "BraceLeft"}, + {Qt::Key_Bar, "Bar"}, + {Qt::Key_BraceRight, "BraceRight"}, + {Qt::Key_AsciiTilde, "AsciiTilde"}, + {Qt::Key_nobreakspace, "nobreakspace"}, + {Qt::Key_exclamdown, "exclamdown"}, + {Qt::Key_cent, "cent"}, + {Qt::Key_sterling, "sterling"}, + {Qt::Key_currency, "currency"}, + {Qt::Key_yen, "yen"}, + {Qt::Key_brokenbar, "brokenbar"}, + {Qt::Key_section, "section"}, + {Qt::Key_diaeresis, "diaeresis"}, + {Qt::Key_copyright, "copyright"}, + {Qt::Key_ordfeminine, "ordfeminine"}, + {Qt::Key_guillemotleft, "guillemotleft"}, + {Qt::Key_notsign, "notsign"}, + {Qt::Key_hyphen, "hyphen"}, + {Qt::Key_registered, "registered"}, + {Qt::Key_macron, "macron"}, + {Qt::Key_degree, "degree"}, + {Qt::Key_plusminus, "plusminus"}, + {Qt::Key_twosuperior, "twosuperior"}, + {Qt::Key_threesuperior, "threesuperior"}, + {Qt::Key_acute, "acute"}, + {Qt::Key_mu, "mu"}, + {Qt::Key_paragraph, "paragraph"}, + {Qt::Key_periodcentered, "periodcentered"}, + {Qt::Key_cedilla, "cedilla"}, + {Qt::Key_onesuperior, "onesuperior"}, + {Qt::Key_masculine, "masculine"}, + {Qt::Key_guillemotright, "guillemotright"}, + {Qt::Key_onequarter, "onequarter"}, + {Qt::Key_onehalf, "onehalf"}, + {Qt::Key_threequarters, "threequarters"}, + {Qt::Key_questiondown, "questiondown"}, + {Qt::Key_Agrave, "Agrave"}, + {Qt::Key_Aacute, "Aacute"}, + {Qt::Key_Acircumflex, "Acircumflex"}, + {Qt::Key_Atilde, "Atilde"}, + {Qt::Key_Adiaeresis, "Adiaeresis"}, + {Qt::Key_Aring, "Aring"}, + {Qt::Key_AE, "AE"}, + {Qt::Key_Ccedilla, "Ccedilla"}, + {Qt::Key_Egrave, "Egrave"}, + {Qt::Key_Eacute, "Eacute"}, + {Qt::Key_Ecircumflex, "Ecircumflex"}, + {Qt::Key_Ediaeresis, "Ediaeresis"}, + {Qt::Key_Igrave, "Igrave"}, + {Qt::Key_Iacute, "Iacute"}, + {Qt::Key_Icircumflex, "Icircumflex"}, + {Qt::Key_Idiaeresis, "Idiaeresis"}, + {Qt::Key_ETH, "ETH"}, + {Qt::Key_Ntilde, "Ntilde"}, + {Qt::Key_Ograve, "Ograve"}, + {Qt::Key_Oacute, "Oacute"}, + {Qt::Key_Ocircumflex, "Ocircumflex"}, + {Qt::Key_Otilde, "Otilde"}, + {Qt::Key_Odiaeresis, "Odiaeresis"}, + {Qt::Key_multiply, "multiply"}, + {Qt::Key_Ooblique, "Ooblique"}, + {Qt::Key_Ugrave, "Ugrave"}, + {Qt::Key_Uacute, "Uacute"}, + {Qt::Key_Ucircumflex, "Ucircumflex"}, + {Qt::Key_Udiaeresis, "Udiaeresis"}, + {Qt::Key_Yacute, "Yacute"}, + {Qt::Key_THORN, "THORN"}, + {Qt::Key_ssharp, "ssharp"}, + {Qt::Key_division, "division"}, + {Qt::Key_ydiaeresis, "ydiaeresis"}, + {Qt::Key_AltGr, "AltGr"}, + {Qt::Key_Multi_key, "Multi_key"}, + {Qt::Key_Codeinput, "Codeinput"}, + {Qt::Key_SingleCandidate, "SingleCandidate"}, + {Qt::Key_MultipleCandidate, "MultipleCandidate"}, + {Qt::Key_PreviousCandidate, "PreviousCandidate"}, + {Qt::Key_Mode_switch, "Mode_switch"}, + {Qt::Key_Kanji, "Kanji"}, + {Qt::Key_Muhenkan, "Muhenkan"}, + {Qt::Key_Henkan, "Henkan"}, + {Qt::Key_Romaji, "Romaji"}, + {Qt::Key_Hiragana, "Hiragana"}, + {Qt::Key_Katakana, "Katakana"}, + {Qt::Key_Hiragana_Katakana, "Hiragana_Katakana"}, + {Qt::Key_Zenkaku, "Zenkaku"}, + {Qt::Key_Hankaku, "Hankaku"}, + {Qt::Key_Zenkaku_Hankaku, "Zenkaku_Hankaku"}, + {Qt::Key_Touroku, "Touroku"}, + {Qt::Key_Massyo, "Massyo"}, + {Qt::Key_Kana_Lock, "Kana_Lock"}, + {Qt::Key_Kana_Shift, "Kana_Shift"}, + {Qt::Key_Eisu_Shift, "Eisu_Shift"}, + {Qt::Key_Eisu_toggle, "Eisu_toggle"}, + {Qt::Key_Hangul, "Hangul"}, + {Qt::Key_Hangul_Start, "Hangul_Start"}, + {Qt::Key_Hangul_End, "Hangul_End"}, + {Qt::Key_Hangul_Hanja, "Hangul_Hanja"}, + {Qt::Key_Hangul_Jamo, "Hangul_Jamo"}, + {Qt::Key_Hangul_Romaja, "Hangul_Romaja"}, + {Qt::Key_Hangul_Jeonja, "Hangul_Jeonja"}, + {Qt::Key_Hangul_Banja, "Hangul_Banja"}, + {Qt::Key_Hangul_PreHanja, "Hangul_PreHanja"}, + {Qt::Key_Hangul_PostHanja, "Hangul_PostHanja"}, + {Qt::Key_Hangul_Special, "Hangul_Special"}, + {Qt::Key_Dead_Grave, "Dead_Grave"}, + {Qt::Key_Dead_Acute, "Dead_Acute"}, + {Qt::Key_Dead_Circumflex, "Dead_Circumflex"}, + {Qt::Key_Dead_Tilde, "Dead_Tilde"}, + {Qt::Key_Dead_Macron, "Dead_Macron"}, + {Qt::Key_Dead_Breve, "Dead_Breve"}, + {Qt::Key_Dead_Abovedot, "Dead_Abovedot"}, + {Qt::Key_Dead_Diaeresis, "Dead_Diaeresis"}, + {Qt::Key_Dead_Abovering, "Dead_Abovering"}, + {Qt::Key_Dead_Doubleacute, "Dead_Doubleacute"}, + {Qt::Key_Dead_Caron, "Dead_Caron"}, + {Qt::Key_Dead_Cedilla, "Dead_Cedilla"}, + {Qt::Key_Dead_Ogonek, "Dead_Ogonek"}, + {Qt::Key_Dead_Iota, "Dead_Iota"}, + {Qt::Key_Dead_Voiced_Sound, "Dead_Voiced_Sound"}, + {Qt::Key_Dead_Semivoiced_Sound, "Dead_Semivoiced_Sound"}, + {Qt::Key_Dead_Belowdot, "Dead_Belowdot"}, + {Qt::Key_Dead_Hook, "Dead_Hook"}, + {Qt::Key_Dead_Horn, "Dead_Horn"}, + {Qt::Key_Back, "Back"}, + {Qt::Key_Forward, "Forward"}, + {Qt::Key_Stop, "Stop"}, + {Qt::Key_Refresh, "Refresh"}, + {Qt::Key_VolumeDown, "VolumeDown"}, + {Qt::Key_VolumeMute, "VolumeMute"}, + {Qt::Key_VolumeUp, "VolumeUp"}, + {Qt::Key_BassBoost, "BassBoost"}, + {Qt::Key_BassUp, "BassUp"}, + {Qt::Key_BassDown, "BassDown"}, + {Qt::Key_TrebleUp, "TrebleUp"}, + {Qt::Key_TrebleDown, "TrebleDown"}, + {Qt::Key_MediaPlay, "MediaPlay"}, + {Qt::Key_MediaStop, "MediaStop"}, + {Qt::Key_MediaPrevious, "MediaPrevious"}, + {Qt::Key_MediaNext, "MediaNext"}, + {Qt::Key_MediaRecord, "MediaRecord"}, + {Qt::Key_MediaPause, "MediaPause"}, + {Qt::Key_MediaTogglePlayPause, "MediaTogglePlayPause"}, + {Qt::Key_HomePage, "HomePage"}, + {Qt::Key_Favorites, "Favorites"}, + {Qt::Key_Search, "Search"}, + {Qt::Key_Standby, "Standby"}, + {Qt::Key_OpenUrl, "OpenUrl"}, + {Qt::Key_LaunchMail, "LaunchMail"}, + {Qt::Key_LaunchMedia, "LaunchMedia"}, + {Qt::Key_Launch0, "Launch0"}, + {Qt::Key_Launch1, "Launch1"}, + {Qt::Key_Launch2, "Launch2"}, + {Qt::Key_Launch3, "Launch3"}, + {Qt::Key_Launch4, "Launch4"}, + {Qt::Key_Launch5, "Launch5"}, + {Qt::Key_Launch6, "Launch6"}, + {Qt::Key_Launch7, "Launch7"}, + {Qt::Key_Launch8, "Launch8"}, + {Qt::Key_Launch9, "Launch9"}, + {Qt::Key_LaunchA, "LaunchA"}, + {Qt::Key_LaunchB, "LaunchB"}, + {Qt::Key_LaunchC, "LaunchC"}, + {Qt::Key_LaunchD, "LaunchD"}, + {Qt::Key_LaunchE, "LaunchE"}, + {Qt::Key_LaunchF, "LaunchF"}, + {Qt::Key_MonBrightnessUp, "MonBrightnessUp"}, + {Qt::Key_MonBrightnessDown, "MonBrightnessDown"}, + {Qt::Key_KeyboardLightOnOff, "KeyboardLightOnOff"}, + {Qt::Key_KeyboardBrightnessUp, "KeyboardBrightnessUp"}, + {Qt::Key_KeyboardBrightnessDown, "KeyboardBrightnessDown"}, + {Qt::Key_PowerOff, "PowerOff"}, + {Qt::Key_WakeUp, "WakeUp"}, + {Qt::Key_Eject, "Eject"}, + {Qt::Key_ScreenSaver, "ScreenSaver"}, + {Qt::Key_WWW, "WWW"}, + {Qt::Key_Memo, "Memo"}, + {Qt::Key_LightBulb, "LightBulb"}, + {Qt::Key_Shop, "Shop"}, + {Qt::Key_History, "History"}, + {Qt::Key_AddFavorite, "AddFavorite"}, + {Qt::Key_HotLinks, "HotLinks"}, + {Qt::Key_BrightnessAdjust, "BrightnessAdjust"}, + {Qt::Key_Finance, "Finance"}, + {Qt::Key_Community, "Community"}, + {Qt::Key_AudioRewind, "AudioRewind"}, + {Qt::Key_BackForward, "BackForward"}, + {Qt::Key_ApplicationLeft, "ApplicationLeft"}, + {Qt::Key_ApplicationRight, "ApplicationRight"}, + {Qt::Key_Book, "Book"}, + {Qt::Key_CD, "CD"}, + {Qt::Key_Calculator, "Calculator"}, + {Qt::Key_ToDoList, "ToDoList"}, + {Qt::Key_ClearGrab, "ClearGrab"}, + {Qt::Key_Close, "Close"}, + {Qt::Key_Copy, "Copy"}, + {Qt::Key_Cut, "Cut"}, + {Qt::Key_Display, "Display"}, + {Qt::Key_DOS, "DOS"}, + {Qt::Key_Documents, "Documents"}, + {Qt::Key_Excel, "Excel"}, + {Qt::Key_Explorer, "Explorer"}, + {Qt::Key_Game, "Game"}, + {Qt::Key_Go, "Go"}, + {Qt::Key_iTouch, "iTouch"}, + {Qt::Key_LogOff, "LogOff"}, + {Qt::Key_Market, "Market"}, + {Qt::Key_Meeting, "Meeting"}, + {Qt::Key_MenuKB, "MenuKB"}, + {Qt::Key_MenuPB, "MenuPB"}, + {Qt::Key_MySites, "MySites"}, + {Qt::Key_News, "News"}, + {Qt::Key_OfficeHome, "OfficeHome"}, + {Qt::Key_Option, "Option"}, + {Qt::Key_Paste, "Paste"}, + {Qt::Key_Phone, "Phone"}, + {Qt::Key_Calendar, "Calendar"}, + {Qt::Key_Reply, "Reply"}, + {Qt::Key_Reload, "Reload"}, + {Qt::Key_RotateWindows, "RotateWindows"}, + {Qt::Key_RotationPB, "RotationPB"}, + {Qt::Key_RotationKB, "RotationKB"}, + {Qt::Key_Save, "Save"}, + {Qt::Key_Send, "Send"}, + {Qt::Key_Spell, "Spell"}, + {Qt::Key_SplitScreen, "SplitScreen"}, + {Qt::Key_Support, "Support"}, + {Qt::Key_TaskPane, "TaskPane"}, + {Qt::Key_Terminal, "Terminal"}, + {Qt::Key_Tools, "Tools"}, + {Qt::Key_Travel, "Travel"}, + {Qt::Key_Video, "Video"}, + {Qt::Key_Word, "Word"}, + {Qt::Key_Xfer, "Xfer"}, + {Qt::Key_ZoomIn, "ZoomIn"}, + {Qt::Key_ZoomOut, "ZoomOut"}, + {Qt::Key_Away, "Away"}, + {Qt::Key_Messenger, "Messenger"}, + {Qt::Key_WebCam, "WebCam"}, + {Qt::Key_MailForward, "MailForward"}, + {Qt::Key_Pictures, "Pictures"}, + {Qt::Key_Music, "Music"}, + {Qt::Key_Battery, "Battery"}, + {Qt::Key_Bluetooth, "Bluetooth"}, + {Qt::Key_WLAN, "WLAN"}, + {Qt::Key_UWB, "UWB"}, + {Qt::Key_AudioForward, "AudioForward"}, + {Qt::Key_AudioRepeat, "AudioRepeat"}, + {Qt::Key_AudioRandomPlay, "AudioRandomPlay"}, + {Qt::Key_Subtitle, "Subtitle"}, + {Qt::Key_AudioCycleTrack, "AudioCycleTrack"}, + {Qt::Key_Time, "Time"}, + {Qt::Key_Hibernate, "Hibernate"}, + {Qt::Key_View, "View"}, + {Qt::Key_TopMenu, "TopMenu"}, + {Qt::Key_PowerDown, "PowerDown"}, + {Qt::Key_Suspend, "Suspend"}, + {Qt::Key_ContrastAdjust, "ContrastAdjust"}, + {Qt::Key_LaunchG, "LaunchG"}, + {Qt::Key_LaunchH, "LaunchH"}, + {Qt::Key_TouchpadToggle, "TouchpadToggle"}, + {Qt::Key_TouchpadOn, "TouchpadOn"}, + {Qt::Key_TouchpadOff, "TouchpadOff"}, + {Qt::Key_MicMute, "MicMute"}, + {Qt::Key_Red, "Red"}, + {Qt::Key_Green, "Green"}, + {Qt::Key_Yellow, "Yellow"}, + {Qt::Key_Blue, "Blue"}, + {Qt::Key_ChannelUp, "ChannelUp"}, + {Qt::Key_ChannelDown, "ChannelDown"}, + {Qt::Key_Guide, "Guide"}, + {Qt::Key_Info, "Info"}, + {Qt::Key_Settings, "Settings"}, + {Qt::Key_MicVolumeUp, "MicVolumeUp"}, + {Qt::Key_MicVolumeDown, "MicVolumeDown"}, + {Qt::Key_New, "New"}, + {Qt::Key_Open, "Open"}, + {Qt::Key_Find, "Find"}, + {Qt::Key_Undo, "Undo"}, + {Qt::Key_Redo, "Redo"}, + {Qt::Key_MediaLast, "MediaLast"}, + {Qt::Key_Select, "Select"}, + {Qt::Key_Yes, "Yes"}, + {Qt::Key_No, "No"}, + {Qt::Key_Cancel, "Cancel"}, + {Qt::Key_Printer, "Printer"}, + {Qt::Key_Execute, "Execute"}, + {Qt::Key_Sleep, "Sleep"}, + {Qt::Key_Play, "Play"}, + {Qt::Key_Zoom, "Zoom"}, + {Qt::Key_Exit, "Exit"}, + {Qt::Key_Context1, "Context1"}, + {Qt::Key_Context2, "Context2"}, + {Qt::Key_Context3, "Context3"}, + {Qt::Key_Context4, "Context4"}, + {Qt::Key_Call, "Call"}, + {Qt::Key_Hangup, "Hangup"}, + {Qt::Key_Flip, "Flip"}, + {Qt::Key_ToggleCallHangup, "ToggleCallHangup"}, + {Qt::Key_VoiceDial, "VoiceDial"}, + {Qt::Key_LastNumberRedial, "LastNumberRedial"}, + {Qt::Key_Camera, "Camera"}, + {Qt::Key_CameraFocus, "CameraFocus"}}; + +std::optional InputManager::ConvertHostKeyboardStringToCode(const std::string_view& str) +{ + for (const KeyCodeName& name : s_qt_key_names) + { + if (str == name.name) + return static_cast(name.code); + } + + return std::nullopt; +} + +std::optional InputManager::ConvertHostKeyboardCodeToString(u32 code) +{ + for (const KeyCodeName& name : s_qt_key_names) + { + if (static_cast(code) == name.code) + return std::string(name.name); + } + + return std::nullopt; +} diff --git a/pcsx2-qt/QtUtils.cpp b/pcsx2-qt/QtUtils.cpp new file mode 100644 index 000000000..c01a5e474 --- /dev/null +++ b/pcsx2-qt/QtUtils.cpp @@ -0,0 +1,703 @@ +/* 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 +#include +#include +#include + +#if QT_VERSION >= QT_VERSION_CHECK(6, 0, 0) +#include +#else +#include +#endif + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include "QtUtils.h" + +namespace QtUtils +{ + void MarkActionAsDefault(QAction* action) + { + QFont new_font(action->font()); + new_font.setBold(true); + action->setFont(new_font); + } + + QFrame* CreateHorizontalLine(QWidget* parent) + { + QFrame* line = new QFrame(parent); + line->setFrameShape(QFrame::HLine); + line->setFrameShadow(QFrame::Sunken); + return line; + } + + QWidget* GetRootWidget(QWidget* widget, bool stop_at_window_or_dialog) + { + QWidget* next_parent = widget->parentWidget(); + while (next_parent) + { + if (stop_at_window_or_dialog && (widget->metaObject()->inherits(&QMainWindow::staticMetaObject) || + widget->metaObject()->inherits(&QDialog::staticMetaObject))) + { + break; + } + + widget = next_parent; + next_parent = widget->parentWidget(); + } + + return widget; + } + + template + static void ResizeColumnsForView(T* view, const std::initializer_list& widths) + { + QHeaderView* header; + if constexpr (std::is_same_v) + header = view->horizontalHeader(); + else + header = view->header(); + + const int min_column_width = header->minimumSectionSize(); + const int scrollbar_width = ((view->verticalScrollBar() && view->verticalScrollBar()->isVisible()) || + view->verticalScrollBarPolicy() == Qt::ScrollBarAlwaysOn) ? + view->verticalScrollBar()->width() : + 0; + int num_flex_items = 0; + int total_width = 0; + int column_index = 0; + for (const int spec_width : widths) + { + if (!view->isColumnHidden(column_index)) + { + if (spec_width < 0) + num_flex_items++; + else + total_width += std::max(spec_width, min_column_width); + } + + column_index++; + } + + const int flex_width = + (num_flex_items > 0) ? + std::max((view->contentsRect().width() - total_width - scrollbar_width) / num_flex_items, 1) : + 0; + + column_index = 0; + for (const int spec_width : widths) + { + if (view->isColumnHidden(column_index)) + { + column_index++; + continue; + } + + const int width = spec_width < 0 ? flex_width : (std::max(spec_width, min_column_width)); + view->setColumnWidth(column_index, width); + column_index++; + } + } + + void ResizeColumnsForTableView(QTableView* view, const std::initializer_list& widths) + { + ResizeColumnsForView(view, widths); + } + + void ResizeColumnsForTreeView(QTreeView* view, const std::initializer_list& widths) + { + ResizeColumnsForView(view, widths); + } + + static const std::map s_qt_key_names = { + {Qt::Key_Escape, QStringLiteral("Escape")}, + {Qt::Key_Tab, QStringLiteral("Tab")}, + {Qt::Key_Backtab, QStringLiteral("Backtab")}, + {Qt::Key_Backspace, QStringLiteral("Backspace")}, + {Qt::Key_Return, QStringLiteral("Return")}, + {Qt::Key_Enter, QStringLiteral("Enter")}, + {Qt::Key_Insert, QStringLiteral("Insert")}, + {Qt::Key_Delete, QStringLiteral("Delete")}, + {Qt::Key_Pause, QStringLiteral("Pause")}, + {Qt::Key_Print, QStringLiteral("Print")}, + {Qt::Key_SysReq, QStringLiteral("SysReq")}, + {Qt::Key_Clear, QStringLiteral("Clear")}, + {Qt::Key_Home, QStringLiteral("Home")}, + {Qt::Key_End, QStringLiteral("End")}, + {Qt::Key_Left, QStringLiteral("Left")}, + {Qt::Key_Up, QStringLiteral("Up")}, + {Qt::Key_Right, QStringLiteral("Right")}, + {Qt::Key_Down, QStringLiteral("Down")}, + {Qt::Key_PageUp, QStringLiteral("PageUp")}, + {Qt::Key_PageDown, QStringLiteral("PageDown")}, + {Qt::Key_Shift, QStringLiteral("Shift")}, + {Qt::Key_Control, QStringLiteral("Control")}, + {Qt::Key_Meta, QStringLiteral("Meta")}, + {Qt::Key_Alt, QStringLiteral("Alt")}, + {Qt::Key_CapsLock, QStringLiteral("CapsLock")}, + {Qt::Key_NumLock, QStringLiteral("NumLock")}, + {Qt::Key_ScrollLock, QStringLiteral("ScrollLock")}, + {Qt::Key_F1, QStringLiteral("F1")}, + {Qt::Key_F2, QStringLiteral("F2")}, + {Qt::Key_F3, QStringLiteral("F3")}, + {Qt::Key_F4, QStringLiteral("F4")}, + {Qt::Key_F5, QStringLiteral("F5")}, + {Qt::Key_F6, QStringLiteral("F6")}, + {Qt::Key_F7, QStringLiteral("F7")}, + {Qt::Key_F8, QStringLiteral("F8")}, + {Qt::Key_F9, QStringLiteral("F9")}, + {Qt::Key_F10, QStringLiteral("F10")}, + {Qt::Key_F11, QStringLiteral("F11")}, + {Qt::Key_F12, QStringLiteral("F12")}, + {Qt::Key_F13, QStringLiteral("F13")}, + {Qt::Key_F14, QStringLiteral("F14")}, + {Qt::Key_F15, QStringLiteral("F15")}, + {Qt::Key_F16, QStringLiteral("F16")}, + {Qt::Key_F17, QStringLiteral("F17")}, + {Qt::Key_F18, QStringLiteral("F18")}, + {Qt::Key_F19, QStringLiteral("F19")}, + {Qt::Key_F20, QStringLiteral("F20")}, + {Qt::Key_F21, QStringLiteral("F21")}, + {Qt::Key_F22, QStringLiteral("F22")}, + {Qt::Key_F23, QStringLiteral("F23")}, + {Qt::Key_F24, QStringLiteral("F24")}, + {Qt::Key_F25, QStringLiteral("F25")}, + {Qt::Key_F26, QStringLiteral("F26")}, + {Qt::Key_F27, QStringLiteral("F27")}, + {Qt::Key_F28, QStringLiteral("F28")}, + {Qt::Key_F29, QStringLiteral("F29")}, + {Qt::Key_F30, QStringLiteral("F30")}, + {Qt::Key_F31, QStringLiteral("F31")}, + {Qt::Key_F32, QStringLiteral("F32")}, + {Qt::Key_F33, QStringLiteral("F33")}, + {Qt::Key_F34, QStringLiteral("F34")}, + {Qt::Key_F35, QStringLiteral("F35")}, + {Qt::Key_Super_L, QStringLiteral("Super_L")}, + {Qt::Key_Super_R, QStringLiteral("Super_R")}, + {Qt::Key_Menu, QStringLiteral("Menu")}, + {Qt::Key_Hyper_L, QStringLiteral("Hyper_L")}, + {Qt::Key_Hyper_R, QStringLiteral("Hyper_R")}, + {Qt::Key_Help, QStringLiteral("Help")}, + {Qt::Key_Direction_L, QStringLiteral("Direction_L")}, + {Qt::Key_Direction_R, QStringLiteral("Direction_R")}, + {Qt::Key_Space, QStringLiteral("Space")}, + {Qt::Key_Any, QStringLiteral("Any")}, + {Qt::Key_Exclam, QStringLiteral("Exclam")}, + {Qt::Key_QuoteDbl, QStringLiteral("QuoteDbl")}, + {Qt::Key_NumberSign, QStringLiteral("NumberSign")}, + {Qt::Key_Dollar, QStringLiteral("Dollar")}, + {Qt::Key_Percent, QStringLiteral("Percent")}, + {Qt::Key_Ampersand, QStringLiteral("Ampersand")}, + {Qt::Key_Apostrophe, QStringLiteral("Apostrophe")}, + {Qt::Key_ParenLeft, QStringLiteral("ParenLeft")}, + {Qt::Key_ParenRight, QStringLiteral("ParenRight")}, + {Qt::Key_Asterisk, QStringLiteral("Asterisk")}, + {Qt::Key_Plus, QStringLiteral("Plus")}, + {Qt::Key_Comma, QStringLiteral("Comma")}, + {Qt::Key_Minus, QStringLiteral("Minus")}, + {Qt::Key_Period, QStringLiteral("Period")}, + {Qt::Key_Slash, QStringLiteral("Slash")}, + {Qt::Key_0, QStringLiteral("0")}, + {Qt::Key_1, QStringLiteral("1")}, + {Qt::Key_2, QStringLiteral("2")}, + {Qt::Key_3, QStringLiteral("3")}, + {Qt::Key_4, QStringLiteral("4")}, + {Qt::Key_5, QStringLiteral("5")}, + {Qt::Key_6, QStringLiteral("6")}, + {Qt::Key_7, QStringLiteral("7")}, + {Qt::Key_8, QStringLiteral("8")}, + {Qt::Key_9, QStringLiteral("9")}, + {Qt::Key_Colon, QStringLiteral("Colon")}, + {Qt::Key_Semicolon, QStringLiteral("Semicolon")}, + {Qt::Key_Less, QStringLiteral("Less")}, + {Qt::Key_Equal, QStringLiteral("Equal")}, + {Qt::Key_Greater, QStringLiteral("Greater")}, + {Qt::Key_Question, QStringLiteral("Question")}, + {Qt::Key_At, QStringLiteral("At")}, + {Qt::Key_A, QStringLiteral("A")}, + {Qt::Key_B, QStringLiteral("B")}, + {Qt::Key_C, QStringLiteral("C")}, + {Qt::Key_D, QStringLiteral("D")}, + {Qt::Key_E, QStringLiteral("E")}, + {Qt::Key_F, QStringLiteral("F")}, + {Qt::Key_G, QStringLiteral("G")}, + {Qt::Key_H, QStringLiteral("H")}, + {Qt::Key_I, QStringLiteral("I")}, + {Qt::Key_J, QStringLiteral("J")}, + {Qt::Key_K, QStringLiteral("K")}, + {Qt::Key_L, QStringLiteral("L")}, + {Qt::Key_M, QStringLiteral("M")}, + {Qt::Key_N, QStringLiteral("N")}, + {Qt::Key_O, QStringLiteral("O")}, + {Qt::Key_P, QStringLiteral("P")}, + {Qt::Key_Q, QStringLiteral("Q")}, + {Qt::Key_R, QStringLiteral("R")}, + {Qt::Key_S, QStringLiteral("S")}, + {Qt::Key_T, QStringLiteral("T")}, + {Qt::Key_U, QStringLiteral("U")}, + {Qt::Key_V, QStringLiteral("V")}, + {Qt::Key_W, QStringLiteral("W")}, + {Qt::Key_X, QStringLiteral("X")}, + {Qt::Key_Y, QStringLiteral("Y")}, + {Qt::Key_Z, QStringLiteral("Z")}, + {Qt::Key_BracketLeft, QStringLiteral("BracketLeft")}, + {Qt::Key_Backslash, QStringLiteral("Backslash")}, + {Qt::Key_BracketRight, QStringLiteral("BracketRight")}, + {Qt::Key_AsciiCircum, QStringLiteral("AsciiCircum")}, + {Qt::Key_Underscore, QStringLiteral("Underscore")}, + {Qt::Key_QuoteLeft, QStringLiteral("QuoteLeft")}, + {Qt::Key_BraceLeft, QStringLiteral("BraceLeft")}, + {Qt::Key_Bar, QStringLiteral("Bar")}, + {Qt::Key_BraceRight, QStringLiteral("BraceRight")}, + {Qt::Key_AsciiTilde, QStringLiteral("AsciiTilde")}, + {Qt::Key_nobreakspace, QStringLiteral("nobreakspace")}, + {Qt::Key_exclamdown, QStringLiteral("exclamdown")}, + {Qt::Key_cent, QStringLiteral("cent")}, + {Qt::Key_sterling, QStringLiteral("sterling")}, + {Qt::Key_currency, QStringLiteral("currency")}, + {Qt::Key_yen, QStringLiteral("yen")}, + {Qt::Key_brokenbar, QStringLiteral("brokenbar")}, + {Qt::Key_section, QStringLiteral("section")}, + {Qt::Key_diaeresis, QStringLiteral("diaeresis")}, + {Qt::Key_copyright, QStringLiteral("copyright")}, + {Qt::Key_ordfeminine, QStringLiteral("ordfeminine")}, + {Qt::Key_guillemotleft, QStringLiteral("guillemotleft")}, + {Qt::Key_notsign, QStringLiteral("notsign")}, + {Qt::Key_hyphen, QStringLiteral("hyphen")}, + {Qt::Key_registered, QStringLiteral("registered")}, + {Qt::Key_macron, QStringLiteral("macron")}, + {Qt::Key_degree, QStringLiteral("degree")}, + {Qt::Key_plusminus, QStringLiteral("plusminus")}, + {Qt::Key_twosuperior, QStringLiteral("twosuperior")}, + {Qt::Key_threesuperior, QStringLiteral("threesuperior")}, + {Qt::Key_acute, QStringLiteral("acute")}, + {Qt::Key_mu, QStringLiteral("mu")}, + {Qt::Key_paragraph, QStringLiteral("paragraph")}, + {Qt::Key_periodcentered, QStringLiteral("periodcentered")}, + {Qt::Key_cedilla, QStringLiteral("cedilla")}, + {Qt::Key_onesuperior, QStringLiteral("onesuperior")}, + {Qt::Key_masculine, QStringLiteral("masculine")}, + {Qt::Key_guillemotright, QStringLiteral("guillemotright")}, + {Qt::Key_onequarter, QStringLiteral("onequarter")}, + {Qt::Key_onehalf, QStringLiteral("onehalf")}, + {Qt::Key_threequarters, QStringLiteral("threequarters")}, + {Qt::Key_questiondown, QStringLiteral("questiondown")}, + {Qt::Key_Agrave, QStringLiteral("Agrave")}, + {Qt::Key_Aacute, QStringLiteral("Aacute")}, + {Qt::Key_Acircumflex, QStringLiteral("Acircumflex")}, + {Qt::Key_Atilde, QStringLiteral("Atilde")}, + {Qt::Key_Adiaeresis, QStringLiteral("Adiaeresis")}, + {Qt::Key_Aring, QStringLiteral("Aring")}, + {Qt::Key_AE, QStringLiteral("AE")}, + {Qt::Key_Ccedilla, QStringLiteral("Ccedilla")}, + {Qt::Key_Egrave, QStringLiteral("Egrave")}, + {Qt::Key_Eacute, QStringLiteral("Eacute")}, + {Qt::Key_Ecircumflex, QStringLiteral("Ecircumflex")}, + {Qt::Key_Ediaeresis, QStringLiteral("Ediaeresis")}, + {Qt::Key_Igrave, QStringLiteral("Igrave")}, + {Qt::Key_Iacute, QStringLiteral("Iacute")}, + {Qt::Key_Icircumflex, QStringLiteral("Icircumflex")}, + {Qt::Key_Idiaeresis, QStringLiteral("Idiaeresis")}, + {Qt::Key_ETH, QStringLiteral("ETH")}, + {Qt::Key_Ntilde, QStringLiteral("Ntilde")}, + {Qt::Key_Ograve, QStringLiteral("Ograve")}, + {Qt::Key_Oacute, QStringLiteral("Oacute")}, + {Qt::Key_Ocircumflex, QStringLiteral("Ocircumflex")}, + {Qt::Key_Otilde, QStringLiteral("Otilde")}, + {Qt::Key_Odiaeresis, QStringLiteral("Odiaeresis")}, + {Qt::Key_multiply, QStringLiteral("multiply")}, + {Qt::Key_Ooblique, QStringLiteral("Ooblique")}, + {Qt::Key_Ugrave, QStringLiteral("Ugrave")}, + {Qt::Key_Uacute, QStringLiteral("Uacute")}, + {Qt::Key_Ucircumflex, QStringLiteral("Ucircumflex")}, + {Qt::Key_Udiaeresis, QStringLiteral("Udiaeresis")}, + {Qt::Key_Yacute, QStringLiteral("Yacute")}, + {Qt::Key_THORN, QStringLiteral("THORN")}, + {Qt::Key_ssharp, QStringLiteral("ssharp")}, + {Qt::Key_division, QStringLiteral("division")}, + {Qt::Key_ydiaeresis, QStringLiteral("ydiaeresis")}, + {Qt::Key_AltGr, QStringLiteral("AltGr")}, + {Qt::Key_Multi_key, QStringLiteral("Multi_key")}, + {Qt::Key_Codeinput, QStringLiteral("Codeinput")}, + {Qt::Key_SingleCandidate, QStringLiteral("SingleCandidate")}, + {Qt::Key_MultipleCandidate, QStringLiteral("MultipleCandidate")}, + {Qt::Key_PreviousCandidate, QStringLiteral("PreviousCandidate")}, + {Qt::Key_Mode_switch, QStringLiteral("Mode_switch")}, + {Qt::Key_Kanji, QStringLiteral("Kanji")}, + {Qt::Key_Muhenkan, QStringLiteral("Muhenkan")}, + {Qt::Key_Henkan, QStringLiteral("Henkan")}, + {Qt::Key_Romaji, QStringLiteral("Romaji")}, + {Qt::Key_Hiragana, QStringLiteral("Hiragana")}, + {Qt::Key_Katakana, QStringLiteral("Katakana")}, + {Qt::Key_Hiragana_Katakana, QStringLiteral("Hiragana_Katakana")}, + {Qt::Key_Zenkaku, QStringLiteral("Zenkaku")}, + {Qt::Key_Hankaku, QStringLiteral("Hankaku")}, + {Qt::Key_Zenkaku_Hankaku, QStringLiteral("Zenkaku_Hankaku")}, + {Qt::Key_Touroku, QStringLiteral("Touroku")}, + {Qt::Key_Massyo, QStringLiteral("Massyo")}, + {Qt::Key_Kana_Lock, QStringLiteral("Kana_Lock")}, + {Qt::Key_Kana_Shift, QStringLiteral("Kana_Shift")}, + {Qt::Key_Eisu_Shift, QStringLiteral("Eisu_Shift")}, + {Qt::Key_Eisu_toggle, QStringLiteral("Eisu_toggle")}, + {Qt::Key_Hangul, QStringLiteral("Hangul")}, + {Qt::Key_Hangul_Start, QStringLiteral("Hangul_Start")}, + {Qt::Key_Hangul_End, QStringLiteral("Hangul_End")}, + {Qt::Key_Hangul_Hanja, QStringLiteral("Hangul_Hanja")}, + {Qt::Key_Hangul_Jamo, QStringLiteral("Hangul_Jamo")}, + {Qt::Key_Hangul_Romaja, QStringLiteral("Hangul_Romaja")}, + {Qt::Key_Hangul_Jeonja, QStringLiteral("Hangul_Jeonja")}, + {Qt::Key_Hangul_Banja, QStringLiteral("Hangul_Banja")}, + {Qt::Key_Hangul_PreHanja, QStringLiteral("Hangul_PreHanja")}, + {Qt::Key_Hangul_PostHanja, QStringLiteral("Hangul_PostHanja")}, + {Qt::Key_Hangul_Special, QStringLiteral("Hangul_Special")}, + {Qt::Key_Dead_Grave, QStringLiteral("Dead_Grave")}, + {Qt::Key_Dead_Acute, QStringLiteral("Dead_Acute")}, + {Qt::Key_Dead_Circumflex, QStringLiteral("Dead_Circumflex")}, + {Qt::Key_Dead_Tilde, QStringLiteral("Dead_Tilde")}, + {Qt::Key_Dead_Macron, QStringLiteral("Dead_Macron")}, + {Qt::Key_Dead_Breve, QStringLiteral("Dead_Breve")}, + {Qt::Key_Dead_Abovedot, QStringLiteral("Dead_Abovedot")}, + {Qt::Key_Dead_Diaeresis, QStringLiteral("Dead_Diaeresis")}, + {Qt::Key_Dead_Abovering, QStringLiteral("Dead_Abovering")}, + {Qt::Key_Dead_Doubleacute, QStringLiteral("Dead_Doubleacute")}, + {Qt::Key_Dead_Caron, QStringLiteral("Dead_Caron")}, + {Qt::Key_Dead_Cedilla, QStringLiteral("Dead_Cedilla")}, + {Qt::Key_Dead_Ogonek, QStringLiteral("Dead_Ogonek")}, + {Qt::Key_Dead_Iota, QStringLiteral("Dead_Iota")}, + {Qt::Key_Dead_Voiced_Sound, QStringLiteral("Dead_Voiced_Sound")}, + {Qt::Key_Dead_Semivoiced_Sound, QStringLiteral("Dead_Semivoiced_Sound")}, + {Qt::Key_Dead_Belowdot, QStringLiteral("Dead_Belowdot")}, + {Qt::Key_Dead_Hook, QStringLiteral("Dead_Hook")}, + {Qt::Key_Dead_Horn, QStringLiteral("Dead_Horn")}, + {Qt::Key_Back, QStringLiteral("Back")}, + {Qt::Key_Forward, QStringLiteral("Forward")}, + {Qt::Key_Stop, QStringLiteral("Stop")}, + {Qt::Key_Refresh, QStringLiteral("Refresh")}, + {Qt::Key_VolumeDown, QStringLiteral("VolumeDown")}, + {Qt::Key_VolumeMute, QStringLiteral("VolumeMute")}, + {Qt::Key_VolumeUp, QStringLiteral("VolumeUp")}, + {Qt::Key_BassBoost, QStringLiteral("BassBoost")}, + {Qt::Key_BassUp, QStringLiteral("BassUp")}, + {Qt::Key_BassDown, QStringLiteral("BassDown")}, + {Qt::Key_TrebleUp, QStringLiteral("TrebleUp")}, + {Qt::Key_TrebleDown, QStringLiteral("TrebleDown")}, + {Qt::Key_MediaPlay, QStringLiteral("MediaPlay")}, + {Qt::Key_MediaStop, QStringLiteral("MediaStop")}, + {Qt::Key_MediaPrevious, QStringLiteral("MediaPrevious")}, + {Qt::Key_MediaNext, QStringLiteral("MediaNext")}, + {Qt::Key_MediaRecord, QStringLiteral("MediaRecord")}, + {Qt::Key_MediaPause, QStringLiteral("MediaPause")}, + {Qt::Key_MediaTogglePlayPause, QStringLiteral("MediaTogglePlayPause")}, + {Qt::Key_HomePage, QStringLiteral("HomePage")}, + {Qt::Key_Favorites, QStringLiteral("Favorites")}, + {Qt::Key_Search, QStringLiteral("Search")}, + {Qt::Key_Standby, QStringLiteral("Standby")}, + {Qt::Key_OpenUrl, QStringLiteral("OpenUrl")}, + {Qt::Key_LaunchMail, QStringLiteral("LaunchMail")}, + {Qt::Key_LaunchMedia, QStringLiteral("LaunchMedia")}, + {Qt::Key_Launch0, QStringLiteral("Launch0")}, + {Qt::Key_Launch1, QStringLiteral("Launch1")}, + {Qt::Key_Launch2, QStringLiteral("Launch2")}, + {Qt::Key_Launch3, QStringLiteral("Launch3")}, + {Qt::Key_Launch4, QStringLiteral("Launch4")}, + {Qt::Key_Launch5, QStringLiteral("Launch5")}, + {Qt::Key_Launch6, QStringLiteral("Launch6")}, + {Qt::Key_Launch7, QStringLiteral("Launch7")}, + {Qt::Key_Launch8, QStringLiteral("Launch8")}, + {Qt::Key_Launch9, QStringLiteral("Launch9")}, + {Qt::Key_LaunchA, QStringLiteral("LaunchA")}, + {Qt::Key_LaunchB, QStringLiteral("LaunchB")}, + {Qt::Key_LaunchC, QStringLiteral("LaunchC")}, + {Qt::Key_LaunchD, QStringLiteral("LaunchD")}, + {Qt::Key_LaunchE, QStringLiteral("LaunchE")}, + {Qt::Key_LaunchF, QStringLiteral("LaunchF")}, + {Qt::Key_MonBrightnessUp, QStringLiteral("MonBrightnessUp")}, + {Qt::Key_MonBrightnessDown, QStringLiteral("MonBrightnessDown")}, + {Qt::Key_KeyboardLightOnOff, QStringLiteral("KeyboardLightOnOff")}, + {Qt::Key_KeyboardBrightnessUp, QStringLiteral("KeyboardBrightnessUp")}, + {Qt::Key_KeyboardBrightnessDown, QStringLiteral("KeyboardBrightnessDown")}, + {Qt::Key_PowerOff, QStringLiteral("PowerOff")}, + {Qt::Key_WakeUp, QStringLiteral("WakeUp")}, + {Qt::Key_Eject, QStringLiteral("Eject")}, + {Qt::Key_ScreenSaver, QStringLiteral("ScreenSaver")}, + {Qt::Key_WWW, QStringLiteral("WWW")}, + {Qt::Key_Memo, QStringLiteral("Memo")}, + {Qt::Key_LightBulb, QStringLiteral("LightBulb")}, + {Qt::Key_Shop, QStringLiteral("Shop")}, + {Qt::Key_History, QStringLiteral("History")}, + {Qt::Key_AddFavorite, QStringLiteral("AddFavorite")}, + {Qt::Key_HotLinks, QStringLiteral("HotLinks")}, + {Qt::Key_BrightnessAdjust, QStringLiteral("BrightnessAdjust")}, + {Qt::Key_Finance, QStringLiteral("Finance")}, + {Qt::Key_Community, QStringLiteral("Community")}, + {Qt::Key_AudioRewind, QStringLiteral("AudioRewind")}, + {Qt::Key_BackForward, QStringLiteral("BackForward")}, + {Qt::Key_ApplicationLeft, QStringLiteral("ApplicationLeft")}, + {Qt::Key_ApplicationRight, QStringLiteral("ApplicationRight")}, + {Qt::Key_Book, QStringLiteral("Book")}, + {Qt::Key_CD, QStringLiteral("CD")}, + {Qt::Key_Calculator, QStringLiteral("Calculator")}, + {Qt::Key_ToDoList, QStringLiteral("ToDoList")}, + {Qt::Key_ClearGrab, QStringLiteral("ClearGrab")}, + {Qt::Key_Close, QStringLiteral("Close")}, + {Qt::Key_Copy, QStringLiteral("Copy")}, + {Qt::Key_Cut, QStringLiteral("Cut")}, + {Qt::Key_Display, QStringLiteral("Display")}, + {Qt::Key_DOS, QStringLiteral("DOS")}, + {Qt::Key_Documents, QStringLiteral("Documents")}, + {Qt::Key_Excel, QStringLiteral("Excel")}, + {Qt::Key_Explorer, QStringLiteral("Explorer")}, + {Qt::Key_Game, QStringLiteral("Game")}, + {Qt::Key_Go, QStringLiteral("Go")}, + {Qt::Key_iTouch, QStringLiteral("iTouch")}, + {Qt::Key_LogOff, QStringLiteral("LogOff")}, + {Qt::Key_Market, QStringLiteral("Market")}, + {Qt::Key_Meeting, QStringLiteral("Meeting")}, + {Qt::Key_MenuKB, QStringLiteral("MenuKB")}, + {Qt::Key_MenuPB, QStringLiteral("MenuPB")}, + {Qt::Key_MySites, QStringLiteral("MySites")}, + {Qt::Key_News, QStringLiteral("News")}, + {Qt::Key_OfficeHome, QStringLiteral("OfficeHome")}, + {Qt::Key_Option, QStringLiteral("Option")}, + {Qt::Key_Paste, QStringLiteral("Paste")}, + {Qt::Key_Phone, QStringLiteral("Phone")}, + {Qt::Key_Calendar, QStringLiteral("Calendar")}, + {Qt::Key_Reply, QStringLiteral("Reply")}, + {Qt::Key_Reload, QStringLiteral("Reload")}, + {Qt::Key_RotateWindows, QStringLiteral("RotateWindows")}, + {Qt::Key_RotationPB, QStringLiteral("RotationPB")}, + {Qt::Key_RotationKB, QStringLiteral("RotationKB")}, + {Qt::Key_Save, QStringLiteral("Save")}, + {Qt::Key_Send, QStringLiteral("Send")}, + {Qt::Key_Spell, QStringLiteral("Spell")}, + {Qt::Key_SplitScreen, QStringLiteral("SplitScreen")}, + {Qt::Key_Support, QStringLiteral("Support")}, + {Qt::Key_TaskPane, QStringLiteral("TaskPane")}, + {Qt::Key_Terminal, QStringLiteral("Terminal")}, + {Qt::Key_Tools, QStringLiteral("Tools")}, + {Qt::Key_Travel, QStringLiteral("Travel")}, + {Qt::Key_Video, QStringLiteral("Video")}, + {Qt::Key_Word, QStringLiteral("Word")}, + {Qt::Key_Xfer, QStringLiteral("Xfer")}, + {Qt::Key_ZoomIn, QStringLiteral("ZoomIn")}, + {Qt::Key_ZoomOut, QStringLiteral("ZoomOut")}, + {Qt::Key_Away, QStringLiteral("Away")}, + {Qt::Key_Messenger, QStringLiteral("Messenger")}, + {Qt::Key_WebCam, QStringLiteral("WebCam")}, + {Qt::Key_MailForward, QStringLiteral("MailForward")}, + {Qt::Key_Pictures, QStringLiteral("Pictures")}, + {Qt::Key_Music, QStringLiteral("Music")}, + {Qt::Key_Battery, QStringLiteral("Battery")}, + {Qt::Key_Bluetooth, QStringLiteral("Bluetooth")}, + {Qt::Key_WLAN, QStringLiteral("WLAN")}, + {Qt::Key_UWB, QStringLiteral("UWB")}, + {Qt::Key_AudioForward, QStringLiteral("AudioForward")}, + {Qt::Key_AudioRepeat, QStringLiteral("AudioRepeat")}, + {Qt::Key_AudioRandomPlay, QStringLiteral("AudioRandomPlay")}, + {Qt::Key_Subtitle, QStringLiteral("Subtitle")}, + {Qt::Key_AudioCycleTrack, QStringLiteral("AudioCycleTrack")}, + {Qt::Key_Time, QStringLiteral("Time")}, + {Qt::Key_Hibernate, QStringLiteral("Hibernate")}, + {Qt::Key_View, QStringLiteral("View")}, + {Qt::Key_TopMenu, QStringLiteral("TopMenu")}, + {Qt::Key_PowerDown, QStringLiteral("PowerDown")}, + {Qt::Key_Suspend, QStringLiteral("Suspend")}, + {Qt::Key_ContrastAdjust, QStringLiteral("ContrastAdjust")}, + {Qt::Key_LaunchG, QStringLiteral("LaunchG")}, + {Qt::Key_LaunchH, QStringLiteral("LaunchH")}, + {Qt::Key_TouchpadToggle, QStringLiteral("TouchpadToggle")}, + {Qt::Key_TouchpadOn, QStringLiteral("TouchpadOn")}, + {Qt::Key_TouchpadOff, QStringLiteral("TouchpadOff")}, + {Qt::Key_MicMute, QStringLiteral("MicMute")}, + {Qt::Key_Red, QStringLiteral("Red")}, + {Qt::Key_Green, QStringLiteral("Green")}, + {Qt::Key_Yellow, QStringLiteral("Yellow")}, + {Qt::Key_Blue, QStringLiteral("Blue")}, + {Qt::Key_ChannelUp, QStringLiteral("ChannelUp")}, + {Qt::Key_ChannelDown, QStringLiteral("ChannelDown")}, + {Qt::Key_Guide, QStringLiteral("Guide")}, + {Qt::Key_Info, QStringLiteral("Info")}, + {Qt::Key_Settings, QStringLiteral("Settings")}, + {Qt::Key_MicVolumeUp, QStringLiteral("MicVolumeUp")}, + {Qt::Key_MicVolumeDown, QStringLiteral("MicVolumeDown")}, + {Qt::Key_New, QStringLiteral("New")}, + {Qt::Key_Open, QStringLiteral("Open")}, + {Qt::Key_Find, QStringLiteral("Find")}, + {Qt::Key_Undo, QStringLiteral("Undo")}, + {Qt::Key_Redo, QStringLiteral("Redo")}, + {Qt::Key_MediaLast, QStringLiteral("MediaLast")}, + {Qt::Key_Select, QStringLiteral("Select")}, + {Qt::Key_Yes, QStringLiteral("Yes")}, + {Qt::Key_No, QStringLiteral("No")}, + {Qt::Key_Cancel, QStringLiteral("Cancel")}, + {Qt::Key_Printer, QStringLiteral("Printer")}, + {Qt::Key_Execute, QStringLiteral("Execute")}, + {Qt::Key_Sleep, QStringLiteral("Sleep")}, + {Qt::Key_Play, QStringLiteral("Play")}, + {Qt::Key_Zoom, QStringLiteral("Zoom")}, + {Qt::Key_Exit, QStringLiteral("Exit")}, + {Qt::Key_Context1, QStringLiteral("Context1")}, + {Qt::Key_Context2, QStringLiteral("Context2")}, + {Qt::Key_Context3, QStringLiteral("Context3")}, + {Qt::Key_Context4, QStringLiteral("Context4")}, + {Qt::Key_Call, QStringLiteral("Call")}, + {Qt::Key_Hangup, QStringLiteral("Hangup")}, + {Qt::Key_Flip, QStringLiteral("Flip")}, + {Qt::Key_ToggleCallHangup, QStringLiteral("ToggleCallHangup")}, + {Qt::Key_VoiceDial, QStringLiteral("VoiceDial")}, + {Qt::Key_LastNumberRedial, QStringLiteral("LastNumberRedial")}, + {Qt::Key_Camera, QStringLiteral("Camera")}, + {Qt::Key_CameraFocus, QStringLiteral("CameraFocus")}}; + + struct QtKeyModifierEntry + { + Qt::KeyboardModifier mod; + Qt::Key key; + QString name; + }; + + static const std::array s_qt_key_modifiers = { + {{Qt::ShiftModifier, Qt::Key_Shift, QStringLiteral("Shift")}, + {Qt::ControlModifier, Qt::Key_Control, QStringLiteral("Control")}, + {Qt::AltModifier, Qt::Key_Alt, QStringLiteral("Alt")}, + {Qt::MetaModifier, Qt::Key_Meta, QStringLiteral("Meta")}, + {Qt::KeypadModifier, static_cast(0), QStringLiteral("Keypad")}}}; + + QString GetKeyIdentifier(int key) + { + const auto it = s_qt_key_names.find(key); + return it == s_qt_key_names.end() ? QString() : it->second; + } + + std::optional GetKeyIdForIdentifier(const QString& key_identifier) + { + for (const auto& it : s_qt_key_names) + { + if (it.second == key_identifier) + return it.first; + } + + return std::nullopt; + } + + QString KeyEventToString(int key, Qt::KeyboardModifiers mods) + { + QString key_name = GetKeyIdentifier(key); + if (key_name.isEmpty()) + return {}; + + QString ret; + for (const QtKeyModifierEntry& mod : s_qt_key_modifiers) + { + if (mods & mod.mod && key != mod.key) + { + ret.append(mod.name); + ret.append('+'); + } + } + + ret.append(key_name); + return ret; + } + + std::optional ParseKeyString(const QString& key_str) + { + const QStringList sections = key_str.split('+'); + std::optional key_id = GetKeyIdForIdentifier(sections.last()); + if (!key_id) + return std::nullopt; + + int ret = key_id.value(); + + if (sections.size() > 1) + { + const int num_modifiers = sections.size() - 1; + for (int i = 0; i < num_modifiers; i++) + { + for (const QtKeyModifierEntry& mod : s_qt_key_modifiers) + { + if (sections[i] == mod.name) + { + ret |= static_cast(mod.mod); + break; + } + } + } + } + + return ret; + } + + int KeyEventToInt(int key, Qt::KeyboardModifiers mods) + { + int val = key; + if (mods != 0) + { + for (const QtKeyModifierEntry& mod : s_qt_key_modifiers) + { + if (mods & mod.mod && key != mod.key) + val |= static_cast(mod.mod); + } + } + + return val; + } + + void OpenURL(QWidget* parent, const QUrl& qurl) + { + if (!QDesktopServices::openUrl(qurl)) + { + QMessageBox::critical(parent, QObject::tr("Failed to open URL"), + QObject::tr("Failed to open URL.\n\nThe URL was: %1").arg(qurl.toString())); + } + } + + void OpenURL(QWidget* parent, const char* url) + { + return OpenURL(parent, QUrl::fromEncoded(QByteArray(url, static_cast(std::strlen(url))))); + } + + void OpenURL(QWidget* parent, const QString& url) + { + return OpenURL(parent, QUrl(url)); + } + + wxString QStringToWxString(const QString& str) + { + return wxString(str.toStdWString()); + } + + QString WxStringToQString(const wxString& str) + { + return QString::fromStdWString(str.ToStdWstring()); + } + +} // namespace QtUtils diff --git a/pcsx2-qt/QtUtils.h b/pcsx2-qt/QtUtils.h new file mode 100644 index 000000000..668eca5fb --- /dev/null +++ b/pcsx2-qt/QtUtils.h @@ -0,0 +1,85 @@ +/* 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 . + */ + +#pragma once +#include +#include +#include +#include +#include +#include + +Q_DECLARE_METATYPE(std::optional); +Q_DECLARE_METATYPE(std::function); + +class ByteStream; + +class QAction; +class QComboBox; +class QFrame; +class QKeyEvent; +class QTableView; +class QTreeView; +class QVariant; +class QWidget; +class QUrl; + +// TODO: Get rid of wx interoperability later on. +#include + +namespace QtUtils +{ + /// Marks an action as the "default" - i.e. makes the text bold. + void MarkActionAsDefault(QAction* action); + + /// Creates a horizontal line widget. + QFrame* CreateHorizontalLine(QWidget* parent); + + /// Returns the greatest parent of a widget, i.e. its dialog/window. + QWidget* GetRootWidget(QWidget* widget, bool stop_at_window_or_dialog = true); + + /// Resizes columns of the table view to at the specified widths. A negative width will stretch the column to use the + /// remaining space. + void ResizeColumnsForTableView(QTableView* view, const std::initializer_list& widths); + void ResizeColumnsForTreeView(QTreeView* view, const std::initializer_list& widths); + + /// Returns a string identifier for a Qt key ID. + QString GetKeyIdentifier(int key); + + /// Returns the integer Qt key ID for an identifier. + std::optional GetKeyIdForIdentifier(const QString& key_identifier); + + /// Stringizes a key event. + QString KeyEventToString(int key, Qt::KeyboardModifiers mods); + + /// Returns an integer id for a stringized key event. Modifiers are in the upper bits. + std::optional ParseKeyString(const QString& key_str); + + /// Returns a key id for a key event, including any modifiers. + int KeyEventToInt(int key, Qt::KeyboardModifiers mods); + + /// Opens a URL with the default handler. + void OpenURL(QWidget* parent, const QUrl& qurl); + + /// Opens a URL string with the default handler. + void OpenURL(QWidget* parent, const char* url); + + /// Opens a URL string with the default handler. + void OpenURL(QWidget* parent, const QString& url); + + // TODO: Get rid of wx interoperability later on. + wxString QStringToWxString(const QString& str); + QString WxStringToQString(const wxString& str); +} // namespace QtUtils \ No newline at end of file diff --git a/pcsx2-qt/SettingWidgetBinder.h b/pcsx2-qt/SettingWidgetBinder.h new file mode 100644 index 000000000..f64621fac --- /dev/null +++ b/pcsx2-qt/SettingWidgetBinder.h @@ -0,0 +1,417 @@ +/* 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 . + */ + +#pragma once +#include +#include +#include + +#if QT_VERSION >= QT_VERSION_CHECK(6, 0, 0) +#include +#else +#include +#endif + +#include +#include +#include +#include +#include +#include + +#include "EmuThread.h" +#include "QtHost.h" + +namespace SettingWidgetBinder +{ + template + struct SettingAccessor + { + static bool getBoolValue(const T* widget); + static void setBoolValue(T* widget, bool value); + + static int getIntValue(const T* widget); + static void setIntValue(T* widget, int value); + + static int getFloatValue(const T* widget); + static void setFloatValue(T* widget, int value); + + static QString getStringValue(const T* widget); + static void setStringValue(T* widget, const QString& value); + + template + static void connectValueChanged(T* widget, F func); + }; + + template <> + struct SettingAccessor + { + static bool getBoolValue(const QLineEdit* widget) { return widget->text().toInt() != 0; } + static void setBoolValue(QLineEdit* widget, bool value) + { + widget->setText(value ? QStringLiteral("1") : QStringLiteral("0")); + } + + static int getIntValue(const QLineEdit* widget) { return widget->text().toInt(); } + static void setIntValue(QLineEdit* widget, int value) { widget->setText(QString::number(value)); } + + static float getFloatValue(const QLineEdit* widget) { return widget->text().toFloat(); } + static void setFloatValue(QLineEdit* widget, float value) { widget->setText(QString::number(value)); } + + static QString getStringValue(const QLineEdit* widget) { return widget->text(); } + static void setStringValue(QLineEdit* widget, const QString& value) { widget->setText(value); } + + template + static void connectValueChanged(QLineEdit* widget, F func) + { + widget->connect(widget, &QLineEdit::textChanged, func); + } + }; + + template <> + struct SettingAccessor + { + static bool getBoolValue(const QComboBox* widget) { return widget->currentIndex() > 0; } + static void setBoolValue(QComboBox* widget, bool value) { widget->setCurrentIndex(value ? 1 : 0); } + + static int getIntValue(const QComboBox* widget) { return widget->currentIndex(); } + static void setIntValue(QComboBox* widget, int value) { widget->setCurrentIndex(value); } + + static float getFloatValue(const QComboBox* widget) { return static_cast(widget->currentIndex()); } + static void setFloatValue(QComboBox* widget, float value) { widget->setCurrentIndex(static_cast(value)); } + + static QString getStringValue(const QComboBox* widget) + { + const QVariant currentData(widget->currentData()); +#if QT_VERSION < QT_VERSION_CHECK(6, 0, 0) + if (currentData.type() == QVariant::String) + return currentData.toString(); +#else + if (currentData.metaType().id() == QMetaType::QString) + return currentData.toString(); +#endif + + return widget->currentText(); + } + + static void setStringValue(QComboBox* widget, const QString& value) + { + const int index = widget->findData(value); + if (index >= 0) + { + widget->setCurrentIndex(index); + return; + } + + widget->setCurrentText(value); + } + + template + static void connectValueChanged(QComboBox* widget, F func) + { + widget->connect(widget, static_cast(&QComboBox::currentIndexChanged), func); + } + }; + + template <> + struct SettingAccessor + { + static bool getBoolValue(const QCheckBox* widget) { return widget->isChecked(); } + static void setBoolValue(QCheckBox* widget, bool value) { widget->setChecked(value); } + + static int getIntValue(const QCheckBox* widget) { return widget->isChecked() ? 1 : 0; } + static void setIntValue(QCheckBox* widget, int value) { widget->setChecked(value != 0); } + + static float getFloatValue(const QCheckBox* widget) { return widget->isChecked() ? 1.0f : 0.0f; } + static void setFloatValue(QCheckBox* widget, float value) { widget->setChecked(value != 0.0f); } + + static QString getStringValue(const QCheckBox* widget) + { + return widget->isChecked() ? QStringLiteral("1") : QStringLiteral("0"); + } + static void setStringValue(QCheckBox* widget, const QString& value) { widget->setChecked(value.toInt() != 0); } + + template + static void connectValueChanged(QCheckBox* widget, F func) + { + widget->connect(widget, &QCheckBox::stateChanged, func); + } + }; + + template <> + struct SettingAccessor + { + static bool getBoolValue(const QSlider* widget) { return widget->value() > 0; } + static void setBoolValue(QSlider* widget, bool value) { widget->setValue(value ? 1 : 0); } + + static int getIntValue(const QSlider* widget) { return widget->value(); } + static void setIntValue(QSlider* widget, int value) { widget->setValue(value); } + + static float getFloatValue(const QSlider* widget) { return static_cast(widget->value()); } + static void setFloatValue(QSlider* widget, float value) { widget->setValue(static_cast(value)); } + + static QString getStringValue(const QSlider* widget) { return QString::number(widget->value()); } + static void setStringValue(QSlider* widget, const QString& value) { widget->setValue(value.toInt()); } + + template + static void connectValueChanged(QSlider* widget, F func) + { + widget->connect(widget, &QSlider::valueChanged, func); + } + }; + + template <> + struct SettingAccessor + { + static bool getBoolValue(const QSpinBox* widget) { return widget->value() > 0; } + static void setBoolValue(QSpinBox* widget, bool value) { widget->setValue(value ? 1 : 0); } + + static int getIntValue(const QSpinBox* widget) { return widget->value(); } + static void setIntValue(QSpinBox* widget, int value) { widget->setValue(value); } + + static float getFloatValue(const QSpinBox* widget) { return static_cast(widget->value()); } + static void setFloatValue(QSpinBox* widget, float value) { widget->setValue(static_cast(value)); } + + static QString getStringValue(const QSpinBox* widget) { return QString::number(widget->value()); } + static void setStringValue(QSpinBox* widget, const QString& value) { widget->setValue(value.toInt()); } + + template + static void connectValueChanged(QSpinBox* widget, F func) + { + widget->connect(widget, QOverload::of(&QSpinBox::valueChanged), func); + } + }; + + template <> + struct SettingAccessor + { + static bool getBoolValue(const QDoubleSpinBox* widget) { return widget->value() > 0.0; } + static void setBoolValue(QDoubleSpinBox* widget, bool value) { widget->setValue(value ? 1.0 : 0.0); } + + static int getIntValue(const QDoubleSpinBox* widget) { return static_cast(widget->value()); } + static void setIntValue(QDoubleSpinBox* widget, int value) { widget->setValue(static_cast(value)); } + + static float getFloatValue(const QDoubleSpinBox* widget) { return static_cast(widget->value()); } + static void setFloatValue(QDoubleSpinBox* widget, float value) { widget->setValue(static_cast(value)); } + + static QString getStringValue(const QDoubleSpinBox* widget) { return QString::number(widget->value()); } + static void setStringValue(QDoubleSpinBox* widget, const QString& value) { widget->setValue(value.toDouble()); } + + template + static void connectValueChanged(QDoubleSpinBox* widget, F func) + { + widget->connect(widget, QOverload::of(&QDoubleSpinBox::valueChanged), func); + } + }; + + template <> + struct SettingAccessor + { + static bool getBoolValue(const QAction* widget) { return widget->isChecked(); } + static void setBoolValue(QAction* widget, bool value) { widget->setChecked(value); } + + static int getIntValue(const QAction* widget) { return widget->isChecked() ? 1 : 0; } + static void setIntValue(QAction* widget, int value) { widget->setChecked(value != 0); } + + static float getFloatValue(const QAction* widget) { return widget->isChecked() ? 1.0f : 0.0f; } + static void setFloatValue(QAction* widget, float value) { widget->setChecked(value != 0.0f); } + + static QString getStringValue(const QAction* widget) + { + return widget->isChecked() ? QStringLiteral("1") : QStringLiteral("0"); + } + static void setStringValue(QAction* widget, const QString& value) { widget->setChecked(value.toInt() != 0); } + + template + static void connectValueChanged(QAction* widget, F func) + { + widget->connect(widget, &QAction::toggled, func); + } + }; + + /// Binds a widget's value to a setting, updating it when the value changes. + + template + static void BindWidgetToBoolSetting(WidgetType* widget, std::string section, std::string key, bool default_value) + { + using Accessor = SettingAccessor; + + bool value = QtHost::GetBaseBoolSettingValue(section.c_str(), key.c_str(), default_value); + + Accessor::setBoolValue(widget, value); + + Accessor::connectValueChanged(widget, [widget, section, key]() { + const bool new_value = Accessor::getBoolValue(widget); + QtHost::SetBaseBoolSettingValue(section.c_str(), key.c_str(), new_value); + g_emu_thread->applySettings(); + }); + } + + template + static void BindWidgetToIntSetting(WidgetType* widget, std::string section, std::string key, int default_value, int option_offset = 0) + { + using Accessor = SettingAccessor; + + s32 value = QtHost::GetBaseIntSettingValue(section.c_str(), key.c_str(), static_cast(default_value)) - option_offset; + + Accessor::setIntValue(widget, static_cast(value)); + + Accessor::connectValueChanged(widget, [widget, section, key, option_offset]() { + const int new_value = Accessor::getIntValue(widget); + QtHost::SetBaseIntSettingValue(section.c_str(), key.c_str(), new_value + option_offset); + g_emu_thread->applySettings(); + }); + } + + template + static void BindWidgetToFloatSetting(WidgetType* widget, std::string section, std::string key, float default_value) + { + using Accessor = SettingAccessor; + + float value = QtHost::GetBaseFloatSettingValue(section.c_str(), key.c_str(), default_value); + + Accessor::setFloatValue(widget, value); + + Accessor::connectValueChanged(widget, [widget, section, key]() { + const float new_value = Accessor::getFloatValue(widget); + QtHost::SetBaseFloatSettingValue(section.c_str(), key.c_str(), new_value); + g_emu_thread->applySettings(); + }); + } + + template + static void BindWidgetToNormalizedSetting(WidgetType* widget, std::string section, std::string key, float range, + float default_value) + { + using Accessor = SettingAccessor; + + float value = QtHost::GetBaseFloatSettingValue(section.c_str(), key.c_str(), default_value); + + Accessor::setIntValue(widget, static_cast(value * range)); + + Accessor::connectValueChanged(widget, [widget, section, key, range]() { + const float new_value = (static_cast(Accessor::getIntValue(widget)) / range); + QtHost::SetBaseFloatSettingValue(section.c_str(), key.c_str(), new_value); + g_emu_thread->applySettings(); + }); + } + + template + static void BindWidgetToStringSetting(WidgetType* widget, std::string section, std::string key, + std::string default_value = std::string()) + { + using Accessor = SettingAccessor; + + std::string value = QtHost::GetBaseStringSettingValue(section.c_str(), key.c_str(), default_value.c_str()); + + Accessor::setStringValue(widget, QString::fromStdString(value)); + + Accessor::connectValueChanged(widget, [widget, section, key]() { + const QString new_value = Accessor::getStringValue(widget); + if (!new_value.isEmpty()) + QtHost::SetBaseStringSettingValue(section.c_str(), key.c_str(), new_value.toUtf8().constData()); + else + QtHost::RemoveBaseSettingValue(section.c_str(), key.c_str()); + + g_emu_thread->applySettings(); + }); + } + + template + static void BindWidgetToEnumSetting(WidgetType* widget, std::string section, std::string key, + std::optional (*from_string_function)(const char* str), + const char* (*to_string_function)(DataType value), DataType default_value) + { + using Accessor = SettingAccessor; + using UnderlyingType = std::underlying_type_t; + + // TODO: Clean this up? + const std::string old_setting_string_value = + QtHost::GetBaseStringSettingValue(section.c_str(), key.c_str(), to_string_function(default_value)); + const std::optional old_setting_value = from_string_function(old_setting_string_value.c_str()); + if (old_setting_value.has_value()) + Accessor::setIntValue(widget, static_cast(static_cast(old_setting_value.value()))); + else + Accessor::setIntValue(widget, static_cast(static_cast(default_value))); + + Accessor::connectValueChanged(widget, [widget, section, key, to_string_function]() { + const DataType value = static_cast(static_cast(Accessor::getIntValue(widget))); + const char* string_value = to_string_function(value); + QtHost::SetBaseStringSettingValue(section.c_str(), key.c_str(), string_value); + g_emu_thread->applySettings(); + }); + } + + template + static void BindWidgetToEnumSetting(WidgetType* widget, std::string section, std::string key, const char** enum_names, + DataType default_value) + { + using Accessor = SettingAccessor; + using UnderlyingType = std::underlying_type_t; + + const std::string old_setting_string_value = QtHost::GetBaseStringSettingValue( + section.c_str(), key.c_str(), enum_names[static_cast(default_value)]); + + UnderlyingType enum_index = static_cast(default_value); + for (UnderlyingType i = 0; enum_names[i] != nullptr; i++) + { + if (old_setting_string_value == enum_names[i]) + { + enum_index = i; + break; + } + } + Accessor::setIntValue(widget, static_cast(enum_index)); + + Accessor::connectValueChanged(widget, [widget, section, key, enum_names]() { + const UnderlyingType value = static_cast(Accessor::getIntValue(widget)); + const char* string_value = enum_names[value]; + QtHost::SetBaseStringSettingValue(section.c_str(), key.c_str(), string_value); + g_emu_thread->applySettings(); + }); + } + + template + static void BindWidgetToEnumSetting(WidgetType* widget, std::string section, std::string key, const char** enum_names, + const char** enum_values, const char* default_value) + { + using Accessor = SettingAccessor; + + const std::string old_setting_string_value = + QtHost::GetBaseStringSettingValue(section.c_str(), key.c_str(), default_value); + + for (int i = 0; enum_names[i] != nullptr; i++) + widget->addItem(QString::fromUtf8(enum_names[i])); + + int enum_index = -1; + for (int i = 0; enum_values[i] != nullptr; i++) + { + if (old_setting_string_value == enum_values[i]) + { + enum_index = i; + break; + } + } + if (enum_index >= 0) + Accessor::setIntValue(widget, enum_index); + + Accessor::connectValueChanged(widget, [widget, section, key, enum_values]() { + const int value = Accessor::getIntValue(widget); + QtHost::SetBaseStringSettingValue(section.c_str(), key.c_str(), enum_values[value]); + g_emu_thread->applySettings(); + }); + } + +} // namespace SettingWidgetBinder diff --git a/pcsx2-qt/Settings/AdvancedSystemSettingsWidget.cpp b/pcsx2-qt/Settings/AdvancedSystemSettingsWidget.cpp new file mode 100644 index 000000000..65b11ab88 --- /dev/null +++ b/pcsx2-qt/Settings/AdvancedSystemSettingsWidget.cpp @@ -0,0 +1,50 @@ +/* PCSX2 - PS2 Emulator for PCs + * Copyright (C) 2002-2022 PCSX2 Dev Team + * + * PCSX2 is free software: you can redistribute it and/or modify it under the terms + * of the GNU Lesser General Public License as published by the Free Software Found- + * ation, either version 3 of the License, or (at your option) any later version. + * + * PCSX2 is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; + * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR + * PURPOSE. See the GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License along with PCSX2. + * If not, see . + */ + +#include "PrecompiledHeader.h" + +#include +#include + +#include "AdvancedSystemSettingsWidget.h" +#include "EmuThread.h" +#include "QtUtils.h" +#include "SettingWidgetBinder.h" +#include "SettingsDialog.h" + +AdvancedSystemSettingsWidget::AdvancedSystemSettingsWidget(QWidget* parent, SettingsDialog* dialog) + : QWidget(parent) +{ + m_ui.setupUi(this); + + SettingWidgetBinder::BindWidgetToBoolSetting(m_ui.eeRecompiler, "EmuCore/CPU/Recompiler", "EnableEE", true); + SettingWidgetBinder::BindWidgetToBoolSetting(m_ui.eeCache, "EmuCore/CPU/Recompiler", "EnableEECache", false); + SettingWidgetBinder::BindWidgetToBoolSetting(m_ui.eeINTCSpinDetection, "EmuCore/Speedhacks", "IntcStat", true); + SettingWidgetBinder::BindWidgetToBoolSetting(m_ui.eeWaitLoopDetection, "EmuCore/Speedhacks", "WaitLoop", true); + + SettingWidgetBinder::BindWidgetToBoolSetting(m_ui.vu0Recompiler, "EmuCore/CPU/Recompiler", "EnableVU0", true); + SettingWidgetBinder::BindWidgetToBoolSetting(m_ui.vu1Recompiler, "EmuCore/CPU/Recompiler", "EnableVU1", true); + SettingWidgetBinder::BindWidgetToBoolSetting(m_ui.vuFlagHack, "EmuCore/Speedhacks", "vuFlagHack", true); + + SettingWidgetBinder::BindWidgetToBoolSetting(m_ui.iopRecompiler, "EmuCore/CPU/Recompiler", "EnableIOP", true); + + SettingWidgetBinder::BindWidgetToBoolSetting(m_ui.gameFixes, "", "EnableGameFixes", true); + SettingWidgetBinder::BindWidgetToBoolSetting(m_ui.patches, "EmuCore", "EnablePatches", true); + + SettingWidgetBinder::BindWidgetToFloatSetting(m_ui.ntscFrameRate, "EmuCore/GS", "FramerateNTSC", 59.94f); + SettingWidgetBinder::BindWidgetToFloatSetting(m_ui.palFrameRate, "EmuCore/GS", "FrameratePAL", 50.00f); +} + +AdvancedSystemSettingsWidget::~AdvancedSystemSettingsWidget() = default; diff --git a/pcsx2-qt/Settings/AdvancedSystemSettingsWidget.h b/pcsx2-qt/Settings/AdvancedSystemSettingsWidget.h new file mode 100644 index 000000000..6eacbf81c --- /dev/null +++ b/pcsx2-qt/Settings/AdvancedSystemSettingsWidget.h @@ -0,0 +1,34 @@ +/* PCSX2 - PS2 Emulator for PCs + * Copyright (C) 2002-2022 PCSX2 Dev Team + * + * PCSX2 is free software: you can redistribute it and/or modify it under the terms + * of the GNU Lesser General Public License as published by the Free Software Found- + * ation, either version 3 of the License, or (at your option) any later version. + * + * PCSX2 is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; + * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR + * PURPOSE. See the GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License along with PCSX2. + * If not, see . + */ + +#pragma once + +#include + +#include "ui_AdvancedSystemSettingsWidget.h" + +class SettingsDialog; + +class AdvancedSystemSettingsWidget : public QWidget +{ + Q_OBJECT + +public: + AdvancedSystemSettingsWidget(QWidget* parent, SettingsDialog* dialog); + ~AdvancedSystemSettingsWidget(); + +private: + Ui::AdvancedSystemSettingsWidget m_ui; +}; diff --git a/pcsx2-qt/Settings/AdvancedSystemSettingsWidget.ui b/pcsx2-qt/Settings/AdvancedSystemSettingsWidget.ui new file mode 100644 index 000000000..665a3c6e4 --- /dev/null +++ b/pcsx2-qt/Settings/AdvancedSystemSettingsWidget.ui @@ -0,0 +1,197 @@ + + + AdvancedSystemSettingsWidget + + + + 0 + 0 + 648 + 481 + + + + Form + + + + 0 + + + 0 + + + 0 + + + 0 + + + + + EmotionEngine (MIPS-IV) + + + + + + Enable Recompiler + + + + + + + Wait Loop Detection + + + + + + + Enable Cache (Slow) + + + + + + + INTC Spin Detection + + + + + + + + + + Vector Units (VU) + + + + + + mVU Flag Hack + + + + + + + Enable VU0 Recompiler (Micro Mode) + + + + + + + Enable VU1 Recompiler + + + + + + + + + + I/O Processor (IOP, MIPS-I) + + + + + + Enable Recompiler + + + + + + + + + + Game Settings + + + + + + Enable Game Fixes + + + + + + + Enable Compatibility Patches + + + + + + + + + + Frame Rate Control + + + + + + hz + + + 0.010000000000000 + + + + + + + hz + + + 0.010000000000000 + + + + + + + PAL Frame Rate: + + + + + + + NTSC Frame Rate: + + + + + + + + + + Qt::Vertical + + + + 20 + 3 + + + + + + + + + + + diff --git a/pcsx2-qt/Settings/BIOSSettingsWidget.cpp b/pcsx2-qt/Settings/BIOSSettingsWidget.cpp new file mode 100644 index 000000000..f566e82ca --- /dev/null +++ b/pcsx2-qt/Settings/BIOSSettingsWidget.cpp @@ -0,0 +1,179 @@ +/* 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 +#include +#include + +#include "pcsx2/ps2/BiosTools.h" + +#include "BIOSSettingsWidget.h" +#include "QtUtils.h" +#include "SettingWidgetBinder.h" +#include "SettingsDialog.h" + +BIOSSettingsWidget::BIOSSettingsWidget(QWidget* parent, SettingsDialog* dialog) + : QWidget(parent) +{ + m_ui.setupUi(this); + + SettingWidgetBinder::BindWidgetToBoolSetting(m_ui.fastBoot, "EmuCore", "EnableFastBoot", true); + + dialog->registerWidgetHelp(m_ui.fastBoot, tr("Fast Boot"), tr("Unchecked"), + tr("Patches the BIOS to skip the console's boot animation.")); + + updateSearchDirectory(); + refreshList(); + + connect(m_ui.searchDirectory, &QLineEdit::textChanged, [this](const QString& text) { + QtHost::SetBaseStringSettingValue("Folders", "Bios", text.toUtf8().constData()); + QtHost::UpdateFolders(); + refreshList(); + }); + connect(m_ui.resetSearchDirectory, &QPushButton::clicked, [this]() { + QtHost::RemoveBaseSettingValue("Folders", "Bios"); + QtHost::UpdateFolders(); + updateSearchDirectory(); + refreshList(); + }); + connect(m_ui.browseSearchDirectory, &QPushButton::clicked, this, &BIOSSettingsWidget::browseSearchDirectory); + connect(m_ui.openSearchDirectory, &QPushButton::clicked, this, &BIOSSettingsWidget::openSearchDirectory); + connect(m_ui.refresh, &QPushButton::clicked, this, &BIOSSettingsWidget::refreshList); + connect(m_ui.fileList, &QTreeWidget::currentItemChanged, this, &BIOSSettingsWidget::listItemChanged); +} + +BIOSSettingsWidget::~BIOSSettingsWidget() +{ + if (m_refresh_thread) + m_refresh_thread->wait(); +} + +void BIOSSettingsWidget::refreshList() +{ + if (m_refresh_thread) + { + m_refresh_thread->wait(); + delete m_refresh_thread; + } + + QSignalBlocker blocker(m_ui.fileList); + m_ui.fileList->clear(); + m_ui.fileList->setEnabled(false); + + m_refresh_thread = new RefreshThread(this, m_ui.searchDirectory->text()); + m_refresh_thread->start(); +} + +void BIOSSettingsWidget::browseSearchDirectory() +{ + QString directory = QDir::toNativeSeparators(QFileDialog::getExistingDirectory( + QtUtils::GetRootWidget(this), tr("Select Directory"), m_ui.searchDirectory->text())); + if (directory.isEmpty()) + return; + + m_ui.searchDirectory->setText(directory); +} + +void BIOSSettingsWidget::openSearchDirectory() +{ + QtUtils::OpenURL(this, QUrl::fromLocalFile(m_ui.searchDirectory->text())); +} + +void BIOSSettingsWidget::updateSearchDirectory() +{ + // this will generate a full path + m_ui.searchDirectory->setText(QtUtils::WxStringToQString(EmuFolders::Bios.ToString())); +} + +void BIOSSettingsWidget::listRefreshed(const QVector& items) +{ + const std::string selected_bios(QtHost::GetBaseStringSettingValue("Filenames", "BIOS")); + + QSignalBlocker sb(m_ui.fileList); + for (const BIOSInfo& bi : items) + { + QTreeWidgetItem* item = new QTreeWidgetItem(); + item->setText(0, QString::fromStdString(bi.filename)); + item->setText(1, QString::fromStdString(bi.description)); + + switch (bi.region) + { + case 2: // Japan + item->setIcon(0, QIcon(QStringLiteral(":/icons/flag-jp.png"))); + break; + + case 3: // USA + item->setIcon(0, QIcon(QStringLiteral(":/icons/flag-us.png"))); + break; + + case 4: // Europe + item->setIcon(0, QIcon(QStringLiteral(":/icons/flag-eu.png"))); + break; + + case 5: // HK + case 6: // Free + case 7: // China + case 0: // T10K + case 1: // Test + default: + item->setIcon(0, QIcon(QStringLiteral(":/icons/flag-jp.png"))); + break; + } + + m_ui.fileList->addTopLevelItem(item); + + if (bi.filename == selected_bios) + item->setSelected(true); + } + m_ui.fileList->setEnabled(true); +} + +void BIOSSettingsWidget::listItemChanged(const QTreeWidgetItem* current, const QTreeWidgetItem* previous) +{ + QtHost::SetBaseStringSettingValue("Filenames", "BIOS", current->text(0).toUtf8().constData()); +} + +BIOSSettingsWidget::RefreshThread::RefreshThread(BIOSSettingsWidget* parent, const QString& directory) + : QThread(parent) + , m_parent(parent) + , m_directory(directory) +{ +} + +BIOSSettingsWidget::RefreshThread::~RefreshThread() = default; + +void BIOSSettingsWidget::RefreshThread::run() +{ + QVector items; + + QDir dir(m_directory); + if (dir.exists()) + { + for (const QFileInfo& info : dir.entryInfoList(QDir::Files)) + { + BIOSInfo bi; + QString full_path(info.absoluteFilePath()); + if (!IsBIOS(full_path.toUtf8().constData(), bi.version, bi.description, bi.region, bi.zone)) + continue; + + bi.filename = info.fileName().toStdString(); + items.push_back(std::move(bi)); + } + } + + QMetaObject::invokeMethod(m_parent, "listRefreshed", Qt::QueuedConnection, Q_ARG(const QVector&, items)); +} diff --git a/pcsx2-qt/Settings/BIOSSettingsWidget.h b/pcsx2-qt/Settings/BIOSSettingsWidget.h new file mode 100644 index 000000000..a76d2321e --- /dev/null +++ b/pcsx2-qt/Settings/BIOSSettingsWidget.h @@ -0,0 +1,75 @@ +/* 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 . + */ + +#pragma once +#include +#include +#include +#include +#include +#include + +#include "ui_BIOSSettingsWidget.h" + +class SettingsDialog; +class QThread; + +// TODO: Move to core. +struct BIOSInfo +{ + std::string filename; + std::string description; + std::string zone; + u32 version; + u32 region; +}; +Q_DECLARE_METATYPE(BIOSInfo); + +class BIOSSettingsWidget : public QWidget +{ + Q_OBJECT + +public: + BIOSSettingsWidget(QWidget* parent, SettingsDialog* dialog); + ~BIOSSettingsWidget(); + +private Q_SLOTS: + void refreshList(); + void browseSearchDirectory(); + void openSearchDirectory(); + void updateSearchDirectory(); + + void listItemChanged(const QTreeWidgetItem* current, const QTreeWidgetItem* previous); + void listRefreshed(const QVector& items); + +private: + Ui::BIOSSettingsWidget m_ui; + + class RefreshThread final : public QThread + { + public: + RefreshThread(BIOSSettingsWidget* parent, const QString& directory); + ~RefreshThread(); + + protected: + void run() override; + + private: + BIOSSettingsWidget* m_parent; + QString m_directory; + }; + + RefreshThread* m_refresh_thread = nullptr; +}; diff --git a/pcsx2-qt/Settings/BIOSSettingsWidget.ui b/pcsx2-qt/Settings/BIOSSettingsWidget.ui new file mode 100644 index 000000000..c51b3cf93 --- /dev/null +++ b/pcsx2-qt/Settings/BIOSSettingsWidget.ui @@ -0,0 +1,161 @@ + + + BIOSSettingsWidget + + + + 0 + 0 + 618 + 408 + + + + Form + + + + 0 + + + 0 + + + 0 + + + 0 + + + + + BIOS Directory + + + + + + PCSX2 will search for BIOS images in this directory. + + + true + + + + + + + + + + + + Browse... + + + + + + + Reset + + + + + + + + + + + + BIOS Selection + + + + + + + + Qt::Horizontal + + + + 40 + 20 + + + + + + + + Open in Explorer... + + + + + + + Refresh List + + + + + + + + + false + + + + Filename + + + + + Version + + + + + + + + + + + Options and Patches + + + + + + Fast Boot + + + + + + + + + + Qt::Vertical + + + + 20 + 40 + + + + + + + + + + + diff --git a/pcsx2-qt/Settings/ControllerBindingWidget.ui b/pcsx2-qt/Settings/ControllerBindingWidget.ui new file mode 100644 index 000000000..b57b05a21 --- /dev/null +++ b/pcsx2-qt/Settings/ControllerBindingWidget.ui @@ -0,0 +1,97 @@ + + + ControllerBindingWidget + + + + 0 + 0 + 834 + 560 + + + + Form + + + + 0 + + + 0 + + + 0 + + + 0 + + + + + Controller Type + + + + + + + + + Qt::Horizontal + + + + 460 + 20 + + + + + + + + + + Automatic binding + + + + .. + + + + + + + Load Profile + + + + .. + + + + + + + Save Profile + + + + .. + + + + + + + + + + + + + + + diff --git a/pcsx2-qt/Settings/ControllerBindingWidget_DualShock2.ui b/pcsx2-qt/Settings/ControllerBindingWidget_DualShock2.ui new file mode 100644 index 000000000..d4334fc31 --- /dev/null +++ b/pcsx2-qt/Settings/ControllerBindingWidget_DualShock2.ui @@ -0,0 +1,1296 @@ + + + ControllerBindingWidget_DualShock2 + + + + 0 + 0 + 1100 + 550 + + + + + 0 + 0 + + + + + 1100 + 550 + + + + Form + + + + 0 + + + 0 + + + 0 + + + 0 + + + + + + + D-Pad + + + + + + Down + + + + 6 + + + 6 + + + 6 + + + 6 + + + + + + 100 + 0 + + + + + 100 + 16777215 + + + + PushButton + + + + + + + + + + Left + + + + 6 + + + 6 + + + 6 + + + 6 + + + + + + 100 + 0 + + + + + 100 + 16777215 + + + + PushButton + + + + + + + + + + Up + + + + 6 + + + 6 + + + 6 + + + 6 + + + + + + 100 + 0 + + + + + 100 + 16777215 + + + + PushButton + + + + + + + + + + Right + + + + 6 + + + 6 + + + 6 + + + 6 + + + + + + 100 + 0 + + + + + 100 + 16777215 + + + + PushButton + + + + + + + + + + + + + Left Analog + + + + + + Down + + + + 6 + + + 6 + + + 6 + + + 6 + + + + + + 100 + 0 + + + + + 100 + 16777215 + + + + PushButton + + + + + + + + + + Left + + + + 6 + + + 6 + + + 6 + + + 6 + + + + + + 100 + 0 + + + + + 100 + 16777215 + + + + PushButton + + + + + + + + + + Up + + + + 6 + + + 6 + + + 6 + + + 6 + + + + + + 100 + 0 + + + + + 100 + 16777215 + + + + PushButton + + + + + + + + + + Right + + + + 6 + + + 6 + + + 6 + + + 6 + + + + + + 100 + 0 + + + + + 100 + 16777215 + + + + PushButton + + + + + + + + + + + + + Axis Scale + + + + + + Qt::Horizontal + + + + + + + 1.0x + + + + + + + + + + Qt::Vertical + + + + 20 + 40 + + + + + + + + + + + + + + L2 + + + + 6 + + + 6 + + + 6 + + + 6 + + + + + + 100 + 0 + + + + + 100 + 16777215 + + + + PushButton + + + + + + + + + + R2 + + + + 6 + + + 6 + + + 6 + + + 6 + + + + + + 100 + 0 + + + + + 100 + 16777215 + + + + PushButton + + + + + + + + + + Select + + + + 6 + + + 6 + + + 6 + + + 6 + + + + + + 100 + 0 + + + + + 100 + 16777215 + + + + PushButton + + + + + + + + + + L1 + + + + 6 + + + 6 + + + 6 + + + 6 + + + + + + 100 + 0 + + + + + 100 + 16777215 + + + + PushButton + + + + + + + + + + R1 + + + + 6 + + + 6 + + + 6 + + + 6 + + + + + + 100 + 0 + + + + + 100 + 16777215 + + + + PushButton + + + + + + + + + + Start + + + + 6 + + + 6 + + + 6 + + + 6 + + + + + + 100 + 0 + + + + + 100 + 16777215 + + + + PushButton + + + + + + + + + + + + + + Qt::Horizontal + + + + 40 + 20 + + + + + + + + + 0 + 0 + + + + + 400 + 266 + + + + + + + :/images/dualshock-2.png + + + true + + + + + + + Qt::Horizontal + + + + 40 + 20 + + + + + + + + + + + + L3 + + + + 6 + + + 6 + + + 6 + + + 6 + + + + + + 100 + 0 + + + + + 100 + 16777215 + + + + PushButton + + + + + + + + + + Large Motor + + + + 6 + + + 6 + + + 6 + + + 6 + + + + + + 100 + 0 + + + + + 100 + 16777215 + + + + PushButton + + + + + + + + + + Small Motor + + + + 6 + + + 6 + + + 6 + + + 6 + + + + + + 100 + 0 + + + + + 100 + 16777215 + + + + PushButton + + + + + + + + + + R3 + + + + 6 + + + 6 + + + 6 + + + 6 + + + + + + 100 + 0 + + + + + 100 + 16777215 + + + + PushButton + + + + + + + + + + Qt::Vertical + + + + 20 + 40 + + + + + + + + + + + + + + Face Buttons + + + + + + Cross + + + + 6 + + + 6 + + + 6 + + + 6 + + + + + + 100 + 0 + + + + + 100 + 16777215 + + + + PushButton + + + + + + + + + + Square + + + + 6 + + + 6 + + + 6 + + + 6 + + + + + + 100 + 0 + + + + + 100 + 16777215 + + + + PushButton + + + + + + + + + + Triangle + + + + 6 + + + 6 + + + 6 + + + 6 + + + + + + 100 + 0 + + + + + 100 + 16777215 + + + + PushButton + + + + + + + + + + Circle + + + + 6 + + + 6 + + + 6 + + + 6 + + + + + + 100 + 0 + + + + + 100 + 16777215 + + + + PushButton + + + + + + + + + + + + + Right Analog + + + + + + Down + + + + 6 + + + 6 + + + 6 + + + 6 + + + + + + 100 + 0 + + + + + 100 + 16777215 + + + + PushButton + + + + + + + + + + Left + + + + 6 + + + 6 + + + 6 + + + 6 + + + + + + 100 + 0 + + + + + 100 + 16777215 + + + + PushButton + + + + + + + + + + Up + + + + 6 + + + 6 + + + 6 + + + 6 + + + + + + 100 + 0 + + + + + 100 + 16777215 + + + + PushButton + + + + + + + + + + Right + + + + 6 + + + 6 + + + 6 + + + 6 + + + + + + 100 + 0 + + + + + 100 + 16777215 + + + + PushButton + + + + + + + + + + + + + Vibration Scale + + + + + + Qt::Horizontal + + + + + + + 1.0x + + + + + + + + + + Qt::Vertical + + + + 20 + 40 + + + + + + + + + + + InputBindingWidget + QPushButton +
Settings/InputBindingWidget.h
+
+ + InputVibrationBindingWidget + QPushButton +
Settings/InputBindingWidget.h
+
+
+ + + + + +
diff --git a/pcsx2-qt/Settings/ControllerBindingWidgets.cpp b/pcsx2-qt/Settings/ControllerBindingWidgets.cpp new file mode 100644 index 000000000..667b89e84 --- /dev/null +++ b/pcsx2-qt/Settings/ControllerBindingWidgets.cpp @@ -0,0 +1,158 @@ +/* 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 +#include + +#include "ControllerBindingWidgets.h" +#include "ControllerSettingsDialog.h" +#include "EmuThread.h" +#include "QtUtils.h" +#include "SettingWidgetBinder.h" +#include "SettingsDialog.h" + +#include "common/StringUtil.h" +#include "pcsx2/PAD/Host/PAD.h" + +ControllerBindingWidget::ControllerBindingWidget(QWidget* parent, ControllerSettingsDialog* dialog, u32 port) + : QWidget(parent) + , m_dialog(dialog) + , m_config_section(StringUtil::StdStringFromFormat("Pad%u", port + 1u)) + , m_port_number(port) +{ + m_ui.setupUi(this); + populateControllerTypes(); + onTypeChanged(); + + SettingWidgetBinder::BindWidgetToStringSetting(m_ui.controllerType, m_config_section, "Type", "None"); + connect(m_ui.controllerType, QOverload::of(&QComboBox::currentIndexChanged), this, + &ControllerBindingWidget::onTypeChanged); +} + +ControllerBindingWidget::~ControllerBindingWidget() +{ +} + +void ControllerBindingWidget::populateControllerTypes() +{ + m_ui.controllerType->addItem(tr("None (Not Connected)"), QStringLiteral("None")); + for (const std::string& type : PAD::GetControllerTypeNames()) + m_ui.controllerType->addItem(QString::fromStdString(type), QString::fromStdString(type)); +} + +void ControllerBindingWidget::onTypeChanged() +{ + if (m_current_widget) + { + m_ui.verticalLayout->removeWidget(m_current_widget); + delete m_current_widget; + m_current_widget = nullptr; + } + + m_controller_type = QtHost::GetBaseStringSettingValue(m_config_section.c_str(), "Type"); + + const int index = m_ui.controllerType->findData(QString::fromStdString(m_controller_type)); + if (index >= 0 && index != m_ui.controllerType->currentIndex()) + { + QSignalBlocker sb(m_ui.controllerType); + m_ui.controllerType->setCurrentIndex(index); + } + + if (m_controller_type == "DualShock2") + m_current_widget = ControllerBindingWidget_DualShock2::createInstance(this); + else + m_current_widget = new ControllerBindingWidget_Base(this); + + m_ui.verticalLayout->addWidget(m_current_widget, 1); +} + +////////////////////////////////////////////////////////////////////////// + +ControllerBindingWidget_Base::ControllerBindingWidget_Base(ControllerBindingWidget* parent) + : QWidget(parent) +{ +} + +ControllerBindingWidget_Base::~ControllerBindingWidget_Base() +{ +} + +void ControllerBindingWidget_Base::initBindingWidgets() +{ + const std::string& type = getControllerType(); + const std::string& config_section = getConfigSection(); + std::vector bindings(PAD::GetControllerBinds(type)); + + for (std::string& binding : bindings) + { + InputBindingWidget* widget = findChild(QString::fromStdString(binding)); + if (!widget) + { + Console.Error("(ControllerBindingWidget_Base) No widget found for '%s' (%*s)", + binding.c_str(), static_cast(type.size()), type.data()); + continue; + } + + widget->setKey(config_section, std::move(binding)); + } + + const PAD::VibrationCapabilities vibe_caps = PAD::GetControllerVibrationCapabilities(type); + switch (vibe_caps) + { + case PAD::VibrationCapabilities::LargeSmallMotors: + { + InputVibrationBindingWidget* widget = findChild(QStringLiteral("LargeMotor")); + if (widget) + widget->setKey(getDialog(), config_section, "LargeMotor"); + + widget = findChild(QStringLiteral("SmallMotor")); + if (widget) + widget->setKey(getDialog(), config_section, "SmallMotor"); + } + break; + + case PAD::VibrationCapabilities::SingleMotor: + { + InputVibrationBindingWidget* widget = findChild(QStringLiteral("Motor")); + if (widget) + widget->setKey(getDialog(), config_section, "Motor"); + } + break; + + case PAD::VibrationCapabilities::NoVibration: + default: + break; + } +} + +ControllerBindingWidget_DualShock2::ControllerBindingWidget_DualShock2(ControllerBindingWidget* parent) + : ControllerBindingWidget_Base(parent) +{ + m_ui.setupUi(this); + initBindingWidgets(); +} + +ControllerBindingWidget_DualShock2::~ControllerBindingWidget_DualShock2() +{ +} + +ControllerBindingWidget_Base* ControllerBindingWidget_DualShock2::createInstance(ControllerBindingWidget* parent) +{ + return new ControllerBindingWidget_DualShock2(parent); +} + +////////////////////////////////////////////////////////////////////////// \ No newline at end of file diff --git a/pcsx2-qt/Settings/ControllerBindingWidgets.h b/pcsx2-qt/Settings/ControllerBindingWidgets.h new file mode 100644 index 000000000..d516f7cf5 --- /dev/null +++ b/pcsx2-qt/Settings/ControllerBindingWidgets.h @@ -0,0 +1,86 @@ +/* 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 . + */ + +#pragma once + +#include + +#include "ui_ControllerBindingWidget.h" +#include "ui_ControllerBindingWidget_DualShock2.h" + +class InputBindingWidget; +class ControllerSettingsDialog; +class ControllerBindingWidget_Base; + +class ControllerBindingWidget final : public QWidget +{ + Q_OBJECT + +public: + ControllerBindingWidget(QWidget* parent, ControllerSettingsDialog* dialog, u32 port); + ~ControllerBindingWidget(); + + __fi ControllerSettingsDialog* getDialog() const { return m_dialog; } + __fi const std::string& getConfigSection() const { return m_config_section; } + __fi const std::string& getControllerType() const { return m_controller_type; } + __fi u32 getPortNumber() const { return m_port_number; } + +private Q_SLOTS: + void onTypeChanged(); + +private: + void populateControllerTypes(); + + Ui::ControllerBindingWidget m_ui; + + ControllerSettingsDialog* m_dialog; + + std::string m_config_section; + std::string m_controller_type; + u32 m_port_number; + + ControllerBindingWidget_Base* m_current_widget = nullptr; +}; + +class ControllerBindingWidget_Base : public QWidget +{ + Q_OBJECT + +public: + ControllerBindingWidget_Base(ControllerBindingWidget* parent); + virtual ~ControllerBindingWidget_Base(); + + __fi ControllerSettingsDialog* getDialog() const { return static_cast(parent())->getDialog(); } + __fi const std::string& getConfigSection() const { return static_cast(parent())->getConfigSection(); } + __fi const std::string& getControllerType() const { return static_cast(parent())->getControllerType(); } + __fi u32 getPortNumber() const { return static_cast(parent())->getPortNumber(); } + +protected: + void initBindingWidgets(); +}; + +class ControllerBindingWidget_DualShock2 final : public ControllerBindingWidget_Base +{ + Q_OBJECT + +public: + ControllerBindingWidget_DualShock2(ControllerBindingWidget* parent); + ~ControllerBindingWidget_DualShock2(); + + static ControllerBindingWidget_Base* createInstance(ControllerBindingWidget* parent); + +private: + Ui::ControllerBindingWidget_DualShock2 m_ui; +}; diff --git a/pcsx2-qt/Settings/ControllerGlobalSettingsWidget.cpp b/pcsx2-qt/Settings/ControllerGlobalSettingsWidget.cpp new file mode 100644 index 000000000..7cf805a78 --- /dev/null +++ b/pcsx2-qt/Settings/ControllerGlobalSettingsWidget.cpp @@ -0,0 +1,64 @@ +/* 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 "Frontend/InputManager.h" +#include "Settings/ControllerGlobalSettingsWidget.h" +#include "Settings/ControllerSettingsDialog.h" +#include "QtUtils.h" +#include "SettingWidgetBinder.h" + +ControllerGlobalSettingsWidget::ControllerGlobalSettingsWidget(QWidget* parent, ControllerSettingsDialog* dialog) + : QWidget(parent) +{ + m_ui.setupUi(this); + + SettingWidgetBinder::BindWidgetToBoolSetting(m_ui.enableSDLSource, "InputSources", "SDL", true); + SettingWidgetBinder::BindWidgetToBoolSetting(m_ui.enableSDLEnhancedMode, "InputSources", "SDLControllerEnhancedMode", false); + SettingWidgetBinder::BindWidgetToBoolSetting(m_ui.enableXInputSource, "InputSources", "XInput", false); + + connect(m_ui.enableSDLSource, &QCheckBox::stateChanged, this, &ControllerGlobalSettingsWidget::updateSDLOptionsEnabled); + updateSDLOptionsEnabled(); +} + +ControllerGlobalSettingsWidget::~ControllerGlobalSettingsWidget() = default; + +void ControllerGlobalSettingsWidget::addDeviceToList(const QString& identifier, const QString& name) +{ + QListWidgetItem* item = new QListWidgetItem(); + item->setText(QStringLiteral("%1: %2").arg(identifier).arg(name)); + item->setData(Qt::UserRole, identifier); + m_ui.deviceList->addItem(item); +} + +void ControllerGlobalSettingsWidget::removeDeviceFromList(const QString& identifier) +{ + const int count = m_ui.deviceList->count(); + for (int i = 0; i < count; i++) + { + QListWidgetItem* item = m_ui.deviceList->item(i); + if (item->data(Qt::UserRole) != identifier) + continue; + + delete m_ui.deviceList->takeItem(i); + } +} + +void ControllerGlobalSettingsWidget::updateSDLOptionsEnabled() +{ + const bool enabled = m_ui.enableSDLSource->isChecked(); + m_ui.enableSDLEnhancedMode->setEnabled(enabled); +} diff --git a/pcsx2-qt/Settings/ControllerGlobalSettingsWidget.h b/pcsx2-qt/Settings/ControllerGlobalSettingsWidget.h new file mode 100644 index 000000000..e49e4ee50 --- /dev/null +++ b/pcsx2-qt/Settings/ControllerGlobalSettingsWidget.h @@ -0,0 +1,42 @@ +/* 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 . + */ + +#pragma once + +#include +#include +#include +#include + +#include "ui_ControllerGlobalSettingsWidget.h" + +class ControllerSettingsDialog; + +class ControllerGlobalSettingsWidget : public QWidget +{ + Q_OBJECT + +public: + ControllerGlobalSettingsWidget(QWidget* parent, ControllerSettingsDialog* dialog); + ~ControllerGlobalSettingsWidget(); + + void addDeviceToList(const QString& identifier, const QString& name); + void removeDeviceFromList(const QString& identifier); + +private: + void updateSDLOptionsEnabled(); + + Ui::ControllerGlobalSettingsWidget m_ui; +}; diff --git a/pcsx2-qt/Settings/ControllerGlobalSettingsWidget.ui b/pcsx2-qt/Settings/ControllerGlobalSettingsWidget.ui new file mode 100644 index 000000000..0dae013b2 --- /dev/null +++ b/pcsx2-qt/Settings/ControllerGlobalSettingsWidget.ui @@ -0,0 +1,173 @@ + + + ControllerGlobalSettingsWidget + + + + 0 + 0 + 880 + 712 + + + + Form + + + + 0 + + + 0 + + + 0 + + + 0 + + + + + + + SDL Input Source + + + + + + The SDL input source supports most controllers, and provides advanced functionality for Dualshock 4/Dualsense pads in Bluetooth mode (Vibration/LED Control). + + + true + + + + + + + Enable SDL Input Source + + + + + + + Dualshock 4/Dualsense Enhanced Mode + + + + + + + + + + XInput Source + + + + + + The XInput source provides support for XBox 360/XBox One/XBox Series controllers, and third party controllers which implement the XInput protocol. + + + true + + + + + + + Enable XInput Input Source + + + + + + + + + + Controller Multitap + + + + + + The multitap enables up to 8 controllers to be connected to the console. Each multitap provides 4 ports. Multitap is not supported by all games. (NOT YET IMPLEMENTED) + + + true + + + + + + + false + + + Multitap on Console Port 1 + + + + + + + false + + + Multitap on Console Port 2 + + + + + + + + + + Qt::Vertical + + + + 20 + 40 + + + + + + + + + + Detected Devices + + + + + + + 200 + 0 + + + + + 200 + 16777215 + + + + + + + + + + + + diff --git a/pcsx2-qt/Settings/ControllerSettingsDialog.cpp b/pcsx2-qt/Settings/ControllerSettingsDialog.cpp new file mode 100644 index 000000000..f10dab90c --- /dev/null +++ b/pcsx2-qt/Settings/ControllerSettingsDialog.cpp @@ -0,0 +1,119 @@ +/* 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 "EmuThread.h" +#include "QtHost.h" +#include "Settings/ControllerSettingsDialog.h" +#include "Settings/ControllerGlobalSettingsWidget.h" +#include "Settings/ControllerBindingWidgets.h" +#include "Settings/HotkeySettingsWidget.h" + +#include +#include + +ControllerSettingsDialog::ControllerSettingsDialog(QWidget* parent /* = nullptr */) + : QDialog(parent) +{ + m_ui.setupUi(this); + + setWindowFlags(windowFlags() & ~Qt::WindowContextHelpButtonHint); + + m_global_settings = new ControllerGlobalSettingsWidget(m_ui.settingsContainer, this); + m_ui.settingsContainer->insertWidget(0, m_global_settings); + + for (u32 i = 0; i < MAX_PORTS; i++) + { + m_port_bindings[i] = new ControllerBindingWidget(m_ui.settingsContainer, this, i); + m_ui.settingsContainer->insertWidget(i + 1, m_port_bindings[i]); + } + + m_hotkey_settings = new HotkeySettingsWidget(m_ui.settingsContainer, this); + m_ui.settingsContainer->insertWidget(3, m_hotkey_settings); + + m_ui.settingsCategory->setSizePolicy(QSizePolicy::Minimum, QSizePolicy::Minimum); + connect(m_ui.settingsCategory, &QListWidget::currentRowChanged, this, &ControllerSettingsDialog::onCategoryCurrentRowChanged); + connect(m_ui.buttonBox, &QDialogButtonBox::rejected, this, &ControllerSettingsDialog::close); + + connect(g_emu_thread, &EmuThread::onInputDevicesEnumerated, this, &ControllerSettingsDialog::onInputDevicesEnumerated); + connect(g_emu_thread, &EmuThread::onInputDeviceConnected, this, &ControllerSettingsDialog::onInputDeviceConnected); + connect(g_emu_thread, &EmuThread::onInputDeviceDisconnected, this, &ControllerSettingsDialog::onInputDeviceDisconnected); + connect(g_emu_thread, &EmuThread::onVibrationMotorsEnumerated, this, &ControllerSettingsDialog::onVibrationMotorsEnumerated); + + // trigger a device enumeration to populate the device list + g_emu_thread->enumerateInputDevices(); + g_emu_thread->enumerateVibrationMotors(); +} + +ControllerSettingsDialog::~ControllerSettingsDialog() = default; + +void ControllerSettingsDialog::setCategory(Category category) +{ + switch (category) + { + case Category::GlobalSettings: + m_ui.settingsCategory->setCurrentRow(0); + break; + + // TODO: These will need to take multitap into consideration in the future. + case Category::FirstControllerSettings: + m_ui.settingsCategory->setCurrentRow(1); + break; + + case Category::HotkeySettings: + m_ui.settingsCategory->setCurrentRow(3); + break; + + default: + break; + } +} + +void ControllerSettingsDialog::onCategoryCurrentRowChanged(int row) +{ + m_ui.settingsContainer->setCurrentIndex(row); +} + +void ControllerSettingsDialog::onInputDevicesEnumerated(const QList>& devices) +{ + for (const QPair& device : devices) + m_global_settings->addDeviceToList(device.first, device.second); +} + +void ControllerSettingsDialog::onInputDeviceConnected(const QString& identifier, const QString& device_name) +{ + m_global_settings->addDeviceToList(identifier, device_name); + g_emu_thread->enumerateVibrationMotors(); +} + +void ControllerSettingsDialog::onInputDeviceDisconnected(const QString& identifier) +{ + m_global_settings->removeDeviceFromList(identifier); + g_emu_thread->enumerateVibrationMotors(); +} + +void ControllerSettingsDialog::onVibrationMotorsEnumerated(const QList& motors) +{ + m_vibration_motors.clear(); + m_vibration_motors.reserve(motors.size()); + + for (const InputBindingKey key : motors) + { + const std::string key_str(InputManager::ConvertInputBindingKeyToString(key)); + if (!key_str.empty()) + m_vibration_motors.push_back(QString::fromStdString(key_str)); + } +} diff --git a/pcsx2-qt/Settings/ControllerSettingsDialog.h b/pcsx2-qt/Settings/ControllerSettingsDialog.h new file mode 100644 index 000000000..5a5a982d6 --- /dev/null +++ b/pcsx2-qt/Settings/ControllerSettingsDialog.h @@ -0,0 +1,74 @@ +/* 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 . + */ + +#pragma once +#include "ui_ControllerSettingsDialog.h" +#include "Frontend/InputManager.h" +#include +#include +#include +#include +#include +#include + +class ControllerGlobalSettingsWidget; +class ControllerBindingWidget; +class HotkeySettingsWidget; + +class ControllerSettingsDialog final : public QDialog +{ + Q_OBJECT + +public: + enum class Category + { + GlobalSettings, + FirstControllerSettings, + HotkeySettings, + Count + }; + + enum : u32 + { + MAX_PORTS = 2 + }; + + ControllerSettingsDialog(QWidget* parent = nullptr); + ~ControllerSettingsDialog(); + + HotkeySettingsWidget* getHotkeySettingsWidget() const { return m_hotkey_settings; } + + __fi const QStringList& getVibrationMotors() const { return m_vibration_motors; } + +public Q_SLOTS: + void setCategory(Category category); + +private Q_SLOTS: + void onCategoryCurrentRowChanged(int row); + + void onInputDevicesEnumerated(const QList>& devices); + void onInputDeviceConnected(const QString& identifier, const QString& device_name); + void onInputDeviceDisconnected(const QString& identifier); + void onVibrationMotorsEnumerated(const QList& motors); + +private: + Ui::ControllerSettingsDialog m_ui; + + ControllerGlobalSettingsWidget* m_global_settings = nullptr; + std::array m_port_bindings{}; + HotkeySettingsWidget* m_hotkey_settings = nullptr; + + QStringList m_vibration_motors; +}; diff --git a/pcsx2-qt/Settings/ControllerSettingsDialog.ui b/pcsx2-qt/Settings/ControllerSettingsDialog.ui new file mode 100644 index 000000000..4c1287d2a --- /dev/null +++ b/pcsx2-qt/Settings/ControllerSettingsDialog.ui @@ -0,0 +1,122 @@ + + + ControllerSettingsDialog + + + Qt::WindowModal + + + + 0 + 0 + 1274 + 668 + + + + + 0 + 0 + + + + PCSX2 Controller Settings + + + + + + + 0 + 0 + + + + + 150 + 0 + + + + + 150 + 16777215 + + + + + 32 + 32 + + + + + Global Settings + + + + .. + + + + + Controller 1 + + + + .. + + + + + Controller 2 + + + + .. + + + + + Hotkeys + + + + .. + + + + + + + + + 1100 + 620 + + + + 4 + + + + + + + + + + + + + QDialogButtonBox::Close + + + + + + + + + + diff --git a/pcsx2-qt/Settings/EmulationSettingsWidget.cpp b/pcsx2-qt/Settings/EmulationSettingsWidget.cpp new file mode 100644 index 000000000..5a09f20d0 --- /dev/null +++ b/pcsx2-qt/Settings/EmulationSettingsWidget.cpp @@ -0,0 +1,165 @@ +/* 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 +#include + +#include "EmulationSettingsWidget.h" +#include "QtUtils.h" +#include "SettingWidgetBinder.h" +#include "SettingsDialog.h" + +static constexpr u32 DEFAULT_FRAME_LATENCY = 2; + +static void FillComboBoxWithEmulationSpeeds(QComboBox* cb) +{ + cb->addItem(qApp->translate("GeneralSettingsWidget", "Unlimited"), QVariant(0.0f)); + + static const int speeds[] = {10, 20, 30, 40, 50, 60, 70, 80, 90, 100, 125, 150, 175, + 200, 250, 300, 350, 400, 450, 500, 600, 700, 800, 900, 1000}; + for (const int speed : speeds) + { + cb->addItem(qApp->translate("EmulationSettingsWidget", "%1% [%2 FPS (NTSC) / %3 FPS (PAL)]") + .arg(speed) + .arg((60 * speed) / 100) + .arg((50 * speed) / 100), + QVariant(static_cast(speed) / 100.0f)); + } +} + +EmulationSettingsWidget::EmulationSettingsWidget(QWidget* parent, SettingsDialog* dialog) + : QWidget(parent) +{ + m_ui.setupUi(this); + + FillComboBoxWithEmulationSpeeds(m_ui.normalSpeed); + if (const int index = + m_ui.normalSpeed->findData(QVariant(QtHost::GetBaseFloatSettingValue("Framerate", "NominalScalar", 1.0f))); + index >= 0) + { + m_ui.normalSpeed->setCurrentIndex(index); + } + connect(m_ui.normalSpeed, QOverload::of(&QComboBox::currentIndexChanged), this, + &EmulationSettingsWidget::onNormalSpeedIndexChanged); + FillComboBoxWithEmulationSpeeds(m_ui.fastForwardSpeed); + + if (const int index = + m_ui.fastForwardSpeed->findData(QVariant(QtHost::GetBaseFloatSettingValue("Framerate", "TurboScalar", 2.0f))); + index >= 0) + { + m_ui.fastForwardSpeed->setCurrentIndex(index); + } + connect(m_ui.fastForwardSpeed, QOverload::of(&QComboBox::currentIndexChanged), this, + &EmulationSettingsWidget::onFastForwardSpeedIndexChanged); + + FillComboBoxWithEmulationSpeeds(m_ui.slowMotionSpeed); + if (const int index = + m_ui.slowMotionSpeed->findData(QVariant(QtHost::GetBaseFloatSettingValue("Framerate", "SlomoScalar", 0.5f))); + index >= 0) + { + m_ui.slowMotionSpeed->setCurrentIndex(index); + } + connect(m_ui.slowMotionSpeed, QOverload::of(&QComboBox::currentIndexChanged), this, + &EmulationSettingsWidget::onSlowMotionSpeedIndexChanged); + + SettingWidgetBinder::BindWidgetToBoolSetting(m_ui.speedLimiter, "EmuCore/GS", "FrameLimitEnable", true); + SettingWidgetBinder::BindWidgetToIntSetting(m_ui.maxFrameLatency, "EmuCore/GS", "VsyncQueueSize", DEFAULT_FRAME_LATENCY); + SettingWidgetBinder::BindWidgetToBoolSetting(m_ui.syncToHostRefreshRate, "EmuCore/GS", "SyncToHostRefreshRate", + false); + connect(m_ui.optimalFramePacing, &QCheckBox::toggled, this, &EmulationSettingsWidget::onOptimalFramePacingChanged); + + SettingWidgetBinder::BindWidgetToBoolSetting(m_ui.cheats, "EmuCore", "EnableCheats", false); + SettingWidgetBinder::BindWidgetToBoolSetting(m_ui.widescreenPatches, "EmuCore", "EnableWideScreenPatches", false); + SettingWidgetBinder::BindWidgetToBoolSetting(m_ui.perGameSettings, "EmuCore", "EnablePerGameSettings", true); + SettingWidgetBinder::BindWidgetToBoolSetting(m_ui.hostFilesystem, "EmuCore", "HostFs", false); + + dialog->registerWidgetHelp( + m_ui.normalSpeed, tr("Normal Speed"), "100%", + tr("Sets the target emulation speed. It is not guaranteed that this speed will be reached, " + "and if not, the emulator will run as fast as it can manage.")); + dialog->registerWidgetHelp( + m_ui.fastForwardSpeed, tr("Fast Forward Speed"), tr("User Preference"), + tr("Sets the fast forward speed. This speed will be used when the fast forward hotkey is pressed/toggled.")); + dialog->registerWidgetHelp( + m_ui.slowMotionSpeed, tr("Slow Motion Speed"), tr("User Preference"), + tr("Sets the slow motion speed. This speed will be used when the slow motion hotkey is pressed/toggled.")); + + dialog->registerWidgetHelp( + m_ui.syncToHostRefreshRate, tr("Sync To Host Refresh Rate"), tr("Unchecked"), + tr("Adjusts the emulation speed so the console's refresh rate matches the host's refresh rate when both VSync and " + "Audio Resampling settings are enabled. This results in the smoothest animations possible, at the cost of " + "potentially increasing the emulation speed by less than 1%. Sync To Host Refresh Rate will not take effect if " + "the console's refresh rate is too far from the host's refresh rate. Users with variable refresh rate displays " + "should disable this option.")); + dialog->registerWidgetHelp(m_ui.cheats, tr("Enable Cheats"), tr("Unchecked"), + tr("Automatically loads and applies cheats on game start.")); + dialog->registerWidgetHelp( + m_ui.perGameSettings, tr("Enable Per-Game Settings"), tr("Checked"), + tr("When enabled, per-game settings will be applied, and incompatible enhancements will be disabled. You should " + "leave this option enabled except when testing enhancements with incompatible games.")); + + updateOptimalFramePacing(); +} + +EmulationSettingsWidget::~EmulationSettingsWidget() = default; + +void EmulationSettingsWidget::onNormalSpeedIndexChanged(int index) +{ + bool okay; + const float value = m_ui.normalSpeed->currentData().toFloat(&okay); + QtHost::SetBaseFloatSettingValue("Framerate", "NominalScalar", okay ? value : 1.0f); + g_emu_thread->applySettings(); +} + +void EmulationSettingsWidget::onFastForwardSpeedIndexChanged(int index) +{ + bool okay; + const float value = m_ui.fastForwardSpeed->currentData().toFloat(&okay); + QtHost::SetBaseFloatSettingValue("Framerate", "TurboScalar", okay ? value : 1.0f); + g_emu_thread->applySettings(); +} + +void EmulationSettingsWidget::onSlowMotionSpeedIndexChanged(int index) +{ + bool okay; + const float value = m_ui.slowMotionSpeed->currentData().toFloat(&okay); + QtHost::SetBaseFloatSettingValue("Framerate", "SlomoScalar", okay ? value : 1.0f); + g_emu_thread->applySettings(); +} + +void EmulationSettingsWidget::onOptimalFramePacingChanged(bool checked) +{ + const QSignalBlocker sb(m_ui.maxFrameLatency); + m_ui.maxFrameLatency->setValue(DEFAULT_FRAME_LATENCY); + m_ui.maxFrameLatency->setEnabled(!checked); + + QtHost::SetBaseIntSettingValue("EmuCore/GS", "VsyncQueueSize", checked ? 0 : DEFAULT_FRAME_LATENCY); + g_emu_thread->applySettings(); +} + +void EmulationSettingsWidget::updateOptimalFramePacing() +{ + const int value = QtHost::GetBaseIntSettingValue("EmuCore/GS", "VsyncQueueSize", DEFAULT_FRAME_LATENCY); + const bool optimal = (value == 0); + + const QSignalBlocker sb(m_ui.optimalFramePacing); + m_ui.optimalFramePacing->setChecked(optimal); + + const QSignalBlocker sb2(m_ui.maxFrameLatency); + m_ui.maxFrameLatency->setEnabled(!optimal); + m_ui.maxFrameLatency->setValue(optimal ? DEFAULT_FRAME_LATENCY : value); +} diff --git a/pcsx2-qt/Settings/EmulationSettingsWidget.h b/pcsx2-qt/Settings/EmulationSettingsWidget.h new file mode 100644 index 000000000..c6b2c05ba --- /dev/null +++ b/pcsx2-qt/Settings/EmulationSettingsWidget.h @@ -0,0 +1,42 @@ +/* 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 . + */ + +#pragma once + +#include + +#include "ui_EmulationSettingsWidget.h" + +class SettingsDialog; + +class EmulationSettingsWidget : public QWidget +{ + Q_OBJECT + +public: + EmulationSettingsWidget(QWidget* parent, SettingsDialog* dialog); + ~EmulationSettingsWidget(); + +private Q_SLOTS: + void onNormalSpeedIndexChanged(int index); + void onFastForwardSpeedIndexChanged(int index); + void onSlowMotionSpeedIndexChanged(int index); + void onOptimalFramePacingChanged(bool checked); + +private: + void updateOptimalFramePacing(); + + Ui::EmulationSettingsWidget m_ui; +}; diff --git a/pcsx2-qt/Settings/EmulationSettingsWidget.ui b/pcsx2-qt/Settings/EmulationSettingsWidget.ui new file mode 100644 index 000000000..ae530145e --- /dev/null +++ b/pcsx2-qt/Settings/EmulationSettingsWidget.ui @@ -0,0 +1,178 @@ + + + EmulationSettingsWidget + + + + 0 + 0 + 672 + 518 + + + + Form + + + + 0 + + + 0 + + + 0 + + + 0 + + + + + Speed Control + + + + + + + + + + + + + + + Normal Speed: + + + + + + + Slow Motion Speed: + + + + + + + Fast Forward Speed: + + + + + + + Enable Speed Limiter + + + + + + + + + + Frame Pacing/Latency Control + + + + + + frames + + + 0 + + + 5 + + + + + + + Maximum Frame Latency: + + + + + + + + + Optimal Frame Pacing + + + + + + + Adjust To Host Refresh Rate + + + + + + + + + + + + Game Settings + + + + + + Enable Per-Game Settings + + + + + + + Enable Cheats + + + + + + + Enable Widescreen Patches + + + + + + + Enable Host Filesystem + + + + + + + + + + Qt::Vertical + + + + 20 + 40 + + + + + + + + + + + diff --git a/pcsx2-qt/Settings/GameFixSettingsWidget.cpp b/pcsx2-qt/Settings/GameFixSettingsWidget.cpp new file mode 100644 index 000000000..01531a471 --- /dev/null +++ b/pcsx2-qt/Settings/GameFixSettingsWidget.cpp @@ -0,0 +1,50 @@ +/* PCSX2 - PS2 Emulator for PCs + * Copyright (C) 2002-2022 PCSX2 Dev Team + * + * PCSX2 is free software: you can redistribute it and/or modify it under the terms + * of the GNU Lesser General Public License as published by the Free Software Found- + * ation, either version 3 of the License, or (at your option) any later version. + * + * PCSX2 is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; + * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR + * PURPOSE. See the GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License along with PCSX2. + * If not, see . + */ + +#include "PrecompiledHeader.h" + +#include +#include + +#include "GameFixSettingsWidget.h" +#include "EmuThread.h" +#include "QtUtils.h" +#include "SettingWidgetBinder.h" +#include "SettingsDialog.h" + +GameFixSettingsWidget::GameFixSettingsWidget(QWidget* parent, SettingsDialog* dialog) + : QWidget(parent) +{ + m_ui.setupUi(this); + + SettingWidgetBinder::BindWidgetToBoolSetting(m_ui.FpuMulHack, "EmuCore/Gamefixes", "FpuMulHack", false); + SettingWidgetBinder::BindWidgetToBoolSetting(m_ui.FpuNegDivHack, "EmuCore/Gamefixes", "FpuNegDivHack", false); + SettingWidgetBinder::BindWidgetToBoolSetting(m_ui.GoemonTlbHack, "EmuCore/Gamefixes", "GoemonTlbHack", false); + SettingWidgetBinder::BindWidgetToBoolSetting(m_ui.SoftwareRendererFMVHack, "EmuCore/Gamefixes", "SoftwareRendererFMVHack", false); + SettingWidgetBinder::BindWidgetToBoolSetting(m_ui.SkipMPEGHack, "EmuCore/Gamefixes", "SkipMPEGHack", false); + SettingWidgetBinder::BindWidgetToBoolSetting(m_ui.OPHFlagHack, "EmuCore/Gamefixes", "OPHFlagHack", false); + SettingWidgetBinder::BindWidgetToBoolSetting(m_ui.EETimingHack, "EmuCore/Gamefixes", "EETimingHack", false); + SettingWidgetBinder::BindWidgetToBoolSetting(m_ui.DMABusyHack, "EmuCore/Gamefixes", "DMABusyHack", false); + SettingWidgetBinder::BindWidgetToBoolSetting(m_ui.GIFFIFOHack, "EmuCore/Gamefixes", "GIFFIFOHack", false); + SettingWidgetBinder::BindWidgetToBoolSetting(m_ui.VIFFIFOHack, "EmuCore/Gamefixes", "VIFFIFOHack", false); + SettingWidgetBinder::BindWidgetToBoolSetting(m_ui.VIF1StallHack, "EmuCore/Gamefixes", "VIF1StallHack", false); + SettingWidgetBinder::BindWidgetToBoolSetting(m_ui.VuAddSubHack, "EmuCore/Gamefixes", "VuAddSubHack", false); + SettingWidgetBinder::BindWidgetToBoolSetting(m_ui.IbitHack, "EmuCore/Gamefixes", "IbitHack", false); + SettingWidgetBinder::BindWidgetToBoolSetting(m_ui.VUKickstartHack, "EmuCore/Gamefixes", "VUKickstartHack", false); + SettingWidgetBinder::BindWidgetToBoolSetting(m_ui.VUOverflowHack, "EmuCore/Gamefixes", "VUOverflowHack", false); + SettingWidgetBinder::BindWidgetToBoolSetting(m_ui.XgKickHack, "EmuCore/Gamefixes", "XgKickHack", false); +} + +GameFixSettingsWidget::~GameFixSettingsWidget() = default; diff --git a/pcsx2-qt/Settings/GameFixSettingsWidget.h b/pcsx2-qt/Settings/GameFixSettingsWidget.h new file mode 100644 index 000000000..c1bdf6aac --- /dev/null +++ b/pcsx2-qt/Settings/GameFixSettingsWidget.h @@ -0,0 +1,34 @@ +/* PCSX2 - PS2 Emulator for PCs + * Copyright (C) 2002-2022 PCSX2 Dev Team + * + * PCSX2 is free software: you can redistribute it and/or modify it under the terms + * of the GNU Lesser General Public License as published by the Free Software Found- + * ation, either version 3 of the License, or (at your option) any later version. + * + * PCSX2 is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; + * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR + * PURPOSE. See the GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License along with PCSX2. + * If not, see . + */ + +#pragma once + +#include + +#include "ui_GameFixSettingsWidget.h" + +class SettingsDialog; + +class GameFixSettingsWidget : public QWidget +{ + Q_OBJECT + +public: + GameFixSettingsWidget(QWidget* parent, SettingsDialog* dialog); + ~GameFixSettingsWidget(); + +private: + Ui::GameFixSettingsWidget m_ui; +}; diff --git a/pcsx2-qt/Settings/GameFixSettingsWidget.ui b/pcsx2-qt/Settings/GameFixSettingsWidget.ui new file mode 100644 index 000000000..eb85de7e2 --- /dev/null +++ b/pcsx2-qt/Settings/GameFixSettingsWidget.ui @@ -0,0 +1,166 @@ + + + GameFixSettingsWidget + + + + 0 + 0 + 648 + 481 + + + + Form + + + + 0 + + + 0 + + + 0 + + + + + Game Fixes (NOT recommended to change globally) + + + + + + FPU Negative Divide Hack (For Gundam Games) + + + + + + + FPU Multiply Hack (For Tales of Destiny) + + + + + + + Use Software Renderer For FMVs + + + + + + + Skip MPEG Hack (Skips Videos/FMVs) + + + + + + + Preload TLB Hack (For Goemon) + + + + + + + EE Timing Hack (General Purpose Timing Hack) + + + + + + + OPH Flag Hack (For Bleach Blade Battlers) + + + + + + + Emulate GIF FIFO (Correct But Slower) + + + + + + + DMA Busy Hack (Deny Writes When Busy) + + + + + + + Delay VIF1 Stalls (For SOCOM 2 HUD/Spy Hunter) + + + + + + + Emulate VIF FIFO (Correct But Slower) + + + + + + + VU I Bit Hack (For Scarface The World is Yours/Crash Tag Team Racing) + + + + + + + VU Add Hack (For Tri-Ace Games) + + + + + + + VU Overflow Hack (Superman Returns) + + + + + + + VU Kickstart Hack (Run Ahead) + + + + + + + VU XGKick Sync (Correct But Slower) + + + + + + + + + + Qt::Vertical + + + + 20 + 40 + + + + + + + + + + + diff --git a/pcsx2-qt/Settings/GameListSettingsWidget.cpp b/pcsx2-qt/Settings/GameListSettingsWidget.cpp new file mode 100644 index 000000000..eb5ff4239 --- /dev/null +++ b/pcsx2-qt/Settings/GameListSettingsWidget.cpp @@ -0,0 +1,244 @@ +/* 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 +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include "GameListSettingsWidget.h" +#include "MainWindow.h" +#include "QtHost.h" +#include "QtUtils.h" + +GameListSettingsWidget::GameListSettingsWidget(QWidget* parent, SettingsDialog* dialog) + : QWidget(parent) +{ + m_ui.setupUi(this); + + m_ui.searchDirectoryList->setSelectionMode(QAbstractItemView::SingleSelection); + m_ui.searchDirectoryList->setSelectionBehavior(QAbstractItemView::SelectRows); + m_ui.searchDirectoryList->setAlternatingRowColors(true); + m_ui.searchDirectoryList->setShowGrid(false); + m_ui.searchDirectoryList->horizontalHeader()->setHighlightSections(false); + m_ui.searchDirectoryList->verticalHeader()->hide(); + m_ui.searchDirectoryList->setCurrentIndex({}); + m_ui.searchDirectoryList->setContextMenuPolicy(Qt::ContextMenuPolicy::CustomContextMenu); + + connect(m_ui.searchDirectoryList, &QTableWidget::customContextMenuRequested, this, + &GameListSettingsWidget::onDirectoryListContextMenuRequested); + connect(m_ui.addSearchDirectoryButton, &QPushButton::clicked, this, + &GameListSettingsWidget::onAddSearchDirectoryButtonClicked); + connect(m_ui.removeSearchDirectoryButton, &QPushButton::clicked, this, + &GameListSettingsWidget::onRemoveSearchDirectoryButtonClicked); + connect(m_ui.addExcludedPath, &QPushButton::clicked, this, &GameListSettingsWidget::onAddExcludedPathButtonClicked); + connect(m_ui.removeExcludedPath, &QPushButton::clicked, this, + &GameListSettingsWidget::onRemoveExcludedPathButtonClicked); + connect(m_ui.rescanAllGames, &QPushButton::clicked, this, &GameListSettingsWidget::onRescanAllGamesClicked); + connect(m_ui.scanForNewGames, &QPushButton::clicked, this, &GameListSettingsWidget::onScanForNewGamesClicked); + + refreshDirectoryList(); + refreshExclusionList(); +} + +GameListSettingsWidget::~GameListSettingsWidget() = default; + +bool GameListSettingsWidget::addExcludedPath(const std::string& path) +{ + if (!QtHost::AddBaseValueToStringList("GameList", "ExcludedPaths", path.c_str())) + return false; + + m_ui.excludedPaths->addItem(QString::fromStdString(path)); + g_main_window->refreshGameList(false); + return true; +} + +void GameListSettingsWidget::refreshExclusionList() +{ + m_ui.excludedPaths->clear(); + + const std::vector paths(QtHost::GetBaseStringListSetting("GameList", "ExcludedPaths")); + for (const std::string& path : paths) + m_ui.excludedPaths->addItem(QString::fromStdString(path)); +} + +void GameListSettingsWidget::resizeEvent(QResizeEvent* event) +{ + QWidget::resizeEvent(event); + + QtUtils::ResizeColumnsForTableView(m_ui.searchDirectoryList, {-1, 100}); +} + +void GameListSettingsWidget::addPathToTable(const std::string& path, bool recursive) +{ + const int row = m_ui.searchDirectoryList->rowCount(); + m_ui.searchDirectoryList->insertRow(row); + + QTableWidgetItem* item = new QTableWidgetItem(); + item->setText(QString::fromStdString(path)); + m_ui.searchDirectoryList->setItem(row, 0, item); + + QCheckBox* cb = new QCheckBox(m_ui.searchDirectoryList); + m_ui.searchDirectoryList->setCellWidget(row, 1, cb); + cb->setChecked(recursive); + + connect(cb, &QCheckBox::stateChanged, [this, item](int state) { + const std::string path(item->text().toStdString()); + if (state == Qt::Checked) + { + QtHost::RemoveBaseValueFromStringList("GameList", "Paths", path.c_str()); + QtHost::AddBaseValueToStringList("GameList", "RecursivePaths", path.c_str()); + } + else + { + QtHost::RemoveBaseValueFromStringList("GameList", "RecursivePaths", path.c_str()); + QtHost::AddBaseValueToStringList("GameList", "Paths", path.c_str()); + } + }); +} + +void GameListSettingsWidget::refreshDirectoryList() +{ + QSignalBlocker sb(m_ui.searchDirectoryList); + while (m_ui.searchDirectoryList->rowCount() > 0) + m_ui.searchDirectoryList->removeRow(0); + + std::vector path_list = QtHost::GetBaseStringListSetting("GameList", "Paths"); + for (const std::string& entry : path_list) + addPathToTable(entry, false); + + path_list = QtHost::GetBaseStringListSetting("GameList", "RecursivePaths"); + for (const std::string& entry : path_list) + addPathToTable(entry, true); + + m_ui.searchDirectoryList->sortByColumn(0, Qt::AscendingOrder); +} + +void GameListSettingsWidget::addSearchDirectory(const QString& path, bool recursive) +{ + const std::string spath(path.toStdString()); + QtHost::RemoveBaseValueFromStringList("GameList", recursive ? "Paths" : "RecursivePaths", spath.c_str()); + QtHost::AddBaseValueToStringList("GameList", recursive ? "RecursivePaths" : "Paths", spath.c_str()); + refreshDirectoryList(); + g_main_window->refreshGameList(false); +} + +void GameListSettingsWidget::removeSearchDirectory(const QString& path) +{ + const std::string spath(path.toStdString()); + if (!QtHost::RemoveBaseValueFromStringList("GameList", "Paths", spath.c_str()) && + !QtHost::RemoveBaseValueFromStringList("GameList", "RecursivePaths", spath.c_str())) + { + return; + } + + refreshDirectoryList(); + g_main_window->refreshGameList(false); +} + +void GameListSettingsWidget::onDirectoryListContextMenuRequested(const QPoint& point) +{ + QModelIndexList selection = m_ui.searchDirectoryList->selectionModel()->selectedIndexes(); + if (selection.size() < 1) + return; + + const int row = selection[0].row(); + + QMenu menu; + menu.addAction(tr("Remove"), [this]() { onRemoveSearchDirectoryButtonClicked(); }); + menu.addSeparator(); + menu.addAction(tr("Open Directory..."), [this, row]() { + QtUtils::OpenURL(this, QUrl::fromLocalFile(m_ui.searchDirectoryList->item(row, 0)->text())); + }); + menu.exec(m_ui.searchDirectoryList->mapToGlobal(point)); +} + +void GameListSettingsWidget::addSearchDirectory(QWidget* parent_widget) +{ + QString dir = + QDir::toNativeSeparators(QFileDialog::getExistingDirectory(parent_widget, tr("Select Search Directory"))); + + if (dir.isEmpty()) + return; + + QMessageBox::StandardButton selection = + QMessageBox::question(this, tr("Scan Recursively?"), + tr("Would you like to scan the directory \"%1\" recursively?\n\nScanning recursively takes " + "more time, but will identify files in subdirectories.") + .arg(dir), + QMessageBox::Yes | QMessageBox::No | QMessageBox::Cancel); + if (selection == QMessageBox::Cancel) + return; + + const bool recursive = (selection == QMessageBox::Yes); + addSearchDirectory(dir, recursive); +} + +void GameListSettingsWidget::onAddSearchDirectoryButtonClicked() +{ + addSearchDirectory(this); +} + +void GameListSettingsWidget::onRemoveSearchDirectoryButtonClicked() +{ + const int row = m_ui.searchDirectoryList->currentRow(); + QTableWidgetItem* item = (row >= 0) ? m_ui.searchDirectoryList->takeItem(row, 0) : nullptr; + if (!item) + return; + + removeSearchDirectory(item->text()); + delete item; +} + +void GameListSettingsWidget::onAddExcludedPathButtonClicked() +{ + QString path = + QDir::toNativeSeparators(QFileDialog::getOpenFileName(QtUtils::GetRootWidget(this), tr("Select Path"))); + if (path.isEmpty()) + return; + + addExcludedPath(path.toStdString()); +} + +void GameListSettingsWidget::onRemoveExcludedPathButtonClicked() +{ + const int row = m_ui.excludedPaths->currentRow(); + QListWidgetItem* item = (row >= 0) ? m_ui.excludedPaths->takeItem(row) : 0; + if (!item) + return; + + QtHost::RemoveBaseValueFromStringList("GameList", "ExcludedPaths", item->text().toUtf8().constData()); + delete item; + + g_main_window->refreshGameList(false); +} + +void GameListSettingsWidget::onRescanAllGamesClicked() +{ + g_main_window->refreshGameList(true); +} + +void GameListSettingsWidget::onScanForNewGamesClicked() +{ + g_main_window->refreshGameList(false); +} diff --git a/pcsx2-qt/Settings/GameListSettingsWidget.h b/pcsx2-qt/Settings/GameListSettingsWidget.h new file mode 100644 index 000000000..4a8bdc23e --- /dev/null +++ b/pcsx2-qt/Settings/GameListSettingsWidget.h @@ -0,0 +1,59 @@ +/* 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 . + */ + +#pragma once +#include +#include + +#include "ui_GameListSettingsWidget.h" + +class SettingsDialog; + +class GameListSearchDirectoriesModel; + +class GameListSettingsWidget : public QWidget +{ + Q_OBJECT + +public: + GameListSettingsWidget(QWidget* parent, SettingsDialog* dialog); + ~GameListSettingsWidget(); + + bool addExcludedPath(const std::string& path); + void refreshExclusionList(); + +public Q_SLOTS: + void addSearchDirectory(QWidget* parent_widget); + +private Q_SLOTS: + void onDirectoryListContextMenuRequested(const QPoint& point); + void onAddSearchDirectoryButtonClicked(); + void onRemoveSearchDirectoryButtonClicked(); + void onAddExcludedPathButtonClicked(); + void onRemoveExcludedPathButtonClicked(); + void onScanForNewGamesClicked(); + void onRescanAllGamesClicked(); + +protected: + void resizeEvent(QResizeEvent* event); + +private: + void addPathToTable(const std::string& path, bool recursive); + void refreshDirectoryList(); + void addSearchDirectory(const QString& path, bool recursive); + void removeSearchDirectory(const QString& path); + + Ui::GameListSettingsWidget m_ui; +}; diff --git a/pcsx2-qt/Settings/GameListSettingsWidget.ui b/pcsx2-qt/Settings/GameListSettingsWidget.ui new file mode 100644 index 000000000..6b61d8133 --- /dev/null +++ b/pcsx2-qt/Settings/GameListSettingsWidget.ui @@ -0,0 +1,213 @@ + + + GameListSettingsWidget + + + + 0 + 0 + 532 + 376 + + + + Form + + + + 0 + + + 0 + + + 0 + + + 0 + + + + + + + Search Directories (will be scanned for games) + + + + + + + Qt::Horizontal + + + + 40 + 20 + + + + + + + + + 0 + 0 + + + + Add + + + + + + + + + + + 0 + 0 + + + + Remove + + + + + + + + + + + + + Search Directory + + + + + Scan Recursively + + + + + + + + + + Excluded Paths (will not be scanned) + + + + + + + Qt::Horizontal + + + + 40 + 20 + + + + + + + + + 0 + 0 + + + + Add + + + + + + + + + + + 0 + 0 + + + + Remove + + + + + + + + + + + + + + + + + Qt::Horizontal + + + + 40 + 20 + + + + + + + + + 0 + 0 + + + + Scan For New Games + + + + + + + + + + + 0 + 0 + + + + Rescan All Games + + + + + + + + + + + + + + + diff --git a/pcsx2-qt/Settings/GraphicsSettingsWidget.cpp b/pcsx2-qt/Settings/GraphicsSettingsWidget.cpp new file mode 100644 index 000000000..e70507c5d --- /dev/null +++ b/pcsx2-qt/Settings/GraphicsSettingsWidget.cpp @@ -0,0 +1,381 @@ +/* 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 "GraphicsSettingsWidget.h" +#include "QtUtils.h" +#include "SettingWidgetBinder.h" +#include "SettingsDialog.h" +#include + +#include "pcsx2/GS/GS.h" +#include "pcsx2/GS/GSUtil.h" + +#include "Frontend/VulkanHostDisplay.h" + +#ifdef _WIN32 +#include "Frontend/D3D11HostDisplay.h" +#endif + +struct RendererInfo +{ + const char* name; + GSRendererType type; +}; + +static constexpr RendererInfo s_renderer_info[] = { + QT_TRANSLATE_NOOP("GraphicsSettingsWidget", "Automatic"), + GSRendererType::Auto, +#ifdef _WIN32 + QT_TRANSLATE_NOOP("GraphicsSettingsWidget", "Direct3D 11"), + GSRendererType::DX11, +#endif + QT_TRANSLATE_NOOP("GraphicsSettingsWidget", "OpenGL"), + GSRendererType::OGL, + QT_TRANSLATE_NOOP("GraphicsSettingsWidget", "Vulkan"), + GSRendererType::VK, + QT_TRANSLATE_NOOP("GraphicsSettingsWidget", "Software"), + GSRendererType::SW, + QT_TRANSLATE_NOOP("GraphicsSettingsWidget", "Null"), + GSRendererType::Null, +}; + +static const char* s_anisotropic_filtering_entries[] = {QT_TRANSLATE_NOOP("GraphicsSettingsWidget", "Off (Default)"), + QT_TRANSLATE_NOOP("GraphicsSettingsWidget", "2x"), + QT_TRANSLATE_NOOP("GraphicsSettingsWidget", "4x"), + QT_TRANSLATE_NOOP("GraphicsSettingsWidget", "8x"), + QT_TRANSLATE_NOOP("GraphicsSettingsWidget", "16x"), + nullptr}; +static const char* s_anisotropic_filtering_values[] = {"1", "2", "4", "8", "16", nullptr}; + +static constexpr int DEFAULT_INTERLACE_MODE = 7; + +GraphicsSettingsWidget::GraphicsSettingsWidget(QWidget* parent, SettingsDialog* dialog) + : QWidget(parent) +{ + m_ui.setupUi(this); + + ////////////////////////////////////////////////////////////////////////// + // Global Settings + ////////////////////////////////////////////////////////////////////////// + SettingWidgetBinder::BindWidgetToStringSetting(m_ui.adapter, "EmuCore/GS", "Adapter"); + SettingWidgetBinder::BindWidgetToIntSetting(m_ui.vsync, "EmuCore/GS", "VsyncEnable", 0); + SettingWidgetBinder::BindWidgetToBoolSetting(m_ui.enableHWFixes, "EmuCore/GS", "UserHacks", false); + + ////////////////////////////////////////////////////////////////////////// + // Game Display Settings + ////////////////////////////////////////////////////////////////////////// + SettingWidgetBinder::BindWidgetToEnumSetting(m_ui.aspectRatio, "EmuCore/GS", "AspectRatio", + Pcsx2Config::GSOptions::AspectRatioNames, AspectRatioType::R4_3); + SettingWidgetBinder::BindWidgetToEnumSetting(m_ui.fmvAspectRatio, "EmuCore/GS", "FMVAspectRatioSwitch", + Pcsx2Config::GSOptions::FMVAspectRatioSwitchNames, + FMVAspectRatioSwitchType::Off); + SettingWidgetBinder::BindWidgetToIntSetting(m_ui.interlacing, "EmuCore/GS", "interlace", DEFAULT_INTERLACE_MODE); + SettingWidgetBinder::BindWidgetToBoolSetting(m_ui.bilinearFiltering, "EmuCore/GS", "LinearPresent", true); + SettingWidgetBinder::BindWidgetToBoolSetting(m_ui.integerScaling, "EmuCore/GS", "IntegerScaling", false); + SettingWidgetBinder::BindWidgetToBoolSetting(m_ui.internalResolutionScreenshots, "EmuCore/GS", + "InternalResolutionScreenshots", false); + SettingWidgetBinder::BindWidgetToFloatSetting(m_ui.zoom, "EmuCore/GS", "Zoom", 100.0f); + SettingWidgetBinder::BindWidgetToFloatSetting(m_ui.stretchY, "EmuCore/GS", "StretchY", 100.0f); + SettingWidgetBinder::BindWidgetToFloatSetting(m_ui.offsetX, "EmuCore/GS", "OffsetX", 0.0f); + SettingWidgetBinder::BindWidgetToFloatSetting(m_ui.offsetY, "EmuCore/GS", "OffsetY", 0.0f); + + connect(m_ui.integerScaling, &QCheckBox::stateChanged, this, &GraphicsSettingsWidget::onIntegerScalingChanged); + onIntegerScalingChanged(); + + connect(m_ui.fullscreenModes, QOverload::of(&QComboBox::currentIndexChanged), this, + &GraphicsSettingsWidget::onFullscreenModeChanged); + + ////////////////////////////////////////////////////////////////////////// + // OSD Settings + ////////////////////////////////////////////////////////////////////////// + SettingWidgetBinder::BindWidgetToFloatSetting(m_ui.osdScale, "EmuCore/GS", "OsdScale", 100.0f); + SettingWidgetBinder::BindWidgetToBoolSetting(m_ui.osdShowMessages, "EmuCore/GS", "OsdShowMessages", true); + SettingWidgetBinder::BindWidgetToBoolSetting(m_ui.osdShowSpeed, "EmuCore/GS", "OsdShowSpeed", false); + SettingWidgetBinder::BindWidgetToBoolSetting(m_ui.osdShowFPS, "EmuCore/GS", "OsdShowFPS", false); + SettingWidgetBinder::BindWidgetToBoolSetting(m_ui.osdShowCPU, "EmuCore/GS", "OsdShowCPU", false); + SettingWidgetBinder::BindWidgetToBoolSetting(m_ui.osdShowResolution, "EmuCore/GS", "OsdShowResolution", false); + SettingWidgetBinder::BindWidgetToBoolSetting(m_ui.osdShowGSStats, "EmuCore/GS", "OsdShowGSStats", false); + + dialog->registerWidgetHelp(m_ui.osdShowMessages, tr("Show OSD Messages"), tr("Checked"), + tr("Shows on-screen-display messages when events occur such as save states being " + "created/loaded, screenshots being taken, etc.")); + dialog->registerWidgetHelp(m_ui.osdShowFPS, tr("Show Game Frame Rate"), tr("Unchecked"), + tr("Shows the internal frame rate of the game in the top-right corner of the display.")); + dialog->registerWidgetHelp( + m_ui.osdShowSpeed, tr("Show Emulation Speed"), tr("Unchecked"), + tr("Shows the current emulation speed of the system in the top-right corner of the display as a percentage.")); + dialog->registerWidgetHelp(m_ui.osdShowResolution, tr("Show Resolution"), tr("Unchecked"), + tr("Shows the resolution of the game in the top-right corner of the display.")); + + ////////////////////////////////////////////////////////////////////////// + // HW Settings + ////////////////////////////////////////////////////////////////////////// + SettingWidgetBinder::BindWidgetToIntSetting(m_ui.upscaleMultiplier, "EmuCore/GS", "upscale_multiplier", 1); + SettingWidgetBinder::BindWidgetToIntSetting(m_ui.textureFiltering, "EmuCore/GS", "filter", + static_cast(BiFiltering::PS2)); + SettingWidgetBinder::BindWidgetToIntSetting(m_ui.trilinearFiltering, "EmuCore/GS", "UserHacks_TriFilter", + static_cast(TriFiltering::Off)); + SettingWidgetBinder::BindWidgetToEnumSetting(m_ui.anisotropicFiltering, "EmuCore/GS", "MaxAnisotropy", + s_anisotropic_filtering_entries, s_anisotropic_filtering_values, "1"); + SettingWidgetBinder::BindWidgetToIntSetting(m_ui.dithering, "EmuCore/GS", "dithering_ps2", 2); + SettingWidgetBinder::BindWidgetToIntSetting(m_ui.mipmapping, "EmuCore/GS", "mipmap_hw", + static_cast(HWMipmapLevel::Automatic), -1); + SettingWidgetBinder::BindWidgetToIntSetting(m_ui.crcFixLevel, "EmuCore/GS", "crc_hack_level", + static_cast(CRCHackLevel::Automatic), -1); + SettingWidgetBinder::BindWidgetToIntSetting(m_ui.blending, "EmuCore/GS", "accurate_blending_unit", + static_cast(AccBlendLevel::Basic)); + SettingWidgetBinder::BindWidgetToBoolSetting(m_ui.accurateDATE, "EmuCore/GS", "accurate_date", true); + SettingWidgetBinder::BindWidgetToBoolSetting(m_ui.conservativeBufferAllocation, "EmuCore/GS", + "conservative_framebuffer", true); + SettingWidgetBinder::BindWidgetToBoolSetting(m_ui.gpuPaletteConversion, "EmuCore/GS", "paltex", false); + SettingWidgetBinder::BindWidgetToBoolSetting(m_ui.preloadTexture, "EmuCore/GS", "preload_texture", false); + + ////////////////////////////////////////////////////////////////////////// + // HW Renderer Fixes + ////////////////////////////////////////////////////////////////////////// + SettingWidgetBinder::BindWidgetToIntSetting(m_ui.halfScreenFix, "EmuCore/GS", "UserHacks_Half_Bottom_Override", -1, + -1); + SettingWidgetBinder::BindWidgetToIntSetting(m_ui.skipDrawRangeStart, "EmuCore/GS", "UserHacks_SkipDraw", 0); + SettingWidgetBinder::BindWidgetToIntSetting(m_ui.skipDrawRangeCount, "EmuCore/GS", "UserHacks_SkipDraw_Offset", 0); + SettingWidgetBinder::BindWidgetToBoolSetting(m_ui.hwAutoFlush, "EmuCore/GS", "UserHacks_AutoFlush", false); + SettingWidgetBinder::BindWidgetToBoolSetting(m_ui.frameBufferConversion, "EmuCore/GS", "UserHacks_CPU_FB_Conversion", + false); + SettingWidgetBinder::BindWidgetToBoolSetting(m_ui.disableDepthEmulation, "EmuCore/GS", + "UserHacks_DisableDepthSupport", false); + SettingWidgetBinder::BindWidgetToBoolSetting(m_ui.memoryWrapping, "EmuCore/GS", "wrap_gs_mem", false); + SettingWidgetBinder::BindWidgetToBoolSetting(m_ui.disableSafeFeatures, "EmuCore/GS", + "UserHacks_Disable_Safe_Features", false); + SettingWidgetBinder::BindWidgetToBoolSetting(m_ui.preloadFrameData, "EmuCore/GS", "preload_frame_with_gs_data", + false); + SettingWidgetBinder::BindWidgetToBoolSetting(m_ui.fastTextureInvalidation, "EmuCore/GS", + "UserHacks_DisablePartialInvalidation", false); + + ////////////////////////////////////////////////////////////////////////// + // HW Upscaling Fixes + ////////////////////////////////////////////////////////////////////////// + SettingWidgetBinder::BindWidgetToIntSetting(m_ui.halfPixelOffset, "EmuCore/GS", "UserHacks_HalfPixelOffset", 0); + SettingWidgetBinder::BindWidgetToIntSetting(m_ui.roundSprite, "EmuCore/GS", "UserHacks_RoundSprite", 0); + SettingWidgetBinder::BindWidgetToIntSetting(m_ui.textureOffsetX, "EmuCore/GS", "UserHacks_TCOffsetX", 0); + SettingWidgetBinder::BindWidgetToIntSetting(m_ui.textureOffsetY, "EmuCore/GS", "UserHacks_TCOffsetY", 0); + SettingWidgetBinder::BindWidgetToBoolSetting(m_ui.alignSprite, "EmuCore/GS", "UserHacks_align_sprite_X", false); + SettingWidgetBinder::BindWidgetToBoolSetting(m_ui.mergeSprite, "EmuCore/GS", "UserHacks_merge_pp_sprite", false); + SettingWidgetBinder::BindWidgetToBoolSetting(m_ui.wildHack, "EmuCore/GS", "UserHacks_WildHack", false); + + ////////////////////////////////////////////////////////////////////////// + // Advanced Settings + ////////////////////////////////////////////////////////////////////////// + SettingWidgetBinder::BindWidgetToBoolSetting(m_ui.useBlitSwapChain, "EmuCore/GS", "UseBlitSwapChain", false); + SettingWidgetBinder::BindWidgetToBoolSetting(m_ui.useDebugDevice, "EmuCore/GS", "UseDebugDevice", false); + + ////////////////////////////////////////////////////////////////////////// + // SW Settings + ////////////////////////////////////////////////////////////////////////// + SettingWidgetBinder::BindWidgetToIntSetting(m_ui.extraSWThreads, "EmuCore/GS", "extrathreads", 2); + SettingWidgetBinder::BindWidgetToBoolSetting(m_ui.swAutoFlush, "EmuCore/GS", "autoflush_sw", true); + SettingWidgetBinder::BindWidgetToBoolSetting(m_ui.swAA1, "EmuCore/GS", "aa1", true); + SettingWidgetBinder::BindWidgetToBoolSetting(m_ui.swMipmap, "EmuCore/GS", "mipmap", true); + + ////////////////////////////////////////////////////////////////////////// + // Non-trivial settings + ////////////////////////////////////////////////////////////////////////// + const GSRendererType current_renderer = static_cast( + QtHost::GetBaseIntSettingValue("EmuCore/GS", "Renderer", static_cast(GSRendererType::Auto))); + for (const RendererInfo& ri : s_renderer_info) + { + m_ui.renderer->addItem(qApp->translate("GraphicsSettingsWidget", ri.name)); + if (ri.type == current_renderer) + m_ui.renderer->setCurrentIndex(m_ui.renderer->count() - 1); + } + connect(m_ui.renderer, QOverload::of(&QComboBox::currentIndexChanged), this, + &GraphicsSettingsWidget::onRendererChanged); + connect(m_ui.enableHWFixes, &QCheckBox::stateChanged, this, &GraphicsSettingsWidget::onEnableHardwareFixesChanged); + updateRendererDependentOptions(); + + dialog->registerWidgetHelp(m_ui.useBlitSwapChain, tr("Use Blit Swap Chain"), tr("Unchecked"), + tr("Uses a blit presentation model instead of flipping when using the Direct3D 11 " + "renderer. This usually results in slower performance, but may be required for some " + "streaming applications, or to uncap framerates on some systems.")); +} + +GraphicsSettingsWidget::~GraphicsSettingsWidget() = default; + +void GraphicsSettingsWidget::onRendererChanged(int index) +{ + QtHost::SetBaseIntSettingValue("EmuCore/GS", "Renderer", static_cast(s_renderer_info[index].type)); + g_emu_thread->applySettings(); + updateRendererDependentOptions(); +} + +void GraphicsSettingsWidget::onAdapterChanged(int index) +{ + if (index == 0) + QtHost::RemoveBaseSettingValue("EmuCore/GS", "Adapter"); + else + QtHost::SetBaseStringSettingValue("EmuCore/GS", "Adapter", m_ui.adapter->currentText().toUtf8().constData()); + g_emu_thread->applySettings(); +} + +void GraphicsSettingsWidget::onEnableHardwareFixesChanged() +{ + const bool enabled = m_ui.enableHWFixes->isChecked(); + m_ui.hardwareRendererGroup->setTabEnabled(2, enabled); + m_ui.hardwareRendererGroup->setTabEnabled(3, enabled); +} + +void GraphicsSettingsWidget::updateRendererDependentOptions() +{ + const int index = m_ui.renderer->currentIndex(); + GSRendererType type = s_renderer_info[index].type; + if (type == GSRendererType::Auto) + type = GSUtil::GetPreferredRenderer(); + +#ifdef _WIN32 + const bool is_dx11 = (type == GSRendererType::DX11 || type == GSRendererType::SW); +#else + const bool is_dx11 = false; +#endif + + const bool is_hardware = (type == GSRendererType::DX11 || type == GSRendererType::OGL || type == GSRendererType::VK); + const bool is_software = (type == GSRendererType::SW); + + // move advanced tab to the correct parent + static constexpr std::array move_tab_names = {{"Display", "On-Screen Display", "Advanced"}}; + const std::array move_tab_pointers = {{m_ui.gameDisplayTab, m_ui.osdTab, m_ui.advancedTab}}; + for (size_t i = 0; i < move_tab_pointers.size(); i++) + { + QWidget* tab = move_tab_pointers[i]; + const QString tab_label(tr(move_tab_names[i])); + if (const int index = m_ui.softwareRendererGroup->indexOf(tab); index >= 0 && is_hardware) + m_ui.softwareRendererGroup->removeTab(index); + if (const int index = m_ui.hardwareRendererGroup->indexOf(tab); index >= 0 && is_software) + m_ui.hardwareRendererGroup->removeTab(index); + if (const int index = m_ui.hardwareRendererGroup->indexOf(tab); index < 0 && is_hardware) + m_ui.hardwareRendererGroup->insertTab((i == 0) ? 0 : m_ui.hardwareRendererGroup->count(), tab, tab_label); + if (const int index = m_ui.softwareRendererGroup->indexOf(tab); index < 0 && is_software) + m_ui.softwareRendererGroup->insertTab((i == 0) ? 0 : m_ui.softwareRendererGroup->count(), tab, tab_label); + } + + if (is_hardware != is_software) + { + if (is_hardware) + m_ui.hardwareRendererGroup->setCurrentIndex(0); + else + m_ui.softwareRendererGroup->setCurrentIndex(0); + } + + if (m_hardware_renderer_visible != is_hardware) + { + m_ui.hardwareRendererGroup->setVisible(is_hardware); + if (!is_hardware) + m_ui.verticalLayout->removeWidget(m_ui.hardwareRendererGroup); + else + m_ui.verticalLayout->insertWidget(1, m_ui.hardwareRendererGroup); + + m_hardware_renderer_visible = is_hardware; + } + + if (m_software_renderer_visible != is_software) + { + m_ui.softwareRendererGroup->setVisible(is_software); + if (!is_hardware) + m_ui.verticalLayout->removeWidget(m_ui.softwareRendererGroup); + else + m_ui.verticalLayout->insertWidget(1, m_ui.softwareRendererGroup); + + m_software_renderer_visible = is_software; + } + + m_ui.useBlitSwapChain->setEnabled(is_dx11); + + // populate adapters + HostDisplay::AdapterAndModeList modes; + switch (type) + { +#ifdef _WIN32 + case GSRendererType::DX11: + modes = D3D11HostDisplay::StaticGetAdapterAndModeList(); + break; +#endif + + case GSRendererType::VK: + modes = VulkanHostDisplay::StaticGetAdapterAndModeList(nullptr); + break; + + case GSRendererType::OGL: + case GSRendererType::SW: + case GSRendererType::Null: + case GSRendererType::Auto: + default: + break; + } + + // fill+select adapters + { + const std::string current_adapter = QtHost::GetBaseStringSettingValue("EmuCore/GS", "Adapter", ""); + QSignalBlocker sb(m_ui.adapter); + m_ui.adapter->clear(); + m_ui.adapter->setEnabled(!modes.adapter_names.empty()); + m_ui.adapter->addItem(tr("(Default)")); + for (const std::string& adapter : modes.adapter_names) + { + m_ui.adapter->addItem(QString::fromStdString(adapter)); + if (current_adapter == adapter) + m_ui.adapter->setCurrentIndex(m_ui.adapter->count() - 1); + } + } + + // fill+select fullscreen modes + { + QSignalBlocker sb(m_ui.fullscreenModes); + + const std::string current_mode(QtHost::GetBaseStringSettingValue("EmuCore/GS", "FullscreenMode", "")); + m_ui.fullscreenModes->clear(); + m_ui.fullscreenModes->addItem(tr("Borderless Fullscreen")); + if (current_mode.empty()) + m_ui.fullscreenModes->setCurrentIndex(0); + + for (const std::string& fs_mode : modes.fullscreen_modes) + { + m_ui.fullscreenModes->addItem(QString::fromStdString(fs_mode)); + if (current_mode == fs_mode) + m_ui.fullscreenModes->setCurrentIndex(m_ui.fullscreenModes->count() - 1); + } + } + + m_ui.enableHWFixes->setEnabled(is_hardware); + onEnableHardwareFixesChanged(); +} + +void GraphicsSettingsWidget::onIntegerScalingChanged() +{ + m_ui.bilinearFiltering->setEnabled(!m_ui.integerScaling->isChecked()); +} + +void GraphicsSettingsWidget::onFullscreenModeChanged(int index) +{ + if (index == 0) + { + QtHost::RemoveBaseSettingValue("EmuCore/GS", "FullscreenMode"); + } + else + { + QtHost::SetBaseStringSettingValue("EmuCore/GS", "FullscreenMode", + m_ui.fullscreenModes->currentText().toUtf8().constData()); + } + + g_emu_thread->applySettings(); +} diff --git a/pcsx2-qt/Settings/GraphicsSettingsWidget.h b/pcsx2-qt/Settings/GraphicsSettingsWidget.h new file mode 100644 index 000000000..9600b3670 --- /dev/null +++ b/pcsx2-qt/Settings/GraphicsSettingsWidget.h @@ -0,0 +1,49 @@ +/* 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 . + */ + +#pragma once + +#include + +#include "ui_GraphicsSettingsWidget.h" + +class SettingsDialog; + +class GraphicsSettingsWidget : public QWidget +{ + Q_OBJECT + +public: + GraphicsSettingsWidget(QWidget* parent, SettingsDialog* dialog); + ~GraphicsSettingsWidget(); + + void updateRendererDependentOptions(); + +Q_SIGNALS: + void fullscreenModesChanged(const QStringList& modes); + +private Q_SLOTS: + void onRendererChanged(int index); + void onAdapterChanged(int index); + void onEnableHardwareFixesChanged(); + void onIntegerScalingChanged(); + void onFullscreenModeChanged(int index); + +private: + Ui::GraphicsSettingsWidget m_ui; + + bool m_hardware_renderer_visible = true; + bool m_software_renderer_visible = true; +}; diff --git a/pcsx2-qt/Settings/GraphicsSettingsWidget.ui b/pcsx2-qt/Settings/GraphicsSettingsWidget.ui new file mode 100644 index 000000000..1a1509ae1 --- /dev/null +++ b/pcsx2-qt/Settings/GraphicsSettingsWidget.ui @@ -0,0 +1,997 @@ + + + GraphicsSettingsWidget + + + + 0 + 0 + 747 + 890 + + + + Form + + + + 0 + + + 0 + + + 0 + + + 0 + + + + + Renderer + + + + + + Renderer: + + + + + + + + + + Adapter: + + + + + + + + + + + + + 0 + + + true + + + + Display + + + + + + Fullscreen Mode: + + + + + + + + + + Aspect Ratio: + + + + + + + + Fit to Window/Screen + + + + + Standard (4:3) + + + + + Widescreen (16:9) + + + + + + + + FMV Aspect Ratio: + + + + + + + + Off (Default) + + + + + Standard (4:3) + + + + + Widescreen (16:9) + + + + + + + + Interlacing: + + + + + + + + None + + + + + Weave (Top Field First, Sawtooth) + + + + + Weave (Bottom Field First, Sawtooth) + + + + + Bob (Top Field First) + + + + + Bob (Bottom Field First) + + + + + Blend (Top Field First, Half FPS) + + + + + Blend (Bottom Field First, Half FPS) + + + + + Automatic (Default) + + + + + + + + Zoom: + + + + + + + % + + + 1 + + + 300 + + + + + + + Stretch Height: + + + + + + + % + + + 1 + + + 300 + + + + + + + Offset: + + + + + + + + + px + + + 1000 + + + + + + + x + + + + + + + px + + + 1000 + + + + + + + + + + + Integer Upscaling + + + + + + + Bilinear Filtering + + + + + + + Sync To Host Refresh (VSync) + + + + + + + Internal Resolution Screenshots + + + + + + + + + + Rendering + + + + + + Internal Resolution: + + + + + + + + Custom + + + + + Native (PS2) + + + + + 2x Native (~720p) + + + + + 3x Native (~1080p) + + + + + 4x Native (~1440p/2K) + + + + + 5x Native (~1620p) + + + + + 6x Native (~2160p/4K) + + + + + 7x Native (~2520p) + + + + + 8x Native (~2280p) + + + + + + + + Mipmapping: + + + + + + + + Automatic (Default) + + + + + Off + + + + + Basic (Generated Mipmaps) + + + + + Full (PS2 Mipmaps) + + + + + + + + Texture Filtering: + + + + + + + + Nearest + + + + + Bilinear (Forced) + + + + + Bilinear (PS2) + + + + + Bilinear (Forced excluding sprite) + + + + + + + + Trilinear Filtering: + + + + + + + + None (Default) + + + + + Trilinear (PS2) + + + + + Trilinear (Forced) + + + + + + + + Anisotropic Filtering: + + + + + + + + + + Dithering: + + + + + + + + Off + + + + + Scaled + + + + + Unscaled (Default) + + + + + + + + CRC Fix Level: + + + + + + + + Automatic (Default) + + + + + None (Debug) + + + + + Minimum (Debug) + + + + + Partial (OpenGL) + + + + + Full (Direct3D) + + + + + Aggressive + + + + + + + + Blending Accuracy: + + + + + + + + Minimum (Fastest) + + + + + Basic (Recommended) + + + + + Medium + + + + + High + + + + + Full (Very Slow) + + + + + Ultra (Ultra Slow) + + + + + + + + + + Accurate Destination Alpha Test + + + + + + + Conservative Buffer Allocation + + + + + + + GPU Palette Conversion + + + + + + + Preload Textures + + + + + + + Enable Hardware Renderer Fixes + + + + + + + + + + Hardware Fixes + + + + + + Half Screen Fix: + + + + + + + + Automatic (Default) + + + + + Force Disabled + + + + + Force Enabled + + + + + + + + Skipdraw Range: + + + + + + + + + + + + + + + + + + + Auto Flush + + + + + + + Frame Buffer Conversion + + + + + + + Disable Depth Emulation + + + + + + + Memory Wrapping + + + + + + + Disable Safe Features + + + + + + + Preload Frame Data + + + + + + + Fast Texture Invalidation + + + + + + + + + + Upscaling Fixes + + + + + + Half Pixel Offset: + + + + + + + + Off (Default) + + + + + Normal (Vertex) + + + + + Special (Texture) + + + + + Special (Texture - Aggressive) + + + + + + + + Round Sprite: + + + + + + + + Off (Default + + + + + Half + + + + + Full + + + + + + + + Texture Offsets: + + + + + + + + + X: + + + + + + + + + + Y: + + + + + + + + + + + + + + Merge Sprite + + + + + + + Align Sprite + + + + + + + Wild Arms Hack + + + + + + + + + + On-Screen Display + + + + + + OSD Scale: + + + + + + + % + + + 100 + + + 500 + + + + + + + + + Show Notifications + + + + + + + Show Speed + + + + + + + Show FPS + + + + + + + Show Resolution + + + + + + + Show CPU Usage + + + + + + + Show Statistics + + + + + + + + + + Advanced + + + + + + + + Use Blit Swap Chain + + + + + + + Use Debug Device + + + + + + + + + + + + + true + + + + Rendering + + + + + + Extra Rendering Threads: + + + + + + + threads + + + + + + + + + Auto Flush + + + + + + + Mipmapping + + + + + + + Edge Anti-Aliasing + + + + + + + + + + + + + Qt::Vertical + + + + 20 + 40 + + + + + + + + + diff --git a/pcsx2-qt/Settings/HotkeySettingsWidget.cpp b/pcsx2-qt/Settings/HotkeySettingsWidget.cpp new file mode 100644 index 000000000..c9f88fa14 --- /dev/null +++ b/pcsx2-qt/Settings/HotkeySettingsWidget.cpp @@ -0,0 +1,84 @@ +/* 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 "Frontend/InputManager.h" +#include "Settings/HotkeySettingsWidget.h" +#include "Settings/ControllerSettingsDialog.h" +#include "InputBindingWidget.h" +#include "QtUtils.h" +#include "SettingWidgetBinder.h" +#include +#include +#include +#include +#include + +HotkeySettingsWidget::HotkeySettingsWidget(QWidget* parent, ControllerSettingsDialog* dialog) + : QWidget(parent) +{ + createUi(); +} + +HotkeySettingsWidget::~HotkeySettingsWidget() = default; + +void HotkeySettingsWidget::createUi() +{ + QGridLayout* layout = new QGridLayout(this); + layout->setContentsMargins(0, 0, 0, 0); + + m_tab_widget = new QTabWidget(this); + + createButtons(); + + layout->addWidget(m_tab_widget, 0, 0, 1, 1); + + setLayout(layout); +} + +void HotkeySettingsWidget::createButtons() +{ + const std::vector hotkeys(InputManager::GetHotkeyList()); + for (const HotkeyInfo* hotkey : hotkeys) + { + const auto category = qApp->translate("Hotkeys", hotkey->category); + + auto iter = m_categories.find(category); + if (iter == m_categories.end()) + { + QScrollArea* scroll = new QScrollArea(m_tab_widget); + QWidget* container = new QWidget(scroll); + QVBoxLayout* vlayout = new QVBoxLayout(container); + QGridLayout* layout = new QGridLayout(); + layout->setContentsMargins(0, 0, 0, 0); + vlayout->addLayout(layout); + vlayout->addStretch(1); + iter = m_categories.insert(category, Category{container, layout}); + scroll->setWidget(container); + scroll->setWidgetResizable(true); + scroll->setBackgroundRole(QPalette::Base); + scroll->setFrameShape(QFrame::NoFrame); + m_tab_widget->addTab(scroll, category); + } + + QWidget* container = iter->container; + QGridLayout* layout = iter->layout; + const int target_row = layout->count() / 2; + + layout->addWidget(new QLabel(qApp->translate("Hotkeys", hotkey->display_name), container), target_row, 0); + layout->addWidget(new InputBindingWidget(container, "Hotkeys", hotkey->name), target_row, 1); + } +} diff --git a/pcsx2-qt/Settings/HotkeySettingsWidget.h b/pcsx2-qt/Settings/HotkeySettingsWidget.h new file mode 100644 index 000000000..00dfd5d99 --- /dev/null +++ b/pcsx2-qt/Settings/HotkeySettingsWidget.h @@ -0,0 +1,48 @@ +/* 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 . + */ + +#pragma once + +#include +#include +#include +#include + +class QTabWidget; +class QGridLayout; + +class ControllerSettingsDialog; + +class HotkeySettingsWidget : public QWidget +{ + Q_OBJECT + +public: + HotkeySettingsWidget(QWidget* parent, ControllerSettingsDialog* dialog); + ~HotkeySettingsWidget(); + +private: + void createUi(); + void createButtons(); + + QTabWidget* m_tab_widget; + + struct Category + { + QWidget* container; + QGridLayout* layout; + }; + QMap m_categories; +}; diff --git a/pcsx2-qt/Settings/InputBindingDialog.cpp b/pcsx2-qt/Settings/InputBindingDialog.cpp new file mode 100644 index 000000000..2e5a18053 --- /dev/null +++ b/pcsx2-qt/Settings/InputBindingDialog.cpp @@ -0,0 +1,230 @@ +/* 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 "EmuThread.h" +#include "QtHost.h" +#include "Settings/InputBindingDialog.h" +#include +#include +#include + +// _BitScanForward() +#include "pcsx2/GS/GSIntrin.h" + +InputBindingDialog::InputBindingDialog(std::string section_name, std::string key_name, + std::vector bindings, QWidget* parent) + : QDialog(parent) + , m_section_name(std::move(section_name)) + , m_key_name(std::move(key_name)) + , m_bindings(std::move(bindings)) +{ + m_ui.setupUi(this); + m_ui.title->setText( + tr("Bindings for %1 %2").arg(QString::fromStdString(m_section_name)).arg(QString::fromStdString(m_key_name))); + m_ui.buttonBox->button(QDialogButtonBox::Close)->setText(tr("Close")); + + connect(m_ui.addBinding, &QPushButton::clicked, this, &InputBindingDialog::onAddBindingButtonClicked); + connect(m_ui.removeBinding, &QPushButton::clicked, this, &InputBindingDialog::onRemoveBindingButtonClicked); + connect(m_ui.clearBindings, &QPushButton::clicked, this, &InputBindingDialog::onClearBindingsButtonClicked); + connect(m_ui.buttonBox, &QDialogButtonBox::rejected, [this]() { done(0); }); + updateList(); +} + +InputBindingDialog::~InputBindingDialog() +{ + Q_ASSERT(!isListeningForInput()); +} + +bool InputBindingDialog::eventFilter(QObject* watched, QEvent* event) +{ + const QEvent::Type event_type = event->type(); + + // if the key is being released, set the input + if (event_type == QEvent::KeyRelease || event_type == QEvent::MouseButtonPress) + { + addNewBinding(); + stopListeningForInput(); + return true; + } + else if (event_type == QEvent::KeyPress) + { + const QKeyEvent* key_event = static_cast(event); + m_new_bindings.push_back(InputManager::MakeHostKeyboardKey(key_event->key())); + return true; + } + else if (event_type == QEvent::MouseButtonPress) + { + unsigned long button_index; + if (_BitScanForward(&button_index, static_cast(static_cast(event)->button()))) + m_new_bindings.push_back(InputManager::MakeHostMouseButtonKey(button_index)); + return true; + } + else if (event_type == QEvent::MouseButtonDblClick) + { + // just eat double clicks + return true; + } + + return false; +} + +void InputBindingDialog::onInputListenTimerTimeout() +{ + m_input_listen_remaining_seconds--; + if (m_input_listen_remaining_seconds == 0) + { + stopListeningForInput(); + return; + } + + m_ui.status->setText(tr("Push Button/Axis... [%1]").arg(m_input_listen_remaining_seconds)); +} + +void InputBindingDialog::startListeningForInput(u32 timeout_in_seconds) +{ + m_input_listen_timer = new QTimer(this); + m_input_listen_timer->setSingleShot(false); + m_input_listen_timer->start(1000); + + m_input_listen_timer->connect(m_input_listen_timer, &QTimer::timeout, this, + &InputBindingDialog::onInputListenTimerTimeout); + m_input_listen_remaining_seconds = timeout_in_seconds; + m_ui.status->setText(tr("Push Button/Axis... [%1]").arg(m_input_listen_remaining_seconds)); + m_ui.addBinding->setEnabled(false); + m_ui.removeBinding->setEnabled(false); + m_ui.clearBindings->setEnabled(false); + m_ui.buttonBox->setEnabled(false); + + installEventFilter(this); + grabKeyboard(); + grabMouse(); +} + +void InputBindingDialog::stopListeningForInput() +{ + m_ui.status->clear(); + m_ui.addBinding->setEnabled(true); + m_ui.removeBinding->setEnabled(true); + m_ui.clearBindings->setEnabled(true); + m_ui.buttonBox->setEnabled(true); + + delete m_input_listen_timer; + m_input_listen_timer = nullptr; + + releaseMouse(); + releaseKeyboard(); + removeEventFilter(this); +} + +void InputBindingDialog::addNewBinding() +{ + if (m_new_bindings.empty()) + return; + + const std::string new_binding( + InputManager::ConvertInputBindingKeysToString(m_new_bindings.data(), m_new_bindings.size())); + if (!new_binding.empty()) + { + if (std::find(m_bindings.begin(), m_bindings.end(), new_binding) != m_bindings.end()) + return; + + m_ui.bindingList->addItem(QString::fromStdString(new_binding)); + m_bindings.push_back(std::move(new_binding)); + saveListToSettings(); + } +} + +void InputBindingDialog::onAddBindingButtonClicked() +{ + if (isListeningForInput()) + stopListeningForInput(); + + startListeningForInput(TIMEOUT_FOR_BINDING); +} + +void InputBindingDialog::onRemoveBindingButtonClicked() +{ + const int row = m_ui.bindingList->currentRow(); + if (row < 0 || static_cast(row) >= m_bindings.size()) + return; + + m_bindings.erase(m_bindings.begin() + row); + delete m_ui.bindingList->takeItem(row); + saveListToSettings(); +} + +void InputBindingDialog::onClearBindingsButtonClicked() +{ + m_bindings.clear(); + m_ui.bindingList->clear(); + saveListToSettings(); +} + +void InputBindingDialog::updateList() +{ + m_ui.bindingList->clear(); + for (const std::string& binding : m_bindings) + m_ui.bindingList->addItem(QString::fromStdString(binding)); +} + +void InputBindingDialog::saveListToSettings() +{ + if (!m_bindings.empty()) + QtHost::SetBaseStringListSettingValue(m_section_name.c_str(), m_key_name.c_str(), m_bindings); + else + QtHost::RemoveBaseSettingValue(m_section_name.c_str(), m_key_name.c_str()); + + g_emu_thread->reloadInputBindings(); +} + +void InputBindingDialog::inputManagerHookCallback(InputBindingKey key, float value) +{ + for (InputBindingKey other_key : m_new_bindings) + { + if (other_key.MaskDirection() == key.MaskDirection()) + { + if (value < 0.25f) + { + // if this key is in our new binding list, it's a "release", and we're done + addNewBinding(); + stopListeningForInput(); + return; + } + + // otherwise, keep waiting + return; + } + } + + // new binding, add it to the list, but wait for a decent distance first, and then wait for release + if (value >= 0.25f) + m_new_bindings.push_back(key); +} + +void InputBindingDialog::hookInputManager() +{ + InputManager::SetHook([this](InputBindingKey key, float value) { + QMetaObject::invokeMethod(this, "inputManagerHookCallback", Qt::QueuedConnection, Q_ARG(InputBindingKey, key), + Q_ARG(float, value)); + return InputInterceptHook::CallbackResult::StopProcessingEvent; + }); +} + +void InputBindingDialog::unhookInputManager() +{ + InputManager::RemoveHook(); +} diff --git a/pcsx2-qt/Settings/InputBindingDialog.h b/pcsx2-qt/Settings/InputBindingDialog.h new file mode 100644 index 000000000..515febea6 --- /dev/null +++ b/pcsx2-qt/Settings/InputBindingDialog.h @@ -0,0 +1,68 @@ +/* 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 . + */ + +#pragma once +#include "ui_InputBindingDialog.h" +#include "Frontend/InputManager.h" +#include +#include +#include +#include + +class InputBindingDialog : public QDialog +{ + Q_OBJECT + +public: + InputBindingDialog(std::string section_name, std::string key_name, std::vector bindings, QWidget* parent); + ~InputBindingDialog(); + +protected Q_SLOTS: + void onAddBindingButtonClicked(); + void onRemoveBindingButtonClicked(); + void onClearBindingsButtonClicked(); + void onInputListenTimerTimeout(); + void inputManagerHookCallback(InputBindingKey key, float value); + +protected: + enum : u32 + { + TIMEOUT_FOR_BINDING = 5 + }; + + virtual bool eventFilter(QObject* watched, QEvent* event) override; + + virtual void startListeningForInput(u32 timeout_in_seconds); + virtual void stopListeningForInput(); + + bool isListeningForInput() const { return m_input_listen_timer != nullptr; } + void addNewBinding(); + + void updateList(); + void saveListToSettings(); + + void hookInputManager(); + void unhookInputManager(); + + Ui::InputBindingDialog m_ui; + + std::string m_section_name; + std::string m_key_name; + std::vector m_bindings; + std::vector m_new_bindings; + + QTimer* m_input_listen_timer = nullptr; + u32 m_input_listen_remaining_seconds = 0; +}; diff --git a/pcsx2-qt/Settings/InputBindingDialog.ui b/pcsx2-qt/Settings/InputBindingDialog.ui new file mode 100644 index 000000000..c951b5a37 --- /dev/null +++ b/pcsx2-qt/Settings/InputBindingDialog.ui @@ -0,0 +1,76 @@ + + + InputBindingDialog + + + Qt::WindowModal + + + + 0 + 0 + 533 + 283 + + + + Edit Bindings + + + true + + + + + + Bindings for Controller0/ButtonCircle + + + + + + + + + + + + + + + + + + + Add Binding + + + + + + + Remove Binding + + + + + + + Clear Bindings + + + + + + + QDialogButtonBox::Close + + + + + + + + + + diff --git a/pcsx2-qt/Settings/InputBindingWidget.cpp b/pcsx2-qt/Settings/InputBindingWidget.cpp new file mode 100644 index 000000000..6690d533a --- /dev/null +++ b/pcsx2-qt/Settings/InputBindingWidget.cpp @@ -0,0 +1,371 @@ +/* PCSX2 - PS2 Emulator for PCs +/* 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 "EmuThread.h" +#include "QtHost.h" +#include "QtUtils.h" +#include "Settings/ControllerSettingsDialog.h" +#include "Settings/InputBindingDialog.h" +#include "Settings/InputBindingWidget.h" +#include +#include +#include +#include +#include +#include + +#include "pcsx2/GS/GSIntrin.h" // _BitScanForward + +InputBindingWidget::InputBindingWidget(QWidget* parent) + : QPushButton(parent) +{ + connect(this, &QPushButton::clicked, this, &InputBindingWidget::onClicked); +} + +InputBindingWidget::InputBindingWidget(QWidget* parent, std::string section_name, std::string key_name) + : QPushButton(parent) +{ + setMinimumWidth(225); + setMaximumWidth(225); + + connect(this, &QPushButton::clicked, this, &InputBindingWidget::onClicked); + + setKey(std::move(section_name), std::move(key_name)); +} + +InputBindingWidget::~InputBindingWidget() +{ + Q_ASSERT(!isListeningForInput()); +} + +void InputBindingWidget::setKey(std::string section_name, std::string key_name) +{ + m_section_name = std::move(section_name); + m_key_name = std::move(key_name); + m_bindings = QtHost::GetBaseStringListSetting(m_section_name.c_str(), m_key_name.c_str()); + updateText(); +} + +void InputBindingWidget::updateText() +{ + if (m_bindings.empty()) + { + setText(QString()); + } + else if (m_bindings.size() > 1) + { + setText(tr("%n bindings", "", static_cast(m_bindings.size()))); + + // keep the full thing for the tooltip + std::stringstream ss; + bool first = true; + for (const std::string& binding : m_bindings) + { + if (first) + first = false; + else + ss << "\n"; + ss << binding; + } + setToolTip(QString::fromStdString(ss.str())); + } + else + { + QString binding_text(QString::fromStdString(m_bindings[0])); + setToolTip(binding_text); + + // fix up accelerators, and if it's too long, ellipsise it + if (binding_text.contains('&')) + binding_text = binding_text.replace(QStringLiteral("&"), QStringLiteral("&&")); + if (binding_text.length() > 35) + binding_text = binding_text.left(35).append(QStringLiteral("...")); + setText(binding_text); + } +} + +bool InputBindingWidget::eventFilter(QObject* watched, QEvent* event) +{ + const QEvent::Type event_type = event->type(); + + // if the key is being released, set the input + if (event_type == QEvent::KeyRelease || event_type == QEvent::MouseButtonPress) + { + setNewBinding(); + stopListeningForInput(); + return true; + } + else if (event_type == QEvent::KeyPress) + { + const QKeyEvent* key_event = static_cast(event); + m_new_bindings.push_back(InputManager::MakeHostKeyboardKey(key_event->key())); + return true; + } + else if (event_type == QEvent::MouseButtonPress) + { + unsigned long button_index; + if (_BitScanForward(&button_index, static_cast(static_cast(event)->button()))) + m_new_bindings.push_back(InputManager::MakeHostMouseButtonKey(button_index)); + return true; + } + else if (event_type == QEvent::MouseButtonDblClick) + { + // just eat double clicks + return true; + } + + return false; +} + +bool InputBindingWidget::event(QEvent* event) +{ + if (event->type() == QEvent::MouseButtonRelease) + { + QMouseEvent* mev = static_cast(event); + if (mev->button() == Qt::LeftButton && mev->modifiers() & Qt::ShiftModifier) + { + openDialog(); + return false; + } + } + + return QPushButton::event(event); +} + +void InputBindingWidget::mouseReleaseEvent(QMouseEvent* e) +{ + if (e->button() == Qt::RightButton) + { + clearBinding(); + return; + } + + QPushButton::mouseReleaseEvent(e); +} + +void InputBindingWidget::setNewBinding() +{ + if (m_new_bindings.empty()) + return; + + const std::string new_binding( + InputManager::ConvertInputBindingKeysToString(m_new_bindings.data(), m_new_bindings.size())); + if (!new_binding.empty()) + { + QtHost::SetBaseStringSettingValue(m_section_name.c_str(), m_key_name.c_str(), new_binding.c_str()); + g_emu_thread->reloadInputBindings(); + } + + m_bindings.clear(); + m_bindings.push_back(std::move(new_binding)); +} + +void InputBindingWidget::clearBinding() +{ + m_bindings.clear(); + QtHost::RemoveBaseSettingValue(m_section_name.c_str(), m_key_name.c_str()); + g_emu_thread->reloadInputBindings(); + updateText(); +} + +void InputBindingWidget::reloadBinding() +{ + m_bindings = QtHost::GetBaseStringListSetting(m_section_name.c_str(), m_key_name.c_str()); + updateText(); +} + +void InputBindingWidget::onClicked() +{ + if (m_bindings.size() > 1) + { + openDialog(); + return; + } + + if (isListeningForInput()) + stopListeningForInput(); + + startListeningForInput(TIMEOUT_FOR_SINGLE_BINDING); +} + +void InputBindingWidget::onInputListenTimerTimeout() +{ + m_input_listen_remaining_seconds--; + if (m_input_listen_remaining_seconds == 0) + { + stopListeningForInput(); + return; + } + + setText(tr("Push Button/Axis... [%1]").arg(m_input_listen_remaining_seconds)); +} + +void InputBindingWidget::startListeningForInput(u32 timeout_in_seconds) +{ + m_new_bindings.clear(); + m_input_listen_timer = new QTimer(this); + m_input_listen_timer->setSingleShot(false); + m_input_listen_timer->start(1000); + + m_input_listen_timer->connect(m_input_listen_timer, &QTimer::timeout, this, + &InputBindingWidget::onInputListenTimerTimeout); + m_input_listen_remaining_seconds = timeout_in_seconds; + setText(tr("Push Button/Axis... [%1]").arg(m_input_listen_remaining_seconds)); + + installEventFilter(this); + grabKeyboard(); + grabMouse(); + hookInputManager(); +} + +void InputBindingWidget::stopListeningForInput() +{ + updateText(); + delete m_input_listen_timer; + m_input_listen_timer = nullptr; + std::vector().swap(m_new_bindings); + + unhookInputManager(); + releaseMouse(); + releaseKeyboard(); + removeEventFilter(this); +} + +void InputBindingWidget::inputManagerHookCallback(InputBindingKey key, float value) +{ + const float abs_value = std::abs(value); + + for (InputBindingKey other_key : m_new_bindings) + { + if (other_key.MaskDirection() == key.MaskDirection()) + { + if (abs_value < 0.5f) + { + // if this key is in our new binding list, it's a "release", and we're done + setNewBinding(); + stopListeningForInput(); + return; + } + + // otherwise, keep waiting + return; + } + } + + // new binding, add it to the list, but wait for a decent distance first, and then wait for release + if (abs_value >= 0.5f) + { + InputBindingKey key_to_add = key; + key_to_add.negative = (value < 0.0f); + m_new_bindings.push_back(key_to_add); + } +} + +void InputBindingWidget::hookInputManager() +{ + InputManager::SetHook([this](InputBindingKey key, float value) { + QMetaObject::invokeMethod(this, "inputManagerHookCallback", Qt::QueuedConnection, Q_ARG(InputBindingKey, key), + Q_ARG(float, value)); + return InputInterceptHook::CallbackResult::StopProcessingEvent; + }); +} + +void InputBindingWidget::unhookInputManager() +{ + InputManager::RemoveHook(); +} + +void InputBindingWidget::openDialog() +{ + InputBindingDialog binding_dialog(m_section_name, m_key_name, m_bindings, QtUtils::GetRootWidget(this)); + binding_dialog.exec(); + reloadBinding(); +} + +InputVibrationBindingWidget::InputVibrationBindingWidget(QWidget* parent) +{ + connect(this, &QPushButton::clicked, this, &InputVibrationBindingWidget::onClicked); +} + +InputVibrationBindingWidget::InputVibrationBindingWidget(QWidget* parent, ControllerSettingsDialog* dialog, std::string section_name, std::string key_name) +{ + setMinimumWidth(225); + setMaximumWidth(225); + + connect(this, &QPushButton::clicked, this, &InputVibrationBindingWidget::onClicked); + + setKey(dialog, std::move(section_name), std::move(key_name)); +} + +InputVibrationBindingWidget::~InputVibrationBindingWidget() +{ +} + +void InputVibrationBindingWidget::setKey(ControllerSettingsDialog* dialog, std::string section_name, std::string key_name) +{ + m_dialog = dialog; + m_section_name = std::move(section_name); + m_key_name = std::move(key_name); + m_binding = QtHost::GetBaseStringSettingValue(m_section_name.c_str(), m_key_name.c_str()); + setText(QString::fromStdString(m_binding)); +} + +void InputVibrationBindingWidget::clearBinding() +{ + m_binding = {}; + QtHost::RemoveBaseSettingValue(m_section_name.c_str(), m_key_name.c_str()); + g_emu_thread->reloadInputBindings(); + setText(QString()); +} + +void InputVibrationBindingWidget::onClicked() +{ + QInputDialog dialog(QtUtils::GetRootWidget(this)); + + const QString full_key(QStringLiteral("%1/%2").arg(QString::fromStdString(m_section_name)).arg(QString::fromStdString(m_key_name))); + const QString current(QString::fromStdString(m_binding)); + QStringList input_options(m_dialog->getVibrationMotors()); + if (!current.isEmpty() && input_options.indexOf(current) < 0) + input_options.append(current); + + QInputDialog input_dialog(this); + input_dialog.setWindowTitle(full_key); + input_dialog.setLabelText(tr("Select vibration motor for %1.").arg(full_key)); + input_dialog.setInputMode(QInputDialog::TextInput); + input_dialog.setOptions(QInputDialog::UseListViewForComboBoxItems); + input_dialog.setComboBoxEditable(false); + input_dialog.setComboBoxItems(std::move(input_options)); + input_dialog.setTextValue(current); + if (input_dialog.exec() == 0) + return; + + const QString new_value(input_dialog.textValue()); + m_binding = new_value.toStdString(); + QtHost::SetBaseStringSettingValue(m_section_name.c_str(), m_key_name.c_str(), m_binding.c_str()); + setText(new_value); +} + +void InputVibrationBindingWidget::mouseReleaseEvent(QMouseEvent* e) +{ + if (e->button() == Qt::RightButton) + { + clearBinding(); + return; + } + + QPushButton::mouseReleaseEvent(e); +} diff --git a/pcsx2-qt/Settings/InputBindingWidget.h b/pcsx2-qt/Settings/InputBindingWidget.h new file mode 100644 index 000000000..59b4b31c6 --- /dev/null +++ b/pcsx2-qt/Settings/InputBindingWidget.h @@ -0,0 +1,102 @@ +/* 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 . + */ + +#pragma once +#include "common/Pcsx2Defs.h" +#include "pcsx2/Frontend/InputManager.h" +#include +#include + +class QTimer; + +class ControllerSettingsDialog; + +class InputBindingWidget : public QPushButton +{ + Q_OBJECT + +public: + InputBindingWidget(QWidget* parent); + InputBindingWidget(QWidget* parent, std::string section_name, std::string key_name); + ~InputBindingWidget(); + + void setKey(std::string section_name, std::string key_name); + +public Q_SLOTS: + void clearBinding(); + void reloadBinding(); + +protected Q_SLOTS: + void onClicked(); + void onInputListenTimerTimeout(); + void inputManagerHookCallback(InputBindingKey key, float value); + +protected: + enum : u32 + { + TIMEOUT_FOR_SINGLE_BINDING = 5, + TIMEOUT_FOR_ALL_BINDING = 10 + }; + + virtual bool eventFilter(QObject* watched, QEvent* event) override; + virtual bool event(QEvent* event) override; + virtual void mouseReleaseEvent(QMouseEvent* e) override; + + virtual void startListeningForInput(u32 timeout_in_seconds); + virtual void stopListeningForInput(); + virtual void openDialog(); + + bool isListeningForInput() const { return m_input_listen_timer != nullptr; } + void setNewBinding(); + void updateText(); + + void hookInputManager(); + void unhookInputManager(); + + std::string m_section_name; + std::string m_key_name; + std::vector m_bindings; + std::vector m_new_bindings; + QTimer* m_input_listen_timer = nullptr; + u32 m_input_listen_remaining_seconds = 0; +}; + +class InputVibrationBindingWidget : public QPushButton +{ + Q_OBJECT + +public: + InputVibrationBindingWidget(QWidget* parent); + InputVibrationBindingWidget(QWidget* parent, ControllerSettingsDialog* dialog, std::string section_name, std::string key_name); + ~InputVibrationBindingWidget(); + + void setKey(ControllerSettingsDialog* dialog, std::string section_name, std::string key_name); + +public Q_SLOTS: + void clearBinding(); + +protected Q_SLOTS: + void onClicked(); + +protected: + virtual void mouseReleaseEvent(QMouseEvent* e) override; + +private: + std::string m_section_name; + std::string m_key_name; + std::string m_binding; + + ControllerSettingsDialog* m_dialog; +}; diff --git a/pcsx2-qt/Settings/InterfaceSettingsWidget.cpp b/pcsx2-qt/Settings/InterfaceSettingsWidget.cpp new file mode 100644 index 000000000..c872d79eb --- /dev/null +++ b/pcsx2-qt/Settings/InterfaceSettingsWidget.cpp @@ -0,0 +1,101 @@ +/* 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 "InterfaceSettingsWidget.h" +#include "MainWindow.h" +#include "SettingWidgetBinder.h" +#include "SettingsDialog.h" + +static const char* THEME_NAMES[] = {QT_TRANSLATE_NOOP("InterfaceSettingsWidget", "Native"), + QT_TRANSLATE_NOOP("InterfaceSettingsWidget", "Fusion"), + QT_TRANSLATE_NOOP("InterfaceSettingsWidget", "Dark Fusion (Gray)"), + QT_TRANSLATE_NOOP("InterfaceSettingsWidget", "Dark Fusion (Blue)"), nullptr}; + +static const char* THEME_VALUES[] = {"", "fusion", "darkfusion", "darkfusionblue", nullptr}; + +InterfaceSettingsWidget::InterfaceSettingsWidget(QWidget* parent, SettingsDialog* dialog) + : QWidget(parent) +{ + m_ui.setupUi(this); + + SettingWidgetBinder::BindWidgetToBoolSetting(m_ui.inhibitScreensaver, "UI", "InhibitScreensaver", true); + SettingWidgetBinder::BindWidgetToBoolSetting(m_ui.discordPresence, "UI", "DiscordPresence", false); + SettingWidgetBinder::BindWidgetToBoolSetting(m_ui.confirmPowerOff, "UI", "ConfirmPowerOff", true); + SettingWidgetBinder::BindWidgetToBoolSetting(m_ui.saveStateOnExit, "EmuCore", "AutoStateLoadSave", false); + SettingWidgetBinder::BindWidgetToBoolSetting(m_ui.pauseOnStart, "UI", "StartPaused", false); + SettingWidgetBinder::BindWidgetToBoolSetting(m_ui.pauseOnFocusLoss, "UI", "PauseOnFocusLoss", false); + + SettingWidgetBinder::BindWidgetToBoolSetting(m_ui.startFullscreen, "UI", "StartFullscreen", false); + SettingWidgetBinder::BindWidgetToBoolSetting(m_ui.doubleClickTogglesFullscreen, "UI", "DoubleClickTogglesFullscreen", + true); + SettingWidgetBinder::BindWidgetToBoolSetting(m_ui.hideMouseCursor, "UI", "HideMouseCursor", false); + SettingWidgetBinder::BindWidgetToBoolSetting(m_ui.renderToMainWindow, "UI", "RenderToMainWindow", true); + + SettingWidgetBinder::BindWidgetToEnumSetting(m_ui.theme, "UI", "Theme", THEME_NAMES, THEME_VALUES, + MainWindow::DEFAULT_THEME_NAME); + connect(m_ui.theme, QOverload::of(&QComboBox::currentIndexChanged), [this]() { emit themeChanged(); }); + + dialog->registerWidgetHelp( + m_ui.inhibitScreensaver, tr("Inhibit Screensaver"), tr("Checked"), + tr("Prevents the screen saver from activating and the host from sleeping while emulation is running.")); + + dialog->registerWidgetHelp(m_ui.discordPresence, tr("Enable Discord Presence"), tr("Unchecked"), + tr("Shows the game you are currently playing as part of your profile in Discord.")); + if (true) + { + SettingWidgetBinder::BindWidgetToBoolSetting(m_ui.autoUpdateEnabled, "AutoUpdater", "CheckAtStartup", true); + dialog->registerWidgetHelp(m_ui.autoUpdateEnabled, tr("Enable Automatic Update Check"), tr("Checked"), + tr("Automatically checks for updates to the program on startup. Updates can be deferred " + "until later or skipped entirely.")); + + // m_ui.autoUpdateTag->addItems(AutoUpdaterDialog::getTagList()); + // SettingWidgetBinder::BindWidgetToStringSetting(m_ui.autoUpdateTag, "AutoUpdater", "UpdateTag", + // AutoUpdaterDialog::getDefaultTag()); + + // m_ui.autoUpdateCurrentVersion->setText(tr("%1 (%2)").arg(g_scm_tag_str).arg(g_scm_date_str)); + // connect(m_ui.checkForUpdates, &QPushButton::clicked, [this]() { + // m_host_interface->getMainWindow()->checkForUpdates(true); }); + } + else + { + m_ui.verticalLayout->removeWidget(m_ui.automaticUpdaterGroup); + m_ui.automaticUpdaterGroup->hide(); + } + + dialog->registerWidgetHelp( + m_ui.confirmPowerOff, tr("Confirm Power Off"), tr("Checked"), + tr("Determines whether a prompt will be displayed to confirm shutting down the emulator/game " + "when the hotkey is pressed.")); + dialog->registerWidgetHelp(m_ui.saveStateOnExit, tr("Save State On Exit"), tr("Checked"), + tr("Automatically saves the emulator state when powering down or exiting. You can then " + "resume directly from where you left off next time.")); + dialog->registerWidgetHelp(m_ui.pauseOnStart, tr("Pause On Start"), tr("Unchecked"), + tr("Pauses the emulator when a game is started.")); + dialog->registerWidgetHelp(m_ui.pauseOnFocusLoss, tr("Pause On Focus Loss"), tr("Unchecked"), + tr("Pauses the emulator when you minimize the window or switch to another application, " + "and unpauses when you switch back.")); + dialog->registerWidgetHelp(m_ui.startFullscreen, tr("Start Fullscreen"), tr("Unchecked"), + tr("Automatically switches to fullscreen mode when a game is started.")); + dialog->registerWidgetHelp(m_ui.hideMouseCursor, tr("Hide Cursor In Fullscreen"), tr("Checked"), + tr("Hides the mouse pointer/cursor when the emulator is in fullscreen mode.")); + dialog->registerWidgetHelp( + m_ui.renderToMainWindow, tr("Render To Main Window"), tr("Checked"), + tr("Renders the display of the simulated console to the main window of the application, over " + "the game list. If unchecked, the display will render in a separate window.")); +} + +InterfaceSettingsWidget::~InterfaceSettingsWidget() = default; diff --git a/pcsx2-qt/Settings/InterfaceSettingsWidget.h b/pcsx2-qt/Settings/InterfaceSettingsWidget.h new file mode 100644 index 000000000..6df38d1e4 --- /dev/null +++ b/pcsx2-qt/Settings/InterfaceSettingsWidget.h @@ -0,0 +1,37 @@ +/* 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 . + */ + +#pragma once + +#include + +#include "ui_InterfaceSettingsWidget.h" + +class SettingsDialog; + +class InterfaceSettingsWidget : public QWidget +{ + Q_OBJECT + +public: + InterfaceSettingsWidget(QWidget* parent, SettingsDialog* dialog); + ~InterfaceSettingsWidget(); + +Q_SIGNALS: + void themeChanged(); + +private: + Ui::InterfaceSettingsWidget m_ui; +}; diff --git a/pcsx2-qt/Settings/InterfaceSettingsWidget.ui b/pcsx2-qt/Settings/InterfaceSettingsWidget.ui new file mode 100644 index 000000000..2620a71ad --- /dev/null +++ b/pcsx2-qt/Settings/InterfaceSettingsWidget.ui @@ -0,0 +1,240 @@ + + + InterfaceSettingsWidget + + + + 0 + 0 + 698 + 740 + + + + Form + + + + 0 + + + 0 + + + 0 + + + 0 + + + + + Behaviour + + + + + + Inhibit Screensaver + + + + + + + Enable Discord Presence + + + + + + + Pause On Start + + + + + + + Pause On Focus Loss + + + + + + + Confirm Power Off + + + + + + + Save/Load State On Exit/Resume + + + + + + + + + + Game Display + + + + + + + + Start Fullscreen + + + + + + + Hide Mouse Cursor + + + + + + + Render To Main Window + + + + + + + Double-Click Toggles Fullscreen + + + + + + + Disable Window Resizing + + + + + + + + + + + + Preferences + + + + + + Language: + + + + + + + + + + Theme: + + + + + + + + + + + + + Automatic Updater + + + + + + Update Channel: + + + + + + + + + + Current Version: + + + + + + + + + + + + + + Enable Automatic Update Check + + + + + + + + + Qt::Horizontal + + + + 40 + 20 + + + + + + + + Check for Updates... + + + + + + + + + + + + Qt::Vertical + + + + 20 + 40 + + + + + + + + + + + diff --git a/pcsx2-qt/Settings/OSDSettingsWidget.ui b/pcsx2-qt/Settings/OSDSettingsWidget.ui new file mode 100644 index 000000000..fd5fe9ce8 --- /dev/null +++ b/pcsx2-qt/Settings/OSDSettingsWidget.ui @@ -0,0 +1,121 @@ + + + OSDSettingsWidget + + + + 0 + 0 + 604 + 469 + + + + Form + + + + 0 + + + 0 + + + 0 + + + 0 + + + + + On-Screen Display + + + + + + OSD Scale: + + + + + + + % + + + 100 + + + 500 + + + + + + + + + Show Notifications + + + + + + + Show Speed + + + + + + + Show FPS + + + + + + + Show Resolution + + + + + + + Show CPU Usage + + + + + + + Show Statistics + + + + + + + + + + + + Qt::Vertical + + + + 20 + 40 + + + + + + + + + diff --git a/pcsx2-qt/Settings/SettingsDialog.cpp b/pcsx2-qt/Settings/SettingsDialog.cpp new file mode 100644 index 000000000..049b1ad2b --- /dev/null +++ b/pcsx2-qt/Settings/SettingsDialog.cpp @@ -0,0 +1,156 @@ +/* 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 "QtHost.h" +#include "SettingsDialog.h" + +#include "AdvancedSystemSettingsWidget.h" +#include "BIOSSettingsWidget.h" +#include "EmulationSettingsWidget.h" +#include "GameFixSettingsWidget.h" +#include "GameListSettingsWidget.h" +#include "GraphicsSettingsWidget.h" +#include "HotkeySettingsWidget.h" +#include "InterfaceSettingsWidget.h" +#include "SystemSettingsWidget.h" + +#include +#include + +static constexpr char DEFAULT_SETTING_HELP_TEXT[] = ""; + +SettingsDialog::SettingsDialog(QWidget* parent /* = nullptr */) + : QDialog(parent) +{ + m_ui.setupUi(this); + setCategoryHelpTexts(); + + setWindowFlags(windowFlags() & ~Qt::WindowContextHelpButtonHint); + + m_interface_settings = new InterfaceSettingsWidget(m_ui.settingsContainer, this); + m_game_list_settings = new GameListSettingsWidget(m_ui.settingsContainer, this); + m_bios_settings = new BIOSSettingsWidget(m_ui.settingsContainer, this); + m_emulation_settings = new EmulationSettingsWidget(m_ui.settingsContainer, this); + m_system_settings = new SystemSettingsWidget(m_ui.settingsContainer, this); + m_advanced_system_settings = new AdvancedSystemSettingsWidget(m_ui.settingsContainer, this); + m_game_fix_settings_widget = new GameFixSettingsWidget(m_ui.settingsContainer, this); + m_graphics_settings = new GraphicsSettingsWidget(m_ui.settingsContainer, this); + + m_ui.settingsContainer->insertWidget(static_cast(Category::InterfaceSettings), m_interface_settings); + m_ui.settingsContainer->insertWidget(static_cast(Category::GameListSettings), m_game_list_settings); + m_ui.settingsContainer->insertWidget(static_cast(Category::BIOSSettings), m_bios_settings); + m_ui.settingsContainer->insertWidget(static_cast(Category::EmulationSettings), m_emulation_settings); + m_ui.settingsContainer->insertWidget(static_cast(Category::SystemSettings), m_system_settings); + m_ui.settingsContainer->insertWidget(static_cast(Category::AdvancedSystemSettings), m_advanced_system_settings); + m_ui.settingsContainer->insertWidget(static_cast(Category::GameFixSettings), m_game_fix_settings_widget); + m_ui.settingsContainer->insertWidget(static_cast(Category::GraphicsSettings), m_graphics_settings); + + m_ui.settingsCategory->setSizePolicy(QSizePolicy::Minimum, QSizePolicy::Minimum); + m_ui.settingsCategory->setCurrentRow(0); + m_ui.settingsContainer->setCurrentIndex(0); + m_ui.helpText->setText(m_category_help_text[0]); + connect(m_ui.settingsCategory, &QListWidget::currentRowChanged, this, &SettingsDialog::onCategoryCurrentRowChanged); + connect(m_ui.closeButton, &QPushButton::clicked, this, &SettingsDialog::accept); + connect(m_ui.restoreDefaultsButton, &QPushButton::clicked, this, &SettingsDialog::onRestoreDefaultsClicked); +} + +SettingsDialog::~SettingsDialog() = default; + +void SettingsDialog::setCategoryHelpTexts() +{ + m_category_help_text[static_cast(Category::InterfaceSettings)] = + tr("Interface Settings
These options control how the software looks and behaves.

Mouse " + "over " + "an option for additional information."); + m_category_help_text[static_cast(Category::SystemSettings)] = + tr("System Settings
These options determine the configuration of the simulated " + "console.

Mouse over an option for additional information."); + m_category_help_text[static_cast(Category::GameListSettings)] = + tr("Game List Settings
The list above shows the directories which will be searched by " + "PCSX2 to populate the game list. Search directories can be added, removed, and switched to " + "recursive/non-recursive."); + m_category_help_text[static_cast(Category::AudioSettings)] = + tr("Audio Settings
These options control the audio output of the console. Mouse over an option " + "for additional information."); +} + +void SettingsDialog::setCategory(Category category) +{ + if (category >= Category::Count) + return; + + m_ui.settingsCategory->setCurrentRow(static_cast(category)); +} + +void SettingsDialog::onCategoryCurrentRowChanged(int row) +{ + Q_ASSERT(row < static_cast(Category::Count)); + m_ui.settingsContainer->setCurrentIndex(row); + m_ui.helpText->setText(m_category_help_text[row]); +} + +void SettingsDialog::onRestoreDefaultsClicked() +{ + if (QMessageBox::question(this, tr("Confirm Restore Defaults"), + tr("Are you sure you want to restore the default settings? Any preferences will be lost."), + QMessageBox::Yes, QMessageBox::No) != QMessageBox::Yes) + { + return; + } + + // TODO +} + +void SettingsDialog::registerWidgetHelp(QObject* object, QString title, QString recommended_value, QString text) +{ + // construct rich text with formatted description + QString full_text; + full_text += "
"; + full_text += title; + full_text += ""; + full_text += tr("Recommended Value"); + full_text += ": "; + full_text += recommended_value; + full_text += "

"; + full_text += text; + + m_widget_help_text_map[object] = std::move(full_text); + object->installEventFilter(this); +} + +bool SettingsDialog::eventFilter(QObject* object, QEvent* event) +{ + if (event->type() == QEvent::Enter) + { + auto iter = m_widget_help_text_map.constFind(object); + if (iter != m_widget_help_text_map.end()) + { + m_current_help_widget = object; + m_ui.helpText->setText(iter.value()); + } + } + else if (event->type() == QEvent::Leave) + { + if (m_current_help_widget) + { + m_current_help_widget = nullptr; + m_ui.helpText->setText(m_category_help_text[m_ui.settingsCategory->currentRow()]); + } + } + + return QDialog::eventFilter(object, event); +} \ No newline at end of file diff --git a/pcsx2-qt/Settings/SettingsDialog.h b/pcsx2-qt/Settings/SettingsDialog.h new file mode 100644 index 000000000..3b40885a3 --- /dev/null +++ b/pcsx2-qt/Settings/SettingsDialog.h @@ -0,0 +1,101 @@ +/* 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 . + */ + +#pragma once +#include "ui_SettingsDialog.h" +#include +#include +#include +#include + +class InterfaceSettingsWidget; +class GameListSettingsWidget; +class EmulationSettingsWidget; +class BIOSSettingsWidget; +class SystemSettingsWidget; +class AdvancedSystemSettingsWidget; +class GameFixSettingsWidget; +class GraphicsSettingsWidget; +class AudioSettingsWidget; +class MemoryCardSettingsWidget; + +class SettingsDialog final : public QDialog +{ + Q_OBJECT + +public: + enum class Category + { + InterfaceSettings, + GameListSettings, + BIOSSettings, + EmulationSettings, + SystemSettings, + AdvancedSystemSettings, + GameFixSettings, + GraphicsSettings, + AudioSettings, + MemoryCardSettings, + Count + }; + + explicit SettingsDialog(QWidget* parent = nullptr); + ~SettingsDialog(); + + InterfaceSettingsWidget* getInterfaceSettingsWidget() const { return m_interface_settings; } + GameListSettingsWidget* getGameListSettingsWidget() const { return m_game_list_settings; } + BIOSSettingsWidget* getBIOSSettingsWidget() const { return m_bios_settings; } + EmulationSettingsWidget* getEmulationSettingsWidget() const { return m_emulation_settings; } + SystemSettingsWidget* getSystemSettingsWidget() const { return m_system_settings; } + AdvancedSystemSettingsWidget* getAdvancedSystemSettingsWidget() const { return m_advanced_system_settings; } + GameFixSettingsWidget* getGameFixSettingsWidget() const { return m_game_fix_settings_widget; } + GraphicsSettingsWidget* getGraphicsSettingsWidget() const { return m_graphics_settings; } + AudioSettingsWidget* getAudioSettingsWidget() const { return m_audio_settings; } + MemoryCardSettingsWidget* getMemoryCardSettingsWidget() const { return m_memory_card_settings; } + + void registerWidgetHelp(QObject* object, QString title, QString recommended_value, QString text); + bool eventFilter(QObject* object, QEvent* event) override; + +Q_SIGNALS: + void settingsResetToDefaults(); + +public Q_SLOTS: + void setCategory(Category category); + +private Q_SLOTS: + void onCategoryCurrentRowChanged(int row); + void onRestoreDefaultsClicked(); + +private: + void setCategoryHelpTexts(); + + Ui::SettingsDialog m_ui; + + InterfaceSettingsWidget* m_interface_settings = nullptr; + GameListSettingsWidget* m_game_list_settings = nullptr; + BIOSSettingsWidget* m_bios_settings = nullptr; + EmulationSettingsWidget* m_emulation_settings = nullptr; + SystemSettingsWidget* m_system_settings = nullptr; + AdvancedSystemSettingsWidget* m_advanced_system_settings = nullptr; + GameFixSettingsWidget* m_game_fix_settings_widget = nullptr; + GraphicsSettingsWidget* m_graphics_settings = nullptr; + AudioSettingsWidget* m_audio_settings = nullptr; + MemoryCardSettingsWidget* m_memory_card_settings = nullptr; + + std::array(Category::Count)> m_category_help_text; + + QObject* m_current_help_widget = nullptr; + QMap m_widget_help_text_map; +}; diff --git a/pcsx2-qt/Settings/SettingsDialog.ui b/pcsx2-qt/Settings/SettingsDialog.ui new file mode 100644 index 000000000..0aa684919 --- /dev/null +++ b/pcsx2-qt/Settings/SettingsDialog.ui @@ -0,0 +1,227 @@ + + + SettingsDialog + + + Qt::WindowModal + + + + 0 + 0 + 780 + 650 + + + + + 0 + 0 + + + + PCSX2 Settings + + + + + + + 0 + 0 + + + + + 150 + 0 + + + + + 150 + 16777215 + + + + + 32 + 32 + + + + + Interface + + + + + + + + Game List + + + + + + + + BIOS + + + + + + + + Emulation + + + + + + + + System + + + + + + + + Advanced System + + + + + + + + Game Fixes + + + + + + + + Graphics + + + + + + + + Audio + + + + + + + + Memory Cards + + + + + + + + + + + + 500 + 0 + + + + 0 + + + + + + + + + + 0 + 120 + + + + + 16777215 + 120 + + + + true + + + + + + + + + + Preset: Custom + + + + + Preset: Safe (Default) + + + + + Preset: Fast + + + + + + + + Restore Defaults + + + + + + + Qt::Horizontal + + + + 40 + 20 + + + + + + + + Close + + + true + + + + + + + + + + + + diff --git a/pcsx2-qt/Settings/SystemSettingsWidget.cpp b/pcsx2-qt/Settings/SystemSettingsWidget.cpp new file mode 100644 index 000000000..90f2276c6 --- /dev/null +++ b/pcsx2-qt/Settings/SystemSettingsWidget.cpp @@ -0,0 +1,88 @@ +/* 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 +#include + +#include "EmuThread.h" +#include "QtUtils.h" +#include "SettingWidgetBinder.h" +#include "SettingsDialog.h" +#include "SystemSettingsWidget.h" + +static constexpr int MINIMUM_EE_CYCLE_RATE = -3; +static constexpr int MAXIMUM_EE_CYCLE_RATE = 3; +static constexpr int DEFAULT_EE_CYCLE_RATE = 0; +static constexpr int DEFAULT_EE_CYCLE_SKIP = 0; + +SystemSettingsWidget::SystemSettingsWidget(QWidget* parent, SettingsDialog* dialog) + : QWidget(parent) +{ + m_ui.setupUi(this); + + SettingWidgetBinder::BindWidgetToIntSetting(m_ui.eeRoundingMode, "EmuCore/CPU", "FPU.Roundmode", 3); + m_ui.eeClampMode->setCurrentIndex(getClampingModeIndex(false)); + connect(m_ui.eeClampMode, QOverload::of(&QComboBox::currentIndexChanged), + [this](int index) { setClampingMode(false, index); }); + m_ui.eeCycleRate->setCurrentIndex( + std::clamp(QtHost::GetBaseIntSettingValue("EmuCore/Speedhacks", "EECycleRate", DEFAULT_EE_CYCLE_RATE), + MINIMUM_EE_CYCLE_RATE, MAXIMUM_EE_CYCLE_RATE) + + (0 - MINIMUM_EE_CYCLE_RATE)); + connect(m_ui.eeCycleRate, QOverload::of(&QComboBox::currentIndexChanged), [](int index) { + QtHost::SetBaseIntSettingValue("EmuCore/Speedhacks", "EECycleRate", MINIMUM_EE_CYCLE_RATE + index); + g_emu_thread->applySettings(); + }); + SettingWidgetBinder::BindWidgetToIntSetting(m_ui.eeCycleSkipping, "EmuCore/Speedhacks", "EECycleSkip", 0); + + SettingWidgetBinder::BindWidgetToBoolSetting(m_ui.MTVU, "EmuCore/Speedhacks", "vuThread", false); + SettingWidgetBinder::BindWidgetToBoolSetting(m_ui.instantVU1, "EmuCore/Speedhacks", "vu1Instant", true); + SettingWidgetBinder::BindWidgetToIntSetting(m_ui.vuRoundingMode, "EmuCore/CPU", "VU.Roundmode", 3); + m_ui.vuClampMode->setCurrentIndex(getClampingModeIndex(true)); + connect(m_ui.vuClampMode, QOverload::of(&QComboBox::currentIndexChanged), + [this](int index) { setClampingMode(true, index); }); + + SettingWidgetBinder::BindWidgetToBoolSetting(m_ui.fastCDVD, "EmuCore/Speedhacks", "fastCDVD", false); + + updateVU1InstantState(); + connect(m_ui.MTVU, &QCheckBox::stateChanged, this, &SystemSettingsWidget::updateVU1InstantState); +} + +SystemSettingsWidget::~SystemSettingsWidget() = default; + +void SystemSettingsWidget::updateVU1InstantState() +{ + m_ui.instantVU1->setEnabled(!m_ui.MTVU->isChecked()); +} + +int SystemSettingsWidget::getClampingModeIndex(bool vu) +{ + if (QtHost::GetBaseBoolSettingValue("EmuCore/CPU/Recompiler", vu ? "vuSignOverflow" : "fpuFullMode", false)) + return 3; + if (QtHost::GetBaseBoolSettingValue("EmuCore/CPU/Recompiler", vu ? "vuExtraOverflow" : "fpuExtraOverflow", false)) + return 2; + if (QtHost::GetBaseBoolSettingValue("EmuCore/CPU/Recompiler", vu ? "vuOverflow" : "fpuOverflow", true)) + return 1; + return 0; +} + +void SystemSettingsWidget::setClampingMode(bool vu, int index) +{ + QtHost::SetBaseBoolSettingValue("EmuCore/CPU/Recompiler", vu ? "vuSignOverflow" : "fpuFullMode", (index >= 3)); + QtHost::SetBaseBoolSettingValue("EmuCore/CPU/Recompiler", vu ? "vuExtraOverflow" : "fpuExtraOverflow", (index >= 2)); + QtHost::SetBaseBoolSettingValue("EmuCore/CPU/Recompiler", vu ? "vuOverflow" : "fpuOverflow", (index >= 1)); + g_emu_thread->applySettings(); +} diff --git a/pcsx2-qt/Settings/SystemSettingsWidget.h b/pcsx2-qt/Settings/SystemSettingsWidget.h new file mode 100644 index 000000000..09f3d468e --- /dev/null +++ b/pcsx2-qt/Settings/SystemSettingsWidget.h @@ -0,0 +1,40 @@ +/* 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 . + */ + +#pragma once + +#include + +#include "ui_SystemSettingsWidget.h" + +class SettingsDialog; + +class SystemSettingsWidget : public QWidget +{ + Q_OBJECT + +public: + SystemSettingsWidget(QWidget* parent, SettingsDialog* dialog); + ~SystemSettingsWidget(); + +private Q_SLOTS: + void updateVU1InstantState(); + +private: + static int getClampingModeIndex(bool vu); + static void setClampingMode(bool vu, int index); + + Ui::SystemSettingsWidget m_ui; +}; diff --git a/pcsx2-qt/Settings/SystemSettingsWidget.ui b/pcsx2-qt/Settings/SystemSettingsWidget.ui new file mode 100644 index 000000000..4e33de1ac --- /dev/null +++ b/pcsx2-qt/Settings/SystemSettingsWidget.ui @@ -0,0 +1,301 @@ + + + SystemSettingsWidget + + + + 0 + 0 + 648 + 454 + + + + Form + + + + 0 + + + 0 + + + 0 + + + 0 + + + + + EmotionEngine (EE, MIPS-IV) + + + + + + Cycle Rate: + + + + + + + + 50% Speed + + + + + 60% Speed + + + + + 75% Speed + + + + + 100% (Normal Speed) + + + + + 130% Speed + + + + + 180% Speed + + + + + 300% Speed + + + + + + + + Cycle Skip: + + + + + + + + Normal + + + + + Mild Underclock + + + + + Moderate Underclock + + + + + Maximum Underclock + + + + + + + + Rounding Mode: + + + + + + + + Nearest + + + + + Negative + + + + + Positive + + + + + Chop/Zero (Default) + + + + + + + + Clamping Mode: + + + + + + + + None + + + + + Normal (Default) + + + + + Extra + Preserve Sign + + + + + Full + + + + + + + + + + + Vector Units (VU) + + + + + + Rounding Mode: + + + + + + + + Nearest + + + + + Negative + + + + + Positive + + + + + Chop/Zero (Default) + + + + + + + + Clamping Mode: + + + + + + + + None + + + + + Normal (Default) + + + + + Extra + Preserve Sign + + + + + Full + + + + + + + + + + MTVU (Multi-Threaded VU1) + + + + + + + Instant VU1 + + + + + + + + + + + + I/O Processor (IOP, MIPS-I) + + + + + + Enable Fast CDVD + + + + + + + + + + Qt::Vertical + + + + 20 + 3 + + + + + + + + + + + diff --git a/pcsx2-qt/pcsx2-qt.vcxproj b/pcsx2-qt/pcsx2-qt.vcxproj new file mode 100644 index 000000000..1476c6a41 --- /dev/null +++ b/pcsx2-qt/pcsx2-qt.vcxproj @@ -0,0 +1,292 @@ + + + + + + + {2A016F21-87AE-4154-8271-1F57E91408E9} + + + + Application + Unicode + $(DefaultPlatformToolset) + true + true + false + + + + + + + + + + + + + + + + + AllRules.ruleset + $(EXEString) + + + + $(SolutionDir)3rdparty\xbyak;%(AdditionalIncludeDirectories) + $(SolutionDir)3rdparty\freetype\include;%(AdditionalIncludeDirectories) + $(SolutionDir)3rdparty\xz\xz\src\liblzma\api;%(AdditionalIncludeDirectories) + $(SolutionDir)3rdparty\baseclasses;%(AdditionalIncludeDirectories) + $(SolutionDir)3rdparty\zlib;%(AdditionalIncludeDirectories) + $(SolutionDir)3rdparty\libpng;%(AdditionalIncludeDirectories) + $(SolutionDir)3rdparty\glad\include;%(AdditionalIncludeDirectories) + $(SolutionDir)3rdparty\simpleini\include;%(AdditionalIncludeDirectories) + $(ProjectDir);$(SolutionDir)pcsx2;%(AdditionalIncludeDirectories) + + %(AdditionalIncludeDirectories);$(ProjectDir)\Settings;$(ProjectDir)\GameList + Async + Use + PrecompiledHeader.h + NoExtensions + WIN32_LEAN_AND_MEAN;LZMA_API_STATIC;BUILD_DX=1;SPU2X_PORTAUDIO;DIRECTINPUT_VERSION=0x0800;PCSX2_CORE;DISABLE_RECORDING;%(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) + _M_SSE=0x401;%(PreprocessorDefinitions) + _M_SSE=0x501;%(PreprocessorDefinitions) + + NotSet + StreamingSIMDExtensions2 + AdvancedVectorExtensions2 + false + $(IntDir)%(RelativeDir) + + + Precise + true + /Zc:__cplusplus /Zo /utf-8%(AdditionalOptions) + + + Windows + 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;%(AdditionalDependencies) + + + + + $(SolutionDir)3rdparty\wxwidgets3.0\$(PlatformName);$(SolutionDir)3rdparty\wxwidgets3.0\include;%(AdditionalIncludeDirectories) + + + + + + + + + + + + + {27f17499-a372-4408-8afa-4f9f4584fbd3} + + + {449ad25e-424a-4714-babc-68706cdcc33b} + + + {bc236261-77e8-4567-8d09-45cd02965eb6} + + + {d6973076-9317-4ef2-a0b8-b7a18ac0713e} + + + {47afdbef-f15f-4bc0-b436-5be443c3f80f} + + + {0fae817d-9a32-4830-857e-81da57246e16} + false + + + {e9b51944-7e6d-4bcd-83f2-7bbd5a46182d} + + + {3fcc50c2-81e9-5db2-b8d8-2129427568b1} + + + {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} + + + {88fb34ec-845e-4f21-a552-f1573b9ed167} + + + {4639972e-424e-4e13-8b07-ca403c481346} + + + {6c7986c4-3e4d-4dcc-b3c6-6bb12b238995} + + + {c0293b32-5acf-40f0-aa6c-e6da6f3bf33a} + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + Create + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + Document + + + + + + + + + + + + + + + + + + + + + + + + + + + NotUsing + + + + + Document + + + Document + + + Document + + + Document + + + Document + + + Document + + + Document + + + Document + + + Document + + + Document + + + Document + + + Document + + + Document + + + Document + + + Document + + + + + + \ No newline at end of file diff --git a/pcsx2-qt/pcsx2-qt.vcxproj.filters b/pcsx2-qt/pcsx2-qt.vcxproj.filters new file mode 100644 index 000000000..18273a68d --- /dev/null +++ b/pcsx2-qt/pcsx2-qt.vcxproj.filters @@ -0,0 +1,301 @@ + + + + + {1f18feb4-85f0-455d-927a-d4d6b226d8a5} + + + {1ab68559-2163-40b5-818b-d2a7ff210c12} + + + {11eec1ee-70c1-42ad-8cd1-e7f72f4c8673} + + + {aff229ec-39ea-40d4-b1c9-b5c4a932a2e0} + + + + + Resources + + + Resources + + + Resources + + + Resources + + + Resources + + + Resources + + + Resources + + + Resources + + + Resources + + + + + + + + + moc + + + Resources + + + + moc + + + + + moc + + + + Settings + + + Settings + + + Settings + + + Settings + + + Settings + + + Settings + + + Settings + + + moc + + + moc + + + moc + + + moc + + + moc + + + moc + + + moc + + + moc + + + Settings + + + moc + + + Settings + + + moc + + + moc + + + moc + + + Settings + + + Settings + + + Settings + + + + GameList + + + GameList + + + GameList + + + moc + + + moc + + + moc + + + moc + + + moc + + + Settings + + + Settings + + + moc + + + Settings + + + moc + + + + + Resources + + + + + + + + + + + + + + Settings + + + Settings + + + Settings + + + Settings + + + Settings + + + Settings + + + Settings + + + Settings + + + Settings + + + Settings + + + Settings + + + Settings + + + + GameList + + + GameList + + + GameList + + + Settings + + + Settings + + + Settings + + + + + Resources + + + + + + Settings + + + Settings + + + Settings + + + Settings + + + Settings + + + Settings + + + Settings + + + Settings + + + Settings + + + Settings + + + Settings + + + Settings + + + Settings + + + Settings + + + \ No newline at end of file diff --git a/pcsx2-qt/resources/generate.sh b/pcsx2-qt/resources/generate.sh new file mode 100644 index 000000000..7fc10c012 --- /dev/null +++ b/pcsx2-qt/resources/generate.sh @@ -0,0 +1,13 @@ +#!/bin/sh + +IFS=" +" + +printf "\n" +printf "\t\n" +for i in $(find . -not -iname '*.sh' -not -iname '*.qrc' -type f | cut -d'/' -f2-99); do + printf "\t\t%s\n" "$i" +done +printf "\t\n" +printf "\n" + diff --git a/pcsx2-qt/resources/icons/AppIcon.png b/pcsx2-qt/resources/icons/AppIcon.png new file mode 100644 index 000000000..66a7d5b77 Binary files /dev/null and b/pcsx2-qt/resources/icons/AppIcon.png differ diff --git a/pcsx2-qt/resources/icons/AppIcon@2x.png b/pcsx2-qt/resources/icons/AppIcon@2x.png new file mode 100644 index 000000000..00587a305 Binary files /dev/null and b/pcsx2-qt/resources/icons/AppIcon@2x.png differ diff --git a/pcsx2-qt/resources/icons/AppIconLarge.png b/pcsx2-qt/resources/icons/AppIconLarge.png new file mode 100644 index 000000000..00587a305 Binary files /dev/null and b/pcsx2-qt/resources/icons/AppIconLarge.png differ diff --git a/pcsx2-qt/resources/icons/AppIconLarge@2x.png b/pcsx2-qt/resources/icons/AppIconLarge@2x.png new file mode 100644 index 000000000..913fd0fae Binary files /dev/null and b/pcsx2-qt/resources/icons/AppIconLarge@2x.png differ diff --git a/pcsx2-qt/resources/icons/QT.png b/pcsx2-qt/resources/icons/QT.png new file mode 100644 index 000000000..7ff73ce53 Binary files /dev/null and b/pcsx2-qt/resources/icons/QT.png differ diff --git a/pcsx2-qt/resources/icons/address-book-new-22.png b/pcsx2-qt/resources/icons/address-book-new-22.png new file mode 100644 index 000000000..3ceee199b Binary files /dev/null and b/pcsx2-qt/resources/icons/address-book-new-22.png differ diff --git a/pcsx2-qt/resources/icons/address-book-new-22@2x.png b/pcsx2-qt/resources/icons/address-book-new-22@2x.png new file mode 100644 index 000000000..f80cff57f Binary files /dev/null and b/pcsx2-qt/resources/icons/address-book-new-22@2x.png differ diff --git a/pcsx2-qt/resources/icons/applications-system-24.png b/pcsx2-qt/resources/icons/applications-system-24.png new file mode 100644 index 000000000..4f738c46c Binary files /dev/null and b/pcsx2-qt/resources/icons/applications-system-24.png differ diff --git a/pcsx2-qt/resources/icons/applications-system-24@2x.png b/pcsx2-qt/resources/icons/applications-system-24@2x.png new file mode 100644 index 000000000..6c72a6fc0 Binary files /dev/null and b/pcsx2-qt/resources/icons/applications-system-24@2x.png differ diff --git a/pcsx2-qt/resources/icons/black/16/artboard-2-line.png b/pcsx2-qt/resources/icons/black/16/artboard-2-line.png new file mode 100644 index 000000000..d9a8162b2 Binary files /dev/null and b/pcsx2-qt/resources/icons/black/16/artboard-2-line.png differ diff --git a/pcsx2-qt/resources/icons/black/16/book-open-line.png b/pcsx2-qt/resources/icons/black/16/book-open-line.png new file mode 100644 index 000000000..cbf19bf8b Binary files /dev/null and b/pcsx2-qt/resources/icons/black/16/book-open-line.png differ diff --git a/pcsx2-qt/resources/icons/black/16/brush-line.png b/pcsx2-qt/resources/icons/black/16/brush-line.png new file mode 100644 index 000000000..7e106776d Binary files /dev/null and b/pcsx2-qt/resources/icons/black/16/brush-line.png differ diff --git a/pcsx2-qt/resources/icons/black/16/close-line.png b/pcsx2-qt/resources/icons/black/16/close-line.png new file mode 100644 index 000000000..0081fa6ba Binary files /dev/null and b/pcsx2-qt/resources/icons/black/16/close-line.png differ diff --git a/pcsx2-qt/resources/icons/black/16/dashboard-line.png b/pcsx2-qt/resources/icons/black/16/dashboard-line.png new file mode 100644 index 000000000..93abfc8e7 Binary files /dev/null and b/pcsx2-qt/resources/icons/black/16/dashboard-line.png differ diff --git a/pcsx2-qt/resources/icons/black/16/disc-line.png b/pcsx2-qt/resources/icons/black/16/disc-line.png new file mode 100644 index 000000000..dc13fc824 Binary files /dev/null and b/pcsx2-qt/resources/icons/black/16/disc-line.png differ diff --git a/pcsx2-qt/resources/icons/black/16/door-open-line.png b/pcsx2-qt/resources/icons/black/16/door-open-line.png new file mode 100644 index 000000000..1d1775335 Binary files /dev/null and b/pcsx2-qt/resources/icons/black/16/door-open-line.png differ diff --git a/pcsx2-qt/resources/icons/black/16/download-2-line.png b/pcsx2-qt/resources/icons/black/16/download-2-line.png new file mode 100644 index 000000000..d1f402195 Binary files /dev/null and b/pcsx2-qt/resources/icons/black/16/download-2-line.png differ diff --git a/pcsx2-qt/resources/icons/black/16/dvd-line.png b/pcsx2-qt/resources/icons/black/16/dvd-line.png new file mode 100644 index 000000000..64ac22477 Binary files /dev/null and b/pcsx2-qt/resources/icons/black/16/dvd-line.png differ diff --git a/pcsx2-qt/resources/icons/black/16/file-add-line.png b/pcsx2-qt/resources/icons/black/16/file-add-line.png new file mode 100644 index 000000000..41e8b74f0 Binary files /dev/null and b/pcsx2-qt/resources/icons/black/16/file-add-line.png differ diff --git a/pcsx2-qt/resources/icons/black/16/file-line.png b/pcsx2-qt/resources/icons/black/16/file-line.png new file mode 100644 index 000000000..5ef1a2ef8 Binary files /dev/null and b/pcsx2-qt/resources/icons/black/16/file-line.png differ diff --git a/pcsx2-qt/resources/icons/black/16/file-list-line.png b/pcsx2-qt/resources/icons/black/16/file-list-line.png new file mode 100644 index 000000000..d7bbbff34 Binary files /dev/null and b/pcsx2-qt/resources/icons/black/16/file-list-line.png differ diff --git a/pcsx2-qt/resources/icons/black/16/file-reduce-line.png b/pcsx2-qt/resources/icons/black/16/file-reduce-line.png new file mode 100644 index 000000000..ab2896983 Binary files /dev/null and b/pcsx2-qt/resources/icons/black/16/file-reduce-line.png differ diff --git a/pcsx2-qt/resources/icons/black/16/file-search-line.png b/pcsx2-qt/resources/icons/black/16/file-search-line.png new file mode 100644 index 000000000..4cfabe46c Binary files /dev/null and b/pcsx2-qt/resources/icons/black/16/file-search-line.png differ diff --git a/pcsx2-qt/resources/icons/black/16/file-settings-line.png b/pcsx2-qt/resources/icons/black/16/file-settings-line.png new file mode 100644 index 000000000..8f9c0e426 Binary files /dev/null and b/pcsx2-qt/resources/icons/black/16/file-settings-line.png differ diff --git a/pcsx2-qt/resources/icons/black/16/flask-line.png b/pcsx2-qt/resources/icons/black/16/flask-line.png new file mode 100644 index 000000000..dbe64d5b7 Binary files /dev/null and b/pcsx2-qt/resources/icons/black/16/flask-line.png differ diff --git a/pcsx2-qt/resources/icons/black/16/folder-add-line.png b/pcsx2-qt/resources/icons/black/16/folder-add-line.png new file mode 100644 index 000000000..a59e31fe7 Binary files /dev/null and b/pcsx2-qt/resources/icons/black/16/folder-add-line.png differ diff --git a/pcsx2-qt/resources/icons/black/16/folder-open-line.png b/pcsx2-qt/resources/icons/black/16/folder-open-line.png new file mode 100644 index 000000000..06516f703 Binary files /dev/null and b/pcsx2-qt/resources/icons/black/16/folder-open-line.png differ diff --git a/pcsx2-qt/resources/icons/black/16/folder-reduce-line.png b/pcsx2-qt/resources/icons/black/16/folder-reduce-line.png new file mode 100644 index 000000000..4b2f1bcc9 Binary files /dev/null and b/pcsx2-qt/resources/icons/black/16/folder-reduce-line.png differ diff --git a/pcsx2-qt/resources/icons/black/16/folder-settings-line.png b/pcsx2-qt/resources/icons/black/16/folder-settings-line.png new file mode 100644 index 000000000..63a6999b0 Binary files /dev/null and b/pcsx2-qt/resources/icons/black/16/folder-settings-line.png differ diff --git a/pcsx2-qt/resources/icons/black/16/fullscreen-line.png b/pcsx2-qt/resources/icons/black/16/fullscreen-line.png new file mode 100644 index 000000000..0dab73af6 Binary files /dev/null and b/pcsx2-qt/resources/icons/black/16/fullscreen-line.png differ diff --git a/pcsx2-qt/resources/icons/black/16/function-line.png b/pcsx2-qt/resources/icons/black/16/function-line.png new file mode 100644 index 000000000..86f9c16b1 Binary files /dev/null and b/pcsx2-qt/resources/icons/black/16/function-line.png differ diff --git a/pcsx2-qt/resources/icons/black/16/gamepad-line.png b/pcsx2-qt/resources/icons/black/16/gamepad-line.png new file mode 100644 index 000000000..760b89c18 Binary files /dev/null and b/pcsx2-qt/resources/icons/black/16/gamepad-line.png differ diff --git a/pcsx2-qt/resources/icons/black/16/hard-drive-2-line.png b/pcsx2-qt/resources/icons/black/16/hard-drive-2-line.png new file mode 100644 index 000000000..128df6859 Binary files /dev/null and b/pcsx2-qt/resources/icons/black/16/hard-drive-2-line.png differ diff --git a/pcsx2-qt/resources/icons/black/16/keyboard-line.png b/pcsx2-qt/resources/icons/black/16/keyboard-line.png new file mode 100644 index 000000000..e0e28e67b Binary files /dev/null and b/pcsx2-qt/resources/icons/black/16/keyboard-line.png differ diff --git a/pcsx2-qt/resources/icons/black/16/layout-grid-line.png b/pcsx2-qt/resources/icons/black/16/layout-grid-line.png new file mode 100644 index 000000000..ca59badeb Binary files /dev/null and b/pcsx2-qt/resources/icons/black/16/layout-grid-line.png differ diff --git a/pcsx2-qt/resources/icons/black/16/list-check.png b/pcsx2-qt/resources/icons/black/16/list-check.png new file mode 100644 index 000000000..f6f8c1cbf Binary files /dev/null and b/pcsx2-qt/resources/icons/black/16/list-check.png differ diff --git a/pcsx2-qt/resources/icons/black/16/pause-line.png b/pcsx2-qt/resources/icons/black/16/pause-line.png new file mode 100644 index 000000000..1cf3293d0 Binary files /dev/null and b/pcsx2-qt/resources/icons/black/16/pause-line.png differ diff --git a/pcsx2-qt/resources/icons/black/16/play-line.png b/pcsx2-qt/resources/icons/black/16/play-line.png new file mode 100644 index 000000000..975bed636 Binary files /dev/null and b/pcsx2-qt/resources/icons/black/16/play-line.png differ diff --git a/pcsx2-qt/resources/icons/black/16/refresh-line.png b/pcsx2-qt/resources/icons/black/16/refresh-line.png new file mode 100644 index 000000000..3dd2cf3e6 Binary files /dev/null and b/pcsx2-qt/resources/icons/black/16/refresh-line.png differ diff --git a/pcsx2-qt/resources/icons/black/16/restart-line.png b/pcsx2-qt/resources/icons/black/16/restart-line.png new file mode 100644 index 000000000..5e5aa600c Binary files /dev/null and b/pcsx2-qt/resources/icons/black/16/restart-line.png differ diff --git a/pcsx2-qt/resources/icons/black/16/save-3-line.png b/pcsx2-qt/resources/icons/black/16/save-3-line.png new file mode 100644 index 000000000..3870b3d5a Binary files /dev/null and b/pcsx2-qt/resources/icons/black/16/save-3-line.png differ diff --git a/pcsx2-qt/resources/icons/black/16/screenshot-2-line.png b/pcsx2-qt/resources/icons/black/16/screenshot-2-line.png new file mode 100644 index 000000000..539e4341c Binary files /dev/null and b/pcsx2-qt/resources/icons/black/16/screenshot-2-line.png differ diff --git a/pcsx2-qt/resources/icons/black/16/sd-card-line.png b/pcsx2-qt/resources/icons/black/16/sd-card-line.png new file mode 100644 index 000000000..4df0dcc58 Binary files /dev/null and b/pcsx2-qt/resources/icons/black/16/sd-card-line.png differ diff --git a/pcsx2-qt/resources/icons/black/16/settings-3-line.png b/pcsx2-qt/resources/icons/black/16/settings-3-line.png new file mode 100644 index 000000000..f7a85502a Binary files /dev/null and b/pcsx2-qt/resources/icons/black/16/settings-3-line.png differ diff --git a/pcsx2-qt/resources/icons/black/16/shut-down-line.png b/pcsx2-qt/resources/icons/black/16/shut-down-line.png new file mode 100644 index 000000000..6ebb60b46 Binary files /dev/null and b/pcsx2-qt/resources/icons/black/16/shut-down-line.png differ diff --git a/pcsx2-qt/resources/icons/black/16/tv-2-line.png b/pcsx2-qt/resources/icons/black/16/tv-2-line.png new file mode 100644 index 000000000..4963c3dbd Binary files /dev/null and b/pcsx2-qt/resources/icons/black/16/tv-2-line.png differ diff --git a/pcsx2-qt/resources/icons/black/16/volume-up-line.png b/pcsx2-qt/resources/icons/black/16/volume-up-line.png new file mode 100644 index 000000000..97c692a12 Binary files /dev/null and b/pcsx2-qt/resources/icons/black/16/volume-up-line.png differ diff --git a/pcsx2-qt/resources/icons/black/16/window-2-line.png b/pcsx2-qt/resources/icons/black/16/window-2-line.png new file mode 100644 index 000000000..411cb6b17 Binary files /dev/null and b/pcsx2-qt/resources/icons/black/16/window-2-line.png differ diff --git a/pcsx2-qt/resources/icons/black/24/artboard-2-line.png b/pcsx2-qt/resources/icons/black/24/artboard-2-line.png new file mode 100644 index 000000000..fca0d241c Binary files /dev/null and b/pcsx2-qt/resources/icons/black/24/artboard-2-line.png differ diff --git a/pcsx2-qt/resources/icons/black/24/book-open-line.png b/pcsx2-qt/resources/icons/black/24/book-open-line.png new file mode 100644 index 000000000..6df595037 Binary files /dev/null and b/pcsx2-qt/resources/icons/black/24/book-open-line.png differ diff --git a/pcsx2-qt/resources/icons/black/24/brush-line.png b/pcsx2-qt/resources/icons/black/24/brush-line.png new file mode 100644 index 000000000..fb34a6754 Binary files /dev/null and b/pcsx2-qt/resources/icons/black/24/brush-line.png differ diff --git a/pcsx2-qt/resources/icons/black/24/close-line.png b/pcsx2-qt/resources/icons/black/24/close-line.png new file mode 100644 index 000000000..e12070dac Binary files /dev/null and b/pcsx2-qt/resources/icons/black/24/close-line.png differ diff --git a/pcsx2-qt/resources/icons/black/24/dashboard-line.png b/pcsx2-qt/resources/icons/black/24/dashboard-line.png new file mode 100644 index 000000000..d8e7b159f Binary files /dev/null and b/pcsx2-qt/resources/icons/black/24/dashboard-line.png differ diff --git a/pcsx2-qt/resources/icons/black/24/disc-line.png b/pcsx2-qt/resources/icons/black/24/disc-line.png new file mode 100644 index 000000000..4c0fc6766 Binary files /dev/null and b/pcsx2-qt/resources/icons/black/24/disc-line.png differ diff --git a/pcsx2-qt/resources/icons/black/24/door-open-line.png b/pcsx2-qt/resources/icons/black/24/door-open-line.png new file mode 100644 index 000000000..17a9f2322 Binary files /dev/null and b/pcsx2-qt/resources/icons/black/24/door-open-line.png differ diff --git a/pcsx2-qt/resources/icons/black/24/download-2-line.png b/pcsx2-qt/resources/icons/black/24/download-2-line.png new file mode 100644 index 000000000..902c8ee97 Binary files /dev/null and b/pcsx2-qt/resources/icons/black/24/download-2-line.png differ diff --git a/pcsx2-qt/resources/icons/black/24/dvd-line.png b/pcsx2-qt/resources/icons/black/24/dvd-line.png new file mode 100644 index 000000000..f48de612b Binary files /dev/null and b/pcsx2-qt/resources/icons/black/24/dvd-line.png differ diff --git a/pcsx2-qt/resources/icons/black/24/file-add-line.png b/pcsx2-qt/resources/icons/black/24/file-add-line.png new file mode 100644 index 000000000..3b90629c8 Binary files /dev/null and b/pcsx2-qt/resources/icons/black/24/file-add-line.png differ diff --git a/pcsx2-qt/resources/icons/black/24/file-line.png b/pcsx2-qt/resources/icons/black/24/file-line.png new file mode 100644 index 000000000..e891c3e41 Binary files /dev/null and b/pcsx2-qt/resources/icons/black/24/file-line.png differ diff --git a/pcsx2-qt/resources/icons/black/24/file-list-line.png b/pcsx2-qt/resources/icons/black/24/file-list-line.png new file mode 100644 index 000000000..1efd53e3f Binary files /dev/null and b/pcsx2-qt/resources/icons/black/24/file-list-line.png differ diff --git a/pcsx2-qt/resources/icons/black/24/file-reduce-line.png b/pcsx2-qt/resources/icons/black/24/file-reduce-line.png new file mode 100644 index 000000000..ad81c1c63 Binary files /dev/null and b/pcsx2-qt/resources/icons/black/24/file-reduce-line.png differ diff --git a/pcsx2-qt/resources/icons/black/24/file-search-line.png b/pcsx2-qt/resources/icons/black/24/file-search-line.png new file mode 100644 index 000000000..c6dd997aa Binary files /dev/null and b/pcsx2-qt/resources/icons/black/24/file-search-line.png differ diff --git a/pcsx2-qt/resources/icons/black/24/file-settings-line.png b/pcsx2-qt/resources/icons/black/24/file-settings-line.png new file mode 100644 index 000000000..3ab5164f8 Binary files /dev/null and b/pcsx2-qt/resources/icons/black/24/file-settings-line.png differ diff --git a/pcsx2-qt/resources/icons/black/24/flask-line.png b/pcsx2-qt/resources/icons/black/24/flask-line.png new file mode 100644 index 000000000..c5fc1b4c0 Binary files /dev/null and b/pcsx2-qt/resources/icons/black/24/flask-line.png differ diff --git a/pcsx2-qt/resources/icons/black/24/folder-add-line.png b/pcsx2-qt/resources/icons/black/24/folder-add-line.png new file mode 100644 index 000000000..0980dd6cd Binary files /dev/null and b/pcsx2-qt/resources/icons/black/24/folder-add-line.png differ diff --git a/pcsx2-qt/resources/icons/black/24/folder-open-line.png b/pcsx2-qt/resources/icons/black/24/folder-open-line.png new file mode 100644 index 000000000..85eab4a36 Binary files /dev/null and b/pcsx2-qt/resources/icons/black/24/folder-open-line.png differ diff --git a/pcsx2-qt/resources/icons/black/24/folder-reduce-line.png b/pcsx2-qt/resources/icons/black/24/folder-reduce-line.png new file mode 100644 index 000000000..8e983fe3d Binary files /dev/null and b/pcsx2-qt/resources/icons/black/24/folder-reduce-line.png differ diff --git a/pcsx2-qt/resources/icons/black/24/folder-settings-line.png b/pcsx2-qt/resources/icons/black/24/folder-settings-line.png new file mode 100644 index 000000000..0f39c380e Binary files /dev/null and b/pcsx2-qt/resources/icons/black/24/folder-settings-line.png differ diff --git a/pcsx2-qt/resources/icons/black/24/fullscreen-line.png b/pcsx2-qt/resources/icons/black/24/fullscreen-line.png new file mode 100644 index 000000000..14b0cd18d Binary files /dev/null and b/pcsx2-qt/resources/icons/black/24/fullscreen-line.png differ diff --git a/pcsx2-qt/resources/icons/black/24/function-line.png b/pcsx2-qt/resources/icons/black/24/function-line.png new file mode 100644 index 000000000..772a83775 Binary files /dev/null and b/pcsx2-qt/resources/icons/black/24/function-line.png differ diff --git a/pcsx2-qt/resources/icons/black/24/gamepad-line.png b/pcsx2-qt/resources/icons/black/24/gamepad-line.png new file mode 100644 index 000000000..976025826 Binary files /dev/null and b/pcsx2-qt/resources/icons/black/24/gamepad-line.png differ diff --git a/pcsx2-qt/resources/icons/black/24/hard-drive-2-line.png b/pcsx2-qt/resources/icons/black/24/hard-drive-2-line.png new file mode 100644 index 000000000..49f709128 Binary files /dev/null and b/pcsx2-qt/resources/icons/black/24/hard-drive-2-line.png differ diff --git a/pcsx2-qt/resources/icons/black/24/keyboard-line.png b/pcsx2-qt/resources/icons/black/24/keyboard-line.png new file mode 100644 index 000000000..c468edefe Binary files /dev/null and b/pcsx2-qt/resources/icons/black/24/keyboard-line.png differ diff --git a/pcsx2-qt/resources/icons/black/24/layout-grid-line.png b/pcsx2-qt/resources/icons/black/24/layout-grid-line.png new file mode 100644 index 000000000..8a0f86011 Binary files /dev/null and b/pcsx2-qt/resources/icons/black/24/layout-grid-line.png differ diff --git a/pcsx2-qt/resources/icons/black/24/list-check.png b/pcsx2-qt/resources/icons/black/24/list-check.png new file mode 100644 index 000000000..1f92a58f0 Binary files /dev/null and b/pcsx2-qt/resources/icons/black/24/list-check.png differ diff --git a/pcsx2-qt/resources/icons/black/24/pause-line.png b/pcsx2-qt/resources/icons/black/24/pause-line.png new file mode 100644 index 000000000..6643b0696 Binary files /dev/null and b/pcsx2-qt/resources/icons/black/24/pause-line.png differ diff --git a/pcsx2-qt/resources/icons/black/24/play-line.png b/pcsx2-qt/resources/icons/black/24/play-line.png new file mode 100644 index 000000000..6518c4925 Binary files /dev/null and b/pcsx2-qt/resources/icons/black/24/play-line.png differ diff --git a/pcsx2-qt/resources/icons/black/24/refresh-line.png b/pcsx2-qt/resources/icons/black/24/refresh-line.png new file mode 100644 index 000000000..10be80e86 Binary files /dev/null and b/pcsx2-qt/resources/icons/black/24/refresh-line.png differ diff --git a/pcsx2-qt/resources/icons/black/24/restart-line.png b/pcsx2-qt/resources/icons/black/24/restart-line.png new file mode 100644 index 000000000..1cbcb5ea0 Binary files /dev/null and b/pcsx2-qt/resources/icons/black/24/restart-line.png differ diff --git a/pcsx2-qt/resources/icons/black/24/save-3-line.png b/pcsx2-qt/resources/icons/black/24/save-3-line.png new file mode 100644 index 000000000..399fa0ae0 Binary files /dev/null and b/pcsx2-qt/resources/icons/black/24/save-3-line.png differ diff --git a/pcsx2-qt/resources/icons/black/24/screenshot-2-line.png b/pcsx2-qt/resources/icons/black/24/screenshot-2-line.png new file mode 100644 index 000000000..8ea02beec Binary files /dev/null and b/pcsx2-qt/resources/icons/black/24/screenshot-2-line.png differ diff --git a/pcsx2-qt/resources/icons/black/24/sd-card-line.png b/pcsx2-qt/resources/icons/black/24/sd-card-line.png new file mode 100644 index 000000000..ebd63d146 Binary files /dev/null and b/pcsx2-qt/resources/icons/black/24/sd-card-line.png differ diff --git a/pcsx2-qt/resources/icons/black/24/settings-3-line.png b/pcsx2-qt/resources/icons/black/24/settings-3-line.png new file mode 100644 index 000000000..e90047dcd Binary files /dev/null and b/pcsx2-qt/resources/icons/black/24/settings-3-line.png differ diff --git a/pcsx2-qt/resources/icons/black/24/shut-down-line.png b/pcsx2-qt/resources/icons/black/24/shut-down-line.png new file mode 100644 index 000000000..295537559 Binary files /dev/null and b/pcsx2-qt/resources/icons/black/24/shut-down-line.png differ diff --git a/pcsx2-qt/resources/icons/black/24/volume-up-line.png b/pcsx2-qt/resources/icons/black/24/volume-up-line.png new file mode 100644 index 000000000..37d71d919 Binary files /dev/null and b/pcsx2-qt/resources/icons/black/24/volume-up-line.png differ diff --git a/pcsx2-qt/resources/icons/black/24/window-2-line.png b/pcsx2-qt/resources/icons/black/24/window-2-line.png new file mode 100644 index 000000000..bae211995 Binary files /dev/null and b/pcsx2-qt/resources/icons/black/24/window-2-line.png differ diff --git a/pcsx2-qt/resources/icons/black/32/artboard-2-line.png b/pcsx2-qt/resources/icons/black/32/artboard-2-line.png new file mode 100644 index 000000000..34c13c3bc Binary files /dev/null and b/pcsx2-qt/resources/icons/black/32/artboard-2-line.png differ diff --git a/pcsx2-qt/resources/icons/black/32/book-open-line.png b/pcsx2-qt/resources/icons/black/32/book-open-line.png new file mode 100644 index 000000000..1087833a5 Binary files /dev/null and b/pcsx2-qt/resources/icons/black/32/book-open-line.png differ diff --git a/pcsx2-qt/resources/icons/black/32/brush-line.png b/pcsx2-qt/resources/icons/black/32/brush-line.png new file mode 100644 index 000000000..bf9451402 Binary files /dev/null and b/pcsx2-qt/resources/icons/black/32/brush-line.png differ diff --git a/pcsx2-qt/resources/icons/black/32/close-line.png b/pcsx2-qt/resources/icons/black/32/close-line.png new file mode 100644 index 000000000..8ea40455e Binary files /dev/null and b/pcsx2-qt/resources/icons/black/32/close-line.png differ diff --git a/pcsx2-qt/resources/icons/black/32/dashboard-line.png b/pcsx2-qt/resources/icons/black/32/dashboard-line.png new file mode 100644 index 000000000..718e7006d Binary files /dev/null and b/pcsx2-qt/resources/icons/black/32/dashboard-line.png differ diff --git a/pcsx2-qt/resources/icons/black/32/disc-line.png b/pcsx2-qt/resources/icons/black/32/disc-line.png new file mode 100644 index 000000000..1201bf92b Binary files /dev/null and b/pcsx2-qt/resources/icons/black/32/disc-line.png differ diff --git a/pcsx2-qt/resources/icons/black/32/door-open-line.png b/pcsx2-qt/resources/icons/black/32/door-open-line.png new file mode 100644 index 000000000..3f73bd767 Binary files /dev/null and b/pcsx2-qt/resources/icons/black/32/door-open-line.png differ diff --git a/pcsx2-qt/resources/icons/black/32/download-2-line.png b/pcsx2-qt/resources/icons/black/32/download-2-line.png new file mode 100644 index 000000000..20d31069e Binary files /dev/null and b/pcsx2-qt/resources/icons/black/32/download-2-line.png differ diff --git a/pcsx2-qt/resources/icons/black/32/dvd-line.png b/pcsx2-qt/resources/icons/black/32/dvd-line.png new file mode 100644 index 000000000..ae7a5ed67 Binary files /dev/null and b/pcsx2-qt/resources/icons/black/32/dvd-line.png differ diff --git a/pcsx2-qt/resources/icons/black/32/file-add-line.png b/pcsx2-qt/resources/icons/black/32/file-add-line.png new file mode 100644 index 000000000..8abe032ca Binary files /dev/null and b/pcsx2-qt/resources/icons/black/32/file-add-line.png differ diff --git a/pcsx2-qt/resources/icons/black/32/file-line.png b/pcsx2-qt/resources/icons/black/32/file-line.png new file mode 100644 index 000000000..bd99b76cf Binary files /dev/null and b/pcsx2-qt/resources/icons/black/32/file-line.png differ diff --git a/pcsx2-qt/resources/icons/black/32/file-list-line.png b/pcsx2-qt/resources/icons/black/32/file-list-line.png new file mode 100644 index 000000000..36d657d85 Binary files /dev/null and b/pcsx2-qt/resources/icons/black/32/file-list-line.png differ diff --git a/pcsx2-qt/resources/icons/black/32/file-reduce-line.png b/pcsx2-qt/resources/icons/black/32/file-reduce-line.png new file mode 100644 index 000000000..020dbc9a3 Binary files /dev/null and b/pcsx2-qt/resources/icons/black/32/file-reduce-line.png differ diff --git a/pcsx2-qt/resources/icons/black/32/file-search-line.png b/pcsx2-qt/resources/icons/black/32/file-search-line.png new file mode 100644 index 000000000..95ef70ac2 Binary files /dev/null and b/pcsx2-qt/resources/icons/black/32/file-search-line.png differ diff --git a/pcsx2-qt/resources/icons/black/32/file-settings-line.png b/pcsx2-qt/resources/icons/black/32/file-settings-line.png new file mode 100644 index 000000000..48dadf061 Binary files /dev/null and b/pcsx2-qt/resources/icons/black/32/file-settings-line.png differ diff --git a/pcsx2-qt/resources/icons/black/32/flask-line.png b/pcsx2-qt/resources/icons/black/32/flask-line.png new file mode 100644 index 000000000..a3ee5d8af Binary files /dev/null and b/pcsx2-qt/resources/icons/black/32/flask-line.png differ diff --git a/pcsx2-qt/resources/icons/black/32/folder-add-line.png b/pcsx2-qt/resources/icons/black/32/folder-add-line.png new file mode 100644 index 000000000..8f45e967e Binary files /dev/null and b/pcsx2-qt/resources/icons/black/32/folder-add-line.png differ diff --git a/pcsx2-qt/resources/icons/black/32/folder-open-line.png b/pcsx2-qt/resources/icons/black/32/folder-open-line.png new file mode 100644 index 000000000..a67baf13c Binary files /dev/null and b/pcsx2-qt/resources/icons/black/32/folder-open-line.png differ diff --git a/pcsx2-qt/resources/icons/black/32/folder-reduce-line.png b/pcsx2-qt/resources/icons/black/32/folder-reduce-line.png new file mode 100644 index 000000000..3b0ddaf5d Binary files /dev/null and b/pcsx2-qt/resources/icons/black/32/folder-reduce-line.png differ diff --git a/pcsx2-qt/resources/icons/black/32/folder-settings-line.png b/pcsx2-qt/resources/icons/black/32/folder-settings-line.png new file mode 100644 index 000000000..24c0a16eb Binary files /dev/null and b/pcsx2-qt/resources/icons/black/32/folder-settings-line.png differ diff --git a/pcsx2-qt/resources/icons/black/32/fullscreen-line.png b/pcsx2-qt/resources/icons/black/32/fullscreen-line.png new file mode 100644 index 000000000..f31b05b32 Binary files /dev/null and b/pcsx2-qt/resources/icons/black/32/fullscreen-line.png differ diff --git a/pcsx2-qt/resources/icons/black/32/function-line.png b/pcsx2-qt/resources/icons/black/32/function-line.png new file mode 100644 index 000000000..a27358328 Binary files /dev/null and b/pcsx2-qt/resources/icons/black/32/function-line.png differ diff --git a/pcsx2-qt/resources/icons/black/32/gamepad-line.png b/pcsx2-qt/resources/icons/black/32/gamepad-line.png new file mode 100644 index 000000000..31fa23028 Binary files /dev/null and b/pcsx2-qt/resources/icons/black/32/gamepad-line.png differ diff --git a/pcsx2-qt/resources/icons/black/32/hard-drive-2-line.png b/pcsx2-qt/resources/icons/black/32/hard-drive-2-line.png new file mode 100644 index 000000000..6945deff0 Binary files /dev/null and b/pcsx2-qt/resources/icons/black/32/hard-drive-2-line.png differ diff --git a/pcsx2-qt/resources/icons/black/32/keyboard-line.png b/pcsx2-qt/resources/icons/black/32/keyboard-line.png new file mode 100644 index 000000000..26515e8b2 Binary files /dev/null and b/pcsx2-qt/resources/icons/black/32/keyboard-line.png differ diff --git a/pcsx2-qt/resources/icons/black/32/layout-grid-line.png b/pcsx2-qt/resources/icons/black/32/layout-grid-line.png new file mode 100644 index 000000000..08d986c4f Binary files /dev/null and b/pcsx2-qt/resources/icons/black/32/layout-grid-line.png differ diff --git a/pcsx2-qt/resources/icons/black/32/list-check.png b/pcsx2-qt/resources/icons/black/32/list-check.png new file mode 100644 index 000000000..a4cfbf3ba Binary files /dev/null and b/pcsx2-qt/resources/icons/black/32/list-check.png differ diff --git a/pcsx2-qt/resources/icons/black/32/pause-line.png b/pcsx2-qt/resources/icons/black/32/pause-line.png new file mode 100644 index 000000000..f6a825b92 Binary files /dev/null and b/pcsx2-qt/resources/icons/black/32/pause-line.png differ diff --git a/pcsx2-qt/resources/icons/black/32/play-line.png b/pcsx2-qt/resources/icons/black/32/play-line.png new file mode 100644 index 000000000..bc5c490f6 Binary files /dev/null and b/pcsx2-qt/resources/icons/black/32/play-line.png differ diff --git a/pcsx2-qt/resources/icons/black/32/refresh-line.png b/pcsx2-qt/resources/icons/black/32/refresh-line.png new file mode 100644 index 000000000..fe3b33221 Binary files /dev/null and b/pcsx2-qt/resources/icons/black/32/refresh-line.png differ diff --git a/pcsx2-qt/resources/icons/black/32/restart-line.png b/pcsx2-qt/resources/icons/black/32/restart-line.png new file mode 100644 index 000000000..e3043f5f4 Binary files /dev/null and b/pcsx2-qt/resources/icons/black/32/restart-line.png differ diff --git a/pcsx2-qt/resources/icons/black/32/save-3-line.png b/pcsx2-qt/resources/icons/black/32/save-3-line.png new file mode 100644 index 000000000..dce50a655 Binary files /dev/null and b/pcsx2-qt/resources/icons/black/32/save-3-line.png differ diff --git a/pcsx2-qt/resources/icons/black/32/screenshot-2-line.png b/pcsx2-qt/resources/icons/black/32/screenshot-2-line.png new file mode 100644 index 000000000..1fd192ab3 Binary files /dev/null and b/pcsx2-qt/resources/icons/black/32/screenshot-2-line.png differ diff --git a/pcsx2-qt/resources/icons/black/32/sd-card-line.png b/pcsx2-qt/resources/icons/black/32/sd-card-line.png new file mode 100644 index 000000000..cc21905be Binary files /dev/null and b/pcsx2-qt/resources/icons/black/32/sd-card-line.png differ diff --git a/pcsx2-qt/resources/icons/black/32/settings-3-line.png b/pcsx2-qt/resources/icons/black/32/settings-3-line.png new file mode 100644 index 000000000..9146a3f9f Binary files /dev/null and b/pcsx2-qt/resources/icons/black/32/settings-3-line.png differ diff --git a/pcsx2-qt/resources/icons/black/32/shut-down-line.png b/pcsx2-qt/resources/icons/black/32/shut-down-line.png new file mode 100644 index 000000000..7d2f69f8f Binary files /dev/null and b/pcsx2-qt/resources/icons/black/32/shut-down-line.png differ diff --git a/pcsx2-qt/resources/icons/black/32/tv-2-line.png b/pcsx2-qt/resources/icons/black/32/tv-2-line.png new file mode 100644 index 000000000..ac03c5827 Binary files /dev/null and b/pcsx2-qt/resources/icons/black/32/tv-2-line.png differ diff --git a/pcsx2-qt/resources/icons/black/32/volume-up-line.png b/pcsx2-qt/resources/icons/black/32/volume-up-line.png new file mode 100644 index 000000000..4911c6510 Binary files /dev/null and b/pcsx2-qt/resources/icons/black/32/volume-up-line.png differ diff --git a/pcsx2-qt/resources/icons/black/32/window-2-line.png b/pcsx2-qt/resources/icons/black/32/window-2-line.png new file mode 100644 index 000000000..c084b1da1 Binary files /dev/null and b/pcsx2-qt/resources/icons/black/32/window-2-line.png differ diff --git a/pcsx2-qt/resources/icons/black/48/artboard-2-line.png b/pcsx2-qt/resources/icons/black/48/artboard-2-line.png new file mode 100644 index 000000000..130efe522 Binary files /dev/null and b/pcsx2-qt/resources/icons/black/48/artboard-2-line.png differ diff --git a/pcsx2-qt/resources/icons/black/48/book-open-line.png b/pcsx2-qt/resources/icons/black/48/book-open-line.png new file mode 100644 index 000000000..d10a82eef Binary files /dev/null and b/pcsx2-qt/resources/icons/black/48/book-open-line.png differ diff --git a/pcsx2-qt/resources/icons/black/48/brush-line.png b/pcsx2-qt/resources/icons/black/48/brush-line.png new file mode 100644 index 000000000..ca8829afa Binary files /dev/null and b/pcsx2-qt/resources/icons/black/48/brush-line.png differ diff --git a/pcsx2-qt/resources/icons/black/48/close-line.png b/pcsx2-qt/resources/icons/black/48/close-line.png new file mode 100644 index 000000000..f7e711acb Binary files /dev/null and b/pcsx2-qt/resources/icons/black/48/close-line.png differ diff --git a/pcsx2-qt/resources/icons/black/48/dashboard-line.png b/pcsx2-qt/resources/icons/black/48/dashboard-line.png new file mode 100644 index 000000000..42440435a Binary files /dev/null and b/pcsx2-qt/resources/icons/black/48/dashboard-line.png differ diff --git a/pcsx2-qt/resources/icons/black/48/disc-line.png b/pcsx2-qt/resources/icons/black/48/disc-line.png new file mode 100644 index 000000000..f93b88bbc Binary files /dev/null and b/pcsx2-qt/resources/icons/black/48/disc-line.png differ diff --git a/pcsx2-qt/resources/icons/black/48/door-open-line.png b/pcsx2-qt/resources/icons/black/48/door-open-line.png new file mode 100644 index 000000000..48af7caef Binary files /dev/null and b/pcsx2-qt/resources/icons/black/48/door-open-line.png differ diff --git a/pcsx2-qt/resources/icons/black/48/download-2-line.png b/pcsx2-qt/resources/icons/black/48/download-2-line.png new file mode 100644 index 000000000..adf469515 Binary files /dev/null and b/pcsx2-qt/resources/icons/black/48/download-2-line.png differ diff --git a/pcsx2-qt/resources/icons/black/48/dvd-line.png b/pcsx2-qt/resources/icons/black/48/dvd-line.png new file mode 100644 index 000000000..8c2446fca Binary files /dev/null and b/pcsx2-qt/resources/icons/black/48/dvd-line.png differ diff --git a/pcsx2-qt/resources/icons/black/48/file-add-line.png b/pcsx2-qt/resources/icons/black/48/file-add-line.png new file mode 100644 index 000000000..1b6a8ed56 Binary files /dev/null and b/pcsx2-qt/resources/icons/black/48/file-add-line.png differ diff --git a/pcsx2-qt/resources/icons/black/48/file-line.png b/pcsx2-qt/resources/icons/black/48/file-line.png new file mode 100644 index 000000000..120d666c6 Binary files /dev/null and b/pcsx2-qt/resources/icons/black/48/file-line.png differ diff --git a/pcsx2-qt/resources/icons/black/48/file-list-line.png b/pcsx2-qt/resources/icons/black/48/file-list-line.png new file mode 100644 index 000000000..c24ca8037 Binary files /dev/null and b/pcsx2-qt/resources/icons/black/48/file-list-line.png differ diff --git a/pcsx2-qt/resources/icons/black/48/file-reduce-line.png b/pcsx2-qt/resources/icons/black/48/file-reduce-line.png new file mode 100644 index 000000000..98fbe14d4 Binary files /dev/null and b/pcsx2-qt/resources/icons/black/48/file-reduce-line.png differ diff --git a/pcsx2-qt/resources/icons/black/48/file-search-line.png b/pcsx2-qt/resources/icons/black/48/file-search-line.png new file mode 100644 index 000000000..c37e6c3b9 Binary files /dev/null and b/pcsx2-qt/resources/icons/black/48/file-search-line.png differ diff --git a/pcsx2-qt/resources/icons/black/48/file-settings-line.png b/pcsx2-qt/resources/icons/black/48/file-settings-line.png new file mode 100644 index 000000000..2a23cc5f4 Binary files /dev/null and b/pcsx2-qt/resources/icons/black/48/file-settings-line.png differ diff --git a/pcsx2-qt/resources/icons/black/48/flask-line.png b/pcsx2-qt/resources/icons/black/48/flask-line.png new file mode 100644 index 000000000..efbebd09e Binary files /dev/null and b/pcsx2-qt/resources/icons/black/48/flask-line.png differ diff --git a/pcsx2-qt/resources/icons/black/48/folder-add-line.png b/pcsx2-qt/resources/icons/black/48/folder-add-line.png new file mode 100644 index 000000000..d491b954d Binary files /dev/null and b/pcsx2-qt/resources/icons/black/48/folder-add-line.png differ diff --git a/pcsx2-qt/resources/icons/black/48/folder-open-line.png b/pcsx2-qt/resources/icons/black/48/folder-open-line.png new file mode 100644 index 000000000..7aa1a86d8 Binary files /dev/null and b/pcsx2-qt/resources/icons/black/48/folder-open-line.png differ diff --git a/pcsx2-qt/resources/icons/black/48/folder-reduce-line.png b/pcsx2-qt/resources/icons/black/48/folder-reduce-line.png new file mode 100644 index 000000000..99859c135 Binary files /dev/null and b/pcsx2-qt/resources/icons/black/48/folder-reduce-line.png differ diff --git a/pcsx2-qt/resources/icons/black/48/folder-settings-line.png b/pcsx2-qt/resources/icons/black/48/folder-settings-line.png new file mode 100644 index 000000000..67743bef6 Binary files /dev/null and b/pcsx2-qt/resources/icons/black/48/folder-settings-line.png differ diff --git a/pcsx2-qt/resources/icons/black/48/fullscreen-line.png b/pcsx2-qt/resources/icons/black/48/fullscreen-line.png new file mode 100644 index 000000000..897dd7fd4 Binary files /dev/null and b/pcsx2-qt/resources/icons/black/48/fullscreen-line.png differ diff --git a/pcsx2-qt/resources/icons/black/48/function-line.png b/pcsx2-qt/resources/icons/black/48/function-line.png new file mode 100644 index 000000000..bc61e2750 Binary files /dev/null and b/pcsx2-qt/resources/icons/black/48/function-line.png differ diff --git a/pcsx2-qt/resources/icons/black/48/gamepad-line.png b/pcsx2-qt/resources/icons/black/48/gamepad-line.png new file mode 100644 index 000000000..6127dbc11 Binary files /dev/null and b/pcsx2-qt/resources/icons/black/48/gamepad-line.png differ diff --git a/pcsx2-qt/resources/icons/black/48/hard-drive-2-line.png b/pcsx2-qt/resources/icons/black/48/hard-drive-2-line.png new file mode 100644 index 000000000..4086783db Binary files /dev/null and b/pcsx2-qt/resources/icons/black/48/hard-drive-2-line.png differ diff --git a/pcsx2-qt/resources/icons/black/48/keyboard-line.png b/pcsx2-qt/resources/icons/black/48/keyboard-line.png new file mode 100644 index 000000000..9f220bb14 Binary files /dev/null and b/pcsx2-qt/resources/icons/black/48/keyboard-line.png differ diff --git a/pcsx2-qt/resources/icons/black/48/layout-grid-line.png b/pcsx2-qt/resources/icons/black/48/layout-grid-line.png new file mode 100644 index 000000000..a88820cd8 Binary files /dev/null and b/pcsx2-qt/resources/icons/black/48/layout-grid-line.png differ diff --git a/pcsx2-qt/resources/icons/black/48/list-check.png b/pcsx2-qt/resources/icons/black/48/list-check.png new file mode 100644 index 000000000..0e19585c0 Binary files /dev/null and b/pcsx2-qt/resources/icons/black/48/list-check.png differ diff --git a/pcsx2-qt/resources/icons/black/48/pause-line.png b/pcsx2-qt/resources/icons/black/48/pause-line.png new file mode 100644 index 000000000..41fd6dc83 Binary files /dev/null and b/pcsx2-qt/resources/icons/black/48/pause-line.png differ diff --git a/pcsx2-qt/resources/icons/black/48/play-line.png b/pcsx2-qt/resources/icons/black/48/play-line.png new file mode 100644 index 000000000..e17f83465 Binary files /dev/null and b/pcsx2-qt/resources/icons/black/48/play-line.png differ diff --git a/pcsx2-qt/resources/icons/black/48/refresh-line.png b/pcsx2-qt/resources/icons/black/48/refresh-line.png new file mode 100644 index 000000000..65e36cf6e Binary files /dev/null and b/pcsx2-qt/resources/icons/black/48/refresh-line.png differ diff --git a/pcsx2-qt/resources/icons/black/48/restart-line.png b/pcsx2-qt/resources/icons/black/48/restart-line.png new file mode 100644 index 000000000..8b928d5da Binary files /dev/null and b/pcsx2-qt/resources/icons/black/48/restart-line.png differ diff --git a/pcsx2-qt/resources/icons/black/48/save-3-line.png b/pcsx2-qt/resources/icons/black/48/save-3-line.png new file mode 100644 index 000000000..aa174bfed Binary files /dev/null and b/pcsx2-qt/resources/icons/black/48/save-3-line.png differ diff --git a/pcsx2-qt/resources/icons/black/48/screenshot-2-line.png b/pcsx2-qt/resources/icons/black/48/screenshot-2-line.png new file mode 100644 index 000000000..ec8529bdc Binary files /dev/null and b/pcsx2-qt/resources/icons/black/48/screenshot-2-line.png differ diff --git a/pcsx2-qt/resources/icons/black/48/sd-card-line.png b/pcsx2-qt/resources/icons/black/48/sd-card-line.png new file mode 100644 index 000000000..dbbf47126 Binary files /dev/null and b/pcsx2-qt/resources/icons/black/48/sd-card-line.png differ diff --git a/pcsx2-qt/resources/icons/black/48/settings-3-line.png b/pcsx2-qt/resources/icons/black/48/settings-3-line.png new file mode 100644 index 000000000..30d9fe794 Binary files /dev/null and b/pcsx2-qt/resources/icons/black/48/settings-3-line.png differ diff --git a/pcsx2-qt/resources/icons/black/48/shut-down-line.png b/pcsx2-qt/resources/icons/black/48/shut-down-line.png new file mode 100644 index 000000000..3016d0f07 Binary files /dev/null and b/pcsx2-qt/resources/icons/black/48/shut-down-line.png differ diff --git a/pcsx2-qt/resources/icons/black/48/volume-up-line.png b/pcsx2-qt/resources/icons/black/48/volume-up-line.png new file mode 100644 index 000000000..9e2f13537 Binary files /dev/null and b/pcsx2-qt/resources/icons/black/48/volume-up-line.png differ diff --git a/pcsx2-qt/resources/icons/black/48/window-2-line.png b/pcsx2-qt/resources/icons/black/48/window-2-line.png new file mode 100644 index 000000000..7e44963b4 Binary files /dev/null and b/pcsx2-qt/resources/icons/black/48/window-2-line.png differ diff --git a/pcsx2-qt/resources/icons/black/64/artboard-2-line.png b/pcsx2-qt/resources/icons/black/64/artboard-2-line.png new file mode 100644 index 000000000..6716c8ea4 Binary files /dev/null and b/pcsx2-qt/resources/icons/black/64/artboard-2-line.png differ diff --git a/pcsx2-qt/resources/icons/black/64/book-open-line.png b/pcsx2-qt/resources/icons/black/64/book-open-line.png new file mode 100644 index 000000000..89bc72275 Binary files /dev/null and b/pcsx2-qt/resources/icons/black/64/book-open-line.png differ diff --git a/pcsx2-qt/resources/icons/black/64/brush-line.png b/pcsx2-qt/resources/icons/black/64/brush-line.png new file mode 100644 index 000000000..f2cd41e14 Binary files /dev/null and b/pcsx2-qt/resources/icons/black/64/brush-line.png differ diff --git a/pcsx2-qt/resources/icons/black/64/close-line.png b/pcsx2-qt/resources/icons/black/64/close-line.png new file mode 100644 index 000000000..31d74e842 Binary files /dev/null and b/pcsx2-qt/resources/icons/black/64/close-line.png differ diff --git a/pcsx2-qt/resources/icons/black/64/dashboard-line.png b/pcsx2-qt/resources/icons/black/64/dashboard-line.png new file mode 100644 index 000000000..6fdafb77d Binary files /dev/null and b/pcsx2-qt/resources/icons/black/64/dashboard-line.png differ diff --git a/pcsx2-qt/resources/icons/black/64/disc-line.png b/pcsx2-qt/resources/icons/black/64/disc-line.png new file mode 100644 index 000000000..7665568ce Binary files /dev/null and b/pcsx2-qt/resources/icons/black/64/disc-line.png differ diff --git a/pcsx2-qt/resources/icons/black/64/door-open-line.png b/pcsx2-qt/resources/icons/black/64/door-open-line.png new file mode 100644 index 000000000..7bc01ed7f Binary files /dev/null and b/pcsx2-qt/resources/icons/black/64/door-open-line.png differ diff --git a/pcsx2-qt/resources/icons/black/64/download-2-line.png b/pcsx2-qt/resources/icons/black/64/download-2-line.png new file mode 100644 index 000000000..3a50a4e69 Binary files /dev/null and b/pcsx2-qt/resources/icons/black/64/download-2-line.png differ diff --git a/pcsx2-qt/resources/icons/black/64/dvd-line.png b/pcsx2-qt/resources/icons/black/64/dvd-line.png new file mode 100644 index 000000000..151c94634 Binary files /dev/null and b/pcsx2-qt/resources/icons/black/64/dvd-line.png differ diff --git a/pcsx2-qt/resources/icons/black/64/file-add-line.png b/pcsx2-qt/resources/icons/black/64/file-add-line.png new file mode 100644 index 000000000..93bcecffa Binary files /dev/null and b/pcsx2-qt/resources/icons/black/64/file-add-line.png differ diff --git a/pcsx2-qt/resources/icons/black/64/file-line.png b/pcsx2-qt/resources/icons/black/64/file-line.png new file mode 100644 index 000000000..6eb9b95c0 Binary files /dev/null and b/pcsx2-qt/resources/icons/black/64/file-line.png differ diff --git a/pcsx2-qt/resources/icons/black/64/file-list-line.png b/pcsx2-qt/resources/icons/black/64/file-list-line.png new file mode 100644 index 000000000..2ad3caaac Binary files /dev/null and b/pcsx2-qt/resources/icons/black/64/file-list-line.png differ diff --git a/pcsx2-qt/resources/icons/black/64/file-reduce-line.png b/pcsx2-qt/resources/icons/black/64/file-reduce-line.png new file mode 100644 index 000000000..5ea561223 Binary files /dev/null and b/pcsx2-qt/resources/icons/black/64/file-reduce-line.png differ diff --git a/pcsx2-qt/resources/icons/black/64/file-search-line.png b/pcsx2-qt/resources/icons/black/64/file-search-line.png new file mode 100644 index 000000000..a90325437 Binary files /dev/null and b/pcsx2-qt/resources/icons/black/64/file-search-line.png differ diff --git a/pcsx2-qt/resources/icons/black/64/file-settings-line.png b/pcsx2-qt/resources/icons/black/64/file-settings-line.png new file mode 100644 index 000000000..d8555e174 Binary files /dev/null and b/pcsx2-qt/resources/icons/black/64/file-settings-line.png differ diff --git a/pcsx2-qt/resources/icons/black/64/flask-line.png b/pcsx2-qt/resources/icons/black/64/flask-line.png new file mode 100644 index 000000000..1c2f2435c Binary files /dev/null and b/pcsx2-qt/resources/icons/black/64/flask-line.png differ diff --git a/pcsx2-qt/resources/icons/black/64/folder-add-line.png b/pcsx2-qt/resources/icons/black/64/folder-add-line.png new file mode 100644 index 000000000..6b55bba69 Binary files /dev/null and b/pcsx2-qt/resources/icons/black/64/folder-add-line.png differ diff --git a/pcsx2-qt/resources/icons/black/64/folder-open-line.png b/pcsx2-qt/resources/icons/black/64/folder-open-line.png new file mode 100644 index 000000000..0cf54dd6d Binary files /dev/null and b/pcsx2-qt/resources/icons/black/64/folder-open-line.png differ diff --git a/pcsx2-qt/resources/icons/black/64/folder-reduce-line.png b/pcsx2-qt/resources/icons/black/64/folder-reduce-line.png new file mode 100644 index 000000000..9d70dea9b Binary files /dev/null and b/pcsx2-qt/resources/icons/black/64/folder-reduce-line.png differ diff --git a/pcsx2-qt/resources/icons/black/64/folder-settings-line.png b/pcsx2-qt/resources/icons/black/64/folder-settings-line.png new file mode 100644 index 000000000..35f46d28d Binary files /dev/null and b/pcsx2-qt/resources/icons/black/64/folder-settings-line.png differ diff --git a/pcsx2-qt/resources/icons/black/64/fullscreen-line.png b/pcsx2-qt/resources/icons/black/64/fullscreen-line.png new file mode 100644 index 000000000..37e6f14e7 Binary files /dev/null and b/pcsx2-qt/resources/icons/black/64/fullscreen-line.png differ diff --git a/pcsx2-qt/resources/icons/black/64/function-line.png b/pcsx2-qt/resources/icons/black/64/function-line.png new file mode 100644 index 000000000..2421d007e Binary files /dev/null and b/pcsx2-qt/resources/icons/black/64/function-line.png differ diff --git a/pcsx2-qt/resources/icons/black/64/gamepad-line.png b/pcsx2-qt/resources/icons/black/64/gamepad-line.png new file mode 100644 index 000000000..e7e6c698c Binary files /dev/null and b/pcsx2-qt/resources/icons/black/64/gamepad-line.png differ diff --git a/pcsx2-qt/resources/icons/black/64/hard-drive-2-line.png b/pcsx2-qt/resources/icons/black/64/hard-drive-2-line.png new file mode 100644 index 000000000..94ede84d2 Binary files /dev/null and b/pcsx2-qt/resources/icons/black/64/hard-drive-2-line.png differ diff --git a/pcsx2-qt/resources/icons/black/64/keyboard-line.png b/pcsx2-qt/resources/icons/black/64/keyboard-line.png new file mode 100644 index 000000000..4139c9566 Binary files /dev/null and b/pcsx2-qt/resources/icons/black/64/keyboard-line.png differ diff --git a/pcsx2-qt/resources/icons/black/64/layout-grid-line.png b/pcsx2-qt/resources/icons/black/64/layout-grid-line.png new file mode 100644 index 000000000..2e4cb8d7f Binary files /dev/null and b/pcsx2-qt/resources/icons/black/64/layout-grid-line.png differ diff --git a/pcsx2-qt/resources/icons/black/64/list-check.png b/pcsx2-qt/resources/icons/black/64/list-check.png new file mode 100644 index 000000000..d5973507c Binary files /dev/null and b/pcsx2-qt/resources/icons/black/64/list-check.png differ diff --git a/pcsx2-qt/resources/icons/black/64/pause-line.png b/pcsx2-qt/resources/icons/black/64/pause-line.png new file mode 100644 index 000000000..01b32a30b Binary files /dev/null and b/pcsx2-qt/resources/icons/black/64/pause-line.png differ diff --git a/pcsx2-qt/resources/icons/black/64/play-line.png b/pcsx2-qt/resources/icons/black/64/play-line.png new file mode 100644 index 000000000..282d9c27d Binary files /dev/null and b/pcsx2-qt/resources/icons/black/64/play-line.png differ diff --git a/pcsx2-qt/resources/icons/black/64/refresh-line.png b/pcsx2-qt/resources/icons/black/64/refresh-line.png new file mode 100644 index 000000000..cc8e735e2 Binary files /dev/null and b/pcsx2-qt/resources/icons/black/64/refresh-line.png differ diff --git a/pcsx2-qt/resources/icons/black/64/restart-line.png b/pcsx2-qt/resources/icons/black/64/restart-line.png new file mode 100644 index 000000000..fa2894c7e Binary files /dev/null and b/pcsx2-qt/resources/icons/black/64/restart-line.png differ diff --git a/pcsx2-qt/resources/icons/black/64/save-3-line.png b/pcsx2-qt/resources/icons/black/64/save-3-line.png new file mode 100644 index 000000000..c6687e8cd Binary files /dev/null and b/pcsx2-qt/resources/icons/black/64/save-3-line.png differ diff --git a/pcsx2-qt/resources/icons/black/64/screenshot-2-line.png b/pcsx2-qt/resources/icons/black/64/screenshot-2-line.png new file mode 100644 index 000000000..ef9c1d391 Binary files /dev/null and b/pcsx2-qt/resources/icons/black/64/screenshot-2-line.png differ diff --git a/pcsx2-qt/resources/icons/black/64/sd-card-line.png b/pcsx2-qt/resources/icons/black/64/sd-card-line.png new file mode 100644 index 000000000..8ba054fb2 Binary files /dev/null and b/pcsx2-qt/resources/icons/black/64/sd-card-line.png differ diff --git a/pcsx2-qt/resources/icons/black/64/settings-3-line.png b/pcsx2-qt/resources/icons/black/64/settings-3-line.png new file mode 100644 index 000000000..d4e5e4b73 Binary files /dev/null and b/pcsx2-qt/resources/icons/black/64/settings-3-line.png differ diff --git a/pcsx2-qt/resources/icons/black/64/shut-down-line.png b/pcsx2-qt/resources/icons/black/64/shut-down-line.png new file mode 100644 index 000000000..ef2f0af3a Binary files /dev/null and b/pcsx2-qt/resources/icons/black/64/shut-down-line.png differ diff --git a/pcsx2-qt/resources/icons/black/64/tv-2-line.png b/pcsx2-qt/resources/icons/black/64/tv-2-line.png new file mode 100644 index 000000000..36f1eea19 Binary files /dev/null and b/pcsx2-qt/resources/icons/black/64/tv-2-line.png differ diff --git a/pcsx2-qt/resources/icons/black/64/volume-up-line.png b/pcsx2-qt/resources/icons/black/64/volume-up-line.png new file mode 100644 index 000000000..1b0f472ca Binary files /dev/null and b/pcsx2-qt/resources/icons/black/64/volume-up-line.png differ diff --git a/pcsx2-qt/resources/icons/black/64/window-2-line.png b/pcsx2-qt/resources/icons/black/64/window-2-line.png new file mode 100644 index 000000000..599967840 Binary files /dev/null and b/pcsx2-qt/resources/icons/black/64/window-2-line.png differ diff --git a/pcsx2-qt/resources/icons/black/index.theme b/pcsx2-qt/resources/icons/black/index.theme new file mode 100644 index 000000000..97fdbceaf --- /dev/null +++ b/pcsx2-qt/resources/icons/black/index.theme @@ -0,0 +1,15 @@ +[Icon Theme] +Name=Black Icon Theme +Comment=Black Icon Theme + +[16] +Size=16 +Type=Fixed + +[32] +Size=32 +Type=Fixed + +[64] +Size=64 +Type=Fixed diff --git a/pcsx2-qt/resources/icons/discord.png b/pcsx2-qt/resources/icons/discord.png new file mode 100644 index 000000000..e3ba8fddf Binary files /dev/null and b/pcsx2-qt/resources/icons/discord.png differ diff --git a/pcsx2-qt/resources/icons/flag-eu.png b/pcsx2-qt/resources/icons/flag-eu.png new file mode 100644 index 000000000..564a73267 Binary files /dev/null and b/pcsx2-qt/resources/icons/flag-eu.png differ diff --git a/pcsx2-qt/resources/icons/flag-eu.svg b/pcsx2-qt/resources/icons/flag-eu.svg new file mode 100644 index 000000000..a605f684e --- /dev/null +++ b/pcsx2-qt/resources/icons/flag-eu.svg @@ -0,0 +1,31 @@ + + + +European Union flag + + + + + + + + + + + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/pcsx2-qt/resources/icons/flag-eu@2x.png b/pcsx2-qt/resources/icons/flag-eu@2x.png new file mode 100644 index 000000000..b2c0e2eaf Binary files /dev/null and b/pcsx2-qt/resources/icons/flag-eu@2x.png differ diff --git a/pcsx2-qt/resources/icons/flag-jp.png b/pcsx2-qt/resources/icons/flag-jp.png new file mode 100644 index 000000000..7a2502d9a Binary files /dev/null and b/pcsx2-qt/resources/icons/flag-jp.png differ diff --git a/pcsx2-qt/resources/icons/flag-jp.svg b/pcsx2-qt/resources/icons/flag-jp.svg new file mode 100644 index 000000000..5b83bd738 --- /dev/null +++ b/pcsx2-qt/resources/icons/flag-jp.svg @@ -0,0 +1,6 @@ + + +Japanese flag + + + \ No newline at end of file diff --git a/pcsx2-qt/resources/icons/flag-jp@2x.png b/pcsx2-qt/resources/icons/flag-jp@2x.png new file mode 100644 index 000000000..4a064d6bc Binary files /dev/null and b/pcsx2-qt/resources/icons/flag-jp@2x.png differ diff --git a/pcsx2-qt/resources/icons/flag-other.png b/pcsx2-qt/resources/icons/flag-other.png new file mode 100644 index 000000000..fe5210eea Binary files /dev/null and b/pcsx2-qt/resources/icons/flag-other.png differ diff --git a/pcsx2-qt/resources/icons/flag-other@2x.png b/pcsx2-qt/resources/icons/flag-other@2x.png new file mode 100644 index 000000000..0235da45b Binary files /dev/null and b/pcsx2-qt/resources/icons/flag-other@2x.png differ diff --git a/pcsx2-qt/resources/icons/flag-us.png b/pcsx2-qt/resources/icons/flag-us.png new file mode 100644 index 000000000..8c18142a9 Binary files /dev/null and b/pcsx2-qt/resources/icons/flag-us.png differ diff --git a/pcsx2-qt/resources/icons/flag-us.svg b/pcsx2-qt/resources/icons/flag-us.svg new file mode 100644 index 000000000..93a01e5c6 --- /dev/null +++ b/pcsx2-qt/resources/icons/flag-us.svg @@ -0,0 +1,28 @@ + + +United States flag, reworked as 2:3. This involved shrinking the stripes from the right until the flag was the right aspect, then shrinking the canton horizontally so that it only occupied 1/2 of the flag's width, then transposing the stars so that they were centered within the canton. + + + + + + + + + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/pcsx2-qt/resources/icons/flag-us@2x.png b/pcsx2-qt/resources/icons/flag-us@2x.png new file mode 100644 index 000000000..835db3a9a Binary files /dev/null and b/pcsx2-qt/resources/icons/flag-us@2x.png differ diff --git a/pcsx2-qt/resources/icons/logo.png b/pcsx2-qt/resources/icons/logo.png new file mode 100644 index 000000000..f715dd5c4 Binary files /dev/null and b/pcsx2-qt/resources/icons/logo.png differ diff --git a/pcsx2-qt/resources/icons/media-optical-24.png b/pcsx2-qt/resources/icons/media-optical-24.png new file mode 100644 index 000000000..db43e1f1e Binary files /dev/null and b/pcsx2-qt/resources/icons/media-optical-24.png differ diff --git a/pcsx2-qt/resources/icons/media-optical-24@2x.png b/pcsx2-qt/resources/icons/media-optical-24@2x.png new file mode 100644 index 000000000..a2eba48eb Binary files /dev/null and b/pcsx2-qt/resources/icons/media-optical-24@2x.png differ diff --git a/pcsx2-qt/resources/icons/media-optical-gear-24.png b/pcsx2-qt/resources/icons/media-optical-gear-24.png new file mode 100644 index 000000000..d71590d63 Binary files /dev/null and b/pcsx2-qt/resources/icons/media-optical-gear-24.png differ diff --git a/pcsx2-qt/resources/icons/media-optical-gear-24@2x.png b/pcsx2-qt/resources/icons/media-optical-gear-24@2x.png new file mode 100644 index 000000000..87b9266a5 Binary files /dev/null and b/pcsx2-qt/resources/icons/media-optical-gear-24@2x.png differ diff --git a/pcsx2-qt/resources/icons/media-optical.png b/pcsx2-qt/resources/icons/media-optical.png new file mode 100644 index 000000000..bc001cc21 Binary files /dev/null and b/pcsx2-qt/resources/icons/media-optical.png differ diff --git a/pcsx2-qt/resources/icons/media-optical@2x.png b/pcsx2-qt/resources/icons/media-optical@2x.png new file mode 100644 index 000000000..1c7a8f2fd Binary files /dev/null and b/pcsx2-qt/resources/icons/media-optical@2x.png differ diff --git a/pcsx2-qt/resources/icons/star-0.png b/pcsx2-qt/resources/icons/star-0.png new file mode 100644 index 000000000..9e45aab1e Binary files /dev/null and b/pcsx2-qt/resources/icons/star-0.png differ diff --git a/pcsx2-qt/resources/icons/star-1.png b/pcsx2-qt/resources/icons/star-1.png new file mode 100644 index 000000000..fefbdd7ed Binary files /dev/null and b/pcsx2-qt/resources/icons/star-1.png differ diff --git a/pcsx2-qt/resources/icons/star-2.png b/pcsx2-qt/resources/icons/star-2.png new file mode 100644 index 000000000..e206cc6c0 Binary files /dev/null and b/pcsx2-qt/resources/icons/star-2.png differ diff --git a/pcsx2-qt/resources/icons/star-3.png b/pcsx2-qt/resources/icons/star-3.png new file mode 100644 index 000000000..3b924f340 Binary files /dev/null and b/pcsx2-qt/resources/icons/star-3.png differ diff --git a/pcsx2-qt/resources/icons/star-4.png b/pcsx2-qt/resources/icons/star-4.png new file mode 100644 index 000000000..f02a46a60 Binary files /dev/null and b/pcsx2-qt/resources/icons/star-4.png differ diff --git a/pcsx2-qt/resources/icons/star-5.png b/pcsx2-qt/resources/icons/star-5.png new file mode 100644 index 000000000..f434bd907 Binary files /dev/null and b/pcsx2-qt/resources/icons/star-5.png differ diff --git a/pcsx2-qt/resources/icons/white/16/artboard-2-line.png b/pcsx2-qt/resources/icons/white/16/artboard-2-line.png new file mode 100644 index 000000000..e9e2adca1 Binary files /dev/null and b/pcsx2-qt/resources/icons/white/16/artboard-2-line.png differ diff --git a/pcsx2-qt/resources/icons/white/16/book-open-line.png b/pcsx2-qt/resources/icons/white/16/book-open-line.png new file mode 100644 index 000000000..577c7bf5f Binary files /dev/null and b/pcsx2-qt/resources/icons/white/16/book-open-line.png differ diff --git a/pcsx2-qt/resources/icons/white/16/brush-line.png b/pcsx2-qt/resources/icons/white/16/brush-line.png new file mode 100644 index 000000000..aa9ae8ed8 Binary files /dev/null and b/pcsx2-qt/resources/icons/white/16/brush-line.png differ diff --git a/pcsx2-qt/resources/icons/white/16/close-line.png b/pcsx2-qt/resources/icons/white/16/close-line.png new file mode 100644 index 000000000..cb63ff3c2 Binary files /dev/null and b/pcsx2-qt/resources/icons/white/16/close-line.png differ diff --git a/pcsx2-qt/resources/icons/white/16/dashboard-line.png b/pcsx2-qt/resources/icons/white/16/dashboard-line.png new file mode 100644 index 000000000..0bf62bd07 Binary files /dev/null and b/pcsx2-qt/resources/icons/white/16/dashboard-line.png differ diff --git a/pcsx2-qt/resources/icons/white/16/disc-line.png b/pcsx2-qt/resources/icons/white/16/disc-line.png new file mode 100644 index 000000000..a3884ca48 Binary files /dev/null and b/pcsx2-qt/resources/icons/white/16/disc-line.png differ diff --git a/pcsx2-qt/resources/icons/white/16/door-open-line.png b/pcsx2-qt/resources/icons/white/16/door-open-line.png new file mode 100644 index 000000000..a841f67b1 Binary files /dev/null and b/pcsx2-qt/resources/icons/white/16/door-open-line.png differ diff --git a/pcsx2-qt/resources/icons/white/16/download-2-line.png b/pcsx2-qt/resources/icons/white/16/download-2-line.png new file mode 100644 index 000000000..d5c0d75ca Binary files /dev/null and b/pcsx2-qt/resources/icons/white/16/download-2-line.png differ diff --git a/pcsx2-qt/resources/icons/white/16/dvd-line.png b/pcsx2-qt/resources/icons/white/16/dvd-line.png new file mode 100644 index 000000000..34fc22986 Binary files /dev/null and b/pcsx2-qt/resources/icons/white/16/dvd-line.png differ diff --git a/pcsx2-qt/resources/icons/white/16/file-add-line.png b/pcsx2-qt/resources/icons/white/16/file-add-line.png new file mode 100644 index 000000000..bc3576b85 Binary files /dev/null and b/pcsx2-qt/resources/icons/white/16/file-add-line.png differ diff --git a/pcsx2-qt/resources/icons/white/16/file-line.png b/pcsx2-qt/resources/icons/white/16/file-line.png new file mode 100644 index 000000000..6c8a23a3f Binary files /dev/null and b/pcsx2-qt/resources/icons/white/16/file-line.png differ diff --git a/pcsx2-qt/resources/icons/white/16/file-list-line.png b/pcsx2-qt/resources/icons/white/16/file-list-line.png new file mode 100644 index 000000000..8242ca999 Binary files /dev/null and b/pcsx2-qt/resources/icons/white/16/file-list-line.png differ diff --git a/pcsx2-qt/resources/icons/white/16/file-reduce-line.png b/pcsx2-qt/resources/icons/white/16/file-reduce-line.png new file mode 100644 index 000000000..0e5b22366 Binary files /dev/null and b/pcsx2-qt/resources/icons/white/16/file-reduce-line.png differ diff --git a/pcsx2-qt/resources/icons/white/16/file-search-line.png b/pcsx2-qt/resources/icons/white/16/file-search-line.png new file mode 100644 index 000000000..be4bfe33c Binary files /dev/null and b/pcsx2-qt/resources/icons/white/16/file-search-line.png differ diff --git a/pcsx2-qt/resources/icons/white/16/file-settings-line.png b/pcsx2-qt/resources/icons/white/16/file-settings-line.png new file mode 100644 index 000000000..0c3fc1766 Binary files /dev/null and b/pcsx2-qt/resources/icons/white/16/file-settings-line.png differ diff --git a/pcsx2-qt/resources/icons/white/16/flask-line.png b/pcsx2-qt/resources/icons/white/16/flask-line.png new file mode 100644 index 000000000..faeedd8a6 Binary files /dev/null and b/pcsx2-qt/resources/icons/white/16/flask-line.png differ diff --git a/pcsx2-qt/resources/icons/white/16/folder-add-line.png b/pcsx2-qt/resources/icons/white/16/folder-add-line.png new file mode 100644 index 000000000..e884216dd Binary files /dev/null and b/pcsx2-qt/resources/icons/white/16/folder-add-line.png differ diff --git a/pcsx2-qt/resources/icons/white/16/folder-open-line.png b/pcsx2-qt/resources/icons/white/16/folder-open-line.png new file mode 100644 index 000000000..1dcd02d6e Binary files /dev/null and b/pcsx2-qt/resources/icons/white/16/folder-open-line.png differ diff --git a/pcsx2-qt/resources/icons/white/16/folder-reduce-line.png b/pcsx2-qt/resources/icons/white/16/folder-reduce-line.png new file mode 100644 index 000000000..02dfd47ff Binary files /dev/null and b/pcsx2-qt/resources/icons/white/16/folder-reduce-line.png differ diff --git a/pcsx2-qt/resources/icons/white/16/folder-settings-line.png b/pcsx2-qt/resources/icons/white/16/folder-settings-line.png new file mode 100644 index 000000000..9f700309c Binary files /dev/null and b/pcsx2-qt/resources/icons/white/16/folder-settings-line.png differ diff --git a/pcsx2-qt/resources/icons/white/16/fullscreen-line.png b/pcsx2-qt/resources/icons/white/16/fullscreen-line.png new file mode 100644 index 000000000..90d693621 Binary files /dev/null and b/pcsx2-qt/resources/icons/white/16/fullscreen-line.png differ diff --git a/pcsx2-qt/resources/icons/white/16/function-line.png b/pcsx2-qt/resources/icons/white/16/function-line.png new file mode 100644 index 000000000..4d3269d1a Binary files /dev/null and b/pcsx2-qt/resources/icons/white/16/function-line.png differ diff --git a/pcsx2-qt/resources/icons/white/16/gamepad-line.png b/pcsx2-qt/resources/icons/white/16/gamepad-line.png new file mode 100644 index 000000000..57dd85521 Binary files /dev/null and b/pcsx2-qt/resources/icons/white/16/gamepad-line.png differ diff --git a/pcsx2-qt/resources/icons/white/16/hard-drive-2-line.png b/pcsx2-qt/resources/icons/white/16/hard-drive-2-line.png new file mode 100644 index 000000000..a81f471a8 Binary files /dev/null and b/pcsx2-qt/resources/icons/white/16/hard-drive-2-line.png differ diff --git a/pcsx2-qt/resources/icons/white/16/keyboard-line.png b/pcsx2-qt/resources/icons/white/16/keyboard-line.png new file mode 100644 index 000000000..f71d6c100 Binary files /dev/null and b/pcsx2-qt/resources/icons/white/16/keyboard-line.png differ diff --git a/pcsx2-qt/resources/icons/white/16/layout-grid-line.png b/pcsx2-qt/resources/icons/white/16/layout-grid-line.png new file mode 100644 index 000000000..4185e61ad Binary files /dev/null and b/pcsx2-qt/resources/icons/white/16/layout-grid-line.png differ diff --git a/pcsx2-qt/resources/icons/white/16/list-check.png b/pcsx2-qt/resources/icons/white/16/list-check.png new file mode 100644 index 000000000..0a3e59001 Binary files /dev/null and b/pcsx2-qt/resources/icons/white/16/list-check.png differ diff --git a/pcsx2-qt/resources/icons/white/16/pause-line.png b/pcsx2-qt/resources/icons/white/16/pause-line.png new file mode 100644 index 000000000..080c48b31 Binary files /dev/null and b/pcsx2-qt/resources/icons/white/16/pause-line.png differ diff --git a/pcsx2-qt/resources/icons/white/16/play-line.png b/pcsx2-qt/resources/icons/white/16/play-line.png new file mode 100644 index 000000000..4bd91ac10 Binary files /dev/null and b/pcsx2-qt/resources/icons/white/16/play-line.png differ diff --git a/pcsx2-qt/resources/icons/white/16/refresh-line.png b/pcsx2-qt/resources/icons/white/16/refresh-line.png new file mode 100644 index 000000000..5c8b0672c Binary files /dev/null and b/pcsx2-qt/resources/icons/white/16/refresh-line.png differ diff --git a/pcsx2-qt/resources/icons/white/16/restart-line.png b/pcsx2-qt/resources/icons/white/16/restart-line.png new file mode 100644 index 000000000..3bbce0fb2 Binary files /dev/null and b/pcsx2-qt/resources/icons/white/16/restart-line.png differ diff --git a/pcsx2-qt/resources/icons/white/16/save-3-line.png b/pcsx2-qt/resources/icons/white/16/save-3-line.png new file mode 100644 index 000000000..a729f2de3 Binary files /dev/null and b/pcsx2-qt/resources/icons/white/16/save-3-line.png differ diff --git a/pcsx2-qt/resources/icons/white/16/screenshot-2-line.png b/pcsx2-qt/resources/icons/white/16/screenshot-2-line.png new file mode 100644 index 000000000..0b3b940ea Binary files /dev/null and b/pcsx2-qt/resources/icons/white/16/screenshot-2-line.png differ diff --git a/pcsx2-qt/resources/icons/white/16/sd-card-line.png b/pcsx2-qt/resources/icons/white/16/sd-card-line.png new file mode 100644 index 000000000..f3795efb5 Binary files /dev/null and b/pcsx2-qt/resources/icons/white/16/sd-card-line.png differ diff --git a/pcsx2-qt/resources/icons/white/16/settings-3-line.png b/pcsx2-qt/resources/icons/white/16/settings-3-line.png new file mode 100644 index 000000000..fc3e6d389 Binary files /dev/null and b/pcsx2-qt/resources/icons/white/16/settings-3-line.png differ diff --git a/pcsx2-qt/resources/icons/white/16/shut-down-line.png b/pcsx2-qt/resources/icons/white/16/shut-down-line.png new file mode 100644 index 000000000..4c12ac070 Binary files /dev/null and b/pcsx2-qt/resources/icons/white/16/shut-down-line.png differ diff --git a/pcsx2-qt/resources/icons/white/16/tv-2-line.png b/pcsx2-qt/resources/icons/white/16/tv-2-line.png new file mode 100644 index 000000000..ea2329e85 Binary files /dev/null and b/pcsx2-qt/resources/icons/white/16/tv-2-line.png differ diff --git a/pcsx2-qt/resources/icons/white/16/volume-up-line.png b/pcsx2-qt/resources/icons/white/16/volume-up-line.png new file mode 100644 index 000000000..fd8f211cd Binary files /dev/null and b/pcsx2-qt/resources/icons/white/16/volume-up-line.png differ diff --git a/pcsx2-qt/resources/icons/white/16/window-2-line.png b/pcsx2-qt/resources/icons/white/16/window-2-line.png new file mode 100644 index 000000000..f8f21a1c7 Binary files /dev/null and b/pcsx2-qt/resources/icons/white/16/window-2-line.png differ diff --git a/pcsx2-qt/resources/icons/white/24/artboard-2-line.png b/pcsx2-qt/resources/icons/white/24/artboard-2-line.png new file mode 100644 index 000000000..4aaeea1dc Binary files /dev/null and b/pcsx2-qt/resources/icons/white/24/artboard-2-line.png differ diff --git a/pcsx2-qt/resources/icons/white/24/book-open-line.png b/pcsx2-qt/resources/icons/white/24/book-open-line.png new file mode 100644 index 000000000..582a7ff99 Binary files /dev/null and b/pcsx2-qt/resources/icons/white/24/book-open-line.png differ diff --git a/pcsx2-qt/resources/icons/white/24/brush-line.png b/pcsx2-qt/resources/icons/white/24/brush-line.png new file mode 100644 index 000000000..720c95509 Binary files /dev/null and b/pcsx2-qt/resources/icons/white/24/brush-line.png differ diff --git a/pcsx2-qt/resources/icons/white/24/close-line.png b/pcsx2-qt/resources/icons/white/24/close-line.png new file mode 100644 index 000000000..76dafacb9 Binary files /dev/null and b/pcsx2-qt/resources/icons/white/24/close-line.png differ diff --git a/pcsx2-qt/resources/icons/white/24/dashboard-line.png b/pcsx2-qt/resources/icons/white/24/dashboard-line.png new file mode 100644 index 000000000..b7dff85a8 Binary files /dev/null and b/pcsx2-qt/resources/icons/white/24/dashboard-line.png differ diff --git a/pcsx2-qt/resources/icons/white/24/disc-line.png b/pcsx2-qt/resources/icons/white/24/disc-line.png new file mode 100644 index 000000000..4dcd22043 Binary files /dev/null and b/pcsx2-qt/resources/icons/white/24/disc-line.png differ diff --git a/pcsx2-qt/resources/icons/white/24/door-open-line.png b/pcsx2-qt/resources/icons/white/24/door-open-line.png new file mode 100644 index 000000000..03c6fcd23 Binary files /dev/null and b/pcsx2-qt/resources/icons/white/24/door-open-line.png differ diff --git a/pcsx2-qt/resources/icons/white/24/download-2-line.png b/pcsx2-qt/resources/icons/white/24/download-2-line.png new file mode 100644 index 000000000..2e9cfbe18 Binary files /dev/null and b/pcsx2-qt/resources/icons/white/24/download-2-line.png differ diff --git a/pcsx2-qt/resources/icons/white/24/dvd-line.png b/pcsx2-qt/resources/icons/white/24/dvd-line.png new file mode 100644 index 000000000..866ba7c66 Binary files /dev/null and b/pcsx2-qt/resources/icons/white/24/dvd-line.png differ diff --git a/pcsx2-qt/resources/icons/white/24/file-add-line.png b/pcsx2-qt/resources/icons/white/24/file-add-line.png new file mode 100644 index 000000000..8b09c15a5 Binary files /dev/null and b/pcsx2-qt/resources/icons/white/24/file-add-line.png differ diff --git a/pcsx2-qt/resources/icons/white/24/file-line.png b/pcsx2-qt/resources/icons/white/24/file-line.png new file mode 100644 index 000000000..06c431fb5 Binary files /dev/null and b/pcsx2-qt/resources/icons/white/24/file-line.png differ diff --git a/pcsx2-qt/resources/icons/white/24/file-list-line.png b/pcsx2-qt/resources/icons/white/24/file-list-line.png new file mode 100644 index 000000000..f71e5930e Binary files /dev/null and b/pcsx2-qt/resources/icons/white/24/file-list-line.png differ diff --git a/pcsx2-qt/resources/icons/white/24/file-reduce-line.png b/pcsx2-qt/resources/icons/white/24/file-reduce-line.png new file mode 100644 index 000000000..201738dc3 Binary files /dev/null and b/pcsx2-qt/resources/icons/white/24/file-reduce-line.png differ diff --git a/pcsx2-qt/resources/icons/white/24/file-search-line.png b/pcsx2-qt/resources/icons/white/24/file-search-line.png new file mode 100644 index 000000000..62db1e9dc Binary files /dev/null and b/pcsx2-qt/resources/icons/white/24/file-search-line.png differ diff --git a/pcsx2-qt/resources/icons/white/24/file-settings-line.png b/pcsx2-qt/resources/icons/white/24/file-settings-line.png new file mode 100644 index 000000000..f2ffd9c4f Binary files /dev/null and b/pcsx2-qt/resources/icons/white/24/file-settings-line.png differ diff --git a/pcsx2-qt/resources/icons/white/24/flask-line.png b/pcsx2-qt/resources/icons/white/24/flask-line.png new file mode 100644 index 000000000..9c0517a5a Binary files /dev/null and b/pcsx2-qt/resources/icons/white/24/flask-line.png differ diff --git a/pcsx2-qt/resources/icons/white/24/folder-add-line.png b/pcsx2-qt/resources/icons/white/24/folder-add-line.png new file mode 100644 index 000000000..42939cb78 Binary files /dev/null and b/pcsx2-qt/resources/icons/white/24/folder-add-line.png differ diff --git a/pcsx2-qt/resources/icons/white/24/folder-open-line.png b/pcsx2-qt/resources/icons/white/24/folder-open-line.png new file mode 100644 index 000000000..ac957c67d Binary files /dev/null and b/pcsx2-qt/resources/icons/white/24/folder-open-line.png differ diff --git a/pcsx2-qt/resources/icons/white/24/folder-reduce-line.png b/pcsx2-qt/resources/icons/white/24/folder-reduce-line.png new file mode 100644 index 000000000..ecf578224 Binary files /dev/null and b/pcsx2-qt/resources/icons/white/24/folder-reduce-line.png differ diff --git a/pcsx2-qt/resources/icons/white/24/folder-settings-line.png b/pcsx2-qt/resources/icons/white/24/folder-settings-line.png new file mode 100644 index 000000000..19bbd6fd3 Binary files /dev/null and b/pcsx2-qt/resources/icons/white/24/folder-settings-line.png differ diff --git a/pcsx2-qt/resources/icons/white/24/fullscreen-line.png b/pcsx2-qt/resources/icons/white/24/fullscreen-line.png new file mode 100644 index 000000000..61bfff693 Binary files /dev/null and b/pcsx2-qt/resources/icons/white/24/fullscreen-line.png differ diff --git a/pcsx2-qt/resources/icons/white/24/function-line.png b/pcsx2-qt/resources/icons/white/24/function-line.png new file mode 100644 index 000000000..b44cbbbcf Binary files /dev/null and b/pcsx2-qt/resources/icons/white/24/function-line.png differ diff --git a/pcsx2-qt/resources/icons/white/24/gamepad-line.png b/pcsx2-qt/resources/icons/white/24/gamepad-line.png new file mode 100644 index 000000000..a97e29e94 Binary files /dev/null and b/pcsx2-qt/resources/icons/white/24/gamepad-line.png differ diff --git a/pcsx2-qt/resources/icons/white/24/hard-drive-2-line.png b/pcsx2-qt/resources/icons/white/24/hard-drive-2-line.png new file mode 100644 index 000000000..be9b1ca27 Binary files /dev/null and b/pcsx2-qt/resources/icons/white/24/hard-drive-2-line.png differ diff --git a/pcsx2-qt/resources/icons/white/24/keyboard-line.png b/pcsx2-qt/resources/icons/white/24/keyboard-line.png new file mode 100644 index 000000000..bbac489cd Binary files /dev/null and b/pcsx2-qt/resources/icons/white/24/keyboard-line.png differ diff --git a/pcsx2-qt/resources/icons/white/24/layout-grid-line.png b/pcsx2-qt/resources/icons/white/24/layout-grid-line.png new file mode 100644 index 000000000..93ae9f853 Binary files /dev/null and b/pcsx2-qt/resources/icons/white/24/layout-grid-line.png differ diff --git a/pcsx2-qt/resources/icons/white/24/list-check.png b/pcsx2-qt/resources/icons/white/24/list-check.png new file mode 100644 index 000000000..43fd6cb68 Binary files /dev/null and b/pcsx2-qt/resources/icons/white/24/list-check.png differ diff --git a/pcsx2-qt/resources/icons/white/24/pause-line.png b/pcsx2-qt/resources/icons/white/24/pause-line.png new file mode 100644 index 000000000..82dd074d9 Binary files /dev/null and b/pcsx2-qt/resources/icons/white/24/pause-line.png differ diff --git a/pcsx2-qt/resources/icons/white/24/play-line.png b/pcsx2-qt/resources/icons/white/24/play-line.png new file mode 100644 index 000000000..960845739 Binary files /dev/null and b/pcsx2-qt/resources/icons/white/24/play-line.png differ diff --git a/pcsx2-qt/resources/icons/white/24/refresh-line.png b/pcsx2-qt/resources/icons/white/24/refresh-line.png new file mode 100644 index 000000000..21dd6178c Binary files /dev/null and b/pcsx2-qt/resources/icons/white/24/refresh-line.png differ diff --git a/pcsx2-qt/resources/icons/white/24/restart-line.png b/pcsx2-qt/resources/icons/white/24/restart-line.png new file mode 100644 index 000000000..b48dcb93b Binary files /dev/null and b/pcsx2-qt/resources/icons/white/24/restart-line.png differ diff --git a/pcsx2-qt/resources/icons/white/24/save-3-line.png b/pcsx2-qt/resources/icons/white/24/save-3-line.png new file mode 100644 index 000000000..818fb6e4f Binary files /dev/null and b/pcsx2-qt/resources/icons/white/24/save-3-line.png differ diff --git a/pcsx2-qt/resources/icons/white/24/screenshot-2-line.png b/pcsx2-qt/resources/icons/white/24/screenshot-2-line.png new file mode 100644 index 000000000..5e1a40b56 Binary files /dev/null and b/pcsx2-qt/resources/icons/white/24/screenshot-2-line.png differ diff --git a/pcsx2-qt/resources/icons/white/24/sd-card-line.png b/pcsx2-qt/resources/icons/white/24/sd-card-line.png new file mode 100644 index 000000000..bb409dce2 Binary files /dev/null and b/pcsx2-qt/resources/icons/white/24/sd-card-line.png differ diff --git a/pcsx2-qt/resources/icons/white/24/settings-3-line.png b/pcsx2-qt/resources/icons/white/24/settings-3-line.png new file mode 100644 index 000000000..ceb6b06f4 Binary files /dev/null and b/pcsx2-qt/resources/icons/white/24/settings-3-line.png differ diff --git a/pcsx2-qt/resources/icons/white/24/shut-down-line.png b/pcsx2-qt/resources/icons/white/24/shut-down-line.png new file mode 100644 index 000000000..e584e154a Binary files /dev/null and b/pcsx2-qt/resources/icons/white/24/shut-down-line.png differ diff --git a/pcsx2-qt/resources/icons/white/24/tv-2-line.png b/pcsx2-qt/resources/icons/white/24/tv-2-line.png new file mode 100644 index 000000000..79ad7dfc9 Binary files /dev/null and b/pcsx2-qt/resources/icons/white/24/tv-2-line.png differ diff --git a/pcsx2-qt/resources/icons/white/24/volume-up-line.png b/pcsx2-qt/resources/icons/white/24/volume-up-line.png new file mode 100644 index 000000000..668131369 Binary files /dev/null and b/pcsx2-qt/resources/icons/white/24/volume-up-line.png differ diff --git a/pcsx2-qt/resources/icons/white/24/window-2-line.png b/pcsx2-qt/resources/icons/white/24/window-2-line.png new file mode 100644 index 000000000..79302a0eb Binary files /dev/null and b/pcsx2-qt/resources/icons/white/24/window-2-line.png differ diff --git a/pcsx2-qt/resources/icons/white/32/artboard-2-line.png b/pcsx2-qt/resources/icons/white/32/artboard-2-line.png new file mode 100644 index 000000000..2a36ec60c Binary files /dev/null and b/pcsx2-qt/resources/icons/white/32/artboard-2-line.png differ diff --git a/pcsx2-qt/resources/icons/white/32/book-open-line.png b/pcsx2-qt/resources/icons/white/32/book-open-line.png new file mode 100644 index 000000000..70ae5362d Binary files /dev/null and b/pcsx2-qt/resources/icons/white/32/book-open-line.png differ diff --git a/pcsx2-qt/resources/icons/white/32/brush-line.png b/pcsx2-qt/resources/icons/white/32/brush-line.png new file mode 100644 index 000000000..974876614 Binary files /dev/null and b/pcsx2-qt/resources/icons/white/32/brush-line.png differ diff --git a/pcsx2-qt/resources/icons/white/32/close-line.png b/pcsx2-qt/resources/icons/white/32/close-line.png new file mode 100644 index 000000000..1a698a237 Binary files /dev/null and b/pcsx2-qt/resources/icons/white/32/close-line.png differ diff --git a/pcsx2-qt/resources/icons/white/32/dashboard-line.png b/pcsx2-qt/resources/icons/white/32/dashboard-line.png new file mode 100644 index 000000000..f3e64dfc9 Binary files /dev/null and b/pcsx2-qt/resources/icons/white/32/dashboard-line.png differ diff --git a/pcsx2-qt/resources/icons/white/32/disc-line.png b/pcsx2-qt/resources/icons/white/32/disc-line.png new file mode 100644 index 000000000..ad79b7852 Binary files /dev/null and b/pcsx2-qt/resources/icons/white/32/disc-line.png differ diff --git a/pcsx2-qt/resources/icons/white/32/door-open-line.png b/pcsx2-qt/resources/icons/white/32/door-open-line.png new file mode 100644 index 000000000..681fad54e Binary files /dev/null and b/pcsx2-qt/resources/icons/white/32/door-open-line.png differ diff --git a/pcsx2-qt/resources/icons/white/32/download-2-line.png b/pcsx2-qt/resources/icons/white/32/download-2-line.png new file mode 100644 index 000000000..2ff35fa6d Binary files /dev/null and b/pcsx2-qt/resources/icons/white/32/download-2-line.png differ diff --git a/pcsx2-qt/resources/icons/white/32/dvd-line.png b/pcsx2-qt/resources/icons/white/32/dvd-line.png new file mode 100644 index 000000000..33f4937c7 Binary files /dev/null and b/pcsx2-qt/resources/icons/white/32/dvd-line.png differ diff --git a/pcsx2-qt/resources/icons/white/32/file-add-line.png b/pcsx2-qt/resources/icons/white/32/file-add-line.png new file mode 100644 index 000000000..6ab4efad5 Binary files /dev/null and b/pcsx2-qt/resources/icons/white/32/file-add-line.png differ diff --git a/pcsx2-qt/resources/icons/white/32/file-line.png b/pcsx2-qt/resources/icons/white/32/file-line.png new file mode 100644 index 000000000..0b4d190b4 Binary files /dev/null and b/pcsx2-qt/resources/icons/white/32/file-line.png differ diff --git a/pcsx2-qt/resources/icons/white/32/file-list-line.png b/pcsx2-qt/resources/icons/white/32/file-list-line.png new file mode 100644 index 000000000..815d88eb9 Binary files /dev/null and b/pcsx2-qt/resources/icons/white/32/file-list-line.png differ diff --git a/pcsx2-qt/resources/icons/white/32/file-reduce-line.png b/pcsx2-qt/resources/icons/white/32/file-reduce-line.png new file mode 100644 index 000000000..d3d09e469 Binary files /dev/null and b/pcsx2-qt/resources/icons/white/32/file-reduce-line.png differ diff --git a/pcsx2-qt/resources/icons/white/32/file-search-line.png b/pcsx2-qt/resources/icons/white/32/file-search-line.png new file mode 100644 index 000000000..9b92c22a0 Binary files /dev/null and b/pcsx2-qt/resources/icons/white/32/file-search-line.png differ diff --git a/pcsx2-qt/resources/icons/white/32/file-settings-line.png b/pcsx2-qt/resources/icons/white/32/file-settings-line.png new file mode 100644 index 000000000..13d08d85a Binary files /dev/null and b/pcsx2-qt/resources/icons/white/32/file-settings-line.png differ diff --git a/pcsx2-qt/resources/icons/white/32/flask-line.png b/pcsx2-qt/resources/icons/white/32/flask-line.png new file mode 100644 index 000000000..160bfec24 Binary files /dev/null and b/pcsx2-qt/resources/icons/white/32/flask-line.png differ diff --git a/pcsx2-qt/resources/icons/white/32/folder-add-line.png b/pcsx2-qt/resources/icons/white/32/folder-add-line.png new file mode 100644 index 000000000..df483248e Binary files /dev/null and b/pcsx2-qt/resources/icons/white/32/folder-add-line.png differ diff --git a/pcsx2-qt/resources/icons/white/32/folder-open-line.png b/pcsx2-qt/resources/icons/white/32/folder-open-line.png new file mode 100644 index 000000000..4b96da026 Binary files /dev/null and b/pcsx2-qt/resources/icons/white/32/folder-open-line.png differ diff --git a/pcsx2-qt/resources/icons/white/32/folder-reduce-line.png b/pcsx2-qt/resources/icons/white/32/folder-reduce-line.png new file mode 100644 index 000000000..048295266 Binary files /dev/null and b/pcsx2-qt/resources/icons/white/32/folder-reduce-line.png differ diff --git a/pcsx2-qt/resources/icons/white/32/folder-settings-line.png b/pcsx2-qt/resources/icons/white/32/folder-settings-line.png new file mode 100644 index 000000000..1ab61a81d Binary files /dev/null and b/pcsx2-qt/resources/icons/white/32/folder-settings-line.png differ diff --git a/pcsx2-qt/resources/icons/white/32/fullscreen-line.png b/pcsx2-qt/resources/icons/white/32/fullscreen-line.png new file mode 100644 index 000000000..938d59fc4 Binary files /dev/null and b/pcsx2-qt/resources/icons/white/32/fullscreen-line.png differ diff --git a/pcsx2-qt/resources/icons/white/32/function-line.png b/pcsx2-qt/resources/icons/white/32/function-line.png new file mode 100644 index 000000000..d6cef0acc Binary files /dev/null and b/pcsx2-qt/resources/icons/white/32/function-line.png differ diff --git a/pcsx2-qt/resources/icons/white/32/gamepad-line.png b/pcsx2-qt/resources/icons/white/32/gamepad-line.png new file mode 100644 index 000000000..b8a0b528a Binary files /dev/null and b/pcsx2-qt/resources/icons/white/32/gamepad-line.png differ diff --git a/pcsx2-qt/resources/icons/white/32/hard-drive-2-line.png b/pcsx2-qt/resources/icons/white/32/hard-drive-2-line.png new file mode 100644 index 000000000..5eca6f646 Binary files /dev/null and b/pcsx2-qt/resources/icons/white/32/hard-drive-2-line.png differ diff --git a/pcsx2-qt/resources/icons/white/32/keyboard-line.png b/pcsx2-qt/resources/icons/white/32/keyboard-line.png new file mode 100644 index 000000000..abe10c84f Binary files /dev/null and b/pcsx2-qt/resources/icons/white/32/keyboard-line.png differ diff --git a/pcsx2-qt/resources/icons/white/32/layout-grid-line.png b/pcsx2-qt/resources/icons/white/32/layout-grid-line.png new file mode 100644 index 000000000..a56f809fe Binary files /dev/null and b/pcsx2-qt/resources/icons/white/32/layout-grid-line.png differ diff --git a/pcsx2-qt/resources/icons/white/32/list-check.png b/pcsx2-qt/resources/icons/white/32/list-check.png new file mode 100644 index 000000000..8928e0837 Binary files /dev/null and b/pcsx2-qt/resources/icons/white/32/list-check.png differ diff --git a/pcsx2-qt/resources/icons/white/32/pause-line.png b/pcsx2-qt/resources/icons/white/32/pause-line.png new file mode 100644 index 000000000..36d893c5e Binary files /dev/null and b/pcsx2-qt/resources/icons/white/32/pause-line.png differ diff --git a/pcsx2-qt/resources/icons/white/32/play-line.png b/pcsx2-qt/resources/icons/white/32/play-line.png new file mode 100644 index 000000000..1c6da4e1a Binary files /dev/null and b/pcsx2-qt/resources/icons/white/32/play-line.png differ diff --git a/pcsx2-qt/resources/icons/white/32/refresh-line.png b/pcsx2-qt/resources/icons/white/32/refresh-line.png new file mode 100644 index 000000000..5d3d6895c Binary files /dev/null and b/pcsx2-qt/resources/icons/white/32/refresh-line.png differ diff --git a/pcsx2-qt/resources/icons/white/32/restart-line.png b/pcsx2-qt/resources/icons/white/32/restart-line.png new file mode 100644 index 000000000..4474c4ab4 Binary files /dev/null and b/pcsx2-qt/resources/icons/white/32/restart-line.png differ diff --git a/pcsx2-qt/resources/icons/white/32/save-3-line.png b/pcsx2-qt/resources/icons/white/32/save-3-line.png new file mode 100644 index 000000000..296968465 Binary files /dev/null and b/pcsx2-qt/resources/icons/white/32/save-3-line.png differ diff --git a/pcsx2-qt/resources/icons/white/32/screenshot-2-line.png b/pcsx2-qt/resources/icons/white/32/screenshot-2-line.png new file mode 100644 index 000000000..722a1ab01 Binary files /dev/null and b/pcsx2-qt/resources/icons/white/32/screenshot-2-line.png differ diff --git a/pcsx2-qt/resources/icons/white/32/sd-card-line.png b/pcsx2-qt/resources/icons/white/32/sd-card-line.png new file mode 100644 index 000000000..24c182f2e Binary files /dev/null and b/pcsx2-qt/resources/icons/white/32/sd-card-line.png differ diff --git a/pcsx2-qt/resources/icons/white/32/settings-3-line.png b/pcsx2-qt/resources/icons/white/32/settings-3-line.png new file mode 100644 index 000000000..52e455439 Binary files /dev/null and b/pcsx2-qt/resources/icons/white/32/settings-3-line.png differ diff --git a/pcsx2-qt/resources/icons/white/32/shut-down-line.png b/pcsx2-qt/resources/icons/white/32/shut-down-line.png new file mode 100644 index 000000000..6fcfb7d46 Binary files /dev/null and b/pcsx2-qt/resources/icons/white/32/shut-down-line.png differ diff --git a/pcsx2-qt/resources/icons/white/32/tv-2-line.png b/pcsx2-qt/resources/icons/white/32/tv-2-line.png new file mode 100644 index 000000000..31d2c2638 Binary files /dev/null and b/pcsx2-qt/resources/icons/white/32/tv-2-line.png differ diff --git a/pcsx2-qt/resources/icons/white/32/volume-up-line.png b/pcsx2-qt/resources/icons/white/32/volume-up-line.png new file mode 100644 index 000000000..a308e4b97 Binary files /dev/null and b/pcsx2-qt/resources/icons/white/32/volume-up-line.png differ diff --git a/pcsx2-qt/resources/icons/white/32/window-2-line.png b/pcsx2-qt/resources/icons/white/32/window-2-line.png new file mode 100644 index 000000000..56a2e000e Binary files /dev/null and b/pcsx2-qt/resources/icons/white/32/window-2-line.png differ diff --git a/pcsx2-qt/resources/icons/white/48/artboard-2-line.png b/pcsx2-qt/resources/icons/white/48/artboard-2-line.png new file mode 100644 index 000000000..37bd53a48 Binary files /dev/null and b/pcsx2-qt/resources/icons/white/48/artboard-2-line.png differ diff --git a/pcsx2-qt/resources/icons/white/48/book-open-line.png b/pcsx2-qt/resources/icons/white/48/book-open-line.png new file mode 100644 index 000000000..38b4853de Binary files /dev/null and b/pcsx2-qt/resources/icons/white/48/book-open-line.png differ diff --git a/pcsx2-qt/resources/icons/white/48/brush-line.png b/pcsx2-qt/resources/icons/white/48/brush-line.png new file mode 100644 index 000000000..b082ba6f1 Binary files /dev/null and b/pcsx2-qt/resources/icons/white/48/brush-line.png differ diff --git a/pcsx2-qt/resources/icons/white/48/close-line.png b/pcsx2-qt/resources/icons/white/48/close-line.png new file mode 100644 index 000000000..225c980c5 Binary files /dev/null and b/pcsx2-qt/resources/icons/white/48/close-line.png differ diff --git a/pcsx2-qt/resources/icons/white/48/dashboard-line.png b/pcsx2-qt/resources/icons/white/48/dashboard-line.png new file mode 100644 index 000000000..c8309d142 Binary files /dev/null and b/pcsx2-qt/resources/icons/white/48/dashboard-line.png differ diff --git a/pcsx2-qt/resources/icons/white/48/disc-line.png b/pcsx2-qt/resources/icons/white/48/disc-line.png new file mode 100644 index 000000000..dfe680e27 Binary files /dev/null and b/pcsx2-qt/resources/icons/white/48/disc-line.png differ diff --git a/pcsx2-qt/resources/icons/white/48/door-open-line.png b/pcsx2-qt/resources/icons/white/48/door-open-line.png new file mode 100644 index 000000000..7fcfaceeb Binary files /dev/null and b/pcsx2-qt/resources/icons/white/48/door-open-line.png differ diff --git a/pcsx2-qt/resources/icons/white/48/download-2-line.png b/pcsx2-qt/resources/icons/white/48/download-2-line.png new file mode 100644 index 000000000..4702cae9a Binary files /dev/null and b/pcsx2-qt/resources/icons/white/48/download-2-line.png differ diff --git a/pcsx2-qt/resources/icons/white/48/dvd-line.png b/pcsx2-qt/resources/icons/white/48/dvd-line.png new file mode 100644 index 000000000..5c1c7bada Binary files /dev/null and b/pcsx2-qt/resources/icons/white/48/dvd-line.png differ diff --git a/pcsx2-qt/resources/icons/white/48/file-add-line.png b/pcsx2-qt/resources/icons/white/48/file-add-line.png new file mode 100644 index 000000000..4b71a144c Binary files /dev/null and b/pcsx2-qt/resources/icons/white/48/file-add-line.png differ diff --git a/pcsx2-qt/resources/icons/white/48/file-line.png b/pcsx2-qt/resources/icons/white/48/file-line.png new file mode 100644 index 000000000..9ccd17335 Binary files /dev/null and b/pcsx2-qt/resources/icons/white/48/file-line.png differ diff --git a/pcsx2-qt/resources/icons/white/48/file-list-line.png b/pcsx2-qt/resources/icons/white/48/file-list-line.png new file mode 100644 index 000000000..a935757db Binary files /dev/null and b/pcsx2-qt/resources/icons/white/48/file-list-line.png differ diff --git a/pcsx2-qt/resources/icons/white/48/file-reduce-line.png b/pcsx2-qt/resources/icons/white/48/file-reduce-line.png new file mode 100644 index 000000000..78640a9a5 Binary files /dev/null and b/pcsx2-qt/resources/icons/white/48/file-reduce-line.png differ diff --git a/pcsx2-qt/resources/icons/white/48/file-search-line.png b/pcsx2-qt/resources/icons/white/48/file-search-line.png new file mode 100644 index 000000000..04ded90a8 Binary files /dev/null and b/pcsx2-qt/resources/icons/white/48/file-search-line.png differ diff --git a/pcsx2-qt/resources/icons/white/48/file-settings-line.png b/pcsx2-qt/resources/icons/white/48/file-settings-line.png new file mode 100644 index 000000000..38206fcff Binary files /dev/null and b/pcsx2-qt/resources/icons/white/48/file-settings-line.png differ diff --git a/pcsx2-qt/resources/icons/white/48/flask-line.png b/pcsx2-qt/resources/icons/white/48/flask-line.png new file mode 100644 index 000000000..627f8215c Binary files /dev/null and b/pcsx2-qt/resources/icons/white/48/flask-line.png differ diff --git a/pcsx2-qt/resources/icons/white/48/folder-add-line.png b/pcsx2-qt/resources/icons/white/48/folder-add-line.png new file mode 100644 index 000000000..b7c439011 Binary files /dev/null and b/pcsx2-qt/resources/icons/white/48/folder-add-line.png differ diff --git a/pcsx2-qt/resources/icons/white/48/folder-open-line.png b/pcsx2-qt/resources/icons/white/48/folder-open-line.png new file mode 100644 index 000000000..02da20c8d Binary files /dev/null and b/pcsx2-qt/resources/icons/white/48/folder-open-line.png differ diff --git a/pcsx2-qt/resources/icons/white/48/folder-reduce-line.png b/pcsx2-qt/resources/icons/white/48/folder-reduce-line.png new file mode 100644 index 000000000..4d4a674b2 Binary files /dev/null and b/pcsx2-qt/resources/icons/white/48/folder-reduce-line.png differ diff --git a/pcsx2-qt/resources/icons/white/48/folder-settings-line.png b/pcsx2-qt/resources/icons/white/48/folder-settings-line.png new file mode 100644 index 000000000..e0bd0699c Binary files /dev/null and b/pcsx2-qt/resources/icons/white/48/folder-settings-line.png differ diff --git a/pcsx2-qt/resources/icons/white/48/fullscreen-line.png b/pcsx2-qt/resources/icons/white/48/fullscreen-line.png new file mode 100644 index 000000000..caa75dde0 Binary files /dev/null and b/pcsx2-qt/resources/icons/white/48/fullscreen-line.png differ diff --git a/pcsx2-qt/resources/icons/white/48/function-line.png b/pcsx2-qt/resources/icons/white/48/function-line.png new file mode 100644 index 000000000..46a48cbb5 Binary files /dev/null and b/pcsx2-qt/resources/icons/white/48/function-line.png differ diff --git a/pcsx2-qt/resources/icons/white/48/gamepad-line.png b/pcsx2-qt/resources/icons/white/48/gamepad-line.png new file mode 100644 index 000000000..99a318a43 Binary files /dev/null and b/pcsx2-qt/resources/icons/white/48/gamepad-line.png differ diff --git a/pcsx2-qt/resources/icons/white/48/hard-drive-2-line.png b/pcsx2-qt/resources/icons/white/48/hard-drive-2-line.png new file mode 100644 index 000000000..98ccb55b2 Binary files /dev/null and b/pcsx2-qt/resources/icons/white/48/hard-drive-2-line.png differ diff --git a/pcsx2-qt/resources/icons/white/48/keyboard-line.png b/pcsx2-qt/resources/icons/white/48/keyboard-line.png new file mode 100644 index 000000000..568d5b026 Binary files /dev/null and b/pcsx2-qt/resources/icons/white/48/keyboard-line.png differ diff --git a/pcsx2-qt/resources/icons/white/48/layout-grid-line.png b/pcsx2-qt/resources/icons/white/48/layout-grid-line.png new file mode 100644 index 000000000..e492e600d Binary files /dev/null and b/pcsx2-qt/resources/icons/white/48/layout-grid-line.png differ diff --git a/pcsx2-qt/resources/icons/white/48/list-check.png b/pcsx2-qt/resources/icons/white/48/list-check.png new file mode 100644 index 000000000..8626ccf8c Binary files /dev/null and b/pcsx2-qt/resources/icons/white/48/list-check.png differ diff --git a/pcsx2-qt/resources/icons/white/48/pause-line.png b/pcsx2-qt/resources/icons/white/48/pause-line.png new file mode 100644 index 000000000..f2521a2e9 Binary files /dev/null and b/pcsx2-qt/resources/icons/white/48/pause-line.png differ diff --git a/pcsx2-qt/resources/icons/white/48/play-line.png b/pcsx2-qt/resources/icons/white/48/play-line.png new file mode 100644 index 000000000..96c33efde Binary files /dev/null and b/pcsx2-qt/resources/icons/white/48/play-line.png differ diff --git a/pcsx2-qt/resources/icons/white/48/refresh-line.png b/pcsx2-qt/resources/icons/white/48/refresh-line.png new file mode 100644 index 000000000..ca63f26e7 Binary files /dev/null and b/pcsx2-qt/resources/icons/white/48/refresh-line.png differ diff --git a/pcsx2-qt/resources/icons/white/48/restart-line.png b/pcsx2-qt/resources/icons/white/48/restart-line.png new file mode 100644 index 000000000..b2fe18689 Binary files /dev/null and b/pcsx2-qt/resources/icons/white/48/restart-line.png differ diff --git a/pcsx2-qt/resources/icons/white/48/save-3-line.png b/pcsx2-qt/resources/icons/white/48/save-3-line.png new file mode 100644 index 000000000..9ad14fc7f Binary files /dev/null and b/pcsx2-qt/resources/icons/white/48/save-3-line.png differ diff --git a/pcsx2-qt/resources/icons/white/48/screenshot-2-line.png b/pcsx2-qt/resources/icons/white/48/screenshot-2-line.png new file mode 100644 index 000000000..d96a42126 Binary files /dev/null and b/pcsx2-qt/resources/icons/white/48/screenshot-2-line.png differ diff --git a/pcsx2-qt/resources/icons/white/48/sd-card-line.png b/pcsx2-qt/resources/icons/white/48/sd-card-line.png new file mode 100644 index 000000000..938177af8 Binary files /dev/null and b/pcsx2-qt/resources/icons/white/48/sd-card-line.png differ diff --git a/pcsx2-qt/resources/icons/white/48/settings-3-line.png b/pcsx2-qt/resources/icons/white/48/settings-3-line.png new file mode 100644 index 000000000..6fda0b693 Binary files /dev/null and b/pcsx2-qt/resources/icons/white/48/settings-3-line.png differ diff --git a/pcsx2-qt/resources/icons/white/48/shut-down-line.png b/pcsx2-qt/resources/icons/white/48/shut-down-line.png new file mode 100644 index 000000000..962006d9a Binary files /dev/null and b/pcsx2-qt/resources/icons/white/48/shut-down-line.png differ diff --git a/pcsx2-qt/resources/icons/white/48/tv-2-line.png b/pcsx2-qt/resources/icons/white/48/tv-2-line.png new file mode 100644 index 000000000..35b8b614c Binary files /dev/null and b/pcsx2-qt/resources/icons/white/48/tv-2-line.png differ diff --git a/pcsx2-qt/resources/icons/white/48/volume-up-line.png b/pcsx2-qt/resources/icons/white/48/volume-up-line.png new file mode 100644 index 000000000..74b65c957 Binary files /dev/null and b/pcsx2-qt/resources/icons/white/48/volume-up-line.png differ diff --git a/pcsx2-qt/resources/icons/white/48/window-2-line.png b/pcsx2-qt/resources/icons/white/48/window-2-line.png new file mode 100644 index 000000000..fc836a195 Binary files /dev/null and b/pcsx2-qt/resources/icons/white/48/window-2-line.png differ diff --git a/pcsx2-qt/resources/icons/white/64/artboard-2-line.png b/pcsx2-qt/resources/icons/white/64/artboard-2-line.png new file mode 100644 index 000000000..7230e09a8 Binary files /dev/null and b/pcsx2-qt/resources/icons/white/64/artboard-2-line.png differ diff --git a/pcsx2-qt/resources/icons/white/64/book-open-line.png b/pcsx2-qt/resources/icons/white/64/book-open-line.png new file mode 100644 index 000000000..965594916 Binary files /dev/null and b/pcsx2-qt/resources/icons/white/64/book-open-line.png differ diff --git a/pcsx2-qt/resources/icons/white/64/brush-line.png b/pcsx2-qt/resources/icons/white/64/brush-line.png new file mode 100644 index 000000000..db664b487 Binary files /dev/null and b/pcsx2-qt/resources/icons/white/64/brush-line.png differ diff --git a/pcsx2-qt/resources/icons/white/64/close-line.png b/pcsx2-qt/resources/icons/white/64/close-line.png new file mode 100644 index 000000000..4eb73db92 Binary files /dev/null and b/pcsx2-qt/resources/icons/white/64/close-line.png differ diff --git a/pcsx2-qt/resources/icons/white/64/dashboard-line.png b/pcsx2-qt/resources/icons/white/64/dashboard-line.png new file mode 100644 index 000000000..8e0002c72 Binary files /dev/null and b/pcsx2-qt/resources/icons/white/64/dashboard-line.png differ diff --git a/pcsx2-qt/resources/icons/white/64/disc-line.png b/pcsx2-qt/resources/icons/white/64/disc-line.png new file mode 100644 index 000000000..2ba7c67af Binary files /dev/null and b/pcsx2-qt/resources/icons/white/64/disc-line.png differ diff --git a/pcsx2-qt/resources/icons/white/64/door-open-line.png b/pcsx2-qt/resources/icons/white/64/door-open-line.png new file mode 100644 index 000000000..86c5dca9f Binary files /dev/null and b/pcsx2-qt/resources/icons/white/64/door-open-line.png differ diff --git a/pcsx2-qt/resources/icons/white/64/download-2-line.png b/pcsx2-qt/resources/icons/white/64/download-2-line.png new file mode 100644 index 000000000..2f0e4a528 Binary files /dev/null and b/pcsx2-qt/resources/icons/white/64/download-2-line.png differ diff --git a/pcsx2-qt/resources/icons/white/64/dvd-line.png b/pcsx2-qt/resources/icons/white/64/dvd-line.png new file mode 100644 index 000000000..68f9aea5e Binary files /dev/null and b/pcsx2-qt/resources/icons/white/64/dvd-line.png differ diff --git a/pcsx2-qt/resources/icons/white/64/file-add-line.png b/pcsx2-qt/resources/icons/white/64/file-add-line.png new file mode 100644 index 000000000..c602339d8 Binary files /dev/null and b/pcsx2-qt/resources/icons/white/64/file-add-line.png differ diff --git a/pcsx2-qt/resources/icons/white/64/file-line.png b/pcsx2-qt/resources/icons/white/64/file-line.png new file mode 100644 index 000000000..d9c34f3b4 Binary files /dev/null and b/pcsx2-qt/resources/icons/white/64/file-line.png differ diff --git a/pcsx2-qt/resources/icons/white/64/file-list-line.png b/pcsx2-qt/resources/icons/white/64/file-list-line.png new file mode 100644 index 000000000..66bf942ba Binary files /dev/null and b/pcsx2-qt/resources/icons/white/64/file-list-line.png differ diff --git a/pcsx2-qt/resources/icons/white/64/file-reduce-line.png b/pcsx2-qt/resources/icons/white/64/file-reduce-line.png new file mode 100644 index 000000000..9b4152821 Binary files /dev/null and b/pcsx2-qt/resources/icons/white/64/file-reduce-line.png differ diff --git a/pcsx2-qt/resources/icons/white/64/file-search-line.png b/pcsx2-qt/resources/icons/white/64/file-search-line.png new file mode 100644 index 000000000..1a64dc287 Binary files /dev/null and b/pcsx2-qt/resources/icons/white/64/file-search-line.png differ diff --git a/pcsx2-qt/resources/icons/white/64/file-settings-line.png b/pcsx2-qt/resources/icons/white/64/file-settings-line.png new file mode 100644 index 000000000..9a71b8bbe Binary files /dev/null and b/pcsx2-qt/resources/icons/white/64/file-settings-line.png differ diff --git a/pcsx2-qt/resources/icons/white/64/flask-line.png b/pcsx2-qt/resources/icons/white/64/flask-line.png new file mode 100644 index 000000000..c4c92f952 Binary files /dev/null and b/pcsx2-qt/resources/icons/white/64/flask-line.png differ diff --git a/pcsx2-qt/resources/icons/white/64/folder-add-line.png b/pcsx2-qt/resources/icons/white/64/folder-add-line.png new file mode 100644 index 000000000..adee6602d Binary files /dev/null and b/pcsx2-qt/resources/icons/white/64/folder-add-line.png differ diff --git a/pcsx2-qt/resources/icons/white/64/folder-open-line.png b/pcsx2-qt/resources/icons/white/64/folder-open-line.png new file mode 100644 index 000000000..dd4d88a7a Binary files /dev/null and b/pcsx2-qt/resources/icons/white/64/folder-open-line.png differ diff --git a/pcsx2-qt/resources/icons/white/64/folder-reduce-line.png b/pcsx2-qt/resources/icons/white/64/folder-reduce-line.png new file mode 100644 index 000000000..968fce3a7 Binary files /dev/null and b/pcsx2-qt/resources/icons/white/64/folder-reduce-line.png differ diff --git a/pcsx2-qt/resources/icons/white/64/folder-settings-line.png b/pcsx2-qt/resources/icons/white/64/folder-settings-line.png new file mode 100644 index 000000000..75a4fcf4a Binary files /dev/null and b/pcsx2-qt/resources/icons/white/64/folder-settings-line.png differ diff --git a/pcsx2-qt/resources/icons/white/64/fullscreen-line.png b/pcsx2-qt/resources/icons/white/64/fullscreen-line.png new file mode 100644 index 000000000..448280ae9 Binary files /dev/null and b/pcsx2-qt/resources/icons/white/64/fullscreen-line.png differ diff --git a/pcsx2-qt/resources/icons/white/64/function-line.png b/pcsx2-qt/resources/icons/white/64/function-line.png new file mode 100644 index 000000000..a2b78a543 Binary files /dev/null and b/pcsx2-qt/resources/icons/white/64/function-line.png differ diff --git a/pcsx2-qt/resources/icons/white/64/gamepad-line.png b/pcsx2-qt/resources/icons/white/64/gamepad-line.png new file mode 100644 index 000000000..7e8fbcff3 Binary files /dev/null and b/pcsx2-qt/resources/icons/white/64/gamepad-line.png differ diff --git a/pcsx2-qt/resources/icons/white/64/hard-drive-2-line.png b/pcsx2-qt/resources/icons/white/64/hard-drive-2-line.png new file mode 100644 index 000000000..3c890757f Binary files /dev/null and b/pcsx2-qt/resources/icons/white/64/hard-drive-2-line.png differ diff --git a/pcsx2-qt/resources/icons/white/64/keyboard-line.png b/pcsx2-qt/resources/icons/white/64/keyboard-line.png new file mode 100644 index 000000000..a63fff7a0 Binary files /dev/null and b/pcsx2-qt/resources/icons/white/64/keyboard-line.png differ diff --git a/pcsx2-qt/resources/icons/white/64/layout-grid-line.png b/pcsx2-qt/resources/icons/white/64/layout-grid-line.png new file mode 100644 index 000000000..0ccbf3007 Binary files /dev/null and b/pcsx2-qt/resources/icons/white/64/layout-grid-line.png differ diff --git a/pcsx2-qt/resources/icons/white/64/list-check.png b/pcsx2-qt/resources/icons/white/64/list-check.png new file mode 100644 index 000000000..6aa880395 Binary files /dev/null and b/pcsx2-qt/resources/icons/white/64/list-check.png differ diff --git a/pcsx2-qt/resources/icons/white/64/pause-line.png b/pcsx2-qt/resources/icons/white/64/pause-line.png new file mode 100644 index 000000000..40872537b Binary files /dev/null and b/pcsx2-qt/resources/icons/white/64/pause-line.png differ diff --git a/pcsx2-qt/resources/icons/white/64/play-line.png b/pcsx2-qt/resources/icons/white/64/play-line.png new file mode 100644 index 000000000..2a650f3ff Binary files /dev/null and b/pcsx2-qt/resources/icons/white/64/play-line.png differ diff --git a/pcsx2-qt/resources/icons/white/64/refresh-line.png b/pcsx2-qt/resources/icons/white/64/refresh-line.png new file mode 100644 index 000000000..efc2e1172 Binary files /dev/null and b/pcsx2-qt/resources/icons/white/64/refresh-line.png differ diff --git a/pcsx2-qt/resources/icons/white/64/restart-line.png b/pcsx2-qt/resources/icons/white/64/restart-line.png new file mode 100644 index 000000000..1d99f7bb5 Binary files /dev/null and b/pcsx2-qt/resources/icons/white/64/restart-line.png differ diff --git a/pcsx2-qt/resources/icons/white/64/save-3-line.png b/pcsx2-qt/resources/icons/white/64/save-3-line.png new file mode 100644 index 000000000..b906669e7 Binary files /dev/null and b/pcsx2-qt/resources/icons/white/64/save-3-line.png differ diff --git a/pcsx2-qt/resources/icons/white/64/screenshot-2-line.png b/pcsx2-qt/resources/icons/white/64/screenshot-2-line.png new file mode 100644 index 000000000..c9f7277b6 Binary files /dev/null and b/pcsx2-qt/resources/icons/white/64/screenshot-2-line.png differ diff --git a/pcsx2-qt/resources/icons/white/64/sd-card-line.png b/pcsx2-qt/resources/icons/white/64/sd-card-line.png new file mode 100644 index 000000000..2ceb37ab0 Binary files /dev/null and b/pcsx2-qt/resources/icons/white/64/sd-card-line.png differ diff --git a/pcsx2-qt/resources/icons/white/64/settings-3-line.png b/pcsx2-qt/resources/icons/white/64/settings-3-line.png new file mode 100644 index 000000000..3b06b150c Binary files /dev/null and b/pcsx2-qt/resources/icons/white/64/settings-3-line.png differ diff --git a/pcsx2-qt/resources/icons/white/64/shut-down-line.png b/pcsx2-qt/resources/icons/white/64/shut-down-line.png new file mode 100644 index 000000000..beffa8c0d Binary files /dev/null and b/pcsx2-qt/resources/icons/white/64/shut-down-line.png differ diff --git a/pcsx2-qt/resources/icons/white/64/tv-2-line.png b/pcsx2-qt/resources/icons/white/64/tv-2-line.png new file mode 100644 index 000000000..5299e8374 Binary files /dev/null and b/pcsx2-qt/resources/icons/white/64/tv-2-line.png differ diff --git a/pcsx2-qt/resources/icons/white/64/volume-up-line.png b/pcsx2-qt/resources/icons/white/64/volume-up-line.png new file mode 100644 index 000000000..d3b9f0a10 Binary files /dev/null and b/pcsx2-qt/resources/icons/white/64/volume-up-line.png differ diff --git a/pcsx2-qt/resources/icons/white/64/window-2-line.png b/pcsx2-qt/resources/icons/white/64/window-2-line.png new file mode 100644 index 000000000..985f61180 Binary files /dev/null and b/pcsx2-qt/resources/icons/white/64/window-2-line.png differ diff --git a/pcsx2-qt/resources/icons/white/index.theme b/pcsx2-qt/resources/icons/white/index.theme new file mode 100644 index 000000000..912dc8572 --- /dev/null +++ b/pcsx2-qt/resources/icons/white/index.theme @@ -0,0 +1,15 @@ +[Icon Theme] +Name=White Icon Theme +Comment=White Icon Theme + +[16] +Size=16 +Type=Fixed + +[32] +Size=32 +Type=Fixed + +[64] +Size=64 +Type=Fixed diff --git a/pcsx2-qt/resources/images/dualshock-2.png b/pcsx2-qt/resources/images/dualshock-2.png new file mode 100644 index 000000000..9ae167ba1 Binary files /dev/null and b/pcsx2-qt/resources/images/dualshock-2.png differ diff --git a/pcsx2-qt/resources/images/dualshock-2@2x.png b/pcsx2-qt/resources/images/dualshock-2@2x.png new file mode 100644 index 000000000..5e01800c4 Binary files /dev/null and b/pcsx2-qt/resources/images/dualshock-2@2x.png differ diff --git a/pcsx2-qt/resources/resources.qrc b/pcsx2-qt/resources/resources.qrc new file mode 100644 index 000000000..6e9f2b4b9 --- /dev/null +++ b/pcsx2-qt/resources/resources.qrc @@ -0,0 +1,430 @@ + + + icons/address-book-new-22.png + icons/address-book-new-22@2x.png + icons/AppIcon.png + icons/AppIcon@2x.png + icons/AppIconLarge.png + icons/AppIconLarge@2x.png + icons/applications-system-24.png + icons/applications-system-24@2x.png + icons/black/16/artboard-2-line.png + icons/black/16/book-open-line.png + icons/black/16/brush-line.png + icons/black/16/close-line.png + icons/black/16/dashboard-line.png + icons/black/16/disc-line.png + icons/black/16/door-open-line.png + icons/black/16/download-2-line.png + icons/black/16/dvd-line.png + icons/black/16/file-add-line.png + icons/black/16/file-line.png + icons/black/16/file-list-line.png + icons/black/16/file-reduce-line.png + icons/black/16/file-search-line.png + icons/black/16/file-settings-line.png + icons/black/16/flask-line.png + icons/black/16/folder-add-line.png + icons/black/16/folder-open-line.png + icons/black/16/folder-reduce-line.png + icons/black/16/folder-settings-line.png + icons/black/16/fullscreen-line.png + icons/black/16/function-line.png + icons/black/16/gamepad-line.png + icons/black/16/hard-drive-2-line.png + icons/black/16/keyboard-line.png + icons/black/16/layout-grid-line.png + icons/black/16/list-check.png + icons/black/16/pause-line.png + icons/black/16/play-line.png + icons/black/16/refresh-line.png + icons/black/16/restart-line.png + icons/black/16/save-3-line.png + icons/black/16/screenshot-2-line.png + icons/black/16/sd-card-line.png + icons/black/16/settings-3-line.png + icons/black/16/shut-down-line.png + icons/black/16/tv-2-line.png + icons/black/16/volume-up-line.png + icons/black/16/window-2-line.png + icons/black/24/artboard-2-line.png + icons/black/24/book-open-line.png + icons/black/24/brush-line.png + icons/black/24/close-line.png + icons/black/24/dashboard-line.png + icons/black/24/disc-line.png + icons/black/24/door-open-line.png + icons/black/24/download-2-line.png + icons/black/24/dvd-line.png + icons/black/24/file-add-line.png + icons/black/24/file-line.png + icons/black/24/file-list-line.png + icons/black/24/file-reduce-line.png + icons/black/24/file-search-line.png + icons/black/24/file-settings-line.png + icons/black/24/flask-line.png + icons/black/24/folder-add-line.png + icons/black/24/folder-open-line.png + icons/black/24/folder-reduce-line.png + icons/black/24/folder-settings-line.png + icons/black/24/fullscreen-line.png + icons/black/24/function-line.png + icons/black/24/gamepad-line.png + icons/black/24/hard-drive-2-line.png + icons/black/24/keyboard-line.png + icons/black/24/layout-grid-line.png + icons/black/24/list-check.png + icons/black/24/pause-line.png + icons/black/24/play-line.png + icons/black/24/refresh-line.png + icons/black/24/restart-line.png + icons/black/24/save-3-line.png + icons/black/24/screenshot-2-line.png + icons/black/24/sd-card-line.png + icons/black/24/settings-3-line.png + icons/black/24/shut-down-line.png + icons/black/24/volume-up-line.png + icons/black/24/window-2-line.png + icons/black/32/artboard-2-line.png + icons/black/32/book-open-line.png + icons/black/32/brush-line.png + icons/black/32/close-line.png + icons/black/32/dashboard-line.png + icons/black/32/disc-line.png + icons/black/32/door-open-line.png + icons/black/32/download-2-line.png + icons/black/32/dvd-line.png + icons/black/32/file-add-line.png + icons/black/32/file-line.png + icons/black/32/file-list-line.png + icons/black/32/file-reduce-line.png + icons/black/32/file-search-line.png + icons/black/32/file-settings-line.png + icons/black/32/flask-line.png + icons/black/32/folder-add-line.png + icons/black/32/folder-open-line.png + icons/black/32/folder-reduce-line.png + icons/black/32/folder-settings-line.png + icons/black/32/fullscreen-line.png + icons/black/32/function-line.png + icons/black/32/gamepad-line.png + icons/black/32/hard-drive-2-line.png + icons/black/32/keyboard-line.png + icons/black/32/layout-grid-line.png + icons/black/32/list-check.png + icons/black/32/pause-line.png + icons/black/32/play-line.png + icons/black/32/refresh-line.png + icons/black/32/restart-line.png + icons/black/32/save-3-line.png + icons/black/32/screenshot-2-line.png + icons/black/32/sd-card-line.png + icons/black/32/settings-3-line.png + icons/black/32/shut-down-line.png + icons/black/32/tv-2-line.png + icons/black/32/volume-up-line.png + icons/black/32/window-2-line.png + icons/black/48/artboard-2-line.png + icons/black/48/book-open-line.png + icons/black/48/brush-line.png + icons/black/48/close-line.png + icons/black/48/dashboard-line.png + icons/black/48/disc-line.png + icons/black/48/door-open-line.png + icons/black/48/download-2-line.png + icons/black/48/dvd-line.png + icons/black/48/file-add-line.png + icons/black/48/file-line.png + icons/black/48/file-list-line.png + icons/black/48/file-reduce-line.png + icons/black/48/file-search-line.png + icons/black/48/file-settings-line.png + icons/black/48/flask-line.png + icons/black/48/folder-add-line.png + icons/black/48/folder-open-line.png + icons/black/48/folder-reduce-line.png + icons/black/48/folder-settings-line.png + icons/black/48/fullscreen-line.png + icons/black/48/function-line.png + icons/black/48/gamepad-line.png + icons/black/48/hard-drive-2-line.png + icons/black/48/keyboard-line.png + icons/black/48/layout-grid-line.png + icons/black/48/list-check.png + icons/black/48/pause-line.png + icons/black/48/play-line.png + icons/black/48/refresh-line.png + icons/black/48/restart-line.png + icons/black/48/save-3-line.png + icons/black/48/screenshot-2-line.png + icons/black/48/sd-card-line.png + icons/black/48/settings-3-line.png + icons/black/48/shut-down-line.png + icons/black/48/volume-up-line.png + icons/black/48/window-2-line.png + icons/black/64/artboard-2-line.png + icons/black/64/book-open-line.png + icons/black/64/brush-line.png + icons/black/64/close-line.png + icons/black/64/dashboard-line.png + icons/black/64/disc-line.png + icons/black/64/door-open-line.png + icons/black/64/download-2-line.png + icons/black/64/dvd-line.png + icons/black/64/file-add-line.png + icons/black/64/file-line.png + icons/black/64/file-list-line.png + icons/black/64/file-reduce-line.png + icons/black/64/file-search-line.png + icons/black/64/file-settings-line.png + icons/black/64/flask-line.png + icons/black/64/folder-add-line.png + icons/black/64/folder-open-line.png + icons/black/64/folder-reduce-line.png + icons/black/64/folder-settings-line.png + icons/black/64/fullscreen-line.png + icons/black/64/function-line.png + icons/black/64/gamepad-line.png + icons/black/64/hard-drive-2-line.png + icons/black/64/keyboard-line.png + icons/black/64/layout-grid-line.png + icons/black/64/list-check.png + icons/black/64/pause-line.png + icons/black/64/play-line.png + icons/black/64/refresh-line.png + icons/black/64/restart-line.png + icons/black/64/save-3-line.png + icons/black/64/screenshot-2-line.png + icons/black/64/sd-card-line.png + icons/black/64/settings-3-line.png + icons/black/64/shut-down-line.png + icons/black/64/tv-2-line.png + icons/black/64/volume-up-line.png + icons/black/64/window-2-line.png + icons/black/index.theme + icons/discord.png + icons/flag-eu.png + icons/flag-eu.svg + icons/flag-eu@2x.png + icons/flag-jp.png + icons/flag-jp.svg + icons/flag-jp@2x.png + icons/flag-other.png + icons/flag-other@2x.png + icons/flag-us.png + icons/flag-us.svg + icons/flag-us@2x.png + icons/logo.png + icons/media-optical-24.png + icons/media-optical-24@2x.png + icons/media-optical-gear-24.png + icons/media-optical-gear-24@2x.png + icons/media-optical.png + icons/media-optical@2x.png + icons/QT.png + icons/star-0.png + icons/star-1.png + icons/star-2.png + icons/star-3.png + icons/star-4.png + icons/star-5.png + icons/white/16/artboard-2-line.png + icons/white/16/book-open-line.png + icons/white/16/brush-line.png + icons/white/16/close-line.png + icons/white/16/dashboard-line.png + icons/white/16/disc-line.png + icons/white/16/door-open-line.png + icons/white/16/download-2-line.png + icons/white/16/dvd-line.png + icons/white/16/file-add-line.png + icons/white/16/file-line.png + icons/white/16/file-list-line.png + icons/white/16/file-reduce-line.png + icons/white/16/file-search-line.png + icons/white/16/file-settings-line.png + icons/white/16/flask-line.png + icons/white/16/folder-add-line.png + icons/white/16/folder-open-line.png + icons/white/16/folder-reduce-line.png + icons/white/16/folder-settings-line.png + icons/white/16/fullscreen-line.png + icons/white/16/function-line.png + icons/white/16/gamepad-line.png + icons/white/16/hard-drive-2-line.png + icons/white/16/keyboard-line.png + icons/white/16/layout-grid-line.png + icons/white/16/list-check.png + icons/white/16/pause-line.png + icons/white/16/play-line.png + icons/white/16/refresh-line.png + icons/white/16/restart-line.png + icons/white/16/save-3-line.png + icons/white/16/screenshot-2-line.png + icons/white/16/sd-card-line.png + icons/white/16/settings-3-line.png + icons/white/16/shut-down-line.png + icons/white/16/tv-2-line.png + icons/white/16/volume-up-line.png + icons/white/16/window-2-line.png + icons/white/24/artboard-2-line.png + icons/white/24/book-open-line.png + icons/white/24/brush-line.png + icons/white/24/close-line.png + icons/white/24/dashboard-line.png + icons/white/24/disc-line.png + icons/white/24/door-open-line.png + icons/white/24/download-2-line.png + icons/white/24/dvd-line.png + icons/white/24/file-add-line.png + icons/white/24/file-line.png + icons/white/24/file-list-line.png + icons/white/24/file-reduce-line.png + icons/white/24/file-search-line.png + icons/white/24/file-settings-line.png + icons/white/24/flask-line.png + icons/white/24/folder-add-line.png + icons/white/24/folder-open-line.png + icons/white/24/folder-reduce-line.png + icons/white/24/folder-settings-line.png + icons/white/24/fullscreen-line.png + icons/white/24/function-line.png + icons/white/24/gamepad-line.png + icons/white/24/hard-drive-2-line.png + icons/white/24/keyboard-line.png + icons/white/24/layout-grid-line.png + icons/white/24/list-check.png + icons/white/24/pause-line.png + icons/white/24/play-line.png + icons/white/24/refresh-line.png + icons/white/24/restart-line.png + icons/white/24/save-3-line.png + icons/white/24/screenshot-2-line.png + icons/white/24/sd-card-line.png + icons/white/24/settings-3-line.png + icons/white/24/shut-down-line.png + icons/white/24/tv-2-line.png + icons/white/24/volume-up-line.png + icons/white/24/window-2-line.png + icons/white/32/artboard-2-line.png + icons/white/32/book-open-line.png + icons/white/32/brush-line.png + icons/white/32/close-line.png + icons/white/32/dashboard-line.png + icons/white/32/disc-line.png + icons/white/32/door-open-line.png + icons/white/32/download-2-line.png + icons/white/32/dvd-line.png + icons/white/32/file-add-line.png + icons/white/32/file-line.png + icons/white/32/file-list-line.png + icons/white/32/file-reduce-line.png + icons/white/32/file-search-line.png + icons/white/32/file-settings-line.png + icons/white/32/flask-line.png + icons/white/32/folder-add-line.png + icons/white/32/folder-open-line.png + icons/white/32/folder-reduce-line.png + icons/white/32/folder-settings-line.png + icons/white/32/fullscreen-line.png + icons/white/32/function-line.png + icons/white/32/gamepad-line.png + icons/white/32/hard-drive-2-line.png + icons/white/32/keyboard-line.png + icons/white/32/layout-grid-line.png + icons/white/32/list-check.png + icons/white/32/pause-line.png + icons/white/32/play-line.png + icons/white/32/refresh-line.png + icons/white/32/restart-line.png + icons/white/32/save-3-line.png + icons/white/32/screenshot-2-line.png + icons/white/32/sd-card-line.png + icons/white/32/settings-3-line.png + icons/white/32/shut-down-line.png + icons/white/32/tv-2-line.png + icons/white/32/volume-up-line.png + icons/white/32/window-2-line.png + icons/white/48/artboard-2-line.png + icons/white/48/book-open-line.png + icons/white/48/brush-line.png + icons/white/48/close-line.png + icons/white/48/dashboard-line.png + icons/white/48/disc-line.png + icons/white/48/door-open-line.png + icons/white/48/download-2-line.png + icons/white/48/dvd-line.png + icons/white/48/file-add-line.png + icons/white/48/file-line.png + icons/white/48/file-list-line.png + icons/white/48/file-reduce-line.png + icons/white/48/file-search-line.png + icons/white/48/file-settings-line.png + icons/white/48/flask-line.png + icons/white/48/folder-add-line.png + icons/white/48/folder-open-line.png + icons/white/48/folder-reduce-line.png + icons/white/48/folder-settings-line.png + icons/white/48/fullscreen-line.png + icons/white/48/function-line.png + icons/white/48/gamepad-line.png + icons/white/48/hard-drive-2-line.png + icons/white/48/keyboard-line.png + icons/white/48/layout-grid-line.png + icons/white/48/list-check.png + icons/white/48/pause-line.png + icons/white/48/play-line.png + icons/white/48/refresh-line.png + icons/white/48/restart-line.png + icons/white/48/save-3-line.png + icons/white/48/screenshot-2-line.png + icons/white/48/sd-card-line.png + icons/white/48/settings-3-line.png + icons/white/48/shut-down-line.png + icons/white/48/tv-2-line.png + icons/white/48/volume-up-line.png + icons/white/48/window-2-line.png + icons/white/64/artboard-2-line.png + icons/white/64/book-open-line.png + icons/white/64/brush-line.png + icons/white/64/close-line.png + icons/white/64/dashboard-line.png + icons/white/64/disc-line.png + icons/white/64/door-open-line.png + icons/white/64/download-2-line.png + icons/white/64/dvd-line.png + icons/white/64/file-add-line.png + icons/white/64/file-line.png + icons/white/64/file-list-line.png + icons/white/64/file-reduce-line.png + icons/white/64/file-search-line.png + icons/white/64/file-settings-line.png + icons/white/64/flask-line.png + icons/white/64/folder-add-line.png + icons/white/64/folder-open-line.png + icons/white/64/folder-reduce-line.png + icons/white/64/folder-settings-line.png + icons/white/64/fullscreen-line.png + icons/white/64/function-line.png + icons/white/64/gamepad-line.png + icons/white/64/hard-drive-2-line.png + icons/white/64/keyboard-line.png + icons/white/64/layout-grid-line.png + icons/white/64/list-check.png + icons/white/64/pause-line.png + icons/white/64/play-line.png + icons/white/64/refresh-line.png + icons/white/64/restart-line.png + icons/white/64/save-3-line.png + icons/white/64/screenshot-2-line.png + icons/white/64/sd-card-line.png + icons/white/64/settings-3-line.png + icons/white/64/shut-down-line.png + icons/white/64/tv-2-line.png + icons/white/64/volume-up-line.png + icons/white/64/window-2-line.png + icons/white/index.theme + images/dualshock-2.png + images/dualshock-2@2x.png + +