You cannot select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and dots ('.'), can be up to 35 characters long. Letters must be lowercase.
549 lines
16 KiB
549 lines
16 KiB
//======================================================================== |
|
// 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. |
|
// |
|
//======================================================================== |
|
|
|
#include "internal.h" |
|
|
|
#if defined(_GLFW_WIN32) |
|
|
|
#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) }; |
|
GetMonitorInfoW(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; |
|
} |
|
|
|
#endif // _GLFW_WIN32 |
|
|
|
|