//======================================================================== // GLFW 3.4 Win32 - www.glfw.org //------------------------------------------------------------------------ // Copyright (c) 2002-2006 Marcus Geelnard // Copyright (c) 2006-2019 Camilla Löwy // // 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" #if defined(_GLFW_WIN32) #include #include #include #include #if WINVER < 0x0601 // To be able to compile on windows Vista and XP even though the feature won't be used. typedef struct DISPLAYCONFIG_PATH_SOURCE_INFO { LUID adapterId; UINT32 id; union { UINT32 modeInfoIdx; struct { UINT32 cloneGroupId : 16; UINT32 sourceModeInfoIdx : 16; } DUMMYSTRUCTNAME; } DUMMYUNIONNAME; UINT32 statusFlags; } DISPLAYCONFIG_PATH_SOURCE_INFO; typedef enum { DISPLAYCONFIG_OUTPUT_TECHNOLOGY_OTHER = -1, DISPLAYCONFIG_OUTPUT_TECHNOLOGY_HD15 = 0, DISPLAYCONFIG_OUTPUT_TECHNOLOGY_SVIDEO = 1, DISPLAYCONFIG_OUTPUT_TECHNOLOGY_COMPOSITE_VIDEO = 2, DISPLAYCONFIG_OUTPUT_TECHNOLOGY_COMPONENT_VIDEO = 3, DISPLAYCONFIG_OUTPUT_TECHNOLOGY_DVI = 4, DISPLAYCONFIG_OUTPUT_TECHNOLOGY_HDMI = 5, DISPLAYCONFIG_OUTPUT_TECHNOLOGY_LVDS = 6, DISPLAYCONFIG_OUTPUT_TECHNOLOGY_D_JPN = 8, DISPLAYCONFIG_OUTPUT_TECHNOLOGY_SDI = 9, DISPLAYCONFIG_OUTPUT_TECHNOLOGY_DISPLAYPORT_EXTERNAL = 10, DISPLAYCONFIG_OUTPUT_TECHNOLOGY_DISPLAYPORT_EMBEDDED = 11, DISPLAYCONFIG_OUTPUT_TECHNOLOGY_UDI_EXTERNAL = 12, DISPLAYCONFIG_OUTPUT_TECHNOLOGY_UDI_EMBEDDED = 13, DISPLAYCONFIG_OUTPUT_TECHNOLOGY_SDTVDONGLE = 14, DISPLAYCONFIG_OUTPUT_TECHNOLOGY_MIRACAST = 15, DISPLAYCONFIG_OUTPUT_TECHNOLOGY_INDIRECT_WIRED = 16, DISPLAYCONFIG_OUTPUT_TECHNOLOGY_INDIRECT_VIRTUAL = 17, DISPLAYCONFIG_OUTPUT_TECHNOLOGY_DISPLAYPORT_USB_TUNNEL, DISPLAYCONFIG_OUTPUT_TECHNOLOGY_INTERNAL = (int)0x80000000, // Cast required to enforce 4 byte enum. DISPLAYCONFIG_OUTPUT_TECHNOLOGY_FORCE_UINT32 = (int)0xFFFFFFFF // Cast required to enforce 4 byte enum. } DISPLAYCONFIG_VIDEO_OUTPUT_TECHNOLOGY; typedef enum { DISPLAYCONFIG_ROTATION_IDENTITY = 1, DISPLAYCONFIG_ROTATION_ROTATE90 = 2, DISPLAYCONFIG_ROTATION_ROTATE180 = 3, DISPLAYCONFIG_ROTATION_ROTATE270 = 4, DISPLAYCONFIG_ROTATION_FORCE_UINT32 = 0xFFFFFFFF } DISPLAYCONFIG_ROTATION; typedef enum { DISPLAYCONFIG_SCALING_IDENTITY = 1, DISPLAYCONFIG_SCALING_CENTERED = 2, DISPLAYCONFIG_SCALING_STRETCHED = 3, DISPLAYCONFIG_SCALING_ASPECTRATIOCENTEREDMAX = 4, DISPLAYCONFIG_SCALING_CUSTOM = 5, DISPLAYCONFIG_SCALING_PREFERRED = 128, DISPLAYCONFIG_SCALING_FORCE_UINT32 = 0xFFFFFFFF } DISPLAYCONFIG_SCALING; typedef struct DISPLAYCONFIG_RATIONAL { UINT32 Numerator; UINT32 Denominator; } DISPLAYCONFIG_RATIONAL; typedef enum { DISPLAYCONFIG_SCANLINE_ORDERING_UNSPECIFIED = 0, DISPLAYCONFIG_SCANLINE_ORDERING_PROGRESSIVE = 1, DISPLAYCONFIG_SCANLINE_ORDERING_INTERLACED = 2, DISPLAYCONFIG_SCANLINE_ORDERING_INTERLACED_UPPERFIELDFIRST, DISPLAYCONFIG_SCANLINE_ORDERING_INTERLACED_LOWERFIELDFIRST = 3, DISPLAYCONFIG_SCANLINE_ORDERING_FORCE_UINT32 = 0xFFFFFFFF } DISPLAYCONFIG_SCANLINE_ORDERING; typedef struct DISPLAYCONFIG_PATH_TARGET_INFO { LUID adapterId; UINT32 id; union { UINT32 modeInfoIdx; struct { UINT32 desktopModeInfoIdx : 16; UINT32 targetModeInfoIdx : 16; } DUMMYSTRUCTNAME; } DUMMYUNIONNAME; DISPLAYCONFIG_VIDEO_OUTPUT_TECHNOLOGY outputTechnology; DISPLAYCONFIG_ROTATION rotation; DISPLAYCONFIG_SCALING scaling; DISPLAYCONFIG_RATIONAL refreshRate; DISPLAYCONFIG_SCANLINE_ORDERING scanLineOrdering; BOOL targetAvailable; UINT32 statusFlags; } DISPLAYCONFIG_PATH_TARGET_INFO; typedef struct DISPLAYCONFIG_PATH_INFO { DISPLAYCONFIG_PATH_SOURCE_INFO sourceInfo; DISPLAYCONFIG_PATH_TARGET_INFO targetInfo; UINT32 flags; } DISPLAYCONFIG_PATH_INFO; typedef enum { DISPLAYCONFIG_MODE_INFO_TYPE_SOURCE = 1, DISPLAYCONFIG_MODE_INFO_TYPE_TARGET = 2, DISPLAYCONFIG_MODE_INFO_TYPE_DESKTOP_IMAGE = 3, DISPLAYCONFIG_MODE_INFO_TYPE_FORCE_UINT32 = 0xFFFFFFFF } DISPLAYCONFIG_MODE_INFO_TYPE; typedef struct DISPLAYCONFIG_2DREGION { UINT32 cx; UINT32 cy; } DISPLAYCONFIG_2DREGION; typedef struct DISPLAYCONFIG_VIDEO_SIGNAL_INFO { UINT64 pixelRate; DISPLAYCONFIG_RATIONAL hSyncFreq; DISPLAYCONFIG_RATIONAL vSyncFreq; DISPLAYCONFIG_2DREGION activeSize; DISPLAYCONFIG_2DREGION totalSize; union { struct { UINT32 videoStandard : 16; UINT32 vSyncFreqDivider : 6; UINT32 reserved : 10; } AdditionalSignalInfo; UINT32 videoStandard; } DUMMYUNIONNAME; DISPLAYCONFIG_SCANLINE_ORDERING scanLineOrdering; } DISPLAYCONFIG_VIDEO_SIGNAL_INFO; typedef struct DISPLAYCONFIG_TARGET_MODE { DISPLAYCONFIG_VIDEO_SIGNAL_INFO targetVideoSignalInfo; } DISPLAYCONFIG_TARGET_MODE; typedef enum { DISPLAYCONFIG_PIXELFORMAT_8BPP = 1, DISPLAYCONFIG_PIXELFORMAT_16BPP = 2, DISPLAYCONFIG_PIXELFORMAT_24BPP = 3, DISPLAYCONFIG_PIXELFORMAT_32BPP = 4, DISPLAYCONFIG_PIXELFORMAT_NONGDI = 5, DISPLAYCONFIG_PIXELFORMAT_FORCE_UINT32 = 0xffffffff } DISPLAYCONFIG_PIXELFORMAT; typedef struct DISPLAYCONFIG_SOURCE_MODE { UINT32 width; UINT32 height; DISPLAYCONFIG_PIXELFORMAT pixelFormat; POINTL position; } DISPLAYCONFIG_SOURCE_MODE; typedef struct DISPLAYCONFIG_DESKTOP_IMAGE_INFO { POINTL PathSourceSize; RECTL DesktopImageRegion; RECTL DesktopImageClip; } DISPLAYCONFIG_DESKTOP_IMAGE_INFO; typedef struct DISPLAYCONFIG_MODE_INFO { DISPLAYCONFIG_MODE_INFO_TYPE infoType; UINT32 id; LUID adapterId; union { DISPLAYCONFIG_TARGET_MODE targetMode; DISPLAYCONFIG_SOURCE_MODE sourceMode; DISPLAYCONFIG_DESKTOP_IMAGE_INFO desktopImageInfo; } DUMMYUNIONNAME; } DISPLAYCONFIG_MODE_INFO; const UINT32 QDC_ONLY_ACTIVE_PATHS = 0x00000002; typedef enum { DISPLAYCONFIG_DEVICE_INFO_GET_SOURCE_NAME = 1, DISPLAYCONFIG_DEVICE_INFO_GET_TARGET_NAME = 2, DISPLAYCONFIG_DEVICE_INFO_GET_TARGET_PREFERRED_MODE = 3, DISPLAYCONFIG_DEVICE_INFO_GET_ADAPTER_NAME = 4, DISPLAYCONFIG_DEVICE_INFO_SET_TARGET_PERSISTENCE = 5, DISPLAYCONFIG_DEVICE_INFO_GET_TARGET_BASE_TYPE = 6, DISPLAYCONFIG_DEVICE_INFO_GET_SUPPORT_VIRTUAL_RESOLUTION = 7, DISPLAYCONFIG_DEVICE_INFO_SET_SUPPORT_VIRTUAL_RESOLUTION = 8, DISPLAYCONFIG_DEVICE_INFO_GET_ADVANCED_COLOR_INFO = 9, DISPLAYCONFIG_DEVICE_INFO_SET_ADVANCED_COLOR_STATE = 10, DISPLAYCONFIG_DEVICE_INFO_GET_SDR_WHITE_LEVEL = 11, DISPLAYCONFIG_DEVICE_INFO_GET_MONITOR_SPECIALIZATION, DISPLAYCONFIG_DEVICE_INFO_SET_MONITOR_SPECIALIZATION, DISPLAYCONFIG_DEVICE_INFO_FORCE_UINT32 = 0xFFFFFFFF } DISPLAYCONFIG_DEVICE_INFO_TYPE; typedef struct DISPLAYCONFIG_DEVICE_INFO_HEADER { DISPLAYCONFIG_DEVICE_INFO_TYPE type; UINT32 size; LUID adapterId; UINT32 id; } DISPLAYCONFIG_DEVICE_INFO_HEADER; typedef struct DISPLAYCONFIG_SOURCE_DEVICE_NAME { DISPLAYCONFIG_DEVICE_INFO_HEADER header; WCHAR viewGdiDeviceName[CCHDEVICENAME]; } DISPLAYCONFIG_SOURCE_DEVICE_NAME; typedef struct DISPLAYCONFIG_TARGET_DEVICE_NAME_FLAGS { union { struct { UINT32 friendlyNameFromEdid : 1; UINT32 friendlyNameForced : 1; UINT32 edidIdsValid : 1; UINT32 reserved : 29; } DUMMYSTRUCTNAME; UINT32 value; } DUMMYUNIONNAME; } DISPLAYCONFIG_TARGET_DEVICE_NAME_FLAGS; typedef struct DISPLAYCONFIG_TARGET_DEVICE_NAME { DISPLAYCONFIG_DEVICE_INFO_HEADER header; DISPLAYCONFIG_TARGET_DEVICE_NAME_FLAGS flags; DISPLAYCONFIG_VIDEO_OUTPUT_TECHNOLOGY outputTechnology; UINT16 edidManufactureId; UINT16 edidProductCodeId; UINT32 connectorInstance; WCHAR monitorFriendlyDeviceName[64]; WCHAR monitorDevicePath[128]; } DISPLAYCONFIG_TARGET_DEVICE_NAME; typedef enum DISPLAYCONFIG_TOPOLOGY_ID { DISPLAYCONFIG_TOPOLOGY_INTERNAL = 0x00000001, DISPLAYCONFIG_TOPOLOGY_CLONE = 0x00000002, DISPLAYCONFIG_TOPOLOGY_EXTEND = 0x00000004, DISPLAYCONFIG_TOPOLOGY_EXTERNAL = 0x00000008, DISPLAYCONFIG_TOPOLOGY_FORCE_UINT32 = 0xFFFFFFFF } DISPLAYCONFIG_TOPOLOGY_ID; #endif //#if WINVER < 0x0601 typedef LONG (*pGetDisplayConfigBufferSizes)(UINT32 flags, UINT32 *numPathArrayElements, UINT32 *numModeInfoArrayElements); typedef LONG (*pQueryDisplayConfig)(UINT32 flags, UINT32 *numPathArrayElements, DISPLAYCONFIG_PATH_INFO *pathArray, UINT32 *numModeInfoArrayElements, DISPLAYCONFIG_MODE_INFO *modeInfoArray, DISPLAYCONFIG_TOPOLOGY_ID *currentTopologyId); typedef LONG (*pDisplayConfigGetDeviceInfo)(DISPLAYCONFIG_DEVICE_INFO_HEADER* requestPacket); typedef struct AccurateMonitorNameRequiredData { HMODULE m_dll; pGetDisplayConfigBufferSizes m_GetDisplayConfigBufferSizes; pQueryDisplayConfig m_QueryDisplayConfig; pDisplayConfigGetDeviceInfo m_DisplayConfigGetDeviceInfo; } AccurateMonitorNameRequiredData; BOOL loadWin7MonitorPointers(AccurateMonitorNameRequiredData *io_ptrs) { if(!IsWindows7OrGreater()) return 0; io_ptrs->m_dll = LoadLibrary(L"User32.dll"); if (io_ptrs == NULL) return 0; io_ptrs->m_GetDisplayConfigBufferSizes = (pGetDisplayConfigBufferSizes)GetProcAddress(io_ptrs->m_dll, "GetDisplayConfigBufferSizes"); if(io_ptrs->m_GetDisplayConfigBufferSizes == NULL) return 0; io_ptrs->m_QueryDisplayConfig = (pQueryDisplayConfig)GetProcAddress(io_ptrs->m_dll, "QueryDisplayConfig"); if(io_ptrs->m_QueryDisplayConfig == NULL) return 0; io_ptrs->m_DisplayConfigGetDeviceInfo = (pDisplayConfigGetDeviceInfo)GetProcAddress(io_ptrs->m_dll, "DisplayConfigGetDeviceInfo"); if(io_ptrs->m_DisplayConfigGetDeviceInfo == NULL) return 0; return 1; } // If the returned pointer is valid (not NULL) the caller of this function is in charge of freeing the memory when he is done. static char * GetAccurateMonitorName(const WCHAR *deviceName) { AccurateMonitorNameRequiredData dllPointers; if(loadWin7MonitorPointers(&dllPointers) == 0) return NULL; DISPLAYCONFIG_PATH_INFO *paths; paths = NULL; DISPLAYCONFIG_MODE_INFO *modes; modes = NULL; char *retval; retval = NULL; UINT32 pathCount; pathCount = 0; UINT32 modeCount; modeCount = 0; UINT32 i; LONG rc; do { rc = dllPointers.m_GetDisplayConfigBufferSizes(QDC_ONLY_ACTIVE_PATHS, &pathCount, &modeCount); if (rc != ERROR_SUCCESS) { goto GET_ACCURATE_MONITOR_NAME_FAILURE; } free(paths); free(modes); paths = (DISPLAYCONFIG_PATH_INFO *) malloc(sizeof (DISPLAYCONFIG_PATH_INFO) * pathCount); modes = (DISPLAYCONFIG_MODE_INFO *) malloc(sizeof (DISPLAYCONFIG_MODE_INFO) * modeCount); if ((paths == NULL) || (modes == NULL)) { goto GET_ACCURATE_MONITOR_NAME_FAILURE; } rc = dllPointers.m_QueryDisplayConfig(QDC_ONLY_ACTIVE_PATHS, &pathCount, paths, &modeCount, modes, 0); } while (rc == ERROR_INSUFFICIENT_BUFFER); if (rc == ERROR_SUCCESS) { for (i = 0; i < pathCount; i++) { DISPLAYCONFIG_SOURCE_DEVICE_NAME sourceName; DISPLAYCONFIG_TARGET_DEVICE_NAME targetName; ZeroMemory(&sourceName, sizeof(sourceName)); sourceName.header.adapterId = paths[i].targetInfo.adapterId; sourceName.header.type = DISPLAYCONFIG_DEVICE_INFO_GET_SOURCE_NAME; sourceName.header.size = sizeof (sourceName); sourceName.header.id = paths[i].sourceInfo.id; rc = dllPointers.m_DisplayConfigGetDeviceInfo(&sourceName.header); if (rc != ERROR_SUCCESS) { break; } else if (wcscmp(deviceName, sourceName.viewGdiDeviceName) != 0) { continue; } ZeroMemory(&targetName, sizeof(targetName)); targetName.header.adapterId = paths[i].targetInfo.adapterId; targetName.header.id = paths[i].targetInfo.id; targetName.header.type = DISPLAYCONFIG_DEVICE_INFO_GET_TARGET_NAME; targetName.header.size = sizeof (targetName); rc = dllPointers.m_DisplayConfigGetDeviceInfo(&targetName.header); if (rc == ERROR_SUCCESS) { retval = _glfwCreateUTF8FromWideStringWin32(targetName.monitorFriendlyDeviceName); /* if we got an empty string, treat it as failure so we'll fallback to getting the generic name. */ if (retval && (*retval == '\0')) { free(retval); retval = NULL; } } break; } } FreeLibrary(dllPointers.m_dll); free(paths); free(modes); return retval; GET_ACCURATE_MONITOR_NAME_FAILURE: FreeLibrary(dllPointers.m_dll); free(retval); free(paths); free(modes); return NULL; } // 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; char *possiblyMoreAccurateMonitorName; possiblyMoreAccurateMonitorName = GetAccurateMonitorName(mi.szDevice); if(possiblyMoreAccurateMonitorName != NULL) { strncpy(monitor->name, possiblyMoreAccurateMonitorName, sizeof(monitor->name) - 1); free(possiblyMoreAccurateMonitorName); possiblyMoreAccurateMonitorName = NULL; } } } 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