dxvk/src/wsi/win32/wsi_window_win32.cpp

302 lines
9.2 KiB
C++

#if defined(DXVK_WSI_WIN32)
#include "wsi_platform_win32.h"
#include "../../util/util_string.h"
#include "../../util/log/log.h"
namespace dxvk::wsi {
static bool getMonitorDisplayMode(
HMONITOR hMonitor,
DWORD modeNum,
DEVMODEW* pMode) {
::MONITORINFOEXW monInfo;
monInfo.cbSize = sizeof(monInfo);
if (!::GetMonitorInfoW(hMonitor, reinterpret_cast<MONITORINFO*>(&monInfo))) {
Logger::err("Failed to query monitor info");
return false;
}
return ::EnumDisplaySettingsW(monInfo.szDevice, modeNum, pMode);
}
static bool setMonitorDisplayMode(
HMONITOR hMonitor,
DEVMODEW* pMode) {
::MONITORINFOEXW monInfo;
monInfo.cbSize = sizeof(monInfo);
if (!::GetMonitorInfoW(hMonitor, reinterpret_cast<MONITORINFO*>(&monInfo))) {
Logger::err("Failed to query monitor info");
return false;
}
Logger::info(str::format("Setting display mode: ",
pMode->dmPelsWidth, "x", pMode->dmPelsHeight, "@",
pMode->dmDisplayFrequency));
DEVMODEW curMode = { };
curMode.dmSize = sizeof(curMode);
if (getMonitorDisplayMode(hMonitor, ENUM_CURRENT_SETTINGS, &curMode)) {
bool eq = curMode.dmPelsWidth == pMode->dmPelsWidth
&& curMode.dmPelsHeight == pMode->dmPelsHeight
&& curMode.dmBitsPerPel == pMode->dmBitsPerPel;
if (pMode->dmFields & DM_DISPLAYFREQUENCY)
eq &= curMode.dmDisplayFrequency == pMode->dmDisplayFrequency;
if (eq)
return true;
}
LONG status = ::ChangeDisplaySettingsExW(monInfo.szDevice,
pMode, nullptr, CDS_FULLSCREEN, nullptr);
if (status != DISP_CHANGE_SUCCESSFUL) {
pMode->dmFields &= ~DM_DISPLAYFREQUENCY;
status = ::ChangeDisplaySettingsExW(monInfo.szDevice,
pMode, nullptr, CDS_FULLSCREEN, nullptr);
}
return status == DISP_CHANGE_SUCCESSFUL;
}
static BOOL CALLBACK restoreDisplayModeCallback(
HMONITOR hMonitor,
HDC hDC,
LPRECT pRect,
LPARAM pUserdata) {
auto success = reinterpret_cast<bool*>(pUserdata);
DEVMODEW devMode = { };
devMode.dmSize = sizeof(devMode);
if (!getMonitorDisplayMode(hMonitor, ENUM_REGISTRY_SETTINGS, &devMode)) {
*success = false;
return false;
}
Logger::info(str::format("Restoring display mode: ",
devMode.dmPelsWidth, "x", devMode.dmPelsHeight, "@",
devMode.dmDisplayFrequency));
if (!setMonitorDisplayMode(hMonitor, &devMode)) {
*success = false;
return false;
}
return true;
}
void Win32WsiDriver::getWindowSize(
HWND hWindow,
uint32_t* pWidth,
uint32_t* pHeight) {
RECT rect = { };
::GetClientRect(hWindow, &rect);
if (pWidth)
*pWidth = rect.right - rect.left;
if (pHeight)
*pHeight = rect.bottom - rect.top;
}
void Win32WsiDriver::resizeWindow(
HWND hWindow,
DxvkWindowState* pState,
uint32_t width,
uint32_t height) {
// Adjust window position and size
RECT newRect = { 0, 0, 0, 0 };
RECT oldRect = { 0, 0, 0, 0 };
::GetWindowRect(hWindow, &oldRect);
::SetRect(&newRect, 0, 0, width, height);
::AdjustWindowRectEx(&newRect,
::GetWindowLongW(hWindow, GWL_STYLE), FALSE,
::GetWindowLongW(hWindow, GWL_EXSTYLE));
::SetRect(&newRect, 0, 0, newRect.right - newRect.left, newRect.bottom - newRect.top);
::OffsetRect(&newRect, oldRect.left, oldRect.top);
::MoveWindow(hWindow, newRect.left, newRect.top,
newRect.right - newRect.left, newRect.bottom - newRect.top, TRUE);
}
bool Win32WsiDriver::setWindowMode(
HMONITOR hMonitor,
HWND hWindow,
const WsiMode& mode) {
::MONITORINFOEXW monInfo;
monInfo.cbSize = sizeof(monInfo);
if (!::GetMonitorInfoW(hMonitor, reinterpret_cast<MONITORINFO*>(&monInfo))) {
Logger::err("Win32 WSI: setWindowMode: Failed to query monitor info");
return false;
}
DEVMODEW devMode = { };
devMode.dmSize = sizeof(devMode);
devMode.dmFields = DM_PELSWIDTH | DM_PELSHEIGHT | DM_BITSPERPEL;
devMode.dmPelsWidth = mode.width;
devMode.dmPelsHeight = mode.height;
devMode.dmBitsPerPel = mode.bitsPerPixel;
if (mode.refreshRate.numerator != 0) {
devMode.dmFields |= DM_DISPLAYFREQUENCY;
devMode.dmDisplayFrequency = mode.refreshRate.numerator
/ mode.refreshRate.denominator;
}
Logger::info(str::format("Setting display mode: ",
devMode.dmPelsWidth, "x", devMode.dmPelsHeight, "@",
devMode.dmDisplayFrequency));
return setMonitorDisplayMode(hMonitor, &devMode);
}
bool Win32WsiDriver::enterFullscreenMode(
HMONITOR hMonitor,
HWND hWindow,
DxvkWindowState* pState,
[[maybe_unused]]
bool modeSwitch) {
// Find a display mode that matches what we need
::GetWindowRect(hWindow, &pState->win.rect);
// Change the window flags to remove the decoration etc.
LONG style = ::GetWindowLongW(hWindow, GWL_STYLE);
LONG exstyle = ::GetWindowLongW(hWindow, GWL_EXSTYLE);
pState->win.style = style;
pState->win.exstyle = exstyle;
style &= ~WS_OVERLAPPEDWINDOW;
exstyle &= ~WS_EX_OVERLAPPEDWINDOW;
::SetWindowLongW(hWindow, GWL_STYLE, style);
::SetWindowLongW(hWindow, GWL_EXSTYLE, exstyle);
RECT rect = { };
getDesktopCoordinates(hMonitor, &rect);
::SetWindowPos(hWindow, HWND_TOPMOST,
rect.left, rect.top, rect.right - rect.left, rect.bottom - rect.top,
SWP_FRAMECHANGED | SWP_SHOWWINDOW | SWP_NOACTIVATE);
return true;
}
bool Win32WsiDriver::leaveFullscreenMode(
HWND hWindow,
DxvkWindowState* pState,
bool restoreCoordinates) {
// Only restore the window style if the application hasn't
// changed them. This is in line with what native DXGI does.
LONG curStyle = ::GetWindowLongW(hWindow, GWL_STYLE) & ~WS_VISIBLE;
LONG curExstyle = ::GetWindowLongW(hWindow, GWL_EXSTYLE) & ~WS_EX_TOPMOST;
if (curStyle == (pState->win.style & ~(WS_VISIBLE | WS_OVERLAPPEDWINDOW))
&& curExstyle == (pState->win.exstyle & ~(WS_EX_TOPMOST | WS_EX_OVERLAPPEDWINDOW))) {
::SetWindowLongW(hWindow, GWL_STYLE, pState->win.style);
::SetWindowLongW(hWindow, GWL_EXSTYLE, pState->win.exstyle);
}
// Restore window position and apply the style
UINT flags = SWP_FRAMECHANGED | SWP_NOACTIVATE;
const RECT rect = pState->win.rect;
if (!restoreCoordinates)
flags |= SWP_NOSIZE | SWP_NOMOVE;
::SetWindowPos(hWindow, (pState->win.exstyle & WS_EX_TOPMOST) ? HWND_TOPMOST : HWND_NOTOPMOST,
rect.left, rect.top, rect.right - rect.left, rect.bottom - rect.top, flags);
return true;
}
bool Win32WsiDriver::restoreDisplayMode() {
bool success = true;
bool result = ::EnumDisplayMonitors(nullptr, nullptr,
&restoreDisplayModeCallback,
reinterpret_cast<LPARAM>(&success));
return result && success;
}
HMONITOR Win32WsiDriver::getWindowMonitor(HWND hWindow) {
RECT windowRect = { 0, 0, 0, 0 };
::GetWindowRect(hWindow, &windowRect);
HMONITOR monitor = ::MonitorFromPoint(
{ (windowRect.left + windowRect.right) / 2,
(windowRect.top + windowRect.bottom) / 2 },
MONITOR_DEFAULTTOPRIMARY);
return monitor;
}
bool Win32WsiDriver::isWindow(HWND hWindow) {
return ::IsWindow(hWindow);
}
void Win32WsiDriver::updateFullscreenWindow(
HMONITOR hMonitor,
HWND hWindow,
bool forceTopmost) {
RECT bounds = { };
wsi::getDesktopCoordinates(hMonitor, &bounds);
// In D3D9, changing display modes re-forces the window
// to become top most, whereas in DXGI, it does not.
if (forceTopmost) {
::SetWindowPos(hWindow, HWND_TOPMOST,
bounds.left, bounds.top,
bounds.right - bounds.left, bounds.bottom - bounds.top,
SWP_FRAMECHANGED | SWP_SHOWWINDOW | SWP_NOACTIVATE);
} else {
::MoveWindow(hWindow, bounds.left, bounds.top,
bounds.right - bounds.left, bounds.bottom - bounds.top, TRUE);
}
}
VkResult Win32WsiDriver::createSurface(
HWND hWindow,
PFN_vkGetInstanceProcAddr pfnVkGetInstanceProcAddr,
VkInstance instance,
VkSurfaceKHR* pSurface) {
HINSTANCE hInstance = reinterpret_cast<HINSTANCE>(
GetWindowLongPtr(hWindow, GWLP_HINSTANCE));
auto pfnVkCreateWin32SurfaceKHR = reinterpret_cast<PFN_vkCreateWin32SurfaceKHR>(
pfnVkGetInstanceProcAddr(instance, "vkCreateWin32SurfaceKHR"));
if (!pfnVkCreateWin32SurfaceKHR)
return VK_ERROR_FEATURE_NOT_PRESENT;
VkWin32SurfaceCreateInfoKHR info = { VK_STRUCTURE_TYPE_WIN32_SURFACE_CREATE_INFO_KHR };
info.hinstance = hInstance;
info.hwnd = hWindow;
return (*pfnVkCreateWin32SurfaceKHR)(instance, &info, nullptr, pSurface);
}
}
#endif