[gtk+/wip/matthiasc/monitor] Implement GdkWin32Monitor subclass of GdkMonitor



commit a4a5a4807f16fd31ebbe776e3227342ffa2ff153
Author: Руслан Ижбулатов <lrn1986 gmail com>
Date:   Wed Apr 20 07:36:00 2016 +0000

    Implement GdkWin32Monitor subclass of GdkMonitor

 configure.ac                 |   50 +++-
 gdk/win32/Makefile.am        |    8 +-
 gdk/win32/gdkdisplay-win32.c |  209 +++++++++++
 gdk/win32/gdkdisplay-win32.h |    6 +
 gdk/win32/gdkmonitor-win32.c |  833 ++++++++++++++++++++++++++++++++++++++++++
 gdk/win32/gdkmonitor-win32.h |   57 +++
 gdk/win32/gdkscreen-win32.c  |  283 ++------------
 gdk/win32/gdkwin32.h         |    1 +
 gdk/win32/gdkwin32monitor.h  |   50 +++
 9 files changed, 1251 insertions(+), 246 deletions(-)
---
diff --git a/configure.ac b/configure.ac
index 1a7a910..e75140d 100644
--- a/configure.ac
+++ b/configure.ac
@@ -385,7 +385,7 @@ if test "$enable_win32_backend" = "yes"; then
   backend_immodules="$backend_immodules,ime"
   GDK_WINDOWING="$GDK_WINDOWING
 #define GDK_WINDOWING_WIN32"
-  GDK_EXTRA_LIBS="$GDK_EXTRA_LIBS -lgdi32 -limm32 -lshell32 -lole32 -Wl,-luuid -lwinmm -ldwmapi"
+  GDK_EXTRA_LIBS="$GDK_EXTRA_LIBS -lgdi32 -limm32 -lshell32 -lole32 -Wl,-luuid -lwinmm -ldwmapi -lsetupapi 
-lcfgmgr32"
   AM_CONDITIONAL(USE_WIN32, true)
   PANGO_PACKAGES="pangowin32 pangocairo"
 else
@@ -756,6 +756,54 @@ case $host in
   ;;
 esac
 
+AS_CASE([$host_os],
+  [mingw*],
+  [
+    AC_CHECK_SIZEOF(
+      [DISPLAYCONFIG_VIDEO_OUTPUT_TECHNOLOGY],
+      [],
+      [
+#define _WIN32_WINNT 0x601
+#include <windows.h>
+      ]
+    )
+    AS_IF(
+      [test x$ac_cv_sizeof_DISPLAYCONFIG_VIDEO_OUTPUT_TECHNOLOGY = x4],
+      [AC_MSG_RESULT([DISPLAYCONFIG_VIDEO_OUTPUT_TECHNOLOGY is OK])],
+      [test x$ac_cv_sizeof_DISPLAYCONFIG_VIDEO_OUTPUT_TECHNOLOGY = x0],
+      [AC_MSG_ERROR([DISPLAYCONFIG_VIDEO_OUTPUT_TECHNOLOGY is unavailable])],
+      [AC_MSG_RESULT([DISPLAYCONFIG_VIDEO_OUTPUT_TECHNOLOGY is not OK])]
+    )
+    AC_MSG_CHECKING([for SetupDiGetDevicePropertyW])
+    gtk_save_LIBS="$LIBS"
+    LIBS="-lsetupapi $LIBS"
+    AC_TRY_LINK(
+      [
+#define _WIN32_WINNT 0x0600
+#include <windows.h>
+#include <devpropdef.h>
+#include <setupapi.h>
+      ],
+      [return SetupDiGetDevicePropertyW(NULL, NULL, NULL, NULL, NULL, 0, NULL, 0);],
+      [have_SetupDiGetDevicePropertyW=yes],
+      [have_SetupDiGetDevicePropertyW=no]
+    )
+    AS_IF(
+      [test x$have_SetupDiGetDevicePropertyW = xyes],
+      [
+       AC_DEFINE(
+         [HAVE_SETUP_DI_GET_DEVICE_PROPERTY_W],
+         [1],
+         [Define to 1 if SetupDiGetDevicePropertyW() is available]
+       )
+      ]
+    )
+    AC_MSG_RESULT([$have_SetupDiGetDevicePropertyW])
+    LIBS="$gtk_save_LIBS"
+  ],
+  []
+)
+
 AC_SUBST(MATH_LIB)
 #
 # see bug 162979
diff --git a/gdk/win32/Makefile.am b/gdk/win32/Makefile.am
index 33a0d61..5b3af48 100644
--- a/gdk/win32/Makefile.am
+++ b/gdk/win32/Makefile.am
@@ -47,6 +47,8 @@ libgdk_win32_la_SOURCES = \
        gdkglobals-win32.c \
        gdkkeys-win32.c \
        gdkmain-win32.c \
+       gdkmonitor-win32.c \
+       gdkmonitor-win32.h \
        gdkprivate-win32.h \
        gdkproperty-win32.c \
        gdkscreen-win32.c \
@@ -56,10 +58,11 @@ libgdk_win32_la_SOURCES = \
        gdkwin32display.h \
        gdkwin32displaymanager.h \
        gdkwin32dnd.h \
-       gdkwin32glcontext.h             \
+       gdkwin32glcontext.h \
        gdkwin32.h \
        gdkwin32id.c \
        gdkwin32keys.h \
+       gdkwin32monitor.h \
        gdkwin32screen.h \
        gdkwin32window.h \
        gdkwindow-win32.c \
@@ -76,9 +79,10 @@ libgdkwin32include_HEADERS = \
        gdkwin32display.h       \
        gdkwin32displaymanager.h\
        gdkwin32dnd.h           \
-       gdkwin32glcontext.h             \
+       gdkwin32glcontext.h     \
        gdkwin32keys.h          \
        gdkwin32misc.h          \
+       gdkwin32monitor.h       \
        gdkwin32screen.h        \
        gdkwin32window.h
 
diff --git a/gdk/win32/gdkdisplay-win32.c b/gdk/win32/gdkdisplay-win32.c
index ce1c5bc..f66e169 100644
--- a/gdk/win32/gdkdisplay-win32.c
+++ b/gdk/win32/gdkdisplay-win32.c
@@ -25,10 +25,178 @@
 #include "gdkwin32display.h"
 #include "gdkwin32screen.h"
 #include "gdkwin32window.h"
+#include "gdkmonitor-win32.h"
 #include "gdkwin32.h"
 
 static int debug_indent = 0;
 
+static GdkMonitor *
+_gdk_win32_display_find_matching_monitor (GdkWin32Display *win32_display,
+                                          GdkMonitor      *needle)
+{
+  int i;
+
+  for (i = 0; i < win32_display->monitors->len; i++)
+    {
+      GdkWin32Monitor *m;
+
+      m = GDK_WIN32_MONITOR (g_ptr_array_index (win32_display->monitors, i));
+
+      if (_gdk_win32_monitor_compare (m, GDK_WIN32_MONITOR (needle)) == 0)
+        return GDK_MONITOR (m);
+    }
+
+  return NULL;
+}
+
+gboolean
+_gdk_win32_display_init_monitors (GdkWin32Display *win32_display)
+{
+  GdkDisplay *display = GDK_DISPLAY (win32_display);
+  GPtrArray *new_monitors;
+  gint i;
+  gboolean changed = FALSE;
+  GdkWin32Monitor *primary_to_move = NULL;
+
+  for (i = 0; i < win32_display->monitors->len; i++)
+    GDK_WIN32_MONITOR (g_ptr_array_index (win32_display->monitors, i))->remove = TRUE;
+
+  new_monitors = _gdk_win32_display_get_monitor_list (win32_display);
+
+  for (i = 0; i < new_monitors->len; i++)
+    {
+      GdkWin32Monitor *w32_m;
+      GdkMonitor *m;
+      GdkWin32Monitor *w32_ex_monitor;
+      GdkMonitor *ex_monitor;
+      GdkRectangle geometry, ex_geometry;
+      GdkRectangle workarea, ex_workarea;
+
+      w32_m = GDK_WIN32_MONITOR (g_ptr_array_index (new_monitors, i));
+      m = GDK_MONITOR (w32_m);
+      ex_monitor = _gdk_win32_display_find_matching_monitor (win32_display, m);
+      w32_ex_monitor = GDK_WIN32_MONITOR (ex_monitor);
+
+      if (ex_monitor == NULL)
+        {
+          w32_m->add = TRUE;
+          changed = TRUE;
+          continue;
+        }
+
+      w32_ex_monitor->remove = FALSE;
+
+      if (i == 0)
+        primary_to_move = w32_ex_monitor;
+
+      gdk_monitor_get_geometry (m, &geometry);
+      gdk_monitor_get_geometry (ex_monitor, &ex_geometry);
+      gdk_monitor_get_workarea (m, &workarea);
+      gdk_monitor_get_workarea (ex_monitor, &ex_workarea);
+
+      if (memcmp (&workarea, &ex_workarea, sizeof (GdkRectangle)) != 0)
+        {
+          w32_ex_monitor->work_rect = workarea;
+          changed = TRUE;
+        }
+
+      if (memcmp (&geometry, &ex_geometry, sizeof (GdkRectangle)) != 0)
+        {
+          gdk_monitor_set_size (ex_monitor, geometry.width, geometry.height);
+          gdk_monitor_set_position (ex_monitor, geometry.x, geometry.y);
+          changed = TRUE;
+        }
+
+      if (gdk_monitor_get_width_mm (m) != gdk_monitor_get_width_mm (ex_monitor) ||
+          gdk_monitor_get_height_mm (m) != gdk_monitor_get_height_mm (ex_monitor))
+        {
+          gdk_monitor_set_physical_size (ex_monitor,
+                                         gdk_monitor_get_width_mm (m),
+                                         gdk_monitor_get_height_mm (m));
+          changed = TRUE;
+        }
+
+      if (g_strcmp0 (gdk_monitor_get_model (m), gdk_monitor_get_model (ex_monitor)) != 0)
+        {
+          gdk_monitor_set_model (ex_monitor,
+                                 gdk_monitor_get_model (m));
+          changed = TRUE;
+        }
+
+      if (g_strcmp0 (gdk_monitor_get_manufacturer (m), gdk_monitor_get_manufacturer (ex_monitor)) != 0)
+        {
+          gdk_monitor_set_manufacturer (ex_monitor,
+                                        gdk_monitor_get_manufacturer (m));
+          changed = TRUE;
+        }
+
+      if (gdk_monitor_get_refresh_rate (m) != gdk_monitor_get_refresh_rate (ex_monitor))
+        {
+          gdk_monitor_set_refresh_rate (ex_monitor, gdk_monitor_get_refresh_rate (m));
+          changed = TRUE;
+        }
+
+      if (gdk_monitor_get_scale_factor (m) != gdk_monitor_get_scale_factor (ex_monitor))
+        {
+          gdk_monitor_set_scale_factor (ex_monitor, gdk_monitor_get_scale_factor (m));
+          changed = TRUE;
+        }
+
+      if (gdk_monitor_get_subpixel_layout (m) != gdk_monitor_get_subpixel_layout (ex_monitor))
+        {
+          gdk_monitor_set_subpixel_layout (ex_monitor, gdk_monitor_get_subpixel_layout (m));
+          changed = TRUE;
+        }
+    }
+
+  for (i = win32_display->monitors->len - 1; i >= 0; i--)
+    {
+      GdkWin32Monitor *w32_ex_monitor;
+      GdkMonitor *ex_monitor;
+
+      w32_ex_monitor = GDK_WIN32_MONITOR (g_ptr_array_index (win32_display->monitors, i));
+      ex_monitor = GDK_MONITOR (w32_ex_monitor);
+
+      if (!w32_ex_monitor->remove)
+        continue;
+
+      changed = TRUE;
+      gdk_display_monitor_removed (display, ex_monitor);
+      g_ptr_array_remove_index (win32_display->monitors, i);
+    }
+
+  for (i = 0; i < new_monitors->len; i++)
+    {
+      GdkWin32Monitor *w32_m;
+      GdkMonitor *m;
+
+      w32_m = GDK_WIN32_MONITOR (g_ptr_array_index (new_monitors, i));
+      m = GDK_MONITOR (w32_m);
+
+      if (!w32_m->add)
+        continue;
+
+      gdk_display_monitor_added (display, m);
+
+      if (i == 0)
+        g_ptr_array_insert (win32_display->monitors, 0, g_object_ref (w32_m));
+      else
+        g_ptr_array_add (win32_display->monitors, g_object_ref (w32_m));
+    }
+
+  g_ptr_array_free (new_monitors, TRUE);
+
+  if (primary_to_move)
+    {
+      g_ptr_array_remove (win32_display->monitors, g_object_ref (primary_to_move));
+      g_ptr_array_insert (win32_display->monitors, 0, primary_to_move);
+      changed = TRUE;
+    }
+
+  return changed;
+}
+
+
 /**
  * gdk_win32_display_set_cursor_theme:
  * @display: (type GdkWin32Display): a #GdkDisplay
@@ -636,12 +804,15 @@ gdk_win32_display_finalize (GObject *object)
   _gdk_win32_display_finalize_cursors (display_win32);
   _gdk_win32_dnd_exit ();
 
+  g_ptr_array_free (display_win32->monitors, TRUE);
+
   G_OBJECT_CLASS (gdk_win32_display_parent_class)->finalize (object);
 }
 
 static void
 gdk_win32_display_init (GdkWin32Display *display)
 {
+  display->monitors = g_ptr_array_new_with_free_func (g_object_unref);
   _gdk_win32_display_init_cursors (display);
 }
 
@@ -672,6 +843,40 @@ gdk_win32_display_pop_error_trap (GdkDisplay *display,
 {
   return 0;
 }
+
+static int
+gdk_win32_display_get_n_monitors (GdkDisplay *display)
+{
+  GdkWin32Display *win32_display = GDK_WIN32_DISPLAY (display);
+
+  return win32_display->monitors->len;
+}
+
+
+static GdkMonitor *
+gdk_win32_display_get_monitor (GdkDisplay *display,
+                               int         monitor_num)
+{
+  GdkWin32Display *win32_display = GDK_WIN32_DISPLAY (display);
+
+  if (0 <= monitor_num || monitor_num < win32_display->monitors->len)
+    return (GdkMonitor *) g_ptr_array_index (win32_display->monitors, monitor_num);
+
+  return NULL;
+}
+
+static GdkMonitor *
+gdk_win32_display_get_primary_monitor (GdkDisplay *display)
+{
+  GdkWin32Display *win32_display = GDK_WIN32_DISPLAY (display);
+
+  /* We arrange for the first monitor in the array to also be the primiary monitor */
+  if (win32_display->monitors->len > 0)
+    return (GdkMonitor *) g_ptr_array_index (win32_display->monitors, 0);
+
+  return NULL;
+}
+
 static void
 gdk_win32_display_class_init (GdkWin32DisplayClass *klass)
 {
@@ -727,5 +932,9 @@ gdk_win32_display_class_init (GdkWin32DisplayClass *klass)
   display_class->utf8_to_string_target = _gdk_win32_display_utf8_to_string_target;
   display_class->make_gl_context_current = _gdk_win32_display_make_gl_context_current;
 
+  display_class->get_n_monitors = gdk_win32_display_get_n_monitors;
+  display_class->get_monitor = gdk_win32_display_get_monitor;
+  display_class->get_primary_monitor = gdk_win32_display_get_primary_monitor;
+
   _gdk_win32_windowing_init ();
 }
diff --git a/gdk/win32/gdkdisplay-win32.h b/gdk/win32/gdkdisplay-win32.h
index cbd4b5d..009b380 100644
--- a/gdk/win32/gdkdisplay-win32.h
+++ b/gdk/win32/gdkdisplay-win32.h
@@ -42,6 +42,8 @@ struct _GdkWin32Display
   HDC gl_hdc;
   HWND gl_hwnd;
 
+  GPtrArray *monitors;
+
   guint hasWglARBCreateContext : 1;
   guint hasWglEXTSwapControl : 1;
   guint hasWglOMLSyncControl : 1;
@@ -52,4 +54,8 @@ struct _GdkWin32DisplayClass
   GdkDisplayClass display_class;
 };
 
+gboolean   _gdk_win32_display_init_monitors    (GdkWin32Display *display);
+
+GPtrArray *_gdk_win32_display_get_monitor_list (GdkWin32Display *display);
+
 #endif /* __GDK_DISPLAY__WIN32_H__ */
diff --git a/gdk/win32/gdkmonitor-win32.c b/gdk/win32/gdkmonitor-win32.c
new file mode 100644
index 0000000..bae9320
--- /dev/null
+++ b/gdk/win32/gdkmonitor-win32.c
@@ -0,0 +1,833 @@
+/*
+ * Copyright © 2016 Red Hat, Inc
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Library General Public
+ * License as published by the Free Software Foundation; either
+ * version 2 of the License, or (at your option) any later version.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ * Library General Public License for more details.
+ *
+ * You should have received a copy of the GNU Library General Public
+ * License along with this library. If not, see <http://www.gnu.org/licenses/>.
+ */
+
+#if defined (_WIN32_WINNT) && WIN32_WINNT < 0x0601
+#  undef _WIN32_WINNT
+
+#  define _WIN32_WINNT 0x0601
+#  ifdef WINVER
+#    undef WINVER
+#  endif
+#  define WINVER _WIN32_WINNT
+#elif !defined (_WIN32_WINNT)
+#  define _WIN32_WINNT 0x0601
+#  ifdef WINVER
+#    undef WINVER
+#  endif
+#  define WINVER _WIN32_WINNT
+#endif
+
+#include "config.h"
+
+#include "gdkmonitor-win32.h"
+
+#include <glib.h>
+#include <gio/gio.h>
+
+#include <cfgmgr32.h>
+#include <devpropdef.h>
+#include <setupapi.h>
+
+#include "gdkprivate-win32.h"
+
+G_DEFINE_TYPE (GdkWin32Monitor, gdk_win32_monitor, GDK_TYPE_MONITOR)
+
+/* MinGW-w64 carelessly put DISPLAYCONFIG_OUTPUT_TECHNOLOGY_OTHER = -1 into this
+ * enum, as documented by MSDN. However, with
+ * DISPLAYCONFIG_OUTPUT_TECHNOLOGY_INTERNAL = 0x80000000 and
+ * DISPLAYCONFIG_OUTPUT_TECHNOLOGY_FORCE_UINT32 = 0xFFFFFFFF
+ * this had the effect of increasing enum size from 4 to 8 bytes,
+ * when compiled by GCC (MSVC doesn't have this problem), breaking ABI.
+ * At the moment of writing MinGW-w64 headers are still broken.
+ * When they are fixed, replace 9999 with actual version numbers.
+ * The fix below is not necessarily correct, but it works.
+ */
+#if SIZEOF_DISPLAYCONFIG_VIDEO_OUTPUT_TECHNOLOGY == 4
+#  define fixedDISPLAYCONFIG_PATH_INFO DISPLAYCONFIG_PATH_INFO
+#  define fixedDISPLAYCONFIG_TARGET_DEVICE_NAME DISPLAYCONFIG_TARGET_DEVICE_NAME
+#else
+typedef enum {
+  fixedDISPLAYCONFIG_OUTPUT_TECHNOLOGY_OTHER                 = (int) -1,
+  fixedDISPLAYCONFIG_OUTPUT_TECHNOLOGY_HD15                  = (int) 0,
+  fixedDISPLAYCONFIG_OUTPUT_TECHNOLOGY_SVIDEO                = (int) 1,
+  fixedDISPLAYCONFIG_OUTPUT_TECHNOLOGY_COMPOSITE_VIDEO       = (int) 2,
+  fixedDISPLAYCONFIG_OUTPUT_TECHNOLOGY_COMPONENT_VIDEO       = (int) 3,
+  fixedDISPLAYCONFIG_OUTPUT_TECHNOLOGY_DVI                   = (int) 4,
+  fixedDISPLAYCONFIG_OUTPUT_TECHNOLOGY_HDMI                  = (int) 5,
+  fixedDISPLAYCONFIG_OUTPUT_TECHNOLOGY_LVDS                  = (int) 6,
+  fixedDISPLAYCONFIG_OUTPUT_TECHNOLOGY_D_JPN                 = (int) 8,
+  fixedDISPLAYCONFIG_OUTPUT_TECHNOLOGY_SDI                   = (int) 9,
+  fixedDISPLAYCONFIG_OUTPUT_TECHNOLOGY_DISPLAYPORT_EXTERNAL  = (int) 10,
+  fixedDISPLAYCONFIG_OUTPUT_TECHNOLOGY_DISPLAYPORT_EMBEDDED  = (int) 11,
+  fixedDISPLAYCONFIG_OUTPUT_TECHNOLOGY_UDI_EXTERNAL          = (int) 12,
+  fixedDISPLAYCONFIG_OUTPUT_TECHNOLOGY_UDI_EMBEDDED          = (int) 13,
+  fixedDISPLAYCONFIG_OUTPUT_TECHNOLOGY_SDTVDONGLE            = (int) 14,
+  fixedDISPLAYCONFIG_OUTPUT_TECHNOLOGY_INTERNAL              = (int) 0x80000000,
+  fixedDISPLAYCONFIG_OUTPUT_TECHNOLOGY_FORCE_UINT32          = (int) 0xFFFFFFFF
+} fixedDISPLAYCONFIG_VIDEO_OUTPUT_TECHNOLOGY;
+
+typedef struct fixedDISPLAYCONFIG_PATH_TARGET_INFO {
+  LUID                                       adapterId;
+  UINT32                                     id;
+  UINT32                                     modeInfoIdx;
+  fixedDISPLAYCONFIG_VIDEO_OUTPUT_TECHNOLOGY outputTechnology;
+  DISPLAYCONFIG_ROTATION                     rotation;
+  DISPLAYCONFIG_SCALING                      scaling;
+  DISPLAYCONFIG_RATIONAL                     refreshRate;
+  DISPLAYCONFIG_SCANLINE_ORDERING            scanLineOrdering;
+  WINBOOL                                    targetAvailable;
+  UINT32                                     statusFlags;
+} fixedDISPLAYCONFIG_PATH_TARGET_INFO;
+
+typedef struct fixedDISPLAYCONFIG_PATH_INFO
+{
+  DISPLAYCONFIG_PATH_SOURCE_INFO      sourceInfo;
+  fixedDISPLAYCONFIG_PATH_TARGET_INFO targetInfo;
+  UINT32                              flags;
+} fixedDISPLAYCONFIG_PATH_INFO;
+
+typedef struct fixedDISPLAYCONFIG_TARGET_DEVICE_NAME
+{
+  DISPLAYCONFIG_DEVICE_INFO_HEADER            header;
+  DISPLAYCONFIG_TARGET_DEVICE_NAME_FLAGS      flags;
+  fixedDISPLAYCONFIG_VIDEO_OUTPUT_TECHNOLOGY  outputTechnology;
+  UINT16                                      edidManufactureId;
+  UINT16                                      edidProductCodeId;
+  UINT32                                      connectorInstance;
+  WCHAR                                       monitorFriendlyDeviceName[64];
+  WCHAR                                       monitorDevicePath[128];
+} fixedDISPLAYCONFIG_TARGET_DEVICE_NAME;
+
+#endif
+
+
+/* MinGW-w64 does not have these functions in its import libraries
+ * at the moment of writing.
+ * Also, Windows Vista doesn't have these functions at all
+ * (according to MSDN it does, but that is a lie), so we'd have
+ * to load them manually anyway (otherwise GTK apps won't even start
+ * on Vista).
+ */
+typedef LONG WINAPI
+(* funcGetDisplayConfigBufferSizes) (UINT32 flags,
+                                     UINT32* numPathArrayElements,
+                                     UINT32* numModeInfoArrayElements);
+
+typedef LONG WINAPI
+(* funcQueryDisplayConfig) (UINT32 flags,
+                            UINT32* numPathArrayElements,
+                            fixedDISPLAYCONFIG_PATH_INFO* pathArray,
+                            UINT32* numModeInfoArrayElements,
+                            DISPLAYCONFIG_MODE_INFO* modeInfoArray,
+                            DISPLAYCONFIG_TOPOLOGY_ID* currentTopologyId);
+
+typedef LONG WINAPI
+(* funcDisplayConfigGetDeviceInfo) (DISPLAYCONFIG_DEVICE_INFO_HEADER* requestPacket);
+
+#ifndef MONITORINFOF_PRIMARY
+#define MONITORINFOF_PRIMARY 1
+#endif
+
+/* MinGW-w64 does not have a prototype for function in its headers
+ * at the moment of writing.
+ */
+#if !defined (HAVE_SETUP_DI_GET_DEVICE_PROPERTY_W)
+BOOL WINAPI SetupDiGetDevicePropertyW (HDEVINFO          DeviceInfoSet,
+                                       PSP_DEVINFO_DATA  DeviceInfoData,
+                                       const DEVPROPKEY *PropertyKey,
+                                       DEVPROPTYPE      *PropertyType,
+                                       PBYTE             PropertyBuffer,
+                                       DWORD             PropertyBufferSize,
+                                       PDWORD            RequiredSize,
+                                       DWORD             Flags);
+#endif
+
+#define G_GUID_FORMAT "%08lX-%04X-%04X-%02X%02X-%02X%02X%02X%02X%02X%02X"
+#define g_format_guid(guid) (guid)->Data1, \
+                            (guid)->Data2, \
+                            (guid)->Data3, \
+                            (guid)->Data4[0], \
+                            (guid)->Data4[1], \
+                            (guid)->Data4[2], \
+                            (guid)->Data4[3], \
+                            (guid)->Data4[4], \
+                            (guid)->Data4[5], \
+                            (guid)->Data4[6], \
+                            (guid)->Data4[7]
+
+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)
+{
+  DEVPROPTYPE property_type;
+  gpointer property;
+  DWORD property_size;
+
+  property = NULL;
+  property_size = 0;
+
+  if (!SetupDiGetDevicePropertyW (device_infoset,
+                                  device_info_data,
+                                  property_key,
+                                  &property_type,
+                                  property,
+                                  property_size,
+                                  &property_size,
+                                  0))
+    {
+      DWORD error_code = GetLastError ();
+
+      if (error_code != ERROR_INSUFFICIENT_BUFFER)
+        {
+          gchar *emsg = g_win32_error_message (error_code);
+          g_warning ("Failed to get device node property {" G_GUID_FORMAT "},%lu size: %s",
+                     g_format_guid (&property_key->fmtid),
+                     property_key->pid,
+                     emsg);
+          g_free (emsg);
+
+          return FALSE;
+        }
+    }
+
+  if (r_buffer)
+    {
+      property = g_malloc (property_size);
+
+      if (!SetupDiGetDevicePropertyW (device_infoset,
+                                      device_info_data,
+                                      property_key,
+                                      &property_type,
+                                      property,
+                                      property_size,
+                                      &property_size,
+                                      0))
+        {
+          DWORD error_code = GetLastError ();
+
+          gchar *emsg = g_win32_error_message (error_code);
+          g_warning ("Failed to get device node property {" G_GUID_FORMAT "},%lu: %s",
+                     g_format_guid (&property_key->fmtid),
+                     property_key->pid,
+                     emsg);
+          g_free (emsg);
+
+          return FALSE;
+        }
+
+      *r_buffer = property;
+    }
+
+  if (r_buffer_size)
+    *r_buffer_size = property_size;
+
+  if (r_property_type)
+    *r_property_type = property_type;
+
+  return TRUE;
+}
+
+static GPtrArray *
+get_monitor_devices (GdkWin32Display *win32_display)
+{
+  GPtrArray *monitor_array;
+  HDEVINFO device_infoset;
+  SP_DEVINFO_DATA device_info_data;
+  DWORD device_index;
+  GUID device_interface_monitor = {0xe6f07b5f, 0xee97, 0x4a90, {0xb0, 0x76, 0x33, 0xf5, 0x7b, 0xf4, 0xea, 
0xa7}};
+  DEVPROPKEY pkey_device_instance_id = {{0x78C34FC8, 0x104A, 0x4ACA, {0x9E, 0xA4, 0x52, 0x4D, 0x52, 0x99, 
0x6E, 0x57}}, 256};
+  DEVPROPKEY pkey_manufacturer = {{0xA45C254E, 0xDF1C, 0x4EFD, {0x80, 0x20, 0x67, 0xD1, 0x46, 0xA8, 0x50, 
0xE0}}, 13};
+  DEVPROPKEY pkey_display_name = {{0xB725F130, 0x47EF, 0x101A, {0xA5, 0xF1, 0x02, 0x60, 0x8C, 0x9E, 0xEB, 
0xAC}}, 10};
+
+  monitor_array = g_ptr_array_new_with_free_func (g_object_unref);
+
+  device_infoset = SetupDiGetClassDevs (&device_interface_monitor, 0, 0, DIGCF_DEVICEINTERFACE | 
DIGCF_PRESENT);
+
+  if (device_infoset == INVALID_HANDLE_VALUE)
+    return monitor_array;
+
+  for (device_index = 0; TRUE; device_index++)
+    {
+      gunichar2 *p;
+      gchar *instance_path;
+      gunichar2 *prop;
+      DWORD proptype;
+      HKEY device_registry_key;
+      GdkWin32Monitor *w32mon;
+      GdkMonitor *mon;
+      unsigned char *edid;
+      DWORD edid_size;
+      DWORD edid_type;
+
+      memset (&device_info_data, 0, sizeof (device_info_data));
+      device_info_data.cbSize = sizeof (device_info_data);
+
+      if (!SetupDiEnumDeviceInfo (device_infoset, device_index, &device_info_data))
+        {
+          DWORD error_code = GetLastError ();
+
+          if (error_code == ERROR_NO_MORE_ITEMS)
+            break;
+
+          g_warning ("SetupDiEnumDeviceInfo() failed: %lu\n", error_code);
+          break;
+        }
+
+      if (!get_device_property (device_infoset,
+                                &device_info_data,
+                                &pkey_device_instance_id,
+                                (gpointer *) &prop,
+                                NULL,
+                                &proptype,
+                                NULL))
+        continue;
+
+      if (proptype != DEVPROP_TYPE_STRING)
+        {
+          g_free (prop);
+          continue;
+        }
+
+      w32mon = g_object_new (GDK_TYPE_WIN32_MONITOR, "display", win32_display, NULL);
+      mon = GDK_MONITOR (w32mon);
+
+      g_ptr_array_add (monitor_array, w32mon);
+
+      /* Half-initialized monitors are candidates for removal */
+      w32mon->remove = TRUE;
+
+      /* device instance ID looks like: DISPLAY\FOO\X&XXXXXXX&X&UIDXXX */
+      for (p = prop; p[0]; p++)
+        if (p[0] == L'\\')
+          p[0] = L'#';
+      /* now device instance ID looks like: DISPLAY#FOO#X&XXXXXXX&X&UIDXXX */
+
+      /* instance path looks like: \\?\DISPLAY#FOO#X&XXXXXXX&X&UIDXXX#{e6f07b5f-ee97-4a90-b076-33f57bf4eaa7} 
*/
+      instance_path = g_strdup_printf ("\\\\?\\%ls#{" G_GUID_FORMAT "}",
+                                       prop,
+                                       g_format_guid (&device_interface_monitor));
+      w32mon->instance_path = g_utf8_strdown (instance_path, -1);
+      g_free (instance_path);
+      g_free (prop);
+
+      if (get_device_property (device_infoset,
+                               &device_info_data,
+                               &pkey_manufacturer,
+                               (gpointer *) &prop,
+                               NULL, &proptype, NULL))
+        {
+          if (proptype == DEVPROP_TYPE_STRING)
+            {
+              gchar *manufacturer = g_utf16_to_utf8 (prop, -1, NULL, NULL, NULL);
+              gdk_monitor_set_manufacturer (mon, manufacturer);
+              g_free (manufacturer);
+            }
+
+          g_free (prop);
+        }
+
+      if (get_device_property (device_infoset,
+                               &device_info_data,
+                               &pkey_display_name,
+                               (gpointer *) &prop,
+                               NULL, &proptype, NULL))
+        {
+          if (proptype == DEVPROP_TYPE_STRING)
+            {
+              gchar *name = g_utf16_to_utf8 (prop, -1, NULL, NULL, NULL);
+              gdk_monitor_set_model (mon, name);
+              g_free (name);
+            }
+
+          g_free (prop);
+        }
+
+      device_registry_key = SetupDiOpenDevRegKey (device_infoset, &device_info_data, DICS_FLAG_GLOBAL, 0, 
DIREG_DEV, KEY_READ);
+
+      if (device_registry_key == NULL || device_registry_key == INVALID_HANDLE_VALUE)
+        continue;
+
+      edid = NULL;
+      edid_size = 0;
+
+
+      if (RegQueryValueExW (device_registry_key, L"EDID",
+                            NULL, &edid_type,
+                            edid, &edid_size) == ERROR_SUCCESS)
+        {
+          edid = g_malloc (edid_size);
+
+          if (RegQueryValueExW (device_registry_key, L"EDID",
+                                NULL, &edid_type,
+                                edid, &edid_size) == ERROR_SUCCESS)
+            {
+              gdk_monitor_set_physical_size (mon,
+                                             ((edid[68] & 0x00F0) << 4) + edid[66],
+                                             ((edid[68] & 0x000F) << 8) + edid[67]);
+            }
+
+          g_free (edid);
+        }
+
+      RegCloseKey (device_registry_key);
+    }
+
+  SetupDiDestroyDeviceInfoList (device_infoset);
+
+  return monitor_array;
+}
+
+static void
+populate_monitor_devices_from_display_config (GPtrArray *monitors)
+{
+  HMODULE user32;
+  LONG return_code;
+  funcGetDisplayConfigBufferSizes getDisplayConfigBufferSizes;
+  funcQueryDisplayConfig queryDisplayConfig;
+  funcDisplayConfigGetDeviceInfo displayConfigGetDeviceInfo;
+  UINT32 dispconf_mode_count;
+  UINT32 dispconf_path_count;
+  fixedDISPLAYCONFIG_PATH_INFO *dispconf_paths;
+  DISPLAYCONFIG_MODE_INFO *dispconf_modes;
+  gint path_index;
+
+  user32 = LoadLibraryA ("user32.dll");
+
+  if (user32 == NULL)
+    return;
+
+  getDisplayConfigBufferSizes = (funcGetDisplayConfigBufferSizes) GetProcAddress (user32,
+                                                                                  
"GetDisplayConfigBufferSizes");
+  queryDisplayConfig = (funcQueryDisplayConfig) GetProcAddress (user32,
+                                                                "QueryDisplayConfig");
+  displayConfigGetDeviceInfo = (funcDisplayConfigGetDeviceInfo) GetProcAddress (user32,
+                                                                                
"DisplayConfigGetDeviceInfo");
+  if (getDisplayConfigBufferSizes == NULL ||
+      queryDisplayConfig == NULL ||
+      displayConfigGetDeviceInfo == NULL)
+    {
+      /* This does happen on Windows Vista, so don't warn about this */
+      FreeLibrary (user32);
+
+      return;
+    }
+
+  return_code = getDisplayConfigBufferSizes (QDC_ONLY_ACTIVE_PATHS,
+                                         &dispconf_path_count,
+                                         &dispconf_mode_count);
+
+  if (return_code != ERROR_SUCCESS)
+    {
+      g_warning ("Can't get displayconfig buffer size: 0x%lx\n", return_code);
+      FreeLibrary (user32);
+
+      return;
+    }
+
+  dispconf_paths = g_new (fixedDISPLAYCONFIG_PATH_INFO, dispconf_path_count);
+  dispconf_modes = g_new (DISPLAYCONFIG_MODE_INFO, dispconf_mode_count);
+
+  return_code = queryDisplayConfig (QDC_ONLY_ACTIVE_PATHS,
+                                    &dispconf_path_count,
+                                    dispconf_paths,
+                                    &dispconf_mode_count,
+                                    dispconf_modes,
+                                    NULL);
+
+  if (return_code != ERROR_SUCCESS)
+    {
+      g_free (dispconf_paths);
+      g_free (dispconf_modes);
+      FreeLibrary (user32);
+
+      return;
+    }
+
+  for (path_index = 0; path_index < dispconf_path_count; path_index++)
+    {
+      fixedDISPLAYCONFIG_TARGET_DEVICE_NAME tdn;
+      gint i;
+      GdkWin32Monitor *w32mon;
+      GdkMonitor *mon;
+      gchar *path, *path_lower;
+      DISPLAYCONFIG_RATIONAL *refresh;
+
+      if ((dispconf_paths[path_index].flags & DISPLAYCONFIG_PATH_ACTIVE) == 0)
+        continue;
+
+      tdn.header.type = DISPLAYCONFIG_DEVICE_INFO_GET_TARGET_NAME;
+      tdn.header.size = sizeof (tdn);
+      tdn.header.adapterId = dispconf_paths[path_index].targetInfo.adapterId;
+      tdn.header.id = dispconf_paths[path_index].targetInfo.id;
+
+      return_code = displayConfigGetDeviceInfo (&tdn.header);
+
+      if (return_code != ERROR_SUCCESS)
+        continue;
+
+      path = g_utf16_to_utf8 (tdn.monitorDevicePath, -1, NULL, NULL, NULL);
+
+      if (path == NULL)
+        continue;
+
+      path_lower = g_utf8_strdown (path, -1);
+
+      g_free (path);
+
+      for (i = 0, w32mon = NULL; i < monitors->len; i++)
+        {
+          GdkWin32Monitor *m = g_ptr_array_index (monitors, i);
+
+          if (g_strcmp0 (m->instance_path, path_lower) != 0)
+            continue;
+
+          w32mon = m;
+          break;
+       }
+
+      g_free (path_lower);
+
+      if (w32mon == NULL)
+        continue;
+
+      mon = GDK_MONITOR (w32mon);
+
+      if (!tdn.flags.friendlyNameForced)
+        {
+          /* monitorFriendlyDeviceName is usually nicer */
+          gchar *name = g_utf16_to_utf8 (tdn.monitorFriendlyDeviceName, -1, NULL, NULL, NULL);
+          gdk_monitor_set_model (mon, name);
+          g_free (name);
+        }
+
+      refresh = &dispconf_paths[path_index].targetInfo.refreshRate;
+      gdk_monitor_set_refresh_rate (mon,
+                                    refresh->Numerator * 1000 / refresh->Denominator);
+    }
+
+  g_free (dispconf_paths);
+  g_free (dispconf_modes);
+
+  FreeLibrary (user32);
+}
+
+typedef struct {
+  GPtrArray *monitors;
+  gboolean have_monitor_devices;
+  GdkWin32Display *display;
+} EnumMonitorData;
+
+static BOOL CALLBACK
+enum_monitor (HMONITOR hmonitor,
+              HDC      hdc,
+              LPRECT   rect,
+              LPARAM   param)
+{
+  EnumMonitorData *data = (EnumMonitorData *) param;
+  MONITORINFOEXW monitor_info;
+  DWORD i_adapter;
+
+  /* Grab monitor_info for this logical monitor */
+  monitor_info.cbSize = sizeof (MONITORINFOEXW);
+  GetMonitorInfoW (hmonitor, (MONITORINFO *) &monitor_info);
+
+  /* Sidestep to enumerate display adapters */
+  for (i_adapter = 0; TRUE; i_adapter++)
+    {
+      DISPLAY_DEVICEW dd;
+      DEVMODEW dm;
+      DWORD i_monitor;
+      DWORD frequency;
+
+      memset (&dd, 0, sizeof (dd));
+      dd.cb = sizeof (dd);
+
+      /* Get i_adapter'th adapter */
+      if (!EnumDisplayDevicesW (NULL, i_adapter, &dd, EDD_GET_DEVICE_INTERFACE_NAME))
+        break;
+
+      if ((dd.StateFlags & DISPLAY_DEVICE_ATTACHED_TO_DESKTOP) == 0)
+        continue;
+
+      /* Match this display adapter to one for which we've got monitor_info
+       * (logical monitor == adapter)
+       */
+      if (wcscmp (dd.DeviceName, monitor_info.szDevice) != 0)
+        continue;
+
+      dm.dmSize = sizeof (dm);
+
+      /* Grab refresh rate for this adapter while we're at it */
+      if (EnumDisplaySettingsW (dd.DeviceName, ENUM_CURRENT_SETTINGS, &dm))
+        frequency = dm.dmDisplayFrequency;
+      else
+        frequency = 0;
+
+      /* Enumerate monitors connected to this display adapter */
+      for (i_monitor = 0; TRUE; i_monitor++)
+        {
+          DISPLAY_DEVICEW dd_monitor;
+          gchar *device_id_lower, *tmp;
+          DWORD i;
+          GdkWin32Monitor *w32mon;
+          GdkMonitor *mon;
+          GdkRectangle rect;
+
+          memset (&dd_monitor, 0, sizeof (dd_monitor));
+          dd_monitor.cb = sizeof (dd_monitor);
+
+          if (data->have_monitor_devices)
+            {
+              /* Get i_monitor'th monitor */
+              if (!EnumDisplayDevicesW (dd.DeviceName, i_monitor, &dd_monitor, 
EDD_GET_DEVICE_INTERFACE_NAME))
+                break;
+
+              tmp = g_utf16_to_utf8 (dd_monitor.DeviceID, -1, NULL, NULL, NULL);
+
+              if (tmp == NULL)
+                continue;
+
+              device_id_lower = g_utf8_strdown (tmp, -1);
+              g_free (tmp);
+
+              /* Match this monitor to one of the monitor devices we found earlier */
+              for (i = 0, w32mon = NULL; i < data->monitors->len; i++)
+                {
+                  GdkWin32Monitor *m = g_ptr_array_index (data->monitors, i);
+
+                  if (g_strcmp0 (device_id_lower, m->instance_path) != 0)
+                    continue;
+
+                  w32mon = m;
+                  break;
+                }
+
+              g_free (device_id_lower);
+
+              if (w32mon == NULL)
+                continue;
+            }
+          else
+            {
+              /* Headless PC or a virtual machine, it has no monitor devices.
+               * Make one up.
+               */
+              w32mon = g_object_new (GDK_TYPE_WIN32_MONITOR, "display", data->display, NULL);
+              g_ptr_array_add (data->monitors, w32mon);
+              i = data->monitors->len - 1;
+              w32mon->madeup = TRUE;
+            }
+
+          mon = GDK_MONITOR (w32mon);
+
+          if (gdk_monitor_get_model (mon) == NULL)
+            {
+              
+              gchar *name = NULL;
+
+              /* Only use dd.DeviceName as a last resort, as it is just
+               * \\.\DISPLAYX\MonitorY (for some values of X and Y).
+               */
+              if (dd_monitor.DeviceName[0] != L'\0')
+                name = g_utf16_to_utf8 (dd_monitor.DeviceName, -1, NULL, NULL, NULL);
+              else if (dd.DeviceName[0] != L'\0')
+                name = g_utf16_to_utf8 (dd.DeviceName, -1, NULL, NULL, NULL);
+
+              if (name != NULL)
+                gdk_monitor_set_model (mon, name);
+
+              g_free (name);
+            }
+
+          /* GetDeviceCaps seems to provide a wild guess, prefer more precise EDID info */
+          if (gdk_monitor_get_width_mm (mon) == 0 &&
+              gdk_monitor_get_height_mm (mon) == 0)
+            {
+              HDC hDC = CreateDCW (L"DISPLAY", monitor_info.szDevice, NULL, NULL);
+
+              gdk_monitor_set_physical_size (mon,
+                                             GetDeviceCaps (hDC, HORZSIZE),
+                                             GetDeviceCaps (hDC, VERTSIZE));
+              DeleteDC (hDC);
+            }
+
+          /* frequency is in Hz and is unsigned long,
+           * prefer more precise refresh_rate found earlier,
+           * which comes as a Numerator & Denominator pair and is more precise.
+           */
+          if (gdk_monitor_get_refresh_rate (mon) == 0)
+            gdk_monitor_set_refresh_rate (mon, frequency * 1000);
+
+          /* This is the reason this function exists. This data is not available
+           * via other functions.
+           */
+          rect.x = monitor_info.rcMonitor.left;
+          rect.y = monitor_info.rcMonitor.top;
+          rect.width = monitor_info.rcMonitor.right - monitor_info.rcMonitor.left;
+          rect.height = monitor_info.rcMonitor.bottom - monitor_info.rcMonitor.top;
+          gdk_monitor_set_position (mon, rect.x, rect.y);
+          gdk_monitor_set_size (mon, rect.width, rect.height);
+
+          rect.x = monitor_info.rcWork.left;
+          rect.y = monitor_info.rcWork.top;
+          rect.width = monitor_info.rcWork.right - monitor_info.rcWork.left;
+          rect.height = monitor_info.rcWork.bottom - monitor_info.rcWork.top;
+          w32mon->work_rect = rect;
+
+          if (monitor_info.dwFlags & MONITORINFOF_PRIMARY && i != 0)
+            {
+              /* Put primary monitor at index 0, just in case somebody needs
+               * to know which one is the primary.
+               */
+              GdkWin32Monitor *temp = g_ptr_array_index (data->monitors, 0);
+              g_ptr_array_index (data->monitors, 0) = w32mon;
+              g_ptr_array_index (data->monitors, i) = temp;
+            }
+
+          /* Work area is the most important component, actively used by GTK,
+           * but our initial list of monitor devices did not have it.
+           * Any monitor devices not matched in this functions will have
+           * 0-filled work area and will therefore be useless, so let them
+           * keep remove == TRUE and be removed further up the stack.
+           */
+          w32mon->remove = FALSE;
+
+          /* One virtual monitor per display adapter */
+          if (w32mon->madeup)
+            break;
+        }
+    }
+
+  return TRUE;
+}
+
+GPtrArray *
+_gdk_win32_display_get_monitor_list (GdkWin32Display *win32_display)
+{
+  EnumMonitorData data;
+  gint i;
+
+  data.display = win32_display;
+  data.monitors = get_monitor_devices (win32_display);
+
+  if (data.monitors->len != 0)
+    {
+      populate_monitor_devices_from_display_config (data.monitors);
+      data.have_monitor_devices = TRUE;
+    }
+  else
+    {
+      data.have_monitor_devices = FALSE;
+    }
+
+  EnumDisplayMonitors (NULL, NULL, enum_monitor, (LPARAM) &data);
+
+  _gdk_offset_x = G_MININT;
+  _gdk_offset_y = G_MININT;
+
+  for (i = 0; i < data.monitors->len; i++)
+    {
+      GdkWin32Monitor *m;
+      GdkRectangle rect;
+
+      m = g_ptr_array_index (data.monitors, i);
+
+      if (m->remove)
+        {
+          g_ptr_array_remove_index (data.monitors, i);
+          continue;
+        }
+
+      /* Calculate offset */
+      gdk_monitor_get_geometry (GDK_MONITOR (m), &rect);
+      _gdk_offset_x = MAX (_gdk_offset_x, -rect.x);
+      _gdk_offset_y = MAX (_gdk_offset_y, -rect.y);
+    }
+
+  GDK_NOTE (MISC, g_print ("Multi-monitor offset: (%d,%d)\n",
+                           _gdk_offset_x, _gdk_offset_y));
+
+  /* Translate monitor coords into GDK coordinate space */
+  for (i = 0; i < data.monitors->len; i++)
+    {
+      GdkWin32Monitor *m;
+      GdkRectangle rect;
+
+      m = g_ptr_array_index (data.monitors, i);
+
+      gdk_monitor_get_geometry (GDK_MONITOR (m), &rect);
+      rect.x += _gdk_offset_x;
+      rect.y += _gdk_offset_y;
+      gdk_monitor_set_position (GDK_MONITOR (m), rect.x, rect.y);
+
+      m->work_rect.x += _gdk_offset_x;
+      m->work_rect.y += _gdk_offset_y;
+
+      GDK_NOTE (MISC, g_print ("Monitor %d: %dx%d %+d%+d\n", i,
+                               rect.width, rect.height, rect.x, rect.y));
+    }
+
+  return data.monitors;
+}
+
+static void
+gdk_win32_monitor_finalize (GObject *object)
+{
+  GdkWin32Monitor *win32_monitor = GDK_WIN32_MONITOR (object);
+
+  g_free (win32_monitor->instance_path);
+
+  G_OBJECT_CLASS (gdk_win32_monitor_parent_class)->finalize (object);
+}
+
+int
+_gdk_win32_monitor_compare (GdkWin32Monitor *a,
+                            GdkWin32Monitor *b)
+{
+  if (a->instance_path != NULL &&
+      b->instance_path != NULL)
+    return g_strcmp0 (a->instance_path, b->instance_path);
+
+  return a == b ? 0 : a < b ? -1 : 1;
+}
+
+static void
+gdk_win32_monitor_get_workarea (GdkMonitor   *monitor,
+                                GdkRectangle *dest)
+{
+  GdkWin32Monitor *win32_monitor = GDK_WIN32_MONITOR (monitor);
+
+  *dest = win32_monitor->work_rect;
+}
+
+static void
+gdk_win32_monitor_init (GdkWin32Monitor *monitor)
+{
+}
+
+static void
+gdk_win32_monitor_class_init (GdkWin32MonitorClass *class)
+{
+  G_OBJECT_CLASS (class)->finalize = gdk_win32_monitor_finalize;
+
+  GDK_MONITOR_CLASS (class)->get_workarea = gdk_win32_monitor_get_workarea;
+}
diff --git a/gdk/win32/gdkmonitor-win32.h b/gdk/win32/gdkmonitor-win32.h
new file mode 100644
index 0000000..c32f403
--- /dev/null
+++ b/gdk/win32/gdkmonitor-win32.h
@@ -0,0 +1,57 @@
+/*
+ * Copyright © 2016 Red Hat, Inc
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Library General Public
+ * License as published by the Free Software Foundation; either
+ * version 2 of the License, or (at your option) any later version.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ * Library General Public License for more details.
+ *
+ * You should have received a copy of the GNU Library General Public
+ * License along with this library. If not, see <http://www.gnu.org/licenses/>.
+ */
+
+#ifndef __GDK_WIN32_MONITOR_PRIVATE_H__
+#define __GDK_WIN32_MONITOR_PRIVATE_H__
+
+#include <windows.h>
+#include <glib.h>
+#include <gio/gio.h>
+
+#include "gdkmonitorprivate.h"
+
+#include "gdkwin32monitor.h"
+
+struct _GdkWin32Monitor
+{
+  GdkMonitor parent;
+
+  /* work area */
+  GdkRectangle work_rect;
+
+  /* Device instance path (used to match GdkWin32Monitor to monitor device) */
+  gchar *instance_path;
+
+  /* TRUE if monitor is made up by us
+   * (this happens when system has logical monitors, but no physical ones).
+   */
+  guint madeup : 1;
+
+  /* TRUE if we should notify GDK about this monitor being added */
+  guint add    : 1;
+
+  /* TRUE if we should notify GDK about this monitor being removed */
+  guint remove : 1;
+};
+
+struct _GdkWin32MonitorClass {
+  GdkMonitorClass parent_class;
+};
+
+int        _gdk_win32_monitor_compare  (GdkWin32Monitor *a, GdkWin32Monitor *b);
+
+#endif
diff --git a/gdk/win32/gdkscreen-win32.c b/gdk/win32/gdkscreen-win32.c
index 2a8d101..0b5d104 100644
--- a/gdk/win32/gdkscreen-win32.c
+++ b/gdk/win32/gdkscreen-win32.c
@@ -22,24 +22,15 @@
 #include "gdkwin32screen.h"
 #include "gdkdisplayprivate.h"
 #include "gdkvisualprivate.h"
+#include "gdkdisplay-win32.h"
+#include "gdkmonitor-win32.h"
 
 #include <dwmapi.h>
 
-typedef struct
-{
-  gchar *name;
-  gint width_mm, height_mm;
-  GdkRectangle rect;
-  GdkRectangle work_rect;
-} GdkWin32Monitor;
-
 struct _GdkWin32Screen
 {
   GdkScreen parent_instance;
 
-  gint num_monitors;
-  GdkWin32Monitor *monitors;
-
   GdkVisual *system_visual;
   GdkVisual *rgba_visual;
   gint available_visual_depths[1];
@@ -257,26 +248,44 @@ init_visual (GdkScreen *screen,
   return visual;
 }
 
-static void
+static gboolean
 init_root_window_size (GdkWin32Screen *screen)
 {
-  GdkRectangle rect;
+  GdkRectangle result;
   int i;
+  GdkDisplay *display = _gdk_display;
+  int monitor_count;
+  GdkMonitor *monitor;
+  gboolean changed;
+
+  monitor_count = gdk_display_get_n_monitors (display);
+  monitor = gdk_display_get_monitor (display, 0);
+  gdk_monitor_get_geometry (monitor, &result);
 
-  rect = screen->monitors[0].rect;
-  for (i = 1; i < screen->num_monitors; i++)
-    gdk_rectangle_union (&rect, &screen->monitors[i].rect, &rect);
+  for (i = 1; i < monitor_count; i++)
+  {
+    GdkRectangle rect;
+
+    monitor = gdk_display_get_monitor (display, i);
+    gdk_monitor_get_geometry (monitor, &rect);
+    gdk_rectangle_union (&result, &rect, &result);
+  }
+
+  changed = screen->root_window->width != result.width ||
+            screen->root_window->height != result.height;
+  screen->root_window->width = result.width;
+  screen->root_window->height = result.height;
 
-  screen->root_window->width = rect.width;
-  screen->root_window->height = rect.height;
+  return changed;
 }
 
-static void
+static gboolean
 init_root_window (GdkWin32Screen *screen_win32)
 {
   GdkScreen *screen;
   GdkWindow *window;
   GdkWindowImplWin32 *impl_win32;
+  gboolean changed;
 
   screen = GDK_SCREEN (screen_win32);
 
@@ -295,7 +304,7 @@ init_root_window (GdkWin32Screen *screen_win32)
 
   screen_win32->root_window = window;
 
-  init_root_window_size (screen_win32);
+  changed = init_root_window_size (screen_win32);
 
   window->x = 0;
   window->y = 0;
@@ -307,134 +316,8 @@ init_root_window (GdkWin32Screen *screen_win32)
   gdk_win32_handle_table_insert ((HANDLE *) &impl_win32->handle, window);
 
   GDK_NOTE (MISC, g_print ("screen->root_window=%p\n", window));
-}
-
-static BOOL CALLBACK
-count_monitor (HMONITOR hmonitor,
-               HDC      hdc,
-               LPRECT   rect,
-               LPARAM   data)
-{
-  gint *n = (gint *) data;
-
-  (*n)++;
-
-  return TRUE;
-}
-
-typedef struct {
-    GdkWin32Screen *screen;
-    gint index;
-} EnumMonitorData;
-
-static BOOL CALLBACK
-enum_monitor (HMONITOR hmonitor,
-              HDC      hdc,
-              LPRECT   rect,
-              LPARAM   param)
-{
-  /* The struct MONITORINFOEX definition is for some reason different
-   * in the winuser.h bundled with mingw64 from that in MSDN and the
-   * official 32-bit mingw (the MONITORINFO part is in a separate "mi"
-   * member). So to keep this easily compileable with either, repeat
-   * the MSDN definition it here.
-   */
-  typedef struct tagMONITORINFOEXA2 {
-    DWORD cbSize;
-    RECT  rcMonitor;
-    RECT  rcWork;
-    DWORD dwFlags;
-    CHAR szDevice[CCHDEVICENAME];
-  } MONITORINFOEXA2;
-
-  EnumMonitorData *data = (EnumMonitorData *) param;
-  GdkWin32Monitor *monitor;
-  MONITORINFOEXA2 monitor_info;
-  HDC hDC;
-
-  g_assert (data->index < data->screen->num_monitors);
-
-  monitor = data->screen->monitors + data->index;
-
-  monitor_info.cbSize = sizeof (MONITORINFOEXA2);
-  GetMonitorInfoA (hmonitor, (MONITORINFO *) &monitor_info);
-
-#ifndef MONITORINFOF_PRIMARY
-#define MONITORINFOF_PRIMARY 1
-#endif
-
-  monitor->name = g_strdup (monitor_info.szDevice);
-  hDC = CreateDCA ("DISPLAY", monitor_info.szDevice, NULL, NULL);
-  monitor->width_mm = GetDeviceCaps (hDC, HORZSIZE);
-  monitor->height_mm = GetDeviceCaps (hDC, VERTSIZE);
-  DeleteDC (hDC);
-  monitor->rect.x = monitor_info.rcMonitor.left;
-  monitor->rect.y = monitor_info.rcMonitor.top;
-  monitor->rect.width = monitor_info.rcMonitor.right - monitor_info.rcMonitor.left;
-  monitor->rect.height = monitor_info.rcMonitor.bottom - monitor_info.rcMonitor.top;
-  monitor->work_rect.x = monitor_info.rcWork.left;
-  monitor->work_rect.y = monitor_info.rcWork.top;
-  monitor->work_rect.width = monitor_info.rcWork.right - monitor_info.rcWork.left;
-  monitor->work_rect.height = monitor_info.rcWork.bottom - monitor_info.rcWork.top;
-
-  if (monitor_info.dwFlags & MONITORINFOF_PRIMARY && data->index != 0)
-    {
-      /* Put primary monitor at index 0, just in case somebody needs
-       * to know which one is the primary.
-       */
-      GdkWin32Monitor temp = *monitor;
-      *monitor = data->screen->monitors[0];
-      data->screen->monitors[0] = temp;
-    }
-
-  data->index++;
 
-  return TRUE;
-}
-
-static void
-init_monitors (GdkWin32Screen *screen)
-{
-  gint count;
-  EnumMonitorData data;
-  gint i;
-
-  count = 0;
-  EnumDisplayMonitors (NULL, NULL, count_monitor, (LPARAM) &count);
-  screen->num_monitors = count;
-
-  screen->monitors = g_renew (GdkWin32Monitor, screen->monitors, screen->num_monitors);
-
-  data.screen = screen;
-  data.index = 0;
-  EnumDisplayMonitors (NULL, NULL, enum_monitor, (LPARAM) &data);
-
-  _gdk_offset_x = G_MININT;
-  _gdk_offset_y = G_MININT;
-
-  /* Calculate offset */
-  for (i = 0; i < screen->num_monitors; i++)
-    {
-      GdkRectangle *rect = &screen->monitors[i].rect;
-      _gdk_offset_x = MAX (_gdk_offset_x, -rect->x);
-      _gdk_offset_y = MAX (_gdk_offset_y, -rect->y);
-    }
-  GDK_NOTE (MISC, g_print ("Multi-monitor offset: (%d,%d)\n",
-                           _gdk_offset_x, _gdk_offset_y));
-
-  /* Translate monitor coords into GDK coordinate space */
-  for (i = 0; i < screen->num_monitors; i++)
-    {
-      GdkRectangle *rect;
-      rect = &screen->monitors[i].rect;
-      rect->x += _gdk_offset_x;
-      rect->y += _gdk_offset_y;
-      rect = &screen->monitors[i].work_rect;
-      rect->x += _gdk_offset_x;
-      rect->y += _gdk_offset_y;
-      GDK_NOTE (MISC, g_print ("Monitor %d: %dx%d %+d%+d\n", i,
-                               rect->width, rect->height, rect->x, rect->y));
-    }
+  return changed;
 }
 
 static void
@@ -470,7 +353,7 @@ gdk_win32_screen_init (GdkWin32Screen *win32_screen)
   win32_screen->available_visual_depths[0] = win32_screen->rgba_visual->depth;
   win32_screen->available_visual_types[0] = win32_screen->rgba_visual->type;
 
-  init_monitors (win32_screen);
+  _gdk_win32_display_init_monitors (GDK_WIN32_DISPLAY (_gdk_display));
   init_root_window (win32_screen);
 
   /* On Windows 8 and later, DWM (composition) is always enabled */
@@ -480,9 +363,15 @@ gdk_win32_screen_init (GdkWin32Screen *win32_screen)
 void
 _gdk_win32_screen_on_displaychange_event (GdkWin32Screen *screen)
 {
-  init_monitors (screen);
-  init_root_window_size (screen);
-  g_signal_emit_by_name (screen, "size-changed");
+  gboolean monitors_changed;
+
+  monitors_changed = _gdk_win32_display_init_monitors (GDK_WIN32_DISPLAY (_gdk_display));
+
+  if (init_root_window_size (screen))
+    g_signal_emit_by_name (screen, "size-changed");
+
+  if (monitors_changed)
+    g_signal_emit_by_name (screen, "monitors-changed");
 }
 
 static GdkDisplay *
@@ -522,84 +411,6 @@ gdk_win32_screen_get_root_window (GdkScreen *screen)
 }
 
 static gint
-gdk_win32_screen_get_n_monitors (GdkScreen *screen)
-{
-  g_return_val_if_fail (screen == gdk_display_get_default_screen (gdk_display_get_default ()), 0);
-
-  return GDK_WIN32_SCREEN (screen)->num_monitors;
-}
-
-static gint
-gdk_win32_screen_get_primary_monitor (GdkScreen *screen)
-{
-  g_return_val_if_fail (screen == gdk_display_get_default_screen (gdk_display_get_default ()), 0);
-
-  return 0;
-}
-
-static gint
-gdk_win32_screen_get_monitor_width_mm (GdkScreen *screen,
-                                       gint       num_monitor)
-{
-  GdkWin32Screen *win32_screen = GDK_WIN32_SCREEN (screen);
-
-  g_return_val_if_fail (screen == gdk_display_get_default_screen (gdk_display_get_default ()), 0);
-  g_return_val_if_fail (num_monitor < win32_screen->num_monitors, 0);
-
-  return win32_screen->monitors[num_monitor].width_mm;
-}
-
-static gint
-gdk_win32_screen_get_monitor_height_mm (GdkScreen *screen,
-                                        gint       num_monitor)
-{
-  GdkWin32Screen *win32_screen = GDK_WIN32_SCREEN (screen);
-
-  g_return_val_if_fail (screen == gdk_display_get_default_screen (gdk_display_get_default ()), 0);
-  g_return_val_if_fail (num_monitor < win32_screen->num_monitors, 0);
-
-  return win32_screen->monitors[num_monitor].height_mm;
-}
-
-static gchar *
-gdk_win32_screen_get_monitor_plug_name (GdkScreen *screen,
-                                        gint       num_monitor)
-{
-  GdkWin32Screen *win32_screen = GDK_WIN32_SCREEN (screen);
-
-  g_return_val_if_fail (screen == gdk_display_get_default_screen (gdk_display_get_default ()), NULL);
-  g_return_val_if_fail (num_monitor < win32_screen->num_monitors, NULL);
-
-  return g_strdup (win32_screen->monitors[num_monitor].name);
-}
-
-static void
-gdk_win32_screen_get_monitor_geometry (GdkScreen    *screen,
-                                       gint          num_monitor,
-                                       GdkRectangle *dest)
-{
-  GdkWin32Screen *win32_screen = GDK_WIN32_SCREEN (screen);
-
-  g_return_if_fail (screen == gdk_display_get_default_screen (gdk_display_get_default ()));
-  g_return_if_fail (num_monitor < win32_screen->num_monitors);
-
-  *dest = win32_screen->monitors[num_monitor].rect;
-}
-
-static void
-gdk_win32_screen_get_monitor_workarea (GdkScreen    *screen,
-                                       gint          num_monitor,
-                                       GdkRectangle *dest)
-{
-  GdkWin32Screen *win32_screen = GDK_WIN32_SCREEN (screen);
-
-  g_return_if_fail (screen == gdk_display_get_default_screen (gdk_display_get_default ()));
-  g_return_if_fail (num_monitor < win32_screen->num_monitors);
-
-  *dest = win32_screen->monitors[num_monitor].work_rect;
-}
-
-static gint
 gdk_win32_screen_get_number (GdkScreen *screen)
 {
   g_return_val_if_fail (screen == gdk_display_get_default_screen (gdk_display_get_default ()), 0);
@@ -750,13 +561,6 @@ gdk_win32_screen_list_visuals (GdkScreen *screen)
 static void
 gdk_win32_screen_finalize (GObject *object)
 {
-  GdkWin32Screen *screen = GDK_WIN32_SCREEN (object);
-  gint i;
-
-  for (i = 0; i < screen->num_monitors; i++)
-    g_free (screen->monitors[i].name);
-  g_free (screen->monitors);
-
   G_OBJECT_CLASS (gdk_win32_screen_parent_class)->finalize (object);
 }
 
@@ -775,13 +579,6 @@ gdk_win32_screen_class_init (GdkWin32ScreenClass *klass)
   screen_class->get_height_mm = gdk_win32_screen_get_height_mm;
   screen_class->get_number = gdk_win32_screen_get_number;
   screen_class->get_root_window = gdk_win32_screen_get_root_window;
-  screen_class->get_n_monitors = gdk_win32_screen_get_n_monitors;
-  screen_class->get_primary_monitor = gdk_win32_screen_get_primary_monitor;
-  screen_class->get_monitor_width_mm = gdk_win32_screen_get_monitor_width_mm;
-  screen_class->get_monitor_height_mm = gdk_win32_screen_get_monitor_height_mm;
-  screen_class->get_monitor_plug_name = gdk_win32_screen_get_monitor_plug_name;
-  screen_class->get_monitor_geometry = gdk_win32_screen_get_monitor_geometry;
-  screen_class->get_monitor_workarea = gdk_win32_screen_get_monitor_workarea;
   screen_class->is_composited = gdk_win32_screen_is_composited;
   screen_class->make_display_name = gdk_win32_screen_make_display_name;
   screen_class->get_active_window = gdk_win32_screen_get_active_window;
diff --git a/gdk/win32/gdkwin32.h b/gdk/win32/gdkwin32.h
index bf8b32b..e098366 100644
--- a/gdk/win32/gdkwin32.h
+++ b/gdk/win32/gdkwin32.h
@@ -35,6 +35,7 @@
 #include <gdk/win32/gdkwin32screen.h>
 #include <gdk/win32/gdkwin32window.h>
 #include <gdk/win32/gdkwin32misc.h>
+#include <gdk/win32/gdkwin32monitor.h>
 #include <gdk/win32/gdkwin32glcontext.h>
 
 #undef __GDKWIN32_H_INSIDE__
diff --git a/gdk/win32/gdkwin32monitor.h b/gdk/win32/gdkwin32monitor.h
new file mode 100644
index 0000000..93762bd
--- /dev/null
+++ b/gdk/win32/gdkwin32monitor.h
@@ -0,0 +1,50 @@
+/*
+ * gdkwin32monitor.h
+ *
+ * Copyright 2016 Red Hat, Inc.
+ *
+ * Matthias Clasen <mclasen redhat com>
+ * Руслан Ижбулатов <lrn1986 gmail com>
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Library General Public
+ * License as published by the Free Software Foundation; either
+ * version 2 of the License, or (at your option) any later version.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ * Library General Public License for more details.
+ *
+ * You should have received a copy of the GNU Library General Public
+ * License along with this library. If not, see <http://www.gnu.org/licenses/>.
+ */
+
+#ifndef __GDK_WIN32_MONITOR_H__
+#define __GDK_WIN32_MONITOR_H__
+
+#if !defined (__GDKWIN32_H_INSIDE__) && !defined (GDK_COMPILATION)
+#error "Only <gdk/gdkwin32.h> can be included directly."
+#endif
+
+#include <gdk/gdkmonitor.h>
+
+G_BEGIN_DECLS
+
+#define GDK_TYPE_WIN32_MONITOR           (gdk_win32_monitor_get_type ())
+#define GDK_WIN32_MONITOR(object)        (G_TYPE_CHECK_INSTANCE_CAST ((object), GDK_TYPE_WIN32_MONITOR, 
GdkWin32Monitor))
+#define GDK_IS_WIN32_MONITOR(object)     (G_TYPE_CHECK_INSTANCE_TYPE ((object), GDK_TYPE_WIN32_MONITOR))
+
+#ifdef GDK_COMPILATION
+typedef struct _GdkWin32Monitor      GdkWin32Monitor;
+#else
+typedef GdkMonitor GdkWin32Monitor;
+#endif
+typedef struct _GdkWin32MonitorClass GdkWin32MonitorClass;
+
+GDK_AVAILABLE_IN_3_22
+GType             gdk_win32_monitor_get_type            (void) G_GNUC_CONST;
+
+G_END_DECLS
+
+#endif  /* __GDK_WIN32_MONITOR_H__ */


[Date Prev][Date Next]   [Thread Prev][Thread Next]   [Thread Index] [Date Index] [Author Index]