1 /* -*- Mode: C; tab-width: 8; indent-tabs-mode: t; c-basic-offset: 8 -*-
2  *
3  * gnome-rr-labeler.c - Utility to label monitors to identify them
4  * while they are being configured.
5  *
6  * Copyright 2008, Novell, Inc.
7  *
8  * This file is part of the Gnome Library.
9  *
10  * The Gnome Library is free software; you can redistribute it and/or
11  * modify it under the terms of the GNU Library General Public License as
12  * published by the Free Software Foundation; either version 2 of the
13  * License, or (at your option) any later version.
14  *
15  * The Gnome Library is distributed in the hope that it will be useful,
16  * but WITHOUT ANY WARRANTY; without even the implied warranty of
17  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
18  * Library General Public License for more details.
19  *
20  * You should have received a copy of the GNU Library General Public
21  * License along with the Gnome Library; see the file COPYING.LIB.  If not,
22  * write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
23  * Boston, MA 02110-1301, USA.
24  *
25  * Author: Federico Mena-Quintero <federico@novell.com>
26  */
27 
28 #define GNOME_DESKTOP_USE_UNSTABLE_API
29 
30 #include <config.h>
31 #include <glib/gi18n-lib.h>
32 #include <gtk/gtk.h>
33 
34 #include <X11/Xproto.h>
35 #include <X11/Xlib.h>
36 #include <X11/Xutil.h>
37 #include <X11/Xatom.h>
38 #include <gdk/gdkx.h>
39 
40 #include "gnome-rr-labeler.h"
41 
42 struct _GnomeRRLabelerPrivate {
43 	GnomeRRConfig *config;
44 
45 	int num_outputs;
46 
47 	GdkRGBA *palette;
48 	GtkWidget **windows;
49 
50 	GdkScreen  *screen;
51 	Atom        workarea_atom;
52 };
53 
54 enum {
55 	PROP_0,
56 	PROP_CONFIG,
57 	PROP_LAST
58 };
59 
60 G_DEFINE_TYPE (GnomeRRLabeler, gnome_rr_labeler, G_TYPE_OBJECT);
61 
62 static void gnome_rr_labeler_finalize (GObject *object);
63 static void setup_from_config (GnomeRRLabeler *labeler);
64 
65 static GdkFilterReturn
screen_xevent_filter(GdkXEvent * xevent,GdkEvent * event,GnomeRRLabeler * labeler)66 screen_xevent_filter (GdkXEvent      *xevent,
67 		      GdkEvent       *event,
68 		      GnomeRRLabeler *labeler)
69 {
70 	XEvent *xev;
71 
72 	xev = (XEvent *) xevent;
73 
74 	if (xev->type == PropertyNotify &&
75 	    xev->xproperty.atom == labeler->priv->workarea_atom) {
76 		/* update label positions */
77 		if (labeler->priv->windows != NULL) {
78 			gnome_rr_labeler_hide (labeler);
79 			gnome_rr_labeler_show (labeler);
80 		}
81 	}
82 
83 	return GDK_FILTER_CONTINUE;
84 }
85 
86 static void
gnome_rr_labeler_init(GnomeRRLabeler * labeler)87 gnome_rr_labeler_init (GnomeRRLabeler *labeler)
88 {
89 	GdkWindow *gdkwindow;
90 
91 	labeler->priv = G_TYPE_INSTANCE_GET_PRIVATE (labeler, GNOME_TYPE_RR_LABELER, GnomeRRLabelerPrivate);
92 
93 	labeler->priv->workarea_atom = XInternAtom (GDK_DISPLAY_XDISPLAY (gdk_display_get_default ()),
94 						    "_NET_WORKAREA",
95 						    True);
96 
97 	labeler->priv->screen = gdk_screen_get_default ();
98 	/* code is not really designed to handle multiple screens so *shrug* */
99 	gdkwindow = gdk_screen_get_root_window (labeler->priv->screen);
100 	gdk_window_add_filter (gdkwindow, (GdkFilterFunc) screen_xevent_filter, labeler);
101 	gdk_window_set_events (gdkwindow, gdk_window_get_events (gdkwindow) | GDK_PROPERTY_CHANGE_MASK);
102 }
103 
104 static void
gnome_rr_labeler_set_property(GObject * gobject,guint property_id,const GValue * value,GParamSpec * param_spec)105 gnome_rr_labeler_set_property (GObject *gobject, guint property_id, const GValue *value, GParamSpec *param_spec)
106 {
107 	GnomeRRLabeler *self = GNOME_RR_LABELER (gobject);
108 
109 	switch (property_id) {
110 	case PROP_CONFIG:
111 		self->priv->config = GNOME_RR_CONFIG (g_value_dup_object (value));
112 		return;
113 	default:
114 		G_OBJECT_WARN_INVALID_PROPERTY_ID (gobject, property_id, param_spec);
115 	}
116 }
117 
118 static GObject *
gnome_rr_labeler_constructor(GType type,guint n_construct_properties,GObjectConstructParam * construct_properties)119 gnome_rr_labeler_constructor (GType type, guint n_construct_properties, GObjectConstructParam *construct_properties)
120 {
121 	GnomeRRLabeler *self = (GnomeRRLabeler*) G_OBJECT_CLASS (gnome_rr_labeler_parent_class)->constructor (type, n_construct_properties, construct_properties);
122 
123 	setup_from_config (self);
124 
125 	return (GObject*) self;
126 }
127 
128 static void
gnome_rr_labeler_class_init(GnomeRRLabelerClass * klass)129 gnome_rr_labeler_class_init (GnomeRRLabelerClass *klass)
130 {
131 	GObjectClass *object_class;
132 
133 	g_type_class_add_private (klass, sizeof (GnomeRRLabelerPrivate));
134 
135 	object_class = (GObjectClass *) klass;
136 
137 	object_class->set_property = gnome_rr_labeler_set_property;
138 	object_class->finalize = gnome_rr_labeler_finalize;
139 	object_class->constructor = gnome_rr_labeler_constructor;
140 
141 	g_object_class_install_property (object_class, PROP_CONFIG, g_param_spec_object ("config",
142 											 "Configuration",
143 											 "RandR configuration to label",
144 											 GNOME_TYPE_RR_CONFIG,
145 											 G_PARAM_WRITABLE | G_PARAM_CONSTRUCT_ONLY |
146 											 G_PARAM_STATIC_NICK | G_PARAM_STATIC_BLURB));
147 }
148 
149 static void
gnome_rr_labeler_finalize(GObject * object)150 gnome_rr_labeler_finalize (GObject *object)
151 {
152 	GnomeRRLabeler *labeler;
153 	GdkWindow      *gdkwindow;
154 
155 	labeler = GNOME_RR_LABELER (object);
156 
157 	gdkwindow = gdk_screen_get_root_window (labeler->priv->screen);
158 	gdk_window_remove_filter (gdkwindow, (GdkFilterFunc) screen_xevent_filter, labeler);
159 
160 	if (labeler->priv->config != NULL) {
161 		g_object_unref (labeler->priv->config);
162 	}
163 
164 	if (labeler->priv->windows != NULL) {
165 		gnome_rr_labeler_hide (labeler);
166 		g_free (labeler->priv->windows);
167 	}
168 
169 	g_free (labeler->priv->palette);
170 
171 	G_OBJECT_CLASS (gnome_rr_labeler_parent_class)->finalize (object);
172 }
173 
174 static int
count_outputs(GnomeRRConfig * config)175 count_outputs (GnomeRRConfig *config)
176 {
177 	int i;
178 	GnomeRROutputInfo **outputs = gnome_rr_config_get_outputs (config);
179 
180 	for (i = 0; outputs[i] != NULL; i++)
181 		;
182 
183 	return i;
184 }
185 
186 static void
make_palette(GnomeRRLabeler * labeler)187 make_palette (GnomeRRLabeler *labeler)
188 {
189 	/* The idea is that we go around an hue color wheel.  We want to start
190 	 * at red, go around to green/etc. and stop at blue --- because magenta
191 	 * is evil.  Eeeeek, no magenta, please!
192 	 *
193 	 * Purple would be nice, though.  Remember that we are watered down
194 	 * (i.e. low saturation), so that would be like Like berries with cream.
195 	 * Mmmmm, berries.
196 	 */
197 	double start_hue;
198 	double end_hue;
199 	int i;
200 
201 	g_assert (labeler->priv->num_outputs > 0);
202 
203 	labeler->priv->palette = g_new (GdkRGBA, labeler->priv->num_outputs);
204 
205 	start_hue = 0.0; /* red */
206 	end_hue   = 2.0/3; /* blue */
207 
208 	for (i = 0; i < labeler->priv->num_outputs; i++) {
209 		double h, s, v;
210 		double r, g, b;
211 
212 		h = start_hue + (end_hue - start_hue) / labeler->priv->num_outputs * i;
213 		s = 1.0 / 3;
214 		v = 1.0;
215 
216 		gtk_hsv_to_rgb (h, s, v, &r, &g, &b);
217 
218 		labeler->priv->palette[i].red   = r;
219 		labeler->priv->palette[i].green = g;
220 		labeler->priv->palette[i].blue  = b;
221 		labeler->priv->palette[i].alpha  = 1.0;
222 	}
223 }
224 
225 static void
rounded_rectangle(cairo_t * cr,gint x,gint y,gint width,gint height,gint x_radius,gint y_radius)226 rounded_rectangle (cairo_t *cr,
227                    gint     x,
228                    gint     y,
229                    gint     width,
230                    gint     height,
231                    gint     x_radius,
232                    gint     y_radius)
233 {
234 	gint x1, x2;
235 	gint y1, y2;
236 	gint xr1, xr2;
237 	gint yr1, yr2;
238 
239 	x1 = x;
240 	x2 = x1 + width;
241 	y1 = y;
242 	y2 = y1 + height;
243 
244 	x_radius = MIN (x_radius, width / 2.0);
245 	y_radius = MIN (y_radius, width / 2.0);
246 
247 	xr1 = x_radius;
248 	xr2 = x_radius / 2.0;
249 	yr1 = y_radius;
250 	yr2 = y_radius / 2.0;
251 
252 	cairo_move_to    (cr, x1 + xr1, y1);
253 	cairo_line_to    (cr, x2 - xr1, y1);
254 	cairo_curve_to   (cr, x2 - xr2, y1, x2, y1 + yr2, x2, y1 + yr1);
255 	cairo_line_to    (cr, x2, y2 - yr1);
256 	cairo_curve_to   (cr, x2, y2 - yr2, x2 - xr2, y2, x2 - xr1, y2);
257 	cairo_line_to    (cr, x1 + xr1, y2);
258 	cairo_curve_to   (cr, x1 + xr2, y2, x1, y2 - yr2, x1, y2 - yr1);
259 	cairo_line_to    (cr, x1, y1 + yr1);
260 	cairo_curve_to   (cr, x1, y1 + yr2, x1 + xr2, y1, x1 + xr1, y1);
261 	cairo_close_path (cr);
262 }
263 
264 #define LABEL_WINDOW_EDGE_THICKNESS 2
265 #define LABEL_WINDOW_PADDING 12
266 /* Look for panel-corner in:
267  * http://git.gnome.org/browse/gnome-shell/tree/data/theme/gnome-shell.css
268  * to match the corner radius */
269 #define LABEL_CORNER_RADIUS 6 + LABEL_WINDOW_EDGE_THICKNESS
270 
271 static void
label_draw_background_and_frame(GtkWidget * widget,cairo_t * cr,gboolean for_shape)272 label_draw_background_and_frame (GtkWidget *widget, cairo_t *cr, gboolean for_shape)
273 {
274 	GdkRGBA shape_color = { 0, 0, 0, 1 };
275 	GdkRGBA *rgba;
276 	GtkAllocation allocation;
277 
278 	rgba = g_object_get_data (G_OBJECT (widget), "rgba");
279 	gtk_widget_get_allocation (widget, &allocation);
280 
281 	cairo_save (cr);
282 	cairo_set_operator (cr, CAIRO_OPERATOR_SOURCE);
283 
284 	/* edge outline */
285 	if (for_shape)
286 		gdk_cairo_set_source_rgba (cr, &shape_color);
287 	else
288 		cairo_set_source_rgba (cr, 0, 0, 0, 0.5);
289 
290 	rounded_rectangle (cr,
291 	                   LABEL_WINDOW_EDGE_THICKNESS / 2.0,
292 	                   LABEL_WINDOW_EDGE_THICKNESS / 2.0,
293 	                   allocation.width - LABEL_WINDOW_EDGE_THICKNESS,
294 	                   allocation.height - LABEL_WINDOW_EDGE_THICKNESS,
295 	                   LABEL_CORNER_RADIUS, LABEL_CORNER_RADIUS);
296 	cairo_set_line_width (cr, LABEL_WINDOW_EDGE_THICKNESS);
297 	cairo_stroke (cr);
298 
299 	/* fill */
300 	if (for_shape) {
301 		gdk_cairo_set_source_rgba (cr, &shape_color);
302 	} else {
303 		rgba->alpha = 0.75;
304 		gdk_cairo_set_source_rgba (cr, rgba);
305 	}
306 
307 	rounded_rectangle (cr,
308 	                   LABEL_WINDOW_EDGE_THICKNESS,
309 	                   LABEL_WINDOW_EDGE_THICKNESS,
310 	                   allocation.width - LABEL_WINDOW_EDGE_THICKNESS * 2,
311 	                   allocation.height - LABEL_WINDOW_EDGE_THICKNESS * 2,
312 	                   LABEL_CORNER_RADIUS - LABEL_WINDOW_EDGE_THICKNESS / 2.0,
313 			   LABEL_CORNER_RADIUS - LABEL_WINDOW_EDGE_THICKNESS / 2.0);
314 	cairo_fill (cr);
315 
316 	cairo_restore (cr);
317 }
318 
319 static void
maybe_update_shape(GtkWidget * widget)320 maybe_update_shape (GtkWidget *widget)
321 {
322 	cairo_t *cr;
323 	cairo_surface_t *surface;
324 	cairo_region_t *region;
325 
326 	/* fallback to XShape only for non-composited clients */
327 	if (gtk_widget_is_composited (widget)) {
328 		gtk_widget_shape_combine_region (widget, NULL);
329 		return;
330 	}
331 
332 	surface = gdk_window_create_similar_surface (gtk_widget_get_window (widget),
333 						     CAIRO_CONTENT_COLOR_ALPHA,
334 						     gtk_widget_get_allocated_width (widget),
335 						     gtk_widget_get_allocated_height (widget));
336 
337 	cr = cairo_create (surface);
338 	label_draw_background_and_frame (widget, cr, TRUE);
339 	cairo_destroy (cr);
340 
341 	region = gdk_cairo_region_create_from_surface (surface);
342 	gtk_widget_shape_combine_region (widget, region);
343 
344 	cairo_surface_destroy (surface);
345 	cairo_region_destroy (region);
346 }
347 
348 static gboolean
label_window_draw_event_cb(GtkWidget * widget,cairo_t * cr,gpointer data)349 label_window_draw_event_cb (GtkWidget *widget, cairo_t *cr, gpointer data)
350 {
351 	if (gtk_widget_is_composited (widget)) {
352 		/* clear any content */
353 		cairo_save (cr);
354 		cairo_set_operator (cr, CAIRO_OPERATOR_SOURCE);
355 		cairo_set_source_rgba (cr, 0, 0, 0, 0);
356 		cairo_paint (cr);
357 		cairo_restore (cr);
358 	}
359 
360 	maybe_update_shape (widget);
361 	label_draw_background_and_frame (widget, cr, FALSE);
362 
363 	return FALSE;
364 }
365 
366 static void
position_window(GnomeRRLabeler * labeler,GtkWidget * window,int x,int y)367 position_window (GnomeRRLabeler  *labeler,
368 		 GtkWidget       *window,
369 		 int              x,
370 		 int              y)
371 {
372 	GdkRectangle    workarea;
373 	GdkRectangle    monitor;
374 	int             monitor_num;
375 
376 	monitor_num = gdk_screen_get_monitor_at_point (labeler->priv->screen, x, y);
377 	gdk_screen_get_monitor_workarea (labeler->priv->screen, monitor_num, &workarea);
378 	gdk_screen_get_monitor_geometry (labeler->priv->screen,
379                                          monitor_num,
380                                          &monitor);
381 	gdk_rectangle_intersect (&monitor, &workarea, &workarea);
382 
383 	gtk_window_move (GTK_WINDOW (window), workarea.x, workarea.y);
384 }
385 
386 static void
label_window_realize_cb(GtkWidget * widget)387 label_window_realize_cb (GtkWidget *widget)
388 {
389 	cairo_region_t *region;
390 
391 	/* make the whole window ignore events */
392 	region = cairo_region_create ();
393 	gtk_widget_input_shape_combine_region (widget, region);
394 	cairo_region_destroy (region);
395 
396 	maybe_update_shape (widget);
397 }
398 
399 static void
label_window_composited_changed_cb(GtkWidget * widget,GnomeRRLabeler * labeler)400 label_window_composited_changed_cb (GtkWidget *widget, GnomeRRLabeler *labeler)
401 {
402 	if (gtk_widget_get_realized (widget))
403 		maybe_update_shape (widget);
404 }
405 
406 static GtkWidget *
create_label_window(GnomeRRLabeler * labeler,GnomeRROutputInfo * output,GdkRGBA * rgba)407 create_label_window (GnomeRRLabeler *labeler, GnomeRROutputInfo *output, GdkRGBA *rgba)
408 {
409 	GtkWidget *window;
410 	GtkWidget *widget;
411 	char *str;
412 	const char *display_name;
413 	GdkRGBA black = { 0, 0, 0, 1.0 };
414 	int x, y;
415 	GdkScreen *screen;
416 	GdkVisual *visual;
417 
418 	window = gtk_window_new (GTK_WINDOW_POPUP);
419 	gtk_window_set_type_hint (GTK_WINDOW (window), GDK_WINDOW_TYPE_HINT_TOOLTIP);
420 	gtk_window_set_resizable (GTK_WINDOW (window), FALSE);
421 	gtk_widget_set_app_paintable (window, TRUE);
422 	screen = gtk_widget_get_screen (window);
423 	visual = gdk_screen_get_rgba_visual (screen);
424 
425 	if (visual != NULL)
426 		gtk_widget_set_visual (window, visual);
427 
428 	gtk_container_set_border_width (GTK_CONTAINER (window), LABEL_WINDOW_PADDING + LABEL_WINDOW_EDGE_THICKNESS);
429 
430 	/* This is semi-dangerous.  The color is part of the labeler->palette
431 	 * array.  Note that in gnome_rr_labeler_finalize(), we are careful to
432 	 * free the palette only after we free the windows.
433 	 */
434 	g_object_set_data (G_OBJECT (window), "rgba", rgba);
435 
436 	g_signal_connect (window, "draw",
437 			  G_CALLBACK (label_window_draw_event_cb), labeler);
438 	g_signal_connect (window, "realize",
439 			  G_CALLBACK (label_window_realize_cb), labeler);
440 	g_signal_connect (window, "composited-changed",
441 			  G_CALLBACK (label_window_composited_changed_cb), labeler);
442 
443 	if (gnome_rr_config_get_clone (labeler->priv->config)) {
444 		/* Keep this string in sync with gnome-control-center/capplets/display/xrandr-capplet.c:get_display_name() */
445 
446 		/* Translators:  this is the feature where what you see on your
447 		 * laptop's screen is the same as your external projector.
448 		 * Here, "Mirrored" is being used as an adjective.  For example,
449 		 * the Spanish translation could be "Pantallas en Espejo".
450 		 */
451 		display_name = _("Mirrored Displays");
452 	} else
453 		display_name = gnome_rr_output_info_get_display_name (output);
454 
455 	str = g_strdup_printf ("<b>%s</b>", display_name);
456 	widget = gtk_label_new (NULL);
457 	gtk_label_set_markup (GTK_LABEL (widget), str);
458 	g_free (str);
459 
460 	/* Make the label explicitly black.  We don't want it to follow the
461 	 * theme's colors, since the label is always shown against a light
462 	 * pastel background.  See bgo#556050
463 	 */
464 	gtk_widget_override_color (widget,
465 				   gtk_widget_get_state_flags (widget),
466 				   &black);
467 
468 	gtk_container_add (GTK_CONTAINER (window), widget);
469 
470 	/* Should we center this at the top edge of the monitor, instead of using the upper-left corner? */
471 	gnome_rr_output_info_get_geometry (output, &x, &y, NULL, NULL);
472 	position_window (labeler, window, x, y);
473 
474 	gtk_widget_show_all (window);
475 
476 	return window;
477 }
478 
479 static void
setup_from_config(GnomeRRLabeler * labeler)480 setup_from_config (GnomeRRLabeler *labeler)
481 {
482 	labeler->priv->num_outputs = count_outputs (labeler->priv->config);
483 
484 	make_palette (labeler);
485 
486 	gnome_rr_labeler_show (labeler);
487 }
488 
489 /**
490  * gnome_rr_labeler_new:
491  * @config: Configuration of the screens to label
492  *
493  * Create a GUI element that will display colored labels on each connected monitor.
494  * This is useful when users are required to identify which monitor is which, e.g. for
495  * for configuring multiple monitors.
496  * The labels will be shown by default, use gnome_rr_labeler_hide to hide them.
497  *
498  * Returns: A new #GnomeRRLabeler
499  */
500 GnomeRRLabeler *
gnome_rr_labeler_new(GnomeRRConfig * config)501 gnome_rr_labeler_new (GnomeRRConfig *config)
502 {
503 	g_return_val_if_fail (GNOME_IS_RR_CONFIG (config), NULL);
504 
505 	return g_object_new (GNOME_TYPE_RR_LABELER, "config", config, NULL);
506 }
507 
508 /**
509  * gnome_rr_labeler_show:
510  * @labeler: A #GnomeRRLabeler
511  *
512  * Show the labels.
513  */
514 void
gnome_rr_labeler_show(GnomeRRLabeler * labeler)515 gnome_rr_labeler_show (GnomeRRLabeler *labeler)
516 {
517 	int i;
518 	gboolean created_window_for_clone;
519 	GnomeRROutputInfo **outputs;
520 
521 	g_return_if_fail (GNOME_IS_RR_LABELER (labeler));
522 
523 	if (labeler->priv->windows != NULL)
524 		return;
525 
526 	labeler->priv->windows = g_new (GtkWidget *, labeler->priv->num_outputs);
527 
528 	created_window_for_clone = FALSE;
529 
530 	outputs = gnome_rr_config_get_outputs (labeler->priv->config);
531 
532 	for (i = 0; i < labeler->priv->num_outputs; i++) {
533 		if (!created_window_for_clone && gnome_rr_output_info_is_active (outputs[i])) {
534 			labeler->priv->windows[i] = create_label_window (labeler, outputs[i], labeler->priv->palette + i);
535 
536 			if (gnome_rr_config_get_clone (labeler->priv->config))
537 				created_window_for_clone = TRUE;
538 		} else
539 			labeler->priv->windows[i] = NULL;
540 	}
541 }
542 
543 /**
544  * gnome_rr_labeler_hide:
545  * @labeler: A #GnomeRRLabeler
546  *
547  * Hide ouput labels.
548  */
549 void
gnome_rr_labeler_hide(GnomeRRLabeler * labeler)550 gnome_rr_labeler_hide (GnomeRRLabeler *labeler)
551 {
552 	int i;
553 	GnomeRRLabelerPrivate *priv;
554 
555 	g_return_if_fail (GNOME_IS_RR_LABELER (labeler));
556 
557 	priv = labeler->priv;
558 
559 	if (priv->windows == NULL)
560 		return;
561 
562 	for (i = 0; i < priv->num_outputs; i++)
563 		if (priv->windows[i] != NULL) {
564 			gtk_widget_destroy (priv->windows[i]);
565 			priv->windows[i] = NULL;
566 	}
567 	g_free (priv->windows);
568 	priv->windows = NULL;
569 }
570 
571 /**
572  * gnome_rr_labeler_get_rgba_for_output:
573  * @labeler: A #GnomeRRLabeler
574  * @output: Output device (i.e. monitor) to query
575  * @rgba_out: (out): Color of selected monitor.
576  *
577  * Get the color used for the label on a given output (monitor).
578  */
579 void
gnome_rr_labeler_get_rgba_for_output(GnomeRRLabeler * labeler,GnomeRROutputInfo * output,GdkRGBA * rgba_out)580 gnome_rr_labeler_get_rgba_for_output (GnomeRRLabeler *labeler, GnomeRROutputInfo *output, GdkRGBA *rgba_out)
581 {
582 	int i;
583 	GnomeRROutputInfo **outputs;
584 
585 	g_return_if_fail (GNOME_IS_RR_LABELER (labeler));
586 	g_return_if_fail (GNOME_IS_RR_OUTPUT_INFO (output));
587 	g_return_if_fail (rgba_out != NULL);
588 
589 	outputs = gnome_rr_config_get_outputs (labeler->priv->config);
590 
591 	for (i = 0; i < labeler->priv->num_outputs; i++)
592 		if (outputs[i] == output) {
593 			*rgba_out = labeler->priv->palette[i];
594 			return;
595 		}
596 
597 	g_warning ("trying to get the color for unknown GnomeOutputInfo %p; returning magenta!", output);
598 
599 	rgba_out->red   = 1.0;
600 	rgba_out->green = 0;
601 	rgba_out->blue  = 1.0;
602 	rgba_out->alpha  = 1.0;
603 }
604