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