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