1 /*
2 * Copyright © 2016 Red Hat, Inc
3 *
4 * This library is free software; you can redistribute it and/or
5 * modify it under the terms of the GNU Library General Public
6 * License as published by the Free Software Foundation; either
7 * version 2 of the License, or (at your option) any later version.
8 *
9 * This library is distributed in the hope that it will be useful,
10 * but WITHOUT ANY WARRANTY; without even the implied warranty of
11 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
12 * Library General Public License for more details.
13 *
14 * You should have received a copy of the GNU Library General Public
15 * License along with this library. If not, see <http://www.gnu.org/licenses/>.
16 */
17
18 #if defined (_WIN32_WINNT) && WIN32_WINNT < 0x0601
19 # undef _WIN32_WINNT
20
21 # define _WIN32_WINNT 0x0601
22 # ifdef WINVER
23 # undef WINVER
24 # endif
25 # define WINVER _WIN32_WINNT
26 #elif !defined (_WIN32_WINNT)
27 # define _WIN32_WINNT 0x0601
28 # ifdef WINVER
29 # undef WINVER
30 # endif
31 # define WINVER _WIN32_WINNT
32 #endif
33
34 #include "config.h"
35
36 #include "gdkprivate-win32.h"
37 #include "gdkdisplay-win32.h"
38 #include "gdkmonitor-win32.h"
39
40 #include <glib.h>
41 #include <gio/gio.h>
42
43 #include <cfgmgr32.h>
44 #include <devpropdef.h>
45 #include <setupapi.h>
46
47 #include "gdkprivate-win32.h"
48
49 G_DEFINE_TYPE (GdkWin32Monitor, gdk_win32_monitor, GDK_TYPE_MONITOR)
50
51 /* MinGW-w64 carelessly put DISPLAYCONFIG_OUTPUT_TECHNOLOGY_OTHER = -1 into this
52 * enum, as documented by MSDN. However, with
53 * DISPLAYCONFIG_OUTPUT_TECHNOLOGY_INTERNAL = 0x80000000 and
54 * DISPLAYCONFIG_OUTPUT_TECHNOLOGY_FORCE_UINT32 = 0xFFFFFFFF
55 * this had the effect of increasing enum size from 4 to 8 bytes,
56 * when compiled by GCC (MSVC doesn't have this problem), breaking ABI.
57 * At the moment of writing MinGW-w64 headers are still broken.
58 * When they are fixed, replace 9999 with actual version numbers.
59 * The fix below is not necessarily correct, but it works.
60 */
61 #if SIZEOF_DISPLAYCONFIG_VIDEO_OUTPUT_TECHNOLOGY == 4
62 # define fixedDISPLAYCONFIG_PATH_INFO DISPLAYCONFIG_PATH_INFO
63 # define fixedDISPLAYCONFIG_TARGET_DEVICE_NAME DISPLAYCONFIG_TARGET_DEVICE_NAME
64 #else
65 typedef enum {
66 fixedDISPLAYCONFIG_OUTPUT_TECHNOLOGY_OTHER = (int) -1,
67 fixedDISPLAYCONFIG_OUTPUT_TECHNOLOGY_HD15 = (int) 0,
68 fixedDISPLAYCONFIG_OUTPUT_TECHNOLOGY_SVIDEO = (int) 1,
69 fixedDISPLAYCONFIG_OUTPUT_TECHNOLOGY_COMPOSITE_VIDEO = (int) 2,
70 fixedDISPLAYCONFIG_OUTPUT_TECHNOLOGY_COMPONENT_VIDEO = (int) 3,
71 fixedDISPLAYCONFIG_OUTPUT_TECHNOLOGY_DVI = (int) 4,
72 fixedDISPLAYCONFIG_OUTPUT_TECHNOLOGY_HDMI = (int) 5,
73 fixedDISPLAYCONFIG_OUTPUT_TECHNOLOGY_LVDS = (int) 6,
74 fixedDISPLAYCONFIG_OUTPUT_TECHNOLOGY_D_JPN = (int) 8,
75 fixedDISPLAYCONFIG_OUTPUT_TECHNOLOGY_SDI = (int) 9,
76 fixedDISPLAYCONFIG_OUTPUT_TECHNOLOGY_DISPLAYPORT_EXTERNAL = (int) 10,
77 fixedDISPLAYCONFIG_OUTPUT_TECHNOLOGY_DISPLAYPORT_EMBEDDED = (int) 11,
78 fixedDISPLAYCONFIG_OUTPUT_TECHNOLOGY_UDI_EXTERNAL = (int) 12,
79 fixedDISPLAYCONFIG_OUTPUT_TECHNOLOGY_UDI_EMBEDDED = (int) 13,
80 fixedDISPLAYCONFIG_OUTPUT_TECHNOLOGY_SDTVDONGLE = (int) 14,
81 fixedDISPLAYCONFIG_OUTPUT_TECHNOLOGY_INTERNAL = (int) 0x80000000,
82 fixedDISPLAYCONFIG_OUTPUT_TECHNOLOGY_FORCE_UINT32 = (int) 0xFFFFFFFF
83 } fixedDISPLAYCONFIG_VIDEO_OUTPUT_TECHNOLOGY;
84
85 typedef struct fixedDISPLAYCONFIG_PATH_TARGET_INFO {
86 LUID adapterId;
87 UINT32 id;
88 UINT32 modeInfoIdx;
89 fixedDISPLAYCONFIG_VIDEO_OUTPUT_TECHNOLOGY outputTechnology;
90 DISPLAYCONFIG_ROTATION rotation;
91 DISPLAYCONFIG_SCALING scaling;
92 DISPLAYCONFIG_RATIONAL refreshRate;
93 DISPLAYCONFIG_SCANLINE_ORDERING scanLineOrdering;
94 BOOL targetAvailable;
95 UINT32 statusFlags;
96 } fixedDISPLAYCONFIG_PATH_TARGET_INFO;
97
98 typedef struct fixedDISPLAYCONFIG_PATH_INFO
99 {
100 DISPLAYCONFIG_PATH_SOURCE_INFO sourceInfo;
101 fixedDISPLAYCONFIG_PATH_TARGET_INFO targetInfo;
102 UINT32 flags;
103 } fixedDISPLAYCONFIG_PATH_INFO;
104
105 typedef struct fixedDISPLAYCONFIG_TARGET_DEVICE_NAME
106 {
107 DISPLAYCONFIG_DEVICE_INFO_HEADER header;
108 DISPLAYCONFIG_TARGET_DEVICE_NAME_FLAGS flags;
109 fixedDISPLAYCONFIG_VIDEO_OUTPUT_TECHNOLOGY outputTechnology;
110 UINT16 edidManufactureId;
111 UINT16 edidProductCodeId;
112 UINT32 connectorInstance;
113 WCHAR monitorFriendlyDeviceName[64];
114 WCHAR monitorDevicePath[128];
115 } fixedDISPLAYCONFIG_TARGET_DEVICE_NAME;
116
117 #endif
118
119
120 /* MinGW-w64 does not have these functions in its import libraries
121 * at the moment of writing.
122 * Also, Windows Vista doesn't have these functions at all
123 * (according to MSDN it does, but that is a lie), so we'd have
124 * to load them manually anyway (otherwise GTK apps won't even start
125 * on Vista).
126 */
127 typedef LONG
128 (WINAPI *funcGetDisplayConfigBufferSizes) (UINT32 flags,
129 UINT32* numPathArrayElements,
130 UINT32* numModeInfoArrayElements);
131
132 typedef LONG
133 (WINAPI *funcQueryDisplayConfig) (UINT32 flags,
134 UINT32* numPathArrayElements,
135 fixedDISPLAYCONFIG_PATH_INFO* pathArray,
136 UINT32* numModeInfoArrayElements,
137 DISPLAYCONFIG_MODE_INFO* modeInfoArray,
138 DISPLAYCONFIG_TOPOLOGY_ID* currentTopologyId);
139
140 typedef LONG
141 (WINAPI *funcDisplayConfigGetDeviceInfo) (DISPLAYCONFIG_DEVICE_INFO_HEADER* requestPacket);
142
143 #ifndef MONITORINFOF_PRIMARY
144 #define MONITORINFOF_PRIMARY 1
145 #endif
146
147 /* MinGW-w64 does not have a prototype for function in its headers
148 * at the moment of writing.
149 */
150 #if !defined (HAVE_SETUP_DI_GET_DEVICE_PROPERTY_W)
151 BOOL WINAPI SetupDiGetDevicePropertyW (HDEVINFO DeviceInfoSet,
152 PSP_DEVINFO_DATA DeviceInfoData,
153 const DEVPROPKEY *PropertyKey,
154 DEVPROPTYPE *PropertyType,
155 PBYTE PropertyBuffer,
156 DWORD PropertyBufferSize,
157 PDWORD RequiredSize,
158 DWORD Flags);
159 #endif
160
161 #define G_GUID_FORMAT "%08lX-%04X-%04X-%02X%02X-%02X%02X%02X%02X%02X%02X"
162 #define g_format_guid(guid) (guid)->Data1, \
163 (guid)->Data2, \
164 (guid)->Data3, \
165 (guid)->Data4[0], \
166 (guid)->Data4[1], \
167 (guid)->Data4[2], \
168 (guid)->Data4[3], \
169 (guid)->Data4[4], \
170 (guid)->Data4[5], \
171 (guid)->Data4[6], \
172 (guid)->Data4[7]
173
174 static gboolean
get_device_property(HDEVINFO device_infoset,SP_DEVINFO_DATA * device_info_data,DEVPROPKEY * property_key,gpointer * r_buffer,gsize * r_buffer_size,DEVPROPTYPE * r_property_type,GError ** error)175 get_device_property (HDEVINFO device_infoset,
176 SP_DEVINFO_DATA *device_info_data,
177 DEVPROPKEY *property_key,
178 gpointer *r_buffer,
179 gsize *r_buffer_size,
180 DEVPROPTYPE *r_property_type,
181 GError **error)
182 {
183 DEVPROPTYPE property_type;
184 gpointer property;
185 DWORD property_size;
186
187 property = NULL;
188 property_size = 0;
189
190 if (!SetupDiGetDevicePropertyW (device_infoset,
191 device_info_data,
192 property_key,
193 &property_type,
194 property,
195 property_size,
196 &property_size,
197 0))
198 {
199 DWORD error_code = GetLastError ();
200
201 if (error_code != ERROR_INSUFFICIENT_BUFFER)
202 {
203 gchar *emsg = g_win32_error_message (error_code);
204 g_warning ("Failed to get device node property {" G_GUID_FORMAT "},%lu size: %s",
205 g_format_guid (&property_key->fmtid),
206 property_key->pid,
207 emsg);
208 g_free (emsg);
209
210 return FALSE;
211 }
212 }
213
214 if (r_buffer)
215 {
216 property = g_malloc (property_size);
217
218 if (!SetupDiGetDevicePropertyW (device_infoset,
219 device_info_data,
220 property_key,
221 &property_type,
222 property,
223 property_size,
224 &property_size,
225 0))
226 {
227 DWORD error_code = GetLastError ();
228
229 gchar *emsg = g_win32_error_message (error_code);
230 g_warning ("Failed to get device node property {" G_GUID_FORMAT "},%lu: %s",
231 g_format_guid (&property_key->fmtid),
232 property_key->pid,
233 emsg);
234 g_free (emsg);
235
236 return FALSE;
237 }
238
239 *r_buffer = property;
240 }
241
242 if (r_buffer_size)
243 *r_buffer_size = property_size;
244
245 if (r_property_type)
246 *r_property_type = property_type;
247
248 return TRUE;
249 }
250
251 static GPtrArray *
get_monitor_devices(GdkWin32Display * win32_display)252 get_monitor_devices (GdkWin32Display *win32_display)
253 {
254 GPtrArray *monitor_array;
255 HDEVINFO device_infoset;
256 SP_DEVINFO_DATA device_info_data;
257 DWORD device_index;
258 GUID device_interface_monitor = {0xe6f07b5f, 0xee97, 0x4a90, {0xb0, 0x76, 0x33, 0xf5, 0x7b, 0xf4, 0xea, 0xa7}};
259 DEVPROPKEY pkey_device_instance_id = {{0x78C34FC8, 0x104A, 0x4ACA, {0x9E, 0xA4, 0x52, 0x4D, 0x52, 0x99, 0x6E, 0x57}}, 256};
260 DEVPROPKEY pkey_manufacturer = {{0xA45C254E, 0xDF1C, 0x4EFD, {0x80, 0x20, 0x67, 0xD1, 0x46, 0xA8, 0x50, 0xE0}}, 13};
261 DEVPROPKEY pkey_display_name = {{0xB725F130, 0x47EF, 0x101A, {0xA5, 0xF1, 0x02, 0x60, 0x8C, 0x9E, 0xEB, 0xAC}}, 10};
262
263 monitor_array = g_ptr_array_new_with_free_func (g_object_unref);
264
265 device_infoset = SetupDiGetClassDevs (&device_interface_monitor, 0, 0, DIGCF_DEVICEINTERFACE | DIGCF_PRESENT);
266
267 if (device_infoset == INVALID_HANDLE_VALUE)
268 return monitor_array;
269
270 for (device_index = 0; TRUE; device_index++)
271 {
272 gunichar2 *p;
273 gchar *instance_path;
274 gunichar2 *prop;
275 DWORD proptype;
276 HKEY device_registry_key;
277 GdkWin32Monitor *w32mon;
278 GdkMonitor *mon;
279 unsigned char *edid;
280 DWORD edid_size;
281 DWORD edid_type;
282
283 memset (&device_info_data, 0, sizeof (device_info_data));
284 device_info_data.cbSize = sizeof (device_info_data);
285
286 if (!SetupDiEnumDeviceInfo (device_infoset, device_index, &device_info_data))
287 {
288 DWORD error_code = GetLastError ();
289
290 if (error_code == ERROR_NO_MORE_ITEMS)
291 break;
292
293 g_warning ("SetupDiEnumDeviceInfo() failed: %lu\n", error_code);
294 break;
295 }
296
297 if (!get_device_property (device_infoset,
298 &device_info_data,
299 &pkey_device_instance_id,
300 (gpointer *) &prop,
301 NULL,
302 &proptype,
303 NULL))
304 continue;
305
306 if (proptype != DEVPROP_TYPE_STRING)
307 {
308 g_free (prop);
309 continue;
310 }
311
312 w32mon = g_object_new (GDK_TYPE_WIN32_MONITOR, "display", win32_display, NULL);
313 mon = GDK_MONITOR (w32mon);
314
315 g_ptr_array_add (monitor_array, w32mon);
316
317 /* Half-initialized monitors are candidates for removal */
318 w32mon->remove = TRUE;
319
320 /* device instance ID looks like: DISPLAY\FOO\X&XXXXXXX&X&UIDXXX */
321 for (p = prop; p[0]; p++)
322 if (p[0] == L'\\')
323 p[0] = L'#';
324 /* now device instance ID looks like: DISPLAY#FOO#X&XXXXXXX&X&UIDXXX */
325
326 /* instance path looks like: \\?\DISPLAY#FOO#X&XXXXXXX&X&UIDXXX#{e6f07b5f-ee97-4a90-b076-33f57bf4eaa7} */
327 instance_path = g_strdup_printf ("\\\\?\\%ls#{" G_GUID_FORMAT "}",
328 prop,
329 g_format_guid (&device_interface_monitor));
330 w32mon->instance_path = g_utf8_strdown (instance_path, -1);
331 g_free (instance_path);
332 g_free (prop);
333
334 if (get_device_property (device_infoset,
335 &device_info_data,
336 &pkey_manufacturer,
337 (gpointer *) &prop,
338 NULL, &proptype, NULL))
339 {
340 if (proptype == DEVPROP_TYPE_STRING)
341 {
342 gchar *manufacturer = g_utf16_to_utf8 (prop, -1, NULL, NULL, NULL);
343 gdk_monitor_set_manufacturer (mon, manufacturer);
344 g_free (manufacturer);
345 }
346
347 g_free (prop);
348 }
349
350 if (get_device_property (device_infoset,
351 &device_info_data,
352 &pkey_display_name,
353 (gpointer *) &prop,
354 NULL, &proptype, NULL))
355 {
356 if (proptype == DEVPROP_TYPE_STRING)
357 {
358 gchar *name = g_utf16_to_utf8 (prop, -1, NULL, NULL, NULL);
359 gdk_monitor_set_model (mon, name);
360 g_free (name);
361 }
362
363 g_free (prop);
364 }
365
366 device_registry_key = SetupDiOpenDevRegKey (device_infoset, &device_info_data, DICS_FLAG_GLOBAL, 0, DIREG_DEV, KEY_READ);
367
368 if (device_registry_key == NULL || device_registry_key == INVALID_HANDLE_VALUE)
369 continue;
370
371 edid = NULL;
372 edid_size = 0;
373
374
375 if (RegQueryValueExW (device_registry_key, L"EDID",
376 NULL, &edid_type,
377 edid, &edid_size) == ERROR_SUCCESS)
378 {
379 edid = g_malloc (edid_size);
380
381 if (RegQueryValueExW (device_registry_key, L"EDID",
382 NULL, &edid_type,
383 edid, &edid_size) == ERROR_SUCCESS)
384 {
385 gdk_monitor_set_physical_size (mon,
386 ((edid[68] & 0x00F0) << 4) + edid[66],
387 ((edid[68] & 0x000F) << 8) + edid[67]);
388 }
389
390 g_free (edid);
391 }
392
393 RegCloseKey (device_registry_key);
394 }
395
396 SetupDiDestroyDeviceInfoList (device_infoset);
397
398 return monitor_array;
399 }
400
401 static void
populate_monitor_devices_from_display_config(GPtrArray * monitors)402 populate_monitor_devices_from_display_config (GPtrArray *monitors)
403 {
404 HMODULE user32;
405 LONG return_code;
406 funcGetDisplayConfigBufferSizes getDisplayConfigBufferSizes;
407 funcQueryDisplayConfig queryDisplayConfig;
408 funcDisplayConfigGetDeviceInfo displayConfigGetDeviceInfo;
409 UINT32 dispconf_mode_count;
410 UINT32 dispconf_path_count;
411 fixedDISPLAYCONFIG_PATH_INFO *dispconf_paths;
412 DISPLAYCONFIG_MODE_INFO *dispconf_modes;
413 gint path_index;
414
415 user32 = LoadLibraryA ("user32.dll");
416
417 if (user32 == NULL)
418 return;
419
420 getDisplayConfigBufferSizes = (funcGetDisplayConfigBufferSizes) GetProcAddress (user32,
421 "GetDisplayConfigBufferSizes");
422 queryDisplayConfig = (funcQueryDisplayConfig) GetProcAddress (user32,
423 "QueryDisplayConfig");
424 displayConfigGetDeviceInfo = (funcDisplayConfigGetDeviceInfo) GetProcAddress (user32,
425 "DisplayConfigGetDeviceInfo");
426 if (getDisplayConfigBufferSizes == NULL ||
427 queryDisplayConfig == NULL ||
428 displayConfigGetDeviceInfo == NULL)
429 {
430 /* This does happen on Windows Vista, so don't warn about this */
431 FreeLibrary (user32);
432
433 return;
434 }
435
436 return_code = getDisplayConfigBufferSizes (QDC_ONLY_ACTIVE_PATHS,
437 &dispconf_path_count,
438 &dispconf_mode_count);
439
440 if (return_code != ERROR_SUCCESS)
441 {
442 g_warning ("Can't get displayconfig buffer size: 0x%lx\n", return_code);
443 FreeLibrary (user32);
444
445 return;
446 }
447
448 dispconf_paths = g_new (fixedDISPLAYCONFIG_PATH_INFO, dispconf_path_count);
449 dispconf_modes = g_new (DISPLAYCONFIG_MODE_INFO, dispconf_mode_count);
450
451 return_code = queryDisplayConfig (QDC_ONLY_ACTIVE_PATHS,
452 &dispconf_path_count,
453 dispconf_paths,
454 &dispconf_mode_count,
455 dispconf_modes,
456 NULL);
457
458 if (return_code != ERROR_SUCCESS)
459 {
460 g_free (dispconf_paths);
461 g_free (dispconf_modes);
462 FreeLibrary (user32);
463
464 return;
465 }
466
467 for (path_index = 0; path_index < dispconf_path_count; path_index++)
468 {
469 fixedDISPLAYCONFIG_TARGET_DEVICE_NAME tdn;
470 gint i;
471 GdkWin32Monitor *w32mon;
472 GdkMonitor *mon;
473 gchar *path, *path_lower;
474 DISPLAYCONFIG_RATIONAL *refresh;
475
476 if ((dispconf_paths[path_index].flags & DISPLAYCONFIG_PATH_ACTIVE) == 0)
477 continue;
478
479 tdn.header.type = DISPLAYCONFIG_DEVICE_INFO_GET_TARGET_NAME;
480 tdn.header.size = sizeof (tdn);
481 tdn.header.adapterId = dispconf_paths[path_index].targetInfo.adapterId;
482 tdn.header.id = dispconf_paths[path_index].targetInfo.id;
483
484 return_code = displayConfigGetDeviceInfo (&tdn.header);
485
486 if (return_code != ERROR_SUCCESS)
487 continue;
488
489 path = g_utf16_to_utf8 (tdn.monitorDevicePath, -1, NULL, NULL, NULL);
490
491 if (path == NULL)
492 continue;
493
494 path_lower = g_utf8_strdown (path, -1);
495
496 g_free (path);
497
498 for (i = 0, w32mon = NULL; i < monitors->len; i++)
499 {
500 GdkWin32Monitor *m = g_ptr_array_index (monitors, i);
501
502 if (g_strcmp0 (m->instance_path, path_lower) != 0)
503 continue;
504
505 w32mon = m;
506 break;
507 }
508
509 g_free (path_lower);
510
511 if (w32mon == NULL)
512 continue;
513
514 mon = GDK_MONITOR (w32mon);
515
516 if (!tdn.flags.friendlyNameForced)
517 {
518 /* monitorFriendlyDeviceName is usually nicer */
519 gchar *name = g_utf16_to_utf8 (tdn.monitorFriendlyDeviceName, -1, NULL, NULL, NULL);
520 gdk_monitor_set_model (mon, name);
521 g_free (name);
522 }
523
524 refresh = &dispconf_paths[path_index].targetInfo.refreshRate;
525 gdk_monitor_set_refresh_rate (mon,
526 refresh->Numerator * (UINT64) 1000 / refresh->Denominator);
527 }
528
529 g_free (dispconf_paths);
530 g_free (dispconf_modes);
531
532 FreeLibrary (user32);
533 }
534
535 typedef struct {
536 GPtrArray *monitors;
537 gboolean have_monitor_devices;
538 GdkWin32Display *display;
539 } EnumMonitorData;
540
541 static BOOL CALLBACK
enum_monitor(HMONITOR hmonitor,HDC hdc,LPRECT rect,LPARAM param)542 enum_monitor (HMONITOR hmonitor,
543 HDC hdc,
544 LPRECT rect,
545 LPARAM param)
546 {
547 EnumMonitorData *data = (EnumMonitorData *) param;
548 MONITORINFOEXW monitor_info;
549 DWORD i_adapter;
550
551 /* Grab monitor_info for this logical monitor */
552 monitor_info.cbSize = sizeof (MONITORINFOEXW);
553 GetMonitorInfoW (hmonitor, (MONITORINFO *) &monitor_info);
554
555 /* Sidestep to enumerate display adapters */
556 for (i_adapter = 0; TRUE; i_adapter++)
557 {
558 DISPLAY_DEVICEW dd;
559 DEVMODEW dm;
560 DWORD i_monitor;
561 DWORD frequency;
562 GdkWin32MonitorRotation orientation;
563
564 memset (&dd, 0, sizeof (dd));
565 dd.cb = sizeof (dd);
566
567 /* Get i_adapter'th adapter */
568 if (!EnumDisplayDevicesW (NULL, i_adapter, &dd, EDD_GET_DEVICE_INTERFACE_NAME))
569 break;
570
571 if ((dd.StateFlags & DISPLAY_DEVICE_ATTACHED_TO_DESKTOP) == 0)
572 continue;
573
574 /* Match this display adapter to one for which we've got monitor_info
575 * (logical monitor == adapter)
576 */
577 if (wcscmp (dd.DeviceName, monitor_info.szDevice) != 0)
578 continue;
579
580 dm.dmSize = sizeof (dm);
581 dm.dmDriverExtra = 0;
582 frequency = 0;
583 orientation = GDK_WIN32_MONITOR_ROTATION_UNKNOWN;
584
585 /* Grab refresh rate for this adapter while we're at it */
586 if (EnumDisplaySettingsExW (dd.DeviceName, ENUM_CURRENT_SETTINGS, &dm, 0))
587 {
588 if ((dm.dmFields & DM_DISPLAYORIENTATION) == DM_DISPLAYORIENTATION)
589 {
590 switch (dm.dmDisplayOrientation)
591 {
592 default:
593 case DMDO_DEFAULT:
594 orientation = GDK_WIN32_MONITOR_ROTATION_0;
595 break;
596 case DMDO_90:
597 orientation = GDK_WIN32_MONITOR_ROTATION_90;
598 break;
599 case DMDO_180:
600 orientation = GDK_WIN32_MONITOR_ROTATION_180;
601 break;
602 case DMDO_270:
603 orientation = GDK_WIN32_MONITOR_ROTATION_270;
604 break;
605 }
606 }
607
608 if ((dm.dmFields & DM_DISPLAYFREQUENCY) == DM_DISPLAYFREQUENCY)
609 frequency = dm.dmDisplayFrequency;
610 }
611
612 /* Enumerate monitors connected to this display adapter */
613 for (i_monitor = 0; TRUE; i_monitor++)
614 {
615 DISPLAY_DEVICEW dd_monitor;
616 gchar *device_id_lower, *tmp;
617 DWORD i;
618 GdkWin32Monitor *w32mon;
619 GdkMonitor *mon;
620 GdkRectangle rect;
621 int scale;
622
623 memset (&dd_monitor, 0, sizeof (dd_monitor));
624 dd_monitor.cb = sizeof (dd_monitor);
625
626 if (data->have_monitor_devices)
627 {
628 /* Get i_monitor'th monitor */
629 if (!EnumDisplayDevicesW (dd.DeviceName, i_monitor, &dd_monitor, EDD_GET_DEVICE_INTERFACE_NAME))
630 break;
631
632 tmp = g_utf16_to_utf8 (dd_monitor.DeviceID, -1, NULL, NULL, NULL);
633
634 if (tmp == NULL)
635 continue;
636
637 device_id_lower = g_utf8_strdown (tmp, -1);
638 g_free (tmp);
639
640 /* Match this monitor to one of the monitor devices we found earlier */
641 for (i = 0, w32mon = NULL; i < data->monitors->len; i++)
642 {
643 GdkWin32Monitor *m = g_ptr_array_index (data->monitors, i);
644
645 if (g_strcmp0 (device_id_lower, m->instance_path) != 0)
646 continue;
647
648 w32mon = m;
649 break;
650 }
651
652 g_free (device_id_lower);
653
654 if (w32mon == NULL)
655 continue;
656 }
657 else
658 {
659 /* Headless PC or a virtual machine, it has no monitor devices.
660 * Make one up.
661 */
662 w32mon = g_object_new (GDK_TYPE_WIN32_MONITOR, "display", data->display, NULL);
663 g_ptr_array_add (data->monitors, w32mon);
664 i = data->monitors->len - 1;
665 w32mon->madeup = TRUE;
666 }
667
668 mon = GDK_MONITOR (w32mon);
669
670 if (gdk_monitor_get_model (mon) == NULL)
671 {
672
673 gchar *name = NULL;
674
675 /* Only use dd.DeviceName as a last resort, as it is just
676 * \\.\DISPLAYX\MonitorY (for some values of X and Y).
677 */
678 if (dd_monitor.DeviceName[0] != L'\0')
679 name = g_utf16_to_utf8 (dd_monitor.DeviceName, -1, NULL, NULL, NULL);
680 else if (dd.DeviceName[0] != L'\0')
681 name = g_utf16_to_utf8 (dd.DeviceName, -1, NULL, NULL, NULL);
682
683 if (name != NULL)
684 gdk_monitor_set_model (mon, name);
685
686 g_free (name);
687 }
688
689 /* GetDeviceCaps seems to provide a wild guess, prefer more precise EDID info */
690 if (gdk_monitor_get_width_mm (mon) == 0 &&
691 gdk_monitor_get_height_mm (mon) == 0)
692 {
693 HDC hDC = CreateDCW (L"DISPLAY", monitor_info.szDevice, NULL, NULL);
694
695 gdk_monitor_set_physical_size (mon,
696 GetDeviceCaps (hDC, HORZSIZE),
697 GetDeviceCaps (hDC, VERTSIZE));
698 DeleteDC (hDC);
699 }
700
701 /* frequency is in Hz and is unsigned long,
702 * prefer more precise refresh_rate found earlier,
703 * which comes as a Numerator & Denominator pair and is more precise.
704 */
705 if (gdk_monitor_get_refresh_rate (mon) == 0)
706 gdk_monitor_set_refresh_rate (mon, frequency * 1000);
707
708 /* This is the reason this function exists. This data is not available
709 * via other functions.
710 */
711 rect.x = monitor_info.rcWork.left;
712 rect.y = monitor_info.rcWork.top;
713 rect.width = (monitor_info.rcWork.right - monitor_info.rcWork.left);
714 rect.height = (monitor_info.rcWork.bottom - monitor_info.rcWork.top);
715 /* This is temporary, scale will be applied below */
716 w32mon->work_rect = rect;
717
718 if (data->display->has_fixed_scale)
719 scale = data->display->window_scale;
720 else
721 {
722 /* First acquire the scale using the current screen */
723 scale = _gdk_win32_display_get_monitor_scale_factor (data->display, NULL, NULL, NULL);
724
725 /* acquire the scale using the monitor which the window is nearest on Windows 8.1+ */
726 if (data->display->have_at_least_win81)
727 {
728 HMONITOR hmonitor;
729 POINT pt;
730
731 /* Not subtracting _gdk_offset_x and _gdk_offset_y because they will only
732 * be added later on, in _gdk_win32_display_get_monitor_list().
733 */
734 pt.x = w32mon->work_rect.x + w32mon->work_rect.width / 2;
735 pt.y = w32mon->work_rect.y + w32mon->work_rect.height / 2;
736 hmonitor = MonitorFromPoint (pt, MONITOR_DEFAULTTONEAREST);
737 scale = _gdk_win32_display_get_monitor_scale_factor (data->display, hmonitor, NULL, NULL);
738 }
739 }
740
741 gdk_monitor_set_scale_factor (mon, scale);
742 /* Now apply the scale to the work rectangle */
743 w32mon->work_rect.x /= scale;
744 w32mon->work_rect.y /= scale;
745 w32mon->work_rect.width /= scale;
746 w32mon->work_rect.height /= scale;
747
748 rect.x = monitor_info.rcMonitor.left / scale;
749 rect.y = monitor_info.rcMonitor.top / scale;
750 rect.width = (monitor_info.rcMonitor.right - monitor_info.rcMonitor.left) / scale;
751 rect.height = (monitor_info.rcMonitor.bottom - monitor_info.rcMonitor.top) / scale;
752 gdk_monitor_set_position (mon, rect.x, rect.y);
753 gdk_monitor_set_size (mon, rect.width, rect.height);
754
755 if (monitor_info.dwFlags & MONITORINFOF_PRIMARY && i != 0)
756 {
757 /* Put primary monitor at index 0, just in case somebody needs
758 * to know which one is the primary.
759 */
760 GdkWin32Monitor *temp = g_ptr_array_index (data->monitors, 0);
761 g_ptr_array_index (data->monitors, 0) = w32mon;
762 g_ptr_array_index (data->monitors, i) = temp;
763 }
764
765 /* Work area is the most important component, actively used by GTK,
766 * but our initial list of monitor devices did not have it.
767 * Any monitor devices not matched in this functions will have
768 * 0-filled work area and will therefore be useless, so let them
769 * keep remove == TRUE and be removed further up the stack.
770 */
771 w32mon->remove = FALSE;
772 w32mon->orientation = orientation;
773
774 /* One virtual monitor per display adapter */
775 if (w32mon->madeup)
776 break;
777 }
778 }
779
780 return TRUE;
781 }
782
783 static void
prune_monitors(EnumMonitorData * data)784 prune_monitors (EnumMonitorData *data)
785 {
786 gint i;
787
788 for (i = 0; i < data->monitors->len; i++)
789 {
790 GdkWin32Monitor *m;
791
792 m = g_ptr_array_index (data->monitors, i);
793
794 if (m->remove)
795 g_ptr_array_remove_index (data->monitors, i--);
796 }
797 }
798
799 const gchar *
_gdk_win32_monitor_get_pixel_structure(GdkMonitor * monitor)800 _gdk_win32_monitor_get_pixel_structure (GdkMonitor *monitor)
801 {
802 GdkWin32Monitor *w32_m;
803 BOOL enabled = TRUE;
804 unsigned int smoothing_orientation = FE_FONTSMOOTHINGORIENTATIONRGB;
805 UINT cleartype = FE_FONTSMOOTHINGCLEARTYPE;
806
807 g_return_val_if_fail (monitor != NULL, NULL);
808
809 w32_m = GDK_WIN32_MONITOR (monitor);
810
811 SystemParametersInfoW (SPI_GETFONTSMOOTHING, 0, &enabled, 0);
812 SystemParametersInfoW (SPI_GETFONTSMOOTHINGTYPE, 0, &cleartype, 0);
813
814 if (!enabled ||
815 (cleartype == FE_FONTSMOOTHINGSTANDARD) ||
816 !SystemParametersInfoW (SPI_GETFONTSMOOTHINGORIENTATION, 0, &smoothing_orientation, 0))
817 return "none";
818
819 if (smoothing_orientation == FE_FONTSMOOTHINGORIENTATIONBGR)
820 switch (w32_m->orientation)
821 {
822 default:
823 case GDK_WIN32_MONITOR_ROTATION_UNKNOWN:
824 return "none";
825 case GDK_WIN32_MONITOR_ROTATION_0:
826 return "bgr";
827 case GDK_WIN32_MONITOR_ROTATION_90:
828 return "vbgr";
829 case GDK_WIN32_MONITOR_ROTATION_180:
830 return "rgb";
831 case GDK_WIN32_MONITOR_ROTATION_270:
832 return "vrgb";
833 }
834 else
835 switch (w32_m->orientation)
836 {
837 default:
838 case GDK_WIN32_MONITOR_ROTATION_UNKNOWN:
839 return "none";
840 case GDK_WIN32_MONITOR_ROTATION_0:
841 return "rgb";
842 case GDK_WIN32_MONITOR_ROTATION_90:
843 return "vrgb";
844 case GDK_WIN32_MONITOR_ROTATION_180:
845 return "bgr";
846 case GDK_WIN32_MONITOR_ROTATION_270:
847 return "vbgr";
848 }
849 }
850
851 GPtrArray *
_gdk_win32_display_get_monitor_list(GdkWin32Display * win32_display)852 _gdk_win32_display_get_monitor_list (GdkWin32Display *win32_display)
853 {
854 EnumMonitorData data;
855 gint i;
856
857 data.display = win32_display;
858 data.monitors = get_monitor_devices (win32_display);
859
860 if (data.monitors->len != 0)
861 {
862 populate_monitor_devices_from_display_config (data.monitors);
863 data.have_monitor_devices = TRUE;
864 }
865 else
866 {
867 data.have_monitor_devices = FALSE;
868 }
869
870 EnumDisplayMonitors (NULL, NULL, enum_monitor, (LPARAM) &data);
871
872 prune_monitors (&data);
873
874 if (data.monitors->len == 0 && data.have_monitor_devices)
875 {
876 /* We thought we had monitors, but enumeration eventually failed, and
877 * we have none. Try again, this time making stuff up as we go.
878 */
879 data.have_monitor_devices = FALSE;
880 EnumDisplayMonitors (NULL, NULL, enum_monitor, (LPARAM) &data);
881 prune_monitors (&data);
882 }
883
884 _gdk_offset_x = G_MININT;
885 _gdk_offset_y = G_MININT;
886
887 for (i = 0; i < data.monitors->len; i++)
888 {
889 GdkWin32Monitor *m;
890 GdkRectangle rect;
891 int scale;
892
893 m = g_ptr_array_index (data.monitors, i);
894 gdk_monitor_get_geometry (GDK_MONITOR (m), &rect);
895 scale = gdk_monitor_get_scale_factor (GDK_MONITOR (m));
896
897 /* Calculate offset */
898 _gdk_offset_x = MAX (_gdk_offset_x, -rect.x * scale);
899 _gdk_offset_y = MAX (_gdk_offset_y, -rect.y * scale);
900 }
901
902 GDK_NOTE (MISC, g_print ("Multi-monitor offset: (%d,%d)\n",
903 _gdk_offset_x, _gdk_offset_y));
904
905 /* Translate monitor coords into GDK coordinate space */
906 for (i = 0; i < data.monitors->len; i++)
907 {
908 GdkWin32Monitor *m;
909 GdkRectangle rect;
910 int scale = 0;
911
912 m = g_ptr_array_index (data.monitors, i);
913
914 gdk_monitor_get_geometry (GDK_MONITOR (m), &rect);
915 scale = gdk_monitor_get_scale_factor (GDK_MONITOR (m));
916
917 rect.x += _gdk_offset_x / scale;
918 rect.y += _gdk_offset_y / scale;
919 gdk_monitor_set_position (GDK_MONITOR (m), rect.x, rect.y);
920
921 m->work_rect.x += _gdk_offset_x / scale;
922 m->work_rect.y += _gdk_offset_y / scale;
923
924 GDK_NOTE (MISC, g_print ("Monitor %d: %dx%d@%+d%+d\n", i,
925 rect.width, rect.height, rect.x, rect.y));
926 }
927
928 return data.monitors;
929 }
930
931 static void
gdk_win32_monitor_finalize(GObject * object)932 gdk_win32_monitor_finalize (GObject *object)
933 {
934 GdkWin32Monitor *win32_monitor = GDK_WIN32_MONITOR (object);
935
936 g_free (win32_monitor->instance_path);
937
938 G_OBJECT_CLASS (gdk_win32_monitor_parent_class)->finalize (object);
939 }
940
941 int
_gdk_win32_monitor_compare(GdkWin32Monitor * a,GdkWin32Monitor * b)942 _gdk_win32_monitor_compare (GdkWin32Monitor *a,
943 GdkWin32Monitor *b)
944 {
945 if (a->instance_path != NULL &&
946 b->instance_path != NULL)
947 return g_strcmp0 (a->instance_path, b->instance_path);
948
949 return a == b ? 0 : a < b ? -1 : 1;
950 }
951
952 static void
gdk_win32_monitor_get_workarea(GdkMonitor * monitor,GdkRectangle * dest)953 gdk_win32_monitor_get_workarea (GdkMonitor *monitor,
954 GdkRectangle *dest)
955 {
956 GdkWin32Monitor *win32_monitor = GDK_WIN32_MONITOR (monitor);
957
958 *dest = win32_monitor->work_rect;
959 }
960
961 static void
gdk_win32_monitor_init(GdkWin32Monitor * monitor)962 gdk_win32_monitor_init (GdkWin32Monitor *monitor)
963 {
964 }
965
966 static void
gdk_win32_monitor_class_init(GdkWin32MonitorClass * class)967 gdk_win32_monitor_class_init (GdkWin32MonitorClass *class)
968 {
969 G_OBJECT_CLASS (class)->finalize = gdk_win32_monitor_finalize;
970
971 GDK_MONITOR_CLASS (class)->get_workarea = gdk_win32_monitor_get_workarea;
972 }
973