Compare commits
9 Commits
70c45f7cf8
...
02234715c5
Author | SHA1 | Date |
---|---|---|
![]() |
02234715c5 | |
![]() |
128bab29f8 | |
![]() |
4dd6365a99 | |
![]() |
28f6358ec5 | |
![]() |
4da7692a2d | |
![]() |
1b678d0ebc | |
![]() |
224cb6ac8e | |
![]() |
231ba050a2 | |
![]() |
e36dbaf255 |
|
@ -2228,26 +2228,6 @@ bool Achievements::ConfirmSystemReset()
|
|||
return true;
|
||||
}
|
||||
|
||||
bool Achievements::ConfirmHardcoreModeDisable(const char* trigger)
|
||||
{
|
||||
#ifdef ENABLE_RAINTEGRATION
|
||||
if (IsUsingRAIntegration())
|
||||
return (RA_WarnDisableHardcore(trigger) != 0);
|
||||
#endif
|
||||
|
||||
// I really hope this doesn't deadlock :/
|
||||
const bool confirmed = Host::ConfirmMessage(
|
||||
TRANSLATE("Achievements", "Confirm Hardcore Mode"),
|
||||
fmt::format(TRANSLATE_FS("Achievements", "{0} cannot be performed while hardcore mode is active. Do you "
|
||||
"want to disable hardcore mode? {0} will be cancelled if you select No."),
|
||||
trigger));
|
||||
if (!confirmed)
|
||||
return false;
|
||||
|
||||
DisableHardcoreMode();
|
||||
return true;
|
||||
}
|
||||
|
||||
void Achievements::ConfirmHardcoreModeDisableAsync(const char* trigger, std::function<void(bool)> callback)
|
||||
{
|
||||
auto real_callback = [callback = std::move(callback)](bool res) mutable {
|
||||
|
@ -2259,43 +2239,21 @@ void Achievements::ConfirmHardcoreModeDisableAsync(const char* trigger, std::fun
|
|||
});
|
||||
};
|
||||
|
||||
#ifndef __ANDROID__
|
||||
#ifdef ENABLE_RAINTEGRATION
|
||||
if (IsUsingRAIntegration())
|
||||
{
|
||||
const bool result = (RA_WarnDisableHardcore(trigger) != 0);
|
||||
callback(result);
|
||||
real_callback(result);
|
||||
return;
|
||||
}
|
||||
#endif
|
||||
|
||||
GPUThread::RunOnThread([trigger = std::string(trigger), real_callback = std::move(real_callback)]() mutable {
|
||||
if (!FullscreenUI::Initialize())
|
||||
{
|
||||
Host::AddOSDMessage(
|
||||
fmt::format(TRANSLATE_FS("Achievements", "Cannot {} while hardcode mode is active."), trigger),
|
||||
Host::OSD_WARNING_DURATION);
|
||||
real_callback(false);
|
||||
return;
|
||||
}
|
||||
|
||||
ImGuiFullscreen::OpenConfirmMessageDialog(
|
||||
TRANSLATE_STR("Achievements", "Confirm Hardcore Mode"),
|
||||
fmt::format(TRANSLATE_FS("Achievements",
|
||||
"{0} cannot be performed while hardcore mode is active. Do you "
|
||||
"want to disable hardcore mode? {0} will be cancelled if you select No."),
|
||||
trigger),
|
||||
std::move(real_callback), fmt::format(ICON_FA_CHECK " {}", TRANSLATE_SV("Achievements", "Yes")),
|
||||
fmt::format(ICON_FA_TIMES " {}", TRANSLATE_SV("Achievements", "No")));
|
||||
});
|
||||
#else
|
||||
Host::ConfirmMessageAsync(
|
||||
TRANSLATE_STR("Achievements", "Confirm Hardcore Mode"),
|
||||
fmt::format(TRANSLATE_FS("Achievements", "{0} cannot be performed while hardcore mode is active. Do you want to "
|
||||
"disable hardcore mode? {0} will be cancelled if you select No."),
|
||||
trigger),
|
||||
std::move(real_callback));
|
||||
#endif
|
||||
}
|
||||
|
||||
void Achievements::ClearUIState()
|
||||
|
|
|
@ -116,7 +116,6 @@ bool ResetHardcoreMode(bool is_booting);
|
|||
void DisableHardcoreMode();
|
||||
|
||||
/// Prompts the user to disable hardcore mode, if they agree, returns true.
|
||||
bool ConfirmHardcoreModeDisable(const char* trigger);
|
||||
void ConfirmHardcoreModeDisableAsync(const char* trigger, std::function<void(bool)> callback);
|
||||
|
||||
/// Returns true if hardcore mode is active, and functionality should be restricted.
|
||||
|
|
|
@ -97,6 +97,7 @@ using ImGuiFullscreen::BeginFullscreenWindow;
|
|||
using ImGuiFullscreen::BeginHorizontalMenu;
|
||||
using ImGuiFullscreen::BeginMenuButtons;
|
||||
using ImGuiFullscreen::BeginNavBar;
|
||||
using ImGuiFullscreen::CancelPendingMenuClose;
|
||||
using ImGuiFullscreen::CenterImage;
|
||||
using ImGuiFullscreen::DefaultActiveButton;
|
||||
using ImGuiFullscreen::DrawShadowedText;
|
||||
|
@ -209,7 +210,6 @@ struct PostProcessingStageInfo
|
|||
//////////////////////////////////////////////////////////////////////////
|
||||
// Main
|
||||
//////////////////////////////////////////////////////////////////////////
|
||||
static void UpdateRunIdleState();
|
||||
static void PauseForMenuOpen(bool set_pause_menu_open);
|
||||
static bool AreAnyDialogsOpen();
|
||||
static void ClosePauseMenu();
|
||||
|
@ -705,7 +705,8 @@ bool FullscreenUI::AreAnyDialogsOpen()
|
|||
{
|
||||
return (s_state.save_state_selector_open || s_state.about_window_open ||
|
||||
s_state.input_binding_type != InputBindingInfo::Type::Unknown || ImGuiFullscreen::IsChoiceDialogOpen() ||
|
||||
ImGuiFullscreen::IsFileSelectorOpen());
|
||||
ImGuiFullscreen::IsInputDialogOpen() || ImGuiFullscreen::IsFileSelectorOpen() ||
|
||||
ImGuiFullscreen::IsMessageBoxDialogOpen());
|
||||
}
|
||||
|
||||
void FullscreenUI::CheckForConfigChanges(const GPUSettings& old_settings)
|
||||
|
@ -719,7 +720,7 @@ void FullscreenUI::UpdateRunIdleState()
|
|||
GPUThread::SetRunIdleReason(GPUThread::RunIdleReason::FullscreenUIActive, new_run_idle);
|
||||
}
|
||||
|
||||
void FullscreenUI::OnSystemStarted()
|
||||
void FullscreenUI::OnSystemStarting()
|
||||
{
|
||||
// NOTE: Called on CPU thread.
|
||||
if (!IsInitialized())
|
||||
|
@ -854,24 +855,18 @@ void FullscreenUI::FixStateIfPaused()
|
|||
|
||||
void FullscreenUI::ClosePauseMenu()
|
||||
{
|
||||
if (!System::IsValid())
|
||||
if (!GPUThread::HasGPUBackend())
|
||||
return;
|
||||
|
||||
const bool paused = System::IsPaused();
|
||||
GPUThread::RunOnThread([paused]() {
|
||||
if (!IsInitialized())
|
||||
return;
|
||||
if (GPUThread::IsSystemPaused() && !s_state.was_paused_on_quick_menu_open)
|
||||
Host::RunOnCPUThread([]() { System::PauseSystem(false); });
|
||||
|
||||
if (paused && !s_state.was_paused_on_quick_menu_open)
|
||||
Host::RunOnCPUThread([]() { System::PauseSystem(false); });
|
||||
|
||||
s_state.current_main_window = MainWindowType::None;
|
||||
s_state.current_pause_submenu = PauseSubMenu::None;
|
||||
s_state.pause_menu_was_open = false;
|
||||
QueueResetFocus(FocusResetType::ViewChanged);
|
||||
UpdateRunIdleState();
|
||||
FixStateIfPaused();
|
||||
});
|
||||
s_state.current_main_window = MainWindowType::None;
|
||||
s_state.current_pause_submenu = PauseSubMenu::None;
|
||||
s_state.pause_menu_was_open = false;
|
||||
QueueResetFocus(FocusResetType::ViewChanged);
|
||||
UpdateRunIdleState();
|
||||
FixStateIfPaused();
|
||||
}
|
||||
|
||||
void FullscreenUI::OpenPauseSubMenu(PauseSubMenu submenu)
|
||||
|
@ -1114,31 +1109,27 @@ void FullscreenUI::DoStartPath(std::string path, std::string state, std::optiona
|
|||
if (GPUThread::HasGPUBackend())
|
||||
return;
|
||||
|
||||
// Switch to nothing, we'll get called back via OnSystemDestroyed() if startup fails.
|
||||
const MainWindowType prev_main_window = std::exchange(s_state.current_main_window, MainWindowType::None);
|
||||
QueueResetFocus(FocusResetType::ViewChanged);
|
||||
// Stop running idle to prevent game list from being redrawn until we know if startup succeeded.
|
||||
GPUThread::SetRunIdleReason(GPUThread::RunIdleReason::FullscreenUIActive, false);
|
||||
|
||||
SystemBootParameters params;
|
||||
params.filename = std::move(path);
|
||||
params.save_state = std::move(state);
|
||||
params.override_fast_boot = std::move(fast_boot);
|
||||
Host::RunOnCPUThread([params = std::move(params), prev_main_window]() {
|
||||
Host::RunOnCPUThread([params = std::move(params)]() mutable {
|
||||
if (System::IsValid())
|
||||
return;
|
||||
|
||||
// This can "fail" if HC mode is enabled and the user cancels, or other startup cancel paths.
|
||||
Error error;
|
||||
if (!System::BootSystem(std::move(params), &error))
|
||||
{
|
||||
GPUThread::RunOnThread([error_desc = error.TakeDescription(), prev_main_window]() {
|
||||
GPUThread::RunOnThread([error_desc = error.TakeDescription()]() {
|
||||
if (!IsInitialized())
|
||||
return;
|
||||
|
||||
OpenInfoMessageDialog(TRANSLATE_STR("System", "Error"),
|
||||
fmt::format(TRANSLATE_FS("System", "Failed to boot system: {}"), error_desc));
|
||||
// ReturnToPreviousWindow();
|
||||
s_state.current_main_window = prev_main_window;
|
||||
QueueResetFocus(FocusResetType::ViewChanged);
|
||||
UpdateRunIdleState();
|
||||
});
|
||||
}
|
||||
|
@ -1247,21 +1238,25 @@ void FullscreenUI::ConfirmIfSavingMemoryCards(std::string action, std::function<
|
|||
|
||||
void FullscreenUI::RequestShutdown(bool save_state)
|
||||
{
|
||||
s_state.current_main_window = MainWindowType::None;
|
||||
|
||||
ConfirmIfSavingMemoryCards(FSUI_STR("shut down"), [save_state](bool result) {
|
||||
if (result)
|
||||
Host::RunOnCPUThread([save_state]() { Host::RequestSystemShutdown(false, save_state); });
|
||||
else
|
||||
ClosePauseMenu();
|
||||
|
||||
ClosePauseMenu();
|
||||
});
|
||||
}
|
||||
|
||||
void FullscreenUI::RequestReset()
|
||||
{
|
||||
s_state.current_main_window = MainWindowType::None;
|
||||
|
||||
ConfirmIfSavingMemoryCards(FSUI_STR("reset"), [](bool result) {
|
||||
if (result)
|
||||
Host::RunOnCPUThread(System::ResetSystem);
|
||||
else
|
||||
ClosePauseMenu();
|
||||
|
||||
ClosePauseMenu();
|
||||
});
|
||||
}
|
||||
|
||||
|
@ -1298,7 +1293,7 @@ void FullscreenUI::StartChangeDiscFromFile()
|
|||
}
|
||||
}
|
||||
|
||||
ReturnToPreviousWindow();
|
||||
ReturnToMainWindow();
|
||||
});
|
||||
};
|
||||
|
||||
|
@ -2198,6 +2193,7 @@ void FullscreenUI::DrawInputBindingButton(SettingsInterface* bsi, InputBindingIn
|
|||
}
|
||||
else if (ImGui::IsItemClicked(ImGuiMouseButton_Right) || ImGui::IsKeyPressed(ImGuiKey_NavGamepadMenu, false))
|
||||
{
|
||||
CancelPendingMenuClose();
|
||||
bsi->DeleteValue(section, name);
|
||||
SetSettingsChanged(bsi);
|
||||
}
|
||||
|
@ -6062,15 +6058,16 @@ void FullscreenUI::DrawAchievementsLoginWindow()
|
|||
|
||||
ImGui::SetCursorPosX((ImGui::GetWindowWidth() - item_width) * 0.5f);
|
||||
ImGui::SetNextItemWidth(item_width);
|
||||
ImGui::InputTextWithHint("##username", FSUI_CSTR("User Name"), username, sizeof(username), is_logging_in ? ImGuiInputTextFlags_ReadOnly : 0);
|
||||
ImGui::InputTextWithHint("##username", FSUI_CSTR("User Name"), username, sizeof(username),
|
||||
is_logging_in ? ImGuiInputTextFlags_ReadOnly : 0);
|
||||
ImGui::NextColumn();
|
||||
|
||||
ImGui::SetCursorPosX((ImGui::GetWindowWidth() - item_width) * 0.5f);
|
||||
ImGui::SetCursorPosY(ImGui::GetCursorPosY() + item_margin);
|
||||
ImGui::SetNextItemWidth(item_width);
|
||||
ImGui::InputTextWithHint("##password", FSUI_CSTR("Password"), password, sizeof(password),
|
||||
is_logging_in ? (ImGuiInputTextFlags_ReadOnly | ImGuiInputTextFlags_Password) :
|
||||
ImGuiInputTextFlags_Password);
|
||||
is_logging_in ? (ImGuiInputTextFlags_ReadOnly | ImGuiInputTextFlags_Password) :
|
||||
ImGuiInputTextFlags_Password);
|
||||
|
||||
ImGui::SetCursorPosY(ImGui::GetCursorPosY() + item_margin);
|
||||
|
||||
|
@ -6682,10 +6679,7 @@ void FullscreenUI::DrawPauseMenu()
|
|||
OpenPauseSubMenu(PauseSubMenu::None);
|
||||
|
||||
if (ActiveButton(FSUI_ICONSTR(ICON_FA_SYNC, "Reset System"), false))
|
||||
{
|
||||
ClosePauseMenu();
|
||||
RequestReset();
|
||||
}
|
||||
|
||||
if (ActiveButton(FSUI_ICONSTR(ICON_FA_SAVE, "Exit And Save State"), false))
|
||||
RequestShutdown(true);
|
||||
|
@ -7154,6 +7148,7 @@ void FullscreenUI::DrawSaveStateSelector(bool is_loading)
|
|||
(ImGui::IsItemClicked(ImGuiMouseButton_Right) ||
|
||||
ImGui::IsKeyPressed(ImGuiKey_NavGamepadInput, false) || ImGui::IsKeyPressed(ImGuiKey_F1, false)))
|
||||
{
|
||||
CancelPendingMenuClose();
|
||||
s_state.save_state_selector_submenu_index = static_cast<s32>(i);
|
||||
}
|
||||
}
|
||||
|
@ -7670,6 +7665,7 @@ void FullscreenUI::DrawGameList(const ImVec2& heading_size)
|
|||
(ImGui::IsItemClicked(ImGuiMouseButton_Right) || ImGui::IsKeyPressed(ImGuiKey_NavGamepadInput, false) ||
|
||||
ImGui::IsKeyPressed(ImGuiKey_F3, false)))
|
||||
{
|
||||
CancelPendingMenuClose();
|
||||
HandleGameListOptions(selected_entry);
|
||||
}
|
||||
}
|
||||
|
@ -7932,6 +7928,7 @@ void FullscreenUI::DrawGameGrid(const ImVec2& heading_size)
|
|||
(ImGui::IsItemClicked(ImGuiMouseButton_Right) || ImGui::IsKeyPressed(ImGuiKey_NavGamepadInput, false) ||
|
||||
ImGui::IsKeyPressed(ImGuiKey_F3, false)))
|
||||
{
|
||||
CancelPendingMenuClose();
|
||||
HandleGameListOptions(entry);
|
||||
}
|
||||
}
|
||||
|
|
|
@ -20,7 +20,7 @@ bool Initialize();
|
|||
bool IsInitialized();
|
||||
bool HasActiveWindow();
|
||||
void CheckForConfigChanges(const GPUSettings& old_settings);
|
||||
void OnSystemStarted();
|
||||
void OnSystemStarting();
|
||||
void OnSystemResumed();
|
||||
void OnSystemDestroyed();
|
||||
void OnRunningGameChanged(const std::string& path, const std::string& serial, const std::string& title);
|
||||
|
@ -34,6 +34,7 @@ void OpenLeaderboardsWindow();
|
|||
void ReturnToMainWindow();
|
||||
void ReturnToPreviousWindow();
|
||||
void SetStandardSelectionFooterText(bool back_instead_of_cancel);
|
||||
void UpdateRunIdleState();
|
||||
#endif
|
||||
|
||||
void Shutdown(bool clear_state);
|
||||
|
|
|
@ -716,7 +716,7 @@ void GameDatabase::Entry::ApplySettings(Settings& settings, bool display_osd_mes
|
|||
"GameDBCompatibility", ICON_EMOJI_INFORMATION,
|
||||
fmt::format("{}{}", TRANSLATE_SV("GameDatabase", "Compatibility settings for this game have been applied."),
|
||||
messages.view()),
|
||||
Host::OSD_WARNING_DURATION);
|
||||
Host::OSD_INFO_DURATION);
|
||||
}
|
||||
|
||||
#undef APPEND_MESSAGE_FMT
|
||||
|
|
|
@ -74,8 +74,9 @@ static bool SleepGPUThread(bool allow_sleep);
|
|||
static bool CreateDeviceOnThread(RenderAPI api, bool fullscreen, bool clear_fsui_state_on_failure, Error* error);
|
||||
static void DestroyDeviceOnThread(bool clear_fsui_state);
|
||||
static void ResizeDisplayWindowOnThread(u32 width, u32 height, float scale);
|
||||
static void UpdateDisplayWindowOnThread(bool fullscreen);
|
||||
static void UpdateDisplayWindowOnThread(bool fullscreen, bool allow_exclusive_fullscreen);
|
||||
static void DisplayWindowResizedOnThread();
|
||||
static bool CheckExclusiveFullscreenOnThread();
|
||||
|
||||
static void ReconfigureOnThread(GPUThreadReconfigureCommand* cmd);
|
||||
static bool CreateGPUBackendOnThread(GPURenderer renderer, bool upload_vram, Error* error);
|
||||
|
@ -725,6 +726,11 @@ bool GPUThread::CreateDeviceOnThread(RenderAPI api, bool fullscreen, bool clear_
|
|||
|
||||
std::atomic_thread_fence(std::memory_order_release);
|
||||
UpdateRunIdle();
|
||||
|
||||
// Switch to borderless if exclusive failed.
|
||||
if (fullscreen_mode.has_value() && !CheckExclusiveFullscreenOnThread())
|
||||
UpdateDisplayWindowOnThread(true, false);
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
|
@ -1145,7 +1151,7 @@ void GPUThread::ResizeDisplayWindowOnThread(u32 width, u32 height, float scale)
|
|||
if (!g_gpu_device->GetMainSwapChain()->ResizeBuffers(width, height, scale, &error))
|
||||
{
|
||||
ERROR_LOG("Failed to resize main swap chain: {}", error.GetDescription());
|
||||
UpdateDisplayWindowOnThread(Host::IsFullscreen());
|
||||
UpdateDisplayWindowOnThread(Host::IsFullscreen(), true);
|
||||
return;
|
||||
}
|
||||
|
||||
|
@ -1154,20 +1160,22 @@ void GPUThread::ResizeDisplayWindowOnThread(u32 width, u32 height, float scale)
|
|||
|
||||
void GPUThread::UpdateDisplayWindow(bool fullscreen)
|
||||
{
|
||||
RunOnThread([fullscreen]() { UpdateDisplayWindowOnThread(fullscreen); });
|
||||
RunOnThread([fullscreen]() { UpdateDisplayWindowOnThread(fullscreen, true); });
|
||||
}
|
||||
|
||||
void GPUThread::UpdateDisplayWindowOnThread(bool fullscreen)
|
||||
void GPUThread::UpdateDisplayWindowOnThread(bool fullscreen, bool allow_exclusive_fullscreen)
|
||||
{
|
||||
// In case we get the event late.
|
||||
if (!g_gpu_device)
|
||||
return;
|
||||
|
||||
bool exclusive_fullscreen_requested = false;
|
||||
std::optional<GPUDevice::ExclusiveFullscreenMode> fullscreen_mode;
|
||||
if (fullscreen && g_gpu_device->GetFeatures().exclusive_fullscreen)
|
||||
if (allow_exclusive_fullscreen && fullscreen && g_gpu_device->GetFeatures().exclusive_fullscreen)
|
||||
{
|
||||
fullscreen_mode =
|
||||
GPUDevice::ExclusiveFullscreenMode::Parse(Host::GetTinyStringSettingValue("GPU", "FullscreenMode", ""));
|
||||
exclusive_fullscreen_requested = fullscreen_mode.has_value();
|
||||
}
|
||||
std::optional<bool> exclusive_fullscreen_control;
|
||||
if (g_settings.display_exclusive_fullscreen_control != DisplayExclusiveFullscreenControl::Automatic)
|
||||
|
@ -1180,7 +1188,7 @@ void GPUThread::UpdateDisplayWindowOnThread(bool fullscreen)
|
|||
|
||||
Error error;
|
||||
std::optional<WindowInfo> wi =
|
||||
Host::AcquireRenderWindow(g_gpu_device->GetRenderAPI(), fullscreen, fullscreen_mode.has_value(), &error);
|
||||
Host::AcquireRenderWindow(g_gpu_device->GetRenderAPI(), fullscreen, exclusive_fullscreen_requested, &error);
|
||||
if (!wi.has_value())
|
||||
{
|
||||
Host::ReportFatalError("Failed to get render window after update", error.GetDescription());
|
||||
|
@ -1205,9 +1213,28 @@ void GPUThread::UpdateDisplayWindowOnThread(bool fullscreen)
|
|||
ERROR_LOG("Failed to switch to surfaceless, rendering commands may fail: {}", error.GetDescription());
|
||||
}
|
||||
|
||||
// If exclusive fullscreen failed, switch to borderless fullscreen.
|
||||
if (exclusive_fullscreen_requested && !CheckExclusiveFullscreenOnThread())
|
||||
{
|
||||
UpdateDisplayWindowOnThread(true, false);
|
||||
return;
|
||||
}
|
||||
|
||||
DisplayWindowResizedOnThread();
|
||||
}
|
||||
|
||||
bool GPUThread::CheckExclusiveFullscreenOnThread()
|
||||
{
|
||||
if (g_gpu_device->HasMainSwapChain() && g_gpu_device->GetMainSwapChain()->IsExclusiveFullscreen())
|
||||
return true;
|
||||
|
||||
Host::AddIconOSDWarning(
|
||||
"ExclusiveFullscreenFailed", ICON_EMOJI_WARNING,
|
||||
TRANSLATE_STR("OSDMessage", "Failed to switch to exclusive fullscreen, using borderless instead."),
|
||||
Host::OSD_INFO_DURATION);
|
||||
return false;
|
||||
}
|
||||
|
||||
void GPUThread::DisplayWindowResizedOnThread()
|
||||
{
|
||||
const GPUSwapChain* swap_chain = g_gpu_device->GetMainSwapChain();
|
||||
|
|
|
@ -223,7 +223,6 @@ static void DoRewind();
|
|||
|
||||
static bool DoRunahead();
|
||||
|
||||
static bool OpenGPUDump(std::string path, Error* error);
|
||||
static bool ChangeGPUDump(std::string new_path);
|
||||
|
||||
static void UpdateSessionTime(const std::string& prev_serial);
|
||||
|
@ -1657,18 +1656,13 @@ bool System::BootSystem(SystemBootParameters parameters, Error* error)
|
|||
else
|
||||
INFO_LOG("Boot Filename: {}", parameters.filename);
|
||||
|
||||
Assert(s_state.state == State::Shutdown);
|
||||
s_state.state = State::Starting;
|
||||
s_state.startup_cancelled.store(false, std::memory_order_relaxed);
|
||||
s_state.region = g_settings.region;
|
||||
std::atomic_thread_fence(std::memory_order_release);
|
||||
Host::OnSystemStarting();
|
||||
|
||||
// Load CD image up and detect region.
|
||||
std::unique_ptr<CDImage> disc;
|
||||
ConsoleRegion console_region = ConsoleRegion::Auto;
|
||||
DiscRegion disc_region = DiscRegion::NonPS1;
|
||||
BootMode boot_mode = BootMode::FullBoot;
|
||||
std::string exe_override;
|
||||
std::unique_ptr<GPUDump::Player> gpu_dump;
|
||||
if (!parameters.filename.empty())
|
||||
{
|
||||
if (IsExePath(parameters.filename))
|
||||
|
@ -1683,24 +1677,22 @@ bool System::BootSystem(SystemBootParameters parameters, Error* error)
|
|||
}
|
||||
else if (IsGPUDumpPath(parameters.filename))
|
||||
{
|
||||
if (!OpenGPUDump(parameters.filename, error))
|
||||
{
|
||||
DestroySystem();
|
||||
gpu_dump = GPUDump::Player::Open(parameters.filename, error);
|
||||
if (!gpu_dump)
|
||||
return false;
|
||||
}
|
||||
|
||||
boot_mode = BootMode::ReplayGPUDump;
|
||||
}
|
||||
|
||||
if (boot_mode == BootMode::BootEXE || boot_mode == BootMode::BootPSF)
|
||||
{
|
||||
if (s_state.region == ConsoleRegion::Auto)
|
||||
if (console_region == ConsoleRegion::Auto)
|
||||
{
|
||||
const DiscRegion file_region =
|
||||
((boot_mode == BootMode::BootEXE) ? GetRegionForExe(parameters.filename.c_str()) :
|
||||
GetRegionForPsf(parameters.filename.c_str()));
|
||||
INFO_LOG("EXE/PSF Region: {}", Settings::GetDiscRegionDisplayName(file_region));
|
||||
s_state.region = GetConsoleRegionForDiscRegion(file_region);
|
||||
console_region = GetConsoleRegionForDiscRegion(file_region);
|
||||
}
|
||||
}
|
||||
else if (boot_mode != BootMode::ReplayGPUDump)
|
||||
|
@ -1710,25 +1702,24 @@ bool System::BootSystem(SystemBootParameters parameters, Error* error)
|
|||
if (!disc)
|
||||
{
|
||||
Error::AddPrefixFmt(error, "Failed to open CD image '{}':\n", Path::GetFileName(parameters.filename));
|
||||
DestroySystem();
|
||||
return false;
|
||||
}
|
||||
|
||||
disc_region = GameList::GetCustomRegionForPath(parameters.filename).value_or(GetRegionForImage(disc.get()));
|
||||
if (s_state.region == ConsoleRegion::Auto)
|
||||
if (console_region == ConsoleRegion::Auto)
|
||||
{
|
||||
if (disc_region != DiscRegion::Other)
|
||||
{
|
||||
s_state.region = GetConsoleRegionForDiscRegion(disc_region);
|
||||
console_region = GetConsoleRegionForDiscRegion(disc_region);
|
||||
INFO_LOG("Auto-detected console {} region for '{}' (region {})",
|
||||
Settings::GetConsoleRegionName(s_state.region), parameters.filename,
|
||||
Settings::GetConsoleRegionName(console_region), parameters.filename,
|
||||
Settings::GetDiscRegionName(disc_region));
|
||||
}
|
||||
else
|
||||
{
|
||||
s_state.region = ConsoleRegion::NTSC_U;
|
||||
console_region = ConsoleRegion::NTSC_U;
|
||||
WARNING_LOG("Could not determine console region for disc region {}. Defaulting to {}.",
|
||||
Settings::GetDiscRegionName(disc_region), Settings::GetConsoleRegionName(s_state.region));
|
||||
Settings::GetDiscRegionName(disc_region), Settings::GetConsoleRegionName(console_region));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -1736,21 +1727,60 @@ bool System::BootSystem(SystemBootParameters parameters, Error* error)
|
|||
else
|
||||
{
|
||||
// Default to NTSC for BIOS boot.
|
||||
if (s_state.region == ConsoleRegion::Auto)
|
||||
s_state.region = ConsoleRegion::NTSC_U;
|
||||
if (console_region == ConsoleRegion::Auto)
|
||||
console_region = ConsoleRegion::NTSC_U;
|
||||
}
|
||||
|
||||
INFO_LOG("Console Region: {}", Settings::GetConsoleRegionDisplayName(s_state.region));
|
||||
INFO_LOG("Console Region: {}", Settings::GetConsoleRegionDisplayName(console_region));
|
||||
|
||||
// Switch subimage.
|
||||
if (disc && parameters.media_playlist_index != 0 && !disc->SwitchSubImage(parameters.media_playlist_index, error))
|
||||
{
|
||||
Error::AddPrefixFmt(error, "Failed to switch to subimage {} in '{}':\n", parameters.media_playlist_index,
|
||||
Path::GetFileName(parameters.filename));
|
||||
DestroySystem();
|
||||
return false;
|
||||
}
|
||||
|
||||
// Achievement hardcore checks before committing to anything.
|
||||
if (!IsReplayingGPUDump())
|
||||
{
|
||||
// Check for resuming with hardcore mode.
|
||||
if (parameters.disable_achievements_hardcore_mode)
|
||||
Achievements::DisableHardcoreMode();
|
||||
else
|
||||
Achievements::ResetHardcoreMode(true);
|
||||
|
||||
if ((!parameters.save_state.empty() || !exe_override.empty()) && Achievements::IsHardcoreModeActive())
|
||||
{
|
||||
const bool is_exe_override_boot = parameters.save_state.empty();
|
||||
Achievements::ConfirmHardcoreModeDisableAsync(
|
||||
is_exe_override_boot ? TRANSLATE("Achievements", "Overriding executable") :
|
||||
TRANSLATE("Achievements", "Resuming state"),
|
||||
[parameters = std::move(parameters)](bool approved) mutable {
|
||||
if (approved)
|
||||
{
|
||||
Host::RunOnCPUThread([parameters = std::move(parameters)]() mutable {
|
||||
parameters.disable_achievements_hardcore_mode = true;
|
||||
BootSystem(std::move(parameters), nullptr);
|
||||
});
|
||||
}
|
||||
});
|
||||
|
||||
// Technically a failure, but user-initiated. Returning false here would try to display a non-existent error.
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
// Can't early cancel without destroying past this point.
|
||||
Assert(s_state.state == State::Shutdown);
|
||||
s_state.state = State::Starting;
|
||||
s_state.region = console_region;
|
||||
s_state.startup_cancelled.store(false, std::memory_order_relaxed);
|
||||
s_state.gpu_dump_player = std::move(gpu_dump);
|
||||
std::atomic_thread_fence(std::memory_order_release);
|
||||
Host::OnSystemStarting();
|
||||
FullscreenUI::OnSystemStarting();
|
||||
|
||||
// Update running game, this will apply settings as well.
|
||||
UpdateRunningGame(disc ? disc->GetPath() : parameters.filename, disc.get(), true);
|
||||
|
||||
|
@ -1770,42 +1800,6 @@ bool System::BootSystem(SystemBootParameters parameters, Error* error)
|
|||
exe_override = std::move(parameters.override_exe);
|
||||
}
|
||||
|
||||
// Check for resuming with hardcore mode.
|
||||
if (parameters.disable_achievements_hardcore_mode)
|
||||
Achievements::DisableHardcoreMode();
|
||||
if ((!parameters.save_state.empty() || !exe_override.empty()) && Achievements::IsHardcoreModeActive())
|
||||
{
|
||||
const bool is_exe_override_boot = parameters.save_state.empty();
|
||||
bool cancelled;
|
||||
if (FullscreenUI::IsInitialized())
|
||||
{
|
||||
Achievements::ConfirmHardcoreModeDisableAsync(is_exe_override_boot ?
|
||||
TRANSLATE("Achievements", "Overriding executable") :
|
||||
TRANSLATE("Achievements", "Resuming state"),
|
||||
[parameters = std::move(parameters)](bool approved) mutable {
|
||||
if (approved)
|
||||
{
|
||||
parameters.disable_achievements_hardcore_mode = true;
|
||||
BootSystem(std::move(parameters), nullptr);
|
||||
}
|
||||
});
|
||||
cancelled = true;
|
||||
}
|
||||
else
|
||||
{
|
||||
cancelled = !Achievements::ConfirmHardcoreModeDisable(is_exe_override_boot ?
|
||||
TRANSLATE("Achievements", "Overriding executable") :
|
||||
TRANSLATE("Achievements", "Resuming state"));
|
||||
}
|
||||
|
||||
if (cancelled)
|
||||
{
|
||||
// Technically a failure, but user-initiated. Returning false here would try to display a non-existent error.
|
||||
DestroySystem();
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
// Are we fast booting? Must be checked after updating game settings.
|
||||
if (boot_mode == BootMode::FullBoot && disc_region != DiscRegion::NonPS1 &&
|
||||
parameters.override_fast_boot.value_or(static_cast<bool>(g_settings.bios_patch_fast_boot)))
|
||||
|
@ -1847,8 +1841,6 @@ bool System::BootSystem(SystemBootParameters parameters, Error* error)
|
|||
return false;
|
||||
}
|
||||
|
||||
FullscreenUI::OnSystemStarted();
|
||||
|
||||
InputManager::UpdateHostMouseMode();
|
||||
|
||||
if (g_settings.inhibit_screensaver)
|
||||
|
@ -4189,9 +4181,6 @@ void System::UpdateRunningGame(const std::string& path, CDImage* image, bool boo
|
|||
|
||||
if (!IsReplayingGPUDump())
|
||||
{
|
||||
if (booting)
|
||||
Achievements::ResetHardcoreMode(true);
|
||||
|
||||
Achievements::GameChanged(s_state.running_game_path, image, booting);
|
||||
|
||||
// game layer reloads cheats, but only the active list, we need new files
|
||||
|
@ -5848,27 +5837,18 @@ void System::UpdateGTEAspectRatio()
|
|||
GTE::SetAspectRatio(gte_ar, custom_num, custom_denom);
|
||||
}
|
||||
|
||||
bool System::OpenGPUDump(std::string path, Error* error)
|
||||
{
|
||||
std::unique_ptr<GPUDump::Player> new_dump = GPUDump::Player::Open(std::move(path), error);
|
||||
if (!new_dump)
|
||||
return false;
|
||||
|
||||
// set properties
|
||||
s_state.gpu_dump_player = std::move(new_dump);
|
||||
s_state.region = s_state.gpu_dump_player->GetRegion();
|
||||
return true;
|
||||
}
|
||||
|
||||
bool System::ChangeGPUDump(std::string new_path)
|
||||
{
|
||||
Error error;
|
||||
if (!OpenGPUDump(std::move(new_path), &error))
|
||||
std::unique_ptr<GPUDump::Player> new_dump = GPUDump::Player::Open(std::move(new_path), &error);
|
||||
if (!new_dump)
|
||||
{
|
||||
Host::ReportErrorAsync("Error", fmt::format(TRANSLATE_FS("Failed to change GPU dump: {}", error.GetDescription())));
|
||||
return false;
|
||||
}
|
||||
|
||||
s_state.gpu_dump_player = std::move(new_dump);
|
||||
s_state.region = s_state.gpu_dump_player->GetRegion();
|
||||
UpdateRunningGame(s_state.gpu_dump_player->GetPath(), nullptr, false);
|
||||
|
||||
// current player object has been changed out, toss call stack
|
||||
|
|
|
@ -871,9 +871,20 @@ void GraphicsSettingsWidget::populateGPUAdaptersAndResolutions(RenderAPI render_
|
|||
current_adapter = &adapter;
|
||||
}
|
||||
|
||||
// default adapter
|
||||
if (!m_adapters.empty() && current_adapter_name.empty())
|
||||
current_adapter = &m_adapters.front();
|
||||
if (!m_adapters.empty())
|
||||
{
|
||||
if (current_adapter_name.empty())
|
||||
{
|
||||
// default adapter
|
||||
current_adapter = &m_adapters.front();
|
||||
}
|
||||
else if (!current_adapter)
|
||||
{
|
||||
// if the adapter is not available, ensure it's in the list anyway, otherwise select the default
|
||||
const QString qadaptername = QString::fromStdString(current_adapter_name);
|
||||
m_ui.adapter->addItem(qadaptername, QVariant(qadaptername));
|
||||
}
|
||||
}
|
||||
|
||||
// disable it if we don't have a choice
|
||||
m_ui.adapter->setEnabled(!m_adapters.empty());
|
||||
|
@ -887,17 +898,30 @@ void GraphicsSettingsWidget::populateGPUAdaptersAndResolutions(RenderAPI render_
|
|||
m_ui.fullscreenMode->clear();
|
||||
|
||||
m_ui.fullscreenMode->addItem(tr("Borderless Fullscreen"), QVariant(QString()));
|
||||
|
||||
const std::string current_fullscreen_mode = m_dialog->getEffectiveStringValue("GPU", "FullscreenMode", "");
|
||||
bool current_fullscreen_mode_found = false;
|
||||
if (current_adapter)
|
||||
{
|
||||
for (const GPUDevice::ExclusiveFullscreenMode& mode : current_adapter->fullscreen_modes)
|
||||
{
|
||||
const QString qmodename = QtUtils::StringViewToQString(mode.ToString());
|
||||
const TinyString mode_str = mode.ToString();
|
||||
current_fullscreen_mode_found = current_fullscreen_mode_found || (current_fullscreen_mode == mode_str.view());
|
||||
|
||||
const QString qmodename = QtUtils::StringViewToQString(mode_str);
|
||||
m_ui.fullscreenMode->addItem(qmodename, QVariant(qmodename));
|
||||
}
|
||||
}
|
||||
|
||||
// if the current mode is not valid (e.g. adapter change), ensure it's in the list so the user isn't confused
|
||||
if (!current_fullscreen_mode_found)
|
||||
{
|
||||
const QString qmodename = QtUtils::StringViewToQString(current_fullscreen_mode);
|
||||
m_ui.fullscreenMode->addItem(qmodename, QVariant(qmodename));
|
||||
}
|
||||
|
||||
// disable it if we don't have a choice
|
||||
m_ui.fullscreenMode->setEnabled(current_adapter && !current_adapter->fullscreen_modes.empty());
|
||||
m_ui.fullscreenMode->setEnabled(m_ui.fullscreenMode->count() > 1);
|
||||
SettingWidgetBinder::BindWidgetToStringSetting(sif, m_ui.fullscreenMode, "GPU", "FullscreenMode");
|
||||
}
|
||||
|
||||
|
|
|
@ -732,7 +732,7 @@ void MainWindow::quit()
|
|||
// Make sure VM is gone. It really should be if we're here.
|
||||
if (s_system_valid)
|
||||
{
|
||||
g_emu_thread->shutdownSystem(false, true);
|
||||
g_emu_thread->shutdownSystem(false, false);
|
||||
QtUtils::ProcessEventsWithSleep(QEventLoop::ExcludeUserInputEvents, []() { return s_system_valid; });
|
||||
}
|
||||
|
||||
|
|
|
@ -1976,16 +1976,99 @@ bool Host::ConfirmMessage(std::string_view title, std::string_view message)
|
|||
QString::fromUtf8(message.data(), message.size()));
|
||||
}
|
||||
|
||||
void Host::ConfirmMessageAsync(std::string_view title, std::string_view message, ConfirmMessageAsyncCallback callback)
|
||||
void Host::ConfirmMessageAsync(std::string_view title, std::string_view message, ConfirmMessageAsyncCallback callback,
|
||||
std::string_view yes_text, std::string_view no_text)
|
||||
{
|
||||
QtHost::RunOnUIThread([title = QtUtils::StringViewToQString(title), message = QtUtils::StringViewToQString(message),
|
||||
callback = std::move(callback)]() mutable {
|
||||
auto lock = g_main_window->pauseAndLockSystem();
|
||||
INFO_LOG("ConfirmMessageAsync({}, {})", title, message);
|
||||
|
||||
const bool result = (QMessageBox::question(lock.getDialogParent(), title, message) != QMessageBox::No);
|
||||
// This is a racey read, but whether FSUI is started should be visible on all threads.
|
||||
std::atomic_thread_fence(std::memory_order_acquire);
|
||||
|
||||
callback(result);
|
||||
});
|
||||
// Default button titles.
|
||||
if (yes_text.empty())
|
||||
yes_text = TRANSLATE_SV("QtHost", "Yes");
|
||||
if (no_text.empty())
|
||||
no_text = TRANSLATE_SV("QtHost", "No");
|
||||
|
||||
// Ensure it always comes from the CPU thread.
|
||||
if (!g_emu_thread->isCurrentThread())
|
||||
{
|
||||
Host::RunOnCPUThread([title = std::string(title), message = std::string(message), callback = std::move(callback),
|
||||
yes_text = std::string(yes_text), no_text = std::string(no_text)]() mutable {
|
||||
ConfirmMessageAsync(title, message, std::move(callback));
|
||||
});
|
||||
return;
|
||||
}
|
||||
|
||||
// Pause system while dialog is up.
|
||||
const bool needs_pause = System::IsValid() && !System::IsPaused();
|
||||
if (needs_pause)
|
||||
System::PauseSystem(true);
|
||||
|
||||
// Use FSUI to display the confirmation if it is active.
|
||||
if (FullscreenUI::IsInitialized())
|
||||
{
|
||||
GPUThread::RunOnThread([title = std::string(title), message = std::string(message), callback = std::move(callback),
|
||||
yes_text = std::string(yes_text), no_text = std::string(no_text), needs_pause]() mutable {
|
||||
if (!FullscreenUI::Initialize())
|
||||
{
|
||||
callback(false);
|
||||
|
||||
if (needs_pause)
|
||||
{
|
||||
Host::RunOnCPUThread([]() {
|
||||
if (System::IsValid())
|
||||
System::PauseSystem(false);
|
||||
});
|
||||
}
|
||||
|
||||
return;
|
||||
}
|
||||
|
||||
// Need to reset run idle state _again_ after displaying.
|
||||
auto final_callback = [callback = std::move(callback)](bool result) {
|
||||
FullscreenUI::UpdateRunIdleState();
|
||||
callback(result);
|
||||
};
|
||||
|
||||
ImGuiFullscreen::OpenConfirmMessageDialog(std::move(title), std::move(message), std::move(final_callback),
|
||||
fmt::format(ICON_FA_CHECK " {}", yes_text),
|
||||
fmt::format(ICON_FA_TIMES " {}", no_text));
|
||||
FullscreenUI::UpdateRunIdleState();
|
||||
});
|
||||
}
|
||||
else
|
||||
{
|
||||
// Otherwise, use the desktop UI.
|
||||
QtHost::RunOnUIThread([title = QtUtils::StringViewToQString(title), message = QtUtils::StringViewToQString(message),
|
||||
callback = std::move(callback), yes_text = QtUtils::StringViewToQString(yes_text),
|
||||
no_text = QtUtils::StringViewToQString(no_text), needs_pause]() mutable {
|
||||
auto lock = g_main_window->pauseAndLockSystem();
|
||||
|
||||
bool result;
|
||||
{
|
||||
QMessageBox msgbox(lock.getDialogParent());
|
||||
msgbox.setIcon(QMessageBox::Question);
|
||||
msgbox.setWindowTitle(title);
|
||||
msgbox.setText(message);
|
||||
|
||||
QPushButton* const yes_button = msgbox.addButton(yes_text, QMessageBox::AcceptRole);
|
||||
msgbox.addButton(no_text, QMessageBox::RejectRole);
|
||||
msgbox.exec();
|
||||
result = (msgbox.clickedButton() == yes_button);
|
||||
}
|
||||
|
||||
callback(result);
|
||||
|
||||
if (needs_pause)
|
||||
{
|
||||
Host::RunOnCPUThread([]() {
|
||||
if (System::IsValid())
|
||||
System::PauseSystem(false);
|
||||
});
|
||||
}
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
void Host::OpenURL(std::string_view url)
|
||||
|
|
|
@ -162,7 +162,8 @@ bool Host::ConfirmMessage(std::string_view title, std::string_view message)
|
|||
return true;
|
||||
}
|
||||
|
||||
void Host::ConfirmMessageAsync(std::string_view title, std::string_view message, ConfirmMessageAsyncCallback callback)
|
||||
void Host::ConfirmMessageAsync(std::string_view title, std::string_view message, ConfirmMessageAsyncCallback callback,
|
||||
std::string_view yes_text, std::string_view no_text)
|
||||
{
|
||||
if (!title.empty() && !message.empty())
|
||||
ERROR_LOG("ConfirmMessage: {}: {}", title, message);
|
||||
|
|
|
@ -449,7 +449,7 @@ CDImage::PrecacheResult CDImageCHD::Precache(ProgressCallback* progress)
|
|||
if (m_precached)
|
||||
return CDImage::PrecacheResult::Success;
|
||||
|
||||
progress->SetStatusText(fmt::format("Precaching {}...", FileSystem::GetDisplayNameFromPath(m_filename)).c_str());
|
||||
progress->SetStatusText("Precaching CHD...");
|
||||
progress->SetProgressRange(100);
|
||||
|
||||
auto callback = [](size_t pos, size_t total, void* param) {
|
||||
|
|
|
@ -233,8 +233,23 @@ bool D3D11SwapChain::InitializeExclusiveFullscreenMode(const GPUDevice::Exclusiv
|
|||
RECT client_rc{};
|
||||
GetClientRect(window_hwnd, &client_rc);
|
||||
|
||||
// Little bit messy...
|
||||
HRESULT hr;
|
||||
ComPtr<IDXGIDevice> dxgi_dev;
|
||||
if (FAILED((hr = D3D11Device::GetD3DDevice()->QueryInterface(IID_PPV_ARGS(dxgi_dev.GetAddressOf())))))
|
||||
{
|
||||
ERROR_LOG("Failed to get DXGIDevice from D3D device: {:08X}", static_cast<unsigned>(hr));
|
||||
return false;
|
||||
}
|
||||
ComPtr<IDXGIAdapter> dxgi_adapter;
|
||||
if (FAILED((hr = dxgi_dev->GetAdapter(dxgi_adapter.GetAddressOf()))))
|
||||
{
|
||||
ERROR_LOG("Failed to get DXGIAdapter from DXGIDevice: {:08X}", static_cast<unsigned>(hr));
|
||||
return false;
|
||||
}
|
||||
|
||||
m_fullscreen_mode = D3DCommon::GetRequestedExclusiveFullscreenModeDesc(
|
||||
D3D11Device::GetDXGIFactory(), client_rc, mode, fm.resource_format, m_fullscreen_output.GetAddressOf());
|
||||
dxgi_adapter.Get(), client_rc, mode, fm.resource_format, m_fullscreen_output.GetAddressOf());
|
||||
return m_fullscreen_mode.has_value();
|
||||
}
|
||||
|
||||
|
@ -444,6 +459,11 @@ bool D3D11SwapChain::SetVSyncMode(GPUVSyncMode mode, bool allow_present_throttle
|
|||
return CreateSwapChain(error) && CreateRTV(error);
|
||||
}
|
||||
|
||||
bool D3D11SwapChain::IsExclusiveFullscreen() const
|
||||
{
|
||||
return m_fullscreen_mode.has_value();
|
||||
}
|
||||
|
||||
std::unique_ptr<GPUSwapChain> D3D11Device::CreateSwapChain(const WindowInfo& wi, GPUVSyncMode vsync_mode,
|
||||
bool allow_present_throttle,
|
||||
const ExclusiveFullscreenMode* exclusive_fullscreen_mode,
|
||||
|
|
|
@ -224,10 +224,10 @@ public:
|
|||
ALWAYS_INLINE ID3D11RenderTargetView* GetRTV() const { return m_swap_chain_rtv.Get(); }
|
||||
ALWAYS_INLINE ID3D11RenderTargetView* const* GetRTVArray() const { return m_swap_chain_rtv.GetAddressOf(); }
|
||||
ALWAYS_INLINE bool IsUsingAllowTearing() const { return m_using_allow_tearing; }
|
||||
ALWAYS_INLINE bool IsExclusiveFullscreen() const { return m_fullscreen_mode.has_value(); }
|
||||
|
||||
bool ResizeBuffers(u32 new_width, u32 new_height, float new_scale, Error* error) override;
|
||||
bool SetVSyncMode(GPUVSyncMode mode, bool allow_present_throttle, Error* error) override;
|
||||
bool IsExclusiveFullscreen() const override;
|
||||
|
||||
private:
|
||||
static u32 GetNewBufferCount(GPUVSyncMode vsync_mode);
|
||||
|
|
|
@ -880,9 +880,8 @@ bool D3D12SwapChain::InitializeExclusiveFullscreenMode(const GPUDevice::Exclusiv
|
|||
RECT client_rc{};
|
||||
GetClientRect(window_hwnd, &client_rc);
|
||||
|
||||
m_fullscreen_mode =
|
||||
D3DCommon::GetRequestedExclusiveFullscreenModeDesc(D3D12Device::GetInstance().GetDXGIFactory(), client_rc, mode,
|
||||
fm.resource_format, m_fullscreen_output.GetAddressOf());
|
||||
m_fullscreen_mode = D3DCommon::GetRequestedExclusiveFullscreenModeDesc(
|
||||
D3D12Device::GetInstance().GetAdapter(), client_rc, mode, fm.resource_format, m_fullscreen_output.GetAddressOf());
|
||||
return m_fullscreen_mode.has_value();
|
||||
}
|
||||
|
||||
|
@ -1082,6 +1081,11 @@ bool D3D12SwapChain::SetVSyncMode(GPUVSyncMode mode, bool allow_present_throttle
|
|||
return CreateSwapChain(dev, error) && CreateRTV(dev, error);
|
||||
}
|
||||
|
||||
bool D3D12SwapChain::IsExclusiveFullscreen() const
|
||||
{
|
||||
return m_fullscreen_mode.has_value();
|
||||
}
|
||||
|
||||
bool D3D12SwapChain::ResizeBuffers(u32 new_width, u32 new_height, float new_scale, Error* error)
|
||||
{
|
||||
m_window_info.surface_scale = new_scale;
|
||||
|
|
|
@ -381,7 +381,6 @@ public:
|
|||
ALWAYS_INLINE IDXGISwapChain1* GetSwapChain() const { return m_swap_chain.Get(); }
|
||||
ALWAYS_INLINE const BufferPair& GetCurrentBuffer() const { return m_swap_chain_buffers[m_current_swap_chain_buffer]; }
|
||||
ALWAYS_INLINE bool IsUsingAllowTearing() const { return m_using_allow_tearing; }
|
||||
ALWAYS_INLINE bool IsExclusiveFullscreen() const { return m_fullscreen_mode.has_value(); }
|
||||
|
||||
void AdvanceBuffer()
|
||||
{
|
||||
|
@ -389,6 +388,7 @@ public:
|
|||
}
|
||||
bool ResizeBuffers(u32 new_width, u32 new_height, float new_scale, Error* error) override;
|
||||
bool SetVSyncMode(GPUVSyncMode mode, bool allow_present_throttle, Error* error) override;
|
||||
bool IsExclusiveFullscreen() const override;
|
||||
|
||||
private:
|
||||
static u32 GetNewBufferCount(GPUVSyncMode vsync_mode);
|
||||
|
|
|
@ -225,50 +225,40 @@ GPUDevice::AdapterInfoList D3DCommon::GetAdapterInfoList()
|
|||
}
|
||||
|
||||
std::optional<DXGI_MODE_DESC>
|
||||
D3DCommon::GetRequestedExclusiveFullscreenModeDesc(IDXGIFactory5* factory, const RECT& window_rect,
|
||||
D3DCommon::GetRequestedExclusiveFullscreenModeDesc(IDXGIAdapter* adapter, const RECT& window_rect,
|
||||
const GPUDevice::ExclusiveFullscreenMode* requested_fullscreen_mode,
|
||||
DXGI_FORMAT format, IDXGIOutput** output)
|
||||
{
|
||||
std::optional<DXGI_MODE_DESC> ret;
|
||||
|
||||
// We need to find which monitor the window is located on.
|
||||
// The adapter must match, you cannot restrict the output to a monitor that is not connected to the device.
|
||||
const GSVector4i client_rc_vec(window_rect.left, window_rect.top, window_rect.right, window_rect.bottom);
|
||||
|
||||
// The window might be on a different adapter to which we are rendering.. so we have to enumerate them all.
|
||||
HRESULT hr;
|
||||
Microsoft::WRL::ComPtr<IDXGIOutput> first_output, intersecting_output;
|
||||
|
||||
for (u32 adapter_index = 0; !intersecting_output; adapter_index++)
|
||||
for (u32 output_index = 0;; output_index++)
|
||||
{
|
||||
Microsoft::WRL::ComPtr<IDXGIAdapter1> adapter;
|
||||
hr = factory->EnumAdapters1(adapter_index, adapter.GetAddressOf());
|
||||
Microsoft::WRL::ComPtr<IDXGIOutput> this_output;
|
||||
DXGI_OUTPUT_DESC output_desc;
|
||||
hr = adapter->EnumOutputs(output_index, this_output.GetAddressOf());
|
||||
if (hr == DXGI_ERROR_NOT_FOUND)
|
||||
break;
|
||||
else if (FAILED(hr))
|
||||
else if (FAILED(hr) || FAILED(this_output->GetDesc(&output_desc)))
|
||||
continue;
|
||||
|
||||
for (u32 output_index = 0;; output_index++)
|
||||
const GSVector4i output_rc(output_desc.DesktopCoordinates.left, output_desc.DesktopCoordinates.top,
|
||||
output_desc.DesktopCoordinates.right, output_desc.DesktopCoordinates.bottom);
|
||||
if (!client_rc_vec.rintersects(output_rc))
|
||||
{
|
||||
Microsoft::WRL::ComPtr<IDXGIOutput> this_output;
|
||||
DXGI_OUTPUT_DESC output_desc;
|
||||
hr = adapter->EnumOutputs(output_index, this_output.GetAddressOf());
|
||||
if (hr == DXGI_ERROR_NOT_FOUND)
|
||||
break;
|
||||
else if (FAILED(hr) || FAILED(this_output->GetDesc(&output_desc)))
|
||||
continue;
|
||||
|
||||
const GSVector4i output_rc(output_desc.DesktopCoordinates.left, output_desc.DesktopCoordinates.top,
|
||||
output_desc.DesktopCoordinates.right, output_desc.DesktopCoordinates.bottom);
|
||||
if (!client_rc_vec.rintersects(output_rc))
|
||||
{
|
||||
intersecting_output = std::move(this_output);
|
||||
break;
|
||||
}
|
||||
|
||||
// Fallback to the first monitor.
|
||||
if (!first_output)
|
||||
first_output = std::move(this_output);
|
||||
intersecting_output = std::move(this_output);
|
||||
break;
|
||||
}
|
||||
|
||||
// Fallback to the first monitor.
|
||||
if (!first_output)
|
||||
first_output = std::move(this_output);
|
||||
}
|
||||
|
||||
if (!intersecting_output)
|
||||
|
@ -561,11 +551,7 @@ std::optional<DynamicHeapArray<u8>> D3DCommon::CompileShaderWithDXC(u32 shader_m
|
|||
DXC_ARG_OPTIMIZATION_LEVEL3,
|
||||
};
|
||||
static constexpr const wchar_t* debug_arguments[] = {
|
||||
L"-Qstrip_reflect",
|
||||
DXC_ARG_DEBUG,
|
||||
L"-Qembed_debug",
|
||||
DXC_ARG_PACK_MATRIX_ROW_MAJOR,
|
||||
DXC_ARG_SKIP_OPTIMIZATIONS,
|
||||
L"-Qstrip_reflect", DXC_ARG_DEBUG, L"-Qembed_debug", DXC_ARG_PACK_MATRIX_ROW_MAJOR, DXC_ARG_SKIP_OPTIMIZATIONS,
|
||||
};
|
||||
const wchar_t* const* arguments = debug_device ? debug_arguments : nondebug_arguments;
|
||||
const size_t arguments_size = debug_device ? std::size(debug_arguments) : std::size(nondebug_arguments);
|
||||
|
|
|
@ -11,6 +11,7 @@
|
|||
|
||||
#include <d3dcommon.h>
|
||||
#include <dxgiformat.h>
|
||||
#include <dxgitype.h>
|
||||
#include <optional>
|
||||
#include <string>
|
||||
#include <vector>
|
||||
|
@ -19,9 +20,9 @@
|
|||
class Error;
|
||||
|
||||
struct IDXGIFactory5;
|
||||
struct IDXGIAdapter;
|
||||
struct IDXGIAdapter1;
|
||||
struct IDXGIOutput;
|
||||
struct DXGI_MODE_DESC;
|
||||
|
||||
namespace D3DCommon {
|
||||
// returns string representation of feature level
|
||||
|
@ -42,7 +43,7 @@ GPUDevice::AdapterInfoList GetAdapterInfoList();
|
|||
|
||||
// returns the fullscreen mode to use for the specified dimensions
|
||||
std::optional<DXGI_MODE_DESC>
|
||||
GetRequestedExclusiveFullscreenModeDesc(IDXGIFactory5* factory, const RECT& window_rect,
|
||||
GetRequestedExclusiveFullscreenModeDesc(IDXGIAdapter* adapter, const RECT& window_rect,
|
||||
const GPUDevice::ExclusiveFullscreenMode* requested_fullscreen_mode,
|
||||
DXGI_FORMAT format, IDXGIOutput** output);
|
||||
|
||||
|
|
|
@ -275,6 +275,11 @@ GSVector4i GPUSwapChain::PreRotateClipRect(WindowInfo::PreRotation prerotation,
|
|||
return new_clip;
|
||||
}
|
||||
|
||||
bool GPUSwapChain::IsExclusiveFullscreen() const
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
bool GPUSwapChain::ShouldSkipPresentingFrame()
|
||||
{
|
||||
// Only needed with FIFO. But since we're so fast, we allow it always.
|
||||
|
|
|
@ -528,6 +528,9 @@ public:
|
|||
virtual bool ResizeBuffers(u32 new_width, u32 new_height, float new_scale, Error* error) = 0;
|
||||
virtual bool SetVSyncMode(GPUVSyncMode mode, bool allow_present_throttle, Error* error) = 0;
|
||||
|
||||
/// Returns true if exclusive fullscreen is currently active on this swap chain.
|
||||
virtual bool IsExclusiveFullscreen() const;
|
||||
|
||||
bool ShouldSkipPresentingFrame();
|
||||
void ThrottlePresentation();
|
||||
|
||||
|
|
|
@ -48,7 +48,8 @@ bool ConfirmMessage(std::string_view title, std::string_view message);
|
|||
/// Displays an asynchronous confirmation on the UI thread, but does not block the caller.
|
||||
/// The callback may be executed on a different thread. Use RunOnCPUThread() in the callback to ensure safety.
|
||||
using ConfirmMessageAsyncCallback = std::function<void(bool)>;
|
||||
void ConfirmMessageAsync(std::string_view title, std::string_view message, ConfirmMessageAsyncCallback callback);
|
||||
void ConfirmMessageAsync(std::string_view title, std::string_view message, ConfirmMessageAsyncCallback callback,
|
||||
std::string_view yes_text = std::string_view(), std::string_view no_text = std::string_view());
|
||||
|
||||
/// Returns the user agent to use for HTTP requests.
|
||||
std::string GetHTTPUserAgent();
|
||||
|
|
|
@ -63,6 +63,16 @@ static ImGuiID GetBackgroundProgressID(const char* str_id);
|
|||
|
||||
namespace {
|
||||
|
||||
enum class CloseButtonState
|
||||
{
|
||||
None,
|
||||
KeyboardPressed,
|
||||
MousePressed,
|
||||
GamepadPressed,
|
||||
AnyReleased,
|
||||
Cancelled,
|
||||
};
|
||||
|
||||
struct FileSelectorItem
|
||||
{
|
||||
FileSelectorItem() = default;
|
||||
|
@ -109,7 +119,7 @@ struct ALIGN_TO_CACHE_LINE UIState
|
|||
std::recursive_mutex shared_state_mutex;
|
||||
|
||||
u32 menu_button_index = 0;
|
||||
u32 close_button_state = 0;
|
||||
CloseButtonState close_button_state = CloseButtonState::None;
|
||||
ImGuiDir has_pending_nav_move = ImGuiDir_None;
|
||||
FocusResetType focus_reset_queued = FocusResetType::None;
|
||||
bool initialized = false;
|
||||
|
@ -199,7 +209,7 @@ bool ImGuiFullscreen::Initialize(const char* placeholder_image_path)
|
|||
std::unique_lock lock(s_state.shared_state_mutex);
|
||||
|
||||
s_state.focus_reset_queued = FocusResetType::ViewChanged;
|
||||
s_state.close_button_state = 0;
|
||||
s_state.close_button_state = CloseButtonState::None;
|
||||
|
||||
s_state.placeholder_texture = LoadTexture(placeholder_image_path);
|
||||
if (!s_state.placeholder_texture)
|
||||
|
@ -661,7 +671,8 @@ void ImGuiFullscreen::PopResetLayout()
|
|||
void ImGuiFullscreen::QueueResetFocus(FocusResetType type)
|
||||
{
|
||||
s_state.focus_reset_queued = type;
|
||||
s_state.close_button_state = 0;
|
||||
s_state.close_button_state =
|
||||
(s_state.close_button_state != CloseButtonState::Cancelled) ? CloseButtonState::None : CloseButtonState::Cancelled;
|
||||
}
|
||||
|
||||
bool ImGuiFullscreen::ResetFocusHere()
|
||||
|
@ -728,29 +739,37 @@ bool ImGuiFullscreen::WantsToCloseMenu()
|
|||
{
|
||||
ImGuiContext& g = *GImGui;
|
||||
|
||||
// Wait for the Close button to be released, THEN pressed
|
||||
if (s_state.close_button_state == 0)
|
||||
// Wait for the Close button to be pressed, THEN released
|
||||
if (s_state.close_button_state == CloseButtonState::None)
|
||||
{
|
||||
if (ImGui::IsKeyPressed(ImGuiKey_Escape, false))
|
||||
s_state.close_button_state = 1;
|
||||
s_state.close_button_state = CloseButtonState::KeyboardPressed;
|
||||
else if (ImGui::IsKeyPressed(ImGuiKey_MouseRight, false))
|
||||
s_state.close_button_state = CloseButtonState::MousePressed;
|
||||
else if (ImGui::IsKeyPressed(ImGuiKey_NavGamepadCancel, false))
|
||||
s_state.close_button_state = 2;
|
||||
s_state.close_button_state = CloseButtonState::GamepadPressed;
|
||||
}
|
||||
else if ((s_state.close_button_state == 1 && ImGui::IsKeyReleased(ImGuiKey_Escape)) ||
|
||||
(s_state.close_button_state == 2 && ImGui::IsKeyReleased(ImGuiKey_NavGamepadCancel)))
|
||||
else if ((s_state.close_button_state == CloseButtonState::KeyboardPressed && ImGui::IsKeyReleased(ImGuiKey_Escape)) ||
|
||||
(s_state.close_button_state == CloseButtonState::MousePressed &&
|
||||
ImGui::IsKeyReleased(ImGuiKey_MouseRight)) ||
|
||||
(s_state.close_button_state == CloseButtonState::GamepadPressed &&
|
||||
ImGui::IsKeyReleased(ImGuiKey_NavGamepadCancel)))
|
||||
{
|
||||
s_state.close_button_state = 3;
|
||||
s_state.close_button_state = CloseButtonState::AnyReleased;
|
||||
}
|
||||
return s_state.close_button_state > 1;
|
||||
return (s_state.close_button_state == CloseButtonState::AnyReleased);
|
||||
}
|
||||
|
||||
void ImGuiFullscreen::ResetCloseMenuIfNeeded()
|
||||
{
|
||||
// If s_close_button_state reached the "Released" state, reset it after the tick
|
||||
if (s_state.close_button_state > 1)
|
||||
{
|
||||
s_state.close_button_state = 0;
|
||||
}
|
||||
s_state.close_button_state =
|
||||
(s_state.close_button_state >= CloseButtonState::AnyReleased) ? CloseButtonState::None : s_state.close_button_state;
|
||||
}
|
||||
|
||||
void ImGuiFullscreen::CancelPendingMenuClose()
|
||||
{
|
||||
s_state.close_button_state = CloseButtonState::Cancelled;
|
||||
}
|
||||
|
||||
void ImGuiFullscreen::PushPrimaryColor()
|
||||
|
|
|
@ -171,6 +171,7 @@ void ForceKeyNavEnabled();
|
|||
|
||||
bool WantsToCloseMenu();
|
||||
void ResetCloseMenuIfNeeded();
|
||||
void CancelPendingMenuClose();
|
||||
|
||||
void PushPrimaryColor();
|
||||
void PopPrimaryColor();
|
||||
|
|
Loading…
Reference in New Issue