/** ****************************************************************************** * Xenia : Xbox 360 Emulator Research Project * ****************************************************************************** * Copyright 2022 Ben Vanik. All rights reserved. * * Released under the BSD license - see LICENSE in the root for more details. * ****************************************************************************** */ #ifndef XENIA_UI_WINDOWED_APP_CONTEXT_WIN_H_ #define XENIA_UI_WINDOWED_APP_CONTEXT_WIN_H_ #include #include "xenia/base/platform_win.h" #include "xenia/ui/windowed_app_context.h" // For per-monitor DPI awareness v1. #include namespace xe { namespace ui { class Win32WindowedAppContext final : public WindowedAppContext { public: // clang-format off struct PerMonitorDpiV1Api { HRESULT (STDAPICALLTYPE* get_dpi_for_monitor)( HMONITOR hmonitor, MONITOR_DPI_TYPE dpi_type, UINT* dpi_x, UINT* dpi_y); }; struct PerMonitorDpiV2Api { // Important note: Even though per-monitor DPI awareness version 2 // inherently has automatic non-client area DPI scaling, PMv2 was added in // Windows 10 1703. However, these functions were added earlier, in 1607. // While we haven't tested the actual behavior on 1607 and this is just a // guess, still, before using AdjustWindowRectExForDpi for the window DPI, // make sure that EnableNonClientDpiScaling is called for the window in its // WM_NCCREATE handler to enable automatic non-client DPI scaling on PMv1 on // 1607, so AdjustWindowRectExForDpi (which doesn't have a window argument // so it can't know whether automatic scaling is enabled for it) will likely // return values that reflect the actual size of the non-client area of the // window (otherwise, for instance, "un-adjusting" the actual window // rectangle using AdjustWindowRectExForDpi may result in a negative client // area size if the DPI passed to it is larger than the DPI actually used // for the non-client area). BOOL (WINAPI* adjust_window_rect_ex_for_dpi)( LPRECT rect, DWORD style, BOOL menu, DWORD ex_style, UINT dpi); BOOL (WINAPI* enable_non_client_dpi_scaling)(HWND hwnd); UINT (WINAPI* get_dpi_for_system)(); UINT (WINAPI* get_dpi_for_window)(HWND hwnd); }; // clang-format on // Must call Initialize and check its result after creating to be able to // perform pending function calls. explicit Win32WindowedAppContext(HINSTANCE hinstance, int show_cmd) : hinstance_(hinstance), show_cmd_(show_cmd) {} ~Win32WindowedAppContext(); bool Initialize(); HINSTANCE hinstance() const { return hinstance_; } int show_cmd() const { return show_cmd_; } void NotifyUILoopOfPendingFunctions() override; void PlatformQuitFromUIThread() override; int RunMainMessageLoop(); // Per-monitor DPI awareness version 2 is expected to be enabled via the // manifest, as that's the recommended way, which also doesn't require calling // SetProcessDpiAwareness before doing anything that may depend on DPI // awareness (so it's safe to use any Windows APIs before what would be // supposed to initialize it). // Windows 8.1 per-monitor DPI awareness version 1. const PerMonitorDpiV1Api* per_monitor_dpi_v1_api() const { return per_monitor_dpi_v1_api_available_ ? &per_monitor_dpi_v1_api_ : nullptr; } // Windows 10 1607 per-monitor DPI awareness API, also heavily used for // per-monitor DPI awareness version 2 functionality added in Windows 10 1703. const PerMonitorDpiV2Api* per_monitor_dpi_v2_api() const { return per_monitor_dpi_v2_api_available_ ? &per_monitor_dpi_v2_api_ : nullptr; } private: enum : UINT { kPendingFunctionsWindowClassMessageExecute = WM_USER, }; static LRESULT CALLBACK PendingFunctionsWndProc(HWND hwnd, UINT message, WPARAM wparam, LPARAM lparam); HINSTANCE hinstance_; int show_cmd_; HMODULE shcore_module_ = nullptr; HMODULE user32_module_ = nullptr; PerMonitorDpiV1Api per_monitor_dpi_v1_api_ = {}; PerMonitorDpiV2Api per_monitor_dpi_v2_api_ = {}; bool per_monitor_dpi_v1_api_available_ = false; bool per_monitor_dpi_v2_api_available_ = false; static bool pending_functions_window_class_registered_; HWND pending_functions_hwnd_ = nullptr; }; } // namespace ui } // namespace xe #endif // XENIA_UI_WINDOWED_APP_CONTEXT_WIN_H_