mirror of
https://github.com/gwm17/glfw.git
synced 2025-06-01 01:08:50 -04:00
The monitor handle could have become invalid just before the call to GetDpiForMonitor. It was possible for both window and monitor content scale queries. This ensures both that an appropriate error is emitted and that the retrieved values are zero on error. Fixes #1615
548 lines
16 KiB
C
548 lines
16 KiB
C
//========================================================================
|
|
// GLFW 3.4 Win32 - www.glfw.org
|
|
//------------------------------------------------------------------------
|
|
// Copyright (c) 2002-2006 Marcus Geelnard
|
|
// Copyright (c) 2006-2019 Camilla Löwy <elmindreda@glfw.org>
|
|
//
|
|
// This software is provided 'as-is', without any express or implied
|
|
// warranty. In no event will the authors be held liable for any damages
|
|
// arising from the use of this software.
|
|
//
|
|
// Permission is granted to anyone to use this software for any purpose,
|
|
// including commercial applications, and to alter it and redistribute it
|
|
// freely, subject to the following restrictions:
|
|
//
|
|
// 1. The origin of this software must not be misrepresented; you must not
|
|
// claim that you wrote the original software. If you use this software
|
|
// in a product, an acknowledgment in the product documentation would
|
|
// be appreciated but is not required.
|
|
//
|
|
// 2. Altered source versions must be plainly marked as such, and must not
|
|
// be misrepresented as being the original software.
|
|
//
|
|
// 3. This notice may not be removed or altered from any source
|
|
// distribution.
|
|
//
|
|
//========================================================================
|
|
// Please use C89 style variable declarations in this file because VS 2010
|
|
//========================================================================
|
|
|
|
#include "internal.h"
|
|
|
|
#include <stdlib.h>
|
|
#include <string.h>
|
|
#include <limits.h>
|
|
#include <wchar.h>
|
|
|
|
|
|
// Callback for EnumDisplayMonitors in createMonitor
|
|
//
|
|
static BOOL CALLBACK monitorCallback(HMONITOR handle,
|
|
HDC dc,
|
|
RECT* rect,
|
|
LPARAM data)
|
|
{
|
|
MONITORINFOEXW mi;
|
|
ZeroMemory(&mi, sizeof(mi));
|
|
mi.cbSize = sizeof(mi);
|
|
|
|
if (GetMonitorInfoW(handle, (MONITORINFO*) &mi))
|
|
{
|
|
_GLFWmonitor* monitor = (_GLFWmonitor*) data;
|
|
if (wcscmp(mi.szDevice, monitor->win32.adapterName) == 0)
|
|
monitor->win32.handle = handle;
|
|
}
|
|
|
|
return TRUE;
|
|
}
|
|
|
|
// Create monitor from an adapter and (optionally) a display
|
|
//
|
|
static _GLFWmonitor* createMonitor(DISPLAY_DEVICEW* adapter,
|
|
DISPLAY_DEVICEW* display)
|
|
{
|
|
_GLFWmonitor* monitor;
|
|
int widthMM, heightMM;
|
|
char* name;
|
|
HDC dc;
|
|
DEVMODEW dm;
|
|
RECT rect;
|
|
|
|
if (display)
|
|
name = _glfwCreateUTF8FromWideStringWin32(display->DeviceString);
|
|
else
|
|
name = _glfwCreateUTF8FromWideStringWin32(adapter->DeviceString);
|
|
if (!name)
|
|
return NULL;
|
|
|
|
ZeroMemory(&dm, sizeof(dm));
|
|
dm.dmSize = sizeof(dm);
|
|
EnumDisplaySettingsW(adapter->DeviceName, ENUM_CURRENT_SETTINGS, &dm);
|
|
|
|
dc = CreateDCW(L"DISPLAY", adapter->DeviceName, NULL, NULL);
|
|
|
|
if (IsWindows8Point1OrGreater())
|
|
{
|
|
widthMM = GetDeviceCaps(dc, HORZSIZE);
|
|
heightMM = GetDeviceCaps(dc, VERTSIZE);
|
|
}
|
|
else
|
|
{
|
|
widthMM = (int) (dm.dmPelsWidth * 25.4f / GetDeviceCaps(dc, LOGPIXELSX));
|
|
heightMM = (int) (dm.dmPelsHeight * 25.4f / GetDeviceCaps(dc, LOGPIXELSY));
|
|
}
|
|
|
|
DeleteDC(dc);
|
|
|
|
monitor = _glfwAllocMonitor(name, widthMM, heightMM);
|
|
_glfw_free(name);
|
|
|
|
if (adapter->StateFlags & DISPLAY_DEVICE_MODESPRUNED)
|
|
monitor->win32.modesPruned = GLFW_TRUE;
|
|
|
|
wcscpy(monitor->win32.adapterName, adapter->DeviceName);
|
|
WideCharToMultiByte(CP_UTF8, 0,
|
|
adapter->DeviceName, -1,
|
|
monitor->win32.publicAdapterName,
|
|
sizeof(monitor->win32.publicAdapterName),
|
|
NULL, NULL);
|
|
|
|
if (display)
|
|
{
|
|
wcscpy(monitor->win32.displayName, display->DeviceName);
|
|
WideCharToMultiByte(CP_UTF8, 0,
|
|
display->DeviceName, -1,
|
|
monitor->win32.publicDisplayName,
|
|
sizeof(monitor->win32.publicDisplayName),
|
|
NULL, NULL);
|
|
}
|
|
|
|
rect.left = dm.dmPosition.x;
|
|
rect.top = dm.dmPosition.y;
|
|
rect.right = dm.dmPosition.x + dm.dmPelsWidth;
|
|
rect.bottom = dm.dmPosition.y + dm.dmPelsHeight;
|
|
|
|
EnumDisplayMonitors(NULL, &rect, monitorCallback, (LPARAM) monitor);
|
|
return monitor;
|
|
}
|
|
|
|
|
|
//////////////////////////////////////////////////////////////////////////
|
|
////// GLFW internal API //////
|
|
//////////////////////////////////////////////////////////////////////////
|
|
|
|
// Poll for changes in the set of connected monitors
|
|
//
|
|
void _glfwPollMonitorsWin32(void)
|
|
{
|
|
int i, disconnectedCount;
|
|
_GLFWmonitor** disconnected = NULL;
|
|
DWORD adapterIndex, displayIndex;
|
|
DISPLAY_DEVICEW adapter, display;
|
|
_GLFWmonitor* monitor;
|
|
|
|
disconnectedCount = _glfw.monitorCount;
|
|
if (disconnectedCount)
|
|
{
|
|
disconnected = _glfw_calloc(_glfw.monitorCount, sizeof(_GLFWmonitor*));
|
|
memcpy(disconnected,
|
|
_glfw.monitors,
|
|
_glfw.monitorCount * sizeof(_GLFWmonitor*));
|
|
}
|
|
|
|
for (adapterIndex = 0; ; adapterIndex++)
|
|
{
|
|
int type = _GLFW_INSERT_LAST;
|
|
|
|
ZeroMemory(&adapter, sizeof(adapter));
|
|
adapter.cb = sizeof(adapter);
|
|
|
|
if (!EnumDisplayDevicesW(NULL, adapterIndex, &adapter, 0))
|
|
break;
|
|
|
|
if (!(adapter.StateFlags & DISPLAY_DEVICE_ACTIVE))
|
|
continue;
|
|
|
|
if (adapter.StateFlags & DISPLAY_DEVICE_PRIMARY_DEVICE)
|
|
type = _GLFW_INSERT_FIRST;
|
|
|
|
for (displayIndex = 0; ; displayIndex++)
|
|
{
|
|
ZeroMemory(&display, sizeof(display));
|
|
display.cb = sizeof(display);
|
|
|
|
if (!EnumDisplayDevicesW(adapter.DeviceName, displayIndex, &display, 0))
|
|
break;
|
|
|
|
if (!(display.StateFlags & DISPLAY_DEVICE_ACTIVE))
|
|
continue;
|
|
|
|
for (i = 0; i < disconnectedCount; i++)
|
|
{
|
|
if (disconnected[i] &&
|
|
wcscmp(disconnected[i]->win32.displayName,
|
|
display.DeviceName) == 0)
|
|
{
|
|
disconnected[i] = NULL;
|
|
// handle may have changed, update
|
|
EnumDisplayMonitors(NULL, NULL, monitorCallback, (LPARAM) _glfw.monitors[i]);
|
|
break;
|
|
}
|
|
}
|
|
|
|
if (i < disconnectedCount)
|
|
continue;
|
|
|
|
monitor = createMonitor(&adapter, &display);
|
|
if (!monitor)
|
|
{
|
|
_glfw_free(disconnected);
|
|
return;
|
|
}
|
|
|
|
_glfwInputMonitor(monitor, GLFW_CONNECTED, type);
|
|
|
|
type = _GLFW_INSERT_LAST;
|
|
}
|
|
|
|
// HACK: If an active adapter does not have any display devices
|
|
// (as sometimes happens), add it directly as a monitor
|
|
if (displayIndex == 0)
|
|
{
|
|
for (i = 0; i < disconnectedCount; i++)
|
|
{
|
|
if (disconnected[i] &&
|
|
wcscmp(disconnected[i]->win32.adapterName,
|
|
adapter.DeviceName) == 0)
|
|
{
|
|
disconnected[i] = NULL;
|
|
break;
|
|
}
|
|
}
|
|
|
|
if (i < disconnectedCount)
|
|
continue;
|
|
|
|
monitor = createMonitor(&adapter, NULL);
|
|
if (!monitor)
|
|
{
|
|
_glfw_free(disconnected);
|
|
return;
|
|
}
|
|
|
|
_glfwInputMonitor(monitor, GLFW_CONNECTED, type);
|
|
}
|
|
}
|
|
|
|
for (i = 0; i < disconnectedCount; i++)
|
|
{
|
|
if (disconnected[i])
|
|
_glfwInputMonitor(disconnected[i], GLFW_DISCONNECTED, 0);
|
|
}
|
|
|
|
_glfw_free(disconnected);
|
|
}
|
|
|
|
// Change the current video mode
|
|
//
|
|
void _glfwSetVideoModeWin32(_GLFWmonitor* monitor, const GLFWvidmode* desired)
|
|
{
|
|
GLFWvidmode current;
|
|
const GLFWvidmode* best;
|
|
DEVMODEW dm;
|
|
LONG result;
|
|
|
|
best = _glfwChooseVideoMode(monitor, desired);
|
|
_glfwGetVideoModeWin32(monitor, ¤t);
|
|
if (_glfwCompareVideoModes(¤t, best) == 0)
|
|
return;
|
|
|
|
ZeroMemory(&dm, sizeof(dm));
|
|
dm.dmSize = sizeof(dm);
|
|
dm.dmFields = DM_PELSWIDTH | DM_PELSHEIGHT | DM_BITSPERPEL |
|
|
DM_DISPLAYFREQUENCY;
|
|
dm.dmPelsWidth = best->width;
|
|
dm.dmPelsHeight = best->height;
|
|
dm.dmBitsPerPel = best->redBits + best->greenBits + best->blueBits;
|
|
dm.dmDisplayFrequency = best->refreshRate;
|
|
|
|
if (dm.dmBitsPerPel < 15 || dm.dmBitsPerPel >= 24)
|
|
dm.dmBitsPerPel = 32;
|
|
|
|
result = ChangeDisplaySettingsExW(monitor->win32.adapterName,
|
|
&dm,
|
|
NULL,
|
|
CDS_FULLSCREEN,
|
|
NULL);
|
|
if (result == DISP_CHANGE_SUCCESSFUL)
|
|
monitor->win32.modeChanged = GLFW_TRUE;
|
|
else
|
|
{
|
|
const char* description = "Unknown error";
|
|
|
|
if (result == DISP_CHANGE_BADDUALVIEW)
|
|
description = "The system uses DualView";
|
|
else if (result == DISP_CHANGE_BADFLAGS)
|
|
description = "Invalid flags";
|
|
else if (result == DISP_CHANGE_BADMODE)
|
|
description = "Graphics mode not supported";
|
|
else if (result == DISP_CHANGE_BADPARAM)
|
|
description = "Invalid parameter";
|
|
else if (result == DISP_CHANGE_FAILED)
|
|
description = "Graphics mode failed";
|
|
else if (result == DISP_CHANGE_NOTUPDATED)
|
|
description = "Failed to write to registry";
|
|
else if (result == DISP_CHANGE_RESTART)
|
|
description = "Computer restart required";
|
|
|
|
_glfwInputError(GLFW_PLATFORM_ERROR,
|
|
"Win32: Failed to set video mode: %s",
|
|
description);
|
|
}
|
|
}
|
|
|
|
// Restore the previously saved (original) video mode
|
|
//
|
|
void _glfwRestoreVideoModeWin32(_GLFWmonitor* monitor)
|
|
{
|
|
if (monitor->win32.modeChanged)
|
|
{
|
|
ChangeDisplaySettingsExW(monitor->win32.adapterName,
|
|
NULL, NULL, CDS_FULLSCREEN, NULL);
|
|
monitor->win32.modeChanged = GLFW_FALSE;
|
|
}
|
|
}
|
|
|
|
void _glfwGetHMONITORContentScaleWin32(HMONITOR handle, float* xscale, float* yscale)
|
|
{
|
|
UINT xdpi, ydpi;
|
|
|
|
if (xscale)
|
|
*xscale = 0.f;
|
|
if (yscale)
|
|
*yscale = 0.f;
|
|
|
|
if (IsWindows8Point1OrGreater())
|
|
{
|
|
if (GetDpiForMonitor(handle, MDT_EFFECTIVE_DPI, &xdpi, &ydpi) != S_OK)
|
|
{
|
|
_glfwInputError(GLFW_PLATFORM_ERROR, "Win32: Failed to query monitor DPI");
|
|
return;
|
|
}
|
|
}
|
|
else
|
|
{
|
|
const HDC dc = GetDC(NULL);
|
|
xdpi = GetDeviceCaps(dc, LOGPIXELSX);
|
|
ydpi = GetDeviceCaps(dc, LOGPIXELSY);
|
|
ReleaseDC(NULL, dc);
|
|
}
|
|
|
|
if (xscale)
|
|
*xscale = xdpi / (float) USER_DEFAULT_SCREEN_DPI;
|
|
if (yscale)
|
|
*yscale = ydpi / (float) USER_DEFAULT_SCREEN_DPI;
|
|
}
|
|
|
|
|
|
//////////////////////////////////////////////////////////////////////////
|
|
////// GLFW platform API //////
|
|
//////////////////////////////////////////////////////////////////////////
|
|
|
|
void _glfwFreeMonitorWin32(_GLFWmonitor* monitor)
|
|
{
|
|
}
|
|
|
|
void _glfwGetMonitorPosWin32(_GLFWmonitor* monitor, int* xpos, int* ypos)
|
|
{
|
|
DEVMODEW dm;
|
|
ZeroMemory(&dm, sizeof(dm));
|
|
dm.dmSize = sizeof(dm);
|
|
|
|
EnumDisplaySettingsExW(monitor->win32.adapterName,
|
|
ENUM_CURRENT_SETTINGS,
|
|
&dm,
|
|
EDS_ROTATEDMODE);
|
|
|
|
if (xpos)
|
|
*xpos = dm.dmPosition.x;
|
|
if (ypos)
|
|
*ypos = dm.dmPosition.y;
|
|
}
|
|
|
|
void _glfwGetMonitorContentScaleWin32(_GLFWmonitor* monitor,
|
|
float* xscale, float* yscale)
|
|
{
|
|
_glfwGetHMONITORContentScaleWin32(monitor->win32.handle, xscale, yscale);
|
|
}
|
|
|
|
void _glfwGetMonitorWorkareaWin32(_GLFWmonitor* monitor,
|
|
int* xpos, int* ypos,
|
|
int* width, int* height)
|
|
{
|
|
MONITORINFO mi = { sizeof(mi) };
|
|
GetMonitorInfo(monitor->win32.handle, &mi);
|
|
|
|
if (xpos)
|
|
*xpos = mi.rcWork.left;
|
|
if (ypos)
|
|
*ypos = mi.rcWork.top;
|
|
if (width)
|
|
*width = mi.rcWork.right - mi.rcWork.left;
|
|
if (height)
|
|
*height = mi.rcWork.bottom - mi.rcWork.top;
|
|
}
|
|
|
|
GLFWvidmode* _glfwGetVideoModesWin32(_GLFWmonitor* monitor, int* count)
|
|
{
|
|
int modeIndex = 0, size = 0;
|
|
GLFWvidmode* result = NULL;
|
|
|
|
*count = 0;
|
|
|
|
for (;;)
|
|
{
|
|
int i;
|
|
GLFWvidmode mode;
|
|
DEVMODEW dm;
|
|
|
|
ZeroMemory(&dm, sizeof(dm));
|
|
dm.dmSize = sizeof(dm);
|
|
|
|
if (!EnumDisplaySettingsW(monitor->win32.adapterName, modeIndex, &dm))
|
|
break;
|
|
|
|
modeIndex++;
|
|
|
|
// Skip modes with less than 15 BPP
|
|
if (dm.dmBitsPerPel < 15)
|
|
continue;
|
|
|
|
mode.width = dm.dmPelsWidth;
|
|
mode.height = dm.dmPelsHeight;
|
|
mode.refreshRate = dm.dmDisplayFrequency;
|
|
_glfwSplitBPP(dm.dmBitsPerPel,
|
|
&mode.redBits,
|
|
&mode.greenBits,
|
|
&mode.blueBits);
|
|
|
|
for (i = 0; i < *count; i++)
|
|
{
|
|
if (_glfwCompareVideoModes(result + i, &mode) == 0)
|
|
break;
|
|
}
|
|
|
|
// Skip duplicate modes
|
|
if (i < *count)
|
|
continue;
|
|
|
|
if (monitor->win32.modesPruned)
|
|
{
|
|
// Skip modes not supported by the connected displays
|
|
if (ChangeDisplaySettingsExW(monitor->win32.adapterName,
|
|
&dm,
|
|
NULL,
|
|
CDS_TEST,
|
|
NULL) != DISP_CHANGE_SUCCESSFUL)
|
|
{
|
|
continue;
|
|
}
|
|
}
|
|
|
|
if (*count == size)
|
|
{
|
|
size += 128;
|
|
result = (GLFWvidmode*) _glfw_realloc(result, size * sizeof(GLFWvidmode));
|
|
}
|
|
|
|
(*count)++;
|
|
result[*count - 1] = mode;
|
|
}
|
|
|
|
if (!*count)
|
|
{
|
|
// HACK: Report the current mode if no valid modes were found
|
|
result = _glfw_calloc(1, sizeof(GLFWvidmode));
|
|
_glfwGetVideoModeWin32(monitor, result);
|
|
*count = 1;
|
|
}
|
|
|
|
return result;
|
|
}
|
|
|
|
void _glfwGetVideoModeWin32(_GLFWmonitor* monitor, GLFWvidmode* mode)
|
|
{
|
|
DEVMODEW dm;
|
|
ZeroMemory(&dm, sizeof(dm));
|
|
dm.dmSize = sizeof(dm);
|
|
|
|
EnumDisplaySettingsW(monitor->win32.adapterName, ENUM_CURRENT_SETTINGS, &dm);
|
|
|
|
mode->width = dm.dmPelsWidth;
|
|
mode->height = dm.dmPelsHeight;
|
|
mode->refreshRate = dm.dmDisplayFrequency;
|
|
_glfwSplitBPP(dm.dmBitsPerPel,
|
|
&mode->redBits,
|
|
&mode->greenBits,
|
|
&mode->blueBits);
|
|
}
|
|
|
|
GLFWbool _glfwGetGammaRampWin32(_GLFWmonitor* monitor, GLFWgammaramp* ramp)
|
|
{
|
|
HDC dc;
|
|
WORD values[3][256];
|
|
|
|
dc = CreateDCW(L"DISPLAY", monitor->win32.adapterName, NULL, NULL);
|
|
GetDeviceGammaRamp(dc, values);
|
|
DeleteDC(dc);
|
|
|
|
_glfwAllocGammaArrays(ramp, 256);
|
|
|
|
memcpy(ramp->red, values[0], sizeof(values[0]));
|
|
memcpy(ramp->green, values[1], sizeof(values[1]));
|
|
memcpy(ramp->blue, values[2], sizeof(values[2]));
|
|
|
|
return GLFW_TRUE;
|
|
}
|
|
|
|
void _glfwSetGammaRampWin32(_GLFWmonitor* monitor, const GLFWgammaramp* ramp)
|
|
{
|
|
HDC dc;
|
|
WORD values[3][256];
|
|
|
|
if (ramp->size != 256)
|
|
{
|
|
_glfwInputError(GLFW_PLATFORM_ERROR,
|
|
"Win32: Gamma ramp size must be 256");
|
|
return;
|
|
}
|
|
|
|
memcpy(values[0], ramp->red, sizeof(values[0]));
|
|
memcpy(values[1], ramp->green, sizeof(values[1]));
|
|
memcpy(values[2], ramp->blue, sizeof(values[2]));
|
|
|
|
dc = CreateDCW(L"DISPLAY", monitor->win32.adapterName, NULL, NULL);
|
|
SetDeviceGammaRamp(dc, values);
|
|
DeleteDC(dc);
|
|
}
|
|
|
|
|
|
//////////////////////////////////////////////////////////////////////////
|
|
////// GLFW native API //////
|
|
//////////////////////////////////////////////////////////////////////////
|
|
|
|
GLFWAPI const char* glfwGetWin32Adapter(GLFWmonitor* handle)
|
|
{
|
|
_GLFWmonitor* monitor = (_GLFWmonitor*) handle;
|
|
_GLFW_REQUIRE_INIT_OR_RETURN(NULL);
|
|
return monitor->win32.publicAdapterName;
|
|
}
|
|
|
|
GLFWAPI const char* glfwGetWin32Monitor(GLFWmonitor* handle)
|
|
{
|
|
_GLFWmonitor* monitor = (_GLFWmonitor*) handle;
|
|
_GLFW_REQUIRE_INIT_OR_RETURN(NULL);
|
|
return monitor->win32.publicDisplayName;
|
|
}
|
|
|