From 026cb53875244d252b55110dd91efacf29a1657f Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=D0=A0=D1=83=D1=81=D0=BB=D0=B0=D0=BD=20=D0=98=D0=B6=D0=B1?= =?UTF-8?q?=D1=83=D0=BB=D0=B0=D1=82=D0=BE=D0=B2?= Date: Wed, 20 Apr 2016 07:36:00 +0000 Subject: [PATCH] win32: Port to new monitor api --- configure.ac | 50 ++- gdk/win32/Makefile.am | 8 +- gdk/win32/gdkdisplay-win32.c | 208 +++++++++ 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, 1250 insertions(+), 246 deletions(-) create mode 100644 gdk/win32/gdkmonitor-win32.c create mode 100644 gdk/win32/gdkmonitor-win32.h create mode 100644 gdk/win32/gdkwin32monitor.h diff --git a/configure.ac b/configure.ac index 4cdfe2ef38..702cb04867 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 + ] + ) + 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 +#include +#include + ], + [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 33a0d617db..5b3af48574 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 71eaa0b70f..1b25d1865a 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); } @@ -676,6 +847,39 @@ 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) { @@ -731,5 +935,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 cbd4b5d7a6..009b380d16 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 0000000000..bae9320272 --- /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 . + */ + +#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 +#include + +#include +#include +#include + +#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 0000000000..c32f403169 --- /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 . + */ + +#ifndef __GDK_WIN32_MONITOR_PRIVATE_H__ +#define __GDK_WIN32_MONITOR_PRIVATE_H__ + +#include +#include +#include + +#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 2a8d1015e1..0b5d104e06 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 -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; - rect = screen->monitors[0].rect; - for (i = 1; i < screen->num_monitors; i++) - gdk_rectangle_union (&rect, &screen->monitors[i].rect, &rect); + monitor_count = gdk_display_get_n_monitors (display); + monitor = gdk_display_get_monitor (display, 0); + gdk_monitor_get_geometry (monitor, &result); - screen->root_window->width = rect.width; - screen->root_window->height = rect.height; + 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; + + 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 * @@ -521,84 +410,6 @@ gdk_win32_screen_get_root_window (GdkScreen *screen) return GDK_WIN32_SCREEN (screen)->root_window; } -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) { @@ -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 bf8b32ba0a..e098366e0f 100644 --- a/gdk/win32/gdkwin32.h +++ b/gdk/win32/gdkwin32.h @@ -35,6 +35,7 @@ #include #include #include +#include #include #undef __GDKWIN32_H_INSIDE__ diff --git a/gdk/win32/gdkwin32monitor.h b/gdk/win32/gdkwin32monitor.h new file mode 100644 index 0000000000..93762bda7e --- /dev/null +++ b/gdk/win32/gdkwin32monitor.h @@ -0,0 +1,50 @@ +/* + * gdkwin32monitor.h + * + * Copyright 2016 Red Hat, Inc. + * + * Matthias Clasen + * Руслан Ижбулатов + * + * 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 . + */ + +#ifndef __GDK_WIN32_MONITOR_H__ +#define __GDK_WIN32_MONITOR_H__ + +#if !defined (__GDKWIN32_H_INSIDE__) && !defined (GDK_COMPILATION) +#error "Only can be included directly." +#endif + +#include + +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__ */