sdl

FORK: Simple Directmedia Layer
git clone https://git.neptards.moe/neptards/sdl.git
Log | Files | Refs

SDL_windowsmodes.c (13993B)


      1 /*
      2   Simple DirectMedia Layer
      3   Copyright (C) 1997-2020 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 /* #define DEBUG_MODES */
     33 
     34 static void
     35 WIN_UpdateDisplayMode(_THIS, LPCTSTR deviceName, DWORD index, SDL_DisplayMode * mode)
     36 {
     37     SDL_DisplayModeData *data = (SDL_DisplayModeData *) mode->driverdata;
     38     HDC hdc;
     39 
     40     data->DeviceMode.dmFields =
     41         (DM_BITSPERPEL | DM_PELSWIDTH | DM_PELSHEIGHT | DM_DISPLAYFREQUENCY |
     42          DM_DISPLAYFLAGS);
     43 
     44     if (index == ENUM_CURRENT_SETTINGS
     45         && (hdc = CreateDC(deviceName, NULL, NULL, NULL)) != NULL) {
     46         char bmi_data[sizeof(BITMAPINFOHEADER) + 256 * sizeof(RGBQUAD)];
     47         LPBITMAPINFO bmi;
     48         HBITMAP hbm;
     49         int logical_width = GetDeviceCaps( hdc, HORZRES );
     50         int logical_height = GetDeviceCaps( hdc, VERTRES );
     51 
     52         mode->w = logical_width;
     53         mode->h = logical_height;
     54         
     55         SDL_zeroa(bmi_data);
     56         bmi = (LPBITMAPINFO) bmi_data;
     57         bmi->bmiHeader.biSize = sizeof(BITMAPINFOHEADER);
     58 
     59         hbm = CreateCompatibleBitmap(hdc, 1, 1);
     60         GetDIBits(hdc, hbm, 0, 1, NULL, bmi, DIB_RGB_COLORS);
     61         GetDIBits(hdc, hbm, 0, 1, NULL, bmi, DIB_RGB_COLORS);
     62         DeleteObject(hbm);
     63         DeleteDC(hdc);
     64         if (bmi->bmiHeader.biCompression == BI_BITFIELDS) {
     65             switch (*(Uint32 *) bmi->bmiColors) {
     66             case 0x00FF0000:
     67                 mode->format = SDL_PIXELFORMAT_RGB888;
     68                 break;
     69             case 0x000000FF:
     70                 mode->format = SDL_PIXELFORMAT_BGR888;
     71                 break;
     72             case 0xF800:
     73                 mode->format = SDL_PIXELFORMAT_RGB565;
     74                 break;
     75             case 0x7C00:
     76                 mode->format = SDL_PIXELFORMAT_RGB555;
     77                 break;
     78             }
     79         } else if (bmi->bmiHeader.biBitCount == 8) {
     80             mode->format = SDL_PIXELFORMAT_INDEX8;
     81         } else if (bmi->bmiHeader.biBitCount == 4) {
     82             mode->format = SDL_PIXELFORMAT_INDEX4LSB;
     83         }
     84     } else if (mode->format == SDL_PIXELFORMAT_UNKNOWN) {
     85         /* FIXME: Can we tell what this will be? */
     86         if ((data->DeviceMode.dmFields & DM_BITSPERPEL) == DM_BITSPERPEL) {
     87             switch (data->DeviceMode.dmBitsPerPel) {
     88             case 32:
     89                 mode->format = SDL_PIXELFORMAT_RGB888;
     90                 break;
     91             case 24:
     92                 mode->format = SDL_PIXELFORMAT_RGB24;
     93                 break;
     94             case 16:
     95                 mode->format = SDL_PIXELFORMAT_RGB565;
     96                 break;
     97             case 15:
     98                 mode->format = SDL_PIXELFORMAT_RGB555;
     99                 break;
    100             case 8:
    101                 mode->format = SDL_PIXELFORMAT_INDEX8;
    102                 break;
    103             case 4:
    104                 mode->format = SDL_PIXELFORMAT_INDEX4LSB;
    105                 break;
    106             }
    107         }
    108     }
    109 }
    110 
    111 static SDL_bool
    112 WIN_GetDisplayMode(_THIS, LPCTSTR deviceName, DWORD index, SDL_DisplayMode * mode)
    113 {
    114     SDL_DisplayModeData *data;
    115     DEVMODE devmode;
    116 
    117     devmode.dmSize = sizeof(devmode);
    118     devmode.dmDriverExtra = 0;
    119     if (!EnumDisplaySettings(deviceName, index, &devmode)) {
    120         return SDL_FALSE;
    121     }
    122 
    123     data = (SDL_DisplayModeData *) SDL_malloc(sizeof(*data));
    124     if (!data) {
    125         return SDL_FALSE;
    126     }
    127 
    128     mode->driverdata = data;
    129     data->DeviceMode = devmode;
    130 
    131     mode->format = SDL_PIXELFORMAT_UNKNOWN;
    132     mode->w = data->DeviceMode.dmPelsWidth;
    133     mode->h = data->DeviceMode.dmPelsHeight;
    134     mode->refresh_rate = data->DeviceMode.dmDisplayFrequency;
    135 
    136     /* Fill in the mode information */
    137     WIN_UpdateDisplayMode(_this, deviceName, index, mode);
    138     return SDL_TRUE;
    139 }
    140 
    141 static SDL_bool
    142 WIN_AddDisplay(_THIS, HMONITOR hMonitor, const MONITORINFOEXW *info, SDL_bool send_event)
    143 {
    144     int i;
    145     SDL_VideoDisplay display;
    146     SDL_DisplayData *displaydata;
    147     SDL_DisplayMode mode;
    148     DISPLAY_DEVICE device;
    149 
    150 #ifdef DEBUG_MODES
    151     SDL_Log("Display: %s\n", WIN_StringToUTF8(info->szDevice));
    152 #endif
    153 
    154     if (!WIN_GetDisplayMode(_this, info->szDevice, ENUM_CURRENT_SETTINGS, &mode)) {
    155         return SDL_FALSE;
    156     }
    157 
    158     // Prevent adding duplicate displays. Do this after we know the display is
    159     // ready to be added to allow any displays that we can't fully query to be
    160     // removed
    161     for(i = 0; i < _this->num_displays; ++i) {
    162         SDL_DisplayData *driverdata = (SDL_DisplayData *)_this->displays[i].driverdata;
    163         if (SDL_wcscmp(driverdata->DeviceName, info->szDevice) == 0) {
    164             driverdata->MonitorHandle = hMonitor;
    165             driverdata->IsValid = SDL_TRUE;
    166             return SDL_FALSE;
    167         }
    168     }
    169 
    170     displaydata = (SDL_DisplayData *) SDL_malloc(sizeof(*displaydata));
    171     if (!displaydata) {
    172         return SDL_FALSE;
    173     }
    174     SDL_memcpy(displaydata->DeviceName, info->szDevice,
    175                sizeof(displaydata->DeviceName));
    176     displaydata->MonitorHandle = hMonitor;
    177     displaydata->IsValid = SDL_TRUE;
    178 
    179     SDL_zero(display);
    180     device.cb = sizeof(device);
    181     if (EnumDisplayDevices(info->szDevice, 0, &device, 0)) {
    182         display.name = WIN_StringToUTF8(device.DeviceString);
    183     }
    184     display.desktop_mode = mode;
    185     display.current_mode = mode;
    186     display.driverdata = displaydata;
    187     SDL_AddVideoDisplay(&display, send_event);
    188     SDL_free(display.name);
    189     return SDL_TRUE;
    190 }
    191 
    192 typedef struct _WIN_AddDisplaysData {
    193     SDL_VideoDevice *video_device;
    194     SDL_bool send_event;
    195     SDL_bool want_primary;
    196 } WIN_AddDisplaysData;
    197 
    198 static BOOL CALLBACK
    199 WIN_AddDisplaysCallback(HMONITOR hMonitor,
    200                         HDC      hdcMonitor,
    201                         LPRECT   lprcMonitor,
    202                         LPARAM   dwData)
    203 {
    204     WIN_AddDisplaysData *data = (WIN_AddDisplaysData*)dwData;
    205     MONITORINFOEXW info;
    206 
    207     SDL_zero(info);
    208     info.cbSize = sizeof(info);
    209 
    210     if (GetMonitorInfoW(hMonitor, (LPMONITORINFO)&info) != 0) {
    211         const SDL_bool is_primary = ((info.dwFlags & MONITORINFOF_PRIMARY) == MONITORINFOF_PRIMARY);
    212 
    213         if (is_primary == data->want_primary) {
    214             WIN_AddDisplay(data->video_device, hMonitor, &info, data->send_event);
    215         }
    216     }
    217 
    218     // continue enumeration
    219     return TRUE;
    220 }
    221 
    222 static void
    223 WIN_AddDisplays(_THIS, SDL_bool send_event)
    224 {
    225     WIN_AddDisplaysData callback_data;
    226     callback_data.video_device = _this;
    227     callback_data.send_event = send_event;
    228 
    229     callback_data.want_primary = SDL_TRUE;
    230     EnumDisplayMonitors(NULL, NULL, WIN_AddDisplaysCallback, (LPARAM)&callback_data);
    231 
    232     callback_data.want_primary = SDL_FALSE;
    233     EnumDisplayMonitors(NULL, NULL, WIN_AddDisplaysCallback, (LPARAM)&callback_data);
    234 }
    235 
    236 int
    237 WIN_InitModes(_THIS)
    238 {
    239     WIN_AddDisplays(_this, SDL_FALSE);
    240 
    241     if (_this->num_displays == 0) {
    242         return SDL_SetError("No displays available");
    243     }
    244     return 0;
    245 }
    246 
    247 int
    248 WIN_GetDisplayBounds(_THIS, SDL_VideoDisplay * display, SDL_Rect * rect)
    249 {
    250     const SDL_DisplayData *data = (const SDL_DisplayData *)display->driverdata;
    251     MONITORINFO minfo;
    252     BOOL rc;
    253 
    254     SDL_zero(minfo);
    255     minfo.cbSize = sizeof(MONITORINFO);
    256     rc = GetMonitorInfo(data->MonitorHandle, &minfo);
    257 
    258     if (!rc) {
    259         return SDL_SetError("Couldn't find monitor data");
    260     }
    261 
    262     rect->x = minfo.rcMonitor.left;
    263     rect->y = minfo.rcMonitor.top;
    264     rect->w = minfo.rcMonitor.right - minfo.rcMonitor.left;
    265     rect->h = minfo.rcMonitor.bottom - minfo.rcMonitor.top;
    266 
    267     return 0;
    268 }
    269 
    270 int
    271 WIN_GetDisplayDPI(_THIS, SDL_VideoDisplay * display, float * ddpi_out, float * hdpi_out, float * vdpi_out)
    272 {
    273     const SDL_DisplayData *displaydata = (SDL_DisplayData *)display->driverdata;
    274     const SDL_VideoData *videodata = (SDL_VideoData *)display->device->driverdata;
    275     float hdpi = 0, vdpi = 0, ddpi = 0;
    276     
    277     if (videodata->GetDpiForMonitor) {
    278         UINT hdpi_uint, vdpi_uint;
    279         // Windows 8.1+ codepath
    280         if (videodata->GetDpiForMonitor(displaydata->MonitorHandle, MDT_EFFECTIVE_DPI, &hdpi_uint, &vdpi_uint) == S_OK) {
    281             // GetDpiForMonitor docs promise to return the same hdpi/vdpi
    282             hdpi = (float)hdpi_uint;
    283             vdpi = (float)hdpi_uint;
    284             ddpi = (float)hdpi_uint;
    285         } else {
    286             return SDL_SetError("GetDpiForMonitor failed");
    287         }
    288     } else {
    289         // Window 8.0 and below: same DPI for all monitors.
    290         HDC hdc;
    291         int hdpi_int, vdpi_int, hpoints, vpoints, hpix, vpix;
    292         float hinches, vinches;
    293 
    294         hdc = GetDC(NULL);
    295         if (hdc == NULL) {
    296             return SDL_SetError("GetDC failed");
    297         }
    298         hdpi_int = GetDeviceCaps(hdc, LOGPIXELSX);
    299         vdpi_int = GetDeviceCaps(hdc, LOGPIXELSY);
    300         ReleaseDC(NULL, hdc);
    301 
    302         hpoints = GetSystemMetrics(SM_CXVIRTUALSCREEN);
    303         vpoints = GetSystemMetrics(SM_CYVIRTUALSCREEN);
    304 
    305         hpix = MulDiv(hpoints, hdpi_int, 96);
    306         vpix = MulDiv(vpoints, vdpi_int, 96);
    307 
    308         hinches = (float)hpoints / 96.0f;
    309         vinches = (float)vpoints / 96.0f;
    310 
    311         hdpi = (float)hdpi_int;
    312         vdpi = (float)vdpi_int;
    313         ddpi = SDL_ComputeDiagonalDPI(hpix, vpix, hinches, vinches);
    314     }
    315 
    316     if (ddpi_out) {
    317         *ddpi_out = ddpi;
    318     }
    319     if (hdpi_out) {
    320         *hdpi_out = hdpi;
    321     }
    322     if (vdpi_out) {
    323         *vdpi_out = vdpi;
    324     }
    325 
    326     return ddpi != 0.0f ? 0 : SDL_SetError("Couldn't get DPI");
    327 }
    328 
    329 int
    330 WIN_GetDisplayUsableBounds(_THIS, SDL_VideoDisplay * display, SDL_Rect * rect)
    331 {
    332     const SDL_DisplayData *data = (const SDL_DisplayData *)display->driverdata;
    333     MONITORINFO minfo;
    334     BOOL rc;
    335 
    336     SDL_zero(minfo);
    337     minfo.cbSize = sizeof(MONITORINFO);
    338     rc = GetMonitorInfo(data->MonitorHandle, &minfo);
    339 
    340     if (!rc) {
    341         return SDL_SetError("Couldn't find monitor data");
    342     }
    343 
    344     rect->x = minfo.rcWork.left;
    345     rect->y = minfo.rcWork.top;
    346     rect->w = minfo.rcWork.right - minfo.rcWork.left;
    347     rect->h = minfo.rcWork.bottom - minfo.rcWork.top;
    348 
    349     return 0;
    350 }
    351 
    352 void
    353 WIN_GetDisplayModes(_THIS, SDL_VideoDisplay * display)
    354 {
    355     SDL_DisplayData *data = (SDL_DisplayData *) display->driverdata;
    356     DWORD i;
    357     SDL_DisplayMode mode;
    358 
    359     for (i = 0;; ++i) {
    360         if (!WIN_GetDisplayMode(_this, data->DeviceName, i, &mode)) {
    361             break;
    362         }
    363         if (SDL_ISPIXELFORMAT_INDEXED(mode.format)) {
    364             /* We don't support palettized modes now */
    365             SDL_free(mode.driverdata);
    366             continue;
    367         }
    368         if (mode.format != SDL_PIXELFORMAT_UNKNOWN) {
    369             if (!SDL_AddDisplayMode(display, &mode)) {
    370                 SDL_free(mode.driverdata);
    371             }
    372         } else {
    373             SDL_free(mode.driverdata);
    374         }
    375     }
    376 }
    377 
    378 int
    379 WIN_SetDisplayMode(_THIS, SDL_VideoDisplay * display, SDL_DisplayMode * mode)
    380 {
    381     SDL_DisplayData *displaydata = (SDL_DisplayData *) display->driverdata;
    382     SDL_DisplayModeData *data = (SDL_DisplayModeData *) mode->driverdata;
    383     LONG status;
    384 
    385     if (mode->driverdata == display->desktop_mode.driverdata) {
    386         status = ChangeDisplaySettingsEx(displaydata->DeviceName, NULL, NULL, CDS_FULLSCREEN, NULL);
    387     } else {
    388         status = ChangeDisplaySettingsEx(displaydata->DeviceName, &data->DeviceMode, NULL, CDS_FULLSCREEN, NULL);
    389     }
    390     if (status != DISP_CHANGE_SUCCESSFUL) {
    391         const char *reason = "Unknown reason";
    392         switch (status) {
    393         case DISP_CHANGE_BADFLAGS:
    394             reason = "DISP_CHANGE_BADFLAGS";
    395             break;
    396         case DISP_CHANGE_BADMODE:
    397             reason = "DISP_CHANGE_BADMODE";
    398             break;
    399         case DISP_CHANGE_BADPARAM:
    400             reason = "DISP_CHANGE_BADPARAM";
    401             break;
    402         case DISP_CHANGE_FAILED:
    403             reason = "DISP_CHANGE_FAILED";
    404             break;
    405         }
    406         return SDL_SetError("ChangeDisplaySettingsEx() failed: %s", reason);
    407     }
    408     EnumDisplaySettings(displaydata->DeviceName, ENUM_CURRENT_SETTINGS, &data->DeviceMode);
    409     WIN_UpdateDisplayMode(_this, displaydata->DeviceName, ENUM_CURRENT_SETTINGS, mode);
    410     return 0;
    411 }
    412 
    413 void
    414 WIN_RefreshDisplays(_THIS)
    415 {
    416     int i;
    417 
    418     // Mark all displays as potentially invalid to detect
    419     // entries that have actually been removed
    420     for (i = 0; i < _this->num_displays; ++i) {
    421         SDL_DisplayData *driverdata = (SDL_DisplayData *)_this->displays[i].driverdata;
    422         driverdata->IsValid = SDL_FALSE;
    423     }
    424 
    425     // Enumerate displays to add any new ones and mark still
    426     // connected entries as valid
    427     WIN_AddDisplays(_this, SDL_TRUE);
    428 
    429     // Delete any entries still marked as invalid, iterate
    430     // in reverse as each delete takes effect immediately
    431     for (i = _this->num_displays - 1; i >= 0; --i) {
    432         SDL_DisplayData *driverdata = (SDL_DisplayData *)_this->displays[i].driverdata;
    433         if (driverdata->IsValid == SDL_FALSE) {
    434             SDL_DelVideoDisplay(i);
    435         }
    436     }
    437 }
    438 
    439 void
    440 WIN_QuitModes(_THIS)
    441 {
    442     /* All fullscreen windows should have restored modes by now */
    443 }
    444 
    445 #endif /* SDL_VIDEO_DRIVER_WINDOWS */
    446 
    447 /* vi: set ts=4 sw=4 expandtab: */