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