File gnome-panel-bnc479684-pick-primary-output.diff of Package gnome-panel

diff --git a/configure.in b/configure.in
index 4824f84..75ec51e 100644
--- a/configure.in
+++ b/configure.in
@@ -266,6 +266,12 @@ PKG_CHECK_MODULES(X, x11 xau, :, [
 
 AC_SUBST(X_LIBS)
 
+dnl X RANDR extension
+
+if $PKG_CONFIG --exists "xrandr >= 1.2.0" ; then
+	AC_DEFINE(HAVE_RANDR, 1, [Have the Xrandr extension library])
+fi
+
 dnl IDL directories
 
 ORBIT_IDL="`$PKG_CONFIG --variable=orbit_idl ORBit-2.0`"
diff --git a/gnome-panel/panel-multiscreen.c b/gnome-panel/panel-multiscreen.c
index 06df009..0a542ce 100644
--- a/gnome-panel/panel-multiscreen.c
+++ b/gnome-panel/panel-multiscreen.c
@@ -25,6 +25,9 @@
 
 #include <config.h>
 
+#include <X11/Xlib.h>
+#include <X11/extensions/Xrandr.h>
+#include <gdk/gdkx.h>
 #include "panel-multiscreen.h"
 
 #include <string.h>
@@ -40,6 +43,134 @@ static int            screens     = 0;
 static int           *monitors    = NULL;
 static GdkRectangle **geometries  = NULL;
 static gboolean	      initialized = FALSE;
+static gboolean       have_randr;
+
+static gboolean
+get_xrandr_monitor_geometries (GdkScreen *screen, int *monitors_ret, GdkRectangle **geometries_ret)
+{
+	if (!have_randr)
+		return FALSE;
+
+#ifdef HAVE_RANDR
+	/* 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
+	 * gnome-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.
+	 */
+
+	Display *xdisplay;
+	XRRScreenResources *resources;
+	GArray *geometries;
+	int i;
+	gboolean driver_is_pre_randr_1_2;
+
+	xdisplay = GDK_SCREEN_XDISPLAY (screen);
+
+	resources = XRRGetScreenResources (xdisplay, GDK_WINDOW_XWINDOW (gdk_screen_get_root_window (screen)));
+	if (!resources)
+		return FALSE;
+
+	geometries = g_array_sized_new (FALSE, FALSE, sizeof (GdkRectangle), resources->noutput);
+
+	driver_is_pre_randr_1_2 = FALSE;
+
+	for (i = 0; i < resources->noutput; i++) {
+		XRROutputInfo *output;
+
+		output = XRRGetOutputInfo (xdisplay, resources, resources->outputs[i]);
+
+		if (g_strcmp0 (output->name, "default") == 0)
+			driver_is_pre_randr_1_2 = TRUE; /* Drivers before RANDR 1.2 return "default" for the output name */
+
+		if (output->connection != RR_Disconnected && output->crtc != 0) {
+			XRRCrtcInfo *crtc;
+			GdkRectangle rect;
+
+			crtc = XRRGetCrtcInfo (xdisplay, resources, output->crtc);
+
+			rect.x	    = crtc->x;
+			rect.y	    = crtc->y;
+			rect.width  = crtc->width;
+			rect.height = crtc->height;
+
+			XRRFreeCrtcInfo (crtc);
+
+			/* "LVDS" is the oh-so-intuitive name that X gives to laptop LCDs.
+			 *
+			 * Note that on RANDR 1.3, the right way to check for
+			 * this is to get the ConnectorType property for the
+			 * output, and see if it is "Panel".  Yes, they changed
+			 * the known output names for 1.3.  Amateurs.
+			 */
+
+			if (g_str_has_prefix (output->name, "LVDS")) /* can be "LVDS0", "LVDS-0", etc. */
+				g_array_prepend_vals (geometries, &rect, 1);
+			else
+				g_array_append_vals (geometries, &rect, 1);
+		}
+
+		XRRFreeOutputInfo (output);
+	}
+
+	XRRFreeScreenResources (resources);
+
+	if (driver_is_pre_randr_1_2) {
+		/* Drivers before RANDR 1.2 don't provide useful info about outputs */
+		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
+get_gdk_monitor_geometries (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]));
+
+	*monitors_ret = num_monitors;
+	*geometries_ret = geometries;
+}
+
+static void
+get_raw_monitor_geometries (GdkScreen *screen, int *monitors_ret, GdkRectangle **geometries_ret)
+{
+	if (get_xrandr_monitor_geometries (screen, monitors_ret, geometries_ret))
+		return;
+
+	get_gdk_monitor_geometries (screen, monitors_ret, geometries_ret);
+}
 
 static gboolean
 overlaps (GdkRectangle *a, GdkRectangle *b)
@@ -54,17 +185,14 @@ pixels_in_rectangle (GdkRectangle *r)
 }
 
 static void
-get_monitors_for_screen (GdkScreen *screen, int *monitors_ret, GdkRectangle **geometries_ret)
+compress_overlapping_monitors (int *num_monitors_inout, GdkRectangle **geometries_inout)
 {
 	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]));
+	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
@@ -142,10 +270,50 @@ get_monitors_for_screen (GdkScreen *screen, int *monitors_ret, GdkRectangle **ge
 		}
 	}
 
+	*num_monitors_inout = num_monitors;
+	*geometries_inout = geometries;
+}
+
+static void
+get_monitors_for_screen (GdkScreen *screen, int *monitors_ret, GdkRectangle **geometries_ret)
+{
+	int num_monitors;
+	GdkRectangle *geometries;
+
+	get_raw_monitor_geometries (screen, &num_monitors, &geometries);
+	compress_overlapping_monitors (&num_monitors, &geometries);
+
 	*monitors_ret = num_monitors;
 	*geometries_ret = geometries;
 }
 
+static void
+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 >= 2) || major > 1)
+			have_randr = TRUE;
+	}
+#endif
+}
+
 void
 panel_multiscreen_init (void)
 {
@@ -158,6 +326,8 @@ panel_multiscreen_init (void)
 	display = gdk_display_get_default ();
 	screens = gdk_display_get_n_screens (display);
 
+	init_randr (display);
+
 	monitors   = g_new0 (int, screens);
 	geometries = g_new0 (GdkRectangle *, screens);
 
openSUSE Build Service is sponsored by