Permalink
| /* | |
| * gdkscreen-x11.c | |
| * | |
| * Copyright 2001 Sun Microsystems Inc. | |
| * | |
| * Erwann Chenede <erwann.chenede@sun.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/>. | |
| */ | |
| #include "config.h" | |
| #include "gdkscreen-x11.h" | |
| #include "gdkdisplay-x11.h" | |
| #include "gdkprivate-x11.h" | |
| #include "xsettings-client.h" | |
| #include "gdkmonitor-x11.h" | |
| #include <glib.h> | |
| #include <stdlib.h> | |
| #include <string.h> | |
| #include <X11/Xatom.h> | |
| #ifdef HAVE_XFREE_XINERAMA | |
| #include <X11/extensions/Xinerama.h> | |
| #endif | |
| #ifdef HAVE_RANDR | |
| #include <X11/extensions/Xrandr.h> | |
| #endif | |
| #ifdef HAVE_XFIXES | |
| #include <X11/extensions/Xfixes.h> | |
| #endif | |
| static void gdk_x11_screen_dispose (GObject *object); | |
| static void gdk_x11_screen_finalize (GObject *object); | |
| static void init_randr_support (GdkX11Screen *screen); | |
| enum | |
| { | |
| WINDOW_MANAGER_CHANGED, | |
| LAST_SIGNAL | |
| }; | |
| static guint signals[LAST_SIGNAL] = { 0 }; | |
| G_DEFINE_TYPE (GdkX11Screen, gdk_x11_screen, G_TYPE_OBJECT) | |
| typedef struct _NetWmSupportedAtoms NetWmSupportedAtoms; | |
| struct _NetWmSupportedAtoms | |
| { | |
| Atom *atoms; | |
| gulong n_atoms; | |
| }; | |
| static void | |
| gdk_x11_screen_init (GdkX11Screen *screen) | |
| { | |
| } | |
| static void | |
| gdk_x11_screen_dispose (GObject *object) | |
| { | |
| GdkX11Screen *x11_screen = GDK_X11_SCREEN (object); | |
| int i; | |
| for (i = 0; i < 32; ++i) | |
| { | |
| if (x11_screen->subwindow_gcs[i]) | |
| { | |
| XFreeGC (x11_screen->xdisplay, x11_screen->subwindow_gcs[i]); | |
| x11_screen->subwindow_gcs[i] = 0; | |
| } | |
| } | |
| _gdk_x11_xsettings_finish (x11_screen); | |
| for (i = 0; i < x11_screen->nvisuals; i++) | |
| g_object_run_dispose (G_OBJECT (x11_screen->visuals[i])); | |
| G_OBJECT_CLASS (gdk_x11_screen_parent_class)->dispose (object); | |
| x11_screen->xdisplay = NULL; | |
| x11_screen->xscreen = NULL; | |
| x11_screen->xroot_window = None; | |
| x11_screen->wmspec_check_window = None; | |
| } | |
| static void | |
| gdk_x11_screen_finalize (GObject *object) | |
| { | |
| GdkX11Screen *x11_screen = GDK_X11_SCREEN (object); | |
| gint i; | |
| /* Visual Part */ | |
| for (i = 0; i < x11_screen->nvisuals; i++) | |
| g_object_unref (x11_screen->visuals[i]); | |
| g_free (x11_screen->visuals); | |
| g_free (x11_screen->window_manager_name); | |
| G_OBJECT_CLASS (gdk_x11_screen_parent_class)->finalize (object); | |
| } | |
| /** | |
| * gdk_x11_screen_get_monitor_output: | |
| * @screen: a #GdkX11Screen | |
| * @monitor_num: number of the monitor, between 0 and gdk_screen_get_n_monitors (screen) | |
| * | |
| * Gets the XID of the specified output/monitor. | |
| * If the X server does not support version 1.2 of the RANDR | |
| * extension, 0 is returned. | |
| * | |
| * Returns: the XID of the monitor | |
| * | |
| * Since: 2.14 | |
| */ | |
| XID | |
| gdk_x11_screen_get_monitor_output (GdkX11Screen *x11_screen, | |
| gint monitor_num) | |
| { | |
| GdkX11Display *x11_display = GDK_X11_DISPLAY (x11_screen->display); | |
| GdkX11Monitor *monitor; | |
| g_return_val_if_fail (monitor_num >= 0, None); | |
| g_return_val_if_fail (monitor_num < x11_display->monitors->len, None); | |
| monitor = x11_display->monitors->pdata[monitor_num]; | |
| return monitor->output; | |
| } | |
| static int | |
| get_current_desktop (GdkX11Screen *screen) | |
| { | |
| Display *display; | |
| Window win; | |
| Atom current_desktop, type; | |
| int format; | |
| unsigned long n_items, bytes_after; | |
| unsigned char *data_return = NULL; | |
| int workspace = 0; | |
| if (!gdk_x11_screen_supports_net_wm_hint (screen, | |
| g_intern_static_string ("_NET_CURRENT_DESKTOP"))) | |
| return workspace; | |
| display = GDK_DISPLAY_XDISPLAY (GDK_SCREEN_DISPLAY (screen)); | |
| win = XRootWindow (display, gdk_x11_screen_get_screen_number (screen)); | |
| current_desktop = XInternAtom (display, "_NET_CURRENT_DESKTOP", True); | |
| XGetWindowProperty (display, | |
| win, | |
| current_desktop, | |
| 0, G_MAXLONG, | |
| False, XA_CARDINAL, | |
| &type, &format, &n_items, &bytes_after, | |
| &data_return); | |
| if (type == XA_CARDINAL && format == 32 && n_items > 0) | |
| workspace = ((long *) data_return)[0]; | |
| if (data_return) | |
| XFree (data_return); | |
| return workspace; | |
| } | |
| void | |
| gdk_x11_screen_get_work_area (GdkX11Screen *x11_screen, | |
| GdkRectangle *area) | |
| { | |
| Atom workarea; | |
| Atom type; | |
| Window win; | |
| int format; | |
| gulong num; | |
| gulong leftovers; | |
| gulong max_len = 4 * 32; | |
| guchar *ret_workarea = NULL; | |
| long *workareas; | |
| int result; | |
| int desktop; | |
| Display *display; | |
| display = GDK_SCREEN_XDISPLAY (x11_screen); | |
| workarea = XInternAtom (display, "_NET_WORKAREA", True); | |
| /* Defaults in case of error */ | |
| area->x = 0; | |
| area->y = 0; | |
| area->width = WidthOfScreen (x11_screen->xscreen); | |
| area->height = HeightOfScreen (x11_screen->xscreen); | |
| if (!gdk_x11_screen_supports_net_wm_hint (x11_screen, | |
| g_intern_static_string ("_NET_WORKAREA"))) | |
| return; | |
| if (workarea == None) | |
| return; | |
| win = XRootWindow (display, gdk_x11_screen_get_screen_number (x11_screen)); | |
| result = XGetWindowProperty (display, | |
| win, | |
| workarea, | |
| 0, | |
| max_len, | |
| False, | |
| AnyPropertyType, | |
| &type, | |
| &format, | |
| &num, | |
| &leftovers, | |
| &ret_workarea); | |
| if (result != Success || | |
| type == None || | |
| format == 0 || | |
| leftovers || | |
| num % 4 != 0) | |
| goto out; | |
| desktop = get_current_desktop (x11_screen); | |
| if (desktop + 1 > num / 4) /* fvwm gets this wrong */ | |
| goto out; | |
| workareas = (long *) ret_workarea; | |
| area->x = workareas[desktop * 4]; | |
| area->y = workareas[desktop * 4 + 1]; | |
| area->width = workareas[desktop * 4 + 2]; | |
| area->height = workareas[desktop * 4 + 3]; | |
| area->x /= x11_screen->window_scale; | |
| area->y /= x11_screen->window_scale; | |
| area->width /= x11_screen->window_scale; | |
| area->height /= x11_screen->window_scale; | |
| out: | |
| if (ret_workarea) | |
| XFree (ret_workarea); | |
| } | |
| /** | |
| * gdk_x11_screen_get_xscreen: | |
| * @screen: a #GdkX11Screen | |
| * | |
| * Returns the screen of a #GdkX11Screen. | |
| * | |
| * Returns: (transfer none): an Xlib Screen* | |
| * | |
| * Since: 2.2 | |
| */ | |
| Screen * | |
| gdk_x11_screen_get_xscreen (GdkX11Screen *screen) | |
| { | |
| return screen->xscreen; | |
| } | |
| /** | |
| * gdk_x11_screen_get_screen_number: | |
| * @screen: a #GdkX11Screen | |
| * | |
| * Returns the index of a #GdkX11Screen. | |
| * | |
| * Returns: the position of @screen among the screens | |
| * of its display | |
| * | |
| * Since: 2.2 | |
| */ | |
| int | |
| gdk_x11_screen_get_screen_number (GdkX11Screen *screen) | |
| { | |
| return screen->screen_num; | |
| } | |
| static GdkX11Monitor * | |
| find_monitor_by_output (GdkX11Display *x11_display, XID output) | |
| { | |
| int i; | |
| for (i = 0; i < x11_display->monitors->len; i++) | |
| { | |
| GdkX11Monitor *monitor = x11_display->monitors->pdata[i]; | |
| if (monitor->output == output) | |
| return monitor; | |
| } | |
| return NULL; | |
| } | |
| static GdkSubpixelLayout | |
| translate_subpixel_order (int subpixel) | |
| { | |
| switch (subpixel) | |
| { | |
| case 1: return GDK_SUBPIXEL_LAYOUT_HORIZONTAL_RGB; | |
| case 2: return GDK_SUBPIXEL_LAYOUT_HORIZONTAL_BGR; | |
| case 3: return GDK_SUBPIXEL_LAYOUT_VERTICAL_RGB; | |
| case 4: return GDK_SUBPIXEL_LAYOUT_VERTICAL_BGR; | |
| case 5: return GDK_SUBPIXEL_LAYOUT_NONE; | |
| default: return GDK_SUBPIXEL_LAYOUT_UNKNOWN; | |
| } | |
| } | |
| static gboolean | |
| init_randr15 (GdkX11Screen *x11_screen, gboolean *changed) | |
| { | |
| #ifdef HAVE_RANDR15 | |
| GdkDisplay *display = GDK_SCREEN_DISPLAY (x11_screen); | |
| GdkX11Display *x11_display = GDK_X11_DISPLAY (display); | |
| XRRScreenResources *resources; | |
| RROutput primary_output = None; | |
| RROutput first_output = None; | |
| int i; | |
| XRRMonitorInfo *rr_monitors; | |
| int num_rr_monitors; | |
| int old_primary; | |
| if (!x11_display->have_randr15) | |
| return FALSE; | |
| resources = XRRGetScreenResourcesCurrent (x11_screen->xdisplay, | |
| x11_screen->xroot_window); | |
| if (!resources) | |
| return FALSE; | |
| rr_monitors = XRRGetMonitors (x11_screen->xdisplay, | |
| x11_screen->xroot_window, | |
| True, | |
| &num_rr_monitors); | |
| if (!rr_monitors) | |
| return FALSE; | |
| for (i = 0; i < x11_display->monitors->len; i++) | |
| { | |
| GdkX11Monitor *monitor = x11_display->monitors->pdata[i]; | |
| monitor->add = FALSE; | |
| monitor->remove = TRUE; | |
| } | |
| for (i = 0; i < num_rr_monitors; i++) | |
| { | |
| RROutput output = rr_monitors[i].outputs[0]; | |
| XRROutputInfo *output_info; | |
| GdkX11Monitor *monitor; | |
| GdkRectangle geometry; | |
| GdkRectangle newgeo; | |
| char *name; | |
| int refresh_rate = 0; | |
| gdk_x11_display_error_trap_push (display); | |
| output_info = XRRGetOutputInfo (x11_screen->xdisplay, resources, output); | |
| if (gdk_x11_display_error_trap_pop (display)) | |
| continue; | |
| if (output_info == NULL) | |
| continue; | |
| if (output_info->connection == RR_Disconnected) | |
| { | |
| XRRFreeOutputInfo (output_info); | |
| continue; | |
| } | |
| if (first_output == None) | |
| first_output = output; | |
| if (output_info->crtc) | |
| { | |
| XRRCrtcInfo *crtc = XRRGetCrtcInfo (x11_screen->xdisplay, resources, output_info->crtc); | |
| int j; | |
| for (j = 0; j < resources->nmode; j++) | |
| { | |
| XRRModeInfo *xmode = &resources->modes[j]; | |
| if (xmode->id == crtc->mode) | |
| { | |
| if (xmode->hTotal != 0 && xmode->vTotal != 0) | |
| refresh_rate = (1000 * xmode->dotClock) / (xmode->hTotal * xmode->vTotal); | |
| break; | |
| } | |
| } | |
| XRRFreeCrtcInfo (crtc); | |
| } | |
| monitor = find_monitor_by_output (x11_display, output); | |
| if (monitor) | |
| monitor->remove = FALSE; | |
| else | |
| { | |
| monitor = g_object_new (GDK_TYPE_X11_MONITOR, | |
| "display", display, | |
| NULL); | |
| monitor->output = output; | |
| monitor->add = TRUE; | |
| g_ptr_array_add (x11_display->monitors, monitor); | |
| } | |
| gdk_monitor_get_geometry (GDK_MONITOR (monitor), &geometry); | |
| name = g_strndup (output_info->name, output_info->nameLen); | |
| newgeo.x = rr_monitors[i].x / x11_screen->window_scale; | |
| newgeo.y = rr_monitors[i].y / x11_screen->window_scale; | |
| newgeo.width = rr_monitors[i].width / x11_screen->window_scale; | |
| newgeo.height = rr_monitors[i].height / x11_screen->window_scale; | |
| if (newgeo.x != geometry.x || | |
| newgeo.y != geometry.y || | |
| newgeo.width != geometry.width || | |
| newgeo.height != geometry.height || | |
| rr_monitors[i].mwidth != gdk_monitor_get_width_mm (GDK_MONITOR (monitor)) || | |
| rr_monitors[i].mheight != gdk_monitor_get_height_mm (GDK_MONITOR (monitor)) || | |
| g_strcmp0 (name, gdk_monitor_get_model (GDK_MONITOR (monitor)))) | |
| *changed = TRUE; | |
| gdk_monitor_set_position (GDK_MONITOR (monitor), newgeo.x, newgeo.y); | |
| gdk_monitor_set_size (GDK_MONITOR (monitor), newgeo.width, newgeo.height); | |
| g_object_notify (G_OBJECT (monitor), "workarea"); | |
| gdk_monitor_set_physical_size (GDK_MONITOR (monitor), | |
| rr_monitors[i].mwidth, | |
| rr_monitors[i].mheight); | |
| gdk_monitor_set_subpixel_layout (GDK_MONITOR (monitor), | |
| translate_subpixel_order (output_info->subpixel_order)); | |
| gdk_monitor_set_refresh_rate (GDK_MONITOR (monitor), refresh_rate); | |
| gdk_monitor_set_scale_factor (GDK_MONITOR (monitor), x11_screen->window_scale); | |
| gdk_monitor_set_model (GDK_MONITOR (monitor), name); | |
| g_free (name); | |
| if (rr_monitors[i].primary) | |
| primary_output = monitor->output; | |
| XRRFreeOutputInfo (output_info); | |
| } | |
| XRRFreeMonitors (rr_monitors); | |
| XRRFreeScreenResources (resources); | |
| for (i = x11_display->monitors->len - 1; i >= 0; i--) | |
| { | |
| GdkX11Monitor *monitor = x11_display->monitors->pdata[i]; | |
| if (monitor->add) | |
| { | |
| gdk_display_monitor_added (display, GDK_MONITOR (monitor)); | |
| *changed = TRUE; | |
| } | |
| else if (monitor->remove) | |
| { | |
| g_object_ref (monitor); | |
| g_ptr_array_remove (x11_display->monitors, monitor); | |
| gdk_display_monitor_removed (display, GDK_MONITOR (monitor)); | |
| g_object_unref (monitor); | |
| *changed = TRUE; | |
| } | |
| } | |
| old_primary = x11_display->primary_monitor; | |
| x11_display->primary_monitor = 0; | |
| for (i = 0; i < x11_display->monitors->len; ++i) | |
| { | |
| GdkX11Monitor *monitor = x11_display->monitors->pdata[i]; | |
| if (monitor->output == primary_output) | |
| { | |
| x11_display->primary_monitor = i; | |
| break; | |
| } | |
| /* No RandR1.3+ available or no primary set, fall back to prefer LVDS as primary if present */ | |
| if (primary_output == None && | |
| g_ascii_strncasecmp (gdk_monitor_get_model (GDK_MONITOR (monitor)), "LVDS", 4) == 0) | |
| { | |
| x11_display->primary_monitor = i; | |
| break; | |
| } | |
| /* No primary specified and no LVDS found */ | |
| if (monitor->output == first_output) | |
| x11_display->primary_monitor = i; | |
| } | |
| if (x11_display->primary_monitor != old_primary) | |
| *changed = TRUE; | |
| return x11_display->monitors->len > 0; | |
| #endif | |
| return FALSE; | |
| } | |
| static gboolean | |
| init_randr13 (GdkX11Screen *x11_screen, gboolean *changed) | |
| { | |
| #ifdef HAVE_RANDR | |
| GdkDisplay *display = GDK_SCREEN_DISPLAY (x11_screen); | |
| GdkX11Display *x11_display = GDK_X11_DISPLAY (display); | |
| XRRScreenResources *resources; | |
| RROutput primary_output = None; | |
| RROutput first_output = None; | |
| int i; | |
| int old_primary; | |
| if (!x11_display->have_randr13) | |
| return FALSE; | |
| resources = XRRGetScreenResourcesCurrent (x11_screen->xdisplay, | |
| x11_screen->xroot_window); | |
| if (!resources) | |
| return FALSE; | |
| for (i = 0; i < x11_display->monitors->len; i++) | |
| { | |
| GdkX11Monitor *monitor = x11_display->monitors->pdata[i]; | |
| monitor->add = FALSE; | |
| monitor->remove = TRUE; | |
| } | |
| for (i = 0; i < resources->noutput; ++i) | |
| { | |
| RROutput output = resources->outputs[i]; | |
| XRROutputInfo *output_info = | |
| XRRGetOutputInfo (x11_screen->xdisplay, resources, output); | |
| if (output_info->connection == RR_Disconnected) | |
| { | |
| XRRFreeOutputInfo (output_info); | |
| continue; | |
| } | |
| if (output_info->crtc) | |
| { | |
| GdkX11Monitor *monitor; | |
| XRRCrtcInfo *crtc = XRRGetCrtcInfo (x11_screen->xdisplay, resources, output_info->crtc); | |
| char *name; | |
| GdkRectangle geometry; | |
| GdkRectangle newgeo; | |
| int j; | |
| int refresh_rate = 0; | |
| for (j = 0; j < resources->nmode; j++) | |
| { | |
| XRRModeInfo *xmode = &resources->modes[j]; | |
| if (xmode->id == crtc->mode) | |
| { | |
| refresh_rate = (1000 * xmode->dotClock) / (xmode->hTotal *xmode->vTotal); | |
| break; | |
| } | |
| } | |
| monitor = find_monitor_by_output (x11_display, output); | |
| if (monitor) | |
| monitor->remove = FALSE; | |
| else | |
| { | |
| monitor = g_object_new (gdk_x11_monitor_get_type (), | |
| "display", display, | |
| NULL); | |
| monitor->output = output; | |
| monitor->add = TRUE; | |
| g_ptr_array_add (x11_display->monitors, monitor); | |
| } | |
| gdk_monitor_get_geometry (GDK_MONITOR (monitor), &geometry); | |
| name = g_strndup (output_info->name, output_info->nameLen); | |
| newgeo.x = crtc->x / x11_screen->window_scale; | |
| newgeo.y = crtc->y / x11_screen->window_scale; | |
| newgeo.width = crtc->width / x11_screen->window_scale; | |
| newgeo.height = crtc->height / x11_screen->window_scale; | |
| if (newgeo.x != geometry.x || | |
| newgeo.y != geometry.y || | |
| newgeo.width != geometry.width || | |
| newgeo.height != geometry.height || | |
| output_info->mm_width != gdk_monitor_get_width_mm (GDK_MONITOR (monitor)) || | |
| output_info->mm_height != gdk_monitor_get_height_mm (GDK_MONITOR (monitor)) || | |
| g_strcmp0 (name, gdk_monitor_get_model (GDK_MONITOR (monitor))) != 0) | |
| *changed = TRUE; | |
| gdk_monitor_set_position (GDK_MONITOR (monitor), newgeo.x, newgeo.y); | |
| gdk_monitor_set_size (GDK_MONITOR (monitor), newgeo.width, newgeo.height); | |
| g_object_notify (G_OBJECT (monitor), "workarea"); | |
| gdk_monitor_set_physical_size (GDK_MONITOR (monitor), | |
| output_info->mm_width, | |
| output_info->mm_height); | |
| gdk_monitor_set_subpixel_layout (GDK_MONITOR (monitor), | |
| translate_subpixel_order (output_info->subpixel_order)); | |
| gdk_monitor_set_refresh_rate (GDK_MONITOR (monitor), refresh_rate); | |
| gdk_monitor_set_scale_factor (GDK_MONITOR (monitor), x11_screen->window_scale); | |
| gdk_monitor_set_model (GDK_MONITOR (monitor), name); | |
| g_free (name); | |
| XRRFreeCrtcInfo (crtc); | |
| } | |
| XRRFreeOutputInfo (output_info); | |
| } | |
| if (resources->noutput > 0) | |
| first_output = resources->outputs[0]; | |
| XRRFreeScreenResources (resources); | |
| /* Which usable multihead data is not returned in non RandR 1.2+ X driver? */ | |
| for (i = x11_display->monitors->len - 1; i >= 0; i--) | |
| { | |
| GdkX11Monitor *monitor = x11_display->monitors->pdata[i]; | |
| if (monitor->add) | |
| { | |
| gdk_display_monitor_added (display, GDK_MONITOR (monitor)); | |
| *changed = TRUE; | |
| } | |
| else if (monitor->remove) | |
| { | |
| g_object_ref (monitor); | |
| g_ptr_array_remove (x11_display->monitors, monitor); | |
| gdk_display_monitor_removed (display, GDK_MONITOR (monitor)); | |
| g_object_unref (monitor); | |
| *changed = TRUE; | |
| } | |
| } | |
| old_primary = x11_display->primary_monitor; | |
| x11_display->primary_monitor = 0; | |
| primary_output = XRRGetOutputPrimary (x11_screen->xdisplay, | |
| x11_screen->xroot_window); | |
| for (i = 0; i < x11_display->monitors->len; ++i) | |
| { | |
| GdkX11Monitor *monitor = x11_display->monitors->pdata[i]; | |
| if (monitor->output == primary_output) | |
| { | |
| x11_display->primary_monitor = i; | |
| break; | |
| } | |
| /* No RandR1.3+ available or no primary set, fall back to prefer LVDS as primary if present */ | |
| if (primary_output == None && | |
| g_ascii_strncasecmp (gdk_monitor_get_model (GDK_MONITOR (monitor)), "LVDS", 4) == 0) | |
| { | |
| x11_display->primary_monitor = i; | |
| break; | |
| } | |
| /* No primary specified and no LVDS found */ | |
| if (monitor->output == first_output) | |
| x11_display->primary_monitor = i; | |
| } | |
| if (x11_display->primary_monitor != old_primary) | |
| *changed = TRUE; | |
| return x11_display->monitors->len > 0; | |
| #endif | |
| return FALSE; | |
| } | |
| static void | |
| init_no_multihead (GdkX11Screen *x11_screen, gboolean *changed) | |
| { | |
| GdkX11Display *x11_display = GDK_X11_DISPLAY (x11_screen->display); | |
| GdkX11Monitor *monitor; | |
| GdkRectangle geometry; | |
| int width_mm, height_mm; | |
| int width, height; | |
| int i; | |
| for (i = 0; i < x11_display->monitors->len; i++) | |
| { | |
| GdkX11Monitor *monitor = x11_display->monitors->pdata[i]; | |
| monitor->add = FALSE; | |
| monitor->remove = TRUE; | |
| } | |
| monitor = find_monitor_by_output (x11_display, 0); | |
| if (monitor) | |
| monitor->remove = FALSE; | |
| else | |
| { | |
| monitor = g_object_new (gdk_x11_monitor_get_type (), | |
| "display", x11_display, | |
| NULL); | |
| monitor->output = 0; | |
| monitor->add = TRUE; | |
| g_ptr_array_add (x11_display->monitors, monitor); | |
| } | |
| width_mm = WidthMMOfScreen (x11_screen->xscreen); | |
| height_mm = HeightMMOfScreen (x11_screen->xscreen); | |
| width = WidthOfScreen (x11_screen->xscreen); | |
| height = HeightOfScreen (x11_screen->xscreen); | |
| gdk_monitor_get_geometry (GDK_MONITOR (monitor), &geometry); | |
| if (0 != geometry.x || | |
| 0 != geometry.y || | |
| width != geometry.width || | |
| height != geometry.height || | |
| width_mm != gdk_monitor_get_width_mm (GDK_MONITOR (monitor)) || | |
| height_mm != gdk_monitor_get_height_mm (GDK_MONITOR (monitor))) | |
| *changed = TRUE; | |
| gdk_monitor_set_position (GDK_MONITOR (monitor), 0, 0); | |
| gdk_monitor_set_size (GDK_MONITOR (monitor), width, height); | |
| g_object_notify (G_OBJECT (monitor), "workarea"); | |
| gdk_monitor_set_physical_size (GDK_MONITOR (monitor), width_mm, height_mm); | |
| gdk_monitor_set_scale_factor (GDK_MONITOR (monitor), x11_screen->window_scale); | |
| if (x11_display->primary_monitor != 0) | |
| *changed = TRUE; | |
| x11_display->primary_monitor = 0; | |
| for (i = x11_display->monitors->len - 1; i >= 0; i--) | |
| { | |
| GdkX11Monitor *monitor = x11_display->monitors->pdata[i]; | |
| if (monitor->add) | |
| { | |
| gdk_display_monitor_added (GDK_DISPLAY (x11_display), GDK_MONITOR (monitor)); | |
| *changed = TRUE; | |
| } | |
| else if (monitor->remove) | |
| { | |
| g_object_ref (monitor); | |
| g_ptr_array_remove (x11_display->monitors, monitor); | |
| gdk_display_monitor_removed (GDK_DISPLAY (x11_display), GDK_MONITOR (monitor)); | |
| g_object_unref (monitor); | |
| *changed = TRUE; | |
| } | |
| } | |
| } | |
| static gboolean | |
| init_multihead (GdkX11Screen *screen) | |
| { | |
| gboolean any_changed = FALSE; | |
| if (!init_randr15 (screen, &any_changed) && | |
| !init_randr13 (screen, &any_changed)) | |
| init_no_multihead (screen, &any_changed); | |
| return any_changed; | |
| } | |
| GdkX11Screen * | |
| _gdk_x11_screen_new (GdkDisplay *display, | |
| gint screen_number, | |
| gboolean setup_display) | |
| { | |
| GdkX11Screen *x11_screen; | |
| GdkX11Display *display_x11 = GDK_X11_DISPLAY (display); | |
| const char *scale_str; | |
| x11_screen = g_object_new (GDK_TYPE_X11_SCREEN, NULL); | |
| x11_screen->display = display; | |
| x11_screen->xdisplay = display_x11->xdisplay; | |
| x11_screen->xscreen = ScreenOfDisplay (display_x11->xdisplay, screen_number); | |
| x11_screen->screen_num = screen_number; | |
| x11_screen->xroot_window = RootWindow (display_x11->xdisplay, screen_number); | |
| x11_screen->wmspec_check_window = None; | |
| /* we want this to be always non-null */ | |
| x11_screen->window_manager_name = g_strdup ("unknown"); | |
| scale_str = g_getenv ("GDK_SCALE"); | |
| if (scale_str) | |
| { | |
| x11_screen->fixed_window_scale = TRUE; | |
| x11_screen->window_scale = atol (scale_str); | |
| if (x11_screen->window_scale == 0) | |
| x11_screen->window_scale = 1; | |
| } | |
| else | |
| x11_screen->window_scale = 1; | |
| init_randr_support (x11_screen); | |
| init_multihead (x11_screen); | |
| _gdk_x11_screen_init_visuals (x11_screen, setup_display); | |
| return x11_screen; | |
| } | |
| void | |
| _gdk_x11_screen_set_window_scale (GdkX11Screen *x11_screen, | |
| gint scale) | |
| { | |
| GdkX11Display *x11_display = GDK_X11_DISPLAY (x11_screen->display); | |
| GList *toplevels, *l; | |
| int i; | |
| if (x11_screen->window_scale == scale) | |
| return; | |
| x11_screen->window_scale = scale; | |
| toplevels = gdk_x11_display_get_toplevel_windows (x11_screen->display); | |
| for (l = toplevels; l != NULL; l = l->next) | |
| { | |
| GdkWindow *window = l->data; | |
| _gdk_x11_window_set_window_scale (window, scale); | |
| } | |
| for (i = 0; i < x11_display->monitors->len; i++) | |
| { | |
| GdkMonitor *monitor = GDK_MONITOR (x11_display->monitors->pdata[i]); | |
| gdk_monitor_set_scale_factor (monitor, scale); | |
| } | |
| } | |
| static void | |
| init_randr_support (GdkX11Screen *x11_screen) | |
| { | |
| /* NB: This is also needed for XSettings, so don't remove. */ | |
| XSelectInput (GDK_SCREEN_XDISPLAY (x11_screen), | |
| x11_screen->xroot_window, | |
| StructureNotifyMask); | |
| #ifdef HAVE_RANDR | |
| if (!GDK_X11_DISPLAY (GDK_SCREEN_DISPLAY (x11_screen))->have_randr12) | |
| return; | |
| XRRSelectInput (GDK_SCREEN_XDISPLAY (x11_screen), | |
| x11_screen->xroot_window, | |
| RRScreenChangeNotifyMask | |
| | RRCrtcChangeNotifyMask | |
| | RROutputPropertyNotifyMask); | |
| #endif | |
| } | |
| static void | |
| process_monitors_change (GdkX11Screen *screen) | |
| { | |
| init_multihead (screen); | |
| } | |
| void | |
| _gdk_x11_screen_size_changed (GdkX11Screen *screen, | |
| const XEvent *event) | |
| { | |
| #ifdef HAVE_RANDR | |
| GdkX11Display *display_x11; | |
| display_x11 = GDK_X11_DISPLAY (GDK_SCREEN_DISPLAY (screen)); | |
| if (display_x11->have_randr13 && event->type == ConfigureNotify) | |
| return; | |
| XRRUpdateConfiguration ((XEvent *) event); | |
| #else | |
| if (event->type != ConfigureNotify) | |
| return; | |
| #endif | |
| process_monitors_change (screen); | |
| } | |
| void | |
| _gdk_x11_screen_get_edge_monitors (GdkX11Screen *x11_screen, | |
| gint *top, | |
| gint *bottom, | |
| gint *left, | |
| gint *right) | |
| { | |
| #ifdef HAVE_XFREE_XINERAMA | |
| gint top_most_pos = HeightOfScreen (x11_screen->xscreen); | |
| gint left_most_pos = WidthOfScreen (x11_screen->xscreen); | |
| gint bottom_most_pos = 0; | |
| gint right_most_pos = 0; | |
| gint i; | |
| XineramaScreenInfo *x_monitors; | |
| int x_n_monitors; | |
| #endif | |
| *top = *bottom = *left = *right = -1; | |
| #ifdef HAVE_XFREE_XINERAMA | |
| if (!XineramaIsActive (x11_screen->xdisplay)) | |
| return; | |
| x_monitors = XineramaQueryScreens (x11_screen->xdisplay, &x_n_monitors); | |
| if (x_n_monitors <= 0 || x_monitors == NULL) | |
| { | |
| if (x_monitors) | |
| XFree (x_monitors); | |
| return; | |
| } | |
| for (i = 0; i < x_n_monitors; i++) | |
| { | |
| if (left && left_most_pos > x_monitors[i].x_org) | |
| { | |
| left_most_pos = x_monitors[i].x_org; | |
| *left = i; | |
| } | |
| if (right && right_most_pos < x_monitors[i].x_org + x_monitors[i].width) | |
| { | |
| right_most_pos = x_monitors[i].x_org + x_monitors[i].width; | |
| *right = i; | |
| } | |
| if (top && top_most_pos > x_monitors[i].y_org) | |
| { | |
| top_most_pos = x_monitors[i].y_org; | |
| *top = i; | |
| } | |
| if (bottom && bottom_most_pos < x_monitors[i].y_org + x_monitors[i].height) | |
| { | |
| bottom_most_pos = x_monitors[i].y_org + x_monitors[i].height; | |
| *bottom = i; | |
| } | |
| } | |
| XFree (x_monitors); | |
| #endif | |
| } | |
| void | |
| _gdk_x11_screen_window_manager_changed (GdkX11Screen *screen) | |
| { | |
| g_signal_emit (screen, signals[WINDOW_MANAGER_CHANGED], 0); | |
| } | |
| gboolean | |
| gdk_x11_screen_get_setting (GdkX11Screen *x11_screen, | |
| const gchar *name, | |
| GValue *value) | |
| { | |
| const GValue *setting; | |
| if (x11_screen->xsettings == NULL) | |
| goto out; | |
| setting = g_hash_table_lookup (x11_screen->xsettings, name); | |
| if (setting == NULL) | |
| goto out; | |
| if (!g_value_transform (setting, value)) | |
| { | |
| g_warning ("Cannot transform xsetting %s of type %s to type %s\n", | |
| name, | |
| g_type_name (G_VALUE_TYPE (setting)), | |
| g_type_name (G_VALUE_TYPE (value))); | |
| goto out; | |
| } | |
| return TRUE; | |
| out: | |
| return _gdk_x11_screen_get_xft_setting (x11_screen, name, value); | |
| } | |
| static void | |
| cleanup_atoms(gpointer data) | |
| { | |
| NetWmSupportedAtoms *supported_atoms = data; | |
| if (supported_atoms->atoms) | |
| XFree (supported_atoms->atoms); | |
| g_free (supported_atoms); | |
| } | |
| static Window | |
| get_net_supporting_wm_check (GdkX11Screen *screen, | |
| Window window) | |
| { | |
| GdkDisplay *display; | |
| Atom type; | |
| gint format; | |
| gulong n_items; | |
| gulong bytes_after; | |
| guchar *data; | |
| Window value; | |
| display = screen->display; | |
| type = None; | |
| data = NULL; | |
| value = None; | |
| gdk_x11_display_error_trap_push (display); | |
| XGetWindowProperty (screen->xdisplay, window, | |
| gdk_x11_get_xatom_by_name_for_display (display, "_NET_SUPPORTING_WM_CHECK"), | |
| 0, G_MAXLONG, False, XA_WINDOW, &type, &format, | |
| &n_items, &bytes_after, &data); | |
| gdk_x11_display_error_trap_pop_ignored (display); | |
| if (type == XA_WINDOW) | |
| value = *(Window *)data; | |
| if (data) | |
| XFree (data); | |
| return value; | |
| } | |
| static void | |
| fetch_net_wm_check_window (GdkX11Screen *x11_screen) | |
| { | |
| GdkDisplay *display; | |
| Window window; | |
| GTimeVal tv; | |
| gint error; | |
| display = x11_screen->display; | |
| g_return_if_fail (GDK_X11_DISPLAY (display)->trusted_client); | |
| if (x11_screen->wmspec_check_window != None) | |
| return; /* already have it */ | |
| g_get_current_time (&tv); | |
| if (ABS (tv.tv_sec - x11_screen->last_wmspec_check_time) < 15) | |
| return; /* we've checked recently */ | |
| window = get_net_supporting_wm_check (x11_screen, x11_screen->xroot_window); | |
| if (window == None) | |
| return; | |
| if (window != get_net_supporting_wm_check (x11_screen, window)) | |
| return; | |
| gdk_x11_display_error_trap_push (display); | |
| /* Find out if this WM goes away, so we can reset everything. */ | |
| XSelectInput (x11_screen->xdisplay, window, StructureNotifyMask); | |
| error = gdk_x11_display_error_trap_pop (display); | |
| if (!error) | |
| { | |
| /* We check the window property again because after XGetWindowProperty() | |
| * and before XSelectInput() the window may have been recycled in such a | |
| * way that XSelectInput() doesn't fail but the window is no longer what | |
| * we want. | |
| */ | |
| if (window != get_net_supporting_wm_check (x11_screen, window)) | |
| return; | |
| x11_screen->wmspec_check_window = window; | |
| x11_screen->last_wmspec_check_time = tv.tv_sec; | |
| x11_screen->need_refetch_net_supported = TRUE; | |
| x11_screen->need_refetch_wm_name = TRUE; | |
| /* Careful, reentrancy */ | |
| _gdk_x11_screen_window_manager_changed (x11_screen); | |
| } | |
| } | |
| /** | |
| * gdk_x11_screen_supports_net_wm_hint: | |
| * @screen: the relevant #GdkX11Screen. | |
| * @property: a property atom. | |
| * | |
| * This function is specific to the X11 backend of GDK, and indicates | |
| * whether the window manager supports a certain hint from the | |
| * [Extended Window Manager Hints](http://www.freedesktop.org/Standards/wm-spec) specification. | |
| * | |
| * When using this function, keep in mind that the window manager | |
| * can change over time; so you shouldn’t use this function in | |
| * a way that impacts persistent application state. A common bug | |
| * is that your application can start up before the window manager | |
| * does when the user logs in, and before the window manager starts | |
| * gdk_x11_screen_supports_net_wm_hint() will return %FALSE for every property. | |
| * You can monitor the window_manager_changed signal on #GdkX11Screen to detect | |
| * a window manager change. | |
| * | |
| * Returns: %TRUE if the window manager supports @property | |
| * | |
| * Since: 2.2 | |
| **/ | |
| gboolean | |
| gdk_x11_screen_supports_net_wm_hint (GdkX11Screen *x11_screen, | |
| GdkAtom property) | |
| { | |
| gulong i; | |
| NetWmSupportedAtoms *supported_atoms; | |
| GdkDisplay *display; | |
| Atom atom; | |
| display = x11_screen->display; | |
| if (!G_LIKELY (GDK_X11_DISPLAY (display)->trusted_client)) | |
| return FALSE; | |
| supported_atoms = g_object_get_data (G_OBJECT (x11_screen), "gdk-net-wm-supported-atoms"); | |
| if (!supported_atoms) | |
| { | |
| supported_atoms = g_new0 (NetWmSupportedAtoms, 1); | |
| g_object_set_data_full (G_OBJECT (x11_screen), "gdk-net-wm-supported-atoms", supported_atoms, cleanup_atoms); | |
| } | |
| fetch_net_wm_check_window (x11_screen); | |
| if (x11_screen->wmspec_check_window == None) | |
| return FALSE; | |
| if (x11_screen->need_refetch_net_supported) | |
| { | |
| /* WM has changed since we last got the supported list, | |
| * refetch it. | |
| */ | |
| Atom type; | |
| gint format; | |
| gulong bytes_after; | |
| x11_screen->need_refetch_net_supported = FALSE; | |
| if (supported_atoms->atoms) | |
| XFree (supported_atoms->atoms); | |
| supported_atoms->atoms = NULL; | |
| supported_atoms->n_atoms = 0; | |
| XGetWindowProperty (GDK_DISPLAY_XDISPLAY (display), x11_screen->xroot_window, | |
| gdk_x11_get_xatom_by_name_for_display (display, "_NET_SUPPORTED"), | |
| 0, G_MAXLONG, False, XA_ATOM, &type, &format, | |
| &supported_atoms->n_atoms, &bytes_after, | |
| (guchar **)&supported_atoms->atoms); | |
| if (type != XA_ATOM) | |
| return FALSE; | |
| } | |
| if (supported_atoms->atoms == NULL) | |
| return FALSE; | |
| atom = gdk_x11_atom_to_xatom_for_display (display, property); | |
| for (i = 0; i < supported_atoms->n_atoms; i++) | |
| { | |
| if (supported_atoms->atoms[i] == atom) | |
| return TRUE; | |
| } | |
| return FALSE; | |
| } | |
| /** | |
| * gdk_x11_screen_get_window_manager_name: | |
| * @screen: a #GdkX11Screen | |
| * | |
| * Returns the name of the window manager for @screen. | |
| * | |
| * Returns: the name of the window manager screen @screen, or | |
| * "unknown" if the window manager is unknown. The string is owned by GDK | |
| * and should not be freed. | |
| * | |
| * Since: 2.2 | |
| **/ | |
| const char* | |
| gdk_x11_screen_get_window_manager_name (GdkX11Screen *x11_screen) | |
| { | |
| GdkDisplay *display; | |
| display = x11_screen->display; | |
| if (!G_LIKELY (GDK_X11_DISPLAY (display)->trusted_client)) | |
| return x11_screen->window_manager_name; | |
| fetch_net_wm_check_window (x11_screen); | |
| if (x11_screen->need_refetch_wm_name) | |
| { | |
| /* Get the name of the window manager */ | |
| x11_screen->need_refetch_wm_name = FALSE; | |
| g_free (x11_screen->window_manager_name); | |
| x11_screen->window_manager_name = g_strdup ("unknown"); | |
| if (x11_screen->wmspec_check_window != None) | |
| { | |
| Atom type; | |
| gint format; | |
| gulong n_items; | |
| gulong bytes_after; | |
| gchar *name; | |
| name = NULL; | |
| gdk_x11_display_error_trap_push (display); | |
| XGetWindowProperty (GDK_DISPLAY_XDISPLAY (display), | |
| x11_screen->wmspec_check_window, | |
| gdk_x11_get_xatom_by_name_for_display (display, | |
| "_NET_WM_NAME"), | |
| 0, G_MAXLONG, False, | |
| gdk_x11_get_xatom_by_name_for_display (display, | |
| "UTF8_STRING"), | |
| &type, &format, | |
| &n_items, &bytes_after, | |
| (guchar **)&name); | |
| gdk_x11_display_error_trap_pop_ignored (display); | |
| if (name != NULL) | |
| { | |
| g_free (x11_screen->window_manager_name); | |
| x11_screen->window_manager_name = g_strdup (name); | |
| XFree (name); | |
| } | |
| } | |
| } | |
| return x11_screen->window_manager_name; | |
| } | |
| static void | |
| gdk_x11_screen_class_init (GdkX11ScreenClass *klass) | |
| { | |
| GObjectClass *object_class = G_OBJECT_CLASS (klass); | |
| object_class->dispose = gdk_x11_screen_dispose; | |
| object_class->finalize = gdk_x11_screen_finalize; | |
| signals[WINDOW_MANAGER_CHANGED] = | |
| g_signal_new (g_intern_static_string ("window-manager-changed"), | |
| G_OBJECT_CLASS_TYPE (object_class), | |
| G_SIGNAL_RUN_LAST, | |
| G_STRUCT_OFFSET (GdkX11ScreenClass, window_manager_changed), | |
| NULL, NULL, | |
| g_cclosure_marshal_VOID__VOID, | |
| G_TYPE_NONE, | |
| 0); | |
| } | |
| static guint32 | |
| get_netwm_cardinal_property (GdkX11Screen *x11_screen, | |
| const gchar *name) | |
| { | |
| GdkAtom atom; | |
| guint32 prop = 0; | |
| Atom type; | |
| gint format; | |
| gulong nitems; | |
| gulong bytes_after; | |
| guchar *data; | |
| atom = g_intern_static_string (name); | |
| if (!gdk_x11_screen_supports_net_wm_hint (x11_screen, atom)) | |
| return 0; | |
| XGetWindowProperty (x11_screen->xdisplay, | |
| x11_screen->xroot_window, | |
| gdk_x11_get_xatom_by_name_for_display (GDK_SCREEN_DISPLAY (x11_screen), name), | |
| 0, G_MAXLONG, | |
| False, XA_CARDINAL, &type, &format, &nitems, | |
| &bytes_after, &data); | |
| if (type == XA_CARDINAL) | |
| { | |
| prop = *(gulong *)data; | |
| XFree (data); | |
| } | |
| return prop; | |
| } | |
| /** | |
| * gdk_x11_screen_get_number_of_desktops: | |
| * @screen: a #GdkX11Screen | |
| * | |
| * Returns the number of workspaces for @screen when running under a | |
| * window manager that supports multiple workspaces, as described | |
| * in the | |
| * [Extended Window Manager Hints](http://www.freedesktop.org/Standards/wm-spec) specification. | |
| * | |
| * Returns: the number of workspaces, or 0 if workspaces are not supported | |
| * | |
| * Since: 3.10 | |
| */ | |
| guint32 | |
| gdk_x11_screen_get_number_of_desktops (GdkX11Screen *screen) | |
| { | |
| return get_netwm_cardinal_property (screen, "_NET_NUMBER_OF_DESKTOPS"); | |
| } | |
| /** | |
| * gdk_x11_screen_get_current_desktop: | |
| * @screen: a #GdkX11Screen | |
| * | |
| * Returns the current workspace for @screen when running under a | |
| * window manager that supports multiple workspaces, as described | |
| * in the | |
| * [Extended Window Manager Hints](http://www.freedesktop.org/Standards/wm-spec) specification. | |
| * | |
| * Returns: the current workspace, or 0 if workspaces are not supported | |
| * | |
| * Since: 3.10 | |
| */ | |
| guint32 | |
| gdk_x11_screen_get_current_desktop (GdkX11Screen *screen) | |
| { | |
| return get_netwm_cardinal_property (screen, "_NET_CURRENT_DESKTOP"); | |
| } |