graphics: restore monitor settings when fullscreen is lost then restored (#664)

## Link to GitHub Issue or related Pull Request, if one exists
Fixes #663 

## Description of change
Apply monitor rotation and refresh rate again when fullscreen focus is
regained.

## Testing
Tested with sdvx7
This commit is contained in:
bicarus
2026-04-28 16:04:52 -07:00
committed by GitHub
parent 3876e38636
commit 82dda33800
3 changed files with 90 additions and 16 deletions
+75 -7
View File
@@ -59,6 +59,11 @@ static CaptureData GRAPHICS_CAPTURE_BUFFER[GRAPHICS_CAPTURE_SCREEN_NO] {};
static std::mutex GRAPHICS_CAPTURE_BUFFER_M[GRAPHICS_CAPTURE_SCREEN_NO] {}; static std::mutex GRAPHICS_CAPTURE_BUFFER_M[GRAPHICS_CAPTURE_SCREEN_NO] {};
static std::condition_variable GRAPHICS_CAPTURE_CV[GRAPHICS_CAPTURE_SCREEN_NO] {}; static std::condition_variable GRAPHICS_CAPTURE_CV[GRAPHICS_CAPTURE_SCREEN_NO] {};
static std::optional<graphics_orientation> target_orientation_on_boot;
static UINT target_refresh_rate_on_boot = 0;
static bool monitor_settings_changed = false;
static bool monitor_layout_needs_reset = false;
// flag settings // flag settings
bool GRAPHICS_CAPTURE_CURSOR = false; bool GRAPHICS_CAPTURE_CURSOR = false;
bool GRAPHICS_LOG_HRESULT = false; bool GRAPHICS_LOG_HRESULT = false;
@@ -104,6 +109,7 @@ static decltype(SetWindowLongA) *SetWindowLongA_orig = nullptr;
static decltype(SetWindowLongW) *SetWindowLongW_orig = nullptr; static decltype(SetWindowLongW) *SetWindowLongW_orig = nullptr;
static decltype(SetWindowPos) *SetWindowPos_orig = nullptr; static decltype(SetWindowPos) *SetWindowPos_orig = nullptr;
static decltype(ShowWindow) *ShowWindow_orig = nullptr; static decltype(ShowWindow) *ShowWindow_orig = nullptr;
static decltype(SetDisplayConfig) *SetDisplayConfig_addr = nullptr;
static void reset_window_hook(HWND hWnd) { static void reset_window_hook(HWND hWnd) {
overlay::destroy(hWnd); overlay::destroy(hWnd);
@@ -232,7 +238,7 @@ static LRESULT CALLBACK WindowProc(HWND hWnd, UINT uMsg, WPARAM wParam, LPARAM l
switch (uMsg) { switch (uMsg) {
case WM_MOVE: case WM_MOVE:
case WM_SIZE: { case WM_SIZE:
// Update SPICETOUCH space when the main window changes size or moves. // Update SPICETOUCH space when the main window changes size or moves.
// The update happens regardless of whether the "fake" spicetouch window is present or not. // The update happens regardless of whether the "fake" spicetouch window is present or not.
// This allows touches received on subscreen window to be translated correctly. // This allows touches received on subscreen window to be translated correctly.
@@ -249,7 +255,16 @@ static LRESULT CALLBACK WindowProc(HWND hWnd, UINT uMsg, WPARAM wParam, LPARAM l
SPICETOUCH_TOUCH_WIDTH, SPICETOUCH_TOUCH_HEIGHT, SPICETOUCH_TOUCH_WIDTH, SPICETOUCH_TOUCH_HEIGHT,
SWP_NOZORDER | SWP_NOREDRAW | SWP_NOREPOSITION | SWP_NOACTIVATE); SWP_NOZORDER | SWP_NOREDRAW | SWP_NOREPOSITION | SWP_NOACTIVATE);
} }
break;
case WM_ACTIVATEAPP:
if (wParam) {
// regained focus
// this *can* get called twice in a row when restoring, but update_monitor_at_runtime
// is idempotent (checks current display settings to see if changes are needed),
// so it shouldn't cause any issues
update_monitor_at_runtime();
} }
break;
default: default:
break; break;
} }
@@ -1142,7 +1157,7 @@ void change_primary_monitor(const std::string &monitor_name) {
const auto DisplayConfigGetDeviceInfo_addr = const auto DisplayConfigGetDeviceInfo_addr =
reinterpret_cast<decltype(DisplayConfigGetDeviceInfo) *>( reinterpret_cast<decltype(DisplayConfigGetDeviceInfo) *>(
GetProcAddress(user32, "DisplayConfigGetDeviceInfo")); GetProcAddress(user32, "DisplayConfigGetDeviceInfo"));
const auto SetDisplayConfig_addr = SetDisplayConfig_addr =
reinterpret_cast<decltype(SetDisplayConfig) *>( reinterpret_cast<decltype(SetDisplayConfig) *>(
GetProcAddress(user32, "SetDisplayConfig")); GetProcAddress(user32, "SetDisplayConfig"));
if (GetDisplayConfigBufferSizes_addr == nullptr || QueryDisplayConfig_addr == nullptr || if (GetDisplayConfigBufferSizes_addr == nullptr || QueryDisplayConfig_addr == nullptr ||
@@ -1257,11 +1272,13 @@ void change_primary_monitor(const std::string &monitor_name) {
log_fatal("graphics", "SetDisplayConfig failed, check -mainmonitor option: {}", status); log_fatal("graphics", "SetDisplayConfig failed, check -mainmonitor option: {}", status);
} }
monitor_layout_needs_reset = true;
// a little extra time for windows to settle and redraw things // a little extra time for windows to settle and redraw things
Sleep(2000); Sleep(2000);
} }
void update_monitor_on_boot(std::optional<graphics_orientation> target_orientation, UINT target_refresh_rate) { void update_monitor(bool is_boot, std::optional<graphics_orientation> target_orientation, UINT target_refresh_rate) {
// note: all of this is only being done for the primary motnior // note: all of this is only being done for the primary motnior
// get current settings // get current settings
@@ -1330,7 +1347,7 @@ void update_monitor_on_boot(std::optional<graphics_orientation> target_orientati
} }
// update refresh rate // update refresh rate
if (target_refresh_rate > 0) { if (target_refresh_rate > 0 && target_refresh_rate != dm.dmDisplayFrequency) {
log_misc("graphics", log_misc("graphics",
"current refresh rate {} => desired refresh rate {}", "current refresh rate {} => desired refresh rate {}",
dm.dmDisplayFrequency, target_refresh_rate); dm.dmDisplayFrequency, target_refresh_rate);
@@ -1342,11 +1359,13 @@ void update_monitor_on_boot(std::optional<graphics_orientation> target_orientati
if (!needs_update) { if (!needs_update) {
// nothing to do // nothing to do
log_misc("graphics", "display settings are already up to date, no changes needed");
return; return;
} }
const auto result = ChangeDisplaySettings(&dm, CDS_FULLSCREEN); const auto result = ChangeDisplaySettings(&dm, CDS_FULLSCREEN);
if (result != DISP_CHANGE_SUCCESSFUL) { if (result != DISP_CHANGE_SUCCESSFUL) {
if (is_boot) {
log_fatal( log_fatal(
"graphics", "graphics",
"failed to update display settings ({}px x {}px @ {}Hz): error {}, double check options", "failed to update display settings ({}px x {}px @ {}Hz): error {}, double check options",
@@ -1355,12 +1374,61 @@ void update_monitor_on_boot(std::optional<graphics_orientation> target_orientati
dm.dmDisplayFrequency, dm.dmDisplayFrequency,
result); result);
} else { } else {
log_warning(
"graphics",
"failed to update display settings ({}px x {}px @ {}Hz): error {}, double check options",
dm.dmPelsWidth,
dm.dmPelsHeight,
dm.dmDisplayFrequency,
result);
}
} else {
monitor_settings_changed = true;
// sleep for a little bit after changing monitor settings to delay game launch/resume
Sleep(1000);
log_info("graphics", "display settings updated successfully ({}px x {}px @ {}Hz)", log_info("graphics", "display settings updated successfully ({}px x {}px @ {}Hz)",
dm.dmPelsWidth, dm.dmPelsWidth,
dm.dmPelsHeight, dm.dmPelsHeight,
dm.dmDisplayFrequency); dm.dmDisplayFrequency);
} }
}
// sleep for a little bit after changing monitor settings to delay game launch
Sleep(1000); void update_monitor_on_boot(std::optional<graphics_orientation> target_orientation, UINT target_refresh_rate) {
target_orientation_on_boot = target_orientation;
target_refresh_rate_on_boot = target_refresh_rate;
log_misc("graphics", "applying monitor updates at boot...");
update_monitor(true, target_orientation, target_refresh_rate);
}
void update_monitor_at_runtime() {
if (monitor_settings_changed) {
log_misc("graphics", "applying monitor updates at runtime as window regained focus...");
update_monitor(false, target_orientation_on_boot, target_refresh_rate_on_boot);
}
}
void reset_monitor_on_exit() {
// while CDS_FULLSCREEN is *supposed* to be temporary & the OS attempts to
// restore the original settings on exit, it can sometimes fail to do that;
// therefore, we try our best to clean things up on the way out
if (monitor_settings_changed) {
monitor_settings_changed = false;
log_misc("graphics", "resetting monitor settings on exit...");
ChangeDisplaySettingsW(nullptr, 0);
}
// same for this one.
if (monitor_layout_needs_reset) {
monitor_layout_needs_reset = false;
log_misc("graphics", "restoring primary monitor on exit...");
if (SetDisplayConfig_addr != nullptr) {
SetDisplayConfig_addr(
0,
nullptr,
0,
nullptr,
SDC_APPLY | SDC_USE_DATABASE_CURRENT);
}
}
} }
+2
View File
@@ -118,3 +118,5 @@ void graphics_window_check_bounds_before_creation(int &x, int &y, const int widt
void change_primary_monitor(const std::string &monitor_name); void change_primary_monitor(const std::string &monitor_name);
void update_monitor_on_boot(std::optional<graphics_orientation> target_orientation, UINT target_refresh_rate); void update_monitor_on_boot(std::optional<graphics_orientation> target_orientation, UINT target_refresh_rate);
void update_monitor_at_runtime();
void reset_monitor_on_exit();
+4
View File
@@ -7,6 +7,7 @@
#include "easrv/easrv.h" #include "easrv/easrv.h"
#include "rawinput/rawinput.h" #include "rawinput/rawinput.h"
#include "hooks/audio/audio.h" #include "hooks/audio/audio.h"
#include "hooks/graphics/graphics.h"
#include "util/deferlog.h" #include "util/deferlog.h"
#include "util/logging.h" #include "util/logging.h"
@@ -22,6 +23,9 @@ namespace launcher {
// therefore, subsystems need to be guarded against multiple unload attempts // therefore, subsystems need to be guarded against multiple unload attempts
log_info("launcher", "stopping subsystems"); log_info("launcher", "stopping subsystems");
// reset monitor settings
reset_monitor_on_exit();
// before shutting down logger, dump any deferred log messages // before shutting down logger, dump any deferred log messages
deferredlogs::dump_to_logger(); deferredlogs::dump_to_logger();