Add initial work on Qt frontend
4
.github/workflows/linux-build.yml
vendored
|
@ -27,7 +27,7 @@ jobs:
|
||||||
run: |
|
run: |
|
||||||
mkdir build-debug
|
mkdir build-debug
|
||||||
cd build-debug
|
cd build-debug
|
||||||
cmake -DCMAKE_BUILD_TYPE=Debug ..
|
cmake -DCMAKE_BUILD_TYPE=Debug -DBUILD_QT_FRONTEND=OFF ..
|
||||||
make
|
make
|
||||||
|
|
||||||
- name: Compile release build
|
- name: Compile release build
|
||||||
|
@ -35,6 +35,6 @@ jobs:
|
||||||
run: |
|
run: |
|
||||||
mkdir build-release
|
mkdir build-release
|
||||||
cd build-release
|
cd build-release
|
||||||
cmake -DCMAKE_BUILD_TYPE=Release ..
|
cmake -DCMAKE_BUILD_TYPE=Release -DBUILD_QT_FRONTEND=OFF ..
|
||||||
make
|
make
|
||||||
|
|
||||||
|
|
|
@ -4,6 +4,11 @@ project(duckstation C CXX)
|
||||||
# Pull in modules.
|
# Pull in modules.
|
||||||
set(CMAKE_MODULE_PATH "${CMAKE_SOURCE_DIR}/CMakeModules/")
|
set(CMAKE_MODULE_PATH "${CMAKE_SOURCE_DIR}/CMakeModules/")
|
||||||
|
|
||||||
|
if(NOT ANDROID)
|
||||||
|
option(BUILD_SDL_FRONTEND "Build the SDL frontend" ON)
|
||||||
|
option(BUILD_QT_FRONTEND "Build the Qt frontend" ON)
|
||||||
|
endif()
|
||||||
|
|
||||||
|
|
||||||
# Common include/library directories on Windows.
|
# Common include/library directories on Windows.
|
||||||
if(WIN32)
|
if(WIN32)
|
||||||
|
@ -30,8 +35,15 @@ endif()
|
||||||
|
|
||||||
# Required libraries.
|
# Required libraries.
|
||||||
if(NOT ANDROID)
|
if(NOT ANDROID)
|
||||||
find_package(SDL2 REQUIRED)
|
if(BUILD_SDL_FRONTEND)
|
||||||
else()
|
find_package(SDL2 REQUIRED)
|
||||||
|
endif()
|
||||||
|
if(BUILD_QT_FRONTEND)
|
||||||
|
find_package(Qt5 COMPONENTS Core Gui Widgets REQUIRED)
|
||||||
|
endif()
|
||||||
|
endif()
|
||||||
|
|
||||||
|
if(ANDROID)
|
||||||
find_package(EGL REQUIRED)
|
find_package(EGL REQUIRED)
|
||||||
endif()
|
endif()
|
||||||
|
|
||||||
|
|
116
dep/msvc/vsprops/QtCompile.props
Normal file
|
@ -0,0 +1,116 @@
|
||||||
|
<?xml version="1.0" encoding="utf-8"?>
|
||||||
|
<Project ToolsVersion="15.0" xmlns="http://schemas.microsoft.com/developer/msbuild/2003">
|
||||||
|
<PropertyGroup Label="UserMacros">
|
||||||
|
<QTDIRDefault>$(SolutionDir)dep\msvc\qt5-x64\</QTDIRDefault>
|
||||||
|
<QTDIR Condition="Exists('$(QTDIRDefault)') And ('$(QTDIR)'=='' Or !Exists('$(QTDIR)'))">$(QTDIRDefault)</QTDIR>
|
||||||
|
<QTDIR Condition="Exists('$(QTDIR)') And !HasTrailingSlash('$(QTDIR)')">$(QTDIR)\</QTDIR>
|
||||||
|
<QtDirValid>false</QtDirValid>
|
||||||
|
<QtDirValid Condition="Exists('$(QTDIR)')">true</QtDirValid>
|
||||||
|
<QtIncludeDir>$(QTDIR)include\</QtIncludeDir>
|
||||||
|
<QtLibDir>$(QTDIR)lib\</QtLibDir>
|
||||||
|
<QtBinDir>$(QTDIR)bin\</QtBinDir>
|
||||||
|
<QtPluginsDir>$(QTDIR)plugins\</QtPluginsDir>
|
||||||
|
<QtToolOutDir>$(SolutionDir)build\$(ProjectName)-$(Platform)-$(Configuration)\</QtToolOutDir>
|
||||||
|
<QtMocOutPrefix>$(QtToolOutDir)moc_</QtMocOutPrefix>
|
||||||
|
<QtDebugSuffix>d</QtDebugSuffix>
|
||||||
|
<QtLibSuffix Condition="'$(Configuration)'=='Debug'">$(QtDebugSuffix)</QtLibSuffix>
|
||||||
|
<QtPluginFolder>QtPlugins</QtPluginFolder>
|
||||||
|
</PropertyGroup>
|
||||||
|
<ItemDefinitionGroup>
|
||||||
|
<ClCompile>
|
||||||
|
<PreprocessorDefinitions Condition="'$(Configuration)'=='Release'">QT_NO_DEBUG;%(PreprocessorDefinitions)</PreprocessorDefinitions>
|
||||||
|
<AdditionalIncludeDirectories>$(ProjectDir);%(AdditionalIncludeDirectories)</AdditionalIncludeDirectories>
|
||||||
|
<AdditionalIncludeDirectories>$(QtToolOutDir);%(AdditionalIncludeDirectories)</AdditionalIncludeDirectories>
|
||||||
|
<AdditionalIncludeDirectories>$(QtIncludeDir);%(AdditionalIncludeDirectories)</AdditionalIncludeDirectories>
|
||||||
|
</ClCompile>
|
||||||
|
<Link>
|
||||||
|
<AdditionalLibraryDirectories>$(QtLibDir);%(AdditionalLibraryDirectories)</AdditionalLibraryDirectories>
|
||||||
|
<AdditionalDependencies>qtmain$(QtLibSuffix).lib;Qt5Core$(QtLibSuffix).lib;Qt5Gui$(QtLibSuffix).lib;Qt5Widgets$(QtLibSuffix).lib;%(AdditionalDependencies)</AdditionalDependencies>
|
||||||
|
</Link>
|
||||||
|
</ItemDefinitionGroup>
|
||||||
|
|
||||||
|
<!--Passes all .qrc files to rcc and puts output in the build directory-->
|
||||||
|
<ItemGroup>
|
||||||
|
<ResFiles Include="$(MSBuildProjectDirectory)\**\*.qrc" />
|
||||||
|
</ItemGroup>
|
||||||
|
<Target Name="QtResource"
|
||||||
|
BeforeTargets="ClCompile"
|
||||||
|
Inputs="@(ResFiles)"
|
||||||
|
Condition="'@(QtResource)'!=''"
|
||||||
|
Outputs="@(ResFiles->'$(QtToolOutDir)qrc_%(Filename).cpp')">
|
||||||
|
<Message Text="rcc %(ResFiles.Filename)" Importance="High" />
|
||||||
|
<Error Condition="!$(QtDirValid)" Text="QTDIR not set or non-existent (pull the submodule?)" />
|
||||||
|
<MakeDir Directories="$(QtToolOutDir)" />
|
||||||
|
<Exec Command=""$(QtBinDir)rcc.exe" "%(ResFiles.FullPath)" -o "$(QtToolOutDir)qrc_%(ResFiles.Filename).cpp"" />
|
||||||
|
</Target>
|
||||||
|
|
||||||
|
<Target Name="QtResourceClean">
|
||||||
|
<Delete Files="@(ResFiles->'$(QtToolOutDir)qrc_%(Filename).cpp')" />
|
||||||
|
</Target>
|
||||||
|
|
||||||
|
<!--Passes all .ui files to uic and puts output in the build directory-->
|
||||||
|
<ItemGroup>
|
||||||
|
<UiFiles Include="$(MSBuildProjectDirectory)\**\*.ui" />
|
||||||
|
</ItemGroup>
|
||||||
|
<Target Name="QtUi"
|
||||||
|
BeforeTargets="ClCompile"
|
||||||
|
Inputs="@(UiFiles)"
|
||||||
|
Condition="'@(QtUi)'!=''"
|
||||||
|
Outputs="@(UiFiles->'$(QtToolOutDir)ui_%(Filename).h')">
|
||||||
|
<Message Text="uic %(UiFiles.Filename)" Importance="High" />
|
||||||
|
<Error Condition="!$(QtDirValid)" Text="QTDIR not set or non-existent (pull the submodule?)" />
|
||||||
|
<MakeDir Directories="$(QtToolOutDir)" />
|
||||||
|
<Exec Command=""$(QtBinDir)uic.exe" "%(UiFiles.FullPath)" -o "$(QtToolOutDir)ui_%(UiFiles.Filename).h"" />
|
||||||
|
</Target>
|
||||||
|
|
||||||
|
<Target Name="QtUiClean">
|
||||||
|
<Delete Files="@(UiFiles->'$(QtToolOutDir)ui_%(Filename).h')" />
|
||||||
|
</Target>
|
||||||
|
|
||||||
|
<!--Compile files needed to MOC and output in the build directory-->
|
||||||
|
<!--TODO find a way to autocreate from ClCompile settings-->
|
||||||
|
<PropertyGroup>
|
||||||
|
<MocDefines></MocDefines>
|
||||||
|
<MocDefines Condition="'$(Configuration)'=='Release'">-DQT_NO_DEBUG -DNDEBUG $(MocDefines)</MocDefines>
|
||||||
|
<!-- !!!HOLY UGLY BATMAN!!!
|
||||||
|
Be very careful here when adding include directories. Each path must have the whole arg surrounded by doublequotes - HOWEVER,
|
||||||
|
the ending doublequote cannot be directly preceeded by a directory seperator. In other words, you must use:
|
||||||
|
"-I$(SomeDir) "
|
||||||
|
instead of
|
||||||
|
"-I$(SomeDir)"
|
||||||
|
in order to prevent the trailing slash from escaping the doublequote after value replacement.
|
||||||
|
-->
|
||||||
|
<MocIncludes>"-I$(QtIncludeDir)" "-I$(SolutionDir)src" -I.</MocIncludes>
|
||||||
|
</PropertyGroup>
|
||||||
|
<Target Name="QtMoc"
|
||||||
|
BeforeTargets="ClCompile"
|
||||||
|
Condition="'@(QtMoc)'!=''"
|
||||||
|
Inputs="%(QtMoc.Identity);%(QtMoc.AdditionalDependencies);$(MSBuildProjectFile)"
|
||||||
|
Outputs="$(QtToolOutDir)moc_%(QtMoc.Filename).cpp">
|
||||||
|
<Message Text="moc %(QtMoc.Filename) $(QtToolOutDir)moc_%(QtMoc.Filename).cpp" Importance="High" />
|
||||||
|
<Error Condition="!$(QtDirValid)" Text="QTDIR not set or non-existent (pull the submodule?)" />
|
||||||
|
<MakeDir Directories="$(QtToolOutDir)" />
|
||||||
|
<Exec Command=""$(QtBinDir)moc.exe" "%(QtMoc.FullPath)" -o "$(QtToolOutDir)moc_%(QtMoc.Filename).cpp" -f%(QtMoc.Filename)%(QtMoc.Extension) $(MocDefines) $(MocIncludes)" />
|
||||||
|
</Target>
|
||||||
|
|
||||||
|
<ItemGroup>
|
||||||
|
<MocOutputs Include="$(QtToolOutDir)moc_*.cpp" />
|
||||||
|
</ItemGroup>
|
||||||
|
<Target Name="QtMocClean">
|
||||||
|
<Delete Files="@(MocOutputs)" />
|
||||||
|
</Target>
|
||||||
|
|
||||||
|
<!--Expose the new targets to VS-->
|
||||||
|
<ItemGroup>
|
||||||
|
<PropertyPageSchema Include="$(MSBuildThisFileDirectory)$(MSBuildThisFileName).xml" />
|
||||||
|
<AvailableItemName Include="QtResource">
|
||||||
|
<Targets>QtResource</Targets>
|
||||||
|
</AvailableItemName>
|
||||||
|
<AvailableItemName Include="QtUi">
|
||||||
|
<Targets>QtUi</Targets>
|
||||||
|
</AvailableItemName>
|
||||||
|
<AvailableItemName Include="QtMoc">
|
||||||
|
<Targets>QtMoc</Targets>
|
||||||
|
</AvailableItemName>
|
||||||
|
</ItemGroup>
|
||||||
|
</Project>
|
10
dep/msvc/vsprops/QtCompile.targets
Normal file
|
@ -0,0 +1,10 @@
|
||||||
|
<?xml version="1.0" encoding="utf-8"?>
|
||||||
|
<Project ToolsVersion="15.0" xmlns="http://schemas.microsoft.com/developer/msbuild/2003">
|
||||||
|
<PropertyGroup>
|
||||||
|
<CleanDependsOn>QtResourceClean;QtUiClean;QtMocClean;$(CleanDependsOn)</CleanDependsOn>
|
||||||
|
<!--
|
||||||
|
<ComputeLinkInputsTargets>$(ComputeLinkInputsTargets);QtComputeMocOutput;</ComputeLinkInputsTargets>
|
||||||
|
<ComputeLibInputsTargets>$(ComputeLibInputsTargets);QtComputeMocOutput;</ComputeLibInputsTargets>
|
||||||
|
-->
|
||||||
|
</PropertyGroup>
|
||||||
|
</Project>
|
11
dep/msvc/vsprops/QtCompile.xml
Normal file
|
@ -0,0 +1,11 @@
|
||||||
|
<?xml version="1.0" encoding="utf-8"?>
|
||||||
|
<ProjectSchemaDefinitions xmlns="http://schemas.microsoft.com/build/2009/properties">
|
||||||
|
<ItemType Name="QtResource" DisplayName="Qt Resource File" />
|
||||||
|
<ItemType Name="QtUi" DisplayName="Qt User Interface File" />
|
||||||
|
<ItemType Name="QtMoc" DisplayName="Qt Meta Object File" />
|
||||||
|
<ContentType Name="QtUi" DisplayName="Qt User Interface Compiler" />
|
||||||
|
<ContentType Name="QtResource" DisplayName="Qt Resource Compiler" />
|
||||||
|
<ContentType Name="QtMoc" DisplayName="Qt Meta Object Compiler" />
|
||||||
|
<FileExtension Name="*.qrc" ContentType="QtResource" />
|
||||||
|
<FileExtension Name="*.ui" ContentType="QtUi" />
|
||||||
|
</ProjectSchemaDefinitions>
|
|
@ -27,6 +27,8 @@ Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "simpleini", "dep\simpleini\
|
||||||
EndProject
|
EndProject
|
||||||
Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "tinyxml2", "dep\tinyxml2\tinyxml2.vcxproj", "{933118A9-68C5-47B4-B151-B03C93961623}"
|
Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "tinyxml2", "dep\tinyxml2\tinyxml2.vcxproj", "{933118A9-68C5-47B4-B151-B03C93961623}"
|
||||||
EndProject
|
EndProject
|
||||||
|
Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "duckstation-qt", "src\duckstation-qt\duckstation-qt.vcxproj", "{28F14272-0EC4-41BB-849F-182ADB81AF70}"
|
||||||
|
EndProject
|
||||||
Global
|
Global
|
||||||
GlobalSection(SolutionConfigurationPlatforms) = preSolution
|
GlobalSection(SolutionConfigurationPlatforms) = preSolution
|
||||||
Debug|x64 = Debug|x64
|
Debug|x64 = Debug|x64
|
||||||
|
@ -215,6 +217,18 @@ Global
|
||||||
{933118A9-68C5-47B4-B151-B03C93961623}.ReleaseLTCG|x64.Build.0 = Release|x64
|
{933118A9-68C5-47B4-B151-B03C93961623}.ReleaseLTCG|x64.Build.0 = Release|x64
|
||||||
{933118A9-68C5-47B4-B151-B03C93961623}.ReleaseLTCG|x86.ActiveCfg = Release|Win32
|
{933118A9-68C5-47B4-B151-B03C93961623}.ReleaseLTCG|x86.ActiveCfg = Release|Win32
|
||||||
{933118A9-68C5-47B4-B151-B03C93961623}.ReleaseLTCG|x86.Build.0 = Release|Win32
|
{933118A9-68C5-47B4-B151-B03C93961623}.ReleaseLTCG|x86.Build.0 = Release|Win32
|
||||||
|
{28F14272-0EC4-41BB-849F-182ADB81AF70}.Debug|x64.ActiveCfg = Debug|x64
|
||||||
|
{28F14272-0EC4-41BB-849F-182ADB81AF70}.Debug|x86.ActiveCfg = Debug|Win32
|
||||||
|
{28F14272-0EC4-41BB-849F-182ADB81AF70}.Debug|x86.Build.0 = Debug|Win32
|
||||||
|
{28F14272-0EC4-41BB-849F-182ADB81AF70}.DebugFast|x64.ActiveCfg = DebugFast|x64
|
||||||
|
{28F14272-0EC4-41BB-849F-182ADB81AF70}.DebugFast|x86.ActiveCfg = DebugFast|Win32
|
||||||
|
{28F14272-0EC4-41BB-849F-182ADB81AF70}.DebugFast|x86.Build.0 = DebugFast|Win32
|
||||||
|
{28F14272-0EC4-41BB-849F-182ADB81AF70}.Release|x64.ActiveCfg = Release|x64
|
||||||
|
{28F14272-0EC4-41BB-849F-182ADB81AF70}.Release|x86.ActiveCfg = Release|Win32
|
||||||
|
{28F14272-0EC4-41BB-849F-182ADB81AF70}.Release|x86.Build.0 = Release|Win32
|
||||||
|
{28F14272-0EC4-41BB-849F-182ADB81AF70}.ReleaseLTCG|x64.ActiveCfg = ReleaseLTCG|x64
|
||||||
|
{28F14272-0EC4-41BB-849F-182ADB81AF70}.ReleaseLTCG|x86.ActiveCfg = ReleaseLTCG|Win32
|
||||||
|
{28F14272-0EC4-41BB-849F-182ADB81AF70}.ReleaseLTCG|x86.Build.0 = ReleaseLTCG|Win32
|
||||||
EndGlobalSection
|
EndGlobalSection
|
||||||
GlobalSection(SolutionProperties) = preSolution
|
GlobalSection(SolutionProperties) = preSolution
|
||||||
HideSolutionNode = FALSE
|
HideSolutionNode = FALSE
|
||||||
|
|
|
@ -1,6 +1,11 @@
|
||||||
add_subdirectory(common)
|
add_subdirectory(common)
|
||||||
add_subdirectory(core)
|
add_subdirectory(core)
|
||||||
|
|
||||||
if(NOT ANDROID)
|
if(BUILD_SDL_FRONTEND)
|
||||||
add_subdirectory(duckstation)
|
add_subdirectory(duckstation)
|
||||||
endif()
|
endif()
|
||||||
|
|
||||||
|
if(BUILD_QT_FRONTEND)
|
||||||
|
add_subdirectory(duckstation-qt)
|
||||||
|
endif()
|
||||||
|
|
||||||
|
|
|
@ -35,7 +35,7 @@ static std::string GetPathDirectory(const char* path)
|
||||||
const char* backslash_ptr = std::strrchr(path, '\\');
|
const char* backslash_ptr = std::strrchr(path, '\\');
|
||||||
const char* slash_ptr;
|
const char* slash_ptr;
|
||||||
if (forwardslash_ptr && backslash_ptr)
|
if (forwardslash_ptr && backslash_ptr)
|
||||||
slash_ptr = std::min(forwardslash_ptr, backslash_ptr);
|
slash_ptr = std::max(forwardslash_ptr, backslash_ptr);
|
||||||
else if (backslash_ptr)
|
else if (backslash_ptr)
|
||||||
slash_ptr = backslash_ptr;
|
slash_ptr = backslash_ptr;
|
||||||
else if (forwardslash_ptr)
|
else if (forwardslash_ptr)
|
||||||
|
|
|
@ -4,6 +4,7 @@
|
||||||
#include "bios.h"
|
#include "bios.h"
|
||||||
#include "common/cd_image.h"
|
#include "common/cd_image.h"
|
||||||
#include "common/iso_reader.h"
|
#include "common/iso_reader.h"
|
||||||
|
#include "settings.h"
|
||||||
#include <algorithm>
|
#include <algorithm>
|
||||||
#include <array>
|
#include <array>
|
||||||
#include <cctype>
|
#include <cctype>
|
||||||
|
@ -194,11 +195,6 @@ std::optional<ConsoleRegion> GameList::GetRegionForPath(const char* image_path)
|
||||||
return GetRegionForImage(cdi.get());
|
return GetRegionForImage(cdi.get());
|
||||||
}
|
}
|
||||||
|
|
||||||
void GameList::AddDirectory(const char* path, bool recursive)
|
|
||||||
{
|
|
||||||
ScanDirectory(path, recursive);
|
|
||||||
}
|
|
||||||
|
|
||||||
bool GameList::IsExeFileName(const char* path)
|
bool GameList::IsExeFileName(const char* path)
|
||||||
{
|
{
|
||||||
const char* extension = std::strrchr(path, '.');
|
const char* extension = std::strrchr(path, '.');
|
||||||
|
@ -404,6 +400,40 @@ private:
|
||||||
GameList::DatabaseMap& m_database;
|
GameList::DatabaseMap& m_database;
|
||||||
};
|
};
|
||||||
|
|
||||||
|
void GameList::AddDirectory(std::string path, bool recursive)
|
||||||
|
{
|
||||||
|
auto iter = std::find_if(m_search_directories.begin(), m_search_directories.end(),
|
||||||
|
[&path](const DirectoryEntry& de) { return de.path == path; });
|
||||||
|
if (iter != m_search_directories.end())
|
||||||
|
{
|
||||||
|
iter->recursive = recursive;
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
m_search_directories.push_back({path, recursive});
|
||||||
|
}
|
||||||
|
|
||||||
|
void GameList::SetDirectoriesFromSettings(SettingsInterface& si)
|
||||||
|
{
|
||||||
|
m_search_directories.clear();
|
||||||
|
|
||||||
|
std::vector<std::string> dirs = si.GetStringList("GameList", "Paths");
|
||||||
|
for (std::string& dir : dirs)
|
||||||
|
m_search_directories.push_back({std::move(dir), false});
|
||||||
|
|
||||||
|
dirs = si.GetStringList("GameList", "RecursivePaths");
|
||||||
|
for (std::string& dir : dirs)
|
||||||
|
m_search_directories.push_back({std::move(dir), true});
|
||||||
|
}
|
||||||
|
|
||||||
|
void GameList::RescanAllDirectories()
|
||||||
|
{
|
||||||
|
m_entries.clear();
|
||||||
|
|
||||||
|
for (const DirectoryEntry& de : m_search_directories)
|
||||||
|
ScanDirectory(de.path.c_str(), de.recursive);
|
||||||
|
}
|
||||||
|
|
||||||
bool GameList::ParseRedumpDatabase(const char* redump_dat_path)
|
bool GameList::ParseRedumpDatabase(const char* redump_dat_path)
|
||||||
{
|
{
|
||||||
tinyxml2::XMLDocument doc;
|
tinyxml2::XMLDocument doc;
|
||||||
|
@ -427,3 +457,8 @@ bool GameList::ParseRedumpDatabase(const char* redump_dat_path)
|
||||||
Log_InfoPrintf("Loaded %zu entries from Redump.org database '%s'", m_database.size(), redump_dat_path);
|
Log_InfoPrintf("Loaded %zu entries from Redump.org database '%s'", m_database.size(), redump_dat_path);
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void GameList::ClearDatabase()
|
||||||
|
{
|
||||||
|
m_database.clear();
|
||||||
|
}
|
||||||
|
|
|
@ -8,6 +8,8 @@
|
||||||
|
|
||||||
class CDImage;
|
class CDImage;
|
||||||
|
|
||||||
|
class SettingsInterface;
|
||||||
|
|
||||||
class GameList
|
class GameList
|
||||||
{
|
{
|
||||||
public:
|
public:
|
||||||
|
@ -54,18 +56,28 @@ public:
|
||||||
const EntryList& GetEntries() const { return m_entries; }
|
const EntryList& GetEntries() const { return m_entries; }
|
||||||
const u32 GetEntryCount() const { return static_cast<u32>(m_entries.size()); }
|
const u32 GetEntryCount() const { return static_cast<u32>(m_entries.size()); }
|
||||||
|
|
||||||
void AddDirectory(const char* path, bool recursive);
|
void AddDirectory(std::string path, bool recursive);
|
||||||
|
void SetDirectoriesFromSettings(SettingsInterface& si);
|
||||||
|
void RescanAllDirectories();
|
||||||
|
|
||||||
bool ParseRedumpDatabase(const char* redump_dat_path);
|
bool ParseRedumpDatabase(const char* redump_dat_path);
|
||||||
|
void ClearDatabase();
|
||||||
|
|
||||||
private:
|
private:
|
||||||
|
struct DirectoryEntry
|
||||||
|
{
|
||||||
|
std::string path;
|
||||||
|
bool recursive;
|
||||||
|
};
|
||||||
|
|
||||||
static bool IsExeFileName(const char* path);
|
static bool IsExeFileName(const char* path);
|
||||||
static bool GetExeListEntry(const char* path, GameListEntry* entry);
|
static bool GetExeListEntry(const char* path, GameListEntry* entry);
|
||||||
|
|
||||||
bool GetGameListEntry(const char* path, GameListEntry* entry);
|
bool GetGameListEntry(const char* path, GameListEntry* entry);
|
||||||
|
|
||||||
void ScanDirectory(const char* path, bool recursive);
|
void ScanDirectory(const char* path, bool recursive);
|
||||||
|
|
||||||
DatabaseMap m_database;
|
DatabaseMap m_database;
|
||||||
EntryList m_entries;
|
EntryList m_entries;
|
||||||
|
|
||||||
|
std::vector<DirectoryEntry> m_search_directories;
|
||||||
};
|
};
|
||||||
|
|
32
src/duckstation-qt/CMakeLists.txt
Normal file
|
@ -0,0 +1,32 @@
|
||||||
|
set(CMAKE_AUTOMOC ON)
|
||||||
|
set(CMAKE_AUTORCC ON)
|
||||||
|
set(CMAKE_AUTOUIC ON)
|
||||||
|
|
||||||
|
add_executable(duckstation-qt
|
||||||
|
resources/icons.qrc
|
||||||
|
consolesettingswidget.cpp
|
||||||
|
consolesettingswidget.h
|
||||||
|
consolesettingswidget.ui
|
||||||
|
gamelistsettingswidget.cpp
|
||||||
|
gamelistsettingswidget.h
|
||||||
|
gamelistsettingswidget.ui
|
||||||
|
gamelistwidget.cpp
|
||||||
|
gamelistwidget.h
|
||||||
|
main.cpp
|
||||||
|
mainwindow.cpp
|
||||||
|
mainwindow.h
|
||||||
|
mainwindow.ui
|
||||||
|
opengldisplaywindow.cpp
|
||||||
|
opengldisplaywindow.h
|
||||||
|
qthostinterface.cpp
|
||||||
|
qthostinterface.h
|
||||||
|
qtsettingsinterface.cpp
|
||||||
|
qtsettingsinterface.h
|
||||||
|
qtutils.cpp
|
||||||
|
qtutils.h
|
||||||
|
settingsdialog.cpp
|
||||||
|
settingsdialog.h
|
||||||
|
settingsdialog.ui
|
||||||
|
)
|
||||||
|
|
||||||
|
target_link_libraries(duckstation-qt PRIVATE YBaseLib core common imgui glad Qt5::Core Qt5::Gui Qt5::Widgets)
|
8
src/duckstation-qt/consolesettingswidget.cpp
Normal file
|
@ -0,0 +1,8 @@
|
||||||
|
#include "consolesettingswidget.h"
|
||||||
|
|
||||||
|
ConsoleSettingsWidget::ConsoleSettingsWidget(QWidget* parent /*= nullptr*/) : QWidget(parent)
|
||||||
|
{
|
||||||
|
m_ui.setupUi(this);
|
||||||
|
}
|
||||||
|
|
||||||
|
ConsoleSettingsWidget::~ConsoleSettingsWidget() = default;
|
17
src/duckstation-qt/consolesettingswidget.h
Normal file
|
@ -0,0 +1,17 @@
|
||||||
|
#pragma once
|
||||||
|
|
||||||
|
#include <QtWidgets/QWidget>
|
||||||
|
|
||||||
|
#include "ui_consolesettingswidget.h"
|
||||||
|
|
||||||
|
class ConsoleSettingsWidget : public QWidget
|
||||||
|
{
|
||||||
|
Q_OBJECT
|
||||||
|
|
||||||
|
public:
|
||||||
|
explicit ConsoleSettingsWidget(QWidget* parent = nullptr);
|
||||||
|
~ConsoleSettingsWidget();
|
||||||
|
|
||||||
|
private:
|
||||||
|
Ui::ConsoleSettingsWidget m_ui;
|
||||||
|
};
|
114
src/duckstation-qt/consolesettingswidget.ui
Normal file
|
@ -0,0 +1,114 @@
|
||||||
|
<?xml version="1.0" encoding="UTF-8"?>
|
||||||
|
<ui version="4.0">
|
||||||
|
<class>ConsoleSettingsWidget</class>
|
||||||
|
<widget class="QWidget" name="ConsoleSettingsWidget">
|
||||||
|
<property name="geometry">
|
||||||
|
<rect>
|
||||||
|
<x>0</x>
|
||||||
|
<y>0</y>
|
||||||
|
<width>502</width>
|
||||||
|
<height>308</height>
|
||||||
|
</rect>
|
||||||
|
</property>
|
||||||
|
<property name="windowTitle">
|
||||||
|
<string>Form</string>
|
||||||
|
</property>
|
||||||
|
<layout class="QGridLayout" name="gridLayout">
|
||||||
|
<property name="leftMargin">
|
||||||
|
<number>0</number>
|
||||||
|
</property>
|
||||||
|
<property name="topMargin">
|
||||||
|
<number>0</number>
|
||||||
|
</property>
|
||||||
|
<property name="rightMargin">
|
||||||
|
<number>0</number>
|
||||||
|
</property>
|
||||||
|
<item row="0" column="0">
|
||||||
|
<layout class="QFormLayout" name="formLayout">
|
||||||
|
<item row="0" column="0">
|
||||||
|
<widget class="QLabel" name="label">
|
||||||
|
<property name="text">
|
||||||
|
<string>Region:</string>
|
||||||
|
</property>
|
||||||
|
</widget>
|
||||||
|
</item>
|
||||||
|
<item row="0" column="1">
|
||||||
|
<widget class="QComboBox" name="region">
|
||||||
|
<item>
|
||||||
|
<property name="text">
|
||||||
|
<string>Auto-Detect</string>
|
||||||
|
</property>
|
||||||
|
</item>
|
||||||
|
<item>
|
||||||
|
<property name="text">
|
||||||
|
<string>NTSC-U (US)</string>
|
||||||
|
</property>
|
||||||
|
</item>
|
||||||
|
<item>
|
||||||
|
<property name="text">
|
||||||
|
<string>NTSC-J (Japan)</string>
|
||||||
|
</property>
|
||||||
|
</item>
|
||||||
|
<item>
|
||||||
|
<property name="text">
|
||||||
|
<string>PAL (Europe, Australia)</string>
|
||||||
|
</property>
|
||||||
|
</item>
|
||||||
|
</widget>
|
||||||
|
</item>
|
||||||
|
<item row="1" column="0">
|
||||||
|
<widget class="QLabel" name="label_2">
|
||||||
|
<property name="text">
|
||||||
|
<string>BIOS Path:</string>
|
||||||
|
</property>
|
||||||
|
</widget>
|
||||||
|
</item>
|
||||||
|
<item row="1" column="1">
|
||||||
|
<layout class="QHBoxLayout" name="horizontalLayout">
|
||||||
|
<item>
|
||||||
|
<widget class="QLineEdit" name="biosPath"/>
|
||||||
|
</item>
|
||||||
|
<item>
|
||||||
|
<widget class="QPushButton" name="biosPathBrowse">
|
||||||
|
<property name="text">
|
||||||
|
<string>...</string>
|
||||||
|
</property>
|
||||||
|
</widget>
|
||||||
|
</item>
|
||||||
|
</layout>
|
||||||
|
</item>
|
||||||
|
<item row="2" column="0" colspan="2">
|
||||||
|
<widget class="QCheckBox" name="enableTTYOutput">
|
||||||
|
<property name="text">
|
||||||
|
<string>Enable TTY Output</string>
|
||||||
|
</property>
|
||||||
|
</widget>
|
||||||
|
</item>
|
||||||
|
<item row="3" column="0" colspan="2">
|
||||||
|
<widget class="QCheckBox" name="fastBoot">
|
||||||
|
<property name="text">
|
||||||
|
<string>Fast Boot</string>
|
||||||
|
</property>
|
||||||
|
</widget>
|
||||||
|
</item>
|
||||||
|
<item row="4" column="0" colspan="2">
|
||||||
|
<widget class="QCheckBox" name="enableSpeedLimiter">
|
||||||
|
<property name="text">
|
||||||
|
<string>Enable Speed Limiter</string>
|
||||||
|
</property>
|
||||||
|
</widget>
|
||||||
|
</item>
|
||||||
|
<item row="5" column="0" colspan="2">
|
||||||
|
<widget class="QCheckBox" name="pauseOnStart">
|
||||||
|
<property name="text">
|
||||||
|
<string>Pause On Start</string>
|
||||||
|
</property>
|
||||||
|
</widget>
|
||||||
|
</item>
|
||||||
|
</layout>
|
||||||
|
</item>
|
||||||
|
</layout>
|
||||||
|
</widget>
|
||||||
|
<resources/>
|
||||||
|
<connections/>
|
||||||
|
</ui>
|
426
src/duckstation-qt/duckstation-qt.vcxproj
Normal file
|
@ -0,0 +1,426 @@
|
||||||
|
<?xml version="1.0" encoding="utf-8"?>
|
||||||
|
<Project DefaultTargets="Build" ToolsVersion="15.0" xmlns="http://schemas.microsoft.com/developer/msbuild/2003">
|
||||||
|
<ItemGroup Label="ProjectConfigurations">
|
||||||
|
<ProjectConfiguration Include="DebugFast|Win32">
|
||||||
|
<Configuration>DebugFast</Configuration>
|
||||||
|
<Platform>Win32</Platform>
|
||||||
|
</ProjectConfiguration>
|
||||||
|
<ProjectConfiguration Include="DebugFast|x64">
|
||||||
|
<Configuration>DebugFast</Configuration>
|
||||||
|
<Platform>x64</Platform>
|
||||||
|
</ProjectConfiguration>
|
||||||
|
<ProjectConfiguration Include="Debug|Win32">
|
||||||
|
<Configuration>Debug</Configuration>
|
||||||
|
<Platform>Win32</Platform>
|
||||||
|
</ProjectConfiguration>
|
||||||
|
<ProjectConfiguration Include="Debug|x64">
|
||||||
|
<Configuration>Debug</Configuration>
|
||||||
|
<Platform>x64</Platform>
|
||||||
|
</ProjectConfiguration>
|
||||||
|
<ProjectConfiguration Include="ReleaseLTCG|Win32">
|
||||||
|
<Configuration>ReleaseLTCG</Configuration>
|
||||||
|
<Platform>Win32</Platform>
|
||||||
|
</ProjectConfiguration>
|
||||||
|
<ProjectConfiguration Include="ReleaseLTCG|x64">
|
||||||
|
<Configuration>ReleaseLTCG</Configuration>
|
||||||
|
<Platform>x64</Platform>
|
||||||
|
</ProjectConfiguration>
|
||||||
|
<ProjectConfiguration Include="Release|Win32">
|
||||||
|
<Configuration>Release</Configuration>
|
||||||
|
<Platform>Win32</Platform>
|
||||||
|
</ProjectConfiguration>
|
||||||
|
<ProjectConfiguration Include="Release|x64">
|
||||||
|
<Configuration>Release</Configuration>
|
||||||
|
<Platform>x64</Platform>
|
||||||
|
</ProjectConfiguration>
|
||||||
|
</ItemGroup>
|
||||||
|
<ItemGroup>
|
||||||
|
<ClCompile Include="consolesettingswidget.cpp" />
|
||||||
|
<ClCompile Include="gamelistsettingswidget.cpp" />
|
||||||
|
<ClCompile Include="gamelistwidget.cpp" />
|
||||||
|
<ClCompile Include="main.cpp" />
|
||||||
|
<ClCompile Include="mainwindow.cpp" />
|
||||||
|
<ClCompile Include="opengldisplaywindow.cpp" />
|
||||||
|
<ClCompile Include="qthostinterface.cpp" />
|
||||||
|
<ClCompile Include="qtsettingsinterface.cpp" />
|
||||||
|
<ClCompile Include="qtutils.cpp" />
|
||||||
|
<ClCompile Include="settingsdialog.cpp" />
|
||||||
|
</ItemGroup>
|
||||||
|
<ItemGroup>
|
||||||
|
<QtMoc Include="consolesettingswidget.h" />
|
||||||
|
<QtMoc Include="gamelistsettingswidget.h" />
|
||||||
|
<QtMoc Include="gamelistwidget.h" />
|
||||||
|
<QtMoc Include="mainwindow.h" />
|
||||||
|
<QtMoc Include="opengldisplaywindow.h" />
|
||||||
|
<QtMoc Include="qthostinterface.h" />
|
||||||
|
<ClInclude Include="qtsettingsinterface.h" />
|
||||||
|
<ClInclude Include="qtutils.h" />
|
||||||
|
<QtMoc Include="settingsdialog.h" />
|
||||||
|
</ItemGroup>
|
||||||
|
<ItemGroup>
|
||||||
|
<ProjectReference Include="..\..\dep\glad\glad.vcxproj">
|
||||||
|
<Project>{43540154-9e1e-409c-834f-b84be5621388}</Project>
|
||||||
|
</ProjectReference>
|
||||||
|
<ProjectReference Include="..\..\dep\imgui\imgui.vcxproj">
|
||||||
|
<Project>{bb08260f-6fbc-46af-8924-090ee71360c6}</Project>
|
||||||
|
</ProjectReference>
|
||||||
|
<ProjectReference Include="..\..\dep\YBaseLib\Source\YBaseLib.vcxproj">
|
||||||
|
<Project>{b56ce698-7300-4fa5-9609-942f1d05c5a2}</Project>
|
||||||
|
</ProjectReference>
|
||||||
|
<ProjectReference Include="..\common\common.vcxproj">
|
||||||
|
<Project>{ee054e08-3799-4a59-a422-18259c105ffd}</Project>
|
||||||
|
</ProjectReference>
|
||||||
|
<ProjectReference Include="..\core\core.vcxproj">
|
||||||
|
<Project>{868b98c8-65a1-494b-8346-250a73a48c0a}</Project>
|
||||||
|
</ProjectReference>
|
||||||
|
</ItemGroup>
|
||||||
|
<ItemGroup>
|
||||||
|
<QtUi Include="consolesettingswidget.ui">
|
||||||
|
<FileType>Document</FileType>
|
||||||
|
</QtUi>
|
||||||
|
<QtUi Include="gamelistsettingswidget.ui">
|
||||||
|
<FileType>Document</FileType>
|
||||||
|
</QtUi>
|
||||||
|
<QtUi Include="mainwindow.ui">
|
||||||
|
<FileType>Document</FileType>
|
||||||
|
</QtUi>
|
||||||
|
<QtUi Include="settingsdialog.ui">
|
||||||
|
<FileType>Document</FileType>
|
||||||
|
</QtUi>
|
||||||
|
</ItemGroup>
|
||||||
|
<ItemGroup>
|
||||||
|
<QtResource Include="resources\icons.qrc">
|
||||||
|
<FileType>Document</FileType>
|
||||||
|
</QtResource>
|
||||||
|
</ItemGroup>
|
||||||
|
<ItemGroup>
|
||||||
|
<ClCompile Include="$(IntDir)moc_consolesettingswidget.cpp" />
|
||||||
|
<ClCompile Include="$(IntDir)moc_gamelistsettingswidget.cpp" />
|
||||||
|
<ClCompile Include="$(IntDir)moc_gamelistwidget.cpp" />
|
||||||
|
<ClCompile Include="$(IntDir)moc_mainwindow.cpp" />
|
||||||
|
<ClCompile Include="$(IntDir)moc_opengldisplaywindow.cpp" />
|
||||||
|
<ClCompile Include="$(IntDir)moc_qthostinterface.cpp" />
|
||||||
|
<ClCompile Include="$(IntDir)moc_settingsdialog.cpp" />
|
||||||
|
<ClCompile Include="$(IntDir)qrc_icons.cpp" />
|
||||||
|
</ItemGroup>
|
||||||
|
<PropertyGroup Label="Globals">
|
||||||
|
<ProjectGuid>{28F14272-0EC4-41BB-849F-182ADB81AF70}</ProjectGuid>
|
||||||
|
<Keyword>Win32Proj</Keyword>
|
||||||
|
<RootNamespace>duckstation-qt</RootNamespace>
|
||||||
|
<WindowsTargetPlatformVersion>10.0</WindowsTargetPlatformVersion>
|
||||||
|
</PropertyGroup>
|
||||||
|
<Import Project="$(VCTargetsPath)\Microsoft.Cpp.Default.props" />
|
||||||
|
<PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Debug|Win32'" Label="Configuration">
|
||||||
|
<ConfigurationType>Application</ConfigurationType>
|
||||||
|
<UseDebugLibraries>true</UseDebugLibraries>
|
||||||
|
<PlatformToolset>v142</PlatformToolset>
|
||||||
|
<CharacterSet>NotSet</CharacterSet>
|
||||||
|
</PropertyGroup>
|
||||||
|
<PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Debug|x64'" Label="Configuration">
|
||||||
|
<ConfigurationType>Application</ConfigurationType>
|
||||||
|
<UseDebugLibraries>true</UseDebugLibraries>
|
||||||
|
<PlatformToolset>v142</PlatformToolset>
|
||||||
|
<CharacterSet>NotSet</CharacterSet>
|
||||||
|
</PropertyGroup>
|
||||||
|
<PropertyGroup Condition="'$(Configuration)|$(Platform)'=='DebugFast|Win32'" Label="Configuration">
|
||||||
|
<ConfigurationType>Application</ConfigurationType>
|
||||||
|
<UseDebugLibraries>true</UseDebugLibraries>
|
||||||
|
<PlatformToolset>v142</PlatformToolset>
|
||||||
|
<CharacterSet>NotSet</CharacterSet>
|
||||||
|
</PropertyGroup>
|
||||||
|
<PropertyGroup Condition="'$(Configuration)|$(Platform)'=='DebugFast|x64'" Label="Configuration">
|
||||||
|
<ConfigurationType>Application</ConfigurationType>
|
||||||
|
<UseDebugLibraries>true</UseDebugLibraries>
|
||||||
|
<PlatformToolset>v142</PlatformToolset>
|
||||||
|
<CharacterSet>NotSet</CharacterSet>
|
||||||
|
</PropertyGroup>
|
||||||
|
<PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Release|Win32'" Label="Configuration">
|
||||||
|
<ConfigurationType>Application</ConfigurationType>
|
||||||
|
<UseDebugLibraries>false</UseDebugLibraries>
|
||||||
|
<PlatformToolset>v142</PlatformToolset>
|
||||||
|
<WholeProgramOptimization>true</WholeProgramOptimization>
|
||||||
|
<CharacterSet>NotSet</CharacterSet>
|
||||||
|
</PropertyGroup>
|
||||||
|
<PropertyGroup Condition="'$(Configuration)|$(Platform)'=='ReleaseLTCG|Win32'" Label="Configuration">
|
||||||
|
<ConfigurationType>Application</ConfigurationType>
|
||||||
|
<UseDebugLibraries>false</UseDebugLibraries>
|
||||||
|
<PlatformToolset>v142</PlatformToolset>
|
||||||
|
<WholeProgramOptimization>true</WholeProgramOptimization>
|
||||||
|
<CharacterSet>NotSet</CharacterSet>
|
||||||
|
</PropertyGroup>
|
||||||
|
<PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Release|x64'" Label="Configuration">
|
||||||
|
<ConfigurationType>Application</ConfigurationType>
|
||||||
|
<UseDebugLibraries>false</UseDebugLibraries>
|
||||||
|
<PlatformToolset>v142</PlatformToolset>
|
||||||
|
<WholeProgramOptimization>true</WholeProgramOptimization>
|
||||||
|
<CharacterSet>NotSet</CharacterSet>
|
||||||
|
</PropertyGroup>
|
||||||
|
<PropertyGroup Condition="'$(Configuration)|$(Platform)'=='ReleaseLTCG|x64'" Label="Configuration">
|
||||||
|
<ConfigurationType>Application</ConfigurationType>
|
||||||
|
<UseDebugLibraries>false</UseDebugLibraries>
|
||||||
|
<PlatformToolset>v142</PlatformToolset>
|
||||||
|
<WholeProgramOptimization>true</WholeProgramOptimization>
|
||||||
|
<CharacterSet>NotSet</CharacterSet>
|
||||||
|
</PropertyGroup>
|
||||||
|
<Import Project="$(VCTargetsPath)\Microsoft.Cpp.props" />
|
||||||
|
<ImportGroup Label="ExtensionSettings">
|
||||||
|
<Import Project="..\..\dep\msvc\vsprops\QtCompile.props" />
|
||||||
|
</ImportGroup>
|
||||||
|
<ImportGroup Label="PropertySheets" Condition="'$(Configuration)|$(Platform)'=='Debug|Win32'">
|
||||||
|
<Import Project="$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props" Condition="exists('$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props')" Label="LocalAppDataPlatform" />
|
||||||
|
</ImportGroup>
|
||||||
|
<ImportGroup Condition="'$(Configuration)|$(Platform)'=='Debug|x64'" Label="PropertySheets">
|
||||||
|
<Import Project="$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props" Condition="exists('$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props')" Label="LocalAppDataPlatform" />
|
||||||
|
</ImportGroup>
|
||||||
|
<ImportGroup Condition="'$(Configuration)|$(Platform)'=='DebugFast|Win32'" Label="PropertySheets">
|
||||||
|
<Import Project="$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props" Condition="exists('$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props')" Label="LocalAppDataPlatform" />
|
||||||
|
</ImportGroup>
|
||||||
|
<ImportGroup Condition="'$(Configuration)|$(Platform)'=='DebugFast|x64'" Label="PropertySheets">
|
||||||
|
<Import Project="$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props" Condition="exists('$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props')" Label="LocalAppDataPlatform" />
|
||||||
|
</ImportGroup>
|
||||||
|
<ImportGroup Label="PropertySheets" Condition="'$(Configuration)|$(Platform)'=='Release|Win32'">
|
||||||
|
<Import Project="$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props" Condition="exists('$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props')" Label="LocalAppDataPlatform" />
|
||||||
|
</ImportGroup>
|
||||||
|
<ImportGroup Condition="'$(Configuration)|$(Platform)'=='ReleaseLTCG|Win32'" Label="PropertySheets">
|
||||||
|
<Import Project="$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props" Condition="exists('$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props')" Label="LocalAppDataPlatform" />
|
||||||
|
</ImportGroup>
|
||||||
|
<ImportGroup Condition="'$(Configuration)|$(Platform)'=='Release|x64'" Label="PropertySheets">
|
||||||
|
<Import Project="$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props" Condition="exists('$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props')" Label="LocalAppDataPlatform" />
|
||||||
|
</ImportGroup>
|
||||||
|
<ImportGroup Condition="'$(Configuration)|$(Platform)'=='ReleaseLTCG|x64'" Label="PropertySheets">
|
||||||
|
<Import Project="$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props" Condition="exists('$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props')" Label="LocalAppDataPlatform" />
|
||||||
|
</ImportGroup>
|
||||||
|
<PropertyGroup Label="UserMacros" />
|
||||||
|
<PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Debug|Win32'">
|
||||||
|
<LinkIncremental>true</LinkIncremental>
|
||||||
|
<OutDir>$(SolutionDir)bin\$(Platform)\</OutDir>
|
||||||
|
<IntDir>$(SolutionDir)build\$(ProjectName)-$(Platform)-$(Configuration)\</IntDir>
|
||||||
|
<TargetName>$(ProjectName)-$(Platform)-$(Configuration)</TargetName>
|
||||||
|
</PropertyGroup>
|
||||||
|
<PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Debug|x64'">
|
||||||
|
<IntDir>$(SolutionDir)build\$(ProjectName)-$(Platform)-$(Configuration)\</IntDir>
|
||||||
|
<TargetName>$(ProjectName)-$(Platform)-$(Configuration)</TargetName>
|
||||||
|
<LinkIncremental>true</LinkIncremental>
|
||||||
|
<OutDir>$(SolutionDir)bin\$(Platform)\</OutDir>
|
||||||
|
</PropertyGroup>
|
||||||
|
<PropertyGroup Condition="'$(Configuration)|$(Platform)'=='DebugFast|Win32'">
|
||||||
|
<LinkIncremental>true</LinkIncremental>
|
||||||
|
<OutDir>$(SolutionDir)bin\$(Platform)\</OutDir>
|
||||||
|
<IntDir>$(SolutionDir)build\$(ProjectName)-$(Platform)-$(Configuration)\</IntDir>
|
||||||
|
<TargetName>$(ProjectName)-$(Platform)-$(Configuration)</TargetName>
|
||||||
|
</PropertyGroup>
|
||||||
|
<PropertyGroup Condition="'$(Configuration)|$(Platform)'=='DebugFast|x64'">
|
||||||
|
<IntDir>$(SolutionDir)build\$(ProjectName)-$(Platform)-$(Configuration)\</IntDir>
|
||||||
|
<TargetName>$(ProjectName)-$(Platform)-$(Configuration)</TargetName>
|
||||||
|
<LinkIncremental>true</LinkIncremental>
|
||||||
|
<OutDir>$(SolutionDir)bin\$(Platform)\</OutDir>
|
||||||
|
</PropertyGroup>
|
||||||
|
<PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Release|Win32'">
|
||||||
|
<LinkIncremental>false</LinkIncremental>
|
||||||
|
<OutDir>$(SolutionDir)bin\$(Platform)\</OutDir>
|
||||||
|
<IntDir>$(SolutionDir)build\$(ProjectName)-$(Platform)-$(Configuration)\</IntDir>
|
||||||
|
<TargetName>$(ProjectName)-$(Platform)-$(Configuration)</TargetName>
|
||||||
|
</PropertyGroup>
|
||||||
|
<PropertyGroup Condition="'$(Configuration)|$(Platform)'=='ReleaseLTCG|Win32'">
|
||||||
|
<LinkIncremental>false</LinkIncremental>
|
||||||
|
<OutDir>$(SolutionDir)bin\$(Platform)\</OutDir>
|
||||||
|
<IntDir>$(SolutionDir)build\$(ProjectName)-$(Platform)-$(Configuration)\</IntDir>
|
||||||
|
<TargetName>$(ProjectName)-$(Platform)-$(Configuration)</TargetName>
|
||||||
|
</PropertyGroup>
|
||||||
|
<PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Release|x64'">
|
||||||
|
<IntDir>$(SolutionDir)build\$(ProjectName)-$(Platform)-$(Configuration)\</IntDir>
|
||||||
|
<TargetName>$(ProjectName)-$(Platform)-$(Configuration)</TargetName>
|
||||||
|
<LinkIncremental>false</LinkIncremental>
|
||||||
|
<OutDir>$(SolutionDir)bin\$(Platform)\</OutDir>
|
||||||
|
</PropertyGroup>
|
||||||
|
<PropertyGroup Condition="'$(Configuration)|$(Platform)'=='ReleaseLTCG|x64'">
|
||||||
|
<IntDir>$(SolutionDir)build\$(ProjectName)-$(Platform)-$(Configuration)\</IntDir>
|
||||||
|
<TargetName>$(ProjectName)-$(Platform)-$(Configuration)</TargetName>
|
||||||
|
<LinkIncremental>false</LinkIncremental>
|
||||||
|
<OutDir>$(SolutionDir)bin\$(Platform)\</OutDir>
|
||||||
|
</PropertyGroup>
|
||||||
|
<ItemDefinitionGroup Condition="'$(Configuration)|$(Platform)'=='Debug|Win32'">
|
||||||
|
<ClCompile>
|
||||||
|
<PrecompiledHeader>
|
||||||
|
</PrecompiledHeader>
|
||||||
|
<WarningLevel>Level4</WarningLevel>
|
||||||
|
<Optimization>Disabled</Optimization>
|
||||||
|
<PreprocessorDefinitions>_CRT_SECURE_NO_WARNINGS;WIN32;_DEBUG;_CONSOLE;_LIB;%(PreprocessorDefinitions)</PreprocessorDefinitions>
|
||||||
|
<SDLCheck>true</SDLCheck>
|
||||||
|
<DebugInformationFormat>ProgramDatabase</DebugInformationFormat>
|
||||||
|
<AdditionalIncludeDirectories>$(SolutionDir)dep\msvc\include;$(SolutionDir)dep\YBaseLib\Include;$(SolutionDir)dep\glad\Include;$(SolutionDir)src;$(SolutionDir)dep\msvc\qt5-x86\include;%(AdditionalIncludeDirectories)</AdditionalIncludeDirectories>
|
||||||
|
<MultiProcessorCompilation>true</MultiProcessorCompilation>
|
||||||
|
<MinimalRebuild>false</MinimalRebuild>
|
||||||
|
<LanguageStandard>stdcpp17</LanguageStandard>
|
||||||
|
</ClCompile>
|
||||||
|
<Link>
|
||||||
|
<SubSystem>Console</SubSystem>
|
||||||
|
<GenerateDebugInformation>true</GenerateDebugInformation>
|
||||||
|
<AdditionalLibraryDirectories>$(SolutionDir)dep\msvc\lib32-debug;$(SolutionDir)dep\msvc\qt5-x86\lib;%(AdditionalLibraryDirectories)</AdditionalLibraryDirectories>
|
||||||
|
<AdditionalDependencies>Qt5Cored.lib;Qt5Guid.lib;Qt5Widgetsd.lib;%(AdditionalDependencies)</AdditionalDependencies>
|
||||||
|
</Link>
|
||||||
|
</ItemDefinitionGroup>
|
||||||
|
<ItemDefinitionGroup Condition="'$(Configuration)|$(Platform)'=='Debug|x64'">
|
||||||
|
<ClCompile>
|
||||||
|
<PrecompiledHeader>
|
||||||
|
</PrecompiledHeader>
|
||||||
|
<WarningLevel>Level4</WarningLevel>
|
||||||
|
<Optimization>Disabled</Optimization>
|
||||||
|
<PreprocessorDefinitions>_CRT_SECURE_NO_WARNINGS;WIN32;_DEBUG;_CONSOLE;_LIB;%(PreprocessorDefinitions)</PreprocessorDefinitions>
|
||||||
|
<SDLCheck>true</SDLCheck>
|
||||||
|
<DebugInformationFormat>ProgramDatabase</DebugInformationFormat>
|
||||||
|
<AdditionalIncludeDirectories>$(SolutionDir)dep\msvc\include;$(SolutionDir)dep\YBaseLib\Include;$(SolutionDir)dep\glad\Include;$(SolutionDir)src;$(SolutionDir)dep\msvc\qt5-x64\include;%(AdditionalIncludeDirectories)</AdditionalIncludeDirectories>
|
||||||
|
<MultiProcessorCompilation>true</MultiProcessorCompilation>
|
||||||
|
<MinimalRebuild>false</MinimalRebuild>
|
||||||
|
<LanguageStandard>stdcpp17</LanguageStandard>
|
||||||
|
</ClCompile>
|
||||||
|
<Link>
|
||||||
|
<SubSystem>Console</SubSystem>
|
||||||
|
<GenerateDebugInformation>true</GenerateDebugInformation>
|
||||||
|
<AdditionalLibraryDirectories>$(SolutionDir)dep\msvc\lib64-debug;$(SolutionDir)dep\msvc\qt5-x64\lib;%(AdditionalLibraryDirectories)</AdditionalLibraryDirectories>
|
||||||
|
<AdditionalDependencies>Qt5Cored.lib;Qt5Guid.lib;Qt5Widgetsd.lib;%(AdditionalDependencies)</AdditionalDependencies>
|
||||||
|
</Link>
|
||||||
|
</ItemDefinitionGroup>
|
||||||
|
<ItemDefinitionGroup Condition="'$(Configuration)|$(Platform)'=='DebugFast|Win32'">
|
||||||
|
<ClCompile>
|
||||||
|
<PrecompiledHeader>
|
||||||
|
</PrecompiledHeader>
|
||||||
|
<WarningLevel>Level4</WarningLevel>
|
||||||
|
<Optimization>Disabled</Optimization>
|
||||||
|
<PreprocessorDefinitions>_ITERATOR_DEBUG_LEVEL=1;_CRT_SECURE_NO_WARNINGS;WIN32;_DEBUGFAST;_DEBUG;_CONSOLE;_LIB;%(PreprocessorDefinitions)</PreprocessorDefinitions>
|
||||||
|
<SDLCheck>true</SDLCheck>
|
||||||
|
<DebugInformationFormat>ProgramDatabase</DebugInformationFormat>
|
||||||
|
<AdditionalIncludeDirectories>$(SolutionDir)dep\msvc\include;$(SolutionDir)dep\YBaseLib\Include;$(SolutionDir)dep\glad\Include;$(SolutionDir)src;$(SolutionDir)dep\msvc\qt5-x86\include;%(AdditionalIncludeDirectories)</AdditionalIncludeDirectories>
|
||||||
|
<BasicRuntimeChecks>Default</BasicRuntimeChecks>
|
||||||
|
<MultiProcessorCompilation>true</MultiProcessorCompilation>
|
||||||
|
<MinimalRebuild>false</MinimalRebuild>
|
||||||
|
<LanguageStandard>stdcpp17</LanguageStandard>
|
||||||
|
<SupportJustMyCode>false</SupportJustMyCode>
|
||||||
|
</ClCompile>
|
||||||
|
<Link>
|
||||||
|
<SubSystem>Console</SubSystem>
|
||||||
|
<GenerateDebugInformation>true</GenerateDebugInformation>
|
||||||
|
<AdditionalLibraryDirectories>$(SolutionDir)dep\msvc\lib32-debug;$(SolutionDir)dep\msvc\qt5-x86\lib;%(AdditionalLibraryDirectories)</AdditionalLibraryDirectories>
|
||||||
|
<AdditionalDependencies>Qt5Cored.lib;Qt5Guid.lib;Qt5Widgetsd.lib;%(AdditionalDependencies)</AdditionalDependencies>
|
||||||
|
</Link>
|
||||||
|
</ItemDefinitionGroup>
|
||||||
|
<ItemDefinitionGroup Condition="'$(Configuration)|$(Platform)'=='DebugFast|x64'">
|
||||||
|
<ClCompile>
|
||||||
|
<PrecompiledHeader>
|
||||||
|
</PrecompiledHeader>
|
||||||
|
<WarningLevel>Level4</WarningLevel>
|
||||||
|
<Optimization>Disabled</Optimization>
|
||||||
|
<PreprocessorDefinitions>_ITERATOR_DEBUG_LEVEL=1;_CRT_SECURE_NO_WARNINGS;WIN32;_DEBUGFAST;_DEBUG;_CONSOLE;_LIB;%(PreprocessorDefinitions)</PreprocessorDefinitions>
|
||||||
|
<SDLCheck>true</SDLCheck>
|
||||||
|
<DebugInformationFormat>ProgramDatabase</DebugInformationFormat>
|
||||||
|
<AdditionalIncludeDirectories>$(SolutionDir)dep\msvc\include;$(SolutionDir)dep\YBaseLib\Include;$(SolutionDir)dep\glad\Include;$(SolutionDir)src;$(SolutionDir)dep\msvc\qt5-x64\include;%(AdditionalIncludeDirectories)</AdditionalIncludeDirectories>
|
||||||
|
<BasicRuntimeChecks>Default</BasicRuntimeChecks>
|
||||||
|
<MultiProcessorCompilation>true</MultiProcessorCompilation>
|
||||||
|
<MinimalRebuild>false</MinimalRebuild>
|
||||||
|
<LanguageStandard>stdcpp17</LanguageStandard>
|
||||||
|
<SupportJustMyCode>false</SupportJustMyCode>
|
||||||
|
</ClCompile>
|
||||||
|
<Link>
|
||||||
|
<SubSystem>Console</SubSystem>
|
||||||
|
<GenerateDebugInformation>true</GenerateDebugInformation>
|
||||||
|
<AdditionalLibraryDirectories>$(SolutionDir)dep\msvc\lib64-debug;$(SolutionDir)dep\msvc\qt5-x64\lib;%(AdditionalLibraryDirectories)</AdditionalLibraryDirectories>
|
||||||
|
<AdditionalDependencies>Qt5Cored.lib;Qt5Guid.lib;Qt5Widgetsd.lib;%(AdditionalDependencies)</AdditionalDependencies>
|
||||||
|
</Link>
|
||||||
|
</ItemDefinitionGroup>
|
||||||
|
<ItemDefinitionGroup Condition="'$(Configuration)|$(Platform)'=='Release|Win32'">
|
||||||
|
<ClCompile>
|
||||||
|
<WarningLevel>Level4</WarningLevel>
|
||||||
|
<PrecompiledHeader>
|
||||||
|
</PrecompiledHeader>
|
||||||
|
<Optimization>MaxSpeed</Optimization>
|
||||||
|
<IntrinsicFunctions>true</IntrinsicFunctions>
|
||||||
|
<PreprocessorDefinitions>_CRT_SECURE_NO_WARNINGS;WIN32;NDEBUG;_CONSOLE;_LIB;%(PreprocessorDefinitions)</PreprocessorDefinitions>
|
||||||
|
<AdditionalIncludeDirectories>$(SolutionDir)dep\msvc\include;$(SolutionDir)dep\YBaseLib\Include;$(SolutionDir)dep\glad\Include;$(SolutionDir)src;$(SolutionDir)dep\msvc\qt5-x86\include;%(AdditionalIncludeDirectories)</AdditionalIncludeDirectories>
|
||||||
|
<MultiProcessorCompilation>true</MultiProcessorCompilation>
|
||||||
|
<WholeProgramOptimization>false</WholeProgramOptimization>
|
||||||
|
<LanguageStandard>stdcpp17</LanguageStandard>
|
||||||
|
</ClCompile>
|
||||||
|
<Link>
|
||||||
|
<SubSystem>Console</SubSystem>
|
||||||
|
<GenerateDebugInformation>true</GenerateDebugInformation>
|
||||||
|
<EnableCOMDATFolding>true</EnableCOMDATFolding>
|
||||||
|
<OptimizeReferences>true</OptimizeReferences>
|
||||||
|
<AdditionalLibraryDirectories>$(SolutionDir)dep\msvc\lib32;$(SolutionDir)dep\msvc\qt5-x86\lib;%(AdditionalLibraryDirectories)</AdditionalLibraryDirectories>
|
||||||
|
<AdditionalDependencies>Qt5Core.lib;Qt5Gui.lib;Qt5Widgets.lib;%(AdditionalDependencies)</AdditionalDependencies>
|
||||||
|
</Link>
|
||||||
|
</ItemDefinitionGroup>
|
||||||
|
<ItemDefinitionGroup Condition="'$(Configuration)|$(Platform)'=='ReleaseLTCG|Win32'">
|
||||||
|
<ClCompile>
|
||||||
|
<WarningLevel>Level4</WarningLevel>
|
||||||
|
<PrecompiledHeader>
|
||||||
|
</PrecompiledHeader>
|
||||||
|
<Optimization>MaxSpeed</Optimization>
|
||||||
|
<IntrinsicFunctions>true</IntrinsicFunctions>
|
||||||
|
<PreprocessorDefinitions>_CRT_SECURE_NO_WARNINGS;WIN32;NDEBUG;_CONSOLE;_LIB;%(PreprocessorDefinitions)</PreprocessorDefinitions>
|
||||||
|
<AdditionalIncludeDirectories>$(SolutionDir)dep\msvc\include;$(SolutionDir)dep\YBaseLib\Include;$(SolutionDir)dep\glad\Include;$(SolutionDir)src;$(SolutionDir)dep\msvc\qt5-x86\include;%(AdditionalIncludeDirectories)</AdditionalIncludeDirectories>
|
||||||
|
<MultiProcessorCompilation>true</MultiProcessorCompilation>
|
||||||
|
<WholeProgramOptimization>true</WholeProgramOptimization>
|
||||||
|
<LanguageStandard>stdcpp17</LanguageStandard>
|
||||||
|
<OmitFramePointers>true</OmitFramePointers>
|
||||||
|
</ClCompile>
|
||||||
|
<Link>
|
||||||
|
<SubSystem>Console</SubSystem>
|
||||||
|
<GenerateDebugInformation>true</GenerateDebugInformation>
|
||||||
|
<EnableCOMDATFolding>true</EnableCOMDATFolding>
|
||||||
|
<OptimizeReferences>true</OptimizeReferences>
|
||||||
|
<AdditionalLibraryDirectories>$(SolutionDir)dep\msvc\lib32;$(SolutionDir)dep\msvc\qt5-x86\lib;%(AdditionalLibraryDirectories)</AdditionalLibraryDirectories>
|
||||||
|
<AdditionalDependencies>Qt5Core.lib;Qt5Gui.lib;Qt5Widgets.lib;%(AdditionalDependencies)</AdditionalDependencies>
|
||||||
|
<LinkTimeCodeGeneration>UseLinkTimeCodeGeneration</LinkTimeCodeGeneration>
|
||||||
|
</Link>
|
||||||
|
</ItemDefinitionGroup>
|
||||||
|
<ItemDefinitionGroup Condition="'$(Configuration)|$(Platform)'=='Release|x64'">
|
||||||
|
<ClCompile>
|
||||||
|
<WarningLevel>Level4</WarningLevel>
|
||||||
|
<PrecompiledHeader>
|
||||||
|
</PrecompiledHeader>
|
||||||
|
<Optimization>MaxSpeed</Optimization>
|
||||||
|
<IntrinsicFunctions>true</IntrinsicFunctions>
|
||||||
|
<PreprocessorDefinitions>_CRT_SECURE_NO_WARNINGS;WIN32;NDEBUG;_CONSOLE;_LIB;%(PreprocessorDefinitions)</PreprocessorDefinitions>
|
||||||
|
<AdditionalIncludeDirectories>$(SolutionDir)dep\msvc\include;$(SolutionDir)dep\YBaseLib\Include;$(SolutionDir)dep\glad\Include;$(SolutionDir)src;$(SolutionDir)dep\msvc\qt5-x64\include;%(AdditionalIncludeDirectories)</AdditionalIncludeDirectories>
|
||||||
|
<MultiProcessorCompilation>true</MultiProcessorCompilation>
|
||||||
|
<WholeProgramOptimization>false</WholeProgramOptimization>
|
||||||
|
<LanguageStandard>stdcpp17</LanguageStandard>
|
||||||
|
</ClCompile>
|
||||||
|
<Link>
|
||||||
|
<SubSystem>Console</SubSystem>
|
||||||
|
<GenerateDebugInformation>true</GenerateDebugInformation>
|
||||||
|
<EnableCOMDATFolding>true</EnableCOMDATFolding>
|
||||||
|
<OptimizeReferences>true</OptimizeReferences>
|
||||||
|
<AdditionalLibraryDirectories>$(SolutionDir)dep\msvc\lib64;$(SolutionDir)dep\msvc\qt5-x64\lib;%(AdditionalLibraryDirectories)</AdditionalLibraryDirectories>
|
||||||
|
<AdditionalDependencies>Qt5Core.lib;Qt5Gui.lib;Qt5Widgets.lib;%(AdditionalDependencies)</AdditionalDependencies>
|
||||||
|
</Link>
|
||||||
|
</ItemDefinitionGroup>
|
||||||
|
<ItemDefinitionGroup Condition="'$(Configuration)|$(Platform)'=='ReleaseLTCG|x64'">
|
||||||
|
<ClCompile>
|
||||||
|
<WarningLevel>Level4</WarningLevel>
|
||||||
|
<PrecompiledHeader>
|
||||||
|
</PrecompiledHeader>
|
||||||
|
<Optimization>MaxSpeed</Optimization>
|
||||||
|
<IntrinsicFunctions>true</IntrinsicFunctions>
|
||||||
|
<PreprocessorDefinitions>_CRT_SECURE_NO_WARNINGS;WIN32;NDEBUG;_CONSOLE;_LIB;%(PreprocessorDefinitions)</PreprocessorDefinitions>
|
||||||
|
<AdditionalIncludeDirectories>$(SolutionDir)dep\msvc\include;$(SolutionDir)dep\YBaseLib\Include;$(SolutionDir)dep\glad\Include;$(SolutionDir)src;$(SolutionDir)dep\msvc\qt5-x64\include;%(AdditionalIncludeDirectories)</AdditionalIncludeDirectories>
|
||||||
|
<MultiProcessorCompilation>true</MultiProcessorCompilation>
|
||||||
|
<WholeProgramOptimization>true</WholeProgramOptimization>
|
||||||
|
<LanguageStandard>stdcpp17</LanguageStandard>
|
||||||
|
<OmitFramePointers>true</OmitFramePointers>
|
||||||
|
</ClCompile>
|
||||||
|
<Link>
|
||||||
|
<SubSystem>Console</SubSystem>
|
||||||
|
<GenerateDebugInformation>true</GenerateDebugInformation>
|
||||||
|
<EnableCOMDATFolding>true</EnableCOMDATFolding>
|
||||||
|
<OptimizeReferences>true</OptimizeReferences>
|
||||||
|
<AdditionalLibraryDirectories>$(SolutionDir)dep\msvc\lib64;$(SolutionDir)dep\msvc\qt5-x64\lib;%(AdditionalLibraryDirectories)</AdditionalLibraryDirectories>
|
||||||
|
<AdditionalDependencies>Qt5Core.lib;Qt5Gui.lib;Qt5Widgets.lib;%(AdditionalDependencies)</AdditionalDependencies>
|
||||||
|
<LinkTimeCodeGeneration>UseLinkTimeCodeGeneration</LinkTimeCodeGeneration>
|
||||||
|
</Link>
|
||||||
|
</ItemDefinitionGroup>
|
||||||
|
<Import Project="$(VCTargetsPath)\Microsoft.Cpp.targets" />
|
||||||
|
<ImportGroup Label="ExtensionTargets">
|
||||||
|
<Import Project="..\..\dep\msvc\vsprops\QtCompile.targets" />
|
||||||
|
</ImportGroup>
|
||||||
|
</Project>
|
40
src/duckstation-qt/duckstation-qt.vcxproj.filters
Normal file
|
@ -0,0 +1,40 @@
|
||||||
|
<?xml version="1.0" encoding="utf-8"?>
|
||||||
|
<Project ToolsVersion="4.0" xmlns="http://schemas.microsoft.com/developer/msbuild/2003">
|
||||||
|
<ItemGroup>
|
||||||
|
<ClCompile Include="main.cpp" />
|
||||||
|
<ClCompile Include="mainwindow.cpp" />
|
||||||
|
<ClCompile Include="gamelistwidget.cpp" />
|
||||||
|
<ClCompile Include="settingsdialog.cpp" />
|
||||||
|
<ClCompile Include="consolesettingswidget.cpp" />
|
||||||
|
<ClCompile Include="opengldisplaywindow.cpp" />
|
||||||
|
<ClCompile Include="qthostinterface.cpp" />
|
||||||
|
<ClCompile Include="gamelistsettingswidget.cpp" />
|
||||||
|
<ClCompile Include="qtsettingsinterface.cpp" />
|
||||||
|
<ClCompile Include="qtutils.cpp" />
|
||||||
|
</ItemGroup>
|
||||||
|
<ItemGroup>
|
||||||
|
<ClInclude Include="mainwindow.h" />
|
||||||
|
<ClInclude Include="gamelistwidget.h" />
|
||||||
|
<ClInclude Include="settingsdialog.h" />
|
||||||
|
<ClInclude Include="consolesettingswidget.h" />
|
||||||
|
<ClInclude Include="opengldisplaywindow.h" />
|
||||||
|
<ClInclude Include="qthostinterface.h" />
|
||||||
|
<ClInclude Include="gamelistsettingswidget.h" />
|
||||||
|
<ClInclude Include="qtsettingsinterface.h" />
|
||||||
|
<ClInclude Include="qtutils.h" />
|
||||||
|
</ItemGroup>
|
||||||
|
<ItemGroup>
|
||||||
|
<None Include="consolesettingswidget.ui" />
|
||||||
|
<None Include="gamelistsettingswidget.ui" />
|
||||||
|
<None Include="mainwindow.ui" />
|
||||||
|
<None Include="settingsdialog.ui" />
|
||||||
|
<None Include="resources\icons.qrc">
|
||||||
|
<Filter>resources</Filter>
|
||||||
|
</None>
|
||||||
|
</ItemGroup>
|
||||||
|
<ItemGroup>
|
||||||
|
<Filter Include="resources">
|
||||||
|
<UniqueIdentifier>{3b2587ae-ce3b-4eb5-ada2-237e853620cf}</UniqueIdentifier>
|
||||||
|
</Filter>
|
||||||
|
</ItemGroup>
|
||||||
|
</Project>
|
230
src/duckstation-qt/gamelistsettingswidget.cpp
Normal file
|
@ -0,0 +1,230 @@
|
||||||
|
#include "gamelistsettingswidget.h"
|
||||||
|
#include "qthostinterface.h"
|
||||||
|
#include "qtutils.h"
|
||||||
|
#include <QtCore/QAbstractTableModel>
|
||||||
|
#include <QtCore/QSettings>
|
||||||
|
#include <QtWidgets/QFileDialog>
|
||||||
|
#include <QtWidgets/QHeaderView>
|
||||||
|
#include <QtWidgets/QMessageBox>
|
||||||
|
#include <algorithm>
|
||||||
|
|
||||||
|
class GameListSearchDirectoriesModel : public QAbstractTableModel
|
||||||
|
{
|
||||||
|
public:
|
||||||
|
GameListSearchDirectoriesModel(QSettings& settings) : m_settings(settings) {}
|
||||||
|
|
||||||
|
~GameListSearchDirectoriesModel() = default;
|
||||||
|
|
||||||
|
int columnCount(const QModelIndex& parent) const override
|
||||||
|
{
|
||||||
|
if (parent.isValid())
|
||||||
|
return 0;
|
||||||
|
|
||||||
|
return 2;
|
||||||
|
}
|
||||||
|
|
||||||
|
QVariant headerData(int section, Qt::Orientation orientation, int role = Qt::DisplayRole) const override
|
||||||
|
{
|
||||||
|
if (orientation != Qt::Horizontal || role != Qt::DisplayRole)
|
||||||
|
return {};
|
||||||
|
|
||||||
|
if (section == 0)
|
||||||
|
return tr("Path");
|
||||||
|
else
|
||||||
|
return tr("Recursive");
|
||||||
|
}
|
||||||
|
|
||||||
|
int rowCount(const QModelIndex& parent) const override
|
||||||
|
{
|
||||||
|
if (parent.isValid())
|
||||||
|
return 0;
|
||||||
|
|
||||||
|
return static_cast<int>(m_entries.size());
|
||||||
|
}
|
||||||
|
|
||||||
|
QVariant data(const QModelIndex& index, int role = Qt::DisplayRole) const override
|
||||||
|
{
|
||||||
|
if (!index.isValid())
|
||||||
|
return {};
|
||||||
|
|
||||||
|
const int row = index.row();
|
||||||
|
const int column = index.column();
|
||||||
|
if (row < 0 || row >= static_cast<int>(m_entries.size()))
|
||||||
|
return {};
|
||||||
|
|
||||||
|
const Entry& entry = m_entries[row];
|
||||||
|
if (role == Qt::CheckStateRole)
|
||||||
|
{
|
||||||
|
if (column == 1)
|
||||||
|
return entry.recursive ? Qt::Checked : Qt::Unchecked;
|
||||||
|
}
|
||||||
|
else if (role == Qt::DisplayRole)
|
||||||
|
{
|
||||||
|
if (column == 0)
|
||||||
|
return entry.path;
|
||||||
|
}
|
||||||
|
|
||||||
|
return {};
|
||||||
|
}
|
||||||
|
|
||||||
|
void addEntry(const QString& path, bool recursive)
|
||||||
|
{
|
||||||
|
if (std::find_if(m_entries.begin(), m_entries.end(), [path](const Entry& e) { return e.path == path; }) !=
|
||||||
|
m_entries.end())
|
||||||
|
{
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
beginInsertRows(QModelIndex(), static_cast<int>(m_entries.size()), static_cast<int>(m_entries.size() + 1));
|
||||||
|
m_entries.push_back({path, recursive});
|
||||||
|
endInsertRows();
|
||||||
|
}
|
||||||
|
|
||||||
|
void removeEntry(int row)
|
||||||
|
{
|
||||||
|
if (row < 0 || row >= static_cast<int>(m_entries.size()))
|
||||||
|
return;
|
||||||
|
|
||||||
|
beginRemoveRows(QModelIndex(), row, row);
|
||||||
|
m_entries.erase(m_entries.begin() + row);
|
||||||
|
endRemoveRows();
|
||||||
|
}
|
||||||
|
|
||||||
|
void loadFromSettings()
|
||||||
|
{
|
||||||
|
QStringList path_list = m_settings.value(QStringLiteral("GameList/Paths")).toStringList();
|
||||||
|
for (QString& entry : path_list)
|
||||||
|
m_entries.push_back({std::move(entry), false});
|
||||||
|
|
||||||
|
path_list = m_settings.value(QStringLiteral("GameList/RecursivePaths")).toStringList();
|
||||||
|
for (QString& entry : path_list)
|
||||||
|
m_entries.push_back({std::move(entry), true});
|
||||||
|
}
|
||||||
|
|
||||||
|
void saveToSettings()
|
||||||
|
{
|
||||||
|
QStringList paths;
|
||||||
|
QStringList recursive_paths;
|
||||||
|
|
||||||
|
for (const Entry& entry : m_entries)
|
||||||
|
{
|
||||||
|
if (entry.recursive)
|
||||||
|
recursive_paths.push_back(entry.path);
|
||||||
|
else
|
||||||
|
paths.push_back(entry.path);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (paths.empty())
|
||||||
|
m_settings.remove(QStringLiteral("GameList/Paths"));
|
||||||
|
else
|
||||||
|
m_settings.setValue(QStringLiteral("GameList/Paths"), paths);
|
||||||
|
|
||||||
|
if (recursive_paths.empty())
|
||||||
|
m_settings.remove(QStringLiteral("GameList/RecursivePaths"));
|
||||||
|
else
|
||||||
|
m_settings.setValue(QStringLiteral("GameList/RecursivePaths"), recursive_paths);
|
||||||
|
}
|
||||||
|
|
||||||
|
private:
|
||||||
|
struct Entry
|
||||||
|
{
|
||||||
|
QString path;
|
||||||
|
bool recursive;
|
||||||
|
};
|
||||||
|
|
||||||
|
QSettings& m_settings;
|
||||||
|
std::vector<Entry> m_entries;
|
||||||
|
};
|
||||||
|
|
||||||
|
GameListSettingsWidget::GameListSettingsWidget(QtHostInterface* host_interface, QWidget* parent /* = nullptr */)
|
||||||
|
: QWidget(parent), m_host_interface(host_interface)
|
||||||
|
{
|
||||||
|
m_ui.setupUi(this);
|
||||||
|
|
||||||
|
QSettings& qsettings = host_interface->getQSettings();
|
||||||
|
|
||||||
|
m_search_directories_model = new GameListSearchDirectoriesModel(qsettings);
|
||||||
|
m_search_directories_model->loadFromSettings();
|
||||||
|
m_ui.redumpDatabasePath->setText(qsettings.value("GameList/RedumpDatabasePath").toString());
|
||||||
|
m_ui.searchDirectoryList->setModel(m_search_directories_model);
|
||||||
|
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->verticalHeader()->hide();
|
||||||
|
m_ui.searchDirectoryList->setCurrentIndex({});
|
||||||
|
|
||||||
|
connect(m_ui.addSearchDirectoryButton, &QToolButton::pressed, this,
|
||||||
|
&GameListSettingsWidget::onAddSearchDirectoryButtonPressed);
|
||||||
|
connect(m_ui.removeSearchDirectoryButton, &QToolButton::pressed, this,
|
||||||
|
&GameListSettingsWidget::onRemoveSearchDirectoryButtonPressed);
|
||||||
|
connect(m_ui.refreshGameListButton, &QToolButton::pressed, this,
|
||||||
|
&GameListSettingsWidget::onRefreshGameListButtonPressed);
|
||||||
|
connect(m_ui.browseRedumpPath, &QToolButton::pressed, this, &GameListSettingsWidget::onBrowseRedumpPathButtonPressed);
|
||||||
|
connect(m_ui.downloadRedumpDatabase, &QToolButton::pressed, this,
|
||||||
|
&GameListSettingsWidget::onDownloadRedumpDatabaseButtonPressed);
|
||||||
|
}
|
||||||
|
|
||||||
|
GameListSettingsWidget::~GameListSettingsWidget() = default;
|
||||||
|
|
||||||
|
void GameListSettingsWidget::resizeEvent(QResizeEvent* event)
|
||||||
|
{
|
||||||
|
QWidget::resizeEvent(event);
|
||||||
|
|
||||||
|
QtUtils::ResizeColumnsForTableView(m_ui.searchDirectoryList, {-1, 100});
|
||||||
|
}
|
||||||
|
|
||||||
|
void GameListSettingsWidget::onAddSearchDirectoryButtonPressed()
|
||||||
|
{
|
||||||
|
QString dir = QFileDialog::getExistingDirectory(this, 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);
|
||||||
|
m_search_directories_model->addEntry(dir, recursive);
|
||||||
|
m_search_directories_model->saveToSettings();
|
||||||
|
m_host_interface->refreshGameList(false);
|
||||||
|
}
|
||||||
|
|
||||||
|
void GameListSettingsWidget::onRemoveSearchDirectoryButtonPressed()
|
||||||
|
{
|
||||||
|
QModelIndexList selection = m_ui.searchDirectoryList->selectionModel()->selectedIndexes();
|
||||||
|
if (selection.size() < 1)
|
||||||
|
return;
|
||||||
|
|
||||||
|
const int row = selection[0].row();
|
||||||
|
m_search_directories_model->removeEntry(row);
|
||||||
|
m_search_directories_model->saveToSettings();
|
||||||
|
m_host_interface->refreshGameList(false);
|
||||||
|
}
|
||||||
|
|
||||||
|
void GameListSettingsWidget::onRefreshGameListButtonPressed()
|
||||||
|
{
|
||||||
|
m_host_interface->refreshGameList(true);
|
||||||
|
}
|
||||||
|
|
||||||
|
void GameListSettingsWidget::onBrowseRedumpPathButtonPressed()
|
||||||
|
{
|
||||||
|
QString filename = QFileDialog::getOpenFileName(this, tr("Select Redump Database File"), QString(),
|
||||||
|
tr("Redump Database Files (*.dat)"));
|
||||||
|
if (filename.isEmpty())
|
||||||
|
return;
|
||||||
|
|
||||||
|
m_ui.redumpDatabasePath->setText(filename);
|
||||||
|
m_host_interface->getQSettings().setValue("GameList/RedumpDatabasePath", filename);
|
||||||
|
m_host_interface->updateGameListDatabase(true);
|
||||||
|
}
|
||||||
|
|
||||||
|
void GameListSettingsWidget::onDownloadRedumpDatabaseButtonPressed()
|
||||||
|
{
|
||||||
|
QMessageBox::information(this, tr("TODO"), tr("TODO"));
|
||||||
|
}
|
35
src/duckstation-qt/gamelistsettingswidget.h
Normal file
|
@ -0,0 +1,35 @@
|
||||||
|
#pragma once
|
||||||
|
|
||||||
|
#include <QtWidgets/QWidget>
|
||||||
|
|
||||||
|
#include "ui_gamelistsettingswidget.h"
|
||||||
|
|
||||||
|
class QtHostInterface;
|
||||||
|
|
||||||
|
class GameListSearchDirectoriesModel;
|
||||||
|
|
||||||
|
class GameListSettingsWidget : public QWidget
|
||||||
|
{
|
||||||
|
Q_OBJECT
|
||||||
|
|
||||||
|
public:
|
||||||
|
GameListSettingsWidget(QtHostInterface* host_interface, QWidget* parent = nullptr);
|
||||||
|
~GameListSettingsWidget();
|
||||||
|
|
||||||
|
private Q_SLOTS:
|
||||||
|
void onAddSearchDirectoryButtonPressed();
|
||||||
|
void onRemoveSearchDirectoryButtonPressed();
|
||||||
|
void onRefreshGameListButtonPressed();
|
||||||
|
void onBrowseRedumpPathButtonPressed();
|
||||||
|
void onDownloadRedumpDatabaseButtonPressed();
|
||||||
|
|
||||||
|
protected:
|
||||||
|
void resizeEvent(QResizeEvent* event);
|
||||||
|
|
||||||
|
private:
|
||||||
|
QtHostInterface* m_host_interface;
|
||||||
|
|
||||||
|
Ui::GameListSettingsWidget m_ui;
|
||||||
|
|
||||||
|
GameListSearchDirectoriesModel* m_search_directories_model = nullptr;
|
||||||
|
};
|
150
src/duckstation-qt/gamelistsettingswidget.ui
Normal file
|
@ -0,0 +1,150 @@
|
||||||
|
<?xml version="1.0" encoding="UTF-8"?>
|
||||||
|
<ui version="4.0">
|
||||||
|
<class>GameListSettingsWidget</class>
|
||||||
|
<widget class="QWidget" name="GameListSettingsWidget">
|
||||||
|
<property name="geometry">
|
||||||
|
<rect>
|
||||||
|
<x>0</x>
|
||||||
|
<y>0</y>
|
||||||
|
<width>527</width>
|
||||||
|
<height>376</height>
|
||||||
|
</rect>
|
||||||
|
</property>
|
||||||
|
<property name="windowTitle">
|
||||||
|
<string>Form</string>
|
||||||
|
</property>
|
||||||
|
<layout class="QGridLayout" name="gridLayout">
|
||||||
|
<property name="leftMargin">
|
||||||
|
<number>0</number>
|
||||||
|
</property>
|
||||||
|
<property name="topMargin">
|
||||||
|
<number>0</number>
|
||||||
|
</property>
|
||||||
|
<property name="rightMargin">
|
||||||
|
<number>0</number>
|
||||||
|
</property>
|
||||||
|
<property name="bottomMargin">
|
||||||
|
<number>0</number>
|
||||||
|
</property>
|
||||||
|
<item row="0" column="0">
|
||||||
|
<layout class="QVBoxLayout" name="verticalLayout">
|
||||||
|
<item>
|
||||||
|
<widget class="QLabel" name="label">
|
||||||
|
<property name="text">
|
||||||
|
<string>Search Directories</string>
|
||||||
|
</property>
|
||||||
|
</widget>
|
||||||
|
</item>
|
||||||
|
<item>
|
||||||
|
<widget class="QTableView" name="searchDirectoryList"/>
|
||||||
|
</item>
|
||||||
|
<item>
|
||||||
|
<layout class="QHBoxLayout" name="horizontalLayout">
|
||||||
|
<item>
|
||||||
|
<widget class="QToolButton" name="addSearchDirectoryButton">
|
||||||
|
<property name="text">
|
||||||
|
<string>Add</string>
|
||||||
|
</property>
|
||||||
|
<property name="icon">
|
||||||
|
<iconset resource="icons.qrc">
|
||||||
|
<normaloff>:/icons/list-add.png</normaloff>:/icons/list-add.png</iconset>
|
||||||
|
</property>
|
||||||
|
<property name="toolButtonStyle">
|
||||||
|
<enum>Qt::ToolButtonTextBesideIcon</enum>
|
||||||
|
</property>
|
||||||
|
</widget>
|
||||||
|
</item>
|
||||||
|
<item>
|
||||||
|
<widget class="QToolButton" name="removeSearchDirectoryButton">
|
||||||
|
<property name="text">
|
||||||
|
<string>Remove</string>
|
||||||
|
</property>
|
||||||
|
<property name="icon">
|
||||||
|
<iconset resource="icons.qrc">
|
||||||
|
<normaloff>:/icons/list-remove.png</normaloff>:/icons/list-remove.png</iconset>
|
||||||
|
</property>
|
||||||
|
<property name="toolButtonStyle">
|
||||||
|
<enum>Qt::ToolButtonTextBesideIcon</enum>
|
||||||
|
</property>
|
||||||
|
</widget>
|
||||||
|
</item>
|
||||||
|
<item>
|
||||||
|
<spacer name="horizontalSpacer">
|
||||||
|
<property name="orientation">
|
||||||
|
<enum>Qt::Horizontal</enum>
|
||||||
|
</property>
|
||||||
|
<property name="sizeHint" stdset="0">
|
||||||
|
<size>
|
||||||
|
<width>40</width>
|
||||||
|
<height>20</height>
|
||||||
|
</size>
|
||||||
|
</property>
|
||||||
|
</spacer>
|
||||||
|
</item>
|
||||||
|
<item>
|
||||||
|
<widget class="QToolButton" name="refreshGameListButton">
|
||||||
|
<property name="text">
|
||||||
|
<string>Refresh</string>
|
||||||
|
</property>
|
||||||
|
<property name="icon">
|
||||||
|
<iconset resource="icons.qrc">
|
||||||
|
<normaloff>:/icons/view-refresh.png</normaloff>:/icons/view-refresh.png</iconset>
|
||||||
|
</property>
|
||||||
|
<property name="toolButtonStyle">
|
||||||
|
<enum>Qt::ToolButtonTextBesideIcon</enum>
|
||||||
|
</property>
|
||||||
|
</widget>
|
||||||
|
</item>
|
||||||
|
</layout>
|
||||||
|
</item>
|
||||||
|
<item>
|
||||||
|
<widget class="QLabel" name="label_2">
|
||||||
|
<property name="text">
|
||||||
|
<string>Redump Database Path</string>
|
||||||
|
</property>
|
||||||
|
</widget>
|
||||||
|
</item>
|
||||||
|
<item>
|
||||||
|
<layout class="QHBoxLayout" name="horizontalLayout_2">
|
||||||
|
<item>
|
||||||
|
<widget class="QLineEdit" name="redumpDatabasePath"/>
|
||||||
|
</item>
|
||||||
|
<item>
|
||||||
|
<widget class="QToolButton" name="browseRedumpPath">
|
||||||
|
<property name="text">
|
||||||
|
<string>Browse...</string>
|
||||||
|
</property>
|
||||||
|
<property name="icon">
|
||||||
|
<iconset resource="icons.qrc">
|
||||||
|
<normaloff>:/icons/system-search.png</normaloff>:/icons/system-search.png</iconset>
|
||||||
|
</property>
|
||||||
|
<property name="toolButtonStyle">
|
||||||
|
<enum>Qt::ToolButtonTextBesideIcon</enum>
|
||||||
|
</property>
|
||||||
|
<property name="autoRaise">
|
||||||
|
<bool>false</bool>
|
||||||
|
</property>
|
||||||
|
</widget>
|
||||||
|
</item>
|
||||||
|
<item>
|
||||||
|
<widget class="QPushButton" name="downloadRedumpDatabase">
|
||||||
|
<property name="text">
|
||||||
|
<string>Download...</string>
|
||||||
|
</property>
|
||||||
|
<property name="icon">
|
||||||
|
<iconset resource="icons.qrc">
|
||||||
|
<normaloff>:/icons/applications-internet.png</normaloff>:/icons/applications-internet.png</iconset>
|
||||||
|
</property>
|
||||||
|
</widget>
|
||||||
|
</item>
|
||||||
|
</layout>
|
||||||
|
</item>
|
||||||
|
</layout>
|
||||||
|
</item>
|
||||||
|
</layout>
|
||||||
|
</widget>
|
||||||
|
<resources>
|
||||||
|
<include location="icons.qrc"/>
|
||||||
|
</resources>
|
||||||
|
<connections/>
|
||||||
|
</ui>
|
164
src/duckstation-qt/gamelistwidget.cpp
Normal file
|
@ -0,0 +1,164 @@
|
||||||
|
#include "gamelistwidget.h"
|
||||||
|
#include "core/settings.h"
|
||||||
|
#include "qthostinterface.h"
|
||||||
|
#include "qtutils.h"
|
||||||
|
#include <QtWidgets/QHeaderView>
|
||||||
|
|
||||||
|
class GameListModel : public QAbstractTableModel
|
||||||
|
{
|
||||||
|
public:
|
||||||
|
enum Column : int
|
||||||
|
{
|
||||||
|
// Column_Icon,
|
||||||
|
Column_Code,
|
||||||
|
Column_Title,
|
||||||
|
Column_Region,
|
||||||
|
Column_Size,
|
||||||
|
|
||||||
|
Column_Count
|
||||||
|
};
|
||||||
|
|
||||||
|
GameListModel(GameList* game_list, QObject* parent = nullptr)
|
||||||
|
: QAbstractTableModel(parent), m_game_list(game_list), m_size(static_cast<int>(m_game_list->GetEntryCount()))
|
||||||
|
{
|
||||||
|
}
|
||||||
|
~GameListModel() = default;
|
||||||
|
|
||||||
|
int rowCount(const QModelIndex& parent = QModelIndex()) const override
|
||||||
|
{
|
||||||
|
if (parent.isValid())
|
||||||
|
return 0;
|
||||||
|
|
||||||
|
return static_cast<int>(m_game_list->GetEntryCount());
|
||||||
|
}
|
||||||
|
|
||||||
|
int columnCount(const QModelIndex& parent = QModelIndex()) const override
|
||||||
|
{
|
||||||
|
if (parent.isValid())
|
||||||
|
return 0;
|
||||||
|
|
||||||
|
return Column_Count;
|
||||||
|
}
|
||||||
|
|
||||||
|
QVariant data(const QModelIndex& index, int role = Qt::DisplayRole) const override
|
||||||
|
{
|
||||||
|
if (!index.isValid())
|
||||||
|
return {};
|
||||||
|
|
||||||
|
if (role != Qt::DisplayRole)
|
||||||
|
return {};
|
||||||
|
|
||||||
|
const int row = index.row();
|
||||||
|
if (row < 0 || row >= static_cast<int>(m_game_list->GetEntryCount()))
|
||||||
|
return {};
|
||||||
|
|
||||||
|
const GameList::GameListEntry& ge = m_game_list->GetEntries()[row];
|
||||||
|
switch (index.column())
|
||||||
|
{
|
||||||
|
case Column_Code:
|
||||||
|
return QString::fromStdString(ge.code);
|
||||||
|
|
||||||
|
case Column_Title:
|
||||||
|
return QString::fromStdString(ge.title);
|
||||||
|
|
||||||
|
case Column_Region:
|
||||||
|
return QString(Settings::GetConsoleRegionName(ge.region));
|
||||||
|
|
||||||
|
case Column_Size:
|
||||||
|
return QString("%1 MB").arg(static_cast<double>(ge.total_size) / 1048576.0, 0, 'f', 2);
|
||||||
|
|
||||||
|
default:
|
||||||
|
return {};
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
QVariant headerData(int section, Qt::Orientation orientation, int role = Qt::DisplayRole) const override
|
||||||
|
{
|
||||||
|
if (orientation != Qt::Horizontal || role != Qt::DisplayRole)
|
||||||
|
return {};
|
||||||
|
|
||||||
|
switch (section)
|
||||||
|
{
|
||||||
|
case Column_Code:
|
||||||
|
return "Code";
|
||||||
|
|
||||||
|
case Column_Title:
|
||||||
|
return "Title";
|
||||||
|
|
||||||
|
case Column_Region:
|
||||||
|
return "Region";
|
||||||
|
|
||||||
|
case Column_Size:
|
||||||
|
return "Size";
|
||||||
|
|
||||||
|
default:
|
||||||
|
return {};
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void refresh()
|
||||||
|
{
|
||||||
|
if (m_size > 0)
|
||||||
|
{
|
||||||
|
beginRemoveRows(QModelIndex(), 0, m_size - 1);
|
||||||
|
endRemoveRows();
|
||||||
|
}
|
||||||
|
|
||||||
|
m_size = static_cast<int>(m_game_list->GetEntryCount());
|
||||||
|
beginInsertRows(QModelIndex(), 0, m_size);
|
||||||
|
endInsertRows();
|
||||||
|
}
|
||||||
|
|
||||||
|
private:
|
||||||
|
GameList* m_game_list;
|
||||||
|
int m_size;
|
||||||
|
};
|
||||||
|
|
||||||
|
GameListWidget::GameListWidget(QWidget* parent /* = nullptr */) : QStackedWidget(parent) {}
|
||||||
|
|
||||||
|
GameListWidget::~GameListWidget() = default;
|
||||||
|
|
||||||
|
void GameListWidget::initialize(QtHostInterface* host_interface)
|
||||||
|
{
|
||||||
|
m_host_interface = host_interface;
|
||||||
|
m_game_list = host_interface->getGameList();
|
||||||
|
|
||||||
|
connect(m_host_interface, &QtHostInterface::gameListRefreshed, this, &GameListWidget::onGameListRefreshed);
|
||||||
|
|
||||||
|
m_table_model = new GameListModel(m_game_list, this);
|
||||||
|
m_table_view = new QTableView(this);
|
||||||
|
m_table_view->setModel(m_table_model);
|
||||||
|
m_table_view->setSelectionMode(QAbstractItemView::SingleSelection);
|
||||||
|
m_table_view->setSelectionBehavior(QAbstractItemView::SelectRows);
|
||||||
|
m_table_view->setAlternatingRowColors(true);
|
||||||
|
m_table_view->setShowGrid(false);
|
||||||
|
m_table_view->setCurrentIndex({});
|
||||||
|
m_table_view->verticalHeader()->hide();
|
||||||
|
m_table_view->resizeColumnsToContents();
|
||||||
|
|
||||||
|
connect(m_table_view, &QTableView::doubleClicked, this, &GameListWidget::onTableViewItemDoubleClicked);
|
||||||
|
|
||||||
|
insertWidget(0, m_table_view);
|
||||||
|
setCurrentIndex(0);
|
||||||
|
}
|
||||||
|
|
||||||
|
void GameListWidget::onGameListRefreshed()
|
||||||
|
{
|
||||||
|
m_table_model->refresh();
|
||||||
|
}
|
||||||
|
|
||||||
|
void GameListWidget::onTableViewItemDoubleClicked(const QModelIndex& index)
|
||||||
|
{
|
||||||
|
if (!index.isValid() || index.row() >= static_cast<int>(m_game_list->GetEntryCount()))
|
||||||
|
return;
|
||||||
|
|
||||||
|
const GameList::GameListEntry& entry = m_game_list->GetEntries().at(index.row());
|
||||||
|
emit bootEntryRequested(entry);
|
||||||
|
}
|
||||||
|
|
||||||
|
void GameListWidget::resizeEvent(QResizeEvent* event)
|
||||||
|
{
|
||||||
|
QStackedWidget::resizeEvent(event);
|
||||||
|
|
||||||
|
QtUtils::ResizeColumnsForTableView(m_table_view, {100, -1, 100, 100});
|
||||||
|
}
|
36
src/duckstation-qt/gamelistwidget.h
Normal file
|
@ -0,0 +1,36 @@
|
||||||
|
#pragma once
|
||||||
|
#include "core/game_list.h"
|
||||||
|
#include <QtWidgets/QStackedWidget>
|
||||||
|
#include <QtWidgets/QTableView>
|
||||||
|
|
||||||
|
class GameListModel;
|
||||||
|
|
||||||
|
class QtHostInterface;
|
||||||
|
|
||||||
|
class GameListWidget : public QStackedWidget
|
||||||
|
{
|
||||||
|
Q_OBJECT
|
||||||
|
|
||||||
|
public:
|
||||||
|
GameListWidget(QWidget* parent = nullptr);
|
||||||
|
~GameListWidget();
|
||||||
|
|
||||||
|
void initialize(QtHostInterface* host_interface);
|
||||||
|
|
||||||
|
Q_SIGNALS:
|
||||||
|
void bootEntryRequested(const GameList::GameListEntry& entry);
|
||||||
|
|
||||||
|
private Q_SLOTS:
|
||||||
|
void onGameListRefreshed();
|
||||||
|
void onTableViewItemDoubleClicked(const QModelIndex& index);
|
||||||
|
|
||||||
|
protected:
|
||||||
|
void resizeEvent(QResizeEvent* event);
|
||||||
|
|
||||||
|
private:
|
||||||
|
QtHostInterface* m_host_interface = nullptr;
|
||||||
|
GameList* m_game_list = nullptr;
|
||||||
|
|
||||||
|
GameListModel* m_table_model = nullptr;
|
||||||
|
QTableView* m_table_view = nullptr;
|
||||||
|
};
|
28
src/duckstation-qt/main.cpp
Normal file
|
@ -0,0 +1,28 @@
|
||||||
|
#include "YBaseLib/Log.h"
|
||||||
|
#include "mainwindow.h"
|
||||||
|
#include "qthostinterface.h"
|
||||||
|
#include <QtWidgets/QApplication>
|
||||||
|
#include <memory>
|
||||||
|
|
||||||
|
static void InitLogging()
|
||||||
|
{
|
||||||
|
// set log flags
|
||||||
|
// g_pLog->SetConsoleOutputParams(true);
|
||||||
|
g_pLog->SetConsoleOutputParams(true, nullptr, LOGLEVEL_PROFILE);
|
||||||
|
g_pLog->SetFilterLevel(LOGLEVEL_PROFILE);
|
||||||
|
// g_pLog->SetDebugOutputParams(true);
|
||||||
|
}
|
||||||
|
|
||||||
|
int main(int argc, char* argv[])
|
||||||
|
{
|
||||||
|
InitLogging();
|
||||||
|
|
||||||
|
QApplication app(argc, argv);
|
||||||
|
|
||||||
|
std::unique_ptr<QtHostInterface> host_interface = std::make_unique<QtHostInterface>();
|
||||||
|
|
||||||
|
std::unique_ptr<MainWindow> window = std::make_unique<MainWindow>(host_interface.get());
|
||||||
|
window->show();
|
||||||
|
|
||||||
|
return app.exec();
|
||||||
|
}
|
207
src/duckstation-qt/mainwindow.cpp
Normal file
|
@ -0,0 +1,207 @@
|
||||||
|
#include "mainwindow.h"
|
||||||
|
#include "core/game_list.h"
|
||||||
|
#include "core/settings.h"
|
||||||
|
#include "gamelistwidget.h"
|
||||||
|
#include "qthostinterface.h"
|
||||||
|
#include "qtsettingsinterface.h"
|
||||||
|
#include "settingsdialog.h"
|
||||||
|
#include <QtWidgets/QFileDialog>
|
||||||
|
|
||||||
|
static constexpr char DISC_IMAGE_FILTER[] =
|
||||||
|
"All File Types (*.bin *.img *.cue *.exe *.psexe);;Single-Track Raw Images (*.bin *.img);;Cue Sheets "
|
||||||
|
"(*.cue);;PlayStation Executables (*.exe *.psexe)";
|
||||||
|
|
||||||
|
MainWindow::MainWindow(QtHostInterface* host_interface) : QMainWindow(nullptr), m_host_interface(host_interface)
|
||||||
|
{
|
||||||
|
m_ui.setupUi(this);
|
||||||
|
setupAdditionalUi();
|
||||||
|
connectSignals();
|
||||||
|
|
||||||
|
// force a scan of the game list
|
||||||
|
m_host_interface->refreshGameList();
|
||||||
|
|
||||||
|
resize(750, 690);
|
||||||
|
}
|
||||||
|
|
||||||
|
MainWindow::~MainWindow()
|
||||||
|
{
|
||||||
|
m_host_interface->destroyDisplayWidget();
|
||||||
|
}
|
||||||
|
|
||||||
|
void MainWindow::onEmulationStarting()
|
||||||
|
{
|
||||||
|
switchToEmulationView();
|
||||||
|
updateEmulationActions(true, false);
|
||||||
|
|
||||||
|
// we need the surface visible..
|
||||||
|
QCoreApplication::processEvents(QEventLoop::ExcludeUserInputEvents);
|
||||||
|
}
|
||||||
|
|
||||||
|
void MainWindow::onEmulationStarted()
|
||||||
|
{
|
||||||
|
updateEmulationActions(false, true);
|
||||||
|
m_emulation_running = true;
|
||||||
|
}
|
||||||
|
|
||||||
|
void MainWindow::onEmulationStopped()
|
||||||
|
{
|
||||||
|
updateEmulationActions(false, false);
|
||||||
|
switchToGameListView();
|
||||||
|
m_emulation_running = false;
|
||||||
|
}
|
||||||
|
|
||||||
|
void MainWindow::onEmulationPaused(bool paused)
|
||||||
|
{
|
||||||
|
m_ui.actionPause->setChecked(paused);
|
||||||
|
}
|
||||||
|
|
||||||
|
void MainWindow::onStartDiscActionTriggered()
|
||||||
|
{
|
||||||
|
QString filename =
|
||||||
|
QFileDialog::getOpenFileName(this, tr("Select Disc Image"), QString(), tr(DISC_IMAGE_FILTER), nullptr);
|
||||||
|
if (filename.isEmpty())
|
||||||
|
return;
|
||||||
|
|
||||||
|
m_host_interface->bootSystem(std::move(filename), QString());
|
||||||
|
}
|
||||||
|
|
||||||
|
void MainWindow::onChangeDiscActionTriggered()
|
||||||
|
{
|
||||||
|
QMenu menu(tr("Change Disc..."), this);
|
||||||
|
QAction* from_file = menu.addAction(tr("From File..."));
|
||||||
|
QAction* from_game_list = menu.addAction(tr("From Game List"));
|
||||||
|
|
||||||
|
QAction* selected = menu.exec(QCursor::pos());
|
||||||
|
if (selected == from_file)
|
||||||
|
{
|
||||||
|
QString filename =
|
||||||
|
QFileDialog::getOpenFileName(this, tr("Select Disc Image"), QString(), tr(DISC_IMAGE_FILTER), nullptr);
|
||||||
|
if (filename.isEmpty())
|
||||||
|
return;
|
||||||
|
|
||||||
|
m_host_interface->changeDisc(filename);
|
||||||
|
}
|
||||||
|
else if (selected == from_game_list)
|
||||||
|
{
|
||||||
|
m_host_interface->pauseSystem(true);
|
||||||
|
switchToGameListView();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void MainWindow::onStartBiosActionTriggered()
|
||||||
|
{
|
||||||
|
m_host_interface->bootSystem(QString(), QString());
|
||||||
|
}
|
||||||
|
|
||||||
|
void MainWindow::onOpenDirectoryActionTriggered() {}
|
||||||
|
|
||||||
|
void MainWindow::onExitActionTriggered() {}
|
||||||
|
|
||||||
|
void MainWindow::onFullscreenActionToggled(bool fullscreen) {}
|
||||||
|
|
||||||
|
void MainWindow::onGitHubRepositoryActionTriggered() {}
|
||||||
|
|
||||||
|
void MainWindow::onIssueTrackerActionTriggered() {}
|
||||||
|
|
||||||
|
void MainWindow::onAboutActionTriggered() {}
|
||||||
|
|
||||||
|
void MainWindow::setupAdditionalUi()
|
||||||
|
{
|
||||||
|
m_game_list_widget = new GameListWidget(m_ui.mainContainer);
|
||||||
|
m_game_list_widget->initialize(m_host_interface);
|
||||||
|
m_ui.mainContainer->insertWidget(0, m_game_list_widget);
|
||||||
|
|
||||||
|
QWidget* display_widget = m_host_interface->createDisplayWidget(m_ui.mainContainer);
|
||||||
|
m_ui.mainContainer->insertWidget(1, display_widget);
|
||||||
|
|
||||||
|
m_ui.mainContainer->setCurrentIndex(0);
|
||||||
|
}
|
||||||
|
|
||||||
|
void MainWindow::updateEmulationActions(bool starting, bool running)
|
||||||
|
{
|
||||||
|
m_ui.actionStartDisc->setDisabled(starting || running);
|
||||||
|
m_ui.actionStartBios->setDisabled(starting || running);
|
||||||
|
m_ui.actionOpenDirectory->setDisabled(starting || running);
|
||||||
|
m_ui.actionPowerOff->setDisabled(starting || running);
|
||||||
|
|
||||||
|
m_ui.actionPowerOff->setDisabled(starting || !running);
|
||||||
|
m_ui.actionReset->setDisabled(starting || !running);
|
||||||
|
m_ui.actionPause->setDisabled(starting || !running);
|
||||||
|
m_ui.actionChangeDisc->setDisabled(starting || !running);
|
||||||
|
|
||||||
|
m_ui.actionLoadState->setDisabled(starting);
|
||||||
|
m_ui.actionSaveState->setDisabled(starting);
|
||||||
|
|
||||||
|
m_ui.actionFullscreen->setDisabled(starting || !running);
|
||||||
|
}
|
||||||
|
|
||||||
|
void MainWindow::switchToGameListView()
|
||||||
|
{
|
||||||
|
m_ui.mainContainer->setCurrentIndex(0);
|
||||||
|
}
|
||||||
|
|
||||||
|
void MainWindow::switchToEmulationView()
|
||||||
|
{
|
||||||
|
m_ui.mainContainer->setCurrentIndex(1);
|
||||||
|
}
|
||||||
|
|
||||||
|
void MainWindow::connectSignals()
|
||||||
|
{
|
||||||
|
updateEmulationActions(false, false);
|
||||||
|
onEmulationPaused(false);
|
||||||
|
|
||||||
|
connect(m_ui.actionStartDisc, &QAction::triggered, this, &MainWindow::onStartDiscActionTriggered);
|
||||||
|
connect(m_ui.actionStartBios, &QAction::triggered, this, &MainWindow::onStartBiosActionTriggered);
|
||||||
|
connect(m_ui.actionChangeDisc, &QAction::triggered, this, &MainWindow::onChangeDiscActionTriggered);
|
||||||
|
connect(m_ui.actionOpenDirectory, &QAction::triggered, this, &MainWindow::onOpenDirectoryActionTriggered);
|
||||||
|
connect(m_ui.actionPowerOff, &QAction::triggered, m_host_interface, &QtHostInterface::powerOffSystem);
|
||||||
|
connect(m_ui.actionReset, &QAction::triggered, m_host_interface, &QtHostInterface::resetSystem);
|
||||||
|
connect(m_ui.actionPause, &QAction::toggled, m_host_interface, &QtHostInterface::pauseSystem);
|
||||||
|
connect(m_ui.actionExit, &QAction::triggered, this, &MainWindow::onExitActionTriggered);
|
||||||
|
connect(m_ui.actionFullscreen, &QAction::toggled, this, &MainWindow::onFullscreenActionToggled);
|
||||||
|
connect(m_ui.actionSettings, &QAction::triggered, [this]() { doSettings(SettingsDialog::Category::Count); });
|
||||||
|
connect(m_ui.actionGameListSettings, &QAction::triggered,
|
||||||
|
[this]() { doSettings(SettingsDialog::Category::GameListSettings); });
|
||||||
|
connect(m_ui.actionCPUSettings, &QAction::triggered, [this]() { doSettings(SettingsDialog::Category::CPUSettings); });
|
||||||
|
connect(m_ui.actionGPUSettings, &QAction::triggered, [this]() { doSettings(SettingsDialog::Category::GPUSettings); });
|
||||||
|
connect(m_ui.actionAudioSettings, &QAction::triggered,
|
||||||
|
[this]() { doSettings(SettingsDialog::Category::AudioSettings); });
|
||||||
|
connect(m_ui.actionGitHubRepository, &QAction::triggered, this, &MainWindow::onGitHubRepositoryActionTriggered);
|
||||||
|
connect(m_ui.actionIssueTracker, &QAction::triggered, this, &MainWindow::onIssueTrackerActionTriggered);
|
||||||
|
connect(m_ui.actionAbout, &QAction::triggered, this, &MainWindow::onAboutActionTriggered);
|
||||||
|
|
||||||
|
connect(m_host_interface, &QtHostInterface::emulationStarting, this, &MainWindow::onEmulationStarting);
|
||||||
|
connect(m_host_interface, &QtHostInterface::emulationStarted, this, &MainWindow::onEmulationStarted);
|
||||||
|
connect(m_host_interface, &QtHostInterface::emulationStopped, this, &MainWindow::onEmulationStopped);
|
||||||
|
connect(m_host_interface, &QtHostInterface::emulationPaused, this, &MainWindow::onEmulationPaused);
|
||||||
|
|
||||||
|
connect(m_game_list_widget, &GameListWidget::bootEntryRequested, [this](const GameList::GameListEntry& entry) {
|
||||||
|
// if we're not running, boot the system, otherwise swap discs
|
||||||
|
QString path = QString::fromStdString(entry.path);
|
||||||
|
if (!m_emulation_running)
|
||||||
|
{
|
||||||
|
m_host_interface->bootSystem(path, QString());
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
m_host_interface->changeDisc(path);
|
||||||
|
m_host_interface->pauseSystem(false);
|
||||||
|
switchToEmulationView();
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
void MainWindow::doSettings(SettingsDialog::Category category)
|
||||||
|
{
|
||||||
|
if (!m_settings_dialog)
|
||||||
|
m_settings_dialog = new SettingsDialog(m_host_interface, this);
|
||||||
|
|
||||||
|
if (!m_settings_dialog->isVisible())
|
||||||
|
{
|
||||||
|
m_settings_dialog->setModal(false);
|
||||||
|
m_settings_dialog->show();
|
||||||
|
}
|
||||||
|
|
||||||
|
if (category != SettingsDialog::Category::Count)
|
||||||
|
m_settings_dialog->setCategory(category);
|
||||||
|
}
|
57
src/duckstation-qt/mainwindow.h
Normal file
|
@ -0,0 +1,57 @@
|
||||||
|
#pragma once
|
||||||
|
#include <QtCore/QThread>
|
||||||
|
#include <QtWidgets/QLabel>
|
||||||
|
#include <QtWidgets/QMainWindow>
|
||||||
|
#include <memory>
|
||||||
|
|
||||||
|
#include "settingsdialog.h"
|
||||||
|
#include "ui_mainwindow.h"
|
||||||
|
|
||||||
|
class GameList;
|
||||||
|
class GameListWidget;
|
||||||
|
class QtHostInterface;
|
||||||
|
|
||||||
|
class MainWindow : public QMainWindow
|
||||||
|
{
|
||||||
|
Q_OBJECT
|
||||||
|
|
||||||
|
public:
|
||||||
|
explicit MainWindow(QtHostInterface* host_interface);
|
||||||
|
~MainWindow();
|
||||||
|
|
||||||
|
public Q_SLOTS:
|
||||||
|
void onEmulationStarting();
|
||||||
|
void onEmulationStarted();
|
||||||
|
void onEmulationStopped();
|
||||||
|
void onEmulationPaused(bool paused);
|
||||||
|
|
||||||
|
void onStartDiscActionTriggered();
|
||||||
|
void onChangeDiscActionTriggered();
|
||||||
|
void onStartBiosActionTriggered();
|
||||||
|
void onOpenDirectoryActionTriggered();
|
||||||
|
void onExitActionTriggered();
|
||||||
|
void onFullscreenActionToggled(bool fullscreen);
|
||||||
|
void onGitHubRepositoryActionTriggered();
|
||||||
|
void onIssueTrackerActionTriggered();
|
||||||
|
void onAboutActionTriggered();
|
||||||
|
|
||||||
|
private:
|
||||||
|
void createGameList();
|
||||||
|
void setupAdditionalUi();
|
||||||
|
void connectSignals();
|
||||||
|
void updateEmulationActions(bool starting, bool running);
|
||||||
|
void switchToGameListView();
|
||||||
|
void switchToEmulationView();
|
||||||
|
void doSettings(SettingsDialog::Category category = SettingsDialog::Category::Count);
|
||||||
|
|
||||||
|
Ui::MainWindow m_ui;
|
||||||
|
|
||||||
|
QtHostInterface* m_host_interface = nullptr;
|
||||||
|
|
||||||
|
std::unique_ptr<GameList> m_game_list;
|
||||||
|
GameListWidget* m_game_list_widget = nullptr;
|
||||||
|
|
||||||
|
SettingsDialog* m_settings_dialog = nullptr;
|
||||||
|
|
||||||
|
bool m_emulation_running = false;
|
||||||
|
};
|
368
src/duckstation-qt/mainwindow.ui
Normal file
|
@ -0,0 +1,368 @@
|
||||||
|
<?xml version="1.0" encoding="UTF-8"?>
|
||||||
|
<ui version="4.0">
|
||||||
|
<class>MainWindow</class>
|
||||||
|
<widget class="QMainWindow" name="MainWindow">
|
||||||
|
<property name="geometry">
|
||||||
|
<rect>
|
||||||
|
<x>0</x>
|
||||||
|
<y>0</y>
|
||||||
|
<width>754</width>
|
||||||
|
<height>600</height>
|
||||||
|
</rect>
|
||||||
|
</property>
|
||||||
|
<property name="windowTitle">
|
||||||
|
<string>DuckStation</string>
|
||||||
|
</property>
|
||||||
|
<property name="windowIcon">
|
||||||
|
<iconset resource="icons.qrc">
|
||||||
|
<normaloff>:/icons/duck.png</normaloff>:/icons/duck.png</iconset>
|
||||||
|
</property>
|
||||||
|
<widget class="QStackedWidget" name="mainContainer">
|
||||||
|
<property name="currentIndex">
|
||||||
|
<number>0</number>
|
||||||
|
</property>
|
||||||
|
<widget class="QWidget" name="page"/>
|
||||||
|
<widget class="QWidget" name="page_2"/>
|
||||||
|
</widget>
|
||||||
|
<widget class="QMenuBar" name="menuBar">
|
||||||
|
<property name="geometry">
|
||||||
|
<rect>
|
||||||
|
<x>0</x>
|
||||||
|
<y>0</y>
|
||||||
|
<width>754</width>
|
||||||
|
<height>21</height>
|
||||||
|
</rect>
|
||||||
|
</property>
|
||||||
|
<widget class="QMenu" name="menuSystem">
|
||||||
|
<property name="title">
|
||||||
|
<string>System</string>
|
||||||
|
</property>
|
||||||
|
<addaction name="actionStartDisc"/>
|
||||||
|
<addaction name="actionStartBios"/>
|
||||||
|
<addaction name="separator"/>
|
||||||
|
<addaction name="actionPowerOff"/>
|
||||||
|
<addaction name="actionReset"/>
|
||||||
|
<addaction name="actionPause"/>
|
||||||
|
<addaction name="actionChangeDisc"/>
|
||||||
|
<addaction name="separator"/>
|
||||||
|
<addaction name="actionLoadState"/>
|
||||||
|
<addaction name="actionSaveState"/>
|
||||||
|
<addaction name="separator"/>
|
||||||
|
<addaction name="actionExit"/>
|
||||||
|
</widget>
|
||||||
|
<widget class="QMenu" name="menuSettings">
|
||||||
|
<property name="title">
|
||||||
|
<string>S&ettings</string>
|
||||||
|
</property>
|
||||||
|
<widget class="QMenu" name="menuRenderer">
|
||||||
|
<property name="title">
|
||||||
|
<string>Renderer</string>
|
||||||
|
</property>
|
||||||
|
<addaction name="actionRendererD3D11"/>
|
||||||
|
<addaction name="actionRendererOpenGL"/>
|
||||||
|
<addaction name="actionRendererSoftware"/>
|
||||||
|
</widget>
|
||||||
|
<widget class="QMenu" name="actionCPUExecutionMode">
|
||||||
|
<property name="title">
|
||||||
|
<string>CPU Execution Mode</string>
|
||||||
|
</property>
|
||||||
|
<addaction name="actionCPUExecutionModeInterpreter"/>
|
||||||
|
<addaction name="actionCPUExecutionModeCachedInterpreter"/>
|
||||||
|
<addaction name="actionCPUExecutionModeRecompiler"/>
|
||||||
|
</widget>
|
||||||
|
<addaction name="actionFullscreen"/>
|
||||||
|
<addaction name="separator"/>
|
||||||
|
<addaction name="actionConsoleSettings"/>
|
||||||
|
<addaction name="actionGameListSettings"/>
|
||||||
|
<addaction name="actionPortSettings"/>
|
||||||
|
<addaction name="actionCPUSettings"/>
|
||||||
|
<addaction name="actionGPUSettings"/>
|
||||||
|
<addaction name="actionAudioSettings"/>
|
||||||
|
<addaction name="separator"/>
|
||||||
|
<addaction name="actionOpenDirectory"/>
|
||||||
|
<addaction name="separator"/>
|
||||||
|
<addaction name="actionCPUExecutionMode"/>
|
||||||
|
<addaction name="menuRenderer"/>
|
||||||
|
</widget>
|
||||||
|
<widget class="QMenu" name="menuHelp">
|
||||||
|
<property name="title">
|
||||||
|
<string>&Help</string>
|
||||||
|
</property>
|
||||||
|
<addaction name="actionGitHubRepository"/>
|
||||||
|
<addaction name="actionIssueTracker"/>
|
||||||
|
<addaction name="separator"/>
|
||||||
|
<addaction name="actionAbout"/>
|
||||||
|
</widget>
|
||||||
|
<addaction name="menuSystem"/>
|
||||||
|
<addaction name="menuSettings"/>
|
||||||
|
<addaction name="menuHelp"/>
|
||||||
|
</widget>
|
||||||
|
<widget class="QToolBar" name="toolBar">
|
||||||
|
<property name="windowTitle">
|
||||||
|
<string>toolBar</string>
|
||||||
|
</property>
|
||||||
|
<property name="iconSize">
|
||||||
|
<size>
|
||||||
|
<width>32</width>
|
||||||
|
<height>32</height>
|
||||||
|
</size>
|
||||||
|
</property>
|
||||||
|
<property name="toolButtonStyle">
|
||||||
|
<enum>Qt::ToolButtonTextUnderIcon</enum>
|
||||||
|
</property>
|
||||||
|
<attribute name="toolBarArea">
|
||||||
|
<enum>TopToolBarArea</enum>
|
||||||
|
</attribute>
|
||||||
|
<attribute name="toolBarBreak">
|
||||||
|
<bool>false</bool>
|
||||||
|
</attribute>
|
||||||
|
<addaction name="actionStartDisc"/>
|
||||||
|
<addaction name="actionStartBios"/>
|
||||||
|
<addaction name="actionOpenDirectory"/>
|
||||||
|
<addaction name="separator"/>
|
||||||
|
<addaction name="actionPowerOff"/>
|
||||||
|
<addaction name="actionReset"/>
|
||||||
|
<addaction name="actionPause"/>
|
||||||
|
<addaction name="actionChangeDisc"/>
|
||||||
|
<addaction name="separator"/>
|
||||||
|
<addaction name="actionLoadState"/>
|
||||||
|
<addaction name="actionSaveState"/>
|
||||||
|
<addaction name="separator"/>
|
||||||
|
<addaction name="actionFullscreen"/>
|
||||||
|
<addaction name="actionSettings"/>
|
||||||
|
</widget>
|
||||||
|
<action name="actionStartDisc">
|
||||||
|
<property name="icon">
|
||||||
|
<iconset resource="icons.qrc">
|
||||||
|
<normaloff>:/icons/drive-optical.png</normaloff>:/icons/drive-optical.png</iconset>
|
||||||
|
</property>
|
||||||
|
<property name="text">
|
||||||
|
<string>Start &Disc...</string>
|
||||||
|
</property>
|
||||||
|
</action>
|
||||||
|
<action name="actionStartBios">
|
||||||
|
<property name="icon">
|
||||||
|
<iconset resource="icons.qrc">
|
||||||
|
<normaloff>:/icons/drive-removable-media.png</normaloff>:/icons/drive-removable-media.png</iconset>
|
||||||
|
</property>
|
||||||
|
<property name="text">
|
||||||
|
<string>Start &BIOS</string>
|
||||||
|
</property>
|
||||||
|
</action>
|
||||||
|
<action name="actionPowerOff">
|
||||||
|
<property name="icon">
|
||||||
|
<iconset resource="icons.qrc">
|
||||||
|
<normaloff>:/icons/system-shutdown.png</normaloff>:/icons/system-shutdown.png</iconset>
|
||||||
|
</property>
|
||||||
|
<property name="text">
|
||||||
|
<string>Power &Off</string>
|
||||||
|
</property>
|
||||||
|
</action>
|
||||||
|
<action name="actionReset">
|
||||||
|
<property name="icon">
|
||||||
|
<iconset resource="icons.qrc">
|
||||||
|
<normaloff>:/icons/view-refresh.png</normaloff>:/icons/view-refresh.png</iconset>
|
||||||
|
</property>
|
||||||
|
<property name="text">
|
||||||
|
<string>&Reset</string>
|
||||||
|
</property>
|
||||||
|
</action>
|
||||||
|
<action name="actionPause">
|
||||||
|
<property name="checkable">
|
||||||
|
<bool>true</bool>
|
||||||
|
</property>
|
||||||
|
<property name="icon">
|
||||||
|
<iconset resource="icons.qrc">
|
||||||
|
<normaloff>:/icons/media-playback-pause.png</normaloff>:/icons/media-playback-pause.png</iconset>
|
||||||
|
</property>
|
||||||
|
<property name="text">
|
||||||
|
<string>&Pause</string>
|
||||||
|
</property>
|
||||||
|
</action>
|
||||||
|
<action name="actionLoadState">
|
||||||
|
<property name="icon">
|
||||||
|
<iconset resource="icons.qrc">
|
||||||
|
<normaloff>:/icons/document-open.png</normaloff>:/icons/document-open.png</iconset>
|
||||||
|
</property>
|
||||||
|
<property name="text">
|
||||||
|
<string>&Load State</string>
|
||||||
|
</property>
|
||||||
|
</action>
|
||||||
|
<action name="actionSaveState">
|
||||||
|
<property name="icon">
|
||||||
|
<iconset resource="icons.qrc">
|
||||||
|
<normaloff>:/icons/document-save.png</normaloff>:/icons/document-save.png</iconset>
|
||||||
|
</property>
|
||||||
|
<property name="text">
|
||||||
|
<string>&Save State</string>
|
||||||
|
</property>
|
||||||
|
</action>
|
||||||
|
<action name="actionExit">
|
||||||
|
<property name="text">
|
||||||
|
<string>E&xit</string>
|
||||||
|
</property>
|
||||||
|
</action>
|
||||||
|
<action name="actionConsoleSettings">
|
||||||
|
<property name="icon">
|
||||||
|
<iconset resource="icons.qrc">
|
||||||
|
<normaloff>:/icons/utilities-system-monitor.png</normaloff>:/icons/utilities-system-monitor.png</iconset>
|
||||||
|
</property>
|
||||||
|
<property name="text">
|
||||||
|
<string>&Console Settings...</string>
|
||||||
|
</property>
|
||||||
|
</action>
|
||||||
|
<action name="actionPortSettings">
|
||||||
|
<property name="icon">
|
||||||
|
<iconset resource="icons.qrc">
|
||||||
|
<normaloff>:/icons/input-gaming.png</normaloff>:/icons/input-gaming.png</iconset>
|
||||||
|
</property>
|
||||||
|
<property name="text">
|
||||||
|
<string>&Port Settings...</string>
|
||||||
|
</property>
|
||||||
|
</action>
|
||||||
|
<action name="actionCPUSettings">
|
||||||
|
<property name="icon">
|
||||||
|
<iconset resource="icons.qrc">
|
||||||
|
<normaloff>:/icons/applications-other.png</normaloff>:/icons/applications-other.png</iconset>
|
||||||
|
</property>
|
||||||
|
<property name="text">
|
||||||
|
<string>&CPU Settings...</string>
|
||||||
|
</property>
|
||||||
|
</action>
|
||||||
|
<action name="actionGPUSettings">
|
||||||
|
<property name="icon">
|
||||||
|
<iconset resource="icons.qrc">
|
||||||
|
<normaloff>:/icons/video-display.png</normaloff>:/icons/video-display.png</iconset>
|
||||||
|
</property>
|
||||||
|
<property name="text">
|
||||||
|
<string>&GPU Settings...</string>
|
||||||
|
</property>
|
||||||
|
</action>
|
||||||
|
<action name="actionFullscreen">
|
||||||
|
<property name="icon">
|
||||||
|
<iconset resource="icons.qrc">
|
||||||
|
<normaloff>:/icons/view-fullscreen.png</normaloff>:/icons/view-fullscreen.png</iconset>
|
||||||
|
</property>
|
||||||
|
<property name="text">
|
||||||
|
<string>Fullscreen</string>
|
||||||
|
</property>
|
||||||
|
</action>
|
||||||
|
<action name="actionRendererD3D11">
|
||||||
|
<property name="checkable">
|
||||||
|
<bool>true</bool>
|
||||||
|
</property>
|
||||||
|
<property name="text">
|
||||||
|
<string>Hardware (D3D11)</string>
|
||||||
|
</property>
|
||||||
|
</action>
|
||||||
|
<action name="actionRendererOpenGL">
|
||||||
|
<property name="checkable">
|
||||||
|
<bool>true</bool>
|
||||||
|
</property>
|
||||||
|
<property name="text">
|
||||||
|
<string>Hardware (OpenGL)</string>
|
||||||
|
</property>
|
||||||
|
</action>
|
||||||
|
<action name="actionRendererSoftware">
|
||||||
|
<property name="checkable">
|
||||||
|
<bool>true</bool>
|
||||||
|
</property>
|
||||||
|
<property name="text">
|
||||||
|
<string>Software</string>
|
||||||
|
</property>
|
||||||
|
</action>
|
||||||
|
<action name="actionCPUExecutionModeInterpreter">
|
||||||
|
<property name="checkable">
|
||||||
|
<bool>true</bool>
|
||||||
|
</property>
|
||||||
|
<property name="text">
|
||||||
|
<string>Interpreter (Slowest)</string>
|
||||||
|
</property>
|
||||||
|
</action>
|
||||||
|
<action name="actionCPUExecutionModeCachedInterpreter">
|
||||||
|
<property name="checkable">
|
||||||
|
<bool>true</bool>
|
||||||
|
</property>
|
||||||
|
<property name="text">
|
||||||
|
<string>Cached Interpreter (Slower)</string>
|
||||||
|
</property>
|
||||||
|
</action>
|
||||||
|
<action name="actionCPUExecutionModeRecompiler">
|
||||||
|
<property name="checkable">
|
||||||
|
<bool>true</bool>
|
||||||
|
</property>
|
||||||
|
<property name="text">
|
||||||
|
<string>Recompiler (Fastest)</string>
|
||||||
|
</property>
|
||||||
|
</action>
|
||||||
|
<action name="actionResolution_Scale">
|
||||||
|
<property name="text">
|
||||||
|
<string>Resolution Scale</string>
|
||||||
|
</property>
|
||||||
|
</action>
|
||||||
|
<action name="actionGitHubRepository">
|
||||||
|
<property name="text">
|
||||||
|
<string>&GitHub Repository...</string>
|
||||||
|
</property>
|
||||||
|
</action>
|
||||||
|
<action name="actionIssueTracker">
|
||||||
|
<property name="text">
|
||||||
|
<string>&Issue Tracker...</string>
|
||||||
|
</property>
|
||||||
|
</action>
|
||||||
|
<action name="actionAbout">
|
||||||
|
<property name="text">
|
||||||
|
<string>&About...</string>
|
||||||
|
</property>
|
||||||
|
</action>
|
||||||
|
<action name="actionChangeDisc">
|
||||||
|
<property name="icon">
|
||||||
|
<iconset resource="icons.qrc">
|
||||||
|
<normaloff>:/icons/media-optical.png</normaloff>:/icons/media-optical.png</iconset>
|
||||||
|
</property>
|
||||||
|
<property name="text">
|
||||||
|
<string>Change Disc...</string>
|
||||||
|
</property>
|
||||||
|
</action>
|
||||||
|
<action name="actionAudioSettings">
|
||||||
|
<property name="icon">
|
||||||
|
<iconset resource="icons.qrc">
|
||||||
|
<normaloff>:/icons/audio-card.png</normaloff>:/icons/audio-card.png</iconset>
|
||||||
|
</property>
|
||||||
|
<property name="text">
|
||||||
|
<string>Audio Settings...</string>
|
||||||
|
</property>
|
||||||
|
</action>
|
||||||
|
<action name="actionGameListSettings">
|
||||||
|
<property name="icon">
|
||||||
|
<iconset resource="icons.qrc">
|
||||||
|
<normaloff>:/icons/folder-open.png</normaloff>:/icons/folder-open.png</iconset>
|
||||||
|
</property>
|
||||||
|
<property name="text">
|
||||||
|
<string>Game List Settings...</string>
|
||||||
|
</property>
|
||||||
|
</action>
|
||||||
|
<action name="actionOpenDirectory">
|
||||||
|
<property name="icon">
|
||||||
|
<iconset resource="icons.qrc">
|
||||||
|
<normaloff>:/icons/edit-find.png</normaloff>:/icons/edit-find.png</iconset>
|
||||||
|
</property>
|
||||||
|
<property name="text">
|
||||||
|
<string>Open Directory...</string>
|
||||||
|
</property>
|
||||||
|
</action>
|
||||||
|
<action name="actionSettings">
|
||||||
|
<property name="icon">
|
||||||
|
<iconset resource="icons.qrc">
|
||||||
|
<normaloff>:/icons/applications-system.png</normaloff>:/icons/applications-system.png</iconset>
|
||||||
|
</property>
|
||||||
|
<property name="text">
|
||||||
|
<string>&Settings...</string>
|
||||||
|
</property>
|
||||||
|
</action>
|
||||||
|
</widget>
|
||||||
|
<resources>
|
||||||
|
<include location="icons.qrc"/>
|
||||||
|
</resources>
|
||||||
|
<connections/>
|
||||||
|
</ui>
|
430
src/duckstation-qt/opengldisplaywindow.cpp
Normal file
|
@ -0,0 +1,430 @@
|
||||||
|
#include "opengldisplaywindow.h"
|
||||||
|
#include "YBaseLib/Assert.h"
|
||||||
|
#include "YBaseLib/Log.h"
|
||||||
|
#include <array>
|
||||||
|
#include <tuple>
|
||||||
|
Log_SetChannel(OpenGLDisplayWindow);
|
||||||
|
|
||||||
|
static thread_local QOpenGLContext* s_thread_gl_context;
|
||||||
|
|
||||||
|
static void* GetProcAddressCallback(const char* name)
|
||||||
|
{
|
||||||
|
QOpenGLContext* ctx = s_thread_gl_context;
|
||||||
|
if (!ctx)
|
||||||
|
return nullptr;
|
||||||
|
|
||||||
|
return (void*)ctx->getProcAddress(name);
|
||||||
|
}
|
||||||
|
|
||||||
|
class OpenGLHostDisplayTexture : public HostDisplayTexture
|
||||||
|
{
|
||||||
|
public:
|
||||||
|
OpenGLHostDisplayTexture(GLuint id, u32 width, u32 height) : m_id(id), m_width(width), m_height(height) {}
|
||||||
|
~OpenGLHostDisplayTexture() override { glDeleteTextures(1, &m_id); }
|
||||||
|
|
||||||
|
void* GetHandle() const override { return reinterpret_cast<void*>(static_cast<uintptr_t>(m_id)); }
|
||||||
|
u32 GetWidth() const override { return m_width; }
|
||||||
|
u32 GetHeight() const override { return m_height; }
|
||||||
|
|
||||||
|
GLuint GetGLID() const { return m_id; }
|
||||||
|
|
||||||
|
static std::unique_ptr<OpenGLHostDisplayTexture> Create(u32 width, u32 height, const void* initial_data,
|
||||||
|
u32 initial_data_stride)
|
||||||
|
{
|
||||||
|
GLuint id;
|
||||||
|
glGenTextures(1, &id);
|
||||||
|
|
||||||
|
GLint old_texture_binding = 0;
|
||||||
|
glGetIntegerv(GL_TEXTURE_BINDING_2D, &old_texture_binding);
|
||||||
|
|
||||||
|
// TODO: Set pack width
|
||||||
|
Assert(!initial_data || initial_data_stride == (width * sizeof(u32)));
|
||||||
|
|
||||||
|
glBindTexture(GL_TEXTURE_2D, id);
|
||||||
|
glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA, width, height, 0, GL_RGBA, GL_UNSIGNED_BYTE, initial_data);
|
||||||
|
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAX_LEVEL, 1);
|
||||||
|
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR);
|
||||||
|
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR);
|
||||||
|
|
||||||
|
glBindTexture(GL_TEXTURE_2D, id);
|
||||||
|
return std::make_unique<OpenGLHostDisplayTexture>(id, width, height);
|
||||||
|
}
|
||||||
|
|
||||||
|
private:
|
||||||
|
GLuint m_id;
|
||||||
|
u32 m_width;
|
||||||
|
u32 m_height;
|
||||||
|
};
|
||||||
|
|
||||||
|
OpenGLDisplayWindow::OpenGLDisplayWindow(QWindow* parent) : QWindow(parent)
|
||||||
|
{
|
||||||
|
setSurfaceType(QWindow::OpenGLSurface);
|
||||||
|
}
|
||||||
|
|
||||||
|
OpenGLDisplayWindow::~OpenGLDisplayWindow() = default;
|
||||||
|
|
||||||
|
HostDisplay::RenderAPI OpenGLDisplayWindow::GetRenderAPI() const
|
||||||
|
{
|
||||||
|
return HostDisplay::RenderAPI::OpenGL;
|
||||||
|
}
|
||||||
|
|
||||||
|
void* OpenGLDisplayWindow::GetRenderDevice() const
|
||||||
|
{
|
||||||
|
return nullptr;
|
||||||
|
}
|
||||||
|
|
||||||
|
void* OpenGLDisplayWindow::GetRenderContext() const
|
||||||
|
{
|
||||||
|
return m_gl_context;
|
||||||
|
}
|
||||||
|
|
||||||
|
void* OpenGLDisplayWindow::GetRenderWindow() const
|
||||||
|
{
|
||||||
|
return const_cast<QWindow*>(static_cast<const QWindow*>(this));
|
||||||
|
}
|
||||||
|
|
||||||
|
void OpenGLDisplayWindow::ChangeRenderWindow(void* new_window)
|
||||||
|
{
|
||||||
|
Panic("Not implemented");
|
||||||
|
}
|
||||||
|
|
||||||
|
std::unique_ptr<HostDisplayTexture> OpenGLDisplayWindow::CreateTexture(u32 width, u32 height, const void* data,
|
||||||
|
u32 data_stride, bool dynamic)
|
||||||
|
{
|
||||||
|
return OpenGLHostDisplayTexture::Create(width, height, data, data_stride);
|
||||||
|
}
|
||||||
|
|
||||||
|
void OpenGLDisplayWindow::UpdateTexture(HostDisplayTexture* texture, u32 x, u32 y, u32 width, u32 height,
|
||||||
|
const void* data, u32 data_stride)
|
||||||
|
{
|
||||||
|
OpenGLHostDisplayTexture* tex = static_cast<OpenGLHostDisplayTexture*>(texture);
|
||||||
|
Assert(data_stride == (width * sizeof(u32)));
|
||||||
|
|
||||||
|
GLint old_texture_binding = 0;
|
||||||
|
glGetIntegerv(GL_TEXTURE_BINDING_2D, &old_texture_binding);
|
||||||
|
|
||||||
|
glBindTexture(GL_TEXTURE_2D, tex->GetGLID());
|
||||||
|
glTexSubImage2D(GL_TEXTURE_2D, 0, x, y, width, height, GL_RGBA, GL_UNSIGNED_BYTE, data);
|
||||||
|
|
||||||
|
glBindTexture(GL_TEXTURE_2D, old_texture_binding);
|
||||||
|
}
|
||||||
|
|
||||||
|
void OpenGLDisplayWindow::SetDisplayTexture(void* texture, s32 offset_x, s32 offset_y, s32 width, s32 height,
|
||||||
|
u32 texture_width, u32 texture_height, float aspect_ratio)
|
||||||
|
{
|
||||||
|
m_display_texture_id = static_cast<GLuint>(reinterpret_cast<uintptr_t>(texture));
|
||||||
|
m_display_offset_x = offset_x;
|
||||||
|
m_display_offset_y = offset_y;
|
||||||
|
m_display_width = width;
|
||||||
|
m_display_height = height;
|
||||||
|
m_display_texture_width = texture_width;
|
||||||
|
m_display_texture_height = texture_height;
|
||||||
|
m_display_aspect_ratio = aspect_ratio;
|
||||||
|
m_display_texture_changed = true;
|
||||||
|
}
|
||||||
|
|
||||||
|
void OpenGLDisplayWindow::SetDisplayLinearFiltering(bool enabled)
|
||||||
|
{
|
||||||
|
m_display_linear_filtering = enabled;
|
||||||
|
}
|
||||||
|
|
||||||
|
void OpenGLDisplayWindow::SetDisplayTopMargin(int height)
|
||||||
|
{
|
||||||
|
m_display_top_margin = height;
|
||||||
|
}
|
||||||
|
|
||||||
|
void OpenGLDisplayWindow::SetVSync(bool enabled)
|
||||||
|
{
|
||||||
|
// Window framebuffer has to be bound to call SetSwapInterval.
|
||||||
|
GLint current_fbo = 0;
|
||||||
|
glGetIntegerv(GL_DRAW_FRAMEBUFFER_BINDING, ¤t_fbo);
|
||||||
|
glBindFramebuffer(GL_DRAW_FRAMEBUFFER, 0);
|
||||||
|
// SDL_GL_SetSwapInterval(enabled ? 1 : 0);
|
||||||
|
glBindFramebuffer(GL_DRAW_FRAMEBUFFER, current_fbo);
|
||||||
|
}
|
||||||
|
|
||||||
|
std::tuple<u32, u32> OpenGLDisplayWindow::GetWindowSize() const
|
||||||
|
{
|
||||||
|
const QSize s = size();
|
||||||
|
return std::make_tuple(static_cast<u32>(s.width()), static_cast<u32>(s.height()));
|
||||||
|
}
|
||||||
|
|
||||||
|
void OpenGLDisplayWindow::WindowResized() {}
|
||||||
|
|
||||||
|
const char* OpenGLDisplayWindow::GetGLSLVersionString() const
|
||||||
|
{
|
||||||
|
return m_is_gles ? "#version 300 es" : "#version 130\n";
|
||||||
|
}
|
||||||
|
|
||||||
|
std::string OpenGLDisplayWindow::GetGLSLVersionHeader() const
|
||||||
|
{
|
||||||
|
std::string header = GetGLSLVersionString();
|
||||||
|
header += "\n\n";
|
||||||
|
if (m_is_gles)
|
||||||
|
{
|
||||||
|
header += "precision highp float;\n";
|
||||||
|
header += "precision highp int;\n\n";
|
||||||
|
}
|
||||||
|
|
||||||
|
return header;
|
||||||
|
}
|
||||||
|
|
||||||
|
static void APIENTRY GLDebugCallback(GLenum source, GLenum type, GLuint id, GLenum severity, GLsizei length,
|
||||||
|
const GLchar* message, const void* userParam)
|
||||||
|
{
|
||||||
|
switch (severity)
|
||||||
|
{
|
||||||
|
case GL_DEBUG_SEVERITY_HIGH_KHR:
|
||||||
|
Log_ErrorPrintf(message);
|
||||||
|
break;
|
||||||
|
case GL_DEBUG_SEVERITY_MEDIUM_KHR:
|
||||||
|
Log_WarningPrint(message);
|
||||||
|
break;
|
||||||
|
case GL_DEBUG_SEVERITY_LOW_KHR:
|
||||||
|
Log_InfoPrintf(message);
|
||||||
|
break;
|
||||||
|
case GL_DEBUG_SEVERITY_NOTIFICATION:
|
||||||
|
// Log_DebugPrint(message);
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
bool OpenGLDisplayWindow::createGLContext(QThread* worker_thread)
|
||||||
|
{
|
||||||
|
m_gl_context = new QOpenGLContext();
|
||||||
|
|
||||||
|
// Prefer a desktop OpenGL context where possible. If we can't get this, try OpenGL ES.
|
||||||
|
static constexpr std::array<std::tuple<int, int>, 11> desktop_versions_to_try = {
|
||||||
|
{{4, 6}, {4, 5}, {4, 4}, {4, 3}, {4, 2}, {4, 1}, {4, 0}, {3, 3}, {3, 2}, {3, 1}, {3, 0}}};
|
||||||
|
static constexpr std::array<std::tuple<int, int>, 4> es_versions_to_try = {{{3, 2}, {3, 1}, {3, 0}}};
|
||||||
|
|
||||||
|
QSurfaceFormat surface_format = requestedFormat();
|
||||||
|
surface_format.setSwapBehavior(QSurfaceFormat::DoubleBuffer);
|
||||||
|
surface_format.setSwapInterval(0);
|
||||||
|
surface_format.setRenderableType(QSurfaceFormat::OpenGL);
|
||||||
|
surface_format.setProfile(QSurfaceFormat::CoreProfile);
|
||||||
|
|
||||||
|
#ifdef _DEBUG
|
||||||
|
surface_format.setOption(QSurfaceFormat::DebugContext);
|
||||||
|
#endif
|
||||||
|
|
||||||
|
for (const auto [major, minor] : desktop_versions_to_try)
|
||||||
|
{
|
||||||
|
surface_format.setVersion(major, minor);
|
||||||
|
m_gl_context->setFormat(surface_format);
|
||||||
|
if (m_gl_context->create())
|
||||||
|
{
|
||||||
|
Log_InfoPrintf("Got a desktop OpenGL %d.%d context", major, minor);
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!m_gl_context)
|
||||||
|
{
|
||||||
|
// try es
|
||||||
|
surface_format.setRenderableType(QSurfaceFormat::OpenGLES);
|
||||||
|
surface_format.setProfile(QSurfaceFormat::NoProfile);
|
||||||
|
#ifdef _DEBUG
|
||||||
|
surface_format.setOption(QSurfaceFormat::DebugContext, false);
|
||||||
|
#endif
|
||||||
|
|
||||||
|
for (const auto [major, minor] : es_versions_to_try)
|
||||||
|
{
|
||||||
|
surface_format.setVersion(major, minor);
|
||||||
|
m_gl_context->setFormat(surface_format);
|
||||||
|
if (m_gl_context->create())
|
||||||
|
{
|
||||||
|
Log_InfoPrintf("Got a OpenGL ES %d.%d context", major, minor);
|
||||||
|
m_is_gles = true;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!m_gl_context->isValid())
|
||||||
|
{
|
||||||
|
Log_ErrorPrintf("Failed to create any GL context");
|
||||||
|
delete m_gl_context;
|
||||||
|
m_gl_context = nullptr;
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!m_gl_context->makeCurrent(this))
|
||||||
|
{
|
||||||
|
Log_ErrorPrintf("Failed to make GL context current on UI thread");
|
||||||
|
delete m_gl_context;
|
||||||
|
m_gl_context = nullptr;
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
m_gl_context->doneCurrent();
|
||||||
|
m_gl_context->moveToThread(worker_thread);
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
bool OpenGLDisplayWindow::initializeGLContext()
|
||||||
|
{
|
||||||
|
if (!m_gl_context->makeCurrent(this))
|
||||||
|
return false;
|
||||||
|
|
||||||
|
s_thread_gl_context = m_gl_context;
|
||||||
|
|
||||||
|
// Load GLAD.
|
||||||
|
const auto load_result =
|
||||||
|
m_is_gles ? gladLoadGLES2Loader(GetProcAddressCallback) : gladLoadGLLoader(GetProcAddressCallback);
|
||||||
|
if (!load_result)
|
||||||
|
{
|
||||||
|
Log_ErrorPrintf("Failed to load GL functions");
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
#if 1
|
||||||
|
if (GLAD_GL_KHR_debug)
|
||||||
|
{
|
||||||
|
glad_glDebugMessageCallbackKHR(GLDebugCallback, nullptr);
|
||||||
|
glEnable(GL_DEBUG_OUTPUT);
|
||||||
|
glEnable(GL_DEBUG_OUTPUT_SYNCHRONOUS);
|
||||||
|
}
|
||||||
|
#endif
|
||||||
|
|
||||||
|
if (!CreateImGuiContext() || !CreateGLResources())
|
||||||
|
{
|
||||||
|
s_thread_gl_context = nullptr;
|
||||||
|
m_gl_context->doneCurrent();
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
void OpenGLDisplayWindow::destroyGLContext()
|
||||||
|
{
|
||||||
|
Assert(m_gl_context && s_thread_gl_context == m_gl_context);
|
||||||
|
s_thread_gl_context = nullptr;
|
||||||
|
|
||||||
|
if (m_display_vao != 0)
|
||||||
|
glDeleteVertexArrays(1, &m_display_vao);
|
||||||
|
if (m_display_linear_sampler != 0)
|
||||||
|
glDeleteSamplers(1, &m_display_linear_sampler);
|
||||||
|
if (m_display_nearest_sampler != 0)
|
||||||
|
glDeleteSamplers(1, &m_display_nearest_sampler);
|
||||||
|
|
||||||
|
m_display_program.Destroy();
|
||||||
|
|
||||||
|
m_gl_context->doneCurrent();
|
||||||
|
delete m_gl_context;
|
||||||
|
m_gl_context = nullptr;
|
||||||
|
}
|
||||||
|
|
||||||
|
bool OpenGLDisplayWindow::CreateImGuiContext()
|
||||||
|
{
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
bool OpenGLDisplayWindow::CreateGLResources()
|
||||||
|
{
|
||||||
|
static constexpr char fullscreen_quad_vertex_shader[] = R"(
|
||||||
|
uniform vec4 u_src_rect;
|
||||||
|
out vec2 v_tex0;
|
||||||
|
|
||||||
|
void main()
|
||||||
|
{
|
||||||
|
vec2 pos = vec2(float((gl_VertexID << 1) & 2), float(gl_VertexID & 2));
|
||||||
|
v_tex0 = u_src_rect.xy + pos * u_src_rect.zw;
|
||||||
|
gl_Position = vec4(pos * vec2(2.0f, -2.0f) + vec2(-1.0f, 1.0f), 0.0f, 1.0f);
|
||||||
|
}
|
||||||
|
)";
|
||||||
|
|
||||||
|
static constexpr char display_fragment_shader[] = R"(
|
||||||
|
uniform sampler2D samp0;
|
||||||
|
|
||||||
|
in vec2 v_tex0;
|
||||||
|
out vec4 o_col0;
|
||||||
|
|
||||||
|
void main()
|
||||||
|
{
|
||||||
|
o_col0 = texture(samp0, v_tex0);
|
||||||
|
}
|
||||||
|
)";
|
||||||
|
|
||||||
|
if (!m_display_program.Compile(GetGLSLVersionHeader() + fullscreen_quad_vertex_shader,
|
||||||
|
GetGLSLVersionHeader() + display_fragment_shader))
|
||||||
|
{
|
||||||
|
Log_ErrorPrintf("Failed to compile display shaders");
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!m_is_gles)
|
||||||
|
m_display_program.BindFragData(0, "o_col0");
|
||||||
|
|
||||||
|
if (!m_display_program.Link())
|
||||||
|
{
|
||||||
|
Log_ErrorPrintf("Failed to link display program");
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
m_display_program.Bind();
|
||||||
|
m_display_program.RegisterUniform("u_src_rect");
|
||||||
|
m_display_program.RegisterUniform("samp0");
|
||||||
|
m_display_program.Uniform1i(1, 0);
|
||||||
|
|
||||||
|
glGenVertexArrays(1, &m_display_vao);
|
||||||
|
|
||||||
|
// samplers
|
||||||
|
glGenSamplers(1, &m_display_nearest_sampler);
|
||||||
|
glSamplerParameteri(m_display_nearest_sampler, GL_TEXTURE_MIN_FILTER, GL_NEAREST);
|
||||||
|
glSamplerParameteri(m_display_nearest_sampler, GL_TEXTURE_MAG_FILTER, GL_NEAREST);
|
||||||
|
glGenSamplers(1, &m_display_linear_sampler);
|
||||||
|
glSamplerParameteri(m_display_linear_sampler, GL_TEXTURE_MIN_FILTER, GL_LINEAR);
|
||||||
|
glSamplerParameteri(m_display_linear_sampler, GL_TEXTURE_MAG_FILTER, GL_LINEAR);
|
||||||
|
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
void OpenGLDisplayWindow::Render()
|
||||||
|
{
|
||||||
|
glDisable(GL_SCISSOR_TEST);
|
||||||
|
glBindFramebuffer(GL_DRAW_FRAMEBUFFER, 0);
|
||||||
|
glClearColor(0.0f, 0.0f, 0.0f, 0.0f);
|
||||||
|
glClear(GL_COLOR_BUFFER_BIT);
|
||||||
|
|
||||||
|
RenderDisplay();
|
||||||
|
|
||||||
|
// ImGui_ImplOpenGL3_RenderDrawData(ImGui::GetDrawData());
|
||||||
|
|
||||||
|
m_gl_context->makeCurrent(this);
|
||||||
|
m_gl_context->swapBuffers(this);
|
||||||
|
|
||||||
|
// ImGui_ImplSDL2_NewFrame(m_window);
|
||||||
|
// ImGui_ImplOpenGL3_NewFrame();
|
||||||
|
|
||||||
|
GL::Program::ResetLastProgram();
|
||||||
|
}
|
||||||
|
|
||||||
|
void OpenGLDisplayWindow::RenderDisplay()
|
||||||
|
{
|
||||||
|
if (!m_display_texture_id)
|
||||||
|
return;
|
||||||
|
|
||||||
|
// - 20 for main menu padding
|
||||||
|
const QSize window_size = size();
|
||||||
|
const auto [vp_left, vp_top, vp_width, vp_height] = CalculateDrawRect(
|
||||||
|
window_size.width(), std::max(window_size.height() - m_display_top_margin, 1), m_display_aspect_ratio);
|
||||||
|
|
||||||
|
glViewport(vp_left, window_size.height() - (m_display_top_margin + vp_top) - vp_height, vp_width, vp_height);
|
||||||
|
glDisable(GL_BLEND);
|
||||||
|
glDisable(GL_CULL_FACE);
|
||||||
|
glDisable(GL_DEPTH_TEST);
|
||||||
|
glDisable(GL_SCISSOR_TEST);
|
||||||
|
glDepthMask(GL_FALSE);
|
||||||
|
m_display_program.Bind();
|
||||||
|
m_display_program.Uniform4f(0, static_cast<float>(m_display_offset_x) / static_cast<float>(m_display_texture_width),
|
||||||
|
static_cast<float>(m_display_offset_y) / static_cast<float>(m_display_texture_height),
|
||||||
|
static_cast<float>(m_display_width) / static_cast<float>(m_display_texture_width),
|
||||||
|
static_cast<float>(m_display_height) / static_cast<float>(m_display_texture_height));
|
||||||
|
glBindTexture(GL_TEXTURE_2D, m_display_texture_id);
|
||||||
|
glBindSampler(0, m_display_linear_filtering ? m_display_linear_sampler : m_display_nearest_sampler);
|
||||||
|
glBindVertexArray(m_display_vao);
|
||||||
|
glDrawArrays(GL_TRIANGLES, 0, 3);
|
||||||
|
glBindSampler(0, 0);
|
||||||
|
}
|
75
src/duckstation-qt/opengldisplaywindow.h
Normal file
|
@ -0,0 +1,75 @@
|
||||||
|
#pragma once
|
||||||
|
#include <glad.h>
|
||||||
|
|
||||||
|
#include <QtGui/QWindow>
|
||||||
|
#include <QtGui/QOpenGLContext>
|
||||||
|
#include "common/gl/program.h"
|
||||||
|
#include "common/gl/texture.h"
|
||||||
|
#include "core/host_display.h"
|
||||||
|
#include <string>
|
||||||
|
#include <memory>
|
||||||
|
|
||||||
|
class OpenGLDisplayWindow final : public QWindow, public HostDisplay
|
||||||
|
{
|
||||||
|
Q_OBJECT
|
||||||
|
|
||||||
|
public:
|
||||||
|
explicit OpenGLDisplayWindow(QWindow* parent);
|
||||||
|
~OpenGLDisplayWindow();
|
||||||
|
|
||||||
|
bool createGLContext(QThread* worker_thread);
|
||||||
|
bool initializeGLContext();
|
||||||
|
void destroyGLContext();
|
||||||
|
|
||||||
|
RenderAPI GetRenderAPI() const override;
|
||||||
|
void* GetRenderDevice() const override;
|
||||||
|
void* GetRenderContext() const override;
|
||||||
|
void* GetRenderWindow() const override;
|
||||||
|
|
||||||
|
void ChangeRenderWindow(void* new_window) override;
|
||||||
|
|
||||||
|
std::unique_ptr<HostDisplayTexture> CreateTexture(u32 width, u32 height, const void* data, u32 data_stride,
|
||||||
|
bool dynamic) override;
|
||||||
|
void UpdateTexture(HostDisplayTexture* texture, u32 x, u32 y, u32 width, u32 height, const void* data,
|
||||||
|
u32 data_stride) override;
|
||||||
|
|
||||||
|
void SetDisplayTexture(void* texture, s32 offset_x, s32 offset_y, s32 width, s32 height, u32 texture_width,
|
||||||
|
u32 texture_height, float aspect_ratio) override;
|
||||||
|
void SetDisplayLinearFiltering(bool enabled) override;
|
||||||
|
void SetDisplayTopMargin(int height) override;
|
||||||
|
|
||||||
|
void SetVSync(bool enabled) override;
|
||||||
|
|
||||||
|
std::tuple<u32, u32> GetWindowSize() const override;
|
||||||
|
void WindowResized() override;
|
||||||
|
|
||||||
|
private:
|
||||||
|
const char* GetGLSLVersionString() const;
|
||||||
|
std::string GetGLSLVersionHeader() const;
|
||||||
|
|
||||||
|
bool CreateImGuiContext();
|
||||||
|
bool CreateGLResources();
|
||||||
|
|
||||||
|
void Render();
|
||||||
|
void RenderDisplay();
|
||||||
|
|
||||||
|
QOpenGLContext* m_gl_context = nullptr;
|
||||||
|
|
||||||
|
GL::Program m_display_program;
|
||||||
|
GLuint m_display_vao = 0;
|
||||||
|
GLuint m_display_texture_id = 0;
|
||||||
|
s32 m_display_offset_x = 0;
|
||||||
|
s32 m_display_offset_y = 0;
|
||||||
|
s32 m_display_width = 0;
|
||||||
|
s32 m_display_height = 0;
|
||||||
|
u32 m_display_texture_width = 0;
|
||||||
|
u32 m_display_texture_height = 0;
|
||||||
|
int m_display_top_margin = 0;
|
||||||
|
float m_display_aspect_ratio = 1.0f;
|
||||||
|
GLuint m_display_nearest_sampler = 0;
|
||||||
|
GLuint m_display_linear_sampler = 0;
|
||||||
|
|
||||||
|
bool m_is_gles = false;
|
||||||
|
bool m_display_texture_changed = false;
|
||||||
|
bool m_display_linear_filtering = false;
|
||||||
|
};
|
282
src/duckstation-qt/qthostinterface.cpp
Normal file
|
@ -0,0 +1,282 @@
|
||||||
|
#include "qthostinterface.h"
|
||||||
|
#include "YBaseLib/Log.h"
|
||||||
|
#include "common/null_audio_stream.h"
|
||||||
|
#include "core/game_list.h"
|
||||||
|
#include "core/gpu.h"
|
||||||
|
#include "core/system.h"
|
||||||
|
#include "qtsettingsinterface.h"
|
||||||
|
#include <QtCore/QCoreApplication>
|
||||||
|
#include <QtWidgets/QMessageBox>
|
||||||
|
#include <memory>
|
||||||
|
Log_SetChannel(QtHostInterface);
|
||||||
|
|
||||||
|
QtHostInterface::QtHostInterface(QObject* parent)
|
||||||
|
: QObject(parent), m_qsettings("duckstation-qt.ini", QSettings::IniFormat)
|
||||||
|
{
|
||||||
|
checkSettings();
|
||||||
|
createGameList();
|
||||||
|
createThread();
|
||||||
|
}
|
||||||
|
|
||||||
|
QtHostInterface::~QtHostInterface()
|
||||||
|
{
|
||||||
|
Assert(!m_opengl_display_window);
|
||||||
|
stopThread();
|
||||||
|
}
|
||||||
|
|
||||||
|
void QtHostInterface::ReportError(const char* message)
|
||||||
|
{
|
||||||
|
// QMessageBox::critical(nullptr, tr("DuckStation Error"), message, QMessageBox::Ok);
|
||||||
|
}
|
||||||
|
|
||||||
|
void QtHostInterface::ReportMessage(const char* message)
|
||||||
|
{
|
||||||
|
// QMessageBox::information(nullptr, tr("DuckStation Information"), message, QMessageBox::Ok);
|
||||||
|
}
|
||||||
|
|
||||||
|
void QtHostInterface::setDefaultSettings()
|
||||||
|
{
|
||||||
|
QtSettingsInterface si(m_qsettings);
|
||||||
|
m_settings.SetDefaults();
|
||||||
|
m_settings.Save(si);
|
||||||
|
m_qsettings.sync();
|
||||||
|
}
|
||||||
|
|
||||||
|
void QtHostInterface::applySettings()
|
||||||
|
{
|
||||||
|
QtSettingsInterface si(m_qsettings);
|
||||||
|
m_settings.Load(si);
|
||||||
|
}
|
||||||
|
|
||||||
|
void QtHostInterface::checkSettings()
|
||||||
|
{
|
||||||
|
const QSettings::Status settings_status = m_qsettings.status();
|
||||||
|
if (settings_status != QSettings::NoError)
|
||||||
|
m_qsettings.clear();
|
||||||
|
|
||||||
|
const QString settings_version_key = QStringLiteral("General/SettingsVersion");
|
||||||
|
const int expected_version = 1;
|
||||||
|
const QVariant settings_version_var = m_qsettings.value(settings_version_key);
|
||||||
|
bool settings_version_okay;
|
||||||
|
int settings_version = settings_version_var.toInt(&settings_version_okay);
|
||||||
|
if (!settings_version_okay)
|
||||||
|
settings_version = 0;
|
||||||
|
if (settings_version != expected_version)
|
||||||
|
{
|
||||||
|
Log_WarningPrintf("Settings version %d does not match expected version %d, resetting", settings_version,
|
||||||
|
expected_version);
|
||||||
|
m_qsettings.clear();
|
||||||
|
m_qsettings.setValue(settings_version_key, expected_version);
|
||||||
|
setDefaultSettings();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void QtHostInterface::createGameList()
|
||||||
|
{
|
||||||
|
m_game_list = std::make_unique<GameList>();
|
||||||
|
updateGameListDatabase(false);
|
||||||
|
refreshGameList(false);
|
||||||
|
}
|
||||||
|
|
||||||
|
void QtHostInterface::updateGameListDatabase(bool refresh_list /*= true*/)
|
||||||
|
{
|
||||||
|
m_game_list->ClearDatabase();
|
||||||
|
|
||||||
|
const QString redump_dat_path = m_qsettings.value("GameList/RedumpDatabasePath").toString();
|
||||||
|
if (!redump_dat_path.isEmpty())
|
||||||
|
m_game_list->ParseRedumpDatabase(redump_dat_path.toStdString().c_str());
|
||||||
|
|
||||||
|
if (refresh_list)
|
||||||
|
refreshGameList(true);
|
||||||
|
}
|
||||||
|
|
||||||
|
void QtHostInterface::refreshGameList(bool invalidate_cache /*= false*/)
|
||||||
|
{
|
||||||
|
QtSettingsInterface si(m_qsettings);
|
||||||
|
m_game_list->SetDirectoriesFromSettings(si);
|
||||||
|
m_game_list->RescanAllDirectories();
|
||||||
|
emit gameListRefreshed();
|
||||||
|
}
|
||||||
|
|
||||||
|
QWidget* QtHostInterface::createDisplayWidget(QWidget* parent)
|
||||||
|
{
|
||||||
|
m_opengl_display_window = new OpenGLDisplayWindow(nullptr);
|
||||||
|
m_display.release();
|
||||||
|
m_display = std::unique_ptr<HostDisplay>(static_cast<HostDisplay*>(m_opengl_display_window));
|
||||||
|
return QWidget::createWindowContainer(m_opengl_display_window, parent);
|
||||||
|
}
|
||||||
|
|
||||||
|
void QtHostInterface::destroyDisplayWidget()
|
||||||
|
{
|
||||||
|
m_display.release();
|
||||||
|
delete m_opengl_display_window;
|
||||||
|
m_opengl_display_window = nullptr;
|
||||||
|
}
|
||||||
|
|
||||||
|
void QtHostInterface::bootSystem(QString initial_filename, QString initial_save_state_filename)
|
||||||
|
{
|
||||||
|
emit emulationStarting();
|
||||||
|
|
||||||
|
if (!m_opengl_display_window->createGLContext(m_worker_thread))
|
||||||
|
{
|
||||||
|
emit emulationStopped();
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
QMetaObject::invokeMethod(this, "doBootSystem", Qt::QueuedConnection, Q_ARG(QString, initial_filename),
|
||||||
|
Q_ARG(QString, initial_save_state_filename));
|
||||||
|
}
|
||||||
|
|
||||||
|
void QtHostInterface::powerOffSystem()
|
||||||
|
{
|
||||||
|
if (!isOnWorkerThread())
|
||||||
|
{
|
||||||
|
QMetaObject::invokeMethod(this, "doPowerOffSystem", Qt::QueuedConnection);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!m_system)
|
||||||
|
{
|
||||||
|
Log_ErrorPrintf("powerOffSystem() called without system");
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
m_system.reset();
|
||||||
|
m_opengl_display_window->destroyGLContext();
|
||||||
|
|
||||||
|
emit emulationStopped();
|
||||||
|
}
|
||||||
|
|
||||||
|
void QtHostInterface::resetSystem()
|
||||||
|
{
|
||||||
|
if (!isOnWorkerThread())
|
||||||
|
{
|
||||||
|
QMetaObject::invokeMethod(this, "resetSystem", Qt::QueuedConnection);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!m_system)
|
||||||
|
{
|
||||||
|
Log_ErrorPrintf("resetSystem() called without system");
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
HostInterface::ResetSystem();
|
||||||
|
}
|
||||||
|
|
||||||
|
void QtHostInterface::pauseSystem(bool paused)
|
||||||
|
{
|
||||||
|
if (!isOnWorkerThread())
|
||||||
|
{
|
||||||
|
QMetaObject::invokeMethod(this, "pauseSystem", Qt::QueuedConnection, Q_ARG(bool, paused));
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
m_paused = paused;
|
||||||
|
emit emulationPaused(paused);
|
||||||
|
}
|
||||||
|
|
||||||
|
void QtHostInterface::changeDisc(QString new_disc_filename) {}
|
||||||
|
|
||||||
|
void QtHostInterface::doBootSystem(QString initial_filename, QString initial_save_state_filename)
|
||||||
|
{
|
||||||
|
if (!m_opengl_display_window->initializeGLContext())
|
||||||
|
{
|
||||||
|
emit emulationStopped();
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
m_audio_stream = NullAudioStream::Create();
|
||||||
|
m_audio_stream->Reconfigure();
|
||||||
|
|
||||||
|
std::string initial_filename_str = initial_filename.toStdString();
|
||||||
|
std::string initial_save_state_filename_str = initial_save_state_filename.toStdString();
|
||||||
|
if (!CreateSystem() ||
|
||||||
|
!BootSystem(initial_filename_str.empty() ? nullptr : initial_filename_str.c_str(),
|
||||||
|
initial_save_state_filename_str.empty() ? nullptr : initial_save_state_filename_str.c_str()))
|
||||||
|
{
|
||||||
|
m_opengl_display_window->destroyGLContext();
|
||||||
|
emit emulationStopped();
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
emit emulationStarted();
|
||||||
|
}
|
||||||
|
|
||||||
|
void QtHostInterface::createThread()
|
||||||
|
{
|
||||||
|
m_original_thread = QThread::currentThread();
|
||||||
|
m_worker_thread = new Thread(this);
|
||||||
|
m_worker_thread->start();
|
||||||
|
moveToThread(m_worker_thread);
|
||||||
|
}
|
||||||
|
|
||||||
|
void QtHostInterface::stopThread()
|
||||||
|
{
|
||||||
|
Assert(!isOnWorkerThread());
|
||||||
|
|
||||||
|
QMetaObject::invokeMethod(this, "doStopThread", Qt::QueuedConnection);
|
||||||
|
m_worker_thread->wait();
|
||||||
|
}
|
||||||
|
|
||||||
|
void QtHostInterface::doStopThread()
|
||||||
|
{
|
||||||
|
m_shutdown_flag.store(true);
|
||||||
|
}
|
||||||
|
|
||||||
|
void QtHostInterface::threadEntryPoint()
|
||||||
|
{
|
||||||
|
while (!m_shutdown_flag.load())
|
||||||
|
{
|
||||||
|
if (!m_system)
|
||||||
|
{
|
||||||
|
// wait until we have a system before running
|
||||||
|
QCoreApplication::processEvents(QEventLoop::AllEvents, 1000);
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
// execute the system, polling events inbetween frames
|
||||||
|
// simulate the system if not paused
|
||||||
|
if (m_system && !m_paused)
|
||||||
|
m_system->RunFrame();
|
||||||
|
|
||||||
|
// rendering
|
||||||
|
{
|
||||||
|
// DrawImGui();
|
||||||
|
|
||||||
|
if (m_system)
|
||||||
|
m_system->GetGPU()->ResetGraphicsAPIState();
|
||||||
|
|
||||||
|
// ImGui::Render();
|
||||||
|
m_display->Render();
|
||||||
|
|
||||||
|
// ImGui::NewFrame();
|
||||||
|
|
||||||
|
if (m_system)
|
||||||
|
{
|
||||||
|
m_system->GetGPU()->RestoreGraphicsAPIState();
|
||||||
|
|
||||||
|
if (m_speed_limiter_enabled)
|
||||||
|
Throttle();
|
||||||
|
}
|
||||||
|
|
||||||
|
UpdatePerformanceCounters();
|
||||||
|
}
|
||||||
|
|
||||||
|
QCoreApplication::processEvents(QEventLoop::AllEvents, m_paused ? 16 : 0);
|
||||||
|
}
|
||||||
|
|
||||||
|
m_system.reset();
|
||||||
|
|
||||||
|
// move back to UI thread
|
||||||
|
moveToThread(m_original_thread);
|
||||||
|
}
|
||||||
|
|
||||||
|
QtHostInterface::Thread::Thread(QtHostInterface* parent) : QThread(parent), m_parent(parent) {}
|
||||||
|
|
||||||
|
QtHostInterface::Thread::~Thread() = default;
|
||||||
|
|
||||||
|
void QtHostInterface::Thread::run()
|
||||||
|
{
|
||||||
|
m_parent->threadEntryPoint();
|
||||||
|
}
|
89
src/duckstation-qt/qthostinterface.h
Normal file
|
@ -0,0 +1,89 @@
|
||||||
|
#pragma once
|
||||||
|
#include <atomic>
|
||||||
|
#include <memory>
|
||||||
|
#include <QtCore/QObject>
|
||||||
|
#include <QtCore/QThread>
|
||||||
|
#include <QtCore/QSettings>
|
||||||
|
#include "core/host_interface.h"
|
||||||
|
#include "opengldisplaywindow.h"
|
||||||
|
|
||||||
|
class QWidget;
|
||||||
|
|
||||||
|
class GameList;
|
||||||
|
|
||||||
|
class QtHostInterface : public QObject, private HostInterface
|
||||||
|
{
|
||||||
|
Q_OBJECT
|
||||||
|
|
||||||
|
public:
|
||||||
|
explicit QtHostInterface(QObject* parent = nullptr);
|
||||||
|
~QtHostInterface();
|
||||||
|
|
||||||
|
void ReportError(const char* message) override;
|
||||||
|
void ReportMessage(const char* message) override;
|
||||||
|
|
||||||
|
const QSettings& getQSettings() const { return m_qsettings; }
|
||||||
|
QSettings& getQSettings() { return m_qsettings; }
|
||||||
|
void setDefaultSettings();
|
||||||
|
void applySettings();
|
||||||
|
|
||||||
|
const GameList* getGameList() const { return m_game_list.get(); }
|
||||||
|
GameList* getGameList() { return m_game_list.get(); }
|
||||||
|
void updateGameListDatabase(bool refresh_list = true);
|
||||||
|
void refreshGameList(bool invalidate_cache = false);
|
||||||
|
|
||||||
|
bool isOnWorkerThread() const { return QThread::currentThread() == m_worker_thread; }
|
||||||
|
|
||||||
|
QWidget* createDisplayWidget(QWidget* parent);
|
||||||
|
void destroyDisplayWidget();
|
||||||
|
|
||||||
|
void bootSystem(QString initial_filename, QString initial_save_state_filename);
|
||||||
|
|
||||||
|
Q_SIGNALS:
|
||||||
|
void emulationStarting();
|
||||||
|
void emulationStarted();
|
||||||
|
void emulationStopped();
|
||||||
|
void emulationPaused(bool paused);
|
||||||
|
void gameListRefreshed();
|
||||||
|
|
||||||
|
public Q_SLOTS:
|
||||||
|
void powerOffSystem();
|
||||||
|
void resetSystem();
|
||||||
|
void pauseSystem(bool paused);
|
||||||
|
void changeDisc(QString new_disc_filename);
|
||||||
|
|
||||||
|
private Q_SLOTS:
|
||||||
|
void doBootSystem(QString initial_filename, QString initial_save_state_filename);
|
||||||
|
void doStopThread();
|
||||||
|
|
||||||
|
private:
|
||||||
|
class Thread : public QThread
|
||||||
|
{
|
||||||
|
public:
|
||||||
|
Thread(QtHostInterface* parent);
|
||||||
|
~Thread();
|
||||||
|
|
||||||
|
protected:
|
||||||
|
void run() override;
|
||||||
|
|
||||||
|
private:
|
||||||
|
QtHostInterface* m_parent;
|
||||||
|
};
|
||||||
|
|
||||||
|
void checkSettings();
|
||||||
|
void createGameList();
|
||||||
|
void createThread();
|
||||||
|
void stopThread();
|
||||||
|
void threadEntryPoint();
|
||||||
|
|
||||||
|
QSettings m_qsettings;
|
||||||
|
|
||||||
|
std::unique_ptr<GameList> m_game_list;
|
||||||
|
|
||||||
|
OpenGLDisplayWindow* m_opengl_display_window = nullptr;
|
||||||
|
QThread* m_original_thread = nullptr;
|
||||||
|
Thread* m_worker_thread = nullptr;
|
||||||
|
|
||||||
|
std::atomic_bool m_shutdown_flag{ false };
|
||||||
|
};
|
||||||
|
|
142
src/duckstation-qt/qtsettingsinterface.cpp
Normal file
|
@ -0,0 +1,142 @@
|
||||||
|
#include "qtsettingsinterface.h"
|
||||||
|
#include <QtCore/QSettings>
|
||||||
|
#include <algorithm>
|
||||||
|
|
||||||
|
static QString GetFullKey(const char* section, const char* key)
|
||||||
|
{
|
||||||
|
return QStringLiteral("%1/%2").arg(section, key);
|
||||||
|
}
|
||||||
|
|
||||||
|
QtSettingsInterface::QtSettingsInterface(QSettings& settings) : m_settings(settings) {}
|
||||||
|
|
||||||
|
QtSettingsInterface::~QtSettingsInterface() = default;
|
||||||
|
|
||||||
|
int QtSettingsInterface::GetIntValue(const char* section, const char* key, int default_value /*= 0*/)
|
||||||
|
{
|
||||||
|
QVariant value = m_settings.value(GetFullKey(section, key));
|
||||||
|
if (!value.isValid())
|
||||||
|
return default_value;
|
||||||
|
|
||||||
|
bool converted_value_okay;
|
||||||
|
int converted_value = value.toInt(&converted_value_okay);
|
||||||
|
if (!converted_value_okay)
|
||||||
|
return default_value;
|
||||||
|
else
|
||||||
|
return converted_value;
|
||||||
|
}
|
||||||
|
|
||||||
|
float QtSettingsInterface::GetFloatValue(const char* section, const char* key, float default_value /*= 0.0f*/)
|
||||||
|
{
|
||||||
|
QVariant value = m_settings.value(GetFullKey(section, key));
|
||||||
|
if (!value.isValid())
|
||||||
|
return default_value;
|
||||||
|
|
||||||
|
bool converted_value_okay;
|
||||||
|
float converted_value = value.toFloat(&converted_value_okay);
|
||||||
|
if (!converted_value_okay)
|
||||||
|
return default_value;
|
||||||
|
else
|
||||||
|
return converted_value;
|
||||||
|
}
|
||||||
|
|
||||||
|
bool QtSettingsInterface::GetBoolValue(const char* section, const char* key, bool default_value /*= false*/)
|
||||||
|
{
|
||||||
|
QVariant value = m_settings.value(GetFullKey(section, key));
|
||||||
|
return value.isValid() ? value.toBool() : default_value;
|
||||||
|
}
|
||||||
|
|
||||||
|
std::string QtSettingsInterface::GetStringValue(const char* section, const char* key,
|
||||||
|
const char* default_value /*= ""*/)
|
||||||
|
{
|
||||||
|
QVariant value = m_settings.value(GetFullKey(section, key));
|
||||||
|
return value.isValid() ? value.toString().toStdString() : std::string(default_value);
|
||||||
|
}
|
||||||
|
|
||||||
|
void QtSettingsInterface::SetIntValue(const char* section, const char* key, int value)
|
||||||
|
{
|
||||||
|
m_settings.setValue(GetFullKey(section, key), QVariant(value));
|
||||||
|
}
|
||||||
|
|
||||||
|
void QtSettingsInterface::SetFloatValue(const char* section, const char* key, float value)
|
||||||
|
{
|
||||||
|
m_settings.setValue(GetFullKey(section, key), QVariant(value));
|
||||||
|
}
|
||||||
|
|
||||||
|
void QtSettingsInterface::SetBoolValue(const char* section, const char* key, bool value)
|
||||||
|
{
|
||||||
|
m_settings.setValue(GetFullKey(section, key), QVariant(value));
|
||||||
|
}
|
||||||
|
|
||||||
|
void QtSettingsInterface::SetStringValue(const char* section, const char* key, const char* value)
|
||||||
|
{
|
||||||
|
m_settings.setValue(GetFullKey(section, key), QVariant(value));
|
||||||
|
}
|
||||||
|
|
||||||
|
std::vector<std::string> QtSettingsInterface::GetStringList(const char* section, const char* key)
|
||||||
|
{
|
||||||
|
QVariant value = m_settings.value(GetFullKey(section, key));
|
||||||
|
if (value.type() == QVariant::String)
|
||||||
|
return { value.toString().toStdString() };
|
||||||
|
else if (value.type() != QVariant::StringList)
|
||||||
|
return {};
|
||||||
|
|
||||||
|
QStringList value_sl = value.toStringList();
|
||||||
|
std::vector<std::string> results;
|
||||||
|
results.reserve(static_cast<unsigned>(value_sl.size()));
|
||||||
|
std::transform(value_sl.begin(), value_sl.end(), std::back_inserter(results),
|
||||||
|
[](const QString& str) { return str.toStdString(); });
|
||||||
|
return results;
|
||||||
|
}
|
||||||
|
|
||||||
|
void QtSettingsInterface::SetStringList(const char* section, const char* key,
|
||||||
|
const std::vector<std::string_view>& items)
|
||||||
|
{
|
||||||
|
QString full_key = GetFullKey(section, key);
|
||||||
|
if (items.empty())
|
||||||
|
{
|
||||||
|
m_settings.remove(full_key);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
QStringList sl;
|
||||||
|
sl.reserve(static_cast<int>(items.size()));
|
||||||
|
std::transform(items.begin(), items.end(), std::back_inserter(sl), [](const std::string_view& sv) {
|
||||||
|
return QString::fromLocal8Bit(sv.data(), static_cast<int>(sv.size()));
|
||||||
|
});
|
||||||
|
m_settings.setValue(full_key, sl);
|
||||||
|
}
|
||||||
|
|
||||||
|
bool QtSettingsInterface::RemoveFromStringList(const char* section, const char* key, const char* item)
|
||||||
|
{
|
||||||
|
QString full_key = GetFullKey(section, key);
|
||||||
|
QVariant var = m_settings.value(full_key);
|
||||||
|
QStringList sl = var.toStringList();
|
||||||
|
if (sl.removeAll(item) == 0)
|
||||||
|
return false;
|
||||||
|
|
||||||
|
if (sl.isEmpty())
|
||||||
|
m_settings.remove(full_key);
|
||||||
|
else
|
||||||
|
m_settings.setValue(full_key, sl);
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
bool QtSettingsInterface::AddToStringList(const char* section, const char* key, const char* item)
|
||||||
|
{
|
||||||
|
QString full_key = GetFullKey(section, key);
|
||||||
|
QVariant var = m_settings.value(full_key);
|
||||||
|
|
||||||
|
QStringList sl = (var.type() == QVariant::StringList) ? var.toStringList() : QStringList();
|
||||||
|
QString qitem(item);
|
||||||
|
if (sl.contains(qitem))
|
||||||
|
return false;
|
||||||
|
|
||||||
|
sl.push_back(qitem);
|
||||||
|
m_settings.setValue(full_key, sl);
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
void QtSettingsInterface::DeleteValue(const char* section, const char* key)
|
||||||
|
{
|
||||||
|
m_settings.remove(GetFullKey(section, key));
|
||||||
|
}
|
31
src/duckstation-qt/qtsettingsinterface.h
Normal file
|
@ -0,0 +1,31 @@
|
||||||
|
#pragma once
|
||||||
|
#include "core/settings.h"
|
||||||
|
|
||||||
|
class QSettings;
|
||||||
|
|
||||||
|
class QtSettingsInterface : public SettingsInterface
|
||||||
|
{
|
||||||
|
public:
|
||||||
|
QtSettingsInterface(QSettings& settings);
|
||||||
|
~QtSettingsInterface();
|
||||||
|
|
||||||
|
int GetIntValue(const char* section, const char* key, int default_value = 0) override;
|
||||||
|
float GetFloatValue(const char* section, const char* key, float default_value = 0.0f) override;
|
||||||
|
bool GetBoolValue(const char* section, const char* key, bool default_value = false) override;
|
||||||
|
std::string GetStringValue(const char* section, const char* key, const char* default_value = "") override;
|
||||||
|
|
||||||
|
void SetIntValue(const char* section, const char* key, int value) override;
|
||||||
|
void SetFloatValue(const char* section, const char* key, float value) override;
|
||||||
|
void SetBoolValue(const char* section, const char* key, bool value) override;
|
||||||
|
void SetStringValue(const char* section, const char* key, const char* value) override;
|
||||||
|
|
||||||
|
std::vector<std::string> GetStringList(const char* section, const char* key) override;
|
||||||
|
void SetStringList(const char* section, const char* key, const std::vector<std::string_view>& items) override;
|
||||||
|
bool RemoveFromStringList(const char* section, const char* key, const char* item) override;
|
||||||
|
bool AddToStringList(const char* section, const char* key, const char* item) override;
|
||||||
|
|
||||||
|
void DeleteValue(const char* section, const char* key) override;
|
||||||
|
|
||||||
|
private:
|
||||||
|
QSettings& m_settings;
|
||||||
|
};
|
23
src/duckstation-qt/qtutils.cpp
Normal file
|
@ -0,0 +1,23 @@
|
||||||
|
#include "qtutils.h"
|
||||||
|
#include <QtWidgets/QTableView>
|
||||||
|
#include <algorithm>
|
||||||
|
|
||||||
|
namespace QtUtils {
|
||||||
|
|
||||||
|
void ResizeColumnsForTableView(QTableView* view, const std::initializer_list<int>& widths)
|
||||||
|
{
|
||||||
|
const int total_width =
|
||||||
|
std::accumulate(widths.begin(), widths.end(), 0, [](int a, int b) { return a + std::max(b, 0); });
|
||||||
|
|
||||||
|
const int flex_width = std::max(view->width() - total_width - 2, 1);
|
||||||
|
|
||||||
|
int column_index = 0;
|
||||||
|
for (const int spec_width : widths)
|
||||||
|
{
|
||||||
|
const int width = spec_width < 0 ? flex_width : spec_width;
|
||||||
|
view->setColumnWidth(column_index, width);
|
||||||
|
column_index++;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
} // namespace QtUtils
|
12
src/duckstation-qt/qtutils.h
Normal file
|
@ -0,0 +1,12 @@
|
||||||
|
#pragma once
|
||||||
|
#include <initializer_list>
|
||||||
|
|
||||||
|
class QTableView;
|
||||||
|
|
||||||
|
namespace QtUtils {
|
||||||
|
|
||||||
|
/// Resizes columns of the table view to at the specified widths. A width of -1 will stretch the column to use the
|
||||||
|
/// remaining space.
|
||||||
|
void ResizeColumnsForTableView(QTableView* view, const std::initializer_list<int>& widths);
|
||||||
|
|
||||||
|
} // namespace QtUtils
|
29
src/duckstation-qt/resources/icons.qrc
Normal file
|
@ -0,0 +1,29 @@
|
||||||
|
<RCC>
|
||||||
|
<qresource>
|
||||||
|
<file>icons/applications-internet.png</file>
|
||||||
|
<file>icons/system-search.png</file>
|
||||||
|
<file>icons/list-add.png</file>
|
||||||
|
<file>icons/list-remove.png</file>
|
||||||
|
<file>icons/duck.png</file>
|
||||||
|
<file>icons/edit-find.png</file>
|
||||||
|
<file>icons/folder-open.png</file>
|
||||||
|
<file>icons/applications-development.png</file>
|
||||||
|
<file>icons/applications-other.png</file>
|
||||||
|
<file>icons/applications-system.png</file>
|
||||||
|
<file>icons/audio-card.png</file>
|
||||||
|
<file>icons/document-open.png</file>
|
||||||
|
<file>icons/document-save.png</file>
|
||||||
|
<file>icons/drive-optical.png</file>
|
||||||
|
<file>icons/drive-removable-media.png</file>
|
||||||
|
<file>icons/input-gaming.png</file>
|
||||||
|
<file>icons/media-flash.png</file>
|
||||||
|
<file>icons/media-optical.png</file>
|
||||||
|
<file>icons/media-playback-pause.png</file>
|
||||||
|
<file>icons/media-playback-start.png</file>
|
||||||
|
<file>icons/system-shutdown.png</file>
|
||||||
|
<file>icons/utilities-system-monitor.png</file>
|
||||||
|
<file>icons/video-display.png</file>
|
||||||
|
<file>icons/view-fullscreen.png</file>
|
||||||
|
<file>icons/view-refresh.png</file>
|
||||||
|
</qresource>
|
||||||
|
</RCC>
|
BIN
src/duckstation-qt/resources/icons/applications-development.png
Normal file
After Width: | Height: | Size: 2.1 KiB |
BIN
src/duckstation-qt/resources/icons/applications-internet.png
Normal file
After Width: | Height: | Size: 2.4 KiB |
BIN
src/duckstation-qt/resources/icons/applications-other.png
Normal file
After Width: | Height: | Size: 1.5 KiB |
BIN
src/duckstation-qt/resources/icons/applications-system.png
Normal file
After Width: | Height: | Size: 2.5 KiB |
BIN
src/duckstation-qt/resources/icons/audio-card.png
Normal file
After Width: | Height: | Size: 1.9 KiB |
BIN
src/duckstation-qt/resources/icons/document-open.png
Normal file
After Width: | Height: | Size: 1.5 KiB |
BIN
src/duckstation-qt/resources/icons/document-save.png
Normal file
After Width: | Height: | Size: 1.9 KiB |
BIN
src/duckstation-qt/resources/icons/drive-optical.png
Normal file
After Width: | Height: | Size: 1.3 KiB |
BIN
src/duckstation-qt/resources/icons/drive-removable-media.png
Normal file
After Width: | Height: | Size: 964 B |
BIN
src/duckstation-qt/resources/icons/duck.png
Normal file
After Width: | Height: | Size: 14 KiB |
BIN
src/duckstation-qt/resources/icons/duck_128.png
Normal file
After Width: | Height: | Size: 8.3 KiB |
BIN
src/duckstation-qt/resources/icons/duck_64.png
Normal file
After Width: | Height: | Size: 3.8 KiB |
BIN
src/duckstation-qt/resources/icons/edit-find.png
Normal file
After Width: | Height: | Size: 1.6 KiB |
BIN
src/duckstation-qt/resources/icons/folder-open.png
Normal file
After Width: | Height: | Size: 1.2 KiB |
BIN
src/duckstation-qt/resources/icons/input-gaming.png
Normal file
After Width: | Height: | Size: 1.4 KiB |
BIN
src/duckstation-qt/resources/icons/list-add.png
Normal file
After Width: | Height: | Size: 601 B |
BIN
src/duckstation-qt/resources/icons/list-remove.png
Normal file
After Width: | Height: | Size: 317 B |
BIN
src/duckstation-qt/resources/icons/media-flash.png
Normal file
After Width: | Height: | Size: 1.3 KiB |
BIN
src/duckstation-qt/resources/icons/media-optical.png
Normal file
After Width: | Height: | Size: 2.2 KiB |
BIN
src/duckstation-qt/resources/icons/media-playback-pause.png
Normal file
After Width: | Height: | Size: 481 B |
BIN
src/duckstation-qt/resources/icons/media-playback-start.png
Normal file
After Width: | Height: | Size: 1 KiB |
BIN
src/duckstation-qt/resources/icons/system-search.png
Normal file
After Width: | Height: | Size: 2.2 KiB |
BIN
src/duckstation-qt/resources/icons/system-shutdown.png
Normal file
After Width: | Height: | Size: 1 KiB |
BIN
src/duckstation-qt/resources/icons/utilities-system-monitor.png
Normal file
After Width: | Height: | Size: 1.8 KiB |
BIN
src/duckstation-qt/resources/icons/video-display.png
Normal file
After Width: | Height: | Size: 1.6 KiB |
BIN
src/duckstation-qt/resources/icons/view-fullscreen.png
Normal file
After Width: | Height: | Size: 1.2 KiB |
BIN
src/duckstation-qt/resources/icons/view-refresh.png
Normal file
After Width: | Height: | Size: 2 KiB |
42
src/duckstation-qt/settingsdialog.cpp
Normal file
|
@ -0,0 +1,42 @@
|
||||||
|
#include "settingsdialog.h"
|
||||||
|
#include "consolesettingswidget.h"
|
||||||
|
#include "gamelistsettingswidget.h"
|
||||||
|
#include "qthostinterface.h"
|
||||||
|
#include <QtWidgets/QTextEdit>
|
||||||
|
|
||||||
|
SettingsDialog::SettingsDialog(QtHostInterface* host_interface, QWidget* parent /* = nullptr */)
|
||||||
|
: QDialog(parent), m_host_interface(host_interface)
|
||||||
|
{
|
||||||
|
m_ui.setupUi(this);
|
||||||
|
|
||||||
|
m_console_settings = new ConsoleSettingsWidget(m_ui.settingsContainer);
|
||||||
|
m_game_list_settings = new GameListSettingsWidget(host_interface, m_ui.settingsContainer);
|
||||||
|
m_cpu_settings = new QWidget(m_ui.settingsContainer);
|
||||||
|
m_gpu_settings = new QWidget(m_ui.settingsContainer);
|
||||||
|
m_audio_settings = new QWidget(m_ui.settingsContainer);
|
||||||
|
|
||||||
|
m_ui.settingsContainer->insertWidget(0, m_console_settings);
|
||||||
|
m_ui.settingsContainer->insertWidget(1, m_game_list_settings);
|
||||||
|
m_ui.settingsContainer->insertWidget(2, m_cpu_settings);
|
||||||
|
m_ui.settingsContainer->insertWidget(3, m_gpu_settings);
|
||||||
|
m_ui.settingsContainer->insertWidget(4, m_audio_settings);
|
||||||
|
|
||||||
|
m_ui.settingsCategory->setCurrentRow(0);
|
||||||
|
m_ui.settingsContainer->setCurrentIndex(0);
|
||||||
|
connect(m_ui.settingsCategory, &QListWidget::currentRowChanged, this, &SettingsDialog::onCategoryCurrentRowChanged);
|
||||||
|
}
|
||||||
|
|
||||||
|
SettingsDialog::~SettingsDialog() = default;
|
||||||
|
|
||||||
|
void SettingsDialog::setCategory(Category category)
|
||||||
|
{
|
||||||
|
if (category >= Category::Count)
|
||||||
|
return;
|
||||||
|
|
||||||
|
m_ui.settingsCategory->setCurrentRow(static_cast<int>(category));
|
||||||
|
}
|
||||||
|
|
||||||
|
void SettingsDialog::onCategoryCurrentRowChanged(int row)
|
||||||
|
{
|
||||||
|
m_ui.settingsContainer->setCurrentIndex(row);
|
||||||
|
}
|
44
src/duckstation-qt/settingsdialog.h
Normal file
|
@ -0,0 +1,44 @@
|
||||||
|
#pragma once
|
||||||
|
|
||||||
|
#include <QtWidgets/QDialog>
|
||||||
|
#include "ui_settingsdialog.h"
|
||||||
|
|
||||||
|
class QtHostInterface;
|
||||||
|
|
||||||
|
class ConsoleSettingsWidget;
|
||||||
|
|
||||||
|
class SettingsDialog : public QDialog
|
||||||
|
{
|
||||||
|
Q_OBJECT
|
||||||
|
|
||||||
|
public:
|
||||||
|
enum class Category
|
||||||
|
{
|
||||||
|
ConsoleSettings,
|
||||||
|
GameListSettings,
|
||||||
|
CPUSettings,
|
||||||
|
GPUSettings,
|
||||||
|
AudioSettings,
|
||||||
|
Count
|
||||||
|
};
|
||||||
|
|
||||||
|
explicit SettingsDialog(QtHostInterface* host_interface, QWidget* parent = nullptr);
|
||||||
|
~SettingsDialog();
|
||||||
|
|
||||||
|
public Q_SLOTS:
|
||||||
|
void setCategory(Category category);
|
||||||
|
|
||||||
|
private Q_SLOTS:
|
||||||
|
void onCategoryCurrentRowChanged(int row);
|
||||||
|
|
||||||
|
private:
|
||||||
|
Ui::SettingsDialog m_ui;
|
||||||
|
|
||||||
|
QtHostInterface* m_host_interface;
|
||||||
|
|
||||||
|
ConsoleSettingsWidget* m_console_settings = nullptr;
|
||||||
|
QWidget* m_game_list_settings = nullptr;
|
||||||
|
QWidget* m_cpu_settings = nullptr;
|
||||||
|
QWidget* m_gpu_settings = nullptr;
|
||||||
|
QWidget* m_audio_settings = nullptr;
|
||||||
|
};
|
150
src/duckstation-qt/settingsdialog.ui
Normal file
|
@ -0,0 +1,150 @@
|
||||||
|
<?xml version="1.0" encoding="UTF-8"?>
|
||||||
|
<ui version="4.0">
|
||||||
|
<class>SettingsDialog</class>
|
||||||
|
<widget class="QDialog" name="SettingsDialog">
|
||||||
|
<property name="windowModality">
|
||||||
|
<enum>Qt::WindowModal</enum>
|
||||||
|
</property>
|
||||||
|
<property name="geometry">
|
||||||
|
<rect>
|
||||||
|
<x>0</x>
|
||||||
|
<y>0</y>
|
||||||
|
<width>637</width>
|
||||||
|
<height>405</height>
|
||||||
|
</rect>
|
||||||
|
</property>
|
||||||
|
<property name="windowTitle">
|
||||||
|
<string>DuckStation Settings</string>
|
||||||
|
</property>
|
||||||
|
<property name="windowIcon">
|
||||||
|
<iconset resource="icons.qrc">
|
||||||
|
<normaloff>:/icons/applications-system.png</normaloff>:/icons/applications-system.png</iconset>
|
||||||
|
</property>
|
||||||
|
<layout class="QGridLayout" name="gridLayout_2">
|
||||||
|
<item row="0" column="0">
|
||||||
|
<layout class="QGridLayout" name="gridLayout">
|
||||||
|
<property name="horizontalSpacing">
|
||||||
|
<number>10</number>
|
||||||
|
</property>
|
||||||
|
<item row="0" column="0">
|
||||||
|
<widget class="QListWidget" name="settingsCategory">
|
||||||
|
<property name="maximumSize">
|
||||||
|
<size>
|
||||||
|
<width>160</width>
|
||||||
|
<height>16777215</height>
|
||||||
|
</size>
|
||||||
|
</property>
|
||||||
|
<property name="iconSize">
|
||||||
|
<size>
|
||||||
|
<width>32</width>
|
||||||
|
<height>32</height>
|
||||||
|
</size>
|
||||||
|
</property>
|
||||||
|
<item>
|
||||||
|
<property name="text">
|
||||||
|
<string>Console Settings</string>
|
||||||
|
</property>
|
||||||
|
<property name="icon">
|
||||||
|
<iconset resource="icons.qrc">
|
||||||
|
<normaloff>:/icons/utilities-system-monitor.png</normaloff>:/icons/utilities-system-monitor.png</iconset>
|
||||||
|
</property>
|
||||||
|
</item>
|
||||||
|
<item>
|
||||||
|
<property name="text">
|
||||||
|
<string>Game List Settings</string>
|
||||||
|
</property>
|
||||||
|
<property name="icon">
|
||||||
|
<iconset resource="icons.qrc">
|
||||||
|
<normaloff>:/icons/folder-open.png</normaloff>:/icons/folder-open.png</iconset>
|
||||||
|
</property>
|
||||||
|
</item>
|
||||||
|
<item>
|
||||||
|
<property name="text">
|
||||||
|
<string>CPU Settings</string>
|
||||||
|
</property>
|
||||||
|
<property name="icon">
|
||||||
|
<iconset resource="icons.qrc">
|
||||||
|
<normaloff>:/icons/applications-other.png</normaloff>:/icons/applications-other.png</iconset>
|
||||||
|
</property>
|
||||||
|
</item>
|
||||||
|
<item>
|
||||||
|
<property name="text">
|
||||||
|
<string>GPU Settings</string>
|
||||||
|
</property>
|
||||||
|
<property name="icon">
|
||||||
|
<iconset resource="icons.qrc">
|
||||||
|
<normaloff>:/icons/video-display.png</normaloff>:/icons/video-display.png</iconset>
|
||||||
|
</property>
|
||||||
|
</item>
|
||||||
|
<item>
|
||||||
|
<property name="text">
|
||||||
|
<string>Audio Settings</string>
|
||||||
|
</property>
|
||||||
|
<property name="icon">
|
||||||
|
<iconset resource="icons.qrc">
|
||||||
|
<normaloff>:/icons/audio-card.png</normaloff>:/icons/audio-card.png</iconset>
|
||||||
|
</property>
|
||||||
|
</item>
|
||||||
|
</widget>
|
||||||
|
</item>
|
||||||
|
<item row="0" column="1">
|
||||||
|
<widget class="QStackedWidget" name="settingsContainer">
|
||||||
|
<property name="currentIndex">
|
||||||
|
<number>0</number>
|
||||||
|
</property>
|
||||||
|
<widget class="QWidget" name="page"/>
|
||||||
|
<widget class="QWidget" name="page_2"/>
|
||||||
|
</widget>
|
||||||
|
</item>
|
||||||
|
<item row="1" column="0" colspan="2">
|
||||||
|
<widget class="QDialogButtonBox" name="buttonBox">
|
||||||
|
<property name="orientation">
|
||||||
|
<enum>Qt::Horizontal</enum>
|
||||||
|
</property>
|
||||||
|
<property name="standardButtons">
|
||||||
|
<set>QDialogButtonBox::Apply|QDialogButtonBox::Cancel|QDialogButtonBox::Ok|QDialogButtonBox::RestoreDefaults</set>
|
||||||
|
</property>
|
||||||
|
</widget>
|
||||||
|
</item>
|
||||||
|
</layout>
|
||||||
|
</item>
|
||||||
|
</layout>
|
||||||
|
</widget>
|
||||||
|
<resources>
|
||||||
|
<include location="icons.qrc"/>
|
||||||
|
</resources>
|
||||||
|
<connections>
|
||||||
|
<connection>
|
||||||
|
<sender>buttonBox</sender>
|
||||||
|
<signal>accepted()</signal>
|
||||||
|
<receiver>SettingsDialog</receiver>
|
||||||
|
<slot>accept()</slot>
|
||||||
|
<hints>
|
||||||
|
<hint type="sourcelabel">
|
||||||
|
<x>248</x>
|
||||||
|
<y>254</y>
|
||||||
|
</hint>
|
||||||
|
<hint type="destinationlabel">
|
||||||
|
<x>157</x>
|
||||||
|
<y>274</y>
|
||||||
|
</hint>
|
||||||
|
</hints>
|
||||||
|
</connection>
|
||||||
|
<connection>
|
||||||
|
<sender>buttonBox</sender>
|
||||||
|
<signal>rejected()</signal>
|
||||||
|
<receiver>SettingsDialog</receiver>
|
||||||
|
<slot>reject()</slot>
|
||||||
|
<hints>
|
||||||
|
<hint type="sourcelabel">
|
||||||
|
<x>316</x>
|
||||||
|
<y>260</y>
|
||||||
|
</hint>
|
||||||
|
<hint type="destinationlabel">
|
||||||
|
<x>286</x>
|
||||||
|
<y>274</y>
|
||||||
|
</hint>
|
||||||
|
</hints>
|
||||||
|
</connection>
|
||||||
|
</connections>
|
||||||
|
</ui>
|