aboutsummaryrefslogtreecommitdiff
path: root/src/frontend/duckstation/window_info.cpp
diff options
context:
space:
mode:
Diffstat (limited to 'src/frontend/duckstation/window_info.cpp')
-rw-r--r--src/frontend/duckstation/window_info.cpp191
1 files changed, 191 insertions, 0 deletions
diff --git a/src/frontend/duckstation/window_info.cpp b/src/frontend/duckstation/window_info.cpp
new file mode 100644
index 0000000..5176dad
--- /dev/null
+++ b/src/frontend/duckstation/window_info.cpp
@@ -0,0 +1,191 @@
+#include "window_info.h"
+#include "common/log.h"
+Log_SetChannel(WindowInfo);
+
+#if defined(_WIN32)
+
+#include "common/windows_headers.h"
+#include <dwmapi.h>
+
+static bool GetRefreshRateFromDWM(HWND hwnd, float* refresh_rate)
+{
+ static HMODULE dwm_module = nullptr;
+ static HRESULT(STDAPICALLTYPE * is_composition_enabled)(BOOL * pfEnabled) = nullptr;
+ static HRESULT(STDAPICALLTYPE * get_timing_info)(HWND hwnd, DWM_TIMING_INFO * pTimingInfo) = nullptr;
+ static bool load_tried = false;
+ if (!load_tried)
+ {
+ load_tried = true;
+ dwm_module = LoadLibrary("dwmapi.dll");
+ if (dwm_module)
+ {
+ std::atexit([]() {
+ FreeLibrary(dwm_module);
+ dwm_module = nullptr;
+ });
+ is_composition_enabled =
+ reinterpret_cast<decltype(is_composition_enabled)>(GetProcAddress(dwm_module, "DwmIsCompositionEnabled"));
+ get_timing_info =
+ reinterpret_cast<decltype(get_timing_info)>(GetProcAddress(dwm_module, "DwmGetCompositionTimingInfo"));
+ }
+ }
+
+ BOOL composition_enabled;
+ if (!is_composition_enabled || FAILED(is_composition_enabled(&composition_enabled) || !get_timing_info))
+ return false;
+
+ DWM_TIMING_INFO ti = {};
+ ti.cbSize = sizeof(ti);
+ HRESULT hr = get_timing_info(nullptr, &ti);
+ if (SUCCEEDED(hr))
+ {
+ if (ti.rateRefresh.uiNumerator == 0 || ti.rateRefresh.uiDenominator == 0)
+ return false;
+
+ *refresh_rate = static_cast<float>(ti.rateRefresh.uiNumerator) / static_cast<float>(ti.rateRefresh.uiDenominator);
+ return true;
+ }
+
+ return false;
+}
+
+static bool GetRefreshRateFromMonitor(HWND hwnd, float* refresh_rate)
+{
+ HMONITOR mon = MonitorFromWindow(hwnd, MONITOR_DEFAULTTONEAREST);
+ if (!mon)
+ return false;
+
+ MONITORINFOEXW mi = {};
+ mi.cbSize = sizeof(mi);
+ if (GetMonitorInfoW(mon, &mi))
+ {
+ DEVMODEW dm = {};
+ dm.dmSize = sizeof(dm);
+
+ // 0/1 are reserved for "defaults".
+ if (EnumDisplaySettingsW(mi.szDevice, ENUM_CURRENT_SETTINGS, &dm) && dm.dmDisplayFrequency > 1)
+ {
+ *refresh_rate = static_cast<float>(dm.dmDisplayFrequency);
+ return true;
+ }
+ }
+
+ return false;
+}
+
+bool WindowInfo::QueryRefreshRateForWindow(const WindowInfo& wi, float* refresh_rate)
+{
+ if (wi.type != Type::Win32 || !wi.window_handle)
+ return false;
+
+ // Try DWM first, then fall back to integer values.
+ const HWND hwnd = static_cast<HWND>(wi.window_handle);
+ return GetRefreshRateFromDWM(hwnd, refresh_rate) || GetRefreshRateFromMonitor(hwnd, refresh_rate);
+}
+
+#else
+
+#ifdef USE_X11
+
+#include "common/scoped_guard.h"
+#include "gl/x11_window.h"
+#include <X11/extensions/Xrandr.h>
+
+static bool GetRefreshRateFromXRandR(const WindowInfo& wi, float* refresh_rate)
+{
+ Display* display = static_cast<Display*>(wi.display_connection);
+ Window window = static_cast<Window>(reinterpret_cast<uintptr_t>(wi.window_handle));
+ if (!display || !window)
+ return false;
+
+ GL::X11InhibitErrors inhibiter;
+
+ XRRScreenResources* res = XRRGetScreenResources(display, window);
+ if (!res)
+ {
+ Log_ErrorPrint("XRRGetScreenResources() failed");
+ return false;
+ }
+
+ ScopedGuard res_guard([res]() { XRRFreeScreenResources(res); });
+
+ int num_monitors;
+ XRRMonitorInfo* mi = XRRGetMonitors(display, window, True, &num_monitors);
+ if (num_monitors < 0)
+ {
+ Log_ErrorPrint("XRRGetMonitors() failed");
+ return false;
+ }
+ else if (num_monitors > 1)
+ {
+ Log_WarningPrintf("XRRGetMonitors() returned %d monitors, using first", num_monitors);
+ }
+
+ ScopedGuard mi_guard([mi]() { XRRFreeMonitors(mi); });
+ if (mi->noutput <= 0)
+ {
+ Log_ErrorPrint("Monitor has no outputs");
+ return false;
+ }
+ else if (mi->noutput > 1)
+ {
+ Log_WarningPrintf("Monitor has %d outputs, using first", mi->noutput);
+ }
+
+ XRROutputInfo* oi = XRRGetOutputInfo(display, res, mi->outputs[0]);
+ if (!oi)
+ {
+ Log_ErrorPrint("XRRGetOutputInfo() failed");
+ return false;
+ }
+
+ ScopedGuard oi_guard([oi]() { XRRFreeOutputInfo(oi); });
+
+ XRRCrtcInfo* ci = XRRGetCrtcInfo(display, res, oi->crtc);
+ if (!ci)
+ {
+ Log_ErrorPrint("XRRGetCrtcInfo() failed");
+ return false;
+ }
+
+ ScopedGuard ci_guard([ci]() { XRRFreeCrtcInfo(ci); });
+
+ XRRModeInfo* mode = nullptr;
+ for (int i = 0; i < res->nmode; i++)
+ {
+ if (res->modes[i].id == ci->mode)
+ {
+ mode = &res->modes[i];
+ break;
+ }
+ }
+ if (!mode)
+ {
+ Log_ErrorPrintf("Failed to look up mode %d (of %d)", static_cast<int>(ci->mode), res->nmode);
+ return false;
+ }
+
+ if (mode->dotClock == 0 || mode->hTotal == 0 || mode->vTotal == 0)
+ {
+ Log_ErrorPrintf("Modeline is invalid: %ld/%d/%d", mode->dotClock, mode->hTotal, mode->vTotal);
+ return false;
+ }
+
+ *refresh_rate =
+ static_cast<double>(mode->dotClock) / (static_cast<double>(mode->hTotal) * static_cast<double>(mode->vTotal));
+ return true;
+}
+
+#endif // USE_X11
+
+bool WindowInfo::QueryRefreshRateForWindow(const WindowInfo& wi, float* refresh_rate)
+{
+#if defined(USE_X11)
+ if (wi.type == WindowInfo::Type::X11)
+ return GetRefreshRateFromXRandR(wi, refresh_rate);
+#endif
+
+ return false;
+}
+
+#endif \ No newline at end of file