src/video/windows/SDL_windowsmodes.c
author Sam Lantinga <slouken@libsdl.org>
Sat, 02 Jan 2016 10:10:34 -0800
changeset 9998 f67cf37e9cd4
parent 9889 7cb2c3ec8205
child 10019 36f7e8084508
permissions -rw-r--r--
Updated copyright to 2016
     1 /*
     2   Simple DirectMedia Layer
     3   Copyright (C) 1997-2016 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 
    27 /* Windows CE compatibility */
    28 #ifndef CDS_FULLSCREEN
    29 #define CDS_FULLSCREEN 0
    30 #endif
    31 
    32 typedef struct _WIN_GetMonitorDPIData {
    33     SDL_VideoData *vid_data;
    34     SDL_DisplayMode *mode;
    35     SDL_DisplayModeData *mode_data;
    36 } WIN_GetMonitorDPIData;
    37 
    38 static BOOL CALLBACK
    39 WIN_GetMonitorDPI(HMONITOR hMonitor,
    40                   HDC      hdcMonitor,
    41                   LPRECT   lprcMonitor,
    42                   LPARAM   dwData)
    43 {
    44     WIN_GetMonitorDPIData *data = (WIN_GetMonitorDPIData*) dwData;
    45     UINT hdpi, vdpi;
    46 
    47     if (data->vid_data->GetDpiForMonitor(hMonitor, MDT_EFFECTIVE_DPI, &hdpi, &vdpi) == S_OK &&
    48         hdpi > 0 &&
    49         vdpi > 0) {
    50         float hsize, vsize;
    51         
    52         data->mode_data->HorzDPI = (float)hdpi;
    53         data->mode_data->VertDPI = (float)vdpi;
    54 
    55         // Figure out the monitor size and compute the diagonal DPI.
    56         hsize = data->mode->w / data->mode_data->HorzDPI;
    57         vsize = data->mode->h / data->mode_data->VertDPI;
    58         
    59         data->mode_data->DiagDPI = SDL_ComputeDiagonalDPI( data->mode->w,
    60                                                            data->mode->h,
    61                                                            hsize,
    62                                                            vsize );
    63 
    64         // We can only handle one DPI per display mode so end the enumeration.
    65         return FALSE;
    66     }
    67 
    68     // We didn't get DPI information so keep going.
    69     return TRUE;
    70 }
    71 
    72 static SDL_bool
    73 WIN_GetDisplayMode(_THIS, LPCTSTR deviceName, DWORD index, SDL_DisplayMode * mode)
    74 {
    75     SDL_VideoData *vid_data = (SDL_VideoData *) _this->driverdata;
    76     SDL_DisplayModeData *data;
    77     DEVMODE devmode;
    78     HDC hdc;
    79 
    80     devmode.dmSize = sizeof(devmode);
    81     devmode.dmDriverExtra = 0;
    82     if (!EnumDisplaySettings(deviceName, index, &devmode)) {
    83         return SDL_FALSE;
    84     }
    85 
    86     data = (SDL_DisplayModeData *) SDL_malloc(sizeof(*data));
    87     if (!data) {
    88         return SDL_FALSE;
    89     }
    90     data->DeviceMode = devmode;
    91     data->DeviceMode.dmFields =
    92         (DM_BITSPERPEL | DM_PELSWIDTH | DM_PELSHEIGHT | DM_DISPLAYFREQUENCY |
    93          DM_DISPLAYFLAGS);
    94     data->ScaleX = 1.0f;
    95     data->ScaleY = 1.0f;
    96     data->DiagDPI = 0.0f;
    97     data->HorzDPI = 0.0f;
    98     data->VertDPI = 0.0f;
    99 
   100     /* Fill in the mode information */
   101     mode->format = SDL_PIXELFORMAT_UNKNOWN;
   102     mode->w = devmode.dmPelsWidth;
   103     mode->h = devmode.dmPelsHeight;
   104     mode->refresh_rate = devmode.dmDisplayFrequency;
   105     mode->driverdata = data;
   106 
   107     if (index == ENUM_CURRENT_SETTINGS
   108         && (hdc = CreateDC(deviceName, NULL, NULL, NULL)) != NULL) {
   109         char bmi_data[sizeof(BITMAPINFOHEADER) + 256 * sizeof(RGBQUAD)];
   110         LPBITMAPINFO bmi;
   111         HBITMAP hbm;
   112         int logical_width = GetDeviceCaps( hdc, HORZRES );
   113         int logical_height = GetDeviceCaps( hdc, VERTRES );
   114 
   115         data->ScaleX = (float)logical_width / devmode.dmPelsWidth;
   116         data->ScaleY = (float)logical_height / devmode.dmPelsHeight;
   117         mode->w = logical_width;
   118         mode->h = logical_height;
   119 
   120         // WIN_GetMonitorDPI needs mode->w and mode->h
   121         // so only call after those are set.
   122         if (vid_data->GetDpiForMonitor) {
   123             WIN_GetMonitorDPIData dpi_data;
   124             RECT monitor_rect;
   125 
   126             dpi_data.vid_data = vid_data;
   127             dpi_data.mode = mode;
   128             dpi_data.mode_data = data;
   129             monitor_rect.left = devmode.dmPosition.x;
   130             monitor_rect.top = devmode.dmPosition.y;
   131             monitor_rect.right = monitor_rect.left + 1;
   132             monitor_rect.bottom = monitor_rect.top + 1;
   133             EnumDisplayMonitors(NULL, &monitor_rect, WIN_GetMonitorDPI, (LPARAM)&dpi_data);
   134         } else {
   135             // We don't have the Windows 8.1 routine so just
   136             // get system DPI.
   137             data->HorzDPI = (float)GetDeviceCaps( hdc, LOGPIXELSX );
   138             data->VertDPI = (float)GetDeviceCaps( hdc, LOGPIXELSY );
   139             if (data->HorzDPI == data->VertDPI) {
   140                 data->DiagDPI = data->HorzDPI;
   141             } else {
   142                 data->DiagDPI = SDL_ComputeDiagonalDPI( mode->w,
   143                                                         mode->h,
   144                                                         (float)GetDeviceCaps( hdc, HORZSIZE ) / 25.4f,
   145                                                         (float)GetDeviceCaps( hdc, VERTSIZE ) / 25.4f );
   146             }
   147         }
   148         
   149         SDL_zero(bmi_data);
   150         bmi = (LPBITMAPINFO) bmi_data;
   151         bmi->bmiHeader.biSize = sizeof(BITMAPINFOHEADER);
   152 
   153         hbm = CreateCompatibleBitmap(hdc, 1, 1);
   154         GetDIBits(hdc, hbm, 0, 1, NULL, bmi, DIB_RGB_COLORS);
   155         GetDIBits(hdc, hbm, 0, 1, NULL, bmi, DIB_RGB_COLORS);
   156         DeleteObject(hbm);
   157         DeleteDC(hdc);
   158         if (bmi->bmiHeader.biCompression == BI_BITFIELDS) {
   159             switch (*(Uint32 *) bmi->bmiColors) {
   160             case 0x00FF0000:
   161                 mode->format = SDL_PIXELFORMAT_RGB888;
   162                 break;
   163             case 0x000000FF:
   164                 mode->format = SDL_PIXELFORMAT_BGR888;
   165                 break;
   166             case 0xF800:
   167                 mode->format = SDL_PIXELFORMAT_RGB565;
   168                 break;
   169             case 0x7C00:
   170                 mode->format = SDL_PIXELFORMAT_RGB555;
   171                 break;
   172             }
   173         } else if (bmi->bmiHeader.biBitCount == 8) {
   174             mode->format = SDL_PIXELFORMAT_INDEX8;
   175         } else if (bmi->bmiHeader.biBitCount == 4) {
   176             mode->format = SDL_PIXELFORMAT_INDEX4LSB;
   177         }
   178     } else {
   179         /* FIXME: Can we tell what this will be? */
   180         if ((devmode.dmFields & DM_BITSPERPEL) == DM_BITSPERPEL) {
   181             switch (devmode.dmBitsPerPel) {
   182             case 32:
   183                 mode->format = SDL_PIXELFORMAT_RGB888;
   184                 break;
   185             case 24:
   186                 mode->format = SDL_PIXELFORMAT_RGB24;
   187                 break;
   188             case 16:
   189                 mode->format = SDL_PIXELFORMAT_RGB565;
   190                 break;
   191             case 15:
   192                 mode->format = SDL_PIXELFORMAT_RGB555;
   193                 break;
   194             case 8:
   195                 mode->format = SDL_PIXELFORMAT_INDEX8;
   196                 break;
   197             case 4:
   198                 mode->format = SDL_PIXELFORMAT_INDEX4LSB;
   199                 break;
   200             }
   201         }
   202     }
   203     return SDL_TRUE;
   204 }
   205 
   206 static SDL_bool
   207 WIN_AddDisplay(_THIS, LPTSTR DeviceName)
   208 {
   209     SDL_VideoDisplay display;
   210     SDL_DisplayData *displaydata;
   211     SDL_DisplayMode mode;
   212     DISPLAY_DEVICE device;
   213 
   214 #ifdef DEBUG_MODES
   215     printf("Display: %s\n", WIN_StringToUTF8(DeviceName));
   216 #endif
   217     if (!WIN_GetDisplayMode(_this, DeviceName, ENUM_CURRENT_SETTINGS, &mode)) {
   218         return SDL_FALSE;
   219     }
   220 
   221     displaydata = (SDL_DisplayData *) SDL_malloc(sizeof(*displaydata));
   222     if (!displaydata) {
   223         return SDL_FALSE;
   224     }
   225     SDL_memcpy(displaydata->DeviceName, DeviceName,
   226                sizeof(displaydata->DeviceName));
   227 
   228     SDL_zero(display);
   229     device.cb = sizeof(device);
   230     if (EnumDisplayDevices(DeviceName, 0, &device, 0)) {
   231         display.name = WIN_StringToUTF8(device.DeviceString);
   232     }
   233     display.desktop_mode = mode;
   234     display.current_mode = mode;
   235     display.driverdata = displaydata;
   236     SDL_AddVideoDisplay(&display);
   237     SDL_free(display.name);
   238     return SDL_TRUE;
   239 }
   240 
   241 int
   242 WIN_InitModes(_THIS)
   243 {
   244     int pass;
   245     DWORD i, j, count;
   246     DISPLAY_DEVICE device;
   247 
   248     device.cb = sizeof(device);
   249 
   250     /* Get the primary display in the first pass */
   251     for (pass = 0; pass < 2; ++pass) {
   252         for (i = 0; ; ++i) {
   253             TCHAR DeviceName[32];
   254 
   255             if (!EnumDisplayDevices(NULL, i, &device, 0)) {
   256                 break;
   257             }
   258             if (!(device.StateFlags & DISPLAY_DEVICE_ATTACHED_TO_DESKTOP)) {
   259                 continue;
   260             }
   261             if (pass == 0) {
   262                 if (!(device.StateFlags & DISPLAY_DEVICE_PRIMARY_DEVICE)) {
   263                     continue;
   264                 }
   265             } else {
   266                 if (device.StateFlags & DISPLAY_DEVICE_PRIMARY_DEVICE) {
   267                     continue;
   268                 }
   269             }
   270             SDL_memcpy(DeviceName, device.DeviceName, sizeof(DeviceName));
   271 #ifdef DEBUG_MODES
   272             printf("Device: %s\n", WIN_StringToUTF8(DeviceName));
   273 #endif
   274             count = 0;
   275             for (j = 0; ; ++j) {
   276                 if (!EnumDisplayDevices(DeviceName, j, &device, 0)) {
   277                     break;
   278                 }
   279                 if (!(device.StateFlags & DISPLAY_DEVICE_ATTACHED_TO_DESKTOP)) {
   280                     continue;
   281                 }
   282                 if (pass == 0) {
   283                     if (!(device.StateFlags & DISPLAY_DEVICE_PRIMARY_DEVICE)) {
   284                         continue;
   285                     }
   286                 } else {
   287                     if (device.StateFlags & DISPLAY_DEVICE_PRIMARY_DEVICE) {
   288                         continue;
   289                     }
   290                 }
   291                 count += WIN_AddDisplay(_this, device.DeviceName);
   292             }
   293             if (count == 0) {
   294                 WIN_AddDisplay(_this, DeviceName);
   295             }
   296         }
   297     }
   298     if (_this->num_displays == 0) {
   299         return SDL_SetError("No displays available");
   300     }
   301     return 0;
   302 }
   303 
   304 int
   305 WIN_GetDisplayBounds(_THIS, SDL_VideoDisplay * display, SDL_Rect * rect)
   306 {
   307     SDL_DisplayModeData *data = (SDL_DisplayModeData *) display->current_mode.driverdata;
   308 
   309     rect->x = (int)SDL_ceil(data->DeviceMode.dmPosition.x * data->ScaleX);
   310     rect->y = (int)SDL_ceil(data->DeviceMode.dmPosition.y * data->ScaleY);
   311     rect->w = (int)SDL_ceil(data->DeviceMode.dmPelsWidth * data->ScaleX);
   312     rect->h = (int)SDL_ceil(data->DeviceMode.dmPelsHeight * data->ScaleY);
   313 
   314     return 0;
   315 }
   316 
   317 int
   318 WIN_GetDisplayDPI(_THIS, SDL_VideoDisplay * display, float * ddpi, float * hdpi, float * vdpi)
   319 {
   320     SDL_DisplayModeData *data = (SDL_DisplayModeData *) display->current_mode.driverdata;
   321 
   322     if (ddpi) {
   323         *ddpi = data->DiagDPI;
   324     }
   325     if (hdpi) {
   326         *hdpi = data->HorzDPI;
   327     }
   328     if (vdpi) {
   329         *vdpi = data->VertDPI;
   330     }
   331 
   332     return data->DiagDPI != 0.0f ? 0 : -1;
   333 }
   334 
   335 void
   336 WIN_GetDisplayModes(_THIS, SDL_VideoDisplay * display)
   337 {
   338     SDL_DisplayData *data = (SDL_DisplayData *) display->driverdata;
   339     DWORD i;
   340     SDL_DisplayMode mode;
   341 
   342     for (i = 0;; ++i) {
   343         if (!WIN_GetDisplayMode(_this, data->DeviceName, i, &mode)) {
   344             break;
   345         }
   346         if (SDL_ISPIXELFORMAT_INDEXED(mode.format)) {
   347             /* We don't support palettized modes now */
   348             SDL_free(mode.driverdata);
   349             continue;
   350         }
   351         if (mode.format != SDL_PIXELFORMAT_UNKNOWN) {
   352             if (!SDL_AddDisplayMode(display, &mode)) {
   353                 SDL_free(mode.driverdata);
   354             }
   355         } else {
   356             SDL_free(mode.driverdata);
   357         }
   358     }
   359 }
   360 
   361 int
   362 WIN_SetDisplayMode(_THIS, SDL_VideoDisplay * display, SDL_DisplayMode * mode)
   363 {
   364     SDL_DisplayData *displaydata = (SDL_DisplayData *) display->driverdata;
   365     SDL_DisplayModeData *data = (SDL_DisplayModeData *) mode->driverdata;
   366     LONG status;
   367 
   368     if (mode->driverdata == display->desktop_mode.driverdata) {
   369         status = ChangeDisplaySettingsEx(displaydata->DeviceName, NULL, NULL, 0, NULL);
   370     } else {
   371         status = ChangeDisplaySettingsEx(displaydata->DeviceName, &data->DeviceMode, NULL, CDS_FULLSCREEN, NULL);
   372     }
   373     if (status != DISP_CHANGE_SUCCESSFUL) {
   374         const char *reason = "Unknown reason";
   375         switch (status) {
   376         case DISP_CHANGE_BADFLAGS:
   377             reason = "DISP_CHANGE_BADFLAGS";
   378             break;
   379         case DISP_CHANGE_BADMODE:
   380             reason = "DISP_CHANGE_BADMODE";
   381             break;
   382         case DISP_CHANGE_BADPARAM:
   383             reason = "DISP_CHANGE_BADPARAM";
   384             break;
   385         case DISP_CHANGE_FAILED:
   386             reason = "DISP_CHANGE_FAILED";
   387             break;
   388         }
   389         return SDL_SetError("ChangeDisplaySettingsEx() failed: %s", reason);
   390     }
   391     EnumDisplaySettings(displaydata->DeviceName, ENUM_CURRENT_SETTINGS, &data->DeviceMode);
   392     return 0;
   393 }
   394 
   395 void
   396 WIN_QuitModes(_THIS)
   397 {
   398     /* All fullscreen windows should have restored modes by now */
   399 }
   400 
   401 #endif /* SDL_VIDEO_DRIVER_WINDOWS */
   402 
   403 /* vi: set ts=4 sw=4 expandtab: */