diff options
author | William Wold <[email protected]> | 2019-06-19 18:30:19 +0000 |
---|---|---|
committer | raveit65 <[email protected]> | 2019-06-21 18:47:54 +0200 |
commit | 8c61fc25221e6ef102c77a859c6a44b9b4e25cb6 (patch) | |
tree | 5f75a36b9ef9a2687b550bf21143a78c8f5a8933 /mate-panel/panel-multiscreen.c | |
parent | 8cb8f197fe2cf2babae6824b2dd89c4b712eaa19 (diff) | |
download | mate-panel-8c61fc25221e6ef102c77a859c6a44b9b4e25cb6.tar.bz2 mate-panel-8c61fc25221e6ef102c77a859c6a44b9b4e25cb6.tar.xz |
Rename panel-multiscreen files panel-multimonitor
Diffstat (limited to 'mate-panel/panel-multiscreen.c')
-rw-r--r-- | mate-panel/panel-multiscreen.c | 703 |
1 files changed, 0 insertions, 703 deletions
diff --git a/mate-panel/panel-multiscreen.c b/mate-panel/panel-multiscreen.c deleted file mode 100644 index 04f42666..00000000 --- a/mate-panel/panel-multiscreen.c +++ /dev/null @@ -1,703 +0,0 @@ -/* - * panel-multiscreen.c: Multi-screen and Xinerama support for the panel. - * - * Copyright (C) 2001 George Lebl <[email protected]> - * 2002 Sun Microsystems Inc. - * - * This program is free software; you can redistribute it and/or - * modify it under the terms of the GNU General Public License as - * published by the Free Software Foundation; either version 2 of the - * License, or (at your option) any later version. - * - * This program 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 General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with this program; if not, write to the Free Software - * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 - * USA - * - * Authors: George Lebl <[email protected]>, - * Mark McLoughlin <[email protected]> - */ - -#include <config.h> - -#include <X11/Xatom.h> -#include <X11/Xlib.h> -#include <X11/extensions/Xrandr.h> -#include <gdk/gdkx.h> - -#include "panel-multiscreen.h" - -#include <string.h> - -static int screens = 0; -static int *monitors = NULL; -static GdkRectangle **geometries = NULL; -static gboolean initialized = FALSE; -static gboolean have_randr = FALSE; -static guint reinit_id = 0; - -#ifdef HAVE_RANDR -static gboolean -_panel_multiscreen_output_should_be_first (Display *xdisplay, - RROutput output, - XRROutputInfo *info, - RROutput primary) -{ - if (primary) - return output == primary; - - Atom connector_type_atom; - Atom actual_type; - int actual_format; - unsigned long nitems; - unsigned long bytes_after; - unsigned char *prop; - char *connector_type; - gboolean retval; - - connector_type_atom = XInternAtom (xdisplay, "ConnectorType", False); - - if (XRRGetOutputProperty (xdisplay, output, connector_type_atom, - 0, 100, False, False, None, - &actual_type, &actual_format, - &nitems, &bytes_after, &prop) == Success) { - if (actual_type == XA_ATOM && nitems == 1 && actual_format == 32) { - connector_type = XGetAtomName (xdisplay, prop[0]); - retval = g_strcmp0 (connector_type, "Panel") == 0; - XFree (connector_type); - return retval; - } - } - - /* Fallback (see https://bugs.freedesktop.org/show_bug.cgi?id=26736) - * "LVDS" is the oh-so-intuitive name that X gives to laptop LCDs. - * It can actually be LVDS0, LVDS-0, Lvds, etc. - */ - return (g_ascii_strncasecmp (info->name, "LVDS", strlen ("LVDS")) == 0); -} -#endif - -static gboolean -panel_multiscreen_get_randr_monitors_for_screen (GdkScreen *screen, - int *monitors_ret, - GdkRectangle **geometries_ret) -{ -#ifdef HAVE_RANDR - GdkDisplay *display; - GdkMonitor *monitor; - Display *xdisplay; - Window xroot; - XRRScreenResources *resources; - RROutput primary; - GArray *geometries; - int scale; - int i; - - if (!have_randr) - return FALSE; - - /* GTK+ 2.14.x uses the Xinerama API, instead of RANDR, to get the - * monitor geometries. It does this to avoid calling - * XRRGetScreenResources(), which is slow as it re-detects all the - * monitors --- note that XRRGetScreenResourcesCurrent() had not been - * introduced yet. Using Xinerama in GTK+ has the bad side effect that - * gdk_screen_get_monitor_plug_name() will return NULL, as Xinerama - * does not provide that information, unlike RANDR. - * - * Here we need to identify the output names, so that we can put the - * built-in LCD in a laptop *before* all other outputs. This is so - * that mate-panel will normally prefer to appear on the "native" - * display rather than on an external monitor. - * - * To get the output names and geometries, we will not use - * gdk_screen_get_n_monitors() and friends, but rather we will call - * XRR*() directly. - * - * See https://bugzilla.novell.com/show_bug.cgi?id=479684 for this - * particular bug, and and - * http://bugzilla.gnome.org/show_bug.cgi?id=562944 for a more - * long-term solution. - */ - - xdisplay = GDK_SCREEN_XDISPLAY (screen); - xroot = GDK_WINDOW_XID (gdk_screen_get_root_window (screen)); - - resources = XRRGetScreenResourcesCurrent (xdisplay, xroot); - if (resources->noutput == 0) { - /* This might happen if nothing tried to get randr - * resources from the server before, so we need an - * active probe. See comment #27 in - * https://bugzilla.gnome.org/show_bug.cgi?id=597101 */ - XRRFreeScreenResources (resources); - resources = XRRGetScreenResources (xdisplay, xroot); - } - - if (!resources) - return FALSE; - - primary = XRRGetOutputPrimary (xdisplay, xroot); - display = gdk_screen_get_display (screen); - monitor = gdk_display_get_primary_monitor (display); - - /* Use scale factor to bring geometries down to device pixels to support HiDPI displays */ - scale = gdk_monitor_get_scale_factor (monitor); - - geometries = g_array_sized_new (FALSE, FALSE, - sizeof (GdkRectangle), - resources->noutput); - - for (i = 0; i < resources->noutput; i++) { - XRROutputInfo *output; - - output = XRRGetOutputInfo (xdisplay, resources, - resources->outputs[i]); - - if (output->connection != RR_Disconnected && - output->crtc != 0) { - XRRCrtcInfo *crtc; - GdkRectangle rect; - - crtc = XRRGetCrtcInfo (xdisplay, resources, - output->crtc); - - rect.x = crtc->x / scale; - rect.y = crtc->y / scale; - rect.width = crtc->width / scale; - rect.height = crtc->height / scale; - - XRRFreeCrtcInfo (crtc); - - if (_panel_multiscreen_output_should_be_first (xdisplay, - resources->outputs[i], - output, primary)) - g_array_prepend_vals (geometries, &rect, 1); - else - g_array_append_vals (geometries, &rect, 1); - } - - XRRFreeOutputInfo (output); - } - - XRRFreeScreenResources (resources); - - if (geometries->len == 0) { - /* This can happen in at least one case: - * https://bugzilla.novell.com/show_bug.cgi?id=543876 where all - * monitors appear disconnected (possibly because the screen - * is behing a KVM switch) -- see comment #8. - * There might be other cases too, so we stay on the safe side. - */ - g_array_free (geometries, TRUE); - return FALSE; - } - - *monitors_ret = geometries->len; - *geometries_ret = (GdkRectangle *) g_array_free (geometries, FALSE); - - return TRUE; -#else - return FALSE; -#endif -} - -static void -panel_multiscreen_get_gdk_monitors_for_screen (GdkScreen *screen, - int *monitors_ret, - GdkRectangle **geometries_ret) -{ - GdkDisplay *display; - int num_monitors; - GdkRectangle *geometries; - int i; - - display = gdk_screen_get_display (screen); - num_monitors = gdk_display_get_n_monitors (display); - geometries = g_new (GdkRectangle, num_monitors); - - for (i = 0; i < num_monitors; i++) - gdk_monitor_get_geometry (gdk_display_get_monitor (display, i), &(geometries[i])); - - *monitors_ret = num_monitors; - *geometries_ret = geometries; -} - -static void -panel_multiscreen_get_raw_monitors_for_screen (GdkScreen *screen, - int *monitors_ret, - GdkRectangle **geometries_ret) -{ - gboolean res; - - *monitors_ret = 0; - *geometries_ret = NULL; - - res = panel_multiscreen_get_randr_monitors_for_screen (screen, - monitors_ret, - geometries_ret); - if (res && *monitors_ret > 0) - return; - - panel_multiscreen_get_gdk_monitors_for_screen (screen, - monitors_ret, - geometries_ret); -} - -static inline gboolean -rectangle_overlaps (GdkRectangle *a, - GdkRectangle *b) -{ - return gdk_rectangle_intersect (a, b, NULL); -} - -static long -pixels_in_rectangle (GdkRectangle *r) -{ - return (long) (r->width * r->height); -} - -static void -panel_multiscreen_compress_overlapping_monitors (int *num_monitors_inout, - GdkRectangle **geometries_inout) -{ - int num_monitors; - GdkRectangle *geometries; - int i; - - num_monitors = *num_monitors_inout; - geometries = *geometries_inout; - - /* http://bugzilla.gnome.org/show_bug.cgi?id=530969 - * https://bugzilla.novell.com/show_bug.cgi?id=310208 - * and many other such bugs... - * - * RANDR sometimes gives us monitors that overlap (i.e. outputs whose - * bounding rectangles overlap). This is sometimes right and sometimes - * wrong: - * - * * Right - two 1024x768 outputs at the same offset (0, 0) that show - * the same thing. Think "laptop plus projector with the same - * resolution". - * - * * Wrong - one 1280x1024 output ("laptop internal LCD") and another - * 1024x768 output ("external monitor"), both at offset (0, 0). - * There is no way for the monitor with the small resolution to - * show the complete image from the laptop's LCD, unless one uses - * panning (but nobody wants panning, right!?). - * - * With overlapping monitors, we may end up placing the panel with - * respect to the "wrong" one. This is always wrong, as the panel - * appears "in the middle of the screen" of the monitor with the - * smaller resolution, instead of at the edge. - * - * Our strategy is to find the subsets of overlapping monitors, and - * "compress" each such set to being like if there were a single - * monitor with the biggest resolution of each of that set's monitors. - * Say we have four monitors - * - * A, B, C, D - * - * where B and D overlap. In that case, we'll generate a new list that - * looks like - * - * A, MAX(B, D), C - * - * with three monitors. - * - * NOTE FOR THE FUTURE: We could avoid most of this mess if we had a - * concept of a "primary monitor". Also, we could look at each - * output's name or properties to see if it is the built-in LCD in a - * laptop. However, with GTK+ 2.14.x we don't get output names, since - * it gets the list outputs from Xinerama, not RANDR (and Xinerama - * doesn't provide output names). - */ - - for (i = 0; i < num_monitors; i++) { - long max_pixels; - int j; - - max_pixels = pixels_in_rectangle (&geometries[i]); - - j = i + 1; - - while (j < num_monitors) { - if (rectangle_overlaps (&geometries[i], - &geometries[j])) { - long pixels; - - pixels = pixels_in_rectangle (&geometries[j]); - if (pixels > max_pixels) { - max_pixels = pixels; - /* keep the maximum */ - geometries[i] = geometries[j]; - } - - /* Shift the remaining monitors to the left */ - if (num_monitors - j - 1 > 0) - memmove (&geometries[j], - &geometries[j + 1], - sizeof (geometries[0]) * (num_monitors - j - 1)); - - num_monitors--; - g_assert (num_monitors > 0); - } else - j++; - } - } - - *num_monitors_inout = num_monitors; - *geometries_inout = geometries; -} - -static void -panel_multiscreen_get_monitors_for_screen (GdkScreen *screen, - int *monitors_ret, - GdkRectangle **geometries_ret) -{ - panel_multiscreen_get_raw_monitors_for_screen (screen, - monitors_ret, - geometries_ret); - panel_multiscreen_compress_overlapping_monitors (monitors_ret, - geometries_ret); -} - -static gboolean -panel_multiscreen_reinit_idle (gpointer data) -{ - panel_multiscreen_reinit (); - reinit_id = 0; - - return FALSE; -} - -static void -panel_multiscreen_queue_reinit (void) -{ - if (reinit_id) - return; - - reinit_id = g_idle_add (panel_multiscreen_reinit_idle, NULL); -} - -static void -panel_multiscreen_init_randr (GdkDisplay *display) -{ -#ifdef HAVE_RANDR - Display *xdisplay; - int event_base, error_base; -#endif - - have_randr = FALSE; - -#ifdef HAVE_RANDR - xdisplay = GDK_DISPLAY_XDISPLAY (display); - - /* We don't remember the event/error bases, as we expect to get "screen - * changed" events from GdkScreen instead. - */ - - if (XRRQueryExtension (xdisplay, &event_base, &error_base)) { - int major, minor; - - XRRQueryVersion (xdisplay, &major, &minor); - if ((major == 1 && minor >= 3) || major > 1) - have_randr = TRUE; - } -#endif -} - -void -panel_multiscreen_init (void) -{ - GdkDisplay *display; - - if (initialized) - return; - - display = gdk_display_get_default (); - screens = 1; - - panel_multiscreen_init_randr (display); - - monitors = g_new0 (int, screens); - geometries = g_new0 (GdkRectangle *, screens); - - GdkScreen *screen; - - screen = gdk_display_get_default_screen (display); - - /* We connect to both signals to be on the safe side, but in - * theory, it should be enough to only connect to - * monitors-changed. Since we'll likely get two signals, we do - * the real callback in the idle loop. */ - g_signal_connect (screen, "size-changed", - G_CALLBACK (panel_multiscreen_queue_reinit), NULL); - g_signal_connect (screen, "monitors-changed", - G_CALLBACK (panel_multiscreen_queue_reinit), NULL); - - panel_multiscreen_get_monitors_for_screen (screen, - &(monitors[0]), - &(geometries[0])); - - initialized = TRUE; -} - -void -panel_multiscreen_reinit (void) -{ - GdkScreen *screen; - GList *toplevels, *l; - - if (monitors) - g_free (monitors); - - if (geometries) { - int j; - - for (j = 0; j < screens; j++) - g_free (geometries[j]); - g_free (geometries); - } - - screen = gdk_screen_get_default (); - g_signal_handlers_disconnect_by_func (screen, panel_multiscreen_queue_reinit, NULL); - - initialized = FALSE; - panel_multiscreen_init (); - - toplevels = gtk_window_list_toplevels (); - - for (l = toplevels; l; l = l->next) - gtk_widget_queue_resize (l->data); - - g_list_free (toplevels); -} - -int -panel_multiscreen_screens (void) -{ - return screens; -} - -int -panel_multiscreen_monitors (GdkScreen *screen) -{ - int n_screen; - - n_screen = gdk_x11_screen_get_screen_number (screen); - - g_return_val_if_fail (n_screen >= 0 && n_screen < screens, 1); - - return monitors [n_screen]; -} - -int -panel_multiscreen_x (GdkScreen *screen, - int monitor) -{ - int n_screen; - - n_screen = gdk_x11_screen_get_screen_number (screen); - - g_return_val_if_fail (n_screen >= 0 && n_screen < screens, 0); - g_return_val_if_fail (monitor >= 0 && monitor < monitors [n_screen], 0); - - return geometries [n_screen][monitor].x; -} - -int -panel_multiscreen_y (GdkScreen *screen, - int monitor) -{ - int n_screen; - - n_screen = gdk_x11_screen_get_screen_number (screen); - - g_return_val_if_fail (n_screen >= 0 && n_screen < screens, 0); - g_return_val_if_fail (monitor >= 0 && monitor < monitors [n_screen], 0); - - return geometries [n_screen][monitor].y; -} - -int -panel_multiscreen_width (GdkScreen *screen, - int monitor) -{ - int n_screen; - - n_screen = gdk_x11_screen_get_screen_number (screen); - - g_return_val_if_fail (n_screen >= 0 && n_screen < screens, 0); - g_return_val_if_fail (monitor >= 0 && monitor < monitors [n_screen], 0); - - return geometries [n_screen][monitor].width; -} - -int -panel_multiscreen_height (GdkScreen *screen, - int monitor) -{ - int n_screen; - - n_screen = gdk_x11_screen_get_screen_number (screen); - - g_return_val_if_fail (n_screen >= 0 && n_screen < screens, 0); - g_return_val_if_fail (monitor >= 0 && monitor < monitors [n_screen], 0); - - return geometries [n_screen][monitor].height; -} - -int -panel_multiscreen_locate_widget_monitor (GtkWidget *widget) -{ - GtkWidget *toplevel; - int retval = -1; - - toplevel = gtk_widget_get_toplevel (widget); - if (!toplevel) - return -1; - - g_object_get (toplevel, "monitor", &retval, NULL); - - return retval; -} - -static int -axis_distance (int p, int axis_start, int axis_size) -{ - if (p >= axis_start && p < axis_start + axis_size) - return 0; - else if (p < axis_start) - return (axis_start - p); - else - return (p - (axis_start + axis_size - 1)); -} - -/* The panel can't use gdk_screen_get_monitor_at_point() since it has its own - * view of which monitors are present. Look at get_monitors_for_screen() above - * to see why. */ -int -panel_multiscreen_get_monitor_at_point (GdkScreen *screen, - int x, - int y) -{ - int n_screen; - int i; - int n_monitors; - GdkRectangle *geoms; - int min_dist_squared; - int closest_monitor; - - /* not -1 as callers expect a real monitor */ - g_return_val_if_fail (GDK_IS_SCREEN (screen), 0); - - n_screen = gdk_x11_screen_get_screen_number (screen); - - n_monitors = monitors[n_screen]; - geoms = geometries[n_screen]; - - min_dist_squared = G_MAXINT32; - closest_monitor = 0; - - for (i = 0; i < n_monitors; i++) { - int dist_x, dist_y; - int dist_squared; - - dist_x = axis_distance (x, geoms[i].x, geoms[i].width); - dist_y = axis_distance (y, geoms[i].y, geoms[i].height); - - if (dist_x == 0 && dist_y == 0) - return i; - - dist_squared = dist_x * dist_x + dist_y * dist_y; - - if (dist_squared < min_dist_squared) { - min_dist_squared = dist_squared; - closest_monitor = i; - } - } - - return closest_monitor; -} - -typedef struct { - int x0; - int y0; - int x1; - int y1; -} MonitorBounds; - -static inline void -get_monitor_bounds (int n_screen, - int n_monitor, - MonitorBounds *bounds) -{ - g_assert (n_screen >= 0 && n_screen < screens); - g_assert (n_monitor >= 0 || n_monitor < monitors [n_screen]); - g_assert (bounds != NULL); - - bounds->x0 = geometries [n_screen][n_monitor].x; - bounds->y0 = geometries [n_screen][n_monitor].y; - bounds->x1 = bounds->x0 + geometries [n_screen][n_monitor].width; - bounds->y1 = bounds->y0 + geometries [n_screen][n_monitor].height; -} - -/* determines whether a given monitor is along the visible - * edge of the logical screen. - */ -void -panel_multiscreen_is_at_visible_extreme (GdkScreen *screen, - int n_monitor, - gboolean *leftmost, - gboolean *rightmost, - gboolean *topmost, - gboolean *bottommost) -{ - MonitorBounds monitor; - int n_screen, i; - - n_screen = gdk_x11_screen_get_screen_number (screen); - - *leftmost = TRUE; - *rightmost = TRUE; - *topmost = TRUE; - *bottommost = TRUE; - - g_return_if_fail (n_screen >= 0 && n_screen < screens); - g_return_if_fail (n_monitor >= 0 && n_monitor < monitors [n_screen]); - - get_monitor_bounds (n_screen, n_monitor, &monitor); - - /* go through each monitor and try to find one either right, - * below, above, or left of the specified monitor - */ - - for (i = 0; i < monitors [n_screen]; i++) { - MonitorBounds iter; - - if (i == n_monitor) continue; - - get_monitor_bounds (n_screen, i, &iter); - - if ((iter.y0 >= monitor.y0 && iter.y0 < monitor.y1) || - (iter.y1 > monitor.y0 && iter.y1 <= monitor.y1)) { - if (iter.x0 < monitor.x0) - *leftmost = FALSE; - if (iter.x1 > monitor.x1) - *rightmost = FALSE; - } - - if ((iter.x0 >= monitor.x0 && iter.x0 < monitor.x1) || - (iter.x1 > monitor.x0 && iter.x1 <= monitor.x1)) { - if (iter.y0 < monitor.y0) - *topmost = FALSE; - if (iter.y1 > monitor.y1) - *bottommost = FALSE; - } - } -} - |