2 Simple DirectMedia Layer
3 Copyright (C) 1997-2020 Sam Lantinga <slouken@libsdl.org>
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.
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:
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.
21 #include "../../SDL_internal.h"
23 #if SDL_VIDEO_DRIVER_WINDOWS
25 #include "SDL_windowsvideo.h"
27 /* Windows CE compatibility */
28 #ifndef CDS_FULLSCREEN
29 #define CDS_FULLSCREEN 0
32 /* #define DEBUG_MODES */
35 WIN_UpdateDisplayMode(_THIS, LPCTSTR deviceName, DWORD index, SDL_DisplayMode * mode)
37 SDL_DisplayModeData *data = (SDL_DisplayModeData *) mode->driverdata;
40 data->DeviceMode.dmFields =
41 (DM_BITSPERPEL | DM_PELSWIDTH | DM_PELSHEIGHT | DM_DISPLAYFREQUENCY |
44 if (index == ENUM_CURRENT_SETTINGS
45 && (hdc = CreateDC(deviceName, NULL, NULL, NULL)) != NULL) {
46 char bmi_data[sizeof(BITMAPINFOHEADER) + 256 * sizeof(RGBQUAD)];
49 int logical_width = GetDeviceCaps( hdc, HORZRES );
50 int logical_height = GetDeviceCaps( hdc, VERTRES );
52 mode->w = logical_width;
53 mode->h = logical_height;
56 bmi = (LPBITMAPINFO) bmi_data;
57 bmi->bmiHeader.biSize = sizeof(BITMAPINFOHEADER);
59 hbm = CreateCompatibleBitmap(hdc, 1, 1);
60 GetDIBits(hdc, hbm, 0, 1, NULL, bmi, DIB_RGB_COLORS);
61 GetDIBits(hdc, hbm, 0, 1, NULL, bmi, DIB_RGB_COLORS);
64 if (bmi->bmiHeader.biCompression == BI_BITFIELDS) {
65 switch (*(Uint32 *) bmi->bmiColors) {
67 mode->format = SDL_PIXELFORMAT_RGB888;
70 mode->format = SDL_PIXELFORMAT_BGR888;
73 mode->format = SDL_PIXELFORMAT_RGB565;
76 mode->format = SDL_PIXELFORMAT_RGB555;
79 } else if (bmi->bmiHeader.biBitCount == 8) {
80 mode->format = SDL_PIXELFORMAT_INDEX8;
81 } else if (bmi->bmiHeader.biBitCount == 4) {
82 mode->format = SDL_PIXELFORMAT_INDEX4LSB;
84 } else if (mode->format == SDL_PIXELFORMAT_UNKNOWN) {
85 /* FIXME: Can we tell what this will be? */
86 if ((data->DeviceMode.dmFields & DM_BITSPERPEL) == DM_BITSPERPEL) {
87 switch (data->DeviceMode.dmBitsPerPel) {
89 mode->format = SDL_PIXELFORMAT_RGB888;
92 mode->format = SDL_PIXELFORMAT_RGB24;
95 mode->format = SDL_PIXELFORMAT_RGB565;
98 mode->format = SDL_PIXELFORMAT_RGB555;
101 mode->format = SDL_PIXELFORMAT_INDEX8;
104 mode->format = SDL_PIXELFORMAT_INDEX4LSB;
112 WIN_GetDisplayMode(_THIS, LPCTSTR deviceName, DWORD index, SDL_DisplayMode * mode)
114 SDL_DisplayModeData *data;
117 devmode.dmSize = sizeof(devmode);
118 devmode.dmDriverExtra = 0;
119 if (!EnumDisplaySettings(deviceName, index, &devmode)) {
123 data = (SDL_DisplayModeData *) SDL_malloc(sizeof(*data));
128 mode->driverdata = data;
129 data->DeviceMode = devmode;
131 mode->format = SDL_PIXELFORMAT_UNKNOWN;
132 mode->w = data->DeviceMode.dmPelsWidth;
133 mode->h = data->DeviceMode.dmPelsHeight;
134 mode->refresh_rate = data->DeviceMode.dmDisplayFrequency;
136 /* Fill in the mode information */
137 WIN_UpdateDisplayMode(_this, deviceName, index, mode);
142 WIN_AddDisplay(_THIS, HMONITOR hMonitor, const MONITORINFOEXW *info, SDL_bool send_event)
145 SDL_VideoDisplay display;
146 SDL_DisplayData *displaydata;
147 SDL_DisplayMode mode;
148 DISPLAY_DEVICE device;
151 SDL_Log("Display: %s\n", WIN_StringToUTF8(info->szDevice));
154 if (!WIN_GetDisplayMode(_this, info->szDevice, ENUM_CURRENT_SETTINGS, &mode)) {
158 // Prevent adding duplicate displays. Do this after we know the display is
159 // ready to be added to allow any displays that we can't fully query to be
161 for(i = 0; i < _this->num_displays; ++i) {
162 SDL_DisplayData *driverdata = (SDL_DisplayData *)_this->displays[i].driverdata;
163 if (SDL_wcscmp(driverdata->DeviceName, info->szDevice) == 0) {
164 driverdata->MonitorHandle = hMonitor;
165 driverdata->IsValid = SDL_TRUE;
170 displaydata = (SDL_DisplayData *) SDL_malloc(sizeof(*displaydata));
174 SDL_memcpy(displaydata->DeviceName, info->szDevice,
175 sizeof(displaydata->DeviceName));
176 displaydata->MonitorHandle = hMonitor;
177 displaydata->IsValid = SDL_TRUE;
180 device.cb = sizeof(device);
181 if (EnumDisplayDevices(info->szDevice, 0, &device, 0)) {
182 display.name = WIN_StringToUTF8(device.DeviceString);
184 display.desktop_mode = mode;
185 display.current_mode = mode;
186 display.driverdata = displaydata;
187 SDL_AddVideoDisplay(&display, send_event);
188 SDL_free(display.name);
192 typedef struct _WIN_AddDisplaysData {
193 SDL_VideoDevice *video_device;
195 SDL_bool want_primary;
196 } WIN_AddDisplaysData;
199 WIN_AddDisplaysCallback(HMONITOR hMonitor,
204 WIN_AddDisplaysData *data = (WIN_AddDisplaysData*)dwData;
208 info.cbSize = sizeof(info);
210 if (GetMonitorInfoW(hMonitor, (LPMONITORINFO)&info) != 0) {
211 const SDL_bool is_primary = ((info.dwFlags & MONITORINFOF_PRIMARY) == MONITORINFOF_PRIMARY);
213 if (is_primary == data->want_primary) {
214 WIN_AddDisplay(data->video_device, hMonitor, &info, data->send_event);
218 // continue enumeration
223 WIN_AddDisplays(_THIS, SDL_bool send_event)
225 WIN_AddDisplaysData callback_data;
226 callback_data.video_device = _this;
227 callback_data.send_event = send_event;
229 callback_data.want_primary = SDL_TRUE;
230 EnumDisplayMonitors(NULL, NULL, WIN_AddDisplaysCallback, (LPARAM)&callback_data);
232 callback_data.want_primary = SDL_FALSE;
233 EnumDisplayMonitors(NULL, NULL, WIN_AddDisplaysCallback, (LPARAM)&callback_data);
239 WIN_AddDisplays(_this, SDL_FALSE);
241 if (_this->num_displays == 0) {
242 return SDL_SetError("No displays available");
248 WIN_GetDisplayBounds(_THIS, SDL_VideoDisplay * display, SDL_Rect * rect)
250 const SDL_DisplayData *data = (const SDL_DisplayData *)display->driverdata;
255 minfo.cbSize = sizeof(MONITORINFO);
256 rc = GetMonitorInfo(data->MonitorHandle, &minfo);
259 return SDL_SetError("Couldn't find monitor data");
262 rect->x = minfo.rcMonitor.left;
263 rect->y = minfo.rcMonitor.top;
264 rect->w = minfo.rcMonitor.right - minfo.rcMonitor.left;
265 rect->h = minfo.rcMonitor.bottom - minfo.rcMonitor.top;
271 WIN_GetDisplayDPI(_THIS, SDL_VideoDisplay * display, float * ddpi_out, float * hdpi_out, float * vdpi_out)
273 const SDL_DisplayData *displaydata = (SDL_DisplayData *)display->driverdata;
274 const SDL_VideoData *videodata = (SDL_VideoData *)display->device->driverdata;
275 float hdpi = 0, vdpi = 0, ddpi = 0;
277 if (videodata->GetDpiForMonitor) {
278 UINT hdpi_uint, vdpi_uint;
279 // Windows 8.1+ codepath
280 if (videodata->GetDpiForMonitor(displaydata->MonitorHandle, MDT_EFFECTIVE_DPI, &hdpi_uint, &vdpi_uint) == S_OK) {
281 // GetDpiForMonitor docs promise to return the same hdpi/vdpi
282 hdpi = (float)hdpi_uint;
283 vdpi = (float)hdpi_uint;
284 ddpi = (float)hdpi_uint;
286 return SDL_SetError("GetDpiForMonitor failed");
289 // Window 8.0 and below: same DPI for all monitors.
291 int hdpi_int, vdpi_int, hpoints, vpoints, hpix, vpix;
292 float hinches, vinches;
296 return SDL_SetError("GetDC failed");
298 hdpi_int = GetDeviceCaps(hdc, LOGPIXELSX);
299 vdpi_int = GetDeviceCaps(hdc, LOGPIXELSY);
300 ReleaseDC(NULL, hdc);
302 hpoints = GetSystemMetrics(SM_CXVIRTUALSCREEN);
303 vpoints = GetSystemMetrics(SM_CYVIRTUALSCREEN);
305 hpix = MulDiv(hpoints, hdpi_int, 96);
306 vpix = MulDiv(vpoints, vdpi_int, 96);
308 hinches = (float)hpoints / 96.0f;
309 vinches = (float)vpoints / 96.0f;
311 hdpi = (float)hdpi_int;
312 vdpi = (float)vdpi_int;
313 ddpi = SDL_ComputeDiagonalDPI(hpix, vpix, hinches, vinches);
326 return ddpi != 0.0f ? 0 : SDL_SetError("Couldn't get DPI");
330 WIN_GetDisplayUsableBounds(_THIS, SDL_VideoDisplay * display, SDL_Rect * rect)
332 const SDL_DisplayData *data = (const SDL_DisplayData *)display->driverdata;
337 minfo.cbSize = sizeof(MONITORINFO);
338 rc = GetMonitorInfo(data->MonitorHandle, &minfo);
341 return SDL_SetError("Couldn't find monitor data");
344 rect->x = minfo.rcWork.left;
345 rect->y = minfo.rcWork.top;
346 rect->w = minfo.rcWork.right - minfo.rcWork.left;
347 rect->h = minfo.rcWork.bottom - minfo.rcWork.top;
353 WIN_GetDisplayModes(_THIS, SDL_VideoDisplay * display)
355 SDL_DisplayData *data = (SDL_DisplayData *) display->driverdata;
357 SDL_DisplayMode mode;
360 if (!WIN_GetDisplayMode(_this, data->DeviceName, i, &mode)) {
363 if (SDL_ISPIXELFORMAT_INDEXED(mode.format)) {
364 /* We don't support palettized modes now */
365 SDL_free(mode.driverdata);
368 if (mode.format != SDL_PIXELFORMAT_UNKNOWN) {
369 if (!SDL_AddDisplayMode(display, &mode)) {
370 SDL_free(mode.driverdata);
373 SDL_free(mode.driverdata);
379 WIN_SetDisplayMode(_THIS, SDL_VideoDisplay * display, SDL_DisplayMode * mode)
381 SDL_DisplayData *displaydata = (SDL_DisplayData *) display->driverdata;
382 SDL_DisplayModeData *data = (SDL_DisplayModeData *) mode->driverdata;
385 if (mode->driverdata == display->desktop_mode.driverdata) {
386 status = ChangeDisplaySettingsEx(displaydata->DeviceName, NULL, NULL, CDS_FULLSCREEN, NULL);
388 status = ChangeDisplaySettingsEx(displaydata->DeviceName, &data->DeviceMode, NULL, CDS_FULLSCREEN, NULL);
390 if (status != DISP_CHANGE_SUCCESSFUL) {
391 const char *reason = "Unknown reason";
393 case DISP_CHANGE_BADFLAGS:
394 reason = "DISP_CHANGE_BADFLAGS";
396 case DISP_CHANGE_BADMODE:
397 reason = "DISP_CHANGE_BADMODE";
399 case DISP_CHANGE_BADPARAM:
400 reason = "DISP_CHANGE_BADPARAM";
402 case DISP_CHANGE_FAILED:
403 reason = "DISP_CHANGE_FAILED";
406 return SDL_SetError("ChangeDisplaySettingsEx() failed: %s", reason);
408 EnumDisplaySettings(displaydata->DeviceName, ENUM_CURRENT_SETTINGS, &data->DeviceMode);
409 WIN_UpdateDisplayMode(_this, displaydata->DeviceName, ENUM_CURRENT_SETTINGS, mode);
414 WIN_RefreshDisplays(_THIS)
418 // Mark all displays as potentially invalid to detect
419 // entries that have actually been removed
420 for (i = 0; i < _this->num_displays; ++i) {
421 SDL_DisplayData *driverdata = (SDL_DisplayData *)_this->displays[i].driverdata;
422 driverdata->IsValid = SDL_FALSE;
425 // Enumerate displays to add any new ones and mark still
426 // connected entries as valid
427 WIN_AddDisplays(_this, SDL_TRUE);
429 // Delete any entries still marked as invalid, iterate
430 // in reverse as each delete takes effect immediately
431 for (i = _this->num_displays - 1; i >= 0; --i) {
432 SDL_DisplayData *driverdata = (SDL_DisplayData *)_this->displays[i].driverdata;
433 if (driverdata->IsValid == SDL_FALSE) {
434 SDL_DelVideoDisplay(i);
442 /* All fullscreen windows should have restored modes by now */
445 #endif /* SDL_VIDEO_DRIVER_WINDOWS */
447 /* vi: set ts=4 sw=4 expandtab: */