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