[gnome-panel] [panel] Ignore overlapping monitors and just use the largest one
- From: Vincent Untz <vuntz src gnome org>
- To: svn-commits-list gnome org
- Cc:
- Subject: [gnome-panel] [panel] Ignore overlapping monitors and just use the largest one
- Date: Tue, 11 Aug 2009 21:03:55 +0000 (UTC)
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]