From 8c61fc25221e6ef102c77a859c6a44b9b4e25cb6 Mon Sep 17 00:00:00 2001 From: William Wold Date: Wed, 19 Jun 2019 18:30:19 +0000 Subject: Rename panel-multiscreen files panel-multimonitor --- mate-panel/Makefile.am | 4 +- mate-panel/launcher.c | 1 - mate-panel/main.c | 2 +- mate-panel/panel-multimonitor.c | 703 ++++++++++++++++++++++++++++++++++++++++ mate-panel/panel-multimonitor.h | 56 ++++ mate-panel/panel-multiscreen.c | 703 ---------------------------------------- mate-panel/panel-multiscreen.h | 56 ---- mate-panel/panel-profile.c | 2 +- mate-panel/panel-recent.c | 1 - mate-panel/panel-run-dialog.c | 2 +- mate-panel/panel-struts.c | 2 +- mate-panel/panel-toplevel.c | 2 +- mate-panel/panel.c | 1 - 13 files changed, 766 insertions(+), 769 deletions(-) create mode 100644 mate-panel/panel-multimonitor.c create mode 100644 mate-panel/panel-multimonitor.h delete mode 100644 mate-panel/panel-multiscreen.c delete mode 100644 mate-panel/panel-multiscreen.h (limited to 'mate-panel') diff --git a/mate-panel/Makefile.am b/mate-panel/Makefile.am index 209c4b6b..0702c17e 100644 --- a/mate-panel/Makefile.am +++ b/mate-panel/Makefile.am @@ -49,7 +49,7 @@ panel_sources = \ panel-toplevel.c \ panel-struts.c \ panel-frame.c \ - panel-multiscreen.c \ + panel-multimonitor.c \ panel-a11y.c \ panel-bindings.c \ panel-layout.c \ @@ -104,7 +104,7 @@ panel_headers = \ panel-toplevel.h \ panel-struts.h \ panel-frame.h \ - panel-multiscreen.h \ + panel-multimonitor.h \ panel-a11y.h \ panel-bindings.h \ panel-layout.h \ diff --git a/mate-panel/launcher.c b/mate-panel/launcher.c index a0246993..470c0d04 100644 --- a/mate-panel/launcher.c +++ b/mate-panel/launcher.c @@ -39,7 +39,6 @@ #include "panel-toplevel.h" #include "panel-a11y.h" #include "panel-globals.h" -#include "panel-multiscreen.h" #include "panel-lockdown.h" #include "panel-ditem-editor.h" #include "panel-icon-names.h" diff --git a/mate-panel/main.c b/mate-panel/main.c index b779939c..067ee993 100644 --- a/mate-panel/main.c +++ b/mate-panel/main.c @@ -23,7 +23,7 @@ #include "panel-profile.h" #include "panel-config-global.h" #include "panel-shell.h" -#include "panel-multiscreen.h" +#include "panel-multimonitor.h" #include "panel-session.h" #include "panel-schemas.h" #include "panel-stock-icons.h" diff --git a/mate-panel/panel-multimonitor.c b/mate-panel/panel-multimonitor.c new file mode 100644 index 00000000..c370838b --- /dev/null +++ b/mate-panel/panel-multimonitor.c @@ -0,0 +1,703 @@ +/* + * panel-multimonitor.c: Multi-monitor and Xinerama support for the panel. + * + * Copyright (C) 2001 George Lebl + * 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 , + * Mark McLoughlin + */ + +#include + +#include +#include +#include +#include + +#include "panel-multiscreen.h" + +#include + +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; + } + } +} + diff --git a/mate-panel/panel-multimonitor.h b/mate-panel/panel-multimonitor.h new file mode 100644 index 00000000..44ae962e --- /dev/null +++ b/mate-panel/panel-multimonitor.h @@ -0,0 +1,56 @@ +/* + * panel-multimonitor.h: Multi-monitor and Xinerama support for the panel. + * + * Copyright (C) 2001 George Lebl + * 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 , + * Mark McLoughlin + */ + +#ifndef __PANEL_MULTIMONITOR_H__ +#define __PANEL_MULTIMONITOR_H__ + +#include + +void panel_multiscreen_init (void); +void panel_multiscreen_reinit (void); + +int panel_multiscreen_screens (void); +int panel_multiscreen_monitors (GdkScreen *screen); + +int panel_multiscreen_x (GdkScreen *screen, + int monitor); +int panel_multiscreen_y (GdkScreen *screen, + int monitor); +int panel_multiscreen_width (GdkScreen *screen, + int monitor); +int panel_multiscreen_height (GdkScreen *screen, + int monitor); +int panel_multiscreen_locate_widget_monitor (GtkWidget *widget); +int panel_multiscreen_get_monitor_at_point (GdkScreen *screen, + int x, + int y); +void panel_multiscreen_is_at_visible_extreme (GdkScreen *screen, + int monitor, + gboolean *leftmost, + gboolean *rightmost, + gboolean *topmost, + gboolean *bottommost); + +#endif /* __PANEL_MULTIMONITOR_H__ */ 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 - * 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 , - * Mark McLoughlin - */ - -#include - -#include -#include -#include -#include - -#include "panel-multiscreen.h" - -#include - -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; - } - } -} - diff --git a/mate-panel/panel-multiscreen.h b/mate-panel/panel-multiscreen.h deleted file mode 100644 index d1f0a77f..00000000 --- a/mate-panel/panel-multiscreen.h +++ /dev/null @@ -1,56 +0,0 @@ -/* - * panel-multiscreen.h: Multi-screen and Xinerama support for the panel. - * - * Copyright (C) 2001 George Lebl - * 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 , - * Mark McLoughlin - */ - -#ifndef __PANEL_MULTISCREEN_H__ -#define __PANEL_MULTISCREEN_H__ - -#include - -void panel_multiscreen_init (void); -void panel_multiscreen_reinit (void); - -int panel_multiscreen_screens (void); -int panel_multiscreen_monitors (GdkScreen *screen); - -int panel_multiscreen_x (GdkScreen *screen, - int monitor); -int panel_multiscreen_y (GdkScreen *screen, - int monitor); -int panel_multiscreen_width (GdkScreen *screen, - int monitor); -int panel_multiscreen_height (GdkScreen *screen, - int monitor); -int panel_multiscreen_locate_widget_monitor (GtkWidget *widget); -int panel_multiscreen_get_monitor_at_point (GdkScreen *screen, - int x, - int y); -void panel_multiscreen_is_at_visible_extreme (GdkScreen *screen, - int monitor, - gboolean *leftmost, - gboolean *rightmost, - gboolean *topmost, - gboolean *bottommost); - -#endif /* __PANEL_MULTISCREEN_H__ */ diff --git a/mate-panel/panel-profile.c b/mate-panel/panel-profile.c index 7d787755..28549fd9 100644 --- a/mate-panel/panel-profile.c +++ b/mate-panel/panel-profile.c @@ -41,7 +41,7 @@ #include "panel.h" #include "panel-widget.h" #include "panel-util.h" -#include "panel-multiscreen.h" +#include "panel-multimonitor.h" #include "panel-toplevel.h" #include "panel-lockdown.h" #include "panel-schemas.h" diff --git a/mate-panel/panel-recent.c b/mate-panel/panel-recent.c index e111c157..e02b8d95 100644 --- a/mate-panel/panel-recent.c +++ b/mate-panel/panel-recent.c @@ -38,7 +38,6 @@ #include "panel-globals.h" #include "panel-recent.h" #include "panel-stock-icons.h" -#include "panel-multiscreen.h" #include "panel-icon-names.h" static gboolean diff --git a/mate-panel/panel-run-dialog.c b/mate-panel/panel-run-dialog.c index 5451b4d9..71dac005 100644 --- a/mate-panel/panel-run-dialog.c +++ b/mate-panel/panel-run-dialog.c @@ -57,7 +57,7 @@ #include "panel-profile.h" #include "panel-schemas.h" #include "panel-stock-icons.h" -#include "panel-multiscreen.h" +#include "panel-multimonitor.h" #include "menu.h" #include "panel-lockdown.h" #include "panel-icon-names.h" diff --git a/mate-panel/panel-struts.c b/mate-panel/panel-struts.c index d5adccc4..c87eda3a 100644 --- a/mate-panel/panel-struts.c +++ b/mate-panel/panel-struts.c @@ -26,7 +26,7 @@ #include "panel-struts.h" -#include "panel-multiscreen.h" +#include "panel-multimonitor.h" #include "panel-xutils.h" diff --git a/mate-panel/panel-toplevel.c b/mate-panel/panel-toplevel.c index c8e454ab..6c32f78e 100644 --- a/mate-panel/panel-toplevel.c +++ b/mate-panel/panel-toplevel.c @@ -39,7 +39,7 @@ #include "panel-profile.h" #include "panel-frame.h" #include "panel-xutils.h" -#include "panel-multiscreen.h" +#include "panel-multimonitor.h" #include "panel-a11y.h" #include "panel-typebuiltins.h" #include "panel-marshal.h" diff --git a/mate-panel/panel.c b/mate-panel/panel.c index 58727bf0..a53dc2a5 100644 --- a/mate-panel/panel.c +++ b/mate-panel/panel.c @@ -41,7 +41,6 @@ #include "panel-action-button.h" #include "panel-menu-bar.h" #include "panel-separator.h" -#include "panel-multiscreen.h" #include "panel-toplevel.h" #include "panel-menu-button.h" #include "panel-globals.h" -- cgit v1.2.1