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