[gnome-panel] [panel] Ignore overlapping monitors and just use the largest one



commit dbf3efde1aac8f09af22e33c9a412e289e9c7d7c
Author: Federico Mena Quintero <federico novell com>
Date:   Tue Aug 11 23:02:59 2009 +0200

    [panel] Ignore overlapping monitors and just use the largest one
    
    http://bugzilla.gnome.org/show_bug.cgi?id=530969

 gnome-panel/panel-multiscreen.c |  177 +++++++++++++++++++++++++++++++++++++--
 gnome-panel/panel-multiscreen.h |    3 +
 gnome-panel/panel-toplevel.c    |    6 +-
 3 files changed, 177 insertions(+), 9 deletions(-)
---
diff --git a/gnome-panel/panel-multiscreen.c b/gnome-panel/panel-multiscreen.c
index b91004a..f503553 100644
--- a/gnome-panel/panel-multiscreen.c
+++ b/gnome-panel/panel-multiscreen.c
@@ -60,6 +60,116 @@ panel_multiscreen_queue_reinit (void)
 	reinit_id = g_idle_add (panel_multiscreen_reinit_idle, NULL);
 }
 
+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_get_monitors_for_screen (GdkScreen     *screen,
+					   int           *monitors_ret,
+					   GdkRectangle **geometries_ret)
+{
+	int num_monitors;
+	GdkRectangle *geometries;
+	int i;
+
+	num_monitors = gdk_screen_get_n_monitors (screen);
+	geometries = g_new (GdkRectangle, num_monitors);
+
+	for (i = 0; i < num_monitors; i++)
+		gdk_screen_get_monitor_geometry (screen, i, &(geometries[i]));
+
+	/* 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++;
+		}
+	}
+
+	*monitors_ret = num_monitors;
+	*geometries_ret = geometries;
+}
+
 void
 panel_multiscreen_init (void)
 {
@@ -90,12 +200,9 @@ panel_multiscreen_init (void)
 		g_signal_connect (screen, "monitors-changed",
 				  G_CALLBACK (panel_multiscreen_queue_reinit), NULL);
 
-		monitors   [i] = gdk_screen_get_n_monitors (screen);
-		geometries [i] = g_new0 (GdkRectangle, monitors [i]);
-
-		for (j = 0; j < monitors [i]; j++)
-			gdk_screen_get_monitor_geometry (
-				screen, j, &geometries [i][j]);
+		panel_multiscreen_get_monitors_for_screen (screen,
+							   &(monitors[i]),
+							   &(geometries[i]));
 	}
 
 	initialized = TRUE;
@@ -234,6 +341,64 @@ panel_multiscreen_locate_widget_monitor (GtkWidget *widget)
 	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_screen_get_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;
diff --git a/gnome-panel/panel-multiscreen.h b/gnome-panel/panel-multiscreen.h
index ba5fe2c..dd31b89 100644
--- a/gnome-panel/panel-multiscreen.h
+++ b/gnome-panel/panel-multiscreen.h
@@ -43,6 +43,9 @@ int	panel_multiscreen_width                 (GdkScreen *screen,
 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,
diff --git a/gnome-panel/panel-toplevel.c b/gnome-panel/panel-toplevel.c
index 1a6d9b0..8c39b77 100644
--- a/gnome-panel/panel-toplevel.c
+++ b/gnome-panel/panel-toplevel.c
@@ -616,7 +616,7 @@ panel_toplevel_calc_new_orientation (PanelToplevel *toplevel,
 
 	screen = gtk_window_get_screen (GTK_WINDOW (toplevel));
 
-	monitor = gdk_screen_get_monitor_at_point (screen, pointer_x, pointer_y);
+	monitor = panel_multiscreen_get_monitor_at_point (screen, pointer_x, pointer_y);
 
 	if (toplevel->priv->geometry.height < toplevel->priv->geometry.width)
 		vborder = hborder = (3 * toplevel->priv->geometry.height) >> 1;
@@ -728,7 +728,7 @@ panel_toplevel_move_to (PanelToplevel *toplevel,
 		 toplevel->priv->orientation & PANEL_HORIZONTAL_MASK)
 		new_orientation = PANEL_ORIENTATION_BOTTOM;
 
-	new_monitor = gdk_screen_get_monitor_at_point (screen, new_x, new_y);
+	new_monitor = panel_multiscreen_get_monitor_at_point (screen, new_x, new_y);
 
 	panel_toplevel_get_monitor_geometry (
 			toplevel, NULL, NULL, &monitor_width, &monitor_height);
@@ -2113,7 +2113,7 @@ panel_toplevel_update_expanded_position (PanelToplevel *toplevel)
 		break;
 	}
 
-	monitor = gdk_screen_get_monitor_at_point (screen, x, y);
+	monitor = panel_multiscreen_get_monitor_at_point (screen, x, y);
 
 	panel_toplevel_set_monitor (toplevel, monitor);
 



[Date Prev][Date Next]   [Thread Prev][Thread Next]   [Thread Index] [Date Index] [Author Index]