src/video/windows/SDL_windowsmodes.c
author Sam Lantinga <slouken@libsdl.org>
Fri, 18 May 2018 13:09:30 -0700
changeset 11983 3a50eb90e4b2
parent 11811 5d94cb6b24d3
child 12503 806492103856
permissions -rw-r--r--
Merged latest changes from Steam Link app
     1 /*
     2   Simple DirectMedia Layer
     3   Copyright (C) 1997-2018 Sam Lantinga <slouken@libsdl.org>
     4 
     5   This software is provided 'as-is', without any express or implied
     6   warranty.  In no event will the authors be held liable for any damages
     7   arising from the use of this software.
     8 
     9   Permission is granted to anyone to use this software for any purpose,
    10   including commercial applications, and to alter it and redistribute it
    11   freely, subject to the following restrictions:
    12 
    13   1. The origin of this software must not be misrepresented; you must not
    14      claim that you wrote the original software. If you use this software
    15      in a product, an acknowledgment in the product documentation would be
    16      appreciated but is not required.
    17   2. Altered source versions must be plainly marked as such, and must not be
    18      misrepresented as being the original software.
    19   3. This notice may not be removed or altered from any source distribution.
    20 */
    21 #include "../../SDL_internal.h"
    22 
    23 #if SDL_VIDEO_DRIVER_WINDOWS
    24 
    25 #include "SDL_windowsvideo.h"
    26 #include "../../../include/SDL_assert.h"
    27 #include "../../../include/SDL_log.h"
    28 
    29 /* Windows CE compatibility */
    30 #ifndef CDS_FULLSCREEN
    31 #define CDS_FULLSCREEN 0
    32 #endif
    33 
    34 /* #define DEBUG_MODES */
    35 
    36 static void
    37 WIN_UpdateDisplayMode(_THIS, LPCTSTR deviceName, DWORD index, SDL_DisplayMode * mode)
    38 {
    39     SDL_DisplayModeData *data = (SDL_DisplayModeData *) mode->driverdata;
    40     HDC hdc;
    41 
    42     data->DeviceMode.dmFields =
    43         (DM_BITSPERPEL | DM_PELSWIDTH | DM_PELSHEIGHT | DM_DISPLAYFREQUENCY |
    44          DM_DISPLAYFLAGS);
    45 
    46     if (index == ENUM_CURRENT_SETTINGS
    47         && (hdc = CreateDC(deviceName, NULL, NULL, NULL)) != NULL) {
    48         char bmi_data[sizeof(BITMAPINFOHEADER) + 256 * sizeof(RGBQUAD)];
    49         LPBITMAPINFO bmi;
    50         HBITMAP hbm;
    51         int logical_width = GetDeviceCaps( hdc, HORZRES );
    52         int logical_height = GetDeviceCaps( hdc, VERTRES );
    53 
    54         mode->w = logical_width;
    55         mode->h = logical_height;
    56         
    57         SDL_zero(bmi_data);
    58         bmi = (LPBITMAPINFO) bmi_data;
    59         bmi->bmiHeader.biSize = sizeof(BITMAPINFOHEADER);
    60 
    61         hbm = CreateCompatibleBitmap(hdc, 1, 1);
    62         GetDIBits(hdc, hbm, 0, 1, NULL, bmi, DIB_RGB_COLORS);
    63         GetDIBits(hdc, hbm, 0, 1, NULL, bmi, DIB_RGB_COLORS);
    64         DeleteObject(hbm);
    65         DeleteDC(hdc);
    66         if (bmi->bmiHeader.biCompression == BI_BITFIELDS) {
    67             switch (*(Uint32 *) bmi->bmiColors) {
    68             case 0x00FF0000:
    69                 mode->format = SDL_PIXELFORMAT_RGB888;
    70                 break;
    71             case 0x000000FF:
    72                 mode->format = SDL_PIXELFORMAT_BGR888;
    73                 break;
    74             case 0xF800:
    75                 mode->format = SDL_PIXELFORMAT_RGB565;
    76                 break;
    77             case 0x7C00:
    78                 mode->format = SDL_PIXELFORMAT_RGB555;
    79                 break;
    80             }
    81         } else if (bmi->bmiHeader.biBitCount == 8) {
    82             mode->format = SDL_PIXELFORMAT_INDEX8;
    83         } else if (bmi->bmiHeader.biBitCount == 4) {
    84             mode->format = SDL_PIXELFORMAT_INDEX4LSB;
    85         }
    86     } else if (mode->format == SDL_PIXELFORMAT_UNKNOWN) {
    87         /* FIXME: Can we tell what this will be? */
    88         if ((data->DeviceMode.dmFields & DM_BITSPERPEL) == DM_BITSPERPEL) {
    89             switch (data->DeviceMode.dmBitsPerPel) {
    90             case 32:
    91                 mode->format = SDL_PIXELFORMAT_RGB888;
    92                 break;
    93             case 24:
    94                 mode->format = SDL_PIXELFORMAT_RGB24;
    95                 break;
    96             case 16:
    97                 mode->format = SDL_PIXELFORMAT_RGB565;
    98                 break;
    99             case 15:
   100                 mode->format = SDL_PIXELFORMAT_RGB555;
   101                 break;
   102             case 8:
   103                 mode->format = SDL_PIXELFORMAT_INDEX8;
   104                 break;
   105             case 4:
   106                 mode->format = SDL_PIXELFORMAT_INDEX4LSB;
   107                 break;
   108             }
   109         }
   110     }
   111 }
   112 
   113 static SDL_bool
   114 WIN_GetDisplayMode(_THIS, LPCTSTR deviceName, DWORD index, SDL_DisplayMode * mode)
   115 {
   116     SDL_DisplayModeData *data;
   117     DEVMODE devmode;
   118 
   119     devmode.dmSize = sizeof(devmode);
   120     devmode.dmDriverExtra = 0;
   121     if (!EnumDisplaySettings(deviceName, index, &devmode)) {
   122         return SDL_FALSE;
   123     }
   124 
   125     data = (SDL_DisplayModeData *) SDL_malloc(sizeof(*data));
   126     if (!data) {
   127         return SDL_FALSE;
   128     }
   129 
   130     mode->driverdata = data;
   131     data->DeviceMode = devmode;
   132 
   133     mode->format = SDL_PIXELFORMAT_UNKNOWN;
   134     mode->w = data->DeviceMode.dmPelsWidth;
   135     mode->h = data->DeviceMode.dmPelsHeight;
   136     mode->refresh_rate = data->DeviceMode.dmDisplayFrequency;
   137 
   138     /* Fill in the mode information */
   139     WIN_UpdateDisplayMode(_this, deviceName, index, mode);
   140     return SDL_TRUE;
   141 }
   142 
   143 static SDL_bool
   144 WIN_AddDisplay(_THIS, HMONITOR hMonitor, const MONITORINFOEX *info)
   145 {
   146     SDL_VideoDisplay display;
   147     SDL_DisplayData *displaydata;
   148     SDL_DisplayMode mode;
   149     DISPLAY_DEVICE device;
   150 
   151 #ifdef DEBUG_MODES
   152     SDL_Log("Display: %s\n", WIN_StringToUTF8(info->szDevice));
   153 #endif
   154 
   155     if (!WIN_GetDisplayMode(_this, info->szDevice, ENUM_CURRENT_SETTINGS, &mode)) {
   156         return SDL_FALSE;
   157     }
   158 
   159     displaydata = (SDL_DisplayData *) SDL_malloc(sizeof(*displaydata));
   160     if (!displaydata) {
   161         return SDL_FALSE;
   162     }
   163     SDL_memcpy(displaydata->DeviceName, info->szDevice,
   164                sizeof(displaydata->DeviceName));
   165     displaydata->MonitorHandle = hMonitor;
   166 
   167     SDL_zero(display);
   168     device.cb = sizeof(device);
   169     if (EnumDisplayDevices(info->szDevice, 0, &device, 0)) {
   170         display.name = WIN_StringToUTF8(device.DeviceString);
   171     }
   172     display.desktop_mode = mode;
   173     display.current_mode = mode;
   174     display.driverdata = displaydata;
   175     SDL_AddVideoDisplay(&display);
   176     SDL_free(display.name);
   177     return SDL_TRUE;
   178 }
   179 
   180 typedef struct _WIN_AddDisplaysData {
   181     SDL_VideoDevice *video_device;
   182     SDL_bool want_primary;
   183 } WIN_AddDisplaysData;
   184 
   185 static BOOL CALLBACK
   186 WIN_AddDisplaysCallback(HMONITOR hMonitor,
   187                         HDC      hdcMonitor,
   188                         LPRECT   lprcMonitor,
   189                         LPARAM   dwData)
   190 {
   191     WIN_AddDisplaysData *data = (WIN_AddDisplaysData*)dwData;
   192     MONITORINFOEX info;
   193 
   194     SDL_zero(info);
   195     info.cbSize = sizeof(info);
   196 
   197     if (GetMonitorInfo(hMonitor, (LPMONITORINFO)&info) != 0) {
   198         const SDL_bool is_primary = ((info.dwFlags & MONITORINFOF_PRIMARY) == MONITORINFOF_PRIMARY);
   199 
   200         if (is_primary == data->want_primary) {
   201             WIN_AddDisplay(data->video_device, hMonitor, &info);
   202         }
   203     }
   204 
   205     // continue enumeration
   206     return TRUE;
   207 }
   208 
   209 static void
   210 WIN_AddDisplays(_THIS)
   211 {
   212     WIN_AddDisplaysData callback_data;
   213     callback_data.video_device = _this;
   214 
   215     callback_data.want_primary = SDL_TRUE;
   216     EnumDisplayMonitors(NULL, NULL, WIN_AddDisplaysCallback, (LPARAM)&callback_data);
   217 
   218     callback_data.want_primary = SDL_FALSE;
   219     EnumDisplayMonitors(NULL, NULL, WIN_AddDisplaysCallback, (LPARAM)&callback_data);
   220 }
   221 
   222 int
   223 WIN_InitModes(_THIS)
   224 {
   225     WIN_AddDisplays(_this);
   226 
   227     if (_this->num_displays == 0) {
   228         return SDL_SetError("No displays available");
   229     }
   230     return 0;
   231 }
   232 
   233 int
   234 WIN_GetDisplayBounds(_THIS, SDL_VideoDisplay * display, SDL_Rect * rect)
   235 {
   236     const SDL_DisplayData *data = (const SDL_DisplayData *)display->driverdata;
   237     MONITORINFO minfo;
   238     BOOL rc;
   239 
   240     SDL_zero(minfo);
   241     minfo.cbSize = sizeof(MONITORINFO);
   242     rc = GetMonitorInfo(data->MonitorHandle, &minfo);
   243 
   244     if (!rc) {
   245         return SDL_SetError("Couldn't find monitor data");
   246     }
   247 
   248     rect->x = minfo.rcMonitor.left;
   249     rect->y = minfo.rcMonitor.top;
   250     rect->w = minfo.rcMonitor.right - minfo.rcMonitor.left;
   251     rect->h = minfo.rcMonitor.bottom - minfo.rcMonitor.top;
   252 
   253     return 0;
   254 }
   255 
   256 int
   257 WIN_GetDisplayDPI(_THIS, SDL_VideoDisplay * display, float * ddpi_out, float * hdpi_out, float * vdpi_out)
   258 {
   259     const SDL_DisplayData *displaydata = (SDL_DisplayData *)display->driverdata;
   260     const SDL_VideoData *videodata = (SDL_VideoData *)display->device->driverdata;
   261     float hdpi = 0, vdpi = 0, ddpi = 0;
   262     
   263     if (videodata->GetDpiForMonitor) {
   264         UINT hdpi_uint, vdpi_uint;
   265         // Windows 8.1+ codepath
   266         if (videodata->GetDpiForMonitor(displaydata->MonitorHandle, MDT_EFFECTIVE_DPI, &hdpi_uint, &vdpi_uint) == S_OK) {
   267             // GetDpiForMonitor docs promise to return the same hdpi/vdpi
   268             hdpi = (float)hdpi_uint;
   269             vdpi = (float)hdpi_uint;
   270             ddpi = (float)hdpi_uint;
   271         } else {
   272             return SDL_SetError("GetDpiForMonitor failed");
   273         }
   274     } else {
   275         // Window 8.0 and below: same DPI for all monitors.
   276         HDC hdc;
   277         int hdpi_int, vdpi_int, hpoints, vpoints, hpix, vpix;
   278         float hinches, vinches;
   279 
   280         hdc = GetDC(NULL);
   281         if (hdc == NULL) {
   282             return SDL_SetError("GetDC failed");
   283         }
   284         hdpi_int = GetDeviceCaps(hdc, LOGPIXELSX);
   285         vdpi_int = GetDeviceCaps(hdc, LOGPIXELSY);
   286         ReleaseDC(NULL, hdc);
   287 
   288         hpoints = GetSystemMetrics(SM_CXVIRTUALSCREEN);
   289         vpoints = GetSystemMetrics(SM_CYVIRTUALSCREEN);
   290 
   291         hpix = MulDiv(hpoints, hdpi_int, 96);
   292         vpix = MulDiv(vpoints, vdpi_int, 96);
   293 
   294         hinches = (float)hpoints / 96.0f;
   295         vinches = (float)vpoints / 96.0f;
   296 
   297         hdpi = (float)hdpi_int;
   298         vdpi = (float)vdpi_int;
   299         ddpi = SDL_ComputeDiagonalDPI(hpix, vpix, hinches, vinches);
   300     }
   301 
   302     if (ddpi_out) {
   303         *ddpi_out = ddpi;
   304     }
   305     if (hdpi_out) {
   306         *hdpi_out = hdpi;
   307     }
   308     if (vdpi_out) {
   309         *vdpi_out = vdpi;
   310     }
   311 
   312     return ddpi != 0.0f ? 0 : SDL_SetError("Couldn't get DPI");
   313 }
   314 
   315 int
   316 WIN_GetDisplayUsableBounds(_THIS, SDL_VideoDisplay * display, SDL_Rect * rect)
   317 {
   318     const SDL_DisplayData *data = (const SDL_DisplayData *)display->driverdata;
   319     MONITORINFO minfo;
   320     BOOL rc;
   321 
   322     SDL_zero(minfo);
   323     minfo.cbSize = sizeof(MONITORINFO);
   324     rc = GetMonitorInfo(data->MonitorHandle, &minfo);
   325 
   326     if (!rc) {
   327         return SDL_SetError("Couldn't find monitor data");
   328     }
   329 
   330     rect->x = minfo.rcWork.left;
   331     rect->y = minfo.rcWork.top;
   332     rect->w = minfo.rcWork.right - minfo.rcWork.left;
   333     rect->h = minfo.rcWork.bottom - minfo.rcWork.top;
   334 
   335     return 0;
   336 }
   337 
   338 void
   339 WIN_GetDisplayModes(_THIS, SDL_VideoDisplay * display)
   340 {
   341     SDL_DisplayData *data = (SDL_DisplayData *) display->driverdata;
   342     DWORD i;
   343     SDL_DisplayMode mode;
   344 
   345     for (i = 0;; ++i) {
   346         if (!WIN_GetDisplayMode(_this, data->DeviceName, i, &mode)) {
   347             break;
   348         }
   349         if (SDL_ISPIXELFORMAT_INDEXED(mode.format)) {
   350             /* We don't support palettized modes now */
   351             SDL_free(mode.driverdata);
   352             continue;
   353         }
   354         if (mode.format != SDL_PIXELFORMAT_UNKNOWN) {
   355             if (!SDL_AddDisplayMode(display, &mode)) {
   356                 SDL_free(mode.driverdata);
   357             }
   358         } else {
   359             SDL_free(mode.driverdata);
   360         }
   361     }
   362 }
   363 
   364 int
   365 WIN_SetDisplayMode(_THIS, SDL_VideoDisplay * display, SDL_DisplayMode * mode)
   366 {
   367     SDL_DisplayData *displaydata = (SDL_DisplayData *) display->driverdata;
   368     SDL_DisplayModeData *data = (SDL_DisplayModeData *) mode->driverdata;
   369     LONG status;
   370 
   371     if (mode->driverdata == display->desktop_mode.driverdata) {
   372         status = ChangeDisplaySettingsEx(displaydata->DeviceName, NULL, NULL, CDS_FULLSCREEN, NULL);
   373     } else {
   374         status = ChangeDisplaySettingsEx(displaydata->DeviceName, &data->DeviceMode, NULL, CDS_FULLSCREEN, NULL);
   375     }
   376     if (status != DISP_CHANGE_SUCCESSFUL) {
   377         const char *reason = "Unknown reason";
   378         switch (status) {
   379         case DISP_CHANGE_BADFLAGS:
   380             reason = "DISP_CHANGE_BADFLAGS";
   381             break;
   382         case DISP_CHANGE_BADMODE:
   383             reason = "DISP_CHANGE_BADMODE";
   384             break;
   385         case DISP_CHANGE_BADPARAM:
   386             reason = "DISP_CHANGE_BADPARAM";
   387             break;
   388         case DISP_CHANGE_FAILED:
   389             reason = "DISP_CHANGE_FAILED";
   390             break;
   391         }
   392         return SDL_SetError("ChangeDisplaySettingsEx() failed: %s", reason);
   393     }
   394     EnumDisplaySettings(displaydata->DeviceName, ENUM_CURRENT_SETTINGS, &data->DeviceMode);
   395     WIN_UpdateDisplayMode(_this, displaydata->DeviceName, ENUM_CURRENT_SETTINGS, mode);
   396     return 0;
   397 }
   398 
   399 void
   400 WIN_QuitModes(_THIS)
   401 {
   402     /* All fullscreen windows should have restored modes by now */
   403 }
   404 
   405 #endif /* SDL_VIDEO_DRIVER_WINDOWS */
   406 
   407 /* vi: set ts=4 sw=4 expandtab: */