| //======================================================================== |
| // 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; |
| } |
| |
| GLFWbool _glfwGetVideoModeWin32(_GLFWmonitor* monitor, GLFWvidmode* mode) |
| { |
| DEVMODEW dm; |
| ZeroMemory(&dm, sizeof(dm)); |
| dm.dmSize = sizeof(dm); |
| |
| if (!EnumDisplaySettingsW(monitor->win32.adapterName, ENUM_CURRENT_SETTINGS, &dm)) |
| { |
| _glfwInputError(GLFW_PLATFORM_ERROR, "Win32: Failed to query display settings"); |
| return GLFW_FALSE; |
| } |
| |
| mode->width = dm.dmPelsWidth; |
| mode->height = dm.dmPelsHeight; |
| mode->refreshRate = dm.dmDisplayFrequency; |
| _glfwSplitBPP(dm.dmBitsPerPel, |
| &mode->redBits, |
| &mode->greenBits, |
| &mode->blueBits); |
| |
| return GLFW_TRUE; |
| } |
| |
| 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); |
| |
| if (_glfw.platform.platformID != GLFW_PLATFORM_WIN32) |
| { |
| _glfwInputError(GLFW_PLATFORM_UNAVAILABLE, "Win32: Platform not initialized"); |
| return NULL; |
| } |
| |
| return monitor->win32.publicAdapterName; |
| } |
| |
| GLFWAPI const char* glfwGetWin32Monitor(GLFWmonitor* handle) |
| { |
| _GLFWmonitor* monitor = (_GLFWmonitor*) handle; |
| _GLFW_REQUIRE_INIT_OR_RETURN(NULL); |
| |
| if (_glfw.platform.platformID != GLFW_PLATFORM_WIN32) |
| { |
| _glfwInputError(GLFW_PLATFORM_UNAVAILABLE, "Win32: Platform not initialized"); |
| return NULL; |
| } |
| |
| return monitor->win32.publicDisplayName; |
| } |
| |
| #endif // _GLFW_WIN32 |
| |