2
* -*- c-basic-offset: 4 -*-
4
* Copyright 2007, 2008, Red Hat, Inc.
5
* Copyright 2010 Giovanni Campagna
7
* This file is part of the Gnome Library.
9
* The Gnome Library is free software; you can redistribute it and/or
10
* modify it under the terms of the GNU Library General Public License as
11
* published by the Free Software Foundation; either version 2 of the
12
* License, or (at your option) any later version.
14
* The Gnome Library is distributed in the hope that it will be useful,
15
* but WITHOUT ANY WARRANTY; without even the implied warranty of
16
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
17
* Library General Public License for more details.
19
* You should have received a copy of the GNU Library General Public
20
* License along with the Gnome Library; see the file COPYING.LIB. If not,
21
* write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
22
* Boston, MA 02110-1301, USA.
24
* Author: Soren Sandmann <sandmann@redhat.com>
27
#define GNOME_DESKTOP_USE_UNSTABLE_API
30
#include <glib/gi18n-lib.h>
34
#include <glib/gstdio.h>
39
#include "gnome-rr-config.h"
42
#include "gnome-rr-private.h"
44
#define CONFIG_INTENDED_BASENAME "monitors.xml"
45
#define CONFIG_BACKUP_BASENAME "monitors.xml.backup"
47
/* Look for DPI_FALLBACK in:
48
* http://git.gnome.org/browse/gnome-settings-daemon/tree/plugins/xsettings/gsd-xsettings-manager.c
49
* for the reasoning */
50
#define DPI_FALLBACK 96.0
52
/* In version 0 of the config file format, we had several <configuration>
53
* toplevel elements and no explicit version number. So, the filed looked
63
* Since version 1 of the config file, the file has a toplevel <monitors>
64
* element to group all the configurations. That element has a "version"
65
* attribute which is an integer. So, the file looks like this:
67
* <monitors version="1">
77
/* A helper wrapper around the GMarkup parser stuff */
78
static gboolean parse_file_gmarkup (const gchar *file,
79
const GMarkupParser *parser,
83
typedef struct CrtcAssignment CrtcAssignment;
85
static gboolean crtc_assignment_apply (CrtcAssignment *assign,
88
static CrtcAssignment *crtc_assignment_new (GnomeRRScreen *screen,
89
GnomeRROutputInfo **outputs,
91
static void crtc_assignment_free (CrtcAssignment *assign);
99
G_DEFINE_TYPE (GnomeRRConfig, gnome_rr_config, G_TYPE_OBJECT)
101
typedef struct Parser Parser;
103
/* Parser for monitor configurations */
106
int config_file_version;
107
GnomeRROutputInfo * output;
108
GnomeRRConfig * configuration;
110
GPtrArray * configurations;
115
parse_int (const char *text)
117
return strtol (text, NULL, 0);
121
parse_uint (const char *text)
123
return strtoul (text, NULL, 0);
127
stack_is (Parser *parser,
136
stack = g_list_prepend (stack, (gpointer)s1);
140
s = va_arg (args, const char *);
143
stack = g_list_prepend (stack, (gpointer)s);
144
s = va_arg (args, const char *);
148
l2 = parser->stack->head;
152
if (strcmp (l1->data, l2->data) != 0)
168
handle_start_element (GMarkupParseContext *context,
170
const gchar **attr_names,
171
const gchar **attr_values,
175
Parser *parser = user_data;
177
if (strcmp (name, "output") == 0)
180
g_assert (parser->output == NULL);
182
parser->output = g_object_new (GNOME_TYPE_RR_OUTPUT_INFO, NULL);
183
parser->output->priv->rotation = 0;
185
for (i = 0; attr_names[i] != NULL; ++i)
187
if (strcmp (attr_names[i], "name") == 0)
189
parser->output->priv->name = g_strdup (attr_values[i]);
194
if (!parser->output->priv->name)
196
/* This really shouldn't happen, but it's better to make
197
* something up than to crash later.
199
g_warning ("Malformed monitor configuration file");
201
parser->output->priv->name = g_strdup ("default");
203
parser->output->priv->connected = FALSE;
204
parser->output->priv->on = FALSE;
205
parser->output->priv->primary = FALSE;
207
else if (strcmp (name, "configuration") == 0)
209
g_assert (parser->configuration == NULL);
211
parser->configuration = g_object_new (GNOME_TYPE_RR_CONFIG, NULL);
212
parser->configuration->priv->clone = FALSE;
213
parser->configuration->priv->outputs = NULL;
215
else if (strcmp (name, "monitors") == 0)
219
for (i = 0; attr_names[i] != NULL; i++)
221
if (strcmp (attr_names[i], "version") == 0)
223
parser->config_file_version = parse_int (attr_values[i]);
229
g_queue_push_tail (parser->stack, g_strdup (name));
233
handle_end_element (GMarkupParseContext *context,
238
Parser *parser = user_data;
240
if (strcmp (name, "output") == 0)
242
/* If no rotation properties were set, just use GNOME_RR_ROTATION_0 */
243
if (parser->output->priv->rotation == 0)
244
parser->output->priv->rotation = GNOME_RR_ROTATION_0;
246
g_ptr_array_add (parser->outputs, parser->output);
248
parser->output = NULL;
250
else if (strcmp (name, "configuration") == 0)
252
g_ptr_array_add (parser->outputs, NULL);
253
parser->configuration->priv->outputs =
254
(GnomeRROutputInfo **)g_ptr_array_free (parser->outputs, FALSE);
255
parser->outputs = g_ptr_array_new ();
256
g_ptr_array_add (parser->configurations, parser->configuration);
257
parser->configuration = NULL;
260
g_free (g_queue_pop_tail (parser->stack));
263
#define TOPLEVEL_ELEMENT (parser->config_file_version > 0 ? "monitors" : NULL)
266
handle_text (GMarkupParseContext *context,
272
Parser *parser = user_data;
274
if (stack_is (parser, "vendor", "output", "configuration", TOPLEVEL_ELEMENT, NULL))
276
parser->output->priv->connected = TRUE;
278
strncpy ((gchar*) parser->output->priv->vendor, text, 3);
279
parser->output->priv->vendor[3] = 0;
281
else if (stack_is (parser, "clone", "configuration", TOPLEVEL_ELEMENT, NULL))
283
if (strcmp (text, "yes") == 0)
284
parser->configuration->priv->clone = TRUE;
286
else if (stack_is (parser, "product", "output", "configuration", TOPLEVEL_ELEMENT, NULL))
288
parser->output->priv->connected = TRUE;
290
parser->output->priv->product = parse_int (text);
292
else if (stack_is (parser, "serial", "output", "configuration", TOPLEVEL_ELEMENT, NULL))
294
parser->output->priv->connected = TRUE;
296
parser->output->priv->serial = parse_uint (text);
298
else if (stack_is (parser, "width", "output", "configuration", TOPLEVEL_ELEMENT, NULL))
300
parser->output->priv->on = TRUE;
302
parser->output->priv->width = parse_int (text);
304
else if (stack_is (parser, "x", "output", "configuration", TOPLEVEL_ELEMENT, NULL))
306
parser->output->priv->on = TRUE;
308
parser->output->priv->x = parse_int (text);
310
else if (stack_is (parser, "y", "output", "configuration", TOPLEVEL_ELEMENT, NULL))
312
parser->output->priv->on = TRUE;
314
parser->output->priv->y = parse_int (text);
316
else if (stack_is (parser, "height", "output", "configuration", TOPLEVEL_ELEMENT, NULL))
318
parser->output->priv->on = TRUE;
320
parser->output->priv->height = parse_int (text);
322
else if (stack_is (parser, "rate", "output", "configuration", TOPLEVEL_ELEMENT, NULL))
324
parser->output->priv->on = TRUE;
326
parser->output->priv->rate = parse_int (text);
328
else if (stack_is (parser, "rotation", "output", "configuration", TOPLEVEL_ELEMENT, NULL))
330
if (strcmp (text, "normal") == 0)
332
parser->output->priv->rotation |= GNOME_RR_ROTATION_0;
334
else if (strcmp (text, "left") == 0)
336
parser->output->priv->rotation |= GNOME_RR_ROTATION_90;
338
else if (strcmp (text, "upside_down") == 0)
340
parser->output->priv->rotation |= GNOME_RR_ROTATION_180;
342
else if (strcmp (text, "right") == 0)
344
parser->output->priv->rotation |= GNOME_RR_ROTATION_270;
347
else if (stack_is (parser, "reflect_x", "output", "configuration", TOPLEVEL_ELEMENT, NULL))
349
if (strcmp (text, "yes") == 0)
351
parser->output->priv->rotation |= GNOME_RR_REFLECT_X;
354
else if (stack_is (parser, "reflect_y", "output", "configuration", TOPLEVEL_ELEMENT, NULL))
356
if (strcmp (text, "yes") == 0)
358
parser->output->priv->rotation |= GNOME_RR_REFLECT_Y;
361
else if (stack_is (parser, "primary", "output", "configuration", TOPLEVEL_ELEMENT, NULL))
363
if (strcmp (text, "yes") == 0)
365
parser->output->priv->primary = TRUE;
370
/* Ignore other properties so we can expand the format in the future */
375
parser_free (Parser *parser)
380
g_assert (parser != NULL);
383
g_object_unref (parser->output);
385
if (parser->configuration)
386
g_object_unref (parser->configuration);
388
for (i = 0; i < parser->outputs->len; ++i)
390
GnomeRROutputInfo *output = parser->outputs->pdata[i];
392
g_object_unref (output);
395
g_ptr_array_free (parser->outputs, TRUE);
397
for (i = 0; i < parser->configurations->len; ++i)
399
GnomeRRConfig *config = parser->configurations->pdata[i];
401
g_object_unref (config);
404
g_ptr_array_free (parser->configurations, TRUE);
406
for (list = parser->stack->head; list; list = list->next)
408
g_queue_free (parser->stack);
413
static GnomeRRConfig **
414
configurations_read_from_file (const gchar *filename, GError **error)
416
Parser *parser = g_new0 (Parser, 1);
417
GnomeRRConfig **result;
418
GMarkupParser callbacks = {
419
handle_start_element,
422
NULL, /* passthrough */
426
parser->config_file_version = 0;
427
parser->configurations = g_ptr_array_new ();
428
parser->outputs = g_ptr_array_new ();
429
parser->stack = g_queue_new ();
431
if (!parse_file_gmarkup (filename, &callbacks, parser, error))
435
g_assert (parser->outputs);
439
g_assert (parser->outputs);
441
g_ptr_array_add (parser->configurations, NULL);
442
result = (GnomeRRConfig **)g_ptr_array_free (parser->configurations, FALSE);
443
parser->configurations = g_ptr_array_new ();
445
g_assert (parser->outputs);
447
parser_free (parser);
453
gnome_rr_config_init (GnomeRRConfig *self)
455
self->priv = G_TYPE_INSTANCE_GET_PRIVATE (self, GNOME_TYPE_RR_CONFIG, GnomeRRConfigPrivate);
457
self->priv->clone = FALSE;
458
self->priv->screen = NULL;
459
self->priv->outputs = NULL;
463
gnome_rr_config_set_property (GObject *gobject, guint property_id, const GValue *value, GParamSpec *property)
465
GnomeRRConfig *self = GNOME_RR_CONFIG (gobject);
467
switch (property_id) {
469
self->priv->screen = g_value_dup_object (value);
472
G_OBJECT_WARN_INVALID_PROPERTY_ID (gobject, property_id, property);
477
gnome_rr_config_finalize (GObject *gobject)
479
GnomeRRConfig *self = GNOME_RR_CONFIG (gobject);
481
if (self->priv->screen)
482
g_object_unref (self->priv->screen);
484
if (self->priv->outputs) {
487
for (i = 0; self->priv->outputs[i] != NULL; i++) {
488
GnomeRROutputInfo *output = self->priv->outputs[i];
489
g_object_unref (output);
491
g_free (self->priv->outputs);
494
G_OBJECT_CLASS (gnome_rr_config_parent_class)->finalize (gobject);
498
gnome_rr_config_load_current (GnomeRRConfig *config, GError **error)
501
GnomeRROutput **rr_outputs;
503
int clone_width = -1;
504
int clone_height = -1;
507
g_return_val_if_fail (GNOME_IS_RR_CONFIG (config), FALSE);
509
a = g_ptr_array_new ();
510
rr_outputs = gnome_rr_screen_list_outputs (config->priv->screen);
512
config->priv->clone = FALSE;
514
for (i = 0; rr_outputs[i] != NULL; ++i)
516
GnomeRROutput *rr_output = rr_outputs[i];
517
GnomeRROutputInfo *output = g_object_new (GNOME_TYPE_RR_OUTPUT_INFO, NULL);
518
GnomeRRMode *mode = NULL;
519
const guint8 *edid_data = gnome_rr_output_get_edid_data (rr_output, NULL);
522
output->priv->name = g_strdup (gnome_rr_output_get_name (rr_output));
523
output->priv->connected = gnome_rr_output_is_connected (rr_output);
524
output->priv->display_name = g_strdup (gnome_rr_output_get_display_name (rr_output));
526
if (!output->priv->connected)
528
output->priv->x = -1;
529
output->priv->y = -1;
530
output->priv->width = -1;
531
output->priv->height = -1;
532
output->priv->rate = -1;
533
output->priv->rotation = GNOME_RR_ROTATION_0;
537
MonitorInfo *info = NULL;
540
info = decode_edid (edid_data);
544
memcpy (output->priv->vendor, info->manufacturer_code,
545
sizeof (output->priv->vendor));
547
output->priv->product = info->product_code;
548
output->priv->serial = info->serial_number;
549
output->priv->aspect = info->aspect_ratio;
553
strcpy (output->priv->vendor, "???");
554
output->priv->product = 0;
555
output->priv->serial = 0;
559
crtc = gnome_rr_output_get_crtc (rr_output);
560
mode = crtc? gnome_rr_crtc_get_current_mode (crtc) : NULL;
564
output->priv->on = TRUE;
566
gnome_rr_crtc_get_position (crtc, &output->priv->x, &output->priv->y);
567
output->priv->width = gnome_rr_mode_get_width (mode);
568
output->priv->height = gnome_rr_mode_get_height (mode);
569
output->priv->rate = gnome_rr_mode_get_freq (mode);
570
output->priv->rotation = gnome_rr_crtc_get_current_rotation (crtc);
572
if (output->priv->x == 0 && output->priv->y == 0) {
573
if (clone_width == -1) {
574
clone_width = output->priv->width;
575
clone_height = output->priv->height;
576
} else if (clone_width == output->priv->width &&
577
clone_height == output->priv->height) {
578
config->priv->clone = TRUE;
584
output->priv->on = FALSE;
585
config->priv->clone = FALSE;
588
/* Get preferred size for the monitor */
589
mode = gnome_rr_output_get_preferred_mode (rr_output);
593
GnomeRRMode **modes = gnome_rr_output_list_modes (rr_output);
595
/* FIXME: we should pick the "best" mode here, where best is
598
* - closest aspect ratio
601
* - We may want to extend randrwrap so that get_preferred
602
* returns that - although that could also depend on
611
output->priv->pref_width = gnome_rr_mode_get_width (mode);
612
output->priv->pref_height = gnome_rr_mode_get_height (mode);
616
/* Pick some random numbers. This should basically never happen */
617
output->priv->pref_width = 1024;
618
output->priv->pref_height = 768;
622
output->priv->primary = gnome_rr_output_get_is_primary (rr_output);
624
g_ptr_array_add (a, output);
627
g_ptr_array_add (a, NULL);
629
config->priv->outputs = (GnomeRROutputInfo **)g_ptr_array_free (a, FALSE);
631
/* Walk the outputs computing the right-most edge of all
635
for (i = 0; config->priv->outputs[i] != NULL; ++i)
637
GnomeRROutputInfo *output = config->priv->outputs[i];
639
if (output->priv->on)
641
last_x = MAX (last_x, output->priv->x + output->priv->width);
645
/* Now position all off displays to the right of the
648
for (i = 0; config->priv->outputs[i] != NULL; ++i)
650
GnomeRROutputInfo *output = config->priv->outputs[i];
652
if (output->priv->connected && !output->priv->on)
654
output->priv->x = last_x;
655
last_x = output->priv->x + output->priv->width;
659
g_assert (gnome_rr_config_match (config, config));
665
gnome_rr_config_load_filename (GnomeRRConfig *result, const char *filename, GError **error)
667
GnomeRRConfig *current;
668
GnomeRRConfig **configs;
669
gboolean found = FALSE;
671
g_return_val_if_fail (GNOME_IS_RR_CONFIG (result), FALSE);
672
g_return_val_if_fail (filename != NULL, FALSE);
673
g_return_val_if_fail (error == NULL || *error == NULL, FALSE);
675
current = gnome_rr_config_new_current (result->priv->screen, error);
677
configs = configurations_read_from_file (filename, error);
683
for (i = 0; configs[i] != NULL; ++i)
685
if (gnome_rr_config_match (configs[i], current))
689
result->priv->clone = configs[i]->priv->clone;
691
array = g_ptr_array_new ();
692
for (j = 0; configs[i]->priv->outputs[j] != NULL; j++) {
693
g_object_ref (configs[i]->priv->outputs[j]);
694
g_ptr_array_add (array, configs[i]->priv->outputs[j]);
696
g_ptr_array_add (array, NULL);
697
result->priv->outputs = (GnomeRROutputInfo **) g_ptr_array_free (array, FALSE);
702
g_object_unref (configs[i]);
707
g_set_error (error, GNOME_RR_ERROR, GNOME_RR_ERROR_NO_MATCHING_CONFIG,
708
_("none of the saved display configurations matched the active configuration"));
711
g_object_unref (current);
716
gnome_rr_config_class_init (GnomeRRConfigClass *klass)
718
GObjectClass *gobject_class = G_OBJECT_CLASS (klass);
720
g_type_class_add_private (klass, sizeof (GnomeRROutputInfoPrivate));
722
gobject_class->set_property = gnome_rr_config_set_property;
723
gobject_class->finalize = gnome_rr_config_finalize;
725
g_object_class_install_property (gobject_class, PROP_SCREEN,
726
g_param_spec_object ("screen", "Screen", "The GnomeRRScreen this config applies to", GNOME_TYPE_RR_SCREEN,
727
G_PARAM_WRITABLE | G_PARAM_CONSTRUCT_ONLY | G_PARAM_STATIC_NICK | G_PARAM_STATIC_BLURB));
731
gnome_rr_config_new_current (GnomeRRScreen *screen, GError **error)
733
GnomeRRConfig *self = g_object_new (GNOME_TYPE_RR_CONFIG, "screen", screen, NULL);
735
if (gnome_rr_config_load_current (self, error))
739
g_object_unref (self);
745
gnome_rr_config_new_stored (GnomeRRScreen *screen, GError **error)
747
GnomeRRConfig *self = g_object_new (GNOME_TYPE_RR_CONFIG, "screen", screen, NULL);
751
filename = gnome_rr_config_get_intended_filename ();
753
success = gnome_rr_config_load_filename (self, filename, error);
761
g_object_unref (self);
767
parse_file_gmarkup (const gchar *filename,
768
const GMarkupParser *parser,
772
GMarkupParseContext *context = NULL;
773
gchar *contents = NULL;
774
gboolean result = TRUE;
777
if (!g_file_get_contents (filename, &contents, &len, err))
783
context = g_markup_parse_context_new (parser, 0, data, NULL);
785
if (!g_markup_parse_context_parse (context, contents, len, err))
791
if (!g_markup_parse_context_end_parse (context, err))
802
g_markup_parse_context_free (context);
808
output_match (GnomeRROutputInfo *output1, GnomeRROutputInfo *output2)
810
g_assert (GNOME_IS_RR_OUTPUT_INFO (output1));
811
g_assert (GNOME_IS_RR_OUTPUT_INFO (output2));
813
if (strcmp (output1->priv->name, output2->priv->name) != 0)
816
if (strcmp (output1->priv->vendor, output2->priv->vendor) != 0)
819
if (output1->priv->product != output2->priv->product)
822
if (output1->priv->serial != output2->priv->serial)
825
if (output1->priv->connected != output2->priv->connected)
832
output_equal (GnomeRROutputInfo *output1, GnomeRROutputInfo *output2)
834
g_assert (GNOME_IS_RR_OUTPUT_INFO (output1));
835
g_assert (GNOME_IS_RR_OUTPUT_INFO (output2));
837
if (!output_match (output1, output2))
840
if (output1->priv->on != output2->priv->on)
843
if (output1->priv->on)
845
if (output1->priv->width != output2->priv->width)
848
if (output1->priv->height != output2->priv->height)
851
if (output1->priv->rate != output2->priv->rate)
854
if (output1->priv->x != output2->priv->x)
857
if (output1->priv->y != output2->priv->y)
860
if (output1->priv->rotation != output2->priv->rotation)
867
static GnomeRROutputInfo *
868
find_output (GnomeRRConfig *config, const char *name)
872
for (i = 0; config->priv->outputs[i] != NULL; ++i)
874
GnomeRROutputInfo *output = config->priv->outputs[i];
876
if (strcmp (name, output->priv->name) == 0)
883
/* Match means "these configurations apply to the same hardware
887
gnome_rr_config_match (GnomeRRConfig *c1, GnomeRRConfig *c2)
890
g_return_val_if_fail (GNOME_IS_RR_CONFIG (c1), FALSE);
891
g_return_val_if_fail (GNOME_IS_RR_CONFIG (c2), FALSE);
893
for (i = 0; c1->priv->outputs[i] != NULL; ++i)
895
GnomeRROutputInfo *output1 = c1->priv->outputs[i];
896
GnomeRROutputInfo *output2;
898
output2 = find_output (c2, output1->priv->name);
899
if (!output2 || !output_match (output1, output2))
906
/* Equal means "the configurations will result in the same
907
* modes being set on the outputs"
910
gnome_rr_config_equal (GnomeRRConfig *c1,
914
g_return_val_if_fail (GNOME_IS_RR_CONFIG (c1), FALSE);
915
g_return_val_if_fail (GNOME_IS_RR_CONFIG (c2), FALSE);
917
for (i = 0; c1->priv->outputs[i] != NULL; ++i)
919
GnomeRROutputInfo *output1 = c1->priv->outputs[i];
920
GnomeRROutputInfo *output2;
922
output2 = find_output (c2, output1->priv->name);
923
if (!output2 || !output_equal (output1, output2))
930
static GnomeRROutputInfo **
931
make_outputs (GnomeRRConfig *config)
934
GnomeRROutputInfo *first_on;
937
outputs = g_ptr_array_new ();
941
for (i = 0; config->priv->outputs[i] != NULL; ++i)
943
GnomeRROutputInfo *old = config->priv->outputs[i];
944
GnomeRROutputInfo *new = g_object_new (GNOME_TYPE_RR_OUTPUT_INFO, NULL);
945
*(new->priv) = *(old->priv);
947
new->priv->name = g_strdup (old->priv->name);
948
if (old->priv->display_name)
949
new->priv->display_name = g_strdup (old->priv->display_name);
951
if (old->priv->on && !first_on)
954
if (config->priv->clone && new->priv->on)
958
new->priv->width = first_on->priv->width;
959
new->priv->height = first_on->priv->height;
960
new->priv->rotation = first_on->priv->rotation;
965
g_ptr_array_add (outputs, new);
968
g_ptr_array_add (outputs, NULL);
970
return (GnomeRROutputInfo **)g_ptr_array_free (outputs, FALSE);
974
gnome_rr_config_applicable (GnomeRRConfig *configuration,
975
GnomeRRScreen *screen,
978
GnomeRROutputInfo **outputs;
979
CrtcAssignment *assign;
983
g_return_val_if_fail (GNOME_IS_RR_CONFIG (configuration), FALSE);
984
g_return_val_if_fail (GNOME_IS_RR_SCREEN (screen), FALSE);
985
g_return_val_if_fail (error == NULL || *error == NULL, FALSE);
987
outputs = make_outputs (configuration);
988
assign = crtc_assignment_new (screen, outputs, error);
993
crtc_assignment_free (assign);
1000
for (i = 0; outputs[i] != NULL; i++) {
1001
g_object_unref (outputs[i]);
1007
/* Database management */
1010
ensure_config_directory (void)
1012
g_mkdir_with_parents (g_get_user_config_dir (), 0700);
1016
gnome_rr_config_get_backup_filename (void)
1018
ensure_config_directory ();
1019
return g_build_filename (g_get_user_config_dir (), CONFIG_BACKUP_BASENAME, NULL);
1023
gnome_rr_config_get_intended_filename (void)
1025
ensure_config_directory ();
1026
return g_build_filename (g_get_user_config_dir (), CONFIG_INTENDED_BASENAME, NULL);
1030
get_rotation_name (GnomeRRRotation r)
1032
if (r & GNOME_RR_ROTATION_0)
1034
if (r & GNOME_RR_ROTATION_90)
1036
if (r & GNOME_RR_ROTATION_180)
1037
return "upside_down";
1038
if (r & GNOME_RR_ROTATION_270)
1047
return x? "yes" : "no";
1051
get_reflect_x (GnomeRRRotation r)
1053
return yes_no (r & GNOME_RR_REFLECT_X);
1057
get_reflect_y (GnomeRRRotation r)
1059
return yes_no (r & GNOME_RR_REFLECT_Y);
1063
emit_configuration (GnomeRRConfig *config,
1068
g_string_append_printf (string, " <configuration>\n");
1070
g_string_append_printf (string, " <clone>%s</clone>\n", yes_no (config->priv->clone));
1072
for (j = 0; config->priv->outputs[j] != NULL; ++j)
1074
GnomeRROutputInfo *output = config->priv->outputs[j];
1076
g_string_append_printf (
1077
string, " <output name=\"%s\">\n", output->priv->name);
1079
if (output->priv->connected && *output->priv->vendor != '\0')
1081
g_string_append_printf (
1082
string, " <vendor>%s</vendor>\n", output->priv->vendor);
1083
g_string_append_printf (
1084
string, " <product>0x%04x</product>\n", output->priv->product);
1085
g_string_append_printf (
1086
string, " <serial>0x%08x</serial>\n", output->priv->serial);
1089
/* An unconnected output which is on does not make sense */
1090
if (output->priv->connected && output->priv->on)
1092
g_string_append_printf (
1093
string, " <width>%d</width>\n", output->priv->width);
1094
g_string_append_printf (
1095
string, " <height>%d</height>\n", output->priv->height);
1096
g_string_append_printf (
1097
string, " <rate>%d</rate>\n", output->priv->rate);
1098
g_string_append_printf (
1099
string, " <x>%d</x>\n", output->priv->x);
1100
g_string_append_printf (
1101
string, " <y>%d</y>\n", output->priv->y);
1102
g_string_append_printf (
1103
string, " <rotation>%s</rotation>\n", get_rotation_name (output->priv->rotation));
1104
g_string_append_printf (
1105
string, " <reflect_x>%s</reflect_x>\n", get_reflect_x (output->priv->rotation));
1106
g_string_append_printf (
1107
string, " <reflect_y>%s</reflect_y>\n", get_reflect_y (output->priv->rotation));
1108
g_string_append_printf (
1109
string, " <primary>%s</primary>\n", yes_no (output->priv->primary));
1112
g_string_append_printf (string, " </output>\n");
1115
g_string_append_printf (string, " </configuration>\n");
1119
gnome_rr_config_sanitize (GnomeRRConfig *config)
1122
int x_offset, y_offset;
1125
/* Offset everything by the top/left-most coordinate to
1126
* make sure the configuration starts at (0, 0)
1128
x_offset = y_offset = G_MAXINT;
1129
for (i = 0; config->priv->outputs[i]; ++i)
1131
GnomeRROutputInfo *output = config->priv->outputs[i];
1133
if (output->priv->on)
1135
x_offset = MIN (x_offset, output->priv->x);
1136
y_offset = MIN (y_offset, output->priv->y);
1140
for (i = 0; config->priv->outputs[i]; ++i)
1142
GnomeRROutputInfo *output = config->priv->outputs[i];
1144
if (output->priv->on)
1146
output->priv->x -= x_offset;
1147
output->priv->y -= y_offset;
1151
/* Only one primary, please */
1153
for (i = 0; config->priv->outputs[i]; ++i)
1155
if (config->priv->outputs[i]->priv->primary)
1159
config->priv->outputs[i]->priv->primary = FALSE;
1170
gnome_rr_config_ensure_primary (GnomeRRConfig *configuration)
1173
GnomeRROutputInfo *laptop;
1174
GnomeRROutputInfo *top_left;
1176
GnomeRRConfigPrivate *priv;
1178
g_return_val_if_fail (GNOME_IS_RR_CONFIG (configuration), FALSE);
1183
priv = configuration->priv;
1185
for (i = 0; priv->outputs[i] != NULL; ++i) {
1186
GnomeRROutputInfo *info = priv->outputs[i];
1188
if (!info->priv->on) {
1189
info->priv->primary = FALSE;
1193
/* ensure only one */
1194
if (info->priv->primary) {
1196
info->priv->primary = FALSE;
1202
if (top_left == NULL
1203
|| (info->priv->x < top_left->priv->x
1204
&& info->priv->y < top_left->priv->y)) {
1208
&& _gnome_rr_output_name_is_laptop (info->priv->name)) {
1209
/* shame we can't find the connector type
1210
as with gnome_rr_output_is_laptop */
1216
if (laptop != NULL) {
1217
laptop->priv->primary = TRUE;
1218
} else if (top_left != NULL) {
1219
/* Note: top_left can be NULL if all outputs are off */
1220
top_left->priv->primary = TRUE;
1228
gnome_rr_config_save (GnomeRRConfig *configuration, GError **error)
1230
GnomeRRConfig **configurations;
1233
gchar *intended_filename;
1234
gchar *backup_filename;
1237
g_return_val_if_fail (GNOME_IS_RR_CONFIG (configuration), FALSE);
1238
g_return_val_if_fail (error == NULL || *error == NULL, FALSE);
1240
output = g_string_new ("");
1242
backup_filename = gnome_rr_config_get_backup_filename ();
1243
intended_filename = gnome_rr_config_get_intended_filename ();
1245
configurations = configurations_read_from_file (intended_filename, NULL); /* NULL-GError */
1247
g_string_append_printf (output, "<monitors version=\"1\">\n");
1251
for (i = 0; configurations[i] != NULL; ++i)
1253
if (!gnome_rr_config_match (configurations[i], configuration))
1254
emit_configuration (configurations[i], output);
1255
g_object_unref (configurations[i]);
1258
g_free (configurations);
1261
emit_configuration (configuration, output);
1263
g_string_append_printf (output, "</monitors>\n");
1265
/* backup the file first */
1266
rename (intended_filename, backup_filename); /* no error checking because the intended file may not even exist */
1268
result = g_file_set_contents (intended_filename, output->str, -1, error);
1271
rename (backup_filename, intended_filename); /* no error checking because the backup may not even exist */
1273
g_free (backup_filename);
1274
g_free (intended_filename);
1280
gnome_rr_config_apply_with_time (GnomeRRConfig *config,
1281
GnomeRRScreen *screen,
1285
CrtcAssignment *assignment;
1286
GnomeRROutputInfo **outputs;
1287
gboolean result = FALSE;
1290
g_return_val_if_fail (GNOME_IS_RR_CONFIG (config), FALSE);
1291
g_return_val_if_fail (GNOME_IS_RR_SCREEN (screen), FALSE);
1293
outputs = make_outputs (config);
1295
assignment = crtc_assignment_new (screen, outputs, error);
1297
for (i = 0; outputs[i] != NULL; i++)
1298
g_object_unref (outputs[i]);
1303
if (crtc_assignment_apply (assignment, timestamp, error))
1306
crtc_assignment_free (assignment);
1314
/* gnome_rr_config_apply_from_filename_with_time:
1315
* @screen: A #GnomeRRScreen
1316
* @filename: Path of the file to look in for stored RANDR configurations.
1317
* @timestamp: X server timestamp from the event that causes the screen configuration to change (a user's button press, for example)
1318
* @error: Location to store error, or %NULL
1320
* Loads the file in @filename and looks for suitable matching RANDR
1321
* configurations in the file; if one is found, that configuration will be
1322
* applied to the current set of RANDR outputs.
1324
* Typically, @filename is the result of gnome_rr_config_get_intended_filename() or
1325
* gnome_rr_config_get_backup_filename().
1327
* Returns: TRUE if the RANDR configuration was loaded and applied from
1328
* the specified file, or FALSE otherwise:
1330
* If the file in question is loaded successfully but the configuration cannot
1331
* be applied, the @error will have a domain of #GNOME_RR_ERROR. Note that an
1332
* error code of #GNOME_RR_ERROR_NO_MATCHING_CONFIG is not a real error; it
1333
* simply means that there were no stored configurations that match the current
1334
* set of RANDR outputs.
1336
* If the file in question cannot be loaded, the @error will have a domain of
1337
* #G_FILE_ERROR. Note that an error code of G_FILE_ERROR_NOENT is not really
1338
* an error, either; it means that there was no stored configuration file and so
1339
* nothing is changed.
1342
gnome_rr_config_apply_from_filename_with_time (GnomeRRScreen *screen, const char *filename, guint32 timestamp, GError **error)
1344
GnomeRRConfig *stored;
1346
g_return_val_if_fail (GNOME_IS_RR_SCREEN (screen), FALSE);
1347
g_return_val_if_fail (filename != NULL, FALSE);
1348
g_return_val_if_fail (error == NULL || *error == NULL, FALSE);
1350
stored = g_object_new (GNOME_TYPE_RR_CONFIG, "screen", screen, NULL);
1352
if (gnome_rr_config_load_filename (stored, filename, error))
1356
gnome_rr_config_ensure_primary (stored);
1357
result = gnome_rr_config_apply_with_time (stored, screen, timestamp, error);
1359
g_object_unref (stored);
1364
g_object_unref (stored);
1370
* gnome_rr_config_get_outputs:
1372
* Returns: (array zero-terminated=1) (element-type GnomeDesktop.RROutputInfo) (transfer none): the output configuration for this #GnomeRRConfig
1374
GnomeRROutputInfo **
1375
gnome_rr_config_get_outputs (GnomeRRConfig *self)
1377
g_return_val_if_fail (GNOME_IS_RR_CONFIG (self), NULL);
1379
return self->priv->outputs;
1383
* gnome_rr_config_get_clone:
1385
* Returns: whether at least two outputs are at (0, 0) offset and they
1386
* have the same width/height. Those outputs are of course connected and on
1387
* (i.e. they have a CRTC assigned).
1390
gnome_rr_config_get_clone (GnomeRRConfig *self)
1392
g_return_val_if_fail (GNOME_IS_RR_CONFIG (self), FALSE);
1394
return self->priv->clone;
1398
gnome_rr_config_set_clone (GnomeRRConfig *self, gboolean clone)
1400
g_return_if_fail (GNOME_IS_RR_CONFIG (self));
1402
self->priv->clone = clone;
1408
typedef struct CrtcInfo CrtcInfo;
1415
GnomeRRRotation rotation;
1419
struct CrtcAssignment
1421
GnomeRRScreen *screen;
1423
GnomeRROutput *primary;
1427
can_clone (CrtcInfo *info,
1428
GnomeRROutput *output)
1432
for (i = 0; i < info->outputs->len; ++i)
1434
GnomeRROutput *clone = info->outputs->pdata[i];
1436
if (!gnome_rr_output_can_clone (clone, output))
1444
crtc_assignment_assign (CrtcAssignment *assign,
1449
GnomeRRRotation rotation,
1451
GnomeRROutput *output,
1454
CrtcInfo *info = g_hash_table_lookup (assign->info, crtc);
1456
const char *output_name;
1458
crtc_id = gnome_rr_crtc_get_id (crtc);
1459
output_name = gnome_rr_output_get_name (output);
1461
if (!gnome_rr_crtc_can_drive_output (crtc, output))
1463
g_set_error (error, GNOME_RR_ERROR, GNOME_RR_ERROR_CRTC_ASSIGNMENT,
1464
_("CRTC %d cannot drive output %s"), crtc_id, output_name);
1468
if (!gnome_rr_output_supports_mode (output, mode))
1470
g_set_error (error, GNOME_RR_ERROR, GNOME_RR_ERROR_CRTC_ASSIGNMENT,
1471
_("output %s does not support mode %dx%d@%dHz"),
1473
gnome_rr_mode_get_width (mode),
1474
gnome_rr_mode_get_height (mode),
1475
gnome_rr_mode_get_freq (mode));
1479
if (!gnome_rr_crtc_supports_rotation (crtc, rotation))
1481
g_set_error (error, GNOME_RR_ERROR, GNOME_RR_ERROR_CRTC_ASSIGNMENT,
1482
_("CRTC %d does not support rotation=%s"),
1484
get_rotation_name (rotation));
1490
if (!(info->mode == mode &&
1493
info->rotation == rotation))
1495
g_set_error (error, GNOME_RR_ERROR, GNOME_RR_ERROR_CRTC_ASSIGNMENT,
1496
_("output %s does not have the same parameters as another cloned output:\n"
1497
"existing mode = %d, new mode = %d\n"
1498
"existing coordinates = (%d, %d), new coordinates = (%d, %d)\n"
1499
"existing rotation = %s, new rotation = %s"),
1501
gnome_rr_mode_get_id (info->mode), gnome_rr_mode_get_id (mode),
1504
get_rotation_name (info->rotation), get_rotation_name (rotation));
1508
if (!can_clone (info, output))
1510
g_set_error (error, GNOME_RR_ERROR, GNOME_RR_ERROR_CRTC_ASSIGNMENT,
1511
_("cannot clone to output %s"),
1516
g_ptr_array_add (info->outputs, output);
1518
if (primary && !assign->primary)
1520
assign->primary = output;
1527
CrtcInfo *info = g_new0 (CrtcInfo, 1);
1532
info->rotation = rotation;
1533
info->outputs = g_ptr_array_new ();
1535
g_ptr_array_add (info->outputs, output);
1537
g_hash_table_insert (assign->info, crtc, info);
1539
if (primary && !assign->primary)
1541
assign->primary = output;
1549
crtc_assignment_unassign (CrtcAssignment *assign,
1551
GnomeRROutput *output)
1553
CrtcInfo *info = g_hash_table_lookup (assign->info, crtc);
1557
g_ptr_array_remove (info->outputs, output);
1559
if (assign->primary == output)
1561
assign->primary = NULL;
1564
if (info->outputs->len == 0)
1565
g_hash_table_remove (assign->info, crtc);
1570
crtc_assignment_free (CrtcAssignment *assign)
1572
g_hash_table_destroy (assign->info);
1581
} ConfigureCrtcState;
1584
configure_crtc (gpointer key,
1588
GnomeRRCrtc *crtc = key;
1589
CrtcInfo *info = value;
1590
ConfigureCrtcState *state = data;
1592
if (state->has_error)
1595
if (!gnome_rr_crtc_set_config_with_time (crtc,
1600
(GnomeRROutput **)info->outputs->pdata,
1603
state->has_error = TRUE;
1607
mode_is_rotated (CrtcInfo *info)
1609
if ((info->rotation & GNOME_RR_ROTATION_270) ||
1610
(info->rotation & GNOME_RR_ROTATION_90))
1618
crtc_is_rotated (GnomeRRCrtc *crtc)
1620
GnomeRRRotation r = gnome_rr_crtc_get_current_rotation (crtc);
1622
if ((r & GNOME_RR_ROTATION_270) ||
1623
(r & GNOME_RR_ROTATION_90))
1632
accumulate_error (GString *accumulated_error, GError *error)
1634
g_string_append_printf (accumulated_error, " %s\n", error->message);
1635
g_error_free (error);
1638
/* Check whether the given set of settings can be used
1639
* at the same time -- ie. whether there is an assignment
1640
* of CRTC's to outputs.
1642
* Brute force - the number of objects involved is small
1643
* enough that it doesn't matter.
1646
real_assign_crtcs (GnomeRRScreen *screen,
1647
GnomeRROutputInfo **outputs,
1648
CrtcAssignment *assignment,
1651
GnomeRRCrtc **crtcs = gnome_rr_screen_list_crtcs (screen);
1652
GnomeRROutputInfo *output;
1654
gboolean tried_mode;
1656
GString *accumulated_error;
1663
/* It is always allowed for an output to be turned off */
1664
if (!output->priv->on)
1666
return real_assign_crtcs (screen, outputs + 1, assignment, error);
1671
accumulated_error = g_string_new (NULL);
1673
for (i = 0; crtcs[i] != NULL; ++i)
1675
GnomeRRCrtc *crtc = crtcs[i];
1676
int crtc_id = gnome_rr_crtc_get_id (crtc);
1679
g_string_append_printf (accumulated_error,
1680
_("Trying modes for CRTC %d\n"),
1683
/* Make two passes, one where frequencies must match, then
1684
* one where they don't have to
1686
for (pass = 0; pass < 2; ++pass)
1688
GnomeRROutput *gnome_rr_output = gnome_rr_screen_get_output_by_name (screen, output->priv->name);
1689
GnomeRRMode **modes = gnome_rr_output_list_modes (gnome_rr_output);
1692
for (j = 0; modes[j] != NULL; ++j)
1694
GnomeRRMode *mode = modes[j];
1699
mode_width = gnome_rr_mode_get_width (mode);
1700
mode_height = gnome_rr_mode_get_height (mode);
1701
mode_freq = gnome_rr_mode_get_freq (mode);
1703
g_string_append_printf (accumulated_error,
1704
_("CRTC %d: trying mode %dx%d@%dHz with output at %dx%d@%dHz (pass %d)\n"),
1706
mode_width, mode_height, mode_freq,
1707
output->priv->width, output->priv->height, output->priv->rate,
1710
if (mode_width == output->priv->width &&
1711
mode_height == output->priv->height &&
1712
(pass == 1 || mode_freq == output->priv->rate))
1717
if (crtc_assignment_assign (
1718
assignment, crtc, modes[j],
1719
output->priv->x, output->priv->y,
1720
output->priv->rotation,
1721
output->priv->primary,
1726
if (real_assign_crtcs (screen, outputs + 1, assignment, &my_error)) {
1730
accumulate_error (accumulated_error, my_error);
1732
crtc_assignment_unassign (assignment, crtc, gnome_rr_output);
1734
accumulate_error (accumulated_error, my_error);
1743
g_string_free (accumulated_error, TRUE);
1747
str = g_string_free (accumulated_error, FALSE);
1750
g_set_error (error, GNOME_RR_ERROR, GNOME_RR_ERROR_CRTC_ASSIGNMENT,
1751
_("could not assign CRTCs to outputs:\n%s"),
1754
g_set_error (error, GNOME_RR_ERROR, GNOME_RR_ERROR_CRTC_ASSIGNMENT,
1755
_("none of the selected modes were compatible with the possible modes:\n%s"),
1765
crtc_info_free (CrtcInfo *info)
1767
g_ptr_array_free (info->outputs, TRUE);
1772
get_required_virtual_size (CrtcAssignment *assign, int *width, int *height)
1774
GList *active_crtcs = g_hash_table_get_keys (assign->info);
1783
/* Compute size of the screen */
1784
*width = *height = 1;
1785
for (list = active_crtcs; list != NULL; list = list->next)
1787
GnomeRRCrtc *crtc = list->data;
1788
CrtcInfo *info = g_hash_table_lookup (assign->info, crtc);
1791
w = gnome_rr_mode_get_width (info->mode);
1792
h = gnome_rr_mode_get_height (info->mode);
1794
if (mode_is_rotated (info))
1801
*width = MAX (*width, info->x + w);
1802
*height = MAX (*height, info->y + h);
1805
g_list_free (active_crtcs);
1808
static CrtcAssignment *
1809
crtc_assignment_new (GnomeRRScreen *screen, GnomeRROutputInfo **outputs, GError **error)
1811
CrtcAssignment *assignment = g_new0 (CrtcAssignment, 1);
1813
assignment->info = g_hash_table_new_full (
1814
g_direct_hash, g_direct_equal, NULL, (GFreeFunc)crtc_info_free);
1816
if (real_assign_crtcs (screen, outputs, assignment, error))
1819
int min_width, max_width, min_height, max_height;
1821
get_required_virtual_size (assignment, &width, &height);
1823
gnome_rr_screen_get_ranges (
1824
screen, &min_width, &max_width, &min_height, &max_height);
1826
if (width < min_width || width > max_width ||
1827
height < min_height || height > max_height)
1829
g_set_error (error, GNOME_RR_ERROR, GNOME_RR_ERROR_BOUNDS_ERROR,
1830
/* Translators: the "requested", "minimum", and
1831
* "maximum" words here are not keywords; please
1832
* translate them as usual. */
1833
_("required virtual size does not fit available size: "
1834
"requested=(%d, %d), minimum=(%d, %d), maximum=(%d, %d)"),
1836
min_width, min_height,
1837
max_width, max_height);
1841
assignment->screen = screen;
1847
crtc_assignment_free (assignment);
1853
crtc_assignment_apply (CrtcAssignment *assign, guint32 timestamp, GError **error)
1855
GnomeRRCrtc **all_crtcs = gnome_rr_screen_list_crtcs (assign->screen);
1858
int min_width, max_width, min_height, max_height;
1859
int width_mm, height_mm;
1860
gboolean success = TRUE;
1862
/* Compute size of the screen */
1863
get_required_virtual_size (assign, &width, &height);
1865
gnome_rr_screen_get_ranges (
1866
assign->screen, &min_width, &max_width, &min_height, &max_height);
1868
/* We should never get here if the dimensions don't fit in the virtual size,
1869
* but just in case we do, fix it up.
1871
width = MAX (min_width, width);
1872
width = MIN (max_width, width);
1873
height = MAX (min_height, height);
1874
height = MIN (max_height, height);
1876
/* FMQ: do we need to check the sizes instead of clamping them? */
1878
/* Grab the server while we fiddle with the CRTCs and the screen, so that
1879
* apps that listen for RANDR notifications will only receive the final
1883
gdk_x11_display_grab (gdk_screen_get_display (assign->screen->priv->gdk_screen));
1885
/* Turn off all crtcs that are currently displaying outside the new screen,
1886
* or are not used in the new setup
1888
for (i = 0; all_crtcs[i] != NULL; ++i)
1890
GnomeRRCrtc *crtc = all_crtcs[i];
1891
GnomeRRMode *mode = gnome_rr_crtc_get_current_mode (crtc);
1897
gnome_rr_crtc_get_position (crtc, &x, &y);
1899
w = gnome_rr_mode_get_width (mode);
1900
h = gnome_rr_mode_get_height (mode);
1902
if (crtc_is_rotated (crtc))
1909
if (x + w > width || y + h > height || !g_hash_table_lookup (assign->info, crtc))
1911
if (!gnome_rr_crtc_set_config_with_time (crtc, timestamp, 0, 0, NULL, GNOME_RR_ROTATION_0, NULL, 0, error))
1921
/* The 'physical size' of an X screen is meaningless if that screen
1922
* can consist of many monitors. So just pick a size that make the
1925
* Firefox and Evince apparently believe what X tells them.
1927
width_mm = (width / DPI_FALLBACK) * 25.4 + 0.5;
1928
height_mm = (height / DPI_FALLBACK) * 25.4 + 0.5;
1932
ConfigureCrtcState state;
1934
gnome_rr_screen_set_size (assign->screen, width, height, width_mm, height_mm);
1936
state.timestamp = timestamp;
1937
state.has_error = FALSE;
1938
state.error = error;
1940
g_hash_table_foreach (assign->info, configure_crtc, &state);
1942
success = !state.has_error;
1945
gnome_rr_screen_set_primary_output (assign->screen, assign->primary);
1947
gdk_x11_display_ungrab (gdk_screen_get_display (assign->screen->priv->gdk_screen));