zorldo

Goofing around with Ebiten
git clone git://bsandro.tech/zorldo
Log | Files | Refs | README

win32_monitor.c (16357B)


      1 //========================================================================
      2 // GLFW 3.3 Win32 - www.glfw.org
      3 //------------------------------------------------------------------------
      4 // Copyright (c) 2002-2006 Marcus Geelnard
      5 // Copyright (c) 2006-2019 Camilla Löwy <elmindreda@glfw.org>
      6 //
      7 // This software is provided 'as-is', without any express or implied
      8 // warranty. In no event will the authors be held liable for any damages
      9 // arising from the use of this software.
     10 //
     11 // Permission is granted to anyone to use this software for any purpose,
     12 // including commercial applications, and to alter it and redistribute it
     13 // freely, subject to the following restrictions:
     14 //
     15 // 1. The origin of this software must not be misrepresented; you must not
     16 //    claim that you wrote the original software. If you use this software
     17 //    in a product, an acknowledgment in the product documentation would
     18 //    be appreciated but is not required.
     19 //
     20 // 2. Altered source versions must be plainly marked as such, and must not
     21 //    be misrepresented as being the original software.
     22 //
     23 // 3. This notice may not be removed or altered from any source
     24 //    distribution.
     25 //
     26 //========================================================================
     27 // Please use C89 style variable declarations in this file because VS 2010
     28 //========================================================================
     29 
     30 #include "internal.h"
     31 
     32 #include <stdlib.h>
     33 #include <string.h>
     34 #include <limits.h>
     35 #include <malloc.h>
     36 #include <wchar.h>
     37 
     38 
     39 // Callback for EnumDisplayMonitors in createMonitor
     40 //
     41 static BOOL CALLBACK monitorCallback(HMONITOR handle,
     42                                      HDC dc,
     43                                      RECT* rect,
     44                                      LPARAM data)
     45 {
     46     MONITORINFOEXW mi;
     47     ZeroMemory(&mi, sizeof(mi));
     48     mi.cbSize = sizeof(mi);
     49 
     50     if (GetMonitorInfoW(handle, (MONITORINFO*) &mi))
     51     {
     52         _GLFWmonitor* monitor = (_GLFWmonitor*) data;
     53         if (wcscmp(mi.szDevice, monitor->win32.adapterName) == 0)
     54             monitor->win32.handle = handle;
     55     }
     56 
     57     return TRUE;
     58 }
     59 
     60 // Create monitor from an adapter and (optionally) a display
     61 //
     62 static _GLFWmonitor* createMonitor(DISPLAY_DEVICEW* adapter,
     63                                    DISPLAY_DEVICEW* display)
     64 {
     65     _GLFWmonitor* monitor;
     66     int widthMM, heightMM;
     67     char* name;
     68     HDC dc;
     69     DEVMODEW dm;
     70     RECT rect;
     71 
     72     if (display)
     73         name = _glfwCreateUTF8FromWideStringWin32(display->DeviceString);
     74     else
     75         name = _glfwCreateUTF8FromWideStringWin32(adapter->DeviceString);
     76     if (!name)
     77         return NULL;
     78 
     79     ZeroMemory(&dm, sizeof(dm));
     80     dm.dmSize = sizeof(dm);
     81     EnumDisplaySettingsW(adapter->DeviceName, ENUM_CURRENT_SETTINGS, &dm);
     82 
     83     dc = CreateDCW(L"DISPLAY", adapter->DeviceName, NULL, NULL);
     84 
     85     if (IsWindows8Point1OrGreater())
     86     {
     87         widthMM  = GetDeviceCaps(dc, HORZSIZE);
     88         heightMM = GetDeviceCaps(dc, VERTSIZE);
     89     }
     90     else
     91     {
     92         widthMM  = (int) (dm.dmPelsWidth * 25.4f / GetDeviceCaps(dc, LOGPIXELSX));
     93         heightMM = (int) (dm.dmPelsHeight * 25.4f / GetDeviceCaps(dc, LOGPIXELSY));
     94     }
     95 
     96     DeleteDC(dc);
     97 
     98     monitor = _glfwAllocMonitor(name, widthMM, heightMM);
     99     free(name);
    100 
    101     if (adapter->StateFlags & DISPLAY_DEVICE_MODESPRUNED)
    102         monitor->win32.modesPruned = GLFW_TRUE;
    103 
    104     wcscpy(monitor->win32.adapterName, adapter->DeviceName);
    105     WideCharToMultiByte(CP_UTF8, 0,
    106                         adapter->DeviceName, -1,
    107                         monitor->win32.publicAdapterName,
    108                         sizeof(monitor->win32.publicAdapterName),
    109                         NULL, NULL);
    110 
    111     if (display)
    112     {
    113         wcscpy(monitor->win32.displayName, display->DeviceName);
    114         WideCharToMultiByte(CP_UTF8, 0,
    115                             display->DeviceName, -1,
    116                             monitor->win32.publicDisplayName,
    117                             sizeof(monitor->win32.publicDisplayName),
    118                             NULL, NULL);
    119     }
    120 
    121     rect.left   = dm.dmPosition.x;
    122     rect.top    = dm.dmPosition.y;
    123     rect.right  = dm.dmPosition.x + dm.dmPelsWidth;
    124     rect.bottom = dm.dmPosition.y + dm.dmPelsHeight;
    125 
    126     EnumDisplayMonitors(NULL, &rect, monitorCallback, (LPARAM) monitor);
    127     return monitor;
    128 }
    129 
    130 
    131 //////////////////////////////////////////////////////////////////////////
    132 //////                       GLFW internal API                      //////
    133 //////////////////////////////////////////////////////////////////////////
    134 
    135 // Poll for changes in the set of connected monitors
    136 //
    137 void _glfwPollMonitorsWin32(void)
    138 {
    139     int i, disconnectedCount;
    140     _GLFWmonitor** disconnected = NULL;
    141     DWORD adapterIndex, displayIndex;
    142     DISPLAY_DEVICEW adapter, display;
    143     _GLFWmonitor* monitor;
    144 
    145     disconnectedCount = _glfw.monitorCount;
    146     if (disconnectedCount)
    147     {
    148         disconnected = calloc(_glfw.monitorCount, sizeof(_GLFWmonitor*));
    149         memcpy(disconnected,
    150                _glfw.monitors,
    151                _glfw.monitorCount * sizeof(_GLFWmonitor*));
    152     }
    153 
    154     for (adapterIndex = 0;  ;  adapterIndex++)
    155     {
    156         int type = _GLFW_INSERT_LAST;
    157 
    158         ZeroMemory(&adapter, sizeof(adapter));
    159         adapter.cb = sizeof(adapter);
    160 
    161         if (!EnumDisplayDevicesW(NULL, adapterIndex, &adapter, 0))
    162             break;
    163 
    164         if (!(adapter.StateFlags & DISPLAY_DEVICE_ACTIVE))
    165             continue;
    166 
    167         if (adapter.StateFlags & DISPLAY_DEVICE_PRIMARY_DEVICE)
    168             type = _GLFW_INSERT_FIRST;
    169 
    170         for (displayIndex = 0;  ;  displayIndex++)
    171         {
    172             ZeroMemory(&display, sizeof(display));
    173             display.cb = sizeof(display);
    174 
    175             if (!EnumDisplayDevicesW(adapter.DeviceName, displayIndex, &display, 0))
    176                 break;
    177 
    178             if (!(display.StateFlags & DISPLAY_DEVICE_ACTIVE))
    179                 continue;
    180 
    181             for (i = 0;  i < disconnectedCount;  i++)
    182             {
    183                 if (disconnected[i] &&
    184                     wcscmp(disconnected[i]->win32.displayName,
    185                            display.DeviceName) == 0)
    186                 {
    187                     disconnected[i] = NULL;
    188                     // handle may have changed, update
    189                     EnumDisplayMonitors(NULL, NULL, monitorCallback, (LPARAM) _glfw.monitors[i]);
    190                     break;
    191                 }
    192             }
    193 
    194             if (i < disconnectedCount)
    195                 continue;
    196 
    197             monitor = createMonitor(&adapter, &display);
    198             if (!monitor)
    199             {
    200                 free(disconnected);
    201                 return;
    202             }
    203 
    204             _glfwInputMonitor(monitor, GLFW_CONNECTED, type);
    205 
    206             type = _GLFW_INSERT_LAST;
    207         }
    208 
    209         // HACK: If an active adapter does not have any display devices
    210         //       (as sometimes happens), add it directly as a monitor
    211         if (displayIndex == 0)
    212         {
    213             for (i = 0;  i < disconnectedCount;  i++)
    214             {
    215                 if (disconnected[i] &&
    216                     wcscmp(disconnected[i]->win32.adapterName,
    217                            adapter.DeviceName) == 0)
    218                 {
    219                     disconnected[i] = NULL;
    220                     break;
    221                 }
    222             }
    223 
    224             if (i < disconnectedCount)
    225                 continue;
    226 
    227             monitor = createMonitor(&adapter, NULL);
    228             if (!monitor)
    229             {
    230                 free(disconnected);
    231                 return;
    232             }
    233 
    234             _glfwInputMonitor(monitor, GLFW_CONNECTED, type);
    235         }
    236     }
    237 
    238     for (i = 0;  i < disconnectedCount;  i++)
    239     {
    240         if (disconnected[i])
    241             _glfwInputMonitor(disconnected[i], GLFW_DISCONNECTED, 0);
    242     }
    243 
    244     free(disconnected);
    245 }
    246 
    247 // Change the current video mode
    248 //
    249 void _glfwSetVideoModeWin32(_GLFWmonitor* monitor, const GLFWvidmode* desired)
    250 {
    251     GLFWvidmode current;
    252     const GLFWvidmode* best;
    253     DEVMODEW dm;
    254     LONG result;
    255 
    256     best = _glfwChooseVideoMode(monitor, desired);
    257     _glfwPlatformGetVideoMode(monitor, &current);
    258     if (_glfwCompareVideoModes(&current, best) == 0)
    259         return;
    260 
    261     ZeroMemory(&dm, sizeof(dm));
    262     dm.dmSize = sizeof(dm);
    263     dm.dmFields           = DM_PELSWIDTH | DM_PELSHEIGHT | DM_BITSPERPEL |
    264                             DM_DISPLAYFREQUENCY;
    265     dm.dmPelsWidth        = best->width;
    266     dm.dmPelsHeight       = best->height;
    267     dm.dmBitsPerPel       = best->redBits + best->greenBits + best->blueBits;
    268     dm.dmDisplayFrequency = best->refreshRate;
    269 
    270     if (dm.dmBitsPerPel < 15 || dm.dmBitsPerPel >= 24)
    271         dm.dmBitsPerPel = 32;
    272 
    273     result = ChangeDisplaySettingsExW(monitor->win32.adapterName,
    274                                       &dm,
    275                                       NULL,
    276                                       CDS_FULLSCREEN,
    277                                       NULL);
    278     if (result == DISP_CHANGE_SUCCESSFUL)
    279         monitor->win32.modeChanged = GLFW_TRUE;
    280     else
    281     {
    282         const char* description = "Unknown error";
    283 
    284         if (result == DISP_CHANGE_BADDUALVIEW)
    285             description = "The system uses DualView";
    286         else if (result == DISP_CHANGE_BADFLAGS)
    287             description = "Invalid flags";
    288         else if (result == DISP_CHANGE_BADMODE)
    289             description = "Graphics mode not supported";
    290         else if (result == DISP_CHANGE_BADPARAM)
    291             description = "Invalid parameter";
    292         else if (result == DISP_CHANGE_FAILED)
    293             description = "Graphics mode failed";
    294         else if (result == DISP_CHANGE_NOTUPDATED)
    295             description = "Failed to write to registry";
    296         else if (result == DISP_CHANGE_RESTART)
    297             description = "Computer restart required";
    298 
    299         _glfwInputError(GLFW_PLATFORM_ERROR,
    300                         "Win32: Failed to set video mode: %s",
    301                         description);
    302     }
    303 }
    304 
    305 // Restore the previously saved (original) video mode
    306 //
    307 void _glfwRestoreVideoModeWin32(_GLFWmonitor* monitor)
    308 {
    309     if (monitor->win32.modeChanged)
    310     {
    311         ChangeDisplaySettingsExW(monitor->win32.adapterName,
    312                                  NULL, NULL, CDS_FULLSCREEN, NULL);
    313         monitor->win32.modeChanged = GLFW_FALSE;
    314     }
    315 }
    316 
    317 void _glfwGetMonitorContentScaleWin32(HMONITOR handle, float* xscale, float* yscale)
    318 {
    319     UINT xdpi, ydpi;
    320 
    321     if (IsWindows8Point1OrGreater())
    322         GetDpiForMonitor(handle, MDT_EFFECTIVE_DPI, &xdpi, &ydpi);
    323     else
    324     {
    325         const HDC dc = GetDC(NULL);
    326         xdpi = GetDeviceCaps(dc, LOGPIXELSX);
    327         ydpi = GetDeviceCaps(dc, LOGPIXELSY);
    328         ReleaseDC(NULL, dc);
    329     }
    330 
    331     if (xscale)
    332         *xscale = xdpi / (float) USER_DEFAULT_SCREEN_DPI;
    333     if (yscale)
    334         *yscale = ydpi / (float) USER_DEFAULT_SCREEN_DPI;
    335 }
    336 
    337 
    338 //////////////////////////////////////////////////////////////////////////
    339 //////                       GLFW platform API                      //////
    340 //////////////////////////////////////////////////////////////////////////
    341 
    342 void _glfwPlatformFreeMonitor(_GLFWmonitor* monitor)
    343 {
    344 }
    345 
    346 void _glfwPlatformGetMonitorPos(_GLFWmonitor* monitor, int* xpos, int* ypos)
    347 {
    348     DEVMODEW dm;
    349     ZeroMemory(&dm, sizeof(dm));
    350     dm.dmSize = sizeof(dm);
    351 
    352     EnumDisplaySettingsExW(monitor->win32.adapterName,
    353                            ENUM_CURRENT_SETTINGS,
    354                            &dm,
    355                            EDS_ROTATEDMODE);
    356 
    357     if (xpos)
    358         *xpos = dm.dmPosition.x;
    359     if (ypos)
    360         *ypos = dm.dmPosition.y;
    361 }
    362 
    363 void _glfwPlatformGetMonitorContentScale(_GLFWmonitor* monitor,
    364                                          float* xscale, float* yscale)
    365 {
    366     _glfwGetMonitorContentScaleWin32(monitor->win32.handle, xscale, yscale);
    367 }
    368 
    369 void _glfwPlatformGetMonitorWorkarea(_GLFWmonitor* monitor,
    370                                      int* xpos, int* ypos,
    371                                      int* width, int* height)
    372 {
    373     MONITORINFO mi = { sizeof(mi) };
    374     GetMonitorInfo(monitor->win32.handle, &mi);
    375 
    376     if (xpos)
    377         *xpos = mi.rcWork.left;
    378     if (ypos)
    379         *ypos = mi.rcWork.top;
    380     if (width)
    381         *width = mi.rcWork.right - mi.rcWork.left;
    382     if (height)
    383         *height = mi.rcWork.bottom - mi.rcWork.top;
    384 }
    385 
    386 GLFWvidmode* _glfwPlatformGetVideoModes(_GLFWmonitor* monitor, int* count)
    387 {
    388     int modeIndex = 0, size = 0;
    389     GLFWvidmode* result = NULL;
    390 
    391     *count = 0;
    392 
    393     for (;;)
    394     {
    395         int i;
    396         GLFWvidmode mode;
    397         DEVMODEW dm;
    398 
    399         ZeroMemory(&dm, sizeof(dm));
    400         dm.dmSize = sizeof(dm);
    401 
    402         if (!EnumDisplaySettingsW(monitor->win32.adapterName, modeIndex, &dm))
    403             break;
    404 
    405         modeIndex++;
    406 
    407         // Skip modes with less than 15 BPP
    408         if (dm.dmBitsPerPel < 15)
    409             continue;
    410 
    411         mode.width  = dm.dmPelsWidth;
    412         mode.height = dm.dmPelsHeight;
    413         mode.refreshRate = dm.dmDisplayFrequency;
    414         _glfwSplitBPP(dm.dmBitsPerPel,
    415                       &mode.redBits,
    416                       &mode.greenBits,
    417                       &mode.blueBits);
    418 
    419         for (i = 0;  i < *count;  i++)
    420         {
    421             if (_glfwCompareVideoModes(result + i, &mode) == 0)
    422                 break;
    423         }
    424 
    425         // Skip duplicate modes
    426         if (i < *count)
    427             continue;
    428 
    429         if (monitor->win32.modesPruned)
    430         {
    431             // Skip modes not supported by the connected displays
    432             if (ChangeDisplaySettingsExW(monitor->win32.adapterName,
    433                                          &dm,
    434                                          NULL,
    435                                          CDS_TEST,
    436                                          NULL) != DISP_CHANGE_SUCCESSFUL)
    437             {
    438                 continue;
    439             }
    440         }
    441 
    442         if (*count == size)
    443         {
    444             size += 128;
    445             result = (GLFWvidmode*) realloc(result, size * sizeof(GLFWvidmode));
    446         }
    447 
    448         (*count)++;
    449         result[*count - 1] = mode;
    450     }
    451 
    452     if (!*count)
    453     {
    454         // HACK: Report the current mode if no valid modes were found
    455         result = calloc(1, sizeof(GLFWvidmode));
    456         _glfwPlatformGetVideoMode(monitor, result);
    457         *count = 1;
    458     }
    459 
    460     return result;
    461 }
    462 
    463 void _glfwPlatformGetVideoMode(_GLFWmonitor* monitor, GLFWvidmode* mode)
    464 {
    465     DEVMODEW dm;
    466     ZeroMemory(&dm, sizeof(dm));
    467     dm.dmSize = sizeof(dm);
    468 
    469     EnumDisplaySettingsW(monitor->win32.adapterName, ENUM_CURRENT_SETTINGS, &dm);
    470 
    471     mode->width  = dm.dmPelsWidth;
    472     mode->height = dm.dmPelsHeight;
    473     mode->refreshRate = dm.dmDisplayFrequency;
    474     _glfwSplitBPP(dm.dmBitsPerPel,
    475                   &mode->redBits,
    476                   &mode->greenBits,
    477                   &mode->blueBits);
    478 }
    479 
    480 GLFWbool _glfwPlatformGetGammaRamp(_GLFWmonitor* monitor, GLFWgammaramp* ramp)
    481 {
    482     HDC dc;
    483     WORD values[3][256];
    484 
    485     dc = CreateDCW(L"DISPLAY", monitor->win32.adapterName, NULL, NULL);
    486     GetDeviceGammaRamp(dc, values);
    487     DeleteDC(dc);
    488 
    489     _glfwAllocGammaArrays(ramp, 256);
    490 
    491     memcpy(ramp->red,   values[0], sizeof(values[0]));
    492     memcpy(ramp->green, values[1], sizeof(values[1]));
    493     memcpy(ramp->blue,  values[2], sizeof(values[2]));
    494 
    495     return GLFW_TRUE;
    496 }
    497 
    498 void _glfwPlatformSetGammaRamp(_GLFWmonitor* monitor, const GLFWgammaramp* ramp)
    499 {
    500     HDC dc;
    501     WORD values[3][256];
    502 
    503     if (ramp->size != 256)
    504     {
    505         _glfwInputError(GLFW_PLATFORM_ERROR,
    506                         "Win32: Gamma ramp size must be 256");
    507         return;
    508     }
    509 
    510     memcpy(values[0], ramp->red,   sizeof(values[0]));
    511     memcpy(values[1], ramp->green, sizeof(values[1]));
    512     memcpy(values[2], ramp->blue,  sizeof(values[2]));
    513 
    514     dc = CreateDCW(L"DISPLAY", monitor->win32.adapterName, NULL, NULL);
    515     SetDeviceGammaRamp(dc, values);
    516     DeleteDC(dc);
    517 }
    518 
    519 
    520 //////////////////////////////////////////////////////////////////////////
    521 //////                        GLFW native API                       //////
    522 //////////////////////////////////////////////////////////////////////////
    523 
    524 GLFWAPI const char* glfwGetWin32Adapter(GLFWmonitor* handle)
    525 {
    526     _GLFWmonitor* monitor = (_GLFWmonitor*) handle;
    527     _GLFW_REQUIRE_INIT_OR_RETURN(NULL);
    528     return monitor->win32.publicAdapterName;
    529 }
    530 
    531 GLFWAPI const char* glfwGetWin32Monitor(GLFWmonitor* handle)
    532 {
    533     _GLFWmonitor* monitor = (_GLFWmonitor*) handle;
    534     _GLFW_REQUIRE_INIT_OR_RETURN(NULL);
    535     return monitor->win32.publicDisplayName;
    536 }
    537