91ed67f8718b309a8cada185c2b1a4185957bd53
[platform/upstream/SDL.git] / src / video / windows / SDL_windowsmodes.c
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: */