/* * 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 "gdkprivate-win32.h" #include "gdkdisplay-win32.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; BOOL 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 #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; guint scale; 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.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); /* This is temporary, scale will be applied below */ w32mon->work_rect = rect; if (data->display->has_fixed_scale) scale = data->display->surface_scale; else { /* First acquire the scale using the current screen */ scale = _gdk_win32_display_get_monitor_scale_factor (data->display, NULL, NULL, NULL); /* acquire the scale using the monitor which the window is nearest on Windows 8.1+ */ if (data->display->have_at_least_win81) { HMONITOR hmonitor; POINT pt; /* Not subtracting _gdk_offset_x and _gdk_offset_y because they will only * be added later on, in _gdk_win32_display_get_monitor_list(). */ pt.x = w32mon->work_rect.x + w32mon->work_rect.width / 2; pt.y = w32mon->work_rect.y + w32mon->work_rect.height / 2; hmonitor = MonitorFromPoint (pt, MONITOR_DEFAULTTONEAREST); scale = _gdk_win32_display_get_monitor_scale_factor (data->display, hmonitor, NULL, NULL); } } gdk_monitor_set_scale_factor (mon, scale); /* Now apply the scale to the work rectangle */ w32mon->work_rect.x /= scale; w32mon->work_rect.y /= scale; w32mon->work_rect.width /= scale; w32mon->work_rect.height /= scale; rect.x = monitor_info.rcMonitor.left / scale; rect.y = monitor_info.rcMonitor.top / scale; rect.width = (monitor_info.rcMonitor.right - monitor_info.rcMonitor.left) / scale; rect.height = (monitor_info.rcMonitor.bottom - monitor_info.rcMonitor.top) / scale; gdk_monitor_set_position (mon, rect.x, rect.y); gdk_monitor_set_size (mon, rect.width, rect.height); 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; } static void prune_monitors (EnumMonitorData *data) { gint i; for (i = 0; i < data->monitors->len; i++) { GdkWin32Monitor *m; m = g_ptr_array_index (data->monitors, i); if (m->remove) g_ptr_array_remove_index (data->monitors, i--); } } 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); prune_monitors (&data); if (data.monitors->len == 0 && data.have_monitor_devices) { /* We thought we had monitors, but enumeration eventually failed, and * we have none. Try again, this time making stuff up as we go. */ data.have_monitor_devices = FALSE; EnumDisplayMonitors (NULL, NULL, enum_monitor, (LPARAM) &data); prune_monitors (&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); /* 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; }