• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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 #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
WIN_GetMonitorDPI(HMONITOR hMonitor,HDC hdcMonitor,LPRECT lprcMonitor,LPARAM dwData)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
WIN_UpdateDisplayMode(_THIS,LPCTSTR deviceName,DWORD index,SDL_DisplayMode * mode)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
WIN_GetDisplayMode(_THIS,LPCTSTR deviceName,DWORD index,SDL_DisplayMode * mode)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
WIN_AddDisplay(_THIS,LPTSTR DeviceName)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
WIN_InitModes(_THIS)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
WIN_GetDisplayBounds(_THIS,SDL_VideoDisplay * display,SDL_Rect * rect)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
WIN_GetDisplayDPI(_THIS,SDL_VideoDisplay * display,float * ddpi,float * hdpi,float * vdpi)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
WIN_GetDisplayUsableBounds(_THIS,SDL_VideoDisplay * display,SDL_Rect * rect)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
WIN_GetDisplayModes(_THIS,SDL_VideoDisplay * display)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
WIN_SetDisplayMode(_THIS,SDL_VideoDisplay * display,SDL_DisplayMode * mode)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
WIN_QuitModes(_THIS)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: */
455