semi-usable

This commit is contained in:
Connor McLaughlin 2022-07-16 00:01:20 +10:00
parent 5e278032c2
commit ed9690413b
154 changed files with 2627 additions and 2578 deletions

View File

@ -2,29 +2,30 @@
<Project ToolsVersion="15.0" xmlns="http://schemas.microsoft.com/developer/msbuild/2003">
<PropertyGroup Label="UserMacros">
<BinaryOutputDir>$(SolutionDir)bin\$(Platform)\</BinaryOutputDir>
<QTDIRDefault Condition="'$(Platform)'=='x64'">$(SolutionDir)dep\msvc\qt\6.1.0\msvc2019_64\</QTDIRDefault>
<QTDIRDefault Condition="'$(Platform)'=='ARM64'">$(SolutionDir)dep\msvc\qt\6.1.0\msvc2019_arm64\</QTDIRDefault>
<QTDIR Condition="Exists('$(QTDIRDefault)') And ('$(QTDIR)'=='' Or !Exists('$(QTDIR)'))">$(QTDIRDefault)</QTDIR>
<QTDIR Condition="Exists('$(QTDIR)') And !HasTrailingSlash('$(QTDIR)')">$(QTDIR)\</QTDIR>
<QTDIRHost>$(SolutionDir)dep\msvc\qt\6.1.0\msvc2019_64\</QTDIRHost>
<QtDirValid>false</QtDirValid>
<QtDirValid Condition="Exists('$(QTDIR)')">true</QtDirValid>
<QtIncludeDir>$(QTDIR)include\</QtIncludeDir>
<QtLibDir>$(QTDIR)lib\</QtLibDir>
<QtBinDir>$(QTDIR)bin\</QtBinDir>
<QtHostBinDir>$(QTDIRHost)bin\</QtHostBinDir>
<QtPluginsDir>$(QTDIR)plugins\</QtPluginsDir>
<QtTranslationsDir>$(QTDIR)translations\</QtTranslationsDir>
<QtToolOutDir>$(SolutionDir)build\$(ProjectName)-$(Platform)-$(Configuration)\</QtToolOutDir>
<DSQTDIRDefault Condition="'$(Platform)'=='x64'">$(SolutionDir)dep\msvc\qt\6.1.0\msvc2019_64\</DSQTDIRDefault>
<DSQTDIRDefault Condition="'$(Platform)'=='ARM64'">$(SolutionDir)dep\msvc\qt\6.1.0\msvc2019_arm64\</DSQTDIRDefault>
<DSQTDIR Condition="Exists('$(DSQTDIRDefault)') And ('$(DSQTDIR)'=='' Or !Exists('$(DSQTDIR)'))">$(DSQTDIRDefault)</DSQTDIR>
<DSQTDIR Condition="Exists('$(DSQTDIR)') And !HasTrailingSlash('$(DSQTDIR)')">$(DSQTDIR)\</DSQTDIR>
<DSQTDIRHost>$(SolutionDir)dep\msvc\qt\6.1.0\msvc2019_64\</DSQTDIRHost>
<DSQTDIRValid>false</DSQTDIRValid>
<DSQTDIRValid Condition="Exists('$(DSQTDIR)')">true</DSQTDIRValid>
<QtIncludeDir>$(DSQTDIR)include\</QtIncludeDir>
<QtLibDir>$(DSQTDIR)lib\</QtLibDir>
<QtBinDir>$(DSQTDIR)bin\</QtBinDir>
<QtHostBinDir>$(DSQTDIRHost)bin\</QtHostBinDir>
<QtPluginsDir>$(DSQTDIR)plugins\</QtPluginsDir>
<QtTranslationsDir>$(DSQTDIR)translations\</QtTranslationsDir>
<QtToolOutDir>$(IntDir)</QtToolOutDir>
<QtMocOutPrefix>$(QtToolOutDir)moc_</QtMocOutPrefix>
<QtTsOutDir>$(BinaryOutputDir)translations\</QtTsOutDir>
<QtDebugSuffix>d</QtDebugSuffix>
<QtLibSuffix Condition="'$(Configuration)'=='Debug' Or '$(Configuration)'=='DebugFast'">$(QtDebugSuffix)</QtLibSuffix>
<QtLibSuffix Condition="$(Configuration.Contains(Debug))">$(QtDebugSuffix)</QtLibSuffix>
<QtPluginFolder>QtPlugins</QtPluginFolder>
<QtEntryPointLib>$(QtLibDir)Qt6EntryPoint$(QtLibSuffix).lib</QtEntryPointLib>
</PropertyGroup>
<ItemDefinitionGroup>
<ClCompile>
<PreprocessorDefinitions Condition="'$(Configuration)'=='Release' Or '$(Configuration)'=='ReleaseLTCG'">QT_NO_DEBUG;%(PreprocessorDefinitions)</PreprocessorDefinitions>
<PreprocessorDefinitions Condition="!$(Configuration.Contains(Debug))">QT_NO_DEBUG;%(PreprocessorDefinitions)</PreprocessorDefinitions>
<AdditionalIncludeDirectories>$(ProjectDir);%(AdditionalIncludeDirectories)</AdditionalIncludeDirectories>
<AdditionalIncludeDirectories>$(QtToolOutDir);%(AdditionalIncludeDirectories)</AdditionalIncludeDirectories>
<AdditionalIncludeDirectories>$(QtIncludeDir);%(AdditionalIncludeDirectories)</AdditionalIncludeDirectories>
@ -45,7 +46,7 @@
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?)" />
<Error Condition="!$(DSQTDIRValid)" Text="Qt directory non-existent (pull the submodule?)" />
<MakeDir Directories="$(QtToolOutDir)" />
<Exec Command="&quot;$(QtHostBinDir)rcc.exe&quot; &quot;%(ResFiles.FullPath)&quot; -o &quot;$(QtToolOutDir)qrc_%(ResFiles.Filename).cpp&quot;" />
</Target>
@ -64,7 +65,7 @@
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?)" />
<Error Condition="!$(DSQTDIRValid)" Text="Qt directory non-existent (pull the submodule?)" />
<MakeDir Directories="$(QtToolOutDir)" />
<Exec Command="&quot;$(QtHostBinDir)uic.exe&quot; &quot;%(UiFiles.FullPath)&quot; -o &quot;$(QtToolOutDir)ui_%(UiFiles.Filename).h&quot;" />
</Target>
@ -77,26 +78,18 @@
<!--TODO find a way to autocreate from ClCompile settings-->
<PropertyGroup>
<MocDefines></MocDefines>
<MocDefines Condition="'$(Configuration)'=='Release' Or '$(Configuration)'=='ReleaseLTCG'">-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>
<MocDefines Condition="!$(Configuration.Contains(Debug))">-DQT_NO_DEBUG -DNDEBUG $(MocDefines)</MocDefines>
<MocIncludes>-I"$(QtIncludeDir)." -I"$(SolutionDir)pcsx2" "-I$(SolutionDir)." -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?)" />
Outputs="$(QtToolOutDir)%(QtMoc.RelativeDir)moc_%(QtMoc.Filename).cpp">
<Message Text="moc %(QtMoc.Filename) $(QtToolOutDir)%(QtMoc.RelativeDir)moc_%(QtMoc.Filename).cpp" Importance="High" />
<Error Condition="!$(DSQTDIRValid)" Text="Qt directory non-existent (pull the submodule?)" />
<MakeDir Directories="$(QtToolOutDir)" />
<Exec Command="&quot;$(QtHostBinDir)moc.exe&quot; &quot;%(QtMoc.FullPath)&quot; -o &quot;$(QtToolOutDir)moc_%(QtMoc.Filename).cpp&quot; -f%(QtMoc.Filename)%(QtMoc.Extension) $(MocDefines) $(MocIncludes)" />
<Exec Command="&quot;$(QtHostBinDir)moc.exe&quot; &quot;%(QtMoc.FullPath)&quot; -o &quot;$(QtToolOutDir)%(QtMoc.RelativeDir)moc_%(QtMoc.Filename).cpp&quot; -f%(QtMoc.Filename)%(QtMoc.Extension) $(MocDefines) $(MocIncludes)" />
</Target>
<ItemGroup>
@ -129,17 +122,20 @@
<QtDlls Include="@(QtLibNames -> '$(QtBinDir)%(Identity).dll')" />
<!--Filter plugins to copy based on the observation that all debug versions end in "d"-->
<QtAllPlugins Include="$(QtPluginsDir)**\*$(QtLibSuffix).dll" />
<QtPlugins Condition="'$(Configuration)'=='Debug' Or '$(Configuration)'=='DebugFast'" Include="@(QtAllPlugins)" />
<QtPlugins Condition="'$(Configuration)'=='Release' Or '$(Configuration)'=='ReleaseLTCG'" Exclude="$(QtPluginsDir)**\*$(QtDebugSuffix).dll" Include="@(QtAllPlugins)" />
<QtPlugins Condition="$(Configuration.Contains(Debug))" Include="@(QtAllPlugins)" />
<QtPlugins Condition="!$(Configuration.Contains(Debug))" Exclude="$(QtPluginsDir)**\*$(QtDebugSuffix).dll" Include="@(QtAllPlugins)" />
<QtPluginsDest Include="@(QtPlugins -> '$(BinaryOutputDir)$(QtPluginFolder)\%(RecursiveDir)%(Filename)%(Extension)')" />
<!--Our normal *d filter fails for the TLS DLLs, because backend ends in d. -->
<!--<QtTLSDlls Include="$(QtPluginsDir)tls\qcertonlybackend$(QtLibSuffix).dll;$(QtPluginsDir)tls\qschannelbackend$(QtLibSuffix).dll" />-->
<QtTLSDllsDest Include="@(QtTLSDlls -> '$(BinaryOutputDir)$(QtPluginFolder)\tls\%(Filename)%(Extension)')" />
</ItemGroup>
<PropertyGroup>
<QtConfFile>$(BinaryOutputDir)qt.conf</QtConfFile>
</PropertyGroup>
<Target Name="QtCopyBinaries"
AfterTargets="Build"
Inputs="@(QtDlls);@(QtPlugins)"
Outputs="@(QtDlls -> '$(BinaryOutputDir)%(RecursiveDir)%(Filename)%(Extension)');@(QtPlugins -> '$(BinaryOutputDir)$(QtPluginFolder)\%(RecursiveDir)%(Filename)%(Extension)')">
Inputs="@(QtDlls);@(QtPlugins);@(QtTLSDlls)"
Outputs="@(QtDlls -> '$(BinaryOutputDir)%(RecursiveDir)%(Filename)%(Extension)');@(QtPluginsDest);@(QtTLSDllsDest)">
<Message Text="Copying Qt .dlls" Importance="High" />
<Copy
SourceFiles="@(QtDlls)"
@ -151,6 +147,11 @@
DestinationFiles="@(QtPluginsDest)"
SkipUnchangedFiles="true"
/>
<Copy
SourceFiles="@(QtTLSDlls)"
DestinationFiles="@(QtTLSDllsDest)"
SkipUnchangedFiles="true"
/>
</Target>
<Target Name="QtCreateConf"
BeforeTargets="QtCopyBinaries"
@ -192,7 +193,7 @@
Condition="'@(QtTs)'!=''"
Outputs="@(TsFiles->'$(QtTsOutDir)%(Filename).qm')">
<Message Text="lrelease %(TsFiles.Filename)" Importance="High" />
<Error Condition="!$(QtDirValid)" Text="QTDIR not set or non-existent (pull the submodule?)" />
<Error Condition="!$(DSQTDIRValid)" Text="Qt directory non-existent (pull the submodule?)" />
<MakeDir Directories="$(QtTsOutDir)" />
<Exec Command="&quot;$(QtHostBinDir)lrelease.exe&quot; &quot;%(TsFiles.FullPath)&quot; -qm &quot;$(QtTsOutDir)%(TsFiles.Filename).qm&quot;" />
</Target>

View File

@ -827,7 +827,6 @@ Global
{0A172B2E-DC67-49FC-A4C1-975F93C586C4}.Release|ARM64.ActiveCfg = Release|ARM64
{0A172B2E-DC67-49FC-A4C1-975F93C586C4}.Release|ARM64.Build.0 = Release|ARM64
{0A172B2E-DC67-49FC-A4C1-975F93C586C4}.Release|x64.ActiveCfg = Release|x64
{0A172B2E-DC67-49FC-A4C1-975F93C586C4}.Release|x64.Build.0 = Release|x64
{0A172B2E-DC67-49FC-A4C1-975F93C586C4}.Release|x86.ActiveCfg = Release|Win32
{0A172B2E-DC67-49FC-A4C1-975F93C586C4}.Release|x86.Build.0 = Release|Win32
{0A172B2E-DC67-49FC-A4C1-975F93C586C4}.ReleaseLTCG|ARM64.ActiveCfg = ReleaseLTCG|ARM64

View File

@ -29,6 +29,7 @@ public:
bool HasMedia() const { return m_reader.HasMedia(); }
const std::string& GetMediaFileName() const { return m_reader.GetMediaFileName(); }
const CDImage* GetMedia() const { return m_reader.GetMedia(); }
DiscRegion GetDiscRegion() const { return m_disc_region; }
bool IsMediaPS1Disc() const;
bool DoesMediaRegionMatchConsole() const;

View File

@ -3,6 +3,7 @@
#include "common/string.h"
#include "common/types.h"
#include <functional>
#include <memory>
#include <optional>
#include <string>
@ -104,4 +105,7 @@ void SetPadVibrationIntensity(u32 pad_index, float large_or_single_motor_intensi
/// Enables "relative" mouse mode, locking the cursor position and returning relative coordinates.
void SetMouseMode(bool relative, bool hide_cursor);
/// Safely executes a function on the VM thread.
void RunOnCPUThread(std::function<void()> function, bool block = false);
} // namespace Host

View File

@ -165,6 +165,7 @@ void Settings::Load(SettingsInterface& si)
pause_on_focus_loss = si.GetBoolValue("Main", "PauseOnFocusLoss", false);
pause_on_menu = si.GetBoolValue("Main", "PauseOnMenu", true);
save_state_on_exit = si.GetBoolValue("Main", "SaveStateOnExit", true);
create_save_state_backups = si.GetBoolValue("Main", "CreateSaveStateBackups", true);
confim_power_off = si.GetBoolValue("Main", "ConfirmPowerOff", true);
load_devices_from_save_states = si.GetBoolValue("Main", "LoadDevicesFromSaveStates", false);
apply_game_settings = si.GetBoolValue("Main", "ApplyGameSettings", true);
@ -363,6 +364,7 @@ void Settings::Save(SettingsInterface& si) const
si.SetBoolValue("Main", "PauseOnFocusLoss", pause_on_focus_loss);
si.SetBoolValue("Main", "PauseOnMenu", pause_on_menu);
si.SetBoolValue("Main", "SaveStateOnExit", save_state_on_exit);
si.SetBoolValue("Main", "CreateSaveStateBackups", create_save_state_backups);
si.SetBoolValue("Main", "ConfirmPowerOff", confim_power_off);
si.SetBoolValue("Main", "LoadDevicesFromSaveStates", load_devices_from_save_states);
si.SetBoolValue("Main", "ApplyGameSettings", apply_game_settings);
@ -1058,12 +1060,17 @@ const char* Settings::GetMemoryCardTypeDisplayName(MemoryCardType type)
return s_memory_card_type_display_names[static_cast<int>(type)];
}
std::string Settings::GetDefaultSharedMemoryCardName(u32 slot)
{
return fmt::format("shared_card_{}.mcd", slot + 1);
}
std::string Settings::GetSharedMemoryCardPath(u32 slot) const
{
std::string ret;
if (memory_card_paths[slot].empty())
ret = Path::Combine(EmuFolders::MemoryCards, fmt::format("shared_card_{}.mcd", slot + 1));
ret = Path::Combine(EmuFolders::MemoryCards, GetDefaultSharedMemoryCardName(slot));
else if (!Path::IsAbsolute(memory_card_paths[slot]))
ret = Path::Combine(EmuFolders::MemoryCards, memory_card_paths[slot]);
else

View File

@ -67,6 +67,7 @@ struct Settings
bool pause_on_focus_loss = false;
bool pause_on_menu = true;
bool save_state_on_exit = true;
bool create_save_state_backups = false;
bool confim_power_off = true;
bool load_devices_from_save_states = false;
bool apply_game_settings = true;
@ -249,6 +250,7 @@ struct Settings
bool HasAnyPerGameMemoryCards() const;
/// Returns the default path to a memory card.
static std::string GetDefaultSharedMemoryCardName(u32 slot);
std::string GetSharedMemoryCardPath(u32 slot) const;
/// Returns the default path to a memory card for a specific game.

View File

@ -76,7 +76,6 @@ struct MemorySaveState
};
namespace System {
static bool InternalLoadState(ByteStream* state, bool update_display = true);
static bool InternalSaveState(ByteStream* state, u32 screenshot_size = 256);
static bool SaveMemoryState(MemorySaveState* mss);
static bool LoadMemoryState(const MemorySaveState& mss);
@ -97,6 +96,7 @@ static void StallCPU(TickCount ticks);
static bool InternalBoot(const SystemBootParameters& params);
static void InternalReset();
static void InternalShutdown();
static void DestroySystem();
static bool DoLoadState(ByteStream* stream, bool force_software_renderer, bool update_display);
static bool DoState(StateWrapper& sw, HostDisplayTexture** host_texture, bool update_display, bool is_memory_state);
static void DoRunFrame();
@ -245,6 +245,11 @@ ConsoleRegion System::GetRegion()
return s_region;
}
DiscRegion System::GetDiscRegion()
{
return g_cdrom.GetDiscRegion();
}
bool System::IsPALRegion()
{
return s_region == ConsoleRegion::PAL;
@ -825,7 +830,7 @@ void System::ApplySettings(bool display_osd_messages)
bool System::ReloadGameSettings(bool display_osd_messages)
{
if (!UpdateGameSettingsLayer())
if (!IsValid() || !UpdateGameSettingsLayer())
return false;
ApplySettings(display_osd_messages);
@ -971,6 +976,9 @@ bool System::BootSystem(std::shared_ptr<SystemBootParameters> parameters)
void System::ResetSystem()
{
if (!IsValid())
return;
InternalReset();
ResetPerformanceCounters();
ResetThrottler();
@ -1030,7 +1038,7 @@ bool System::LoadState(const char* filename)
SaveUndoLoadState();
if (!InternalLoadState(stream.get()))
if (!DoLoadState(stream.get(), false, true))
{
Host::ReportFormattedErrorAsync(
"Load State Error", Host::TranslateString("OSDMessage", "Loading state from '%s' failed. Resetting."), filename);
@ -1047,8 +1055,15 @@ bool System::LoadState(const char* filename)
return true;
}
bool System::SaveState(const char* filename)
bool System::SaveState(const char* filename, bool backup_existing_save)
{
if (backup_existing_save && FileSystem::FileExists(filename))
{
const std::string backup_filename(Path::ReplaceExtension(filename, "bak"));
if (!FileSystem::RenamePath(filename, backup_filename.c_str()))
Log_ErrorPrintf("Failed to rename save state backup '%s'", backup_filename.c_str());
}
std::unique_ptr<ByteStream> stream =
ByteStream::OpenFile(filename, BYTESTREAM_OPEN_CREATE | BYTESTREAM_OPEN_WRITE | BYTESTREAM_OPEN_TRUNCATE |
BYTESTREAM_OPEN_ATOMIC_UPDATE | BYTESTREAM_OPEN_STREAMED);
@ -1137,19 +1152,6 @@ bool System::InternalBoot(const SystemBootParameters& params)
Log_InfoPrintf("Console Region: %s", Settings::GetConsoleRegionDisplayName(s_region));
// Load BIOS image.
std::optional<BIOS::Image> bios_image(BIOS::GetBIOSImage(s_region));
if (!bios_image)
{
Host::ReportFormattedErrorAsync("Error", Host::TranslateString("System", "Failed to load %s BIOS."),
Settings::GetConsoleRegionName(s_region));
InternalShutdown();
return false;
}
// Notify change of disc.
UpdateRunningGame(media ? media->GetFileName().c_str() : params.filename.c_str(), media.get(), true);
// Check for SBI.
if (!CheckForSBIFile(media.get()))
{
@ -1166,6 +1168,19 @@ bool System::InternalBoot(const SystemBootParameters& params)
return false;
}
// Notify change of disc.
UpdateRunningGame(media ? media->GetFileName().c_str() : params.filename.c_str(), media.get(), true);
// Load BIOS image.
std::optional<BIOS::Image> bios_image(BIOS::GetBIOSImage(s_region));
if (!bios_image)
{
Host::ReportFormattedErrorAsync("Error", Host::TranslateString("System", "Failed to load %s BIOS."),
Settings::GetConsoleRegionName(s_region));
InternalShutdown();
return false;
}
// Component setup.
if (!Initialize(params.force_software_renderer))
{
@ -1618,14 +1633,6 @@ void System::InternalReset()
g_gpu->ResetGraphicsAPIState();
}
bool System::InternalLoadState(ByteStream* state, bool update_display)
{
if (IsShutdown())
return false;
return DoLoadState(state, false, update_display);
}
bool System::DoLoadState(ByteStream* state, bool force_software_renderer, bool update_display)
{
SAVE_STATE_HEADER header;
@ -1762,6 +1769,8 @@ bool System::DoLoadState(ByteStream* state, bool force_software_renderer, bool u
s_state = State::Running;
g_spu.GetOutputStream()->EmptyBuffers();
ResetPerformanceCounters();
ResetThrottler();
return true;
}
@ -3412,22 +3421,16 @@ void System::SetRunaheadReplayFlag()
s_runahead_replay_pending = true;
}
bool System::SaveResumeSaveState()
{
if (System::IsShutdown())
return false;
const bool global = System::GetRunningCode().empty();
return SaveStateToSlot(global, -1);
}
void System::PowerOffSystem(bool save_resume_state)
void System::ShutdownSystem(bool save_resume_state)
{
if (!IsValid())
return;
if (save_resume_state)
SaveResumeSaveState();
if (save_resume_state && !s_running_game_code.empty())
{
std::string path(GetGameSaveStateFileName(s_running_game_code, -1));
SaveState(path.c_str(), false);
}
DestroySystem();
}
@ -3465,7 +3468,7 @@ bool System::UndoLoadState()
Assert(IsValid());
m_undo_load_state->SeekAbsolute(0);
if (!InternalLoadState(m_undo_load_state.get()))
if (!DoLoadState(m_undo_load_state.get(), false, true))
{
Host::ReportErrorAsync("Error", "Failed to load undo state, resetting system.");
m_undo_load_state.reset();
@ -3473,9 +3476,6 @@ bool System::UndoLoadState()
return false;
}
ResetPerformanceCounters();
ResetThrottler();
Log_InfoPrintf("Loaded undo save state.");
m_undo_load_state.reset();
return true;
@ -3498,33 +3498,6 @@ bool System::SaveUndoLoadState()
return true;
}
bool System::LoadStateFromSlot(bool global, s32 slot)
{
if (!global && (System::IsShutdown() || System::GetRunningCode().empty()))
{
Host::ReportErrorAsync("Error", "Can't save per-game state without a running game code.");
return false;
}
std::string save_path =
global ? GetGlobalSaveStateFileName(slot) : GetGameSaveStateFileName(System::GetRunningCode().c_str(), slot);
return LoadState(save_path.c_str());
}
bool System::SaveStateToSlot(bool global, s32 slot)
{
const std::string& code = System::GetRunningCode();
if (!global && code.empty())
{
Host::ReportErrorAsync("Error", "Can't save per-game state without a running game code.");
return false;
}
std::string save_path = global ? GetGlobalSaveStateFileName(slot) : GetGameSaveStateFileName(code.c_str(), slot);
RenameCurrentSaveStateToBackup(save_path.c_str());
return SaveState(save_path.c_str());
}
bool System::ResumeSystemFromMostRecentState()
{
const std::string path = GetMostRecentResumeSaveStatePath();
@ -3537,11 +3510,6 @@ bool System::ResumeSystemFromMostRecentState()
return LoadState(path.c_str());
}
bool System::ShouldSaveResumeState()
{
return g_settings.save_state_on_exit;
}
bool System::IsRunningAtNonStandardSpeed()
{
if (!IsValid())
@ -3662,7 +3630,7 @@ bool System::SaveScreenshot(const char* filename /* = nullptr */, bool full_reso
return true;
}
std::string System::GetGameSaveStateFileName(const char* game_code, s32 slot)
std::string System::GetGameSaveStateFileName(const std::string_view& game_code, s32 slot)
{
if (slot < 0)
return Path::Combine(EmuFolders::SaveStates, fmt::format("{}_resume.sav", game_code));
@ -3678,24 +3646,6 @@ std::string System::GetGlobalSaveStateFileName(s32 slot)
return Path::Combine(EmuFolders::SaveStates, fmt::format("savestate_{}.sav", slot));
}
void System::RenameCurrentSaveStateToBackup(const char* filename)
{
if (!Host::GetBaseBoolSettingValue("General", "CreateSaveStateBackups", false))
return;
if (!FileSystem::FileExists(filename))
return;
const std::string backup_filename(Path::ReplaceExtension(filename, "bak"));
if (!FileSystem::RenamePath(filename, backup_filename.c_str()))
{
Log_ErrorPrintf("Failed to rename save state backup '%s'", backup_filename.c_str());
return;
}
Log_InfoPrintf("Renamed save state '%s' to '%s'", filename, backup_filename.c_str());
}
std::vector<SaveStateInfo> System::GetAvailableSaveStates(const char* game_code)
{
std::vector<SaveStateInfo> si;

View File

@ -121,6 +121,7 @@ bool IsStartupCancelled();
void CancelPendingStartup();
ConsoleRegion GetRegion();
DiscRegion GetDiscRegion();
bool IsPALRegion();
ALWAYS_INLINE TickCount GetTicksPerSecond()
@ -188,17 +189,10 @@ bool ReloadGameSettings(bool display_osd_messages);
bool BootSystem(std::shared_ptr<SystemBootParameters> parameters);
void PauseSystem(bool paused);
void ResetSystem();
void DestroySystem();
/// Loads state from the specified filename.
bool LoadState(const char* filename);
bool SaveState(const char* filename);
/// Loads the current emulation state from file. Specifying a slot of -1 loads the "resume" game state.
bool LoadStateFromSlot(bool global, s32 slot);
/// Saves the current emulation state to a file. Specifying a slot of -1 saves the "resume" save state.
bool SaveStateToSlot(bool global, s32 slot);
bool SaveState(const char* filename, bool backup_existing_save);
/// Runs the VM until the CPU execution is canceled.
void Execute();
@ -309,7 +303,7 @@ void DoFrameStep();
void DoToggleCheats();
/// Returns the path to a save state file. Specifying an index of -1 is the "resume" save state.
std::string GetGameSaveStateFileName(const char* game_code, s32 slot);
std::string GetGameSaveStateFileName(const std::string_view& game_code, s32 slot);
/// Returns the path to a save state file. Specifying an index of -1 is the "resume" save state.
std::string GetGlobalSaveStateFileName(s32 slot);
@ -324,7 +318,7 @@ std::string GetMostRecentResumeSaveStatePath();
std::string GetCheatFileName();
/// Powers off the system, optionally saving the resume state.
void PowerOffSystem(bool save_resume_state);
void ShutdownSystem(bool save_resume_state);
/// Returns true if an undo load state exists.
bool CanUndoLoadState();
@ -338,9 +332,6 @@ bool UndoLoadState();
/// Loads the most recent resume save state. This may be global or per-game.
bool ResumeSystemFromMostRecentState();
/// Saves the resume save state, call when shutting down.
bool SaveResumeSaveState();
/// Returns a list of save states for the specified game code.
std::vector<SaveStateInfo> GetAvailableSaveStates(const char* game_code);
@ -409,9 +400,6 @@ void ReloadPostProcessingShaders();
/// Toggle Widescreen Hack and Aspect Ratio
void ToggleWidescreen();
/// Returns true if the state should be saved on shutdown.
bool ShouldSaveResumeState();
/// Returns true if fast forwarding or slow motion is currently active.
bool IsRunningAtNonStandardSpeed();
@ -489,9 +477,6 @@ void PumpMessagesOnCPUThread();
/// Requests a specific display window size.
void RequestResizeHostDisplay(s32 width, s32 height);
/// Safely executes a function on the VM thread.
// void RunOnCPUThread(std::function<void()> function, bool block = false);
/// Asynchronously starts refreshing the game list.
// void RefreshGameListAsync(bool invalidate_cache);

View File

@ -24,7 +24,7 @@ void AchievementLoginDialog::loginClicked()
m_ui.status->setText(tr("Logging in..."));
enableUI(false);
g_emu_thread->executeOnEmulationThread([this, username, password]() {
Host::RunOnCPUThread([this, username, password]() {
const bool result = Cheevos::Login(username.toStdString().c_str(), password.toStdString().c_str());
QMetaObject::invokeMethod(this, "processLoginResult", Qt::QueuedConnection, Q_ARG(bool, result));
});

View File

@ -50,7 +50,7 @@ AchievementSettingsWidget::AchievementSettingsWidget(SettingsDialog* dialog, QWi
connect(m_ui.loginButton, &QPushButton::clicked, this, &AchievementSettingsWidget::onLoginLogoutPressed);
connect(m_ui.viewProfile, &QPushButton::clicked, this, &AchievementSettingsWidget::onViewProfilePressed);
connect(m_ui.challengeMode, &QCheckBox::toggled, this, &AchievementSettingsWidget::onChallengeModeToggled);
connect(g_emu_thread, &QtHostInterface::achievementsLoaded, this, &AchievementSettingsWidget::onAchievementsLoaded);
connect(g_emu_thread, &EmuThread::achievementsLoaded, this, &AchievementSettingsWidget::onAchievementsLoaded);
updateEnableState();
updateLoginState();
@ -98,7 +98,7 @@ void AchievementSettingsWidget::onLoginLogoutPressed()
{
if (!Host::GetBaseStringSettingValue("Cheevos", "Username").empty())
{
g_emu_thread->executeOnEmulationThread([]() { Cheevos::Logout(); }, true);
Host::RunOnCPUThread([]() { Cheevos::Logout(); }, true);
updateLoginState();
return;
}

View File

@ -3,6 +3,7 @@
#include "common/log.h"
#include "common/minizip_helpers.h"
#include "common/string_util.h"
#include "mainwindow.h"
#include "qthost.h"
#include "qtutils.h"
#include "scmversion/scmversion.h"
@ -45,7 +46,7 @@ static const char* THIS_RELEASE_TAG = SCM_RELEASE_TAG;
#endif
AutoUpdaterDialog::AutoUpdaterDialog(QtHostInterface* host_interface, QWidget* parent /* = nullptr */)
AutoUpdaterDialog::AutoUpdaterDialog(EmuThread* host_interface, QWidget* parent /* = nullptr */)
: QDialog(parent), m_host_interface(host_interface)
{
m_network_access_mgr = new QNetworkAccessManager(this);
@ -375,7 +376,7 @@ void AutoUpdaterDialog::downloadUpdateClicked()
progress.setValue(static_cast<int>(received));
});
connect(m_network_access_mgr, &QNetworkAccessManager::finished, [this, &progress](QNetworkReply* reply) {
connect(m_network_access_mgr, &QNetworkAccessManager::finished, this, [this, &progress](QNetworkReply* reply) {
m_network_access_mgr->disconnect();
if (reply->error() != QNetworkReply::NoError)
@ -408,7 +409,7 @@ void AutoUpdaterDialog::downloadUpdateClicked()
else if (result == 1)
{
// updater started
m_host_interface->requestExit();
g_main_window->requestExit();
done(0);
}
@ -417,8 +418,7 @@ void AutoUpdaterDialog::downloadUpdateClicked()
bool AutoUpdaterDialog::updateNeeded() const
{
QString last_checked_sha =
QString::fromStdString(Host::GetBaseStringSettingValue("AutoUpdater", "LastVersion"));
QString last_checked_sha = QString::fromStdString(Host::GetBaseStringSettingValue("AutoUpdater", "LastVersion"));
Log_InfoPrintf("Current SHA: %s", g_scm_hash_str);
Log_InfoPrintf("Latest SHA: %s", m_latest_sha.toUtf8().constData());

View File

@ -7,14 +7,14 @@
class QNetworkAccessManager;
class QNetworkReply;
class QtHostInterface;
class EmuThread;
class AutoUpdaterDialog final : public QDialog
{
Q_OBJECT
public:
explicit AutoUpdaterDialog(QtHostInterface* host_interface, QWidget* parent = nullptr);
explicit AutoUpdaterDialog(EmuThread* host_interface, QWidget* parent = nullptr);
~AutoUpdaterDialog();
static bool isSupported();
@ -54,7 +54,7 @@ private:
Ui::AutoUpdaterDialog m_ui;
QtHostInterface* m_host_interface;
EmuThread* m_host_interface;
QNetworkAccessManager* m_network_access_mgr = nullptr;
QString m_latest_sha;
QString m_download_url;

View File

@ -7,13 +7,90 @@
#include <QtWidgets/QFileDialog>
#include <algorithm>
static void populateDropDownForRegion(ConsoleRegion region, QComboBox* cb,
std::vector<std::pair<std::string, const BIOS::ImageInfo*>>& images)
BIOSSettingsWidget::BIOSSettingsWidget(SettingsDialog* dialog, QWidget* parent) : QWidget(parent), m_dialog(dialog)
{
SettingsInterface* sif = dialog->getSettingsInterface();
m_ui.setupUi(this);
SettingWidgetBinder::BindWidgetToBoolSetting(sif, m_ui.enableTTYOutput, "BIOS", "PatchTTYEnable", false);
SettingWidgetBinder::BindWidgetToBoolSetting(sif, m_ui.fastBoot, "BIOS", "PatchFastBoot", false);
dialog->registerWidgetHelp(m_ui.fastBoot, tr("Fast Boot"), tr("Unchecked"),
tr("Patches the BIOS to skip the console's boot animation. Does not work with all games, "
"but usually safe to enable."));
dialog->registerWidgetHelp(
m_ui.enableTTYOutput, tr("Enable TTY Output"), tr("Unchecked"),
tr("Patches the BIOS to log calls to printf(). Only use when debugging, can break games."));
connect(m_ui.imageNTSCJ, QOverload<int>::of(&QComboBox::currentIndexChanged), [this](int index) {
if (m_dialog->isPerGameSettings() && index == 0)
{
m_dialog->removeSettingValue("BIOS", "PathNTSCJ");
}
else
{
m_dialog->setStringSettingValue("BIOS", "PathNTSCJ",
m_ui.imageNTSCJ->itemData(index).toString().toStdString().c_str());
}
});
connect(m_ui.imageNTSCU, QOverload<int>::of(&QComboBox::currentIndexChanged), [this](int index) {
if (m_dialog->isPerGameSettings() && index == 0)
{
m_dialog->removeSettingValue("BIOS", "PathNTSCU");
}
else
{
m_dialog->setStringSettingValue("BIOS", "PathNTSCU",
m_ui.imageNTSCU->itemData(index).toString().toStdString().c_str());
}
});
connect(m_ui.imagePAL, QOverload<int>::of(&QComboBox::currentIndexChanged), [this](int index) {
if (m_dialog->isPerGameSettings() && index == 0)
{
m_dialog->removeSettingValue("BIOS", "PathPAL");
}
else
{
m_dialog->setStringSettingValue("BIOS", "PathPAL",
m_ui.imagePAL->itemData(index).toString().toStdString().c_str());
}
});
connect(m_ui.refresh, &QPushButton::clicked, this, &BIOSSettingsWidget::refreshList);
m_ui.searchDirectory->setText(QString::fromStdString(EmuFolders::Bios));
SettingWidgetBinder::BindWidgetToFolderSetting(sif, m_ui.searchDirectory, m_ui.browseSearchDirectory,
m_ui.openSearchDirectory, nullptr, "BIOS", "SearchDirectory",
Path::Combine(EmuFolders::DataRoot, "bios"));
connect(m_ui.searchDirectory, &QLineEdit::textChanged, this, &BIOSSettingsWidget::refreshList);
refreshList();
}
BIOSSettingsWidget::~BIOSSettingsWidget() = default;
void BIOSSettingsWidget::refreshList()
{
auto images = BIOS::FindBIOSImagesInDirectory(m_ui.searchDirectory->text().toUtf8().constData());
populateDropDownForRegion(ConsoleRegion::NTSC_J, m_ui.imageNTSCJ, images);
populateDropDownForRegion(ConsoleRegion::NTSC_U, m_ui.imageNTSCU, images);
populateDropDownForRegion(ConsoleRegion::PAL, m_ui.imagePAL, images);
setDropDownValue(m_ui.imageNTSCJ, m_dialog->getStringValue("BIOS", "PathNTSCJ", std::nullopt));
setDropDownValue(m_ui.imageNTSCU, m_dialog->getStringValue("BIOS", "PathNTSCU", std::nullopt));
setDropDownValue(m_ui.imagePAL, m_dialog->getStringValue("BIOS", "PathPAL", std::nullopt));
}
void BIOSSettingsWidget::populateDropDownForRegion(ConsoleRegion region, QComboBox* cb,
std::vector<std::pair<std::string, const BIOS::ImageInfo*>>& images)
{
QSignalBlocker sb(cb);
cb->clear();
cb->addItem(QIcon(QStringLiteral(":/icons/system-search.png")), qApp->translate("BIOSSettingsWidget", "Auto-Detect"));
if (m_dialog->isPerGameSettings())
cb->addItem(QIcon(QStringLiteral(":/icons/system-search.png")), tr("Use Global Setting"));
cb->addItem(QIcon(QStringLiteral(":/icons/system-search.png")), tr("Auto-Detect"));
std::sort(images.begin(), images.end(), [region](const auto& left, const auto& right) {
const bool left_region_match = (left.second && left.second->region == region);
@ -28,32 +105,8 @@ static void populateDropDownForRegion(ConsoleRegion region, QComboBox* cb,
for (const auto& [name, info] : images)
{
QIcon icon;
if (info)
{
switch (info->region)
{
case ConsoleRegion::NTSC_J:
icon = QIcon(QStringLiteral(":/icons/flag-jp.png"));
break;
case ConsoleRegion::PAL:
icon = QIcon(QStringLiteral(":/icons/flag-eu.png"));
break;
case ConsoleRegion::NTSC_U:
icon = QIcon(QStringLiteral(":/icons/flag-uc.png"));
break;
default:
icon = QIcon(QStringLiteral(":/icons/applications-other.png"));
break;
}
}
else
{
icon = QIcon(QStringLiteral(":/icons/applications-other.png"));
}
QString name_str(QString::fromStdString(name));
cb->addItem(icon,
cb->addItem(QtUtils::GetIconForRegion(info ? info->region : ConsoleRegion::Count),
QStringLiteral("%1 (%2)")
.arg(info ? QString(info->description) : qApp->translate("BIOSSettingsWidget", "Unknown"))
.arg(name_str),
@ -61,17 +114,17 @@ static void populateDropDownForRegion(ConsoleRegion region, QComboBox* cb,
}
}
static void setDropDownValue(QComboBox* cb, const std::string& name)
void BIOSSettingsWidget::setDropDownValue(QComboBox* cb, const std::optional<std::string>& name)
{
QSignalBlocker sb(cb);
if (name.empty())
if (!name.has_value() || name->empty())
{
cb->setCurrentIndex(0);
cb->setCurrentIndex((m_dialog->isPerGameSettings() && name.has_value()) ? 1 : 0);
return;
}
QString qname(QString::fromStdString(name));
QString qname(QString::fromStdString(name.value()));
for (int i = 1; i < cb->count(); i++)
{
if (cb->itemData(i) == qname)
@ -84,55 +137,3 @@ static void setDropDownValue(QComboBox* cb, const std::string& name)
cb->addItem(qname, QVariant(qname));
cb->setCurrentIndex(cb->count() - 1);
}
BIOSSettingsWidget::BIOSSettingsWidget(SettingsDialog* dialog, QWidget* parent)
: QWidget(parent), m_dialog(dialog)
{
SettingsInterface* sif = dialog->getSettingsInterface();
m_ui.setupUi(this);
SettingWidgetBinder::BindWidgetToBoolSetting(sif, m_ui.enableTTYOutput, "BIOS", "PatchTTYEnable", false);
SettingWidgetBinder::BindWidgetToBoolSetting(sif, m_ui.fastBoot, "BIOS", "PatchFastBoot", false);
dialog->registerWidgetHelp(m_ui.fastBoot, tr("Fast Boot"), tr("Unchecked"),
tr("Patches the BIOS to skip the console's boot animation. Does not work with all games, "
"but usually safe to enable."));
dialog->registerWidgetHelp(m_ui.enableTTYOutput, tr("Enable TTY Output"), tr("Unchecked"),
tr("Patches the BIOS to log calls to printf(). Only use when debugging, can break games."));
refreshList();
connect(m_ui.imageNTSCJ, QOverload<int>::of(&QComboBox::currentIndexChanged), [this](int index) {
m_dialog->setStringSettingValue("BIOS", "PathNTSCJ",
m_ui.imageNTSCJ->itemData(index).toString().toStdString().c_str());
});
connect(m_ui.imageNTSCU, QOverload<int>::of(&QComboBox::currentIndexChanged), [this](int index) {
m_dialog->setStringSettingValue("BIOS", "PathNTSCU",
m_ui.imageNTSCU->itemData(index).toString().toStdString().c_str());
});
connect(m_ui.imagePAL, QOverload<int>::of(&QComboBox::currentIndexChanged), [this](int index) {
m_dialog->setStringSettingValue("BIOS", "PathPAL",
m_ui.imagePAL->itemData(index).toString().toStdString().c_str());
});
connect(m_ui.refresh, &QPushButton::clicked, this, &BIOSSettingsWidget::refreshList);
m_ui.searchDirectory->setText(QString::fromStdString(EmuFolders::Bios));
SettingWidgetBinder::BindWidgetToFolderSetting(sif, m_ui.searchDirectory, m_ui.browseSearchDirectory, m_ui.openSearchDirectory, nullptr, "BIOS", "SearchDirectory", Path::Combine(EmuFolders::DataRoot, "bios"));
connect(m_ui.searchDirectory, &QLineEdit::textChanged, this, &BIOSSettingsWidget::refreshList);
}
BIOSSettingsWidget::~BIOSSettingsWidget() = default;
void BIOSSettingsWidget::refreshList()
{
auto images = BIOS::FindBIOSImagesInDirectory(m_ui.searchDirectory->text().toUtf8().constData());
populateDropDownForRegion(ConsoleRegion::NTSC_J, m_ui.imageNTSCJ, images);
populateDropDownForRegion(ConsoleRegion::NTSC_U, m_ui.imageNTSCU, images);
populateDropDownForRegion(ConsoleRegion::PAL, m_ui.imagePAL, images);
setDropDownValue(m_ui.imageNTSCJ, m_dialog->getEffectiveStringValue("BIOS", "PathNTSCJ", ""));
setDropDownValue(m_ui.imageNTSCU, m_dialog->getEffectiveStringValue("BIOS", "PathNTSCU", ""));
setDropDownValue(m_ui.imagePAL, m_dialog->getEffectiveStringValue("BIOS", "PathPAL", ""));
}

View File

@ -6,6 +6,11 @@
class SettingsDialog;
enum class ConsoleRegion;
namespace BIOS {
struct ImageInfo;
}
class BIOSSettingsWidget : public QWidget
{
Q_OBJECT
@ -18,6 +23,10 @@ private Q_SLOTS:
void refreshList();
private:
void populateDropDownForRegion(ConsoleRegion region, QComboBox* cb,
std::vector<std::pair<std::string, const BIOS::ImageInfo*>>& images);
void setDropDownValue(QComboBox* cb, const std::optional<std::string>& name);
Ui::BIOSSettingsWidget m_ui;
SettingsDialog* m_dialog;

View File

@ -188,7 +188,7 @@ void CheatManagerDialog::connectUi()
connect(m_ui.scanTable, &QTableWidget::itemChanged, this, &CheatManagerDialog::scanItemChanged);
connect(m_ui.watchTable, &QTableWidget::itemChanged, this, &CheatManagerDialog::watchItemChanged);
connect(g_emu_thread, &QtHostInterface::cheatEnabled, this,
connect(g_emu_thread, &EmuThread::cheatEnabled, this,
&CheatManagerDialog::setCheatCheckState);
}
@ -351,7 +351,7 @@ CheatList* CheatManagerDialog::getCheatList() const
}
if (!list)
{
g_emu_thread->executeOnEmulationThread(
Host::RunOnCPUThread(
[]() { System::SetCheatList(std::make_unique<CheatList>()); }, true);
list = System::GetCheatList();
}
@ -417,7 +417,7 @@ void CheatManagerDialog::fillItemForCheatCode(QTreeWidgetItem* item, u32 index,
void CheatManagerDialog::saveCheatList()
{
g_emu_thread->executeOnEmulationThread([]() { System::SaveCheatList(); });
Host::RunOnCPUThread([]() { System::SaveCheatList(); });
}
void CheatManagerDialog::cheatListCurrentItemChanged(QTreeWidgetItem* current, QTreeWidgetItem* previous)
@ -479,7 +479,7 @@ void CheatManagerDialog::cheatListItemChanged(QTreeWidgetItem* item, int column)
if (cc.enabled == new_enabled)
return;
g_emu_thread->executeOnEmulationThread([index, new_enabled]() {
Host::RunOnCPUThread([index, new_enabled]() {
System::GetCheatList()->SetCodeEnabled(static_cast<u32>(index), new_enabled);
System::SaveCheatList();
});
@ -501,7 +501,7 @@ void CheatManagerDialog::activateCheat(u32 index)
const bool new_enabled = !cc.enabled;
setCheatCheckState(index, new_enabled);
g_emu_thread->executeOnEmulationThread([index, new_enabled]() {
Host::RunOnCPUThread([index, new_enabled]() {
System::GetCheatList()->SetCodeEnabled(index, new_enabled);
System::SaveCheatList();
});
@ -551,7 +551,7 @@ void CheatManagerDialog::addCodeClicked()
fillItemForCheatCode(item, list->GetCodeCount(), new_code);
group_item->setExpanded(true);
g_emu_thread->executeOnEmulationThread(
Host::RunOnCPUThread(
[this, &new_code]() {
System::GetCheatList()->AddCode(std::move(new_code));
System::SaveCheatList();
@ -597,7 +597,7 @@ void CheatManagerDialog::editCodeClicked()
updateCheatList();
}
g_emu_thread->executeOnEmulationThread(
Host::RunOnCPUThread(
[index, &new_code]() {
System::GetCheatList()->SetCode(static_cast<u32>(index), std::move(new_code));
System::SaveCheatList();
@ -623,7 +623,7 @@ void CheatManagerDialog::deleteCodeClicked()
return;
}
g_emu_thread->executeOnEmulationThread(
Host::RunOnCPUThread(
[index]() {
System::GetCheatList()->RemoveCode(static_cast<u32>(index));
System::SaveCheatList();
@ -663,7 +663,7 @@ void CheatManagerDialog::importFromFileTriggered()
return;
}
g_emu_thread->executeOnEmulationThread(
Host::RunOnCPUThread(
[&new_cheats]() {
DebugAssert(System::HasCheatList());
System::GetCheatList()->MergeList(new_cheats);
@ -686,7 +686,7 @@ void CheatManagerDialog::importFromTextTriggered()
return;
}
g_emu_thread->executeOnEmulationThread(
Host::RunOnCPUThread(
[&new_cheats]() {
DebugAssert(System::HasCheatList());
System::GetCheatList()->MergeList(new_cheats);
@ -716,7 +716,7 @@ void CheatManagerDialog::clearClicked()
return;
}
g_emu_thread->executeOnEmulationThread([] { System::ClearCheatList(true); }, true);
Host::RunOnCPUThread([] { System::ClearCheatList(true); }, true);
updateCheatList();
}
@ -731,7 +731,7 @@ void CheatManagerDialog::resetClicked()
return;
}
g_emu_thread->executeOnEmulationThread([] { System::DeleteCheatList(); }, true);
Host::RunOnCPUThread([] { System::DeleteCheatList(); }, true);
updateCheatList();
}

View File

@ -25,12 +25,6 @@ ConsoleSettingsWidget::ConsoleSettingsWidget(SettingsDialog* dialog, QWidget* pa
qApp->translate("CPUExecutionMode", Settings::GetCPUExecutionModeDisplayName(static_cast<CPUExecutionMode>(i))));
}
for (u32 i = 0; i < static_cast<u32>(MultitapMode::Count); i++)
{
m_ui.multitapMode->addItem(
qApp->translate("MultitapMode", Settings::GetMultitapModeDisplayName(static_cast<MultitapMode>(i))));
}
static constexpr float TIME_PER_SECTOR_DOUBLE_SPEED = 1000.0f / 150.0f;
m_ui.cdromReadaheadSectors->addItem(tr("Disabled (Synchronous)"));
for (u32 i = 1; i <= 32; i++)
@ -55,9 +49,7 @@ ConsoleSettingsWidget::ConsoleSettingsWidget(SettingsDialog* dialog, QWidget* pa
SettingWidgetBinder::BindWidgetToBoolSetting(sif, m_ui.cdromLoadImageToRAM, "CDROM", "LoadImageToRAM", false);
SettingWidgetBinder::BindWidgetToBoolSetting(sif, m_ui.cdromLoadImagePatches, "CDROM", "LoadImagePatches", false);
SettingWidgetBinder::BindWidgetToIntSetting(sif, m_ui.cdromSeekSpeedup, "CDROM", "SeekSpeedup", 1);
SettingWidgetBinder::BindWidgetToEnumSetting(sif, m_ui.multitapMode, "ControllerPorts", "MultitapMode",
&Settings::ParseMultitapModeName, &Settings::GetMultitapModeName,
Settings::DEFAULT_MULTITAP_MODE);
SettingWidgetBinder::BindWidgetToIntSetting(sif, m_ui.cdromReadSpeedup, "CDROM", "ReadSpeedup", 1, 1);
dialog->registerWidgetHelp(m_ui.region, tr("Region"), tr("Auto-Detect"),
tr("Determines the emulated hardware type."));
@ -99,21 +91,12 @@ ConsoleSettingsWidget::ConsoleSettingsWidget(SettingsDialog* dialog, QWidget* pa
dialog->registerWidgetHelp(m_ui.cdromLoadImagePatches, tr("Apply Image Patches"), tr("Unchecked"),
tr("Automatically applies patches to disc images when they are present in the same "
"directory. Currently only PPF patches are supported with this option."));
dialog->registerWidgetHelp(
m_ui.multitapMode, tr("Multitap"), tr("Disabled"),
tr("Enables multitap support on specified controller ports. Leave disabled for games that do "
"not support multitap input."));
m_ui.cpuClockSpeed->setEnabled(m_ui.enableCPUClockSpeedControl->checkState() == Qt::Checked);
m_ui.cdromReadSpeedup->setCurrentIndex(m_dialog->getEffectiveIntValue("CDROM", "ReadSpeedup", 1) - 1);
connect(m_ui.enableCPUClockSpeedControl, &QCheckBox::stateChanged, this,
&ConsoleSettingsWidget::onEnableCPUClockSpeedControlChecked);
connect(m_ui.cpuClockSpeed, &QSlider::valueChanged, this, &ConsoleSettingsWidget::onCPUClockSpeedValueChanged);
connect(m_ui.cdromReadSpeedup, QOverload<int>::of(&QComboBox::currentIndexChanged), this,
&ConsoleSettingsWidget::onCDROMReadSpeedupValueChanged);
connect(m_ui.multitapMode, QOverload<int>::of(&QComboBox::currentIndexChanged),
[this](int index) { emit multitapModeChanged(); });
calculateCPUClockValue();
}
@ -164,11 +147,6 @@ void ConsoleSettingsWidget::updateCPUClockSpeedLabel()
m_ui.cpuClockSpeedLabel->setText(tr("%1% (%2MHz)").arg(percent).arg(frequency / 1000000.0, 0, 'f', 2));
}
void ConsoleSettingsWidget::onCDROMReadSpeedupValueChanged(int value)
{
m_dialog->setIntSettingValue("CDROM", "ReadSpeedup", value + 1);
}
void ConsoleSettingsWidget::calculateCPUClockValue()
{
const u32 numerator = static_cast<u32>(m_dialog->getEffectiveIntValue("CPU", "OverclockNumerator", 1));

View File

@ -14,14 +14,10 @@ public:
explicit ConsoleSettingsWidget(SettingsDialog* dialog, QWidget* parent);
~ConsoleSettingsWidget();
Q_SIGNALS:
void multitapModeChanged();
private Q_SLOTS:
void onEnableCPUClockSpeedControlChecked(int state);
void onCPUClockSpeedValueChanged(int value);
void updateCPUClockSpeedLabel();
void onCDROMReadSpeedupValueChanged(int value);
private:
void calculateCPUClockValue();

View File

@ -305,25 +305,6 @@
</layout>
</widget>
</item>
<item>
<widget class="QGroupBox" name="groupBox_3">
<property name="title">
<string>Controller Ports</string>
</property>
<layout class="QFormLayout" name="formLayout_2">
<item row="0" column="0">
<widget class="QLabel" name="label_4">
<property name="text">
<string>Multitap:</string>
</property>
</widget>
</item>
<item row="0" column="1">
<widget class="QComboBox" name="multitapMode"/>
</item>
</layout>
</widget>
</item>
<item>
<spacer name="verticalSpacer">
<property name="orientation">

View File

@ -56,7 +56,7 @@
<string>Automatic binding</string>
</property>
<property name="icon">
<iconset theme="gamepad-line">
<iconset theme="ControllerSettings">
<normaloff>.</normaloff>.</iconset>
</property>
</widget>
@ -67,7 +67,7 @@
<string>Clear Bindings</string>
</property>
<property name="icon">
<iconset theme="file-reduce-line">
<iconset theme="Clear">
<normaloff>.</normaloff>.</iconset>
</property>
</widget>

View File

@ -22,7 +22,7 @@ ControllerBindingWidget::ControllerBindingWidget(QWidget* parent, ControllerSett
{
m_ui.setupUi(this);
populateControllerTypes();
onTypeChanged();
populateBindingWidget();
connect(m_ui.controllerType, QOverload<int>::of(&QComboBox::currentIndexChanged), this,
&ControllerBindingWidget::onTypeChanged);
@ -96,12 +96,15 @@ void ControllerBindingWidget::onTypeChanged()
SettingsInterface* sif = m_dialog->getProfileSettingsInterface();
if (sif)
{
sif->SetStringValue(m_config_section.c_str(), "Type", Settings::GetControllerTypeName(m_controller_type));
g_emu_thread->reloadGameSettings();
}
else
{
Host::SetBaseStringSettingValue(m_config_section.c_str(), "Type", Settings::GetControllerTypeName(m_controller_type));
// TODO: reloadInputProfile() ?
g_emu_thread->applySettings();
g_emu_thread->applySettings();
}
populateBindingWidget();
}
@ -197,7 +200,7 @@ ControllerBindingWidget_Base::~ControllerBindingWidget_Base() {}
QIcon ControllerBindingWidget_Base::getIcon() const
{
return QIcon::fromTheme("artboard-2-line");
return QIcon::fromTheme("BIOSSettings");
}
void ControllerBindingWidget_Base::initBindingWidgets()
@ -304,7 +307,7 @@ ControllerBindingWidget_DigitalController::~ControllerBindingWidget_DigitalContr
QIcon ControllerBindingWidget_DigitalController::getIcon() const
{
return QIcon::fromTheme("gamepad-line");
return QIcon::fromTheme("ControllerSettings");
}
ControllerBindingWidget_Base* ControllerBindingWidget_DigitalController::createInstance(ControllerBindingWidget* parent)
@ -325,7 +328,7 @@ ControllerBindingWidget_AnalogController::~ControllerBindingWidget_AnalogControl
QIcon ControllerBindingWidget_AnalogController::getIcon() const
{
return QIcon::fromTheme("gamepad-line");
return QIcon::fromTheme("ControllerSettings");
}
ControllerBindingWidget_Base* ControllerBindingWidget_AnalogController::createInstance(ControllerBindingWidget* parent)

View File

@ -21,18 +21,24 @@ ControllerGlobalSettingsWidget::ControllerGlobalSettingsWidget(QWidget* parent,
#else
m_ui.enableRawInput->setEnabled(false);
#endif
ControllerSettingWidgetBinder::BindWidgetToInputProfileBool(sif, m_ui.multitapPort1, "Pad", "MultitapPort1", false);
ControllerSettingWidgetBinder::BindWidgetToInputProfileBool(sif, m_ui.multitapPort2, "Pad", "MultitapPort2", false);
ControllerSettingWidgetBinder::BindWidgetToInputProfileBool(sif, m_ui.pointerXInvert, "Pad", "PointerXInvert", false);
ControllerSettingWidgetBinder::BindWidgetToInputProfileBool(sif, m_ui.pointerYInvert, "Pad", "PointerYInvert", false);
ControllerSettingWidgetBinder::BindWidgetToInputProfileFloat(sif, m_ui.pointerXScale, "Pad", "PointerXScale", 8.0f);
ControllerSettingWidgetBinder::BindWidgetToInputProfileFloat(sif, m_ui.pointerYScale, "Pad", "PointerYScale", 8.0f);
SettingWidgetBinder::BindWidgetToEnumSetting(sif, m_ui.multitapMode, "ControllerPorts", "MultitapMode",
&Settings::ParseMultitapModeName, &Settings::GetMultitapModeName,
Settings::DEFAULT_MULTITAP_MODE);
ControllerSettingWidgetBinder::BindWidgetToInputProfileBool(sif, m_ui.pointerXInvert, "ControllerPorts",
"PointerXInvert", false);
ControllerSettingWidgetBinder::BindWidgetToInputProfileBool(sif, m_ui.pointerYInvert, "ControllerPorts",
"PointerYInvert", false);
ControllerSettingWidgetBinder::BindWidgetToInputProfileFloat(sif, m_ui.pointerXScale, "ControllerPorts",
"PointerXScale", 8.0f);
ControllerSettingWidgetBinder::BindWidgetToInputProfileFloat(sif, m_ui.pointerYScale, "ControllerPorts",
"PointerYScale", 8.0f);
if (dialog->isEditingProfile())
{
m_ui.useProfileHotkeyBindings->setChecked(m_dialog->getBoolValue("Pad", "UseProfileHotkeyBindings", false));
m_ui.useProfileHotkeyBindings->setChecked(
m_dialog->getBoolValue("ControllerPorts", "UseProfileHotkeyBindings", false));
connect(m_ui.useProfileHotkeyBindings, &QCheckBox::stateChanged, this, [this](int new_state) {
m_dialog->setBoolValue("Pad", "UseProfileHotkeyBindings", (new_state == Qt::Checked));
m_dialog->setBoolValue("ControllerPorts", "UseProfileHotkeyBindings", (new_state == Qt::Checked));
emit bindingSetupChanged();
});
}
@ -46,8 +52,7 @@ ControllerGlobalSettingsWidget::ControllerGlobalSettingsWidget(QWidget* parent,
connect(m_ui.enableSDLSource, &QCheckBox::stateChanged, this,
&ControllerGlobalSettingsWidget::updateSDLOptionsEnabled);
for (QCheckBox* cb : {m_ui.multitapPort1, m_ui.multitapPort2})
connect(cb, &QCheckBox::stateChanged, this, [this]() { emit bindingSetupChanged(); });
connect(m_ui.multitapMode, &QComboBox::currentIndexChanged, this, [this]() { emit bindingSetupChanged(); });
connect(m_ui.pointerXScale, &QSlider::valueChanged, this,
[this](int value) { m_ui.pointerXScaleLabel->setText(QStringLiteral("%1").arg(value)); });

View File

@ -239,7 +239,7 @@
<property name="title">
<string>Controller Multitap</string>
</property>
<layout class="QGridLayout" name="gridLayout_4">
<layout class="QFormLayout" name="formLayout">
<item row="0" column="0" colspan="2">
<widget class="QLabel" name="label">
<property name="text">
@ -251,17 +251,34 @@
</widget>
</item>
<item row="1" column="0">
<widget class="QCheckBox" name="multitapPort1">
<widget class="QLabel" name="label_8">
<property name="text">
<string>Multitap on Console Port 1</string>
<string>Multitap Mode:</string>
</property>
</widget>
</item>
<item row="1" column="1">
<widget class="QCheckBox" name="multitapPort2">
<property name="text">
<string>Multitap on Console Port 2</string>
</property>
<widget class="QComboBox" name="multitapMode">
<item>
<property name="text">
<string>Disabled</string>
</property>
</item>
<item>
<property name="text">
<string>Enable on Port 1 Only</string>
</property>
</item>
<item>
<property name="text">
<string>Enable on Port 2 Only</string>
</property>
</item>
<item>
<property name="text">
<string>Enable on Ports 1 and 2</string>
</property>
</item>
</widget>
</item>
</layout>

View File

@ -5,10 +5,10 @@
#include "controllerglobalsettingswidget.h"
#include "core/controller.h"
#include "core/host_settings.h"
#include "util/ini_settings_interface.h"
#include "frontend-common/input_manager.h"
#include "hotkeysettingswidget.h"
#include "qthost.h"
#include "util/ini_settings_interface.h"
#include <QtWidgets/QInputDialog>
#include <QtWidgets/QMessageBox>
@ -37,13 +37,12 @@ ControllerSettingsDialog::ControllerSettingsDialog(QWidget* parent /* = nullptr
connect(m_ui.deleteProfile, &QPushButton::clicked, this, &ControllerSettingsDialog::onDeleteProfileClicked);
connect(m_ui.restoreDefaults, &QPushButton::clicked, this, &ControllerSettingsDialog::onRestoreDefaultsClicked);
connect(g_emu_thread, &QtHostInterface::onInputDevicesEnumerated, this,
connect(g_emu_thread, &EmuThread::onInputDevicesEnumerated, this,
&ControllerSettingsDialog::onInputDevicesEnumerated);
connect(g_emu_thread, &QtHostInterface::onInputDeviceConnected, this,
&ControllerSettingsDialog::onInputDeviceConnected);
connect(g_emu_thread, &QtHostInterface::onInputDeviceDisconnected, this,
connect(g_emu_thread, &EmuThread::onInputDeviceConnected, this, &ControllerSettingsDialog::onInputDeviceConnected);
connect(g_emu_thread, &EmuThread::onInputDeviceDisconnected, this,
&ControllerSettingsDialog::onInputDeviceDisconnected);
connect(g_emu_thread, &QtHostInterface::onVibrationMotorsEnumerated, this,
connect(g_emu_thread, &EmuThread::onVibrationMotorsEnumerated, this,
&ControllerSettingsDialog::onVibrationMotorsEnumerated);
// trigger a device enumeration to populate the device list
@ -330,7 +329,7 @@ void ControllerSettingsDialog::createWidgets()
// global settings
QListWidgetItem* item = new QListWidgetItem();
item->setText(tr("Global Settings"));
item->setIcon(QIcon::fromTheme("settings-3-line"));
item->setIcon(QIcon::fromTheme("GeneralSettings"));
m_ui.settingsCategory->addItem(item);
m_ui.settingsCategory->setCurrentRow(0);
m_global_settings = new ControllerGlobalSettingsWidget(m_ui.settingsContainer, this);
@ -342,8 +341,14 @@ void ControllerSettingsDialog::createWidgets()
}
// load mtap settings
const MultitapMode mtap_mode =
Settings::ParseMultitapModeName(
getStringValue("ControllerPorts", "MultitapMode", Settings::GetMultitapModeName(Settings::DEFAULT_MULTITAP_MODE))
.c_str())
.value_or(Settings::DEFAULT_MULTITAP_MODE);
const std::array<bool, 2> mtap_enabled = {
{getBoolValue("Pad", "MultitapPort1", false), getBoolValue("Pad", "MultitapPort2", false)}};
{(mtap_mode == MultitapMode::Port1Only || mtap_mode == MultitapMode::BothPorts),
(mtap_mode == MultitapMode::Port2Only || mtap_mode == MultitapMode::BothPorts)}};
// we reorder things a little to make it look less silly for mtap
static constexpr const std::array<u32, MAX_PORTS> mtap_port_order = {{0, 2, 3, 4, 1, 5, 6, 7}};
@ -373,11 +378,11 @@ void ControllerSettingsDialog::createWidgets()
}
// only add hotkeys if we're editing global settings
if (!m_profile_interface || m_profile_interface->GetBoolValue("Pad", "UseProfileHotkeyBindings", false))
if (!m_profile_interface || m_profile_interface->GetBoolValue("ControllerPorts", "UseProfileHotkeyBindings", false))
{
QListWidgetItem* item = new QListWidgetItem();
item->setText(tr("Hotkeys"));
item->setIcon(QIcon::fromTheme("keyboard-line"));
item->setIcon(QIcon::fromTheme("HotkeySettings"));
m_ui.settingsCategory->addItem(item);
m_hotkey_settings = new HotkeySettingsWidget(m_ui.settingsContainer, this);
m_ui.settingsContainer->addWidget(m_hotkey_settings);
@ -397,7 +402,7 @@ void ControllerSettingsDialog::updateListDescription(u32 global_slot, Controller
bool is_ok;
if (item_data.toUInt(&is_ok) == global_slot && is_ok)
{
//const bool is_mtap_port = Controller::PadIsMultitapSlot(global_slot);
// const bool is_mtap_port = Controller::PadIsMultitapSlot(global_slot);
const auto [port, slot] = Controller::ConvertPadToPortAndSlot(global_slot);
const bool mtap_enabled = getBoolValue("Pad", (port == 0) ? "MultitapPort1" : "MultitapPort2", false);

View File

@ -81,7 +81,7 @@
<string>New Profile</string>
</property>
<property name="icon">
<iconset theme="file-add-line">
<iconset theme="PostProcessingAdd">
<normaloff>.</normaloff>.</iconset>
</property>
</widget>
@ -92,7 +92,7 @@
<string>Load Profile</string>
</property>
<property name="icon">
<iconset theme="folder-open-line"/>
<iconset theme="LoadState"/>
</property>
</widget>
</item>
@ -102,7 +102,7 @@
<string>Delete Profile</string>
</property>
<property name="icon">
<iconset theme="file-reduce-line">
<iconset theme="PostProcessingRemove">
<normaloff>.</normaloff>.</iconset>
</property>
</widget>
@ -113,7 +113,7 @@
<string>Restore Defaults</string>
</property>
<property name="icon">
<iconset theme="restart-line"/>
<iconset theme="Reset"/>
</property>
</widget>
</item>

View File

@ -84,7 +84,7 @@ void DebuggerWindow::onPauseActionToggled(bool paused)
setUIEnabled(false);
}
g_emu_thread->pauseSystem(paused);
g_emu_thread->setSystemPaused(paused);
}
void DebuggerWindow::onRunToCursorTriggered()
@ -97,7 +97,7 @@ void DebuggerWindow::onRunToCursorTriggered()
}
CPU::AddBreakpoint(addr.value(), true, true);
g_emu_thread->pauseSystem(false);
g_emu_thread->setSystemPaused(false);
}
void DebuggerWindow::onGoToPCTriggered()
@ -195,7 +195,7 @@ void DebuggerWindow::onStepOverActionTriggered()
// unpause to let it run to the breakpoint
m_registers_model->saveCurrentValues();
g_emu_thread->pauseSystem(false);
g_emu_thread->setSystemPaused(false);
}
void DebuggerWindow::onStepOutActionTriggered()
@ -209,7 +209,7 @@ void DebuggerWindow::onStepOutActionTriggered()
// unpause to let it run to the breakpoint
m_registers_model->saveCurrentValues();
g_emu_thread->pauseSystem(false);
g_emu_thread->setSystemPaused(false);
}
void DebuggerWindow::onCodeViewItemActivated(QModelIndex index)
@ -355,9 +355,9 @@ void DebuggerWindow::onMemorySearchStringChanged(const QString&)
void DebuggerWindow::closeEvent(QCloseEvent* event)
{
QMainWindow::closeEvent(event);
g_emu_thread->pauseSystem(true, true);
g_emu_thread->setSystemPaused(true, true);
CPU::ClearBreakpoints();
g_emu_thread->pauseSystem(false);
g_emu_thread->setSystemPaused(false);
emit closed();
}
@ -383,10 +383,10 @@ void DebuggerWindow::setupAdditionalUi()
void DebuggerWindow::connectSignals()
{
QtHostInterface* hi = g_emu_thread;
connect(hi, &QtHostInterface::systemPaused, this, &DebuggerWindow::onEmulationPaused);
connect(hi, &QtHostInterface::systemResumed, this, &DebuggerWindow::onEmulationResumed);
connect(hi, &QtHostInterface::debuggerMessageReported, this, &DebuggerWindow::onDebuggerMessageReported);
EmuThread* hi = g_emu_thread;
connect(hi, &EmuThread::systemPaused, this, &DebuggerWindow::onEmulationPaused);
connect(hi, &EmuThread::systemResumed, this, &DebuggerWindow::onEmulationResumed);
connect(hi, &EmuThread::debuggerMessageReported, this, &DebuggerWindow::onDebuggerMessageReported);
connect(m_ui.actionPause, &QAction::toggled, this, &DebuggerWindow::onPauseActionToggled);
connect(m_ui.actionRunToCursor, &QAction::triggered, this, &DebuggerWindow::onRunToCursorTriggered);
@ -415,7 +415,7 @@ void DebuggerWindow::connectSignals()
void DebuggerWindow::disconnectSignals()
{
QtHostInterface* hi = g_emu_thread;
EmuThread* hi = g_emu_thread;
hi->disconnect(this);
}

View File

@ -17,6 +17,10 @@
#include <qpa/qplatformnativeinterface.h>
#endif
#ifdef _WIN32
#include "common/windows_headers.h"
#endif
Log_SetChannel(DisplayWidget);
DisplayWidget::DisplayWidget(QWidget* parent) : QWidget(parent)
@ -288,7 +292,7 @@ bool DisplayWidget::event(QEvent* event)
if (event->type() == QEvent::MouseButtonDblClick &&
static_cast<const QMouseEvent*>(event)->button() == Qt::LeftButton &&
!InputManager::HasAnyBindingsForKey(InputManager::MakePointerButtonKey(0, 0)) &&
Host::GetBoolSettingValue("UI", "DoubleClickTogglesFullscreen", true))
Host::GetBoolSettingValue("Main", "DoubleClickTogglesFullscreen", true))
{
g_emu_thread->toggleFullscreen();
}

View File

@ -49,7 +49,7 @@ private:
bool m_should_hide_cursor = false;
bool m_cursor_hidden = false;
std::vector<int> m_keys_pressed_with_modifiers;
std::vector<u32> m_keys_pressed_with_modifiers;
u32 m_last_window_width = 0;
u32 m_last_window_height = 0;

View File

@ -20,6 +20,7 @@
<ClCompile Include="debuggermodels.cpp" />
<ClCompile Include="debuggerwindow.cpp" />
<ClCompile Include="enhancementsettingswidget.cpp" />
<ClCompile Include="foldersettingswidget.cpp" />
<ClCompile Include="gamelistmodel.cpp" />
<ClCompile Include="gamelistsearchdirectoriesmodel.cpp" />
<ClCompile Include="generalsettingswidget.cpp" />
@ -32,10 +33,10 @@
<ClCompile Include="gamelistsettingswidget.cpp" />
<ClCompile Include="gamelistrefreshthread.cpp" />
<ClCompile Include="gamelistwidget.cpp" />
<ClCompile Include="gamesummarywidget.cpp" />
<ClCompile Include="gamepropertiesdialog.cpp" />
<ClCompile Include="gdbconnection.cpp" />
<ClCompile Include="gdbserver.cpp" />
<ClCompile Include="main.cpp" />
<ClCompile Include="mainwindow.cpp" />
<ClCompile Include="memorycardsettingswidget.cpp" />
<ClCompile Include="memorycardeditordialog.cpp" />
@ -86,6 +87,7 @@
<QtMoc Include="gamelistrefreshthread.h" />
<QtMoc Include="gamelistwidget.h" />
<QtMoc Include="gamepropertiesdialog.h" />
<QtMoc Include="gamesummarywidget.h" />
<QtMoc Include="gdbconnection.h" />
<QtMoc Include="gdbserver.h" />
<QtMoc Include="postprocessingchainconfigwidget.h" />
@ -93,6 +95,7 @@
<QtMoc Include="postprocessingsettingswidget.h" />
<QtMoc Include="mainwindow.h" />
<QtMoc Include="qthost.h" />
<QtMoc Include="foldersettingswidget.h" />
<ClInclude Include="qtutils.h" />
<QtMoc Include="settingsdialog.h" />
</ItemGroup>
@ -187,6 +190,12 @@
<QtUi Include="gamelistwidget.ui">
<FileType>Document</FileType>
</QtUi>
<QtUi Include="gamesummarywidget.ui">
<FileType>Document</FileType>
</QtUi>
<QtUi Include="foldersettingswidget.ui">
<FileType>Document</FileType>
</QtUi>
</ItemGroup>
<ItemGroup>
<QtResource Include="resources\resources.qrc">
@ -211,12 +220,14 @@
<ClCompile Include="$(IntDir)moc_displaywidget.cpp" />
<ClCompile Include="$(IntDir)moc_emulationsettingswidget.cpp" />
<ClCompile Include="$(IntDir)moc_enhancementsettingswidget.cpp" />
<ClCompile Include="$(IntDir)moc_foldersettingswidget.cpp" />
<ClCompile Include="$(IntDir)moc_gamelistmodel.cpp" />
<ClCompile Include="$(IntDir)moc_gamelistrefreshthread.cpp" />
<ClCompile Include="$(IntDir)moc_gamelistsearchdirectoriesmodel.cpp" />
<ClCompile Include="$(IntDir)moc_gamelistsettingswidget.cpp" />
<ClCompile Include="$(IntDir)moc_gamelistwidget.cpp" />
<ClCompile Include="$(IntDir)moc_gamepropertiesdialog.cpp" />
<ClCompile Include="$(IntDir)moc_gamesummarywidget.cpp" />
<ClCompile Include="$(IntDir)moc_gdbconnection.cpp" />
<ClCompile Include="$(IntDir)moc_gdbserver.cpp" />
<ClCompile Include="$(IntDir)moc_generalsettingswidget.cpp" />
@ -310,9 +321,10 @@
<ItemDefinitionGroup>
<Link>
<AdditionalDependencies>$(RootBuildDir)frontend-common\frontend-common.lib;%(AdditionalDependencies)</AdditionalDependencies>
<AdditionalDependencies>$(QtEntryPointLib);%(AdditionalDependencies)</AdditionalDependencies>
</Link>
<ClCompile>
<DisableSpecificWarnings Condition="'$(Configuration)|$(Platform)'=='DebugFast|x64'">4127;%(DisableSpecificWarnings)</DisableSpecificWarnings>
<DisableSpecificWarnings>4127;%(DisableSpecificWarnings)</DisableSpecificWarnings>
</ClCompile>
</ItemDefinitionGroup>
<Import Project="..\..\dep\msvc\vsprops\Targets.props" />

View File

@ -1,7 +1,6 @@
<?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" />
@ -21,7 +20,6 @@
<ClCompile Include="$(IntDir)moc_inputbindingwidgets.cpp" />
<ClCompile Include="audiosettingswidget.cpp" />
<ClCompile Include="$(IntDir)moc_audiosettingswidget.cpp" />
<ClCompile Include="$(IntDir)moc_qtdisplaywidget.cpp" />
<ClCompile Include="displaywidget.cpp" />
<ClCompile Include="qtprogresscallback.cpp" />
<ClCompile Include="$(IntDir)moc_qtprogresscallback.cpp" />
@ -89,6 +87,11 @@
<ClCompile Include="$(IntDir)moc_controllersettingsdialog.cpp" />
<ClCompile Include="gamelistrefreshthread.cpp" />
<ClCompile Include="$(IntDir)moc_gamelistrefreshthread.cpp" />
<ClCompile Include="foldersettingswidget.cpp" />
<ClCompile Include="gamesummarywidget.cpp" />
<ClCompile Include="$(IntDir)moc_displaywidget.cpp" />
<ClCompile Include="$(IntDir)moc_foldersettingswidget.cpp" />
<ClCompile Include="$(IntDir)moc_gamesummarywidget.cpp" />
</ItemGroup>
<ItemGroup>
<ClInclude Include="qtutils.h" />
@ -147,6 +150,8 @@
<QtMoc Include="controllerglobalsettingswidget.h" />
<QtMoc Include="controllersettingsdialog.h" />
<QtMoc Include="gamelistrefreshthread.h" />
<QtMoc Include="gamesummarywidget.h" />
<QtMoc Include="foldersettingswidget.h" />
</ItemGroup>
<ItemGroup>
<QtUi Include="consolesettingswidget.ui" />
@ -179,6 +184,8 @@
<QtUi Include="controllerbindingwidget_digital_controller.ui" />
<QtUi Include="emptygamelistwidget.ui" />
<QtUi Include="gamelistwidget.ui" />
<QtUi Include="gamesummarywidget.ui" />
<QtUi Include="foldersettingswidget.ui" />
</ItemGroup>
<ItemGroup>
<Natvis Include="qt5.natvis" />

View File

@ -1,4 +1,5 @@
#include "emulationsettingswidget.h"
#include "common/make_array.h"
#include "core/system.h"
#include "qtutils.h"
#include "settingsdialog.h"
@ -14,30 +15,52 @@ EmulationSettingsWidget::EmulationSettingsWidget(SettingsDialog* dialog, QWidget
m_ui.setupUi(this);
SettingWidgetBinder::BindWidgetToBoolSetting(sif, m_ui.rewindEnable, "Main", "RewindEnable", false);
SettingWidgetBinder::BindWidgetToFloatSetting(sif, m_ui.rewindSaveFrequency, "Main", "RewindFrequency",
10.0f);
SettingWidgetBinder::BindWidgetToFloatSetting(sif, m_ui.rewindSaveFrequency, "Main", "RewindFrequency", 10.0f);
SettingWidgetBinder::BindWidgetToIntSetting(sif, m_ui.rewindSaveSlots, "Main", "RewindSaveSlots", 10);
SettingWidgetBinder::BindWidgetToIntSetting(sif, m_ui.runaheadFrames, "Main", "RunaheadFrameCount", 0);
QtUtils::FillComboBoxWithEmulationSpeeds(m_ui.emulationSpeed);
const int emulation_speed_index =
m_ui.emulationSpeed->findData(QVariant(m_dialog->getEffectiveFloatValue("Main", "EmulationSpeed", 1.0f)));
if (emulation_speed_index >= 0)
m_ui.emulationSpeed->setCurrentIndex(emulation_speed_index);
const float effective_emulation_speed = m_dialog->getEffectiveFloatValue("Main", "EmulationSpeed", 1.0f);
fillComboBoxWithEmulationSpeeds(m_ui.emulationSpeed, effective_emulation_speed);
if (m_dialog->isPerGameSettings() && !m_dialog->getFloatValue("Main", "EmulationSpeed", std::nullopt).has_value())
{
m_ui.emulationSpeed->setCurrentIndex(0);
}
else
{
const int emulation_speed_index = m_ui.emulationSpeed->findData(QVariant(effective_emulation_speed));
if (emulation_speed_index >= 0)
m_ui.emulationSpeed->setCurrentIndex(emulation_speed_index);
}
connect(m_ui.emulationSpeed, QOverload<int>::of(&QComboBox::currentIndexChanged), this,
&EmulationSettingsWidget::onEmulationSpeedIndexChanged);
QtUtils::FillComboBoxWithEmulationSpeeds(m_ui.fastForwardSpeed);
const int fast_forward_speed_index =
m_ui.fastForwardSpeed->findData(QVariant(m_dialog->getEffectiveFloatValue("Main", "FastForwardSpeed", 0.0f)));
if (fast_forward_speed_index >= 0)
m_ui.fastForwardSpeed->setCurrentIndex(fast_forward_speed_index);
const float effective_fast_forward_speed = m_dialog->getEffectiveFloatValue("Main", "FastForwardSpeed", 0.0f);
fillComboBoxWithEmulationSpeeds(m_ui.fastForwardSpeed, effective_fast_forward_speed);
if (m_dialog->isPerGameSettings() && !m_dialog->getFloatValue("Main", "FastForwardSpeed", std::nullopt).has_value())
{
m_ui.emulationSpeed->setCurrentIndex(0);
}
else
{
const int fast_forward_speed_index = m_ui.fastForwardSpeed->findData(QVariant(effective_fast_forward_speed));
if (fast_forward_speed_index >= 0)
m_ui.fastForwardSpeed->setCurrentIndex(fast_forward_speed_index);
}
connect(m_ui.fastForwardSpeed, QOverload<int>::of(&QComboBox::currentIndexChanged), this,
&EmulationSettingsWidget::onFastForwardSpeedIndexChanged);
QtUtils::FillComboBoxWithEmulationSpeeds(m_ui.turboSpeed);
const int turbo_speed_index =
m_ui.turboSpeed->findData(QVariant(m_dialog->getEffectiveFloatValue("Main", "TurboSpeed", 0.0f)));
if (turbo_speed_index >= 0)
m_ui.turboSpeed->setCurrentIndex(turbo_speed_index);
const float effective_turbo_speed = m_dialog->getEffectiveFloatValue("Main", "TurboSpeed", 0.0f);
fillComboBoxWithEmulationSpeeds(m_ui.turboSpeed, effective_turbo_speed);
if (m_dialog->isPerGameSettings() && !m_dialog->getFloatValue("Main", "TurboSpeed", std::nullopt).has_value())
{
m_ui.emulationSpeed->setCurrentIndex(0);
}
else
{
const int turbo_speed_index = m_ui.turboSpeed->findData(QVariant(effective_turbo_speed));
if (turbo_speed_index >= 0)
m_ui.turboSpeed->setCurrentIndex(turbo_speed_index);
}
connect(m_ui.turboSpeed, QOverload<int>::of(&QComboBox::currentIndexChanged), this,
&EmulationSettingsWidget::onTurboSpeedIndexChanged);
@ -77,8 +100,35 @@ EmulationSettingsWidget::EmulationSettingsWidget(SettingsDialog* dialog, QWidget
EmulationSettingsWidget::~EmulationSettingsWidget() = default;
void EmulationSettingsWidget::fillComboBoxWithEmulationSpeeds(QComboBox* cb, float global_value)
{
if (m_dialog->isPerGameSettings())
{
if (global_value == 0.0f)
cb->addItem(tr("Use Global Setting [Unlimited]"));
else
cb->addItem(tr("Use Global Setting [%1%]").arg(static_cast<u32>(global_value * 100.0f)));
}
cb->addItem(tr("Unlimited"), QVariant(0.0f));
static constexpr auto speeds = make_array(10, 20, 30, 40, 50, 60, 70, 80, 90, 100, 125, 150, 175, 200, 250, 300, 350,
400, 450, 500, 600, 700, 800, 900, 1000);
for (const int speed : speeds)
{
cb->addItem(tr("%1% [%2 FPS (NTSC) / %3 FPS (PAL)]").arg(speed).arg((60 * speed) / 100).arg((50 * speed) / 100),
QVariant(static_cast<float>(speed) / 100.0f));
}
}
void EmulationSettingsWidget::onEmulationSpeedIndexChanged(int index)
{
if (m_dialog->isPerGameSettings() && index == 0)
{
m_dialog->removeSettingValue("Main", "EmulationSpeed");
return;
}
bool okay;
const float value = m_ui.emulationSpeed->currentData().toFloat(&okay);
m_dialog->setFloatSettingValue("Main", "EmulationSpeed", okay ? value : 1.0f);
@ -86,6 +136,12 @@ void EmulationSettingsWidget::onEmulationSpeedIndexChanged(int index)
void EmulationSettingsWidget::onFastForwardSpeedIndexChanged(int index)
{
if (m_dialog->isPerGameSettings() && index == 0)
{
m_dialog->removeSettingValue("Main", "FastForwardSpeed");
return;
}
bool okay;
const float value = m_ui.fastForwardSpeed->currentData().toFloat(&okay);
m_dialog->setFloatSettingValue("Main", "FastForwardSpeed", okay ? value : 0.0f);
@ -93,6 +149,12 @@ void EmulationSettingsWidget::onFastForwardSpeedIndexChanged(int index)
void EmulationSettingsWidget::onTurboSpeedIndexChanged(int index)
{
if (m_dialog->isPerGameSettings() && index == 0)
{
m_dialog->removeSettingValue("Main", "TurboSpeed");
return;
}
bool okay;
const float value = m_ui.turboSpeed->currentData().toFloat(&okay);
m_dialog->setFloatSettingValue("Main", "TurboSpeed", okay ? value : 0.0f);
@ -100,9 +162,11 @@ void EmulationSettingsWidget::onTurboSpeedIndexChanged(int index)
void EmulationSettingsWidget::updateRewind()
{
m_ui.rewindEnable->setEnabled(!runaheadEnabled());
const bool rewind_enabled = m_dialog->getEffectiveBoolValue("Main", "RewindEnable", false);
const bool runahead_enabled = m_dialog->getIntValue("Main", "RunaheadFrameCount", 0) > 0;
m_ui.rewindEnable->setEnabled(!runahead_enabled);
if (m_ui.rewindEnable->isEnabled() && m_ui.rewindEnable->isChecked())
if (!runahead_enabled && rewind_enabled)
{
const u32 frames = static_cast<u32>(m_ui.rewindSaveSlots->value());
const float frequency = static_cast<float>(m_ui.rewindSaveFrequency->value());
@ -122,7 +186,7 @@ void EmulationSettingsWidget::updateRewind()
}
else
{
if (!m_ui.rewindEnable->isEnabled())
if (runahead_enabled)
{
m_ui.rewindSummary->setText(tr(
"Rewind is disabled because runahead is enabled. Runahead will significantly increase system requirements."));

View File

@ -21,7 +21,7 @@ private Q_SLOTS:
void updateRewind();
private:
bool runaheadEnabled() { return m_ui.runaheadFrames->currentIndex() > 0; }
void fillComboBoxWithEmulationSpeeds(QComboBox* cb, float global_value);
Ui::EmulationSettingsWidget m_ui;

View File

@ -0,0 +1,24 @@
#include <QtWidgets/QMessageBox>
#include <algorithm>
#include "foldersettingswidget.h"
#include "settingsdialog.h"
#include "settingwidgetbinder.h"
FolderSettingsWidget::FolderSettingsWidget(SettingsDialog* dialog, QWidget* parent) : QWidget(parent)
{
SettingsInterface* sif = dialog->getSettingsInterface();
m_ui.setupUi(this);
SettingWidgetBinder::BindWidgetToFolderSetting(sif, m_ui.cache, m_ui.cacheBrowse, m_ui.cacheOpen, m_ui.cacheReset,
"Folders", "Cache", "cache");
SettingWidgetBinder::BindWidgetToFolderSetting(sif, m_ui.covers, m_ui.coversBrowse, m_ui.coversOpen, m_ui.coversReset,
"Folders", "Covers", "covers");
SettingWidgetBinder::BindWidgetToFolderSetting(sif, m_ui.screenshots, m_ui.screenshotsBrowse, m_ui.screenshotsOpen,
m_ui.screenshotsReset, "Folders", "Screenshots", "screenshots");
SettingWidgetBinder::BindWidgetToFolderSetting(sif, m_ui.saveStates, m_ui.saveStatesBrowse, m_ui.saveStatesOpen,
m_ui.saveStatesReset, "Folders", "SaveStates", "savestates");
}
FolderSettingsWidget::~FolderSettingsWidget() = default;

View File

@ -0,0 +1,19 @@
#pragma once
#include <QtWidgets/QWidget>
#include "ui_foldersettingswidget.h"
class SettingsDialog;
class FolderSettingsWidget : public QWidget
{
Q_OBJECT
public:
FolderSettingsWidget(SettingsDialog* dialog, QWidget* parent);
~FolderSettingsWidget();
private:
Ui::FolderSettingsWidget m_ui;
};

View File

@ -0,0 +1,208 @@
<?xml version="1.0" encoding="UTF-8"?>
<ui version="4.0">
<class>FolderSettingsWidget</class>
<widget class="QWidget" name="FolderSettingsWidget">
<property name="geometry">
<rect>
<x>0</x>
<y>0</y>
<width>648</width>
<height>487</height>
</rect>
</property>
<property name="windowTitle">
<string>Form</string>
</property>
<layout class="QVBoxLayout" name="verticalLayout">
<property name="leftMargin">
<number>0</number>
</property>
<property name="topMargin">
<number>0</number>
</property>
<property name="rightMargin">
<number>0</number>
</property>
<property name="bottomMargin">
<number>0</number>
</property>
<item>
<widget class="QGroupBox" name="groupBox_3">
<property name="title">
<string>Cache Directory</string>
</property>
<layout class="QGridLayout" name="gridLayout_3">
<item row="1" column="0">
<widget class="QLineEdit" name="cache"/>
</item>
<item row="1" column="1">
<widget class="QPushButton" name="cacheBrowse">
<property name="text">
<string>Browse...</string>
</property>
</widget>
</item>
<item row="1" column="2">
<widget class="QPushButton" name="cacheOpen">
<property name="text">
<string>Open...</string>
</property>
</widget>
</item>
<item row="1" column="3">
<widget class="QPushButton" name="cacheReset">
<property name="text">
<string>Reset</string>
</property>
</widget>
</item>
<item row="0" column="0" colspan="4">
<widget class="QLabel" name="label_3">
<property name="text">
<string>Used for storing shaders and game list data.</string>
</property>
</widget>
</item>
</layout>
</widget>
</item>
<item>
<widget class="QGroupBox" name="groupBox_4">
<property name="title">
<string>Covers Directory</string>
</property>
<layout class="QGridLayout" name="gridLayout_4">
<item row="1" column="0">
<widget class="QLineEdit" name="covers"/>
</item>
<item row="1" column="1">
<widget class="QPushButton" name="coversBrowse">
<property name="text">
<string>Browse...</string>
</property>
</widget>
</item>
<item row="1" column="2">
<widget class="QPushButton" name="coversOpen">
<property name="text">
<string>Open...</string>
</property>
</widget>
</item>
<item row="1" column="3">
<widget class="QPushButton" name="coversReset">
<property name="text">
<string>Reset</string>
</property>
</widget>
</item>
<item row="0" column="0" colspan="4">
<widget class="QLabel" name="label_4">
<property name="text">
<string>Used for storing covers in the game grid/Big Picture UIs.</string>
</property>
</widget>
</item>
</layout>
</widget>
</item>
<item>
<widget class="QGroupBox" name="groupBox">
<property name="title">
<string>Screenshots Directory</string>
</property>
<layout class="QGridLayout" name="gridLayout">
<item row="1" column="0">
<widget class="QLineEdit" name="screenshots"/>
</item>
<item row="1" column="1">
<widget class="QPushButton" name="screenshotsBrowse">
<property name="text">
<string>Browse...</string>
</property>
</widget>
</item>
<item row="1" column="2">
<widget class="QPushButton" name="screenshotsOpen">
<property name="text">
<string>Open...</string>
</property>
</widget>
</item>
<item row="1" column="3">
<widget class="QPushButton" name="screenshotsReset">
<property name="text">
<string>Reset</string>
</property>
</widget>
</item>
<item row="0" column="0" colspan="4">
<widget class="QLabel" name="label">
<property name="text">
<string>Used for screenshots.</string>
</property>
</widget>
</item>
</layout>
</widget>
</item>
<item>
<widget class="QGroupBox" name="groupBox_2">
<property name="title">
<string>Save States Directory</string>
</property>
<layout class="QGridLayout" name="gridLayout_2">
<item row="1" column="0">
<widget class="QLineEdit" name="saveStates"/>
</item>
<item row="1" column="1">
<widget class="QPushButton" name="saveStatesBrowse">
<property name="text">
<string>Browse...</string>
</property>
</widget>
</item>
<item row="1" column="2">
<widget class="QPushButton" name="saveStatesOpen">
<property name="text">
<string>Open...</string>
</property>
</widget>
</item>
<item row="1" column="3">
<widget class="QPushButton" name="saveStatesReset">
<property name="text">
<string>Reset</string>
</property>
</widget>
</item>
<item row="0" column="0" colspan="4">
<widget class="QLabel" name="label_2">
<property name="text">
<string>Used for storing save states.</string>
</property>
</widget>
</item>
</layout>
</widget>
</item>
<item>
<spacer name="verticalSpacer">
<property name="orientation">
<enum>Qt::Vertical</enum>
</property>
<property name="sizeHint" stdset="0">
<size>
<width>20</width>
<height>132</height>
</size>
</property>
</spacer>
</item>
</layout>
</widget>
<resources>
<include location="../../p2-qt-rebase/pcsx2-qt/resources/resources.qrc"/>
</resources>
<connections/>
</ui>

View File

@ -3,6 +3,7 @@
#include "common/path.h"
#include "common/string_util.h"
#include "core/system.h"
#include "qtutils.h"
#include <QtCore/QDate>
#include <QtCore/QDateTime>
#include <QtGui/QGuiApplication>
@ -290,43 +291,18 @@ QVariant GameListModel::data(const QModelIndex& index, int role) const
{
case Column_Type:
{
switch (ge->type)
{
case GameList::EntryType::Disc:
return (/*(ge.settings.GetUserSettingsCount() > 0)*/ false ? m_type_disc_with_settings_pixmap :
m_type_disc_pixmap);
case GameList::EntryType::Playlist:
return m_type_playlist_pixmap;
case GameList::EntryType::PSF:
return m_type_psf_pixmap;
case GameList::EntryType::PSExe:
default:
return m_type_exe_pixmap;
}
// TODO: Test for settings
return m_type_pixmaps[static_cast<u32>(ge->type)];
}
case Column_Region:
{
switch (ge->region)
{
case DiscRegion::NTSC_J:
return m_region_jp_pixmap;
case DiscRegion::NTSC_U:
return m_region_us_pixmap;
case DiscRegion::Other:
return m_region_other_pixmap;
case DiscRegion::PAL:
default:
return m_region_eu_pixmap;
}
return m_region_pixmaps[static_cast<u32>(ge->region)];
}
case Column_Compatibility:
{
return m_compatibiliy_pixmaps[static_cast<int>(
(ge->compatibility >= GameDatabase::CompatibilityRating::Count) ?
GameDatabase::CompatibilityRating::Unknown :
ge->compatibility)];
return m_compatibiliy_pixmaps[static_cast<u32>(ge->compatibility)];
}
case Column_Cover:
@ -514,16 +490,11 @@ bool GameListModel::lessThan(const QModelIndex& left_index, const QModelIndex& r
void GameListModel::loadCommonImages()
{
// TODO: Use svg instead of png
m_type_disc_pixmap = QIcon(QStringLiteral(":/icons/media-optical-24.png")).pixmap(QSize(24, 24));
m_type_disc_with_settings_pixmap = QIcon(QStringLiteral(":/icons/media-optical-gear-24.png")).pixmap(QSize(24, 24));
m_type_exe_pixmap = QIcon(QStringLiteral(":/icons/applications-system-24.png")).pixmap(QSize(24, 24));
m_type_playlist_pixmap = QIcon(QStringLiteral(":/icons/address-book-new-22.png")).pixmap(QSize(22, 22));
m_type_psf_pixmap = QIcon(QStringLiteral(":/icons/multimedia-player.png")).pixmap(QSize(22, 22));
m_region_eu_pixmap = QIcon(QStringLiteral(":/icons/flag-eu.png")).pixmap(QSize(42, 30));
m_region_jp_pixmap = QIcon(QStringLiteral(":/icons/flag-jp.png")).pixmap(QSize(42, 30));
m_region_us_pixmap = QIcon(QStringLiteral(":/icons/flag-uc.png")).pixmap(QSize(42, 30));
m_region_other_pixmap = QIcon(QStringLiteral(":/icons/flag-other.png")).pixmap(QSize(42, 30));
for (u32 i = 0; i < static_cast<u32>(GameList::EntryType::Count); i++)
m_type_pixmaps[i] = QtUtils::GetIconForEntryType(static_cast<GameList::EntryType>(i)).pixmap(QSize(24, 24));
for (u32 i = 0; i < static_cast<u32>(DiscRegion::Count); i++)
m_region_pixmaps[i] = QtUtils::GetIconForRegion(static_cast<DiscRegion>(i)).pixmap(42, 30);
for (int i = 0; i < static_cast<int>(GameDatabase::CompatibilityRating::Count); i++)
m_compatibiliy_pixmaps[i].load(QStringLiteral(":/icons/star-%1.png").arg(i));

View File

@ -71,17 +71,8 @@ private:
std::array<QString, Column_Count> m_column_display_names;
QPixmap m_type_disc_pixmap;
QPixmap m_type_disc_with_settings_pixmap;
QPixmap m_type_exe_pixmap;
QPixmap m_type_playlist_pixmap;
QPixmap m_type_psf_pixmap;
QPixmap m_region_jp_pixmap;
QPixmap m_region_eu_pixmap;
QPixmap m_region_us_pixmap;
QPixmap m_region_other_pixmap;
std::array<QPixmap, static_cast<int>(GameList::EntryType::Count)> m_type_pixmaps;
std::array<QPixmap, static_cast<int>(DiscRegion::Count)> m_region_pixmaps;
std::array<QPixmap, static_cast<int>(GameDatabase::CompatibilityRating::Count)> m_compatibiliy_pixmaps;
mutable UnorderedStringMap<QPixmap> m_cover_pixmap_cache;
};

View File

@ -4,7 +4,7 @@
#include "qtutils.h"
#include <QtCore/QUrl>
GameListSearchDirectoriesModel::GameListSearchDirectoriesModel(QtHostInterface* host_interface)
GameListSearchDirectoriesModel::GameListSearchDirectoriesModel(EmuThread* host_interface)
: m_host_interface(host_interface)
{
loadFromSettings();

View File

@ -3,14 +3,14 @@
#include <QtCore/QString>
#include <vector>
class QtHostInterface;
class EmuThread;
class GameListSearchDirectoriesModel : public QAbstractTableModel
{
Q_OBJECT
public:
GameListSearchDirectoriesModel(QtHostInterface* host_interface);
GameListSearchDirectoriesModel(EmuThread* host_interface);
~GameListSearchDirectoriesModel();
int columnCount(const QModelIndex& parent) const override;
@ -36,6 +36,6 @@ private:
bool recursive;
};
QtHostInterface* m_host_interface;
EmuThread* m_host_interface;
std::vector<Entry> m_entries;
};

View File

@ -89,8 +89,17 @@ void GameListWidget::initialize()
m_sort_model->setSourceModel(m_model);
m_ui.setupUi(this);
for (u32 type = 0; type < static_cast<u32>(GameList::EntryType::Count); type++)
{
m_ui.filterType->addItem(
QtUtils::GetIconForEntryType(static_cast<GameList::EntryType>(type)),
qApp->translate("GameList", GameList::GetEntryTypeDisplayName(static_cast<GameList::EntryType>(type))));
}
for (u32 region = 0; region < static_cast<u32>(DiscRegion::Count); region++)
m_ui.filterRegion->addItem(QString::fromUtf8(Settings::GetDiscRegionDisplayName(static_cast<DiscRegion>(region))));
{
m_ui.filterRegion->addItem(QtUtils::GetIconForRegion(static_cast<DiscRegion>(region)),
QString::fromUtf8(Settings::GetDiscRegionName(static_cast<DiscRegion>(region))));
}
connect(m_ui.viewGameList, &QPushButton::clicked, this, &GameListWidget::showGameList);
connect(m_ui.viewGameGrid, &QPushButton::clicked, this, &GameListWidget::showGameGrid);
@ -107,7 +116,7 @@ void GameListWidget::initialize()
[this](const QString& text) { m_sort_model->setFilterName(text); });
// Works around a strange bug where after hiding the game list, the cursor for the whole window changes to a beam..
//m_ui.searchText->setCursor(QCursor(Qt::ArrowCursor));
// m_ui.searchText->setCursor(QCursor(Qt::ArrowCursor));
m_table_view = new QTableView(m_ui.stack);
m_table_view->setModel(m_sort_model);
@ -347,7 +356,10 @@ void GameListWidget::refreshGridCovers()
void GameListWidget::showGameList()
{
if (m_ui.stack->currentIndex() == 0 || m_model->rowCount() == 0)
{
updateToolbar();
return;
}
Host::SetBaseBoolSettingValue("UI", "GameListGridView", false);
m_ui.stack->setCurrentIndex(0);
@ -359,7 +371,10 @@ void GameListWidget::showGameList()
void GameListWidget::showGameGrid()
{
if (m_ui.stack->currentIndex() == 1 || m_model->rowCount() == 0)
{
updateToolbar();
return;
}
Host::SetBaseBoolSettingValue("UI", "GameListGridView", true);
m_ui.stack->setCurrentIndex(1);
@ -370,7 +385,10 @@ void GameListWidget::showGameGrid()
void GameListWidget::setShowCoverTitles(bool enabled)
{
if (m_model->getShowCoverTitles() == enabled)
{
updateToolbar();
return;
}
Host::SetBaseBoolSettingValue("UI", "GameListShowCoverTitles", enabled);
m_model->setShowCoverTitles(enabled);

View File

@ -60,7 +60,7 @@
<string>Game List</string>
</property>
<property name="icon">
<iconset theme="list-check">
<iconset theme="GameList">
<normaloff>.</normaloff>.</iconset>
</property>
<property name="checkable">
@ -83,7 +83,7 @@
<string>Game Grid</string>
</property>
<property name="icon">
<iconset theme="function-line">
<iconset theme="GameGrid">
<normaloff>.</normaloff>.</iconset>
</property>
<property name="checkable">
@ -106,7 +106,7 @@
<string>Show Titles</string>
</property>
<property name="icon">
<iconset theme="window-2-line">
<iconset theme="RescanAllGames">
<normaloff>.</normaloff>.</iconset>
</property>
<property name="checkable">
@ -162,25 +162,10 @@
<property name="text">
<string>All Types</string>
</property>
</item>
<item>
<property name="text">
<string>PS2 Discs</string>
</property>
</item>
<item>
<property name="text">
<string>PS1 Discs</string>
</property>
</item>
<item>
<property name="text">
<string>PS2 ELFs</string>
</property>
</item>
<item>
<property name="text">
<string>Playlists</string>
<property name="icon">
<iconset>
<normalon>:/icons/system-file-manager.png</normalon>
</iconset>
</property>
</item>
</widget>
@ -191,6 +176,9 @@
<property name="text">
<string>All Regions</string>
</property>
<property name="icon">
<iconset theme="Language"/>
</property>
</item>
</widget>
</item>

View File

@ -0,0 +1,69 @@
#include "common/string_util.h"
#include "frontend-common/game_list.h"
#include "gamesummarywidget.h"
#include "qthost.h"
#include "settingsdialog.h"
GameSummaryWidget::GameSummaryWidget(const std::string& path, const std::string& serial, DiscRegion region,
const GameDatabase::Entry* entry, SettingsDialog* dialog, QWidget* parent)
: m_dialog(dialog)
{
m_ui.setupUi(this);
#if 0
const QString base_path(QtHost::GetResourcesBasePath());
for (int i = 0; i < m_ui.region->count(); i++)
{
m_ui.region->setItemIcon(i, QIcon(
QStringLiteral("%1/icons/flags/%2.png").arg(base_path).arg(GameList::RegionToString(static_cast<GameList::Region>(i)))));
}
for (int i = 1; i < m_ui.compatibility->count(); i++)
{
m_ui.compatibility->setItemIcon(i, QIcon(
QStringLiteral("%1/icons/star-%2.png").arg(base_path).arg(i)));
}
#endif
populateUi(path, serial, region, entry);
// connect(m_ui.inputProfile, &QComboBox::currentIndexChanged, this, &GameSummaryWidget::onInputProfileChanged);
}
GameSummaryWidget::~GameSummaryWidget() = default;
void GameSummaryWidget::populateUi(const std::string& path, const std::string& serial, DiscRegion region,
const GameDatabase::Entry* entry)
{
m_ui.path->setText(QString::fromStdString(path));
m_ui.serial->setText(QString::fromStdString(serial));
m_ui.region->setCurrentIndex(static_cast<int>(region));
if (entry)
{
m_ui.title->setText(QString::fromStdString(entry->title));
m_ui.compatibility->setCurrentIndex(static_cast<int>(entry->compatibility));
}
#if 0
for (const std::string& name : PAD::GetInputProfileNames())
m_ui.inputProfile->addItem(QString::fromStdString(name));
std::optional<std::string> profile(m_dialog->getStringValue("EmuCore", "InputProfileName", std::nullopt));
if (profile.has_value())
m_ui.inputProfile->setCurrentIndex(m_ui.inputProfile->findText(QString::fromStdString(profile.value())));
else
m_ui.inputProfile->setCurrentIndex(0);
#endif
}
void GameSummaryWidget::onInputProfileChanged(int index)
{
#if 0
if (index == 0)
m_dialog->setStringSettingValue("EmuCore", "InputProfileName", std::nullopt);
else
m_dialog->setStringSettingValue("EmuCore", "InputProfileName", m_ui.inputProfile->itemText(index).toUtf8());
#endif
}

View File

@ -0,0 +1,32 @@
#pragma once
#include "common/types.h"
#include <QtWidgets/QWidget>
#include "ui_gamesummarywidget.h"
enum class DiscRegion : u8;
namespace GameDatabase {
struct Entry;
}
class SettingsDialog;
class GameSummaryWidget : public QWidget
{
Q_OBJECT
public:
GameSummaryWidget(const std::string& path, const std::string& serial, DiscRegion region,
const GameDatabase::Entry* entry, SettingsDialog* dialog, QWidget* parent);
~GameSummaryWidget();
private:
void populateUi(const std::string& path, const std::string& serial, DiscRegion region,
const GameDatabase::Entry* entry);
void onInputProfileChanged(int index);
Ui::GameSummaryWidget m_ui;
SettingsDialog* m_dialog;
};

View File

@ -0,0 +1,204 @@
<?xml version="1.0" encoding="UTF-8"?>
<ui version="4.0">
<class>GameSummaryWidget</class>
<widget class="QWidget" name="GameSummaryWidget">
<property name="geometry">
<rect>
<x>0</x>
<y>0</y>
<width>692</width>
<height>562</height>
</rect>
</property>
<property name="windowTitle">
<string>Dialog</string>
</property>
<property name="windowIcon">
<iconset>
<normaloff>:/icons/duck.png</normaloff>:/icons/duck.png</iconset>
</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="8" column="0" colspan="2">
<widget class="QLabel" name="label_8">
<property name="text">
<string>Tracks:</string>
</property>
</widget>
</item>
<item row="7" column="0">
<widget class="QLabel" name="label_5">
<property name="text">
<string>Comments:</string>
</property>
</widget>
</item>
<item row="3" column="1">
<widget class="QLineEdit" name="revision">
<property name="readOnly">
<bool>true</bool>
</property>
<property name="placeholderText">
<string/>
</property>
</widget>
</item>
<item row="2" column="1">
<widget class="QLineEdit" name="title">
<property name="readOnly">
<bool>true</bool>
</property>
</widget>
</item>
<item row="9" column="0" colspan="2">
<widget class="QTableWidget" name="tracks">
<property name="editTriggers">
<set>QAbstractItemView::NoEditTriggers</set>
</property>
<property name="cornerButtonEnabled">
<bool>false</bool>
</property>
<attribute name="verticalHeaderVisible">
<bool>false</bool>
</attribute>
<column>
<property name="text">
<string>#</string>
</property>
</column>
<column>
<property name="text">
<string>Mode</string>
</property>
</column>
<column>
<property name="text">
<string>Start</string>
</property>
</column>
<column>
<property name="text">
<string>Length</string>
</property>
</column>
<column>
<property name="text">
<string>Hash</string>
</property>
</column>
<column>
<property name="text">
<string>Status</string>
</property>
</column>
</widget>
</item>
<item row="6" column="1">
<widget class="QLineEdit" name="upscalingIssues"/>
</item>
<item row="4" column="1">
<widget class="QComboBox" name="region">
<property name="enabled">
<bool>false</bool>
</property>
</widget>
</item>
<item row="4" column="0">
<widget class="QLabel" name="label_3">
<property name="text">
<string>Region:</string>
</property>
</widget>
</item>
<item row="0" column="0">
<widget class="QLabel" name="label_4">
<property name="text">
<string>Image Path:</string>
</property>
</widget>
</item>
<item row="5" column="0">
<widget class="QLabel" name="label_7">
<property name="text">
<string>Compatibility:</string>
</property>
</widget>
</item>
<item row="2" column="0">
<widget class="QLabel" name="label_2">
<property name="text">
<string>Title:</string>
</property>
</widget>
</item>
<item row="3" column="0">
<widget class="QLabel" name="label_36">
<property name="text">
<string>Revision:</string>
</property>
</widget>
</item>
<item row="6" column="0">
<widget class="QLabel" name="label_6">
<property name="text">
<string>Upscaling Issues:</string>
</property>
</widget>
</item>
<item row="5" column="1">
<layout class="QHBoxLayout" name="horizontalLayout">
<item>
<widget class="QComboBox" name="compatibility"/>
</item>
<item>
<widget class="QLineEdit" name="versionTested"/>
</item>
<item>
<widget class="QPushButton" name="editCompatibility">
<property name="text">
<string>Edit...</string>
</property>
</widget>
</item>
</layout>
</item>
<item row="1" column="0">
<widget class="QLabel" name="label">
<property name="text">
<string>Game Serial:</string>
</property>
</widget>
</item>
<item row="0" column="1">
<widget class="QLineEdit" name="path">
<property name="readOnly">
<bool>true</bool>
</property>
</widget>
</item>
<item row="1" column="1">
<widget class="QLineEdit" name="serial">
<property name="readOnly">
<bool>true</bool>
</property>
</widget>
</item>
<item row="7" column="1">
<widget class="QLineEdit" name="comments"/>
</item>
</layout>
</widget>
<resources/>
<connections/>
</ui>

View File

@ -13,7 +13,7 @@ GDBConnection::GDBConnection(QObject* parent, int descriptor) : QThread(parent),
if (m_socket.setSocketDescriptor(m_descriptor))
{
g_emu_thread->pauseSystem(true, true);
g_emu_thread->setSystemPaused(true, true);
}
else
{
@ -42,13 +42,13 @@ void GDBConnection::receivedData()
if (GDBProtocol::IsPacketInterrupt(m_readBuffer))
{
Log_DebugPrintf("(%u) > Interrupt request", m_descriptor);
g_emu_thread->pauseSystem(true, true);
g_emu_thread->setSystemPaused(true, true);
m_readBuffer.erase();
}
else if (GDBProtocol::IsPacketContinue(m_readBuffer))
{
Log_DebugPrintf("(%u) > Continue request", m_descriptor);
g_emu_thread->pauseSystem(false, false);
g_emu_thread->setSystemPaused(false, false);
m_readBuffer.erase();
}
else if (GDBProtocol::IsPacketComplete(m_readBuffer))

View File

@ -29,8 +29,8 @@ void GDBServer::incomingConnection(qintptr descriptor)
{
Log_InfoPrint("Accepted connection on GDB server");
GDBConnection *thread = new GDBConnection(this, descriptor);
connect(g_emu_thread, &QtHostInterface::systemPaused, thread, &GDBConnection::onEmulationPaused);
connect(g_emu_thread, &QtHostInterface::systemResumed, thread, &GDBConnection::onEmulationResumed);
connect(g_emu_thread, &EmuThread::systemPaused, thread, &GDBConnection::onEmulationPaused);
connect(g_emu_thread, &EmuThread::systemResumed, thread, &GDBConnection::onEmulationResumed);
thread->start();
m_connections.push_back(thread);
}

View File

@ -13,20 +13,30 @@ GeneralSettingsWidget::GeneralSettingsWidget(SettingsDialog* dialog, QWidget* pa
m_ui.setupUi(this);
SettingWidgetBinder::BindWidgetToBoolSetting(sif, m_ui.pauseOnStart, "Main", "StartPaused", false);
SettingWidgetBinder::BindWidgetToBoolSetting(sif, m_ui.pauseOnFocusLoss, "Main", "PauseOnFocusLoss", false);
SettingWidgetBinder::BindWidgetToBoolSetting(sif, m_ui.startFullscreen, "Main", "StartFullscreen", false);
SettingWidgetBinder::BindWidgetToBoolSetting(sif, m_ui.hideCursorInFullscreen, "Main", "HideCursorInFullscreen",
true);
SettingWidgetBinder::BindWidgetToBoolSetting(sif, m_ui.inhibitScreensaver, "Main", "InhibitScreensaver", true);
SettingWidgetBinder::BindWidgetToBoolSetting(sif, m_ui.renderToMain, "Main", "RenderToMainWindow", true);
SettingWidgetBinder::BindWidgetToBoolSetting(sif, m_ui.pauseOnFocusLoss, "Main", "PauseOnFocusLoss", false);
SettingWidgetBinder::BindWidgetToBoolSetting(sif, m_ui.pauseOnStart, "Main", "StartPaused", false);
SettingWidgetBinder::BindWidgetToBoolSetting(sif, m_ui.saveStateOnExit, "Main", "SaveStateOnExit", true);
SettingWidgetBinder::BindWidgetToBoolSetting(sif, m_ui.confirmPowerOff, "Main", "ConfirmPowerOff", true);
SettingWidgetBinder::BindWidgetToBoolSetting(sif, m_ui.loadDevicesFromSaveStates, "Main", "LoadDevicesFromSaveStates",
false);
SettingWidgetBinder::BindWidgetToBoolSetting(sif, m_ui.applyGameSettings, "Main", "ApplyGameSettings", true);
SettingWidgetBinder::BindWidgetToBoolSetting(sif, m_ui.autoLoadCheats, "Main", "AutoLoadCheats", true);
SettingWidgetBinder::BindWidgetToBoolSetting(sif, m_ui.enableFullscreenUI, "Main", "EnableFullscreenUI", false);
SettingWidgetBinder::BindWidgetToBoolSetting(sif, m_ui.startFullscreen, "Main", "StartFullscreen", false);
SettingWidgetBinder::BindWidgetToBoolSetting(sif, m_ui.doubleClickTogglesFullscreen, "Main",
"DoubleClickTogglesFullscreen", true);
SettingWidgetBinder::BindWidgetToBoolSetting(sif, m_ui.renderToSeparateWindow, "Main", "RenderToSeparateWindow",
false);
SettingWidgetBinder::BindWidgetToBoolSetting(sif, m_ui.hideMainWindow, "Main", "HideMainWindowWhenRunning", false);
SettingWidgetBinder::BindWidgetToBoolSetting(sif, m_ui.disableWindowResizing, "Main", "DisableWindowResize", false);
SettingWidgetBinder::BindWidgetToBoolSetting(sif, m_ui.hideMouseCursor, "Main", "HideCursorInFullscreen", true);
SettingWidgetBinder::BindWidgetToBoolSetting(sif, m_ui.createSaveStateBackups, "Main", "CreateSaveStateBackups",
false);
connect(m_ui.renderToSeparateWindow, &QCheckBox::stateChanged, this,
&GeneralSettingsWidget::onRenderToSeparateWindowChanged);
onRenderToSeparateWindowChanged();
dialog->registerWidgetHelp(
m_ui.confirmPowerOff, tr("Confirm Power Off"), tr("Checked"),
@ -37,15 +47,15 @@ GeneralSettingsWidget::GeneralSettingsWidget(SettingsDialog* dialog, QWidget* pa
"resume directly from where you left off next time."));
dialog->registerWidgetHelp(m_ui.startFullscreen, tr("Start Fullscreen"), tr("Unchecked"),
tr("Automatically switches to fullscreen mode when a game is started."));
dialog->registerWidgetHelp(m_ui.hideCursorInFullscreen, tr("Hide Cursor In Fullscreen"), tr("Checked"),
dialog->registerWidgetHelp(m_ui.hideMouseCursor, tr("Hide Cursor In Fullscreen"), tr("Checked"),
tr("Hides the mouse pointer/cursor when the emulator is in fullscreen mode."));
dialog->registerWidgetHelp(
m_ui.inhibitScreensaver, tr("Inhibit Screensaver"), tr("Checked"),
tr("Prevents the screen saver from activating and the host from sleeping while emulation is running."));
dialog->registerWidgetHelp(
m_ui.renderToMain, tr("Render To Main Window"), tr("Checked"),
m_ui.renderToSeparateWindow, tr("Render To Separate Window"), tr("Checked"),
tr("Renders the display of the simulated console to the main window of the application, over "
"the game list. If unchecked, the display will render in a separate window."));
"the game list. If checked, the display will render in a separate window."));
dialog->registerWidgetHelp(m_ui.pauseOnStart, tr("Pause On Start"), tr("Unchecked"),
tr("Pauses the emulator when a game is started."));
dialog->registerWidgetHelp(m_ui.pauseOnFocusLoss, tr("Pause On Focus Loss"), tr("Unchecked"),
@ -62,23 +72,17 @@ GeneralSettingsWidget::GeneralSettingsWidget(SettingsDialog* dialog, QWidget* pa
"leave this option enabled except when testing enhancements with incompatible games."));
dialog->registerWidgetHelp(m_ui.autoLoadCheats, tr("Automatically Load Cheats"), tr("Unchecked"),
tr("Automatically loads and applies cheats on game start."));
dialog->registerWidgetHelp(
m_ui.enableFullscreenUI, tr("Enable Fullscreen UI"), tr("Unchecked"),
tr("Enables the fullscreen UI mode, suitable for controller operation which is used in the NoGUI frontend."));
// Since this one is compile-time selected, we don't put it in the .ui file.
int current_col = 0;
int current_row = m_ui.formLayout_4->rowCount() - current_col;
#ifdef WITH_DISCORD_PRESENCE
{
QCheckBox* enableDiscordPresence = new QCheckBox(tr("Enable Discord Presence"), m_ui.groupBox_4);
SettingWidgetBinder::BindWidgetToBoolSetting(sif, enableDiscordPresence, "Main", "EnableDiscordPresence", false);
m_ui.formLayout_4->addWidget(enableDiscordPresence, current_row, current_col);
dialog->registerWidgetHelp(enableDiscordPresence, tr("Enable Discord Presence"), tr("Unchecked"),
SettingWidgetBinder::BindWidgetToBoolSetting(sif, m_ui.enableDiscordPresence, "Main", "EnableDiscordPresence",
false);
dialog->registerWidgetHelp(m_ui.enableDiscordPresence, tr("Enable Discord Presence"), tr("Unchecked"),
tr("Shows the game you are currently playing as part of your profile in Discord."));
current_col++;
current_row += (current_col / 2);
current_col %= 2;
}
#else
{
m_ui.enableDiscordPresence->setEnabled(false);
}
#endif
if (AutoUpdaterDialog::isSupported())
@ -94,9 +98,6 @@ GeneralSettingsWidget::GeneralSettingsWidget(SettingsDialog* dialog, QWidget* pa
m_ui.autoUpdateCurrentVersion->setText(tr("%1 (%2)").arg(g_scm_tag_str).arg(g_scm_date_str));
connect(m_ui.checkForUpdates, &QPushButton::clicked, [this]() { g_main_window->checkForUpdates(true); });
current_col++;
current_row += (current_col / 2);
current_col %= 2;
}
else
{
@ -106,3 +107,8 @@ GeneralSettingsWidget::GeneralSettingsWidget(SettingsDialog* dialog, QWidget* pa
}
GeneralSettingsWidget::~GeneralSettingsWidget() = default;
void GeneralSettingsWidget::onRenderToSeparateWindowChanged()
{
m_ui.hideMainWindow->setEnabled(m_ui.renderToSeparateWindow->isChecked());
}

View File

@ -14,6 +14,9 @@ public:
explicit GeneralSettingsWidget(SettingsDialog* dialog, QWidget* parent);
~GeneralSettingsWidget();
private Q_SLOTS:
void onRenderToSeparateWindowChanged();
private:
Ui::GeneralSettingsWidget m_ui;

View File

@ -32,52 +32,17 @@
<string>Behaviour</string>
</property>
<layout class="QGridLayout" name="formLayout_4">
<item row="1" column="0">
<widget class="QCheckBox" name="confirmPowerOff">
<item row="6" column="1">
<widget class="QCheckBox" name="autoLoadCheats">
<property name="text">
<string>Confirm Power Off</string>
<string>Automatically Load Cheats</string>
</property>
</widget>
</item>
<item row="3" column="1">
<widget class="QCheckBox" name="inhibitScreensaver">
<property name="text">
<string>Inhibit Screensaver</string>
</property>
</widget>
</item>
<item row="4" column="0">
<widget class="QCheckBox" name="renderToMain">
<property name="text">
<string>Render To Main Window</string>
</property>
</widget>
</item>
<item row="0" column="0">
<widget class="QCheckBox" name="pauseOnStart">
<property name="text">
<string>Pause On Start</string>
</property>
</widget>
</item>
<item row="0" column="1">
<widget class="QCheckBox" name="pauseOnFocusLoss">
<property name="text">
<string>Pause On Focus Loss</string>
</property>
</widget>
</item>
<item row="2" column="0">
<widget class="QCheckBox" name="startFullscreen">
<property name="text">
<string>Start Fullscreen</string>
</property>
</widget>
</item>
<item row="1" column="1">
<widget class="QCheckBox" name="saveStateOnExit">
<property name="text">
<string>Save State On Exit</string>
<string>Save State On Shutdown</string>
</property>
</widget>
</item>
@ -88,10 +53,10 @@
</property>
</widget>
</item>
<item row="6" column="1">
<widget class="QCheckBox" name="autoLoadCheats">
<item row="0" column="0">
<widget class="QCheckBox" name="inhibitScreensaver">
<property name="text">
<string>Automatically Load Cheats</string>
<string>Inhibit Screensaver</string>
</property>
</widget>
</item>
@ -102,17 +67,38 @@
</property>
</widget>
</item>
<item row="2" column="1">
<widget class="QCheckBox" name="hideCursorInFullscreen">
<item row="4" column="0">
<widget class="QCheckBox" name="confirmPowerOff">
<property name="text">
<string>Hide Cursor In Fullscreen</string>
<string>Confirm Power Off</string>
</property>
</widget>
</item>
<item row="0" column="1">
<widget class="QCheckBox" name="pauseOnFocusLoss">
<property name="text">
<string>Pause On Focus Loss</string>
</property>
</widget>
</item>
<item row="3" column="0">
<widget class="QCheckBox" name="enableFullscreenUI">
<widget class="QCheckBox" name="pauseOnStart">
<property name="text">
<string>Enable Fullscreen UI</string>
<string>Pause On Start</string>
</property>
</widget>
</item>
<item row="7" column="0">
<widget class="QCheckBox" name="createSaveStateBackups">
<property name="text">
<string>Create Save State Backups</string>
</property>
</widget>
</item>
<item row="7" column="1">
<widget class="QCheckBox" name="enableDiscordPresence">
<property name="text">
<string>Enable Discord Presence</string>
</property>
</widget>
</item>
@ -120,24 +106,52 @@
</widget>
</item>
<item>
<widget class="QGroupBox" name="groupBox">
<widget class="QGroupBox" name="groupBox_2">
<property name="title">
<string>Miscellaneous</string>
<string>Game Display</string>
</property>
<layout class="QGridLayout" name="gridLayout">
<item row="0" column="0">
<layout class="QHBoxLayout" name="horizontalLayout">
<item>
<widget class="QLabel" name="label">
<property name="text">
<string>Controller Backend:</string>
</property>
</widget>
</item>
<item>
<widget class="QComboBox" name="controllerBackend"/>
</item>
</layout>
<widget class="QCheckBox" name="startFullscreen">
<property name="text">
<string>Start Fullscreen</string>
</property>
</widget>
</item>
<item row="0" column="1">
<widget class="QCheckBox" name="doubleClickTogglesFullscreen">
<property name="text">
<string>Double-Click Toggles Fullscreen</string>
</property>
</widget>
</item>
<item row="1" column="0">
<widget class="QCheckBox" name="renderToSeparateWindow">
<property name="text">
<string>Render To Separate Window</string>
</property>
</widget>
</item>
<item row="1" column="1">
<widget class="QCheckBox" name="hideMainWindow">
<property name="text">
<string>Hide Main Window When Running</string>
</property>
</widget>
</item>
<item row="2" column="0">
<widget class="QCheckBox" name="disableWindowResizing">
<property name="text">
<string>Disable Window Resizing</string>
</property>
</widget>
</item>
<item row="2" column="1">
<widget class="QCheckBox" name="hideMouseCursor">
<property name="text">
<string>Hide Cursor In Fullscreen</string>
</property>
</widget>
</item>
</layout>
</widget>

View File

@ -1,152 +0,0 @@
#include "common/crash_handler.h"
#include "mainwindow.h"
#include "qthost.h"
#include "qtutils.h"
#include <QtWidgets/QApplication>
#include <QtWidgets/QMessageBox>
#include <csignal>
#include <cstdio>
#include <cstdlib>
#include <memory>
static bool ParseCommandLineParameters(QApplication& app, QtHostInterface* host_interface,
std::unique_ptr<SystemBootParameters>* boot_params)
{
const QStringList args(app.arguments());
std::vector<std::string> converted_args;
std::vector<char*> converted_argv;
converted_args.reserve(args.size());
converted_argv.reserve(args.size());
for (const QString& arg : args)
converted_args.push_back(arg.toStdString());
for (std::string& arg : converted_args)
converted_argv.push_back(arg.data());
return CommonHost::ParseCommandLineParameters(args.size(), converted_argv.data(), boot_params);
}
static void SignalHandler(int signal)
{
// First try the normal (graceful) shutdown/exit.
static bool graceful_shutdown_attempted = false;
if (!graceful_shutdown_attempted)
{
std::fprintf(stderr, "Received CTRL+C, attempting graceful shutdown. Press CTRL+C again to force.\n");
graceful_shutdown_attempted = true;
g_emu_thread->requestExit();
return;
}
std::signal(signal, SIG_DFL);
// MacOS is missing std::quick_exit() despite it being C++11...
#ifndef __APPLE__
std::quick_exit(1);
#else
_Exit(1);
#endif
}
static void HookSignals()
{
std::signal(SIGINT, SignalHandler);
std::signal(SIGTERM, SignalHandler);
}
int main(int argc, char* argv[])
{
CrashHandler::Install();
// Register any standard types we need elsewhere
qRegisterMetaType<std::optional<bool>>();
qRegisterMetaType<std::function<void()>>();
QApplication app(argc, argv);
// TODO: Remove me
Log::SetFilterLevel(LOGLEVEL_DEBUG);
Log::SetConsoleOutputParams(true, nullptr, LOGLEVEL_DEBUG);
std::unique_ptr<QtHostInterface> host_interface = std::make_unique<QtHostInterface>();
std::unique_ptr<SystemBootParameters> boot_params;
if (!ParseCommandLineParameters(app, host_interface.get(), &boot_params))
return EXIT_FAILURE;
MainWindow* window = new MainWindow();
if (!host_interface->Initialize())
{
host_interface->Shutdown();
QMessageBox::critical(nullptr, QObject::tr("DuckStation Error"),
QObject::tr("Failed to initialize host interface. Cannot continue."), QMessageBox::Ok);
return EXIT_FAILURE;
}
window->initializeAndShow();
HookSignals();
// When running in batch mode, ensure game list is loaded, but don't scan for any new files.
if (!QtHost::InBatchMode())
window->refreshGameList(false);
else
GameList::Refresh(false, true);
if (boot_params)
{
host_interface->bootSystem(std::move(boot_params));
}
else
{
window->startupUpdateCheck();
}
int result = app.exec();
host_interface->Shutdown();
return result;
}
#ifdef _WIN32
// Apparently Qt6 got rid of this?
#include "common/windows_headers.h"
#include <shellapi.h>
/*
WinMain() - Initializes Windows and calls user's startup function main().
NOTE: WinMain() won't be called if the application was linked as a "console"
application.
*/
// Convert a wchar_t to char string, equivalent to QString::toLocal8Bit()
// when passed CP_ACP.
static inline char* wideToMulti(unsigned int codePage, const wchar_t* aw)
{
const int required = WideCharToMultiByte(codePage, 0, aw, -1, nullptr, 0, nullptr, nullptr);
char* result = new char[required];
WideCharToMultiByte(codePage, 0, aw, -1, result, required, nullptr, nullptr);
return result;
}
extern "C" int APIENTRY WinMain(HINSTANCE, HINSTANCE, LPSTR /*cmdParamarg*/, int /* cmdShow */)
{
int argc = 0;
wchar_t** argvW = CommandLineToArgvW(GetCommandLineW(), &argc);
if (argvW == nullptr)
return -1;
char** argv = new char* [argc + 1];
for (int i = 0; i != argc; ++i)
argv[i] = wideToMulti(CP_ACP, argvW[i]);
argv[argc] = nullptr;
LocalFree(argvW);
const int exitCode = main(argc, argv);
for (int i = 0; (i != argc) && (argv[i] != nullptr); ++i)
delete[] argv[i];
delete[] argv;
return exitCode;
}
#endif

View File

@ -7,6 +7,7 @@
#include "common/log.h"
#include "core/host.h"
#include "core/host_display.h"
#include "core/memory_card.h"
#include "core/settings.h"
#include "core/system.h"
#include "debuggerwindow.h"
@ -90,7 +91,7 @@ MainWindow::~MainWindow()
g_main_window = nullptr;
}
void MainWindow::initializeAndShow()
void MainWindow::initialize()
{
setIconThemeFromSettings();
@ -102,8 +103,6 @@ void MainWindow::initializeAndShow()
restoreStateFromConfig();
switchToGameListView();
show();
#ifdef WITH_RAINTEGRATION
if (Cheevos::IsUsingRAIntegration())
Cheevos::RAIntegration::MainWindowChanged((void*)winId());
@ -113,15 +112,13 @@ void MainWindow::initializeAndShow()
void MainWindow::reportError(const QString& title, const QString& message)
{
QMessageBox::critical(this, title, message, QMessageBox::Ok);
focusDisplayWidget();
}
bool MainWindow::confirmMessage(const QString& title, const QString& message)
{
const int result = QMessageBox::question(this, title, message);
focusDisplayWidget();
SystemLock lock(pauseAndLockSystem());
return (result == QMessageBox::Yes);
return (QMessageBox::question(this, title, message) == QMessageBox::Yes);
}
bool MainWindow::shouldHideCursorInFullscreen() const
@ -611,7 +608,7 @@ void MainWindow::onApplicationStateChanged(Qt::ApplicationState state)
{
if (!m_was_paused_by_focus_loss && !s_system_paused)
{
g_emu_thread->pauseSystem(true);
g_emu_thread->setSystemPaused(true);
m_was_paused_by_focus_loss = true;
updateMouseMode(true);
}
@ -621,7 +618,7 @@ void MainWindow::onApplicationStateChanged(Qt::ApplicationState state)
if (m_was_paused_by_focus_loss)
{
if (s_system_paused)
g_emu_thread->pauseSystem(false);
g_emu_thread->setSystemPaused(false);
m_was_paused_by_focus_loss = false;
updateMouseMode(false);
}
@ -683,25 +680,131 @@ std::string MainWindow::getDeviceDiscPath(const QString& title)
void MainWindow::recreate()
{
if (s_system_valid)
g_emu_thread->synchronousPowerOffSystem();
requestShutdown(false, true, true);
close();
g_main_window = nullptr;
MainWindow* new_main_window = new MainWindow();
new_main_window->initializeAndShow();
new_main_window->initialize();
new_main_window->refreshGameList(false);
new_main_window->show();
deleteLater();
}
std::optional<bool> MainWindow::promptForResumeState(const QString& save_state_path)
void MainWindow::populateGameListContextMenu(const GameList::Entry* entry, QWidget* parent_window, QMenu* menu)
{
if (save_state_path.isEmpty())
return false;
QAction* resume_action = menu->addAction(tr("Resume"));
resume_action->setEnabled(false);
QFileInfo fi(save_state_path);
if (!fi.exists())
QMenu* load_state_menu = menu->addMenu(tr("Load State"));
load_state_menu->setEnabled(false);
if (!entry->serial.empty())
{
std::vector<SaveStateInfo> available_states(System::GetAvailableSaveStates(entry->serial.c_str()));
const QString timestamp_format = QLocale::system().dateTimeFormat(QLocale::ShortFormat);
const bool challenge_mode = Cheevos::IsChallengeModeActive();
for (SaveStateInfo& ssi : available_states)
{
if (ssi.global)
continue;
const s32 slot = ssi.slot;
const QDateTime timestamp(QDateTime::fromSecsSinceEpoch(static_cast<qint64>(ssi.timestamp)));
const QString timestamp_str(timestamp.toString(timestamp_format));
QAction* action;
if (slot < 0)
{
resume_action->setText(tr("Resume (%1)").arg(timestamp_str));
resume_action->setEnabled(!challenge_mode);
action = resume_action;
}
else
{
load_state_menu->setEnabled(true);
action = load_state_menu->addAction(tr("Game Save %1 (%2)").arg(slot).arg(timestamp_str));
}
action->setDisabled(challenge_mode);
connect(action, &QAction::triggered, [this, entry, path = std::move(ssi.path)]() {
startGameListEntry(entry, std::move(path), std::nullopt);
});
}
}
QAction* open_memory_cards_action = menu->addAction(tr("Edit Memory Cards..."));
connect(open_memory_cards_action, &QAction::triggered, [this, entry]() {
QString paths[2];
for (u32 i = 0; i < 2; i++)
{
MemoryCardType type = g_settings.memory_card_types[i];
if (entry->serial.empty() && type == MemoryCardType::PerGame)
type = MemoryCardType::Shared;
switch (type)
{
case MemoryCardType::None:
continue;
case MemoryCardType::Shared:
if (g_settings.memory_card_paths[i].empty())
{
paths[i] = QString::fromStdString(g_settings.GetSharedMemoryCardPath(i));
}
else
{
QFileInfo path(QString::fromStdString(g_settings.memory_card_paths[i]));
path.makeAbsolute();
paths[i] = QDir::toNativeSeparators(path.canonicalFilePath());
}
break;
case MemoryCardType::PerGame:
paths[i] = QString::fromStdString(g_settings.GetGameMemoryCardPath(entry->serial.c_str(), i));
break;
case MemoryCardType::PerGameTitle:
paths[i] = QString::fromStdString(
g_settings.GetGameMemoryCardPath(MemoryCard::SanitizeGameTitleForFileName(entry->title).c_str(), i));
break;
case MemoryCardType::PerGameFileTitle:
{
const std::string display_name(FileSystem::GetDisplayNameFromPath(entry->path));
paths[i] = QString::fromStdString(g_settings.GetGameMemoryCardPath(
MemoryCard::SanitizeGameTitleForFileName(Path::GetFileTitle(display_name)).c_str(), i));
}
break;
default:
break;
}
}
g_main_window->openMemoryCardEditor(paths[0], paths[1]);
});
const bool has_any_states = resume_action->isEnabled() || load_state_menu->isEnabled();
QAction* delete_save_states_action = menu->addAction(tr("Delete Save States..."));
delete_save_states_action->setEnabled(has_any_states);
if (has_any_states)
{
connect(delete_save_states_action, &QAction::triggered, [this, parent_window, entry] {
if (QMessageBox::warning(
parent_window, tr("Confirm Save State Deletion"),
tr("Are you sure you want to delete all save states for %1?\n\nThe saves will not be recoverable.")
.arg(QString::fromStdString(entry->serial)),
QMessageBox::Yes, QMessageBox::No) != QMessageBox::Yes)
{
return;
}
System::DeleteSaveStates(entry->serial.c_str(), true);
});
}
}
std::optional<bool> MainWindow::promptForResumeState(const std::string& save_state_path)
{
FILESYSTEM_STAT_DATA sd;
if (save_state_path.empty() || !FileSystem::StatFile(save_state_path.c_str(), &sd))
return false;
QMessageBox msgbox(this);
@ -709,7 +812,7 @@ std::optional<bool> MainWindow::promptForResumeState(const QString& save_state_p
msgbox.setWindowTitle(tr("Load Resume State"));
msgbox.setText(tr("A resume save state was found for this game, saved at:\n\n%1.\n\nDo you want to load this state, "
"or start from a fresh boot?")
.arg(fi.lastModified().toLocalTime().toString()));
.arg(QDateTime::fromSecsSinceEpoch(sd.ModificationTime, Qt::UTC).toLocalTime().toString()));
QPushButton* load = msgbox.addButton(tr("Load State"), QMessageBox::AcceptRole);
QPushButton* boot = msgbox.addButton(tr("Fresh Boot"), QMessageBox::RejectRole);
@ -729,8 +832,11 @@ std::optional<bool> MainWindow::promptForResumeState(const QString& save_state_p
}
else if (delboot == clicked)
{
if (!QFile::remove(save_state_path))
QMessageBox::critical(this, tr("Error"), tr("Failed to delete save state file '%1'.").arg(save_state_path));
if (!FileSystem::DeleteFile(save_state_path.c_str()))
{
QMessageBox::critical(this, tr("Error"),
tr("Failed to delete save state file '%1'.").arg(QString::fromStdString(save_state_path)));
}
return false;
}
@ -738,24 +844,14 @@ std::optional<bool> MainWindow::promptForResumeState(const QString& save_state_p
return std::nullopt;
}
void MainWindow::startGameListEntry(const GameList::Entry* entry, std::optional<s32> save_slot,
void MainWindow::startGameListEntry(const GameList::Entry* entry, std::optional<std::string> save_path,
std::optional<bool> fast_boot)
{
std::shared_ptr<SystemBootParameters> params = std::make_shared<SystemBootParameters>();
params->filename = entry->path;
params->override_fast_boot = fast_boot;
if (save_slot.has_value() && !entry->serial.empty())
{
std::string state_filename = System::GetGameSaveStateFileName(entry->serial.c_str(), save_slot.value());
if (!FileSystem::FileExists(state_filename.c_str()))
{
QMessageBox::critical(this, tr("Error"), tr("This save state does not exist."));
return;
}
params->save_state = std::move(state_filename);
}
if (save_path.has_value())
params->save_state = std::move(save_path.value());
g_emu_thread->bootSystem(std::move(params));
}
@ -786,7 +882,7 @@ void MainWindow::onChangeDiscFromFileActionTriggered()
void MainWindow::onChangeDiscFromGameListActionTriggered()
{
g_emu_thread->pauseSystem(true);
g_emu_thread->setSystemPaused(true);
switchToGameListView();
}
@ -880,22 +976,11 @@ void MainWindow::onViewGamePropertiesActionTriggered()
return;
const std::string& path = System::GetRunningPath();
if (path.empty())
const std::string& serial = System::GetRunningCode();
if (path.empty() || serial.empty())
return;
#if 0
const GameListEntry* entry = m_host_interface->getGameList()->GetEntryForPath(path.c_str());
if (!entry)
{
QMessageBox::critical(this, tr("DuckStation"),
tr("Could not find a game list entry for the currently running file. Please make sure this "
"file is in a location scanned by the game list."));
return;
}
GamePropertiesDialog::showForEntry(m_host_interface, entry, this);
#endif
Panic("FIXME");
SettingsDialog::openGamePropertiesDialog(path, serial, System::GetDiscRegion());
}
void MainWindow::onGitHubRepositoryActionTriggered()
@ -967,20 +1052,22 @@ void MainWindow::onGameListEntryActivated()
// we might still be saving a resume state...
// VMManager::WaitForSaveStateFlush();
std::optional<bool> resume = true;
std::optional<std::string> save_path;
if (!entry->serial.empty())
{
resume =
promptForResumeState(QString::fromStdString(System::GetGameSaveStateFileName(entry->serial.c_str(), -1)));
std::string resume_path(System::GetGameSaveStateFileName(entry->serial.c_str(), -1));
std::optional<bool> resume = promptForResumeState(resume_path);
if (!resume.has_value())
{
// cancelled
return;
}
else if (resume.value())
save_path = std::move(resume_path);
}
// only resume if the option is enabled, and we have one for this game
startGameListEntry(entry, resume.value() ? std::optional<s32>(-1) : std::optional<s32>(), std::nullopt);
startGameListEntry(entry, std::move(save_path), std::nullopt);
}
void MainWindow::onGameListEntryContextMenuRequested(const QPoint& point)
@ -995,7 +1082,7 @@ void MainWindow::onGameListEntryContextMenuRequested(const QPoint& point)
{
QAction* action = menu.addAction(tr("Properties..."));
connect(action, &QAction::triggered,
[this, entry]() { /*GamePropertiesDialog::showForEntry(m_host_interface, entry, this);*/ });
[this, entry]() { SettingsDialog::openGamePropertiesDialog(entry->path, entry->serial, entry->region); });
connect(menu.addAction(tr("Open Containing Directory...")), &QAction::triggered, [this, entry]() {
const QFileInfo fi(QString::fromStdString(entry->path));
@ -1009,7 +1096,7 @@ void MainWindow::onGameListEntryContextMenuRequested(const QPoint& point)
if (!s_system_valid)
{
g_emu_thread->populateGameListContextMenu(entry, this, &menu);
populateGameListContextMenu(entry, this, &menu);
menu.addSeparator();
connect(menu.addAction(tr("Default Boot")), &QAction::triggered,
@ -1042,7 +1129,7 @@ void MainWindow::onGameListEntryContextMenuRequested(const QPoint& point)
{
connect(menu.addAction(tr("Change Disc")), &QAction::triggered, [this, entry]() {
g_emu_thread->changeDisc(QString::fromStdString(entry->path));
g_emu_thread->pauseSystem(false);
g_emu_thread->setSystemPaused(false);
switchToEmulationView();
});
}
@ -1194,7 +1281,7 @@ void MainWindow::setupAdditionalUi()
const QString current_language(QString::fromStdString(Host::GetBaseStringSettingValue("Main", "Language", "")));
QActionGroup* language_group = new QActionGroup(m_ui.menuSettingsLanguage);
for (const std::pair<QString, QString>& it : g_emu_thread->getAvailableLanguageList())
for (const std::pair<QString, QString>& it : QtHost::GetAvailableLanguageList())
{
QAction* action = language_group->addAction(it.first);
action->setCheckable(true);
@ -1215,7 +1302,7 @@ void MainWindow::setupAdditionalUi()
connect(action, &QAction::triggered, [this, action]() {
const QString new_language = action->data().toString();
Host::SetBaseStringSettingValue("Main", "Language", new_language.toUtf8().constData());
g_emu_thread->reinstallTranslator();
QtHost::ReinstallTranslator();
recreate();
});
}
@ -1223,7 +1310,7 @@ void MainWindow::setupAdditionalUi()
for (u32 scale = 1; scale <= 10; scale++)
{
QAction* action = m_ui.menuWindowSize->addAction(tr("%1x Scale").arg(scale));
connect(action, &QAction::triggered, [scale]() { g_emu_thread->requestRenderWindowScale(scale); });
connect(action, &QAction::triggered, [scale]() { g_emu_thread->requestDisplaySize(scale); });
}
#ifdef WITH_RAINTEGRATION
@ -1243,9 +1330,8 @@ void MainWindow::setupAdditionalUi()
}
QAction* raAction = raMenu->addAction(QString::fromUtf8(title));
connect(raAction, &QAction::triggered, this, [id]() {
g_emu_thread->executeOnEmulationThread([id]() { Cheevos::RAIntegration::ActivateMenuItem(id); });
});
connect(raAction, &QAction::triggered, this,
[id]() { Host::RunOnCPUThread([id]() { Cheevos::RAIntegration::ActivateMenuItem(id); }); });
}
});
m_ui.menuDebug->insertMenu(m_ui.menuCPUExecutionMode->menuAction(), raMenu);
@ -1406,7 +1492,7 @@ void MainWindow::updateWindowState(bool force_visible)
return;
const bool hide_window = !isRenderingToMain() && shouldHideMainWindow();
const bool disable_resize = Host::GetBaseBoolSettingValue("UI", "DisableWindowResize", false);
const bool disable_resize = Host::GetBaseBoolSettingValue("Main", "DisableWindowResize", false);
const bool has_window = s_system_valid || m_display_widget;
// Need to test both valid and display widget because of startup (vm invalid while window is created).
@ -1467,12 +1553,12 @@ bool MainWindow::isRenderingToMain() const
bool MainWindow::shouldHideMouseCursor() const
{
return isRenderingFullscreen() && Host::GetBoolSettingValue("UI", "HideMouseCursor", false);
return isRenderingFullscreen() && Host::GetBoolSettingValue("Main", "HideMouseCursor", false);
}
bool MainWindow::shouldHideMainWindow() const
{
return Host::GetBaseBoolSettingValue("UI", "HideMainWindowWhenRunning", false) || isRenderingFullscreen() ||
return Host::GetBaseBoolSettingValue("Main", "HideMainWindowWhenRunning", false) || isRenderingFullscreen() ||
QtHost::InNoGUIMode();
}
@ -1488,7 +1574,7 @@ void MainWindow::switchToGameListView()
{
m_was_paused_on_surface_loss = s_system_paused;
if (!s_system_paused)
g_emu_thread->pauseSystem(true);
g_emu_thread->setSystemPaused(true);
// switch to surfaceless. we have to wait until the display widget is gone before we swap over.
g_emu_thread->setSurfaceless(true);
@ -1513,7 +1599,7 @@ void MainWindow::switchToEmulationView()
// resume if we weren't paused at switch time
if (s_system_paused && !m_was_paused_on_surface_loss)
g_emu_thread->pauseSystem(false);
g_emu_thread->setSystemPaused(false);
if (m_display_widget)
m_display_widget->setFocus();
@ -1528,8 +1614,7 @@ void MainWindow::connectSignals()
connect(m_ui.actionStartFile, &QAction::triggered, this, &MainWindow::onStartFileActionTriggered);
connect(m_ui.actionStartDisc, &QAction::triggered, this, &MainWindow::onStartDiscActionTriggered);
connect(m_ui.actionStartBios, &QAction::triggered, this, &MainWindow::onStartBIOSActionTriggered);
connect(m_ui.actionResumeLastState, &QAction::triggered, g_emu_thread,
&QtHostInterface::resumeSystemFromMostRecentState);
connect(m_ui.actionResumeLastState, &QAction::triggered, g_emu_thread, &EmuThread::resumeSystemFromMostRecentState);
connect(m_ui.actionChangeDisc, &QAction::triggered, [this] { m_ui.menuChangeDisc->exec(QCursor::pos()); });
connect(m_ui.actionChangeDiscFromFile, &QAction::triggered, this, &MainWindow::onChangeDiscFromFileActionTriggered);
connect(m_ui.actionChangeDiscFromDevice, &QAction::triggered, this,
@ -1545,47 +1630,35 @@ void MainWindow::connectSignals()
connect(m_ui.actionRemoveDisc, &QAction::triggered, this, &MainWindow::onRemoveDiscActionTriggered);
connect(m_ui.actionAddGameDirectory, &QAction::triggered,
[this]() { getSettingsDialog()->getGameListSettingsWidget()->addSearchDirectory(this); });
connect(m_ui.actionPowerOff, &QAction::triggered, g_emu_thread, &QtHostInterface::powerOffSystem);
connect(m_ui.actionPowerOffWithoutSaving, &QAction::triggered, g_emu_thread,
&QtHostInterface::powerOffSystemWithoutSaving);
connect(m_ui.actionReset, &QAction::triggered, g_emu_thread, &QtHostInterface::resetSystem);
connect(m_ui.actionPause, &QAction::toggled, [this](bool active) { g_emu_thread->pauseSystem(active); });
connect(m_ui.actionScreenshot, &QAction::triggered, g_emu_thread, &QtHostInterface::saveScreenshot);
connect(m_ui.actionPowerOff, &QAction::triggered, this, [this]() { requestShutdown(true, true); });
connect(m_ui.actionPowerOffWithoutSaving, &QAction::triggered, this, [this]() { requestShutdown(false, false); });
connect(m_ui.actionReset, &QAction::triggered, g_emu_thread, &EmuThread::resetSystem);
connect(m_ui.actionPause, &QAction::toggled, [this](bool active) { g_emu_thread->setSystemPaused(active); });
connect(m_ui.actionScreenshot, &QAction::triggered, g_emu_thread, &EmuThread::saveScreenshot);
connect(m_ui.actionScanForNewGames, &QAction::triggered, this, [this]() { refreshGameList(false); });
connect(m_ui.actionRescanAllGames, &QAction::triggered, this, [this]() { refreshGameList(true); });
connect(m_ui.actionLoadState, &QAction::triggered, this, [this]() { m_ui.menuLoadState->exec(QCursor::pos()); });
connect(m_ui.actionSaveState, &QAction::triggered, this, [this]() { m_ui.menuSaveState->exec(QCursor::pos()); });
connect(m_ui.actionExit, &QAction::triggered, this, &MainWindow::close);
connect(m_ui.actionFullscreen, &QAction::triggered, g_emu_thread, &QtHostInterface::toggleFullscreen);
connect(m_ui.actionSettings, &QAction::triggered, [this]() { doSettings(SettingsDialog::Category::Count); });
connect(m_ui.actionGeneralSettings, &QAction::triggered,
[this]() { doSettings(SettingsDialog::Category::GeneralSettings); });
connect(m_ui.actionBIOSSettings, &QAction::triggered,
[this]() { doSettings(SettingsDialog::Category::BIOSSettings); });
connect(m_ui.actionConsoleSettings, &QAction::triggered,
[this]() { doSettings(SettingsDialog::Category::ConsoleSettings); });
connect(m_ui.actionEmulationSettings, &QAction::triggered,
[this]() { doSettings(SettingsDialog::Category::EmulationSettings); });
connect(m_ui.actionGameListSettings, &QAction::triggered,
[this]() { doSettings(SettingsDialog::Category::GameListSettings); });
connect(m_ui.actionFullscreen, &QAction::triggered, g_emu_thread, &EmuThread::toggleFullscreen);
connect(m_ui.actionSettings, &QAction::triggered, [this]() { doSettings(); });
connect(m_ui.actionGeneralSettings, &QAction::triggered, [this]() { doSettings("General"); });
connect(m_ui.actionBIOSSettings, &QAction::triggered, [this]() { doSettings("BIOS"); });
connect(m_ui.actionConsoleSettings, &QAction::triggered, [this]() { doSettings("Console"); });
connect(m_ui.actionEmulationSettings, &QAction::triggered, [this]() { doSettings("Emulation"); });
connect(m_ui.actionGameListSettings, &QAction::triggered, [this]() { doSettings("Game List"); });
connect(m_ui.actionHotkeySettings, &QAction::triggered,
[this]() { doControllerSettings(ControllerSettingsDialog::Category::HotkeySettings); });
connect(m_ui.actionControllerSettings, &QAction::triggered,
[this]() { doControllerSettings(ControllerSettingsDialog::Category::GlobalSettings); });
connect(m_ui.actionMemoryCardSettings, &QAction::triggered,
[this]() { doSettings(SettingsDialog::Category::MemoryCardSettings); });
connect(m_ui.actionDisplaySettings, &QAction::triggered,
[this]() { doSettings(SettingsDialog::Category::DisplaySettings); });
connect(m_ui.actionEnhancementSettings, &QAction::triggered,
[this]() { doSettings(SettingsDialog::Category::EnhancementSettings); });
connect(m_ui.actionPostProcessingSettings, &QAction::triggered,
[this]() { doSettings(SettingsDialog::Category::PostProcessingSettings); });
connect(m_ui.actionAudioSettings, &QAction::triggered,
[this]() { doSettings(SettingsDialog::Category::AudioSettings); });
connect(m_ui.actionAchievementSettings, &QAction::triggered,
[this]() { doSettings(SettingsDialog::Category::AchievementSettings); });
connect(m_ui.actionAdvancedSettings, &QAction::triggered,
[this]() { doSettings(SettingsDialog::Category::AdvancedSettings); });
connect(m_ui.actionMemoryCardSettings, &QAction::triggered, [this]() { doSettings("Memory Cards"); });
connect(m_ui.actionDisplaySettings, &QAction::triggered, [this]() { doSettings("Display"); });
connect(m_ui.actionEnhancementSettings, &QAction::triggered, [this]() { doSettings("Enhancements"); });
connect(m_ui.actionPostProcessingSettings, &QAction::triggered, [this]() { doSettings("Post-Processing"); });
connect(m_ui.actionAudioSettings, &QAction::triggered, [this]() { doSettings("Audio"); });
connect(m_ui.actionAchievementSettings, &QAction::triggered, [this]() { doSettings("Achievements"); });
connect(m_ui.actionFolderSettings, &QAction::triggered, [this]() { doSettings("Folders"); });
connect(m_ui.actionAdvancedSettings, &QAction::triggered, [this]() { doSettings("Advanced"); });
connect(m_ui.actionViewToolbar, &QAction::toggled, this, &MainWindow::onViewToolbarActionToggled);
connect(m_ui.actionViewLockToolbar, &QAction::toggled, this, &MainWindow::onViewLockToolbarActionToggled);
connect(m_ui.actionViewStatusBar, &QAction::toggled, this, &MainWindow::onViewStatusBarActionToggled);
@ -1615,27 +1688,25 @@ void MainWindow::connectSignals()
connect(m_ui.actionGridViewRefreshCovers, &QAction::triggered, m_game_list_widget,
&GameListWidget::refreshGridCovers);
connect(g_emu_thread, &QtHostInterface::settingsResetToDefault, this, &MainWindow::onSettingsResetToDefault);
connect(g_emu_thread, &QtHostInterface::errorReported, this, &MainWindow::reportError, Qt::BlockingQueuedConnection);
connect(g_emu_thread, &QtHostInterface::messageConfirmed, this, &MainWindow::confirmMessage,
connect(g_emu_thread, &EmuThread::settingsResetToDefault, this, &MainWindow::onSettingsResetToDefault);
connect(g_emu_thread, &EmuThread::errorReported, this, &MainWindow::reportError, Qt::BlockingQueuedConnection);
connect(g_emu_thread, &EmuThread::messageConfirmed, this, &MainWindow::confirmMessage, Qt::BlockingQueuedConnection);
connect(g_emu_thread, &EmuThread::createDisplayRequested, this, &MainWindow::createDisplay,
Qt::BlockingQueuedConnection);
connect(g_emu_thread, &QtHostInterface::createDisplayRequested, this, &MainWindow::createDisplay,
connect(g_emu_thread, &EmuThread::destroyDisplayRequested, this, &MainWindow::destroyDisplay);
connect(g_emu_thread, &EmuThread::updateDisplayRequested, this, &MainWindow::updateDisplay,
Qt::BlockingQueuedConnection);
connect(g_emu_thread, &QtHostInterface::destroyDisplayRequested, this, &MainWindow::destroyDisplay);
connect(g_emu_thread, &QtHostInterface::updateDisplayRequested, this, &MainWindow::updateDisplay,
Qt::BlockingQueuedConnection);
connect(g_emu_thread, &QtHostInterface::displaySizeRequested, this, &MainWindow::displaySizeRequested);
connect(g_emu_thread, &QtHostInterface::focusDisplayWidgetRequested, this, &MainWindow::focusDisplayWidget);
connect(g_emu_thread, &QtHostInterface::systemStarting, this, &MainWindow::onSystemStarting);
connect(g_emu_thread, &QtHostInterface::systemStarted, this, &MainWindow::onSystemStarted);
connect(g_emu_thread, &QtHostInterface::systemDestroyed, this, &MainWindow::onSystemDestroyed);
connect(g_emu_thread, &QtHostInterface::systemPaused, this, &MainWindow::onSystemPaused);
connect(g_emu_thread, &QtHostInterface::systemResumed, this, &MainWindow::onSystemResumed);
connect(g_emu_thread, &QtHostInterface::systemPerformanceCountersUpdated, this,
connect(g_emu_thread, &EmuThread::displaySizeRequested, this, &MainWindow::displaySizeRequested);
connect(g_emu_thread, &EmuThread::focusDisplayWidgetRequested, this, &MainWindow::focusDisplayWidget);
connect(g_emu_thread, &EmuThread::systemStarting, this, &MainWindow::onSystemStarting);
connect(g_emu_thread, &EmuThread::systemStarted, this, &MainWindow::onSystemStarted);
connect(g_emu_thread, &EmuThread::systemDestroyed, this, &MainWindow::onSystemDestroyed);
connect(g_emu_thread, &EmuThread::systemPaused, this, &MainWindow::onSystemPaused);
connect(g_emu_thread, &EmuThread::systemResumed, this, &MainWindow::onSystemResumed);
connect(g_emu_thread, &EmuThread::systemPerformanceCountersUpdated, this,
&MainWindow::onSystemPerformanceCountersUpdated);
connect(g_emu_thread, &QtHostInterface::runningGameChanged, this, &MainWindow::onRunningGameChanged);
connect(g_emu_thread, &QtHostInterface::exitRequested, this, &MainWindow::close);
connect(g_emu_thread, &QtHostInterface::mouseModeRequested, this, &MainWindow::onMouseModeRequested);
connect(g_emu_thread, &EmuThread::runningGameChanged, this, &MainWindow::onRunningGameChanged);
connect(g_emu_thread, &EmuThread::mouseModeRequested, this, &MainWindow::onMouseModeRequested);
// These need to be queued connections to stop crashing due to menus opening/closing and switching focus.
connect(m_game_list_widget, &GameListWidget::refreshProgress, this, &MainWindow::onGameListRefreshProgress);
@ -1766,6 +1837,7 @@ void MainWindow::setStyleFromSettings()
darkPalette.setColor(QPalette::Link, blue);
darkPalette.setColor(QPalette::Highlight, lighterGray);
darkPalette.setColor(QPalette::HighlightedText, Qt::white);
darkPalette.setColor(QPalette::PlaceholderText, QColor(Qt::white).darker());
darkPalette.setColor(QPalette::Active, QPalette::Button, gray.darker());
darkPalette.setColor(QPalette::Disabled, QPalette::ButtonText, gray);
@ -1802,6 +1874,7 @@ void MainWindow::setStyleFromSettings()
darkPalette.setColor(QPalette::Link, blue);
darkPalette.setColor(QPalette::Highlight, blue2);
darkPalette.setColor(QPalette::HighlightedText, Qt::white);
darkPalette.setColor(QPalette::PlaceholderText, QColor(Qt::white).darker());
darkPalette.setColor(QPalette::Active, QPalette::Button, gray.darker());
darkPalette.setColor(QPalette::Disabled, QPalette::ButtonText, gray);
@ -1930,7 +2003,7 @@ SettingsDialog* MainWindow::getSettingsDialog()
return m_settings_dialog;
}
void MainWindow::doSettings(SettingsDialog::Category category)
void MainWindow::doSettings(const char* category /* = nullptr */)
{
SettingsDialog* dlg = getSettingsDialog();
if (!dlg->isVisible())
@ -1939,7 +2012,7 @@ void MainWindow::doSettings(SettingsDialog::Category category)
dlg->show();
}
if (category != SettingsDialog::Category::Count)
if (category)
dlg->setCategory(category);
}
@ -2038,8 +2111,20 @@ void MainWindow::updateMenuSelectedTheme()
void MainWindow::closeEvent(QCloseEvent* event)
{
g_emu_thread->synchronousPowerOffSystem();
if (!requestShutdown(true, true, true))
{
event->ignore();
return;
}
#if 0
if (g_emu_thread->isRunningFullscreenUI())
g_emu_thread->stopFullscreenUI();
#endif
saveStateToConfig();
m_is_closing = true;
QMainWindow::closeEvent(event);
}
@ -2107,6 +2192,93 @@ void MainWindow::refreshGameList(bool invalidate_cache)
m_game_list_widget->refresh(invalidate_cache);
}
void MainWindow::runOnUIThread(const std::function<void()>& func)
{
func();
}
bool MainWindow::requestShutdown(bool allow_confirm /* = true */, bool allow_save_to_state /* = true */,
bool block_until_done /* = false */)
{
if (!s_system_valid)
return true;
// If we don't have a serial, we can't save state.
allow_save_to_state &= !m_current_game_code.empty();
bool save_state = allow_save_to_state && g_settings.save_state_on_exit;
// Only confirm on UI thread because we need to display a msgbox.
if (!m_is_closing && allow_confirm && g_settings.confim_power_off)
{
SystemLock lock(pauseAndLockSystem());
QMessageBox msgbox(lock.getDialogParent());
msgbox.setIcon(QMessageBox::Question);
msgbox.setWindowTitle(tr("Confirm Shutdown"));
msgbox.setText("Are you sure you want to shut down the virtual machine?");
QCheckBox* save_cb = new QCheckBox(tr("Save State For Resume"), &msgbox);
save_cb->setChecked(save_state);
save_cb->setEnabled(allow_save_to_state);
msgbox.setCheckBox(save_cb);
msgbox.addButton(QMessageBox::Yes);
msgbox.addButton(QMessageBox::No);
msgbox.setDefaultButton(QMessageBox::Yes);
if (msgbox.exec() != QMessageBox::Yes)
return false;
save_state = save_cb->isChecked();
// Don't switch back to fullscreen when we're shutting down anyway.
lock.cancelResume();
}
// This is a little bit annoying. Qt will close everything down if we don't have at least one window visible,
// but we might not be visible because the user is using render-to-separate and hide. We don't want to always
// reshow the main window during display updates, because otherwise fullscreen transitions and renderer switches
// would briefly show and then hide the main window. So instead, we do it on shutdown, here. Except if we're in
// batch mode, when we're going to exit anyway.
if (!isRenderingToMain() && isHidden() && !QtHost::InBatchMode())
updateWindowState(true);
// Now we can actually shut down the VM.
g_emu_thread->shutdownSystem(save_state);
if (block_until_done || m_is_closing || QtHost::InBatchMode())
{
// We need to yield here, since the display gets destroyed.
while (s_system_valid || System::GetState() != System::State::Shutdown)
QApplication::processEvents(QEventLoop::ExcludeUserInputEvents, 1);
}
if (!m_is_closing && QtHost::InBatchMode())
{
// Closing the window should shut down everything. If we don't set the closing flag here,
// the VM shutdown may not complete by the time closeEvent() is called, leading to a confirm.
m_is_closing = true;
close();
}
return true;
}
void MainWindow::requestExit()
{
// this is block, because otherwise closeEvent() will also prompt
if (!requestShutdown(true, true, true))
return;
close();
}
void MainWindow::checkForSettingChanges()
{
if (m_display_widget)
m_display_widget->updateRelativeMode(s_system_valid && !s_system_paused);
updateWindowState();
}
void MainWindow::onCheckForUpdatesActionTriggered()
{
// Wipe out the last version, that way it displays the update if we've previously skipped it.
@ -2226,7 +2398,7 @@ void MainWindow::onToolsCheatManagerTriggered()
void MainWindow::openCPUDebugger()
{
g_emu_thread->pauseSystem(true, true);
g_emu_thread->setSystemPaused(true, true);
if (!System::IsValid())
return;
@ -2299,3 +2471,49 @@ void MainWindow::onUpdateCheckComplete()
m_auto_updater_dialog->deleteLater();
m_auto_updater_dialog = nullptr;
}
MainWindow::SystemLock MainWindow::pauseAndLockSystem()
{
const bool was_fullscreen = isRenderingFullscreen();
const bool was_paused = s_system_paused;
// We use surfaceless rather than switching out of fullscreen, because
// we're paused, so we're not going to be rendering anyway.
if (was_fullscreen)
g_emu_thread->setSurfaceless(true);
if (!was_paused)
g_emu_thread->setSystemPaused(true);
// We want to parent dialogs to the display widget, except if we were fullscreen,
// since it's going to get destroyed by the surfaceless call above.
QWidget* dialog_parent = was_fullscreen ? static_cast<QWidget*>(this) : getDisplayContainer();
return SystemLock(dialog_parent, was_paused, was_fullscreen);
}
MainWindow::SystemLock::SystemLock(QWidget* dialog_parent, bool was_paused, bool was_fullscreen)
: m_dialog_parent(dialog_parent), m_was_paused(was_paused), m_was_fullscreen(was_fullscreen)
{
}
MainWindow::SystemLock::SystemLock(SystemLock&& lock)
: m_dialog_parent(lock.m_dialog_parent), m_was_paused(lock.m_was_paused), m_was_fullscreen(lock.m_was_fullscreen)
{
lock.m_dialog_parent = nullptr;
lock.m_was_paused = true;
lock.m_was_fullscreen = false;
}
MainWindow::SystemLock::~SystemLock()
{
if (m_was_fullscreen)
g_emu_thread->setSurfaceless(false);
if (!m_was_paused)
g_emu_thread->setSystemPaused(false);
}
void MainWindow::SystemLock::cancelResume()
{
m_was_paused = true;
m_was_fullscreen = false;
}

View File

@ -16,15 +16,16 @@ class QThread;
class QProgressBar;
class GameListWidget;
class QtHostInterface;
class EmuThread;
class AutoUpdaterDialog;
class MemoryCardEditorDialog;
class CheatManagerDialog;
class DebuggerWindow;
class MainWindow;
class HostDisplay;
namespace GameList {
struct GameListEntry;
struct Entry;
}
class GDBServer;
@ -33,12 +34,39 @@ class MainWindow final : public QMainWindow
{
Q_OBJECT
public:
/// This class is a scoped lock on the VM, which prevents it from running while
/// the object exists. Its purpose is to be used for blocking/modal popup boxes,
/// where the VM needs to exit fullscreen temporarily.
class SystemLock
{
public:
SystemLock(SystemLock&& lock);
SystemLock(const SystemLock&) = delete;
~SystemLock();
/// Returns the parent widget, which can be used for any popup dialogs.
ALWAYS_INLINE QWidget* getDialogParent() const { return m_dialog_parent; }
/// Cancels any pending unpause/fullscreen transition.
/// Call when you're going to destroy the VM anyway.
void cancelResume();
private:
SystemLock(QWidget* dialog_parent, bool was_paused, bool was_fullscreen);
friend MainWindow;
QWidget* m_dialog_parent;
bool m_was_paused;
bool m_was_fullscreen;
};
public:
explicit MainWindow();
~MainWindow();
/// Initializes the window. Call once at startup.
void initializeAndShow();
void initialize();
/// Performs update check if enabled in settings.
void startupUpdateCheck();
@ -49,11 +77,20 @@ public:
/// Updates the state of the controls which should be disabled by achievements challenge mode.
void onAchievementsChallengeModeToggled(bool enabled);
/// Locks the VM by pausing it, while a popup dialog is displayed.
SystemLock pauseAndLockSystem();
public Q_SLOTS:
/// Updates debug menu visibility (hides if disabled).
void updateDebugMenuVisibility();
void refreshGameList(bool invalidate_cache);
void runOnUIThread(const std::function<void()>& func);
bool requestShutdown(bool allow_confirm = true, bool allow_save_to_state = true, bool block_until_done = false);
void requestExit();
void checkForSettingChanges();
void checkForUpdates(bool display_message);
private Q_SLOTS:
@ -158,7 +195,7 @@ private:
bool shouldHideCursorInFullscreen() const;
SettingsDialog* getSettingsDialog();
void doSettings(SettingsDialog::Category category = SettingsDialog::Category::Count);
void doSettings(const char* category = nullptr);
ControllerSettingsDialog* getControllerSettingsDialog();
void doControllerSettings(ControllerSettingsDialog::Category category = ControllerSettingsDialog::Category::Count);
@ -171,8 +208,11 @@ private:
void setGameListEntryCoverImage(const GameList::Entry* entry);
void recreate();
std::optional<bool> promptForResumeState(const QString& save_state_path);
void startGameListEntry(const GameList::Entry* entry, std::optional<s32> save_slot, std::optional<bool> fast_boot);
/// Fills menu with save state info and handlers.
void populateGameListContextMenu(const GameList::Entry* entry, QWidget* parent_window, QMenu* menu);
std::optional<bool> promptForResumeState(const std::string& save_state_path);
void startGameListEntry(const GameList::Entry* entry, std::optional<std::string> save_path, std::optional<bool> fast_boot);
Ui::MainWindow m_ui;

View File

@ -25,7 +25,7 @@
<rect>
<x>0</x>
<y>0</y>
<width>754</width>
<width>800</width>
<height>22</height>
</rect>
</property>
@ -38,7 +38,8 @@
<string>Change Disc</string>
</property>
<property name="icon">
<iconset theme="ChangeDisc"/>
<iconset theme="ChangeDisc">
<normaloff>.</normaloff>.</iconset>
</property>
<actiongroup name="actionGroupChangeDiscSubImages"/>
<addaction name="actionChangeDiscFromFile"/>
@ -52,7 +53,8 @@
<string>Cheats</string>
</property>
<property name="icon">
<iconset theme="Cheats"/>
<iconset theme="Cheats">
<normaloff>.</normaloff>.</iconset>
</property>
</widget>
<widget class="QMenu" name="menuLoadState">
@ -60,7 +62,8 @@
<string>Load State</string>
</property>
<property name="icon">
<iconset theme="LoadState"/>
<iconset theme="LoadState">
<normaloff>.</normaloff>.</iconset>
</property>
</widget>
<widget class="QMenu" name="menuSaveState">
@ -68,7 +71,8 @@
<string>Save State</string>
</property>
<property name="icon">
<iconset theme="SaveState"/>
<iconset theme="SaveState">
<normaloff>.</normaloff>.</iconset>
</property>
</widget>
<addaction name="actionStartFile"/>
@ -99,28 +103,36 @@
<property name="title">
<string>Theme</string>
</property>
<property name="icon">
<iconset theme="Clear"/>
</property>
</widget>
<widget class="QMenu" name="menuSettingsLanguage">
<property name="title">
<string>Language</string>
</property>
<property name="icon">
<iconset theme="Language"/>
</property>
</widget>
<addaction name="separator"/>
<addaction name="actionGeneralSettings"/>
<addaction name="actionGameListSettings"/>
<addaction name="actionBIOSSettings"/>
<addaction name="actionConsoleSettings"/>
<addaction name="actionEmulationSettings"/>
<addaction name="actionGameListSettings"/>
<addaction name="actionHotkeySettings"/>
<addaction name="actionControllerSettings"/>
<addaction name="actionMemoryCardSettings"/>
<addaction name="actionDisplaySettings"/>
<addaction name="actionEnhancementSettings"/>
<addaction name="actionPostProcessingSettings"/>
<addaction name="actionAudioSettings"/>
<addaction name="actionAchievementSettings"/>
<addaction name="actionFolderSettings"/>
<addaction name="actionAdvancedSettings"/>
<addaction name="separator"/>
<addaction name="actionControllerSettings"/>
<addaction name="actionHotkeySettings"/>
<addaction name="separator"/>
<addaction name="actionAddGameDirectory"/>
<addaction name="actionScanForNewGames"/>
<addaction name="actionRescanAllGames"/>
@ -266,7 +278,8 @@
<widget class="QStatusBar" name="statusBar"/>
<action name="actionStartFile">
<property name="icon">
<iconset theme="StartfileSettings"/>
<iconset theme="StartfileSettings">
<normaloff>.</normaloff>.</iconset>
</property>
<property name="text">
<string>Start &amp;File...</string>
@ -274,7 +287,8 @@
</action>
<action name="actionStartDisc">
<property name="icon">
<iconset theme="StartdiscSettings"/>
<iconset theme="StartdiscSettings">
<normaloff>.</normaloff>.</iconset>
</property>
<property name="text">
<string>Start &amp;Disc...</string>
@ -282,7 +296,8 @@
</action>
<action name="actionStartBios">
<property name="icon">
<iconset theme="BIOSSettings"/>
<iconset theme="BIOSSettings">
<normaloff>.</normaloff>.</iconset>
</property>
<property name="text">
<string>Start &amp;BIOS</string>
@ -290,7 +305,8 @@
</action>
<action name="actionScanForNewGames">
<property name="icon">
<iconset theme="ScanForGames"/>
<iconset theme="ScanForGames">
<normaloff>.</normaloff>.</iconset>
</property>
<property name="text">
<string>&amp;Scan For New Games</string>
@ -298,7 +314,8 @@
</action>
<action name="actionRescanAllGames">
<property name="icon">
<iconset theme="RescanAllGames"/>
<iconset theme="RescanAllGames">
<normaloff>.</normaloff>.</iconset>
</property>
<property name="text">
<string>&amp;Rescan All Games</string>
@ -306,7 +323,8 @@
</action>
<action name="actionPowerOff">
<property name="icon">
<iconset theme="PowerOff"/>
<iconset theme="PowerOff">
<normaloff>.</normaloff>.</iconset>
</property>
<property name="text">
<string>Power &amp;Off</string>
@ -314,7 +332,8 @@
</action>
<action name="actionReset">
<property name="icon">
<iconset theme="Reset"/>
<iconset theme="Reset">
<normaloff>.</normaloff>.</iconset>
</property>
<property name="text">
<string>&amp;Reset</string>
@ -325,7 +344,8 @@
<bool>true</bool>
</property>
<property name="icon">
<iconset theme="Pause"/>
<iconset theme="Pause">
<normaloff>.</normaloff>.</iconset>
</property>
<property name="text">
<string>&amp;Pause</string>
@ -333,7 +353,8 @@
</action>
<action name="actionLoadState">
<property name="icon">
<iconset theme="LoadState"/>
<iconset theme="LoadState">
<normaloff>.</normaloff>.</iconset>
</property>
<property name="text">
<string>&amp;Load State</string>
@ -341,7 +362,8 @@
</action>
<action name="actionSaveState">
<property name="icon">
<iconset theme="SaveState"/>
<iconset theme="SaveState">
<normaloff>.</normaloff>.</iconset>
</property>
<property name="text">
<string>&amp;Save State</string>
@ -349,7 +371,8 @@
</action>
<action name="actionExit">
<property name="icon">
<iconset theme="Exit"/>
<iconset theme="Exit">
<normaloff>.</normaloff>.</iconset>
</property>
<property name="text">
<string>E&amp;xit</string>
@ -357,71 +380,80 @@
</action>
<action name="actionBIOSSettings">
<property name="icon">
<iconset theme="BIOSSettings"/>
<iconset theme="BIOSSettings">
<normaloff>.</normaloff>.</iconset>
</property>
<property name="text">
<string>B&amp;IOS Settings...</string>
<string>B&amp;IOS</string>
</property>
</action>
<action name="actionConsoleSettings">
<property name="icon">
<iconset theme="ConsoleSettings"/>
<iconset theme="ConsoleSettings">
<normaloff>.</normaloff>.</iconset>
</property>
<property name="text">
<string>C&amp;onsole Settings...</string>
<string>C&amp;onsole</string>
</property>
</action>
<action name="actionEmulationSettings">
<property name="icon">
<iconset theme="EmulationSettings"/>
<iconset theme="EmulationSettings">
<normaloff>.</normaloff>.</iconset>
</property>
<property name="text">
<string>E&amp;mulation Settings...</string>
<string>E&amp;mulation</string>
</property>
</action>
<action name="actionControllerSettings">
<property name="icon">
<iconset theme="ControllerSettings"/>
<iconset theme="ControllerSettings">
<normaloff>.</normaloff>.</iconset>
</property>
<property name="text">
<string>&amp;Controller Settings...</string>
<string>&amp;Controllers</string>
</property>
</action>
<action name="actionHotkeySettings">
<property name="icon">
<iconset theme="HotkeySettings"/>
<iconset theme="HotkeySettings">
<normaloff>.</normaloff>.</iconset>
</property>
<property name="text">
<string>&amp;Hotkey Settings...</string>
<string>&amp;Hotkeys</string>
</property>
</action>
<action name="actionDisplaySettings">
<property name="icon">
<iconset theme="DisplaySettings"/>
<iconset theme="DisplaySettings">
<normaloff>.</normaloff>.</iconset>
</property>
<property name="text">
<string>&amp;Display Settings...</string>
<string>&amp;Display</string>
</property>
</action>
<action name="actionEnhancementSettings">
<property name="icon">
<iconset theme="EnhancementSettings"/>
<iconset theme="EnhancementSettings">
<normaloff>.</normaloff>.</iconset>
</property>
<property name="text">
<string>&amp;Enhancement Settings...</string>
<string>&amp;Enhancements</string>
</property>
</action>
<action name="actionPostProcessingSettings">
<property name="icon">
<iconset theme="PostprocessingSettings"/>
<iconset theme="PostprocessingSettings">
<normaloff>.</normaloff>.</iconset>
</property>
<property name="text">
<string>&amp;Post-Processing Settings...</string>
<string>&amp;Post-Processing</string>
</property>
</action>
<action name="actionFullscreen">
<property name="icon">
<iconset theme="Fullscreen"/>
<iconset theme="Fullscreen">
<normaloff>.</normaloff>.</iconset>
</property>
<property name="text">
<string>Fullscreen</string>
@ -488,7 +520,8 @@
</action>
<action name="actionChangeDisc">
<property name="icon">
<iconset theme="ChangeDisc"/>
<iconset theme="ChangeDisc">
<normaloff>.</normaloff>.</iconset>
</property>
<property name="text">
<string>Change Disc...</string>
@ -496,7 +529,8 @@
</action>
<action name="actionCheats">
<property name="icon">
<iconset theme="Cheats"/>
<iconset theme="Cheats">
<normaloff>.</normaloff>.</iconset>
</property>
<property name="text">
<string>Cheats...</string>
@ -504,47 +538,62 @@
</action>
<action name="actionAudioSettings">
<property name="icon">
<iconset theme="AudioSettings"/>
<iconset theme="AudioSettings">
<normaloff>.</normaloff>.</iconset>
</property>
<property name="text">
<string>Audio Settings...</string>
<string>Audio</string>
</property>
</action>
<action name="actionAchievementSettings">
<property name="icon">
<iconset theme="AchievementsSettings"/>
<iconset theme="AchievementsSettings">
<normaloff>.</normaloff>.</iconset>
</property>
<property name="text">
<string>Achievement Settings...</string>
<string>Achievements</string>
</property>
</action>
<action name="actionFolderSettings">
<property name="icon">
<iconset theme="Options">
<normaloff>.</normaloff>.</iconset>
</property>
<property name="text">
<string>Folders</string>
</property>
</action>
<action name="actionGameListSettings">
<property name="icon">
<iconset theme="GamelistSettings"/>
<iconset theme="GamelistSettings">
<normaloff>.</normaloff>.</iconset>
</property>
<property name="text">
<string>Game List Settings...</string>
<string>Game List</string>
</property>
</action>
<action name="actionGeneralSettings">
<property name="icon">
<iconset theme="GeneralSettings"/>
<iconset theme="GeneralSettings">
<normaloff>.</normaloff>.</iconset>
</property>
<property name="text">
<string>General Settings...</string>
<string>General</string>
</property>
</action>
<action name="actionAdvancedSettings">
<property name="icon">
<iconset theme="AdvancedSettings"/>
<iconset theme="AdvancedSettings">
<normaloff>.</normaloff>.</iconset>
</property>
<property name="text">
<string>Advanced Settings...</string>
<string>Advanced</string>
</property>
</action>
<action name="actionAddGameDirectory">
<property name="icon">
<iconset theme="AddGameDirectory"/>
<iconset theme="AddGameDirectory">
<normaloff>.</normaloff>.</iconset>
</property>
<property name="text">
<string>Add Game Directory...</string>
@ -552,7 +601,8 @@
</action>
<action name="actionSettings">
<property name="icon">
<iconset theme="GeneralSettings"/>
<iconset theme="GeneralSettings">
<normaloff>.</normaloff>.</iconset>
</property>
<property name="text">
<string>&amp;Settings...</string>
@ -709,7 +759,8 @@
</action>
<action name="actionScreenshot">
<property name="icon">
<iconset theme="Screenshot"/>
<iconset theme="Screenshot">
<normaloff>.</normaloff>.</iconset>
</property>
<property name="text">
<string>&amp;Screenshot</string>
@ -717,15 +768,17 @@
</action>
<action name="actionMemoryCardSettings">
<property name="icon">
<iconset theme="MemorycardSettings"/>
<iconset theme="MemorycardSettings">
<normaloff>.</normaloff>.</iconset>
</property>
<property name="text">
<string>&amp;Memory Card Settings...</string>
<string>&amp;Memory Cards</string>
</property>
</action>
<action name="actionResumeLastState">
<property name="icon">
<iconset theme="Resume"/>
<iconset theme="Resume">
<normaloff>.</normaloff>.</iconset>
</property>
<property name="text">
<string>Resume</string>
@ -769,7 +822,8 @@
</action>
<action name="actionViewGameList">
<property name="icon">
<iconset theme="GameList"/>
<iconset theme="GameList">
<normaloff>.</normaloff>.</iconset>
</property>
<property name="text">
<string>Game &amp;List</string>
@ -808,7 +862,8 @@
</action>
<action name="actionViewGameGrid">
<property name="icon">
<iconset theme="GameGrid"/>
<iconset theme="GameGrid">
<normaloff>.</normaloff>.</iconset>
</property>
<property name="text">
<string>Game &amp;Grid</string>
@ -858,7 +913,8 @@
</action>
<action name="actionPowerOffWithoutSaving">
<property name="icon">
<iconset theme="PoweroffWsaving"/>
<iconset theme="PoweroffWsaving">
<normaloff>.</normaloff>.</iconset>
</property>
<property name="text">
<string>Power Off &amp;Without Saving</string>
@ -866,7 +922,7 @@
</action>
<action name="actionStartFullscreenUI">
<property name="icon">
<iconset theme="tv-2-line">
<iconset theme="DisplaySettings">
<normaloff>.</normaloff>.</iconset>
</property>
<property name="text">
@ -875,7 +931,7 @@
</action>
<action name="actionStartFullscreenUI2">
<property name="icon">
<iconset theme="tv-2-line">
<iconset theme="DisplaySettings">
<normaloff>.</normaloff>.</iconset>
</property>
<property name="text">

View File

@ -128,8 +128,8 @@ void MemoryCardSettingsWidget::createPortSettingsUi(SettingsDialog* dialog, int
QHBoxLayout* memory_card_layout = new QHBoxLayout();
ui->memory_card_path = new QLineEdit(ui->container);
SettingWidgetBinder::BindWidgetToStringSetting(m_dialog->getSettingsInterface(), ui->memory_card_path, "MemoryCards",
StringUtil::StdStringFromFormat("Card%dPath", index + 1));
updateMemoryCardPath(index);
connect(ui->memory_card_path, &QLineEdit::textChanged, this, [this, index]() { onMemoryCardPathChanged(index); });
if (ui->memory_card_path->text().isEmpty())
{
QSignalBlocker sb(ui->memory_card_path);
@ -138,11 +138,13 @@ void MemoryCardSettingsWidget::createPortSettingsUi(SettingsDialog* dialog, int
memory_card_layout->addWidget(ui->memory_card_path);
QPushButton* memory_card_path_browse = new QPushButton(tr("Browse..."), ui->container);
connect(memory_card_path_browse, &QPushButton::clicked, [this, index]() { onBrowseMemoryCardPathClicked(index); });
connect(memory_card_path_browse, &QPushButton::clicked, this,
[this, index]() { onBrowseMemoryCardPathClicked(index); });
memory_card_layout->addWidget(memory_card_path_browse);
QPushButton* memory_card_path_reset = new QPushButton(tr("Reset"), ui->container);
connect(memory_card_path_reset, &QPushButton::clicked, [this, index]() { onResetMemoryCardPathClicked(index); });
connect(memory_card_path_reset, &QPushButton::clicked, this,
[this, index]() { onResetMemoryCardPathClicked(index); });
memory_card_layout->addWidget(memory_card_path_reset);
ui->layout->addWidget(new QLabel(tr("Shared Memory Card Path:"), ui->container));
@ -161,14 +163,33 @@ void MemoryCardSettingsWidget::onBrowseMemoryCardPathClicked(int index)
m_port_ui[index].memory_card_path->setText(path);
}
void MemoryCardSettingsWidget::onMemoryCardPathChanged(int index)
{
const auto key = TinyString::FromFormat("Card%dPath", index + 1);
std::string relative_path(
Path::MakeRelative(m_port_ui[index].memory_card_path->text().toStdString(), EmuFolders::MemoryCards));
m_dialog->setStringSettingValue("MemoryCards", key, relative_path.c_str());
}
void MemoryCardSettingsWidget::onResetMemoryCardPathClicked(int index)
{
m_dialog->removeSettingValue("MemoryCards", TinyString::FromFormat("Card%dPath", index + 1));
const auto key = TinyString::FromFormat("Card%dPath", index + 1);
if (m_dialog->isPerGameSettings())
m_dialog->removeSettingValue("MemoryCards", key);
else
m_dialog->setStringSettingValue("MemoryCards", key, Settings::GetDefaultSharedMemoryCardName(index).c_str());
Panic("Fixme");
#if 0
QSignalBlocker db(m_port_ui[index].memory_card_path);
m_port_ui[index].memory_card_path->setText(
QString::fromStdString(g_emu_thread->GetSharedMemoryCardPath(index)));
#endif
updateMemoryCardPath(index);
}
void MemoryCardSettingsWidget::updateMemoryCardPath(int index)
{
const auto key = TinyString::FromFormat("Card%dPath", index + 1);
std::string path(
m_dialog->getEffectiveStringValue("MemoryCards", key, Settings::GetDefaultSharedMemoryCardName(index).c_str()));
if (!Path::IsAbsolute(path))
path = Path::Combine(EmuFolders::MemoryCards, path);
QSignalBlocker db(m_port_ui[index].memory_card_path);
m_port_ui[index].memory_card_path->setText(QString::fromStdString(path));
}

View File

@ -33,6 +33,8 @@ private:
void createPortSettingsUi(SettingsDialog* dialog, int index, PortSettingsUI* ui);
void onBrowseMemoryCardPathClicked(int index);
void onResetMemoryCardPathClicked(int index);
void onMemoryCardPathChanged(int index);
void updateMemoryCardPath(int index);
std::array<PortSettingsUI, 2> m_port_ui = {};
QLineEdit* m_memory_card_directory = nullptr;

File diff suppressed because it is too large Load Diff

View File

@ -1,5 +1,4 @@
#pragma once
#include "common/event.h"
#include "core/host.h"
#include "core/host_settings.h"
#include "core/system.h"
@ -9,6 +8,7 @@
#include "qtutils.h"
#include <QtCore/QByteArray>
#include <QtCore/QObject>
#include <QtCore/QSemaphore>
#include <QtCore/QSettings>
#include <QtCore/QString>
#include <QtCore/QThread>
@ -40,50 +40,35 @@ Q_DECLARE_METATYPE(std::shared_ptr<SystemBootParameters>);
Q_DECLARE_METATYPE(GPURenderer);
Q_DECLARE_METATYPE(InputBindingKey);
class QtHostInterface final : public QObject
class EmuThread : public QThread
{
Q_OBJECT
public:
explicit QtHostInterface(QObject* parent = nullptr);
~QtHostInterface();
explicit EmuThread(QThread* ui_thread);
~EmuThread();
bool Initialize();
void Shutdown();
static void start();
static void stop();
void RunLater(std::function<void()> func);
ALWAYS_INLINE bool isOnThread() const { return QThread::currentThread() == this; }
public:
ALWAYS_INLINE void requestExit() { RequestExit(); }
ALWAYS_INLINE bool isOnWorkerThread() const { return QThread::currentThread() == m_worker_thread; }
ALWAYS_INLINE QEventLoop* getEventLoop() const { return m_worker_thread_event_loop; }
ALWAYS_INLINE QEventLoop* getEventLoop() const { return m_event_loop; }
ALWAYS_INLINE bool isFullscreen() const { return m_is_fullscreen; }
ALWAYS_INLINE bool isRenderingToMain() const { return m_is_rendering_to_main; }
ALWAYS_INLINE bool isSurfaceless() const { return m_is_surfaceless; }
ALWAYS_INLINE bool isRunningFullscreenUI() const { return m_run_fullscreen_ui; }
void reinstallTranslator();
void populateLoadStateMenu(const char* game_code, QMenu* menu);
void populateSaveStateMenu(const char* game_code, QMenu* menu);
/// Fills menu with save state info and handlers.
void populateGameListContextMenu(const GameList::Entry* entry, QWidget* parent_window, QMenu* menu);
/// Fills menu with the current playlist entries. The disc index is marked as checked.
void populateChangeDiscSubImageMenu(QMenu* menu, QActionGroup* action_group);
/// Fills menu with the current cheat options.
void populateCheatsMenu(QMenu* menu);
void saveInputProfile(const QString& profile_path);
/// Returns a list of supported languages and codes (suffixes for translation files).
static std::vector<std::pair<QString, QString>> getAvailableLanguageList();
/// Called back from the GS thread when the display state changes (e.g. fullscreen, render to main).
HostDisplay* acquireHostDisplay();
void connectDisplaySignals(DisplayWidget* widget);
@ -94,6 +79,13 @@ public:
void stopBackgroundControllerPollTimer();
void wakeThread();
bool shouldRenderToMain() const;
void loadSettings(SettingsInterface& si);
void setInitialState();
void checkForSettingsChanges(const Settings& old_settings);
void bootOrLoadState(std::string path);
Q_SIGNALS:
void errorReported(const QString& title, const QString& message);
bool messageConfirmed(const QString& title, const QString& message);
@ -118,7 +110,6 @@ Q_SIGNALS:
GPURenderer renderer, quint32 render_width, quint32 render_height,
bool render_interlaced);
void runningGameChanged(const QString& filename, const QString& game_code, const QString& game_title);
void exitRequested();
void inputProfileLoaded();
void mouseModeRequested(bool relative, bool hide_cursor);
void achievementsLoaded(quint32 id, const QString& game_info_string, quint32 total, quint32 points);
@ -127,20 +118,16 @@ Q_SIGNALS:
public Q_SLOTS:
void setDefaultSettings();
void applySettings(bool display_osd_messages = false);
void reloadGameSettings();
void applyInputProfile(const QString& profile_path);
void reloadGameSettings(bool display_osd_messages = false);
void reloadInputSources();
void reloadInputBindings();
void enumerateInputDevices();
void enumerateVibrationMotors();
void bootSystem(std::shared_ptr<SystemBootParameters> params);
void resumeSystemFromState(const QString& filename, bool boot_on_failure);
void resumeSystemFromMostRecentState();
void powerOffSystem();
void powerOffSystemWithoutSaving();
void synchronousPowerOffSystem();
void shutdownSystem(bool save_state = true);
void resetSystem();
void pauseSystem(bool paused, bool wait_until_paused = false);
void setSystemPaused(bool paused, bool wait_until_paused = false);
void changeDisc(const QString& new_disc_filename);
void changeDiscFromPlaylist(quint32 index);
void loadState(const QString& filename);
@ -161,81 +148,40 @@ public Q_SLOTS:
void toggleFullscreen();
void setFullscreen(bool fullscreen);
void setSurfaceless(bool surfaceless);
void requestDisplaySize(float scale);
void loadCheatList(const QString& filename);
void setCheatEnabled(quint32 index, bool enabled);
void applyCheat(quint32 index);
void reloadPostProcessingShaders();
void requestRenderWindowScale(qreal scale);
void executeOnEmulationThread(std::function<void()> callback, bool wait = false);
private Q_SLOTS:
void doStopThread();
void stopInThread();
void onDisplayWindowMouseMoveEvent(bool relative, float x, float y);
void onDisplayWindowMouseButtonEvent(int button, bool pressed);
void onDisplayWindowMouseWheelEvent(const QPoint& delta_angle);
void onDisplayWindowResized(int width, int height);
void onDisplayWindowKeyEvent(int key, bool pressed);
void doBackgroundControllerPoll();
void runOnEmuThread(std::function<void()> callback);
protected:
bool IsFullscreen() const;
bool SetFullscreen(bool enabled);
void RequestExit();
void run() override;
private:
enum : u32
{
BACKGROUND_CONTROLLER_POLLING_INTERVAL =
100, /// Interval at which the controllers are polled when the system is not active.
};
using InputButtonHandler = std::function<void(bool)>;
using InputAxisHandler = std::function<void(float)>;
class Thread : public QThread
{
public:
Thread(QtHostInterface* parent);
~Thread();
void setInitResult(bool result);
bool waitForInit();
protected:
void run() override;
private:
QtHostInterface* m_parent;
std::atomic_bool m_init_result{false};
Common::Event m_init_event;
};
void createBackgroundControllerPollTimer();
void destroyBackgroundControllerPollTimer();
void setImGuiFont();
void createThread();
void stopThread();
void threadEntryPoint();
bool initializeOnThread();
void shutdownOnThread();
void installTranslator();
void checkRenderToMainState();
void updateDisplayState();
void queueSettingsSave();
QThread* m_original_thread = nullptr;
Thread* m_worker_thread = nullptr;
QEventLoop* m_worker_thread_event_loop = nullptr;
Common::Event m_worker_thread_sync_execute_done;
QThread* m_ui_thread;
QSemaphore m_started_semaphore;
QEventLoop* m_event_loop = nullptr;
QTimer* m_background_controller_polling_timer = nullptr;
std::atomic_bool m_shutdown_flag{false};
QTimer* m_background_controller_polling_timer = nullptr;
std::vector<QTranslator*> m_translators;
bool m_run_fullscreen_ui = false;
bool m_is_rendering_to_main = false;
bool m_is_fullscreen = false;
@ -243,14 +189,19 @@ private:
bool m_lost_exclusive_fullscreen = false;
bool m_is_surfaceless = false;
bool m_save_state_on_shutdown = false;
bool m_pause_on_focus_loss = false;
bool m_was_paused_by_focus_loss = false;
};
extern QtHostInterface* g_emu_thread;
extern EmuThread* g_emu_thread;
namespace QtHost {
/// Startup, creates the thread and loads config.
bool Initialize();
/// Execute before exiting, stops the thread and saves settings.
void Shutdown();
/// Sets batch mode (exit after game shutdown).
bool InBatchMode();
@ -260,6 +211,12 @@ bool InNoGUIMode();
/// Executes a function on the UI thread.
void RunOnUIThread(const std::function<void()>& func, bool block = false);
/// Returns a list of supported languages and codes (suffixes for translation files).
std::vector<std::pair<QString, QString>> GetAvailableLanguageList();
/// Call when the language changes.
void ReinstallTranslator();
/// Returns the application name and version, optionally including debug/devel config indicator.
QString GetAppNameAndVersion();

View File

@ -1,6 +1,8 @@
#include "qtutils.h"
#include "common/byte_stream.h"
#include "common/make_array.h"
#include "core/system.h"
#include "frontend-common/game_list.h"
#include <QtCore/QCoreApplication>
#include <QtCore/QMetaObject>
#include <QtGui/QDesktopServices>
@ -730,22 +732,6 @@ void FillComboBoxWithMSAAModes(QComboBox* cb)
cb->addItem(qApp->translate("GPUSettingsWidget", "%1x SSAA").arg(i), GetMSAAModeValue(i, true));
}
void FillComboBoxWithEmulationSpeeds(QComboBox* cb)
{
cb->addItem(qApp->translate("GeneralSettingsWidget", "Unlimited"), QVariant(0.0f));
static constexpr auto speeds = make_array(10, 20, 30, 40, 50, 60, 70, 80, 90, 100, 125, 150, 175, 200, 250, 300, 350,
400, 450, 500, 600, 700, 800, 900, 1000);
for (const int speed : speeds)
{
cb->addItem(qApp->translate("GeneralSettingsWidget", "%1% [%2 FPS (NTSC) / %3 FPS (PAL)]")
.arg(speed)
.arg((60 * speed) / 100)
.arg((50 * speed) / 100),
QVariant(static_cast<float>(speed) / 100.0f));
}
}
std::optional<unsigned> PromptForAddress(QWidget* parent, const QString& title, const QString& label, bool code)
{
const QString address_str(
@ -823,4 +809,53 @@ void ResizePotentiallyFixedSizeWindow(QWidget* widget, int width, int height)
widget->resize(width, height);
}
QIcon GetIconForRegion(ConsoleRegion region)
{
switch (region)
{
case ConsoleRegion::NTSC_J:
return QIcon(QStringLiteral(":/icons/flag-jp.png"));
case ConsoleRegion::PAL:
return QIcon(QStringLiteral(":/icons/flag-eu.png"));
case ConsoleRegion::NTSC_U:
return QIcon(QStringLiteral(":/icons/flag-uc.png"));
default:
return QIcon(QStringLiteral(":/icons/applications-other.png"));
}
}
QIcon GetIconForRegion(DiscRegion region)
{
switch (region)
{
case DiscRegion::NTSC_J:
return QIcon(QStringLiteral(":/icons/flag-jp.png"));
case DiscRegion::PAL:
return QIcon(QStringLiteral(":/icons/flag-eu.png"));
case DiscRegion::NTSC_U:
return QIcon(QStringLiteral(":/icons/flag-uc.png"));
case DiscRegion::Other:
default:
return QIcon(QStringLiteral(":/icons/applications-other.png"));
}
}
QIcon GetIconForEntryType(GameList::EntryType type)
{
switch (type)
{
case GameList::EntryType::Disc:
return QIcon(
QStringLiteral(":/icons/media-optical-24.png")); // QIcon(QStringLiteral(":/icons/media-optical-gear-24.png"))
case GameList::EntryType::Playlist:
return QIcon(QStringLiteral(":/icons/address-book-new-22.png"));
case GameList::EntryType::PSF:
return QIcon(QStringLiteral(":/icons/multimedia-player.png"));
case GameList::EntryType::PSExe:
default:
return QIcon(QStringLiteral(":/icons/applications-system-24.png"));
}
}
} // namespace QtUtils

View File

@ -3,6 +3,7 @@
#include <QtCore/QByteArray>
#include <QtCore/QMetaType>
#include <QtCore/QString>
#include <QtGui/QIcon>
#include <functional>
#include <initializer_list>
#include <optional>
@ -21,6 +22,12 @@ class QVariant;
class QWidget;
class QUrl;
enum class ConsoleRegion;
enum class DiscRegion : u8;
namespace GameList {
enum class EntryType;
}
namespace QtUtils {
/// Wheel delta is 120 as in winapi.
@ -61,9 +68,6 @@ QVariant GetMSAAModeValue(uint multisamples, bool ssaa);
void DecodeMSAAModeValue(const QVariant& userdata, uint* multisamples, bool* ssaa);
void FillComboBoxWithMSAAModes(QComboBox* cb);
/// Fills a combo box with emulation speed options.
void FillComboBoxWithEmulationSpeeds(QComboBox* cb);
/// Prompts for an address in hex.
std::optional<unsigned> PromptForAddress(QWidget* parent, const QString& title, const QString& label, bool code);
@ -79,4 +83,11 @@ void SetWindowResizeable(QWidget* widget, bool resizeable);
/// Adjusts the fixed size for a window if it's not resizeable.
void ResizePotentiallyFixedSizeWindow(QWidget* widget, int width, int height);
/// Returns icon for region.
QIcon GetIconForRegion(ConsoleRegion region);
QIcon GetIconForRegion(DiscRegion region);
/// Returns icon for entry type.
QIcon GetIconForEntryType(GameList::EntryType type);
} // namespace QtUtils

View File

@ -1 +0,0 @@
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24" width="24" height="24"><path fill="none" d="M0 0h24v24H0z"/><path d="M16.05 12.05L21 17l-4.95 4.95-1.414-1.414 2.536-2.537L4 18v-2h13.172l-2.536-2.536 1.414-1.414zm-8.1-10l1.414 1.414L6.828 6 20 6v2H6.828l2.536 2.536L7.95 11.95 3 7l4.95-4.95z" fill="#000000"/></svg>

Before

Width:  |  Height:  |  Size: 326 B

View File

@ -1,6 +0,0 @@
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24">
<g>
<path fill="none" d="M0 0h24v24H0z"/>
<path d="M8 8v8h8V8H8zM6 6h12v12H6V6zm0-4h2v3H6V2zm0 17h2v3H6v-3zM2 6h3v2H2V6zm0 10h3v2H2v-2zM19 6h3v2h-3V6zm0 10h3v2h-3v-2zM16 2h2v3h-2V2zm0 17h2v3h-2v-3z"/>
</g>
</svg>

Before

Width:  |  Height:  |  Size: 298 B

View File

@ -1,6 +0,0 @@
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24">
<g>
<path fill="none" d="M0 0h24v24H0z"/>
<path d="M13 21v2h-2v-2H3a1 1 0 0 1-1-1V4a1 1 0 0 1 1-1h6a3.99 3.99 0 0 1 3 1.354A3.99 3.99 0 0 1 15 3h6a1 1 0 0 1 1 1v16a1 1 0 0 1-1 1h-8zm7-2V5h-5a2 2 0 0 0-2 2v12h7zm-9 0V7a2 2 0 0 0-2-2H4v14h7z"/>
</g>
</svg>

Before

Width:  |  Height:  |  Size: 340 B

View File

@ -1,6 +0,0 @@
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24">
<g>
<path fill="none" d="M0 0h24v24H0z"/>
<path d="M15.456 9.678l-.142-.142a5.475 5.475 0 0 0-2.39-1.349c-2.907-.778-5.699.869-6.492 3.83-.043.16-.066.34-.104.791-.154 1.87-.594 3.265-1.8 4.68 2.26.888 4.938 1.514 6.974 1.514a5.505 5.505 0 0 0 5.31-4.078 5.497 5.497 0 0 0-1.356-5.246zM13.29 6.216l4.939-3.841a1 1 0 0 1 1.32.082l2.995 2.994a1 1 0 0 1 .082 1.321l-3.84 4.938a7.505 7.505 0 0 1-7.283 9.292C8 21.002 3.5 19.5 1 18c3.98-3 3.047-4.81 3.5-6.5 1.058-3.95 4.842-6.257 8.789-5.284zm3.413 1.879c.065.063.13.128.193.194l1.135 1.134 2.475-3.182-1.746-1.746-3.182 2.475 1.125 1.125z"/>
</g>
</svg>

Before

Width:  |  Height:  |  Size: 686 B

View File

@ -1,6 +0,0 @@
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24">
<g>
<path fill="none" d="M0 0h24v24H0z"/>
<path d="M12 10.586l4.95-4.95 1.414 1.414-4.95 4.95 4.95 4.95-1.414 1.414-4.95-4.95-4.95 4.95-1.414-1.414 4.95-4.95-4.95-4.95L7.05 5.636z"/>
</g>
</svg>

Before

Width:  |  Height:  |  Size: 280 B

View File

@ -1,6 +0,0 @@
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24">
<g>
<path fill="none" d="M0 0h24v24H0z"/>
<path d="M13 21V11h8v10h-8zM3 13V3h8v10H3zm6-2V5H5v6h4zM3 21v-6h8v6H3zm2-2h4v-2H5v2zm10 0h4v-6h-4v6zM13 3h8v6h-8V3zm2 2v2h4V5h-4z"/>
</g>
</svg>

Before

Width:  |  Height:  |  Size: 272 B

View File

@ -1,6 +0,0 @@
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24">
<g>
<path fill="none" d="M0 0h24v24H0z"/>
<path d="M15 4.582V12a3 3 0 1 1-2-2.83V2.05c5.053.501 9 4.765 9 9.95 0 5.523-4.477 10-10 10S2 17.523 2 12c0-5.185 3.947-9.449 9-9.95v2.012A8.001 8.001 0 0 0 12 20a8 8 0 0 0 3-15.418z"/>
</g>
</svg>

Before

Width:  |  Height:  |  Size: 325 B

View File

@ -1,6 +0,0 @@
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24">
<g>
<path fill="none" d="M0 0H24V24H0z"/>
<path d="M2 21v-2h2V4.835c0-.484.346-.898.821-.984l9.472-1.722c.326-.06.638.157.697.483.007.035.01.07.01.107v1.28L19 4c.552 0 1 .448 1 1v14h2v2h-4V6h-3v15H2zM13 4.396L6 5.67V19h7V4.396zM12 11v2h-2v-2h2z"/>
</g>
</svg>

Before

Width:  |  Height:  |  Size: 345 B

View File

@ -1,6 +0,0 @@
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24">
<g>
<path fill="none" d="M0 0h24v24H0z"/>
<path d="M13 10h5l-6 6-6-6h5V3h2v7zm-9 9h16v-7h2v8a1 1 0 0 1-1 1H3a1 1 0 0 1-1-1v-8h2v7z"/>
</g>
</svg>

Before

Width:  |  Height:  |  Size: 231 B

View File

@ -1,6 +0,0 @@
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24">
<g>
<path fill="none" d="M0 0h24v24H0z"/>
<path d="M12 22C6.477 22 2 17.523 2 12S6.477 2 12 2s10 4.477 10 10-4.477 10-10 10zm0-2a8 8 0 1 0 0-16 8 8 0 0 0 0 16zm1-9h3l-5 7v-5H8l5-7v5z"/>
</g>
</svg>

Before

Width:  |  Height:  |  Size: 283 B

View File

@ -1,6 +0,0 @@
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24">
<g>
<path fill="none" d="M0 0h24v24H0z"/>
<path d="M7.737 13h8.526L12 6.606 7.737 13zm4.679-9.376l7.066 10.599a.5.5 0 0 1-.416.777H4.934a.5.5 0 0 1-.416-.777l7.066-10.599a.5.5 0 0 1 .832 0zM5 17h14a1 1 0 0 1 0 2H5a1 1 0 0 1 0-2z"/>
</g>
</svg>

Before

Width:  |  Height:  |  Size: 329 B

View File

@ -1,6 +0,0 @@
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24">
<g>
<path fill="none" d="M0 0h24v24H0z"/>
<path d="M15 4H5v16h14V8h-4V4zM3 2.992C3 2.444 3.447 2 3.999 2H16l5 5v13.993A1 1 0 0 1 20.007 22H3.993A1 1 0 0 1 3 21.008V2.992zM11 11V8h2v3h3v2h-3v3h-2v-3H8v-2h3z"/>
</g>
</svg>

Before

Width:  |  Height:  |  Size: 306 B

View File

@ -1,6 +0,0 @@
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24">
<g>
<path fill="none" d="M0 0h24v24H0z"/>
<path d="M9 2.003V2h10.998C20.55 2 21 2.455 21 2.992v18.016a.993.993 0 0 1-.993.992H3.993A1 1 0 0 1 3 20.993V8l6-5.997zM5.83 8H9V4.83L5.83 8zM11 4v5a1 1 0 0 1-1 1H5v10h14V4h-8z"/>
</g>
</svg>

Before

Width:  |  Height:  |  Size: 319 B

View File

@ -1,6 +0,0 @@
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24">
<g>
<path fill="none" d="M0 0h24v24H0z"/>
<path d="M20 22H4a1 1 0 0 1-1-1V3a1 1 0 0 1 1-1h16a1 1 0 0 1 1 1v18a1 1 0 0 1-1 1zm-1-2V4H5v16h14zM8 7h8v2H8V7zm0 4h8v2H8v-2zm0 4h8v2H8v-2z"/>
</g>
</svg>

Before

Width:  |  Height:  |  Size: 282 B

View File

@ -1,6 +0,0 @@
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24">
<g>
<path fill="none" d="M0 0h24v24H0z"/>
<path d="M15 4H5v16h14V8h-4V4zM3 2.992C3 2.444 3.447 2 3.999 2H16l5 5v13.993A1 1 0 0 1 20.007 22H3.993A1 1 0 0 1 3 21.008V2.992zM16 11v2H8v-2h8z"/>
</g>
</svg>

Before

Width:  |  Height:  |  Size: 287 B

View File

@ -1,6 +0,0 @@
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24">
<g>
<path fill="none" d="M0 0h24v24H0z"/>
<path d="M15 4H5v16h14V8h-4V4zM3 2.992C3 2.444 3.447 2 3.999 2H16l5 5v13.993A1 1 0 0 1 20.007 22H3.993A1 1 0 0 1 3 21.008V2.992zm10.529 11.454a4.002 4.002 0 0 1-4.86-6.274 4 4 0 0 1 6.274 4.86l2.21 2.21-1.414 1.415-2.21-2.21zm-.618-2.032a2 2 0 1 0-2.828-2.828 2 2 0 0 0 2.828 2.828z"/>
</g>
</svg>

Before

Width:  |  Height:  |  Size: 425 B

View File

@ -1,6 +0,0 @@
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24">
<g>
<path fill="none" d="M0 0h24v24H0z"/>
<path fill-rule="nonzero" d="M8.595 12.812a3.51 3.51 0 0 1 0-1.623l-.992-.573 1-1.732.992.573A3.496 3.496 0 0 1 11 8.645V7.5h2v1.145c.532.158 1.012.44 1.405.812l.992-.573 1 1.732-.992.573a3.51 3.51 0 0 1 0 1.622l.992.573-1 1.732-.992-.573a3.496 3.496 0 0 1-1.405.812V16.5h-2v-1.145a3.496 3.496 0 0 1-1.405-.812l-.992.573-1-1.732.992-.572zM12 13.5a1.5 1.5 0 1 0 0-3 1.5 1.5 0 0 0 0 3zM15 4H5v16h14V8h-4V4zM3 2.992C3 2.444 3.447 2 3.999 2H16l5 5v13.993A1 1 0 0 1 20.007 22H3.993A1 1 0 0 1 3 21.008V2.992z"/>
</g>
</svg>

Before

Width:  |  Height:  |  Size: 645 B

View File

@ -1,6 +0,0 @@
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24">
<g>
<path fill="none" d="M0 0H24V24H0z"/>
<path d="M16 2v2h-1v3.243c0 1.158.251 2.301.736 3.352l4.282 9.276c.347.753.018 1.644-.734 1.99-.197.092-.411.139-.628.139H5.344c-.828 0-1.5-.672-1.5-1.5 0-.217.047-.432.138-.629l4.282-9.276C8.749 9.545 9 8.401 9 7.243V4H8V2h8zm-2.612 8.001h-2.776c-.104.363-.23.721-.374 1.071l-.158.361L6.125 20h11.749l-3.954-8.567c-.214-.464-.392-.943-.532-1.432zM11 7.243c0 .253-.01.506-.029.758h2.058c-.01-.121-.016-.242-.021-.364L13 7.243V4h-2v3.243z"/>
</g>
</svg>

Before

Width:  |  Height:  |  Size: 580 B

View File

@ -1,6 +0,0 @@
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24">
<g>
<path fill="none" d="M0 0h24v24H0z"/>
<path d="M12.414 5H21a1 1 0 0 1 1 1v14a1 1 0 0 1-1 1H3a1 1 0 0 1-1-1V4a1 1 0 0 1 1-1h7.414l2 2zM4 5v14h16V7h-8.414l-2-2H4zm7 7V9h2v3h3v2h-3v3h-2v-3H8v-2h3z"/>
</g>
</svg>

Before

Width:  |  Height:  |  Size: 298 B

View File

@ -1,6 +0,0 @@
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24">
<g>
<path fill="none" d="M0 0h24v24H0z"/>
<path d="M3 21a1 1 0 0 1-1-1V4a1 1 0 0 1 1-1h7.414l2 2H20a1 1 0 0 1 1 1v3h-2V7h-7.414l-2-2H4v11.998L5.5 11h17l-2.31 9.243a1 1 0 0 1-.97.757H3zm16.938-8H7.062l-1.5 6h12.876l1.5-6z"/>
</g>
</svg>

Before

Width:  |  Height:  |  Size: 321 B

View File

@ -1,6 +0,0 @@
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24">
<g>
<path fill="none" d="M0 0h24v24H0z"/>
<path d="M12.414 5H21a1 1 0 0 1 1 1v14a1 1 0 0 1-1 1H3a1 1 0 0 1-1-1V4a1 1 0 0 1 1-1h7.414l2 2zM4 5v14h16V7h-8.414l-2-2H4zm4 7h8v2H8v-2z"/>
</g>
</svg>

Before

Width:  |  Height:  |  Size: 279 B

View File

@ -1,6 +0,0 @@
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24">
<g>
<path fill="none" d="M0 0h24v24H0z"/>
<path d="M12.414 5H21a1 1 0 0 1 1 1v14a1 1 0 0 1-1 1H3a1 1 0 0 1-1-1V4a1 1 0 0 1 1-1h7.414l2 2zM4 5v14h16V7h-8.414l-2-2H4zm4.591 8.809a3.508 3.508 0 0 1 0-1.622l-.991-.572 1-1.732.991.573a3.495 3.495 0 0 1 1.404-.812V8.5h2v1.144c.532.159 1.01.44 1.403.812l.992-.573 1 1.731-.991.573a3.508 3.508 0 0 1 0 1.622l.991.572-1 1.731-.991-.572a3.495 3.495 0 0 1-1.404.811v1.145h-2V16.35a3.495 3.495 0 0 1-1.404-.811l-.991.572-1-1.73.991-.573zm3.404.688a1.5 1.5 0 1 0 0-2.998 1.5 1.5 0 0 0 0 2.998z"/>
</g>
</svg>

Before

Width:  |  Height:  |  Size: 632 B

View File

@ -1,6 +0,0 @@
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24">
<g>
<path fill="none" d="M0 0h24v24H0z"/>
<path d="M20 3h2v6h-2V5h-4V3h4zM4 3h4v2H4v4H2V3h2zm16 16v-4h2v6h-6v-2h4zM4 19h4v2H2v-6h2v4z"/>
</g>
</svg>

Before

Width:  |  Height:  |  Size: 234 B

View File

@ -1,6 +0,0 @@
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24">
<g>
<path fill="none" d="M0 0h24v24H0z"/>
<path d="M3 3h8v8H3V3zm0 10h8v8H3v-8zM13 3h8v8h-8V3zm0 10h8v8h-8v-8zm2-8v4h4V5h-4zm0 10v4h4v-4h-4zM5 5v4h4V5H5zm0 10v4h4v-4H5z"/>
</g>
</svg>

Before

Width:  |  Height:  |  Size: 269 B

View File

@ -1,6 +0,0 @@
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24">
<g>
<path fill="none" d="M0 0h24v24H0z"/>
<path fill-rule="nonzero" d="M17 4a6 6 0 0 1 6 6v4a6 6 0 0 1-6 6H7a6 6 0 0 1-6-6v-4a6 6 0 0 1 6-6h10zm0 2H7a4 4 0 0 0-3.995 3.8L3 10v4a4 4 0 0 0 3.8 3.995L7 18h10a4 4 0 0 0 3.995-3.8L21 14v-4a4 4 0 0 0-3.8-3.995L17 6zm-7 3v2h2v2H9.999L10 15H8l-.001-2H6v-2h2V9h2zm8 4v2h-2v-2h2zm-2-4v2h-2V9h2z"/>
</g>
</svg>

Before

Width:  |  Height:  |  Size: 435 B

View File

@ -1,6 +0,0 @@
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24">
<g>
<path fill="none" d="M0 0h24v24H0z"/>
<path d="M5 14h14V4H5v10zm0 2v4h14v-4H5zM4 2h16a1 1 0 0 1 1 1v18a1 1 0 0 1-1 1H4a1 1 0 0 1-1-1V3a1 1 0 0 1 1-1zm11 15h2v2h-2v-2z"/>
</g>
</svg>

Before

Width:  |  Height:  |  Size: 271 B

View File

@ -1,6 +0,0 @@
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24">
<g>
<path fill="none" d="M0 0h24v24H0z"/>
<path d="M3 17h18v2H3v-2zm0-6h3v3H3v-3zm5 0h3v3H8v-3zM3 5h3v3H3V5zm10 0h3v3h-3V5zm5 0h3v3h-3V5zm-5 6h3v3h-3v-3zm5 0h3v3h-3v-3zM8 5h3v3H8V5z"/>
</g>
</svg>

Before

Width:  |  Height:  |  Size: 282 B

View File

@ -1,6 +0,0 @@
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24">
<g>
<path fill="none" d="M0 0h24v24H0z"/>
<path fill-rule="nonzero" d="M21 3a1 1 0 0 1 1 1v16a1 1 0 0 1-1 1H3a1 1 0 0 1-1-1V4a1 1 0 0 1 1-1h18zM11 13H4v6h7v-6zm9 0h-7v6h7v-6zm-9-8H4v6h7V5zm9 0h-7v6h7V5z"/>
</g>
</svg>

Before

Width:  |  Height:  |  Size: 303 B

View File

@ -1,6 +0,0 @@
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24">
<g>
<path fill="none" d="M0 0h24v24H0z"/>
<path d="M8 4h13v2H8V4zm-5-.5h3v3H3v-3zm0 7h3v3H3v-3zm0 7h3v3H3v-3zM8 11h13v2H8v-2zm0 7h13v2H8v-2z"/>
</g>
</svg>

Before

Width:  |  Height:  |  Size: 241 B

View File

@ -1,6 +0,0 @@
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24">
<g>
<path fill="none" d="M0 0h24v24H0z"/>
<path d="M6 5h2v14H6V5zm10 0h2v14h-2V5z"/>
</g>
</svg>

Before

Width:  |  Height:  |  Size: 182 B

View File

@ -1,6 +0,0 @@
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24">
<g>
<path fill="none" d="M0 0h24v24H0z"/>
<path d="M16.394 12L10 7.737v8.526L16.394 12zm2.982.416L8.777 19.482A.5.5 0 0 1 8 19.066V4.934a.5.5 0 0 1 .777-.416l10.599 7.066a.5.5 0 0 1 0 .832z"/>
</g>
</svg>

Before

Width:  |  Height:  |  Size: 290 B

View File

@ -1,6 +0,0 @@
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24">
<g>
<path fill="none" d="M0 0h24v24H0z"/>
<path d="M5.463 4.433A9.961 9.961 0 0 1 12 2c5.523 0 10 4.477 10 10 0 2.136-.67 4.116-1.81 5.74L17 12h3A8 8 0 0 0 6.46 6.228l-.997-1.795zm13.074 15.134A9.961 9.961 0 0 1 12 22C6.477 22 2 17.523 2 12c0-2.136.67-4.116 1.81-5.74L7 12H4a8 8 0 0 0 13.54 5.772l.997 1.795z"/>
</g>
</svg>

Before

Width:  |  Height:  |  Size: 409 B

View File

@ -1,6 +0,0 @@
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24">
<g>
<path fill="none" d="M0 0h24v24H0z"/>
<path d="M18.537 19.567A9.961 9.961 0 0 1 12 22C6.477 22 2 17.523 2 12S6.477 2 12 2s10 4.477 10 10c0 2.136-.67 4.116-1.81 5.74L17 12h3a8 8 0 1 0-2.46 5.772l.997 1.795z"/>
</g>
</svg>

Before

Width:  |  Height:  |  Size: 310 B

View File

@ -1,6 +0,0 @@
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24">
<g>
<path fill="none" d="M0 0h24v24H0z"/>
<path d="M18 19h1V6.828L17.172 5H16v4H7V5H5v14h1v-7h12v7zM4 3h14l2.707 2.707a1 1 0 0 1 .293.707V20a1 1 0 0 1-1 1H4a1 1 0 0 1-1-1V4a1 1 0 0 1 1-1zm4 11v5h8v-5H8z"/>
</g>
</svg>

Before

Width:  |  Height:  |  Size: 303 B

View File

@ -1,6 +0,0 @@
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24">
<g>
<path fill="none" d="M0 0h24v24H0z"/>
<path d="M3 3h2v2H3V3zm4 0h2v2H7V3zm4 0h2v2h-2V3zm4 0h2v2h-2V3zm4 0h2v2h-2V3zm0 4h2v2h-2V7zM3 19h2v2H3v-2zm0-4h2v2H3v-2zm0-4h2v2H3v-2zm0-4h2v2H3V7zm7.667 4l1.036-1.555A1 1 0 0 1 12.535 9h2.93a1 1 0 0 1 .832.445L17.333 11H20a1 1 0 0 1 1 1v8a1 1 0 0 1-1 1H8a1 1 0 0 1-1-1v-8a1 1 0 0 1 1-1h2.667zM9 19h10v-6h-2.737l-1.333-2h-1.86l-1.333 2H9v6zm5-1a2 2 0 1 1 0-4 2 2 0 0 1 0 4z"/>
</g>
</svg>

Before

Width:  |  Height:  |  Size: 516 B

View File

@ -1,6 +0,0 @@
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24">
<g>
<path fill="none" d="M0 0h24v24H0z"/>
<path d="M6 7.828V20h12V4H9.828L6 7.828zm-1.707-1.12L9 2h10a1 1 0 0 1 1 1v18a1 1 0 0 1-1 1H5a1 1 0 0 1-1-1V7.414a1 1 0 0 1 .293-.707zM15 5h2v4h-2V5zm-3 0h2v4h-2V5zM9 6h2v3H9V6z"/>
</g>
</svg>

Before

Width:  |  Height:  |  Size: 319 B

View File

@ -1,6 +0,0 @@
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24">
<g>
<path fill="none" d="M0 0h24v24H0z"/>
<path d="M3.34 17a10.018 10.018 0 0 1-.978-2.326 3 3 0 0 0 .002-5.347A9.99 9.99 0 0 1 4.865 4.99a3 3 0 0 0 4.631-2.674 9.99 9.99 0 0 1 5.007.002 3 3 0 0 0 4.632 2.672c.579.59 1.093 1.261 1.525 2.01.433.749.757 1.53.978 2.326a3 3 0 0 0-.002 5.347 9.99 9.99 0 0 1-2.501 4.337 3 3 0 0 0-4.631 2.674 9.99 9.99 0 0 1-5.007-.002 3 3 0 0 0-4.632-2.672A10.018 10.018 0 0 1 3.34 17zm5.66.196a4.993 4.993 0 0 1 2.25 2.77c.499.047 1 .048 1.499.001A4.993 4.993 0 0 1 15 17.197a4.993 4.993 0 0 1 3.525-.565c.29-.408.54-.843.748-1.298A4.993 4.993 0 0 1 18 12c0-1.26.47-2.437 1.273-3.334a8.126 8.126 0 0 0-.75-1.298A4.993 4.993 0 0 1 15 6.804a4.993 4.993 0 0 1-2.25-2.77c-.499-.047-1-.048-1.499-.001A4.993 4.993 0 0 1 9 6.803a4.993 4.993 0 0 1-3.525.565 7.99 7.99 0 0 0-.748 1.298A4.993 4.993 0 0 1 6 12c0 1.26-.47 2.437-1.273 3.334a8.126 8.126 0 0 0 .75 1.298A4.993 4.993 0 0 1 9 17.196zM12 15a3 3 0 1 1 0-6 3 3 0 0 1 0 6zm0-2a1 1 0 1 0 0-2 1 1 0 0 0 0 2z"/>
</g>
</svg>

Before

Width:  |  Height:  |  Size: 1.1 KiB

View File

@ -1,6 +0,0 @@
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24">
<g>
<path fill="none" d="M0 0h24v24H0z"/>
<path d="M6.265 3.807l1.147 1.639a8 8 0 1 0 9.176 0l1.147-1.639A9.988 9.988 0 0 1 22 12c0 5.523-4.477 10-10 10S2 17.523 2 12a9.988 9.988 0 0 1 4.265-8.193zM11 12V2h2v10h-2z"/>
</g>
</svg>

Before

Width:  |  Height:  |  Size: 315 B

View File

@ -1,6 +0,0 @@
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24">
<g>
<path fill="none" d="M0 0h24v24H0z"/>
<path d="M2 4c0-.552.455-1 .992-1h18.016c.548 0 .992.445.992 1v14c0 .552-.455 1-.992 1H2.992A.994.994 0 0 1 2 18V4zm2 1v12h16V5H4zm1 15h14v2H5v-2z"/>
</g>
</svg>

Before

Width:  |  Height:  |  Size: 289 B

View File

@ -1,6 +0,0 @@
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24">
<g>
<path fill="none" d="M0 0h24v24H0z"/>
<path d="M10 7.22L6.603 10H3v4h3.603L10 16.78V7.22zM5.889 16H2a1 1 0 0 1-1-1V9a1 1 0 0 1 1-1h3.889l5.294-4.332a.5.5 0 0 1 .817.387v15.89a.5.5 0 0 1-.817.387L5.89 16zm13.517 4.134l-1.416-1.416A8.978 8.978 0 0 0 21 12a8.982 8.982 0 0 0-3.304-6.968l1.42-1.42A10.976 10.976 0 0 1 23 12c0 3.223-1.386 6.122-3.594 8.134zm-3.543-3.543l-1.422-1.422A3.993 3.993 0 0 0 16 12c0-1.43-.75-2.685-1.88-3.392l1.439-1.439A5.991 5.991 0 0 1 18 12c0 1.842-.83 3.49-2.137 4.591z"/>
</g>
</svg>

Before

Width:  |  Height:  |  Size: 601 B

View File

@ -1,6 +0,0 @@
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24">
<g>
<path fill="none" d="M0 0h24v24H0z"/>
<path d="M3 3h18a1 1 0 0 1 1 1v16a1 1 0 0 1-1 1H3a1 1 0 0 1-1-1V4a1 1 0 0 1 1-1zm17 8H4v8h16v-8zm0-2V5H4v4h16zm-5-3h4v2h-4V6z"/>
</g>
</svg>

Before

Width:  |  Height:  |  Size: 268 B

View File

@ -1 +0,0 @@
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24" width="24" height="24"><path fill="none" d="M0 0h24v24H0z"/><path d="M16.05 12.05L21 17l-4.95 4.95-1.414-1.414 2.536-2.537L4 18v-2h13.172l-2.536-2.536 1.414-1.414zm-8.1-10l1.414 1.414L6.828 6 20 6v2H6.828l2.536 2.536L7.95 11.95 3 7l4.95-4.95z" fill="#ffffff"/></svg>

Before

Width:  |  Height:  |  Size: 326 B

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