Add initial work on Qt frontend
|
@ -27,7 +27,7 @@ jobs:
|
|||
run: |
|
||||
mkdir build-debug
|
||||
cd build-debug
|
||||
cmake -DCMAKE_BUILD_TYPE=Debug ..
|
||||
cmake -DCMAKE_BUILD_TYPE=Debug -DBUILD_QT_FRONTEND=OFF ..
|
||||
make
|
||||
|
||||
- name: Compile release build
|
||||
|
@ -35,6 +35,6 @@ jobs:
|
|||
run: |
|
||||
mkdir build-release
|
||||
cd build-release
|
||||
cmake -DCMAKE_BUILD_TYPE=Release ..
|
||||
cmake -DCMAKE_BUILD_TYPE=Release -DBUILD_QT_FRONTEND=OFF ..
|
||||
make
|
||||
|
||||
|
|
|
@ -4,6 +4,11 @@ project(duckstation C CXX)
|
|||
# Pull in modules.
|
||||
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.
|
||||
if(WIN32)
|
||||
|
@ -30,8 +35,15 @@ endif()
|
|||
|
||||
# Required libraries.
|
||||
if(NOT ANDROID)
|
||||
find_package(SDL2 REQUIRED)
|
||||
else()
|
||||
if(BUILD_SDL_FRONTEND)
|
||||
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)
|
||||
endif()
|
||||
|
||||
|
@ -117,4 +129,4 @@ add_subdirectory(src)
|
|||
|
||||
if(ANDROID)
|
||||
add_subdirectory(android/app/src/cpp)
|
||||
endif()
|
||||
endif()
|
||||
|
|
|
@ -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>
|
|
@ -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>
|
|
@ -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
|
||||
Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "tinyxml2", "dep\tinyxml2\tinyxml2.vcxproj", "{933118A9-68C5-47B4-B151-B03C93961623}"
|
||||
EndProject
|
||||
Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "duckstation-qt", "src\duckstation-qt\duckstation-qt.vcxproj", "{28F14272-0EC4-41BB-849F-182ADB81AF70}"
|
||||
EndProject
|
||||
Global
|
||||
GlobalSection(SolutionConfigurationPlatforms) = preSolution
|
||||
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|x86.ActiveCfg = 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
|
||||
GlobalSection(SolutionProperties) = preSolution
|
||||
HideSolutionNode = FALSE
|
||||
|
|
|
@ -1,6 +1,11 @@
|
|||
add_subdirectory(common)
|
||||
add_subdirectory(core)
|
||||
|
||||
if(NOT ANDROID)
|
||||
if(BUILD_SDL_FRONTEND)
|
||||
add_subdirectory(duckstation)
|
||||
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* slash_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)
|
||||
slash_ptr = backslash_ptr;
|
||||
else if (forwardslash_ptr)
|
||||
|
|
|
@ -4,6 +4,7 @@
|
|||
#include "bios.h"
|
||||
#include "common/cd_image.h"
|
||||
#include "common/iso_reader.h"
|
||||
#include "settings.h"
|
||||
#include <algorithm>
|
||||
#include <array>
|
||||
#include <cctype>
|
||||
|
@ -194,11 +195,6 @@ std::optional<ConsoleRegion> GameList::GetRegionForPath(const char* image_path)
|
|||
return GetRegionForImage(cdi.get());
|
||||
}
|
||||
|
||||
void GameList::AddDirectory(const char* path, bool recursive)
|
||||
{
|
||||
ScanDirectory(path, recursive);
|
||||
}
|
||||
|
||||
bool GameList::IsExeFileName(const char* path)
|
||||
{
|
||||
const char* extension = std::strrchr(path, '.');
|
||||
|
@ -404,6 +400,40 @@ private:
|
|||
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)
|
||||
{
|
||||
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);
|
||||
return true;
|
||||
}
|
||||
|
||||
void GameList::ClearDatabase()
|
||||
{
|
||||
m_database.clear();
|
||||
}
|
||||
|
|
|
@ -8,6 +8,8 @@
|
|||
|
||||
class CDImage;
|
||||
|
||||
class SettingsInterface;
|
||||
|
||||
class GameList
|
||||
{
|
||||
public:
|
||||
|
@ -54,18 +56,28 @@ public:
|
|||
const EntryList& GetEntries() const { return m_entries; }
|
||||
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);
|
||||
void ClearDatabase();
|
||||
|
||||
private:
|
||||
struct DirectoryEntry
|
||||
{
|
||||
std::string path;
|
||||
bool recursive;
|
||||
};
|
||||
|
||||
static bool IsExeFileName(const char* path);
|
||||
static bool GetExeListEntry(const char* path, GameListEntry* entry);
|
||||
|
||||
bool GetGameListEntry(const char* path, GameListEntry* entry);
|
||||
|
||||
void ScanDirectory(const char* path, bool recursive);
|
||||
|
||||
DatabaseMap m_database;
|
||||
EntryList m_entries;
|
||||
|
||||
std::vector<DirectoryEntry> m_search_directories;
|
||||
};
|
||||
|
|
|
@ -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)
|
|
@ -0,0 +1,8 @@
|
|||
#include "consolesettingswidget.h"
|
||||
|
||||
ConsoleSettingsWidget::ConsoleSettingsWidget(QWidget* parent /*= nullptr*/) : QWidget(parent)
|
||||
{
|
||||
m_ui.setupUi(this);
|
||||
}
|
||||
|
||||
ConsoleSettingsWidget::~ConsoleSettingsWidget() = default;
|
|
@ -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;
|
||||
};
|
|
@ -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>
|
|
@ -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>
|
|
@ -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>
|
|
@ -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"));
|
||||
}
|
|
@ -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;
|
||||
};
|
|
@ -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>
|
|
@ -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});
|
||||
}
|
|
@ -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;
|
||||
};
|
|
@ -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();
|
||||
}
|
|
@ -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);
|
||||
}
|
|
@ -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;
|
||||
};
|
|
@ -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>
|
|
@ -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);
|
||||
}
|
|
@ -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;
|
||||
};
|
|
@ -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();
|
||||
}
|
|
@ -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 };
|
||||
};
|
||||
|
|
@ -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));
|
||||
}
|
|
@ -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;
|
||||
};
|
|
@ -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
|
|
@ -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
|
|
@ -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>
|
After Width: | Height: | Size: 2.1 KiB |
After Width: | Height: | Size: 2.4 KiB |
After Width: | Height: | Size: 1.5 KiB |
After Width: | Height: | Size: 2.5 KiB |
After Width: | Height: | Size: 1.9 KiB |
After Width: | Height: | Size: 1.5 KiB |
After Width: | Height: | Size: 1.9 KiB |
After Width: | Height: | Size: 1.3 KiB |
After Width: | Height: | Size: 964 B |
After Width: | Height: | Size: 14 KiB |
After Width: | Height: | Size: 8.3 KiB |
After Width: | Height: | Size: 3.8 KiB |
After Width: | Height: | Size: 1.6 KiB |
After Width: | Height: | Size: 1.2 KiB |
After Width: | Height: | Size: 1.4 KiB |
After Width: | Height: | Size: 601 B |
After Width: | Height: | Size: 317 B |
After Width: | Height: | Size: 1.3 KiB |
After Width: | Height: | Size: 2.2 KiB |
After Width: | Height: | Size: 481 B |
After Width: | Height: | Size: 1.0 KiB |
After Width: | Height: | Size: 2.2 KiB |
After Width: | Height: | Size: 1.0 KiB |
After Width: | Height: | Size: 1.8 KiB |
After Width: | Height: | Size: 1.6 KiB |
After Width: | Height: | Size: 1.2 KiB |
After Width: | Height: | Size: 2.0 KiB |
|
@ -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);
|
||||
}
|
|
@ -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;
|
||||
};
|
|
@ -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>
|